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