aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--xen/arch/arm/mm.c152
-rw-r--r--xen/arch/arm/smpboot.c6
-rw-r--r--xen/include/asm-arm/config.h4
-rw-r--r--xen/include/asm-arm/mm.h4
4 files changed, 146 insertions, 20 deletions
diff --git a/xen/arch/arm/mm.c b/xen/arch/arm/mm.c
index 656aa82b4b..03492dfa7d 100644
--- a/xen/arch/arm/mm.c
+++ b/xen/arch/arm/mm.c
@@ -39,22 +39,48 @@
struct domain *dom_xen, *dom_io, *dom_cow;
-/* Static start-of-day pagetables that we use before the allocators are up */
-/* boot_pgtable == root of the trie (zeroeth level on 64-bit, first on 32-bit) */
+/* Static start-of-day pagetables that we use before the
+ * allocators are up. These go on to become the boot CPU's real pagetables.
+ */
lpae_t boot_pgtable[LPAE_ENTRIES] __attribute__((__aligned__(4096)));
#ifdef CONFIG_ARM_64
lpae_t boot_first[LPAE_ENTRIES] __attribute__((__aligned__(4096)));
#endif
-/* N.B. The second-level table is 4 contiguous pages long, and covers
- * all addresses from 0 to 0xffffffff. Offsets into it are calculated
- * with second_linear_offset(), not second_table_offset(). */
+
+/*
+ * xen_pgtable and xen_dommap are per-PCPU and are allocated before
+ * bringing up each CPU. On 64-bit a first level table is also allocated.
+ *
+ * xen_second, xen_fixmap and xen_xenmap are shared between all PCPUs.
+ */
+
+/* Per-CPU pagetable pages */
+/* xen_pgtable == root of the trie (zeroeth level on 64-bit, first on 32-bit) */
+static DEFINE_PER_CPU(lpae_t *, xen_pgtable);
+/* xen_dommap == pages used by map_domain_page, these pages contain
+ * the second level pagetables which mapp the domheap region
+ * DOMHEAP_VIRT_START...DOMHEAP_VIRT_END in 2MB chunks. */
+static DEFINE_PER_CPU(lpae_t *, xen_dommap);
+
+/* Common pagetable leaves */
+/* Second level page tables.
+ *
+ * The second-level table is 2 contiguous pages long, and covers all
+ * addresses from 0 to 0x7fffffff. Offsets into it are calculated
+ * with second_linear_offset(), not second_table_offset().
+ *
+ * Addresses 0x80000000 to 0xffffffff are covered by the per-cpu
+ * xen_domheap mappings described above. However we allocate 4 pages
+ * here for use in the boot page tables and the second two pages
+ * become the boot CPUs xen_dommap pages.
+ */
lpae_t xen_second[LPAE_ENTRIES*4] __attribute__((__aligned__(4096*4)));
+/* First level page table used for fixmap */
lpae_t xen_fixmap[LPAE_ENTRIES] __attribute__((__aligned__(4096)));
+/* First level page table used to map Xen itself with the XN bit set
+ * as appropriate. */
static lpae_t xen_xenmap[LPAE_ENTRIES] __attribute__((__aligned__(4096)));
-/* boot_pgtable becomes the boot processors pagetable, eventually this will
- * become a per-cpu variable */
-#define xen_pgtable boot_pgtable
/* Non-boot CPUs use this to find the correct pagetables. */
uint64_t boot_ttbr;
@@ -118,12 +144,17 @@ done:
void dump_hyp_walk(vaddr_t addr)
{
uint64_t ttbr = READ_SYSREG64(TTBR0_EL2);
+ lpae_t *pgtable = this_cpu(xen_pgtable);
- printk("Walking Hypervisor VA 0x%"PRIvaddr" via TTBR 0x%016"PRIx64"\n",
- addr, ttbr);
+ printk("Walking Hypervisor VA 0x%"PRIvaddr" "
+ "on CPU%d via TTBR 0x%016"PRIx64"\n",
+ addr, smp_processor_id(), ttbr);
- BUG_ON( (lpae_t *)(unsigned long)(ttbr - phys_offset) != xen_pgtable );
- dump_pt_walk(xen_pgtable, addr);
+ if ( smp_processor_id() == 0 )
+ BUG_ON( (lpae_t *)(unsigned long)(ttbr - phys_offset) != pgtable );
+ else
+ BUG_ON( virt_to_maddr(pgtable) != ttbr );
+ dump_pt_walk(pgtable, addr);
}
/* Map a 4k page in a fixmap entry */
@@ -149,7 +180,7 @@ void clear_fixmap(unsigned map)
void *map_domain_page(unsigned long mfn)
{
unsigned long flags;
- lpae_t *map = xen_second + second_linear_offset(DOMHEAP_VIRT_START);
+ lpae_t *map = this_cpu(xen_dommap);
unsigned long slot_mfn = mfn & ~LPAE_ENTRY_MASK;
vaddr_t va;
lpae_t pte;
@@ -215,7 +246,7 @@ void *map_domain_page(unsigned long mfn)
void unmap_domain_page(const void *va)
{
unsigned long flags;
- lpae_t *map = xen_second + second_linear_offset(DOMHEAP_VIRT_START);
+ lpae_t *map = this_cpu(xen_dommap);
int slot = ((unsigned long) va - DOMHEAP_VIRT_START) >> SECOND_SHIFT;
local_irq_save(flags);
@@ -230,7 +261,7 @@ void unmap_domain_page(const void *va)
unsigned long domain_page_map_to_mfn(const void *va)
{
- lpae_t *map = xen_second + second_linear_offset(DOMHEAP_VIRT_START);
+ lpae_t *map = this_cpu(xen_dommap);
int slot = ((unsigned long) va - DOMHEAP_VIRT_START) >> SECOND_SHIFT;
unsigned long offset = ((unsigned long)va>>THIRD_SHIFT) & LPAE_ENTRY_MASK;
@@ -276,6 +307,16 @@ void __cpuinit setup_virt_paging(void)
WRITE_SYSREG32(0x80002558, VTCR_EL2); isb();
}
+/* This needs to be a macro to stop the compiler spilling to the stack
+ * which will change when we change pagetables */
+#define WRITE_TTBR(ttbr) \
+ flush_xen_text_tlb(); \
+ WRITE_SYSREG64(ttbr, TTBR0_EL2); \
+ dsb(); /* ensure memory accesses do not cross over the TTBR0 write */ \
+ /* flush_xen_text_tlb contains an initial isb which ensures the \
+ * write to TTBR0 has completed. */ \
+ flush_xen_text_tlb()
+
/* Boot-time pagetable setup.
* Changes here may need matching changes in head.S */
void __init setup_pagetables(unsigned long boot_phys_offset, paddr_t xen_paddr)
@@ -323,11 +364,8 @@ void __init setup_pagetables(unsigned long boot_phys_offset, paddr_t xen_paddr)
boot_ttbr = (uintptr_t) boot_pgtable + phys_offset;
flush_xen_dcache(boot_ttbr);
flush_xen_dcache_va_range((void*)dest_va, _end - _start);
- flush_xen_text_tlb();
- WRITE_SYSREG64(boot_ttbr, TTBR0_EL2);
- dsb(); /* Ensure visibility of HTTBR update */
- flush_xen_text_tlb();
+ WRITE_TTBR(boot_ttbr);
/* Undo the temporary map */
pte.bits = 0;
@@ -373,11 +411,87 @@ void __init setup_pagetables(unsigned long boot_phys_offset, paddr_t xen_paddr)
WRITE_SYSREG32(READ_SYSREG32(SCTLR_EL2) | SCTLR_WXN, SCTLR_EL2);
/* Flush everything after setting WXN bit. */
flush_xen_text_tlb();
+
+ per_cpu(xen_pgtable, 0) = boot_pgtable;
+ per_cpu(xen_dommap, 0) = xen_second +
+ second_linear_offset(DOMHEAP_VIRT_START);
+
+ /* Some of these slots may have been used during start of day and/or
+ * relocation. Make sure they are clear now. */
+ memset(this_cpu(xen_dommap), 0, DOMHEAP_SECOND_PAGES*PAGE_SIZE);
+ flush_xen_dcache_va_range(this_cpu(xen_dommap),
+ DOMHEAP_SECOND_PAGES*PAGE_SIZE);
+}
+
+int init_secondary_pagetables(int cpu)
+{
+ lpae_t *root, *first, *domheap, pte;
+ int i;
+
+ root = alloc_xenheap_page();
+#ifdef CONFIG_ARM_64
+ first = alloc_xenheap_page();
+#else
+ first = root; /* root == first level on 32-bit 3-level trie */
+#endif
+ domheap = alloc_xenheap_pages(get_order_from_pages(DOMHEAP_SECOND_PAGES), 0);
+
+ if ( root == NULL || domheap == NULL || first == NULL )
+ {
+ printk("Not enough free memory for secondary CPU%d pagetables\n", cpu);
+ free_xenheap_pages(domheap, get_order_from_pages(DOMHEAP_SECOND_PAGES));
+#ifdef CONFIG_ARM_64
+ free_xenheap_page(first);
+#endif
+ free_xenheap_page(root);
+ return -ENOMEM;
+ }
+
+ /* Initialise root pagetable from root of boot tables */
+ memcpy(root, boot_pgtable, PAGE_SIZE);
+
+#ifdef CONFIG_ARM_64
+ /* Initialise first pagetable from first level of boot tables, and
+ * hook into the new root. */
+ memcpy(first, boot_first, PAGE_SIZE);
+ pte = mfn_to_xen_entry(virt_to_mfn(first));
+ pte.pt.table = 1;
+ write_pte(root, pte);
+#endif
+
+ /* Ensure the domheap has no stray mappings */
+ memset(domheap, 0, DOMHEAP_SECOND_PAGES*PAGE_SIZE);
+
+ /* Update the first level mapping to reference the local CPUs
+ * domheap mapping pages. */
+ for ( i = 0; i < DOMHEAP_SECOND_PAGES; i++ )
+ {
+ pte = mfn_to_xen_entry(virt_to_mfn(domheap+i*LPAE_ENTRIES));
+ pte.pt.table = 1;
+ write_pte(&first[first_table_offset(DOMHEAP_VIRT_START+i*FIRST_SIZE)], pte);
+ }
+
+ flush_xen_dcache_va_range(root, PAGE_SIZE);
+#ifdef CONFIG_ARM_64
+ flush_xen_dcache_va_range(first, PAGE_SIZE);
+#endif
+ flush_xen_dcache_va_range(domheap, DOMHEAP_SECOND_PAGES*PAGE_SIZE);
+
+ per_cpu(xen_pgtable, cpu) = root;
+ per_cpu(xen_dommap, cpu) = domheap;
+
+ return 0;
}
/* MMU setup for secondary CPUS (which already have paging enabled) */
void __cpuinit mmu_init_secondary_cpu(void)
{
+ uint64_t ttbr;
+
+ /* Change to this CPU's pagetables */
+ ttbr = (uintptr_t)virt_to_maddr(this_cpu(xen_pgtable));
+ WRITE_TTBR(ttbr);
+
/* From now on, no mapping may be both writable and executable. */
WRITE_SYSREG32(READ_SYSREG32(SCTLR_EL2) | SCTLR_WXN, SCTLR_EL2);
flush_xen_text_tlb();
diff --git a/xen/arch/arm/smpboot.c b/xen/arch/arm/smpboot.c
index bd353c897d..8011987310 100644
--- a/xen/arch/arm/smpboot.c
+++ b/xen/arch/arm/smpboot.c
@@ -220,6 +220,12 @@ void stop_cpu(void)
/* Bring up a remote CPU */
int __cpu_up(unsigned int cpu)
{
+ int rc;
+
+ rc = init_secondary_pagetables(cpu);
+ if ( rc < 0 )
+ return rc;
+
/* Tell the remote CPU which stack to boot on. */
init_stack = idle_vcpu[cpu]->arch.stack;
diff --git a/xen/include/asm-arm/config.h b/xen/include/asm-arm/config.h
index 8be85634ed..e49aac132e 100644
--- a/xen/include/asm-arm/config.h
+++ b/xen/include/asm-arm/config.h
@@ -98,12 +98,16 @@
#define EARLY_VMAP_VIRT_START mk_unsigned_long(0x10000000)
#define XENHEAP_VIRT_START mk_unsigned_long(0x40000000)
#define DOMHEAP_VIRT_START mk_unsigned_long(0x80000000)
+#define DOMHEAP_VIRT_END mk_unsigned_long(0xffffffff)
#define EARLY_VMAP_VIRT_END XENHEAP_VIRT_START
#define HYPERVISOR_VIRT_START XEN_VIRT_START
#define DOMHEAP_ENTRIES 1024 /* 1024 2MB mapping slots */
+/* Number of domheap pagetable pages required at the second level (2MB mappings) */
+#define DOMHEAP_SECOND_PAGES ((DOMHEAP_VIRT_END - DOMHEAP_VIRT_START + 1) >> FIRST_SHIFT)
+
/* Fixmap slots */
#define FIXMAP_CONSOLE 0 /* The primary UART */
#define FIXMAP_PT 1 /* Temporary mappings of pagetable pages */
diff --git a/xen/include/asm-arm/mm.h b/xen/include/asm-arm/mm.h
index 4be383e447..26c271e747 100644
--- a/xen/include/asm-arm/mm.h
+++ b/xen/include/asm-arm/mm.h
@@ -138,7 +138,9 @@ extern unsigned long total_pages;
/* Boot-time pagetable setup */
extern void setup_pagetables(unsigned long boot_phys_offset, paddr_t xen_paddr);
-/* MMU setup for secondary CPUS (which already have paging enabled) */
+/* Allocate and initialise pagetables for a secondary CPU */
+extern int __cpuinit init_secondary_pagetables(int cpu);
+/* Switch secondary CPUS to its own pagetables and finalise MMU setup */
extern void __cpuinit mmu_init_secondary_cpu(void);
/* Second stage paging setup, to be called on all CPUs */
extern void __cpuinit setup_virt_paging(void);