From: Zhenzhong Duan <zhenzhong.duan@xxxxxxxxx> Sub-test1: Test APIC self IPI with vector < 16 trigger #VE. Sub-test2: Test single step on simulation instructions work well with single step emulation in #VE handler, we choose cpuid(0xb, Extended Topology Enumeration Leaf) and wrmsr(0x828, IA32_X2APIC_ESR) to test. Please note not all cpuid trigger #VE, e.g., cpuid(0) will not. And not all MSRs could be used for single step test, e.g., the MSR_IA32_MISC_ENABLE (0x1a0) is reading native (if PERFMON) and #VE in writing. If ~PERFMON, the guest read value will be changed by TDX module: the bit 7 = 0, bit 12 = 1. So the write value usually is not equal to the kvm stored value, and single step test breaks. Sub-test3: Support checks for the fixed value specified in TDX Module 1.5 ABI Spec, Table 2.2 CPUID Virtualization. Signed-off-by: Zhenzhong Duan <zhenzhong.duan@xxxxxxxxx> Reviewed-by: Yu Zhang <yu.c.zhang@xxxxxxxxx> Link: https://lore.kernel.org/r/20220303071907.650203-15-zhenzhong.duan@xxxxxxxxx Co-developed-by: Dan Wu <dan1.wu@xxxxxxxxx> Signed-off-by: Dan Wu <dan1.wu@xxxxxxxxx> Signed-off-by: Qian Wen <qian.wen@xxxxxxxxx> --- x86/Makefile.x86_64 | 1 + x86/intel_tdx.c | 326 ++++++++++++++++++++++++++++++++++++++++++++ x86/unittests.cfg | 4 + 3 files changed, 331 insertions(+) create mode 100644 x86/intel_tdx.c diff --git a/x86/Makefile.x86_64 b/x86/Makefile.x86_64 index 2771a6fa..55c44719 100644 --- a/x86/Makefile.x86_64 +++ b/x86/Makefile.x86_64 @@ -41,6 +41,7 @@ tests += $(TEST_DIR)/pmu_pebs.$(exe) ifeq ($(CONFIG_EFI),y) tests += $(TEST_DIR)/amd_sev.$(exe) +tests += $(TEST_DIR)/intel_tdx.$(exe) endif # The following test cases are disabled when building EFI tests because they diff --git a/x86/intel_tdx.c b/x86/intel_tdx.c new file mode 100644 index 00000000..292dc87b --- /dev/null +++ b/x86/intel_tdx.c @@ -0,0 +1,326 @@ +#include "libcflat.h" +#include "x86/processor.h" +#include "x86/apic-defs.h" +#include "x86/tdx.h" +#include "msr.h" + +static volatile unsigned long db_addr[10], dr6[10]; +static volatile unsigned int n; + +static void test_selfipi_msr(void) +{ + unsigned char vector; + u64 i; + + printf("\nStart APIC_SELF_IPI MSR write test.\n"); + + for (i = 0; i < 16; i++) { + vector = wrmsr_safe(APIC_SELF_IPI, i); + report(vector == VE_VECTOR, + "Expected #VE on WRSMR(%s, 0x%lx), got vector %d", + "APIC_SELF_IPI", i, vector); + } + + printf("End APIC_SELF_IPI MSR write test.\n"); +} + +static void handle_db(struct ex_regs *regs) +{ + db_addr[n] = regs->rip; + dr6[n] = read_dr6(); + + if (dr6[n] & 0x1) + regs->rflags |= (1 << 16); + + if (++n >= 10) { + regs->rflags &= ~(1 << 8); + write_dr7(0x00000400); + } +} + +static void test_single_step(void) +{ + unsigned long start; + + printf("\nStart single step test.\n"); + handle_exception(DB_VECTOR, handle_db); + + /* + * cpuid(0xb) and wrmsr(0x828) trigger #VE and are then emulated. + * Test #DB on these instructions as there is single step + * simulation in #VE handler. This is complement to x86/debug.c + * which test cpuid(0) and in(0x3fd) instruction. In fact, + * cpuid(0) is emulated by seam module. + */ + n = 0; + write_dr6(0); + asm volatile( + "pushf\n\t" + "pop %%rax\n\t" + "or $(1<<8),%%rax\n\t" + "push %%rax\n\t" + "lea (%%rip),%0\n\t" + "popf\n\t" + "and $~(1<<8),%%rax\n\t" + "push %%rax\n\t" + "mov $0xb,%%rax\n\t" + "cpuid\n\t" + "movl $0x828,%%ecx\n\t" + "rdmsr\n\t" + "wrmsr\n\t" + "popf\n\t" + : "=r" (start) : : "rax", "ebx", "ecx", "edx"); + report(n == 8 && + db_addr[0] == start + 1 + 6 && dr6[0] == 0xffff4ff0 && + db_addr[1] == start + 1 + 6 + 1 && dr6[1] == 0xffff4ff0 && + db_addr[2] == start + 1 + 6 + 1 + 7 && dr6[2] == 0xffff4ff0 && + db_addr[3] == start + 1 + 6 + 1 + 7 + 2 && dr6[3] == 0xffff4ff0 && + db_addr[4] == start + 1 + 6 + 1 + 7 + 2 + 5 && dr6[4] == 0xffff4ff0 && + db_addr[5] == start + 1 + 6 + 1 + 7 + 2 + 5 + 2 && dr6[5] == 0xffff4ff0 && + db_addr[6] == start + 1 + 6 + 1 + 7 + 2 + 5 + 2 + 2 && dr6[6] == 0xffff4ff0 && + db_addr[7] == start + 1 + 6 + 1 + 7 + 2 + 5 + 2 + 2 + 1 && dr6[6] == 0xffff4ff0, + "single step emulated instructions"); + printf("End single step test.\n"); +} + +#define CPUID_FIXED0 (0x0) +#define CPUID_FIXED1 (0xffffffff) + +#define CPUID_0_EAX_FIXED (0x23) +#define CPUID_0_EAX_MASK (0xffffffff) + +#define CPUID_1_EAX_MASK (0x3 << 14 | 0xf << 28) +#define CPUID_1_EBX_FIXED (0x8 << 8) +#define CPUID_1_EBX_MASK (0xff | 0xff << 8) +#define CPUID_1_ECX_FIXED (7 | 1 << 4 | 1 << 9 | 1 << 13 | 1 << 15 | 1 << 17 | 0xf << 19 | 1 << 23 | 3 << 25 | 3 << 30) +#define CPUID_1_ECX_MASK (7 | 7 << 4 | 1 << 9 | 1 << 13 | 7 << 15 | 0xf << 19 | 1 << 23 | 3 << 25 | 3 << 30) +#define CPUID_1_EDX_FIXED (0x3ff | 0x3f << 11 | 5 << 19 | 0xf << 23) +#define CPUID_1_EDX_MASK (0xffff | 3 << 16 | 7 << 19 | 0xf << 23 |1 << 30) + +#define CPUID_3_EAX_MASK CPUID_FIXED1 +#define CPUID_3_EBX_MASK CPUID_FIXED1 +#define CPUID_3_ECX_MASK CPUID_FIXED1 +#define CPUID_3_EDX_MASK CPUID_FIXED1 + + +#define CPUID_4_0_EAX_MASK (0xf << 10) +#define CPUID_4_0_EDX_MASK (1 << 2) +#define CPUID_4_1_EAX_MASK CPUID_4_0_EAX_MASK +#define CPUID_4_1_EDX_MASK CPUID_4_0_EDX_MASK +#define CPUID_4_2_EAX_MASK CPUID_4_0_EAX_MASK +#define CPUID_4_2_EDX_MASK CPUID_4_0_EDX_MASK +#define CPUID_4_3_EAX_MASK CPUID_4_0_EAX_MASK +#define CPUID_4_3_EDX_MASK (0xfffffff8) +#define CPUID_4_4_EAX_MASK CPUID_FIXED1 +#define CPUID_4_4_EBX_MASK CPUID_FIXED1 +#define CPUID_4_4_ECX_MASK CPUID_FIXED1 +#define CPUID_4_4_EDX_MASK CPUID_FIXED1 + +#define CPUID_7_0_EAX_FIXED (2) +#define CPUID_7_0_EAX_MASK CPUID_FIXED1 +#define CPUID_7_0_EBX_FIXED (1 | 3 << 6 | 1 << 10 | 1 << 13 | 5 << 18 | 3 << 23 | 1 << 29) +#define CPUID_7_0_EBX_MASK (7 | 3 << 6 | 1 << 10 | 3 << 13 | 5 << 18 | 7 << 22 | 1 << 29) +#define CPUID_7_0_ECX_FIXED (1 << 24 | 3 << 27) +#define CPUID_7_0_ECX_MASK (1 << 15 | 0x1f << 17 | 1 << 24 | 0x1f << 26 ) +#define CPUID_7_0_EDX_FIXED (1 << 10 | 0x3f << 26) +#define CPUID_7_0_EDX_MASK (3 | 3 << 6 | 0x1f << 9 | 1 << 17 | 1 << 21 | 0x3f << 26) +#define CPUID_7_1_EAX_MASK (0xf | 5 << 7 | 0x3ff << 16 | 0x1f << 27) +#define CPUID_7_1_EBX_MASK CPUID_FIXED1 +#define CPUID_7_1_ECX_MASK CPUID_FIXED1 +#define CPUID_7_1_EDX_MASK CPUID_FIXED1 + +#define CPUID_8_0_EAX_MASK CPUID_FIXED1 +#define CPUID_8_0_EBX_MASK CPUID_FIXED1 +#define CPUID_8_0_ECX_MASK CPUID_FIXED1 +#define CPUID_8_0_EDX_MASK CPUID_FIXED1 + +#define CPUID_a_EBX_MASK (0x0) +#define CPUID_a_EDX_MASK (0x7ffff << 13) +#define CPUID_a_EDX_FIXED BIT_ULL(15) + +#define CPUID_d_0_EAX_FIXED (3) +#define CPUID_d_0_EAX_MASK (3 | 3 << 3 | 1 << 8 | 1 << 10 | 0x1fff << 19) +#define CPUID_d_0_EDX_MASK CPUID_FIXED1 +#define CPUID_d_1_EAX_FIXED (0xf) +#define CPUID_d_1_EAX_MASK (0xf | 0x7ffffff << 5) +#define CPUID_d_1_ECX_MASK (0xff | 3 << 9 | 1 << 13 | 0xffff << 16) +#define CPUID_d_1_EDX_MASK CPUID_FIXED1 + +#define CPUID_15_EAX_FIXED (1) +#define CPUID_15_EAX_MASK CPUID_FIXED1 +#define CPUID_15_ECX_FIXED 0x17d7840 +#define CPUID_15_ECX_MASK CPUID_FIXED1 +#define CPUID_15_EDX_MASK CPUID_FIXED1 + +#define CPUID_19_ECX_MASK (0xfffffffe) +#define CPUID_19_EDX_MASK CPUID_FIXED1 + +#define CPUID_21_0_EAX_MASK CPUID_FIXED1 +#define CPUID_21_0_EBX_FIXED 0x65746e49 +#define CPUID_21_0_EBX_MASK CPUID_FIXED1 +#define CPUID_21_0_ECX_FIXED 0x20202020 +#define CPUID_21_0_ECX_MASK CPUID_FIXED1 +#define CPUID_21_0_EDX_FIXED 0x5844546c +#define CPUID_21_0_EDX_MASK CPUID_FIXED1 + +#define CPUID_80000000_EBX_MASK CPUID_FIXED1 +#define CPUID_80000000_ECX_MASK CPUID_FIXED1 +#define CPUID_80000000_EDX_MASK CPUID_FIXED1 + +#define CPUID_80000001_EAX_MASK CPUID_FIXED1 +#define CPUID_80000001_EBX_MASK CPUID_FIXED1 +#define CPUID_80000001_ECX_MASK CPUID_FIXED1 +#define CPUID_80000001_ECX_FIXED (1 | 1 << 5 | 1<< 8) +#define CPUID_80000001_EDX_FIXED (1 << 20 | 3 << 26 | 1 << 29) +#define CPUID_80000001_EDX_MASK (0xfffff7ff) + +#define CPUID_80000008_EAX_FIXED (0x3934) +#define CPUID_80000008_EAX_MASK CPUID_FIXED1 +#define CPUID_80000008_EBX_MASK (0xfffffdff) +#define CPUID_80000008_ECX_MASK CPUID_FIXED1 +#define CPUID_80000008_EDX_MASK CPUID_FIXED1 + +typedef struct CPUIDINFO { + uint32_t eax; /* Input EAX for CPUID */ + bool needs_ecx; /* CPUID instruction uses ECX as input */ + enum cpuid_output_regs ecx; /* Input ECX value for CPUID */ + int reg; /* output register (R_* constant) */ + uint32_t mask; /* The virtual bit value is fixed 0 */ + uint32_t value; /* The virtual bit value is fixed 1 */ +} CPUIDINFO; + +CPUIDINFO cpuid_info[] = { + {.eax = 0, .reg = EAX, .mask = CPUID_0_EAX_MASK, .value = CPUID_0_EAX_FIXED}, + {.eax = 1, .reg = EAX, .mask = CPUID_1_EAX_MASK}, + {.eax = 1, .reg = EBX, .mask = CPUID_1_EBX_MASK, .value = CPUID_1_EBX_FIXED}, + {.eax = 1, .reg = ECX, .mask = CPUID_1_ECX_MASK, .value = CPUID_1_ECX_FIXED}, + {.eax = 1, .reg = EDX, .mask = CPUID_1_EDX_MASK, .value = CPUID_1_EDX_FIXED}, + {.eax = 3, .reg = EAX, .mask = CPUID_3_EAX_MASK}, + {.eax = 3, .reg = EBX, .mask = CPUID_3_EBX_MASK}, + {.eax = 3, .reg = ECX, .mask = CPUID_3_ECX_MASK}, + {.eax = 3, .reg = EDX, .mask = CPUID_3_EDX_MASK}, + {.eax = 4, .needs_ecx = true, .ecx = 0, .reg = EAX, .mask = CPUID_4_0_EAX_MASK}, + {.eax = 4, .needs_ecx = true, .ecx = 0, .reg = EDX, .mask = CPUID_4_0_EDX_MASK}, + {.eax = 4, .needs_ecx = true, .ecx = 1, .reg = EAX, .mask = CPUID_4_1_EAX_MASK}, + {.eax = 4, .needs_ecx = true, .ecx = 1, .reg = EDX, .mask = CPUID_4_1_EDX_MASK}, + {.eax = 4, .needs_ecx = true, .ecx = 2, .reg = EAX, .mask = CPUID_4_2_EAX_MASK}, + {.eax = 4, .needs_ecx = true, .ecx = 2, .reg = EDX, .mask = CPUID_4_2_EDX_MASK}, + {.eax = 4, .needs_ecx = true, .ecx = 3, .reg = EAX, .mask = CPUID_4_3_EAX_MASK}, + {.eax = 4, .needs_ecx = true, .ecx = 3, .reg = EDX, .mask = CPUID_4_3_EDX_MASK}, + {.eax = 4, .needs_ecx = true, .ecx = 4, .reg = EAX, .mask = CPUID_4_4_EAX_MASK}, + {.eax = 4, .needs_ecx = true, .ecx = 4, .reg = EBX, .mask = CPUID_4_4_EBX_MASK}, + {.eax = 4, .needs_ecx = true, .ecx = 4, .reg = ECX, .mask = CPUID_4_4_ECX_MASK}, + {.eax = 4, .needs_ecx = true, .ecx = 4, .reg = EDX, .mask = CPUID_4_4_EDX_MASK}, + {.eax = 7, .needs_ecx = true, .ecx = 0, .reg = EAX, .mask = CPUID_7_0_EAX_MASK, .value = CPUID_7_0_EAX_FIXED}, + {.eax = 7, .needs_ecx = true, .ecx = 0, .reg = EBX, .mask = CPUID_7_0_EBX_MASK, .value = CPUID_7_0_EBX_FIXED}, + {.eax = 7, .needs_ecx = true, .ecx = 0, .reg = ECX, .mask = CPUID_7_0_ECX_MASK, .value = CPUID_7_0_ECX_FIXED}, + {.eax = 7, .needs_ecx = true, .ecx = 0, .reg = EDX, .mask = CPUID_7_0_EDX_MASK, .value = CPUID_7_0_EDX_FIXED}, + {.eax = 7, .needs_ecx = true, .ecx = 1, .reg = EAX, .mask = CPUID_7_1_EAX_MASK}, + {.eax = 7, .needs_ecx = true, .ecx = 1, .reg = EBX, .mask = CPUID_7_1_EBX_MASK}, + {.eax = 7, .needs_ecx = true, .ecx = 1, .reg = ECX, .mask = CPUID_7_1_ECX_MASK}, + {.eax = 7, .needs_ecx = true, .ecx = 1, .reg = EDX, .mask = CPUID_7_1_EDX_MASK}, + {.eax = 0xa, .reg = EBX, .mask = CPUID_a_EBX_MASK}, + {.eax = 0xa, .reg = EDX, .mask = CPUID_a_EDX_MASK, .value = CPUID_a_EDX_FIXED}, + {.eax = 0xd, .needs_ecx = true, .ecx = 0, .reg = EAX, .mask = CPUID_d_0_EAX_MASK, .value = CPUID_d_0_EAX_FIXED}, + {.eax = 0xd, .needs_ecx = true, .ecx = 0, .reg = EDX, .mask = CPUID_d_0_EDX_MASK}, + {.eax = 0xd, .needs_ecx = true, .ecx = 1, .reg = EAX, .mask = CPUID_d_1_EAX_MASK, .value = CPUID_d_1_EAX_FIXED}, + {.eax = 0xd, .needs_ecx = true, .ecx = 1, .reg = ECX, .mask = CPUID_d_1_ECX_MASK}, + {.eax = 0xd, .needs_ecx = true, .ecx = 1, .reg = EDX, .mask = CPUID_d_1_EDX_MASK}, + {.eax = 0x15, .reg = EAX, .mask = CPUID_15_EAX_MASK, .value = CPUID_15_EAX_FIXED}, + {.eax = 0x15, .reg = ECX, .mask = CPUID_15_ECX_MASK, .value = CPUID_15_ECX_FIXED}, + {.eax = 0x15, .reg = EDX, .mask = CPUID_15_EDX_MASK}, + {.eax = 0x19, .reg = ECX, .mask = CPUID_19_ECX_MASK}, + {.eax = 0x19, .reg = EDX, .mask = CPUID_19_EDX_MASK}, + {.eax = 0x21, .needs_ecx = true, .ecx = 0, .reg = EAX, .mask = CPUID_21_0_EAX_MASK}, + {.eax = 0x21, .needs_ecx = true, .ecx = 0, .reg = EBX, .mask = CPUID_21_0_EBX_MASK, .value = CPUID_21_0_EBX_FIXED}, + {.eax = 0x21, .needs_ecx = true, .ecx = 0, .reg = ECX, .mask = CPUID_21_0_ECX_MASK, .value = CPUID_21_0_ECX_FIXED}, + {.eax = 0x21, .needs_ecx = true, .ecx = 0, .reg = EDX, .mask = CPUID_21_0_EDX_MASK, .value = CPUID_21_0_EDX_FIXED}, + {.eax = 0x80000000, .reg = EBX, .mask = CPUID_80000000_EBX_MASK}, + {.eax = 0x80000000, .reg = ECX, .mask = CPUID_80000000_ECX_MASK}, + {.eax = 0x80000000, .reg = EDX, .mask = CPUID_80000000_EDX_MASK}, + {.eax = 0x80000001, .reg = EAX, .mask = CPUID_80000001_EAX_MASK}, + {.eax = 0x80000001, .reg = EBX, .mask = CPUID_80000001_EBX_MASK}, + {.eax = 0x80000001, .reg = ECX, .mask = CPUID_80000001_ECX_MASK, .value = CPUID_80000001_ECX_FIXED}, + {.eax = 0x80000001, .reg = EDX, .mask = CPUID_80000001_EDX_MASK, .value = CPUID_80000001_EDX_FIXED}, + {.eax = 0x80000008, .reg = EAX, .mask = CPUID_80000008_EAX_MASK, .value = CPUID_80000008_EAX_FIXED}, + {.eax = 0x80000008, .reg = EBX, .mask = CPUID_80000008_EBX_MASK}, + {.eax = 0x80000008, .reg = ECX, .mask = CPUID_80000008_ECX_MASK}, + {.eax = 0x80000008, .reg = EDX, .mask = CPUID_80000008_EDX_MASK}, +}; + +static void check_cpuid_fixed(CPUIDINFO *ci) +{ + struct cpuid c = raw_cpuid(ci->eax, ci->ecx); + uint32_t value = 0; + + switch (ci->reg) { + case EAX: + value = c.a; + break; + case EBX: + value = c.b; + break; + case ECX: + value = c.c; + break; + case EDX: + value = c.d; + break; + } + + report((value & ci->mask) == ci->value, + "cpuid check failure, eax %x, ecx %x, reg %x, mask %x, expect %x, got %x", + ci->eax, ci->ecx, ci->reg, ci->mask, ci->value, value); +} + +CPUIDINFO cpuid_all_zero_info[] = { + {.eax = 3}, + {.eax = 8}, + {.eax = 0xe}, + {.eax = 0x11}, + {.eax = 0x12}, + {.eax = 0x13}, + {.eax = 0x20}, +}; + +static void test_cpuid(void) +{ + int i; + printf("\nStart CPUID checking.\n"); + for (i = 0; i < sizeof(cpuid_info)/sizeof(*cpuid_info); i++) + { + check_cpuid_fixed(cpuid_info + i); + } + + /* Some cpuid result are all zero */ + for (i = 0; i < sizeof(cpuid_all_zero_info)/sizeof(*cpuid_all_zero_info); i++) + { + CPUIDINFO *ci = cpuid_all_zero_info + i; + ci->mask = CPUID_FIXED1; + + for (int j = 0; j < 4; j++) { + ci->reg = j; + check_cpuid_fixed(ci); + } + } + + /* Leaf 0xd / Sub-leaves 0x2-0x12 EDX zero */ + for (i = 2; i <= 0x12; i++) { + CPUIDINFO ci = {.eax = 0xd, .needs_ecx = true, .ecx = i, .reg = EDX, .mask = CPUID_FIXED1}; + check_cpuid_fixed(&ci); + } + printf("End CPUID checking.\n"); +} + +int main(void) +{ + if (!is_tdx_guest()) { + printf("Not TDX environment!\n"); + return report_summary(); + } + + test_selfipi_msr(); + test_single_step(); + test_cpuid(); + return report_summary(); +} diff --git a/x86/unittests.cfg b/x86/unittests.cfg index 3fe59449..8a3830d8 100644 --- a/x86/unittests.cfg +++ b/x86/unittests.cfg @@ -491,3 +491,7 @@ file = cet.flat arch = x86_64 smp = 2 extra_params = -enable-kvm -m 2048 -cpu host + +[intel_tdx] +file = intel_tdx.flat +arch = x86_64 -- 2.25.1