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_,$@)" ] || \ {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; }