/*
* 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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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)
// PAGE_OFFSET = 0xC0000000
// TEXT_OFFSET = 0x00008000
// KERNEL_RAM_VADDR = 0xC0008000
#if (KERNEL_RAM_VADDR & 0xffff) != 0x8000
#error KERNEL_RAM_VADDR must start at 0xXXXX8000
#endif
#ifdef CONFIG_ARM_LPAE // 未定义,所以PG_DIR_SIZE = 0x4000,PMD_ORDER = 2
// PG_DIR_SIZE代表一级也表的大小,2^12个entry,每个entry是4字节,即需要16K(0x4000)空间
// PMD_ORDER代表每个entry是4字节(2^2=4)
/* 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
// swapper_pg_dir = 0x50004000
.macro pgtbl, rd, phys
add \rd, \phys, #TEXT_OFFSET // rd = phys + 0x8000
sub \rd, \rd, #PG_DIR_SIZE // rd = rd - 0x4000
.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
__HEAD
ENTRY(stext)
ARM_BE8(setend be) @ ensure we are in BE8 mode
THUMB( 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_EXT
bl __hyp_stub_install
#endif
@ ensure svc mode and all interrupts masked
safe_svcmode_maskall r9
mrc p15, 0, r9, c0, c0 @ get processor id
// 获取处理器id,我的是r9 = 0x410fb766
bl __lookup_processor_type @ r5=procinfo r9=cpuid
// r5 = 0x50619e38
movs r10, r5 @ invalid processor (r5=0)?
// r10 = 0x50619e38
THUMB( it eq ) @ force fixup-able long branch encoding
beq __error_p @ yes, error 'p'
#ifdef CONFIG_ARM_LPAE // 未定义,跳过
mrc p15, 0, r3, c0, c1, 4 @ read ID_MMFR0
and r3, r3, #0xf @ extract VMSA support
cmp r3, #5 @ long-descriptor translation table format?
THUMB( it lo ) @ force fixup-able long branch encoding
blo __error_lpae @ only classic page table format
#endif
#ifndef CONFIG_XIP_KERNEL // 未定义,不跳过
adr r3, 2f // r3 = 0xc000807c
ldmia r3, {r4, r8} // r4 = 0xc000807c, r8 = 0xc0000000
sub r4, r3, r4 @ (PHYS_OFFSET - PAGE_OFFSET)
// PHYS_OFFSET = 0x50000000
// PAGE_OFFSET = 0xC0000000
// r4 = 0x90000000
add r8, r8, r4 @ PHYS_OFFSET
// r8 = 0x50000000
#else // 跳过
ldr 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_UP // 未定义,跳过
bl __fixup_smp
#endif
#ifdef CONFIG_ARM_PATCH_PHYS_VIRT
bl __fixup_pv_table
#endif
bl __create_page_tables // 简单初始化MMU页表项,后面还会继续初始化
/*
* 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 enabled
adr lr, BSYM(1f) @ return (PIC) address
mov r8, r4 @ set TTBR1 to swapper_pg_dir
ldr r12, [r10, #PROCINFO_INITFUNC]
add r12, r12, r10
ret r12
1: b __enable_mmu
ENDPROC(stext)
.ltorg
#ifndef CONFIG_XIP_KERNEL
2: .long .
.long PAGE_OFFSET
#endif
包含指令地址,用于与上文对比
c0008000 <stext>:
c0008000: e10f9000 mrs r9, CPSR
c0008004: e229901a eor r9, r9, #26
c0008008: e319001f tst r9, #31
c000800c: e3c9901f bic r9, r9, #31
c0008010: e38990d3 orr r9, r9, #211 ; 0xd3
c0008014: 1a000004 bne c000802c <stext+0x2c>
c0008018: e3899c01 orr r9, r9, #256 ; 0x100
c000801c: e28fe00c add lr, pc, #12
c0008020: e16ff009 msr SPSR_fsxc, r9
c0008024: e12ef30e .word 0xe12ef30e
c0008028: e160006e .word 0xe160006e
c000802c: e121f009 msr CPSR_c, r9
c0008030: ee109f10 mrc 15, 0, r9, cr0, cr0, {0}
c0008034: eb00051b bl c00094a8 <__lookup_processor_type>
c0008038: e1b0a005 movs sl, r5
c000803c: 0a00053e beq c000953c <__error_p>
c0008040: e28f3034 add r3, pc, #52 ; 0x34
c0008044: e8930110 ldm r3, {r4, r8}
c0008048: e0434004 sub r4, r3, r4
c000804c: e0888004 add r8, r8, r4
c0008050: eb00005f bl c00081d4 <__vet_atags>
c0008054: eb000049 bl c0008180 <__fixup_pv_table>
c0008058: eb000009 bl c0008084 <__create_page_tables>
c000805c: e59fd014 ldr sp, [pc, #20] ; c0008078 <stext+0x78>
c0008060: e28fe00c add lr, pc, #12
c0008064: e1a08004 mov r8, r4
c0008068: e59ac010 ldr ip, [sl, #16]
c000806c: e08cc00a add ip, ip, sl
c0008070: e1a0f00c mov pc, ip
c0008074: ea00003c b c000816c <__enable_mmu>
c0008078: c058a2e0 .word 0xc058a2e0
c000807c: c000807c .word 0xc000807c
c0008080: c0000000 .word 0xc0000000
/*
* 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
// pgtbl是汇编宏,定义在第一节
// r4 = 0x50004000
/*
* Clear the swapper page table
* 0x50004000~0x50007fff物理地址中的数据清零
*/
mov r0, r4 // r0 = 0x50004000
mov r3, #0 // r3 = 0
add r6, r0, #PG_DIR_SIZE // r6 = 0x50008000
1: str r3, [r0], #4 // *(0x50004000) = 0;*(0x50004010) = 0;...
str r3, [r0], #4 // *(0x50004004) = 0;*(0x50004014) = 0;...
str r3, [r0], #4 // *(0x50004008) = 0;*(0x50004018) = 0;...
str r3, [r0], #4 // *(0x5000400c) = 0;*(0x5000401c) = 0;...
teq r0, r6 // if r0 != r6
bne 1b // goto 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, r4
add r3, r4, #0x1000 @ first PMD table address
orr r3, r3, #3 @ PGD block type
mov r6, #4 @ PTRS_PER_PGD
mov r7, #1 << (55 - 32) @ L_PGD_SWAPPER
1:
#ifdef CONFIG_CPU_ENDIAN_BE8
str r7, [r0], #4 @ set top PGD entry bits
str r3, [r0], #4 @ set bottom PGD entry bits
#else
str r3, [r0], #4 @ set bottom PGD entry bits
str r7, [r0], #4 @ set top PGD entry bits
#endif
add r3, r3, #0x1000 @ next PMD table
subs r6, r6, #1
bne 1b
add r4, r4, #0x1000 @ point to the PMD tables
#ifdef CONFIG_CPU_ENDIAN_BE8
add r4, r4, #4 @ we only write the bottom word
#endif
#endif
ldr r7, [r10, #PROCINFO_MM_MMUFLAGS] @ mm_mmuflags // r7 = 0xc0e(mmu低12位标志符)
/*
* Create identity mapping to cater for __enable_mmu.
* This identity mapping will be removed by paging_init().
*/
adr r0, __turn_mmu_on_loc // r0 = 0x50008160
ldmia r0, {r3, r5, r6} // r3 = 0xc0008160
// r5 = 0xc0008220
// r6 = 0xc0008240
sub r0, r0, r3 @ virt->phys offset
// r0 = 0x90000000
add r5, r5, r0 @ phys __turn_mmu_on
// r5 = 0x50008220
add r6, r6, r0 @ phys __turn_mmu_on_end
// r6 = 0x50008240
mov r5, r5, lsr #SECTION_SHIFT // SECTION_SHIFT = 20
// r5 = 0x500
mov r6, r6, lsr #SECTION_SHIFT // r6 = 0x500
1: orr r3, r7, r5, lsl #SECTION_SHIFT @ flags + kernel base
// r3 = 0x50000c0e
str r3, [r4, r5, lsl #PMD_ORDER] @ identity mapping
// *(0x50004000 + 0x1400) = 0x50000c0e
cmp r5, r6 // if r5 < r6
// cmp指令是不保存结果只设置状态寄存器的sub指令
// 此时r5 == r6,下面的addlo(addcc)与blo(bcc)只有r5小于r6时才会执行
addlo r5, r5, #1 @ next section
// r5 = 0x500
blo 1b // 不跳转
/*
* Map our RAM from the start to the end of the kernel .bss section.
* 将0xc000000到.end之间的虚拟内存地址映射到0x50007000到0x50007xxx;我这里是0x5000701a
* (gdb) x/32x 0x50007000
* 0x50007000: 0x50000c0e 0x50100c0e 0x50200c0e 0x50300c0e
* 0x50007010: 0x50400c0e 0x50500c0e 0x50600c0e 0x00000000
* 0x50007020: 0x00000000 0x00000000 0x00000000 0x00000000
*/
add r0, r4, #PAGE_OFFSET >> (SECTION_SHIFT - PMD_ORDER)
// r0 = 0x50004000 + (0xc0000000 >> 18) = 0x50007000
ldr r6, =(_end - 1) // vmlinux.lds.S中,BSS_SECTION后,_end = .;
// 我这里是r6 = 0xc06b15d7
orr r3, r8, r7 // r3 = 0x50000000 | 0xc0e = 0x50000c0e
add r6, r4, r6, lsr #(SECTION_SHIFT - PMD_ORDER)
// r6 = 0x50007xxx;我这里是0x5000701a
1: str r3, [r0], #1 << PMD_ORDER // *(0x50007000) = 0x50000c0e
// r0++
add r3, r3, #1 << SECTION_SHIFT // r3 = 0x50100c0e
cmp r0, r6 // if r0 < r6;这个cmp与前一段的套路完全一样
bls 1b // goto 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, pc
mov r3, r3, lsr #SECTION_SHIFT
orr r3, r7, r3, lsl #SECTION_SHIFT
add 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_ORDER
add r6, r4, r6, lsr #(SECTION_SHIFT - PMD_ORDER)
1: cmp r0, r6
add r3, r3, #1 << SECTION_SHIFT
strls r3, [r0], #1 << PMD_ORDER
bls 1b
#endif
/*
* Then map boot params address in r2 if specified.
* We map 2 sections in case the ATAGs/DTB crosses a section boundary.
* 这一段没看懂,最后设置了页表项0x50007000于0x50007004,不过该页表项前面已经设置过了,内容也没有任何改变
*/
mov r0, r2, lsr #SECTION_SHIFT // r2 = 0x50000100, r0 = 0x500
movs r0, r0, lsl #SECTION_SHIFT // r0 = 0x50000000
subne r3, r0, r8 // r3 = 0
addne r3, r3, #PAGE_OFFSET // r3 = 0xc0000000
addne r3, r4, r3, lsr #(SECTION_SHIFT - PMD_ORDER) // r3 = 0x50007000
orrne r6, r7, r0 // r6 = 0x50000c0e
strne r6, [r3], #1 << PMD_ORDER // *(0x50007000) = 0x50000c0e
addne r6, r6, #1 << SECTION_SHIFT // r6 = 0x50100c0e
strne r6, [r3] // *(0x50007004) = 0x50100c0e
#if defined(CONFIG_ARM_LPAE) && defined(CONFIG_CPU_ENDIAN_BE8) // CONFIG_ARM_LPAE未定义,跳过
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.
* 设置串口控制器物理地址对应的虚拟地址页表项
* (gdb) x/4x 0x50007dc0
* 0x50007dc0: 0x7f000c12 0x00000000 0x00000000 0x00000000
*/
addruart r7, r3, r0 // r7 = 0x7f005000, r3 = 0xf7005000
// addruart是宏定义:
// 将串口控制器的物理地址赋值给r7,该地址由平台体系决定,
// 翻看arm11的datasheet可知地址固定为0x7f005000
// 将串口控制器的虚拟地址赋值给r3,该地址由内核开发者决定
mov r3, r3, lsr #SECTION_SHIFT // r3 = 0xf70
mov r3, r3, lsl #PMD_ORDER // r3 = 0x3dc0
add r0, r4, r3 // 0x50007dc0
mov r3, r7, lsr #SECTION_SHIFT // r3 = 0xf70
ldr r7, [r10, #PROCINFO_IO_MMUFLAGS] @ io_mmuflags // r7 = 0xc12
orr r3, r7, r3, lsl #SECTION_SHIFT // r3 = 0x7f000c12
#ifdef CONFIG_ARM_LPAE // 未定义,跳过
mov r7, #1 << (54 - 32) @ XN
#ifdef CONFIG_CPU_ENDIAN_BE8
str r7, [r0], #4
str r3, [r0], #4
#else
str r3, [r0], #4
str r7, [r0], #4
#endif
#else // 不跳过
orr r3, r3, #PMD_SECT_XN // r3 = 0x7f000c12
str r3, [r0], #4 // *(0x50007dc0) = 0x7f000c12
#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, #0x7c000000
str 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, #0x02000000
str r3, [r0]
add r0, r4, #0xd8000000 >> (SECTION_SHIFT - PMD_ORDER)
str r3, [r0]
#endif
#endif
#ifdef CONFIG_ARM_LPAE // 未定义,跳过
sub r4, r4, #0x1000 @ point to the PGD table
mov r4, r4, lsr #ARCH_PGD_SHIFT
#endif
ret lr // return
ENDPROC(__create_page_tables)
.ltorg
.align
__turn_mmu_on_loc:
.long .
.long __turn_mmu_on
.long __turn_mmu_on_end
包含指令地址,用于与上文对比
c0008084 <__create_page_tables>:
c0008084: e2884902 add r4, r8, #32768 ; 0x8000
c0008088: e2444901 sub r4, r4, #16384 ; 0x4000
c000808c: e1a00004 mov r0, r4
c0008090: e3a03000 mov r3, #0
c0008094: e2806901 add r6, r0, #16384 ; 0x4000
c0008098: e4803004 str r3, [r0], #4
c000809c: e4803004 str r3, [r0], #4
c00080a0: e4803004 str r3, [r0], #4
c00080a4: e4803004 str r3, [r0], #4
c00080a8: e1300006 teq r0, r6
c00080ac: 1afffff9 bne c0008098 <__create_page_tables+0x14>
c00080b0: e59a7008 ldr r7, [sl, #8]
c00080b4: e28f00a4 add r0, pc, #164 ; 0xa4
c00080b8: e8900068 ldm r0, {r3, r5, r6}
c00080bc: e0400003 sub r0, r0, r3
c00080c0: e0855000 add r5, r5, r0
c00080c4: e0866000 add r6, r6, r0
c00080c8: e1a05a25 lsr r5, r5, #20
c00080cc: e1a06a26 lsr r6, r6, #20
c00080d0: e1873a05 orr r3, r7, r5, lsl #20
c00080d4: e7843105 str r3, [r4, r5, lsl #2]
c00080d8: e1550006 cmp r5, r6
c00080dc: 32855001 addcc r5, r5, #1
c00080e0: 3afffffa bcc c00080d0 <__create_page_tables+0x4c>
c00080e4: e2840a03 add r0, r4, #12288 ; 0x3000
c00080e8: e59f6064 ldr r6, [pc, #100] ; c0008154 <__create_page_tables+0xd0>
c00080ec: e1883007 orr r3, r8, r7
c00080f0: e0846926 add r6, r4, r6, lsr #18
c00080f4: e4803004 str r3, [r0], #4
c00080f8: e2833601 add r3, r3, #1048576 ; 0x100000
c00080fc: e1500006 cmp r0, r6
c0008100: 9afffffb bls c00080f4 <__create_page_tables+0x70>
c0008104: e1a00a22 lsr r0, r2, #20
c0008108: e1b00a00 lsls r0, r0, #20
c000810c: 10403008 subne r3, r0, r8
c0008110: 12833103 addne r3, r3, #-1073741824 ; 0xc0000000
c0008114: 10843923 addne r3, r4, r3, lsr #18
c0008118: 11876000 orrne r6, r7, r0
c000811c: 14836004 strne r6, [r3], #4
c0008120: 12866601 addne r6, r6, #1048576 ; 0x100000
c0008124: 15836000 strne r6, [r3]
c0008128: e59f7028 ldr r7, [pc, #40] ; c0008158 <__create_page_tables+0xd4>
c000812c: e59f3028 ldr r3, [pc, #40] ; c000815c <__create_page_tables+0xd8>
c0008130: e1a03a23 lsr r3, r3, #20
c0008134: e1a03103 lsl r3, r3, #2
c0008138: e0840003 add r0, r4, r3
c000813c: e1a03a27 lsr r3, r7, #20
c0008140: e59a700c ldr r7, [sl, #12]
c0008144: e1873a03 orr r3, r7, r3, lsl #20
c0008148: e3833010 orr r3, r3, #16
c000814c: e4803004 str r3, [r0], #4
c0008150: e12fff1e bx lr
c0008154: c06354d7 .word 0xc06354d7
c0008158: 7f005000 .word 0x7f005000
c000815c: f7005000 .word 0xf7005000
c0008160 <__turn_mmu_on_loc>:
c0008160: c0008160 .word 0xc0008160
c0008164: c0008220 .word 0xc0008220
c0008168: c0008240 .word 0xc0008240
#if defined(CONFIG_SMP) // 未定义,跳过;直接跳到__enable_mmu
.text
ENTRY(secondary_startup_arm)
.arm
THUMB( 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_EXT
bl __hyp_stub_install_secondary
#endif
safe_svcmode_maskall r9
mrc p15, 0, r9, c0, c0 @ get processor id
bl __lookup_processor_type
movs r10, r5 @ invalid processor?
moveq r0, #'p' @ yes, error 'p'
THUMB( it eq ) @ force fixup-able long branch encoding
beq __error_p
/*
* Use the page tables supplied from __cpu_up.
*/
adr r4, __secondary_data
ldmia r4, {r5, r7, r12} @ address to jump to after
sub lr, r4, r5 @ mmu has been enabled
ldr r4, [r7, lr] @ get secondary_data.pgdir
add r7, r7, #4
ldr r8, [r7, lr] @ get secondary_data.swapper_pg_dir
adr lr, BSYM(__enable_mmu) @ return address
mov r13, r12 @ __secondary_switched address
ldr r12, [r10, #PROCINFO_INITFUNC]
add r12, r12, r10 @ initialise processor
@ (return control reg)
ret r12
ENDPROC(secondary_startup)
ENDPROC(secondary_startup_arm)
/*
* r6 = &secondary_data
*/
ENTRY(__secondary_switched)
ldr sp, [r7, #4] @ get secondary_data.stack
mov fp, #0
b secondary_start_kernel
ENDPROC(__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__ < 6
orr r0, r0, #CR_A
#else
bic r0, r0, #CR_A
#endif
#ifdef CONFIG_CPU_DCACHE_DISABLE
bic r0, r0, #CR_C
#endif
#ifdef CONFIG_CPU_BPREDICT_DISABLE
bic r0, r0, #CR_Z
#endif
#ifdef CONFIG_CPU_ICACHE_DISABLE
bic r0, r0, #CR_I
#endif
#ifndef CONFIG_ARM_LPAE
mov 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 register
mcr p15, 0, r4, c2, c0, 0 @ load page table pointer
#endif
b __turn_mmu_on
ENDPROC(__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, r0
instr_sync
mcr p15, 0, r0, c1, c0, 0 @ write control reg
mrc p15, 0, r3, c0, c0, 0 @ read id reg
instr_sync
mov r3, r3
mov r3, r13
ret r3
__turn_mmu_on_end:
ENDPROC(__turn_mmu_on)
.popsection
#ifdef CONFIG_SMP_ON_UP
__HEAD
__fixup_smp:
and r3, r9, #0x000f0000 @ architecture version
teq r3, #0x000f0000 @ CPU ID supported?
bne __fixup_smp_on_up @ no, assume UP
bic r3, r9, #0x00ff0000
bic r3, r3, #0x0000000f @ mask 0xff00fff0
mov r4, #0x41000000
orr r4, r4, #0x0000b000
orr r4, r4, #0x00000020 @ val 0x4100b020
teq r3, r4 @ ARM 11MPCore?
reteq lr @ yes, assume SMP
mrc p15, 0, r0, c0, c0, 5 @ read MPIDR
and r0, r0, #0xc0000000 @ multiprocessing extensions and
teq 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, #0x41000000
orr r4, r4, #0x0000c000
orr r4, r4, #0x00000090
teq r3, r4 @ Check for ARM Cortex-A9
retne 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 address
teq r0, #0x0 @ '0' on actual UP A9 hardware
beq __fixup_smp_on_up @ So its an A9 UP
ldr r0, [r0, #4] @ read SCU Config
ARM_BE8(rev r0, r0) @ byteswap if big endian
and r0, r0, #0x3 @ number of CPUs
teq r0, #0x0 @ is 1?
retne lr
__fixup_smp_on_up:
adr r0, 1f
ldmia r0, {r3 - r5}
sub r3, r0, r3
add r4, r4, r3
add r5, r5, r3
b __do_fixup_smp_on_up
ENDPROC(__fixup_smp)
.align
1: .word .
.word __smpalt_begin
.word __smpalt_end
.pushsection .data
.globl smp_on_up
smp_on_up:
ALT_SMP(.long 1)
ALT_UP(.long 0)
.popsection
#endif
.text
__do_fixup_smp_on_up:
cmp r4, r5
reths lr
ldmia 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.
#endif
THUMB( strh r6, [r0], #2 ) @ For Thumb-2, store as two halfwords
THUMB( mov r6, r6, lsr #16 ) @ to be robust against misaligned r3.
THUMB( strh r6, [r0] )
b __do_fixup_smp_on_up
ENDPROC(__do_fixup_smp_on_up)
ENTRY(fixup_smp)
stmfd sp!, {r4 - r6, lr}
mov r4, r0
add r5, r0, r1
mov r3, #0
bl __do_fixup_smp_on_up
ldmfd 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, 1f
ldmia r0, {r3-r7}
mvn ip, #0
subs r3, r0, r3 @ PHYS_OFFSET - PAGE_OFFSET
add r4, r4, r3 @ adjust table start address
add r5, r5, r3 @ adjust table end address
add r6, r6, r3 @ adjust __pv_phys_pfn_offset address
add r7, r7, r3 @ adjust __pv_offset address
mov r0, r8, lsr #PAGE_SHIFT @ convert to PFN
str r0, [r6] @ save computed PHYS_OFFSET to __pv_phys_pfn_offset
strcc ip, [r7, #HIGH_OFFSET] @ save to __pv_offset high bits
mov r6, r3, lsr #24 @ constant for add/sub instructions
teq r3, r6, lsl #24 @ must be 16MiB aligned
THUMB( it ne @ cross section branch )
bne __error
str r3, [r7, #LOW_OFFSET] @ save to __pv_offset low bits
b __fixup_a_pv_table
ENDPROC(__fixup_pv_table)
.align
1: .long .
.long __pv_table_begin
.long __pv_table_end
2: .long __pv_phys_pfn_offset
.long __pv_offset
.text
__fixup_a_pv_table:
adr r0, 3f
ldr r6, [r0]
add r6, r6, r3
ldr r0, [r6, #HIGH_OFFSET] @ pv_offset high word
ldr r6, [r6, #LOW_OFFSET] @ pv_offset low word
mov r6, r6, lsr #24
cmn r0, #1
#ifdef CONFIG_THUMB2_KERNEL
moveq r0, #0x200000 @ set bit 21, mov to mvn instruction
lsls r6, #24
beq 2f
clz r7, r6
lsr r6, #24
lsl r6, r7
bic r6, #0x0080
lsrs r7, #1
orrcs r6, #0x0080
orr r6, r6, r7, lsl #12
orr r6, #0x4000
b 2f
1: add r7, r3
ldrh ip, [r7, #2]
ARM_BE8(rev16 ip, ip)
tst ip, #0x4000
and ip, #0x8f00
orrne ip, r6 @ mask in offset bits 31-24
orreq ip, r0 @ mask in offset bits 7-0
ARM_BE8(rev16 ip, ip)
strh ip, [r7, #2]
bne 2f
ldrh ip, [r7]
ARM_BE8(rev16 ip, ip)
bic ip, #0x20
orr ip, ip, r0, lsr #16
ARM_BE8(rev16 ip, ip)
strh ip, [r7]
2: cmp r4, r5
ldrcc r7, [r4], #4 @ use branch for delay slot
bcc 1b
bx lr
#else
#ifdef CONFIG_CPU_ENDIAN_BE8
moveq r0, #0x00004000 @ set bit 22, mov to mvn instruction
#else
moveq r0, #0x400000 @ set bit 22, mov to mvn instruction
#endif
b 2f
1: ldr ip, [r7, r3]
#ifdef CONFIG_CPU_ENDIAN_BE8
@ in BE8, we load data in BE, but instructions still in LE
bic ip, ip, #0xff000000
tst ip, #0x000f0000 @ check the rotation field
orrne ip, ip, r6, lsl #24 @ mask in offset bits 31-24
biceq ip, ip, #0x00004000 @ clear bit 22
orreq ip, ip, r0 @ mask in offset bits 7-0
#else
bic ip, ip, #0x000000ff
tst ip, #0xf00 @ check the rotation field
orrne ip, ip, r6 @ mask in offset bits 31-24
biceq ip, ip, #0x400000 @ clear bit 22
orreq ip, ip, r0 @ mask in offset bits 7-0
#endif
str ip, [r7, r3]
2: cmp r4, r5
ldrcc r7, [r4], #4 @ use branch for delay slot
bcc 1b
ret lr
#endif
ENDPROC(__fixup_a_pv_table)
.align
3: .long __pv_offset
ENTRY(fixup_pv_table)
stmfd sp!, {r4 - r7, lr}
mov r3, #0 @ no offset
mov r4, r0 @ r0 = table start
add r5, r0, r1 @ r1 = table size
bl __fixup_a_pv_table
ldmfd 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"
/*
* linux/arch/arm/kernel/head-common.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.
*
*/
#include
#define ATAG_CORE 0x54410001
#define ATAG_CORE_SIZE ((2*4 + 3*4) >> 2)
#define ATAG_CORE_SIZE_EMPTY ((2*4) >> 2)
#ifdef CONFIG_CPU_BIG_ENDIAN
#define OF_DT_MAGIC 0xd00dfeed
#else
#define OF_DT_MAGIC 0xedfe0dd0 /* 0xd00dfeed in big-endian */
#endif
/*
* Exception handling. Something went wrong and we can't proceed. We
* ought to tell the user, but since we don't have any guarantee that
* we're even running on the right architecture, we do virtually nothing.
*
* If CONFIG_DEBUG_LL is set we try to print out something about the error
* and hope for the best (useful if bootloader fails to pass a proper
* machine ID for example).
*/
__HEAD
/* Determine validity of the r2 atags pointer. The heuristic requires
* that the pointer be aligned, in the first 16k of physical RAM and
* that the ATAG_CORE marker is first and present. If CONFIG_OF_FLATTREE
* is selected, then it will also accept a dtb pointer. Future revisions
* of this function may be more lenient with the physical address and
* may also be able to move the ATAGS block if necessary.
*
* Returns:
* r2 either valid atags pointer, valid dtb pointer, or zero
* r5, r6 corrupted
*/
__vet_atags:
tst r2, #0x3 @ aligned?
bne 1f
ldr r5, [r2, #0]
#ifdef CONFIG_OF_FLATTREE
ldr r6, =OF_DT_MAGIC @ is it a DTB?
cmp r5, r6
beq 2f
#endif
cmp r5, #ATAG_CORE_SIZE @ is first tag ATAG_CORE?
cmpne r5, #ATAG_CORE_SIZE_EMPTY
bne 1f
ldr r5, [r2, #4]
ldr r6, =ATAG_CORE
cmp r5, r6
bne 1f
2: ret lr @ atag/dtb pointer is ok
1: mov r2, #0
ret lr
ENDPROC(__vet_atags)
/*
* The following fragment of code is executed with the MMU on in MMU mode,
* and uses absolute addresses; this is not position independent.
*
* r0 = cp#15 control register
* r1 = machine ID
* r2 = atags/dtb pointer
* r9 = processor ID
*/
__INIT
__mmap_switched:
adr r3, __mmap_switched_data
ldmia r3!, {r4, r5, r6, r7}
cmp r4, r5 @ Copy data segment if needed
1: cmpne r5, r6
ldrne fp, [r4], #4
strne fp, [r5], #4
bne 1b
mov fp, #0 @ Clear BSS (and zero fp)
1: cmp r6, r7
strcc fp, [r6],#4
bcc 1b
ARM( ldmia r3, {r4, r5, r6, r7, sp})
THUMB( ldmia r3, {r4, r5, r6, r7} )
THUMB( ldr sp, [r3, #16] )
str r9, [r4] @ Save processor ID
str r1, [r5] @ Save machine type
str r2, [r6] @ Save atags pointer
cmp r7, #0
strne r0, [r7] @ Save control register values
b start_kernel
ENDPROC(__mmap_switched)
.align 2
.type __mmap_switched_data, %object
__mmap_switched_data:
.long __data_loc @ r4
.long _sdata @ r5
.long __bss_start @ r6
.long _end @ r7
.long processor_id @ r4
.long __machine_arch_type @ r5
.long __atags_pointer @ r6
#ifdef CONFIG_CPU_CP15
.long cr_alignment @ r7
#else
.long 0 @ r7
#endif
.long init_thread_union + THREAD_START_SP @ sp
.size __mmap_switched_data, . - __mmap_switched_data
/*
* This provides a C-API version of __lookup_processor_type
*/
ENTRY(lookup_processor_type)
stmfd sp!, {r4 - r6, r9, lr}
mov r9, r0
bl __lookup_processor_type
mov r0, r5
ldmfd sp!, {r4 - r6, r9, pc}
ENDPROC(lookup_processor_type)
__FINIT
.text
/*
* Read processor ID register (CP#15, CR0), and look up in the linker-built
* supported processor list. Note that we can't use the absolute addresses
* for the __proc_info lists since we aren't running with the MMU on
* (and therefore, we are not in the correct address space). We have to
* calculate the offset.
*
* r9 = cpuid
* Returns:
* r3, r4, r6 corrupted
* r5 = proc_info pointer in physical address space
* r9 = cpuid (preserved)
*/
__lookup_processor_type:
adr r3, __lookup_processor_type_data
ldmia r3, {r4 - r6}
sub r3, r3, r4 @ get offset between virt&phys
add r5, r5, r3 @ convert virt addresses to
add r6, r6, r3 @ physical address space
1: ldmia r5, {r3, r4} @ value, mask
and r4, r4, r9 @ mask wanted bits
teq r3, r4
beq 2f
add r5, r5, #PROC_INFO_SZ @ sizeof(proc_info_list)
cmp r5, r6
blo 1b
mov r5, #0 @ unknown processor
2: ret lr
ENDPROC(__lookup_processor_type)
/*
* Look in for information about the __proc_info structure.
*/
.align 2
.type __lookup_processor_type_data, %object
__lookup_processor_type_data:
.long .
.long __proc_info_begin
.long __proc_info_end
.size __lookup_processor_type_data, . - __lookup_processor_type_data
__error_lpae:
#ifdef CONFIG_DEBUG_LL
adr r0, str_lpae
bl printascii
b __error
str_lpae: .asciz "\nError: Kernel with LPAE support, but CPU does not support LPAE.\n"
#else
b __error
#endif
.align
ENDPROC(__error_lpae)
__error_p:
#ifdef CONFIG_DEBUG_LL
adr r0, str_p1
bl printascii
mov r0, r9
bl printhex8
adr r0, str_p2
bl printascii
b __error
str_p1: .asciz "\nError: unrecognized/unsupported processor variant (0x"
str_p2: .asciz ").\n"
.align
#endif
ENDPROC(__error_p)
__error:
#ifdef CONFIG_ARCH_RPC
/*
* Turn the screen red on a error - RiscPC only.
*/
mov r0, #0x02000000
mov r3, #0x11
orr r3, r3, r3, lsl #8
orr r3, r3, r3, lsl #16
str r3, [r0], #4
str r3, [r0], #4
str r3, [r0], #4
str r3, [r0], #4
#endif
1: mov r0, r0
b 1b
ENDPROC(__error)
arch/arm/kernel/head.S/stext
arch/arm/kernel/head.S/__enable_mmu
arch/arm/kernel/head.S/__trun_mmu_on
arch/arm/kernel/head-common.S/__mmap_switched
init/main.c/start_kernel