通过分析可知ARM-linux内核的启动分为两个过程,首先,自解压内核到内存中;其次,跳转到解压缩之后的内核运行。压缩内核的启动部分在 linux/arch/arm/boot/compressed/head.S和arch\arm\boot\compressed\misc.c两个文件中。而分压缩内核的启动部分,是真正的linux内核的入口,在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.进入时的条件
*
* 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.
*/
__INIT
.type stext, #function
ENTRY(stext)
mov r12, r0
mov r0, #PSR_F_BIT | PSR_I_BIT | MODE_SVC@ make sure svc mode确定SVC模式
msr cpsr_c, r0@ and all irqs disabled关闭所有中断
bl __lookup_processor_type跳转到查找处理器函数段
teq r10, #0@ invalid processor?判断是否是无效处理器
moveq r0, #'p'@ yes, error 'p'
beq __error
bl __lookup_architecture_type跳转到查找结构函数段
teq r7, #0@ invalid architecture?
moveq r0, #'a'@ yes, error 'a'
beq __error
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_architecture_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.
*/注释说的很详细。此时CPU已经准备好打开MMU,r0保存CPU控制寄存器的值
adr lr, __turn_mmu_on@ return (PIC) address
addpc, r10, #12
.type __switch_data, %object
__switch_data:
.long __mmap_switched
.long __data_loc@ r2
.long __data_start@ r3
.long __bss_start@ r4
.long _end@ r5
.long processor_id@ r6
.long __machine_arch_type@ r7
.long cr_alignment@ r8
.long init_thread_union+8192@ sp
/*
* Enable the MMU. This completely changes the structure of the visible
* memory space. 这将完全改变可见存储空间的结构
*/
.align 5
.type __turn_mmu_on, %function
__turn_mmu_on:
ldr lr, __switch_data
#ifdef CONFIG_ALIGNMENT_TRAP
orr r0, r0, #2@ ...........A.
#endif
mcr p15, 0, r0, c1, c0, 0@ write control reg
mrc p15, 0, r3, c0, c0, 0@ read id reg
mov r3, r3
mov r3, r3
mov pc, lr
/*
* The following fragment of code is executed with the MMU on, and uses
* absolute addresses; this is not position independent.下面代码在MMU打开的情况下运行,用绝对地址,与位置无关
*
* r0 = processor control register
* r1 = machine ID机器ID
* r9 = processor ID处理器ID
* r12 = value of r0 when kernel was called (currently always zero)
*/
这里主要是为调用c函数start_kernel做准备。
.align 5
__mmap_switched:
adr r2, __switch_data + 4
ldmia r2, {r2, r3, r4, r5, r6, r7, r8, sp}
cmp r2, r3@ Copy data segment if needed
1: cmpne r3, r4
ldrne fp, [r2], #4
strne fp, [r3], #4
bne 1b
mov fp, #0@ Clear BSS (and zero fp)
1: cmp r4, r5
strcc fp, [r4],#4
bcc 1b
str r9, [r6]@ Save processor ID
str r1, [r7]@ Save machine type
bic r2, r0, #2@ Clear 'A' bit
stmia r8, {r0, r2}@ Save control register values
b start_kernel
内核编译连接过程的连接脚本是vmlinux.lds文件,ARM是arch\arm\kernel\vmlinux.lds.S,列出部分,如下所示:
OUTPUT_ARCH(arm)
ENTRY(stext)
#ifndef __ARMEB__
jiffies = jiffies_64;
#else
jiffies = jiffies_64 + 4;
#endif
SECTIONS
{
. = TEXTADDR;内核启动的虚拟地址,为0xc0008000
.init : { /* Init code and data */
_stext = .;
_sinittext = .;
*(.init.text)
_einittext = .;
__proc_info_begin = .;
*(.proc.info)
__proc_info_end = .;
__arch_info_begin = .;
*(.arch.info)
__arch_info_end = .;
__tagtable_begin = .;
*(.taglist)
__tagtable_end = .;
. = ALIGN(16);
__setup_start = .;
*(.init.setup)
__setup_end = .;
__early_begin = .;
*(__early_param)
__early_end = .;
__initcall_start = .;
*(.initcall1.init)
*(.initcall2.init)
*(.initcall3.init)
*(.initcall4.init)
*(.initcall5.init)
*(.initcall6.init)
*(.initcall7.init)
__initcall_end = .;
__con_initcall_start = .;
*(.con_initcall.init)
__con_initcall_end = .;
__security_initcall_start = .;
*(.security_initcall.init)
__security_initcall_end = .;
. = ALIGN(32);
__initramfs_start = .;
usr/built-in.o(.init.ramfs)
__initramfs_end = .;
. = ALIGN(64);
__per_cpu_start = .;
*(.data.percpu)
__per_cpu_end = .;
#ifndef CONFIG_XIP_KERNEL
__init_begin = _stext;
*(.init.data)
. = ALIGN(4096);
__init_end = .;
#endif
zImage是由生成的vmlinux再经过压缩生成的,此时的连接脚本是\arch\arm\boot\compressed\vmlinux.lds,内容如下:
*/
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
. = TEXT_START;内核RAM启动的偏移地址,这个地址是物理地址。
在\arch\arm\boot\compressed\Makefile中有如下
SEDFLAGS= s/TEXT_START/$(ZTEXTADDR)/;s/BSS_START/$(ZBSSADDR)/使得TEXT_START=ZTEXTADDR。
_text = .;
.text : {
_start = .;
*(.start)
*(.text)
*(.fixup)
*(.gnu.warning)
*(.rodata)
*(.rodata.*)
*(.glue_7)
*(.glue_7t)
*(.piggydata)
. = ALIGN(4);
}
这篇先到这里,下篇再说。
ARM Linux内核启动(2)的连接