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 ] ; 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 ] && exit [ $# -gt ] && exit #这里就是配置时窗口显示的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-$ asm LNPREFIX="../../include2/asm/" cd ../include rm -rf asm-$ rm -f asm mkdir asm-$ ln -s asm-$ asm else cd ./include rm -f asm ln -s asm-$ asm fi rm -f asm-$/arch if [ -z "$6" -o "$6" = "NULL" ] ; then ln -s ${LNPREFIX}arch-$ asm-$/arch else ln -s ${LNPREFIX}arch-$ asm-$/arch fi if [ "$2" = "arm" ] ; then rm -f asm-$/proc ln -s ${LNPREFIX}proc-armv asm-$/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
查看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(); .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(); .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) } . = ALIGN(); .data : { *(.data) } . = ALIGN(); .got : { *(.got) } . = .; __u_boot_cmd_start = .; .u_boot_cmd : { *(.u_boot_cmd) } __u_boot_cmd_end = .; . = ALIGN(); _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 ,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, # @ set up for MCR mcr p15, , r0, c8, c7, @ invalidate TLBs mcr p15, , r0, c7, c5, @ invalidate icache /*关闭mmu和catch*/ mrc p15, , r0, c1, c0, bic r0, r0, #0x00002000 @ clear bits (--V-) bic r0, r0, #0x00000007 @ clear bits : (-CAM) orr r0, r0, #0x00000002 @ set bit (--A-) Align orr r0, r0, #0x00000800 @ set bit (Z---) BTB mcr p15, , r0, c1, c0, 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, , sizeof (gd_t)); gd->bd = (bd_t*)((char*)gd - sizeof(bd_t)); memset (gd->bd, , 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)() != ) { 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[]; gd->env_valid = ; return ; }
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 # endif /* * reserve memory for LCD display (always full pages) */ /* bss_end is defined in the board-specific linker script */ addr = (_bss_end + (PAGE_SIZE - )) & ~(PAGE_SIZE - ); 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 = ; 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 (); }
继续接下来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[]; 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, ); } #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] = { , }; int len; int rc = ; 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, ) : 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 >= && s && !abortboot (bootdelay)) { 。。。省略。。。 # ifndef CONFIG_SYS_HUSH_PARSER run_command (s, ); # 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, , 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 = ; int ret; boot_os_fn *boot_fn; /* relocate boot function table */ if (!relocated) { int i; for (i = ; i < ARRAY_SIZE(boot_os); i++) if (boot_os[i] != NULL) boot_os[i] += gd->reloc_off; relocated = ; } 。。。省略。。。 /*取得镜像信息*/ if (bootm_start(cmdtp, flag, argc, argv)) return ; /*关中断*/ 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, ); if (ret < ) { 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 (-); do_reset (cmdtp, flag, argc, argv); } } if (ret == BOOTM_ERR_UNIMPLEMENTED) { if (iflag) enable_interrupts(); show_boot_progress (-); return ; } } lmb_reserve(&images.lmb, images.os.load, (load_end - images.os.load)); 。。。省略。。。 show_boot_progress (); #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(, argc, argv, &images); /*不会运行到这里*/ show_boot_progress (-); #ifdef DEBUG puts ("\n## Control returned to monitor - resetting...\n"); #endif do_reset (cmdtp, flag, argc, argv); return ; }
其中比较关键的函数为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, , 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 == ) { puts ("ERROR: can't get kernel image!\n"); return ; } /* 得到镜像的一些参数 */ 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 ; } /* 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 ; } #endif } else { puts ("Could not find kernel entry point!\n"); return ; } 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 ; } #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 ; } set_working_fdt_addr(images.ft_addr); #endif #endif } images.os.start = (ulong)os_hdr; images.state = BOOTM_STATE_START; return ; }
从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 != ) && (flag != BOOTM_STATE_OS_GO)) return ; /*得到函数的地址*/ theKernel = (void (*)(int, int, uint))images->ep; /*获取到机器码*/ s = getenv ("machid"); if (s) { machid = simple_strtoul (s, NULL, ); printf ("Using machid 0x%x from environment\n", machid); } show_boot_progress (); 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 (, machid, bd->bi_boot_params); /* does not return */ return ; }
Uboot流程分析的更多相关文章
- u-boot 流程分析
u-boot 介绍: 对于计算机来说 , 从一开始上机通电是无法直接启动操作系统的 , 这中间需要一个引导过程 , 嵌入式Linux系统同样离不开引导程序 , 这个启动程序就叫启动加载程序(Boot ...
- uboot流程分析--修改android启动模式按键【转】
本文转载自:http://blog.csdn.net/dkleikesa/article/details/9792747 本人用的android平台用的bootloader用的是uboot,貌似大多数 ...
- u-boot中nandflash初始化流程分析(转)
u-boot中nandflash初始化流程分析(转) 原文地址http://zhuairlunjj.blog.163.com/blog/static/80050945201092011249136/ ...
- u-boot启动流程分析(2)_板级(board)部分
转自:http://www.wowotech.net/u-boot/boot_flow_2.html 目录: 1. 前言 2. Generic Board 3. _main 4. global dat ...
- u-boot启动流程分析(1)_平台相关部分
转自:http://www.wowotech.net/u-boot/boot_flow_1.html 1. 前言 本文将结合u-boot的“board—>machine—>arch—> ...
- [国嵌笔记][030][U-Boot工作流程分析]
uboot工作流程分析 程序入口 1.打开顶层目录的Makefile,找到目标smdk2440_config的命令中的第三项(smdk2440) 2.进入目录board/samsung/smdk244 ...
- 嵌入式Linux开发之uboot启动Linux整体流程分析
嵌入式Linux开发之uboot启动Linux整体流程分析 Uboot全称Universal Boot Loader,一个遵循GPL协议的的开源项目,其作用是引导操作系统,支持引导linux.VxWo ...
- u-boot移植(三)---修改前工作:代码流程分析2
一.vectors.S 1.1 代码地址 vectors.S (arch\arm\lib) 1.2 流程跳转 跳转符号 B 为 start.S 中的 reset 执行代码,暂且先不看,先看看 vect ...
- u-boot移植(二)---修改前工作:代码流程分析1
一.代码执行总体流程图 1.1 代码路径 U-boot.lds (arch\arm\cpu) vectors.S (arch\arm\lib) start.S (arch\arm\cpu\arm920 ...
随机推荐
- 「HNOI 2016」 序列
\(Description\) 给你一个序列,每次询问一个区间,求其所有子区间的最小值之和 \(Solution\) 这里要用莫队算法 首先令\(val\)数组为原序列 我们考虑怎么由一个区间\([l ...
- Android Studio设置字体
一,点"Settings"按钮,调出配置界面: 然后如图找到 Editor-colors&font-font ,默认的不让修改 所以先点击save as 随便起个名字 , ...
- Maven的Mirror和Repository 的详细讲解
1 Repository(仓库) 1.1 Maven仓库主要有2种: remote repository:相当于公共的仓库,大家都能访问到,一般可以用URL的形式访问 local repository ...
- GCD 使用若干注意事项
这篇文章写的是看完 WWDC 17 - Modernizing GCD Usage 之后的笔记. 一.Parallelism & Concurrency Parallelism 指的是在多个 ...
- python 之 比较哪个数据大小
#定义一个字典info={}#定义比较的人数n=int(input("请输入你要比较的人数"))#循环while(n): #输入a,b 两个数据 ,分别代表学号 和分数 # 把输入 ...
- delphi 10.2 ----简单的叠乘例子
unit Unit11; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, Syste ...
- 第一个PSP0级
1.计划: 需求描述: 按照图片要求设计添加新课程界面.(0.5分) 在后台数据库中建立相应的表结构存储课程信息.(0.5分) 实现新课程添加的功能. 要求判断任课教师为王建民.刘立嘉.刘丹.王辉.杨 ...
- 架构师养成记--19.netty
一.Netty初步 为什么选择Netty? 和NIO比较,要实现一个通信要简单得很多,性能很好.分布式消息中间件.storm.Dubble都是使用Netty作为底层通信. Netty5.0要求jdk1 ...
- 架构师养成记--16.disruptor并发框架中RingBuffer的使用
很多时候我们只需要消息中间件这样的功能,那么直需要RinBuffer就可以了. 入口: import java.util.concurrent.Callable; import java.util.c ...
- 【Alpha】Phylab 展示博客
目录 Phylab Alpha 展示博客 一.团队简介 二.项目目标 2.1 典型用户 2.2 功能描述 2.3 用户量 三.项目发布与展示 3.1 新功能 3.2 修复缺陷 3.3 问题与限制 3. ...