嵌入式 Bootloader 的移植入门
2019-12-29
关键字:嵌入式底层开发、嵌入式启动流程
什么是Bootloader?
简单来理解,Bootloader 就是嵌入式开发板上电后所运行的第一个程序。它的作用是用来启动硬件与初始化硬件设置的。同时 Bootloader 还可以设置启动参数 bootargs 并通过 bootcmd 中的命令来引导、加载内核程序。
Bootloader 是嵌入式系统中的第一个程序,它是运行操作系统的前提。
Bootloader 一般采用汇编与C语言开发。它不属于操作系统,它仅仅是一个普通的裸机程序而已,因此,Bootloader 程序通常需要针对特定的硬件平台来编写。
Bootloader 可分为两种模式:
1、自启动模式;
2、交互模式;
自启动模式是指板子在上电过后完全不需要用户去干预,Bootloader 自动完成上电启动并加载内核的过程。
交互模式则是在 Bootloader 启动完成末期通常会有一个倒计时提示,用户在倒计时结束前通过特定的按键命令让 Bootloader 中止后面的加载内核的过程,从而进入到与 Bootloader 通过命令交互的机制。这种模式可以在我们的内核有问题,需要停留在 Bootloader 阶段操作时使用。
Bootloader 的各类繁多,但目前嵌入式领域最常用的 Bootloader 程序是 'U-boot'。这种 Bootloader 通用性好,可以同时支持 x86、arm与PowerPC架构的芯片。
U-boot 中关于环境变量的命令有三条:
1、printenv
2、setenv
3、saveenv
这三条命令分别对应u-boot环境变量的查看、设置与保存。
在 u-boot 中可以通过 tftp 命令下载远端主机的文件。
tftp 0x410000000 uImage
以上命令表示将远端的 uImage 文件下载到 0x41000000 地址的内存中去。值得注意的是,这个 tftp 下载中,关于远端主机的信息其实是记载在环境变量中的,如 ethaddr, ipaddr, serverip。当然最最重要的还是远端主机要能正常提供 tftp 服务。
Nor Flash 是按字节访问数据的。Nand Flash 则是按块访问数据的。三星则推出了另一种兼容性较好的 Flash,称为 eMMC。
bootcmd 就是自启动命令。作用就是记载在 Bootloader 在初始化完成去引导加载内核时该执行的命令。Bootloader 在执行完成后会去执行记载在 bootcmd 中的命令集。
如果想在 Bootloader 阶段运行裸机程序,则可以使用 go 命令来执行内存中某个位置上的程序。如:go 0x4100000
u-boot 编译产物通常有以下四种:
1、u-boot.map
镜像符号表,方便源码跟踪。
2、u-boot
镜像的 ELF 格式。
3、u-boot.bin
镜像原始的二进程格式,用于烧录的。也是最重要的编译产物。
4、u-boot.srec
镜像的 s-record 格式。
BootLoader的启动流程
这里以 u-boot 的启动流程来讲解。
毫无疑问,Bootloader 程序是存储在 Flash 中的。Bootloader 程序在启动时,首先会去初始化时钟模块,其次是串口、Flash、内存等。同时,考虑到在某些模块初始化时可能会花较多的时间的情况,通常会在初始化这些硬件设备之前先将看门狗、中断等关闭掉。
嵌入式板子在启动时会将 Bootloader 程序与 kernel 程序从 Flash 中搬移到内存中,然后再去执行相应的代码。
U-boot 程序由汇编语言与 C 语言混合编写而成。其中上电之初那部分由汇编语言来实现,主要是一些基本初始化之类的工作。剩余大部分的初始化工作则是交由C语言编写而成的代码来实现。U-boot 的启动可以分成两个阶段:stage1 与 stage2。stage1 是由汇编语言编写而成,通常用于初始化与CPU紧密相关的参数。这个阶段的代码主要在 starg.S 文件中。stage2 则是由C语言编写的,就是我们经常会见到的 u-boot 代码了。
Bootloader的开发流程
要擅于使用 Beyond Compare 对比工具。
去官网选择相应版本下载并编译。这一过程主要就是指定交叉编译链与指定cpu与平台。
接下来是实现串口信息的输出。
网卡移植。
Flash移植。
bootargs 释义
以下是一段 bootargs 的示例:
console=ttySAC2,115200 init=/linuxrc root=/dev/nfs rw nfsroot=192.168.7.21:/opt/4412/rootfs ip=192.168.77.22
bootargs 是 uboot 传递给内核的一串字符串,这串字符串记载了内核的启动参数。
console=xxx 表示告诉内核调试信息往哪个设备输出。
init=xxx 表示告诉内核第一个进程是谁。
root=xxx 表示告诉内核根文件系统在哪里。上面示例中 /dev/nfs 表示根文件系统在网络端。nfsroot 表示网络端的根文件系统的路径。
ip=xxx 表示告诉内核开机的时候的 IP 地址是多少,可以理解为是静态分配设备IP。