一套軟硬件跑起來的樣子就像上面圖里面的一樣,it works。對(duì)應(yīng)我們的SCP固件中,有那些框架來支撐這個(gè)系統(tǒng)運(yùn)行起來,這里就需要一套基于M核或者單片機(jī)的通用框架程序,市面上的這種系統(tǒng)并不少見,例如freeRTOS等。
為了強(qiáng)調(diào)安全、簡(jiǎn)單等特性,適配ARM的控制系統(tǒng)固件,ARM又搞了這套通用的框架,適合在M核或者R核上工作,甚至A核的某些特權(quán)系統(tǒng)例如OPTEE中。安全的核心就是隔離,隔離就是按功能形成module或者domain,模塊之間禁止無權(quán)限的訪問。
1. module介紹
SCP的每個(gè)功能都實(shí)現(xiàn)為一個(gè)單獨(dú)的module,module間耦合性盡量低,確保安全特性,通常固件所需的整體功能應(yīng)來自模塊之間的交互。module間隔離就像上圖中的狗咬架,一旦伸手產(chǎn)生交互就禍福不能預(yù)測(cè)了,所以加上欄桿,規(guī)定好那些module間可以交互伸手,這都是通過API函數(shù)實(shí)現(xiàn)的,在系統(tǒng)初始化的時(shí)候設(shè)定死,下面模塊間綁定章節(jié)會(huì)講到。
SCP中的module分為兩部分:在代碼根目錄module文件夾下,共77個(gè)公共模塊,另外每個(gè)產(chǎn)品下面還有module,小100個(gè)可真不少。
一個(gè)固件只包含一部分module,在Firmware.cmake中定義,gen_module_code.py腳本生成源碼
這些module在framework啟動(dòng)時(shí)候初始化啟動(dòng)運(yùn)行。
公共的module比較有通用性,產(chǎn)品自己的module一般是驅(qū)動(dòng)需要進(jìn)行定制
模塊類型及軟件協(xié)議棧:
這個(gè)協(xié)議棧就是SCP軟件跟外界交互的流程,一般消息都是通過驅(qū)動(dòng)-》HAL層上來,然后處理的過程就是服務(wù)-》協(xié)議-》HAL-》驅(qū)動(dòng)再操作硬件做出反應(yīng),這次交互就算結(jié)束了。具體如下:
2.framework框架流程
framework框架負(fù)責(zé)固件的通用流程實(shí)現(xiàn),包括系統(tǒng)初始化,module初始化,中斷服務(wù)提供,event服務(wù)提供等。這樣module就可以專注于自己功能和對(duì)外交互api的實(shí)現(xiàn)。SCP framework初始化流程圖如下:
備注:這里的framework框架流程適用于scp_romfw和scp_ramfw,兩者區(qū)別只是包含module不同,定義包含了那些module在其目錄下的Firmware.cmake文件中。
編譯過程中gen_module_code.py腳本會(huì)生成module信息和配置信息的代碼,過程如下:SCP/MCP 軟件構(gòu)建系統(tǒng)由一個(gè)頂級(jí) Makefile :Makefile.cmake和一組 .mk 文件組成,例如juno產(chǎn)品product/juno/product.mk
BS_PRODUCT_NAME := juno BS_FIRMWARE_LIST := scp_romfw scp_romfw_bypass scp_ramfw |
模塊可以在項(xiàng)目根目錄的 modules/ 目錄下實(shí)現(xiàn),也可以是產(chǎn)品特定的并在product/
gen_module_code.py腳本會(huì)根據(jù)
product/juno/scp_romfw/Firmware.cmake中SCP_MODULES變量
list(APPEND SCP_MODULES "juno-ppu") list(APPEND SCP_MODULES "juno-rom") list(APPEND SCP_MODULES "juno-soc-clock") list(APPEND SCP_MODULES "clock") list(APPEND SCP_MODULES "gtimer") list(APPEND SCP_MODULES "sds") list(APPEND SCP_MODULES "bootloader") |
生成
?fwk_module_idx.h:包含構(gòu)成固件的模塊索引的枚舉。fwk_module_idx.h中枚舉中模塊索引的順序保證遵循固件firmware.mk 文件中 BS_FIRMWARE_MODULES列表中模塊名稱的順序。當(dāng)執(zhí)行涉及迭代固件中存在的所有模塊的操作時(shí),框架在運(yùn)行時(shí)使用相同的順序,例如 fwk_module.c 中的 init_modules() 函數(shù)。
enum fwk_module_idx { FWK_MODULE_IDX_JUNO_PPU = 0, FWK_MODULE_IDX_JUNO_ROM = 1, FWK_MODULE_IDX_JUNO_SOC_CLOCK = 2, FWK_MODULE_IDX_CLOCK = 3, FWK_MODULE_IDX_GTIMER = 4, FWK_MODULE_IDX_SDS = 5, FWK_MODULE_IDX_BOOTLOADER = 6, FWK_MODULE_IDX_COUNT = 7, }; |
?fwk_module_list.c:包含一個(gè)指向模塊描述符的指針表,每個(gè)模塊對(duì)應(yīng)一個(gè)作為固件一部分構(gòu)建的模塊。該文件及其內(nèi)容由框架內(nèi)部使用,通常不應(yīng)由其他單元(如模塊)使用。
const struct fwk_module *module_table[FWK_MODULE_IDX_COUNT] = { &module_juno_ppu, &module_juno_rom, &module_juno_soc_clock, &module_clock, &module_gtimer, &module_sds, &module_bootloader, }; const struct fwk_module_config *module_config_table[FWK_MODULE_IDX_COUNT] = { &config_juno_ppu, &config_juno_rom, &config_juno_soc_clock, &config_clock, &config_gtimer, &config_sds, &config_bootloader, }; |
module_table和module_config_table用于模塊初始化。
固件的Firmware.cmake文件中可以對(duì)配置開關(guān)進(jìn)行聲明,例如:
set(SCP_FIRMWARE_SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}") set(SCP_GENERATE_FLAT_BINARY TRUE) set(SCP_ARCHITECTURE "arm-m") |
framework.md-固件相關(guān)配置文件說明
產(chǎn)品始終包含定義一個(gè)或多個(gè)固件目標(biāo)的product.mk文件。
在一個(gè)產(chǎn)品中,總會(huì)有至少一個(gè)固件。
對(duì)于每個(gè)固件,必須在fmw_memory.h文件中提供鏈接器信息,例如product/juno/scp_romfw/fmw_memory.h中:
#define FMW_MEM_MODE ARCH_MEM_MODE_DUAL_REGION_RELOCATION /* ROM */ #define FMW_MEM0_SIZE SCP_ROM_SIZE #define FMW_MEM0_BASE SCP_ROM_BASE /* RAM */ #define FMW_MEM1_SIZE (16 * 1024) #define FMW_MEM1_BASE (SCP_RAM_BASE + SCP_RAM_SIZE - FMW_MEM1_SIZE) |
如果使用雙區(qū)域內(nèi)存配置,則還必須定義FMW_MEM1_BASE和 FMW_MEM1_SIZE 。
Toolchain-*.cmake 中定義image的體系結(jié)構(gòu)目標(biāo)
product/juno/scp_romfw/Toolchain-GNU.cmake
set(CMAKE_SYSTEM_PROCESSOR "cortex-m3") set(CMAKE_TOOLCHAIN_PREFIX "arm-none-eabi-") ... |
product/juno/scp_romfw/CMakeLists.txt 定義編譯范圍和目標(biāo),cmake會(huì)使用生成makefile文件。
編譯選項(xiàng)說明:
cmake_readme.md-構(gòu)建配置選項(xiàng):
?SCP_ENABLE_NOTIFICATIONS:?jiǎn)⒂?禁用 SCP 固件中的通知。
?SCP_ENABLE_SCMI_NOTIFICATIONS:?jiǎn)⒂?禁用 SCMI 通知。
?SCP_ENABLE_RESOURCE_PERMISSIONS:?jiǎn)⒂?禁用資源權(quán)限設(shè)置。
單獨(dú)配置編譯:
配置生效命令:
cmake -B /tmp/build -DSCP_FIRMWARE_SOURCE_DIR:PATH=juno/scp_romfw -DSCP_ENABLE_DEBUG_UNIT=TRUE |
然后就是編譯命令:
cmake --build /tmp/build |
在編譯文件中配置,例如在product/juno/scp_romfw/Firmware.cmake中
set(SCP_ENABLE_NOTIFICATIONS TRUE) |
修改后需要clean下,再繼續(xù)編譯。
2.1 平臺(tái)初始化
arch/arm/arm-m/CMakeLists.txt中,arch-arm-m庫的入口是arch_exception_reset()函數(shù):
if(CMAKE_C_COMPILER_ID STREQUAL "ARMClang") target_link_options(arch-arm-mPUBLIC "LINKER:--entry=arch_exception_reset")endif()
arch_exception_reset()函數(shù)在arch/arm/arm-m/src/arch.ld.S鏈接文件中也被定位了入口函數(shù)
其實(shí)現(xiàn)在arch/arm/arm-m/src/arch_handlers.c:
noreturn void arch_exception_reset(void) { extern noreturn void __main(void); __main(); }
__main在c運(yùn)行時(shí)調(diào)用main函數(shù),對(duì)于M核實(shí)現(xiàn)來說,arch/arm/arm-m/src/arch_main.c中有main()函數(shù)
int main(void) { ////初始化 ARM Cortex-M 系列芯片的 Configuration Control Register (CCR)。 //其中,通過設(shè)置 SCB_CCR_DIV_0_TRP_Msk 來啟用除以零的異常處理 arch_init_ccr(); //scp 入口及應(yīng)用函數(shù) return fwk_arch_init(&arch_init_driver); } |
scp 入口及應(yīng)用為fwk_arch_init函數(shù),在framework/src/fwk_arch.c中
int fwk_arch_init(const struct fwk_arch_init_driver *driver) { //scp 框架初始化,完成module_table、module_config_table所有模塊信息的初始化 //scp/module目錄下的模塊的初始化 fwk_module_init(); //這里構(gòu)建了一個(gè)全局的fwk_io_stdin、 fwk_io_stdout, 在后面的終端輸出有用 status = fwk_io_init(); //初始化日志輸出方式 status = fwk_log_init(); //中斷gic初始化 status = fwk_arch_interrupt_init(driver->interrupt); //所有模塊初始化,開始任務(wù) status = fwk_module_start(); //循環(huán)等待處理隊(duì)列事件 __fwk_run_main_loop(); } |
2.2 module初始化
fwk_module_init函數(shù),在framework/src/fwk_module.c中實(shí)現(xiàn)
在系統(tǒng)構(gòu)建章節(jié)中module_table和module_config_table是由配置文件Firmware.cmake生成的fwk_module_list.c中定義。
module見module介紹章節(jié)
module_config_table就是模塊的上下文信息
void fwk_module_init(void) { for (uint32_t i = 0U; i < ?(uint32_t)FWK_MODULE_IDX_COUNT; i++) { //獲取模塊的上下文信息 struct fwk_module_context *ctx = &fwk_module_ctx.module_ctx_table[i]; fwk_id_t id = FWK_ID_MODULE(i); const struct fwk_module *desc = module_table[i]; const struct fwk_module_config *config = module_config_table[i]; //給模塊上下文信息賦值 *ctx = (struct fwk_module_context){ .id = id, .desc = desc, .config = config, }; //初始化模塊的鏈表 fwk_list_init(&ctx->delayed_response_list); if (config->elements.type == FWK_MODULE_ELEMENTS_TYPE_STATIC) { size_t notification_count = 0; #ifdef BUILD_HAS_NOTIFICATION notification_count = desc->notification_count; #endif fwk_module_init_element_ctxs( ctx, config->elements.table, notification_count); } #ifdef BUILD_HAS_NOTIFICATION if (desc->notification_count > 0) { fwk_module_init_subscriptions( &ctx->subscription_dlist_table, desc->notification_count); } #endif } } |
2.3 中斷初始化
static int fwk_arch_interrupt_init(int (*interrupt_init_handler)( const struct fwk_arch_interrupt_driver **driver)) { const struct fwk_arch_interrupt_driver *driver; status = interrupt_init_handler(&driver); /* Initialize the interrupt management component */ status = fwk_interrupt_init(driver); return FWK_SUCCESS; } |
interrupt_init_handler是入?yún)⒒卣{(diào)函數(shù),對(duì)應(yīng)為arch_init_driver
static const struct fwk_arch_init_driver arch_init_driver = { .interrupt = arch_nvic_init, }; |
在arch_nvic_init中有*driver =&arch_nvic_driver;
static const struct fwk_arch_interrupt_driver arch_nvic_driver = { .global_enable = global_enable, .global_disable = global_disable, .is_enabled = is_enabled, .enable = enable, .disable = disable, .is_pending = is_pending, .set_pending = set_pending, .clear_pending = clear_pending, .set_isr_irq = set_isr_irq, .set_isr_irq_param = set_isr_irq_param, .set_isr_nmi = set_isr_nmi, .set_isr_nmi_param = set_isr_nmi_param, .set_isr_fault = set_isr_fault, .get_current = get_current, .is_interrupt_context = is_interrupt_context, }; |
拿到driver的值后,執(zhí)行fwk_interrupt_init(driver);
int fwk_interrupt_init(const struct fwk_arch_interrupt_driver *driver) { //校驗(yàn)driver fwk_interrupt_driver = driver; initialized = true; return FWK_SUCCESS; } |
fwk_interrupt_driver 全局變量用于中斷處理。
模塊使用中斷時(shí),需要調(diào)用對(duì)外接口在framework/include/fwk_interrupt.h中,
例如開啟中斷fwk_interrupt_enable函數(shù)的實(shí)現(xiàn):
int fwk_interrupt_enable(unsigned int interrupt) { if (!initialized) { return FWK_E_INIT; } return fwk_interrupt_driver->enable(interrupt); } |
2.4 module啟動(dòng)
fwk_module_start()在framework/src/fwk_module.c中定義
int fwk_module_start(void) { //初始化任務(wù)列表 status = __fwk_init(FWK_MODULE_EVENT_COUNT); fwk_module_ctx.stage = MODULE_STAGE_INITIALIZE; //從功能方面初始化所有module fwk_module_init_modules(); fwk_module_ctx.stage = MODULE_STAGE_BIND; //調(diào)用模塊.bind回調(diào)函數(shù)完成所有模塊的綁定。(此處共進(jìn)行兩輪調(diào)用fwk_module_bind_module(round=0 1), //每輪都將分別綁定模塊module和模塊的元素element) for (bind_round = 0; bind_round <= FWK_MODULE_BIND_ROUND_MAX; bind_round++) { status = fwk_module_bind_modules(bind_round); if (status != FWK_SUCCESS) { return status; } } fwk_module_ctx.stage = MODULE_STAGE_START; //啟動(dòng)模塊 status = start_modules(); fwk_module_ctx.initialized = true; return FWK_SUCCESS; } |
fwk_module_init_modules函數(shù)調(diào)用fwk_module_init_module對(duì)每個(gè)模塊進(jìn)行功能初始化
//初始化模塊元素上下文(element_ctxs), //調(diào)用模塊的config->elements.generator,獲取element信息,加入模塊上下文表 elements = config->elements.generator(ctx->id); fwk_module_init_element_ctxs(ctx, elements, notification_count); //調(diào)用模塊的init函數(shù),傳入element_count,config->dat status = desc->init(ctx->id, ctx->element_count, config->data); //初始化模塊元素(element),調(diào)用模塊回調(diào)函數(shù).element_init將模塊element->data配置信息導(dǎo)入到模塊內(nèi)部 fwk_module_init_elements(ctx); |
start_modules函數(shù)調(diào)用fwk_module_start_module對(duì)每個(gè)模塊進(jìn)行啟動(dòng)
module = fwk_mod_ctx->desc; //調(diào)用模塊.start回調(diào)函數(shù) module->start(fwk_mod_ctx->id); |
例如在juno_rom的.start回調(diào)函數(shù)函數(shù)中,通過event和notification機(jī)制,到達(dá)juno_rom模塊的相應(yīng)回調(diào)函數(shù),在juno_rom中,通過ctx.bootloader_api->load_image()調(diào)用mod_bootloader的api,從安全內(nèi)存拷貝到指定位置,在該bootloader模塊api中加載跳轉(zhuǎn)scp_ramfw。(注mod_bootloader_boot為匯編實(shí)現(xiàn),依賴arm指令)。在product/juno/module/juno_rom/src/mod_juno_rom.c中:
const struct fwk_module module_juno_rom = { .type = FWK_MODULE_TYPE_SERVICE, .event_count = (unsigned int)MOD_JUNO_ROM_EVENT_COUNT, .notification_count = (unsigned int)MOD_JUNO_ROM_NOTIFICATION_COUNT, .init = juno_rom_init, .bind = juno_rom_bind, .start = juno_rom_start, .process_event = juno_rom_process_event, .process_notification = juno_rom_process_notification, }; |
2.5 運(yùn)行狀態(tài)機(jī)
scp-firmware在完成了所有的初始化操作后,進(jìn)入死循環(huán),處理隊(duì)列里面的事件或者休眠等待事件到來。
noreturn void __fwk_run_main_loop(void) { for (;;) { fwk_process_event_queue(); if (fwk_log_unbuffer() == FWK_SUCCESS) { fwk_arch_suspend(); } } } |
fwk_process_event_queue主要處理三個(gè)重要的鏈表:free_event_queue, event_queue, isr_event_queue所有的操作都是圍繞這三個(gè)隊(duì)列展開。
void fwk_process_event_queue(void) { for (;;) { while (!fwk_list_is_empty(&ctx.event_queue)) { process_next_event(); } if (!process_isr()) { break; } } } |
event_queue中根據(jù)target_id找到對(duì)應(yīng)module,然后調(diào)用module->process_event進(jìn)行處理,詳細(xì)見module中說明。
process_next_event中調(diào)用duplicate_event會(huì)處理free_event_queue隊(duì)列中的事件
process_isr從中斷isr_event_queue隊(duì)列中取到事件,然后加入到event_queue中
3.module對(duì)外接口
在scp代碼中,所有的功能都由一個(gè)個(gè)模塊提供。每個(gè)模塊以api枚舉及其結(jié)構(gòu)體的方式對(duì)外提供該模塊的功能,并在模塊通用結(jié)構(gòu)體fwk_module中提供,例如
module/scmi_power_domain/src/mod_scmi_power_domain.c中,
/* SCMI Power Domain Management Protocol Definition */ const struct fwk_module module_scmi_power_domain = { .api_count = 1, .type = FWK_MODULE_TYPE_PROTOCOL, .init = scmi_pd_init, .bind = scmi_pd_bind, .start = scmi_pd_start, .process_bind_request = scmi_pd_process_bind_request, .event_count = (unsigned int)SCMI_PD_EVENT_IDX_COUNT, .process_event = scmi_pd_process_event, #ifdef BUILD_HAS_MOD_DEBUG .process_notification = scmi_pd_process_notification, #endif }; |
.init(模塊初始化)
.bind(獲取綁定別的模塊的api)
.process_bind_request(被其他模塊依賴的api的獲取并綁定請(qǐng)求函數(shù))等通用接口。
.start模塊啟動(dòng)
.process_event事件處理
.process_notification通知處理
初始化模塊:
模塊在初始化時(shí)由fwk_module.c 中fwk_module_start函數(shù),調(diào)用回調(diào)函數(shù).init,.bind,.start
?模塊初始化:調(diào)用模塊API的init()函數(shù)指針
?元素初始化:調(diào)用框架模塊API的element_init()函數(shù)指針
?后初始化:元素初始化后,模塊交互之前的一些可選處理操作
?綁定:模塊必須綁定好才能調(diào)用對(duì)方的api
?開始
運(yùn)行時(shí):
一旦運(yùn)行前階段成功完成,固件將開始處理模塊或中斷引發(fā)的事件。默認(rèn)情況下,固件將永遠(yuǎn)循環(huán)等待新事件在運(yùn)行前階段結(jié)束時(shí)處理,但當(dāng)事件列表為空時(shí),可以在處理未決事件后返回。
模塊配置:
模塊初始化的時(shí)候,模塊配置被讀入存放到模塊上下文中:
const struct fwk_module_config *config = module_config_table[i]; //給模塊上下文信息賦值 *ctx = (struct fwk_module_context){ .id = id, .desc = desc, .config = config, }; |
在module_config_table在fwk_module_list.c中定義,這里以config_juno_ppu為例:
const struct fwk_module_config *module_config_table[FWK_MODULE_IDX_COUNT] = { &config_juno_ppu, struct fwk_module_config config_juno_ppu = { .data = &(struct mod_juno_ppu_config){ .timer_alarm_id = FWK_ID_SUB_ELEMENT_INIT( FWK_MODULE_IDX_TIMER, 0, JUNO_PPU_ALARM_IDX), }, .elements = FWK_MODULE_DYNAMIC_ELEMENTS(get_element_table), }; #define FWK_MODULE_DYNAMIC_ELEMENTS(GENERATOR) { .type = FWK_MODULE_ELEMENTS_TYPE_DYNAMIC, .generator = (GENERATOR), } |
如果類型為FWK_MODULE_ELEMENTS_TYPE_STATIC ,框架使用表指針中給出的靜態(tài)表來訪問產(chǎn)品為模塊提供的元素表。
如果類型為 FWK_MODULE_ELEMENTS_TYPE_DYNAMIC ,則框架使用生成器函數(shù)指針。
get_element_table對(duì)應(yīng)一個(gè)配置結(jié)構(gòu)體數(shù)組:
static struct fwk_element element_table[] = { [JUNO_PPU_DEV_IDX_BIG_SSTOP] = { .name = "", .data = &(const struct mod_juno_ppu_element_config) { .reg_base = PPU_BIG_SSTOP_BASE, .timer_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_TIMER, 0), .pd_type = MOD_PD_TYPE_CLUSTER, }, }, .... enum juno_ppu_idx { JUNO_PPU_DEV_IDX_BIG_CPU0, JUNO_PPU_DEV_IDX_BIG_CPU1, JUNO_PPU_DEV_IDX_BIG_SSTOP, JUNO_PPU_DEV_IDX_LITTLE_CPU0, JUNO_PPU_DEV_IDX_LITTLE_CPU1, JUNO_PPU_DEV_IDX_LITTLE_CPU2, JUNO_PPU_DEV_IDX_LITTLE_CPU3, JUNO_PPU_DEV_IDX_LITTLE_SSTOP, JUNO_PPU_DEV_IDX_GPUTOP, JUNO_PPU_DEV_IDX_SYSTOP, JUNO_PPU_DEV_IDX_DBGSYS, JUNO_PPU_DEV_IDX_COUNT, }; |
struct fwk_element結(jié)構(gòu)體表示元素,里面有名字,子元素個(gè)數(shù)和數(shù)據(jù)
元素:
元素表示由模塊擁有或管理的資源。每個(gè)元素將表示模塊與之交互和/或負(fù)責(zé)的對(duì)象。
例如,驅(qū)動(dòng)程序類型的模塊可能具有表示它所控制的硬件設(shè)備的元素。因?yàn)樵嘏渲脭?shù)據(jù)靈活多變,使用通用的方式const void *data實(shí)現(xiàn)。
子元素表示由元素?fù)碛谢蚬芾淼馁Y源。子元素僅由它們的索引和/或標(biāo)識(shí)符表示。
索引和標(biāo)識(shí)符:
由于框架設(shè)計(jì)為模塊化,因此需要一種標(biāo)準(zhǔn)化方法來識(shí)別和引用模塊、元素、子元素、事件、通知和 API。該框架為此定義了兩個(gè)組件:indices和identifiers。
indices:
模塊索引由構(gòu)建系統(tǒng)為每個(gè)固件生成,并放在fwk_module_idx.h頭文件中。
enum fwk_module_idx { FWK_MODULE_IDX_JUNO_PPU = 0, FWK_MODULE_IDX_JUNO_ROM = 1, ...... |
identifiers:
標(biāo)識(shí)符有一個(gè)類型,這決定了標(biāo)識(shí)符中包含的信息。在內(nèi)部,標(biāo)識(shí)符始終包含模塊的索引,并且可能包含在該模塊的上下文中標(biāo)識(shí)項(xiàng)目的附加索引。也在fwk_module_idx.h頭文件中,有宏和變量?jī)刹糠侄x,值是一樣的:
#define FWK_MODULE_ID_JUNO_PPU FWK_ID_MODULE(FWK_MODULE_IDX_JUNO_PPU) #define FWK_ID_MODULE(MODULE_IDX) ((fwk_id_t)FWK_ID_MODULE_INIT(MODULE_IDX)) #define FWK_ID_MODULE_INIT(MODULE_IDX) { .common = { .type = (uint32_t)__FWK_ID_TYPE_MODULE, .module_idx = (uint32_t)MODULE_IDX, }, } static const fwk_id_t fwk_module_id_juno_ppu = FWK_MODULE_ID_JUNO_PPU_INIT; #define FWK_MODULE_ID_JUNO_PPU_INIT FWK_ID_MODULE_INIT(FWK_MODULE_IDX_JUNO_PPU) |
可用的標(biāo)識(shí)符類型有:
?模塊:僅由模塊索引組成
?元素:由模塊索引和模塊內(nèi)元素的索引組成
?子元素:由模塊索引、模塊內(nèi)元素的索引和該元素?fù)碛械淖釉氐乃饕M成。
?API:由模塊索引和模塊提供的API的索引組成
?事件:由模塊索引和模塊可能產(chǎn)生的事件的索引組成
?通知:由模塊索引和模塊可能生成的通知索引組成。
日志:
日志記錄功能定義并實(shí)現(xiàn)了該組件的公共接口。該接口的文檔可以在 fwk_log.h 中找到。
#include FWK_LOG_ERR("[ROM] ERROR: Failed to turn on LITTLE cluster."); # define FWK_LOG_ERR(...) fwk_log_printf(__VA_ARGS__) |
fwk_log_printf()函數(shù)在framework/src/fwk_log.c中定義。
4. event事件
模塊可以給自己或者別的模塊發(fā)送event事件,事件的參數(shù)是結(jié)構(gòu)化消息structfwk_event。
static int juno_rom_process_event( const struct fwk_event *event, struct fwk_event *resp) { truct fwk_event { struct fwk_slist_node slist_node; fwk_id_t source_id; fwk_id_t target_id; uint32_t cookie; bool is_response; bool response_requested; bool is_notification; bool is_delayed_response; fwk_id_t id; alignas(max_align_t) uint8_t params[FWK_EVENT_PARAMETERS_SIZE]; }; |
該事件包含一個(gè)response_requested 屬性,該屬性指示源實(shí)體是否期望對(duì)其事件的響應(yīng)。為了響應(yīng)這個(gè)事件,接收實(shí)體填寫響應(yīng)參數(shù),框架發(fā)出一個(gè)事件,該事件以發(fā)出原始事件的實(shí)體為目標(biāo)。
事件的is_response屬性用于指示新生成的事件是對(duì)原始事件的響應(yīng)。
例如在juno_rom固件初始化時(shí),初始化juno_rom模塊,product/juno/module/juno_rom/src/mod_juno_rom.c
會(huì)執(zhí)行.start回調(diào)函數(shù)函數(shù)juno_rom_start(),給自己發(fā)了一個(gè)event,如下:
static int juno_rom_start(fwk_id_t id) { struct fwk_event event = { .source_id = fwk_module_id_juno_rom, .target_id = fwk_module_id_juno_rom, .id = mod_juno_rom_event_id_run, }; ..... return fwk_put_event(&event); } #define fwk_put_event(event) _Generic((event), struct fwk_event * : __fwk_put_event, struct fwk_event_light * : __fwk_put_event_light)(event) |
fwk_put_event把event分為兩類,fwk_event_light 是輕量級(jí)的攜帶不攜帶額外數(shù)據(jù)參數(shù)。這里我們用fwk_event 則處理函數(shù)為:
__fwk_put_event --》put_event(event, intr_state, FWK_EVENT_TYPE_STD); --》fwk_list_push_tail(&ctx.event_queue, &allocated_event->slist_node);
固件狀態(tài)機(jī)運(yùn)行的時(shí)候會(huì)循環(huán)執(zhí)行framework/src/fwk_core.c中process_next_event()函數(shù)
static void process_next_event(void) { ctx.current_event = event = FWK_LIST_GET( fwk_list_pop_head(&ctx.event_queue), struct fwk_event, slist_node); module = fwk_module_get_ctx(event->target_id)->desc; process_event = event->is_notification ? module->process_notification : module->process_event; status = process_event(event, &async_response_event); |
這里找到模塊juno_rom,然后取出其event處理函數(shù)process_event并執(zhí)行,實(shí)際執(zhí)行的是juno_rom_process_event(),其發(fā)了一條通知消息如下:
static int juno_rom_process_event( const struct fwk_event *event, struct fwk_event *resp) { .... /* Send SYSTOP ON notification */ systop_on_event = (struct fwk_event){ .response_requested = true, .id = mod_juno_rom_notification_id_systop, .source_id = FWK_ID_NONE }; notification_params = (void *)systop_on_event.params; notification_params->state = (unsigned int)MOD_PD_STATE_ON; //發(fā)notification消息 status = fwk_notification_notify(&systop_on_event, &ctx.notification_count); if (!fwk_expect(status == FWK_SUCCESS)) { return FWK_E_PANIC; } //通過ctx.bootloader_api->load_image()調(diào)用mod_bootloader的api,從安全內(nèi)存拷貝到指定位置, //在該bootloader 模塊api中加載跳轉(zhuǎn)scp_ramfw。 if (ctx.notification_count == 0) { return deferred_setup(); } |
fwk_notification_notify的解釋見notification章節(jié)
5.motificaiont通知
notification涉及到兩個(gè)模塊的通信,跟event的區(qū)別是:
?event是一個(gè)模塊發(fā)給另外一個(gè)模塊或者發(fā)給自己,比較確定
?notification是發(fā)給訂閱了這個(gè)模塊的所有模塊,算廣播,需要先進(jìn)行訂閱
notification接口:
?fwk_notification_subscribe//訂閱指定模塊指定通知
?fwk_notification_unsubscribe//取消訂閱通知
?fwk_notification_notify//向訂閱該通知的模塊發(fā)送通知
在實(shí)現(xiàn)上notification使用event的消息傳遞機(jī)制,只在發(fā)消息和處理消息的時(shí)候做微小改動(dòng)。
例如上面例子中使用fwk_notification_notify()函數(shù)發(fā)送的通知
int fwk_notification_notify(struct fwk_event *notification_event,unsigned int *count){ send_notifications(notification_event, count); |
通知的參數(shù)沿用event的struct fwk_event ,發(fā)送通知的時(shí)候,需要先找到訂閱鏈表,然后進(jìn)行過濾
static void send_notifications(struct fwk_event *notification_event, unsigned int *count) { //根據(jù)id和source_id找到訂閱的鏈表 subscription_dlist = get_subscription_dlist(notification_event->id, notification_event->source_id); notification_event->is_response = false; notification_event->is_notification = true; for (node = fwk_list_head(subscription_dlist); node != NULL; node = fwk_list_next(subscription_dlist, node)) { subscription = FWK_LIST_GET(node, struct __fwk_notification_subscription, dlist_node); //對(duì)比源id如果相同就進(jìn)行發(fā)送 if (!fwk_id_is_equal( subscription->source_id, notification_event->source_id)) { continue; } notification_event->target_id = subscription->target_id; status = __fwk_put_notification(notification_event); if (status == FWK_SUCCESS) { (*count)++; } } } |
get_subscription_dlist函數(shù)中source_id 決定是模塊上下文還是元素上下文
.id = mod_juno_rom_notification_id_systop, .source_id = FWK_ID_NONE }; static const fwk_id_t mod_juno_rom_notification_id_systop = FWK_ID_NOTIFICATION_INIT( FWK_MODULE_IDX_JUNO_ROM, MOD_JUNO_ROM_NOTIFICATION_IDX_SYSTOP); |
拿到subscription_dlist訂閱列表后,就進(jìn)行過濾發(fā)送通知
int __fwk_put_notification(struct fwk_event *event) { event->is_response = false; event->is_notification = true; return put_event(event, UNKNOWN_STATE, FWK_EVENT_TYPE_STD); } |
這里就使用了event進(jìn)行實(shí)現(xiàn)。然后系統(tǒng)狀態(tài)機(jī)在處理event的時(shí)候,
static void process_next_event(void) { ctx.current_event = event = FWK_LIST_GET( fwk_list_pop_head(&ctx.event_queue), struct fwk_event, slist_node); module = fwk_module_get_ctx(event->target_id)->desc; process_event = event->is_notification ? module->process_notification : module->process_event; |
根據(jù)is_notification 就可以知道是notification 了,然后調(diào)用process_notification 進(jìn)行處理
6.模塊綁定
一個(gè)模塊或元素可以綁定到另一個(gè)模塊或模塊內(nèi)的元素。目標(biāo)是相同的 - 獲取指向可在后續(xù)階段使用的 API 的指針。當(dāng)嘗試綁定到模塊內(nèi)的元素(而不是模塊本身)時(shí),主要區(qū)別在于接收和處理綁定請(qǐng)求的模塊能夠根據(jù)目標(biāo)元素更改其行為。例如,可以允許請(qǐng)求綁定的模塊僅綁定到處理請(qǐng)求的模塊內(nèi)的元素子集。
思路:A模塊要與B模塊通信,A模塊的全局變量要拿到B模塊的回調(diào)函數(shù)。
A模塊在初始化的時(shí)候,會(huì)調(diào)用自己的bind函數(shù),
bind--》fwk_module_bind--》B模塊的process_bind_request()函數(shù),從而拿到api
scmi_power_domain模塊調(diào)用scmi模塊的api函數(shù)示例圖
scmi_pd_ctx.scmi_api賦值為scmi模塊的處理函數(shù),在.bind = scmi_pd_bind中,
static int scmi_pd_bind(fwk_id_t id, unsigned int round) { status = 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); |
fwk_module_bind調(diào)用依賴模塊提供的process_bind_request函數(shù)來獲取依賴模塊的api,并綁定。
int fwk_module_bind(fwk_id_t target_id, fwk_id_t api_id, const void *api) { fwk_mod_ctx = fwk_module_get_ctx(target_id); status = fwk_mod_ctx->desc->process_bind_request( fwk_module_ctx.bind_id, target_id, api_id, (const void **)api); |
target_id是FWK_MODULE_IDX_SCMI,對(duì)應(yīng)SCMI模塊,fwk_mod_ctx 是SCMI模塊的上下文
/* SCMI module definition */ const struct fwk_module module_scmi = { .api_count = (unsigned int)MOD_SCMI_API_IDX_COUNT, .event_count = 1, #ifdef BUILD_HAS_NOTIFICATION .notification_count = (unsigned int)MOD_SCMI_NOTIFICATION_IDX_COUNT, #endif .type = FWK_MODULE_TYPE_SERVICE, .init = scmi_init, .element_init = scmi_service_init, .bind = scmi_bind, .start = scmi_start, .process_bind_request = scmi_process_bind_request, .process_event = scmi_process_event, #ifdef BUILD_HAS_NOTIFICATION .process_notification = scmi_process_notification, #endif }; |
scmi_process_bind_request調(diào)用到
static int scmi_process_bind_request(fwk_id_t source_id, fwk_id_t target_id, fwk_id_t api_id, const void **api) { unsigned int api_idx; struct scmi_service_ctx *ctx; enum mod_scmi_api_idx api_id_type; api_idx = fwk_id_get_api_idx(api_id); api_id_type = (enum mod_scmi_api_idx)api_idx; switch (api_id_type) { case MOD_SCMI_API_IDX_PROTOCOL: if (!fwk_id_is_type(target_id, FWK_ID_TYPE_MODULE)) { return FWK_E_SUPPORT; } if (scmi_ctx.protocol_count >= scmi_ctx.config->protocol_count_max) { return FWK_E_NOMEM; } scmi_ctx.protocol_table[PROTOCOL_TABLE_RESERVED_ENTRIES_COUNT + scmi_ctx.protocol_count++].id = source_id; *api = &scmi_from_protocol_api; break; |
到此scmi_power_domain模塊拿到了scmi模塊的處理函數(shù),放入&scmi_pd_ctx.scmi_api
幾個(gè)關(guān)鍵的模塊間api調(diào)用示例:
mod_scmi_power_domain模塊中scmi消息收發(fā):
?scmi模塊綁定各個(gè)scmi協(xié)議模塊的protocol_api,根據(jù)id來找到此模塊api,執(zhí)行該protocol;
?scmi_power_domain模塊綁定scmi模塊的api,通過調(diào)用scmi_api(mod_scmi.c中)來回復(fù)該protocol;
mod_scmi.c/transport_api調(diào)用mod_smt中的transport相關(guān)功能來完成scmi協(xié)議的transport層(scmi 數(shù)據(jù)收發(fā)及解析);
mod_smt.c/driver_api調(diào)用scmi更下一級(jí)的channel來產(chǎn)生中斷(scmi消息通知中斷產(chǎn)生和處理)。
審核編輯:劉清
-
ARM處理器
+關(guān)注
關(guān)注
6文章
360瀏覽量
41604 -
耦合器
+關(guān)注
關(guān)注
8文章
715瀏覽量
59563 -
FreeRTOS
+關(guān)注
關(guān)注
12文章
483瀏覽量
61875 -
狀態(tài)機(jī)
+關(guān)注
關(guān)注
2文章
490瀏覽量
27442 -
MCP
+關(guān)注
關(guān)注
0文章
252瀏覽量
13851
原文標(biāo)題:ARM SCP入門-framework框架代碼分析
文章出處:【微信號(hào):OS與AUTOSAR研究,微信公眾號(hào):OS與AUTOSAR研究】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論