内核的启动也可以分为两个阶段
第一阶段是用汇编写的,主要是判断是否支持CPU、单板、创建页表等
第二阶段是用C语言写的,主要是处理u-boot传递的参数,挂接根文件系统,调用应用程序等
本节简要分析启动流程的第一阶段,代码是内核链接的第一个文件arch/arm/kernel/head.S
1、判断内核是否支持cpu
mrc p15, 0, r9, c0, c0 @ get processor id
bl __lookup_processor_type @ r5=procinfo r9=cpuid
/* 读取机器 id,判断内核是否支持 */
movs r10, r5 @ invalid processor (r5=0)?
beq __error_p @ yes, error 'p'
2、判断内核是否支持单板
要支持某块单板,要在内核的arch目录下提供一个machine_desc结构体,它的段属性是.arch.info.init,在判断是否支持单板的时候,内核会从这个段中查找相应结构体,如果找到,则表明支持单板
bl __lookup_machine_type @ r5=machinfo
movs r8, r5 @ invalid machine (r5=0)?
beq __error_a @ yes, error 'a'
其中__lookup_machine_type函数位于同目录下的head-common.S中,该文件部分代码如下
3: .long . /*虚拟地址*/
.long __arch_info_begin /*机器ID起始地址*/
.long __arch_info_end /*机器ID结束地址*/
/*__arch_info_begin、__arch_info_end定义在链接脚本:*/
__arch_info_begin = .;
*(.arch.info.init)
__arch_info_end = .;
__lookup_machine_type:
/* 获得代码段标号3的地址(实际的物理地址),存入r3*/
adr r3, 3b
/* 取r3、r3+4、r3+8地内容存入r4、r5、r6 */
ldmia r3, {r4, r5, r6} //r4、r5、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
/* 比较机器id */
1: ldr r3, [r5, #MACHINFO_TYPE] @ get machine type
teq r3, r1 @ matches loader number?
beq 2f @ found
add r5, r5, #SIZEOF_MACHINE_DESC @ next machine_desc
cmp r5, r6
blo 1b
mov r5, #0 @ unknown machine
2: mov pc, lr
以arch/arm/mach-s3c2440/mach-smdk2440.c为例,其中定义的machine_desc结构体是:
MACHINE_START(S3C2440, "SMDK2440")
/* Maintainer: Ben Dooks <ben@fluff.org> */
.phys_io = S3C2410_PA_UART,
.io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
.boot_params = S3C2410_SDRAM_PA + 0x100,
.init_irq = s3c24xx_init_irq,
.map_io = smdk2440_map_io,
.init_machine = smdk2440_machine_init,
.timer = &s3c24xx_timer,
MACHINE_END
所用到的宏定义于include/asm-arm/mach/arch.h:
#define MACHINE_START(_type,_name) \
static const struct machine_desc __mach_desc_##_type \
__used \
__attribute__((__section__(".arch.info.init"))) = { \
.nr = MACH_TYPE_##_type, \
.name = _name,
#define MACHINE_END \
};
将宏展开后:
static const struct machine_desc __mach_desc_S3C2440 \
__used \
__attribute__((__section__(".arch.info.init"))) = { \
.nr = MACH_TYPE_S3C2440, \
.name = “SMDK2440”,
.phys_io = S3C2410_PA_UART,
.io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
.boot_params = S3C2410_SDRAM_PA + 0x100,
.init_irq = s3c24xx_init_irq,
.map_io = smdk2440_map_io,
.init_machine = smdk2440_machine_init,
.timer = &s3c24xx_timer,
};
其中
.nr就是单板的ID,用于使内核支持该单板
3、创建页表
bl __create_page_tables
4、保存__mmap_switched的地址,使能mmu后跳转过去
ldr r13, __switch_data @ address to jump to after
/*使能mmu*/
adr lr, __enable_mmu @ return (PIC) address
其中__switch_data:
__switch_data:
.long __mmap_switched
.long __data_loc @ r4
.long __data_start @ r5
.long __bss_start @ r6
.long _end @ r7
.long processor_id @ r4
.long __machine_arch_type @ r5
.long cr_alignment @ r6
.long init_thread_union + THREAD_START_SP @ sp
而在__mmap_switched程序段,内核会调用start_kernel,进入启动的第二阶段
__mmap_switched:
adr r3, __switch_data + 4
/* 拷贝数据段 */
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
/* 清除bss段 */
mov fp, #0 @ Clear BSS (and zero fp)
1: cmp r6, r7
strcc fp, [r6],#4
bcc 1b
/* 保存CPU id、单板 id,清除累加器,保存控制寄存器的值 */
ldmia r3, {r4, r5, r6, sp}
str r9, [r4] @ Save processor ID
str r1, [r5] @ Save machine type
bic r4, r0, #CR_A @ Clear 'A' bit
stmia r6, {r0, r4} @ Save control register values
b start_kernel