感谢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 code13 add r0, r0, #4 @ skip reset vector14 mov r2, #64 @ r2 <- size to copy15 add r2, r0, r2 @ r2 <- source end address16 mov r1, #SRAM_OFFSET0 @ build vect addr17 mov r3, #SRAM_OFFSET118 add r1, r1, r319 mov r3, #SRAM_OFFSET220 add r1, r1, r321 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 done28 * in NAND/oneNAND Boot.29 */30 bl cpy_clk_code @ put dpll adjust code behind vectors31 #endif /* NAND Boot */32 #endif33 /* the mask ROM code should have PLL and others stable */34 #ifndef CONFIG_SKIP_LOWLEVEL_INIT35 bl cpu_init_crit36 #endif37 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,=0x0000000043 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 TLBs10 mcr p15, 0, r0, c7, c5, 0 @ invalidate icache11 12 /*关闭MMU和caches13 * disable MMU stuff and caches14 */15 mrc p15, 0, r0, c1, c0, 016 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-) Align19 orr r0, r0, #0x00000800 @ set bit 12 (Z---) BTB20 mcr p15, 0, r0, c1, c0, 021 22 /*23 * Jump to board specific initialization...24 * The Mask ROM will have already initialized25 * basic memory. Go here to bump up clock rate and handle26 * wake up conditions.27 */28 mov ip, lr @ persevere link reg across call29 bl lowlevel_init @ go setup pll,mux,memory30 mov lr, ip @ restore link31 mov pc, lr @ back to my caller
cache_init ,被定义在 board/samsung/tiny4412/lowlevel_init.S 文件中
.globl cache_initcache_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_SLEEP14 cmp r1, r215 beq wakeup_reset16 17 /* set CP reset to low */18 ldr r0, =0x11000C6019 ldr r1, [r0]20 ldr r2, =0xFFFFFF0F21 and r1, r1, r222 orr r1, r1, #0x1023 str r1, [r0]24 ldr r0, =0x11000C6825 ldr r1, [r0]26 ldr r2, =0xFFFFFFF327 and r1, r1, r228 orr r1, r1, #0x429 str r1, [r0]30 ldr r0, =0x11000C6431 ldr r1, [r0]32 ldr r2, =0xFFFFFFFD33 and r1, r1, r234 str r1, [r0]35 36 /* led (GPM4_0~3) on */37 ldr r0, =0x110002E038 ldr r1, =0x0000111139 str r1, [r0]40 ldr r1, =0x0e41 str r1, [r0, #0x04]42 43 /* During sleep/wakeup or AFTR mode, pmic_init function is not available44 * and it causes delays. So except for sleep/wakeup and AFTR mode,45 * the below function is needed46 */47 #if defined(CONFIG_HAS_PMIC)48 bl pmic_init49 #endif50 51 #if defined(CONFIG_ONENAND)52 bl onenandcon_init53 #endif54 55 #if defined(NAND_BOOTING)56 bl nand_asm_init57 #endif58 59 bl read_om60 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-Boot63 * is running in ram.64 */65 ldr r0, =0xff000fff66 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_SMDKC22073 ldr r0, =CHIP_ID_BASE74 ldr r1, [r0]75 lsr r1, r1, #876 and r1, r1, #377 cmp r1, #278 bne v310_179 #endif80 81 /* init system clock */82 bl system_clock_init83 84 /* Memory initialize */85 bl mem_ctrl_asm_init86 87 /* init uart for debug */88 bl uart_asm_init89 90 #if CONFIG_LL_DEBUG91 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
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 #endif105 106 debug ("Reserving %ldk for U-Boot at: %08lx\n", gd->mon_len >> 10, addr);107 108 #ifndef CONFIG_PRELOADER109 /*110 * reserve memory for malloc() arena111 */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 struct117 * and a permanent copy of the "global" data118 */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_IRQ132 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 #endif136 137 /* leave 3 words for abort-stack */138 addr_sp -= 3;139 140 /* 8-byte alignment for ABI compliance */141 addr_sp &= ~0x07;142 #else143 addr_sp += 128; /* leave 32 words for abort-stack */144 gd->irq_sp = addr_sp;145 #endif146 147 debug ("New Stack Pointer is: %08lx\n", addr_sp);148 149 #ifdef CONFIG_POST150 post_bootmode_init();151 post_run(NULL, POST_ROM | post_bootmode_get(0));152 #endif153 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_ESDHC10 get_clocks,11 #endif12 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 #endif17 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 #endif22 #if defined(CONFIG_DISPLAY_BOARDINFO)23 checkboard, /* display board info */24 #endif25 #if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)26 init_func_i2c,27 #endif28 dram_init, /* configure available RAM banks */29 #if defined(CONFIG_CMD_PCI) || defined(CONFIG_PCI)30 arm_pci_init,31 #endif32 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 - _start105
搬移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 #endif100 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 #endif107 #if defined(CONFIG_MISC_INIT_R)108 /* miscellaneous platform dependent initialisations */109 misc_init_r();110 #endif111 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 #endif135 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 #endif145 146 #ifdef BOARD_LATE_INIT147 board_late_init();148 #endif149 150 #ifdef CONFIG_BITBANGMII151 bb_miiphy_init();152 #endif153 #if defined(CONFIG_CMD_NET)154 #if defined(CONFIG_NET_MULTI)155 puts("Net:\t");156 #endif157 eth_initialize(gd->bd);158 #if defined(CONFIG_RESET_PHY_R)159 debug ("Reset Ethernet PHY\n");160 reset_phy();161 #endif162 #endif163 164 #ifdef CONFIG_POST165 post_run(NULL, POST_RAM | post_bootmode_get(0));166 #endif167 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 memory172 */173 {174 ulong pram;175 uchar memsz[32];176 #ifdef CONFIG_PRAM177 char *s;178 179 if ((s = getenv("pram")) != NULL) {180 pram = simple_strtoul(s, NULL, 10);181 } else {182 pram = CONFIG_PRAM;183 }184 #else185 pram=0;186 #endif187 #ifdef CONFIG_LOGBUFFER188 #ifndef CONFIG_ALT_LB_ADDR189 /* Also take the logbuffer into account (pram is in kB) */190 pram += (LOGBUFF_LEN+LOGBUFF_OVERHEAD)/1024;191 #endif192 #endif193 sprintf((char *)memsz, "%ldk", (bd->bi_memsize / 1024) - pram);194 setenv("mem", (char *)memsz);195 }196 #endif197 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_TIME106 init_cmd_timeout ();107 # endif /* CONFIG_BOOT_RETRY_TIME */108 109 #ifdef CONFIG_POST110 if (gd->flags & GD_FLG_POSTFAIL) {111 s = getenv("failbootcmd");112 }113 else114 #endif /* CONFIG_POST */115 #ifdef CONFIG_BOOTCOUNT_LIMIT116 if (bootlimit && (bootcount > bootlimit)) {117 printf ("Warning: Bootlimit (%u) exceeded. Using altbootcmd.\n",118 (unsigned)bootlimit);119 s = getenv ("altbootcmd");120 }121 else122 #endif /* CONFIG_BOOTCOUNT_LIMIT */123 s = getenv ("bootcmd");124 125 debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "");126 127 if (bootdelay >= 0 && s && !abortboot (bootdelay)) {128 # ifdef CONFIG_AUTOBOOT_KEYED129 int prev = disable_ctrlc(1); /* disable Control C checking */130 # endif131 132 # ifndef CONFIG_SYS_HUSH_PARSER133 run_command (s, 0);134 # else135 parse_string_outer(s, FLAG_PARSE_SEMICOLON |136 FLAG_EXIT_FROM_LOOP);137 # endif138 139 # ifdef CONFIG_AUTOBOOT_KEYED140 disable_ctrlc(prev); /* restore Control C checking */141 # endif142 }143 144 # ifdef CONFIG_MENUKEY145 if (menukey == CONFIG_MENUKEY) {146 s = getenv("menucmd");147 if (s) {148 # ifndef CONFIG_SYS_HUSH_PARSER149 run_command (s, 0);150 # else151 parse_string_outer(s, FLAG_PARSE_SEMICOLON |152 FLAG_EXIT_FROM_LOOP);153 # endif154 }155 }156 #endif /* CONFIG_MENUKEY */157 #endif /* CONFIG_BOOTDELAY */158 159 /*160 * Main Loop for Monitor Command Processing161 */162 #ifdef CONFIG_SYS_HUSH_PARSER163 parse_file_outer();164 /* This point is never reached */165 for (;;);166 #else167 for (;;) {168 #ifdef CONFIG_BOOT_RETRY_TIME169 if (rc >= 0) {170 /* Saw enough of a valid command to171 * restart the timeout.172 */173 reset_cmd_timeout();174 }175 #endif176 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_TIME184 else if (len == -2) {185 /* -2 means timed out, retry autoboot186 */187 puts ("\nTimed out waiting for command\n");188 # ifdef CONFIG_RESET_TO_RETRY189 /* Reinit board to run initialization code again */190 do_reset (NULL, 0, 0, NULL);191 # else192 return; /* retry autoboot */193 # endif194 }195 #endif196 197 if (len == -1)198 puts (" \n");199 else200 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_TIME211 /***************************************************************************212 * initialize command line timeout213 */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 else221 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 : "");
在/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 ("\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()来处理的.
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#include 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相当于声明一个函数,如果该函数在其他地方没有定义,则为空函数,有定义则调用该定义的函数。