kernel内核启动流程
(1)自解压代码
linux-2.6.22.6\arch\arm\boot\compressed\head.S
对比于linux-2.6.22.6\arch\arm\kernel\head.S, 是自解压代码+原本的代码,执行时执行自解压代码的内容
(2)第一阶段:
ENTRY(stext)
msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE @ ensure svc mode
@ and irqs disabled
mrc p15, 0, r9, c0, c0 @ get processor id
bl __lookup_processor_type @ r5=procinfo r9=cpuid //判断是否支持这个CPU
movs r10, r5 @ invalid processor (r5=0)?
beq __error_p @ yes, error 'p'
bl __lookup_machine_type @ r5=machinfo //比较遍历内核的机器码的 machine_desc 结构体,与uboot传入的机器码比较,判断是否支持这个单板
movs r8, r5 @ invalid machine (r5=0)?
beq __error_a @ yes, error 'a'
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 //后面的mmu使能后,在执行该跳转
@ mmu has been enabled
adr lr, __enable_mmu @ return (PIC) address //使能mmu
add pc, r10, #PROCINFO_INITFUNC
arch/arm/kernel/vmlinux.lds:217: *(.arch.info.init)
include/asm-arm/mach/arch.h:53: __attribute__((__section__(".arch.info.init"))) = {
#define MACHINE_START(_type,_name) \ linux-2.6.22.6\include/asm-arm/mach/arch.h
static const struct machine_desc __mach_desc_##_type \
__used \
__attribute__((__section__(".arch.info.init"))) = { \ //注意设置为段:.arch.info.init
.nr = MACH_TYPE_##_type, \
.name = _name,
#define MACHINE_END \
};
MACHINE_START(S3C2440, "SMDK2440") 是定义一个结构体,在Linux中被广泛调用用来定义板子机器码对应的结构体 linux-2.6.22.6\arch\arm\mach-s3c2440\mach-smdk2440.c
/* 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, //这里等于 0x30000000+0x100
.init_irq = s3c24xx_init_irq,
.map_io = smdk2440_map_io,
.init_machine = smdk2440_machine_init,
.timer = &s3c24xx_timer,
MACHINE_END
.type __switch_data, %object
__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
.type __mmap_switched, %function
__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
mov fp, #0 @ Clear BSS (and zero fp)
1: cmp r6, r7
strcc fp, [r6],#4
bcc 1b
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
(3)第二阶段:start_kernel linux-2.6.22.6\init\main.c
asmlinkage void __init start_kernel(void)
{
printk(KERN_NOTICE);
printk(linux_banner); //打印Linux内核版本信息
setup_arch(&command_line); //解析uboot传入的启动参数, 并会调用 machine_desc 的相关初始化函数
setup_command_line(command_line); //解析uboot传入的启动参数
parse_early_param();
| do_early_param
|从 __setup_start 到 __setup_end 调用 early 函数,
unknown_bootoption
| obsolete_checksetup
|从 __setup_start 到 __setup_end 调用 非early 函数
rest_init(); //
| kernel_init
| prepare_namespace
| mount_root //挂载根文件系统
| init_post //执行应用程序
}
void __init setup_arch(char **cmdline_p)
{
char *from = default_command_line;
if (mdesc->boot_params)
tags = phys_to_virt(mdesc->boot_params);
parse_cmdline(cmdline_p, from); //解析,若没有传入命令行参数,则使用默认参数
}
static void noinline __init_refok rest_init(void)
__releases(kernel_lock)
{
kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND); //创建内核线程
}
static int __init kernel_init(void * unused)
{
prepare_namespace();
init_post(); //执行应用程序
}
void __init prepare_namespace(void)
{
if (saved_root_name[0]) {
root_device_name = saved_root_name;
mount_root(); //挂载根文件系统
}
static int noinline init_post(void) linux-2.6.22.6\init\main.c
{
if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
run_init_process("/sbin/init");
run_init_process("/etc/init");
run_init_process("/bin/init");
run_init_process("/bin/sh");
}
=============
linux-2.6.22.6\init\do_mounts.c
static int __init root_dev_setup(char *line)
{
strlcpy(saved_root_name, line, sizeof(saved_root_name));
return 1;
}
__setup("root=", root_dev_setup); //定义一个结构体,不同的参数名 会对应一个处理函数
linux-2.6.22.6\include\linux\init.h
#define __setup_param(str, unique_id, fn, early) \
static char __setup_str_##unique_id[] __initdata = str; \
static struct obs_kernel_param __setup_##unique_id \
__attribute_used__ \
__attribute__((__section__(".init.setup"))) \ //注意设置为段:.init.setup
__attribute__((aligned((sizeof(long))))) \
= { __setup_str_##unique_id, fn, early }
#define __setup(str, fn) \
__setup_param(str, fn, fn, 0)
arch/arm/kernel/vmlinux.lds 链接文件中定义
__setup_start = .;
*(.init.setup)
__setup_end = .;
void __init parse_early_param(void) linux-2.6.22.6\init\main.c
{
strlcpy(tmp_cmdline, boot_command_line, COMMAND_LINE_SIZE);
parse_args("early options", tmp_cmdline, NULL, 0, do_early_param);
}
static int __init do_early_param(char *param, char *val) linux-2.6.22.6\init\main.c
{
for (p = __setup_start; p < __setup_end; p++) {
if (p->early && strcmp(param, p->str) == 0) { //p->early为0
if (p->setup_func(val) != 0)
}
参考:韦东山Linux教程