中断如何送到不同EL?如何配置?
1. BL31异常向量表
BL31异常向量表根据如下定义实现:
ARMv8-A规定Exception Vector Table大小为2KB,并且是2KB对齐;一共6个Vector Entry,每一个Vector Entry大小为128B。
.macro vector_base label, section_name=.vectors .section \section_name, "ax" .align 11, 0------------------------------------------定义Exception Vector Table,2KB对齐。 \label: .endm .macro vector_entry label, section_name=.vectors-----创建一个Exception Vector Entry,128B对齐,大小也为128B。 .cfi_sections .debug_frame .section \section_name, "ax" .align 7, 0 .type \label, %function .cfi_startproc \label: .endm .macro end_vector_entry label------------------------一个Exception Vector Entry大小为128B,不足部分用0补齐。超出128B,则报错。 .cfi_endproc .fill \label + (32 * 4) - . .endm
vector_base定义了Exception Vector Table基地址,vector_entry/end_vector_entry定义了Exception Vector Entry。
vector_base runtime_exceptions /* --------------------------------------------------------------------- * Current EL with SP_EL0 : 0x0 - 0x200 * --------------------------------------------------------------------- */ vector_entry sync_exception_sp_el0 b report_unhandled_exception end_vector_entry sync_exception_sp_el0 vector_entry irq_sp_el0 b report_unhandled_interrupt end_vector_entry irq_sp_el0 vector_entry fiq_sp_el0 b report_unhandled_interrupt end_vector_entry fiq_sp_el0 vector_entry serror_sp_el0 no_ret plat_handle_el3_ea--------------------------------------调用report_unhandled_exception。 end_vector_entry serror_sp_el0 /* --------------------------------------------------------------------- * Current EL with SP_ELx: 0x200 - 0x400 * --------------------------------------------------------------------- */ vector_entry sync_exception_sp_elx b report_unhandled_exception end_vector_entry sync_exception_sp_elx vector_entry irq_sp_elx b report_unhandled_interrupt end_vector_entry irq_sp_elx vector_entry fiq_sp_elx b report_unhandled_interrupt end_vector_entry fiq_sp_elx vector_entry serror_sp_elx no_ret plat_handle_el3_ea--------------------------------------调用report_unhandled_exception。 end_vector_entry serror_sp_elx /* --------------------------------------------------------------------- * Lower EL using AArch64 : 0x400 - 0x600 * --------------------------------------------------------------------- */ vector_entry sync_exception_aarch64 apply_at_speculative_wa check_and_unmask_ea handle_sync_exception---------------------------------------------处理SMC指令或者External Aborts。 end_vector_entry sync_exception_aarch64 vector_entry irq_aarch64 apply_at_speculative_wa check_and_unmask_ea handle_interrupt_exception irq_aarch64 end_vector_entry irq_aarch64 vector_entry fiq_aarch64 apply_at_speculative_wa check_and_unmask_ea handle_interrupt_exception fiq_aarch64 end_vector_entry fiq_aarch64 vector_entry serror_aarch64 apply_at_speculative_wa msr daifclr, #DAIF_ABT_BIT b enter_lower_el_async_ea end_vector_entry serror_aarch64 /* --------------------------------------------------------------------- * Lower EL using AArch32 : 0x600 - 0x800 * --------------------------------------------------------------------- */ ...
在bl2_entrypoint中将early_exceptions异常向量表设置到vbar_el1异常向量表中。
在tsp_entrypoint中将tsp_exceptions异常向量表设置到vbar_el1异常向量表中,替换early_exception。同时将tsp_vector_table地址返回给BL31。
func bl2_entrypoint ... /* --------------------------------------------- * Set the exception vector to something sane. * --------------------------------------------- */ adr x0, early_exceptions msr vbar_el1, x0 isb ... endfunc bl2_entrypoint func tsp_entrypoint _align=3 ... /* --------------------------------------------- * Set the exception vector to something sane. * --------------------------------------------- */ adr x0, tsp_exceptions msr vbar_el1, x0 isb ... /* --------------------------------------------- * Jump to main function. * --------------------------------------------- */ bl tsp_main----------------------------------------------返回值为tsp_vector_table的地址。 /* --------------------------------------------- * Tell TSPD that we are done initialising * --------------------------------------------- */ mov x1, x0-----------------------------------------------x1为tsp_vector_table地址。 mov x0, #TSP_ENTRY_DONE----------------------------------x0为TSP_ENTRY_DONE,即SMC第一个参数SMC FID。 smc #0---------------------------------------------------执行smc从S.EL1进入EL3。 tsp_entrypoint_panic: b tsp_entrypoint_panic endfunc tsp_entrypoint
2. 异常处理框架
参考文档:《Exception Handling Framework》。
本文主要介绍除SMC之外的指向EL3的由BL31处理的异常。本文主要覆盖如下指向EL3的异常:
- 中断
- 同步外部异常
- 异步外部异常
当EL3_EXCEPTION_HANDLING置为1时,EL3将会处理相关异常。
4.1 Introduction
通过配置SCR_EL3寄存器,ARM架构允许异步异常被路由到EL3。
基于选择的中断路由模型,TF-A适当的配置SCR_EL3的FIQ和IRQ来决定次路由。
除非为了实现安全和非安全世界切换,大多数情况下,路由到EL3的FIQ和IRQ不需要在EL3中处理。
4.2 The role of Exception Handling Framework
一个实例:runtime_exceptions(BL31) -> irq_aarch64/fiq_aarch64(BL31) -> handle_interrupt_exception(BL31) -> tspd_sel1_interrupt_handler/tspd_ns_interrupt_handler(BL31) -> tsp_sel1_intr_entry(BL32)。
3. 中断管理框架
参考文档:《Interrupt Management Framework》。
FEL:First Exception Level capable of handling interrupts。第一个能够处理中断的EL。
TEL3:Target Exception Level 3
CSS:Current Security State
此框架负责管理路由到EL3的中断,允许EL3软件配置中断路由行为。他主要目标是实现如下两个需求:
- 当CPU处于非安全状态时,负责将由安全软件处理的中断路由到EL3。
- 当CPU处于EL3以下安全状态时,负责将由非安全软件处理的中断路由到安全世界最近执行EL。
5.1 Concepts
5.1.1 Interrupt types
此框架根据中断在哪个EL中进行处理划分为如下几种:
- S.EL1中断。
- NS中断。
- EL3中断。
5.1.2 Routing model
一个中断可以作为IRQ或者FIQ,中断的目标EL可以通过SCR_EL3.IRQ和SCR_EL3.FIQ进行配置。
SCR_EL3.FIQ=1,FIQ被路由到EL3。
SCR_EL3.IRQ=1,IRQ被路由到EL3。
5.1.3 Valid routing models
CSS:0表示安全状态,1表示非安全状态。
TEL3:0表示目标位FEL,1表示目标为EL3。
Secure-EL1 interrupts |
Non-secure interrupts |
EL3 interrupts |
|
CSS=0 TEL3=0 |
执行于安全状态时,路由到FEL valid:安全软件处理安全中断 |
执行于安全状态时,路由到FEL valid:安全软件控制自身执行如何被非安全中断抢占。安全软件捕获非安全中断,对中断记账并将其通过EL3转交给非安全软件。 |
执行于S.EL1/S.EL0时,路由到FEL valid:S.EL1/S.EL0的安全软件负责控制自身执行如何被EL3中断抢占,并且将中断转交给EL3处理。 当设置了EL3_EXCEPTION_HANDLING=1时,则invalid,因为EL3 interrupts被无条件的路由到EL3,EL3中断此时总是抢占S.EL1/S.EL0执行然后再转交给EL3进行处理。 |
CSS=0 TEL3=1 |
执行于安全状态时,路由到EL3 valid:EL3安全软件可将中断转交给S.EL1处理 |
执行于安全状态时,路由到EL3 valid:EL3安全软件在将中断转交给非安全软件前,保存S.EL1/S.EL0状态。这需要S.EL1和EL3软件协调保证前者的状态被后者正确保存了。 |
执行S.EL1/S.EL0时,路由到EL3 valid:EL3中安全软件可以处理中断 |
CSS=1 TEL3=0 |
执行于非安全状态时,路由到FEL invalid:安全中断对非安全软件不可见,违反了ARM安全扩展的初衷。 |
执行于非安全状态时,路由到FEL valid:非安全软件处理非安全中断 |
执行于非安全状态时,路由到FEL invalid:安全中断对非安全软件不可见,,违反了ARM安全扩展的初衷。 |
CSS=1 TEL3=1 |
执行于非安全状态时,路由到EL3 valid:EL3安全软件可见中断转交给S.EL1处理。 |
执行于非安全状态时,路由到EL3 invalid:没理由将中断路由到EL3,然后EL3再将其递回给非安全软件进行处理。 |
执行于非安全状态时,路由到EL3 valid:EL3安全软件可以处理EL3中断。 |
5.1.4. Mapping of interrupt type to signal
5.2. Assumptions in Interrupt Management Framework
此框架做了如下假定来简化实现:
- 虽然框架支持两种类型安全中断(EL3和S.EL1中断),但是仅有GICv3以Group 0中断形式支持EL3中断。假设GICv2的所有安全中断在S.EL1中被处理。他们可以通过EL3传递到S.EL1,但是不能被EL3处理。
- EL3执行时中断异常(PSTATE.I和F)被屏蔽。
- 中断管理:
- 提供注册某一类型中断的路由规格和处理函数。
- 支持将一种类型中断控制权转交给注册处理函数处理。
5.3. Software components
中断管理角色和职责被细分为运行在EL3和S.EL1件的一系列组件:
- EL3 Runtime FIrmware。
- SPD(Secure Payload Dispatcher)服务。SPD和运行在S.EL1/S.EL0中软件交互,并负责安全和非安全状态切换。中断和SMC都会触发安全和非安全切换。SPD属于EL3 Runtime Firmware一部分。
- SP(Secure Payload)。SP指的是运行于S.EL1/S.EL0的安全OS。它和SPD服务交互,处理和非安全软件的通信。
5.4. Interrupt registration
下面介绍不同软件组件在中断处理函数注册细节。
5.4.1. EL3 runtime firmware
中断处理函数定义如下:
typedef uint64_t (*interrupt_type_handler_t)(uint32_t id, uint32_t flags, void *handle, void *cookie);
id:保留参数。
flags:
- 安全状态,bit[0]。1表示处于非安全状态,0表示处于安全状态。
- 保留,bit[31:1]。
handle:指向flags指向当前CPU特定安全状态的cpu_context结构体。
EL3 Runtime Firmware提供如下特定类型中断注册接口。SPD可以使用这些API来为S.EL1或者非安全中断注册处理函数。
int32_t register_interrupt_type_handler(uint32_t type, interrupt_type_handler_t handler, uint32_t flags);
type:INTR_TYPE_S_EL1、INTR_TYPE_EL3、INTR_TYPE_NS
flags:bit[1:0]保存中断路由模型。bit[0]保存安全状态路由模型,bit[1]保存非安全状态路由模型。0表示中断路由目的地是FEL,1表示中断路由目的地是EL3。
hander:中断处理函数。
函数调用成功返回0,中断已注册返回-EALREADY。type、flags、handler非法,则返回-EINVAL。
框架提供如下API给EL3 Runtime Firmware配置和查看当前CPU不同安全状态的中断路由。
u_register_t cm_get_scr_el3(uint32_t security_state); void cm_write_scr_el3_bit(uint32_t security_state, uint32_t bit_pos, uint32_t value);
cm_get_scr_el3()返回当前CPU指定安全状态的SCR_EL3寄存器值。
cm_write_scr_el3_bit()对SCR_EL3指定位写0或1。register_interrupt_type_handler()调用set_routing_model()来设置SCR_EL3,间接调用了cm_get_scr_el3()和cm_write_scr_el3_bit()函数。
int32_t register_interrupt_type_handler(uint32_t type, interrupt_type_handler_t handler, uint32_t flags) { int32_t rc; ... rc = set_routing_model(type, flags); if (rc != 0) return rc; /* Save the handler */ intr_type_descs[type].handler = handler; return 0; } int32_t set_routing_model(uint32_t type, uint32_t flags) { int32_t rc; rc = validate_interrupt_type(type);----------------检查中断类型是否有效。 if (rc != 0) return rc; rc = validate_routing_model(type, flags);----------根据中断类型,检查中断路由是否有效。 if (rc != 0) return rc; /* Update the routing model in internal data structures */ intr_type_descs[type].flags = flags; set_scr_el3_from_rm(type, flags, SECURE); set_scr_el3_from_rm(type, flags, NON_SECURE); return 0; } static void set_scr_el3_from_rm(uint32_t type, uint32_t interrupt_type_flags, uint32_t security_state) { uint32_t flag, bit_pos; flag = get_interrupt_rm_flag(interrupt_type_flags, security_state); bit_pos = plat_interrupt_type_to_line(type, security_state); intr_type_descs[type].scr_el3[security_state] = (u_register_t)flag << bit_pos; if (cm_get_context(security_state) != NULL) cm_write_scr_el3_bit(security_state, bit_pos, flag); } void cm_write_scr_el3_bit(uint32_t security_state, uint32_t bit_pos, uint32_t value) { cpu_context_t *ctx; el3_state_t *state; u_register_t scr_el3; ctx = cm_get_context(security_state); assert(ctx != NULL); ... state = get_el3state_ctx(ctx); scr_el3 = read_ctx_reg(state, CTX_SCR_EL3);-------------------从寄存器SCR_EL3中读取值到scr_el3中。 scr_el3 &= ~(1UL << bit_pos); scr_el3 |= (u_register_t)value << bit_pos;--------------------更新SCR_EL3寄存器的某个bit值。 write_ctx_reg(state, CTX_SCR_EL3, scr_el3);-------------------将scr_el3写入SCR_EL3寄存器中。 } u_register_t cm_get_scr_el3(uint32_t security_state) { cpu_context_t *ctx; el3_state_t *state; ctx = cm_get_context(security_state); assert(ctx != NULL); state = get_el3state_ctx(ctx); return read_ctx_reg(state, CTX_SCR_EL3); }
当前框架中,EL3软件负责对路由模型编程,SPD负责确保在接收到中断时遵循路由模型。
/* Routed to EL3 from NS. Taken to S-EL1 from Secure */ #define INTR_SEL1_VALID_RM0 U(0x2)------------------------S.EL1中断发生在非安全世界,路由到EL3,最终送到S.EL1。 /* Routed to EL3 from NS and Secure */ #define INTR_SEL1_VALID_RM1 U(0x3)------------------------S.EL1中断发生在安全或非安全世界,都路由到EL3. /* Routed to EL1/EL2 from NS and to S-EL1 from Secure */ #define INTR_NS_VALID_RM0 U(0x0)--------------------------非安全中断发生在非安全世界,路由到EL1/EL2;发生在安全世界,路由到S.EL1。 /* Routed to EL1/EL2 from NS and to EL3 from Secure */ #define INTR_NS_VALID_RM1 U(0x1)--------------------------非安全中断发生在非安全世界,路由到EL1/EL2;发生在安全世界,路由到EL3。 /* Routed to EL3 from NS. Taken to S-EL1 from Secure and handed over to EL3 */ #define INTR_EL3_VALID_RM0 U(0x2)-------------------------EL3中断发生在非安全世界,路由到EL3;发生在安全世界,先带到S.EL1,再被转交到EL3。 /* Routed to EL3 from NS and Secure */ #define INTR_EL3_VALID_RM1 U(0x3)-------------------------发生在安全或非安全世界,路由到EL3。 /* This is the default routing model */ #define INTR_DEFAULT_RM U(0x0)
5.4.2. Secure payload dispatcher
SPD负责决定和维护自身和SP支持的中断的路由模型。它还负责根据路由模型在安全软件和非安全软件之间传递中断。
路由模型可以在编译时或运行时确定,EL3软件使用register_interrupt_type_handler()为每个中断注册处理函数。
SPD必须要SP初始化完成后,才能对路由模型进行编程。
在接收EL3软件中断后,SPD必须确定将控制权移交给SP的机制。
5.4.3. Test secure payload dispatcher behavior
TSPD仅处理S.EL1中断,并且在构建时提供以下路由模型。- 当处于非安全状态时,S.EL1中断路由到EL3;当处于安全状态时,即CSS=0,TEL3=0&CSS=1,TEL3=1,S.EL1中断路由到FEL。
- 当TSP_NS_INTR_ASYNC_PREEMPT为0时,默认路由模型用于非安全中断。对于非安全中断,它们在任一安全状态下被路由到FEL,即CSS=0,TEL3=0&CSS=1,TEL3=0。
- 当TSP_NS_INTR_ASYNC_PREEMPT为1时,当执行处于安全状态时,非安全中断被路由到EL3,即CSS=0,TEL3=1表示非安全中断。这有效地抢占了Secure-EL1。默认路由模型用于非安全状态下的非安全中断,CSS=1,TEL3=0。
tspd_init()执行以下操作以满足前面提到的需求:
-
将控制权传递给TSP以执行其初始化。
-
TSPD实现了Secure-EL1中断的处理函数。
- 当TSP_NS_INTR_ASYNC_PREEMPT为1时,TSPD为非安全中断实现一个处理函数。
tspd_setup()对跳转到SP做一些简单配置,并且赋值tspd_init()给bl32_init()。bl31_main()中调用bl32_init()进行跳转到BL32,即SP的工作。
static int32_t tspd_setup(void) { entry_point_info_t *tsp_ep_info; uint32_t linear_id; linear_id = plat_my_core_pos(); tsp_ep_info = bl31_plat_get_next_image_ep_info(SECURE);------------------获取BL32,即SP的入口信息entry_point_info_t,后面跳转到SP需要。 if (!tsp_ep_info) { WARN("No TSP provided by BL2 boot loader, Booting device" " without TSP initialization. SMC`s destined for TSP" " will return SMC_UNK\n"); return 1; } if (!tsp_ep_info->pc) return 1; tspd_init_tsp_ep_state(tsp_ep_info, TSP_AARCH64, tsp_ep_info->pc, &tspd_sp_context[linear_id]);-------------------------------初始化tsp_ep_info和tspd_sp_context[]。结果分别放入全局变量bl32_image_ep_info和tspd_sp_context[linear_id]中。 #if TSP_INIT_ASYNC bl31_set_next_image_type(SECURE); #else /* * All TSPD initialization done. Now register our init function with * BL31 for deferred invocation */ bl31_register_bl32_init(&tspd_init);-----------------------------------注册tspd_init,bl32_init指向tspd_init,后续bl31_main()中会调用。 #endif return 0; } int32_t tspd_init(void) { uint32_t linear_id = plat_my_core_pos(); tsp_context_t *tsp_ctx = &tspd_sp_context[linear_id];------------------获取当前CPU的tspd和sp的上下文。 entry_point_info_t *tsp_entry_point; uint64_t rc; tsp_entry_point = bl31_plat_get_next_image_ep_info(SECURE);------------获取BL32的入口信息。 assert(tsp_entry_point); cm_init_my_context(tsp_entry_point); rc = tspd_synchronous_sp_entry(tsp_ctx);-------------------------------跳转到SP执行。 assert(rc != 0); return rc; }
tspd_smc_handler()是tspd的核心,处理安全和非安全世界发起的SMC调用。
static uintptr_t tspd_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) { cpu_context_t *ns_cpu_context; uint32_t linear_id = plat_my_core_pos(), ns; tsp_context_t *tsp_ctx = &tspd_sp_context[linear_id]; uint64_t rc; ... ns = is_caller_non_secure(flags);----------------------------------------发起SMC调用的是安全还是非安全世界,ns为1表示非安全世界,ns为0表示安全世界。 switch (smc_fid) { case TSP_PREEMPTED: if (ns) SMC_RET1(handle, SMC_UNK); return tspd_handle_sp_preemption(handle); case TSP_HANDLED_S_EL1_INTR: if (ns) SMC_RET1(handle, SMC_UNK); assert(handle == cm_get_context(SECURE)); /* * Restore the relevant EL3 state which saved to service * this SMC. */ if (get_yield_smc_active_flag(tsp_ctx->state)) { SMC_SET_EL3(&tsp_ctx->cpu_ctx, CTX_SPSR_EL3, tsp_ctx->saved_spsr_el3); SMC_SET_EL3(&tsp_ctx->cpu_ctx, CTX_ELR_EL3, tsp_ctx->saved_elr_el3); #if TSP_NS_INTR_ASYNC_PREEMPT /* * Need to restore the previously interrupted * secure context. */ memcpy(&tsp_ctx->cpu_ctx, &tsp_ctx->sp_ctx, TSPD_SP_CTX_SIZE); #endif } /* Get a reference to the non-secure context */ ns_cpu_context = cm_get_context(NON_SECURE); assert(ns_cpu_context); cm_el1_sysregs_context_restore(NON_SECURE); cm_set_next_eret_context(NON_SECURE); SMC_RET0((uint64_t) ns_cpu_context); case TSP_ENTRY_DONE:----------------------------------------------------BL32 SP告诉EL3软件自身初始化完成,此时TSPD注册PSCI钩子函数、注册非安全状态下S.EL1中断处理函数等。 if (ns) SMC_RET1(handle, SMC_UNK); /* * Stash the SP entry points information. This is done * only once on the primary cpu */ assert(tsp_vectors == NULL); tsp_vectors = (tsp_vectors_t *) x1;---------------------------------BL32返回的第二个参数,BL31可以直接调用tsp_vectors来完成BL32提供的功能。 if (tsp_vectors) { set_tsp_pstate(tsp_ctx->state, TSP_PSTATE_ON); /* * TSP has been successfully initialized. Register power * management hooks with PSCI */ psci_register_spd_pm_hook(&tspd_pm);----------------------------注册SPD提供的功耗管理狗子函数。 flags = 0; set_interrupt_rm_flag(flags, NON_SECURE); rc = register_interrupt_type_handler(INTR_TYPE_S_EL1, tspd_sel1_interrupt_handler, flags);---------------------------------------------注册INTR_TYPE_S_EL1类型中断的处理函数tspd_sel1_interrupt_handler()。当CPU执行于安全状态,注册非安全中断路由到EL3进行处理函数。 if (rc) panic(); #if TSP_NS_INTR_ASYNC_PREEMPT flags = 0; set_interrupt_rm_flag(flags, SECURE); rc = register_interrupt_type_handler(INTR_TYPE_NS, tspd_ns_interrupt_handler, flags);-----------------------------------------------注册INTR_TYPE_NS类型中断的处理函数tspd_ns_interrupt_handler。 if (rc) panic(); disable_intr_rm_local(INTR_TYPE_NS, SECURE); #endif } #if TSP_INIT_ASYNC ... #else tspd_synchronous_sp_exit(tsp_ctx, x1); break; #endif ... default: break; } SMC_RET1(handle, SMC_UNK); }
5.4.4. Secure payload
SP必须在S.EL1实现中断处理框架,以支持其选择的中断路由模型。
SP会在以下情况之间交替进行:
- 在启用IRQ、FIQ或两个中断都使能的代码中,如果中断类型以FEL为目标,那么它将被路由到Secure-EL1异常向量表。这被定义为处理中断的异步模式。此模式适用于S.EL1和非安全中断。
- 在两个中断都被禁用的代码中,如果中断类型以FEL为目标,那么执行最终将迁移到非安全状态。任何不安全的中断都将按照路由模型中的描述进行处理,其中CSS=1,TEL3=0。Secure-EL1中断将路由到EL3(根据CSS=1和TEL3=1的路由模型),SPD服务将把它们交给SP。这被定义为处理中断的同步模式。
5.4.5. Test secure payload behavior
TSP实现了一个入口点(tsp_sel1_intr_entry()),用于处理在非安全状态下发生并通过TSPD服务路由的Secure-EL1中断(同步处理模型)。它通过tsp_vectors将对该入口点的引用传递给TSPD服务。
TSP还用一个能够处理S.EL1异常级别上发生的FIQ和IRQ异常的向量表来替换通过early_exceptions(BL2使用)变量引用的默认异常向量表。此表通过tsp_exceptions(BL32使用)变量引用并编程到VBAR_EL1中。它迎合了异步处理模型。
TSP还对Arm通用计时器块中的安全物理计时器进行编程,以引发周期性中断(每半秒一次),以便测试软件组件中列出的所有软件组件的中断管理。
vector_base early_exceptions--------------------------------------------BL2使用的异常向量表。 /* ----------------------------------------------------- * Current EL with SP0 : 0x0 - 0x200 * ----------------------------------------------------- */... /* ----------------------------------------------------- * Lower EL using AArch32 : 0x600 - 0x800 * ----------------------------------------------------- */ ... vector_base tsp_exceptions----------------------------------------------BL32使用的异常向量表。 /* ----------------------------------------------------- * Current EL with _sp_el0 : 0x0 - 0x200. No exceptions * are expected and treated as irrecoverable errors. * ----------------------------------------------------- */... /* ----------------------------------------------------- * Lower EL using AArch32 : 0x600 - 0x800. No exceptions * handled since the TSP does not implement a lower EL. * ----------------------------------------------------- */ ... typedef uint32_t tsp_vector_isn_t; typedef struct tsp_vectors { tsp_vector_isn_t yield_smc_entry; tsp_vector_isn_t fast_smc_entry; tsp_vector_isn_t cpu_on_entry; tsp_vector_isn_t cpu_off_entry; tsp_vector_isn_t cpu_resume_entry; tsp_vector_isn_t cpu_suspend_entry; tsp_vector_isn_t sel1_intr_entry; tsp_vector_isn_t system_off_entry; tsp_vector_isn_t system_reset_entry; tsp_vector_isn_t abort_yield_smc_entry; } tsp_vectors_t; vector_base tsp_vector_table---------------------------------------------BL32运行起来后使用的向量表。 b tsp_yield_smc_entry b tsp_fast_smc_entry b tsp_cpu_on_entry b tsp_cpu_off_entry b tsp_cpu_resume_entry b tsp_cpu_suspend_entry b tsp_sel1_intr_entry b tsp_system_off_entry b tsp_system_reset_entry b tsp_abort_yield_smc_entry
5.5. Interrupt handling
5.5.1. EL3 runtime firmware
EL3软件的异常向量表为runtime_exceptions,其中handle_interrupt_exception负责处理送到EL3的IRQ、FIQ中断。
当中断发生时,每个中断的异常向量负责如下任务:
1. 进入异常立即保存所有的通用寄存器上下文x0-x30。寄存器保存在SP_EL3寄存器引用的per-cpu数据结构cpu_context中。
2. 保存系统寄存器ELR_EL3, SP_EL0 and SPSR_EL3到SP_EL3应用的per-cpu数据结构cpu_context中。
3. 通过从S_EL0的per-cpu cpu_context数据结构中恢复CTX_RUNTIME_SP值切换到C执行环境,执行msr spsel, #0指令。
4. 确定中断类型。Secure-EL1中断将在FIQ向量处发出信号。非安全中断将在IRQ向量处发出信号。平台应该实现以下API来确定挂起中断的类型。
uint32_t plat_ic_get_interrupt_type(void);
它应该返回INTR_TYPE_S_EL1 或INTR_TYPE_NS。
5. 确定已生成的中断类型的处理程序。
interrupt_type_handler get_interrupt_type_handler(uint32_t interrupt_type);
.macro handle_interrupt_exception label /* * Save general purpose and ARMv8.3-PAuth registers (if enabled). * If Secure Cycle Counter is not disabled in MDCR_EL3 when * ARMv8.5-PMU is implemented, save PMCR_EL0 and disable Cycle Counter. */ bl save_gp_pmcr_pauth_regs----------------------------1. #if ENABLE_PAUTH /* Load and program APIAKey firmware key */ bl pauth_load_bl31_apiakey #endif /* Save the EL3 system registers needed to return from this exception */ mrs x0, spsr_el3---------------------------------------2. mrs x1, elr_el3 stp x0, x1, [sp, #CTX_EL3STATE_OFFSET + CTX_SPSR_EL3] /* Switch to the runtime stack i.e. SP_EL0 */ ldr x2, [sp, #CTX_EL3STATE_OFFSET + CTX_RUNTIME_SP]----3. mov x20, sp msr spsel, #MODE_SP_EL0 mov sp, x2 /* * Find out whether this is a valid interrupt type. * If the interrupt controller reports a spurious interrupt then return * to where we came from. */ bl plat_ic_get_pending_interrupt_type-------------------4. 获取中断类型INTR_TYPE_NS、INTR_TYPE_S_EL1、INTR_TYPE_EL3。 cmp x0, #INTR_TYPE_INVAL--------------------------------验证中断有效性。 b.eq interrupt_exit_\label bl get_interrupt_type_handler---------------------------5.根据中断类型获取中断处理函数。 cbz x0, interrupt_exit_\label---------------------------检查中断处理函数是否正确。 mov x21, x0 mov x0, #INTR_ID_UNAVAILABLE /* Set the current security state in the 'flags' parameter */ mrs x2, scr_el3 ubfx x1, x2, #0, #1 /* Restore the reference to the 'handle' i.e. SP_EL3 */ mov x2, x20 /* x3 will point to a cookie (not used now) */ mov x3, xzr /* Call the interrupt type handler */ blr x21--------------------------------------------------调用中断函数进行同步处理。 interrupt_exit_\label: /* Return from exception, possibly in a different security state */ b el3_exit .endm
5.5.2. Secure payload dispatcher
5.5.2.1. Interrupt entry
5.5.2.2. Interrupt exit
5.5.2.3. Test secure payload dispatcher Secure-EL1 interrupt handling
TSPD调用tspd_sel1_interrupt_handler()处理S.EL1中断,它首先验证中断有效性,成功后进入TSP调用tsp_vectors->sel1_intr_entry处理中断。
static uint64_t tspd_sel1_interrupt_handler(uint32_t id, uint32_t flags, void *handle, void *cookie) { uint32_t linear_id; tsp_context_t *tsp_ctx; /* Check the security state when the exception was generated */ assert(get_interrupt_src_ss(flags) == NON_SECURE); /* Sanity check the pointer to this cpu's context */ assert(handle == cm_get_context(NON_SECURE)); /* Save the non-secure context before entering the TSP */ cm_el1_sysregs_context_save(NON_SECURE); /* Get a reference to this cpu's TSP context */ linear_id = plat_my_core_pos(); tsp_ctx = &tspd_sp_context[linear_id]; assert(&tsp_ctx->cpu_ctx == cm_get_context(SECURE)); if (get_yield_smc_active_flag(tsp_ctx->state)) { tsp_ctx->saved_spsr_el3 = (uint32_t)SMC_GET_EL3(&tsp_ctx->cpu_ctx, CTX_SPSR_EL3); tsp_ctx->saved_elr_el3 = SMC_GET_EL3(&tsp_ctx->cpu_ctx, CTX_ELR_EL3); #if TSP_NS_INTR_ASYNC_PREEMPT /*Need to save the previously interrupted secure context */ memcpy(&tsp_ctx->sp_ctx, &tsp_ctx->cpu_ctx, TSPD_SP_CTX_SIZE); #endif } cm_el1_sysregs_context_restore(SECURE);-----------------------------------切换到S.EL1。 cm_set_elr_spsr_el3(SECURE, (uint64_t) &tsp_vectors->sel1_intr_entry, SPSR_64(MODE_EL1, MODE_SP_ELX, DISABLE_ALL_EXCEPTIONS));----------在S.EL1执行中断处理函数。 cm_set_next_eret_context(SECURE);-----------------------------------------返回EL3. SMC_RET2(&tsp_ctx->cpu_ctx, TSP_HANDLE_SEL1_INTR_AND_RETURN, read_elr_el3()); }
5.5.2.4. Test secure payload dispatcher non-secure interrupt handling
5.5.3. Secure payload interrupt handling
5.5.3.1. Test secure payload behavior
4. 平台中断控制器API
参考文档:《Platform Interrupt Controller API》。
vector_entry标签:中断,Firmware,路由,Trusted,entry,EL3,EL1,ARM,tsp From: https://www.cnblogs.com/arnoldlu/p/14383972.html