--- /dev/null
+/** @file\r
+ CPU MP Initialize helper function for AMD SEV.\r
+\r
+ Copyright (c) 2021, AMD Inc. All rights reserved.<BR>\r
+\r
+ SPDX-License-Identifier: BSD-2-Clause-Patent\r
+\r
+**/\r
+\r
+#include "MpLib.h"\r
+#include <Library/VmgExitLib.h>\r
+\r
+/**\r
+ Get Protected mode code segment with 16-bit default addressing\r
+ from current GDT table.\r
+\r
+ @return Protected mode 16-bit code segment value.\r
+**/\r
+STATIC\r
+UINT16\r
+GetProtectedMode16CS (\r
+ VOID\r
+ )\r
+{\r
+ IA32_DESCRIPTOR GdtrDesc;\r
+ IA32_SEGMENT_DESCRIPTOR *GdtEntry;\r
+ UINTN GdtEntryCount;\r
+ UINT16 Index;\r
+\r
+ Index = (UINT16)-1;\r
+ AsmReadGdtr (&GdtrDesc);\r
+ GdtEntryCount = (GdtrDesc.Limit + 1) / sizeof (IA32_SEGMENT_DESCRIPTOR);\r
+ GdtEntry = (IA32_SEGMENT_DESCRIPTOR *)GdtrDesc.Base;\r
+ for (Index = 0; Index < GdtEntryCount; Index++) {\r
+ if ((GdtEntry->Bits.L == 0) &&\r
+ (GdtEntry->Bits.DB == 0) &&\r
+ (GdtEntry->Bits.Type > 8))\r
+ {\r
+ break;\r
+ }\r
+\r
+ GdtEntry++;\r
+ }\r
+\r
+ ASSERT (Index != GdtEntryCount);\r
+ return Index * 8;\r
+}\r
+\r
+/**\r
+ Get Protected mode code segment with 32-bit default addressing\r
+ from current GDT table.\r
+\r
+ @return Protected mode 32-bit code segment value.\r
+**/\r
+STATIC\r
+UINT16\r
+GetProtectedMode32CS (\r
+ VOID\r
+ )\r
+{\r
+ IA32_DESCRIPTOR GdtrDesc;\r
+ IA32_SEGMENT_DESCRIPTOR *GdtEntry;\r
+ UINTN GdtEntryCount;\r
+ UINT16 Index;\r
+\r
+ Index = (UINT16)-1;\r
+ AsmReadGdtr (&GdtrDesc);\r
+ GdtEntryCount = (GdtrDesc.Limit + 1) / sizeof (IA32_SEGMENT_DESCRIPTOR);\r
+ GdtEntry = (IA32_SEGMENT_DESCRIPTOR *)GdtrDesc.Base;\r
+ for (Index = 0; Index < GdtEntryCount; Index++) {\r
+ if ((GdtEntry->Bits.L == 0) &&\r
+ (GdtEntry->Bits.DB == 1) &&\r
+ (GdtEntry->Bits.Type > 8))\r
+ {\r
+ break;\r
+ }\r
+\r
+ GdtEntry++;\r
+ }\r
+\r
+ ASSERT (Index != GdtEntryCount);\r
+ return Index * 8;\r
+}\r
+\r
+/**\r
+ Reset an AP when in SEV-ES mode.\r
+\r
+ If successful, this function never returns.\r
+\r
+ @param[in] Ghcb Pointer to the GHCB\r
+ @param[in] CpuMpData Pointer to CPU MP Data\r
+\r
+**/\r
+VOID\r
+MpInitLibSevEsAPReset (\r
+ IN GHCB *Ghcb,\r
+ IN CPU_MP_DATA *CpuMpData\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN ProcessorNumber;\r
+ UINT16 Code16, Code32;\r
+ AP_RESET *APResetFn;\r
+ UINTN BufferStart;\r
+ UINTN StackStart;\r
+\r
+ Status = GetProcessorNumber (CpuMpData, &ProcessorNumber);\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ Code16 = GetProtectedMode16CS ();\r
+ Code32 = GetProtectedMode32CS ();\r
+\r
+ if (CpuMpData->WakeupBufferHigh != 0) {\r
+ APResetFn = (AP_RESET *)(CpuMpData->WakeupBufferHigh + CpuMpData->AddressMap.SwitchToRealNoNxOffset);\r
+ } else {\r
+ APResetFn = (AP_RESET *)(CpuMpData->MpCpuExchangeInfo->BufferStart + CpuMpData->AddressMap.SwitchToRealOffset);\r
+ }\r
+\r
+ BufferStart = CpuMpData->MpCpuExchangeInfo->BufferStart;\r
+ StackStart = CpuMpData->SevEsAPResetStackStart -\r
+ (AP_RESET_STACK_SIZE * ProcessorNumber);\r
+\r
+ //\r
+ // This call never returns.\r
+ //\r
+ APResetFn (BufferStart, Code16, Code32, StackStart);\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
+/**\r
+ The function puts the AP in halt loop.\r
+\r
+ @param[in] CpuMpData The pointer to CPU MP Data structure.\r
+**/\r
+VOID\r
+SevEsPlaceApHlt (\r
+ CPU_MP_DATA *CpuMpData\r
+ )\r
+{\r
+ MSR_SEV_ES_GHCB_REGISTER Msr;\r
+ GHCB *Ghcb;\r
+ UINT64 Status;\r
+ BOOLEAN DoDecrement;\r
+ BOOLEAN InterruptState;\r
+\r
+ DoDecrement = (BOOLEAN)(CpuMpData->InitFlag == ApInitConfig);\r
+\r
+ while (TRUE) {\r
+ Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);\r
+ Ghcb = Msr.Ghcb;\r
+\r
+ VmgInit (Ghcb, &InterruptState);\r
+\r
+ if (DoDecrement) {\r
+ DoDecrement = FALSE;\r
+\r
+ //\r
+ // Perform the delayed decrement just before issuing the first\r
+ // VMGEXIT with AP_RESET_HOLD.\r
+ //\r
+ InterlockedDecrement ((UINT32 *)&CpuMpData->MpCpuExchangeInfo->NumApsExecuting);\r
+ }\r
+\r
+ Status = VmgExit (Ghcb, SVM_EXIT_AP_RESET_HOLD, 0, 0);\r
+ if ((Status == 0) && (Ghcb->SaveArea.SwExitInfo2 != 0)) {\r
+ VmgDone (Ghcb, InterruptState);\r
+ break;\r
+ }\r
+\r
+ VmgDone (Ghcb, InterruptState);\r
+ }\r
+\r
+ //\r
+ // Awakened in a new phase? Use the new CpuMpData\r
+ //\r
+ if (CpuMpData->NewCpuMpData != NULL) {\r
+ CpuMpData = CpuMpData->NewCpuMpData;\r
+ }\r
+\r
+ MpInitLibSevEsAPReset (Ghcb, CpuMpData);\r
+}\r
X64/MpFuncs.nasm\r
\r
[Sources.common]\r
+ AmdSev.c\r
MpEqu.inc\r
DxeMpLib.c\r
MpLib.c\r
SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateIdle);\r
}\r
\r
-/**\r
- Get Protected mode code segment with 16-bit default addressing\r
- from current GDT table.\r
-\r
- @return Protected mode 16-bit code segment value.\r
-**/\r
-STATIC\r
-UINT16\r
-GetProtectedMode16CS (\r
- VOID\r
- )\r
-{\r
- IA32_DESCRIPTOR GdtrDesc;\r
- IA32_SEGMENT_DESCRIPTOR *GdtEntry;\r
- UINTN GdtEntryCount;\r
- UINT16 Index;\r
-\r
- Index = (UINT16)-1;\r
- AsmReadGdtr (&GdtrDesc);\r
- GdtEntryCount = (GdtrDesc.Limit + 1) / sizeof (IA32_SEGMENT_DESCRIPTOR);\r
- GdtEntry = (IA32_SEGMENT_DESCRIPTOR *)GdtrDesc.Base;\r
- for (Index = 0; Index < GdtEntryCount; Index++) {\r
- if ((GdtEntry->Bits.L == 0) &&\r
- (GdtEntry->Bits.DB == 0) &&\r
- (GdtEntry->Bits.Type > 8))\r
- {\r
- break;\r
- }\r
-\r
- GdtEntry++;\r
- }\r
-\r
- ASSERT (Index != GdtEntryCount);\r
- return Index * 8;\r
-}\r
-\r
-/**\r
- Get Protected mode code segment with 32-bit default addressing\r
- from current GDT table.\r
-\r
- @return Protected mode 32-bit code segment value.\r
-**/\r
-STATIC\r
-UINT16\r
-GetProtectedMode32CS (\r
- VOID\r
- )\r
-{\r
- IA32_DESCRIPTOR GdtrDesc;\r
- IA32_SEGMENT_DESCRIPTOR *GdtEntry;\r
- UINTN GdtEntryCount;\r
- UINT16 Index;\r
-\r
- Index = (UINT16)-1;\r
- AsmReadGdtr (&GdtrDesc);\r
- GdtEntryCount = (GdtrDesc.Limit + 1) / sizeof (IA32_SEGMENT_DESCRIPTOR);\r
- GdtEntry = (IA32_SEGMENT_DESCRIPTOR *)GdtrDesc.Base;\r
- for (Index = 0; Index < GdtEntryCount; Index++) {\r
- if ((GdtEntry->Bits.L == 0) &&\r
- (GdtEntry->Bits.DB == 1) &&\r
- (GdtEntry->Bits.Type > 8))\r
- {\r
- break;\r
- }\r
-\r
- GdtEntry++;\r
- }\r
-\r
- ASSERT (Index != GdtEntryCount);\r
- return Index * 8;\r
-}\r
-\r
-/**\r
- Reset an AP when in SEV-ES mode.\r
-\r
- If successful, this function never returns.\r
-\r
- @param[in] Ghcb Pointer to the GHCB\r
- @param[in] CpuMpData Pointer to CPU MP Data\r
-\r
-**/\r
-STATIC\r
-VOID\r
-MpInitLibSevEsAPReset (\r
- IN GHCB *Ghcb,\r
- IN CPU_MP_DATA *CpuMpData\r
- )\r
-{\r
- EFI_STATUS Status;\r
- UINTN ProcessorNumber;\r
- UINT16 Code16, Code32;\r
- AP_RESET *APResetFn;\r
- UINTN BufferStart;\r
- UINTN StackStart;\r
-\r
- Status = GetProcessorNumber (CpuMpData, &ProcessorNumber);\r
- ASSERT_EFI_ERROR (Status);\r
-\r
- Code16 = GetProtectedMode16CS ();\r
- Code32 = GetProtectedMode32CS ();\r
-\r
- if (CpuMpData->WakeupBufferHigh != 0) {\r
- APResetFn = (AP_RESET *)(CpuMpData->WakeupBufferHigh + CpuMpData->AddressMap.SwitchToRealNoNxOffset);\r
- } else {\r
- APResetFn = (AP_RESET *)(CpuMpData->MpCpuExchangeInfo->BufferStart + CpuMpData->AddressMap.SwitchToRealOffset);\r
- }\r
-\r
- BufferStart = CpuMpData->MpCpuExchangeInfo->BufferStart;\r
- StackStart = CpuMpData->SevEsAPResetStackStart -\r
- (AP_RESET_STACK_SIZE * ProcessorNumber);\r
-\r
- //\r
- // This call never returns.\r
- //\r
- APResetFn (BufferStart, Code16, Code32, StackStart);\r
-}\r
-\r
/**\r
This function will be called from AP reset code if BSP uses WakeUpAP.\r
\r
while (TRUE) {\r
DisableInterrupts ();\r
if (CpuMpData->SevEsIsEnabled) {\r
- MSR_SEV_ES_GHCB_REGISTER Msr;\r
- GHCB *Ghcb;\r
- UINT64 Status;\r
- BOOLEAN DoDecrement;\r
- BOOLEAN InterruptState;\r
-\r
- DoDecrement = (BOOLEAN)(CpuMpData->InitFlag == ApInitConfig);\r
-\r
- while (TRUE) {\r
- Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);\r
- Ghcb = Msr.Ghcb;\r
-\r
- VmgInit (Ghcb, &InterruptState);\r
-\r
- if (DoDecrement) {\r
- DoDecrement = FALSE;\r
-\r
- //\r
- // Perform the delayed decrement just before issuing the first\r
- // VMGEXIT with AP_RESET_HOLD.\r
- //\r
- InterlockedDecrement ((UINT32 *)&CpuMpData->MpCpuExchangeInfo->NumApsExecuting);\r
- }\r
-\r
- Status = VmgExit (Ghcb, SVM_EXIT_AP_RESET_HOLD, 0, 0);\r
- if ((Status == 0) && (Ghcb->SaveArea.SwExitInfo2 != 0)) {\r
- VmgDone (Ghcb, InterruptState);\r
- break;\r
- }\r
-\r
- VmgDone (Ghcb, InterruptState);\r
- }\r
-\r
- //\r
- // Awakened in a new phase? Use the new CpuMpData\r
- //\r
- if (CpuMpData->NewCpuMpData != NULL) {\r
- CpuMpData = CpuMpData->NewCpuMpData;\r
- }\r
-\r
- MpInitLibSevEsAPReset (Ghcb, CpuMpData);\r
+ SevEsPlaceApHlt (CpuMpData);\r
} else {\r
CpuSleep ();\r
}\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
/**\r
This function will be called by BSP to wakeup AP.\r
\r
#include <Library/PcdLib.h>\r
#include <Library/MicrocodeLib.h>\r
\r
+#include <Register/Amd/Fam17Msr.h>\r
+#include <Register/Amd/Ghcb.h>\r
+\r
#include <Guid/MicrocodePatchHob.h>\r
\r
#define WAKEUP_AP_SIGNAL SIGNATURE_32 ('S', 'T', 'A', 'P')\r
from long mode to real mode.\r
**/\r
typedef\r
-VOID\r
+ VOID\r
(EFIAPI AP_RESET)(\r
IN UINTN BufferStart,\r
IN UINT16 Code16,\r
@param[in] PmCodeSegment Protected mode code segment value.\r
**/\r
typedef\r
-VOID\r
+ VOID\r
(EFIAPI *ASM_RELOCATE_AP_LOOP)(\r
IN BOOLEAN MwaitSupport,\r
IN UINTN ApTargetCState,\r
IN OUT CPU_MP_DATA *CpuMpData\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
+/**\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
+/**\r
+ The function puts the AP in halt loop.\r
+\r
+ @param[in] CpuMpData The pointer to CPU MP Data structure.\r
+**/\r
+VOID\r
+SevEsPlaceApHlt (\r
+ CPU_MP_DATA *CpuMpData\r
+ );\r
+\r
#endif\r
X64/MpFuncs.nasm\r
\r
[Sources.common]\r
+ AmdSev.c\r
MpEqu.inc\r
PeiMpLib.c\r
MpLib.c\r
--- /dev/null
+;------------------------------------------------------------------------------ ;\r
+; Copyright (c) 2021, AMD Inc. All rights reserved.<BR>\r
+; SPDX-License-Identifier: BSD-2-Clause-Patent\r
+;\r
+; Module Name:\r
+;\r
+; AmdSev.nasm\r
+;\r
+; Abstract:\r
+;\r
+; This provides helper used by the MpFunc.nasm. If AMD SEV-ES is active\r
+; then helpers perform the additional setups (such as GHCB).\r
+;\r
+;-------------------------------------------------------------------------------\r
+\r
+%define SIZE_4KB 0x1000\r
+\r
+;\r
+; The function checks whether SEV-ES is enabled, if enabled\r
+; then setup the GHCB page.\r
+;\r
+SevEsSetupGhcb:\r
+ lea edi, [esi + MP_CPU_EXCHANGE_INFO_FIELD (SevEsIsEnabled)]\r
+ cmp byte [edi], 1 ; SevEsIsEnabled\r
+ jne SevEsSetupGhcbExit\r
+\r
+ ;\r
+ ; program GHCB\r
+ ; Each page after the GHCB is a per-CPU page, so the calculation programs\r
+ ; a GHCB to be every 8KB.\r
+ ;\r
+ mov eax, SIZE_4KB\r
+ shl eax, 1 ; EAX = SIZE_4K * 2\r
+ mov ecx, ebx\r
+ mul ecx ; EAX = SIZE_4K * 2 * CpuNumber\r
+ mov edi, esi\r
+ add edi, MP_CPU_EXCHANGE_INFO_FIELD (GhcbBase)\r
+ add rax, qword [edi]\r
+ mov rdx, rax\r
+ shr rdx, 32\r
+ mov rcx, 0xc0010130\r
+ wrmsr\r
+\r
+SevEsSetupGhcbExit:\r
+ OneTimeCallRet SevEsSetupGhcb\r
+\r
+;\r
+; The function checks whether SEV-ES is enabled, if enabled, use\r
+; the GHCB\r
+;\r
+SevEsGetApicId:\r
+ lea edi, [esi + MP_CPU_EXCHANGE_INFO_FIELD (SevEsIsEnabled)]\r
+ cmp byte [edi], 1 ; SevEsIsEnabled\r
+ jne SevEsGetApicIdExit\r
+\r
+ ;\r
+ ; Since we don't have a stack yet, we can't take a #VC\r
+ ; exception. Use the GHCB protocol to perform the CPUID\r
+ ; calls.\r
+ ;\r
+ mov rcx, 0xc0010130\r
+ rdmsr\r
+ shl rdx, 32\r
+ or rax, rdx\r
+ mov rdi, rax ; RDI now holds the original GHCB GPA\r
+\r
+ mov rdx, 0 ; CPUID function 0\r
+ mov rax, 0 ; RAX register requested\r
+ or rax, 4\r
+ wrmsr\r
+ rep vmmcall\r
+ rdmsr\r
+ cmp edx, 0bh\r
+ jb NoX2ApicSevEs ; CPUID level below CPUID_EXTENDED_TOPOLOGY\r
+\r
+ mov rdx, 0bh ; CPUID function 0x0b\r
+ mov rax, 040000000h ; RBX register requested\r
+ or rax, 4\r
+ wrmsr\r
+ rep vmmcall\r
+ rdmsr\r
+ test edx, 0ffffh\r
+ jz NoX2ApicSevEs ; CPUID.0BH:EBX[15:0] is zero\r
+\r
+ mov rdx, 0bh ; CPUID function 0x0b\r
+ mov rax, 0c0000000h ; RDX register requested\r
+ or rax, 4\r
+ wrmsr\r
+ rep vmmcall\r
+ rdmsr\r
+\r
+ ; Processor is x2APIC capable; 32-bit x2APIC ID is now in EDX\r
+ jmp RestoreGhcb\r
+\r
+NoX2ApicSevEs:\r
+ ; Processor is not x2APIC capable, so get 8-bit APIC ID\r
+ mov rdx, 1 ; CPUID function 1\r
+ mov rax, 040000000h ; RBX register requested\r
+ or rax, 4\r
+ wrmsr\r
+ rep vmmcall\r
+ rdmsr\r
+ shr edx, 24\r
+\r
+RestoreGhcb:\r
+ mov rbx, rdx ; Save x2APIC/APIC ID\r
+\r
+ mov rdx, rdi ; RDI holds the saved GHCB GPA\r
+ shr rdx, 32\r
+ mov eax, edi\r
+ wrmsr\r
+\r
+ mov rdx, rbx\r
+\r
+ ; x2APIC ID or APIC ID is in EDX\r
+ jmp GetProcessorNumber\r
+\r
+SevEsGetApicIdExit:\r
+ OneTimeCallRet SevEsGetApicId\r
%include "MpEqu.inc"\r
extern ASM_PFX(InitializeFloatingPointUnits)\r
\r
+%macro OneTimeCall 1\r
+ jmp %1\r
+%1 %+ OneTimerCallReturn:\r
+%endmacro\r
+\r
+%macro OneTimeCallRet 1\r
+ jmp %1 %+ OneTimerCallReturn\r
+%endmacro\r
+\r
DEFAULT REL\r
\r
SECTION .text\r
jmp far [edi]\r
\r
BITS 64\r
+\r
+;\r
+; Required for the AMD SEV helper functions\r
+;\r
+%include "AmdSev.nasm"\r
+\r
LongModeStart:\r
mov esi, ebx\r
lea edi, [esi + MP_CPU_EXCHANGE_INFO_FIELD (InitFlag)]\r
add rax, qword [edi]\r
mov rsp, rax\r
\r
- lea edi, [esi + MP_CPU_EXCHANGE_INFO_FIELD (SevEsIsEnabled)]\r
- cmp byte [edi], 1 ; SevEsIsEnabled\r
- jne CProcedureInvoke\r
-\r
;\r
- ; program GHCB\r
- ; Each page after the GHCB is a per-CPU page, so the calculation programs\r
- ; a GHCB to be every 8KB.\r
+ ; Setup the GHCB when AMD SEV-ES active.\r
;\r
- mov eax, SIZE_4KB\r
- shl eax, 1 ; EAX = SIZE_4K * 2\r
- mov ecx, ebx\r
- mul ecx ; EAX = SIZE_4K * 2 * CpuNumber\r
- mov edi, esi\r
- add edi, MP_CPU_EXCHANGE_INFO_FIELD (GhcbBase)\r
- add rax, qword [edi]\r
- mov rdx, rax\r
- shr rdx, 32\r
- mov rcx, 0xc0010130\r
- wrmsr\r
+ OneTimeCall SevEsSetupGhcb\r
jmp CProcedureInvoke\r
\r
GetApicId:\r
- lea edi, [esi + MP_CPU_EXCHANGE_INFO_FIELD (SevEsIsEnabled)]\r
- cmp byte [edi], 1 ; SevEsIsEnabled\r
- jne DoCpuid\r
-\r
;\r
- ; Since we don't have a stack yet, we can't take a #VC\r
- ; exception. Use the GHCB protocol to perform the CPUID\r
- ; calls.\r
+ ; Use the GHCB protocol to get the ApicId when SEV-ES is active.\r
;\r
- mov rcx, 0xc0010130\r
- rdmsr\r
- shl rdx, 32\r
- or rax, rdx\r
- mov rdi, rax ; RDI now holds the original GHCB GPA\r
-\r
- mov rdx, 0 ; CPUID function 0\r
- mov rax, 0 ; RAX register requested\r
- or rax, 4\r
- wrmsr\r
- rep vmmcall\r
- rdmsr\r
- cmp edx, 0bh\r
- jb NoX2ApicSevEs ; CPUID level below CPUID_EXTENDED_TOPOLOGY\r
-\r
- mov rdx, 0bh ; CPUID function 0x0b\r
- mov rax, 040000000h ; RBX register requested\r
- or rax, 4\r
- wrmsr\r
- rep vmmcall\r
- rdmsr\r
- test edx, 0ffffh\r
- jz NoX2ApicSevEs ; CPUID.0BH:EBX[15:0] is zero\r
-\r
- mov rdx, 0bh ; CPUID function 0x0b\r
- mov rax, 0c0000000h ; RDX register requested\r
- or rax, 4\r
- wrmsr\r
- rep vmmcall\r
- rdmsr\r
-\r
- ; Processor is x2APIC capable; 32-bit x2APIC ID is now in EDX\r
- jmp RestoreGhcb\r
-\r
-NoX2ApicSevEs:\r
- ; Processor is not x2APIC capable, so get 8-bit APIC ID\r
- mov rdx, 1 ; CPUID function 1\r
- mov rax, 040000000h ; RBX register requested\r
- or rax, 4\r
- wrmsr\r
- rep vmmcall\r
- rdmsr\r
- shr edx, 24\r
-\r
-RestoreGhcb:\r
- mov rbx, rdx ; Save x2APIC/APIC ID\r
-\r
- mov rdx, rdi ; RDI holds the saved GHCB GPA\r
- shr rdx, 32\r
- mov eax, edi\r
- wrmsr\r
-\r
- mov rdx, rbx\r
-\r
- ; x2APIC ID or APIC ID is in EDX\r
- jmp GetProcessorNumber\r
+ OneTimeCall SevEsGetApicId\r
\r
DoCpuid:\r
mov eax, 0\r