aboutsummaryrefslogtreecommitdiffstats
path: root/xen/arch/arm/arm64/head.S
diff options
context:
space:
mode:
Diffstat (limited to 'xen/arch/arm/arm64/head.S')
-rw-r--r--xen/arch/arm/arm64/head.S396
1 files changed, 272 insertions, 124 deletions
diff --git a/xen/arch/arm/arm64/head.S b/xen/arch/arm/arm64/head.S
index ac1b75ab5f..b2d44ccf4c 100644
--- a/xen/arch/arm/arm64/head.S
+++ b/xen/arch/arm/arm64/head.S
@@ -33,6 +33,41 @@
#include EARLY_PRINTK_INC
#endif
+/*
+ * Common register usage in this file:
+ * x0 -
+ * x1 -
+ * x2 -
+ * x3 -
+ * x4 -
+ * x5 -
+ * x6 -
+ * x7 -
+ * x8 -
+ * x9 -
+ * x10 -
+ * x11 -
+ * x12 -
+ * x13 -
+ * x14 -
+ * x15 -
+ * x16 -
+ * x17 -
+ * x18 -
+ * x19 - paddr(start)
+ * x20 - phys offset
+ * x21 - DTB address (boot cpu only)
+ * x22 - is_secondary_cpu
+ * x23 - UART address
+ * x24 - cpuid
+ * x25 -
+ * x26 -
+ * x27 -
+ * x28 -
+ * x29 -
+ * x30 - lr
+ */
+
/* Macro to print a string to the UART, if there is one.
* Clobbers x0-x3. */
#ifdef EARLY_PRINTK
@@ -65,7 +100,6 @@
.global start
start:
-GLOBAL(init_secondary) /* currently unused */
/*
* DO NOT MODIFY. Image header expected by Linux boot-loaders.
*/
@@ -100,69 +134,73 @@ real_start:
add x21, x21, x20 /* x21 := paddr(DTB) */
#endif
- /* Are we the boot CPU? */
- mov x22, #0 /* x22 := CPU ID */
+ mov x22, #0 /* x22 := is_secondary_cpu */
+
+ b common_start
+
+GLOBAL(init_secondary)
+ msr DAIFSet, 0xf /* Disable all interrupts */
+
+ /* Find out where we are */
+ ldr x0, =start
+ adr x19, start /* x19 := paddr (start) */
+ sub x20, x19, x0 /* x20 := phys-offset */
+
+ mov x22, #1 /* x22 := is_secondary_cpu */
+
+common_start:
+ mov x24, #0 /* x24 := CPU ID. Initialy zero until we
+ * find that multiprocessor extensions are
+ * present and the system is SMP */
mrs x0, mpidr_el1
- tbz x0, 31, boot_cpu /* Multiprocessor extension supported? */
- tbnz x0, 30, boot_cpu /* Uniprocessor system? */
+ tbz x0, 31, 1f /* Multiprocessor extension not supported? */
+ tbnz x0, 30, 1f /* Uniprocessor system? */
mov x13, #(0xff << 24)
- bics x22, x0, x13 /* Mask out flags to get CPU ID */
- b.eq boot_cpu /* If we're CPU 0, boot now */
-
- /* Non-boot CPUs wait here to be woken up one at a time. */
-1: dsb sy
- ldr x0, =smp_up_cpu /* VA of gate */
- add x0, x0, x20 /* PA of gate */
- ldr x1, [x0] /* Which CPU is being booted? */
- cmp x1, x22 /* Is it us? */
- b.eq 2f
+ bic x24, x0, x13 /* Mask out flags to get CPU ID */
+1:
+
+ /* Non-boot CPUs wait here until __cpu_up is ready for them */
+ cbz x22, 1f
+
+ ldr x0, =smp_up_cpu
+ add x0, x0, x20 /* Apply physical offset */
+ dsb sy
+2: ldr x1, [x0]
+ cmp x1, x24
+ beq 1f
wfe
- b 1b
-2:
+ b 2b
+1:
-boot_cpu:
#ifdef EARLY_PRINTK
ldr x23, =EARLY_UART_BASE_ADDRESS /* x23 := UART base address */
cbnz x22, 1f
-#ifdef EARLY_PRINTK_INIT_UART
- bl init_uart /* CPU 0 sets up the UART too */
-#endif
+ bl init_uart /* Boot CPU sets up the UART too */
1: PRINT("- CPU ")
- mov x0, x22
+ mov x0, x24
bl putn
PRINT(" booting -\r\n")
#endif
PRINT("- Current EL ")
- mrs x0, CurrentEL
+ mrs x4, CurrentEL
+ mov x0, x4
bl putn
PRINT(" -\r\n")
- /* Are we in EL3 */
- mrs x0, CurrentEL
- cmp x0, #PSR_MODE_EL3t
- ccmp x0, #PSR_MODE_EL3h, #0x4, ne
- b.eq 1f /* Yes */
-
/* Are we in EL2 */
- cmp x0, #PSR_MODE_EL2t
- ccmp x0, #PSR_MODE_EL2h, #0x4, ne
- b.eq 2f /* Yes */
+ cmp x4, #PSR_MODE_EL2t
+ ccmp x4, #PSR_MODE_EL2h, #0x4, ne
+ b.eq el2 /* Yes */
- /* Otherwise, it must have been EL0 or EL1 */
- PRINT("- CPU is not in EL3 or EL2 -\r\n")
- b fail
+ /* OK, we're boned. */
+ PRINT("- Xen must be entered in NS EL2 mode -\r\n" \
+ "- Please update the bootloader -\r\n")
+ b fail
-1: PRINT("- Started in EL3 -\r\n- Entering EL2 -\r\n")
- ldr x1, =enter_el2_mode /* VA of function */
- add x1, x1, x20 /* PA of function */
- adr x30, el2 /* Set return address for call */
- br x1 /* Call function */
+el2: PRINT("- Xen starting at EL2 -\r\n")
-2: PRINT("- Started in EL2 mode -\r\n")
-
-el2:
/* Zero BSS On the boot CPU to avoid nasty surprises */
cbnz x22, skip_bss
@@ -177,9 +215,10 @@ el2:
b.lo 1b
skip_bss:
-
PRINT("- Setting up control registers -\r\n")
+ /* XXXX call PROCINFO_cpu_init here */
+
/* Set up memory attribute type tables */
ldr x0, =MAIRVAL
msr mair_el2, x0
@@ -193,7 +232,7 @@ skip_bss:
ldr x0, =0x80802500
msr tcr_el2, x0
- /* Set up the HSCTLR:
+ /* Set up the SCTLR_EL2:
* Exceptions in LE ARM,
* Low-latency IRQs disabled,
* Write-implies-XN disabled (for now),
@@ -204,69 +243,90 @@ skip_bss:
ldr x0, =(HSCTLR_BASE|SCTLR_A)
msr SCTLR_EL2, x0
- /* Write Xen's PT's paddr into the HTTBR */
+ /* Rebuild the boot pagetable's first-level entries. The structure
+ * is described in mm.c.
+ *
+ * After the CPU enables paging it will add the fixmap mapping
+ * to these page tables, however this may clash with the 1:1
+ * mapping. So each CPU must rebuild the page tables here with
+ * the 1:1 in place. */
+
+ /* Write Xen's PT's paddr into TTBR0_EL2 */
ldr x4, =boot_pgtable
- add x4, x4, x20 /* x4 := paddr (xen_pagetable) */
+ add x4, x4, x20 /* x4 := paddr (boot_pagetable) */
msr TTBR0_EL2, x4
- /* Non-boot CPUs don't need to rebuild the pagetable */
- cbnz x22, pt_ready
-
+ /* Setup boot_pgtable: */
ldr x1, =boot_first
- add x1, x1, x20 /* x1 := paddr (xen_first) */
- mov x3, #PT_PT /* x2 := table map of xen_first */
- orr x2, x1, x3 /* (+ rights for linear PT) */
- str x2, [x4, #0] /* Map it in slot 0 */
+ add x1, x1, x20 /* x1 := paddr (boot_first) */
- mov x4, x1 /* Next level into xen_first */
+ /* ... map boot_first in boot_pgtable[0] */
+ mov x3, #PT_PT /* x2 := table map of boot_first */
+ orr x2, x1, x3 /* + rights for linear PT */
+ str x2, [x4, #0] /* Map it in slot 0 */
- /* console fixmap */
- ldr x1, =xen_fixmap
- add x1, x1, x20 /* x1 := paddr (xen_fixmap) */
- lsr x2, x23, #12
- lsl x2, x2, #12 /* 4K aligned paddr of UART */
- mov x3, #PT_DEV_L3
- orr x2, x2, x3 /* x2 := 4K dev map including UART */
- str x2, [x1, #(FIXMAP_CONSOLE*8)] /* Map it in the first fixmap's slot */
+ /* ... map of paddr(start) in boot_pgtable */
+ lsr x1, x19, #39 /* Offset of base paddr in boot_pgtable */
+ cbz x1, 1f /* It's in slot 0, map in boot_first
+ * or boot_second later on */
- /* Build the baseline idle pagetable's first-level entries */
- ldr x1, =xen_second
- add x1, x1, x20 /* x1 := paddr (xen_second) */
- mov x3, #PT_PT /* x2 := table map of xen_second */
- orr x2, x1, x3 /* (+ rights for linear PT) */
+ lsl x2, x1, #39 /* Base address for 512GB mapping */
+ mov x3, #PT_MEM /* x2 := Section mapping */
+ orr x2, x2, x3
+ lsl x1, x1, #3 /* x1 := Slot offset */
+ str x2, [x4, x1] /* Mapping of paddr(start)*/
+
+1: /* Setup boot_first: */
+ ldr x4, =boot_first /* Next level into boot_first */
+ add x4, x4, x20 /* x4 := paddr(boot_first) */
+
+ /* ... map boot_second in boot_first[0] */
+ ldr x1, =boot_second
+ add x1, x1, x20 /* x1 := paddr(boot_second) */
+ mov x3, #PT_PT /* x2 := table map of boot_first */
+ orr x2, x1, x3 /* + rights for linear PT */
str x2, [x4, #0] /* Map it in slot 0 */
- add x2, x2, #0x1000
- str x2, [x4, #8] /* Map 2nd page in slot 1 */
- add x2, x2, #0x1000
- str x2, [x4, #16] /* Map 3rd page in slot 2 */
- add x2, x2, #0x1000
- str x2, [x4, #24] /* Map 4th page in slot 3 */
-
- /* Now set up the second-level entries */
- mov x3, #PT_MEM
- orr x2, x19, x3 /* x2 := 2MB normal map of Xen */
- orr x4, xzr, x19, lsr #18
- str x2, [x1, x4] /* Map Xen there */
- ldr x4, =start
- lsr x4, x4, #18 /* Slot for vaddr(start) */
- str x2, [x1, x4] /* Map Xen there too */
-
- /* xen_fixmap pagetable */
- ldr x2, =xen_fixmap
- add x2, x2, x20 /* x2 := paddr (xen_fixmap) */
- mov x3, #PT_PT
- orr x2, x2, x3 /* x2 := table map of xen_fixmap */
- add x4, x4, #8
- str x2, [x1, x4] /* Map it in the fixmap's slot */
- lsr x2, x21, #21
- lsl x2, x2, #21 /* 2MB-aligned paddr of DTB */
- mov x3, #PT_MEM /* x2 := 2MB RAM incl. DTB */
+ /* ... map of paddr(start) in boot_first */
+ lsr x2, x19, #30 /* x2 := Offset of base paddr in boot_first */
+ and x1, x2, 0x1ff /* x1 := Slot to use */
+ cbz x1, 1f /* It's in slot 0, map in boot_second */
+
+ lsl x2, x2, #30 /* Base address for 1GB mapping */
+ mov x3, #PT_MEM /* x2 := Section map */
orr x2, x2, x3
- add x4, x4, #8
- str x2, [x1, x4] /* Map it in the early fdt slot */
+ lsl x1, x1, #3 /* x1 := Slot offset */
+ str x2, [x4, x1] /* Create mapping of paddr(start)*/
+
+1: /* Setup boot_second: */
+ ldr x4, =boot_second
+ add x4, x4, x20 /* x4 := paddr (boot_second) */
+
+ lsr x2, x19, #20 /* Base address for 2MB mapping */
+ lsl x2, x2, #20
+ mov x3, #PT_MEM /* x2 := Section map */
+ orr x2, x2, x3
+
+ /* ... map of vaddr(start) in boot_second */
+ ldr x1, =start
+ lsr x1, x1, #18 /* Slot for vaddr(start) */
+ str x2, [x4, x1] /* Map vaddr(start) */
+
+ /* ... map of paddr(start) in boot_second */
+ lsr x1, x19, #30 /* Base paddr */
+ cbnz x1, 1f /* If paddr(start) is not in slot 0
+ * then the mapping was done in
+ * boot_pgtable or boot_first above */
+
+ lsr x1, x19, #18 /* Slot for paddr(start) */
+ str x2, [x4, x1] /* Map Xen there */
+1:
+
+ /* Defer fixmap and dtb mapping until after paging enabled, to
+ * avoid them clashing with the 1:1 mapping. */
+
+ /* boot pagetable setup complete */
-pt_ready:
PRINT("- Turning on paging -\r\n")
ldr x1, =paging /* Explicit vaddr, not RIP-relative */
@@ -279,17 +339,60 @@ pt_ready:
br x1 /* Get a proper vaddr into PC */
paging:
+ /* Now we can install the fixmap and dtb mappings, since we
+ * don't need the 1:1 map any more */
+ dsb sy
+#if defined(EARLY_PRINTK) /* Fixmap is only used by early printk */
+ /* Non-boot CPUs don't need to rebuild the fixmap itself, just
+ * the mapping from boot_second to xen_fixmap */
+ cbnz x22, 1f
+
+ /* Add UART to the fixmap table */
+ ldr x1, =xen_fixmap
+ add x1, x1, x20 /* x1 := paddr (xen_fixmap) */
+ lsr x2, x23, #12
+ lsl x2, x2, #12 /* 4K aligned paddr of UART */
+ mov x3, #PT_DEV_L3
+ orr x2, x2, x3 /* x2 := 4K dev map including UART */
+ str x2, [x1, #(FIXMAP_CONSOLE*8)] /* Map it in the first fixmap's slot */
+1:
+
+ /* Map fixmap into boot_second */
+ ldr x4, =boot_second /* x4 := vaddr (boot_second) */
+ ldr x2, =xen_fixmap
+ add x2, x2, x20 /* x2 := paddr (xen_fixmap) */
+ mov x3, #PT_PT
+ orr x2, x2, x3 /* x2 := table map of xen_fixmap */
+ ldr x1, =FIXMAP_ADDR(0)
+ lsr x1, x1, #18 /* x1 := Slot for FIXMAP(0) */
+ str x2, [x4, x1] /* Map it in the fixmap's slot */
+
/* Use a virtual address to access the UART. */
ldr x23, =FIXMAP_ADDR(FIXMAP_CONSOLE)
+#endif
+
+ /* Map the DTB in the boot misc slot */
+ cbnz x22, 1f /* Only on boot CPU */
+
+ lsr x2, x21, #21
+ lsl x2, x2, #21 /* x2 := 2MB-aligned paddr of DTB */
+ mov x3, #PT_MEM /* x2 := 2MB RAM incl. DTB */
+ orr x2, x2, x3
+ ldr x1, =BOOT_FDT_VIRT_START
+ lsr x1, x1, #18 /* x4 := Slot for BOOT_FDT_VIRT_START */
+ str x2, [x4, x1] /* Map it in the early fdt slot */
+ dsb sy
+1:
PRINT("- Ready -\r\n")
/* The boot CPU should go straight into C now */
cbz x22, launch
- /* Non-boot CPUs need to move on to the relocated pagetables */
- ldr x4, =boot_ttbr /* VA of TTBR0_EL2 stashed by CPU 0 */
- add x4, x4, x20 /* PA of it */
+ /* Non-boot CPUs need to move on to the proper pagetables, which were
+ * setup in init_secondary_pagetables. */
+
+ ldr x4, =init_ttbr /* VA of TTBR0_EL2 stashed by CPU 0 */
ldr x4, [x4] /* Actual value */
dsb sy
msr TTBR0_EL2, x4
@@ -299,28 +402,6 @@ paging:
dsb sy /* Ensure completion of TLB flush */
isb
- /* Non-boot CPUs report that they've got this far */
- ldr x0, =ready_cpus
-1: ldaxr x1, [x0] /* { read # of ready CPUs } */
- add x1, x1, #1 /* Atomically { ++ } */
- stlxr w2, x1, [x0] /* { writeback } */
- cbnz w2, 1b
- dsb sy
- dc cvac, x0 /* Flush D-Cache */
- dsb sy
-
- /* Here, the non-boot CPUs must wait again -- they're now running on
- * the boot CPU's pagetables so it's safe for the boot CPU to
- * overwrite the non-relocated copy of Xen. Once it's done that,
- * and brought up the memory allocator, non-boot CPUs can get their
- * own stacks and enter C. */
-1: wfe
- dsb sy
- ldr x0, =smp_up_cpu
- ldr x1, [x0] /* Which CPU is being booted? */
- cmp x1, x22 /* Is it us? */
- b.ne 1b
-
launch:
ldr x0, =init_data
add x0, x0, #INITINFO_stack /* Find the boot-time stack */
@@ -331,7 +412,7 @@ launch:
mov x0, x20 /* Marshal args: - phys_offset */
mov x1, x21 /* - FDT */
- mov x2, x22 /* - CPU ID */
+ mov x2, x24 /* - CPU ID */
cbz x22, start_xen /* and disappear into the land of C */
b start_secondary /* (to the appropriate entry point) */
@@ -341,13 +422,80 @@ fail: PRINT("- Boot failed -\r\n")
1: wfe
b 1b
-#ifdef EARLY_PRINTK
+/* Copy Xen to new location and switch TTBR
+ * x0 ttbr
+ * x1 source address
+ * x2 destination address
+ * x3 length
+ *
+ * Source and destination must be word aligned, length is rounded up
+ * to a 16 byte boundary.
+ *
+ * MUST BE VERY CAREFUL when saving things to RAM over the copy */
+ENTRY(relocate_xen)
+ /* Copy 16 bytes at a time using:
+ * x9: counter
+ * x10: data
+ * x11: data
+ * x12: source
+ * x13: destination
+ */
+ mov x9, x3
+ mov x12, x1
+ mov x13, x2
+1: ldp x10, x11, [x12], #16
+ stp x10, x11, [x13], #16
+
+ subs x9, x9, #16
+ bgt 1b
+
+ /* Flush destination from dcache using:
+ * x9: counter
+ * x10: step
+ * x11: vaddr
+ */
+ dsb sy /* So the CPU issues all writes to the range */
+
+ mov x9, x3
+ ldr x10, =cacheline_bytes /* x10 := step */
+ ldr x10, [x10]
+ mov x11, x2
+
+1: dc cvac, x11
+
+ add x11, x11, x10
+ subs x9, x9, x10
+ bgt 1b
+
+ dsb sy /* Ensure the flushes happen before
+ * continuing */
+ isb /* Ensure synchronization with previous
+ * changes to text */
+ tlbi alle2 /* Flush hypervisor TLB */
+ ic iallu /* Flush I-cache */
+ dsb sy /* Ensure completion of TLB flush */
+ isb
+
+ msr TTBR0_EL2, x0
+
+ isb /* Ensure synchronization with previous
+ * changes to text */
+ tlbi alle2 /* Flush hypervisor TLB */
+ ic iallu /* Flush I-cache */
+ dsb sy /* Ensure completion of TLB flush */
+ isb
+
+ ret
+
+#ifdef EARLY_PRINTK
/* Bring up the UART.
* x23: Early UART base address
* Clobbers x0-x1 */
init_uart:
+#ifdef EARLY_PRINTK_INIT_UART
early_uart_init x23, 0
+#endif
adr x0, 1f
b puts
1: .asciz "- UART enabled -\r\n"