[答疑]-中断流程举例:在REE(SCR.FIQ=1)侧时产生了FIQ,跳转到EL3后做了哪些事情?

快速链接:
.
???????????? 个人博客笔记导读目录(全部) ????????????

中断流程举例的章节中,图中第1步骤中,给REE的SCR.FIQ=1, 在normal EL0/EL1或EL3时来了一个secure group1的中断,该中断将会被标记为FIQ,target到EL3,然后在EL3的软件逻辑,会将CPU切换到TEE进行处理…
[答疑]-中断流程举例:在REE(SCR.FIQ=1)侧时产生了FIQ,跳转到EL3后做了哪些事情?

以上的流程,最近又被小伙伴们问起,那么我们贴下相关文档和代码看下吧。

中断为何被标记FIQ?
看gicv3的官方文档,可知当cpu处于non-secure EL0/1/2时,来了一个secure group1中断,该中断将被标记为FIQ;当cpu处于EL3时,来了一个secure group1中断,也是被标记为FIQ
[答疑]-中断流程举例:在REE(SCR.FIQ=1)侧时产生了FIQ,跳转到EL3后做了哪些事情?
那么中断为什么target到EL3?
从armv8的官方文档中的SCR_EL3寄存器中,可以看到,当SCR_EL3.FIQ=1时,来了一个FIQ,该FIQ会直接target到EL3。
[答疑]-中断流程举例:在REE(SCR.FIQ=1)侧时产生了FIQ,跳转到EL3后做了哪些事情?
或者去看armv8文档中的rounting表也行,由下图可以看到,当SCR寄存器的EA或IRQ或FIQ等于1时,这个配置下,cpu在EL0/EL1/EL3产生的EA/IRQ/FIQ异常都将直接target到EL3
[答疑]-中断流程举例:在REE(SCR.FIQ=1)侧时产生了FIQ,跳转到EL3后做了哪些事情?
那么FIQ target到EL3之后,意味着什么?
意味着CPU将跳转到VBAR_EL3 + offset_fiq, 我们知道向量表有4个FIQ,那么跳转到哪一个呢?如下图所示,标出了cpu在EL3时来的FIQ、cpu在EL0/EL1时来的FIQ跳转的偏移
[答疑]-中断流程举例:在REE(SCR.FIQ=1)侧时产生了FIQ,跳转到EL3后做了哪些事情?

硬件介绍完了,开始撸代码

//这一段一段未实现,猜测,cpu在EL3时,PSTATE.F是disabled,中断不会被PE acknowledged,中断pendind状态
[思考]-ATF中异常向量表为何没有实现“Current Exception level with SP_ELx, x>0.“

	.align	7
fiq_sp_elx:
	bl	report_unhandled_interrupt   
	check_vector_size fiq_sp_elx

这一段,是cpu在EL0/1时,产生的target到EL3的FIQ的向量表偏移

	.align	7
fiq_aarch64:
	handle_interrupt_exception fiq_aarch64
	check_vector_size fiq_aarch64

handle_interrupt_exception的具体实现如下

	.macro	handle_interrupt_exception label
	/* Enable the SError interrupt */
	msr	daifclr, #DAIF_ABT_BIT

	str	x30, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_LR]
	bl	save_gp_registers

	/*
	 * Save the EL3 system registers needed to return from
	 * this exception.
	 */
	mrs	x0, spsr_el3
	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]
	mov	x20, sp
	msr	spsel, #0
	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
	cmp	x0, #INTR_TYPE_INVAL
	b.eq	interrupt_exit_\label

	/*
	 * Get the registered handler for this interrupt type. A
	 * NULL return value could be 'cause of the following
	 * conditions:
	 *
	 * a. An interrupt of a type was routed correctly but a
	 *    handler for its type was not registered.
	 *
	 * b. An interrupt of a type was not routed correctly so
	 *    a handler for its type was not registered.
	 *
	 * c. An interrupt of a type was routed correctly to EL3,
	 *    but was deasserted before its pending state could
	 *    be read. Another interrupt of a different type pended
	 *    at the same time and its type was reported as pending
	 *    instead. However, a handler for this type was not
	 *    registered.
	 *
	 * a. and b. can only happen due to a programming error.
	 * The occurrence of c. could be beyond the control of
	 * Trusted Firmware. It makes sense to return from this
	 * exception instead of reporting an error.
	 */
	bl	get_interrupt_type_handler
	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

剖析下上述代码,在get_interrupt_type_handler,其实是获取SPD(Secure Payload Dispatcher,何为SPD,请点击这里)初始化时注册的中断。
获取到中断handler后,handler地址返回给X0,又拷贝给X21,最后又blr x21,跳转到handler函数

	bl	get_interrupt_type_handler
	cbz	x0, interrupt_exit_\label
	mov	x21, x0

	/* Call the interrupt type handler */
	blr	x21

那么我们在看看handler函数里都干了什么,我的代码环境使用的optee, 我们就看下SPD=opteed的情况,在ATF代码的opteed_main.c中,如下代码所示,其实就是保存下non-secure寄存器,恢复secure寄存器,然后跳转到secure的线程向量表中。
注意这个线程向量表optee_vectors是在optee中软件定义的,然后又将物理地址传到了ATF的全局变量中。

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_vectors->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());
}

至此,你以为讲完了吗,
再补充一点在gicv3文档中1020和1021中断号的描述

[答疑]-中断流程举例:在REE(SCR.FIQ=1)侧时产生了FIQ,跳转到EL3后做了哪些事情?
我们应该会用到1020,那么用在哪里的呢?请看上述汇编代码bl plat_ic_get_pending_interrupt_type的具体实现:

uint32_t plat_ic_get_pending_interrupt_type(void)
{
	unsigned int irqnr;

	assert(IS_IN_EL3());
	irqnr = gicv3_get_pending_interrupt_type();

	switch (irqnr) {
	case PENDING_G1S_INTID:
		return INTR_TYPE_S_EL1;
	case PENDING_G1NS_INTID:
		return INTR_TYPE_NS;
	case GIC_SPURIOUS_INTERRUPT:
		return INTR_TYPE_INVAL;
	default:
		return INTR_TYPE_EL3;
	}
}

其实就是在读取pending的中断号,看看有没有1020或1021,从而获得此次的中断是从secure或non-secure过来的,还是在EL3产生的。然后走相应的逻辑。

上一篇:Linux档案权限篇之一


下一篇:[architecture]-ARM Core中与中断相关的寄存器