0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

详谈嵌入式之Bootloader

Q4MP_gh_c472c21 来源:lq 2019-02-04 15:27 次阅读

一、Bootloader 的引入

1.1 Bootloader 的引入

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

Bootloader并不是只有Linux才需要,是几乎所有的运行操作系统的设备都必须具备的。 PC电脑的BIOS就是bootloader的一部分,对于Linux PC来说:Bootloader = BIOS + GRUB/LILO。

1.2 嵌入式 Linux 系统软件结构与分布

一般情况下嵌入式 Linux 系统中软件主要由以下几个部分组成:

1.引导加载程序:其中包括内部ROM中的固化启动代码和bootloader两部分。固化ROM 是厂家在芯片生产时固化,用于引导 bootloader。

2.Linux Kernel 和Drivers。

3.文件系统:包括根文件系统和建立于Flash 内存设备之上的文件系统(ext4、UBI、 CRAMFS 等)。它是提供管理系统的各种配置文件以及系统执行用于应用程序的良好运行环 境的载体。

4.应用程序:用于自定义的应用程序,存放于文件系统之中。 在Flash 存储器中,上面四个部分的分布如图 1 所示:

图1:嵌入式Linux的软件分布

但是以上只是大部分情况下的分布,也有一些根文件系统可能是 initramfs,被一起压缩到了内核映像里,或者没有 bootloader 参数区等。

二、Bootloader 介绍

2.1 Bootloader 的功能

Bootloader是在操作系统内核运行之前运行的一段小程序,通过它我们可以初始化硬件设备,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核准备好 正确的环境,最后从别处(Flash、以太网UART 等)载入内核映像并跳到入口地址运行。

简单的说,Bootloader 就是这么一小段程序,它在系统上电时开始执行,初始化硬件设备、准备好软件环境,最后调用操作系统内核。

可以增强 Bootloader 的功能,比如增加网络功能、从 PC 上通过串口或网络下载文件、 烧写文件、将 Flash 上压缩的文件解压后再运行等,这就是一个功能更为强大的 Bootloader, 也称为 Monitor。实际上,在最终产品中用户并不需要这些功能,他们只是为了方便开发。

2.2 Bootloader 的特点

由于 Bootloader 直接操作硬件,因此它严重依赖于硬件,且依据所引导的操作系统的不同而不同。在嵌入式世界中建立一个通用的 Bootloader 几乎是不可能的,而有可能的是 让一个 Bootloader 代码支持多种不同的架构和操作系统,并让他方便移植。Bootloader的启动过程通常是多阶段的,这样既能够提供复杂的功能,又具有更好的可移植性。

大多数 Bootloader 都包含两种不同的操作模式:本地加载模式和远程下载模式。远程下载模式只对开发人员才有意义。

Bootloader 都映射在 CPU复位后运行的第一条指令的地址处,以保证系统上电或复位 后首先执行 Bootloader。

2.3 Bootloader 的分类

首先区分一下“Bootloader”和“Monitor”的概念:严格的讲,“Bootloader”只是引 导设备并执行主程序的固件;而“Monitor”还提供更多的命令行接口,可进行调试、读写内存、烧写 Flash、配置环境变量等。“Monitor”在嵌入式系统开发过程中可以提供更好的调试功能,开发完成后,就完全设置成一个“Bootloader”了。所以习惯上将他们统称为 Bootloader。表 2.1 为常见的开放源码的 Linux 引导程序。

表2.1 常见的开放源码 Linux 引导程序

其中 U-Boot 支持大多 CPU,可以烧写 EXT2、JFFS2 文件系统映像,支持串口下载、网络 下载,并提供了大量的命令。

2.4 Bootloader 的启动模式

Bootloader 的主要功能是引导操作系统,但在开发时,通常需要使用各种命令操作 Bootloader,一般通过串口来连接 PC 和开发板,可以在串口上输入各种命令、观察运行结果等。这也只是对开发人员才有意义,用户使用产品时是不用接串口来控制 Bootloader 的。 从这个角度可以将 Bootloader 分为启动加载(Boot loading)模式和下载(Down loading)模 式。

2.4.1 启动加载(Bootloading)模式

这种模式也称为"自主"(Autonomous)模式。也即 Boot Loader 从目标机上的某个固态存储设备上将操作系统加载到 RAM 中运行,整个过程并没有用户的介入。这种模式是 Boot Loader 的正常工作模式,因此在嵌入式产品发布的时侯,Bootloader显然必须工作在这种模 式下。

2.4.2 下载(Downloading)模式

在这种模式下,目标机上的 Boot Loader 将通过串口连接或网络连接等通信手段从主机(Host)下载文件,比如:下载内核映像和根文件系统映像等。从主机下载的文件通常首先 被 Boot Loader 保存到目标机的 RAM 中,然后再被 Boot Loader 写到目标机上的 FLASH 类固 态存储设备中。Boot Loader 的这种模式通常在第一次安装内核与根文件系统时被使用;此 外,以后的系统更新也会使用Boot Loader的这种工作模式。工作于这种模式下的Boot Loader 通常都会向它的终端用户提供一个简单的命令行接口。

像 Blob 或 U-Boot 等这样功能强大的 Boot Loader 通常同时支持这两种工作模式,而且 允许用户在这两种工作模式之间进行切换。比如,Blob 在启动时处于正常的启动加载模式, 但是它会延时 10 秒等待终端用户按下任意键而将 blob 切换到下载模式。如果在 10 秒内没 有用户按键,则 blob 继续启动 Linux 内核。

2.4.3 下载模式之网络启动方式

这种方式开发板不需要配置较大的存储介质(跟无盘工作站有点类似),但是使用这种 启动方式之前,需要把 Bootloader 安装到板上的 EPPROM 或者 Flash 中。Bootloader 通过以 太网接口远程下载 Linux 内核映像或者文件系统到 RAM 中运行。这种方式对于嵌入式系统开发来说非常重要。

使用这种方式的前提条件,就是目标板有串口、以太网接口、USB 接口或者其他链接方式:串口一般作为控制台,同时可以用来下载内核映像和 RAMDISK 文件系统;用网络接口 来挂载 NFS 文件系统;也可以使用 USB 接口虚拟成以太网口来通讯。

使用网络启动方式,还需要在服务器上配置启动相关网络服务:使用 TFTP 服务为 Bootloader 客户端提供文件下载功能;DHCP 服务动态为 Bootloader 设置 IP 地址,配置网络 参数。网络启动方式如图 2.1 所示。

图2.1 网络方式启动系统

2.5 Bootloader 的启动流程

Bootloader 的启动过程可以分为单阶段(Single Stage)、多阶段(Multi-Stage)两种。通 常多阶段的 Bootloader 能提供更为复杂的功能以及更好的可移植性。从固态存储设备上启动 的Bootloader 大多都是两个阶段的启动过程。第一阶段使用汇编来实现,它完成一些依赖于 CPU 体系结构的初始化,并调用第二阶段的代码;第二阶段则通常使用 C 语言来实现,这样 可以实现更复杂的功能,而且代码会有更好的可读性和可移植性。

一般而言,这两个阶段完成的功能可以如下分类:

2.5.1 Bootloader 第一阶段的功能

Bootloader 在第一阶段主要完成以下功能:

硬件设备初始化;

为加载 Bootloader 的第二阶段代码准备 RAM 空间;

复制 Bootloader 的第二阶段代码到 RAM 空间中;

设置好栈;

跳转到第二阶段代码的 C 入口点;

在第一阶段进行的硬件初始化一般包括:关闭 WATCHDOG、关中断、设置 CPU 的速度 和时钟频率、RAM 初始化等。这些并不都是必须的,比如 S3C2410/S3C2440 的开发板所使 用的 U-Boot 中,就将 CPU 的速度和时钟频率放在第二阶段进行设置。

甚至,将第二阶段的代码复制到 RAM 空间也不是必须的,对于 Nor Flash 等支持 XIP 的 存储设备,完全可以在上面直接执行代码,只不过相比在 RAM 中执行效率大为降低。

2.5.2 Bootloader 第二阶段的功能

Bootloader 在第二阶段主要完成以下功能:

初始化本阶段要使用到的硬件设备;

检测系统内存映射(Memory map);

将内核映像和根文件系统映像从 Flash 上读到 RAM 空间中;

为内核设置启动参数;

调用内核;

为了方便开发,只要要初始化一个串口以便程序员与 Bootloader 进行交互。

所谓检测内存映射,就是确定板上使用了多少内存、他们的地址空间是什么。由于嵌入 式开发中的 Bootloader 多是针对某类板子进行编写,所以可以根据板子的情况直接设置,不 需要考虑可以适用于各类情况的复杂算法

Flash 上的内核映像有可能是经过压缩的,在读到 RAM 之后,还需要进行解压。当然, 对于有自解压功能的内核,不需要Bootloader 来解压。

将根文件系统映像复制到 RAM 中并不是必须的,这取决于是什么类型的根文件系统,以及内核访问它的方法。

将内核放在适当的位置后,在跳入执行内核之前,需要根据内核启动的需求,配置相应 的启动参数。如 Linux 内核的启动要求如表 2.2 所示。

表2.2 Linux 内核启动条件

在 C 语言中,可以像下列示例代码一样来调用内核:

void (*theKernel)(int zero, int arch, u32params_addr) = (void (*)(int, int, u32))KERNEL_RAM_BASE;

theKernel(0,ARCH_NUMBER, (u32)kernel_params_start);

2.6 Bootloader 与内核的交互

U-Boot 与内核之间的交互是单向的,U-Boot 将各类参数传递给内核。由于他们(U-Boot 与内核)不能同时运行,传递的办法只有一个:U-Boot 将参数放在放在某个约定的地方之 后,再启动内核,内核启动后从这个地方获得参数。

除了约定好参数存放的地址外,还要规定参数的结构。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 头文件中(在 U-Boot 的 include/asm-arm/目录下的 setup.h 中也有定义),如下所示:

struct tag_header {

u32 size;

u32 tag;

};

struct tag {

struct tag_header hdr;

union {

struct tag_core core;

struct tag_mem32 mem;

struct tag_videotext videotext;

struct tag_ramdisk ramdisk;

struct tag_initrd initrd;

struct tag_serialnr serialnr;

struct tag_revision revision;

struct tag_videolfb videolfb;

struct tag_cmdline cmdline;

/*

* Acorn specific

*/

struct tag_acorn acorn;

/*

* DC21285 specific

*/

struct tag_memclk memclk;

} u;

};

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

2.6.1 设置标记ATAG_CORE

标记列表以标记 ATAG_CORE 开始,其结构体定义如下:

/* The list must start with an ATAG_COREnode */

#define ATAG_CORE 0x54410001

struct tag_core {

u32 flags; /* bit 0 =read-only */

u32 pagesize;

u32 rootdev;

};

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

#define tag_next(t) ((struct tag *)((u32*)(t) + (t)->hdr.size))

static struct tag *params; //定义全局变量 params,为 struct tag 结构

static void setup_start_tag (bd_t *bd)

{

params = (struct tag *) bd->bi_boot_params; //bi_boot_params =0x30000100

params->hdr.tag = ATAG_CORE;// ATAG_CORE 在前面定义,指定标记类型

params->hdr.size = tag_size (tag_core); //计算标记大小

// 设置 ATAG_CORE 标记的内容

params->u.core.flags = 0;

params->u.core.pagesize = 0;

params->u.core.rootdev = 0;

params = tag_next (params);

}

其中,tag_next定义为指向当前标记的末尾。

2.6.2 设置内存标记

内存标记 tag_mem32 的定义如下:

struct tag_mem32 {

u32 size; // 内存的大小

u32 start; /* physical start address */

};

假设开发板使用的内存起始地址为 0x30000000,大小为 0x4000000,则内存标记可以如 下设置:

static void setup_memory_tags (bd_t *bd)

{

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);

}

2.6.3 设置命令行标记

命令行就是一个字符串,它被用来控制内核的一些行为。比如“root=/dev/mtdlock2 init=/linuxrc console=ttySAC0”表示根文件系统在 MTD2 分区上,系统启动后执行的第一个程序为/linuxrc,控制台为 ttySAC0。

命令行可以在 Bootloader 中通过命令设置好,然后按如下构造标记传给内核。

char *p = ”root=/dev/mtdlock 2init=/linuxrc console=ttySAC0”;

params->hdr.tag = ATAG_CMDLINE;

params->hdr.size = (sizeof (structtag_header) + strlen (p) + 1 + 4) >> 2;

strcpy (params->u.cmdline.cmdline,p);

params = tag_next (params);

2.6.4 设置标记ATAG_NONE

标记列表以标记 ATAG_NONE 结束,如下设置:

static void setup_end_tag (bd_t *bd)

{

params->hdr.tag = ATAG_NONE;

params->hdr.size = 0;

}

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 嵌入式
    +关注

    关注

    5082

    文章

    19126

    浏览量

    305313
  • Linux
    +关注

    关注

    87

    文章

    11304

    浏览量

    209550
  • bootloader
    +关注

    关注

    2

    文章

    235

    浏览量

    45630

原文标题:详谈嵌入式之Bootloader

文章出处:【微信号:gh_c472c2199c88,微信公众号:嵌入式微处理器】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    嵌入式Linux启动时间优化的秘密五-Bootloader

    本文主要讲述嵌入式Linux启动时间优化的秘密,我们继续上篇没有讲完的嵌入式Linux启动时间优化方法,本文主要会讲Bootloader。想看上一篇的请查看本文结尾的链接。 Bootloade
    发表于 04-21 17:24 2850次阅读
    <b class='flag-5'>嵌入式</b>Linux启动时间优化的秘密<b class='flag-5'>之</b>五-<b class='flag-5'>Bootloader</b>

    #嵌入式软件设计 BootLoader

    嵌入式嵌入式软件bootloaderBoot
    电子技术那些事儿
    发布于 :2022年09月03日 09:26:33

    基于网络加载的嵌入式BootLoader的设计与实现

    1摘要:由于嵌入式系统引导装载程序(BootLoader)的网络加载方式在产品开发初期阶段的广泛应用,因此基于网络的BootLoader设计与实现具有较强的实际意义。提出了基于网络加载
    发表于 01-16 10:58

    基于网络加载的嵌入式BootLoader有什么优点?

    嵌入式系统从软件结构层次的角度来看,一般由引导装载程序(BootLoader,BSP)、操作系统和应用程序几部分组成。然而在有的嵌入式系统中,操作系统和应用程序存放在系统以外的设备中,如网络、硬盘等,整个系统只有引导装载程序。因
    发表于 04-10 07:19

    嵌入式开发系列课程六:启动程序BootLoader的分析

    嵌入式开发系列课程六:启动程序BootLoader的分析
    发表于 03-25 08:59 27次下载

    嵌入式系统Bootloader开发移植

    嵌入式系统Bootloader开发移植
    发表于 02-11 10:42 54次下载
    <b class='flag-5'>嵌入式</b>系统<b class='flag-5'>Bootloader</b>开发移植

    嵌入式系统BootLoader 移植

    嵌入式系统:BootLoader 移植 U-Boot,全称Universal Boot Loader,是遵循GPL条款的开放源码项目。从FADSROM、8xxROM、PPCBOOT逐步发展演化而来。其源码目录、编译形式与Linux
    发表于 03-01 14:54 47次下载

    基于MIPS64的嵌入式Linux Bootloader的移

    结合OCTEON3010(MIPS64)的U-boot移植经验,介绍了嵌入式Linux Bootloader的工作原理及功能,设计一个基于MIPS体系的Bootloader模型。对OCTEON系列的MIPS处理器
    发表于 12-17 16:30 56次下载

    嵌入式考试笔记嵌入式系统基础知识

    嵌入式考试笔记嵌入式系统基础知识 一、引言 自《嵌入式系统设
    发表于 05-17 09:25 979次阅读

    基于Xilinx FPGA特点的嵌入式Bootloader设计与实现

    Bootloader程序是指嵌入式系统在正常工作之前,配置系统运行环境,引导操作系统的一小段程序。通过这段程序,我们可以初始化硬件设备、建立内存空间映射等,从而将系统的软硬件环境带到一个合适的环境
    发表于 07-16 13:07 1578次阅读
    基于Xilinx FPGA特点的<b class='flag-5'>嵌入式</b><b class='flag-5'>Bootloader</b>设计与实现

    基于嵌入式Linux系统的Bootloader模型在MIPS64上的移植设计浅析

    Linux具备良好的裁剪性和移植性,并且开源、资源丰富,已成为嵌入式系统的开发热点。一个完整的嵌入式Linux系通通常包括Bootloader、内核、文件系统三部分,目标板上电后由
    发表于 09-12 16:13 928次阅读

    嵌入式软件c语言编码规范

    嵌入式软件c语言编码规范
    发表于 10-28 18:13 28次下载

    VScode嵌入式开发入门教程

    VScode嵌入式开发入门教程1.VScode进行单片机嵌入式开发教程2.单片机开发环境的搭建3.相关技巧与设置 VScode
    发表于 11-03 09:51 44次下载
    VScode<b class='flag-5'>嵌入式</b>开发<b class='flag-5'>之</b>入门教程

    分享嵌入式软件Bootloader设计与移植方法

      嵌入式电子产品由于其专用性的特性 ,使得与硬件相关的软件需要经过移植才能使用。如何设计与移植设备开机第一个运行的代码引导程序 Bootloader尤为重要。不同内核开发平台
    发表于 04-02 17:24 3次下载

    嵌入式系统中u-boot和bootloader详解

    嵌入式软件工程师听说过 u-boot 和 bootloader,但很多工程师依然不知道他们到底是啥。
    发表于 10-20 13:12 2014次阅读