Linux内核的启动分为压缩内核和非压缩内核两种,这里我们以压缩内核为例。压缩内核运行时,将运行一段解压缩程序,得到真正的内核镜像,然后跳转到内核镜像运行。此时,Linux进入非压缩内核入口,在非压缩内核入口中,完成各种初始化操作后跳转到C语言入口处运行。主要流程如下所示。
1.解压缩内核镜像
解压缩程序通常在arch/arm/boot/compressed/目录中
├── atags_to_fdt.c
├── big-endian.S
├── decompress.c
├──head.S
├──head-sa1100.S
├──head-shark.S
├──head-sharpsl.S
├──head-shmobile.S
├──head-vt8500.S
├──head-xscale.S
├── libfdt_env.h
├── ll_char_wr.S
├── Makefile
├── misc.c
├── mmcif-sh7372.c
├── ofw-shark.c
├── piggy.gzip.S
├── piggy.lzma.S
├── piggy.lzo.S
├── piggy.xzkern.S
├── sdhi-sh7372.c
├── sdhi-shmobile.c
├── sdhi-shmobile.h
├──string.c
└── vmlinux.lds.in
它们经过编译后,生成的内容独立于真正的Linux内核,这部分内容的功能就是初始化环境,解压缩和运行真正的Linux内核。在压缩内核启动时,首先进入arch/arm/boot/compressed目录中的compressed目录中的head.S文件。
start:.type start,#function
.rept7
movr0, r0
.endr
ARM(movr0, r0 )
ARM( b 1f )
THUMB( adr r12, BSYM(1f) )
THUMB( bx r12 )
.word 0x016f2818 @ Magic numbers to help the loader
.word start @ absolute load/run zImage address
.word _edata @ zImage end address
THUMB( .thumb )1: movr7, r1 @ save architecture IDmov r8, r2 @ save atags pointer
start是head.S的程序的开始,在此之前都是一些宏定义。在1标号处保存由bootloader传递过来的参数
#ifndef __ARM_ARCH_2__
/*
* Booting from Angel - need toenter SVC mode anddisable
* FIQs/IRQs (numeric definitions from angel arm.h source).
* We only do this if we wereinuser mode on entry.
*/
@获取当前运行模式
mrs r2, cpsr @ get current mode
@测试是否为usr模式
tst r2, #3 @ notuser?
bne not_angelmovr0, #0x17 @ angel_SWIreason_EnterSVC
ARM( swi 0x123456 ) @ angel_SWI_ARM
THUMB( svc 0xab ) @ angel_SWI_THUMBnot_angel:mrs r2, cpsr @ turn off interrupts to
orr r2, r2, #0xc0 @ prevent angel from running
msr cpsr_c, r2
#else
teqp pc, #0x0c000003 @ turn off interrupts
#endif
如果内核从angel运行进入的运行模式将是usr mode,这时需要进入svc mode ,并禁止所有FIQ和IRQ中断。这些只有在进入时处于用户模式的时候才会执行。正常情况下,将运行not_angel标号处关闭中断的代码。然后对内核代码进行重定向(telocate)--(这部分代码还没看懂-_-!!!),重定向完成之后会跳转到not_relocated标号处运行。
not_relocated: mov r0, #0
1: str r0, [r2], #4@ clear bssstr r0, [r2], #4
str r0, [r2], #4
str r0, [r2], #4
cmpr2, r3
blo 1b
/*
* The C runtime environment should now be setup sufficiently.
* Set up some pointers,andstart decompressing.
* r4 = kernel execution address
* r7 = architecture ID
* r8 = atags pointer
*/movr0, r4movr1, sp @ malloc space above stackadd r2, sp, #0x10000 @ 64kmaxmovr3, r7
bl decompress_kernel
bl cache_clean_flush
bl cache_offmov r0, #0@ must be zeromovr1, r7 @ restore architecture numbermovr2, r8 @ restore atags pointer
ARM(mov pc, r4 ) @ call kernel
重定向完成之后,首先清bss段,这时所有初始化C语言运行环境都要做的,然后调用decompress_kernel解压内核,之后跳转到非压缩内核启动阶段。
2.汇编阶段启动流程
对于tiny4412而言,内核的链接脚本为arch/arm/kernel/vmlinux.lds,它是由arch/arm/kernel/vmlinux.lds.S生成的。在链接脚本中,我们可以找到内核的入口
OUTPUT_ARCH(arm)
ENTRY(stext)
jiffies = jiffies_64;SECTIONS
{
可以看出内核的入口为stext,它在 linux/arch/arm/kernel/head.S 中被定义。
.arm
__HEAD
ENTRY(stext)
THUMB( adr r9, BSYM(1f) ) @ Kernel is always enteredinARM.
THUMB( bx r9 ) @ If this is a Thumb-2kernel,
THUMB( .thumb ) @ switch to Thumb now.
THUMB(1: )
@设定为SVC模式,关闭IRQ、FIQ
setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 @ ensure svc mode
@andirqs disabled
@检查CPU ID 是否匹配
mrc p15,0, r9, c0, c0 @ get processor id
bl __lookup_processor_type @ r5=procinfo r9=cpuidmovs r10, r5 @ invalid processor (r5=0)?
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_MMFR0andr3, r3, #0xf @ extract VMSA supportcmp r3, #5@ long-descriptor translation table format?
THUMB( it lo ) @ force fixup-able long branch encoding
blo __error_p @ only classic page table format
#endif
#ifndef CONFIG_XIP_KERNEL
adr r3, 2f
ldmia r3, {r4, r8}subr4, r3, r4 @ (PHYS_OFFSET - PAGE_OFFSET)addr8, r8, r4 @ PHYS_OFFSET
#else
ldr r8, =PHYS_OFFSET @ always constantinthis case
#endif
/*
* r1 = machine no, r2 = atagsordtb,
* r8 = phys_offset, r9 =cpuid, r10 = procinfo
*/
@检查bootloader传入的参数列表atags的合法性
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
/*
* The following calls CPU specific codeina 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,andr0 will hold the CPU control register value.
*/
@建立C语言环境(代码重定位、清bss段)
ldr r13, =__mmap_switched @ address to jump to after
@ mmu has been enabled
adr lr, BSYM(1f) @ return (PIC) addressmovr8, r4 @ set TTBR1 to swapper_pg_dir
ARM(addpc, r10, #PROCINFO_INITFUNC )
THUMB(addr12, r10, #PROCINFO_INITFUNC )
THUMB(movpc, r12 )
@开启MMU1: b __enable_mmu
ENDPROC(stext)
__mmap_switched定义在arch/arm/kernel/head-common.S中
__mmap_switched:adr r3, __mmap_switched_data
ldmia r3!, {r4, r5, r6, r7}cmpr4, r5 @ Copy data segment if needed1: cmpne r5, r6
ldrne fp, [r4], #4strne fp, [r5], #4bne 1bmov fp, #0 @ Clear BSS (andzero fp)1: cmpr6, r7
strcc fp, [r6],#4bcc 1b
ARM( ldmia r3, {r4, r5, r6, r7, sp})
THUMB( ldmia r3, {r4, r5, r6, r7} )
THUMB( ldr sp, [r3, #16] )strr9, [r4] @ Save processor IDstrr1, [r5] @ Save machine typestrr2, [r6] @ Save atags pointer
bic r4, r0, #CR_A @ Clear‘A‘bit
stmia r7, {r0, r4} @ Save control register values
b start_kernel
汇编阶段代码主要完成了以下工作
①设置处理器为SVC模式并关闭中断
②调用__lookup_processor_type查找处理器信息结构体proc_info
③调用__enable_mmu打开MMU
④调用__create_page_tables创建初始页表
⑤调用__mmap_switched初始化C语言运行环境,最红跳转到C语言阶段入口函数start_kernel
3.C语言阶段启动流程
内核启动流程,知识储备还不完善,以后更新 -_- ......
参考文章:
http://blog.csdn.net/zqixiao_09/article/details/50821995