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>
12 #include <Library/CcExitLib.h>
13 #include <Library/BaseMemoryLib.h>
14 #include <IndustryStandard/Tdx.h>
15 #include <IndustryStandard/InstructionParsing.h>
16 #include "CcInstruction.h"
18 #define TDX_MMIO_READ 0
19 #define TDX_MMIO_WRITE 1
53 Handle an CPUID event.
55 Use the TDVMCALL instruction to handle cpuid #ve
57 @param[in, out] Regs x64 processor context
58 @param[in] Veinfo VE Info
60 @retval 0 Event handled successfully
61 @return New exception value to propagate
67 IN EFI_SYSTEM_CONTEXT_X64
*Regs
,
68 IN TDCALL_VEINFO_RETURN_DATA
*Veinfo
74 Status
= TdVmCallCpuid (Regs
->Rax
, Regs
->Rcx
, &CpuIdData
);
77 Regs
->Rax
= CpuIdData
.Regs
[0];
78 Regs
->Rbx
= CpuIdData
.Regs
[1];
79 Regs
->Rcx
= CpuIdData
.Regs
[2];
80 Regs
->Rdx
= CpuIdData
.Regs
[3];
89 Use the TDVMCALL instruction to handle either an IO read or an IO write.
91 @param[in, out] Regs x64 processor context
92 @param[in] Veinfo VE Info
94 @retval 0 Event handled successfully
95 @return New exception value to propagate
101 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
102 IN TDCALL_VEINFO_RETURN_DATA
*Veinfo
113 Write
= Veinfo
->ExitQualification
.Io
.Direction
? FALSE
: TRUE
;
114 Size
= Veinfo
->ExitQualification
.Io
.Size
+ 1;
115 Port
= Veinfo
->ExitQualification
.Io
.Port
;
117 if (Veinfo
->ExitQualification
.Io
.String
) {
119 // If REP is set, get rep-cnt from Rcx
121 RepCnt
= Veinfo
->ExitQualification
.Io
.Rep
? Regs
->Rcx
: 1;
126 CopyMem (&Val
, (VOID
*)Regs
->Rsi
, Size
);
130 Status
= TdVmCall (EXIT_REASON_IO_INSTRUCTION
, Size
, Write
, Port
, Val
, (Write
? NULL
: &Val
));
135 if (Write
== FALSE
) {
136 CopyMem ((VOID
*)Regs
->Rdi
, &Val
, Size
);
140 if (Veinfo
->ExitQualification
.Io
.Rep
) {
148 CopyMem (&Val
, (VOID
*)&Regs
->Rax
, Size
);
151 Status
= TdVmCall (EXIT_REASON_IO_INSTRUCTION
, Size
, Write
, Port
, Val
, (Write
? NULL
: &Val
));
152 if ((Status
== 0) && (Write
== FALSE
)) {
153 CopyMem ((VOID
*)&Regs
->Rax
, &Val
, Size
);
161 Handle an READ MSR event.
163 Use the TDVMCALL instruction to handle msr read
165 @param[in, out] Regs x64 processor context
166 @param[in] Veinfo VE Info
168 @retval 0 Event handled successfully
169 @return New exception value to propagate
174 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
175 IN TDCALL_VEINFO_RETURN_DATA
*Veinfo
181 Status
= TdVmCall (EXIT_REASON_MSR_READ
, Regs
->Rcx
, 0, 0, 0, &Data
);
183 Regs
->Rax
= Data
.Regs
.Eax
;
184 Regs
->Rdx
= Data
.Regs
.Edx
;
191 Handle an WRITE MSR event.
193 Use the TDVMCALL instruction to handle msr write
195 @param[in, out] Regs x64 processor context
196 @param[in] Veinfo VE Info
198 @retval 0 Event handled successfully
199 @return New exception value to propagate
204 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
205 IN TDCALL_VEINFO_RETURN_DATA
*Veinfo
211 Data
.Regs
.Eax
= (UINT32
)Regs
->Rax
;
212 Data
.Regs
.Edx
= (UINT32
)Regs
->Rdx
;
214 Status
= TdVmCall (EXIT_REASON_MSR_WRITE
, Regs
->Rcx
, Data
.Val
, 0, 0, NULL
);
222 TdxDecodeInstruction (
229 DEBUG ((DEBUG_INFO
, "TDX: #TD[EPT] instruction (%p):", Rip
));
230 for (i
= 0; i
< MIN (15, Length
); i
++) {
231 DEBUG ((DEBUG_INFO
, "%02x ", Rip
[i
]));
234 DEBUG ((DEBUG_INFO
, "\n"));
237 #define TDX_DECODER_BUG_ON(x) \
239 TdxDecodeInstruction(Rip); \
240 TdVmCall(TDVMCALL_HALT, 0, 0, 0, 0, 0); \
245 * Tdx MMIO access via TdVmcall.
247 * @param MmioSize Size of the MMIO access
248 * @param ReadOrWrite Read or write operation
249 * @param GuestPA Guest physical address
250 * @param Val Pointer to the value which is read or written
252 * @retval EFI_SUCCESS Successfully access the mmio
253 * @retval Others Other errors as indicated
259 IN UINT32 ReadOrWrite
,
266 if ((MmioSize
!= 1) && (MmioSize
!= 2) && (MmioSize
!= 4) && (MmioSize
!= 8)) {
267 DEBUG ((DEBUG_ERROR
, "%a: Invalid MmioSize - %d\n", __FUNCTION__
, MmioSize
));
268 return EFI_INVALID_PARAMETER
;
272 return EFI_INVALID_PARAMETER
;
276 if (ReadOrWrite
== TDX_MMIO_READ
) {
277 TdStatus
= TdVmCall (TDVMCALL_MMIO
, MmioSize
, TDX_MMIO_READ
, GuestPA
, 0, Val
);
278 } else if (ReadOrWrite
== TDX_MMIO_WRITE
) {
279 TdStatus
= TdVmCall (TDVMCALL_MMIO
, MmioSize
, TDX_MMIO_WRITE
, GuestPA
, *Val
, 0);
281 return EFI_INVALID_PARAMETER
;
285 DEBUG ((DEBUG_ERROR
, "%a: TdVmcall failed with %llx\n", __FUNCTION__
, TdStatus
));
295 EFI_PHYSICAL_ADDRESS Address
;
299 } MMIO_EXIT_PARSED_INSTRUCTION
;
302 * Parse the MMIO instructions.
304 * @param Regs Pointer to the EFI_SYSTEM_CONTEXT_X64 which includes the instructions
305 * @param InstructionData Pointer to the CC_INSTRUCTION_DATA
306 * @param ParsedInstruction Pointer to the parsed instruction data
308 * @retval EFI_SUCCESS Successfully parsed the instructions
309 * @retval Others Other error as indicated
313 ParseMmioExitInstructions (
314 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
315 IN OUT CC_INSTRUCTION_DATA
*InstructionData
,
316 OUT MMIO_EXIT_PARSED_INSTRUCTION
*ParsedInstruction
323 EFI_PHYSICAL_ADDRESS Address
;
331 Status
= EFI_SUCCESS
;
334 Status
= CcInitInstructionData (InstructionData
, NULL
, Regs
);
335 if (EFI_ERROR (Status
)) {
336 DEBUG ((DEBUG_ERROR
, "%a: Initialize InstructionData failed! (%r)\n", __FUNCTION__
, Status
));
340 OpCode
= *(InstructionData
->OpCodes
);
341 if (OpCode
== TWO_BYTE_OPCODE_ESCAPE
) {
342 OpCode
= *(InstructionData
->OpCodes
+ 1);
347 // MMIO write (MOV reg/memX, regX)
355 CcDecodeModRm (Regs
, InstructionData
);
356 Bytes
= ((Bytes
!= 0) ? Bytes
:
357 (InstructionData
->DataSize
== Size16Bits
) ? 2 :
358 (InstructionData
->DataSize
== Size32Bits
) ? 4 :
359 (InstructionData
->DataSize
== Size64Bits
) ? 8 :
362 if (InstructionData
->Ext
.ModRm
.Mod
== 3) {
363 DEBUG ((DEBUG_ERROR
, "%a: Parse Ext.ModRm.Mod error! (OpCode: 0x%x)\n", __FUNCTION__
, OpCode
));
364 return EFI_UNSUPPORTED
;
367 Address
= InstructionData
->Ext
.RmData
;
368 Val
= InstructionData
->Ext
.RegData
;
369 ReadOrWrite
= TDX_MMIO_WRITE
;
374 // MMIO write (MOV moffsetX, aX)
382 Bytes
= ((Bytes
!= 0) ? Bytes
:
383 (InstructionData
->DataSize
== Size16Bits
) ? 2 :
384 (InstructionData
->DataSize
== Size32Bits
) ? 4 :
385 (InstructionData
->DataSize
== Size64Bits
) ? 8 :
388 InstructionData
->ImmediateSize
= (UINTN
)(1 << InstructionData
->AddrSize
);
389 InstructionData
->End
+= InstructionData
->ImmediateSize
;
390 CopyMem (&Address
, InstructionData
->Immediate
, InstructionData
->ImmediateSize
);
393 ReadOrWrite
= TDX_MMIO_WRITE
;
397 // MMIO write (MOV reg/memX, immX)
405 CcDecodeModRm (Regs
, InstructionData
);
406 Bytes
= ((Bytes
!= 0) ? Bytes
:
407 (InstructionData
->DataSize
== Size16Bits
) ? 2 :
408 (InstructionData
->DataSize
== Size32Bits
) ? 4 :
409 (InstructionData
->DataSize
== Size64Bits
) ? 8 :
412 InstructionData
->ImmediateSize
= Bytes
;
413 InstructionData
->End
+= Bytes
;
416 CopyMem (&Val
, InstructionData
->Immediate
, InstructionData
->ImmediateSize
);
418 Address
= InstructionData
->Ext
.RmData
;
419 ReadOrWrite
= TDX_MMIO_WRITE
;
424 // MMIO read (MOV regX, reg/memX)
432 CcDecodeModRm (Regs
, InstructionData
);
433 Bytes
= ((Bytes
!= 0) ? Bytes
:
434 (InstructionData
->DataSize
== Size16Bits
) ? 2 :
435 (InstructionData
->DataSize
== Size32Bits
) ? 4 :
436 (InstructionData
->DataSize
== Size64Bits
) ? 8 :
438 if (InstructionData
->Ext
.ModRm
.Mod
== 3) {
440 // NPF on two register operands???
442 DEBUG ((DEBUG_ERROR
, "%a: Parse Ext.ModRm.Mod error! (OpCode: 0x%x)\n", __FUNCTION__
, OpCode
));
443 return EFI_UNSUPPORTED
;
446 Address
= InstructionData
->Ext
.RmData
;
447 ReadOrWrite
= TDX_MMIO_READ
;
449 Register
= CcGetRegisterPointer (Regs
, InstructionData
->Ext
.ModRm
.Reg
);
450 if (Register
== NULL
) {
456 // Zero-extend for 32-bit operation
464 // MMIO read (MOV aX, moffsetX)
472 Bytes
= ((Bytes
!= 0) ? Bytes
:
473 (InstructionData
->DataSize
== Size16Bits
) ? 2 :
474 (InstructionData
->DataSize
== Size32Bits
) ? 4 :
475 (InstructionData
->DataSize
== Size64Bits
) ? 8 :
478 InstructionData
->ImmediateSize
= (UINTN
)(1 << InstructionData
->AddrSize
);
479 InstructionData
->End
+= InstructionData
->ImmediateSize
;
484 InstructionData
->Immediate
,
485 InstructionData
->ImmediateSize
490 // Zero-extend for 32-bit operation
495 Register
= &Regs
->Rax
;
496 ReadOrWrite
= TDX_MMIO_READ
;
501 // MMIO read w/ zero-extension ((MOVZX regX, reg/memX)
509 CcDecodeModRm (Regs
, InstructionData
);
510 Bytes
= (Bytes
!= 0) ? Bytes
: 2;
511 Address
= InstructionData
->Ext
.RmData
;
513 Register
= CcGetRegisterPointer (Regs
, InstructionData
->Ext
.ModRm
.Reg
);
514 if (Register
== NULL
) {
518 SetMem (Register
, (UINTN
)(1 << InstructionData
->DataSize
), 0);
520 ReadOrWrite
= TDX_MMIO_READ
;
525 // MMIO read w/ sign-extension (MOVSX regX, reg/memX)
533 CcDecodeModRm (Regs
, InstructionData
);
534 Bytes
= (Bytes
!= 0) ? Bytes
: 2;
536 Address
= InstructionData
->Ext
.RmData
;
540 Data
= (UINT8
*)&Val
;
541 SignByte
= ((*Data
& BIT7
) != 0) ? 0xFF : 0x00;
544 Data
= (UINT16
*)&Val
;
545 SignByte
= ((*Data
& BIT15
) != 0) ? 0xFF : 0x00;
548 Register
= CcGetRegisterPointer (Regs
, InstructionData
->Ext
.ModRm
.Reg
);
549 if (Register
== NULL
) {
553 SetMem (Register
, (UINTN
)(1 << InstructionData
->DataSize
), SignByte
);
555 ReadOrWrite
= TDX_MMIO_READ
;
560 DEBUG ((DEBUG_ERROR
, "%a: Invalid MMIO opcode (%x)\n", __FUNCTION__
, OpCode
));
561 Status
= EFI_UNSUPPORTED
;
564 if (!EFI_ERROR (Status
)) {
565 ParsedInstruction
->OpCode
= OpCode
;
566 ParsedInstruction
->Address
= Address
;
567 ParsedInstruction
->Bytes
= Bytes
;
568 ParsedInstruction
->Register
= Register
;
569 ParsedInstruction
->Val
= Val
;
570 ParsedInstruction
->ReadOrWrite
= ReadOrWrite
;
577 Handle an MMIO event.
579 Use the TDVMCALL instruction to handle either an mmio read or an mmio write.
581 @param[in, out] Regs x64 processor context
582 @param[in] Veinfo VE Info
584 @retval 0 Event handled successfully
590 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
591 IN TDCALL_VEINFO_RETURN_DATA
*Veinfo
596 TD_RETURN_DATA TdReturnData
;
599 UINT64 TdSharedPageMask
;
600 CC_INSTRUCTION_DATA InstructionData
;
601 MMIO_EXIT_PARSED_INSTRUCTION ParsedInstruction
;
603 TdStatus
= TdCall (TDCALL_TDINFO
, 0, 0, 0, &TdReturnData
);
604 if (TdStatus
== TDX_EXIT_REASON_SUCCESS
) {
605 Gpaw
= (UINT8
)(TdReturnData
.TdInfo
.Gpaw
& 0x3f);
606 TdSharedPageMask
= 1ULL << (Gpaw
- 1);
608 DEBUG ((DEBUG_ERROR
, "%a: TDCALL failed with status=%llx\n", __FUNCTION__
, TdStatus
));
612 if ((Veinfo
->GuestPA
& TdSharedPageMask
) == 0) {
613 DEBUG ((DEBUG_ERROR
, "%a: EPT-violation #VE on private memory is not allowed!", __FUNCTION__
));
617 Status
= ParseMmioExitInstructions (Regs
, &InstructionData
, &ParsedInstruction
);
618 if (EFI_ERROR (Status
)) {
622 if (Veinfo
->GuestPA
!= (ParsedInstruction
.Address
| TdSharedPageMask
)) {
625 "%a: Address is not correct! (%d: 0x%llx != 0x%llx)\n",
627 ParsedInstruction
.OpCode
,
629 ParsedInstruction
.Address
634 if (ParsedInstruction
.ReadOrWrite
== TDX_MMIO_WRITE
) {
635 Status
= TdxMmioReadWrite (ParsedInstruction
.Bytes
, TDX_MMIO_WRITE
, Veinfo
->GuestPA
, &ParsedInstruction
.Val
);
636 } else if (ParsedInstruction
.ReadOrWrite
== TDX_MMIO_READ
) {
638 Status
= TdxMmioReadWrite (ParsedInstruction
.Bytes
, TDX_MMIO_READ
, Veinfo
->GuestPA
, &Val
);
639 if (!EFI_ERROR (Status
)) {
640 CopyMem (ParsedInstruction
.Register
, &Val
, ParsedInstruction
.Bytes
);
646 if (EFI_ERROR (Status
)) {
651 // We change instruction length to reflect true size so handler can
654 Veinfo
->ExitInstructionLength
= (UINT32
)(CcInstructionLength (&InstructionData
));
655 TdxDecodeInstruction ((UINT8
*)Regs
->Rip
, Veinfo
->ExitInstructionLength
);
660 TdVmCall (TDVMCALL_HALT
, 0, 0, 0, 0, 0);
666 Handle a #VE exception.
668 Performs the necessary processing to handle a #VE exception.
670 @param[in, out] ExceptionType Pointer to an EFI_EXCEPTION_TYPE to be set
671 as value to use on error.
672 @param[in, out] SystemContext Pointer to EFI_SYSTEM_CONTEXT
674 @retval EFI_SUCCESS Exception handled
675 @retval EFI_UNSUPPORTED #VE not supported, (new) exception value to
677 @retval EFI_PROTOCOL_ERROR #VE handling failed, (new) exception value to
684 IN OUT EFI_EXCEPTION_TYPE
*ExceptionType
,
685 IN OUT EFI_SYSTEM_CONTEXT SystemContext
689 TD_RETURN_DATA ReturnData
;
690 EFI_SYSTEM_CONTEXT_X64
*Regs
;
692 Regs
= SystemContext
.SystemContextX64
;
693 Status
= TdCall (TDCALL_TDGETVEINFO
, 0, 0, 0, &ReturnData
);
694 ASSERT (Status
== 0);
696 DEBUG ((DEBUG_ERROR
, "#VE happened. TDGETVEINFO failed with Status = 0x%llx\n", Status
));
697 TdVmCall (TDVMCALL_HALT
, 0, 0, 0, 0, 0);
701 switch (ReturnData
.VeInfo
.ExitReason
) {
702 case EXIT_REASON_CPUID
:
703 Status
= CpuIdExit (Regs
, &ReturnData
.VeInfo
);
706 "CPUID #VE happened, ExitReasion is %d, ExitQualification = 0x%x.\n",
707 ReturnData
.VeInfo
.ExitReason
,
708 ReturnData
.VeInfo
.ExitQualification
.Val
712 case EXIT_REASON_HLT
:
713 Status
= TdVmCall (EXIT_REASON_HLT
, 0, 0, 0, 0, 0);
716 case EXIT_REASON_IO_INSTRUCTION
:
717 Status
= IoExit (Regs
, &ReturnData
.VeInfo
);
720 "IO_Instruction #VE happened, ExitReasion is %d, ExitQualification = 0x%x.\n",
721 ReturnData
.VeInfo
.ExitReason
,
722 ReturnData
.VeInfo
.ExitQualification
.Val
726 case EXIT_REASON_MSR_READ
:
727 Status
= ReadMsrExit (Regs
, &ReturnData
.VeInfo
);
730 "RDMSR #VE happened, ExitReasion is %d, ExitQualification = 0x%x. Regs->Rcx=0x%llx, Status = 0x%llx\n",
731 ReturnData
.VeInfo
.ExitReason
,
732 ReturnData
.VeInfo
.ExitQualification
.Val
,
738 case EXIT_REASON_MSR_WRITE
:
739 Status
= WriteMsrExit (Regs
, &ReturnData
.VeInfo
);
742 "WRMSR #VE happened, ExitReasion is %d, ExitQualification = 0x%x. Regs->Rcx=0x%llx, Status = 0x%llx\n",
743 ReturnData
.VeInfo
.ExitReason
,
744 ReturnData
.VeInfo
.ExitQualification
.Val
,
750 case EXIT_REASON_EPT_VIOLATION
:
751 Status
= MmioExit (Regs
, &ReturnData
.VeInfo
);
754 "MMIO #VE happened, ExitReasion is %d, ExitQualification = 0x%x.\n",
755 ReturnData
.VeInfo
.ExitReason
,
756 ReturnData
.VeInfo
.ExitQualification
.Val
760 case EXIT_REASON_VMCALL
:
761 case EXIT_REASON_MWAIT_INSTRUCTION
:
762 case EXIT_REASON_MONITOR_INSTRUCTION
:
763 case EXIT_REASON_WBINVD
:
764 case EXIT_REASON_RDPMC
:
765 case EXIT_REASON_INVD
:
766 /* Handle as nops. */
772 "Unsupported #VE happened, ExitReason is %d, ExitQualification = 0x%x.\n",
773 ReturnData
.VeInfo
.ExitReason
,
774 ReturnData
.VeInfo
.ExitQualification
.Val
784 "#VE Error (0x%llx) returned from host, ExitReason is %d, ExitQualification = 0x%x.\n",
786 ReturnData
.VeInfo
.ExitReason
,
787 ReturnData
.VeInfo
.ExitQualification
.Val
790 TdVmCall (TDVMCALL_HALT
, 0, 0, 0, 0, 0);
794 SystemContext
.SystemContextX64
->Rip
+= ReturnData
.VeInfo
.ExitInstructionLength
;