uboot1.1.6
配置分析
以smdk2410为例:执行make smdk2410_config
由顶层目录makefile开始
#makefile
MKCONFIG := $(SRCTREE)/mkconfig
smdk2410_config : unconfig
@$(MKCONFIG) $(@:_config=) arm arm920t smdk2410 NULL s3c24x0
/mkconfig smdk2410 arm arm920t smdk2410 NULL s3c2440
#mkconfig
[ "${BOARD_NAME}" ] || BOARD_NAME="$1" BOARD_NAME = smdk2410
cd ./include
rm -f asm
ln -s asm-$2 asm asm -> asm-arm
rm -f asm-$2/arch
LNPREFIX="../../include2/asm/"
ln -s ${LNPREFIX}arch-$6 asm-$2/arch aasm-arm/arch -> arch-s3c2440
rm -f asm-$2/proc
ln -s ${LNPREFIX}proc-armv asm-$2/proc asm-arm/proc -> proc-arv
echo "ARCH = $2" > config.mk ARCH = arm
echo "CPU = $3" >> config.mk CPU = arm920t
echo "BOARD = $4" >> config.mk BOARD = smdk2410
[ "$5" ] && [ "$5" != "NULL" ] && echo "VENDOR = $5" >> config.mk
[ "$6" ] && [ "$6" != "NULL" ] && echo "SOC = $6" >> config.mk
> config.h # Create new config file
echo "/* Automatically generated - do not edit */" >>config.h
echo "#include <configs/$1.h>" >>config.h
$(@:_config=) :目标文件去除后面字符
[ “$5” ] && [ “$5” != “NULL” ] && echo"VENDOR = $5" >> config.mk :第一个条件为真则继续执行。
ls -s 源文件 目标文件:建立目标文件到源文件的软链接(个人理解相当于win快捷方式)
总结:
make smdk2410_config
1.执行命令:
/mkconfig smdk2410 arm arm920t smdk2410 NULL s3c2440
在mkconfig中:
1.BOARD_NAME = smdk2440(如果是给开发板起新名字则需要在board目录下新建已开发版名称命名的文件夹)
2.进入include目录
文件重定向(与架构/指令集配置相关,一般不做更改)
在该目录下创建congig.mk,输入架构,开发板名称等信息
创建config.h,包含<config/BOARD_NAME.h> (开发板名称更改,则需在相应目录下新建该文件)
对配置的裁剪通过设置配置文件完成
对s3c2410:(不同板修改不同boardtype.h)
#/include/configs/smdk2410.h
Configuation settings for the SAMSUNG SMDK2410 board.
主要用于开发板主要参数设置及驱动裁剪工作。
例如:cpu类型,时钟,flash,网卡驱动等。
makefile 分析
ifeq ($(ARCH),arm)
CROSS_COMPILE = arm-linux-
OBJS = cpu/$(CPU)/start.o
OBJS += ...
LIBS = lib_generic/libgeneric.a
LIBS += ...
ALL = $(obj)u-boot.srec $(obj)u-boot.bin $(obj)System.map $(U_BOOT_NAND)
all: $(ALL)
$(obj)u-boot.bin: $(obj)u-boot
$(OBJCOPY) ${OBJCFLAGS} -O binary $< $@
$(obj)u-boot: depend version $(SUBDIRS) $(OBJS) $(LIBS) $(LDSCRIPT)
UNDEF_SYM=`$(OBJDUMP) -x $(LIBS) |sed -n -e 's/.*\(__u_boot_cmd_.*\)/-u\1/p'|sort|uniq`;\
cd $(LNDIR) && $(LD) $(LDFLAGS) $$UNDEF_SYM $(__OBJS) \
--start-group $(__LIBS) --end-group $(PLATFORM_LIBS) \
-Map u-boot.map -o u-boot
$(OBJS):
$(MAKE) -C cpu/$(CPU) $(if $(REMOTE_BUILD),$@,$(notdir $@))
$(LIBS):
$(MAKE) -C $(dir $(subst $(obj),,$@))
$(SUBDIRS):
$(MAKE) -C $@ all
$(NAND_SPL): version
$(MAKE) -C nand_spl/board/$(BOARDDIR) all
$(U_BOOT_NAND): $(NAND_SPL) $(obj)u-boot.bin
cat $(obj)nand_spl/u-boot-spl-16k.bin $(obj)u-boot.bin > $(obj)u-boot-nand.bin
链接文件(board/smdk2410/u-boot.lds):
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
/*OUTPUT_FORMAT("elf32-arm", "elf32-arm", "elf32-arm")*/
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
. = 0x00000000;
. = ALIGN(4);
.text :
{
cpu/arm920t/start.o (.text) //程序起始
*(.text)
}
. = ALIGN(4);
.rodata : { *(.rodata) }
. = ALIGN(4);
.data : { *(.data) }
. = ALIGN(4);
.got : { *(.got) }
. = .;
__u_boot_cmd_start = .;
.u_boot_cmd : { *(.u_boot_cmd) }
__u_boot_cmd_end = .;
. = ALIGN(4);
__bss_start = .;
.bss : { *(.bss) }
_end = .;
}
以上可知程序起始点位于start.s
第一阶段start.s
初始化向量表、关看门狗、设置栈、copy代码至SDRAM等。
.globl _start
_start: b reset
ldr pc, _undefined_instruction
ldr pc, _software_interrupt
ldr pc, _prefetch_abort
ldr pc, _data_abort
ldr pc, _not_used
ldr pc, _irq
ldr pc, _fiq
_undefined_instruction: .word undefined_instruction
_software_interrupt: .word software_interrupt
_prefetch_abort: .word prefetch_abort
_data_abort: .word data_abort
_not_used: .word not_used
_irq: .word irq
_fiq: .word fiq
.balignl 16,0xdeadbeef
异常向量设置,重启异常和七种异常模式
ldr指令能够支持长字节地址跳转,为程序进入sdram做准备。
_TEXT_BASE: 代码段基地址
.word TEXT_BASE
.globl _armboot_start c程序入口
_armboot_start:
.word _start
.globl _bss_start bss段接口
_bss_start:
.word __bss_start
.globl _bss_end
_bss_end:
.word _end
#ifdef CONFIG_USE_IRQ 设置中断模式栈
/* IRQ stack memory (calculated at run-time) */
.globl IRQ_STACK_START
IRQ_STACK_START:
.word 0x0badc0de
/* IRQ stack memory (calculated at run-time) */
.globl FIQ_STACK_START
FIQ_STACK_START:
.word 0x0badc0de
#endif
reset: 启动入口
* set the cpu to SVC32 mode cpu设为系统模式
/* turn off the watchdog */ 关看门狗
#ifndef CONFIG_SKIP_LOWLEVEL_INIT 使用sdram时调用
bl cpu_init_crit 设置存储控制器函数
#endif
#ifndef CONFIG_SKIP_RELOCATE_UBOOT
relocate: /* relocate U-Boot to RAM */
adr r0, _start /* r0 <- current position of code */
ldr r1, _TEXT_BASE /* test if we run from flash or RAM */
cmp r0, r1 /* don't reloc during debug */
beq stack_setup
ldr r2, _armboot_start
ldr r3, _bss_start
sub r2, r3, r2 /* r2 <- size of armboot */
add r2, r0, r2 /* r2 <- source end address */
copy_loop:
ldmia r0!, {r3-r10} /* copy from source address [r0] */
stmia r1!, {r3-r10} /* copy to target address [r1] */
cmp r0, r2 /* until source end addreee [r2] */
ble copy_loop
#endif /* CONFIG_SKIP_RELOCATE_UBOOT */
clear_bss: 清空bss
ldr r0, _bss_start /* find start of bss segment */
ldr r1, _bss_end /* stop here */
mov r2, #0x00000000 /* clear */
clbss_l:str r2, [r0] /* clear loop... */
add r0, r0, #4
cmp r0, r1
ble clbss_l
ldr pc, _start_armboot:进入c函数
第二阶段(_start_armboot)
主要完成开发板初始化,根据配置信息初始化各模块(串口、网卡等)
void start_armboot (void)
{
//typedef int (init_fnc_t) (void);函数指针,通过此指针访问各类初始化函数
init_fnc_t **init_fnc_ptr;
char *s;
#ifndef CFG_NO_FLASH
ulong size;
#endif
#if defined(CONFIG_VFD) || defined(CONFIG_LCD)
unsigned long addr;
#endif
/* Pointer is writable since we allocated a register for it */
gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));
/* compiler optimization barrier needed for GCC >= 3.4 */
__asm__ __volatile__("": : :"memory");
//结构体gd_t bd_t与开发板/系统配置相关信息
//推测config.mk配置过程相关信息在此储存
memset ((void*)gd, 0, sizeof (gd_t));
gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
memset (gd->bd, 0, sizeof (bd_t));
monitor_flash_len = _bss_start - _armboot_start;
//将初始化函数存储于数组中,通过函数指针访问完成初始化。
//通过修改相应寄存器设置获得初始化时钟,串口等设备。
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) {
hang ();
}
}
#ifndef CFG_NO_FLASH
/* configure available FLASH banks */
size = flash_init ();
display_flash_config (size);
#endif /* CFG_NO_FLASH */
#if defined(CONFIG_MISC_INIT_R)
/* miscellaneous platform dependent initialisations */
misc_init_r ();
#endif
/* enable exceptions */
enable_interrupts ();
/* Perform network card initialisation if necessary */
#ifdef CONFIG_DRIVER_CS8900
cs8900_get_enetaddr (gd->bd->bi_enetaddr);
#endif
/* main_loop() can return to retry autoboot, if so just run it again. */
for (;;) {
main_loop ();
}
/* NOTREACHED - no way out of command loop except booting */
}
main_loop
启动内核过程:
s = getenv ("bootcmd");
run_command (s, 0);
环境变量:环境变量的作用域和全局变量相同;环境变量的生命周期为一旦设置好环境变量并保存好后,下次开机还存在;而全局变量在关机后就灭亡了,下次开机产生了一个新的全局变量。
(摘自:https://blog.csdn.net/x_jjt/article/details/50733483)
/************************************************************************
- Look up variable from environment,
- return address of storage for that variable,
- or NULL if not found
*/
char *getenv (char *name)
{
int i, nxt;
WATCHDOG_RESET();
for (i=0; env_get_char(i) != '\0'; i=nxt+1) {
int val;
for (nxt=i; env_get_char(nxt) != '\0'; ++nxt) {
if (nxt >= CFG_ENV_SIZE) {
return (NULL);
}
}
if ((val=envmatch((uchar *)name, i)) < 0)
continue;
return ((char *)env_get_addr(val));
}
return (NULL);
void main_loop (void)
{
#ifndef CFG_HUSH_PARSER
static char lastcommand[CFG_CBSIZE] = { 0, };
int len;
int rc = 1;
int flag;
#endif
#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
char *s;
int bootdelay;
#endif
#ifdef CONFIG_PREBOOT
char *p;
#endif
//启动次数限制功能,启动次数限制可以被用户设置一个启动次数,然后保存在Flash存储器的特定位置,当到达启动次数后,U-Boot无法启动。该功能适合一些商业产品,通过配置不同的License限制用户重新启动系统。
#ifdef CONFIG_BOOTCOUNT_LIMIT
unsigned long bootcount = 0;
unsigned long bootlimit = 0;
char *bcs;
char bcs_set[16];
#endif /* CONFIG_BOOTCOUNT_LIMIT */
#if defined(CONFIG_VFD) && defined(VFD_TEST_LOGO)
ulong bmp = 0; /* default bitmap */
extern int trab_vfd (ulong bitmap);
#ifdef CONFIG_MODEM_SUPPORT
if (do_mdm_init)
bmp = 1; /* alternate bitmap */
#endif
trab_vfd (bmp);
#endif /* CONFIG_VFD && VFD_TEST_LOGO */
#ifdef CONFIG_BOOTCOUNT_LIMIT
bootcount = bootcount_load();
bootcount++;
bootcount_store (bootcount);
sprintf (bcs_set, "%lu", bootcount);
setenv ("bootcount", bcs_set);
bcs = getenv ("bootlimit");
bootlimit = bcs ? simple_strtoul (bcs, NULL, 10) : 0;
#endif /* CONFIG_BOOTCOUNT_LIMIT */
//Modem功能。如果系统中有Modem,打开该功能可以接受其他用户通过电话网络的拨号请求。Modem功能通常供一些远程控制的系统使用。
#ifdef CONFIG_MODEM_SUPPORT
debug ("DEBUG: main_loop: do_mdm_init=%d\n", do_mdm_init);
if (do_mdm_init) {
char *str = strdup(getenv("mdm_cmd"));
setenv ("preboot", str); /* set or delete definition */
if (str != NULL)
free (str);
mdm_init(); /* wait for modem connection */
}
#endif /* CONFIG_MODEM_SUPPORT */
//设置U-Boot的版本号,初始化命令自动完成功能等。动态版本号功能支持代码,version_string变量是在其他文件定义的一个字符串变量,当用户改变U-Boot版本的时候会更新该变量。打开动态版本支持功能后,U-Boot在启动的时候会显示最新的版本号。
#ifdef CONFIG_VERSION_VARIABLE
{
extern char version_string[];
setenv ("ver", version_string); /* set version variable */
}
#endif /* CONFIG_VERSION_VARIABLE */
#ifdef CFG_HUSH_PARSER
u_boot_hush_start ();
#endif
#ifdef CONFIG_AUTO_COMPLETE
install_auto_complete();
#endif
//设置命令行自动完成功能,该功能与Linux的shell类似,当用户输入一部分命令后,可以通过按下键盘上的Tab键补全命令的剩余部分。main_loop()函数不同的功能使用宏开关控制不仅能提高代码模块化,更主要的是针对嵌入式系统Flash存储器大小设计的。在嵌入式系统上,不同的系统Flash存储空间不同。对于一些Flash空间比较紧张的设备来说,通过宏开关关闭一些不是特别必要的功能如命令行自动完成,可以减小U-Boot编译后的文件大小。
#ifdef CONFIG_PREBOOT
if ((p = getenv ("preboot")) != NULL) {
# ifdef CONFIG_AUTOBOOT_KEYED
int prev = disable_ctrlc(1); /* disable Control C checking */
# endif
# ifndef CFG_HUSH_PARSER
run_command (p, 0);
# else
parse_string_outer(p, FLAG_PARSE_SEMICOLON |
FLAG_EXIT_FROM_LOOP);
# endif
# ifdef CONFIG_AUTOBOOT_KEYED
disable_ctrlc(prev); /* restore Control C checking */
# endif
}
#endif /* CONFIG_PREBOOT */
//启动延迟功能,需要等待用户从串口或者网络接口输入。如果用户按下任意键打断,启动流程,会向终端打印出一个启动菜单。
#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
s = getenv ("bootdelay");
bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;
debug ("### main_loop entered: bootdelay=%d\n\n", bootdelay);
# ifdef CONFIG_BOOT_RETRY_TIME
init_cmd_timeout ();
# endif /* CONFIG_BOOT_RETRY_TIME */
#ifdef CONFIG_BOOTCOUNT_LIMIT
if (bootlimit && (bootcount > bootlimit)) {
printf ("Warning: Bootlimit (%u) exceeded. Using altbootcmd.\n",
(unsigned)bootlimit);
s = getenv ("altbootcmd");
}
else
#endif /* CONFIG_BOOTCOUNT_LIMIT */
//取命令
s = getenv ("bootcmd");
debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");
//在启动命令非0(有启动命令)的情况下,如果在启动延时时间到之前按下键终止了,abortboot返回1,否则如果一直没有键按下,直到启动延时时间到,那么函数返回0。所以,如果按下键了,就可以跳过启动操作系统;如果没按就直接启动操作系统了。
if (bootdelay >= 0 && s && !abortboot (bootdelay)) {
# ifdef CONFIG_AUTOBOOT_KEYED
int prev = disable_ctrlc(1); /* disable Control C checking */
# endif
# ifndef CFG_HUSH_PARSER
run_command (s, 0);
# else
parse_string_outer(s, FLAG_PARSE_SEMICOLON |
FLAG_EXIT_FROM_LOOP);
# endif
# ifdef CONFIG_AUTOBOOT_KEYED
disable_ctrlc(prev); /* restore Control C checking */
# endif
}
# ifdef CONFIG_MENUKEY
if (menukey == CONFIG_MENUKEY) {
s = getenv("menucmd");
if (s) {
# ifndef CFG_HUSH_PARSER
run_command (s, 0);
# else
parse_string_outer(s, FLAG_PARSE_SEMICOLON |
FLAG_EXIT_FROM_LOOP);
# endif
}
}
#endif /* CONFIG_MENUKEY */
#endif /* CONFIG_BOOTDELAY */
#ifdef CONFIG_AMIGAONEG3SE
{
extern void video_banner(void);
video_banner();
}
#endif
//读取命令,解析命令。这是一个for死循环,该循环不断使用readline()函数(第463行)从控制台(一般是串口)读取用户的输入,然后解析。有关如何解析命令请参考U-Boot代码中run_command()函数的定义。
/*
* Main Loop for Monitor Command Processing
*/
#ifdef CFG_HUSH_PARSER
parse_file_outer();
/* This point is never reached */
for (;;);
#else
for (;;) {
#ifdef CONFIG_BOOT_RETRY_TIME
if (rc >= 0) {
/* Saw enough of a valid command to
* restart the timeout.
*/
reset_cmd_timeout();
}
#endif
len = readline (CFG_PROMPT);// 读取命令,读取到的命令存储在全局变量console_buffer 中
flag = 0; /* assume no special flags for now */
if (len > 0)
strcpy (lastcommand, console_buffer);//将读取到的命令copy到lastcommand中
else if (len == 0)
flag |= CMD_FLAG_REPEAT;
#ifdef CONFIG_BOOT_RETRY_TIME
else if (len == -2) {
/* -2 means timed out, retry autoboot
*/
puts ("\nTimed out waiting for command\n");
# ifdef CONFIG_RESET_TO_RETRY
/* Reinit board to run initialization code again */
do_reset (NULL, 0, 0, NULL);
# else
return; /* retry autoboot */
# endif
}
#endif
if (len == -1)
puts ("<INTERRUPT>\n");
else
rc = run_command (lastcommand, flag);//解析并运行命令
if (rc <= 0) {
/* invalid command or not repeatable, forget it */
lastcommand[0] = 0;
}
}
#endif /*CFG_HUSH_PARSER*/
}
main_loop注释参考:https://blog.csdn.net/bugouyonggan/article/details/37764447
命令体系
/****************************************************************************
* returns:
* 1 - command executed, repeatable
* 0 - command executed but not repeatable, interrupted commands are
* always considered not repeatable
* -1 - not executed (unrecognized, bootd recursion or too many args)
* (If cmd is NULL or "" or longer than CFG_CBSIZE-1 it is
* considered unrecognized)
*
* WARNING:
*
* We must create a temporary copy of the command since the command we get
* may be the result from getenv(), which returns a pointer directly to
* the environment data, which may change magicly when the command we run
* creates or modifies environment variables (like "bootp" does).
*/
int run_command (const char *cmd, int flag)
{
cmd_tbl_t *cmdtp;
char cmdbuf[CFG_CBSIZE]; /* working copy of cmd */
char *token; /* start of token in cmdbuf */
char *sep; /* end of token (separator) in cmdbuf */
char finaltoken[CFG_CBSIZE];
char *str = cmdbuf;
char *argv[CFG_MAXARGS + 1]; /* NULL terminated */
int argc, inquotes;
int repeatable = 1;
int rc = 0;
strcpy (cmdbuf, cmd);
/* Process separators and check for invalid
* repeatable commands
*/
while (*str) {
/*
* Find separator, or string end
* Allow simple escape of ';' by writing "\;"
*/
//命令拆解,将输入的命令按照分号拆解成单独指令
for (inquotes = 0, sep = str; *sep; sep++) {
if ((*sep=='\'') &&
(*(sep-1) != '\\'))
inquotes=!inquotes;
if (!inquotes &&
(*sep == ';') && /* separator */
( sep != str) && /* past string start */
(*(sep-1) != '\\')) /* and NOT escaped */
break;
}
/*
* Limit the token to data between separators
*/
token = str;
if (*sep) {
str = sep + 1; /* start of command for next pass */
*sep = '\0';
}
else
str = sep; /* no more commands for next pass */
/* find macros in this token and replace them */
process_macros (token, finaltoken);
/* Extract arguments */
if ((argc = parse_line (finaltoken, argv)) == 0) {
rc = -1; /* no command at all */
continue;
}
/* Look up command in command table */
//命令查找函数
//由此可知,通过find_cmd查找命令字符串并返回cmd_tbl_t类型指针
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;
}
#if (CONFIG_COMMANDS & CFG_CMD_BOOTD)
/* avoid "bootd" recursion */
if (cmdtp->cmd == do_bootd) {
#ifdef DEBUG_PARSER
printf ("[%s]\n", finaltoken);
#endif
if (flag & CMD_FLAG_BOOTD) {
puts ("'bootd' recursion detected\n");
rc = -1;
continue;
} else {
flag |= CMD_FLAG_BOOTD;
}
}
#endif /* CFG_CMD_BOOTD */
/* OK - call function to do the command */
//执行命令程序
if ((cmdtp->cmd) (cmdtp, flag, argc, argv) != 0) {
rc = -1;
}
repeatable &= cmdtp->repeatable;
/* Did the user stop this? */
if (had_ctrlc ())
return 0; /* if stopped then not repeatable */
}
return rc ? rc : repeatable;
}
cmd_tbl_s:
/*
* Monitor Command Table
*/
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:
/***************************************************************************
1. find command table entry for a command
*/
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;
int len;
int n_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);
//在__u_boot_cmd_start ~__u_boot_cmd_end之间查找命令。
//__u_boot_cmd_start 、__u_boot_cmd_end的定义位于u-boot.lds中。
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++;
}
}
if (n_found == 1) { /* exactly one match */
return cmdtp_temp;
}
return NULL; /* not found or ambiguous command */
}
以bootm为例解析命令封装过程:
- 在源码中搜索bootm,发现其被U_BOOT_CMD封装。
- 查找U_BOOT_CMD宏可发现其参数包括命令名称、最大参数、可重复性(按空格重复执行)、命令、使用说明、帮助信息。
- Struct_Section 将命令函数强制设定为段属性.u_boot_cmd(由链接文件可知该段位于在__u_boot_cmd_start 、__u_boot_cmd_end之间,与上文find_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}
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
);
命令扩展(如何自定义命令):https://blog.csdn.net/SilverFOX111/article/details/86892231
启动内核
启动阶段参考:https://blog.csdn.net/wangweijundeqq/article/details/81435399
s = getenv ("bootcmd");
run_command (s, 0);
环境变量bootcmd中包括命令bootm
由命令分析过程知bootm命令对应实现函数为do_bootm
//在嵌入式系统的启动过程中,开机时uboot运行在SoC内部的SRAM中,*uboot会在BL1阶段将整个uboot拷贝到SDRAM中0xC3E00000并远跳转到SDRAM中的BL2运行。Uboot启动kernel时,同样会将kernel从启动设备拷贝到SDRAM中的指定kernel链接位置,最终跳转到kernel运行。
//内核也有类似要求,uboot启动内核时将内存从SD卡读取放到DDR中(其实就是个重定位的过程),不能随意放置,必须放在内核的链接地址处,否则启动不起来。譬如我们使用的内核链接地址是0x30008000.
//uboot是无条件启动的,从零开始启动的。
//内核是不能开机自动完全从零开始启动的,内核启动要别人帮忙。uboot要帮助内核实现重定位(从SD卡到DDR),uboot还要给内核提供启动参数
int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
ulong iflag;
ulong addr;
ulong data, len, checksum;
ulong *len_ptr;
uint unc_len = CFG_BOOTM_LEN;
int i, verify;
char *name, *s;
int (*appl)(int, char *[]);
image_header_t *hdr = &header;
s = getenv ("verify");
verify = (s && (*s == 'n')) ? 0 : 1;
if (argc < 2) {
addr = load_addr;
} else {
addr = simple_strtoul(argv[1], NULL, 16);
}
SHOW_BOOT_PROGRESS (1);
printf ("## Booting image at %08lx ...\n", addr);
/* Copy header so we can blank CRC field for re-calculation */
/* for multi-file images we need the data part, too */
print_image_hdr ((image_header_t *)addr);
data = addr + sizeof(image_header_t);
len = ntohl(hdr->ih_size);
if (verify) {
puts (" Verifying Checksum ... ");
if (crc32 (0, (uchar *)data, len) != ntohl(hdr->ih_dcrc)) {
printf ("Bad Data CRC\n");
SHOW_BOOT_PROGRESS (-3);
return 1;
}
puts ("OK\n");
}
SHOW_BOOT_PROGRESS (4);
len_ptr = (ulong *)data;
#elif defined(__ARM__)
if (hdr->ih_arch != IH_CPU_ARM)
#endif
switch (hdr->ih_type) {
}
SHOW_BOOT_PROGRESS (6);
/*
* We have reached the point of no return: we are going to
* overwrite all exception vector code, so we cannot easily
* recover from any failures any more...
*/
iflag = disable_interrupts();
switch (hdr->ih_comp) {
}
puts ("OK\n");
SHOW_BOOT_PROGRESS (7);
switch (hdr->ih_type) {
//挂载内核
do_bootm_linux (cmdtp, flag, argc, argv,
addr, len_ptr, verify);
break;
#ifdef DEBUG
puts ("\n## Control returned to monitor - resetting...\n");
do_reset (cmdtp, flag, argc, argv);
#endif
return 1;
}
do_bootm_linux (cmdtp, flag, argc, argv, addr, len_ptr, verify);
void do_bootm_linux (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[], ulong addr, ulong *len_ptr, int verify)
{
ulong len = 0, checksum;
ulong initrd_start, initrd_end;
ulong data;
void (*theKernel)(int zero, int arch, uint params);
image_header_t *hdr = &header;
bd_t *bd = gd->bd;
#ifdef CONFIG_CMDLINE_TAG
char *commandline = getenv ("bootargs");
#endif
theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);
/*
* Check if there is an initrd image
*/
if (verify) {
ulong csum = 0;
printf (" Verifying Checksum ... ");
csum = crc32 (0, (unsigned char *) data, len);
if (csum != ntohl (hdr->ih_dcrc)) {
printf ("Bad Data CRC\n");
SHOW_BOOT_PROGRESS (-12);
do_reset (cmdtp, flag, argc, argv);
}
printf ("OK\n");
}
SHOW_BOOT_PROGRESS (11);
if ((hdr->ih_os != IH_OS_LINUX) ||
(hdr->ih_arch != IH_CPU_ARM) ||
(hdr->ih_type != IH_TYPE_RAMDISK)) {
printf ("No Linux ARM Ramdisk Image\n");
SHOW_BOOT_PROGRESS (-13);
do_reset (cmdtp, flag, argc, argv);
}
/*
* Now check if we have a multifile image
*/
#ifdef DEBUG
if (!data) {
printf ("No initrd\n");
}
#endif
if (data) {
initrd_start = data;
initrd_end = initrd_start + len;
} else {
initrd_start = 0;
initrd_end = 0;
}
SHOW_BOOT_PROGRESS (15);
debug ("## Transferring control to Linux (at address %08lx) ...\n",
(ulong) theKernel);
//参数设置
#if defined (CONFIG_SETUP_MEMORY_TAGS) || \
defined (CONFIG_CMDLINE_TAG) || \
defined (CONFIG_INITRD_TAG) || \
defined (CONFIG_SERIAL_TAG) || \
defined (CONFIG_REVISION_TAG) || \
defined (CONFIG_LCD) || \
defined (CONFIG_VFD)
setup_start_tag (bd);
#ifdef CONFIG_SERIAL_TAG
setup_serial_tag (¶ms);
#endif
#ifdef CONFIG_REVISION_TAG
setup_revision_tag (¶ms);
#endif
#ifdef CONFIG_SETUP_MEMORY_TAGS
setup_memory_tags (bd);
#endif
#ifdef CONFIG_CMDLINE_TAG
setup_commandline_tag (bd, commandline);
#endif
#ifdef CONFIG_INITRD_TAG
if (initrd_start && initrd_end)
setup_initrd_tag (bd, initrd_start, initrd_end);
#endif
#if defined (CONFIG_VFD) || defined (CONFIG_LCD)
setup_videolfb_tag ((gd_t *) gd);
#endif
setup_end_tag (bd);
#endif
/* we assume that the kernel is in place */
printf ("\nStarting kernel ...\n\n");
#ifdef CONFIG_USB_DEVICE
{
extern void udc_disconnect (void);
udc_disconnect ();
}
#endif
cleanup_before_linux ();
//启动内核
theKernel (0, bd->bi_arch_number, bd->bi_boot_params);
}
setup_start_tag (bd);
setup_commandline_tag (bd, commandline);
setup_end_tag (bd);
setup_memory_tags (bd);
#if defined (CONFIG_SETUP_MEMORY_TAGS) || \
defined (CONFIG_CMDLINE_TAG) || \
defined (CONFIG_INITRD_TAG) || \
defined (CONFIG_SERIAL_TAG) || \
defined (CONFIG_REVISION_TAG) || \
defined (CONFIG_LCD) || \
defined (CONFIG_VFD)
static void setup_start_tag (bd_t *bd)
{
params = (struct tag *) bd->bi_boot_params;
params->hdr.tag = ATAG_CORE;
params->hdr.size = tag_size (tag_core);
params->u.core.flags = 0;
params->u.core.pagesize = 0;
params->u.core.rootdev = 0;
params = tag_next (params);
}