]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/Library/CcExitLib/CcExitVeHandler.c
OvmfPkg/UefiCpuPkg/UefiPayloadPkg: Rename VmgExitLib to CcExitLib
[mirror_edk2.git] / OvmfPkg / Library / CcExitLib / CcExitVeHandler.c
CommitLineData
daf8f642
MX
1/** @file\r
2\r
3 Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>\r
4\r
5 SPDX-License-Identifier: BSD-2-Clause-Patent\r
6\r
7**/\r
8\r
9#include <Library/BaseLib.h>\r
10#include <Library/DebugLib.h>\r
a89f558d
MX
11#include "CcExitTd.h"\r
12#include <Library/CcExitLib.h>\r
daf8f642
MX
13#include <Library/BaseMemoryLib.h>\r
14#include <IndustryStandard/Tdx.h>\r
15#include <IndustryStandard/InstructionParsing.h>\r
16\r
17typedef union {\r
18 struct {\r
19 UINT32 Eax;\r
20 UINT32 Edx;\r
21 } Regs;\r
22 UINT64 Val;\r
23} MSR_DATA;\r
24\r
25typedef union {\r
26 UINT8 Val;\r
27 struct {\r
28 UINT8 B : 1;\r
29 UINT8 X : 1;\r
30 UINT8 R : 1;\r
31 UINT8 W : 1;\r
32 } Bits;\r
33} REX;\r
34\r
35typedef union {\r
36 UINT8 Val;\r
37 struct {\r
38 UINT8 Rm : 3;\r
39 UINT8 Reg : 3;\r
40 UINT8 Mod : 2;\r
41 } Bits;\r
42} MODRM;\r
43\r
44typedef struct {\r
45 UINT64 Regs[4];\r
46} CPUID_DATA;\r
47\r
48/**\r
49 Handle an CPUID event.\r
50\r
51 Use the TDVMCALL instruction to handle cpuid #ve\r
52\r
53 @param[in, out] Regs x64 processor context\r
54 @param[in] Veinfo VE Info\r
55\r
56 @retval 0 Event handled successfully\r
57 @return New exception value to propagate\r
58**/\r
59STATIC\r
60UINT64\r
61EFIAPI\r
62CpuIdExit (\r
63 IN EFI_SYSTEM_CONTEXT_X64 *Regs,\r
64 IN TDCALL_VEINFO_RETURN_DATA *Veinfo\r
65 )\r
66{\r
67 CPUID_DATA CpuIdData;\r
68 UINT64 Status;\r
69\r
70 Status = TdVmCallCpuid (Regs->Rax, Regs->Rcx, &CpuIdData);\r
71\r
72 if (Status == 0) {\r
73 Regs->Rax = CpuIdData.Regs[0];\r
74 Regs->Rbx = CpuIdData.Regs[1];\r
75 Regs->Rcx = CpuIdData.Regs[2];\r
76 Regs->Rdx = CpuIdData.Regs[3];\r
77 }\r
78\r
79 return Status;\r
80}\r
81\r
82/**\r
83 Handle an IO event.\r
84\r
85 Use the TDVMCALL instruction to handle either an IO read or an IO write.\r
86\r
87 @param[in, out] Regs x64 processor context\r
88 @param[in] Veinfo VE Info\r
89\r
90 @retval 0 Event handled successfully\r
91 @return New exception value to propagate\r
92**/\r
93STATIC\r
94UINT64\r
95EFIAPI\r
96IoExit (\r
97 IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,\r
98 IN TDCALL_VEINFO_RETURN_DATA *Veinfo\r
99 )\r
100{\r
101 BOOLEAN Write;\r
102 UINTN Size;\r
103 UINTN Port;\r
104 UINT64 Val;\r
105 UINT64 RepCnt;\r
106 UINT64 Status;\r
107\r
108 Val = 0;\r
109 Write = Veinfo->ExitQualification.Io.Direction ? FALSE : TRUE;\r
110 Size = Veinfo->ExitQualification.Io.Size + 1;\r
111 Port = Veinfo->ExitQualification.Io.Port;\r
112\r
113 if (Veinfo->ExitQualification.Io.String) {\r
114 //\r
115 // If REP is set, get rep-cnt from Rcx\r
116 //\r
117 RepCnt = Veinfo->ExitQualification.Io.Rep ? Regs->Rcx : 1;\r
118\r
119 while (RepCnt) {\r
120 Val = 0;\r
121 if (Write == TRUE) {\r
122 CopyMem (&Val, (VOID *)Regs->Rsi, Size);\r
123 Regs->Rsi += Size;\r
124 }\r
125\r
126 Status = TdVmCall (EXIT_REASON_IO_INSTRUCTION, Size, Write, Port, Val, (Write ? NULL : &Val));\r
127 if (Status != 0) {\r
128 break;\r
129 }\r
130\r
131 if (Write == FALSE) {\r
132 CopyMem ((VOID *)Regs->Rdi, &Val, Size);\r
133 Regs->Rdi += Size;\r
134 }\r
135\r
136 if (Veinfo->ExitQualification.Io.Rep) {\r
137 Regs->Rcx -= 1;\r
138 }\r
139\r
140 RepCnt -= 1;\r
141 }\r
142 } else {\r
143 if (Write == TRUE) {\r
144 CopyMem (&Val, (VOID *)&Regs->Rax, Size);\r
145 }\r
146\r
147 Status = TdVmCall (EXIT_REASON_IO_INSTRUCTION, Size, Write, Port, Val, (Write ? NULL : &Val));\r
148 if ((Status == 0) && (Write == FALSE)) {\r
149 CopyMem ((VOID *)&Regs->Rax, &Val, Size);\r
150 }\r
151 }\r
152\r
153 return Status;\r
154}\r
155\r
156/**\r
157 Handle an READ MSR event.\r
158\r
159 Use the TDVMCALL instruction to handle msr read\r
160\r
161 @param[in, out] Regs x64 processor context\r
162 @param[in] Veinfo VE Info\r
163\r
164 @retval 0 Event handled successfully\r
165 @return New exception value to propagate\r
166**/\r
167STATIC\r
168UINT64\r
169ReadMsrExit (\r
170 IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,\r
171 IN TDCALL_VEINFO_RETURN_DATA *Veinfo\r
172 )\r
173{\r
174 MSR_DATA Data;\r
175 UINT64 Status;\r
176\r
177 Status = TdVmCall (EXIT_REASON_MSR_READ, Regs->Rcx, 0, 0, 0, &Data);\r
178 if (Status == 0) {\r
179 Regs->Rax = Data.Regs.Eax;\r
180 Regs->Rdx = Data.Regs.Edx;\r
181 }\r
182\r
183 return Status;\r
184}\r
185\r
186/**\r
187 Handle an WRITE MSR event.\r
188\r
189 Use the TDVMCALL instruction to handle msr write\r
190\r
191 @param[in, out] Regs x64 processor context\r
192 @param[in] Veinfo VE Info\r
193\r
194 @retval 0 Event handled successfully\r
195 @return New exception value to propagate\r
196**/\r
197STATIC\r
198UINT64\r
199WriteMsrExit (\r
200 IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,\r
201 IN TDCALL_VEINFO_RETURN_DATA *Veinfo\r
202 )\r
203{\r
204 UINT64 Status;\r
205 MSR_DATA Data;\r
206\r
207 Data.Regs.Eax = (UINT32)Regs->Rax;\r
208 Data.Regs.Edx = (UINT32)Regs->Rdx;\r
209\r
210 Status = TdVmCall (EXIT_REASON_MSR_WRITE, Regs->Rcx, Data.Val, 0, 0, NULL);\r
211\r
212 return Status;\r
213}\r
214\r
215STATIC\r
216VOID\r
217EFIAPI\r
218TdxDecodeInstruction (\r
219 IN UINT8 *Rip\r
220 )\r
221{\r
222 UINTN i;\r
223\r
224 DEBUG ((DEBUG_INFO, "TDX: #TD[EPT] instruction (%p):", Rip));\r
225 for (i = 0; i < 15; i++) {\r
226 DEBUG ((DEBUG_INFO, "%02x:", Rip[i]));\r
227 }\r
228\r
229 DEBUG ((DEBUG_INFO, "\n"));\r
230}\r
231\r
232#define TDX_DECODER_BUG_ON(x) \\r
233 if ((x)) { \\r
234 TdxDecodeInstruction(Rip); \\r
235 TdVmCall(TDVMCALL_HALT, 0, 0, 0, 0, 0); \\r
236 }\r
237\r
238STATIC\r
239UINT64 *\r
240EFIAPI\r
241GetRegFromContext (\r
242 IN EFI_SYSTEM_CONTEXT_X64 *Regs,\r
243 IN UINTN RegIndex\r
244 )\r
245{\r
246 switch (RegIndex) {\r
247 case 0: return &Regs->Rax;\r
248 break;\r
249 case 1: return &Regs->Rcx;\r
250 break;\r
251 case 2: return &Regs->Rdx;\r
252 break;\r
253 case 3: return &Regs->Rbx;\r
254 break;\r
255 case 4: return &Regs->Rsp;\r
256 break;\r
257 case 5: return &Regs->Rbp;\r
258 break;\r
259 case 6: return &Regs->Rsi;\r
260 break;\r
261 case 7: return &Regs->Rdi;\r
262 break;\r
263 case 8: return &Regs->R8;\r
264 break;\r
265 case 9: return &Regs->R9;\r
266 break;\r
267 case 10: return &Regs->R10;\r
268 break;\r
269 case 11: return &Regs->R11;\r
270 break;\r
271 case 12: return &Regs->R12;\r
272 break;\r
273 case 13: return &Regs->R13;\r
274 break;\r
275 case 14: return &Regs->R14;\r
276 break;\r
277 case 15: return &Regs->R15;\r
278 break;\r
279 }\r
280\r
281 return NULL;\r
282}\r
283\r
284/**\r
285 Handle an MMIO event.\r
286\r
287 Use the TDVMCALL instruction to handle either an mmio read or an mmio write.\r
288\r
289 @param[in, out] Regs x64 processor context\r
290 @param[in] Veinfo VE Info\r
291\r
292 @retval 0 Event handled successfully\r
293 @return New exception value to propagate\r
294**/\r
295STATIC\r
296INTN\r
297EFIAPI\r
298MmioExit (\r
299 IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,\r
300 IN TDCALL_VEINFO_RETURN_DATA *Veinfo\r
301 )\r
302{\r
eff44c00
MX
303 UINT64 Status;\r
304 UINT32 MmioSize;\r
305 UINT32 RegSize;\r
306 UINT8 OpCode;\r
307 BOOLEAN SeenRex;\r
308 UINT64 *Reg;\r
309 UINT8 *Rip;\r
310 UINT64 Val;\r
311 UINT32 OpSize;\r
312 MODRM ModRm;\r
313 REX Rex;\r
314 TD_RETURN_DATA TdReturnData;\r
315 UINT8 Gpaw;\r
316 UINT64 TdSharedPageMask;\r
daf8f642
MX
317\r
318 Rip = (UINT8 *)Regs->Rip;\r
319 Val = 0;\r
320 Rex.Val = 0;\r
321 SeenRex = FALSE;\r
322\r
eff44c00
MX
323 Status = TdCall (TDCALL_TDINFO, 0, 0, 0, &TdReturnData);\r
324 if (Status == TDX_EXIT_REASON_SUCCESS) {\r
325 Gpaw = (UINT8)(TdReturnData.TdInfo.Gpaw & 0x3f);\r
326 TdSharedPageMask = 1ULL << (Gpaw - 1);\r
327 } else {\r
328 DEBUG ((DEBUG_ERROR, "TDCALL failed with status=%llx\n", Status));\r
329 return Status;\r
330 }\r
331\r
332 if ((Veinfo->GuestPA & TdSharedPageMask) == 0) {\r
333 DEBUG ((DEBUG_ERROR, "EPT-violation #VE on private memory is not allowed!"));\r
334 TdVmCall (TDVMCALL_HALT, 0, 0, 0, 0, 0);\r
335 CpuDeadLoop ();\r
336 }\r
337\r
daf8f642
MX
338 //\r
339 // Default to 32bit transfer\r
340 //\r
341 OpSize = 4;\r
342\r
343 do {\r
344 OpCode = *Rip++;\r
345 if (OpCode == 0x66) {\r
346 OpSize = 2;\r
347 } else if ((OpCode == 0x64) || (OpCode == 0x65) || (OpCode == 0x67)) {\r
348 continue;\r
349 } else if ((OpCode >= 0x40) && (OpCode <= 0x4f)) {\r
350 SeenRex = TRUE;\r
351 Rex.Val = OpCode;\r
352 } else {\r
353 break;\r
354 }\r
355 } while (TRUE);\r
356\r
357 //\r
358 // We need to have at least 2 more bytes for this instruction\r
359 //\r
360 TDX_DECODER_BUG_ON (((UINT64)Rip - Regs->Rip) > 13);\r
361\r
362 OpCode = *Rip++;\r
363 //\r
364 // Two-byte opecode, get next byte\r
365 //\r
366 if (OpCode == 0x0F) {\r
367 OpCode = *Rip++;\r
368 }\r
369\r
370 switch (OpCode) {\r
371 case 0x88:\r
372 case 0x8A:\r
373 case 0xB6:\r
374 MmioSize = 1;\r
375 break;\r
376 case 0xB7:\r
377 MmioSize = 2;\r
378 break;\r
379 default:\r
380 MmioSize = Rex.Bits.W ? 8 : OpSize;\r
381 break;\r
382 }\r
383\r
384 /* Punt on AH/BH/CH/DH unless it shows up. */\r
385 ModRm.Val = *Rip++;\r
386 TDX_DECODER_BUG_ON (MmioSize == 1 && ModRm.Bits.Reg > 4 && !SeenRex && OpCode != 0xB6);\r
387 Reg = GetRegFromContext (Regs, ModRm.Bits.Reg | ((int)Rex.Bits.R << 3));\r
388 TDX_DECODER_BUG_ON (!Reg);\r
389\r
390 if (ModRm.Bits.Rm == 4) {\r
391 ++Rip; /* SIB byte */\r
392 }\r
393\r
394 if ((ModRm.Bits.Mod == 2) || ((ModRm.Bits.Mod == 0) && (ModRm.Bits.Rm == 5))) {\r
395 Rip += 4; /* DISP32 */\r
396 } else if (ModRm.Bits.Mod == 1) {\r
397 ++Rip; /* DISP8 */\r
398 }\r
399\r
400 switch (OpCode) {\r
401 case 0x88:\r
402 case 0x89:\r
403 CopyMem ((void *)&Val, Reg, MmioSize);\r
404 Status = TdVmCall (TDVMCALL_MMIO, MmioSize, 1, Veinfo->GuestPA, Val, 0);\r
405 break;\r
406 case 0xC7:\r
407 CopyMem ((void *)&Val, Rip, OpSize);\r
408 Status = TdVmCall (TDVMCALL_MMIO, MmioSize, 1, Veinfo->GuestPA, Val, 0);\r
409 Rip += OpSize;\r
410 default:\r
411 //\r
412 // 32-bit write registers are zero extended to the full register\r
413 // Hence 'MOVZX r[32/64], r/m16' is\r
414 // hardcoded to reg size 8, and the straight MOV case has a reg\r
415 // size of 8 in the 32-bit read case.\r
416 //\r
417 switch (OpCode) {\r
418 case 0xB6:\r
419 RegSize = Rex.Bits.W ? 8 : OpSize;\r
420 break;\r
421 case 0xB7:\r
422 RegSize = 8;\r
423 break;\r
424 default:\r
425 RegSize = MmioSize == 4 ? 8 : MmioSize;\r
426 break;\r
427 }\r
428\r
429 Status = TdVmCall (TDVMCALL_MMIO, MmioSize, 0, Veinfo->GuestPA, 0, &Val);\r
430 if (Status == 0) {\r
431 ZeroMem (Reg, RegSize);\r
432 CopyMem (Reg, (void *)&Val, MmioSize);\r
433 }\r
434 }\r
435\r
436 if (Status == 0) {\r
437 TDX_DECODER_BUG_ON (((UINT64)Rip - Regs->Rip) > 15);\r
438\r
439 //\r
440 // We change instruction length to reflect true size so handler can\r
441 // bump rip\r
442 //\r
443 Veinfo->ExitInstructionLength = (UINT32)((UINT64)Rip - Regs->Rip);\r
444 }\r
445\r
446 return Status;\r
447}\r
448\r
449/**\r
450 Handle a #VE exception.\r
451\r
452 Performs the necessary processing to handle a #VE exception.\r
453\r
454 @param[in, out] ExceptionType Pointer to an EFI_EXCEPTION_TYPE to be set\r
455 as value to use on error.\r
456 @param[in, out] SystemContext Pointer to EFI_SYSTEM_CONTEXT\r
457\r
458 @retval EFI_SUCCESS Exception handled\r
459 @retval EFI_UNSUPPORTED #VE not supported, (new) exception value to\r
460 propagate provided\r
461 @retval EFI_PROTOCOL_ERROR #VE handling failed, (new) exception value to\r
462 propagate provided\r
463\r
464**/\r
465EFI_STATUS\r
466EFIAPI\r
467VmTdExitHandleVe (\r
468 IN OUT EFI_EXCEPTION_TYPE *ExceptionType,\r
469 IN OUT EFI_SYSTEM_CONTEXT SystemContext\r
470 )\r
471{\r
472 UINT64 Status;\r
473 TD_RETURN_DATA ReturnData;\r
474 EFI_SYSTEM_CONTEXT_X64 *Regs;\r
475\r
476 Regs = SystemContext.SystemContextX64;\r
477 Status = TdCall (TDCALL_TDGETVEINFO, 0, 0, 0, &ReturnData);\r
478 ASSERT (Status == 0);\r
479 if (Status != 0) {\r
480 DEBUG ((DEBUG_ERROR, "#VE happened. TDGETVEINFO failed with Status = 0x%llx\n", Status));\r
481 TdVmCall (TDVMCALL_HALT, 0, 0, 0, 0, 0);\r
482 }\r
483\r
484 switch (ReturnData.VeInfo.ExitReason) {\r
485 case EXIT_REASON_CPUID:\r
486 Status = CpuIdExit (Regs, &ReturnData.VeInfo);\r
487 DEBUG ((\r
488 DEBUG_VERBOSE,\r
489 "CPUID #VE happened, ExitReasion is %d, ExitQualification = 0x%x.\n",\r
490 ReturnData.VeInfo.ExitReason,\r
491 ReturnData.VeInfo.ExitQualification.Val\r
492 ));\r
493 break;\r
494\r
495 case EXIT_REASON_HLT:\r
496 Status = TdVmCall (EXIT_REASON_HLT, 0, 0, 0, 0, 0);\r
497 break;\r
498\r
499 case EXIT_REASON_IO_INSTRUCTION:\r
500 Status = IoExit (Regs, &ReturnData.VeInfo);\r
501 DEBUG ((\r
502 DEBUG_VERBOSE,\r
503 "IO_Instruction #VE happened, ExitReasion is %d, ExitQualification = 0x%x.\n",\r
504 ReturnData.VeInfo.ExitReason,\r
505 ReturnData.VeInfo.ExitQualification.Val\r
506 ));\r
507 break;\r
508\r
509 case EXIT_REASON_MSR_READ:\r
510 Status = ReadMsrExit (Regs, &ReturnData.VeInfo);\r
511 DEBUG ((\r
512 DEBUG_VERBOSE,\r
513 "RDMSR #VE happened, ExitReasion is %d, ExitQualification = 0x%x. Regs->Rcx=0x%llx, Status = 0x%llx\n",\r
514 ReturnData.VeInfo.ExitReason,\r
515 ReturnData.VeInfo.ExitQualification.Val,\r
516 Regs->Rcx,\r
517 Status\r
518 ));\r
519 break;\r
520\r
521 case EXIT_REASON_MSR_WRITE:\r
522 Status = WriteMsrExit (Regs, &ReturnData.VeInfo);\r
523 DEBUG ((\r
524 DEBUG_VERBOSE,\r
525 "WRMSR #VE happened, ExitReasion is %d, ExitQualification = 0x%x. Regs->Rcx=0x%llx, Status = 0x%llx\n",\r
526 ReturnData.VeInfo.ExitReason,\r
527 ReturnData.VeInfo.ExitQualification.Val,\r
528 Regs->Rcx,\r
529 Status\r
530 ));\r
531 break;\r
532\r
533 case EXIT_REASON_EPT_VIOLATION:\r
534 Status = MmioExit (Regs, &ReturnData.VeInfo);\r
535 DEBUG ((\r
536 DEBUG_VERBOSE,\r
537 "MMIO #VE happened, ExitReasion is %d, ExitQualification = 0x%x.\n",\r
538 ReturnData.VeInfo.ExitReason,\r
539 ReturnData.VeInfo.ExitQualification.Val\r
540 ));\r
541 break;\r
542\r
543 case EXIT_REASON_VMCALL:\r
544 case EXIT_REASON_MWAIT_INSTRUCTION:\r
545 case EXIT_REASON_MONITOR_INSTRUCTION:\r
546 case EXIT_REASON_WBINVD:\r
547 case EXIT_REASON_RDPMC:\r
548 /* Handle as nops. */\r
549 break;\r
550\r
551 default:\r
552 DEBUG ((\r
553 DEBUG_ERROR,\r
554 "Unsupported #VE happened, ExitReason is %d, ExitQualification = 0x%x.\n",\r
555 ReturnData.VeInfo.ExitReason,\r
556 ReturnData.VeInfo.ExitQualification.Val\r
557 ));\r
558\r
559 ASSERT (FALSE);\r
560 CpuDeadLoop ();\r
561 }\r
562\r
563 if (Status) {\r
564 DEBUG ((\r
565 DEBUG_ERROR,\r
566 "#VE Error (0x%llx) returned from host, ExitReason is %d, ExitQualification = 0x%x.\n",\r
567 Status,\r
568 ReturnData.VeInfo.ExitReason,\r
569 ReturnData.VeInfo.ExitQualification.Val\r
570 ));\r
571\r
572 TdVmCall (TDVMCALL_HALT, 0, 0, 0, 0, 0);\r
573 }\r
574\r
575 SystemContext.SystemContextX64->Rip += ReturnData.VeInfo.ExitInstructionLength;\r
576 return EFI_SUCCESS;\r
577}\r