bootloader 版本
u-boot-2012.10-psp05.06.00.00
bootloader 启动流程
概述
am335x bootloader整体分为三个部分:rom_code,SPL(Secondary Program Loader)及u-boot。在分析流程之前,先大概浏览以下源代码的结构:
u-boot 源码组织结构
子目录 | 目录内容 |
---|---|
api | 存放uboot提供的接口函数 |
arch | 与体系结构相关的代码 |
board | 根据不同开发板所定制的代码 |
common | 通用的代码,涵盖各个方面,以对命令行的处理为主 |
disk | 磁盘分区相关代码 |
doc | 文档,readme |
drivers | 驱动相关代码,每种类型的设备驱动占用一个子目录 |
examples | 示例程序 |
fs | 文件系统,支持嵌入式开发板常见的文件系统 |
include | 头文件,以通用的头文件为主 |
lib | 通用库文件 |
nand_spl | nand存储器相关的代码 |
net | 网络相关的代码,小型的协议栈 |
onenand_ipl | onenand存储器启动的代码 |
post | 上电自检程序 |
tools | 辅助功能程序,用于制作uboot镜像等 |
需要重点关注的源码
文件名称 | 路径 |
---|---|
u-boot-spl.lds | arch\arm\cpu\armv7\omap-common |
start.S | arch\arm\cpu\armv7/ |
lowlevel_init.S | \arch\arm\cpu\armv7\ti81xx/ |
spl.c | \common |
board.c | \arch\arm\cpu\armv7\am33xx |
am335x_evm.h | \include\configs\ |
bootloader的整体运行流程为:
1. 芯片上电或复位
2. rom_code 运行,从外设或外部存储器中加载二级bootloader(SPL)到内存中运行
3. SPL做CPU和外设的初始化(主要是DDR)并将u-boot加载到内存运行
4. u-boot 继续做其它板载或外设的初始化并加载linux内核
5. linux内核开始启动
rom_code
am335x 系列soc在芯片内部的flash中固化了一段代码,称为rom_code,在芯片上电或复位后首先执行芯片内部固化的这段代码,这段代码的作用为引导二级bootloader(SPL)的镜像文件到内部sram中运行。
am335x引导模式
所谓am335x的引导模式其实就是二级bootloader(SPL)镜像的读取方式,刚才提到rom_code的作用是将二级 bootloader加载到内部的sram运行,那么rom_code从什么地方以及通过什么样的方式获取SPL的可执行镜像文件呢?这就决定于am335x的引导模式了。
am335x支持两大类的引导模式,分别为memory模式及外设模式。memory模式可以从NAND flash、NOR flash及eeprom中读取SPL镜像文件并加载到内部sram中运行。外设模式是指rom_code可以通过芯片的一些通信接口(比如EMAC、Serial、USB)获取SPL镜像文件并加载到内部sram中运行。
am335x的引导模式取决于上电或复位时芯片一组引脚的状态,称作SYSBOOT[15:0]共16位。其中SYSBOOT[4:0]决定了芯片的引导顺序。
上图中可以看出当SYSBOOT[4:0]为00010时,芯片会首先从UART0开始引导,如果失败会尝试SPI0、NAND、NANDI2C。
rom_code的运行流程
参考am335x的芯片技术手册,可以知道rome_code的执行流程如下:
下面分析rom_code的执行流程:
- 芯片复位后PC指针首先跳转到0x20000地址开始执行rom_code并做芯片的简单初始化
- rom_code根据SYSBOOT的配置生成引导设备列表
- 查看当前的引导类型是memory模式还是外设模式,并通过相应的接口或地址读取SPL
- SPL image读取成功,执行SPL镜像,SPL开始运行
- SPL image读取失败,尝试启动列表中的下一个外设或接口
- 若所有外设都没有正确读取到SPL image,则进入死循环(dead loop)等待看门狗复位
addr 说明 0x20000 rom_code的运行起始地址 0x402F0400 SPL镜像在内部sram中的加载地址,也是内部ram的起始地址
am335x 内部rom和内部ram的结构如下:
SPL(Secondary Program Loader)
SPL的作用
SPL是紧接着rom_code的第二级bootloader,主要负责板载的部分初始化、加载及运行u-bootSPL运行的起始点
SPL在芯片的内部sram中运行,那么它是从哪儿开始运行的呢?这需要查看spl在链接的时候的链接文件
u-boot-spl.lds,下面是 u-boot-spl.lds的文件内容:
# 定义内部sram区块
MEMORY { .sram : ORIGIN = CONFIG_SPL_TEXT_BASE,\ # 这个定义在/include/configs/am335x_evm.h
LENGTH = CONFIG_SPL_MAX_SIZE }
# 定义外部sdram区块
MEMORY { .sdram : ORIGIN = CONFIG_SPL_BSS_START_ADDR, \
LENGTH = CONFIG_SPL_BSS_MAX_SIZE }
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
# 定义入口为_start
ENTRY(_start)
SECTIONS
{
.text :
{
__start = .;
arch/arm/cpu/armv7/start.o (.text) # 首先被执行的地方
*(.text*)
} >.sram # 将代码段搬移到内部sram运行
. = ALIGN(4);
.rodata : { *(SORT_BY_ALIGNMENT(.rodata*)) } >.sram
. = ALIGN(4);
.data : { *(SORT_BY_ALIGNMENT(.data*)) } >.sram
. = ALIGN(4);
__image_copy_end = .;
_end = .;
.bss :
{
. = ALIGN(4);
__bss_start = .;
*(.bss*)
. = ALIGN(4);
__bss_end__ = .;
} >.sdram
}
上述文件中 ENTRY(_start) 定义了SPL镜像的入口符号为start,它所存在的位置在arch/arm/cpu/armv7/start.o (.text) 中,而start.o是由start.S编译得到的,所以SPL的入口文件为arch/arm/cpu/armv7/start.S,另外SPL执行的地址由 CONFIG_SPL_TEXT_BASE定义得到的,我们看一下位于/include/configs/am335x_evm.h中的定义:
/* Defines for SPL */
#define CONFIG_SPL
#define CONFIG_SPL_FRAMEWORK
#define CONFIG_SPL_TEXT_BASE 0x402F0400 // 定义spl的入口地址
#define CONFIG_SPL_MAX_SIZE (101 * 1024)
#define CONFIG_SPL_STACK CONFIG_SYS_INIT_SP_ADDR
#define CONFIG_SPL_LDSCRIPT "$(CPUDIR)/omap-common/u-boot-spl.lds" // 定义spl的链接文件
#define CONFIG_SPL_BSS_START_ADDR 0x80000000
#define CONFIG_SPL_BSS_MAX_SIZE 0x80000 /* 512 KB */
- SPL执行流程
1. Start.S: bl lowlevel_init
2. lowlevel_init.S::lowlevel_init bl s_init
3. arch/arm/cpu/armv7/am33xx/board.c::s_init
• 关闭看门狗
• 初始化PLL
• 初始化控制台串口
• 初始化外设pinmux
• 初始化DDR
4. Start.S: bl board_init_f
5. arch/arm/lib/board::board_init_f
• 初始化堆栈
• 清零bss段
• call board_init_r
6. common/spl/spl.c:: board_init_r
• 设置timer
• call board_init
7. arch/arm/cpu/armv7/am33xx/board.c::board_init
• i2c_init
• Lcd_Init
• gpmc_init
8. common/spl/spl.c:: 继续初始化
• 根据当前的启动设备,加载u-boot到DDR
• 运行并将控制权交给 u-boot
• PC跳转到0x80800000(CONFIG_SYS_TEXT_BASE)执行u-boot
- u-boot 执行流程
0. u-boot的起始点也是start.S
1. Start.S: bl lowlevel_init
2. lowlevel_init.S::lowlevel_init bl s_init
3. arch/arm/cpu/armv7/am33xx/board.c::s_init
• 关闭看门狗
• 初始化PLL
• 初始化控制台串口
• 初始化外设pinmux
• 初始化DDR
4. Start.S: bl board_init_f
5. arch/arm/lib/board.C::board_init_f
• 初始化堆栈
• 清零bss段
• call board_init_r
6. arch/arm/lib/board.C::board_init_r
• call board_init
7. \arch\arm\cpu\armv7\am33xx\board.c::board_init
• i2c_init
• lcd_init
• gpmc_init
• dcache_enable
8. arch/arm/lib/board.C::board_init_r
• serial_initialize
• mem_malloc_init
• flash_init
• nand_init
• mmc_initialize
• env_relocate
• console_init_r
• interrupt_init
• enable_interrupts
• eth_initialize
• reset_phy
9. common/main.c::main_loop
• 解析命令,执行命令,boot the linux kernel