说说Bootloader(UBOOT)那些事儿

1、官方说bootloader

BootLoader是在操作系统内核运行之前运行。可以初始化硬件设备(配置CPU功能模块寄存器)、建立内存空间映射图(确认DDR是否工作正常),从而将系统的软硬件环境带到一个合适状态,以便为最终调用操作系统内核准备好正确的环境。在嵌入式系统中,通常并没有像BIOS那样的固件程序(注,有的嵌入式CPU也会内嵌一段短小的启动程序),因此整个系统的加载启动任务就完全由BootLoader来完成。在一个基于ARM7TDMI core的嵌入式系统中,系统在上电或复位时通常都从地址0x00000000处开始执行,而在这个地址处安排的通常就是系统的BootLoader程序。

其基本功能为: -初始化硬件(CPU,存储器,中断,RS232串口) -引导系统启动

  • 提供支持多种型号FLASH的驱动 -识别二进制、ELF32、pImage格式的Image -监控读写I/O,内存,寄存器、内存、外设测试功能等 -脚本语言支持(类似BASH脚本) -支持WatchDog,LCD logo,状态指示功能等

CPU刚上电启动的时候,一般连内存控制器都没有配置过,根本无法在内存中运行程序,更不可能处在Linux内核的启动环境中。为了初始化CPU及其他外设,使得Linux内核可以在系统主存中跑起来,并让系统符合Linux内核启动的必备条件,必须要有一个先于内核运行的程序,他就是所谓的引导加载程序(Boot Loader)。

从操作系统的角度看,Boot Loader 的总目标就是正确地调用内核来执行。 由于 Boot Loader 的实现依赖于 CPU 的体系结构,因此大多数 Boot Loader 都分为 stage1 和 stage2 两大部分。依赖于 CPU 体系结构的代码,比如设备初始化代码等,通常都放在 stage1 中,而且通常都用汇编语言来实现,以达到短小精悍的目的。而 stage2 则通常用C语言来实现,这样可以实现给复杂的功能,而且代码会具有更好的可读性和可移植性。

Boot Loader 的 stage1 通常包括以下步骤(以执行的先后顺序): stage1:基本的硬件初始化 这是 Boot Loader 一开始就执行的操作,其目的是为 stage2 的执行以及随后的 kernel 的执行准备好一些基本的硬件环境。它通常包括以下步骤(以执行的先后顺序): 1. 屏蔽所有的中断。为中断提供服务通常是 OS 设备驱动程序的责任,因此在 Boot Loader 的执行全过程中可以不必响应任何中断。中断屏蔽可以通过写 CPU 的中断屏蔽寄存器或状态寄存器(比如 ARM 的 CPSR 寄存器)来完成。 2. 设置 CPU 的速度和时钟频率。 3. RAM 初始化。包括正确地设置系统的内存控制器的功能寄存器以及各内存库控制寄存器等。 4. 初始化 LED。典型地,通过 GPIO 来驱动 LED,其目的是表明系统的状态是 OK 还是 Error。如果板子上没有 LED,那么也可以通过初始化 UART 向串口打印 Boot Loader 的 Logo 字符信息来完成这一点。 5. 关闭 CPU 内部指令/数据 cache。 stage 2:为加载 stage2 准备 RAM 空间 申请RAM 空间,对所申请的地址范围进行测试 stage2 通常包括以下步骤(以执行的先后顺序): 初始化本阶段要使用到的硬件设备。 检测系统内存映射(memory map)。 将 kernel 映像和根文件系统映像从 flash 上读到 RAM 空间中。 为内核设置启动参数。 调用内核。

一个嵌入式系统从软件角度来看分为三个层次: 1.引导加载程序 包括固化在固化中的boot程序(可选),和BootLoader两大部分 2.linux内核 特定于嵌入式平台的定制内核 3.文件系统 包括了系统命令和应用程序

BootLoader–>Boot Parameters–>Kernel–>Root Filesystem

为什么需要进行bootloader移植? 答:1.因为每种不同的CPU体系结构都有不同的BootLoader 2.BootLoader依赖于具体的嵌入式板级设备的配置

BootLoader启动过程可分为单阶段和多阶段(stage1、stage2),其中stage1完成初始化硬件,如CPU寄存器、内存控制器,为stage2准备内存空间。一般stage1是可以直接在nor flash中运行的,并将stage2复制到内存RAM中,设置堆栈,然后跳转到stage2(从这也可以看出stage2是在RAM中运行的,与stage1不同)

BootLoader的stage1通常包括以下步骤(汇编): 1.硬件设备初始化 如CPU寄存器、内存控制器 2.为加载BootLoader的stage2准备RAM空间 3.拷贝BootLoader的stage2到RAM空间中 4.设置好堆栈 为什么?为了跳转到stage2的入口,因为stage2大多数是用C语言写的 5.跳转到stage2的C入口点

BootLoader的stage2通常包括以下步骤(C代码): 1.初始化本阶段要使用到的硬件设备 各种设备,如网卡 2.将内核映像和根文件系统映像从flash上读到RAM中去 3.调用内核

Uboot :ftp://ftp.denx.de/pub/u-boot/

Uboot用于多种嵌入式CPU的BootLoader程序,支持多种嵌入式操作系统的引导。UBOOT目录结构: 1.Borad 与开发板有关的文件。第一个开发板都以一个子目录出现在当前目录中 2.Common 实现Uboot支持的命令 3.Cpu 与特定CPU架构相关的代码,支持的CPU对应一个子目录(注意CPU与开发板的区别) 3.Disk 对磁盘的支持 4.Doc 文档目录 5.Drivers Uboot支持的设备驱动程序 如各种网卡、串品、USB、支持CFI的Flash 6.Fs 文件系统的支持 7.Iclude Uboot使用的头文件。该目录下configs目录有与开发板相关的配置头文件 该目录下的asm目录有与CPU体系结构相关的头文件 8.Net 与网络协议相关的代码 各路协议的实现 9.Tools 生成Uboot的工具,如:mkimage,crc等等

Uboot编译分为两步 1.执行每种board相关的配置 如:make amdk_2420 2.编译生成uboot.bin文件 如:make CROSS_COMPILE=arm-linux-(指定正确路径)

编译注意两步 :

a. 连接地址 ../board/samsung/mini6410/u-boot.lds(链接文件)   +   config.mk 里面的偏移地址(根据flash大小定地址)TEXT_BASE = 0xc7e00000

b. ../cpu/s3c64xx/start.S

u-boot终极目的就是启动内核

a. 从flash读出内核UImage, 分区名不重要, 关键是代码中写死的分区起始地址。

b. 启动内核, do_bootm_linux。do_bootm_linux又分两步,设置启动参数,告诉内核一些参数(有一个起始地址,按固定格式写入),跳到内核入口地址再启动内核,内核启动后要读取设置的参数可以从起始地址读取。