]>
Commit | Line | Data |
---|---|---|
06544455 TL |
1 | /** @file\r |
2 | \r | |
3 | AMD SEV helper function.\r | |
4 | \r | |
5 | Copyright (c) 2021, AMD Incorporated. All rights reserved.<BR>\r | |
6 | \r | |
7 | SPDX-License-Identifier: BSD-2-Clause-Patent\r | |
8 | \r | |
9 | **/\r | |
10 | \r | |
11 | #include "MpLib.h"\r | |
a89f558d | 12 | #include <Library/CcExitLib.h>\r |
06544455 TL |
13 | #include <Register/Amd/Fam17Msr.h>\r |
14 | #include <Register/Amd/Ghcb.h>\r | |
15 | \r | |
16 | /**\r | |
17 | Create an SEV-SNP AP save area (VMSA) for use in running the vCPU.\r | |
18 | \r | |
19 | @param[in] CpuMpData Pointer to CPU MP Data\r | |
20 | @param[in] CpuData Pointer to CPU AP Data\r | |
21 | @param[in] ApicId APIC ID of the vCPU\r | |
22 | **/\r | |
23 | VOID\r | |
24 | SevSnpCreateSaveArea (\r | |
25 | IN CPU_MP_DATA *CpuMpData,\r | |
26 | IN CPU_AP_DATA *CpuData,\r | |
27 | UINT32 ApicId\r | |
28 | )\r | |
29 | {\r | |
30 | SEV_ES_SAVE_AREA *SaveArea;\r | |
31 | IA32_CR0 ApCr0;\r | |
32 | IA32_CR0 ResetCr0;\r | |
33 | IA32_CR4 ApCr4;\r | |
34 | IA32_CR4 ResetCr4;\r | |
35 | UINTN StartIp;\r | |
36 | UINT8 SipiVector;\r | |
37 | UINT32 RmpAdjustStatus;\r | |
38 | UINT64 VmgExitStatus;\r | |
39 | MSR_SEV_ES_GHCB_REGISTER Msr;\r | |
40 | GHCB *Ghcb;\r | |
41 | BOOLEAN InterruptState;\r | |
42 | UINT64 ExitInfo1;\r | |
43 | UINT64 ExitInfo2;\r | |
44 | \r | |
45 | //\r | |
46 | // Allocate a single page for the SEV-ES Save Area and initialize it.\r | |
47 | //\r | |
48 | SaveArea = AllocateReservedPages (1);\r | |
49 | if (!SaveArea) {\r | |
50 | return;\r | |
51 | }\r | |
52 | \r | |
53 | ZeroMem (SaveArea, EFI_PAGE_SIZE);\r | |
54 | \r | |
55 | //\r | |
56 | // Propogate the CR0.NW and CR0.CD setting to the AP\r | |
57 | //\r | |
58 | ResetCr0.UintN = 0x00000010;\r | |
59 | ApCr0.UintN = CpuData->VolatileRegisters.Cr0;\r | |
60 | if (ApCr0.Bits.NW) {\r | |
61 | ResetCr0.Bits.NW = 1;\r | |
62 | }\r | |
63 | \r | |
64 | if (ApCr0.Bits.CD) {\r | |
65 | ResetCr0.Bits.CD = 1;\r | |
66 | }\r | |
67 | \r | |
68 | //\r | |
69 | // Propagate the CR4.MCE setting to the AP\r | |
70 | //\r | |
71 | ResetCr4.UintN = 0;\r | |
72 | ApCr4.UintN = CpuData->VolatileRegisters.Cr4;\r | |
73 | if (ApCr4.Bits.MCE) {\r | |
74 | ResetCr4.Bits.MCE = 1;\r | |
75 | }\r | |
76 | \r | |
77 | //\r | |
78 | // Convert the start IP into a SIPI Vector\r | |
79 | //\r | |
80 | StartIp = CpuMpData->MpCpuExchangeInfo->BufferStart;\r | |
81 | SipiVector = (UINT8)(StartIp >> 12);\r | |
82 | \r | |
83 | //\r | |
84 | // Set the CS:RIP value based on the start IP\r | |
85 | //\r | |
86 | SaveArea->Cs.Base = SipiVector << 12;\r | |
87 | SaveArea->Cs.Selector = SipiVector << 8;\r | |
88 | SaveArea->Cs.Limit = 0xFFFF;\r | |
89 | SaveArea->Cs.Attributes.Bits.Present = 1;\r | |
90 | SaveArea->Cs.Attributes.Bits.Sbit = 1;\r | |
91 | SaveArea->Cs.Attributes.Bits.Type = SEV_ES_RESET_CODE_SEGMENT_TYPE;\r | |
92 | SaveArea->Rip = StartIp & 0xFFF;\r | |
93 | \r | |
94 | //\r | |
95 | // Set the remaining values as defined in APM for INIT\r | |
96 | //\r | |
97 | SaveArea->Ds.Limit = 0xFFFF;\r | |
98 | SaveArea->Ds.Attributes.Bits.Present = 1;\r | |
99 | SaveArea->Ds.Attributes.Bits.Sbit = 1;\r | |
100 | SaveArea->Ds.Attributes.Bits.Type = SEV_ES_RESET_DATA_SEGMENT_TYPE;\r | |
101 | SaveArea->Es = SaveArea->Ds;\r | |
102 | SaveArea->Fs = SaveArea->Ds;\r | |
103 | SaveArea->Gs = SaveArea->Ds;\r | |
104 | SaveArea->Ss = SaveArea->Ds;\r | |
105 | \r | |
106 | SaveArea->Gdtr.Limit = 0xFFFF;\r | |
107 | SaveArea->Ldtr.Limit = 0xFFFF;\r | |
108 | SaveArea->Ldtr.Attributes.Bits.Present = 1;\r | |
109 | SaveArea->Ldtr.Attributes.Bits.Type = SEV_ES_RESET_LDT_TYPE;\r | |
110 | SaveArea->Idtr.Limit = 0xFFFF;\r | |
111 | SaveArea->Tr.Limit = 0xFFFF;\r | |
112 | SaveArea->Ldtr.Attributes.Bits.Present = 1;\r | |
113 | SaveArea->Ldtr.Attributes.Bits.Type = SEV_ES_RESET_TSS_TYPE;\r | |
114 | \r | |
115 | SaveArea->Efer = 0x1000;\r | |
116 | SaveArea->Cr4 = ResetCr4.UintN;\r | |
117 | SaveArea->Cr0 = ResetCr0.UintN;\r | |
118 | SaveArea->Dr7 = 0x0400;\r | |
119 | SaveArea->Dr6 = 0xFFFF0FF0;\r | |
120 | SaveArea->Rflags = 0x0002;\r | |
121 | SaveArea->GPat = 0x0007040600070406ULL;\r | |
122 | SaveArea->XCr0 = 0x0001;\r | |
123 | SaveArea->Mxcsr = 0x1F80;\r | |
124 | SaveArea->X87Ftw = 0x5555;\r | |
125 | SaveArea->X87Fcw = 0x0040;\r | |
126 | \r | |
127 | //\r | |
128 | // Set the SEV-SNP specific fields for the save area:\r | |
129 | // VMPL - always VMPL0\r | |
130 | // SEV_FEATURES - equivalent to the SEV_STATUS MSR right shifted 2 bits\r | |
131 | //\r | |
132 | SaveArea->Vmpl = 0;\r | |
133 | SaveArea->SevFeatures = AsmReadMsr64 (MSR_SEV_STATUS) >> 2;\r | |
134 | \r | |
135 | //\r | |
136 | // To turn the page into a recognized VMSA page, issue RMPADJUST:\r | |
137 | // Target VMPL but numerically higher than current VMPL\r | |
138 | // Target PermissionMask is not used\r | |
139 | //\r | |
140 | RmpAdjustStatus = SevSnpRmpAdjust (\r | |
141 | (EFI_PHYSICAL_ADDRESS)(UINTN)SaveArea,\r | |
142 | TRUE\r | |
143 | );\r | |
144 | ASSERT (RmpAdjustStatus == 0);\r | |
145 | \r | |
146 | ExitInfo1 = (UINT64)ApicId << 32;\r | |
147 | ExitInfo1 |= SVM_VMGEXIT_SNP_AP_CREATE;\r | |
148 | ExitInfo2 = (UINT64)(UINTN)SaveArea;\r | |
149 | \r | |
150 | Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);\r | |
151 | Ghcb = Msr.Ghcb;\r | |
152 | \r | |
765ba5bf | 153 | CcExitVmgInit (Ghcb, &InterruptState);\r |
06544455 | 154 | Ghcb->SaveArea.Rax = SaveArea->SevFeatures;\r |
765ba5bf MX |
155 | CcExitVmgSetOffsetValid (Ghcb, GhcbRax);\r |
156 | VmgExitStatus = CcExitVmgExit (\r | |
06544455 TL |
157 | Ghcb,\r |
158 | SVM_EXIT_SNP_AP_CREATION,\r | |
159 | ExitInfo1,\r | |
160 | ExitInfo2\r | |
161 | );\r | |
765ba5bf | 162 | CcExitVmgDone (Ghcb, InterruptState);\r |
06544455 TL |
163 | \r |
164 | ASSERT (VmgExitStatus == 0);\r | |
165 | if (VmgExitStatus != 0) {\r | |
166 | RmpAdjustStatus = SevSnpRmpAdjust (\r | |
167 | (EFI_PHYSICAL_ADDRESS)(UINTN)SaveArea,\r | |
168 | FALSE\r | |
169 | );\r | |
170 | if (RmpAdjustStatus == 0) {\r | |
171 | FreePages (SaveArea, 1);\r | |
172 | } else {\r | |
173 | DEBUG ((DEBUG_INFO, "SEV-SNP: RMPADJUST failed, leaking VMSA page\n"));\r | |
174 | }\r | |
175 | \r | |
176 | SaveArea = NULL;\r | |
177 | }\r | |
178 | \r | |
179 | if (CpuData->SevEsSaveArea) {\r | |
180 | RmpAdjustStatus = SevSnpRmpAdjust (\r | |
181 | (EFI_PHYSICAL_ADDRESS)(UINTN)CpuData->SevEsSaveArea,\r | |
182 | FALSE\r | |
183 | );\r | |
184 | if (RmpAdjustStatus == 0) {\r | |
185 | FreePages (CpuData->SevEsSaveArea, 1);\r | |
186 | } else {\r | |
187 | DEBUG ((DEBUG_INFO, "SEV-SNP: RMPADJUST failed, leaking VMSA page\n"));\r | |
188 | }\r | |
189 | }\r | |
190 | \r | |
191 | CpuData->SevEsSaveArea = SaveArea;\r | |
192 | }\r | |
193 | \r | |
194 | /**\r | |
195 | Create SEV-SNP APs.\r | |
196 | \r | |
197 | @param[in] CpuMpData Pointer to CPU MP Data\r | |
198 | @param[in] ProcessorNumber The handle number of specified processor\r | |
199 | (-1 for all APs)\r | |
200 | **/\r | |
201 | VOID\r | |
202 | SevSnpCreateAP (\r | |
203 | IN CPU_MP_DATA *CpuMpData,\r | |
204 | IN INTN ProcessorNumber\r | |
205 | )\r | |
206 | {\r | |
207 | CPU_INFO_IN_HOB *CpuInfoInHob;\r | |
208 | CPU_AP_DATA *CpuData;\r | |
209 | UINTN Index;\r | |
210 | UINT32 ApicId;\r | |
211 | \r | |
212 | ASSERT (CpuMpData->MpCpuExchangeInfo->BufferStart < 0x100000);\r | |
213 | \r | |
214 | CpuInfoInHob = (CPU_INFO_IN_HOB *)(UINTN)CpuMpData->CpuInfoInHob;\r | |
215 | \r | |
216 | if (ProcessorNumber < 0) {\r | |
217 | for (Index = 0; Index < CpuMpData->CpuCount; Index++) {\r | |
218 | if (Index != CpuMpData->BspNumber) {\r | |
219 | CpuData = &CpuMpData->CpuData[Index];\r | |
220 | ApicId = CpuInfoInHob[Index].ApicId,\r | |
221 | SevSnpCreateSaveArea (CpuMpData, CpuData, ApicId);\r | |
222 | }\r | |
223 | }\r | |
224 | } else {\r | |
225 | Index = (UINTN)ProcessorNumber;\r | |
226 | CpuData = &CpuMpData->CpuData[Index];\r | |
227 | ApicId = CpuInfoInHob[ProcessorNumber].ApicId,\r | |
228 | SevSnpCreateSaveArea (CpuMpData, CpuData, ApicId);\r | |
229 | }\r | |
230 | }\r | |
231 | \r | |
232 | /**\r | |
233 | Issue RMPADJUST to adjust the VMSA attribute of an SEV-SNP page.\r | |
234 | \r | |
235 | @param[in] PageAddress\r | |
236 | @param[in] VmsaPage\r | |
237 | \r | |
238 | @return RMPADJUST return value\r | |
239 | **/\r | |
240 | UINT32\r | |
241 | SevSnpRmpAdjust (\r | |
242 | IN EFI_PHYSICAL_ADDRESS PageAddress,\r | |
243 | IN BOOLEAN VmsaPage\r | |
244 | )\r | |
245 | {\r | |
246 | UINT64 Rdx;\r | |
247 | \r | |
248 | //\r | |
249 | // The RMPADJUST instruction is used to set or clear the VMSA bit for a\r | |
250 | // page. The VMSA change is only made when running at VMPL0 and is ignored\r | |
251 | // otherwise. If too low a target VMPL is specified, the instruction can\r | |
252 | // succeed without changing the VMSA bit when not running at VMPL0. Using a\r | |
253 | // target VMPL level of 1, RMPADJUST will return a FAIL_PERMISSION error if\r | |
254 | // not running at VMPL0, thus ensuring that the VMSA bit is set appropriately\r | |
255 | // when no error is returned.\r | |
256 | //\r | |
257 | Rdx = 1;\r | |
258 | if (VmsaPage) {\r | |
259 | Rdx |= RMPADJUST_VMSA_PAGE_BIT;\r | |
260 | }\r | |
261 | \r | |
262 | return AsmRmpAdjust ((UINT64)PageAddress, 0, Rdx);\r | |
263 | }\r |