]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
KVM: arm64: Extend pKVM selftest for np-guests
authorQuentin Perret <qperret@google.com>
Wed, 16 Apr 2025 16:09:00 +0000 (16:09 +0000)
committerMarc Zyngier <maz@kernel.org>
Tue, 6 May 2025 08:56:18 +0000 (09:56 +0100)
The pKVM selftest intends to test as many memory 'transitions' as
possible, so extend it to cover sharing pages with non-protected guests,
including in the case of multi-sharing.

Signed-off-by: Quentin Perret <qperret@google.com>
Link: https://lore.kernel.org/r/20250416160900.3078417-5-qperret@google.com
Signed-off-by: Marc Zyngier <maz@kernel.org>
arch/arm64/include/asm/kvm_pkvm.h
arch/arm64/kvm/hyp/include/nvhe/mem_protect.h
arch/arm64/kvm/hyp/nvhe/mem_protect.c
arch/arm64/kvm/hyp/nvhe/setup.c
arch/arm64/kvm/pkvm.c

index abd693ce5b93e628c74bf187d0bdfbc2ba687ffb..d91bfcf2db56db32d37aed1497aa84d1a22e7808 100644 (file)
@@ -135,6 +135,12 @@ static inline unsigned long host_s2_pgtable_pages(void)
        return res;
 }
 
+#ifdef CONFIG_NVHE_EL2_DEBUG
+static inline unsigned long pkvm_selftest_pages(void) { return 32; }
+#else
+static inline unsigned long pkvm_selftest_pages(void) { return 0; }
+#endif
+
 #define KVM_FFA_MBOX_NR_PAGES  1
 
 static inline unsigned long hyp_ffa_proxy_pages(void)
index 535722cd8417e2c4141548166200f28844dd8efc..26016eb9323fa49e3b386897f6f9874949b4514e 100644 (file)
@@ -69,8 +69,8 @@ static __always_inline void __load_host_stage2(void)
 }
 
 #ifdef CONFIG_NVHE_EL2_DEBUG
-void pkvm_ownership_selftest(void);
+void pkvm_ownership_selftest(void *base);
 #else
-static inline void pkvm_ownership_selftest(void) { }
+static inline void pkvm_ownership_selftest(void *base) { }
 #endif
 #endif /* __KVM_NVHE_MEM_PROTECT__ */
index aee9359f5b9f7413cd642aa764ef82869e2cb9fe..f726a5a7c6e2c379a5a99f67bbaa6c787a319912 100644 (file)
@@ -1095,16 +1095,60 @@ int __pkvm_host_mkyoung_guest(u64 gfn, struct pkvm_hyp_vcpu *vcpu)
 struct pkvm_expected_state {
        enum pkvm_page_state host;
        enum pkvm_page_state hyp;
+       enum pkvm_page_state guest[2]; /* [ gfn, gfn + 1 ] */
 };
 
 static struct pkvm_expected_state selftest_state;
 static struct hyp_page *selftest_page;
 
+static struct pkvm_hyp_vm selftest_vm = {
+       .kvm = {
+               .arch = {
+                       .mmu = {
+                               .arch = &selftest_vm.kvm.arch,
+                               .pgt = &selftest_vm.pgt,
+                       },
+               },
+       },
+};
+
+static struct pkvm_hyp_vcpu selftest_vcpu = {
+       .vcpu = {
+               .arch = {
+                       .hw_mmu = &selftest_vm.kvm.arch.mmu,
+               },
+               .kvm = &selftest_vm.kvm,
+       },
+};
+
+static void init_selftest_vm(void *virt)
+{
+       struct hyp_page *p = hyp_virt_to_page(virt);
+       int i;
+
+       selftest_vm.kvm.arch.mmu.vtcr = host_mmu.arch.mmu.vtcr;
+       WARN_ON(kvm_guest_prepare_stage2(&selftest_vm, virt));
+
+       for (i = 0; i < pkvm_selftest_pages(); i++) {
+               if (p[i].refcount)
+                       continue;
+               p[i].refcount = 1;
+               hyp_put_page(&selftest_vm.pool, hyp_page_to_virt(&p[i]));
+       }
+}
+
+static u64 selftest_ipa(void)
+{
+       return BIT(selftest_vm.pgt.ia_bits - 1);
+}
+
 static void assert_page_state(void)
 {
        void *virt = hyp_page_to_virt(selftest_page);
        u64 size = PAGE_SIZE << selftest_page->order;
+       struct pkvm_hyp_vcpu *vcpu = &selftest_vcpu;
        u64 phys = hyp_virt_to_phys(virt);
+       u64 ipa[2] = { selftest_ipa(), selftest_ipa() + PAGE_SIZE };
 
        host_lock_component();
        WARN_ON(__host_check_page_state_range(phys, size, selftest_state.host));
@@ -1113,6 +1157,11 @@ static void assert_page_state(void)
        hyp_lock_component();
        WARN_ON(__hyp_check_page_state_range(phys, size, selftest_state.hyp));
        hyp_unlock_component();
+
+       guest_lock_component(&selftest_vm);
+       WARN_ON(__guest_check_page_state_range(vcpu, ipa[0], size, selftest_state.guest[0]));
+       WARN_ON(__guest_check_page_state_range(vcpu, ipa[1], size, selftest_state.guest[1]));
+       guest_unlock_component(&selftest_vm);
 }
 
 #define assert_transition_res(res, fn, ...)            \
@@ -1121,21 +1170,27 @@ static void assert_page_state(void)
                assert_page_state();                    \
        } while (0)
 
-void pkvm_ownership_selftest(void)
+void pkvm_ownership_selftest(void *base)
 {
+       enum kvm_pgtable_prot prot = KVM_PGTABLE_PROT_RWX;
        void *virt = hyp_alloc_pages(&host_s2_pool, 0);
-       u64 phys, size, pfn;
+       struct pkvm_hyp_vcpu *vcpu = &selftest_vcpu;
+       struct pkvm_hyp_vm *vm = &selftest_vm;
+       u64 phys, size, pfn, gfn;
 
        WARN_ON(!virt);
        selftest_page = hyp_virt_to_page(virt);
        selftest_page->refcount = 0;
+       init_selftest_vm(base);
 
        size = PAGE_SIZE << selftest_page->order;
        phys = hyp_virt_to_phys(virt);
        pfn = hyp_phys_to_pfn(phys);
+       gfn = hyp_phys_to_pfn(selftest_ipa());
 
        selftest_state.host = PKVM_NOPAGE;
        selftest_state.hyp = PKVM_PAGE_OWNED;
+       selftest_state.guest[0] = selftest_state.guest[1] = PKVM_NOPAGE;
        assert_page_state();
        assert_transition_res(-EPERM,   __pkvm_host_donate_hyp, pfn, 1);
        assert_transition_res(-EPERM,   __pkvm_host_share_hyp, pfn);
@@ -1143,6 +1198,8 @@ void pkvm_ownership_selftest(void)
        assert_transition_res(-EPERM,   __pkvm_host_share_ffa, pfn, 1);
        assert_transition_res(-EPERM,   __pkvm_host_unshare_ffa, pfn, 1);
        assert_transition_res(-EPERM,   hyp_pin_shared_mem, virt, virt + size);
+       assert_transition_res(-EPERM,   __pkvm_host_share_guest, pfn, gfn, vcpu, prot);
+       assert_transition_res(-ENOENT,  __pkvm_host_unshare_guest, gfn, vm);
 
        selftest_state.host = PKVM_PAGE_OWNED;
        selftest_state.hyp = PKVM_NOPAGE;
@@ -1150,6 +1207,7 @@ void pkvm_ownership_selftest(void)
        assert_transition_res(-EPERM,   __pkvm_hyp_donate_host, pfn, 1);
        assert_transition_res(-EPERM,   __pkvm_host_unshare_hyp, pfn);
        assert_transition_res(-EPERM,   __pkvm_host_unshare_ffa, pfn, 1);
+       assert_transition_res(-ENOENT,  __pkvm_host_unshare_guest, gfn, vm);
        assert_transition_res(-EPERM,   hyp_pin_shared_mem, virt, virt + size);
 
        selftest_state.host = PKVM_PAGE_SHARED_OWNED;
@@ -1159,6 +1217,8 @@ void pkvm_ownership_selftest(void)
        assert_transition_res(-EPERM,   __pkvm_host_donate_hyp, pfn, 1);
        assert_transition_res(-EPERM,   __pkvm_host_share_ffa, pfn, 1);
        assert_transition_res(-EPERM,   __pkvm_hyp_donate_host, pfn, 1);
+       assert_transition_res(-EPERM,   __pkvm_host_share_guest, pfn, gfn, vcpu, prot);
+       assert_transition_res(-ENOENT,  __pkvm_host_unshare_guest, gfn, vm);
 
        assert_transition_res(0,        hyp_pin_shared_mem, virt, virt + size);
        assert_transition_res(0,        hyp_pin_shared_mem, virt, virt + size);
@@ -1169,6 +1229,8 @@ void pkvm_ownership_selftest(void)
        assert_transition_res(-EPERM,   __pkvm_host_donate_hyp, pfn, 1);
        assert_transition_res(-EPERM,   __pkvm_host_share_ffa, pfn, 1);
        assert_transition_res(-EPERM,   __pkvm_hyp_donate_host, pfn, 1);
+       assert_transition_res(-EPERM,   __pkvm_host_share_guest, pfn, gfn, vcpu, prot);
+       assert_transition_res(-ENOENT,  __pkvm_host_unshare_guest, gfn, vm);
 
        hyp_unpin_shared_mem(virt, virt + size);
        assert_page_state();
@@ -1186,6 +1248,8 @@ void pkvm_ownership_selftest(void)
        assert_transition_res(-EPERM,   __pkvm_host_share_hyp, pfn);
        assert_transition_res(-EPERM,   __pkvm_host_unshare_hyp, pfn);
        assert_transition_res(-EPERM,   __pkvm_hyp_donate_host, pfn, 1);
+       assert_transition_res(-EPERM,   __pkvm_host_share_guest, pfn, gfn, vcpu, prot);
+       assert_transition_res(-ENOENT,  __pkvm_host_unshare_guest, gfn, vm);
        assert_transition_res(-EPERM,   hyp_pin_shared_mem, virt, virt + size);
 
        selftest_state.host = PKVM_PAGE_OWNED;
@@ -1193,6 +1257,28 @@ void pkvm_ownership_selftest(void)
        assert_transition_res(0,        __pkvm_host_unshare_ffa, pfn, 1);
        assert_transition_res(-EPERM,   __pkvm_host_unshare_ffa, pfn, 1);
 
+       selftest_state.host = PKVM_PAGE_SHARED_OWNED;
+       selftest_state.guest[0] = PKVM_PAGE_SHARED_BORROWED;
+       assert_transition_res(0,        __pkvm_host_share_guest, pfn, gfn, vcpu, prot);
+       assert_transition_res(-EPERM,   __pkvm_host_share_guest, pfn, gfn, vcpu, prot);
+       assert_transition_res(-EPERM,   __pkvm_host_share_ffa, pfn, 1);
+       assert_transition_res(-EPERM,   __pkvm_host_donate_hyp, pfn, 1);
+       assert_transition_res(-EPERM,   __pkvm_host_share_hyp, pfn);
+       assert_transition_res(-EPERM,   __pkvm_host_unshare_hyp, pfn);
+       assert_transition_res(-EPERM,   __pkvm_hyp_donate_host, pfn, 1);
+       assert_transition_res(-EPERM,   hyp_pin_shared_mem, virt, virt + size);
+
+       selftest_state.guest[1] = PKVM_PAGE_SHARED_BORROWED;
+       assert_transition_res(0,        __pkvm_host_share_guest, pfn, gfn + 1, vcpu, prot);
+       WARN_ON(hyp_virt_to_page(virt)->host_share_guest_count != 2);
+
+       selftest_state.guest[0] = PKVM_NOPAGE;
+       assert_transition_res(0,        __pkvm_host_unshare_guest, gfn, vm);
+
+       selftest_state.guest[1] = PKVM_NOPAGE;
+       selftest_state.host = PKVM_PAGE_OWNED;
+       assert_transition_res(0,        __pkvm_host_unshare_guest, gfn + 1, vm);
+
        selftest_state.host = PKVM_NOPAGE;
        selftest_state.hyp = PKVM_PAGE_OWNED;
        assert_transition_res(0,        __pkvm_host_donate_hyp, pfn, 1);
index 54006f959e1b1e94daf14e7d4baf973cf1c08bab..814548134a8369aaeba7e85ff8f02ea2be1885a1 100644 (file)
@@ -28,6 +28,7 @@ static void *vmemmap_base;
 static void *vm_table_base;
 static void *hyp_pgt_base;
 static void *host_s2_pgt_base;
+static void *selftest_base;
 static void *ffa_proxy_pages;
 static struct kvm_pgtable_mm_ops pkvm_pgtable_mm_ops;
 static struct hyp_pool hpool;
@@ -38,6 +39,11 @@ static int divide_memory_pool(void *virt, unsigned long size)
 
        hyp_early_alloc_init(virt, size);
 
+       nr_pages = pkvm_selftest_pages();
+       selftest_base = hyp_early_alloc_contig(nr_pages);
+       if (nr_pages && !selftest_base)
+               return -ENOMEM;
+
        nr_pages = hyp_vmemmap_pages(sizeof(struct hyp_page));
        vmemmap_base = hyp_early_alloc_contig(nr_pages);
        if (!vmemmap_base)
@@ -309,7 +315,7 @@ void __noreturn __pkvm_init_finalise(void)
 
        pkvm_hyp_vm_table_init(vm_table_base);
 
-       pkvm_ownership_selftest();
+       pkvm_ownership_selftest(selftest_base);
 out:
        /*
         * We tail-called to here from handle___pkvm_init() and will not return,
index c462140e640ab9999c5ec7d7b8685a0099c2aaef..f4761a47928037d3e35d3c91b7d752162db3dbcd 100644 (file)
@@ -79,6 +79,7 @@ void __init kvm_hyp_reserve(void)
        hyp_mem_pages += host_s2_pgtable_pages();
        hyp_mem_pages += hyp_vm_table_pages();
        hyp_mem_pages += hyp_vmemmap_pages(STRUCT_HYP_PAGE_SIZE);
+       hyp_mem_pages += pkvm_selftest_pages();
        hyp_mem_pages += hyp_ffa_proxy_pages();
 
        /*