慢慢欣赏arm64内核启动9 primary_entry之el2_setup代码第七部分

分析代码

	cbz	x2, install_el2_stub

	mov	w0, #BOOT_CPU_MODE_EL2		// This CPU booted in EL2
	isb
	ret

SYM_INNER_LABEL(install_el2_stub, SYM_L_LOCAL)
	/*
	 * When VHE is not in use, early init of EL2 and EL1 needs to be
	 * done here.
	 * When VHE _is_ in use, EL1 will not be used in the host and
	 * requires no configuration, and all non-hyp-specific EL2 setup
	 * will be done via the _EL1 system register aliases in __cpu_setup.
	 */
	mov_q	x0, (SCTLR_EL1_RES1 | ENDIAN_SET_EL1)
	msr	sctlr_el1, x0

	/* Coprocessor traps. */
	mov	x0, #0x33ff
	msr	cptr_el2, x0			// Disable copro. traps to EL2

	/* SVE register access */
	mrs	x1, id_aa64pfr0_el1
	ubfx	x1, x1, #ID_AA64PFR0_SVE_SHIFT, #4
	cbz	x1, 7f

	bic	x0, x0, #CPTR_EL2_TZ		// Also disable SVE traps
	msr	cptr_el2, x0			// Disable copro. traps to EL2
	isb
	mov	x1, #ZCR_ELx_LEN_MASK		// SVE: Enable full vector
	msr_s	SYS_ZCR_EL2, x1			// length for EL1.

	/* Hypervisor stub */
7:	adr_l	x0, __hyp_stub_vectors
	msr	vbar_el2, x0

	/* spsr */
	mov	x0, #(PSR_F_BIT | PSR_I_BIT | PSR_A_BIT | PSR_D_BIT |\
		      PSR_MODE_EL1h)
	msr	spsr_el2, x0
	msr	elr_el2, lr
	mov	w0, #BOOT_CPU_MODE_EL2		// This CPU booted in EL2
	eret
SYM_FUNC_END(el2_setup)

install_el2_stub

el2_setup分析了这么多,终于到了结尾部分。但是这段代码还是较长。我们一点点分析。
第1行代码:x2寄存器如果为0,则跳转到函数 install_el2_stub 执行;
其中,x2寄存器始终保存 VHE 的值,也就是说,如果当前 PE 不支持 VHE,则执行 install_el2_stub,我们分析一下 函数 install_el2_stub

第7行代码就是函数 install_el2_stub 的开头部分,这个函数看名字像是个桩函数,安装 el2 异常模式的桩,这是啥意思呢,我们先看一下注释吧。

第8行到14行是注释部分,我们尝试翻译一下:
如果 PE 不支持 VHE 模式,那么第一阶段的启动过程中,对异常模式 EL1/EL2 的初始化到此结束了;
但是如果 PE 支持 VHE 模式,那么主机侧不需要 EL1 异常模式,并且 EL1 异常模式也不需要配置,所有非hyp特有的EL2设置都将在函数__cpu_setup中通过EL1系统寄存器别名完成。
这段话表面翻译起来,还是绕,只能等到后续真正分析 __cpu_setup 再慢慢消化了。

第15行到16行代码在 el2_setup 的前面的章节分析过。

CPTR_EL2

第18行到第20行涉及到寄存器 CPTR_EL2, Architectural Feature Trap Register (EL2),该寄存器的含义如下:
Controls:
    Trapping to EL2 of access to CPACR, CPACR_EL1, trace functionality, and to SVE, Advanced SIMD and floating-point functionality.
    EL2 access to trace functionality, and to SVE, Advanced SIMD and floating-point functionality.
该寄存器用于控制对特定架构特性的陷阱(trap)行为。
具体来说,CPTR_EL2寄存器允许EL2(虚拟化扩展的虚拟监控程序Hypervisor运行的级别)管理对处理器某些功能的访问,当这些功能被访问时,可以决定是否将它们重定向为陷阱(trap)异常,以便EL2可以进行处理。
CPTR_EL2寄存器中的每一位都对应一个特定的架构特性或指令集扩展。当某位被设置为1时,表示对应的特性或指令集扩展的访问将被陷阱到EL2。这允许Hypervisor对底层硬件功能进行更细粒度的控制,防止客户操作系统(运行在EL1或更低级别)直接访问这些特性。
通过控制对特定架构特性的访问,CPTR_EL2有助于在虚拟化环境中实现安全隔离。它确保只有Hypervisor能够访问或控制那些可能对系统稳定性或安全性产生影响的特性。
根据具体的应用场景和安全需求,Hypervisor可以通过编程方式配置CPTR_EL2寄存器,灵活地启用或禁用对特定架构特性的陷阱。这种灵活性使得ARM64架构能够支持多种不同的虚拟化解决方案和场景。
魔数字0x33ff 主要涉及2个位域
TFP, bit [10]
Traps accesses to SVE, Advanced SIMD and floating-point functionality to EL2 when it is enabled in the current Security state, from both Execution states.
    0b'0 This control does not cause any instructions to be trapped.
    0b'1 Any attempt at EL0, EL1 or EL2, to execute an instruction that uses the registers associated with SVE, Advanced SIMD and floating-point execution is trapped to EL2 when it is enabled in the current Security state, subject to the exception prioritization rules, unless it is trapped by CPTR_EL2.TZ.
TZ, bit [8]
When SVE is implemented:
    Traps execution at EL2, EL1, or EL0 of SVE instructions and instructions that access SVE System registers to EL2 when it is enabled in the current Security state.
        0b'0 This control does not cause any instruction to be trapped.
        0b'1 This control causes these instructions to be trapped, subject to the exception prioritization rules.
        This field resets to an architecturally UNKNOWN value.
Otherwise:
    Reserved, RES1.

SVE

第22行到第25行涉及到 SVE寄存器的访问控制。
寄存器 ID_AA64PFR0_EL1 在前面的章节讲解过,宏 ID_AA64PFR0_SVE_SHIFT 的定义如下

#define ID_AA64PFR0_SVE_SHIFT		32

相关位域定义如下:
SVE, bits [35:32]
    From ARMv8.2:
        Scalable Vector Extension. Defined values are:
        0b'0000SVE is not implemented.
        0b'0001SVE is implemented.
        All other values are reserved.
    Otherwise:
        Reserved, RES0.
含义是:是否支持 SVE 矢量,如果不支持 SVE,则直接跳转到第34行。

第27行到第31行是对 SVE 的配置

异常向量入口

第33行到第35行异常向量入口地址的配置,当然,当前情况下,只是配置了个桩

第34行将 __hyp_stub_vectors 的地址加载到了x0寄存器;

第35行将 x0的值赋值给了寄存器 vbar_el2
VBAR_EL2, Vector Base Address Register (EL2)的含义如下:
Holds the vector base address for any exception that is taken to EL2.
也就是说,传递给EL2异常模式下的异常向量的入口地址。

异常模式EL2到EL1的切换

第37行到第43行是整个 el2_setup 的核心部分之一。主要作用就是当前 PE 如果不支持 VHE,那么需要从当前的EL2异常模式切换到EL1异常模式继续运行。

涉及到的宏定义如下:

#define PSR_F_BIT	0x00000040
#define PSR_I_BIT	0x00000080
#define PSR_A_BIT	0x00000100
#define PSR_D_BIT	0x00000200
#define PSR_MODE_EL1h	0x00000005


切换前需要设置两个寄存器:
    elr_el2:EL1从lr寄存器保留的地址继续运行的地址, lr保留的是 install_el2_stub 函数的返回地址;
    spsr_el2:EL2的状态保留在 spsr_el2寄存器(Saved Program Status Register (EL2))
        其中异常返回前(When exception taken from AArch64 state:),位域bits[3:0]的含义如下:
        M[3:0], bits [3:0]
        AArch64 state (Exception level and selected SP) that an exception was taken from.
        0b'0000    EL0t.
        0b'0100    EL1t.
        0b'0101    EL1h.
        Other values are reserved, and returning to an Exception level that is using AArch64 with a reserved
        value in this field is treated as an illegal exception return.
        The bits in this field are interpreted as follows:
            M[3:2] holds the Exception Level.
            M[1] is unused and is RES0 for all non-reserved values.
            M[0] is used to select the SP:
                0 means the SP is always SP0.
                1 means the exception SP is determined by the EL.
        This field resets to an architecturally UNKNOWN value.
        根据上面芯片手册的描述,我们得知,位域M[3:0]表示异常等级切换后,需要切换回的异常等级模式,根据宏定义 PSR_MODE_EL1h 的定义,我们得知切换到 EL1, 并且使用 SP1 作为堆栈
        其它涉及的位域bits[9:6]表示屏蔽所有的异常干扰

第42行将启动的异常模式EL2 保留在 w0中。

第43行 eret 进行模式切换,从EL2模式切换到EL1模式运行,切换到 install_el2_stub 函数的返回地址继续运行,注意,不是第3行,而是 el2_setup整个函数的lr寄存器,lr 保存的是进入 el2_setup 函数时下一条指令的地址,也就是 el2_setup 函数返回后应该继续执行的位置 继续运行。原因(nbz是jump指令而不是call指令),整个el2_setup就没有函数调用流程。

<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-VHE和VHE模式下有何性能差异? 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])。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值