博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
4412 uboot启动分析
阅读量:5235 次
发布时间:2019-06-14

本文共 38890 字,大约阅读时间需要 129 分钟。

感谢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 }
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处

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
reset

   可看出,首先将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
cpu_init_crit

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

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

  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
relocate_code

   搬移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 }
borad_init_r

 

  最后,转入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 }
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 : "
");

  在/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()来处理的. 

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 
#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相当于声明一个函数,如果该函数在其他地方没有定义,则为空函数,有定义则调用该定义的函数。

转载于:https://www.cnblogs.com/chu-yi/p/10200327.html

你可能感兴趣的文章
如何在vue单页应用中使用百度地图
查看>>
Springboot使用步骤
查看>>
Spring属性注入
查看>>
Springboot-配置文件
查看>>
Springboot-日志框架
查看>>
SpringBoot-thymeleaf
查看>>
P1908-逆序对
查看>>
P1192-台阶问题
查看>>
一、使用pip安装Python包
查看>>
spring与quartz整合
查看>>
Kattis之旅——Eight Queens
查看>>
3.PHP 教程_PHP 语法
查看>>
Duilib扩展《01》— 双击、右键消息扩展
查看>>
利用Fiddler拦截接口请求并篡改数据
查看>>
python习题:unittest参数化-数据从文件或excel中读取
查看>>
Android控件之GridView探究
查看>>
在工程中要加入新的错误弹出方法
查看>>
PS 滤镜— — sparkle 效果
查看>>
snmpwalk命令常用方法总结
查看>>
网站产品设计
查看>>