感谢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)
1 OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") 2 OUTPUT_ARCH(arm) 3 ENTRY(_start) 4 SECTIONS 5 { 6 . = 0x00000000; 7 . = ALIGN(4); 8 .text : 9 { 10 arch/arm/cpu/armv7/start.o (.text) 11 board/samsung/tiny4412/libtiny4412.o (.text) 12 arch/arm/cpu/armv7/exynos/libexynos.o (.text) 13 *(.text) 14 } 15 . = ALIGN(4); 16 .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) } 17 . = ALIGN(4); 18 .data : { 19 *(.data) 20 } 21 . = ALIGN(4); 22 . = .; 23 __u_boot_cmd_start = .; 24 .u_boot_cmd : { *(.u_boot_cmd) } 25 __u_boot_cmd_end = .; 26 . = ALIGN(4); 27 .rel.dyn : { 28 __rel_dyn_start = .; 29 *(.rel*) 30 __rel_dyn_end = .; 31 } 32 .dynsym : { 33 __dynsym_start = .; 34 *(.dynsym) 35 } 36 .bss __rel_dyn_start (OVERLAY) : { 37 __bss_start = .; 38 *(.bss) 39 . = ALIGN(4); 40 _end = .; 41 } 42 /DISCARD/ : { *(.dynstr*) } 43 /DISCARD/ : { *(.dynamic*) } 44 /DISCARD/ : { *(.plt*) } 45 /DISCARD/ : { *(.interp*) } 46 /DISCARD/ : { *(.gnu*) } 47 }
从第三行可看出,指定_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处
1 reset: 2 /* 3 * set the cpu to SVC32 mode 4 */ 5 mrs r0, cpsr 6 bic r0, r0, #0x1f 7 orr r0, r0, #0xd3 8 msr cpsr,r0 9 10 #if (CONFIG_OMAP34XX) 11 /* Copy vectors to mask ROM indirect addr */ 12 adr r0, _start @ r0 <- current position of code 13 add r0, r0, #4 @ skip reset vector 14 mov r2, #64 @ r2 <- size to copy 15 add r2, r0, r2 @ r2 <- source end address 16 mov r1, #SRAM_OFFSET0 @ build vect addr 17 mov r3, #SRAM_OFFSET1 18 add r1, r1, r3 19 mov r3, #SRAM_OFFSET2 20 add r1, r1, r3 21 next: 22 ldmia r0!, {r3 - r10} @ copy from source address [r0] 23 stmia r1!, {r3 - r10} @ copy to target address [r1] 24 cmp r0, r2 @ until source end address [r2] 25 bne next @ loop until equal */ 26 #if !defined(CONFIG_SYS_NAND_BOOT) && !defined(CONFIG_SYS_ONENAND_BOOT) 27 /* No need to copy/exec the clock code - DPLL adjust already done 28 * in NAND/oneNAND Boot. 29 */ 30 bl cpy_clk_code @ put dpll adjust code behind vectors 31 #endif /* NAND Boot */ 32 #endif 33 /* the mask ROM code should have PLL and others stable */ 34 #ifndef CONFIG_SKIP_LOWLEVEL_INIT 35 bl cpu_init_crit 36 #endif 37 38 /* Set stackpointer in internal RAM to call board_init_f */ 39 call_board_init_f: 40 ldr sp, =(CONFIG_SYS_INIT_SP_ADDR) 41 bic sp, sp, #7 /* 8-byte alignment for ABI compliance */ 42 ldr r0,=0x00000000 43 bl board_init_f
可看出,首先将CPU设置为SVC32模式,然后在146行 调用 bl cpu_init_crit ,调用cpu_init_crit函数后返回reset。在调用board_init_f (arch/arm/lib/board.c),由于函数写在C语言中,不止lr 一个寄存器,所以首先需要设置sp堆栈,进行8字节对齐
1 cpu_init_crit: 2 3 bl cache_init 4 5 /* 使L1 I/D 无效 6 * Invalidate L1 I/D 7 */ 8 mov r0, #0 @ set up for MCR 9 mcr p15, 0, r0, c8, c7, 0 @ invalidate TLBs 10 mcr p15, 0, r0, c7, c5, 0 @ invalidate icache 11 12 /*关闭MMU和caches 13 * disable MMU stuff and caches 14 */ 15 mrc p15, 0, r0, c1, c0, 0 16 bic r0, r0, #0x00002000 @ clear bits 13 (--V-) 17 bic r0, r0, #0x00000007 @ clear bits 2:0 (-CAM) 18 orr r0, r0, #0x00000002 @ set bit 1 (--A-) Align 19 orr r0, r0, #0x00000800 @ set bit 12 (Z---) BTB 20 mcr p15, 0, r0, c1, c0, 0 21 22 /* 23 * Jump to board specific initialization... 24 * The Mask ROM will have already initialized 25 * basic memory. Go here to bump up clock rate and handle 26 * wake up conditions. 27 */ 28 mov ip, lr @ persevere link reg across call 29 bl lowlevel_init @ go setup pll,mux,memory 30 mov lr, ip @ restore link 31 mov pc, lr @ back to my caller
cache_init ,被定义在 board/samsung/tiny4412/lowlevel_init.S 文件中
.globl cache_init cache_init: mov pc, lr
并没有什么作用,接下来 bl lowlevel_init ,此函数主要作用使初始化系统时钟,内存,串口等。为BL2代码搬移做准备
1 .globl lowlevel_init 2 lowlevel_init: 3 4 /* use iROM stack in bl2 */ 5 ldr sp, =0x02060000 6 push {lr} 7 8 /* check reset status */ 9 ldr r0, =(INF_REG_BASE + INF_REG1_OFFSET) 10 ldr r1, [r0] 11 12 /* Sleep wakeup reset */ 13 ldr r2, =S5P_CHECK_SLEEP 14 cmp r1, r2 15 beq wakeup_reset 16 17 /* set CP reset to low */ 18 ldr r0, =0x11000C60 19 ldr r1, [r0] 20 ldr r2, =0xFFFFFF0F 21 and r1, r1, r2 22 orr r1, r1, #0x10 23 str r1, [r0] 24 ldr r0, =0x11000C68 25 ldr r1, [r0] 26 ldr r2, =0xFFFFFFF3 27 and r1, r1, r2 28 orr r1, r1, #0x4 29 str r1, [r0] 30 ldr r0, =0x11000C64 31 ldr r1, [r0] 32 ldr r2, =0xFFFFFFFD 33 and r1, r1, r2 34 str r1, [r0] 35 36 /* led (GPM4_0~3) on */ 37 ldr r0, =0x110002E0 38 ldr r1, =0x00001111 39 str r1, [r0] 40 ldr r1, =0x0e 41 str r1, [r0, #0x04] 42 43 /* During sleep/wakeup or AFTR mode, pmic_init function is not available 44 * and it causes delays. So except for sleep/wakeup and AFTR mode, 45 * the below function is needed 46 */ 47 #if defined(CONFIG_HAS_PMIC) 48 bl pmic_init 49 #endif 50 51 #if defined(CONFIG_ONENAND) 52 bl onenandcon_init 53 #endif 54 55 #if defined(NAND_BOOTING) 56 bl nand_asm_init 57 #endif 58 59 bl read_om 60 61 /* when we already run in ram, we don't need to relocate U-Boot. 62 * and actually, memory controller must be configured before U-Boot 63 * is running in ram. 64 */ 65 ldr r0, =0xff000fff 66 bic r1, pc, r0 /* r0 <- current base addr of code */ 67 ldr r2, _TEXT_BASE /* r1 <- original base addr in ram */ 68 bic r2, r2, r0 /* r0 <- current base addr of code */ 69 cmp r1, r2 /* compare r0, r1 */ 70 beq after_copy /* r0 == r1 then skip sdram init and u-boot.bin loading */ 71 72 #ifndef CONFIG_SMDKC220 73 ldr r0, =CHIP_ID_BASE 74 ldr r1, [r0] 75 lsr r1, r1, #8 76 and r1, r1, #3 77 cmp r1, #2 78 bne v310_1 79 #endif 80 81 /* init system clock */ 82 bl system_clock_init 83 84 /* Memory initialize */ 85 bl mem_ctrl_asm_init 86 87 /* init uart for debug */ 88 bl uart_asm_init 89 90 #if CONFIG_LL_DEBUG 91 mov r4, #0x4000
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 结构)
1 void board_init_f(ulong bootflag) 2 { 3 bd_t *bd; 4 init_fnc_t **init_fnc_ptr; 5 gd_t *id; 6 ulong addr, addr_sp; 7 8 /* Pointer is writable since we allocated a register for it 计算全局变量数据结构的地址,保存在gd中*/ 9 gd = (gd_t *) ((CONFIG_SYS_INIT_SP_ADDR) & ~0x07); //8字节对齐,为gd_t的空间。10 11 /* compiler optimization barrier needed for GCC >= 3.4 */ 12 __asm__ __volatile__("": : :"memory"); //空函数,告诉系统要写一个内存,防止优化13 14 memset((void*)gd, 0, sizeof (gd_t)); 15 16 gd->mon_len = _bss_end_ofs; 17 18 for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) { // 逐个调用init_sequence数组的初始化函数 19 if ((*init_fnc_ptr)() != 0) { 20 hang(); 21 } 22 } 23 24 debug ("monitor len: %08lX\n", gd->mon_len); 25 /* 26 * Ram is setup, size stored in gd !! 27 */ 28 debug ("ramsize: %08lX\n", gd->ram_size); 29 #if defined(CONFIG_SYS_MEM_TOP_HIDE) 30 /* 31 * Subtract specified amount of memory to hide so that it won't 32 * get "touched" at all by U-Boot. By fixing up gd->ram_size 33 * the Linux kernel should now get passed the now "corrected" 34 * memory size and won't touch it either. This should work 35 * for arch/ppc and arch/powerpc. Only Linux board ports in 36 * arch/powerpc with bootwrapper support, that recalculate the 37 * memory size from the SDRAM controller setup will have to 38 * get fixed. 39 */ 40 gd->ram_size -= CONFIG_SYS_MEM_TOP_HIDE; 41 #endif 42 43 addr = CONFIG_SYS_SDRAM_BASE + gd->ram_size; 44 45 #ifdef CONFIG_LOGBUFFER 46 #ifndef CONFIG_ALT_LB_ADDR 47 /* reserve kernel log buffer */ 48 addr -= (LOGBUFF_RESERVE); 49 debug ("Reserving %dk for kernel logbuffer at %08lx\n", LOGBUFF_LEN, addr); 50 #endif 51 #endif 52 53 #ifdef CONFIG_PRAM 54 /* 55 * reserve protected RAM 56 */ 57 i = getenv_r("pram", (char *)tmp, sizeof (tmp)); 58 reg = (i > 0) ? simple_strtoul((const char *)tmp, NULL, 10) : CONFIG_PRAM; 59 addr -= (reg << 10); /* size is in kB */ 60 debug ("Reserving %ldk for protected RAM at %08lx\n", reg, addr); 61 #endif /* CONFIG_PRAM */ 62 63 #if !(defined(CONFIG_SYS_NO_ICACHE) && defined(CONFIG_SYS_NO_DCACHE)) 64 /* reserve TLB table */ 65 addr -= (4096 * 4); 66 67 /* round down to next 64 kB limit */ 68 addr &= ~(0x10000 - 1); 69 70 gd->tlb_addr = addr; 71 debug ("TLB table at: %08lx\n", addr); 72 #endif 73 74 /* round down to next 4 kB limit */ 75 addr &= ~(4096 - 1); 76 debug ("Top of RAM usable for U-Boot at: %08lx\n", addr); 77 78 #ifdef CONFIG_VFD 79 # ifndef PAGE_SIZE 80 # define PAGE_SIZE 4096 81 # endif 82 /* 83 * reserve memory for VFD display (always full pages) 84 */ 85 addr -= vfd_setmem(addr); 86 gd->fb_base = addr; 87 #endif /* CONFIG_VFD */ 88 89 #ifdef CONFIG_LCD 90 /* reserve memory for LCD display (always full pages) */ 91 addr = lcd_setmem(addr); 92 gd->fb_base = addr; 93 #endif /* CONFIG_LCD */ 94 95 /* 96 * reserve memory for U-Boot code, data & bss 97 * round down to next 4 kB limit 98 */ 99 addr -= gd->mon_len; 100 addr &= ~(4096 - 1); 101 102 #if defined(CONFIG_S5P) || defined(CONFIG_S5P6450) 103 addr = CONFIG_SYS_LOAD_ADDR; 104 #endif 105 106 debug ("Reserving %ldk for U-Boot at: %08lx\n", gd->mon_len >> 10, addr); 107 108 #ifndef CONFIG_PRELOADER 109 /* 110 * reserve memory for malloc() arena 111 */ 112 addr_sp = addr - TOTAL_MALLOC_LEN; 113 debug ("Reserving %dk for malloc() at: %08lx\n", 114 TOTAL_MALLOC_LEN >> 10, addr_sp); 115 /* 116 * (permanently) allocate a Board Info struct 117 * and a permanent copy of the "global" data 118 */ 119 addr_sp -= sizeof (bd_t); 120 bd = (bd_t *) addr_sp; 121 gd->bd = bd; 122 debug ("Reserving %zu Bytes for Board Info at: %08lx\n", 123 sizeof (bd_t), addr_sp); 124 addr_sp -= sizeof (gd_t); 125 id = (gd_t *) addr_sp; 126 debug ("Reserving %zu Bytes for Global Data at: %08lx\n", 127 sizeof (gd_t), addr_sp); 128 129 /* setup stackpointer for exeptions */ 130 gd->irq_sp = addr_sp; 131 #ifdef CONFIG_USE_IRQ 132 addr_sp -= (CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ); 133 debug ("Reserving %zu Bytes for IRQ stack at: %08lx\n", 134 CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ, addr_sp); 135 #endif 136 137 /* leave 3 words for abort-stack */ 138 addr_sp -= 3; 139 140 /* 8-byte alignment for ABI compliance */ 141 addr_sp &= ~0x07; 142 #else 143 addr_sp += 128; /* leave 32 words for abort-stack */ 144 gd->irq_sp = addr_sp; 145 #endif 146 147 debug ("New Stack Pointer is: %08lx\n", addr_sp); 148 149 #ifdef CONFIG_POST 150 post_bootmode_init(); 151 post_run(NULL, POST_ROM | post_bootmode_get(0)); 152 #endif 153 154 gd->bd->bi_baudrate = gd->baudrate; 155 /* Ram ist board specific, so move it to board code ... */ 156 dram_init_banksize(); 157 display_dram_config(); /* and display it */ 158 159 gd->relocaddr = addr; 160 gd->start_addr_sp = addr_sp; 161 gd->reloc_off = addr - _TEXT_BASE; 162 debug ("relocation Offset is: %08lx\n", gd->reloc_off); 163 memcpy(id, (void *)gd, sizeof (gd_t)); 164 165 relocate_code(addr_sp, id, addr); 166 /* NOTREACHED - relocate_code() does not return */ 167 } 168 169 #if !defined(CONFIG_SYS_NO_FLASH) 170 static char *failed = "*** failed ***\n"; 171 #endif
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 #if defined(CONFIG_BOARD_EARLY_INIT_F) 6 board_early_init_f, 7 #endif 8 timer_init, /* initialize timer */ 9 #ifdef CONFIG_FSL_ESDHC 10 get_clocks, 11 #endif 12 env_init, /* initialize environment */ 13 #if defined(CONFIG_S5P6450) && !defined(CONFIG_S5P6460_IP_TEST) 14 init_baudrate, /* initialze baudrate settings */ 15 serial_init, /* serial communications setup */ 16 #endif 17 console_init_f, /* stage 1 init of console */ 18 display_banner, /* say that we are here */ 19 #if defined(CONFIG_DISPLAY_CPUINFO) 20 print_cpuinfo, /* display cpu info (and speed) */ 21 #endif 22 #if defined(CONFIG_DISPLAY_BOARDINFO) 23 checkboard, /* display board info */ 24 #endif 25 #if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C) 26 init_func_i2c, 27 #endif 28 dram_init, /* configure available RAM banks */ 29 #if defined(CONFIG_CMD_PCI) || defined(CONFIG_PCI) 30 arm_pci_init, 31 #endif 32 NULL, 33 };
u-boot使用一个init_sequence数组来存储大多数开发板都要执行的初始化函数的函数指针
board_init_f函数在调用完初始化函数指针、填充完gd结构之后,调用了arch/arm/cpu/armv7/start.S中的relocate_code
1 .globl relocate_code 2 relocate_code: 3 mov r4, r0 /* save addr_sp */ 4 mov r5, r1 /* save addr of gd */ 5 mov r6, r2 /* save addr of destination */ 6 7 /* Set up the stack */ 8 stack_setup: 9 mov sp, r4 10 11 adr r0, _start 12 #if defined(CONFIG_S5PC110) && defined(CONFIG_EVT1) && !defined(CONFIG_FUSED) 13 sub r0, r0, #16 14 #endif 15 #ifndef CONFIG_PRELOADER 16 cmp r0, r6 17 beq clear_bss /* skip relocation */ 18 #endif 19 mov r1, r6 /* r1 <- scratch for copy_loop */ 20 ldr r2, _TEXT_BASE 21 ldr r3, _bss_start_ofs 22 add r2, r0, r3 /* r2 <- source end address */ 23 24 copy_loop: 25 ldmia r0!, {r9-r10} /* copy from source address [r0] */ 26 stmia r1!, {r9-r10} /* copy to target address [r1] */ 27 cmp r0, r2 /* until source end address [r2] */ 28 blo copy_loop 29 30 #ifndef CONFIG_PRELOADER 31 /* 32 * fix .rel.dyn relocations 33 */ 34 ldr r0, _TEXT_BASE /* r0 <- Text base */ 35 sub r9, r6, r0 /* r9 <- relocation offset */ 36 ldr r10, _dynsym_start_ofs /* r10 <- sym table ofs */ 37 add r10, r10, r0 /* r10 <- sym table in FLASH */ 38 ldr r2, _rel_dyn_start_ofs /* r2 <- rel dyn start ofs */ 39 add r2, r2, r0 /* r2 <- rel dyn start in FLASH */ 40 ldr r3, _rel_dyn_end_ofs /* r3 <- rel dyn end ofs */ 41 add r3, r3, r0 /* r3 <- rel dyn end in FLASH */ 42 fixloop: 43 ldr r0, [r2] /* r0 <- location to fix up, IN FLASH! */ 44 add r0, r0, r9 /* r0 <- location to fix up in RAM */ 45 ldr r1, [r2, #4] 46 and r7, r1, #0xff 47 cmp r7, #23 /* relative fixup? */ 48 beq fixrel 49 cmp r7, #2 /* absolute fixup? */ 50 beq fixabs 51 /* ignore unknown type of fixup */ 52 b fixnext 53 fixabs: 54 /* absolute fix: set location to (offset) symbol value */ 55 mov r1, r1, LSR #4 /* r1 <- symbol index in .dynsym */ 56 add r1, r10, r1 /* r1 <- address of symbol in table */ 57 ldr r1, [r1, #4] /* r1 <- symbol value */ 58 add r1, r1, r9 /* r1 <- relocated sym addr */ 59 b fixnext 60 fixrel: 61 /* relative fix: increase location by offset */ 62 ldr r1, [r0] 63 add r1, r1, r9 64 fixnext: 65 str r1, [r0] 66 add r2, r2, #8 /* each rel.dyn entry is 8 bytes */ 67 cmp r2, r3 68 blo fixloop 69 70 clear_bss: 71 ldr r0, _bss_start_ofs 72 ldr r1, _bss_end_ofs 73 ldr r3, _TEXT_BASE /* Text base */ 74 mov r4, r6 /* reloc addr */ 75 add r0, r0, r4 76 add r1, r1, r4 77 mov r2, #0x00000000 /* clear */ 78 79 clbss_l:str r2, [r0] /* clear loop... */ 80 add r0, r0, #4 81 cmp r0, r1 82 bne clbss_l 83 #endif /* #ifndef CONFIG_PRELOADER */ 84 85 /* 86 * We are done. Do not return, instead branch to second part of board 87 * initialization, now running from RAM. 88 */ 89 jump_2_ram: 90 ldr r0, _board_init_r_ofs 91 adr r1, _start 92 add lr, r0, r1 93 @ add lr, lr, r9 94 /* setup parameters for board_init_r */ 95 mov r0, r5 /* gd_t */ 96 mov r1, r6 /* dest_addr */ 97 /* jump to it ... */ 98 mov pc, lr 99 100 101 102 103 _board_init_r_ofs: 104 .word board_init_r - _start 105
搬移uboot.bin
转入borad_init_r
1 void board_init_r(gd_t *id, ulong dest_addr) 2 { 3 char *s; 4 bd_t *bd; 5 ulong malloc_start; 6 #if !defined(CONFIG_SYS_NO_FLASH) 7 ulong flash_size; 8 #endif 9 10 gd = id; 11 bd = gd->bd; 12 13 gd->flags |= GD_FLG_RELOC; /* tell others: relocation done */ 14 15 monitor_flash_len = _bss_start_ofs; 16 debug ("monitor flash len: %08lX\n", monitor_flash_len); 17 board_init(); /* Setup chipselects */ 18 19 #ifdef CONFIG_SERIAL_MULTI 20 //serial_initialize(); 21 #endif 22 23 debug ("Now running in RAM - U-Boot at: %08lx\n", dest_addr); 24 25 #ifdef CONFIG_LOGBUFFER 26 logbuff_init_ptrs(); 27 #endif 28 #ifdef CONFIG_POST 29 post_output_backlog(); 30 #endif 31 32 /* The Malloc area is immediately below the monitor copy in DRAM */ 33 malloc_start = dest_addr - TOTAL_MALLOC_LEN; 34 mem_malloc_init(malloc_start, TOTAL_MALLOC_LEN); 35 36 #if !defined(CONFIG_SYS_NO_FLASH) 37 puts("FLASH:\t"); 38 39 if ((flash_size = flash_init()) > 0) { 40 # ifdef CONFIG_SYS_FLASH_CHECKSUM 41 print_size(flash_size, ""); 42 /* 43 * Compute and print flash CRC if flashchecksum is set to 'y' 44 * 45 * NOTE: Maybe we should add some WATCHDOG_RESET()? XXX 46 */ 47 s = getenv("flashchecksum"); 48 if (s && (*s == 'y')) { 49 printf(" CRC: %08X", 50 crc32 (0, (const unsigned char *) CONFIG_SYS_FLASH_BASE, flash_size) 51 ); 52 } 53 putc('\n'); 54 # else /* !CONFIG_SYS_FLASH_CHECKSUM */ 55 print_size(flash_size, "\n"); 56 # endif /* CONFIG_SYS_FLASH_CHECKSUM */ 57 } else { 58 puts(failed); 59 hang(); 60 } 61 #endif 62 63 #if defined(CONFIG_CMD_NAND) 64 puts("NAND:\t"); 65 nand_init(); /* go init the NAND */ 66 #endif 67 68 #if defined(CONFIG_CMD_ONENAND) 69 onenand_init(); 70 #endif 71 72 #ifdef CONFIG_GENERIC_MMC 73 mmc_initialize(bd); 74 #endif 75 76 #ifdef CONFIG_HAS_DATAFLASH 77 AT91F_DataflashInit(); 78 dataflash_print_info(); 79 #endif 80 81 /* initialize environment */ 82 env_relocate(); 83 84 #ifdef CONFIG_VFD 85 /* must do this after the framebuffer is allocated */ 86 drv_vfd_init(); 87 #endif /* CONFIG_VFD */ 88 89 /* IP Address */ 90 gd->bd->bi_ip_addr = getenv_IPaddr("ipaddr"); 91 92 stdio_init(); /* get the devices list going. */ 93 94 jumptable_init(); 95 96 #if defined(CONFIG_API) 97 /* Initialize API */ 98 api_init(); 99 #endif 100 101 //console_init_r(); /* fully init console as a device */ 102 103 #if defined(CONFIG_ARCH_MISC_INIT) 104 /* miscellaneous arch dependent initialisations */ 105 arch_misc_init(); 106 #endif 107 #if defined(CONFIG_MISC_INIT_R) 108 /* miscellaneous platform dependent initialisations */ 109 misc_init_r(); 110 #endif 111 112 /* set up exceptions */ 113 interrupt_init(); 114 /* enable exceptions */ 115 enable_interrupts(); 116 117 /* Perform network card initialisation if necessary */ 118 #if defined(CONFIG_DRIVER_SMC91111) || defined(CONFIG_DRIVER_LAN91C96) 119 /* XXX: this needs to be moved to board init */ 120 if (getenv("ethaddr")) { 121 uchar enetaddr[6]; 122 eth_getenv_enetaddr("ethaddr", enetaddr); 123 smc_set_mac_addr(enetaddr); 124 } 125 #endif /* CONFIG_DRIVER_SMC91111 || CONFIG_DRIVER_LAN91C96 */ 126 127 #if defined(CONFIG_DRIVER_DM9000) 128 /* XXX: this needs to be moved to board init */ 129 if (getenv("ethaddr")) { 130 uchar enetaddr[6]; 131 eth_getenv_enetaddr("ethaddr", enetaddr); 132 dm9000_set_mac_addr(enetaddr); 133 } 134 #endif 135 136 /* Initialize from environment */ 137 if ((s = getenv("loadaddr")) != NULL) { 138 load_addr = simple_strtoul(s, NULL, 16); 139 } 140 #if defined(CONFIG_CMD_NET) 141 if ((s = getenv("bootfile")) != NULL) { 142 copy_filename(BootFile, s, sizeof (BootFile)); 143 } 144 #endif 145 146 #ifdef BOARD_LATE_INIT 147 board_late_init(); 148 #endif 149 150 #ifdef CONFIG_BITBANGMII 151 bb_miiphy_init(); 152 #endif 153 #if defined(CONFIG_CMD_NET) 154 #if defined(CONFIG_NET_MULTI) 155 puts("Net:\t"); 156 #endif 157 eth_initialize(gd->bd); 158 #if defined(CONFIG_RESET_PHY_R) 159 debug ("Reset Ethernet PHY\n"); 160 reset_phy(); 161 #endif 162 #endif 163 164 #ifdef CONFIG_POST 165 post_run(NULL, POST_RAM | post_bootmode_get(0)); 166 #endif 167 168 #if defined(CONFIG_PRAM) || defined(CONFIG_LOGBUFFER) 169 /* 170 * Export available size of memory for Linux, 171 * taking into account the protected RAM at top of memory 172 */ 173 { 174 ulong pram; 175 uchar memsz[32]; 176 #ifdef CONFIG_PRAM 177 char *s; 178 179 if ((s = getenv("pram")) != NULL) { 180 pram = simple_strtoul(s, NULL, 10); 181 } else { 182 pram = CONFIG_PRAM; 183 } 184 #else 185 pram=0; 186 #endif 187 #ifdef CONFIG_LOGBUFFER 188 #ifndef CONFIG_ALT_LB_ADDR 189 /* Also take the logbuffer into account (pram is in kB) */ 190 pram += (LOGBUFF_LEN+LOGBUFF_OVERHEAD)/1024; 191 #endif 192 #endif 193 sprintf((char *)memsz, "%ldk", (bd->bi_memsize / 1024) - pram); 194 setenv("mem", (char *)memsz); 195 } 196 #endif 197 198 /* main_loop() can return to retry autoboot, if so just run it again. */ 199 for (;;) { 200 main_loop(); 201 } 202 203 /* NOTREACHED - no way out of command loop except booting */ 204 }
最后,转入mian_loop
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 #ifdef CONFIG_BOOTCOUNT_LIMIT 36 bootcount = bootcount_load(); 37 bootcount++; 38 bootcount_store (bootcount); 39 sprintf (bcs_set, "%lu", bootcount); 40 setenv ("bootcount", bcs_set); 41 bcs = getenv ("bootlimit"); 42 bootlimit = bcs ? simple_strtoul (bcs, NULL, 10) : 0; 43 #endif /* CONFIG_BOOTCOUNT_LIMIT */ 44 45 #ifdef CONFIG_MODEM_SUPPORT 46 debug ("DEBUG: main_loop: do_mdm_init=%d\n", do_mdm_init); 47 if (do_mdm_init) { 48 char *str = strdup(getenv("mdm_cmd")); 49 setenv ("preboot", str); /* set or delete definition */ 50 if (str != NULL) 51 free (str); 52 mdm_init(); /* wait for modem connection */ 53 } 54 #endif /* CONFIG_MODEM_SUPPORT */ 55 56 #ifdef CONFIG_VERSION_VARIABLE 57 { 58 extern char version_string[]; 59 60 setenv ("ver", version_string); /* set version variable */ 61 } 62 #endif /* CONFIG_VERSION_VARIABLE */ 63 64 #ifdef CONFIG_SYS_HUSH_PARSER 65 u_boot_hush_start (); 66 #endif 67 68 #if defined(CONFIG_HUSH_INIT_VAR) 69 hush_init_var (); 70 #endif 71 72 #ifdef CONFIG_AUTO_COMPLETE 73 install_auto_complete(); 74 #endif 75 76 #ifdef CONFIG_PREBOOT 77 if ((p = getenv ("preboot")) != NULL) { 78 # ifdef CONFIG_AUTOBOOT_KEYED 79 int prev = disable_ctrlc(1); /* disable Control C checking */ 80 # endif 81 82 # ifndef CONFIG_SYS_HUSH_PARSER 83 run_command (p, 0); 84 # else 85 parse_string_outer(p, FLAG_PARSE_SEMICOLON | 86 FLAG_EXIT_FROM_LOOP); 87 # endif 88 89 # ifdef CONFIG_AUTOBOOT_KEYED 90 disable_ctrlc(prev); /* restore Control C checking */ 91 # endif 92 } 93 #endif /* CONFIG_PREBOOT */ 94 95 #if defined(CONFIG_UPDATE_TFTP) 96 update_tftp (); 97 #endif /* CONFIG_UPDATE_TFTP */ 98 99 #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) 100 s = getenv ("bootdelay"); 101 bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_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"); 124 125 debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>"); 126 127 if (bootdelay >= 0 && s && !abortboot (bootdelay)) { 128 # ifdef CONFIG_AUTOBOOT_KEYED 129 int prev = disable_ctrlc(1); /* disable Control C checking */ 130 # endif 131 132 # ifndef CONFIG_SYS_HUSH_PARSER 133 run_command (s, 0); 134 # else 135 parse_string_outer(s, FLAG_PARSE_SEMICOLON | 136 FLAG_EXIT_FROM_LOOP); 137 # endif 138 139 # ifdef CONFIG_AUTOBOOT_KEYED 140 disable_ctrlc(prev); /* restore Control C checking */ 141 # endif 142 } 143 144 # ifdef CONFIG_MENUKEY 145 if (menukey == CONFIG_MENUKEY) { 146 s = getenv("menucmd"); 147 if (s) { 148 # ifndef CONFIG_SYS_HUSH_PARSER 149 run_command (s, 0); 150 # else 151 parse_string_outer(s, FLAG_PARSE_SEMICOLON | 152 FLAG_EXIT_FROM_LOOP); 153 # endif 154 } 155 } 156 #endif /* CONFIG_MENUKEY */ 157 #endif /* CONFIG_BOOTDELAY */ 158 159 /* 160 * Main Loop for Monitor Command Processing 161 */ 162 #ifdef CONFIG_SYS_HUSH_PARSER 163 parse_file_outer(); 164 /* This point is never reached */ 165 for (;;); 166 #else 167 for (;;) { 168 #ifdef CONFIG_BOOT_RETRY_TIME 169 if (rc >= 0) { 170 /* Saw enough of a valid command to 171 * restart the timeout. 172 */ 173 reset_cmd_timeout(); 174 } 175 #endif 176 len = readline (CONFIG_SYS_PROMPT); 177 178 flag = 0; /* assume no special flags for now */ 179 if (len > 0) 180 strcpy (lastcommand, console_buffer); 181 else if (len == 0) 182 flag |= CMD_FLAG_REPEAT; 183 #ifdef CONFIG_BOOT_RETRY_TIME 184 else if (len == -2) { 185 /* -2 means timed out, retry autoboot 186 */ 187 puts ("\nTimed out waiting for command\n"); 188 # ifdef CONFIG_RESET_TO_RETRY 189 /* Reinit board to run initialization code again */ 190 do_reset (NULL, 0, 0, NULL); 191 # else 192 return; /* retry autoboot */ 193 # endif 194 } 195 #endif 196 197 if (len == -1) 198 puts ("<INTERRUPT>\n"); 199 else 200 rc = run_command (lastcommand, flag); 201 202 if (rc <= 0) { 203 /* invalid command or not repeatable, forget it */ 204 lastcommand[0] = 0; 205 } 206 } 207 #endif /*CONFIG_SYS_HUSH_PARSER*/ 208 } 209 210 #ifdef CONFIG_BOOT_RETRY_TIME 211 /*************************************************************************** 212 * initialize command line timeout 213 */ 214 void init_cmd_timeout(void) 215 { 216 char *s = getenv ("bootretry"); 217 218 if (s != NULL) 219 retry_time = (int)simple_strtol(s, NULL, 10); 220 else 221 retry_time = CONFIG_BOOT_RETRY_TIME; 222 223 if (retry_time >= 0 && retry_time < CONFIG_BOOT_RETRY_MIN) 224 retry_time = CONFIG_BOOT_RETRY_MIN; 225 }
其中,主要是
#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相当于声明一个函数,如果该函数在其他地方没有定义,则为空函数,有定义则调用该定义的函数。