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 if (CpuMpData
->WakeupBufferHigh
!= 0) {
114 APResetFn
= (AP_RESET
*)(CpuMpData
->WakeupBufferHigh
+ CpuMpData
->AddressMap
.SwitchToRealNoNxOffset
);
116 APResetFn
= (AP_RESET
*)(CpuMpData
->MpCpuExchangeInfo
->BufferStart
+ CpuMpData
->AddressMap
.SwitchToRealOffset
);
119 BufferStart
= CpuMpData
->MpCpuExchangeInfo
->BufferStart
;
120 StackStart
= CpuMpData
->SevEsAPResetStackStart
-
121 (AP_RESET_STACK_SIZE
* ProcessorNumber
);
124 // This call never returns.
126 APResetFn (BufferStart
, Code16
, Code32
, StackStart
);
130 Allocate the SEV-ES AP jump table buffer.
132 @param[in, out] CpuMpData The pointer to CPU MP Data structure.
135 AllocateSevEsAPMemory (
136 IN OUT CPU_MP_DATA
*CpuMpData
139 if (CpuMpData
->SevEsAPBuffer
== (UINTN
)-1) {
140 CpuMpData
->SevEsAPBuffer
=
141 CpuMpData
->SevEsIsEnabled
? GetSevEsAPMemory () : 0;
146 Program the SEV-ES AP jump table buffer.
148 @param[in] SipiVector The SIPI vector used for the AP Reset
155 SEV_ES_AP_JMP_FAR
*JmpFar
;
156 UINT32 Offset
, InsnByte
;
159 JmpFar
= (SEV_ES_AP_JMP_FAR
*)(UINTN
)FixedPcdGet32 (PcdSevEsWorkAreaBase
);
160 ASSERT (JmpFar
!= NULL
);
163 // Obtain the address of the Segment/Rip location in the workarea.
164 // This will be set to a value derived from the SIPI vector and will
165 // be the memory address used for the far jump below.
167 Offset
= FixedPcdGet32 (PcdSevEsWorkAreaBase
);
168 Offset
+= sizeof (JmpFar
->InsnBuffer
);
169 LoNib
= (UINT8
)Offset
;
170 HiNib
= (UINT8
)(Offset
>> 8);
173 // Program the workarea (which is the initial AP boot address) with
174 // far jump to the SIPI vector (where XX and YY represent the
175 // address of where the SIPI vector is stored.
177 // JMP FAR [CS:XXYY] => 2E FF 2E YY XX
180 JmpFar
->InsnBuffer
[InsnByte
++] = 0x2E; // CS override prefix
181 JmpFar
->InsnBuffer
[InsnByte
++] = 0xFF; // JMP (FAR)
182 JmpFar
->InsnBuffer
[InsnByte
++] = 0x2E; // ModRM (JMP memory location)
183 JmpFar
->InsnBuffer
[InsnByte
++] = LoNib
; // YY offset ...
184 JmpFar
->InsnBuffer
[InsnByte
++] = HiNib
; // XX offset ...
187 // Program the Segment/Rip based on the SIPI vector (always at least
188 // 16-byte aligned, so Rip is set to 0).
191 JmpFar
->Segment
= (UINT16
)(SipiVector
>> 4);
195 The function puts the AP in halt loop.
197 @param[in] CpuMpData The pointer to CPU MP Data structure.
201 CPU_MP_DATA
*CpuMpData
204 MSR_SEV_ES_GHCB_REGISTER Msr
;
208 BOOLEAN InterruptState
;
210 DoDecrement
= (BOOLEAN
)(CpuMpData
->InitFlag
== ApInitConfig
);
213 Msr
.GhcbPhysicalAddress
= AsmReadMsr64 (MSR_SEV_ES_GHCB
);
216 VmgInit (Ghcb
, &InterruptState
);
222 // Perform the delayed decrement just before issuing the first
223 // VMGEXIT with AP_RESET_HOLD.
225 InterlockedDecrement ((UINT32
*)&CpuMpData
->MpCpuExchangeInfo
->NumApsExecuting
);
228 Status
= VmgExit (Ghcb
, SVM_EXIT_AP_RESET_HOLD
, 0, 0);
229 if ((Status
== 0) && (Ghcb
->SaveArea
.SwExitInfo2
!= 0)) {
230 VmgDone (Ghcb
, InterruptState
);
234 VmgDone (Ghcb
, InterruptState
);
238 // Awakened in a new phase? Use the new CpuMpData
240 if (CpuMpData
->NewCpuMpData
!= NULL
) {
241 CpuMpData
= CpuMpData
->NewCpuMpData
;
244 MpInitLibSevEsAPReset (Ghcb
, CpuMpData
);
248 The function fills the exchange data for the AP.
250 @param[in] ExchangeInfo The pointer to CPU Exchange Data structure
253 FillExchangeInfoDataSevEs (
254 IN
volatile MP_CPU_EXCHANGE_INFO
*ExchangeInfo
259 AsmCpuid (CPUID_SIGNATURE
, &StdRangeMax
, NULL
, NULL
, NULL
);
260 if (StdRangeMax
>= CPUID_EXTENDED_TOPOLOGY
) {
261 CPUID_EXTENDED_TOPOLOGY_EBX ExtTopoEbx
;
263 AsmCpuid (CPUID_EXTENDED_TOPOLOGY
, NULL
, &ExtTopoEbx
.Uint32
, NULL
, NULL
);
264 ExchangeInfo
->ExtTopoAvail
= !!ExtTopoEbx
.Bits
.LogicalProcessors
;