启动第一阶段分析
(学习笔记,有错见谅,欢迎指出)
以下为uboot启动的第一阶段,即分析start.s。
reset函数
-
reset函数
首先第一条指令跳转到reset函数,那么我们就对reset函数的功能进行分析。- 设置CPU为管理模式(SVC32)。
- 关闭看门狗
- 屏蔽中断
- 进行cpu初始化(调用cpu_init_crit函数,主要是SDRAM初始化)
其中,先判断代码实际代码位置(_start)与链接地址(_TEXT_BASE)是否相等,若不等,证明为将代码拷贝至SDRAM?处。(若)#ifndef CONFIG_SKIP_LOWLEVEL_INIT adr r0, _start /* r0 <- current position of code */ ldr r1, _TEXT_BASE /* test if we run from flash or RAM */ cmp r0, r1 /* dont reloc during debug */ blne cpu_init_crit #endif
- 设置栈
栈中保存的数据分别为以下内容,起始地址为_TEXT_BASE规定的0x33F8 0000,然后是堆+环境数据区(256K),接下来是初始化数据区,再接下来是中断IRQ+FIQ的栈值(如果初始化中使用了中断),最后是为终止异常预留的12字节栈。
详细请看设置堆栈sp指针
- 初始化时钟
- 将代码(什么代码)从flash读取到SDRAM中即重定位,调用“CopyCode2Ram”函数。
- 清BSS段
BSS段通常是指用来存放程序中未初始化的全局变量和静态变量的一块内存区域 - 调用start_armboot函数
-
总结:以上为U-boot第一阶段,主要初始化硬件相关
start_armboot分析
第二阶段初始化
U-boot的核心工作为启动内核,主要操作为:
- 从FLASH中读出内核
- nor flash
- nand flash
- 启动读出的内核
以下为具体分析。
- gd指针(没讲)
- 一系列初始化
每种初始化都对应着相应的c函数。board_init中有两个变量的存储地址比较重要,分别为bi_arch_number和bi_boot_params,存在MACH_TYPE_SMDK2410(具体??193?)和0x30000100处。init_fnc_t *init_sequence[] = { cpu_init, /* basic cpu dependent setup */ board_init, /* basic board dependent setup */ interrupt_init, /* set up exceptions */ env_init, /* initialize environment */ init_baudrate, /* initialze baudrate settings */ serial_init, /* serial communications setup */ console_init_f, /* stage 1 init of console */ display_banner, /* say that we are here */ };
- 堆(mem_malloc_init)
视频中又说是192k空间??回头再详细分析(自己) - 中间穿插有nand_init();与flash_init ();分别初始化nand flash与nor flash(为什么第二个对应nor??)。
此时系统已可以读写FLash。 - 初始化环境变量
环境变量分为两种:- 系统默认
- flash上保存的
- USB与调试器相关初始化函数等等相关初始化函数
- 最终调用main_loop()函数
main_loop分析
- s = getenv (“bootdelay”);
只讲了=10,字符串转化为数字,此函数为设定倒计时时间,在下边函数有用到。 - s = getenv (“bootcmd”);
run_comman(s),此时便启动了内核(由第4条插入)
在本例程中,bootcmd为:nand read.jffs2 0x30007FC0 kernel;bootm 0x30007FC0 。
指令的分析为:在nand flash中从kernel分区读出jffs到0x30007FC0; 在地址0x30007FC0 启动内核。 - 没说清楚倒计时未结束时按下任意键????
- run_command()
执行command指令,变量s只是得到环境变量。此函数为U-boot启动的核心,因为最终都由此函数完成最终操作。 - U-boot界面:
先读取串口的数据(readline()),再执行run_command。
run_command()分析
-
解析指令字符
举例: 当输入md.w 0时
会在内存内存储 argv[0] = md.w argw[1] = 0。
此外还支持分号式输入两个命令。 -
查找命令cmdtp = find_cmd(argv[0]));
2.1 其中argv[0]存储输入的指令字符
2.2 find_cmd查找命令函数
首先,通过链接脚本中的__u_boot_cmd_start与__u_boot_cmd_end确定查找范围(大致理解),如果找到匹配命令,则返回该指令,未对应则++继续查找for (cmdtp = &__u_boot_cmd_start;//cmd起始位置 cmdtp != &__u_boot_cmd_end;//cmd终止位置 cmdtp++) if (strncmp (cmd, cmdtp->name, len) == 0) { if (len == strlen (cmdtp->name)) return cmdtp; /* full match */ cmdtp_temp = cmdtp; /* abbreviated command ? */ n_found++; }
2.3 cmdtp为结构体
– 第一个参数:添加的命令的名字
– 第二个参数:添加的命令最多有几个参数(注 意,假如你设置的参数个数是3,而实际的参数个数是4,那么执行命令会输出帮助信息的)
– 第三个参数:是否重复(1重复,0不重复)(即按下Enter键的时候,自动执行上次的命令)
– 第四个参数:执行函数,即运行了命令具体做啥会在这个函数中体现出来,调用了那些函数。
– 第五个参数:帮助信息(short)
– 第六个参数:帮助信息(long)
– 第七个参数:个人猜测代码补全??不确定,回头分析。struct cmd_tbl_s { char *name; /* Command Name */ int maxargs; /* maximum number of arguments */ int repeatable; /* autorepeat allowed? */ /* Implementation function */ int (*cmd)(struct cmd_tbl_s *, int, int, char *[]); char *usage; /* Usage message (short) */ #ifdef CFG_LONGHELP char *help; /* Help message (long) */ #endif #ifdef CONFIG_AUTO_COMPLETE /* do auto completion on the arguments */ int (*complete)(int argc, char *argv[], char last_char, int maxv, char *cmdv[]); #endif };
-
bootm分析
首先标记有bootm的函数为U_BOOT_CMD( bootm, CFG_MAXARGS, 1, do_bootm, "bootm - boot application image from memory\n", "[addr [arg ...]]\n - boot application image stored in memory\n" "\tpassing arguments 'arg ...'; when booting a Linux kernel,\n" "\t'arg' can be the address of an initrd image\n" #ifdef CONFIG_OF_FLAT_TREE "\tWhen booting a Linux kernel which requires a flat device-tree\n" "\ta third argument is required which is the address of the of the\n" "\tdevice-tree blob. To boot that kernel without an initrd image,\n" "\tuse a '-' for the second argument. If you do not pass a third\n" "\ta bd_info struct will be passed instead\n" #endif );
接下来分析查找 __u_boot_cmd_start 与__u_boot_cmd_end 之间的.u_boot_cmd : { *(.u_boot_cmd) },其结果为:
#define Struct_Section __attribute__ ((unused,section (".u_boot_cmd")))
再查找U_BOOT_CMD的定义,可以发现以下定义
#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help)\ cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage, help}
接下来我们将宏定义展开一一对应
3.1 name = bootm
3.2 Struct_Section = attribute ((unused,section (".u_boot_cmd")))
3.3 maxargs = CFG_MAXARGS
3.4 rep = 1
3.5 cmd = do_bootm
3.6 usage = “bootm - boot application image from memory\n”
3.7 help = “[addr [arg …]]\n - boot application image stored in memory\n”
“\tpassing arguments ‘arg …’; when booting a Linux kernel,\n”
“\t’arg’ can be the address of an initrd image\n”
最终整合后的字符串为cmd_tbl_t __u_boot_cmd_bootm __attribute__ ((unused,section (".u_boot_cmd")))= {bootm, CFG_MAXARGS, 1, do_bootm, usage(省略), help(省略)}
相当于定义了上述的结构体,并将结构的段强制转换为".u_boot_cmd"
-
自己制作一个hello world的U-boot功能(省略)