new file mode 100644
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef _TOOLS_LINUX_ASM_ARM64_KVM_HOST_H
+#define _TOOLS_LINUX_ASM_ARM64_KVM_HOST_H
+
+struct kvm_vm_arch {};
+
+#endif // _TOOLS_LINUX_ASM_ARM64_KVM_HOST_H
new file mode 100644
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef _TOOLS_LINUX_ASM_RISCV_KVM_HOST_H
+#define _TOOLS_LINUX_ASM_RISCV_KVM_HOST_H
+
+struct kvm_vm_arch {};
+
+#endif // _TOOLS_LINUX_ASM_RISCV_KVM_HOST_H
new file mode 100644
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef _TOOLS_LINUX_ASM_S390_KVM_HOST_H
+#define _TOOLS_LINUX_ASM_S390_KVM_HOST_H
+
+struct kvm_vm_arch {};
+
+#endif // _TOOLS_LINUX_ASM_S390_KVM_HOST_H
new file mode 100644
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef _TOOLS_LINUX_ASM_X86_KVM_HOST_H
+#define _TOOLS_LINUX_ASM_X86_KVM_HOST_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+struct kvm_vm_arch {
+ uint64_t c_bit;
+ uint64_t s_bit;
+};
+
+#endif // _TOOLS_LINUX_ASM_X86_KVM_HOST_H
@@ -18,6 +18,8 @@
#include <linux/types.h>
#include <asm/atomic.h>
+#include <asm/kvm.h>
+#include <asm/kvm_host.h>
#include <sys/ioctl.h>
@@ -155,6 +157,9 @@ struct kvm_vm {
vm_vaddr_t idt;
vm_vaddr_t handlers;
uint32_t dirty_ring_size;
+ uint64_t gpa_tag_mask;
+
+ struct kvm_vm_arch arch;
/* VM protection enabled: SEV, etc*/
bool protected;
@@ -489,6 +494,12 @@ void *addr_gva2hva(struct kvm_vm *vm, vm_vaddr_t gva);
vm_paddr_t addr_hva2gpa(struct kvm_vm *vm, void *hva);
void *addr_gpa2alias(struct kvm_vm *vm, vm_paddr_t gpa);
+
+static inline vm_paddr_t vm_untag_gpa(struct kvm_vm *vm, vm_paddr_t gpa)
+{
+ return gpa & ~vm->gpa_tag_mask;
+}
+
void vcpu_run(struct kvm_vcpu *vcpu);
int _vcpu_run(struct kvm_vcpu *vcpu);
@@ -967,4 +978,6 @@ void kvm_selftest_arch_init(void);
void kvm_arch_vm_post_create(struct kvm_vm *vm);
+bool vm_is_gpa_protected(struct kvm_vm *vm, vm_paddr_t paddr);
+
#endif /* SELFTEST_KVM_UTIL_BASE_H */
@@ -165,10 +165,11 @@ const char *vm_guest_mode_string(uint32_t i)
};
_Static_assert(sizeof(strings)/sizeof(char *) == NUM_VM_MODES,
"Missing new mode strings?");
+ const uint32_t mode = i & VM_MODE_PRIMARY_MASK;
- TEST_ASSERT(i < NUM_VM_MODES, "Guest mode ID %d too big", i);
+ TEST_ASSERT(mode < NUM_VM_MODES, "Guest mode ID %d too big", mode);
- return strings[i];
+ return strings[mode];
}
const struct vm_guest_mode_params vm_guest_mode_params[] = {
@@ -315,7 +316,7 @@ static uint64_t vm_nr_pages_required(uint32_t mode,
uint32_t nr_runnable_vcpus,
uint64_t extra_mem_pages)
{
- uint64_t page_size = vm_guest_mode_params[mode].page_size;
+ uint64_t page_size = vm_guest_mode_params[mode & VM_MODE_PRIMARY_MASK].page_size;
uint64_t nr_pages;
TEST_ASSERT(nr_runnable_vcpus,
@@ -1485,6 +1486,8 @@ void *addr_gpa2hva(struct kvm_vm *vm, vm_paddr_t gpa)
{
struct userspace_mem_region *region;
+ gpa = vm_untag_gpa(vm, gpa);
+
region = userspace_mem_region_find(vm, gpa, gpa);
if (!region) {
TEST_FAIL("No vm physical memory at 0x%lx", gpa);
@@ -2190,3 +2193,21 @@ void __attribute((constructor)) kvm_selftest_init(void)
kvm_selftest_arch_init();
}
+
+bool vm_is_gpa_protected(struct kvm_vm *vm, vm_paddr_t paddr)
+{
+ sparsebit_idx_t pg = 0;
+ struct userspace_mem_region *region;
+
+ if (!vm->protected)
+ return false;
+
+ region = userspace_mem_region_find(vm, paddr, paddr);
+ if (!region) {
+ TEST_FAIL("No vm physical memory at 0x%lx", paddr);
+ return false;
+ }
+
+ pg = paddr >> vm->page_shift;
+ return sparsebit_is_set(region->protected_phy_pages, pg);
+}
@@ -157,6 +157,8 @@ static uint64_t *virt_create_upper_pte(struct kvm_vm *vm,
{
uint64_t *pte = virt_get_pte(vm, parent_pte, vaddr, current_level);
+ paddr = vm_untag_gpa(vm, paddr);
+
if (!(*pte & PTE_PRESENT_MASK)) {
*pte = PTE_PRESENT_MASK | PTE_WRITABLE_MASK;
if (current_level == target_level)
@@ -200,6 +202,8 @@ void __virt_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr, int level)
"Physical address beyond maximum supported,\n"
" paddr: 0x%lx vm->max_gfn: 0x%lx vm->page_size: 0x%x",
paddr, vm->max_gfn, vm->page_size);
+ TEST_ASSERT(vm_untag_gpa(vm, paddr) == paddr,
+ "Unexpected bits in paddr: %lx", paddr);
/*
* Allocate upper level page tables, if not already present. Return
@@ -222,6 +226,15 @@ void __virt_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr, int level)
TEST_ASSERT(!(*pte & PTE_PRESENT_MASK),
"PTE already present for 4k page at vaddr: 0x%lx\n", vaddr);
*pte = PTE_PRESENT_MASK | PTE_WRITABLE_MASK | (paddr & PHYSICAL_PAGE_MASK);
+
+ /*
+ * Neither SEV nor TDX supports shared page tables, so only the final
+ * leaf PTE needs manually set the C/S-bit.
+ */
+ if (vm_is_gpa_protected(vm, paddr))
+ *pte |= vm->arch.c_bit;
+ else
+ *pte |= vm->arch.s_bit;
}
void virt_arch_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr)
@@ -496,7 +509,7 @@ vm_paddr_t addr_arch_gva2gpa(struct kvm_vm *vm, vm_vaddr_t gva)
* No need for a hugepage mask on the PTE, x86-64 requires the "unused"
* address bits to be zero.
*/
- return PTE_GET_PA(*pte) | (gva & ~HUGEPAGE_MASK(level));
+ return vm_untag_gpa(vm, PTE_GET_PA(*pte)) | (gva & ~HUGEPAGE_MASK(level));
}
static void kvm_setup_gdt(struct kvm_vm *vm, struct kvm_dtable *dt)
SEV guests rely on an encyption bit (C-bit) which resides within the physical address range, i.e. is stolen from the guest's GPA space. Guest code in selftests will expect the C-Bit to be set appropriately in the guest's page tables, whereas the rest of the kvm_util functions will generally expect these bits to not be present. Introduce pte_me_mask and struct kvm_vm_arch to allow for arch specific address tagging. Currently just adding x86 c_bit and s_bit support for SEV and TDX. 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> Originally-by: Michael Roth <michael.roth@amd.com> Signed-off-by: Peter Gonda <pgonda@google.com> --- tools/arch/arm64/include/asm/kvm_host.h | 7 +++++ tools/arch/riscv/include/asm/kvm_host.h | 7 +++++ tools/arch/s390/include/asm/kvm_host.h | 7 +++++ tools/arch/x86/include/asm/kvm_host.h | 13 +++++++++ .../selftests/kvm/include/kvm_util_base.h | 13 +++++++++ tools/testing/selftests/kvm/lib/kvm_util.c | 27 ++++++++++++++++--- .../selftests/kvm/lib/x86_64/processor.c | 15 ++++++++++- 7 files changed, 85 insertions(+), 4 deletions(-) create mode 100644 tools/arch/arm64/include/asm/kvm_host.h create mode 100644 tools/arch/riscv/include/asm/kvm_host.h create mode 100644 tools/arch/s390/include/asm/kvm_host.h create mode 100644 tools/arch/x86/include/asm/kvm_host.h