uboot流程分析--修改android启动模式按键

   本人用的android平台用的bootloader用的是uboot,貌似大多数手持设备平台都不用这个,因为功能过于强大用不上,反而显得太复杂了。不知道这个平台开发者是怎么想的。既然用了那就来分析一下,顺便修改一下其中的几个小问题,以符合我们的要求。

  uboot等同于其他所有的bootloader程序,从根本上讲是一个稍复杂的裸机程序,是最底层的东西,要分析裸机程序我们要从它的连接文件开始。连接文件(.lds文件)定义了程序编译之后整个连接过程,这样我们就可以找到这个程序的第一句汇编代码,进而来下一步分析。uboot的链接文件代码在android\bootable\bootloader\uboot-imx\u-boot.lds

  1. OUTPUT_FORMAT("elf32-littlearm""elf32-littlearm""elf32-littlearm")  //文件输出格式   
  2. OUTPUT_ARCH(arm)  
  3. ENTRY(_start)       //首地址标示符   
  4. SECTIONS  
  5. {  
  6.  . = 0x00000000;    //其实地址0   
  7.  . = ALIGN(4);      //4字节对齐   
  8.  .text :        //代码段   
  9.  {  
  10.    board/freescale/mx6q_sabresd/flash_header.o (.text.flasheader)   //第一个文件是board/freescale/mx6q_sabresd/flash_header.o   
  11.    cpu/arm_cortexa8/start.o         //第二个cpu/arm_cortexa8/start.o    
  12.    board/freescale/mx6q_sabresd/libmx6q_sabresd.a (.text)  
  13.    lib_arm/libarm.a (.text)  
  14.    net/libnet.a (.text)  
  15.    drivers/mtd/libmtd.a (.text)  
  16.    drivers/mmc/libmmc.a (.text)  
  17.    . = DEFINED(env_offset) ? env_offset : .;  
  18.    common/env_embedded.o(.text)  
  19.    *(.text)             //剩余的所有代码   
  20.  }  
  21.  . = ALIGN(4);  
  22.  .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) } //readonly data 段   
  23.  . = ALIGN(4);  
  24.  .data : { *(.data) }       //所有的readonly data   
  25.  . = ALIGN(4);  
  26.  .got : { *(.got) }  
  27.  . = .;  
  28.  __u_boot_cmd_start = .;        //u_boot_cmd段,里面是所有uboot命令的一个列表   
  29.  .u_boot_cmd : { *(.u_boot_cmd) }  
  30.  __u_boot_cmd_end = .;  
  31.  . = ALIGN(4);  
  32.  _end_of_copy = .;  
  33.  __bss_start = .;           //bss段 就是内存数据段   
  34.  .bss : { *(.bss) }  
  35.  _end = .;  
  36. }  
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这个文件。
  这个文件中除了分配内存和宏定义的伪汇编指令以外,真正执行的命令有一条
  1. .section ".text.flasheader""x"  
  2.     b   _start  
  3.     .org    CONFIG_FLASH_HEADER_OFFSET  
.section ".text.flasheader", "x"
	b	_start
	.org	CONFIG_FLASH_HEADER_OFFSET
也就是说,这个文件一执行就直接跳到_start 位置处。_start 在android\bootable\bootloader\uboot-imx\cpu\arm_cortexa8\ start.S中,因此我们来看这个文件代码

  1. .globl _start  
  2. _start: b   reset         
.globl _start
_start: b	reset       
这里直接跳转的reset中接下来看

  1. reset:  
  2.     /* 
  3.      * set the cpu to SVC32 mode    cpu设置成32位管理模式 
  4.      */  
  5.     mrs r0, cpsr  
  6.     bic r0, r0, #0x1f  
  7.     orr r0, r0, #0xd3  
  8.     msr cpsr,r0  
  9.  
  10. #if (CONFIG_OMAP34XX)   //因为我们的cpu不是ompa的 所以这段不会编译   
  11. .............................  
  12. #endif   
  13.     /* the mask ROM code should have PLL and others stable */ 
  14. #ifndef CONFIG_SKIP_LOWLEVEL_INIT   
  15.     bl  cpu_init_crit         
  16. #endif  
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

  1. /************************************************************************* 
  2.  * 
  3.  * CPU_init_critical registers 
  4.  * 
  5.  * setup important registers 
  6.  * setup memory timing 
  7.  * 
  8.  *************************************************************************/  
  9. cpu_init_crit:  
  10.     /* 
  11.      * Invalidate L1 I/D 
  12.      */  
  13.     mov r0, #0          @ set up for MCR  
  14.     mcr p15, 0, r0, c8, c7, 0   @ invalidate TLBs  
  15.     mcr p15, 0, r0, c7, c5, 0   @ invalidate icache  
  16.   
  17.     /* 
  18.      * disable MMU stuff and caches     //关闭mmu 
  19.      */  
  20.     mrc p15, 0, r0, c1, c0, 0  
  21.     bic r0, r0, #0x00002000 @ clear bits 13 (--V-)  
  22.     bic r0, r0, #0x00000007 @ clear bits 2:0 (-CAM)  
  23.     orr r0, r0, #0x00000002 @ set bit 1 (--A-) Align  
  24.     orr r0, r0, #0x00000800 @ set bit 12 (Z---) BTB  
  25.     mcr p15, 0, r0, c1, c0, 0  
  26.   
  27.     /* 
  28.      * Jump to board specific initialization... 
  29.      * The Mask ROM will have already initialized 
  30.      * basic memory. Go here to bump up clock rate and handle 
  31.      * wake up conditions. 
  32.      */  
  33.     mov ip, lr      @ persevere link reg across call  
  34.     bl  lowlevel_init   @ go setup pll,mux,memory//执行lowlevel_init这个函数代码在   
  35.                             @\bootloader\uboot-imx\board\freescale\mx6q_sabresd\lowlevel_init.S中  
  36.                             @主要对时钟,外部ram,rom等进行了初始化代码不贴了。  
  37.     mov lr, ip      @ restore link  
  38.     mov pc, lr      @ back to my caller  
/*************************************************************************
 *
 * 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

初始化完成后,接下来执行

  1. #ifndef CONFIG_SKIP_RELOCATE_UBOOT   
  2. relocate:               @ relocate U-Boot to RAM    将uboot重新定位到内存中  
  3.     adr r0, _start      @ r0 <- current position of code  
  4.     ldr r1, _TEXT_BASE      @ test if we run from flash or RAM   
  5.     cmp r0, r1          @ don't reloc during debug测试当前代码是否已经在内存中  
  6.     beq stack_setup     @如果在的话就直接跳转到stack_setup  
  7.   
  8.   
  9.     ldr r2, _armboot_start  @如果不在的话,加载_armboot_start地址到r2中。_armboot_start是uboot执行的主体c函数。  
  10.     ldr r3, _bss_start  
  11.     sub r2, r3, r2      @ r2 <- size of armboot计算bss_start-armboot_start 保存到R2中,也就是uboot的总大小  
  12.     add r2, r0, r2      @ r2 <- source end address 计算出uboot代码和rodata地址  
  13.   
  14. copy_loop:              @ copy 32 bytes at a time   //开始拷贝   
  15.     ldmia   r0!, {r3 - r10}     @ copy from source address [r0]  
  16.     stmia   r1!, {r3 - r10}     @ copy to   target address [r1]  
  17.     cmp r0, r2          @ until source end addreee [r2]  
  18.     ble copy_loop  
  19. #endif  /* CONFIG_SKIP_RELOCATE_UBOOT */   
  20.   
  21.     /* Set up the stack */  
  22. stack_setup:  
  23.     ldr r0, _TEXT_BASE      @ upper 128 KiB: relocated uboot  
  24.     sub r0, r0, #CONFIG_SYS_MALLOC_LEN @ malloc area//为c语言malloc函数分配内存   
  25.     sub r0, r0, #CONFIG_SYS_GBL_DATA_SIZE @ bdinfo  
  26. #ifdef CONFIG_USE_IRQ   
  27.     sub r0, r0, #(CONFIG_STACKSIZE_IRQ + CONFIG_STACKSIZE_FIQ)  
  28. #endif   
  29.     sub sp, r0, #12     @ leave 3 words for abort-stack//分配c语言堆栈   
  30.     and sp, sp, #~7     @ 8 byte alinged for (ldr/str)d  
  31.   
  32.     /* Clear BSS (if any). Is below tx (watch load addr - need space) */  
  33. clear_bss:  
  34.     ldr r0, _bss_start      @ find start of bss segment //清除bss段   
  35.     ldr r1, _bss_end        @ stop here  
  36.     mov r2, #0x00000000     @ clear value  
  37. clbss_l:  
  38.     str r2, [r0]        @ clear BSS location  
  39.     cmp r0, r1          @ are we at the end yet  
  40.     add r0, r0, #4      @ increment clear index pointer  
  41.     bne clbss_l         @ keep clearing till at end  
  42.  
  43. #ifdef CONFIG_ARCH_MMU   
  44.     bl board_mmu_init   //初始化mmu  
  45. #endif   
  46.     ldr pc, _start_armboot  @ jump to C code以上所有的初始化就已经完成了,接下类正式执行c语言代码了。这才是我们的重点  
  47.   
  48. _start_armboot: .word start_armboot  
#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中

  1. void start_armboot (void)  
  2. {  
  3.     init_fnc_t **init_fnc_ptr;  
  4.     char *s;  
  5. #if defined(CONFIG_VFD) || defined(CONFIG_LCD)   
  6.     unsigned long addr;  
  7. #endif   
  8.   
  9.     /* Pointer is writable since we allocated a register for it */  
  10.     gd = (gd_t*)(_armboot_start - CONFIG_SYS_MALLOC_LEN - sizeof(gd_t));  
  11.     //分配一段内存.在cpu存储控制器初始化之前,是不能访问外部ram的,因此需要一小段   
  12.     //内存来运行最初的初始化函数,这段内存一般是cpu内部ram   
  13.     /* compiler optimization barrier needed for GCC >= 3.4 */  
  14.     __asm__ __volatile__("": : :"memory");  
  15.   
  16.     memset ((void*)gd, 0, sizeof (gd_t));  
  17.     gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));  
  18.     memset (gd->bd, 0, sizeof (bd_t));  
  19.   
  20.     gd->flags |= GD_FLG_RELOC;  
  21.   
  22.     monitor_flash_len = _bss_start - _armboot_start;  
  23.   
  24.     for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {   
  25.         if ((*init_fnc_ptr)() != 0) {  
  26.             hang ();  
  27.         }  
  28.     }  
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的定义

  1. init_fnc_t *init_sequence[] = {  
  2. #if defined(CONFIG_ARCH_CPU_INIT)   
  3.     arch_cpu_init,      /* basic arch cpu dependent setup */  
  4. #endif   
  5.     board_init,     /* basic board dependent setup */  
  6. #if defined(CONFIG_USE_IRQ)   
  7.     interrupt_init,     /* set up exceptions */  
  8. #endif   
  9.     timer_init,     /* initialize timer */  
  10.     env_init,       /* initialize environment */  
  11.     init_baudrate,      /* initialze baudrate settings */  
  12.     serial_init,        /* serial communications setup */  
  13.     console_init_f,     /* stage 1 init of console */  
  14.     display_banner,     /* say that we are here */  
  15. #if defined(CONFIG_DISPLAY_CPUINFO)   
  16.     print_cpuinfo,      /* display cpu info (and speed) */  
  17. #endif   
  18. #if defined(CONFIG_DISPLAY_BOARDINFO)   
  19.     checkboard,     /* display board info */  
  20. #endif   
  21. #if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)   
  22.     init_func_i2c,  
  23. #endif   
  24.     dram_init,      /* configure available RAM banks */  
  25. #if defined(CONFIG_CMD_PCI) || defined (CONFIG_PCI)   
  26.     arm_pci_init,  
  27. #endif   
  28.     display_dram_config,  
  29.     NULL,  
  30. };  
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的所有初始化都完成了

我们继续看板子的其他配置

  1. #ifdef CONFIG_LCD   //lcd缓存设置   
  2.     /* board init may have inited fb_base */  
  3.     if (!gd->fb_base) {  
  4. #       ifndef PAGE_SIZE   
  5. #         define PAGE_SIZE 4096   
  6. #       endif   
  7.         /* 
  8.          * reserve memory for LCD display (always full pages) 
  9.          */  
  10.         /* bss_end is defined in the board-specific linker script */  
  11.         addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);  
  12.         lcd_setmem (addr);  
  13.         gd->fb_base = addr;  
  14.     }  
  15. #endif /* CONFIG_LCD */   
  16.   
  17.     env_relocate ();//设置环境变量 也就是printenv 打印出来的那些   
  18.   
  19. #ifdef CONFIG_VFD   
  20.     /* must do this after the framebuffer is allocated */  
  21.     drv_vfd_init(); //空函数   
  22. #endif /* CONFIG_VFD */   
  23.   
  24. #ifdef CONFIG_SERIAL_MULTI   
  25.     serial_initialize();//串口初始化   
  26. #endif   
  27.   
  28.     /* IP Address */  
  29.     gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");  
  30.   
  31. #if defined CONFIG_SPLASH_SCREEN && defined CONFIG_VIDEO_MX5   
  32.     setup_splash_image();//lcd显示log   
  33. #endif   
  34.     //重新定义stdio的位置,在本环境中被定义到了串口上   
  35.     stdio_init ();  /* get the devices list going. */  
  36.   
  37.     jumptable_init ();//把一些初始化函数的指针放到gd中,为以后调用   
  38.   
  39. #if defined(CONFIG_API)   
  40.     /* Initialize API */  
  41.     api_init ();  
  42. #endif   
  43.   
  44.     console_init_r ();  /* fully init console as a device 控制台初始化*/  
  45.   
  46. #if defined(CONFIG_ARCH_MISC_INIT)   
  47.     /* miscellaneous arch dependent initialisations */  
  48.     arch_misc_init ();//空函数   
  49. #endif   
  50. #if defined(CONFIG_MISC_INIT_R)   
  51.     /* miscellaneous platform dependent initialisations */  
  52.     misc_init_r ();//空函数   
  53. #endif   
  54.   
  55.     /* enable exceptions */  
  56.     enable_interrupts ();//使能中断   
  57.   
  58.     /* Perform network card initialisation if necessary */  
  59. #ifdef CONFIG_DRIVER_TI_EMAC       
  60.     /* XXX: this needs to be moved to board init */  
  61. extern void davinci_eth_set_mac_addr (const u_int8_t *addr);//不编译   
  62.     if (getenv ("ethaddr")) {  
  63.         uchar enetaddr[6];       
  64.         eth_getenv_enetaddr("ethaddr", enetaddr);  
  65.         davinci_eth_set_mac_addr(enetaddr);  
  66.     }  
  67. #endif   
  68.   
  69. #ifdef CONFIG_DRIVER_CS8900   
  70.     /* XXX: this needs to be moved to board init */  
  71.     cs8900_get_enetaddr ();//不编译   
  72. #endif   
  73.   
  74. #if defined(CONFIG_DRIVER_SMC91111) || defined (CONFIG_DRIVER_LAN91C96)   
  75.     /* XXX: this needs to be moved to board init */  
  76.     if (getenv ("ethaddr")) {  
  77.         uchar enetaddr[6];  
  78.         eth_getenv_enetaddr("ethaddr", enetaddr);//不编译   
  79.         smc_set_mac_addr(enetaddr);  
  80.     }  
  81. #endif /* CONFIG_DRIVER_SMC91111 || CONFIG_DRIVER_LAN91C96 */   
  82.   
  83. #if defined(CONFIG_ENC28J60_ETH) && !defined(CONFIG_ETHADDR)   
  84.     extern void enc_set_mac_addr (void);//不编译   
  85.     enc_set_mac_addr ();  
  86. #endif /* CONFIG_ENC28J60_ETH && !CONFIG_ETHADDR*/   
  87.   
  88.     /* Initialize from environment */  
  89.     if ((s = getenv ("loadaddr")) != NULL) {  
  90.         load_addr = simple_strtoul (s, NULL, 16);  
  91.     }  
  92. #if defined(CONFIG_CMD_NET)   
  93.     if ((s = getenv ("bootfile")) != NULL) {  
  94.         copy_filename (BootFile, s, sizeof (BootFile));  
  95.     }  
  96. #endif   
  97.   
  98. #ifdef BOARD_LATE_INIT   
  99.     board_late_init (); //初始化i2c,pmic等   
  100. #endif  
#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
接下来涉及到了我们最关心的地方,启动模式和按键响应

  1. #ifdef CONFIG_ANDROID_RECOVERY   
  2.     check_recovery_mode();  //检测是否进入recovery   
  3. #endif   
  4.   
  5. #if defined(CONFIG_CMD_NET)   
  6. #if defined(CONFIG_NET_MULTI)   
  7.     puts ("Net:   ");  
  8. #endif   
  9.     eth_initialize(gd->bd);  //根据gd的配置初始化以太网   
  10. #if defined(CONFIG_RESET_PHY_R)   
  11.     debug ("Reset Ethernet PHY\n");  
  12.     reset_phy();  
  13. #endif   
  14. #endif   
  15. #ifdef CONFIG_FASTBOOT   
  16.     check_fastboot_mode();  //检测是否进入fastboot   
  17. #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

  1. void check_recovery_mode(void)  
  2. {  
  3.     if (check_key_pressing())  
  4.         setup_recovery_env();  
  5.     else if (check_recovery_cmd_file()) {  
  6.         puts("Recovery command file founded!\n");  
  7.         setup_recovery_env();  
  8.     }  
  9. }  
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()

  1. int check_recovery_cmd_file(void)  
  2. {  
  3.     int button_pressed = 0;  
  4.     int recovery_mode = 0;  
  5.   
  6.     recovery_mode = check_and_clean_recovery_flag();//读取kernel的recovery标志位,如果有的话就要进入recovery   
  7.   
  8.     /* Check Recovery Combo Button press or not. */  
  9.     mxc_iomux_v3_setup_pad(MX6X_IOMUX(PAD_GPIO_5__GPIO_1_5));   //初始化vol down的gpio   
  10.   
  11.     gpio_direction_input(GPIO_VOL_DN_KEY);  
  12.   
  13.     if (gpio_get_value(GPIO_VOL_DN_KEY) == 0) { /* VOL_DN key is low assert *///如果vol down已经按下  
  14.         button_pressed = 1;  
  15.         printf("Recovery key pressed\n");  
  16.     }  
  17.   
  18.     return recovery_mode || button_pressed; //返回进入recovery   
  19. }  
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怎么进入的

  1. /* export to lib_arm/board.c */  
  2. void check_fastboot_mode(void)  
  3. {  
  4.     if (fastboot_check_and_clean_flag())  
  5.         do_fastboot(NULL, 0, 0, 0);  
  6. }  
/* 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
  1. /* check if the recovery bit is set by kernel, it can be set by kernel 
  2.  * issue a command '# reboot fastboot' */  
  3. int fastboot_check_and_clean_flag(void)  
  4. {  
  5.     int flag_set = 0;  
  6.     u32 reg;  
  7.     reg = readl(SRC_BASE_ADDR + SRC_GPR10);  
  8.   
  9.     flag_set = !!(reg & ANDROID_FASTBOOT_BOOT);  
  10.   
  11.     /* clean it in case looping infinite here.... */  
  12.     if (flag_set) {  
  13.         reg &= ~ANDROID_FASTBOOT_BOOT;  
  14.         writel(reg, SRC_BASE_ADDR + SRC_GPR10);  
  15.     }  
  16.   
  17.     return flag_set;  
  18. }  
/* 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()

  1. int check_key(void)  
  2. {  
  3.     #define PRESSED_VOLUP 1   
  4.     #define PRESSED_VOLDOWN 2   
  5.     #define KEY_MASK  (PRESSED_VOLUP|PRESSED_VOLDOWN)   
  6.     #define RECOVERY_KEY_MASK (PRESSED_VOLUP)   
  7.     #define FASTBOOT_KEY_MASK (PRESSED_VOLDOWN)   
  8.   
  9.     int state = 0;  
  10.     mxc_iomux_v3_setup_pad(MX6X_IOMUX(PAD_GPIO_5__GPIO_1_5));//vol down   
  11.     mxc_iomux_v3_setup_pad(MX6X_IOMUX(PAD_GPIO_5__GPIO_1_4));//vol up   
  12.     gpio_direction_input(GPIO_VOL_DN_KEY);  
  13.     gpio_direction_input(GPIO_VOL_UP_KEY);  
  14.       
  15.     if (gpio_get_value(GPIO_VOL_UP_KEY) == 0)  
  16.         state |= PRESSED_VOLUP;  
  17.     if (gpio_get_value(GPIO_VOL_DN_KEY) == 0)  
  18.         state |= PRESSED_VOLDOWN;  
  19.   
  20.     //如果摁下power+voldown就进入fastboot 这个的优先级要比recovery高。   
  21.     //就算同时按下power+volup+voldown三个键也要进入fastboot模式   
  22.     if ((state & KEY_MASK) == FASTBOOT_KEY_MASK)      
  23.         return 1;  
  24.     if(((state & KEY_MASK) == FASTBOOT_KEY_MASK))  
  25.         return 2;  
  26.   
  27.     return 0;  
  28. }  
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;
}

主函数判定的代码段修改为

  1. if (check_key()==1)  
  2.     do_fastboot(NULL, 0, 0, 0);  
  3. if (check_key()==2)      
  4.     setup_recovery_env();  
  5.   
  6. if (check_and_clean_recovery_flag()) {  
  7.     setup_recovery_env();  
  8. }  
  9. if (fastboot_check_and_clean_flag())  
  10.     do_fastboot(NULL, 0, 0, 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

  1. void main_loop (void)  
  2. {  
  3. #ifndef CONFIG_SYS_HUSH_PARSER   
  4.     static char lastcommand[CONFIG_SYS_CBSIZE] = { 0, };  
  5.     int len;  
  6.     int rc = 1;  
  7.     int flag;  
  8. #endif   
  9.   
  10. #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)   
  11.     char *s;  
  12.     int bootdelay;  
  13. #endif   
  14. #ifdef CONFIG_PREBOOT   
  15.     char *p;  
  16. #endif   
  17. #ifdef CONFIG_BOOTCOUNT_LIMIT   
  18.     unsigned long bootcount = 0;  
  19.     unsigned long bootlimit = 0;  
  20.     char *bcs;  
  21.     char bcs_set[16];  
  22. #endif /* CONFIG_BOOTCOUNT_LIMIT */   
  23.   
  24. #if defined(CONFIG_VFD) && defined(VFD_TEST_LOGO)   
  25.     ulong bmp = 0;      /* default bitmap */  
  26.     extern int trab_vfd (ulong bitmap);  
  27.   
  28. #ifdef CONFIG_MODEM_SUPPORT   
  29.     if (do_mdm_init)  
  30.         bmp = 1;    /* alternate bitmap */  
  31. #endif   
  32.     trab_vfd (bmp);  
  33. #endif  /* CONFIG_VFD && VFD_TEST_LOGO */   
  34.   
  35. #if defined(CONFIG_UPDATE_TFTP)   
  36.     update_tftp ();  
  37. #endif /* CONFIG_UPDATE_TFTP */   
  38.   
  39. #ifdef CONFIG_BOOTCOUNT_LIMIT   
  40.     bootcount = bootcount_load();  
  41.     bootcount++;  
  42.     bootcount_store (bootcount);  
  43.     sprintf (bcs_set, "%lu", bootcount);  
  44.     setenv ("bootcount", bcs_set);  
  45.     bcs = getenv ("bootlimit");  
  46.     bootlimit = bcs ? simple_strtoul (bcs, NULL, 10) : 0;  
  47. #endif /* CONFIG_BOOTCOUNT_LIMIT */   
  48.   
  49. #ifdef CONFIG_MODEM_SUPPORT   
  50.     debug ("DEBUG: main_loop:   do_mdm_init=%d\n", do_mdm_init);  
  51.     if (do_mdm_init) {  
  52.         char *str = strdup(getenv("mdm_cmd"));  
  53.         setenv ("preboot", str);  /* set or delete definition */  
  54.         if (str != NULL)  
  55.             free (str);  
  56.         mdm_init(); /* wait for modem connection */  
  57.     }  
  58. #endif  /* CONFIG_MODEM_SUPPORT */   
  59.   
  60. #ifdef CONFIG_VERSION_VARIABLE   
  61.     {  
  62.         extern char version_string[];  
  63.   
  64.         setenv ("ver", version_string);  /* set version variable */  
  65.     }  
  66. #endif /* CONFIG_VERSION_VARIABLE */   
  67.   
  68. #ifdef CONFIG_SYS_HUSH_PARSER   
  69.     u_boot_hush_start ();  
  70. #endif   
  71.   
  72. #if defined(CONFIG_HUSH_INIT_VAR)   
  73.     hush_init_var ();  
  74. #endif   
  75.   
  76. #ifdef CONFIG_AUTO_COMPLETE   
  77.     install_auto_complete();  
  78. #endif   
  79.   
  80. #ifdef CONFIG_PREBOOT   
  81.     if ((p = getenv ("preboot")) != NULL) {  
  82. # ifdef CONFIG_AUTOBOOT_KEYED   
  83.         int prev = disable_ctrlc(1);    /* disable Control C checking */  
  84. # endif   
  85.   
  86. # ifndef CONFIG_SYS_HUSH_PARSER   
  87.         run_command (p, 0);  
  88. # else   
  89.         parse_string_outer(p, FLAG_PARSE_SEMICOLON |  
  90.                     FLAG_EXIT_FROM_LOOP);  
  91. # endif   
  92.   
  93. # ifdef CONFIG_AUTOBOOT_KEYED   
  94.         disable_ctrlc(prev);    /* restore Control C checking */  
  95. # endif   
  96.     }  
  97. #endif /* CONFIG_PREBOOT */   
  98.   
  99. #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)   
  100.     s = getenv ("bootdelay");  
  101.     bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;//计算bootdelay   
  102.   
  103.     debug ("### main_loop entered: bootdelay=%d\n\n", bootdelay);  
  104.   
  105. # ifdef CONFIG_BOOT_RETRY_TIME   
  106.     init_cmd_timeout ();  
  107. # endif /* CONFIG_BOOT_RETRY_TIME */   
  108.   
  109. #ifdef CONFIG_POST   
  110.     if (gd->flags & GD_FLG_POSTFAIL) {  
  111.         s = getenv("failbootcmd");  
  112.     }  
  113.     else  
  114. #endif /* CONFIG_POST */   
  115. #ifdef CONFIG_BOOTCOUNT_LIMIT   
  116.     if (bootlimit && (bootcount > bootlimit)) {  
  117.         printf ("Warning: Bootlimit (%u) exceeded. Using altbootcmd.\n",  
  118.                 (unsigned)bootlimit);  
  119.         s = getenv ("altbootcmd");  
  120.     }  
  121.     else  
  122. #endif /* CONFIG_BOOTCOUNT_LIMIT */   
  123.         s = getenv ("bootcmd");//得到bootcmd命令   
  124.   
  125.     debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");  
  126. //每10ms从控制台读取一个字符,并且显示倒计时。如果读取成功的话就继续执行main_loop代码,   
  127. //如果失败的话就执行下面的run_command(s,0)   
  128.     if (bootdelay >= 0 && s && !abortboot (bootdelay)) {  
  129. # ifdef CONFIG_AUTOBOOT_KEYED   
  130.         int prev = disable_ctrlc(1);    /* disable Control C checking */  
  131. # endif   
  132.   
  133. # ifndef CONFIG_SYS_HUSH_PARSER   
  134.         run_command (s, 0);//执行 bootcmd命令   
  135. # else   
  136.         parse_string_outer(s, FLAG_PARSE_SEMICOLON |  
  137.                     FLAG_EXIT_FROM_LOOP);  
  138. # endif   
  139.   
  140. # ifdef CONFIG_AUTOBOOT_KEYED   
  141.         disable_ctrlc(prev);    /* restore Control C checking */  
  142. # endif   
  143.     }  
  144.   
  145. # ifdef CONFIG_MENUKEY   
  146.     if (menukey == CONFIG_MENUKEY) {  
  147.         s = getenv("menucmd");  
  148.         if (s) {  
  149. # ifndef CONFIG_SYS_HUSH_PARSER   
  150.         run_command (s, 0);  
  151. # else   
  152.         parse_string_outer(s, FLAG_PARSE_SEMICOLON |  
  153.                     FLAG_EXIT_FROM_LOOP);  
  154. # endif   
  155.         }  
  156.     }  
  157. #endif /* CONFIG_MENUKEY */   
  158. #endif  /* CONFIG_BOOTDELAY */   
  159.   
  160. #ifdef CONFIG_AMIGAONEG3SE   
  161.     {  
  162.         extern void video_banner(void);  
  163.         video_banner();  
  164.     }  
  165. #endif   
  166.   
  167.     /* 
  168.      * Main Loop for Monitor Command Processing 
  169.      */  
  170. #ifdef CONFIG_SYS_HUSH_PARSER   
  171.     parse_file_outer();  
  172.     /* This point is never reached */  
  173.     for (;;);  
  174. #else   
  175.     for (;;) {  //如果bootdelay时候有按键 就进入命令处理模式   
  176. #ifdef CONFIG_BOOT_RETRY_TIME   
  177.         if (rc >= 0) {  
  178.             /* Saw enough of a valid command to 
  179.              * restart the timeout. 
  180.              */  
  181.             reset_cmd_timeout();  
  182.         }  
  183. #endif   
  184.         len = readline (CONFIG_SYS_PROMPT);//从控制台读取一行数据,以回车为标志   
  185.   
  186.         flag = 0;   /* assume no special flags for now */  
  187.         if (len > 0)  
  188.             z (lastcommand, console_buffer);  
  189.         else if (len == 0)  
  190.             flag |= CMD_FLAG_REPEAT;  
  191. #ifdef CONFIG_BOOT_RETRY_TIME   
  192.         else if (len == -2) {  
  193.             /* -2 means timed out, retry autoboot 
  194.              */  
  195.             puts ("\nTimed out waiting for command\n");  
  196. # ifdef CONFIG_RESET_TO_RETRY   
  197.             /* Reinit board to run initialization code again */  
  198.             do_reset (NULL, 0, 0, NULL);  
  199. # else   
  200.             return;     /* retry autoboot */  
  201. # endif   
  202.         }  
  203. #endif   
  204.   
  205.         if (len == -1)  
  206.             puts ("<INTERRUPT>\n");  
  207.         else  
  208.             rc = run_command (lastcommand, flag);//处理这条命令   
  209.   
  210.         if (rc <= 0) {  
  211.             /* invalid command or not repeatable, forget it */  
  212.             lastcommand[0] = 0;  
  213.         }  
  214.     }  
  215. #endif /*CONFIG_SYS_HUSH_PARSER*/   
  216. }     
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是怎么启动的。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值