2 CPU MP Initialize helper function for AMD SEV.
4 Copyright (c) 2021, AMD Inc. All rights reserved.<BR>
6 SPDX-License-Identifier: BSD-2-Clause-Patent
11 #include <Library/VmgExitLib.h>
14 Get Protected mode code segment with 16-bit default addressing
15 from current GDT table.
17 @return Protected mode 16-bit code segment value.
21 GetProtectedMode16CS (
25 IA32_DESCRIPTOR GdtrDesc
;
26 IA32_SEGMENT_DESCRIPTOR
*GdtEntry
;
31 AsmReadGdtr (&GdtrDesc
);
32 GdtEntryCount
= (GdtrDesc
.Limit
+ 1) / sizeof (IA32_SEGMENT_DESCRIPTOR
);
33 GdtEntry
= (IA32_SEGMENT_DESCRIPTOR
*)GdtrDesc
.Base
;
34 for (Index
= 0; Index
< GdtEntryCount
; Index
++) {
35 if ((GdtEntry
->Bits
.L
== 0) &&
36 (GdtEntry
->Bits
.DB
== 0) &&
37 (GdtEntry
->Bits
.Type
> 8))
45 ASSERT (Index
!= GdtEntryCount
);
50 Get Protected mode code segment with 32-bit default addressing
51 from current GDT table.
53 @return Protected mode 32-bit code segment value.
57 GetProtectedMode32CS (
61 IA32_DESCRIPTOR GdtrDesc
;
62 IA32_SEGMENT_DESCRIPTOR
*GdtEntry
;
67 AsmReadGdtr (&GdtrDesc
);
68 GdtEntryCount
= (GdtrDesc
.Limit
+ 1) / sizeof (IA32_SEGMENT_DESCRIPTOR
);
69 GdtEntry
= (IA32_SEGMENT_DESCRIPTOR
*)GdtrDesc
.Base
;
70 for (Index
= 0; Index
< GdtEntryCount
; Index
++) {
71 if ((GdtEntry
->Bits
.L
== 0) &&
72 (GdtEntry
->Bits
.DB
== 1) &&
73 (GdtEntry
->Bits
.Type
> 8))
81 ASSERT (Index
!= GdtEntryCount
);
86 Reset an AP when in SEV-ES mode.
88 If successful, this function never returns.
90 @param[in] Ghcb Pointer to the GHCB
91 @param[in] CpuMpData Pointer to CPU MP Data
95 MpInitLibSevEsAPReset (
97 IN CPU_MP_DATA
*CpuMpData
101 UINTN ProcessorNumber
;
102 UINT16 Code16
, Code32
;
107 Status
= GetProcessorNumber (CpuMpData
, &ProcessorNumber
);
108 ASSERT_EFI_ERROR (Status
);
110 Code16
= GetProtectedMode16CS ();
111 Code32
= GetProtectedMode32CS ();
113 APResetFn
= (AP_RESET
*)(CpuMpData
->WakeupBufferHigh
+ CpuMpData
->AddressMap
.SwitchToRealNoNxOffset
);
115 BufferStart
= CpuMpData
->MpCpuExchangeInfo
->BufferStart
;
116 StackStart
= CpuMpData
->SevEsAPResetStackStart
-
117 (AP_RESET_STACK_SIZE
* ProcessorNumber
);
120 // This call never returns.
122 APResetFn (BufferStart
, Code16
, Code32
, StackStart
);
126 Allocate the SEV-ES AP jump table buffer.
128 @param[in, out] CpuMpData The pointer to CPU MP Data structure.
131 AllocateSevEsAPMemory (
132 IN OUT CPU_MP_DATA
*CpuMpData
135 if (CpuMpData
->SevEsAPBuffer
== (UINTN
)-1) {
136 CpuMpData
->SevEsAPBuffer
=
137 CpuMpData
->SevEsIsEnabled
? GetSevEsAPMemory () : 0;
142 Program the SEV-ES AP jump table buffer.
144 @param[in] SipiVector The SIPI vector used for the AP Reset
151 SEV_ES_AP_JMP_FAR
*JmpFar
;
152 UINT32 Offset
, InsnByte
;
155 JmpFar
= (SEV_ES_AP_JMP_FAR
*)(UINTN
)FixedPcdGet32 (PcdSevEsWorkAreaBase
);
156 ASSERT (JmpFar
!= NULL
);
159 // Obtain the address of the Segment/Rip location in the workarea.
160 // This will be set to a value derived from the SIPI vector and will
161 // be the memory address used for the far jump below.
163 Offset
= FixedPcdGet32 (PcdSevEsWorkAreaBase
);
164 Offset
+= sizeof (JmpFar
->InsnBuffer
);
165 LoNib
= (UINT8
)Offset
;
166 HiNib
= (UINT8
)(Offset
>> 8);
169 // Program the workarea (which is the initial AP boot address) with
170 // far jump to the SIPI vector (where XX and YY represent the
171 // address of where the SIPI vector is stored.
173 // JMP FAR [CS:XXYY] => 2E FF 2E YY XX
176 JmpFar
->InsnBuffer
[InsnByte
++] = 0x2E; // CS override prefix
177 JmpFar
->InsnBuffer
[InsnByte
++] = 0xFF; // JMP (FAR)
178 JmpFar
->InsnBuffer
[InsnByte
++] = 0x2E; // ModRM (JMP memory location)
179 JmpFar
->InsnBuffer
[InsnByte
++] = LoNib
; // YY offset ...
180 JmpFar
->InsnBuffer
[InsnByte
++] = HiNib
; // XX offset ...
183 // Program the Segment/Rip based on the SIPI vector (always at least
184 // 16-byte aligned, so Rip is set to 0).
187 JmpFar
->Segment
= (UINT16
)(SipiVector
>> 4);
191 The function puts the AP in halt loop.
193 @param[in] CpuMpData The pointer to CPU MP Data structure.
197 CPU_MP_DATA
*CpuMpData
200 MSR_SEV_ES_GHCB_REGISTER Msr
;
204 BOOLEAN InterruptState
;
206 DoDecrement
= (BOOLEAN
)(CpuMpData
->InitFlag
== ApInitConfig
);
209 Msr
.GhcbPhysicalAddress
= AsmReadMsr64 (MSR_SEV_ES_GHCB
);
212 VmgInit (Ghcb
, &InterruptState
);
218 // Perform the delayed decrement just before issuing the first
219 // VMGEXIT with AP_RESET_HOLD.
221 InterlockedDecrement ((UINT32
*)&CpuMpData
->MpCpuExchangeInfo
->NumApsExecuting
);
224 Status
= VmgExit (Ghcb
, SVM_EXIT_AP_RESET_HOLD
, 0, 0);
225 if ((Status
== 0) && (Ghcb
->SaveArea
.SwExitInfo2
!= 0)) {
226 VmgDone (Ghcb
, InterruptState
);
230 VmgDone (Ghcb
, InterruptState
);
234 // Awakened in a new phase? Use the new CpuMpData
236 if (CpuMpData
->NewCpuMpData
!= NULL
) {
237 CpuMpData
= CpuMpData
->NewCpuMpData
;
240 MpInitLibSevEsAPReset (Ghcb
, CpuMpData
);
244 The function fills the exchange data for the AP.
246 @param[in] ExchangeInfo The pointer to CPU Exchange Data structure
249 FillExchangeInfoDataSevEs (
250 IN
volatile MP_CPU_EXCHANGE_INFO
*ExchangeInfo
255 AsmCpuid (CPUID_SIGNATURE
, &StdRangeMax
, NULL
, NULL
, NULL
);
256 if (StdRangeMax
>= CPUID_EXTENDED_TOPOLOGY
) {
257 CPUID_EXTENDED_TOPOLOGY_EBX ExtTopoEbx
;
259 AsmCpuid (CPUID_EXTENDED_TOPOLOGY
, NULL
, &ExtTopoEbx
.Uint32
, NULL
, NULL
);
260 ExchangeInfo
->ExtTopoAvail
= !!ExtTopoEbx
.Bits
.LogicalProcessors
;