|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [PATCH v4 1/2] x86/mm: add API for marking only part of a MMIO page read only
On Tue, Jun 11, 2024 at 01:38:35PM +0200, Marek Marczykowski-Górecki wrote:
> On Tue, Jun 11, 2024 at 12:40:49PM +0200, Roger Pau Monné wrote:
> > On Wed, May 22, 2024 at 05:39:03PM +0200, Marek Marczykowski-Górecki wrote:
> > > + if ( !entry )
> > > + {
> > > + /* iter == NULL marks it was a newly allocated entry */
> > > + iter = NULL;
> > > + entry = xzalloc(struct subpage_ro_range);
> > > + if ( !entry )
> > > + return -ENOMEM;
> > > + entry->mfn = mfn;
> > > + }
> > > +
> > > + for ( i = offset_s; i <= offset_e; i += MMIO_RO_SUBPAGE_GRAN )
> > > + {
> > > + bool oldbit = __test_and_set_bit(i / MMIO_RO_SUBPAGE_GRAN,
> > > + entry->ro_elems);
> > > + ASSERT(!oldbit);
> > > + }
> > > +
> > > + if ( !iter )
> > > + list_add(&entry->list, &subpage_ro_ranges);
> > > +
> > > + return iter ? 1 : 0;
> > > +}
> > > +
> > > +/* This needs subpage_ro_lock already taken */
> > > +static void __init subpage_mmio_ro_remove_page(
> > > + mfn_t mfn,
> > > + unsigned int offset_s,
> > > + unsigned int offset_e)
> > > +{
> > > + struct subpage_ro_range *entry = NULL, *iter;
> > > + unsigned int i;
> > > +
> > > + list_for_each_entry(iter, &subpage_ro_ranges, list)
> > > + {
> > > + if ( mfn_eq(iter->mfn, mfn) )
> > > + {
> > > + entry = iter;
> > > + break;
> > > + }
> > > + }
> > > + if ( !entry )
> > > + return;
> > > +
> > > + for ( i = offset_s; i <= offset_e; i += MMIO_RO_SUBPAGE_GRAN )
> > > + __clear_bit(i / MMIO_RO_SUBPAGE_GRAN, entry->ro_elems);
> > > +
> > > + if ( !bitmap_empty(entry->ro_elems, PAGE_SIZE /
> > > MMIO_RO_SUBPAGE_GRAN) )
> > > + return;
> > > +
> > > + list_del(&entry->list);
> > > + if ( entry->mapped )
> > > + iounmap(entry->mapped);
> > > + xfree(entry);
> > > +}
> > > +
> > > +int __init subpage_mmio_ro_add(
> > > + paddr_t start,
> > > + size_t size)
> > > +{
> > > + mfn_t mfn_start = maddr_to_mfn(start);
> > > + paddr_t end = start + size - 1;
> > > + mfn_t mfn_end = maddr_to_mfn(end);
> > > + unsigned int offset_end = 0;
> > > + int rc;
> > > + bool subpage_start, subpage_end;
> > > +
> > > + ASSERT(IS_ALIGNED(start, MMIO_RO_SUBPAGE_GRAN));
> > > + ASSERT(IS_ALIGNED(size, MMIO_RO_SUBPAGE_GRAN));
> > > + if ( !IS_ALIGNED(size, MMIO_RO_SUBPAGE_GRAN) )
> > > + size = ROUNDUP(size, MMIO_RO_SUBPAGE_GRAN);
> > > +
> > > + if ( !size )
> > > + return 0;
> > > +
> > > + if ( mfn_eq(mfn_start, mfn_end) )
> > > + {
> > > + /* Both starting and ending parts handled at once */
> > > + subpage_start = PAGE_OFFSET(start) || PAGE_OFFSET(end) !=
> > > PAGE_SIZE - 1;
> > > + subpage_end = false;
> >
> > Given the intended usage of this, don't we want to limit to only a
> > single page? So that PFN_DOWN(start + size) == PFN_DOWN/(start), as
> > that would simplify the logic here?
>
> I have considered that, but I haven't found anything in the spec
> mandating the XHCI DbC registers to not cross page boundary. Currently
> (on a system I test this on) they don't cross page boundary, but I don't
> want to assume extra constrains - to avoid issues like before (when
> on the older system I tested the DbC registers didn't shared page with
> other registers, but then they shared the page on a newer hardware).
Oh, from our conversation at XenSummit I got the impression debug registers
where always at the same position. Looking at patch 2/2, it seems you
only need to block access to a single register. Are registers in XHCI
size aligned? As this would guarantee it doesn't cross a page
boundary (as long as the register is <= 4096 in size).
> > > + if ( !addr )
> > > + {
> > > + gprintk(XENLOG_ERR,
> > > + "Failed to map page for MMIO write at
> > > 0x%"PRI_mfn"%03x\n",
> > > + mfn_x(mfn), offset);
> > > + return;
> > > + }
> > > +
> > > + switch ( len )
> > > + {
> > > + case 1:
> > > + writeb(*(const uint8_t*)data, addr);
> > > + break;
> > > + case 2:
> > > + writew(*(const uint16_t*)data, addr);
> > > + break;
> > > + case 4:
> > > + writel(*(const uint32_t*)data, addr);
> > > + break;
> > > + case 8:
> > > + writeq(*(const uint64_t*)data, addr);
> > > + break;
> > > + default:
> > > + /* mmio_ro_emulated_write() already validated the size */
> > > + ASSERT_UNREACHABLE();
> > > + goto write_ignored;
> > > + }
> > > + return;
> > > + }
> > > + }
> > > + /* Do not print message for pages without any writable parts. */
> > > +}
> > > +
> > > +#ifdef CONFIG_HVM
> > > +bool subpage_mmio_write_accept(mfn_t mfn, unsigned long gla)
> > > +{
> > > + unsigned int offset = PAGE_OFFSET(gla);
> > > + const struct subpage_ro_range *entry;
> > > +
> > > + list_for_each_entry(entry, &subpage_ro_ranges, list)
> > > + if ( mfn_eq(entry->mfn, mfn) &&
> > > + !test_bit(offset / MMIO_RO_SUBPAGE_GRAN, entry->ro_elems) )
> > > + {
> > > + /*
> > > + * We don't know the write size at this point yet, so it
> > > could be
> > > + * an unaligned write, but accept it here anyway and deal
> > > with it
> > > + * later.
> > > + */
> > > + return true;
> >
> > For accesses that fall into the RO region, I think you need to accept
> > them here and just terminate them? I see no point in propagating
> > them further in hvm_hap_nested_page_fault().
>
> If write hits an R/O region on a page with some writable regions the
> handling should be the same as it would be just on the mmio_ro_ranges.
> This is what the patch does.
> There may be an opportunity to simplify mmio_ro_ranges handling
> somewhere, but I don't think it belongs to this patch.
Maybe worth adding a comment that the logic here intends to deal only
with the RW bits of a page that's otherwise RO, and that by not
handling the RO regions the intention is that those are dealt just
like fully RO pages.
I guess there's some message printed when attempting to write to a RO
page that you would also like to print here?
Thanks, Roger.
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |