Skip to contentSkip to conversation

PAE for xen

PATCH New Gerd Knorr 1 participant 1 message May 18, 2005 x86

lore
Share & tools
lore

Gerd Knorr

author
#0 lore raw

PATCH 1/1 PAE for xen

Hi,

This patch adds initial support for PAE paging to xen. It's against cset 1.1442 (today in the morning).

This patch does:

* boot Xen itself with PAE paging enabled.
* add PAE support to the dom0 domain builder.

Not yet done (some details come with another mail):

* fix hypercall interfaces to handle 64bit page table entries.
* actually use memory above 4GB (depends on hypercall interface changes).
* boot something else than domain 0.
* shadow mode support.

I'll submit xenlinux patches (hopefully) later this week or next week. Current state can be found @ http://dl.bytesex.org/patches/

Some notes on the design:

* There are two new config options: CONFIG_X86_PAE (boolean, same name Linux uses to simply things) and CONFIG_PAGING_LEVELS (int, possible values are 2,3,4). I've used #if CONFIG_PAGING_LEVELS for stuff which simply depends on the number of paging levels in the code common for x86-32/64, and CONFIG_X86_PAE for special PAE quirks or i386-only stuff. I've tried to avoid ifdefs if possible though, often I rearranged code to make it work in both PAE and non-PAE case instead.

* idle_pg_table: 3rd level is statically initialized, 2nd level is contignous in physical and virtual memory, so it can be addressed linear (the dom0 builder uses the same trick to simplify things a bit btw.). There are two new symbols:
idle_pg_table_l3 and idle_pg_table_l2 for the two tables. idle_pg_table is aliased to the toplevel page table, i.e. idle_pg_table_l3 in PAE mode and idle_pg_table_l2 in non-pae mode. The idle l3 table is actually never ever touched after boot, the l2 table is accessed via idle_pg_table_l2 and addressed linear in both PAE and non-PAE mode.

please apply. comments & questions are welcome.

Gerd

PS: Some bits in this patch are from Scott Parish <[email protected]>

include/asm-x86/config.h +23 -10
Index: xen/include/asm-x86/config.h
===================================================================
@@ -9,6 +9,19 @@
 
 #define CONFIG_VMX 1
 
+#if defined(__i386__)
+// # define CONFIG_X86_PAE 1   /* yes */
+# undef CONFIG_X86_PAE      /* no  */
+#endif
+
+#if defined(__x86_64)
+# define CONFIG_PAGING_LEVELS 4
+#elif defined(CONFIG_X86_PAE)
+# define CONFIG_PAGING_LEVELS 3
+#else
+# define CONFIG_PAGING_LEVELS 2
+#endif
+
 #define CONFIG_X86 1
 #define CONFIG_SHADOW 1
 
@@ -194,7 +207,7 @@ extern unsigned long _end; /* standard E
  *  Per-domain mappings                                  ( 4MB)
  *  Shadow linear pagetable                              ( 4MB) ( 8MB)
  *  Guest linear pagetable                               ( 4MB) ( 8MB)
- *  Machine-to-physical translation table [writable]     ( 4MB)
+ *  Machine-to-physical translation table [writable]     ( 4MB) (16MB)
  *  Frame-info table                                     (24MB) (96MB)
  *   * Start of guest inaccessible area
  *  Machine-to-physical translation table [read-only]    ( 4MB)
@@ -208,8 +221,8 @@ extern unsigned long _end; /* standard E
 
 #ifdef CONFIG_X86_PAE
 # define LINEARPT_MBYTES         8
-# define MACHPHYS_MBYTES         4 /* KAF: This needs to be bigger */
-# define FRAMETABLE_MBYTES	96 /* 16 GB mem limit (total)      */
+# define MACHPHYS_MBYTES        16 /* 1 MB needed per 1 GB memory */
+# define FRAMETABLE_MBYTES (MACHPHYS_MBYTES * 6)
 #else
 # define LINEARPT_MBYTES         4
 # define MACHPHYS_MBYTES         4
@@ -242,21 +255,21 @@ extern unsigned long _end; /* standard E
 #define GUEST_SEGMENT_MAX_ADDR  RO_MPT_VIRT_END
 
 #ifdef CONFIG_X86_PAE
-/* Hypervisor owns top 144MB of virtual address space. */
-# define __HYPERVISOR_VIRT_START  0xF7000000
-# define HYPERVISOR_VIRT_START   (0xF7000000UL)
+/* Hypervisor owns top 168MB of virtual address space. */
+# define __HYPERVISOR_VIRT_START  0xF5800000
+# define HYPERVISOR_VIRT_START   (0xF5800000UL)
 #else
 /* Hypervisor owns top 64MB of virtual address space. */
 # define __HYPERVISOR_VIRT_START  0xFC000000
 # define HYPERVISOR_VIRT_START   (0xFC000000UL)
 #endif
 
-#define ROOT_PAGETABLE_FIRST_XEN_SLOT \
+#define L2_PAGETABLE_FIRST_XEN_SLOT \
     (HYPERVISOR_VIRT_START >> L2_PAGETABLE_SHIFT)
-#define ROOT_PAGETABLE_LAST_XEN_SLOT  \
+#define L2_PAGETABLE_LAST_XEN_SLOT  \
     (~0UL >> L2_PAGETABLE_SHIFT)
-#define ROOT_PAGETABLE_XEN_SLOTS \
-    (ROOT_PAGETABLE_LAST_XEN_SLOT - ROOT_PAGETABLE_FIRST_XEN_SLOT + 1)
+#define L2_PAGETABLE_XEN_SLOTS \
+    (L2_PAGETABLE_LAST_XEN_SLOT - L2_PAGETABLE_FIRST_XEN_SLOT + 1)
 
 #define PGT_base_page_table PGT_l2_page_table
 
arch/x86/setup.c +1 -1
Index: xen/arch/x86/setup.c
===================================================================
@@ -70,7 +70,7 @@ extern int do_timer_lists_from_pit;
 
 struct cpuinfo_x86 boot_cpu_data = { 0, 0, 0, 0, -1 };
 
-#if defined(__x86_64__)
+#if CONFIG_PAGING_LEVELS > 2
 unsigned long mmu_cr4_features = X86_CR4_PSE | X86_CR4_PGE | X86_CR4_PAE;
 #else
 unsigned long mmu_cr4_features = X86_CR4_PSE | X86_CR4_PGE;
arch/x86/domain_build.c +74 -12

164 lines not on this page · raw message

include/asm-x86/page.h +28 -5
Index: xen/include/asm-x86/page.h
===================================================================
@@ -9,10 +9,14 @@
 #endif
 #define PAGE_MASK           (~(PAGE_SIZE-1))
 
+#ifndef __ASSEMBLY__
+# include <asm/types.h>
+#endif
+
 #if defined(__i386__)
-#include <asm/x86_32/page.h>
+# include <asm/x86_32/page.h>
 #elif defined(__x86_64__)
-#include <asm/x86_64/page.h>
+# include <asm/x86_64/page.h>
 #endif
 
 /* Convert a pointer to a page-table entry into pagetable slot index. */
@@ -21,9 +25,18 @@
 
 /* Page-table type. */
 #ifndef __ASSEMBLY__
-typedef struct { unsigned long pt_lo; } pagetable_t;
-#define pagetable_val(_x)   ((_x).pt_lo)
-#define pagetable_get_pfn(_x) ((_x).pt_lo >> PAGE_SHIFT)
+#if CONFIG_PAGING_LEVELS == 2
+/* x86_32 default */
+typedef struct { u32 pt; } pagetable_t;
+#elif CONFIG_PAGING_LEVELS == 3
+/* x86_32 PAE */
+typedef struct { u32 pt; } pagetable_t; /* FIXME */
+#elif CONFIG_PAGING_LEVELS == 4
+/* x86_64 */
+typedef struct { u64 pt; } pagetable_t;
+#endif
+#define pagetable_val(_x)   ((_x).pt)
+#define pagetable_get_pfn(_x) ((_x).pt >> PAGE_SHIFT)
 #define mk_pagetable(_x)    ( (pagetable_t) { (_x) } )
 #endif
 
@@ -39,6 +52,7 @@ typedef struct { unsigned long pt_lo; } 
 #define pfn_valid(_pfn)     ((_pfn) < max_page)
 
 /* High table entries are reserved by the hypervisor. */
+/* FIXME: this breaks with PAE -- kraxel */
 #define DOMAIN_ENTRIES_PER_L2_PAGETABLE     \
   (HYPERVISOR_VIRT_START >> L2_PAGETABLE_SHIFT)
 #define HYPERVISOR_ENTRIES_PER_L2_PAGETABLE \
@@ -73,7 +87,14 @@ typedef struct { unsigned long pt_lo; } 
 #define va_to_l1mfn(_ed, _va) \
     (l2e_get_pfn(linear_l2_table(_ed)[_va>>L2_PAGETABLE_SHIFT]))
 
+#if CONFIG_PAGING_LEVELS == 3
 extern root_pgentry_t idle_pg_table[ROOT_PAGETABLE_ENTRIES];
+extern l3_pgentry_t   idle_pg_table_l3[ROOT_PAGETABLE_ENTRIES];
+extern l2_pgentry_t   idle_pg_table_l2[ROOT_PAGETABLE_ENTRIES*L2_PAGETABLE_ENTRIES];
+#else
+extern root_pgentry_t idle_pg_table[ROOT_PAGETABLE_ENTRIES];
+extern l2_pgentry_t   idle_pg_table_l2[ROOT_PAGETABLE_ENTRIES];
+#endif
 
 extern void paging_init(void);
 
@@ -131,6 +152,8 @@ static __inline__ int get_order(unsigned
     return order;
 }
 
+extern void printk_page_flags(u32 flags);
+
 /* Map physical byte range (@p, @p+@s) at virt address @v in pagetable @pt. */
 extern int
 map_pages(
include/asm-x86/x86_32/page-2l.h new file +109 -0

109 lines not on this page · raw message

include/asm-x86/x86_32/page-3l.h new file +204 -0

204 lines not on this page · raw message

arch/x86/boot/x86_32.S +48 -1

77 lines not on this page · raw message

arch/x86/mm.c +251 -97

756 lines not on this page · raw message

include/asm-x86/x86_32/page.h +12 -103

141 lines not on this page · raw message

arch/x86/x86_32/traps.c +10 -7
Index: xen/arch/x86/x86_32/traps.c
===================================================================
@@ -160,21 +160,24 @@ void show_registers(struct cpu_user_regs
 
 void show_page_walk(unsigned long addr)
 {
-    unsigned long page;
+    l2_pgentry_t pmd;
+    l1_pgentry_t *pte;
 
     if ( addr < PAGE_OFFSET )
         return;
 
     printk("Pagetable walk from %08lx:\n", addr);
     
-    page = l2e_get_value(idle_pg_table[l2_table_offset(addr)]);
-    printk(" L2 = %08lx %s\n", page, (page & _PAGE_PSE) ? "(4MB)" : "");
-    if ( !(page & _PAGE_PRESENT) || (page & _PAGE_PSE) )
+    pmd = idle_pg_table_l2[l2_linear_offset(addr)];
+    printk(" L2 = %08llx %s\n", (u64)l2e_get_value(pmd),
+           (l2e_get_flags(pmd) & _PAGE_PSE) ? "(2/4MB)" : "");
+    if ( !(l2e_get_flags(pmd) & _PAGE_PRESENT) ||
+         (l2e_get_flags(pmd) & _PAGE_PSE) )
         return;
 
-    page &= PAGE_MASK;
-    page = ((unsigned long *) __va(page))[l1_table_offset(addr)];
-    printk("  L1 = %08lx\n", page);
+    pte  = __va(l2e_get_phys(pmd));
+    pte += l1_table_offset(addr);
+    printk("  L1 = %08llx\n", (u64)l1e_get_value(*pte));
 }
 
 #define DOUBLEFAULT_STACK_SIZE 1024
arch/x86/x86_32/mm.c +74 -40

171 lines not on this page · raw message

include/public/arch-x86_32.h +5 -1
Index: xen/include/public/arch-x86_32.h
===================================================================
@@ -72,7 +72,11 @@
  * Virtual addresses beyond this are not modifiable by guest OSes. The 
  * machine->physical mapping table starts at this address, read-only.
  */
-#define HYPERVISOR_VIRT_START (0xFC000000UL)
+#ifdef CONFIG_X86_PAE
+# define HYPERVISOR_VIRT_START (0xF5800000UL)
+#else
+# define HYPERVISOR_VIRT_START (0xFC000000UL)
+#endif
 #ifndef machine_to_phys_mapping
 #define machine_to_phys_mapping ((u32 *)HYPERVISOR_VIRT_START)
 #endif
common/elf.c +14 -1
Index: xen/common/elf.c
===================================================================
@@ -82,7 +82,20 @@ int parseelfimage(struct domain_setup_in
             printk("ERROR: Xen will only load images built for Xen v3.0\n");
             return -EINVAL;
         }
-
+#if defined(__i386__)
+#ifdef CONFIG_X86_PAE
+        int xen_pae = 1;
+#else
+        int xen_pae = 0;
+#endif
+        int guest_pae = strstr(guestinfo, "PAE=yes") ? 1 : 0;
+        if (xen_pae != guest_pae) {
+            printk("ERROR: PAE mode mismatch (xen=%s,guest=%s)\n",
+                   xen_pae   ? "yes" : "no",
+                   guest_pae ? "yes" : "no");
+            return -EINVAL;
+        }
+#endif
         break;
     }
     if ( guestinfo == NULL )
arch/x86/x86_32/domain_page.c +1 -1
Index: xen/arch/x86/x86_32/domain_page.c
===================================================================
@@ -72,7 +72,7 @@ void *map_domain_mem(unsigned long pa)
             shadow_epoch[cpu] = ++epoch;
         }
     }
-    while ( l1e_get_value(cache[idx]) != 0 );
+    while ( l1e_get_flags(cache[idx]) & _PAGE_PRESENT );
 
     cache[idx] = l1e_create_phys(pa, __PAGE_HYPERVISOR);
 
include/asm-x86/mm.h +7 -7
Index: xen/include/asm-x86/mm.h
===================================================================
@@ -76,15 +76,15 @@ struct pfn_info
  /* Owning guest has pinned this page to its current type? */
 #define _PGT_pinned         27
 #define PGT_pinned          (1U<<_PGT_pinned)
- /* The 10 most significant bits of virt address if this is a page table. */
-#define PGT_va_shift        17
-#define PGT_va_mask         (((1U<<10)-1)<<PGT_va_shift)
+ /* The 11 most significant bits of virt address if this is a page table. */
+#define PGT_va_shift        16
+#define PGT_va_mask         (((1U<<11)-1)<<PGT_va_shift)
  /* Is the back pointer still mutable (i.e. not fixed yet)? */
-#define PGT_va_mutable      (((1U<<10)-1)<<PGT_va_shift)
+#define PGT_va_mutable      (((1U<<11)-1)<<PGT_va_shift)
  /* Is the back pointer unknown (e.g., p.t. is mapped at multiple VAs)? */
-#define PGT_va_unknown      (((1U<<10)-2)<<PGT_va_shift)
- /* 17-bit count of uses of this frame as its current type. */
-#define PGT_count_mask      ((1U<<17)-1)
+#define PGT_va_unknown      (((1U<<11)-2)<<PGT_va_shift)
+ /* 16-bit count of uses of this frame as its current type. */
+#define PGT_count_mask      ((1U<<16)-1)
 
 #define PGT_mfn_mask        ((1U<<20)-1) /* mfn mask for shadow types */
 
arch/x86/shadow.c +23 -14
Index: xen/arch/x86/shadow.c
===================================================================
@@ -358,13 +358,13 @@ free_shadow_hl2_table(struct domain *d, 
 }
 
 static void inline
-free_shadow_l2_table(struct domain *d, unsigned long smfn)
+free_shadow_l2_table(struct domain *d, unsigned long smfn, unsigned int type)
 {
     l2_pgentry_t *pl2e = map_domain_mem(smfn << PAGE_SHIFT);
     int i, external = shadow_mode_external(d);
 
     for ( i = 0; i < L2_PAGETABLE_ENTRIES; i++ )
-        if ( external || is_guest_l2_slot(i) )
+        if ( external || is_guest_l2_slot(type, i) )
             if ( l2e_get_flags(pl2e[i]) & _PAGE_PRESENT )
                 put_shadow_ref(l2e_get_pfn(pl2e[i]));
 
@@ -404,7 +404,7 @@ void free_shadow_page(unsigned long smfn
     case PGT_l2_shadow:
         perfc_decr(shadow_l2_pages);
         shadow_demote(d, gpfn, gmfn);
-        free_shadow_l2_table(d, smfn);
+        free_shadow_l2_table(d, smfn, page->u.inuse.type_info);
         break;
 
     case PGT_hl2_shadow:
@@ -1093,7 +1093,8 @@ translate_l1pgtable(struct domain *d, l1
 // up dom0.
 //
 void
-translate_l2pgtable(struct domain *d, l1_pgentry_t *p2m, unsigned long l2mfn)
+translate_l2pgtable(struct domain *d, l1_pgentry_t *p2m, unsigned long l2mfn,
+                    unsigned int type)
 {
     int i;
     l2_pgentry_t *l2;
@@ -1103,7 +1104,7 @@ translate_l2pgtable(struct domain *d, l1
     l2 = map_domain_mem(l2mfn << PAGE_SHIFT);
     for (i = 0; i < L2_PAGETABLE_ENTRIES; i++)
     {
-        if ( is_guest_l2_slot(i) &&
+        if ( is_guest_l2_slot(type, i) &&
              (l2e_get_flags(l2[i]) & _PAGE_PRESENT) )
         {
             unsigned long mfn = l2e_get_pfn(l2[i]);
@@ -1409,8 +1410,8 @@ gpfn_to_mfn_foreign(struct domain *d, un
     unmap_domain_mem(l2);
     if ( !(l2e_get_flags(l2e) & _PAGE_PRESENT) )
     {
-        printk("gpfn_to_mfn_foreign(d->id=%d, gpfn=%lx) => 0 l2e=%lx\n",
-               d->domain_id, gpfn, l2e_get_value(l2e));
+        printk("gpfn_to_mfn_foreign(d->id=%d, gpfn=%lx) => 0 l2e=%llx\n",
+               d->domain_id, gpfn, (u64)l2e_get_value(l2e));
         return INVALID_MFN;
     }
     unsigned long l1tab = l2e_get_phys(l2e);
@@ -1425,8 +1426,8 @@ gpfn_to_mfn_foreign(struct domain *d, un
 
     if ( !(l1e_get_flags(l1e) & _PAGE_PRESENT) )
     {
-        printk("gpfn_to_mfn_foreign(d->id=%d, gpfn=%lx) => 0 l1e=%lx\n",
-               d->domain_id, gpfn, l1e_get_value(l1e));
+        printk("gpfn_to_mfn_foreign(d->id=%d, gpfn=%lx) => 0 l1e=%llx\n",
+               d->domain_id, gpfn, (u64)l1e_get_value(l1e));
         return INVALID_MFN;
     }
 
@@ -2389,7 +2390,10 @@ static int resync_all(struct domain *d, 
             changed = 0;
             for ( i = 0; i < L2_PAGETABLE_ENTRIES; i++ )
             {
-                if ( !is_guest_l2_slot(i) && !external )
+#if CONFIG_X86_PAE
+                BUG();  /* FIXME: need type_info */
+#endif
+                if ( !is_guest_l2_slot(0,i) && !external )
                     continue;
 
                 l2_pgentry_t new_pde = guest2[i];
@@ -2432,7 +2436,10 @@ static int resync_all(struct domain *d, 
             changed = 0;
             for ( i = 0; i < L2_PAGETABLE_ENTRIES; i++ )
             {
-                if ( !is_guest_l2_slot(i) && !external )
+#if CONFIG_X86_PAE
+                BUG();  /* FIXME: need type_info */
+#endif
+                if ( !is_guest_l2_slot(0, i) && !external )
                     continue;
 
                 l2_pgentry_t new_pde = guest2[i];
@@ -2645,8 +2652,8 @@ int shadow_fault(unsigned long va, struc
                                      &gpte, sizeof(gpte))) )
         {
             printk("%s() failed, crashing domain %d "
-                   "due to a read-only L2 page table (gpde=%lx), va=%lx\n",
-                   __func__, d->domain_id, l2e_get_value(gpde), va);
+                   "due to a read-only L2 page table (gpde=%llx), va=%lx\n",
+                   __func__,d->domain_id, (u64)l2e_get_value(gpde), va);
             domain_crash_synchronous();
         }
 
@@ -2719,7 +2726,7 @@ void shadow_l2_normal_pt_update(
     shadow_unlock(d);
 }
 
-#ifdef __x86_64__
+#if CONFIG_PAGING_LEVELS >= 3
 void shadow_l3_normal_pt_update(
     struct domain *d,
     unsigned long pa, l3_pgentry_t gpde,
@@ -2727,7 +2734,9 @@ void shadow_l3_normal_pt_update(
 {
     BUG(); // not yet implemented
 }
+#endif
 
+#if CONFIG_PAGING_LEVELS >= 4
 void shadow_l4_normal_pt_update(
     struct domain *d,
     unsigned long pa, l4_pgentry_t gpde,
include/asm-x86/types.h +6 -0
Index: xen/include/asm-x86/types.h
===================================================================
@@ -44,11 +44,17 @@ typedef signed long long s64;
 typedef unsigned long long u64;
 #define BITS_PER_LONG 32
 typedef unsigned int size_t;
+#if defined(CONFIG_X86_PAE)
+typedef u64 physaddr_t;
+#else
+typedef u32 physaddr_t;
+#endif
 #elif defined(__x86_64__)
 typedef signed long s64;
 typedef unsigned long u64;
 #define BITS_PER_LONG 64
 typedef unsigned long size_t;
+typedef u64 physaddr_t;
 #endif
 
 /* DMA addresses come in generic and 64-bit flavours.  */
include/asm-x86/shadow.h +3 -1
Index: xen/include/asm-x86/shadow.h
===================================================================
@@ -126,10 +126,12 @@ extern void shadow_l1_normal_pt_update(s
 extern void shadow_l2_normal_pt_update(struct domain *d,
                                        unsigned long pa, l2_pgentry_t l2e,
                                        struct map_dom_mem_cache *cache);
-#ifdef __x86_64__
+#if CONFIG_PAGING_LEVELS >= 3
 extern void shadow_l3_normal_pt_update(struct domain *d,
                                        unsigned long pa, l3_pgentry_t l3e,
                                        struct map_dom_mem_cache *cache);
+#endif
+#if CONFIG_PAGING_LEVELS >= 4
 extern void shadow_l4_normal_pt_update(struct domain *d,
                                        unsigned long pa, l4_pgentry_t l4e,
                                        struct map_dom_mem_cache *cache);
include/asm-x86/smp.h +1 -1
Index: xen/include/asm-x86/smp.h
===================================================================
@@ -38,7 +38,7 @@ extern cpumask_t cpu_sibling_map[];
 extern void smp_flush_tlb(void);
 extern void smp_invalidate_rcv(void);		/* Process an NMI */
 extern void (*mtrr_hook) (void);
-extern void zap_low_mappings (void);
+extern void zap_low_mappings(l2_pgentry_t *base);
 
 #define MAX_APICID 256
 extern u8 x86_cpu_to_apicid[];