目录
_main函数详解
board_init_f函数详解
relocate_code函数详解
relocate_vectors函数详解
board_init_r 函数详解
_main函数详解
在上一章得知会执行_main函数_main函数定义在文件arch/arm/lib/crt0.S 中,函数内容如下:
第76行,设置sp指针为CONFIG_SYS_INIT_SP_ADDR,也就是sp指向0x0091FF00。
第83行,sp做8字节对齐。
第85行,读取sp到寄存器r0里面,此时r0-0x0091FF00
第86行,调用函数board_init_f_alloc_reserve,此函数有一个参数,参数为r0中的值,也就是0X0091FF00,此函数定义在文件common/init/board initc中,内容如下:
函数board_init_f alloc-reserve主要是留出早期的malloc内存区域和gd内存区域,其中CONFIG_SYS_MALLOC_F_LEN-0X400(在文件include/generated/autoconf.h中定义),sizeof(struct global_data)=248(GD_SIZE 值),完成以后的内存分布如图所示:
函数board_init_f_alloc_reserve是有返回值的,返回值为新的top值,从图可知,此时top=0X0091FA00。
继续回到示例代码32.2.4.1中,第87行,将r0写入到sp里面, r0保存着函数board_init_f_alloc _reserve的返回值,所以这一句也就是设置sp=0X0091FA00。
第89行,将r0寄存器的值写到寄存器r9里面,因为r9寄存器存放着全局变量gd的地址,在文件arch/arm/include/asm/global_data.h中有如图所示宏定义:
从图可以看出, uboot中定义了一个指向gd_t的指针gd, gd存放在寄存器 r9 里面的,因此gd是个全局变量。gd_t是个结构体,在include/asm-generic/global_data.h里面有定义,gd定义如下:
因此这一行代码就是设置gd所指向的位置,也就是gd指向0X0091FA00。
继续回到示例代码32.2.4.1中,第90行调用函数board_init_f_init_reserve,此函数在文件common/init/board_init.c中有定义,函数内容如下:
可以看出,此函数用于初始化gd,其实就是清零处理。另外,此函数还设置了gd->malloc_base为gd基地址+gd大小=0X0091FA00+248=0X0091FAF8,在做16字节对齐,最终gd->malloc_base-0X0091FB00,这个也就是early malloc的起始地址。
继续回到示例代码32.2.4.1中,第92行设置R0为0。
第93行,调用 board_init_f函数,此函数定义在文件common/board_f.c中!主要用来初始化DDR,定时器,完成代码拷贝等等,此函数我们后面在详细的分析。
第103行,重新设置环境(sp和 gd)、获取 gd->start_addr_sp的值赋给sp,在函数 board_init_f中会初始化gd的所有成员变量,其中gd->start_addr_sp-0X9EF44E90,所以这里相当于设置sp-gd->start_addr_sp-0X9EF44E90。0X9EF44E90是DDR中的地址,说明新的sp和gd将会存放到 DDR 中,而不是内部的RAM了。GD_START_ADDR_SP=64。
第109行,sp做8字节对齐。
第111行,获取gd->bd的地址赋给r9,此时r9存放的是老的gd,这里通过获取gd->bd的地址来计算出新的gd的位置。
第112行,新的gd在bd下面,所以r9减去gd的大小就是新的gd的位置,获取到新的gd的位置以后赋值给r9。
第114行,设置Ir寄存器为here,这样后面执行其他函数返回的时候就返回到了第122行的here位置处。
第115,读取gd->reloc_off的值复制给r0寄存器,GD_RELOC_OFF=68。
第116行,Ir寄存器的值加上r0寄存器的值,重新赋值给Ir寄存器。因为接下来要重定位代码,也就是把代码拷贝到新的地方去(现在的uboot存放的起始地址为0X87800000,下面要将 uboot拷贝到DDR最后面的地址空间出,将0X87800000开始的内存空出来),其中就包括here,因此Ir中的here要使用重定位后的位置。
第120行,读取gd->relocaddr的值赋给r0寄存器,此时r0寄存器就保存着uboot要拷贝的目的地址,为0X9FF47000。GD_RELOCADDR=48。
第121行,调用函数relocate_code,也就是代码重定位函数,此函数负责将uboot拷贝到新的地方去,此函数定义在文件arch/arm/lib/relocate.S中稍后会详细分析此函数。
继续回到示例代码32.2.4.1第131行,调用函数c_runtime_cpu_setup,此函数定义在文件arch/arm/cpu/army7/start.S中,函数内容如下:
继续回到示例代码32.2.4.1第141~159行,清除BSS段。
第167行,设置函数board_init_r的两个参数,函数board_init_r声明如下:
board init r(gd t *id, ulong dest addr)
第一个参数是gd,因此读取r9保存到r0里面。
第168行,设置函数board_init_r的第二个参数是目的地址,因此rl=gd->relocaddr。
第174行,调用函数board_init_r,此函数定义在文件common/board_r.c中,稍后会详细的分析此函数。
这个就是_main函数的运行流程,在_main函数里面调用了board_init_f、relocate_code、relocate_vectors和board_init_r这4个函数,接下来依次看一下这4个函数都是干啥的。
board_init_f函数详解
_main中会调用board_init_f函数,board_init_f函数主要有两个工作:
1.初始化一系列外设,比如串口、定时器,或者打印一些消息等。
2.初始化gd的各个成员变量,uboot会将自己重定位到DRAM最后面的地址区域,也就是将自己拷贝到DRAM最后面的内存区域中。这么做的目的是给Linux腾出空间,防止Linux kernel覆盖掉uboot,将DRAM前面的区域完整的空出来。在拷贝之前肯定要给uboot各部分分配好内存位置和大小,比如gd应该存放到哪个位置, malloc内存池应该存放到哪个位置等等。这些信息都保存在gd的成员变量中,因此要对gd的这些成员变量做初始化。最终形成一个完整的内存“分配图”,在后面重定位uboot的时候就会用到这个内存“分配图”。
此函数定义在文件common/board_f.c中定义,代码如下:
因为没有定义CONFIG_SYS_GENERIC_GLOBAL_DATA,所以第1037~1054行代码无效。
第1056行,初始化gd->flags=boot_flags=0。
第1057行,设置gd->have console=0。
重点在第1059行!通过函数 initcall_run_list来运行初始化序列init_sequence_f里面的一些列函数, init_sequence_f.c里面包含了一系列的初始化函数, init_sequence_f也是定义在文件
common/board f.c中,由于init_sequence_f的内容比较长,里面有大量的条件编译代码,这里为了缩小篇幅,将条件编译部分删除掉了,去掉条件编译以后的init_sequence_f定义如下:
接下来分析以上函数执行完以后的结果:
第2行, setup_mon_len函数设置gd的mon_len成员变量,此处为-bss_end-start,也就是整个代码的长度。0X878A8E74-0x87800000-0XA8E74,这个就是代码长度
第3行, initf_malloc函数初始化gd中跟malloc有关的成员变量,比如malloc_limit,此函数会设置gd->malloc_limit=CONFIG_SYS_MALLOC_F_LEN-0X400. malloc_limit表示malloc内存池大小。
第4行,initf_console_record,如果定义了宏CONFIG_CONSOLE-RECORD和宏CONFIG_SYS_MALLOC_F_LEN的话此函数就会调用函数console_record_init,但是IMX6ULL的uboot没有定义宏CONFIG CONSOLE RECORD,所以此函数直接返回0
第5行, arch_cpu_init函数。
第6行, initf_dm函数,驱动模型的一些初始化。
第7行, arch_cpu_init_dm函数未实现。
第8行, mark_bootstage函数应该是和啥标记有关的
第9行, board_early_init_f函数,板子相关的早期的一些初始化设置, I.MX6ULL用来初始化串口的IO配置
第10行,timer_init,初始化定时器,Cortex-A7内核有一个定时器,这里初始化的就是CortexA 内核的那个定时器。通过这个定时器来为uboot提供时间。就跟Cortex-M内核Systick定时器一样。
第11行,board_postelk_init,对于I.MX6ULL来说是设置VDDSOC电压。
第12行, get_clocks函数用于获取一些时钟值, I.MX6ULL获取的是sdhc_clk时钟,也就是SD 卡外设的时钟。
第13行, env_init函数是和环境变量有关的,设置gd的成员变量env_addr,也就是环境变量的保存地址。
第14行,init_baud_rate函数用于初始化波特率,根据环境变量baudrate来初始化 gd->baudrate。
第16行, console_init_f,设置gd->have_console为1,表示有个控制台,此函数也将前面暂存在缓冲区中的数据通过控制台打印出来。
第17行display_options,通过串口输出一些信息,如图所示:
第18行, display_text_info,打印一些文本信息,如果开启UBOOT的DEBUG功能的话就会输出text_base、bss_start、bss_end,形式如下:
debug("U-Boot code: %08IX->%08IX BSS:->%081Xn",text base, bss start, bss end);
第19行, print_cpuinfo函数用于打印CPU信息,结果如图所示:
第20行, show_board_info函数用于打印板子信息,会调用checkboard函数,结果如图所示:
第21行,INIT_FUNC_WATCHDOG_INIT,初始化看门狗,对于I.MX6ULL来说是空函数
第22行,INIT_FUNC_WATCHDOG_RESET,复位看门狗,对于I.MX6ULL来说是空函数
第23行,init_func_i2c函数用于初始化I2C,初始化完成以后会输出如图所示信息:
第24行, announce_dram_init,此函数很简单,就是输出字符串"DRAM:"
第26行,dram_init,并非真正的初始化DDR,只是设置gd->ram_size的值,对于正点原子 I.MX6ULL开发板EMMC版本核心板来说就是512MB。
第27行,post_init_f,此函数用来完成一些测试,初始化gd->post_init_f_time
第29行, testdram,测试DRAM,空函数。
第44行,setup_dest_addr函数,设置目的地址,设置gd->ram_size,gd->ram_top,gd->relocaddr这三个的值。接下来我们会遇到很多跟数值有关的设置,如果直接看代码分析的话就太费时间了,我可以修改 uboot代码,直接将这些值通过串口打印出来,比如这里我们修改文件common/board_f.c,因为setup_dest_addr函数定义在文件common/board_f.c 中,在setup_dest_addr函数输入如图所示内容:
设置好以后重新编译uboot,然后烧写到SD卡中,选择SD卡启动,重启开发板,打开SecureCRT, uboot会输出如图所示信息:
第45行,reserve_round_4k函数用于对gd->relocaddr做4KB对齐,因为gd->relocaddr=0XA0000000,已经是4K对齐了,所以调整后不变。
第46行,reserve_mmu,留出MMU的TLB表的位置,分配MMU的TLB表内存以后会对 gd->relocaddr做64K字节对齐。完成以后gd->arch.tlb_size、gd->arch.tlb_addr和 gd->relocaddr如图所示:
第47行,reserve_trace函数,留出跟踪调试的内存,I.MX6ULL没有用到!
第48行,reserve_uboot,留出重定位后的uboot所占用的内存区域,uboot所占用大小由gd->mon_len所指定,留出uboot的空间以后还要对gd->relocaddr做4K字节对齐,并且重新设置gd->start addr sp,结果如图所示:
第49行, reserve_malloc,留出malloc区域,"调整gd->start_addr_sp位置, malloc区域由宏TOTAL_MALLOC_LEN定义,宏定义如下:
mx6ull_alientek_emmc.h文件中定义宏CONFIG_SYS_MALLOC_LEN为16MB-0X1000000,宏CONFIG_ENV_SIZE=8KB=0X2000,因此TOTAL_MALLOC_LEN=0X1002000。调整以后gd->start addr sp如图所示:
第50行, reserve_board函数,留出板子bd所占的内存区, bd是结构体bd_t, bd_t大小为80 字节,结果如图所示:
第51行, setup_machine,设置机器ID, linux启动的时候会和这个机器ID匹配,如果匹·配的话linux就会启动正常。但是!! 1.MX6ULL不用这种方式了,这是以前老版本的uboot和linux使用的,新版本使用设备树了,因此此函数无效。
第52行, reserve_global_data函数,保留出gd_t的内存区域, gd_t结构体大小为248B,结果如图所示:
第53行, reserve_fdt,留出设备树相关的内存区域, I.MX6ULL的uboot没有用到,因此此函数无效。
第54行,reserve_arch是个空函数。
第55行,reserve_stacks,留出栈空间,先对gd->start_addr_sp减去16,然后做16字节对其。如果使能IRQ的话还要留出IRQ相应的内存,具体工作是由arch/arm/lib/stack.c文件中的函数arch_reserve_stacks完成。结果如图所示:
在本uboot中并没有使用到IRQ,所以不会留出IRQ相应的内存区域,此时:
gd->start_addr_sp=0X9EF44E90
第56行, setup_dram_config函数设置dram信息,就是设置gd->bd->bi_dram[0].start和gd->bd->bi_dram[0].size,后面会传递给linux内核,告诉linux DRAM的起始地址和大小。结果如图所示:
第57行,show_dram config函数,用于显示DRAM的配置,如图所示:
第58行, display_new_sp函数,显示新的sp位置,也就是gd->start_addr_sp,不过要定义宏DEBUG,结果如图所示:
第60行, reloc fdt函数用于重定位fdt,没有用到。
第61行, setup_reloc,设置gd的其他一些成员变量,供后面重定位的时候使用,并且将以前的gd拷贝到gd->new_gd处。需要使能DEBUG才能看到相应的信息输出,如图所示:
从图可以看出, uboot重定位后的偏移为0X18747000,重定位后的新地址为0X9FF4700,新的gd首地址为0X9EF44EB8,最终的sp为0X9EF44E90。
至此, board_init_f函数就执行完成了,最终的内存分配如图所示:
relocate_code函数详解
Relocate_code函数是用于代码拷贝的,此函数定义在文件arch/arm/lib/relocate.S中,代码如下:
第80行, r1=-image-copy_start,也就是r1寄存器保存源地址,可知,__image_copy start=0X87800000。
第81行,r0-0X9FF47000,这个地址就是uboot拷贝的目标首地址。r4=r0-r1=0X9FF470000X87800000=0X18747000,因此r4保存偏移量。
第82行,如果在第81中,r0-r1等于0,说明r0和r1相等,也就是源地址和目的地址是一样的,那肯定就不需要拷贝了!执行relocate_done函数
第83行, r2=_image_copy_end, r2中保存拷贝之前的代码结束地址,可知,__image copy end=0x8785dd54
第84行,函数copy_loop完成代码拷贝工作!从r1,也就是__image_copy_start开始,读取uboot代码保存到r10和r11中,一次就只拷贝这2个32位的数据。拷贝完成以后r1的值会更新,保存下一个要拷贝的数据地址。
第87行,将r10和r11的数据写到r0开始的地方,也就是目的地址。写完以后r0的值会更新,更新为下一个要写入的数据地址。
第88行,比较r1是否和r2相等,也就是检查是否拷贝完成,如果不相等的话说明没有拷贝完成,没有拷贝完成的话就跳转到copy_loop接着拷贝,直至拷贝完成。
接下来的第94行~109行是重定位.rel.dyn段, .rel.dyn段是存放.text段中需要重定位地址的集合。重定位就是uboot将自身拷贝到DRAM的另一个地放去继续运行(DRAM的高地址处)。我们知道,一个可执行的bin文件,其链接地址和运行地址要相等,也就是链接到哪个地址,在运行之前就要拷贝到哪个地址去。现在我们重定位以后,运行地址就和链接地址不同了,这样寻址的时候不会出问题吗?为了分析这个问题,我们需要在mx6ull_alientek_emmc.c中输入如下所示内容:
最后还需要在mx6ullevk.c文件中的board_init函数里面调用rel_test函数,否则rel_reset不会被编译进uboot。修改完成后的mx6ullevk.c如图所示:
Board_init函数会调用rel_test, rel_test会调用全局变量rel_a,使用如下命令编译uboot:
./mx6ull_alientek_emmc.sh
编译完成以后,使用arm-linux-gnueabihf-objdump将u-boot进行反汇编,得到u-boot.dis 这个汇编文件,命令如下:
arm-linux-gnueabihf-objdump-D-m arm u-boot > u-boot.dis
在u-boot.dis文件中找到rel_a、rel_rest和board_init,相关内容如下所示:
第12行是borad init调用rel_test函数,用到了b指令,而 b指令是位置无关指令, bl指令是相对寻址的(pc+offset),因此uboot中函数调用是与绝对位置无关的。
再来看一下函数 rel_test 对于全局变量 rel_a 的调用,第2行设置r3的值为pc+12地址处的值,因为ARM流水线的原因,pc寄存器的值为当前地址+8,因此pc=0X87804184+8=0X8780418C,r3=0X8780418C+12=0X87804198,第7行就是0X87804198这个地址,0X87804198处的值为0X8785DA50。根据第17行可知,0X8785DA50 正是变量rel_a的地址,最终r3=0X8785DA50。
第3行, r2=100。
第5行,将r2内的值写到r3地址处,也就是设置地址0X8785DA50的值为100,这不就是示例代码代码中的第5行: rel_a=100
总结一下rel_a=100的汇编执行过程:
1.在函数rel_test末尾处有一个地址为0X87804198的内存空间,此内存空间保存着变量rel_a的地址。
2.函数rel_test 要想访问变量rel_a,首先访问末尾的0X87804198来获取变量rel_a的地址,而访问0X87804198是通过偏移来访问的,很明显是个位置无关的操作。
3.通过0X87804198获取到变量rel_a的地址,对变量rel_a进行操作。
4.可以看出,函数rel_test对变量rel_a的访问没有直接进行,而是使用了一个第三方偏移地址0X87804198,专业术语叫做Label。这个第三方偏移地址就是实现重定位后运行不会出错的重要原因!
uboot重定位后偏移为0X18747000,那么重定位后函数rel_test的首地址就是0X87804184+0X18747000=0X9FF4B184。保存变量rel_a地址的Label就是0X9FF4B184+8+12=0X9FF4B198(既: 0X87804198+0X18747000),变量rel_a的地址就为0X8785DA50+0X18747000-0X9FFA4A50。重定位后函数rel-test要想正常访问变量rel_a就得设置0X9FF4B198(重定位后的Label)地址出的值为0X9FFA4A50(重定位后的变量rel_a地址)。这样就解决了重定位后链接地址和运行地址不一致的问题。
可以看出,uboot对于重定位后链接地址和运行地址不一致的解决方法就是采用位置无关码,在使用Id 进行链接的时候使用选项“-pie”生成位置无关的可执行文件。在文件arch/arm/config mk下有如下代码:
第83行就是设置uboot链接选项,加入了"-pie”选项,编译链接uboot的时候就会使用到"-pie",如图所示:
使用“-pie”选项以后会生成一个.rel.dyn 段, uboot就是靠这个.rel.dyn来解决重定位问题的,在u-bot.dis的rel.dyn段中有如下所示内容:
先来看一下.rel.dyn段的格式,类似第7行和第8行这样的是一组,也就是两个4字节数据为一组。高4字节是Label地址标识0x17,低4字节就是Label的地址,首先判断Label地址标识是否正确,也就是判断高4字节是否为0X17,如果是的话低4字节就是Label地址值。
第7行值为0X87804198,第8行为0X00000017,说明第7行的0X87804198是个Label,这个正是示例代码中存放变量rel_a地址的那个Label。根据前面的分析,只要将地址0X87804198+offset处的值改为重定位后的变量rel_a地址即可。我们猜测的是否正确,看一下uboot对.rel.dyn段的重定位即可.rel.dyn段的重定位代码如下:
第94行, r2=__rel_dyn_start,也就是.rel.dyn段的起始地址。
第95行,r3=__rel_dyn_end,也就是.rel.dyn段的终止地址。
第97行,从.rel.dyn段起始地址开始,每次读取两个4字节的数据存放到r0和r1寄存器中,r0 存放低4字节的数据,也就是Label地址;r1存放高4字节的数据,也就是Label标志。
第98行,r1中给的值与0xff进行与运算,其实就是取r1的低8位。
第99行,判断r1中的值是否等于23(0X17)。
第100行,如果r1 不等于23的话就说明不是描述Label的,执行函数fixnext,否则的话继续执行下面的代码。
第103行,r0保存着Label值,r4保存着重定位后的地址偏移,r0+r4就得到了重定位后的Label 值。此时r0保存着重定位后的Label值,相当于0X87804198+0X18747000=0X9FF4B198。
第104行,读取重定位后Label所保存的变量地址,此时这个变量地址还是重定位前的(相当于 rel_a重定位前的地址0X8785DA50),将得到的值放到r1寄存器中。
第105行, r1+r4即可得到重定位后的变量地址,相当于rel_a重定位后的0X8785DA50+0X18747000=0X9FFA4A50。
第106行,重定位后的变量地址写入到重定位后的Label中,相等于设置地址0X9FF4B198处的值为0X9FFA4A50。
第108行,比较r2和r3,查看.rel.dyn段重定位是否完成。
第109行,如果r2和r3不相等,说明.rel.dyn重定位还未完成,因此跳到fixloop继续重定位.rel.dyn 段。可以看出,uboot中对.rel.dyn段的重定位方法和我们猜想的一致。.rel.dyn 段的重定位比较复杂一点,有点绕,因为涉及到链接地址和运行地址的问题。
relocate_vectors函数详解
函数relocate_vectors用于重定位向量表,此函数定义在文件relocate.S中,函数源码如下:
第29行,如果定义了CONFIG_CPU_V7M的话就执行第30-36行的代码,这是Cortex-M内核单片机执行的语句,因此对于I.MX6ULL来说是无效的。
第38行,如果定义了CONFIG_HAS_VBAR的话就执行此语句,这个是向量表偏移,Cortex.A7是支持向量表偏移的。而且,在.config里面定义了CONFIG_HAS_VBAR,因此会执行这个分支。
第43行,r0-gd->relocaddr,也就是重定位后uboot的首地址,向量表肯定是从这个地址开始存放的。
第44行,将r0的值写入到CP15的VBAR寄存器中,也就是将新的向量表首地址写入到寄存器VBAR中,设置向量表偏移。
board_init_r 函数详解
board_init_f函数,在此函数里面会调用一系列的函数来初始化一些外设和gd的成员变量。但是board_init_f并没有初始化所有的外设,还需要做一些后续工作,这些后续工作就是由函数 board_init_r来完成的, board_init_r函数定义在文件common/board_r.c中,代码如下:
第1010行调用initcall_run_list函数来执行初始化序列init_sequence_r, init_sequence_r是一个函数集合, init_sequence_r也定义在文件 common/board_r.c中,由于init_sequence_f的内容比较长,里面有大量的条件编译代码,这里为了缩小篇幅,将条件编译部分删除掉了,去掉条件编译以后的init_sequence_r定义如下:
第2行, initr_trace函数,如果定义了宏CONFIG_TRACE的话就会调用函数trace_init,初始化和调试跟踪有关的内容。
第3行,initr_reloc函数用于设置gd->flags,标记重定位完成。
第4行, initr_caches函数用于初始化cache,使能cache。
第5行, initr_reloc_global_data函数,初始化重定位后gd的一些成员变量。
第6行,initr_barrier函数,I.MX6ULL未用到。
第7行,initr_malloc函数,初始化malloc。
第8行, initr_console_record函数,初始化控制台相关的内容, I.MX6ULL未用到,空函数。
第9行, bootstage_relocate函数,启动状态重定位。
第10行,initr_bootstage函数,初始化bootstage什么的。
第11行,board_init 函数,板级初始化,包括74XX芯片,I2C、FEC、USB和QSPI等。这里执行的是mx6ull_alientek_emmc.c文件中的board_init函数。
第12行,stdio_init_tables函数,stdio相关初始化。
第13行,initr_serial函数,初始化串口。
第14行, initr_announce函数,与调试有关,通知已经在RAM中运行。
第18行, power_init_board函数,初始化电源芯片,正点原子的I.MX6ULL开发板没有用到。
第19行, initr_flash函数,对于I.MX6ULL而言,没有定义宏CONFIG_SYS_NO_FLASH的话函数initr_flash才有效。但是mx6_common.h中定义了宏CONFIG_SYS_NO_FLASH,所以此函数无效。
第21行, initr_nand函数,初始化NAND,如果使用NAND版本核心板的话就会初始化
第22行, initr_mmc函数,初始化EMMC,如果使用EMMC版本核心板的话就会初始化EMMC,串口输出如图所示信息:
第23行, initr_env函数,初始化环境变量。
第25行, initr_secondary_cpu函数,初始化其他CPU核, I.MX6ULL只有一个核,因此此函数没用。
第27行, stdio_add_devices函数,各种输入输出设备的初始化,如LCDdriver, I.MX6ULL使用 drE_video_init函数初始化LCD。会输出如图所示信息:
第28行,initr_jumptable函数,初始化跳转表。
第29行,console_init_r函数,控制台初始化,初始化完成以后此函数会调用stdio_print_current_devices函数来打印出当前的控制台设备,如图所示:
第31行, interrupt_init函数,初始化中断。
第32行, initr_enable_interrupts函数,使能中断。
第33行, initr_ethaddr函数,初始化网络地址,也就是获取MAC地址。读取环境变量“ethaddr”的值。
第34行, board_late_init函数,板子后续初始化,此函数定义在文件mx6ull_alientek_emmc.c中,如果环境变量存储在EMMC或者SD卡中的话此函数会调用boardlate_mme_env_init函数初始化EMMC/SD。会切换到正在时候用的emmc设备,代码如图所示:
图中的第46行和第47行就是运行“mmc dev xx”命令,用于切换到正在使用的EMMC设备,串口输出信息如图所示:
第38行,initr_net函数,初始化网络设备,函数调用顺序为:
initr_net->eth_initialize->board_eth_init(),串口输出如图所示信息:
第40行,run_main_loop行,主循环,处理命令。