linux内核启动过程分析
1.处理U-boot 传入的参数
在Linux/arch/arm/kernel/head.s中
(1)判断是否支持该CPU(uboot启动时传入的参数bd->bi_arch_number为362)
mrc p15, 0, r9, c0, c0 @ get processor id
bl __lookup_processor_type @ r5=procinfo r9=cpuid
/*theKernel (0, bd->bi_arch_number, bd->bi_boot_params);*/
g:\linux-2.6.22.6\arch\arm\kernel\head-common.S下
/*
* Read processor ID register (CP#15, CR0), and look up in the linker-built
* supported processor list. Note that we can't use the absolute addresses
* for the __proc_info lists since we aren't running with the MMU on
* (and therefore, we are not in the correct address space). We have to
* calculate the offset.
*
* r9 = cpuid
* Returns:
* r3, r4, r6 corrupted
* r5 = proc_info pointer in physical address space
* r9 = cpuid (preserved)
*/
.type __lookup_processor_type, %function
__lookup_processor_type:
adr r3, 3f /* R3=3f的地址,为实际地址,物理地址 */
ldmda r3, {r5 - r7} /* ldm为加载多个寄存器,DA 每次传送后地址减四,LDMDA指令,每次传送后地址减4,long 型每次占4个字节。r5=__proc_info_begin ,r6=__proc_info_end ,r7="."代表虚拟地址*/
sub r3, r3, r7 @ 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
/* ldm为加载多个寄存器,iA 每次传送后地址加四*/
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
/*
* This provides a C-API version of the above function.
*/
ENTRY(lookup_processor_type)
stmfd sp!, {r4 - r7, r9, lr}
mov r9, r0
bl __lookup_processor_type
mov r0, r5
ldmfd sp!, {r4 - r7, r9, pc}
/*
* Look in include/asm-arm/procinfo.h and arch/arm/kernel/arch.[ch] for
* more information about the __proc_info and __arch_info structures.
*/
.long __proc_info_begin
.long __proc_info_end
3: .long .
.long __arch_info_begin
.long __arch_info_end
在G:\linux-2.6.22.6\arch\arm\kernel\vmlinux.lds.S中
SECTIONS
{
.......
. = PAGE_OFFSET + TEXT_OFFSET;/*TEXT_OFFSET=0x00000800*/
.....
.init : { /* Init code and data */
......
__arch_info_begin = .;
*(.arch.info.init)/* *表示所有的*/
__arch_info_end = .;
PAGE_OFFSET 在Linux/include/asm-arm/Memory.c下定义
/*
* Page offset: 3GB
*/
#ifndef PAGE_OFFSET
#define PAGE_OFFSET UL(0xc0000000)
#endif
在内核中查找
grep ".arch.info.init" * -nR
在include/asm-arm/mach/arch.h中,
/*
* Set of macros to define architecture features. This is built into
* a table by the linker.
*/
#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 \
};
关于MACHINE_START(_type,_name)的定义在include/configs/100ask24x0.h中
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,/*0x30000100*/
.init_irq = s3c24xx_init_irq,
.map_io = smdk2440_map_io,
.init_machine = smdk2440_machine_init,
.timer = &s3c24xx_timer,
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,
};
machine_desc 结构体在linux/asm-arm/arch/Arch.h中
struct machine_desc {
/*
* Note! The first four elements are used
* by assembler code in head-armv.S
*/
unsigned int nr; /* architecture number */
unsigned int phys_io; /* start of physical io */
unsigned int io_pg_offst; /* byte offset for io
* page tabe entry */
const char *name; /* architecture name */
unsigned long boot_params; /* tagged list */
unsigned int video_start; /* start of video RAM */
unsigned int video_end; /* end of video RAM */
unsigned int reserve_lp0 :1; /* never has lp0 */
unsigned int reserve_lp1 :1; /* never has lp1 */
unsigned int reserve_lp2 :1; /* never has lp2 */
unsigned int soft_reboot :1; /* soft reboot */
void (*fixup)(struct machine_desc *,
struct tag *, char **,
struct meminfo *);
void (*map_io)(void);/* IO mapping function */
void (*init_irq)(void);
struct sys_timer *timer; /* system tick timer */
void (*init_machine)(void);
};
(2)判断是否支持该单板
bl __lookup_machine_type @ r5=machinfo
(3)创建页表,由于链接地址是虚拟地址,需要创建页表与实际内存进行对应。
bl __create_page_tables
(4)使能mmu
adr lr, __enable_mmu @ return (PIC) address
add pc, r10, #PROCINFO_INITFUNC
(5)跳转到secondary_start_kernel
b secondary_start_kernel
函数在Linux/arch/arm/kernel/smp.c
asmlinkage void __cpuinit secondary_start_kernel(void)
{
}
Linux内核启动的第二阶段在
Linux/init/main.C
主要函数
asmlinkage void __init start_kernel(void)
{
printk(linux_banner);
setup_arch(&command_line);
/*解析uboot传入的启动参数*/
setup_command_line(command_line);
/*解析uboot传入的启动参数*/
parse_early_param();
console_init();
.......
rest_init();
}
在该函数中首先打印内核版本信息,
printk(linux_banner);
设置与体系结构相关的环境
setup_arch(&command_line);
/*解析uboot传入的启动参数*/
setup_command_line(command_line);
/*解析uboot传入的启动参数*/
在arch/arm/kernel/setup.c中
void __init setup_arch(char **cmdline_p)
{
struct tag *tags = (struct tag *)&init_tags;
struct machine_desc *mdesc;/*machine_desc 为前面linux/asm-arm/arch/Arch.h中定义的结构体*/
char *from = default_command_line;
setup_processor();
mdesc = setup_machine(machine_arch_type);
machine_name = mdesc->name;
/*.name = "SMDK2440",*/
if (mdesc->soft_reboot)
reboot_setup("s");
if (mdesc->boot_params)
tags = phys_to_virt(mdesc->boot_params);
/* .boot_params = S3C2410_SDRAM_PA + 0x100,=0x30000100,也是uboot启动的最后theKernel (0, bd->bi_arch_number, bd->bi_boot_params);(uboot/lib_arm/Armlinux.c)告诉内核的存放参数的地址gd->bd->bi_boot_params = 0x30000100(uboot/board/100ask24x0.c);*/
parse_early_param();
该函数同样在Linux/init/main.C下
void __init parse_early_param(void)
{
static __initdata int done = 0;
static __initdata char tmp_cmdline[COMMAND_LINE_SIZE];
if (done)
return;
/* All fall through to do_early_param. */
strlcpy(tmp_cmdline, boot_command_line, COMMAND_LINE_SIZE);
parse_args("early options", tmp_cmdline, NULL, 0, do_early_param);
done = 1;
}
初始化控制台
console_init();