/*1* linux/arch/arm/kernel/head.S2*3* Copyright (C) 1994-2002 Russell King4* Copyright (c) 2003 ARM Limited5* All Rights Reserved6*7* This program is free software; you can redistribute it and/or modify8* it under the terms of the GNU General Public License version 2 as9* published by the Free Software Foundation.10*11* Kernel startup code for all 32-bit CPUs12*/13#include <linux/linkage.h>14#include <linux/init.h>1516#include <asm/assembler.h>17#include <asm/domain.h>18#include <asm/ptrace.h>19#include <asm/asm-offsets.h>20#include <asm/memory.h>21#include <asm/thread_info.h>22#include <asm/system.h>2324#ifdef CONFIG_DEBUG_LL25#include <mach/debug-macro.S>26#endif2728/*29* swapper_pg_dir is the virtual address of the initial page table.30* We place the page tables 16K below KERNEL_RAM_VADDR. Therefore, we must31* make sure that KERNEL_RAM_VADDR is correctly set. Currently, we expect32* the least significant 16 bits to be 0x8000, but we could probably33* relax this restriction to KERNEL_RAM_VADDR >= PAGE_OFFSET + 0x4000.34*/35#define KERNEL_RAM_VADDR (PAGE_OFFSET + TEXT_OFFSET)36#if (KERNEL_RAM_VADDR & 0xffff) != 0x800037#error KERNEL_RAM_VADDR must start at 0xXXXX800038#endif3940.globl swapper_pg_dir41.equ swapper_pg_dir, KERNEL_RAM_VADDR - 0x40004243.macro pgtbl, rd, phys44add \rd, \phys, #TEXT_OFFSET - 0x400045.endm4647#ifdef CONFIG_XIP_KERNEL48#define KERNEL_START XIP_VIRT_ADDR(CONFIG_XIP_PHYS_ADDR)49#define KERNEL_END _edata_loc50#else51#define KERNEL_START KERNEL_RAM_VADDR52#define KERNEL_END _end53#endif5455/*56* Kernel startup entry point.57* ---------------------------58*59* This is normally called from the decompressor code. The requirements60* are: MMU = off, D-cache = off, I-cache = dont care, r0 = 0,61* r1 = machine nr, r2 = atags or dtb pointer.62*63* This code is mostly position independent, so if you link the kernel at64* 0xc0008000, you call this at __pa(0xc0008000).65*66* See linux/arch/arm/tools/mach-types for the complete list of machine67* numbers for r1.68*69* We're trying to keep crap to a minimum; DO NOT add any machine specific70* crap here - that's what the boot loader (or in extreme, well justified71* circumstances, zImage) is for.72*/73__HEAD74ENTRY(stext)75setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 @ ensure svc mode76@ and irqs disabled77mrc p15, 0, r9, c0, c0 @ get processor id78bl __lookup_processor_type @ r5=procinfo r9=cpuid79movs r10, r5 @ invalid processor (r5=0)?80THUMB( it eq ) @ force fixup-able long branch encoding81beq __error_p @ yes, error 'p'8283#ifndef CONFIG_XIP_KERNEL84adr r3, 2f85ldmia r3, {r4, r8}86sub r4, r3, r4 @ (PHYS_OFFSET - PAGE_OFFSET)87add r8, r8, r4 @ PHYS_OFFSET88#else89ldr r8, =PLAT_PHYS_OFFSET90#endif9192/*93* r1 = machine no, r2 = atags or dtb,94* r8 = phys_offset, r9 = cpuid, r10 = procinfo95*/96bl __vet_atags97#ifdef CONFIG_SMP_ON_UP98bl __fixup_smp99#endif100#ifdef CONFIG_ARM_PATCH_PHYS_VIRT101bl __fixup_pv_table102#endif103bl __create_page_tables104105/*106* The following calls CPU specific code in a position independent107* manner. See arch/arm/mm/proc-*.S for details. r10 = base of108* xxx_proc_info structure selected by __lookup_processor_type109* above. On return, the CPU will be ready for the MMU to be110* turned on, and r0 will hold the CPU control register value.111*/112ldr r13, =__mmap_switched @ address to jump to after113@ mmu has been enabled114adr lr, BSYM(1f) @ return (PIC) address115mov r8, r4 @ set TTBR1 to swapper_pg_dir116ARM( add pc, r10, #PROCINFO_INITFUNC )117THUMB( add r12, r10, #PROCINFO_INITFUNC )118THUMB( mov pc, r12 )1191: b __enable_mmu120ENDPROC(stext)121.ltorg122#ifndef CONFIG_XIP_KERNEL1232: .long .124.long PAGE_OFFSET125#endif126127/*128* Setup the initial page tables. We only setup the barest129* amount which are required to get the kernel running, which130* generally means mapping in the kernel code.131*132* r8 = phys_offset, r9 = cpuid, r10 = procinfo133*134* Returns:135* r0, r3, r5-r7 corrupted136* r4 = physical page table address137*/138__create_page_tables:139pgtbl r4, r8 @ page table address140141/*142* Clear the 16K level 1 swapper page table143*/144mov r0, r4145mov r3, #0146add r6, r0, #0x40001471: str r3, [r0], #4148str r3, [r0], #4149str r3, [r0], #4150str r3, [r0], #4151teq r0, r6152bne 1b153154ldr r7, [r10, #PROCINFO_MM_MMUFLAGS] @ mm_mmuflags155156/*157* Create identity mapping to cater for __enable_mmu.158* This identity mapping will be removed by paging_init().159*/160adr r0, __enable_mmu_loc161ldmia r0, {r3, r5, r6}162sub r0, r0, r3 @ virt->phys offset163add r5, r5, r0 @ phys __enable_mmu164add r6, r6, r0 @ phys __enable_mmu_end165mov r5, r5, lsr #20166mov r6, r6, lsr #201671681: orr r3, r7, r5, lsl #20 @ flags + kernel base169str r3, [r4, r5, lsl #2] @ identity mapping170teq r5, r6171addne r5, r5, #1 @ next section172bne 1b173174/*175* Now setup the pagetables for our kernel direct176* mapped region.177*/178mov r3, pc179mov r3, r3, lsr #20180orr r3, r7, r3, lsl #20181add r0, r4, #(KERNEL_START & 0xff000000) >> 18182str r3, [r0, #(KERNEL_START & 0x00f00000) >> 18]!183ldr r6, =(KERNEL_END - 1)184add r0, r0, #4185add r6, r4, r6, lsr #181861: cmp r0, r6187add r3, r3, #1 << 20188strls r3, [r0], #4189bls 1b190191#ifdef CONFIG_XIP_KERNEL192/*193* Map some ram to cover our .data and .bss areas.194*/195add r3, r8, #TEXT_OFFSET196orr r3, r3, r7197add r0, r4, #(KERNEL_RAM_VADDR & 0xff000000) >> 18198str r3, [r0, #(KERNEL_RAM_VADDR & 0x00f00000) >> 18]!199ldr r6, =(_end - 1)200add r0, r0, #4201add r6, r4, r6, lsr #182021: cmp r0, r6203add r3, r3, #1 << 20204strls r3, [r0], #4205bls 1b206#endif207208/*209* Then map boot params address in r2 or210* the first 1MB of ram if boot params address is not specified.211*/212mov r0, r2, lsr #20213movs r0, r0, lsl #20214moveq r0, r8215sub r3, r0, r8216add r3, r3, #PAGE_OFFSET217add r3, r4, r3, lsr #18218orr r6, r7, r0219str r6, [r3]220221#ifdef CONFIG_DEBUG_LL222#ifndef CONFIG_DEBUG_ICEDCC223/*224* Map in IO space for serial debugging.225* This allows debug messages to be output226* via a serial console before paging_init.227*/228addruart r7, r3229230mov r3, r3, lsr #20231mov r3, r3, lsl #2232233add r0, r4, r3234rsb r3, r3, #0x4000 @ PTRS_PER_PGD*sizeof(long)235cmp r3, #0x0800 @ limit to 512MB236movhi r3, #0x0800237add r6, r0, r3238mov r3, r7, lsr #20239ldr r7, [r10, #PROCINFO_IO_MMUFLAGS] @ io_mmuflags240orr r3, r7, r3, lsl #202411: str r3, [r0], #4242add r3, r3, #1 << 20243teq r0, r6244bne 1b245246#else /* CONFIG_DEBUG_ICEDCC */247/* we don't need any serial debugging mappings for ICEDCC */248ldr r7, [r10, #PROCINFO_IO_MMUFLAGS] @ io_mmuflags249#endif /* !CONFIG_DEBUG_ICEDCC */250251#if defined(CONFIG_ARCH_NETWINDER) || defined(CONFIG_ARCH_CATS)252/*253* If we're using the NetWinder or CATS, we also need to map254* in the 16550-type serial port for the debug messages255*/256add r0, r4, #0xff000000 >> 18257orr r3, r7, #0x7c000000258str r3, [r0]259#endif260#ifdef CONFIG_ARCH_RPC261/*262* Map in screen at 0x02000000 & SCREEN2_BASE263* Similar reasons here - for debug. This is264* only for Acorn RiscPC architectures.265*/266add r0, r4, #0x02000000 >> 18267orr r3, r7, #0x02000000268str r3, [r0]269add r0, r4, #0xd8000000 >> 18270str r3, [r0]271#endif272#endif273mov pc, lr274ENDPROC(__create_page_tables)275.ltorg276.align277__enable_mmu_loc:278.long .279.long __enable_mmu280.long __enable_mmu_end281282#if defined(CONFIG_SMP)283__CPUINIT284ENTRY(secondary_startup)285/*286* Common entry point for secondary CPUs.287*288* Ensure that we're in SVC mode, and IRQs are disabled. Lookup289* the processor type - there is no need to check the machine type290* as it has already been validated by the primary processor.291*/292setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9293mrc p15, 0, r9, c0, c0 @ get processor id294bl __lookup_processor_type295movs r10, r5 @ invalid processor?296moveq r0, #'p' @ yes, error 'p'297THUMB( it eq ) @ force fixup-able long branch encoding298beq __error_p299300/*301* Use the page tables supplied from __cpu_up.302*/303adr r4, __secondary_data304ldmia r4, {r5, r7, r12} @ address to jump to after305sub lr, r4, r5 @ mmu has been enabled306ldr r4, [r7, lr] @ get secondary_data.pgdir307add r7, r7, #4308ldr r8, [r7, lr] @ get secondary_data.swapper_pg_dir309adr lr, BSYM(__enable_mmu) @ return address310mov r13, r12 @ __secondary_switched address311ARM( add pc, r10, #PROCINFO_INITFUNC ) @ initialise processor312@ (return control reg)313THUMB( add r12, r10, #PROCINFO_INITFUNC )314THUMB( mov pc, r12 )315ENDPROC(secondary_startup)316317/*318* r6 = &secondary_data319*/320ENTRY(__secondary_switched)321ldr sp, [r7, #4] @ get secondary_data.stack322mov fp, #0323b secondary_start_kernel324ENDPROC(__secondary_switched)325326.align327328.type __secondary_data, %object329__secondary_data:330.long .331.long secondary_data332.long __secondary_switched333#endif /* defined(CONFIG_SMP) */334335336337/*338* Setup common bits before finally enabling the MMU. Essentially339* this is just loading the page table pointer and domain access340* registers.341*342* r0 = cp#15 control register343* r1 = machine ID344* r2 = atags or dtb pointer345* r4 = page table pointer346* r9 = processor ID347* r13 = *virtual* address to jump to upon completion348*/349__enable_mmu:350#ifdef CONFIG_ALIGNMENT_TRAP351orr r0, r0, #CR_A352#else353bic r0, r0, #CR_A354#endif355#ifdef CONFIG_CPU_DCACHE_DISABLE356bic r0, r0, #CR_C357#endif358#ifdef CONFIG_CPU_BPREDICT_DISABLE359bic r0, r0, #CR_Z360#endif361#ifdef CONFIG_CPU_ICACHE_DISABLE362bic r0, r0, #CR_I363#endif364mov r5, #(domain_val(DOMAIN_USER, DOMAIN_MANAGER) | \365domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | \366domain_val(DOMAIN_TABLE, DOMAIN_MANAGER) | \367domain_val(DOMAIN_IO, DOMAIN_CLIENT))368mcr p15, 0, r5, c3, c0, 0 @ load domain access register369mcr p15, 0, r4, c2, c0, 0 @ load page table pointer370b __turn_mmu_on371ENDPROC(__enable_mmu)372373/*374* Enable the MMU. This completely changes the structure of the visible375* memory space. You will not be able to trace execution through this.376* If you have an enquiry about this, *please* check the linux-arm-kernel377* mailing list archives BEFORE sending another post to the list.378*379* r0 = cp#15 control register380* r1 = machine ID381* r2 = atags or dtb pointer382* r9 = processor ID383* r13 = *virtual* address to jump to upon completion384*385* other registers depend on the function called upon completion386*/387.align 5388__turn_mmu_on:389mov r0, r0390mcr p15, 0, r0, c1, c0, 0 @ write control reg391mrc p15, 0, r3, c0, c0, 0 @ read id reg392mov r3, r3393mov r3, r13394mov pc, r3395__enable_mmu_end:396ENDPROC(__turn_mmu_on)397398399#ifdef CONFIG_SMP_ON_UP400__INIT401__fixup_smp:402and r3, r9, #0x000f0000 @ architecture version403teq r3, #0x000f0000 @ CPU ID supported?404bne __fixup_smp_on_up @ no, assume UP405406bic r3, r9, #0x00ff0000407bic r3, r3, #0x0000000f @ mask 0xff00fff0408mov r4, #0x41000000409orr r4, r4, #0x0000b000410orr r4, r4, #0x00000020 @ val 0x4100b020411teq r3, r4 @ ARM 11MPCore?412moveq pc, lr @ yes, assume SMP413414mrc p15, 0, r0, c0, c0, 5 @ read MPIDR415and r0, r0, #0xc0000000 @ multiprocessing extensions and416teq r0, #0x80000000 @ not part of a uniprocessor system?417moveq pc, lr @ yes, assume SMP418419__fixup_smp_on_up:420adr r0, 1f421ldmia r0, {r3 - r5}422sub r3, r0, r3423add r4, r4, r3424add r5, r5, r3425b __do_fixup_smp_on_up426ENDPROC(__fixup_smp)427428.align4291: .word .430.word __smpalt_begin431.word __smpalt_end432433.pushsection .data434.globl smp_on_up435smp_on_up:436ALT_SMP(.long 1)437ALT_UP(.long 0)438.popsection439#endif440441.text442__do_fixup_smp_on_up:443cmp r4, r5444movhs pc, lr445ldmia r4!, {r0, r6}446ARM( str r6, [r0, r3] )447THUMB( add r0, r0, r3 )448#ifdef __ARMEB__449THUMB( mov r6, r6, ror #16 ) @ Convert word order for big-endian.450#endif451THUMB( strh r6, [r0], #2 ) @ For Thumb-2, store as two halfwords452THUMB( mov r6, r6, lsr #16 ) @ to be robust against misaligned r3.453THUMB( strh r6, [r0] )454b __do_fixup_smp_on_up455ENDPROC(__do_fixup_smp_on_up)456457ENTRY(fixup_smp)458stmfd sp!, {r4 - r6, lr}459mov r4, r0460add r5, r0, r1461mov r3, #0462bl __do_fixup_smp_on_up463ldmfd sp!, {r4 - r6, pc}464ENDPROC(fixup_smp)465466#ifdef CONFIG_ARM_PATCH_PHYS_VIRT467468/* __fixup_pv_table - patch the stub instructions with the delta between469* PHYS_OFFSET and PAGE_OFFSET, which is assumed to be 16MiB aligned and470* can be expressed by an immediate shifter operand. The stub instruction471* has a form of '(add|sub) rd, rn, #imm'.472*/473__HEAD474__fixup_pv_table:475adr r0, 1f476ldmia r0, {r3-r5, r7}477sub r3, r0, r3 @ PHYS_OFFSET - PAGE_OFFSET478add r4, r4, r3 @ adjust table start address479add r5, r5, r3 @ adjust table end address480add r7, r7, r3 @ adjust __pv_phys_offset address481str r8, [r7] @ save computed PHYS_OFFSET to __pv_phys_offset482#ifndef CONFIG_ARM_PATCH_PHYS_VIRT_16BIT483mov r6, r3, lsr #24 @ constant for add/sub instructions484teq r3, r6, lsl #24 @ must be 16MiB aligned485#else486mov r6, r3, lsr #16 @ constant for add/sub instructions487teq r3, r6, lsl #16 @ must be 64kiB aligned488#endif489THUMB( it ne @ cross section branch )490bne __error491str r6, [r7, #4] @ save to __pv_offset492b __fixup_a_pv_table493ENDPROC(__fixup_pv_table)494495.align4961: .long .497.long __pv_table_begin498.long __pv_table_end4992: .long __pv_phys_offset500501.text502__fixup_a_pv_table:503#ifdef CONFIG_THUMB2_KERNEL504#ifdef CONFIG_ARM_PATCH_PHYS_VIRT_16BIT505lsls r0, r6, #24506lsr r6, #8507beq 1f508clz r7, r0509lsr r0, #24510lsl r0, r7511bic r0, 0x0080512lsrs r7, #1513orrcs r0, #0x0080514orr r0, r0, r7, lsl #12515#endif5161: lsls r6, #24517beq 4f518clz r7, r6519lsr r6, #24520lsl r6, r7521bic r6, #0x0080522lsrs r7, #1523orrcs r6, #0x0080524orr r6, r6, r7, lsl #12525orr r6, #0x4000526b 4f5272: @ at this point the C flag is always clear528add r7, r3529#ifdef CONFIG_ARM_PATCH_PHYS_VIRT_16BIT530ldrh ip, [r7]531tst ip, 0x0400 @ the i bit tells us LS or MS byte532beq 3f533cmp r0, #0 @ set C flag, and ...534biceq ip, 0x0400 @ immediate zero value has a special encoding535streqh ip, [r7] @ that requires the i bit cleared536#endif5373: ldrh ip, [r7, #2]538and ip, 0x8f00539orrcc ip, r6 @ mask in offset bits 31-24540orrcs ip, r0 @ mask in offset bits 23-16541strh ip, [r7, #2]5424: cmp r4, r5543ldrcc r7, [r4], #4 @ use branch for delay slot544bcc 2b545bx lr546#else547#ifdef CONFIG_ARM_PATCH_PHYS_VIRT_16BIT548and r0, r6, #255 @ offset bits 23-16549mov r6, r6, lsr #8 @ offset bits 31-24550#else551mov r0, #0 @ just in case...552#endif553b 3f5542: ldr ip, [r7, r3]555bic ip, ip, #0x000000ff556tst ip, #0x400 @ rotate shift tells us LS or MS byte557orrne ip, ip, r6 @ mask in offset bits 31-24558orreq ip, ip, r0 @ mask in offset bits 23-16559str ip, [r7, r3]5603: cmp r4, r5561ldrcc r7, [r4], #4 @ use branch for delay slot562bcc 2b563mov pc, lr564#endif565ENDPROC(__fixup_a_pv_table)566567ENTRY(fixup_pv_table)568stmfd sp!, {r4 - r7, lr}569ldr r2, 2f @ get address of __pv_phys_offset570mov r3, #0 @ no offset571mov r4, r0 @ r0 = table start572add r5, r0, r1 @ r1 = table size573ldr r6, [r2, #4] @ get __pv_offset574bl __fixup_a_pv_table575ldmfd sp!, {r4 - r7, pc}576ENDPROC(fixup_pv_table)577578.align5792: .long __pv_phys_offset580581.data582.globl __pv_phys_offset583.type __pv_phys_offset, %object584__pv_phys_offset:585.long 0586.size __pv_phys_offset, . - __pv_phys_offset587__pv_offset:588.long 0589#endif590591#include "head-common.S"592593594