mirror of https://github.com/acidanthera/audk.git
280 lines
6.9 KiB
C
280 lines
6.9 KiB
C
/** @file
|
|
CPU MP Initialize helper function for AMD SEV.
|
|
|
|
Copyright (c) 2021, AMD Inc. All rights reserved.<BR>
|
|
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
|
|
**/
|
|
|
|
#include "MpLib.h"
|
|
#include <Library/CcExitLib.h>
|
|
|
|
/**
|
|
Get Protected mode code segment with 16-bit default addressing
|
|
from current GDT table.
|
|
|
|
@return Protected mode 16-bit code segment value.
|
|
**/
|
|
STATIC
|
|
UINT16
|
|
GetProtectedMode16CS (
|
|
VOID
|
|
)
|
|
{
|
|
IA32_DESCRIPTOR GdtrDesc;
|
|
IA32_SEGMENT_DESCRIPTOR *GdtEntry;
|
|
UINTN GdtEntryCount;
|
|
UINT16 Index;
|
|
|
|
Index = (UINT16)-1;
|
|
AsmReadGdtr (&GdtrDesc);
|
|
GdtEntryCount = (GdtrDesc.Limit + 1) / sizeof (IA32_SEGMENT_DESCRIPTOR);
|
|
GdtEntry = (IA32_SEGMENT_DESCRIPTOR *)GdtrDesc.Base;
|
|
for (Index = 0; Index < GdtEntryCount; Index++) {
|
|
if ((GdtEntry->Bits.L == 0) &&
|
|
(GdtEntry->Bits.DB == 0) &&
|
|
(GdtEntry->Bits.Type > 8))
|
|
{
|
|
break;
|
|
}
|
|
|
|
GdtEntry++;
|
|
}
|
|
|
|
ASSERT (Index != GdtEntryCount);
|
|
return Index * 8;
|
|
}
|
|
|
|
/**
|
|
Get Protected mode code segment with 32-bit default addressing
|
|
from current GDT table.
|
|
|
|
@return Protected mode 32-bit code segment value.
|
|
**/
|
|
STATIC
|
|
UINT16
|
|
GetProtectedMode32CS (
|
|
VOID
|
|
)
|
|
{
|
|
IA32_DESCRIPTOR GdtrDesc;
|
|
IA32_SEGMENT_DESCRIPTOR *GdtEntry;
|
|
UINTN GdtEntryCount;
|
|
UINT16 Index;
|
|
|
|
Index = (UINT16)-1;
|
|
AsmReadGdtr (&GdtrDesc);
|
|
GdtEntryCount = (GdtrDesc.Limit + 1) / sizeof (IA32_SEGMENT_DESCRIPTOR);
|
|
GdtEntry = (IA32_SEGMENT_DESCRIPTOR *)GdtrDesc.Base;
|
|
for (Index = 0; Index < GdtEntryCount; Index++) {
|
|
if ((GdtEntry->Bits.L == 0) &&
|
|
(GdtEntry->Bits.DB == 1) &&
|
|
(GdtEntry->Bits.Type > 8))
|
|
{
|
|
break;
|
|
}
|
|
|
|
GdtEntry++;
|
|
}
|
|
|
|
ASSERT (Index != GdtEntryCount);
|
|
return Index * 8;
|
|
}
|
|
|
|
/**
|
|
Reset an AP when in SEV-ES mode.
|
|
|
|
If successful, this function never returns.
|
|
|
|
@param[in] Ghcb Pointer to the GHCB
|
|
@param[in] CpuMpData Pointer to CPU MP Data
|
|
|
|
**/
|
|
VOID
|
|
MpInitLibSevEsAPReset (
|
|
IN GHCB *Ghcb,
|
|
IN CPU_MP_DATA *CpuMpData
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN ProcessorNumber;
|
|
UINT16 Code16, Code32;
|
|
AP_RESET *APResetFn;
|
|
UINTN BufferStart;
|
|
UINTN StackStart;
|
|
|
|
Status = GetProcessorNumber (CpuMpData, &ProcessorNumber);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
Code16 = GetProtectedMode16CS ();
|
|
Code32 = GetProtectedMode32CS ();
|
|
|
|
APResetFn = (AP_RESET *)(CpuMpData->WakeupBufferHigh + CpuMpData->AddressMap.SwitchToRealNoNxOffset);
|
|
|
|
BufferStart = CpuMpData->MpCpuExchangeInfo->BufferStart;
|
|
StackStart = CpuMpData->SevEsAPResetStackStart -
|
|
(AP_RESET_STACK_SIZE * ProcessorNumber);
|
|
|
|
//
|
|
// This call never returns.
|
|
//
|
|
APResetFn (BufferStart, Code16, Code32, StackStart);
|
|
}
|
|
|
|
/**
|
|
Allocate the SEV-ES AP jump table buffer.
|
|
|
|
@param[in, out] CpuMpData The pointer to CPU MP Data structure.
|
|
**/
|
|
VOID
|
|
AllocateSevEsAPMemory (
|
|
IN OUT CPU_MP_DATA *CpuMpData
|
|
)
|
|
{
|
|
if (CpuMpData->SevEsAPBuffer == (UINTN)-1) {
|
|
CpuMpData->SevEsAPBuffer =
|
|
CpuMpData->SevEsIsEnabled ? GetSevEsAPMemory () : 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
Program the SEV-ES AP jump table buffer.
|
|
|
|
@param[in] SipiVector The SIPI vector used for the AP Reset
|
|
**/
|
|
VOID
|
|
SetSevEsJumpTable (
|
|
IN UINTN SipiVector
|
|
)
|
|
{
|
|
SEV_ES_AP_JMP_FAR *JmpFar;
|
|
UINT32 Offset, InsnByte;
|
|
UINT8 LoNib, HiNib;
|
|
|
|
JmpFar = (SEV_ES_AP_JMP_FAR *)(UINTN)FixedPcdGet32 (PcdSevEsWorkAreaBase);
|
|
ASSERT (JmpFar != NULL);
|
|
|
|
//
|
|
// Obtain the address of the Segment/Rip location in the workarea.
|
|
// This will be set to a value derived from the SIPI vector and will
|
|
// be the memory address used for the far jump below.
|
|
//
|
|
Offset = FixedPcdGet32 (PcdSevEsWorkAreaBase);
|
|
Offset += sizeof (JmpFar->InsnBuffer);
|
|
LoNib = (UINT8)Offset;
|
|
HiNib = (UINT8)(Offset >> 8);
|
|
|
|
//
|
|
// Program the workarea (which is the initial AP boot address) with
|
|
// far jump to the SIPI vector (where XX and YY represent the
|
|
// address of where the SIPI vector is stored.
|
|
//
|
|
// JMP FAR [CS:XXYY] => 2E FF 2E YY XX
|
|
//
|
|
InsnByte = 0;
|
|
JmpFar->InsnBuffer[InsnByte++] = 0x2E; // CS override prefix
|
|
JmpFar->InsnBuffer[InsnByte++] = 0xFF; // JMP (FAR)
|
|
JmpFar->InsnBuffer[InsnByte++] = 0x2E; // ModRM (JMP memory location)
|
|
JmpFar->InsnBuffer[InsnByte++] = LoNib; // YY offset ...
|
|
JmpFar->InsnBuffer[InsnByte++] = HiNib; // XX offset ...
|
|
|
|
//
|
|
// Program the Segment/Rip based on the SIPI vector (always at least
|
|
// 16-byte aligned, so Rip is set to 0).
|
|
//
|
|
JmpFar->Rip = 0;
|
|
JmpFar->Segment = (UINT16)(SipiVector >> 4);
|
|
}
|
|
|
|
/**
|
|
The function puts the AP in halt loop.
|
|
|
|
@param[in] CpuMpData The pointer to CPU MP Data structure.
|
|
**/
|
|
VOID
|
|
SevEsPlaceApHlt (
|
|
CPU_MP_DATA *CpuMpData
|
|
)
|
|
{
|
|
MSR_SEV_ES_GHCB_REGISTER Msr;
|
|
GHCB *Ghcb;
|
|
UINT64 Status;
|
|
BOOLEAN DoDecrement;
|
|
BOOLEAN InterruptState;
|
|
|
|
DoDecrement = (BOOLEAN)(CpuMpData->InitFlag == ApInitConfig);
|
|
|
|
while (TRUE) {
|
|
Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);
|
|
Ghcb = Msr.Ghcb;
|
|
|
|
CcExitVmgInit (Ghcb, &InterruptState);
|
|
|
|
if (DoDecrement) {
|
|
DoDecrement = FALSE;
|
|
|
|
//
|
|
// Perform the delayed decrement just before issuing the first
|
|
// VMGEXIT with AP_RESET_HOLD.
|
|
//
|
|
InterlockedDecrement ((UINT32 *)&CpuMpData->MpCpuExchangeInfo->NumApsExecuting);
|
|
}
|
|
|
|
Status = CcExitVmgExit (Ghcb, SVM_EXIT_AP_RESET_HOLD, 0, 0);
|
|
if ((Status == 0) && (Ghcb->SaveArea.SwExitInfo2 != 0)) {
|
|
CcExitVmgDone (Ghcb, InterruptState);
|
|
break;
|
|
}
|
|
|
|
CcExitVmgDone (Ghcb, InterruptState);
|
|
}
|
|
|
|
//
|
|
// Awakened in a new phase? Use the new CpuMpData
|
|
//
|
|
if (CpuMpData->NewCpuMpData != NULL) {
|
|
CpuMpData = CpuMpData->NewCpuMpData;
|
|
}
|
|
|
|
MpInitLibSevEsAPReset (Ghcb, CpuMpData);
|
|
}
|
|
|
|
/**
|
|
The function fills the exchange data for the AP.
|
|
|
|
@param[in] ExchangeInfo The pointer to CPU Exchange Data structure
|
|
**/
|
|
VOID
|
|
FillExchangeInfoDataSevEs (
|
|
IN volatile MP_CPU_EXCHANGE_INFO *ExchangeInfo
|
|
)
|
|
{
|
|
UINT32 StdRangeMax;
|
|
|
|
AsmCpuid (CPUID_SIGNATURE, &StdRangeMax, NULL, NULL, NULL);
|
|
if (StdRangeMax >= CPUID_EXTENDED_TOPOLOGY) {
|
|
CPUID_EXTENDED_TOPOLOGY_EBX ExtTopoEbx;
|
|
|
|
AsmCpuid (CPUID_EXTENDED_TOPOLOGY, NULL, &ExtTopoEbx.Uint32, NULL, NULL);
|
|
ExchangeInfo->ExtTopoAvail = !!ExtTopoEbx.Bits.LogicalProcessors;
|
|
}
|
|
}
|
|
|
|
/**
|
|
Get pointer to CPU MP Data structure from GUIDed HOB.
|
|
|
|
@param[in] CpuMpData The pointer to CPU MP Data structure.
|
|
**/
|
|
VOID
|
|
AmdSevUpdateCpuMpData (
|
|
IN CPU_MP_DATA *CpuMpData
|
|
)
|
|
{
|
|
CPU_MP_DATA *OldCpuMpData;
|
|
|
|
OldCpuMpData = GetCpuMpDataFromGuidedHob ();
|
|
|
|
OldCpuMpData->NewCpuMpData = CpuMpData;
|
|
}
|