Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

KVM: selftests: Allow tagging protected memory in guest page tables

Add support for tagging and untagging guest physical address, e.g. to
allow x86's SEV and TDX guests to embed shared vs. private information in
the GPA. SEV (encryption, a.k.a. C-bit) and TDX (shared, a.k.a. S-bit)
steal bits from the guest's physical address space that is consumed by the
CPU metadata, i.e. effectively aliases the "real" GPA.

Implement generic "tagging" so that the shared vs. private metadata can be
managed by x86 without bleeding too many details into common code.

Cc: Paolo Bonzini <pbonzini@redhat.com>
Cc: Sean Christopherson <seanjc@google.com>
Cc: Vishal Annapurve <vannapurve@google.com>
Cc: Ackerly Tng <ackerleytng@google.com>
cc: Andrew Jones <andrew.jones@linux.dev>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Michael Roth <michael.roth@amd.com>
Tested-by: Carlos Bilbao <carlos.bilbao@amd.com>
Originally-by: Michael Roth <michael.roth@amd.com>
Signed-off-by: Peter Gonda <pgonda@google.com>
Link: https://lore.kernel.org/r/20240223004258.3104051-8-seanjc@google.com
Signed-off-by: Sean Christopherson <seanjc@google.com>

authored by

Peter Gonda and committed by
Sean Christopherson
be1bd4c5 31e00dae

+86 -1
+7
tools/testing/selftests/kvm/include/aarch64/kvm_util_arch.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + #ifndef SELFTEST_KVM_UTIL_ARCH_H 3 + #define SELFTEST_KVM_UTIL_ARCH_H 4 + 5 + struct kvm_vm_arch {}; 6 + 7 + #endif // SELFTEST_KVM_UTIL_ARCH_H
+13
tools/testing/selftests/kvm/include/kvm_util_base.h
··· 18 18 #include <linux/types.h> 19 19 20 20 #include <asm/atomic.h> 21 + #include <asm/kvm.h> 21 22 22 23 #include <sys/ioctl.h> 23 24 25 + #include "kvm_util_arch.h" 24 26 #include "sparsebit.h" 25 27 26 28 /* ··· 115 113 vm_vaddr_t idt; 116 114 vm_vaddr_t handlers; 117 115 uint32_t dirty_ring_size; 116 + uint64_t gpa_tag_mask; 117 + 118 + struct kvm_vm_arch arch; 118 119 119 120 /* Cache of information for binary stats interface */ 120 121 int stats_fd; ··· 605 600 void *addr_gva2hva(struct kvm_vm *vm, vm_vaddr_t gva); 606 601 vm_paddr_t addr_hva2gpa(struct kvm_vm *vm, void *hva); 607 602 void *addr_gpa2alias(struct kvm_vm *vm, vm_paddr_t gpa); 603 + 604 + 605 + static inline vm_paddr_t vm_untag_gpa(struct kvm_vm *vm, vm_paddr_t gpa) 606 + { 607 + return gpa & ~vm->gpa_tag_mask; 608 + } 608 609 609 610 void vcpu_run(struct kvm_vcpu *vcpu); 610 611 int _vcpu_run(struct kvm_vcpu *vcpu); ··· 1123 1112 void kvm_selftest_arch_init(void); 1124 1113 1125 1114 void kvm_arch_vm_post_create(struct kvm_vm *vm); 1115 + 1116 + bool vm_is_gpa_protected(struct kvm_vm *vm, vm_paddr_t paddr); 1126 1117 1127 1118 #endif /* SELFTEST_KVM_UTIL_BASE_H */
+7
tools/testing/selftests/kvm/include/riscv/kvm_util_arch.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + #ifndef SELFTEST_KVM_UTIL_ARCH_H 3 + #define SELFTEST_KVM_UTIL_ARCH_H 4 + 5 + struct kvm_vm_arch {}; 6 + 7 + #endif // SELFTEST_KVM_UTIL_ARCH_H
+7
tools/testing/selftests/kvm/include/s390x/kvm_util_arch.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + #ifndef SELFTEST_KVM_UTIL_ARCH_H 3 + #define SELFTEST_KVM_UTIL_ARCH_H 4 + 5 + struct kvm_vm_arch {}; 6 + 7 + #endif // SELFTEST_KVM_UTIL_ARCH_H
+21
tools/testing/selftests/kvm/include/x86_64/kvm_util_arch.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + #ifndef SELFTEST_KVM_UTIL_ARCH_H 3 + #define SELFTEST_KVM_UTIL_ARCH_H 4 + 5 + #include <stdbool.h> 6 + #include <stdint.h> 7 + 8 + struct kvm_vm_arch { 9 + uint64_t c_bit; 10 + uint64_t s_bit; 11 + }; 12 + 13 + static inline bool __vm_arch_has_protected_memory(struct kvm_vm_arch *arch) 14 + { 15 + return arch->c_bit || arch->s_bit; 16 + } 17 + 18 + #define vm_arch_has_protected_memory(vm) \ 19 + __vm_arch_has_protected_memory(&(vm)->arch) 20 + 21 + #endif // SELFTEST_KVM_UTIL_ARCH_H
+17
tools/testing/selftests/kvm/lib/kvm_util.c
··· 1546 1546 { 1547 1547 struct userspace_mem_region *region; 1548 1548 1549 + gpa = vm_untag_gpa(vm, gpa); 1550 + 1549 1551 region = userspace_mem_region_find(vm, gpa, gpa); 1550 1552 if (!region) { 1551 1553 TEST_FAIL("No vm physical memory at 0x%lx", gpa); ··· 2255 2253 setbuf(stdout, NULL); 2256 2254 2257 2255 kvm_selftest_arch_init(); 2256 + } 2257 + 2258 + bool vm_is_gpa_protected(struct kvm_vm *vm, vm_paddr_t paddr) 2259 + { 2260 + sparsebit_idx_t pg = 0; 2261 + struct userspace_mem_region *region; 2262 + 2263 + if (!vm_arch_has_protected_memory(vm)) 2264 + return false; 2265 + 2266 + region = userspace_mem_region_find(vm, paddr, paddr); 2267 + TEST_ASSERT(region, "No vm physical memory at 0x%lx", paddr); 2268 + 2269 + pg = paddr >> vm->page_shift; 2270 + return sparsebit_is_set(region->protected_phy_pages, pg); 2258 2271 }
+14 -1
tools/testing/selftests/kvm/lib/x86_64/processor.c
··· 157 157 { 158 158 uint64_t *pte = virt_get_pte(vm, parent_pte, vaddr, current_level); 159 159 160 + paddr = vm_untag_gpa(vm, paddr); 161 + 160 162 if (!(*pte & PTE_PRESENT_MASK)) { 161 163 *pte = PTE_PRESENT_MASK | PTE_WRITABLE_MASK; 162 164 if (current_level == target_level) ··· 202 200 "Physical address beyond maximum supported,\n" 203 201 " paddr: 0x%lx vm->max_gfn: 0x%lx vm->page_size: 0x%x", 204 202 paddr, vm->max_gfn, vm->page_size); 203 + TEST_ASSERT(vm_untag_gpa(vm, paddr) == paddr, 204 + "Unexpected bits in paddr: %lx", paddr); 205 205 206 206 /* 207 207 * Allocate upper level page tables, if not already present. Return ··· 226 222 TEST_ASSERT(!(*pte & PTE_PRESENT_MASK), 227 223 "PTE already present for 4k page at vaddr: 0x%lx\n", vaddr); 228 224 *pte = PTE_PRESENT_MASK | PTE_WRITABLE_MASK | (paddr & PHYSICAL_PAGE_MASK); 225 + 226 + /* 227 + * Neither SEV nor TDX supports shared page tables, so only the final 228 + * leaf PTE needs manually set the C/S-bit. 229 + */ 230 + if (vm_is_gpa_protected(vm, paddr)) 231 + *pte |= vm->arch.c_bit; 232 + else 233 + *pte |= vm->arch.s_bit; 229 234 } 230 235 231 236 void virt_arch_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr) ··· 509 496 * No need for a hugepage mask on the PTE, x86-64 requires the "unused" 510 497 * address bits to be zero. 511 498 */ 512 - return PTE_GET_PA(*pte) | (gva & ~HUGEPAGE_MASK(level)); 499 + return vm_untag_gpa(vm, PTE_GET_PA(*pte)) | (gva & ~HUGEPAGE_MASK(level)); 513 500 } 514 501 515 502 static void kvm_setup_gdt(struct kvm_vm *vm, struct kvm_dtable *dt)