赞
踩
前注:本文是Documentation/arm64/booting的翻译。
这篇文章基于Russell King所写的《the ARM booting document》,并与AArch64 Linux kernel的所有公开版本相关。
AArch64异常模型由几级异常组成,分别是EL0 - EL3,EL0和EL1又分别有安全和非安全模式,EL2是hypervisor级别,仅存在于安全模式,EL3是最高异常优先级别,仅存在于安全模式。
在本文中,我们使用术语“boot loader”来简单地定义在控制权传递给Linux kernel之前,在CPU上执行的所有软件,这可能包括Secure Monitor和hypervisor代码,或者仅仅是一小段预备好最小引导环境的指令代码。
基本上来说,boot loader应该(至少)下列一些操作:
1. 设立并初始化RAM
2. 设立设备树
3. 解压缩kernel映像
4. 调用kernel映像
1. 设立并初始化RAM
----------------------------------------------------
要求:必须
boot loader应找到并初始化在系统中kernel将用来存放临时数据的所有RAM,这依赖于具体的处理器。(它可能使用内部算法来自动定位和计算所有RAM的大小,或者可能使用处理器的提供的RAM的数据,或者是任何其他boot loader设计者觉得合适方法。)
2. 设立设备树
----------------------------------------------------
要求:必须
设备树blob(dtb)必须放置于kernel映像开始前的512MB空间范围内8B字节边界对齐的地址空间,并且这个空间不能跨2MB的边界,这样做的目的是让kernel映像在初始页表中使用单一块来映射设备树blob。
3. 解压缩kernel映像
----------------------------------------------------
要求:可选
目前,AArch64 kernel映像不提供解压缩程序,因此如果使用的是压缩的kernel映像(如Image.gz),那么需要boot loader来做解压缩的工作(如gzip)。对于没有实现解压缩的boot loader,则需要使用非压缩的kernel映像。
4. 调用kernel映像
-----------------------------------------------------
要求:必须
解压缩后的kernel映像包含一个64B的头,它的格式如下:
u32 code0; /* 可执行代码 */
u32 code1; /* 可执行代码 */
u64 text_offset;/* 映像加载偏移地址, 小端 */
u64 image_size;/* 有效映像大小, 小端 */
u64 flags;/*kernel标志,小端*/
u64 res2 = 0;/*保留 */
u64 res3 = 0;/*保留 */
u64 res4 = 0;/*保留 */
u32 magic = 0x644d5241;/*魔术数,小端,“ARM\x64” */
u32 res5;/*保留(用于PE COFF偏移地址) */
有关这个kernel映像头的一些注解:
- 版本v3.17以后,除非明确指示,此头格式的所有域都是小端格式。
- code0 / code1处的代码负责跳转到stext。
- 当使用EFI引导kernel映像时,起初会跳过开始的code0 / code1,res5则是PE头的偏移地址,而在PE头中包含EFI的入口(efi_stub_entry),当stub完成它的工作后,会跳回code0处来恢复正常引导过程。
- 在版本v3.17之前,text_offset域的大、小端没有指定,在此情况下,image_size域为0,而text_offset
- flags域(自v3.17开始引入)是小端的64-bit格式,按下列解码:
Bit 0: Kernel大、小端格式。大端为1,小端为0
Bits 1-63: 保留。
- 当image_size为0时,boot loader应在kernel映像结束地址后面保留足够的内存空间位kernel所使用,具体大小依赖于所选择的kernel特性多少而变化。
Kernel映像必须放置在可使用的RAM开始附近2MB对齐基地址的text_offset位置,并从那被调用。该基地址一下的内存目前Linux并没有使用,因此强烈建议基地址就是于RAM的开始地址,从映像开始,必须保证有image_size大小可用空间给kernel映像使用。
传递给kernel的任何内存(甚至低于2MB对齐的基地址空间),如没有在设备树使用memreserve region标示为从kernel保留的内存空间,kernel就它可以使用。
在跳转进入kernel之前,下列条件必须满足:
- 停止所有可DMA的设备,以保证内存没有被网络数据包或者磁盘数据污染,这样能节省你许多调试时间。
- 主CPU通用寄存器设置
x0 = 系统内存中设备树blob的物理地址
x1 = 0 (保留将来使用)
x2 = 0 (保留将来使用)
x3 = 0 (保留将来使用)
- CPU模式
在PSTATE.DAIF屏蔽中断的所有形式(Debug,SError,IRQ,和FIQ)
CPU必须处于EL2(推荐此优先级以便于能访问虚拟扩展特性),或者非安全EL1。
- Caches,MMUs
MMU必须关闭。
I Cache可以使能或者关闭。
必须将与加载kernel映像对应的地址范围清0,在有系统cache,或其可cache的master的情况下,典型情况下是通过VA操作而不是set / way操作来进行cache一致性维护。支持通过VA操作来进行系统cache一致性维护的系统cache必须配置并启用。
不支持通过VA操作(不推荐)进行体系cache一致性维护的系统cache必须配置和禁用。
- Architected timers
必须编程设置CNTFRQ定时器频率,必须编程设置所有CPU的CNTVOFF为一个一致的数值。如果kernel是进入EL1异常级别,那么那么必须将CNTHCTL_EL2[EL1PCTEN]比特置1.
- Coherency
在进入kernel时,必须保证由kernel引导的所有CPU都处在同一个同步域内,这可能要求具体实现来让每一给CPU能够接收维护操作。
- System registers
为防止在一种不确定状态中执行,在kernel在开始进入一个异常级别运行前,在一个更高异常级别上的软件必须首先将kernel即将要运行的异常级别中的所有可写架构系统寄存器初始化到一种确定的状态。
对于具有GICv中断控制器的系统来说:
- 如果kernel是进入EL3:
ICC_SRE_EL3.Enable(bit 3)必须初始化为0b1
ICC_SRE_EL3.SRE(bit 0)必须初始化为0b1
- 如果kernel是进入EL1:
ICC_SRE_EL2.Enable(bit 3)必须初始化为0b1
ICC_SRE_EL2.SRE(bit 0)必须初始化为0b1
上面所描述对于CPU模式、Cache、MMU、体系timer、coherency、和系统寄存器的要求适用于所有的CPU,所有CPU都必须让kernel必须进入同一个异常级别。
boot loader应该让每一个CPU按下列方式进入kernel:
- 主CPU必须直接跳转到kernel映像的第一条指令,由主CPU传递的设备树blob必须为每一个CPU包含一“enable-method”的属性,该enable-method属性将在下面介绍。
boot loader应该生成这些设备树属性,并在进入kernel前将它们插入到设备树blob当中。
- 具有“spin-table” enable-method属性的CPU必须在其cpu节点上有一“cpu-release-addr”属性,该属性标识64-bit自然对齐的已经初始化为0的内存位置。
这些CPU应在kernel外的一保留内存区域(该保留内存区域通过设备树的memreserve节点传递给kernel)自旋轮询它们在保留内存区域上各自的cpu-release-addr地址。为降低繁忙轮询的开销,可插入一条wfe指令,然后通过主CPU发一条sev指令来唤醒。当某一个CPU读其cpu-release-addr地址的返回值不是0时,该CPU必须跳到该返回值所指的地址。这个数值是作为单个64-bit小端格式写入,因此CPU跳转到该地址之前,必须将其转化为与该CPU大小端相匹配的格式。
- 具有“psci” enable-method属性的CPU应继续留在kernel外,(意即在设备树中为kernel的memory节点所描述的内存区域外,或者在设备树中为kernel的memreseerve节点所描述的保留内存区域内)。根据编号为ARM DEN 0022A的ARM文档所描述的,kernel起来时将发起CPU_ON调用来将这些CPU引导起来进入kernel。
- 次CPU通用目的寄存器设置
x0 = 0 (保留将来使用)
x1 = 0 (保留将来使用)
x2 = 0 (保留将来使用)
x3 = 0 (保留将来使用)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。