Akagi201
8/23/2014 - 6:36 AM

u-boot.md

uboot代码架构

  • board: 和一些已有开发板有关的文件, 每个开发板都以一个子目录出现在当前目录中. 比如说: SMDK2410, 子目录中存放与开发板相关的配置文件.
  • common: 实现uboot命令行下支持的命令, 每一条命令都对应一个文件. 例如, bootm命令对应就是cmd_bootm.c.
  • cpu: 与特定CPU架构相关目录, 每一款uboot下支持的CPU在该目录下对应一个子目录, 比如有子目录arm920t等.
  • disk: 对磁盘的支持.
  • doc: 文档目录. uboot有非常完善的文档, 推荐大家参考阅读.
  • drivers: Uboot支持的设备驱动程序都放在该目录, 比如各种网卡, 支持CFI的Flash, 串口和USB等.
  • fs: 支持的文件系统, Uboot现在支持cramfs, fat, fdos, jffs2和registerfs.
  • include: Uboot使用的头文件, 还有对各种硬件平台支持的汇编文件, 系统的配置文件和对文件系统支持的文件. 该目录下configs目录有与开发板相关的配置头文件, 如: smdk2410.h. 该目录下的asm目录有与CPU体系结构相关的头文件, asm对应asm arm9
  • lib_xxx: 与体系结构相关的库文件. 如与ARM相关的库放在lib_arm中.
  • net: 与网络协议族相关的代码, BOOTP协议, TFTP协议, RARP协议和NFS文件系统的实现.
  • tools: 生成Uboot的工具, 如: mkimage, crc等等.

u-boot.bin的生成

  • u-boot的Makefile从功能上可以分成两个部分. 一部分是用来编译生成uboot.bin文件. 另一部分是用来执行每种board相关的配置.
  • u-boot.bin的生成分为两步. 以smdk2410为例来说明, 如下:
  1. 对于board进行配置: make smdk2410_config
  2. 进行编译生成u-boot.bin: make CROSS_COMPILE=arm-linux-

u-boot的Makefile分析

  • $make smdk2410_config 在shell执行以上命令, 对应于Makefile执行的命令是:
smdk2410_config: unconfig
    @./mkconfig $(@:_config=) arm arm920t smdk2410 NULL s3c24x0
  • 第一步: unconfig
unconfig:
    @rm -f include/config.h include/config.mk board/*/config.tmp

删除文件include/config.h include/config.mk board/*/config.tmp

  • 第二步: @./mkconfig $(@:_config=) arm arm920t smdk2410 NULL s3c24x0 mkconfig是脚本文件, 传入的参数$1$6分别为: smdk2410 arm arm920t smdk2410 NULL s3c24x0, 根据传入的参数执行如下命令:
cd ./include
rm -f asm
ln -s asm-arm/arch
ln -s arch-s3c24x0 asm-arm/srch
rm -f asm-arm/proc
ln -s proc-armv asm-arm/proc
  • 生成文件config.mk, 文件内容为:
ARCH = arm
CPU = arm920t
BOARD = smdk2410
SOC = s3c24x0
  • 生成文件config.h, 文件内容为:
/* Automatically generated - do not edit */
#include <configs/smdk2410.h>
  • $make CROSS_COMPILE=arm-linux- Makefile的执行首先包含include/config.mk文件, 获取ARCH CPU BOARD VENDOR SOC的定义, 然后根据宏的配置编译指定的文件, 最终生成u-boot.bin文件, 执行流程请自行分析.

启动模式

  • u-boot包含两种不同的操作模式: "启动加载"模式和"下载"模式.
  • 启动加载(Boot loading)模式: 这种模式也称为"自主"(Autonomous)模式. 也即Bootloader从目标机上某个固态存储设备上将操作系统加载到RAM中运行, 整个过程并没有用户的介入. 这种模式是Bootloader的正常工作模式, 因此, 在嵌入式产品发布的时候, Bootloader显然必须工作在这种模式下.
  • 下载(Downloading)模式: 在这种模式下, 目标机上的Boot Loader将通过串口连接或网络连接等通信手段从主机(Host)下载文件, 然后控制启动流程.

stage1 (入口定义)

  • 主要功能为进入C代码建立一个基本的环境.
  • 由于一个可执行的Image必须有一个入口点, 并且只能有一个全局入口, 通常这个入口放在ROM(Flash)的0x0地址, 因此, 必须通知编译器以使其知道这个入口, 该工作可通过修改链接器脚本来完成.
  • u-boot的stage1代码通常放在start.s文件中, 用汇编语言写成.
  • board/smdk2410/uboot.lds: ENTRY(_start) ==> cpu/arm920t/start.o (.text)
  • u-boot在ram的代码区(TEXT_BASE = 0x33F80000)定义在board/smdk2410/config.mk

设置异常向量

.global _start
_start: b reset
    ldr pc, _undefined_instruction
    ldr pc, _software_interrupt
    ldr pc, _prefetch_abort
    ldr pc, _data_abort
    ldr pc, _not_used
    ldr pc, _irq
    ldr pc, _fiq

当发生异常时, 执行cpu/arm920t/interrupts.c中定义的中断处理函数.

设置CPU的模式为SVC模式

mrc r0, cpsr
bic r0, r0, #0x1f
orr r0, r0, #0xd3
msr cpsr, r0

关闭看门狗, 禁掉所有中断, 设置CPU的频率

#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)
ldr r0, =pWTCON
mov r1, #0x0
str r1, [r0]

/*
 * mask all IRQs by setting all bits in the INTMR - default
 */
mov r1, #0ffffffff
ldr r0, =INTMSK
str r1, [r0]
#if defined(CONFIG_S3C2410)
ldr r1, =0x3ff
ldr r0, =INTSUBMSK
str r1, [r0]
#endif

/* FCLK:HCLK:PCLK = 1:2:4 */
/* default FCLK is 120 MHz! */
ldr r0, =CLKDIVN
mov r1, #3
str r1, [r0]
#endif /* CONFIG_S3C2400 || CONFIG_S3C2410 */

与内存管理相关寄存器的设置, cp15协处理器, 配置内存区控制寄存器

cpu_init_crit:
/*
 * flush v4 I/D caches
 */
mov r0, #0
mcr p15, 0, r0, c7, c7, 0 /* 失效I/D cache, 见S3C2410手册附录的2-16 */
mcr p15, 0, r0, c8, c7, 0 /* 失效TLB, 见S3C2410手册附录的2-18 */ 

/*
 * disable MMU stuff and caches
 */
mrc p15, 0, r0, c1, c0, 0
bic r0, r0, #0x00002300 
/* 清除bits 13, 9:8 (--V --RS)
 * Bit 8: Disable System Protection
 * Bit 7: Disable ROM Protection
 * Bit 13: 异常向量表基地址: 0x0000 0000
 */
bit r0, r0, #0x00000087
/*
 * 清除bits 7, 2:0 (B--CAM)
 * Bit 0: MMU disabled
 * Bit 1: Alignment Fault checking disabled
 * Bit 7: 0 == Little-endian operation
 */
orr r0, r0, #0x00000002 @ set bit 2 (A) Align
orr r0, r0, #0x00001000 @ set bit 12 (I) I-Cache
mcr p15, 0, r0, c1, c0, 0
/*
 * before relocating, we have to setup RAM timing
 * because memory timing is board-dependend, you will
 * find a lowlevel_init.S in your board directory
 */
mov ip, lr
bl lowlevel_init /* 寄存器的具体值的设置需要对总线周期及外围芯片非常熟悉, 根据所采用的内存芯片确定 */
mov lr, ip
mov pc, lr
  • 把 u-boot.lds定义的text端, rodata端, data端, got端, __u_boot_cmd端搬移到ram区.
  • 建立stack空间
  • bss段清0
  • 进入C代码部分
ldr pc, _start_armboot
_start_armboot: .word start_armboot

stage2 C语言代码部分

lib_arm/board.c中的start_armboot是C语言开始的函数, 也是整个启动代码中C的主函数, 同时还是整个u-boot(armboot)的主函数, 该函数主要完成如下操作:

  1. 调用一系列的初始化函数 指定初始函数表:
init_fnc_t *init_sequence[] = {
    cpu_init, /* cpu的基本设置 */
    board_init, /* 开发板的基本初始化 */
    interrupt_init, /* 初始化中断 */
    env_init, /* 初始化环境变量 */
    init_baudrate, /* 初始化波特率 */
    serial_init, /* 串口通讯初始化 */
    console_init_f, /* 控制台初始化第一阶段 */
    display_banner, /* 通知代码已经运行到该处 */
    dram_init, /* 配置可用的内存区 */
    display_dram_config,
    #if defined(CONFIG_VCMA9) || defined(CONFIG_CMC_PU2)
    checkboard,
    #endif
    NULL
};
  1. 配置可用的Flash区.
flash_init();
  1. 初始化内存分配函数.
mem_malloc_init();
  1. nand flash初始化
#if (CONFIG_COMMANDS & CFG_CMD_NAND)
puts("NAND");
nand_init(); /* 初始化NAND */
  1. 初始化环境变量
env_relocate();
  1. 外围设备初始化.
devices_init()
  1. I2C总线初始化
i2c_init();
  1. LCD初始化
drv_lcd_init();
  1. VIDEO初始化
drv_video_init();
  1. 键盘初始化
drv_keyboard_init();
  1. 系统初始化
drv_system_init();

初始化网络设备

初始化相关网络设备, 填写IP, MAC地址等.

进入主UBOOT命令行.

进入命令循环(即整个boot的工作循环), 接受用户从串口输入的命令, 然后进行相应的工作.

for (;;) {
    main_loop(); /* common/main.c */
}