On 12/12/2023 12:46 PM, Sagi Shahar wrote: > The test verifies MMIO reads of various sizes from the host to the guest. > > Signed-off-by: Sagi Shahar <sagis@xxxxxxxxxx> > Signed-off-by: Ackerley Tng <ackerleytng@xxxxxxxxxx> > Signed-off-by: Ryan Afranji <afranji@xxxxxxxxxx> > --- > .../selftests/kvm/include/x86_64/tdx/tdcall.h | 2 + > .../selftests/kvm/include/x86_64/tdx/tdx.h | 3 + > .../kvm/include/x86_64/tdx/test_util.h | 23 +++++ > .../selftests/kvm/lib/x86_64/tdx/tdx.c | 19 ++++ > .../selftests/kvm/x86_64/tdx_vm_tests.c | 87 +++++++++++++++++++ > 5 files changed, 134 insertions(+) > > diff --git a/tools/testing/selftests/kvm/include/x86_64/tdx/tdcall.h b/tools/testing/selftests/kvm/include/x86_64/tdx/tdcall.h > index b5e94b7c48fa..95fcdbd8404e 100644 > --- a/tools/testing/selftests/kvm/include/x86_64/tdx/tdcall.h > +++ b/tools/testing/selftests/kvm/include/x86_64/tdx/tdcall.h > @@ -9,6 +9,8 @@ > > #define TDG_VP_VMCALL_INSTRUCTION_IO_READ 0 > #define TDG_VP_VMCALL_INSTRUCTION_IO_WRITE 1 > +#define TDG_VP_VMCALL_VE_REQUEST_MMIO_READ 0 > +#define TDG_VP_VMCALL_VE_REQUEST_MMIO_WRITE 1 > > #define TDG_VP_VMCALL_SUCCESS 0x0000000000000000 > #define TDG_VP_VMCALL_INVALID_OPERAND 0x8000000000000000 > diff --git a/tools/testing/selftests/kvm/include/x86_64/tdx/tdx.h b/tools/testing/selftests/kvm/include/x86_64/tdx/tdx.h > index b18e39d20498..13ce60df5684 100644 > --- a/tools/testing/selftests/kvm/include/x86_64/tdx/tdx.h > +++ b/tools/testing/selftests/kvm/include/x86_64/tdx/tdx.h > @@ -12,6 +12,7 @@ > #define TDG_VP_VMCALL_INSTRUCTION_IO 30 > #define TDG_VP_VMCALL_INSTRUCTION_RDMSR 31 > #define TDG_VP_VMCALL_INSTRUCTION_WRMSR 32 > +#define TDG_VP_VMCALL_VE_REQUEST_MMIO 48 > > void handle_userspace_tdg_vp_vmcall_exit(struct kvm_vcpu *vcpu); > uint64_t tdg_vp_vmcall_instruction_io(uint64_t port, uint64_t size, > @@ -22,5 +23,7 @@ uint64_t tdg_vp_vmcall_get_td_vmcall_info(uint64_t *r11, uint64_t *r12, > uint64_t tdg_vp_vmcall_instruction_rdmsr(uint64_t index, uint64_t *ret_value); > uint64_t tdg_vp_vmcall_instruction_wrmsr(uint64_t index, uint64_t value); > uint64_t tdg_vp_vmcall_instruction_hlt(uint64_t interrupt_blocked_flag); > +uint64_t tdg_vp_vmcall_ve_request_mmio_read(uint64_t address, uint64_t size, > + uint64_t *data_out); > > #endif // SELFTEST_TDX_TDX_H > diff --git a/tools/testing/selftests/kvm/include/x86_64/tdx/test_util.h b/tools/testing/selftests/kvm/include/x86_64/tdx/test_util.h > index 8a9b6a1bec3e..af412b764604 100644 > --- a/tools/testing/selftests/kvm/include/x86_64/tdx/test_util.h > +++ b/tools/testing/selftests/kvm/include/x86_64/tdx/test_util.h > @@ -35,6 +35,29 @@ > (VCPU)->run->io.direction); \ > } while (0) > > + > +/** > + * Assert that some MMIO operation involving TDG.VP.VMCALL <#VERequestMMIO> was > + * called in the guest. > + */ > +#define TDX_TEST_ASSERT_MMIO(VCPU, ADDR, SIZE, DIR) \ > + do { \ > + TEST_ASSERT((VCPU)->run->exit_reason == KVM_EXIT_MMIO, \ > + "Got exit_reason other than KVM_EXIT_MMIO: %u (%s)\n", \ > + (VCPU)->run->exit_reason, \ > + exit_reason_str((VCPU)->run->exit_reason)); \ > + \ > + TEST_ASSERT(((VCPU)->run->exit_reason == KVM_EXIT_MMIO) && \ > + ((VCPU)->run->mmio.phys_addr == (ADDR)) && \ > + ((VCPU)->run->mmio.len == (SIZE)) && \ > + ((VCPU)->run->mmio.is_write == (DIR)), \ > + "Got an unexpected MMIO exit values: %u (%s) %llu %d %d\n", \ > + (VCPU)->run->exit_reason, \ > + exit_reason_str((VCPU)->run->exit_reason), \ > + (VCPU)->run->mmio.phys_addr, (VCPU)->run->mmio.len, \ > + (VCPU)->run->mmio.is_write); \ > + } while (0) > + > /** > * Check and report if there was some failure in the guest, either an exception > * like a triple fault, or if a tdx_test_fatal() was hit. > diff --git a/tools/testing/selftests/kvm/lib/x86_64/tdx/tdx.c b/tools/testing/selftests/kvm/lib/x86_64/tdx/tdx.c > index 9485bafedc38..b19f07ebc0e7 100644 > --- a/tools/testing/selftests/kvm/lib/x86_64/tdx/tdx.c > +++ b/tools/testing/selftests/kvm/lib/x86_64/tdx/tdx.c > @@ -124,3 +124,22 @@ uint64_t tdg_vp_vmcall_instruction_hlt(uint64_t interrupt_blocked_flag) > > return __tdx_hypercall(&args, 0); > } > + > +uint64_t tdg_vp_vmcall_ve_request_mmio_read(uint64_t address, uint64_t size, > + uint64_t *data_out) > +{ > + uint64_t ret; > + struct tdx_hypercall_args args = { > + .r11 = TDG_VP_VMCALL_VE_REQUEST_MMIO, > + .r12 = size, > + .r13 = TDG_VP_VMCALL_VE_REQUEST_MMIO_READ, > + .r14 = address, > + }; > + > + ret = __tdx_hypercall(&args, TDX_HCALL_HAS_OUTPUT); > + > + if (data_out) > + *data_out = args.r11; > + > + return ret; > +} > 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 5fae4c6e5f95..48902b69d13e 100644 > --- a/tools/testing/selftests/kvm/x86_64/tdx_vm_tests.c > +++ b/tools/testing/selftests/kvm/x86_64/tdx_vm_tests.c > @@ -799,6 +799,92 @@ void verify_guest_hlt(void) > _verify_guest_hlt(0); > } > > +/* Pick any address that was not mapped into the guest to test MMIO */ > +#define TDX_MMIO_TEST_ADDR 0x200000000 > + > +void guest_mmio_reads(void) > +{ > + uint64_t data; > + uint64_t ret; > + > + ret = tdg_vp_vmcall_ve_request_mmio_read(TDX_MMIO_TEST_ADDR, 1, &data); > + if (ret) > + tdx_test_fatal(ret); > + if (data != 0x12) > + tdx_test_fatal(1); > + > + ret = tdg_vp_vmcall_ve_request_mmio_read(TDX_MMIO_TEST_ADDR, 2, &data); > + if (ret) > + tdx_test_fatal(ret); > + if (data != 0x1234) > + tdx_test_fatal(2); > + > + ret = tdg_vp_vmcall_ve_request_mmio_read(TDX_MMIO_TEST_ADDR, 4, &data); > + if (ret) > + tdx_test_fatal(ret); > + if (data != 0x12345678) > + tdx_test_fatal(4); > + > + ret = tdg_vp_vmcall_ve_request_mmio_read(TDX_MMIO_TEST_ADDR, 8, &data); > + if (ret) > + tdx_test_fatal(ret); > + if (data != 0x1234567890ABCDEF) > + tdx_test_fatal(8); > + > + // Read an invalid number of bytes. > + ret = tdg_vp_vmcall_ve_request_mmio_read(TDX_MMIO_TEST_ADDR, 10, &data); > + if (ret) > + tdx_test_fatal(ret); > + > + tdx_test_success(); > +} > + > +/* > + * Varifies guest MMIO reads. Nit: typo? Varifies ==> Verifies > + */ > +void verify_mmio_reads(void) > +{ > + struct kvm_vm *vm; > + struct kvm_vcpu *vcpu; > + > + vm = td_create(); > + td_initialize(vm, VM_MEM_SRC_ANONYMOUS, 0); > + vcpu = td_vcpu_add(vm, 0, guest_mmio_reads); > + td_finalize(vm); > + > + printf("Verifying TD MMIO reads:\n"); > + > + td_vcpu_run(vcpu); > + TDX_TEST_CHECK_GUEST_FAILURE(vcpu); > + TDX_TEST_ASSERT_MMIO(vcpu, TDX_MMIO_TEST_ADDR, 1, TDG_VP_VMCALL_VE_REQUEST_MMIO_READ); > + *(uint8_t *)vcpu->run->mmio.data = 0x12; > + > + td_vcpu_run(vcpu); > + TDX_TEST_CHECK_GUEST_FAILURE(vcpu); > + TDX_TEST_ASSERT_MMIO(vcpu, TDX_MMIO_TEST_ADDR, 2, TDG_VP_VMCALL_VE_REQUEST_MMIO_READ); > + *(uint16_t *)vcpu->run->mmio.data = 0x1234; > + > + td_vcpu_run(vcpu); > + TDX_TEST_CHECK_GUEST_FAILURE(vcpu); > + TDX_TEST_ASSERT_MMIO(vcpu, TDX_MMIO_TEST_ADDR, 4, TDG_VP_VMCALL_VE_REQUEST_MMIO_READ); > + *(uint32_t *)vcpu->run->mmio.data = 0x12345678; > + > + td_vcpu_run(vcpu); > + TDX_TEST_CHECK_GUEST_FAILURE(vcpu); > + TDX_TEST_ASSERT_MMIO(vcpu, TDX_MMIO_TEST_ADDR, 8, TDG_VP_VMCALL_VE_REQUEST_MMIO_READ); > + *(uint64_t *)vcpu->run->mmio.data = 0x1234567890ABCDEF; > + > + td_vcpu_run(vcpu); > + TEST_ASSERT_EQ(vcpu->run->exit_reason, KVM_EXIT_SYSTEM_EVENT); > + TEST_ASSERT_EQ(vcpu->run->system_event.data[1], TDG_VP_VMCALL_INVALID_OPERAND); > + > + td_vcpu_run(vcpu); > + TDX_TEST_ASSERT_SUCCESS(vcpu); > + > + kvm_vm_free(vm); > + printf("\t ... PASSED\n"); > +} > + > int main(int argc, char **argv) > { > setbuf(stdout, NULL); > @@ -818,6 +904,7 @@ int main(int argc, char **argv) > run_in_new_process(&verify_guest_msr_writes); > run_in_new_process(&verify_guest_msr_reads); > run_in_new_process(&verify_guest_hlt); > + run_in_new_process(&verify_mmio_reads); > > return 0; > }