]> git.proxmox.com Git - mirror_edk2.git/blob - UefiCpuPkg/Library/MpInitLib/AmdSev.c
UefiCpuPkg/MpInitLib: move SEV specific routines in AmdSev.c
[mirror_edk2.git] / UefiCpuPkg / Library / MpInitLib / AmdSev.c
1 /** @file
2 CPU MP Initialize helper function for AMD SEV.
3
4 Copyright (c) 2021, AMD Inc. All rights reserved.<BR>
5
6 SPDX-License-Identifier: BSD-2-Clause-Patent
7
8 **/
9
10 #include "MpLib.h"
11 #include <Library/VmgExitLib.h>
12
13 /**
14 Get Protected mode code segment with 16-bit default addressing
15 from current GDT table.
16
17 @return Protected mode 16-bit code segment value.
18 **/
19 STATIC
20 UINT16
21 GetProtectedMode16CS (
22 VOID
23 )
24 {
25 IA32_DESCRIPTOR GdtrDesc;
26 IA32_SEGMENT_DESCRIPTOR *GdtEntry;
27 UINTN GdtEntryCount;
28 UINT16 Index;
29
30 Index = (UINT16)-1;
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))
38 {
39 break;
40 }
41
42 GdtEntry++;
43 }
44
45 ASSERT (Index != GdtEntryCount);
46 return Index * 8;
47 }
48
49 /**
50 Get Protected mode code segment with 32-bit default addressing
51 from current GDT table.
52
53 @return Protected mode 32-bit code segment value.
54 **/
55 STATIC
56 UINT16
57 GetProtectedMode32CS (
58 VOID
59 )
60 {
61 IA32_DESCRIPTOR GdtrDesc;
62 IA32_SEGMENT_DESCRIPTOR *GdtEntry;
63 UINTN GdtEntryCount;
64 UINT16 Index;
65
66 Index = (UINT16)-1;
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))
74 {
75 break;
76 }
77
78 GdtEntry++;
79 }
80
81 ASSERT (Index != GdtEntryCount);
82 return Index * 8;
83 }
84
85 /**
86 Reset an AP when in SEV-ES mode.
87
88 If successful, this function never returns.
89
90 @param[in] Ghcb Pointer to the GHCB
91 @param[in] CpuMpData Pointer to CPU MP Data
92
93 **/
94 VOID
95 MpInitLibSevEsAPReset (
96 IN GHCB *Ghcb,
97 IN CPU_MP_DATA *CpuMpData
98 )
99 {
100 EFI_STATUS Status;
101 UINTN ProcessorNumber;
102 UINT16 Code16, Code32;
103 AP_RESET *APResetFn;
104 UINTN BufferStart;
105 UINTN StackStart;
106
107 Status = GetProcessorNumber (CpuMpData, &ProcessorNumber);
108 ASSERT_EFI_ERROR (Status);
109
110 Code16 = GetProtectedMode16CS ();
111 Code32 = GetProtectedMode32CS ();
112
113 if (CpuMpData->WakeupBufferHigh != 0) {
114 APResetFn = (AP_RESET *)(CpuMpData->WakeupBufferHigh + CpuMpData->AddressMap.SwitchToRealNoNxOffset);
115 } else {
116 APResetFn = (AP_RESET *)(CpuMpData->MpCpuExchangeInfo->BufferStart + CpuMpData->AddressMap.SwitchToRealOffset);
117 }
118
119 BufferStart = CpuMpData->MpCpuExchangeInfo->BufferStart;
120 StackStart = CpuMpData->SevEsAPResetStackStart -
121 (AP_RESET_STACK_SIZE * ProcessorNumber);
122
123 //
124 // This call never returns.
125 //
126 APResetFn (BufferStart, Code16, Code32, StackStart);
127 }
128
129 /**
130 Allocate the SEV-ES AP jump table buffer.
131
132 @param[in, out] CpuMpData The pointer to CPU MP Data structure.
133 **/
134 VOID
135 AllocateSevEsAPMemory (
136 IN OUT CPU_MP_DATA *CpuMpData
137 )
138 {
139 if (CpuMpData->SevEsAPBuffer == (UINTN)-1) {
140 CpuMpData->SevEsAPBuffer =
141 CpuMpData->SevEsIsEnabled ? GetSevEsAPMemory () : 0;
142 }
143 }
144
145 /**
146 Program the SEV-ES AP jump table buffer.
147
148 @param[in] SipiVector The SIPI vector used for the AP Reset
149 **/
150 VOID
151 SetSevEsJumpTable (
152 IN UINTN SipiVector
153 )
154 {
155 SEV_ES_AP_JMP_FAR *JmpFar;
156 UINT32 Offset, InsnByte;
157 UINT8 LoNib, HiNib;
158
159 JmpFar = (SEV_ES_AP_JMP_FAR *)(UINTN)FixedPcdGet32 (PcdSevEsWorkAreaBase);
160 ASSERT (JmpFar != NULL);
161
162 //
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.
166 //
167 Offset = FixedPcdGet32 (PcdSevEsWorkAreaBase);
168 Offset += sizeof (JmpFar->InsnBuffer);
169 LoNib = (UINT8)Offset;
170 HiNib = (UINT8)(Offset >> 8);
171
172 //
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.
176 //
177 // JMP FAR [CS:XXYY] => 2E FF 2E YY XX
178 //
179 InsnByte = 0;
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 ...
185
186 //
187 // Program the Segment/Rip based on the SIPI vector (always at least
188 // 16-byte aligned, so Rip is set to 0).
189 //
190 JmpFar->Rip = 0;
191 JmpFar->Segment = (UINT16)(SipiVector >> 4);
192 }
193
194 /**
195 The function puts the AP in halt loop.
196
197 @param[in] CpuMpData The pointer to CPU MP Data structure.
198 **/
199 VOID
200 SevEsPlaceApHlt (
201 CPU_MP_DATA *CpuMpData
202 )
203 {
204 MSR_SEV_ES_GHCB_REGISTER Msr;
205 GHCB *Ghcb;
206 UINT64 Status;
207 BOOLEAN DoDecrement;
208 BOOLEAN InterruptState;
209
210 DoDecrement = (BOOLEAN)(CpuMpData->InitFlag == ApInitConfig);
211
212 while (TRUE) {
213 Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);
214 Ghcb = Msr.Ghcb;
215
216 VmgInit (Ghcb, &InterruptState);
217
218 if (DoDecrement) {
219 DoDecrement = FALSE;
220
221 //
222 // Perform the delayed decrement just before issuing the first
223 // VMGEXIT with AP_RESET_HOLD.
224 //
225 InterlockedDecrement ((UINT32 *)&CpuMpData->MpCpuExchangeInfo->NumApsExecuting);
226 }
227
228 Status = VmgExit (Ghcb, SVM_EXIT_AP_RESET_HOLD, 0, 0);
229 if ((Status == 0) && (Ghcb->SaveArea.SwExitInfo2 != 0)) {
230 VmgDone (Ghcb, InterruptState);
231 break;
232 }
233
234 VmgDone (Ghcb, InterruptState);
235 }
236
237 //
238 // Awakened in a new phase? Use the new CpuMpData
239 //
240 if (CpuMpData->NewCpuMpData != NULL) {
241 CpuMpData = CpuMpData->NewCpuMpData;
242 }
243
244 MpInitLibSevEsAPReset (Ghcb, CpuMpData);
245 }