4412 uboot启动分析
感谢sea1105, https://blog.csdn.net/sea1105/article/details/52142772
在学习过程中,由于tiny4412资料太过于少,因此参考210的视屏,但友善提供的uboot算是比较新。启动过程不太一样,我分析出来的board_init_f 已经超出了16K的范围,但relocate_code,及后面的栈设置stack_setup,和copy_loop后搬移还在board_init_f之后调用。这两天一直难以理解。突然翻到了这部帖子,感觉解释的比较清楚过程,
对uboot 反编译。
_start : 0xc3e00000
reset: 0xc3e00050
borad_init_f : 0xc3e051fc
relocate_code : 0xc3e00074
stack_setup : 0xc3e00080
copy_loop : 0xc3e000a0
——————————————————————————————————————————————————————
主要参考:www.cnblogs.com/CoderTian/p/5995409.html
首先从链接库分析(board/samsung/tiny4412/u-boot.lds)
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
. = 0x00000000;
. = ALIGN();
.text :
{
arch/arm/cpu/armv7/start.o (.text)
board/samsung/tiny4412/libtiny4412.o (.text)
arch/arm/cpu/armv7/exynos/libexynos.o (.text)
*(.text)
}
. = ALIGN();
.rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }
. = ALIGN();
.data : {
*(.data)
}
. = ALIGN();
. = .;
__u_boot_cmd_start = .;
.u_boot_cmd : { *(.u_boot_cmd) }
__u_boot_cmd_end = .;
. = ALIGN();
.rel.dyn : {
__rel_dyn_start = .;
*(.rel*)
__rel_dyn_end = .;
}
.dynsym : {
__dynsym_start = .;
*(.dynsym)
}
.bss __rel_dyn_start (OVERLAY) : {
__bss_start = .;
*(.bss)
. = ALIGN();
_end = .;
}
/DISCARD/ : { *(.dynstr*) }
/DISCARD/ : { *(.dynamic*) }
/DISCARD/ : { *(.plt*) }
/DISCARD/ : { *(.interp*) }
/DISCARD/ : { *(.gnu*) }
}
u-boot.lds
从第三行可看出,指定_start 为入口 ,文本段.text 为入口文件。
在本链接脚本文件中,定义了起始地址为0x00000000,每个段使用4字节对齐(.ALIGN(4)),几个段分别为代码段(.text)、只读数据段(.rodata)、数据段(.data)。
下面打开start.o
.globl _start
_start: b reset
ldr pc, _undefined_instruction //未定义指令异常
ldr pc, _software_interrupt //软中断
ldr pc, _prefetch_abort //预取值终止
ldr pc, _data_abort //到一个不存在的地方取值
ldr pc, _not_used
ldr pc, _irq
ldr pc, _fiq _undefined_instruction: .word undefined_instruction
_software_interrupt: .word software_interrupt
_prefetch_abort: .word prefetch_abort
_data_abort: .word data_abort
_not_used: .word not_used
_irq: .word irq
_fiq: .word fiq
_pad: .word 0x12345678 /* now 16*4=64 */
.global _end_vect
_end_vect:
开始,先跳转至reset处
reset:
/*
* set the cpu to SVC32 mode
*/
mrs r0, cpsr
bic r0, r0, #0x1f
orr r0, r0, #0xd3
msr cpsr,r0 #if (CONFIG_OMAP34XX)
/* Copy vectors to mask ROM indirect addr */
adr r0, _start @ r0 <- current position of code
add r0, r0, # @ skip reset vector
mov r2, # @ r2 <- size to copy
add r2, r0, r2 @ r2 <- source end address
mov r1, #SRAM_OFFSET0 @ build vect addr
mov r3, #SRAM_OFFSET1
add r1, r1, r3
mov r3, #SRAM_OFFSET2
add r1, r1, r3
next:
ldmia r0!, {r3 - r10} @ copy from source address [r0]
stmia r1!, {r3 - r10} @ copy to target address [r1]
cmp r0, r2 @ until source end address [r2]
bne next @ loop until equal */
#if !defined(CONFIG_SYS_NAND_BOOT) && !defined(CONFIG_SYS_ONENAND_BOOT)
/* No need to copy/exec the clock code - DPLL adjust already done
* in NAND/oneNAND Boot.
*/
bl cpy_clk_code @ put dpll adjust code behind vectors
#endif /* NAND Boot */
#endif
/* the mask ROM code should have PLL and others stable */
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_crit
#endif /* Set stackpointer in internal RAM to call board_init_f */
call_board_init_f:
ldr sp, =(CONFIG_SYS_INIT_SP_ADDR)
bic sp, sp, # /* 8-byte alignment for ABI compliance */
ldr r0,=0x00000000
bl board_init_f
reset
可看出,首先将CPU设置为SVC32模式,然后在146行 调用 bl cpu_init_crit ,调用cpu_init_crit函数后返回reset。在调用board_init_f (arch/arm/lib/board.c),由于函数写在C语言中,不止lr 一个寄存器,所以首先需要设置sp堆栈,进行8字节对齐
cpu_init_crit: bl cache_init /* 使L1 I/D 无效
* Invalidate L1 I/D
*/
mov r0, # @ set up for MCR
mcr p15, , r0, c8, c7, @ invalidate TLBs
mcr p15, , r0, c7, c5, @ invalidate icache /*关闭MMU和caches
* disable MMU stuff and caches
*/
mrc p15, , r0, c1, c0,
bic r0, r0, #0x00002000 @ clear bits (--V-)
bic r0, r0, #0x00000007 @ clear bits : (-CAM)
orr r0, r0, #0x00000002 @ set bit (--A-) Align
orr r0, r0, #0x00000800 @ set bit (Z---) BTB
mcr p15, , r0, c1, c0, /*
* 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
mov lr, ip @ restore link
mov pc, lr @ back to my caller
cpu_init_crit
cache_init ,被定义在 board/samsung/tiny4412/lowlevel_init.S 文件中
.globl cache_init
cache_init:
mov pc, lr
并没有什么作用,接下来 bl lowlevel_init ,此函数主要作用使初始化系统时钟,内存,串口等。为BL2代码搬移做准备
.globl lowlevel_init
lowlevel_init: /* use iROM stack in bl2 */
ldr sp, =0x02060000
push {lr} /* check reset status */
ldr r0, =(INF_REG_BASE + INF_REG1_OFFSET)
ldr r1, [r0] /* Sleep wakeup reset */
ldr r2, =S5P_CHECK_SLEEP
cmp r1, r2
beq wakeup_reset /* set CP reset to low */
ldr r0, =0x11000C60
ldr r1, [r0]
ldr r2, =0xFFFFFF0F
and r1, r1, r2
orr r1, r1, #0x10
str r1, [r0]
ldr r0, =0x11000C68
ldr r1, [r0]
ldr r2, =0xFFFFFFF3
and r1, r1, r2
orr r1, r1, #0x4
str r1, [r0]
ldr r0, =0x11000C64
ldr r1, [r0]
ldr r2, =0xFFFFFFFD
and r1, r1, r2
str r1, [r0] /* led (GPM4_0~3) on */
ldr r0, =0x110002E0
ldr r1, =0x00001111
str r1, [r0]
ldr r1, =0x0e
str r1, [r0, #0x04] /* During sleep/wakeup or AFTR mode, pmic_init function is not available
* and it causes delays. So except for sleep/wakeup and AFTR mode,
* the below function is needed
*/
#if defined(CONFIG_HAS_PMIC)
bl pmic_init
#endif #if defined(CONFIG_ONENAND)
bl onenandcon_init
#endif #if defined(NAND_BOOTING)
bl nand_asm_init
#endif bl read_om /* when we already run in ram, we don't need to relocate U-Boot.
* and actually, memory controller must be configured before U-Boot
* is running in ram.
*/
ldr r0, =0xff000fff
bic r1, pc, r0 /* r0 <- current base addr of code */
ldr r2, _TEXT_BASE /* r1 <- original base addr in ram */
bic r2, r2, r0 /* r0 <- current base addr of code */
cmp r1, r2 /* compare r0, r1 */
beq after_copy /* r0 == r1 then skip sdram init and u-boot.bin loading */ #ifndef CONFIG_SMDKC220
ldr r0, =CHIP_ID_BASE
ldr r1, [r0]
lsr r1, r1, #
and r1, r1, #
cmp r1, #
bne v310_1
#endif /* init system clock */
bl system_clock_init /* Memory initialize */
bl mem_ctrl_asm_init /* init uart for debug */
bl uart_asm_init #if CONFIG_LL_DEBUG
mov r4, #0x4000
lowlevel_init
uart_asm_init:前一段是将GPIOA配置为UART,相当于初始化。从S5PV310_UART_CONSOLE_BASE:(arch/arm/include/asm/arch-exynos/cpu.h)配置UART0
ldr r1, =UART_UDIVSLOT_VAL
str r1, [r0, #UDIVSLOT_OFFSET]
UART_UDIVSLOT_VAL 为 UFRACVALn 与波特率分频有关 (tiny4412_val.h)
#UDIVSLOT_OFFSET 0x2C UFRACVALn 基于基地址的偏移 (cpu.h)
接着跳转至board_init_f (分析gd_t bd_t 结构)
void board_init_f(ulong bootflag)
{
bd_t *bd;
init_fnc_t **init_fnc_ptr;
gd_t *id;
ulong addr, addr_sp; /* Pointer is writable since we allocated a register for it 计算全局变量数据结构的地址,保存在gd中*/
gd = (gd_t *) ((CONFIG_SYS_INIT_SP_ADDR) & ~0x07);
//8字节对齐,为gd_t的空间。10 /* compiler optimization barrier needed for GCC >= 3.4 */
__asm__ __volatile__("": : :"memory");
//空函数,告诉系统要写一个内存,防止优化13
memset((void*)gd, , sizeof (gd_t)); gd->mon_len = _bss_end_ofs; for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
// 逐个调用init_sequence数组的初始化函数 19 if ((*init_fnc_ptr)() != ) {
hang();
}
} debug ("monitor len: %08lX\n", gd->mon_len);
/*
* Ram is setup, size stored in gd !!
*/
debug ("ramsize: %08lX\n", gd->ram_size);
#if defined(CONFIG_SYS_MEM_TOP_HIDE)
/*
* Subtract specified amount of memory to hide so that it won't
* get "touched" at all by U-Boot. By fixing up gd->ram_size
* the Linux kernel should now get passed the now "corrected"
* memory size and won't touch it either. This should work
* for arch/ppc and arch/powerpc. Only Linux board ports in
* arch/powerpc with bootwrapper support, that recalculate the
* memory size from the SDRAM controller setup will have to
* get fixed.
*/
gd->ram_size -= CONFIG_SYS_MEM_TOP_HIDE;
#endif addr = CONFIG_SYS_SDRAM_BASE + gd->ram_size; #ifdef CONFIG_LOGBUFFER
#ifndef CONFIG_ALT_LB_ADDR
/* reserve kernel log buffer */
addr -= (LOGBUFF_RESERVE);
debug ("Reserving %dk for kernel logbuffer at %08lx\n", LOGBUFF_LEN, addr);
#endif
#endif #ifdef CONFIG_PRAM
/*
* reserve protected RAM
*/
i = getenv_r("pram", (char *)tmp, sizeof (tmp));
reg = (i > ) ? simple_strtoul((const char *)tmp, NULL, ) : CONFIG_PRAM;
addr -= (reg << ); /* size is in kB */
debug ("Reserving %ldk for protected RAM at %08lx\n", reg, addr);
#endif /* CONFIG_PRAM */ #if !(defined(CONFIG_SYS_NO_ICACHE) && defined(CONFIG_SYS_NO_DCACHE))
/* reserve TLB table */
addr -= ( * ); /* round down to next 64 kB limit */
addr &= ~(0x10000 - ); gd->tlb_addr = addr;
debug ("TLB table at: %08lx\n", addr);
#endif /* round down to next 4 kB limit */
addr &= ~( - );
debug ("Top of RAM usable for U-Boot at: %08lx\n", addr); #ifdef CONFIG_VFD
# ifndef PAGE_SIZE
# define PAGE_SIZE
# endif
/*
* reserve memory for VFD display (always full pages)
*/
addr -= vfd_setmem(addr);
gd->fb_base = addr;
#endif /* CONFIG_VFD */ #ifdef CONFIG_LCD
/* reserve memory for LCD display (always full pages) */
addr = lcd_setmem(addr);
gd->fb_base = addr;
#endif /* CONFIG_LCD */ /*
* reserve memory for U-Boot code, data & bss
* round down to next 4 kB limit
*/
addr -= gd->mon_len;
addr &= ~( - ); #if defined(CONFIG_S5P) || defined(CONFIG_S5P6450)
addr = CONFIG_SYS_LOAD_ADDR;
#endif debug ("Reserving %ldk for U-Boot at: %08lx\n", gd->mon_len >> , addr); #ifndef CONFIG_PRELOADER
/*
* reserve memory for malloc() arena
*/
addr_sp = addr - TOTAL_MALLOC_LEN;
debug ("Reserving %dk for malloc() at: %08lx\n",
TOTAL_MALLOC_LEN >> , addr_sp);
/*
* (permanently) allocate a Board Info struct
* and a permanent copy of the "global" data
*/
addr_sp -= sizeof (bd_t);
bd = (bd_t *) addr_sp;
gd->bd = bd;
debug ("Reserving %zu Bytes for Board Info at: %08lx\n",
sizeof (bd_t), addr_sp);
addr_sp -= sizeof (gd_t);
id = (gd_t *) addr_sp;
debug ("Reserving %zu Bytes for Global Data at: %08lx\n",
sizeof (gd_t), addr_sp); /* setup stackpointer for exeptions */
gd->irq_sp = addr_sp;
#ifdef CONFIG_USE_IRQ
addr_sp -= (CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ);
debug ("Reserving %zu Bytes for IRQ stack at: %08lx\n",
CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ, addr_sp);
#endif /* leave 3 words for abort-stack */
addr_sp -= ; /* 8-byte alignment for ABI compliance */
addr_sp &= ~0x07;
#else
addr_sp += ; /* leave 32 words for abort-stack */
gd->irq_sp = addr_sp;
#endif debug ("New Stack Pointer is: %08lx\n", addr_sp); #ifdef CONFIG_POST
post_bootmode_init();
post_run(NULL, POST_ROM | post_bootmode_get());
#endif gd->bd->bi_baudrate = gd->baudrate;
/* Ram ist board specific, so move it to board code ... */
dram_init_banksize();
display_dram_config(); /* and display it */ gd->relocaddr = addr;
gd->start_addr_sp = addr_sp;
gd->reloc_off = addr - _TEXT_BASE;
debug ("relocation Offset is: %08lx\n", gd->reloc_off);
memcpy(id, (void *)gd, sizeof (gd_t)); relocate_code(addr_sp, id, addr);
/* NOTREACHED - relocate_code() does not return */
} #if !defined(CONFIG_SYS_NO_FLASH)
static char *failed = "*** failed ***\n";
#endif
borad_init_f
init_fnc_t *init_sequence[] = {
#if defined(CONFIG_ARCH_CPU_INIT)
arch_cpu_init, /* basic arch cpu dependent setup */
#endif
#if defined(CONFIG_BOARD_EARLY_INIT_F)
board_early_init_f,
#endif
timer_init, /* initialize timer */
#ifdef CONFIG_FSL_ESDHC
get_clocks,
#endif
env_init, /* initialize environment */
#if defined(CONFIG_S5P6450) && !defined(CONFIG_S5P6460_IP_TEST)
init_baudrate, /* initialze baudrate settings */
serial_init, /* serial communications setup */
#endif
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
NULL,
};
init_seqence
u-boot使用一个init_sequence数组来存储大多数开发板都要执行的初始化函数的函数指针
board_init_f函数在调用完初始化函数指针、填充完gd结构之后,调用了arch/arm/cpu/armv7/start.S中的relocate_code
.globl relocate_code
relocate_code:
mov r4, r0 /* save addr_sp */
mov r5, r1 /* save addr of gd */
mov r6, r2 /* save addr of destination */ /* Set up the stack */
stack_setup:
mov sp, r4 adr r0, _start
#if defined(CONFIG_S5PC110) && defined(CONFIG_EVT1) && !defined(CONFIG_FUSED)
sub r0, r0, #
#endif
#ifndef CONFIG_PRELOADER
cmp r0, r6
beq clear_bss /* skip relocation */
#endif
mov r1, r6 /* r1 <- scratch for copy_loop */
ldr r2, _TEXT_BASE
ldr r3, _bss_start_ofs
add r2, r0, r3 /* r2 <- source end address */ copy_loop:
ldmia r0!, {r9-r10} /* copy from source address [r0] */
stmia r1!, {r9-r10} /* copy to target address [r1] */
cmp r0, r2 /* until source end address [r2] */
blo copy_loop #ifndef CONFIG_PRELOADER
/*
* fix .rel.dyn relocations
*/
ldr r0, _TEXT_BASE /* r0 <- Text base */
sub r9, r6, r0 /* r9 <- relocation offset */
ldr r10, _dynsym_start_ofs /* r10 <- sym table ofs */
add r10, r10, r0 /* r10 <- sym table in FLASH */
ldr r2, _rel_dyn_start_ofs /* r2 <- rel dyn start ofs */
add r2, r2, r0 /* r2 <- rel dyn start in FLASH */
ldr r3, _rel_dyn_end_ofs /* r3 <- rel dyn end ofs */
add r3, r3, r0 /* r3 <- rel dyn end in FLASH */
fixloop:
ldr r0, [r2] /* r0 <- location to fix up, IN FLASH! */
add r0, r0, r9 /* r0 <- location to fix up in RAM */
ldr r1, [r2, #]
and r7, r1, #0xff
cmp r7, # /* relative fixup? */
beq fixrel
cmp r7, # /* absolute fixup? */
beq fixabs
/* ignore unknown type of fixup */
b fixnext
fixabs:
/* absolute fix: set location to (offset) symbol value */
mov r1, r1, LSR # /* r1 <- symbol index in .dynsym */
add r1, r10, r1 /* r1 <- address of symbol in table */
ldr r1, [r1, #] /* r1 <- symbol value */
add r1, r1, r9 /* r1 <- relocated sym addr */
b fixnext
fixrel:
/* relative fix: increase location by offset */
ldr r1, [r0]
add r1, r1, r9
fixnext:
str r1, [r0]
add r2, r2, # /* each rel.dyn entry is 8 bytes */
cmp r2, r3
blo fixloop clear_bss:
ldr r0, _bss_start_ofs
ldr r1, _bss_end_ofs
ldr r3, _TEXT_BASE /* Text base */
mov r4, r6 /* reloc addr */
add r0, r0, r4
add r1, r1, r4
mov r2, #0x00000000 /* clear */ clbss_l:str r2, [r0] /* clear loop... */
add r0, r0, #
cmp r0, r1
bne clbss_l
#endif /* #ifndef CONFIG_PRELOADER */ /*
* We are done. Do not return, instead branch to second part of board
* initialization, now running from RAM.
*/
jump_2_ram:
ldr r0, _board_init_r_ofs
adr r1, _start
add lr, r0, r1
@ add lr, lr, r9
/* setup parameters for board_init_r */
mov r0, r5 /* gd_t */
mov r1, r6 /* dest_addr */
/* jump to it ... */
mov pc, lr _board_init_r_ofs:
.word board_init_r - _start
relocate_code
搬移uboot.bin
转入borad_init_r
void board_init_r(gd_t *id, ulong dest_addr)
{
char *s;
bd_t *bd;
ulong malloc_start;
#if !defined(CONFIG_SYS_NO_FLASH)
ulong flash_size;
#endif gd = id;
bd = gd->bd; gd->flags |= GD_FLG_RELOC; /* tell others: relocation done */ monitor_flash_len = _bss_start_ofs;
debug ("monitor flash len: %08lX\n", monitor_flash_len);
board_init(); /* Setup chipselects */ #ifdef CONFIG_SERIAL_MULTI
//serial_initialize();
#endif debug ("Now running in RAM - U-Boot at: %08lx\n", dest_addr); #ifdef CONFIG_LOGBUFFER
logbuff_init_ptrs();
#endif
#ifdef CONFIG_POST
post_output_backlog();
#endif /* The Malloc area is immediately below the monitor copy in DRAM */
malloc_start = dest_addr - TOTAL_MALLOC_LEN;
mem_malloc_init(malloc_start, TOTAL_MALLOC_LEN); #if !defined(CONFIG_SYS_NO_FLASH)
puts("FLASH:\t"); if ((flash_size = flash_init()) > ) {
# ifdef CONFIG_SYS_FLASH_CHECKSUM
print_size(flash_size, "");
/*
* Compute and print flash CRC if flashchecksum is set to 'y'
*
* NOTE: Maybe we should add some WATCHDOG_RESET()? XXX
*/
s = getenv("flashchecksum");
if (s && (*s == 'y')) {
printf(" CRC: %08X",
crc32 (, (const unsigned char *) CONFIG_SYS_FLASH_BASE, flash_size)
);
}
putc('\n');
# else /* !CONFIG_SYS_FLASH_CHECKSUM */
print_size(flash_size, "\n");
# endif /* CONFIG_SYS_FLASH_CHECKSUM */
} else {
puts(failed);
hang();
}
#endif #if defined(CONFIG_CMD_NAND)
puts("NAND:\t");
nand_init(); /* go init the NAND */
#endif #if defined(CONFIG_CMD_ONENAND)
onenand_init();
#endif #ifdef CONFIG_GENERIC_MMC
mmc_initialize(bd);
#endif #ifdef CONFIG_HAS_DATAFLASH
AT91F_DataflashInit();
dataflash_print_info();
#endif /* initialize environment */
env_relocate(); #ifdef CONFIG_VFD
/* must do this after the framebuffer is allocated */
drv_vfd_init();
#endif /* CONFIG_VFD */ /* IP Address */
gd->bd->bi_ip_addr = getenv_IPaddr("ipaddr"); stdio_init(); /* get the devices list going. */ jumptable_init(); #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 /* set up exceptions */
interrupt_init();
/* enable exceptions */
enable_interrupts(); /* Perform network card initialisation if necessary */
#if defined(CONFIG_DRIVER_SMC91111) || defined(CONFIG_DRIVER_LAN91C96)
/* XXX: this needs to be moved to board init */
if (getenv("ethaddr")) {
uchar enetaddr[];
eth_getenv_enetaddr("ethaddr", enetaddr);
smc_set_mac_addr(enetaddr);
}
#endif /* CONFIG_DRIVER_SMC91111 || CONFIG_DRIVER_LAN91C96 */ #if defined(CONFIG_DRIVER_DM9000)
/* XXX: this needs to be moved to board init */
if (getenv("ethaddr")) {
uchar enetaddr[];
eth_getenv_enetaddr("ethaddr", enetaddr);
dm9000_set_mac_addr(enetaddr);
}
#endif /* Initialize from environment */
if ((s = getenv("loadaddr")) != NULL) {
load_addr = simple_strtoul(s, NULL, );
}
#if defined(CONFIG_CMD_NET)
if ((s = getenv("bootfile")) != NULL) {
copy_filename(BootFile, s, sizeof (BootFile));
}
#endif #ifdef BOARD_LATE_INIT
board_late_init();
#endif #ifdef CONFIG_BITBANGMII
bb_miiphy_init();
#endif
#if defined(CONFIG_CMD_NET)
#if defined(CONFIG_NET_MULTI)
puts("Net:\t");
#endif
eth_initialize(gd->bd);
#if defined(CONFIG_RESET_PHY_R)
debug ("Reset Ethernet PHY\n");
reset_phy();
#endif
#endif #ifdef CONFIG_POST
post_run(NULL, POST_RAM | post_bootmode_get());
#endif #if defined(CONFIG_PRAM) || defined(CONFIG_LOGBUFFER)
/*
* Export available size of memory for Linux,
* taking into account the protected RAM at top of memory
*/
{
ulong pram;
uchar memsz[];
#ifdef CONFIG_PRAM
char *s; if ((s = getenv("pram")) != NULL) {
pram = simple_strtoul(s, NULL, );
} else {
pram = CONFIG_PRAM;
}
#else
pram=;
#endif
#ifdef CONFIG_LOGBUFFER
#ifndef CONFIG_ALT_LB_ADDR
/* Also take the logbuffer into account (pram is in kB) */
pram += (LOGBUFF_LEN+LOGBUFF_OVERHEAD)/;
#endif
#endif
sprintf((char *)memsz, "%ldk", (bd->bi_memsize / ) - pram);
setenv("mem", (char *)memsz);
}
#endif /* main_loop() can return to retry autoboot, if so just run it again. */
for (;;) {
main_loop();
} /* NOTREACHED - no way out of command loop except booting */
}
borad_init_r
最后,转入mian_loop
void main_loop (void)
{
#ifndef CONFIG_SYS_HUSH_PARSER
static char lastcommand[CONFIG_SYS_CBSIZE] = { , };
int len;
int rc = ;
int flag;
#endif #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
char *s;
int bootdelay;
#endif
#ifdef CONFIG_PREBOOT
char *p;
#endif
#ifdef CONFIG_BOOTCOUNT_LIMIT
unsigned long bootcount = ;
unsigned long bootlimit = ;
char *bcs;
char bcs_set[];
#endif /* CONFIG_BOOTCOUNT_LIMIT */ #if defined(CONFIG_VFD) && defined(VFD_TEST_LOGO)
ulong bmp = ; /* default bitmap */
extern int trab_vfd (ulong bitmap); #ifdef CONFIG_MODEM_SUPPORT
if (do_mdm_init)
bmp = ; /* alternate bitmap */
#endif
trab_vfd (bmp);
#endif /* CONFIG_VFD && VFD_TEST_LOGO */ #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, ) : ;
#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(); /* disable Control C checking */
# endif # ifndef CONFIG_SYS_HUSH_PARSER
run_command (p, );
# 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_UPDATE_TFTP)
update_tftp ();
#endif /* CONFIG_UPDATE_TFTP */ #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
s = getenv ("bootdelay");
bootdelay = s ? (int)simple_strtol(s, NULL, ) : CONFIG_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"); debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>"); if (bootdelay >= && s && !abortboot (bootdelay)) {
# ifdef CONFIG_AUTOBOOT_KEYED
int prev = disable_ctrlc(); /* disable Control C checking */
# endif # ifndef CONFIG_SYS_HUSH_PARSER
run_command (s, );
# 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, );
# else
parse_string_outer(s, FLAG_PARSE_SEMICOLON |
FLAG_EXIT_FROM_LOOP);
# endif
}
}
#endif /* CONFIG_MENUKEY */
#endif /* CONFIG_BOOTDELAY */ /*
* Main Loop for Monitor Command Processing
*/
#ifdef CONFIG_SYS_HUSH_PARSER
parse_file_outer();
/* This point is never reached */
for (;;);
#else
for (;;) {
#ifdef CONFIG_BOOT_RETRY_TIME
if (rc >= ) {
/* Saw enough of a valid command to
* restart the timeout.
*/
reset_cmd_timeout();
}
#endif
len = readline (CONFIG_SYS_PROMPT); flag = ; /* assume no special flags for now */
if (len > )
strcpy (lastcommand, console_buffer);
else if (len == )
flag |= CMD_FLAG_REPEAT;
#ifdef CONFIG_BOOT_RETRY_TIME
else if (len == -) {
/* -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, , , NULL);
# else
return; /* retry autoboot */
# endif
}
#endif if (len == -)
puts ("<INTERRUPT>\n");
else
rc = run_command (lastcommand, flag); if (rc <= ) {
/* invalid command or not repeatable, forget it */
lastcommand[] = ;
}
}
#endif /*CONFIG_SYS_HUSH_PARSER*/
} #ifdef CONFIG_BOOT_RETRY_TIME
/***************************************************************************
* initialize command line timeout
*/
void init_cmd_timeout(void)
{
char *s = getenv ("bootretry"); if (s != NULL)
retry_time = (int)simple_strtol(s, NULL, );
else
retry_time = CONFIG_BOOT_RETRY_TIME; if (retry_time >= && retry_time < CONFIG_BOOT_RETRY_MIN)
retry_time = CONFIG_BOOT_RETRY_MIN;
}
main_loop
其中,主要是
#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
s = getenv ("bootdelay"); //获取bootdelay参数
bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY; debug ("### main_loop entered: bootdelay=%d\n\n", bootdelay);
s = getenv ("bootcmd"); //获取bootcmd 参数 debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");
在/include/configs/ting4412.h中 ,可找到
#define CONFIG_BOOTDELAY 3
/* Default boot commands for Android booting. */
#define CONFIG_BOOTCOMMAND "movi read kernel 0 40008000;movi read rootfs 0 41000000 100000;bootm 40008000 41000000"
#define CONFIG_BOOTARGS ""
我们可以看到,bootcmd默认执行的是"movi read kernel 0 40008000;movi read rootfs 0 41000000400000;bootm 40008000 41000000",首先读取kernel,再读取rootfs,之后跳转到40008000地址运行
——————————————————————————————————————————————————————————
命令获取
其实原理很简单,就是获取输入的第一个单词,从扫描所有的cmd_tbl_t结构体,与name成员进行比较,如果匹配,则将后续输入整理成参数列表,调用cmd指针指向的函数完成命令行。
for (;;) { len = readline (CONFIG_SYS_PROMPT);
// 在readline中首先先显示CONFIG_SYS_PROMPT定义的字符串, 然后等待键盘输入
// 每次从终端读入一个字符, 先判断是否是正常字符(ctrl+c, 回车等属于非正常字符)
// 对与正常字符那么将其存入console_buffer中, 并在终端回显
//查看/include/configs/ting4412.h知,#define CONFIG_SYS_PROMPT "TINY4412 # " 相当于用户名
flag = 0; /* assume no special flags for now */
if (len > 0)
strcpy (lastcommand, console_buffer);
else if (len == 0)
flag |= CMD_FLAG_REPEAT; if (len == -1)
puts ("<INTERRUPT>\n");
else
rc = run_command (lastcommand, flag);
// 命令从console_buffer搬运到lastcommand中
readline -->readline_into_buffer() --> , read_line()读取到命令行后会调用common/main.c文件中的run_command(). 现在是分析run_command()的时候了, 不管是从环境变量还是终端获得命令, 都是由run_command()来处理的.
cmd_tbl_t *cmdtp;
char cmdbuf[CONFIG_SYS_CBSIZE]; /* working copy of cmd */
char *token; /* start of token in cmdbuf */
char *sep; /*end of token (separator) in cmdbuf */
char finaltoken[CONFIG_SYS_CBSIZE];
char *str = cmdbuf;
char *argv[CONFIG_SYS_MAXARGS + 1]; /* NULL terminated 空终止*/
int argc, inquotes;
int repeatable = 1;
int rc = 0;
clear_ctrlc(); /* forget any previous Control C 忽略之前C的控制*/
//下面if语句判断命令是否太长, 还是避免输入了超过CONFIG_SYS_CBSIZE个字符的命令
if (!cmd || !*cmd) {
return -1; /* empty command */
} if (strlen(cmd) >= CONFIG_SYS_CBSIZE) {
puts ("## Command too long!\n");
return -1;
} strcpy (cmdbuf, cmd); /* Process separators and check for invalid
* repeatable commands 处理分隔符并检查是否有无效的可重复命令
*/ #ifdef DEBUG_PARSER
printf ("[PROCESS_SEPARATORS] %s\n", cmd);
#endif
//str就是指向cmdbuf的指针
while (*str) { /*
* Find separator, or string end
* Allow simple escape of ';' by writing "\;"
*找到;作为命令结束符, 因为多个命令可以一次输入, 并以;分割. 忽略'\;'
*/
for (inquotes = 0, sep = str; *sep; sep++) {
if ((*sep=='\'') &&
(*(sep-1) != '\\'))
inquotes=!inquotes; if (!inquotes &&
(*sep == ';') && /* separator */
( sep != str) && /* past string start */
(*(sep-1) != '\\')) /* and NOT escaped */
break;
}//如果上面for循环找到一条以';'结束的命令, 那么sep指向命令末尾 /*
* Limit the token to data between separators将命令限制为分隔符之间的数据
*/
token = str;
if (*sep) {
str = sep + 1; /* str为下一条命令开始start of command for next pass */
*sep = '\0';/* 作为token的结束 */
}
else
str = sep; /* no more commands for next pass */ /* find macros in this token and replace them 在此命令中查找宏并替换它们*/
process_macros (token, finaltoken);
/*将命令行中的关键词取出放入argv中, 注意, 命令行被分解到argv数组中, Extract arguments */
if ((argc = parse_line (finaltoken, argv)) == 0) {
rc = -1; /* no command at all */
continue;
} /* Look up command in command table */
if ((cmdtp = find_cmd(argv[0])) == NULL) {
printf ("Unknown command '%s' - try 'help'\n", argv[0]);
rc = -1; /* give up after bad command */
continue;
} /* found - check max args */
if (argc > cmdtp->maxargs) {
cmd_usage(cmdtp);
rc = -1;
continue;
}
——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
#if defined(CONFIG_CMD_BOOTD)
/* avoid "bootd" recursion 避免“bootd”递归*/
if (cmdtp->cmd == do_bootd) {
#ifdef DEBUG_PARSER
printf ("[%s]\n", finaltoken);
#endif
if (flag & CMD_FLAG_BOOTD) {
puts ("'bootd' recursion detected\n");
rc = -1;
continue;
} else {
flag |= CMD_FLAG_BOOTD;
}
}
#endif /* OK - call function to do the command调用函数执行命令 */
if ((cmdtp->cmd) (cmdtp, flag, argc, argv) != 0) {
rc = -1;
} repeatable &= cmdtp->repeatable; /* ctrl+c 终止命令 。。 Did the user stop this? */
if (had_ctrlc ())
return -1; /* if stopped then not repeatable */
} return rc ? rc : repeatable;
}
————————————————————————————————————————————————————————————————————
命令执行
首要会执行find_cmd()在.u_boot_cmd段中寻找该命令的cmd_tbl_t结构, 找到后返回该结构. 该命令的结构是通过定义在include/command.h中的宏定义U_BOOT_CMD登记进.u_boot_cmd段中的.
#ifdef CONFIG_SYS_LONGHELP #define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \
cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage, help} #define U_BOOT_CMD_MKENT(name,maxargs,rep,cmd,usage,help) \
{#name, maxargs, rep, cmd, usage, help} #else /* no long help info */ #define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \
cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage} #define U_BOOT_CMD_MKENT(name,maxargs,rep,cmd,usage,help) \
{#name, maxargs, rep, cmd, usage}
这里有两种定义方式,区别在于是否保留帮助信息
这个宏的作用是定义一个cmd_tbl_t结构体,并且将此结构体的存储区域通过Struct_Section限制在了u_boot_cmd域。同时我们看一下u-boot.lds文件中u_boot_cmd域的定义
u-boot.lds
__u_boot_cmd_start = .;
.u_boot_cmd : { *(.u_boot_cmd) }
__u_boot_cmd_end = .;
这里定义了u_boot_cmd域的位置,同时也定义了两个变量:__u_boot_cmd_start和__u_boot_cmd_end,用来标识存储空间的起始位置,在查找命令时,防止数组溢出。
通过U_BOOT_CMD宏,uboot可以在一块连续的空间中定义若干个cmd_tbl_t变量。cmd_tbl_t的定义如下:
/*
* Monitor Command Table
*/ struct cmd_tbl_s {
char *name; /* Command Name */
int maxargs; /* maximum number of arguments 最大参数*/
int repeatable; /* autorepeat allowed? 是不是可以重复运行*/
/* Implementation function 功能*/
int (*cmd)(struct cmd_tbl_s *, int, int, char * const []);
char *usage; /* Usage message (short) */
#ifdef CONFIG_SYS_LONGHELP
char *help; /* Help message (long) */
#endif
#ifdef CONFIG_AUTO_COMPLETE
/* do auto completion on the arguments */
int (*complete)(int argc, char * const argv[], char last_char, int maxv, char *cmdv[]);
#endif
};
这个结构体将一个命令行与参数个数、用法信息、帮助信息和对应的执行函数关联起来。
cmd_tbl_t *find_cmd(const char *cmd);
cmd_tbl_t *find_cmd_tbl (const char *cmd, cmd_tbl_t *table, int table_len); 具体函数在/common/command.c下 extern cmd_tbl_t __u_boot_cmd_bdinfo;
extern cmd_tbl_t __u_boot_cmd_showvar; cmd_tbl_t *find_cmd (const char *cmd)
{
//int len = &__u_boot_cmd_end - &__u_boot_cmd_start;
int len = &__u_boot_cmd_showvar - &__u_boot_cmd_bdinfo + 1;
//return find_cmd_tbl(cmd, &__u_boot_cmd_start, len);
return find_cmd_tbl(cmd, &__u_boot_cmd_bdinfo, len);
} 函数会找到命令区的入口以及命令总数,最后调用find_cmd_tbl(); * find command table entry for a command 查找命令的命令表条目,找到指定函数入口
*/
cmd_tbl_t *find_cmd_tbl (const char *cmd, cmd_tbl_t *table, int table_len)
{
cmd_tbl_t *cmdtp;
cmd_tbl_t *cmdtp_temp = table; /*Init value */
const char *p;
int len;
int n_found = 0; if (!cmd)
return NULL;
/*
* Some commands allow length modifiers (like "cp.b");
* compare command name only until first dot.
*/
len = ((p = strchr(cmd, '.')) == NULL) ? strlen (cmd) : (p - cmd);
//这个for循环是find_cmd()的核心, _table指定的是在__.u_boot_cmd的开始地址, table + table_len就是相对应的结束地址.
//注意:for中的cmdtp++每次加的不是1二是sizeof(cmd_tbl_t). 所以在这个循环中将会遍历在__.u_boot_cmd段中的所有命令, 并查找与本次命令向对应的那个命令的结构体.
for (cmdtp = table;
cmdtp != table + table_len;
cmdtp++) {
if (strncmp (cmd, cmdtp->name, len) == 0) {
if (len == strlen (cmdtp->name))
return cmdtp; /* 命令完全匹配, 返回命令结构体 full match */ cmdtp_temp = cmdtp; /* 部分匹配, 后面会返回cmdtp_temp, abbreviated command ? */
n_found++;
}
}
if (n_found == 1) { /* exactly one match */
return cmdtp_temp;
} return NULL; /* not found or ambiguous command */
}
简单的例子:
#include <common.h>
#include <command.h> extern char version_string[]; int do_version(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
printf("\n%s\n", version_string); return 0;
}
// 在u_boot_cmd中注册命令
U_BOOT_CMD(
version, 1, 1, do_version,
"print monitor version",
""
);
.weak关键字,在网上找了到的解释,我的理解是.weak相当于声明一个函数,如果该函数在其他地方没有定义,则为空函数,有定义则调用该定义的函数。
4412 uboot启动分析的更多相关文章
- 如何在Ubuntu下使用TF/SD 卡制作Exynos 4412 u-boot启动盘
/** ****************************************************************************** * @author Maox ...
- 如何在Mac下使用TF/SD 卡制作Exynos 4412 u-boot启动盘
/** ****************************************************************************** * @author Maox ...
- Uboot启动分析之Start.S
1.start.S引入 1.1.u-boot.lds中找到start.S入口 1)C语言中代码的分析第一步就是找到main.c,找到函数的入口 2)uboot中因为有汇编语言参与所以就不能像C一样.U ...
- u-boot简单学习笔记(三)——AR9331 uboot启动分析
1.最开始系统上电后 ENTRY(_start)程序入口点是 _start 由board/ap121/u-boot.lds引导 2._start: cpu/mips/start.S 是第一个源程序文 ...
- jLink V8调试exynos 4412 u-boot的几点补充
/** ****************************************************************************** * @author Maox ...
- 嵌入式Linux驱动学习之路(五)u-boot启动流程分析
这里说的u-boot启动流程,值得是从上电开机执行u-boot,到u-boot,到u-boot加载操作系统的过程.这一过程可以分为两个过程,各个阶段的功能如下. 第一阶段的功能: 硬件设备初始化. 加 ...
- (转载)U-boot启动完全分析
1.1 U-Boot工作过程 U-Boot启动内核的过程可以分为两个阶段,两个阶段的功能如下: (1)第一阶段的功能 Ø 硬件设备初始化 Ø 加载U-Boot第二阶段代码到RAM空间 Ø 设置好栈 Ø ...
- uboot启动 及命令分析(3)
u-boot命令 先贴一个重要结构,位于uboot/include/command.h,这个结构代表每个uboot命令 struct cmd_tbl_s { char *name; /* ...
- u-boot启动流程分析(2)_板级(board)部分
转自:http://www.wowotech.net/u-boot/boot_flow_2.html 目录: 1. 前言 2. Generic Board 3. _main 4. global dat ...
随机推荐
- 1.1.8 怎样在Word的页眉中插入一级标题
可以通过域来实现,其具体的操作步骤: 1.为章.节标题使用标题样式.例如:章标题使用标题1样式,节标题使用标题2样式.操作方法:选中章(节)标题,然后点击选项卡中“样式”中的). 2.设置文档页眉和页 ...
- MVC Ajax.BeginForm 提交上传图片
吃水不忘挖井人,如果对你有帮助,请说声谢谢.如果你要转载,请注明出处.谢谢! 异步提交时,出现图片不能上传. 起初我定格在 System.Web.Mvc 中.查询源码时,也是没有问题的.那问题出现 ...
- 配置jboss为windows服务
先确保jdk和jboss的环境变量是正常可用的 1.(下载binaries 2.x.x-windows x86)找到service.bat和jbosssvc.exe两个文件 1.1 binaries ...
- 图像小波变换去噪——MATLAB实现
clear; [A,map]=imread('C:\Users\wangd\Documents\MATLAB\1.jpg'); X=rgb2gray(A); %画出原始图像 subplot(,,);i ...
- redis集群设置密码
redis集群密码设置 1.密码设置(推荐)方式一:修改所有Redis集群中的redis.conf文件加入: masterauth passwd123 requirepass passwd123 说 ...
- 利用MYSQL的函数实现用户登录功能,进出都是JSON(第一版)
以HMAC密钥形式发放密钥令牌 功能如下 1:记录用户的登录的IP地址.时间 2:实现密码错误次数超限后锁定,并提示何时解锁 CREATE DEFINER=`root`@`%` FUNCTION `u ...
- Filedset
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- java中拼接两个对象集合
目标: 根据两个list中每条记录的某个属性是否相同来拼接. 1.首先定义一个字符串 String str = "[{\"ITEMID\":2,\"ITEMN ...
- CIF 搜索逻辑
test code #include <cstddef> class CIF { }; template <typename OBJ> class CList { public ...
- Nios II Host-Based File System
Nios II Host-Based File System 允许运行在Nios II的程序,在Debug模式下,通过Altera download cable来读写PC上当前工程目录下(及其子目录) ...