]>
Commit | Line | Data |
---|---|---|
b461d676 BS |
1 | ;------------------------------------------------------------------------------\r |
2 | ; @file\r | |
3 | ; Provide the functions to check whether SEV and SEV-ES is enabled.\r | |
4 | ;\r | |
5 | ; Copyright (c) 2017 - 2021, Advanced Micro Devices, Inc. All rights reserved.<BR>\r | |
6 | ; SPDX-License-Identifier: BSD-2-Clause-Patent\r | |
7 | ;\r | |
8 | ;------------------------------------------------------------------------------\r | |
9 | \r | |
10 | BITS 32\r | |
11 | \r | |
12 | ;\r | |
13 | ; SEV-ES #VC exception handler support\r | |
14 | ;\r | |
15 | ; #VC handler local variable locations\r | |
16 | ;\r | |
17 | %define VC_CPUID_RESULT_EAX 0\r | |
18 | %define VC_CPUID_RESULT_EBX 4\r | |
19 | %define VC_CPUID_RESULT_ECX 8\r | |
20 | %define VC_CPUID_RESULT_EDX 12\r | |
21 | %define VC_GHCB_MSR_EDX 16\r | |
22 | %define VC_GHCB_MSR_EAX 20\r | |
23 | %define VC_CPUID_REQUEST_REGISTER 24\r | |
24 | %define VC_CPUID_FUNCTION 28\r | |
25 | \r | |
26 | ; #VC handler total local variable size\r | |
27 | ;\r | |
28 | %define VC_VARIABLE_SIZE 32\r | |
29 | \r | |
30 | ; #VC handler GHCB CPUID request/response protocol values\r | |
31 | ;\r | |
32 | %define GHCB_CPUID_REQUEST 4\r | |
33 | %define GHCB_CPUID_RESPONSE 5\r | |
34 | %define GHCB_CPUID_REGISTER_SHIFT 30\r | |
35 | %define CPUID_INSN_LEN 2\r | |
36 | \r | |
37 | \r | |
7f05102f BS |
38 | %define SEV_GHCB_MSR 0xc0010130\r |
39 | %define SEV_STATUS_MSR 0xc0010131\r | |
40 | \r | |
dc485c55 BS |
41 | ; The #VC was not for CPUID\r |
42 | %define TERM_VC_NOT_CPUID 1\r | |
43 | \r | |
44 | ; The unexpected response code\r | |
45 | %define TERM_UNEXPECTED_RESP_CODE 2\r | |
46 | \r | |
47 | \r | |
7f05102f BS |
48 | ; Macro is used to issue the MSR protocol based VMGEXIT. The caller is\r |
49 | ; responsible to populate values in the EDX:EAX registers. After the vmmcall\r | |
50 | ; returns, it verifies that the response code matches with the expected\r | |
51 | ; code. If it does not match then terminate the guest. The result of request\r | |
52 | ; is returned in the EDX:EAX.\r | |
53 | ;\r | |
54 | ; args 1:Request code, 2: Response code\r | |
55 | %macro VmgExit 2\r | |
56 | ;\r | |
57 | ; Add request code:\r | |
58 | ; GHCB_MSR[11:0] = Request code\r | |
59 | or eax, %1\r | |
60 | \r | |
61 | mov ecx, SEV_GHCB_MSR\r | |
62 | wrmsr\r | |
63 | \r | |
64 | ; Issue VMGEXIT - NASM doesn't support the vmmcall instruction in 32-bit\r | |
65 | ; mode, so work around this by temporarily switching to 64-bit mode.\r | |
66 | ;\r | |
67 | BITS 64\r | |
68 | rep vmmcall\r | |
69 | BITS 32\r | |
70 | \r | |
71 | mov ecx, SEV_GHCB_MSR\r | |
72 | rdmsr\r | |
73 | \r | |
74 | ;\r | |
75 | ; Verify the reponse code, if it does not match then request to terminate\r | |
76 | ; GHCB_MSR[11:0] = Response code\r | |
77 | mov ecx, eax\r | |
78 | and ecx, 0xfff\r | |
79 | cmp ecx, %2\r | |
80 | jne SevEsUnexpectedRespTerminate\r | |
81 | %endmacro\r | |
82 | \r | |
dc485c55 BS |
83 | ; Macro to terminate the guest using the VMGEXIT.\r |
84 | ; arg 1: reason code\r | |
85 | %macro TerminateVmgExit 1\r | |
86 | mov eax, %1\r | |
87 | ;\r | |
88 | ; Use VMGEXIT to request termination. At this point the reason code is\r | |
89 | ; located in EAX, so shift it left 16 bits to the proper location.\r | |
90 | ;\r | |
91 | ; EAX[11:0] => 0x100 - request termination\r | |
92 | ; EAX[15:12] => 0x1 - OVMF\r | |
93 | ; EAX[23:16] => 0xXX - REASON CODE\r | |
94 | ;\r | |
95 | shl eax, 16\r | |
96 | or eax, 0x1100\r | |
97 | xor edx, edx\r | |
98 | mov ecx, SEV_GHCB_MSR\r | |
99 | wrmsr\r | |
100 | ;\r | |
101 | ; Issue VMGEXIT - NASM doesn't support the vmmcall instruction in 32-bit\r | |
102 | ; mode, so work around this by temporarily switching to 64-bit mode.\r | |
103 | ;\r | |
104 | BITS 64\r | |
105 | rep vmmcall\r | |
106 | BITS 32\r | |
107 | \r | |
108 | ;\r | |
109 | ; We shouldn't come back from the VMGEXIT, but if we do, just loop.\r | |
110 | ;\r | |
111 | %%TerminateHlt:\r | |
112 | hlt\r | |
113 | jmp %%TerminateHlt\r | |
114 | %endmacro\r | |
115 | \r | |
116 | ; Terminate the guest due to unexpected response code.\r | |
117 | SevEsUnexpectedRespTerminate:\r | |
118 | TerminateVmgExit TERM_UNEXPECTED_RESP_CODE\r | |
119 | \r | |
b461d676 BS |
120 | ; Check if Secure Encrypted Virtualization (SEV) features are enabled.\r |
121 | ;\r | |
122 | ; Register usage is tight in this routine, so multiple calls for the\r | |
123 | ; same CPUID and MSR data are performed to keep things simple.\r | |
124 | ;\r | |
125 | ; Modified: EAX, EBX, ECX, EDX, ESP\r | |
126 | ;\r | |
127 | ; If SEV is enabled then EAX will be at least 32.\r | |
128 | ; If SEV is disabled then EAX will be zero.\r | |
129 | ;\r | |
130 | CheckSevFeatures:\r | |
131 | ; Set the first byte of the workarea to zero to communicate to the SEC\r | |
132 | ; phase that SEV-ES is not enabled. If SEV-ES is enabled, the CPUID\r | |
133 | ; instruction will trigger a #VC exception where the first byte of the\r | |
134 | ; workarea will be set to one or, if CPUID is not being intercepted,\r | |
135 | ; the MSR check below will set the first byte of the workarea to one.\r | |
136 | mov byte[SEV_ES_WORK_AREA], 0\r | |
137 | \r | |
138 | ;\r | |
139 | ; Set up exception handlers to check for SEV-ES\r | |
140 | ; Load temporary RAM stack based on PCDs (see SevEsIdtVmmComm for\r | |
141 | ; stack usage)\r | |
142 | ; Establish exception handlers\r | |
143 | ;\r | |
144 | mov esp, SEV_ES_VC_TOP_OF_STACK\r | |
145 | mov eax, ADDR_OF(Idtr)\r | |
146 | lidt [cs:eax]\r | |
147 | \r | |
148 | ; Check if we have a valid (0x8000_001F) CPUID leaf\r | |
149 | ; CPUID raises a #VC exception if running as an SEV-ES guest\r | |
150 | mov eax, 0x80000000\r | |
151 | cpuid\r | |
152 | \r | |
153 | ; This check should fail on Intel or Non SEV AMD CPUs. In future if\r | |
154 | ; Intel CPUs supports this CPUID leaf then we are guranteed to have exact\r | |
155 | ; same bit definition.\r | |
156 | cmp eax, 0x8000001f\r | |
157 | jl NoSev\r | |
158 | \r | |
159 | ; Check for SEV memory encryption feature:\r | |
160 | ; CPUID Fn8000_001F[EAX] - Bit 1\r | |
161 | ; CPUID raises a #VC exception if running as an SEV-ES guest\r | |
162 | mov eax, 0x8000001f\r | |
163 | cpuid\r | |
164 | bt eax, 1\r | |
165 | jnc NoSev\r | |
166 | \r | |
167 | ; Check if SEV memory encryption is enabled\r | |
168 | ; MSR_0xC0010131 - Bit 0 (SEV enabled)\r | |
7f05102f | 169 | mov ecx, SEV_STATUS_MSR\r |
b461d676 BS |
170 | rdmsr\r |
171 | bt eax, 0\r | |
172 | jnc NoSev\r | |
173 | \r | |
174 | ; Check for SEV-ES memory encryption feature:\r | |
175 | ; CPUID Fn8000_001F[EAX] - Bit 3\r | |
176 | ; CPUID raises a #VC exception if running as an SEV-ES guest\r | |
177 | mov eax, 0x8000001f\r | |
178 | cpuid\r | |
179 | bt eax, 3\r | |
180 | jnc GetSevEncBit\r | |
181 | \r | |
182 | ; Check if SEV-ES is enabled\r | |
183 | ; MSR_0xC0010131 - Bit 1 (SEV-ES enabled)\r | |
7f05102f | 184 | mov ecx, SEV_STATUS_MSR\r |
b461d676 BS |
185 | rdmsr\r |
186 | bt eax, 1\r | |
187 | jnc GetSevEncBit\r | |
188 | \r | |
189 | ; Set the first byte of the workarea to one to communicate to the SEC\r | |
190 | ; phase that SEV-ES is enabled.\r | |
191 | mov byte[SEV_ES_WORK_AREA], 1\r | |
192 | \r | |
193 | GetSevEncBit:\r | |
194 | ; Get pte bit position to enable memory encryption\r | |
195 | ; CPUID Fn8000_001F[EBX] - Bits 5:0\r | |
196 | ;\r | |
197 | and ebx, 0x3f\r | |
198 | mov eax, ebx\r | |
199 | \r | |
200 | ; The encryption bit position is always above 31\r | |
201 | sub ebx, 32\r | |
202 | jns SevSaveMask\r | |
203 | \r | |
204 | ; Encryption bit was reported as 31 or below, enter a HLT loop\r | |
205 | SevEncBitLowHlt:\r | |
206 | cli\r | |
207 | hlt\r | |
208 | jmp SevEncBitLowHlt\r | |
209 | \r | |
210 | SevSaveMask:\r | |
211 | xor edx, edx\r | |
212 | bts edx, ebx\r | |
213 | \r | |
214 | mov dword[SEV_ES_WORK_AREA_ENC_MASK], 0\r | |
215 | mov dword[SEV_ES_WORK_AREA_ENC_MASK + 4], edx\r | |
216 | jmp SevExit\r | |
217 | \r | |
218 | NoSev:\r | |
219 | ;\r | |
220 | ; Perform an SEV-ES sanity check by seeing if a #VC exception occurred.\r | |
221 | ;\r | |
222 | cmp byte[SEV_ES_WORK_AREA], 0\r | |
223 | jz NoSevPass\r | |
224 | \r | |
225 | ;\r | |
226 | ; A #VC was received, yet CPUID indicates no SEV-ES support, something\r | |
227 | ; isn't right.\r | |
228 | ;\r | |
229 | NoSevEsVcHlt:\r | |
230 | cli\r | |
231 | hlt\r | |
232 | jmp NoSevEsVcHlt\r | |
233 | \r | |
234 | NoSevPass:\r | |
235 | xor eax, eax\r | |
236 | \r | |
237 | SevExit:\r | |
238 | ;\r | |
239 | ; Clear exception handlers and stack\r | |
240 | ;\r | |
241 | push eax\r | |
242 | mov eax, ADDR_OF(IdtrClear)\r | |
243 | lidt [cs:eax]\r | |
244 | pop eax\r | |
245 | mov esp, 0\r | |
246 | \r | |
247 | OneTimeCallRet CheckSevFeatures\r | |
248 | \r | |
249 | ; Check if Secure Encrypted Virtualization - Encrypted State (SEV-ES) feature\r | |
250 | ; is enabled.\r | |
251 | ;\r | |
252 | ; Modified: EAX\r | |
253 | ;\r | |
254 | ; If SEV-ES is enabled then EAX will be non-zero.\r | |
255 | ; If SEV-ES is disabled then EAX will be zero.\r | |
256 | ;\r | |
257 | IsSevEsEnabled:\r | |
258 | xor eax, eax\r | |
259 | \r | |
260 | ; During CheckSevFeatures, the SEV_ES_WORK_AREA was set to 1 if\r | |
261 | ; SEV-ES is enabled.\r | |
262 | cmp byte[SEV_ES_WORK_AREA], 1\r | |
263 | jne SevEsDisabled\r | |
264 | \r | |
265 | mov eax, 1\r | |
266 | \r | |
267 | SevEsDisabled:\r | |
268 | OneTimeCallRet IsSevEsEnabled\r | |
269 | \r | |
270 | ; Start of #VC exception handling routines\r | |
271 | ;\r | |
272 | \r | |
273 | SevEsIdtNotCpuid:\r | |
dc485c55 | 274 | TerminateVmgExit TERM_VC_NOT_CPUID\r |
b461d676 BS |
275 | iret\r |
276 | \r | |
277 | ;\r | |
278 | ; Total stack usage for the #VC handler is 44 bytes:\r | |
279 | ; - 12 bytes for the exception IRET (after popping error code)\r | |
280 | ; - 32 bytes for the local variables.\r | |
281 | ;\r | |
282 | SevEsIdtVmmComm:\r | |
283 | ;\r | |
284 | ; If we're here, then we are an SEV-ES guest and this\r | |
285 | ; was triggered by a CPUID instruction\r | |
286 | ;\r | |
287 | ; Set the first byte of the workarea to one to communicate that\r | |
288 | ; a #VC was taken.\r | |
289 | mov byte[SEV_ES_WORK_AREA], 1\r | |
290 | \r | |
291 | pop ecx ; Error code\r | |
292 | cmp ecx, 0x72 ; Be sure it was CPUID\r | |
293 | jne SevEsIdtNotCpuid\r | |
294 | \r | |
295 | ; Set up local variable room on the stack\r | |
296 | ; CPUID function : + 28\r | |
297 | ; CPUID request register : + 24\r | |
298 | ; GHCB MSR (EAX) : + 20\r | |
299 | ; GHCB MSR (EDX) : + 16\r | |
300 | ; CPUID result (EDX) : + 12\r | |
301 | ; CPUID result (ECX) : + 8\r | |
302 | ; CPUID result (EBX) : + 4\r | |
303 | ; CPUID result (EAX) : + 0\r | |
304 | sub esp, VC_VARIABLE_SIZE\r | |
305 | \r | |
306 | ; Save the CPUID function being requested\r | |
307 | mov [esp + VC_CPUID_FUNCTION], eax\r | |
308 | \r | |
309 | ; The GHCB CPUID protocol uses the following mapping to request\r | |
310 | ; a specific register:\r | |
311 | ; 0 => EAX, 1 => EBX, 2 => ECX, 3 => EDX\r | |
312 | ;\r | |
313 | ; Set EAX as the first register to request. This will also be used as a\r | |
314 | ; loop variable to request all register values (EAX to EDX).\r | |
315 | xor eax, eax\r | |
316 | mov [esp + VC_CPUID_REQUEST_REGISTER], eax\r | |
317 | \r | |
318 | ; Save current GHCB MSR value\r | |
7f05102f | 319 | mov ecx, SEV_GHCB_MSR\r |
b461d676 BS |
320 | rdmsr\r |
321 | mov [esp + VC_GHCB_MSR_EAX], eax\r | |
322 | mov [esp + VC_GHCB_MSR_EDX], edx\r | |
323 | \r | |
324 | NextReg:\r | |
325 | ;\r | |
326 | ; Setup GHCB MSR\r | |
327 | ; GHCB_MSR[63:32] = CPUID function\r | |
328 | ; GHCB_MSR[31:30] = CPUID register\r | |
329 | ; GHCB_MSR[11:0] = CPUID request protocol\r | |
330 | ;\r | |
331 | mov eax, [esp + VC_CPUID_REQUEST_REGISTER]\r | |
332 | cmp eax, 4\r | |
333 | jge VmmDone\r | |
334 | \r | |
335 | shl eax, GHCB_CPUID_REGISTER_SHIFT\r | |
b461d676 | 336 | mov edx, [esp + VC_CPUID_FUNCTION]\r |
b461d676 | 337 | \r |
7f05102f | 338 | VmgExit GHCB_CPUID_REQUEST, GHCB_CPUID_RESPONSE\r |
b461d676 BS |
339 | \r |
340 | ;\r | |
7f05102f | 341 | ; Response GHCB MSR\r |
b461d676 BS |
342 | ; GHCB_MSR[63:32] = CPUID register value\r |
343 | ; GHCB_MSR[31:30] = CPUID register\r | |
344 | ; GHCB_MSR[11:0] = CPUID response protocol\r | |
345 | ;\r | |
b461d676 BS |
346 | \r |
347 | ; Save returned value\r | |
348 | shr eax, GHCB_CPUID_REGISTER_SHIFT\r | |
349 | mov [esp + eax * 4], edx\r | |
350 | \r | |
351 | ; Next register\r | |
352 | inc word [esp + VC_CPUID_REQUEST_REGISTER]\r | |
353 | \r | |
354 | jmp NextReg\r | |
355 | \r | |
356 | VmmDone:\r | |
357 | ;\r | |
358 | ; At this point we have all CPUID register values. Restore the GHCB MSR,\r | |
359 | ; set the return register values and return.\r | |
360 | ;\r | |
361 | mov eax, [esp + VC_GHCB_MSR_EAX]\r | |
362 | mov edx, [esp + VC_GHCB_MSR_EDX]\r | |
7f05102f | 363 | mov ecx, SEV_GHCB_MSR\r |
b461d676 BS |
364 | wrmsr\r |
365 | \r | |
366 | mov eax, [esp + VC_CPUID_RESULT_EAX]\r | |
367 | mov ebx, [esp + VC_CPUID_RESULT_EBX]\r | |
368 | mov ecx, [esp + VC_CPUID_RESULT_ECX]\r | |
369 | mov edx, [esp + VC_CPUID_RESULT_EDX]\r | |
370 | \r | |
371 | add esp, VC_VARIABLE_SIZE\r | |
372 | \r | |
373 | ; Update the EIP value to skip over the now handled CPUID instruction\r | |
374 | ; (the CPUID instruction has a length of 2)\r | |
375 | add word [esp], CPUID_INSN_LEN\r | |
376 | iret\r | |
377 | \r | |
378 | ALIGN 2\r | |
379 | \r | |
380 | Idtr:\r | |
381 | dw IDT_END - IDT_BASE - 1 ; Limit\r | |
382 | dd ADDR_OF(IDT_BASE) ; Base\r | |
383 | \r | |
384 | IdtrClear:\r | |
385 | dw 0 ; Limit\r | |
386 | dd 0 ; Base\r | |
387 | \r | |
388 | ALIGN 16\r | |
389 | \r | |
390 | ;\r | |
391 | ; The Interrupt Descriptor Table (IDT)\r | |
392 | ; This will be used to determine if SEV-ES is enabled. Upon execution\r | |
393 | ; of the CPUID instruction, a VMM Communication Exception will occur.\r | |
394 | ; This will tell us if SEV-ES is enabled. We can use the current value\r | |
395 | ; of the GHCB MSR to determine the SEV attributes.\r | |
396 | ;\r | |
397 | IDT_BASE:\r | |
398 | ;\r | |
399 | ; Vectors 0 - 28 (No handlers)\r | |
400 | ;\r | |
401 | %rep 29\r | |
402 | dw 0 ; Offset low bits 15..0\r | |
403 | dw 0x10 ; Selector\r | |
404 | db 0 ; Reserved\r | |
405 | db 0x8E ; Gate Type (IA32_IDT_GATE_TYPE_INTERRUPT_32)\r | |
406 | dw 0 ; Offset high bits 31..16\r | |
407 | %endrep\r | |
408 | ;\r | |
409 | ; Vector 29 (VMM Communication Exception)\r | |
410 | ;\r | |
411 | dw (ADDR_OF(SevEsIdtVmmComm) & 0xffff) ; Offset low bits 15..0\r | |
412 | dw 0x10 ; Selector\r | |
413 | db 0 ; Reserved\r | |
414 | db 0x8E ; Gate Type (IA32_IDT_GATE_TYPE_INTERRUPT_32)\r | |
415 | dw (ADDR_OF(SevEsIdtVmmComm) >> 16) ; Offset high bits 31..16\r | |
416 | ;\r | |
417 | ; Vectors 30 - 31 (No handlers)\r | |
418 | ;\r | |
419 | %rep 2\r | |
420 | dw 0 ; Offset low bits 15..0\r | |
421 | dw 0x10 ; Selector\r | |
422 | db 0 ; Reserved\r | |
423 | db 0x8E ; Gate Type (IA32_IDT_GATE_TYPE_INTERRUPT_32)\r | |
424 | dw 0 ; Offset high bits 31..16\r | |
425 | %endrep\r | |
426 | IDT_END:\r |