uboot启动 及命令分析(3)
?
u-boot命令
先貼一個重要結(jié)構(gòu),位于uboot/include/command.h,這個結(jié)構(gòu)代表每個uboot命令
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)簡短用法信息*/
#ifdefCFG_LONGHELP
???char?????*help;???/* Help??message???(long)?長的幫助信息*/
#endif
#ifdef?CONFIG_AUTO_COMPLETE
???????????????????/* do auto completion on the arguments */
???int??(*complete)(intargc,?char?*argv[],?charlast_char,?intmaxv,?char*cmdv[]);
#endif
};
?
typedef struct?cmd_tbl_s????cmd_tbl_t;
============================================================
?
uboot的第一階段:硬件相關(guān)初始化
0.reset?執(zhí)行arm920t/start.s??過程如下
1.設(shè)置cpu svc管理模式
2.關(guān)看門狗中斷,mmu等
3.設(shè)置時鐘,sdram,外部總線
4.代碼重定位,搬運(yùn)代碼,從flash到sdram
5.設(shè)置棧,bss段清零, bss用于未初始化的全局變量和靜態(tài)變量
6.ldr pc, _start_armboot
???即進(jìn)入uboot啟動的第二階段,調(diào)用c函數(shù)start_armboot()
?
從start_armboot?開始
經(jīng)過一系列外設(shè)初始化
比如
falsh_init
nand_init
...
最后循環(huán)調(diào)用mian_loop()
main_loop主要流程
{
???1.?生成環(huán)境變量mtdparts,?調(diào)用mtdparts_init
???2.?在啟動過程中
??????若無空格鍵按下則boot_zImage,即run_command(getenv("bootcmd"),0)
??????有空格鍵按下則?run_command("menu",0)
???3. shell過程,讀取用戶的輸入并執(zhí)行相應(yīng)的命令
??????{
?????????從控制臺獲得命令,保存在全局變量comsole_buffer中
?????????解析命令行字符串,分割命令與參數(shù),最后執(zhí)行?run_command(...);
??????}????????????????????
}
?
也就是說在mian_loop中,是處理環(huán)境變量和控制臺人機(jī)交互,
mian_loop調(diào)用readline ()讀取命令行到console_buffer,
再把console_buffer復(fù)制到lastcommand中去,
還要設(shè)置flag,最后調(diào)用run_command (lastcommand, flag)函數(shù),
run_command (lastcommand, flag)函數(shù)中,首先定義cmd_tbl_t *cmdtp,再解析命令行。
再調(diào)用find_cmd(argv[0])函數(shù),其中argv[0]應(yīng)該是命令本身,參數(shù)已經(jīng)被剝離,
這個函數(shù)返回的是一個cmd_tbl_t結(jié)構(gòu)體,
就是說每個命令都有一個cmd_tbl_t結(jié)構(gòu)體相對應(yīng),關(guān)于run_command函數(shù)后面再分析
?
mian_loop中有
#define CONFIG_BOOTDELAY 3??//設(shè)置啟動延時時間
//如果延時大于等于零,并且沒有在延時過程中接收到按鍵,則引導(dǎo)內(nèi)核
if (bootdelay >=?0?&& s && !abortboot (bootdelay)) { //
# ifdef?CONFIG_AUTOBOOT_KEYED
??????intprev?= disable_ctrlc(1);/* disable Control C checking */
# endif???//狀態(tài)設(shè)置
?
# ifndef?CFG_HUSH_PARSER
????????{
?????????????printf("Booting Linux ...\n");???????//啟動?linux???
?????????run_command?(s,?0);??//運(yùn)行引導(dǎo)內(nèi)核的命令,s=getenv("bootcmd")??????????
????????}
?
加載linux內(nèi)核時將使用變量“bootcmd”和?“bootargs”,
變量“bootcmd”和?“bootargs”的值可以在在加載linux內(nèi)核前,
uboot的命令控制臺中進(jìn)行修改
?
bootcmd=nand read.jffs2 0x30007FC0 kernel;?bootm?0x30007FC0
第一條命令??從flash上讀出內(nèi)核???kernel是一個分區(qū)標(biāo)志
第二條命令??啟動命令指示了啟動地址
?
而bootargs是其它參數(shù)信息
而?run_command (getenv ("bootcmd"), flag)
?
bootcmd中的bootm,即boot application image from memory
參數(shù)形式:"bootm addr"
當(dāng)addr省略的時候bootm加載默認(rèn)的配置宏
#define CONFIG_SYS_LOAD_ADDR??0x30008000??/* default load address */
?
uboot中,"bootm"命令的執(zhí)行函數(shù)為do_bootm(),這個是由U_BOOT_CMD綁定的函數(shù)指針,
在do_bootm()中執(zhí)行了do_bootm_linux(),
do_bootm_linux()函數(shù)中獲取了"bootargs"環(huán)境變量的值,最終將此值傳遞給linux內(nèi)核,
并調(diào)用theKernel函數(shù),完成對linux內(nèi)核的加載啟動
?
linux內(nèi)核的啟動,主要就是執(zhí)行環(huán)境變量bootcmd和bootargs所定義的命令.
============================================================
?
uboot的核心功能是用run_command()來執(zhí)行的
run_command是怎么實(shí)現(xiàn)的?
?
int?run_command (const?char?*cmd,?intflag)
{
???cmd_tbl_t?*cmdtp;
???charcmdbuf[CFG_CBSIZE];????/* working copy of cmd??????*/
???char?*token;??????????/* start of token in cmdbuf*/
???char?*sep;???????????????/* end of token (separator) in cmdbuf */
???charfinaltoken[CFG_CBSIZE];
???char?*str?=?cmdbuf;
???char?*argv[CFG_MAXARGS +?1];???/* NULL terminated*/
???intargc,?inquotes;
???intrepeatable?=?1;
???intrc?=?0;
???...
???if ((cmdtp =?find_cmd(argv[0])) == NULL) {
?????????printf ("Unknown command '%s' - try 'help'\n", argv[0]);
?????????rc = -1;/* give up after bad command */
?????????continue;
??????}
???...
???if ((cmdtp->cmd) (cmdtp, flag, argc, argv)?!= 0) {
??????rc = -1;
???}
???...
}
?
run_command(...)????//流程解析
{
???1.?對\;解析,分割出一個個命令
???2.?然后對每一個完整的命令執(zhí)行
?????{
??????parse_line
??????{
?????????line是整條的命令行bootcmd的值
?????????例如line =?nand read.jffs2 0x30007FC0 kernel;?bootm?0x30007FC0
?????????先去掉開頭的空格,
?????????然后對命令進(jìn)行解析,找到空格之后將空格替換為\0,這樣解析出命令和參數(shù)
??????}
?
??????find_cmd(argv[0])
??????{
?????????從?__u_boot_cmd_start?到?__u_boot_cmd_end?的array進(jìn)行遍歷,
?????????從找到的cmd_tbl_t中,字符串尋找cmdtp->name與argv[0]相同的命令
??????}
??????找到匹配的命令后,調(diào)用cmd_tbl_t->cmd執(zhí)行
?????}
}
?
run_command函數(shù)中的parse_line函數(shù)主要代碼如下
int?parse_line?(char *line, char *argv[])
{
???...
???while ((*line == ' ') || (*line == '\t'))
???{
??????++line;
???}
???...
}
============================================================
run_command函數(shù)中的find_cmd()
cmd_tbl_t *find_cmd (const?char?*cmd)
{
???cmd_tbl_t *cmdtp;
???cmd_tbl_t *cmdtp_temp?= &__u_boot_cmd_start;???/*Init value */
???const?char?*p;
???intlen;
???intn_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);
?
???for (cmdtp??= &__u_boot_cmd_start;
????????cmdtp?!= &__u_boot_cmd_start;
????????cmdtp++) {
??????if (strncmp?(cmd,?cmdtp->name,?len) ==?0) {
?????????if (len?==?strlen?(cmdtp->name))
????????????return?cmdtp;???/* full match */
?????????//如果名字匹配,就返回這個結(jié)構(gòu)體,否則比較下一個
?????????cmdtp_temp?=?cmdtp;???/* abbreviated command ? */
?????????n_found++;
??????}
???}
?
???if (n_found?==?1) {?????????/* exactly one match */
??????return?cmdtp_temp;
???}
?
???return?NULL;/* not found or ambiguous command */
}
?
其中__u_boot_cmd_start和__u_boot_cmd_start是怎么來的?
查找發(fā)現(xiàn)只有在command.h中有聲明
extern cmd_tbl_t??__u_boot_cmd_start;
extern cmd_tbl_t??__u_boot_cmd_end;
?
而__u_boot_cmd_start是在鏈接腳本uboot.lds里面定義的
?
???. = .;
???__u_boot_cmd_start = .;
???.u_boot_cmd : { *(.u_boot_cmd) }??//所有u_boot_cmd宏命令都保存在這個段
???__u_boot_cmd_end = .;
?
在command.h中有
#define Struct_Section??__attribute__ ((unused,section (".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_bootm.c中有?U_BOOT_CMD的實(shí)參
U_BOOT_CMD(
???bootm,CFG_MAXARGS,1,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
);
?
將這個宏展開并替換
#define?U_BOOT_CMD(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, "bootm???- boot application image from memory\n", "陰影部分"}
參數(shù)說明---------------
名稱:bootm
將段屬性設(shè)置為: .u_boot_cmd
最大參數(shù)個數(shù):CFG_MAXARGS
是否可重復(fù):1 ,?可重復(fù),即下一次按回車時可重復(fù)執(zhí)行
cmd對應(yīng)do_bootm,這是在cmd_tblt_t中定義的函數(shù)指針,命令就是在這個函數(shù)中實(shí)現(xiàn)
usage:使用概要?"bootm???- boot application image from memory\n"
help:詳細(xì)幫助:那一大段陰影部分
?
?
?
總結(jié):
每個U_BOOT_CMD宏組成的命令實(shí)質(zhì)上是一個個cmd_tbl_t結(jié)構(gòu),
它們在鏈接時全部被定位保存到__u_boot_cmd_start起始地址開始的段中,也就是.u_boot_cmd段中.
?
當(dāng)上電后,若啟動的是默認(rèn)的linux內(nèi)核,執(zhí)行run_command (getenv ("bootcmd"), flag),
由bootcmd字串中得知bootm,bootm的執(zhí)行函數(shù)是do_bootm(),
在do_bootm()中執(zhí)行了do_bootm_linux(...),
do_bootm_linux()函數(shù)中獲取了"bootargs"環(huán)境變量的值,
最終將此值傳遞給linux內(nèi)核,并調(diào)用theKernel函數(shù),完成對linux內(nèi)核的加載啟動
?
當(dāng)上電后,若用戶按空格并輸入命令,先同樣執(zhí)行run_command函數(shù),調(diào)用find_cmd遍歷每一個cmd_tbl_t結(jié)構(gòu),
比較cmdtp->name,當(dāng)名稱匹配時,就通過cmd_tbl_t結(jié)構(gòu)的(*cmd)(...)函數(shù)指針來執(zhí)行命令功能,
即執(zhí)行cmd_tbl_t->cmd
?
============================================================
?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??添加uboot命令
在uboot/include/configs/xxx.h文件中,添加#define CONFIG_CMD_HELLO啟用我們的自定義命令
也可以在uboot/include/config_cmd_default.h文件中添加,不過這個define在這里不是必須的
?
在common目錄,命令都是在cmd_xxx.c文件中實(shí)現(xiàn)的,這個是命名規(guī)范,必須是cmd_xxx.c形式,
所以我們在common目錄新建一個文件cmd_myCmd.c
?
#include <common.h>
?#include <command.h>
#ifdef?CONFIG_CMD_HELLO
int do_hello (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
? int i;
? printf("hello the word!,%d\n",argc);
?
? for(is = 0;i < argc;i++) ?
? { ???
printf("argv[%d]:%s\n",i,argv[i]); ?
? } ? ?
return 0;
}
?
U_BOOT_CMD( ??hello,?CFG_MAXARGS,?1,?do_hello,
??"hello?? - just for test\n", ??
"hello,long help .................\n"
);???
#endif
?
?
?
U_BOOT_CMD這里3個hello寫法最好一致,為什么?
第一個hello是uboot命令
第二個hello是在命令行輸入help時輸出的概要信息
第三個hello是當(dāng)輸入help myTest的時候顯示的詳細(xì)信息
?
最后,修改common下的Makefile文件,將cmd_myCmd.o加入編譯
輸入hello a b c d e f
輸出
hello the word!,7
argv[0]:hello
argv[1]:a
argv[2]:b
argv[3]:c
argv[4]:d
argv[5]:e
argv[6]:f
?
========================================================
?
?
轉(zhuǎn)載于:https://www.cnblogs.com/CZM-/p/5069920.html
總結(jié)
以上是生活随笔為你收集整理的uboot启动 及命令分析(3)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【python】编程学习练习题-2
- 下一篇: [转载]使用awk进行数字计算,保留指定