atf源码篇(七):docs文件夹-components组件(6)异常处理框架_摸肚子的小胖子的博客-爱代码爱编程
前面首先了解EL3大概都有些什么东西,以及这些东西是是干嘛的。这里你还有映像吗?
这里来看看异常处理框架。嘤嘤嘤,这里看到了老师考我的SMC是什么异常,我怎么没有早点看到,made,损失大了。
6、异常处理-Exception Handling Framework
本文档描述了针对EL3而非SMC的运行时固件(BL31)处理异常的各个方面。针对EL3时,|EHF|会处理以下异常:
(EL3中的SMC只是其中的一部分)
中断
同步外部中止
异步外部中止
|TF-A|对从较低EL引发的同步SMC异常的处理:ref:`固件设计文档<处理SMC>中描述。
但是,|EHF|改变了除SMC之外的中断处理和ref:同步异常<对SMC调用的影响>
的语义。
通过将构建选项EL3_EXCEPTION_HANDLING设置为1来选择|EHF|,该选项仅适用于AArch64系统。
SMC只是EHF的一个部分
1、介绍
通过SCR_EL3寄存器中的各种控制位,Arm体系结构允许异步异常路由到EL3。
如:ref:`中断管理框架’文档中所述,根据所选的中断路由模型,TF-A适当地设置SCR_EL3寄存器的FIQ和IRQ位以实现此路由。对于大多数用例,除了为了促进正常和安全世界之间的上下文切换,路由到EL3的FIQ和IRQ不需要在EL3中处理。
然而,不断发展的系统和标准要求EL3针对并处理各种异常。例如:
-
从ARMv8.2架构扩展开始,Arm架构中引入了许多RAS功能。随着RAS功能的实现,系统的各个组件可以使用其中一个异步异常向PE发送错误情况信号。这些错误情况具有关键性,必须尽早采取纠正或补救措施。因此,通常遵循固件优先处理方法来响应系统中的RAS事件。
-
Arm SDEI规范定义了正常世界与运行时固件交互以请求系统事件通知的接口。|SDEI|规范要求即使在正常世界执行时屏蔽异常,也要通知这些事件。这也意味着需要固件优先处理,其中事件首先由EL3固件接收,然后通过纯软件机制发送到Normal world。
对于|TF-A|,固件优先处理意味着异步异常被适当地路由到EL3,并且运行时固件(BL31)被扩展为包括能够处理以EL3为目标的那些异常的软件组件。这些组件通常被称为调度程序[1],可以选择:
- 完全在EL3中接收和处理异常,这意味着异常处理在EL3终止。
- 接收异常,但在EL3中处理部分异常,并将其余处理委托给在较低安全ELs上运行的专用软件堆栈。在该方案中,处理跨越各种安全EL。
- 接收异常,但在EL3中处理部分异常,并将错误处理委托给在较低安全ELs上运行的专用软件堆栈(如上所述);此外,正常世界也可能被要求参与处理,或被通知此类事件(例如,作为|SDEI|事件)。在该方案中,异常处理可能并最大限度地跨越安全和正常世界中的所有EL。
在任何给定的系统上,根据平台选择和接收到的异常的性质,可以独立地使用所有上述处理模型。
不要与:ref:`Secure Payload Dispatcher<firmware_design_sel1_spd>`混淆,**它是一个代表Secure OS在EL3中运行的EL3组件。**
2、异常处理框架的作用
与上述用例类似,|EHF|的主要作用是促进固件优先处理Arm系统上的异常。因此,|EHF|使运行时固件中的多个异常调度器能够共存、注册和处理针对EL3的异常。本节概述了基本知识,本文档的其余部分扩展了|EHF||的各个方面。(多个)
为了仲裁调度程序之间的异常处理,|EHF|操作基于优先级方案。这个优先级方案与Arm GIC架构如何定义它密切相关,尽管它也适用于非中断异常(例如SErrors)。
平台需要将安全优先级空间划分为适用于安全软件堆栈的优先级级别。然后,它将调度员分配到一个或多个优先级。然后,调度程序在运行时为优先级注册处理程序。调度器可以为多个优先级注册处理程序。
当处于该优先级的处理程序当前正在EL3中执行,或已将执行委托给较低的EL时,该优先级处于活动状态。
对于中断,当在EL3中针对并确认中断时,这是隐式的,并且确认的中断的优先级用于匹配其注册的处理程序。当中断处理通过结束中断而结束时,优先级同样被隐式停用。
非中断异常(例如SErrors)没有优先级的概念。为了使优先级仲裁工作,|EHF|提供了API,以便这些非中断异常具有优先级,并与中断交互工作。因此,处理此类异常的调度员必须在处理或委托时显式激活和停用相应的优先级。
因为中断处理的优先级激活和去激活是隐式的,并且涉及GIC优先级屏蔽,所以较低优先级的中断不可能抢占较高优先级的中断。扩展而言,这意味着较低优先级的调度器不能抢占较高优先级的调度程序。然而,非中断异常的优先级激活和停用必须是明确的。因此,当较高优先级处于活动状态时,|EHF|不允许激活较低优先级,这将导致panic。同样,如果在较高优先级处于活动状态时尝试停用较低优先级,则会导致panic。
从本质上讲,优先级激活和停用在概念上就像是以严格递增的方式堆叠优先级,并且需要严格按照相反的顺序来解除堆叠。对于中断,GIC确保情况如此;对于非中断,|EHF|监视并断言这一点。请参见优先级转换。
中断处理
|EHF|是中断管理框架的客户端,并为目标EL3的中断注册顶级处理程序,如:ref:Interrupt Management Framework
文档中所述。这具有以下含义:
-
在GICv3系统上,当在S-EL1中执行时,具有足够优先级的未决非安全中断以FIQ的形式发出信号,因此将被路由到EL3。因此,S-EL1软件无法处理S-EL1处的非安全中断。本质上,这不赞成描述为:ref:
CSS=0,TEL3=0<EL3 interrupts>
的路由模式。
为了使S-EL1软件在启用|EHF|的情况下处理非安全中断,调度器必须采用这样一种模型,即在EL3处接收非安全中断但随后:ref:同步<sp同步int>
处理给S-EL1。 -
在GICv2系统上,需要将构建选项GICv2_G0_FOR_EL3设置为1,以便组0中断目标EL3。
-
在安全世界中执行时,|EHF|将GIC优先级掩码寄存器设置为最低的安全优先级。这意味着没有非安全中断可以抢占安全执行。有关详细信息,请参见对SMC呼叫的影响。
如上所述,使用|EHF|,平台需要将组0中断划分为不同的优先级。选择接收中断的调度器可以拥有一个或多个优先级,并为它们注册中断处理程序。给定的优先级只能分配给一个处理程序。调度器可以注册多个优先级。
通过两个步骤为调度器分配中断优先级:
-
分区优先级
中断通过分组和将中断分配给优先级的方式与调度器相关联。换言之,针对特定调度器的所有中断都应该处于特定的优先级。对于优先级分配:- Arm GIC架构允许的8位优先级中,第7位必须为0(安全空间)。
- 根据要支持的调度器的数量,平台必须选择使用剩余7位中的前n位来识别中断并将中断分配给各个调度器。选择n位最多支持2n个不同的调度器。例如,通过选择2个附加位(即位6和位5),平台可以划分为4个安全优先级范围:0x0、0x20、0x40和0x60。参见中断处理示例。
笔记
Arm GIC架构要求支持两个安全状态的GIC实现必须实现至少32个优先级;i、 例如,8位中的至少5个高位是可写的。在上述方案中,当为优先级范围分配选择n位时,平台必须确保GIC优先级的至少n+1个高位是可写的。
因此分配给中断的优先级也用于确定较低EL中委托执行的优先级。较低EL中的委托执行与ehf_activate_priority()API(稍后描述)选择的优先级相关联。所选择的优先级还确定在较低EL中执行时屏蔽的中断,因此控制委托执行的抢占。
平台通过声明优先级描述符数组来表示所选的优先级。数组中的每个条目都是ehf_pri_desc_t类型,并声明了一个优先级,并且应由ehf_pri_desc()宏填充。
警告
宏EHF_PRI_DESC()在数组中的计算索引处安装描述符,而不必在数组中放置宏的位置。因此,阵列的大小可能比它看起来的大。因此,应使用ARRAY_SIZE()宏来确定数组的大小。
最后,这个描述符数组通过EHF_REGISTER_PRIORITIES()宏暴露给|EHF|。
有关用法,请参阅中断处理示例。另请参见:中断优先级考虑。
-
编程优先级
分区优先级中的文本仅描述平台如何表示所需的优先级。然而,它不会选择中断,也不会在GIC中编程所需的优先级。
:ref:固件设计指南<配置安全中断>
解释了配置安全中断的方法|EHF|要求平台枚举安全中断的中断属性(而不仅仅是数字)。安全中断的优先级必须与上面的分区优先级部分中确定的优先级相匹配。
请参阅限制,并参阅中断处理示例以获取说明。
注册handler
调度程序通过以下API为其优先级注册处理程序:
int ehf_register_priority_handler(int pri, ehf_handler_t handler)
这个API有两个参数:
正在注册处理程序的优先级;
要注册的处理程序。处理程序必须对齐到4个字节。
如果调度器拥有多个优先级,则必须为每个优先级调用API。
只有在以下情况下,API才会成功并返回0:
- 存在具有请求的优先级的描述符。
- 之前调用API时没有注册任何处理程序。
否则,API返回-1。
中断处理程序应具有以下签名:
typedef int (*ehf_handler_t)(uint32_t intr_raw, uint32_t flags, void *handle,
void *cookie);
这些参数是从这里可以获得:ref:EL3 interrupt handler <el3-runtime-firmware>
.
The :ref:SDEI dispatcher<SDEI: Software Delegated Exception Interface>
, 例如, 希望平台分配两个不同的优先级-PLAT_SDEI_CRITICAL_PRI和PLAT_SDEI_NORMAL_PRI-并注册相同的处理程序来处理这两个级别。
中断处理函数栗子
以下带注·释的片段演示了平台如何选择将中断分配给虚构的调度器:
#include <common/interrupt_props.h>
#include <drivers/arm/gic_common.h>
#include <exception_mgmt.h>
...
/*
* This platform uses 2 bits for interrupt association. In total, 3 upper
* bits are in use.
*
* 7 6 5 3 0
* .-.-.-.----------.
* |0|b|b| ..0.. |
* '-'-'-'----------'
*/
#define PLAT_PRI_BITS 2
/* Priorities for individual dispatchers */
#define DISP0_PRIO 0x00 /* Not used */
#define DISP1_PRIO 0x20
#define DISP2_PRIO 0x40
#define DISP3_PRIO 0x60
/* Install priority level descriptors for each dispatcher */
ehf_pri_desc_t plat_exceptions[] = {
EHF_PRI_DESC(PLAT_PRI_BITS, DISP1_PRIO),
EHF_PRI_DESC(PLAT_PRI_BITS, DISP2_PRIO),
EHF_PRI_DESC(PLAT_PRI_BITS, DISP3_PRIO),
};
/* Expose priority descriptors to Exception Handling Framework */
EHF_REGISTER_PRIORITIES(plat_exceptions, ARRAY_SIZE(plat_exceptions),
PLAT_PRI_BITS);
...
/* List interrupt properties for GIC driver. All interrupts target EL3 */
const interrupt_prop_t plat_interrupts[] = {
/* Dispatcher 1 owns interrupts d1_0 and d1_1, so assigns priority DISP1_PRIO */
INTR_PROP_DESC(d1_0, DISP1_PRIO, INTR_TYPE_EL3, GIC_INTR_CFG_LEVEL),
INTR_PROP_DESC(d1_1, DISP1_PRIO, INTR_TYPE_EL3, GIC_INTR_CFG_LEVEL),
/* Dispatcher 2 owns interrupts d2_0 and d2_1, so assigns priority DISP2_PRIO */
INTR_PROP_DESC(d2_0, DISP2_PRIO, INTR_TYPE_EL3, GIC_INTR_CFG_LEVEL),
INTR_PROP_DESC(d2_1, DISP2_PRIO, INTR_TYPE_EL3, GIC_INTR_CFG_LEVEL),
/* Dispatcher 3 owns interrupts d3_0 and d3_1, so assigns priority DISP3_PRIO */
INTR_PROP_DESC(d3_0, DISP3_PRIO, INTR_TYPE_EL3, GIC_INTR_CFG_LEVEL),
INTR_PROP_DESC(d3_1, DISP3_PRIO, INTR_TYPE_EL3, GIC_INTR_CFG_LEVEL),
};
...
/* Dispatcher 1 registers its handler */
ehf_register_priority_handler(DISP1_PRIO, disp1_handler);
/* Dispatcher 2 registers its handler */
ehf_register_priority_handler(DISP2_PRIO, disp2_handler);
/* Dispatcher 3 registers its handler */
ehf_register_priority_handler(DISP3_PRIO, disp3_handler);
...
另请参见构建时流和运行时流。
激活和停用优先级
当处理该优先级的异常时,该优先级被称为活动:对于中断,当中断被确认时,这意味着;对于非中断异常,如SErrors或:ref:SDEI显式调度<显式事件调度>
,这必须通过调用ehf_activate_priority()来完成。请参见运行时流。
相反,当调度器对异常原因达成逻辑解决方案时,应停用相应的优先级。如上所述,对于中断,当中断是GIC中的EOId时,这是隐含的;对于其他例外情况,这必须通过调用ehf_deactivate_priority()来完成。
由于异常委托的不同规定,可能存在多个停用工作流:
-
调度员已经解决了异常的原因,并决定不再采取进一步的行动。在这种情况下,调度器的处理程序在返回|EHF|之前停用优先级。运行时固件在通过ERET退出时,在中断发生之前恢复执行。
-
调度员必须将执行委托给下级EL,只有当下级EL在未来某个时间点(通过SMC)返回完整的信号时,才能认为异常原因已解决。接下来是以下顺序:
- 调度器调用setjmp()来设置跳转点,并安排在下一次ERET时输入较低的EL。
- 通过随后的运行时固件的ERET,将执行委托给较低的EL。 下部EL完成其执行,并通过SMC发出完成信号。
- SMC由先前处理异常的同一调度程序处理。注意到异常处理的结束,调度器执行longjmp()以恢复到上一个跳转点之外。
如上所述,|EHF|提供了以下用于激活和停用中断的API:
- ehf-activate_priority()激活提供的优先级,但仅当当前活动优先级高于给定优先级时;否则就会恐慌。此外,为了防止较低优先级的物理中断造成的中断,|EHF|将与PE对应的优先级掩码寄存器编程为激活的优先级。调度器通常只需要在处理除中断以外的异常时调用此函数,并且需要将执行委托给具有所需优先级的较低EL。
- ehf-deactive_priority()停用给定优先级,但仅当当前活动优先级等于给定优先级时;否则就会恐慌|EHF|还将PE对应的优先级掩码寄存器恢复为调用EHF_activate_Priority()之前的优先级。调度器通常只需要在处理异常(而不是中断)之后调用此函数。
API的调用受允许的转换限制。另请参见运行时流程。
优先级别的转换
可以调用|EHF|API ehf_activate_priority()和ehf_desactive_prioriy()来转换PE上的当前优先级。对这些API的给定调用序列受以下条件限制:
- 对于激活,|EHF|只允许优先级增加(即数值减少);
- 对于停用,|EHF|只允许优先级降低(即数值增加)。此外,需要停用的优先级为当前优先级。
如果违反这些规定,将导致恐慌。
对SMC调用的影响
通常,安全执行被认为比非安全执行更重要。如本文档其他部分所述,EL3执行以及此后的任何委托执行都会通过确认安全中断或当调度器调用ehf_activate_priority()时隐式提高GIC的优先级掩码。
因此,非安全中断不能抢占任何安全执行。
**来自非安全世界的SMC是同步例外,是非安全世界请求安全服务的机制。**它们大致分为fast或yielding(见SMCCC)。
-
从调用者的角度来看,快速SMC是原子的。一、 例如,只有当安全世界完成服务请求时,它们才返回给调用者。同时挂起的任何非安全中断都不能抢先安全执行。
-
yielding的SMC**具有可抢占、低优先级请求的语义。挂起的非安全中断可以抢占处理屈服SMC的安全执行。**一、 例如,当以下任一情况发生时,呼叫者可能会观察到yielding SMC返回:
- Secure world完成请求,调用者将找到SMC_OK作为返回代码。
- 非安全中断优先于安全执行。非安全中断被处理,非安全执行在SMC指令后恢复。
处理yielding SMC的调度员必须向非安全调用者提供不同的返回代码,以区分后一种情况。然而,此返回代码不是标准化的(例如,与SMC_UNKNOWN或SMC_OK不同),因此处理请求的调度器会有所不同。
对于上面的后一种情况,|EHF|之前的调度器预期非安全中断将被带到S-EL1[2],因此将有机会在返回非安全世界之前填充指定的抢占错误代码。
|EHF|的引入改变了中断处理中描述的行为。
当启用|EHF|时,为了允许非安全中断抢占yielding SMC处理,调度器必须调用ehf_allow_ns_pemption()API。API接受一个参数,即在被抢占时返回给非安全世界的错误代码。
在GICv2的情况下,S-EL1中的非安全中断被信号通知为IRQ,在GICv3的情况下则为FIQ。
构建时间流
请参考上图。
构建时流程包括以下步骤:
- 平台通过为各个调度程序安装优先级描述符来分配优先级,如分区优先级中所述。
- 平台为GIC驱动程序提供中断属性,如编程优先级中所述。
- 调度器调用ehf_register_priority_handler()来注册中断处理程序。
另请参阅中断处理示例。
运行时间流
以下是中断的示例流程:
- GIC驱动程序在初始化期间迭代平台提供的中断属性(参见编程优先级),并配置中断。这对属于不同调度程序的中断编程适当的优先级和组(组0)。
-
|EHF|在初始化期间,为el3中断使用:ref:`中断管理框架<el3运行时固件>`注册顶级中断处理程序。这也导致在SCR_EL3中设置路由比特。
-
当属于调度器的中断触发时,GIC引发EL3/组0中断,并被带到EL3。
- 顶级EL3中断处理程序执行。处理程序确认中断,读取其运行优先级,并据此确定调度程序处理程序。
- |EHF|将PE的优先级掩码寄存器编程为接收到的中断的优先级。 |EHF|将该优先级标记为活动,并跳转到调度程序处理程序。
- 一旦调度程序处理程序完成其工作,它必须在返回|EHF|之前立即停用优先级。请参见停用工作流。
以下是针对EL3而非中断的异常的示例流程:
- 平台为特定类型的异常提供处理程序。
- 异常到达,并执行相应的处理程序。
- 处理程序调用ehf_activate_priority()来激活所需的优先级。这还具有提高GIC优先级掩码的效果,从而防止优先级较低的中断抢占处理。处理程序可以选择完全在EL3中进行处理,或者委托给较低的EL。
- 异常处理结束后,处理程序调用ehf_desactive_priority()来停用先前激活的优先级。这也有将GIC优先级掩码降低到以前的效果。(所以这个掩码就是和优先级有关系)
中断优先级考虑
-
GIC优先级方案在设计上优先考虑安全中断,而不是普通中断。平台还通过|EHF|在安全调度员之间分配相对优先级。
-
如分区优先级中所述,针对不同调度程序的中断具有不同的优先级。因为它们是通过GIC路由的,所以向PE的中断传递受GIC优先级规则的约束。特别地,当PE正在处理中断(即,中断处于活动状态)时,即使相同或较低优先级的中断挂起,也仅向PE发送较高优先级的中断。这有一个副作用,即由于另一个调度器处理其(更高优先级)的中断,一个调制器缺少中断。
-
|EHF|不强制执行特定的优先级策略,但平台应仔细考虑将优先级分配给集成到运行时固件中的调度程序。平台应根据不同调度员的性质,合理划分其优先级。特别是,关键性质的调度员(例如RAS)应被分配比其他调度员更高的优先级(例如|SDEI|);并且在|SDEI|内,关键优先级|SDEI应被分配比正常优先级更高的优先级。
SDEI
Software Delegated Exception Interface-软件委托异常接口
RAS
Reliability, Availability, and Serviceability extensions. A mandatory extension for the Armv8.2 architecture and later. An optional extension to the base Armv8 architecture.可靠性、可用性和可维护性扩展。Armv8.2架构和更高版本的强制扩展。基本Armv8架构的可选扩展。
Limitations-限制
EHF有如下的限制
- 尽管GIC优先级方案支持多达128个安全调度程序,但EHF_REGISTER_PRIORITIES()宏所公开的描述符数组的大小目前限制为32。这适用于大多数预期的用例。如果用例需要,这可能会在未来扩展。
- 平台必须确保异常描述符中分配给调度器的优先级与调度器处理的中断的编程优先级匹配。|EHF|无法验证是否遵循了此操作。
7、fconf/索引
固件更新-firmware-update
measured_boot/index
每毫米英里
平台中断控制器API
ras公司
romlib设计
sdei公司
安全分区管理器
安全分区管理器mm
xlat-tables-lib-v2-设计
胶布装订
领域管理扩展
颗粒保护台设计