启动 ARM Linux

作者:Russell King

日期:2002年5月18日

以下文档适用于 2.4.18-rmk6 及更高版本。

为了启动 ARM Linux,你需要一个引导加载程序(boot loader),这是一个在主内核运行之前运行的小型程序。引导加载程序负责初始化各种设备,并最终调用 Linux 内核,向内核传递信息。

本质上,引导加载程序应(至少)提供以下功能

  1. 设置并初始化 RAM。

  2. 初始化一个串口。

  3. 检测机器类型。

  4. 设置内核标签列表(tagged list)。

  5. 加载 initramfs。

  6. 调用内核镜像。

1. 设置并初始化 RAM

现有引导加载程序

强制要求

新引导加载程序

强制要求

引导加载程序应查找并初始化系统中内核将用于易失性数据存储的所有 RAM。它以与机器相关的方式执行此操作。(它可以使用内部算法自动定位所有 RAM 并确定其大小,也可以使用关于机器中 RAM 的已知信息,或者使用引导加载程序设计者认为合适的任何其他方法。)

2. 初始化一个串口

现有引导加载程序

可选,推荐

新引导加载程序

可选,推荐

引导加载程序应初始化并启用目标板上的一个串口。这允许内核串口驱动程序自动检测它应该将哪个串口用于内核控制台(通常用于调试目的,或与目标板通信)。

作为替代方案,引导加载程序可以通过标签列表向内核传递相关的 “console=” 选项,指定端口和串口格式选项,具体如...所述

3. 检测机器类型

现有引导加载程序

可选

新引导加载程序

强制要求(纯设备树平台除外)

引导加载程序应通过某种方法检测其正在运行的机器类型。无论是硬编码值还是查看连接硬件的某种算法,都不在本文档的讨论范围之内。引导加载程序最终必须能够向内核提供一个 MACH_TYPE_xxx 值(参见 linux/arch/arm/tools/mach-types)。该值应通过寄存器 r1 传递给内核。

对于纯设备树(DT-only)平台,机器类型将由设备树决定。将机器类型设置为全 1(~0)。这并非绝对必要,但可以确保它不会与任何现有类型匹配。

4. 设置启动数据

现有引导加载程序

可选,强烈推荐

新引导加载程序

强制要求

引导加载程序必须提供标签列表(tagged list)或 dtb 镜像,以便向内核传递配置数据。启动数据的物理地址通过寄存器 r2 传递给内核。

4a. 设置内核标签列表

引导加载程序必须创建并初始化内核标签列表。一个有效的标签列表以 ATAG_CORE 开始,以 ATAG_NONE 结束。ATAG_CORE 标签可以为空,也可以不为空。空的 ATAG_CORE 标签的大小字段设置为“2”(0x00000002)。ATAG_NONE 的大小字段必须设置为零。

可以在列表中放置任意数量的标签。重复的标签是附加在前一个标签携带的信息后面,还是完全替换该信息,目前尚未定义;某些标签表现为前者,另一些表现为后者。

引导加载程序必须至少传递系统内存的大小和位置,以及根文件系统的位置。因此,最简单的标签列表看起来应该类似于

              +-----------+
base ->       | ATAG_CORE |  |
              +-----------+  |
              | ATAG_MEM  |  | increasing address
              +-----------+  |
              | ATAG_NONE |  |
              +-----------+  v

标签列表应存储在系统 RAM 中。

标签列表必须放置在内存的一个区域中,确保内核解压器和 initrd 的“bootp”程序都不会覆盖它。建议将其放置在 RAM 的前 16KiB 中。

4b. 设置设备树

引导加载程序必须将设备树镜像(dtb)加载到系统 RAM 中一个 64 位对齐的地址,并使用启动数据对其进行初始化。dtb 格式记录在 https://www.devicetree.org/specifications/ 中。内核将在 dtb 的物理地址处查找魔数(magic value)0xd00dfeed,以确定传递的是 dtb 还是标签列表。

引导加载程序必须至少传递系统内存的大小和位置,以及根文件系统的位置。dtb 必须放置在内核解压器不会覆盖它的内存区域中,同时该区域必须在内核低内存映射(low-memory mapping)所覆盖的范围内。

一个安全的位置是刚好在距离 RAM 起始地址 128MiB 边界之上的地方。

5. 加载 initramfs。

现有引导加载程序

可选

新引导加载程序

可选

如果正在使用 initramfs,那么与 dtb 一样,它必须放置在内核解压器不会覆盖的内存区域中,同时也必须在内核低内存映射所覆盖的区域内。

一个安全的位置是刚好在设备树 blob 之上,而设备树 blob 本身如上所述将加载在距离 RAM 起始地址 128MiB 边界之上的地方。

6. 调用内核镜像

现有引导加载程序

强制要求

新引导加载程序

强制要求

调用内核 zImage 有两种选择。如果 zImage 存储在闪存(flash)中,并且正确链接以从闪存运行,则引导加载程序直接调用闪存中的 zImage 是合法的。

zImage 也可以放置在系统 RAM 中并在那里调用。内核应放置在 RAM 的前 128MiB 中。建议将其加载在 32MiB 以上,以避免在解压前进行重定位,从而使启动过程稍微快一些。

当引导原始内核(非 zImage)时,约束更严格。在这种情况下,内核必须加载到系统中等于 TEXT_OFFSET - PAGE_OFFSET 的偏移量处。

无论如何,必须满足以下条件

  • 使所有支持 DMA 的设备静默(Quiesce),以便内存不会被虚假的网络数据包或磁盘数据损坏。这将为您节省数小时的调试时间。

  • CPU 寄存器设置

    • r0 = 0,

    • r1 = 上文 (3) 中发现的机器类型编号。

    • r2 = 系统 RAM 中标签列表的物理地址,或系统 RAM 中设备树块(dtb)的物理地址

  • CPU 模式

    必须禁用所有形式的中断(IRQ 和 FIQ)

    对于不包含 ARM 虚拟化扩展的 CPU,CPU 必须处于 SVC 模式。(Angel 有一个特殊的例外)

    对于包含虚拟化扩展支持的 CPU,可以进入 HYP 模式,以便内核充分利用这些扩展。这是此类 CPU 的推荐引导方法,除非虚拟化已被预先安装的虚拟机监控程序(hypervisor)使用。

    如果由于任何原因未以 HYP 模式进入内核,则必须以 SVC 模式进入。

  • 缓存、MMU

    MMU 必须关闭。

    指令缓存可以打开或关闭。

    数据缓存必须关闭。

    如果以 HYP 模式进入内核,上述要求除了适用于普通的 PL1(特权内核模式)配置外,还适用于 HYP 模式配置。此外,必须禁用所有进入虚拟机监控程序的陷阱(traps),并且对于架构上可能的所有外设和 CPU 资源,必须授予 PL1 访问权限。除了进入 HYP 模式外,系统配置应确保不包含虚拟化扩展支持的内核也能在没有额外帮助的情况下正确启动。

  • 引导加载程序应通过直接跳转到内核镜像的第一条指令来调用内核镜像。

    在支持 ARM 指令集的 CPU 上,必须在 ARM 状态下进行入口调用,即使对于 Thumb-2 内核也是如此。

    在仅支持 Thumb 指令集的 CPU(例如 Cortex-M 类 CPU)上,必须在 Thumb 状态下进行入口调用。