uboot指令的實作
先分一下,uboot如何解析我們輸進去的指令,其中
argv[x]
就是用來儲存每一條指令,uboot支援多條指令一起輸入,用分号;隔開
/* Extract arguments */
if ((argc = parse_line (finaltoken, argv)) == 0) {
rc = -1; /* no command at all */
continue;
}
然後是在表中查找是否有輸入的指令,後續還要做一些指令合格性的檢查
/* Look up command in command table */
if ((cmdtp = find_cmd(argv[0])) == NULL) {
printf ("Unknown command '%s' - try 'help'\n", argv[0]);
rc = -1; /* give up after bad command */
continue;
}
/* found - check max args */
if (argc > cmdtp->maxargs) {
printf ("Usage:\n%s\n", cmdtp->usage);
rc = -1;
continue;
}
其中有個結構體需要注意,每個指令,都有一個名字,還有對應的參數,以及執行指令的函數,這些都被打包放在一起結構體變量中
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
};
在
find_cmd
中,通過一個for循環去找指令是否存在
for (cmdtp = &__u_boot_cmd_start;
cmdtp != &__u_boot_cmd_end;
cmdtp++) {
if (strncmp (cmd, cmdtp->name, len) == 0) {
if (len == strlen (cmdtp->name))
return cmdtp; /* full match */
cmdtp_temp = cmdtp; /* abbreviated command ? */
n_found++;
}
}
其中
__u_boot_cmd_start
和
__u_boot_cmd_end
是在連結腳本中指定的
. = .;
__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")))
#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}
如果現在執行
bootm 0x30007FC0
,看看跟宏
U_BOOT_CMD
有什麼關系
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
);
展開後,發現是定義了一個
cmd_tbl_t
類型的結構體變量
__u_boot_cmd_bootm
,後面的
Struct_Section
也宏展開,發現段屬性被強制設為
u_boot_cmd
段
cmd_tbl_t __u_boot_cmd_bootm Struct_Section = {"bootm", CFG_MAXARGS, 1, do_bootm, usage, help}
Struct_Section __attribute__ ((unused,section (".u_boot_cmd")))
這裡面的usage和help,是替換上面的字元串,其中
- usage:“bootm - boot application image from memory\n”
-
help:剩下的所有字元串
可以發現usage替換的後面是有逗号的,help的字元串雙引号之間沒有任何間隔符号
接下來自己寫一個指令試試看,在common目錄下建立檔案cmd_hello.c
#include <common.h>
#include <watchdog.h>
#include <command.h>
#include <image.h>
#include <malloc.h>
#include <zlib.h>
#include <bzlib.h>
#include <environment.h>
#include <asm/byteorder.h>
int do_hello (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
printf("hello world uboot sb things\n");
}
U_BOOT_CMD(
hello, CFG_MAXARGS, 1, do_hello,
"hello - this is a first uboot cmd, hello world\n",
"this is a infomation for help, long string"
);
修改command目錄下的Makefile檔案,增加一個源檔案cmd_hello.o,編譯燒錄即可
uboot啟動核心
通過之前的分析,可以知道,核心是由bootm啟動,是以接下來分析整個bootcmd指令的參數
s = getenv ("bootcmd");
printf("Booting Linux ...\n");
run_command (s, 0);
這條指令,第一句話,将核心從kernel分區讀出,并放到SDRAM的指定位址,然後從這個位址啟動核心
在uboot中,一般會将分區大小寫死
#define MTDPARTS_DEFAULT "mtdparts=nandflash0:[email protected](bootloader)," \
"128k(params)," \
"2m(kernel)," \
"-(root)"
是以第一句話等價于下面的,從0x00060000讀取核心,大小為0x00200000,存放到0x30007FC0
bootcmd=nand read.jffs2 0x30007FC0 kernel;
bootcmd=nand read.jffs2 0x30007FC0 0x00060000 0x00200000;
接下來看如何從nand中讀取核心,重點在于
nand_read_opts
int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
{
...
if (read) {
/* read */
nand_read_options_t opts;
memset(&opts, 0, sizeof(opts));
opts.buffer = (u_char*) addr;
opts.length = size;
opts.offset = off;
opts.quiet = quiet;
ret = nand_read_opts(nand, &opts);
}
...
}
讀出了核心後,接下來如何啟動,分析bootm
首先分析一下uImage的結構,它是一個64位元組的頭部+真正的核心組成,其中有兩項比較重要的參數
ih_load加載位址
和
ih_ep入口位址
typedef struct image_header {
uint32_t ih_magic; /* Image Header Magic Number */
uint32_t ih_hcrc; /* Image Header CRC Checksum */
uint32_t ih_time; /* Image Creation Timestamp */
uint32_t ih_size; /* Image Data Size */
uint32_t ih_load; /* Data Load Address */
uint32_t ih_ep; /* Entry Point Address */
uint32_t ih_dcrc; /* Image Data CRC Checksum */
uint8_t ih_os; /* Operating System */
uint8_t ih_arch; /* CPU architecture */
uint8_t ih_type; /* Image Type */
uint8_t ih_comp; /* Compression Type */
uint8_t ih_name[IH_NMLEN]; /* Image Name */
} image_header_t;
bootm指令先去讀取uImage的頭部資訊,如果發現目前核心并不位于它指定的加載位址,就将核心移到指定的加載位址,然後uboot需要告訴核心一些啟動參數,這些參數是存放到某些位址,然後核心去這些位址去讀取對應的參數,就是所謂的tag,參數傳完後,跳轉到入口位址執行
memmove (&header, (char *)addr, sizeof(image_header_t));
memmove ((void *) ntohl(hdr->ih_load), (uchar *)data, len);
do_bootm_linux(...);
setup_start_tag (bd); //設定參數
setup_serial_tag (¶ms);
setup_revision_tag (¶ms);
setup_memory_tags (bd);
setup_commandline_tag (bd, commandline);
等等
theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);
theKernel (0, bd->bi_arch_number, bd->bi_boot_params); //啟動核心
啟動核心時傳遞了三個參數,其中
bd->bi_arch_number
是機器ID,
bd->bi_boot_params
是傳遞的參數位址