ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

optee3.14中的异常向量表解读--中断处理解读

2021-09-12 09:59:58  阅读:368  来源: 互联网

标签:fiq -- irq 解读 elx serror el0 el1 optee3.14


optee3.14中的异常向量表、VBAR_EL1、中断实现的介绍

★★★ 个人博客导读首页—点击此处 ★★★
.
说明:
在默认情况下,本文讲述的都是ARMV8-aarch64架构,optee3.14版本

文章目录

1、armv8-aarch64的异常向量表介绍

在这里插入图片描述
我们可以看出,实际上有四组表,每组表有四个异常入口,分别对应同步异常,IRQ,FIQ和serror。

  • 如果发生异常后并没有exception level切换,并且发生异常之前使用的栈指针是SP_EL0,那么使用第一组异常向量表。
  • 如果发生异常后并没有exception level切换,并且发生异常之前使用的栈指针是SP_EL1/2/3,那么使用第二组异常向量表。
  • 如果发生异常导致了exception level切换,并且发生异常之前的exception
    level运行在AARCH64模式,那么使用第三组异常向量表。
  • 如果发生异常导致了exception level切换,并且发生异常之前的exception
    level运行在AARCH32模式,那么使用第四组异常向量表。

另外我们还可以看到的一点是,每一个异常入口不再仅仅占用4bytes的空间,而是占用0x80 bytes空间,也就是说,每一个异常入口可以放置多条指令,而不仅仅是一条跳转指令

2、armv8的VBAR_ELx寄存器

armv8定义了VBAR_EL1、VBAR_EL2、VBAR_EL3三个基地址寄存器
在这里插入图片描述
在这里插入图片描述

3、optee异常向量表的实现

(optee_os/core/arch/arm/kernel/thread_a64.S)

#define INV_INSN	0
FUNC thread_excp_vect , : align=2048
	/* -----------------------------------------------------
	 * EL1 with SP0 : 0x0 - 0x180
	 * -----------------------------------------------------
	 */
	.balign	128, INV_INSN
el1_sync_sp0:
	store_xregs sp, THREAD_CORE_LOCAL_X0, 0, 3
	b	el1_sync_abort
	check_vector_size el1_sync_sp0

	.balign	128, INV_INSN
el1_irq_sp0:
	store_xregs sp, THREAD_CORE_LOCAL_X0, 0, 3
	b	elx_irq
	check_vector_size el1_irq_sp0

	.balign	128, INV_INSN
el1_fiq_sp0:
	store_xregs sp, THREAD_CORE_LOCAL_X0, 0, 3
	b	elx_fiq
	check_vector_size el1_fiq_sp0

	.balign	128, INV_INSN
el1_serror_sp0:
	b	el1_serror_sp0
	check_vector_size el1_serror_sp0

	/* -----------------------------------------------------
	 * Current EL with SP1: 0x200 - 0x380
	 * -----------------------------------------------------
	 */
	.balign	128, INV_INSN
el1_sync_sp1:
	b	el1_sync_sp1
	check_vector_size el1_sync_sp1

	.balign	128, INV_INSN
el1_irq_sp1:
	b	el1_irq_sp1
	check_vector_size el1_irq_sp1

	.balign	128, INV_INSN
el1_fiq_sp1:
	b	el1_fiq_sp1
	check_vector_size el1_fiq_sp1

	.balign	128, INV_INSN
el1_serror_sp1:
	b	el1_serror_sp1
	check_vector_size el1_serror_sp1

	/* -----------------------------------------------------
	 * Lower EL using AArch64 : 0x400 - 0x580
	 * -----------------------------------------------------
	 */
	.balign	128, INV_INSN
el0_sync_a64:
	restore_mapping

	mrs	x2, esr_el1
	mrs	x3, sp_el0
	lsr	x2, x2, #ESR_EC_SHIFT
	cmp	x2, #ESR_EC_AARCH64_SVC
	b.eq	el0_svc
	b	el0_sync_abort
	check_vector_size el0_sync_a64

	.balign	128, INV_INSN
el0_irq_a64:
	restore_mapping

	b	elx_irq
	check_vector_size el0_irq_a64

	.balign	128, INV_INSN
el0_fiq_a64:
	restore_mapping

	b	elx_fiq
	check_vector_size el0_fiq_a64

	.balign	128, INV_INSN
el0_serror_a64:
	b   	el0_serror_a64
	check_vector_size el0_serror_a64

	/* -----------------------------------------------------
	 * Lower EL using AArch32 : 0x0 - 0x180
	 * -----------------------------------------------------
	 */
	.balign	128, INV_INSN
el0_sync_a32:
	restore_mapping

	mrs	x2, esr_el1
	mrs	x3, sp_el0
	lsr	x2, x2, #ESR_EC_SHIFT
	cmp	x2, #ESR_EC_AARCH32_SVC
	b.eq	el0_svc
	b	el0_sync_abort
	check_vector_size el0_sync_a32

	.balign	128, INV_INSN
el0_irq_a32:
	restore_mapping

	b	elx_irq
	check_vector_size el0_irq_a32

	.balign	128, INV_INSN
el0_fiq_a32:
	restore_mapping

	b	elx_fiq
	check_vector_size el0_fiq_a32

	.balign	128, INV_INSN
el0_serror_a32:
	b	el0_serror_a32
	check_vector_size el0_serror_a32

(1)、check_vector_size
check_vector_size其实就是检查异常向量中的指令size,不能草果32*4=128字节,因为armv8-arch64定义的异常向量每一个offset中的地址范围是128字节

	.macro check_vector_size since
	  .if (. - \since) > (32 * 4)
	    .error "Vector exceeds 32 instructions"
	  .endif
	.endm

(2)、128字节对其的异常向量
balign 128就是告诉汇编代码,接下来的函数定义是128字节对其的。这也和armv8-arch64定义的异常向量的地址范围一致

.balign	128, INV_INSN

(3)、异常向量实现的总结

异常向量处理的函数判定是否实现
第一组el1_sync_sp0b el1_sync_abortY
第一组el1_irq_sp0b elx_irqY
第一组el1_fiq_sp0b elx_fiqY
第一组el1_serror_sp0b el1_serror_sp0
自己跳转到自己,相当于死循环
N
第二组el1_sync_sp1b el1_sync_sp1
自己跳转到自己,相当于死循环
N
第二组el1_irq_sp1b el1_irq_sp1
自己跳转到自己,相当于死循环
N
第二组el1_fiq_sp1b el1_fiq_sp1
自己跳转到自己,相当于死循环
N
第二组el1_serror_sp1b el1_serror_sp1
自己跳转到自己,相当于死循环
N
第三组el0_sync_a64b el0_sync_abortY
第三组el0_irq_a64b elx_irqY
第三组el0_fiq_a64b elx_fiqY
第三组el0_serror_a64b el0_serror_a64
自己跳转到自己,相当于死循环
N
第四组el0_sync_a32b el0_svcY
第四组el0_irq_a32b elx_irqY
第四组el0_fiq_a32b elx_fiqY
第四组el0_serror_a32b el0_serror_a32
自己跳转到自己,相当于死循环
N

总结一下也是很好理解:

  • 在optee os中,使用的sp_el0栈,同时支持aarch32、aarch64的user程序,所以实现了第一、三、四组异常向量,另外optee不处理serror异常,所以serror也不实现。
  • 在Linux kernel中,使用sp_el1栈,同时支持aarch32、aarch64的user程序,所以实现了第二、三、四组异常向量.
    注:虽然Linux Kernel实现了FIQ向量,但该向量下的逻辑最终跳转到panic()函数,也就是如果触发了target到Linux Kernel的FIQ,将发生panic.

(4)、elx_irq和elx_fiq
以irq/fiq为例,我们还可以发现,无论是哪种分组异常,最终跳转的都是同一类函数:elx_irq和elx_fiq,即无论是下面哪种情况,跳转的都是elx_irq和elx_fiq函数。

  • PE在optee os特权级(S-EL1)执行时,来了一个irq/fiq中断
  • PE在userspace非特权级(S-EL0)执行aarch64时,来了一个irq/fiq中断
  • PE在userspace非特权级(S-user mode)执行aarch32时,来了一个irq/fiq中断

4、optee异常向量表基地址的定义

从上文的异常向量表的实现中可以发现,异常向量定义在了thread_excp_vect函数中, 那么该函数(异常向量)是如何布局到内存的? 该函数的基地址又是如何写入到VBAR_EL1的?

FUNC thread_excp_vect , : align=2048

在这里插入图片描述
thread_init_vbar(vaddr_t addr)将addr写入到vbar_el1

(optee_os/core/arch/arm/kernel/thread_a64.S)

FUNC thread_init_vbar , :
	msr	vbar_el1, x0
	ret
END_FUNC thread_init_vbar

get_excp_vect()返回异常向量表基地址(当然是虚拟地址)

(optee_os/core/arch/arm/kernel/thread.c)

static vaddr_t get_excp_vect(void)
{
#ifdef CFG_CORE_WORKAROUND_SPECTRE_BP_SEC
	uint32_t midr = read_midr();

	if (get_midr_implementer(midr) != MIDR_IMPLEMENTER_ARM)
		return (vaddr_t)thread_excp_vect;

	switch (get_midr_primary_part(midr)) {
#ifdef ARM32
	case CORTEX_A8_PART_NUM:
	case CORTEX_A9_PART_NUM:
	case CORTEX_A17_PART_NUM:
#endif
	case CORTEX_A57_PART_NUM:
	case CORTEX_A72_PART_NUM:
	case CORTEX_A73_PART_NUM:
	case CORTEX_A75_PART_NUM:
		return select_vector((vaddr_t)thread_excp_vect_workaround);
#ifdef ARM32
	case CORTEX_A15_PART_NUM:
		return select_vector((vaddr_t)thread_excp_vect_workaround_a15);
#endif
	default:
		return (vaddr_t)thread_excp_vect;
	}
#endif /*CFG_CORE_WORKAROUND_SPECTRE_BP_SEC*/

	return (vaddr_t)thread_excp_vect;
}

关于从cpu的启动(从cpu启动时设置VBAR_EL1):
在这里插入图片描述

  • 如果在整个系统中有实现ATF,则CFG_WITH_ARM_TRUSTED_FW宏是打开的,那么从cpu是从boot_cpu_on_handler启动,也就是从ATF调来的。
  • 如果在整个系统中没有实现ATF,则CFG_WITH_ARM_TRUSTED_FW宏是关闭的,那么从cpu是从reset_secondary---->boot_init_secondary调用过来的
(optee_os/core/arch/arm/kernel/boot.c)

#if defined(CFG_WITH_ARM_TRUSTED_FW)
unsigned long boot_cpu_on_handler(unsigned long a0 __maybe_unused,
				  unsigned long a1 __unused)
{
	init_secondary_helper(PADDR_INVALID);
	return 0;
}
#else
void boot_init_secondary(unsigned long nsec_entry)
{
	init_secondary_helper(nsec_entry);
}
#endif

细心的同学看代码可以发现:

  • armv8-aarch64架构都是有实现ATF,一般情况下CFG_WITH_ARM_TRUSTED_FW宏也都是打开的
  • 在optee的aarch64体系中,是没有调用boot_init_secondary函数的,仅仅在optee_os/core/arch/arm/kernel/entry_a32.S中的reset_secondary中进行了调用boot_init_secondary()

5、elx_irq和elx_fiq

gicv3/gicv2有着不同的处理

  • 如果是gicv2,则会将irq视为外系统中断,fiq视为本系统中断;
  • 如果是gicv3,恰好相反,将fiq视为外系统中断,irq视为本系统中断.

(注从optee中断软件的视角来看,gic可以分为两类,gicv2、非gicv2, 这里说说的gicv3其实就是非gicv2,如果你使用的是gicv4,那么也会定义CFG_ARM_GICV3宏)

本系统中断和外部系统中断的处理:

  • 如果是本系统中断,则调用native_intr_handler
  • 如果是外部系统中断则调用foreign_intr_handler
(optee_os/core/arch/arm/kernel/thread_a64.S)

 LOCAL_FUNC elx_irq , :
 #if defined(CFG_ARM_GICV3)
 	native_intr_handler	irq
 #else
 	foreign_intr_handler	irq
 #endif
 END_FUNC elx_irq
 
 LOCAL_FUNC elx_fiq , :
 #if defined(CFG_ARM_GICV3)
 	foreign_intr_handler	fiq
 #else
 	native_intr_handler	fiq
 #endif
 END_FUNC elx_fiq

在这里插入图片描述

标签:fiq,--,irq,解读,elx,serror,el0,el1,optee3.14
来源: https://blog.csdn.net/weixin_42135087/article/details/120246360

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有