kernel的启动流程分析(未完)
来源:互联网 发布:魔术师约翰逊数据统计 编辑:程序博客网 时间:2024/05/14 08:04
文章只做学习记录。。。。。
先说一下内核启动时的工作:
1.处理uboot传入的参数(比如mem的大小,bootargs的参数)2.挂接根文件系统,启动应用程序。
因为有可能编译出来的内核比较大,在运行内核之前会先进行自解压的操作,自解压的那段代码不分析(以后有时间分析)。
那么我们就首先分析下kernel是如何处理uboot传入的参数的,文件arch/arm/kernel/head.S
分析之前,先看看uboot是如何启动内核的,
theKernel (0, bd->bi_arch_number, bd->bi_boot_params);
这段指令跳转到了内核的入口地址,并且传入了三个参数,0,bd->bi_arch_number(机器ID),bd->bi_boot_params(TAG传入的起始地址)。
由寄存器的交互规则可知bd->bi_arch_number是存放在r1寄存器里的(具体的交互规则以后分析)。
ENTRY(stext) //这个地址等同于theKernelmsrcpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE @ ensure svc mode@ and irqs disabledmrcp15, 0, r9, c0, c0@ get processor idbl__lookup_processor_type@ r5=procinfo r9=cpuidmovsr10, r5@ invalid processor (r5=0)?beq__error_p@ yes, error 'p'bl__lookup_machine_type@ r5=machinfomovsr8, 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. */ldrr13, __switch_data@ address to jump to after@ mmu has been enabledadrlr, __enable_mmu@ return (PIC) addressaddpc, r10, #PROCINFO_INITFUNC
上面的代码主要做的工作如下:
一、判断是否支持这个CPU __lookup_processor_type
二、__lookup_machine_type:判断是否支持这个单板,即bd->bi_arch_number
下面看看函数__lookup_machine_type的实现:
3: .long . //3b的地址 .long __arch_info_begin .long __arch_info_end
__lookup_machine_type:adrr3, 3b //r3 == 3b的地址,真正的地址,物理地址ldmiar3, {r4, r5, r6} //r3 == '.'(3b的虚拟地址) r5 == __arch_info_begin r6 == __arch_info_endsubr3, r3, r4@ get offset between virt&phys 获取虚拟地址与物理地址偏差addr5, r5, r3@ convert virt addresses to 获取r5和r6的物理地址addr6, r6, r3@ physical address space1:ldrr3, [r5, #MACHINFO_TYPE]@ get machine typeteqr3, r1@ matches loader number? 判断是否和uboot传进来的机器id匹配beq2f@ found 匹配的话,直接跳出 addr5, r5, #SIZEOF_MACHINE_DESC@ next machine_desccmpr5, r6blo1bmovr5, #0@ unknown machine2:movpc, lr
解析:
r3 = 3b的地址,即:
3: .long .
.long __arch_info_begin
.long __arch_info_end
__arch_info_begin和__arch_info_end分别是什么???
搜索一下发现这两个变量的定义在链接脚本中:
__arch_info_begin = .;*(.arch.info.init)__arch_info_end = .;
在这两个地址之间有一个*(.arch.info.init)段,这是一个什么段呢?继续搜索:发现在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 \};定义了一个宏MACHINE_START(_type,_name)结构体machine_desc放在了.arch.info.init段中。那么继续搜索MACHINE_START,发现在arch\arm\mach-s3c2440\Mach-smdk2440.c使用到了这个宏:代码如下: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下面将这段代码展开:static const struct machine_desc __mach_desc_S3C2440 \ __used \ __attribute__((__section__(".arch.info.init"))) = { \ ====>属性就是将段强制转换为了.arch.info.init .nr = MACH_TYPE_S3C2440, \ ====>这就是uboot传进来的机器ID,将它存放在了.arch.info.init段内 .name = "SMDK2440", .phys_io = S3C2410_PA_UART, .io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc, .boot_params = S3C2410_SDRAM_PA + 0x100, //=====>这个就是0x30000100也就是bi->boot_params,所以感觉uboot传进来的TAG地址是不是根据内核中的这个地址决定的??? .init_irq = s3c24xx_init_irq, .map_io = smdk2440_map_io, .init_machine = smdk2440_machine_init, .timer = &s3c24xx_timer,};
下面列出machine_desc的定义
struct machine_desc {/* * Note! The first four elements are used * by assembler code in head-armv.S */unsigned intnr;/* architecture number*/unsigned intphys_io;/* start of physical io*/unsigned intio_pg_offst;/* byte offset for io * page tabe entry*/const char*name;/* architecture name*/unsigned longboot_params;/* tagged list*/unsigned intvideo_start;/* start of video RAM*/unsigned intvideo_end;/* end of video RAM*/unsigned intreserve_lp0 :1;/* never has lp0*/unsigned intreserve_lp1 :1;/* never has lp1*/unsigned intreserve_lp2 :1;/* never has lp2*/unsigned intsoft_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);};
对于内核支持的单板的machine_desc信息都会存放在.arch.info.init段中,我们的这个单板的ID为MACH_TYPE_S3C2440,通过__lookup_machine_type进行匹配
2440 2410 所支持的单板信息都保存在.arch.info.init段中,然后从__arch_info_begin 到__arch_info_end中的信息依次和机器ID比对。如果吻合的话。表示这个内核支持这个单板。
到这里uboot传进来的机器ID和内核已经匹配了。
三、建立页表,启动mmu 。__create_page_tables 和 __enable_mmu
四、跳转到start_kernel(这也是内核的第一个c函数start_kernel)
ldr r13, __switch_data
__mmap_switched =========> start_kernel
内核的启动流程参照开发手册上面的一幅图
大致总结一下内核的启动流程:
arch/am/kernel/head.Sstart_kernel setup_arch //解析uboot传入的启动参数 setup_command_line(command_line);//解析uboot传入的启动参数 parse_early_param do_early_param //从 __setup_start到 __setup_end;执行标志early的函数,这个没哟走 obsolete_checksetup //走的是这个从 __setup_start到 __setup_end;执行标志非early的函数 rest_init kernel_init prepare_namespace mount_root //挂接根文件系统 init_post //执行应用程序
在挂接根文件系统的时候内核是如何识别的??
root=/dev/mtdblock3
static int __init root_dev_setup(char *line){ strlcpy(saved_root_name, line, sizeof(saved_root_name)); return 1;}__setup("root=", root_dev_setup);
继续搜索看看__setup()宏的实现:定义在: include\linux\Init.h
#define __setup(str, fn)\__setup_param(str, fn, fn, 0)
__setup_param的定义:
#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")))\__attribute__((aligned((sizeof(long)))))\= { __setup_str_##unique_id, fn, early }
将__setup("root=", root_dev_setup);展开:
#define __setup_param("root=", root_dev_setup, root_dev_setup, 0)\static char __setup_str_root_dev_setup[] __initdata = "root=";\static struct obs_kernel_param __setup_root_dev_setup\__attribute_used__\__attribute__((__section__(".init.setup")))\__attribute__((aligned((sizeof(long)))))\= { __setup_str_root_dev_setup, root_dev_setup, 0}
定义了一个.init.setup段obs_kernel_param类型的结构体
定义如下:
struct obs_kernel_param {const char *str;int (*setup_func)(char *);int early;};
我们先搜索:
__setup_start = .; .init.setup : { *(.init.setup) } __setup_end = .;
继续搜索__setup_start的使用地方(发现有两个地方用到了):
第一个地方:
static int __init obsolete_checksetup(char *line){struct obs_kernel_param *p;int had_early_param = 0;p = __setup_start;do {int n = strlen(p->str);if (!strncmp(line, p->str, n)) {if (p->early) {/* Already done in parse_early_param? * (Needs exact match on param part). * Keep iterating, as we can have early * params and __setups of same names 8( */if (line[n] == '\0' || line[n] == '=')had_early_param = 1;} else if (!p->setup_func) {printk(KERN_WARNING "Parameter %s is obsolete," " ignored\n", p->str);return 1;} else if (p->setup_func(line + n))return 1;}p++;} while (p < __setup_end);return had_early_param;}
第二个地方:
static int __init do_early_param(char *param, char *val){struct obs_kernel_param *p;for (p = __setup_start; p < __setup_end; p++) {if (p->early && strcmp(param, p->str) == 0) {if (p->setup_func(val) != 0)printk(KERN_WARNING "Malformed early option '%s'\n", param);}}/* We accept everything at this stage. */return 0;}
经过分析do_early_param在p->early == 1的时候才会执行p->setup_func(val),而我们传下来的p->early == 0,所以使用的是第一个地方obsolete_checksetup,这个p->setup_func就是前面所讲的
root_dev_setup,这个函数就将分区的信息赋值给了 saved_root_name。
root=/dev/mtdblock3也是在内核中写死的。要和uboot对应
grep -inr "\"Bootloader\"" *
本板子的mtd分区定义在 arch\arm\plat-s3c24xx\Common-smdk.c
static struct mtd_partition smdk_default_nand_part[] = {[0] = { .name = "bootloader", .size = 0x00040000,.offset= 0,},[1] = { .name = "params", .offset = MTDPART_OFS_APPEND, .size = 0x00020000,},[2] = { .name = "kernel", .offset = MTDPART_OFS_APPEND, .size = 0x00200000,},[3] = { .name = "root", .offset = MTDPART_OFS_APPEND, .size = MTDPART_SIZ_FULL,}};
- kernel的启动流程分析(未完)
- DragonBoard 410c的Little Kernel启动流程分析
- linux kernel 启动流程简单分析
- BK1080 流程分析(未完待续)
- asterisk1.8启动信息分析(未完)
- [uboot] uboot启动kernel篇(二)——bootm跳转到kernel的流程
- [uboot] uboot启动kernel篇(二)——bootm跳转到kernel的流程
- [kernel 启动流程] (第一章)概述
- [kernel 启动流程] (第一章)概述
- [kernel 启动流程] (第一章)概述
- kernel启动流程概要
- Kernel启动流程
- MTk kernel启动流程
- kernel启动流程概要
- kernel启动流程
- kernel启动流程
- kernel 启动流程
- linux kernel启动流程
- vs如何检测内存泄漏
- C++多线程编程入门(转)
- 统一开发环境——HTML5跨平台一站式应用开发、调试和部署工具
- 最大流算法
- Ubuntu 10.04 拼音输入法
- kernel的启动流程分析(未完)
- (转)Android中APK安装过程及原理解析
- 关于jquery内容验证代码
- Uva-1203-Argus
- Linux IP 命令使用举例
- ImageView大全
- hive 创建外表
- ORACLE与SQL SERVER的区别
- (转)Android中获取应用程序(包)的信息-----PackageManager的使用(一)