当前位置: 首页 > 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.dtcms.com/a/302722.html

相关文章:

  • 【Web】京麒CTF 2025 决赛 wp
  • USRP-X440 2025年太空与导弹防御研讨会
  • 近屿智能正式发布AI得贤招聘官的AI面试官智能体6.3版本:交付替代人类面试官的打分结果
  • 1990-2024年上市公司财务指标/应计利润数据(30+指标)
  • MFC UI对话框
  • 基于Uniapp及Spring Boot的奢侈品二手交易平台的设计与实现/基于微信小程序的二手交易系统
  • 零基础学习性能测试第九章:全链路追踪-系统中间件节点监控
  • 【pytest高阶】源码的走读方法及插件hook
  • Ubuntu lamp
  • 商用车的自动驾驶应用场景主要包括七大领域
  • 十七、K8s 可观测性:全链路追踪
  • AI对服务器行业的冲击与启示:从挑战走向重构
  • vue3【组件封装】头像裁剪 S-avatar.vue
  • 谋先飞(Motphys)亮相 2025 世界人工智能大会:以物理仿真重构智能未来
  • Apache Commons VFS:Java内存虚拟文件系统,屏蔽不同IO细节
  • YOLOv11改进:添加SCConv空间和通道重构卷积二次创新C3k2
  • Error reading config file (/home/ansible.cfg): ‘ACTION_WARNINGS(default) = True
  • 如何理解有符号数在计算机中用​​补码​​存储
  • 网络安全第14集
  • C51:使用PWM波调节LED灯的亮度
  • GitLab 18.2 发布几十项与 DevSecOps 有关的功能,可升级体验【三】
  • 如何检测并修复服务器中的rootkit威胁
  • 中型企业如何用 RUM 技术破解地理分布式用户体验难题?从指标监测到优化实操
  • 暴雨服务器更懂人工智能+
  • Jetson Orin nx识别不到imx219 需要额外设置
  • [ The Missing Semester of Your CS Education ] 学习笔记 Vim篇
  • 4.DRF 认证--Authentication4.DRF 认证--Authentication
  • 从文件到文件描述符:理解程序与文件的交互本质
  • TapData 出席 TDBC 2025 可信数据库发展大会,分享“实时+信创”时代的数据基础设施演进路径
  • Kylin10 安装tomcat9