TianoCore EDK2 master
VmTdExitVeHandler.c
Go to the documentation of this file.
1
9#include <Library/BaseLib.h>
10#include <Library/DebugLib.h>
11#include "VmTdExitHandler.h"
12#include <Library/VmgExitLib.h>
16
17typedef union {
18 struct {
19 UINT32 Eax;
20 UINT32 Edx;
21 } Regs;
22 UINT64 Val;
23} MSR_DATA;
24
25typedef union {
26 UINT8 Val;
27 struct {
28 UINT8 B : 1;
29 UINT8 X : 1;
30 UINT8 R : 1;
31 UINT8 W : 1;
32 } Bits;
33} REX;
34
35typedef union {
36 UINT8 Val;
37 struct {
38 UINT8 Rm : 3;
39 UINT8 Reg : 3;
40 UINT8 Mod : 2;
41 } Bits;
42} MODRM;
43
44typedef struct {
45 UINT64 Regs[4];
47
60UINT64
61EFIAPI
65 )
66{
67 CPUID_DATA CpuIdData;
68 UINT64 Status;
69
70 Status = TdVmCallCpuid (Regs->Rax, Regs->Rcx, &CpuIdData);
71
72 if (Status == 0) {
73 Regs->Rax = CpuIdData.Regs[0];
74 Regs->Rbx = CpuIdData.Regs[1];
75 Regs->Rcx = CpuIdData.Regs[2];
76 Regs->Rdx = CpuIdData.Regs[3];
77 }
78
79 return Status;
80}
81
94UINT64
95EFIAPI
99 )
100{
101 BOOLEAN Write;
102 UINTN Size;
103 UINTN Port;
104 UINT64 Val;
105 UINT64 RepCnt;
106 UINT64 Status;
107
108 Val = 0;
109 Write = Veinfo->ExitQualification.Io.Direction ? FALSE : TRUE;
110 Size = Veinfo->ExitQualification.Io.Size + 1;
111 Port = Veinfo->ExitQualification.Io.Port;
112
113 if (Veinfo->ExitQualification.Io.String) {
114 //
115 // If REP is set, get rep-cnt from Rcx
116 //
117 RepCnt = Veinfo->ExitQualification.Io.Rep ? Regs->Rcx : 1;
118
119 while (RepCnt) {
120 Val = 0;
121 if (Write == TRUE) {
122 CopyMem (&Val, (VOID *)Regs->Rsi, Size);
123 Regs->Rsi += Size;
124 }
125
126 Status = TdVmCall (EXIT_REASON_IO_INSTRUCTION, Size, Write, Port, Val, (Write ? NULL : &Val));
127 if (Status != 0) {
128 break;
129 }
130
131 if (Write == FALSE) {
132 CopyMem ((VOID *)Regs->Rdi, &Val, Size);
133 Regs->Rdi += Size;
134 }
135
136 if (Veinfo->ExitQualification.Io.Rep) {
137 Regs->Rcx -= 1;
138 }
139
140 RepCnt -= 1;
141 }
142 } else {
143 if (Write == TRUE) {
144 CopyMem (&Val, (VOID *)&Regs->Rax, Size);
145 }
146
147 Status = TdVmCall (EXIT_REASON_IO_INSTRUCTION, Size, Write, Port, Val, (Write ? NULL : &Val));
148 if ((Status == 0) && (Write == FALSE)) {
149 CopyMem ((VOID *)&Regs->Rax, &Val, Size);
150 }
151 }
152
153 return Status;
154}
155
167STATIC
168UINT64
172 )
173{
174 MSR_DATA Data;
175 UINT64 Status;
176
177 Status = TdVmCall (EXIT_REASON_MSR_READ, Regs->Rcx, 0, 0, 0, &Data);
178 if (Status == 0) {
179 Regs->Rax = Data.Regs.Eax;
180 Regs->Rdx = Data.Regs.Edx;
181 }
182
183 return Status;
184}
185
197STATIC
198UINT64
202 )
203{
204 UINT64 Status;
205 MSR_DATA Data;
206
207 Data.Regs.Eax = (UINT32)Regs->Rax;
208 Data.Regs.Edx = (UINT32)Regs->Rdx;
209
210 Status = TdVmCall (EXIT_REASON_MSR_WRITE, Regs->Rcx, Data.Val, 0, 0, NULL);
211
212 return Status;
213}
214
215STATIC
216VOID
217EFIAPI
218TdxDecodeInstruction (
219 IN UINT8 *Rip
220 )
221{
222 UINTN i;
223
224 DEBUG ((DEBUG_INFO, "TDX: #TD[EPT] instruction (%p):", Rip));
225 for (i = 0; i < 15; i++) {
226 DEBUG ((DEBUG_INFO, "%02x:", Rip[i]));
227 }
228
229 DEBUG ((DEBUG_INFO, "\n"));
230}
231
232#define TDX_DECODER_BUG_ON(x) \
233 if ((x)) { \
234 TdxDecodeInstruction(Rip); \
235 TdVmCall(TDVMCALL_HALT, 0, 0, 0, 0, 0); \
236 }
237
238STATIC
239UINT64 *
240EFIAPI
241GetRegFromContext (
243 IN UINTN RegIndex
244 )
245{
246 switch (RegIndex) {
247 case 0: return &Regs->Rax;
248 break;
249 case 1: return &Regs->Rcx;
250 break;
251 case 2: return &Regs->Rdx;
252 break;
253 case 3: return &Regs->Rbx;
254 break;
255 case 4: return &Regs->Rsp;
256 break;
257 case 5: return &Regs->Rbp;
258 break;
259 case 6: return &Regs->Rsi;
260 break;
261 case 7: return &Regs->Rdi;
262 break;
263 case 8: return &Regs->R8;
264 break;
265 case 9: return &Regs->R9;
266 break;
267 case 10: return &Regs->R10;
268 break;
269 case 11: return &Regs->R11;
270 break;
271 case 12: return &Regs->R12;
272 break;
273 case 13: return &Regs->R13;
274 break;
275 case 14: return &Regs->R14;
276 break;
277 case 15: return &Regs->R15;
278 break;
279 }
280
281 return NULL;
282}
283
295STATIC
296INTN
297EFIAPI
301 )
302{
303 UINT64 Status;
304 UINT32 MmioSize;
305 UINT32 RegSize;
306 UINT8 OpCode;
307 BOOLEAN SeenRex;
308 UINT64 *Reg;
309 UINT8 *Rip;
310 UINT64 Val;
311 UINT32 OpSize;
312 MODRM ModRm;
313 REX Rex;
314
315 Rip = (UINT8 *)Regs->Rip;
316 Val = 0;
317 Rex.Val = 0;
318 SeenRex = FALSE;
319
320 //
321 // Default to 32bit transfer
322 //
323 OpSize = 4;
324
325 do {
326 OpCode = *Rip++;
327 if (OpCode == 0x66) {
328 OpSize = 2;
329 } else if ((OpCode == 0x64) || (OpCode == 0x65) || (OpCode == 0x67)) {
330 continue;
331 } else if ((OpCode >= 0x40) && (OpCode <= 0x4f)) {
332 SeenRex = TRUE;
333 Rex.Val = OpCode;
334 } else {
335 break;
336 }
337 } while (TRUE);
338
339 //
340 // We need to have at least 2 more bytes for this instruction
341 //
342 TDX_DECODER_BUG_ON (((UINT64)Rip - Regs->Rip) > 13);
343
344 OpCode = *Rip++;
345 //
346 // Two-byte opecode, get next byte
347 //
348 if (OpCode == 0x0F) {
349 OpCode = *Rip++;
350 }
351
352 switch (OpCode) {
353 case 0x88:
354 case 0x8A:
355 case 0xB6:
356 MmioSize = 1;
357 break;
358 case 0xB7:
359 MmioSize = 2;
360 break;
361 default:
362 MmioSize = Rex.Bits.W ? 8 : OpSize;
363 break;
364 }
365
366 /* Punt on AH/BH/CH/DH unless it shows up. */
367 ModRm.Val = *Rip++;
368 TDX_DECODER_BUG_ON (MmioSize == 1 && ModRm.Bits.Reg > 4 && !SeenRex && OpCode != 0xB6);
369 Reg = GetRegFromContext (Regs, ModRm.Bits.Reg | ((int)Rex.Bits.R << 3));
370 TDX_DECODER_BUG_ON (!Reg);
371
372 if (ModRm.Bits.Rm == 4) {
373 ++Rip; /* SIB byte */
374 }
375
376 if ((ModRm.Bits.Mod == 2) || ((ModRm.Bits.Mod == 0) && (ModRm.Bits.Rm == 5))) {
377 Rip += 4; /* DISP32 */
378 } else if (ModRm.Bits.Mod == 1) {
379 ++Rip; /* DISP8 */
380 }
381
382 switch (OpCode) {
383 case 0x88:
384 case 0x89:
385 CopyMem ((void *)&Val, Reg, MmioSize);
386 Status = TdVmCall (TDVMCALL_MMIO, MmioSize, 1, Veinfo->GuestPA, Val, 0);
387 break;
388 case 0xC7:
389 CopyMem ((void *)&Val, Rip, OpSize);
390 Status = TdVmCall (TDVMCALL_MMIO, MmioSize, 1, Veinfo->GuestPA, Val, 0);
391 Rip += OpSize;
392 default:
393 //
394 // 32-bit write registers are zero extended to the full register
395 // Hence 'MOVZX r[32/64], r/m16' is
396 // hardcoded to reg size 8, and the straight MOV case has a reg
397 // size of 8 in the 32-bit read case.
398 //
399 switch (OpCode) {
400 case 0xB6:
401 RegSize = Rex.Bits.W ? 8 : OpSize;
402 break;
403 case 0xB7:
404 RegSize = 8;
405 break;
406 default:
407 RegSize = MmioSize == 4 ? 8 : MmioSize;
408 break;
409 }
410
411 Status = TdVmCall (TDVMCALL_MMIO, MmioSize, 0, Veinfo->GuestPA, 0, &Val);
412 if (Status == 0) {
413 ZeroMem (Reg, RegSize);
414 CopyMem (Reg, (void *)&Val, MmioSize);
415 }
416 }
417
418 if (Status == 0) {
419 TDX_DECODER_BUG_ON (((UINT64)Rip - Regs->Rip) > 15);
420
421 //
422 // We change instruction length to reflect true size so handler can
423 // bump rip
424 //
425 Veinfo->ExitInstructionLength = (UINT32)((UINT64)Rip - Regs->Rip);
426 }
427
428 return Status;
429}
430
448EFIAPI
450 IN OUT EFI_EXCEPTION_TYPE *ExceptionType,
451 IN OUT EFI_SYSTEM_CONTEXT SystemContext
452 )
453{
454 UINT64 Status;
455 TD_RETURN_DATA ReturnData;
457
458 Regs = SystemContext.SystemContextX64;
459 Status = TdCall (TDCALL_TDGETVEINFO, 0, 0, 0, &ReturnData);
460 ASSERT (Status == 0);
461 if (Status != 0) {
462 DEBUG ((DEBUG_ERROR, "#VE happened. TDGETVEINFO failed with Status = 0x%llx\n", Status));
463 TdVmCall (TDVMCALL_HALT, 0, 0, 0, 0, 0);
464 }
465
466 switch (ReturnData.VeInfo.ExitReason) {
467 case EXIT_REASON_CPUID:
468 Status = CpuIdExit (Regs, &ReturnData.VeInfo);
469 DEBUG ((
470 DEBUG_VERBOSE,
471 "CPUID #VE happened, ExitReasion is %d, ExitQualification = 0x%x.\n",
472 ReturnData.VeInfo.ExitReason,
473 ReturnData.VeInfo.ExitQualification.Val
474 ));
475 break;
476
477 case EXIT_REASON_HLT:
478 Status = TdVmCall (EXIT_REASON_HLT, 0, 0, 0, 0, 0);
479 break;
480
481 case EXIT_REASON_IO_INSTRUCTION:
482 Status = IoExit (Regs, &ReturnData.VeInfo);
483 DEBUG ((
484 DEBUG_VERBOSE,
485 "IO_Instruction #VE happened, ExitReasion is %d, ExitQualification = 0x%x.\n",
486 ReturnData.VeInfo.ExitReason,
487 ReturnData.VeInfo.ExitQualification.Val
488 ));
489 break;
490
491 case EXIT_REASON_MSR_READ:
492 Status = ReadMsrExit (Regs, &ReturnData.VeInfo);
493 DEBUG ((
494 DEBUG_VERBOSE,
495 "RDMSR #VE happened, ExitReasion is %d, ExitQualification = 0x%x. Regs->Rcx=0x%llx, Status = 0x%llx\n",
496 ReturnData.VeInfo.ExitReason,
497 ReturnData.VeInfo.ExitQualification.Val,
498 Regs->Rcx,
499 Status
500 ));
501 break;
502
503 case EXIT_REASON_MSR_WRITE:
504 Status = WriteMsrExit (Regs, &ReturnData.VeInfo);
505 DEBUG ((
506 DEBUG_VERBOSE,
507 "WRMSR #VE happened, ExitReasion is %d, ExitQualification = 0x%x. Regs->Rcx=0x%llx, Status = 0x%llx\n",
508 ReturnData.VeInfo.ExitReason,
509 ReturnData.VeInfo.ExitQualification.Val,
510 Regs->Rcx,
511 Status
512 ));
513 break;
514
515 case EXIT_REASON_EPT_VIOLATION:
516 Status = MmioExit (Regs, &ReturnData.VeInfo);
517 DEBUG ((
518 DEBUG_VERBOSE,
519 "MMIO #VE happened, ExitReasion is %d, ExitQualification = 0x%x.\n",
520 ReturnData.VeInfo.ExitReason,
521 ReturnData.VeInfo.ExitQualification.Val
522 ));
523 break;
524
525 case EXIT_REASON_VMCALL:
526 case EXIT_REASON_MWAIT_INSTRUCTION:
527 case EXIT_REASON_MONITOR_INSTRUCTION:
528 case EXIT_REASON_WBINVD:
529 case EXIT_REASON_RDPMC:
530 /* Handle as nops. */
531 break;
532
533 default:
534 DEBUG ((
535 DEBUG_ERROR,
536 "Unsupported #VE happened, ExitReason is %d, ExitQualification = 0x%x.\n",
537 ReturnData.VeInfo.ExitReason,
538 ReturnData.VeInfo.ExitQualification.Val
539 ));
540
541 ASSERT (FALSE);
542 CpuDeadLoop ();
543 }
544
545 if (Status) {
546 DEBUG ((
547 DEBUG_ERROR,
548 "#VE Error (0x%llx) returned from host, ExitReason is %d, ExitQualification = 0x%x.\n",
549 Status,
550 ReturnData.VeInfo.ExitReason,
551 ReturnData.VeInfo.ExitQualification.Val
552 ));
553
554 TdVmCall (TDVMCALL_HALT, 0, 0, 0, 0, 0);
555 }
556
557 SystemContext.SystemContextX64->Rip += ReturnData.VeInfo.ExitInstructionLength;
558 return EFI_SUCCESS;
559}
UINT64 UINTN
INT64 INTN
#define NULL
Definition: Base.h:312
#define STATIC
Definition: Base.h:264
#define TRUE
Definition: Base.h:301
#define FALSE
Definition: Base.h:307
#define IN
Definition: Base.h:279
#define OUT
Definition: Base.h:284
VOID EFIAPI CpuDeadLoop(VOID)
Definition: CpuDeadLoop.c:23
VOID *EFIAPI CopyMem(OUT VOID *DestinationBuffer, IN CONST VOID *SourceBuffer, IN UINTN Length)
VOID *EFIAPI ZeroMem(OUT VOID *Buffer, IN UINTN Length)
#define DEBUG(Expression)
Definition: DebugLib.h:417
#define ASSERT(Expression)
Definition: DebugLib.h:391
INTN EFI_EXCEPTION_TYPE
Definition: DebugSupport.h:35
UINTN EFIAPI TdCall(IN UINT64 Leaf, IN UINT64 Arg1, IN UINT64 Arg2, IN UINT64 Arg3, IN OUT VOID *Results)
Definition: IntelTdxNull.c:31
UINTN EFIAPI TdVmCall(IN UINT64 Leaf, IN UINT64 Arg1, IN UINT64 Arg2, IN UINT64 Arg3, IN UINT64 Arg4, IN OUT VOID *Results)
Definition: IntelTdxNull.c:59
RETURN_STATUS EFI_STATUS
Definition: UefiBaseType.h:28
#define EFI_SUCCESS
Definition: UefiBaseType.h:111
EFI_STATUS EFIAPI TdVmCallCpuid(IN UINT64 Eax, IN UINT64 Ecx, OUT VOID *Results)
STATIC INTN EFIAPI MmioExit(IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs, IN TDCALL_VEINFO_RETURN_DATA *Veinfo)
STATIC UINT64 EFIAPI CpuIdExit(IN EFI_SYSTEM_CONTEXT_X64 *Regs, IN TDCALL_VEINFO_RETURN_DATA *Veinfo)
EFI_STATUS EFIAPI VmTdExitHandleVe(IN OUT EFI_EXCEPTION_TYPE *ExceptionType, IN OUT EFI_SYSTEM_CONTEXT SystemContext)
STATIC UINT64 ReadMsrExit(IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs, IN TDCALL_VEINFO_RETURN_DATA *Veinfo)
STATIC UINT64 EFIAPI IoExit(IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs, IN TDCALL_VEINFO_RETURN_DATA *Veinfo)
STATIC UINT64 WriteMsrExit(IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs, IN TDCALL_VEINFO_RETURN_DATA *Veinfo)