diff options
Diffstat (limited to 'xen/arch/arm/arm32/head.S')
-rw-r--r-- | xen/arch/arm/arm32/head.S | 391 |
1 files changed, 247 insertions, 144 deletions
diff --git a/xen/arch/arm/arm32/head.S b/xen/arch/arm/arm32/head.S index bbcb3a0cad..510ccff3ed 100644 --- a/xen/arch/arm/arm32/head.S +++ b/xen/arch/arm/arm32/head.S @@ -37,6 +37,25 @@ #include EARLY_PRINTK_INC #endif +/* + * Common register usage in this file: + * r0 - + * r1 - + * r2 - + * r3 - + * r4 - + * r5 - + * r6 - + * r7 - CPUID + * r8 - DTB address (boot CPU only) + * r9 - paddr(start) + * r10 - phys offset + * r11 - UART address + * r12 - is_secondary_cpu + * r13 - SP + * r14 - LR + * r15 - PC + */ /* Macro to print a string to the UART, if there is one. * Clobbers r0-r3. */ #ifdef EARLY_PRINTK @@ -59,7 +78,6 @@ * or the initial pagetable code below will need adjustment. */ .global start start: -GLOBAL(init_secondary) /* currently unused */ /* zImage magic header, see: * http://www.simtec.co.uk/products/SWLINUX/files/booting_article.html#d0e309 */ @@ -77,7 +95,6 @@ past_zImage: cpsid aif /* Disable all interrupts */ /* Save the bootloader arguments in less-clobberable registers */ - mov r5, r1 /* r5: ARM-linux machine type */ mov r8, r2 /* r8 := DTB base address */ /* Find out where we are */ @@ -91,53 +108,55 @@ past_zImage: add r8, r10 /* r8 := paddr(DTB) */ #endif - /* Are we the boot CPU? */ - mov r12, #0 /* r12 := CPU ID */ - mrc CP32(r0, MPIDR) - tst r0, #(1<<31) /* Multiprocessor extension supported? */ - beq boot_cpu - tst r0, #(1<<30) /* Uniprocessor system? */ - bne boot_cpu - bics r12, r0, #(~MPIDR_HWID_MASK) /* Mask out flags to get CPU ID */ - beq boot_cpu /* If we're CPU 0, boot now */ - - /* Non-boot CPUs wait here to be woken up one at a time. */ -1: dsb - ldr r0, =smp_up_cpu /* VA of gate */ - add r0, r0, r10 /* PA of gate */ - ldr r1, [r0] /* Which CPU is being booted? */ - teq r1, r12 /* Is it us? */ - wfene - bne 1b + mov r12, #0 /* r12 := is_secondary_cpu */ + + b common_start + +GLOBAL(init_secondary) + cpsid aif /* Disable all interrupts */ + + /* Find out where we are */ + ldr r0, =start + adr r9, start /* r9 := paddr (start) */ + sub r10, r9, r0 /* r10 := phys-offset */ + + mov r12, #1 /* r12 := is_secondary_cpu */ + +common_start: + mov r7, #0 /* r7 := CPU ID. Initialy zero until we + * find that multiprocessor extensions are + * present and the system is SMP */ + mrc CP32(r1, MPIDR) + tst r1, #(1<<31) /* Multiprocessor extension supported? */ + beq 1f + tst r1, #(1<<30) /* Uniprocessor system? */ + bne 1f + bic r7, r1, #(~MPIDR_HWID_MASK) /* Mask out flags to get CPU ID */ +1: + + /* Non-boot CPUs wait here until __cpu_up is ready for them */ + teq r12, #0 + beq 1f + + ldr r0, =smp_up_cpu + add r0, r0, r10 /* Apply physical offset */ + dsb +2: ldr r1, [r0] + cmp r1, r7 + beq 1f + wfe + b 2b +1: -boot_cpu: #ifdef EARLY_PRINTK ldr r11, =EARLY_UART_BASE_ADDRESS /* r11 := UART base address */ - teq r12, #0 /* CPU 0 sets up the UART too */ + teq r12, #0 /* Boot CPU sets up the UART too */ bleq init_uart PRINT("- CPU ") - mov r0, r12 + mov r0, r7 bl putn PRINT(" booting -\r\n") #endif - /* Secondary CPUs doesn't have machine ID - * - Store machine ID on boot CPU - * - Load machine ID on secondary CPUs - * Machine ID is needed in kick_cpus and enter_hyp_mode */ - ldr r0, =machine_id /* VA of machine_id */ - add r0, r0, r10 /* PA of machine_id */ - teq r12, #0 - streq r5, [r0] /* On boot CPU save machine ID */ - ldrne r5, [r0] /* If non boot cpu r5 := machine ID */ - - /* Wake up secondary cpus */ - teq r12, #0 - bleq kick_cpus - - PRINT("- Machine ID ") - mov r0, r5 - bl putn - PRINT(" -\r\n") /* Check that this CPU has Hyp mode */ mrc CP32(r0, ID_PFR1) @@ -147,28 +166,19 @@ boot_cpu: PRINT("- CPU doesn't support the virtualization extensions -\r\n") b fail 1: - /* Check if we're already in it */ + + /* Check that we're already in Hyp mode */ mrs r0, cpsr and r0, r0, #0x1f /* Mode is in the low 5 bits of CPSR */ teq r0, #0x1a /* Hyp Mode? */ - bne 1f - PRINT("- Started in Hyp mode -\r\n") - b hyp -1: - /* Otherwise, it must have been Secure Supervisor mode */ - mrc CP32(r0, SCR) - tst r0, #0x1 /* Not-Secure bit set? */ - beq 1f - PRINT("- CPU is not in Hyp mode or Secure state -\r\n") + beq hyp + + /* OK, we're boned. */ + PRINT("- Xen must be entered in NS Hyp mode -\r\n" \ + "- Please update the bootloader -\r\n") b fail -1: - /* OK, we're in Secure state. */ - PRINT("- Started in Secure state -\r\n- Entering Hyp mode -\r\n") - ldr r0, =enter_hyp_mode /* VA of function */ - adr lr, hyp /* Set return address for call */ - add pc, r0, r10 /* Call PA of function */ -hyp: +hyp: PRINT("- Xen starting in Hyp mode -\r\n") /* Zero BSS On the boot CPU to avoid nasty surprises */ teq r12, #0 @@ -208,8 +218,8 @@ skip_bss: bl putn PRINT(" -\r\n") b fail - 2: + /* Jump to cpu_init */ ldr r1, [r1, #PROCINFO_cpu_init] /* r1 := vaddr(init func) */ adr lr, cpu_init_done /* Save return address */ @@ -242,68 +252,69 @@ cpu_init_done: ldr r0, =(HSCTLR_BASE|SCTLR_A) mcr CP32(r0, HSCTLR) + /* 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 the HTTBR */ ldr r4, =boot_pgtable - add r4, r4, r10 /* r4 := paddr (xen_pagetable) */ - mov r5, #0 /* r4:r5 is paddr (xen_pagetable) */ + add r4, r4, r10 /* r4 := paddr (boot_pagetable) */ + mov r5, #0 /* r4:r5 is paddr (boot_pagetable) */ mcrr CP64(r4, r5, HTTBR) - /* Non-boot CPUs don't need to rebuild the pagetable */ - teq r12, #0 - bne pt_ready - - /* console fixmap */ -#if defined(EARLY_PRINTK) - ldr r1, =xen_fixmap - add r1, r1, r10 /* r1 := paddr (xen_fixmap) */ - mov r3, #0 - lsr r2, r11, #12 - lsl r2, r2, #12 /* 4K aligned paddr of UART */ - orr r2, r2, #PT_UPPER(DEV_L3) - orr r2, r2, #PT_LOWER(DEV_L3) /* r2:r3 := 4K dev map including UART */ - strd r2, r3, [r1, #(FIXMAP_CONSOLE*8)] /* Map it in the first fixmap's slot */ -#endif - - /* Build the baseline idle pagetable's first-level entries */ - ldr r1, =xen_second - add r1, r1, r10 /* r1 := paddr (xen_second) */ + /* Setup boot_pgtable: */ + ldr r1, =boot_second + add r1, r1, r10 /* r1 := paddr (boot_second) */ mov r3, #0x0 - orr r2, r1, #PT_UPPER(PT) /* r2:r3 := table map of xen_second */ + + /* ... map boot_second in boot_pgtable[0] */ + orr r2, r1, #PT_UPPER(PT) /* r2:r3 := table map of boot_second */ orr r2, r2, #PT_LOWER(PT) /* (+ rights for linear PT) */ strd r2, r3, [r4, #0] /* Map it in slot 0 */ - add r2, r2, #0x1000 - strd r2, r3, [r4, #8] /* Map 2nd page in slot 1 */ - add r2, r2, #0x1000 - strd r2, r3, [r4, #16] /* Map 3rd page in slot 2 */ - add r2, r2, #0x1000 - strd r2, r3, [r4, #24] /* Map 4th page in slot 3 */ - - /* Now set up the second-level entries */ - orr r2, r9, #PT_UPPER(MEM) - orr r2, r2, #PT_LOWER(MEM) /* r2:r3 := 2MB normal map of Xen */ - mov r4, r9, lsr #18 /* Slot for paddr(start) */ - strd r2, r3, [r1, r4] /* Map Xen there */ - ldr r4, =start - lsr r4, #18 /* Slot for vaddr(start) */ - strd r2, r3, [r1, r4] /* Map Xen there too */ - - /* xen_fixmap pagetable */ - ldr r2, =xen_fixmap - add r2, r2, r10 /* r2 := paddr (xen_fixmap) */ - orr r2, r2, #PT_UPPER(PT) - orr r2, r2, #PT_LOWER(PT) /* r2:r3 := table map of xen_fixmap */ - add r4, r4, #8 - strd r2, r3, [r1, r4] /* Map it in the fixmap's slot */ - mov r3, #0x0 - lsr r2, r8, #21 - lsl r2, r2, #21 /* 2MB-aligned paddr of DTB */ - orr r2, r2, #PT_UPPER(MEM) - orr r2, r2, #PT_LOWER(MEM) /* r2:r3 := 2MB RAM incl. DTB */ - add r4, r4, #8 - strd r2, r3, [r1, r4] /* Map it in the early fdt slot */ + /* ... map of paddr(start) in boot_pgtable */ + lsrs r1, r9, #30 /* Offset of base paddr in boot_pgtable */ + beq 1f /* If it is in slot 0 then map in boot_second + * later on */ + lsl r2, r1, #30 /* Base address for 1GB mapping */ + orr r2, r2, #PT_UPPER(MEM) /* r2:r3 := section map */ + orr r2, r2, #PT_LOWER(MEM) + lsl r1, r1, #3 /* r1 := Slot offset */ + strd r2, r3, [r4, r1] /* Mapping of paddr(start) */ + +1: /* Setup boot_second: */ + ldr r4, =boot_second + add r4, r4, r10 /* r1 := paddr (boot_second) */ + + lsr r2, r9, #20 /* Base address for 2MB mapping */ + lsl r2, r2, #20 + orr r2, r2, #PT_UPPER(MEM) /* r2:r3 := section map */ + orr r2, r2, #PT_LOWER(MEM) + + /* ... map of vaddr(start) in boot_second */ + ldr r1, =start + lsr r1, #18 /* Slot for vaddr(start) */ + strd r2, r3, [r4, r1] /* Map vaddr(start) */ + + /* ... map of paddr(start) in boot_second */ + lsrs r1, r9, #30 /* Base paddr */ + bne 1f /* If paddr(start) is not in slot 0 + * then the mapping was done in + * boot_pgtable above */ + + mov r1, r9, lsr #18 /* Slot for paddr(start) */ + strd r2, r3, [r4, r1] /* 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 r1, =paging /* Explicit vaddr, not RIP-relative */ @@ -315,11 +326,53 @@ pt_ready: mov pc, r1 /* 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 +#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 */ + teq r12, #0 + bne 1f + + /* Add UART to the fixmap table */ + ldr r1, =xen_fixmap /* r1 := vaddr (xen_fixmap) */ + mov r3, #0 + lsr r2, r11, #12 + lsl r2, r2, #12 /* 4K aligned paddr of UART */ + orr r2, r2, #PT_UPPER(DEV_L3) + orr r2, r2, #PT_LOWER(DEV_L3) /* r2:r3 := 4K dev map including UART */ + strd r2, r3, [r1, #(FIXMAP_CONSOLE*8)] /* Map it in the first fixmap's slot */ +1: + + /* Map fixmap into boot_second */ + ldr r1, =boot_second /* r1 := vaddr (xen_fixmap) */ + ldr r2, =xen_fixmap + add r2, r2, r10 /* r2 := paddr (xen_fixmap) */ + orr r2, r2, #PT_UPPER(PT) + orr r2, r2, #PT_LOWER(PT) /* r2:r3 := table map of xen_fixmap */ + ldr r4, =FIXMAP_ADDR(0) + mov r4, r4, lsr #18 /* r4 := Slot for FIXMAP(0) */ + strd r2, r3, [r1, r4] /* Map it in the fixmap's slot */ -#ifdef EARLY_PRINTK /* Use a virtual address to access the UART. */ ldr r11, =FIXMAP_ADDR(FIXMAP_CONSOLE) #endif + /* Map the DTB in the boot misc slot */ + teq r12, #0 /* Only on boot CPU */ + bne 1f + + ldr r1, =boot_second + mov r3, #0x0 + lsr r2, r8, #21 + lsl r2, r2, #21 /* r2: 2MB-aligned paddr of DTB */ + orr r2, r2, #PT_UPPER(MEM) + orr r2, r2, #PT_LOWER(MEM) /* r2:r3 := 2MB RAM incl. DTB */ + ldr r4, =BOOT_FDT_VIRT_START + mov r4, r4, lsr #18 /* Slot for BOOT_FDT_VIRT_START */ + strd r2, r3, [r1, r4] /* Map it in the early fdt slot */ + dsb +1: PRINT("- Ready -\r\n") @@ -327,10 +380,10 @@ paging: teq r12, #0 beq launch - /* Non-boot CPUs need to move on to the relocated pagetables */ - mov r0, #0 - ldr r4, =boot_ttbr /* VA of HTTBR value stashed by CPU 0 */ - add r4, r4, r10 /* PA of it */ + /* Non-boot CPUs need to move on to the proper pagetables, which were + * setup in init_secondary_pagetables. */ + + ldr r4, =init_ttbr /* VA of HTTBR value stashed by CPU 0 */ ldrd r4, r5, [r4] /* Actual value */ dsb mcrr CP64(r4, r5, HTTBR) @@ -342,29 +395,6 @@ paging: dsb /* Ensure completion of TLB+BP flush */ isb - /* Non-boot CPUs report that they've got this far */ - ldr r0, =ready_cpus -1: ldrex r1, [r0] /* { read # of ready CPUs } */ - add r1, r1, #1 /* Atomically { ++ } */ - strex r2, r1, [r0] /* { writeback } */ - teq r2, #0 - bne 1b - dsb - mcr CP32(r0, DCCMVAC) /* flush D-Cache */ - dsb - - /* 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 - ldr r0, =smp_up_cpu - ldr r1, [r0] /* Which CPU is being booted? */ - teq r1, r12 /* Is it us? */ - bne 1b - launch: ldr r0, =init_data add r0, #INITINFO_stack /* Find the boot-time stack */ @@ -373,7 +403,7 @@ launch: sub sp, #CPUINFO_sizeof /* Make room for CPU save record */ mov r0, r10 /* Marshal args: - phys_offset */ mov r1, r8 /* - DTB address */ - movs r2, r12 /* - CPU ID */ + movs r2, r7 /* - CPU ID */ beq start_xen /* and disappear into the land of C */ b start_secondary /* (to the appropriate entry point) */ @@ -383,6 +413,82 @@ fail: PRINT("- Boot failed -\r\n") 1: wfe b 1b +/* Copy Xen to new location and switch TTBR + * r1:r0 ttbr + * r2 source address + * r3 destination address + * [sp]=>r4 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) + push {r4,r5,r6,r7,r8,r9,r10,r11} + + ldr r4, [sp, #8*4] /* Get 4th argument from stack */ + + /* Copy 16 bytes at a time using: + * r5: counter + * r6: data + * r7: data + * r8: data + * r9: data + * r10: source + * r11: destination + */ + mov r5, r4 + mov r10, r2 + mov r11, r3 +1: ldmia r10!, {r6, r7, r8, r9} + stmia r11!, {r6, r7, r8, r9} + + subs r5, r5, #16 + bgt 1b + + /* Flush destination from dcache using: + * r5: counter + * r6: step + * r7: vaddr + */ + dsb /* So the CPU issues all writes to the range */ + + mov r5, r4 + ldr r6, =cacheline_bytes /* r6 := step */ + ldr r6, [r6] + mov r7, r3 + +1: mcr CP32(r7, DCCMVAC) + + add r7, r7, r6 + subs r5, r5, r6 + bgt 1b + + dsb /* Ensure the flushes happen before + * continuing */ + isb /* Ensure synchronization with previous + * changes to text */ + mcr CP32(r0, TLBIALLH) /* Flush hypervisor TLB */ + mcr CP32(r0, ICIALLU) /* Flush I-cache */ + mcr CP32(r0, BPIALL) /* Flush branch predictor */ + dsb /* Ensure completion of TLB+BP flush */ + isb + + mcrr CP64(r0, r1, HTTBR) + + dsb /* ensure memory accesses do not cross + * over the TTBR0 write */ + isb /* Ensure synchronization with previous + * changes to text */ + mcr CP32(r0, TLBIALLH) /* Flush hypervisor TLB */ + mcr CP32(r0, ICIALLU) /* Flush I-cache */ + mcr CP32(r0, BPIALL) /* Flush branch predictor */ + dsb /* Ensure completion of TLB+BP flush */ + isb + + pop {r4, r5,r6,r7,r8,r9,r10,r11} + + mov pc, lr #ifdef EARLY_PRINTK /* Bring up the UART. @@ -439,9 +545,6 @@ putn: mov pc, lr #endif /* !EARLY_PRINTK */ -/* Place holder for machine ID */ -machine_id: .word 0x0 - /* * Local variables: * mode: ASM |