On Wed, Aug 20, 2025 at 09:29:03PM -0700, Sagi Shahar wrote: > 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 vectors > 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. > > Suggested-by: Sean Christopherson <sea...@google.com> > Co-developed-by: Erdem Aktas <erdemak...@google.com> > Signed-off-by: Erdem Aktas <erdemak...@google.com> > Signed-off-by: Sagi Shahar <sa...@google.com> > --- > tools/testing/selftests/kvm/Makefile.kvm | 1 + > .../selftests/kvm/include/x86/tdx/tdx_util.h | 2 + > .../selftests/kvm/lib/x86/tdx/tdx_util.c | 54 +++++++++++++++++++ > 3 files changed, 57 insertions(+) > create mode 100644 tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c > > diff --git a/tools/testing/selftests/kvm/Makefile.kvm > b/tools/testing/selftests/kvm/Makefile.kvm > index 03754ce2e983..c42b579fb7c5 100644 > --- a/tools/testing/selftests/kvm/Makefile.kvm > +++ b/tools/testing/selftests/kvm/Makefile.kvm > @@ -31,6 +31,7 @@ LIBKVM_x86 += lib/x86/sev.c > LIBKVM_x86 += lib/x86/svm.c > LIBKVM_x86 += lib/x86/ucall.c > LIBKVM_x86 += lib/x86/vmx.c > +LIBKVM_x86 += lib/x86/tdx/tdx_util.c > LIBKVM_x86 += lib/x86/tdx/td_boot.S > > LIBKVM_arm64 += lib/arm64/gic.c > diff --git a/tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h > b/tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h > index 286d5e3c24b1..ec05bcd59145 100644 > --- a/tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h > +++ b/tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h > @@ -11,4 +11,6 @@ static inline bool is_tdx_vm(struct kvm_vm *vm) > return vm->type == KVM_X86_TDX_VM; > } > > +void vm_tdx_setup_boot_code_region(struct kvm_vm *vm); > + > #endif // SELFTESTS_TDX_TDX_UTIL_H > diff --git a/tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c > b/tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c > new file mode 100644 > index 000000000000..15833b9eb5d5 > --- /dev/null > +++ b/tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c > @@ -0,0 +1,54 @@ > +// SPDX-License-Identifier: GPL-2.0-only > + > +#include <stdint.h> > + > +#include "kvm_util.h" > +#include "processor.h" > +#include "tdx/td_boot.h" > +#include "tdx/tdx_util.h" > + > +/* Arbitrarily selected to avoid overlaps with anything else */ > +#define TD_BOOT_CODE_SLOT 20 > + > +#define X86_RESET_VECTOR 0xfffffff0ul > +#define X86_RESET_VECTOR_SIZE 16 > + > +void vm_tdx_setup_boot_code_region(struct kvm_vm *vm) > +{ > + size_t total_code_size = TD_BOOT_CODE_SIZE + X86_RESET_VECTOR_SIZE; > + vm_paddr_t boot_code_gpa = X86_RESET_VECTOR - TD_BOOT_CODE_SIZE; > + vm_paddr_t alloc_gpa = round_down(boot_code_gpa, PAGE_SIZE); > + size_t nr_pages = DIV_ROUND_UP(total_code_size, PAGE_SIZE); > + vm_paddr_t gpa; > + uint8_t *hva; > + > + vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, > + alloc_gpa, > + TD_BOOT_CODE_SLOT, nr_pages, > + KVM_MEM_GUEST_MEMFD); > + > + 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 > + * 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 < 256, Looks TD_BOOT_CODE_SIZE needs to be <= 126, as the jump range is limited to -128 to +127 for JMP rel8.
> + "TD boot code not addressable by 'JMP rel8'"); > + hva[0] = 0xeb; > + hva[1] = 256 - 2 - TD_BOOT_CODE_SIZE; > + hva[2] = 0xcc; > +} > -- > 2.51.0.rc1.193.gad69d77794-goog > >