1.汇编过程启动简介
内核的汇编阶段其实比uboot要简单点,因为uboot已经做了不少的工作,内核的启动过程主要包含汇编、c语言两个阶段;其中的汇编阶段也主要可以分为两个过程:
1).校验处理器的id,以及传过来的r2参数,完成部分通用寄存器的传参;
r1 = machine no, r2 = atags or dtb,
r8 = phys_offset, r9 = cpuid, r10 = procinfo
2).创建mmu的页表__create_page_tables,使能mmu控制器,跳转至c语言阶段,start_kernel。
2.源码分析–汇编的第一阶段
其启动代码主要集中在以下几个文件:
/arch/arm/kernel/head.S
/arch/arm/include/asm/assembler.h
/arch/arm/kernel/head-common.S
//head.S中
ENTRY(stext)
setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 @ ensure svc mode
@ and irqs disabled
mrc p15, 0, r9, c0, c0 @ get processor id
bl __lookup_processor_type @ r5=procinfo r9=cpuid
movs r10, r5 @ invalid processor (r5=0)?
THUMB( it eq ) @ force fixup-able long branch encoding
beq __error_p @ yes, error 'p'
#ifndef CONFIG_XIP_KERNEL
adr r3, 2f
ldmia r3, {r4, r8}
sub r4, r3, r4 @ (PHYS_OFFSET - PAGE_OFFSET)
add r8, r8, r4 @ PHYS_OFFSET
#else
ldr r8, =PLAT_PHYS_OFFSET
#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
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
ARM( add pc, r10, #PROCINFO_INITFUNC )
THUMB( add r12, r10, #PROCINFO_INITFUNC )
THUMB( mov pc, r12 )
1: b __enable_mmu
ENDPROC(stext)
其中setmode 是汇编宏,在此其实是配置ARM的cpsr_c寄存器;
//assembler.h中
#ifdef CONFIG_THUMB2_KERNEL
.macro setmode, mode, reg
mov \reg, #\mode
msr cpsr_c, \reg
.endm
#else
.macro setmode, mode, reg
msr cpsr_c, #\mode
.endm
#endif
ARM的cpsr作为控制寄存器简介:
1.确保进行SVC模式,并去使能IRQ、FIQ中断位;
2.读取CP15协处理的Main ID Register,存在r9中;

3.
获取r5 = procinfo;
head-common.S
__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: mov pc, lr
ENDPROC(__lookup_processor_type)
bl __lookup_processor_type:
到此为止,r5=procinfo r9=cpuid;
另外在u-boot启动内核是传递了三个参数分别为:
r0 = 0, r1 = machine nr, r2 = atags or dtb pointer.
这也是为何在开始时不用r0-r3的原因,因为操作后会破坏掉u-boot传递过来的参数;
然后将r5存入r10中;
截止head.S第7至此:
r0 = 0;r1=machine nr; r2 = atags or dtb pointer
r9 = cpuid; r10 = procinfo
继续往下执行THUMB it eq是一个条件执行指令,满足则beq跳转之__err_p;
下面的__vet_atg 主要是校验uboot传过来r2参数,如果参数异常,内核也不会启动。
本文详细介绍了Linux内核启动的汇编阶段,包括处理器ID校验、通用寄存器传参、MMU页表创建及启用,以及C语言阶段的进入。通过分析`head.S`、`assembler.h`和`head-common.S`等关键文件,阐述了如何从汇编代码逐步过渡到C语言代码,启动内核的进程。在启动过程中,检查了u-boot传递的参数并进行了校验,确保内核启动的安全性和正确性。
8871

被折叠的 条评论
为什么被折叠?



