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启动第二阶段以及界面命令分析的更多相关文章

  1. Spring Boot 启动(一) SpringApplication 分析

    Spring Boot 启动(一) SpringApplication 分析 Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698.html ...

  2. centos 7.0 更改启动环境/启动进入图形界面/命令行模式

    当前我桌面模式 systemctl set-default multi-user.target ln -sf /lib/systemd/system/multi-user.target /etc/sy ...

  3. Spring Boot启动命令参数详解及源码分析

    使用过Spring Boot,我们都知道通过java -jar可以快速启动Spring Boot项目.同时,也可以通过在执行jar -jar时传递参数来进行配置.本文带大家系统的了解一下Spring ...

  4. [Spring Boot] Spring Boot启动过程源码分析

    关于Spring Boot,已经有很多介绍其如何使用的文章了,本文从源代码(基于Spring-boot 1.5.6)的角度来看看Spring Boot的启动过程到底是怎么样的,为何以往纷繁复杂的配置到 ...

  5. Spring Boot启动过程源码分析--转

    https://blog.csdn.net/dm_vincent/article/details/76735888 关于Spring Boot,已经有很多介绍其如何使用的文章了,本文从源代码(基于Sp ...

  6. spring boot启动原理步骤分析

    spring boot最重要的三个文件:1.启动类 2.pom.xml 3.application.yml配置文件 一.启动类->main方法 spring boot启动原理步骤分析 1.spr ...

  7. Spring Boot 启动原理分析

    https://yq.aliyun.com/articles/6056 转 在spring boot里,很吸引人的一个特性是可以直接把应用打包成为一个jar/war,然后这个jar/war是可以直接启 ...

  8. Spring Boot启动流程分析

    引言 早在15年的时候就开始用spring boot进行开发了,然而一直就只是用用,并没有深入去了解spring boot是以什么原理怎样工作的,说来也惭愧.今天让我们从spring boot启动开始 ...

  9. Spring Boot 启动(四) EnvironmentPostProcessor

    Spring Boot 启动(四) EnvironmentPostProcessor Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698. ...

随机推荐

  1. 深入理解JVM一配置参数

    一.JVM配置参数分为三类参数: 1.跟踪参数 2.堆分配参数 3.栈分配参数 这三类参数分别用于跟踪监控JVM状态,分配堆内存以及分配栈内存. 二.跟踪参数 跟踪参数用于跟踪监控JVM,往往被开发人 ...

  2. Day21-获取用户请求相关信息及请求头

    1. request里面还包含请求头等信息,可以打印看一下. views.py中的程序 from django.shortcuts import render,HttpResponse from dj ...

  3. python基础(3)

    使用list和tuple list Python内置的一种数据类型是列表:list.list是一种有序的集合,可以随时添加和删除其中的元素. 比如,列出班里所有同学的名字,就可以用一个list表示: ...

  4. linux 递归删除目录文件

    比如删.svn文件 >find . -name ".svn" | xargs -exec rm -rf

  5. 并发时-修改Linux系统下的最大文件描述符限制

    通常我们通过终端连接到linux系统后执行ulimit -n 命令可以看到本次登录的session其文件描述符的限制,如下: $ulimit -n1024 当然可以通过ulimit -SHn 1024 ...

  6. 网站统计IP PV UV实现原理

    网站流量统计可以帮助我们分析网站的访问和广告来访等数据,里面包含很多数据的,比如访问试用的系统,浏览器,ip归属地,访问时间,搜索引擎来源,广告效果等.原来是一样的,这次先实现了PV,UV,IP三个重 ...

  7. 埃及分数&&The Rotation Game&&骑士精神——IDA*

    IDA*:非常好用的搜索,可以解决很多深度浅,但是规模大的搜索问题. 估价函数设计思路:观察一步最多能向答案靠近多少. 埃及分数 题目大意: 给出一个分数,由分子a 和分母b 构成,现在要你分解成一系 ...

  8. 「Linux」centos7安装python

    •安装python3.6可能使用的依赖 yum install openssl-devel bzip2-devel expat-devel gdbm-devel readline-devel sqli ...

  9. 前端PHP入门-021-重点日期函数之日期验证函数

    checkdate可以判断一个输出的日期是否有效. 在实际的工作中,我们需要经常用于检测常用于用户提交表单的数据验证. 函数的语法格式如下: bool checkdate ( int month,in ...

  10. 关闭eclipse自动弹出console功能

    使用eclipse时经常会用到最大化窗口,而如果此时是开着tomcat等服务的话,一段后台有打印什么东西出来都会自己弹出 console挺烦人的.可以使用以下操作关闭这个功能. Preferences ...