ARM TrustZone:STM32MP1平台安全服务访问非安全缓冲区地址转换问题
核心问题原因
你当前崩溃的根本原因是x1寄存器传入的是Linux NS EL1态的虚拟地址,EL3运行时使用独立的地址空间映射,没有对应该虚拟地址的页表项,直接解引用会触发数据中止异常。
实现步骤
Linux侧前置处理
EL3无法直接解析NS EL1的页表完成虚拟地址转换,需要Linux侧先完成虚拟地址到物理地址的转换再传入SMC:
- 如果你使用内核空间内存,调用
virt_to_phys()将内核虚拟地址转换为非安全物理地址后再通过x1传入 - 建议使用
dma_alloc_coherent分配物理连续、无缓存的内存块,避免后续缓存一致性问题 - 禁止直接将内核/用户空间虚拟地址作为参数传入SMC
TF-A EL3侧处理
- 先对传入的物理地址做安全校验,避免非法访问安全内存
- 将非安全物理地址临时映射到EL3的虚拟地址空间,明确标记非安全属性
- 完成数据操作后释放临时映射
- 按需刷写数据缓存保证非安全侧可见
修改后参考代码
#include <lib/mmio.h> #include <lib/xlat_tables/xlat_tables_v2.h> static int32_t my_svc_setup(void) { return 0; } static uintptr_t my_svc_smc_handler(uint32_t smc_fid, u_register_t x1, u_register_t x2, u_register_t x3, u_register_t x4, void *cookie, void *handle, u_register_t flags) { uint16_t smc_function_number = (uint16_t) smc_fid; uintptr_t ns_phys_addr = x1; uint32_t *el3_virt_addr; int map_ret; switch(smc_function_number){ case 123: // 校验传入的物理地址是否为合法非安全内存范围 if (!mmio_is_valid_ns(ns_phys_addr, sizeof(uint32_t))) { SMC_RET1(handle, SMC_INVALID_PARAMETER); } // 动态映射非安全物理地址到EL3地址空间,标记非安全可读写属性 map_ret = mmap_add_dynamic_region(ns_phys_addr, (uintptr_t *)&el3_virt_addr, sizeof(uint32_t), MT_MEMORY | MT_RW | MT_NS); if (map_ret != 0) { SMC_RET1(handle, SMC_INVALID_PARAMETER); } // 执行数据写入 *el3_virt_addr = 42; // 刷写缓存保证非安全侧数据可见 flush_dcache_range((uintptr_t)el3_virt_addr, sizeof(uint32_t)); // 释放临时映射 mmap_remove_dynamic_region((uintptr_t)el3_virt_addr, sizeof(uint32_t)); SMC_RET1(handle, 1); default: SMC_RET1(handle, SMC_UNK); } } DECLARE_RT_SVC( my_svc, OEN_OEM_START, OEN_OEM_END, SMC_TYPE_FAST, my_svc_setup, my_svc_smc_handler );
注意事项
- 频繁调用的场景可以在平台的固定内存映射表中提前配置对应非安全内存段的映射,省略动态映射步骤提升性能
- 所有从非安全侧传入的地址、长度参数必须做严格边界校验,禁止越权访问安全内存
- 如果从用户空间发起SMC,需要在Linux内核的SMC代理驱动中额外做地址合法性校验,避免用户空间传入非法地址
内容的提问来源于stack exchange,提问作者TNTea
相关产品推荐
相关产品推荐

