/** linux/arch/arm/kernel/head.S** Copyright (C) 1994-2002 Russell King* Copyright (c) 2003 ARM Limited* All Rights Reserved** This program is free software; you can redistribute it and/or modify* it under the terms of the GNU General Public License version 2 as* published by the Free Software Foundation.** Kernel startup code for all 32-bit CPUs*/#include <linux/linkage.h>#include <linux/init.h>#include <asm/assembler.h>#include <asm/cp15.h>#include <asm/domain.h>#include <asm/ptrace.h>#include <asm/asm-offsets.h>#include <asm/memory.h>#include <asm/thread_info.h>#include <asm/pgtable.h>#if defined(CONFIG_DEBUG_LL) && !defined(CONFIG_DEBUG_SEMIHOSTING)#include CONFIG_DEBUG_LL_INCLUDE#endif/** swapper_pg_dir is the virtual address of the initial page table.* We place the page tables 16K below KERNEL_RAM_VADDR. Therefore, we must* make sure that KERNEL_RAM_VADDR is correctly set. Currently, we expect* the least significant 16 bits to be 0x8000, but we could probably* relax this restriction to KERNEL_RAM_VADDR >= PAGE_OFFSET + 0x4000.*/#define KERNEL_RAM_VADDR (PAGE_OFFSET + TEXT_OFFSET)#if (KERNEL_RAM_VADDR & 0xffff) != 0x8000#error KERNEL_RAM_VADDR must start at 0xXXXX8000#endif#ifdef CONFIG_ARM_LPAE/* LPAE requires an additional page for the PGD */#define PG_DIR_SIZE 0x5000#define PMD_ORDER 3#else#define PG_DIR_SIZE 0x4000#define PMD_ORDER 2#endif.globl swapper_pg_dir.equ swapper_pg_dir, KERNEL_RAM_VADDR - PG_DIR_SIZE.macro pgtbl, rd, physadd \rd, \phys, #TEXT_OFFSETsub \rd, \rd, #PG_DIR_SIZE.endm/** Kernel startup entry point.* ---------------------------** This is normally called from the decompressor code. The requirements* are: MMU = off, D-cache = off, I-cache = dont care, r0 = 0,* r1 = machine nr, r2 = atags or dtb pointer.** This code is mostly position independent, so if you link the kernel at* 0xc0008000, you call this at __pa(0xc0008000).** See linux/arch/arm/tools/mach-types for the complete list of machine* numbers for r1.** We're trying to keep crap to a minimum; DO NOT add any machine specific* crap here - that's what the boot loader (or in extreme, well justified* circumstances, zImage) is for.*/.arm__HEADENTRY(stext)ARM_BE8(setend be ) @ ensure we are in BE8 modeTHUMB( adr r9, BSYM(1f) ) @ Kernel is always entered in ARM.THUMB( bx r9 ) @ If this is a Thumb-2 kernel,THUMB( .thumb ) @ switch to Thumb now.THUMB(1: )#ifdef CONFIG_ARM_VIRT_EXTbl __hyp_stub_install#endif@ ensure svc mode and all interrupts maskedsafe_svcmode_maskall r9mrc p15, 0, r9, c0, c0 @ get processor idbl __lookup_processor_type @ r5=procinfo r9=cpuidmovs r10, r5 @ invalid processor (r5=0)?THUMB( it eq ) @ force fixup-able long branch encodingbeq __error_p @ yes, error 'p'#ifdef CONFIG_ARM_LPAEmrc p15, 0, r3, c0, c1, 4 @ read ID_MMFR0and r3, r3, #0xf @ extract VMSA supportcmp r3, #5 @ long-descriptor translation table format?THUMB( it lo ) @ force fixup-able long branch encodingblo __error_lpae @ only classic page table format#endif#ifndef CONFIG_XIP_KERNELadr r3, 2fldmia r3, {r4, r8}sub r4, r3, r4 @ (PHYS_OFFSET - PAGE_OFFSET)add r8, r8, r4 @ PHYS_OFFSET#elseldr r8, =PLAT_PHYS_OFFSET @ always constant in this case#endif/** r1 = machine no, r2 = atags or dtb,* r8 = phys_offset, r9 = cpuid, r10 = procinfo*/bl __vet_atags#ifdef CONFIG_SMP_ON_UPbl __fixup_smp#endif#ifdef CONFIG_ARM_PATCH_PHYS_VIRTbl __fixup_pv_table#endifbl __create_page_tables/** The following calls CPU specific code in a position independent* manner. See arch/arm/mm/proc-*.S for details. r10 = base of* xxx_proc_info structure selected by __lookup_processor_type* above. On return, the CPU will be ready for the MMU to be* turned on, and r0 will hold the CPU control register value.*/ldr r13, =__mmap_switched @ address to jump to after@ mmu has been enabledadr lr, BSYM(1f) @ return (PIC) addressmov r8, r4 @ set TTBR1 to swapper_pg_dirARM( add pc, r10, #PROCINFO_INITFUNC )THUMB( add r12, r10, #PROCINFO_INITFUNC )THUMB( ret r12 )1: b __enable_mmuENDPROC(stext).ltorg#ifndef CONFIG_XIP_KERNEL2: .long ..long PAGE_OFFSET#endif/** Setup the initial page tables. We only setup the barest* amount which are required to get the kernel running, which* generally means mapping in the kernel code.** r8 = phys_offset, r9 = cpuid, r10 = procinfo** Returns:* r0, r3, r5-r7 corrupted* r4 = page table (see ARCH_PGD_SHIFT in asm/memory.h)*/__create_page_tables:pgtbl r4, r8 @ page table address/** Clear the swapper page table*/mov r0, r4mov r3, #0add r6, r0, #PG_DIR_SIZE1: str r3, [r0], #4str r3, [r0], #4str r3, [r0], #4str r3, [r0], #4teq r0, r6bne 1b#ifdef CONFIG_ARM_LPAE/** Build the PGD table (first level) to point to the PMD table. A PGD* entry is 64-bit wide.*/mov r0, r4add r3, r4, #0x1000 @ first PMD table addressorr r3, r3, #3 @ PGD block typemov r6, #4 @ PTRS_PER_PGDmov r7, #1 << (55 - 32) @ L_PGD_SWAPPER1:#ifdef CONFIG_CPU_ENDIAN_BE8str r7, [r0], #4 @ set top PGD entry bitsstr r3, [r0], #4 @ set bottom PGD entry bits#elsestr r3, [r0], #4 @ set bottom PGD entry bitsstr r7, [r0], #4 @ set top PGD entry bits#endifadd r3, r3, #0x1000 @ next PMD tablesubs r6, r6, #1bne 1badd r4, r4, #0x1000 @ point to the PMD tables#ifdef CONFIG_CPU_ENDIAN_BE8add r4, r4, #4 @ we only write the bottom word#endif#endifldr r7, [r10, #PROCINFO_MM_MMUFLAGS] @ mm_mmuflags/** Create identity mapping to cater for __enable_mmu.* This identity mapping will be removed by paging_init().*/adr r0, __turn_mmu_on_locldmia r0, {r3, r5, r6}sub r0, r0, r3 @ virt->phys offsetadd r5, r5, r0 @ phys __turn_mmu_onadd r6, r6, r0 @ phys __turn_mmu_on_endmov r5, r5, lsr #SECTION_SHIFTmov r6, r6, lsr #SECTION_SHIFT1: orr r3, r7, r5, lsl #SECTION_SHIFT @ flags + kernel basestr r3, [r4, r5, lsl #PMD_ORDER] @ identity mappingcmp r5, r6addlo r5, r5, #1 @ next sectionblo 1b/** Map our RAM from the start to the end of the kernel .bss section.*/add r0, r4, #PAGE_OFFSET >> (SECTION_SHIFT - PMD_ORDER)ldr r6, =(_end - 1)orr r3, r8, r7add r6, r4, r6, lsr #(SECTION_SHIFT - PMD_ORDER)1: str r3, [r0], #1 << PMD_ORDERadd r3, r3, #1 << SECTION_SHIFTcmp r0, r6bls 1b#ifdef CONFIG_XIP_KERNEL/** Map the kernel image separately as it is not located in RAM.*/#define XIP_START XIP_VIRT_ADDR(CONFIG_XIP_PHYS_ADDR)mov r3, pcmov r3, r3, lsr #SECTION_SHIFTorr r3, r7, r3, lsl #SECTION_SHIFTadd r0, r4, #(XIP_START & 0xff000000) >> (SECTION_SHIFT - PMD_ORDER)str r3, [r0, #((XIP_START & 0x00f00000) >> SECTION_SHIFT) << PMD_ORDER]!ldr r6, =(_edata_loc - 1)add r0, r0, #1 << PMD_ORDERadd r6, r4, r6, lsr #(SECTION_SHIFT - PMD_ORDER)1: cmp r0, r6add r3, r3, #1 << SECTION_SHIFTstrls r3, [r0], #1 << PMD_ORDERbls 1b#endif/** Then map boot params address in r2 if specified.* We map 2 sections in case the ATAGs/DTB crosses a section boundary.*/mov r0, r2, lsr #SECTION_SHIFTmovs r0, r0, lsl #SECTION_SHIFTsubne r3, r0, r8addne r3, r3, #PAGE_OFFSETaddne r3, r4, r3, lsr #(SECTION_SHIFT - PMD_ORDER)orrne r6, r7, r0strne r6, [r3], #1 << PMD_ORDERaddne r6, r6, #1 << SECTION_SHIFTstrne r6, [r3]#if defined(CONFIG_ARM_LPAE) && defined(CONFIG_CPU_ENDIAN_BE8)sub r4, r4, #4 @ Fixup page table pointer@ for 64-bit descriptors#endif#ifdef CONFIG_DEBUG_LL#if !defined(CONFIG_DEBUG_ICEDCC) && !defined(CONFIG_DEBUG_SEMIHOSTING)/** Map in IO space for serial debugging.* This allows debug messages to be output* via a serial console before paging_init.*/addruart r7, r3, r0mov r3, r3, lsr #SECTION_SHIFTmov r3, r3, lsl #PMD_ORDERadd r0, r4, r3mov r3, r7, lsr #SECTION_SHIFTldr r7, [r10, #PROCINFO_IO_MMUFLAGS] @ io_mmuflagsorr r3, r7, r3, lsl #SECTION_SHIFT#ifdef CONFIG_ARM_LPAEmov r7, #1 << (54 - 32) @ XN#ifdef CONFIG_CPU_ENDIAN_BE8str r7, [r0], #4str r3, [r0], #4#elsestr r3, [r0], #4str r7, [r0], #4#endif#elseorr r3, r3, #PMD_SECT_XNstr r3, [r0], #4#endif#else /* CONFIG_DEBUG_ICEDCC || CONFIG_DEBUG_SEMIHOSTING *//* we don't need any serial debugging mappings */ldr r7, [r10, #PROCINFO_IO_MMUFLAGS] @ io_mmuflags#endif#if defined(CONFIG_ARCH_NETWINDER) || defined(CONFIG_ARCH_CATS)/** If we're using the NetWinder or CATS, we also need to map* in the 16550-type serial port for the debug messages*/add r0, r4, #0xff000000 >> (SECTION_SHIFT - PMD_ORDER)orr r3, r7, #0x7c000000str r3, [r0]#endif#ifdef CONFIG_ARCH_RPC/** Map in screen at 0x02000000 & SCREEN2_BASE* Similar reasons here - for debug. This is* only for Acorn RiscPC architectures.*/add r0, r4, #0x02000000 >> (SECTION_SHIFT - PMD_ORDER)orr r3, r7, #0x02000000str r3, [r0]add r0, r4, #0xd8000000 >> (SECTION_SHIFT - PMD_ORDER)str r3, [r0]#endif#endif#ifdef CONFIG_ARM_LPAEsub r4, r4, #0x1000 @ point to the PGD tablemov r4, r4, lsr #ARCH_PGD_SHIFT#endifret lrENDPROC(__create_page_tables).ltorg.align__turn_mmu_on_loc:.long ..long __turn_mmu_on.long __turn_mmu_on_end#if defined(CONFIG_SMP).textENTRY(secondary_startup_arm).armTHUMB( adr r9, BSYM(1f) ) @ Kernel is entered in ARM.THUMB( bx r9 ) @ If this is a Thumb-2 kernel,THUMB( .thumb ) @ switch to Thumb now.THUMB(1: )ENTRY(secondary_startup)/** Common entry point for secondary CPUs.** Ensure that we're in SVC mode, and IRQs are disabled. Lookup* the processor type - there is no need to check the machine type* as it has already been validated by the primary processor.*/ARM_BE8(setend be) @ ensure we are in BE8 mode#ifdef CONFIG_ARM_VIRT_EXTbl __hyp_stub_install_secondary#endifsafe_svcmode_maskall r9mrc p15, 0, r9, c0, c0 @ get processor idbl __lookup_processor_typemovs r10, r5 @ invalid processor?moveq r0, #'p' @ yes, error 'p'THUMB( it eq ) @ force fixup-able long branch encodingbeq __error_p/** Use the page tables supplied from __cpu_up.*/adr r4, __secondary_dataldmia r4, {r5, r7, r12} @ address to jump to aftersub lr, r4, r5 @ mmu has been enabledldr r4, [r7, lr] @ get secondary_data.pgdiradd r7, r7, #4ldr r8, [r7, lr] @ get secondary_data.swapper_pg_diradr lr, BSYM(__enable_mmu) @ return addressmov r13, r12 @ __secondary_switched addressARM( add pc, r10, #PROCINFO_INITFUNC ) @ initialise processor@ (return control reg)THUMB( add r12, r10, #PROCINFO_INITFUNC )THUMB( ret r12 )ENDPROC(secondary_startup)ENDPROC(secondary_startup_arm)/** r6 = &secondary_data*/ENTRY(__secondary_switched)ldr sp, [r7, #4] @ get secondary_data.stackmov fp, #0b secondary_start_kernelENDPROC(__secondary_switched).align.type __secondary_data, %object__secondary_data:.long ..long secondary_data.long __secondary_switched#endif /* defined(CONFIG_SMP) *//** Setup common bits before finally enabling the MMU. Essentially* this is just loading the page table pointer and domain access* registers.** r0 = cp#15 control register* r1 = machine ID* r2 = atags or dtb pointer* r4 = page table (see ARCH_PGD_SHIFT in asm/memory.h)* r9 = processor ID* r13 = *virtual* address to jump to upon completion*/__enable_mmu:#if defined(CONFIG_ALIGNMENT_TRAP) && __LINUX_ARM_ARCH__ < 6orr r0, r0, #CR_A#elsebic r0, r0, #CR_A#endif#ifdef CONFIG_CPU_DCACHE_DISABLEbic r0, r0, #CR_C#endif#ifdef CONFIG_CPU_BPREDICT_DISABLEbic r0, r0, #CR_Z#endif#ifdef CONFIG_CPU_ICACHE_DISABLEbic r0, r0, #CR_I#endif#ifndef CONFIG_ARM_LPAEmov r5, #(domain_val(DOMAIN_USER, DOMAIN_MANAGER) | \domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | \domain_val(DOMAIN_TABLE, DOMAIN_MANAGER) | \domain_val(DOMAIN_IO, DOMAIN_CLIENT))mcr p15, 0, r5, c3, c0, 0 @ load domain access registermcr p15, 0, r4, c2, c0, 0 @ load page table pointer#endifb __turn_mmu_onENDPROC(__enable_mmu)/** Enable the MMU. This completely changes the structure of the visible* memory space. You will not be able to trace execution through this.* If you have an enquiry about this, *please* check the linux-arm-kernel* mailing list archives BEFORE sending another post to the list.** r0 = cp#15 control register* r1 = machine ID* r2 = atags or dtb pointer* r9 = processor ID* r13 = *virtual* address to jump to upon completion** other registers depend on the function called upon completion*/.align 5.pushsection .idmap.text, "ax"ENTRY(__turn_mmu_on)mov r0, r0instr_syncmcr p15, 0, r0, c1, c0, 0 @ write control regmrc p15, 0, r3, c0, c0, 0 @ read id reginstr_syncmov r3, r3mov r3, r13ret r3__turn_mmu_on_end:ENDPROC(__turn_mmu_on).popsection#ifdef CONFIG_SMP_ON_UP__HEAD__fixup_smp:and r3, r9, #0x000f0000 @ architecture versionteq r3, #0x000f0000 @ CPU ID supported?bne __fixup_smp_on_up @ no, assume UPbic r3, r9, #0x00ff0000bic r3, r3, #0x0000000f @ mask 0xff00fff0mov r4, #0x41000000orr r4, r4, #0x0000b000orr r4, r4, #0x00000020 @ val 0x4100b020teq r3, r4 @ ARM 11MPCore?reteq lr @ yes, assume SMPmrc p15, 0, r0, c0, c0, 5 @ read MPIDRand r0, r0, #0xc0000000 @ multiprocessing extensions andteq r0, #0x80000000 @ not part of a uniprocessor system?bne __fixup_smp_on_up @ no, assume UP@ Core indicates it is SMP. Check for Aegis SOC where a single@ Cortex-A9 CPU is present but SMP operations fault.mov r4, #0x41000000orr r4, r4, #0x0000c000orr r4, r4, #0x00000090teq r3, r4 @ Check for ARM Cortex-A9retne lr @ Not ARM Cortex-A9,@ If a future SoC *does* use 0x0 as the PERIPH_BASE, then the@ below address check will need to be #ifdef'd or equivalent@ for the Aegis platform.mrc p15, 4, r0, c15, c0 @ get SCU base addressteq r0, #0x0 @ '0' on actual UP A9 hardwarebeq __fixup_smp_on_up @ So its an A9 UPldr r0, [r0, #4] @ read SCU ConfigARM_BE8(rev r0, r0) @ byteswap if big endianand r0, r0, #0x3 @ number of CPUsteq r0, #0x0 @ is 1?retne lr__fixup_smp_on_up:adr r0, 1fldmia r0, {r3 - r5}sub r3, r0, r3add r4, r4, r3add r5, r5, r3b __do_fixup_smp_on_upENDPROC(__fixup_smp).align1: .word ..word __smpalt_begin.word __smpalt_end.pushsection .data.globl smp_on_upsmp_on_up:ALT_SMP(.long 1)ALT_UP(.long 0).popsection#endif.text__do_fixup_smp_on_up:cmp r4, r5reths lrldmia r4!, {r0, r6}ARM( str r6, [r0, r3] )THUMB( add r0, r0, r3 )#ifdef __ARMEB__THUMB( mov r6, r6, ror #16 ) @ Convert word order for big-endian.#endifTHUMB( strh r6, [r0], #2 ) @ For Thumb-2, store as two halfwordsTHUMB( mov r6, r6, lsr #16 ) @ to be robust against misaligned r3.THUMB( strh r6, [r0] )b __do_fixup_smp_on_upENDPROC(__do_fixup_smp_on_up)ENTRY(fixup_smp)stmfd sp!, {r4 - r6, lr}mov r4, r0add r5, r0, r1mov r3, #0bl __do_fixup_smp_on_upldmfd sp!, {r4 - r6, pc}ENDPROC(fixup_smp)#ifdef __ARMEB__#define LOW_OFFSET 0x4#define HIGH_OFFSET 0x0#else#define LOW_OFFSET 0x0#define HIGH_OFFSET 0x4#endif#ifdef CONFIG_ARM_PATCH_PHYS_VIRT/* __fixup_pv_table - patch the stub instructions with the delta between* PHYS_OFFSET and PAGE_OFFSET, which is assumed to be 16MiB aligned and* can be expressed by an immediate shifter operand. The stub instruction* has a form of '(add|sub) rd, rn, #imm'.*/__HEAD__fixup_pv_table:adr r0, 1fldmia r0, {r3-r7}mvn ip, #0subs r3, r0, r3 @ PHYS_OFFSET - PAGE_OFFSETadd r4, r4, r3 @ adjust table start addressadd r5, r5, r3 @ adjust table end addressadd r6, r6, r3 @ adjust __pv_phys_pfn_offset addressadd r7, r7, r3 @ adjust __pv_offset addressmov r0, r8, lsr #PAGE_SHIFT @ convert to PFNstr r0, [r6] @ save computed PHYS_OFFSET to __pv_phys_pfn_offsetstrcc ip, [r7, #HIGH_OFFSET] @ save to __pv_offset high bitsmov r6, r3, lsr #24 @ constant for add/sub instructionsteq r3, r6, lsl #24 @ must be 16MiB alignedTHUMB( it ne @ cross section branch )bne __errorstr r3, [r7, #LOW_OFFSET] @ save to __pv_offset low bitsb __fixup_a_pv_tableENDPROC(__fixup_pv_table).align1: .long ..long __pv_table_begin.long __pv_table_end2: .long __pv_phys_pfn_offset.long __pv_offset.text__fixup_a_pv_table:adr r0, 3fldr r6, [r0]add r6, r6, r3ldr r0, [r6, #HIGH_OFFSET] @ pv_offset high wordldr r6, [r6, #LOW_OFFSET] @ pv_offset low wordmov r6, r6, lsr #24cmn r0, #1#ifdef CONFIG_THUMB2_KERNELmoveq r0, #0x200000 @ set bit 21, mov to mvn instructionlsls r6, #24beq 2fclz r7, r6lsr r6, #24lsl r6, r7bic r6, #0x0080lsrs r7, #1orrcs r6, #0x0080orr r6, r6, r7, lsl #12orr r6, #0x4000b 2f1: add r7, r3ldrh ip, [r7, #2]ARM_BE8(rev16 ip, ip)tst ip, #0x4000and ip, #0x8f00orrne ip, r6 @ mask in offset bits 31-24orreq ip, r0 @ mask in offset bits 7-0ARM_BE8(rev16 ip, ip)strh ip, [r7, #2]bne 2fldrh ip, [r7]ARM_BE8(rev16 ip, ip)bic ip, #0x20orr ip, ip, r0, lsr #16ARM_BE8(rev16 ip, ip)strh ip, [r7]2: cmp r4, r5ldrcc r7, [r4], #4 @ use branch for delay slotbcc 1bbx lr#else#ifdef CONFIG_CPU_ENDIAN_BE8moveq r0, #0x00004000 @ set bit 22, mov to mvn instruction#elsemoveq r0, #0x400000 @ set bit 22, mov to mvn instruction#endifb 2f1: ldr ip, [r7, r3]#ifdef CONFIG_CPU_ENDIAN_BE8@ in BE8, we load data in BE, but instructions still in LEbic ip, ip, #0xff000000tst ip, #0x000f0000 @ check the rotation fieldorrne ip, ip, r6, lsl #24 @ mask in offset bits 31-24biceq ip, ip, #0x00004000 @ clear bit 22orreq ip, ip, r0 @ mask in offset bits 7-0#elsebic ip, ip, #0x000000fftst ip, #0xf00 @ check the rotation fieldorrne ip, ip, r6 @ mask in offset bits 31-24biceq ip, ip, #0x400000 @ clear bit 22orreq ip, ip, r0 @ mask in offset bits 7-0#endifstr ip, [r7, r3]2: cmp r4, r5ldrcc r7, [r4], #4 @ use branch for delay slotbcc 1bret lr#endifENDPROC(__fixup_a_pv_table).align3: .long __pv_offsetENTRY(fixup_pv_table)stmfd sp!, {r4 - r7, lr}mov r3, #0 @ no offsetmov r4, r0 @ r0 = table startadd r5, r0, r1 @ r1 = table sizebl __fixup_a_pv_tableldmfd sp!, {r4 - r7, pc}ENDPROC(fixup_pv_table).data.globl __pv_phys_pfn_offset.type __pv_phys_pfn_offset, %object__pv_phys_pfn_offset:.word 0.size __pv_phys_pfn_offset, . -__pv_phys_pfn_offset.globl __pv_offset.type __pv_offset, %object__pv_offset:.quad 0.size __pv_offset, . -__pv_offset#endif#include "head-common.S"
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。