您的位置:首页 > 移动开发 > Android开发

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

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

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

[cpp]
view plaincopyprint?

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 = .;
}

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这个文件。

这个文件中除了分配内存和宏定义的伪汇编指令以外,真正执行的命令有一条

[cpp]
view plaincopyprint?

.section ".text.flasheader", "x"
b _start
.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中,因此我们来看这个文件代码

[cpp]
view plaincopyprint?

.globl _start
_start: b reset

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

[csharp]
view plaincopyprint?

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

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

[csharp]
view plaincopyprint?

/*************************************************************************
*
* 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

/*************************************************************************
*
* 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

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

[csharp]
view plaincopyprint?

#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

#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中

[cpp]
view plaincopyprint?

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 ();
}
}

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的定义

[cpp]
view plaincopyprint?

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,
};

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的所有初始化都完成了

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

[cpp]
view plaincopyprint?

#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_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
接下来涉及到了我们最关心的地方,启动模式和按键响应

[cpp]
view plaincopyprint?

#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

#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

[cpp]
view plaincopyprint?

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(); } }
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()

[cpp]
view plaincopyprint?

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

}

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怎么进入的

[cpp]
view plaincopyprint?

/* export to lib_arm/board.c */ void check_fastboot_mode(void) { if (fastboot_check_and_clean_flag()) do_fastboot(NULL, 0, 0, 0); }
/* 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

[cpp]
view plaincopyprint?

/* 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;
}

/* 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()

[cpp]
view plaincopyprint?

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;
}

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;
}


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

[cpp]
view plaincopyprint?

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);

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

[cpp]
view plaincopyprint?

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*/

}

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是怎么启动的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: