diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:17:27 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:17:27 +0000 |
commit | f215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch) | |
tree | 6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei | |
parent | Initial commit. (diff) | |
download | virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip |
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei')
8 files changed, 2685 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei/CpuBist.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei/CpuBist.c new file mode 100644 index 00000000..3c0a0148 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei/CpuBist.c @@ -0,0 +1,291 @@ +/** @file + Update and publish processors' BIST information. + + Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "CpuMpPei.h" + +EFI_SEC_PLATFORM_INFORMATION2_PPI mSecPlatformInformation2Ppi = { + SecPlatformInformation2 +}; + +EFI_PEI_PPI_DESCRIPTOR mPeiSecPlatformInformation2Ppi = { + (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), + &gEfiSecPlatformInformation2PpiGuid, + &mSecPlatformInformation2Ppi +}; + +/** + Implementation of the PlatformInformation2 service in EFI_SEC_PLATFORM_INFORMATION2_PPI. + + @param PeiServices The pointer to the PEI Services Table. + @param StructureSize The pointer to the variable describing size of the input buffer. + @param PlatformInformationRecord2 The pointer to the EFI_SEC_PLATFORM_INFORMATION_RECORD2. + + @retval EFI_SUCCESS The data was successfully returned. + @retval EFI_BUFFER_TOO_SMALL The buffer was too small. The current buffer size needed to + hold the record is returned in StructureSize. + +**/ +EFI_STATUS +EFIAPI +SecPlatformInformation2 ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN OUT UINT64 *StructureSize, + OUT EFI_SEC_PLATFORM_INFORMATION_RECORD2 *PlatformInformationRecord2 + ) +{ + EFI_HOB_GUID_TYPE *GuidHob; + VOID *DataInHob; + UINTN DataSize; + + GuidHob = GetFirstGuidHob (&gEfiSecPlatformInformation2PpiGuid); + if (GuidHob == NULL) { + *StructureSize = 0; + return EFI_SUCCESS; + } + + DataInHob = GET_GUID_HOB_DATA (GuidHob); + DataSize = GET_GUID_HOB_DATA_SIZE (GuidHob); + + // + // return the information from BistHob + // + if ((*StructureSize) < (UINT64) DataSize) { + *StructureSize = (UINT64) DataSize; + return EFI_BUFFER_TOO_SMALL; + } + + *StructureSize = (UINT64) DataSize; + CopyMem (PlatformInformationRecord2, DataInHob, DataSize); + return EFI_SUCCESS; +} + +/** + Worker function to get CPUs' BIST by calling SecPlatformInformationPpi + or SecPlatformInformation2Ppi. + + @param PeiServices Pointer to PEI Services Table + @param Guid PPI Guid + @param PpiDescriptor Return a pointer to instance of the + EFI_PEI_PPI_DESCRIPTOR + @param BistInformationData Pointer to BIST information data + @param BistInformationSize Return the size in bytes of BIST information + + @retval EFI_SUCCESS Retrieve of the BIST data successfully + @retval EFI_NOT_FOUND No sec platform information(2) ppi export + @retval EFI_DEVICE_ERROR Failed to get CPU Information + +**/ +EFI_STATUS +GetBistInfoFromPpi ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN CONST EFI_GUID *Guid, + OUT EFI_PEI_PPI_DESCRIPTOR **PpiDescriptor, + OUT VOID **BistInformationData, + OUT UINT64 *BistInformationSize OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_SEC_PLATFORM_INFORMATION2_PPI *SecPlatformInformation2Ppi; + EFI_SEC_PLATFORM_INFORMATION_RECORD2 *SecPlatformInformation2; + UINT64 InformationSize; + + Status = PeiServicesLocatePpi ( + Guid, // GUID + 0, // INSTANCE + PpiDescriptor, // EFI_PEI_PPI_DESCRIPTOR + (VOID **)&SecPlatformInformation2Ppi // PPI + ); + if (Status == EFI_NOT_FOUND) { + return EFI_NOT_FOUND; + } + + if (Status == EFI_SUCCESS) { + // + // Get the size of the sec platform information2(BSP/APs' BIST data) + // + InformationSize = 0; + SecPlatformInformation2 = NULL; + Status = SecPlatformInformation2Ppi->PlatformInformation2 ( + PeiServices, + &InformationSize, + SecPlatformInformation2 + ); + if (Status == EFI_BUFFER_TOO_SMALL) { + Status = PeiServicesAllocatePool ( + (UINTN) InformationSize, + (VOID **) &SecPlatformInformation2 + ); + if (Status == EFI_SUCCESS) { + // + // Retrieve BIST data + // + Status = SecPlatformInformation2Ppi->PlatformInformation2 ( + PeiServices, + &InformationSize, + SecPlatformInformation2 + ); + if (Status == EFI_SUCCESS) { + *BistInformationData = SecPlatformInformation2; + if (BistInformationSize != NULL) { + *BistInformationSize = InformationSize; + } + return EFI_SUCCESS; + } + } + } + } + + return EFI_DEVICE_ERROR; +} + +/** + Collects BIST data from PPI. + + This function collects BIST data from Sec Platform Information2 PPI + or SEC Platform Information PPI. + + @param PeiServices Pointer to PEI Services Table + +**/ +VOID +CollectBistDataFromPpi ( + IN CONST EFI_PEI_SERVICES **PeiServices + ) +{ + EFI_STATUS Status; + EFI_PEI_PPI_DESCRIPTOR *SecInformationDescriptor; + EFI_SEC_PLATFORM_INFORMATION_RECORD2 *SecPlatformInformation2; + EFI_SEC_PLATFORM_INFORMATION_RECORD *SecPlatformInformation; + UINTN NumberOfData; + EFI_SEC_PLATFORM_INFORMATION_CPU *CpuInstance; + EFI_SEC_PLATFORM_INFORMATION_CPU BspCpuInstance; + UINTN ProcessorNumber; + UINTN CpuIndex; + EFI_PROCESSOR_INFORMATION ProcessorInfo; + EFI_HEALTH_FLAGS BistData; + UINTN NumberOfProcessors; + UINTN NumberOfEnabledProcessors; + UINTN BistInformationSize; + EFI_SEC_PLATFORM_INFORMATION_RECORD2 *PlatformInformationRecord2; + EFI_SEC_PLATFORM_INFORMATION_CPU *CpuInstanceInHob; + + + MpInitLibGetNumberOfProcessors(&NumberOfProcessors, &NumberOfEnabledProcessors); + + BistInformationSize = sizeof (EFI_SEC_PLATFORM_INFORMATION_RECORD2) + + sizeof (EFI_SEC_PLATFORM_INFORMATION_CPU) * NumberOfProcessors; + Status = PeiServicesAllocatePool ( + (UINTN) BistInformationSize, + (VOID **) &PlatformInformationRecord2 + ); + ASSERT_EFI_ERROR (Status); + PlatformInformationRecord2->NumberOfCpus = (UINT32)NumberOfProcessors; + + SecPlatformInformation2 = NULL; + SecPlatformInformation = NULL; + NumberOfData = 0; + CpuInstance = NULL; + // + // Get BIST information from Sec Platform Information2 Ppi firstly + // + Status = GetBistInfoFromPpi ( + PeiServices, + &gEfiSecPlatformInformation2PpiGuid, + &SecInformationDescriptor, + (VOID *) &SecPlatformInformation2, + NULL + ); + if (Status == EFI_SUCCESS) { + // + // Sec Platform Information2 PPI includes BSP/APs' BIST information + // + NumberOfData = SecPlatformInformation2->NumberOfCpus; + CpuInstance = SecPlatformInformation2->CpuInstance; + } else { + // + // Otherwise, get BIST information from Sec Platform Information Ppi + // + Status = GetBistInfoFromPpi ( + PeiServices, + &gEfiSecPlatformInformationPpiGuid, + &SecInformationDescriptor, + (VOID *) &SecPlatformInformation, + NULL + ); + if (Status == EFI_SUCCESS) { + NumberOfData = 1; + // + // SEC Platform Information only includes BSP's BIST information + // and does not have BSP's APIC ID + // + BspCpuInstance.CpuLocation = GetInitialApicId (); + BspCpuInstance.InfoRecord.IA32HealthFlags.Uint32 = SecPlatformInformation->IA32HealthFlags.Uint32; + CpuInstance = &BspCpuInstance; + } else { + DEBUG ((EFI_D_INFO, "Does not find any stored CPU BIST information from PPI!\n")); + } + } + for (ProcessorNumber = 0; ProcessorNumber < NumberOfProcessors; ProcessorNumber ++) { + MpInitLibGetProcessorInfo (ProcessorNumber, &ProcessorInfo, &BistData); + for (CpuIndex = 0; CpuIndex < NumberOfData; CpuIndex ++) { + ASSERT (CpuInstance != NULL); + if (ProcessorInfo.ProcessorId == CpuInstance[CpuIndex].CpuLocation) { + // + // Update processor's BIST data if it is already stored before + // + BistData = CpuInstance[CpuIndex].InfoRecord.IA32HealthFlags; + } + } + if (BistData.Uint32 != 0) { + // + // Report Status Code that self test is failed + // + REPORT_STATUS_CODE ( + EFI_ERROR_CODE | EFI_ERROR_MAJOR, + (EFI_COMPUTING_UNIT_HOST_PROCESSOR | EFI_CU_HP_EC_SELF_TEST) + ); + } + DEBUG ((EFI_D_INFO, " APICID - 0x%08x, BIST - 0x%08x\n", + (UINT32) ProcessorInfo.ProcessorId, + BistData + )); + CpuInstanceInHob = PlatformInformationRecord2->CpuInstance; + CpuInstanceInHob[ProcessorNumber].CpuLocation = (UINT32) ProcessorInfo.ProcessorId; + CpuInstanceInHob[ProcessorNumber].InfoRecord.IA32HealthFlags = BistData; + } + + // + // Build SecPlatformInformation2 PPI GUIDed HOB that also could be consumed + // by CPU MP driver to get CPU BIST data + // + BuildGuidDataHob ( + &gEfiSecPlatformInformation2PpiGuid, + PlatformInformationRecord2, + (UINTN) BistInformationSize + ); + + if (SecPlatformInformation2 != NULL) { + if (NumberOfData < NumberOfProcessors) { + // + // Reinstall SecPlatformInformation2 PPI to include new BIST information + // + Status = PeiServicesReInstallPpi ( + SecInformationDescriptor, + &mPeiSecPlatformInformation2Ppi + ); + ASSERT_EFI_ERROR (Status); + } + } else { + // + // Install SecPlatformInformation2 PPI + // + Status = PeiServicesInstallPpi (&mPeiSecPlatformInformation2Ppi); + ASSERT_EFI_ERROR(Status); + } +} + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei/CpuMp2Pei.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei/CpuMp2Pei.c new file mode 100644 index 00000000..88176676 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei/CpuMp2Pei.c @@ -0,0 +1,417 @@ +/** @file + EDKII_PEI_MP_SERVICES2_PPI Implementation code. + + Copyright (c) 2019, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "CpuMpPei.h" + +/** + This service retrieves the number of logical processor in the platform + and the number of those logical processors that are enabled on this boot. + This service may only be called from the BSP. + + This function is used to retrieve the following information: + - The number of logical processors that are present in the system. + - The number of enabled logical processors in the system at the instant + this call is made. + + Because MP Service Ppi provides services to enable and disable processors + dynamically, the number of enabled logical processors may vary during the + course of a boot session. + + If this service is called from an AP, then EFI_DEVICE_ERROR is returned. + If NumberOfProcessors or NumberOfEnabledProcessors is NULL, then + EFI_INVALID_PARAMETER is returned. Otherwise, the total number of processors + is returned in NumberOfProcessors, the number of currently enabled processor + is returned in NumberOfEnabledProcessors, and EFI_SUCCESS is returned. + + @param[in] This Pointer to this instance of the PPI. + @param[out] NumberOfProcessors Pointer to the total number of logical processors in + the system, including the BSP and disabled APs. + @param[out] NumberOfEnabledProcessors + Number of processors in the system that are enabled. + + @retval EFI_SUCCESS The number of logical processors and enabled + logical processors was retrieved. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_INVALID_PARAMETER NumberOfProcessors is NULL. + NumberOfEnabledProcessors is NULL. +**/ +EFI_STATUS +EFIAPI +EdkiiPeiGetNumberOfProcessors ( + IN EDKII_PEI_MP_SERVICES2_PPI *This, + OUT UINTN *NumberOfProcessors, + OUT UINTN *NumberOfEnabledProcessors + ) +{ + if ((NumberOfProcessors == NULL) || (NumberOfEnabledProcessors == NULL)) { + return EFI_INVALID_PARAMETER; + } + + return MpInitLibGetNumberOfProcessors ( + NumberOfProcessors, + NumberOfEnabledProcessors + ); +} + +/** + Gets detailed MP-related information on the requested processor at the + instant this call is made. This service may only be called from the BSP. + + This service retrieves detailed MP-related information about any processor + on the platform. Note the following: + - The processor information may change during the course of a boot session. + - The information presented here is entirely MP related. + + Information regarding the number of caches and their sizes, frequency of operation, + slot numbers is all considered platform-related information and is not provided + by this service. + + @param[in] This Pointer to this instance of the PPI. + @param[in] ProcessorNumber Pointer to the total number of logical processors in + the system, including the BSP and disabled APs. + @param[out] ProcessorInfoBuffer Number of processors in the system that are enabled. + + @retval EFI_SUCCESS Processor information was returned. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_INVALID_PARAMETER ProcessorInfoBuffer is NULL. + @retval EFI_NOT_FOUND The processor with the handle specified by + ProcessorNumber does not exist in the platform. +**/ +EFI_STATUS +EFIAPI +EdkiiPeiGetProcessorInfo ( + IN EDKII_PEI_MP_SERVICES2_PPI *This, + IN UINTN ProcessorNumber, + OUT EFI_PROCESSOR_INFORMATION *ProcessorInfoBuffer + ) +{ + return MpInitLibGetProcessorInfo (ProcessorNumber, ProcessorInfoBuffer, NULL); +} + +/** + This service executes a caller provided function on all enabled APs. APs can + run either simultaneously or one at a time in sequence. This service supports + both blocking requests only. This service may only + be called from the BSP. + + This function is used to dispatch all the enabled APs to the function specified + by Procedure. If any enabled AP is busy, then EFI_NOT_READY is returned + immediately and Procedure is not started on any AP. + + If SingleThread is TRUE, all the enabled APs execute the function specified by + Procedure one by one, in ascending order of processor handle number. Otherwise, + all the enabled APs execute the function specified by Procedure simultaneously. + + If the timeout specified by TimeoutInMicroSeconds expires before all APs return + from Procedure, then Procedure on the failed APs is terminated. All enabled APs + are always available for further calls to EDKII_PEI_MP_SERVICES2_PPI.StartupAllAPs() + and EDKII_PEI_MP_SERVICES2_PPI.StartupThisAP(). If FailedCpuList is not NULL, its + content points to the list of processor handle numbers in which Procedure was + terminated. + + Note: It is the responsibility of the consumer of the EDKII_PEI_MP_SERVICES2_PPI.StartupAllAPs() + to make sure that the nature of the code that is executed on the BSP and the + dispatched APs is well controlled. The MP Services Ppi does not guarantee + that the Procedure function is MP-safe. Hence, the tasks that can be run in + parallel are limited to certain independent tasks and well-controlled exclusive + code. PEI services and Ppis may not be called by APs unless otherwise + specified. + + In blocking execution mode, BSP waits until all APs finish or + TimeoutInMicroSeconds expires. + + @param[in] This A pointer to the EDKII_PEI_MP_SERVICES2_PPI instance. + @param[in] Procedure A pointer to the function to be run on enabled APs of + the system. + @param[in] SingleThread If TRUE, then all the enabled APs execute the function + specified by Procedure one by one, in ascending order + of processor handle number. If FALSE, then all the + enabled APs execute the function specified by Procedure + simultaneously. + @param[in] TimeoutInMicroSeconds + Indicates the time limit in microseconds for APs to + return from Procedure, for blocking mode only. Zero + means infinity. If the timeout expires before all APs + return from Procedure, then Procedure on the failed APs + is terminated. All enabled APs are available for next + function assigned by EDKII_PEI_MP_SERVICES2_PPI.StartupAllAPs() + or EDKII_PEI_MP_SERVICES2_PPI.StartupThisAP(). If the + timeout expires in blocking mode, BSP returns + EFI_TIMEOUT. + @param[in] ProcedureArgument The parameter passed into Procedure for all APs. + + @retval EFI_SUCCESS In blocking mode, all APs have finished before the + timeout expired. + @retval EFI_DEVICE_ERROR Caller processor is AP. + @retval EFI_NOT_STARTED No enabled APs exist in the system. + @retval EFI_NOT_READY Any enabled APs are busy. + @retval EFI_TIMEOUT In blocking mode, the timeout expired before all + enabled APs have finished. + @retval EFI_INVALID_PARAMETER Procedure is NULL. +**/ +EFI_STATUS +EFIAPI +EdkiiPeiStartupAllAPs ( + IN EDKII_PEI_MP_SERVICES2_PPI *This, + IN EFI_AP_PROCEDURE Procedure, + IN BOOLEAN SingleThread, + IN UINTN TimeoutInMicroSeconds, + IN VOID *ProcedureArgument OPTIONAL + ) +{ + return MpInitLibStartupAllAPs ( + Procedure, + SingleThread, + NULL, + TimeoutInMicroSeconds, + ProcedureArgument, + NULL + ); +} + +/** + This service lets the caller get one enabled AP to execute a caller-provided + function. The caller can request the BSP to wait for the completion + of the AP. This service may only be called from the BSP. + + This function is used to dispatch one enabled AP to the function specified by + Procedure passing in the argument specified by ProcedureArgument. + The execution is in blocking mode. The BSP waits until the AP finishes or + TimeoutInMicroSecondss expires. + + If the timeout specified by TimeoutInMicroseconds expires before the AP returns + from Procedure, then execution of Procedure by the AP is terminated. The AP is + available for subsequent calls to EDKII_PEI_MP_SERVICES2_PPI.StartupAllAPs() and + EDKII_PEI_MP_SERVICES2_PPI.StartupThisAP(). + + @param[in] This A pointer to the EDKII_PEI_MP_SERVICES2_PPI instance. + @param[in] Procedure A pointer to the function to be run on enabled APs of + the system. + @param[in] ProcessorNumber The handle number of the AP. The range is from 0 to the + total number of logical processors minus 1. The total + number of logical processors can be retrieved by + EDKII_PEI_MP_SERVICES2_PPI.GetNumberOfProcessors(). + @param[in] TimeoutInMicroseconds + Indicates the time limit in microseconds for APs to + return from Procedure, for blocking mode only. Zero + means infinity. If the timeout expires before all APs + return from Procedure, then Procedure on the failed APs + is terminated. All enabled APs are available for next + function assigned by EDKII_PEI_MP_SERVICES2_PPI.StartupAllAPs() + or EDKII_PEI_MP_SERVICES2_PPI.StartupThisAP(). If the + timeout expires in blocking mode, BSP returns + EFI_TIMEOUT. + @param[in] ProcedureArgument The parameter passed into Procedure for all APs. + + @retval EFI_SUCCESS In blocking mode, specified AP finished before the + timeout expires. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_TIMEOUT In blocking mode, the timeout expired before the + specified AP has finished. + @retval EFI_NOT_FOUND The processor with the handle specified by + ProcessorNumber does not exist. + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP or disabled AP. + @retval EFI_INVALID_PARAMETER Procedure is NULL. +**/ +EFI_STATUS +EFIAPI +EdkiiPeiStartupThisAP ( + IN EDKII_PEI_MP_SERVICES2_PPI *This, + IN EFI_AP_PROCEDURE Procedure, + IN UINTN ProcessorNumber, + IN UINTN TimeoutInMicroseconds, + IN VOID *ProcedureArgument OPTIONAL + ) +{ + return MpInitLibStartupThisAP ( + Procedure, + ProcessorNumber, + NULL, + TimeoutInMicroseconds, + ProcedureArgument, + NULL + ); +} + +/** + This service switches the requested AP to be the BSP from that point onward. + This service changes the BSP for all purposes. This call can only be performed + by the current BSP. + + This service switches the requested AP to be the BSP from that point onward. + This service changes the BSP for all purposes. The new BSP can take over the + execution of the old BSP and continue seamlessly from where the old one left + off. + + If the BSP cannot be switched prior to the return from this service, then + EFI_UNSUPPORTED must be returned. + + @param[in] This A pointer to the EDKII_PEI_MP_SERVICES2_PPI instance. + @param[in] ProcessorNumber The handle number of the AP. The range is from 0 to the + total number of logical processors minus 1. The total + number of logical processors can be retrieved by + EDKII_PEI_MP_SERVICES2_PPI.GetNumberOfProcessors(). + @param[in] EnableOldBSP If TRUE, then the old BSP will be listed as an enabled + AP. Otherwise, it will be disabled. + + @retval EFI_SUCCESS BSP successfully switched. + @retval EFI_UNSUPPORTED Switching the BSP cannot be completed prior to this + service returning. + @retval EFI_UNSUPPORTED Switching the BSP is not supported. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_NOT_FOUND The processor with the handle specified by + ProcessorNumber does not exist. + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the current BSP or a disabled + AP. + @retval EFI_NOT_READY The specified AP is busy. +**/ +EFI_STATUS +EFIAPI +EdkiiPeiSwitchBSP ( + IN EDKII_PEI_MP_SERVICES2_PPI *This, + IN UINTN ProcessorNumber, + IN BOOLEAN EnableOldBSP + ) +{ + return MpInitLibSwitchBSP (ProcessorNumber, EnableOldBSP); +} + +/** + This service lets the caller enable or disable an AP from this point onward. + This service may only be called from the BSP. + + This service allows the caller enable or disable an AP from this point onward. + The caller can optionally specify the health status of the AP by Health. If + an AP is being disabled, then the state of the disabled AP is implementation + dependent. If an AP is enabled, then the implementation must guarantee that a + complete initialization sequence is performed on the AP, so the AP is in a state + that is compatible with an MP operating system. + + If the enable or disable AP operation cannot be completed prior to the return + from this service, then EFI_UNSUPPORTED must be returned. + + @param[in] This A pointer to the EDKII_PEI_MP_SERVICES2_PPI instance. + @param[in] ProcessorNumber The handle number of the AP. The range is from 0 to the + total number of logical processors minus 1. The total + number of logical processors can be retrieved by + EDKII_PEI_MP_SERVICES2_PPI.GetNumberOfProcessors(). + @param[in] EnableAP Specifies the new state for the processor for enabled, + FALSE for disabled. + @param[in] HealthFlag If not NULL, a pointer to a value that specifies the + new health status of the AP. This flag corresponds to + StatusFlag defined in EDKII_PEI_MP_SERVICES2_PPI.GetProcessorInfo(). + Only the PROCESSOR_HEALTH_STATUS_BIT is used. All other + bits are ignored. If it is NULL, this parameter is + ignored. + + @retval EFI_SUCCESS The specified AP was enabled or disabled successfully. + @retval EFI_UNSUPPORTED Enabling or disabling an AP cannot be completed prior + to this service returning. + @retval EFI_UNSUPPORTED Enabling or disabling an AP is not supported. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_NOT_FOUND Processor with the handle specified by ProcessorNumber + does not exist. + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP. +**/ +EFI_STATUS +EFIAPI +EdkiiPeiEnableDisableAP ( + IN EDKII_PEI_MP_SERVICES2_PPI *This, + IN UINTN ProcessorNumber, + IN BOOLEAN EnableAP, + IN UINT32 *HealthFlag OPTIONAL + ) +{ + return MpInitLibEnableDisableAP (ProcessorNumber, EnableAP, HealthFlag); +} + +/** + This return the handle number for the calling processor. This service may be + called from the BSP and APs. + + This service returns the processor handle number for the calling processor. + The returned value is in the range from 0 to the total number of logical + processors minus 1. The total number of logical processors can be retrieved + with EDKII_PEI_MP_SERVICES2_PPI.GetNumberOfProcessors(). This service may be + called from the BSP and APs. If ProcessorNumber is NULL, then EFI_INVALID_PARAMETER + is returned. Otherwise, the current processors handle number is returned in + ProcessorNumber, and EFI_SUCCESS is returned. + + @param[in] This A pointer to the EDKII_PEI_MP_SERVICES2_PPI instance. + @param[out] ProcessorNumber The handle number of the AP. The range is from 0 to the + total number of logical processors minus 1. The total + number of logical processors can be retrieved by + EDKII_PEI_MP_SERVICES2_PPI.GetNumberOfProcessors(). + + @retval EFI_SUCCESS The current processor handle number was returned in + ProcessorNumber. + @retval EFI_INVALID_PARAMETER ProcessorNumber is NULL. +**/ +EFI_STATUS +EFIAPI +EdkiiPeiWhoAmI ( + IN EDKII_PEI_MP_SERVICES2_PPI *This, + OUT UINTN *ProcessorNumber + ) +{ + return MpInitLibWhoAmI (ProcessorNumber); +} + +/** + This service executes a caller provided function on all enabled CPUs. CPUs can + run either simultaneously or one at a time in sequence. This service may only + be called from the BSP. + + @param[in] This A pointer to the EDKII_PEI_MP_SERVICES2_PPI instance. + @param[in] Procedure A pointer to the function to be run on enabled APs of + the system. + @param[in] TimeoutInMicroSeconds + Indicates the time limit in microseconds for APs to + return from Procedure, for blocking mode only. Zero + means infinity. If the timeout expires in blocking + mode, BSP returns EFI_TIMEOUT. + @param[in] ProcedureArgument The parameter passed into Procedure for all CPUs. + + @retval EFI_SUCCESS In blocking mode, all APs have finished before the + timeout expired. + @retval EFI_DEVICE_ERROR Caller processor is AP. + @retval EFI_NOT_READY Any enabled APs are busy. + @retval EFI_TIMEOUT In blocking mode, the timeout expired before all + enabled APs have finished. + @retval EFI_INVALID_PARAMETER Procedure is NULL. +**/ +EFI_STATUS +EFIAPI +EdkiiPeiStartupAllCPUs ( + IN EDKII_PEI_MP_SERVICES2_PPI *This, + IN EFI_AP_PROCEDURE Procedure, + IN UINTN TimeoutInMicroSeconds, + IN VOID *ProcedureArgument OPTIONAL + ) +{ + return MpInitLibStartupAllCPUs ( + Procedure, + TimeoutInMicroSeconds, + ProcedureArgument + ); +} + +// +// CPU MP2 PPI to be installed +// +EDKII_PEI_MP_SERVICES2_PPI mMpServices2Ppi = { + EdkiiPeiGetNumberOfProcessors, + EdkiiPeiGetProcessorInfo, + EdkiiPeiStartupAllAPs, + EdkiiPeiStartupThisAP, + EdkiiPeiSwitchBSP, + EdkiiPeiEnableDisableAP, + EdkiiPeiWhoAmI, + EdkiiPeiStartupAllCPUs +}; + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei/CpuMpPei.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei/CpuMpPei.c new file mode 100644 index 00000000..07b950f3 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei/CpuMpPei.c @@ -0,0 +1,710 @@ +/** @file + CPU PEI Module installs CPU Multiple Processor PPI. + + Copyright (c) 2015 - 2021, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "CpuMpPei.h" + +extern EDKII_PEI_MP_SERVICES2_PPI mMpServices2Ppi; + +// +// CPU MP PPI to be installed +// +EFI_PEI_MP_SERVICES_PPI mMpServicesPpi = { + PeiGetNumberOfProcessors, + PeiGetProcessorInfo, + PeiStartupAllAPs, + PeiStartupThisAP, + PeiSwitchBSP, + PeiEnableDisableAP, + PeiWhoAmI, +}; + +EFI_PEI_PPI_DESCRIPTOR mPeiCpuMpPpiList[] = { + { + EFI_PEI_PPI_DESCRIPTOR_PPI, + &gEdkiiPeiMpServices2PpiGuid, + &mMpServices2Ppi + }, + { + (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), + &gEfiPeiMpServicesPpiGuid, + &mMpServicesPpi + } +}; + +/** + This service retrieves the number of logical processor in the platform + and the number of those logical processors that are enabled on this boot. + This service may only be called from the BSP. + + This function is used to retrieve the following information: + - The number of logical processors that are present in the system. + - The number of enabled logical processors in the system at the instant + this call is made. + + Because MP Service Ppi provides services to enable and disable processors + dynamically, the number of enabled logical processors may vary during the + course of a boot session. + + If this service is called from an AP, then EFI_DEVICE_ERROR is returned. + If NumberOfProcessors or NumberOfEnabledProcessors is NULL, then + EFI_INVALID_PARAMETER is returned. Otherwise, the total number of processors + is returned in NumberOfProcessors, the number of currently enabled processor + is returned in NumberOfEnabledProcessors, and EFI_SUCCESS is returned. + + @param[in] PeiServices An indirect pointer to the PEI Services Table + published by the PEI Foundation. + @param[in] This Pointer to this instance of the PPI. + @param[out] NumberOfProcessors Pointer to the total number of logical processors in + the system, including the BSP and disabled APs. + @param[out] NumberOfEnabledProcessors + Number of processors in the system that are enabled. + + @retval EFI_SUCCESS The number of logical processors and enabled + logical processors was retrieved. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_INVALID_PARAMETER NumberOfProcessors is NULL. + NumberOfEnabledProcessors is NULL. +**/ +EFI_STATUS +EFIAPI +PeiGetNumberOfProcessors ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_MP_SERVICES_PPI *This, + OUT UINTN *NumberOfProcessors, + OUT UINTN *NumberOfEnabledProcessors + ) +{ + if ((NumberOfProcessors == NULL) || (NumberOfEnabledProcessors == NULL)) { + return EFI_INVALID_PARAMETER; + } + + return MpInitLibGetNumberOfProcessors ( + NumberOfProcessors, + NumberOfEnabledProcessors + ); +} + +/** + Gets detailed MP-related information on the requested processor at the + instant this call is made. This service may only be called from the BSP. + + This service retrieves detailed MP-related information about any processor + on the platform. Note the following: + - The processor information may change during the course of a boot session. + - The information presented here is entirely MP related. + + Information regarding the number of caches and their sizes, frequency of operation, + slot numbers is all considered platform-related information and is not provided + by this service. + + @param[in] PeiServices An indirect pointer to the PEI Services Table + published by the PEI Foundation. + @param[in] This Pointer to this instance of the PPI. + @param[in] ProcessorNumber Pointer to the total number of logical processors in + the system, including the BSP and disabled APs. + @param[out] ProcessorInfoBuffer Number of processors in the system that are enabled. + + @retval EFI_SUCCESS Processor information was returned. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_INVALID_PARAMETER ProcessorInfoBuffer is NULL. + @retval EFI_NOT_FOUND The processor with the handle specified by + ProcessorNumber does not exist in the platform. +**/ +EFI_STATUS +EFIAPI +PeiGetProcessorInfo ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_MP_SERVICES_PPI *This, + IN UINTN ProcessorNumber, + OUT EFI_PROCESSOR_INFORMATION *ProcessorInfoBuffer + ) +{ + return MpInitLibGetProcessorInfo (ProcessorNumber, ProcessorInfoBuffer, NULL); +} + +/** + This service executes a caller provided function on all enabled APs. APs can + run either simultaneously or one at a time in sequence. This service supports + both blocking requests only. This service may only + be called from the BSP. + + This function is used to dispatch all the enabled APs to the function specified + by Procedure. If any enabled AP is busy, then EFI_NOT_READY is returned + immediately and Procedure is not started on any AP. + + If SingleThread is TRUE, all the enabled APs execute the function specified by + Procedure one by one, in ascending order of processor handle number. Otherwise, + all the enabled APs execute the function specified by Procedure simultaneously. + + If the timeout specified by TimeoutInMicroSeconds expires before all APs return + from Procedure, then Procedure on the failed APs is terminated. All enabled APs + are always available for further calls to EFI_PEI_MP_SERVICES_PPI.StartupAllAPs() + and EFI_PEI_MP_SERVICES_PPI.StartupThisAP(). If FailedCpuList is not NULL, its + content points to the list of processor handle numbers in which Procedure was + terminated. + + Note: It is the responsibility of the consumer of the EFI_PEI_MP_SERVICES_PPI.StartupAllAPs() + to make sure that the nature of the code that is executed on the BSP and the + dispatched APs is well controlled. The MP Services Ppi does not guarantee + that the Procedure function is MP-safe. Hence, the tasks that can be run in + parallel are limited to certain independent tasks and well-controlled exclusive + code. PEI services and Ppis may not be called by APs unless otherwise + specified. + + In blocking execution mode, BSP waits until all APs finish or + TimeoutInMicroSeconds expires. + + @param[in] PeiServices An indirect pointer to the PEI Services Table + published by the PEI Foundation. + @param[in] This A pointer to the EFI_PEI_MP_SERVICES_PPI instance. + @param[in] Procedure A pointer to the function to be run on enabled APs of + the system. + @param[in] SingleThread If TRUE, then all the enabled APs execute the function + specified by Procedure one by one, in ascending order + of processor handle number. If FALSE, then all the + enabled APs execute the function specified by Procedure + simultaneously. + @param[in] TimeoutInMicroSeconds + Indicates the time limit in microseconds for APs to + return from Procedure, for blocking mode only. Zero + means infinity. If the timeout expires before all APs + return from Procedure, then Procedure on the failed APs + is terminated. All enabled APs are available for next + function assigned by EFI_PEI_MP_SERVICES_PPI.StartupAllAPs() + or EFI_PEI_MP_SERVICES_PPI.StartupThisAP(). If the + timeout expires in blocking mode, BSP returns + EFI_TIMEOUT. + @param[in] ProcedureArgument The parameter passed into Procedure for all APs. + + @retval EFI_SUCCESS In blocking mode, all APs have finished before the + timeout expired. + @retval EFI_DEVICE_ERROR Caller processor is AP. + @retval EFI_NOT_STARTED No enabled APs exist in the system. + @retval EFI_NOT_READY Any enabled APs are busy. + @retval EFI_TIMEOUT In blocking mode, the timeout expired before all + enabled APs have finished. + @retval EFI_INVALID_PARAMETER Procedure is NULL. +**/ +EFI_STATUS +EFIAPI +PeiStartupAllAPs ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_MP_SERVICES_PPI *This, + IN EFI_AP_PROCEDURE Procedure, + IN BOOLEAN SingleThread, + IN UINTN TimeoutInMicroSeconds, + IN VOID *ProcedureArgument OPTIONAL + ) +{ + return MpInitLibStartupAllAPs ( + Procedure, + SingleThread, + NULL, + TimeoutInMicroSeconds, + ProcedureArgument, + NULL + ); +} + +/** + This service lets the caller get one enabled AP to execute a caller-provided + function. The caller can request the BSP to wait for the completion + of the AP. This service may only be called from the BSP. + + This function is used to dispatch one enabled AP to the function specified by + Procedure passing in the argument specified by ProcedureArgument. + The execution is in blocking mode. The BSP waits until the AP finishes or + TimeoutInMicroSecondss expires. + + If the timeout specified by TimeoutInMicroseconds expires before the AP returns + from Procedure, then execution of Procedure by the AP is terminated. The AP is + available for subsequent calls to EFI_PEI_MP_SERVICES_PPI.StartupAllAPs() and + EFI_PEI_MP_SERVICES_PPI.StartupThisAP(). + + @param[in] PeiServices An indirect pointer to the PEI Services Table + published by the PEI Foundation. + @param[in] This A pointer to the EFI_PEI_MP_SERVICES_PPI instance. + @param[in] Procedure A pointer to the function to be run on enabled APs of + the system. + @param[in] ProcessorNumber The handle number of the AP. The range is from 0 to the + total number of logical processors minus 1. The total + number of logical processors can be retrieved by + EFI_PEI_MP_SERVICES_PPI.GetNumberOfProcessors(). + @param[in] TimeoutInMicroseconds + Indicates the time limit in microseconds for APs to + return from Procedure, for blocking mode only. Zero + means infinity. If the timeout expires before all APs + return from Procedure, then Procedure on the failed APs + is terminated. All enabled APs are available for next + function assigned by EFI_PEI_MP_SERVICES_PPI.StartupAllAPs() + or EFI_PEI_MP_SERVICES_PPI.StartupThisAP(). If the + timeout expires in blocking mode, BSP returns + EFI_TIMEOUT. + @param[in] ProcedureArgument The parameter passed into Procedure for all APs. + + @retval EFI_SUCCESS In blocking mode, specified AP finished before the + timeout expires. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_TIMEOUT In blocking mode, the timeout expired before the + specified AP has finished. + @retval EFI_NOT_FOUND The processor with the handle specified by + ProcessorNumber does not exist. + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP or disabled AP. + @retval EFI_INVALID_PARAMETER Procedure is NULL. +**/ +EFI_STATUS +EFIAPI +PeiStartupThisAP ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_MP_SERVICES_PPI *This, + IN EFI_AP_PROCEDURE Procedure, + IN UINTN ProcessorNumber, + IN UINTN TimeoutInMicroseconds, + IN VOID *ProcedureArgument OPTIONAL + ) +{ + return MpInitLibStartupThisAP ( + Procedure, + ProcessorNumber, + NULL, + TimeoutInMicroseconds, + ProcedureArgument, + NULL + ); +} + +/** + This service switches the requested AP to be the BSP from that point onward. + This service changes the BSP for all purposes. This call can only be performed + by the current BSP. + + This service switches the requested AP to be the BSP from that point onward. + This service changes the BSP for all purposes. The new BSP can take over the + execution of the old BSP and continue seamlessly from where the old one left + off. + + If the BSP cannot be switched prior to the return from this service, then + EFI_UNSUPPORTED must be returned. + + @param[in] PeiServices An indirect pointer to the PEI Services Table + published by the PEI Foundation. + @param[in] This A pointer to the EFI_PEI_MP_SERVICES_PPI instance. + @param[in] ProcessorNumber The handle number of the AP. The range is from 0 to the + total number of logical processors minus 1. The total + number of logical processors can be retrieved by + EFI_PEI_MP_SERVICES_PPI.GetNumberOfProcessors(). + @param[in] EnableOldBSP If TRUE, then the old BSP will be listed as an enabled + AP. Otherwise, it will be disabled. + + @retval EFI_SUCCESS BSP successfully switched. + @retval EFI_UNSUPPORTED Switching the BSP cannot be completed prior to this + service returning. + @retval EFI_UNSUPPORTED Switching the BSP is not supported. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_NOT_FOUND The processor with the handle specified by + ProcessorNumber does not exist. + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the current BSP or a disabled + AP. + @retval EFI_NOT_READY The specified AP is busy. +**/ +EFI_STATUS +EFIAPI +PeiSwitchBSP ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_MP_SERVICES_PPI *This, + IN UINTN ProcessorNumber, + IN BOOLEAN EnableOldBSP + ) +{ + return MpInitLibSwitchBSP (ProcessorNumber, EnableOldBSP); +} + +/** + This service lets the caller enable or disable an AP from this point onward. + This service may only be called from the BSP. + + This service allows the caller enable or disable an AP from this point onward. + The caller can optionally specify the health status of the AP by Health. If + an AP is being disabled, then the state of the disabled AP is implementation + dependent. If an AP is enabled, then the implementation must guarantee that a + complete initialization sequence is performed on the AP, so the AP is in a state + that is compatible with an MP operating system. + + If the enable or disable AP operation cannot be completed prior to the return + from this service, then EFI_UNSUPPORTED must be returned. + + @param[in] PeiServices An indirect pointer to the PEI Services Table + published by the PEI Foundation. + @param[in] This A pointer to the EFI_PEI_MP_SERVICES_PPI instance. + @param[in] ProcessorNumber The handle number of the AP. The range is from 0 to the + total number of logical processors minus 1. The total + number of logical processors can be retrieved by + EFI_PEI_MP_SERVICES_PPI.GetNumberOfProcessors(). + @param[in] EnableAP Specifies the new state for the processor for enabled, + FALSE for disabled. + @param[in] HealthFlag If not NULL, a pointer to a value that specifies the + new health status of the AP. This flag corresponds to + StatusFlag defined in EFI_PEI_MP_SERVICES_PPI.GetProcessorInfo(). + Only the PROCESSOR_HEALTH_STATUS_BIT is used. All other + bits are ignored. If it is NULL, this parameter is + ignored. + + @retval EFI_SUCCESS The specified AP was enabled or disabled successfully. + @retval EFI_UNSUPPORTED Enabling or disabling an AP cannot be completed prior + to this service returning. + @retval EFI_UNSUPPORTED Enabling or disabling an AP is not supported. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_NOT_FOUND Processor with the handle specified by ProcessorNumber + does not exist. + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP. +**/ +EFI_STATUS +EFIAPI +PeiEnableDisableAP ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_MP_SERVICES_PPI *This, + IN UINTN ProcessorNumber, + IN BOOLEAN EnableAP, + IN UINT32 *HealthFlag OPTIONAL + ) +{ + return MpInitLibEnableDisableAP (ProcessorNumber, EnableAP, HealthFlag); +} + +/** + This return the handle number for the calling processor. This service may be + called from the BSP and APs. + + This service returns the processor handle number for the calling processor. + The returned value is in the range from 0 to the total number of logical + processors minus 1. The total number of logical processors can be retrieved + with EFI_PEI_MP_SERVICES_PPI.GetNumberOfProcessors(). This service may be + called from the BSP and APs. If ProcessorNumber is NULL, then EFI_INVALID_PARAMETER + is returned. Otherwise, the current processors handle number is returned in + ProcessorNumber, and EFI_SUCCESS is returned. + + @param[in] PeiServices An indirect pointer to the PEI Services Table + published by the PEI Foundation. + @param[in] This A pointer to the EFI_PEI_MP_SERVICES_PPI instance. + @param[out] ProcessorNumber The handle number of the AP. The range is from 0 to the + total number of logical processors minus 1. The total + number of logical processors can be retrieved by + EFI_PEI_MP_SERVICES_PPI.GetNumberOfProcessors(). + + @retval EFI_SUCCESS The current processor handle number was returned in + ProcessorNumber. + @retval EFI_INVALID_PARAMETER ProcessorNumber is NULL. +**/ +EFI_STATUS +EFIAPI +PeiWhoAmI ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_MP_SERVICES_PPI *This, + OUT UINTN *ProcessorNumber + ) +{ + return MpInitLibWhoAmI (ProcessorNumber); +} + +/** + Get GDT register value. + + This function is mainly for AP purpose because AP may have different GDT + table than BSP. + + @param[in,out] Buffer The pointer to private data buffer. + +**/ +VOID +EFIAPI +GetGdtr ( + IN OUT VOID *Buffer + ) +{ + AsmReadGdtr ((IA32_DESCRIPTOR *)Buffer); +} + +/** + Initializes CPU exceptions handlers for the sake of stack switch requirement. + + This function is a wrapper of InitializeCpuExceptionHandlersEx. It's mainly + for the sake of AP's init because of EFI_AP_PROCEDURE API requirement. + + @param[in,out] Buffer The pointer to private data buffer. + +**/ +VOID +EFIAPI +InitializeExceptionStackSwitchHandlers ( + IN OUT VOID *Buffer + ) +{ + CPU_EXCEPTION_INIT_DATA *EssData; + IA32_DESCRIPTOR Idtr; + EFI_STATUS Status; + + EssData = Buffer; + // + // We don't plan to replace IDT table with a new one, but we should not assume + // the AP's IDT is the same as BSP's IDT either. + // + AsmReadIdtr (&Idtr); + EssData->Ia32.IdtTable = (VOID *)Idtr.Base; + EssData->Ia32.IdtTableSize = Idtr.Limit + 1; + Status = InitializeCpuExceptionHandlersEx (NULL, EssData); + ASSERT_EFI_ERROR (Status); +} + +/** + Initializes MP exceptions handlers for the sake of stack switch requirement. + + This function will allocate required resources required to setup stack switch + and pass them through CPU_EXCEPTION_INIT_DATA to each logic processor. + +**/ +VOID +InitializeMpExceptionStackSwitchHandlers ( + VOID + ) +{ + EFI_STATUS Status; + UINTN Index; + UINTN Bsp; + UINTN ExceptionNumber; + UINTN OldGdtSize; + UINTN NewGdtSize; + UINTN NewStackSize; + IA32_DESCRIPTOR Gdtr; + CPU_EXCEPTION_INIT_DATA EssData; + UINT8 *GdtBuffer; + UINT8 *StackTop; + UINTN NumberOfProcessors; + + if (!PcdGetBool (PcdCpuStackGuard)) { + return; + } + + MpInitLibGetNumberOfProcessors(&NumberOfProcessors, NULL); + MpInitLibWhoAmI (&Bsp); + + ExceptionNumber = FixedPcdGetSize (PcdCpuStackSwitchExceptionList); + NewStackSize = FixedPcdGet32 (PcdCpuKnownGoodStackSize) * ExceptionNumber; + + StackTop = AllocatePages (EFI_SIZE_TO_PAGES (NewStackSize * NumberOfProcessors)); + ASSERT(StackTop != NULL); + if (StackTop == NULL) { + return; + } + StackTop += NewStackSize * NumberOfProcessors; + + // + // The default exception handlers must have been initialized. Let's just skip + // it in this method. + // + EssData.Ia32.Revision = CPU_EXCEPTION_INIT_DATA_REV; + EssData.Ia32.InitDefaultHandlers = FALSE; + + EssData.Ia32.StackSwitchExceptions = FixedPcdGetPtr(PcdCpuStackSwitchExceptionList); + EssData.Ia32.StackSwitchExceptionNumber = ExceptionNumber; + EssData.Ia32.KnownGoodStackSize = FixedPcdGet32(PcdCpuKnownGoodStackSize); + + // + // Initialize Gdtr to suppress incorrect compiler/analyzer warnings. + // + Gdtr.Base = 0; + Gdtr.Limit = 0; + for (Index = 0; Index < NumberOfProcessors; ++Index) { + // + // To support stack switch, we need to re-construct GDT but not IDT. + // + if (Index == Bsp) { + GetGdtr(&Gdtr); + } else { + // + // AP might have different size of GDT from BSP. + // + MpInitLibStartupThisAP (GetGdtr, Index, NULL, 0, (VOID *)&Gdtr, NULL); + } + + // + // X64 needs only one TSS of current task working for all exceptions + // because of its IST feature. IA32 needs one TSS for each exception + // in addition to current task. Since AP is not supposed to allocate + // memory, we have to do it in BSP. To simplify the code, we allocate + // memory for IA32 case to cover both IA32 and X64 exception stack + // switch. + // + // Layout of memory to allocate for each processor: + // -------------------------------- + // | Alignment | (just in case) + // -------------------------------- + // | | + // | Original GDT | + // | | + // -------------------------------- + // | Current task descriptor | + // -------------------------------- + // | | + // | Exception task descriptors | X ExceptionNumber + // | | + // -------------------------------- + // | Current task-state segment | + // -------------------------------- + // | | + // | Exception task-state segment | X ExceptionNumber + // | | + // -------------------------------- + // + OldGdtSize = Gdtr.Limit + 1; + EssData.Ia32.ExceptionTssDescSize = sizeof (IA32_TSS_DESCRIPTOR) * + (ExceptionNumber + 1); + EssData.Ia32.ExceptionTssSize = sizeof (IA32_TASK_STATE_SEGMENT) * + (ExceptionNumber + 1); + NewGdtSize = sizeof (IA32_TSS_DESCRIPTOR) + + OldGdtSize + + EssData.Ia32.ExceptionTssDescSize + + EssData.Ia32.ExceptionTssSize; + + Status = PeiServicesAllocatePool ( + NewGdtSize, + (VOID **)&GdtBuffer + ); + ASSERT (GdtBuffer != NULL); + if (EFI_ERROR (Status)) { + ASSERT_EFI_ERROR (Status); + return; + } + + // + // Make sure GDT table alignment + // + EssData.Ia32.GdtTable = ALIGN_POINTER(GdtBuffer, sizeof (IA32_TSS_DESCRIPTOR)); + NewGdtSize -= ((UINT8 *)EssData.Ia32.GdtTable - GdtBuffer); + EssData.Ia32.GdtTableSize = NewGdtSize; + + EssData.Ia32.ExceptionTssDesc = ((UINT8 *)EssData.Ia32.GdtTable + OldGdtSize); + EssData.Ia32.ExceptionTss = ((UINT8 *)EssData.Ia32.GdtTable + OldGdtSize + + EssData.Ia32.ExceptionTssDescSize); + + EssData.Ia32.KnownGoodStackTop = (UINTN)StackTop; + DEBUG ((DEBUG_INFO, + "Exception stack top[cpu%lu]: 0x%lX\n", + (UINT64)(UINTN)Index, + (UINT64)(UINTN)StackTop)); + + if (Index == Bsp) { + InitializeExceptionStackSwitchHandlers (&EssData); + } else { + MpInitLibStartupThisAP ( + InitializeExceptionStackSwitchHandlers, + Index, + NULL, + 0, + (VOID *)&EssData, + NULL + ); + } + + StackTop -= NewStackSize; + } +} + +/** + Initializes MP and exceptions handlers. + + @param PeiServices The pointer to the PEI Services Table. + + @retval EFI_SUCCESS MP was successfully initialized. + @retval others Error occurred in MP initialization. + +**/ +EFI_STATUS +InitializeCpuMpWorker ( + IN CONST EFI_PEI_SERVICES **PeiServices + ) +{ + EFI_STATUS Status; + EFI_VECTOR_HANDOFF_INFO *VectorInfo; + EFI_PEI_VECTOR_HANDOFF_INFO_PPI *VectorHandoffInfoPpi; + + // + // Get Vector Hand-off Info PPI + // + VectorInfo = NULL; + Status = PeiServicesLocatePpi ( + &gEfiVectorHandoffInfoPpiGuid, + 0, + NULL, + (VOID **)&VectorHandoffInfoPpi + ); + if (Status == EFI_SUCCESS) { + VectorInfo = VectorHandoffInfoPpi->Info; + } + + // + // Initialize default handlers + // + Status = InitializeCpuExceptionHandlers (VectorInfo); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = MpInitLibInitialize (); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Special initialization for the sake of Stack Guard + // + InitializeMpExceptionStackSwitchHandlers (); + + // + // Update and publish CPU BIST information + // + CollectBistDataFromPpi (PeiServices); + + // + // Install CPU MP PPI + // + Status = PeiServicesInstallPpi(mPeiCpuMpPpiList); + ASSERT_EFI_ERROR (Status); + + return Status; +} + +/** + The Entry point of the MP CPU PEIM. + + This function will wakeup APs and collect CPU AP count and install the + Mp Service Ppi. + + @param FileHandle Handle of the file being invoked. + @param PeiServices Describes the list of possible PEI Services. + + @retval EFI_SUCCESS MpServicePpi is installed successfully. + +**/ +EFI_STATUS +EFIAPI +CpuMpPeimInit ( + IN EFI_PEI_FILE_HANDLE FileHandle, + IN CONST EFI_PEI_SERVICES **PeiServices + ) +{ + EFI_STATUS Status; + + // + // For the sake of special initialization needing to be done right after + // memory discovery. + // + Status = PeiServicesNotifyPpi (&mPostMemNotifyList[0]); + ASSERT_EFI_ERROR (Status); + + return Status; +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei/CpuMpPei.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei/CpuMpPei.h new file mode 100644 index 00000000..1a6068f4 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei/CpuMpPei.h @@ -0,0 +1,450 @@ +/** @file + Definitions to install Multiple Processor PPI. + + Copyright (c) 2015 - 2021, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _CPU_MP_PEI_H_ +#define _CPU_MP_PEI_H_ + +#include <PiPei.h> + +#include <Ppi/MpServices.h> +#include <Ppi/SecPlatformInformation.h> +#include <Ppi/SecPlatformInformation2.h> +#include <Ppi/EndOfPeiPhase.h> +#include <Ppi/MpServices2.h> + +#include <Library/BaseLib.h> +#include <Library/DebugLib.h> +#include <Library/HobLib.h> +#include <Library/LocalApicLib.h> +#include <Library/PeimEntryPoint.h> +#include <Library/PeiServicesLib.h> +#include <Library/ReportStatusCodeLib.h> +#include <Library/CpuExceptionHandlerLib.h> +#include <Library/MpInitLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/MemoryAllocationLib.h> + +extern EFI_PEI_PPI_DESCRIPTOR mPeiCpuMpPpiDesc; + +/** + This service retrieves the number of logical processor in the platform + and the number of those logical processors that are enabled on this boot. + This service may only be called from the BSP. + + This function is used to retrieve the following information: + - The number of logical processors that are present in the system. + - The number of enabled logical processors in the system at the instant + this call is made. + + Because MP Service Ppi provides services to enable and disable processors + dynamically, the number of enabled logical processors may vary during the + course of a boot session. + + If this service is called from an AP, then EFI_DEVICE_ERROR is returned. + If NumberOfProcessors or NumberOfEnabledProcessors is NULL, then + EFI_INVALID_PARAMETER is returned. Otherwise, the total number of processors + is returned in NumberOfProcessors, the number of currently enabled processor + is returned in NumberOfEnabledProcessors, and EFI_SUCCESS is returned. + + @param[in] PeiServices An indirect pointer to the PEI Services Table + published by the PEI Foundation. + @param[in] This Pointer to this instance of the PPI. + @param[out] NumberOfProcessors Pointer to the total number of logical processors in + the system, including the BSP and disabled APs. + @param[out] NumberOfEnabledProcessors + Number of processors in the system that are enabled. + + @retval EFI_SUCCESS The number of logical processors and enabled + logical processors was retrieved. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_INVALID_PARAMETER NumberOfProcessors is NULL. + NumberOfEnabledProcessors is NULL. +**/ +EFI_STATUS +EFIAPI +PeiGetNumberOfProcessors ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_MP_SERVICES_PPI *This, + OUT UINTN *NumberOfProcessors, + OUT UINTN *NumberOfEnabledProcessors + ); + +/** + Gets detailed MP-related information on the requested processor at the + instant this call is made. This service may only be called from the BSP. + + This service retrieves detailed MP-related information about any processor + on the platform. Note the following: + - The processor information may change during the course of a boot session. + - The information presented here is entirely MP related. + + Information regarding the number of caches and their sizes, frequency of operation, + slot numbers is all considered platform-related information and is not provided + by this service. + + @param[in] PeiServices An indirect pointer to the PEI Services Table + published by the PEI Foundation. + @param[in] This Pointer to this instance of the PPI. + @param[in] ProcessorNumber Pointer to the total number of logical processors in + the system, including the BSP and disabled APs. + @param[out] ProcessorInfoBuffer Number of processors in the system that are enabled. + + @retval EFI_SUCCESS Processor information was returned. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_INVALID_PARAMETER ProcessorInfoBuffer is NULL. + @retval EFI_NOT_FOUND The processor with the handle specified by + ProcessorNumber does not exist in the platform. +**/ +EFI_STATUS +EFIAPI +PeiGetProcessorInfo ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_MP_SERVICES_PPI *This, + IN UINTN ProcessorNumber, + OUT EFI_PROCESSOR_INFORMATION *ProcessorInfoBuffer + ); + +/** + This service executes a caller provided function on all enabled APs. APs can + run either simultaneously or one at a time in sequence. This service supports + both blocking requests only. This service may only + be called from the BSP. + + This function is used to dispatch all the enabled APs to the function specified + by Procedure. If any enabled AP is busy, then EFI_NOT_READY is returned + immediately and Procedure is not started on any AP. + + If SingleThread is TRUE, all the enabled APs execute the function specified by + Procedure one by one, in ascending order of processor handle number. Otherwise, + all the enabled APs execute the function specified by Procedure simultaneously. + + If the timeout specified by TimeoutInMicroSeconds expires before all APs return + from Procedure, then Procedure on the failed APs is terminated. All enabled APs + are always available for further calls to EFI_PEI_MP_SERVICES_PPI.StartupAllAPs() + and EFI_PEI_MP_SERVICES_PPI.StartupThisAP(). If FailedCpuList is not NULL, its + content points to the list of processor handle numbers in which Procedure was + terminated. + + Note: It is the responsibility of the consumer of the EFI_PEI_MP_SERVICES_PPI.StartupAllAPs() + to make sure that the nature of the code that is executed on the BSP and the + dispatched APs is well controlled. The MP Services Ppi does not guarantee + that the Procedure function is MP-safe. Hence, the tasks that can be run in + parallel are limited to certain independent tasks and well-controlled exclusive + code. PEI services and Ppis may not be called by APs unless otherwise + specified. + + In blocking execution mode, BSP waits until all APs finish or + TimeoutInMicroSeconds expires. + + @param[in] PeiServices An indirect pointer to the PEI Services Table + published by the PEI Foundation. + @param[in] This A pointer to the EFI_PEI_MP_SERVICES_PPI instance. + @param[in] Procedure A pointer to the function to be run on enabled APs of + the system. + @param[in] SingleThread If TRUE, then all the enabled APs execute the function + specified by Procedure one by one, in ascending order + of processor handle number. If FALSE, then all the + enabled APs execute the function specified by Procedure + simultaneously. + @param[in] TimeoutInMicroSeconds + Indicates the time limit in microseconds for APs to + return from Procedure, for blocking mode only. Zero + means infinity. If the timeout expires before all APs + return from Procedure, then Procedure on the failed APs + is terminated. All enabled APs are available for next + function assigned by EFI_PEI_MP_SERVICES_PPI.StartupAllAPs() + or EFI_PEI_MP_SERVICES_PPI.StartupThisAP(). If the + timeout expires in blocking mode, BSP returns + EFI_TIMEOUT. + @param[in] ProcedureArgument The parameter passed into Procedure for all APs. + + @retval EFI_SUCCESS In blocking mode, all APs have finished before the + timeout expired. + @retval EFI_DEVICE_ERROR Caller processor is AP. + @retval EFI_NOT_STARTED No enabled APs exist in the system. + @retval EFI_NOT_READY Any enabled APs are busy. + @retval EFI_TIMEOUT In blocking mode, the timeout expired before all + enabled APs have finished. + @retval EFI_INVALID_PARAMETER Procedure is NULL. +**/ +EFI_STATUS +EFIAPI +PeiStartupAllAPs ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_MP_SERVICES_PPI *This, + IN EFI_AP_PROCEDURE Procedure, + IN BOOLEAN SingleThread, + IN UINTN TimeoutInMicroSeconds, + IN VOID *ProcedureArgument OPTIONAL + ); + +/** + This service lets the caller get one enabled AP to execute a caller-provided + function. The caller can request the BSP to wait for the completion + of the AP. This service may only be called from the BSP. + + This function is used to dispatch one enabled AP to the function specified by + Procedure passing in the argument specified by ProcedureArgument. + The execution is in blocking mode. The BSP waits until the AP finishes or + TimeoutInMicroSecondss expires. + + If the timeout specified by TimeoutInMicroseconds expires before the AP returns + from Procedure, then execution of Procedure by the AP is terminated. The AP is + available for subsequent calls to EFI_PEI_MP_SERVICES_PPI.StartupAllAPs() and + EFI_PEI_MP_SERVICES_PPI.StartupThisAP(). + + @param[in] PeiServices An indirect pointer to the PEI Services Table + published by the PEI Foundation. + @param[in] This A pointer to the EFI_PEI_MP_SERVICES_PPI instance. + @param[in] Procedure A pointer to the function to be run on enabled APs of + the system. + @param[in] ProcessorNumber The handle number of the AP. The range is from 0 to the + total number of logical processors minus 1. The total + number of logical processors can be retrieved by + EFI_PEI_MP_SERVICES_PPI.GetNumberOfProcessors(). + @param[in] TimeoutInMicroseconds + Indicates the time limit in microseconds for APs to + return from Procedure, for blocking mode only. Zero + means infinity. If the timeout expires before all APs + return from Procedure, then Procedure on the failed APs + is terminated. All enabled APs are available for next + function assigned by EFI_PEI_MP_SERVICES_PPI.StartupAllAPs() + or EFI_PEI_MP_SERVICES_PPI.StartupThisAP(). If the + timeout expires in blocking mode, BSP returns + EFI_TIMEOUT. + @param[in] ProcedureArgument The parameter passed into Procedure for all APs. + + @retval EFI_SUCCESS In blocking mode, specified AP finished before the + timeout expires. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_TIMEOUT In blocking mode, the timeout expired before the + specified AP has finished. + @retval EFI_NOT_FOUND The processor with the handle specified by + ProcessorNumber does not exist. + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP or disabled AP. + @retval EFI_INVALID_PARAMETER Procedure is NULL. +**/ +EFI_STATUS +EFIAPI +PeiStartupThisAP ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_MP_SERVICES_PPI *This, + IN EFI_AP_PROCEDURE Procedure, + IN UINTN ProcessorNumber, + IN UINTN TimeoutInMicroseconds, + IN VOID *ProcedureArgument OPTIONAL + ); + +/** + This service switches the requested AP to be the BSP from that point onward. + This service changes the BSP for all purposes. This call can only be performed + by the current BSP. + + This service switches the requested AP to be the BSP from that point onward. + This service changes the BSP for all purposes. The new BSP can take over the + execution of the old BSP and continue seamlessly from where the old one left + off. + + If the BSP cannot be switched prior to the return from this service, then + EFI_UNSUPPORTED must be returned. + + @param[in] PeiServices An indirect pointer to the PEI Services Table + published by the PEI Foundation. + @param[in] This A pointer to the EFI_PEI_MP_SERVICES_PPI instance. + @param[in] ProcessorNumber The handle number of the AP. The range is from 0 to the + total number of logical processors minus 1. The total + number of logical processors can be retrieved by + EFI_PEI_MP_SERVICES_PPI.GetNumberOfProcessors(). + @param[in] EnableOldBSP If TRUE, then the old BSP will be listed as an enabled + AP. Otherwise, it will be disabled. + + @retval EFI_SUCCESS BSP successfully switched. + @retval EFI_UNSUPPORTED Switching the BSP cannot be completed prior to this + service returning. + @retval EFI_UNSUPPORTED Switching the BSP is not supported. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_NOT_FOUND The processor with the handle specified by + ProcessorNumber does not exist. + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the current BSP or a disabled + AP. + @retval EFI_NOT_READY The specified AP is busy. +**/ +EFI_STATUS +EFIAPI +PeiSwitchBSP ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_MP_SERVICES_PPI *This, + IN UINTN ProcessorNumber, + IN BOOLEAN EnableOldBSP + ); + +/** + This service lets the caller enable or disable an AP from this point onward. + This service may only be called from the BSP. + + This service allows the caller enable or disable an AP from this point onward. + The caller can optionally specify the health status of the AP by Health. If + an AP is being disabled, then the state of the disabled AP is implementation + dependent. If an AP is enabled, then the implementation must guarantee that a + complete initialization sequence is performed on the AP, so the AP is in a state + that is compatible with an MP operating system. + + If the enable or disable AP operation cannot be completed prior to the return + from this service, then EFI_UNSUPPORTED must be returned. + + @param[in] PeiServices An indirect pointer to the PEI Services Table + published by the PEI Foundation. + @param[in] This A pointer to the EFI_PEI_MP_SERVICES_PPI instance. + @param[in] ProcessorNumber The handle number of the AP. The range is from 0 to the + total number of logical processors minus 1. The total + number of logical processors can be retrieved by + EFI_PEI_MP_SERVICES_PPI.GetNumberOfProcessors(). + @param[in] EnableAP Specifies the new state for the processor for enabled, + FALSE for disabled. + @param[in] HealthFlag If not NULL, a pointer to a value that specifies the + new health status of the AP. This flag corresponds to + StatusFlag defined in EFI_PEI_MP_SERVICES_PPI.GetProcessorInfo(). + Only the PROCESSOR_HEALTH_STATUS_BIT is used. All other + bits are ignored. If it is NULL, this parameter is + ignored. + + @retval EFI_SUCCESS The specified AP was enabled or disabled successfully. + @retval EFI_UNSUPPORTED Enabling or disabling an AP cannot be completed prior + to this service returning. + @retval EFI_UNSUPPORTED Enabling or disabling an AP is not supported. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_NOT_FOUND Processor with the handle specified by ProcessorNumber + does not exist. + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP. +**/ +EFI_STATUS +EFIAPI +PeiEnableDisableAP ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_MP_SERVICES_PPI *This, + IN UINTN ProcessorNumber, + IN BOOLEAN EnableAP, + IN UINT32 *HealthFlag OPTIONAL + ); + +/** + This return the handle number for the calling processor. This service may be + called from the BSP and APs. + + This service returns the processor handle number for the calling processor. + The returned value is in the range from 0 to the total number of logical + processors minus 1. The total number of logical processors can be retrieved + with EFI_PEI_MP_SERVICES_PPI.GetNumberOfProcessors(). This service may be + called from the BSP and APs. If ProcessorNumber is NULL, then EFI_INVALID_PARAMETER + is returned. Otherwise, the current processors handle number is returned in + ProcessorNumber, and EFI_SUCCESS is returned. + + @param[in] PeiServices An indirect pointer to the PEI Services Table + published by the PEI Foundation. + @param[in] This A pointer to the EFI_PEI_MP_SERVICES_PPI instance. + @param[out] ProcessorNumber The handle number of the AP. The range is from 0 to the + total number of logical processors minus 1. The total + number of logical processors can be retrieved by + EFI_PEI_MP_SERVICES_PPI.GetNumberOfProcessors(). + + @retval EFI_SUCCESS The current processor handle number was returned in + ProcessorNumber. + @retval EFI_INVALID_PARAMETER ProcessorNumber is NULL. +**/ +EFI_STATUS +EFIAPI +PeiWhoAmI ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_MP_SERVICES_PPI *This, + OUT UINTN *ProcessorNumber + ); + +/** + Collects BIST data from PPI. + + This function collects BIST data from Sec Platform Information2 PPI + or SEC Platform Information PPI. + + @param PeiServices Pointer to PEI Services Table + +**/ +VOID +CollectBistDataFromPpi ( + IN CONST EFI_PEI_SERVICES **PeiServices + ); + +/** + Implementation of the PlatformInformation2 service in EFI_SEC_PLATFORM_INFORMATION2_PPI. + + @param PeiServices The pointer to the PEI Services Table. + @param StructureSize The pointer to the variable describing size of the input buffer. + @param PlatformInformationRecord2 The pointer to the EFI_SEC_PLATFORM_INFORMATION_RECORD2. + + @retval EFI_SUCCESS The data was successfully returned. + @retval EFI_BUFFER_TOO_SMALL The buffer was too small. The current buffer size needed to + hold the record is returned in StructureSize. + +**/ +EFI_STATUS +EFIAPI +SecPlatformInformation2 ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN OUT UINT64 *StructureSize, + OUT EFI_SEC_PLATFORM_INFORMATION_RECORD2 *PlatformInformationRecord2 + ); + +/** + Migrates the Global Descriptor Table (GDT) to permanent memory. + + @retval EFI_SUCCESS The GDT was migrated successfully. + @retval EFI_OUT_OF_RESOURCES The GDT could not be migrated due to lack of available memory. + +**/ +EFI_STATUS +MigrateGdt ( + VOID + ); + +/** + Initializes MP and exceptions handlers. + + @param PeiServices The pointer to the PEI Services Table. + + @retval EFI_SUCCESS MP was successfully initialized. + @retval others Error occurred in MP initialization. + +**/ +EFI_STATUS +InitializeCpuMpWorker ( + IN CONST EFI_PEI_SERVICES **PeiServices + ); + +/** + Enable/setup stack guard for each processor if PcdCpuStackGuard is set to TRUE. + + Doing this in the memory-discovered callback is to make sure the Stack Guard + feature to cover as most PEI code as possible. + + @param[in] PeiServices General purpose services available to every PEIM. + @param[in] NotifyDescriptor The notification structure this PEIM registered on install. + @param[in] Ppi The memory discovered PPI. Not used. + + @retval EFI_SUCCESS The function completed successfully. + @retval others There's error in MP initialization. +**/ +EFI_STATUS +EFIAPI +MemoryDiscoveredPpiNotifyCallback ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor, + IN VOID *Ppi + ); + +extern EFI_PEI_NOTIFY_DESCRIPTOR mPostMemNotifyList[]; + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei/CpuMpPei.inf b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei/CpuMpPei.inf new file mode 100644 index 00000000..c56d1e01 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei/CpuMpPei.inf @@ -0,0 +1,76 @@ +## @file +# CPU driver installs CPU PI Multi-processor PPI. +# +# Copyright (c) 2015 - 2021, Intel Corporation. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = CpuMpPei + MODULE_UNI_FILE = CpuMpPei.uni + FILE_GUID = EDADEB9D-DDBA-48BD-9D22-C1C169C8C5C6 + MODULE_TYPE = PEIM + VERSION_STRING = 1.0 + ENTRY_POINT = CpuMpPeimInit + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources] + CpuMpPei.h + CpuMpPei.c + CpuBist.c + CpuPaging.c + CpuMp2Pei.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + +[LibraryClasses] + BaseLib + DebugLib + HobLib + LocalApicLib + PeimEntryPoint + PeiServicesLib + ReportStatusCodeLib + CpuExceptionHandlerLib + MpInitLib + BaseMemoryLib + CpuLib + MemoryAllocationLib + +[Guids] + gEdkiiMigratedFvInfoGuid ## SOMETIMES_CONSUMES ## HOB + +[Ppis] + gEfiPeiMpServicesPpiGuid ## PRODUCES + gEfiSecPlatformInformationPpiGuid ## SOMETIMES_CONSUMES + ## SOMETIMES_CONSUMES + ## PRODUCES + ## UNDEFINED # HOB + gEfiSecPlatformInformation2PpiGuid + gEfiVectorHandoffInfoPpiGuid ## SOMETIMES_CONSUMES + gEfiPeiMemoryDiscoveredPpiGuid ## CONSUMES + gEdkiiPeiMpServices2PpiGuid ## PRODUCES + +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdPteMemoryEncryptionAddressOrMask ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdCpuStackGuard ## CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuStackSwitchExceptionList ## SOMETIMES_CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuKnownGoodStackSize ## SOMETIMES_CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuApStackSize ## SOMETIMES_CONSUMES + +[Depex] + TRUE + +[UserExtensions.TianoCore."ExtraFiles"] + CpuMpPeiExtra.uni + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei/CpuMpPei.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei/CpuMpPei.uni new file mode 100644 index 00000000..bbb395fd --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei/CpuMpPei.uni @@ -0,0 +1,16 @@ +// /** @file
+// CPU driver installs CPU PI Multi-processor PPI.
+//
+// CPU driver installs CPU PI Multi-processor PPI.
+//
+// Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Installs CPU PI Multi-processor PPI"
+
+#string STR_MODULE_DESCRIPTION #language en-US "CPU driver installs CPU PI Multi-processor PPI."
+
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei/CpuMpPeiExtra.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei/CpuMpPeiExtra.uni new file mode 100644 index 00000000..f84e1841 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei/CpuMpPeiExtra.uni @@ -0,0 +1,14 @@ +// /** @file
+// CpuMpPei Localized Strings and Content
+//
+// Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"CPU Multi-processor PEIM Driver"
+
+
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei/CpuPaging.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei/CpuPaging.c new file mode 100644 index 00000000..23c5cca4 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei/CpuPaging.c @@ -0,0 +1,711 @@ +/** @file + Basic paging support for the CPU to enable Stack Guard. + +Copyright (c) 2018 - 2019, Intel Corporation. All rights reserved.<BR> + +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <Register/Intel/Cpuid.h> +#include <Register/Intel/Msr.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/CpuLib.h> +#include <Library/BaseLib.h> +#include <Guid/MigratedFvInfo.h> +#ifdef VBOX +# define IN_RING0 +# include <iprt/asm.h> +#endif + +#include "CpuMpPei.h" + +#define IA32_PG_P BIT0 +#define IA32_PG_RW BIT1 +#define IA32_PG_U BIT2 +#define IA32_PG_A BIT5 +#define IA32_PG_D BIT6 +#define IA32_PG_PS BIT7 +#define IA32_PG_NX BIT63 + +#define PAGE_ATTRIBUTE_BITS (IA32_PG_RW | IA32_PG_P) +#define PAGE_PROGATE_BITS (IA32_PG_D | IA32_PG_A | IA32_PG_NX | IA32_PG_U |\ + PAGE_ATTRIBUTE_BITS) + +#define PAGING_PAE_INDEX_MASK 0x1FF +#define PAGING_4K_ADDRESS_MASK_64 0x000FFFFFFFFFF000ull +#define PAGING_2M_ADDRESS_MASK_64 0x000FFFFFFFE00000ull +#define PAGING_1G_ADDRESS_MASK_64 0x000FFFFFC0000000ull +#define PAGING_512G_ADDRESS_MASK_64 0x000FFF8000000000ull + +typedef enum { + PageNone = 0, + PageMin = 1, + Page4K = PageMin, + Page2M = 2, + Page1G = 3, + Page512G = 4, + PageMax = Page512G +} PAGE_ATTRIBUTE; + +typedef struct { + PAGE_ATTRIBUTE Attribute; + UINT64 Length; + UINT64 AddressMask; + UINTN AddressBitOffset; + UINTN AddressBitLength; +} PAGE_ATTRIBUTE_TABLE; + +PAGE_ATTRIBUTE_TABLE mPageAttributeTable[] = { + {PageNone, 0, 0, 0, 0}, + {Page4K, SIZE_4KB, PAGING_4K_ADDRESS_MASK_64, 12, 9}, + {Page2M, SIZE_2MB, PAGING_2M_ADDRESS_MASK_64, 21, 9}, + {Page1G, SIZE_1GB, PAGING_1G_ADDRESS_MASK_64, 30, 9}, + {Page512G, SIZE_512GB, PAGING_512G_ADDRESS_MASK_64, 39, 9}, +}; + +EFI_PEI_NOTIFY_DESCRIPTOR mPostMemNotifyList[] = { + { + (EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), + &gEfiPeiMemoryDiscoveredPpiGuid, + MemoryDiscoveredPpiNotifyCallback + } +}; + +#ifdef VBOX +/** + Safe page table entry write function, make 104% sure the compiler won't + split up the access (fatal if modifying entries for current code or data). + + @param[in] PageEntry The page table entry to modify.* + @param[in] CurrentPageEntry The old page table value (for cmpxchg8b). + @param[in] NewPageEntry What to write. +**/ +static VOID SafePageTableEntryWrite64 (UINT64 volatile *PageEntry, UINT64 CurrentPageEntry, UINT64 NewPageEntry) +{ +# ifdef VBOX + ASMAtomicWriteU64(PageEntry, NewPageEntry); RT_NOREF(CurrentPageEntry); +# else + for (;;) { + UINT64 CurValue = InterlockedCompareExchange64(PageEntry, CurrentPageEntry, NewPageEntry); + if (CurValue == CurrentPageEntry) + return; + CurrentPageEntry = CurValue; + } +# endif +} +#endif + +/** + The function will check if IA32 PAE is supported. + + @retval TRUE IA32 PAE is supported. + @retval FALSE IA32 PAE is not supported. + +**/ +BOOLEAN +IsIa32PaeSupported ( + VOID + ) +{ + UINT32 RegEax; + CPUID_VERSION_INFO_EDX RegEdx; + + AsmCpuid (CPUID_SIGNATURE, &RegEax, NULL, NULL, NULL); + if (RegEax >= CPUID_VERSION_INFO) { + AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, &RegEdx.Uint32); + if (RegEdx.Bits.PAE != 0) { + return TRUE; + } + } + + return FALSE; +} + +/** + This API provides a way to allocate memory for page table. + + @param Pages The number of 4 KB pages to allocate. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +AllocatePageTableMemory ( + IN UINTN Pages + ) +{ + VOID *Address; + + Address = AllocatePages(Pages); + if (Address != NULL) { + ZeroMem(Address, EFI_PAGES_TO_SIZE (Pages)); + } + + return Address; +} + +/** + Get the address width supported by current processor. + + @retval 32 If processor is in 32-bit mode. + @retval 36-48 If processor is in 64-bit mode. + +**/ +UINTN +GetPhysicalAddressWidth ( + VOID + ) +{ + UINT32 RegEax; + + if (sizeof(UINTN) == 4) { + return 32; + } + + AsmCpuid(CPUID_EXTENDED_FUNCTION, &RegEax, NULL, NULL, NULL); + if (RegEax >= CPUID_VIR_PHY_ADDRESS_SIZE) { + AsmCpuid (CPUID_VIR_PHY_ADDRESS_SIZE, &RegEax, NULL, NULL, NULL); + RegEax &= 0xFF; + if (RegEax > 48) { + return 48; + } + + return (UINTN)RegEax; + } + + return 36; +} + +/** + Get the type of top level page table. + + @retval Page512G PML4 paging. + @retval Page1G PAE paging. + +**/ +PAGE_ATTRIBUTE +GetPageTableTopLevelType ( + VOID + ) +{ + MSR_IA32_EFER_REGISTER MsrEfer; + + MsrEfer.Uint64 = AsmReadMsr64 (MSR_CORE_IA32_EFER); + + return (MsrEfer.Bits.LMA == 1) ? Page512G : Page1G; +} + +/** + Return page table entry matching the address. + + @param[in] Address The address to be checked. + @param[out] PageAttributes The page attribute of the page entry. + + @return The page entry. +**/ +VOID * +GetPageTableEntry ( + IN PHYSICAL_ADDRESS Address, + OUT PAGE_ATTRIBUTE *PageAttribute + ) +{ + INTN Level; + UINTN Index; + UINT64 *PageTable; + UINT64 AddressEncMask; + + AddressEncMask = PcdGet64 (PcdPteMemoryEncryptionAddressOrMask); + PageTable = (UINT64 *)(UINTN)(AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64); + for (Level = (INTN)GetPageTableTopLevelType (); Level > 0; --Level) { + Index = (UINTN)RShiftU64 (Address, mPageAttributeTable[Level].AddressBitOffset); + Index &= PAGING_PAE_INDEX_MASK; + + // + // No mapping? + // + if (PageTable[Index] == 0) { + *PageAttribute = PageNone; + return NULL; + } + + // + // Page memory? + // + if ((PageTable[Index] & IA32_PG_PS) != 0 || Level == PageMin) { + *PageAttribute = (PAGE_ATTRIBUTE)Level; + return &PageTable[Index]; + } + + // + // Page directory or table + // + PageTable = (UINT64 *)(UINTN)(PageTable[Index] & + ~AddressEncMask & + PAGING_4K_ADDRESS_MASK_64); + } + + *PageAttribute = PageNone; + return NULL; +} + +/** + This function splits one page entry to smaller page entries. + + @param[in] PageEntry The page entry to be splitted. + @param[in] PageAttribute The page attribute of the page entry. + @param[in] SplitAttribute How to split the page entry. + @param[in] Recursively Do the split recursively or not. + + @retval RETURN_SUCCESS The page entry is splitted. + @retval RETURN_INVALID_PARAMETER If target page attribute is invalid + @retval RETURN_OUT_OF_RESOURCES No resource to split page entry. +**/ +RETURN_STATUS +SplitPage ( +#ifdef VBOX + IN UINT64 volatile *PageEntry, +#else + IN UINT64 *PageEntry, +#endif + IN PAGE_ATTRIBUTE PageAttribute, + IN PAGE_ATTRIBUTE SplitAttribute, + IN BOOLEAN Recursively + ) +{ +#ifdef VBOX + UINT64 CurrentPageEntry; +#endif + UINT64 BaseAddress; + UINT64 *NewPageEntry; + UINTN Index; + UINT64 AddressEncMask; + PAGE_ATTRIBUTE SplitTo; + + if (SplitAttribute == PageNone || SplitAttribute >= PageAttribute) { + ASSERT (SplitAttribute != PageNone); + ASSERT (SplitAttribute < PageAttribute); + return RETURN_INVALID_PARAMETER; + } + + NewPageEntry = AllocatePageTableMemory (1); + if (NewPageEntry == NULL) { + ASSERT (NewPageEntry != NULL); + return RETURN_OUT_OF_RESOURCES; + } + + // + // One level down each step to achieve more compact page table. + // + SplitTo = PageAttribute - 1; + AddressEncMask = PcdGet64 (PcdPteMemoryEncryptionAddressOrMask) & + mPageAttributeTable[SplitTo].AddressMask; +#ifdef VBOX + CurrentPageEntry = *PageEntry; + BaseAddress = CurrentPageEntry & +#else + BaseAddress = *PageEntry & +#endif + ~PcdGet64 (PcdPteMemoryEncryptionAddressOrMask) & + mPageAttributeTable[PageAttribute].AddressMask; + for (Index = 0; Index < SIZE_4KB / sizeof(UINT64); Index++) { + NewPageEntry[Index] = BaseAddress | AddressEncMask | +#ifdef VBOX + (CurrentPageEntry & PAGE_PROGATE_BITS); +#else + ((*PageEntry) & PAGE_PROGATE_BITS); +#endif + + if (SplitTo != PageMin) { + NewPageEntry[Index] |= IA32_PG_PS; + } + + if (Recursively && SplitTo > SplitAttribute) { + SplitPage (&NewPageEntry[Index], SplitTo, SplitAttribute, Recursively); + } + + BaseAddress += mPageAttributeTable[SplitTo].Length; + } + +#ifdef VBOX + SafePageTableEntryWrite64 (PageEntry, CurrentPageEntry, + (UINT64)(UINTN)NewPageEntry | AddressEncMask | PAGE_ATTRIBUTE_BITS); +#else + (*PageEntry) = (UINT64)(UINTN)NewPageEntry | AddressEncMask | PAGE_ATTRIBUTE_BITS; +#endif + + return RETURN_SUCCESS; +} + +/** + This function modifies the page attributes for the memory region specified + by BaseAddress and Length from their current attributes to the attributes + specified by Attributes. + + Caller should make sure BaseAddress and Length is at page boundary. + + @param[in] BaseAddress Start address of a memory region. + @param[in] Length Size in bytes of the memory region. + @param[in] Attributes Bit mask of attributes to modify. + + @retval RETURN_SUCCESS The attributes were modified for the memory + region. + @retval RETURN_INVALID_PARAMETER Length is zero; or, + Attributes specified an illegal combination + of attributes that cannot be set together; or + Addressis not 4KB aligned. + @retval RETURN_OUT_OF_RESOURCES There are not enough system resources to modify + the attributes. + @retval RETURN_UNSUPPORTED Cannot modify the attributes of given memory. + +**/ +RETURN_STATUS +EFIAPI +ConvertMemoryPageAttributes ( + IN PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN UINT64 Attributes + ) +{ +#ifdef VBOX + UINT64 volatile *PageEntry; + UINT64 CurrentPageEntry; +#else + UINT64 *PageEntry; +#endif + PAGE_ATTRIBUTE PageAttribute; + RETURN_STATUS Status; + EFI_PHYSICAL_ADDRESS MaximumAddress; + + if (Length == 0 || + (BaseAddress & (SIZE_4KB - 1)) != 0 || + (Length & (SIZE_4KB - 1)) != 0) { + + ASSERT (Length > 0); + ASSERT ((BaseAddress & (SIZE_4KB - 1)) == 0); + ASSERT ((Length & (SIZE_4KB - 1)) == 0); + + return RETURN_INVALID_PARAMETER; + } + + MaximumAddress = (EFI_PHYSICAL_ADDRESS)MAX_UINT32; + if (BaseAddress > MaximumAddress || + Length > MaximumAddress || + (BaseAddress > MaximumAddress - (Length - 1))) { + return RETURN_UNSUPPORTED; + } + + // + // Below logic is to check 2M/4K page to make sure we do not waste memory. + // + while (Length != 0) { + PageEntry = GetPageTableEntry (BaseAddress, &PageAttribute); + if (PageEntry == NULL) { + return RETURN_UNSUPPORTED; + } + + if (PageAttribute != Page4K) { + Status = SplitPage (PageEntry, PageAttribute, Page4K, FALSE); + if (RETURN_ERROR (Status)) { + return Status; + } + // + // Do it again until the page is 4K. + // + continue; + } + + // + // Just take care of 'present' bit for Stack Guard. + // +#ifdef VBOX + CurrentPageEntry = *PageEntry; + if ((CurrentPageEntry & IA32_PG_P) != (Attributes & IA32_PG_P)) + SafePageTableEntryWrite64 (PageEntry, CurrentPageEntry, + (CurrentPageEntry & ~(UINT64)IA32_PG_P) | (Attributes & IA32_PG_P)); +#else + if ((Attributes & IA32_PG_P) != 0) { + *PageEntry |= (UINT64)IA32_PG_P; + } else { + *PageEntry &= ~((UINT64)IA32_PG_P); + } +#endif + + // + // Convert success, move to next + // + BaseAddress += SIZE_4KB; + Length -= SIZE_4KB; + } + + return RETURN_SUCCESS; +} + +/** + Get maximum size of page memory supported by current processor. + + @param[in] TopLevelType The type of top level page entry. + + @retval Page1G If processor supports 1G page and PML4. + @retval Page2M For all other situations. + +**/ +PAGE_ATTRIBUTE +GetMaxMemoryPage ( + IN PAGE_ATTRIBUTE TopLevelType + ) +{ + UINT32 RegEax; + UINT32 RegEdx; + + if (TopLevelType == Page512G) { + AsmCpuid (CPUID_EXTENDED_FUNCTION, &RegEax, NULL, NULL, NULL); + if (RegEax >= CPUID_EXTENDED_CPU_SIG) { + AsmCpuid (CPUID_EXTENDED_CPU_SIG, NULL, NULL, NULL, &RegEdx); + if ((RegEdx & BIT26) != 0) { + return Page1G; + } + } + } + + return Page2M; +} + +/** + Create PML4 or PAE page table. + + @return The address of page table. + +**/ +UINTN +CreatePageTable ( + VOID + ) +{ + RETURN_STATUS Status; + UINTN PhysicalAddressBits; + UINTN NumberOfEntries; + PAGE_ATTRIBUTE TopLevelPageAttr; + UINTN PageTable; + PAGE_ATTRIBUTE MaxMemoryPage; + UINTN Index; + UINT64 AddressEncMask; + UINT64 *PageEntry; + EFI_PHYSICAL_ADDRESS PhysicalAddress; + + TopLevelPageAttr = (PAGE_ATTRIBUTE)GetPageTableTopLevelType (); + PhysicalAddressBits = GetPhysicalAddressWidth (); + NumberOfEntries = (UINTN)1 << (PhysicalAddressBits - + mPageAttributeTable[TopLevelPageAttr].AddressBitOffset); + + PageTable = (UINTN) AllocatePageTableMemory (1); + if (PageTable == 0) { + return 0; + } + + AddressEncMask = PcdGet64 (PcdPteMemoryEncryptionAddressOrMask); + AddressEncMask &= mPageAttributeTable[TopLevelPageAttr].AddressMask; + MaxMemoryPage = GetMaxMemoryPage (TopLevelPageAttr); + PageEntry = (UINT64 *)PageTable; + + PhysicalAddress = 0; + for (Index = 0; Index < NumberOfEntries; ++Index) { + *PageEntry = PhysicalAddress | AddressEncMask | PAGE_ATTRIBUTE_BITS; + + // + // Split the top page table down to the maximum page size supported + // + if (MaxMemoryPage < TopLevelPageAttr) { + Status = SplitPage(PageEntry, TopLevelPageAttr, MaxMemoryPage, TRUE); + ASSERT_EFI_ERROR (Status); + } + + if (TopLevelPageAttr == Page1G) { + // + // PDPTE[2:1] (PAE Paging) must be 0. SplitPage() might change them to 1. + // + *PageEntry &= ~(UINT64)(IA32_PG_RW | IA32_PG_U); + } + + PageEntry += 1; + PhysicalAddress += mPageAttributeTable[TopLevelPageAttr].Length; + } + + + return PageTable; +} + +/** + Setup page tables and make them work. + +**/ +VOID +EnablePaging ( + VOID + ) +{ + UINTN PageTable; + + PageTable = CreatePageTable (); + ASSERT (PageTable != 0); + if (PageTable != 0) { + AsmWriteCr3(PageTable); + AsmWriteCr4 (AsmReadCr4 () | BIT5); // CR4.PAE + AsmWriteCr0 (AsmReadCr0 () | BIT31); // CR0.PG + } +} + +/** + Get the base address of current AP's stack. + + This function is called in AP's context and assumes that whole calling stacks + (till this function) consumed by AP's wakeup procedure will not exceed 4KB. + + PcdCpuApStackSize must be configured with value taking the Guard page into + account. + + @param[in,out] Buffer The pointer to private data buffer. + +**/ +VOID +EFIAPI +GetStackBase ( + IN OUT VOID *Buffer + ) +{ + EFI_PHYSICAL_ADDRESS StackBase; + + StackBase = (EFI_PHYSICAL_ADDRESS)(UINTN)&StackBase; + StackBase += BASE_4KB; + StackBase &= ~((EFI_PHYSICAL_ADDRESS)BASE_4KB - 1); + StackBase -= PcdGet32(PcdCpuApStackSize); + + *(EFI_PHYSICAL_ADDRESS *)Buffer = StackBase; +} + +/** + Setup stack Guard page at the stack base of each processor. BSP and APs have + different way to get stack base address. + +**/ +VOID +SetupStackGuardPage ( + VOID + ) +{ + EFI_PEI_HOB_POINTERS Hob; + EFI_PHYSICAL_ADDRESS StackBase; + UINTN NumberOfProcessors; + UINTN Bsp; + UINTN Index; + + // + // One extra page at the bottom of the stack is needed for Guard page. + // + if (PcdGet32(PcdCpuApStackSize) <= EFI_PAGE_SIZE) { + DEBUG ((DEBUG_ERROR, "PcdCpuApStackSize is not big enough for Stack Guard!\n")); + ASSERT (FALSE); + } + + MpInitLibGetNumberOfProcessors(&NumberOfProcessors, NULL); + MpInitLibWhoAmI (&Bsp); + for (Index = 0; Index < NumberOfProcessors; ++Index) { + StackBase = 0; + + if (Index == Bsp) { + Hob.Raw = GetHobList (); + while ((Hob.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, Hob.Raw)) != NULL) { + if (CompareGuid (&gEfiHobMemoryAllocStackGuid, + &(Hob.MemoryAllocationStack->AllocDescriptor.Name))) { + StackBase = Hob.MemoryAllocationStack->AllocDescriptor.MemoryBaseAddress; + break; + } + Hob.Raw = GET_NEXT_HOB (Hob); + } + } else { + // + // Ask AP to return is stack base address. + // + MpInitLibStartupThisAP(GetStackBase, Index, NULL, 0, (VOID *)&StackBase, NULL); + } + ASSERT (StackBase != 0); + // + // Set Guard page at stack base address. + // + ConvertMemoryPageAttributes(StackBase, EFI_PAGE_SIZE, 0); + DEBUG ((DEBUG_INFO, "Stack Guard set at %lx [cpu%lu]!\n", + (UINT64)StackBase, (UINT64)Index)); + } + + // + // Publish the changes of page table. + // + CpuFlushTlb (); +} + +/** + Enable/setup stack guard for each processor if PcdCpuStackGuard is set to TRUE. + + Doing this in the memory-discovered callback is to make sure the Stack Guard + feature to cover as most PEI code as possible. + + @param[in] PeiServices General purpose services available to every PEIM. + @param[in] NotifyDescriptor The notification structure this PEIM registered on install. + @param[in] Ppi The memory discovered PPI. Not used. + + @retval EFI_SUCCESS The function completed successfully. + @retval others There's error in MP initialization. +**/ +EFI_STATUS +EFIAPI +MemoryDiscoveredPpiNotifyCallback ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor, + IN VOID *Ppi + ) +{ + EFI_STATUS Status; + BOOLEAN InitStackGuard; + EDKII_MIGRATED_FV_INFO *MigratedFvInfo; + EFI_PEI_HOB_POINTERS Hob; + + // + // Paging must be setup first. Otherwise the exception TSS setup during MP + // initialization later will not contain paging information and then fail + // the task switch (for the sake of stack switch). + // + InitStackGuard = FALSE; + Hob.Raw = NULL; + if (IsIa32PaeSupported ()) { + Hob.Raw = GetFirstGuidHob (&gEdkiiMigratedFvInfoGuid); + InitStackGuard = PcdGetBool (PcdCpuStackGuard); + } + + if (InitStackGuard || Hob.Raw != NULL) { + EnablePaging (); + } + + Status = InitializeCpuMpWorker ((CONST EFI_PEI_SERVICES **)PeiServices); + ASSERT_EFI_ERROR (Status); + + if (InitStackGuard) { + SetupStackGuardPage (); + } + + while (Hob.Raw != NULL) { + MigratedFvInfo = GET_GUID_HOB_DATA (Hob); + + // + // Enable #PF exception, so if the code access SPI after disable NEM, it will generate + // the exception to avoid potential vulnerability. + // + ConvertMemoryPageAttributes (MigratedFvInfo->FvOrgBase, MigratedFvInfo->FvLength, 0); + + Hob.Raw = GET_NEXT_HOB (Hob); + Hob.Raw = GetNextGuidHob (&gEdkiiMigratedFvInfoGuid, Hob.Raw); + } + CpuFlushTlb (); + + return Status; +} + |