[architecture]-ARM-linux异常向量表介绍

1、ARM的异常向量表基地址寄存器–VBAR

1.1、armv8 : VBAR寄存器

在这里插入图片描述在这里插入图片描述

1.2、armv7 : VBAR寄存器

在这里插入图片描述

2、ARM的异常向量表的定义

2.1 armv8 :异常向量表offset的定义

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

  • 如果发生异常并不会导致exception level切换,并且使用的栈指针是SP_EL0,那么使用第一组异常向量表。
  • 如果发生异常并不会导致exception level切换,并且使用的栈指针是SP_EL1/2/3,那么使用第二组异常向量表。
  • 如果发生异常会导致exception level切换,并且比目的exception level低一级的exception
    level运行在AARCH64模式,那么使用第三组异常向量表。
  • 如果发生异常会导致exception level切换,并且比目的exception level低一级的exception
    level运行在AARCH32模式,那么使用第四组异常向量表。

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

2.2 armv8 : 在linux kernel中异常向量表的实现定义

我们再来看在linux kernel中定义的异常向量表

	.align	11
ENTRY(vectors)
(1)
	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
(2)
	kernel_ventry	1, sync				// Synchronous EL1h
	kernel_ventry	1, irq				// IRQ EL1h
	kernel_ventry	1, fiq_invalid			// FIQ EL1h
	kernel_ventry	1, error_invalid		// Error EL1h
(3)
	kernel_ventry	0, sync				// Synchronous 64-bit EL0
	kernel_ventry	0, irq				// IRQ 64-bit EL0
	kernel_ventry	0, fiq_invalid			// FIQ 64-bit EL0
	kernel_ventry	0, error_invalid		// Error 64-bit EL0
(4)
#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_invalid_compat, 32	// FIQ 32-bit EL0
	kernel_ventry	0, error_invalid_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
	kernel_ventry	0, error_invalid, 32		// Error 32-bit EL0
#endif
END(vectors)

第(1)段对应的是第一行向量表、第(2)段对应的是第二行向量表、第(3)段对应的是第三行向量表、第四段对应的是第一行向量表.
带invalid后缀的,都是未实现的。然后我们抽出实现了的部分,仅4行:

kernel_ventry	1, sync				// Synchronous EL1h
kernel_ventry	1, irq				// IRQ EL1h
kernel_ventry	0, sync				// Synchronous 64-bit EL0
kernel_ventry	0, irq				// IRQ 64-bit EL0

他们对应的函数分别是:
el1_sync //在kernel mode中调用svc指令,触发同步异常
el1_irq //在kernel mode中触发了irq异步异常
el0_sync //在user mode中调用svc指令,触发同步异常
el0_irq //在user mode中触发了irq异步异常

2.3 armv8 : 在ATF中异常向量表的实现定义

在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

2.4 armv8 : 异常向量表总结和示例:

以异步异常irq为例
(1)、当irq产生,如果该irq被target到了EL3,那么将使用VBAR_EL3寄存器中的基地址.
我们知道armv8有四组异常向量表,对应表格四行。然后再看使用哪组表:
cpu运行在EL0时产生了irq, 切未发生EL级别切换(中断需被target到EL0),使用第一组表,这个条件不会存在.
cpu是在EL3时产生的irq, 未发生EL级别切换,使用第二组表,跳转到VBAR_EL3 + 0x280处,对应的irq_sp_elx函数
cpu运行在aarch64级别,在EL1/EL2时产生的irq,发生了EL级别切换,使用第三组表,跳转到VBAR_EL3 + 0x480处,对应的irq_aarch64函数
cpu运行在aarch32级别,在EL1/EL2时产生的irq,发生了EL级别切换,使用第四组表,跳转到VBAR_EL3 + 0x680处,对应的irq_aarch32函数

(2)、当irq产生,如果该irq被target到了EL1,那么将使用VBAR_EL1寄存器中的基地址.
我们知道armv8有四组异常向量表,对应表格四行。然后再看使用哪组表:
cpu运行在EL0时产生了irq, 切未发生EL级别切换(中断需被target到EL0),使用第一组表,这个条件不会存在.
cpu是在EL1时产生的irq, 未发生EL级别切换,使用第二组表,跳转到VBAR_EL1 + 0x280处,对应的el1_irq函数
cpu运行在aarch64级别,在EL0时产生的irq,发生了EL级别切换,使用第三组表,跳转到VBAR_EL1 + 0x480处,对应的el0_irq函数
cpu运行在aarch32级别,在EL0时产生的irq,发生了EL级别切换,使用第四组表,跳转到VBAR_EL1 + 0x680处,对应的irq_invalid函数,也就是未实现

2.5 armv7 :异常向量表offset的定义

在这里插入图片描述
在这里插入图片描述

2.6 armv7 : 在linux kernel中异常向量表的实现定义

	.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、总结:

1、在armv7下使用的是data abort、prefetch abort、undefined instruction,在armv8下使用的是SError.
2、在linux kernel中,armv7体系下均已实现data abort、prefetch abort、undefined instruction异常处理函数,在linux kernel的armv8体系下,没有实现SError异常处理
3、在linux kernel的armv8体系中,未实现fiq函数
4、
在armv7的向量表offset中,每一个异常入口都是占4 bytes,是这样排的:0x1c 0x18 0x10 0x0c 0x08 0x04
在armv8的向量表offset中,每一个异常入口都是占0x80bytes,是这样排的00 0x80 0x100 0x180 0x200 0x280…
那么我们在linux kernel中定义的向量表,是怎样和上面的offset对应上的?

W(b)	vector_irq
W(b)	vector_fiq

5、在armv7中,VBAR是banked的,在linux/tee各有一份拷贝. 在armv7中,没有gic的cpu interface。所以armv7的芯片只能使用gicv2.
在gicv2中,FIQ表示安全中断,给TEE用的,IRQ表示非安全中断,给Linux用的.

  • 当cpu在REE(linux)执行时,来了一个非安全中断(linux中断、IRQ), 那么cpu陷入异常,跳转到 "normal VBAR + irq offset“ 处, 也就是linux irq中断处理函数
  • 当cpu在REE(linux)执行时,来了一个安全中断(tee中断、FIQ),那么cpu陷入异常,跳转到 "normal VBAR + fiq offset“ 处,也就是linux fiq中断处理函数
  • 当cpu在TEE(tee)执行时,来了一个安全中断(TEE中断、FIQ), 那么cpu陷入异常,跳转到 "secure VBAR + fiq offset“ 处, 也就是tee irq中断处理函数
  • 当cpu在TEE(tee)执行时,来了一个非安全中断(linux中断、irq),那么cpu陷入异常,跳转到 "secure VBAR + irq offset“ 处,也就是tee fiq中断处理函数, 在该函数中会主动将cpu切换到linux进行处理.

6、如果在某一时刻,将发送给cpu0的IPI_RESCHEDULE(SGI=2)中断屏蔽了,过了一会再恢复. 这样会对linux系统有影响吗?
内核的中断屏蔽,是将cpu的PSTATE.I置0了。 local_disable_irq后,gic再产生的中断,ARM Core不会去处理,也就不会清除gic寄存器中的中断状态. 等到 local_enable_irq后, 该中断还会再送给ARM Core. 所以这种情况中断不会丢失…
所以在某一时刻屏蔽了IPI_RESCHEDULE,也只是屏蔽,也没有去清gic中相关寄存器。所以等到恢复屏蔽后,应该也不会丢中断

猜你喜欢

转载自blog.csdn.net/weixin_42135087/article/details/107332167
今日推荐