ARM64TrustFirmware[五]
- 开源代码
- 2025-08-30 01:18:01
![ARM64TrustFirmware[五]](/0pic/pp_67.jpg)
本章介绍 ATF 中的 Runtime Service 是如何定义和被调用的。
要了解 SMC,必须从 SMC 指令本身开始,其指令如下图:
指令格式为:SMC #<imm>,从官方文档了解到该指令只能在 EL1 以及更高的异常等级上调用,用户空间 EL0 是无法直接调用该指令的。
同时根据 ARM 官方文档关于 SMCCC 的描述如下:
可以知道 SMC 可以根据不同的 OEN(Owning Entity Number)区分不同分服务的功能,当注册具体了 SMC runtime service 时,需要为对应的 service 指定正确的 OEN 范围。
这里比较抽象,我们来举个例子就明白了,比如我在 Hypervisor 中介绍的关于虚拟机管理器通过 SMC 系统调用去 ATF 通过 PSCI 协议拉起从核,其中启动从核的 PSIC 的 function id 为:PSCI_CPU_ON_AARCH64(0xc4000003),不考虑后面跟的参数,也就是最终调用了:
smc #0xc4000003按照其 32bit 划分:
bit[31] = 1:Fast call;bit[32] = 0:SMC64 call convention;bit[29 : 24] = 4:Standard service call;bit[15 : 0] = 3:该类型的 call type 下的 function number;我们对 runtime service 进行了如下的定义和分类:
ARM Architecture Calls:获取 smcc 版本,arch features 等;CPU Service Calls:提供针对该平台的 CPU 实现特定服务的接口;SIP Service Calls:System IP 的驱动;OEM Service Calls:OEM 服务的接口;Standard Secure Service:PSCI call 就属于这个服务类型;... ... Runtime Service 的注册:BL31 通过宏定义 DECLARE_RT_SVC 注册一个服务:
比如 Standard Service 的注册:
/* 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 );比如 SIP Service 的注册:
/* Define a runtime service descriptor for fast SMC calls */ DECLARE_RT_SVC( arm_sip_svc, OEN_SIP_START, OEN_SIP_END, SMC_TYPE_FAST, arm_sip_setup, arm_sip_handler );在注册处理 SMC 命令服务时,DECLARE_RT_SVC 空定义定义了一个结构体__svc_desc_##_name,并将其放到了 OS 镜像的一个特殊的段.rt_svc_descs 中,其中__RT_SVC_DESCS_START__ 和__RT_SVC_DESCS_END__ 是该断的起始地址和结束地址,并且可以通过地址范围计算出服务的数量 RT_SVC_DECS_NUM:
/* * 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) \ } #define RT_SVC_DESCS \ . = ALIGN(STRUCT_ALIGN); \ __RT_SVC_DESCS_START__ = .; \ KEEP(*(.rt_svc_descs)) \ __RT_SVC_DESCS_END__ = .; Runtime Service 的启动: void __init runtime_svc_init(void) { int rc = 0; uint8_t index, start_idx, end_idx; rt_svc_desc_t *rt_svc_descs; /* Assert the number of descriptors detected are less than maximum indices */ assert((RT_SVC_DESCS_END >= RT_SVC_DESCS_START) && (RT_SVC_DECS_NUM < MAX_RT_SVCS)); /* If no runtime services are implemented then simply bail out */ if (RT_SVC_DECS_NUM == 0U) return; /* Initialise internal variables to invalid state */ (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]; /* * An invalid descriptor is an error condition since it is * difficult to predict the system behaviour in the absence * of this service. */ rc = validate_rt_svc_desc(service); if (rc != 0) { ERROR("Invalid runtime service descriptor %p\n", (void *) service); panic(); } /* * The runtime service may have separate rt_svc_desc_t * for its fast smc and yielding smc. Since the service itself * need to be initialized only once, only one of them will have * an initialisation routine defined. Call the initialisation * routine for this runtime service, if it is defined. */ if (service->init != NULL) { rc = service->init(); if (rc != 0) { ERROR("Error initializing runtime service %s\n", service->name); continue; } } /* * Fill the indices corresponding to the start and end * owning entity numbers with the index of the * descriptor which will handle the SMCs for this owning * entity range. */ 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; } } 先通过RT_SVC_DESCS_START 和RT_SVC_DESCS_END 之间的大小,计算出当前的服务数量RT_SVC_DECS_NUM;从RT_SVC_DESCS_START 位置开始遍历所有服务,拿到其结构体地址service;通过validate_rt_svc_desc 对该服务的参数进行校验;通过 service->init 初始化该服务,当然有些服务可能不需要 init setup;初始化rt_svc_descs_indices 表;在 EL1/EL2 发起 SMC 调用时,smc_fid 作为第一个参数传递给 ATF,ATF 需要根据 smc_fid 定位到是哪种 service。由于 service 有两种 type 组成,每种 type 的 oen 最多有 64 个,所以 type 和 oen 的组合会有 128 中可能。为了加快查找 service 类别,ATF 在初始化 runtime service 时会维护一个表,我们用如下两个服务作为例子:
DECLARE_RT_SVC( arm_sip_svc, OEN_SIP_START, == 2 OEN_SIP_END, == 2 SMC_TYPE_FAST, == 1 arm_sip_setup, arm_sip_handler ); start_idx = SMC_TYPE_FAST << 6 | OEN_SIP_START = 66 end_idx = SMC_TYPE_FAST << 6 | OEN_SIP_END = 66 DECLARE_RT_SVC( tos_svc, OEN_TOS_START, == 50 OEN_TOS_END, == 63 SMC_TYPE_YIELD, == 0 tos_svc_setup, tos_svc_smc_handler ); start_idx = SMC_TYPE_YIELD << 6 | OEN_TOS_START = 50 end_idx = SMC_TYPE_YIELD << 6 | OEN_TOS_END = 63 SMC 的处理流程:根据 bl31/aarch64/runtime_exceptions.S 中异常向量表 runtime_exceptions 的定义,当 EL1/EL2 发起 SMC 调用后,会触发sync_exception_aarch64,并在最终的 smc_handler64 处理中跳转到相应的服务处理函数中:
runtime_exceptions -->sync_exception_aarch64 -->handle_sync_exception -->sync_handler64sync_handler64 中根据参数 1 中 smc_fid 的 type 和 oen 得到 desc_index,由rt_svc_descs_indices[desc_index] 可以得到 svc_index,再 由rt_svc_descs[svc_index] 就可以得到对应 runtime service 描述符的指针rt_svc_desc_t,最后调用其 handler 处理函数:
and x16, x0, #(FUNCID_SVE_HINT_MASK << FUNCID_SVE_HINT_SHIFT) orr x7, x7, x16 bic x0, x0, #(FUNCID_SVE_HINT_MASK << FUNCID_SVE_HINT_SHIFT) /* Get the unique owning entity number */ ubfx x16, x0, #FUNCID_OEN_SHIFT, #FUNCID_OEN_WIDTH ubfx x15, x0, #FUNCID_TYPE_SHIFT, #FUNCID_TYPE_WIDTH orr x16, x16, x15, lsl #FUNCID_OEN_WIDTH /* Load descriptor index from array of indices */ adrp x14, rt_svc_descs_indices add x14, x14, :lo12:rt_svc_descs_indices ldrb w15, [x14, x16] /* Any index greater than 127 is invalid. Check bit 7. */ tbnz w15, 7, smc_unknown /* * Get the descriptor using the index * x11 = (base + off), w15 = index * * handler = (base + off) + (index << log2(size)) */ adr_l x11, (__RT_SVC_DESCS_START__ + RT_SVC_DESC_HANDLE) lsl w10, w15, #RT_SVC_SIZE_LOG2 ldr x15, [x11, w10, uxtw] /* * Call the Secure Monitor Call handler and then drop directly into * el3_exit() which will program any remaining architectural state * prior to issuing the ERET to the desired lower EL. */ #if DEBUG cbz x15, rt_svc_fw_critical_error #endif blr x15以 psci 为例,由于 psci 属于 standard service,所以会调用std_svc_smc_handler,在std_svc_smc_handler 中再根据smc_fid 区分不同的细分服务,然后调用psci_smc_handler 去处理 psci 协议:
static uintptr_t std_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) { if (((smc_fid >> FUNCID_CC_SHIFT) & FUNCID_CC_MASK) == SMC_32) { /* 32-bit SMC function, clear top parameter bits */ x1 &= UINT32_MAX; x2 &= UINT32_MAX; x3 &= UINT32_MAX; x4 &= UINT32_MAX; } /* * Dispatch PSCI calls to PSCI SMC handler and return its return * value */ if (is_psci_fid(smc_fid)) { uint64_t ret; ret = psci_smc_handler(smc_fid, x1, x2, x3, x4, cookie, handle, flags); ... }ARM64TrustFirmware[五]由讯客互联开源代码栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“ARM64TrustFirmware[五]”