uboot启动内核的实现
前面我们分析了uboot 的整个流程,我们知道uboot启动以后所有功能都是通过命令来实现的,启动kernel就是执行了bootcmd里面的命令。命令执行过程在uboot中是非常重要的现在我们就来看uboot命令的实现过程。
在main_loop()代码 中可以知道,uboot处理命令的函数是run_command()
代码在bootable\bootloader\uboot-imx\common\main.c中
- int run_command (const char *cmd, int flag)
- {
- cmd_tbl_t *cmdtp;
- char cmdbuf[CONFIG_SYS_CBSIZE]; /* working copy of cmd */
- char *token; /* start of token in cmdbuf */
- char *sep; /* end of token (separator) in cmdbuf */
- char finaltoken[CONFIG_SYS_CBSIZE];
- char *str = cmdbuf;
- char *argv[CONFIG_SYS_MAXARGS + 1]; /* NULL terminated */
- int argc, inquotes;
- int repeatable = 1;
- int rc = 0;
- #ifdef DEBUG_PARSER
- printf ("[RUN_COMMAND] cmd[%p]=\"", cmd);
- puts (cmd ? cmd : "NULL"); /* use puts - string may be loooong */
- puts ("\"\n");
- #endif
- clear_ctrlc(); /* forget any previous Control C 清除所有的ctrl+c*/
- if (!cmd || !*cmd) {
- return -1; /* 命令为空就返回 */
- }
- if (strlen(cmd) >= CONFIG_SYS_CBSIZE) {
- puts ("##命令过长\n");
- return -1;
- }
- strcpy (cmdbuf, cmd);//拷贝到buff中
- /* Process separators and check for invalid
- * repeatable commands
- */
- #ifdef DEBUG_PARSER
- printf ("[PROCESS_SEPARATORS] %s\n", cmd);
- #endif
- while (*str) {
- /*
- * Find separator, or string end
- * Allow simple escape of ';' by writing "\;"
- */
- //提取出一条命令,命令的结尾是';'或者‘\’
- //注意这里的';'也就是说如果要一次输入多条命令的话,可以用';'隔开
- for (inquotes = 0, sep = str; *sep; sep++) {
- if ((*sep=='\'') &&
- (*(sep-1) != '\\'))
- inquotes=!inquotes;
- if (!inquotes &&
- (*sep == ';') && /* separator */
- ( sep != str) && /* past string start */
- (*(sep-1) != '\\')) /* and NOT escaped */
- break;
- }
- /*
- * Limit the token to data between separators
- */
- token = str;
- if (*sep) {
- str = sep + 1; /* start of command for next pass */
- *sep = '\0';
- }
- else
- str = sep; /* no more commands for next pass */
- #ifdef DEBUG_PARSER
- printf ("token: \"%s\"\n", token);
- #endif
- /* find macros in this token and replace them */
- //处理一些宏定义比如字符串引用时候的“${}”
- //命令分行符'\'等等
- process_macros (token, finaltoken);
- /* Extract arguments */
- //将命令按照格式分配到argv数组中
- //比如我们输入的命令 bootdelay=3;
- //转换以后我们得到argv[0]="bootdelay"
- //argv[1]="3"
- if ((argc = parse_line (finaltoken, argv)) == 0) {
- rc = -1; /* no command at all */
- continue;
- }
- /* Look up command in command table */
- if ((cmdtp = find_cmd(argv[0])) == NULL) {//查找这条命令并得到描述它的结构体
- printf ("Unknown command '%s' - try 'help'\n", argv[0]);
- rc = -1; /* give up after bad command */
- continue;
- }
这里我们来分析一下uboot是怎么查找命令的也就是find_cmd()这个函数是怎么实现的。
- 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);
- }
//这里的两个参数__u_boot_cmd_end,__u_boot_cmd_start都是在u-boot.lds中定义的
//是uboot所有命令的结构体的一个列表的起始地址和结束地址。
- /***************************************************************************
- * find command table entry for a command
- */
- cmd_tbl_t *find_cmd_tbl (const char *cmd, cmd_tbl_t *table, int table_len)
- {
- cmd_tbl_t *cmdtp;
- cmd_tbl_t *cmdtp_temp = table; /*Init value */
- const char *p;
- int len;
- int n_found = 0;
- /*
- * Some commands allow length modifiers (like "cp.b");
- * compare command name only until first dot.
- */
- len = ((p = strchr(cmd, '.')) == NULL) ? strlen (cmd) : (p - cmd);
- //从首地址开始逐条往下查找,看能否找到相应的命令,如果找到就返回相应命令的结构体指针,找不到就返回NULL
- for (cmdtp = table;
- cmdtp != table + table_len;
- cmdtp++) {
- if (strncmp (cmd, cmdtp->name, len) == 0) {
- if (len == strlen (cmdtp->name))
- return cmdtp; /* full match */
- cmdtp_temp = cmdtp; /* abbreviated command ? */
- n_found++;
- }
- }
- if (n_found == 1) { /* exactly one match */
- return cmdtp_temp;
- }
- return NULL; /* not found or ambiguous command */
- }
到了这里我们得到了命令的结构体指针,就可以与命令的处理函数来交互了。下面继续看run_command()
- /* found - check max args */
- //检测最大参数个数
- if (argc > cmdtp->maxargs) {
- cmd_usage(cmdtp);
- rc = -1;
- continue;
- }
- #if defined(CONFIG_CMD_BOOTD)
- /* avoid "bootd" recursion */
- if (cmdtp->cmd == do_bootd) {
- #ifdef DEBUG_PARSER
- printf ("[%s]\n", finaltoken);
- #endif
- if (flag & CMD_FLAG_BOOTD) {
- puts ("'bootd' recursion detected\n");
- rc = -1;
- continue;
- } else {
- flag |= CMD_FLAG_BOOTD;
- }
- }
- #endif
- /* OK - call function to do the command */
- if ((cmdtp->cmd) (cmdtp, flag, argc, argv) != 0) {//执行命令处理函数
- rc = -1;
- }
- repeatable &= cmdtp->repeatable;
- /* Did the user stop this? */
- //处理用户输入的ctrl+c
- if (had_ctrlc ())
- return -1; /* if stopped then not repeatable */
- }
- return rc ? rc : repeatable;
- }
到了这里命令的处理流程就完成了。下面我们重点来说明一下,cmdtp是怎么定义的。
cmd_tbl_t *cmdtp;
- 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 CONFIG_SYS_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
- };
可以看出命令的结构体包含这些项中有一个是命令处理函数,这会命令跟函数关联的重点。那么每个命令的结构体是在哪定义的呢?这里涉及到一个宏U_BOOT_CMD(),这个宏就是定义一个这样的结构体然后,编译的时候,编译器会自动将这个结构体计入到命令列表里面去。我们以bootcmd为例子来屡一下整个过程
在我用的板子上有环境变量bootcmd=booti mmc3,也就是说,系统启动的时候要执行booti mmc3这条命令,那么booti的结构体的定义在\bootable\bootloader\uboot-imx\common\cmd_bootm.c
- U_BOOT_CMD(
- booti, 3, 1, do_booti,
- "booti - boot android bootimg from memory\n",
- "[<addr> | mmc0 | mmc1 | mmc2 | mmcX] [<partition>] \n - boot application image stored in memory or mmc\n"
- "\t'addr' should be the address of boot image which is zImage+ramdisk.img\n"
- "\t'mmcX' is the mmc device you store your boot.img, which will read the boot.img from 1M offset('/boot' partition)\n"
- "\t 'partition' (optional) is the partition id of your device, if no partition give, will going to 'boot' partition\n"
- );
可知booti的处理函数是do_booti
- /* booti <addr> [ mmc0 | mmc1 [ <partition> ] ] */
- int do_booti(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
- {
- unsigned addr = 0;
- char *ptn = "boot";
- int mmcc = -1;
- boot_img_hdr *hdr = (void *)boothdr;
- if (argc < 2) //至少有1个有效参数
- return -1;
- if (!strncmp(argv[1], "mmc", 3))
- mmcc = simple_strtoul(argv[1]+3, NULL, 10); //得到启动的mmc名字
- else
- addr = simple_strtoul(argv[1], NULL, 16);
- if (argc > 2)
- ptn = argv[2];
- if (mmcc != -1) {
- #ifdef CONFIG_MMC
- struct fastboot_ptentry *pte;
- struct mmc *mmc;
- disk_partition_t info;
- block_dev_desc_t *dev_desc = NULL;
- unsigned sector, partno = -1;
- memset((void *)&info, 0 , sizeof(disk_partition_t));
- /* i.MX use MBR as partition table, so this will have
- to find the start block and length for the
- partition name and register the fastboot pte we
- define the partition number of each partition in
- config file
- */
- mmc = find_mmc_device(mmcc); //查找启动的mmc设备
- if (!mmc) {
- printf("booti: cannot find '%d' mmc device\n", mmcc);
- goto fail;
- }
- dev_desc = get_dev("mmc", mmcc);//得到设备描述
- if (NULL == dev_desc) {
- printf("** Block device MMC %d not supported\n", mmcc);
- goto fail;
- }
- /* below was i.MX mmc operation code */
- if (mmc_init(mmc)) { //初始化mmc
- printf("mmc%d init failed\n", mmcc);
- goto fail;
- }
- #ifdef CONFIG_ANDROID_BOOT_PARTITION_MMC
- #ifdef CONFIG_ANDROID_RECOVERY_PARTITION_MMC
- if (!strcmp(ptn, "boot"))
- partno = CONFIG_ANDROID_BOOT_PARTITION_MMC;
- if (!strcmp(ptn, "recovery"))
- partno = CONFIG_ANDROID_RECOVERY_PARTITION_MMC;
- if (get_partition_info(dev_desc, partno, &info)) { //获取mmc的分区信息
- printf("booti: device don't have such partition:%s\n", ptn);
- goto fail;
- }
- #endif
- #endif
- #ifdef CONFIG_FASTBOOT
- 。。。。。。。。//没配置fastboot
- #else
- // 这里首先说明一下bootimage的构成
- /*
- ** +-----------------+
- ** | boot header | 1 page
- ** +-----------------+
- ** | kernel | n pages
- ** +-----------------+
- ** | ramdisk | m pages
- ** +-----------------+
- ** | second stage | o pages
- ** +-----------------+
- **
- ** n = (kernel_size + page_size - 1) / page_size
- ** m = (ramdisk_size + page_size - 1) / page_size
- ** o = (second_size + page_size - 1) / page_size
- **
- ** 0. all entities are page_size aligned in flash
- ** 1. kernel and ramdisk are required (size != 0)
- ** 2. second is optional (second_size == 0 -> no second)
- ** 3. load each element (kernel, ramdisk, second) at
- ** the specified physical address (kernel_addr, etc)
- ** 4. prepare tags at tag_addr. kernel_args[] is
- ** appended to the kernel commandline in the tags.
- ** 5. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr
- ** 6. if second_size != 0: jump to second_addr
- ** else: jump to kernel_addr
- */
- if (mmc->block_dev.block_read(mmcc, info.start,//读取mmc的第一块数据,即boot header
- 1, (void *)hdr) < 0) {
- printf("booti: mmc failed to read bootimg header\n");
- goto fail;
- }
- /* flush cache after read */
- //这是个空函数
- flush_cache((ulong)hdr, 512); /* FIXME *///
- if (memcmp(hdr->magic, BOOT_MAGIC, 8)) {//看是否是android
- printf("booti: bad boot image magic\n");
- goto fail;
- }
- sector = info.start + (hdr->page_size / 512);//得到kernel的首地址
- #endif
- if (mmc->block_dev.block_read(mmcc, sector, //读取kernel到内存中
- (hdr->kernel_size / 512) + 1,
- (void *)hdr->kernel_addr) < 0) {
- printf("booti: mmc failed to read kernel\n");
- goto fail;
- }
- /* flush cache after read */
- flush_cache((ulong)hdr->kernel_addr, hdr->kernel_size); /* FIXME */
- sector += ALIGN_SECTOR(hdr->kernel_size, hdr->page_size) / 512;得到ramdisk的首地址
- if (mmc->block_dev.block_read(mmcc, sector, //读取ramdisk到内存中
- (hdr->ramdisk_size / 512) + 1,
- (void *)hdr->ramdisk_addr) < 0) {
- printf("booti: mmc failed to read kernel\n");
- goto fail;
- }
- /* flush cache after read */
- flush_cache((ulong)hdr->ramdisk_addr, hdr->ramdisk_size); /* FIXME */
- #else
- return -1;
- #endif
- } else {
- //如果没有得到mmc设备,就从参数中得到bootimage的地址然后初始化hdr
- unsigned kaddr, raddr;
- /* set this aside somewhere safe */
- memcpy(hdr, (void *) addr, sizeof(*hdr));
- if (memcmp(hdr->magic, BOOT_MAGIC, 8)) {
- printf("booti: bad boot image magic\n");
- return 1;
- }
- bootimg_print_image_hdr(hdr);
- kaddr = addr + hdr->page_size;
- raddr = kaddr + ALIGN_SECTOR(hdr->kernel_size, hdr->page_size);
- memmove((void *) hdr->kernel_addr, (void *)kaddr, hdr->kernel_size);
- memmove((void *) hdr->ramdisk_addr, (void *)raddr, hdr->ramdisk_size);
- }
- printf("kernel @ %08x (%d)\n", hdr->kernel_addr, hdr->kernel_size);
- printf("ramdisk @ %08x (%d)\n", hdr->ramdisk_addr, hdr->ramdisk_size);
- do_booti_linux(hdr);//启动内核
- puts ("booti: Control returned to monitor - resetting...\n");
- do_reset (cmdtp, flag, argc, argv);
- return 1;
- fail:
- #ifdef CONFIG_FASTBOOT
- return do_fastboot(NULL, 0, 0, NULL);
- #else
- return -1;
- #endif
- }
这个函数主要就是把boot.img文件按照格式读取到hdr结构体中传递给do_booti_linux(hdr);进而进一步启动内核我们想继续看do_booti_linux()
- void do_booti_linux (boot_img_hdr *hdr)
- {
- ulong initrd_start, initrd_end;
- void (*theKernel)(int zero, int arch, uint params);
- bd_t *bd = gd->bd;
- #ifdef CONFIG_CMDLINE_TAG
- char *commandline = getenv("bootargs");//读取环境变量中的bootargs
- /* If no bootargs env, just use hdr command line */
- if (!commandline)
- commandline = (char *)hdr->cmdline;
- /* XXX: in production, you should always use boot.img 's cmdline !!! */
- printf("kernel cmdline: \n\tuse %s command line:\n\t%s \n",
- getenv("bootargs") ? "uboot" : "boot.img",
- commandline);
- #endif
- theKernel = (void (*)(int, int, uint))(hdr->kernel_addr);//得到内存中kernel的main函数指针
- initrd_start = hdr->ramdisk_addr;
- initrd_end = initrd_start + hdr->ramdisk_size;
- #if defined (CONFIG_SETUP_MEMORY_TAGS)
- 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);//将命令行参数加入到bd中
- #endif
- #ifdef CONFIG_INITRD_TAG
- if (hdr->ramdisk_size)
- setup_initrd_tag (bd, initrd_start, initrd_end);
- #endif
- #if defined (CONFIG_VFD) || defined (CONFIG_LCD)
- setup_videolfb_tag ((gd_t *) gd);
- #endif
- setup_end_tag (bd);
- #endif
- /* we assume that the kernel is in place */
- printf ("\nStarting kernel ...\n\n");
- #ifdef CONFIG_USB_DEVICE
- {
- extern void udc_disconnect (void);
- udc_disconnect ();
- }
- #endif
- cleanup_before_linux ();
- theKernel (0, bd->bi_arch_number, bd->bi_boot_params);//直接传递参数启动kernel
- }
到了这里uboot就已经驱动了kernel。
整个过程比较简单,关键是uboot命令实现过程很重要,在自己移植uboot时候很有肯恩更要修改这些代码。需要进一步掌握
uboot启动内核的实现的更多相关文章
- UBOOT启动内核过程
1.摘要 (1).启动4步骤第一步:将内核搬移到DDR中第二步:校验内核格式.CRC等第三步:准备传参第四步:跳转执行内核(2).涉及到的主要函数是:do_bootm和do_bootm_linux(3 ...
- U-boot 启动内核
1:什么是UBOOT,为什么要有UBOOT? UBOOT的主要作用是用来启动linux内核,因为CPU不能直接从块设备中执行代码,需要把块设备中的程序复制到内存中,而复制之前还需要进行很多初始化工作, ...
- linux的几个内核镜像格式Image 和 u-boot启动内核和文件系统时的一些环境变量的设置
关于编译powerpc linux的几个Image参考原文 http://blog.sina.com.cn/s/blog_86a30b0c0100wfzt.html 转载▼ PowerPC架构 L ...
- 嵌入式linux开发uboot启动内核的机制(二)
一.嵌入式系统的分区 嵌入式系统部署在Flash设备上时,对于不同SoC和Flash设备,bootloader.kernel.rootfs的分区是不同的.三星S5PV210规定启动设备的分区方案如下: ...
- 【转】UBOOT——启动内核
转自:https://www.cnblogs.com/biaohc/p/6403863.html 1:什么是UBOOT,为什么要有UBOOT? UBOOT的主要作用是用来启动linux内核,因为CPU ...
- 嵌入式Linux驱动学习之路(六)u-boot启动内核
内核启动是需要必要的启动参数.不能开机自动完全从0开始启动,需要uboot帮助内核实现重定位并提供参数. 首先,uboo会从Kernel分区中读取bootcmd环境变量,根据环境变量可自动启动. 分区 ...
- 使用Uboot启动内核并挂载NFS根文件系统
配置编译好内核之后,将生成的内核文件uImage拷贝到/tftpboot/下,通过tftp服务器将内核下载到开发板,使用命令:tftp 31000000 uImage.下载完成之后配置bootargs ...
- tiny4412学习(一)之从零搭建linux系统(烧写uboot、内核进emmc+uboot启动内核)【转】
本文转载自:http://blog.csdn.net/fengyuwuzu0519/article/details/74080109 版权声明:本文为博主原创文章,转载请注明http://blog.c ...
- u-boot学习(五):u-boot启动内核
u-boot的目的是启动内核.内核位于Flash中,那么u-boot就要将内核转移到内存中.然后执行命令执行之.这些操作是由bootcmd命令完毕的. bootcmd=nand read.jffs2 ...
随机推荐
- rust 入门
hello rust fn main() { println!("Hello, world!"); } 从hello world入手,rust的语法是比较简洁. 在mac os中, ...
- 在C#中用MediaInfo获取视频或音频的属性
MediaInfo是一个开源的获取视频或音频的信息的非常便利的工具,它本身就带有一个GUI界面,可以非常方便我们查看视频信息.但是,当我们写一些转码程序时,往往需要在程序中获取视频信息的时候. 以前我 ...
- android 动态改变控件位置和大小 .
动态改变控件位置的方法: setPadding()的方法更改布局位置. 如我要把Imageview下移200px: ImageView.setPadding( ImageVie ...
- 虚拟机NAT网络设置
1. 虚拟机设置 2. 本地网络设置 3. 本地虚拟网卡设置 4. 安装虚拟机,设置网络为NAT方式即可访问外网.
- python redis-string、list、set操作
string操作 redis中的string在内存中都是按照一个key对应一个value来存储的 方法: set() 方法 : 写入一条数据 mset() 方法: 写入多条数据 , 可是Key-Val ...
- 使用Git Wiki 管理文档时,文档编写的基本用法
自己初次接触GitLab,通过百度和自己查找资料,了解了一部分.在自己的工作中,主要用到GitLab的Wiki文档版本管理能力.我总结了一小部分文本编辑需要用到的东西. 一.文本的排版 为了让文本/文 ...
- LINUX gcc安装rpm包顺序
rpm -ivh cpp-4.1.2-42.el5.i386.rpm rpm -ihv kernel-headers-2.6.18-92.el5.i386.rpm rpm -ivh glibc-hea ...
- LightOJ 1024 Eid(高精度乘法+求n个数最小公约数)
题目链接:https://vjudge.net/contest/28079#problem/T 题目大意:给你n个数求这些数的最小公倍数(约数). 解题思路:还太菜了,看了别人的题解才会写,转自这里, ...
- Yii 简明学习教程
Yii是一个基于组件的高性能PHP框架,用于快速开发Web应用程序(下面内容基于Yii 1.1) 1. 典型的工作流 用户向入口脚本index.php发起请求 入口脚本加载应用配置config.php ...
- 开源IDS系列--snorby 2.6.2 undefined method `run_daily_report' for Event:Class (NoMethodError)
rails runner "Event.run_daily_report"测试邮件配置undefined method `run_daily_report' for Event:C ...