diff options
Diffstat (limited to 'xen/arch/arm/arm32/head.S')
-rw-r--r-- | xen/arch/arm/arm32/head.S | 415 |
1 files changed, 415 insertions, 0 deletions
diff --git a/xen/arch/arm/arm32/head.S b/xen/arch/arm/arm32/head.S new file mode 100644 index 0000000000..93f4edba53 --- /dev/null +++ b/xen/arch/arm/arm32/head.S @@ -0,0 +1,415 @@ +/* + * xen/arch/arm/head.S + * + * Start-of-day code for an ARMv7-A with virt extensions. + * + * Tim Deegan <tim@xen.org> + * Copyright (c) 2011 Citrix Systems. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <asm/config.h> +#include <asm/page.h> +#include <asm/processor-ca15.h> +#include <asm/asm_defns.h> + +#define ZIMAGE_MAGIC_NUMBER 0x016f2818 + +#define PT_PT 0xe7f /* nG=1, AF=1, SH=10, AP=01, NS=1, ATTR=111, T=1, P=1 */ +#define PT_MEM 0xe7d /* nG=1, AF=1, SH=10, AP=01, NS=1, ATTR=111, T=0, P=1 */ +#define PT_DEV 0xe71 /* nG=1, AF=1, SH=10, AP=01, NS=1, ATTR=100, T=0, P=1 */ +#define PT_DEV_L3 0xe73 /* lev3: nG=1, AF=1, SH=10, AP=01, NS=1, ATTR=100, T=1, P=1 */ + +#define PT_UPPER(x) (PT_##x & 0xf00) +#define PT_LOWER(x) (PT_##x & 0x0ff) + +/* Macro to print a string to the UART, if there is one. + * Clobbers r0-r3. */ +#ifdef EARLY_UART_ADDRESS +#define PRINT(_s) \ + adr r0, 98f ; \ + bl puts ; \ + b 99f ; \ +98: .asciz _s ; \ + .align 2 ; \ +99: +#else +#define PRINT(s) +#endif + + .arm + + /* This must be the very first address in the loaded image. + * It should be linked at XEN_VIRT_START, and loaded at any + * 2MB-aligned address. All of text+data+bss must fit in 2MB, + * or the initial pagetable code below will need adjustment. */ + .global start +start: + + /* zImage magic header, see: + * http://www.simtec.co.uk/products/SWLINUX/files/booting_article.html#d0e309 + */ + .rept 8 + mov r0, r0 + .endr + b past_zImage + + .word ZIMAGE_MAGIC_NUMBER /* Magic numbers to help the loader */ + .word 0x00000000 /* absolute load/run zImage address or + * 0 for PiC */ + .word (_end - start) /* zImage end address */ + +past_zImage: + cpsid aif /* Disable all interrupts */ + + /* Save the bootloader arguments in less-clobberable registers */ + mov r7, r1 /* r7 := ARM-linux machine type */ + mov r8, r2 /* r8 := ATAG base address */ + + /* Find out where we are */ + ldr r0, =start + adr r9, start /* r9 := paddr (start) */ + sub r10, r9, r0 /* r10 := phys-offset */ + + /* Using the DTB in the .dtb section? */ +#ifdef CONFIG_DTB_FILE + ldr r8, =_sdtb + 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, #(0xff << 24) /* 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 + +boot_cpu: +#ifdef EARLY_UART_ADDRESS + ldr r11, =EARLY_UART_ADDRESS /* r11 := UART base address */ + teq r12, #0 /* CPU 0 sets up the UART too */ + bleq init_uart + PRINT("- CPU ") + mov r0, r12 + bl putn + PRINT(" booting -\r\n") +#endif + + /* Wake up secondary cpus */ + teq r12, #0 + bleq kick_cpus + + /* Check that this CPU has Hyp mode */ + mrc CP32(r0, ID_PFR1) + and r0, r0, #0xf000 /* Bits 12-15 define virt extensions */ + teq r0, #0x1000 /* Must == 0x1 or may be incompatible */ + beq 1f + PRINT("- CPU doesn't support the virtualization extensions -\r\n") + b fail +1: + /* Check if we're already in it */ + 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") + 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: + + /* Zero BSS On the boot CPU to avoid nasty surprises */ + teq r12, #0 + bne skip_bss + + PRINT("- Zero BSS -\r\n") + ldr r0, =__bss_start /* Load start & end of bss */ + ldr r1, =__bss_end + add r0, r0, r10 /* Apply physical offset */ + add r1, r1, r10 + + mov r2, #0 +1: str r2, [r0], #4 + cmp r0, r1 + blo 1b + +skip_bss: + + PRINT("- Setting up control registers -\r\n") + + /* Read CPU ID */ + mrc CP32(r0, MIDR) + ldr r1, =(MIDR_MASK) + and r0, r0, r1 + /* Is this a Cortex A15? */ + ldr r1, =(CORTEX_A15_ID) + teq r0, r1 + bleq cortex_a15_init + + /* Set up memory attribute type tables */ + ldr r0, =MAIR0VAL + ldr r1, =MAIR1VAL + mcr CP32(r0, MAIR0) + mcr CP32(r1, MAIR1) + mcr CP32(r0, HMAIR0) + mcr CP32(r1, HMAIR1) + + /* Set up the HTCR: + * PT walks use Outer-Shareable accesses, + * PT walks are write-back, no-write-allocate in both cache levels, + * Full 32-bit address space goes through this table. */ + ldr r0, =0x80002500 + mcr CP32(r0, HTCR) + + /* Set up the HSCTLR: + * Exceptions in LE ARM, + * Low-latency IRQs disabled, + * Write-implies-XN disabled (for now), + * D-cache disabled (for now), + * I-cache enabled, + * Alignment checking enabled, + * MMU translation disabled (for now). */ + ldr r0, =(HSCTLR_BASE|SCTLR_A) + mcr CP32(r0, HSCTLR) + + /* Write Xen's PT's paddr into the HTTBR */ + ldr r4, =xen_pgtable + add r4, r4, r10 /* r4 := paddr (xen_pagetable) */ + mov r5, #0 /* r4:r5 is paddr (xen_pagetable) */ + mcrr CP64(r4, r5, HTTBR) + + /* Non-boot CPUs don't need to rebuild the pagetable */ + teq r12, #0 + bne pt_ready + + /* console fixmap */ +#ifdef EARLY_UART_ADDRESS + 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) */ + mov r3, #0x0 + orr r2, r1, #PT_UPPER(PT) /* r2:r3 := table map of xen_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 boot slot */ + +pt_ready: + PRINT("- Turning on paging -\r\n") + + ldr r1, =paging /* Explicit vaddr, not RIP-relative */ + mrc CP32(r0, HSCTLR) + orr r0, r0, #(SCTLR_M|SCTLR_C) /* Enable MMU and D-cache */ + dsb /* Flush PTE writes and finish reads */ + mcr CP32(r0, HSCTLR) /* now paging is enabled */ + isb /* Now, flush the icache */ + mov pc, r1 /* Get a proper vaddr into PC */ +paging: + + +#ifdef EARLY_UART_ADDRESS + /* Use a virtual address to access the UART. */ + ldr r11, =FIXMAP_ADDR(FIXMAP_CONSOLE) +#endif + + PRINT("- Ready -\r\n") + + /* The boot CPU should go straight into C now */ + teq r12, #0 + beq launch + + /* Non-boot CPUs need to move on to the relocated pagetables */ + mov r0, #0 + ldr r4, =boot_httbr /* VA of HTTBR value stashed by CPU 0 */ + add r4, r4, r10 /* PA of it */ + ldrd r4, r5, [r4] /* Actual value */ + dsb + mcrr CP64(r4, r5, HTTBR) + dsb + isb + 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 + + /* 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_stack /* Find the boot-time stack */ + ldr sp, [r0] + add sp, #STACK_SIZE /* (which grows down from the top). */ + sub sp, #CPUINFO_sizeof /* Make room for CPU save record */ + mov r0, r10 /* Marshal args: - phys_offset */ + mov r1, r7 /* - machine type */ + mov r2, r8 /* - ATAG address */ + movs r3, r12 /* - CPU ID */ + beq start_xen /* and disappear into the land of C */ + b start_secondary /* (to the appropriate entry point) */ + +/* Fail-stop + * r0: string explaining why */ +fail: PRINT("- Boot failed -\r\n") +1: wfe + b 1b + +#ifdef EARLY_UART_ADDRESS + +/* Bring up the UART. Specific to the PL011 UART. + * Clobbers r0-r2 */ +init_uart: + mov r1, #0x0 + str r1, [r11, #0x24] /* -> UARTIBRD (Baud divisor fraction) */ + mov r1, #0x4 /* 7.3728MHz / 0x4 == 16 * 115200 */ + str r1, [r11, #0x24] /* -> UARTIBRD (Baud divisor integer) */ + mov r1, #0x60 /* 8n1 */ + str r1, [r11, #0x24] /* -> UARTLCR_H (Line control) */ + ldr r1, =0x00000301 /* RXE | TXE | UARTEN */ + str r1, [r11, #0x30] /* -> UARTCR (Control Register) */ + adr r0, 1f + b puts +1: .asciz "- UART enabled -\r\n" + .align 4 + +/* Print early debug messages. Specific to the PL011 UART. + * r0: Nul-terminated string to print. + * Clobbers r0-r2 */ +puts: + ldr r2, [r11, #0x18] /* <- UARTFR (Flag register) */ + tst r2, #0x8 /* Check BUSY bit */ + bne puts /* Wait for the UART to be ready */ + ldrb r2, [r0], #1 /* Load next char */ + teq r2, #0 /* Exit on nul */ + moveq pc, lr + str r2, [r11] /* -> UARTDR (Data Register) */ + b puts + +/* Print a 32-bit number in hex. Specific to the PL011 UART. + * r0: Number to print. + * clobbers r0-r3 */ +putn: + adr r1, hex + mov r3, #8 +1: ldr r2, [r11, #0x18] /* <- UARTFR (Flag register) */ + tst r2, #0x8 /* Check BUSY bit */ + bne 1b /* Wait for the UART to be ready */ + and r2, r0, #0xf0000000 /* Mask off the top nybble */ + ldrb r2, [r1, r2, lsr #28] /* Convert to a char */ + str r2, [r11] /* -> UARTDR (Data Register) */ + lsl r0, #4 /* Roll it through one nybble at a time */ + subs r3, r3, #1 + bne 1b + mov pc, lr + +hex: .ascii "0123456789abcdef" + .align 2 + +#else /* EARLY_UART_ADDRESS */ + +init_uart: +.global early_puts +early_puts: +puts: +putn: mov pc, lr + +#endif /* EARLY_UART_ADDRESS */ + +/* + * Local variables: + * mode: ASM + * indent-tabs-mode: nil + * End: + */ |