资讯详情

Bootloader之uBoot简介

本文转载:http://blog.ednchina.com/hhuwxf/1915416/message.aspx

一、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则从地址0x从一万开始。嵌入式单板需要存储器件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从软件的角度来看,系统通常可以分为四个层次:

(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.一是典型的分区结构。[[Image:]]

图15.1 嵌入式Linux典型的分区结构在系统中

“Bootparameters一些可设置的参数存储在分区中,例如IP要传递给核心的地址、串口波特率、命令行参数等。正常启动时,Bootloader首先运行,然后将内核复制到内存中(一些内核可以直接在固态存储设备上运行),并在内存的固定地址设置传输到内核的参数,最后运行内核。内核启动移动后,它会挂接(mount)根文件系统(Root filesystem)启动文件系统中的应用程序。

  • 2. Bootloader的两个阶段

Bootloader启动过程的启动过程可分为单阶段(SingleStage)、多阶段(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
2
3
4
5
6
7
8
struct  tag_header { u32 size; u32 tag; };
<br> struct  tag { struct  tag_header hdr; union  { struct
tag_corecore; struct  tag_mem32mem; struct  tag_videotextvideotext;
struct  tag_ramdiskramdisk; struct  tag_initrdinitrd; struct
tag_serialnrserialnr; struct  tag_revisionrevision; struct
tag_videolfbvideolfb; struct  tag_cmdlinecmdline; <br> /* * Acorn
specific */ struct  tag_acornacorn; <br> /* * DC21285 specific
*/ struct  tag_memclkmemclk; } u; };

下面以设置内存标记、命令行标记为例说明参数的传递:

(1)设置标记 ATAG_CORE。

标记列表以标记 ATAG_CORE开始,假设Bootloader与内核约定的参数存放地址为0x30000100,则可以以如下代码设置标记 ATAG_CORE:

params = (struct tag *) 0x30000100; <br>params->hdr.tag = ATAG_CORE; params->hdr.size = tag_size (tag_core); <br>params->u.core.flags = 0; params->u.core.pagesize = 0; params->u.core.rootdev = 0; <br>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引导程序

<table fck__showtableborders?="">

Bootloader Monitor 描述 X86 ARM PowerPC

LILO 否 Linux磁盘引导程序 是 否 否

GRUB 否 GNU的LILO替代程序 是 否 否

Loadlin 否 从DOS引导Linux 是 否 否

ROLO 否 从ROM引导Linux而不需要BIOS 是 否 否

Etherboot 否 通过以太网卡启动Linux系统的固件 是 否 �

标签: 温度传感器型号86x86mm

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台