3 Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
9 #include <Library/BaseLib.h>
10 #include <Library/DebugLib.h>
11 #include "VmTdExitHandler.h"
12 #include <Library/VmgExitLib.h>
13 #include <Library/BaseMemoryLib.h>
14 #include <IndustryStandard/Tdx.h>
15 #include <IndustryStandard/InstructionParsing.h>
49 Handle an CPUID event.
51 Use the TDVMCALL instruction to handle cpuid #ve
53 @param[in, out] Regs x64 processor context
54 @param[in] Veinfo VE Info
56 @retval 0 Event handled successfully
57 @return New exception value to propagate
63 IN EFI_SYSTEM_CONTEXT_X64
*Regs
,
64 IN TDCALL_VEINFO_RETURN_DATA
*Veinfo
70 Status
= TdVmCallCpuid (Regs
->Rax
, Regs
->Rcx
, &CpuIdData
);
73 Regs
->Rax
= CpuIdData
.Regs
[0];
74 Regs
->Rbx
= CpuIdData
.Regs
[1];
75 Regs
->Rcx
= CpuIdData
.Regs
[2];
76 Regs
->Rdx
= CpuIdData
.Regs
[3];
85 Use the TDVMCALL instruction to handle either an IO read or an IO write.
87 @param[in, out] Regs x64 processor context
88 @param[in] Veinfo VE Info
90 @retval 0 Event handled successfully
91 @return New exception value to propagate
97 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
98 IN TDCALL_VEINFO_RETURN_DATA
*Veinfo
109 Write
= Veinfo
->ExitQualification
.Io
.Direction
? FALSE
: TRUE
;
110 Size
= Veinfo
->ExitQualification
.Io
.Size
+ 1;
111 Port
= Veinfo
->ExitQualification
.Io
.Port
;
113 if (Veinfo
->ExitQualification
.Io
.String
) {
115 // If REP is set, get rep-cnt from Rcx
117 RepCnt
= Veinfo
->ExitQualification
.Io
.Rep
? Regs
->Rcx
: 1;
122 CopyMem (&Val
, (VOID
*)Regs
->Rsi
, Size
);
126 Status
= TdVmCall (EXIT_REASON_IO_INSTRUCTION
, Size
, Write
, Port
, Val
, (Write
? NULL
: &Val
));
131 if (Write
== FALSE
) {
132 CopyMem ((VOID
*)Regs
->Rdi
, &Val
, Size
);
136 if (Veinfo
->ExitQualification
.Io
.Rep
) {
144 CopyMem (&Val
, (VOID
*)&Regs
->Rax
, Size
);
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
);
157 Handle an READ MSR event.
159 Use the TDVMCALL instruction to handle msr read
161 @param[in, out] Regs x64 processor context
162 @param[in] Veinfo VE Info
164 @retval 0 Event handled successfully
165 @return New exception value to propagate
170 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
171 IN TDCALL_VEINFO_RETURN_DATA
*Veinfo
177 Status
= TdVmCall (EXIT_REASON_MSR_READ
, Regs
->Rcx
, 0, 0, 0, &Data
);
179 Regs
->Rax
= Data
.Regs
.Eax
;
180 Regs
->Rdx
= Data
.Regs
.Edx
;
187 Handle an WRITE MSR event.
189 Use the TDVMCALL instruction to handle msr write
191 @param[in, out] Regs x64 processor context
192 @param[in] Veinfo VE Info
194 @retval 0 Event handled successfully
195 @return New exception value to propagate
200 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
201 IN TDCALL_VEINFO_RETURN_DATA
*Veinfo
207 Data
.Regs
.Eax
= (UINT32
)Regs
->Rax
;
208 Data
.Regs
.Edx
= (UINT32
)Regs
->Rdx
;
210 Status
= TdVmCall (EXIT_REASON_MSR_WRITE
, Regs
->Rcx
, Data
.Val
, 0, 0, NULL
);
218 TdxDecodeInstruction (
224 DEBUG ((DEBUG_INFO
, "TDX: #TD[EPT] instruction (%p):", Rip
));
225 for (i
= 0; i
< 15; i
++) {
226 DEBUG ((DEBUG_INFO
, "%02x:", Rip
[i
]));
229 DEBUG ((DEBUG_INFO
, "\n"));
232 #define TDX_DECODER_BUG_ON(x) \
234 TdxDecodeInstruction(Rip); \
235 TdVmCall(TDVMCALL_HALT, 0, 0, 0, 0, 0); \
242 IN EFI_SYSTEM_CONTEXT_X64
*Regs
,
247 case 0: return &Regs
->Rax
;
249 case 1: return &Regs
->Rcx
;
251 case 2: return &Regs
->Rdx
;
253 case 3: return &Regs
->Rbx
;
255 case 4: return &Regs
->Rsp
;
257 case 5: return &Regs
->Rbp
;
259 case 6: return &Regs
->Rsi
;
261 case 7: return &Regs
->Rdi
;
263 case 8: return &Regs
->R8
;
265 case 9: return &Regs
->R9
;
267 case 10: return &Regs
->R10
;
269 case 11: return &Regs
->R11
;
271 case 12: return &Regs
->R12
;
273 case 13: return &Regs
->R13
;
275 case 14: return &Regs
->R14
;
277 case 15: return &Regs
->R15
;
285 Handle an MMIO event.
287 Use the TDVMCALL instruction to handle either an mmio read or an mmio write.
289 @param[in, out] Regs x64 processor context
290 @param[in] Veinfo VE Info
292 @retval 0 Event handled successfully
293 @return New exception value to propagate
299 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
300 IN TDCALL_VEINFO_RETURN_DATA
*Veinfo
314 TD_RETURN_DATA TdReturnData
;
316 UINT64 TdSharedPageMask
;
318 Rip
= (UINT8
*)Regs
->Rip
;
323 Status
= TdCall (TDCALL_TDINFO
, 0, 0, 0, &TdReturnData
);
324 if (Status
== TDX_EXIT_REASON_SUCCESS
) {
325 Gpaw
= (UINT8
)(TdReturnData
.TdInfo
.Gpaw
& 0x3f);
326 TdSharedPageMask
= 1ULL << (Gpaw
- 1);
328 DEBUG ((DEBUG_ERROR
, "TDCALL failed with status=%llx\n", Status
));
332 if ((Veinfo
->GuestPA
& TdSharedPageMask
) == 0) {
333 DEBUG ((DEBUG_ERROR
, "EPT-violation #VE on private memory is not allowed!"));
334 TdVmCall (TDVMCALL_HALT
, 0, 0, 0, 0, 0);
339 // Default to 32bit transfer
345 if (OpCode
== 0x66) {
347 } else if ((OpCode
== 0x64) || (OpCode
== 0x65) || (OpCode
== 0x67)) {
349 } else if ((OpCode
>= 0x40) && (OpCode
<= 0x4f)) {
358 // We need to have at least 2 more bytes for this instruction
360 TDX_DECODER_BUG_ON (((UINT64
)Rip
- Regs
->Rip
) > 13);
364 // Two-byte opecode, get next byte
366 if (OpCode
== 0x0F) {
380 MmioSize
= Rex
.Bits
.W
? 8 : OpSize
;
384 /* Punt on AH/BH/CH/DH unless it shows up. */
386 TDX_DECODER_BUG_ON (MmioSize
== 1 && ModRm
.Bits
.Reg
> 4 && !SeenRex
&& OpCode
!= 0xB6);
387 Reg
= GetRegFromContext (Regs
, ModRm
.Bits
.Reg
| ((int)Rex
.Bits
.R
<< 3));
388 TDX_DECODER_BUG_ON (!Reg
);
390 if (ModRm
.Bits
.Rm
== 4) {
391 ++Rip
; /* SIB byte */
394 if ((ModRm
.Bits
.Mod
== 2) || ((ModRm
.Bits
.Mod
== 0) && (ModRm
.Bits
.Rm
== 5))) {
395 Rip
+= 4; /* DISP32 */
396 } else if (ModRm
.Bits
.Mod
== 1) {
403 CopyMem ((void *)&Val
, Reg
, MmioSize
);
404 Status
= TdVmCall (TDVMCALL_MMIO
, MmioSize
, 1, Veinfo
->GuestPA
, Val
, 0);
407 CopyMem ((void *)&Val
, Rip
, OpSize
);
408 Status
= TdVmCall (TDVMCALL_MMIO
, MmioSize
, 1, Veinfo
->GuestPA
, Val
, 0);
412 // 32-bit write registers are zero extended to the full register
413 // Hence 'MOVZX r[32/64], r/m16' is
414 // hardcoded to reg size 8, and the straight MOV case has a reg
415 // size of 8 in the 32-bit read case.
419 RegSize
= Rex
.Bits
.W
? 8 : OpSize
;
425 RegSize
= MmioSize
== 4 ? 8 : MmioSize
;
429 Status
= TdVmCall (TDVMCALL_MMIO
, MmioSize
, 0, Veinfo
->GuestPA
, 0, &Val
);
431 ZeroMem (Reg
, RegSize
);
432 CopyMem (Reg
, (void *)&Val
, MmioSize
);
437 TDX_DECODER_BUG_ON (((UINT64
)Rip
- Regs
->Rip
) > 15);
440 // We change instruction length to reflect true size so handler can
443 Veinfo
->ExitInstructionLength
= (UINT32
)((UINT64
)Rip
- Regs
->Rip
);
450 Handle a #VE exception.
452 Performs the necessary processing to handle a #VE exception.
454 @param[in, out] ExceptionType Pointer to an EFI_EXCEPTION_TYPE to be set
455 as value to use on error.
456 @param[in, out] SystemContext Pointer to EFI_SYSTEM_CONTEXT
458 @retval EFI_SUCCESS Exception handled
459 @retval EFI_UNSUPPORTED #VE not supported, (new) exception value to
461 @retval EFI_PROTOCOL_ERROR #VE handling failed, (new) exception value to
468 IN OUT EFI_EXCEPTION_TYPE
*ExceptionType
,
469 IN OUT EFI_SYSTEM_CONTEXT SystemContext
473 TD_RETURN_DATA ReturnData
;
474 EFI_SYSTEM_CONTEXT_X64
*Regs
;
476 Regs
= SystemContext
.SystemContextX64
;
477 Status
= TdCall (TDCALL_TDGETVEINFO
, 0, 0, 0, &ReturnData
);
478 ASSERT (Status
== 0);
480 DEBUG ((DEBUG_ERROR
, "#VE happened. TDGETVEINFO failed with Status = 0x%llx\n", Status
));
481 TdVmCall (TDVMCALL_HALT
, 0, 0, 0, 0, 0);
484 switch (ReturnData
.VeInfo
.ExitReason
) {
485 case EXIT_REASON_CPUID
:
486 Status
= CpuIdExit (Regs
, &ReturnData
.VeInfo
);
489 "CPUID #VE happened, ExitReasion is %d, ExitQualification = 0x%x.\n",
490 ReturnData
.VeInfo
.ExitReason
,
491 ReturnData
.VeInfo
.ExitQualification
.Val
495 case EXIT_REASON_HLT
:
496 Status
= TdVmCall (EXIT_REASON_HLT
, 0, 0, 0, 0, 0);
499 case EXIT_REASON_IO_INSTRUCTION
:
500 Status
= IoExit (Regs
, &ReturnData
.VeInfo
);
503 "IO_Instruction #VE happened, ExitReasion is %d, ExitQualification = 0x%x.\n",
504 ReturnData
.VeInfo
.ExitReason
,
505 ReturnData
.VeInfo
.ExitQualification
.Val
509 case EXIT_REASON_MSR_READ
:
510 Status
= ReadMsrExit (Regs
, &ReturnData
.VeInfo
);
513 "RDMSR #VE happened, ExitReasion is %d, ExitQualification = 0x%x. Regs->Rcx=0x%llx, Status = 0x%llx\n",
514 ReturnData
.VeInfo
.ExitReason
,
515 ReturnData
.VeInfo
.ExitQualification
.Val
,
521 case EXIT_REASON_MSR_WRITE
:
522 Status
= WriteMsrExit (Regs
, &ReturnData
.VeInfo
);
525 "WRMSR #VE happened, ExitReasion is %d, ExitQualification = 0x%x. Regs->Rcx=0x%llx, Status = 0x%llx\n",
526 ReturnData
.VeInfo
.ExitReason
,
527 ReturnData
.VeInfo
.ExitQualification
.Val
,
533 case EXIT_REASON_EPT_VIOLATION
:
534 Status
= MmioExit (Regs
, &ReturnData
.VeInfo
);
537 "MMIO #VE happened, ExitReasion is %d, ExitQualification = 0x%x.\n",
538 ReturnData
.VeInfo
.ExitReason
,
539 ReturnData
.VeInfo
.ExitQualification
.Val
543 case EXIT_REASON_VMCALL
:
544 case EXIT_REASON_MWAIT_INSTRUCTION
:
545 case EXIT_REASON_MONITOR_INSTRUCTION
:
546 case EXIT_REASON_WBINVD
:
547 case EXIT_REASON_RDPMC
:
548 /* Handle as nops. */
554 "Unsupported #VE happened, ExitReason is %d, ExitQualification = 0x%x.\n",
555 ReturnData
.VeInfo
.ExitReason
,
556 ReturnData
.VeInfo
.ExitQualification
.Val
566 "#VE Error (0x%llx) returned from host, ExitReason is %d, ExitQualification = 0x%x.\n",
568 ReturnData
.VeInfo
.ExitReason
,
569 ReturnData
.VeInfo
.ExitQualification
.Val
572 TdVmCall (TDVMCALL_HALT
, 0, 0, 0, 0, 0);
575 SystemContext
.SystemContextX64
->Rip
+= ReturnData
.VeInfo
.ExitInstructionLength
;