赞
踩
本文文档链接:
目录
6.1 scmi transport,channel,agent的对应关系
scp 代码结构在文档 doc/framework.md 中有描述。
scp 目录结构如下:
├── arch
├── cmake
├── contrib
├── debugger
├── doc
├── docker
├── framework
├── module
├── product
└── tools
doc:项目文档
framework:存放scp架构的定义和实现,是scp 的代码的主干。
module:存放scp的各个通用功能模块的代码,例如power_domain, smt,scmi,scmi_power_domain,scmi_reset_domain,scmi_sensordeng,scmi_system_power,scmi_voltage_domain等。
product:
a.存放具体的产品代码,每个具体product中目录如下:
root/product/productname/
├── include
├── module
├── firmware_a //eg:mcp_ramfw_fvp
├── firmware_b //eg:mcp_romfw
├── firmware_c //eg:scp_ramfw_fvp
└── src
b. product中module/目录结构和根目录下的一致,但是是该product的私有模块。编译哪些模块由firmware_x/firmware.mk 文件中的BS_FIRMWARE_MODULES变量指定module的文件夹名或由firmware_x/Firmware.cmake文件指定。注:root/product/productname/module也可重新实现root/module中已实现的模块,只需要修改productname/module/下的该文件夹名即可,例root/product/morello/morello_smt/和root/product/smt/。
tools:存放编译脚本等工具
fwk_module_idx.h中包含了所有模块的枚举,定义如下:
enum fwk_module_idx {
FWK_MODULE_IDX_TEST0,
FWK_MODULE_IDX_TEST1,
FWK_MODULE_IDX_TEST2,
FWK_MODULE_IDX_COUNT,
};
static const fwk_id_t fwk_module_id_test0 =
FWK_ID_MODULE_INIT(FWK_MODULE_IDX_TEST0);
static const fwk_id_t fwk_module_id_test1 =
FWK_ID_MODULE_INIT(FWK_MODULE_IDX_TEST1);
static const fwk_id_t fwk_module_id_test2 =
FWK_ID_MODULE_INIT(FWK_MODULE_IDX_TEST2);
注:fwk_id_t = FWK_ID_MODULE(fwk_module_idx),FWK_ID_MODULE为转换id的宏定义
fwk_module_list.c 中包含了所有模块的结构体表及其配置结构体表。定义如下:
const struct fwk_module *module_table[FWK_MODULE_IDX_COUNT] = {
@SCP_MODULE_GEN@
};
const struct fwk_module_config *module_config_table[FWK_MODULE_IDX_COUNT] = {
@SCP_MODULE_CONFIG_GEN@
};
fwk_module_idx.h、fwk_module_list.c为自动生成的文件。其中
Makefile 中BS_FIRMWARE_MODULES变量经过处理变为FIRMWARE_MODULES_LIST,并入传入到gen_module_code.py脚本自动生成文件.
在scp代码中,所有的功能都由一个个模块提供。每个模块以api枚举及其结构体的方式对外提供该模块的功能,,并在模块通用结构体fwk_module中提供.init(模块初始化)、.bind(该模块获取并绑定其依赖模块的api)、process_bind_request(该模块被其他模块依赖的api的获取并绑定请求函数)等通用接口。
模块在初始化时由fwk_module.c 调用回调函数.init,.bind,在该模块bind依赖模块api时,调用fwk_module_bind函数来实现。fwk_module_bind调用依赖模块提供的process_bind_request函数来获取依赖模块的api,并绑定。由此实现a模块bind b模块的api。
一个模块依赖关系例下:
mod_scmi_power_domain.c/scmi_api------>mod_scmi.c/FWK_MODULE_IDX_SCMI,MOD_SCMI_API_IDX_PROTOCOL
mod_scmi_power_domain.c/pd_api-------->mod_power_domain.c/fwk_module_id_power_domain,mod_pd_api_id_restricted
mod_scmi.c/transport_api------------------->mod_xxx.c/ctx->config->transport_id,ctx->config->transport_api_id //eg:mod_smt
mod_smt.c/driver_api------------------------>mod_xxx.c/channel_ctx->config->driver_id,channel_ctx->config->driver_api_id
其中mod_scmi_power_domain处理scmi 中power_domain protocol消息,并通过pd_api调用mod_power_domain来执行该protocol;通过调用scmi_api调用mod_scmi.c来回复该protocol;
mod_scmi.c通过transport_api调用mod_smt中的transport相关功能来完成scmi协议的transport层(scmi 数据收发及解析);
mod_smt.c/driver_api调用scmi更下一级的channel来产生中断(scmi 消息通知中断产生和处理)。
注:smt: Shared Memory Transport
scp 入口及应用函数为:fwk_arch_init,包含如下内容:
fwk_module_init(); //scp 框架初始化;
status = fwk_io_init();
status = fwk_log_init();
/* Initialize interrupt management */
status = fwk_arch_interrupt_init(driver->interrupt); //中断初始化
status = fwk_module_start(); //模块初始化,开始任务
其中,fwk_module_init完成module_table、module_config_table所有模块信息的初始化
fwk_module_start完成所有模块的初始化工作:
scp firmware的加载及boot 由arm Arm Trusted Firmware-A (TF-A)来完成。TF(Trusted Firmware)是ARM在Armv8引入的安全解决方案,为安全提供了整体解决方案。它包括启动和运行过程中的特权级划分,对Armv7中的TrustZone(TZ)进行了提高,补充了启动过程信任链的传导,细化了运行过程的特权级区间。TF实际有两种Profile,对ARM Profile A的CPU应用TF-A,对ARM Profile M的CPU应用TF-M。我们一般接触的都是TF-A,又因为这个概念是ARM提出的,有时候也缩写做ATF(ARM Trusted Firmware),所以本文对ATF和TF-A不再做特殊说明,ATF也是TF-A。
ATF启动流程分为BL1、BL2、BL31、BL32、BL33。详细如下图所示:
其中,scp firmware 在BL2中加载。
对于一个scp 代码中product的firmware,有romfw,和ramfw。其中romfw主要是用于加载ramfw,并跳转执行。ramfw为scp主要应用代码,提升scp各种服务(例scmi)。
scp_bl1用于加载和执行scp_bl2。
以juno和morello为例,其目录结构如下:
product/juno/
├── include
├── module
├── scp_ramfw
├── scp_romfw
│ ├── config_bootloader.c
│ ├── config_clock.c
│ ├── config_juno_ppu.c
│ ├── config_juno_rom.c
│ ├── config_juno_soc_clock.c
│ ├── config_pl011.c
│ ├── config_sds.c
│ ├── config_timer.c
| └──...
├── scp_romfw_bypass
└── src
product/morello/
├── include
├── mcp_ramfw_fvp
├── mcp_romfw
├── module
├── scp_ramfw_fvp
├── scp_romfw
│ ├── config_clock.c
│ ├── config_morello_rom.c
│ ├── config_pl011.c
│ ├── Firmware.cmake
│ ├── firmware.mk
│ ├── fmw_cmsis.h
│ ├── fmw_memory.h
│ └──...
└── src
从目录结构中可以看出,其romfw的应用config较少,其主要目的服务于加载运行ramfw。
scp boot 流程如下:
在morello中,ATF该平台代码没有BL2实现,故ramfw直接从flash拷贝scp_ramfw的代码到指定位置.
在juno中,ATF的BL2中通过SDS(Shared-Data-Structure,在Juno平台中替代之前Boot-Over_MHU (BOM)协议)和SCP的romfw通信(mod_bootloader调用sds API),发送ramfw到安全内存,然后再由romfw从安全内存加载到其他位置并执行。
ATF中,BL2的编译选项在如下位置定义plat/arm/board/juno/platform.mk中的BL2_SOURCES,morello ATF plat/arm/board/morello/platform.mk中未定义BL2_SOURCES,不包含BL2.是否包含BL2相关通用代码及配置由CSS_LOAD_SCP_IMAGES决定
老的Boot-Over_MHU (BOM)传输流程图如下:(SDS的启动传输流程类似,可参考下图)
例mhu中的中断处理函数mhu_isr。在该函数中通过中断源查表获取对应的设备和smt channel。然后调用smt模块的api(mod_smt_driver_input_api的signal_message)发送消息。
scmi到smt底层channel 绑定关系如下(以product juno为例):
1. config_scmi.c 中配置scmi的元素表,指定每个元素的transport_id,transport_api_id,scmi_agent_id,完成scmi模块中对transport的依赖配置
static const struct fwk_element element_table[] = {
[JUNO_SCMI_SERVICE_IDX_PSCI_A2P] = {
.name = "PSCI",
.data = &(struct mod_scmi_service_config) {
.transport_id = FWK_ID_ELEMENT_INIT(
FWK_MODULE_IDX_SMT,
JUNO_SCMI_SERVICE_IDX_PSCI_A2P),
.transport_api_id = FWK_ID_API_INIT(
FWK_MODULE_IDX_SMT,
MOD_SMT_API_IDX_SCMI_TRANSPORT),
.transport_notification_init_id =
FWK_ID_NOTIFICATION_INIT(FWK_MODULE_IDX_SMT,
MOD_SMT_NOTIFICATION_IDX_INITIALIZED),
.scmi_agent_id = (unsigned int) JUNO_SCMI_AGENT_IDX_PSCI,
.scmi_p2a_id = FWK_ID_NONE_INIT,
},
},
....
}
2. config_smt.c 中配置smt的元素表,每个元素为一个smt channel。指定其type(master/slave),policies,mailbox_address,mailbox_size,driver_id,driver_api_id,pd_source_id。完成smt自身和对mhu依赖的配置
static const struct fwk_element element_table[] = {
[JUNO_SCMI_SERVICE_IDX_PSCI_A2P] = {
.name = "",
.data = &(struct mod_smt_channel_config) {
.type = MOD_SMT_CHANNEL_TYPE_SLAVE,
.policies = (MOD_SMT_POLICY_INIT_MAILBOX | MOD_SMT_POLICY_SECURE),
.mailbox_address = (uintptr_t)SCMI_PAYLOAD_S_A2P_BASE,
.mailbox_size = SCMI_PAYLOAD_SIZE,
.driver_id = FWK_ID_SUB_ELEMENT_INIT(
FWK_MODULE_IDX_MHU,
JUNO_MHU_DEVICE_IDX_S,
0),
.driver_api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_MHU, 0),
.pd_source_id = FWK_ID_ELEMENT_INIT(
FWK_MODULE_IDX_POWER_DOMAIN,
POWER_DOMAIN_IDX_SYSTOP),
}
},
....
}
3. 在round == 0的scmi_power_domain模块的.bind回调函数中。调用fwk_module_bind(FWK_ID_MODULE(FWK_MODULE_IDX_SCMI),FWK_ID_API(FWK_MODULE_IDX_SCMI, MOD_SCMI_API_IDX_PROTOCOL),&scmi_pd_ctx.scmi_api)绑定scmi api;调用fwk_module_bind(fwk_module_id_power_domain, mod_pd_api_id_restricted,&scmi_pd_ctx.pd_api) 绑定power domain 的api。
scmi 的.process_bind_request回调函数中,会对scmi的protocol_table的id号进行维护,并将全局的scmi_ctx.protocol_count++。scmi_ctx.protocol_table[PROTOCOL_TABLE_RESERVED_ENTRIES_COUNT + scmi_ctx.protocol_count++].id = source_id;
4. 在round == 0的scmi模块的.bind回调函数中。对每个scmi模块config_scmi.c中配置的elemen(对应一个smt的transport)t执行.bind函数,进而调用fwk_module_bind(ctx->config->transport_id,ctx->config->transport_api_id, &transport_api)绑定api;scmi模块的.bind回调函数中也会对每个在上一步注册的protocol_table的id号依次进行protocol_api的获取。(protocol->message_handler = protocol_api->message_handler;)。
fwk_module_bind将调用smt的回调函数.process_bind_request。smt的.process_bind_request回调函数中配置smt 的channel_ctx->service_id为scmi模块module_idx+element_idx(用于标识scmi模块+scmi内的element)。(在下一轮执行smt的.bind回调函数时,会判断该id是否是scmi 模块,并赋值给channel_ctx->is_scmi_channel = true)。
5. 在round == 0的smt模块的.bind回调函数中。该函数对每个smt channel调用fwk_module_bind(channel_ctx->config->driver_id,channel_ctx->config->driver_api_id, &channel_ctx->driver_api)绑定mhu的api。
fwk_module_bind将调用mhu的回调函数.process_bind_request。在该函数中配置device_ctx->smt_channel_table[slot].id 为smt模块id。
6. 在round == 1的mhu模块的.bind回调函数中。该函数通过fwk_module_bind(smt_channel->id,FWK_ID_API(FWK_MODULE_IDX_SMT, MOD_SMT_API_IDX_DRIVER_INPUT),&smt_channel->api)获取smt 模块的消息处理api,以便在中断中使用channel_ctx->smt_signal.scmi_api->signal_message处理scmi消息。
fwk_notification_subscribe
订阅指定模块指定通知
fwk_notification_unsubscribe
取消订阅通知
fwk_notification_notify
向订阅该通知的模块发送通知
fwk_notification_notify函数调用send_notifications发送通知
send_notifications函数为每个订阅该通知的模块生成通知event,放入scp 全局event_queue/isr_event_queue(isr_event_queue最终会在scp 任务处理中放入到event_queue中)
scp 任务处理循环中调用event_queue目标模块的process_notification回调函数
由编译宏BUILD_HAS_SCMI_NOTIFICATIONS打开,各个scmi protocol 模块发送通知是通过调用scmi 的api scmi_notification_notify完成发送的。
scmi 通知,可通过notification机制到对应scmi protocol的process_notification函数发送通知。或者该scmi protocol有scmi 消息的set/change 等操作时自行发送通知
例:
1.module_reset_domain(.process_event)------>module_scmi_reset_domain(.process_notification)
注:module_reset_domain process_event的事件产生路径如下例:module_scmi_reset_domain(scmi_reset_mod_scmi_to_protocol_api的message_handler)---->mod_reset_domain(driver_api->set_reset_state)---->module_juno_reset_domain(juno_reset_domain_drv_api的set_reset_state产生事件)
2.module_scmi_power_domain 的debug 模式(BUILD_HAS_MOD_DEBUG),或scmi_pd_mod_scmi_to_protocol_api的message_handler
1.一个scp可以有多个agent,agent是运行在操作系统,安全固件的软件或者一个使用scmi协议的设备。例如juno有如下代理,0保留给平台。
enum juno_scmi_agent_idx {
/* 0 is reserved for the platform */
JUNO_SCMI_AGENT_IDX_OSPM = 1,
JUNO_SCMI_AGENT_IDX_PSCI,
JUNO_SCMI_AGENT_IDX_COUNT,
};
2.transport定义了scmi协议如何传输。比如shared memory。一个agent可以有多个A2P或P2A channel,channel是双向的,但是协议发起者(主)-接收者(从)关系是固定的。故若要使能通知功能,除了一个A2P channel外,还需要一个P2A channel分配给这个agent.
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。