linux-arm-kernel.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
From: Quentin Perret <qperret@google.com>
To: Will Deacon <will@kernel.org>
Cc: Marc Zyngier <maz@kernel.org>,
	Oliver Upton <oliver.upton@linux.dev>,
	Joey Gouly <joey.gouly@arm.com>,
	Suzuki K Poulose <suzuki.poulose@arm.com>,
	Zenghui Yu <yuzenghui@huawei.com>,
	Catalin Marinas <catalin.marinas@arm.com>,
	linux-arm-kernel@lists.infradead.org, kvmarm@lists.linux.dev,
	linux-kernel@vger.kernel.org
Subject: Re: [PATCH] KVM: arm64: Selftest for pKVM transitions
Date: Fri, 20 Dec 2024 15:14:36 +0000	[thread overview]
Message-ID: <Z2WJ3F0qiqvV3qTw@google.com> (raw)
In-Reply-To: <20241220141321.GA26794@willie-the-truck>

On Friday 20 Dec 2024 at 14:13:22 (+0000), Will Deacon wrote:
> 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?

Nope, sadly we don't, but if you get a failure after turning on that
config then you can at least be confident there is a problem with your
code :-)

As discussed in the other thread, once we get the hyp state into the
vmemmap (I've got patches that do exactly that, happy to send them out)
we could just plain remove the 'skip_pgtable_check()' logic, and then
I'm not opposed to merging CONFIG_PKVM_SELFTESTS and CONFIG_EL2_NVHE_DEBUG
together as the latter won't have the same side effects.

> > +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?

Yep, sounds like a good idea.

> > +	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.

Sadly no, unshare_ffa() isn't super smart, it'll only check that a
page is SHARED_OWNED from the host's PoV and turn it to PAGE_OWNED. So
we'd break the state in this case :/

> > +	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?

You mean doing pin > pin > unpin > sanity checks > unpin ?

I guess that would test that we can indeed stack the pins, so sounds
like a good idea too.

> > +	WARN_ON(!hyp_page_count(virt));
> 
> Can we assert an exact value (e.g. count == 1)?

Sure.

> > +	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.

Same as above.

> > +	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.

The idea was to check if the previous transitions had other sad effects
not visible through the <state, refcount> pair that would cause
problems. But I guess that is a bit far fetched so happy to remove that
part.

> > +	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?

Sg.

Thanks!
Quentin


      reply	other threads:[~2024-12-20 17:25 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-11-29 12:58 [PATCH] KVM: arm64: Selftest for pKVM transitions Quentin Perret
2024-12-20 14:13 ` Will Deacon
2024-12-20 15:14   ` Quentin Perret [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=Z2WJ3F0qiqvV3qTw@google.com \
    --to=qperret@google.com \
    --cc=catalin.marinas@arm.com \
    --cc=joey.gouly@arm.com \
    --cc=kvmarm@lists.linux.dev \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=maz@kernel.org \
    --cc=oliver.upton@linux.dev \
    --cc=suzuki.poulose@arm.com \
    --cc=will@kernel.org \
    --cc=yuzenghui@huawei.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).