kernel启动流程-head.S的执行_4.el2_setup

本文基于《arm64_linux head.S的执行流程》系列文章,针对内核5.10,分析head.S的el2_setup执行过程。包括选择栈、判断异常级别、设置端模式、支持VHE或Hyp模式、启动定时器、GIC设置等工作,最后总结了el2_setup的主要工作内容。

1.前言

kernel版本:5.10
平台:arm64

本专题主要基于《arm64_linux head.S的执行流程》系列文章,前者是基于3.18,本专题针对的是内核5.10。主要分析head.S的执行过程。本文主要记录head.S的el2_setup执行过程。

2. el2_setup

选择栈

 msr     SPsel, #1

由于当前异常级别可能是EL1也可能是EL2,因此msr SPsel, #1切换到当前异常级别的SP,如果当前为EL1,则切换到SPEL1,如果当前为EL2,则切换到SPEL2

判断当前的异常级别

mrs     x0, CurrentEL
cmp     x0, #CurrentEL_EL2
b.eq    1f 

从CurrentEL获取当前的异常级别,保存到x0中,并判断是否处于EL2, 如果处于EL2则直接跳转到1f设置EL2的端模式

设置EL2端模式

1:      mov_q   x0, (SCTLR_EL2_RES1 | ENDIAN_SET_EL2)
msr     sctlr_el2, x0

设置EL2下为大端存储

设置EL0/EL1的端模式

 mov_q   x0, (SCTLR_EL1_RES1 | ENDIAN_SET_EL1)
 msr     sctlr_el1, x0
 mov     w0, #BOOT_CPU_MODE_EL1
 isb
 ret

如果处于EL1级别,设置EL0和EL1下为大端存储,将启动时的异常级别EL1保存到w0

VHE支持

#ifdef CONFIG_ARM64_VHE
        /*
         * Check for VHE being present. For the rest of the EL2 setup,
         * x2 being non-zero indicates that we do have VHE, and that the
         * kernel is intended to run at EL2.
         */
        mrs     x2, id_aa64mmfr1_el1
        ubfx    x2, x2, #ID_AA64MMFR1_VHE_SHIFT, #4
#else
        mov     x2, xzr
#endif

通过读取id_aa64mmfr1_el1,存入X2, 判断CPU是支持虚拟主机扩展VHE模式,还是传统的分离Hyp模式。

vhe的全称是Virtualization Host Extension support。是armv8.1的新特性,其最终要就是支持type-2的hypervisors 这种扩展让kernel直接跑在el2上,这样可以减少host和guest之间share的寄存器,并减少overhead of virtualization 具体实现如下面的patch:https://lwn.net/Articles/650524/

Hyp configuration

/* Hyp configuration. */
mov_q   x0, HCR_HOST_NVHE_FLAGS
cbz     x2, set_hcr
mov_q   x0, HCR_HOST_VHE_FLAGS
set_hcr:
		msr     hcr_el2, x0
		isb

当寄存器x2,零表示Hyp模式;非零表示VHE模式
HCR_HOST_NVHE_FLAGS宏定义如下:

#define HCR_HOST_NVHE_FLAGS (HCR_RW | HCR_API | HCR_APK | HCR_ATA)

HCR_HOST_NVHE_FLAGS宏定义如下

#define HCR_HOST_NVHE_FLAGS (HCR_RW | HCR_API | HCR_APK | HCR_ATA)

access physical timer and counter

        /*
         * Allow Non-secure EL1 and EL0 to access physical timer and counter.
         * This is not necessary for VHE, since the host kernel runs in EL2,
         * and EL0 accesses are configured in the later stage
<think> 我们正在讨论的是ARM架构下的KVM虚拟化技术,特别是与EL2(Hypervisor层)相关的模块加载问题。用户提到了`__pkvm_load_el2_module`函数,并关联到之前关于`ENOSYS`(无效系统调用号)的警告。 ### 背景知识 1. **ARMv8/v9架构的异常等级**(参考引用[3]): - EL0:用户态 - EL1:内核态 - EL2:Hypervisor层(负责虚拟机管理) - EL3:Secure Monitor层 2. **KVM在ARM中的运行模式**(参考引用[2]): - 在non-VHE模式下: - Host内核运行在EL1 - Hypervisor(KVM核心)运行在EL2 - `__pkvm_load_el2_module`正是用于加载运行在EL2的模块 ### 问题核心:`__pkvm_load_el2_module`与ENOSYS #### 一、函数作用 `__pkvm_load_el2_module`是ARM KVM中用于**动态加载EL2特权模块**的内部函数,主要功能: 1. 将模块代码从主机内存(EL1)安全传输到EL2专属内存区域 2. 验证模块签名和完整性 3. 初始化模块在EL2的运行时环境 #### 二、ENOSYS错误触发场景 当用户空间(如QEMU)通过`ioctl(KVM_CREATE_VM)`创建虚拟机时: ```c fd = open("/dev/kvm", O_RDWR); // 打开KVM设备 vm_fd = ioctl(fd, KVM_CREATE_VM, 0); // 创建VM ``` 若底层需要加载EL2模块但失败,可能抛出ENOSYS错误,具体路径: ```mermaid graph TD A[用户空间 ioctl调用] --> B(内核KVM模块) B --> C{是否需要加载EL2模块} C -->|是| D[调用__pkvm_load_el2_module] D --> E[执行HVC #0x8000 陷入EL2] E --> F{EL2处理状态} F -->|成功| G[返回EL1继续执行] F -->|失败| H[设置errno=ENOSYS] ``` #### 三、关键机制:HVC调用 `__pkvm_load_el2_module`通过**HVC(Hypervisor Call)指令**陷入EL2: ```armasm hvc #0x8000 // 自定义调用号 ``` 此时若发生`ENOSYS`错误,根本原因可能是: 1. **HVC调用号无效**(直接对应ENOSYS语义) - EL2未注册0x8000对应的处理函数 - 调用号超出EL2支持范围 2. **模块加载条件不满足**: - EL2内存不足(应返回ENOMEM) - 模块签名验证失败(应返回EPERM) - **但在ARMv8.4之前,EL2可能将资源错误统一报告为ENOSYS** #### 四、解决方案 ##### 场景1:HVC调用号错误 **诊断**: 1. 检查内核配置: ```bash zcat /proc/config.gz | grep CONFIG_ARM64_PKVM # 确保配置为 =y 或 =m ``` 2. 验证HVC调用号兼容性: ```c // 内核源码查找调用号定义 arch/arm64/kvm/hyp/pkvm.c: #define PKVM_HYP_CALL_ID 0x8000 ``` **修复**:升级内核至支持该调用号的版本(≥5.10) ##### 场景2EL2模块加载失败 **诊断**:查看内核日志 ```bash dmesg | grep pkvm # 可能出现: # [ error] pkvm: unable to load module, ret=-38 ``` **修复**: 1. 确保固件支持EL2: ```bash cat /proc/cpuinfo | grep Features | grep id_aa64mmfr4 | grep EL2 # 应包含 EL2 支持标志 ``` 2. 修复模块签名: ```bash # 重新编译KVM模块并签名 make M=arch/arm64/kvm scripts/sign-file sha256 signing_key.priv signing_key.x509 pkvm.ko ``` ### 根本原因总结 | 错误类型 | 触发条件 | 解决方向 | |------------------|----------------------------------|-----------------------------| | 架构不支持 | CPU不支持EL2或未开启PKVM | 更换硬件/启用内核配置 | | HVC调用号过时 | 内核与用户空间调用号不匹配 | 同步用户态工具与内核版本 | | 模块加载失败 | 内存不足/签名错误/版本冲突 | 检查日志并修复模块 | > **关键结论**:在ARM KVM环境中,`ENOSYS`若发生在`__pkvm_load_el2_module`上下文中,首要怀疑对象是**HVC调用号的有效性**,其次才是模块加载本身的资源问题。 §§相关问题§§ 1. 如何在ARM架构下调试HVC调用? 2. KVM在non-VHEVHE模式下有何性能差异? 3. 如何为EL2模块生成合法的签名? </think> ### `__pkvm_load_el2_module` 与 ENOSYS 错误分析 #### 一、`__pkvm_load_el2_module` 的核心作用 `__pkvm_load_el2_module` 是 ARM 架构中**用于加载 EL2 特权模块的关键函数**,主要应用于 KVM 虚拟化环境(参考引用[2][3]): 1. **功能定位** 在 ARMv8/v9 non-VHE 模式下,该函数负责将**EL2 特权模块**从主机内存(EL1)安全加载到 Hypervisor 层(EL2): ```c // 伪代码示意 int __pkvm_load_el2_module(void *module_addr, size_t size) { // 1. 验证模块签名和完整性 // 2. 建立 EL2 内存映射 // 3. 通过 HVC 调用陷入 EL2 执行加载 hvc #0x8000 // 触发 EL2 执行 } ``` 2. **执行层级关系**(参考引用[3]) ```mermaid graph TD A[Host Kernel EL1] -->|调用| B(__pkvm_load_el2_module) B -->|HVC 指令| C[Hypervisor EL2] C --> D[加载特权模块] ``` 3. **典型应用场景** - KVM 初始化时加载 pKVM(Protected KVM)模块 - 安全敏感的虚拟机监控组件加载 - 需要 EL2 特权访问的硬件虚拟化驱动 #### 二、ENOSYS 错误与加载过程的关联 当出现 `ENOSYS`(无效系统调用号)错误时,通常发生在以下环节: ##### 场景1:HVC 调用号无效 ```c // 内核源码片段 (arch/arm64/kvm/hyp/pkvm.c) static int __pkvm_load_el2_module(...) { ... // 触发 EL2 执行的 HVC 调用 asm volatile("hvc #0x8000" : ... ); // 关键指令 } ``` - **错误机制**: 若 `0x8000` 未在 EL2 注册为合法调用号 → 触发 `ENOSYS` - **常见原因**: 1. 内核与固件版本不匹配 2. EL2 未实现该调用号的处理函数 3. 安全启动策略禁用了该调用 ##### 场景2:模块加载协议变更 ```diff ! 加载协议版本冲突示例 + 新版本协议要求模块头包含 [magic: 0xPKV3] - 旧版本模块头使用 [magic: 0xPKV2] ``` 当 EL2 期望的模块格式与传入模块不兼容时,可能通过 `ENOSYS` 拒绝执行 ##### 场景3:权限不足 在启用 TEE(如 TrustZone)的系统: 1. EL3 阻止了 EL1→EL2 的模块加载请求 2. pKVM 的安全策略禁止动态加载 → 内核可能统一返回 `ENOSYS` 避免泄露安全信息 #### 三、诊断与解决方案 ##### 诊断步骤 1. **检查 HVC 调用号有效性** ```bash # 查看 EL2 支持的调用号范围 grep ARM_SMCCC_CALL_VAL /path/to/arm-smccc.h ``` 2. **验证模块兼容性** ```bash # 检查模块头格式 hexdump -C module.ko | head -n 5 ``` 3. **调试 EL2 日志** ```bash # 启用 EL2 调试输出 (需内核支持) echo 8 > /sys/module/kvm/parameters/debug_level dmesg | grep EL2 ``` ##### 解决方案 1. **修复调用号不匹配** ```c // 内核补丁示例:更新调用号 - asm volatile("hvc #0x8000"); + asm volatile("hvc #0x8010"); // 适配新固件 ``` 2. **模块格式适配** ```makefile # Makefile 修改:强制使用新ABI CFLAGS += -DPKVM_MODULE_ABI_VERSION=3 ``` 3. **安全策略调整** ```bash # 禁用严格检查 (仅测试环境) echo N > /sys/kernel/debug/pkvm/enforce_module_signing ``` #### 四、根本原因总结 | **问题类型** | **触发条件** | **解决方案** | |-------------------|----------------------------------|-----------------------------| | HVC 调用号失效 | EL2 未注册该调用号 | 同步内核与固件调用号定义 | | 模块协议不兼容 | EL2 拒绝旧版模块格式 | 重新编译模块适配新ABI | | 安全策略拦截 | TEE/SE 策略禁止动态加载 | 调整安全策略或签名密钥 | | 硬件支持缺失 | CPU 不支持 EL2 模块加载 | 更换硬件或回退软件方案 | > **关键结论**:当 `__pkvm_load_el2_module` 相关流程出现 `ENOSYS` 时,首要排查方向是 **HVC 调用号的合法性和模块格式兼容性**,其次检查安全策略限制(参考引用[2][3])。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值