From: Roger Wang <runanwang@xxxxxxxxxx> Adds a test for TDG.VP.INFO Signed-off-by: Roger Wang <runanwang@xxxxxxxxxx> Signed-off-by: Sagi Shahar <sagis@xxxxxxxxxx> --- tools/testing/selftests/kvm/lib/x86_64/tdx.h | 103 ++++++++---- .../selftests/kvm/lib/x86_64/tdx_lib.c | 18 ++- .../selftests/kvm/x86_64/tdx_vm_tests.c | 150 ++++++++++++++++++ 3 files changed, 235 insertions(+), 36 deletions(-) diff --git a/tools/testing/selftests/kvm/lib/x86_64/tdx.h b/tools/testing/selftests/kvm/lib/x86_64/tdx.h index 3729543a05a3..7af2d189043f 100644 --- a/tools/testing/selftests/kvm/lib/x86_64/tdx.h +++ b/tools/testing/selftests/kvm/lib/x86_64/tdx.h @@ -10,6 +10,11 @@ */ #define TDX_GUEST_MAX_NR_PAGES 10000 +/* + * Max number of vCPUs for the guest VM + */ + #define TDX_GUEST_MAX_NUM_VCPUS 3 + /* * Page Table Address used when paging is enabled. */ @@ -71,6 +76,11 @@ #define TDX_MMIO_READ 0 #define TDX_MMIO_WRITE 1 +#define TDX_TDCALL_INFO 1 + +#define TDX_TDPARAM_ATTR_SEPT_VE_DISABLE_BIT (1UL << 28) +#define TDX_TDPARAM_ATTR_PKS_BIT (1UL << 30) + #define GDT_ENTRY(flags, base, limit) \ ((((base) & 0xff000000ULL) << (56-24)) | \ (((flags) & 0x0000f0ffULL) << 40) | \ @@ -98,6 +108,7 @@ void add_td_memory(struct kvm_vm *vm, void *source_page, uint64_t gpa, int size); void finalize_td_memory(struct kvm_vm *vm); void initialize_td(struct kvm_vm *vm); +void initialize_td_with_attributes(struct kvm_vm *vm, uint64_t attributes); void initialize_td_vcpu(struct kvm_vcpu *vcpu); void prepare_source_image(struct kvm_vm *vm, void *guest_code, size_t guest_code_size, @@ -116,40 +127,41 @@ void prepare_source_image(struct kvm_vm *vm, void *guest_code, static inline void tdcall(struct kvm_regs *regs) { asm volatile ( - "mov %13, %%rax;\n\t" - "mov %14, %%rbx;\n\t" - "mov %15, %%rcx;\n\t" - "mov %16, %%rdx;\n\t" - "mov %17, %%r8;\n\t" - "mov %18, %%r9;\n\t" - "mov %19, %%r10;\n\t" - "mov %20, %%r11;\n\t" - "mov %21, %%r12;\n\t" - "mov %22, %%r13;\n\t" - "mov %23, %%r14;\n\t" - "mov %24, %%r15;\n\t" - "mov %25, %%rbp;\n\t" - "mov %26, %%rsi;\n\t" - "mov %27, %%rdi;\n\t" + "mov %14, %%rax;\n\t" + "mov %15, %%rbx;\n\t" + "mov %16, %%rcx;\n\t" + "mov %17, %%rdx;\n\t" + "mov %18, %%r8;\n\t" + "mov %19, %%r9;\n\t" + "mov %20, %%r10;\n\t" + "mov %21, %%r11;\n\t" + "mov %22, %%r12;\n\t" + "mov %23, %%r13;\n\t" + "mov %24, %%r14;\n\t" + "mov %25, %%r15;\n\t" + "mov %26, %%rbp;\n\t" + "mov %27, %%rsi;\n\t" + "mov %28, %%rdi;\n\t" ".byte 0x66, 0x0F, 0x01, 0xCC;\n\t" "mov %%rax, %0;\n\t" "mov %%rbx, %1;\n\t" - "mov %%rdx, %2;\n\t" - "mov %%r8, %3;\n\t" - "mov %%r9, %4;\n\t" - "mov %%r10, %5;\n\t" - "mov %%r11, %6;\n\t" - "mov %%r12, %7;\n\t" - "mov %%r13, %8;\n\t" - "mov %%r14, %9;\n\t" - "mov %%r15, %10;\n\t" - "mov %%rsi, %11;\n\t" - "mov %%rdi, %12;\n\t" - : "=m" (regs->rax), "=m" (regs->rbx), "=m" (regs->rdx), - "=m" (regs->r8), "=m" (regs->r9), "=m" (regs->r10), - "=m" (regs->r11), "=m" (regs->r12), "=m" (regs->r13), - "=m" (regs->r14), "=m" (regs->r15), "=m" (regs->rsi), - "=m" (regs->rdi) + "mov %%rcx, %2;\n\t" + "mov %%rdx, %3;\n\t" + "mov %%r8, %4;\n\t" + "mov %%r9, %5;\n\t" + "mov %%r10, %6;\n\t" + "mov %%r11, %7;\n\t" + "mov %%r12, %8;\n\t" + "mov %%r13, %9;\n\t" + "mov %%r14, %10;\n\t" + "mov %%r15, %11;\n\t" + "mov %%rsi, %12;\n\t" + "mov %%rdi, %13;\n\t" + : "=m" (regs->rax), "=m" (regs->rbx), "=m" (regs->rcx), + "=m" (regs->rdx), "=m" (regs->r8), "=m" (regs->r9), + "=m" (regs->r10), "=m" (regs->r11), "=m" (regs->r12), + "=m" (regs->r13), "=m" (regs->r14), "=m" (regs->r15), + "=m" (regs->rsi), "=m" (regs->rdi) : "m" (regs->rax), "m" (regs->rbx), "m" (regs->rcx), "m" (regs->rdx), "m" (regs->r8), "m" (regs->r9), "m" (regs->r10), "m" (regs->r11), "m" (regs->r12), @@ -370,6 +382,35 @@ static inline uint64_t tdvmcall_cpuid(uint32_t eax, uint32_t ecx, return regs.r10; } +/* + * Execute TDG.VP.INFO instruction. + */ +static inline uint64_t tdcall_vp_info(uint64_t *rcx, uint64_t *rdx, + uint64_t *r8, uint64_t *r9, + uint64_t *r10, uint64_t *r11) +{ + struct kvm_regs regs; + + memset(®s, 0, sizeof(regs)); + regs.rax = TDX_TDCALL_INFO; + tdcall(®s); + + if (rcx) + *rcx = regs.rcx; + if (rdx) + *rdx = regs.rdx; + if (r8) + *r8 = regs.r8; + if (r9) + *r9 = regs.r9; + if (r10) + *r10 = regs.r10; + if (r11) + *r11 = regs.r11; + + return regs.rax; +} + /* * Reports a 32 bit value from the guest to user space using a TDVM IO call. * Data is reported on port TDX_DATA_REPORT_PORT. diff --git a/tools/testing/selftests/kvm/lib/x86_64/tdx_lib.c b/tools/testing/selftests/kvm/lib/x86_64/tdx_lib.c index 72bf2ff24a29..dc9a44ae4064 100644 --- a/tools/testing/selftests/kvm/lib/x86_64/tdx_lib.c +++ b/tools/testing/selftests/kvm/lib/x86_64/tdx_lib.c @@ -78,10 +78,10 @@ static struct tdx_cpuid_data get_tdx_cpuid_data(struct kvm_vm *vm) } /* - * Initialize a VM as a TD. + * Initialize a VM as a TD with attributes. * */ -void initialize_td(struct kvm_vm *vm) +void initialize_td_with_attributes(struct kvm_vm *vm, uint64_t attributes) { struct tdx_cpuid_data cpuid_data; int rc; @@ -99,7 +99,7 @@ void initialize_td(struct kvm_vm *vm) KVM_X2APIC_API_DISABLE_BROADCAST_QUIRK); vm_enable_cap(vm, KVM_CAP_SPLIT_IRQCHIP, 24); - /* Allocate and setup memoryfor the td guest. */ + /* Allocate and setup memory for the td guest. */ vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, TDX_GUEST_PT_FIXED_ADDR, 0, TDX_GUEST_MAX_NR_PAGES, 0); @@ -108,12 +108,20 @@ void initialize_td(struct kvm_vm *vm) cpuid_data = get_tdx_cpuid_data(vm); - init_vm.max_vcpus = 1; - init_vm.attributes = 0; + init_vm.max_vcpus = TDX_GUEST_MAX_NUM_VCPUS; + init_vm.attributes = attributes; memcpy(&init_vm.cpuid, &cpuid_data, sizeof(cpuid_data)); tdx_ioctl(vm->fd, KVM_TDX_INIT_VM, 0, &init_vm); } +/* + * Initialize a VM as a TD with no attributes. + * + */ +void initialize_td(struct kvm_vm *vm) +{ + initialize_td_with_attributes(vm, 0); +} void initialize_td_vcpu(struct kvm_vcpu *vcpu) { diff --git a/tools/testing/selftests/kvm/x86_64/tdx_vm_tests.c b/tools/testing/selftests/kvm/x86_64/tdx_vm_tests.c index 1776b39b7d9e..8d49099e1ed8 100644 --- a/tools/testing/selftests/kvm/x86_64/tdx_vm_tests.c +++ b/tools/testing/selftests/kvm/x86_64/tdx_vm_tests.c @@ -2,6 +2,7 @@ #include "asm/kvm.h" #include "linux/kernel.h" +#include <assert.h> #include <bits/stdint-uintn.h> #include <fcntl.h> #include <limits.h> @@ -1366,6 +1367,154 @@ void verify_host_reading_private_mem(void) printf("\t ... PASSED\n"); } +/* + * Do a TDG.VP.INFO call from the guest + */ +TDX_GUEST_FUNCTION(guest_tdcall_vp_info) +{ + uint64_t err; + uint64_t rcx, rdx, r8, r9, r10, r11; + + err = tdcall_vp_info(&rcx, &rdx, &r8, &r9, &r10, &r11); + if (err) + tdvmcall_fatal(err); + + /* return values to user space host */ + err = tdvm_report_64bit_to_user_space(rcx); + if (err) + tdvmcall_fatal(err); + + err = tdvm_report_64bit_to_user_space(rdx); + if (err) + tdvmcall_fatal(err); + + err = tdvm_report_64bit_to_user_space(r8); + if (err) + tdvmcall_fatal(err); + + err = tdvm_report_64bit_to_user_space(r9); + if (err) + tdvmcall_fatal(err); + + err = tdvm_report_64bit_to_user_space(r10); + if (err) + tdvmcall_fatal(err); + + err = tdvm_report_64bit_to_user_space(r11); + if (err) + tdvmcall_fatal(err); + + tdvmcall_success(); +} + +/* + * TDG.VP.INFO call from the guest. Verify the right values are returned + */ +void verify_tdcall_vp_info(void) +{ + const int num_vcpus = 2; + struct kvm_vcpu *vcpus[num_vcpus]; + struct kvm_vm *vm; + uint64_t rcx, rdx, r8, r9, r10, r11; + uint32_t ret_num_vcpus, ret_max_vcpus; + uint64_t attributes; + uint32_t i; + struct kvm_cpuid_entry2 *cpuid_entry; + struct tdx_cpuid_data cpuid_data; + int max_pa = -1; + int ret; + + printf("Verifying TDG.VP.INFO call:\n"); + /* Create a TD VM with no memory.*/ + vm = vm_create_tdx(); + + /* Setting attributes parameter used by TDH.MNG.INIT to 0x50000000 */ + attributes = TDX_TDPARAM_ATTR_SEPT_VE_DISABLE_BIT | + TDX_TDPARAM_ATTR_PKS_BIT; + + /* Allocate TD guest memory and initialize the TD.*/ + initialize_td_with_attributes(vm, attributes); + + /* Create vCPUs*/ + for (i = 0; i < num_vcpus; i++) + vcpus[i] = vm_vcpu_add_tdx(vm, i); + + /* Setup and initialize VM memory */ + prepare_source_image(vm, guest_tdcall_vp_info, + TDX_FUNCTION_SIZE(guest_tdcall_vp_info), 0); + finalize_td_memory(vm); + + /* Get KVM CPUIDs for reference */ + memset(&cpuid_data, 0, sizeof(cpuid_data)); + cpuid_data.cpuid.nent = KVM_MAX_CPUID_ENTRIES; + ret = ioctl(vm->kvm_fd, KVM_GET_SUPPORTED_CPUID, &cpuid_data); + TEST_ASSERT(!ret, "KVM_GET_SUPPORTED_CPUID failed\n"); + cpuid_entry = find_cpuid_entry(cpuid_data, 0x80000008, 0); + TEST_ASSERT(cpuid_entry, "CPUID entry missing\n"); + max_pa = cpuid_entry->eax & 0xff; + + for (i = 0; i < num_vcpus; i++) { + struct kvm_vcpu *vcpu = vcpus[i]; + + /* Wait for guest to report rcx value */ + vcpu_run(vcpu); + CHECK_GUEST_FAILURE(vcpu); + rcx = read_64bit_from_guest(vcpu, TDX_DATA_REPORT_PORT); + + /* Wait for guest to report rdx value */ + vcpu_run(vcpu); + CHECK_GUEST_FAILURE(vcpu); + rdx = read_64bit_from_guest(vcpu, TDX_DATA_REPORT_PORT); + + /* Wait for guest to report r8 value */ + vcpu_run(vcpu); + CHECK_GUEST_FAILURE(vcpu); + r8 = read_64bit_from_guest(vcpu, TDX_DATA_REPORT_PORT); + + /* Wait for guest to report r9 value */ + vcpu_run(vcpu); + CHECK_GUEST_FAILURE(vcpu); + r9 = read_64bit_from_guest(vcpu, TDX_DATA_REPORT_PORT); + + /* Wait for guest to report r10 value */ + vcpu_run(vcpu); + CHECK_GUEST_FAILURE(vcpu); + r10 = read_64bit_from_guest(vcpu, TDX_DATA_REPORT_PORT); + + /* Wait for guest to report r11 value */ + vcpu_run(vcpu); + CHECK_GUEST_FAILURE(vcpu); + r11 = read_64bit_from_guest(vcpu, TDX_DATA_REPORT_PORT); + + ret_num_vcpus = r8 & 0xFFFFFFFF; + ret_max_vcpus = (r8 >> 32) & 0xFFFFFFFF; + + /* first bits 5:0 of rcx represent the GPAW */ + ASSERT_EQ(rcx & 0x3F, max_pa); + /* next 63:6 bits of rcx is reserved and must be 0 */ + ASSERT_EQ(rcx >> 6, 0); + ASSERT_EQ(rdx, attributes); + ASSERT_EQ(ret_num_vcpus, num_vcpus); + ASSERT_EQ(ret_max_vcpus, TDX_GUEST_MAX_NUM_VCPUS); + /* VCPU_INDEX = i */ + ASSERT_EQ(r9, i); + /* verify reserved registers are 0 */ + ASSERT_EQ(r10, 0); + ASSERT_EQ(r11, 0); + + /* Wait for guest to complete execution */ + vcpu_run(vcpu); + + CHECK_GUEST_FAILURE(vcpu); + CHECK_GUEST_COMPLETION(vcpu); + + printf("\t ... Guest completed run on VCPU=%u\n", i); + } + + kvm_vm_free(vm); + printf("\t ... PASSED\n"); +} + int main(int argc, char **argv) { if (!is_tdx_enabled()) { @@ -1387,6 +1536,7 @@ int main(int argc, char **argv) run_in_new_process(&verify_mmio_reads); run_in_new_process(&verify_mmio_writes); run_in_new_process(&verify_host_reading_private_mem); + run_in_new_process(&verify_tdcall_vp_info); return 0; } -- 2.37.2.789.g6183377224-goog