u-boot-2011.09 am335x启动流程: 1.rom code(详见芯片手册) 2.spl(Secondary Program Loader) 根据spl/u-boot-spl.lds<===arch/arm/cpu/armv7/omap-common/u-boot-spl.lds: arch/arm/cpu/armv7/start.o (.text) _start: b reset bl save_boot_params://arch/arm/cpu/armv7/ti81xx/lowlevel_init.S #ifdef CONFIG_SPL_BUILD ldr r4, =ti81xx_boot_device ldr r5, [r0, #BOOT_DEVICE_OFFSET]//rom启动后,将一个BootParametersStructure的指针保存在r0,其中r0+8保存boot device(详见芯片手册) and r5, r5, #BOOT_DEVICE_MASK//取低八位 str r5, [r4]//将boot device保存在变量ti81xx_boot_device #endif bx lr #ifndef CONFIG_SKIP_LOWLEVEL_INIT bl cpu_init_crit//只有在spl中才执行,uboot中不执行 bl lowlevel_init//lowlevel_init.S,设置堆栈,跳转到c函数 bl s_init//evm.c l2_cache_enable();//cache.S,cp15 //关看门狗 pll_init();*** preloader_console_init();//spl.c //#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r8")//r8寄存器保存gd的地址 //static gd_t gdata __attribute__ ((section(".data"))); gd = &gdata;//spl阶段r8=gd=&gdata,位于sram (MLO的data段) config_am335x_ddr(); #endif bl board_init_f//spl.c /* * We call relocate_code() with relocation target same as the * CONFIG_SYS_SPL_TEXT_BASE. This will result in relocation getting * skipped. Instead, only .bss initialization will happen. That's * all we need //设置重定向地址为sram基地址,故意跳过relocation,只清bbs段 */ relocate_code(CONFIG_SPL_STACK, &gdata, CONFIG_SPL_TEXT_BASE); relocate_code://start.S mov r4, r0 /* save addr_sp */ //栈地址=CONFIG_SPL_STACK mov r5, r1 /* save addr of gd */ //gd地址=&gdata(位于data段) mov r6, r2 /* save addr of destination */ //重定位地址=CONFIG_SPL_TEXT_BASE /* Set up the stack */ stack_setup: mov sp, r4 /* adr伪指令相对于pc(运行时确定)。adr的反汇编就是基于PC的add sub指令 位置无关,用于获得_start的真正运行地址(而不是链接地址) */ adr r0, _start// adr伪指令相对于pc(运行时确定),当前PC位于sram,所以r0=CONFIG_SPL_TEXT_BASE cmp r0, r6 // r0=r6 说明在sram中运行 moveq r9, #0 /* no relocation. relocation offset(r9) = 0 */ //r9保存重定向地址的偏移地址,spl中不进行重定向,r9=0 beq clear_bss /* skip relocation */ //跳过relocation,以下代码在spl中没有执行。调用clear_bss /************ skip relocation in spl ***********/ mov r1, r6 /* r1 <- scratch for copy_loop*/ //r1=sram_start ldr r3, _image_copy_end_ofs //_image_copy_end_ofs=__image_copy_end - _start add r2, r0, r3 /* r2 <- source end address */ copy_loop: //在spl中,这段代码是将自己copy给自己。实际上在spl中这段代码没有被调用,因为clear_bss没有返回 ldmia r0!, {r9-r10} /* copy from source address [r0] */ stmia r1!, {r9-r10} /* copy to target address [r1] */ cmp r0, r2 /* until source end address [r2] */ blo copy_loop /************ skip relocation in spl ***********/ clear_bss: #ifdef CONFIG_SPL_BUILD /* No relocation for SPL */ ldr r0, =__bss_start /* *u-boot-spl.lds,sdram中的bss = CONFIG_SPL_BSS_START_ADDR = 0x80000000,即sdram起始地址 *CONFIG_SPL_BSS_MAX_SIZE = 0x80000,即MLO的BBS段位于sdram 0x80000000-0x80080000(512k), */ ldr r1, =__bss_end__ #endif mov r2, #0x00000000 /* clear */ clbss_l:str r2, [r0] /* clear loop... */ add r0, r0, #4 cmp r0, r1 bne clbss_l /* * We are done. Do not return, instead branch to second part of board * initialization, now running from RAM. */ jump_2_ram: /* * If I-cache is enabled invalidate it */ #ifndef CONFIG_SYS_ICACHE_OFF mcr p15, 0, r0, c7, c5, 0 @ invalidate icache mcr p15, 0, r0, c7, c10, 4 @ DSB mcr p15, 0, r0, c7, c5, 4 @ ISB #endif ldr r0, _board_init_r_ofs//r0=board_init_r - _start(board_init_r偏移地址) adr r1, _start //r1=_start的运行地址 add lr, r0, r1 //lr=board_init_r的真正运行地址 add lr, lr, r9 //r9保存重定向偏移大小,spl中 r9 = 0 /* setup parameters for board_init_r */ mov r0, r5 /* gd_t */ //r0 = &gdata mov r1, r6 /* dest_addr */ //r6=CONFIG_SPL_TEXT_BASE /* jump to it ... */ mov pc, lr //跳转到board_init_r _board_init_r_ofs: .word board_init_r - _start //确定board_init_r和_start差值。如果是spl,board_init_r位于spl.c。如果是uboot,board_init_r位于board.c void board_init_r(gd_t *id, ulong dummy)//spl.c,这段代码仍然在sram中运行 timer_init(); i2c_init spl_board_init(); boot_device = omap_boot_device() = ti81xx_boot_device; spl_nand_load_image(); { gpmc_init();**** nand_init();****//nand_spl_simple.c board_nand_init//ti81xx_nand.c /*use CONFIG_SYS_TEXT_BASE as temporary storage area */ header = (struct image_header *)(CONFIG_SYS_TEXT_BASE);//临时保存image_header到CONFIG_SYS_TEXT_BASE nand_spl_load_image(CONFIG_SYS_NAND_U_BOOT_OFFS,//#define CONFIG_SYS_NAND_U_BOOT_OFFS 0x80000//nand的第5块 CONFIG_SYS_NAND_PAGE_SIZE, (void *)header);//先读nand flash中uboot分区的第一页数据:image_header(64字节) spl_parse_image_header(header);//解析 image_header,结果保存在spl_image.其内容由mkimage的参数决定。 nand_spl_load_image(CONFIG_SYS_NAND_U_BOOT_OFFS,//再读整个uboot镜像到load_addr(sdram) spl_image.size, (void *)spl_image.load_addr);//load_addr=0x80800000(entry_point) - 64(image_header_size) = 807FFFC0 nand_deselect(); } jump_to_image_no_args(); { typedef void (*image_entry_noargs_t)(void)__attribute__ ((noreturn)); image_entry_noargs_t image_entry = (image_entry_noargs_t) spl_image.entry_point; // image_entry_noargs_t image_entry = // (image_entry_noargs_t) 0x80800000; image_entry(); /* 在反汇编中,无论是使用blx还是ldr,最终从sram跳转到sdram中运行。 当使用spl_image.entry_point(运行时确定,保存在bss段),反汇编使用ldr pc,[0x8000000c](位于bss)(u-boot-spl.map)。 当使用0x80800000时,反汇编使用blx 0x80800000,超过了32M,具体实现细节由连接器实现 */ } //至此,第二阶段spl结束。PC跳转到0x80800000(CONFIG_SYS_TEXT_BASE)执行u-boot 3.u-boot 根据u-boot.lds<===arch/arm/cpu/armv7/u-boot.lds: arch/arm/cpu/armv7/start.o (.text)//u-boot入口函数与spl相同,都是start.S中的_start _start: b reset bl save_boot_params: bx lr//直接返回 /* Set stackpointer in internal RAM to call board_init_f */ call_board_init_f: ldr sp, =(CONFIG_SYS_INIT_SP_ADDR) bic sp, sp, #7 /* 8-byte alignment for ABI compliance //8字节对齐*/ ldr r0,=0x00000000 bl board_init_f//board.c //#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r8")//r8寄存器保存gd的地址 /* Pointer is writable since we allocated a register for it */ //r8=gd=CONFIG_SYS_INIT_SP_ADDR(位于sram尾部128字节),并且8字节对齐。 gd = (gd_t *) ((CONFIG_SYS_INIT_SP_ADDR) & ~0x07); memset((void *)gd, 0, sizeof(gd_t));//说明uboot阶段的gd和spl没有关系 for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) { if ((*init_fnc_ptr)() != 0)//遍历执行init_sequence数组中的函数,在这些函数中给gd的成员赋值 { env_init, /* initialize environment */ } } /* gd->mon_len = _bss_end_ofs; 整个u-boot镜像的大小,从_start到_bss_end(包括bss,不包括image_header) 每次修改代码后编译都不同 */ gd->mon_len = _bss_end_ofs;//__bss_end__ - _start //dram_init:gd->ram_size = PHYS_DRAM_1_SIZE; //addr=CONFIG_SYS_SDRAM_BASE + PHYS_DRAM_1_SIZE=0xa0000000,sdram尾部 addr = CONFIG_SYS_SDRAM_BASE + gd->ram_size; #if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF)) /* reserve TLB table */ addr -= (4096 * 4);//addr=0xa0000000 - 16k = 0x9FFFC000 /* round down to next 64 kB limit */ addr &= ~(0x10000 - 1);//64k对齐,addr=0x9FFFC000 & ~ 0xFFFF=0x9FFF0000 gd->tlb_addr = addr; #endif addr &= ~(4096 - 1);// 4k对齐,addr=0x9FFF0000 /* * reserve memory for U-Boot code, data & bss * round down to next 4 kB limit */ addr -= gd->mon_len;//addr=0x9FFF0000-0x4B6D70 = 0x9FB39290 addr &= ~(4096 - 1);// 4k对齐 addr = 0x9FB39000,uboot重定向地址addr /* * reserve memory for malloc() arena */ addr_sp = addr - TOTAL_MALLOC_LEN;//addr_sp=0x9FB39000 - 1048k = 0x9FA33000 /* * (permanently) allocate a Board Info struct * and a permanent copy of the "global" data * 在sdram分配一个gd_t:id,用于拷贝gd(sram),最后在将id赋值给gd,之后的gd也指向0x9FA32F70 */ addr_sp -= sizeof (bd_t);//addr_sp=0x9FA33000 - 24 = 0x9FA32FE8 bd = (bd_t *) addr_sp;//bd = 0x9FA32FE8 gd->bd = bd; //sizeof (gd_t)=120 ?? 计算为116 addr_sp -= sizeof (gd_t);//addr_sp=0x9FA32FE8-120=0x9FA32F70 id = (gd_t *) addr_sp;//id=0x9FA32F70 /* leave 3 words for abort-stack */ addr_sp -= 12;//addr_sp=0x9FA32F70-12=0x9FA32F64 /* 8-byte alignment for ABI compliance */ addr_sp &= ~0x07;//add_sp=0x9FA32F60 gd->bd->bi_baudrate = gd->baudrate;//init_baudrate /* Ram ist board specific, so move it to board code ... */ dram_init_banksize(); display_dram_config(); /* and display it */ gd->relocaddr = addr;//addr=0x9FB39000,uboot重定向地址addr gd->start_addr_sp = addr_sp;//addr_sp=0x9FA32F60 gd->reloc_off = addr - _TEXT_BASE;//gd->reloc_off=0x9FB39000-CONFIG_SYS_TEXT_BASE=0x9FB39000-0x80800000=1F339000 memcpy(id, (void *)gd, sizeof(gd_t));//将gd(sram:CONFIG_SYS_INIT_SP_ADDR)内容付给id(sdram:0x9FA32F70) relocate_code(addr_sp, id, addr);//addr_sp=0x9FA32F60;id=0x9FA32F70;addr=0x9FB39000 relocate_code://start.S relocate_code: mov r4, r0 /* save addr_sp */ //栈地址=0x9FA32F60 mov r5, r1 /* save addr of gd */ //id地址=0x9FA32F70 mov r6, r2 /* save addr of destination */ //重定位地址=0x9FB39000 /* Set up the stack */ stack_setup: mov sp, r4 /* adr伪指令相对于pc(运行时确定)。adr的反汇编就是基于PC的add sub指令 位置无关,用于获得_start的真正运行地址(而不是链接地址) */ adr r0, _start //r0 = 0x80800000 cmp r0, r6 // r0!=r6 需要重定向 /**** 不执行 ****/ moveq r9, #0 /* no relocation. relocation offset(r9) = 0 */ beq clear_bss /* skip relocation */ /**** 不执行 ****/ /************ relocation ***********/ mov r1, r6 /* r1 <- scratch for copy_loop*/ //r1=0x9FB39000 重定向地址 ldr r3, _image_copy_end_ofs //_image_copy_end_ofs=__image_copy_end(u-boot.lds) - _start;表示需要copy的uboot镜像大小=0x64854(反汇编) add r2, r0, r3 /* r2 <- source end address */ //r2=0x80800000+0x64854=0x80864854 copy_loop: //将uboot(从_start到___image_copy_end)从地址r0到地址r2 copy到 地址r1 ldmia r0!, {r9-r10} /* copy from source address [r0] */ stmia r1!, {r9-r10} /* copy to target address [r1] */ cmp r0, r2 /* until source end address [r2] */ blo copy_loop /* ldr pc, _undefined_instruction//位置无关,_undefined_instruction基于PC计算 _undefined_instruction: .word undefined_instruction//位置相关,undefined_instruction编译期间确定 Disassembly of section .text: 80800020 <_undefined_instruction>: 80800020: 808001a0 .word 0x808001a0 808001a0 <undefined_instruction>: 808001a0: e51fd154 ldr sp, [pc, #-340] ; 80800054 <IRQ_STACK_START_IN> Disassembly of section .rel.dyn 808648dc: 80800020 addhi r0, r0, r0, lsr #32 修复代码重定向之后某些位置相关的代码不能执行问题。 uboot 在ld时,指定 -pie,生成的uboot会包含_rel_dyn段, _rel_dyn段(808648dc)的作用就是保存“保存函数(text)、常量(rodata)绝对地址(808001a0:undefined_instruction)”的地址(80800020) 下面的代码就是遍历重定位的uboot的_rel_dyn段(还有dynsym段), 将_rel_dyn段(808648dc)中的每一个地址(80800020)所指向的地址(808001a0)加上relocation offset 80800020: 808001a0 .word 0x808001a0 变为 80800020: 808001a0+offset .word 0x808001a0 */ #ifndef CONFIG_SPL_BUILD//uboot /* * fix .rel.dyn relocations */ ldr r0, _TEXT_BASE /* r0 <- Text base */ //r0=0x80800000 sub r9, r6, r0 /* r9 <- relocation offset */ //r9=0x9FB39000 - 0x80800000=1F339000 ldr r10, _dynsym_start_ofs /* r10 <- sym table ofs */ //r10=0x6d104(反汇编) add r10, r10, r0 /* r10 <- sym table in FLASH */ //r10=0x8086D104,_dynsym_start在sdram中的真实地址 ldr r2, _rel_dyn_start_ofs /* r2 <- rel dyn start ofs */ //r2=0x64854(反汇编) add r2, r2, r0 /* r2 <- rel dyn start in FLASH */ //r2=0x80864854,_rel_dyn_start在sdram中的真实地址 ldr r3, _rel_dyn_end_ofs /* r3 <- rel dyn end ofs */ //r3=0x6d104(反汇编) add r3, r3, r0 /* r3 <- rel dyn end in FLASH */ //r3=0x8086D104,_rel_dyn_end在sdram中的真实地址=_dynsym_start(u-boot.lds) fixloop: ldr r0, [r2] /* r0 <- location to fix up, IN FLASH! */ add r0, r0, r9 /* r0 <- location to fix up in RAM */ ldr r1, [r2, #4] and r7, r1, #0xff cmp r7, #23 /* relative fixup? */ beq fixrel cmp r7, #2 /* absolute fixup? */ beq fixabs /* ignore unknown type of fixup */ b fixnext fixabs: /* absolute fix: set location to (offset) symbol value */ mov r1, r1, LSR #4 /* r1 <- symbol index in .dynsym */ add r1, r10, r1 /* r1 <- address of symbol in table */ ldr r1, [r1, #4] /* r1 <- symbol value */ add r1, r1, r9 /* r1 <- relocated sym addr */ b fixnext fixrel: /* relative fix: increase location by offset */ ldr r1, [r0] add r1, r1, r9 fixnext: str r1, [r0] add r2, r2, #8 /* each rel.dyn entry is 8 bytes */ cmp r2, r3 blo fixloop b clear_bss _rel_dyn_start_ofs: .word __rel_dyn_start - _start _rel_dyn_end_ofs: .word __rel_dyn_end - _start _dynsym_start_ofs: .word __dynsym_start - _start #endif /* #ifndef CONFIG_SPL_BUILD */ clear_bss: ldr r0, _bss_start_ofs//r0=0x64854 ldr r1, _bss_end_ofs//r1=0x4b6d70 mov r4, r6 /* reloc addr */ //r4=0x9FB39000 (重定向地址) add r0, r0, r4 //r0=9FB9D854 (重定向后的bbs_start) add r1, r1, r4 //r1=9FFEFD70 (重定向后的bbs_end) mov r2, #0x00000000 /* clear */ clbss_l:str r2, [r0] /* clear loop... */ add r0, r0, #4 cmp r0, r1 bne clbss_l /* * We are done. Do not return, instead branch to second part of board * initialization, now running from RAM. */ jump_2_ram: ldr r0, _board_init_r_ofs//r0=0x01a04(反汇编) adr r1, _start //r1=0x80800000(adr基于pc的位置无关码,如果pc=当前链接地址,那么_start=_TEXT_BASE) add lr, r0, r1 //lr=0x01a04 + 0x80800000 = 80801A04 add lr, lr, r9 //(r9 保存着 relocation offset;spl中 r9 = 0;uboot中 r9=1F339000) //lr=80801A04+1F339000=1F33AA04(重定向后board_init_r的地址) /* setup parameters for board_init_r */ mov r0, r5 /* gd_t */ //r0 = id地址 =0x9FA32F70 mov r1, r6 /* dest_addr */ //r6=重定位地址=0x9FB39000 /* jump to it ... */ mov pc, lr //跳转到1F33AA04(重定向后board_init_r的地址) 绝对跳转 board_init_r//board.c { gd = id;//r8=gd=id=0x9FA32F70 bd = gd->bd;//bd=gd->bd=0x9FA32FE8 board_init();//evm.c { gpmc_init(); board_evm_init(); { gd->bd->bi_arch_number = MACH_TYPE_TIAM335EVM;//3589 /* address of boot parameters */ gd->bd->bi_boot_params = PHYS_DRAM_1 + 0x100;//0x80000100 } } nand_init();//nand.c { nand_init_chip { int board_nand_init(struct nand_chip *nand);//初始化nand_chip { ti81xx_nand_switch_ecc(NAND_ECC_HW, 2); { __ti81xx_nand_switch_ecc { nand->options |= NAND_OWN_BUFFERS; ti81xx_hwecc_init_bch(nand, NAND_ECC_READ); } nand_scan_tail(mtd); //初始化阶段,在这里调用的nand_scan_tail本质上没有作用,直接返回(因为在ti81xx_hwecc_init_bch设置了NAND_OWN_BUFFERS) nand->options &= ~NAND_OWN_BUFFERS; } } nand_scan { nand_scan_ident { nand_set_defaults chip->cmdfunc = nand_command;//这时还是小页的nand_command nand_get_flash_type { chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);//READID *maf_id = chip->read_byte(mtd);//1st MID *dev_id = chip->read_byte(mtd);//2nd PID nand_flash_detect_onfi//不支持onfi,直接返回0; { chip->cmdfunc(mtd, NAND_CMD_READID, 0x20, -1);//READID,ONFI read_byte //4个周期 O、N、F、I chip->cmdfunc(mtd, NAND_CMD_PARAM, 0, -1); chip->read_buf(mtd, (uint8_t *)p, sizeof(*p));//读nand_onfi_params结构体 } nand_flash_detect_non_onfi//readid 3rd/4th个周期 chip->erase_cmd = single_erase_cmd; if (mtd->writesize > 512 && chip->cmdfunc == nand_command) chip->cmdfunc = nand_command_lp;//改为大页的nand_command_lp } } nand_scan_tail } } } /* initialize environment */ env_relocate();//初始化环境变量 ... ... main_loop(); { #if//超时,启动内核 s = getenv ("bootcmd"); run_command (s, 0); #else//没有超时,进入命令行模式 run_command("menu", 0); for (;;) {//uboot命令行模式的死循环 len = readline (CONFIG_SYS_PROMPT);//读命令行字符串到console_buffer { return readline_into_buffer(prompt, console_buffer); } flag = 0; if (len > 0)//命令行敲入命令 { strcpy (lastcommand, console_buffer); if(strcmp(get_cmd,"00")==0 ... )////如果是自定义的菜单命令 { run_menu_command(get_cmd); run_command(load_cmd, 0); } }else if (len == 0)//直接回车,重复上个命令 { flag |= CMD_FLAG_REPEAT; } /* * returns: * 1 - command executed, repeatable * 0 - command executed but not repeatable, interrupted commands are * always considered not repeatable * -1 - not executed (unrecognized, bootd recursion or too many args) * (If cmd is NULL or "" or longer than CONFIG_SYS_CBSIZE-1 it is * considered unrecognized) */ rc = run_command (lastcommand, flag); { if (!cmd || !*cmd) { return -1; /* empty command */ } process_macros (token, finaltoken);//处理宏定义 { envval = getenv (envname); } /* Extract arguments */ argc = parse_line (finaltoken, argv);//解析参数 argv argc /* Look up command in command table */ cmdtp = find_cmd(argv[0]);//argv[0]=命令的名字 { int len = &__u_boot_cmd_end - &__u_boot_cmd_start; return find_cmd_tbl(cmd, &__u_boot_cmd_start, len); } (cmdtp->cmd) (cmdtp, flag, argc, argv);//执行函数 repeatable &= cmdtp->repeatable; //run_command执行没有错误rc=0,返回repeatable //run_command执行有错误rc=-1,返回rc=-1 return rc ? rc : repeatable; } /* 命令执行失败或者命令成功执行但不能重复执行,清除lastcommand; 下次循环如果直接回车,执行到run_command时直接返回-1; */ if (rc <= 0) { /* invalid command or not repeatable, forget it */ lastcommand[0] = 0; } else (rc == 1) /* 命令执行成功并且可重复执行, lastcommand保持为这次成功执行的可重复的命令; 下次循环如果直接回车,执行到run_command时和上次循环相同,实现了命令的重复执行; */ } } }