summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
commitf215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch)
tree6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei
parentInitial commit. (diff)
downloadvirtualbox-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')
-rw-r--r--src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei/CpuBist.c291
-rw-r--r--src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei/CpuMp2Pei.c417
-rw-r--r--src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei/CpuMpPei.c710
-rw-r--r--src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei/CpuMpPei.h450
-rw-r--r--src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei/CpuMpPei.inf76
-rw-r--r--src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei/CpuMpPei.uni16
-rw-r--r--src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei/CpuMpPeiExtra.uni14
-rw-r--r--src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei/CpuPaging.c711
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;
+}
+