diff mbox

[Xen-devel,RFC,3/9] xen: arm: handle concatenated root tables in dump_pt_walk

Message ID c9754e1a775ab757183662ad4cbf8c6ccf8f0ad5.1406728037.git.ian.campbell@citrix.com
State New
Headers show

Commit Message

Ian Campbell July 30, 2014, 1:47 p.m. UTC
ARM allows for the concatenation of pages at the root of a p2m (but not a
regular page table) in order to support a larger IPA space than the number of
levels in the P2M would normally support. We use this to support 40-bit guest
addresses.

Previously we were unable to dump IPAs which were outside the first page of the
root. To fix this we adjust dump_pt_walk to take the machine address of the
page table root instead of expecting the caller to have mapper it. This allows
the walker code to select the correct page to map.

Signed-off-by: Ian Campbell <ian.campbell@citrix.com>
---
 xen/arch/arm/mm.c          |   45 +++++++++++++++++++++++++++++++-------------
 xen/arch/arm/p2m.c         |   13 +++----------
 xen/include/asm-arm/page.h |   15 +++++++++++++--
 3 files changed, 48 insertions(+), 25 deletions(-)

Comments

Julien Grall July 30, 2014, 4:58 p.m. UTC | #1
Hi Ian,

On 07/30/2014 02:47 PM, Ian Campbell wrote:
> ARM allows for the concatenation of pages at the root of a p2m (but not a
> regular page table) in order to support a larger IPA space than the number of
> levels in the P2M would normally support. We use this to support 40-bit guest
> addresses.
> 
> Previously we were unable to dump IPAs which were outside the first page of the
> root. To fix this we adjust dump_pt_walk to take the machine address of the
> page table root instead of expecting the caller to have mapper it. This allows
> the walker code to select the correct page to map.
> 
> Signed-off-by: Ian Campbell <ian.campbell@citrix.com>
> ---
>  xen/arch/arm/mm.c          |   45 +++++++++++++++++++++++++++++++-------------
>  xen/arch/arm/p2m.c         |   13 +++----------
>  xen/include/asm-arm/page.h |   15 +++++++++++++--
>  3 files changed, 48 insertions(+), 25 deletions(-)
> 
> diff --git a/xen/arch/arm/mm.c b/xen/arch/arm/mm.c
> index fa6a729..4ff783a 100644
> --- a/xen/arch/arm/mm.c
> +++ b/xen/arch/arm/mm.c
> @@ -167,50 +167,69 @@ static inline void check_memory_layout_alignment_constraints(void) {
>  #endif
>  }
>  
> -void dump_pt_walk(lpae_t *root, paddr_t addr, int root_level)
> +void dump_pt_walk(paddr_t ttbr, paddr_t addr, int root_level, int nr_root_tables)

I should have said that yon the previous patch...

Both root_level and nr_root_tables can't be negative. I would use
unsigned int here.

>  {
>      static const char *level_strs[4] = { "0TH", "1ST", "2ND", "3RD" };
> +    const unsigned long root_pfn = paddr_to_pfn(ttbr);
>      const unsigned int offsets[4] = {
>          zeroeth_table_offset(addr),
>          first_table_offset(addr),
>          second_table_offset(addr),
>          third_table_offset(addr)
>      };
> -    lpae_t pte, *mappings[4] = { 0, };
> -    int level;
> +    lpae_t pte, *mapping;
> +    int level, root_table;

unsigned int here too.

>  #ifdef CONFIG_ARM_32
>      BUG_ON(root_level < 1);
>  #endif
>  
> -    mappings[root_level] = root;
> +    if ( nr_root_tables > 1 )
> +    {
> +        /*
> +         * Concatenated root-level tables. The table number will be
> +         * the offset at the previous level. It is not possible to
> +         * concetenate a level-0 root.

concatenate

> +         */
> +        BUG_ON(root_level == 0);
> +        root_table = offsets[root_level - 1];
> +        printk("Using concatenated root table %d\n", root_table);
> +        if ( root_table >= nr_root_tables )
> +        {
> +            printk("Invalid root table offset\n");
> +            return;
> +        }
> +    }
> +    else
> +        root_table = 0;
> +
> +    mapping = map_domain_page(root_pfn + root_table);
>  
>      for ( level = root_level; level < 4; level++ )
>      {
>          if ( offsets[level] > LPAE_ENTRIES )
>              break;
>  
> -        if ( !mappings[level] )
> +        if ( !mapping )
>          {
>              printk("%s: Failed to map PT page\n", level_strs[level]);
>              break;
>          }
>  
> -        pte = mappings[level][offsets[level]];
> +        pte = mapping[offsets[level]];
>  
>          printk("%s[0x%x] = 0x%"PRIpaddr"\n",
>                 level_strs[level], offsets[level], pte.bits);
> +

Spurious change, maybe it belong to the previous patch?

[..]


>  /* Map a 4k page in a fixmap entry */
> diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c
> index 64efdce..6839acf 100644
> --- a/xen/arch/arm/p2m.c
> +++ b/xen/arch/arm/p2m.c
> @@ -16,6 +16,7 @@
>  /* First level P2M is 2 consecutive pages */
>  #define P2M_ROOT_ORDER 1
>  #define P2M_ROOT_ENTRIES (LPAE_ENTRIES<<P2M_ROOT_ORDER)
> +#define P2M_ROOT_PAGES    (1<<P2M_ROOT_ORDER)
>  
>  static bool_t p2m_valid(lpae_t pte)
>  {
> @@ -52,22 +53,14 @@ void p2m_dump_info(struct domain *d)
>  void dump_p2m_lookup(struct domain *d, paddr_t addr)
>  {
>      struct p2m_domain *p2m = &d->arch.p2m;
> -    lpae_t *first;
>  
>      printk("dom%d IPA 0x%"PRIpaddr"\n", d->domain_id, addr);
>  
> -    if ( first_linear_offset(addr) > LPAE_ENTRIES )
> -    {
> -        printk("Cannot dump addresses in second of first level pages...\n");
> -        return;
> -    }
> -
>      printk("P2M @ %p mfn:0x%lx\n",
>             p2m->root, page_to_mfn(p2m->root));
>  
> -    first = __map_domain_page(p2m->root);
> -    dump_pt_walk(first, addr, P2M_ROOT_LEVEL);
> -    unmap_domain_page(first);
> +    dump_pt_walk(page_to_maddr(p2m->root), addr,

You can directly use p2m->vttbr and avoid to call page_to_maddr.

Regards,
diff mbox

Patch

diff --git a/xen/arch/arm/mm.c b/xen/arch/arm/mm.c
index fa6a729..4ff783a 100644
--- a/xen/arch/arm/mm.c
+++ b/xen/arch/arm/mm.c
@@ -167,50 +167,69 @@  static inline void check_memory_layout_alignment_constraints(void) {
 #endif
 }
 
-void dump_pt_walk(lpae_t *root, paddr_t addr, int root_level)
+void dump_pt_walk(paddr_t ttbr, paddr_t addr, int root_level, int nr_root_tables)
 {
     static const char *level_strs[4] = { "0TH", "1ST", "2ND", "3RD" };
+    const unsigned long root_pfn = paddr_to_pfn(ttbr);
     const unsigned int offsets[4] = {
         zeroeth_table_offset(addr),
         first_table_offset(addr),
         second_table_offset(addr),
         third_table_offset(addr)
     };
-    lpae_t pte, *mappings[4] = { 0, };
-    int level;
+    lpae_t pte, *mapping;
+    int level, root_table;
 
 #ifdef CONFIG_ARM_32
     BUG_ON(root_level < 1);
 #endif
 
-    mappings[root_level] = root;
+    if ( nr_root_tables > 1 )
+    {
+        /*
+         * Concatenated root-level tables. The table number will be
+         * the offset at the previous level. It is not possible to
+         * concetenate a level-0 root.
+         */
+        BUG_ON(root_level == 0);
+        root_table = offsets[root_level - 1];
+        printk("Using concatenated root table %d\n", root_table);
+        if ( root_table >= nr_root_tables )
+        {
+            printk("Invalid root table offset\n");
+            return;
+        }
+    }
+    else
+        root_table = 0;
+
+    mapping = map_domain_page(root_pfn + root_table);
 
     for ( level = root_level; level < 4; level++ )
     {
         if ( offsets[level] > LPAE_ENTRIES )
             break;
 
-        if ( !mappings[level] )
+        if ( !mapping )
         {
             printk("%s: Failed to map PT page\n", level_strs[level]);
             break;
         }
 
-        pte = mappings[level][offsets[level]];
+        pte = mapping[offsets[level]];
 
         printk("%s[0x%x] = 0x%"PRIpaddr"\n",
                level_strs[level], offsets[level], pte.bits);
+
         if ( !pte.walk.valid || !pte.walk.table )
             break;
 
-        mappings[level+1] = map_domain_page(pte.walk.base);
+        /* For next iteration */
+        unmap_domain_page(mapping);
+        mapping = map_domain_page(pte.walk.base);
     }
 
-    /* mappings[root_level] is provided by the caller */
-    for ( level = root_level + 1 ; level < 4; level++ )
-    {
-        unmap_domain_page(mappings[level]);
-    }
+    unmap_domain_page(mapping);
 }
 
 void dump_hyp_walk(vaddr_t addr)
@@ -226,7 +245,7 @@  void dump_hyp_walk(vaddr_t addr)
         BUG_ON( (lpae_t *)(unsigned long)(ttbr - phys_offset) != pgtable );
     else
         BUG_ON( virt_to_maddr(pgtable) != ttbr );
-    dump_pt_walk(pgtable, addr, HYP_PT_ROOT_LEVEL);
+    dump_pt_walk(ttbr, addr, HYP_PT_ROOT_LEVEL, 1);
 }
 
 /* Map a 4k page in a fixmap entry */
diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c
index 64efdce..6839acf 100644
--- a/xen/arch/arm/p2m.c
+++ b/xen/arch/arm/p2m.c
@@ -16,6 +16,7 @@ 
 /* First level P2M is 2 consecutive pages */
 #define P2M_ROOT_ORDER 1
 #define P2M_ROOT_ENTRIES (LPAE_ENTRIES<<P2M_ROOT_ORDER)
+#define P2M_ROOT_PAGES    (1<<P2M_ROOT_ORDER)
 
 static bool_t p2m_valid(lpae_t pte)
 {
@@ -52,22 +53,14 @@  void p2m_dump_info(struct domain *d)
 void dump_p2m_lookup(struct domain *d, paddr_t addr)
 {
     struct p2m_domain *p2m = &d->arch.p2m;
-    lpae_t *first;
 
     printk("dom%d IPA 0x%"PRIpaddr"\n", d->domain_id, addr);
 
-    if ( first_linear_offset(addr) > LPAE_ENTRIES )
-    {
-        printk("Cannot dump addresses in second of first level pages...\n");
-        return;
-    }
-
     printk("P2M @ %p mfn:0x%lx\n",
            p2m->root, page_to_mfn(p2m->root));
 
-    first = __map_domain_page(p2m->root);
-    dump_pt_walk(first, addr, P2M_ROOT_LEVEL);
-    unmap_domain_page(first);
+    dump_pt_walk(page_to_maddr(p2m->root), addr,
+                 P2M_ROOT_LEVEL, P2M_ROOT_PAGES);
 }
 
 static void p2m_load_VTTBR(struct domain *d)
diff --git a/xen/include/asm-arm/page.h b/xen/include/asm-arm/page.h
index d1f8d52..c1c8680 100644
--- a/xen/include/asm-arm/page.h
+++ b/xen/include/asm-arm/page.h
@@ -351,8 +351,19 @@  static inline void flush_xen_data_tlb_range_va(unsigned long va,
 /* Flush the dcache for an entire page. */
 void flush_page_to_ram(unsigned long mfn);
 
-/* Print a walk of an arbitrary page table */
-void dump_pt_walk(lpae_t *table, paddr_t addr, int root_level);
+/*
+ * Print a walk of a page table or p2m
+ *
+ * ttbr is the base address register (TTBR0_EL2 or VTTBR_EL2)
+ * addr is the PA or IPA to translate
+ * root_level is the starting level of the page table
+ *   (e.g. TCR_EL2.SL0 or VTCR_EL2.SL0 )
+ * nr_root_tables is the number of concatenated tables at the root.
+ *   this can only be != 1 for P2M walks starting at the first or
+ *   subsequent level.
+ */
+void dump_pt_walk(paddr_t ttbr, paddr_t addr,
+                  int root_level, int nr_root_tables);
 
 /* Print a walk of the hypervisor's page tables for a virtual addr. */
 extern void dump_hyp_walk(vaddr_t addr);