[PATCH] KVM: arm64: Selftest for pKVM transitions
Will Deacon
will at kernel.org
Fri Dec 20 06:13:22 PST 2024
On Fri, Nov 29, 2024 at 12:58:00PM +0000, Quentin Perret wrote:
> We have recently found a bug [1] in the pKVM memory ownership
> transitions by code inspection, but it could have been caught with a
> test.
>
> Introduce a boot-time selftest exercising all the known pKVM memory
> transitions and importantly checks the rejection of illegal transitions.
Thanks for doing this!
>
> The new test is hidden behind a new Kconfig option separate from
> CONFIG_EL2_NVHE_DEBUG on purpose as that has side effects on the
> transition checks ([1] doesn't reproduce with EL2 debug enabled).
Hmm. What does a test failure look like without CONFIG_EL2_NVHE_DEBUG
enabled? Do we still get file:line information?
[...]
> +void pkvm_ownership_selftest(void)
> +{
> + void *virt = hyp_alloc_pages(&host_s2_pool, 0);
> + u64 phys, size, pfn;
> +
> + WARN_ON(!virt);
> + selftest_page = hyp_virt_to_page(virt);
> + selftest_page->refcount = 0;
> +
> + size = PAGE_SIZE << selftest_page->order;
> + phys = hyp_virt_to_phys(virt);
> + pfn = hyp_phys_to_pfn(phys);
> +
> + selftest_state.host = PKVM_NOPAGE;
> + selftest_state.hyp = PKVM_PAGE_OWNED;
> + assert_page_state();
> + 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_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);
> +
> + selftest_state.host = PKVM_PAGE_OWNED;
> + selftest_state.hyp = PKVM_NOPAGE;
> + assert_transition_res(0, __pkvm_hyp_donate_host, pfn, 1);
> + assert_transition_res(-EPERM, __pkvm_hyp_donate_host, pfn, 1);
> + assert_transition_res(-EPERM, hyp_pin_shared_mem, virt, virt + size);
Should we recheck the unshare transitions here?
> + selftest_state.host = PKVM_PAGE_SHARED_OWNED;
> + selftest_state.hyp = PKVM_PAGE_SHARED_BORROWED;
> + assert_transition_res(0, __pkvm_host_share_hyp, pfn);
> + assert_transition_res(-EPERM, __pkvm_host_share_hyp, pfn);
> + assert_transition_res(-EPERM, __pkvm_host_donate_hyp, pfn, 1);
> + assert_transition_res(-EPERM, __pkvm_host_share_ffa, pfn, 1);
We could check unshare_ffa here.
> + assert_transition_res(-EPERM, __pkvm_hyp_donate_host, pfn, 1);
> +
> + assert_transition_res(0, hyp_pin_shared_mem, virt, virt + size);
Is it worth trying an extra pin + unpin?
> + WARN_ON(!hyp_page_count(virt));
Can we assert an exact value (e.g. count == 1)?
> + assert_transition_res(-EBUSY, __pkvm_host_unshare_hyp, pfn);
> + assert_transition_res(-EPERM, __pkvm_host_share_hyp, pfn);
> + assert_transition_res(-EPERM, __pkvm_host_donate_hyp, pfn, 1);
> + assert_transition_res(-EPERM, __pkvm_host_share_ffa, pfn, 1);
unshare_ffa again here.
> + assert_transition_res(-EPERM, __pkvm_hyp_donate_host, pfn, 1);
> +
> + hyp_unpin_shared_mem(virt, virt + size);
> + assert_page_state();
> + WARN_ON(hyp_page_count(virt));
> + assert_transition_res(-EPERM, __pkvm_host_share_hyp, pfn);
> + 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);
Is this block actually testing anything new? Once we've asserted the page
state and checked the refcount, the transitions seem redundant to me.
> + selftest_state.host = PKVM_PAGE_OWNED;
> + selftest_state.hyp = PKVM_NOPAGE;
> + assert_transition_res(0, __pkvm_host_unshare_hyp, pfn);
> +
> + selftest_state.host = PKVM_PAGE_SHARED_OWNED;
> + selftest_state.hyp = PKVM_NOPAGE;
> + assert_transition_res(0, __pkvm_host_share_ffa, pfn, 1);
> + 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.host = PKVM_PAGE_OWNED;
> + selftest_state.hyp = PKVM_NOPAGE;
> + assert_transition_res(0, __pkvm_host_unshare_ffa, pfn, 1);
Try it twice?
Will
More information about the linux-arm-kernel
mailing list