启动 AArch64 Linux¶
作者:Will Deacon <will.deacon@arm.com>
日期:2012年9月7日
本文档基于 Russell King 编写的 ARM 启动文档,适用于 AArch64 Linux 内核的所有公开版本。
AArch64 异常模型由多个异常级别(EL0 - EL3)组成,其中 EL0、EL1 和 EL2 都有安全和非安全对应级别。EL2 是虚拟机管理程序(hypervisor)级别,EL3 是最高优先级级别且仅存在于安全模式下。这两者在架构上都是可选的。
为了本文的目的,我们将使用术语 boot loader(引导加载程序)来泛指在控制权传递给 Linux 内核之前在 CPU 上执行的所有软件。这可能包括安全监视器和 hypervisor 代码,也可能只是用于准备最简引导环境的少量指令。
本质上,引导加载程序应(至少)提供以下功能:
设置并初始化 RAM
设置设备树
解压内核镜像
调用内核镜像
1. 设置并初始化 RAM¶
要求:强制(MANDATORY)
引导加载程序应查找并初始化系统中内核将用于易失性数据存储的所有 RAM。它以与机器相关的方式执行此操作。(它可以使用内部算法自动定位所有 RAM 并调整其大小,也可以使用有关机器中 RAM 的知识,或者使用引导加载程序设计者认为合适的任何其他方法。)
对于 Arm 机密计算域(Confidential Compute Realms),这包括确保所有受保护的 RAM 具有“RAM”的 Realm IPA 状态 (RIPAS)。
2. 设置设备树¶
要求:强制(MANDATORY)
设备树 blob (dtb) 必须放置在 8 字节边界上,并且大小不得超过 2 兆字节。由于 dtb 将使用最大 2 兆字节大小的块进行可缓存映射,因此它不得放置在必须使用任何特定属性进行映射的任何 2M 区域内。
注意:v4.2 之前的版本还要求将 DTB 放置在位于内核 Image 下方 text_offset 字节处的 512 MB 区域内。
3. 解压内核镜像¶
要求:可选(OPTIONAL)
AArch64 内核目前不提供解压器,因此如果使用压缩的 Image 目标(例如 Image.gz),则需要由引导加载程序执行解压(gzip 等)。对于未实现此要求的引导加载程序,可以使用未压缩的 Image 目标。
4. 调用内核镜像¶
要求:强制(MANDATORY)
解压后的内核镜像包含一个 64 字节的头部,如下所示:
u32 code0; /* Executable code */
u32 code1; /* Executable code */
u64 text_offset; /* Image load offset, little endian */
u64 image_size; /* Effective Image size, little endian */
u64 flags; /* kernel flags, little endian */
u64 res2 = 0; /* reserved */
u64 res3 = 0; /* reserved */
u64 res4 = 0; /* reserved */
u32 magic = 0x644d5241; /* Magic number, little endian, "ARM\x64" */
u32 res5; /* reserved (used for PE COFF offset) */
头部说明
从 v3.17 开始,除非另有说明,所有字段均为小端序(little endian)。
code0/code1 负责跳转到 stext。
当通过 EFI 启动时,最初会跳过 code0/code1。res5 是到 PE 头部的偏移量,PE 头部具有 EFI 入口点(efi_stub_entry)。当存根(stub)完成其工作后,它会跳转到 code0 以恢复正常的引导过程。
在 v3.17 之前,未指定 text_offset 的端序。在这些情况下,image_size 为零,且 text_offset 采用内核的端序为 0x80000。如果 image_size 非零,则 image_size 是小端序且必须遵循。如果 image_size 为零,可以假定 text_offset 为 0x80000。
flags 字段(于 v3.17 引入)是一个小端序 64 位字段,组成如下:
位 0 (Bit 0)
内核端序。如果是大端序 (BE) 则为 1,如果是小端序 (LE) 则为 0。
位 1-2 (Bit 1-2)
内核页面大小。
0 - 未指定。
1 - 4K
2 - 16K
3 - 64K
位 3 (Bit 3)
内核物理放置
- 0
2MB 对齐的基址应尽可能靠近 DRAM 的基址,因为其下方的内存无法通过线性映射访问
- 1
2MB 对齐的基址,使得从镜像开始计算的所有 image_size 字节都位于物理内存的 48 位可寻址范围内
位 4-63 (Bits 4-63)
保留。
当 image_size 为零时,引导加载程序应尝试在内核镜像结束后保留尽可能多的空闲内存供内核立即使用。所需空间大小将根据所选功能而变化,且实际上是无上限的。
Image 必须放置在可用系统 RAM 中任意 2MB 对齐基址偏移 text_offset 字节处,并在此处被调用。2MB 对齐基址与镜像起点之间的区域对内核没有特殊意义,可用于其他目的。从镜像起点开始的至少 image_size 字节必须空闲以供内核使用。注意:v4.6 之前的版本无法利用 Image 物理偏移量以下的内存,因此建议将 Image 尽量靠近系统 RAM 的起点放置。
如果在启动时向内核传递了 initrd/initramfs,它必须完全驻留在大小最大为 32 GB 且 1 GB 对齐的物理内存窗口内,该窗口也必须完全覆盖内核 Image。
描述给内核的任何内存(甚至是镜像起点以下的内存),只要未被标记为对内核保留(例如,在设备树中使用 memreserve 区域),都将被视为可供内核使用。
在跳转进入内核之前,必须满足以下条件:
使所有支持 DMA 的设备静默(Quiesce),以防内存被伪造的网络数据包或磁盘数据损坏。这将为您节省大量调试时间。
主 CPU 通用寄存器设置
x0 = 系统 RAM 中设备树 blob (dtb) 的物理地址。
x1 = 0(保留供将来使用)
x2 = 0(保留供将来使用)
x3 = 0(保留供将来使用)
CPU 模式
必须在 PSTATE.DAIF(Debug、SError、IRQ 和 FIQ)中屏蔽所有形式的中断。CPU 必须处于非安全状态,可以是在 EL2(推荐,以便访问虚拟化扩展),也可以是在 EL1。
缓存、MMU
MMU 必须关闭。
指令缓存可以开启或关闭,并且不得包含与已加载内核镜像对应的任何陈旧条目(stale entries)。
对应于已加载内核镜像的地址范围必须被清理到 PoC(Point of Coherency,一致性点)。在存在系统缓存或其他启用了缓存的一致性主控端(coherent masters)的情况下,这通常需要按 VA 进行缓存维护,而不是通过 set/way 操作。遵循架构定义的按 VA 操作进行缓存维护的系统缓存必须进行配置并且可以启用。不遵循架构定义的按 VA 操作进行缓存维护的系统缓存(不推荐)必须进行配置并禁用。
架构定时器(Architected timers)
必须使用定时器频率对 CNTFRQ 进行编程,并且必须在所有 CPU 上使用一致的值对 CNTVOFF 进行编程。如果在 EL1 进入内核,在可用情况下,CNTHCTL_EL2 必须设置 EL1PCTEN(位 0)。
一致性(Coherency)
内核要引导的所有 CPU 在进入内核时必须属于同一一致性域。这可能需要进行由实现定义(IMPLEMENTATION DEFINED)的初始化,以使每个 CPU 能够接收维护操作。
系统寄存器
在将进入内核镜像的异常级别及其以下的所有可写架构系统寄存器,必须由更高异常级别的软件进行初始化,以防止在 UNKNOWN(未知)状态下执行。
对于所有系统:- 如果存在 EL3
在内核正在其上执行的所有 CPU 之间,SCR_EL3.FIQ 必须具有相同的值。
只要内核在执行,SCR_EL3.FIQ 的值就必须与引导时存在的值相同。
如果存在 EL3 且在 EL2 进入内核
SCR_EL3.HCE(位 8)必须初始化为 0b1。
对于要在 v5 模式下使用的带有 GICv5 中断控制器的系统
如果在 EL1 进入内核且存在 EL2
ICH_HFGRTR_EL2.ICC_PPI_ACTIVERn_EL1(位 20)必须初始化为 0b1。
ICH_HFGRTR_EL2.ICC_PPI_PRIORITYRn_EL1(位 19)必须初始化为 0b1。
ICH_HFGRTR_EL2.ICC_PPI_PENDRn_EL1(位 18)必须初始化为 0b1。
ICH_HFGRTR_EL2.ICC_PPI_ENABLERn_EL1(位 17)必须初始化为 0b1。
ICH_HFGRTR_EL2.ICC_PPI_HMRn_EL1(位 16)必须初始化为 0b1。
ICH_HFGRTR_EL2.ICC_IAFFIDR_EL1(位 7)必须初始化为 0b1。
ICH_HFGRTR_EL2.ICC_ICSR_EL1(位 6)必须初始化为 0b1。
ICH_HFGRTR_EL2.ICC_PCR_EL1(位 5)必须初始化为 0b1。
ICH_HFGRTR_EL2.ICC_HPPIR_EL1(位 4)必须初始化为 0b1。
ICH_HFGRTR_EL2.ICC_HAPR_EL1(位 3)必须初始化为 0b1。
ICH_HFGRTR_EL2.ICC_CR0_EL1(位 2)必须初始化为 0b1。
ICH_HFGRTR_EL2.ICC_IDRn_EL1(位 1)必须初始化为 0b1。
ICH_HFGRTR_EL2.ICC_APR_EL1(位 0)必须初始化为 0b1。
ICH_HFGWTR_EL2.ICC_PPI_ACTIVERn_EL1(位 20)必须初始化为 0b1。
ICH_HFGWTR_EL2.ICC_PPI_PRIORITYRn_EL1(位 19)必须初始化为 0b1。
ICH_HFGWTR_EL2.ICC_PPI_PENDRn_EL1(位 18)必须初始化为 0b1。
ICH_HFGWTR_EL2.ICC_PPI_ENABLERn_EL1(位 17)必须初始化为 0b1。
ICH_HFGWTR_EL2.ICC_ICSR_EL1(位 6)必须初始化为 0b1。
ICH_HFGWTR_EL2.ICC_PCR_EL1(位 5)必须初始化为 0b1。
ICH_HFGWTR_EL2.ICC_CR0_EL1(位 2)必须初始化为 0b1。
ICH_HFGWTR_EL2.ICC_APR_EL1(位 0)必须初始化为 0b1。
ICH_HFGITR_EL2.GICRCDNMIA(位 10)必须初始化为 0b1。
ICH_HFGITR_EL2.GICRCDIA(位 9)必须初始化为 0b1。
ICH_HFGITR_EL2.GICCDDI(位 8)必须初始化为 0b1。
ICH_HFGITR_EL2.GICCDEOI(位 7)必须初始化为 0b1。
ICH_HFGITR_EL2.GICCDHM(位 6)必须初始化为 0b1。
ICH_HFGITR_EL2.GICCDRCFG(位 5)必须初始化为 0b1。
ICH_HFGITR_EL2.GICCDPEND(位 4)必须初始化为 0b1。
ICH_HFGITR_EL2.GICCDAFF(位 3)必须初始化为 0b1。
ICH_HFGITR_EL2.GICCDPRI(位 2)必须初始化为 0b1。
ICH_HFGITR_EL2.GICCDDIS(位 1)必须初始化为 0b1。
ICH_HFGITR_EL2.GICCDEN(位 0)必须初始化为 0b1。
DT 或 ACPI 表必须描述 GICv5 中断控制器。
对于要在 v3 模式下使用的带有 GICv3 中断控制器的系统:- 如果存在 EL3
ICC_SRE_EL3.Enable(位 3)必须初始化为 0b1。
ICC_SRE_EL3.SRE(位 0)必须初始化为 0b1。
在内核正在其上执行的所有 CPU 之间,ICC_CTLR_EL3.PMHE(位 6)必须设置为相同的值,并且在内核的整个生命周期中必须保持不变。
如果在 EL1 进入内核
ICC_SRE_EL2.Enable(位 3)必须初始化为 0b1
ICC_SRE_EL2.SRE(位 0)必须初始化为 0b1。
DT 或 ACPI 表必须描述 GICv3 中断控制器。
对于要在兼容(v2)模式下使用的带有 GICv3 中断控制器的系统
如果存在 EL3
ICC_SRE_EL3.SRE(位 0)必须初始化为 0b0。
如果在 EL1 进入内核
ICC_SRE_EL2.SRE(位 0)必须初始化为 0b0。
DT 或 ACPI 表必须描述 GICv2 中断控制器。
对于具有指针验证(pointer authentication)功能的 CPU
如果存在 EL3
SCR_EL3.APK(位 16)必须初始化为 0b1
SCR_EL3.API(位 17)必须初始化为 0b1
如果在 EL1 进入内核
HCR_EL2.APK(位 40)必须初始化为 0b1
HCR_EL2.API(位 41)必须初始化为 0b1
对于存在活动监视器单元 v1 (AMUv1) 扩展的 CPU
如果存在 EL3
CPTR_EL3.TAM(位 30)必须初始化为 0b0
CPTR_EL2.TAM(位 30)必须初始化为 0b0
AMCNTENSET0_EL0 必须初始化为 0b1111
AMCNTENSET1_EL0 必须初始化为一个平台特定的值,该值对于存在的每个辅助计数器对应的位设置了 0b1。
如果在 EL1 进入内核
AMCNTENSET0_EL0 必须初始化为 0b1111
AMCNTENSET1_EL0 必须初始化为一个平台特定的值,该值对于存在的每个辅助计数器对应的位设置了 0b1。
对于存在细粒度陷阱(Fine Grained Traps, FEAT_FGT)扩展的 CPU
如果存在 EL3 且在 EL2 进入内核
SCR_EL3.FGTEn(位 27)必须初始化为 0b1。
对于存在细粒度陷阱 2 (FEAT_FGT2) 扩展的 CPU
如果存在 EL3 且在 EL2 进入内核
SCR_EL3.FGTEn2(位 59)必须初始化为 0b1。
对于支持 HCRX_EL2 (FEAT_HCX) 的 CPU
如果存在 EL3 且在 EL2 进入内核
SCR_EL3.HXEn(位 38)必须初始化为 0b1。
对于支持高级 SIMD 和浮点运算的 CPU
如果存在 EL3
CPTR_EL3.TFP(位 10)必须初始化为 0b0。
如果存在 EL2 且在 EL1 进入内核
CPTR_EL2.TFP(位 10)必须初始化为 0b0。
对于存在可伸缩向量扩展(Scalable Vector Extension, FEAT_SVE)的 CPU
如果存在 EL3
CPTR_EL3.EZ(位 8)必须初始化为 0b1。
对于内核在其上执行的所有 CPU,ZCR_EL3.LEN 必须初始化为相同的值。
如果在 EL1 进入内核且存在 EL2
CPTR_EL2.TZ(位 8)必须初始化为 0b0。
CPTR_EL2.ZEN(位 17:16)必须初始化为 0b11。
对于内核将在其上执行的所有 CPU,ZCR_EL2.LEN 必须初始化为相同的值。
对于具有可伸缩矩阵扩展(Scalable Matrix Extension, FEAT_SME)的 CPU
如果存在 EL3
CPTR_EL3.ESM(位 12)必须初始化为 0b1。
SCR_EL3.EnTP2(位 41)必须初始化为 0b1。
对于内核将在其上执行的所有 CPU,SMCR_EL3.LEN 必须初始化为相同的值。
如果在 EL1 进入内核且存在 EL2
CPTR_EL2.TSM(位 12)必须初始化为 0b0。
CPTR_EL2.SMEN(位 25:24)必须初始化为 0b11。
SCTLR_EL2.EnTP2(位 60)必须初始化为 0b1。
对于内核将在其上执行的所有 CPU,SMCR_EL2.LEN 必须初始化为相同的值。
HFGRTR_EL2.nTPIDR2_EL0(位 55)必须初始化为 0b01。
HFGWTR_EL2.nTPIDR2_EL0(位 55)必须初始化为 0b01。
HFGRTR_EL2.nSMPRI_EL1(位 54)必须初始化为 0b01。
HFGWTR_EL2.nSMPRI_EL1(位 54)必须初始化为 0b01。
对于具有可伸缩矩阵扩展 FA64 功能(FEAT_SME_FA64)的 CPU
如果存在 EL3
SMCR_EL3.FA64(位 31)必须初始化为 0b1。
如果在 EL1 进入内核且存在 EL2
SMCR_EL2.FA64(位 31)必须初始化为 0b1。
对于具有内存标记扩展功能(Memory Tagging Extension, FEAT_MTE2)的 CPU
如果存在 EL3
SCR_EL3.ATA(位 26)必须初始化为 0b1。
如果在 EL1 进入内核且存在 EL2
HCR_EL2.ATA(位 56)必须初始化为 0b1。
对于具有可伸缩矩阵扩展第 2 版(FEAT_SME2)的 CPU
如果存在 EL3
SMCR_EL3.EZT0(位 30)必须初始化为 0b1。
如果在 EL1 进入内核且存在 EL2
SMCR_EL2.EZT0(位 30)必须初始化为 0b1。
对于具有分支记录缓冲区扩展(Branch Record Buffer Extension, FEAT_BRBE)的 CPU
如果存在 EL3
MDCR_EL3.SBRBE(位 33:32)必须初始化为 0b01 或 0b11。
如果在 EL1 进入内核且存在 EL2
BRBCR_EL2.CC(位 3)必须初始化为 0b1。
BRBCR_EL2.MPRED(位 4)必须初始化为 0b1。
HDFGRTR_EL2.nBRBDATA(位 61)必须初始化为 0b1。
HDFGRTR_EL2.nBRBCTL(位 60)必须初始化为 0b1。
HDFGRTR_EL2.nBRBIDR(位 59)必须初始化为 0b1。
HDFGWTR_EL2.nBRBDATA(位 61)必须初始化为 0b1。
HDFGWTR_EL2.nBRBCTL(位 60)必须初始化为 0b1。
HFGITR_EL2.nBRBIALL(位 56)必须初始化为 0b1。
HFGITR_EL2.nBRBINJ(位 55)必须初始化为 0b1。
对于具有性能监视器扩展(FEAT_PMUv3p9)的 CPU
如果存在 EL3
MDCR_EL3.EnPM2(位 7)必须初始化为 0b1。
如果在 EL1 进入内核且存在 EL2
HDFGRTR2_EL2.nPMICNTR_EL0(位 2)必须初始化为 0b1。
HDFGRTR2_EL2.nPMICFILTR_EL0(位 3)必须初始化为 0b1。
HDFGRTR2_EL2.nPMUACR_EL1(位 4)必须初始化为 0b1。
HDFGWTR2_EL2.nPMICNTR_EL0(位 2)必须初始化为 0b1。
HDFGWTR2_EL2.nPMICFILTR_EL0(位 3)必须初始化为 0b1。
HDFGWTR2_EL2.nPMUACR_EL1(位 4)必须初始化为 0b1。
对于具有 SPE 数据源过滤(FEAT_SPE_FDS)功能的 CPU
如果存在 EL3
MDCR_EL3.EnPMS3(位 42)必须初始化为 0b1。
如果在 EL1 进入内核且存在 EL2
HDFGRTR2_EL2.nPMSDSFR_EL1(位 19)必须初始化为 0b1。
HDFGWTR2_EL2.nPMSDSFR_EL1(位 19)必须初始化为 0b1。
对于具有内存复制和内存设置指令(FEAT_MOPS)的 CPU
如果在 EL1 进入内核且存在 EL2
HCRX_EL2.MSCEn(位 11)必须初始化为 0b1。
HCRX_EL2.MCE2(位 10)必须初始化为 0b1,并且 hypervisor 必须按照 Hypervisor 要求 中所述处理 MOPS 异常。
对于具有扩展转换控制寄存器功能(FEAT_TCR2)的 CPU
如果存在 EL3
SCR_EL3.TCR2En(位 43)必须初始化为 0b1。
如果在 EL1 进入内核且存在 EL2
HCRX_EL2.TCR2En(位 14)必须初始化为 0b1。
对于具有阶段 1 权限间接扩展功能(FEAT_S1PIE)的 CPU
如果存在 EL3
SCR_EL3.PIEn(位 45)必须初始化为 0b1。
如果在 EL1 进入内核且存在 EL2
HFGRTR_EL2.nPIR_EL1(位 58)必须初始化为 0b1。
HFGWTR_EL2.nPIR_EL1(位 58)必须初始化为 0b1。
HFGRTR_EL2.nPIRE0_EL1(位 57)必须初始化为 0b1。
HFGRWR_EL2.nPIRE0_EL1(位 57)必须初始化为 0b1。
对于具有保护控制栈(Guarded Control Stacks, FEAT_GCS)的 CPU
GCSCR_EL1 必须初始化为 0。
GCSCRE0_EL1 必须初始化为 0。
如果存在 EL3
SCR_EL3.GCSEn(位 39)必须初始化为 0b1。
如果存在 EL2
GCSCR_EL2 必须初始化为 0。
如果在 EL1 进入内核且存在 EL2
HCRX_EL2.GCSEn 必须初始化为 0b1。
HFGITR_EL2.nGCSEPP(位 59)必须初始化为 0b1。
HFGITR_EL2.nGCSSTR_EL1(位 58)必须初始化为 0b1。
HFGITR_EL2.nGCSPUSHM_EL1(位 57)必须初始化为 0b1。
HFGRTR_EL2.nGCS_EL1(位 53)必须初始化为 0b1。
HFGRTR_EL2.nGCS_EL0(位 52)必须初始化为 0b1。
HFGWTR_EL2.nGCS_EL1(位 53)必须初始化为 0b1。
HFGWTR_EL2.nGCS_EL0(位 52)必须初始化为 0b1。
对于具有调试架构的 CPU,即 FEAT_Debugv8pN(所有版本)
如果存在 EL3
MDCR_EL3.TDA(位 9)必须初始化为 0b0
对于具有 FEAT_PMUv3 的 CPU
如果存在 EL3
MDCR_EL3.TPM(位 6)必须初始化为 0b0
对于支持不带状态的 64 字节加载和存储(FEAT_LS64)的 CPU
如果在 EL1 进入内核且存在 EL2
HCRX_EL2.EnALS(位 1)必须初始化为 0b1。
对于支持带状态的 64 字节存储(FEAT_LS64_V)的 CPU
如果在 EL1 进入内核且存在 EL2
HCRX_EL2.EnASR(位 2)必须初始化为 0b1。
上述关于 CPU 模式、缓存、MMU、架构定时器、一致性和系统寄存器的要求适用于所有 CPU。所有 CPU 必须在相同的异常级别进入内核。在文档记录的值禁用了陷阱(traps)的情况下,只要这些陷阱由更高异常级别以透明方式处理,就像设置了文档记录的值一样,允许启用这些陷阱。
引导加载程序应以以下方式在每个 CPU 上进入内核:
主 CPU 必须直接跳转到内核镜像的第一条指令。此 CPU 传递的设备树 blob 必须为每个 cpu 节点包含一个“enable-method”属性。支持的启动方法(enable-methods)描述如下。
预计引导加载程序将在进入内核之前生成这些设备树属性并将它们插入到 blob 中。
具有 “spin-table” 启动方法的 CPU 必须在其 cpu 节点中具有 “cpu-release-addr” 属性。此属性标识一个自然对齐的 64 位零初始化内存位置。
这些 CPU 应在内核之外的保留内存区域(通过设备树中的 `/memreserve/` 区域告知内核)中自旋(spin),轮询其 cpu-release-addr 位置,该位置必须包含在保留区域内。可以插入一条 wfe 指令以减少忙等待循环的开销,并且主 CPU 将发出 sev。当读取由 cpu-release-addr 指向的位置返回非零值时,CPU 必须跳转到该值。该值将作为单个 64 位小端序值写入,因此 CPU 在跳转到该值之前必须将读取的值转换为其本地端序。
具有 “psci” 启动方法的 CPU 应保持在内核之外(即在内存节点中向内核描述的内存区域之外,或者在设备树中的 `/memreserve/` 区域向内核描述的保留内存区域中)。内核将按照 ARM 文档编号 ARM DEN 0022A(“Power State Coordination Interface System Software on ARM processors”)中所述发出 CPU_ON 调用,将 CPU 引入内核。
设备树应包含一个 ‘psci’ 节点,如 `Documentation/devicetree/bindings/arm/psci.yaml` 中所述。
从 CPU 通用寄存器设置
x0 = 0(保留供将来使用)
x1 = 0(保留供将来使用)
x2 = 0(保留供将来使用)
x3 = 0(保留供将来使用)