|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [Xen-devel] [PATCH v3] x86/mm: Add mem access rights to NPT
On Mi, 2018-07-18 at 15:33 +0000, George Dunlap wrote:
>
> >
> > On Jul 2, 2018, at 8:42 AM, Alexandru Isaila <aisaila@bitdefender.c
> > om> wrote:
> >
> > From: Isaila Alexandru <aisaila@xxxxxxxxxxxxxxx>
> >
> > This patch adds access rights for the NPT pages. The access rights
> > are
> > saved in a radix tree with the root saved in p2m_domain. The rights
> > are manipulated through p2m_set_access()
> > and p2m_get_access() functions.
> > The patch follows the ept logic.
> This description needs to be much more complete. Something like
> this:
>
> ---
> This patch adds access control for NPT mode.
>
> There aren’t enough extra bits to store the access rights in the NPT
> p2m table, so we add a radix tree to store the rights. For
> efficiency, remove entries which match the default permissions rather
> than continuing to store them.
>
> Modify p2m-pt.c:p2m_type_to_flags() to mirror the ept version: taking
> an access, and removing / adding RW or NX flags as appropriate.
> ---
>
I will update the patch description.
> >
> >
> > Note: It was tested with xen-access write
> >
> > Signed-off-by: Alexandru Isaila <aisaila@xxxxxxxxxxxxxxx>
>
> >
> >
> > ---
> > Changes since V2:
> > - Delete blak line
> > - Add return if p2m_access_rwx = a
> > - Delete the comment from p2m_pt_get_entry()
> > - Moved radix_tree_init() to arch_monitor_init_domain().
> > ---
> > xen/arch/x86/mm/mem_access.c | 3 ++
> > xen/arch/x86/mm/p2m-pt.c | 109
> > ++++++++++++++++++++++++++++++++++-----
> > xen/arch/x86/mm/p2m.c | 6 +++
> > xen/arch/x86/monitor.c | 13 +++++
> > xen/include/asm-x86/mem_access.h | 2 +-
> > xen/include/asm-x86/p2m.h | 6 +++
> > 6 files changed, 124 insertions(+), 15 deletions(-)
> >
> > diff --git a/xen/arch/x86/mm/mem_access.c
> > b/xen/arch/x86/mm/mem_access.c
> > index c0cd017..d78c82c 100644
> > --- a/xen/arch/x86/mm/mem_access.c
> > +++ b/xen/arch/x86/mm/mem_access.c
> > @@ -221,7 +221,10 @@ bool p2m_mem_access_check(paddr_t gpa,
> > unsigned long gla,
> > {
> > req->u.mem_access.flags |= MEM_ACCESS_GLA_VALID;
> > req->u.mem_access.gla = gla;
> > + }
> >
> > + if ( npfec.gla_valid || cpu_has_svm )
> > + {
> I can’t immediately tell what this is about, which means it needs a
> comment.
>
> It may also be (depending on why this is here) that “cpu_has_svm”
> makes this more fragile — if this is really about having a radix
> tree, for instance, then you should probably check for a radix tree.
This is about the different npfec on SVN. The gla in never valid so the
fault flag will not be set.
>
> >
> > if ( npfec.kind == npfec_kind_with_gla )
> > req->u.mem_access.flags |=
> > MEM_ACCESS_FAULT_WITH_GLA;
> > else if ( npfec.kind == npfec_kind_in_gpt )
> > diff --git a/xen/arch/x86/mm/p2m-pt.c b/xen/arch/x86/mm/p2m-pt.c
> > index b8c5d2e..4330d1f 100644
> > --- a/xen/arch/x86/mm/p2m-pt.c
> > +++ b/xen/arch/x86/mm/p2m-pt.c
> > @@ -68,7 +68,8 @@
> > static unsigned long p2m_type_to_flags(const struct p2m_domain
> > *p2m,
> > p2m_type_t t,
> > mfn_t mfn,
> > - unsigned int level)
> > + unsigned int level,
> > + p2m_access_t access)
> > {
> > unsigned long flags;
> > /*
> > @@ -87,23 +88,27 @@ static unsigned long p2m_type_to_flags(const
> > struct p2m_domain *p2m,
> > case p2m_ram_paged:
> > case p2m_ram_paging_in:
> > default:
> > - return flags | _PAGE_NX_BIT;
> > + flags |= P2M_BASE_FLAGS | _PAGE_NX_BIT;
> > + break;
> > case p2m_grant_map_ro:
> > return flags | P2M_BASE_FLAGS | _PAGE_NX_BIT;
> > case p2m_ioreq_server:
> > flags |= P2M_BASE_FLAGS | _PAGE_RW | _PAGE_NX_BIT;
> > if ( p2m->ioreq.flags & XEN_DMOP_IOREQ_MEM_ACCESS_WRITE )
> > - return flags & ~_PAGE_RW;
> > - return flags;
> > + flags &= ~_PAGE_RW;
> > + break;
> > case p2m_ram_ro:
> > case p2m_ram_logdirty:
> > case p2m_ram_shared:
> > - return flags | P2M_BASE_FLAGS;
> > + flags |= P2M_BASE_FLAGS;
> > + break;
> > case p2m_ram_rw:
> > - return flags | P2M_BASE_FLAGS | _PAGE_RW;
> > + flags |= P2M_BASE_FLAGS | _PAGE_RW;
> > + break;
> > case p2m_grant_map_rw:
> > case p2m_map_foreign:
> > - return flags | P2M_BASE_FLAGS | _PAGE_RW | _PAGE_NX_BIT;
> > + flags |= P2M_BASE_FLAGS | _PAGE_RW | _PAGE_NX_BIT;
> > + break;
> > case p2m_mmio_direct:
> > if ( !rangeset_contains_singleton(mmio_ro_ranges,
> > mfn_x(mfn)) )
> > flags |= _PAGE_RW;
> > @@ -112,8 +117,37 @@ static unsigned long p2m_type_to_flags(const
> > struct p2m_domain *p2m,
> > flags |= _PAGE_PWT;
> > ASSERT(!level);
> > }
> > - return flags | P2M_BASE_FLAGS | _PAGE_PCD;
> > + flags |= P2M_BASE_FLAGS | _PAGE_PCD;
> > + break;
> > + }
> I think you want a blank line here.
>
> >
> > + switch ( access )
> > + {
> > + case p2m_access_r:
> > + case p2m_access_w:
> > + flags |= _PAGE_NX_BIT;
> > + flags &= ~_PAGE_RW;
> > + break;
> > + case p2m_access_rw:
> > + flags |= _PAGE_NX_BIT;
> > + break;
> > + case p2m_access_n:
> > + case p2m_access_n2rwx:
> > + flags |= _PAGE_NX_BIT;
> > + flags &= ~_PAGE_RW;
> > + break;
> > + case p2m_access_rx:
> > + case p2m_access_wx:
> > + case p2m_access_rx2rw:
> > + flags &= ~(_PAGE_NX_BIT | _PAGE_RW);
> This looks like a mistake — this will unconditionally enable
> execution, even if the underlying p2m type forbids it.
> ept_p2m_type_to_flags() doesn’t do that.
>
> >
> > + break;
> > + case p2m_access_x:
> > + flags &= ~_PAGE_RW;
> > + break;
> > + case p2m_access_rwx:
> > + default:
> > + break;
> > }
> I think you want another blank line here too.
>
> Also, this doesn’t seem to capture the ‘r’ part of the equation —
> shouldn’t p2m_access_n end up with a not-present p2m entry?
SVM dosen't explicitly provide a read access bit so we treat read and
write the same way.
Alex
>
> >
> > + return flags;
> > }
> >
> >
> > @@ -174,6 +208,44 @@ static void p2m_add_iommu_flags(l1_pgentry_t
> > *p2m_entry,
> > l1e_add_flags(*p2m_entry, iommu_nlevel_to_flags(nlevel,
> > flags));
> > }
> >
> > +static p2m_access_t p2m_get_access(struct p2m_domain *p2m,
> > unsigned long gfn)
> > +{
> > + void *ptr;
> > +
> > + if ( !p2m->mem_access_settings )
> > + return p2m_access_rwx;
> > +
> > + ptr = radix_tree_lookup(p2m->mem_access_settings, gfn);
> > + if ( !ptr )
> > + return p2m_access_rwx;
> > + else
> > + return radix_tree_ptr_to_int(ptr);
> > +}
> > +
> > +static void p2m_set_access(struct p2m_domain *p2m, unsigned long
> > gfn,
> > + p2m_access_t a)
> > +{
> > + int rc;
> > +
> > + if ( !p2m->mem_access_settings )
> > + return;
> > +
> > + if ( p2m_access_rwx == a )
> > + {
> > + radix_tree_delete(p2m->mem_access_settings, gfn);
> > + return;
> > + }
> > +
> > + rc = radix_tree_insert(p2m->mem_access_settings, gfn,
> > + radix_tree_int_to_ptr(a));
> > + if ( rc == -EEXIST )
> > + /* If a setting already exists, change it to the new one.
> > */
> > + radix_tree_replace_slot(
> > + radix_tree_lookup_slot(
> > + p2m->mem_access_settings, gfn),
> > + radix_tree_int_to_ptr(a));
> > +}
> > +
> > /* Returns: 0 for success, -errno for failure */
> > static int
> > p2m_next_level(struct p2m_domain *p2m, void **table,
> > @@ -201,6 +273,7 @@ p2m_next_level(struct p2m_domain *p2m, void
> > **table,
> > new_entry = l1e_from_mfn(mfn, P2M_BASE_FLAGS | _PAGE_RW);
> >
> > p2m_add_iommu_flags(&new_entry, level,
> > IOMMUF_readable|IOMMUF_writable);
> > + p2m_set_access(p2m, gfn, p2m->default_access);
> > p2m->write_p2m_entry(p2m, gfn, p2m_entry, new_entry, level
> > + 1);
> > }
> > else if ( flags & _PAGE_PSE )
> > @@ -249,6 +322,7 @@ p2m_next_level(struct p2m_domain *p2m, void
> > **table,
> > {
> > new_entry = l1e_from_pfn(pfn | (i << ((level - 1) *
> > PAGETABLE_ORDER)),
> > flags);
> > + p2m_set_access(p2m, gfn, p2m->default_access);
> > p2m->write_p2m_entry(p2m, gfn, l1_entry + i, new_entry,
> > level);
> > }
> >
> > @@ -256,6 +330,7 @@ p2m_next_level(struct p2m_domain *p2m, void
> > **table,
> >
> > new_entry = l1e_from_mfn(mfn, P2M_BASE_FLAGS | _PAGE_RW);
> > p2m_add_iommu_flags(&new_entry, level,
> > IOMMUF_readable|IOMMUF_writable);
> > + p2m_set_access(p2m, gfn, p2m->default_access);
> > p2m->write_p2m_entry(p2m, gfn, p2m_entry, new_entry, level
> > + 1);
> > }
> > else
> > @@ -420,8 +495,9 @@ static int do_recalc(struct p2m_domain *p2m,
> > unsigned long gfn)
> > if ( nt != ot )
> > {
> > unsigned long mfn = l1e_get_pfn(e);
> > + p2m_access_t a = p2m_get_access(p2m, gfn);
> > unsigned long flags = p2m_type_to_flags(p2m, nt,
> > - _mfn(mfn),
> > level);
> > + _mfn(mfn),
> > level, a);
> >
> > if ( level )
> > {
> > @@ -569,13 +645,14 @@ p2m_pt_set_entry(struct p2m_domain *p2m,
> > gfn_t gfn_, mfn_t mfn,
> > ASSERT(!mfn_valid(mfn) || p2mt != p2m_mmio_direct);
> > l3e_content = mfn_valid(mfn) ||
> > p2m_allows_invalid_mfn(p2mt)
> > ? p2m_l3e_from_pfn(mfn_x(mfn),
> > - p2m_type_to_flags(p2m, p2mt, mfn,
> > 2))
> > + p2m_type_to_flags(p2m, p2mt, mfn,
> > 2, p2ma))
> > : l3e_empty();
> > entry_content.l1 = l3e_content.l3;
> >
> > if ( entry_content.l1 != 0 )
> > p2m_add_iommu_flags(&entry_content, 0,
> > iommu_pte_flags);
> >
> > + p2m_set_access(p2m, gfn, p2ma);
> > p2m->write_p2m_entry(p2m, gfn, p2m_entry, entry_content,
> > 3);
> > /* NB: paging_write_p2m_entry() handles tlb flushes
> > properly */
> > }
> > @@ -608,7 +685,7 @@ p2m_pt_set_entry(struct p2m_domain *p2m, gfn_t
> > gfn_, mfn_t mfn,
> >
> > if ( mfn_valid(mfn) || p2m_allows_invalid_mfn(p2mt) )
> > entry_content = p2m_l1e_from_pfn(mfn_x(mfn),
> > - p2m_type_to_flags(p2m,
> > p2mt, mfn, 0));
> > + p2m_type_to_flags(p2m,
> > p2mt, mfn, 0, p2ma));
> > else
> > entry_content = l1e_empty();
> >
> > @@ -630,6 +707,7 @@ p2m_pt_set_entry(struct p2m_domain *p2m, gfn_t
> > gfn_, mfn_t mfn,
> > p2m->ioreq.entry_count--;
> > }
> >
> > + p2m_set_access(p2m, gfn, p2ma);
> > /* level 1 entry */
> > p2m->write_p2m_entry(p2m, gfn, p2m_entry, entry_content,
> > 1);
> > /* NB: paging_write_p2m_entry() handles tlb flushes
> > properly */
> > @@ -661,13 +739,14 @@ p2m_pt_set_entry(struct p2m_domain *p2m,
> > gfn_t gfn_, mfn_t mfn,
> > ASSERT(!mfn_valid(mfn) || p2mt != p2m_mmio_direct);
> > l2e_content = mfn_valid(mfn) ||
> > p2m_allows_invalid_mfn(p2mt)
> > ? p2m_l2e_from_pfn(mfn_x(mfn),
> > - p2m_type_to_flags(p2m, p2mt, mfn,
> > 1))
> > + p2m_type_to_flags(p2m, p2mt, mfn,
> > 1, p2ma))
> > : l2e_empty();
> > entry_content.l1 = l2e_content.l2;
> >
> > if ( entry_content.l1 != 0 )
> > p2m_add_iommu_flags(&entry_content, 0,
> > iommu_pte_flags);
> >
> > + p2m_set_access(p2m, gfn, p2ma);
> > p2m->write_p2m_entry(p2m, gfn, p2m_entry, entry_content,
> > 2);
> > /* NB: paging_write_p2m_entry() handles tlb flushes
> > properly */
> > }
> > @@ -749,8 +828,7 @@ p2m_pt_get_entry(struct p2m_domain *p2m, gfn_t
> > gfn_,
> > * XXX Once we start explicitly registering MMIO regions in the
> > p2m
> > * XXX we will return p2m_invalid for unmapped gfns */
> > *t = p2m_mmio_dm;
> > - /* Not implemented except with EPT */
> > - *a = p2m_access_rwx;
> > + *a = p2m_access_n;
> >
> > if ( gfn > p2m->max_mapped_pfn )
> > {
> > @@ -813,6 +891,7 @@ pod_retry_l3:
> > l1_table_offset(addr));
> > *t = p2m_recalc_type(recalc || _needs_recalc(flags),
> > p2m_flags_to_type(flags), p2m,
> > gfn);
> > + *a = p2m_get_access(p2m, gfn);
> > unmap_domain_page(l3e);
> >
> > ASSERT(mfn_valid(mfn) || !p2m_is_ram(*t));
> > @@ -852,6 +931,7 @@ pod_retry_l2:
> > mfn = _mfn(l2e_get_pfn(*l2e) + l1_table_offset(addr));
> > *t = p2m_recalc_type(recalc || _needs_recalc(flags),
> > p2m_flags_to_type(flags), p2m, gfn);
> > + *a = p2m_get_access(p2m, gfn);
> > unmap_domain_page(l2e);
> >
> > ASSERT(mfn_valid(mfn) || !p2m_is_ram(*t));
> > @@ -888,6 +968,7 @@ pod_retry_l1:
> > }
> > mfn = l1e_get_mfn(*l1e);
> > *t = p2m_recalc_type(recalc || _needs_recalc(flags), l1t, p2m,
> > gfn);
> > + *a = p2m_get_access(p2m, gfn);
> > unmap_domain_page(l1e);
> >
> > ASSERT(mfn_valid(mfn) || !p2m_is_ram(*t) || p2m_is_paging(*t));
> > diff --git a/xen/arch/x86/mm/p2m.c b/xen/arch/x86/mm/p2m.c
> > index c53cab4..12e2d24 100644
> > --- a/xen/arch/x86/mm/p2m.c
> > +++ b/xen/arch/x86/mm/p2m.c
> > @@ -675,6 +675,12 @@ void p2m_teardown(struct p2m_domain *p2m)
> >
> > d = p2m->domain;
> >
> > + if ( p2m->mem_access_settings )
> > + {
> > + radix_tree_destroy(p2m->mem_access_settings, NULL);
> > + xfree(p2m->mem_access_settings);
> > + }
> > +
> > p2m_lock(p2m);
> > ASSERT(atomic_read(&d->shr_pages) == 0);
> > p2m->phys_table = pagetable_null();
> > diff --git a/xen/arch/x86/monitor.c b/xen/arch/x86/monitor.c
> > index 3fb6531..18b88a1 100644
> > --- a/xen/arch/x86/monitor.c
> > +++ b/xen/arch/x86/monitor.c
> > @@ -20,10 +20,13 @@
> > */
> >
> > #include <asm/monitor.h>
> > +#include <asm/p2m.h>
> > #include <public/vm_event.h>
> >
> > int arch_monitor_init_domain(struct domain *d)
> > {
> > + struct p2m_domain *p2m = p2m_get_hostp2m(d);
> > +
> > if ( !d->arch.monitor.msr_bitmap )
> > d->arch.monitor.msr_bitmap = xzalloc_array(struct
> > monitor_msr_bitmap,
> > 2);
> > @@ -31,6 +34,16 @@ int arch_monitor_init_domain(struct domain *d)
> > if ( !d->arch.monitor.msr_bitmap )
> > return -ENOMEM;
> >
> > + if ( cpu_has_svm && !p2m->mem_access_settings )
> > + {
> > + p2m->mem_access_settings = xmalloc(struct
> > radix_tree_root);
> > +
> > + if( !p2m->mem_access_settings )
> > + return -ENOMEM;
> This will leak d->arch.monitor.msr_bitmap. You need to use the `goto
> out_free:` pattern.
>
> Everything else looks OK.
>
> -George
>
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxxx
https://lists.xenproject.org/mailman/listinfo/xen-devel
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |