Linux arm64内核启动
原创翻译,转载请注明出处。
arm64的异常模型由一组异常级别(EL0-EL3)组成。EL0,EL1有安全模式和非安全模式的区别。EL2是虚拟机管理级别并且只有非安全模式。EL3是最高优先级并且只存在安全模式中。
为了描述方便,下面将使用术语“boot loader”来简化所有执行在cpu将控制权转交给内核之前的软件的称呼。这里包含了安全监视器(secure monitor)和虚拟机管理器(hypervisor)的代码,或者可能是少量用来准备一个最小的启动环境的指令。
基本上,boot loader至少提供以下几个功能:
- 安装与初始化物理内存
- 安装设备树
- 解压内核镜像
- 启动内核镜像
1、安装与初始化物理内存
boot loader需要初始化物理内存,内核将使用这些内存来存储volatile类型的数据。这个是与机器有关的,可能使用了内部算法自动的定位并取得物理内存的大小,
或者可能是机器有关内存方面的特性,也可能是boot loader设计者知道的获取内存某种方法。(囧)
2、安装设备树
dtb(device tree blob)必须位于8-BYTE对齐的位置并且不能超过2MB的大小。因为dtb会被映射到最大2MB的缓存块上,它不能放在任何映射了特定属性的2M区域内。
注意,在内核4.2以前,要求将DTB放在内核镜像里以text_offset为起始位置的512M区域内。
3、解压内核镜像(这个是可选的)
arm64(aarch64)的内核当前并不提供自解压功能,因此需要解压在boot loader里完成(比如gzip格式)。如果boot loader不支持解压,可以使用不压缩的镜像来启动。
4、启动内核镜像
解压后的内核镜像包含64byte的头,头结构定义如下:
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) */
头结构说明:
(1)、在内核3.17版本以前,所有字段都是小端字节序,除非有特别说明。
(2)、code0/code1 是为了响应 stext 分支。
(3)、如果以EFI(可扩展固件接口 Extensible Firmware Interface)启动,code0/code1一开始就会被跳过。res5是指PE头和有EFI入口点(efi_stub_entry)的PE头的偏移。当efi完成了它的工作,就会跳转到 code0 的位置继续正常的启动流程。
(4)、在内核3.17版本以前,text_offset字段的字节序是不确定的。举个栗子,在内核字节序里,image_size 为0,text_offset为 0x80000。如果 image_size 是一个非0值,必须注意了,这时image_size 是小端字节序。如果 image_size 为0,那么text_offset可以认为是 0x80000。
(5)、flags 字段(内核3.17版本引入的)是一个小端字节序的64bit字段,它的组成如下:
Bit 0: 内核字节序标识,
1是大端, 0是小端;
Bit 1-2:内核页的大小,
0 - 表示未说明,1 - 表示4K大小,2 - 16K,3 - 64K;
Bit 3:内核物理布局,
0 - 基地址2MB对齐并且基地址应该离DRAM的基地址越可能的近,因为是线性映射,所以内存地址低于它的不能访问。
1 - 基地址2MB对齐,可以位于物理内存的任何地方。
Bit 4-63:保留字段。
(6)如果 image_size 为0,在内核镜像启动结束之后,bootloader应提供尽量多的空闲内存给内核使用。这个空间的数量会随着不同的特性变化,实际上是没有明确的限制的。
内核镜像位于任何可用的 text_offset 大小的字节数的内存基地址上,这个地址必须是2MB对齐的。2MB对齐的基地址与内核镜像起始位置这之间的区域对内核是没有特别的意义的,可以用作它用。
在内核镜像起始的位置起,至少 image_size 大小的字节数必须是空闲的,以供内核使用。
注意:在内核4.6版本之前不能使用低于镜像大小的物理偏移的内存,所以推荐镜像放在离物理内存地址起始位置尽可能近的地方。
如果 initd/initramfs 在启动的时候传递给了内核,它必须整个属于1GB对齐的物理内存窗口到32GB大小之间,以全部覆盖内核镜像为好。
任何描述给内核的内存(包括低于镜像起始地址的),如果没有标记为保留的(dtb里的 /memreserve指定)将被内核认为是可以使用的。
在跳转到内核之前,下面的条件必须满足:
(1)禁用所有的具有DMA能力的设备,这样内存就不会被伪造的网络数据包或者硬盘数据污染。这将节省你大量的调试时间。
(2)主CPU的通用寄存器设置:
x0 = dtb在系统内存的物理地址
x1 = 0 (保留给以后使用)
x2 = 0 (保留给以后使用)
x3 = 0 (保留给以后使用)
(3)CPU模式
所有的中断都必须在 PSTATE.DAIF (Debug,SError,IRQ,FIQ) 中设置掩码位。CPU必须处于EL2(推荐模式,方便虚拟化扩展访问)或者非安全模式的EL1模式中。
(4)Caches,MMUs
MMU必须关闭。
指令缓存可以开启或关闭。
对应于内核镜像的地址范围应该清理成PoC(PoC不知道是啥)。要使能系统缓存或者其他一致性主缓存,要求缓存维护通过VA,而不是 set/way 操作。
系统缓存 (依赖体系结构的缓存维护通过VA操作的)必须被配置并且使能,而不依赖体系结构通过VA维护的系统缓存必须禁止。
(5)定时器
CNTFRQ 必须对定时器频率是可编程的,并且CNTVOFF必须对在所有CPU上具有一致性的值是可编程的。如果进入内核时是在EL1模式,CNTHCTL_EL2 必须有EL1PCTEN (bit 0)设置可用。
(6)一致性
所有CPU通过内核启动必须是相同一致的内核入口的一部分。这将要求“IMPLEMENTATION DEFINED”的初始化来使能每个CPU来接收维护操作。
(7)系统寄存器
所有可写的系统寄存器在这内核镜像将要进入的异常级别(EL)必须在一个更高的异常级别(EL)通过软件初始化,来防止在一个未知的状态执行。
在一个有GICv3的中断控制器的系统可以使用v3模式:
1、如果是 EL3 :
ICC_SRE_EL3.Enable (bit 3) 必须初始化为 0b1.
ICC_SRE_EL3.SRE (bit 0) 必须初始化为 0b1.
2、内核是在 EL1 :
ICC.SRE_EL2.Enable (bit 3) 必须初始化为 0b1
ICC_SRE_EL2.SRE (bit 0) 必须初始化为 0b1.
3、DT或者ACPI表必须在GICv3中断控制器中。
上述的CPU模式,缓存,MMU,定时器,一致性,系统寄存器对应所有的CPU,所有CPU必须在相同异常级别进入内核。
bootloader在进入内核(每个cpu)都有如下规则:
(1)主CPU直接跳转到内核镜像的第一条指令。dtb传给每个CPU必须包含“enable-method”属性,这个属性在下面会描述。
bootloader会生成这些设备树的属性并在内核入口之前插入到二进制执行文件中。
(2)带有“spin-table”使能方法的CPU必须有一个“cpu-release-addr”的属性节点。这个属性标识符以64bit自然对齐并在内存中初始化为0。
这些CPU在内核之外的保留的内存区域(dtb里的 /memreserve/ 的指定区域)空转,并轮询“cpu-release-addr”地址,该地址也在保留区域内。
“wfe”指令可以用来插入减少这种busy-loop的开销,并且主CPU会发出“sev”(嘛东西。)。 当读取“cpu-release-addr”返回一个非0值,这个CPU必须跳转到这个值的地址。
这个值就是一个简单的64bit的小端的数值,所有这些cpu必须转换成它自己的原生字节序之后才能跳转过去。
(3)具有“psci”的使能方法的CPU应该停留在内核之外的保留内存区域。内核会发出“CPU_ON”的调用来将CPU带入内核。
设备树应该包含一个“psci”节点。可以参考Documentation/devicetree/bindings/arm/psci.txt。
(4)从CPU上的通用寄存器设置:
x0 = 0 (reserved for future use)
x1 = 0 (reserved for future use)
x2 = 0 (reserved for future use)
x3 = 0 (reserved for future use)
Linux arm64内核启动的更多相关文章
- Linux - 修改内核启动顺序及删除无用内核
现象: CentOS7开机启动界面显示多个内核选项 原因: 正常情况下,有两个启动项,一个是"正常启动",另一个是"救援模式启动"(rescue). 如果启动项 ...
- Linux内核启动过程概述
版权声明:本文原创,转载需声明作者ID和原文链接地址. Hi!大家好,我是CrazyCatJack.今天给大家带来的是Linux内核启动过程概述.希望能够帮助大家更好的理解Linux内核的启动,并且创 ...
- linux内核启动以及文件系统的加载过程
Linux 内核启动及文件系统加载过程 当u-boot 开始执行 bootcmd 命令,就进入 Linux 内核启动阶段.普通 Linux 内核的启动过程也可以分为两个阶段.本文以项目中使用的 lin ...
- Linux内核启动logo
之前在分析samsung的fb驱动代码的时候,其中有一段代码是处理内核logo显示相关的,今天就内核logo这个话题来聊一聊! 一.处理内核logo显示相关的代码在哪? 回到samsung的fb驱动代 ...
- Linux内核启动分析过程-《Linux内核分析》week3作业
环境搭建 环境的搭建参考课件,主要就是编译内核源码和生成镜像 start_kernel 从start_kernel开始,才真正进入了Linux内核的启动过程.我们可以把start_kernel看做平时 ...
- 【转载】linux内核启动android文件系统过程分析
主要介绍linux 内核启动过程以及挂载android 根文件系统的过程,以及介绍android 源代码中文件系统部分的浅析. 主要源代码目录介绍Makefile (全局的Makefile)bioni ...
- Linux内核启动
Linux内核启动过程概述 Linux的启动代码真的挺大,从汇编到C,从Makefile到LDS文件,需要理解的东西很多.毕竟Linux内核是由很多人,花费了巨大的时间和精力写出来的.而且直到现在,这 ...
- linux内核启动笔记
一. 1.解压 tar xjf linux-2.6.22.6.tar.bz2 2.打补丁 patch -p1 < ../linux-2.6.22.6_jz2440.patch 3.配置 ...
- 通过从代码层面分析Linux内核启动来探知操作系统的启动过程
通过从代码层面分析Linux内核启动来探知操作系统的启动过程 前言说明 本篇为网易云课堂Linux内核分析课程的第三周作业,我将围绕Linux 3.18的内核中的start_kernel到init进程 ...
随机推荐
- js中的throw err的作用
1.阻止程序的运行 2.把错误消息打到控制台
- HDU1159(LCS)
#include<iostream> #include<algorithm> #include<cstring> #include<cstdio> us ...
- Mac mysql安装失败解决方法
在mac终端通过命令安装mysql,提示错误,解决方法如下: (1)安装命令:brew install mysql (2)提示错误: Error:Could not create /usr/local ...
- python排序算法的整理
#冒泡排序,主要思想:从第一个元素开始,向尾部开始比较大小换位置 def bubble_sort(alist): for j in range(len(alist)-1,0,-1): for i in ...
- Firewalld共享上网及本地yum仓库搭建
1.firewalld共享上网 1.服务端操作(有外网的服务器) 1.开启防火墙并加入开机自启动 [root@zeq ~]# systemctl start firewalld [root@zeq ~ ...
- python应用:经纬度匹配
需要安装第三方包:requests 本次经纬度匹配采用高德地图api,首先将gps坐标转化为高德地图的经纬度坐标,然后再根据转化后的坐标进行匹配. 本次匹配主要是获取距离给定经纬度最近的poi点地址信 ...
- YUM工具使用
一.yum命令概述: 1.简介: yum命令时在Fedora和RedHat以及SUSE中基于rpm的软件包管理器,它可以使系统管理人员交互和自动化地更细与管理RPM软件包,能够从指定的服务器自动下载R ...
- Kubernetes-创建集群(四)
Kubernetes可以运行在多种平台,从笔记本到云服务商的虚拟机,再到机架上的裸机服务器.要创建一个Kubernetes集群,根据不同的场景需要做的也不尽相同,可能是运行一条命令,也可能是配置自己定 ...
- (数据科学学习手札19)R中基本统计分析技巧总结
在获取数据,并且完成数据的清洗之后,首要的事就是对整个数据集进行探索性的研究,这个过程中会利用到各种描述性统计量和推断性统计量来初探变量间和变量内部的基本关系,本篇笔者便基于R,对一些常用的数据探索方 ...
- [POJ1785]Binary Search Heap Construction(笛卡尔树)
Code #include <cstdio> #include <algorithm> #include <cstring> #define N 500010 us ...