uboot流程——命令行模式以及命令处理介绍
[uboot] (第六章)uboot流程——命令行模式以及命令處理介紹
2016年11月14日 20:39:26閱讀數(shù):4323以下例子都以project X項(xiàng)目tiny210(s5pv210平臺(tái),armv7架構(gòu))為例
[uboot] uboot流程系列:?
[project X] tiny210(s5pv210)上電啟動(dòng)流程(BL0-BL2)?
[project X] tiny210(s5pv210)從存儲(chǔ)設(shè)備加載代碼到DDR?
[uboot] (第一章)uboot流程——概述?
[uboot] (第二章)uboot流程——uboot-spl編譯流程?
[uboot] (第三章)uboot流程——uboot-spl代碼流程?
[uboot] (第四章)uboot流程——uboot編譯流程?
[uboot] (第五章)uboot流程——uboot啟動(dòng)流程?
[uboot] (番外篇)global_data介紹?
[uboot] (番外篇)uboot relocation介紹
建議先看《[uboot] (第五章)uboot流程——uboot啟動(dòng)流程》
=================================================================================
一、說明
命令行模式就是指uboot執(zhí)行完一切必要的初始化過程之后,等待終端輸入命令和處理命令的一個(gè)模式。?
所以后面的章節(jié),我們先介紹命令如何存儲(chǔ)以及處理,再簡(jiǎn)單說明命令行模式是如何工作的
1、需要打開哪些宏
CONFIG_CMDLINE?
表示是否支持命令行模式,定義如下:?
./configs/bubblegum_defconfig:201:CONFIG_CMDLINE=y?
./configs/tiny210_defconfig:202:CONFIG_CMDLINE=yCONFIG_SYS_GENERIC_BOARD?
用于定義板子為通用類型的板子。打開這個(gè)宏之后,common/board_f.c和common/board_r.c才會(huì)被編譯進(jìn)去,否則,需要自己實(shí)現(xiàn)。?
./configs/bubblegum_defconfig:7:CONFIG_SYS_GENERIC_BOARD=y?
./configs/tiny210_defconfig:7:CONFIG_SYS_GENERIC_BOARD=y?
打開之后,board_r.c中最終會(huì)執(zhí)行run_main_loop進(jìn)入命令行模式。具體參考《[uboot] (第五章)uboot流程——uboot啟動(dòng)流程》。CONFIG_SYS_PROMPT?
命令行模式下的提示符。在tiny210中定義如下:?
./configs/tiny210_defconfig:203:CONFIG_SYS_PROMPT=”TINY210 => “CONFIG_SYS_HUSH_PARSER?
表示使用使用hush來對(duì)命令行進(jìn)行解析。后續(xù)會(huì)繼續(xù)說明。在tiny210中定義如下:?
./include/configs/tiny210.h:121:#define CONFIG_SYS_HUSH_PARSER /* use “hush” command parser */對(duì)應(yīng)命令需要打開對(duì)應(yīng)命令的宏?
以bootm命令為例,如果要支持bootm,則需要打開CONFIG_CMD_BOOTM宏,具體可以參考cmd/Makefile?
./configs/bubblegum_defconfig:226:CONFIG_CMD_BOOTM=y?
./configs/tiny210_defconfig:226:CONFIG_CMD_BOOTM=y
2、結(jié)合以下幾個(gè)問題來看后面的章節(jié)
- 命令的數(shù)據(jù)結(jié)構(gòu),也就是代碼里面如何表示一個(gè)命令?
- 如何定義一個(gè)命令,我們?nèi)绾翁砑右粋€(gè)自己的命令?
- 命令的存放和獲取?
- 命令行模式的處理流程?
3、API
- U_BOOT_CMD?
#define U_BOOT_CMD(_name, _maxargs, _rep, _cmd, _usage, _help)?
定義一個(gè)命令。 - cmd_process?
enum command_ret_t cmd_process(int flag, int argc, char * const argv[], int *repeatable, ulong *ticks)?
命令的處理函數(shù),命令是作為argv[0]傳入。
具體參數(shù)意義和實(shí)現(xiàn)參考后面。
二、命令處理數(shù)據(jù)結(jié)構(gòu)的存放
1、數(shù)據(jù)結(jié)構(gòu)
uboot把所有命令的數(shù)據(jù)結(jié)構(gòu)都放在一個(gè)表格中,我們后續(xù)稱之為命令表。表中的每一項(xiàng)代表著一個(gè)命令,其項(xiàng)的類型是cmd_tbl_t。?
數(shù)據(jù)結(jié)構(gòu)如下:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
參數(shù)說明如下:
- name:定義一個(gè)命令的名字。?其實(shí)就是執(zhí)行的命令的字符串。這個(gè)要注意。
- maxargs:這個(gè)命令支持的最大參數(shù)
- repeatable:是否需要重復(fù)
- cmd:命令處理函數(shù)的地址
- usage:字符串,使用說明
- help:字符串,幫助
2、在dump里面的表示
通過以下命令解析出dump。
arm-none-linux-gnueabi-objdump -D u-boot > uboot_objdump.txt- 1
以bootm命令為例,提取一部分信息,加上了注釋信息:
23e364cc <_u_boot_list_2_cmd_2_bootm>: // bootm命令對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu)符號(hào)是_u_boot_list_2_cmd_2_bootm,后續(xù)我們會(huì)說明,其起始地址是0x23e364cc 23e364cc: 23e2bbc2 mvncs fp, #198656 ; 0x30800 // 這里對(duì)應(yīng)第一個(gè)成員name,其地址是0x23e2bbc2 23e364d0: 00000040 andeq r0, r0, r0, asr #32 // 這里對(duì)應(yīng)第二個(gè)成員maxargs,maxargs=0x40 23e364d4: 00000001 andeq r0, r0, r1 // 這里對(duì)應(yīng)第三個(gè)成員repeatable,repeatable=1 23e364d8: 23e02028 mvncs r2, #40 ; 0x28 // 這里對(duì)應(yīng)第四個(gè)成員cmd,cmd命令處理函數(shù)的地址是0x23e02028,和下面的do_bootm的地址一致!!! 23e364dc: 23e2bbc8 mvncs fp, #204800 ; 0x32000 // 這里對(duì)應(yīng)第五個(gè)成員usage,usage字符串的地址是0x23e2bbc8 23e364e0: 23e34cb0 mvncs r4, #45056 ; 0xb000 // 這里對(duì)應(yīng)第六個(gè)成員help,help字符串的地址是0x23e34cb023e02028 <do_bootm>: 23e34cb0 <bootm_help_text>:- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
根據(jù)上述dump就可以把bootm命令的數(shù)據(jù)結(jié)構(gòu)定義都找出來了。
3、命令數(shù)據(jù)結(jié)構(gòu)在u-boot.map符號(hào)表中的位置定義
通過查看u-boot.map,過濾出和u_boot_list中cmd相關(guān)的部分,對(duì)應(yīng)符號(hào)表如下:
*(SORT(.u_boot_list*)).u_boot_list_2_cmd_10x23e3649c 0x0 cmd/built-in.o.u_boot_list_2_cmd_10x23e3649c 0x0 common/built-in.o.u_boot_list_2_cmd_2_bootefi0x23e3649c 0x18 cmd/built-in.o0x23e3649c _u_boot_list_2_cmd_2_bootefi.u_boot_list_2_cmd_2_bootelf0x23e364b4 0x18 cmd/built-in.o0x23e364b4 _u_boot_list_2_cmd_2_bootelf.u_boot_list_2_cmd_2_bootm0x23e364cc 0x18 cmd/built-in.o0x23e364cc _u_boot_list_2_cmd_2_bootm .......u_boot_list_2_cmd_2_true0x23e3670c 0x18 cmd/built-in.o0x23e3670c _u_boot_list_2_cmd_2_true.u_boot_list_2_cmd_2_version0x23e36724 0x18 cmd/built-in.o0x23e36724 _u_boot_list_2_cmd_2_version.u_boot_list_2_cmd_30x23e3673c 0x0 cmd/built-in.o.u_boot_list_2_cmd_30x23e3673c 0x0 common/built-in.o- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
可以觀察到命令表是被定義在0x23e3649c( .u_boot_list_2_cmd_1)到0x23e3673c( .u_boot_list_2_cmd_3)的位置中。?
并且每一個(gè)項(xiàng)占用了24個(gè)字節(jié),和cmd_tbl_t結(jié)構(gòu)的大小是一致的。?
注意,根據(jù)注釋,.u_boot_list_2_cmd_1和.u_boot_list_2_cmd_3這兩個(gè)符號(hào)是由鏈接器自己生成的。?
這里簡(jiǎn)單有個(gè)印象,bootm命令對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu)符號(hào)是u_boot_list_2_cmd_2_bootm
4、如何定義一個(gè)命令
(1)我們以bootm命令的定義為例:?
cmd/bootm.c中
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
通過上面,可以看出是通過U_BOOT_CMD來定義了bootm命令對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu)!!!?
并且命令處理函數(shù)的格式如下:
- 1
當(dāng)命令處理函數(shù)執(zhí)行成功時(shí),需要返回0.返回非0值?
所以可以參照如上方式自己定義一個(gè)命令。
5、介紹一下U_BOOT_CMD的實(shí)現(xiàn)
include/common.h
#define U_BOOT_CMD(_name, _maxargs, _rep, _cmd, _usage, _help) \U_BOOT_CMD_COMPLETE(_name, _maxargs, _rep, _cmd, _usage, _help, NULL)#define U_BOOT_CMD_COMPLETE(_name, _maxargs, _rep, _cmd, _usage, _help, _comp) \ll_entry_declare(cmd_tbl_t, _name, cmd) = \U_BOOT_CMD_MKENT_COMPLETE(_name, _maxargs, _rep, _cmd, \_usage, _help, _comp);#define U_BOOT_CMD_MKENT_COMPLETE(_name, _maxargs, _rep, _cmd, \_usage, _help, _comp) \{ #_name, _maxargs, _rep, _cmd, _usage, \_CMD_HELP(_help) _CMD_COMPLETE(_comp) }#define ll_entry_declare(_type, _name, _list) \_type _u_boot_list_2_##_list##_2_##_name __aligned(4) \__attribute__((unused, \section(".u_boot_list_2_"#_list"_2_"#_name)))ll_entry_declare(cmd_tbl_t, _name, cmd) // 以bootm為例 // _type=cmd_tbl_t // _name=bootm // _list=cmd // 這里最終會(huì)轉(zhuǎn)化為如下數(shù)據(jù)結(jié)構(gòu) // // cmd_tbl_t _u_boot_list_2_cmd_2_bootm= // { // _name=bootm, // _maxargs=CONFIG_SYS_MAXARGS, // _rep=1, // _cmd=do_bootm, // _usage="boot application image from memory", // _help=bootm_help_text, // _comp=NULL, // } // 并且這個(gè)數(shù)據(jù)結(jié)構(gòu)給存放到了 .u_boot_list_2_cmd_2_bootm段中!!!和我們上述的完全一致。- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
三、命令的處理
1、簡(jiǎn)單流程說明:
假設(shè)傳進(jìn)來的命令是cmd。?
* 獲取命令表?
* 從命令表中搜索和cmd匹配的項(xiàng)?
* 執(zhí)行對(duì)應(yīng)項(xiàng)中的命令
后續(xù)我們我們分成“查找cmd對(duì)應(yīng)的表項(xiàng)”、“執(zhí)行對(duì)應(yīng)表項(xiàng)中的命令”兩部分進(jìn)行說明
2、查找cmd對(duì)應(yīng)的表項(xiàng)——find_cmd
通過find_cmd可以獲取命令對(duì)應(yīng)的命令表項(xiàng)cmd_tbl_t 。
(1)原理簡(jiǎn)單說明?
前面我們知道了可以觀察到命令表是被定義在 .u_boot_list_2_cmd_1到.u_boot_list_2_cmd_3的位置中。所以我們從這個(gè)區(qū)間獲取命令表。?
并且根據(jù)表項(xiàng)中的name是否和傳進(jìn)來的命令是否匹配來判斷是否是我們需要的表項(xiàng)。
(2)對(duì)應(yīng)代碼?
common/command.c
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
include/linker_lists.h
#define ll_entry_start(_type, _list) \ ({ \static char start[0] __aligned(4) __attribute__((unused, \section(".u_boot_list_2_"#_list"_1"))); \(_type *)&start; \ }) // 因?yàn)閭鬟M(jìn)來的_list是cmd,所以這里就是獲取命令表的起始地址,也就是.u_boot_list_2_cmd_1的地址,#define ll_entry_end(_type, _list) \ ({ \static char end[0] __aligned(4) __attribute__((unused, \section(".u_boot_list_2_"#_list"_3"))); \(_type *)&end; \ }) // 因?yàn)閭鬟M(jìn)來的_list是cmd,所以這里就是獲取命令表的結(jié)束地址,也就是.u_boot_list_2_cmd_3的地址,#define ll_entry_count(_type, _list) \({ \_type *start = ll_entry_start(_type, _list); \_type *end = ll_entry_end(_type, _list); \unsigned int _ll_result = end - start; \_ll_result; \}) // 因?yàn)閭鬟M(jìn)來的_list是cmd,所以這里就是計(jì)算命令表的長(zhǎng)度- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
3、執(zhí)行對(duì)應(yīng)表項(xiàng)中的命令——cmd_call
通過調(diào)用cmd_call可以執(zhí)行命令表項(xiàng)cmd_tbl_t 中的命令?
common/command.c
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
4、命令處理函數(shù)——cmd_process
代碼如下:?
common/command.c
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
返回0表示執(zhí)行成功,返回非0值表示執(zhí)行失敗。?
后續(xù)需要執(zhí)行一個(gè)命令的時(shí)候,直接調(diào)用cmd_process即可。
四、命令行模式的流程
命令行模式有兩種簡(jiǎn)單的方式。正常模式是簡(jiǎn)單地獲取串口數(shù)據(jù)、解析和處理命令。?
hush模式則是指命令的接收和解析使用busybox的hush工具,對(duì)應(yīng)代碼是hush.c。?
關(guān)于hush模式的作用和使用自己還不是很清楚,還要再研究一下。這里簡(jiǎn)單的寫一點(diǎn)流程。
1、入口
通過《[uboot] (第五章)uboot流程——uboot啟動(dòng)流程》,我們知道了uboot在執(zhí)行完所有初始化程序之后,調(diào)用run_main_loop進(jìn)入主循環(huán)。?
通過主循環(huán)進(jìn)入了命令行模式。?
common/board_r.c
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
main_loop實(shí)現(xiàn)如下:?
這里了解一個(gè)縮寫,cli,Command Line Interface,命令行接口,命令行界面。?
common/main.c
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
通過調(diào)用cli_loop進(jìn)入了命令行模式,并且不允許返回。?
common/cli.c
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
最終通過cli_simple_loop進(jìn)入通用命令行模式,或者通過parse_file_outer進(jìn)入hush命令行模式。?
因?yàn)橥ㄓ妹钚心J较鄬?duì)較為簡(jiǎn)單,所以這邊先說明通用命令行模式。
2、通用命令行模式
代碼如下:
void cli_simple_loop(void) {static char lastcommand[CONFIG_SYS_CBSIZE + 1] = { 0, };int len;int flag;int rc = 1;for (;;) {len = cli_readline(CONFIG_SYS_PROMPT); // 打印CONFIG_SYS_PROMPT,然后從串口讀取一行作為命令,存儲(chǔ)在console_buffer中 // 在tiny210中定義如下:./configs/tiny210_defconfig:203:CONFIG_SYS_PROMPT="TINY210 => "flag = 0; /* assume no special flags for now */if (len > 0)strlcpy(lastcommand, console_buffer,CONFIG_SYS_CBSIZE + 1); // 如果獲得了一個(gè)新行時(shí),命令會(huì)存儲(chǔ)在console_buffer,將命令復(fù)制到lastcommand中else if (len == 0)flag |= CMD_FLAG_REPEAT; // 只是得到一個(gè)單純的換行符時(shí),設(shè)置重復(fù)標(biāo)識(shí),后續(xù)重復(fù)執(zhí)行上一次命令if (len == -1)puts("<INTERRUPT>\n"); // 獲得非數(shù)據(jù)值時(shí),直接打印中斷elserc = run_command_repeatable(lastcommand, flag); // 否則,執(zhí)行l(wèi)astcommand中的命令if (rc <= 0) {/* invalid command or not repeatable, forget it */lastcommand[0] = 0; // 命令執(zhí)行出錯(cuò)時(shí),清空l(shuí)astcommand,防止下一次重復(fù)執(zhí)行這個(gè)命令}} }/* run_command_repeatable實(shí)現(xiàn)如下 */ int run_command_repeatable(const char *cmd, int flag) {return cli_simple_run_command(cmd, flag); }/* cli_simple_run_command實(shí)現(xiàn)如下 */ int cli_simple_run_command(const char *cmd, int flag) {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;debug_parser("[RUN_COMMAND] cmd[%p]=\"", cmd);if (DEBUG_PARSER) {/* use puts - string may be loooong */puts(cmd ? cmd : "NULL");puts("\"\n");}clear_ctrlc(); /* forget any previous Control C */if (!cmd || !*cmd)return -1; /* empty command */if (strlen(cmd) >= CONFIG_SYS_CBSIZE) {puts("## Command too long!\n");return -1;}strcpy(cmdbuf, cmd);/* Process separators and check for invalid* repeatable commands*/debug_parser("[PROCESS_SEPARATORS] %s\n", cmd);while (*str) { // 這里過濾掉一些對(duì)命令進(jìn)行處理的部分代碼/* find macros in this token and replace them */cli_simple_process_macros(token, finaltoken);/* Extract arguments */argc = cli_simple_parse_line(finaltoken, argv); // 對(duì)命令進(jìn)行加工處理,轉(zhuǎn)化成argv和argc格式。if (argc == 0) {rc = -1; /* no command at all */continue;}if (cmd_process(flag, argc, argv, &repeatable, NULL))rc = -1; // 調(diào)用cmd_process對(duì)命令進(jìn)行處理 // 關(guān)于這個(gè)的實(shí)現(xiàn)我們?cè)谏鲜鋈?中說明過了/* Did the user stop this? */if (had_ctrlc())return -1; /* if stopped then not repeatable */}return rc ? rc : repeatable; }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
3、hush命令行模式
hush的實(shí)現(xiàn)自己也沒搞太懂,簡(jiǎn)單的說明一下流程
static int parse_file_outer(FILE *f) {int rcode;struct in_str input;setup_file_in_str(&input); // 安裝一個(gè)輸入流,具體沒怎么研究rcode = parse_stream_outer(&input, FLAG_PARSE_SEMICOLON); // 處理流數(shù)據(jù)return rcode; } 后續(xù)的流程簡(jiǎn)單說明如下: parse_stream_outer ——》run_list ————》run_list_real ——————》run_pipe_real ————————》cmd_process- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
可以觀察到,最終還是調(diào)用了cmd_process來對(duì)命令進(jìn)行處理,上述第三節(jié)已經(jīng)說明了,這里不重復(fù)說明了。
總結(jié)
以上是生活随笔為你收集整理的uboot流程——命令行模式以及命令处理介绍的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: uboot流程——uboot启动流程
- 下一篇: kernel 下串口serial输入输出