uboot2015.04代码执行流程
记录uboot源码的下载地址。http://ftp.denx.de/pub/u-boot/
其实记录这些东西,我当时的处境是那么多相同名字的文件,相同名字的函数,自己不知道对于i.MX6dl来说,到底执行的是哪个文件里的哪个函数。
所以这里涉及到特定文件、函数的时候,会标红。
说到当初分析代码执行流程,找了N久,找到了链接文件。当初又是打算分析Makefile,又是看编译的最后一步打印日志,闹的头都大了。找这些东西真的坑爹。
我就不知道到底有没有文档来明明白白的把这些问题说清楚。倒是带了很多的ReadMe文件,但是打开一看,你妹的看完这个文件我tm修仙成功了都。。。
/arch/arm/cpu/u-boot.lds还有一个是u-boot-spl.lds,我们没用SPL机制,所以不提他了
文件内容如下:
1 ENTRY(_start) 最开始的入口 2 SECTIONS 3 { 4 . = 0x00000000; 5 6 . = ALIGN(4); 7 .text : 8 { 9 *(.__image_copy_start) 需要拷贝的image文件,开始位置标志 10 *(.vectors) 中断向量表 11 CPUDIR/start.o (.text*) start.S函数的代码段,不含数据段 12 *(.text*) 13 } 14 15 16 CONFIG_ARMV7_SECURE_BASE相关的,略 17 18 . = ALIGN(4); 19 .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) } 20 21 . = ALIGN(4); 22 .data : { 23 *(.data*) 24 } 25 26 . = ALIGN(4); 27 28 . = .; 29 30 . = ALIGN(4); 31 .u_boot_list : { 32 KEEP(*(SORT(.u_boot_list*))); 33 } 34 35 . = ALIGN(4); 36 37 .image_copy_end : 38 { 39 *(.__image_copy_end) 需要拷贝的image文件,开始位置标志 40 不包括rel_dyn数据,和bss段的数据 41 } 42 43 .rel_dyn_start : 44 { 45 *(.__rel_dyn_start) 46 } 47 48 .rel.dyn : { 49 *(.rel*) 50 } 51 52 .rel_dyn_end : 53 { 54 *(.__rel_dyn_end) 55 } 56 57 .end : 58 { 59 *(.__end) 60 } 61 62 _image_binary_end = .; 63 64 65 /* bss段 66 * Compiler-generated __bss_start and __bss_end, see arch/arm/lib/bss.c 67 * __bss_base and __bss_limit are for linker only (overlay ordering) 68 */ 69 70 .bss_start __rel_dyn_start (OVERLAY) : { 71 KEEP(*(.__bss_start)); 72 __bss_base = .; 73 } 74 75 .bss __bss_base (OVERLAY) : { 76 *(.bss*) 77 . = ALIGN(4); 78 __bss_limit = .; 79 } 80 81 .bss_end __bss_limit (OVERLAY) : { 82 KEEP(*(.__bss_end)); 83 } 84 85 //其他数据 86 .dynsym _image_binary_end : { *(.dynsym) } 87 .dynbss : { *(.dynbss) } 88 .dynstr : { *(.dynstr*) } 89 .dynamic : { *(.dynamic*) } 90 .plt : { *(.plt*) } 91 .interp : { *(.interp*) } 92 .gnu.hash : { *(.gnu.hash) } 93 .gnu : { *(.gnu*) } 94 .ARM.exidx : { *(.ARM.exidx*) } 95 .gnu.linkonce.armexidx : { *(.gnu.linkonce.armexidx.*) } 96 } 97
最开始的入口是_start,他在vectors.S arch\arm\lib\vector.S中
他的作用就是中断向量表,第一个跳转位置为reset函数,其后的各个函数,是中断入口地址,不过uboot的初始化过程是不涉及中断的,这里先忽略他们。
__image_copy_start这个玩意儿就是个标号,表示需要拷贝的image文件的开始位置。
在SourceInsight里面搜索这个标号,发现定义在 arch\arm\lib\sections.c中。略
于是找到了入口点,i.mx6dl是armv7架构的,所以是arch\arm\cpu\armv7\start.S文件。
首先执行reset标号处的内容,代码的注释很详细:disable interrupts (FIQ and IRQ), also set the cpu to SVC32 mode
#ifndef CONFIG_SKIP_LOWLEVEL_INIT bl cpu_init_cp15 @[this two func] ALL in this file bl cpu_init_crit @here is lowlevel_init #endif bl _main @ arch\arm\lib\crt0.S
两个cpu_init都在当前文件中。不管他俩,直接看跳到_main里面做了什么。
另外这个
CONFIG_SKIP_LOWLEVEL_INIT
有没有定义呢,抡起Linux下的板斧,因为别的命令不会,只会grep * -nR -nwR和find -name哈哈哈
搜索【此前已经问飞凌开发板的客服知道了配置文件是include/configs/mx6sabre_common.h】
grep '#define CONFIG_SKIP_LOWLEVEL_INIT' include/configs/mx* -nR
单引号表示搜索的东东是一个整体。
发现没有在include/configs/mx6sabre_common.h当中定义。
下面到_main里面看看,目录在 arch\arm\lib\crt0.S
详细的内存分布,在这个函数里面开始形成,但这不是我的重点,我的重点就是管你怎么分布的呢,我又不写你源代码,我就想看看你代码是怎么个执行过程!
之后就跳转到board_init_f函数了
/* u-boot的基本策略,就是声明一系列的API(如low_level_init、board_init_f、board_init_r等等), 并在u-boot的核心逻辑中调用它们。 平台的移植和开发者,所需要做的,就是根据实际情况,实现它们。 与此同时,为了减少开发的工作量,u-boot为大部分API提供了通用实现 (一般通过CONFIG配置项或者若定义去控制是否编译)。 以board_init_f和board_init_r两个板级的初始化接口为例, u-boot分别在common/board_f.c和common/board_r.c两个文件中提供了通用实现。 查看common/Makefile可知: obj-$(CONFIG_SYS_GENERIC_BOARD) += board_f.o obj-$(CONFIG_SYS_GENERIC_BOARD) += board_r.o ./include/configs/mx6sabre_common.h 里定义了 CONFIG_SYS_GENERIC_BOARD */
这句话引自窝窝科技:uboot章节:http://www.wowotech.net/sort/u-boot
/* 别的不管,这里是一个初始化函数的数组,会一一调用他们来初始化开发板。 */ if (initcall_run_list(init_sequence_f)) hang();
这里的函数有没有执行,我都是一个个添加打印进去看他的输出日志有没有那句话来判断的,一个个的加,想想就感觉蛋碎!
( 打开debug输出日志的方法是,在 include/common.h 当中 加入 #define DEBUG )
//#define DEBUG #ifdef DEBUG #define _DEBUG 1 #else #define _DEBUG 0 #endif #define debug_cond(cond, fmt, args...) \ do { \ if (cond) \ printf(pr_fmt(fmt), ##args); \ } while (0) #define debug(fmt, args...) \ debug_cond(_DEBUG, fmt, ##args) /* 要想打印输出成功,只能定义DEBUG */
看一下这个数组里的东东:
static init_fnc_t init_sequence_f[] = { 前略 ... env_init, /* initialize environment 环境变量初始化开始 env_mmc.c*/ init_baud_rate, /* initialze baudrate settings */ serial_init, /* serial communications setup */ console_init_f, /* stage 1 init of console */ // 如果你想裁剪uboot的启动时间,这里的函数有一部分是可以去掉的。 // 初始化DDR dram_init, /* configure available RAM banks */ // 为代码重定位所做的工作,主要是操作内存的空间 }
之后跳转到代码重定位:
adr lr, here ldr r0, [r9, #GD_RELOC_OFF] /* r0 = gd->reloc_off 新旧地址的偏移长度*/ add lr, lr, r0 /* lr = relocate后,新的here地址 */ ldr r0, [r9, #GD_RELOCADDR] /* r0 = gd->relocaddr */ b relocate_code 代码重定位的函数所在文件: /* relocate.S (arch\arm\lib) */ here: /* * now relocate vectors */ bl relocate_vectors
这里对比说为什么新版的uboot代码的重定位搞的这么复杂,又是代码段、又是rel_dyn数据段的,为什么不像以前一样直接打包成一个文件,全部从flash拷贝的内存里去。
看了一篇文章里说,是为了把uboot定位到内存比较高的地址,因为将来内核是加载到内存低地址的。
二一个是,反正都要重定位,你管我怎么做呢?我Fxck,你厉害
代码重定位具体的实现就不说了,韦东山也讲过,很多其他博客也都有讲解。
之后就到了重定位之后的半段:
board_init_r函数,在common\board_r.c,他里面同样有一个初始化函数数组,init_sequence_r
同样,我不能确定这里的宏,哪个定义了与否,便不能确定这个数组里的函数被初始化调用了与否。
所以只能是一个个函数加上打印语句。这样也能根据函数执行的情况,知道当下的这个宏有没有被定义。
#ifdef CONFIG_GENERIC_MMC initr_mmc, //mmc preinit? #endif //undef CONFIG_HAS_DATAFLASH initr_env, /*环境变量重定位,初始化结束,第一次mmc_init*/ #ifdef CONFIG_BOARD_LATE_INIT board_late_init, //mmc_init 1次 //board_late_mmc_env_init => setenv_ulong => setenv => _do_env_set //board_late_mmc_env_init => run_command[mmc dev N] => do_mmc_dev(cmd_mmc.c) //=> init_mmc_device => mmc_init #endif run_main_loop, // 最终大BOSS!!! };
到这里,板子的MMC也初始化成功了,最后执行到run_main_loop不再返回。