1129198Scognet/* $NetBSD: locore.S,v 1.14 2003/04/20 16:21:40 thorpej Exp $ */ 2129198Scognet 3139735Simp/*- 4239268Sgonzo * Copyright 2011 Semihalf 5129198Scognet * Copyright (C) 1994-1997 Mark Brinicombe 6129198Scognet * Copyright (C) 1994 Brini 7129198Scognet * All rights reserved. 8129198Scognet * 9129198Scognet * Redistribution and use in source and binary forms, with or without 10129198Scognet * modification, are permitted provided that the following conditions 11129198Scognet * are met: 12129198Scognet * 1. Redistributions of source code must retain the above copyright 13129198Scognet * notice, this list of conditions and the following disclaimer. 14129198Scognet * 2. Redistributions in binary form must reproduce the above copyright 15129198Scognet * notice, this list of conditions and the following disclaimer in the 16129198Scognet * documentation and/or other materials provided with the distribution. 17129198Scognet * 3. All advertising materials mentioning features or use of this software 18129198Scognet * must display the following acknowledgement: 19129198Scognet * This product includes software developed by Brini. 20129198Scognet * 4. The name of Brini may not be used to endorse or promote products 21129198Scognet * derived from this software without specific prior written permission. 22129198Scognet * 23129198Scognet * THIS SOFTWARE IS PROVIDED BY BRINI ``AS IS'' AND ANY EXPRESS OR 24129198Scognet * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 25129198Scognet * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26129198Scognet * IN NO EVENT SHALL BRINI BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 27129198Scognet * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 28129198Scognet * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 29129198Scognet * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 30129198Scognet * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 31129198Scognet * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 32129198Scognet * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33129198Scognet * 34129198Scognet */ 35129198Scognet 36129198Scognet#include "assym.s" 37135640Scognet#include <sys/syscall.h> 38129198Scognet#include <machine/asm.h> 39129198Scognet#include <machine/armreg.h> 40295801Sskra#include <machine/pte-v4.h> 41236524Simp 42129198Scognet__FBSDID("$FreeBSD: stable/11/sys/arm/arm/locore-v4.S 331890 2018-04-02 22:02:49Z gonzo $"); 43129198Scognet 44292523Sian/* 2K initial stack is plenty, it is only used by initarm() */ 45292523Sian#define INIT_ARM_STACK_SIZE 2048 46273288Sandrew 47129198Scognet#define CPWAIT_BRANCH \ 48129198Scognet sub pc, pc, #4 49129198Scognet 50129198Scognet#define CPWAIT(tmp) \ 51129198Scognet mrc p15, 0, tmp, c2, c0, 0 /* arbitrary read of CP15 */ ;\ 52129198Scognet mov tmp, tmp /* wait for it to complete */ ;\ 53129198Scognet CPWAIT_BRANCH /* branch to next insn */ 54129198Scognet 55235277Simp/* 56273288Sandrew * This is for libkvm, and should be the address of the beginning 57235277Simp * of the kernel text segment (not necessarily the same as kernbase). 58273288Sandrew * 59273288Sandrew * These are being phased out. Newer copies of libkvm don't need these 60273288Sandrew * values as the information is added to the core file by inspecting 61273288Sandrew * the running kernel. 62235277Simp */ 63129198Scognet .text 64276596Sian .align 2 65273288Sandrew#ifdef PHYSADDR 66129198Scognet.globl kernbase 67129198Scognet.set kernbase,KERNBASE 68150863Scognet.globl physaddr 69150863Scognet.set physaddr,PHYSADDR 70273288Sandrew#endif 71129198Scognet 72183878Sraj/* 73236524Simp * On entry for FreeBSD boot ABI: 74236524Simp * r0 - metadata pointer or 0 (boothowto on AT91's boot2) 75218227Smarcel * r1 - if (r0 == 0) then metadata pointer 76236524Simp * On entry for Linux boot ABI: 77236524Simp * r0 - 0 78236524Simp * r1 - machine type (passed as arg2 to initarm) 79236524Simp * r2 - Pointer to a tagged list or dtb image (phys addr) (passed as arg1 initarm) 80236524Simp * 81236524Simp * For both types of boot we gather up the args, put them in a struct arm_boot_params 82236524Simp * structure and pass that to initarm. 83183878Sraj */ 84269390Sian .globl btext 85269390Sianbtext: 86218227SmarcelASENTRY_NP(_start) 87250253Sian STOP_UNWINDING /* Can't unwind into the bootloader! */ 88250253Sian 89236524Simp mov r9, r0 /* 0 or boot mode from boot2 */ 90236524Simp mov r8, r1 /* Save Machine type */ 91236524Simp mov ip, r2 /* Save meta data */ 92266271Sgavin mov fp, r3 /* Future expansion */ 93183878Sraj 94193846Smarcel /* Make sure interrupts are disabled. */ 95193846Smarcel mrs r7, cpsr 96271398Sandrew orr r7, r7, #(PSR_I | PSR_F) 97193846Smarcel msr cpsr_c, r7 98193846Smarcel 99166819Scognet#if defined (FLASHADDR) && defined(LOADERRAMADDR) 100292523Sian/* 101292523Sian * Sanity check the configuration. 102292523Sian * FLASHADDR and LOADERRAMADDR depend on PHYSADDR in some cases. 103292523Sian * ARMv4 and ARMv5 make assumptions on where they are loaded. 104292523Sian * TODO: Fix the ARMv4/v5 case. 105292523Sian */ 106292523Sian#ifndef PHYSADDR 107292523Sian#error PHYSADDR must be defined for this configuration 108292523Sian#endif 109292523Sian 110166819Scognet /* Check if we're running from flash. */ 111166819Scognet ldr r7, =FLASHADDR 112175983Sraj /* 113166819Scognet * If we're running with MMU disabled, test against the 114166819Scognet * physical address instead. 115166819Scognet */ 116300533Sian mrc CP15_SCTLR(r2) 117166819Scognet ands r2, r2, #CPU_CONTROL_MMU_ENABLE 118236524Simp ldreq r6, =PHYSADDR 119236524Simp ldrne r6, =LOADERRAMADDR 120236524Simp cmp r7, r6 121166819Scognet bls flash_lower 122166819Scognet cmp r7, pc 123166819Scognet bhi from_ram 124166819Scognet b do_copy 125282025Sandrew 126166819Scognetflash_lower: 127236524Simp cmp r6, pc 128166819Scognet bls from_ram 129166819Scognetdo_copy: 130236524Simp ldr r7, =KERNBASE 131175983Sraj adr r1, _start 132166819Scognet ldr r0, Lreal_start 133166819Scognet ldr r2, Lend 134166819Scognet sub r2, r2, r0 135236524Simp sub r0, r0, r7 136236524Simp add r0, r0, r6 137166819Scognet mov r4, r0 138166819Scognet bl memcpy 139166819Scognet ldr r0, Lram_offset 140166819Scognet add pc, r4, r0 141166819ScognetLram_offset: .word from_ram-_C_LABEL(_start) 142166819Scognetfrom_ram: 143166819Scognet nop 144135640Scognet#endif 145143681Sjmg 146166819Scognetdisable_mmu: 147135640Scognet /* Disable MMU for a while */ 148300533Sian mrc CP15_SCTLR(r2) 149153550Scognet bic r2, r2, #(CPU_CONTROL_MMU_ENABLE | CPU_CONTROL_DC_ENABLE |\ 150153550Scognet CPU_CONTROL_WBUF_ENABLE) 151153550Scognet bic r2, r2, #(CPU_CONTROL_IC_ENABLE) 152153550Scognet bic r2, r2, #(CPU_CONTROL_BPRD_ENABLE) 153300533Sian mcr CP15_SCTLR(r2) 154129198Scognet 155135640Scognet nop 156135640Scognet nop 157135640Scognet nop 158271240Sandrew CPWAIT(r0) 159271240Sandrew 160135640ScognetLunmapped: 161261227Sandrew /* 162261227Sandrew * Build page table from scratch. 163261227Sandrew */ 164261227Sandrew 165292523Sian /* 166292523Sian * Figure out the physical address we're loaded at by assuming this 167292523Sian * entry point code is in the first L1 section and so if we clear the 168292523Sian * offset bits of the pc that will give us the section-aligned load 169292523Sian * address, which remains in r5 throughout all the following code. 170292523Sian */ 171292523Sian ldr r2, =(L1_S_OFFSET) 172292523Sian bic r5, pc, r2 173292523Sian 174292523Sian /* Find the delta between VA and PA, result stays in r0 throughout. */ 175261855Sandrew adr r0, Lpagetable 176271232Sandrew bl translate_va_to_pa 177261855Sandrew 178292523Sian /* 179292523Sian * First map the entire 4GB address space as VA=PA. It's mapped as 180292523Sian * normal (cached) memory because it's for things like accessing the 181292523Sian * parameters passed in from the bootloader, which might be at any 182292523Sian * physical address, different for every platform. 183266849Scognet */ 184292523Sian mov r1, #0 185292523Sian mov r2, #0 186292523Sian mov r3, #4096 187292523Sian bl build_pagetables 188292523Sian 189292523Sian /* 190292523Sian * Next we do 64MiB starting at the physical load address, mapped to 191292523Sian * the VA the kernel is linked for. 192282025Sandrew */ 193282025Sandrew mov r1, r5 194292523Sian ldr r2, =(KERNVIRTADDR) 195282025Sandrew mov r3, #64 196282025Sandrew bl build_pagetables 197331890Sgonzo#if defined(PHYSADDR) && (KERNVIRTADDR != KERNBASE) 198331890Sgonzo/* 199331890Sgonzo * If the kernel wasn't loaded at the beginning of the ram, map the memory 200331890Sgonzo * before the kernel too, as some ports use that for pagetables, stack, etc... 201331890Sgonzo */ 202331890Sgonzo ldr r1, =PHYSADDR 203331890Sgonzo ldr r2, =KERNBASE 204331890Sgonzo ldr r3, =((KERNVIRTADDR - KERNBASE) / L1_S_SIZE) 205331890Sgonzo bl build_pagetables 206331890Sgonzo#endif 207282025Sandrew 208292523Sian /* Create a device mapping for early_printf if specified. */ 209261606Sandrew#if defined(SOCDEV_PA) && defined(SOCDEV_VA) 210261783Simp ldr r1, =SOCDEV_PA 211261783Simp ldr r2, =SOCDEV_VA 212292523Sian mov r3, #1 213292523Sian bl build_device_pagetables 214261606Sandrew#endif 215129198Scognet 216129198Scognet mcr p15, 0, r0, c2, c0, 0 /* Set TTB */ 217129198Scognet mcr p15, 0, r0, c8, c7, 0 /* Flush TLB */ 218129198Scognet 219129198Scognet /* Set the Domain Access register. Very important! */ 220282025Sandrew mov r0, #((DOMAIN_CLIENT << (PMAP_DOMAIN_KERNEL*2)) | DOMAIN_CLIENT) 221129198Scognet mcr p15, 0, r0, c3, c0, 0 222282025Sandrew /* 223248961Sian * Enable MMU. 224248961Sian */ 225300533Sian mrc CP15_SCTLR(r0) 226243602Sgonzo orr r0, r0, #(CPU_CONTROL_MMU_ENABLE) 227300533Sian mcr CP15_SCTLR(r0) 228153550Scognet nop 229153550Scognet nop 230153550Scognet nop 231129198Scognet CPWAIT(r0) 232129198Scognet 233292523Sian /* Transition the PC from physical to virtual addressing. */ 234292523Sian ldr pc,=mmu_done 235292523Sian 236129198Scognetmmu_done: 237153550Scognet nop 238129198Scognet adr r1, .Lstart 239129198Scognet ldmia r1, {r1, r2, sp} /* Set initial stack and */ 240129198Scognet sub r2, r2, r1 /* get zero init data */ 241129198Scognet mov r3, #0 242129198Scognet.L1: 243150863Scognet str r3, [r1], #0x0004 /* get zero init data */ 244129198Scognet subs r2, r2, #4 245129198Scognet bgt .L1 246129198Scognet 247142145Scognetvirt_done: 248261663Sandrew mov r1, #28 /* loader info size is 28 bytes also second arg */ 249236524Simp subs sp, sp, r1 /* allocate arm_boot_params struct on stack */ 250261855Sandrew mov r0, sp /* loader info pointer is first arg */ 251247608Sandrew bic sp, sp, #7 /* align stack to 8 bytes */ 252236524Simp str r1, [r0] /* Store length of loader info */ 253236524Simp str r9, [r0, #4] /* Store r0 from boot loader */ 254236524Simp str r8, [r0, #8] /* Store r1 from boot loader */ 255236524Simp str ip, [r0, #12] /* store r2 from boot loader */ 256236524Simp str fp, [r0, #16] /* store r3 from boot loader */ 257261562Sandrew str r5, [r0, #20] /* store the physical address */ 258261855Sandrew adr r4, Lpagetable /* load the pagetable address */ 259261855Sandrew ldr r5, [r4, #4] 260261663Sandrew str r5, [r0, #24] /* store the pagetable address */ 261175983Sraj mov fp, #0 /* trace back starts here */ 262129198Scognet bl _C_LABEL(initarm) /* Off we go */ 263129198Scognet 264129198Scognet /* init arm will return the new stack pointer. */ 265129198Scognet mov sp, r0 266129198Scognet 267282024Sandrew bl _C_LABEL(mi_startup) /* call mi_startup()! */ 268129198Scognet 269129198Scognet adr r0, .Lmainreturned 270130164Sphk b _C_LABEL(panic) 271175983Sraj /* NOTREACHED */ 272261606SandrewEND(_start) 273261606Sandrew 274271232Sandrew#define VA_TO_PA_POINTER(name, table) \ 275271232Sandrewname: ;\ 276271232Sandrew .word . ;\ 277271232Sandrew .word table 278271232Sandrew 279261606Sandrew/* 280271232Sandrew * Returns the physical address of a magic va to pa pointer. 281271232Sandrew * r0 - The pagetable data pointer. This must be built using the 282271232Sandrew * VA_TO_PA_POINTER macro. 283271232Sandrew * e.g. 284271232Sandrew * VA_TO_PA_POINTER(Lpagetable, pagetable) 285271232Sandrew * ... 286271232Sandrew * adr r0, Lpagetable 287271232Sandrew * bl translate_va_to_pa 288271232Sandrew * r0 will now contain the physical address of pagetable 289271232Sandrew * r1, r2 - Trashed 290271232Sandrew */ 291271232Sandrewtranslate_va_to_pa: 292271232Sandrew ldr r1, [r0] 293271232Sandrew sub r2, r1, r0 294271232Sandrew /* At this point: r2 = VA - PA */ 295271232Sandrew 296271232Sandrew /* 297271232Sandrew * Find the physical address of the table. After these two 298271232Sandrew * instructions: 299271232Sandrew * r1 = va(pagetable) 300271232Sandrew * 301271232Sandrew * r0 = va(pagetable) - (VA - PA) 302271232Sandrew * = va(pagetable) - VA + PA 303271232Sandrew * = pa(pagetable) 304271232Sandrew */ 305271232Sandrew ldr r1, [r0, #4] 306271232Sandrew sub r0, r1, r2 307271232Sandrew RET 308271232Sandrew 309271232Sandrew/* 310261606Sandrew * Builds the page table 311261606Sandrew * r0 - The table base address 312261606Sandrew * r1 - The physical address (trashed) 313261606Sandrew * r2 - The virtual address (trashed) 314261606Sandrew * r3 - The number of 1MiB sections 315261606Sandrew * r4 - Trashed 316261606Sandrew * 317261606Sandrew * Addresses must be 1MiB aligned 318261606Sandrew */ 319292523Sianbuild_device_pagetables: 320292523Sian ldr r4, =(L1_TYPE_S|L1_S_AP(AP_KRW)) 321292523Sian b 1f 322261606Sandrewbuild_pagetables: 323261606Sandrew /* Set the required page attributed */ 324261606Sandrew ldr r4, =(L1_TYPE_S|L1_S_C|L1_S_AP(AP_KRW)) 325292523Sian1: 326261606Sandrew orr r1, r4 327261606Sandrew 328261606Sandrew /* Move the virtual address to the correct bit location */ 329261606Sandrew lsr r2, #(L1_S_SHIFT - 2) 330261606Sandrew 331261606Sandrew mov r4, r3 332292523Sian2: 333261606Sandrew str r1, [r0, r2] 334261606Sandrew add r2, r2, #4 335261606Sandrew add r1, r1, #(L1_S_SIZE) 336261606Sandrew adds r4, r4, #-1 337292523Sian bhi 2b 338261606Sandrew 339261606Sandrew RET 340261606Sandrew 341271232SandrewVA_TO_PA_POINTER(Lpagetable, pagetable) 342261855Sandrew 343166819ScognetLreal_start: 344166819Scognet .word _start 345261855SandrewLend: 346166819Scognet .word _edata 347261855Sandrew 348129198Scognet.Lstart: 349129198Scognet .word _edata 350261227Sandrew .word _ebss 351129198Scognet .word svcstk + INIT_ARM_STACK_SIZE 352129198Scognet 353153550Scognet.Lvirt_done: 354153550Scognet .word virt_done 355239268Sgonzo 356129198Scognet.Lmainreturned: 357129198Scognet .asciz "main() returned" 358276596Sian .align 2 359129198Scognet 360129198Scognet .bss 361129198Scognetsvcstk: 362129198Scognet .space INIT_ARM_STACK_SIZE 363129198Scognet 364261227Sandrew/* 365261227Sandrew * Memory for the initial pagetable. We are unable to place this in 366261227Sandrew * the bss as this will be cleared after the table is loaded. 367261227Sandrew */ 368321049Semaste .section ".init_pagetable", "aw", %nobits 369261227Sandrew .align 14 /* 16KiB aligned */ 370261227Sandrewpagetable: 371261227Sandrew .space L1_TABLE_SIZE 372261227Sandrew 373129198Scognet .text 374276596Sian .align 2 375129198Scognet 376143681Sjmg.Lcpufuncs: 377129198Scognet .word _C_LABEL(cpufuncs) 378129198Scognet 379135640ScognetENTRY_NP(cpu_halt) 380282025Sandrew mrs r2, cpsr 381129198Scognet bic r2, r2, #(PSR_MODE) 382282025Sandrew orr r2, r2, #(PSR_SVC32_MODE) 383271398Sandrew orr r2, r2, #(PSR_I | PSR_F) 384282025Sandrew msr cpsr_fsxc, r2 385129198Scognet 386129198Scognet ldr r4, .Lcpu_reset_address 387129198Scognet ldr r4, [r4] 388129198Scognet 389129198Scognet ldr r0, .Lcpufuncs 390129198Scognet mov lr, pc 391129198Scognet ldr pc, [r0, #CF_IDCACHE_WBINV_ALL] 392183839Sraj mov lr, pc 393183839Sraj ldr pc, [r0, #CF_L2CACHE_WBINV_ALL] 394129198Scognet 395129198Scognet /* 396129198Scognet * Load the cpu_reset_needs_v4_MMU_disable flag to determine if it's 397129198Scognet * necessary. 398129198Scognet */ 399129198Scognet 400129198Scognet ldr r1, .Lcpu_reset_needs_v4_MMU_disable 401129198Scognet ldr r1, [r1] 402129198Scognet cmp r1, #0 403129198Scognet mov r2, #0 404129198Scognet 405129198Scognet /* 406175983Sraj * MMU & IDC off, 32 bit program & data space 407129198Scognet * Hurl ourselves into the ROM 408129198Scognet */ 409129198Scognet mov r0, #(CPU_CONTROL_32BP_ENABLE | CPU_CONTROL_32BD_ENABLE) 410300533Sian mcr CP15_SCTLR(r0) 411282025Sandrew mcrne p15, 0, r2, c8, c7, 0 /* nail I+D TLB on ARMv4 and greater */ 412282025Sandrew mov pc, r4 413129198Scognet 414129198Scognet /* 415129198Scognet * _cpu_reset_address contains the address to branch to, to complete 416129198Scognet * the cpu reset after turning the MMU off 417129198Scognet * This variable is provided by the hardware specific code 418129198Scognet */ 419129198Scognet.Lcpu_reset_address: 420129198Scognet .word _C_LABEL(cpu_reset_address) 421129198Scognet 422129198Scognet /* 423129198Scognet * cpu_reset_needs_v4_MMU_disable contains a flag that signals if the 424129198Scognet * v4 MMU disable instruction needs executing... it is an illegal instruction 425129198Scognet * on f.e. ARM6/7 that locks up the computer in an endless illegal 426129198Scognet * instruction / data-abort / reset loop. 427129198Scognet */ 428129198Scognet.Lcpu_reset_needs_v4_MMU_disable: 429129198Scognet .word _C_LABEL(cpu_reset_needs_v4_MMU_disable) 430248361SandrewEND(cpu_halt) 431129198Scognet 432129198Scognet 433129198Scognet/* 434129198Scognet * setjump + longjmp 435129198Scognet */ 436129198ScognetENTRY(setjmp) 437129198Scognet stmia r0, {r4-r14} 438129198Scognet mov r0, #0x00000000 439137463Scognet RET 440248361SandrewEND(setjmp) 441129198Scognet 442129198ScognetENTRY(longjmp) 443129198Scognet ldmia r0, {r4-r14} 444129198Scognet mov r0, #0x00000001 445137463Scognet RET 446248361SandrewEND(longjmp) 447129198Scognet 448129198Scognet .data 449282024Sandrew .global _C_LABEL(esym) 450129198Scognet_C_LABEL(esym): .word _C_LABEL(end) 451129198Scognet 452129198ScognetENTRY_NP(abort) 453129198Scognet b _C_LABEL(abort) 454248361SandrewEND(abort) 455129198Scognet 456135640ScognetENTRY_NP(sigcode) 457135640Scognet mov r0, sp 458262903Sian add r0, r0, #SIGF_UC 459245414Sandrew 460245414Sandrew /* 461245414Sandrew * Call the sigreturn system call. 462282024Sandrew * 463245414Sandrew * We have to load r7 manually rather than using 464245414Sandrew * "ldr r7, =SYS_sigreturn" to ensure the value of szsigcode is 465245414Sandrew * correct. Using the alternative places esigcode at the address 466245414Sandrew * of the data rather than the address one past the data. 467245414Sandrew */ 468245414Sandrew 469245414Sandrew ldr r7, [pc, #12] /* Load SYS_sigreturn */ 470135640Scognet swi SYS_sigreturn 471135640Scognet 472135640Scognet /* Well if that failed we better exit quick ! */ 473135640Scognet 474245414Sandrew ldr r7, [pc, #8] /* Load SYS_exit */ 475135640Scognet swi SYS_exit 476135640Scognet 477245414Sandrew /* Branch back to retry SYS_sigreturn */ 478245414Sandrew b . - 16 479269390SianEND(sigcode) 480245414Sandrew .word SYS_sigreturn 481245414Sandrew .word SYS_exit 482245414Sandrew 483276596Sian .align 2 484135640Scognet .global _C_LABEL(esigcode) 485135640Scognet _C_LABEL(esigcode): 486143681Sjmg 487135640Scognet .data 488135640Scognet .global szsigcode 489135640Scognetszsigcode: 490135640Scognet .long esigcode-sigcode 491269390Sian 492129198Scognet/* End of locore.S */ 493