u-boot分析(三)---boot命令实现以及内核的启动
上片博文总结出了u-boot的工作流程,今天我们来分析,u-boot的两个比较重要的内容
1. U-boot命令的实现
2. U-boot如何启动内核
l 命令实现
我们的u-boot可以解析输入的命令,比如print、setenv、saveenv等命令,我们下来对其的实现进行分析。
我们昨天分析到BL2最后停在了main_loop处,那么我们输入的命令肯定也是在这个函数中实现的,我们找到该函数,在main_loop函数中run_command函数很容易引起我们的关注,跳到该函数进行分析,在该函数中有下面几个比较重要的点
1. 从注释我们很容易知道这段代码是在对命令进行分离,并且u-boot支持’;’分离命令。
- /*
- * Find separator, or string end
- * Allow simple escape of ';' by writing "\;"
- */
- for (inquotes = , sep = str; *sep; sep++) {
- if ((*sep=='\'') &&
- (*(sep-) != '\\'))
- inquotes=!inquotes;
- if (!inquotes &&
- (*sep == ';') && /* separator */
- ( sep != str) && /* past string start */
- (*(sep-) != '\\')) /* and NOT escaped */
- break;
- }
2. 分离参数
- /* Extract arguments */
- if ((argc = parse_line (finaltoken, argv)) == ) {
- rc = -; /* no command at all */
- continue;
- }
3. 用第一个参数argv[0]在命令列表中寻找对应的命令,并返回一个cmd_tbl_t类型的实例。我们可以猜到这个结构体应该保函了有关命令的一系列内容。
- /* Look up command in command table */
- if ((cmdtp = find_cmd(argv[])) == NULL) {
- printf ("Unknown command '%s' - try 'help'\n", argv[]);
- rc = -; /* give up after bad command */
- continue;
- }
n 我们先看find_cmd,通过代码跟踪我们会在find_cmd_tbl函数中找到核心代码
- for (cmdtp = table;
- cmdtp != table + table_len;
- cmdtp++) {
- if (strncmp (cmd, cmdtp->name, len) == ) {
- if (len == strlen (cmdtp->name))
- return cmdtp; /* full match */
- cmdtp_temp = cmdtp; /* abbreviated command ? */
- n_found++;
- }
- }
通过上面代码我们知道了其查找方法,但是相信很多人和我一样很疑惑这个命令表到底在什么地方。
按照我们对上面代码的阅读,和猜测我们可以知道这个表的开始地址是table,我们可以轻松的找到table的来源。
- cmd_tbl_t *find_cmd (const char *cmd)
- {
- int len = &__u_boot_cmd_end - &__u_boot_cmd_start;
- return find_cmd_tbl(cmd, &__u_boot_cmd_start, len);
- }
通过上面代码我们知道table等于__u_boot_cmd_start,通过全局搜索,我们找到这个地址的来源是\arch\arm\cpu\armv7\u-boot.lds
- __u_boot_cmd_start = .;
- .u_boot_cmd : { *(.u_boot_cmd) }
- __u_boot_cmd_end = .;
在__u_boot_cmd_start和__u_boot_cmd_end之间放了一个.u_boot_cmd段,我们再对这个段名进行搜索找到了下面的宏
- #define Struct_Section __attribute__ ((unused,section (".u_boot_cmd")))//强制设置段属性为.u_boot_cmd
其肯定通过该宏又定义了什么东西,再经过搜索我们找到以下内容
- #define U_BOOT_CMD_COMPLETE(name,maxargs,rep,cmd,usage,help,comp) \
- cmd_tbl_t __u_boot_cmd_##name Struct_Section = \
- U_BOOT_CMD_MKENT_COMPLETE(name,maxargs,rep,cmd,usage,help,comp)
其又定义了一个宏,通过对宏的阅读我们可以知道,通过U_BOOT_CMD_COMPLETE这个宏可以定义一个cmd_tbl_t类型的结构体,并且将该结构体的段属性强制设置为.u_boot_cmd,再对这个宏搜索,找到的只有几个命令,完全对不上,但是我们又找到下面的宏
- #define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \
- U_BOOT_CMD_COMPLETE(name,maxargs,rep,cmd,usage,help,NULL)
通过对U_BOOT_CMD的搜索我们找到了大量的命令定义。
至此我们可以完全清楚了这个命令表示怎么来的,其是通过U_BOOT_CMD这样一个宏去定义命令。随便可以找到例子:
- U_BOOT_CMD(
- help, CONFIG_SYS_MAXARGS, , do_help,
- "print command description/usage",
- "\n"
- " - print brief description of all commands\n"
- "help command ...\n"
- " - print detailed usage of 'command'"
- );
n 再来看cmd_tbl_t结构体,其中保函了命令名,最大参数,以及对应函数等内容。
- 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 * const []);
- char *usage; /* Usage message (short) */
- #ifdef CONFIG_SYS_LONGHELP
- char *help; /* Help message (long) */
- #endif
- #ifdef CONFIG_AUTO_COMPLETE
- /* do auto completion on the arguments */
- int (*complete)(int argc, char * const argv[], char last_char, int maxv, char *cmdv[]);
- #endif
- };
至此我们可以通过上面的内容实现我们自己的简单u-boot命令,下面是我实现的hello命令
- #include <common.h>
- #include <command.h>
- int do_hello(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])
- {
- printf("hello u-boot");
- return ;
- }
- U_BOOT_CMD(
- hello, CONFIG_SYS_MAXARGS, , do_hello,
- "print hello",/*短帮助信息*/
- "\n hello cmd ............"//长帮帮助信息
- );
加入上面内容,并且修改common目录下的makefile,然后重新编译u-boot,就会完成我们自己的u-boot命令,至此我们的u-boot命令实现分析完毕。
l 启动内核
我们的u-boot可以通过nand、tftp等方式,将我们的内核加载至内存,对于这个过程今天就不重点去分析了,今天我们重点分析从内存中如何启动内核,我们都知道启动内核的时候要用到bootm命令,按照我们上面分析命令实现的经验,可以猜出其必然会去运行do_bootm函数。下面我们主要分析这个函数的实现。
我们的bootm只能启动uImage,然而uImage = zImage(真正的内核) + 头信息。所以我们首先来看看头信息:
- typedef struct image_header {
- uint32_t ih_magic; /* Image Header Magic Number */
- uint32_t ih_hcrc; /* Image Header CRC Checksum */
- uint32_t ih_time; /* Image Creation Timestamp */
- uint32_t ih_size; /* Image Data Size */
- uint32_t ih_load; /* Data Load Address */
- uint32_t ih_ep; /* Entry Point Address */
- uint32_t ih_dcrc; /* Image Data CRC Checksum */
- uint8_t ih_os; /* Operating System */
- uint8_t ih_arch; /* CPU architecture */
- uint8_t ih_type; /* Image Type */
- uint8_t ih_comp; /* Compression Type */
- uint8_t ih_name[IH_NMLEN]; /* Image Name */
- } image_header_t;
这里面存放了大量的内核信息,我们也可以找到do_bootm中用这些信息进行内核的校验,加载地址的校验等工作。
假设我们启动的是linux(u-boot支持多种系统启动,下面代码列出),我们的u-boot会执行到do_bootm_linux函数
- static boot_os_fn *boot_os[] = {
- #ifdef CONFIG_BOOTM_LINUX
- [IH_OS_LINUX] = do_bootm_linux,
- #endif
- #ifdef CONFIG_BOOTM_NETBSD
- [IH_OS_NETBSD] = do_bootm_netbsd,
- #endif
- #ifdef CONFIG_LYNXKDI
- [IH_OS_LYNXOS] = do_bootm_lynxkdi,
- #endif
- #ifdef CONFIG_BOOTM_RTEMS
- [IH_OS_RTEMS] = do_bootm_rtems,
- #endif
- #if defined(CONFIG_BOOTM_OSE)
- [IH_OS_OSE] = do_bootm_ose,
- #endif
- #if defined(CONFIG_CMD_ELF)
- [IH_OS_VXWORKS] = do_bootm_vxworks,
- [IH_OS_QNX] = do_bootm_qnxelf,
- #endif
- #ifdef CONFIG_INTEGRITY
- [IH_OS_INTEGRITY] = do_bootm_integrity,
- #endif
- };
下面我们来分析do_bootm_linux函数主要有以下内容
a) 和内核进行交接工作,为内核设置启动参数
- #if defined (CONFIG_SETUP_MEMORY_TAGS) || \
- defined (CONFIG_CMDLINE_TAG) || \
- defined (CONFIG_INITRD_TAG) || \
- defined (CONFIG_SERIAL_TAG) || \
- defined (CONFIG_REVISION_TAG)
- setup_start_tag (bd);
- #ifdef CONFIG_SERIAL_TAG
- setup_serial_tag (¶ms);
- #endif
- #ifdef CONFIG_REVISION_TAG
- setup_revision_tag (¶ms);
- #endif
- #ifdef CONFIG_SETUP_MEMORY_TAGS
- setup_memory_tags (bd);
- #endif
- #ifdef CONFIG_CMDLINE_TAG
- setup_commandline_tag (bd, commandline);
- #endif
- #ifdef CONFIG_INITRD_TAG
- if (images->rd_start && images->rd_end)
- setup_initrd_tag (bd, images->rd_start, images->rd_end);
- #endif
- setup_end_tag(bd);
- #endif
这些代码主要是将参数,按照固定的格式写到固定的地方。内核启动后将会去这个地址读取参数。
b) 跳到入口地址,启动内核
- kernel_entry = (void (*)(int, int, uint))images->ep;
- kernel_entry(, machid, bd->bi_boot_params);
machid:我们的机器ID
bd->bi_boot_params:刚才提到的参数的地址
至此我们今天的工作全部结束。
u-boot分析(三)---boot命令实现以及内核的启动的更多相关文章
- [ipsec][strongswan] strongswan源码分析-- (三) xfrm与strongswan内核接口分析
目录 strongwan sa分析(三) xfrm与strongswan内核接口分析 1. strongswan的实现 2. 交互机制 4. xfrm的消息通信的实现 strongwan sa分析(三 ...
- spring boot / cloud (三) 集成springfox-swagger2构建在线API文档
spring boot / cloud (三) 集成springfox-swagger2构建在线API文档 前言 不能同步更新API文档会有什么问题? 理想情况下,为所开发的服务编写接口文档,能提高与 ...
- Spring Boot 2 (三):Spring Boot 2 相关开源软件
Spring Boot 2 (三):Spring Boot 2 相关开源软件 一.awesome-spring-boot Spring Boot 中文索引,这是一个专门收集 Spring Boot 相 ...
- Spring Boot 第三弹,一文带你了解日志如何配置?
前言 日志通常不会在需求阶段作为一个功能单独提出来,也不会在产品方案中看到它的细节.但是,这丝毫不影响它在任何一个系统中的重要的地位. 今天就来介绍一下Spring Boot中的日志如何配置. Spr ...
- Linux内核及分析 第三周 Linux内核的启动过程
实验过程: 打开shell终端,执行以下命令: cd LinuxKernel/ qemu -kernel linux-3.18.6/arch/x86/boot/bzImage-initrd rootf ...
- Linux内核分析 实验三:跟踪分析Linux内核的启动过程
贺邦 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程 http://mooc.study.163.com/course/USTC-1000029000 一. 实验过程 ...
- Linux内核设计第三周学习总结 跟踪分析Linux内核的启动过程
陈巧然 原创作品 转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 实验步骤 登陆实验楼虚 ...
- Linux第三周——跟踪分析内核的启动过程
跟踪分析内核的启动过程实验 张潇月<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 这周主要学习的是对内核 ...
- 第1阶段——uboot分析之查找命令run_command函数和命令定义过程(6)
本节主要学习,run_command函数命令查找过程,命令生成过程 1.run_command函数命令查找过程分析:在u-boot界面中(main_loop();位于u-boot-1.1.6/comm ...
随机推荐
- FPGA基础学习(2) -- FIFO IP核(Quartus)
ALTERA在LPM(library of parameterized mudules)库中提供了参数可配置的单时钟FIFO(SCFIFO)和双时钟FIFO(DCFIFO).FIFO主要应用在需要数据 ...
- java 在web应用中获取本地目录和服务器上的目录不一致的问题
先来讲讲我所遇到的问题.最近有个新的项目添加新的功能. 修改之后部署到服务器上面发现取到classpath目录跑到别的地方去了.在本地测试却正常. 当时毛的着火了.硬是想不懂什么问题. 终于发现了这个 ...
- Vscode 配置 maven debug
# maven.cmd 上方设置此变量 set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address= ...
- C语言中变量的存储类型
在C语言中,对变量的存储类型说明有以下四种: auto 自动变量register 寄存器变量extern 外部变量static 静态变量 自动变量和寄存器变量属于动态存储方式,外部变量和静态变 ...
- POJ2528 Mayor's posters(线段树+离散化)
题意 : 在墙上贴海报, n(n<=10000)个人依次贴海报,给出每张海报所贴的范围li,ri(1<=li<=ri<=10000000).求出最后还能看见多少张海报. 分析 ...
- 在IntelliJ IDEA中配置Google Java Code Style及代码格式化快捷键
google-java-format plugin should intercept the “Reformat Code” action in IDEA (Ctrl+Alt+L) and apply ...
- 在本地安装oracle-maven库
mvn install:install-file -DgroupId=com.oracle -DartifactId=ojdbc14 -Dversion=10.2.0.4.0 -Dpackaging= ...
- Tiles框架入门教程
1.为何选用Tiles 刚接触Java Web开发的人都知道,JSP中可以通过include标签动态插入一个JSP页面.在了解这个功能后可能会兴奋不已,因为这样可以实现多个JSP页面共用一个JSP的内 ...
- 配置编译器(GCC和GFortran)
平台信息 Description: CentOS Linux release 7.6.1810 (Core) 检查环境 $ gfortran -v $ gcc -v 安装 GCC和Fortran 环境 ...
- 20181031 temp
https://wiki.jenkins.io/display/JENKINS/M2+Release+Plugin https://issues.jenkins-ci.org/browse/JENKI ...