当前位置: 首页 > news >正文

ATF 运行时服务

ATF 运行时服务

前言

芯片所有的软硬件资源都能够在 NXP 官网找到,本文档也是对 NXP 开源 LSDK 代码工程的学习与分析。
官网链接如下:
LSDK SDK资料
LX2160 芯片资料

本笔记是 lx2160 芯片的 ATF 固件工程源码的学习记录。

0. 关于可行根中的运行时服务

每一个服务通过两个概念来区分:

  1. 服务类型:fast = 1 或 yeild = 0;
  2. 服务 oen 编号: 每一个服务会被分配一个 oen 编号或一个 oen 范围;

比如,psci 服务的类型为 fast, oen 编号为4。

服务类型与 oen 编号会在进行 smc 调用时,体现在 smcid 中。其中 bit31 表示服务类型,bit[29:24] 表示 oen 范围。

1. psci 服务注册

psci 服务属于 arm 标准规范的运行时服务, oen 编号为 4,在 atf 代码中,BL31 的代码作为可信根服务.会一致驻留在 DDR0 的最后66MB 范围的安全内存中:0xfbe00000 : 0xffffffff.

bl31 镜像使用 DECLARE_RT_SVC 定义一个运行时服务描述符,宏定义在 atf\include\common\runtime_svc.h

/** Convenience macros to declare a service descriptor* 辅助宏声明服务描述符*/
#define DECLARE_RT_SVC(_name, _start, _end, _type, _setup, _smch)	\static const rt_svc_desc_t __svc_desc_ ## _name			\__section("rt_svc_descs") __used = {			\.start_oen = (_start),				\.end_oen = (_end),				\.call_type = (_type),				\.name = #_name,					\.init = (_setup),				\.handle = (_smch)				\}

该宏会定义在 .section rt_svc_descs 段中定义一个 rt_svc_desc_t 描述符变量:__svc_desc_ ## _name, 运行时服务描述符对象类型定义如下, 文件atf\include\common\runtime_svc.h

typedef struct rt_svc_desc {uint8_t start_oen;uint8_t end_oen;uint8_t call_type;/* 运行时服务函数类型 */const char *name;rt_svc_init_t init; /* 初始化函数 */rt_svc_handle_t handle; /* 运行时服务处理函数 */
} rt_svc_desc_t;

成员说明如下:

成员描述
start_oen当前服务所属的起始 oen 编号
end_oen当前服务所属的结束 oen 编号
call_type服务类型,fast 为1,yeild 为0
name服务名
init服务初始化函数
handlesmc 调用处理函数中会调用的服务处理函数

PSCI 服务定义与注册在文件 atf\services\std_svc\std_svc_setup.c中:

/* Register Standard Service Calls as runtime service */
DECLARE_RT_SVC(std_svc,OEN_STD_START,OEN_STD_END,SMC_TYPE_FAST,std_svc_setup,std_svc_smc_handler
);

上述代码会将 psci 服务注册到 .section rt_svc_descs 段, 该段只保存 rt_svc_desc_t对象,可以理解为数组。

bl31_main() 中,运行定义在文件atf\common\runtime_svc.c 文件中的runtime_svc_init() 会调用注册的运行时服务初始化函数:

void __init runtime_svc_init(void)
{int rc = 0;uint8_t index, start_idx, end_idx;rt_svc_desc_t *rt_svc_descs;assert((RT_SVC_DESCS_END >= RT_SVC_DESCS_START) &&(RT_SVC_DECS_NUM < MAX_RT_SVCS));if (RT_SVC_DECS_NUM == 0U)return;(void)memset(rt_svc_descs_indices, -1, sizeof(rt_svc_descs_indices));rt_svc_descs = (rt_svc_desc_t *) RT_SVC_DESCS_START;for (index = 0U; index < RT_SVC_DECS_NUM; index++) {rt_svc_desc_t *service = &rt_svc_descs[index];rc = validate_rt_svc_desc(service);if (rc != 0) {ERROR("Invalid runtime service descriptor %p\n",(void *) service);panic();}if (service->init != NULL) {rc = service->init();if (rc != 0) {ERROR("Error initializing runtime service %s\n",service->name);continue;}}start_idx = (uint8_t)get_unique_oen(service->start_oen,service->call_type);end_idx = (uint8_t)get_unique_oen(service->end_oen,service->call_type);assert(start_idx <= end_idx);assert(end_idx < MAX_RT_SVCS);for (; start_idx <= end_idx; start_idx++)rt_svc_descs_indices[start_idx] = index;}
}

同时会根据服务所属的 oen 编号范围,与服务类型,初始化 oen 描述符索引数组rt_svc_descs_indices[] ,数组索引的计算方式为:
index=(type<<6)+oenindex = (type << 6) + oen index=(type<<6)+oen
数组成员的值为服务描述符在 rt_svc_descs 段中的索引:rt_svc_descs_indices[start_idx] = index

BL31 的 SMC handler 根据保存在 x0 的 smcid 中bit[31]与 bit[29:24] (上一节也有提及), 可以直接换算出低异常等级请求的 oen 数组索引。

1. psci 服务调用

当低异常等级触发 SMC 调用后,会运行 BL31 注册的 SMC 服务(BL31阶段的代码会完整保留在 DDR 的 0xfbe00000-0xffffffff 中,因为这部分内存在 EL3 的 MMU 配置页表中被配置为安全内存,非安全 EL2及更低异常等级无法正常访问。)

BL31 异常向量表定义在文件atf\bl31\aarch64\runtime_exceptions.S 中,

如果低异常等级为 aarch64, 那么进行 SMC 调用时,根据 armv8 异常模型,会进入 VBAR_EL3 + 0x400 向量地址处(aarch64 状态的低异常等级触发了 EL3 同步异常), 也就是函数 sync_exception_aarch64

	/* ---------------------------------------------------------------------* Lower EL using AArch64 : 0x400 - 0x600, 低异常等级触发的异常,低异常等级处于 aarch64 状态* ---------------------------------------------------------------------*/
vector_entry sync_exception_aarch64/* 低异常等级调用 SMC 后,此时 sp 使用 sp_el3, 指向上下文对象的地址 *//** This exception vector will be the entry point for SMCs and traps* that are unhandled at lower ELs most commonly. SP_EL3 should point* to a valid cpu context where the general purpose and system register* state can be saved.*/apply_at_speculative_wa/* check_and_unmask_ea 会解除 SError 的屏蔽, 并将 x30 的值保存到上下文对象中 */check_and_unmask_ea/* 处理同步异常, 包括 SMC 调用 */handle_sync_exception
end_vector_entry sync_exception_aarch64

结合反汇编代码,上述汇编代码扩展为:

00000000fbe0b400 <sync_exception_aarch64>:fbe0b400:	d50344ff 	msr	daifclr, #0x4fbe0b404:	f9007bfe 	str	x30, [sp, #240]fbe0b408:	d53e521e 	mrs	x30, esr_el3fbe0b40c:	d35a7fde 	ubfx	x30, x30, #26, #6fbe0b410:	f1004fdf 	cmp	x30, #0x13fbe0b414:	54fc9d60 	b.eq	fbe047c0 <smc_handler>  // b.nonefbe0b418:	f1005fdf 	cmp	x30, #0x17fbe0b41c:	54fc9d40 	b.eq	fbe047c4 <smc_handler64>  // b.nonefbe0b420:	f9407bfe 	ldr	x30, [sp, #240]fbe0b424:	17ffe35e 	b	fbe0419c <enter_lower_el_sync_ea>

在上述代码中,根据 esr_el3 中 bit[31:26] 的 EC值,判断是否是低异常等级触发的 SMC 调用:

EC 值描述
EC 值 = 0x13低异常等级为 AARCH32 触发的 SMC 调用,运行 smc_handler
EC 值 = 0x17低异常等级为 AARCH64 触发的 SMC 调用, 运行 smc_handler64
其他 EC 值请根据 arm 参考手册自行分析

在上述代码中,入口处 sp 指针使用的是 sp_el3, 该指针保存了当前核的 cpu_cotext_t 对象的地址。(可以查看 《BL31 启动流程分析》,最后在跳转到低异常等级的函数 el3_exit()代码中,将 EL3 使用的 sp 切换为了 sp_el3.)

代码 str x30, [sp, #240] 表示保存 x30 的值到 cpu_context_t 对象中。

2. smc_handler64 实现

smc_handler64() 同样定义在 atf\bl31\aarch64\runtime_exceptions.S 文件中,该函数不会返回,反汇编如下(请结合源码阅读):

00000000fbe047c4 <smc_handler64>:/* 保存低异常等级的通用寄存器到 cpu_context_t 中 */fbe047c4:	97ffffe2 	bl	fbe0474c <save_gp_pmcr_pauth_regs>/* x5 清0 */fbe047c8:	aa1f03e5 	mov	x5, xzr/* 将 cpu_context_t 的地址保存在 x6 中 */fbe047cc:	910003e6 	mov	x6, sp/* 获取之前 EL3 等级的运行栈 */fbe047d0:	f94088cc 	ldr	x12, [x6, #272]/* 将 sp 指针切换为 sp_el0 */fbe047d4:	d50040bf 	msr	spsel, #0x0/* 保存特殊寄存器到 cpu_context_t 对象中 */fbe047d8:	d53e4010 	mrs	x16, spsr_el3fbe047dc:	d53e4031 	mrs	x17, elr_el3fbe047e0:	d53e1112 	mrs	x18, scr_el3fbe047e4:	a911c4d0 	stp	x16, x17, [x6, #280]fbe047e8:	f90080d2 	str	x18, [x6, #256]/* x7 保存低异常等级安全状态 */fbe047ec:	b3400247 	bfxil	x7, x18, #0, #1/* 恢复 el3 运行栈 */fbe047f0:	9100019f 	mov	sp, x12/* 根据 x0 传入的 smc_func_id 计算 oen 描述符数组 rt_svc_descs_indices[] 索引 *//* index = (type << 6) + oen */fbe047f4:	d3587410 	ubfx	x16, x0, #24, #6fbe047f8:	d35f7c0f 	ubfx	x15, x0, #31, #1fbe047fc:	aa0f1a10 	orr	x16, x16, x15, lsl #6fbe04800:	900000ee 	adrp	x14, fbe20000 <type_el3_interrupt_table+0x1b8>fbe04804:	911791ce 	add	x14, x14, #0x5e4/* 根据描述符数组索引获取 rt_svc_descs 数组索引,并保存在 w15 */fbe04808:	387069cf 	ldrb	w15, [x14, x16]/* 判断数组是否越界 */fbe0480c:	373800cf 	tbnz	w15, #7, fbe04824 <smc_unknown>/* 根据 oen 描述符数组成员值,获取 rt_svc_descs 数组中的某一个具体描述符 */fbe04810:	10041e0b 	adr	x11, fbe0cbd0 <__RT_SVC_DESCS_START__+0x18>fbe04814:	531b69ea 	lsl	w10, w15, #5/* 获取 handler 地址 */fbe04818:	f86a496f 	ldr	x15, [x11, w10, uxtw]/* 跳转到运行时服务的 handler 进行处理 */fbe0481c:	d63f01e0 	blr	x15/* 根据 cpu_context_t 返回低异常等级 */fbe04820:	17fffe35 	b	fbe040f4 <el3_exit>

该函数主要作用为保存低异常等级的代码执行环境(上下文)到 cpu_context_t 对象中,包括通用目的寄存器 x0-x30, 特殊寄存器spsr_el3, elr_el3, scr_el3等。。

低异常等级的代码执行环境(上下文)保存完毕后,会根据 smcid 计算请求的服务在 oen 数组 rt_svc_descs_indices[]的成员索引号:

index = (type << 6) | oen

其中,type 为 bit31, oen 为 bit[29:24]。

rt_svc_descs_indices[] 数组保存的是具体服务描述符在 rt_svc_descs 数组(section)中的索引。

最后,会通过 blr x15 跳转到具体的服务 handler 函数, 其中:

  1. x0 保存 smcid,由低异常等级传入;
  2. x1 由低异常等级传入;
  3. x2 由低异常等级传入;
  4. x3 由低异常等级传入;
  5. x4 由低异常等级传入;
  6. x5 在 smc_handler64 中设置为 0;
  7. x6 在 smc_handler64 中设置为 cpu_context_t 对象地址;
  8. x7 为低异常等级安全状态,目前固定为非安全为 1。

handler 函数执行完毕后,最后会执行 el3_exit() 函数根据之前配置的 cpu_context_t 对象返回低异常等级中继续执行,而且在该代码中,会将 sp 指针切换为 sp_el3, sp_el3固定保存 cpu_context_t 对象地址。

http://www.lryc.cn/news/603079.html

相关文章:

  • ros2的package.xml和rosdep
  • 基于深度学习的医学图像分析:使用3D CNN实现肿瘤检测
  • 第十天:字符菱形
  • 一个Pycharm窗口添加多个项目来满足运行多个项目的需求
  • DDoS攻击防御:从5G到T级防护方案全对比
  • 企业级日志分析系统ELK
  • Python动态规划:从基础到高阶优化的全面指南(3)
  • 历史版本的vscode下载地址
  • 实验-静态路由
  • 智慧工地系统:科技赋能建筑新未来
  • 学习dify:一个开源的 LLM 应用开发平台
  • 【GitHub Workflows 基础(二)】深入理解 on、jobs、steps 的核心语法与执行逻辑
  • 【2025/07/28】GitHub 今日热门项目
  • 【iOS】类和分类的加载过程
  • LNMP架构+wordpress实现动静分离
  • Cacti RCE漏洞复现
  • 四、计算机组成原理——第1章:计算机系统概述
  • 可扩展架构模式——微服务架构最佳实践应该如何去做(方法篇)
  • 《汇编语言:基于X86处理器》第10章 结构和宏(2)
  • linux命令grep的实际应用
  • 在虚拟机ubuntu上修改framebuffer桌面不能显示图像
  • 1.vue体验
  • Android 媒体播放开发完全指南
  • Ansible提权sudo后执行报错
  • 电脑开机不显示网卡的原因
  • selenium 特殊场景处理
  • 刘润探展科大讯飞WAIC,讯飞医疗AI该咋看?
  • CSP-J 2022_第三题逻辑表达式
  • 技术工具箱 |五、一个避免头文件重复引用的 Python 脚本
  • 嵌入式Linux:注册线程清理处理函数