locore-v6.S revision 295801
1/*- 2 * Copyright 2004-2014 Olivier Houchard <cognet@FreeBSD.org> 3 * Copyright 2012-2014 Ian Lepore <ian@FreeBSD.org> 4 * Copyright 2013-2014 Andrew Turner <andrew@FreeBSD.org> 5 * Copyright 2014 Svatopluk Kraus <onwahe@gmail.com> 6 * Copyright 2014 Michal Meloun <meloun@miracle.cz> 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31#include "assym.s" 32#include <sys/syscall.h> 33#include <machine/acle-compat.h> 34#include <machine/asm.h> 35#include <machine/asmacros.h> 36#include <machine/armreg.h> 37#include <machine/sysreg.h> 38#include <machine/cpuconf.h> 39#include <machine/pte-v6.h> 40 41__FBSDID("$FreeBSD: head/sys/arm/arm/locore-v6.S 295801 2016-02-19 09:23:32Z skra $"); 42 43 44#if __ARM_ARCH >= 7 45#if defined(__ARM_ARCH_7VE__) || defined(__clang__) 46/* 47 * HYP support is in bintuils >= 2.21 and gcc >= 4.9 defines __ARM_ARCH_7VE__ 48 * when enabled. llvm >= 3.6 supports it too. 49 */ 50.arch_extension virt 51#define MSR_ELR_HYP(regnum) msr elr_hyp, lr 52#define ERET eret 53#else 54#define MSR_ELR_HYP(regnum) .word (0xe12ef300 | regnum) 55#define ERET .word 0xe160006e 56#endif 57#endif /* __ARM_ARCH >= 7 */ 58 59/* A small statically-allocated stack used only during initarm() and AP startup. */ 60#define INIT_ARM_STACK_SIZE 2048 61 62 .text 63 .align 2 64 65#if __ARM_ARCH >= 7 66#define LEAVE_HYP \ 67 /* Leave HYP mode */ ;\ 68 mrs r0, cpsr ;\ 69 and r0, r0, #(PSR_MODE) /* Mode is in the low 5 bits of CPSR */ ;\ 70 teq r0, #(PSR_HYP32_MODE) /* Hyp Mode? */ ;\ 71 bne 1f ;\ 72 /* Ensure that IRQ, FIQ and Aborts will be disabled after eret */ ;\ 73 mrs r0, cpsr ;\ 74 bic r0, r0, #(PSR_MODE) ;\ 75 orr r0, r0, #(PSR_SVC32_MODE) ;\ 76 orr r0, r0, #(PSR_I | PSR_F | PSR_A) ;\ 77 msr spsr_cxsf, r0 ;\ 78 /* Exit hypervisor mode */ ;\ 79 adr lr, 1f ;\ 80 MSR_ELR_HYP(14) ;\ 81 ERET ;\ 821: 83#else 84#define LEAVE_HYP 85#endif /* __ARM_ARCH >= 7 */ 86 87/* 88 * On entry for FreeBSD boot ABI: 89 * r0 - metadata pointer or 0 (boothowto on AT91's boot2) 90 * r1 - if (r0 == 0) then metadata pointer 91 * On entry for Linux boot ABI: 92 * r0 - 0 93 * r1 - machine type (passed as arg2 to initarm) 94 * r2 - Pointer to a tagged list or dtb image (phys addr) (passed as arg1 initarm) 95 * 96 * For both types of boot we gather up the args, put them in a struct arm_boot_params 97 * structure and pass that to initarm. 98 */ 99 .globl btext 100btext: 101ASENTRY_NP(_start) 102 STOP_UNWINDING /* Can't unwind into the bootloader! */ 103 104 /* Make sure interrupts are disabled. */ 105 cpsid ifa 106 107 mov r8, r0 /* 0 or boot mode from boot2 */ 108 mov r9, r1 /* Save Machine type */ 109 mov r10, r2 /* Save meta data */ 110 mov r11, r3 /* Future expansion */ 111 112 LEAVE_HYP 113 114 /* 115 * Check whether data cache is enabled. If it is, then we know 116 * current tags are valid (not power-on garbage values) and there 117 * might be dirty lines that need cleaning. Disable cache to prevent 118 * new lines being allocated, then call wbinv_poc_all to clean it. 119 */ 120 mrc CP15_SCTLR(r7) 121 tst r7, #CPU_CONTROL_DC_ENABLE 122 blne dcache_wbinv_poc_all 123 124 /* ! Do not write to memory between wbinv and disabling cache ! */ 125 126 /* 127 * Now there are no dirty lines, but there may still be lines marked 128 * valid. Disable all caches and the MMU, and invalidate everything 129 * before setting up new page tables and re-enabling the mmu. 130 */ 1311: 132 bic r7, #CPU_CONTROL_DC_ENABLE 133 bic r7, #CPU_CONTROL_MMU_ENABLE 134 bic r7, #CPU_CONTROL_IC_ENABLE 135 bic r7, #CPU_CONTROL_BPRD_ENABLE 136 bic r7, #CPU_CONTROL_SW_ENABLE 137 orr r7, #CPU_CONTROL_UNAL_ENABLE 138 orr r7, #CPU_CONTROL_AFLT_ENABLE 139 orr r7, #CPU_CONTROL_VECRELOC 140 mcr CP15_SCTLR(r7) 141 DSB 142 ISB 143 bl dcache_inv_poc_all 144 mcr CP15_ICIALLU 145 DSB 146 ISB 147 148 /* 149 * Build page table from scratch. 150 */ 151 152 /* 153 * Figure out the physical address we're loaded at by assuming this 154 * entry point code is in the first L1 section and so if we clear the 155 * offset bits of the pc that will give us the section-aligned load 156 * address, which remains in r5 throughout all the following code. 157 */ 158 ldr r2, =(L1_S_OFFSET) 159 bic r5, pc, r2 160 161 /* Find the delta between VA and PA, result stays in r0 throughout. */ 162 adr r0, Lpagetable 163 bl translate_va_to_pa 164 165 /* 166 * First map the entire 4GB address space as VA=PA. It's mapped as 167 * normal (cached) memory because it's for things like accessing the 168 * parameters passed in from the bootloader, which might be at any 169 * physical address, different for every platform. 170 */ 171 mov r1, #0 172 mov r2, #0 173 mov r3, #4096 174 bl build_pagetables 175 176 /* 177 * Next we do 64MiB starting at the physical load address, mapped to 178 * the VA the kernel is linked for. 179 */ 180 mov r1, r5 181 ldr r2, =(KERNVIRTADDR) 182 mov r3, #64 183 bl build_pagetables 184 185 /* Create a device mapping for early_printf if specified. */ 186#if defined(SOCDEV_PA) && defined(SOCDEV_VA) 187 ldr r1, =SOCDEV_PA 188 ldr r2, =SOCDEV_VA 189 mov r3, #1 190 bl build_device_pagetables 191#endif 192 bl init_mmu 193 194 /* Transition the PC from physical to virtual addressing. */ 195 ldr pc, =1f 1961: 197 198 /* Setup stack, clear BSS */ 199 ldr r1, =.Lstart 200 ldmia r1, {r1, r2, sp} /* Set initial stack and */ 201 add sp, sp, #INIT_ARM_STACK_SIZE 202 sub r2, r2, r1 /* get zero init data */ 203 mov r3, #0 2042: 205 str r3, [r1], #0x0004 /* get zero init data */ 206 subs r2, r2, #4 207 bgt 2b 208 209 mov r1, #28 /* loader info size is 28 bytes also second arg */ 210 subs sp, sp, r1 /* allocate arm_boot_params struct on stack */ 211 mov r0, sp /* loader info pointer is first arg */ 212 bic sp, sp, #7 /* align stack to 8 bytes */ 213 str r1, [r0] /* Store length of loader info */ 214 str r8, [r0, #4] /* Store r0 from boot loader */ 215 str r9, [r0, #8] /* Store r1 from boot loader */ 216 str r10, [r0, #12] /* store r2 from boot loader */ 217 str r11, [r0, #16] /* store r3 from boot loader */ 218 str r5, [r0, #20] /* store the physical address */ 219 adr r4, Lpagetable /* load the pagetable address */ 220 ldr r5, [r4, #4] 221 str r5, [r0, #24] /* store the pagetable address */ 222 mov fp, #0 /* trace back starts here */ 223 bl _C_LABEL(initarm) /* Off we go */ 224 225 /* init arm will return the new stack pointer. */ 226 mov sp, r0 227 228 bl _C_LABEL(mi_startup) /* call mi_startup()! */ 229 230 ldr r0, =.Lmainreturned 231 b _C_LABEL(panic) 232 /* NOTREACHED */ 233END(_start) 234 235#define VA_TO_PA_POINTER(name, table) \ 236name: ;\ 237 .word . ;\ 238 .word table 239 240/* 241 * Returns the physical address of a magic va to pa pointer. 242 * r0 - The pagetable data pointer. This must be built using the 243 * VA_TO_PA_POINTER macro. 244 * e.g. 245 * VA_TO_PA_POINTER(Lpagetable, pagetable) 246 * ... 247 * adr r0, Lpagetable 248 * bl translate_va_to_pa 249 * r0 will now contain the physical address of pagetable 250 * r1, r2 - Trashed 251 */ 252translate_va_to_pa: 253 ldr r1, [r0] 254 sub r2, r1, r0 255 /* At this point: r2 = VA - PA */ 256 257 /* 258 * Find the physical address of the table. After these two 259 * instructions: 260 * r1 = va(pagetable) 261 * 262 * r0 = va(pagetable) - (VA - PA) 263 * = va(pagetable) - VA + PA 264 * = pa(pagetable) 265 */ 266 ldr r1, [r0, #4] 267 sub r0, r1, r2 268 mov pc, lr 269 270/* 271 * Init MMU 272 * r0 - the table base address 273 */ 274 275ASENTRY_NP(init_mmu) 276 277 /* Setup TLB and MMU registers */ 278 mcr CP15_TTBR0(r0) /* Set TTB */ 279 mov r0, #0 280 mcr CP15_CONTEXTIDR(r0) /* Set ASID to 0 */ 281 282 /* Set the Domain Access register */ 283 mov r0, #DOMAIN_CLIENT /* Only domain #0 is used */ 284 mcr CP15_DACR(r0) 285 286 /* 287 * Set TEX remap registers 288 * - All is set to uncacheable memory 289 */ 290 ldr r0, =0xAAAAA 291 mcr CP15_PRRR(r0) 292 mov r0, #0 293 mcr CP15_NMRR(r0) 294 mcr CP15_TLBIALL /* Flush TLB */ 295 DSB 296 ISB 297 298 /* Enable MMU */ 299 mrc CP15_SCTLR(r0) 300 orr r0, r0, #CPU_CONTROL_MMU_ENABLE 301 orr r0, r0, #CPU_CONTROL_V6_EXTPAGE 302 orr r0, r0, #CPU_CONTROL_TR_ENABLE 303 orr r0, r0, #CPU_CONTROL_AF_ENABLE 304 mcr CP15_SCTLR(r0) 305 DSB 306 ISB 307 mcr CP15_TLBIALL /* Flush TLB */ 308 mcr CP15_BPIALL /* Flush Branch predictor */ 309 DSB 310 ISB 311 312 mov pc, lr 313END(init_mmu) 314 315 316/* 317 * Init SMP coherent mode, enable caching and switch to final MMU table. 318 * Called with disabled caches 319 * r0 - The table base address 320 * r1 - clear bits for aux register 321 * r2 - set bits for aux register 322 */ 323ASENTRY_NP(reinit_mmu) 324 push {r4-r11, lr} 325 mov r4, r0 326 mov r5, r1 327 mov r6, r2 328 329 /* !! Be very paranoid here !! */ 330 /* !! We cannot write single bit here !! */ 331 332#if 0 /* XXX writeback shouldn't be necessary */ 333 /* Write back and invalidate all integrated caches */ 334 bl dcache_wbinv_poc_all 335#else 336 bl dcache_inv_pou_all 337#endif 338 mcr CP15_ICIALLU 339 DSB 340 ISB 341 342 /* Set auxiliary register */ 343 mrc CP15_ACTLR(r7) 344 bic r8, r7, r5 /* Mask bits */ 345 eor r8, r8, r6 /* Set bits */ 346 teq r7, r8 347 mcrne CP15_ACTLR(r8) 348 DSB 349 ISB 350 351 /* Enable caches. */ 352 mrc CP15_SCTLR(r7) 353 orr r7, #CPU_CONTROL_DC_ENABLE 354 orr r7, #CPU_CONTROL_IC_ENABLE 355 orr r7, #CPU_CONTROL_BPRD_ENABLE 356 mcr CP15_SCTLR(r7) 357 DSB 358 359 mcr CP15_TTBR0(r4) /* Set new TTB */ 360 DSB 361 ISB 362 363 mcr CP15_TLBIALL /* Flush TLB */ 364 mcr CP15_BPIALL /* Flush Branch predictor */ 365 DSB 366 ISB 367 368#if 0 /* XXX writeback shouldn't be necessary */ 369 /* Write back and invalidate all integrated caches */ 370 bl dcache_wbinv_poc_all 371#else 372 bl dcache_inv_pou_all 373#endif 374 mcr CP15_ICIALLU 375 DSB 376 ISB 377 378 pop {r4-r11, pc} 379END(reinit_mmu) 380 381 382/* 383 * Builds the page table 384 * r0 - The table base address 385 * r1 - The physical address (trashed) 386 * r2 - The virtual address (trashed) 387 * r3 - The number of 1MiB sections 388 * r4 - Trashed 389 * 390 * Addresses must be 1MiB aligned 391 */ 392build_device_pagetables: 393 ldr r4, =PTE1_V|PTE1_A|PTE1_AP_KRW|TEX1_CLASS_0 394 b 1f 395build_pagetables: 396 /* Set the required page attributed */ 397 ldr r4, =PTE1_V|PTE1_A|PTE1_AP_KRW|TEX1_CLASS_0 3981: 399 orr r1, r4 400 401 /* Move the virtual address to the correct bit location */ 402 lsr r2, #(PTE1_SHIFT - 2) 403 404 mov r4, r3 4052: 406 str r1, [r0, r2] 407 add r2, r2, #4 408 add r1, r1, #(PTE1_SIZE) 409 adds r4, r4, #-1 410 bhi 2b 411 412 mov pc, lr 413 414VA_TO_PA_POINTER(Lpagetable, boot_pt1) 415 416 417.Lstart: 418 .word _edata /* Note that these three items are */ 419 .word _ebss /* loaded with a single ldmia and */ 420 .word svcstk /* must remain in order together. */ 421 422.Lmainreturned: 423 .asciz "main() returned" 424 .align 2 425 426 .bss 427svcstk: 428 .space INIT_ARM_STACK_SIZE * MAXCPU 429 430/* 431 * Memory for the initial pagetable. We are unable to place this in 432 * the bss as this will be cleared after the table is loaded. 433 */ 434 .section ".init_pagetable" 435 .align 14 /* 16KiB aligned */ 436 .globl boot_pt1 437boot_pt1: 438 .space L1_TABLE_SIZE 439 440 .text 441 .align 2 442 443.Lcpufuncs: 444 .word _C_LABEL(cpufuncs) 445 446#if defined(SMP) 447 448ASENTRY_NP(mpentry) 449 /* Make sure interrupts are disabled. */ 450 cpsid ifa 451 452 LEAVE_HYP 453 454 /* Setup core, disable all caches. */ 455 mrc CP15_SCTLR(r0) 456 bic r0, #CPU_CONTROL_MMU_ENABLE 457 bic r0, #CPU_CONTROL_DC_ENABLE 458 bic r0, #CPU_CONTROL_IC_ENABLE 459 bic r0, #CPU_CONTROL_BPRD_ENABLE 460 bic r0, #CPU_CONTROL_SW_ENABLE 461 orr r0, #CPU_CONTROL_UNAL_ENABLE 462 orr r0, #CPU_CONTROL_AFLT_ENABLE 463 orr r0, #CPU_CONTROL_VECRELOC 464 mcr CP15_SCTLR(r0) 465 DSB 466 ISB 467 468 /* Invalidate L1 cache I+D cache */ 469 bl dcache_inv_pou_all 470 mcr CP15_ICIALLU 471 DSB 472 ISB 473 474 /* Find the delta between VA and PA */ 475 adr r0, Lpagetable 476 bl translate_va_to_pa 477 478 bl init_mmu 479 480 adr r1, .Lstart+8 /* Get initstack pointer from */ 481 ldr sp, [r1] /* startup data. */ 482 mrc CP15_MPIDR(r0) /* Get processor id number. */ 483 and r0, r0, #0x0f 484 mov r1, #INIT_ARM_STACK_SIZE 485 mul r2, r1, r0 /* Point sp to initstack */ 486 add sp, sp, r2 /* area for this processor. */ 487 488 /* Switch to virtual addresses. */ 489 ldr pc, =1f 4901: 491 mov fp, #0 /* trace back starts here */ 492 bl _C_LABEL(init_secondary)/* Off we go, cpu id in r0. */ 493 494 adr r0, .Lmpreturned 495 b _C_LABEL(panic) 496 /* NOTREACHED */ 497END(mpentry) 498 499.Lmpreturned: 500 .asciz "init_secondary() returned" 501 .align 2 502#endif 503 504ENTRY_NP(cpu_halt) 505 506 /* XXX re-implement !!! */ 507 cpsid ifa 508 bl dcache_wbinv_poc_all 509 510 ldr r4, .Lcpu_reset_address 511 ldr r4, [r4] 512 teq r4, #0 513 movne pc, r4 5141: 515 WFI 516 b 1b 517 518 /* 519 * _cpu_reset_address contains the address to branch to, to complete 520 * the cpu reset after turning the MMU off 521 * This variable is provided by the hardware specific code 522 */ 523.Lcpu_reset_address: 524 .word _C_LABEL(cpu_reset_address) 525END(cpu_halt) 526 527 528/* 529 * setjump + longjmp 530 */ 531ENTRY(setjmp) 532 stmia r0, {r4-r14} 533 mov r0, #0x00000000 534 RET 535END(setjmp) 536 537ENTRY(longjmp) 538 ldmia r0, {r4-r14} 539 mov r0, #0x00000001 540 RET 541END(longjmp) 542 543 .data 544 .global _C_LABEL(esym) 545_C_LABEL(esym): .word _C_LABEL(end) 546 547ENTRY_NP(abort) 548 b _C_LABEL(abort) 549END(abort) 550 551ENTRY_NP(sigcode) 552 mov r0, sp 553 add r0, r0, #SIGF_UC 554 555 /* 556 * Call the sigreturn system call. 557 * 558 * We have to load r7 manually rather than using 559 * "ldr r7, =SYS_sigreturn" to ensure the value of szsigcode is 560 * correct. Using the alternative places esigcode at the address 561 * of the data rather than the address one past the data. 562 */ 563 564 ldr r7, [pc, #12] /* Load SYS_sigreturn */ 565 swi SYS_sigreturn 566 567 /* Well if that failed we better exit quick ! */ 568 569 ldr r7, [pc, #8] /* Load SYS_exit */ 570 swi SYS_exit 571 572 /* Branch back to retry SYS_sigreturn */ 573 b . - 16 574END(sigcode) 575 .word SYS_sigreturn 576 .word SYS_exit 577 578 .align 2 579 .global _C_LABEL(esigcode) 580 _C_LABEL(esigcode): 581 582 .data 583 .global szsigcode 584szsigcode: 585 .long esigcode-sigcode 586 587/* End of locore.S */ 588