21-Linux Kernel/optee/ATF等操作系统的异常向量表的速查

268 篇文章 390 订阅

引流关键词: IRQ,FIQ,Serror, 中断,同步异常,异步异常,TF-A,TF-M,ATF,TrustedFirmware,trustzone,TEE,optee,trusty,tlk,lk,armv8,armv9,arm,secureboot,BL31,BL32,BL1,BL2,hypervisor,终端安全,secureboot,security,virtulization

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

在这里插入图片描述

说明:
在默认情况下,本文讲述的都是ARMV8-aarch64架构,linux kernel 5.14, optee3.14, TF-A 2.4, armv8.8

  • 软件中定义的向量表,是否和ARM文档中的向量offset一致
  • 向量表的基地址是否写入到了VBAR寄存器

硬件:armv8-aarch64\arch以及armv7的向量表和基地址寄存器介绍

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 aarch32的异常向量表介绍

在这里插入图片描述

3、ARMV7 4的异常向量表介绍

在这里插入图片描述

4、ARMV8 aarch64的向量表基地址

VBAR(Vector Base Address Register)的寄存器有:

(如果是aarch64)

  • VBAR_EL1
  • VBAR_EL2
  • VBAR_EL3

在开启MMU的系统,VBAR中写入的是虚拟地址,以VBAR_EL1为例,介绍下field的使用:
在这里插入图片描述

Bits [10:0] reserved

Bits [11:63]:
如果不支持ARMv8.2-LVA(Large VA support:使用64kb页面时,有效虚拟地址达到52bit)
(1)、如果支持tagged addresses, bits [55:48]必需都是一样的
(2)、如果不支持tagged addresses , bits [63:48] 必需都是一样的
如果支持ARMv8.2-LVA(Large VA support:使用64kb页面时,有效虚拟地址达到52bit)
(1)、如果支持tagged addresses , bits [55:52] 必需都是一样的
(2)、如果不支持tagged addresses , bits [63:52] 必需都是一样的

该寄存器的低11bit是reserve的,11~63表示了Vector Base Address,因此这里的异常向量表基地址是2K对齐的

5、ARMV8 aarch的向量表基地址

如果是aarch32

  • VBAR
  • HVBAR
  • MVBAR
6、ARMV7的向量表基地址

TODO

软件:各个系统的异常向量表(Linux, optee, ATF …32位/64位)

1、Linux Kernel 中arm64定义的向量表
(linux/arch/arm64/kernel/entry.S)

/*
 * Exception vectors.
 */
	.pushsection ".entry.text", "ax"

	.align	11
SYM_CODE_START(vectors)
	kernel_ventry	1, sync_invalid			// Synchronous EL1t
	kernel_ventry	1, irq_invalid			// IRQ EL1t
	kernel_ventry	1, fiq_invalid			// FIQ EL1t
	kernel_ventry	1, error_invalid		// Error EL1t

	kernel_ventry	1, sync				// Synchronous EL1h
	kernel_ventry	1, irq				// IRQ EL1h
	kernel_ventry	1, fiq				// FIQ EL1h
	kernel_ventry	1, error			// Error EL1h

	kernel_ventry	0, sync				// Synchronous 64-bit EL0
	kernel_ventry	0, irq				// IRQ 64-bit EL0
	kernel_ventry	0, fiq				// FIQ 64-bit EL0
	kernel_ventry	0, error			// Error 64-bit EL0

#ifdef CONFIG_COMPAT
	kernel_ventry	0, sync_compat, 32		// Synchronous 32-bit EL0
	kernel_ventry	0, irq_compat, 32		// IRQ 32-bit EL0
	kernel_ventry	0, fiq_compat, 32		// FIQ 32-bit EL0
	kernel_ventry	0, error_compat, 32		// Error 32-bit EL0
#else
	kernel_ventry	0, sync_invalid, 32		// Synchronous 32-bit EL0
	kernel_ventry	0, irq_invalid, 32		// IRQ 32-bit EL0
	kernel_ventry	0, fiq_invalid, 32		// FIQ 32-bit EL0
	kcernel_ventry	0, error_invalid, 32		// Error 32-bit EL0
#endif
SYM_CODE_END(vectors)

注意.align=7,说明该段代码是以2^7=128字节对其的,这和向量表中每一个offset的大小是一致的
代码看似非常复杂,其实最终跳转到了b el\()\el\()_\label, 翻译一下,其实就是跳转到了如下这样的函数中

el1_sync_invalid	
el1_irq_invalid	
el1_fiq_invalid	
el1_error_invalid

el1_sync			
el1_irq			
el1_fiq			
el1_error		

el0_sync			
el0_irq			
el0_fiq			
el0_error	
2、Linux Kernel 中arm定义的向量表
	.section .stubs, "ax", %progbits
__stubs_start:
	@ This must be the first word
	.word	vector_swi

	.section .vectors, "ax", %progbits
__vectors_start:
	W(b)	vector_rst
	W(b)	vector_und
	W(ldr)	pc, __vectors_start + 0x1000
	W(b)	vector_pabt
	W(b)	vector_dabt
	W(b)	vector_addrexcptn
	W(b)	vector_irq
	W(b)	vector_fiq
3、optee中arm64定义的异常向量表
(core/arch/arm/kernel/thread_a64.S)
	.section .text.thread_excp_vect
	.align	11, INV_INSN
FUNC thread_excp_vect , :
	/* -----------------------------------------------------
	 * EL1 with SP0 : 0x0 - 0x180
	 * -----------------------------------------------------
	 */
	.align	7, INV_INSN
el1_sync_sp0:
	store_xregs sp, THREAD_CORE_LOCAL_X0, 0, 3
	b	el1_sync_abort
	check_vector_size el1_sync_sp0

	.align	7, INV_INSN
el1_irq_sp0:
	store_xregs sp, THREAD_CORE_LOCAL_X0, 0, 3
	b	elx_irq
	check_vector_size el1_irq_sp0

	.align	7, INV_INSN
el1_fiq_sp0:
	store_xregs sp, THREAD_CORE_LOCAL_X0, 0, 3
	b	elx_fiq
	check_vector_size el1_fiq_sp0

	.align	7, INV_INSN
el1_serror_sp0:
	b	el1_serror_sp0
	check_vector_size el1_serror_sp0

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

	.align	7, INV_INSN
el1_irq_sp1:
	b	el1_irq_sp1
	check_vector_size el1_irq_sp1

	.align	7, INV_INSN
el1_fiq_sp1:
	b	el1_fiq_sp1
	check_vector_size el1_fiq_sp1

	.align	7, INV_INSN
el1_serror_sp1:
	b	el1_serror_sp1
	check_vector_size el1_serror_sp1

	/* -----------------------------------------------------
	 * Lower EL using AArch64 : 0x400 - 0x580
	 * -----------------------------------------------------
	 */
	.align	7, 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

	.align	7, INV_INSN
el0_irq_a64:
	restore_mapping

	b	elx_irq
	check_vector_size el0_irq_a64

	.align	7, INV_INSN
el0_fiq_a64:
	restore_mapping

	b	elx_fiq
	check_vector_size el0_fiq_a64

	.align	7, INV_INSN
el0_serror_a64:
	b   	el0_serror_a64
	check_vector_size el0_serror_a64

	/* -----------------------------------------------------
	 * Lower EL using AArch32 : 0x0 - 0x180
	 * -----------------------------------------------------
	 */
	.align	7, 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

	.align	7, INV_INSN
el0_irq_a32:
	restore_mapping

	b	elx_irq
	check_vector_size el0_irq_a32

	.align	7, INV_INSN
el0_fiq_a32:
	restore_mapping

	b	elx_fiq
	check_vector_size el0_fiq_a32

	.align	7, INV_INSN
el0_serror_a32:
	b	el0_serror_a32
	check_vector_size el0_serror_a32

align 7,对齐方式为7,也就是0x80对齐,恰好符合armv7-aarch64中文档中的向量表的offset偏移

4、optee中arm定义的异常向量表
(core/arch/arm/kernel/thread_a32.S)
	.section .text.thread_excp_vect
        .align	5
FUNC thread_excp_vect , :
UNWIND(	.fnstart)
UNWIND(	.cantunwind)
	b	.			/* Reset			*/
	b	thread_und_handler	/* Undefined instruction	*/
	b	thread_svc_handler	/* System call			*/
	b	thread_pabort_handler	/* Prefetch abort		*/
	b	thread_dabort_handler	/* Data abort			*/
	b	.			/* Reserved			*/
	b	thread_irq_handler	/* IRQ				*/
	b	thread_fiq_handler	/* FIQ				*/

一条指令占4个字节,所以这里也是和aarch32的异常向量表的offset一一对应的

5、在ATF中arm64异常向量表的实现定义

在ATF的代码中,在不同的阶段有着不同的异常向量表:

  • 在bl1阶段使用bl1_exceptions
  • 在bl2阶段使用bl2_entrypoint
  • 在bl31及其之后使用runtime_exceptions
func bl1_entrypoint
......
	el3_entrypoint_common					\
		_set_endian=1					\
		_warm_boot_mailbox=!PROGRAMMABLE_RESET_ADDRESS	\
		_secondary_cold_boot=!COLD_BOOT_SINGLE_CPU	\
		_init_memory=1					\
		_init_c_runtime=1				\
		_exception_vectors=bl1_exceptions
		
func bl2_entrypoint
	el3_entrypoint_common					\
		_set_endian=0					\
		_warm_boot_mailbox=0				\
		_secondary_cold_boot=0				\
		_secondary_cpu = 0				\
		_init_memory=0					\
		_init_c_runtime=1				\
		_exception_vectors=bl2_vector
		
func bl31_entrypoint
......
	el3_entrypoint_common					\
		_set_endian=0					\
		_warm_boot_mailbox=0				\
		_secondary_cold_boot=0				\
		_init_memory=0					\
		_init_c_runtime=1				\
		_exception_vectors=runtime_exceptions

我们常说的ATF中的向量表,其实就是bl31之后使用runtime_exceptions向量表,下面重点介绍下

(注意 : 带unhandled的都是未实现的)

  • 用于处理EL0产生异常时的entire(对应第一行向量表)
vector_entry sync_exception_sp_el0
b report_unhandled_exception
check_vector_size sync_exception_sp_el0
vector_entry irq_sp_el0
b report_unhandled_interrupt
check_vector_size irq_sp_el0
vector_entry fiq_sp_el0
b report_unhandled_interrupt
check_vector_size fiq_sp_el0
vector_entry serror_sp_el0
b report_unhandled_exception
check_vector_size serror_sp_el0
  • 用于处理当前ELx产生异常时的entire(对应第二行向量表)
vector_entry sync_exception_sp_elx
b report_unhandled_exception
check_vector_size sync_exception_sp_elx
vector_entry irq_sp_elx
b report_unhandled_interrupt
check_vector_size irq_sp_elx
vector_entry fiq_sp_elx
b report_unhandled_interrupt
check_vector_size fiq_sp_elx
vector_entry serror_sp_elx
b report_unhandled_exception
check_vector_size serror_sp_elx
  • 用于处理AArch64指令产生的异常,且发生了EL的迁移的entire(对应第三行向量表)
vector_entry sync_exception_aarch64
handle_sync_exception
check_vector_size sync_exception_aarch64
vector_entry irq_aarch64
handle_interrupt_exception irq_aarch64
check_vector_size irq_aarch64
vector_entry fiq_aarch64
handle_interrupt_exception fiq_aarch64
check_vector_size fiq_aarch64
vector_entry serror_aarch64
b report_unhandled_exception
check_vector_size serror_aarch64
  • 用于处理AArch32指令产生的异常,且发生了EL的迁移的entire(对应第四行向量表)
vector_entry sync_exception_aarch32
handle_sync_exception
check_vector_size sync_exception_aarch32
vector_entry irq_aarch32
handle_interrupt_exception irq_aarch32
check_vector_size irq_aarch32
vector_entry fiq_aarch32
handle_interrupt_exception fiq_aarch32
check_vector_size fiq_aarch32
vector_entry serror_aarch32
b report_unhandled_exception
check_vector_size serror_aarch32
6、、在ATF中arm64异常向量表的实现定义

TODO

软件:各个系统的向量表基地址的设置(Linux, optee, ATF …32位/64位)

1、linux kernel的arm64下设置向量表基地址VBAR

在__primary_switched将vectors写入到了VBAR_EL1

__primary_switched:
	adrp	x4, init_thread_union
	add	sp, x4, #THREAD_SIZE
	adr_l	x5, init_task
	msr	sp_el0, x5			// Save thread_info

	adr_l	x8, vectors			// load VBAR_EL1 with virtual
	msr	vbar_el1, x8			// vector table address
	isb

	stp	xzr, x30, [sp, #-16]!
	mov	x29, sp

	str_l	x21, __fdt_pointer, x5		// Save FDT pointer

..
	b	start_kernel
ENDPROC(__primary_switched)
2、linux kernel的arm32下设置向量表基地址VBAR

Linux Kernel的arm的异常向量表定义在__vectors_start

在vmlinux.lds.S描述了__vectors_start的起始位置,从0xffff0000开始**

(kernel-4.14/arch/arm/kernel/vmlinux.lds.S)
__vectors_start = .;
.vectors 0xffff0000 : AT(__vectors_start) {
	*(.vectors)
}
. = __vectors_start + SIZEOF(.vectors);
__vectors_end = .;

__stubs_start = .;
.stubs ADDR(.vectors) + 0x1000 : AT(__stubs_start) {
	*(.stubs)
}
. = __stubs_start + SIZEOF(.stubs);
__stubs_end = .;

在nommu.c中,setup_vectors_base函数通过操作cp15协处理器来写入VBAR.

(kernel-4.14/arch/arm/mm/nommu.c)
static unsigned long __init setup_vectors_base(void)
{
	unsigned long reg = get_cr();

	set_cr(reg | CR_V);   //其实就是将CR_V写入到了cp15, 也就是写入到了VBAR寄存器.
	return 0xffff0000;
}
static inline void set_cr(unsigned long val)
{
	asm volatile("mcr p15, 0, %0, c1, c0, 0	@ set CR"
	  : : "r" (val) : "cc");
	isb();
}

而CR_V的定义在cp15.h中,恰好就是0xffff0000(也就是最地址处,空出64KB的地方,给vector使用)

(kernel-4.14/arch/arm/include/asm/cp15.h)
#define CR_M	(1 << 0)	/* MMU enable				*/
#define CR_A	(1 << 1)	/* Alignment abort enable		*/
#define CR_C	(1 << 2)	/* Dcache enable			*/
#define CR_W	(1 << 3)	/* Write buffer enable			*/
#define CR_P	(1 << 4)	/* 32-bit exception handler		*/
#define CR_D	(1 << 5)	/* 32-bit data address range		*/
#define CR_L	(1 << 6)	/* Implementation defined		*/
#define CR_B	(1 << 7)	/* Big endian				*/
#define CR_S	(1 << 8)	/* System MMU protection		*/
#define CR_R	(1 << 9)	/* ROM MMU protection			*/
#define CR_F	(1 << 10)	/* Implementation defined		*/
#define CR_Z	(1 << 11)	/* Implementation defined		*/
#define CR_I	(1 << 12)	/* Icache enable			*/
#define CR_V	(1 << 13)	/* Vectors relocated to 0xffff0000	*/
#define CR_RR	(1 << 14)	/* Round Robin cache replacement	*/
#define CR_L4	(1 << 15)	/* LDR pc can set T bit			*/
#define CR_DT	(1 << 16)

setup_vectors_base是在开机的时候调用的

setup_arch ----> arm_memblock_init ----> arm_mm_memblock_reserve  ---> setup_vectors_base
3、optee中arm64设置向量表基地址VBAR_EL1

get_excp_vect()函数获取到thread_a64.S中定义的向量表thread_excp_vect地址

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

在thread_init_per_cpu()时,将向量表基地址写入到VBAR_EL1

void thread_init_per_cpu(void)
{
	size_t pos = get_core_pos();
	struct thread_core_local *l = thread_get_core_local();

	init_sec_mon(pos);

	set_tmp_stack(l, GET_STACK(stack_tmp[pos]) - STACK_TMP_OFFS);
	set_abt_stack(l, GET_STACK(stack_abt[pos]));

	thread_init_vbar(get_excp_vect());
}

thread_init_vbar函数完成将基地址写入VBAR_EL1(将参数1写入到VBAR_EL1)

(core/arch/arm/kernel/thread_a64.S)
FUNC thread_init_vbar , :
	msr	vbar_el1, x0   
	ret
END_FUNC thread_init_vbar
4、optee中arm设置向量表基地址VBAR_EL1

其流程同aarch64的流程相同,都是thread_init_per_cpu()---->thread_init_vbar ()

(core/arch/arm/kernel/thread_a32.S)
FUNC thread_init_vbar , :
UNWIND(	.fnstart)
	/* Set vector (VBAR) */
	write_vbar r0
	bx	lr
UNWIND(	.fnend)
END_FUNC thread_init_vbar

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Arm精选

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值