【中断】【ARM64】学习总结

从arm手册可以看出,异常向量表在EL1~EL3都有,本文只涉及Linux kernel中的实现,即只涉及EL1和EL0。

参考Wiki:
optee中的异常向量表解读–中断处理解读

在这里插入图片描述
参考Wiki:
armv8/armv9中断系列详解-学这篇就够了

kernel版本:v6.9-rc6

(arch/arm64/kernel/head.S)

SYM_FUNC_START_LOCAL(__secondary_switched)
   ...
   /* 向量表起始地址放到EL1向量表基址寄存器中 */
   	adr_l	x5, vectors
	msr	vbar_el1, x5
	isb
	...
SYM_FUNC_END(__secondary_switched)

向量表入口:

(arch/arm64/kernel/entry.S)

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

	.align	11
SYM_CODE_START(vectors)
	kernel_ventry	1, t, 64, sync		// Synchronous EL1t
	kernel_ventry	1, t, 64, irq		// IRQ EL1t
	kernel_ventry	1, t, 64, fiq		// FIQ EL1t
	kernel_ventry	1, t, 64, error		// Error EL1t

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

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

	kernel_ventry	0, t, 32, sync		// Synchronous 32-bit EL0
	kernel_ventry	0, t, 32, irq		// IRQ 32-bit EL0
	kernel_ventry	0, t, 32, fiq		// FIQ 32-bit EL0
	kernel_ventry	0, t, 32, error		// Error 32-bit EL0
SYM_CODE_END(vectors)

遗留问题:向量表中的t/h代表啥意思呢?

查找资料,有如下解释:
处理器模式为t(指Task模式,即用户态模式)
处理器模式为h(指Handler模式,即内核态模式)

扩展:

向量表在kernel镜像中的链接位置

(include/asm-generic/vmlinux.lds.h)

#define ENTRY_TEXT							\
		ALIGN_FUNCTION();					\
		__entry_text_start = .;					\
		*(.entry.text)						\
		__entry_text_end = .;

(arch/arm64/kernel/vmlinux.lds.S)

SECTIONS
{
	...
	.text : ALIGN(SEGMENT_ALIGN) {	/* Real text segment		*/
		_stext = .;		/* Text and read-only data	*/
			IRQENTRY_TEXT
			SOFTIRQENTRY_TEXT
			ENTRY_TEXT
			TEXT_TEXT
			SCHED_TEXT
			LOCK_TEXT
			KPROBES_TEXT
			HYPERVISOR_TEXT
			*(.gnu.warning)
	}

	. = ALIGN(SEGMENT_ALIGN);
	_etext = .;
	...
}

向量表分析

(arch/arm64/kernel/entry.S)

	.macro kernel_ventry, el:req, ht:req, regsize:req, label:req
	.align 7
	...
	b	el\el\ht\()_\regsize\()_\label
	...
	.endm

(arch/arm64/kernel/entry.S)

	.macro entry_handler el:req, ht:req, regsize:req, label:req
SYM_CODE_START_LOCAL(el\el\ht\()_\regsize\()_\label)
	kernel_entry \el, \regsize
	mov	x0, sp
	bl	el\el\ht\()_\regsize\()_\label\()_handler
	.if \el == 0
	b	ret_to_user
	.else
	b	ret_to_kernel
	.endif
SYM_CODE_END(el\el\ht\()_\regsize\()_\label)
	.endm

上述汇编代码片段中,汇编宏 kernel_ventry的参数结合el\el\ht()\regsize()\label()_handler函数对应如下不同异常的入口:

(arch/arm64/include/asm/exception.h)

asmlinkage void el1t_64_sync_handler(struct pt_regs *regs);
asmlinkage void el1t_64_irq_handler(struct pt_regs *regs);
asmlinkage void el1t_64_fiq_handler(struct pt_regs *regs);
asmlinkage void el1t_64_error_handler(struct pt_regs *regs);

asmlinkage void el1h_64_sync_handler(struct pt_regs *regs);
asmlinkage void el1h_64_irq_handler(struct pt_regs *regs);
asmlinkage void el1h_64_fiq_handler(struct pt_regs *regs);
asmlinkage void el1h_64_error_handler(struct pt_regs *regs);

asmlinkage void el0t_64_sync_handler(struct pt_regs *regs);
asmlinkage void el0t_64_irq_handler(struct pt_regs *regs);
asmlinkage void el0t_64_fiq_handler(struct pt_regs *regs);
asmlinkage void el0t_64_error_handler(struct pt_regs *regs);

asmlinkage void el0t_32_sync_handler(struct pt_regs *regs);
asmlinkage void el0t_32_irq_handler(struct pt_regs *regs);
asmlinkage void el0t_32_fiq_handler(struct pt_regs *regs);
asmlinkage void el0t_32_error_handler(struct pt_regs *regs);

kernel中异常向量表的具体实现

(arch/arm64/kernel/entry-common.c)

UNHANDLED(el1t, 64, sync)
UNHANDLED(el1t, 64, irq)
UNHANDLED(el1t, 64, fiq)
UNHANDLED(el1t, 64, error)

#define UNHANDLED(el, regsize, vector)							\
asmlinkage void noinstr el##_##regsize##_##vector##_handler(struct pt_regs *regs)	\
{											\
	const char *desc = #regsize "-bit " #el " " #vector;				\
	__panic_unhandled(regs, desc, read_sysreg(esr_el1));				\
}

先把以el1h_64_irq_handler/el1h_64_fiq_handler为例,看下实现
(arch/arm64/kernel/entry-common.c)

asmlinkage void noinstr el1h_64_irq_handler(struct pt_regs *regs)
{
	el1_interrupt(regs, handle_arch_irq);
}

asmlinkage void noinstr el1h_64_fiq_handler(struct pt_regs *regs)
{
	el1_interrupt(regs, handle_arch_fiq);
}
static void noinstr el1_interrupt(struct pt_regs *regs,
				  void (*handler)(struct pt_regs *))
{
	write_sysreg(DAIF_PROCCTX_NOIRQ, daif);

	...
		__el1_irq(regs, handler);
}
static __always_inline void __el1_irq(struct pt_regs *regs,
				      void (*handler)(struct pt_regs *))
{
	enter_from_kernel_mode(regs);

	irq_enter_rcu();
	do_interrupt_handler(regs, handler);
	irq_exit_rcu();

	arm64_preempt_schedule_irq();

	exit_to_kernel_mode(regs);
}
static void do_interrupt_handler(struct pt_regs *regs,
				 void (*handler)(struct pt_regs *))
{
	struct pt_regs *old_regs = set_irq_regs(regs);

	if (on_thread_stack())
		call_on_irq_stack(regs, handler);
	else
		handler(regs); // 非线程化中断处理方式

	set_irq_regs(old_regs);
}

(arch/arm64/kernel/irq.c)

static void default_handle_irq(struct pt_regs *regs)
{
	panic("IRQ taken without a root IRQ handler\n");
}

static void default_handle_fiq(struct pt_regs *regs)
{
	panic("FIQ taken without a root FIQ handler\n");
}

void (*handle_arch_irq)(struct pt_regs *) __ro_after_init = default_handle_irq;
void (*handle_arch_fiq)(struct pt_regs *) __ro_after_init = default_handle_fiq;

int __init set_handle_irq(void (*handle_irq)(struct pt_regs *))
{
	if (handle_arch_irq != default_handle_irq)
		return -EBUSY;

	handle_arch_irq = handle_irq;
	pr_info("Root IRQ handler: %ps\n", handle_irq);
	return 0;
}

int __init set_handle_fiq(void (*handle_fiq)(struct pt_regs *))
{
	if (handle_arch_fiq != default_handle_fiq)
		return -EBUSY;

	handle_arch_fiq = handle_fiq;
	pr_info("Root FIQ handler: %ps\n", handle_fiq);
	return 0;
}

(drivers/irqchip/irq-gic-v3.c)

static int __init gic_init_bases(phys_addr_t dist_phys_base,
				 void __iomem *dist_base,
				 struct redist_region *rdist_regs,
				 u32 nr_redist_regions,
				 u64 redist_stride,
				 struct fwnode_handle *handle)
{
	...

	set_handle_irq(gic_handle_irq);
	...
}
static int __init gic_of_init(struct device_node *node, struct device_node *parent)
{
	...

	err = gic_init_bases(dist_phys_base, dist_base, rdist_regs,
			     nr_redist_regions, redist_stride, &node->fwnode);
	...
}
IRQCHIP_DECLARE(gic_v3, "arm,gic-v3", gic_of_init);

static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
{
	if (unlikely(gic_supports_nmi() && !interrupts_enabled(regs)))
		__gic_handle_irq_from_irqsoff(regs);
	else
		__gic_handle_irq_from_irqson(regs);
}
static void __gic_handle_irq_from_irqson(struct pt_regs *regs)
{
	...
		__gic_handle_irq(irqnr, regs);
}
static void __gic_handle_irq(u32 irqnr, struct pt_regs *regs)
{
	...

	if (generic_handle_domain_irq(gic_data.domain, irqnr)) { // generic_handle_domain_irq接口往下调用是gic的处理流程,此处就不展开了
		WARN_ONCE(true, "Unexpected interrupt (irqnr %u)\n", irqnr);
		gic_deactivate_unhandled(irqnr);
	}
}

()


()


()


参考Wiki:
armv8/armv9中断系列详解-软件篇-Linux kernel中断相关软件导读

补充:
在早期版本中,向量表式这样的,基本大同小异。
从表中可以看出来,Linux kernel里主要实现了EL1/EL0的同步异常和中断;

(CONFIG_COMPAT 是 Linux 内核中一个配置选项的宏,用于控制兼容性相关的特性。当启用了 CONFIG_COMPAT 宏时,Linux 内核会支持在 64 位系统上运行 32 位的用户空间程序。这样做的目的是为了确保新版本的 Linux 内核仍然能够运行旧版本的 32 位用户空间程序,以提供更好的兼容性。)
在这里插入图片描述

参考资料:
《linux内核深度解析_余华兵》4.1.3 异常向量表

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值