本人用的android平台用的bootloader用的是uboot,貌似大多数手持设备平台都不用这个,因为功能过于强大用不上,反而显得太复杂了。不知道这个平台开发者是怎么想的。既然用了那就来分析一下,顺便修改一下其中的几个小问题,以符合我们的要求。
uboot等同于其他所有的bootloader程序,从根本上讲是一个稍复杂的裸机程序,是最底层的东西,要分析裸机程序我们要从它的连接文件开始。连接文件(.lds文件)定义了程序编译之后整个连接过程,这样我们就可以找到这个程序的第一句汇编代码,进而来下一步分析。uboot的链接文件代码在android\bootable\bootloader\uboot-imx\u-boot.lds
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") //文件输出格式 OUTPUT_ARCH(arm) ENTRY(_start) //首地址标示符 SECTIONS { . = 0x00000000; //其实地址0 . = ALIGN(4); //4字节对齐 .text : //代码段 { board/freescale/mx6q_sabresd/flash_header.o (.text.flasheader) //第一个文件是board/freescale/mx6q_sabresd/flash_header.o cpu/arm_cortexa8/start.o //第二个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*))) } //readonly data 段 . = ALIGN(4); .data : { *(.data) } //所有的readonly data . = ALIGN(4); .got : { *(.got) } . = .; __u_boot_cmd_start = .; //u_boot_cmd段,里面是所有uboot命令的一个列表 .u_boot_cmd : { *(.u_boot_cmd) } __u_boot_cmd_end = .; . = ALIGN(4); _end_of_copy = .; __bss_start = .; //bss段 就是内存数据段 .bss : { *(.bss) } _end = .; }
从上面的代码可以看出我们编译生成的二进制应用程序组成是:代码段->rodata段->uboot命令列表->bss段。我们启动这个应用程序时候是从,0地址开始的,因此我们来看
board/freescale/mx6q_sabresd/flash_header.s这个文件。
这个文件中除了分配内存和宏定义的伪汇编指令以外,真正执行的命令有一条
.section ".text.flasheader", "x" b _start .org CONFIG_FLASH_HEADER_OFFSET
也就是说,这个文件一执行就直接跳到_start 位置处。_start 在android\bootable\bootloader\uboot-imx\cpu\arm_cortexa8\ start.S中,因此我们来看这个文件代码
.globl _start _start: b reset
这里直接跳转的reset中接下来看
reset: /* * set the cpu to SVC32 mode cpu设置成32位管理模式 */ mrs r0, cpsr bic r0, r0, #0x1f orr r0, r0, #0xd3 msr cpsr,r0 #if (CONFIG_OMAP34XX) //因为我们的cpu不是ompa的 所以这段不会编译 ............................. #endif /* the mask ROM code should have PLL and others stable */ #ifndef CONFIG_SKIP_LOWLEVEL_INIT bl cpu_init_crit #endif
这里接下来执行cpu_init_crit
/************************************************************************* * * CPU_init_critical registers * * setup important registers * setup memory timing * *************************************************************************/ 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 /* * disable MMU stuff and caches //关闭mmu */ 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 /* * Jump to board specific initialization... * The Mask ROM will have already initialized * basic memory. Go here to bump up clock rate and handle * wake up conditions. */ mov ip, lr @ persevere link reg across call bl lowlevel_init @ go setup pll,mux,memory//执行lowlevel_init这个函数代码在 @\bootloader\uboot-imx\board\freescale\mx6q_sabresd\lowlevel_init.S中 @主要对时钟,外部ram,rom等进行了初始化代码不贴了。 mov lr, ip @ restore link mov pc, lr @ back to my caller
初始化完成后,接下来执行
#ifndef CONFIG_SKIP_RELOCATE_UBOOT relocate: @ relocate U-Boot to RAM 将uboot重新定位到内存中 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 @如果在的话就直接跳转到stack_setup ldr r2, _armboot_start @如果不在的话,加载_armboot_start地址到r2中。_armboot_start是uboot执行的主体c函数。 ldr r3, _bss_start sub r2, r3, r2 @ r2 <- size of armboot计算bss_start-armboot_start 保存到R2中,也就是uboot的总大小 add r2, r0, r2 @ r2 <- source end address 计算出uboot代码和rodata地址 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 */ /* Set up the stack */ stack_setup: ldr r0, _TEXT_BASE @ upper 128 KiB: relocated uboot sub r0, r0, #CONFIG_SYS_MALLOC_LEN @ malloc area//为c语言malloc函数分配内存 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//分配c语言堆栈 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 //清除bss段 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 //初始化mmu #endif ldr pc, _start_armboot @ jump to C code以上所有的初始化就已经完成了,接下类正式执行c语言代码了。这才是我们的重点 _start_armboot: .word start_armboot
接下来正式看C代码,也就是start_armboot这个函数代码在android\bootable\bootloader\uboot-imx\lib_arm\board.c中
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)); //分配一段内存.在cpu存储控制器初始化之前,是不能访问外部ram的,因此需要一小段 //内存来运行最初的初始化函数,这段内存一般是cpu内部ram /* 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; for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) { if ((*init_fnc_ptr)() != 0) { hang (); } }
注意看这里init_sequence的定义
init_fnc_t *init_sequence[] = { #if defined(CONFIG_ARCH_CPU_INIT) arch_cpu_init, /* basic arch cpu dependent setup */ #endif board_init, /* basic board dependent setup */ #if defined(CONFIG_USE_IRQ) interrupt_init, /* set up exceptions */ #endif timer_init, /* initialize timer */ env_init, /* initialize environment */ init_baudrate, /* initialze baudrate settings */ serial_init, /* serial communications setup */ console_init_f, /* stage 1 init of console */ display_banner, /* say that we are here */ #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, /* configure available RAM banks */ #if defined(CONFIG_CMD_PCI) || defined (CONFIG_PCI) arm_pci_init, #endif display_dram_config, NULL, };
这个是一个函数指针的数组,涉及到cpu的最后的一些初始化。到了这里cpu的所有初始化都完成了
我们继续看板子的其他配置
#ifdef CONFIG_LCD //lcd缓存设置 /* board init may have inited fb_base */ 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 */ env_relocate ();//设置环境变量 也就是printenv 打印出来的那些 #ifdef CONFIG_VFD /* must do this after the framebuffer is allocated */ drv_vfd_init(); //空函数 #endif /* CONFIG_VFD */ #ifdef CONFIG_SERIAL_MULTI serial_initialize();//串口初始化 #endif /* IP Address */ gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr"); #if defined CONFIG_SPLASH_SCREEN && defined CONFIG_VIDEO_MX5 setup_splash_image();//lcd显示log #endif //重新定义stdio的位置,在本环境中被定义到了串口上 stdio_init (); /* get the devices list going. */ jumptable_init ();//把一些初始化函数的指针放到gd中,为以后调用 #if defined(CONFIG_API) /* Initialize API */ api_init (); #endif console_init_r (); /* fully init console as a device 控制台初始化*/ #if defined(CONFIG_ARCH_MISC_INIT) /* miscellaneous arch dependent initialisations */ arch_misc_init ();//空函数 #endif #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_TI_EMAC /* XXX: this needs to be moved to board init */ extern void davinci_eth_set_mac_addr (const u_int8_t *addr);//不编译 if (getenv ("ethaddr")) { uchar enetaddr[6]; eth_getenv_enetaddr("ethaddr", enetaddr); davinci_eth_set_mac_addr(enetaddr); } #endif #ifdef CONFIG_DRIVER_CS8900 /* XXX: this needs to be moved to board init */ cs8900_get_enetaddr ();//不编译 #endif #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 (); //初始化i2c,pmic等 #endif
接下来涉及到了我们最关心的地方,启动模式和按键响应
#ifdef CONFIG_ANDROID_RECOVERY check_recovery_mode(); //检测是否进入recovery #endif #if defined(CONFIG_CMD_NET) #if defined(CONFIG_NET_MULTI) puts ("Net: "); #endif eth_initialize(gd->bd); //根据gd的配置初始化以太网 #if defined(CONFIG_RESET_PHY_R) debug ("Reset Ethernet PHY\n"); reset_phy(); #endif #endif #ifdef CONFIG_FASTBOOT check_fastboot_mode(); //检测是否进入fastboot #endif
从代码里可以看出我们是首先检测recovery,然后才检测fastboot模式。
我们先来看原版是怎么做的,首先是recovery
void check_recovery_mode(void) { if (check_key_pressing()) setup_recovery_env(); else if (check_recovery_cmd_file()) { puts("Recovery command file founded!\n"); setup_recovery_env(); } }
这里首先检测是否有合法的按键按下,如果有的话就配置环境变量进入recovery
没有按键就检测uboot命令文件,看是不是主系统要求进入recovery
因此我们这里的重点是check_key_pressing()这个函数,仔细研究发现这个函数用的是uboot
标准的gpio驱动,官方给我们移植的uboot里面并没有初始化这个驱动,而是自己另外写的。也就是说
我们调用check_key_pressing()这个函数永远都返回0值而执行else if (check_recovery_cmd_file())这一句
我们来看 check_recovery_cmd_file()
int check_recovery_cmd_file(void) { int button_pressed = 0; int recovery_mode = 0; recovery_mode = check_and_clean_recovery_flag();//读取kernel的recovery标志位,如果有的话就要进入recovery /* Check Recovery Combo Button press or not. */ mxc_iomux_v3_setup_pad(MX6X_IOMUX(PAD_GPIO_5__GPIO_1_5)); //初始化vol down的gpio gpio_direction_input(GPIO_VOL_DN_KEY); if (gpio_get_value(GPIO_VOL_DN_KEY) == 0) { /* VOL_DN key is low assert *///如果vol down已经按下 button_pressed = 1; printf("Recovery key pressed\n"); } return recovery_mode || button_pressed; //返回进入recovery }
也就是说官方修改的uboot走了偷懒的方法,直接在check_recovery_cmd_file()增加了一个按键的盘定。很不正规
因此我们下一步要修改它,从官方的基础上走,我们也不走标准uboot 按键驱动,而是自己写。
修改之前先来看原版fastboot怎么进入的
/* export to lib_arm/board.c */ void check_fastboot_mode(void) { if (fastboot_check_and_clean_flag()) do_fastboot(NULL, 0, 0, 0); }
这里调用fastboot_check_and_clean_flag()来判定是否进入fastboot
/* check if the recovery bit is set by kernel, it can be set by kernel * issue a command '# reboot fastboot' */ int fastboot_check_and_clean_flag(void) { int flag_set = 0; u32 reg; reg = readl(SRC_BASE_ADDR + SRC_GPR10); flag_set = !!(reg & ANDROID_FASTBOOT_BOOT); /* clean it in case looping infinite here.... */ if (flag_set) { reg &= ~ANDROID_FASTBOOT_BOOT; writel(reg, SRC_BASE_ADDR + SRC_GPR10); } return flag_set; }
从这里看出,要进入进入fastboot,只能检测(SRC_BASE_ADDR + SRC_GPR10)寄存器的
ANDROID_FASTBOOT_BOOT位是否被kernel置位,并没有按键,因此我们的板子不可能靠
按键进入fastboot的实际情况也确实这样。因此我们要修改这一块,由于我们的cpu在power键按住5s
以后会强制关机。因此开机后我们必须松开power键,我们板子检测的按键只能是1个。开机时vol up键进入
recovery,按住vol down进入fastboot模式。我们修改代码如下
新建个按键检测函数check_key()
int check_key(void) { #define PRESSED_VOLUP 1 #define PRESSED_VOLDOWN 2 #define KEY_MASK (PRESSED_VOLUP|PRESSED_VOLDOWN) #define RECOVERY_KEY_MASK (PRESSED_VOLUP) #define FASTBOOT_KEY_MASK (PRESSED_VOLDOWN) int state = 0; mxc_iomux_v3_setup_pad(MX6X_IOMUX(PAD_GPIO_5__GPIO_1_5));//vol down mxc_iomux_v3_setup_pad(MX6X_IOMUX(PAD_GPIO_5__GPIO_1_4));//vol up gpio_direction_input(GPIO_VOL_DN_KEY); gpio_direction_input(GPIO_VOL_UP_KEY); if (gpio_get_value(GPIO_VOL_UP_KEY) == 0) state |= PRESSED_VOLUP; if (gpio_get_value(GPIO_VOL_DN_KEY) == 0) state |= PRESSED_VOLDOWN; //如果摁下power+voldown就进入fastboot 这个的优先级要比recovery高。 //就算同时按下power+volup+voldown三个键也要进入fastboot模式 if ((state & KEY_MASK) == FASTBOOT_KEY_MASK) return 1; if(((state & KEY_MASK) == FASTBOOT_KEY_MASK)) return 2; return 0; }
主函数判定的代码段修改为
if (check_key()==1) do_fastboot(NULL, 0, 0, 0); if (check_key()==2) setup_recovery_env(); if (check_and_clean_recovery_flag()) { setup_recovery_env(); } if (fastboot_check_and_clean_flag()) do_fastboot(NULL, 0, 0, 0);
这样我们的启动模式按键就修改完成了,编译后测试成功。
下面我们还有代码没有分析完:uboot的主循环:main_loop()
代码在:\bootable\bootloader\uboot-imx\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_PREBOOT char *p; #endif #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 */ #if defined(CONFIG_UPDATE_TFTP) update_tftp (); #endif /* CONFIG_UPDATE_TFTP */ #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 */ #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 */ #ifdef CONFIG_VERSION_VARIABLE { extern char version_string[]; setenv ("ver", version_string); /* set version variable */ } #endif /* CONFIG_VERSION_VARIABLE */ #ifdef CONFIG_SYS_HUSH_PARSER u_boot_hush_start (); #endif #if defined(CONFIG_HUSH_INIT_VAR) hush_init_var (); #endif #ifdef CONFIG_AUTO_COMPLETE install_auto_complete(); #endif #ifdef CONFIG_PREBOOT if ((p = getenv ("preboot")) != NULL) { # ifdef CONFIG_AUTOBOOT_KEYED int prev = disable_ctrlc(1); /* disable Control C checking */ # endif # ifndef CONFIG_SYS_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;//计算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_POST if (gd->flags & GD_FLG_POSTFAIL) { s = getenv("failbootcmd"); } else #endif /* CONFIG_POST */ #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");//得到bootcmd命令 debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>"); //每10ms从控制台读取一个字符,并且显示倒计时。如果读取成功的话就继续执行main_loop代码, //如果失败的话就执行下面的run_command(s,0) if (bootdelay >= 0 && s && !abortboot (bootdelay)) { # ifdef CONFIG_AUTOBOOT_KEYED int prev = disable_ctrlc(1); /* disable Control C checking */ # endif # ifndef CONFIG_SYS_HUSH_PARSER run_command (s, 0);//执行 bootcmd命令 # 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 CONFIG_SYS_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 /* * Main Loop for Monitor Command Processing */ #ifdef CONFIG_SYS_HUSH_PARSER parse_file_outer(); /* This point is never reached */ for (;;); #else for (;;) { //如果bootdelay时候有按键 就进入命令处理模式 #ifdef CONFIG_BOOT_RETRY_TIME if (rc >= 0) { /* Saw enough of a valid command to * restart the timeout. */ reset_cmd_timeout(); } #endif len = readline (CONFIG_SYS_PROMPT);//从控制台读取一行数据,以回车为标志 flag = 0; /* assume no special flags for now */ if (len > 0) z (lastcommand, console_buffer); 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 /*CONFIG_SYS_HUSH_PARSER*/ }
到了这里整个的uboot流程已经走完了。从这里可以知道,uboot正式运行以后,实现的所有功能都是通过命令实现的,要继续分析的话,就要分析uboot的命令的实现了。
我们在下一篇文章里面讲述uboot命令是怎么实现的,kernel是怎么启动的。