PAE for xen
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
@@ -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
@@ -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
@@ -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(unsignedreturn 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
@@ -160,21 +160,24 @@ void show_registers(struct cpu_user_regsvoid 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
@@ -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
@@ -82,7 +82,20 @@ int parseelfimage(struct domain_setup_inprintk ("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
@@ -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
@@ -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
@@ -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 smfncase 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, ununmap_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, unif ( !(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
@@ -44,11 +44,17 @@ typedef signed long long s64;typedef unsigned long long u64 ;#define BITS_PER_LONG 32typedef 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 64typedef 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
@@ -126,10 +126,12 @@ extern void shadow_l1_normal_pt_update(sextern 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
@@ -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 256extern u8 x86_cpu_to_apicid[];