Hello,
This is v3 of the patch series for TDX selftests.
It has been updated for Intel’s V10 of the TDX host patches which was
proposed in https://lkml.org/lkml/2022/8/8/877
The tree can be found at
https://github.com/googleprodkernel/linux-cc/tree/tdx-selftests-rfc-v3/
Changes from RFC v2:
Selftest setup now builds upon the KVM selftest framework when setting
up the guest for testing. We now use the KVM selftest framework to
build the guest page tables and load the ELF binary into guest memory.
Inlining of the entire guest image is no longer required and that
allows us to cleanly separate code into different compilation units
and be able to use proper assembly instead of inline assembly
(addresses Sean’s comment).
To achieve this, we take a dependency on the SEV VM tests:
https://lore.kernel.org/lkml/[email protected]/T/. Those
patches provide functions for the host to allocate and track protected
memory in the guest.
In RFCv3, TDX selftest code is organized into:
+ headers in tools/testing/selftests/kvm/include/x86_64/tdx/
+ common code in tools/testing/selftests/kvm/lib/x86_64/tdx/
+ selftests in tools/testing/selftests/kvm/x86_64/tdx_*
RFCv3 also adds additional selftests for UPM.
Dependencies
+ Peter’s patches, which provide functions for the host to allocate
and track protected memory in the
guest. https://lore.kernel.org/lkml/[email protected]/T/
+ Peter’s patches depend on Sean’s patches:
+ https://lore.kernel.org/linux-arm-kernel/[email protected]/T/
+ https://lore.kernel.org/lkml/[email protected]/T/
+ Proposed fixes for these these issues mentioned on the mailing list
+ https://lore.kernel.org/lkml/[email protected]/
+ https://lore.kernel.org/lkml/[email protected]/
+ https://lore.kernel.org/lkml/[email protected]/
+ https://lore.kernel.org/lkml/[email protected]/
+ https://lore.kernel.org/linux-mm/[email protected]/
Further work for this patch series/TODOs
+ Sean’s comments for the non-confidential UPM selftests patch series
at https://lore.kernel.org/lkml/[email protected]/T/#u apply
here as well
+ Add ucall support for TDX selftests
I would also like to acknowledge the following people, who helped
review or test patches in RFCv1 and RFCv2:
+ Sean Christopherson <[email protected]>
+ Zhenzhong Duan <[email protected]>
+ Peter Gonda <[email protected]>
+ Andrew Jones <[email protected]>
+ Maxim Levitsky <[email protected]>
+ Xiaoyao Li <[email protected]>
+ David Matlack <[email protected]>
+ Marc Orr <[email protected]>
+ Isaku Yamahata <[email protected]>
Links to earlier patch series
+ RFC v1: https://lore.kernel.org/lkml/[email protected]/T/#u
+ RFC v2: https://lore.kernel.org/lkml/[email protected]/T/#u
Ackerley Tng (14):
KVM: selftests: Add function to allow one-to-one GVA to GPA mappings
KVM: selftests: Expose function that sets up sregs based on VM's mode
KVM: selftests: Store initial stack address in struct kvm_vcpu
KVM: selftests: Refactor steps in vCPU descriptor table initialization
KVM: selftests: TDX: Use KVM_TDX_CAPABILITIES to validate TDs'
attribute configuration
KVM: selftests: Require GCC to realign stacks on function entry
KVM: selftests: Add functions to allow mapping as shared
KVM: selftests: Add support for restricted memory
KVM: selftests: TDX: Update load_td_memory_region for VM memory backed
by restricted memfd
KVM: selftests: Expose _vm_vaddr_alloc
KVM: selftests: TDX: Add support for TDG.MEM.PAGE.ACCEPT
KVM: selftests: TDX: Add support for TDG.VP.VEINFO.GET
KVM: selftests: TDX: Add TDX UPM selftest
KVM: selftests: TDX: Add TDX UPM selftests for implicit conversion
Erdem Aktas (4):
KVM: selftests: Add support for creating non-default type VMs
KVM: selftests: Add helper functions to create TDX VMs
KVM: selftests: TDX: Add TDX lifecycle test
KVM: selftests: TDX: Adding test case for TDX port IO
Roger Wang (1):
KVM: selftests: TDX: Add TDG.VP.INFO test
Ryan Afranji (2):
KVM: selftests: TDX: Verify the behavior when host consumes a TD
private memory
KVM: selftests: TDX: Add shared memory test
Sagi Shahar (10):
KVM: selftests: TDX: Add report_fatal_error test
KVM: selftests: TDX: Add basic TDX CPUID test
KVM: selftests: TDX: Add basic get_td_vmcall_info test
KVM: selftests: TDX: Add TDX IO writes test
KVM: selftests: TDX: Add TDX IO reads test
KVM: selftests: TDX: Add TDX MSR read/write tests
KVM: selftests: TDX: Add TDX HLT exit test
KVM: selftests: TDX: Add TDX MMIO reads test
KVM: selftests: TDX: Add TDX MMIO writes test
KVM: selftests: TDX: Add TDX CPUID TDVMCALL test
tools/testing/selftests/kvm/.gitignore | 3 +
tools/testing/selftests/kvm/Makefile | 10 +-
.../selftests/kvm/include/kvm_util_base.h | 43 +-
.../testing/selftests/kvm/include/test_util.h | 2 +
.../selftests/kvm/include/x86_64/processor.h | 4 +
.../kvm/include/x86_64/tdx/td_boot.h | 82 +
.../kvm/include/x86_64/tdx/td_boot_asm.h | 16 +
.../selftests/kvm/include/x86_64/tdx/tdcall.h | 59 +
.../selftests/kvm/include/x86_64/tdx/tdx.h | 65 +
.../kvm/include/x86_64/tdx/tdx_util.h | 19 +
.../kvm/include/x86_64/tdx/test_util.h | 164 ++
tools/testing/selftests/kvm/lib/kvm_util.c | 123 +-
tools/testing/selftests/kvm/lib/test_util.c | 7 +
.../selftests/kvm/lib/x86_64/processor.c | 77 +-
tools/testing/selftests/kvm/lib/x86_64/sev.c | 2 +-
.../selftests/kvm/lib/x86_64/tdx/td_boot.S | 101 ++
.../selftests/kvm/lib/x86_64/tdx/tdcall.S | 158 ++
.../selftests/kvm/lib/x86_64/tdx/tdx.c | 231 +++
.../selftests/kvm/lib/x86_64/tdx/tdx_util.c | 562 +++++++
.../selftests/kvm/lib/x86_64/tdx/test_util.c | 101 ++
.../kvm/x86_64/tdx_shared_mem_test.c | 137 ++
.../selftests/kvm/x86_64/tdx_upm_test.c | 460 ++++++
.../selftests/kvm/x86_64/tdx_vm_tests.c | 1329 +++++++++++++++++
23 files changed, 3709 insertions(+), 46 deletions(-)
create mode 100644 tools/testing/selftests/kvm/include/x86_64/tdx/td_boot.h
create mode 100644 tools/testing/selftests/kvm/include/x86_64/tdx/td_boot_asm.h
create mode 100644 tools/testing/selftests/kvm/include/x86_64/tdx/tdcall.h
create mode 100644 tools/testing/selftests/kvm/include/x86_64/tdx/tdx.h
create mode 100644 tools/testing/selftests/kvm/include/x86_64/tdx/tdx_util.h
create mode 100644 tools/testing/selftests/kvm/include/x86_64/tdx/test_util.h
create mode 100644 tools/testing/selftests/kvm/lib/x86_64/tdx/td_boot.S
create mode 100644 tools/testing/selftests/kvm/lib/x86_64/tdx/tdcall.S
create mode 100644 tools/testing/selftests/kvm/lib/x86_64/tdx/tdx.c
create mode 100644 tools/testing/selftests/kvm/lib/x86_64/tdx/tdx_util.c
create mode 100644 tools/testing/selftests/kvm/lib/x86_64/tdx/test_util.c
create mode 100644 tools/testing/selftests/kvm/x86_64/tdx_shared_mem_test.c
create mode 100644 tools/testing/selftests/kvm/x86_64/tdx_upm_test.c
create mode 100644 tools/testing/selftests/kvm/x86_64/tdx_vm_tests.c
--
2.39.0.246.g2a6d74b583-goog
From: Sagi Shahar <[email protected]>
The test verifies MMIO writes of various sizes from the guest to the host.
Signed-off-by: Sagi Shahar <[email protected]>
Signed-off-by: Ackerley Tng <[email protected]>
---
.../selftests/kvm/include/x86_64/tdx/tdx.h | 2 +
.../selftests/kvm/lib/x86_64/tdx/tdx.c | 14 +++
.../selftests/kvm/x86_64/tdx_vm_tests.c | 85 +++++++++++++++++++
3 files changed, 101 insertions(+)
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 8dd6a65485260..f2a90ad8a55c6 100644
--- a/tools/testing/selftests/kvm/include/x86_64/tdx/tdx.h
+++ b/tools/testing/selftests/kvm/include/x86_64/tdx/tdx.h
@@ -24,5 +24,7 @@ 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);
+uint64_t tdg_vp_vmcall_ve_request_mmio_write(uint64_t address, uint64_t size,
+ uint64_t data_in);
#endif // SELFTEST_TDX_TDX_H
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 dcdacf08bcd60..8b12ac7049572 100644
--- a/tools/testing/selftests/kvm/lib/x86_64/tdx/tdx.c
+++ b/tools/testing/selftests/kvm/lib/x86_64/tdx/tdx.c
@@ -122,3 +122,17 @@ uint64_t tdg_vp_vmcall_ve_request_mmio_read(uint64_t address, uint64_t size,
return ret;
}
+
+uint64_t tdg_vp_vmcall_ve_request_mmio_write(uint64_t address, uint64_t size,
+ uint64_t data_in)
+{
+ struct tdx_hypercall_args args = {
+ .r11 = TDG_VP_VMCALL_VE_REQUEST_MMIO,
+ .r12 = size,
+ .r13 = TDG_VP_VMCALL_VE_REQUEST_MMIO_WRITE,
+ .r14 = address,
+ .r15 = data_in,
+ };
+
+ return __tdx_hypercall(&args, 0);
+}
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 88f0429db0176..dcc0940a74e92 100644
--- a/tools/testing/selftests/kvm/x86_64/tdx_vm_tests.c
+++ b/tools/testing/selftests/kvm/x86_64/tdx_vm_tests.c
@@ -892,6 +892,90 @@ void verify_mmio_reads(void)
printf("\t ... PASSED\n");
}
+void guest_mmio_writes(void)
+{
+ uint64_t ret;
+
+ ret = tdg_vp_vmcall_ve_request_mmio_write(TDX_MMIO_TEST_ADDR, 1, 0x12);
+ if (ret)
+ tdx_test_fatal(ret);
+
+ ret = tdg_vp_vmcall_ve_request_mmio_write(TDX_MMIO_TEST_ADDR, 2, 0x1234);
+ if (ret)
+ tdx_test_fatal(ret);
+
+ ret = tdg_vp_vmcall_ve_request_mmio_write(TDX_MMIO_TEST_ADDR, 4, 0x12345678);
+ if (ret)
+ tdx_test_fatal(ret);
+
+ ret = tdg_vp_vmcall_ve_request_mmio_write(TDX_MMIO_TEST_ADDR, 8, 0x1234567890ABCDEF);
+ if (ret)
+ tdx_test_fatal(ret);
+
+ // Write across page boundary.
+ ret = tdg_vp_vmcall_ve_request_mmio_write(PAGE_SIZE - 1, 8, 0);
+ if (ret)
+ tdx_test_fatal(ret);
+
+ tdx_test_success();
+}
+
+/*
+ * Varifies guest MMIO writes.
+ */
+void verify_mmio_writes(void)
+{
+ struct kvm_vm *vm;
+ struct kvm_vcpu *vcpu;
+
+ uint8_t byte_1;
+ uint16_t byte_2;
+ uint32_t byte_4;
+ uint64_t byte_8;
+
+ vm = td_create();
+ td_initialize(vm, VM_MEM_SRC_ANONYMOUS, 0);
+ vcpu = td_vcpu_add(vm, 0, guest_mmio_writes);
+ td_finalize(vm);
+
+ printf("Verifying TD MMIO writes:\n");
+
+ 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_WRITE);
+ byte_1 = *(uint8_t *)(vcpu->run->mmio.data);
+
+ 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_WRITE);
+ byte_2 = *(uint16_t *)(vcpu->run->mmio.data);
+
+ 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_WRITE);
+ byte_4 = *(uint32_t *)(vcpu->run->mmio.data);
+
+ 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_WRITE);
+ byte_8 = *(uint64_t *)(vcpu->run->mmio.data);
+
+ ASSERT_EQ(byte_1, 0x12);
+ ASSERT_EQ(byte_2, 0x1234);
+ ASSERT_EQ(byte_4, 0x12345678);
+ ASSERT_EQ(byte_8, 0x1234567890ABCDEF);
+
+ vcpu_run(vcpu);
+ ASSERT_EQ(vcpu->run->exit_reason, KVM_EXIT_SYSTEM_EVENT);
+ ASSERT_EQ(vcpu->run->system_event.data[1], TDG_VP_VMCALL_INVALID_OPERAND);
+
+ 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);
@@ -912,6 +996,7 @@ int main(int argc, char **argv)
run_in_new_process(&verify_guest_msr_reads);
run_in_new_process(&verify_guest_hlt);
run_in_new_process(&verify_mmio_reads);
+ run_in_new_process(&verify_mmio_writes);
return 0;
}
--
2.39.0.246.g2a6d74b583-goog
From: Roger Wang <[email protected]>
Adds a test for TDG.VP.INFO
Signed-off-by: Roger Wang <[email protected]>
Signed-off-by: Sagi Shahar <[email protected]>
Signed-off-by: Ackerley Tng <[email protected]>
---
Changes RFCv2 -> RFCv3
+ Use KVM_CAP_MAX_VCPUS to set max_vcpus, check that it was passed to
the TDX module by reading it back
---
.../selftests/kvm/include/x86_64/tdx/tdcall.h | 19 +++
.../selftests/kvm/include/x86_64/tdx/tdx.h | 6 +
.../selftests/kvm/lib/x86_64/tdx/tdcall.S | 68 ++++++++
.../selftests/kvm/lib/x86_64/tdx/tdx.c | 27 ++++
.../selftests/kvm/x86_64/tdx_vm_tests.c | 148 ++++++++++++++++++
5 files changed, 268 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 95fcdbd8404e9..a65ce8f3c109b 100644
--- a/tools/testing/selftests/kvm/include/x86_64/tdx/tdcall.h
+++ b/tools/testing/selftests/kvm/include/x86_64/tdx/tdcall.h
@@ -37,4 +37,23 @@ struct tdx_hypercall_args {
/* Used to request services from the VMM */
u64 __tdx_hypercall(struct tdx_hypercall_args *args, unsigned long flags);
+/*
+ * Used to gather the output registers values of the TDCALL and SEAMCALL
+ * instructions when requesting services from the TDX module.
+ *
+ * This is a software only structure and not part of the TDX module/VMM ABI.
+ */
+struct tdx_module_output {
+ u64 rcx;
+ u64 rdx;
+ u64 r8;
+ u64 r9;
+ u64 r10;
+ u64 r11;
+};
+
+/* Used to communicate with the TDX module */
+u64 __tdx_module_call(u64 fn, u64 rcx, u64 rdx, u64 r8, u64 r9,
+ struct tdx_module_output *out);
+
#endif // SELFTESTS_TDX_TDCALL_H
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 e746372206a25..ffab2c3ca312b 100644
--- a/tools/testing/selftests/kvm/include/x86_64/tdx/tdx.h
+++ b/tools/testing/selftests/kvm/include/x86_64/tdx/tdx.h
@@ -4,6 +4,8 @@
#include <stdint.h>
+#define TDG_VP_INFO 1
+
#define TDG_VP_VMCALL_GET_TD_VM_CALL_INFO 0x10000
#define TDG_VP_VMCALL_REPORT_FATAL_ERROR 0x10003
@@ -31,4 +33,8 @@ uint64_t tdg_vp_vmcall_instruction_cpuid(uint32_t eax, uint32_t ecx,
uint32_t *ret_eax, uint32_t *ret_ebx,
uint32_t *ret_ecx, uint32_t *ret_edx);
+uint64_t tdg_vp_info(uint64_t *rcx, uint64_t *rdx,
+ uint64_t *r8, uint64_t *r9,
+ uint64_t *r10, uint64_t *r11);
+
#endif // SELFTEST_TDX_TDX_H
diff --git a/tools/testing/selftests/kvm/lib/x86_64/tdx/tdcall.S b/tools/testing/selftests/kvm/lib/x86_64/tdx/tdcall.S
index df9c1ed4bb2d1..601d715314434 100644
--- a/tools/testing/selftests/kvm/lib/x86_64/tdx/tdcall.S
+++ b/tools/testing/selftests/kvm/lib/x86_64/tdx/tdcall.S
@@ -86,5 +86,73 @@ __tdx_hypercall:
pop %rbp
ret
+#define TDX_MODULE_rcx 0 /* offsetof(struct tdx_module_output, rcx) */
+#define TDX_MODULE_rdx 8 /* offsetof(struct tdx_module_output, rdx) */
+#define TDX_MODULE_r8 16 /* offsetof(struct tdx_module_output, r8) */
+#define TDX_MODULE_r9 24 /* offsetof(struct tdx_module_output, r9) */
+#define TDX_MODULE_r10 32 /* offsetof(struct tdx_module_output, r10) */
+#define TDX_MODULE_r11 40 /* offsetof(struct tdx_module_output, r11) */
+
+.globl __tdx_module_call
+.type __tdx_module_call, @function
+__tdx_module_call:
+ /* Set up stack frame */
+ push %rbp
+ movq %rsp, %rbp
+
+ /* Callee-saved, so preserve it */
+ push %r12
+
+ /*
+ * Push output pointer to stack.
+ * After the operation, it will be fetched into R12 register.
+ */
+ push %r9
+
+ /* Mangle function call ABI into TDCALL/SEAMCALL ABI: */
+ /* Move Leaf ID to RAX */
+ mov %rdi, %rax
+ /* Move input 4 to R9 */
+ mov %r8, %r9
+ /* Move input 3 to R8 */
+ mov %rcx, %r8
+ /* Move input 1 to RCX */
+ mov %rsi, %rcx
+ /* Leave input param 2 in RDX */
+
+ tdcall
+
+ /*
+ * Fetch output pointer from stack to R12 (It is used
+ * as temporary storage)
+ */
+ pop %r12
+
+ /*
+ * Since this macro can be invoked with NULL as an output pointer,
+ * check if caller provided an output struct before storing output
+ * registers.
+ *
+ * Update output registers, even if the call failed (RAX != 0).
+ * Other registers may contain details of the failure.
+ */
+ test %r12, %r12
+ jz .Lno_output_struct
+
+ /* Copy result registers to output struct: */
+ movq %rcx, TDX_MODULE_rcx(%r12)
+ movq %rdx, TDX_MODULE_rdx(%r12)
+ movq %r8, TDX_MODULE_r8(%r12)
+ movq %r9, TDX_MODULE_r9(%r12)
+ movq %r10, TDX_MODULE_r10(%r12)
+ movq %r11, TDX_MODULE_r11(%r12)
+
+.Lno_output_struct:
+ /* Restore the state of R12 register */
+ pop %r12
+
+ pop %rbp
+ ret
+
/* Disable executable stack */
.section .note.GNU-stack,"",%progbits
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 d9d60dd58dfdd..a280136634d3b 100644
--- a/tools/testing/selftests/kvm/lib/x86_64/tdx/tdx.c
+++ b/tools/testing/selftests/kvm/lib/x86_64/tdx/tdx.c
@@ -162,3 +162,30 @@ uint64_t tdg_vp_vmcall_instruction_cpuid(uint32_t eax, uint32_t ecx,
return ret;
}
+
+uint64_t tdg_vp_info(uint64_t *rcx, uint64_t *rdx,
+ uint64_t *r8, uint64_t *r9,
+ uint64_t *r10, uint64_t *r11)
+{
+ uint64_t ret;
+ struct tdx_module_output out;
+
+ memset(&out, 0, sizeof(struct tdx_module_output));
+
+ ret = __tdx_module_call(TDG_VP_INFO, 0, 0, 0, 0, &out);
+
+ if (rcx)
+ *rcx = out.rcx;
+ if (rdx)
+ *rdx = out.rdx;
+ if (r8)
+ *r8 = out.r8;
+ if (r9)
+ *r9 = out.r9;
+ if (r10)
+ *r10 = out.r10;
+ if (r11)
+ *r11 = out.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 4c338206dbaf2..e2dba3b5ee63e 100644
--- a/tools/testing/selftests/kvm/x86_64/tdx_vm_tests.c
+++ b/tools/testing/selftests/kvm/x86_64/tdx_vm_tests.c
@@ -1153,6 +1153,153 @@ void verify_host_reading_private_mem(void)
printf("\t ... PASSED\n");
}
+/*
+ * Do a TDG.VP.INFO call from the guest
+ */
+void guest_tdcall_vp_info(void)
+{
+ uint64_t err;
+ uint64_t rcx, rdx, r8, r9, r10, r11;
+
+ err = tdg_vp_info(&rcx, &rdx, &r8, &r9, &r10, &r11);
+ if (err)
+ tdx_test_fatal(err);
+
+ /* return values to user space host */
+ err = tdx_test_report_64bit_to_user_space(rcx);
+ if (err)
+ tdx_test_fatal(err);
+
+ err = tdx_test_report_64bit_to_user_space(rdx);
+ if (err)
+ tdx_test_fatal(err);
+
+ err = tdx_test_report_64bit_to_user_space(r8);
+ if (err)
+ tdx_test_fatal(err);
+
+ err = tdx_test_report_64bit_to_user_space(r9);
+ if (err)
+ tdx_test_fatal(err);
+
+ err = tdx_test_report_64bit_to_user_space(r10);
+ if (err)
+ tdx_test_fatal(err);
+
+ err = tdx_test_report_64bit_to_user_space(r11);
+ if (err)
+ tdx_test_fatal(err);
+
+ tdx_test_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;
+ const struct kvm_cpuid_entry2 *cpuid_entry;
+ int max_pa = -1;
+ int ret;
+
+ vm = td_create();
+
+ /* Set value for kvm->max_vcpus to be checked later */
+#define TEST_VP_INFO_MAX_VCPUS 75
+ ret = kvm_check_cap(KVM_CAP_MAX_VCPUS);
+ TEST_ASSERT(ret, "TDX: KVM_CAP_MAX_VCPUS is not supported!");
+ vm_enable_cap(vm, KVM_CAP_MAX_VCPUS, TEST_VP_INFO_MAX_VCPUS);
+
+#define TDX_TDPARAM_ATTR_SEPT_VE_DISABLE_BIT (1UL << 28)
+#define TDX_TDPARAM_ATTR_PKS_BIT (1UL << 30)
+ /* Setting attributes parameter used by TDH.MNG.INIT to 0x50000000 */
+ attributes = TDX_TDPARAM_ATTR_SEPT_VE_DISABLE_BIT |
+ TDX_TDPARAM_ATTR_PKS_BIT;
+
+ td_initialize(vm, VM_MEM_SRC_ANONYMOUS, attributes);
+
+ for (i = 0; i < num_vcpus; i++)
+ vcpus[i] = td_vcpu_add(vm, i, guest_tdcall_vp_info);
+
+ td_finalize(vm);
+
+ printf("Verifying TDG.VP.INFO call:\n");
+
+ /* Get KVM CPUIDs for reference */
+ cpuid_entry = kvm_get_supported_cpuid_entry(0x80000008);
+ 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);
+ TDX_TEST_CHECK_GUEST_FAILURE(vcpu);
+ rcx = tdx_test_read_64bit_report_from_guest(vcpu);
+
+ /* Wait for guest to report rdx value */
+ vcpu_run(vcpu);
+ TDX_TEST_CHECK_GUEST_FAILURE(vcpu);
+ rdx = tdx_test_read_64bit_report_from_guest(vcpu);
+
+ /* Wait for guest to report r8 value */
+ vcpu_run(vcpu);
+ TDX_TEST_CHECK_GUEST_FAILURE(vcpu);
+ r8 = tdx_test_read_64bit_report_from_guest(vcpu);
+
+ /* Wait for guest to report r9 value */
+ vcpu_run(vcpu);
+ TDX_TEST_CHECK_GUEST_FAILURE(vcpu);
+ r9 = tdx_test_read_64bit_report_from_guest(vcpu);
+
+ /* Wait for guest to report r10 value */
+ vcpu_run(vcpu);
+ TDX_TEST_CHECK_GUEST_FAILURE(vcpu);
+ r10 = tdx_test_read_64bit_report_from_guest(vcpu);
+
+ /* Wait for guest to report r11 value */
+ vcpu_run(vcpu);
+ TDX_TEST_CHECK_GUEST_FAILURE(vcpu);
+ r11 = tdx_test_read_64bit_report_from_guest(vcpu);
+
+ 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, TEST_VP_INFO_MAX_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);
+
+ TDX_TEST_CHECK_GUEST_FAILURE(vcpu);
+ TDX_TEST_ASSERT_SUCCESS(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)
{
setbuf(stdout, NULL);
@@ -1176,6 +1323,7 @@ int main(int argc, char **argv)
run_in_new_process(&verify_mmio_writes);
run_in_new_process(&verify_td_cpuid_tdcall);
run_in_new_process(&verify_host_reading_private_mem);
+ run_in_new_process(&verify_tdcall_vp_info);
return 0;
}
--
2.39.0.246.g2a6d74b583-goog
From: Sagi Shahar <[email protected]>
The test verifies reads and writes for MSR registers with different access
level.
Signed-off-by: Sagi Shahar <[email protected]>
Signed-off-by: Ackerley Tng <[email protected]>
---
Changes RFCv2 -> RFCv3
+ Fixed typo in MTTR->MTRR
---
.../selftests/kvm/include/x86_64/tdx/tdx.h | 4 +
.../selftests/kvm/lib/x86_64/tdx/tdx.c | 27 +++
.../selftests/kvm/x86_64/tdx_vm_tests.c | 217 ++++++++++++++++++
3 files changed, 248 insertions(+)
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 37ad16943e299..fbac1951cfe35 100644
--- a/tools/testing/selftests/kvm/include/x86_64/tdx/tdx.h
+++ b/tools/testing/selftests/kvm/include/x86_64/tdx/tdx.h
@@ -8,11 +8,15 @@
#define TDG_VP_VMCALL_REPORT_FATAL_ERROR 0x10003
#define TDG_VP_VMCALL_INSTRUCTION_IO 30
+#define TDG_VP_VMCALL_INSTRUCTION_RDMSR 31
+#define TDG_VP_VMCALL_INSTRUCTION_WRMSR 32
uint64_t tdg_vp_vmcall_instruction_io(uint64_t port, uint64_t size,
uint64_t write, uint64_t *data);
void tdg_vp_vmcall_report_fatal_error(uint64_t error_code, uint64_t data_gpa);
uint64_t tdg_vp_vmcall_get_td_vmcall_info(uint64_t *r11, uint64_t *r12,
uint64_t *r13, uint64_t *r14);
+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);
#endif // SELFTEST_TDX_TDX_H
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 7254d61515db2..43088d6f40b50 100644
--- a/tools/testing/selftests/kvm/lib/x86_64/tdx/tdx.c
+++ b/tools/testing/selftests/kvm/lib/x86_64/tdx/tdx.c
@@ -66,3 +66,30 @@ uint64_t tdg_vp_vmcall_get_td_vmcall_info(uint64_t *r11, uint64_t *r12,
return ret;
}
+
+uint64_t tdg_vp_vmcall_instruction_rdmsr(uint64_t index, uint64_t *ret_value)
+{
+ uint64_t ret;
+ struct tdx_hypercall_args args = {
+ .r11 = TDG_VP_VMCALL_INSTRUCTION_RDMSR,
+ .r12 = index,
+ };
+
+ ret = __tdx_hypercall(&args, TDX_HCALL_HAS_OUTPUT);
+
+ if (ret_value)
+ *ret_value = args.r11;
+
+ return ret;
+}
+
+uint64_t tdg_vp_vmcall_instruction_wrmsr(uint64_t index, uint64_t value)
+{
+ struct tdx_hypercall_args args = {
+ .r11 = TDG_VP_VMCALL_INSTRUCTION_WRMSR,
+ .r12 = index,
+ .r13 = value,
+ };
+
+ return __tdx_hypercall(&args, 0);
+}
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 71aa4e5907a05..65ca1ec2a6e82 100644
--- a/tools/testing/selftests/kvm/x86_64/tdx_vm_tests.c
+++ b/tools/testing/selftests/kvm/x86_64/tdx_vm_tests.c
@@ -514,6 +514,221 @@ void verify_guest_reads(void)
printf("\t ... PASSED\n");
}
+/*
+ * Define a filter which denies all MSR access except the following:
+ * MTTR_BASE_0: Allow read/write access
+ * MTTR_BASE_1: Allow read access
+ * MTTR_BASE_2: Allow write access
+ */
+static u64 tdx_msr_test_allow_bits = 0xFFFFFFFFFFFFFFFF;
+#define MTTR_BASE_0 (0x200)
+#define MTTR_BASE_1 (0x202)
+#define MTTR_BASE_2 (0x204)
+struct kvm_msr_filter tdx_msr_test_filter = {
+ .flags = KVM_MSR_FILTER_DEFAULT_DENY,
+ .ranges = {
+ {
+ .flags = KVM_MSR_FILTER_READ |
+ KVM_MSR_FILTER_WRITE,
+ .nmsrs = 1,
+ .base = MTTR_BASE_0,
+ .bitmap = (uint8_t *)&tdx_msr_test_allow_bits,
+ }, {
+ .flags = KVM_MSR_FILTER_READ,
+ .nmsrs = 1,
+ .base = MTTR_BASE_1,
+ .bitmap = (uint8_t *)&tdx_msr_test_allow_bits,
+ }, {
+ .flags = KVM_MSR_FILTER_WRITE,
+ .nmsrs = 1,
+ .base = MTTR_BASE_2,
+ .bitmap = (uint8_t *)&tdx_msr_test_allow_bits,
+ },
+ },
+};
+
+/*
+ * Verifies MSR read functionality.
+ */
+void guest_msr_read(void)
+{
+ uint64_t data;
+ uint64_t ret;
+
+ ret = tdg_vp_vmcall_instruction_rdmsr(MTTR_BASE_0, &data);
+ if (ret)
+ tdx_test_fatal(ret);
+
+ ret = tdx_test_report_64bit_to_user_space(data);
+ if (ret)
+ tdx_test_fatal(ret);
+
+ ret = tdg_vp_vmcall_instruction_rdmsr(MTTR_BASE_1, &data);
+ if (ret)
+ tdx_test_fatal(ret);
+
+ ret = tdx_test_report_64bit_to_user_space(data);
+ if (ret)
+ tdx_test_fatal(ret);
+
+ /* We expect this call to fail since MTTR_BASE_2 is write only */
+ ret = tdg_vp_vmcall_instruction_rdmsr(MTTR_BASE_2, &data);
+ if (ret) {
+ ret = tdx_test_report_64bit_to_user_space(ret);
+ if (ret)
+ tdx_test_fatal(ret);
+ } else {
+ tdx_test_fatal(-99);
+ }
+
+ tdx_test_success();
+}
+
+void verify_guest_msr_reads(void)
+{
+ struct kvm_vm *vm;
+ struct kvm_vcpu *vcpu;
+
+ uint64_t data;
+ int ret;
+
+ vm = td_create();
+ td_initialize(vm, VM_MEM_SRC_ANONYMOUS, 0);
+
+ /*
+ * Set explicit MSR filter map to control access to the MSR registers
+ * used in the test.
+ */
+ printf("\t ... Setting test MSR filter\n");
+ ret = kvm_check_cap(KVM_CAP_X86_USER_SPACE_MSR);
+ TEST_ASSERT(ret, "KVM_CAP_X86_USER_SPACE_MSR is unavailable");
+ vm_enable_cap(vm, KVM_CAP_X86_USER_SPACE_MSR, KVM_MSR_EXIT_REASON_FILTER);
+
+ ret = kvm_check_cap(KVM_CAP_X86_MSR_FILTER);
+ TEST_ASSERT(ret, "KVM_CAP_X86_MSR_FILTER is unavailable");
+
+ ret = ioctl(vm->fd, KVM_X86_SET_MSR_FILTER, &tdx_msr_test_filter);
+ TEST_ASSERT(ret == 0,
+ "KVM_X86_SET_MSR_FILTER failed, ret: %i errno: %i (%s)",
+ ret, errno, strerror(errno));
+
+ vcpu = td_vcpu_add(vm, 0, guest_msr_read);
+ td_finalize(vm);
+
+ printf("Verifying guest msr reads:\n");
+
+ printf("\t ... Setting test MTTR values\n");
+ /* valid values for mtrr type are 0, 1, 4, 5, 6 */
+ vcpu_set_msr(vcpu, MTTR_BASE_0, 4);
+ vcpu_set_msr(vcpu, MTTR_BASE_1, 5);
+ vcpu_set_msr(vcpu, MTTR_BASE_2, 6);
+
+ printf("\t ... Running guest\n");
+ vcpu_run(vcpu);
+ TDX_TEST_CHECK_GUEST_FAILURE(vcpu);
+ data = tdx_test_read_64bit_report_from_guest(vcpu);
+ ASSERT_EQ(data, 4);
+
+ vcpu_run(vcpu);
+ TDX_TEST_CHECK_GUEST_FAILURE(vcpu);
+ data = tdx_test_read_64bit_report_from_guest(vcpu);
+ ASSERT_EQ(data, 5);
+
+ vcpu_run(vcpu);
+ TDX_TEST_CHECK_GUEST_FAILURE(vcpu);
+ data = tdx_test_read_64bit_report_from_guest(vcpu);
+ ASSERT_EQ(data, TDG_VP_VMCALL_INVALID_OPERAND);
+
+ vcpu_run(vcpu);
+ TDX_TEST_ASSERT_SUCCESS(vcpu);
+
+ kvm_vm_free(vm);
+ printf("\t ... PASSED\n");
+}
+
+/*
+ * Verifies MSR write functionality.
+ */
+void guest_msr_write(void)
+{
+ uint64_t ret;
+
+ ret = tdg_vp_vmcall_instruction_wrmsr(MTTR_BASE_0, 4);
+ if (ret)
+ tdx_test_fatal(ret);
+
+ /* We expect this call to fail since MTTR_BASE_1 is read only */
+ ret = tdg_vp_vmcall_instruction_wrmsr(MTTR_BASE_1, 5);
+ if (ret) {
+ ret = tdx_test_report_64bit_to_user_space(ret);
+ if (ret)
+ tdx_test_fatal(ret);
+ } else {
+ tdx_test_fatal(-99);
+ }
+
+
+ ret = tdg_vp_vmcall_instruction_wrmsr(MTTR_BASE_2, 6);
+ if (ret)
+ tdx_test_fatal(ret);
+
+ tdx_test_success();
+}
+
+void verify_guest_msr_writes(void)
+{
+ struct kvm_vcpu *vcpu;
+ struct kvm_vm *vm;
+
+ uint64_t data;
+ int ret;
+
+ vm = td_create();
+ td_initialize(vm, VM_MEM_SRC_ANONYMOUS, 0);
+
+ /*
+ * Set explicit MSR filter map to control access to the MSR registers
+ * used in the test.
+ */
+ printf("\t ... Setting test MSR filter\n");
+ ret = kvm_check_cap(KVM_CAP_X86_USER_SPACE_MSR);
+ TEST_ASSERT(ret, "KVM_CAP_X86_USER_SPACE_MSR is unavailable");
+ vm_enable_cap(vm, KVM_CAP_X86_USER_SPACE_MSR, KVM_MSR_EXIT_REASON_FILTER);
+
+ ret = kvm_check_cap(KVM_CAP_X86_MSR_FILTER);
+ TEST_ASSERT(ret, "KVM_CAP_X86_MSR_FILTER is unavailable");
+
+ ret = ioctl(vm->fd, KVM_X86_SET_MSR_FILTER, &tdx_msr_test_filter);
+ TEST_ASSERT(ret == 0,
+ "KVM_X86_SET_MSR_FILTER failed, ret: %i errno: %i (%s)",
+ ret, errno, strerror(errno));
+
+ vcpu = td_vcpu_add(vm, 0, guest_msr_write);
+ td_finalize(vm);
+
+ printf("Verifying guest msr writes:\n");
+
+ printf("\t ... Running guest\n");
+ /* Only the write to MTTR_BASE_1 should trigger an exit */
+ vcpu_run(vcpu);
+ TDX_TEST_CHECK_GUEST_FAILURE(vcpu);
+ data = tdx_test_read_64bit_report_from_guest(vcpu);
+ ASSERT_EQ(data, TDG_VP_VMCALL_INVALID_OPERAND);
+
+ vcpu_run(vcpu);
+ TDX_TEST_ASSERT_SUCCESS(vcpu);
+
+ printf("\t ... Verifying MTTR values writen by guest\n");
+
+ ASSERT_EQ(vcpu_get_msr(vcpu, MTTR_BASE_0), 4);
+ ASSERT_EQ(vcpu_get_msr(vcpu, MTTR_BASE_1), 0);
+ ASSERT_EQ(vcpu_get_msr(vcpu, MTTR_BASE_2), 6);
+
+ kvm_vm_free(vm);
+ printf("\t ... PASSED\n");
+}
+
+
int main(int argc, char **argv)
{
setbuf(stdout, NULL);
@@ -530,6 +745,8 @@ int main(int argc, char **argv)
run_in_new_process(&verify_get_td_vmcall_info);
run_in_new_process(&verify_guest_writes);
run_in_new_process(&verify_guest_reads);
+ run_in_new_process(&verify_guest_msr_writes);
+ run_in_new_process(&verify_guest_msr_reads);
return 0;
}
--
2.39.0.246.g2a6d74b583-goog
With this, vm_userspace_mem_region_add() can use restricted memory to
back guest memory.
Signed-off-by: Ackerley Tng <[email protected]>
---
.../selftests/kvm/include/kvm_util_base.h | 7 ++-
.../testing/selftests/kvm/include/test_util.h | 2 +
tools/testing/selftests/kvm/lib/kvm_util.c | 48 ++++++++++++++++---
tools/testing/selftests/kvm/lib/test_util.c | 7 +++
4 files changed, 55 insertions(+), 9 deletions(-)
diff --git a/tools/testing/selftests/kvm/include/kvm_util_base.h b/tools/testing/selftests/kvm/include/kvm_util_base.h
index 30453e2de8396..950fd337898e1 100644
--- a/tools/testing/selftests/kvm/include/kvm_util_base.h
+++ b/tools/testing/selftests/kvm/include/kvm_util_base.h
@@ -33,7 +33,10 @@ typedef uint64_t vm_paddr_t; /* Virtual Machine (Guest) physical address */
typedef uint64_t vm_vaddr_t; /* Virtual Machine (Guest) virtual address */
struct userspace_mem_region {
- struct kvm_userspace_memory_region region;
+ union {
+ struct kvm_userspace_memory_region region;
+ struct kvm_userspace_memory_region_ext region_ext;
+ };
struct sparsebit *unused_phy_pages;
struct sparsebit *protected_phy_pages;
int fd;
@@ -214,7 +217,7 @@ static inline bool kvm_has_cap(long cap)
#define kvm_do_ioctl(fd, cmd, arg) \
({ \
- static_assert(!_IOC_SIZE(cmd) || sizeof(*arg) == _IOC_SIZE(cmd), ""); \
+ static_assert(!_IOC_SIZE(cmd) || sizeof(*arg) >= _IOC_SIZE(cmd), ""); \
ioctl(fd, cmd, arg); \
})
diff --git a/tools/testing/selftests/kvm/include/test_util.h b/tools/testing/selftests/kvm/include/test_util.h
index befc754ce9b3b..01456a78b3a2e 100644
--- a/tools/testing/selftests/kvm/include/test_util.h
+++ b/tools/testing/selftests/kvm/include/test_util.h
@@ -94,6 +94,7 @@ enum vm_mem_backing_src_type {
VM_MEM_SRC_ANONYMOUS_HUGETLB_1GB,
VM_MEM_SRC_ANONYMOUS_HUGETLB_2GB,
VM_MEM_SRC_ANONYMOUS_HUGETLB_16GB,
+ VM_MEM_SRC_ANONYMOUS_AND_RESTRICTED_MEMFD,
VM_MEM_SRC_SHMEM,
VM_MEM_SRC_SHARED_HUGETLB,
NUM_SRC_TYPES,
@@ -104,6 +105,7 @@ enum vm_mem_backing_src_type {
struct vm_mem_backing_src_alias {
const char *name;
uint32_t flag;
+ bool need_restricted_memfd;
};
#define MIN_RUN_DELAY_NS 200000UL
diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c
index 6673be2f49c31..4e5928fa71c44 100644
--- a/tools/testing/selftests/kvm/lib/kvm_util.c
+++ b/tools/testing/selftests/kvm/lib/kvm_util.c
@@ -15,7 +15,6 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
-#include <linux/kernel.h>
#define KVM_UTIL_MIN_PFN 2
@@ -799,6 +798,27 @@ void vm_set_user_memory_region(struct kvm_vm *vm, uint32_t slot, uint32_t flags,
errno, strerror(errno));
}
+/**
+ * Initialize memory in restricted_fd with size @memory_region_size and return
+ * the fd.
+ *
+ * Errors out if there's any error
+ */
+static int initialize_restricted_memfd(uint64_t memory_region_size)
+{
+ int ret;
+ int mfd = -1;
+
+ mfd = syscall(__NR_memfd_restricted, 0);
+ TEST_ASSERT(mfd != -1, "Failed to create private memfd");
+ ret = ftruncate(mfd, memory_region_size);
+ TEST_ASSERT(ret != -1, "Failed to resize memfd %d to %lx", mfd, memory_region_size);
+ ret = fallocate(mfd, 0, 0, memory_region_size);
+ TEST_ASSERT(ret != -1, "Failed to allocate %lx bytes in memfd %d", memory_region_size, mfd);
+
+ return mfd;
+}
+
/*
* VM Userspace Memory Region Add
*
@@ -830,6 +850,7 @@ void vm_userspace_mem_region_add(struct kvm_vm *vm,
struct userspace_mem_region *region;
size_t backing_src_pagesz = get_backing_src_pagesz(src_type);
size_t alignment;
+ int restricted_memfd = -1;
TEST_ASSERT(vm_adjust_num_guest_pages(vm->mode, npages) == npages,
"Number of guest pages is not compatible with the host. "
@@ -927,14 +948,24 @@ void vm_userspace_mem_region_add(struct kvm_vm *vm,
/* As needed perform madvise */
if ((src_type == VM_MEM_SRC_ANONYMOUS ||
- src_type == VM_MEM_SRC_ANONYMOUS_THP) && thp_configured()) {
- ret = madvise(region->host_mem, npages * vm->page_size,
- src_type == VM_MEM_SRC_ANONYMOUS ? MADV_NOHUGEPAGE : MADV_HUGEPAGE);
+ src_type == VM_MEM_SRC_ANONYMOUS_THP ||
+ src_type == VM_MEM_SRC_ANONYMOUS_AND_RESTRICTED_MEMFD) && thp_configured()) {
+ int advice = src_type == VM_MEM_SRC_ANONYMOUS_THP
+ ? MADV_HUGEPAGE
+ : MADV_NOHUGEPAGE;
+ ret = madvise(region->host_mem, npages * vm->page_size, advice);
TEST_ASSERT(ret == 0, "madvise failed, addr: %p length: 0x%lx src_type: %s",
region->host_mem, npages * vm->page_size,
vm_mem_backing_src_alias(src_type)->name);
}
+ if (vm_mem_backing_src_alias(src_type)->need_restricted_memfd) {
+ restricted_memfd = initialize_restricted_memfd(npages * vm->page_size);
+ TEST_ASSERT(restricted_memfd != -1,
+ "Failed to create restricted memfd");
+ flags |= KVM_MEM_PRIVATE;
+ }
+
region->unused_phy_pages = sparsebit_alloc();
region->protected_phy_pages = sparsebit_alloc();
sparsebit_set_num(region->unused_phy_pages,
@@ -944,13 +975,16 @@ void vm_userspace_mem_region_add(struct kvm_vm *vm,
region->region.guest_phys_addr = guest_paddr;
region->region.memory_size = npages * vm->page_size;
region->region.userspace_addr = (uintptr_t) region->host_mem;
- ret = __vm_ioctl(vm, KVM_SET_USER_MEMORY_REGION, ®ion->region);
+ region->region_ext.restricted_fd = restricted_memfd;
+ region->region_ext.restricted_offset = 0;
+ ret = __vm_ioctl(vm, KVM_SET_USER_MEMORY_REGION, ®ion->region_ext);
TEST_ASSERT(ret == 0, "KVM_SET_USER_MEMORY_REGION IOCTL failed,\n"
" rc: %i errno: %i\n"
" slot: %u flags: 0x%x\n"
- " guest_phys_addr: 0x%lx size: 0x%lx",
+ " guest_phys_addr: 0x%lx size: 0x%lx restricted_fd: %d",
ret, errno, slot, flags,
- guest_paddr, (uint64_t) region->region.memory_size);
+ guest_paddr, (uint64_t) region->region.memory_size,
+ restricted_memfd);
/* Add to quick lookup data structures */
vm_userspace_mem_region_gpa_insert(&vm->regions.gpa_tree, region);
diff --git a/tools/testing/selftests/kvm/lib/test_util.c b/tools/testing/selftests/kvm/lib/test_util.c
index 6d23878bbfe1a..2d53e55d13565 100644
--- a/tools/testing/selftests/kvm/lib/test_util.c
+++ b/tools/testing/selftests/kvm/lib/test_util.c
@@ -8,6 +8,7 @@
#include <assert.h>
#include <ctype.h>
#include <limits.h>
+#include <stdbool.h>
#include <stdlib.h>
#include <time.h>
#include <sys/stat.h>
@@ -254,6 +255,11 @@ const struct vm_mem_backing_src_alias *vm_mem_backing_src_alias(uint32_t i)
*/
.flag = MAP_SHARED,
},
+ [VM_MEM_SRC_ANONYMOUS_AND_RESTRICTED_MEMFD] = {
+ .name = "anonymous_and_restricted_memfd",
+ .flag = ANON_FLAGS,
+ .need_restricted_memfd = true,
+ },
};
_Static_assert(ARRAY_SIZE(aliases) == NUM_SRC_TYPES,
"Missing new backing src types?");
@@ -272,6 +278,7 @@ size_t get_backing_src_pagesz(uint32_t i)
switch (i) {
case VM_MEM_SRC_ANONYMOUS:
case VM_MEM_SRC_SHMEM:
+ case VM_MEM_SRC_ANONYMOUS_AND_RESTRICTED_MEMFD:
return getpagesize();
case VM_MEM_SRC_ANONYMOUS_THP:
return get_trans_hugepagesz();
--
2.39.0.246.g2a6d74b583-goog
Some SSE instructions assume a 16-byte aligned stack, and GCC compiles
assuming the stack is aligned:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=40838. This combination
results in a #GP in guests.
Adding this compiler flag will generate an alternate prologue and
epilogue to realign the runtime stack, which makes selftest code
slower and bigger, but this is okay since we do not need selftest code
to be extremely performant.
Similar issue discussed at
https://lore.kernel.org/all/CAGtprH9yKvuaF5yruh3BupQe4BxDGiBQk3ExtY2m39yP-tppsg@mail.gmail.com/
Signed-off-by: Ackerley Tng <[email protected]>
---
tools/testing/selftests/kvm/Makefile | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
index 317927d9c55bd..5f9cc1e6ee67e 100644
--- a/tools/testing/selftests/kvm/Makefile
+++ b/tools/testing/selftests/kvm/Makefile
@@ -205,7 +205,7 @@ LINUX_TOOL_ARCH_INCLUDE = $(top_srcdir)/tools/arch/x86/include
else
LINUX_TOOL_ARCH_INCLUDE = $(top_srcdir)/tools/arch/$(ARCH)/include
endif
-CFLAGS += -Wall -Wstrict-prototypes -Wuninitialized -O2 -g -std=gnu99 \
+CFLAGS += -mstackrealign -Wall -Wstrict-prototypes -Wuninitialized -O2 -g -std=gnu99 \
-fno-stack-protector -fno-PIE -I$(LINUX_TOOL_INCLUDE) \
-I$(LINUX_TOOL_ARCH_INCLUDE) -I$(LINUX_HDR_PATH) -Iinclude \
-I$(<D) -Iinclude/$(UNAME_M) -I ../rseq -I.. $(EXTRA_CFLAGS) \
--
2.39.0.246.g2a6d74b583-goog
From: Sagi Shahar <[email protected]>
The test checks report_fatal_error functionality.
Signed-off-by: Sagi Shahar <[email protected]>
Signed-off-by: Ackerley Tng <[email protected]>
---
.../selftests/kvm/include/x86_64/tdx/tdx.h | 3 ++
.../kvm/include/x86_64/tdx/test_util.h | 19 ++++++++
.../selftests/kvm/lib/x86_64/tdx/tdx.c | 18 ++++++++
.../selftests/kvm/lib/x86_64/tdx/test_util.c | 10 +++++
.../selftests/kvm/x86_64/tdx_vm_tests.c | 44 +++++++++++++++++++
5 files changed, 94 insertions(+)
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 a7161efe4ee2e..28959bdb07628 100644
--- a/tools/testing/selftests/kvm/include/x86_64/tdx/tdx.h
+++ b/tools/testing/selftests/kvm/include/x86_64/tdx/tdx.h
@@ -4,9 +4,12 @@
#include <stdint.h>
+#define TDG_VP_VMCALL_REPORT_FATAL_ERROR 0x10003
+
#define TDG_VP_VMCALL_INSTRUCTION_IO 30
uint64_t tdg_vp_vmcall_instruction_io(uint64_t port, uint64_t size,
uint64_t write, uint64_t *data);
+void tdg_vp_vmcall_report_fatal_error(uint64_t error_code, uint64_t data_gpa);
#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 b570b6d978ff1..6d69921136bd2 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
@@ -49,4 +49,23 @@ bool is_tdx_enabled(void);
*/
void tdx_test_success(void);
+/**
+ * Report an error with @error_code to userspace.
+ *
+ * Return value from tdg_vp_vmcall_report_fatal_error is ignored since execution
+ * is not expected to continue beyond this point.
+ */
+void tdx_test_fatal(uint64_t error_code);
+
+/**
+ * Report an error with @error_code to userspace.
+ *
+ * @data_gpa may point to an optional shared guest memory holding the error
+ * string.
+ *
+ * Return value from tdg_vp_vmcall_report_fatal_error is ignored since execution
+ * is not expected to continue beyond this point.
+ */
+void tdx_test_fatal_with_data(uint64_t error_code, uint64_t data_gpa);
+
#endif // SELFTEST_TDX_TEST_UTIL_H
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 c2414523487a7..e8c399f2277cf 100644
--- a/tools/testing/selftests/kvm/lib/x86_64/tdx/tdx.c
+++ b/tools/testing/selftests/kvm/lib/x86_64/tdx/tdx.c
@@ -1,5 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
+#include <string.h>
+
#include "tdx/tdcall.h"
#include "tdx/tdx.h"
@@ -25,3 +27,19 @@ uint64_t tdg_vp_vmcall_instruction_io(uint64_t port, uint64_t size,
return ret;
}
+
+void tdg_vp_vmcall_report_fatal_error(uint64_t error_code, uint64_t data_gpa)
+{
+ struct tdx_hypercall_args args;
+
+ memset(&args, 0, sizeof(struct tdx_hypercall_args));
+
+ if (data_gpa)
+ error_code |= 0x8000000000000000;
+
+ args.r11 = TDG_VP_VMCALL_REPORT_FATAL_ERROR;
+ args.r12 = error_code;
+ args.r13 = data_gpa;
+
+ __tdx_hypercall(&args, 0);
+}
diff --git a/tools/testing/selftests/kvm/lib/x86_64/tdx/test_util.c b/tools/testing/selftests/kvm/lib/x86_64/tdx/test_util.c
index 6905d0ca38774..7f3cd8089cea3 100644
--- a/tools/testing/selftests/kvm/lib/x86_64/tdx/test_util.c
+++ b/tools/testing/selftests/kvm/lib/x86_64/tdx/test_util.c
@@ -32,3 +32,13 @@ void tdx_test_success(void)
TDX_TEST_SUCCESS_SIZE,
TDG_VP_VMCALL_INSTRUCTION_IO_WRITE, &code);
}
+
+void tdx_test_fatal_with_data(uint64_t error_code, uint64_t data_gpa)
+{
+ tdg_vp_vmcall_report_fatal_error(error_code, data_gpa);
+}
+
+void tdx_test_fatal(uint64_t error_code)
+{
+ tdx_test_fatal_with_data(error_code, 0);
+}
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 a18d1c9d60264..627d60b573bb6 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 <signal.h>
#include "kvm_util_base.h"
+#include "tdx/tdx.h"
#include "tdx/tdx_util.h"
#include "tdx/test_util.h"
#include "test_util.h"
@@ -30,6 +31,48 @@ void verify_td_lifecycle(void)
printf("\t ... PASSED\n");
}
+void guest_code_report_fatal_error(void)
+{
+ uint64_t err;
+
+ /*
+ * Note: err should follow the GHCI spec definition:
+ * bits 31:0 should be set to 0.
+ * bits 62:32 are used for TD-specific extended error code.
+ * bit 63 is used to mark additional information in shared memory.
+ */
+ err = 0x0BAAAAAD00000000;
+ if (err)
+ tdx_test_fatal(err);
+
+ tdx_test_success();
+}
+void verify_report_fatal_error(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_code_report_fatal_error);
+ td_finalize(vm);
+
+ printf("Verifying report_fatal_error:\n");
+
+ vcpu_run(vcpu);
+ ASSERT_EQ(vcpu->run->exit_reason, KVM_EXIT_SYSTEM_EVENT);
+ ASSERT_EQ(vcpu->run->system_event.ndata, 3);
+ ASSERT_EQ(vcpu->run->system_event.data[0], TDG_VP_VMCALL_REPORT_FATAL_ERROR);
+ ASSERT_EQ(vcpu->run->system_event.data[1], 0x0BAAAAAD00000000);
+ ASSERT_EQ(vcpu->run->system_event.data[2], 0);
+
+ 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);
@@ -40,6 +83,7 @@ int main(int argc, char **argv)
}
run_in_new_process(&verify_td_lifecycle);
+ run_in_new_process(&verify_report_fatal_error);
return 0;
}
--
2.39.0.246.g2a6d74b583-goog
From: Sagi Shahar <[email protected]>
The test verifies MMIO reads of various sizes from the host to the guest.
Signed-off-by: Sagi Shahar <[email protected]>
Signed-off-by: Ackerley Tng <[email protected]>
---
.../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 b5e94b7c48fa5..95fcdbd8404e9 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 7eba9d80b3681..8dd6a65485260 100644
--- a/tools/testing/selftests/kvm/include/x86_64/tdx/tdx.h
+++ b/tools/testing/selftests/kvm/include/x86_64/tdx/tdx.h
@@ -11,6 +11,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
uint64_t tdg_vp_vmcall_instruction_io(uint64_t port, uint64_t size,
@@ -21,5 +22,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 8a9b6a1bec3eb..af412b7646049 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 1af0626c2a4ad..dcdacf08bcd60 100644
--- a/tools/testing/selftests/kvm/lib/x86_64/tdx/tdx.c
+++ b/tools/testing/selftests/kvm/lib/x86_64/tdx/tdx.c
@@ -103,3 +103,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 346c1e07af9c0..88f0429db0176 100644
--- a/tools/testing/selftests/kvm/x86_64/tdx_vm_tests.c
+++ b/tools/testing/selftests/kvm/x86_64/tdx_vm_tests.c
@@ -806,6 +806,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.
+ */
+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");
+
+ 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;
+
+ 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;
+
+ 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;
+
+ 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;
+
+ vcpu_run(vcpu);
+ ASSERT_EQ(vcpu->run->exit_reason, KVM_EXIT_SYSTEM_EVENT);
+ ASSERT_EQ(vcpu->run->system_event.data[1], TDG_VP_VMCALL_INVALID_OPERAND);
+
+ 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);
@@ -825,6 +911,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;
}
--
2.39.0.246.g2a6d74b583-goog
From: Ryan Afranji <[email protected]>
Adds a test that sets up shared memory between the host and guest.
Signed-off-by: Ryan Afranji <[email protected]>
Signed-off-by: Sagi Shahar <[email protected]>
Signed-off-by: Ackerley Tng <[email protected]>
---
tools/testing/selftests/kvm/.gitignore | 1 +
tools/testing/selftests/kvm/Makefile | 1 +
.../selftests/kvm/include/x86_64/tdx/tdx.h | 2 +
.../kvm/include/x86_64/tdx/tdx_util.h | 3 +
.../selftests/kvm/lib/x86_64/tdx/tdx.c | 16 ++
.../selftests/kvm/lib/x86_64/tdx/tdx_util.c | 30 ++++
.../kvm/x86_64/tdx_shared_mem_test.c | 137 ++++++++++++++++++
7 files changed, 190 insertions(+)
create mode 100644 tools/testing/selftests/kvm/x86_64/tdx_shared_mem_test.c
diff --git a/tools/testing/selftests/kvm/.gitignore b/tools/testing/selftests/kvm/.gitignore
index 370d6430b32b4..e1663b0f809b4 100644
--- a/tools/testing/selftests/kvm/.gitignore
+++ b/tools/testing/selftests/kvm/.gitignore
@@ -66,6 +66,7 @@
/x86_64/vmx_pmu_caps_test
/x86_64/triple_fault_event_test
/x86_64/tdx_vm_tests
+/x86_64/tdx_shared_mem_test
/access_tracking_perf_test
/demand_paging_test
/dirty_log_test
diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
index 9f289322a4933..27e9148212fa5 100644
--- a/tools/testing/selftests/kvm/Makefile
+++ b/tools/testing/selftests/kvm/Makefile
@@ -152,6 +152,7 @@ TEST_GEN_PROGS_x86_64 += steal_time
TEST_GEN_PROGS_x86_64 += kvm_binary_stats_test
TEST_GEN_PROGS_x86_64 += system_counter_offset_test
TEST_GEN_PROGS_x86_64 += x86_64/tdx_vm_tests
+TEST_GEN_PROGS_x86_64 += x86_64/tdx_shared_mem_test
# Compiled outputs used by test targets
TEST_GEN_PROGS_EXTENDED_x86_64 += x86_64/nx_huge_pages_test
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 ffab2c3ca312b..857a297e51ac6 100644
--- a/tools/testing/selftests/kvm/include/x86_64/tdx/tdx.h
+++ b/tools/testing/selftests/kvm/include/x86_64/tdx/tdx.h
@@ -7,6 +7,7 @@
#define TDG_VP_INFO 1
#define TDG_VP_VMCALL_GET_TD_VM_CALL_INFO 0x10000
+#define TDG_VP_VMCALL_MAP_GPA 0x10001
#define TDG_VP_VMCALL_REPORT_FATAL_ERROR 0x10003
#define TDG_VP_VMCALL_INSTRUCTION_CPUID 10
@@ -36,5 +37,6 @@ uint64_t tdg_vp_vmcall_instruction_cpuid(uint32_t eax, uint32_t ecx,
uint64_t tdg_vp_info(uint64_t *rcx, uint64_t *rdx,
uint64_t *r8, uint64_t *r9,
uint64_t *r10, uint64_t *r11);
+uint64_t tdg_vp_vmcall_map_gpa(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/tdx_util.h b/tools/testing/selftests/kvm/include/x86_64/tdx/tdx_util.h
index 274b245f200bf..58374453b4b7e 100644
--- a/tools/testing/selftests/kvm/include/x86_64/tdx/tdx_util.h
+++ b/tools/testing/selftests/kvm/include/x86_64/tdx/tdx_util.h
@@ -13,4 +13,7 @@ void td_initialize(struct kvm_vm *vm, enum vm_mem_backing_src_type src_type,
uint64_t attributes);
void td_finalize(struct kvm_vm *vm);
+void handle_memory_conversion(struct kvm_vm *vm, uint64_t gpa, uint64_t size,
+ bool shared_to_private);
+
#endif // SELFTESTS_TDX_KVM_UTIL_H
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 a280136634d3b..e0a39f29a0662 100644
--- a/tools/testing/selftests/kvm/lib/x86_64/tdx/tdx.c
+++ b/tools/testing/selftests/kvm/lib/x86_64/tdx/tdx.c
@@ -189,3 +189,19 @@ uint64_t tdg_vp_info(uint64_t *rcx, uint64_t *rdx,
return ret;
}
+
+uint64_t tdg_vp_vmcall_map_gpa(uint64_t address, uint64_t size, uint64_t *data_out)
+{
+ uint64_t ret;
+ struct tdx_hypercall_args args = {
+ .r11 = TDG_VP_VMCALL_MAP_GPA,
+ .r12 = address,
+ .r13 = size
+ };
+
+ ret = __tdx_hypercall(&args, TDX_HCALL_HAS_OUTPUT);
+
+ if (data_out)
+ *data_out = args.r11;
+ return ret;
+}
diff --git a/tools/testing/selftests/kvm/lib/x86_64/tdx/tdx_util.c b/tools/testing/selftests/kvm/lib/x86_64/tdx/tdx_util.c
index 2e9679d24a843..4d6615b97770a 100644
--- a/tools/testing/selftests/kvm/lib/x86_64/tdx/tdx_util.c
+++ b/tools/testing/selftests/kvm/lib/x86_64/tdx/tdx_util.c
@@ -505,3 +505,33 @@ void td_finalize(struct kvm_vm *vm)
tdx_td_finalizemr(vm);
}
+
+/**
+ * Handle conversion of memory with @size beginning @gpa for @vm. Set
+ * @shared_to_private to true for shared to private conversions and false
+ * otherwise.
+ *
+ * Since this is just for selftests, we will just keep both pieces of backing
+ * memory allocated and not deallocate/allocate memory; we'll just do the
+ * minimum of calling KVM_MEMORY_ENCRYPT_REG_REGION and
+ * KVM_MEMORY_ENCRYPT_UNREG_REGION.
+ */
+void handle_memory_conversion(struct kvm_vm *vm, uint64_t gpa, uint64_t size,
+ bool shared_to_private)
+{
+ struct kvm_enc_region range;
+ char *ioctl_string = shared_to_private
+ ? "KVM_MEMORY_ENCRYPT_REG_REGION"
+ : "KVM_MEMORY_ENCRYPT_UNREG_REGION";
+
+ range.addr = gpa;
+ range.size = size;
+
+ printf("\t ... calling %s ioctl with gpa=%#lx, size=%#lx\n",
+ ioctl_string, gpa, size);
+
+ if (shared_to_private)
+ vm_ioctl(vm, KVM_MEMORY_ENCRYPT_REG_REGION, &range);
+ else
+ vm_ioctl(vm, KVM_MEMORY_ENCRYPT_UNREG_REGION, &range);
+}
diff --git a/tools/testing/selftests/kvm/x86_64/tdx_shared_mem_test.c b/tools/testing/selftests/kvm/x86_64/tdx_shared_mem_test.c
new file mode 100644
index 0000000000000..eb4cf64ae83a8
--- /dev/null
+++ b/tools/testing/selftests/kvm/x86_64/tdx_shared_mem_test.c
@@ -0,0 +1,137 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/kvm.h>
+#include <stdint.h>
+
+#include "kvm_util_base.h"
+#include "processor.h"
+#include "tdx/tdcall.h"
+#include "tdx/tdx.h"
+#include "tdx/tdx_util.h"
+#include "tdx/test_util.h"
+#include "test_util.h"
+
+#define TDX_SHARED_MEM_TEST_PRIVATE_GVA (0x80000000)
+#define TDX_SHARED_MEM_TEST_VADDR_SHARED_MASK BIT_ULL(30)
+#define TDX_SHARED_MEM_TEST_SHARED_GVA \
+ (TDX_SHARED_MEM_TEST_PRIVATE_GVA | \
+ TDX_SHARED_MEM_TEST_VADDR_SHARED_MASK)
+
+#define TDX_SHARED_MEM_TEST_GUEST_WRITE_VALUE (0xcafecafe)
+#define TDX_SHARED_MEM_TEST_HOST_WRITE_VALUE (0xabcdabcd)
+
+#define TDX_SHARED_MEM_TEST_INFO_PORT 0x87
+
+/*
+ * Shared variables between guest and host
+ */
+static uint64_t test_mem_private_gpa;
+static uint64_t test_mem_shared_gpa;
+
+void guest_shared_mem(void)
+{
+ uint32_t *test_mem_shared_gva =
+ (uint32_t *)TDX_SHARED_MEM_TEST_SHARED_GVA;
+
+ uint64_t placeholder;
+ uint64_t ret;
+
+ /* Map gpa as shared */
+ ret = tdg_vp_vmcall_map_gpa(test_mem_shared_gpa, PAGE_SIZE,
+ &placeholder);
+ if (ret)
+ tdx_test_fatal_with_data(ret, __LINE__);
+
+ *test_mem_shared_gva = TDX_SHARED_MEM_TEST_GUEST_WRITE_VALUE;
+
+ /* Exit so host can read shared value */
+ ret = tdg_vp_vmcall_instruction_io(TDX_SHARED_MEM_TEST_INFO_PORT, 4,
+ TDG_VP_VMCALL_INSTRUCTION_IO_WRITE,
+ &placeholder);
+ if (ret)
+ tdx_test_fatal_with_data(ret, __LINE__);
+
+ /* Read value written by host and send it back out for verification */
+ ret = tdg_vp_vmcall_instruction_io(TDX_SHARED_MEM_TEST_INFO_PORT, 4,
+ TDG_VP_VMCALL_INSTRUCTION_IO_WRITE,
+ (uint64_t *)test_mem_shared_gva);
+ if (ret)
+ tdx_test_fatal_with_data(ret, __LINE__);
+}
+
+int verify_shared_mem(void)
+{
+ struct kvm_vm *vm;
+ struct kvm_vcpu *vcpu;
+
+ vm_vaddr_t test_mem_private_gva;
+ uint32_t *test_mem_hva;
+
+ vm = td_create();
+ td_initialize(vm, VM_MEM_SRC_ANONYMOUS, 0);
+ vcpu = td_vcpu_add(vm, 0, guest_shared_mem);
+
+ /*
+ * Set up shared memory page for testing by first allocating as private
+ * and then mapping the same GPA again as shared. This way, the TD does
+ * not have to remap its page tables at runtime.
+ */
+ test_mem_private_gva = vm_vaddr_alloc(vm, vm->page_size,
+ TDX_SHARED_MEM_TEST_PRIVATE_GVA);
+ ASSERT_EQ(test_mem_private_gva, TDX_SHARED_MEM_TEST_PRIVATE_GVA);
+
+ test_mem_hva = addr_gva2hva(vm, test_mem_private_gva);
+ TEST_ASSERT(test_mem_hva != NULL,
+ "Guest address not found in guest memory regions\n");
+
+ test_mem_private_gpa = addr_gva2gpa(vm, test_mem_private_gva);
+ virt_pg_map_shared(vm, TDX_SHARED_MEM_TEST_SHARED_GVA,
+ test_mem_private_gpa);
+
+ test_mem_shared_gpa = test_mem_private_gpa | BIT_ULL(vm->pa_bits - 1);
+ sync_global_to_guest(vm, test_mem_private_gpa);
+ sync_global_to_guest(vm, test_mem_shared_gpa);
+
+ td_finalize(vm);
+
+ printf("Verifying shared memory accesses for TDX\n");
+
+ /* Begin guest execution; guest writes to shared memory. */
+ printf("\t ... Starting guest execution\n");
+
+ /* Handle map gpa as shared */
+ /* TODO: MapGPA should exit to the host VMM, but now it doesn't */
+ // vcpu_run(vcpu);
+ // ASSERT_EQ(vcpu->run->exit_reason, KVM_EXIT_MEMORY_FAULT);
+ // handle_memory_conversion(vm, vcpu->run->memory.gpa, vcpu->run->memory.size,
+ // vcpu->run->memory.flags == KVM_MEMORY_EXIT_FLAG_PRIVATE);
+
+ vcpu_run(vcpu);
+ TDX_TEST_ASSERT_IO(vcpu, TDX_SHARED_MEM_TEST_INFO_PORT, 4,
+ TDG_VP_VMCALL_INSTRUCTION_IO_WRITE);
+ ASSERT_EQ(*test_mem_hva, TDX_SHARED_MEM_TEST_GUEST_WRITE_VALUE);
+
+ *test_mem_hva = TDX_SHARED_MEM_TEST_HOST_WRITE_VALUE;
+ vcpu_run(vcpu);
+ TDX_TEST_ASSERT_IO(vcpu, TDX_SHARED_MEM_TEST_INFO_PORT, 4,
+ TDG_VP_VMCALL_INSTRUCTION_IO_WRITE);
+ ASSERT_EQ(*(uint32_t *)((void *)vcpu->run + vcpu->run->io.data_offset),
+ TDX_SHARED_MEM_TEST_HOST_WRITE_VALUE);
+
+ printf("\t ... PASSED\n");
+
+ kvm_vm_free(vm);
+
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ if (!is_tdx_enabled()) {
+ printf("TDX is not supported by the KVM\n"
+ "Skipping the TDX tests.\n");
+ return 0;
+ }
+
+ return verify_shared_mem();
+}
--
2.39.0.246.g2a6d74b583-goog
This tests the use of restricted memory without explicit MapGPA calls.
Signed-off-by: Ackerley Tng <[email protected]>
---
.../selftests/kvm/x86_64/tdx_upm_test.c | 88 ++++++++++++++++---
1 file changed, 78 insertions(+), 10 deletions(-)
diff --git a/tools/testing/selftests/kvm/x86_64/tdx_upm_test.c b/tools/testing/selftests/kvm/x86_64/tdx_upm_test.c
index 13914aebd7da7..56ba3d4fb15a5 100644
--- a/tools/testing/selftests/kvm/x86_64/tdx_upm_test.c
+++ b/tools/testing/selftests/kvm/x86_64/tdx_upm_test.c
@@ -149,11 +149,18 @@ enum {
* Does vcpu_run, and also manages memory conversions if requested by the TD.
*/
void vcpu_run_and_manage_memory_conversions(struct kvm_vm *vm,
- struct kvm_vcpu *vcpu)
+ struct kvm_vcpu *vcpu, bool handle_conversions)
{
for (;;) {
vcpu_run(vcpu);
- if (
+ if (handle_conversions &&
+ vcpu->run->exit_reason == KVM_EXIT_MEMORY_FAULT) {
+ handle_memory_conversion(
+ vm, vcpu->run->memory.gpa,
+ vcpu->run->memory.size,
+ vcpu->run->memory.flags == KVM_MEMORY_EXIT_FLAG_PRIVATE);
+ continue;
+ } else if (
vcpu->run->exit_reason == KVM_EXIT_IO &&
vcpu->run->io.port == TDX_UPM_TEST_ACCEPT_PRINT_PORT) {
uint64_t gpa = tdx_test_read_64bit(
@@ -233,8 +240,53 @@ static void guest_upm_explicit(void)
tdx_test_success();
}
+static void guest_upm_implicit(void)
+{
+ struct tdx_upm_test_area *test_area_gva_private =
+ (struct tdx_upm_test_area *)TDX_UPM_TEST_AREA_GVA_PRIVATE;
+ struct tdx_upm_test_area *test_area_gva_shared =
+ (struct tdx_upm_test_area *)TDX_UPM_TEST_AREA_GVA_SHARED;
+
+ /* Check: host reading private memory does not modify guest's view */
+ fill_test_area(test_area_gva_private, PATTERN_GUEST_GENERAL);
+
+ tdx_test_report_to_user_space(SYNC_CHECK_READ_PRIVATE_MEMORY_FROM_HOST);
+
+ TDX_UPM_TEST_ASSERT(
+ check_test_area(test_area_gva_private, PATTERN_GUEST_GENERAL));
+
+ /* Use focus area as shared */
+ fill_focus_area(test_area_gva_shared, PATTERN_GUEST_FOCUS);
+
+ /* General areas should not be affected */
+ TDX_UPM_TEST_ASSERT(
+ check_general_areas(test_area_gva_private, PATTERN_GUEST_GENERAL));
+
+ tdx_test_report_to_user_space(SYNC_CHECK_READ_SHARED_MEMORY_FROM_HOST);
+
+ /* Check that guest has the same view of shared memory */
+ TDX_UPM_TEST_ASSERT(
+ check_focus_area(test_area_gva_shared, PATTERN_HOST_FOCUS));
+
+ /* Use focus area as private */
+ fill_focus_area(test_area_gva_private, PATTERN_GUEST_FOCUS);
+
+ /* General areas should be unaffected by remapping */
+ TDX_UPM_TEST_ASSERT(
+ check_general_areas(test_area_gva_private, PATTERN_GUEST_GENERAL));
+
+ tdx_test_report_to_user_space(SYNC_CHECK_READ_PRIVATE_MEMORY_FROM_HOST_AGAIN);
+
+ /* Check that guest can use private memory after focus area is remapped as private */
+ TDX_UPM_TEST_ASSERT(
+ fill_and_check(test_area_gva_private, PATTERN_GUEST_GENERAL));
+
+ tdx_test_success();
+}
+
static void run_selftest(struct kvm_vm *vm, struct kvm_vcpu *vcpu,
- struct tdx_upm_test_area *test_area_base_hva)
+ struct tdx_upm_test_area *test_area_base_hva,
+ bool implicit)
{
vcpu_run(vcpu);
TDX_TEST_CHECK_GUEST_FAILURE(vcpu);
@@ -253,7 +305,7 @@ static void run_selftest(struct kvm_vm *vm, struct kvm_vcpu *vcpu,
TEST_ASSERT(check_test_area(test_area_base_hva, PATTERN_CONFIDENCE_CHECK),
"Host should read PATTERN_CONFIDENCE_CHECK from guest's private memory.");
- vcpu_run_and_manage_memory_conversions(vm, vcpu);
+ vcpu_run_and_manage_memory_conversions(vm, vcpu, implicit);
TDX_TEST_CHECK_GUEST_FAILURE(vcpu);
TDX_TEST_ASSERT_IO(vcpu, TDX_TEST_REPORT_PORT, TDX_TEST_REPORT_SIZE,
TDG_VP_VMCALL_INSTRUCTION_IO_WRITE);
@@ -270,7 +322,7 @@ static void run_selftest(struct kvm_vm *vm, struct kvm_vcpu *vcpu,
TEST_ASSERT(check_focus_area(test_area_base_hva, PATTERN_HOST_FOCUS),
"Host should be able to use shared memory.");
- vcpu_run_and_manage_memory_conversions(vm, vcpu);
+ vcpu_run_and_manage_memory_conversions(vm, vcpu, implicit);
TDX_TEST_CHECK_GUEST_FAILURE(vcpu);
TDX_TEST_ASSERT_IO(vcpu, TDX_TEST_REPORT_PORT, TDX_TEST_REPORT_SIZE,
TDG_VP_VMCALL_INSTRUCTION_IO_WRITE);
@@ -319,18 +371,20 @@ static void guest_ve_handler(struct ex_regs *regs)
TDX_UPM_TEST_ASSERT(!ret);
}
-static void verify_upm_test(void)
+static void verify_upm_test(bool implicit)
{
struct kvm_vm *vm;
struct kvm_vcpu *vcpu;
+ void *guest_code;
vm_vaddr_t test_area_gva_private;
struct tdx_upm_test_area *test_area_base_hva;
uint64_t test_area_npages;
vm = td_create();
td_initialize(vm, VM_MEM_SRC_ANONYMOUS, 0);
- vcpu = td_vcpu_add(vm, 0, guest_upm_explicit);
+ guest_code = implicit ? guest_upm_implicit : guest_upm_explicit;
+ vcpu = td_vcpu_add(vm, 0, guest_code);
vm_install_exception_handler(vm, VE_VECTOR, guest_ve_handler);
@@ -370,13 +424,26 @@ static void verify_upm_test(void)
td_finalize(vm);
- printf("Verifying UPM functionality: explicit MapGPA\n");
+ if (implicit)
+ printf("Verifying UPM functionality: implicit conversion\n");
+ else
+ printf("Verifying UPM functionality: explicit MapGPA\n");
- run_selftest(vm, vcpu, test_area_base_hva);
+ run_selftest(vm, vcpu, test_area_base_hva, implicit);
kvm_vm_free(vm);
}
+void verify_upm_test_explicit(void)
+{
+ verify_upm_test(false);
+}
+
+void verify_upm_test_implicit(void)
+{
+ verify_upm_test(true);
+}
+
int main(int argc, char **argv)
{
/* Disable stdout buffering */
@@ -388,5 +455,6 @@ int main(int argc, char **argv)
return 0;
}
- run_in_new_process(&verify_upm_test);
+ run_in_new_process(&verify_upm_test_explicit);
+ run_in_new_process(&verify_upm_test_implicit);
}
--
2.39.0.246.g2a6d74b583-goog
Signed-off-by: Ackerley Tng <[email protected]>
---
.../selftests/kvm/include/x86_64/tdx/tdx.h | 21 +++++++++++++++++++
.../selftests/kvm/lib/x86_64/tdx/tdx.c | 19 +++++++++++++++++
2 files changed, 40 insertions(+)
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 c8e4b9ce795ea..2dfb9432c32f9 100644
--- a/tools/testing/selftests/kvm/include/x86_64/tdx/tdx.h
+++ b/tools/testing/selftests/kvm/include/x86_64/tdx/tdx.h
@@ -5,6 +5,7 @@
#include <stdint.h>
#define TDG_VP_INFO 1
+#define TDG_VP_VEINFO_GET 3
#define TDG_MEM_PAGE_ACCEPT 6
#define TDG_VP_VMCALL_GET_TD_VM_CALL_INFO 0x10000
@@ -41,4 +42,24 @@ uint64_t tdg_vp_info(uint64_t *rcx, uint64_t *rdx,
uint64_t tdg_vp_vmcall_map_gpa(uint64_t address, uint64_t size, uint64_t *data_out);
uint64_t tdg_mem_page_accept(uint64_t gpa, uint8_t level);
+/*
+ * Used by the #VE exception handler to gather the #VE exception
+ * info from the TDX module. This is a software only structure
+ * and not part of the TDX module/VMM ABI.
+ *
+ * Adapted from arch/x86/include/asm/tdx.h
+ */
+struct ve_info {
+ uint64_t exit_reason;
+ uint64_t exit_qual;
+ /* Guest Linear (virtual) Address */
+ uint64_t gla;
+ /* Guest Physical Address */
+ uint64_t gpa;
+ uint32_t instr_len;
+ uint32_t instr_info;
+};
+
+uint64_t tdg_vp_veinfo_get(struct ve_info *ve);
+
#endif // SELFTEST_TDX_TDX_H
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 2ebc47e268779..11a19832758bb 100644
--- a/tools/testing/selftests/kvm/lib/x86_64/tdx/tdx.c
+++ b/tools/testing/selftests/kvm/lib/x86_64/tdx/tdx.c
@@ -210,3 +210,22 @@ uint64_t tdg_mem_page_accept(uint64_t gpa, uint8_t level)
{
return __tdx_module_call(TDG_MEM_PAGE_ACCEPT, gpa | level, 0, 0, 0, NULL);
}
+
+uint64_t tdg_vp_veinfo_get(struct ve_info *ve)
+{
+ uint64_t ret;
+ struct tdx_module_output out;
+
+ memset(&out, 0, sizeof(struct tdx_module_output));
+
+ ret = __tdx_module_call(TDG_VP_VEINFO_GET, 0, 0, 0, 0, &out);
+
+ ve->exit_reason = out.rcx;
+ ve->exit_qual = out.rdx;
+ ve->gla = out.r8;
+ ve->gpa = out.r9;
+ ve->instr_len = out.r10 & 0xffffffff;
+ ve->instr_info = out.r10 >> 32;
+
+ return ret;
+}
--
2.39.0.246.g2a6d74b583-goog
This also exercises the KVM_TDX_CAPABILITIES ioctl.
Suggested-by: Isaku Yamahata <[email protected]>
Signed-off-by: Ackerley Tng <[email protected]>
---
.../selftests/kvm/lib/x86_64/tdx/tdx_util.c | 75 ++++++++++++++++++-
1 file changed, 72 insertions(+), 3 deletions(-)
diff --git a/tools/testing/selftests/kvm/lib/x86_64/tdx/tdx_util.c b/tools/testing/selftests/kvm/lib/x86_64/tdx/tdx_util.c
index 3564059c0b89b..2e9679d24a843 100644
--- a/tools/testing/selftests/kvm/lib/x86_64/tdx/tdx_util.c
+++ b/tools/testing/selftests/kvm/lib/x86_64/tdx/tdx_util.c
@@ -27,10 +27,9 @@ static char *tdx_cmd_str[] = {
};
#define TDX_MAX_CMD_STR (ARRAY_SIZE(tdx_cmd_str))
-static void tdx_ioctl(int fd, int ioctl_no, uint32_t flags, void *data)
+static int _tdx_ioctl(int fd, int ioctl_no, uint32_t flags, void *data)
{
struct kvm_tdx_cmd tdx_cmd;
- int r;
TEST_ASSERT(ioctl_no < TDX_MAX_CMD_STR, "Unknown TDX CMD : %d\n",
ioctl_no);
@@ -40,11 +39,63 @@ static void tdx_ioctl(int fd, int ioctl_no, uint32_t flags, void *data)
tdx_cmd.flags = flags;
tdx_cmd.data = (uint64_t)data;
- r = ioctl(fd, KVM_MEMORY_ENCRYPT_OP, &tdx_cmd);
+ return ioctl(fd, KVM_MEMORY_ENCRYPT_OP, &tdx_cmd);
+}
+
+static void tdx_ioctl(int fd, int ioctl_no, uint32_t flags, void *data)
+{
+ int r;
+
+ r = _tdx_ioctl(fd, ioctl_no, flags, data);
TEST_ASSERT(r == 0, "%s failed: %d %d", tdx_cmd_str[ioctl_no], r,
errno);
}
+static struct kvm_tdx_capabilities *tdx_read_capabilities(void)
+{
+ int i;
+ int rc = -1;
+ int nr_cpuid_configs = 4;
+ struct kvm_tdx_capabilities *tdx_cap = NULL;
+ int kvm_fd;
+
+ kvm_fd = open_kvm_dev_path_or_exit();
+
+ do {
+ nr_cpuid_configs *= 2;
+
+ tdx_cap = realloc(
+ tdx_cap, sizeof(*tdx_cap) +
+ nr_cpuid_configs * sizeof(*tdx_cap->cpuid_configs));
+ TEST_ASSERT(tdx_cap != NULL,
+ "Could not allocate memory for tdx capability nr_cpuid_configs %d\n",
+ nr_cpuid_configs);
+
+ tdx_cap->nr_cpuid_configs = nr_cpuid_configs;
+ rc = _tdx_ioctl(kvm_fd, KVM_TDX_CAPABILITIES, 0, tdx_cap);
+ } while (rc < 0 && errno == E2BIG);
+
+ TEST_ASSERT(rc == 0, "KVM_TDX_CAPABILITIES failed: %d %d",
+ rc, errno);
+
+ pr_debug("tdx_cap: attrs: fixed0 0x%016llx fixed1 0x%016llx\n"
+ "tdx_cap: xfam fixed0 0x%016llx fixed1 0x%016llx\n",
+ tdx_cap->attrs_fixed0, tdx_cap->attrs_fixed1,
+ tdx_cap->xfam_fixed0, tdx_cap->xfam_fixed1);
+
+ for (i = 0; i < tdx_cap->nr_cpuid_configs; i++) {
+ const struct kvm_tdx_cpuid_config *config =
+ &tdx_cap->cpuid_configs[i];
+ pr_debug("cpuid config[%d]: leaf 0x%x sub_leaf 0x%x eax 0x%08x ebx 0x%08x ecx 0x%08x edx 0x%08x\n",
+ i, config->leaf, config->sub_leaf,
+ config->eax, config->ebx, config->ecx, config->edx);
+ }
+
+ close(kvm_fd);
+
+ return tdx_cap;
+}
+
#define XFEATURE_LBR 15
#define XFEATURE_XTILECFG 17
#define XFEATURE_XTILEDATA 18
@@ -90,6 +141,21 @@ static void tdx_apply_cpuid_restrictions(struct kvm_cpuid2 *cpuid_data)
}
}
+static void tdx_check_attributes(uint64_t attributes)
+{
+ struct kvm_tdx_capabilities *tdx_cap;
+
+ tdx_cap = tdx_read_capabilities();
+
+ /* TDX spec: any bits 0 in attrs_fixed0 must be 0 in attributes */
+ ASSERT_EQ(attributes & ~tdx_cap->attrs_fixed0, 0);
+
+ /* TDX spec: any bits 1 in attrs_fixed1 must be 1 in attributes */
+ ASSERT_EQ(attributes & tdx_cap->attrs_fixed1, tdx_cap->attrs_fixed1);
+
+ free(tdx_cap);
+}
+
static void tdx_td_init(struct kvm_vm *vm, uint64_t attributes)
{
const struct kvm_cpuid2 *cpuid;
@@ -100,6 +166,9 @@ static void tdx_td_init(struct kvm_vm *vm, uint64_t attributes)
cpuid = kvm_get_supported_cpuid();
memcpy(&init_vm.cpuid, cpuid, kvm_cpuid2_size(cpuid->nent));
+
+ tdx_check_attributes(attributes);
+
init_vm.attributes = attributes;
tdx_apply_cpuid_restrictions(&init_vm.cpuid);
--
2.39.0.246.g2a6d74b583-goog
On Sat, Jan 21, 2023, Ackerley Tng wrote:
> Some SSE instructions assume a 16-byte aligned stack, and GCC compiles
> assuming the stack is aligned:
> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=40838. This combination
> results in a #GP in guests.
>
> Adding this compiler flag will generate an alternate prologue and
> epilogue to realign the runtime stack, which makes selftest code
> slower and bigger, but this is okay since we do not need selftest code
> to be extremely performant.
Huh, I had completely forgotten that this is why SSE is problematic. I ran into
this with the base UPM selftests and just disabled SSE. /facepalm.
We should figure out exactly what is causing a misaligned stack. As you've noted,
the x86-64 ABI requires a 16-byte aligned RSP. Unless I'm misreading vm_arch_vcpu_add(),
the starting stack should be page aligned, which means something is causing the
stack to become unaligned at runtime. I'd rather hunt down that something than
paper over it by having the compiler force realignment.
> Similar issue discussed at
> https://lore.kernel.org/all/CAGtprH9yKvuaF5yruh3BupQe4BxDGiBQk3ExtY2m39yP-tppsg@mail.gmail.com/
>
> Signed-off-by: Ackerley Tng <[email protected]>
> ---
> tools/testing/selftests/kvm/Makefile | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
> index 317927d9c55bd..5f9cc1e6ee67e 100644
> --- a/tools/testing/selftests/kvm/Makefile
> +++ b/tools/testing/selftests/kvm/Makefile
> @@ -205,7 +205,7 @@ LINUX_TOOL_ARCH_INCLUDE = $(top_srcdir)/tools/arch/x86/include
> else
> LINUX_TOOL_ARCH_INCLUDE = $(top_srcdir)/tools/arch/$(ARCH)/include
> endif
> -CFLAGS += -Wall -Wstrict-prototypes -Wuninitialized -O2 -g -std=gnu99 \
> +CFLAGS += -mstackrealign -Wall -Wstrict-prototypes -Wuninitialized -O2 -g -std=gnu99 \
> -fno-stack-protector -fno-PIE -I$(LINUX_TOOL_INCLUDE) \
> -I$(LINUX_TOOL_ARCH_INCLUDE) -I$(LINUX_HDR_PATH) -Iinclude \
> -I$(<D) -Iinclude/$(UNAME_M) -I ../rseq -I.. $(EXTRA_CFLAGS) \
> --
> 2.39.0.246.g2a6d74b583-goog
>
From: Sagi Shahar <[email protected]>
The test verifies IO writes of various sizes from the guest to the host.
Signed-off-by: Sagi Shahar <[email protected]>
Signed-off-by: Ackerley Tng <[email protected]>
---
.../selftests/kvm/include/x86_64/tdx/tdcall.h | 3 +
.../selftests/kvm/x86_64/tdx_vm_tests.c | 91 +++++++++++++++++++
2 files changed, 94 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 78001bfec9c8d..b5e94b7c48fa5 100644
--- a/tools/testing/selftests/kvm/include/x86_64/tdx/tdcall.h
+++ b/tools/testing/selftests/kvm/include/x86_64/tdx/tdcall.h
@@ -10,6 +10,9 @@
#define TDG_VP_VMCALL_INSTRUCTION_IO_READ 0
#define TDG_VP_VMCALL_INSTRUCTION_IO_WRITE 1
+#define TDG_VP_VMCALL_SUCCESS 0x0000000000000000
+#define TDG_VP_VMCALL_INVALID_OPERAND 0x8000000000000000
+
#define TDX_HCALL_HAS_OUTPUT BIT(0)
#define TDX_HYPERCALL_STANDARD 0
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 188442a734dca..ac23d1ad1e687 100644
--- a/tools/testing/selftests/kvm/x86_64/tdx_vm_tests.c
+++ b/tools/testing/selftests/kvm/x86_64/tdx_vm_tests.c
@@ -338,6 +338,96 @@ void verify_get_td_vmcall_info(void)
printf("\t ... PASSED\n");
}
+#define TDX_IO_WRITES_TEST_PORT 0x51
+
+/*
+ * Verifies IO functionality by writing values of different sizes
+ * to the host.
+ */
+void guest_io_writes(void)
+{
+ uint64_t byte_1 = 0xAB;
+ uint64_t byte_2 = 0xABCD;
+ uint64_t byte_4 = 0xFFABCDEF;
+ uint64_t ret;
+
+ ret = tdg_vp_vmcall_instruction_io(TDX_IO_WRITES_TEST_PORT, 1,
+ TDG_VP_VMCALL_INSTRUCTION_IO_WRITE,
+ &byte_1);
+ if (ret)
+ tdx_test_fatal(ret);
+
+ ret = tdg_vp_vmcall_instruction_io(TDX_IO_WRITES_TEST_PORT, 2,
+ TDG_VP_VMCALL_INSTRUCTION_IO_WRITE,
+ &byte_2);
+ if (ret)
+ tdx_test_fatal(ret);
+
+ ret = tdg_vp_vmcall_instruction_io(TDX_IO_WRITES_TEST_PORT, 4,
+ TDG_VP_VMCALL_INSTRUCTION_IO_WRITE,
+ &byte_4);
+ if (ret)
+ tdx_test_fatal(ret);
+
+ // Write an invalid number of bytes.
+ ret = tdg_vp_vmcall_instruction_io(TDX_IO_WRITES_TEST_PORT, 5,
+ TDG_VP_VMCALL_INSTRUCTION_IO_WRITE,
+ &byte_4);
+ if (ret)
+ tdx_test_fatal(ret);
+
+ tdx_test_success();
+}
+
+void verify_guest_writes(void)
+{
+ struct kvm_vm *vm;
+ struct kvm_vcpu *vcpu;
+
+ uint8_t byte_1;
+ uint16_t byte_2;
+ uint32_t byte_4;
+
+ vm = td_create();
+ td_initialize(vm, VM_MEM_SRC_ANONYMOUS, 0);
+ vcpu = td_vcpu_add(vm, 0, guest_io_writes);
+ td_finalize(vm);
+
+ printf("Verifying guest writes:\n");
+
+ vcpu_run(vcpu);
+ TDX_TEST_CHECK_GUEST_FAILURE(vcpu);
+ TDX_TEST_ASSERT_IO(vcpu, TDX_IO_WRITES_TEST_PORT, 1,
+ TDG_VP_VMCALL_INSTRUCTION_IO_WRITE);
+ byte_1 = *(uint8_t *)((void *)vcpu->run + vcpu->run->io.data_offset);
+
+ vcpu_run(vcpu);
+ TDX_TEST_CHECK_GUEST_FAILURE(vcpu);
+ TDX_TEST_ASSERT_IO(vcpu, TDX_IO_WRITES_TEST_PORT, 2,
+ TDG_VP_VMCALL_INSTRUCTION_IO_WRITE);
+ byte_2 = *(uint16_t *)((void *)vcpu->run + vcpu->run->io.data_offset);
+
+ vcpu_run(vcpu);
+ TDX_TEST_CHECK_GUEST_FAILURE(vcpu);
+ TDX_TEST_ASSERT_IO(vcpu, TDX_IO_WRITES_TEST_PORT, 4,
+ TDG_VP_VMCALL_INSTRUCTION_IO_WRITE);
+ byte_4 = *(uint32_t *)((void *)vcpu->run + vcpu->run->io.data_offset);
+
+ ASSERT_EQ(byte_1, 0xAB);
+ ASSERT_EQ(byte_2, 0xABCD);
+ ASSERT_EQ(byte_4, 0xFFABCDEF);
+
+ vcpu_run(vcpu);
+ ASSERT_EQ(vcpu->run->exit_reason, KVM_EXIT_SYSTEM_EVENT);
+ ASSERT_EQ(vcpu->run->system_event.data[1], TDG_VP_VMCALL_INVALID_OPERAND);
+
+ 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);
@@ -352,6 +442,7 @@ int main(int argc, char **argv)
run_in_new_process(&verify_td_ioexit);
run_in_new_process(&verify_td_cpuid);
run_in_new_process(&verify_get_td_vmcall_info);
+ run_in_new_process(&verify_guest_writes);
return 0;
}
--
2.39.0.246.g2a6d74b583-goog
This allows initializing sregs without setting vCPU registers in
KVM.
No functional change intended.
Signed-off-by: Ackerley Tng <[email protected]>
---
.../selftests/kvm/include/x86_64/processor.h | 2 +
.../selftests/kvm/lib/x86_64/processor.c | 39 ++++++++++---------
2 files changed, 23 insertions(+), 18 deletions(-)
diff --git a/tools/testing/selftests/kvm/include/x86_64/processor.h b/tools/testing/selftests/kvm/include/x86_64/processor.h
index e8ca0d8a6a7e0..74e0d3698f30c 100644
--- a/tools/testing/selftests/kvm/include/x86_64/processor.h
+++ b/tools/testing/selftests/kvm/include/x86_64/processor.h
@@ -644,6 +644,8 @@ const struct kvm_cpuid_entry2 *get_cpuid_entry(const struct kvm_cpuid2 *cpuid,
void vcpu_init_cpuid(struct kvm_vcpu *vcpu, const struct kvm_cpuid2 *cpuid);
void vcpu_set_hv_cpuid(struct kvm_vcpu *vcpu);
+void vcpu_setup_mode_sregs(struct kvm_vm *vm, struct kvm_sregs *sregs);
+
static inline struct kvm_cpuid_entry2 *__vcpu_get_cpuid_entry(struct kvm_vcpu *vcpu,
uint32_t function,
uint32_t index)
diff --git a/tools/testing/selftests/kvm/lib/x86_64/processor.c b/tools/testing/selftests/kvm/lib/x86_64/processor.c
index ed811181320de..1bb07d3c025b0 100644
--- a/tools/testing/selftests/kvm/lib/x86_64/processor.c
+++ b/tools/testing/selftests/kvm/lib/x86_64/processor.c
@@ -589,35 +589,38 @@ static void kvm_setup_tss_64bit(struct kvm_vm *vm, struct kvm_segment *segp,
kvm_seg_fill_gdt_64bit(vm, segp);
}
-static void vcpu_setup(struct kvm_vm *vm, struct kvm_vcpu *vcpu)
+void vcpu_setup_mode_sregs(struct kvm_vm *vm, struct kvm_sregs *sregs)
{
- struct kvm_sregs sregs;
-
- /* Set mode specific system register values. */
- vcpu_sregs_get(vcpu, &sregs);
-
- sregs.idt.limit = 0;
+ sregs->idt.limit = 0;
- kvm_setup_gdt(vm, &sregs.gdt);
+ kvm_setup_gdt(vm, &sregs->gdt);
switch (vm->mode) {
case VM_MODE_PXXV48_4K:
- sregs.cr0 = X86_CR0_PE | X86_CR0_NE | X86_CR0_PG;
- sregs.cr4 |= X86_CR4_PAE | X86_CR4_OSFXSR;
- sregs.efer |= (EFER_LME | EFER_LMA | EFER_NX);
-
- kvm_seg_set_unusable(&sregs.ldt);
- kvm_seg_set_kernel_code_64bit(vm, DEFAULT_CODE_SELECTOR, &sregs.cs);
- kvm_seg_set_kernel_data_64bit(vm, DEFAULT_DATA_SELECTOR, &sregs.ds);
- kvm_seg_set_kernel_data_64bit(vm, DEFAULT_DATA_SELECTOR, &sregs.es);
- kvm_setup_tss_64bit(vm, &sregs.tr, 0x18);
+ sregs->cr0 = X86_CR0_PE | X86_CR0_NE | X86_CR0_PG;
+ sregs->cr4 |= X86_CR4_PAE | X86_CR4_OSFXSR;
+ sregs->efer |= (EFER_LME | EFER_LMA | EFER_NX);
+
+ kvm_seg_set_unusable(&sregs->ldt);
+ kvm_seg_set_kernel_code_64bit(vm, DEFAULT_CODE_SELECTOR, &sregs->cs);
+ kvm_seg_set_kernel_data_64bit(vm, DEFAULT_DATA_SELECTOR, &sregs->ds);
+ kvm_seg_set_kernel_data_64bit(vm, DEFAULT_DATA_SELECTOR, &sregs->es);
+ kvm_setup_tss_64bit(vm, &sregs->tr, 0x18);
break;
default:
TEST_FAIL("Unknown guest mode, mode: 0x%x", vm->mode);
}
- sregs.cr3 = vm->pgd;
+ sregs->cr3 = vm->pgd;
+}
+
+static void vcpu_setup(struct kvm_vm *vm, struct kvm_vcpu *vcpu)
+{
+ struct kvm_sregs sregs;
+
+ vcpu_sregs_get(vcpu, &sregs);
+ vcpu_setup_mode_sregs(vm, &sregs);
vcpu_sregs_set(vcpu, &sregs);
}
--
2.39.0.246.g2a6d74b583-goog
From: Sagi Shahar <[email protected]>
The test calls get_td_vmcall_info from the guest and verifies the
expected returned values.
Signed-off-by: Sagi Shahar <[email protected]>
Signed-off-by: Ackerley Tng <[email protected]>
---
.../selftests/kvm/include/x86_64/tdx/tdx.h | 3 +
.../kvm/include/x86_64/tdx/test_util.h | 27 +++++++
.../selftests/kvm/lib/x86_64/tdx/tdx.c | 23 ++++++
.../selftests/kvm/lib/x86_64/tdx/test_util.c | 46 +++++++++++
.../selftests/kvm/x86_64/tdx_vm_tests.c | 80 +++++++++++++++++++
5 files changed, 179 insertions(+)
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 28959bdb07628..37ad16943e299 100644
--- a/tools/testing/selftests/kvm/include/x86_64/tdx/tdx.h
+++ b/tools/testing/selftests/kvm/include/x86_64/tdx/tdx.h
@@ -4,6 +4,7 @@
#include <stdint.h>
+#define TDG_VP_VMCALL_GET_TD_VM_CALL_INFO 0x10000
#define TDG_VP_VMCALL_REPORT_FATAL_ERROR 0x10003
#define TDG_VP_VMCALL_INSTRUCTION_IO 30
@@ -11,5 +12,7 @@
uint64_t tdg_vp_vmcall_instruction_io(uint64_t port, uint64_t size,
uint64_t write, uint64_t *data);
void tdg_vp_vmcall_report_fatal_error(uint64_t error_code, uint64_t data_gpa);
+uint64_t tdg_vp_vmcall_get_td_vmcall_info(uint64_t *r11, uint64_t *r12,
+ uint64_t *r13, uint64_t *r14);
#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 af0ddbfe8d71b..8a9b6a1bec3eb 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
@@ -4,6 +4,7 @@
#include <stdbool.h>
+#include "kvm_util_base.h"
#include "tdcall.h"
#define TDX_TEST_SUCCESS_PORT 0x30
@@ -111,4 +112,30 @@ void tdx_test_fatal_with_data(uint64_t error_code, uint64_t data_gpa);
*/
uint64_t tdx_test_report_to_user_space(uint32_t data);
+/**
+ * Report a 64 bit value from the guest to user space using TDG.VP.VMCALL
+ * <Instruction.IO> call.
+ *
+ * Data is sent to host in 2 calls. LSB is sent (and needs to be read) first.
+ */
+uint64_t tdx_test_send_64bit(uint64_t port, uint64_t data);
+
+/**
+ * Report a 64 bit value from the guest to user space using TDG.VP.VMCALL
+ * <Instruction.IO> call. Data is reported on port TDX_TEST_REPORT_PORT.
+ */
+uint64_t tdx_test_report_64bit_to_user_space(uint64_t data);
+
+/**
+ * Read a 64 bit value from the guest in user space, sent using
+ * tdx_test_send_64bit().
+ */
+uint64_t tdx_test_read_64bit(struct kvm_vcpu *vcpu, uint64_t port);
+
+/**
+ * Read a 64 bit value from the guest in user space, sent using
+ * tdx_test_report_64bit_to_user_space.
+ */
+uint64_t tdx_test_read_64bit_report_from_guest(struct kvm_vcpu *vcpu);
+
#endif // SELFTEST_TDX_TEST_UTIL_H
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 e8c399f2277cf..7254d61515db2 100644
--- a/tools/testing/selftests/kvm/lib/x86_64/tdx/tdx.c
+++ b/tools/testing/selftests/kvm/lib/x86_64/tdx/tdx.c
@@ -43,3 +43,26 @@ void tdg_vp_vmcall_report_fatal_error(uint64_t error_code, uint64_t data_gpa)
__tdx_hypercall(&args, 0);
}
+
+uint64_t tdg_vp_vmcall_get_td_vmcall_info(uint64_t *r11, uint64_t *r12,
+ uint64_t *r13, uint64_t *r14)
+{
+ uint64_t ret;
+ struct tdx_hypercall_args args = {
+ .r11 = TDG_VP_VMCALL_GET_TD_VM_CALL_INFO,
+ .r12 = 0,
+ };
+
+ ret = __tdx_hypercall(&args, TDX_HCALL_HAS_OUTPUT);
+
+ if (r11)
+ *r11 = args.r11;
+ if (r12)
+ *r12 = args.r12;
+ if (r13)
+ *r13 = args.r13;
+ if (r14)
+ *r14 = args.r14;
+
+ return ret;
+}
diff --git a/tools/testing/selftests/kvm/lib/x86_64/tdx/test_util.c b/tools/testing/selftests/kvm/lib/x86_64/tdx/test_util.c
index 55c5a1e634df7..3ae651cd5fac4 100644
--- a/tools/testing/selftests/kvm/lib/x86_64/tdx/test_util.c
+++ b/tools/testing/selftests/kvm/lib/x86_64/tdx/test_util.c
@@ -7,6 +7,7 @@
#include <unistd.h>
#include "kvm_util_base.h"
+#include "tdx/tdcall.h"
#include "tdx/tdx.h"
#include "tdx/test_util.h"
@@ -53,3 +54,48 @@ uint64_t tdx_test_report_to_user_space(uint32_t data)
TDG_VP_VMCALL_INSTRUCTION_IO_WRITE,
&data_64);
}
+
+uint64_t tdx_test_send_64bit(uint64_t port, uint64_t data)
+{
+ uint64_t err;
+ uint64_t data_lo = data & 0xFFFFFFFF;
+ uint64_t data_hi = (data >> 32) & 0xFFFFFFFF;
+
+ err = tdg_vp_vmcall_instruction_io(port, 4,
+ TDG_VP_VMCALL_INSTRUCTION_IO_WRITE,
+ &data_lo);
+ if (err)
+ return err;
+
+ return tdg_vp_vmcall_instruction_io(port, 4,
+ TDG_VP_VMCALL_INSTRUCTION_IO_WRITE,
+ &data_hi);
+}
+
+uint64_t tdx_test_report_64bit_to_user_space(uint64_t data)
+{
+ return tdx_test_send_64bit(TDX_TEST_REPORT_PORT, data);
+}
+
+uint64_t tdx_test_read_64bit(struct kvm_vcpu *vcpu, uint64_t port)
+{
+ uint32_t lo, hi;
+ uint64_t res;
+
+ TDX_TEST_ASSERT_IO(vcpu, port, 4, TDG_VP_VMCALL_INSTRUCTION_IO_WRITE);
+ lo = *(uint32_t *)((void *)vcpu->run + vcpu->run->io.data_offset);
+
+ vcpu_run(vcpu);
+
+ TDX_TEST_ASSERT_IO(vcpu, port, 4, TDG_VP_VMCALL_INSTRUCTION_IO_WRITE);
+ hi = *(uint32_t *)((void *)vcpu->run + vcpu->run->io.data_offset);
+
+ res = hi;
+ res = (res << 32) | lo;
+ return res;
+}
+
+uint64_t tdx_test_read_64bit_report_from_guest(struct kvm_vcpu *vcpu)
+{
+ return tdx_test_read_64bit(vcpu, TDX_TEST_REPORT_PORT);
+}
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 b6072769967fa..188442a734dca 100644
--- a/tools/testing/selftests/kvm/x86_64/tdx_vm_tests.c
+++ b/tools/testing/selftests/kvm/x86_64/tdx_vm_tests.c
@@ -259,6 +259,85 @@ void verify_td_cpuid(void)
printf("\t ... PASSED\n");
}
+/*
+ * Verifies get_td_vmcall_info functionality.
+ */
+void guest_code_get_td_vmcall_info(void)
+{
+ uint64_t err;
+ uint64_t r11, r12, r13, r14;
+
+ err = tdg_vp_vmcall_get_td_vmcall_info(&r11, &r12, &r13, &r14);
+ if (err)
+ tdx_test_fatal(err);
+
+ err = tdx_test_report_64bit_to_user_space(r11);
+ if (err)
+ tdx_test_fatal(err);
+
+ err = tdx_test_report_64bit_to_user_space(r12);
+ if (err)
+ tdx_test_fatal(err);
+
+ err = tdx_test_report_64bit_to_user_space(r13);
+ if (err)
+ tdx_test_fatal(err);
+
+ err = tdx_test_report_64bit_to_user_space(r14);
+ if (err)
+ tdx_test_fatal(err);
+
+ tdx_test_success();
+}
+
+void verify_get_td_vmcall_info(void)
+{
+ struct kvm_vm *vm;
+ struct kvm_vcpu *vcpu;
+
+ uint64_t r11, r12, r13, r14;
+
+ vm = td_create();
+ td_initialize(vm, VM_MEM_SRC_ANONYMOUS, 0);
+ vcpu = td_vcpu_add(vm, 0, guest_code_get_td_vmcall_info);
+ td_finalize(vm);
+
+ printf("Verifying TD get vmcall info:\n");
+
+ /* Wait for guest to report r11 value */
+ vcpu_run(vcpu);
+ TDX_TEST_CHECK_GUEST_FAILURE(vcpu);
+ r11 = tdx_test_read_64bit_report_from_guest(vcpu);
+
+ /* Wait for guest to report r12 value */
+ vcpu_run(vcpu);
+ TDX_TEST_CHECK_GUEST_FAILURE(vcpu);
+ r12 = tdx_test_read_64bit_report_from_guest(vcpu);
+
+ /* Wait for guest to report r13 value */
+ vcpu_run(vcpu);
+ TDX_TEST_CHECK_GUEST_FAILURE(vcpu);
+ r13 = tdx_test_read_64bit_report_from_guest(vcpu);
+
+ /* Wait for guest to report r14 value */
+ vcpu_run(vcpu);
+ TDX_TEST_CHECK_GUEST_FAILURE(vcpu);
+ r14 = tdx_test_read_64bit_report_from_guest(vcpu);
+
+ ASSERT_EQ(r11, 0);
+ ASSERT_EQ(r12, 0);
+ ASSERT_EQ(r13, 0);
+ ASSERT_EQ(r14, 0);
+
+ /* Wait for guest to complete execution */
+ vcpu_run(vcpu);
+ TDX_TEST_CHECK_GUEST_FAILURE(vcpu);
+ TDX_TEST_ASSERT_SUCCESS(vcpu);
+
+ kvm_vm_free(vm);
+ printf("\t ... PASSED\n");
+}
+
int main(int argc, char **argv)
{
setbuf(stdout, NULL);
@@ -272,6 +351,7 @@ int main(int argc, char **argv)
run_in_new_process(&verify_report_fatal_error);
run_in_new_process(&verify_td_ioexit);
run_in_new_process(&verify_td_cpuid);
+ run_in_new_process(&verify_get_td_vmcall_info);
return 0;
}
--
2.39.0.246.g2a6d74b583-goog
One-to-one GVA to GPA mappings can be used in the guest to set up boot
sequences during which paging is enabled, hence requiring a transition
from using physical to virtual addresses in consecutive instructions.
Signed-off-by: Ackerley Tng <[email protected]>
---
.../selftests/kvm/include/kvm_util_base.h | 2 ++
tools/testing/selftests/kvm/lib/kvm_util.c | 35 ++++++++++++++++---
2 files changed, 32 insertions(+), 5 deletions(-)
diff --git a/tools/testing/selftests/kvm/include/kvm_util_base.h b/tools/testing/selftests/kvm/include/kvm_util_base.h
index 8dac0f49d20b9..0db5cd4b8383a 100644
--- a/tools/testing/selftests/kvm/include/kvm_util_base.h
+++ b/tools/testing/selftests/kvm/include/kvm_util_base.h
@@ -402,6 +402,8 @@ void vm_mem_region_delete(struct kvm_vm *vm, uint32_t slot);
struct kvm_vcpu *__vm_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id);
vm_vaddr_t vm_vaddr_alloc(struct kvm_vm *vm, size_t sz, vm_vaddr_t vaddr_min);
vm_vaddr_t vm_vaddr_alloc_shared(struct kvm_vm *vm, size_t sz, vm_vaddr_t vaddr_min);
+vm_vaddr_t vm_vaddr_alloc_1to1(struct kvm_vm *vm, size_t sz, vm_vaddr_t vaddr_min,
+ uint32_t data_memslot);
vm_vaddr_t vm_vaddr_alloc_pages(struct kvm_vm *vm, int nr_pages);
vm_vaddr_t vm_vaddr_alloc_page(struct kvm_vm *vm);
diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c
index fa8aea97cdb62..5257bce6f546d 100644
--- a/tools/testing/selftests/kvm/lib/kvm_util.c
+++ b/tools/testing/selftests/kvm/lib/kvm_util.c
@@ -1237,6 +1237,8 @@ static vm_vaddr_t vm_vaddr_unused_gap(struct kvm_vm *vm, size_t sz,
* vm - Virtual Machine
* sz - Size in bytes
* vaddr_min - Minimum starting virtual address
+ * paddr_min - Minimum starting physical address
+ * data_memslot - memslot number to allocate in
* encrypt - Whether the region should be handled as encrypted
*
* Output Args: None
@@ -1251,14 +1253,15 @@ static vm_vaddr_t vm_vaddr_unused_gap(struct kvm_vm *vm, size_t sz,
* a page.
*/
static vm_vaddr_t
-_vm_vaddr_alloc(struct kvm_vm *vm, size_t sz, vm_vaddr_t vaddr_min, bool encrypt)
+_vm_vaddr_alloc(struct kvm_vm *vm, size_t sz, vm_vaddr_t vaddr_min,
+ vm_paddr_t paddr_min, uint32_t data_memslot, bool encrypt)
{
uint64_t pages = (sz >> vm->page_shift) + ((sz % vm->page_size) != 0);
virt_pgd_alloc(vm);
vm_paddr_t paddr = _vm_phy_pages_alloc(vm, pages,
- KVM_UTIL_MIN_PFN * vm->page_size,
- 0, encrypt);
+ paddr_min,
+ data_memslot, encrypt);
/*
* Find an unused range of virtual page addresses of at least
@@ -1281,12 +1284,34 @@ _vm_vaddr_alloc(struct kvm_vm *vm, size_t sz, vm_vaddr_t vaddr_min, bool encrypt
vm_vaddr_t vm_vaddr_alloc(struct kvm_vm *vm, size_t sz, vm_vaddr_t vaddr_min)
{
- return _vm_vaddr_alloc(vm, sz, vaddr_min, vm->protected);
+ return _vm_vaddr_alloc(vm, sz, vaddr_min,
+ KVM_UTIL_MIN_PFN * vm->page_size, 0,
+ vm->protected);
}
vm_vaddr_t vm_vaddr_alloc_shared(struct kvm_vm *vm, size_t sz, vm_vaddr_t vaddr_min)
{
- return _vm_vaddr_alloc(vm, sz, vaddr_min, false);
+ return _vm_vaddr_alloc(vm, sz, vaddr_min,
+ KVM_UTIL_MIN_PFN * vm->page_size, 0, false);
+}
+
+/**
+ * Allocate memory in @vm of size @sz in memslot with id @data_memslot,
+ * beginning with the desired address of @vaddr_min.
+ *
+ * If there isn't enough memory at @vaddr_min, find the next possible address
+ * that can meet the requested size in the given memslot.
+ *
+ * Return the address where the memory is allocated.
+ */
+vm_vaddr_t vm_vaddr_alloc_1to1(struct kvm_vm *vm, size_t sz, vm_vaddr_t vaddr_min,
+ uint32_t data_memslot)
+{
+ vm_vaddr_t gva = _vm_vaddr_alloc(vm, sz, vaddr_min, (vm_paddr_t) vaddr_min,
+ data_memslot, vm->protected);
+ ASSERT_EQ(gva, addr_gva2gpa(vm, gva));
+
+ return gva;
}
/*
--
2.39.0.246.g2a6d74b583-goog
This tests the use of restricted memory with explicit MapGPA calls.
Signed-off-by: Ackerley Tng <[email protected]>
---
tools/testing/selftests/kvm/.gitignore | 1 +
tools/testing/selftests/kvm/Makefile | 1 +
.../selftests/kvm/x86_64/tdx_upm_test.c | 392 ++++++++++++++++++
3 files changed, 394 insertions(+)
create mode 100644 tools/testing/selftests/kvm/x86_64/tdx_upm_test.c
diff --git a/tools/testing/selftests/kvm/.gitignore b/tools/testing/selftests/kvm/.gitignore
index e1663b0f809b4..c1def2271f4cd 100644
--- a/tools/testing/selftests/kvm/.gitignore
+++ b/tools/testing/selftests/kvm/.gitignore
@@ -67,6 +67,7 @@
/x86_64/triple_fault_event_test
/x86_64/tdx_vm_tests
/x86_64/tdx_shared_mem_test
+/x86_64/tdx_upm_test
/access_tracking_perf_test
/demand_paging_test
/dirty_log_test
diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
index 27e9148212fa5..2b1434368a0f2 100644
--- a/tools/testing/selftests/kvm/Makefile
+++ b/tools/testing/selftests/kvm/Makefile
@@ -153,6 +153,7 @@ TEST_GEN_PROGS_x86_64 += kvm_binary_stats_test
TEST_GEN_PROGS_x86_64 += system_counter_offset_test
TEST_GEN_PROGS_x86_64 += x86_64/tdx_vm_tests
TEST_GEN_PROGS_x86_64 += x86_64/tdx_shared_mem_test
+TEST_GEN_PROGS_x86_64 += x86_64/tdx_upm_test
# Compiled outputs used by test targets
TEST_GEN_PROGS_EXTENDED_x86_64 += x86_64/nx_huge_pages_test
diff --git a/tools/testing/selftests/kvm/x86_64/tdx_upm_test.c b/tools/testing/selftests/kvm/x86_64/tdx_upm_test.c
new file mode 100644
index 0000000000000..13914aebd7da7
--- /dev/null
+++ b/tools/testing/selftests/kvm/x86_64/tdx_upm_test.c
@@ -0,0 +1,392 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <asm/kvm.h>
+#include <asm/vmx.h>
+#include <linux/kvm.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "kvm_util_base.h"
+#include "processor.h"
+#include "tdx/tdcall.h"
+#include "tdx/tdx.h"
+#include "tdx/tdx_util.h"
+#include "tdx/test_util.h"
+#include "test_util.h"
+
+/* TDX UPM test patterns */
+#define PATTERN_CONFIDENCE_CHECK (0x11)
+#define PATTERN_HOST_FOCUS (0x22)
+#define PATTERN_GUEST_GENERAL (0x33)
+#define PATTERN_GUEST_FOCUS (0x44)
+
+/*
+ * 0x80000000 is arbitrarily selected. The selected address need not be the same
+ * as TDX_UPM_TEST_AREA_GVA_PRIVATE, but it should not overlap with selftest
+ * code or boot page.
+ */
+#define TDX_UPM_TEST_AREA_GPA (0x80000000)
+/* Test area GPA is arbitrarily selected */
+#define TDX_UPM_TEST_AREA_GVA_PRIVATE (0x90000000)
+/* Select any bit that can be used as a flag */
+#define TDX_UPM_TEST_AREA_GVA_SHARED_BIT (32)
+/*
+ * TDX_UPM_TEST_AREA_GVA_SHARED is used to map the same GPA twice into the
+ * guest, once as shared and once as private
+ */
+#define TDX_UPM_TEST_AREA_GVA_SHARED \
+ (TDX_UPM_TEST_AREA_GVA_PRIVATE | \
+ BIT_ULL(TDX_UPM_TEST_AREA_GVA_SHARED_BIT))
+
+/* The test area is 2MB in size */
+#define TDX_UPM_TEST_AREA_SIZE (2 << 20)
+/* 0th general area is 1MB in size */
+#define TDX_UPM_GENERAL_AREA_0_SIZE (1 << 20)
+/* Focus area is 40KB in size */
+#define TDX_UPM_FOCUS_AREA_SIZE (40 << 10)
+/* 1st general area is the rest of the space in the test area */
+#define TDX_UPM_GENERAL_AREA_1_SIZE \
+ (TDX_UPM_TEST_AREA_SIZE - TDX_UPM_GENERAL_AREA_0_SIZE - \
+ TDX_UPM_FOCUS_AREA_SIZE)
+
+/*
+ * The test memory area is set up as two general areas, sandwiching a focus
+ * area. The general areas act as control areas. After they are filled, they
+ * are not expected to change throughout the tests. The focus area is memory
+ * permissions change from private to shared and vice-versa.
+ *
+ * The focus area is intentionally small, and sandwiched to test that when the
+ * focus area's permissions change, the other areas' permissions are not
+ * affected.
+ */
+struct __packed tdx_upm_test_area {
+ uint8_t general_area_0[TDX_UPM_GENERAL_AREA_0_SIZE];
+ uint8_t focus_area[TDX_UPM_FOCUS_AREA_SIZE];
+ uint8_t general_area_1[TDX_UPM_GENERAL_AREA_1_SIZE];
+};
+
+static void fill_test_area(struct tdx_upm_test_area *test_area_base,
+ uint8_t pattern)
+{
+ memset(test_area_base, pattern, sizeof(*test_area_base));
+}
+
+static void fill_focus_area(struct tdx_upm_test_area *test_area_base,
+ uint8_t pattern)
+{
+ memset(test_area_base->focus_area, pattern,
+ sizeof(test_area_base->focus_area));
+}
+
+static bool check_area(uint8_t *base, uint64_t size, uint8_t expected_pattern)
+{
+ size_t i;
+
+ for (i = 0; i < size; i++) {
+ if (base[i] != expected_pattern)
+ return false;
+ }
+
+ return true;
+}
+
+static bool check_general_areas(struct tdx_upm_test_area *test_area_base,
+ uint8_t expected_pattern)
+{
+ return (check_area(test_area_base->general_area_0,
+ sizeof(test_area_base->general_area_0),
+ expected_pattern) &&
+ check_area(test_area_base->general_area_1,
+ sizeof(test_area_base->general_area_1),
+ expected_pattern));
+}
+
+static bool check_focus_area(struct tdx_upm_test_area *test_area_base,
+ uint8_t expected_pattern)
+{
+ return check_area(test_area_base->focus_area,
+ sizeof(test_area_base->focus_area), expected_pattern);
+}
+
+static bool check_test_area(struct tdx_upm_test_area *test_area_base,
+ uint8_t expected_pattern)
+{
+ return (check_general_areas(test_area_base, expected_pattern) &&
+ check_focus_area(test_area_base, expected_pattern));
+}
+
+static bool fill_and_check(struct tdx_upm_test_area *test_area_base, uint8_t pattern)
+{
+ fill_test_area(test_area_base, pattern);
+
+ return check_test_area(test_area_base, pattern);
+}
+
+#define TDX_UPM_TEST_ASSERT(x) \
+ do { \
+ if (!(x)) \
+ tdx_test_fatal(__LINE__); \
+ } while (0)
+
+/*
+ * Shared variables between guest and host
+ */
+static struct tdx_upm_test_area *test_area_gpa_private;
+static struct tdx_upm_test_area *test_area_gpa_shared;
+
+/*
+ * Test stages for syncing with host
+ */
+enum {
+ SYNC_CHECK_READ_PRIVATE_MEMORY_FROM_HOST = 1,
+ SYNC_CHECK_READ_SHARED_MEMORY_FROM_HOST,
+ SYNC_CHECK_READ_PRIVATE_MEMORY_FROM_HOST_AGAIN,
+};
+
+#define TDX_UPM_TEST_ACCEPT_PRINT_PORT 0x87
+
+/**
+ * Does vcpu_run, and also manages memory conversions if requested by the TD.
+ */
+void vcpu_run_and_manage_memory_conversions(struct kvm_vm *vm,
+ struct kvm_vcpu *vcpu)
+{
+ for (;;) {
+ vcpu_run(vcpu);
+ if (
+ vcpu->run->exit_reason == KVM_EXIT_IO &&
+ vcpu->run->io.port == TDX_UPM_TEST_ACCEPT_PRINT_PORT) {
+ uint64_t gpa = tdx_test_read_64bit(
+ vcpu, TDX_UPM_TEST_ACCEPT_PRINT_PORT);
+ printf("\t ... guest accepting 1 page at GPA: 0x%lx\n", gpa);
+ continue;
+ }
+
+ break;
+ }
+}
+
+static void guest_upm_explicit(void)
+{
+ uint64_t ret = 0;
+ uint64_t failed_gpa;
+
+ struct tdx_upm_test_area *test_area_gva_private =
+ (struct tdx_upm_test_area *)TDX_UPM_TEST_AREA_GVA_PRIVATE;
+ struct tdx_upm_test_area *test_area_gva_shared =
+ (struct tdx_upm_test_area *)TDX_UPM_TEST_AREA_GVA_SHARED;
+
+ /* Check: host reading private memory does not modify guest's view */
+ fill_test_area(test_area_gva_private, PATTERN_GUEST_GENERAL);
+
+ tdx_test_report_to_user_space(SYNC_CHECK_READ_PRIVATE_MEMORY_FROM_HOST);
+
+ TDX_UPM_TEST_ASSERT(
+ check_test_area(test_area_gva_private, PATTERN_GUEST_GENERAL));
+
+ /* Remap focus area as shared */
+ ret = tdg_vp_vmcall_map_gpa((uint64_t)test_area_gpa_shared->focus_area,
+ sizeof(test_area_gpa_shared->focus_area),
+ &failed_gpa);
+ TDX_UPM_TEST_ASSERT(!ret);
+
+ /* General areas should be unaffected by remapping */
+ TDX_UPM_TEST_ASSERT(
+ check_general_areas(test_area_gva_private, PATTERN_GUEST_GENERAL));
+
+ /*
+ * Use memory contents to confirm that the memory allocated using mmap
+ * is used as backing memory for shared memory - PATTERN_CONFIDENCE_CHECK
+ * was written by the VMM at the beginning of this test.
+ */
+ TDX_UPM_TEST_ASSERT(
+ check_focus_area(test_area_gva_shared, PATTERN_CONFIDENCE_CHECK));
+
+ /* Guest can use focus area after remapping as shared */
+ fill_focus_area(test_area_gva_shared, PATTERN_GUEST_FOCUS);
+
+ tdx_test_report_to_user_space(SYNC_CHECK_READ_SHARED_MEMORY_FROM_HOST);
+
+ /* Check that guest has the same view of shared memory */
+ TDX_UPM_TEST_ASSERT(
+ check_focus_area(test_area_gva_shared, PATTERN_HOST_FOCUS));
+
+ /* Remap focus area back to private */
+ ret = tdg_vp_vmcall_map_gpa((uint64_t)test_area_gpa_private->focus_area,
+ sizeof(test_area_gpa_private->focus_area),
+ &failed_gpa);
+ TDX_UPM_TEST_ASSERT(!ret);
+
+ /* General areas should be unaffected by remapping */
+ TDX_UPM_TEST_ASSERT(
+ check_general_areas(test_area_gva_private, PATTERN_GUEST_GENERAL));
+
+ /* Focus area should be zeroed after remapping */
+ TDX_UPM_TEST_ASSERT(check_focus_area(test_area_gva_private, 0));
+
+ tdx_test_report_to_user_space(SYNC_CHECK_READ_PRIVATE_MEMORY_FROM_HOST_AGAIN);
+
+ /* Check that guest can use private memory after focus area is remapped as private */
+ TDX_UPM_TEST_ASSERT(
+ fill_and_check(test_area_gva_private, PATTERN_GUEST_GENERAL));
+
+ tdx_test_success();
+}
+
+static void run_selftest(struct kvm_vm *vm, struct kvm_vcpu *vcpu,
+ struct tdx_upm_test_area *test_area_base_hva)
+{
+ vcpu_run(vcpu);
+ TDX_TEST_CHECK_GUEST_FAILURE(vcpu);
+ TDX_TEST_ASSERT_IO(vcpu, TDX_TEST_REPORT_PORT, TDX_TEST_REPORT_SIZE,
+ TDG_VP_VMCALL_INSTRUCTION_IO_WRITE);
+ ASSERT_EQ(*(uint32_t *)((void *)vcpu->run + vcpu->run->io.data_offset),
+ SYNC_CHECK_READ_PRIVATE_MEMORY_FROM_HOST);
+
+ /*
+ * Check that host should read PATTERN_CONFIDENCE_CHECK from guest's
+ * private memory. This confirms that regular memory (userspace_addr in
+ * struct kvm_userspace_memory_region) is used to back the host's view
+ * of private memory, since PATTERN_CONFIDENCE_CHECK was written to that
+ * memory before starting the guest.
+ */
+ TEST_ASSERT(check_test_area(test_area_base_hva, PATTERN_CONFIDENCE_CHECK),
+ "Host should read PATTERN_CONFIDENCE_CHECK from guest's private memory.");
+
+ vcpu_run_and_manage_memory_conversions(vm, vcpu);
+ TDX_TEST_CHECK_GUEST_FAILURE(vcpu);
+ TDX_TEST_ASSERT_IO(vcpu, TDX_TEST_REPORT_PORT, TDX_TEST_REPORT_SIZE,
+ TDG_VP_VMCALL_INSTRUCTION_IO_WRITE);
+ ASSERT_EQ(*(uint32_t *)((void *)vcpu->run + vcpu->run->io.data_offset),
+ SYNC_CHECK_READ_SHARED_MEMORY_FROM_HOST);
+
+ TEST_ASSERT(check_focus_area(test_area_base_hva, PATTERN_GUEST_FOCUS),
+ "Host should have the same view of shared memory as guest.");
+ TEST_ASSERT(check_general_areas(test_area_base_hva, PATTERN_CONFIDENCE_CHECK),
+ "Host's view of private memory should still be backed by regular memory.");
+
+ /* Check that host can use shared memory */
+ fill_focus_area(test_area_base_hva, PATTERN_HOST_FOCUS);
+ TEST_ASSERT(check_focus_area(test_area_base_hva, PATTERN_HOST_FOCUS),
+ "Host should be able to use shared memory.");
+
+ vcpu_run_and_manage_memory_conversions(vm, vcpu);
+ TDX_TEST_CHECK_GUEST_FAILURE(vcpu);
+ TDX_TEST_ASSERT_IO(vcpu, TDX_TEST_REPORT_PORT, TDX_TEST_REPORT_SIZE,
+ TDG_VP_VMCALL_INSTRUCTION_IO_WRITE);
+ ASSERT_EQ(*(uint32_t *)((void *)vcpu->run + vcpu->run->io.data_offset),
+ SYNC_CHECK_READ_PRIVATE_MEMORY_FROM_HOST_AGAIN);
+
+ TEST_ASSERT(check_general_areas(test_area_base_hva, PATTERN_CONFIDENCE_CHECK),
+ "Host's view of private memory should be backed by regular memory.");
+ TEST_ASSERT(check_focus_area(test_area_base_hva, PATTERN_HOST_FOCUS),
+ "Host's view of private memory should be backed by regular memory.");
+
+ vcpu_run(vcpu);
+ TDX_TEST_CHECK_GUEST_FAILURE(vcpu);
+ TDX_TEST_ASSERT_SUCCESS(vcpu);
+
+ printf("\t ... PASSED\n");
+}
+
+static bool address_between(uint64_t addr, void *lo, void *hi)
+{
+ return (uint64_t)lo <= addr && addr < (uint64_t)hi;
+}
+
+static void guest_ve_handler(struct ex_regs *regs)
+{
+ uint64_t ret;
+ struct ve_info ve;
+
+ ret = tdg_vp_veinfo_get(&ve);
+ TDX_UPM_TEST_ASSERT(!ret);
+
+ /* For this test, we will only handle EXIT_REASON_EPT_VIOLATION */
+ TDX_UPM_TEST_ASSERT(ve.exit_reason == EXIT_REASON_EPT_VIOLATION);
+
+ /* Validate GPA in fault */
+ TDX_UPM_TEST_ASSERT(
+ address_between(ve.gpa,
+ test_area_gpa_private->focus_area,
+ test_area_gpa_private->general_area_1));
+
+ tdx_test_send_64bit(TDX_UPM_TEST_ACCEPT_PRINT_PORT, ve.gpa);
+
+#define MEM_PAGE_ACCEPT_LEVEL_4K 0
+#define MEM_PAGE_ACCEPT_LEVEL_2M 1
+ ret = tdg_mem_page_accept(ve.gpa, MEM_PAGE_ACCEPT_LEVEL_4K);
+ TDX_UPM_TEST_ASSERT(!ret);
+}
+
+static void verify_upm_test(void)
+{
+ struct kvm_vm *vm;
+ struct kvm_vcpu *vcpu;
+
+ vm_vaddr_t test_area_gva_private;
+ struct tdx_upm_test_area *test_area_base_hva;
+ uint64_t test_area_npages;
+
+ vm = td_create();
+ td_initialize(vm, VM_MEM_SRC_ANONYMOUS, 0);
+ vcpu = td_vcpu_add(vm, 0, guest_upm_explicit);
+
+ vm_install_exception_handler(vm, VE_VECTOR, guest_ve_handler);
+
+ /*
+ * Set up shared memory page for testing by first allocating as private
+ * and then mapping the same GPA again as shared. This way, the TD does
+ * not have to remap its page tables at runtime.
+ */
+ test_area_npages = TDX_UPM_TEST_AREA_SIZE / vm->page_size;
+ vm_userspace_mem_region_add(vm,
+ VM_MEM_SRC_ANONYMOUS_AND_RESTRICTED_MEMFD,
+ TDX_UPM_TEST_AREA_GPA, 3, test_area_npages,
+ 0);
+
+ test_area_gva_private = _vm_vaddr_alloc(
+ vm, TDX_UPM_TEST_AREA_SIZE, TDX_UPM_TEST_AREA_GVA_PRIVATE,
+ TDX_UPM_TEST_AREA_GPA, 3, true);
+ ASSERT_EQ(test_area_gva_private, TDX_UPM_TEST_AREA_GVA_PRIVATE);
+
+ test_area_gpa_private = (struct tdx_upm_test_area *)
+ addr_gva2gpa(vm, test_area_gva_private);
+ virt_map_shared(vm, TDX_UPM_TEST_AREA_GVA_SHARED,
+ (uint64_t)test_area_gpa_private,
+ test_area_npages);
+ ASSERT_EQ(addr_gva2gpa(vm, TDX_UPM_TEST_AREA_GVA_SHARED),
+ (vm_paddr_t)test_area_gpa_private);
+
+ test_area_base_hva = addr_gva2hva(vm, TDX_UPM_TEST_AREA_GVA_PRIVATE);
+
+ TEST_ASSERT(fill_and_check(test_area_base_hva, PATTERN_CONFIDENCE_CHECK),
+ "Failed to mark memory intended as backing memory for TD shared memory");
+
+ sync_global_to_guest(vm, test_area_gpa_private);
+ test_area_gpa_shared = (struct tdx_upm_test_area *)
+ ((uint64_t)test_area_gpa_private | BIT_ULL(vm->pa_bits - 1));
+ sync_global_to_guest(vm, test_area_gpa_shared);
+
+ td_finalize(vm);
+
+ printf("Verifying UPM functionality: explicit MapGPA\n");
+
+ run_selftest(vm, vcpu, test_area_base_hva);
+
+ kvm_vm_free(vm);
+}
+
+int main(int argc, char **argv)
+{
+ /* Disable stdout buffering */
+ setbuf(stdout, NULL);
+
+ if (!is_tdx_enabled()) {
+ printf("TDX is not supported by the KVM\n"
+ "Skipping the TDX tests.\n");
+ return 0;
+ }
+
+ run_in_new_process(&verify_upm_test);
+}
--
2.39.0.246.g2a6d74b583-goog
On Fri, Jan 20, 2023 at 4:28 PM Sean Christopherson <[email protected]> wrote:
>
> On Sat, Jan 21, 2023, Ackerley Tng wrote:
> > Some SSE instructions assume a 16-byte aligned stack, and GCC compiles
> > assuming the stack is aligned:
> > https://gcc.gnu.org/bugzilla/show_bug.cgi?id=40838. This combination
> > results in a #GP in guests.
> >
> > Adding this compiler flag will generate an alternate prologue and
> > epilogue to realign the runtime stack, which makes selftest code
> > slower and bigger, but this is okay since we do not need selftest code
> > to be extremely performant.
>
> Huh, I had completely forgotten that this is why SSE is problematic. I ran into
> this with the base UPM selftests and just disabled SSE. /facepalm.
>
> We should figure out exactly what is causing a misaligned stack. As you've noted,
> the x86-64 ABI requires a 16-byte aligned RSP. Unless I'm misreading vm_arch_vcpu_add(),
> the starting stack should be page aligned, which means something is causing the
> stack to become unaligned at runtime. I'd rather hunt down that something than
> paper over it by having the compiler force realignment.
Is not it due to the 32bit execution part of the guest code at boot
time. Any push/pop of 32bit registers might make it a 16-byte
unaligned stack.
>
> > Similar issue discussed at
> > https://lore.kernel.org/all/CAGtprH9yKvuaF5yruh3BupQe4BxDGiBQk3ExtY2m39yP-tppsg@mail.gmail.com/
> >
> > Signed-off-by: Ackerley Tng <[email protected]>
> > ---
> > tools/testing/selftests/kvm/Makefile | 2 +-
> > 1 file changed, 1 insertion(+), 1 deletion(-)
> >
> > diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
> > index 317927d9c55bd..5f9cc1e6ee67e 100644
> > --- a/tools/testing/selftests/kvm/Makefile
> > +++ b/tools/testing/selftests/kvm/Makefile
> > @@ -205,7 +205,7 @@ LINUX_TOOL_ARCH_INCLUDE = $(top_srcdir)/tools/arch/x86/include
> > else
> > LINUX_TOOL_ARCH_INCLUDE = $(top_srcdir)/tools/arch/$(ARCH)/include
> > endif
> > -CFLAGS += -Wall -Wstrict-prototypes -Wuninitialized -O2 -g -std=gnu99 \
> > +CFLAGS += -mstackrealign -Wall -Wstrict-prototypes -Wuninitialized -O2 -g -std=gnu99 \
> > -fno-stack-protector -fno-PIE -I$(LINUX_TOOL_INCLUDE) \
> > -I$(LINUX_TOOL_ARCH_INCLUDE) -I$(LINUX_HDR_PATH) -Iinclude \
> > -I$(<D) -Iinclude/$(UNAME_M) -I ../rseq -I.. $(EXTRA_CFLAGS) \
> > --
> > 2.39.0.246.g2a6d74b583-goog
> >
On Mon, Jan 23, 2023, Maciej S. Szmigiero wrote:
> On 23.01.2023 19:30, Erdem Aktas wrote:
> > On Fri, Jan 20, 2023 at 4:28 PM Sean Christopherson <[email protected]> wrote:
> > >
> > > On Sat, Jan 21, 2023, Ackerley Tng wrote:
> > > > Some SSE instructions assume a 16-byte aligned stack, and GCC compiles
> > > > assuming the stack is aligned:
> > > > https://gcc.gnu.org/bugzilla/show_bug.cgi?id=40838. This combination
> > > > results in a #GP in guests.
> > > >
> > > > Adding this compiler flag will generate an alternate prologue and
> > > > epilogue to realign the runtime stack, which makes selftest code
> > > > slower and bigger, but this is okay since we do not need selftest code
> > > > to be extremely performant.
> > >
> > > Huh, I had completely forgotten that this is why SSE is problematic. I ran into
> > > this with the base UPM selftests and just disabled SSE. /facepalm.
> > >
> > > We should figure out exactly what is causing a misaligned stack. As you've noted,
> > > the x86-64 ABI requires a 16-byte aligned RSP. Unless I'm misreading vm_arch_vcpu_add(),
> > > the starting stack should be page aligned, which means something is causing the
> > > stack to become unaligned at runtime. I'd rather hunt down that something than
> > > paper over it by having the compiler force realignment.
> >
> > Is not it due to the 32bit execution part of the guest code at boot
> > time. Any push/pop of 32bit registers might make it a 16-byte
> > unaligned stack.
>
> 32-bit stack needs to be 16-byte aligned, too (at function call boundaries) -
> see [1] chapter 2.2.2 "The Stack Frame"
And this showing up in the non-TDX selftests rules that out as the sole problem;
the selftests stuff 64-bit mode, i.e. don't have 32-bit boot code.
On 23.01.2023 19:30, Erdem Aktas wrote:
> On Fri, Jan 20, 2023 at 4:28 PM Sean Christopherson <[email protected]> wrote:
>>
>> On Sat, Jan 21, 2023, Ackerley Tng wrote:
>>> Some SSE instructions assume a 16-byte aligned stack, and GCC compiles
>>> assuming the stack is aligned:
>>> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=40838. This combination
>>> results in a #GP in guests.
>>>
>>> Adding this compiler flag will generate an alternate prologue and
>>> epilogue to realign the runtime stack, which makes selftest code
>>> slower and bigger, but this is okay since we do not need selftest code
>>> to be extremely performant.
>>
>> Huh, I had completely forgotten that this is why SSE is problematic. I ran into
>> this with the base UPM selftests and just disabled SSE. /facepalm.
>>
>> We should figure out exactly what is causing a misaligned stack. As you've noted,
>> the x86-64 ABI requires a 16-byte aligned RSP. Unless I'm misreading vm_arch_vcpu_add(),
>> the starting stack should be page aligned, which means something is causing the
>> stack to become unaligned at runtime. I'd rather hunt down that something than
>> paper over it by having the compiler force realignment.
>
> Is not it due to the 32bit execution part of the guest code at boot
> time. Any push/pop of 32bit registers might make it a 16-byte
> unaligned stack.
32-bit stack needs to be 16-byte aligned, too (at function call boundaries) -
see [1] chapter 2.2.2 "The Stack Frame"
Thanks,
Maciej
[1]: https://www.uclibc.org/docs/psABI-i386.pdf
On Mon, Jan 23, 2023 at 10:53 AM Sean Christopherson <[email protected]> wrote:
>
> On Mon, Jan 23, 2023, Maciej S. Szmigiero wrote:
> > On 23.01.2023 19:30, Erdem Aktas wrote:
> > > On Fri, Jan 20, 2023 at 4:28 PM Sean Christopherson <[email protected]> wrote:
> > > >
> > > > On Sat, Jan 21, 2023, Ackerley Tng wrote:
> > > > > Some SSE instructions assume a 16-byte aligned stack, and GCC compiles
> > > > > assuming the stack is aligned:
> > > > > https://gcc.gnu.org/bugzilla/show_bug.cgi?id=40838. This combination
> > > > > results in a #GP in guests.
> > > > >
> > > > > Adding this compiler flag will generate an alternate prologue and
> > > > > epilogue to realign the runtime stack, which makes selftest code
> > > > > slower and bigger, but this is okay since we do not need selftest code
> > > > > to be extremely performant.
> > > >
> > > > Huh, I had completely forgotten that this is why SSE is problematic. I ran into
> > > > this with the base UPM selftests and just disabled SSE. /facepalm.
> > > >
> > > > We should figure out exactly what is causing a misaligned stack. As you've noted,
> > > > the x86-64 ABI requires a 16-byte aligned RSP. Unless I'm misreading vm_arch_vcpu_add(),
> > > > the starting stack should be page aligned, which means something is causing the
> > > > stack to become unaligned at runtime. I'd rather hunt down that something than
> > > > paper over it by having the compiler force realignment.
> > >
> > > Is not it due to the 32bit execution part of the guest code at boot
> > > time. Any push/pop of 32bit registers might make it a 16-byte
> > > unaligned stack.
> >
> > 32-bit stack needs to be 16-byte aligned, too (at function call boundaries) -
> > see [1] chapter 2.2.2 "The Stack Frame"
>
> And this showing up in the non-TDX selftests rules that out as the sole problem;
> the selftests stuff 64-bit mode, i.e. don't have 32-bit boot code.
Thanks Maciej and Sean for the clarification. I was suspecting the
hand-coded assembly part that we have for TDX tests but it being
happening in the non-TDX selftests disproves it.
On Mon, Jan 23, 2023, Erdem Aktas wrote:
> On Mon, Jan 23, 2023 at 10:53 AM Sean Christopherson <[email protected]> wrote:
> >
> > On Mon, Jan 23, 2023, Maciej S. Szmigiero wrote:
> > > On 23.01.2023 19:30, Erdem Aktas wrote:
> > > > On Fri, Jan 20, 2023 at 4:28 PM Sean Christopherson <[email protected]> wrote:
> > > > >
> > > > > On Sat, Jan 21, 2023, Ackerley Tng wrote:
> > > > > > Some SSE instructions assume a 16-byte aligned stack, and GCC compiles
> > > > > > assuming the stack is aligned:
> > > > > > https://gcc.gnu.org/bugzilla/show_bug.cgi?id=40838. This combination
> > > > > > results in a #GP in guests.
> > > > > >
> > > > > > Adding this compiler flag will generate an alternate prologue and
> > > > > > epilogue to realign the runtime stack, which makes selftest code
> > > > > > slower and bigger, but this is okay since we do not need selftest code
> > > > > > to be extremely performant.
> > > > >
> > > > > Huh, I had completely forgotten that this is why SSE is problematic. I ran into
> > > > > this with the base UPM selftests and just disabled SSE. /facepalm.
> > > > >
> > > > > We should figure out exactly what is causing a misaligned stack. As you've noted,
> > > > > the x86-64 ABI requires a 16-byte aligned RSP. Unless I'm misreading vm_arch_vcpu_add(),
> > > > > the starting stack should be page aligned, which means something is causing the
> > > > > stack to become unaligned at runtime. I'd rather hunt down that something than
> > > > > paper over it by having the compiler force realignment.
> > > >
> > > > Is not it due to the 32bit execution part of the guest code at boot
> > > > time. Any push/pop of 32bit registers might make it a 16-byte
> > > > unaligned stack.
> > >
> > > 32-bit stack needs to be 16-byte aligned, too (at function call boundaries) -
> > > see [1] chapter 2.2.2 "The Stack Frame"
> >
> > And this showing up in the non-TDX selftests rules that out as the sole problem;
> > the selftests stuff 64-bit mode, i.e. don't have 32-bit boot code.
>
> Thanks Maciej and Sean for the clarification. I was suspecting the
> hand-coded assembly part that we have for TDX tests but it being
> happening in the non-TDX selftests disproves it.
Not necessarily, it could be both. Goofs in the handcoded assembly and PEBKAC
on my end :-)
> On Mon, Jan 23, 2023, Erdem Aktas wrote:
> > On Mon, Jan 23, 2023 at 10:53 AM Sean Christopherson
> <[email protected]> wrote:
> > >
> > > On Mon, Jan 23, 2023, Maciej S. Szmigiero wrote:
> > > > On 23.01.2023 19:30, Erdem Aktas wrote:
> > > > > On Fri, Jan 20, 2023 at 4:28 PM Sean Christopherson
> <[email protected]> wrote:
> > > > > >
> > > > > > On Sat, Jan 21, 2023, Ackerley Tng wrote:
> > > > > > > Some SSE instructions assume a 16-byte aligned stack, and GCC
> compiles
> > > > > > > assuming the stack is aligned:
> > > > > > > https://gcc.gnu.org/bugzilla/show_bug.cgi?id=40838. This
> combination
> > > > > > > results in a #GP in guests.
> > > > > > >
> > > > > > > Adding this compiler flag will generate an alternate prologue
> and
> > > > > > > epilogue to realign the runtime stack, which makes selftest
> code
> > > > > > > slower and bigger, but this is okay since we do not need
> selftest code
> > > > > > > to be extremely performant.
> > > > > >
> > > > > > Huh, I had completely forgotten that this is why SSE is
> problematic. I ran into
> > > > > > this with the base UPM selftests and just disabled SSE.
> /facepalm.
> > > > > >
> > > > > > We should figure out exactly what is causing a misaligned
> stack. As you've noted,
> > > > > > the x86-64 ABI requires a 16-byte aligned RSP. Unless I'm
> misreading vm_arch_vcpu_add(),
> > > > > > the starting stack should be page aligned, which means
> something is causing the
> > > > > > stack to become unaligned at runtime. I'd rather hunt down
> that something than
> > > > > > paper over it by having the compiler force realignment.
> > > > >
> > > > > Is not it due to the 32bit execution part of the guest code at
> boot
> > > > > time. Any push/pop of 32bit registers might make it a 16-byte
> > > > > unaligned stack.
> > > >
> > > > 32-bit stack needs to be 16-byte aligned, too (at function call
> boundaries) -
> > > > see [1] chapter 2.2.2 "The Stack Frame"
> > >
> > > And this showing up in the non-TDX selftests rules that out as the
> sole problem;
> > > the selftests stuff 64-bit mode, i.e. don't have 32-bit boot code.
> >
> > Thanks Maciej and Sean for the clarification. I was suspecting the
> > hand-coded assembly part that we have for TDX tests but it being
> > happening in the non-TDX selftests disproves it.
> Not necessarily, it could be both. Goofs in the handcoded assembly and
> PEBKAC
> on my end :-)
I figured it out!
GCC assumes that the stack is 16-byte aligned **before** the call
instruction. Since call pushes rip to the stack, GCC will compile code
assuming that on entrance to the function, the stack is -8 from a
16-byte aligned address.
Since for TDs we do a ljmp to guest code, providing a function's
address, the stack was not modified by a call instruction pushing rip to
the stack, so the stack is 16-byte aligned when the guest code starts
running, instead of 16-byte aligned -8 that GCC expects.
For VMs, we set rip to a function pointer, and the VM starts running
with a 16-byte algined stack too.
To fix this, I propose that in vm_arch_vcpu_add(), we align the
allocated stack address and then subtract 8 from that:
@@ -573,10 +573,13 @@ struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm,
uint32_t vcpu_id,
vcpu_init_cpuid(vcpu, kvm_get_supported_cpuid());
vcpu_setup(vm, vcpu);
+ stack_vaddr += (DEFAULT_STACK_PGS * getpagesize());
+ stack_vaddr = ALIGN_DOWN(stack_vaddr, 16) - 8;
+
/* Setup guest general purpose registers */
vcpu_regs_get(vcpu, ®s);
regs.rflags = regs.rflags | 0x2;
- regs.rsp = stack_vaddr + (DEFAULT_STACK_PGS * getpagesize());
+ regs.rsp = stack_vaddr;
regs.rip = (unsigned long) guest_code;
vcpu_regs_set(vcpu, ®s);
On 15.02.2023 01:50, Ackerley Tng wrote:
>
>> On Mon, Jan 23, 2023, Erdem Aktas wrote:
>> > On Mon, Jan 23, 2023 at 10:53 AM Sean Christopherson <[email protected]> wrote:
>> > >
>> > > On Mon, Jan 23, 2023, Maciej S. Szmigiero wrote:
>> > > > On 23.01.2023 19:30, Erdem Aktas wrote:
>> > > > > On Fri, Jan 20, 2023 at 4:28 PM Sean Christopherson <[email protected]> wrote:
>> > > > > >
>> > > > > > On Sat, Jan 21, 2023, Ackerley Tng wrote:
>> > > > > > > Some SSE instructions assume a 16-byte aligned stack, and GCC compiles
>> > > > > > > assuming the stack is aligned:
>> > > > > > > https://gcc.gnu.org/bugzilla/show_bug.cgi?id=40838. This combination
>> > > > > > > results in a #GP in guests.
>> > > > > > >
>> > > > > > > Adding this compiler flag will generate an alternate prologue and
>> > > > > > > epilogue to realign the runtime stack, which makes selftest code
>> > > > > > > slower and bigger, but this is okay since we do not need selftest code
>> > > > > > > to be extremely performant.
>> > > > > >
>> > > > > > Huh, I had completely forgotten that this is why SSE is problematic. I ran into
>> > > > > > this with the base UPM selftests and just disabled SSE. /facepalm.
>> > > > > >
>> > > > > > We should figure out exactly what is causing a misaligned stack. As you've noted,
>> > > > > > the x86-64 ABI requires a 16-byte aligned RSP. Unless I'm misreading vm_arch_vcpu_add(),
>> > > > > > the starting stack should be page aligned, which means something is causing the
>> > > > > > stack to become unaligned at runtime. I'd rather hunt down that something than
>> > > > > > paper over it by having the compiler force realignment.
>> > > > >
>> > > > > Is not it due to the 32bit execution part of the guest code at boot
>> > > > > time. Any push/pop of 32bit registers might make it a 16-byte
>> > > > > unaligned stack.
>> > > >
>> > > > 32-bit stack needs to be 16-byte aligned, too (at function call boundaries) -
>> > > > see [1] chapter 2.2.2 "The Stack Frame"
>> > >
>> > > And this showing up in the non-TDX selftests rules that out as the sole problem;
>> > > the selftests stuff 64-bit mode, i.e. don't have 32-bit boot code.
>> >
>> > Thanks Maciej and Sean for the clarification. I was suspecting the
>> > hand-coded assembly part that we have for TDX tests but it being
>> > happening in the non-TDX selftests disproves it.
>
>> Not necessarily, it could be both. Goofs in the handcoded assembly and PEBKAC
>> on my end :-)
>
> I figured it out!
>
> GCC assumes that the stack is 16-byte aligned **before** the call
> instruction. Since call pushes rip to the stack, GCC will compile code
> assuming that on entrance to the function, the stack is -8 from a
> 16-byte aligned address.
>
> Since for TDs we do a ljmp to guest code, providing a function's
> address, the stack was not modified by a call instruction pushing rip to
> the stack, so the stack is 16-byte aligned when the guest code starts
> running, instead of 16-byte aligned -8 that GCC expects.
>
> For VMs, we set rip to a function pointer, and the VM starts running
> with a 16-byte algined stack too.
>
> To fix this, I propose that in vm_arch_vcpu_add(), we align the
> allocated stack address and then subtract 8 from that:
>
Note that if this code is ever used to launch a vCPU with 32-bit entry
point it will need to subtract 4 bytes instead of 8 bytes.
I think it would be worthwhile to at least place a comment mentioning
this near the stack aligning expression so nobody misses this fact.
Thanks,
Maciej
On Wed, Feb 15, 2023, Maciej S. Szmigiero wrote:
> On 15.02.2023 01:50, Ackerley Tng wrote:
> > To fix this, I propose that in vm_arch_vcpu_add(), we align the
> > allocated stack address and then subtract 8 from that:
>
> Note that if this code is ever used to launch a vCPU with 32-bit entry
> point it will need to subtract 4 bytes instead of 8 bytes.
>
> I think it would be worthwhile to at least place a comment mentioning
> this near the stack aligning expression so nobody misses this fact.
Heh, I've no objection to a comment, though this really is the tip of the iceberg
if we want to add 32-bit guest support in selftests.
On Wed, Feb 15, 2023, Ackerley Tng wrote:
> I figured it out!
>
> GCC assumes that the stack is 16-byte aligned **before** the call
> instruction. Since call pushes rip to the stack, GCC will compile code
> assuming that on entrance to the function, the stack is -8 from a
> 16-byte aligned address.
>
> Since for TDs we do a ljmp to guest code, providing a function's
> address, the stack was not modified by a call instruction pushing rip to
> the stack, so the stack is 16-byte aligned when the guest code starts
> running, instead of 16-byte aligned -8 that GCC expects.
>
> For VMs, we set rip to a function pointer, and the VM starts running
> with a 16-byte algined stack too.
>
> To fix this, I propose that in vm_arch_vcpu_add(), we align the
> allocated stack address and then subtract 8 from that:
>
> @@ -573,10 +573,13 @@ struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm,
> uint32_t vcpu_id,
> vcpu_init_cpuid(vcpu, kvm_get_supported_cpuid());
> vcpu_setup(vm, vcpu);
>
> + stack_vaddr += (DEFAULT_STACK_PGS * getpagesize());
> + stack_vaddr = ALIGN_DOWN(stack_vaddr, 16) - 8;
The ALIGN_DOWN should be unnecessary, we've got larger issues if getpagesize() isn't
16-byte aligned and/or if __vm_vaddr_alloc() returns anything but a page-aligned
address. Maybe add a TEST_ASSERT() sanity check that stack_vaddr is page-aligned
at this point?
And in addition to the comment suggested by Maciej, can you also add a comment
explaining the -8 adjust? Yeah, someone can go read the changelog, but I think
this is worth explicitly documenting in code.
Lastly, can you post it as a standalone patch?
Many thanks!
> > I figured it out!
> >
> > GCC assumes that the stack is 16-byte aligned **before** the call
> > instruction. Since call pushes rip to the stack, GCC will compile code
> > assuming that on entrance to the function, the stack is -8 from a
> > 16-byte aligned address.
> >
> > Since for TDs we do a ljmp to guest code, providing a function's
> > address, the stack was not modified by a call instruction pushing rip to
> > the stack, so the stack is 16-byte aligned when the guest code starts
> > running, instead of 16-byte aligned -8 that GCC expects.
> >
> > For VMs, we set rip to a function pointer, and the VM starts running
> > with a 16-byte algined stack too.
> >
> > To fix this, I propose that in vm_arch_vcpu_add(), we align the
> > allocated stack address and then subtract 8 from that:
> >
> > @@ -573,10 +573,13 @@ struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm
> *vm,
> > uint32_t vcpu_id,
> > vcpu_init_cpuid(vcpu, kvm_get_supported_cpuid());
> > vcpu_setup(vm, vcpu);
> >
> > + stack_vaddr += (DEFAULT_STACK_PGS * getpagesize());
> > + stack_vaddr = ALIGN_DOWN(stack_vaddr, 16) - 8;
> The ALIGN_DOWN should be unnecessary, we've got larger issues if
> getpagesize() isn't
> 16-byte aligned and/or if __vm_vaddr_alloc() returns anything but a
> page-aligned
> address. Maybe add a TEST_ASSERT() sanity check that stack_vaddr is
> page-aligned
> at this point?
> And in addition to the comment suggested by Maciej, can you also add a
> comment
> explaining the -8 adjust? Yeah, someone can go read the changelog, but I
> think
> this is worth explicitly documenting in code.
> Lastly, can you post it as a standalone patch?
> Many thanks!
Thanks Maciej and Sean, I've made the changes you requested and posted
it as a standalone patch at
https://lore.kernel.org/lkml/32866e5d00174697730d6231d2fb81f6b8d98c8a.1676659352.git.ackerleytng@google.com/