]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/ResetVector/Ia32/AmdSev.asm
OvmfPkg/ResetVector: add the macro to request guest termination
[mirror_edk2.git] / OvmfPkg / ResetVector / Ia32 / AmdSev.asm
CommitLineData
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
10BITS 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
67BITS 64\r
68 rep vmmcall\r
69BITS 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
104BITS 64\r
105 rep vmmcall\r
106BITS 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
117SevEsUnexpectedRespTerminate:\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
130CheckSevFeatures:\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
193GetSevEncBit:\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
205SevEncBitLowHlt:\r
206 cli\r
207 hlt\r
208 jmp SevEncBitLowHlt\r
209\r
210SevSaveMask:\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
218NoSev:\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
229NoSevEsVcHlt:\r
230 cli\r
231 hlt\r
232 jmp NoSevEsVcHlt\r
233\r
234NoSevPass:\r
235 xor eax, eax\r
236\r
237SevExit:\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
257IsSevEsEnabled:\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
267SevEsDisabled:\r
268 OneTimeCallRet IsSevEsEnabled\r
269\r
270; Start of #VC exception handling routines\r
271;\r
272\r
273SevEsIdtNotCpuid:\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
282SevEsIdtVmmComm:\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
324NextReg:\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
356VmmDone:\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
378ALIGN 2\r
379\r
380Idtr:\r
381 dw IDT_END - IDT_BASE - 1 ; Limit\r
382 dd ADDR_OF(IDT_BASE) ; Base\r
383\r
384IdtrClear:\r
385 dw 0 ; Limit\r
386 dd 0 ; Base\r
387\r
388ALIGN 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
397IDT_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
426IDT_END:\r