TDX KVM selftests
Subsystems (8)
v14 cover TDX KVM selftests
This patch series focuses on setting up a TDX VM and adding all code necessary to run a basic lifecycle test.
Unlike standard KVM selftests can set up the VM through guest registers, TDX module protects TDs' register state from the host. This feature of TDX causes problems on VM boot state initialization and the ucall implementation.
In standard KVM selftests, the host directly initializes the guest state by manipulating Special Registers (SREGs) and General Purpose Registers (GPRs) via IOCTLs (KVM_SET_SREGS, etc.) before the first KVM_RUN.
To bypass direct register initialization by the host, we utilize the standard x86 reset vector as the default entry point.
The mechanism works as follows:
1. The host places register values into a specific memory region and inserts boot code at the VM's default starting point.
2. When the VM starts, it executes this boot code to "pull" values from memory and manually set up its own SREGs and GPRs.
3. Once the environment is ready, the boot code jumps to the guest code.
The standard x86 ucall() implementation uses PIO, but it does not actually transmit data through the 4-byte PIO data. Instead, it relies on the host reading the ucall address directly from the guest's RDI register.
TDX selftests cannot utilize the standard x86 ucall implementation, because the host is unable to access the guest's RDI register. Based on this restriction, we used TDCALL with an 8-byte MMIO data to implement ucall. This method is the cleanest and efficient implementation for the overall ucall architecture.
v14 revision for TDX KVM selftests based on kvm/next and guest_memfd:
In-place conversion support[1]. This series needs some changes from in-place conversion v9. For ease of testing, an extra hack commit has been added. The complete code is available at:
https://github.com/googleprodkernel/linux-cc/commits/tdx-selftests-v14
Changes from v13[2]:
1. Fixed some bugs, including typo and commit messages.
2. Outlined TDCALL from tdx.c to tdx.S.
3. Used HPET(0xfed00000) for ucall GPA address.
4. Supported guest_memfd w/wo in-place conversion support.
Thanks review from Ackerley, Binbin, Chenyi, Sean and Xiaoyao!
Series is organized by:
1. Patches 1 - 4: Initialize the TDX VM
2. Patches 5 - 8: Add the TDX boot code
3. Patches 9 - 13: Set up the boot region
4. Patches 14 - 17: Set up the vCPU
5. Patches 18 - 19: Finalize the TDX VM
6. Patches 20 - 22: Implement the ucall and run the TDX test
[1]: https://lore.kernel.org/all/[email protected]/T/
[2]: https://lore.kernel.org/all/[email protected]/
---
Ackerley Tng (2):
KVM: selftests: Add helpers to init TDX memory and finalize VM
KVM: selftests: Add ucall support for TDX
Erdem Aktas (2):
KVM: selftests: Add TDX boot code
KVM: selftests: Implement MMIO WRITE for the TDX VM
Isaku Yamahata (2):
KVM: selftests: Update kvm_init_vm_address_properties() for TDX
KVM: selftests: TDX: Use KVM_TDX_CAPABILITIES to validate TDs' attribute configuration
Lisa Wang (2):
KVM: selftests: Require guest_memfd for TDX VMs
KVM: selftests: Support guest_memfd in-place conversion
Sagi Shahar (13):
KVM: selftests: Initialize the TDX VM
KVM: selftests: Expose segment definitions to assembly files
tools: include: Add kbuild.h for assembly structure offsets
KVM: selftests: Introduce structures for TDX guest boot parameters
KVM: selftests: Expose functions to get default sregs values
KVM: selftests: Set up TDX boot code region
KVM: selftests: Set up TDX boot parameters region
KVM: selftests: Expose function to allocate vCPU stack
KVM: selftests: Call KVM_TDX_INIT_VCPU when creating a new TDX vcpu
KVM: selftests: Load per-vCPU guest stack in TDX boot parameters
KVM: selftests: Set entry point for TDX guest code
KVM: selftests: Finalize TD memory as part of kvm_arch_vm_finalize_vcpus
KVM: selftests: Add TDX lifecycle test
Sean Christopherson (1):
KVM: selftests: Add macros to simplify creating VM shapes for non-default types
tools/include/linux/kbuild.h | 11 + tools/testing/selftests/kvm/.gitignore | 3 +- tools/testing/selftests/kvm/Makefile.kvm | 33 ++- tools/testing/selftests/kvm/include/kvm_util.h | 27 +- .../testing/selftests/kvm/include/x86/processor.h | 44 +++ .../selftests/kvm/include/x86/processor_asm.h | 12 + tools/testing/selftests/kvm/include/x86/sev.h | 2 - .../selftests/kvm/include/x86/tdx/td_boot.h | 82 ++++++ tools/testing/selftests/kvm/include/x86/tdx/tdx.h | 17 ++ .../selftests/kvm/include/x86/tdx/tdx_util.h | 82 ++++++ tools/testing/selftests/kvm/include/x86/ucall.h | 6 - tools/testing/selftests/kvm/lib/kvm_util.c | 22 +- tools/testing/selftests/kvm/lib/ucall_common.c | 8 + tools/testing/selftests/kvm/lib/x86/processor.c | 125 +++++--- tools/testing/selftests/kvm/lib/x86/sev.c | 16 -- tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S | 61 ++++ .../selftests/kvm/lib/x86/tdx/td_boot_offsets.c | 21 ++ tools/testing/selftests/kvm/lib/x86/tdx/tdx.S | 37 +++ tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c | 316 +++++++++++++++++++++ tools/testing/selftests/kvm/lib/x86/ucall.c | 33 +++ tools/testing/selftests/kvm/x86/sev_smoke_test.c | 40 +-- tools/testing/selftests/kvm/x86/tdx_vm_test.c | 33 +++ 22 files changed, 927 insertions(+), 104 deletions(-)
---
base-commit: 2145a419fb2a4cdc8eefc8497425d2827f46e660
change-id: 20260722-tdx-selftests-1da5587cf047
Best regards,
Signature · 1 line
Lisa Wang <[email protected]>
PATCH v14 1/22 KVM: selftests: Add macros to simplify creating VM shapes for non-default types
From: Sean Christopherson <[email protected]>
Add VM_TYPE() and __VM_SHAPE() macros to create a vm_shape structure given a type (and mode), and use the macros to define VM_SHAPE_{SEV,SEV_ES,SNP} shapes for x86's SEV family of VM shapes. Providing common infrastructure will avoid having to copy+paste vm_sev_create_with_one_vcpu() for TDX.
Use the new SEV+ shapes and drop vm_sev_create_with_one_vcpu().
Opportunistically move the existing VM_SHAPE() (now __VM_SHAPE()) macro below the definitions of VM_MODE_DEFAULT so that all of the SHAPE/TYPE macros are bundled together.
No functional change intended.
--- tools/testing/selftests/kvm/include/kvm_util.h | 27 ++++++++------- .../testing/selftests/kvm/include/x86/processor.h | 4 +++ tools/testing/selftests/kvm/include/x86/sev.h | 2 -- tools/testing/selftests/kvm/lib/x86/sev.c | 16 --------- tools/testing/selftests/kvm/x86/sev_smoke_test.c | 40 +++++++++++----------- 5 files changed, 39 insertions(+), 50 deletions(-)
tools/testing/selftests/kvm/include/kvm_util.h +15 -12
@@ -221,18 +221,6 @@ struct vm_shape {kvm_static_assert (sizeof (struct vm_shape ) ==sizeof (u64)); -#define VM_TYPE_DEFAULT 0 - -#define VM_SHAPE (__mode) \ -({ \ - struct vm_shape shape = { \ - .mode = (__mode), \ - .type = VM_TYPE_DEFAULT \ - }; \ - \ - shape; \ -}) -extern enum vm_guest_mode vm_mode_default;#if defined(__aarch64__)
@@ -270,8 +258,23 @@ extern enum vm_guest_mode vm_mode_default;#endif +#define VM_TYPE_DEFAULT 0 + +#define __VM_SHAPE (__mode, __type) \ +({ \ + struct vm_shape shape = { \ + .mode = (__mode), \ + .type = (__type), \ + }; \ + \ + shape; \ +}) + +#define VM_SHAPE (__mode) __VM_SHAPE(__mode, VM_TYPE_DEFAULT)#define VM_SHAPE_DEFAULT VM_SHAPE(VM_MODE_DEFAULT) +#define VM_TYPE (__type) __VM_SHAPE(VM_MODE_DEFAULT, __type) +#define MIN_PAGE_SIZE (1U << MIN_PAGE_SHIFT)#define PTES_PER_MIN_PAGE ptes_per_page(MIN_PAGE_SIZE)
tools/testing/selftests/kvm/include/x86/processor.h +4 -0
@@ -365,6 +365,10 @@ static inline unsigned int x86_model(unsigned int eax)return ((eax >>12 ) &0xf0 ) | ((eax >>4 ) &0x0f ); } +#define VM_SHAPE_SEV VM_TYPE(KVM_X86_SEV_VM) +#define VM_SHAPE_SEV_ES VM_TYPE(KVM_X86_SEV_ES_VM) +#define VM_SHAPE_SNP VM_TYPE(KVM_X86_SNP_VM) +#define PHYSICAL_PAGE_MASK GENMASK_ULL(51, 12)#define PAGE_SHIFT 12
tools/testing/selftests/kvm/include/x86/sev.h +0 -2
@@ -53,8 +53,6 @@ void snp_vm_launch_start(struct kvm_vm *vm, u64 policy);void snp_vm_launch_update (struct kvm_vm *vm);void snp_vm_launch_finish (struct kvm_vm *vm); -struct kvm_vm *vm_sev_create_with_one_vcpu (u32 type,void *guest_code, -struct kvm_vcpu **cpu);void vm_sev_launch (struct kvm_vm *vm,u64 policy,u8 *measurement);kvm_static_assert (SEV_RET_SUCCESS ==0 );
tools/testing/selftests/kvm/lib/x86/sev.c +0 -16
@@ -158,22 +158,6 @@ void snp_vm_launch_finish(struct kvm_vm *vm)vm_sev_ioctl (vm,KVM_SEV_SNP_LAUNCH_FINISH , &launch_finish); } -struct kvm_vm *vm_sev_create_with_one_vcpu (u32 type,void *guest_code, -struct kvm_vcpu **cpu) -{ -struct vm_shape shape = { - .mode =VM_MODE_DEFAULT , - .type = type, - }; -struct kvm_vm *vm; -struct kvm_vcpu *cpus[1 ]; - - vm =__vm_create_with_vcpus (shape,1 ,0 , guest_code, cpus); - *cpu = cpus[0 ]; - -return vm; -} -void vm_sev_launch (struct kvm_vm *vm,u64 policy,u8 *measurement) {if (is_sev_snp_vm (vm)) {
tools/testing/selftests/kvm/x86/sev_smoke_test.c +20 -20
@@ -104,7 +104,7 @@ static void compare_xsave(u8 *from_host, u8 *from_guest)abort (); } -static void test_sync_vmsa (u32 type,u64 policy) +static void test_sync_vmsa (struct vm_shape shape,u64 policy) {struct kvm_vcpu *vcpu;struct kvm_vm *vm;
@@ -114,7 +114,7 @@ static void test_sync_vmsa(u32 type, u64 policy)double x87val =M_PI ;struct kvm_xsave __attribute__((aligned (64 ))) xsave = {0 }; - vm =vm_sev_create_with_one_vcpu (type, guest_code_xsave, &vcpu); + vm =vm_create_shape_with_one_vcpu (shape, &vcpu, guest_code_xsave); gva =vm_alloc_shared (vm,PAGE_SIZE ,KVM_UTIL_MIN_VADDR ,MEM_REGION_TEST_DATA ); hva =addr_gva2hva (vm, gva);
@@ -150,13 +150,13 @@ static void test_sync_vmsa(u32 type, u64 policy)kvm_vm_free (vm); } -static void test_sev (void *guest_code,u32 type,u64 policy) +static void test_sev (void *guest_code,struct vm_shape shape,u64 policy) {struct kvm_vcpu *vcpu;struct kvm_vm *vm;struct ucall uc; - vm =vm_sev_create_with_one_vcpu (type, guest_code, &vcpu); + vm =vm_create_shape_with_one_vcpu (shape, &vcpu, guest_code);/* TODO: Validate the measurement is as expected. */ vm_sev_launch (vm, policy,NULL );
@@ -201,12 +201,12 @@ static void guest_shutdown_code(void) __asm__ __volatile__("ud2" ); } -static void test_sev_shutdown (u32 type,u64 policy) +static void test_sev_shutdown (struct vm_shape shape,u64 policy) {struct kvm_vcpu *vcpu;struct kvm_vm *vm; - vm =vm_sev_create_with_one_vcpu (type, guest_shutdown_code, &vcpu); + vm =vm_create_shape_with_one_vcpu (shape, &vcpu, guest_shutdown_code);vm_sev_launch (vm, policy,NULL );
@@ -218,28 +218,28 @@ static void test_sev_shutdown(u32 type, u64 policy)kvm_vm_free (vm); } -static void test_sev_smoke (void *guest,u32 type,u64 policy) +static void test_sev_smoke (void *guest,struct vm_shape shape,u64 policy) {const u64 xf_mask =XFEATURE_MASK_X87_AVX ; -if (type ==KVM_X86_SNP_VM ) -test_sev (guest, type, policy |SNP_POLICY_DBG ); +if (shape.type ==KVM_X86_SNP_VM ) +test_sev (guest, shape, policy |SNP_POLICY_DBG );else -test_sev (guest, type, policy |SEV_POLICY_NO_DBG ); -test_sev (guest, type, policy); +test_sev (guest, shape, policy |SEV_POLICY_NO_DBG ); +test_sev (guest, shape, policy); -if (type ==KVM_X86_SEV_VM ) +if (shape.type ==KVM_X86_SEV_VM )return ; -test_sev_shutdown (type, policy); +test_sev_shutdown (shape, policy);if (kvm_has_cap (KVM_CAP_XCRS ) && (xgetbv (0 ) &kvm_cpu_supported_xcr0 () & xf_mask) == xf_mask) { -test_sync_vmsa (type, policy); -if (type ==KVM_X86_SNP_VM ) -test_sync_vmsa (type, policy |SNP_POLICY_DBG ); +test_sync_vmsa (shape, policy); +if (shape.type ==KVM_X86_SNP_VM ) +test_sync_vmsa (shape, policy |SNP_POLICY_DBG );else -test_sync_vmsa (type, policy |SEV_POLICY_NO_DBG ); +test_sync_vmsa (shape, policy |SEV_POLICY_NO_DBG ); } }
@@ -247,13 +247,13 @@ int main(int argc, char *argv[]) {TEST_REQUIRE (kvm_cpu_has (X86_FEATURE_SEV )); -test_sev_smoke (guest_sev_code,KVM_X86_SEV_VM ,0 ); +test_sev_smoke (guest_sev_code,VM_SHAPE_SEV ,0 );if (kvm_cpu_has (X86_FEATURE_SEV_ES )) -test_sev_smoke (guest_sev_es_code,KVM_X86_SEV_ES_VM ,SEV_POLICY_ES ); +test_sev_smoke (guest_sev_es_code,VM_SHAPE_SEV_ES ,SEV_POLICY_ES );if (kvm_cpu_has (X86_FEATURE_SEV_SNP )) -test_sev_smoke (guest_snp_code,KVM_X86_SNP_VM ,snp_default_policy ()); +test_sev_smoke (guest_snp_code,VM_SHAPE_SNP ,snp_default_policy ());return 0 ; }
Signature · 1 line
2.55.0.229.g6434b31f56-goog
Re: [PATCH v14 01/22] KVM: selftests: Add macros to simplify creating VM shapes for non-default types
Quoted 13 lines · Lisa Wang
From: Sean Christopherson <[email protected]>
Add
VM_TYPE()and__VM_SHAPE()macros to create avm_shapestructure given a type (and mode), and use the macros to define VM_SHAPE_{SEV,SEV_ES,SNP} shapes for x86's SEV family of VM shapes. Providing common infrastructure will avoid having to copy+pastevm_sev_create_with_one_vcpu()for TDX.Use the new SEV+ shapes and drop
vm_sev_create_with_one_vcpu().Opportunistically move the existing
VM_SHAPE()(now__VM_SHAPE()) macro below the definitions ofVM_MODE_DEFAULTso that all of the SHAPE/TYPE macros are bundled together.
Quoted 6 lines
No functional change intended.
Reviewed-by Binbin Wu <[email protected]>Reviewed-by Ira Weiny <[email protected]>Reviewed-by Xiaoyao Li <[email protected]>Signed-off-by Sean Christopherson <[email protected]>
Missing the submitter's SOB?
Quoted 8 lines
--- tools/testing/selftests/kvm/include/kvm_util.h | 27 ++++++++------- .../testing/selftests/kvm/include/x86/processor.h | 4 +++ tools/testing/selftests/kvm/include/x86/sev.h | 2 -- tools/testing/selftests/kvm/lib/x86/sev.c | 16 --------- tools/testing/selftests/kvm/x86/sev_smoke_test.c | 40 +++++++++++----------- 5 files changed, 39 insertions(+), 50 deletions(-)
Re: [PATCH v14 01/22] KVM: selftests: Add macros to simplify creating VM shapes for non-default types
In reply to Lisa WangQuoted 13 lines · Lisa Wang
From: Sean Christopherson <[email protected]>
Add
VM_TYPE()and__VM_SHAPE()macros to create avm_shapestructure given a type (and mode), and use the macros to define VM_SHAPE_{SEV,SEV_ES,SNP} shapes for x86's SEV family of VM shapes. Providing common infrastructure will avoid having to copy+pastevm_sev_create_with_one_vcpu()for TDX.Use the new SEV+ shapes and drop
vm_sev_create_with_one_vcpu().Opportunistically move the existing
VM_SHAPE()(now__VM_SHAPE()) macro below the definitions ofVM_MODE_DEFAULTso that all of the SHAPE/TYPE macros are bundled together.
Quoted 9 lines
No functional change intended.
Reviewed-by Binbin Wu <[email protected]>Reviewed-by Ira Weiny <[email protected]>Reviewed-by Xiaoyao Li <[email protected]>Signed-off-by Sean Christopherson <[email protected]>[...snip...]
PATCH v14 2/22 KVM: selftests: Update kvm_init_vm_address_properties() for TDX
In reply to Lisa WangFrom: Isaku Yamahata <[email protected]>
Initialize the TDX S-bit and the GPA tag mask in kvm_init_vm_address_properties() for TDX VMs, similar to how the C-bit is initialized for SEV VMs.
The TDX S-bit is used to distinguish between shared and private guest physical addresses. Its position is determined by the guest physical address width, which is either 48 or 52 bits for current TDX implementations.
--- tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h | 14 ++++++++++++++ tools/testing/selftests/kvm/lib/x86/processor.c | 12 ++++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-)
tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h new file +14 -0
@@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef SELFTESTS_TDX_TDX_UTIL_H +#define SELFTESTS_TDX_TDX_UTIL_H + +#include <stdbool.h> + +#include "kvm_util.h" + +static inline bool is_tdx_vm (struct kvm_vm *vm) +{ +return vm->type ==KVM_X86_TDX_VM ; +} + +#endif /* SELFTESTS_TDX_TDX_UTIL_H */
tools/testing/selftests/kvm/lib/x86/processor.c +10 -2
@@ -11,6 +11,7 @@#include "smm.h" #include "svm_util.h" #include "sev.h" +#include "tdx/tdx_util.h" #include "vmx.h" #ifndef NUM_INTERRUPTS
@@ -1311,12 +1312,19 @@ void kvm_get_cpu_address_width(unsigned int *pa_bits, unsigned int *va_bits)void kvm_init_vm_address_properties (struct kvm_vm *vm) { +u32 gpa_bits =kvm_cpu_property (X86_PROPERTY_GUEST_MAX_PHY_ADDR ); + + vm->arch.sev_fd =-1 ; +if (is_sev_vm (vm)) { vm->arch.sev_fd =open_sev_dev_path_or_exit (); vm->arch.c_bit =BIT_ULL (this_cpu_property (X86_PROPERTY_SEV_C_BIT )); vm->gpa_tag_mask = vm->arch.c_bit; - }else { - vm->arch.sev_fd =-1 ; + }else if (is_tdx_vm (vm)) { +TEST_ASSERT (gpa_bits ==48 || gpa_bits ==52 , +"TDX: bad X86_PROPERTY_GUEST_MAX_PHY_ADDR value: %u" , gpa_bits); + vm->arch.s_bit =BIT_ULL (gpa_bits -1 ); + vm->gpa_tag_mask = vm->arch.s_bit; } }
Signature · 1 line
2.55.0.229.g6434b31f56-goog
Edgecombe, Rick P
maintainerRe: [PATCH v14 02/22] KVM: selftests: Update kvm_init_vm_address_properties() for TDX
Quoted 10 lines · Lisa Wang
From: Isaku Yamahata <[email protected]>
Initialize the TDX S-bit and the GPA tag mask in
kvm_init_vm_address_properties()for TDX VMs, similar to how the C-bit is initialized for SEV VMs.The TDX S-bit is used to distinguish between shared and private guest physical addresses. Its position is determined by the guest physical address width, which is either 48 or 52 bits for current TDX implementations.
Since S-bit=1 means shared and C-bit=1 means private, we can't have a single bit. I'd justify why a second field is needed. For "untagging" GPAs we could have a single field, but there are other usages?
Quoted 11 lines
Reviewed-by Binbin Wu <[email protected]>Co-developed-by Adrian Hunter <[email protected]>Signed-off-by Adrian Hunter <[email protected]>Signed-off-by Isaku Yamahata <[email protected]>Co-developed-by Sagi Shahar <[email protected]>Signed-off-by Sagi Shahar <[email protected]>Reviewed-by Ira Weiny <[email protected]>Signed-off-by Lisa Wang <[email protected]>Reviewed-by Xiaoyao Li <[email protected]>---
Nit: these are not ordered correctly. I think KVM prefers the order in:Documentation/process/maintainer-tip.rst
But I think at least the RBs can be grouped together. Also... can't really point fingers here, but that is a fair amount of of patch history.
Re: [PATCH v14 02/22] KVM: selftests: Update kvm_init_vm_address_properties() for TDX
Quoted 7 lines · Rick P
Quoted 6 lines · Lisa Wang
From: Isaku Yamahata <[email protected]>
Initialize the TDX S-bit and the GPA tag mask in
kvm_init_vm_address_properties()for TDX VMs, similar to how the C-bit is initialized for SEV VMs.
Quoted 7 lines
Quoted 4 lines
The TDX S-bit is used to distinguish between shared and private guest physical addresses. Its position is determined by the guest physical address width, which is either 48 or 52 bits for current TDX implementations.
Since S-bit=1 means shared and C-bit=1 means private, we can't have a single bit. I'd justify why a second field is needed.
The s_bit field was introduced prior to this patch series, so I think the patch introducing the s_bit field should have justified the addition of this second field.
Did you mean that we should add something related to "Since S-bit=1 means shared and C-bit=1 means private, we can't have a single bit." in the commit message to reiterate/as a refresher?
Quoted 3 lines
For "untagging" GPAs we could
have a single field, but there are other usages?
I think this part in __virt_pg_map() requires separate fields, are you requesting to only retain one of the c_bit/s_bit vs gpa_tag_mask?
if (vm_is_gpa_protected(vm, gpa)) *pte |= PTE_C_BIT_MASK(mmu); else *pte |= PTE_S_BIT_MASK(mmu);
I think if there's any unification/simplification required among these fields, perhaps that can be left to another series.
Quoted 11 lines
Quoted 11 lines
Reviewed-by Binbin Wu <[email protected]>Co-developed-by Adrian Hunter <[email protected]>Signed-off-by Adrian Hunter <[email protected]>Signed-off-by Isaku Yamahata <[email protected]>Co-developed-by Sagi Shahar <[email protected]>Signed-off-by Sagi Shahar <[email protected]>Reviewed-by Ira Weiny <[email protected]>Signed-off-by Lisa Wang <[email protected]>Reviewed-by Xiaoyao Li <[email protected]>---
Quoted 5 lines
Nit: these are not ordered correctly. I think KVM prefers the order in:
Documentation/process/maintainer-tip.rstBut I think at least the RBs can be grouped together. Also... can't really point fingers here, but that is a fair amount of of patch history.
I was also involved in some earlier revision of this series, tracing this history is not trivial...
For this series, given the long history and many handoffs, shall we help Lisa out by explicitly requesting to be credited on specific patches? Emailing either Lisa/Ackerley privately or on-list is fine :)
PATCH v14 3/22 KVM: selftests: Initialize the TDX VM
In reply to Lisa WangFrom: Sagi Shahar <[email protected]>
Add tdx_init_vm() to handle the mandatory VM-level initialization sequence required for Intel TDX.
For TDX, the guest's CPUID configuration must be "sealed" during KVM_TDX_INIT_VM before any vCPUs are created. This is necessary because the TDX hardware directly virtualizes CPUID and includes the configuration in the guest's initial security measurement.
The helper calculates the required CPUID values by filtering the host- supported bits (kvm_get_supported_cpuid) against the "directly configurable" bits reported by KVM_TDX_CAPABILITIES, ensuring compliance with the strict requirements of the TDH.MNG.INIT SEAMCALL.
--- tools/testing/selftests/kvm/Makefile.kvm | 1 + .../testing/selftests/kvm/include/x86/processor.h | 2 + .../selftests/kvm/include/x86/tdx/tdx_util.h | 35 ++++++ tools/testing/selftests/kvm/lib/x86/processor.c | 21 +++- tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c | 120 +++++++++++++++++++++ 5 files changed, 175 insertions(+), 4 deletions(-)
tools/testing/selftests/kvm/Makefile.kvm +1 -0
@@ -27,6 +27,7 @@ LIBKVM_x86 += lib/x86/pmu.c LIBKVM_x86 += lib/x86/processor.c LIBKVM_x86 += lib/x86/sev.c LIBKVM_x86 += lib/x86/svm.c +LIBKVM_x86 += lib/x86/tdx/tdx_util.c LIBKVM_x86 += lib/x86/ucall.c LIBKVM_x86 += lib/x86/vmx.c
tools/testing/selftests/kvm/include/x86/processor.h +2 -0
@@ -956,6 +956,8 @@ static inline void vcpu_xcrs_set(struct kvm_vcpu *vcpu, struct kvm_xcrs *xcrs)vcpu_ioctl (vcpu,KVM_SET_XCRS , xcrs); } +const struct kvm_cpuid_entry2 *__get_cpuid_entry (const struct kvm_cpuid2 *cpuid, +u32 function,u32 index);const struct kvm_cpuid_entry2 *get_cpuid_entry (const struct kvm_cpuid2 *cpuid,u32 function,u32 index);const struct kvm_cpuid2 *kvm_get_supported_cpuid (void );
tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h +35 -0
@@ -11,4 +11,39 @@ static inline bool is_tdx_vm(struct kvm_vm *vm) return vm->type == KVM_X86_TDX_VM; } +/* +* TDX ioctls +* Use underscores to avoid collisions with struct member names. +*/ +#define __tdx_vm_ioctl (vm, cmd, _flags, arg) \ +({ \ + u64 r; \ + \ + union { \ + struct kvm_tdx_cmd c; \ + unsigned long raw; \ + } tdx_cmd = { .c = { \ + .id = (cmd), \ + .flags = (u32)(_flags), \ + .data = (u64)(arg), \ + } }; \ + \ + r = __vm_ioctl(vm, KVM_MEMORY_ENCRYPT_OP, &tdx_cmd.raw); \ + r ?: tdx_cmd.c.hw_error; \ +}) + +#define tdx_vm_ioctl (vm, cmd, flags, arg) \ +({ \ + u64 ret = __tdx_vm_ioctl(vm, cmd, flags, arg); \ + \ + if (ret) { \ + TEST_ASSERT(!ret, \ + "%s failed, rc: 0x%llx errno: %i (%s)", \ + #cmd, (unsigned long long)ret, \ + errno, strerror(errno)); \ + } \ +}) + +void tdx_init_vm (struct kvm_vm *vm,u64 attributes); +#endif /* SELFTESTS_TDX_TDX_UTIL_H */
tools/testing/selftests/kvm/lib/x86/processor.c +17 -4
@@ -802,6 +802,9 @@ void kvm_arch_vm_post_create(struct kvm_vm *vm, unsigned int nr_vcpus)vm_sev_ioctl (vm,KVM_SEV_INIT2 , &init); } +if (is_tdx_vm (vm)) +tdx_init_vm (vm,0 ); + r =__vm_ioctl (vm,KVM_GET_TSC_KHZ ,NULL );TEST_ASSERT (r >0 ,"KVM_GET_TSC_KHZ did not provide a valid TSC frequency." ); guest_tsc_khz = r;
@@ -1328,8 +1331,8 @@ void kvm_init_vm_address_properties(struct kvm_vm *vm) } } -const struct kvm_cpuid_entry2 *get_cpuid_entry (const struct kvm_cpuid2 *cpuid, -u32 function,u32 index) +const struct kvm_cpuid_entry2 *__get_cpuid_entry (const struct kvm_cpuid2 *cpuid, +u32 function,u32 index) {int i;
@@ -1339,11 +1342,21 @@ const struct kvm_cpuid_entry2 *get_cpuid_entry(const struct kvm_cpuid2 *cpuid,return &cpuid->entries[i]; } -TEST_FAIL ("CPUID function 0x%x index 0x%x not found " , function, index); -return NULL ; } +const struct kvm_cpuid_entry2 *get_cpuid_entry (const struct kvm_cpuid2 *cpuid, +u32 function,u32 index) +{ +const struct kvm_cpuid_entry2 *entry; + + entry =__get_cpuid_entry (cpuid, function, index); +if (!entry) +TEST_FAIL ("CPUID function 0x%x index 0x%x not found " , function, index); + +return entry; +} + #define X86_HYPERCALL(inputs...) \ ({ \ u64 r; \
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c new file +120 -0
120 lines not on this page · raw message
Signature · 1 line
2.55.0.229.g6434b31f56-goog
Re: [PATCH v14 03/22] KVM: selftests: Initialize the TDX VM
Quoted 14 lines · Lisa Wang
From: Sagi Shahar <[email protected]>
Add
tdx_init_vm()to handle the mandatory VM-level initialization sequence required for Intel TDX.For TDX, the guest's CPUID configuration must be "sealed" during
KVM_TDX_INIT_VMbefore any vCPUs are created. This is necessary because the TDX hardware directly virtualizes CPUID and includes the configuration in the guest's initial security measurement.The helper calculates the required CPUID values by filtering the host- supported bits (
kvm_get_supported_cpuid) against the "directly configurable" bits reported byKVM_TDX_CAPABILITIES, ensuring compliance with the strict requirements of the TDH.MNG.INIT SEAMCALL.
<snip>
tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h static inline bool is_tdx_vm(struct kvm_vm *vm) · line 18
Quoted 5 lines
tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h +5
-0+/* +* TDX ioctls +* Use underscores to avoid collisions with struct member names. +*/ +#define __tdx_vm_ioctl (vm, cmd, _flags, arg) \
sev uses the name __vm_sev_ioctl, I think we need to keep them consistent.
Quoted 4 lines
tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h +4
-0+({ \ + u64 r; \ + \ + union { \
tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h static inline bool is_tdx_vm(struct kvm_vm *vm) · line 32
Quoted 10 lines
tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h +10
-0+struct kvm_tdx_cmd c; \ +unsigned long raw; \ + } tdx_cmd = { .c = { \ + .id = (cmd), \ + .flags = (u32)(_flags), \ + .data = (u64)(arg), \ + } }; \ + \ + r =__vm_ioctl (vm,KVM_MEMORY_ENCRYPT_OP , &tdx_cmd.raw); \ + r ?: tdx_cmd.c.hw_error; \
I know it takes the same handling from __vm_sev_ioctl(). But I think the handling for hw_error is not correct, at least for TDX (I didn't check for SEV).
the hw_error is the additional info, to tell the SEAMCALL return code, when the IOCTL fails. KVM requires hw_error to be in the input, and KVM puts the SEAMCALL return code into hw_error when the IOCTL fails due to SEAMCALL failure. That means, when r == 0, the hw_error is always 0.
I think we need to provide hw_error along with r to the caller so that caller can print them together.
tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h static inline bool is_tdx_vm(struct kvm_vm *vm) · line 43
Quoted 11 lines
tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h +11
-0+}) + +#define tdx_vm_ioctl (vm, cmd, flags, arg) \ +({ \ + u64 ret = __tdx_vm_ioctl(vm, cmd, flags, arg); \ + \ + if (ret) { \ + TEST_ASSERT(!ret, \ + "%s failed, rc: 0x%llx errno: %i (%s)", \ + #cmd, (unsigned long long)ret, \ + errno, strerror(errno)); \
The if() looks silly. Why add it? And why change it from __TEST_ASSERT_VM_VCPU_IOCTL() in the v13?
Considering the suggestion of hw_error above, I think we need to introduce the TEST_ASSERT_TDX_VM_VCPU_IOCTL() which accepts additional hw_error?
<snip>
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c line 6
Quoted 12 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c new file +6
-0@@ -0,0 +1,120 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include "processor.h" +#include "tdx/tdx_util.h" + +static struct kvm_tdx_capabilities *tdx_read_capabilities (struct kvm_vm *vm)
make it const, is better.
<snip>
Quoted 11 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c new file +11
-0+ +void tdx_init_vm (struct kvm_vm *vm,u64 attributes) +{ +struct kvm_tdx_init_vm *init_vm; +const struct kvm_cpuid2 *tmp; +struct kvm_cpuid2 *cpuid; + + tmp =kvm_get_supported_cpuid (); + + cpuid =allocate_kvm_cpuid2 (tmp->nent); +memcpy (cpuid, tmp,kvm_cpuid2_size (tmp->nent));
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c line 115
Quoted 10 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c new file +10
-0+tdx_filter_cpuid (vm, cpuid); + + init_vm =calloc (1 ,sizeof (*init_vm) + +sizeof (init_vm->cpuid.entries[0 ]) * cpuid->nent); +TEST_ASSERT (init_vm,"init_vm allocation failed" ); + +memcpy (&init_vm->cpuid, cpuid,kvm_cpuid2_size (cpuid->nent)); +free (cpuid); + + init_vm->attributes = attributes;
Besides CPUID, it only allows attributes to be configure but leave XFAM as 0. I think the changelog needs to explain why we need to configure attributes.
The rest of the patch looks good to me.
Quoted 6 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c new file +5
-0+ +tdx_vm_ioctl (vm,KVM_TDX_INIT_VM ,0 , init_vm); + +free (init_vm); +}
Edgecombe, Rick P
maintainerRe: [PATCH v14 03/22] KVM: selftests: Initialize the TDX VM
Quoted 7 lines · Xiaoyao Li
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c line 115
Quoted 1 line
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c new file +1
-0+ init_vm->attributes = attributes;Besides CPUID, it only allows attributes to be configure but leave XFAM as 0. I think the changelog needs to explain why we need to configure attributes.
The rest of the patch looks good to me.
How about directly setting xfam to zero for clarity?
Re: [PATCH v14 03/22] KVM: selftests: Initialize the TDX VM
Quoted 10 lines · Rick P
Quoted 7 lines · Xiaoyao Li
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c line 115
Quoted 1 line
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c new file +1
-0+ init_vm->attributes = attributes;Besides CPUID, it only allows attributes to be configure but leave XFAM as 0. I think the changelog needs to explain why we need to configure attributes.
The rest of the patch looks good to me.
How about directly setting xfam to zero for clarity?
Agreed. It's simple.
Re: [PATCH v14 03/22] KVM: selftests: Initialize the TDX VM
In reply to Xiaoyao LiQuoted 4 lines · Xiaoyao Li
Quoted 2 lines
+ */
+#define__tdx_vm_ioctl(vm, cmd, _flags, arg)\sev uses the name
__vm_sev_ioctl, I think we need to keep them consistent.
While __vm_tdx_ioctl matches SEV, the TDX selftest follows a tdx_<scope>_* naming convention (1. TDX prefix, 2. Scope: VM or vCPU). I named it __tdx_vm_ioctl to keep the TDX codebase internally consistent.[1]
Do you think we should align with SEV's naming convention instead of sticking with the internal TDX pattern?
[1]: https://lore.kernel.org/kvm/[email protected]/
Quoted 19 lines
Quoted 4 lines
tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h +4
-0+({ \ + u64 r; \ + \ + union { \tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h static inline bool is_tdx_vm(struct kvm_vm *vm) · line 32
Quoted 10 lines
tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h +10
-0+struct kvm_tdx_cmd c; \ +unsigned long raw; \ + } tdx_cmd = { .c = { \ + .id = (cmd), \ + .flags = (u32)(_flags), \ + .data = (u64)(arg), \ + } }; \ + \ + r =__vm_ioctl (vm,KVM_MEMORY_ENCRYPT_OP , &tdx_cmd.raw); \ + r ?: tdx_cmd.c.hw_error; \I know it takes the same handling from
__vm_sev_ioctl(). But I think the handling forhw_erroris not correct, at least for TDX (I didn't check for SEV).
Quoted 7 lines
the
hw_erroris the additional info, to tell the SEAMCALL return code, when the IOCTL fails. KVM requireshw_errorto be in the input, and KVM puts the SEAMCALL return code intohw_errorwhen the IOCTL fails due to SEAMCALL failure. That means, when r == 0, thehw_erroris always 0.I think we need to provide
hw_erroralong with r to the caller so that caller can print them together.
I think the value of r is not important, because the ioctl failure is already captured in errno.
We only need to fix the return values for SEV and TDX and have TEST_ASSERT_* print formatted error logs with errno and hw_error.
- r ?: {tdx, sev}_cmd.c.hw_error;
+ r ? {tdx, sev}_cmd.c.hw_error : 0;
Quoted 15 lines
tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h static inline bool is_tdx_vm(struct kvm_vm *vm) · line 43
Quoted 11 lines
tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h +11
-0+}) + +#define tdx_vm_ioctl (vm, cmd, flags, arg) \ +({ \ + u64 ret = __tdx_vm_ioctl(vm, cmd, flags, arg); \ + \ + if (ret) { \ + TEST_ASSERT(!ret, \ + "%s failed, rc: 0x%llx errno: %i (%s)", \ + #cmd, (unsigned long long)ret, \ + errno, strerror(errno)); \The
if()looks silly. Why add it? And why change it from__TEST_ASSERT_VM_VCPU_IOCTL()in the v13?
Quoted 2 lines
Considering the suggestion of
hw_errorabove, I think we need to introduce theTEST_ASSERT_TDX_VM_VCPU_IOCTL()which accepts additionalhw_error?
The reason we could not use __TEST_ASSERT_VM_VCPU_IOCTL() directly[2] is because it formats ther return value as %i (32-bit), whereas __tdx_vm_ioctl might return a u64 hardware error code.
I agree with your suggestion to introduce a new TEST_ASSERT_TDX_VM_VCPU_IOCTL() macro to print out u64 hardware error code properly.
[2]: https://lore.kernel.org/all/[email protected]/
Quoted 15 lines
<snip>
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c line 6
Quoted 12 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c new file +6
-0@@ -0,0 +1,120 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include "processor.h" +#include "tdx/tdx_util.h" + +static struct kvm_tdx_capabilities *tdx_read_capabilities (struct kvm_vm *vm)make it const, is better.
Thanks, noted.
Quoted 5 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c line 115
Quoted 1 line
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c new file +1
-0+ init_vm->attributes = attributes;Besides CPUID, it only allows attributes to be configure but leave XFAM as
0. I think the changelog needs to explain why we need to configure attributes.
Thanks, noted.
Quoted 1 line
The rest of the patch looks good to me.
Re: [PATCH v14 03/22] KVM: selftests: Initialize the TDX VM
Quoted 6 lines · Lisa Wang
Quoted 4 lines · Xiaoyao Li
Quoted 2 lines
+ */
+#define__tdx_vm_ioctl(vm, cmd, _flags, arg)\sev uses the name
__vm_sev_ioctl, I think we need to keep them consistent.
Quoted 9 lines
While
__vm_tdx_ioctlmatches SEV, the TDX selftest follows a tdx_<scope>_* naming convention (1. TDX prefix, 2. Scope: VM or vCPU). I named it__tdx_vm_ioctlto keep the TDX codebase internally consistent.[1]Do you think we should align with SEV's naming convention instead of sticking with the internal TDX pattern?
Given that __vm_sev_ioctl and __tdx_vm_ioctl aren't likely to appear next to each other, I think it's better to have the tdx prefix earlier to keep the TDX code consistent. To move things along, I think we could continue as-is and not swap to align with sev.
(The sev functions actually look kind of inconsistent in sev.h, but that's a discussion for another series.)
Quoted 27 lines
Quoted 19 lines
Quoted 4 lines
tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h +4
-0+({ \ + u64 r; \ + \ + union { \tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h static inline bool is_tdx_vm(struct kvm_vm *vm) · line 32
Quoted 10 lines
tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h +10
-0+struct kvm_tdx_cmd c; \ +unsigned long raw; \ + } tdx_cmd = { .c = { \ + .id = (cmd), \ + .flags = (u32)(_flags), \ + .data = (u64)(arg), \ + } }; \ + \ + r =__vm_ioctl (vm,KVM_MEMORY_ENCRYPT_OP , &tdx_cmd.raw); \ + r ?: tdx_cmd.c.hw_error; \I know it takes the same handling from
__vm_sev_ioctl(). But I think the handling forhw_erroris not correct, at least for TDX (I didn't check for SEV).Quoted 7 lines
the
hw_erroris the additional info, to tell the SEAMCALL return code, when the IOCTL fails. KVM requireshw_errorto be in the input, and KVM puts the SEAMCALL return code intohw_errorwhen the IOCTL fails due to SEAMCALL failure. That means, when r == 0, thehw_erroris always 0.I think we need to provide
hw_erroralong with r to the caller so that caller can print them together.
Quoted 9 lines
I think the value of r is not important, because the ioctl failure is already captured in errno.
We only need to fix the return values for SEV and TDX and have TEST_ASSERT_* print formatted error logs with errno and
hw_error.- r ?: {tdx, sev}_cmd.c.hw_error;
+ r ? {tdx, sev}_cmd.c.hw_error : 0;
I didn't look in detail, perhaps Lisa could look into these:
+ What are the possible values of r? Is it always going to be 1 on error?
+ Is r always positive or negative?
+ Is tdx_cmd.c.hw_error always positive or negative? It's probably not one of the standard Linux errors, right?
Perhaps squashing hw_error together with a retval conflates the two.
In the lower-level __tdx_vm_ioctl() we could pass a hw_error and have the macro set hw_error? That will allow the caller to print both.
Quoted 22 lines
Quoted 15 lines
tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h static inline bool is_tdx_vm(struct kvm_vm *vm) · line 43
Quoted 11 lines
tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h +11
-0+}) + +#define tdx_vm_ioctl (vm, cmd, flags, arg) \ +({ \ + u64 ret = __tdx_vm_ioctl(vm, cmd, flags, arg); \ + \ + if (ret) { \ + TEST_ASSERT(!ret, \ + "%s failed, rc: 0x%llx errno: %i (%s)", \ + #cmd, (unsigned long long)ret, \ + errno, strerror(errno)); \The
if()looks silly. Why add it? And why change it from__TEST_ASSERT_VM_VCPU_IOCTL()in the v13?Quoted 2 lines
Considering the suggestion of
hw_errorabove, I think we need to introduce theTEST_ASSERT_TDX_VM_VCPU_IOCTL()which accepts additionalhw_error?The reason we could not use
__TEST_ASSERT_VM_VCPU_IOCTL()directly[2] is because it formats ther return value as %i (32-bit), whereas__tdx_vm_ioctlmight return a u64 hardware error code.
Quoted 6 lines
I agree with your suggestion to introduce a new
TEST_ASSERT_TDX_VM_VCPU_IOCTL()macro to print out u64 hardware error code properly.
Is tdx_vm_ioctl() the only place where TEST_ASSERT_TDX_VM_VCPU_IOCTL() is going to be used though? If so, maybe we should defer introducing TEST_ASSERT_TDX_VM_VCPU_IOCTL() till later.
I think the issue with if (ret) is just that TEST_ASSERT(!ret) already does that same check, and so we can drop the if (ret) part.
Quoted 28 lines
Quoted 15 lines
<snip>
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c line 6
Quoted 12 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c new file +6
-0@@ -0,0 +1,120 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include "processor.h" +#include "tdx/tdx_util.h" + +static struct kvm_tdx_capabilities *tdx_read_capabilities (struct kvm_vm *vm)make it const, is better.
Thanks, noted.
Quoted 5 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c line 115
Quoted 1 line
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c new file +1
-0+ init_vm->attributes = attributes;Besides CPUID, it only allows attributes to be configure but leave XFAM as
0. I think the changelog needs to explain why we need to configure attributes.Thanks, noted.
Quoted 1 line
The rest of the patch looks good to me.
Re: [PATCH v14 03/22] KVM: selftests: Initialize the TDX VM
Quoted 18 lines · Ackerley Tng
Quoted 6 lines · Lisa Wang
Quoted 4 lines · Xiaoyao Li
Quoted 2 lines
+ */
+#define__tdx_vm_ioctl(vm, cmd, _flags, arg)\sev uses the name
__vm_sev_ioctl, I think we need to keep them consistent.Quoted 9 lines
While
__vm_tdx_ioctlmatches SEV, the TDX selftest follows a tdx_<scope>_* naming convention (1. TDX prefix, 2. Scope: VM or vCPU). I named it__tdx_vm_ioctlto keep the TDX codebase internally consistent.[1]Do you think we should align with SEV's naming convention instead of sticking with the internal TDX pattern?
Quoted 7 lines
Given that
__vm_sev_ioctland__tdx_vm_ioctlaren't likely to appear next to each other, I think it's better to have the tdx prefix earlier to keep the TDX code consistent. To move things along, I think we could continue as-is and not swap to align with sev.(The sev functions actually look kind of inconsistent in
sev.h, but that's a discussion for another series.)
Yeah, either adjusting this patch to follow SEV or renaming existing SEV MACROs is OK.
Keep the patch as-is and leave the SEV work to future are OK.
Quoted 30 lines
Quoted 30 lines
Quoted 19 lines
Quoted 4 lines
tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h +4
-0+({ \ + u64 r; \ + \ + union { \tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h static inline bool is_tdx_vm(struct kvm_vm *vm) · line 32
Quoted 10 lines
tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h +10
-0+struct kvm_tdx_cmd c; \ +unsigned long raw; \ + } tdx_cmd = { .c = { \ + .id = (cmd), \ + .flags = (u32)(_flags), \ + .data = (u64)(arg), \ + } }; \ + \ + r =__vm_ioctl (vm,KVM_MEMORY_ENCRYPT_OP , &tdx_cmd.raw); \ + r ?: tdx_cmd.c.hw_error; \I know it takes the same handling from
__vm_sev_ioctl(). But I think the handling forhw_erroris not correct, at least for TDX (I didn't check for SEV).Quoted 7 lines
the
hw_erroris the additional info, to tell the SEAMCALL return code, when the IOCTL fails. KVM requireshw_errorto be in the input, and KVM puts the SEAMCALL return code intohw_errorwhen the IOCTL fails due to SEAMCALL failure. That means, when r == 0, thehw_erroris always 0.I think we need to provide
hw_erroralong with r to the caller so that caller can print them together.I think the value of r is not important, because the ioctl failure is already captured in errno.
Quoted 6 lines
Quoted 6 lines
We only need to fix the return values for SEV and TDX and have TEST_ASSERT_* print formatted error logs with errno and
hw_error.- r ?: {tdx, sev}_cmd.c.hw_error;
+ r ? {tdx, sev}_cmd.c.hw_error : 0;
No. It is not correct because hw_error can be 0 when r != 0.
Quoted 8 lines
I didn't look in detail, perhaps Lisa could look into these:
+ What are the possible values of r? Is it always going to be 1 on error?
+ Is r always positive or negative?
+ Is tdx_cmd.c.hw_error always positive or negative? It's probably not one of the standard Linux errors, right?
Quoted 4 lines
Perhaps squashing
hw_errortogether with a retval conflates the two.In the lower-level
__tdx_vm_ioctl()we could pass ahw_errorand have the macro sethw_error? That will allow the caller to print both.
yeah. This idea can work.
Quoted 15 lines
Quoted 15 lines
Quoted 15 lines
tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h static inline bool is_tdx_vm(struct kvm_vm *vm) · line 43
Quoted 11 lines
tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h +11
-0+}) + +#define tdx_vm_ioctl (vm, cmd, flags, arg) \ +({ \ + u64 ret = __tdx_vm_ioctl(vm, cmd, flags, arg); \ + \ + if (ret) { \ + TEST_ASSERT(!ret, \ + "%s failed, rc: 0x%llx errno: %i (%s)", \ + #cmd, (unsigned long long)ret, \ + errno, strerror(errno)); \The
if()looks silly. Why add it? And why change it from__TEST_ASSERT_VM_VCPU_IOCTL()in the v13?
Quoted 7 lines
Quoted 7 lines
Quoted 2 lines
Considering the suggestion of
hw_errorabove, I think we need to introduce theTEST_ASSERT_TDX_VM_VCPU_IOCTL()which accepts additionalhw_error?The reason we could not use
__TEST_ASSERT_VM_VCPU_IOCTL()directly[2] is because it formats ther return value as %i (32-bit), whereas__tdx_vm_ioctlmight return a u64 hardware error code.
Since we cannot simply use hw_error to replace ret, there will be not 32bit vs 64bit issue. But ...
Quoted 3 lines
Quoted 3 lines
I agree with your suggestion to introduce a new
TEST_ASSERT_TDX_VM_VCPU_IOCTL()macro to print out u64 hardware error code properly.
... if we want to print hw_error as well, we still need a new macro.
Quoted 6 lines
Quoted 2 lines
Is
tdx_vm_ioctl()the only place whereTEST_ASSERT_TDX_VM_VCPU_IOCTL()is going to be used though? If so, maybe we should defer introducingTEST_ASSERT_TDX_VM_VCPU_IOCTL()till later.
I think tdx_vcpu_ioctl() will use it as well?
Quoted 3 lines
I think the issue with if (ret) is just that
TEST_ASSERT(!ret)already does that same check, and so we can drop the if (ret) part.
Re: [PATCH v14 03/22] KVM: selftests: Initialize the TDX VM
Quoted 14 lines · Xiaoyao Li
Quoted 4 lines
Quoted 4 lines
The reason we could not use
__TEST_ASSERT_VM_VCPU_IOCTL()directly[2] is because it formats ther return value as %i (32-bit), whereas__tdx_vm_ioctlmight return a u64 hardware error code.Since we cannot simply use
hw_errorto replace ret, there will be not 32bit vs 64bit issue. But ...Quoted 3 lines
Quoted 3 lines
I agree with your suggestion to introduce a new
TEST_ASSERT_TDX_VM_VCPU_IOCTL()macro to print out u64 hardware error code properly.... if we want to print
hw_erroras well, we still need a new macro.
Quoted 8 lines
Quoted 6 lines
Quoted 2 lines
Is
tdx_vm_ioctl()the only place whereTEST_ASSERT_TDX_VM_VCPU_IOCTL()is going to be used though? If so, maybe we should defer introducingTEST_ASSERT_TDX_VM_VCPU_IOCTL()till later.I think
tdx_vcpu_ioctl()will use it as well?
Thanks for replying.
I agree all of the other parts of your comments.
Just wanted to point out one detail: tdx_vm_ioctl() is the only place that actually needs to evaluate hw_error right now. Unlike the VM-scoped ioctls in the x86 kernel code, the functions dispatched via tdx_vcpu_unlocked_ioctl() (such as tdx_vcpu_init or tdx_vcpu_init_mem_region) do not currently return the TDX SEAMCALL error codes into the hw_error field.
Thus, I would prefer to inline TEST_ASSERT directly inside tdx_vm_ioctl() instead of introducing a new TEST_ASSERT_TDX_VM_VCPU_IOCTL() right now.
Lisa
Quoted 3 lines
Quoted 3 lines
I think the issue with if (ret) is just that
TEST_ASSERT(!ret)already does that same check, and so we can drop the if (ret) part.
Edgecombe, Rick P
maintainerRe: [PATCH v14 03/22] KVM: selftests: Initialize the TDX VM
In reply to Lisa WangQuoted 9 lines · Lisa Wang
From: Sagi Shahar <[email protected]>
Add
tdx_init_vm()to handle the mandatory VM-level initialization sequence required for Intel TDX.For TDX, the guest's CPUID configuration must be "sealed" during
KVM_TDX_INIT_VMbefore any vCPUs are created. This is necessary because the TDX hardware directly virtualizes CPUID and includes the configuration in the guest's initial security measurement.
"TDX hardware" should be TDX module. But I'm not sure what it is trying to say about directly virtualizes.
Also, it is not accurate to say that the CPUID configuration is included in the measurement? Is that right Peter? It's not in the report, but is it in the measurement?
Or maybe it doesn't really need to be in the log anyway.
Quoted 95 lines
The helper calculates the required CPUID values by filtering the host- supported bits (
kvm_get_supported_cpuid) against the "directly configurable" bits reported byKVM_TDX_CAPABILITIES, ensuring compliance with the strict requirements of the TDH.MNG.INIT SEAMCALL.Co-developed-by Isaku Yamahata <[email protected]>Signed-off-by Isaku Yamahata <[email protected]>Co-developed-by Rick Edgecombe <[email protected]>Signed-off-by Rick Edgecombe <[email protected]>Signed-off-by Sagi Shahar <[email protected]>Reviewed-by Ira Weiny <[email protected]>Signed-off-by Lisa Wang <[email protected]>--- tools/testing/selftests/kvm/Makefile.kvm | 1 + .../testing/selftests/kvm/include/x86/processor.h | 2 + .../selftests/kvm/include/x86/tdx/tdx_util.h | 35 ++++++ tools/testing/selftests/kvm/lib/x86/processor.c | 21 +++- tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c | 120 +++++++++++++++++++++ 5 files changed, 175 insertions(+), 4 deletions(-)tools/testing/selftests/kvm/Makefile.kvm +1
-0@@ -27,6 +27,7 @@ LIBKVM_x86 += lib/x86/pmu.c LIBKVM_x86 += lib/x86/processor.c LIBKVM_x86 += lib/x86/sev.c LIBKVM_x86 += lib/x86/svm.c +LIBKVM_x86 += lib/x86/tdx/tdx_util.c LIBKVM_x86 += lib/x86/ucall.c LIBKVM_x86 += lib/x86/vmx.ctools/testing/selftests/kvm/include/x86/processor.h +2
-0@@ -956,6 +956,8 @@ static inline void vcpu_xcrs_set(struct kvm_vcpu *vcpu, struct kvm_xcrs *xcrs)vcpu_ioctl (vcpu,KVM_SET_XCRS , xcrs); } +const struct kvm_cpuid_entry2 *__get_cpuid_entry (const struct kvm_cpuid2 *cpuid, +u32 function,u32 index);const struct kvm_cpuid_entry2 *get_cpuid_entry (const struct kvm_cpuid2 *cpuid,u32 function,u32 index);const struct kvm_cpuid2 *kvm_get_supported_cpuid (void );tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h +35
-0@@ -11,4 +11,39 @@ static inline bool is_tdx_vm(struct kvm_vm *vm) return vm->type == KVM_X86_TDX_VM; } +/* +* TDX ioctls +* Use underscores to avoid collisions with struct member names. +*/ +#define __tdx_vm_ioctl (vm, cmd, _flags, arg) \ +({ \ + u64 r; \ + \ + union { \ + struct kvm_tdx_cmd c; \ + unsigned long raw; \ + } tdx_cmd = { .c = { \ + .id = (cmd), \ + .flags = (u32)(_flags), \ + .data = (u64)(arg), \ + } }; \ + \ + r = __vm_ioctl(vm, KVM_MEMORY_ENCRYPT_OP, &tdx_cmd.raw); \ + r ?: tdx_cmd.c.hw_error; \ +}) + +#define tdx_vm_ioctl (vm, cmd, flags, arg) \ +({ \ + u64 ret = __tdx_vm_ioctl(vm, cmd, flags, arg); \ + \ + if (ret) { \ + TEST_ASSERT(!ret, \ + "%s failed, rc: 0x%llx errno: %i (%s)", \ + #cmd, (unsigned long long)ret, \ + errno, strerror(errno)); \ + } \ +}) + +void tdx_init_vm (struct kvm_vm *vm,u64 attributes); +#endif /* SELFTESTS_TDX_TDX_UTIL_H */ tools/testing/selftests/kvm/lib/x86/processor.c +0
-0
tools/testing/selftests/kvm/lib/x86/processor.c void kvm_arch_vm_post_create(struct kvm_vm *vm, unsigned int nr_vcpus) · line 806
Quoted 5 lines
tools/testing/selftests/kvm/lib/x86/processor.c +2
-0@@ -802,6 +802,9 @@ void kvm_arch_vm_post_create(struct kvm_vm *vm, unsigned int nr_vcpus)vm_sev_ioctl (vm,KVM_SEV_INIT2 , &init); } +if (is_tdx_vm (vm)) +tdx_init_vm (vm,0 );
In this series tdx_init_vm() is only called with 0 for attributes. Do we need the arg or could we just set it to 0 internally like xfam?
Quoted 166 lines
tools/testing/selftests/kvm/lib/x86/processor.c +15
-4+ r =__vm_ioctl (vm,KVM_GET_TSC_KHZ ,NULL );TEST_ASSERT (r >0 ,"KVM_GET_TSC_KHZ did not provide a valid TSC frequency." ); guest_tsc_khz = r;@@ -1328,8 +1331,8 @@ void kvm_init_vm_address_properties(struct kvm_vm *vm) } } -const struct kvm_cpuid_entry2 *get_cpuid_entry (const struct kvm_cpuid2 *cpuid, -u32 function,u32 index) +const struct kvm_cpuid_entry2 *__get_cpuid_entry (const struct kvm_cpuid2 *cpuid, +u32 function,u32 index) {int i;@@ -1339,11 +1342,21 @@ const struct kvm_cpuid_entry2 *get_cpuid_entry(const struct kvm_cpuid2 *cpuid,return &cpuid->entries[i]; } -TEST_FAIL ("CPUID function 0x%x index 0x%x not found " , function, index); -return NULL ; } +const struct kvm_cpuid_entry2 *get_cpuid_entry (const struct kvm_cpuid2 *cpuid, +u32 function,u32 index) +{ +const struct kvm_cpuid_entry2 *entry; + + entry =__get_cpuid_entry (cpuid, function, index); +if (!entry) +TEST_FAIL ("CPUID function 0x%x index 0x%x not found " , function, index); + +return entry; +} + #define X86_HYPERCALL(inputs...) \ ({ \ u64 r; \tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c new file +120
-0@@ -0,0 +1,120 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include "processor.h" +#include "tdx/tdx_util.h" + +static struct kvm_tdx_capabilities *tdx_read_capabilities (struct kvm_vm *vm) +{ +static struct kvm_tdx_capabilities *tdx_cap; +int nr_cpuid_configs =4 ; +int rc =-1 ; +int i; + +if (tdx_cap) +return tdx_cap; + +do { + nr_cpuid_configs *=2 ; + + tdx_cap =realloc (tdx_cap,sizeof (*tdx_cap) + + (sizeof (struct kvm_cpuid_entry2 ) * nr_cpuid_configs)); +TEST_ASSERT (tdx_cap, +"Could not allocate memory for tdx capability nr_cpuid_configs %d\n" , + nr_cpuid_configs); + + tdx_cap->cpuid.nent = nr_cpuid_configs; + rc =__tdx_vm_ioctl (vm,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: supported_attrs: 0x%016llx\n" +"tdx_cap: supported_xfam 0x%016llx\n" , + tdx_cap->supported_attrs, tdx_cap->supported_xfam); + +for (i =0 ; i < tdx_cap->cpuid.nent; i++) { +const struct kvm_cpuid_entry2 *config = &tdx_cap->cpuid.entries[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->function, config->index, + config->eax, config->ebx, config->ecx, config->edx); + } + +return tdx_cap; +} + +/* +* Filter CPUID based on TDX supported capabilities +* +* Input Args: +* vm - Virtual Machine +* cpuid_data - CPUID fields to filter +* +* Output Args: None +* +* Return: None +* +* For each CPUID leaf, filter out unsupported bits based on the capabilities +* reported by the TDX module +*/ +static void tdx_filter_cpuid (struct kvm_vm *vm, +struct kvm_cpuid2 *cpuid_data) +{ +struct kvm_tdx_capabilities *tdx_cap; +const struct kvm_cpuid_entry2 *config; +struct kvm_cpuid_entry2 *e; +int i; + + tdx_cap =tdx_read_capabilities (vm); + + i =0 ; +while (i < cpuid_data->nent) { + e = cpuid_data->entries + i; + config =__get_cpuid_entry (&tdx_cap->cpuid, e->function, e->index); + +if (!config) { +int left = cpuid_data->nent - i -1 ; + +if (left >0 ) +memmove (cpuid_data->entries + i, + cpuid_data->entries + i +1 , +sizeof (*cpuid_data->entries) * left); + cpuid_data->nent--; +continue ; + } + + e->eax &= config->eax; + e->ebx &= config->ebx; + e->ecx &= config->ecx; + e->edx &= config->edx; + + i++; + } +} + +void tdx_init_vm (struct kvm_vm *vm,u64 attributes) +{ +struct kvm_tdx_init_vm *init_vm; +const struct kvm_cpuid2 *tmp; +struct kvm_cpuid2 *cpuid; + + tmp =kvm_get_supported_cpuid (); + + cpuid =allocate_kvm_cpuid2 (tmp->nent); +memcpy (cpuid, tmp,kvm_cpuid2_size (tmp->nent)); +tdx_filter_cpuid (vm, cpuid); + + init_vm =calloc (1 ,sizeof (*init_vm) + +sizeof (init_vm->cpuid.entries[0 ]) * cpuid->nent); +TEST_ASSERT (init_vm,"init_vm allocation failed" ); + +memcpy (&init_vm->cpuid, cpuid,kvm_cpuid2_size (cpuid->nent)); +free (cpuid); + + init_vm->attributes = attributes; + +tdx_vm_ioctl (vm,KVM_TDX_INIT_VM ,0 , init_vm); + +free (init_vm); +}
Re: [PATCH v14 03/22] KVM: selftests: Initialize the TDX VM
Quoted 11 lines · Rick P
Quoted 9 lines · Lisa Wang
From: Sagi Shahar <[email protected]>
Add
tdx_init_vm()to handle the mandatory VM-level initialization sequence required for Intel TDX.For TDX, the guest's CPUID configuration must be "sealed" during
KVM_TDX_INIT_VMbefore any vCPUs are created. This is necessary because the TDX hardware directly virtualizes CPUID and includes the configuration in the guest's initial security measurement.
Quoted 6 lines
"TDX hardware" should be TDX module. But I'm not sure what it is trying to say about directly virtualizes.
Also, it is not accurate to say that the CPUID configuration is included in the measurement? Is that right Peter? It's not in the report, but is it in the measurement?
CPUID is not in the report or the TDX module's measurement. TD attributes and XFAM are in both the report and initial measurement, and some of the bits in there are related to CPUID (e.g. ATTRIBUTES.PKS or XFAM.MAXPA_VIRT). Probably better to make a distinction between these things.
Quoted 4 lines
Or maybe it doesn't really need to be in the log anyway.
Quoted 1 line
Re: [PATCH v14 03/22] KVM: selftests: Initialize the TDX VM
In reply to Lisa WangQuoted 15 lines · Lisa Wang
From: Sagi Shahar <[email protected]>
Add
tdx_init_vm()to handle the mandatory VM-level initialization sequence required for Intel TDX.For TDX, the guest's CPUID configuration must be "sealed" during
KVM_TDX_INIT_VMbefore any vCPUs are created. This is necessary because the TDX hardware directly virtualizes CPUID and includes the configuration in the guest's initial security measurement.The helper calculates the required CPUID values by filtering the host- supported bits (
kvm_get_supported_cpuid) against the "directly configurable" bits reported byKVM_TDX_CAPABILITIES, ensuring compliance with the strict requirements of the TDH.MNG.INIT SEAMCALL.
[ ... ]
Quoted 10 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c new file +10
-0+ +/* +* Filter CPUID based on TDX supported capabilities +* +* Input Args: +* vm - Virtual Machine +* cpuid_data - CPUID fields to filter +* +* Output Args: None +*
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c line 65
Quoted 10 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c new file +10
-0+* Return: None +* +* For each CPUID leaf, filter out unsupported bits based on the capabilities +* reported by the TDX module +*/ +static void tdx_filter_cpuid (struct kvm_vm *vm, +struct kvm_cpuid2 *cpuid_data) +{ +struct kvm_tdx_capabilities *tdx_cap; +const struct kvm_cpuid_entry2 *config;
Nit: reverse fir tree order i.e. declaring "config" first?
Quoted 3 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c new file +3
-0+struct kvm_cpuid_entry2 *e; +int i; +
PATCH v14 4/22 KVM: selftests: TDX: Use KVM_TDX_CAPABILITIES to validate TDs' attribute configuration
In reply to Lisa WangFrom: Isaku Yamahata <[email protected]>
Make sure that all the attributes enabled by the test are reported as supported by both the TDX module and KVM. KVM filters out the attributes not supported by itself.
This also exercises the KVM_TDX_CAPABILITIES ioctl.
--- tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c | 12 ++++++++++++ 1 file changed, 12 insertions(+)
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +12 -0
@@ -93,6 +93,16 @@ static void tdx_filter_cpuid(struct kvm_vm *vm, } } +static void tdx_check_attributes (struct kvm_vm *vm,u64 attributes) +{ +struct kvm_tdx_capabilities *tdx_cap; + + tdx_cap =tdx_read_capabilities (vm); + +/* Make sure all the attributes are reported as supported */ +TEST_ASSERT_EQ (attributes & tdx_cap->supported_attrs, attributes); +} +void tdx_init_vm (struct kvm_vm *vm,u64 attributes) {struct kvm_tdx_init_vm *init_vm;
@@ -112,6 +122,8 @@ void tdx_init_vm(struct kvm_vm *vm, u64 attributes)memcpy (&init_vm->cpuid, cpuid,kvm_cpuid2_size (cpuid->nent));free (cpuid); +tdx_check_attributes (vm, attributes); + init_vm->attributes = attributes;tdx_vm_ioctl (vm,KVM_TDX_INIT_VM ,0 , init_vm);
Signature · 1 line
2.55.0.229.g6434b31f56-goog
Edgecombe, Rick P
maintainerRe: [PATCH v14 04/22] KVM: selftests: TDX: Use KVM_TDX_CAPABILITIES to validate TDs' attribute configuration
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c static void tdx_filter_cpuid(struct kvm_vm *vm, · line 103
Quoted 8 lines · Lisa Wang
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +8
-0+static void tdx_check_attributes (struct kvm_vm *vm,u64 attributes) +{ +struct kvm_tdx_capabilities *tdx_cap; + + tdx_cap =tdx_read_capabilities (vm); + +/* Make sure all the attributes are reported as supported */ +TEST_ASSERT_EQ (attributes & tdx_cap->supported_attrs, attributes);
I think this patch actually does nothing because attributes is passed in as 0? So 0 & supported == 0. It does exercise KVM_TDX_CAPABILITIES as claimed, but so does tdx_filter_cpuid(). So for a minimal support, we could probably drop this patch.
Quoted 14 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +2
-0+} +void
tdx_init_vm(struct kvm_vm *vm, u64 attributes){
structkvm_tdx_init_vm*init_vm;tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +0
-0@@ -112,6 +122,8 @@ void tdx_init_vm(struct kvm_vm *vm, u64 attributes) memcpy(&init_vm->cpuid, cpuid, kvm_cpuid2_size(cpuid->nent)); free(cpuid);tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +2
-0+tdx_check_attributes (vm, attributes); +
init_vm->attributes= attributes;
tdx_vm_ioctl(vm, KVM_TDX_INIT_VM, 0, init_vm);
Re: [PATCH v14 04/22] KVM: selftests: TDX: Use KVM_TDX_CAPABILITIES to validate TDs' attribute configuration
Quoted 15 lines · Rick P
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c static void tdx_filter_cpuid(struct kvm_vm *vm, · line 103
Quoted 8 lines · Lisa Wang
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +8
-0+static void tdx_check_attributes (struct kvm_vm *vm,u64 attributes) +{ +struct kvm_tdx_capabilities *tdx_cap; + + tdx_cap =tdx_read_capabilities (vm); + +/* Make sure all the attributes are reported as supported */ +TEST_ASSERT_EQ (attributes & tdx_cap->supported_attrs, attributes);I think this patch actually does nothing because attributes is passed in as 0? So 0 & supported == 0. It does exercise
KVM_TDX_CAPABILITIESas claimed, but so doestdx_filter_cpuid(). So for a minimal support, we could probably drop this patch.
For more context, Isaku suggested this in [1], but after all the changes tdx_read_capabilities() is already used in the tdx_init_vm() so I think we're good to drop this patch.
[1] https://lore.kernel.org/all/[email protected]/
Quoted 14 lines
Quoted 14 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +2
-0+} +void
tdx_init_vm(struct kvm_vm *vm, u64 attributes){
structkvm_tdx_init_vm*init_vm;tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +2
-0@@ -112,6 +122,8 @@ void tdx_init_vm(struct kvm_vm *vm, u64 attributes) memcpy(&init_vm->cpuid, cpuid, kvm_cpuid2_size(cpuid->nent)); free(cpuid); +tdx_check_attributes (vm, attributes); + init_vm->attributes = attributes; tdx_vm_ioctl(vm, KVM_TDX_INIT_VM, 0, init_vm);
PATCH v14 5/22 KVM: selftests: Expose segment definitions to assembly files
In reply to Lisa WangFrom: Sagi Shahar <[email protected]>
Move kernel segment definitions to a separate file which can be included from assembly files.
--- tools/testing/selftests/kvm/include/x86/processor_asm.h | 12 ++++++++++++ tools/testing/selftests/kvm/lib/x86/processor.c | 5 +---- 2 files changed, 13 insertions(+), 4 deletions(-)
tools/testing/selftests/kvm/include/x86/processor_asm.h new file +12 -0
@@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* +* Used for storing defines used by both c and assembly code. +*/ +#ifndef SELFTEST_KVM_PROCESSOR_ASM_H +#define SELFTEST_KVM_PROCESSOR_ASM_H + +#define KERNEL_CS 0x8 +#define KERNEL_DS 0x10 +#define KERNEL_TSS 0x18 + +#endif /* SELFTEST_KVM_PROCESSOR_ASM_H */
tools/testing/selftests/kvm/lib/x86/processor.c +1 -4
@@ -8,6 +8,7 @@#include "kvm_util.h" #include "pmu.h" #include "processor.h" +#include "processor_asm.h" #include "smm.h" #include "svm_util.h" #include "sev.h"
@@ -18,10 +19,6 @@#define NUM_INTERRUPTS 256#endif -#define KERNEL_CS 0x8 -#define KERNEL_DS 0x10 -#define KERNEL_TSS 0x18 -gva_t exception_handlers;bool host_cpu_is_amd;bool host_cpu_is_intel;
Signature · 1 line
2.55.0.229.g6434b31f56-goog
Edgecombe, Rick P
maintainerRe: [PATCH v14 05/22] KVM: selftests: Expose segment definitions to assembly files
Quoted 4 lines · Lisa Wang
From: Sagi Shahar <[email protected]>
Move kernel segment definitions to a separate file which can be included from assembly files.
Log nit, it's not just a move, it's extracting them to a new file.
Quoted 6 lines
Reviewed-by Ira Weiny <[email protected]>Reviewed-by Binbin Wu <[email protected]>Signed-off-by Sagi Shahar <[email protected]>Signed-off-by Lisa Wang <[email protected]>Reviewed-by Xiaoyao Li <[email protected]>
Re: [PATCH v14 05/22] KVM: selftests: Expose segment definitions to assembly files
In reply to Lisa WangQuoted 58 lines · Lisa Wang
From: Sagi Shahar <[email protected]>
Move kernel segment definitions to a separate file which can be included from assembly files.
Reviewed-by Ira Weiny <[email protected]>Reviewed-by Binbin Wu <[email protected]>Signed-off-by Sagi Shahar <[email protected]>Signed-off-by Lisa Wang <[email protected]>Reviewed-by Xiaoyao Li <[email protected]>--- tools/testing/selftests/kvm/include/x86/processor_asm.h | 12 ++++++++++++ tools/testing/selftests/kvm/lib/x86/processor.c | 5 +---- 2 files changed, 13 insertions(+), 4 deletions(-)tools/testing/selftests/kvm/include/x86/processor_asm.h new file +12
-0@@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* +* Used for storing defines used by both c and assembly code. +*/ +#ifndef SELFTEST_KVM_PROCESSOR_ASM_H +#define SELFTEST_KVM_PROCESSOR_ASM_H + +#define KERNEL_CS 0x8 +#define KERNEL_DS 0x10 +#define KERNEL_TSS 0x18 + +#endif /* SELFTEST_KVM_PROCESSOR_ASM_H */ tools/testing/selftests/kvm/lib/x86/processor.c +1
-5@@ -8,6 +8,7 @@#include "kvm_util.h" #include "pmu.h" #include "processor.h" +#include "processor_asm.h" #include "smm.h" #include "svm_util.h" #include "sev.h" @@ -18,10 +19,6 @@#define NUM_INTERRUPTS 256#endif -#define KERNEL_CS 0x8 -#define KERNEL_DS 0x10 -#define KERNEL_TSS 0x18 -gva_t exception_handlers;bool host_cpu_is_amd;bool host_cpu_is_intel; --
Quoted 1 line
2.55.0.229.g6434b31f56-goog
PATCH v14 6/22 tools: include: Add kbuild.h for assembly structure offsets
In reply to Lisa WangFrom: Sagi Shahar <[email protected]>
Add the Kbuild macros needed to enable the filechk_offsets mechanism to generate C header files containing structure member offset information.
Tools depending on assembly code that operate on structures have to hardcode the offsets of structure members. The Kbuild infrastructure can instead generate C header files with these offsets automatically, allowing them to be included in assembly code as symbolic constants.
For example, the TDX guest boot code requires access to parameters passed in the C structure (struct td_boot_parameters). This header provides the macros needed to extract these offsets from C code and expose them to assembly, ensuring the two remain synchronized.
--- tools/include/linux/kbuild.h | 11 +++++++++++ 1 file changed, 11 insertions(+)
tools/include/linux/kbuild.h new file +11 -0
@@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __TOOLS_LINUX_KBUILD_H +#define __TOOLS_LINUX_KBUILD_H + +#define DEFINE (sym, val) \ + asm volatile("\n.ascii \"->" #sym " %0 " #val "\"" : : "i" (val)) + +#define OFFSET (sym, str, mem) \ + DEFINE(sym, __builtin_offsetof(struct str, mem)) + +#endif /* __TOOLS_LINUX_KBUILD_H */
Signature · 1 line
2.55.0.229.g6434b31f56-goog
Re: [PATCH v14 06/22] tools: include: Add kbuild.h for assembly structure offsets
Quoted 42 lines · Lisa Wang
From: Sagi Shahar <[email protected]>
Add the
Kbuildmacros needed to enable thefilechk_offsetsmechanism to generate C header files containing structure member offset information.Tools depending on assembly code that operate on structures have to hardcode the offsets of structure members. The
Kbuildinfrastructure can instead generate C header files with these offsets automatically, allowing them to be included in assembly code as symbolic constants.For example, the TDX guest boot code requires access to parameters passed in the C structure (struct
td_boot_parameters). This header provides the macros needed to extract these offsets from C code and expose them to assembly, ensuring the two remain synchronized.Signed-off-by Sagi Shahar <[email protected]>Reviewed-by Ira Weiny <[email protected]>Signed-off-by Lisa Wang <[email protected]>Reviewed-by Binbin Wu <[email protected]>--- tools/include/linux/kbuild.h | 11 +++++++++++ 1 file changed, 11 insertions(+)tools/include/linux/kbuild.h new file +11
-1@@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __TOOLS_LINUX_KBUILD_H +#define __TOOLS_LINUX_KBUILD_H + +#define DEFINE (sym, val) \ + asm volatile("\n.ascii \"->" #sym " %0 " #val "\"" : : "i" (val)) + +#define OFFSET (sym, str, mem) \ + DEFINE(sym, __builtin_offsetof(struct str, mem)) + +#endif /* __TOOLS_LINUX_KBUILD_H */ --
Quoted 1 line
2.55.0.229.g6434b31f56-goog
PATCH v14 7/22 KVM: selftests: Introduce structures for TDX guest boot parameters
In reply to Lisa WangFrom: Sagi Shahar <[email protected]>
Introduce td_boot_parameters and td_per_vcpu_parameters, and export their offsets to assembly via the kbuild infrastructure.
TDX guest registers are private and must be initialized by guest-side assembly. These structures allow the assembly code to retrieve boot parameters and index into per-vCPU data based on the vCPU ID, while keeping host and guest definitions synchronized.
Use kbuild.h to expose the offsets into the structs from c code to assembly code.
--- tools/testing/selftests/kvm/.gitignore | 3 +- tools/testing/selftests/kvm/Makefile.kvm | 29 +++++++++- .../selftests/kvm/include/x86/tdx/td_boot.h | 66 ++++++++++++++++++++++ .../selftests/kvm/lib/x86/tdx/td_boot_offsets.c | 21 +++++++ 4 files changed, 116 insertions(+), 3 deletions(-)
tools/testing/selftests/kvm/.gitignore +2 -1
@@ -9,4 +9,5 @@ !config !settings !Makefile -!Makefile.kvm +!Makefile.kvm +include/x86/**/*_offsets.h
tools/testing/selftests/kvm/Makefile.kvm +27 -2
@@ -19,6 +19,8 @@ LIBKVM += lib/userfaultfd_util.c LIBKVM_STRING += lib/string_override.c +LIBKVM_ASM_DEFS += lib/x86/tdx/td_boot_offsets.c + LIBKVM_x86 += lib/x86/apic.c LIBKVM_x86 += lib/x86/handlers.S LIBKVM_x86 += lib/x86/hyperv.c
@@ -261,6 +263,10 @@ OVERRIDE_TARGETS = 1 include ../lib.mk include ../cgroup/lib/libcgroup.mk +# Enable Kbuild tools. +include $(top_srcdir)/scripts/Kbuild.include +include $(top_srcdir)/scripts/Makefile.lib + INSTALL_HDR_PATH = $(top_srcdir)/usr LINUX_HDR_PATH = $(INSTALL_HDR_PATH)/include/ LINUX_TOOL_INCLUDE = $(top_srcdir)/tools/include
@@ -273,15 +279,24 @@ CFLAGS += -Wall -Wstrict-prototypes -Wuninitialized -O2 -g -std=gnu99 \ -fno-stack-protector -fno-PIE -fno-strict-aliasing \ -I$(LINUX_TOOL_INCLUDE) -I$(LINUX_TOOL_ARCH_INCLUDE) \ -I$(LINUX_HDR_PATH) -Iinclude -I$(<D) -Iinclude/$(ARCH) \ - -I ../rseq -I.. $(EXTRA_CFLAGS) $(KHDR_INCLUDES) + -I ../rseq -I.. -I$(OUTPUT)/include/$(ARCH) $(EXTRA_CFLAGS) $(KHDR_INCLUDES) ifeq ($(ARCH),s390) CFLAGS += -march=z10 endif + ifeq ($(ARCH),x86) + ifeq ($(shell echo "void foo(void) { }" | $(CC) -march=x86-64-v2 -x c - -c -o /dev/null 2>/dev/null; echo "$$?"),0) CFLAGS += -march=x86-64-v2 endif + +KVM_GEN_HDRS := $(patsubst lib/x86/%.c, $(OUTPUT)/include/x86/%.h, $(filter lib/x86/%, $(LIBKVM_ASM_DEFS))) +$(shell mkdir -p $(sort $(dir $(KVM_GEN_HDRS)))) +$(KVM_GEN_HDRS): GUARD = $(shell echo $(*F) | tr a-z A-Z | tr '.' '_') +$(KVM_GEN_HDRS): $(OUTPUT)/include/x86/%.h: $(OUTPUT)/lib/x86/%.s FORCE + $(call filechk,offsets,__$(GUARD)_H__) endif + ifeq ($(ARCH),arm64) tools_dir := $(top_srcdir)/tools arm64_tools_dir := $(tools_dir)/arch/arm64/tools/
@@ -314,6 +329,7 @@ LIBKVM_S := $(filter %.S,$(LIBKVM)) LIBKVM_C_OBJ := $(patsubst %.c, $(OUTPUT)/%.o, $(LIBKVM_C)) LIBKVM_S_OBJ := $(patsubst %.S, $(OUTPUT)/%.o, $(LIBKVM_S)) LIBKVM_STRING_OBJ := $(patsubst %.c, $(OUTPUT)/%.o, $(LIBKVM_STRING)) +LIBKVM_ASM_DEFS_OBJ += $(patsubst %.c, $(OUTPUT)/%.s, $(LIBKVM_ASM_DEFS)) LIBKVM_OBJS = $(LIBKVM_C_OBJ) $(LIBKVM_S_OBJ) $(LIBKVM_STRING_OBJ) $(LIBCGROUP_O) SPLIT_TEST_GEN_PROGS := $(patsubst %, $(OUTPUT)/%, $(SPLIT_TESTS)) SPLIT_TEST_GEN_OBJ := $(patsubst %, $(OUTPUT)/$(ARCH)/%.o, $(SPLIT_TESTS))
@@ -339,7 +355,9 @@ $(SPLIT_TEST_GEN_OBJ): $(OUTPUT)/$(ARCH)/%.o: $(ARCH)/%.c $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@ EXTRA_CLEAN += $(GEN_HDRS) \ + $(KVM_GEN_HDRS) \ $(LIBKVM_OBJS) \ + $(LIBKVM_ASM_DEFS_OBJ) \ $(SPLIT_TEST_GEN_OBJ) \ $(TEST_DEP_FILES) \ $(TEST_GEN_OBJ) \
@@ -351,6 +369,9 @@ $(LIBKVM_C_OBJ): $(OUTPUT)/%.o: %.c $(GEN_HDRS) $(LIBKVM_S_OBJ): $(OUTPUT)/%.o: %.S $(GEN_HDRS) $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@ +$(LIBKVM_ASM_DEFS_OBJ): $(OUTPUT)/%.s: %.c FORCE + $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -S $< -o $@ + # Compile the string overrides as freestanding to prevent the compiler from # generating self-referential code, e.g. without "freestanding" the compiler may # "optimize" memcmp() by invoking memcmp(), thus causing infinite recursion.
@@ -359,11 +380,15 @@ $(LIBKVM_STRING_OBJ): $(OUTPUT)/%.o: %.c $(shell mkdir -p $(sort $(dir $(TEST_GEN_PROGS)))) $(SPLIT_TEST_GEN_OBJ): $(GEN_HDRS) +$(LIBKVM_OBJS): $(KVM_GEN_HDRS) $(TEST_GEN_PROGS): $(LIBKVM_OBJS) $(TEST_GEN_PROGS_EXTENDED): $(LIBKVM_OBJS) $(TEST_GEN_OBJ): $(GEN_HDRS) -cscope: include_paths = $(LINUX_TOOL_INCLUDE) $(LINUX_HDR_PATH) include lib .. +FORCE: + +cscope: include_paths = $(LINUX_TOOL_INCLUDE) $(LINUX_HDR_PATH) include lib .. \ + $(wildcard $(sort $(dir $(KVM_GEN_HDRS)))) cscope: $(RM) cscope.* (find $(include_paths) -name '*.h' \
tools/testing/selftests/kvm/include/x86/tdx/td_boot.h new file +66 -0
66 lines not on this page · raw message
tools/testing/selftests/kvm/lib/x86/tdx/td_boot_offsets.c new file +21 -0
@@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0 +#define COMPILE_OFFSETS + +#include <linux/kbuild.h> + +#include "tdx/td_boot.h" + +static void __attribute__((used))common (void ) +{ +OFFSET (TD_BOOT_PARAMETERS_CR0 , td_boot_parameters, cr0); +OFFSET (TD_BOOT_PARAMETERS_CR3 , td_boot_parameters, cr3); +OFFSET (TD_BOOT_PARAMETERS_CR4 , td_boot_parameters, cr4); +OFFSET (TD_BOOT_PARAMETERS_GDT , td_boot_parameters, gdtr); +OFFSET (TD_BOOT_PARAMETERS_IDT , td_boot_parameters, idtr); +OFFSET (TD_BOOT_PARAMETERS_PER_VCPU , td_boot_parameters, per_vcpu); +OFFSET (TD_PER_VCPU_PARAMETERS_ESP_GVA , td_per_vcpu_parameters, esp_gva); +OFFSET (TD_PER_VCPU_PARAMETERS_GUEST_CODE , td_per_vcpu_parameters, + guest_code); +DEFINE (SIZEOF_TD_PER_VCPU_PARAMETERS , +sizeof (struct td_per_vcpu_parameters )); +}
Signature · 1 line
2.55.0.229.g6434b31f56-goog
PATCH v14 8/22 KVM: selftests: Add TDX boot code
In reply to Lisa WangFrom: Erdem Aktas <[email protected]>
Add code to boot a TDX test VM. Since TDX registers are inaccessible to KVM, the boot code loads the relevant values from memory into the registers before jumping to the guest code.
--- tools/testing/selftests/kvm/Makefile.kvm | 1 + .../selftests/kvm/include/x86/tdx/td_boot.h | 22 ++++++-- tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S | 61 ++++++++++++++++++++++ 3 files changed, 81 insertions(+), 3 deletions(-)
tools/testing/selftests/kvm/Makefile.kvm +1 -0
@@ -32,6 +32,7 @@ LIBKVM_x86 += lib/x86/svm.c LIBKVM_x86 += lib/x86/tdx/tdx_util.c LIBKVM_x86 += lib/x86/ucall.c LIBKVM_x86 += lib/x86/vmx.c +LIBKVM_x86 += lib/x86/tdx/td_boot.S LIBKVM_arm64 += lib/arm64/gic.c LIBKVM_arm64 += lib/arm64/gic_v3.c
tools/testing/selftests/kvm/include/x86/tdx/td_boot.h +19 -3
@@ -2,9 +2,6 @@#ifndef SELFTEST_TDX_TD_BOOT_H #define SELFTEST_TDX_TD_BOOT_H -#include <linux/compiler.h> -#include <linux/types.h> -/* * Layout for boot section (not to scale) *
@@ -24,7 +21,19 @@* | | * | | * |___________________________|____ 0x0_ffff_0000: TD_BOOT_PARAMETERS_GPA +* +* TD_BOOT_PARAMETERS_GPA is arbitrarily chosen to +* +* + be within the 4GB address space +* + provide enough contiguous memory for the struct td_boot_parameters such +* that there is one struct td_per_vcpu_parameters for KVM_MAX_VCPUS */ +#define TD_BOOT_PARAMETERS_GPA 0xffff0000 + +#if !defined(__ASSEMBLY__) && !defined(__ASSEMBLER__) + +#include <linux/compiler.h> +#include <linux/types.h> /* * The exact memory layout for LGDT or LIDT instructions.
@@ -63,4 +72,11 @@ struct td_boot_parameters {struct td_per_vcpu_parameters per_vcpu[]; }; +void td_boot (void ); +void td_boot_code_end (void ); + +#define TD_BOOT_CODE_SIZE (td_boot_code_end - td_boot) + +#endif /* !defined(__ASSEMBLY__) && !defined(__ASSEMBLER__) */ +#endif /* SELFTEST_TDX_TD_BOOT_H */
tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S new file +61 -0
61 lines not on this page · raw message
Signature · 1 line
2.55.0.229.g6434b31f56-goog
Re: [PATCH v14 08/22] KVM: selftests: Add TDX boot code
Quoted 6 lines · Lisa Wang
From: Erdem Aktas <[email protected]>
Add code to boot a TDX test VM. Since TDX registers are inaccessible to KVM, the boot code loads the relevant values from memory into the registers before jumping to the guest code.
Quoted 7 lines
Reviewed-by Binbin Wu <[email protected]>Signed-off-by Erdem Aktas <[email protected]>Co-developed-by Ackerley Tng <[email protected]>Signed-off-by Ackerley Tng <[email protected]>Co-developed-by Sagi Shahar <[email protected]>Signed-off-by Sagi Shahar <[email protected]>Signed-off-by Lisa Wang <[email protected]>
Quoted 132 lines
--- tools/testing/selftests/kvm/Makefile.kvm | 1 + .../selftests/kvm/include/x86/tdx/td_boot.h | 22 ++++++-- tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S | 61 ++++++++++++++++++++++ 3 files changed, 81 insertions(+), 3 deletions(-)tools/testing/selftests/kvm/Makefile.kvm +1
-0@@ -32,6 +32,7 @@ LIBKVM_x86 += lib/x86/svm.c LIBKVM_x86 += lib/x86/tdx/tdx_util.c LIBKVM_x86 += lib/x86/ucall.c LIBKVM_x86 += lib/x86/vmx.c +LIBKVM_x86 += lib/x86/tdx/td_boot.S LIBKVM_arm64 += lib/arm64/gic.c LIBKVM_arm64 += lib/arm64/gic_v3.ctools/testing/selftests/kvm/include/x86/tdx/td_boot.h +19
-3@@ -2,9 +2,6 @@#ifndef SELFTEST_TDX_TD_BOOT_H #define SELFTEST_TDX_TD_BOOT_H -#include <linux/compiler.h> -#include <linux/types.h> -/* * Layout for boot section (not to scale) * @@ -24,7 +21,19 @@* | | * | | * |___________________________|____ 0x0_ffff_0000: TD_BOOT_PARAMETERS_GPA +* +* TD_BOOT_PARAMETERS_GPA is arbitrarily chosen to +* +* + be within the 4GB address space +* + provide enough contiguous memory for the struct td_boot_parameters such +* that there is one struct td_per_vcpu_parameters for KVM_MAX_VCPUS */ +#define TD_BOOT_PARAMETERS_GPA 0xffff0000 + +#if !defined(__ASSEMBLY__) && !defined(__ASSEMBLER__) + +#include <linux/compiler.h> +#include <linux/types.h> /* * The exact memory layout for LGDT or LIDT instructions. @@ -63,4 +72,11 @@ struct td_boot_parameters {struct td_per_vcpu_parameters per_vcpu[]; }; +void td_boot (void ); +void td_boot_code_end (void ); + +#define TD_BOOT_CODE_SIZE (td_boot_code_end - td_boot) + +#endif /* !defined(__ASSEMBLY__) && !defined(__ASSEMBLER__) */ +#endif /* SELFTEST_TDX_TD_BOOT_H */ tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S new file +61
-0@@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include "tdx/td_boot.h" +#include "tdx/td_boot_offsets.h" +#include "processor_asm.h" + +.code32 + +.globl td_boot +td_boot: + /* + * In this procedure, edi is used as a temporary register. + * Paging is turned off. + */ + cli + + movl $TD_BOOT_PARAMETERS_GPA, %ebx + + /* + * Find the address of struct td_per_vcpu_parameters for this + * vCPU based on esi (TDX spec: initialized with vCPU id). Put + * struct address into register for indirect addressing. + */ + movl $SIZEOF_TD_PER_VCPU_PARAMETERS, %eax + mul %esi + leal TD_BOOT_PARAMETERS_PER_VCPU(%ebx), %edi + addl %edi, %eax + + /* Setup stack. */ + movl TD_PER_VCPU_PARAMETERS_ESP_GVA(%eax), %esp + + /* Setup GDT. */ + leal TD_BOOT_PARAMETERS_GDT(%ebx), %edi + lgdt (%edi) + + /* Setup IDT. */ + leal TD_BOOT_PARAMETERS_IDT(%ebx), %edi + lidt (%edi) + + /* + * Set up control registers (There are no instructions to mov from + * memory to control registers, hence use edi as a scratch register). + */ + movl TD_BOOT_PARAMETERS_CR4(%ebx), %edi + movl %edi, %cr4 + movl TD_BOOT_PARAMETERS_CR3(%ebx), %edi + movl %edi, %cr3 + movl TD_BOOT_PARAMETERS_CR0(%ebx), %edi + movl %edi, %cr0 + + /* Switching to 64bit mode after ljmp and then jump to guest code */ + ljmp $(KERNEL_CS),$1f +1: + jmp *TD_PER_VCPU_PARAMETERS_GUEST_CODE(%eax) + +/* Leave marker so size of td_boot code can be computed. */ +.globl td_boot_code_end +td_boot_code_end: + +/* Disable executable stack. */ +.section .note.GNU-stack,"",%progbits
Re: [PATCH v14 08/22] KVM: selftests: Add TDX boot code
In reply to Lisa WangQuoted 14 lines · Lisa Wang
From: Erdem Aktas <[email protected]>
Add code to boot a TDX test VM. Since TDX registers are inaccessible to KVM, the boot code loads the relevant values from memory into the registers before jumping to the guest code.
Reviewed-by Binbin Wu <[email protected]>Signed-off-by Erdem Aktas <[email protected]>Co-developed-by Ackerley Tng <[email protected]>Signed-off-by Ackerley Tng <[email protected]>Co-developed-by Sagi Shahar <[email protected]>Signed-off-by Sagi Shahar <[email protected]>Signed-off-by Lisa Wang <[email protected]>---
[ ... ]
Quoted 18 lines
tools/testing/selftests/kvm/include/x86/tdx/td_boot.h +0
-3@@ -2,9 +2,6 @@#ifndef SELFTEST_TDX_TD_BOOT_H #define SELFTEST_TDX_TD_BOOT_H -#include <linux/compiler.h> -#include <linux/types.h> -/* * Layout for boot section (not to scale) * @@ -24,7 +21,19 @@* | | * | | * |___________________________|____ 0x0_ffff_0000: TD_BOOT_PARAMETERS_GPA
tools/testing/selftests/kvm/include/x86/tdx/td_boot.h line 33
Quoted 10 lines
tools/testing/selftests/kvm/include/x86/tdx/td_boot.h +9
-0+* +* TD_BOOT_PARAMETERS_GPA is arbitrarily chosen to +* +* + be within the 4GB address space +* + provide enough contiguous memory for the struct td_boot_parameters such +* that there is one struct td_per_vcpu_parameters for KVM_MAX_VCPUS */ +#define TD_BOOT_PARAMETERS_GPA 0xffff0000 + +#if !defined(__ASSEMBLY__) && !defined(__ASSEMBLER__)
Is the "__ASSEMBLY__" check really needed here? I don't think the toolchain uses -D__ASSEMBLY__... Also, I think __ASSEMBLY__ is slowly being removed from the tree [1][2].
[1] https://lore.kernel.org/all/[email protected]/
[2] https://lore.kernel.org/all/[email protected]/
tools/testing/selftests/kvm/include/x86/tdx/td_boot.h struct td_boot_parameters { · line 76
Quoted 12 lines
tools/testing/selftests/kvm/include/x86/tdx/td_boot.h +5
-0+ +#include <linux/compiler.h> +#include <linux/types.h> /* * The exact memory layout for LGDT or LIDT instructions. @@ -63,4 +72,11 @@ struct td_boot_parameters {struct td_per_vcpu_parameters per_vcpu[]; }; +void td_boot (void ); +void td_boot_code_end (void );
This looks a bit off to me. td_boot_code_end is just a symbol and not a function at all. Maybe something like this?
extern u8 td_boot_code_start[], td_boot_code_end[];
Quoted 14 lines
tools/testing/selftests/kvm/include/x86/tdx/td_boot.h +5
-0+ +#define TD_BOOT_CODE_SIZE (td_boot_code_end - td_boot) + +#endif /* !defined(__ASSEMBLY__) && !defined(__ASSEMBLER__) */ +#endif /* SELFTEST_TDX_TD_BOOT_H */ tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S new file +2
-0@@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +
tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S line 12
Quoted 10 lines
tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S new file +10
-0+#include "tdx/td_boot.h" +#include "tdx/td_boot_offsets.h" +#include "processor_asm.h" + +.code32 + +.globl td_boot +td_boot: + /* + * In this procedure, edi is used as a temporary register.
Hmm, "mul %esi" clobbers edx as well. Not sure if that needs to be called out. Perhaps documenting the registers that need to be preserved thoughout this procedure (eax, ebx) is more useful. A comment below already mentions that edi is a scratch register.
tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S line 22
Quoted 10 lines
tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S new file +10
-0+ * Paging is turned off. + */ + cli + + movl $TD_BOOT_PARAMETERS_GPA, %ebx + + /* + * Find the address of struct td_per_vcpu_parameters for this + * vCPU based on esi (TDX spec: initialized with vCPU id). Put + * struct address into register for indirect addressing.
^^^^^^^^ maybe just say eax?
Quoted 6 lines
tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S new file +6
-0+ */ + movl $SIZEOF_TD_PER_VCPU_PARAMETERS, %eax + mul %esi + leal TD_BOOT_PARAMETERS_PER_VCPU(%ebx), %edi + addl %edi, %eax +
PATCH v14 9/22 KVM: selftests: Expose functions to get default sregs values
In reply to Lisa WangFrom: Sagi Shahar <[email protected]>
TDX can't set sregs values directly using KVM_SET_SREGS. Expose the default values of certain sregs used by TDX VMs so they can be set manually.
--- .../testing/selftests/kvm/include/x86/processor.h | 35 ++++++++++++++++++++++ tools/testing/selftests/kvm/lib/x86/processor.c | 20 ++++--------- 2 files changed, 41 insertions(+), 14 deletions(-)
tools/testing/selftests/kvm/include/x86/processor.h +35 -0
@@ -29,6 +29,10 @@ extern u64 guest_tsc_khz;#define MAX_NR_CPUID_ENTRIES 100#endif +#ifndef NUM_INTERRUPTS +#define NUM_INTERRUPTS 256 +#endif +#define NONCANONICAL 0xaaaaaaaaaaaaaaaaull/* Forced emulation prefix, used to invoke the emulator unconditionally. */
@@ -1564,4 +1568,35 @@ u64 *tdp_get_pte(struct kvm_vm *vm, u64 l2_gpa);bool sys_clocksource_is_based_on_tsc (void ); +static inline u16 kvm_get_default_idt_limit (void ) +{ +return NUM_INTERRUPTS *sizeof (struct idt_entry ) -1 ; +} + +static inline u16 kvm_get_default_gdt_limit (void ) +{ +return getpagesize () -1 ; +} + +static inline u64 kvm_get_default_cr0 (void ) +{ +return X86_CR0_PE |X86_CR0_NE |X86_CR0_PG ; +} + +static inline u64 kvm_get_default_cr4 (int pgtable_levels) +{ +u64 cr4 =X86_CR4_PAE |X86_CR4_OSFXSR ; + +if (kvm_cpu_has (X86_FEATURE_XSAVE )) + cr4 |=X86_CR4_OSXSAVE ; +if (pgtable_levels ==5 ) + cr4 |=X86_CR4_LA57 ; +return cr4; +} + +static inline u64 kvm_get_default_efer (void ) +{ +return EFER_LME |EFER_LMA |EFER_NX ; +} +#endif /* SELFTEST_KVM_PROCESSOR_H */
tools/testing/selftests/kvm/lib/x86/processor.c +6 -14
@@ -15,10 +15,6 @@#include "tdx/tdx_util.h" #include "vmx.h" -#ifndef NUM_INTERRUPTS -#define NUM_INTERRUPTS 256 -#endif -gva_t exception_handlers;bool host_cpu_is_amd;bool host_cpu_is_intel;
@@ -647,17 +643,13 @@ static void vcpu_init_sregs(struct kvm_vm *vm, struct kvm_vcpu *vcpu)vcpu_sregs_get (vcpu, &sregs); sregs.idt.base = vm->arch.idt; - sregs.idt.limit =NUM_INTERRUPTS *sizeof (struct idt_entry ) -1 ; + sregs.idt.limit =kvm_get_default_idt_limit (); sregs.gdt.base = vm->arch.gdt; - sregs.gdt.limit =getpagesize () -1 ; - - sregs.cr0 =X86_CR0_PE |X86_CR0_NE |X86_CR0_PG ; - sregs.cr4 |=X86_CR4_PAE |X86_CR4_OSFXSR ; -if (kvm_cpu_has (X86_FEATURE_XSAVE )) - sregs.cr4 |=X86_CR4_OSXSAVE ; -if (vm->mmu.pgtable_levels ==5 ) - sregs.cr4 |=X86_CR4_LA57 ; - sregs.efer |= (EFER_LME |EFER_LMA |EFER_NX ); + sregs.gdt.limit =kvm_get_default_gdt_limit (); + + sregs.cr0 =kvm_get_default_cr0 (); + sregs.cr4 |=kvm_get_default_cr4 (vm->mmu.pgtable_levels); + sregs.efer |=kvm_get_default_efer ();kvm_seg_set_unusable (&sregs.ldt);kvm_seg_set_kernel_code_64bit (&sregs.cs);
Signature · 1 line
2.55.0.229.g6434b31f56-goog
Re: [PATCH v14 09/22] KVM: selftests: Expose functions to get default sregs values
Quoted 10 lines · Lisa Wang
From: Sagi Shahar <[email protected]>
TDX can't set sregs values directly using
KVM_SET_SREGS. Expose the default values of certain sregs used by TDX VMs so they can be set manually.Reviewed-by Binbin Wu <[email protected]>Signed-off-by Sagi Shahar <[email protected]>Reviewed-by Ira Weiny <[email protected]>Signed-off-by Lisa Wang <[email protected]>
Quoted 96 lines
--- .../testing/selftests/kvm/include/x86/processor.h | 35 ++++++++++++++++++++++ tools/testing/selftests/kvm/lib/x86/processor.c | 20 ++++--------- 2 files changed, 41 insertions(+), 14 deletions(-)tools/testing/selftests/kvm/include/x86/processor.h +35
-0@@ -29,6 +29,10 @@ extern u64 guest_tsc_khz;#define MAX_NR_CPUID_ENTRIES 100#endif +#ifndef NUM_INTERRUPTS +#define NUM_INTERRUPTS 256 +#endif +#define NONCANONICAL 0xaaaaaaaaaaaaaaaaull/* Forced emulation prefix, used to invoke the emulator unconditionally. */ @@ -1564,4 +1568,35 @@ u64 *tdp_get_pte(struct kvm_vm *vm, u64 l2_gpa);bool sys_clocksource_is_based_on_tsc (void ); +static inline u16 kvm_get_default_idt_limit (void ) +{ +return NUM_INTERRUPTS *sizeof (struct idt_entry ) -1 ; +} + +static inline u16 kvm_get_default_gdt_limit (void ) +{ +return getpagesize () -1 ; +} + +static inline u64 kvm_get_default_cr0 (void ) +{ +return X86_CR0_PE |X86_CR0_NE |X86_CR0_PG ; +} + +static inline u64 kvm_get_default_cr4 (int pgtable_levels) +{ +u64 cr4 =X86_CR4_PAE |X86_CR4_OSFXSR ; + +if (kvm_cpu_has (X86_FEATURE_XSAVE )) + cr4 |=X86_CR4_OSXSAVE ; +if (pgtable_levels ==5 ) + cr4 |=X86_CR4_LA57 ; +return cr4; +} + +static inline u64 kvm_get_default_efer (void ) +{ +return EFER_LME |EFER_LMA |EFER_NX ; +} +#endif /* SELFTEST_KVM_PROCESSOR_H */ tools/testing/selftests/kvm/lib/x86/processor.c +6
-14@@ -15,10 +15,6 @@#include "tdx/tdx_util.h" #include "vmx.h" -#ifndef NUM_INTERRUPTS -#define NUM_INTERRUPTS 256 -#endif -gva_t exception_handlers;bool host_cpu_is_amd;bool host_cpu_is_intel;@@ -647,17 +643,13 @@ static void vcpu_init_sregs(struct kvm_vm *vm, struct kvm_vcpu *vcpu)vcpu_sregs_get (vcpu, &sregs); sregs.idt.base = vm->arch.idt; - sregs.idt.limit =NUM_INTERRUPTS *sizeof (struct idt_entry ) -1 ; + sregs.idt.limit =kvm_get_default_idt_limit (); sregs.gdt.base = vm->arch.gdt; - sregs.gdt.limit =getpagesize () -1 ; - - sregs.cr0 =X86_CR0_PE |X86_CR0_NE |X86_CR0_PG ; - sregs.cr4 |=X86_CR4_PAE |X86_CR4_OSFXSR ; -if (kvm_cpu_has (X86_FEATURE_XSAVE )) - sregs.cr4 |=X86_CR4_OSXSAVE ; -if (vm->mmu.pgtable_levels ==5 ) - sregs.cr4 |=X86_CR4_LA57 ; - sregs.efer |= (EFER_LME |EFER_LMA |EFER_NX ); + sregs.gdt.limit =kvm_get_default_gdt_limit (); + + sregs.cr0 =kvm_get_default_cr0 (); + sregs.cr4 |=kvm_get_default_cr4 (vm->mmu.pgtable_levels); + sregs.efer |=kvm_get_default_efer ();kvm_seg_set_unusable (&sregs.ldt);kvm_seg_set_kernel_code_64bit (&sregs.cs);
Edgecombe, Rick P
maintainerRe: [PATCH v14 09/22] KVM: selftests: Expose functions to get default sregs values
In reply to Lisa Wangtools/testing/selftests/kvm/include/x86/processor.h extern u64 guest_tsc_khz; · line 35
Quoted 5 lines · Lisa Wang
tools/testing/selftests/kvm/include/x86/processor.h +4
-0+#ifndef NUM_INTERRUPTS +#define NUM_INTERRUPTS 256 +#endif +
Is this necessary for this patch? I see why to extract kvm_get_default_idt_limit() because now it's used twice. But why extract this define too?
Re: [PATCH v14 09/22] KVM: selftests: Expose functions to get default sregs values
Quoted 10 lines · Rick P
tools/testing/selftests/kvm/include/x86/processor.h extern u64 guest_tsc_khz; · line 35
Quoted 5 lines · Lisa Wang
tools/testing/selftests/kvm/include/x86/processor.h +4
-0+#ifndef NUM_INTERRUPTS +#define NUM_INTERRUPTS 256 +#endif +Is this necessary for this patch? I see why to extract
kvm_get_default_idt_limit()because now it's used twice. But why extract this define too?
kvm_get_default_idt_limit() uses NUM_INTERRUPTS, and kvm_get_default_idt_limit() is extracted as a helper from .c file to .h file. Shouldn't the define of NUM_INTERRUPTS be moved together?
Edgecombe, Rick P
maintainerRe: [PATCH v14 09/22] KVM: selftests: Expose functions to get default sregs values
Quoted 8 lines · Xiaoyao Li
Quoted 4 lines
Is this necessary for this patch? I see why to extract
kvm_get_default_idt_limit()because now it's used twice. But why extract this
define too?
kvm_get_default_idt_limit()usesNUM_INTERRUPTS, andkvm_get_default_idt_limit()is extracted as a helper from .c file to .h file. Shouldn't the define ofNUM_INTERRUPTSbe moved together?
Err. yes. My bad. Trying to do too many things at once.
PATCH v14 10/22 KVM: selftests: Set up TDX boot code region
In reply to Lisa WangFrom: Sagi Shahar <[email protected]>
Add memory for TDX boot code in a separate memslot.
Use virt_map() to get identity map in this memory region to allow for seamless transition from paging disabled to paging enabled code.
Copy the boot code into the memory region and set up the reset vector at this point. While it's possible to separate the memory allocation and boot code initialization into separate functions, having all the calculations for memory size and offsets in one place simplifies the code and avoids duplications.
Handcode the reset vector as suggested by Sean Christopherson.
--- .../selftests/kvm/include/x86/tdx/tdx_util.h | 1 + tools/testing/selftests/kvm/lib/x86/processor.c | 4 +- tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c | 46 ++++++++++++++++++++++ 3 files changed, 50 insertions(+), 1 deletion(-)
tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h +1 -0
@@ -45,5 +45,6 @@ static inline bool is_tdx_vm(struct kvm_vm *vm) })void tdx_init_vm (struct kvm_vm *vm,u64 attributes); +void tdx_vm_setup_boot_code_region (struct kvm_vm *vm);#endif /* SELFTESTS_TDX_TDX_UTIL_H */
tools/testing/selftests/kvm/lib/x86/processor.c +3 -1
@@ -791,8 +791,10 @@ void kvm_arch_vm_post_create(struct kvm_vm *vm, unsigned int nr_vcpus)vm_sev_ioctl (vm,KVM_SEV_INIT2 , &init); } -if (is_tdx_vm (vm)) +if (is_tdx_vm (vm)) {tdx_init_vm (vm,0 ); +tdx_vm_setup_boot_code_region (vm); + } r =__vm_ioctl (vm,KVM_GET_TSC_KHZ ,NULL );TEST_ASSERT (r >0 ,"KVM_GET_TSC_KHZ did not provide a valid TSC frequency." );
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +46 -0
54 lines not on this page · raw message
Signature · 1 line
2.55.0.229.g6434b31f56-goog
Re: [PATCH v14 10/22] KVM: selftests: Set up TDX boot code region
Quoted 13 lines · Lisa Wang
From: Sagi Shahar <[email protected]>
Add memory for TDX boot code in a separate memslot.
Use
virt_map()to get identity map in this memory region to allow for seamless transition from paging disabled to paging enabled code.Copy the boot code into the memory region and set up the reset vector at this point. While it's possible to separate the memory allocation and boot code initialization into separate functions, having all the calculations for memory size and offsets in one place simplifies the code and avoids duplications.
Quoted 8 lines
Handcode the reset vector as suggested by Sean Christopherson.
Reviewed-by Binbin Wu <[email protected]>Suggested-by Sean Christopherson <[email protected]>Co-developed-by Erdem Aktas <[email protected]>Signed-off-by Erdem Aktas <[email protected]>Signed-off-by Sagi Shahar <[email protected]>Signed-off-by Lisa Wang <[email protected]>
Re: [PATCH v14 10/22] KVM: selftests: Set up TDX boot code region
In reply to Lisa WangQuoted 27 lines · Lisa Wang
From: Sagi Shahar <[email protected]>
Add memory for TDX boot code in a separate memslot.
Use
virt_map()to get identity map in this memory region to allow for seamless transition from paging disabled to paging enabled code.Copy the boot code into the memory region and set up the reset vector at this point. While it's possible to separate the memory allocation and boot code initialization into separate functions, having all the calculations for memory size and offsets in one place simplifies the code and avoids duplications.
Handcode the reset vector as suggested by Sean Christopherson.
Reviewed-by Binbin Wu <[email protected]>Suggested-by Sean Christopherson <[email protected]>Co-developed-by Erdem Aktas <[email protected]>Signed-off-by Erdem Aktas <[email protected]>Signed-off-by Sagi Shahar <[email protected]>Signed-off-by Lisa Wang <[email protected]>--- .../selftests/kvm/include/x86/tdx/tdx_util.h | 1 + tools/testing/selftests/kvm/lib/x86/processor.c | 4 +- tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c | 46 ++++++++++++++++++++++ 3 files changed, 50 insertions(+), 1 deletion(-)
[ ... ]
Quoted 28 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +28
-0+ +void tdx_vm_setup_boot_code_region (struct kvm_vm *vm) +{ +size_t total_code_size =TD_BOOT_CODE_SIZE +X86_RESET_VECTOR_SIZE ; +gpa_t boot_code_gpa =X86_RESET_VECTOR -TD_BOOT_CODE_SIZE ; +gpa_t alloc_gpa =round_down (boot_code_gpa,PAGE_SIZE ); +size_t nr_pages =DIV_ROUND_UP (total_code_size,PAGE_SIZE ); +const u64 gmem_flags =0 ; +gpa_t gpa; +u8 *hva; + +vm_mem_add (vm,VM_MEM_SRC_SHMEM , alloc_gpa,TD_BOOT_CODE_SLOT , + nr_pages,KVM_MEM_GUEST_MEMFD ,-1 ,0 , gmem_flags); + + gpa =vm_phy_pages_alloc (vm, nr_pages, alloc_gpa,TD_BOOT_CODE_SLOT ); +TEST_ASSERT (gpa == alloc_gpa,"Failed vm_phy_pages_alloc\n" ); + +virt_map (vm, alloc_gpa, alloc_gpa, nr_pages); + hva =addr_gpa2hva (vm, boot_code_gpa); +memcpy (hva, td_boot,TD_BOOT_CODE_SIZE ); + + hva +=TD_BOOT_CODE_SIZE ; +TEST_ASSERT (hva ==addr_gpa2hva (vm,X86_RESET_VECTOR ), +"Expected RESET vector at hva 0x%lx, got %lx" , + (unsigned long )addr_gpa2hva (vm,X86_RESET_VECTOR ), (unsigned long )hva); + +/* +* Handcode "JMP rel8" at the RESET vector to jump back to the TD boot
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c line 49
Quoted 10 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +10
-0+* code, as there are only 16 bytes at the RESET vector before RIP will +* wrap back to zero. Insert a trailing int3 so that the vCPU crashes in +* case the JMP somehow falls through. Note! The target address is +* relative to the end of the instruction! +*/ +TEST_ASSERT (TD_BOOT_CODE_SIZE +2 <=128 , +"TD boot code not addressable by 'JMP rel8'" ); + hva[0 ] =0xeb ; + hva[1 ] =256 -2 -TD_BOOT_CODE_SIZE ; + hva[2 ] =0xcc ;
This handcoding has persisted for several versions (since v9) so I only want to comment gently... The current code looks correct but I think this could be simpler. But feel free to keep the current implementation.
I can see handcoding "JMP rel8" generates a jump instruction predictably. But having to calculate and sanity check the boot code offset by hand seems quite painful. And the math is quite tricky. I was thinking the assembler could do a lot more heavy lifting here.
A nice property of this boot code is the fact that it's executed in 32-bit mode from the get go. So there's no need for 16-bit programming and "JMP rel32" is readily available. I.e. a "jmp td_boot" in td_boot.S should suffice and the assembler screams if td_boot isn't reachable. And the int3's after the jump can probably go away as well since the jump is now very, very likely to succeed.
There shouldn't be a need to "pad reset_vector to its full size of 16 bytes" as stated in v8 [1]. The exported "reset_vector" symbol in v8, plus the boot code start & end markers, should be enough to help tdx_vm_setup_boot_code_region() put this boot blob in the right place.
Quoted 10 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +2
-0+} +static struct kvm_tdx_capabilities *tdx_read_capabilities (struct kvm_vm *vm) {static struct kvm_tdx_capabilities *tdx_cap;Signature · 2 lines
-- 2.55.0.229.g6434b31f56-goog
Sean Christopherson
maintainerRe: [PATCH v14 10/22] KVM: selftests: Set up TDX boot code region
Quoted 55 lines · Peter Fang
Quoted 27 lines · Lisa Wang
(quoted diff) +27
-0+void tdx_vm_setup_boot_code_region(struct kvm_vm *vm) +{ + size_t total_code_size = TD_BOOT_CODE_SIZE + X86_RESET_VECTOR_SIZE; + gpa_t boot_code_gpa = X86_RESET_VECTOR - TD_BOOT_CODE_SIZE; + gpa_t alloc_gpa = round_down(boot_code_gpa, PAGE_SIZE); + size_t nr_pages = DIV_ROUND_UP(total_code_size, PAGE_SIZE); + const u64 gmem_flags = 0; + gpa_t gpa; + u8 *hva; + + vm_mem_add(vm, VM_MEM_SRC_SHMEM, alloc_gpa, TD_BOOT_CODE_SLOT, + nr_pages, KVM_MEM_GUEST_MEMFD, -1, 0, gmem_flags); + + gpa = vm_phy_pages_alloc(vm, nr_pages, alloc_gpa, TD_BOOT_CODE_SLOT); + TEST_ASSERT(gpa == alloc_gpa, "Failed vm_phy_pages_alloc\n"); + + virt_map(vm, alloc_gpa, alloc_gpa, nr_pages); + hva = addr_gpa2hva(vm, boot_code_gpa); + memcpy(hva, td_boot, TD_BOOT_CODE_SIZE); + + hva += TD_BOOT_CODE_SIZE; + TEST_ASSERT(hva == addr_gpa2hva(vm, X86_RESET_VECTOR), + "Expected RESET vector at hva 0x%lx, got %lx", + (unsigned long)addr_gpa2hva(vm, X86_RESET_VECTOR), (unsigned long)hva); + + /* + * Handcode "JMP rel8" at the RESET vector to jump back to the TD bootReview
Quoted 10 lines
(quoted diff) +10
-0+ * code, as there are only 16 bytes at the RESET vector before RIP will + * wrap back to zero. Insert a trailing int3 so that the vCPU crashes in + * case the JMP somehow falls through. Note! The target address is + * relative to the end of the instruction! + */ + TEST_ASSERT(TD_BOOT_CODE_SIZE + 2 <= 128, + "TD boot code not addressable by 'JMP rel8'"); + hva[0] = 0xeb; + hva[1] = 256 - 2 - TD_BOOT_CODE_SIZE; + hva[2] = 0xcc;This handcoding has persisted for several versions (since v9) so I only want to comment gently... The current code looks correct but I think this could be simpler. But feel free to keep the current implementation.
I can see handcoding "JMP rel8" generates a jump instruction predictably. But having to calculate and sanity check the boot code offset by hand seems quite painful. And the math is quite tricky. I was thinking the assembler could do a lot more heavy lifting here.
A nice property of this boot code is the fact that it's executed in 32-bit mode from the get go. So there's no need for 16-bit programming and "JMP rel32" is readily available. I.e. a "jmp
td_boot" intd_boot.Sshould suffice and the assembler screams iftd_bootisn't reachable. And the int3's after the jump can probably go away as well since the jump is now very, very likely to succeed.
Quoted 4 lines
There shouldn't be a need to "pad
reset_vectorto its full size of 16 bytes" as stated in v8 [1]. The exported "reset_vector" symbol in v8, plus the boot code start & end markers, should be enough to helptdx_vm_setup_boot_code_region()put this boot blob in the right place.
Ya, looking at this with fresh eyes, AFAICT there's no reason to handcode anything, it's just basic arithmetic.
Side topic, this series doesn't compile for me, so the below isn't even properly compile-tested (I hacked in arbitrary literals to get past the undefined references).
/usr/bin/x86_64-linux-gnu-ld.bfd: tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:26:(.text+0xf): undefined reference to TD_BOOT_PARAMETERS_PER_VCPU' /usr/bin/x86_64-linux-gnu-ld.bfd: tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:30:(.text+0x17): undefined reference to TD_PER_VCPU_PARAMETERS_ESP_GVA' /usr/bin/x86_64-linux-gnu-ld.bfd: tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:33:(.text+0x1d): undefined reference to TD_BOOT_PARAMETERS_GDT' /usr/bin/x86_64-linux-gnu-ld.bfd: tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:37:(.text+0x26): undefined reference to TD_BOOT_PARAMETERS_IDT' /usr/bin/x86_64-linux-gnu-ld.bfd: tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:44:(.text+0x2f): undefined reference to TD_BOOT_PARAMETERS_CR4' /usr/bin/x86_64-linux-gnu-ld.bfd: tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:46:(.text+0x38): undefined reference to TD_BOOT_PARAMETERS_CR3' /usr/bin/x86_64-linux-gnu-ld.bfd: tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:48:(.text+0x41): undefined reference to TD_BOOT_PARAMETERS_CR0' /usr/bin/x86_64-linux-gnu-ld.bfd: tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:54:(.text+0x51): undefined reference to TD_PER_VCPU_PARAMETERS_GUEST_CODE'
tools/testing/selftests/kvm/include/x86/tdx/td_boot.h +1 -2
@@ -73,10 +73,9 @@ struct td_boot_parameters { };void td_boot (void ); +void td_boot_reset_vector_trampoline (void );void td_boot_code_end (void ); -#define TD_BOOT_CODE_SIZE (td_boot_code_end - td_boot) -#endif /* !defined(__ASSEMBLY__) && !defined(__ASSEMBLER__) */ #endif /* SELFTEST_TDX_TD_BOOT_H */
tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S +6 -0
@@ -52,6 +52,12 @@ td_boot: ljmp $(KERNEL_CS),$1f 1: jmp *TD_PER_VCPU_PARAMETERS_GUEST_CODE(%eax) + int3 + +.globl td_boot_reset_vector_trampoline +td_boot_reset_vector_trampoline: + jmp td_boot + int3 /* Leave marker so size of td_boot code can be computed. */ .globl td_boot_code_end
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +6 -18
@@ -15,10 +15,11 @@void tdx_vm_setup_boot_code_region (struct kvm_vm *vm) { -size_t total_code_size =TD_BOOT_CODE_SIZE +X86_RESET_VECTOR_SIZE ; -gpa_t boot_code_gpa =X86_RESET_VECTOR -TD_BOOT_CODE_SIZE ; +const size_t total_size = td_boot_code_end - td_boot; +const size_t boot_code_size = td_boot_reset_vector_trampoline - td_boot; +const gpa_t boot_code_gpa =X86_RESET_VECTOR - boot_code_size;gpa_t alloc_gpa =round_down (boot_code_gpa,PAGE_SIZE ); -size_t nr_pages =DIV_ROUND_UP (total_code_size,PAGE_SIZE ); +size_t nr_pages =DIV_ROUND_UP (total_size,PAGE_SIZE );u64 gmem_flags =0 ;gpa_t gpa;u8 *hva;
@@ -33,25 +34,12 @@ void tdx_vm_setup_boot_code_region(struct kvm_vm *vm)virt_map (vm, alloc_gpa, alloc_gpa, nr_pages); hva =addr_gpa2hva (vm, boot_code_gpa); -memcpy (hva, td_boot,TD_BOOT_CODE_SIZE ); +memcpy (hva, td_boot, total_size); - hva +=TD_BOOT_CODE_SIZE ; + hva += boot_code_size;TEST_ASSERT (hva ==addr_gpa2hva (vm,X86_RESET_VECTOR ),"Expected RESET vector at hva 0x%lx, got %lx" , (unsigned long )addr_gpa2hva (vm,X86_RESET_VECTOR ), (unsigned long )hva); - -/* -* Handcode "JMP rel8" at the RESET vector to jump back to the TD boot -* code, as there are only 16 bytes at the RESET vector before RIP will -* wrap back to zero. Insert a trailing int3 so that the vCPU crashes in -* case the JMP somehow falls through. Note! The target address is -* relative to the end of the instruction! -*/ -TEST_ASSERT (TD_BOOT_CODE_SIZE +2 <=128 , -"TD boot code not addressable by 'JMP rel8'" ); - hva[0 ] =0xeb ; - hva[1 ] =256 -2 -TD_BOOT_CODE_SIZE ; - hva[2 ] =0xcc ; }void tdx_vm_setup_boot_parameters_region (struct kvm_vm *vm,u32 nr_runnable_vcpus)
Re: [PATCH v14 10/22] KVM: selftests: Set up TDX boot code region
tools/testing/selftests/kvm/include/x86/tdx/td_boot.h
Quoted 21 lines · Sean Christopherson
Ya, looking at this with fresh eyes, AFAICT there's no reason to handcode anything, it's just basic arithmetic.
Side topic, this series doesn't compile for me, so the below isn't even properly compile-tested (I hacked in arbitrary literals to get past the undefined references).
/usr/bin/x86_64-linux-gnu-ld.bfd: tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:26:(.text+0xf): undefined reference toTD_BOOT_PARAMETERS_PER_VCPU' /usr/bin/x86_64-linux-gnu-ld.bfd: tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:30:(.text+0x17): undefined reference toTD_PER_VCPU_PARAMETERS_ESP_GVA'/usr/bin/x86_64-linux-gnu-ld.bfd: tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:33:(.text+0x1d): undefined reference toTD_BOOT_PARAMETERS_GDT' /usr/bin/x86_64-linux-gnu-ld.bfd: tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:37:(.text+0x26): undefined reference toTD_BOOT_PARAMETERS_IDT'/usr/bin/x86_64-linux-gnu-ld.bfd: tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:44:(.text+0x2f): undefined reference toTD_BOOT_PARAMETERS_CR4' /usr/bin/x86_64-linux-gnu-ld.bfd: tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:46:(.text+0x38): undefined reference toTD_BOOT_PARAMETERS_CR3'/usr/bin/x86_64-linux-gnu-ld.bfd: tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:48:(.text+0x41): undefined reference toTD_BOOT_PARAMETERS_CR0' /usr/bin/x86_64-linux-gnu-ld.bfd: tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:54:(.text+0x51): undefined reference toTD_PER_VCPU_PARAMETERS_GUEST_CODE'tools/testing/selftests/kvm/include/x86/tdx/td_boot.h +0
-0
Thanks Sean! This diff is pretty much what I had in mind. Just one comment below...
Quoted 6 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +0
-0@@ -15,10 +15,11 @@
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c line 22
Quoted 10 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +4
-3void tdx_vm_setup_boot_code_region (struct kvm_vm *vm) { -size_t total_code_size =TD_BOOT_CODE_SIZE +X86_RESET_VECTOR_SIZE ; -gpa_t boot_code_gpa =X86_RESET_VECTOR -TD_BOOT_CODE_SIZE ; +const size_t total_size = td_boot_code_end - td_boot; +const size_t boot_code_size = td_boot_reset_vector_trampoline - td_boot; +const gpa_t boot_code_gpa =X86_RESET_VECTOR - boot_code_size;gpa_t alloc_gpa =round_down (boot_code_gpa,PAGE_SIZE ); -size_t nr_pages =DIV_ROUND_UP (total_code_size,PAGE_SIZE ); +size_t nr_pages =DIV_ROUND_UP (total_size,PAGE_SIZE );
td_boot_code_end and td_boot could both be non-page-aligned, so instead this can be:
size_t nr_pages = (SZ_4G - alloc_gpa) / PAGE_SIZE;
Quoted 3 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +0
-0u64 gmem_flags =0 ;gpa_t gpa;u8 *hva;
Re: [PATCH v14 10/22] KVM: selftests: Set up TDX boot code region
In reply to Sean ChristophersonQuoted 12 lines · Sean Christopherson
Quoted 5 lines
There shouldn't be a need to "pad
reset_vectorto its full size of 16 bytes" as stated in v8 [1]. The exported "reset_vector" symbol in v8, plus the boot code start & end markers, should be enough to helptdx_vm_setup_boot_code_region()put this boot blob in the right place.Ya, looking at this with fresh eyes, AFAICT there's no reason to handcode anything, it's just basic arithmetic.
Side topic, this series doesn't compile for me, so the below isn't even properly compile-tested (I hacked in arbitrary literals to get past the undefined references).
Quoted 8 lines
/usr/bin/x86_64-linux-gnu-ld.bfd: tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:26:(.text+0xf): undefined reference toTD_BOOT_PARAMETERS_PER_VCPU' /usr/bin/x86_64-linux-gnu-ld.bfd: tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:30:(.text+0x17): undefined reference toTD_PER_VCPU_PARAMETERS_ESP_GVA'/usr/bin/x86_64-linux-gnu-ld.bfd: tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:33:(.text+0x1d): undefined reference toTD_BOOT_PARAMETERS_GDT' /usr/bin/x86_64-linux-gnu-ld.bfd: tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:37:(.text+0x26): undefined reference toTD_BOOT_PARAMETERS_IDT'/usr/bin/x86_64-linux-gnu-ld.bfd: tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:44:(.text+0x2f): undefined reference toTD_BOOT_PARAMETERS_CR4' /usr/bin/x86_64-linux-gnu-ld.bfd: tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:46:(.text+0x38): undefined reference toTD_BOOT_PARAMETERS_CR3'/usr/bin/x86_64-linux-gnu-ld.bfd: tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:48:(.text+0x41): undefined reference toTD_BOOT_PARAMETERS_CR0' /usr/bin/x86_64-linux-gnu-ld.bfd: tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:54:(.text+0x51): undefined reference toTD_PER_VCPU_PARAMETERS_GUEST_CODE'
The patch series posted here on the mailing list is correct, but I made a one-line mistake when pushing the code to the GitHub branch. I have just force-pushed the fix to the branch. It should compile correctly now. Please let me know if you run into any further compilation issues.
I will test this change on my side. Thanks!
Lisa
PATCH v14 11/22 KVM: selftests: Set up TDX boot parameters region
In reply to Lisa WangFrom: Sagi Shahar <[email protected]>
Allocate memory for TDX boot parameters and define the utility functions necessary to fill this memory with the boot parameters.
--- .../selftests/kvm/include/x86/tdx/tdx_util.h | 2 + tools/testing/selftests/kvm/lib/x86/processor.c | 2 + tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c | 51 ++++++++++++++++++++++ 3 files changed, 55 insertions(+)
tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h +2 -0
@@ -46,5 +46,7 @@ static inline bool is_tdx_vm(struct kvm_vm *vm)void tdx_init_vm (struct kvm_vm *vm,u64 attributes);void tdx_vm_setup_boot_code_region (struct kvm_vm *vm); +void tdx_vm_setup_boot_parameters_region (struct kvm_vm *vm,u32 nr_runnable_vcpus); +void tdx_vm_load_common_boot_parameters (struct kvm_vm *vm);#endif /* SELFTESTS_TDX_TDX_UTIL_H */
tools/testing/selftests/kvm/lib/x86/processor.c +2 -0
@@ -794,6 +794,8 @@ void kvm_arch_vm_post_create(struct kvm_vm *vm, unsigned int nr_vcpus)if (is_tdx_vm (vm)) {tdx_init_vm (vm,0 );tdx_vm_setup_boot_code_region (vm); +tdx_vm_setup_boot_parameters_region (vm, nr_vcpus); +tdx_vm_load_common_boot_parameters (vm); } r =__vm_ioctl (vm,KVM_GET_TSC_KHZ ,NULL );
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +51 -0
63 lines not on this page · raw message
Signature · 1 line
2.55.0.229.g6434b31f56-goog
Re: [PATCH v14 11/22] KVM: selftests: Set up TDX boot parameters region
Quoted 9 lines · Lisa Wang
From: Sagi Shahar <[email protected]>
Allocate memory for TDX boot parameters and define the utility functions necessary to fill this memory with the boot parameters.
Co-developed-by Ackerley Tng <[email protected]>Signed-off-by Ackerley Tng <[email protected]>Signed-off-by Sagi Shahar <[email protected]>Signed-off-by Lisa Wang <[email protected]>
2 nits below. I think you can keep the code as-is unless maintainers ask you to change.
Quoted 44 lines
--- .../selftests/kvm/include/x86/tdx/tdx_util.h | 2 + tools/testing/selftests/kvm/lib/x86/processor.c | 2 + tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c | 51 ++++++++++++++++++++++ 3 files changed, 55 insertions(+)tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h +2
-0@@ -46,5 +46,7 @@ static inline bool is_tdx_vm(struct kvm_vm *vm)void tdx_init_vm (struct kvm_vm *vm,u64 attributes);void tdx_vm_setup_boot_code_region (struct kvm_vm *vm); +void tdx_vm_setup_boot_parameters_region (struct kvm_vm *vm,u32 nr_runnable_vcpus); +void tdx_vm_load_common_boot_parameters (struct kvm_vm *vm);#endif /* SELFTESTS_TDX_TDX_UTIL_H */ tools/testing/selftests/kvm/lib/x86/processor.c +2
-0@@ -794,6 +794,8 @@ void kvm_arch_vm_post_create(struct kvm_vm *vm, unsigned int nr_vcpus)if (is_tdx_vm (vm)) {tdx_init_vm (vm,0 );tdx_vm_setup_boot_code_region (vm); +tdx_vm_setup_boot_parameters_region (vm, nr_vcpus); +tdx_vm_load_common_boot_parameters (vm); } r =__vm_ioctl (vm,KVM_GET_TSC_KHZ ,NULL );tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +1
-0@@ -6,6 +6,7 @@/* Arbitrarily selected to avoid overlaps with anything else */ #define TD_BOOT_CODE_SLOT 20 +#define TD_BOOT_PARAMETERS_SLOT 21#define X86_RESET_VECTOR 0xfffffff0ul#define X86_RESET_VECTOR_SIZE 16
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c void tdx_vm_setup_boot_code_region(struct kvm_vm *vm) · line 57
Quoted 8 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +5
-0@@ -49,6 +50,56 @@ void tdx_vm_setup_boot_code_region(struct kvm_vm *vm) hva[2 ] =0xcc ; } +void tdx_vm_setup_boot_parameters_region (struct kvm_vm *vm,u32 nr_runnable_vcpus) +{ +size_t boot_params_size = +sizeof (struct td_boot_parameters ) + + nr_runnable_vcpus *sizeof (struct td_per_vcpu_parameters );
Nit: I'm not sure if the style is acceptable to selftest or not. From KVM coding style, it's not common.
Quoted 7 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +7
-0+int npages =DIV_ROUND_UP (boot_params_size,PAGE_SIZE ); +const u64 gmem_flags =0 ; +gpa_t gpa; + +vm_mem_add (vm,VM_MEM_SRC_SHMEM ,TD_BOOT_PARAMETERS_GPA , +TD_BOOT_PARAMETERS_SLOT , npages, +KVM_MEM_GUEST_MEMFD ,-1 ,0 , gmem_flags);
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c void tdx_vm_setup_boot_code_region(struct kvm_vm *vm) · line 74
Quoted 10 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +10
-0+ gpa =vm_phy_pages_alloc (vm, npages,TD_BOOT_PARAMETERS_GPA ,TD_BOOT_PARAMETERS_SLOT ); +TEST_ASSERT (gpa ==TD_BOOT_PARAMETERS_GPA ,"Failed vm_phy_pages_alloc\n" ); + +virt_map (vm,TD_BOOT_PARAMETERS_GPA ,TD_BOOT_PARAMETERS_GPA , npages); +} + +void tdx_vm_load_common_boot_parameters (struct kvm_vm *vm) +{ +struct td_boot_parameters *params = +addr_gpa2hva (vm,TD_BOOT_PARAMETERS_GPA );
Nit: I think it's OK to make it just one line.
Quoted 32 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +28
-0+u32 cr4; + + cr4 =kvm_get_default_cr4 (vm->mmu.pgtable_levels); + +/* TDX spec 11.6.2: CR4 bit MCE is fixed to 1 */ + cr4 |=X86_CR4_MCE ; + +/* TDX spec 11.6.2: CR4 bit VMXE and SMXE are fixed to 0 */ + cr4 &= ~(X86_CR4_VMXE |X86_CR4_SMXE ); + +/* Set parameters! */ + params->cr0 =kvm_get_default_cr0 (); +TEST_ASSERT (vm->mmu.pgd < (1ULL <<32 ), +"PGD must be within 32-bit address space for 32-bit boot code" ); + params->cr3 = vm->mmu.pgd; + params->cr4 = cr4; + params->idtr.base = vm->arch.idt; + params->idtr.limit =kvm_get_default_idt_limit (); + params->gdtr.base = vm->arch.gdt; + params->gdtr.limit =kvm_get_default_gdt_limit (); + +TEST_ASSERT (params->cr0 !=0 ,"cr0 should not be 0" ); +TEST_ASSERT (params->cr3 !=0 ,"cr3 should not be 0" ); +TEST_ASSERT (params->cr4 !=0 ,"cr4 should not be 0" ); +TEST_ASSERT (params->gdtr.base !=0 ,"gdt base address should not be 0" ); +TEST_ASSERT (params->idtr.base !=0 ,"idt base address should not be 0" ); +} +static struct kvm_tdx_capabilities *tdx_read_capabilities (struct kvm_vm *vm) {static struct kvm_tdx_capabilities *tdx_cap;
Re: [PATCH v14 11/22] KVM: selftests: Set up TDX boot parameters region
In reply to Lisa WangQuoted 4 lines · Lisa Wang
From: Sagi Shahar <[email protected]>
Allocate memory for TDX boot parameters and define the utility functions necessary to fill this memory with the boot parameters.
The code change looks good to me.
But I think it's better to describe how the parameters are initialized based on the HVA when VM_MEM_SRC_SHMEM is used alongside KVM_MEM_GUEST_MEMFD. Same for the patch 10.
Quoted 106 lines
Co-developed-by Ackerley Tng <[email protected]>Signed-off-by Ackerley Tng <[email protected]>Signed-off-by Sagi Shahar <[email protected]>Signed-off-by Lisa Wang <[email protected]>--- .../selftests/kvm/include/x86/tdx/tdx_util.h | 2 + tools/testing/selftests/kvm/lib/x86/processor.c | 2 + tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c | 51 ++++++++++++++++++++++ 3 files changed, 55 insertions(+)tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h +2
-0@@ -46,5 +46,7 @@ static inline bool is_tdx_vm(struct kvm_vm *vm)void tdx_init_vm (struct kvm_vm *vm,u64 attributes);void tdx_vm_setup_boot_code_region (struct kvm_vm *vm); +void tdx_vm_setup_boot_parameters_region (struct kvm_vm *vm,u32 nr_runnable_vcpus); +void tdx_vm_load_common_boot_parameters (struct kvm_vm *vm);#endif /* SELFTESTS_TDX_TDX_UTIL_H */ tools/testing/selftests/kvm/lib/x86/processor.c +2
-0@@ -794,6 +794,8 @@ void kvm_arch_vm_post_create(struct kvm_vm *vm, unsigned int nr_vcpus)if (is_tdx_vm (vm)) {tdx_init_vm (vm,0 );tdx_vm_setup_boot_code_region (vm); +tdx_vm_setup_boot_parameters_region (vm, nr_vcpus); +tdx_vm_load_common_boot_parameters (vm); } r =__vm_ioctl (vm,KVM_GET_TSC_KHZ ,NULL );tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +51
-0@@ -6,6 +6,7 @@/* Arbitrarily selected to avoid overlaps with anything else */ #define TD_BOOT_CODE_SLOT 20 +#define TD_BOOT_PARAMETERS_SLOT 21#define X86_RESET_VECTOR 0xfffffff0ul#define X86_RESET_VECTOR_SIZE 16@@ -49,6 +50,56 @@ void tdx_vm_setup_boot_code_region(struct kvm_vm *vm) hva[2 ] =0xcc ; } +void tdx_vm_setup_boot_parameters_region (struct kvm_vm *vm,u32 nr_runnable_vcpus) +{ +size_t boot_params_size = +sizeof (struct td_boot_parameters ) + + nr_runnable_vcpus *sizeof (struct td_per_vcpu_parameters ); +int npages =DIV_ROUND_UP (boot_params_size,PAGE_SIZE ); +const u64 gmem_flags =0 ; +gpa_t gpa; + +vm_mem_add (vm,VM_MEM_SRC_SHMEM ,TD_BOOT_PARAMETERS_GPA , +TD_BOOT_PARAMETERS_SLOT , npages, +KVM_MEM_GUEST_MEMFD ,-1 ,0 , gmem_flags); + gpa =vm_phy_pages_alloc (vm, npages,TD_BOOT_PARAMETERS_GPA ,TD_BOOT_PARAMETERS_SLOT ); +TEST_ASSERT (gpa ==TD_BOOT_PARAMETERS_GPA ,"Failed vm_phy_pages_alloc\n" ); + +virt_map (vm,TD_BOOT_PARAMETERS_GPA ,TD_BOOT_PARAMETERS_GPA , npages); +} + +void tdx_vm_load_common_boot_parameters (struct kvm_vm *vm) +{ +struct td_boot_parameters *params = +addr_gpa2hva (vm,TD_BOOT_PARAMETERS_GPA ); +u32 cr4; + + cr4 =kvm_get_default_cr4 (vm->mmu.pgtable_levels); + +/* TDX spec 11.6.2: CR4 bit MCE is fixed to 1 */ + cr4 |=X86_CR4_MCE ; + +/* TDX spec 11.6.2: CR4 bit VMXE and SMXE are fixed to 0 */ + cr4 &= ~(X86_CR4_VMXE |X86_CR4_SMXE ); + +/* Set parameters! */ + params->cr0 =kvm_get_default_cr0 (); +TEST_ASSERT (vm->mmu.pgd < (1ULL <<32 ), +"PGD must be within 32-bit address space for 32-bit boot code" ); + params->cr3 = vm->mmu.pgd; + params->cr4 = cr4; + params->idtr.base = vm->arch.idt; + params->idtr.limit =kvm_get_default_idt_limit (); + params->gdtr.base = vm->arch.gdt; + params->gdtr.limit =kvm_get_default_gdt_limit (); + +TEST_ASSERT (params->cr0 !=0 ,"cr0 should not be 0" ); +TEST_ASSERT (params->cr3 !=0 ,"cr3 should not be 0" ); +TEST_ASSERT (params->cr4 !=0 ,"cr4 should not be 0" ); +TEST_ASSERT (params->gdtr.base !=0 ,"gdt base address should not be 0" ); +TEST_ASSERT (params->idtr.base !=0 ,"idt base address should not be 0" ); +} +static struct kvm_tdx_capabilities *tdx_read_capabilities (struct kvm_vm *vm) {static struct kvm_tdx_capabilities *tdx_cap;
Re: [PATCH v14 11/22] KVM: selftests: Set up TDX boot parameters region
In reply to Lisa WangQuoted 15 lines · Lisa Wang
From: Sagi Shahar <[email protected]>
Allocate memory for TDX boot parameters and define the utility functions necessary to fill this memory with the boot parameters.
Co-developed-by Ackerley Tng <[email protected]>Signed-off-by Ackerley Tng <[email protected]>Signed-off-by Sagi Shahar <[email protected]>Signed-off-by Lisa Wang <[email protected]>--- .../selftests/kvm/include/x86/tdx/tdx_util.h | 2 + tools/testing/selftests/kvm/lib/x86/processor.c | 2 + tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c | 51 ++++++++++++++++++++++ 3 files changed, 55 insertions(+)
[ ... ]
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c void tdx_vm_setup_boot_code_region(struct kvm_vm *vm) · line 82
Quoted 13 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +13
-0+ +void tdx_vm_load_common_boot_parameters (struct kvm_vm *vm) +{ +struct td_boot_parameters *params = +addr_gpa2hva (vm,TD_BOOT_PARAMETERS_GPA ); +u32 cr4; + + cr4 =kvm_get_default_cr4 (vm->mmu.pgtable_levels); + +/* TDX spec 11.6.2: CR4 bit MCE is fixed to 1 */ + cr4 |=X86_CR4_MCE ; + +/* TDX spec 11.6.2: CR4 bit VMXE and SMXE are fixed to 0 */
This is now out of date (11.9.2 as of today)...
I wonder if it's better to just say something like: "Per the TDX spec, ..."
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c void tdx_vm_setup_boot_code_region(struct kvm_vm *vm) · line 93
Quoted 11 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +11
-0+ cr4 &= ~(X86_CR4_VMXE |X86_CR4_SMXE ); + +/* Set parameters! */ + params->cr0 =kvm_get_default_cr0 (); +TEST_ASSERT (vm->mmu.pgd < (1ULL <<32 ), +"PGD must be within 32-bit address space for 32-bit boot code" ); + params->cr3 = vm->mmu.pgd; + params->cr4 = cr4; + params->idtr.base = vm->arch.idt; + params->idtr.limit =kvm_get_default_idt_limit (); + params->gdtr.base = vm->arch.gdt;
vm->arch.{idt,gdt} must also be 32-bit addressable here, just
like vm->mmu.pgd.tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c void tdx_vm_setup_boot_code_region(struct kvm_vm *vm) · line 100
Quoted 7 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +7
-0+ params->gdtr.limit =kvm_get_default_gdt_limit (); + +TEST_ASSERT (params->cr0 !=0 ,"cr0 should not be 0" ); +TEST_ASSERT (params->cr3 !=0 ,"cr3 should not be 0" ); +TEST_ASSERT (params->cr4 !=0 ,"cr4 should not be 0" ); +TEST_ASSERT (params->gdtr.base !=0 ,"gdt base address should not be 0" ); +TEST_ASSERT (params->idtr.base !=0 ,"idt base address should not be 0" );
Making sure that cr0/cr4 are simply non-zero feels kind of odd to me. And cr3/gdtr/idtr all came from memory allocators with non-zero minimum addresses.
Quoted 10 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +2
-0+} +static struct kvm_tdx_capabilities *tdx_read_capabilities (struct kvm_vm *vm) {static struct kvm_tdx_capabilities *tdx_cap;Signature · 2 lines
-- 2.55.0.229.g6434b31f56-goog
Re: [PATCH v14 11/22] KVM: selftests: Set up TDX boot parameters region
Quoted 28 lines · Peter Fang
Quoted 15 lines · Lisa Wang
From: Sagi Shahar <[email protected]>
Allocate memory for TDX boot parameters and define the utility functions necessary to fill this memory with the boot parameters.
Co-developed-by Ackerley Tng <[email protected]>Signed-off-by Ackerley Tng <[email protected]>Signed-off-by Sagi Shahar <[email protected]>Signed-off-by Lisa Wang <[email protected]>--- .../selftests/kvm/include/x86/tdx/tdx_util.h | 2 + tools/testing/selftests/kvm/lib/x86/processor.c | 2 + tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c | 51 ++++++++++++++++++++++ 3 files changed, 55 insertions(+)[ ... ]
Quoted 9 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +9
-0+ +void tdx_vm_load_common_boot_parameters (struct kvm_vm *vm) +{ +struct td_boot_parameters *params = +addr_gpa2hva (vm,TD_BOOT_PARAMETERS_GPA ); +u32 cr4; + + cr4 =kvm_get_default_cr4 (vm->mmu.pgtable_levels); +
Quoted 9 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c void tdx_vm_setup_boot_code_region(struct kvm_vm *vm) · line 82
Quoted 4 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +4
-0+/* TDX spec 11.6.2: CR4 bit MCE is fixed to 1 */ + cr4 |=X86_CR4_MCE ; + +/* TDX spec 11.6.2: CR4 bit VMXE and SMXE are fixed to 0 */ This is now out of date (11.9.2 as of today)...
I wonder if it's better to just say something like: "Per the TDX spec, ..."
Makes sense, thanks!
Quoted 6 lines
Quoted 6 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +6
-0+ cr4 &= ~(X86_CR4_VMXE |X86_CR4_SMXE ); + +/* Set parameters! */ + params->cr0 =kvm_get_default_cr0 (); +TEST_ASSERT (vm->mmu.pgd < (1ULL <<32 ), +"PGD must be within 32-bit address space for 32-bit boot code" );
Quoted 9 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c void tdx_vm_setup_boot_code_region(struct kvm_vm *vm) · line 93
Quoted 5 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +5
-0+ params->cr3 = vm->mmu.pgd; + params->cr4 = cr4; + params->idtr.base = vm->arch.idt; + params->idtr.limit =kvm_get_default_idt_limit (); + params->gdtr.base = vm->arch.gdt;vm->arch.{idt,gdt} must also be 32-bit addressable here, just like vm->mmu.pgd.
Something like this?
TEST_ASSERT(params->gdtr.base < (1ULL << 32),
"gdt must be within 32-bit address space for 32-bit boot code");Quoted 12 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c void tdx_vm_setup_boot_code_region(struct kvm_vm *vm) · line 100
Quoted 7 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +7
-0+ params->gdtr.limit =kvm_get_default_gdt_limit (); + +TEST_ASSERT (params->cr0 !=0 ,"cr0 should not be 0" ); +TEST_ASSERT (params->cr3 !=0 ,"cr3 should not be 0" ); +TEST_ASSERT (params->cr4 !=0 ,"cr4 should not be 0" ); +TEST_ASSERT (params->gdtr.base !=0 ,"gdt base address should not be 0" ); +TEST_ASSERT (params->idtr.base !=0 ,"idt base address should not be 0" );Making sure that cr0/cr4 are simply non-zero feels kind of odd to me. And cr3/gdtr/idtr all came from memory allocators with non-zero minimum addresses.
I guess I intended these to be sanity checks that the caller didn't leave these unset (defaulting to 0), for an early warning to the selftest writer if something went wrong. I'd prefer to keep something that would retain this early warning for the writer.
What would you replace this with, to provide the early warning, or would you rather just remove it?
Quoted 10 lines
Quoted 10 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +2
-1+} +static struct kvm_tdx_capabilities *tdx_read_capabilities (struct kvm_vm *vm) {static struct kvm_tdx_capabilities *tdx_cap; --2.55.0.229.g6434b31f56-goog
Re: [PATCH v14 11/22] KVM: selftests: Set up TDX boot parameters region
Quoted 17 lines · Ackerley Tng
Quoted 16 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c void tdx_vm_setup_boot_code_region(struct kvm_vm *vm) · line 93
Quoted 12 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +11
-0+ cr4 &= ~(X86_CR4_VMXE |X86_CR4_SMXE ); + +/* Set parameters! */ + params->cr0 =kvm_get_default_cr0 (); +TEST_ASSERT (vm->mmu.pgd < (1ULL <<32 ), +"PGD must be within 32-bit address space for 32-bit boot code" ); + params->cr3 = vm->mmu.pgd; + params->cr4 = cr4; + params->idtr.base = vm->arch.idt; + params->idtr.limit =kvm_get_default_idt_limit (); + params->gdtr.base = vm->arch.gdt;vm->arch.{idt,gdt} must also be 32-bit addressable here, just like vm->mmu.pgd.
Quoted 4 lines
Something like this?
TEST_ASSERT(params->gdtr.base < (1ULL << 32), "gdt must be within 32-bit address space for 32-bit boot code");
Yep, looks good to me.
Quoted 15 lines
Quoted 14 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c void tdx_vm_setup_boot_code_region(struct kvm_vm *vm) · line 100
Quoted 9 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +7
-0+ params->gdtr.limit =kvm_get_default_gdt_limit (); + +TEST_ASSERT (params->cr0 !=0 ,"cr0 should not be 0" ); +TEST_ASSERT (params->cr3 !=0 ,"cr3 should not be 0" ); +TEST_ASSERT (params->cr4 !=0 ,"cr4 should not be 0" ); +TEST_ASSERT (params->gdtr.base !=0 ,"gdt base address should not be 0" ); +TEST_ASSERT (params->idtr.base !=0 ,"idt base address should not be 0" );Making sure that cr0/cr4 are simply non-zero feels kind of odd to me. And cr3/gdtr/idtr all came from memory allocators with non-zero minimum addresses.
Quoted 7 lines
I guess I intended these to be sanity checks that the caller didn't leave these unset (defaulting to 0), for an early warning to the selftest writer if something went wrong. I'd prefer to keep something that would retain this early warning for the writer.
What would you replace this with, to provide the early warning, or would you rather just remove it?
Hmm... In earlier versions this was:
params->cr0 = sregs->cr0;
params->cr4 = sregs->cr4;
TEST_ASSERT(params->cr0 != 0, "cr0 should not be 0");
TEST_ASSERT(params->cr4 != 0, "cr4 should not be 0");Which made more sense because perhaps @sregs could be sanity checked. But now the assignments are all in the same function:
cr4 = kvm_get_default_cr4(vm->mmu.pgtable_levels);
cr4 |= X86_CR4_MCE;
cr4 &= ~(X86_CR4_VMXE | X86_CR4_SMXE);
params->cr0 = kvm_get_default_cr0();
params->cr4 = cr4;
TEST_ASSERT(params->cr0 != 0, "cr0 should not be 0");
TEST_ASSERT(params->cr4 != 0, "cr4 should not be 0");This makes much less sense to me. It looks like sanity checking kvm_get_default_{cr0,cr4}().
So I'd remove the two asserts for cr0/cr4. Keeping the ones for cr3/gdtr/idtr is probably fine.
Quoted 1 line
PATCH v14 12/22 KVM: selftests: Require guest_memfd for TDX VMs
In reply to Lisa WangForce guest_memfd for TDX VMs.
KVM supports guest_memfd for TDX private memory. Without in-place conversion supports, all pages default attributes are private. Thus, set page to "shared" for shared pages.
--- tools/testing/selftests/kvm/lib/kvm_util.c | 2 +- tools/testing/selftests/kvm/lib/ucall_common.c | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-)
tools/testing/selftests/kvm/lib/kvm_util.c +1 -1
@@ -472,7 +472,7 @@ void kvm_set_files_rlimit(u32 nr_vcpus)static bool is_guest_memfd_required (struct vm_shape shape) {#ifdef __x86_64__ -return shape.type ==KVM_X86_SNP_VM ; +return (shape.type ==KVM_X86_SNP_VM || shape.type ==KVM_X86_TDX_VM );#else return false;#endif
tools/testing/selftests/kvm/lib/ucall_common.c +8 -0
@@ -27,6 +27,7 @@ static struct ucall_header *ucall_pool;void ucall_init (struct kvm_vm *vm,gpa_t mmio_gpa) { +struct userspace_mem_region *region;struct ucall_header *hdr;struct ucall *uc;gva_t gva;
@@ -37,6 +38,13 @@ void ucall_init(struct kvm_vm *vm, gpa_t mmio_gpa) hdr = (struct ucall_header *)addr_gva2hva (vm, gva);memset (hdr,0 ,sizeof (*hdr)); + region =vm_get_mem_region (vm,MEM_REGION_DATA ); +if (region->region.flags &KVM_MEM_GUEST_MEMFD ) { +size_t aligned_sz =align_up (sizeof (*hdr), vm->page_size); + +vm_mem_set_shared (vm,addr_gva2gpa (vm, gva), aligned_sz); + } +for (i =0 ; i <KVM_MAX_VCPUS ; ++i) { uc = &hdr->ucalls[i]; uc->hva = uc;
Signature · 1 line
2.55.0.229.g6434b31f56-goog
Re: [PATCH v14 12/22] KVM: selftests: Require guest_memfd for TDX VMs
Quoted 7 lines · Lisa Wang
Force
guest_memfdfor TDX VMs.KVM supports
guest_memfdfor TDX private memory. Without in-place conversion supports, all pages default attributes are private. Thus, set page to "shared" for shared pages.Signed-off-by Lisa Wang <[email protected]>
One nit below.
Quoted 10 lines
--- tools/testing/selftests/kvm/lib/kvm_util.c | 2 +- tools/testing/selftests/kvm/lib/ucall_common.c | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-)tools/testing/selftests/kvm/lib/kvm_util.c +0
-0
tools/testing/selftests/kvm/lib/kvm_util.c void kvm_set_files_rlimit(u32 nr_vcpus) · line 475
Quoted 5 lines
tools/testing/selftests/kvm/lib/kvm_util.c +1
-1@@ -472,7 +472,7 @@ void kvm_set_files_rlimit(u32 nr_vcpus)static bool is_guest_memfd_required (struct vm_shape shape) {#ifdef __x86_64__ -return shape.type ==KVM_X86_SNP_VM ; +return (shape.type ==KVM_X86_SNP_VM || shape.type ==KVM_X86_TDX_VM );
Nit:
The parentheses can be dropped.
Quoted 30 lines
tools/testing/selftests/kvm/lib/kvm_util.c +0
-0#else return false;#endif tools/testing/selftests/kvm/lib/ucall_common.c +8
-0@@ -27,6 +27,7 @@ static struct ucall_header *ucall_pool;void ucall_init (struct kvm_vm *vm,gpa_t mmio_gpa) { +struct userspace_mem_region *region;struct ucall_header *hdr;struct ucall *uc;gva_t gva;@@ -37,6 +38,13 @@ void ucall_init(struct kvm_vm *vm, gpa_t mmio_gpa) hdr = (struct ucall_header *)addr_gva2hva (vm, gva);memset (hdr,0 ,sizeof (*hdr)); + region =vm_get_mem_region (vm,MEM_REGION_DATA ); +if (region->region.flags &KVM_MEM_GUEST_MEMFD ) { +size_t aligned_sz =align_up (sizeof (*hdr), vm->page_size); + +vm_mem_set_shared (vm,addr_gva2gpa (vm, gva), aligned_sz); + } +for (i =0 ; i <KVM_MAX_VCPUS ; ++i) { uc = &hdr->ucalls[i]; uc->hva = uc;
Re: [PATCH v14 12/22] KVM: selftests: Require guest_memfd for TDX VMs
In reply to Lisa WangQuoted 17 lines · Lisa Wang
Force
guest_memfdfor TDX VMs.KVM supports
guest_memfdfor TDX private memory. Without in-place conversion supports, all pages default attributes are private. Thus, set page to "shared" for shared pages.Signed-off-by Lisa Wang <[email protected]>--- tools/testing/selftests/kvm/lib/kvm_util.c | 2 +- tools/testing/selftests/kvm/lib/ucall_common.c | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-)tools/testing/selftests/kvm/lib/kvm_util.c +0
-0
tools/testing/selftests/kvm/lib/kvm_util.c void kvm_set_files_rlimit(u32 nr_vcpus) · line 475
Quoted 5 lines
tools/testing/selftests/kvm/lib/kvm_util.c +1
-1@@ -472,7 +472,7 @@ void kvm_set_files_rlimit(u32 nr_vcpus)static bool is_guest_memfd_required (struct vm_shape shape) {#ifdef __x86_64__ -return shape.type ==KVM_X86_SNP_VM ; +return (shape.type ==KVM_X86_SNP_VM || shape.type ==KVM_X86_TDX_VM );
Instead of updating is_guest_memfd_required(), I'm thinking how about just replacing is_guest_memfd_required() with vm_arch_has_protected_memory()?
Quoted 16 lines
tools/testing/selftests/kvm/lib/kvm_util.c +0
-0#else return false;#endif tools/testing/selftests/kvm/lib/ucall_common.c +1
-0@@ -27,6 +27,7 @@ static struct ucall_header *ucall_pool;void ucall_init (struct kvm_vm *vm,gpa_t mmio_gpa) { +struct userspace_mem_region *region;struct ucall_header *hdr;struct ucall *uc;gva_t gva;
tools/testing/selftests/kvm/lib/ucall_common.c void ucall_init(struct kvm_vm *vm, gpa_t mmio_gpa) · line 46
Quoted 9 lines
tools/testing/selftests/kvm/lib/ucall_common.c +6
-0@@ -37,6 +38,13 @@ void ucall_init(struct kvm_vm *vm, gpa_t mmio_gpa) hdr = (struct ucall_header *)addr_gva2hva (vm, gva);memset (hdr,0 ,sizeof (*hdr)); + region =vm_get_mem_region (vm,MEM_REGION_DATA ); +if (region->region.flags &KVM_MEM_GUEST_MEMFD ) { +size_t aligned_sz =align_up (sizeof (*hdr), vm->page_size); + +vm_mem_set_shared (vm,addr_gva2gpa (vm, gva), aligned_sz); + }
this diff about the ucall needs to be a separate patch. What's the reason to add it to this patch in this version?
Quoted 4 lines
tools/testing/selftests/kvm/lib/ucall_common.c +0
-0for (i =0 ; i <KVM_MAX_VCPUS ; ++i) { uc = &hdr->ucalls[i]; uc->hva = uc;
Re: [PATCH v14 12/22] KVM: selftests: Require guest_memfd for TDX VMs
Quoted 8 lines · Xiaoyao Li
tools/testing/selftests/kvm/lib/kvm_util.c void kvm_set_files_rlimit(u32 nr_vcpus) · line 475
Quoted 5 lines
static bool is_guest_memfd_required(struct vm_shape shape) { #ifdef __x86_64__tools/testing/selftests/kvm/lib/kvm_util.c +1
-1-return shape.type ==KVM_X86_SNP_VM ; +return (shape.type ==KVM_X86_SNP_VM || shape.type ==KVM_X86_TDX_VM );Instead of updating
is_guest_memfd_required(), I'm thinking how about just replacingis_guest_memfd_required()withvm_arch_has_protected_memory()?
vm_arch_has_protected_memory() returns true for SEV, but an SEV VM is expected not to use guest_memfd. If we replace is_guest_memfd_required() with vm_arch_has_protected_memory(), we need an extra test to ensure SEV VMs work well without guest_memfd.
Thus, I prefer keeping is_guest_memfd_required().
Quoted 11 lines
Quoted 11 lines
tools/testing/selftests/kvm/lib/ucall_common.c +2
-0@@ -27,6 +27,7 @@ static struct ucall_header *ucall_pool;void ucall_init (struct kvm_vm *vm,gpa_t mmio_gpa) { +struct userspace_mem_region *region;struct ucall_header *hdr;struct ucall *uc;gva_t gva;@@ -37,6 +38,13 @@ void ucall_init(struct kvm_vm *vm, gpa_t mmio_gpa) hdr = (struct ucall_header *)addr_gva2hva (vm, gva);memset (hdr,0 ,sizeof (*hdr)); + region =vm_get_mem_region (vm,MEM_REGION_DATA );
Quoted 8 lines
tools/testing/selftests/kvm/lib/ucall_common.c void ucall_init(struct kvm_vm *vm, gpa_t mmio_gpa) · line 46
Quoted 5 lines
tools/testing/selftests/kvm/lib/ucall_common.c +5
-0+if (region->region.flags &KVM_MEM_GUEST_MEMFD ) { +size_t aligned_sz =align_up (sizeof (*hdr), vm->page_size); + +vm_mem_set_shared (vm,addr_gva2gpa (vm, gva), aligned_sz); + }this diff about the ucall needs to be a separate patch. What's the reason to add it to this patch in this version?
Thanks. Will separate in the next version.
Quoted 5 lines
Quoted 4 lines
tools/testing/selftests/kvm/lib/ucall_common.c +0
-0for (i =0 ; i <KVM_MAX_VCPUS ; ++i) { uc = &hdr->ucalls[i]; uc->hva = uc;
PATCH v14 13/22 KVM: selftests: Support guest_memfd in-place conversion
In reply to Lisa WangSet the memory region as shared if guest_memfd in-place conversion is support, so that the KVM selftest framework functions can populate mmap()-ed guest_memfd memory the same way memory from other memory providers are populated.
For CoCo VMs, pages that need to be private are explicitly set to private before executing the VM.
--- tools/testing/selftests/kvm/lib/kvm_util.c | 20 ++++++++++++++------ tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c | 8 ++++++-- 2 files changed, 20 insertions(+), 8 deletions(-)
tools/testing/selftests/kvm/lib/kvm_util.c +14 -6
@@ -484,8 +484,10 @@ struct kvm_vm *__vm_create(struct vm_shape shape, u32 nr_runnable_vcpus,u64 nr_pages =vm_nr_pages_required (shape.mode, nr_runnable_vcpus, nr_extra_pages);struct userspace_mem_region *slot0; +u64 gmem_flags =0 ;struct kvm_vm *vm; -int i, flags; +int flags =0 ; +int i;kvm_set_files_rlimit (nr_runnable_vcpus);
@@ -495,14 +497,20 @@ struct kvm_vm *__vm_create(struct vm_shape shape, u32 nr_runnable_vcpus, vm =____vm_create (shape);/* -* Force GUEST_MEMFD for the primary memory region if necessary, e.g. -* for CoCo VMs that require GUEST_MEMFD backed private memory. +* Force GUEST_MEMFD for the primary memory region if necessary, and +* initialize it as shared if in-place conversion is support, so the +* selftest framework can populate it exactly like other memory +* providers. */ - flags =0 ; -if (is_guest_memfd_required (shape)) +if (is_guest_memfd_required (shape)) { flags |=KVM_MEM_GUEST_MEMFD ; +if (kvm_has_gmem_attributes) + gmem_flags |=GUEST_MEMFD_FLAG_INIT_SHARED | +GUEST_MEMFD_FLAG_MMAP ; + } -vm_userspace_mem_region_add (vm,VM_MEM_SRC_ANONYMOUS ,0 ,0 , nr_pages, flags); +vm_mem_add (vm,VM_MEM_SRC_ANONYMOUS ,0 ,0 , nr_pages, flags, +-1 ,0 , gmem_flags);for (i =0 ; i <NR_MEM_REGIONS ; i++) vm->memslots[i] =0 ;
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +6 -2
@@ -17,10 +17,12 @@ void tdx_vm_setup_boot_code_region(struct kvm_vm *vm)gpa_t boot_code_gpa =X86_RESET_VECTOR -TD_BOOT_CODE_SIZE ;gpa_t alloc_gpa =round_down (boot_code_gpa,PAGE_SIZE );size_t nr_pages =DIV_ROUND_UP (total_code_size,PAGE_SIZE ); -const u64 gmem_flags =0 ; +u64 gmem_flags =0 ;gpa_t gpa;u8 *hva; +if (kvm_has_gmem_attributes) + gmem_flags |=GUEST_MEMFD_FLAG_INIT_SHARED |GUEST_MEMFD_FLAG_MMAP ;vm_mem_add (vm,VM_MEM_SRC_SHMEM , alloc_gpa,TD_BOOT_CODE_SLOT , nr_pages,KVM_MEM_GUEST_MEMFD ,-1 ,0 , gmem_flags);
@@ -56,9 +58,11 @@ void tdx_vm_setup_boot_parameters_region(struct kvm_vm *vm, u32 nr_runnable_vcpusizeof (struct td_boot_parameters ) + nr_runnable_vcpus *sizeof (struct td_per_vcpu_parameters );int npages =DIV_ROUND_UP (boot_params_size,PAGE_SIZE ); -const u64 gmem_flags =0 ; +u64 gmem_flags =0 ;gpa_t gpa; +if (kvm_has_gmem_attributes) + gmem_flags |=GUEST_MEMFD_FLAG_INIT_SHARED |GUEST_MEMFD_FLAG_MMAP ;vm_mem_add (vm,VM_MEM_SRC_SHMEM ,TD_BOOT_PARAMETERS_GPA ,TD_BOOT_PARAMETERS_SLOT , npages,KVM_MEM_GUEST_MEMFD ,-1 ,0 , gmem_flags);
Signature · 1 line
2.55.0.229.g6434b31f56-goog
Re: [PATCH v14 13/22] KVM: selftests: Support guest_memfd in-place conversion
Quoted 31 lines · Lisa Wang
tools/testing/selftests/kvm/lib/kvm_util.c +8
-5@@ -484,8 +484,10 @@ struct kvm_vm *__vm_create(struct vm_shape shape, u32 nr_runnable_vcpus,u64 nr_pages =vm_nr_pages_required (shape.mode, nr_runnable_vcpus, nr_extra_pages);struct userspace_mem_region *slot0; +u64 gmem_flags =0 ;struct kvm_vm *vm; -int i, flags; +int flags =0 ; +int i;kvm_set_files_rlimit (nr_runnable_vcpus);@@ -495,14 +497,20 @@ struct kvm_vm *__vm_create(struct vm_shape shape, u32 nr_runnable_vcpus, vm =____vm_create (shape);/* -* Force GUEST_MEMFD for the primary memory region if necessary, e.g. -* for CoCo VMs that require GUEST_MEMFD backed private memory. +* Force GUEST_MEMFD for the primary memory region if necessary, and +* initialize it as shared if in-place conversion is support, so the +* selftest framework can populate it exactly like other memory +* providers. */ - flags =0 ; -if (is_guest_memfd_required (shape)) +if (is_guest_memfd_required (shape)) { flags |=KVM_MEM_GUEST_MEMFD ;
tools/testing/selftests/kvm/lib/kvm_util.c struct kvm_vm *__vm_create(struct vm_shape shape, u32 nr_runnable_vcpus, · line 515
Quoted 10 lines
tools/testing/selftests/kvm/lib/kvm_util.c +6
-1+if (kvm_has_gmem_attributes) + gmem_flags |=GUEST_MEMFD_FLAG_INIT_SHARED | +GUEST_MEMFD_FLAG_MMAP ; + } -vm_userspace_mem_region_add (vm,VM_MEM_SRC_ANONYMOUS ,0 ,0 , nr_pages, flags); +vm_mem_add (vm,VM_MEM_SRC_ANONYMOUS ,0 ,0 , nr_pages, flags, +-1 ,0 , gmem_flags);for (i =0 ; i <NR_MEM_REGIONS ; i++) vm->memslots[i] =0 ;
IMO, this diff belongs to the gmem in-place series.
Sean Christopherson
maintainerRe: [PATCH v14 13/22] KVM: selftests: Support guest_memfd in-place conversion
Quoted 13 lines · Xiaoyao Li
tools/testing/selftests/kvm/lib/kvm_util.c struct kvm_vm *__vm_create(struct vm_shape shape, u32 nr_runnable_vcpus, · line 489
Quoted 12 lines · Lisa Wang
tools/testing/selftests/kvm/lib/kvm_util.c +2
-1@@ -484,8 +484,10 @@ struct kvm_vm *__vm_create(struct vm_shape shape, u32 nr_runnable_vcpus,u64 nr_pages =vm_nr_pages_required (shape.mode, nr_runnable_vcpus, nr_extra_pages);struct userspace_mem_region *slot0; +u64 gmem_flags =0 ;struct kvm_vm *vm; -int i, flags; +int flags =0 ;
Don't make unnecessary changes. It's not just a "nice to have" thing, e.g. in this case this results in extra conflicts with other changes.
Quoted 4 lines
Quoted 4 lines
tools/testing/selftests/kvm/lib/kvm_util.c +1
-0+int i;kvm_set_files_rlimit (nr_runnable_vcpus);@@ -495,14 +497,20 @@ struct kvm_vm *__vm_create(struct vm_shape shape, u32 nr_runnable_vcpus, vm =____vm_create (shape);
Quoted 8 lines
tools/testing/selftests/kvm/lib/kvm_util.c struct kvm_vm *__vm_create(struct vm_shape shape, u32 nr_runnable_vcpus, · line 503
Quoted 8 lines
tools/testing/selftests/kvm/lib/kvm_util.c +4
-2/* -* Force GUEST_MEMFD for the primary memory region if necessary, e.g. -* for CoCo VMs that require GUEST_MEMFD backed private memory. +* Force GUEST_MEMFD for the primary memory region if necessary, and +* initialize it as shared if in-place conversion is support, so the +* selftest framework can populate it exactly like other memory +* providers. */
Leave the existing comment as is. The new comment should be anchored to the code below.
Quoted 7 lines
Quoted 7 lines
tools/testing/selftests/kvm/lib/kvm_util.c +4
-2- flags =0 ; -if (is_guest_memfd_required (shape)) +if (is_guest_memfd_required (shape)) { flags |=KVM_MEM_GUEST_MEMFD ; +if (kvm_has_gmem_attributes) + gmem_flags |=GUEST_MEMFD_FLAG_INIT_SHARED | +GUEST_MEMFD_FLAG_MMAP ;
Quoted 8 lines
tools/testing/selftests/kvm/lib/kvm_util.c struct kvm_vm *__vm_create(struct vm_shape shape, u32 nr_runnable_vcpus, · line 513
Quoted 6 lines
tools/testing/selftests/kvm/lib/kvm_util.c +3
-1+ } -vm_userspace_mem_region_add (vm,VM_MEM_SRC_ANONYMOUS ,0 ,0 , nr_pages, flags); +vm_mem_add (vm,VM_MEM_SRC_ANONYMOUS ,0 ,0 , nr_pages, flags, +-1 ,0 , gmem_flags);for (i =0 ; i <NR_MEM_REGIONS ; i++) vm->memslots[i] =0 ;IMO, this diff belongs to the gmem in-place series.
Yep, you're 100% right. I pushed this to "coco" for now...
testing/selftests/kvm/lib/kvm_util.c +13 -2
@@ -494,6 +494,7 @@ struct kvm_vm *__vm_create(struct vm_shape shape, u32 nr_runnable_vcpus,u64 nr_pages =vm_nr_pages_required (shape.mode, nr_runnable_vcpus, nr_extra_pages);struct userspace_mem_region *slot0; +u64 gmem_flags =0 ;struct kvm_vm *vm;int i, flags;
@@ -509,10 +510,20 @@ struct kvm_vm *__vm_create(struct vm_shape shape, u32 nr_runnable_vcpus,* for CoCo VMs that require GUEST_MEMFD backed private memory. */ flags =0 ; -if (is_guest_memfd_required (shape)) +if (is_guest_memfd_required (shape)) { flags |=KVM_MEM_GUEST_MEMFD ; -vm_userspace_mem_region_add (vm,VM_MEM_SRC_ANONYMOUS ,0 ,0 , nr_pages, flags); +/* +* Allow mmap() and initialize the gmem instance as SHARED if +* in-place conversions are supported, so that the memory can +* be populated/initialized like any other memory region. +*/ +if (kvm_has_gmem_attributes) + gmem_flags |=GUEST_MEMFD_FLAG_INIT_SHARED | +GUEST_MEMFD_FLAG_MMAP ; + } + +vm_mem_add (vm,VM_MEM_SRC_ANONYMOUS ,0 ,0 , nr_pages, flags,-1 ,0 , gmem_flags);for (i =0 ; i <NR_MEM_REGIONS ; i++) vm->memslots[i] =0 ;
but the entire in-place conversion series may need to be dropped because there's a potential deadlock, and I don't see an obviously non-awful way to fix it.
[ 2260.466972] ======================================================
[ 2260.473150] WARNING: possible circular locking dependency detected
[ 2260.479329] 7.3.0-smp--ce115a0096358-seanjc-soink #75 Tainted: G U O
[ 2260.486981] ------------------------------------------------------
[ 2260.493161] sev_snp_vmsa_pu/24505 is trying to acquire lock:
[ 2260.498821] ffffffffc01f7900 (sev_cmd_mutex){+.+.}-{4:4}, at: sev_issue_cmd_external_user+0x39/0x60 [ccp]
[ 2260.508387]
but task is already holding lock:
[ 2260.514213] ffff93c9dd1cf070 (mapping.invalidate_lock#4){++++}-{4:4}, at: kvm_gmem_populate+0x18c/0x300 [kvm]
[ 2260.524275]
which lock already depends on the new lock.[ 2260.532446]
the existing dependency chain (in reverse order) is:
[ 2260.539917]
-> #2 (mapping.invalidate_lock#4){++++}-{4:4}:
[ 2260.546878] down_read+0x3d/0x160
[ 2260.550716] kvm_gmem_fault_user_mapping+0x43/0x180 [kvm]
[ 2260.556663] __do_fault+0x3f/0x110
[ 2260.560588] do_pte_missing+0x515/0x1000
[ 2260.565035] handle_mm_fault+0x7f2/0x1290
[ 2260.569567] do_user_addr_fault+0x47d/0x6c0
[ 2260.574271] exc_page_fault+0x67/0xa0
[ 2260.578458] asm_exc_page_fault+0x26/0x30
[ 2260.582991]
-> #1 (&mm->mmap_lock){++++}-{4:4}:
[ 2260.588997] __might_fault+0x59/0x80
[ 2260.593096] _copy_to_user+0x23/0x60
[ 2260.597197] sev_ioctl_do_platform_status+0x50/0x80 [ccp]
[ 2260.603116] sev_ioctl+0x220/0x2b0 [ccp]
[ 2260.607560] __se_sys_ioctl+0x6d/0xb0
[ 2260.611749] do_syscall_64+0xfe/0x460
[ 2260.615933] entry_SYSCALL_64_after_hwframe+0x4b/0x53
[ 2260.621506]
-> #0 (sev_cmd_mutex){+.+.}-{4:4}:
[ 2260.627426] __lock_acquire+0x160d/0x2940
[ 2260.631957] lock_acquire+0xf1/0x300
[ 2260.636050] __mutex_lock+0x8f/0xdf0
[ 2260.640147] sev_issue_cmd_external_user+0x39/0x60 [ccp]
[ 2260.645982] sev_gmem_post_populate+0x189/0x230 [kvm_amd]
[ 2260.651901] kvm_gmem_populate+0x1ed/0x300 [kvm]
[ 2260.657066] snp_launch_update+0x1a7/0x250 [kvm_amd]
[ 2260.662551] sev_mem_enc_ioctl+0x371/0x3d0 [kvm_amd]
[ 2260.668036] kvm_vm_ioctl+0x52c/0x580 [kvm]
[ 2260.672761] __se_sys_ioctl+0x6d/0xb0
[ 2260.676946] do_syscall_64+0xfe/0x460
[ 2260.681133] entry_SYSCALL_64_after_hwframe+0x4b/0x53
[ 2260.686706]
other info that might help us debug this:[ 2260.694695] Chain exists of:
sev_cmd_mutex --> &mm->mmap_lock --> mapping.invalidate_lock#4[ 2260.706077] Possible unsafe locking scenario:
[ 2260.711993] CPU0 CPU1
[ 2260.716520] ---- ----
[ 2260.721052] lock(mapping.invalidate_lock#4);
[ 2260.725498] lock(&mm->mmap_lock);
[ 2260.731503] lock(mapping.invalidate_lock#4);
[ 2260.738463] lock(sev_cmd_mutex);
[ 2260.741868]
*** DEADLOCK ***[ 2260.747778] locks held by sev_snp_vmsa_pu/24505: 3, last CPU#35:
[ 2260.753785] #0: ffff93c9d54bd3a8 (&kvm->lock){+.+.}-{4:4}, at: sev_mem_enc_ioctl+0x73/0x3d0 [kvm_amd]
[ 2260.763083] #1: ffff93c9d54bc0a0 (&kvm->slots_lock){+.+.}-{4:4}, at: snp_launch_update+0x145/0x250 [kvm_amd]
[ 2260.772991] #2: ffff93c9dd1cf070 (mapping.invalidate_lock#4){++++}-{4:4}, at: kvm_gmem_populate+0x18c/0x300 [kvm]
[ 2260.783356]
stack backtrace:
[ 2260.787718] CPU: 35 UID: 0 PID: 24505 Comm: sev_snp_vmsa_pu Tainted: G U O 7.3.0-smp--ce115a0096358-seanjc-soink #75 PREEMPT
[ 2260.800310] Tainted: [U]=USER, [O]=OOT_MODULE
[ 2260.804668] Hardware name: Google, Inc. Arcadia_IT_80/Arcadia_IT_80, BIOS 34.86.0-102 01/25/2026
[ 2260.818127] Call Trace:
[ 2260.820579] <TASK>
[ 2260.822687] dump_stack_lvl+0x54/0x70
[ 2260.826351] print_circular_bug+0x2e4/0x300
[ 2260.830537] check_noncircular+0xf5/0x120
[ 2260.834553] __lock_acquire+0x160d/0x2940
[ 2260.838565] ? __lock_acquire+0x65e/0x2940
[ 2260.842664] ? rcu_is_watching+0x11/0x40
[ 2260.846587] ? trace_sched_exit_tp+0x25/0xb0
[ 2260.850861] lock_acquire+0xf1/0x300
[ 2260.854440] ? sev_issue_cmd_external_user+0x39/0x60 [ccp]
[ 2260.859928] ? lock_is_held_type+0xc7/0x120
[ 2260.864112] ? sev_issue_cmd_external_user+0x39/0x60 [ccp]
[ 2260.869599] __mutex_lock+0x8f/0xdf0
[ 2260.873178] ? sev_issue_cmd_external_user+0x39/0x60 [ccp]
[ 2260.878663] ? pfn_valid+0x89/0x220
[ 2260.882155] ? pfn_valid+0x1fb/0x220
[ 2260.885736] ? lookup_address+0x49/0x70
[ 2260.889577] sev_issue_cmd_external_user+0x39/0x60 [ccp]
[ 2260.894890] sev_gmem_post_populate+0x189/0x230 [kvm_amd]
[ 2260.900289] kvm_gmem_populate+0x1ed/0x300 [kvm]
[ 2260.904938] ? snp_bind_asid+0xd0/0xd0 [kvm_amd]
[ 2260.909561] snp_launch_update+0x1a7/0x250 [kvm_amd]
[ 2260.914528] sev_mem_enc_ioctl+0x371/0x3d0 [kvm_amd]
[ 2260.919493] kvm_vm_ioctl+0x52c/0x580 [kvm]
[ 2260.923706] ? kvm_gmem_ioctl+0x192/0x1e0 [kvm]
[ 2260.928266] __se_sys_ioctl+0x6d/0xb0
[ 2260.931930] do_syscall_64+0xfe/0x460
[ 2260.935595] ? entry_SYSCALL_64_after_hwframe+0x4b/0x53
[ 2260.940822] ? trace_irq_disable+0x25/0xc0
[ 2260.944921] entry_SYSCALL_64_after_hwframe+0x4b/0x53
[ 2260.949972] RIP: 0033:0x474bdb
[ 2260.953031] Code: 00 48 89 44 24 18 31 c0 48 8d 44 24 60 c7 04 24 10 00 00 00 48 89 44 24 08 48 8d 44 24 20 48 89 44 24 10 b8 10 00 00 00 0f 05 <89> c2 3d 00 f0 ff ff 77 1c 48 8b 44 24 18 64 48 2b 04 25 28 00 00
[ 2260.971770] RSP: 002b:00007ffd188ac3f0 EFLAGS: 00000246 ORIG_RAX: 0000000000000010
[ 2260.979337] RAX: ffffffffffffffda RBX: 0000000028691b30 RCX: 0000000000474bdb
[ 2260.986468] RDX: 00007ffd188ac4a0 RSI: 00000000c008aeba RDI: 0000000000000007
[ 2260.993600] RBP: 0000000000135000 R08: 00000000004d50b8 R09: 00000000004e1b78
[ 2261.000734] R10: 0000000000000002 R11: 0000000000000246 R12: 00007ffd188ac4c0
[ 2261.007867] R13: 0000000000137000 R14: 0000000028693db0 R15: 0000000000137000
[ 2261.015001] </TASK>Sean Christopherson
maintainerRe: [PATCH v14 13/22] KVM: selftests: Support guest_memfd in-place conversion
+Ashish, Tom and Mike, dropped a bunch of others that probably don't care about the CCP/PSP driver.
Quoted 55 lines · Sean Christopherson
Quoted 16 lines · Xiaoyao Li
Review
Quoted 13 lines · Lisa Wang
(quoted diff) +7
-3- flags = 0; - if (is_guest_memfd_required(shape)) + if (is_guest_memfd_required(shape)) { flags |= KVM_MEM_GUEST_MEMFD; + if (kvm_has_gmem_attributes) + gmem_flags |= GUEST_MEMFD_FLAG_INIT_SHARED | + GUEST_MEMFD_FLAG_MMAP; + } - vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, 0, 0, nr_pages, flags); + vm_mem_add(vm, VM_MEM_SRC_ANONYMOUS, 0, 0, nr_pages, flags, + -1, 0, gmem_flags); for (i = 0; i < NR_MEM_REGIONS; i++) vm->memslots[i] = 0;IMO, this diff belongs to the gmem in-place series.
Yep, you're 100% right. I pushed this to "coco" for now...
testing/selftests/kvm/lib/kvm_util.c +13
-2@@ -494,6 +494,7 @@ struct kvm_vm *__vm_create(struct vm_shape shape, u32 nr_runnable_vcpus,u64 nr_pages =vm_nr_pages_required (shape.mode, nr_runnable_vcpus, nr_extra_pages);struct userspace_mem_region *slot0; +u64 gmem_flags =0 ;struct kvm_vm *vm;int i, flags;@@ -509,10 +510,20 @@ struct kvm_vm *__vm_create(struct vm_shape shape, u32 nr_runnable_vcpus,* for CoCo VMs that require GUEST_MEMFD backed private memory. */ flags =0 ; -if (is_guest_memfd_required (shape)) +if (is_guest_memfd_required (shape)) { flags |=KVM_MEM_GUEST_MEMFD ; -vm_userspace_mem_region_add (vm,VM_MEM_SRC_ANONYMOUS ,0 ,0 , nr_pages, flags); +/* +* Allow mmap() and initialize the gmem instance as SHARED if +* in-place conversions are supported, so that the memory can +* be populated/initialized like any other memory region. +*/ +if (kvm_has_gmem_attributes) + gmem_flags |=GUEST_MEMFD_FLAG_INIT_SHARED | +GUEST_MEMFD_FLAG_MMAP ; + } + +vm_mem_add (vm,VM_MEM_SRC_ANONYMOUS ,0 ,0 , nr_pages, flags,-1 ,0 , gmem_flags);for (i =0 ; i <NR_MEM_REGIONS ; i++) vm->memslots[i] =0 ;
Quoted 2 lines
but the entire in-place conversion series may need to be dropped because there's a potential deadlock, and I don't see an obviously non-awful way to fix it.
The underlying problem is that drivers/crypto/ccp/sev-dev.c does memory allocations and copy_{to,from}_user() while holding sev_cmd_mutex in a variety of locations.
I genuinely think the right fix is to not do that, i.e. to perform invasive surgery on the driver so that it holds sev_cmd_mutex only for the operations that really truly need its protection. Because taking page faults while holding a *system*-wide lock is asking for trouble. Deadlock aside, if one of the user pages being accessed happens to be swapped out, tasks could be blocked on sev_cmd_mutex for quite some time waiting for the page to come back from disk or wherever. Rinse and repeat, and I wouldn't be surprised if VM creation/boot can stall for hundreds of milliseconds or worse.
The alternative would be to take sev_cmd_mutex around the kvm_gmem_populate() call in snp_launch_update(), but even if the CPP maintainers were willing to provide hooks to allow that, I'm not willing to take such a hack in KVM.
So, very sadly, I think in-place conversion is dead for 7.4, and will need to be revived on top of an overhaul of sev_cmd_mutex locking.
Or I guess maybe a less painful temporary workaround would be to disable support for SNP if in-place conversion is enabled. That would avoid extending the in-place conversion bottleneck for TDX, pKVM, and CCA, without having to take a gross hack to make SNP play nice.
E.g. this as a workaround for 7.4 (not yet tested)
arch/x86/kvm/svm/sev.c +8 -0
@@ -3213,6 +3213,14 @@ void __init sev_hardware_setup(void) sev_snp_supported = sev_snp_enabled &&cc_platform_has (CC_ATTR_HOST_SEV_SNP ); out: +/* +* FIXME: Allow SNP with in-place guest_memfd conversion once the PSP +* driver no longer takes its global mutex outside of mmap_lock (which +* leads to deadlock with sev_gmem_post_populate()). +*/ +if (gmem_in_place_conversion) + sev_snp_supported = false; +if (sev_enabled) { init_args.probe = true;
Quoted 114 lines
[ 2260.466972] ====================================================== [ 2260.473150] WARNING: possible circular locking dependency detected [ 2260.479329] 7.3.0-smp--ce115a0096358-seanjc-soink #75 Tainted: G U O [ 2260.486981] ------------------------------------------------------ [ 2260.493161] sev_snp_vmsa_pu/24505 is trying to acquire lock: [ 2260.498821] ffffffffc01f7900 (sev_cmd_mutex){+.+.}-{4:4}, at: sev_issue_cmd_external_user+0x39/0x60 [ccp] [ 2260.508387] but task is already holding lock: [ 2260.514213] ffff93c9dd1cf070 (mapping.invalidate_lock#4){++++}-{4:4}, at: kvm_gmem_populate+0x18c/0x300 [kvm] [ 2260.524275] which lock already depends on the new lock.[ 2260.532446] the existing dependency chain (in reverse order) is: [ 2260.539917] -> #2 (mapping.invalidate_lock#4){++++}-{4:4}: [ 2260.546878] down_read+0x3d/0x160 [ 2260.550716] kvm_gmem_fault_user_mapping+0x43/0x180 [kvm] [ 2260.556663] __do_fault+0x3f/0x110 [ 2260.560588] do_pte_missing+0x515/0x1000 [ 2260.565035] handle_mm_fault+0x7f2/0x1290 [ 2260.569567] do_user_addr_fault+0x47d/0x6c0 [ 2260.574271] exc_page_fault+0x67/0xa0 [ 2260.578458] asm_exc_page_fault+0x26/0x30 [ 2260.582991] -> #1 (&mm->mmap_lock){++++}-{4:4}: [ 2260.588997] __might_fault+0x59/0x80 [ 2260.593096] _copy_to_user+0x23/0x60 [ 2260.597197] sev_ioctl_do_platform_status+0x50/0x80 [ccp] [ 2260.603116] sev_ioctl+0x220/0x2b0 [ccp] [ 2260.607560] __se_sys_ioctl+0x6d/0xb0 [ 2260.611749] do_syscall_64+0xfe/0x460 [ 2260.615933] entry_SYSCALL_64_after_hwframe+0x4b/0x53 [ 2260.621506] -> #0 (sev_cmd_mutex){+.+.}-{4:4}: [ 2260.627426] __lock_acquire+0x160d/0x2940 [ 2260.631957] lock_acquire+0xf1/0x300 [ 2260.636050] __mutex_lock+0x8f/0xdf0 [ 2260.640147] sev_issue_cmd_external_user+0x39/0x60 [ccp] [ 2260.645982] sev_gmem_post_populate+0x189/0x230 [kvm_amd] [ 2260.651901] kvm_gmem_populate+0x1ed/0x300 [kvm] [ 2260.657066] snp_launch_update+0x1a7/0x250 [kvm_amd] [ 2260.662551] sev_mem_enc_ioctl+0x371/0x3d0 [kvm_amd] [ 2260.668036] kvm_vm_ioctl+0x52c/0x580 [kvm] [ 2260.672761] __se_sys_ioctl+0x6d/0xb0 [ 2260.676946] do_syscall_64+0xfe/0x460 [ 2260.681133] entry_SYSCALL_64_after_hwframe+0x4b/0x53 [ 2260.686706] other info that might help us debug this:[ 2260.694695] Chain exists of: sev_cmd_mutex --> &mm->mmap_lock --> mapping.invalidate_lock#4[ 2260.706077] Possible unsafe locking scenario:[ 2260.711993] CPU0 CPU1 [ 2260.716520] ---- ---- [ 2260.721052] lock(mapping.invalidate_lock#4); [ 2260.725498] lock(&mm->mmap_lock); [ 2260.731503] lock(mapping.invalidate_lock#4); [ 2260.738463] lock(sev_cmd_mutex); [ 2260.741868] *** DEADLOCK ***[ 2260.747778] locks held by sev_snp_vmsa_pu/24505: 3, last CPU#35: [ 2260.753785] #0: ffff93c9d54bd3a8 (&kvm->lock){+.+.}-{4:4}, at: sev_mem_enc_ioctl+0x73/0x3d0 [kvm_amd] [ 2260.763083] #1: ffff93c9d54bc0a0 (&kvm->slots_lock){+.+.}-{4:4}, at: snp_launch_update+0x145/0x250 [kvm_amd] [ 2260.772991] #2: ffff93c9dd1cf070 (mapping.invalidate_lock#4){++++}-{4:4}, at: kvm_gmem_populate+0x18c/0x300 [kvm] [ 2260.783356] stack backtrace: [ 2260.787718] CPU: 35 UID: 0 PID: 24505 Comm: sev_snp_vmsa_pu Tainted: G U O 7.3.0-smp--ce115a0096358-seanjc-soink #75 PREEMPT [ 2260.800310] Tainted: [U]=USER, [O]=OOT_MODULE [ 2260.804668] Hardware name: Google, Inc. Arcadia_IT_80/Arcadia_IT_80, BIOS 34.86.0-102 01/25/2026 [ 2260.818127] Call Trace: [ 2260.820579] <TASK> [ 2260.822687] dump_stack_lvl+0x54/0x70 [ 2260.826351] print_circular_bug+0x2e4/0x300 [ 2260.830537] check_noncircular+0xf5/0x120 [ 2260.834553] __lock_acquire+0x160d/0x2940 [ 2260.838565] ? __lock_acquire+0x65e/0x2940 [ 2260.842664] ? rcu_is_watching+0x11/0x40 [ 2260.846587] ? trace_sched_exit_tp+0x25/0xb0 [ 2260.850861] lock_acquire+0xf1/0x300 [ 2260.854440] ? sev_issue_cmd_external_user+0x39/0x60 [ccp] [ 2260.859928] ? lock_is_held_type+0xc7/0x120 [ 2260.864112] ? sev_issue_cmd_external_user+0x39/0x60 [ccp] [ 2260.869599] __mutex_lock+0x8f/0xdf0 [ 2260.873178] ? sev_issue_cmd_external_user+0x39/0x60 [ccp] [ 2260.878663] ? pfn_valid+0x89/0x220 [ 2260.882155] ? pfn_valid+0x1fb/0x220 [ 2260.885736] ? lookup_address+0x49/0x70 [ 2260.889577] sev_issue_cmd_external_user+0x39/0x60 [ccp] [ 2260.894890] sev_gmem_post_populate+0x189/0x230 [kvm_amd] [ 2260.900289] kvm_gmem_populate+0x1ed/0x300 [kvm] [ 2260.904938] ? snp_bind_asid+0xd0/0xd0 [kvm_amd] [ 2260.909561] snp_launch_update+0x1a7/0x250 [kvm_amd] [ 2260.914528] sev_mem_enc_ioctl+0x371/0x3d0 [kvm_amd] [ 2260.919493] kvm_vm_ioctl+0x52c/0x580 [kvm] [ 2260.923706] ? kvm_gmem_ioctl+0x192/0x1e0 [kvm] [ 2260.928266] __se_sys_ioctl+0x6d/0xb0 [ 2260.931930] do_syscall_64+0xfe/0x460 [ 2260.935595] ? entry_SYSCALL_64_after_hwframe+0x4b/0x53 [ 2260.940822] ? trace_irq_disable+0x25/0xc0 [ 2260.944921] entry_SYSCALL_64_after_hwframe+0x4b/0x53 [ 2260.949972] RIP: 0033:0x474bdb [ 2260.953031] Code: 00 48 89 44 24 18 31 c0 48 8d 44 24 60 c7 04 24 10 00 00 00 48 89 44 24 08 48 8d 44 24 20 48 89 44 24 10 b8 10 00 00 00 0f 05 <89> c2 3d 00 f0 ff ff 77 1c 48 8b 44 24 18 64 48 2b 04 25 28 00 00 [ 2260.971770] RSP: 002b:00007ffd188ac3f0 EFLAGS: 00000246 ORIG_RAX: 0000000000000010 [ 2260.979337] RAX: ffffffffffffffda RBX: 0000000028691b30 RCX: 0000000000474bdb [ 2260.986468] RDX: 00007ffd188ac4a0 RSI: 00000000c008aeba RDI: 0000000000000007 [ 2260.993600] RBP: 0000000000135000 R08: 00000000004d50b8 R09: 00000000004e1b78 [ 2261.000734] R10: 0000000000000002 R11: 0000000000000246 R12: 00007ffd188ac4c0 [ 2261.007867] R13: 0000000000137000 R14: 0000000028693db0 R15: 0000000000137000 [ 2261.015001] </TASK>
Re: [PATCH v14 13/22] KVM: selftests: Support guest_memfd in-place conversion
Quoted 65 lines · Sean Christopherson
+Ashish, Tom and Mike, dropped a bunch of others that probably don't care about the CCP/PSP driver.
Quoted 55 lines · Sean Christopherson
Quoted 16 lines · Xiaoyao Li
Review
Quoted 13 lines · Lisa Wang
(quoted diff) +7
-3- flags = 0; - if (is_guest_memfd_required(shape)) + if (is_guest_memfd_required(shape)) { flags |= KVM_MEM_GUEST_MEMFD; + if (kvm_has_gmem_attributes) + gmem_flags |= GUEST_MEMFD_FLAG_INIT_SHARED | + GUEST_MEMFD_FLAG_MMAP; + } - vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, 0, 0, nr_pages, flags); + vm_mem_add(vm, VM_MEM_SRC_ANONYMOUS, 0, 0, nr_pages, flags, + -1, 0, gmem_flags); for (i = 0; i < NR_MEM_REGIONS; i++) vm->memslots[i] = 0;IMO, this diff belongs to the gmem in-place series.
Yep, you're 100% right. I pushed this to "coco" for now...
testing/selftests/kvm/lib/kvm_util.c +13
-2@@ -494,6 +494,7 @@ struct kvm_vm *__vm_create(struct vm_shape shape, u32 nr_runnable_vcpus,u64 nr_pages =vm_nr_pages_required (shape.mode, nr_runnable_vcpus, nr_extra_pages);struct userspace_mem_region *slot0; +u64 gmem_flags =0 ;struct kvm_vm *vm;int i, flags;@@ -509,10 +510,20 @@ struct kvm_vm *__vm_create(struct vm_shape shape, u32 nr_runnable_vcpus,* for CoCo VMs that require GUEST_MEMFD backed private memory. */ flags =0 ; -if (is_guest_memfd_required (shape)) +if (is_guest_memfd_required (shape)) { flags |=KVM_MEM_GUEST_MEMFD ; -vm_userspace_mem_region_add (vm,VM_MEM_SRC_ANONYMOUS ,0 ,0 , nr_pages, flags); +/* +* Allow mmap() and initialize the gmem instance as SHARED if +* in-place conversions are supported, so that the memory can +* be populated/initialized like any other memory region. +*/ +if (kvm_has_gmem_attributes) + gmem_flags |=GUEST_MEMFD_FLAG_INIT_SHARED | +GUEST_MEMFD_FLAG_MMAP ; + } + +vm_mem_add (vm,VM_MEM_SRC_ANONYMOUS ,0 ,0 , nr_pages, flags,-1 ,0 , gmem_flags);for (i =0 ; i <NR_MEM_REGIONS ; i++) vm->memslots[i] =0 ;Quoted 2 lines
but the entire in-place conversion series may need to be dropped because there's a potential deadlock, and I don't see an obviously non-awful way to fix it.
The underlying problem is that
drivers/crypto/ccp/sev-dev.cdoes memory allocations and copy_{to,from}_user() while holdingsev_cmd_mutexin a variety of locations.
Quoted 8 lines
I genuinely think the right fix is to not do that, i.e. to perform invasive surgery on the driver so that it holds
sev_cmd_mutexonly for the operations that really truly need its protection. Because taking page faults while holding a *system*-wide lock is asking for trouble. Deadlock aside, if one of the user pages being accessed happens to be swapped out, tasks could be blocked onsev_cmd_mutexfor quite some time waiting for the page to come back from disk or wherever. Rinse and repeat, and I wouldn't be surprised if VM creation/boot can stall for hundreds of milliseconds or worse.
Hi Sean,
Thanks for catching this. I had a discussion with Tom and Ashish and we agree that making the CCP driver more granular with it's use of sev_cmd_mutex seems like the best choice.
I've include a patch (below) that reworks the CCP driver to take this approach and did some light regression/build testing. Still need to take a closer look but am planning to post this as a proper patch if the approach seems okay to you. This mainly addresses /dev/sev IOCTLs since KVM/internal users use sev_issue_cmd_external_user() which does not do any copy_to/from_user()'s and is basically a direct firmware call which is the latest point where sev_cmd_mutex is needed.
I don't have a reproducer for the specific ABBA in question though; I'm confused at why sev_ioctl_do_platform_status() / SEV_CMD_PLATFORM_STATUS would trigger a gmem fault (outside of malicious/intentional case) since it is just querying status on behalf of host userspace and storing the result in userspace buffer where there's no reason to use a guest/gmem page. Was this test specifically set up to do this or was this reachable from a normal path? Just trying to see if there are interactions I'm failing to consider here.
Quoted 24 lines
The alternative would be to take
sev_cmd_mutexaround thekvm_gmem_populate()call insnp_launch_update(), but even if the CPP maintainers were willing to provide hooks to allow that, I'm not willing to take such a hack in KVM.So, very sadly, I think in-place conversion is dead for 7.4, and will need to be revived on top of an overhaul of
sev_cmd_mutexlocking.Or I guess maybe a less painful temporary workaround would be to disable support for SNP if in-place conversion is enabled. That would avoid extending the in-place conversion bottleneck for TDX, pKVM, and CCA, without having to take a gross hack to make SNP play nice.
E.g. this as a workaround for 7.4 (not yet tested)
arch/x86/kvm/svm/sev.c +1
-0@@ -3213,6 +3213,14 @@ void __init sev_hardware_setup(void) sev_snp_supported = sev_snp_enabled &&cc_platform_has (CC_ATTR_HOST_SEV_SNP ); out: +/*
arch/x86/kvm/svm/sev.c void __init sev_hardware_setup(void) · line 3226
Quoted 10 lines
arch/x86/kvm/svm/sev.c +7
-0+* FIXME: Allow SNP with in-place guest_memfd conversion once the PSP +* driver no longer takes its global mutex outside of mmap_lock (which +* leads to deadlock with sev_gmem_post_populate()). +*/ +if (gmem_in_place_conversion) + sev_snp_supported = false; +if (sev_enabled) { init_args.probe = true;
I would hate to hold everyone else up due to an SNP issue that (hopefully) already has a potential fix. So, if it comes to that and hard decisions need to be made, it would be great if we can just disable SNP specifically. The above approach seems reasonable to me.
Thanks,
Mike
From: Michael Roth <[email protected]>
Date: Mon, 12 Oct 2026 14:32:28 -0500
Subject: [PATCH] crypto: ccp: Don't hold sev_cmd_mutex when copying from/to user pages
(Proper commit message pending)
This can lead to deadlocks and slow performance. Copying to/from userspace pages so handle that outside of the criticial path.
The only bit of awkwardness here is that sev->snp_initialized also relies on the sev_cmd_mutex(), so in that case push the related checks down into the critical path. This currently results in sev_ioctl_do_snp_set_config() potentially doing an unnecessary copy_from_user() before it reports the original ENODEV error in cases, whereas before it would report ENODEV before doing the copy. This can be improved upon based on the fact that sev->snp_initialized can only be set a specific points in time rather than being truly dynamic.
--- drivers/crypto/ccp/sev-dev.c | 73 ++++++++++++++++++++++++------------ 1 file changed, 49 insertions(+), 24 deletions(-)
drivers/crypto/ccp/sev-dev.c +49 -24
@@ -1914,6 +1914,8 @@ static int sev_ioctl_do_reset(struct sev_issue_cmd *argp, bool writable)if (!writable)return -EPERM ; +guard (mutex)(&sev_cmd_mutex); +/* * The SEV spec requires that FACTORY_RESET must be issued in * UNINIT state. Before we go further lets check if any guest is
@@ -1946,7 +1948,8 @@ static int sev_ioctl_do_platform_status(struct sev_issue_cmd *argp)memset (&data,0 ,sizeof (data)); - ret =__sev_do_cmd_locked (SEV_CMD_PLATFORM_STATUS , &data, &argp->error); +scoped_guard (mutex, &sev_cmd_mutex) + ret = __sev_do_cmd_locked(SEV_CMD_PLATFORM_STATUS, &data, &argp->error);if (ret)return ret;
@@ -1965,6 +1968,8 @@ static int sev_ioctl_do_pek_pdh_gen(int cmd, struct sev_issue_cmd *argp, bool wrif (!writable)return -EPERM ; +guard (mutex)(&sev_cmd_mutex); +if (sev->sev_plat_status.state ==SEV_STATE_UNINIT ) { rc =sev_move_to_init_state (argp, &shutdown_required);if (rc)
@@ -2014,14 +2019,23 @@ static int sev_ioctl_do_pek_csr(struct sev_issue_cmd *argp, bool writable) data.len = input.length; cmd: +mutex_lock (&sev_cmd_mutex); +if (sev->sev_plat_status.state ==SEV_STATE_UNINIT ) { ret =sev_move_to_init_state (argp, &shutdown_required); -if (ret) +if (ret) { +mutex_unlock (&sev_cmd_mutex); goto e_free_blob; + } } ret =__sev_do_cmd_locked (SEV_CMD_PEK_CSR , &data, &argp->error); +if (shutdown_required) +__sev_firmware_shutdown (sev, false); + +mutex_unlock (&sev_cmd_mutex); +/* * Firmware will returns the length of the CSR blob (either the minimum * required length or the actual length written), return it to the user.
@@ -2042,9 +2056,6 @@ static int sev_ioctl_do_pek_csr(struct sev_issue_cmd *argp, bool writable) } e_free_blob: -if (shutdown_required) -__sev_firmware_shutdown (sev, false); -kfree (blob);return ret; }
@@ -2318,18 +2329,22 @@ static int sev_ioctl_do_pek_import(struct sev_issue_cmd *argp, bool writable) data.oca_cert_len = input.oca_cert_len;/* If platform is not in INIT state then transition it to INIT */ +mutex_lock (&sev_cmd_mutex); +if (sev->sev_plat_status.state !=SEV_STATE_INIT ) { ret =sev_move_to_init_state (argp, &shutdown_required);if (ret) - goto e_free_oca; + goto e_unlock; } ret =__sev_do_cmd_locked (SEV_CMD_PEK_CERT_IMPORT , &data, &argp->error); -e_free_oca: +e_unlock:if (shutdown_required)__sev_firmware_shutdown (sev, false); +mutex_unlock (&sev_cmd_mutex); +kfree (oca_blob); e_free_pek:kfree (pek_blob);
@@ -2372,7 +2387,8 @@ static int sev_ioctl_do_get_id2(struct sev_issue_cmd *argp) data.len =0 ; } - ret =__sev_do_cmd_locked (SEV_CMD_GET_ID , &data, &argp->error); +scoped_guard (mutex, &sev_cmd_mutex) + ret = __sev_do_cmd_locked(SEV_CMD_GET_ID, &data, &argp->error);/* * Firmware will return the length of the ID value (either the minimum
@@ -2430,7 +2446,8 @@ static int sev_ioctl_do_get_id(struct sev_issue_cmd *argp) data->address =__psp_pa (id_blob); data->len = user_size; - ret =__sev_do_cmd_locked (SEV_CMD_GET_ID , data, &argp->error); +scoped_guard (mutex, &sev_cmd_mutex) + ret = __sev_do_cmd_locked(SEV_CMD_GET_ID, data, &argp->error);if (!ret) {if (copy_to_user ((void __user *)argp->data, id_blob, data->len)) ret = -EFAULT ;
@@ -2492,19 +2509,29 @@ static int sev_ioctl_do_pdh_export(struct sev_issue_cmd *argp, bool writable) data.cert_chain_len = input.cert_chain_len; cmd: +mutex_lock (&sev_cmd_mutex); +/* If platform is not in INIT state then transition it to INIT. */ if (sev->sev_plat_status.state !=SEV_STATE_INIT ) {if (!writable) { ret = -EPERM ; +mutex_unlock (&sev_cmd_mutex); goto e_free_cert; } ret =sev_move_to_init_state (argp, &shutdown_required); -if (ret) +if (ret) { +mutex_unlock (&sev_cmd_mutex); goto e_free_cert; + } } ret =__sev_do_cmd_locked (SEV_CMD_PDH_CERT_EXPORT , &data, &argp->error); +if (shutdown_required) +__sev_firmware_shutdown (sev, false); + +mutex_unlock (&sev_cmd_mutex); +/* * Firmware will return the length of the blobs (either the minimum * required length or the actual length written), return 'em to the user.
@@ -2535,9 +2562,6 @@ static int sev_ioctl_do_pdh_export(struct sev_issue_cmd *argp, bool writable) } e_free_cert: -if (shutdown_required) -__sev_firmware_shutdown (sev, false); -kfree (cert_blob); e_free_pdh:kfree (pdh_blob);
@@ -2605,7 +2629,8 @@ static int sev_ioctl_do_snp_platform_status(struct sev_issue_cmd *argp)if (!argp->data)return -EINVAL ; - ret =__sev_do_snp_platform_status (&status, &argp->error); +scoped_guard (mutex, &sev_cmd_mutex) + ret = __sev_do_snp_platform_status(&status, &argp->error);if (ret <0 )return ret;
@@ -2623,6 +2648,8 @@ static int sev_ioctl_do_snp_commit(struct sev_issue_cmd *argp) buf.len =sizeof (buf); +guard (mutex)(&sev_cmd_mutex); + ret =__sev_do_cmd_locked (SEV_CMD_SNP_COMMIT , &buf, &argp->error);return ret;
@@ -2639,12 +2666,14 @@ static int sev_ioctl_do_snp_set_config(struct sev_issue_cmd *argp, bool writableif (!writable)return -EPERM ; -if (!sev->snp_initialized) -return -ENODEV ; -if (copy_from_user (&config, (void __user *)argp->data,sizeof (config)))return -EFAULT ; +guard (mutex)(&sev_cmd_mutex); + +if (!sev->snp_initialized) +return -ENODEV ; +return __sev_do_cmd_locked (SEV_CMD_SNP_CONFIG , &config, &argp->error); }
@@ -2677,7 +2706,8 @@ static int sev_ioctl_do_snp_vlek_load(struct sev_issue_cmd *argp, bool writable) input.vlek_wrapped_address =__psp_pa (blob); - ret =__sev_do_cmd_locked (SEV_CMD_SNP_VLEK_LOAD , &input, &argp->error); +scoped_guard (mutex, &sev_cmd_mutex) + ret = __sev_do_cmd_locked(SEV_CMD_SNP_VLEK_LOAD, &input, &argp->error);kfree (blob);return ret;
@@ -2702,8 +2732,6 @@ static long sev_ioctl(struct file *file, unsigned int ioctl, unsigned long arg)if (input.cmd >SEV_MAX )return -EINVAL ; -mutex_lock (&sev_cmd_mutex); -switch (input.cmd) {case SEV_FACTORY_RESET :
@@ -2747,14 +2775,11 @@ static long sev_ioctl(struct file *file, unsigned int ioctl, unsigned long arg) ret =sev_ioctl_do_snp_vlek_load (&input, writable);break ;default : - ret = -EINVAL ; - goto out; +return -EINVAL ; }if (copy_to_user (argp, &input,sizeof (struct sev_issue_cmd ))) ret = -EFAULT ; -out: -mutex_unlock (&sev_cmd_mutex);return ret; }
Signature · 1 line
2.43.0
Quoted 114 lines
Quoted 114 lines
[ 2260.466972] ====================================================== [ 2260.473150] WARNING: possible circular locking dependency detected [ 2260.479329] 7.3.0-smp--ce115a0096358-seanjc-soink #75 Tainted: G U O [ 2260.486981] ------------------------------------------------------ [ 2260.493161] sev_snp_vmsa_pu/24505 is trying to acquire lock: [ 2260.498821] ffffffffc01f7900 (sev_cmd_mutex){+.+.}-{4:4}, at: sev_issue_cmd_external_user+0x39/0x60 [ccp] [ 2260.508387] but task is already holding lock: [ 2260.514213] ffff93c9dd1cf070 (mapping.invalidate_lock#4){++++}-{4:4}, at: kvm_gmem_populate+0x18c/0x300 [kvm] [ 2260.524275] which lock already depends on the new lock.[ 2260.532446] the existing dependency chain (in reverse order) is: [ 2260.539917] -> #2 (mapping.invalidate_lock#4){++++}-{4:4}: [ 2260.546878] down_read+0x3d/0x160 [ 2260.550716] kvm_gmem_fault_user_mapping+0x43/0x180 [kvm] [ 2260.556663] __do_fault+0x3f/0x110 [ 2260.560588] do_pte_missing+0x515/0x1000 [ 2260.565035] handle_mm_fault+0x7f2/0x1290 [ 2260.569567] do_user_addr_fault+0x47d/0x6c0 [ 2260.574271] exc_page_fault+0x67/0xa0 [ 2260.578458] asm_exc_page_fault+0x26/0x30 [ 2260.582991] -> #1 (&mm->mmap_lock){++++}-{4:4}: [ 2260.588997] __might_fault+0x59/0x80 [ 2260.593096] _copy_to_user+0x23/0x60 [ 2260.597197] sev_ioctl_do_platform_status+0x50/0x80 [ccp] [ 2260.603116] sev_ioctl+0x220/0x2b0 [ccp] [ 2260.607560] __se_sys_ioctl+0x6d/0xb0 [ 2260.611749] do_syscall_64+0xfe/0x460 [ 2260.615933] entry_SYSCALL_64_after_hwframe+0x4b/0x53 [ 2260.621506] -> #0 (sev_cmd_mutex){+.+.}-{4:4}: [ 2260.627426] __lock_acquire+0x160d/0x2940 [ 2260.631957] lock_acquire+0xf1/0x300 [ 2260.636050] __mutex_lock+0x8f/0xdf0 [ 2260.640147] sev_issue_cmd_external_user+0x39/0x60 [ccp] [ 2260.645982] sev_gmem_post_populate+0x189/0x230 [kvm_amd] [ 2260.651901] kvm_gmem_populate+0x1ed/0x300 [kvm] [ 2260.657066] snp_launch_update+0x1a7/0x250 [kvm_amd] [ 2260.662551] sev_mem_enc_ioctl+0x371/0x3d0 [kvm_amd] [ 2260.668036] kvm_vm_ioctl+0x52c/0x580 [kvm] [ 2260.672761] __se_sys_ioctl+0x6d/0xb0 [ 2260.676946] do_syscall_64+0xfe/0x460 [ 2260.681133] entry_SYSCALL_64_after_hwframe+0x4b/0x53 [ 2260.686706] other info that might help us debug this:[ 2260.694695] Chain exists of: sev_cmd_mutex --> &mm->mmap_lock --> mapping.invalidate_lock#4[ 2260.706077] Possible unsafe locking scenario:[ 2260.711993] CPU0 CPU1 [ 2260.716520] ---- ---- [ 2260.721052] lock(mapping.invalidate_lock#4); [ 2260.725498] lock(&mm->mmap_lock); [ 2260.731503] lock(mapping.invalidate_lock#4); [ 2260.738463] lock(sev_cmd_mutex); [ 2260.741868] *** DEADLOCK ***[ 2260.747778] locks held by sev_snp_vmsa_pu/24505: 3, last CPU#35: [ 2260.753785] #0: ffff93c9d54bd3a8 (&kvm->lock){+.+.}-{4:4}, at: sev_mem_enc_ioctl+0x73/0x3d0 [kvm_amd] [ 2260.763083] #1: ffff93c9d54bc0a0 (&kvm->slots_lock){+.+.}-{4:4}, at: snp_launch_update+0x145/0x250 [kvm_amd] [ 2260.772991] #2: ffff93c9dd1cf070 (mapping.invalidate_lock#4){++++}-{4:4}, at: kvm_gmem_populate+0x18c/0x300 [kvm] [ 2260.783356] stack backtrace: [ 2260.787718] CPU: 35 UID: 0 PID: 24505 Comm: sev_snp_vmsa_pu Tainted: G U O 7.3.0-smp--ce115a0096358-seanjc-soink #75 PREEMPT [ 2260.800310] Tainted: [U]=USER, [O]=OOT_MODULE [ 2260.804668] Hardware name: Google, Inc. Arcadia_IT_80/Arcadia_IT_80, BIOS 34.86.0-102 01/25/2026 [ 2260.818127] Call Trace: [ 2260.820579] <TASK> [ 2260.822687] dump_stack_lvl+0x54/0x70 [ 2260.826351] print_circular_bug+0x2e4/0x300 [ 2260.830537] check_noncircular+0xf5/0x120 [ 2260.834553] __lock_acquire+0x160d/0x2940 [ 2260.838565] ? __lock_acquire+0x65e/0x2940 [ 2260.842664] ? rcu_is_watching+0x11/0x40 [ 2260.846587] ? trace_sched_exit_tp+0x25/0xb0 [ 2260.850861] lock_acquire+0xf1/0x300 [ 2260.854440] ? sev_issue_cmd_external_user+0x39/0x60 [ccp] [ 2260.859928] ? lock_is_held_type+0xc7/0x120 [ 2260.864112] ? sev_issue_cmd_external_user+0x39/0x60 [ccp] [ 2260.869599] __mutex_lock+0x8f/0xdf0 [ 2260.873178] ? sev_issue_cmd_external_user+0x39/0x60 [ccp] [ 2260.878663] ? pfn_valid+0x89/0x220 [ 2260.882155] ? pfn_valid+0x1fb/0x220 [ 2260.885736] ? lookup_address+0x49/0x70 [ 2260.889577] sev_issue_cmd_external_user+0x39/0x60 [ccp] [ 2260.894890] sev_gmem_post_populate+0x189/0x230 [kvm_amd] [ 2260.900289] kvm_gmem_populate+0x1ed/0x300 [kvm] [ 2260.904938] ? snp_bind_asid+0xd0/0xd0 [kvm_amd] [ 2260.909561] snp_launch_update+0x1a7/0x250 [kvm_amd] [ 2260.914528] sev_mem_enc_ioctl+0x371/0x3d0 [kvm_amd] [ 2260.919493] kvm_vm_ioctl+0x52c/0x580 [kvm] [ 2260.923706] ? kvm_gmem_ioctl+0x192/0x1e0 [kvm] [ 2260.928266] __se_sys_ioctl+0x6d/0xb0 [ 2260.931930] do_syscall_64+0xfe/0x460 [ 2260.935595] ? entry_SYSCALL_64_after_hwframe+0x4b/0x53 [ 2260.940822] ? trace_irq_disable+0x25/0xc0 [ 2260.944921] entry_SYSCALL_64_after_hwframe+0x4b/0x53 [ 2260.949972] RIP: 0033:0x474bdb [ 2260.953031] Code: 00 48 89 44 24 18 31 c0 48 8d 44 24 60 c7 04 24 10 00 00 00 48 89 44 24 08 48 8d 44 24 20 48 89 44 24 10 b8 10 00 00 00 0f 05 <89> c2 3d 00 f0 ff ff 77 1c 48 8b 44 24 18 64 48 2b 04 25 28 00 00 [ 2260.971770] RSP: 002b:00007ffd188ac3f0 EFLAGS: 00000246 ORIG_RAX: 0000000000000010 [ 2260.979337] RAX: ffffffffffffffda RBX: 0000000028691b30 RCX: 0000000000474bdb [ 2260.986468] RDX: 00007ffd188ac4a0 RSI: 00000000c008aeba RDI: 0000000000000007 [ 2260.993600] RBP: 0000000000135000 R08: 00000000004d50b8 R09: 00000000004e1b78 [ 2261.000734] R10: 0000000000000002 R11: 0000000000000246 R12: 00007ffd188ac4c0 [ 2261.007867] R13: 0000000000137000 R14: 0000000028693db0 R15: 0000000000137000 [ 2261.015001] </TASK>
Sean Christopherson
maintainerRe: [PATCH v14 13/22] KVM: selftests: Support guest_memfd in-place conversion
Quoted 8 lines · Michael Roth
I don't have a reproducer for the specific ABBA in question though; I'm confused at why
sev_ioctl_do_platform_status()/SEV_CMD_PLATFORM_STATUSwould trigger a gmem fault (outside of malicious/intentional case) since it is just querying status on behalf of host userspace and storing the result in userspace buffer where there's no reason to use a guest/gmem page. Was this test specifically set up to do this or was this reachable from a normal path? Just trying to see if there are interactions I'm failing to consider here.
I was just running KVM selftests, although I was running all of them at once.
The condition doesn't require an actual gmem fault. The uaccess APIs have a __might_fault() to communicate that a page fault may occur, and lockdep is smart enough to piece together the problematic chain of locks.
-> #1 (&mm->mmap_lock){++++}-{4:4}:
__might_fault+0x59/0x80 <===================
_copy_to_user+0x23/0x60
sev_ioctl_do_platform_status+0x50/0x80 [ccp]
sev_ioctl+0x220/0x2b0 [ccp]
__se_sys_ioctl+0x6d/0xb0
do_syscall_64+0xfe/0x460
entry_SYSCALL_64_after_hwframe+0x4b/0x53Re: [PATCH v14 13/22] KVM: selftests: Support guest_memfd in-place conversion
Quoted 16 lines · Sean Christopherson
Quoted 8 lines · Michael Roth
I don't have a reproducer for the specific ABBA in question though; I'm confused at why
sev_ioctl_do_platform_status()/SEV_CMD_PLATFORM_STATUSwould trigger a gmem fault (outside of malicious/intentional case) since it is just querying status on behalf of host userspace and storing the result in userspace buffer where there's no reason to use a guest/gmem page. Was this test specifically set up to do this or was this reachable from a normal path? Just trying to see if there are interactions I'm failing to consider here.I was just running KVM selftests, although I was running all of them at once.
The condition doesn't require an actual gmem fault. The uaccess APIs have a
__might_fault()to communicate that a page fault may occur, and lockdep is smart enough to piece together the problematic chain of locks.
Quoted 8 lines
-> #1 (&mm->mmap_lock){++++}-{4:4}:
__might_fault+0x59/0x80 <=================== _copy_to_user+0x23/0x60 sev_ioctl_do_platform_status+0x50/0x80 [ccp] sev_ioctl+0x220/0x2b0 [ccp] __se_sys_ioctl+0x6d/0xb0 do_syscall_64+0xfe/0x460 entry_SYSCALL_64_after_hwframe+0x4b/0x53
Ok, that's pretty spiffy. I'll try that on my end as well.
Thanks!
-Mike
Re: [PATCH v14 13/22] KVM: selftests: Support guest_memfd in-place conversion
In reply to Sean ChristophersonQuoted 65 lines · Sean Christopherson
+Ashish, Tom and Mike, dropped a bunch of others that probably don't care about the CCP/PSP driver.
Quoted 55 lines · Sean Christopherson
Quoted 16 lines · Xiaoyao Li
Review
Quoted 13 lines · Lisa Wang
(quoted diff) +7
-3- flags = 0; - if (is_guest_memfd_required(shape)) + if (is_guest_memfd_required(shape)) { flags |= KVM_MEM_GUEST_MEMFD; + if (kvm_has_gmem_attributes) + gmem_flags |= GUEST_MEMFD_FLAG_INIT_SHARED | + GUEST_MEMFD_FLAG_MMAP; + } - vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, 0, 0, nr_pages, flags); + vm_mem_add(vm, VM_MEM_SRC_ANONYMOUS, 0, 0, nr_pages, flags, + -1, 0, gmem_flags); for (i = 0; i < NR_MEM_REGIONS; i++) vm->memslots[i] = 0;IMO, this diff belongs to the gmem in-place series.
Yep, you're 100% right. I pushed this to "coco" for now...
testing/selftests/kvm/lib/kvm_util.c +13
-2@@ -494,6 +494,7 @@ struct kvm_vm *__vm_create(struct vm_shape shape, u32 nr_runnable_vcpus,u64 nr_pages =vm_nr_pages_required (shape.mode, nr_runnable_vcpus, nr_extra_pages);struct userspace_mem_region *slot0; +u64 gmem_flags =0 ;struct kvm_vm *vm;int i, flags;@@ -509,10 +510,20 @@ struct kvm_vm *__vm_create(struct vm_shape shape, u32 nr_runnable_vcpus,* for CoCo VMs that require GUEST_MEMFD backed private memory. */ flags =0 ; -if (is_guest_memfd_required (shape)) +if (is_guest_memfd_required (shape)) { flags |=KVM_MEM_GUEST_MEMFD ; -vm_userspace_mem_region_add (vm,VM_MEM_SRC_ANONYMOUS ,0 ,0 , nr_pages, flags); +/* +* Allow mmap() and initialize the gmem instance as SHARED if +* in-place conversions are supported, so that the memory can +* be populated/initialized like any other memory region. +*/ +if (kvm_has_gmem_attributes) + gmem_flags |=GUEST_MEMFD_FLAG_INIT_SHARED | +GUEST_MEMFD_FLAG_MMAP ; + } + +vm_mem_add (vm,VM_MEM_SRC_ANONYMOUS ,0 ,0 , nr_pages, flags,-1 ,0 , gmem_flags);for (i =0 ; i <NR_MEM_REGIONS ; i++) vm->memslots[i] =0 ;Quoted 2 lines
but the entire in-place conversion series may need to be dropped because there's a potential deadlock, and I don't see an obviously non-awful way to fix it.
The underlying problem is that
drivers/crypto/ccp/sev-dev.cdoes memory allocations and copy_{to,from}_user() while holdingsev_cmd_mutexin a variety of locations.
Quoted 3 lines
I genuinely think the right fix is to not do that, i.e. to perform invasive surgery on the driver so that it holds
sev_cmd_mutexonly for the operations that really truly need its protection.
I reached this same conclusion, and I think this is the right fix.
To add on, the main purpose of taking the invalidate lock in kvm_gmem_fault_user_mapping() is to synchronize with conversions. mapping.invalidate_lock is the lock used to protect attributes.
(The maple_tree's internal RCU lock is for lockless readers (these readers are only within KVM) and respect the KVM MMU protocol and will check again before committing the result of the read.)
The purpose of taking the invalidate lock in the populate flow is also to synchronize with conversions, so using a lock other than mapping.invalidate_lock wouldn't work, both cases would still take that other lock.
Quoted 9 lines
Because taking page faults while holding a *system*-wide
lock is asking for trouble. Deadlock aside, if one of the user pages being accessed happens to be swapped out, tasks could be blocked onsev_cmd_mutexfor quite some time waiting for the page to come back from disk or wherever. Rinse and repeat, and I wouldn't be surprised if VM creation/boot can stall for hundreds of milliseconds or worse.The alternative would be to take
sev_cmd_mutexaround thekvm_gmem_populate()call insnp_launch_update(), but even if the CPP maintainers were willing to provide hooks
I realized you meant CCP/PSP instead of CPP :)
Quoted 2 lines
to allow that, I'm not willing to take such a hack in KVM.
Is this a hack because sev_cmd_mutex() would be held for a long time while the populate loop executes?
Quoted 16 lines
So, very sadly, I think in-place conversion is dead for 7.4, and will need to be revived on top of an overhaul of
sev_cmd_mutexlocking.Or I guess maybe a less painful temporary workaround would be to disable support for SNP if in-place conversion is enabled. That would avoid extending the in-place conversion bottleneck for TDX, pKVM, and CCA, without having to take a gross hack to make SNP play nice.
E.g. this as a workaround for 7.4 (not yet tested)
arch/x86/kvm/svm/sev.c +0
-0@@ -3213,6 +3213,14 @@ void __init sev_hardware_setup(void) sev_snp_supported = sev_snp_enabled &&cc_platform_has (CC_ATTR_HOST_SEV_SNP );
arch/x86/kvm/svm/sev.c void __init sev_hardware_setup(void) · line 3223
Quoted 10 lines
arch/x86/kvm/svm/sev.c +8
-0out: +/* +* FIXME: Allow SNP with in-place guest_memfd conversion once the PSP +* driver no longer takes its global mutex outside of mmap_lock (which +* leads to deadlock with sev_gmem_post_populate()). +*/ +if (gmem_in_place_conversion) + sev_snp_supported = false; +
If we go with something like this, when SNP and gmem_in_place_conversion starts to work together, would we need some kind of new CAP or other way to advertise that guest_memfd and SNP are now friends?
If Michael/Tom/Ashish are fine with this, I'd definitely prefer some stopgap over delaying all of conversions.
I just saw Michael's email, thank you so much for jumping on this quickly!
Quoted 117 lines
arch/x86/kvm/svm/sev.c +0
-0if (sev_enabled) { init_args.probe = true;Quoted 114 lines
[ 2260.466972] ====================================================== [ 2260.473150] WARNING: possible circular locking dependency detected [ 2260.479329] 7.3.0-smp--ce115a0096358-seanjc-soink #75 Tainted: G U O [ 2260.486981] ------------------------------------------------------ [ 2260.493161] sev_snp_vmsa_pu/24505 is trying to acquire lock: [ 2260.498821] ffffffffc01f7900 (sev_cmd_mutex){+.+.}-{4:4}, at: sev_issue_cmd_external_user+0x39/0x60 [ccp] [ 2260.508387] but task is already holding lock: [ 2260.514213] ffff93c9dd1cf070 (mapping.invalidate_lock#4){++++}-{4:4}, at: kvm_gmem_populate+0x18c/0x300 [kvm] [ 2260.524275] which lock already depends on the new lock.[ 2260.532446] the existing dependency chain (in reverse order) is: [ 2260.539917] -> #2 (mapping.invalidate_lock#4){++++}-{4:4}: [ 2260.546878] down_read+0x3d/0x160 [ 2260.550716] kvm_gmem_fault_user_mapping+0x43/0x180 [kvm] [ 2260.556663] __do_fault+0x3f/0x110 [ 2260.560588] do_pte_missing+0x515/0x1000 [ 2260.565035] handle_mm_fault+0x7f2/0x1290 [ 2260.569567] do_user_addr_fault+0x47d/0x6c0 [ 2260.574271] exc_page_fault+0x67/0xa0 [ 2260.578458] asm_exc_page_fault+0x26/0x30 [ 2260.582991] -> #1 (&mm->mmap_lock){++++}-{4:4}: [ 2260.588997] __might_fault+0x59/0x80 [ 2260.593096] _copy_to_user+0x23/0x60 [ 2260.597197] sev_ioctl_do_platform_status+0x50/0x80 [ccp] [ 2260.603116] sev_ioctl+0x220/0x2b0 [ccp] [ 2260.607560] __se_sys_ioctl+0x6d/0xb0 [ 2260.611749] do_syscall_64+0xfe/0x460 [ 2260.615933] entry_SYSCALL_64_after_hwframe+0x4b/0x53 [ 2260.621506] -> #0 (sev_cmd_mutex){+.+.}-{4:4}: [ 2260.627426] __lock_acquire+0x160d/0x2940 [ 2260.631957] lock_acquire+0xf1/0x300 [ 2260.636050] __mutex_lock+0x8f/0xdf0 [ 2260.640147] sev_issue_cmd_external_user+0x39/0x60 [ccp] [ 2260.645982] sev_gmem_post_populate+0x189/0x230 [kvm_amd] [ 2260.651901] kvm_gmem_populate+0x1ed/0x300 [kvm] [ 2260.657066] snp_launch_update+0x1a7/0x250 [kvm_amd] [ 2260.662551] sev_mem_enc_ioctl+0x371/0x3d0 [kvm_amd] [ 2260.668036] kvm_vm_ioctl+0x52c/0x580 [kvm] [ 2260.672761] __se_sys_ioctl+0x6d/0xb0 [ 2260.676946] do_syscall_64+0xfe/0x460 [ 2260.681133] entry_SYSCALL_64_after_hwframe+0x4b/0x53 [ 2260.686706] other info that might help us debug this:[ 2260.694695] Chain exists of: sev_cmd_mutex --> &mm->mmap_lock --> mapping.invalidate_lock#4[ 2260.706077] Possible unsafe locking scenario:[ 2260.711993] CPU0 CPU1 [ 2260.716520] ---- ---- [ 2260.721052] lock(mapping.invalidate_lock#4); [ 2260.725498] lock(&mm->mmap_lock); [ 2260.731503] lock(mapping.invalidate_lock#4); [ 2260.738463] lock(sev_cmd_mutex); [ 2260.741868] *** DEADLOCK ***[ 2260.747778] locks held by sev_snp_vmsa_pu/24505: 3, last CPU#35: [ 2260.753785] #0: ffff93c9d54bd3a8 (&kvm->lock){+.+.}-{4:4}, at: sev_mem_enc_ioctl+0x73/0x3d0 [kvm_amd] [ 2260.763083] #1: ffff93c9d54bc0a0 (&kvm->slots_lock){+.+.}-{4:4}, at: snp_launch_update+0x145/0x250 [kvm_amd] [ 2260.772991] #2: ffff93c9dd1cf070 (mapping.invalidate_lock#4){++++}-{4:4}, at: kvm_gmem_populate+0x18c/0x300 [kvm] [ 2260.783356] stack backtrace: [ 2260.787718] CPU: 35 UID: 0 PID: 24505 Comm: sev_snp_vmsa_pu Tainted: G U O 7.3.0-smp--ce115a0096358-seanjc-soink #75 PREEMPT [ 2260.800310] Tainted: [U]=USER, [O]=OOT_MODULE [ 2260.804668] Hardware name: Google, Inc. Arcadia_IT_80/Arcadia_IT_80, BIOS 34.86.0-102 01/25/2026 [ 2260.818127] Call Trace: [ 2260.820579] <TASK> [ 2260.822687] dump_stack_lvl+0x54/0x70 [ 2260.826351] print_circular_bug+0x2e4/0x300 [ 2260.830537] check_noncircular+0xf5/0x120 [ 2260.834553] __lock_acquire+0x160d/0x2940 [ 2260.838565] ? __lock_acquire+0x65e/0x2940 [ 2260.842664] ? rcu_is_watching+0x11/0x40 [ 2260.846587] ? trace_sched_exit_tp+0x25/0xb0 [ 2260.850861] lock_acquire+0xf1/0x300 [ 2260.854440] ? sev_issue_cmd_external_user+0x39/0x60 [ccp] [ 2260.859928] ? lock_is_held_type+0xc7/0x120 [ 2260.864112] ? sev_issue_cmd_external_user+0x39/0x60 [ccp] [ 2260.869599] __mutex_lock+0x8f/0xdf0 [ 2260.873178] ? sev_issue_cmd_external_user+0x39/0x60 [ccp] [ 2260.878663] ? pfn_valid+0x89/0x220 [ 2260.882155] ? pfn_valid+0x1fb/0x220 [ 2260.885736] ? lookup_address+0x49/0x70 [ 2260.889577] sev_issue_cmd_external_user+0x39/0x60 [ccp] [ 2260.894890] sev_gmem_post_populate+0x189/0x230 [kvm_amd] [ 2260.900289] kvm_gmem_populate+0x1ed/0x300 [kvm] [ 2260.904938] ? snp_bind_asid+0xd0/0xd0 [kvm_amd] [ 2260.909561] snp_launch_update+0x1a7/0x250 [kvm_amd] [ 2260.914528] sev_mem_enc_ioctl+0x371/0x3d0 [kvm_amd] [ 2260.919493] kvm_vm_ioctl+0x52c/0x580 [kvm] [ 2260.923706] ? kvm_gmem_ioctl+0x192/0x1e0 [kvm] [ 2260.928266] __se_sys_ioctl+0x6d/0xb0 [ 2260.931930] do_syscall_64+0xfe/0x460 [ 2260.935595] ? entry_SYSCALL_64_after_hwframe+0x4b/0x53 [ 2260.940822] ? trace_irq_disable+0x25/0xc0 [ 2260.944921] entry_SYSCALL_64_after_hwframe+0x4b/0x53 [ 2260.949972] RIP: 0033:0x474bdb [ 2260.953031] Code: 00 48 89 44 24 18 31 c0 48 8d 44 24 60 c7 04 24 10 00 00 00 48 89 44 24 08 48 8d 44 24 20 48 89 44 24 10 b8 10 00 00 00 0f 05 <89> c2 3d 00 f0 ff ff 77 1c 48 8b 44 24 18 64 48 2b 04 25 28 00 00 [ 2260.971770] RSP: 002b:00007ffd188ac3f0 EFLAGS: 00000246 ORIG_RAX: 0000000000000010 [ 2260.979337] RAX: ffffffffffffffda RBX: 0000000028691b30 RCX: 0000000000474bdb [ 2260.986468] RDX: 00007ffd188ac4a0 RSI: 00000000c008aeba RDI: 0000000000000007 [ 2260.993600] RBP: 0000000000135000 R08: 00000000004d50b8 R09: 00000000004e1b78 [ 2261.000734] R10: 0000000000000002 R11: 0000000000000246 R12: 00007ffd188ac4c0 [ 2261.007867] R13: 0000000000137000 R14: 0000000028693db0 R15: 0000000000137000 [ 2261.015001] </TASK>
Sean Christopherson
maintainerRe: [PATCH v14 13/22] KVM: selftests: Support guest_memfd in-place conversion
Quoted 11 lines · Ackerley Tng
Quoted 2 lines · Sean Christopherson
The alternative would be to take
sev_cmd_mutexaround thekvm_gmem_populate()call insnp_launch_update(), but even if the CPP maintainers were willing to provide hooksI realized you meant CCP/PSP instead of CPP :)
Quoted 2 lines
to allow that, I'm not willing to take such a hack in KVM.
Is this a hack because
sev_cmd_mutex()would be held for a long time while the populate loop executes?
It would also bleed CCP internals into KVM, and in general doesn't pass a basic "is this the right thing to do" test.
Quoted 17 lines
Quoted 17 lines
So, very sadly, I think in-place conversion is dead for 7.4, and will need to be revived on top of an overhaul of
sev_cmd_mutexlocking.Or I guess maybe a less painful temporary workaround would be to disable support for SNP if in-place conversion is enabled. That would avoid extending the in-place conversion bottleneck for TDX, pKVM, and CCA, without having to take a gross hack to make SNP play nice.
E.g. this as a workaround for 7.4 (not yet tested)
arch/x86/kvm/svm/sev.c +0
-0@@ -3213,6 +3213,14 @@ void __init sev_hardware_setup(void) sev_snp_supported = sev_snp_enabled &&cc_platform_has (CC_ATTR_HOST_SEV_SNP );
Quoted 13 lines
arch/x86/kvm/svm/sev.c void __init sev_hardware_setup(void) · line 3223
Quoted 9 lines
arch/x86/kvm/svm/sev.c +8
-0out: +/* +* FIXME: Allow SNP with in-place guest_memfd conversion once the PSP +* driver no longer takes its global mutex outside of mmap_lock (which +* leads to deadlock with sev_gmem_post_populate()). +*/ +if (gmem_in_place_conversion) + sev_snp_supported = false; +If we go with something like this, when SNP and
gmem_in_place_conversionstarts to work together, would we need some kind of new CAP or other way to advertise thatguest_memfdand SNP are now friends?
I don't think so? KVM already enumerates support for SNP, so reverting the block should tell userspace everything it needs. We'd need to document that enabling in-place conversion disables SNP support, but I think it's purely a documentation problem.
Re: [PATCH v14 13/22] KVM: selftests: Support guest_memfd in-place conversion
In reply to Lisa WangQuoted 2 lines · Lisa Wang
Set the memory region as shared if
guest_memfdin-place conversion is support, so that the KVM selftest framework functions can populate
^^^^^^^ nit: supported
Quoted 3 lines
mmap()-ed
guest_memfdmemory the same way memory from other memory providers are populated.
[ ... ]
tools/testing/selftests/kvm/lib/kvm_util.c struct kvm_vm *__vm_create(struct vm_shape shape, u32 nr_runnable_vcpus, · line 501
Quoted 9 lines
tools/testing/selftests/kvm/lib/kvm_util.c +2
-2@@ -495,14 +497,20 @@ struct kvm_vm *__vm_create(struct vm_shape shape, u32 nr_runnable_vcpus, vm =____vm_create (shape);/* -* Force GUEST_MEMFD for the primary memory region if necessary, e.g. -* for CoCo VMs that require GUEST_MEMFD backed private memory. +* Force GUEST_MEMFD for the primary memory region if necessary, and +* initialize it as shared if in-place conversion is support, so the
^^^^^^^
same nitQuoted 3 lines
tools/testing/selftests/kvm/lib/kvm_util.c +2
-0+* selftest framework can populate it exactly like other memory +* providers. */
PATCH v14 14/22 KVM: selftests: Expose function to allocate vCPU stack
In reply to Lisa WangFrom: Sagi Shahar <[email protected]>
Introduce kvm_allocate_vcpu_stack() to allocate a vCPU's stack in preparation for TDX to allocate a vCPU's stack and initialize its stack pointer.
TDX VMs' registers are protected state and cannot be initialized using the KVM_SET_REGS ioctl() that is used for normal VMs. A TDX vCPU's stack address will be a property of the TDX specific boot code that initializes the vCPUs' stack pointers at boot.
--- tools/testing/selftests/kvm/include/x86/processor.h | 2 ++ tools/testing/selftests/kvm/lib/x86/processor.c | 16 +++++++++++----- 2 files changed, 13 insertions(+), 5 deletions(-)
tools/testing/selftests/kvm/include/x86/processor.h +2 -0
@@ -1144,6 +1144,8 @@ static inline void vcpu_clear_cpuid_feature(struct kvm_vcpu *vcpu,vcpu_set_or_clear_cpuid_feature (vcpu, feature, false); } +gva_t kvm_allocate_vcpu_stack (struct kvm_vm *vm); +u64 vcpu_get_msr (struct kvm_vcpu *vcpu,u64 msr_index);int _vcpu_set_msr (struct kvm_vcpu *vcpu,u64 msr_index,u64 msr_value);
tools/testing/selftests/kvm/lib/x86/processor.c +11 -5
@@ -813,12 +813,9 @@ void vcpu_arch_set_entry_point(struct kvm_vcpu *vcpu, void *guest_code)vcpu_regs_set (vcpu, ®s); } -struct kvm_vcpu *vm_arch_vcpu_add (struct kvm_vm *vm,u32 vcpu_id) +gva_t kvm_allocate_vcpu_stack (struct kvm_vm *vm) { -struct kvm_mp_state mp_state; -struct kvm_regs regs;gva_t stack_gva; -struct kvm_vcpu *vcpu; stack_gva =__vm_alloc (vm,DEFAULT_STACK_PGS *getpagesize (),DEFAULT_GUEST_STACK_VADDR_MIN ,MEM_REGION_DATA );
@@ -838,6 +835,15 @@ struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, u32 vcpu_id) "__vm_alloc() did not provide a page-aligned address"); stack_gva -=8 ; +return stack_gva; +} + +struct kvm_vcpu *vm_arch_vcpu_add (struct kvm_vm *vm,u32 vcpu_id) +{ +struct kvm_mp_state mp_state; +struct kvm_vcpu *vcpu; +struct kvm_regs regs; + vcpu =__vm_vcpu_add (vm, vcpu_id);vcpu_init_cpuid (vcpu,kvm_get_supported_cpuid ());vcpu_init_sregs (vm, vcpu);
@@ -846,7 +852,7 @@ struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, u32 vcpu_id)/* Setup guest general purpose registers */ vcpu_regs_get (vcpu, ®s); regs.rflags = regs.rflags |0x2 ; - regs.rsp = stack_gva; + regs.rsp =kvm_allocate_vcpu_stack (vm);vcpu_regs_set (vcpu, ®s);/* Setup the MP state */
Signature · 1 line
2.55.0.229.g6434b31f56-goog
Re: [PATCH v14 14/22] KVM: selftests: Expose function to allocate vCPU stack
Quoted 11 lines · Lisa Wang
From: Sagi Shahar <[email protected]>
Introduce
kvm_allocate_vcpu_stack()to allocate a vCPU's stack in preparation for TDX to allocate a vCPU's stack and initialize its stack pointer.TDX VMs' registers are protected state and cannot be initialized using the
KVM_SET_REGSioctl()that is used for normal VMs. A TDX vCPU's stack address will be a property of the TDX specific boot code that initializes the vCPUs' stack pointers at boot.
Quoted 4 lines
Reviewed-by Binbin Wu <[email protected]>Signed-off-by Sagi Shahar <[email protected]>Reviewed-by Ira Weiny <[email protected]>Signed-off-by Lisa Wang <[email protected]>
PATCH v14 15/22 KVM: selftests: Call KVM_TDX_INIT_VCPU when creating a new TDX vcpu
In reply to Lisa WangFrom: Sagi Shahar <[email protected]>
TDX VMs need to issue the KVM_TDX_INIT_VCPU ioctl for each vcpu after vcpu creation.
Since the cpuids for TD are managed by the TDX module, read the values virtualized for the TD using KVM_TDX_GET_CPUID and set them in kvm using KVM_SET_CPUID2 so that kvm has an accurate view of the VM cpuid values.
--- .../selftests/kvm/include/x86/tdx/tdx_util.h | 21 ++++++++++++++ tools/testing/selftests/kvm/lib/x86/processor.c | 33 ++++++++++++++++------ 2 files changed, 46 insertions(+), 8 deletions(-)
tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h +21 -0
@@ -44,6 +44,27 @@ static inline bool is_tdx_vm(struct kvm_vm *vm) } \ }) +#define __tdx_vcpu_ioctl (vcpu, cmd, _flags, arg) \ +({ \ + union { \ + struct kvm_tdx_cmd c; \ + unsigned long raw; \ + } tdx_cmd = { .c = { \ + .id = (cmd), \ + .flags = (u32)(_flags), \ + .data = (u64)(arg), \ + } }; \ + \ + __vcpu_ioctl(vcpu, KVM_MEMORY_ENCRYPT_OP, &tdx_cmd.raw); \ +}) + +#define tdx_vcpu_ioctl (vcpu, cmd, flags, arg) \ +({ \ + int ret = __tdx_vcpu_ioctl(vcpu, cmd, flags, arg); \ + TEST_ASSERT(!ret, "%s failed, errno: %d (%s)", \ + #cmd, errno, strerror(errno)); \ +}) +void tdx_init_vm (struct kvm_vm *vm,u64 attributes);void tdx_vm_setup_boot_code_region (struct kvm_vm *vm);void tdx_vm_setup_boot_parameters_region (struct kvm_vm *vm,u32 nr_runnable_vcpus);
tools/testing/selftests/kvm/lib/x86/processor.c +25 -8
@@ -838,6 +838,17 @@ gva_t kvm_allocate_vcpu_stack(struct kvm_vm *vm)return stack_gva; } +static void tdx_vcpu_init (struct kvm_vm *vm,struct kvm_vcpu *vcpu) +{ +struct kvm_cpuid2 *cpuid; + + cpuid =allocate_kvm_cpuid2 (MAX_NR_CPUID_ENTRIES ); +tdx_vcpu_ioctl (vcpu,KVM_TDX_GET_CPUID ,0 , cpuid); +vcpu_init_cpuid (vcpu, cpuid); +free (cpuid); +tdx_vcpu_ioctl (vcpu,KVM_TDX_INIT_VCPU ,0 ,NULL ); +} +struct kvm_vcpu *vm_arch_vcpu_add (struct kvm_vm *vm,u32 vcpu_id) {struct kvm_mp_state mp_state;
@@ -845,15 +856,21 @@ struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, u32 vcpu_id)struct kvm_regs regs; vcpu =__vm_vcpu_add (vm, vcpu_id); -vcpu_init_cpuid (vcpu,kvm_get_supported_cpuid ()); -vcpu_init_sregs (vm, vcpu); -vcpu_init_xcrs (vm, vcpu); -/* Setup guest general purpose registers */ -vcpu_regs_get (vcpu, ®s); - regs.rflags = regs.rflags |0x2 ; - regs.rsp =kvm_allocate_vcpu_stack (vm); -vcpu_regs_set (vcpu, ®s); +if (is_tdx_vm (vm)) { +tdx_vcpu_init (vm, vcpu); + }else { +vcpu_init_cpuid (vcpu,kvm_get_supported_cpuid ()); + +vcpu_init_sregs (vm, vcpu); +vcpu_init_xcrs (vm, vcpu); + +/* Setup guest general purpose registers */ +vcpu_regs_get (vcpu, ®s); + regs.rflags = regs.rflags |0x2 ; + regs.rsp =kvm_allocate_vcpu_stack (vm); +vcpu_regs_set (vcpu, ®s); + }/* Setup the MP state */ mp_state.mp_state =0 ;
Signature · 1 line
2.55.0.229.g6434b31f56-goog
Re: [PATCH v14 15/22] KVM: selftests: Call KVM_TDX_INIT_VCPU when creating a new TDX vcpu
Quoted 6 lines · Lisa Wang
From: Sagi Shahar <[email protected]>
TDX VMs need to issue the
KVM_TDX_INIT_VCPUioctl for each vcpu after vcpu creation.Since the cpuids for TD are managed by the TDX module, read the values
Please use CPUIDs instead of cpuids.
Quoted 2 lines
virtualized for the TD using
KVM_TDX_GET_CPUIDand set them in kvm usingKVM_SET_CPUID2so that kvm has an accurate view of the VM cpuid values.
This does two things, and KVM_SET_CPUID2 isn't a hard requirement for KVM_TDX_INIT_VCPU. we'd better split it into two patches.
Quoted 25 lines
Signed-off-by Sagi Shahar <[email protected]>Signed-off-by Lisa Wang <[email protected]>--- .../selftests/kvm/include/x86/tdx/tdx_util.h | 21 ++++++++++++++ tools/testing/selftests/kvm/lib/x86/processor.c | 33 ++++++++++++++++------ 2 files changed, 46 insertions(+), 8 deletions(-)tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h +10
-0@@ -44,6 +44,27 @@ static inline bool is_tdx_vm(struct kvm_vm *vm) } \ }) +#define __tdx_vcpu_ioctl (vcpu, cmd, _flags, arg) \ +({ \ + union { \ + struct kvm_tdx_cmd c; \ + unsigned long raw; \ + } tdx_cmd = { .c = { \ + .id = (cmd), \ + .flags = (u32)(_flags), \ + .data = (u64)(arg), \ + } }; \
tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h static inline bool is_tdx_vm(struct kvm_vm *vm) · line 66
Quoted 10 lines
tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h +10
-0+ \ +__vcpu_ioctl (vcpu,KVM_MEMORY_ENCRYPT_OP , &tdx_cmd.raw); \ +}) + +#define tdx_vcpu_ioctl (vcpu, cmd, flags, arg) \ +({ \ + int ret = __tdx_vcpu_ioctl(vcpu, cmd, flags, arg); \ + TEST_ASSERT(!ret, "%s failed, errno: %d (%s)", \ + #cmd, errno, strerror(errno)); \ +})
It doesn't handle the tdx_cmd.c.hw_error for vcpu ioctl, but handle it for vm ioctl, which looks inconsistent. This at least deserves a justification in the changelog.
Quoted 8 lines
tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h +0
-0void tdx_init_vm (struct kvm_vm *vm,u64 attributes);void tdx_vm_setup_boot_code_region (struct kvm_vm *vm);void tdx_vm_setup_boot_parameters_region (struct kvm_vm *vm,u32 nr_runnable_vcpus);tools/testing/selftests/kvm/lib/x86/processor.c +0
-0
tools/testing/selftests/kvm/lib/x86/processor.c gva_t kvm_allocate_vcpu_stack(struct kvm_vm *vm) · line 846
Quoted 9 lines
tools/testing/selftests/kvm/lib/x86/processor.c +6
-0@@ -838,6 +838,17 @@ gva_t kvm_allocate_vcpu_stack(struct kvm_vm *vm)return stack_gva; } +static void tdx_vcpu_init (struct kvm_vm *vm,struct kvm_vcpu *vcpu) +{ +struct kvm_cpuid2 *cpuid; + + cpuid =allocate_kvm_cpuid2 (MAX_NR_CPUID_ENTRIES ); +tdx_vcpu_ioctl (vcpu,KVM_TDX_GET_CPUID ,0 , cpuid);
If I remember correctly, though KVM_TDX_GET_CPUID is a vcpu ioctl, the data returned by KVM is retrived from TDX module and TDX module only maintains the TD scope CPUID. So the CPUID returned here is TD scope, for per-vcpu CPUIDs, e.g., x2apicid, we need to update them accordingly.
This seems not a functional gap. I'm OK to leave it to the future, but please leave a TODO comment for it.
Quoted 39 lines
tools/testing/selftests/kvm/lib/x86/processor.c +19
-8+vcpu_init_cpuid (vcpu, cpuid); +free (cpuid); +tdx_vcpu_ioctl (vcpu,KVM_TDX_INIT_VCPU ,0 ,NULL ); +} +struct kvm_vcpu *vm_arch_vcpu_add (struct kvm_vm *vm,u32 vcpu_id) {struct kvm_mp_state mp_state;@@ -845,15 +856,21 @@ struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, u32 vcpu_id)struct kvm_regs regs; vcpu =__vm_vcpu_add (vm, vcpu_id); -vcpu_init_cpuid (vcpu,kvm_get_supported_cpuid ()); -vcpu_init_sregs (vm, vcpu); -vcpu_init_xcrs (vm, vcpu); -/* Setup guest general purpose registers */ -vcpu_regs_get (vcpu, ®s); - regs.rflags = regs.rflags |0x2 ; - regs.rsp =kvm_allocate_vcpu_stack (vm); -vcpu_regs_set (vcpu, ®s); +if (is_tdx_vm (vm)) { +tdx_vcpu_init (vm, vcpu); + }else { +vcpu_init_cpuid (vcpu,kvm_get_supported_cpuid ()); + +vcpu_init_sregs (vm, vcpu); +vcpu_init_xcrs (vm, vcpu); + +/* Setup guest general purpose registers */ +vcpu_regs_get (vcpu, ®s); + regs.rflags = regs.rflags |0x2 ; + regs.rsp =kvm_allocate_vcpu_stack (vm); +vcpu_regs_set (vcpu, ®s); + }/* Setup the MP state */ mp_state.mp_state =0 ;
Re: [PATCH v14 15/22] KVM: selftests: Call KVM_TDX_INIT_VCPU when creating a new TDX vcpu
On 8/14/2026 4:32 PM, Xiaoyao Li wrote:
[...]
Quoted 8 lines
tools/testing/selftests/kvm/lib/x86/processor.c gva_t kvm_allocate_vcpu_stack(struct kvm_vm *vm) · line 846
Quoted 6 lines
+static void
tdx_vcpu_init(struct kvm_vm *vm, struct kvm_vcpu *vcpu)tools/testing/selftests/kvm/lib/x86/processor.c +5
-0+{ + struct kvm_cpuid2 *cpuid; + + cpuid = allocate_kvm_cpuid2(MAX_NR_CPUID_ENTRIES); + tdx_vcpu_ioctl(vcpu, KVM_TDX_GET_CPUID, 0, cpuid);If I remember correctly, though
KVM_TDX_GET_CPUIDis a vcpu ioctl, the data returned by KVM is retrived from TDX module and TDX module only maintains the TD scope CPUID.
KVM_TDX_GET_CPUID does return TD-scoped CPUID metadata only.
Quoted 4 lines
So the CPUID returned here is TD scope, for per-vcpu CPUIDs, e.g., x2apicid, we need to update them accordingly.
This seems not a functional gap. I'm OK to leave it to the future, but please leave a TODO comment for it.
The handling for legacy VMs is similar, i.e. kvm_get_supported_cpuid()also has no per-vcpu CPUID info like APIC ID. It seems that KVM selftests doesn't test CPUID topology enumeration.
A comment could be helpful, although it may not be a TODO comment.
Re: [PATCH v14 15/22] KVM: selftests: Call KVM_TDX_INIT_VCPU when creating a new TDX vcpu
In reply to Xiaoyao LiQuoted 18 lines · Xiaoyao Li
Quoted 18 lines
} \ })tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h +16
-0+#define __tdx_vcpu_ioctl (vcpu, cmd, _flags, arg) \ +({ \ + union { \ + struct kvm_tdx_cmd c; \ + unsigned long raw; \ + } tdx_cmd = { .c = { \ + .id = (cmd), \ + .flags = (u32)(_flags), \ + .data = (u64)(arg), \ + } }; \ + \ + __vcpu_ioctl(vcpu, KVM_MEMORY_ENCRYPT_OP, &tdx_cmd.raw); \ +}) + +#define tdx_vcpu_ioctl (vcpu, cmd, flags, arg) \ +({ \
Quoted 8 lines
tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h static inline bool is_tdx_vm(struct kvm_vm *vm) · line 66
Quoted 4 lines
tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h +4
-0+int ret =__tdx_vcpu_ioctl (vcpu, cmd, flags, arg); \ + TEST_ASSERT(!ret, "%s failed, errno: %d (%s)", \ +#cmd , errno, strerror(errno)); \ +})It doesn't handle the tdx_cmd.c.hw_error for vcpu ioctl, but handle it for vm ioctl, which looks inconsistent. This at least deserves a justification in the changelog.
Looks like this assymetry came from a v13 comment [1].
tdx_vcpu_ioctl() actually doesn't write back hw_error to userspace like tdx_vm_ioctl() does, so hw_error is vCPU-scoped only right now. This is a bit subtle so probably deserves to be called out.
[1] https://lore.kernel.org/all/[email protected]/
Quoted 1 line
PATCH v14 16/22 KVM: selftests: Load per-vCPU guest stack in TDX boot parameters
In reply to Lisa WangFrom: Sagi Shahar <[email protected]>
Allocate a guest stack for each vCPU and record the GVA in the TDX boot parameters region to allow proper vCPU initialization.
--- tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h | 1 + tools/testing/selftests/kvm/lib/x86/processor.c | 2 ++ tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c | 10 ++++++++++ 3 files changed, 13 insertions(+)
tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h +1 -0
@@ -69,5 +69,6 @@ void tdx_init_vm(struct kvm_vm *vm, u64 attributes);void tdx_vm_setup_boot_code_region (struct kvm_vm *vm);void tdx_vm_setup_boot_parameters_region (struct kvm_vm *vm,u32 nr_runnable_vcpus);void tdx_vm_load_common_boot_parameters (struct kvm_vm *vm); +void tdx_vcpu_load_boot_parameters (struct kvm_vm *vm,struct kvm_vcpu *vcpu);#endif /* SELFTESTS_TDX_TDX_UTIL_H */
tools/testing/selftests/kvm/lib/x86/processor.c +2 -0
@@ -847,6 +847,8 @@ static void tdx_vcpu_init(struct kvm_vm *vm, struct kvm_vcpu *vcpu)vcpu_init_cpuid (vcpu, cpuid);free (cpuid);tdx_vcpu_ioctl (vcpu,KVM_TDX_INIT_VCPU ,0 ,NULL ); + +tdx_vcpu_load_boot_parameters (vm, vcpu); }struct kvm_vcpu *vm_arch_vcpu_add (struct kvm_vm *vm,u32 vcpu_id)
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +10 -0
@@ -104,6 +104,16 @@ void tdx_vm_load_common_boot_parameters(struct kvm_vm *vm)TEST_ASSERT (params->idtr.base !=0 ,"idt base address should not be 0" ); } +void tdx_vcpu_load_boot_parameters (struct kvm_vm *vm,struct kvm_vcpu *vcpu) +{ +struct td_boot_parameters *params = +addr_gpa2hva (vm,TD_BOOT_PARAMETERS_GPA ); +struct td_per_vcpu_parameters *vcpu_params = + ¶ms->per_vcpu[vcpu->id]; + + vcpu_params->esp_gva =kvm_allocate_vcpu_stack (vm); +} +static struct kvm_tdx_capabilities *tdx_read_capabilities (struct kvm_vm *vm) {static struct kvm_tdx_capabilities *tdx_cap;
Signature · 1 line
2.55.0.229.g6434b31f56-goog
Re: [PATCH v14 16/22] KVM: selftests: Load per-vCPU guest stack in TDX boot parameters
Quoted 10 lines · Lisa Wang
From: Sagi Shahar <[email protected]>
Allocate a guest stack for each vCPU and record the GVA in the TDX boot parameters region to allow proper vCPU initialization.
Co-developed-by Ackerley Tng <[email protected]>Signed-off-by Ackerley Tng <[email protected]>Signed-off-by Sagi Shahar <[email protected]>Signed-off-by Lisa Wang <[email protected]>Reviewed-by Binbin Wu <[email protected]>
Quoted 52 lines
--- tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h | 1 + tools/testing/selftests/kvm/lib/x86/processor.c | 2 ++ tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c | 10 ++++++++++ 3 files changed, 13 insertions(+)tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h +1
-0@@ -69,5 +69,6 @@ void tdx_init_vm(struct kvm_vm *vm, u64 attributes);void tdx_vm_setup_boot_code_region (struct kvm_vm *vm);void tdx_vm_setup_boot_parameters_region (struct kvm_vm *vm,u32 nr_runnable_vcpus);void tdx_vm_load_common_boot_parameters (struct kvm_vm *vm); +void tdx_vcpu_load_boot_parameters (struct kvm_vm *vm,struct kvm_vcpu *vcpu);#endif /* SELFTESTS_TDX_TDX_UTIL_H */ tools/testing/selftests/kvm/lib/x86/processor.c +2
-0@@ -847,6 +847,8 @@ static void tdx_vcpu_init(struct kvm_vm *vm, struct kvm_vcpu *vcpu)vcpu_init_cpuid (vcpu, cpuid);free (cpuid);tdx_vcpu_ioctl (vcpu,KVM_TDX_INIT_VCPU ,0 ,NULL ); + +tdx_vcpu_load_boot_parameters (vm, vcpu); }struct kvm_vcpu *vm_arch_vcpu_add (struct kvm_vm *vm,u32 vcpu_id)tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +10
-0@@ -104,6 +104,16 @@ void tdx_vm_load_common_boot_parameters(struct kvm_vm *vm)TEST_ASSERT (params->idtr.base !=0 ,"idt base address should not be 0" ); } +void tdx_vcpu_load_boot_parameters (struct kvm_vm *vm,struct kvm_vcpu *vcpu) +{ +struct td_boot_parameters *params = +addr_gpa2hva (vm,TD_BOOT_PARAMETERS_GPA ); +struct td_per_vcpu_parameters *vcpu_params = + ¶ms->per_vcpu[vcpu->id]; + + vcpu_params->esp_gva =kvm_allocate_vcpu_stack (vm); +} +static struct kvm_tdx_capabilities *tdx_read_capabilities (struct kvm_vm *vm) {static struct kvm_tdx_capabilities *tdx_cap;
Re: [PATCH v14 16/22] KVM: selftests: Load per-vCPU guest stack in TDX boot parameters
In reply to Lisa WangQuoted 16 lines · Lisa Wang
From: Sagi Shahar <[email protected]>
Allocate a guest stack for each vCPU and record the GVA in the TDX boot parameters region to allow proper vCPU initialization.
Co-developed-by Ackerley Tng <[email protected]>Signed-off-by Ackerley Tng <[email protected]>Signed-off-by Sagi Shahar <[email protected]>Signed-off-by Lisa Wang <[email protected]>Reviewed-by Binbin Wu <[email protected]>--- tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h | 1 + tools/testing/selftests/kvm/lib/x86/processor.c | 2 ++ tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c | 10 ++++++++++ 3 files changed, 13 insertions(+)
[ ... ]
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c void tdx_vm_load_common_boot_parameters(struct kvm_vm *vm) · line 114
Quoted 9 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +8
-0+void tdx_vcpu_load_boot_parameters (struct kvm_vm *vm,struct kvm_vcpu *vcpu) +{ +struct td_boot_parameters *params = +addr_gpa2hva (vm,TD_BOOT_PARAMETERS_GPA ); +struct td_per_vcpu_parameters *vcpu_params = + ¶ms->per_vcpu[vcpu->id]; + + vcpu_params->esp_gva =kvm_allocate_vcpu_stack (vm);
Just flagging that there is a subtle truncation here. The TDX boot code sets esp in 32-bit mode so it has the same visibility constraint as CR3, which has an explicit check ("vm->mmu.pgd < (1ULL << 32)").
Quoted 10 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +2
-0+} +static struct kvm_tdx_capabilities *tdx_read_capabilities (struct kvm_vm *vm) {static struct kvm_tdx_capabilities *tdx_cap;Signature · 2 lines
-- 2.55.0.229.g6434b31f56-goog
PATCH v14 17/22 KVM: selftests: Set entry point for TDX guest code
In reply to Lisa WangFrom: Sagi Shahar <[email protected]>
Since the rip register is inaccessible for TDX VMs, we need a different way to set the guest entry point for TDX VMs. This is done by writing the guest code address to a predefined location in the guest memory and loading it into rip as part of the TDX boot code.
--- tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h | 1 + tools/testing/selftests/kvm/lib/x86/processor.c | 10 +++++++--- tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c | 10 ++++++++++ 3 files changed, 18 insertions(+), 3 deletions(-)
tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h +1 -0
@@ -70,5 +70,6 @@ void tdx_vm_setup_boot_code_region(struct kvm_vm *vm);void tdx_vm_setup_boot_parameters_region (struct kvm_vm *vm,u32 nr_runnable_vcpus);void tdx_vm_load_common_boot_parameters (struct kvm_vm *vm);void tdx_vcpu_load_boot_parameters (struct kvm_vm *vm,struct kvm_vcpu *vcpu); +void tdx_vcpu_set_entry_point (struct kvm_vcpu *vcpu,void *guest_code);#endif /* SELFTESTS_TDX_TDX_UTIL_H */
tools/testing/selftests/kvm/lib/x86/processor.c +7 -3
@@ -808,9 +808,13 @@ void vcpu_arch_set_entry_point(struct kvm_vcpu *vcpu, void *guest_code) {struct kvm_regs regs; -vcpu_regs_get (vcpu, ®s); - regs.rip = (unsigned long ) guest_code; -vcpu_regs_set (vcpu, ®s); +if (is_tdx_vm (vcpu->vm)) { +tdx_vcpu_set_entry_point (vcpu, guest_code); + }else { +vcpu_regs_get (vcpu, ®s); + regs.rip = (unsigned long )guest_code; +vcpu_regs_set (vcpu, ®s); + } }gva_t kvm_allocate_vcpu_stack (struct kvm_vm *vm)
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +10 -0
@@ -114,6 +114,16 @@ void tdx_vcpu_load_boot_parameters(struct kvm_vm *vm, struct kvm_vcpu *vcpu) vcpu_params->esp_gva =kvm_allocate_vcpu_stack (vm); } +void tdx_vcpu_set_entry_point (struct kvm_vcpu *vcpu,void *guest_code) +{ +struct td_boot_parameters *params = +addr_gpa2hva (vcpu->vm,TD_BOOT_PARAMETERS_GPA ); +struct td_per_vcpu_parameters *vcpu_params = + ¶ms->per_vcpu[vcpu->id]; + + vcpu_params->guest_code = (u64 )guest_code; +} +static struct kvm_tdx_capabilities *tdx_read_capabilities (struct kvm_vm *vm) {static struct kvm_tdx_capabilities *tdx_cap;
Signature · 1 line
2.55.0.229.g6434b31f56-goog
Re: [PATCH v14 17/22] KVM: selftests: Set entry point for TDX guest code
Quoted 9 lines · Lisa Wang
From: Sagi Shahar <[email protected]>
Since the rip register is inaccessible for TDX VMs, we need a different way to set the guest entry point for TDX VMs. This is done by writing the guest code address to a predefined location in the guest memory and loading it into rip as part of the TDX boot code.
Signed-off-by Sagi Shahar <[email protected]>Signed-off-by Lisa Wang <[email protected]>
Quoted 60 lines
--- tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h | 1 + tools/testing/selftests/kvm/lib/x86/processor.c | 10 +++++++--- tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c | 10 ++++++++++ 3 files changed, 18 insertions(+), 3 deletions(-)tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h +1
-0@@ -70,5 +70,6 @@ void tdx_vm_setup_boot_code_region(struct kvm_vm *vm);void tdx_vm_setup_boot_parameters_region (struct kvm_vm *vm,u32 nr_runnable_vcpus);void tdx_vm_load_common_boot_parameters (struct kvm_vm *vm);void tdx_vcpu_load_boot_parameters (struct kvm_vm *vm,struct kvm_vcpu *vcpu); +void tdx_vcpu_set_entry_point (struct kvm_vcpu *vcpu,void *guest_code);#endif /* SELFTESTS_TDX_TDX_UTIL_H */ tools/testing/selftests/kvm/lib/x86/processor.c +7
-3@@ -808,9 +808,13 @@ void vcpu_arch_set_entry_point(struct kvm_vcpu *vcpu, void *guest_code) {struct kvm_regs regs; -vcpu_regs_get (vcpu, ®s); - regs.rip = (unsigned long ) guest_code; -vcpu_regs_set (vcpu, ®s); +if (is_tdx_vm (vcpu->vm)) { +tdx_vcpu_set_entry_point (vcpu, guest_code); + }else { +vcpu_regs_get (vcpu, ®s); + regs.rip = (unsigned long )guest_code; +vcpu_regs_set (vcpu, ®s); + } }gva_t kvm_allocate_vcpu_stack (struct kvm_vm *vm)tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +10
-0@@ -114,6 +114,16 @@ void tdx_vcpu_load_boot_parameters(struct kvm_vm *vm, struct kvm_vcpu *vcpu) vcpu_params->esp_gva =kvm_allocate_vcpu_stack (vm); } +void tdx_vcpu_set_entry_point (struct kvm_vcpu *vcpu,void *guest_code) +{ +struct td_boot_parameters *params = +addr_gpa2hva (vcpu->vm,TD_BOOT_PARAMETERS_GPA ); +struct td_per_vcpu_parameters *vcpu_params = + ¶ms->per_vcpu[vcpu->id]; + + vcpu_params->guest_code = (u64 )guest_code; +} +static struct kvm_tdx_capabilities *tdx_read_capabilities (struct kvm_vm *vm) {static struct kvm_tdx_capabilities *tdx_cap;
Re: [PATCH v14 17/22] KVM: selftests: Set entry point for TDX guest code
In reply to Lisa WangQuoted 9 lines · Lisa Wang
From: Sagi Shahar <[email protected]>
Since the rip register is inaccessible for TDX VMs, we need a different way to set the guest entry point for TDX VMs. This is done by writing the guest code address to a predefined location in the guest memory and loading it into rip as part of the TDX boot code.
Signed-off-by Sagi Shahar <[email protected]>Signed-off-by Lisa Wang <[email protected]>
PATCH v14 18/22 KVM: selftests: Add helpers to init TDX memory and finalize VM
In reply to Lisa WangFrom: Ackerley Tng <[email protected]>
TDX protected memory needs to be measured and encrypted before it can be used by the guest. Traverse the VM's memory regions and initialize all the protected ranges by calling KVM_TDX_INIT_MEM_REGION.
Once all the memory is initialized, the VM can be finalized by calling KVM_TDX_FINALIZE_VM.
--- .../selftests/kvm/include/x86/tdx/tdx_util.h | 2 + tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c | 63 ++++++++++++++++++++++ 2 files changed, 65 insertions(+)
tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h +2 -0
@@ -72,4 +72,6 @@ void tdx_vm_load_common_boot_parameters(struct kvm_vm *vm);void tdx_vcpu_load_boot_parameters (struct kvm_vm *vm,struct kvm_vcpu *vcpu);void tdx_vcpu_set_entry_point (struct kvm_vcpu *vcpu,void *guest_code); +void tdx_vm_finalize (struct kvm_vm *vm); +#endif /* SELFTESTS_TDX_TDX_UTIL_H */
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +63 -0
71 lines not on this page · raw message
Signature · 1 line
2.55.0.229.g6434b31f56-goog
Re: [PATCH v14 18/22] KVM: selftests: Add helpers to init TDX memory and finalize VM
Quoted 75 lines · Lisa Wang
From: Ackerley Tng <[email protected]>
TDX protected memory needs to be measured and encrypted before it can be used by the guest. Traverse the VM's memory regions and initialize all the protected ranges by calling
KVM_TDX_INIT_MEM_REGION.Once all the memory is initialized, the VM can be finalized by calling
KVM_TDX_FINALIZE_VM.Signed-off-by Ackerley Tng <[email protected]>Co-developed-by Erdem Aktas <[email protected]>Signed-off-by Erdem Aktas <[email protected]>Co-developed-by Sagi Shahar <[email protected]>Signed-off-by Sagi Shahar <[email protected]>Reviewed-by Binbin Wu <[email protected]>Reviewed-by Ira Weiny <[email protected]>Co-developed-by Lisa Wang <[email protected]>Signed-off-by Lisa Wang <[email protected]>--- .../selftests/kvm/include/x86/tdx/tdx_util.h | 2 + tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c | 63 ++++++++++++++++++++++ 2 files changed, 65 insertions(+)tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h +2
-0@@ -72,4 +72,6 @@ void tdx_vm_load_common_boot_parameters(struct kvm_vm *vm);void tdx_vcpu_load_boot_parameters (struct kvm_vm *vm,struct kvm_vcpu *vcpu);void tdx_vcpu_set_entry_point (struct kvm_vcpu *vcpu,void *guest_code); +void tdx_vm_finalize (struct kvm_vm *vm); +#endif /* SELFTESTS_TDX_TDX_UTIL_H */ tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +27
-0@@ -1,5 +1,7 @@// SPDX-License-Identifier: GPL-2.0-only +#include <linux/align.h> +#include "processor.h" #include "tdx/td_boot.h" #include "tdx/tdx_util.h" @@ -251,3 +253,64 @@ void tdx_init_vm(struct kvm_vm *vm, u64 attributes)free (init_vm); } + +static void tdx_init_mem_region (struct kvm_vm *vm,void *source_pages, +u64 gpa,u64 size) +{ +u32 flags =KVM_TDX_MEASURE_MEMORY_REGION ; +struct kvm_tdx_init_mem_region mem_region = { + .source_addr = (u64 )source_pages, + .gpa = gpa, + .nr_pages = size /PAGE_SIZE , + }; +struct kvm_vcpu *vcpu; + + vcpu = list_first_entry_or_null(&vm->vcpus, struct kvm_vcpu, list); + +TEST_ASSERT (size &&IS_ALIGNED (size,PAGE_SIZE ), +"Cannot add partial pages to the guest memory.\n" ); +TEST_ASSERT (IS_ALIGNED ((u64 )source_pages,PAGE_SIZE ), +"Source memory buffer is not page aligned\n" ); +tdx_vcpu_ioctl (vcpu,KVM_TDX_INIT_MEM_REGION , flags, &mem_region); +} + +static void tdx_load_private_memory (struct kvm_vm *vm) +{ +struct userspace_mem_region *region; +int ctr;
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c void tdx_init_vm(struct kvm_vm *vm, u64 attributes) · line 290
Quoted 10 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +10
-0+ +hash_for_each (vm->regions.slot_hash, ctr, region, slot_node) { +const struct sparsebit *protected_pages = region->protected_phy_pages; +const gpa_t gpa_base = region->region.guest_phys_addr; +const u64 hva_base = region->region.userspace_addr; +const sparsebit_idx_t lowest_page_in_region = gpa_base >> vm->page_shift; +void *source_pages =NULL ; +sparsebit_idx_t i, j; + +if (!sparsebit_any_set (protected_pages))
sparebit_any_set() doens't check if the input is NULL. So we need to check it here.
Quoted 7 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +7
-0+continue ; + +TEST_ASSERT (region->region.guest_memfd !=-1 , +"TD private memory must be backed by guest_memfd" ); + +sparsebit_for_each_set_range (protected_pages, i, j) { +const u64 size_to_load = (j - i +1 ) * vm->page_size;
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c void tdx_init_vm(struct kvm_vm *vm, u64 attributes) · line 306
Quoted 10 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +9
-0+const u64 offset = + (i - lowest_page_in_region) * vm->page_size; +const u64 hva = hva_base + offset; +const u64 gpa = gpa_base + offset; + +if (!kvm_has_gmem_attributes) + source_pages = (void *)hva; + +vm_mem_set_private (vm, gpa, size_to_load);
So vm_mem_set_private() has to be called at this late stage when run with in-place gmem. But for non in-place gmem, we can actually call vm_mem_set_private() in __vm_phy_pages_alloc().
Calling vm_mem_set_private() here instead of in __vm_phy_pages_alloc() looks like a trick to me. That is, we cannot set the page as private when allocating a guest physical page as protected because if doing so, we cannot write the initial content to it.
This is the topic about how to implement the infras for in-place gmem, not the issue of this series. Let me go read the selftest patches of gmem in-place series and we can discuss there.
Quoted 11 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +10
-0+tdx_init_mem_region (vm, source_pages, gpa, size_to_load); + } + } +} + +void tdx_vm_finalize (struct kvm_vm *vm) +{ +tdx_load_private_memory (vm); +tdx_vm_ioctl (vm,KVM_TDX_FINALIZE_VM ,0 ,NULL ); +}
Re: [PATCH v14 18/22] KVM: selftests: Add helpers to init TDX memory and finalize VM
Quoted 37 lines · Xiaoyao Li
[...snip...]
Quoted 4 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +4
-0+static void tdx_load_private_memory (struct kvm_vm *vm) +{ +struct userspace_mem_region *region; +int ctr;tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c void tdx_init_vm(struct kvm_vm *vm, u64 attributes) · line 290
Quoted 10 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +10
-0+ +hash_for_each (vm->regions.slot_hash, ctr, region, slot_node) { +const struct sparsebit *protected_pages = region->protected_phy_pages; +const gpa_t gpa_base = region->region.guest_phys_addr; +const u64 hva_base = region->region.userspace_addr; +const sparsebit_idx_t lowest_page_in_region = gpa_base >> vm->page_shift; +void *source_pages =NULL ; +sparsebit_idx_t i, j; + +if (!sparsebit_any_set (protected_pages))
sparebit_any_set()doens't check if the input is NULL. So we need to check it here.Quoted 15 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +15
-0+continue ; + +TEST_ASSERT (region->region.guest_memfd !=-1 , +"TD private memory must be backed by guest_memfd" ); + +sparsebit_for_each_set_range (protected_pages, i, j) { +const u64 size_to_load = (j - i +1 ) * vm->page_size; +const u64 offset = + (i - lowest_page_in_region) * vm->page_size; +const u64 hva = hva_base + offset; +const u64 gpa = gpa_base + offset; + +if (!kvm_has_gmem_attributes) + source_pages = (void *)hva; +
Quoted 8 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c void tdx_init_vm(struct kvm_vm *vm, u64 attributes) · line 306
Quoted 1 line
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +1
-0+vm_mem_set_private (vm, gpa, size_to_load);So
vm_mem_set_private()has to be called at this late stage when run with in-place gmem. But for non in-place gmem, we can actually callvm_mem_set_private()in__vm_phy_pages_alloc().Calling
vm_mem_set_private()here instead of in__vm_phy_pages_alloc()looks like a trick to me.
I thought this is fine because __vm_phy_pages_alloc() seems to be a rather low-level function, where the responsibility of the function is just to allocate (for find some physical pages). Calling vm_mem_set_private() in there seems to be doing too much.
Quoted 4 lines
That is, we cannot set the page as private
when allocating a guest physical page as protected because if doing so, we cannot write the initial content to it.
I think calling it here isn't a trick, it's a good way to reuse all the existing code that builds up the guest image in place. In-place conversion allows you to set stuff up in shared memory and then convert everything when you're done and also populate the memory.
Quoted 15 lines
This is the topic about how to implement the infras for in-place gmem, not the issue of this series. Let me go read the selftest patches of gmem in-place series and we can discuss there.
Quoted 11 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +10
-0+tdx_init_mem_region (vm, source_pages, gpa, size_to_load); + } + } +} + +void tdx_vm_finalize (struct kvm_vm *vm) +{ +tdx_load_private_memory (vm); +tdx_vm_ioctl (vm,KVM_TDX_FINALIZE_VM ,0 ,NULL ); +}
Re: [PATCH v14 18/22] KVM: selftests: Add helpers to init TDX memory and finalize VM
Quoted 45 lines · Ackerley Tng
Quoted 43 lines · Xiaoyao Li
[...snip...]
Quoted 4 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +4
-0+static void tdx_load_private_memory (struct kvm_vm *vm) +{ +struct userspace_mem_region *region; +int ctr;tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c void tdx_init_vm(struct kvm_vm *vm, u64 attributes) · line 290
Quoted 10 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +10
-0+ +hash_for_each (vm->regions.slot_hash, ctr, region, slot_node) { +const struct sparsebit *protected_pages = region->protected_phy_pages; +const gpa_t gpa_base = region->region.guest_phys_addr; +const u64 hva_base = region->region.userspace_addr; +const sparsebit_idx_t lowest_page_in_region = gpa_base >> vm->page_shift; +void *source_pages =NULL ; +sparsebit_idx_t i, j; + +if (!sparsebit_any_set (protected_pages))
sparebit_any_set()doens't check if the input is NULL. So we need to check it here.Quoted 7 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +7
-0+continue ; + +TEST_ASSERT (region->region.guest_memfd !=-1 , +"TD private memory must be backed by guest_memfd" ); + +sparsebit_for_each_set_range (protected_pages, i, j) { +const u64 size_to_load = (j - i +1 ) * vm->page_size;tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c void tdx_init_vm(struct kvm_vm *vm, u64 attributes) · line 306
Quoted 10 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +9
-0+const u64 offset = + (i - lowest_page_in_region) * vm->page_size; +const u64 hva = hva_base + offset; +const u64 gpa = gpa_base + offset; + +if (!kvm_has_gmem_attributes) + source_pages = (void *)hva; + +vm_mem_set_private (vm, gpa, size_to_load);So
vm_mem_set_private()has to be called at this late stage when run with in-place gmem. But for non in-place gmem, we can actually callvm_mem_set_private()in__vm_phy_pages_alloc().
Quoted 7 lines
Quoted 2 lines
Calling
vm_mem_set_private()here instead of in__vm_phy_pages_alloc()looks like a trick to me.I thought this is fine because
__vm_phy_pages_alloc()seems to be a rather low-level function, where the responsibility of the function is just to allocate (for find some physical pages). Callingvm_mem_set_private()in there seems to be doing too much.
__vm_phy_pages_alloc() takes a parameter @protected, which is used to tell the allocated physical pages need to be protected(private) or not. It looks weird that a page is allocated as protected but actually it is still shared.
Quoted 9 lines
Quoted 4 lines
That is, we cannot set the page as private
when allocating a guest physical page as protected because if doing so, we cannot write the initial content to it.I think calling it here isn't a trick, it's a good way to reuse all the existing code that builds up the guest image in place. In-place conversion allows you to set stuff up in shared memory and then convert everything when you're done and also populate the memory.
This is based on the assumption of "in-place conversion". However the TDX selftests should be able to run without "in-place conversion". There is no hard dependency on it.
I think this patch implements what patch 13[1] of this series says "For CoCo VMs, pages that need to be private are explicitly set to private before executing the VM."
But all of this is for the case of "in-place conversion". When "in-place conversion" is not supported/enabled by the kernel, GUEST_MEMFD_FLAG_INIT_SHARED for Coco VMs is *not* allowed and all the memory are private by default and no need to call vm_mem_set_private() here at all.
[1] https://lore.kernel.org/all/[email protected]/
Quoted 15 lines
Quoted 15 lines
This is the topic about how to implement the infras for in-place gmem, not the issue of this series. Let me go read the selftest patches of gmem in-place series and we can discuss there.
Quoted 11 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +10
-0+tdx_init_mem_region (vm, source_pages, gpa, size_to_load); + } + } +} + +void tdx_vm_finalize (struct kvm_vm *vm) +{ +tdx_load_private_memory (vm); +tdx_vm_ioctl (vm,KVM_TDX_FINALIZE_VM ,0 ,NULL ); +}
Re: [PATCH v14 18/22] KVM: selftests: Add helpers to init TDX memory and finalize VM
Quoted 54 lines · Xiaoyao Li
Quoted 45 lines · Ackerley Tng
Quoted 43 lines · Xiaoyao Li
[...snip...]
Quoted 4 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +4
-0+static void tdx_load_private_memory (struct kvm_vm *vm) +{ +struct userspace_mem_region *region; +int ctr;tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c void tdx_init_vm(struct kvm_vm *vm, u64 attributes) · line 290
Quoted 10 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +10
-0+ +hash_for_each (vm->regions.slot_hash, ctr, region, slot_node) { +const struct sparsebit *protected_pages = region->protected_phy_pages; +const gpa_t gpa_base = region->region.guest_phys_addr; +const u64 hva_base = region->region.userspace_addr; +const sparsebit_idx_t lowest_page_in_region = gpa_base >> vm->page_shift; +void *source_pages =NULL ; +sparsebit_idx_t i, j; + +if (!sparsebit_any_set (protected_pages))
sparebit_any_set()doens't check if the input is NULL. So we need to check it here.Quoted 7 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +7
-0+continue ; + +TEST_ASSERT (region->region.guest_memfd !=-1 , +"TD private memory must be backed by guest_memfd" ); + +sparsebit_for_each_set_range (protected_pages, i, j) { +const u64 size_to_load = (j - i +1 ) * vm->page_size;tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c void tdx_init_vm(struct kvm_vm *vm, u64 attributes) · line 306
Quoted 10 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +9
-0+const u64 offset = + (i - lowest_page_in_region) * vm->page_size; +const u64 hva = hva_base + offset; +const u64 gpa = gpa_base + offset; + +if (!kvm_has_gmem_attributes) + source_pages = (void *)hva; + +vm_mem_set_private (vm, gpa, size_to_load);So
vm_mem_set_private()has to be called at this late stage when run with in-place gmem. But for non in-place gmem, we can actually callvm_mem_set_private()in__vm_phy_pages_alloc().Quoted 7 lines
Quoted 2 lines
Calling
vm_mem_set_private()here instead of in__vm_phy_pages_alloc()looks like a trick to me.I thought this is fine because
__vm_phy_pages_alloc()seems to be a rather low-level function, where the responsibility of the function is just to allocate (for find some physical pages). Callingvm_mem_set_private()in there seems to be doing too much.
Quoted 5 lines
__vm_phy_pages_alloc()takes a parameter @protected, which is used to tell the allocated physical pages need to be protected(private) or not. It looks weird that a page is allocated as protected but actually it is still shared.
I think there are a few different things here:
1. Finding some range of guest physical addresses for a page. This is __vm_phy_pages_alloc().
2. Tracking the guest physical addresses as shared or private in the userspace VMM. This is setting region->protected_phy_pages.
3. Creating private vs shared mappings in guest page tables. This is done in __virt_pg_map().
4. Transferring region->protected_phy_pages into the kernel. This is either the VM or guest_memfd conversion ioctl.
5. Loading private memory based on region->protected_phy_pages. This is done in tdx_load_private_memory() and the SNP counterpart.
So protected_phy_pages tracks all the pages that will be initialized as private, not the pages that are private (since vm_mem_set_private() doesn't update protected_phy_pages).
(2) is coupled to whether the VM has protected memory in vm_phy_pages_alloc(). This is kind of an awkward assumption since a CoCo VM can have shared memory too, and especially for selftests there can be shared pages right from the start.
However, setting protected_phy_pages in (2) and referencing in it (3),
(4) and (5) makes sense since that's the common path, and we want the common path to be easy for the selftest writer.
I think there could be some refactoring so that the different components 1 through 5 are opened up for easier usage but I haven't thought about how it should look like and anyway I think that belongs in a separate series.
Imo the direct response to looking allocated as protected but is actually shared is that the page was allocated as "will be loaded as private", and doesn't make any statement on whether it is shared or private.
Does seeing protected_phy_pages as "will be loaded as private" help in explaining why vm_mem_set_private() is not called in __vm_phy_pages_alloc() but is done only in tdx_load_private_memory()?
Quoted 18 lines
Quoted 9 lines
Quoted 4 lines
That is, we cannot set the page as private
when allocating a guest physical page as protected because if doing so, we cannot write the initial content to it.I think calling it here isn't a trick, it's a good way to reuse all the existing code that builds up the guest image in place. In-place conversion allows you to set stuff up in shared memory and then convert everything when you're done and also populate the memory.
This is based on the assumption of "in-place conversion". However the TDX selftests should be able to run without "in-place conversion". There is no hard dependency on it.
I think this patch implements what patch 13[1] of this series says "For CoCo VMs, pages that need to be private are explicitly set to private before executing the VM."
Quoted 9 lines
But all of this is for the case of "in-place conversion". When "in-place conversion" is not supported/enabled by the kernel,
GUEST_MEMFD_FLAG_INIT_SHAREDfor Coco VMs is *not* allowed and all the memory are private by default and no need to callvm_mem_set_private()here at all.
Doing (3) as part of (5) isn't just for in-place conversion. Without in-place conversion, source_pages should be the address mmap()-ed from some non-guest_memfd memory, which by the time of tdx_load_private_memory(), would already be populated with whatever needs to be loaded. Populate needs to be called on private memory regardless of whether we use in-place conversions.
Quoted 15 lines
Quoted 15 lines
Quoted 15 lines
This is the topic about how to implement the infras for in-place gmem, not the issue of this series. Let me go read the selftest patches of gmem in-place series and we can discuss there.
Quoted 11 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +10
-0+tdx_init_mem_region (vm, source_pages, gpa, size_to_load); + } + } +} + +void tdx_vm_finalize (struct kvm_vm *vm) +{ +tdx_load_private_memory (vm); +tdx_vm_ioctl (vm,KVM_TDX_FINALIZE_VM ,0 ,NULL ); +}
PATCH v14 19/22 KVM: selftests: Finalize TD memory as part of kvm_arch_vm_finalize_vcpus
In reply to Lisa WangFrom: Sagi Shahar <[email protected]>
Finalize TDX VM after creation to make it runnable.
--- tools/testing/selftests/kvm/lib/x86/processor.c | 6 ++++++ 1 file changed, 6 insertions(+)
tools/testing/selftests/kvm/lib/x86/processor.c +6 -0
@@ -1489,6 +1489,12 @@ bool kvm_arch_has_default_irqchip(void)return true; } +void kvm_arch_vm_finalize_vcpus (struct kvm_vm *vm) +{ +if (is_tdx_vm (vm)) +tdx_vm_finalize (vm); +} +void setup_smram (struct kvm_vm *vm,struct kvm_vcpu *vcpu,u64 smram_gpa,const void *smi_handler,size_t handler_size) {
Signature · 1 line
2.55.0.229.g6434b31f56-goog
Re: [PATCH v14 19/22] KVM: selftests: Finalize TD memory as part of kvm_arch_vm_finalize_vcpus
Quoted 3 lines · Lisa Wang
From: Sagi Shahar <[email protected]>
Finalize TDX VM after creation to make it runnable.
The changelog is too weak.
Quoted 26 lines
Signed-off-by Sagi Shahar <[email protected]>Reviewed-by Ira Weiny <[email protected]>Signed-off-by Lisa Wang <[email protected]>--- tools/testing/selftests/kvm/lib/x86/processor.c | 6 ++++++ 1 file changed, 6 insertions(+)tools/testing/selftests/kvm/lib/x86/processor.c +6
-0@@ -1489,6 +1489,12 @@ bool kvm_arch_has_default_irqchip(void)return true; } +void kvm_arch_vm_finalize_vcpus (struct kvm_vm *vm) +{ +if (is_tdx_vm (vm)) +tdx_vm_finalize (vm); +} +void setup_smram (struct kvm_vm *vm,struct kvm_vcpu *vcpu,u64 smram_gpa,const void *smi_handler,size_t handler_size) {
Re: [PATCH v14 19/22] KVM: selftests: Finalize TD memory as part of kvm_arch_vm_finalize_vcpus
Quoted 7 lines · Xiaoyao Li
Quoted 3 lines · Lisa Wang
From: Sagi Shahar <[email protected]>
Finalize TDX VM after creation to make it runnable.
The changelog is too weak.
Some other points I can think of (Xiaoyao, please add on!):
+ Why was kvm_arch_vm_finalize_vcpus() was picked as the lifecycle hook in which to finalize the VM? (See comments from the earlier revision from Sean) What are the rejected alternatives?
+ Expand on "make it runnable": finalizing also performs measurements
Just confirming: Peter, you meant to add the () at the end of kvm_arch_vm_finalize_vcpus in the commit subject, right?
Quoted 26 lines
Quoted 26 lines
Signed-off-by Sagi Shahar <[email protected]>Reviewed-by Ira Weiny <[email protected]>Signed-off-by Lisa Wang <[email protected]>--- tools/testing/selftests/kvm/lib/x86/processor.c | 6 ++++++ 1 file changed, 6 insertions(+)tools/testing/selftests/kvm/lib/x86/processor.c +6
-0@@ -1489,6 +1489,12 @@ bool kvm_arch_has_default_irqchip(void)return true; } +void kvm_arch_vm_finalize_vcpus (struct kvm_vm *vm) +{ +if (is_tdx_vm (vm)) +tdx_vm_finalize (vm); +} +void setup_smram (struct kvm_vm *vm,struct kvm_vcpu *vcpu,u64 smram_gpa,const void *smi_handler,size_t handler_size) {
Re: [PATCH v14 19/22] KVM: selftests: Finalize TD memory as part of kvm_arch_vm_finalize_vcpus
Quoted 12 lines · Ackerley Tng
Quoted 7 lines · Xiaoyao Li
Quoted 3 lines · Lisa Wang
From: Sagi Shahar <[email protected]>
Finalize TDX VM after creation to make it runnable.
The changelog is too weak.
Some other points I can think of (Xiaoyao, please add on!):
Quoted 7 lines
+ Why was
kvm_arch_vm_finalize_vcpus()was picked as the lifecycle hook in which to finalize the VM? (See comments from the earlier revision from Sean) What are the rejected alternatives?
+ Expand on "make it runnable": finalizing also performs measurementsJust confirming: Peter, you meant to add the () at the end of
kvm_arch_vm_finalize_vcpusin the commit subject, right?
Yep correct
Quoted 1 line
Re: [PATCH v14 19/22] KVM: selftests: Finalize TD memory as part of kvm_arch_vm_finalize_vcpus
In reply to Lisa WangNit: use "kvm_arch_vm_finalize_vcpus()" in the subject line
Quoted 11 lines · Lisa Wang
From: Sagi Shahar <[email protected]>
Finalize TDX VM after creation to make it runnable.
Signed-off-by Sagi Shahar <[email protected]>Reviewed-by Ira Weiny <[email protected]>Signed-off-by Lisa Wang <[email protected]>--- tools/testing/selftests/kvm/lib/x86/processor.c | 6 ++++++ 1 file changed, 6 insertions(+)
PATCH v14 20/22 KVM: selftests: Implement MMIO WRITE for the TDX VM
In reply to Lisa WangFrom: Erdem Aktas <[email protected]>
Implement the tdx_mmio_write() to allow TDX VMs to request MMIO emulation.
Follow the Intel Guest-Hypervisor Communication Interface (GHCI) spec to the minimum extent that a spec-abiding TDX module will pass the request to KVM. Skip implementing the #VE handler as described in the GHCI spec to reduce selftests dependency.
To perform emulated I/O, VMs use the TDG.VP.VMCALL instruction to request MMIO.
--- tools/testing/selftests/kvm/Makefile.kvm | 1 + tools/testing/selftests/kvm/include/x86/tdx/tdx.h | 17 +++++++++++ tools/testing/selftests/kvm/lib/x86/tdx/tdx.S | 37 +++++++++++++++++++++++ 3 files changed, 55 insertions(+)
tools/testing/selftests/kvm/Makefile.kvm +1 -0
@@ -33,6 +33,7 @@ LIBKVM_x86 += lib/x86/tdx/tdx_util.c LIBKVM_x86 += lib/x86/ucall.c LIBKVM_x86 += lib/x86/vmx.c LIBKVM_x86 += lib/x86/tdx/td_boot.S +LIBKVM_x86 += lib/x86/tdx/tdx.S LIBKVM_arm64 += lib/arm64/gic.c LIBKVM_arm64 += lib/arm64/gic_v3.c
tools/testing/selftests/kvm/include/x86/tdx/tdx.h new file +17 -0
@@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef SELFTEST_KVM_TDX_TDX_H +#define SELFTEST_KVM_TDX_TDX_H + +#include <linux/types.h> + +#define TDG_VP_VMCALL_VE_REQUEST_MMIO 48 +#define TDVMCALL_MMIO_WRITE 1 + +u64 __tdcall (u64 leaf,u64 r12,u64 r13,u64 r14,u64 r15); + +static inline u64 tdx_mmio_write (u64 address,u32 size,u64 data_in) +{ +return __tdcall (TDG_VP_VMCALL_VE_REQUEST_MMIO , size, +TDVMCALL_MMIO_WRITE , address, data_in); +} +#endif /* SELFTEST_KVM_TDX_TDX_H */
tools/testing/selftests/kvm/lib/x86/tdx/tdx.S new file +37 -0
@@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +.globl __tdcall +/* + * u64 __tdcall(u64 leaf, u64 r12, u64 r13, u64 r14, u64 r15) + */ +__tdcall: + push %r12 + push %r13 + push %r14 + push %r15 + + /* Map C ABI fast arguments to TDX GHCI payload registers */ + /* RDI=leaf, RSI=r12, RDX=r13, RCX=r14, R8=r15 */ + + mov %rdi, %r11 + mov %rsi, %r12 + mov %rdx, %r13 + mov %rcx, %r14 + mov %r8, %r15 + + /* TDCALL boilerplate */ + mov $0, %rax + mov $0xFC00, %rcx /* Expose R10-R15 */ + mov $0, %r10 + + /* tdcall instruction */ + .byte 0x66, 0x0f, 0x01, 0xcc + + /* Extract status */ + mov %r10, %rax + + pop %r15 + pop %r14 + pop %r13 + pop %r12 + ret
Signature · 1 line
2.55.0.229.g6434b31f56-goog
Re: [PATCH v14 20/22] KVM: selftests: Implement MMIO WRITE for the TDX VM
Quoted 104 lines · Lisa Wang
From: Erdem Aktas <[email protected]>
Implement the
tdx_mmio_write()to allow TDX VMs to request MMIO emulation.Follow the Intel Guest-Hypervisor Communication Interface (GHCI) spec to the minimum extent that a spec-abiding TDX module will pass the request to KVM. Skip implementing the #VE handler as described in the GHCI spec to reduce selftests dependency.
To perform emulated I/O, VMs use the TDG.VP.VMCALL instruction to request MMIO.
Signed-off-by Erdem Aktas <[email protected]>Co-developed-by Sagi Shahar <[email protected]>Signed-off-by Sagi Shahar <[email protected]>Co-developed-by Lisa Wang <[email protected]>Signed-off-by Lisa Wang <[email protected]>--- tools/testing/selftests/kvm/Makefile.kvm | 1 + tools/testing/selftests/kvm/include/x86/tdx/tdx.h | 17 +++++++++++ tools/testing/selftests/kvm/lib/x86/tdx/tdx.S | 37 +++++++++++++++++++++++ 3 files changed, 55 insertions(+)tools/testing/selftests/kvm/Makefile.kvm +1
-0@@ -33,6 +33,7 @@ LIBKVM_x86 += lib/x86/tdx/tdx_util.c LIBKVM_x86 += lib/x86/ucall.c LIBKVM_x86 += lib/x86/vmx.c LIBKVM_x86 += lib/x86/tdx/td_boot.S +LIBKVM_x86 += lib/x86/tdx/tdx.S LIBKVM_arm64 += lib/arm64/gic.c LIBKVM_arm64 += lib/arm64/gic_v3.ctools/testing/selftests/kvm/include/x86/tdx/tdx.h new file +17
-0@@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef SELFTEST_KVM_TDX_TDX_H +#define SELFTEST_KVM_TDX_TDX_H + +#include <linux/types.h> + +#define TDG_VP_VMCALL_VE_REQUEST_MMIO 48 +#define TDVMCALL_MMIO_WRITE 1 + +u64 __tdcall (u64 leaf,u64 r12,u64 r13,u64 r14,u64 r15); + +static inline u64 tdx_mmio_write (u64 address,u32 size,u64 data_in) +{ +return __tdcall (TDG_VP_VMCALL_VE_REQUEST_MMIO , size, +TDVMCALL_MMIO_WRITE , address, data_in); +} +#endif /* SELFTEST_KVM_TDX_TDX_H */ tools/testing/selftests/kvm/lib/x86/tdx/tdx.S new file +37
-1@@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +.globl __tdcall +/* + * u64 __tdcall(u64 leaf, u64 r12, u64 r13, u64 r14, u64 r15) + */ +__tdcall: + push %r12 + push %r13 + push %r14 + push %r15 + + /* Map C ABI fast arguments to TDX GHCI payload registers */ + /* RDI=leaf, RSI=r12, RDX=r13, RCX=r14, R8=r15 */ + + mov %rdi, %r11 + mov %rsi, %r12 + mov %rdx, %r13 + mov %rcx, %r14 + mov %r8, %r15 + + /* TDCALL boilerplate */ + mov $0, %rax + mov $0xFC00, %rcx /* Expose R10-R15 */ + mov $0, %r10 + + /* tdcall instruction */ + .byte 0x66, 0x0f, 0x01, 0xcc + + /* Extract status */ + mov %r10, %rax + + pop %r15 + pop %r14 + pop %r13 + pop %r12 + ret --
Quoted 1 line
2.55.0.229.g6434b31f56-goog
We've been discussing internally if there's a nicer way to write this and let it scale to other tdcall wrappers in future tests, especially when we run out of registers for passing parameters (input and output) for other tdcalls.
Looks like the only way to prevent the compiler from messing up register state before the tdcall instruction is to be completely explicit about it, and to use assembly in a .S file.
I miss how readable it was in v13 [1]. Sean's comments at [1] are also echoed in [2].
[1] https://lore.kernel.org/all/[email protected]/
[2] https://lore.kernel.org/all/[email protected]/
We can figure out how best to extend this when the other tests are worked on.
Re: [PATCH v14 20/22] KVM: selftests: Implement MMIO WRITE for the TDX VM
In reply to Lisa WangQuoted 8 lines · Lisa Wang
From: Erdem Aktas <[email protected]>
Implement the
tdx_mmio_write()to allow TDX VMs to request MMIO emulation.Follow the Intel Guest-Hypervisor Communication Interface (GHCI) spec to the minimum extent that a spec-abiding TDX module will pass the request to KVM.
I don't get what it wants to say with this.
Quoted 4 lines
Skip implementing the #VE handler as described in the GHCI spec to reduce selftests dependency.
To perform emulated I/O, VMs use the TDG.VP.VMCALL instruction to
To perform emulated MMIO?
Quoted 31 lines
request MMIO.
Signed-off-by Erdem Aktas <[email protected]>Co-developed-by Sagi Shahar <[email protected]>Signed-off-by Sagi Shahar <[email protected]>Co-developed-by Lisa Wang <[email protected]>Signed-off-by Lisa Wang <[email protected]>--- tools/testing/selftests/kvm/Makefile.kvm | 1 + tools/testing/selftests/kvm/include/x86/tdx/tdx.h | 17 +++++++++++ tools/testing/selftests/kvm/lib/x86/tdx/tdx.S | 37 +++++++++++++++++++++++ 3 files changed, 55 insertions(+)tools/testing/selftests/kvm/Makefile.kvm +1
-0@@ -33,6 +33,7 @@ LIBKVM_x86 += lib/x86/tdx/tdx_util.c LIBKVM_x86 += lib/x86/ucall.c LIBKVM_x86 += lib/x86/vmx.c LIBKVM_x86 += lib/x86/tdx/td_boot.S +LIBKVM_x86 += lib/x86/tdx/tdx.S LIBKVM_arm64 += lib/arm64/gic.c LIBKVM_arm64 += lib/arm64/gic_v3.ctools/testing/selftests/kvm/include/x86/tdx/tdx.h new file +0
-0
tools/testing/selftests/kvm/include/x86/tdx/tdx.h line 7
Quoted 7 lines
tools/testing/selftests/kvm/include/x86/tdx/tdx.h new file +7
-0@@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef SELFTEST_KVM_TDX_TDX_H +#define SELFTEST_KVM_TDX_TDX_H + +#include <linux/types.h> + +#define TDG_VP_VMCALL_VE_REQUEST_MMIO 48
I know it is the name in GHCI spec. But MMIO doesn't have to be invoked by #VE. I think we can directly call it
TDG_VP_VMCALL_MMIO
tools/testing/selftests/kvm/include/x86/tdx/tdx.h line 8
Quoted 1 line
tools/testing/selftests/kvm/include/x86/tdx/tdx.h new file +1
-0+#define TDVMCALL_MMIO_WRITE 1
So we are defining TDG_VP_VMCALL_* and TDVMCALL_*. We'd better to make it consistent.
Quoted 14 lines
tools/testing/selftests/kvm/include/x86/tdx/tdx.h new file +8
-0+u64 __tdcall (u64 leaf,u64 r12,u64 r13,u64 r14,u64 r15); + +static inline u64 tdx_mmio_write (u64 address,u32 size,u64 data_in) +{ +return __tdcall (TDG_VP_VMCALL_VE_REQUEST_MMIO , size, +TDVMCALL_MMIO_WRITE , address, data_in); +} +#endif /* SELFTEST_KVM_TDX_TDX_H */ tools/testing/selftests/kvm/lib/x86/tdx/tdx.S new file +0
-0
tools/testing/selftests/kvm/lib/x86/tdx/tdx.S line 5
Quoted 5 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx.S new file +5
-0@@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +.globl __tdcall +/* + * u64 __tdcall(u64 leaf, u64 r12, u64 r13, u64 r14, u64 r15)
this funtion is specific to TDG.VP.VMCALL. Please just name it tdg_vp_vmcall()
tools/testing/selftests/kvm/lib/x86/tdx/tdx.S line 14
Quoted 9 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx.S new file +9
-0+ */ +__tdcall: + push %r12 + push %r13 + push %r14 + push %r15 + + /* Map C ABI fast arguments to TDX GHCI payload registers */ + /* RDI=leaf, RSI=r12, RDX=r13, RCX=r14, R8=r15 */
/*
*
*/
for multiple-line comments?
tools/testing/selftests/kvm/lib/x86/tdx/tdx.S line 22
Quoted 7 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx.S new file +7
-0+ mov %rdi, %r11 + mov %rsi, %r12 + mov %rdx, %r13 + mov %rcx, %r14 + mov %r8, %r15 + + /* TDCALL boilerplate */
it's not TDCALL boilerplate but TDG.VP.VMCALL since below rax is hardcoded to 0.
Quoted 16 lines
tools/testing/selftests/kvm/lib/x86/tdx/tdx.S new file +15
-0+ mov $0, %rax + mov $0xFC00, %rcx /* Expose R10-R15 */ + mov $0, %r10 + + /* tdcall instruction */ + .byte 0x66, 0x0f, 0x01, 0xcc + + /* Extract status */ + mov %r10, %rax + + pop %r15 + pop %r14 + pop %r13 + pop %r12 + ret
Re: [PATCH v14 20/22] KVM: selftests: Implement MMIO WRITE for the TDX VM
In reply to Lisa WangQuoted 13 lines · Lisa Wang
From: Erdem Aktas <[email protected]>
Implement the
tdx_mmio_write()to allow TDX VMs to request MMIO emulation.Follow the Intel Guest-Hypervisor Communication Interface (GHCI) spec to the minimum extent that a spec-abiding TDX module will pass the request to KVM. Skip implementing the #VE handler as described in the GHCI spec to reduce selftests dependency.
To perform emulated I/O, VMs use the TDG.VP.VMCALL instruction to request MMIO.
[ ... ]
Quoted 6 lines
tools/testing/selftests/kvm/include/x86/tdx/tdx.h new file +0
-0
tools/testing/selftests/kvm/include/x86/tdx/tdx.h line 8
Quoted 8 lines
tools/testing/selftests/kvm/include/x86/tdx/tdx.h new file +8
-0@@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef SELFTEST_KVM_TDX_TDX_H +#define SELFTEST_KVM_TDX_TDX_H + +#include <linux/types.h> + +#define TDG_VP_VMCALL_VE_REQUEST_MMIO 48 +#define TDVMCALL_MMIO_WRITE 1
The indentation in these two lines is off.
tools/testing/selftests/kvm/include/x86/tdx/tdx.h line 10
Quoted 2 lines
tools/testing/selftests/kvm/include/x86/tdx/tdx.h new file +2
-0+ +u64 __tdcall (u64 leaf,u64 r12,u64 r13,u64 r14,u64 r15);
As Xiaoyao already pointed out, this function name is misleading. This is a TDG.VP.VMCALL (or a TDVMCALL), and r11 is a subfunction (the guest kernel simply calls it "fn").
Quoted 13 lines
tools/testing/selftests/kvm/include/x86/tdx/tdx.h new file +13
-0+ +.globl __tdcall +/* +* u64 __tdcall(u64 leaf, u64 r12, u64 r13, u64 r14, u64 r15) + */ +__tdcall: + push %r12 + push %r13 + push %r14 + push %r15 + +/* Map C ABI fast arguments to TDX GHCI payload registers */ +/* RDI=leaf, RSI=r12, RDX=r13, RCX=r14, R8=r15 */
[ ... ]
tools/testing/selftests/kvm/include/x86/tdx/tdx.h
Quoted 6 lines
tools/testing/selftests/kvm/include/x86/tdx/tdx.h new file +6
-0+ + pop %r15 + pop %r14 + pop %r13 + pop %r12 + ret
This is probably more of a nit... td_boot.S has:
.section .note.GNU-stack,"",%progbits
...to disable executable stack.
Quoted 5 lines
Signature · 2 lines
-- 2.55.0.229.g6434b31f56-goog
PATCH v14 21/22 KVM: selftests: Add ucall support for TDX
In reply to Lisa WangFrom: Ackerley Tng <[email protected]>
Implement TDX ucall using TDCALL-based MMIO to pass the ucall address from the VM to the host.
In standard KVM selftests, ucall uses a PIO instruction as a trigger to exit to the host, which then retrieves the ucall address by reading the guest's RDI register. This approach is incompatible with TDX because the host cannot access guest registers.
Furthermore, PIO exits only expose 4 bytes of immediate data, which is insufficient for a 8-byte ucall address. By using TDCALL-based MMIO, the VM can share the full 8-byte address in a single exit without refactoring the common ucall framework and other non-x86 architectures.
--- tools/testing/selftests/kvm/include/x86/ucall.h | 6 ----- tools/testing/selftests/kvm/lib/x86/ucall.c | 33 +++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 6 deletions(-)
tools/testing/selftests/kvm/include/x86/ucall.h +0 -6
@@ -2,12 +2,6 @@#ifndef SELFTEST_KVM_UCALL_H #define SELFTEST_KVM_UCALL_H -#include "kvm_util.h" -#define UCALL_EXIT_REASON KVM_EXIT_IO -static inline void ucall_arch_init (struct kvm_vm *vm,gpa_t mmio_gpa) -{ -} -#endif
tools/testing/selftests/kvm/lib/x86/ucall.c +33 -0
@@ -5,11 +5,35 @@* Copyright (C) 2018, Red Hat, Inc. */ #include "kvm_util.h" +#include "tdx/tdx.h" +#include "tdx/tdx_util.h" #define UCALL_PIO_PORT ((u16)0x1000) +/* HPET address is guaranteed to be unused for ucall MMIO */ +#define UCALL_MMIO_GPA 0xfed00000 + +static u8 vm_type; +static gpa_t ucall_mmio_gpa; + +void ucall_arch_init (struct kvm_vm *vm,gpa_t mmio_gpa) +{ + vm_type = vm->type; +sync_global_to_guest (vm, vm_type); + +if (is_tdx_vm (vm)) { + ucall_mmio_gpa =UCALL_MMIO_GPA | vm->arch.s_bit; +sync_global_to_guest (vm, ucall_mmio_gpa); + } +} +void ucall_arch_do_ucall (gva_t uc) { +if (vm_type ==KVM_X86_TDX_VM ) { +tdx_mmio_write (ucall_mmio_gpa,sizeof (gva_t), uc); +return ; + } +/* * FIXME: Revert this hack (the entire commit that added it) once nVMX * preserves L2 GPRs across a nested VM-Exit. If a ucall from L2, e.g.
@@ -46,6 +70,15 @@ void *ucall_arch_get_ucall(struct kvm_vcpu *vcpu) {struct kvm_run *run = vcpu->run; +if (vm_type ==KVM_X86_TDX_VM ) { +if (run->exit_reason ==KVM_EXIT_MMIO && + run->mmio.phys_addr ==UCALL_MMIO_GPA && + run->mmio.len ==sizeof (gva_t) && + run->mmio.is_write) +return (void *)(*((gva_t *)run->mmio.data)); +return NULL ; + } +if (run->exit_reason ==KVM_EXIT_IO && run->io.port ==UCALL_PIO_PORT ) {struct kvm_regs regs;
Signature · 1 line
2.55.0.229.g6434b31f56-goog
Re: [PATCH v14 21/22] KVM: selftests: Add ucall support for TDX
Quoted 30 lines · Lisa Wang
From: Ackerley Tng <[email protected]>
Implement TDX ucall using TDCALL-based MMIO to pass the ucall address from the VM to the host.
In standard KVM selftests, ucall uses a PIO instruction as a trigger to exit to the host, which then retrieves the ucall address by reading the guest's RDI register. This approach is incompatible with TDX because the host cannot access guest registers.
Furthermore, PIO exits only expose 4 bytes of immediate data, which is insufficient for a 8-byte ucall address. By using TDCALL-based MMIO, the VM can share the full 8-byte address in a single exit without refactoring the common ucall framework and other non-x86 architectures.
Signed-off-by Ackerley Tng <[email protected]>Co-developed-by Sagi Shahar <[email protected]>Signed-off-by Sagi Shahar <[email protected]>Co-developed-by Lisa Wang <[email protected]>Signed-off-by Lisa Wang <[email protected]>--- tools/testing/selftests/kvm/include/x86/ucall.h | 6 ----- tools/testing/selftests/kvm/lib/x86/ucall.c | 33 +++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 6 deletions(-)tools/testing/selftests/kvm/include/x86/ucall.h +0
-0
tools/testing/selftests/kvm/include/x86/ucall.h line 5
Quoted 6 lines
tools/testing/selftests/kvm/include/x86/ucall.h +0
-2@@ -2,12 +2,6 @@#ifndef SELFTEST_KVM_UCALL_H #define SELFTEST_KVM_UCALL_H -#include "kvm_util.h" -#define UCALL_EXIT_REASON KVM_EXIT_IO
This seems to leave the potential issue for future since x86 now supports different EXIT_REASON for UCALL.
Quoted 18 lines
tools/testing/selftests/kvm/include/x86/ucall.h +0
-4-static inline void ucall_arch_init (struct kvm_vm *vm,gpa_t mmio_gpa) -{ -} -#endif tools/testing/selftests/kvm/lib/x86/ucall.c +2
-0@@ -5,11 +5,35 @@* Copyright (C) 2018, Red Hat, Inc. */ #include "kvm_util.h" +#include "tdx/tdx.h" +#include "tdx/tdx_util.h" #define UCALL_PIO_PORT ((u16)0x1000)
tools/testing/selftests/kvm/lib/x86/ucall.c line 22
Quoted 10 lines
tools/testing/selftests/kvm/lib/x86/ucall.c +10
-0+/* HPET address is guaranteed to be unused for ucall MMIO */ +#define UCALL_MMIO_GPA 0xfed00000 + +static u8 vm_type; +static gpa_t ucall_mmio_gpa; + +void ucall_arch_init (struct kvm_vm *vm,gpa_t mmio_gpa) +{ + vm_type = vm->type; +sync_global_to_guest (vm, vm_type);
It works and it looks simple. But we have the architectural approach to test if a guest is TD guest, by checking the CPUID 0x21.
Since checking CPUID 0x21 is not complex, and as a bonus it can help test if TDX module behaves correctly for CPUID leaf 0x21, I think we should switch to use CPUID 0x21 to check if it is TDX VM in guest code?
tools/testing/selftests/kvm/lib/x86/ucall.c line 25
Quoted 2 lines
tools/testing/selftests/kvm/lib/x86/ucall.c +2
-0+if (is_tdx_vm (vm)) { + ucall_mmio_gpa =UCALL_MMIO_GPA | vm->arch.s_bit;
So the passed-in @mmio_gpa is not used. Why cannot use the mmio_gpa,
slot0->region.guest_phys_addr + slot0->region.memory_size
chosen by __vm_create()?
Quoted 31 lines
tools/testing/selftests/kvm/lib/x86/ucall.c +18
-0+sync_global_to_guest (vm, ucall_mmio_gpa); + } +} +void ucall_arch_do_ucall (gva_t uc) { +if (vm_type ==KVM_X86_TDX_VM ) { +tdx_mmio_write (ucall_mmio_gpa,sizeof (gva_t), uc); +return ; + } +/* * FIXME: Revert this hack (the entire commit that added it) once nVMX * preserves L2 GPRs across a nested VM-Exit. If a ucall from L2, e.g. @@ -46,6 +70,15 @@ void *ucall_arch_get_ucall(struct kvm_vcpu *vcpu) {struct kvm_run *run = vcpu->run; +if (vm_type ==KVM_X86_TDX_VM ) { +if (run->exit_reason ==KVM_EXIT_MMIO && + run->mmio.phys_addr ==UCALL_MMIO_GPA && + run->mmio.len ==sizeof (gva_t) && + run->mmio.is_write) +return (void *)(*((gva_t *)run->mmio.data)); +return NULL ; + } +if (run->exit_reason ==KVM_EXIT_IO && run->io.port ==UCALL_PIO_PORT ) {struct kvm_regs regs;
Re: [PATCH v14 21/22] KVM: selftests: Add ucall support for TDX
Quoted 7 lines · Xiaoyao Li
tools/testing/selftests/kvm/lib/x86/ucall.c line 21
Quoted 7 lines
tools/testing/selftests/kvm/lib/x86/ucall.c +7
-0+ +static u8 vm_type; +static gpa_t ucall_mmio_gpa; + +void ucall_arch_init (struct kvm_vm *vm,gpa_t mmio_gpa) +{ + vm_type = vm->type;
vm_type is u8 but vm->type is unsigned long
Quoted 8 lines
tools/testing/selftests/kvm/lib/x86/ucall.c line 22
Quoted 1 line
tools/testing/selftests/kvm/lib/x86/ucall.c +1
-0+sync_global_to_guest (vm, vm_type);It works and it looks simple. But we have the architectural approach to test if a guest is TD guest, by checking the CPUID
0x21.Since checking CPUID
0x21is not complex, and as a bonus it can help test if TDX module behaves correctly for CPUID leaf0x21, I think we should switch to use CPUID0x21to check if it is TDX VM in guest code?
Hmm... This makes me wonder if vm->arch.s_bit below could be replaced with the same architectural approach. GPAW is available through TDG.VP.INFO or the initial RBX value. This does require a bit more plumbing though.
Quoted 9 lines
tools/testing/selftests/kvm/lib/x86/ucall.c line 25
Quoted 3 lines
tools/testing/selftests/kvm/lib/x86/ucall.c +2
-0+if (is_tdx_vm (vm)) { + ucall_mmio_gpa =UCALL_MMIO_GPA | vm->arch.s_bit;So the passed-in @mmio_gpa is not used. Why cannot use the
mmio_gpa,
slot0->region.guest_phys_addr+slot0->region.memory_sizechosen by
__vm_create()?
Looks like this was discussed previously [1]. Maybe at least a comment here would help?
[1] https://lore.kernel.org/all/[email protected]/
Quoted 1 line
Re: [PATCH v14 21/22] KVM: selftests: Add ucall support for TDX
Quoted 16 lines · Peter Fang
Quoted 7 lines · Xiaoyao Li
tools/testing/selftests/kvm/lib/x86/ucall.c line 21
Quoted 7 lines
tools/testing/selftests/kvm/lib/x86/ucall.c +7
-0+ +static u8 vm_type; +static gpa_t ucall_mmio_gpa; + +void ucall_arch_init (struct kvm_vm *vm,gpa_t mmio_gpa) +{ + vm_type = vm->type;
vm_typeis u8 butvm->typeis unsigned longQuoted 5 lines
tools/testing/selftests/kvm/lib/x86/ucall.c line 22
Quoted 1 line
tools/testing/selftests/kvm/lib/x86/ucall.c +1
-0+sync_global_to_guest (vm, vm_type);It works and it looks simple. But we have the architectural approach to test if a guest is TD guest, by checking the CPUID
0x21.
Quoted 8 lines
Quoted 3 lines
Since checking CPUID
0x21is not complex, and as a bonus it can help test if TDX module behaves correctly for CPUID leaf0x21, I think we should switch to use CPUID0x21to check if it is TDX VM in guest code?Hmm... This makes me wonder if
vm->arch.s_bitbelow could be replaced with the same architectural approach. GPAW is available through TDG.VP.INFO or the initial RBX value. This does require a bit more plumbing though.
I'm afraid not. Because below is host code, and vm->arch.s_bit is not used in guest code.
Or are suggesting something like dropping the
if (is_tdx_vm(vm)) {
ucall_mmio_gpa = UCALL_MMIO_GPA | vm->arch.s_bit;
sync_global_to_guest(vm, ucall_mmio_gpa);
}entirely and use below hardcoded value instead in guest code?
UCALL_MMIO_GPA | 1 << (GPAW - 1)
Quoted 6 lines
Quoted 6 lines
tools/testing/selftests/kvm/lib/x86/ucall.c line 25
Quoted 3 lines
tools/testing/selftests/kvm/lib/x86/ucall.c +2
-0+if (is_tdx_vm (vm)) { + ucall_mmio_gpa =UCALL_MMIO_GPA | vm->arch.s_bit;So the passed-in @mmio_gpa is not used. Why cannot use the
mmio_gpa,
Quoted 6 lines
Quoted 3 lines
slot0->region.guest_phys_addr + slot0->region.memory_sizechosen by
__vm_create()?Looks like this was discussed previously [1]. Maybe at least a comment here would help?
yeah. A comment surely will help.
Quoted 3 lines
[1] https://lore.kernel.org/all/[email protected]/
Quoted 1 line
Re: [PATCH v14 21/22] KVM: selftests: Add ucall support for TDX
Quoted 11 lines · Xiaoyao Li
Quoted 5 lines
Hmm... This makes me wonder if
vm->arch.s_bitbelow could be replaced with the same architectural approach. GPAW is available through TDG.VP.INFO or the initial RBX value. This does require a bit more plumbing though.I'm afraid not. Because below is host code, and
vm->arch.s_bitis not used in guest code.Or are suggesting something like dropping the
Quoted 8 lines
if (is_tdx_vm(vm)) { ucall_mmio_gpa = UCALL_MMIO_GPA | vm->arch.s_bit; sync_global_to_guest(vm, ucall_mmio_gpa); }entirely and use below hardcoded value instead in guest code?
UCALL_MMIO_GPA | 1 << (GPAW - 1)
Yeah this is what I meant. Just drop sync_global_to_guest() entirely and do things like a normal TDX guest would.
Quoted 20 lines
Quoted 7 lines
Quoted 7 lines
tools/testing/selftests/kvm/lib/x86/ucall.c line 25
Quoted 4 lines
tools/testing/selftests/kvm/lib/x86/ucall.c +2
-0+if (is_tdx_vm (vm)) { + ucall_mmio_gpa =UCALL_MMIO_GPA | vm->arch.s_bit;So the passed-in @mmio_gpa is not used. Why cannot use the
mmio_gpa,Quoted 6 lines
Quoted 3 lines
slot0->region.guest_phys_addr + slot0->region.memory_sizechosen by
__vm_create()?Looks like this was discussed previously [1]. Maybe at least a comment here would help?
yeah. A comment surely will help.
Quoted 3 lines
[1] https://lore.kernel.org/all/[email protected]/
Quoted 1 line
Sean Christopherson
maintainerRe: [PATCH v14 21/22] KVM: selftests: Add ucall support for TDX
Quoted 17 lines · Peter Fang
Quoted 16 lines · Xiaoyao Li
Quoted 5 lines
Hmm... This makes me wonder if
vm->arch.s_bitbelow could be replaced with the same architectural approach. GPAW is available through TDG.VP.INFO or the initial RBX value. This does require a bit more plumbing though.I'm afraid not. Because below is host code, and
vm->arch.s_bitis not used in guest code.Or are suggesting something like dropping the
if (is_tdx_vm(vm)) { ucall_mmio_gpa = UCALL_MMIO_GPA | vm->arch.s_bit; sync_global_to_guest(vm, ucall_mmio_gpa); }
Quoted 6 lines
Quoted 3 lines
entirely and use below hardcoded value instead in guest code?
UCALL_MMIO_GPA | 1 << (GPAW - 1)Yeah this is what I meant. Just drop
sync_global_to_guest()entirely and do things like a normal TDX guest would.
Blech. Every time I come back to this series we're still discussing ucall crud, and "doing thing like a normal TDX guest". Selftests aren't normal guests.
I know I suggested using the HPET base, but I only did so very begrudgingly as I couldn't come up with a better alternative to emulated MMIO, and the end result is quite gross. Not only does the code ignore @mmio_gpa but still obviously use emulated MMIO, it requires synchronizing data to the guest because KVM disallows "private" MMIO.
void ucall_arch_init(struct kvm_vm *vm, gpa_t mmio_gpa)
{
vm_type = vm->type;
sync_global_to_guest(vm, vm_type); if (is_tdx_vm(vm)) {
ucall_mmio_gpa = UCALL_MMIO_GPA | vm->arch.s_bit;
sync_global_to_guest(vm, ucall_mmio_gpa);
}}
Retrieving GPA via TDG.VP.INFO isn't any better, it's still an absurd amount of "work" for something that should be trivial.
Can't we just abuse TDVMCALL_REPORT_FATAL_ERROR? AFAICT, there's no restriction on the data payload, and there's enough space to all but guarantee we'll never get a false positive.
Pulling in Xiaoyao's idea about using CPUID...
Quoted 11 lines
Review
Quoted 4 lines
(quoted diff) +4
-0+void ucall_arch_init(struct kvm_vm *vm, gpa_t mmio_gpa) +{ + vm_type = vm->type; + sync_global_to_guest(vm, vm_type);It works and it looks simple. But we have the architectural approach to test if a guest is TD guest, by checking the CPUID
0x21.Since checking CPUID
0x21is not complex, and as a bonus it can help test if TDX module behaves correctly for CPUID leaf0x21, I think we should switch to use CPUID0x21to check if it is TDX VM in guest code?
Absolutely not. It will require at least one an extra VM-Exit for TDX and non-TDX guests alike, and thanks to Intel's wonderful CPUID behavior of having unsupported leaves return the last supported leaf, the guest would have to check CPUID.0x0 and then CPUID.0x21 on modern hardware, i.e. would incur two extra VM-Exits. I don't care about the performance, but from a debug perspective that's going to be awful, as what should be a super simple operation will be polluted with unwanted data.
Stop trying to reinvent the wheel and just use a virtual function table.
I've verified the attached patches don't break non-TDX selftests, someone just needs to test the TDX changes.
From 659487e0a9862900b052836229e3ac366c602f59 Mon Sep 17 00:00:00 2001
From: Sean Christopherson <[email protected]>
Date: Fri, 28 Aug 2026 06:46:51 -0700
Subject: [PATCH 1/3] KVM: selftests: Add support for per-VM ucall ops on x86
Add a layer of indirection to x86's ucall infrastructure to allow wiring up a different set of {do,get}_ucall() operations for TDX VMs. TDX can't use port I/O (at least, not robustly), as the TDX ABI doesn't allow the guest to share arbitrary register state with the host on a PIO exit, and the PIO data payload is limited to 4 bytes, i.e. would potentially truncate the address of the per-ucall structure.
No functional change intended, as the ops are still hardwirte to the common x86 ucall functions.
--- .../testing/selftests/kvm/include/x86/ucall.h | 6 +---- tools/testing/selftests/kvm/lib/x86/ucall.c | 27 +++++++++++++++++-- 2 files changed, 26 insertions(+), 7 deletions(-)
tools/testing/selftests/kvm/include/x86/ucall.h +1 -5
@@ -2,12 +2,8 @@#ifndef SELFTEST_KVM_UCALL_H #define SELFTEST_KVM_UCALL_H -#include "kvm_util.h" +#include "linux/kvm.h" #define UCALL_EXIT_REASON KVM_EXIT_IO -static inline void ucall_arch_init (struct kvm_vm *vm,gpa_t mmio_gpa) -{ -} -#endif
tools/testing/selftests/kvm/lib/x86/ucall.c +25 -2
@@ -8,7 +8,7 @@#define UCALL_PIO_PORT ((u16)0x1000) -void ucall_arch_do_ucall (gva_t uc) +static void ucall_x86_do_ucall (gva_t uc) {/* * FIXME: Revert this hack (the entire commit that added it) once nVMX
@@ -42,7 +42,7 @@ void ucall_arch_do_ucall(gva_t uc) HORRIFIC_L2_UCALL_CLOBBER_HACK); } -void *ucall_arch_get_ucall (struct kvm_vcpu *vcpu) +static void *ucall_x86_get_ucall (struct kvm_vcpu *vcpu) {struct kvm_run *run = vcpu->run;
@@ -54,3 +54,26 @@ void *ucall_arch_get_ucall(struct kvm_vcpu *vcpu) }return NULL ; } + +static struct { +void (*do_ucall)(gva_t uc); +void *(*get_ucall)(struct kvm_vcpu *vcpu); +} ucall_x86_ops = { + .do_ucall = ucall_x86_do_ucall, + .get_ucall = ucall_x86_get_ucall, +}; + +void ucall_arch_init (struct kvm_vm *vm,gpa_t mmio_gpa) +{ +sync_global_to_guest (vm, ucall_x86_ops); +} + +void ucall_arch_do_ucall (gva_t uc) +{ +return ucall_x86_ops.do_ucall (uc); +} + +void *ucall_arch_get_ucall (struct kvm_vcpu *vcpu) +{ +return ucall_x86_ops.get_ucall (vcpu); +}
base-commit: 8ab7454faa8be2d6c1fc29c857b9f9611ddb174b
Signature · 1 line
2.55.0.897.gb25b4bd76c-goog
From 60f3e988c65048c63217ec157e9ec228a4c484b7 Mon Sep 17 00:00:00 2001
From: Sean Christopherson <[email protected]>
Date: Fri, 28 Aug 2026 06:52:56 -0700
Subject: [PATCH 2/3] KVM: selftests: Add support for TDX ucalls, via
TDVMCALL_REPORT_FATAL_ERROR
Add support for doing ucalls on TDX by abusing TDVMCALL_REPORT_FATAL_ERROR to pass the address of the payload to the host. The "fatal error" TDVMCALL is perfectly suited for passing information to host userspace, is both the TDX Module and KVM allow the guest to pass (almost) all registers to the host, i.e. provide enough of a data payload to make a collision with a real fatal error practically impossible.
TDX can't use port I/O, as the TDX ABI doesn't allow the guest to share arbitrary register state with the host on a port I/O exit, and the port I/O data payload is limited to 4 bytes, i.e. would potentially truncate the ucall address.
Alternatively, TDX could use MMIO, but using a magic emulated MMIO address is fragile (see the TODO in __vm_create()), especially for TDX since TDX doesn't support read-only memslots, i.e. doesn't have line of sight towards addressing the TODO. E.g. TDX could hardcode the address to something that is all but guaranteed to be unused on x86, e.g. the I/O APIC base address or the HPET address, but that doesn't truly address the fragility concerns, and it's ugly because ucall_arch_init() would completely ignore the passed in @mmio_gpa despite obviously utilizing emulated MMIO.
--- .../selftests/kvm/include/x86/tdx/tdx.h | 7 +++++ tools/testing/selftests/kvm/lib/x86/ucall.c | 27 ++++++++++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-)
tools/testing/selftests/kvm/include/x86/tdx/tdx.h +7 -0
@@ -4,6 +4,13 @@#include <linux/types.h> +/* TDX hypercall Leaf IDs */ +#define TDVMCALL_GET_TD_VM_CALL_INFO 0x10000 +#define TDVMCALL_MAP_GPA 0x10001 +#define TDVMCALL_GET_QUOTE 0x10002 +#define TDVMCALL_REPORT_FATAL_ERROR 0x10003 +#define TDVMCALL_SETUP_EVENT_NOTIFY_INTERRUPT 0x10004 +#define TDG_VP_VMCALL_VE_REQUEST_MMIO 48#define TDVMCALL_MMIO_WRITE 1
tools/testing/selftests/kvm/lib/x86/ucall.c +26 -1
@@ -5,8 +5,28 @@* Copyright (C) 2018, Red Hat, Inc. */ #include "kvm_util.h" +#include "tdx/tdx.h" +#include "tdx/tdx_util.h" -#define UCALL_PIO_PORT ((u16)0x1000) +#define UCALL_PIO_PORT ((u16)0x1000) +#define UCALL_TDX_MAGIC 0xabacadabaULL + +static void ucall_tdx_do_ucall (gva_t uc) +{ +__tdcall (TDVMCALL_REPORT_FATAL_ERROR ,UCALL_TDX_MAGIC , uc,0 ,0 ); +} + +static void *ucall_tdx_get_ucall (struct kvm_vcpu *vcpu) +{ +struct kvm_run *run = vcpu->run; + +if (run->exit_reason ==KVM_EXIT_SYSTEM_EVENT && + run->system_event.type ==KVM_SYSTEM_EVENT_TDX_FATAL && + run->system_event.data[12 ] ==UCALL_TDX_MAGIC ) +return (void *)(run->system_event.data[13 ]); + +return NULL ; +}static void ucall_x86_do_ucall (gva_t uc) {
@@ -65,6 +85,11 @@ static struct {void ucall_arch_init (struct kvm_vm *vm,gpa_t mmio_gpa) { +if (is_tdx_vm (vm)) { + ucall_x86_ops.do_ucall = ucall_tdx_do_ucall; + ucall_x86_ops.get_ucall = ucall_tdx_get_ucall; + } +sync_global_to_guest (vm, ucall_x86_ops); }
Signature · 1 line
2.55.0.897.gb25b4bd76c-goog
Re: [PATCH v14 21/22] KVM: selftests: Add ucall support for TDX
Quoted 43 lines · Sean Christopherson
Quoted 17 lines · Peter Fang
Quoted 16 lines · Xiaoyao Li
Quoted 5 lines
Hmm... This makes me wonder if
vm->arch.s_bitbelow could be replaced with the same architectural approach. GPAW is available through TDG.VP.INFO or the initial RBX value. This does require a bit more plumbing though.I'm afraid not. Because below is host code, and
vm->arch.s_bitis not used in guest code.Or are suggesting something like dropping the
if (is_tdx_vm(vm)) { ucall_mmio_gpa = UCALL_MMIO_GPA | vm->arch.s_bit; sync_global_to_guest(vm, ucall_mmio_gpa); }Quoted 6 lines
Quoted 3 lines
entirely and use below hardcoded value instead in guest code?
UCALL_MMIO_GPA | 1 << (GPAW - 1)Yeah this is what I meant. Just drop
sync_global_to_guest()entirely and do things like a normal TDX guest would.Blech. Every time I come back to this series we're still discussing ucall crud, and "doing thing like a normal TDX guest". Selftests aren't normal guests.
I know I suggested using the HPET base, but I only did so very begrudgingly as I couldn't come up with a better alternative to emulated MMIO, and the end result is quite gross. Not only does the code ignore @mmio_gpa but still obviously use emulated MMIO, it requires synchronizing data to the guest because KVM disallows "private" MMIO.
void ucall_arch_init(struct kvm_vm *vm, gpa_t mmio_gpa) { vm_type = vm->type; sync_global_to_guest(vm, vm_type);if (is_tdx_vm(vm)) { ucall_mmio_gpa = UCALL_MMIO_GPA | vm->arch.s_bit; sync_global_to_guest(vm, ucall_mmio_gpa); }
Quoted 8 lines
}
Retrieving GPA via TDG.VP.INFO isn't any better, it's still an absurd amount of "work" for something that should be trivial.
Can't we just abuse
TDVMCALL_REPORT_FATAL_ERROR? AFAICT, there's no restriction on the data payload, and there's enough space to all but guarantee we'll never get a false positive.
Ohh interesting idea to use TDVMCALL_REPORT_FATAL_ERROR to pass through data. Yeah this looks good to me.
Quoted 1 line
Re: [PATCH v14 21/22] KVM: selftests: Add ucall support for TDX
In reply to Sean ChristophersonQuoted 66 lines · Sean Christopherson
Quoted 17 lines · Peter Fang
Quoted 16 lines · Xiaoyao Li
Quoted 5 lines
Hmm... This makes me wonder if
vm->arch.s_bitbelow could be replaced with the same architectural approach. GPAW is available through TDG.VP.INFO or the initial RBX value. This does require a bit more plumbing though.I'm afraid not. Because below is host code, and
vm->arch.s_bitis not used in guest code.Or are suggesting something like dropping the
if (is_tdx_vm(vm)) { ucall_mmio_gpa = UCALL_MMIO_GPA | vm->arch.s_bit; sync_global_to_guest(vm, ucall_mmio_gpa); }Quoted 6 lines
Quoted 3 lines
entirely and use below hardcoded value instead in guest code?
UCALL_MMIO_GPA | 1 << (GPAW - 1)Yeah this is what I meant. Just drop
sync_global_to_guest()entirely and do things like a normal TDX guest would.Blech. Every time I come back to this series we're still discussing ucall crud, and "doing thing like a normal TDX guest". Selftests aren't normal guests.
I know I suggested using the HPET base, but I only did so very begrudgingly as I couldn't come up with a better alternative to emulated MMIO, and the end result is quite gross. Not only does the code ignore @mmio_gpa but still obviously use emulated MMIO, it requires synchronizing data to the guest because KVM disallows "private" MMIO.
void ucall_arch_init(struct kvm_vm *vm, gpa_t mmio_gpa) { vm_type = vm->type; sync_global_to_guest(vm, vm_type);if (is_tdx_vm(vm)) { ucall_mmio_gpa = UCALL_MMIO_GPA | vm->arch.s_bit; sync_global_to_guest(vm, ucall_mmio_gpa); }}
Retrieving GPA via TDG.VP.INFO isn't any better, it's still an absurd amount of "work" for something that should be trivial.
Can't we just abuse
TDVMCALL_REPORT_FATAL_ERROR? AFAICT, there's no restriction on the data payload, and there's enough space to all but guarantee we'll never get a false positive.Pulling in Xiaoyao's idea about using CPUID...
Quoted 11 lines
tools/testing/selftests/kvm/lib/x86/ucall.c void *ucall_arch_get_ucall(struct kvm_vcpu *vcpu)
Quoted 4 lines
tools/testing/selftests/kvm/lib/x86/ucall.c +4
-0+void ucall_arch_init (struct kvm_vm *vm,gpa_t mmio_gpa) +{ + vm_type = vm->type; +sync_global_to_guest (vm, vm_type);It works and it looks simple. But we have the architectural approach to test if a guest is TD guest, by checking the CPUID
0x21.Since checking CPUID
0x21is not complex, and as a bonus it can help test if TDX module behaves correctly for CPUID leaf0x21, I think we should switch to use CPUID0x21to check if it is TDX VM in guest code?
Quoted 6 lines
Absolutely not. It will require at least one an extra VM-Exit for TDX and non-TDX guests alike, and thanks to Intel's wonderful CPUID behavior of having unsupported leaves return the last supported leaf, the guest would have to check CPUID.0x0 and then CPUID.0x21 on modern hardware, i.e. would incur two extra VM-Exits. I don't care about the performance, but from a debug perspective that's going to be awful, as what should be a super simple operation will be polluted with unwanted data.
As you said, "Selftests aren't normal guests". So the guest code doesn't need to check CPUID.0x0 to get maximum supported leaf before CPUID.0x21.
And for TDX, the CPUID doesn't cause VM-Exit to KVM, it only exits to TDX module, which is transparent to KVM.
Anyway, if we go with the TDVMCALL_REPORT_FATAL_ERROR as you proposed, we don't need the logic to check if it is a TDX VM in the guest.
Quoted 4 lines
Stop trying to reinvent the wheel and just use a virtual function table.
I've verified the attached patches don't break non-TDX selftests, someone just needs to test the TDX changes.
I test it and it works.
PATCH v14 22/22 KVM: selftests: Add TDX lifecycle test
In reply to Lisa WangFrom: Sagi Shahar <[email protected]>
Adding a test to verify TDX lifecycle by creating a simple TDX VM.
--- tools/testing/selftests/kvm/Makefile.kvm | 1 + .../testing/selftests/kvm/include/x86/processor.h | 1 + .../selftests/kvm/include/x86/tdx/tdx_util.h | 5 ++++ tools/testing/selftests/kvm/x86/tdx_vm_test.c | 33 ++++++++++++++++++++++ 4 files changed, 40 insertions(+)
tools/testing/selftests/kvm/Makefile.kvm +1 -0
@@ -167,6 +167,7 @@ TEST_GEN_PROGS_x86 += rseq_test TEST_GEN_PROGS_x86 += steal_time TEST_GEN_PROGS_x86 += system_counter_offset_test TEST_GEN_PROGS_x86 += pre_fault_memory_test +TEST_GEN_PROGS_x86 += x86/tdx_vm_test # Compiled outputs used by test targets TEST_GEN_PROGS_EXTENDED_x86 += x86/nx_huge_pages_test
tools/testing/selftests/kvm/include/x86/processor.h +1 -0
@@ -372,6 +372,7 @@ static inline unsigned int x86_model(unsigned int eax)#define VM_SHAPE_SEV VM_TYPE(KVM_X86_SEV_VM)#define VM_SHAPE_SEV_ES VM_TYPE(KVM_X86_SEV_ES_VM)#define VM_SHAPE_SNP VM_TYPE(KVM_X86_SNP_VM) +#define VM_SHAPE_TDX VM_TYPE(KVM_X86_TDX_VM)#define PHYSICAL_PAGE_MASK GENMASK_ULL(51, 12)
tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h +5 -0
@@ -11,6 +11,11 @@ static inline bool is_tdx_vm(struct kvm_vm *vm)return vm->type ==KVM_X86_TDX_VM ; } +static inline bool is_tdx_supported (void ) +{ +return !!(kvm_check_cap (KVM_CAP_VM_TYPES ) &BIT (KVM_X86_TDX_VM )); +} +/* * TDX ioctls * Use underscores to avoid collisions with struct member names.
tools/testing/selftests/kvm/x86/tdx_vm_test.c new file +33 -0
@@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include "processor.h" +#include "kvm_util.h" +#include "tdx/tdx_util.h" +#include "ucall_common.h" +#include "kselftest_harness.h" + +static void guest_code_lifecycle (void ) +{ +GUEST_DONE (); +} + +TEST (verify_td_lifecycle) +{ +struct kvm_vcpu *vcpu; +struct kvm_vm *vm; +struct ucall uc; + + vm =vm_create_shape_with_one_vcpu (VM_SHAPE_TDX , &vcpu, + guest_code_lifecycle); + +vcpu_run (vcpu); +TEST_ASSERT_EQ (get_ucall (vcpu, &uc),UCALL_DONE ); + +kvm_vm_free (vm); +} + +int main (int argc,char **argv) +{ +TEST_REQUIRE (is_tdx_supported ()); +return test_harness_run (argc, argv); +}
Signature · 1 line
2.55.0.229.g6434b31f56-goog
Re: [PATCH v14 22/22] KVM: selftests: Add TDX lifecycle test
Quoted 6 lines · Lisa Wang
From: Sagi Shahar <[email protected]>
Adding a test to verify TDX lifecycle by creating a simple TDX VM.
Signed-off-by Sagi Shahar <[email protected]>Signed-off-by Lisa Wang <[email protected]>
Quoted 87 lines
--- tools/testing/selftests/kvm/Makefile.kvm | 1 + .../testing/selftests/kvm/include/x86/processor.h | 1 + .../selftests/kvm/include/x86/tdx/tdx_util.h | 5 ++++ tools/testing/selftests/kvm/x86/tdx_vm_test.c | 33 ++++++++++++++++++++++ 4 files changed, 40 insertions(+)tools/testing/selftests/kvm/Makefile.kvm +1
-0@@ -167,6 +167,7 @@ TEST_GEN_PROGS_x86 += rseq_test TEST_GEN_PROGS_x86 += steal_time TEST_GEN_PROGS_x86 += system_counter_offset_test TEST_GEN_PROGS_x86 += pre_fault_memory_test +TEST_GEN_PROGS_x86 += x86/tdx_vm_test # Compiled outputs used by test targets TEST_GEN_PROGS_EXTENDED_x86 += x86/nx_huge_pages_testtools/testing/selftests/kvm/include/x86/processor.h +1
-0@@ -372,6 +372,7 @@ static inline unsigned int x86_model(unsigned int eax)#define VM_SHAPE_SEV VM_TYPE(KVM_X86_SEV_VM)#define VM_SHAPE_SEV_ES VM_TYPE(KVM_X86_SEV_ES_VM)#define VM_SHAPE_SNP VM_TYPE(KVM_X86_SNP_VM) +#define VM_SHAPE_TDX VM_TYPE(KVM_X86_TDX_VM)#define PHYSICAL_PAGE_MASK GENMASK_ULL(51, 12)tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h +5
-0@@ -11,6 +11,11 @@ static inline bool is_tdx_vm(struct kvm_vm *vm)return vm->type ==KVM_X86_TDX_VM ; } +static inline bool is_tdx_supported (void ) +{ +return !!(kvm_check_cap (KVM_CAP_VM_TYPES ) &BIT (KVM_X86_TDX_VM )); +} +/* * TDX ioctls * Use underscores to avoid collisions with struct member names. tools/testing/selftests/kvm/x86/tdx_vm_test.c new file +33
-0@@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include "processor.h" +#include "kvm_util.h" +#include "tdx/tdx_util.h" +#include "ucall_common.h" +#include "kselftest_harness.h" + +static void guest_code_lifecycle (void ) +{ +GUEST_DONE (); +} + +TEST (verify_td_lifecycle) +{ +struct kvm_vcpu *vcpu; +struct kvm_vm *vm; +struct ucall uc; + + vm =vm_create_shape_with_one_vcpu (VM_SHAPE_TDX , &vcpu, + guest_code_lifecycle); + +vcpu_run (vcpu); +TEST_ASSERT_EQ (get_ucall (vcpu, &uc),UCALL_DONE ); + +kvm_vm_free (vm); +} + +int main (int argc,char **argv) +{ +TEST_REQUIRE (is_tdx_supported ()); +return test_harness_run (argc, argv); +}
Re: [PATCH v14 22/22] KVM: selftests: Add TDX lifecycle test
In reply to Lisa WangQuoted 18 lines · Lisa Wang
From: Sagi Shahar <[email protected]>
Adding a test to verify TDX lifecycle by creating a simple TDX VM.
Signed-off-by Sagi Shahar <[email protected]>Signed-off-by Lisa Wang <[email protected]>--- tools/testing/selftests/kvm/Makefile.kvm | 1 + .../testing/selftests/kvm/include/x86/processor.h | 1 + .../selftests/kvm/include/x86/tdx/tdx_util.h | 5 ++++ tools/testing/selftests/kvm/x86/tdx_vm_test.c | 33 ++++++++++++++++++++++ 4 files changed, 40 insertions(+)tools/testing/selftests/kvm/Makefile.kvm +0
-0
tools/testing/selftests/kvm/Makefile.kvm TEST_GEN_PROGS_x86 += rseq_test · line 170
Quoted 4 lines
tools/testing/selftests/kvm/Makefile.kvm +1
-0@@ -167,6 +167,7 @@ TEST_GEN_PROGS_x86 += rseq_test TEST_GEN_PROGS_x86 += steal_time TEST_GEN_PROGS_x86 += system_counter_offset_test TEST_GEN_PROGS_x86 += pre_fault_memory_test +TEST_GEN_PROGS_x86 += x86/tdx_vm_test
Nit: move this up to keep the x86/* grouping
Quoted 3 lines
tools/testing/selftests/kvm/Makefile.kvm +0
-0# Compiled outputs used by test targets TEST_GEN_PROGS_EXTENDED_x86 += x86/nx_huge_pages_test
Edgecombe, Rick P
maintainerQuoted 5 lines · Lisa Wang
v14 revision for TDX KVM selftests based on kvm/next and
guest_memfd:
In-place conversion support[1]. This series needs some changes from in-place conversion v9. For ease of testing, an extra hack commit has been added. The complete code is available at:
https://github.com/googleprodkernel/linux-cc/commits/tdx-selftests-v14
Are you able to get sashiko review on this? Or did you internally somehow? It seems the public sashiko cannot figure out this stack.
Edgecombe, Rick P
maintainerQuoted 9 lines · Rick Edgecombe
Quoted 5 lines · Lisa Wang
v14 revision for TDX KVM selftests based on kvm/next and
guest_memfd:
In-place conversion support[1]. This series needs some changes from in-place conversion v9. For ease of testing, an extra hack commit has been added. The complete code is available at:
https://github.com/googleprodkernel/linux-cc/commits/tdx-selftests-v14Are you able to get sashiko review on this? Or did you internally somehow? It seems the public sashiko cannot figure out this stack.
Actually why is this on top of in-place conversion? I thought the idea was to break the series apart to get something basic upstream:
https://lore.kernel.org/all/[email protected]/
Adding in-place conversion tests seems to be going in the opposite direction.
Also, the branch does not build for me:/usr/bin/ld: .../linux/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:26:
undefined reference to `TD_BOOT_PARAMETERS_PER_VCPU' /usr/bin/ld: .../linux/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:30:
undefined reference to `TD_PER_VCPU_PARAMETERS_ESP_GVA' /usr/bin/ld: .../linux/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:33:
undefined reference to `TD_BOOT_PARAMETERS_GDT'/usr/bin/ld: .../linux/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:37:
undefined reference to `TD_BOOT_PARAMETERS_IDT'/usr/bin/ld: .../linux/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:44:
undefined reference to `TD_BOOT_PARAMETERS_CR4'/usr/bin/ld: .../linux/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:46:
undefined reference to `TD_BOOT_PARAMETERS_CR3'/usr/bin/ld: .../linux/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:48:
undefined reference to `TD_BOOT_PARAMETERS_CR0'/usr/bin/ld: .../linux/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:54:
undefined reference to `TD_PER_VCPU_PARAMETERS_GUEST_CODE'
collect2: error: ld returned 1 exit status
make: *** [Makefile.kvm:351:
.../linux/tools/testing/selftests/kvm/demand_paging_test] Error 1
make: *** Waiting for unfinished jobs..../usr/bin/ld: .../linux/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.o: in function `td_boot':.../linux/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:24: undefined reference to `SIZEOF_TD_PER_VCPU_PARAMETERS'/usr/bin/ld: .../linux/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:26:
undefined reference to `TD_BOOT_PARAMETERS_PER_VCPU' /usr/bin/ld: .../linux/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:30:
undefined reference to `TD_PER_VCPU_PARAMETERS_ESP_GVA' /usr/bin/ld: .../linux/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:33:
undefined reference to `TD_BOOT_PARAMETERS_GDT'/usr/bin/ld: .../linux/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:37:
undefined reference to `TD_BOOT_PARAMETERS_IDT'/usr/bin/ld: .../linux/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:44:
undefined reference to `TD_BOOT_PARAMETERS_CR4'/usr/bin/ld: .../linux/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:46:
undefined reference to `TD_BOOT_PARAMETERS_CR3'/usr/bin/ld: .../linux/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:48:
undefined reference to `TD_BOOT_PARAMETERS_CR0'/usr/bin/ld: .../linux/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:54:
undefined reference to `TD_PER_VCPU_PARAMETERS_GUEST_CODE'
collect2: error: ld returned 1 exit status
make: *** [Makefile.kvm:351:
.../linux/tools/testing/selftests/kvm/dirty_log_test] Error 1
If I apply these patches on 8cf3969a0b7d4 from the branch, it builds though. It looks like a few changes are missing from the branch:
(quoted diff) +0 -0
b/tools/testing/selftests/kvm/guest_memfd_test.c
index d6528c6f5e031..b099b09cbae3d 100644
tools/testing/selftests/kvm/guest_memfd_test.c +1 -1
@@ -14,7 +14,7 @@#include <linux/bitmap.h> #include <linux/falloc.h> #include <linux/sizes.h> -#include <sys/mman.h> +#include <linux/mman.h> #include <sys/types.h> #include <sys/stat.h>
(quoted diff) +0 -0
b/tools/testing/selftests/kvm/lib/x86/tdx/td_boot_offsets.c index b81cd16198ecf..7f76a3585b99a 100644
tools/testing/selftests/kvm/lib/x86/tdx/td_boot_offsets.c +1 -1
@@ -5,7 +5,7 @@#include "tdx/td_boot.h" -static void __used common (void ) +static void __attribute__((used))common (void ) {OFFSET (TD_BOOT_PARAMETERS_CR0 , td_boot_parameters, cr0);OFFSET (TD_BOOT_PARAMETERS_CR3 , td_boot_parameters, cr3);
Quoted 8 lines · Rick P
Quoted 7 lines · Rick Edgecombe
Quoted 5 lines · Lisa Wang
v14 revision for TDX KVM selftests based on kvm/next and
guest_memfd:
In-place conversion support[1]. This series needs some changes from in-place conversion v9. For ease of testing, an extra hack commit has been added. The complete code is available at:
https://github.com/googleprodkernel/linux-cc/commits/tdx-selftests-v14
Quoted 9 lines
Quoted 2 lines
Are you able to get sashiko review on this? Or did you internally somehow? It seems the public sashiko cannot figure out this stack.
Actually why is this on top of in-place conversion? I thought the idea was to break the series apart to get something basic upstream:
https://lore.kernel.org/all/[email protected]/Adding in-place conversion tests seems to be going in the opposite direction.
We wanted to have this series based on in-place conversion since non-in-place conversion is on a deprecation path. Also, it makes the set up easier :)
At this point I don't think this series is going to make it for 7.3, so at best it makes it for 7.4 (together with in-place conversion), which I think works out?
Sean, let us know what you think about scheduling. We can rebase without in-place conversion if necessary.
Quoted 55 lines
Also, the branch does not build for me:
/usr/bin/ld:.../linux/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:26:
undefined reference to `TD_BOOT_PARAMETERS_PER_VCPU'/usr/bin/ld:.../linux/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:30:
undefined reference to `TD_PER_VCPU_PARAMETERS_ESP_GVA'/usr/bin/ld:.../linux/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:33:
undefined reference to `TD_BOOT_PARAMETERS_GDT'/usr/bin/ld:.../linux/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:37:
undefined reference to `TD_BOOT_PARAMETERS_IDT'/usr/bin/ld:.../linux/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:44:
undefined reference to `TD_BOOT_PARAMETERS_CR4'/usr/bin/ld:.../linux/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:46:
undefined reference to `TD_BOOT_PARAMETERS_CR3'/usr/bin/ld:.../linux/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:48:
undefined reference to `TD_BOOT_PARAMETERS_CR0'/usr/bin/ld:.../linux/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:54:
undefined reference to `TD_PER_VCPU_PARAMETERS_GUEST_CODE'
collect2: error: ld returned 1 exit status
make: *** [Makefile.kvm:351:
.../linux/tools/testing/selftests/kvm/demand_paging_test] Error 1
make: *** Waiting for unfinished jobs..../usr/bin/ld:.../linux/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.o: in function `td_boot':.../linux/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:24: undefined reference to `SIZEOF_TD_PER_VCPU_PARAMETERS'/usr/bin/ld:.../linux/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:26:
undefined reference to `TD_BOOT_PARAMETERS_PER_VCPU'/usr/bin/ld:.../linux/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:30:
undefined reference to `TD_PER_VCPU_PARAMETERS_ESP_GVA'/usr/bin/ld:.../linux/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:33:
undefined reference to `TD_BOOT_PARAMETERS_GDT'/usr/bin/ld:.../linux/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:37:
undefined reference to `TD_BOOT_PARAMETERS_IDT'/usr/bin/ld:.../linux/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:44:
undefined reference to `TD_BOOT_PARAMETERS_CR4'/usr/bin/ld:.../linux/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:46:
undefined reference to `TD_BOOT_PARAMETERS_CR3'/usr/bin/ld:.../linux/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:48:
undefined reference to `TD_BOOT_PARAMETERS_CR0'/usr/bin/ld:.../linux/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:54:
undefined reference to `TD_PER_VCPU_PARAMETERS_GUEST_CODE'
collect2: error: ld returned 1 exit status
make: *** [Makefile.kvm:351:
.../linux/tools/testing/selftests/kvm/dirty_log_test] Error 1If I apply these patches on
8cf3969a0b7d4from the branch, it builds though. It looks like a few changes are missing from the branch:tools/testing/selftests/kvm/guest_memfd_test.c +0
-0
tools/testing/selftests/kvm/guest_memfd_test.c line 17
Quoted 5 lines
tools/testing/selftests/kvm/guest_memfd_test.c +1
-1@@ -14,7 +14,7 @@#include <linux/bitmap.h> #include <linux/falloc.h> #include <linux/sizes.h> -#include <sys/mman.h> +#include <linux/mman.h>
I recall a similar fix [1] you posted, I think both Lisa and I are relying on some LSP auto-including, we'll adopt <linux/mman.h>. Let me know you know a good way to configure the auto-includes.
[1] https://lore.kernel.org/all/[email protected]/
What compiler/version are you using? We'd like to reproduce.
Quoted 9 lines
tools/testing/selftests/kvm/guest_memfd_test.c +0
-0#include <sys/types.h> #include <sys/stat.h> tools/testing/selftests/kvm/lib/x86/tdx/td_boot_offsets.c +0
-0
tools/testing/selftests/kvm/lib/x86/tdx/td_boot_offsets.c line 8
Quoted 5 lines
tools/testing/selftests/kvm/lib/x86/tdx/td_boot_offsets.c +1
-1@@ -5,7 +5,7 @@#include "tdx/td_boot.h" -static void __used common (void ) +static void __attribute__((used))common (void )
Not sure about this, I'll let Lisa look at this in more detail. I wonder if declaring __used is necessary at all.
Quoted 3 lines
tools/testing/selftests/kvm/lib/x86/tdx/td_boot_offsets.c +0
-0{OFFSET (TD_BOOT_PARAMETERS_CR0 , td_boot_parameters, cr0);OFFSET (TD_BOOT_PARAMETERS_CR3 , td_boot_parameters, cr3);
Edgecombe, Rick P
maintainerQuoted 17 lines · Ackerley Tng
Quoted 15 lines · Rick P
Quoted 9 lines · Rick Edgecombe
Quoted 5 lines · Lisa Wang
v14 revision for TDX KVM selftests based on kvm/next and
guest_memfd:
In-place conversion support[1]. This series needs some changes from in-place conversion v9. For ease of testing, an extra hack commit has been added. The complete code is available at:
https://github.com/googleprodkernel/linux-cc/commits/tdx-selftests-v14Are you able to get sashiko review on this? Or did you internally somehow? It seems the public sashiko cannot figure out this stack.
Actually why is this on top of in-place conversion? I thought the idea was to break the series apart to get something basic upstream:
https://lore.kernel.org/all/[email protected]/
Quoted 6 lines
Quoted 3 lines
Adding in-place conversion tests seems to be going in the opposite direction.
We wanted to have this series based on in-place conversion since non-in-place conversion is on a deprecation path.
Hmm, what kind of timeline do you see for the old interface actually getting yanked out of the kernel? I was imagining it would be there for many years at minimum, in which case it would need tests too.
The level of dependencies we have on coco stuff is pretty unfortunate I think. Considering these tests should have ideally quickly followed the TDX base upstreaming, it would be nice if we could avoid it here.
Quoted 1 line
Also, it makes the set up easier :)
Hmm, yea maybe. I'm just wishing to unblock this finally. And so it seems like adding new roadblocks.
Quoted 73 lines
At this point I don't think this series is going to make it for 7.3, so at best it makes it for 7.4 (together with in-place conversion), which I think works out?
Sean, let us know what you think about scheduling. We can rebase without in-place conversion if necessary.
Quoted 55 lines
Also, the branch does not build for me:
/usr/bin/ld:.../linux/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:26:
undefined reference to `TD_BOOT_PARAMETERS_PER_VCPU'/usr/bin/ld:.../linux/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:30:
undefined reference to `TD_PER_VCPU_PARAMETERS_ESP_GVA'/usr/bin/ld:.../linux/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:33:
undefined reference to `TD_BOOT_PARAMETERS_GDT'/usr/bin/ld:.../linux/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:37:
undefined reference to `TD_BOOT_PARAMETERS_IDT'/usr/bin/ld:.../linux/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:44:
undefined reference to `TD_BOOT_PARAMETERS_CR4'/usr/bin/ld:.../linux/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:46:
undefined reference to `TD_BOOT_PARAMETERS_CR3'/usr/bin/ld:.../linux/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:48:
undefined reference to `TD_BOOT_PARAMETERS_CR0'/usr/bin/ld:.../linux/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:54:
undefined reference to `TD_PER_VCPU_PARAMETERS_GUEST_CODE'
collect2: error: ld returned 1 exit status
make: *** [Makefile.kvm:351:
.../linux/tools/testing/selftests/kvm/demand_paging_test] Error 1
make: *** Waiting for unfinished jobs..../usr/bin/ld:.../linux/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.o: in function `td_boot':.../linux/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:24: undefined reference to `SIZEOF_TD_PER_VCPU_PARAMETERS'/usr/bin/ld:.../linux/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:26:
undefined reference to `TD_BOOT_PARAMETERS_PER_VCPU'/usr/bin/ld:.../linux/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:30:
undefined reference to `TD_PER_VCPU_PARAMETERS_ESP_GVA'/usr/bin/ld:.../linux/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:33:
undefined reference to `TD_BOOT_PARAMETERS_GDT'/usr/bin/ld:.../linux/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:37:
undefined reference to `TD_BOOT_PARAMETERS_IDT'/usr/bin/ld:.../linux/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:44:
undefined reference to `TD_BOOT_PARAMETERS_CR4'/usr/bin/ld:.../linux/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:46:
undefined reference to `TD_BOOT_PARAMETERS_CR3'/usr/bin/ld:.../linux/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:48:
undefined reference to `TD_BOOT_PARAMETERS_CR0'/usr/bin/ld:.../linux/tools/testing/selftests/kvm/lib/x86/tdx/td_boot.S:54:
undefined reference to `TD_PER_VCPU_PARAMETERS_GUEST_CODE'
collect2: error: ld returned 1 exit status
make: *** [Makefile.kvm:351:
.../linux/tools/testing/selftests/kvm/dirty_log_test] Error 1If I apply these patches on
8cf3969a0b7d4from the branch, it builds though. It looks like a few changes are missing from the branch:tools/testing/selftests/kvm/guest_memfd_test.c +0
-0tools/testing/selftests/kvm/guest_memfd_test.c line 17
Quoted 5 lines
tools/testing/selftests/kvm/guest_memfd_test.c +1
-1@@ -14,7 +14,7 @@ #include <linux/bitmap.h> #include <linux/falloc.h> #include <linux/sizes.h> -#include <sys/mman.h> +#include <linux/mman.h> I recall a similar fix [1] you posted, I think both Lisa and I are relying on some LSP auto-including, we'll adopt <
linux/mman.h>. Let me know you know a good way to configure the auto-includes.
Quoted 4 lines
[1]
https://lore.kernel.org/all/[email protected]/What compiler/version are you using? We'd like to reproduce.
I don't have access to that computer today. I'll report back when I do. But it seems the issue was caught and fixed in these posted patches. Just not in the linked branch. So somehow it was noticed?
Quoted 22 lines
Quoted 10 lines
tools/testing/selftests/kvm/guest_memfd_test.c +0
-0#include <sys/types.h> #include <sys/stat.h>tools/testing/selftests/kvm/lib/x86/tdx/td_boot_offsets.c +0
-0tools/testing/selftests/kvm/lib/x86/tdx/td_boot_offsets.c line 8
Quoted 5 lines
tools/testing/selftests/kvm/lib/x86/tdx/td_boot_offsets.c +1
-1@@ -5,7 +5,7 @@ #include "tdx/td_boot.h" -static void __used common (void ) +static void __attribute__((used))common (void )Not sure about this, I'll let Lisa look at this in more detail. I wonder if declaring
__usedis necessary at all.Quoted 3 lines
tools/testing/selftests/kvm/lib/x86/tdx/td_boot_offsets.c +0
-0{ OFFSET(TD_BOOT_PARAMETERS_CR0, td_boot_parameters, cr0); OFFSET(TD_BOOT_PARAMETERS_CR3, td_boot_parameters, cr3);
Edgecombe, Rick P
maintainerQuoted 11 lines · Rick P
Quoted 10 lines
Quoted 7 lines
Actually why is this on top of in-place conversion? I thought the idea was to break the series apart to get something basic upstream:
https://lore.kernel.org/all/[email protected]/Adding in-place conversion tests seems to be going in the opposite direction.
We wanted to have this series based on in-place conversion since non-in-place conversion is on a deprecation path.
Quoted 7 lines
Hmm, what kind of timeline do you see for the old interface actually getting yanked out of the kernel? I was imagining it would be there for many years at minimum, in which case it would need tests too.
The level of dependencies we have on coco stuff is pretty unfortunate I think. Considering these tests should have ideally quickly followed the TDX base upstreaming, it would be nice if we could avoid it here.
I saw the new version is also based on in-place conversion (which is now headed upstream, congrats!). But can we talk about the legacy deprecation? Is there a plan to actually deprecate and remove it, or even move qemu off of the old interface?
Otherwise, I disagree with the plan to drop the tests for the old interface. That said I'd would be thrilled to have anything upstream, so if the set can be smaller or faster that way, then I agree with the change.
But in general, I'd like to know the plan for the old interface.
Quoted 20 lines · Rick P
Quoted 11 lines · Rick P
Quoted 10 lines
Quoted 7 lines
Actually why is this on top of in-place conversion? I thought the idea was to break the series apart to get something basic upstream:
https://lore.kernel.org/all/[email protected]/Adding in-place conversion tests seems to be going in the opposite direction.
We wanted to have this series based on in-place conversion since non-in-place conversion is on a deprecation path.
Quoted 7 lines
Hmm, what kind of timeline do you see for the old interface actually getting yanked out of the kernel? I was imagining it would be there for many years at minimum, in which case it would need tests too.
The level of dependencies we have on coco stuff is pretty unfortunate I think. Considering these tests should have ideally quickly followed the TDX base upstreaming, it would be nice if we could avoid it here.
Quoted 8 lines
I saw the new version is also based on in-place conversion (which is now headed upstream, congrats!). But can we talk about the legacy deprecation? Is there a plan to actually deprecate and remove it, or even move qemu off of the old interface?
Otherwise, I disagree with the plan to drop the tests for the old interface. That said I'd would be thrilled to have anything upstream, so if the set can be smaller or faster that way, then I agree with the change.
The v15 actually supports both: out-of-place conversion (per-VM memory attributes) and in-place conversion.
Quoted 1 line
But in general, I'd like to know the plan for the old interface.
Sean talked something about it:
https://lore.kernel.org/all/[email protected]/
Edgecombe, Rick P
maintainerQuoted 7 lines · Xiaoyao Li
Quoted 4 lines
Otherwise, I disagree with the plan to drop the tests for the old interface. That said I'd would be thrilled to have anything upstream, so if the set can be
smaller or faster that way, then I agree with the change.The v15 actually supports both: out-of-place conversion (per-VM memory attributes) and in-place conversion.
Oh, hmm... I'll take a look. Previously the direction was to break it apart into smaller pieces. So I would have assumed that would have meant supporting only one in the initial set.
Quoted 5 lines
Quoted 2 lines
But in general, I'd like to know the plan for the old interface.
Sean talked something about it:
https://lore.kernel.org/all/[email protected]/
tools/testing/selftests/kvm/lib/x86/tdx/td_boot_offsets.c line 11
Quoted 12 lines · Rick P
tools/testing/selftests/kvm/lib/x86/tdx/td_boot_offsets.c +1
-1@@ -5,7 +5,7 @@#include "tdx/td_boot.h" -static void __used common (void ) +static void __attribute__((used))common (void ) {OFFSET (TD_BOOT_PARAMETERS_CR0 , td_boot_parameters, cr0);OFFSET (TD_BOOT_PARAMETERS_CR3 , td_boot_parameters, cr3);
Thanks for testing this! The patch series posted here on the mailing list is correct, but I made the mistake when pushing the code to the branch.
I have just force-pushed the fix to the branch. It should compile correctly now. Could you please pull the latest version and give it another try?
Lisa