[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [Xen-devel] [PATCH MM-PART3 v2 10/12] xen/arm: mm: Rework Xen page-tables walk during update
On Tue, 14 May 2019, Julien Grall wrote: > Currently, xen_pt_update_entry() is only able to update the region covered > by xen_second (i.e 0 to 0x7fffffff). > > Because of the restriction we end to have multiple functions in mm.c > modifying the page-tables differently. > > Furthermore, we never walked the page-tables fully. This means that any > change in the layout may requires major rewrite of the page-tables code. > > Lastly, we have been quite lucky that no one ever tried to pass an address > outside this range because it would have blown-up. > > xen_pt_update_entry() is reworked to walk over the page-tables every > time. The logic has been borrowed from arch/arm/p2m.c and contain some > limitations for the time being: > - Superpage cannot be shattered > - Only level 3 (i.e 4KB) can be done > > Note that the parameter 'addr' has been renamed to 'virt' to make clear > we are dealing with a virtual address. > > Signed-off-by: Julien Grall <julien.grall@xxxxxxx> > Reviewed-by: Andrii Anisov <andrii_anisov@xxxxxxxx> > > --- > Changes in v2: > - Add Andrii's reviewed-by > --- > xen/arch/arm/mm.c | 121 > +++++++++++++++++++++++++++++++++++++++++++++++------- > 1 file changed, 106 insertions(+), 15 deletions(-) > > diff --git a/xen/arch/arm/mm.c b/xen/arch/arm/mm.c > index f5979f549b..9a40754f44 100644 > --- a/xen/arch/arm/mm.c > +++ b/xen/arch/arm/mm.c > @@ -984,6 +984,53 @@ static void xen_unmap_table(const lpae_t *table) > unmap_domain_page(table); > } > > +#define XEN_TABLE_MAP_FAILED 0 > +#define XEN_TABLE_SUPER_PAGE 1 > +#define XEN_TABLE_NORMAL_PAGE 2 Minor NIT: do we want to have XEN_TABLE_MAP_FAILED be -1 to follow the pattern that errors are < 0 ? Not important though. > +/* > + * Take the currently mapped table, find the corresponding entry, > + * and map the next table, if available. > + * > + * The read_only parameters indicates whether intermediate tables should > + * be allocated when not present. I wonder if it would be a good idea to rename read_only to something more obviously connected to the idea that tables get created. Maybe create_missing? It would have to match the variable and comment added below in xen_pt_update_entry. I don't have a strong opinion on this. > + * Return values: > + * XEN_TABLE_MAP_FAILED: Either read_only was set and the entry > + * was empty, or allocating a new page failed. > + * XEN_TABLE_NORMAL_PAGE: next level mapped normally > + * XEN_TABLE_SUPER_PAGE: The next entry points to a superpage. > + */ > +static int xen_pt_next_level(bool read_only, unsigned int level, > + lpae_t **table, unsigned int offset) > +{ > + lpae_t *entry; > + int ret; > + > + entry = *table + offset; > + > + if ( !lpae_is_valid(*entry) ) > + { > + if ( read_only ) > + return XEN_TABLE_MAP_FAILED; > + > + ret = create_xen_table(entry); > + if ( ret ) > + return XEN_TABLE_MAP_FAILED; > + } > + > + ASSERT(lpae_is_valid(*entry)); Why the ASSERT just after the lpae_is_valid check above? > + /* The function xen_pt_next_level is never called at the 3rd level */ > + if ( lpae_is_mapping(*entry, level) ) > + return XEN_TABLE_SUPER_PAGE; > + > + xen_unmap_table(*table); > + *table = xen_map_table(lpae_get_mfn(*entry)); > + > + return XEN_TABLE_NORMAL_PAGE; > +} > + > /* Sanity check of the entry */ > static bool xen_pt_check_entry(lpae_t entry, mfn_t mfn, unsigned int flags) > { > @@ -1043,30 +1090,65 @@ static bool xen_pt_check_entry(lpae_t entry, mfn_t > mfn, unsigned int flags) > return true; > } > > -static int xen_pt_update_entry(unsigned long addr, mfn_t mfn, > - unsigned int flags) > +static int xen_pt_update_entry(mfn_t root, unsigned long virt, > + mfn_t mfn, unsigned int flags) > { > int rc; > + unsigned int level; > + /* We only support 4KB mapping (i.e level 3) for now */ > + unsigned int target = 3; > + lpae_t *table; > + /* > + * The intermediate page tables are read-only when the MFN is not valid > + * and we are not populating page table. > + * This means we either modify permissions or remove an entry. > + */ > + bool read_only = mfn_eq(mfn, INVALID_MFN) && !(flags & _PAGE_POPULATE); > lpae_t pte, *entry; > - lpae_t *third = NULL; > + > + /* convenience aliases */ > + DECLARE_OFFSETS(offsets, (paddr_t)virt); > > /* _PAGE_POPULATE and _PAGE_PRESENT should never be set together. */ > ASSERT((flags & (_PAGE_POPULATE|_PAGE_PRESENT)) != > (_PAGE_POPULATE|_PAGE_PRESENT)); > > - entry = &xen_second[second_linear_offset(addr)]; > - if ( !lpae_is_valid(*entry) || !lpae_is_table(*entry, 2) ) > + table = xen_map_table(root); > + for ( level = HYP_PT_ROOT_LEVEL; level < target; level++ ) > { > - int rc = create_xen_table(entry); > - if ( rc < 0 ) { > - printk("%s: L2 failed\n", __func__); > - return rc; > + rc = xen_pt_next_level(read_only, level, &table, offsets[level]); > + if ( rc == XEN_TABLE_MAP_FAILED ) > + { > + /* > + * We are here because xen_pt_next_level has failed to map > + * the intermediate page table (e.g the table does not exist > + * and the pt is read-only). It is a valid case when > + * removing a mapping as it may not exist in the page table. > + * In this case, just ignore it. > + */ > + if ( flags & (_PAGE_PRESENT|_PAGE_POPULATE) ) > + { > + mm_printk("%s: Unable to map level %u\n", __func__, level); > + rc = -ENOENT; > + goto out; > + } > + else > + { > + rc = 0; > + goto out; > + } > } > + else if ( rc != XEN_TABLE_NORMAL_PAGE ) > + break; > } > > - BUG_ON(!lpae_is_valid(*entry)); > + if ( level != target ) > + { > + mm_printk("%s: Shattering superpage is not supported\n", __func__); > + rc = -EOPNOTSUPP; > + goto out; > + } > > - third = xen_map_table(lpae_get_mfn(*entry)); > - entry = &third[third_table_offset(addr)]; > + entry = table + offsets[level]; > > rc = -EINVAL; > if ( !xen_pt_check_entry(*entry, mfn, flags) ) > @@ -1103,7 +1185,7 @@ static int xen_pt_update_entry(unsigned long addr, > mfn_t mfn, > rc = 0; > > out: > - xen_unmap_table(third); > + xen_unmap_table(table); > > return rc; > } > @@ -1119,6 +1201,15 @@ static int xen_pt_update(unsigned long virt, > unsigned long addr = virt, addr_end = addr + nr_mfns * PAGE_SIZE; > > /* > + * For arm32, page-tables are different on each CPUs. Yet, they share > + * some common mappings. It is assumed that only common mappings > + * will be modified with this function. > + * > + * XXX: Add a check. > + */ > + const mfn_t root = virt_to_mfn(THIS_CPU_PGTABLE); > + > + /* > * The hardware was configured to forbid mapping both writeable and > * executable. > * When modifying/creating mapping (i.e _PAGE_PRESENT is set), > @@ -1139,9 +1230,9 @@ static int xen_pt_update(unsigned long virt, > > spin_lock(&xen_pt_lock); > > - for( ; addr < addr_end; addr += PAGE_SIZE ) > + for ( ; addr < addr_end; addr += PAGE_SIZE ) > { > - rc = xen_pt_update_entry(addr, mfn, flags); > + rc = xen_pt_update_entry(root, addr, mfn, flags); > if ( rc ) > break; > > -- > 2.11.0 > _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxxxxxxxxx https://lists.xenproject.org/mailman/listinfo/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |