在说bootloader之前我们在复习一下板卡的启动流程。
一.板卡启动流程(查看串口打印信息可以很好的跟踪启动过程)
1. 上电启动bootloader
- 硬件基本初始化
- 自搬移到内存
- 搬移内核到内存
- 传递内核启动参数(parmer_struct 或taglist)
2. 加载内核
- a. 自解压内核 decompess //arch/arm/boot/compressed/head.S
- b. 运行内核汇编部分 head.S 入口 stext //arch/arm/kernel/head.S
- 检测合法性(CPU 类型,机器类型)
- c. 运行内核C部分 start_kernel //init/main.c
- CPU,机器参数的安装 setup_arch
- 中断,定时,终端,内存等最基本的初始化
- 创建核心进程 kernel_init运行,启动多任务调度,原父进程空转cpu_ide
3. 挂载rootfs (mount_root)
4. 运行应用程序 //第一个应用程序是init (由u-boot的bootargs里的init=/linuxrc来指定)
- a. 运行启动脚本 (run_init_process("/etc/init.d/rcS")) //由init来解析脚本执行
- b. 其它应用程序 //一般添加在脚本的最后(如在rcS末尾加 ./app)
二.bootloader 基本概念
- Bootloader是硬件启动的引导程序,是运行操作系统的前提;
- 在操作系统内核或用户应用程序运行之前运行的一小段代码。对软硬件进行相应的初始化和设定,为最终运行操作系统准备好环境;
- 在嵌入式系统中,整个系统的启动加载任务通常由Bootloader来完成。
bootloader的作用说白了就是为了启动内核,顺带再初始化一些必要的硬件。
三.bootloader 特点
- Bootloader不属于操作系统,一般采用汇编语言和C语言开发。需要针对特定的硬件平台编写。
- 在移植系统时,首先为开发板移植Bootloader。
- Bootloader不但依赖于CPU的体系结构,而且依赖于嵌入式系统板级设备的配置。
它首要要稳定,所以一般都使用汇编和c开发,我们平时的使用中也可以体会到,uboot功能多了,会为我们的开发提高效率。
四.bootlader操作模式
- 自启动模式:
- 交互模式:
bootloader一方面为我们开发提供一些便捷的指令,其主要的功能就是为启动内核做准备并成功加载内核。
五.常用的bootloader
作为了解就行了,最为常用的就是u-boot.
u-boot(Universal Boot Loader)是德国DENX小组开发的用于多种嵌入式CPU的bootloader程序。遵循GPL条款。
六.u-boot常用命令
当我们通过串口进入命令模式,使用help就会打印出boot支持的指令,指令很多。
分类:环境设置、数据传输、存储器访问、加载运行
1.环境设置
- printenv (pri) 显示所有环境变量
- setenv 设置新的环境变量
- saveenv 将当前定义的所有的环境变量值存入flash中
2.数据传输
- tftp 通过网络下载程序
例:
U-boot # setenv ethaddr 11:22:33:44:55:66
U-boot # setenv ipaddr 192.168.1.100
U-boot # setenv serverip 192.168.1.10
U-boot # tftp 41000000 zImage
3.存储器访问
- protect 对Nor Flash(按字节访问,启动引导)写保护
- protect on 0 10000 // 对区间[0x0, 0x10000]进行写保护
- protect off 0 10000 //对上述区间取消写保护
- erase 擦除Nor FLASH
- erase all 擦除FLASH所有的扇区
- erase 0 10000 把FLASH区间 [0x0, 0x10000]擦除
- Nand(按块访问,存储大数据)相关命令
- nand read addr off size
- nand write addr off size
- nand erase [clean] [off size]
- movi 命令
- movi init ---初始化eMMC并显示相关信息
- movi read u-boot/kernel addr
- movi write u-boot/kernel addr
- movi read rootfs addr size
- movi write rootfs addr size
4.加载运行
- bootcmd 自启动命令
- 如果定义了该变量,在自启动模式下将会执行该环境变量中的命令。
- U-boot # setenv bootcmd tftp 41000000 uImage\; bootm 41000000
- U-boot # saveenv
- go addr 执行内存中的二进制代码,简单的跳转到指定地址
- bootm kernel-addr ramdisk-addr dtb-addr
- 引导内核为内核传参,其中内核和ramdisk通常为mkimage处理过的二进制文件。
七.u-boot的配置编译
在自己移植boot之前我们可以先编译下别人弄好的boot.
1.首先通过共享文件夹将编译好的u-boot先拷贝到虚拟机,进行解压
tar -xvf u-boot-2013.01.tar.bz2
2../bulid.sh 执行写好的脚本文件(如果报错,指定下交叉工具链环境变量)
或者使用单个指令
- 在Makefile中指定使用的交叉工具链
- 配置u-boot: make fs4412_config
- 编译: make
3.通过sd卡,usb,或者tftp进行u-boot的下载。
注:u-boot编译生成的映像文件
文件名称 | 说明 |
u-boot.map | U-boot映像的符号表(方便源码跟踪) |
u-boot | U-Boot映像的ELF格式 |
u-boot.bin | U-Boot映像原始的二进制格式(烧录用) 注意exynos4412需加入BL1 (三星剥离出去的一段代码) |
u-boot.srec | U-Boot映像的S-Record格式 |
八. u-boot 目录结构
- 平台相关
arch, board, include…
- 平台无关(共用)
common, net, fs, drivers…
- 工具和文档
tools, doc
九.u-boot 启动步骤 (重点)
1.阶段一(汇编)
设置为SVC模式,关闭中断,MMU,看门狗 //准备
基本硬件设备初始化 //初始化时钟,串口,flash,内存 见cpu/arm_cortexa8/start.S 的 cpu_init_crit
自搬移到内存 //copy_uboot_to_ram 或relocate
设置好栈 //stack_setup
跳转到第二阶段代码入口 //ldr pc, _start_armboot
2.阶段二(C语言)
大部分硬件初始化 //lib_arm/board.c/start_armboot -> init_sequence
搬移内核到内存 //common/main.c main_loop -> getenv ("bootcmd") bootdelay >= 0 && s && !abortboot (bootdelay)) 下的 run_command (bootcmd)
运行内核
两个阶段,两次搬移,两次初始化,可以通过程序进行整个流程的确认。
//---u-boot.lds 或 u-boot.map 指明了第一条指令位置
arch/arm/cpu/armv7/start.o
问题:
1.上电系统从哪里开始,启动代码放在什么位置?
上电后,有的系统可通过硬件管脚的电平来选择从Nandflash还是从Norflash启动。这里是指定从Nandflash启动的
启动代码U-boot 是不能放在RAM中的,因掉电就消失了。
2.boot第一件是为何是设置为SVC模式?
为安全性,CPU本身提出了多种模式来实现安全性,和效率兼顾。
SVC模式就是CPU对资源的一种保护。在普通用户模式是不能访问到的。只有切换到SVC模式才能访问。
3.为什么要并关闭中断,MMU,看门狗等?
初始时为安全性,把系统单纯化, 排除掉别的干扰,u-boot实现单纯的搬移代码的功能。
所以关掉中断,避免中断打断带来的保存返回的问题。 关掉MMU,因为u-boot软件是硬件地址访问。
根本没有用到内存地址映射。 看门狗如果没关,硬件上默认到时会复位的。 所以要关掉
4.为何设备初始引导不能用C代码,要用汇编代码?
特殊指令,如操作协处理器, MMC CASH ,切换SVC模式, 等C不能做到。
另C是需要先准备栈的.
5.U-BOOT为何要搬移到RAM中运行?
在RAM中速度更快,但因掉电就没了,所以要保存在Nandflash或Norflash中。
如果搬移,直接在Norflash上运行也是可以的。 Nandflash因是按块操作的,不太合适直接运行。
6.U-boot如何传递参数到内核。 移植时要注意哪些?
方式一 采用param_struct: (我们现在使用的是这种方式) 见common/cmd_boot.c go命令的优化
方式二 采用taglist:
十.移植的方法
- 善用对比软件Beyond
- 选择官方源码版本下载, 配置编译
a. 指定交叉编译工具链
b. 指定cpu 和board(参考最类似配置如origen)
c. 编译
- 实现串口信息输出
a. 跟踪运行路径(led点灯法)
b. 串口输出(检查uart初始化相关部分代码 见lowlevel_init.s)
- 网卡移植(实现能用tftp nfs 方便开发调试)
a. 寄存器地址
b. 参数设置
- FLASH移植(实现能下载软件到FLASH,产品能离线运行)
先下载源码,如果有移植好的u-boot可以与官网源码进行对比。boot一般做好后不会经常更改。