网站的ftp上传地址,郑州做网站公司中,办公空间设计原则,做报纸能经常更新网站本章介绍 ATF 中的 Runtime Service 是如何定义和被调用的。
要了解 SMC#xff0c;必须从 SMC 指令本身开始#xff0c;其指令如下图#xff1a; 指令格式为#xff1a;SMC #imm#xff0c;从官方文档了解到该指令只能在 EL1 以及更高的异常等级上调用#xff…本章介绍 ATF 中的 Runtime Service 是如何定义和被调用的。
要了解 SMC必须从 SMC 指令本身开始其指令如下图 指令格式为SMC #imm从官方文档了解到该指令只能在 EL1 以及更高的异常等级上调用用户空间 EL0 是无法直接调用该指令的。
同时根据 ARM 官方文档关于 SMCCC 的描述如下 可以知道 SMC 可以根据不同的 OENOwning Entity Number区分不同分服务的功能当注册具体了 SMC runtime service 时需要为对应的 service 指定正确的 OEN 范围。
这里比较抽象我们来举个例子就明白了比如我在 Hypervisor 中介绍的关于虚拟机管理器通过 SMC 系统调用去 ATF 通过 PSCI 协议拉起从核其中启动从核的 PSIC 的 function id 为PSCI_CPU_ON_AARCH640xc4000003不考虑后面跟的参数也就是最终调用了
smc #0xc4000003
按照其 32bit 划分
bit[31] 1Fast callbit[32] 0SMC64 call conventionbit[29 : 24] 4Standard service callbit[15 : 0] 3该类型的 call type 下的 function number
我们对 runtime service 进行了如下的定义和分类
ARM Architecture Calls获取 smcc 版本arch features 等CPU Service Calls提供针对该平台的 CPU 实现特定服务的接口SIP Service CallsSystem IP 的驱动OEM Service CallsOEM 服务的接口Standard Secure ServicePSCI 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 作为第一个参数传递给 ATFATF 需要根据 smc_fid 定位到是哪种 service。由于 service 有两种 type 组成每种 type 的 oen 最多有 64 个所以 type 和 oen 的组合会有 128 中可能。为了加快查找 service 类别ATF 在初始化 runtime service 时会维护一个表我们用如下两个服务作为例子
DECLARE_RT_SVC(arm_sip_svc,OEN_SIP_START, 2OEN_SIP_END, 2SMC_TYPE_FAST, 1arm_sip_setup,arm_sip_handler
);start_idx SMC_TYPE_FAST 6 | OEN_SIP_START 66
end_idx SMC_TYPE_FAST 6 | OEN_SIP_END 66DECLARE_RT_SVC(tos_svc,OEN_TOS_START, 50OEN_TOS_END, 63SMC_TYPE_YIELD, 0tos_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_handler64
sync_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, x16bic x0, x0, #(FUNCID_SVE_HINT_MASK FUNCID_SVE_HINT_SHIFT)/* Get the unique owning entity number */ubfx x16, x0, #FUNCID_OEN_SHIFT, #FUNCID_OEN_WIDTHubfx x15, x0, #FUNCID_TYPE_SHIFT, #FUNCID_TYPE_WIDTHorr x16, x16, x15, lsl #FUNCID_OEN_WIDTH/* Load descriptor index from array of indices */adrp x14, rt_svc_descs_indicesadd x14, x14, :lo12:rt_svc_descs_indicesldrb 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_LOG2ldr 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 DEBUGcbz x15, rt_svc_fw_critical_error
#endifblr 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);...
}