代码改变世界

u-boot 内核引导与添加指令

2012-06-09 20:31  至上  阅读(6831)  评论(0编辑  收藏  举报

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);