@@ -112,6 +112,7 @@ const char* exception_mnemonic(int vector)
case 17: return "#AC";
case 18: return "#MC";
case 19: return "#XM";
+ case 20: return "#VE";
default: return "#??";
}
}
@@ -227,6 +228,7 @@ EX(mf, 16);
EX_E(ac, 17);
EX(mc, 18);
EX(xm, 19);
+EX(ve, 20);
EX_E(cp, 21);
asm (".pushsection .text \n\t"
@@ -273,6 +275,7 @@ static void *idt_handlers[32] = {
[17] = &ac_fault,
[18] = &mc_fault,
[19] = &xm_fault,
+ [20] = &ve_fault,
[21] = &cp_fault,
};
@@ -28,6 +28,7 @@
#define GP_VECTOR 13
#define PF_VECTOR 14
#define AC_VECTOR 17
+#define VE_VECTOR 20
#define CP_VECTOR 21
#define X86_CR0_PE 0x00000001
@@ -267,10 +267,97 @@ static bool tdx_handle_io(struct ex_regs *regs, u32 exit_qual)
return ret ? false : true;
}
+static bool tdx_get_ve_info(struct ve_info *ve)
+{
+ struct tdx_module_output out;
+ u64 ret;
+
+ if (!ve)
+ return false;
+
+ /*
+ * NMIs and machine checks are suppressed. Before this point any
+ * #VE is fatal. After this point (TDGETVEINFO call), NMIs and
+ * additional #VEs are permitted (but it is expected not to
+ * happen unless kernel panics).
+ */
+ ret = __tdx_module_call(TDX_GET_VEINFO, 0, 0, 0, 0, &out);
+ if (ret)
+ return false;
+
+ ve->exit_reason = out.rcx;
+ ve->exit_qual = out.rdx;
+ ve->gla = out.r8;
+ ve->gpa = out.r9;
+ ve->instr_len = out.r10 & UINT_MAX;
+ ve->instr_info = out.r10 >> 32;
+
+ return true;
+}
+
+static bool tdx_handle_virtualization_exception(struct ex_regs *regs,
+ struct ve_info *ve)
+{
+ bool ret = true;
+ u64 val = ~0ULL;
+ bool do_sti;
+
+ switch (ve->exit_reason) {
+ case EXIT_REASON_HLT:
+ do_sti = !!(regs->rflags & X86_EFLAGS_IF);
+ /* Bypass failed hlt is better than hang */
+ if (!_tdx_halt(!do_sti, do_sti))
+ tdx_printf("HLT instruction emulation failed\n");
+ break;
+ case EXIT_REASON_MSR_READ:
+ ret = tdx_read_msr(regs->rcx, &val);
+ if (ret) {
+ regs->rax = (u32)val;
+ regs->rdx = val >> 32;
+ }
+ break;
+ case EXIT_REASON_MSR_WRITE:
+ ret = tdx_write_msr(regs->rcx, regs->rax, regs->rdx);
+ break;
+ case EXIT_REASON_CPUID:
+ ret = tdx_handle_cpuid(regs);
+ break;
+ case EXIT_REASON_IO_INSTRUCTION:
+ ret = tdx_handle_io(regs, ve->exit_qual);
+ break;
+ default:
+ tdx_printf("Unexpected #VE: %ld\n", ve->exit_reason);
+ return false;
+ }
+
+ /* After successful #VE handling, move the IP */
+ if (ret)
+ regs->rip += ve->instr_len;
+
+ return ret;
+}
+
+/* #VE exception handler. */
+static void tdx_handle_ve(struct ex_regs *regs)
+{
+ struct ve_info ve;
+
+ if (!tdx_get_ve_info(&ve)) {
+ tdx_printf("tdx_get_ve_info failed\n");
+ return;
+ }
+
+ tdx_handle_virtualization_exception(regs, &ve);
+}
+
efi_status_t setup_tdx(void)
{
if (!is_tdx_guest())
return EFI_UNSUPPORTED;
+ handle_exception(20, tdx_handle_ve);
+
+ printf("Initialized TDX.\n");
+
return EFI_SUCCESS;
}
@@ -29,6 +29,9 @@
#define EXIT_REASON_MSR_READ 31
#define EXIT_REASON_MSR_WRITE 32
+/* TDX Module call Leaf IDs */
+#define TDX_GET_VEINFO 3
+
/*
* Used in __tdx_module_call() helper function to gather the
* output registers' values of TDCALL instruction when requesting
@@ -59,6 +62,20 @@ struct tdx_hypercall_output {
u64 r15;
};
+/*
+ * Used by #VE exception handler to gather the #VE exception
+ * info from the TDX module. This is software only structure
+ * and not related to TDX module/VMM.
+ */
+struct ve_info {
+ u64 exit_reason;
+ u64 exit_qual;
+ u64 gla; /* Guest Linear (virtual) Address */
+ u64 gpa; /* Guest Physical (virtual) Address */
+ u32 instr_len;
+ u32 instr_info;
+};
+
bool is_tdx_guest(void);
efi_status_t setup_tdx(void);