u-boot启动第二阶段以及界面命令分析
u-boot第一阶段完成了一些平台相关的硬件的配置,第一阶段所做的事情也是为第二阶段的准备,我们知道在第一阶段最后时搭建好C运行环境,之后调用了start_armboot(),那么很显然第二阶段从start_armboot()函数入手。第二阶段u-boot所要做的工作就是从Flash中读取内核,然后将内核加载到RAM中。并且引导内核启动。
start_armboot()函数的路径为:./lib_arm/boad.c
进入start_armboot()函数,找到init_sequence
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) { ) { hang (); } }
init_sequence,这个函数指针数组初始化了一系列变量,如下所示
init_fnc_t *init_sequence[] = { cpu_init, /* basic cpu dependent setup cpu初始化 */ 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 串口初始化,串口初始化好了就可以从串口看到打印消息了,serial_init的路径为/cpu/arm920t/s3c24x0/*/ console_init_f, /* stage 1 init of console 终端初始化 */ display_banner, /* say that we are here */ dram_init, /* configure available RAM banks 内存初始化 ,函数实现路径为/board/sndk2410/中*/ int dram_init (void) { gd->bd->bi_dram[].start = PHYS_SDRAM_1; //内存的起始地址 gd->bd->bi_dram[].size = PHYS_SDRAM_1_SIZE;//内存大小 ; } display_dram_config, NULL, };
紧接着从start_armboot可以看到flash_init,这是很重要的一个函数,因为咱们明确说了,第二阶段就是将内核从Flash中读取出来。
#ifndef CFG_NO_FLASH /* configure available FLASH banks */ size = flash_init (); display_flash_config (size); #endif /* CFG_NO_FLASH */
mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);
这个mem_malloc_init是对内存中堆区的初始化。
env_relocate ();/* initialize environment */
env_relocate是导入环境变的初始化,因为u-boot在启动过程中要输入一些信息,比如bootdelay的值啊,serverip,开发板的ip地址ipaddr,bootargs等,这些变量的赋值都是通过环境变量来赋值的。环境变量的来源有两种,第一种是默认的,第二种是Flash中保存的,当u-boot从Flash中读取不到变量时,他自己就设置默认的环境变量,用户在u-boot启动时可以设置变量,设置的变量都保存在Flash中,下一次上电时,u-boot会自动读取。
本阶段还有一些其他初始化,如usb_init(); devices_init ();
start_armboot中最后有一段代码如下
for (;;) { main_loop (); }
这是个死循环,在前面所有的设置之后,就开始进入死循环,调用main_loop()。通常情况下,u-boot启动之后会进入一个界面,等待用户输入命令,比如u-boot启动之后会显示是否download u-boot,也会显示download kernel等,这个等待的状态就是执行main_loop()函数。
总结一下start_armboot函数实现的主要功能:1.对Flash的操作(原因很简单,要搬运内核)。2.进入main_loop。
接下来分析main_loop函数
在main_loop中有这样一段代码
s = getenv ("bootdelay"); bootdelay = s ? () : CONFIG_BOOTDELAY;
getenv ("bootdelay")获取延时时间 ,然后将s转化为整数类型。这个延时时间就是在几秒之后,内核将会启动。
&& s && !abortboot (bootdelay)) { printf("Booting Linux ...\n"); run_command (s, ); }
(1)循环倒计时, !abortboot (bootdelay)意思就是没有按下停止按键时,则booting Linux。
(2)当按下停止键时,则程序会运行到
run_command("menu", 0);
(3)当按下停止键,且输入选项时,u-boot会进行选项判断,判断代码为:
len = readline (CFG_PROMPT); flag = ; /* assume no special flags for now */ ) strcpy (lastcommand, console_buffer); ) flag |= CMD_FLAG_REPEAT;
在终端输入命令print或者printenv就会打印出bootcmd的参数值。如下:bootcmd=nand read.jffs2 0x30007FC0 kernel; bootm 0x30007FC0
解释一下这些参数的含义:
nand read.jffs 0x30007fc0 kernel将kernel从Flash中读到内存中,地址为0x30007fc0的内存中,kernel是Flash上的一个分区。
bootm 0x30007fc0 bootm是启动参数,从0x30007fc0开始启动。所以,内核启动时依赖宇bootcmd这个变量的参数。
判断完毕之后还是会执行:run_command (lastcommand, flag);
总结: u-boot界面选项所依赖的函数readline (CFG_PROMPT),run_command (lastcommand, flag);
内核启动时要执行的函数s = getenv ("bootcmd"),run_command (s, 0),run_command就是为内核启动传入了参数。
进入run_command分析
/* Look up command in command table */ ])) == NULL) { printf (]); rc = -; /* give up after bad command */ continue; }
从终端输入命令argv[0],则查找结果返回给cmdtp,那么cmdtp究竟是什么?在include/command.h中
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 };
name 是命令的名字,maxargs 参数的最大个数,repeatable 是否重复,
int (*cmd)(struct cmd_tbl_s *, int, int, char *[])是一个函数指针,这个函数中执行了命令需要的操作。
进入common/command.c 继续分析find_cmd:
for (cmdtp = &__u_boot_cmd_start; cmdtp != &__u_boot_cmd_end; cmdtp++) { ) { if (len == strlen (cmdtp->name)) return cmdtp; /* full match */ cmdtp_temp = cmdtp; /* abbreviated command ? */ n_found++; } }
从这段代码中可以看书,所有的命令放在__u_boot_cmd中,因为这段代码是从__u_boot_cmd_start与__u_boot_cmd_end
中。并且这两个宏在u-boot.lds中有。看下边:
. = .; __u_boot_cmd_start = .; .u_boot_cmd : { *(.u_boot_cmd) } __u_boot_cmd_end = .;
.u_boot_cmd这个段的定义为
#define Struct_Section __attribute__ ((unused,section (".u_boot_cmd")))
由以上分析可得:u-boot的所有启动命令都存在放在.u_boot_cm段中。当用户想执行某一个命令时,就会进入find_cmd函数进行命令查找。
那么启动命令bootm 0x30007FC0是如何执行的?将bootm找到,在/common/cmd_bootm.c中的宏函数
U_BOOT_CMD( bootm, CFG_MAXARGS, , 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的定义为
#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}
cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage, help}等价于
cmd_tbl_t __u_boot_cmd_bootm __attribute__ ((unused,section (".u_boot_cmd"))) ={“bootm”, CFG_MAXARGS, 1, do_bootm, usage, help}
解释一下上边这行代码:
##name为bootm, Struct_Section为__attribute__ ((unused,section (".u_boot_cmd"))),代码定义了一个cmd_tbl_t类型的结构体__u_boot_cmd_bootm,将段属性设置为.u_boot_cmd(这儿是强制性转换)。给U_BOOT_CMD传入的参数和结构体cmd_tbl_t的属性一致{“bootm”, CFG_MAXARGS, 1, do_bootm, usage, help},name 就是要执行的命令,cmd就是实现这个命令的函数。do_bootm这个命令的实现就在/common/cmd_bootm.c中实现。
所以,bootm 0x30007FC0这个命令的执行,就是将bootm传给name ,而执行函数就是U_BOOT_CMD中的do_bootm。
u-boot启动第二阶段以及界面命令分析的更多相关文章
- Spring Boot 启动(一) SpringApplication 分析
Spring Boot 启动(一) SpringApplication 分析 Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698.html ...
- centos 7.0 更改启动环境/启动进入图形界面/命令行模式
当前我桌面模式 systemctl set-default multi-user.target ln -sf /lib/systemd/system/multi-user.target /etc/sy ...
- Spring Boot启动命令参数详解及源码分析
使用过Spring Boot,我们都知道通过java -jar可以快速启动Spring Boot项目.同时,也可以通过在执行jar -jar时传递参数来进行配置.本文带大家系统的了解一下Spring ...
- [Spring Boot] Spring Boot启动过程源码分析
关于Spring Boot,已经有很多介绍其如何使用的文章了,本文从源代码(基于Spring-boot 1.5.6)的角度来看看Spring Boot的启动过程到底是怎么样的,为何以往纷繁复杂的配置到 ...
- Spring Boot启动过程源码分析--转
https://blog.csdn.net/dm_vincent/article/details/76735888 关于Spring Boot,已经有很多介绍其如何使用的文章了,本文从源代码(基于Sp ...
- spring boot启动原理步骤分析
spring boot最重要的三个文件:1.启动类 2.pom.xml 3.application.yml配置文件 一.启动类->main方法 spring boot启动原理步骤分析 1.spr ...
- Spring Boot 启动原理分析
https://yq.aliyun.com/articles/6056 转 在spring boot里,很吸引人的一个特性是可以直接把应用打包成为一个jar/war,然后这个jar/war是可以直接启 ...
- Spring Boot启动流程分析
引言 早在15年的时候就开始用spring boot进行开发了,然而一直就只是用用,并没有深入去了解spring boot是以什么原理怎样工作的,说来也惭愧.今天让我们从spring boot启动开始 ...
- Spring Boot 启动(四) EnvironmentPostProcessor
Spring Boot 启动(四) EnvironmentPostProcessor Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698. ...
随机推荐
- robot framework Selenium2关键字介绍
*** Settings *** Library Selenium2Library *** Keywords *** Checkbox应该不被选择 [Arguments] ${locator} Che ...
- BZOJ4975 区间翻转
这个范围给的很像区间dp之类的,想了半天没一点思路,滚去看了一眼status被吓傻了.然后瞎猜了一发结论就过掉了. 求出逆序对数,判断是否为奇数即可.因为翻转区间会把将这段区间的逆序对取反,而长度为4 ...
- Contest 8
A:做法应该很多,比较好想的是每个点都往上倍增找到其能更新到的点. #include<iostream> #include<cstdio> #include<cstdli ...
- 《转》理解Object.defineProperty的作用
对象是由多个名/值对组成的无序的集合.对象中每个属性对应任意类型的值.定义对象可以使用构造函数或字面量的形式: var obj = new Object; //obj = {} obj.name = ...
- C++四种类型转化
2018-08-02 (星期四)C++类型转换:static_cast提供编译时期静态类型检测: static_cast <type-id> (expression) 1)完成 ...
- 【BZOJ3566】概率充电器(动态规划)
[BZOJ3566]概率充电器(动态规划) 题面 BZOJ Description 著名的电子产品品牌 SHOI 刚刚发布了引领世界潮流的下一代电子产品--概率充电器: "采用全新纳米级加工 ...
- URAL1519 Formula 1 【插头dp】
题目链接 URAL1519 题解 看题型显然插头\(dp\) 考虑如何设计状态 有这样一个方案 当我们决策到某个位置 轮廓线长这样 你会发现插头一定是相互匹配的 所以我们实际上可以把状态用括号序列表示 ...
- 【乱搞】【CF1095E】 Almost Regular Bracket Sequence
Description 给定一个长度为 \(n\) 的小括号序列,求有多少个位置满足将这个位置的括号方向反过来后使得新序列是一个合法的括号序列.即在任意一个位置前缀左括号的个数不少于前缀右括号的个数, ...
- TCP粘包处理
TCP(transport control protocol,传输控制协议)是面向连接的,面向流的,提供高可靠性服务.收发两端(客户端和服务器端)都要有一一成对的socket, 因此,发送端为了将多个 ...
- Linux常用网络工具:fping主机扫描
Linux下有很多强大网络扫描工具,网络扫描工具可以分为:主机扫描.主机服务扫描.路由扫描等. fping是一个主机扫描工具,相比于ping工具可以批量扫描主机. fping官方网站:http://f ...