免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1131 | 回复: 1

[BootLoader] Bootloader之uBoot简介 [复制链接]

论坛徽章:
0
发表于 2016-06-27 17:36 |显示全部楼层
一、Bootloader的引入
从前面的硬件实验可以知道,系统上电之后,需要一段程序来进行初始化:关闭WATCHDOG、改变系统时钟、初始化存储控制器、将更多的代码复制到内存中 等等。如果它能将操作系统内核(无论从本地,比如Flash;还是从远端,比如通过网络)复制到内存中运行,就称这段程序为Bootloader。
简单地说,Bootloader就是这么一小段程序,它在系统上电时开始执行,初始化硬件设备、准备好软件环境,最后调用操作系统内核。
可以增强Bootloader的功能,比如增加网络功能、从PC上通过串口或网络下载文件、烧写文件、将Flash上压缩的文件解压后再运行等──这就是 一个功能更为强大的Bootloader,也称为Monitor。实际上,在最终产品中用户并不需要这些功能,它们只是为了方便开发。
Bootloader的实现严重依赖于具体硬件,在嵌入式系统中硬件配置千差万别,即使是相同的CPU,它的外设(比如Flash)也可能不同,所以不可 能有一个Bootloader支持所有的CPU、所有的电路板。即使是支持CPU架构比较多的U-Boot,也不是一拿来就可以使用的(除非里面的配置刚好与你的板子相同),需要进行一些移植。
二、 Bootloader的启动方式
CPU上电后,会从某个地址开始执行。比如MIPS结构的CPU会从0xBFC00000取第一条指令,而ARM结构的CPU则从地址0x0000000 开始。嵌入式单板中,需要把存储器件ROM或Flash等映射到这个地址,Bootloader就存放在这个地址开始处,这样一上电就可以执行。
在开发时,通常需要使用各种命令操作Bootloader,一般通过串口来连接PC和开发板,可以在串口上输入各种命令、观察运行结果等。这也只是对开发 人员才有意义,用户使用产品时是不用接串口来控制Bootloader的。从这个观点来看,Bootloader可以分为两种操作模式 (Operation Mode):
(1)启动加载(Boot loading)模式。
上电后,Bootloader从板子上的某个固态存储设备上将操作系统加载到RAM中运行,整个过程并没有用户的介入。产品发布时,Bootloader工作在这种模式下。
(2)下载(Downloading)模式。
在这种模式下,开发人员可以使用各种命令,通过串口连接或网络连接等通信手段从主机(Host)下载文件(比如内核映像、文件系统映像),将它们直接放在内存运行或是烧入Flash类固态存储设备中。
板子与主机间传输文件时,可以使用串口的xmodem/ymodem/zmodem协议,它们使用简单,只是速度比较慢;还可以使用网络通过tftp、nfs协议来传输,这时,主机上要开启tftp、nfs服务;还有其他方法,比如USB等。
像Blob或U-Boot等这样功能强大的Bootloader通常同时支持这两种工作模式,而且允许用户在这两种工作模式之间进行切换。比如,U- Boot在启动时处于正常的启动加载模式,但是它会延时若干秒(这可以设置)等待终端用户按下任意键而将U-Boot切换到下载模式。如果在指定时间内没 有用户按键,则U-Boot继续启动Linux内核。
15.1.2 Bootloader的结构和启动过程

1. 概述

在移植之前先了解Bootloader的一些通用概念,对理解它的代码会有所帮助。
在一个嵌入式Linux系统中,从软件的角度通常可以分为4个层次:
(1)引导加载程序,包括固化在固件(firmware)中的 boot 代码(可选)和Bootloader两大部分。
有些CPU在运行Bootloader之前先运行一段固化的程序(固件,firmware),比如x86结构的CPU就是先运行BIOS中的固件,然后才运行硬盘第一个分区(MBR)中的Bootloader。
在大多嵌入式系统中并没有固件,Bootloader是上电后执行的第一个程序。
(2)Linux内核。
特定于嵌入式板子的定制内核以及内核的启动参数。内核的启动参数可以是内核默认的,或是由Bootloader传递给它的。
(3)文件系统。
包括根文件系统和建立于Flash内存设备之上的文件系统。里面包含了Linux系统能够运行所必需的应用程序、库等,比如可以给用户提供操作Linux的控制界面的shell程序,动态连接的程序运行时需要的glibc或uClibc库,等等。
(4)用户应用程序。
特定于用户的应用程序,它们也存储在文件系统中。有时在用户应用程序和内核层之间可能还会包括一个嵌入式图形用户界面。常用的嵌入式 GUI 有:Qtopia 和 MiniGUI 等。
显然,在嵌入系统的固态存储设备上有相应的分区来存储它们,图15.1是一个典型的分区结构。 [[Image:]]
图15.1 嵌入式Linux系统中的典型分区结构
“Boot parameters”分区中存放一些可设置的参数,比如IP地址、串口波特率、要传递给内核的命令行参数等。正常启动过程中,Bootloader首先 运行,然后它将内核复制到内存中(也有些内核可以在固态存储设备上直接运行),并且在内存某个固定的地址设置好要传递给内核的参数,最后运行内核。内核启 动之后,它会挂接(mount)根文件系统(“Root filesystem”),启动文件系统中的应用程序。

2. Bootloader的两个阶段

Bootloader的启动过程启动过程可以分为单阶段(Single Stage)、多阶段(Multi-Stage)两种。通常多阶段的Bootloader能提供更为复杂的功能,以及更好的可移植性。从固态存储设备上启动的Bootloader大多都是 2 阶段的启动过程。这从前面的硬件实验可以很好地理解这点:第一阶段使用汇编来实现,它完成一些依赖于 CPU 体系结构的初始化,并调用第二阶段的代码。第二阶段则通常使用C语言来实现,这样可以实现更复杂的功能,而且代码会有更好的可读性和可移植性。
一般而言,这两个阶段完成的功能可以如下分类,但这不是绝对的:
(1)Bootloader第一阶段的功能。
硬件设备初始化。
为加载Bootloader的第二阶段代码准备RAM空间。
拷贝Bootloader的第二阶段代码到 RAM 空间中。
设置好栈。
跳转到第二阶段代码的C入口点。

在第一阶段进行的硬件初始化一般包括:关闭WATCHDOG、关中断、设置CPU的速度和时钟频率、RAM初始化等。这些并不都是必需的,比如S3C2410/S3C2440的开发板所使用的U-Boot中,就将CPU的速度和时钟频率的设置放在第二阶段。
甚至,将第二阶段的代码复制到RAM空间中也不是必需的,对于NOR Flash等存储设备,完全可以在上面直接执行代码,只不过这相比在RAM中执行效率大为降低。
(2)Bootloader第二阶段的功能。
初始化本阶段要使用到的硬件设备。
检测系统内存映射(memory map)。
将内核映像和根文件系统映像从Flash上读到RAM空间中。
为内核设置启动参数。
调用内核。

为了方便开发,至少要初始化一个串口以便程序员与Bootloader进行交互。
所谓检测内存映射,就是确定板上使用了多少内存,它们的地址空间是什么。由于嵌入式开发中,Bootloader多是针对某类板子进行编写,所以可以根据板子的情况直接设置,不需要考虑可以适用于各类情况的复杂算法。
Flash上的内核映像有可能是经过压缩的,在读到RAM之后,还需要进行解压。当然,对于有自解压功能的内核,不需要Bootloader来解压。
将根文件系统映像复制到RAM中,这不是必需的。这取决于是什么类型的根文件系统,以及内核访问它的方法。
为内核设置启动参数将在下一小节介绍。
将内核存放在适当的位置后,直接跳到到它的入口点即可调用内核。调用内核之前,下列条件要满足:
(1)CPU 寄存器的设置。
R0=0
R1=机器类型ID;对于ARM结构的CPU,其机器类型ID可以参见 linux/arch/arm/tools/mach-types。
R2=启动参数标记列表在 RAM 中起始基地址

(2)CPU工作模式。
必须禁止中断(IRQs和FIQs)
CPU 必须 SVC 模式

(3)Cache 和 MMU 的设置。
MMU 必须关闭
指令 Cache 可以打开也可以关闭
数据 Cache 必须关闭

如果用C语言,可以像下列示例代码一样来调用内核:
void (*theKernel)(int zero, int arch, u32 params_addr) = (void (*)(int, int, u32))KERNEL_RAM_BASE; …… theKernel(0, ARCH_NUMBER, (u32) kernel_params_start);

3. Bootloader与内核的交互

Bootloader与内核的交互是单向的,Bootloader将各类参数传给内核。由于它们不能同时运行,传递办法只有一个:Bootloader将参数放在某个约定的地方之后,再启动内核,内核启动后从这个地方获得参数。
除了约定好参数存放的地址外,还要规定参数的结构。Linux 2.4.x 以后的内核都期望以标记列表(tagged list)的形式来传递启动参数。标记,就是一种数据结构;标记列表,就是挨着存放的多个标记。标记列表以标记ATAG_CORE 开始,以标记ATAG_NONE 结束。标记的数据结构为tag,它由一个tag_header结构和一个联合(union)组成。tag_header结构表示标记的类型及长度,比如是 表示内存还是表示命令行参数等。对于不同类型的标记使用不同的联合(union),比如表示内存时使用tag_mem32,表示命令行时使用 tag_cmdline。数据结构tag和tag_header定义在Linux内核源码的include/asm/setup.h头文件中:
1        struct tag_header { u32 size; u32 tag; };
2        <br>struct tag { struct tag_header hdr; union { struct
3        tag_corecore; struct tag_mem32mem; struct tag_videotextvideotext;
4        struct tag_ramdiskramdisk; struct tag_initrdinitrd; struct
5        tag_serialnrserialnr; struct tag_revisionrevision; struct
6        tag_videolfbvideolfb; struct tag_cmdlinecmdline; <br>struct tag_acornacorn; <br>struct tag_memclkmemclk; } u; };
下面以设置内存标记、命令行标记为例说明参数的传递:
(1)设置标记 ATAG_CORE。
标记列表以标记 ATAG_CORE开始,假设Bootloader与内核约定的参数存放地址为0x30000100,则可以以如下代码设置标记 ATAG_CORE:
params = (struct tag *) 0x30000100;
       params->hdr.tag = ATAG_CORE;
params->hdr.size = tag_size (tag_core);
params->u.core.flags = 0;
       params->u.core.pagesize = 0;
params->u.core.rootdev = 0;
       params = tag_next (params);
其中,tag_next定义如下,它指向当前标记的末尾:
#define tag_next(t)((struct tag *)((u32 *)(t) + (t)->hdr.size))
(2)设置内存标记。
假设开发板使用的内存起始地址为0x30000000,大小为0x4000000,则内存标记可以如下设置:
params->hdr.tag = ATAG_MEM;
params->hdr.size = tag_size (tag_mem32);
params->u.mem.start = 0x30000000;
params->u.mem.size = 0x4000000;
params = tag_next (params);
(3)设置命令行标记。
命令行就是一个字符串,它被用来控制内核的一些行为。比如"root=/dev/mtdblock2 init=/linuxrc console=ttySAC0"表示根文件系统在MTD2分区上,系统启动后执行的第一个程序为/linuxrc,控制台为ttySAC0(即第一个串口)。
命令行可以在Bootloader中通过命令设置好,然后如下构造标记传给内核:
char *p = "root=/dev/mtdblock2 init=/linuxrc console=ttySAC0";
params->hdr.tag = ATAG_CMDLINE;
params->hdr.size = (sizeof (struct tag_header) + strlen (p) + 1 + 4) >> 2;
strcpy (params->u.cmdline.cmdline, p);
params = tag_next (params);
(4)设置标记ATAG_NONE。
标记列表以标记ATAG_NONE结束,如下设置:
params->hdr.tag = ATAG_NONE;
params->hdr.size = 0;
常用Bootloader介绍
现在Bootloader种类繁多,比如x86上有LILO、GRUB等。对于ARM架构的CPU,有U-Boot、Vivi等。它们各有特点,下面列出Linux的开放源代码的Bootloader及其支持的体系架构,如表15.1所示。
开放源码的Linux引导程序

Bootloader        Monitor        描述        X86        ARM        PowerPC
LILO        否        Linux磁盘引导程序        是        否        否
GRUB        否        GNU的LILO替代程序        是        否        否
Loadlin        否        从DOS引导Linux        是        否        否
ROLO        否        从ROM引导Linux而不需要BIOS        是        否        否
Etherboot        否        通过以太网卡启动Linux系统的固件        是        否        否
LinuxBIOS        否        完全替代BUIS的Linux引导程序        是        否        否
BLOB        是        LART等硬件平台的引导程序        否        是        否
U-Boot        是        通用引导程序        是        是        是
RedBoot        是        基于eCos的引导程序        是        是        是
Vivi        是        Mizi公司针对SAMSUNG的ARM CPU设计的引导程序        否        是        否

对于本书使用的S3C2410/S3C2440开发板,U-Boot和Vivi是两个好选择。Vivi是Mizi公司针对SAMSUNG的ARM架构 CPU专门设计的,基本上可以直接使用,命令简单方便。不过其初始版本只支持串口下载,速度较慢。在网上出现了各种改进版本:支持网络功能、USB功能、 烧写YAFFS文件系统映像等。U-Boot则支持大多CPU,可以烧写EXT2、JFFS2文件系统映像,支持串口下载、网络下载,并提供了大量的命 令。相对于Vivi,它的使用更复杂,但是可以用来更方便地调试程序。

论坛徽章:
12
数据库技术版块每日发帖之星
日期:2015-07-08 22:20:00IT运维版块每日发帖之星
日期:2016-05-01 06:20:00IT运维版块每周发帖之星
日期:2016-03-07 16:27:44IT运维版块每月发帖之星
日期:2016-03-07 16:25:25IT运维版块每日发帖之星
日期:2016-02-21 06:20:00IT运维版块每日发帖之星
日期:2016-02-20 06:20:00数据库技术版块每日发帖之星
日期:2016-02-06 06:20:00IT运维版块每日发帖之星
日期:2016-01-30 06:20:00IT运维版块每日发帖之星
日期:2016-01-03 06:20:00IT运维版块每日发帖之星
日期:2015-11-29 06:20:00IT运维版块每日发帖之星
日期:2015-08-10 06:20:00数据库技术版块每日发帖之星
日期:2016-06-16 06:20:00
发表于 2016-06-28 17:31 |显示全部楼层
膜拜一下牛人!
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

SACC2019中国系统架构师大会

【数字转型 架构演进】SACC2019中国系统架构师大会,7折限时优惠重磅来袭!
2019年10月31日~11月2日第11届中国系统架构师大会(SACC2019)将在北京隆重召开。四大主线并行的演讲模式,1个主会场、20个技术专场、超千人参与的会议规模,100+来自互联网、金融、制造业、电商等领域的嘉宾阵容,将为广大参会者提供一场最具价值的技术交流盛会。

限时七折期:2019年8月31日前


----------------------------------------

大会官网>>
  

北京盛拓优讯信息技术有限公司. 版权所有 16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122
中国互联网协会会员  联系我们:huangweiwei@it168.com
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP