Uboot流程分析
Posted maogefff
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Uboot流程分析相关的知识,希望对你有一定的参考价值。
1. uboot的配置分析
1).配置入口分析
首先分析配置:
从make mx6dl_sabresd_android_config可知配置项,搜索Makefile:
mx6solo_sabresd_android_config \
mx6dl_sabresd_config \
mx6dl_sabresd_mfg_config \
mx6dl_sabresd_android_config \
mx6q_sabresd_config \
mx6q_sabresd_android_config \
mx6q_sabresd_mfg_config \
mx6q_sabresd_iram_config : unconfig
@[ -z "$(findstring iram_,[email protected])" ] || \
{echo "TEXT_BASE = 0x00907000" > $(obj)board/freescale/mx6q_sabresd/config.tmp ; \
echo "... with iram configuration" ; \
}
@$(MKCONFIG) $(@:_config=) arm arm_cortexa8 mx6q_sabresd freescale mx6
其中MKCONFIG就是mkconfig
展开后结果为:mkconfig mx6dl_sabresd_android arm arm_cortexa8 mx6q_sabresd freescale mx6
2). mkconfig
然后打开mkconfig查看:
APPEND=no # Default: Create new config file
BOARD_NAME="" # Name to print in make output
#循环查看参数并处理
while [ $# -gt 0 ] ; do
case "$1" in
--) shift ; break ;;
-a) shift ; APPEND=yes ;;
-n) shift ; BOARD_NAME="${1%%_config}" ; shift ;;
*) break ;;
esac
done
[ "${BOARD_NAME}" ] || BOARD_NAME="$1"
#当参数数量小于4或者大于6个的时候退出
[ $# -lt 4 ] && exit 1
[ $# -gt 6 ] && exit 1
#这里就是配置时窗口显示的Configuring for mx6dl_sabresd_android board...
echo "Configuring for ${BOARD_NAME} board..."
#
# Create link to architecture specific headers
# 根据不同的架构,将不同的文件进行软连接
if [ "$SRCTREE" != "$OBJTREE" ] ; then
mkdir -p ${OBJTREE}/include
mkdir -p ${OBJTREE}/include2
cd ${OBJTREE}/include2
rm -f asm
ln -s ${SRCTREE}/include/asm-$2 asm
LNPREFIX="../../include2/asm/"
cd ../include
rm -rf asm-$2
rm -f asm
mkdir asm-$2
ln -s asm-$2 asm
else
cd ./include
rm -f asm
ln -s asm-$2 asm
fi
rm -f asm-$2/arch
if [ -z "$6" -o "$6" = "NULL" ] ; then
ln -s ${LNPREFIX}arch-$3 asm-$2/arch
else
ln -s ${LNPREFIX}arch-$6 asm-$2/arch
fi
if [ "$2" = "arm" ] ; then
rm -f asm-$2/proc
ln -s ${LNPREFIX}proc-armv asm-$2/proc
fi
#创建一个/include/config.mk,并将传进来的参数放入里面
echo "ARCH = $2" > config.mk
echo "CPU = $3" >> config.mk
echo "BOARD = $4" >> config.mk
[ "$5" ] && [ "$5" != "NULL" ] && echo "VENDOR = $5" >> config.mk
[ "$6" ] && [ "$6" != "NULL" ] && echo "SOC = $6" >> config.mk
#创建/include/config.h并将两个架构相关的头文件放入其中
if [ "$APPEND" = "yes" ] # Append to existing config file
then
echo >> config.h
else
> config.h # Create new config file
fi
echo "/* Automatically generated - do not edit */" >>config.h
echo "#include <configs/$1.h>" >>config.h
echo "#include <asm/config.h>" >>config.h
exit 0
查看MKCONFIG可知,最后这五个会生成include/config.mk中
ARCH = arm
CPU = arm_cortexa8
BOARD = mx6q_sabresd
VENDOR = freescale
SOC = mx6
由此可以推出./board/freescale/mx6q_sabresd/u-boot.lds
由此可知入口为flash_header.S
3).编译过程分析
通过之前分析查看/include/config.mk
ARCH = arm
CPU = arm_cortexa8
BOARD = mx6q_sabresd
VENDOR = freescale
SOC = mx6
通过全局搜索可以找到/board/freescale/mx6q_sabresd/u-boot.lds为链接脚本文件
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
. = 0x00000000;
. = ALIGN(4);
.text :
{
/* WARNING - the following is hand-optimized to fit within */
/* the sector layout of our flash chips! XXX FIXME XXX */
board/freescale/mx6q_sabresd/flash_header.o (.text.flasheader)
cpu/arm_cortexa8/start.o
board/freescale/mx6q_sabresd/libmx6q_sabresd.a (.text)
lib_arm/libarm.a (.text)
net/libnet.a (.text)
drivers/mtd/libmtd.a (.text)
drivers/mmc/libmmc.a (.text)
. = DEFINED(env_offset) ? env_offset : .;
common/env_embedded.o(.text)
*(.text)
}
. = ALIGN(4);
.rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.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);
_end_of_copy = .; /* end_of ROM copy code here */
/* Extend to align to 0x1000, then put the Hab Data */
. = ALIGN(0x1000);
__hab_data = .;
. = . + 0x2000;
__data_enc_key = .;
/* actually, only 64bytes are needed, but this generates
a size multiple of 512bytes, which is optimal for SD boot */
. = . + 0x200;
__hab_data_end = .;
/* End of Hab Data, Place it before BSS section */
__bss_start = .;
.bss : { *(.bss) }
_end = .;
}
从这个文件可知,代码的入口为_start
text为代码段,rodata为只读数据段,u_boot_cmd为命令的存放空间。
发现第一个文件为board/freescale/mx6q_sabresd/flash_header.S,他是保存在(.text.flasheader)段中
2. uboot的第一阶段
从flash_header.S中可知
.section ".text.flasheader", "x"
b _start
这个时候跳入了_start中了,其在cpu/arm_cortexa8/start.S当中
/*第一步:首先进行复位*/
.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
_pad: .word 0x12345678 /* now 16*4=64 */
.global _end_vect
_end_vect:
.balignl 16,0xdeadbeef
。。。省略。。。
/*复位,设置CPSR寄存器*/
reset:
/*
* set the cpu to SVC32 mode
*/
mrs r0, cpsr
bic r0, r0, #0x1f
orr r0, r0, #0xd3
msr cpsr,r0
/*没有定义CONFIG_SKIP_LOWLEVEL_INIT ,所以进入cpu_init_crit */
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_crit
#endif
。。。省略。。。
cpu_init_crit:
/*
* Invalidate L1 I/D
*/
mov r0, #0 @ set up for MCR
mcr p15, 0, r0, c8, c7, 0 @ invalidate TLBs
mcr p15, 0, r0, c7, c5, 0 @ invalidate icache
/*关闭mmu和catch*/
mrc p15, 0, r0, c1, c0, 0
bic r0, r0, #0x00002000 @ clear bits 13 (--V-)
bic r0, r0, #0x00000007 @ clear bits 2:0 (-CAM)
orr r0, r0, #0x00000002 @ set bit 1 (--A-) Align
orr r0, r0, #0x00000800 @ set bit 12 (Z---) BTB
mcr p15, 0, r0, c1, c0, 0
mov ip, lr @ persevere link reg across call
bl lowlevel_init @这里是跳转到lowlevel_init去初始化一些基本的板级信息。
mov lr, ip @ restore link
mov pc, lr @ back to my caller
通过全局搜索可知lowlevel_init在board\freescale\mx6q_sabresd目录中的lowlevel_init.S中,打开lowlevel_init.S
.globl lowlevel_init
lowlevel_init:
/* 禁止D-CACHE */
inv_dcache
/*禁止L2Cache */
init_l2cc
init_aips
/*初始化时钟*/
init_clock
/*跳回去*/
mov pc, lr
返回到start.S中,如下代码可知,返回到了cpu_init_crit调用之后的代码
#ifndef CONFIG_SKIP_RELOCATE_UBOOT
/*比较_start 的值和_TEXT_BASE的值是否一致,如果不一致的话则进行拷贝,
*在uboot的第一阶段是不一致的,因为_start为nor flash的地址值,而
*_TEST_BASE为RAM中确定的运行地址值 */
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: @ copy 32 bytes at a time
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 */
/*重定位之后,设置堆栈,,初始化C语言环境 */
stack_setup:
ldr r0, _TEXT_BASE @ upper 128 KiB: relocated uboot
sub r0, r0, #CONFIG_SYS_MALLOC_LEN @ malloc area
sub r0, r0, #CONFIG_SYS_GBL_DATA_SIZE @ bdinfo
#ifdef CONFIG_USE_IRQ
sub r0, r0, #(CONFIG_STACKSIZE_IRQ + CONFIG_STACKSIZE_FIQ)
#endif
sub sp, r0, #12 @ leave 3 words for abort-stack
and sp, sp, #~7 @ 8 byte alinged for (ldr/str)d
/* Clear BSS (if any). Is below tx (watch load addr - need space) */
clear_bss:
ldr r0, _bss_start @ find start of bss segment
ldr r1, _bss_end @ stop here
mov r2, #0x00000000 @ clear value
clbss_l:
str r2, [r0] @ clear BSS location
cmp r0, r1 @ are we at the end yet
add r0, r0, #4 @ increment clear index pointer
bne clbss_l @ keep clearing till at end
#ifdef CONFIG_ARCH_MMU
bl board_mmu_init
#endif
ldr pc, _start_armboot @ jump to C code
/*从这里跳入uboot的第二阶段*/
_start_armboot: .word start_armboot
第一阶段流程总结:
flash_header.S b _start
start.S _start: b reset //复位
start.S cpu_init_crit //关闭mmu和catch
lowlevel_init.S lowlevel_init //关闭D-CACHE和L2Cache,初始化时钟
start.S relocate //查看是否需要代码进行重定位
start.S copy_loop //代码进行重定位
start.S stack_setup //设置堆栈,清BSS等初始化C语言环境
start.S start_armboot //跳入第二阶段
3. uboot的第二阶段
通过搜索可知第二阶段代码在:lib_arm/board.c中的start_armboot
void start_armboot (void)
{
init_fnc_t **init_fnc_ptr;
char *s;
#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 - CONFIG_SYS_MALLOC_LEN - sizeof(gd_t));
/* compiler optimization barrier needed for GCC >= 3.4 */
__asm__ __volatile__("": : :"memory");
memset ((void*)gd, 0, sizeof (gd_t));
gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
memset (gd->bd, 0, sizeof (bd_t));
gd->flags |= GD_FLG_RELOC;
monitor_flash_len = _bss_start - _armboot_start;
/*这里的init_sequence是一个函数指针数组,他将分别对硬件进行初始化*/
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) {
hang ();
}
}
在lib_arm/board.c中查看所初始化的函数:
init_fnc_t *init_sequence[] = {
#if defined(CONFIG_ARCH_CPU_INIT)
arch_cpu_init, /* CPU基本的初始化:打开icache,dcache,清看门狗,和一些外设掉电 */
#endif
board_init, /* 板子的基本信息:机器码=3980,LCD基本信息 */
#if defined(CONFIG_USE_IRQ)
interrupt_init, /* set up exceptions */
#endif
timer_init, /*初始化定时器*/
env_init, /* 初始化环境变量,所在文件为common/env_sf.c */
init_baudrate, /* 设置波特率 */
serial_init, /* 设置串口 */
console_init_f, /* 设置是否有无控制台 */
display_banner, /*打印一些基本信息给控制台*/
#if defined(CONFIG_DISPLAY_CPUINFO)
print_cpuinfo, /* display cpu info (and speed) */
#endif
#if defined(CONFIG_DISPLAY_BOARDINFO)
checkboard, /* display board info */
#endif
#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)
init_func_i2c,
#endif
dram_init, /* 设置DRAM的大小和地址 */
#if defined(CONFIG_CMD_PCI) || defined (CONFIG_PCI)
arm_pci_init,
#endif
display_dram_config, /*打印配置的DRAM的基本信息*/
NULL,
};
其中env_init为环境变量的初始化,所在文件为common/env_sf.c
int env_init(void)
{
/* SPI flash isn‘t usable before relocation */
gd->env_addr = (ulong)&default_environment[0];
gd->env_valid = 1;
return 0;
}
default_environment则为默认环境变量在/common/env_common.c中
uchar default_environment[] = {
#ifdef CONFIG_BOOTARGS
"bootargs=" CONFIG_BOOTARGS "\0"
#endif
#ifdef CONFIG_BOOTCOMMAND
"bootcmd=" CONFIG_BOOTCOMMAND "\0"
#endif
#ifdef CONFIG_RAMBOOTCOMMAND
"ramboot=" CONFIG_RAMBOOTCOMMAND "\0"
#endif
#ifdef CONFIG_NFSBOOTCOMMAND
"nfsboot=" CONFIG_NFSBOOTCOMMAND "\0"
#endif
#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
"bootdelay=" MK_STR(CONFIG_BOOTDELAY) "\0"
#endif
#if defined(CONFIG_BAUDRATE) && (CONFIG_BAUDRATE >= 0)
"baudrate=" MK_STR(CONFIG_BAUDRATE) "\0"
#endif
#ifdef CONFIG_LOADS_ECHO
"loads_echo=" MK_STR(CONFIG_LOADS_ECHO) "\0"
#endif
#ifdef CONFIG_ETHADDR
"ethaddr=" MK_STR(CONFIG_ETHADDR) "\0"
#endif
#ifdef CONFIG_ETH1ADDR
"eth1addr=" MK_STR(CONFIG_ETH1ADDR) "\0"
#endif
#ifdef CONFIG_ETH2ADDR
"eth2addr=" MK_STR(CONFIG_ETH2ADDR) "\0"
#endif
#ifdef CONFIG_ETH3ADDR
"eth3addr=" MK_STR(CONFIG_ETH3ADDR) "\0"
#endif
#ifdef CONFIG_ETH4ADDR
"eth4addr=" MK_STR(CONFIG_ETH4ADDR) "\0"
#endif
#ifdef CONFIG_ETH5ADDR
"eth5addr=" MK_STR(CONFIG_ETH5ADDR) "\0"
#endif
#ifdef CONFIG_IPADDR
"ipaddr=" MK_STR(CONFIG_IPADDR) "\0"
#endif
#ifdef CONFIG_SERVERIP
"serverip=" MK_STR(CONFIG_SERVERIP) "\0"
#endif
#ifdef CONFIG_SYS_AUTOLOAD
"autoload=" CONFIG_SYS_AUTOLOAD "\0"
#endif
#ifdef CONFIG_PREBOOT
"preboot=" CONFIG_PREBOOT "\0"
#endif
#ifdef CONFIG_ROOTPATH
"rootpath=" MK_STR(CONFIG_ROOTPATH) "\0"
#endif
#ifdef CONFIG_GATEWAYIP
"gatewayip=" MK_STR(CONFIG_GATEWAYIP) "\0"
#endif
#ifdef CONFIG_NETMASK
"netmask=" MK_STR(CONFIG_NETMASK) "\0"
#endif
#ifdef CONFIG_HOSTNAME
"hostname=" MK_STR(CONFIG_HOSTNAME) "\0"
#endif
#ifdef CONFIG_BOOTFILE
"bootfile=" MK_STR(CONFIG_BOOTFILE) "\0"
#endif
#ifdef CONFIG_LOADADDR
"loadaddr=" MK_STR(CONFIG_LOADADDR) "\0"
#endif
#ifdef CONFIG_RD_LOADADDR
"rd_loadaddr=" MK_STR(CONFIG_RD_LOADADDR) "\0"
#endif
#ifdef CONFIG_CLOCKS_IN_MHZ
"clocks_in_mhz=1\0"
#endif
#if defined(CONFIG_PCI_BOOTDELAY) && (CONFIG_PCI_BOOTDELAY > 0)
"pcidelay=" MK_STR(CONFIG_PCI_BOOTDELAY) "\0"
#endif
#ifdef CONFIG_EXTRA_ENV_SETTINGS
CONFIG_EXTRA_ENV_SETTINGS
#endif
"\0"
};
返回到lib_arm/board.c中的start_armboot接着往下看
/* armboot_start is defined in the board-specific linker script */
mem_malloc_init (_armboot_start - CONFIG_SYS_MALLOC_LEN);
#ifndef CONFIG_SYS_NO_FLASH
/* 初始化Flash */
display_flash_config (flash_init ());
#endif /* CONFIG_SYS_NO_FLASH */
。。。省略。。。
#ifdef CONFIG_LCD
/* 在之前arch_cpu_init进行过设置gd->fb_base = CONFIG_FB_BASE=(TEXT_BASE + 0x300000)
所以这里就不进入了*/
if (!gd->fb_base) {
# ifndef PAGE_SIZE
# define PAGE_SIZE 4096
# endif
/*
* reserve memory for LCD display (always full pages)
*/
/* bss_end is defined in the board-specific linker script */
addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
lcd_setmem (addr);
gd->fb_base = addr;
}
#endif /* CONFIG_LCD */
。。。省略。。。
#ifdef CONFIG_GENERIC_MMC
/*初始化MMC设备*/
puts ("MMC: ");
mmc_initialize (gd->bd);
#endif
/* 加载环境变量,如果没有则使用默认的 */
env_relocate ();
。。。省略。。。
/* IP Address */
gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");
#if defined CONFIG_SPLASH_SCREEN && defined CONFIG_VIDEO_MX5
setup_splash_image();
#endif
/*对设备进行初始化,并将其加入链表进行统一的管理*/
stdio_init (); /* get the devices list going. */
在stdio_init中,采用了uboot的设备管理模型对设备进行了管理,对于设备的具体操作方式统一了接口,并将它们放在了链表上进行统一的管理。如下:
int stdio_init (void)
{
#ifndef CONFIG_ARM /* already relocated for current ARM implementation */
ulong relocation_offset = gd->reloc_off;
int i;
/* relocate device name pointers */
for (i = 0; i < (sizeof (stdio_names) / sizeof (char *)); ++i) {
stdio_names[i] = (char *) (((ulong) stdio_names[i]) +
relocation_offset);
}
#endif
/* 初始化设备链表 */
INIT_LIST_HEAD(&(devs.list));
#ifdef CONFIG_ARM_DCC_MULTI
drv_arm_dcc_init ();
#endif
#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)
/*初始化I2C设备*/
i2c_init (CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
#endif
#ifdef CONFIG_LCD
/*初始化LCD设备*/
drv_lcd_init ();
#endif
#if defined(CONFIG_VIDEO) || defined(CONFIG_CFB_CONSOLE)
/*初始化视频设备*/
drv_video_init ();
#endif
#ifdef CONFIG_KEYBOARD
/*初始化按键设备*/
drv_keyboard_init ();
#endif
#ifdef CONFIG_LOGBUFFER
drv_logbuff_init ();
#endif
/*初始化系统设备*/
drv_system_init ();
#ifdef CONFIG_SERIAL_MULTI
serial_stdio_init ();
#endif
#ifdef CONFIG_USB_TTY
/*初始化usbtty设备*/
drv_usbtty_init ();
#endif
#ifdef CONFIG_NETCONSOLE
drv_nc_init ();
#endif
#ifdef CONFIG_JTAG_CONSOLE
drv_jtag_console_init ();
#endif
return (0);
}
继续接下来start_armboot中的代码。
/*将一些常用的函数统一放入gd->jt当中*/
jumptable_init ();
。。。省略。。。
/*控制台*/
console_init_r (); /* fully init console as a device */
。。。省略。。。
/* enable exceptions */
enable_interrupts ();
/* Perform network card initialisation if necessary */
。。。省略。。。
#if defined(CONFIG_DRIVER_SMC91111) || defined (CONFIG_DRIVER_LAN91C96)
/* XXX: this needs to be moved to board init */
if (getenv ("ethaddr")) {
uchar enetaddr[6];
eth_getenv_enetaddr("ethaddr", enetaddr);
smc_set_mac_addr(enetaddr);
}
#endif /* CONFIG_DRIVER_SMC91111 || CONFIG_DRIVER_LAN91C96 */
#if defined(CONFIG_ENC28J60_ETH) && !defined(CONFIG_ETHADDR)
extern void enc_set_mac_addr (void);
enc_set_mac_addr ();
#endif /* CONFIG_ENC28J60_ETH && !CONFIG_ETHADDR*/
/* Initialize from environment */
if ((s = getenv ("loadaddr")) != NULL) {
load_addr = simple_strtoul (s, NULL, 16);
}
#if defined(CONFIG_CMD_NET)
if ((s = getenv ("bootfile")) != NULL) {
copy_filename (BootFile, s, sizeof (BootFile));
}
#endif
#ifdef BOARD_LATE_INIT
board_late_init ();
#endif
#ifdef CONFIG_ANDROID_RECOVERY
check_recovery_mode();
#endif
#if defined(CONFIG_CMD_NET)
#if defined(CONFIG_NET_MULTI)
puts ("Net: ");
#endif
eth_initialize(gd->bd);
#if defined(CONFIG_RESET_PHY_R)
debug ("Reset Ethernet PHY\n");
reset_phy();
#endif
#endif
#ifdef CONFIG_FASTBOOT
check_fastboot_mode();
#endif
/* main_loop() can return to retry autoboot, if so just run it again. */
for (;;) {
main_loop ();
}
}
在main_loop后就进入了死循环,不停的接收命令,执行命令,其代码在/common/main.c当中
void main_loop (void)
{
#ifndef CONFIG_SYS_HUSH_PARSER
static char lastcommand[CONFIG_SYS_CBSIZE] = { 0, };
int len;
int rc = 1;
int flag;
#endif
#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
char *s;
int bootdelay;
#endif
。。。省略。。。
#ifdef CONFIG_SYS_HUSH_PARSER
u_boot_hush_start ();
#endif
#ifdef CONFIG_AUTO_COMPLETE
install_auto_complete();
#endif
#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
/*从环境变量中获取到bootdelay的值*/
s = getenv ("bootdelay");
bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;
debug ("### main_loop entered: bootdelay=%d\n\n", bootdelay);
。。。省略。。。
/*从环境变量中获取到bootcmd的值*/
s = getenv ("bootcmd");
debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");
/*等待bootdelay秒并且没有按下确认键则进入if*/
if (bootdelay >= 0 && s && !abortboot (bootdelay)) {
。。。省略。。。
# ifndef CONFIG_SYS_HUSH_PARSER
run_command (s, 0);
# else
/*在这里执行bootcmd,正常流程就是进入到启动内核程序中了*/
parse_string_outer(s, FLAG_PARSE_SEMICOLON |
FLAG_EXIT_FROM_LOOP);
# endif
。。。省略。。。
}
#endif /* CONFIG_BOOTDELAY */
#ifdef CONFIG_SYS_HUSH_PARSER
/*这里进入循环的命令解析执行*/
parse_file_outer();
/* 不会到这里 */
for (;;);
#else
。。。省略。。。
#endif /*CONFIG_SYS_HUSH_PARSER*/
}
4. uboot启动kernel
首先注意比较重要的结构体cmd_tbl_s
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
/* do auto completion on the arguments */
int (*complete)(int argc, char *argv[], char last_char, int maxv, char *cmdv[]);
#endif
};
Bootm的宏为:
U_BOOT_CMD(
bootm, CONFIG_SYS_MAXARGS, 1, do_bootm,
。。。省略。。。
);
宏展开后可以推出:
cmd_tbl_t __u_boot_cmd_bootm = {"bootm", CONFIG_SYS_MAXARGS, 1, do_bootm, 。。。}
启动流程:
lib_arm/bootm.c
int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
ulong iflag;
ulong load_end = 0;
int ret;
boot_os_fn *boot_fn;
/* relocate boot function table */
if (!relocated) {
int i;
for (i = 0; i < ARRAY_SIZE(boot_os); i++)
if (boot_os[i] != NULL)
boot_os[i] += gd->reloc_off;
relocated = 1;
}
。。。省略。。。
/*取得镜像信息*/
if (bootm_start(cmdtp, flag, argc, argv))
return 1;
/*关中断*/
iflag = disable_interrupts();
#if defined(CONFIG_CMD_USB)
usb_stop();
#endif
#ifdef CONFIG_AMIGAONEG3SE
icache_disable();
dcache_disable();
#endif
/*加载内核,在这里面首先会判断内核镜像是否需要解压缩。我们把解压缩放在了kernel的开头进行,所以在这里并不需要,然后再把内核加载到指定的地址*/
ret = bootm_load_os(images.os, &load_end, 1);
if (ret < 0) {
if (ret == BOOTM_ERR_RESET)
do_reset (cmdtp, flag, argc, argv);
if (ret == BOOTM_ERR_OVERLAP) {
if (images.legacy_hdr_valid) {
if (image_get_type (&images.legacy_hdr_os_copy) == IH_TYPE_MULTI)
puts ("WARNING: legacy format multi component "
"image overwritten\n");
} else {
puts ("ERROR: new format image overwritten - "
"must RESET the board to recover\n");
show_boot_progress (-113);
do_reset (cmdtp, flag, argc, argv);
}
}
if (ret == BOOTM_ERR_UNIMPLEMENTED) {
if (iflag)
enable_interrupts();
show_boot_progress (-7);
return 1;
}
}
lmb_reserve(&images.lmb, images.os.load, (load_end - images.os.load));
。。。省略。。。
show_boot_progress (8);
#ifdef CONFIG_SILENT_CONSOLE
if (images.os.os == IH_OS_LINUX)
fixup_silent_linux();
#endif
/*获取到启动函数的函数指针,在这里为do_bootm_linux*/
boot_fn = boot_os[images.os.os];
/*跳转至do_bootm_linux*/
boot_fn(0, argc, argv, &images);
/*不会运行到这里*/
show_boot_progress (-9);
#ifdef DEBUG
puts ("\n## Control returned to monitor - resetting...\n");
#endif
do_reset (cmdtp, flag, argc, argv);
return 1;
}
其中比较关键的函数为bootm_start(获取镜像信息)和bootm_load_os(加载内核)还有在bootm.c当中的do_bootm_linux,下面我们来看一下bootm_start实现的功能:
static int bootm_start(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
ulong mem_start;
phys_size_t mem_size;
void *os_hdr;
int ret;
/*先将信息清零,然后再填充*/
memset ((void *)&images, 0, sizeof (images));
images.verify = getenv_yesno ("verify");
lmb_init(&images.lmb);
/*获取到dram的起始地址*/
mem_start = getenv_bootm_low();
/*获取到dram的大小*/
mem_size = getenv_bootm_size();
/*应该是对dram进行某些管理*/
lmb_add(&images.lmb, (phys_addr_t)mem_start, mem_size);
arch_lmb_reserve(&images.lmb);
board_lmb_reserve(&images.lmb);
/* 得到内核镜像的头,启动地址和长度 ,在我们这里是由自己固定的,从0x10800000处取出,在得到内核后,接下来会调用image_get_kernel对内核镜像进行检测:包括对魔数、头的CRC、内核架构,image_start和image_len为函数在去掉内核镜像头部信息以后的地址和长度,如果正确,则返回内核地址,错误返回NULL*/
os_hdr = boot_get_kernel (cmdtp, flag, argc, argv,
&images, &images.os.image_start, &images.os.image_len);
if (images.os.image_len == 0) {
puts ("ERROR: can‘t get kernel image!\n");
return 1;
}
/* 得到镜像的一些参数 */
switch (genimg_get_format (os_hdr)) {
case IMAGE_FORMAT_LEGACY:
images.os.type = image_get_type (os_hdr);
images.os.comp = image_get_comp (os_hdr);
images.os.os = image_get_os (os_hdr);
images.os.end = image_get_image_end (os_hdr);
images.os.load = image_get_load (os_hdr);
break;
。。。省略。。。
default:
puts ("ERROR: unknown image format type!\n");
return 1;
}
/* find kernel entry point */
if (images.legacy_hdr_valid) {
images.ep = image_get_ep (&images.legacy_hdr_os_copy);
#if defined(CONFIG_FIT)
} else if (images.fit_uname_os) {
ret = fit_image_get_entry (images.fit_hdr_os,
images.fit_noffset_os, &images.ep);
if (ret) {
puts ("Can‘t get entry point property!\n");
return 1;
}
#endif
} else {
puts ("Could not find kernel entry point!\n");
return 1;
}
if (images.os.os == IH_OS_LINUX) {
/* find ramdisk */
ret = boot_get_ramdisk (argc, argv, &images, IH_INITRD_ARCH,
&images.rd_start, &images.rd_end);
if (ret) {
puts ("Ramdisk image is corrupt or invalid\n");
return 1;
}
#if defined(CONFIG_OF_LIBFDT)
#if defined(CONFIG_PPC) || defined(CONFIG_M68K) || defined(CONFIG_SPARC)
/* find flattened device tree */
ret = boot_get_fdt (flag, argc, argv, &images,
&images.ft_addr, &images.ft_len);
if (ret) {
puts ("Could not find a valid device tree\n");
return 1;
}
set_working_fdt_addr(images.ft_addr);
#endif
#endif
}
images.os.start = (ulong)os_hdr;
images.state = BOOTM_STATE_START;
return 0;
}
从do_bootm可知,最后会跳转至lib_arm/bootm.c中的do_bootm_linux
int do_bootm_linux(int flag, int argc, char *argv[], bootm_headers_t *images)
{
bd_t *bd = gd->bd;
char *s;
int machid = bd->bi_arch_number;
void (*theKernel)(int zero, int arch, uint params);
#ifdef CONFIG_CMDLINE_TAG
char *commandline = getenv ("bootargs");
#endif
if ((flag != 0) && (flag != BOOTM_STATE_OS_GO))
return 1;
/*得到函数的地址*/
theKernel = (void (*)(int, int, uint))images->ep;
/*获取到机器码*/
s = getenv ("machid");
if (s) {
machid = simple_strtoul (s, NULL, 16);
printf ("Using machid 0x%x from environment\n", machid);
}
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 (images->rd_start && images->rd_end)
setup_initrd_tag (bd, images->rd_start, images->rd_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
/*在启动kernel之前进行一些清理工作*/
cleanup_before_linux ();
/*从这个地方就正式跳入了kernel,并将机器码和参数传入kernel,机器码用于kernel的比对,参数用于指导kernel的启动。到此,uboot的所有功能就结束了*/
theKernel (0, machid, bd->bi_boot_params);
/* does not return */
return 1;
}
以上是关于Uboot流程分析的主要内容,如果未能解决你的问题,请参考以下文章