文章目录

  • 1. ARMv8 IRQ/FIQ/SError路由流程图
  • 2. 中断分组
  • 3. 中断路由实例
  • 3.1 GICD_CTLR.DS=1, 假设中断A配置成Group 0,即中断A为FIQ
  • 3.2 GICD_CTLR.DS=1, 假设中断A配置成Group 1,即中断A为IRQ
  • 3.3 GICD_CTRL.DS=0, 假设中断A配置成`Non-secure Group 1`
  • 3.4 GICD_CTRL.DS=0, 假设中断A配置成`Secure Group 1`
  • 3.5 GICD_CTRL.DS=0, 假设中断A配置成`Group 0`
  • 4. VBAR
  • 5. Reference



1. ARMv8 IRQ/FIQ/SError路由流程图

arm架构 路由器 armv8路由器_GICv3

  • SCR_EL3.FIQ=1, FIQs are routed to EL3
  • SCR_EL3.FIQ=0, FIQs are routed to the First Exception Level(FEL) capable of handling interrupts
  • SCR_EL3.IRQ=1, IRQs are routed to EL3
  • SCR_EL3.IRQ=0, IRQs are routed to FEL
  • SCR_EL3.EA: SError interrupt routing(aarch64); External Abort interrupt routing(aarch32)
  • EA=1: routed to EL3
  • EA=0: routed to FEL
  • SCR_EL3.NS=1, EL0, EL1是non-secure state
  • SCR_EL3.NS=0, EL0, EL1是secure state

2. 中断分组

GICD_CTLR.DS=0,支持两种secure state:secure 和 non-secure,有3个group:

  • Group0,always Secure,EL3 handle
  • Secure Group1,Secure EL1 handle
  • Non-secure Group1,Non-secure EL1/Non-secure EL2 handle

GICD_CTLR.DS=1, 只支持一种secure state:secure 或者 non-secure,只有2个group:

  • Group0,always Secure,FIQ,EL3 handle
  • Group1,IRQ
    Secure Group1可以认为是划到了Group 0,Group 1是 Non-secure Group1.

当EL3是aarch64,GICD_CTLR.DS=0时,Group和EL的对应关系如下。

  • 所有的Group 0 interrupt都是FIQ
  • 所有的EL3 interrupt都是FIQ
  • Group 1的secure state与CPU所处的当前Exception Level的secure状态不一样,为FIQ,否则为IRQ

当EL是aarch64,GICD_CTLR.DS=1时,Group和EL的对应关系如下。

  • 所有的Group 0 interrupt都是FIQ
  • 所有的Group 1 interrupt都是IRQ

ARMv8中FIQ含义的变化:

FIQ is often reserved for secure interrupt sources. In earlier architecture versions, FIQ and IRQ were used to denote high and standard interrupt priority, but this is not the case in ARMv8-A (ARMv8 Programmer’s Guide)

3. 中断路由实例

以实际的系统举例,加深理解。

3.1 GICD_CTLR.DS=1, 假设中断A配置成Group 0,即中断A为FIQ

  1. CPU跑在ATF(EL3),中断A到来,因为此时ATF I/F bit是mask的,即中断是关闭的,所以中断A会pending,直至ATF处理完事情,跳回到linux kernel(Non secure EL1),发现中断A是Group0,路由到EL3(SCR_EL3.FIQ=1),从VBAR_EL3中找到EL3 vector table,跳到此vector table去执行

ATF在run的过程中I/F被HW自动mask.
When the processor takes an exception to AArch64 execution state, all of the PSTATE interrupt masks is set automatically. This means that further exceptions are disabled. If software is to support nested exceptions, for example, to allow a higher priority interrupt to interrupt the handling of a lower priority source, then software needs to explicitly re-enable interrupts.

  1. CPU跑在Linux Kernel(Non-secure EL1),中断A到来,发现中断A是Group0,路由到EL3(SCR_EL3.FIQ=1),从VBAR_EL3中找到EL3 vector table,跳到此vector table去执行
  2. CPU跑在TEEOS(Secure EL1),中断A到来,发现中断A是Group0,路由到EL3,从VBAR_EL3中找到EL3 vector table,跳到此vector table去执行

3.2 GICD_CTLR.DS=1, 假设中断A配置成Group 1,即中断A为IRQ

  1. CPU跑在ATF(EL3),中断A到来,因为此时ATF I/F bit是mask的,即中断是关闭的,所以中断A会pending,直至ATF处理完事情,跳回到linux kernel(Non secure EL1),再处理此中断
  2. CPU跑在Linux Kernel(Non-secure EL1),中断A到来,直接处理
  3. CPU跑在TEEOS(Secure EL1),中断A到来,进入TEEOS VBAR_EL1所指向的 irq vector table,经SMC跳到ATF(EL3),返回linux kernel处理该中断,处理完之后再SMC到ATF,最终返回到TEEOS

3.3 GICD_CTRL.DS=0, 假设中断A配置成Non-secure Group 1

  1. CPU 跑在ATF(EL3)时,中断A到来,作为FIQ处理,因为此时ATF I/F bit是mask的,中断会pending, 等从ATF跳回linux kernel (Non-secure EL1)再处理
  2. CPU跑在linux kernel(non secure EL1)时,中断A到来,就直接作为IRQ处理
  3. CPU在跑TEEOS(secure EL1)时,中断A到来,作为FIQ处理,进入TEEOS的VBAR_EL1所指向的 fiq vetor table, 经SMC跳到ATF(EL3),返回linux kernel处理该中断,处理完之后再SMC到ATF,最终再返回到TEEOS
    下图来自atf interrupt framework design. 有助于很好的理解第3种情形。
    TSP是Test Secure Payload,可以理解为TEEOS,CSS是Current Secure State,TEL3是Target Exception Level 3.

arm架构 路由器 armv8路由器_arm架构 路由器_02

下图也不错
.

arm架构 路由器 armv8路由器_arm架构 路由器_03

EL3在此过程中只是forward interrupt 的作用。

TEEOS 收到 non-secure group1 interrupt,进入TEEOS fiq handler 处理,TEEOS fiq handler会调用SMC进入ATF(EL3),ATF(EL3)设置non secure cpu context, 然后eret跳到 Linux Kernel(non-secure EL1),跳到Linux Kernel后,会立马再次触发一次IRQ,进入Kernel IRQ entry 进行处理,处理完之后回到的是Linux kernel 之前跳到 TEEOS 的位置,如果TEEOS是optee,回到的位置是 optee_do_call_with_arg()drivers/tee/optee/call.c),然后再次执行SMC进入ATF(EL3),eret跳到TEEOS的optee_vectors->fast_smc_entry继续执行。可参考optee运行时来了一个REE(linux)中断–代码导读

上图中步骤③是从ATF切换到Linux Kernel,为何步骤④又产生了一次IRQ呢?

在CPU interfaces (ICC_*_ELn)寄存器的描述中:

• Provide general control and configuration to enable interrupt handling
• Acknowledge an interrupt
Perform a priority drop and deactivation of interruptsSet an interrupt priority mask for the PE • Define the preemption policy for the PE
• Determine the highest priority pending interrupt for the PE

也就是cpu interface掌管着中断优先级和将IRQ/FIQ发送给ARM Core.

以Level sensitive interrupts的中断为例,先不考虑active and pending的情况:CPU interface发送给Core后,中断状态变为pending,当Core感知到中断后(PE跳转到中断向量表)中断状态变为active,当中断退出后,CPU interface会再次将优先级最高的中断发送给Core,Core处理下一个中断。

arm架构 路由器 armv8路由器_arm架构 路由器_04


再看下中断的退出流程( End of interrupt), 中断的退出有两种方式:

  • Priority drop 将中断优先级降到中断产生之前的值
  • Deactivation 将中断从active变成inactive

重点来了,在中断退出的时候,软件中一般会有Priority drop和Deactivation,既要么将中断优先级降低,要么将中断变为inactive,那么中断退出之后,CPU interface感知到的优先级最高的中断,就可能不会是此中断了。

再看下上述的中断流程举例,在TEE中,cpu interface发了一个FIQ给Core,跳转到TEE OS的FIQ向量表,在FIQ的处理流程中,软件几乎什么都没干,没有Priority drop和Deactivation, 那么当SMC切换到了EL3之后,又退回Linux Kernel后,CPU interface感知到上一个中断处理完成,会再次发送下一个优先级最高的中断,由于之前的中断号的优先级没变,此时基本上依然是最高的优先级。此时CPU interface会再次发送该中断给Core,由于SCR.NS发生了变化,此时CPU interface发送给Core的就变成了IRQ.

3.4 GICD_CTRL.DS=0, 假设中断A配置成Secure Group 1

  1. CPU跑在ATF(EL3)时,中断A到来,作为FIQ处理,因为此时ATF I/F bit是mask的,中断会pending, 等从ATF跳回Linux kernel或TEEOS再处理A
  2. CPU跑在TEEOS,中断A到来,直接作为IRQ处理
  3. CPU跑在Linux Kernel(non secure EL1),中断A到来,作为FIQ处理,路由到ATF(EL3)处理(SCR.FIQ=1),ATF判断此中断需TEEOS处理,会跳到TEEOS 的fiq vector table进行处理,处理完后再依次返回到ATF,Linux Kernel

arm架构 路由器 armv8路由器_Group_05

ATF fiq handler 里先调用plat_ic_get_pending_interrupt_typeICC_HPPIR0_EL1register 获取INTID,根据INTID可以判断中断是属于哪一种类型。

  • 如果INTID是1020,则说明是G1S(Secure Group 1)中断
  • 如果INTID是1021,则说明是G1NS(Non-Secure Group 1)中断
    此时的INTID为1020,调用get_interrupt_type_handler获取注册了的interrupt handler。以 opteed spd 为例,optee初始化完成后,通过SMC跳进ATF,在SMC handler 里注册了Secure EL1 interrupt handler.
static uintptr_t opteed_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)
{
	...

	/*
	 * OPTEE has finished initialising itself after a cold boot
	 */
	case TEESMC_OPTEED_RETURN_ENTRY_DONE:
			set_interrupt_rm_flag(flags, NON_SECURE);
			rc = register_interrupt_type_handler(INTR_TYPE_S_EL1,
						opteed_sel1_interrupt_handler,
						flags);
	...
}

opteed_sel1_interrupt_handler得到执行。通过eret指令跳到TEEOS的optee_vector_table->fiq_entry去处理。

/*******************************************************************************
 * This function is the handler registered for S-EL1 interrupts by the
 * OPTEED. It validates the interrupt and upon success arranges entry into
 * the OPTEE at 'optee_fiq_entry()' for handling the interrupt.
 ******************************************************************************/
static uint64_t opteed_sel1_interrupt_handler(uint32_t id,
					    uint32_t flags,
					    void *handle,
					    void *cookie)
{
	uint32_t linear_id;
	optee_context_t *optee_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 OPTEE */
	cm_el1_sysregs_context_save(NON_SECURE);

	/* Get a reference to this cpu's OPTEE context */
	linear_id = plat_my_core_pos();
	optee_ctx = &opteed_sp_context[linear_id];
	assert(&optee_ctx->cpu_ctx == cm_get_context(SECURE));

	cm_set_elr_el3(SECURE, (uint64_t)&optee_vector_table->fiq_entry);
	cm_el1_sysregs_context_restore(SECURE);
	cm_set_next_eret_context(SECURE);

	/*
	 * Tell the OPTEE that it has to handle an FIQ (synchronously).
	 * Also the instruction in normal world where the interrupt was
	 * generated is passed for debugging purposes. It is safe to
	 * retrieve this address from ELR_EL3 as the secure context will
	 * not take effect until el3_exit().
	 */
	SMC_RET1(&optee_ctx->cpu_ctx, read_elr_el3());
}

3.5 GICD_CTRL.DS=0, 假设中断A配置成Group 0

  1. CPU跑在ATF时 ,中断A到来,作为FIQ处理,因为此时ATF I/F bit是mask的,中断会pending, 等从ATF跳回Linux kernel,发现此中断是EL3 FIQ,就从VBAR_EL3找到EL3的vector table,到此vector table去执行
  2. CPU如果是跑在kernel(non secure EL1),中断A到来,作为FIQ处理,跳到ATF vector table执行
  3. 如果CPU跑在 TEEOS(secure EL1),中断A到来,作为FIQ处理,跳到ATF vector table执行

4. VBAR

VBAR: Vector Base Address Register,异常向量表的基地址是保存在VBAR register中的。
关于VBAR,补充一个需要注意的地方:
AArch64 VBAR 有 VBAR_EL1, VBAR_EL2, VBAR_EL3.
TEEOS(Secure EL1)和Linux kernel(Non-secure EL1)都是用VBAR_EL1,因此在 secure 状态切换时,需要重新设置 VBAR_EL1.