中断管理框架负责管理路由到 EL3 的中断,还允许 EL3 软件配置中断的路由行为。其主要实现两个要求:
根据处理中断的异常等级,中断管理框架将中断分为几类:
下面定义了框架实现的各种中断类型。
#define INTR_TYPE_S_EL1 0
#define INTR_TYPE_EL3 1
#define INTR_TYPE_NS 2
中断可以作为 FIQ 或 IRQ 生成。中断的目标异常等级是 EL3 Secure Configuration Register(安全配置寄存器SCR)中的 FIQ 和 IRQ 位(SCR_EL3.FIQ
和SCR_EL3.IRQ
位)进行配置的。当SCR_EL3.FIQ
=1时,FIQ被路由到EL3,否则路由到能够处理中断的 First Exception Level (FEL)。当 SCR_EL3.IRQ
=1 时,IRQ 将路由到 EL3,否则路由至 FEL。在进入较低异常等级之前, EL3 软件已经配置了每个安全状态的SCR。
中断( FIQ 或 IRQ)的路由模型定义为每个安全状态的目标异常等级。对于每个安全状态,它由一个位表示,0
表示中断应路由至 FEL,而1
表示中断应路由至 EL3。仅当不在 EL3 执行时,路由模型才适用。
中断的默认路由模型是将其路由到任一安全状态下的 FEL。
中断管理框架认为中断的某些路由模型是不正确的,因为它们与上面提到的要求相冲突。下面介绍了哪些路由模型是有效的,哪些是无效的。目前只有 GIC 版本 3.0 (Arm GICv3) 支持 EL3 中断,GIC 版本 2.0 (Arm GICv2) 仅支持 Secure-EL1 和 Non-secure 中断。下面是定义的一些术语:
0
表示安全,1
表示非安全0
表示目标异常等级为 FEL,1
表示目标异常等级为 EL3注意:异常等级和安全状态要区分开,不能混淆,Armv8-A包括两种安全状态和四个异常等级。
CSS=0,TEL3=0。当在安全状态执行时,中断将路由至 FEL。这是一个有效的路由模型,因为安全软件控制着安全中断的处理。
CSS=0,TEL3=1。当在安全状态执行时,中断被路由到 EL3。这是一个有效的路由模型,因为 EL3 中的安全软件可以将中断移交给 Secure-EL1 进行处理。
CSS=1,TEL3=0。当在非安全状态执行时,中断将路由至 FEL。这是一个无效的路由模型,因为这个安全中断对于安全侧软件来说是不可见的,这违反了 Arm Security Extensions 的设计思想。
CSS=1,TEL3=1。当在非安全状态执行时,中断被路由到 EL3。这是一个有效的路由模型,因为 EL3 中的安全软件可以将中断移交给 Secure-EL1 进行处理。
EL3_EXCEPTION_HANDLING
为 时1
,此路由模型无效,因为 EL3 中断将无条件路由到 EL3,并且 EL3 中断将始终抢占Secure EL1/EL0 执行。请参阅异常处理文档。中断管理框架是在实现的中断控制器下使用。中断控制器可以根据当前的安全状态生成一种中断类型,向 CPU 发送 FIQ 或 IRQ 信号。中断类型和信号之间的映射关系由平台实现确定,这些信息用于是否配置SCR_EL3
中的IRQ 或 FIQ 位。平台通过 plat_interrupt_type_to_line()
API 提供此信息(在 移植指南中进行了描述)。例如,在 FVP 平台上,当使用 Arm GICv2 中断控制器时,Secure-EL1 中断发出 FIQ 信号,而 Non-secure 中断发出 IRQ 信号,这些运用在任意安全状态。
应当注意,如果将多种中断类型映射到一个中断信号,并且其中任一中断设置TEL3=1,则该中断信号将路由到EL3。这意味着使用相同中断信号的其他类型中断将强制使用相同的路由模型,在选择路由模型时应牢记这一点。
例如,在 Arm GICv3 中,当执行上下文为 Secure-EL1/Secure-EL0 时,EL3 和非安全中断类型都会映射成 FIQ 信号。因此,如果其中任一中断采用此路由模型,即配置CSS=0和TEL3=1 ,则在 Secure-EL1/Secure-EL0 中执行时,FIQ 位将被配置,使 FIQ 信号路由到EL3,这会导致其他中断也会路由到 EL3。
该框架做出以下假设以简化其实现。
PSTATE.I
和F
位)在 EL3 执行期间被屏蔽。中断管理涉及从 EL3 到 Secure-EL1 安全软件栈中的各个组件,这些在软件组件章节中进行了描述。该框架使用以下结构存储中断的相关信息:
typedef struct intr_type_desc {
interrupt_type_handler_t handler;
uint32_t flags;
uint32_t scr_el3[2];
} intr_type_desc_t;
flags
字段的低两位存储中断的路由模型。Bit[0] 存储处于安全状态时的路由模型,Bit[1] 存储处于非安全状态时的路由模型。正如路由模型一节中提到的, 值0
意味着中断路由到 FEL ,值1
意味着中断路由到 EL3,其余位保留为 SBZ。set_interrupt_rm_flag()
宏用于配置flags
字段。
scr_el3[2]
字段也存储路由模型,但作为flags
字段(两种安全状态)到SCR_EL3
位(FIQ
或者IRQ位)的映射,即如从NS和Secure路由到EL3,NS路由到EL1/EL2,Secure路由到S-EL1等等。
该框架还依赖于平台来配置中断控制器,用以区分安全和非安全中断。平台必须知道系统中的安全设备及其关联的中断号。平台应该配置中断控制器以启用安全中断,确保安全中断优先级始终高于非安全中断,并将其路由到主 CPU。平台还应该导出移植指南中关于中断处理的接口。
在本文档的其余部分中,为了简单起见,只考虑了 Arm GICv2 系统,并假设 FIQ 信号用于生成 Secure-EL1 中断,IRQ 信号用于生成非安全中断,不考虑 EL3 中断。
中断管理组件在 EL3 和 Secure-EL1 中功能不同:
EL3 Runtime Firmware。该组件对于所有平台移植 TF-A 是通用的。
Secure Payload Dispatcher (SPD) 服务。该服务与在 Secure-EL1/Secure-EL0 中运行的 Secure Payload (SP) 软件进行通信,负责安全与非安全状态的切换。切换通过 Secure Monitor Call 触发,其通过上下文管理库导出的 API 来实现。在两种安全状态之间切换执行也是中断管理的要求,这导致严重依赖 SPD 服务。TF-A 实现了一个示例 Test Secure Payload Dispatcher (TSPD) 服务。
一个SPD 服务是集成到 EL3 runtime firmware 中,并且可以与某些平台移植 TF-A 是通用的。
Secure Payload (SP)。Secure Payload 对应于在 Secure-EL1/Secure-EL0 中运行的 Secure OS。它与 SPD 服务交互,管理与非安全软件间的通信。TF-A 实现了一个 Test Secure Payload 示例,其仅在Secure-EL1 中运行。
Secure payload 对于某些平台移植 TF-A 是通用的,就像 SPD 服务一样。
本节详细描述了中断软件组件的作用。
该组件为中断处理的原型声明。
typedef uint64_t (*interrupt_type_handler_t)(uint32_t id,
uint32_t flags,
void *handle,
void *cookie);
其中id
参数是保留的,未来用于传递最高挂起中断 id,目前值为INTR_ID_UNAVAILABLE
。
flags
参数含义如下:
1
表示处于非安全状态,0
表示它处于安全状态。中断处理程序使用此位进行中断路由。handle
参数指向当前安全状态的cpu_context
上下文。
一旦中断处理程序执行完成,将返回到安全或非安全状态。中断处理程序必须返回指向目标安全状态的cpu_context
指针。在 AArch64 上,调用者目前会忽略此返回值,因为处理程序应当通过上下文管理库 API 设置相应的值,移植中断处理程序必须设置目标上下文结构。中断处理程序应将所有错误条件视为严重错误,并采取适当的操作,例如使用断言失败。
Runtime firmware 提供以下 API,用于注册中断的处理程序。Secure Payload Dispatcher 服务应使用此 API 来注册 Secure-EL1 的处理程序,并可选择用于非安全中断,该API还要求调用者指定中断的路由模型。
int32_t register_interrupt_type_handler(uint32_t type,
interrupt_type_handler handler,
uint64_t flags);
type
参数可以是上面列出的三种中断类型,即INTR_TYPE_S_EL1
,INTR_TYPE_NS
和 INTR_TYPE_EL3
。
注册成功后该函数将返回0,如果中断处理程序已经注册,它将返回-EALREADY
。如果type
无法识别或flags
或handler
无效,则会返回-EINVAL
。
在任一安全状态下,SCR_EL3.FIQ/IRQ
位控制中断路由,即路由到当前异常等级还是EL3。上下文管理库为每个CPU的安全状态维护了一个SCR_EL3
系统寄存器副本。 EL3 运行时固件可以使用下面API,检索当前 CPU 每个安全状态的路由模型。在从 EL3 执行异常返回时,el3_exit()
函数利用cpu_context
保存的SCR_EL3
值来配置 SCR_EL3
寄存器 。
uint32_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()
将0
or1
写入位bit_pos
,即SCR_EL3
寄存器中的相应位。函数register_interrupt_type_handler()
调用 set_routing_model()
API,配置SCR_EL3
寄存器,完成路由模型的设置。
值得注意的是,在当前框架的实现中,EL3 Runtime Firmware 负责配置路由模型,SPD 负责确保在收到中断时遵守此路由模型。
SPD服务负责确定和维护其自身和Secure Payload支持的中断路由模型,它还根据路由模型在安全和非安全软件之间传送中断,可以在构建时或运行时确定路由模型。它必须使用这些信息来注册中断处理程序,使用register_interrupt_type_handler()
API。
如果 SPD 服务在构建时不知道路由模型,则 SP 初始化完成后必须提供。SPD应该在SP初始化完成之后对路由模型进行配置,例如在初始化函数bl32_init
中。
SPD 应在收到来自 EL3 runtime firmware 的中断后将控制权传递给 Secure Payload,该信息可以在编译时提供给 SPD 服务,也可以由 SP 在运行时提供。
TSPD 仅处理 Secure-EL1 中断,并在编译时提供以下路由模型。
TSP_NS_INTR_ASYNC_PREEMPT
为0时,Non-secure 中断使用默认路由模型。对于 Non-secure 中断,在任一安全状态下都路由到 FEL,即CSS=0、TEL3=0和CSS=1、TEL3=0。(这种情况下:Non-secure中断将在Secure-EL1 IRQ 异常向量处触发)TSP_NS_INTR_ASYNC_PREEMPT
被定义为1时,当执行处于安全状态时,Non-secure 中断强制被路由到EL3,即对于 Non-secure 中断CSS=0,TEL3=1,这事实上抢占了 Secure-EL1。此时默认路由模型只用于非安全状态下的非安全中断,即CSS=1,TEL3=0。通过执行函数tspd_init()
来满足前面提到的要求。
它将控制权传递给 Test Secure Payload 以执行初始化。TSP 提供了 SP 中向量表的地址tsp_vectors
,其还包括 Secure-EL1 的中断处理程序sel1_intr_entry
。当 TSPD 接收到 Secure-EL1 中断时,它会将控制权传递给该地址处的 TSP。
当调用tsp_sel1_intr_entry()
时,要求TSPD屏蔽所有中断(PSTATE.DAIF
位)。TSP 必须保存被调用者的通用寄存器、SP_EL1/Secure-EL0、LR、VFP 和系统寄存器,可以用 x0-x18
启用 C runtime。
TSPD 实现 Secure-EL1 中断的处理函数,该函数使用 register_interrupt_type_handler()
API 注册到 EL3 runtime firmware,如下所示:
/* Forward declaration */
interrupt_type_handler tspd_secure_el1_interrupt_handler;
int32_t rc, flags = 0;
set_interrupt_rm_flag(flags, NON_SECURE); // 这里NON_SECURE表示生成中断时PE在非安全状态执行
rc = register_interrupt_type_handler(INTR_TYPE_S_EL1,
tspd_secure_el1_interrupt_handler,
flags);
if (rc)
panic();
当编译标志TSP_NS_INTR_ASYNC_PREEMPT
定义为 1 时,TSPD 实现非安全中断的处理函数。同样,如下所示:
/* Forward declaration */
interrupt_type_handler tspd_ns_interrupt_handler;
int32_t rc, flags = 0;
set_interrupt_rm_flag(flags, SECURE); // 这里SECURE表示生成中断时PE在安全状态执行
rc = register_interrupt_type_handler(INTR_TYPE_NS,
tspd_ns_interrupt_handler,
flags);
if (rc)
panic();
Secure payload 必须在 Secure-EL1 (Secure-EL1 IHF) 上实现中断处理框架,以支持其选择的中断路由模型。Secure payload 将在以下情况之间交替执行。
SP 实现的中断处理框架应该支持这些中断处理模型中的一种或两种,具体取决于所选的路由模型。
以下列表简要描述了有效路由模型的选择(请参阅有效路由模型)如何影响 Secure-EL1 IHF 的实现。如果 SPD 服务在编译时不知道中断路由模型的选择,则 SP 应在运行时的初始化阶段将此信息传递给 SPD 服务。
如前所述,考虑了 Arm GICv2 系统,并假设 FIQ 信号用于生成 Secure-EL1 中断,IRQ 信号用于在任一安全状态下生成非安全中断。
CSS=0,TLE3=0。如果PSTATE.F=0
,Secure-EL1 中断将进入 Secure-EL1 FIQ 异常向量。Secure-EL1 IHF 应实现支持处理异步 FIQ 中断。
如果PSTATE.F=1
,此时 Secure-EL1 中断将按照同步中断处理模型进行处理。在注册阶段,SP 可以将 Secure-EL1 中断入口点导出给到 SPD 服务。SPD 服务还需要了解系统的状态,通用目的以及PSTATE
状态。如果 SPD 服务在构建时不知道该信息,则 SP 应在注册阶段以实现定义的方式提供此信息。
CSS=1,TEL3=1。当执行处于非安全状态时,中断将路由至 EL3。它们应该通过上面 1 中描述的同步中断处理模型来处理。
CSS=0,TEL3=1。当执行处于安全状态时,Secure-EL1 中断将路由至 EL3,这对 SP 不可见。Secure-EL1/Secure-EL0 中的PSTATE.F
位不会屏蔽 FIQ。EL3 运行时固件将调用 SPD 服务为 Secure-EL1 中断注册处理程序。然后,Secure-EL1 IHF 应通过上面 1 中描述的同步中断处理模型来处理所有 Secure-EL1 中断。
CSS=0,TEL3=0。如果是PSTATE.I=0
,非安全中断将进入 Secure-EL1 IRQ 异常向量。Secure-EL1 IHF 应与 SPD 服务协调,转移到非安全状态处理中断,例如 SP 可以分配一个ID以向 SPD 服务发出 SMC64 或 SMC32,以指示 SP 执行已被非安全中断抢占。如果 SPD 服务在编译时不知道此ID,则 SP 可以在注册阶段提供。
如果PSTATE.I=1
那么非安全中断将挂起,直到在非安全状态下恢复执行。
CSS=0,TEL3=1。非安全中断被路由到 EL3,这对 SP 不可见。Secure-EL1/Secure-EL0 中的位PSTATE.I
将不起作用。SPD 服务应注册一个非安全中断处理程序,该处理程序应正确保存 SP 状态,并在非安全状态下恢复执行处理中断。Secure-EL1 IHF 不需要采取任何操作。
CSS=1,TEL3=0。非安全中断在非安全状态 (EL1/EL2) 的 FEL 中处理,并且对 SP 不可见。此路由模型不影响 SP 行为。
Secure Payload 还必须确保所有 Secure-EL1 中断在中断控制器上进行正确配置,它还应该配置 EL3 runtime firmware 无法识别的其他 Secure-EL1 中断。
TSP 选择的 Secure-EL1 和 non-secure 中断的路由模型在Secure Payload Dispatcher部分中进行了描述,TSPD 服务在编译时就知道。
TSP 实现了一个中断处理入口 ( tsp_sel1_intr_entry()
),用于处理在非安全状态下发生并通过 TSPD 服务(同步处理模型)路由的 Secure-EL1 中断。它通过 tsp_vectors
将对此入口点传递给 TSPD 服务。
通过变量early_exceptions
,TSP 还会替换默认的异常向量表,替换成 Secure-EL1 异常等级发生的 FIQ 和 IRQ 异常向量表。该表通过变量tsp_exceptions
引用并配置到 VBAR_EL1 中,它适合异步处理模型。
TSP 还在 Arm 通用定时器模块中对 Secure Physical Timer 进行配置,以引发周期性中断(每半秒),以测试软件组件中的中断管理。
本节详细描述了每个软件组件(请参阅软件组件一节)在中断处理时的作用。
EL3 runtime firmware使用runtime_exceptions
配置IRQ 和 FIQ 异常向量,如下所示。
SP_EL0
和SP_EL3
类型的 IRQ 和 FIQ 异常上报为错误,不进行处理。如前所述,EL3 runtime firmware执行时,PSTATE.I
和PSTATE.F
位均置位。当产生中断时,每个中断向量负责:
在异常进入时立即保存整个通用寄存器上下文 (x0-x30),这些寄存器保存在cpu_context
上下文结构体中。
将ELR_EL3
、SP_EL0
和系统寄存器保存在cpu_context
上下文结构体中。
从cpu_context
数据结构中恢复CTX_RUNTIME_SP
,切换 C 运行时堆栈,执行msr spsel, #0
指令。
确定中断类型。Secure-EL1 中断以 FIQ 发出信号,Non-secure 中断以 IRQ 发出信号。平台应实现以下 API 来确定待处理中断的类型。
uint32_t plat_ic_get_interrupt_type(void);
它应该返回INTR_TYPE_S_EL1
或INTR_TYPE_NS
。
确定已生成中断的处理程序,为此添加了以下 API。
interrupt_type_handler get_interrupt_type_handler(uint32_t interrupt_type);
它返回此中断的注册处理程序。中断处理程序handler
从上面提到的intr_type_desc_t
中获取。如果没有为此类的中断注册处理程序,则返回NULL
,这种情况被报告为不可恢复的错误。
为生成的中断调用注册的处理函数。该id
参数当前设置为INTR_ID_UNAVAILABLE
。当前安全状态的 id 和cpu_context_t
均通过参数传递给处理函数。
中断处理程序函数返回目标安全状态的cpu_context_t
。
调用el3_exit()
从 EL3 执行异常返回,该el3_exit()
函数负责从 cpu_context_t
目标安全状态的数据结构中恢复寄存器上下文。
当 EL3 runtime firmware 调用中断的处理函数时,SPD 服务开始处理该中断。SPD 服务负责以下工作:
验证中断。确保中断是按照 SPD 服务在注册期间指定的中断路由模型生成的,其通过中断发生时异常等级的安全状态(flags
参数)来判断。如果中断无法识别,处理程序应将其视为不可恢复的错误情形。
SPD 服务可以注册 Secure-EL1 或 Non-secure 中断的处理程序。Non-secure 中断不应该从非安全状态路由到 EL3。此外,如果在 Secure 状态下执行时,将 Secure-EL1 中断路由到 S-EL1,S-EL1 中断也不应从 Secure 状态路由到 EL3。中断处理程序可以使用安全状态标志来检查这一点。
确定是否需要上下文切换。这取决于路由模型和中断类型,对于非安全中断和 S-EL1 中断,如果产生中断的执行上下文的安全状态与处理中断所需的安全状态不同,则需要进行上下文切换。以下两种情况需要执行从安全到非安全的上下文切换,反之亦然:
SPD服务必须保存当前安全状态的系统寄存器上下文,然后恢复目标安全状态的系统寄存器上下文,使用cm_set_next_eret_context()
API来保证要恢复的目标安全状态。
如果目标状态是安全的,则同步中断的处理模型将执行交给 SP。当在 SP 中执行时,Secure-EL1 中断可以路由到 EL3。这意味着在处理中断时,SP 执行可以被另一个更高优先级的 Secure-EL1 中断或 EL3 中断抢占。SPD 服务需要在将控制权交给 SP 之前处理此抢占或管理安全中断优先级。
如果中断通过验证并准备进入低异常等级处理,处理程序需要设置异常返回的上下文cpu_context
。
路由模型允许非安全中断抢占 Secure-EL1 执行(如果已配置)。SPD 服务和 SP 应该实现一种机制,用于将这些中断路由到非安全状态下最后的已知异常等级。SPD 服务应该保存 SP上下文,恢复非安全上下文并准备进入非安全状态,以便处理中断。
当 Secure Payload 处理 完成 Secure-EL1 中断时,它可以通过 SMC32 或 SMC64 将控制权返回给 SPD 服务。SPD 服务应处理此 secure monitor 调用,恢复以前的异常等级和安全状态,继续执行。
TSPD 示例服务注册了一个 Secure-EL1 中断处理程序(中断触发来自 non-secure 状态)。在 S-EL1 中执行期间,TSPD 期望 Secure-EL1 中断由 TSP 在 S-EL1 中处理。中断处理程序 tspd_secure_el1_interrupt_handler()
仅期望被来自非安全状态的 Secure-EL1 中断调用,当调用时会执行以下操作。
flags
参数中提供的安全状态来确保安全中断来自非安全状态。如果情况并非如此,它会断言。cm_el1_sysregs_context_save(NON_SECURE);
保存非安全状态的系统寄存器上下文。tsp_sel1_intr_entry
配置到ELR_EL3
系统寄存器,并设置安全 CPU 上下文中 SPSR_EL3.DAIF
位。设置x0
为 TSP_HANDLE_SEL1_INTR_AND_RETURN
。 如果在 yielding
类型的 SMC 处理期间 TSP 被非安全中断抢占,为了能够重新进入 TSP 进行 Secure-EL1 中断处理,请保存将被丢弃的寄存器,即ELR_EL3
和SRSR_EL3
。它不需要保存任何其他安全上下文,因为 TSP 应该保留它。cm_el1_sysregs_context_restore(SECURE);
恢复安全状态系统寄存器上下文。cm_set_next_eret_context(SECURE);
设置从EL3异常返回的 CPU 上下文为Secure。cpu_context
,指示 SP 现在可以处理该中断。x1
是elr_el3
非安全状态寄存器的值,SP 使用此信息进行调试。下图描述了在非安全状态下执行,生成 Secure-EL1 中断时, TSPD 如何实现中断处理。
TSP 发出ID为TSP_HANDLED_S_EL1_INTR
的SMC来表示中断处理已完成。
TSPD 服务在接收到TSP_HANDLED_S_EL1_INTR
的SMC后,在函数tspd_smc_handler()
里执行以下操作:
x0
值为SMC_UNK
。ELR_EL3
和SPSR_EL3
系统寄存器,回到安全 CPU 上下文,以防 TSP 之前被非安全中断抢占。cm_el1_sysregs_context_restore(NON_SECURE)
恢复非安全状态的系统寄存器上下文。cm_set_next_eret_context(NON_SECURE)
设置下一异常等级的非安全 CPU 上下文。tspd_smc_handler()
返回对非安全的上下文指针cpu_context
。Secure-EL1 中的 TSP 可以在yielding
类型 SMC 调度处理期间被非安全中断抢占,或者在 Secure-EL1 中断处理期间被更高优先级的 EL3 中断抢占。当EL3_EXCEPTION_HANDLING
is 时0
,只有非安全中断才能导致 TSP 抢占,因为系统中没有 EL3 中断。然而EL3_EXCEPTION_HANDLING=1
,任何 EL3 中断都可能抢占Secure执行。
应该注意的是,当 TSP 被抢占时,TSPD 仅允许进入 TSP 以进行 Secure-EL1 中断处理,或者恢复被抢占的yielding
类型 SMC,以响应来自非安全世界的 SMC(TSP_FID_RESUME
)。
在调用yielding
类型 SMC 处理期间,在 Secure-EL1 中触发的非安全中断可以路由到 EL3 或 Secure-EL1,并由编译控制TSP_NS_INTR_ASYNC_PREEMPT
。如果设置了该编译选项,TSPD 将设置路由模型:非安全中断从安全状态路由到 EL3,即TEL3=1、CSS=0,并注册非安全中断处理程序tspd_ns_interrupt_handler()
。tspd_ns_interrupt_handler()
确保中断源来自安全状态,并禁止将非安全中断从安全状态路由到 EL3。当TSP重新进入处理Secure-EL1中断(在非安全世界触发生成的)时,这样做可以防止被非安全中断进一步抢占,然后tspd_ns_interrupt_handler()
调用 tspd_handle_sp_preemption()
进一步处理。
如果TSP_NS_INTR_ASYNC_PREEMPT
编译选项为零(默认),则安全状态下非安全中断的默认路由模型生效,即TEL3=0, CSS=0。在yielding
类型 SMC 处理期间,IRQ 异常不会被屏蔽PSTATE.I=0
,即非安全中断将在 Secure-EL1 IRQ 异常向量处触发。TSP 保存通用寄存器上下文,并发出TSP_PREEMPTED
的 SMC 作为抢占 TSP 的信号。TSPD SMC 处理程序 tspd_smc_handler()
保证 SMC 调用来自安全状态,否则执行将返回到非安全状态。然后它调用tspd_handle_sp_preemption()
进一步处理。
tspd_handle_sp_preemption()
调用后会执行以下操作:
cm_el1_sysregs_context_save(SECURE)
来保存安全状态的系统寄存器上下文。cm_el1_sysregs_context_restore(NON_SECURE)
恢复非安全状态的系统寄存器上下文。cm_set_next_eret_context(NON_SECURE)
设置下一异常等级的非安全 CPU 上下文。x0
为SMC_PREEMPTED
,返回到非安全状态。在yielding
类型 SMC 被抢占后,通过发出TSP_FID_RESUME
的SMC,非安全世界期望恢复TSP。TSPD 服务在收到此 SMC 后将采取以下操作:
cm_el1_sysregs_context_save(NON_SECURE)
保存非安全状态的系统寄存器上下文。cm_el1_sysregs_context_restore(SECURE)
恢复安全上下文。cm_set_next_eret_context(SECURE)
设置下一异常等级的安全 CPU 上下文。tspd_smc_handler()
返回安全的上下文指针cpu_context
。下图描述了当编译标志TSP_NS_INTR_ASYNC_PREEMPT
为 0 时,TSP/TSPD 如何处理在 TSP 执行期间生成且PSTATE.I
= 0 的非安全中断。
SP 应实现同步或者异步中断处理模型。
在同步模型中,在 SPD 服务跳转到入口点后,开始处理 Secure-EL1 中断。在处理中断之前,SP 应保存 Secure-EL1 系统寄存器上下文,例如SPSR_EL1
和ELR_EL1
,这些上下文在稍后 SP 恢复正常执行时需要。在完成处理中断后,SP 可以返回到中断发生的异常等级和安全状态,SP 应使用 SMC32 或 SMC64 来要求 SPD 服务来实现这一点。
在异步模型中,当PSTATE.I
和PSTATE.F
位为0时,Secure Payload 负责处理非安全和 Secure-EL1 中断。如前所述,当生成非安全中断时, SP 应与 SPD 服务协调,将控制权传递回最后已知异常等级中的非安全状态,这将允许在非安全状态下处理非安全中断。
TSPD 将 Secure-EL1 中断的控制权交给TSP的 tsp_sel1_intr_entry()
。TSP 处理中断,同时确保维护Test secure payload dispatcher behavior部分中描述的切换协议。它调用 tsp_update_sync_sel1_intr_stats()
更新一些统计数据,然后调用 tsp_common_int_handler()
。
plat_ic_get_pending_interrupt_id()
API来获取中断号。如果不是安全物理定时器中断,则意味着更高优先级的中断已抢占它。通过发出TSP_PREEMPTED
的SMC,调用tsp_handle_preemption()
将控制权移交回 EL3。plat_ic_acknowledge_interrupt()
API 确认安全定时器中断,调用 tsp_generic_timer_handler()
对安全物理通用定时器重新配置,以及调用plat_ic_end_of_interrupt()
API 发出中断处理结束信号。发出TSP_HANDLED_S_EL1_INTR
的SMC,TSP 将控制权传递回 TSPD 。
在异步模型下 TSP 处理中断如下:
tsp_common_int_handler()
函数来处理,上面已经描述了该功能。tsp_common_int_handler()
,最终调用tsp_handle_preemption()
并发出TSP_PREEMPTED
的SMC64。当 TSPD 响应来自非安全状态的SMC, 并将控制权移交给 TSP 时,执行将在该 SMC 指令之后恢复。对Secure payload的yielding
SMC调用可以被非安全中断抢占,并且执行可以返回到非安全世界来处理中断。在这种情况下,SMC 调用尚未完成,并且执行必须返回到secure payload以恢复抢占的 SMC 调用。这可以通过请求 SMC 调用,恢复被强占的 SMC 实现。
fast
类型的SMC无法被抢占,因此对于快速 SMC 调用不会发生这种情况。
在 Test Secure Payload 实现中,TSP_FID_RESUME
设计为恢复 SMC FID。值得注意的是,TSP_FID_RESUME
是一个 yielding
SMC,这意味着它也可以被抢占。假设处于非安全状态P.STATE.I=0
,请求yielding
SMC 的典型非安全软件流程如下:
int rc;
rc = smc(TSP_YIELD_SMC_FID, ...); /* Issue a Yielding SMC call */
/* The pending non-secure interrupt is handled by the interrupt handler
and returns back here. */
while (rc == SMC_PREEMPTED) { /* Check if the SMC call is preempted */
rc = smc(TSP_FID_RESUME); /* Issue resume SMC call */
}
TSP_YIELD_SMC_FID
是yielding
SMC ID,smc() 函数使用所需参数进行 SMC 调用。挂起的非安全中断会导致 IRQ 异常,进行非安全中断处理,并返回。检查 SMC 调用的返回值SMC_PREEMPTED
判断它是否被抢占。如果是,则发出恢复 SMC 调用TSP_FID_RESUME
,然后再次检查SMC调用的返回值,检查是否被抢占。这是递归完成的,直到 SMC 调用成功或失败。如果yielding
SMC 被抢占,则在使用TSP_FID_RESUME
SMC 恢复并完成之前,当前 TSPD 会阻止任何重新进入TSP的其他 SMC 调用,并返回SMC_UNK
错误。