zoukankan      html  css  js  c++  java
  • uboot 内核引导与添加指令

    1.Uboot:Start.s -> start_armboot ()<./lib_arm/board.c>  ->  main_loop ()<./common/main.c>
           main_loop()会调用abortboot (bootdelay)判断在delay time内有没有键按下,并给出prompt“Hit any key to stop autoboot”,若没有键按下则run_command (s, 0)(s为默认的bootcmd)。这里默认的bootcmd需要根据内核的位置设置,若bootcmd未赋值,Uboot就会一直等待。如果敲入bootm,也会启动kernel,并且传入参数。如果使用tftp从pc中下载kernel并启动也是可以的。

    2.添加u-boot命令。也就是在进入main_loop()函数后,在等待的时间里可以识别的命令。

    需要修改5个地方:

    1. /include/config_cmd_all.h    添加CONFIG_CMD_NANDBOOT宏
    2. /include/config_cmd_default.h  同上
    3. /include/configs/SEP0611.h  添加宏   (以上头文件的修改,都是为了条件编译做准备的)
    4. /common/cmd_nandboot.c   自己定义的命令   具体实现命令的函数   (命令的实现与命令的注册都在这儿)
    5. /common/Makefile   把自己写的实现命令文件添加到Makefile中,(参加编译需要配置一下) 

    最主要的还是自己写的命令实现文件。

    那么为什么要修改这几个文件呢?下面先分析一下正常的执行过程:

    对应的函数执行过程是:

    其中main_loop() run_command() 都在comman/main.c中,而find_cmd(),find_cmd_tbl()是在common/command.c中的。

    1)实现命令的具体功能,在comman文件夹中建立对应的.c文件。

    2)如果要添加指令,首先为了能让系统找到该指令,所以要在命令表中注册一下。

    #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}

      ##”与“#”都是预编译操作符,“##”有字符串连接的功能,“#”表示后面紧接着的是一个字符串。

    #define Struct_Section  __attribute__ ((unused,section (".u_boot_cmd")))

    其中,unused表示该函数或变量可能不使用,这个属性可以避免编译器产生警告信息。

    凡通过U_BOOT_CMD定义的cmd_tbl_t变量会全部被放在.u_boot_cmd段当中。这也是在你写的.c文件的末尾必须要写的,为了完成注册这个动作。

    比如说:U_BOOT_CMD(nandboot,0,0,do_nandboot,"boot from nand","--help")  通过宏展开就是:

    cmd_tbl_t  __u_boot_cmd_nandboot  __attribute__((unused, section(".u_boot_cmd"))) = {"nandboot", 0, 0, do_nandboot, "boot from nand","--help"}

    struct cmd_tbl_s {
    
           char              *name;          /* 命令名*/
    
           int          maxargs;       /* 最大参数个数*/
    
           int          repeatable;    /* 是否自动重复*/
    
           int          (*cmd)(struct cmd_tbl_s *, int, int, char *[]);  /*  响应函数*/
    
           char              *usage;         /* 简短的帮助信息*/
    
    #ifdef    CONFIG_SYS_LONGHELP
    
           char              *help;           /*  较详细的帮助信息*/
    
    #endif
    
    #ifdef CONFIG_AUTO_COMPLETE
    
           /* 自动补全参数*/
    
           int          (*complete)(int argc, char *argv[], char last_char, int maxv, char *cmdv[]);
    
    #endif
    
    };

    所谓注册就是把一个特定命令的信息填在这个结构体中,然后把这个结构体放到一个表中,用于查找和跳转。

     

    #include <common.h>
    #include <command.h>
    
    #if defined(CONFIG_CMD_NANDBOOT)
    int do_bootfromnand (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
    {
    
    
        printf ("## you are using command nandboot\n");
            run_command("nand read 0x40008000 0x400000 0x300000",0);
            run_command("bootm 0x40008000",0);
        return 0;
    }
    #endif
    /* -------------------------------------------------------------------- */
    
    U_BOOT_CMD(
        nandboot, CONFIG_SYS_MAXARGS, 1,    do_bootfromnand,
        "boot from nand",
        "boot from nand"
    );


     


    bootm命令执行过程解析:

    当你输入bootm命令的时候,最终执行的就是do_bootm()函数。

    执行过程大体是这样的:先到ram内存中kernel所在位置(uboot从nandflash中拷贝过来的),找到uImage的header,header中保存了很多关于镜像的信息,用一个结构体来保存的,这个header一共占64个字节,还保存了一些bootargs参数。那么怎么找到这个地址呢,那就是从bootm后面跟的参数得知的,或者使用默认地址,但这些地址必须与uboot拷贝kernel到ram中的地址一样,否则是找不到的。找到header之后,就把header中的信息保存起来。后面就是关于要不要在loadkernel的问题,那么需要比较两个地址,如果mkimage时候定的load地址与bootm后面跟随的地址不一样,那么就需要再load一次。但是load之后load地址与entrypoint地址肯定是一样的。如果前两个地址一样,那么就不需要再次load,但是这时候的entrypoint就与load地址相差0x40,其实相差的就是一个header的大小,第一次是省去了header,所以相等。

    load之后就可以进入do_bootm_linux()函数了。在这个函数中呢,会把bootargs参数做成TAG链表形式。然后void    (*theKernel)(int zero, int arch,uint params);  其中thekernel赋值为ep,就是entrypoint即入口地址。这是mkimage制作镜像时定义的。

    问题说说:

    1.一直在想在bootm中,加入nand read ,完成kernel由nandflash到ram的拷贝,只需要bootm一条指令就可以完成autoboot的工作,其实这样不好,想想autoboot就知道了。所谓的autoboot,是执行的bootcmd,bootcmd=nand read 0x40008000 0x400000 0x300000 ;bootm 0x40008000;两条指令完成的。还是不要改boom的实现吧。

    2.tftp 0x40008000 uImage;然后就启动内核了,把文件系统从nandflash拷贝到ram空间不是必须得,只需要把参数bootargs传给内核,内核在引导文件系统的时候会自动加载。

    3.这次学习添加了2条指令,感觉还不错。关于0x40008000这个地址的理解,花了不少时间的。首先,这是个ram地址,第二kernel从nandflash拷贝到ram就是到这儿,第三bootm 后面的地址参数或者默认的地址参数,都必须与此地址相同,否则找不到header 第四 mkimage指定的load地址与之是否一样决定了kernel的解压地址与entrypoint。比如mkimage指定的load地址是0x41800000,与0x40008000不一样,那么kernel将解压到0x41800000,此时已经不含header了,所以entrypoint就必须是0x41800000。

    arch

     

     

    , uint params); 

     

     

  • 相关阅读:
    结构层HTML + 表现层CSS
    移动端:项目实战
    移动端:开发技巧
    两个对象数组,把其中相同的name的before相加,不同的对象添加到数组里
    js中遍历数组和遍历对象
    css学习笔记一
    Angular2父子组件数据传递之@ViewChild获取子组件详解
    css知识点总结
    js中的apply,call,arguments,callee,caller详解
    javascript中的排序
  • 原文地址:https://www.cnblogs.com/autum/p/u_boot.html
Copyright © 2011-2022 走看看