+/**\r
+ Get available system memory below 1MB by specified size.\r
+\r
+ @param[in] CpuMpData The pointer to CPU MP Data structure.\r
+**/\r
+VOID\r
+BackupAndPrepareWakeupBuffer (\r
+ IN CPU_MP_DATA *CpuMpData\r
+ )\r
+{\r
+ CopyMem (\r
+ (VOID *)CpuMpData->BackupBuffer,\r
+ (VOID *)CpuMpData->WakeupBuffer,\r
+ CpuMpData->BackupBufferSize\r
+ );\r
+ CopyMem (\r
+ (VOID *)CpuMpData->WakeupBuffer,\r
+ (VOID *)CpuMpData->AddressMap.RendezvousFunnelAddress,\r
+ CpuMpData->AddressMap.RendezvousFunnelSize +\r
+ CpuMpData->AddressMap.SwitchToRealSize\r
+ );\r
+}\r
+\r
+/**\r
+ Restore wakeup buffer data.\r
+\r
+ @param[in] CpuMpData The pointer to CPU MP Data structure.\r
+**/\r
+VOID\r
+RestoreWakeupBuffer (\r
+ IN CPU_MP_DATA *CpuMpData\r
+ )\r
+{\r
+ CopyMem (\r
+ (VOID *)CpuMpData->WakeupBuffer,\r
+ (VOID *)CpuMpData->BackupBuffer,\r
+ CpuMpData->BackupBufferSize\r
+ );\r
+}\r
+\r
+/**\r
+ Calculate the size of the reset vector.\r
+\r
+ @param[in] AddressMap The pointer to Address Map structure.\r
+\r
+ @return Total amount of memory required for the AP reset area\r
+**/\r
+STATIC\r
+UINTN\r
+GetApResetVectorSize (\r
+ IN MP_ASSEMBLY_ADDRESS_MAP *AddressMap\r
+ )\r
+{\r
+ UINTN Size;\r
+\r
+ Size = AddressMap->RendezvousFunnelSize +\r
+ AddressMap->SwitchToRealSize +\r
+ sizeof (MP_CPU_EXCHANGE_INFO);\r
+\r
+ return Size;\r
+}\r
+\r
+/**\r
+ Allocate reset vector buffer.\r
+\r
+ @param[in, out] CpuMpData The pointer to CPU MP Data structure.\r
+**/\r
+VOID\r
+AllocateResetVector (\r
+ IN OUT CPU_MP_DATA *CpuMpData\r
+ )\r
+{\r
+ UINTN ApResetVectorSize;\r
+ UINTN ApResetStackSize;\r
+\r
+ if (CpuMpData->WakeupBuffer == (UINTN)-1) {\r
+ ApResetVectorSize = GetApResetVectorSize (&CpuMpData->AddressMap);\r
+\r
+ CpuMpData->WakeupBuffer = GetWakeupBuffer (ApResetVectorSize);\r
+ CpuMpData->MpCpuExchangeInfo = (MP_CPU_EXCHANGE_INFO *)(UINTN)\r
+ (CpuMpData->WakeupBuffer +\r
+ CpuMpData->AddressMap.RendezvousFunnelSize +\r
+ CpuMpData->AddressMap.SwitchToRealSize);\r
+ CpuMpData->WakeupBufferHigh = GetModeTransitionBuffer (\r
+ CpuMpData->AddressMap.RendezvousFunnelSize +\r
+ CpuMpData->AddressMap.SwitchToRealSize -\r
+ CpuMpData->AddressMap.ModeTransitionOffset\r
+ );\r
+ //\r
+ // The AP reset stack is only used by SEV-ES guests. Do not allocate it\r
+ // if SEV-ES is not enabled.\r
+ //\r
+ if (PcdGetBool (PcdSevEsIsEnabled)) {\r
+ //\r
+ // Stack location is based on ProcessorNumber, so use the total number\r
+ // of processors for calculating the total stack area.\r
+ //\r
+ ApResetStackSize = (AP_RESET_STACK_SIZE *\r
+ PcdGet32 (PcdCpuMaxLogicalProcessorNumber));\r
+\r
+ //\r
+ // Invoke GetWakeupBuffer a second time to allocate the stack area\r
+ // below 1MB. The returned buffer will be page aligned and sized and\r
+ // below the previously allocated buffer.\r
+ //\r
+ CpuMpData->SevEsAPResetStackStart = GetWakeupBuffer (ApResetStackSize);\r
+\r
+ //\r
+ // Check to be sure that the "allocate below" behavior hasn't changed.\r
+ // This will also catch a failed allocation, as "-1" is returned on\r
+ // failure.\r
+ //\r
+ if (CpuMpData->SevEsAPResetStackStart >= CpuMpData->WakeupBuffer) {\r
+ DEBUG ((\r
+ DEBUG_ERROR,\r
+ "SEV-ES AP reset stack is not below wakeup buffer\n"\r
+ ));\r
+\r
+ ASSERT (FALSE);\r
+ CpuDeadLoop ();\r
+ }\r
+ }\r
+ }\r
+\r
+ BackupAndPrepareWakeupBuffer (CpuMpData);\r
+}\r
+\r
+/**\r
+ Free AP reset vector buffer.\r
+\r
+ @param[in] CpuMpData The pointer to CPU MP Data structure.\r
+**/\r
+VOID\r
+FreeResetVector (\r
+ IN CPU_MP_DATA *CpuMpData\r
+ )\r
+{\r
+ //\r
+ // If SEV-ES is enabled, the reset area is needed for AP parking and\r
+ // and AP startup in the OS, so the reset area is reserved. Do not\r
+ // perform the restore as this will overwrite memory which has data\r
+ // needed by SEV-ES.\r
+ //\r
+ if (!CpuMpData->SevEsIsEnabled) {\r
+ RestoreWakeupBuffer (CpuMpData);\r
+ }\r
+}\r
+\r
+/**\r
+ Allocate the SEV-ES AP jump table buffer.\r
+\r
+ @param[in, out] CpuMpData The pointer to CPU MP Data structure.\r
+**/\r
+VOID\r
+AllocateSevEsAPMemory (\r
+ IN OUT CPU_MP_DATA *CpuMpData\r
+ )\r
+{\r
+ if (CpuMpData->SevEsAPBuffer == (UINTN)-1) {\r
+ CpuMpData->SevEsAPBuffer =\r
+ CpuMpData->SevEsIsEnabled ? GetSevEsAPMemory () : 0;\r
+ }\r
+}\r
+\r
+/**\r
+ Program the SEV-ES AP jump table buffer.\r
+\r
+ @param[in] SipiVector The SIPI vector used for the AP Reset\r
+**/\r
+VOID\r
+SetSevEsJumpTable (\r
+ IN UINTN SipiVector\r
+ )\r
+{\r
+ SEV_ES_AP_JMP_FAR *JmpFar;\r
+ UINT32 Offset, InsnByte;\r
+ UINT8 LoNib, HiNib;\r
+\r
+ JmpFar = (SEV_ES_AP_JMP_FAR *)(UINTN)FixedPcdGet32 (PcdSevEsWorkAreaBase);\r
+ ASSERT (JmpFar != NULL);\r
+\r
+ //\r
+ // Obtain the address of the Segment/Rip location in the workarea.\r
+ // This will be set to a value derived from the SIPI vector and will\r
+ // be the memory address used for the far jump below.\r
+ //\r
+ Offset = FixedPcdGet32 (PcdSevEsWorkAreaBase);\r
+ Offset += sizeof (JmpFar->InsnBuffer);\r
+ LoNib = (UINT8)Offset;\r
+ HiNib = (UINT8)(Offset >> 8);\r
+\r
+ //\r
+ // Program the workarea (which is the initial AP boot address) with\r
+ // far jump to the SIPI vector (where XX and YY represent the\r
+ // address of where the SIPI vector is stored.\r
+ //\r
+ // JMP FAR [CS:XXYY] => 2E FF 2E YY XX\r
+ //\r
+ InsnByte = 0;\r
+ JmpFar->InsnBuffer[InsnByte++] = 0x2E; // CS override prefix\r
+ JmpFar->InsnBuffer[InsnByte++] = 0xFF; // JMP (FAR)\r
+ JmpFar->InsnBuffer[InsnByte++] = 0x2E; // ModRM (JMP memory location)\r
+ JmpFar->InsnBuffer[InsnByte++] = LoNib; // YY offset ...\r
+ JmpFar->InsnBuffer[InsnByte++] = HiNib; // XX offset ...\r
+\r
+ //\r
+ // Program the Segment/Rip based on the SIPI vector (always at least\r
+ // 16-byte aligned, so Rip is set to 0).\r
+ //\r
+ JmpFar->Rip = 0;\r
+ JmpFar->Segment = (UINT16)(SipiVector >> 4);\r
+}\r
+\r