接上一节:七.linux开发之uboot移植(七)——uboot源码分析2-启动第二阶段之start_armboot函数分析1
还是老规矩:有道云笔记地址:http://note.youdao.com/noteshare?id=fef34a7ee3bcdfdf7638db662564ae66&sub=69B7DF889DAF4064BF8D7852C69EE452
1、CFG_NO_FLASH
(1)虽然NandFlash和NorFlash都是Flash,但是一般NandFlash会简称为Nand而不是Flash,一般讲Flash都是指的Norflash。这里2行代码是Norflash相关的。
(2)flash_init执行的是开发板中对应的NorFlash的初始化、display_flash_config打印的也是NorFlash的配置信息(Flash:
8 MB就是这里打印出来的)。
但是实际上X210中是没有Norflash的。所以着两行代码是可以去掉的(实验证明:加上CONFIG_NOFLASH宏之后编译出错,说明代码移植的不好,那个文件的包含没有被这个宏控制。于是乎移植的人就直接放这没管。)
CONFIG_VFD和CONFIG_LCD是显示相关的,这个是uboot中自带的LCD显示的软件架构。但是实际上我们用LCD而没有使用uboot中设置的这套软件架构,我们自己在后面自己添加了一个LCD显示的部分。
2、mem_malloc_init
(1)mem_malloc_init函数用来初始化uboot的堆管理器。
(2)uboot中自己维护了一段堆内存,肯定自己就有一套代码来管理这个堆内存。有了这些东西uboot中你也可以malloc、free这套机制来申请内存和释放内存。我们在DDR内存中给堆预留了896KB的内存。
1、开发板独有初始化:mmc初始化
(1)从536到768行为开发板独有的初始化。意思是三星用一套uboot同时满足了好多个系列型号的开发板,然后在这里把不同开发板自己独有的一些初始化写到了这里。用#if条件编译配合CONFIG_xxx宏来选定特定的开发板。
(2)X210相关的配置在599行到632行。
(3)mmc_initialize看名字就应该是MMC相关的一些基础的初始化,其实就是用来初始化SoC内部的SD/MMC控制器的。函数在uboot/drivers/mmc/mmc.c里。
(4)uboot中对硬件的操作(譬如网卡、SD卡···)都是借用的linux内核中的驱动来实现的,uboot根目录底下有个drivers文件夹,这里面放的全都是从linux内核中移植过来的各种驱动源文件。
(5)mmc_initialize是具体硬件架构无关的一个MMC初始化函数,所有的使用了这套架构的代码都掉用这个函数来完成MMC的初始化。mmc_initialize中再调用board_mmc_init和cpu_mmc_init来完成具体的硬件的MMC控制器初始化工作。
(6)cpu_mmc_init在uboot/cpu/s5pc11x/cpu.c中,这里面又间接的调用了drivers/mmc/s3c_mmcxxx.c中的驱动代码来初始化硬件MMC控制器。这里面分层很多,分层的思想一定要有,否则完全就糊涂了。
1、env_relocate
(1)env_relocate是环境变量的重定位,完成从SD卡中将环境变量读取到DDR中的任务。
(2)环境变量到底从哪里来?SD卡中有一些(8个)独立的扇区作为环境变量存储区域的。但是我们烧录/部署系统时,我们只是烧录了uboot分区、kernel分区和rootfs分区,根本不曾烧录env分区。所以当我们烧录完系统第一次启动时ENV分区是空的,本次启动uboot尝试去SD卡的ENV分区读取环境变量时失败(读取回来后进行CRC校验时失败),我们uboot选择从uboot内部代码中设置的一套默认的环境变量出发来使用(这就是默认环境变量);这套默认的环境变量在本次运行时会被读取到DDR中的环境变量中,然后被写入(也可能是你saveenv时写入,也可能是uboot设计了第一次读取默认环境变量后就写入)SD卡的ENV分区。然后下次再次开机时uboot就会从SD卡的ENV分区读取环境变量到DDR中,这次读取就不会失败了。
(3)真正的从SD卡到DDR中重定位ENV的代码是在env_relocate_spec内部的movi_read_env完成的。
1、IP地址、MAC地址的确定
gd->bd->bi_ip_addr gd数据结构赋值
gd->bd->bi_enetaddr gd数据结构赋值
(1)开发板的IP地址是在gd->bd中维护的,来源于环境变量ipaddr。getenv函数用来获取字符串格式的IP地址,然后用string_to_ip将字符串格式的IP地址转成字符串格式的点分十进制格式。
(2)IP地址由4个0-255之间的数字组成,因此一个IP地址在程序中最简单的存储方法就是一个unsigend
int。但是人类容易看懂的并不是这种类型,而是点分十进制类型(192.168.1.2)。这两种类型可以互相转换。
2、devices_init
(1)devices_init看名字就是设备的初始化。这里的设备指的就是开发板上的硬件设备。放在这里初始化的设备都是驱动设备,这个函数本来就是从驱动框架中衍生出来的。uboot中很多设备的驱动是直接移植linux内核的(譬如网卡、SD卡),linux内核中的驱动都有相应的设备初始化函数。linux内核在启动过程中就有一个devices_init(名字不一定完全对,但是差不多),作用就是集中执行各种硬件驱动的init函数。
(2)uboot的这个函数其实就是从linux内核中移植过来的,它的作用也是去执行所有的从linux内核中继承来的那些硬件驱动的初始化函数。
3、jumptable_init
(1)jumptable跳转表,本身是一个函数指针数组,里面记录了很多函数的函数名。看这阵势是要实现一个函数指针到具体函数的映射关系,将来通过跳转表中的函数指针就可以执行具体的函数。这个其实就是在用C语言实现面向对象编程。在linux内核中有很多这种技巧。
(2)通过分析发现跳转表只是被赋值从未被引用,因此跳转表在uboot中根本就没使用。
1、console_init_r
(1)console_init_f是控制台的第一阶段初始化,console_init_r是第二阶段初始化。实际上第一阶段初始化并没有实质性工作,第二阶段初始化才进行了实质性工作。
(2)console_init_r就是console的纯软件架构方面的初始化(说白了就是去给console相关的数据结构中填充相应的值),所以属于纯软件配置类型的初始化。
(3)uboot的console实际上并没有干有意义的转化,它就是直接调用的串口通信的函数。所以用不用console实际并没有什么分别。(在linux内console就可以提供缓冲机制等不用console不能实现的东西)。
.2、enable_interrupts
(1)看名字应该是中断初始化代码。这里指的是CPSR中总中断标志位的使能。
(2)因为我们uboot中没有使用中断,因为没有定义CONFIG_USE_IRQ宏,因此我们这里这个函数是个空壳子。
(3)uboot中经常出现一种情况就是根据一个宏是否定义了来条件编译决定是否调用一个函数内部的代码。uboot中有2种解决方案来处理这种情况:方案一:在调用函数处使用条件编译,然后函数体实际完全提供代码。方案二:在调用函数处直接调用,然后在函数体处提供2个函数体,一个是有实体的一个是空壳子,用宏定义条件编译来决定实际编译时编译哪个函数进去。
3、loadaddr、bootfile两个环境变量
(1)这两个环境变量都是内核启动有关的,在启动linux内核时会参考这两个环境变量的值。
4、board_late_init
(1)看名字这个函数就是开发板级别的一些初始化里比较晚的了,就是晚期初始化。所以晚期就是前面该初始化的都初始化过了,剩下的一些必须放在后面初始化的就在这里了。侧面说明了开发板级别的硬件软件初始化告一段落了。
(2)对于X210来说,这个函数是空的。
1、eth_initialize
(1)看名字应该是网卡相关的初始化。这里不是SoC与网卡芯片连接时SoC这边的初始化,而是网卡芯片本身的一些初始化。
(2)对于X210(DM9000)来说,这个函数是空的。X210的网卡初始化在board_init函数中,网卡芯片的初始化在驱动中。
2、x210_preboot_init(LCD和logo显示)
(1)x210开发板在启动起来之前的一些初始化,以及LCD屏幕上的logo显示。
3、check menukey to update from sd
(1)uboot启动的最后阶段设计了一个自动更新的功能。就是:我们可以将要升级的镜像放到SD卡的固定目录中,然后开机时在uboot启动的最后阶段检查升级标志(是一个按键。按键中标志为”LEFT”的那个按键,这个按键如果按下则表示update
mode,如果启动时未按下则表示boot mode)。如果进入update
mode则uboot会自动从SD卡中读取镜像文件然后烧录到iNand中;如果进入boot
mode则uboot不执行update,直接启动正常运行。
(2)这种机制能够帮助我们快速烧录系统,常用于量产时用SD卡进行系统烧录部署。
4、死循环、
(1)解析器(即接收命令,解析命令,执行命令)
(2)开机倒数自动执行
(3)命令补全
到这里为止,start_armboot函数就解析完了!!
1、启动流程回顾、重点函数标出
(1)第二阶段主要是对开发板级别的硬件、软件数据结构进行初始化。
(2)init_sequence函数指针数组里面的函数元素在上节最后已经总结,这里不再赘述。,一下是接着往下分析的。
mem_malloc_init函数初始化uboot自己维护的堆管理器的内存
mmc_initialize函数inand/SD卡的SoC控制器和卡的初始化
env_relocate函数环境变量重定位
gd->bd->bi_ip_addr gd数据结构赋值
gd->bd->bi_enetaddrgd数据结构赋值
devices_init函数 空的
jumptable_init函数 不用关注的
console_init_r函数真正的控制台初始化
enable_interrupts函数 空的
loadaddr、bootfile 环境变量读出初始化全局变量
board_late_init 函数 空的
eth_initialize函数 空的
x210_preboot_init函数 LCD初始化和显示logo
check_menu_update_from_sd函数 检查自动更新
main_loop函数主循环
2、启动过程特征总结
(1)第一阶段为汇编阶段、第二阶段为C阶段
(2)第一阶段在SRAM中、第二阶段在DRAM中
(3)第一阶段注重SoC内部、第二阶段注重SoC外部Board内部(比如网卡,lcd)
① 硬件设备初始化包括:关闭 Watchdog、关闭中断、设置 CPU 的速度和时钟频 率、配置 SDRAM 存储控制器及 IO、关闭处理器内部指令/数据 Cache 等;
② 为加载 Bootloader 的 stage2 代码准备 DRAM 空间(这个地址由链接脚本指定 为运行域地址,通常为 RAM 的高端地址)测试内存空间是否有效;
③ 复制 Bootloader 的 stage2 代码到 DRAM 空间中;
④ 设置好堆栈;
⑤ 跳转到 stage2 的 C 函数入口点。 stage2 则是通常用 C 语言来实现,这样可以实现更复杂的功能,而且代码会 具有更好的可读性和可移植性。
.
① 初始化本阶段要使到的硬件设备(如串口、 Flash 和网卡等);
② 检测系统内存映射;
③ 没有用户干预时将内核映像从 Flash 读到 DRAM 空间中;
④ 为内核设置启动参数;
⑤ 调用内核。**
uboot 调用内核的方法就是直接跳转到内核的第一条指令处。在调 用内核之前下列的条件必须要满足: (所以前面stage1和stage2 已经把这些初始化了)
① R0 为 0;
② R1 为机器码;
③ R2 为启动参数,标记列表在 DRAM 中的起始基地址。
① 必须禁止中断( IRQ 和 FIQ);
② CPU 必须设置为 SVC 模式。 3.Cache 和 MMU 的设置
① MMU 必须关闭;
② 指令 Cache 可以打开或关闭;
③ 数据 Cache 必须关闭。