/* * xen/arch/arm/head.S * * Start-of-day code for an ARMv8. * * Ian Campbell * Copyright (c) 2012 Citrix Systems. * * Based on ARMv7-A head.S by * Tim Deegan * * 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 #include #include #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 /* nG=1 AF=1 SH=10 AP=01 NS=1 ATTR=100 T=1 P=1 */ #if (defined (EARLY_PRINTK)) && (defined (EARLY_PRINTK_INC)) #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 #define PRINT(_s) \ adr x0, 98f ; \ bl puts ; \ b 99f ; \ 98: .asciz _s ; \ .align 2 ; \ 99: #else /* EARLY_PRINTK */ #define PRINT(s) #endif /* !EARLY_PRINTK */ /*.aarch64*/ /* * Kernel startup entry point. * --------------------------- * * The requirements are: * MMU = off, D-cache = off, I-cache = on or off, * x0 = physical address to the FDT blob. * * 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: /* * DO NOT MODIFY. Image header expected by Linux boot-loaders. */ b real_start /* branch to kernel start, magic */ .long 0 /* reserved */ .quad 0 /* Image load offset from start of RAM */ .quad 0 /* reserved */ .quad 0 /* reserved */ .quad 0 /* reserved */ .quad 0 /* reserved */ .quad 0 /* reserved */ .byte 0x41 /* Magic number, "ARM\x64" */ .byte 0x52 .byte 0x4d .byte 0x64 .word 0 /* reserved */ real_start: msr DAIFSet, 0xf /* Disable all interrupts */ /* Save the bootloader arguments in less-clobberable registers */ mov x21, x0 /* x21 := DTB, physical address */ /* Find out where we are */ ldr x0, =start adr x19, start /* x19 := paddr (start) */ sub x20, x19, x0 /* x20 := phys-offset */ /* Using the DTB in the .dtb section? */ #ifdef CONFIG_DTB_FILE ldr x21, =_sdtb add x21, x21, x20 /* x21 := paddr(DTB) */ #endif 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, _MPIDR_SMP, 1f /* Multiprocessor extension not supported? */ tbnz x0, _MPIDR_UP, 1f /* Uniprocessor system? */ mov x13, #(~MPIDR_HWID_MASK) 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 2b 1: #ifdef EARLY_PRINTK ldr x23, =EARLY_UART_BASE_ADDRESS /* x23 := UART base address */ cbnz x22, 1f bl init_uart /* Boot CPU sets up the UART too */ 1: PRINT("- CPU ") mov x0, x24 bl putn PRINT(" booting -\r\n") #endif PRINT("- Current EL ") mrs x4, CurrentEL mov x0, x4 bl putn PRINT(" -\r\n") /* Are we in EL2 */ cmp x4, #PSR_MODE_EL2t ccmp x4, #PSR_MODE_EL2h, #0x4, ne b.eq el2 /* Yes */ /* OK, we're boned. */ PRINT("- Xen must be entered in NS EL2 mode -\r\n" \ "- Please update the bootloader -\r\n") b fail el2: PRINT("- Xen starting at EL2 -\r\n") /* Zero BSS On the boot CPU to avoid nasty surprises */ cbnz x22, skip_bss PRINT("- Zero BSS -\r\n") ldr x0, =__bss_start /* Load start & end of bss */ ldr x1, =__bss_end add x0, x0, x20 /* Apply physical offset */ add x1, x1, x20 1: str xzr, [x0], #8 cmp x0, x1 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 /* Set up the HTCR: * PASize -- 4G * Top byte is used * PT walks use Outer-Shareable accesses, * PT walks are write-back, write-allocate in both cache levels, * Full 64-bit address space goes through this table. */ ldr x0, =0x80802500 msr tcr_el2, x0 /* Set up the SCTLR_EL2: * 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 x0, =(HSCTLR_BASE|SCTLR_A) msr SCTLR_EL2, x0 /* 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 (boot_pagetable) */ msr TTBR0_EL2, x4 /* Setup boot_pgtable: */ ldr x1, =boot_first add x1, x1, x20 /* x1 := paddr (boot_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 */ /* ... 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 */ 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 */ /* ... 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 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 */ PRINT("- Turning on paging -\r\n") ldr x1, =paging /* Explicit vaddr, not RIP-relative */ mrs x0, SCTLR_EL2 orr x0, x0, #SCTLR_M /* Enable MMU */ orr x0, x0, #SCTLR_C /* Enable D-cache */ dsb sy /* Flush PTE writes and finish reads */ msr SCTLR_EL2, x0 /* now paging is enabled */ isb /* Now, flush the icache */ 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 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 dsb sy isb tlbi alle2 dsb sy /* Ensure completion of TLB flush */ isb launch: ldr x0, =init_data add x0, x0, #INITINFO_stack /* Find the boot-time stack */ ldr x0, [x0] add x0, x0, #STACK_SIZE /* (which grows down from the top). */ sub x0, x0, #CPUINFO_sizeof /* Make room for CPU save record */ mov sp, x0 mov x0, x20 /* Marshal args: - phys_offset */ mov x1, x21 /* - FDT */ mov x2, x24 /* - CPU ID */ cbz x22, 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 /* 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" .align 4 /* Print early debug messages. * x0: Nul-terminated string to print. * x23: Early UART base address * Clobbers x0-x1 */ puts: early_uart_ready x23, 1 ldrb w1, [x0], #1 /* Load next char */ cbz w1, 1f /* Exit on nul */ early_uart_transmit x23, w1 b puts 1: ret /* Print a 32-bit number in hex. Specific to the PL011 UART. * x0: Number to print. * x23: Early UART base address * Clobbers x0-x3 */ putn: adr x1, hex mov x3, #8 1: early_uart_ready x23, 2 and x2, x0, #0xf0000000 /* Mask off the top nybble */ lsr x2, x2, #28 ldrb w2, [x1, x2] /* Convert to a char */ early_uart_transmit x23, w2 lsl x0, x0, #4 /* Roll it through one nybble at a time */ subs x3, x3, #1 b.ne 1b ret hex: .ascii "0123456789abcdef" .align 2 #else /* EARLY_PRINTK */ init_uart: .global early_puts early_puts: puts: putn: ret #endif /* EARLY_PRINTK */ /* * Local variables: * mode: ASM * indent-tabs-mode: nil * End: */