linux的arch/arm/kernel/head.S
* 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 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.
*/
.section “.text.head”, “ax”
ENTRY(stext)
msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE @ ensure svc mode /*这里是内核真正的第一条指令,确保MMU不工作,数据cache不工作,指令cache不关心,必须是SVC模式,IRQ关闭*/
@ and irqs disabled
mrc p15, 0, r9, c0, c0 @ get processor id /*得到处理机ID*/
bl __lookup_processor_type @ r5=procinfo r9=cpuid /*得到处理机类型信息,返回r5指向结构,见head-common.S*/
beq __error_p @ yes, error ‘p’ /*输出错误,见head-common.S*/
beq __error_a @ yes, error ‘a’ /*输出错误,见head-common.S*/
bl __vet_atags /*判断参数有效性,见head-common.S*/
bl __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_machine_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, __switch_data @ address to jump to after /*r13设置为__switch_data,见head-common.S,在MMU使能后会使用到*/
@ mmu has been enabled
adr lr, __enable_mmu @ return (PIC) address /*设置返回地址为__enable_mmu,执行完下面函数后就会跳到__enable_mmu执行*/
add pc, r10, #PROCINFO_INITFUNC /*r10上面置为了处理器结构proc_info_list(从__lookup_processor_type这里知道,Proc_info_list在__proc_info_begin开始,从vmlinux.lds.S知道,即是proc.info.init,定义在proc-(arch).S里面)的地址,这里就会跳到__cpu_flush处(从proc-(arch).S里面看到,基本都是一条跳转指令,如:b __v6_setup,)执行,从__v6_setup执行完后会使用:mov pc, lr,跳转到上面这条指令执行,即__eanble_mmu。*/
#if defined(CONFIG_SMP)
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.
*/
msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE
mrc p15, 0, r9, c0, c0 @ get processor id
bl __lookup_processor_type
movs r10, r5 @ invalid processor?
moveq r0, #’p’ @ yes, error ‘p’
beq __error
/*
* Use the page tables supplied from __cpu_up.
*/
adr r4, __secondary_data
ldmia r4, {r5, r7, r13} @ address to jump to after
sub r4, r4, r5 @ mmu has been enabled
ldr r4, [r7, r4] @ get secondary_data.pgdir
adr lr, __enable_mmu @ return address
add pc, r10, #PROCINFO_INITFUNC @ initialise processor
@ (return control reg)
ENDPROC(secondary_startup)
/*
* r6 = &secondary_data
*/
ENTRY(__secondary_switched)
ldr sp, [r7, #4] @ get secondary_data.stack
mov fp, #0
b secondary_start_kernel
ENDPROC(__secondary_switched)
.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.
*/
__enable_mmu: /*Eable MMU之前的最后执行代码,为进入MMU做准备*/
#ifdef CONFIG_ALIGNMENT_TRAP /*设置地址对齐错误检测*/
orr r0, r0, #CR_A
#else
bic r0, r0, #CR_A
#endif
#ifdef CONFIG_CPU_DCACHE_DISABLE
bic r0, r0, #CR_C /*禁止数据cache*/
#endif
#ifdef CONFIG_CPU_BPREDICT_DISABLE
bic r0, r0, #CR_Z
#endif
#ifdef CONFIG_CPU_ICACHE_DISABLE
bic r0, r0, #CR_I /*禁止指令cache*/
#endif
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 /*设置域访问寄存器c3,访问权限*/
mcr p15, 0, r4, c2, c0, 0 @ load page table pointer /*设置页表地址c2,页表基地址*/
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
* r13 = *virtual* address to jump to upon completion
*
* other registers depend on the function called upon completion
*/
.align 5
__turn_mmu_on: /*进入MMU方式,这将改变可见的内存空间,不能跟踪执行*/
mov r0, r0
mcr p15, 0, r0, c1, c0, 0 @ write control reg /*写入控制寄存器,打开MMU,打开cache等*/
mrc p15, 0, r3, c0, c0, 0 @ read id reg /*读以下id寄存器*/
mov r3, r3 /*等大指令执行完*/
mov r3, r3
mov pc, r13 /*前面进入__enable_mmu之前设置了r13=__switch_data,而__switch_data的第一个地址处为__mmap_switched,所以将执行__mmap_switched,在head-common.S里面*/
ENDPROC(__turn_mmu_on)
/*
* 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 = machinfo
* r9 = cpuid
* r10 = procinfo
*
* Returns:
* r0, r3, r6, r7 corrupted
* r4 = physical page table address
*/
__create_page_tables:
pgtbl r4 @ page table address /*宏,页表地址16k对齐:ldr \rd, =(KERNEL_RAM_PADDR – 0x4000)*/
/*
* Clear the 16K level 1 swapper page table
*/
mov r0, r4 /*为内核代码存储区域创建页表,首先将内核起始地址-0x4000到内核起始地址之间的16K存储器清0,将创建的页表存于此处。*/
mov r3, #0
add r6, r0, #0x4000
1: str r3, [r0], #4
str r3, [r0], #4
str r3, [r0], #4
str r3, [r0], #4
teq r0, r6
bne 1b
ldr r7, [r10, #PROCINFO_MM_MMUFLAGS] @ mm_mmuflags /*从proc_info_list结构中获取字段__cpu_mm_mmu_flags,该字段包含了存储空间访问权限等。此处指令执行之后r7=0x00000c1e*/
/*
* Create identity mapping for first MB of kernel to
* cater for the MMU enable. This identity mapping
* will be removed by paging_init(). We use our current program
* counter to determine corresponding section base address.
*/
mov r6, pc, lsr #20 @ start of kernel section /*此处建立一个物理地址到物理地址的平板映射,这个映射将在函数paging_init(). 被清除。r6 = 0x300 r3 = 0x30000c1e [0x30004c00]=0x30000c1e*/
orr r3, r7, r6, lsl #20 @ flags + kernel base
str r3, [r4, r6, lsl #2] @ identity mapping
/*
* Now setup the pagetables for our kernel direct
* mapped region.
*/
add r0, r4, #(KERNEL_START & 0xff000000) >> 18 /*MMU是通过C2中基地址(高18位)与虚拟地址的高12位组合成物理地址,在转换表中查找地址条目。R4中存放的就是这个基地址0x30004000。下面通过两次获取虚拟地址KERNEL_START的高12位。KERNEL_START是内核存放的起始地址,为0X30008000。r0 = 0x30007000*/
str r3, [r0, #(KERNEL_START & 0x00f00000) >> 18]! /*r0 存放的是转换表的起始位置*/
ldr r6, =(KERNEL_END – 1) /*获取内核的尾部虚拟地址存于r6中*/
add r0, r0, #4 /*第一个地址条目存放在0x30007004处,以后一次递增*/
add r6, r4, r6, lsr #18 /*计算最后一个地址条目存放的位置*/
1: cmp r0, r6 /*填充这之间的地址条目*/
add r3, r3, #1 << 20 /*每一个地址条目代表了1MB空间的地址映射。物理地址将从0x30100000 开始映射。0X30000000开始的1MB空间将在下面映射。*/
strls r3, [r0], #4
bls 1b
#ifdef CONFIG_XIP_KERNEL /*如果是XIP就进行以下映射,这只是将内核代码存储的空间重新映射。*/
/*
* Map some ram to cover our .data and .bss areas.
*/
orr r3, r7, #(KERNEL_RAM_PADDR & 0xff000000)
.if (KERNEL_RAM_PADDR & 0x00f00000)
orr r3, r3, #(KERNEL_RAM_PADDR & 0x00f00000)
.endif
add r0, r4, #(KERNEL_RAM_VADDR & 0xff000000) >> 18
str r3, [r0, #(KERNEL_RAM_VADDR & 0x00f00000) >> 18]!
ldr r6, =(_end – 1)
add r0, r0, #4
add r6, r4, r6, lsr #18
1: cmp r0, r6
add r3, r3, #1 << 20
strls r3, [r0], #4
bls 1b
#endif
/*
* Then map first 1MB of ram in case it contains our boot params.
*/
add r0, r4, #PAGE_OFFSET >> 18 /*映射0X30000000开始的1MB空间。PAGE_OFFSET = 0XC0000000,PHYS_OFFSET = 0X30000000, r0 = 0x30007000,上面是从0x30007004开始存放地址条目的。*/
orr r6, r7, #(PHYS_OFFSET & 0xff000000) /*r6= 0x30000c1e*/
.if (PHYS_OFFSET & 0x00f00000)
orr r6, r6, #(PHYS_OFFSET & 0x00f00000)
.endif
str r6, [r0] /*将0x30000c1e存于0x30007000处。*/
#ifdef CONFIG_DEBUG_LL /*调试而做的相关映射*/
ldr r7, [r10, #PROCINFO_IO_MMUFLAGS] @ io_mmuflags
/*
* Map in IO space for serial debugging.
* This allows debug messages to be output
* via a serial console before paging_init.
*/
ldr r3, [r8, #MACHINFO_PGOFFIO]
add r0, r4, r3
rsb r3, r3, #0x4000 @ PTRS_PER_PGD*sizeof(long)
cmp r3, #0x0800 @ limit to 512MB
movhi r3, #0x0800
add r6, r0, r3
ldr r3, [r8, #MACHINFO_PHYSIO]
orr r3, r3, r7
1: str r3, [r0], #4
add r3, r3, #1 << 20
teq r0, r6
bne 1b
#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 >> 18
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 >> 18
orr r3, r7, #0x02000000
str r3, [r0]
add r0, r4, #0xd8000000 >> 18
str r3, [r0]
#endif
#endif
mov pc, lr
ENDPROC(__create_page_tables)
.ltorg
#include “head-common.S”