linux arm64 中断处理流程完整分析 (一)—— 中断向量表、中断处理流程汇编部分

2023-05-16

中断流程老生常谈,但我一直以来也只是知道中断过来之后,会保护现场,跳到中断向量表,执行中断,恢复现场,然后返回。至于更多细节,就不得而知了。这篇文章旨在把更完整的linux 中断处理流程梳理一遍。主要带着以下几个问题:

  1. 中断向量表是在代码哪里配置的
  2. 响应中断时,必然有一些读写gic 寄存器的操作,这些操作在什么位置
  3. /proc/interrupts 这个proc 文件是如何实现的
  4. linux 进入中断前,会屏蔽其他所有中断,这个是如何实现的
  5. 系统调用的系统中断0x80,是如何从用户态调用到内核态file_operations接口的?和普通的中断有什么区别

本文代码均来自linux4.19

下一篇的传送门:linux arm64 中断处理流程完整分析 (二)—— 中断处理流程c代码部分 以gic_v3 为例

配置中断向量表

从 arch/arm64/kernel/vmlinux.lds.S 中可以看到,内核ENTRY 为_text,

/*arch/arm64/kernel/vmlinux.lds.S*/
ENTRY(_text)
...
	.head.text : {
		_text = .;
		HEAD_TEXT
	}

HEAD_TEXT定义在如下位置

/*include/asm-generic/vmlinux.lds.h*/
/* Section used for early init (in .S files) */
#define HEAD_TEXT  KEEP(*(.head.text))

也就是说linux的入口为指定为.head.text senction 的代码,grep 可以发现

/*include/linux/init.h*/
#define __HEAD      .section    ".head.text","ax"

__HEAD宏仅在在如下位置使用

/*arch/arm64/kernel/head.S*/
	__HEAD
_head:
	...
	b	stext
	...
ENTRY(stext)
	...
	b	__primary_switch
ENDPROC(stext)

__primary_switched:
	...
	adr_l	x8, vectors			// load VBAR_EL1 with virtual
	msr	vbar_el1, x8			// vector table address
	isb
	...
		b	start_kernel
ENDPROC(__primary_switched)

从上边的代码中可以看到从linux 入口到设置向量表的完整流程。最终设置向量表的指令,就是将vector 的地址,写入vbar_el1 寄存器。
对于vbar_el1 寄存器的作用,arm 官方文档的描述如下
在这里插入图片描述
对于其他vbar_elX 的写入类似,不再赘述。

中断向量表详解

/*arch/arm64/kernel/entry.S*/
ENTRY(vectors)
	...
	kernel_ventry	1, sync				// Synchronous EL1h
	kernel_ventry	1, irq				// IRQ EL1h
	kernel_ventry	1, fiq_invalid			// 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_invalid			// FIQ 64-bit EL0
	kernel_ventry	0, error			// Error 64-bit EL0
	...

kernel_ventry 宏定义(忽略所有#ifdef)如下:

	.macro kernel_ventry, el, label, regsize = 64
	.align 7
	sub sp, sp, #S_FRAME_SIZE
	b   el\()\el\()_\label
	.endm

可以看到,“kernel_ventry 1, irq” 最终跳转到 el1_irq去执行

现场保护与中断栈切换

el1_irq:
	kernel_entry 1	/*保存现场*/
	enable_da_f (msr daifclr, #(8 | 4 | 1)) /*屏蔽了D(Debug mask bit)A(SError mask bit)I( IRQ mask bit) F(FIQ mask bit.)中的DAF*/

	irq_handler	/*进入中断处理*/

	kernel_exit 1	/*恢复现场*/
ENDPROC(el1_irq)

保存现场比较冗长,放到了文末。
接着屏蔽了DAIF中的DAF。这里看起来是不希望处理irq 的时候,被DAF 打断。具体为什么暂不探究了。。。
然后就开始进入中断处理irq_handler 了。处理完成后使用kernel_exit 恢复现场。
irq_handler 代码如下,

/*
 * Interrupt handling.
 */
	.macro	irq_handler
	ldr_l	x1, handle_arch_irq
	mov	x0, sp
	irq_stack_entry
	blr	x1
	irq_stack_exit
	.endm

注意,在跳转handle_arch_irq 之前与之后分使用irq_stack_entry 和irq_stack_exit 进行了栈指针切换。irq_stack_entry 代码如下

	.macro	irq_stack_entry
	mov	x19, sp	 // 将当前的栈指针sp保存到x19,在irq_stack_exit恢复栈指针时,再从x19中取出。疑问:如何在中断代码中保证x19的值不被覆盖?

	/*
	 * 将 sp 与任务堆栈的基数进行比较。如果顶部 ~(THREAD_SIZE - 1) 位匹配,我们在任务堆栈上,
	 * 并且应该切换到 irq 堆栈。
	 */
	ldr	x25, [tsk, TSK_STACK]
	eor	x25, x25, x19
	and	x25, x25, #~(THREAD_SIZE - 1)
	cbnz	x25, 9998f

	ldr_this_cpu x25, irq_stack_ptr, x26 /*读取当前cpu irq 栈指针*/
	mov	x26, #IRQ_STACK_SIZE
	add	x26, x25, x26	/*预留irq 栈空间,即,irq的栈使用不能超过IRQ_STACK_SIZE*/

	/* switch to the irq stack */
	mov	sp, x26
9998:
	.endm

handle_arch_irq 详解

从这里就完全进入c 代码了。先看下handle_arch_irq 是怎么来的。

/*kernel/irq/handle.c*/
void (*handle_arch_irq)(struct pt_regs *) __ro_after_init; /*这里定义了一个函数指针*/

IRQCHIP_DECLARE(gic_v3, "arm,gic-v3", gic_of_init);
	gic_of_init
		gic_init_bases
			set_handle_irq(handle_irq=gic_handle_irq)
				handle_arch_irq = handle_irq
gic_handle_irq
	

附:kernel_entry 保护现场详解

	.macro	kernel_entry, el, regsize = 64
	.if	\regsize == 32
	mov	w0, w0				// zero upper 32 bits of x0
	.endif
	stp	x0, x1, [sp, #16 * 0]
	stp	x2, x3, [sp, #16 * 1]
	stp	x4, x5, [sp, #16 * 2]
	stp	x6, x7, [sp, #16 * 3]
	stp	x8, x9, [sp, #16 * 4]
	stp	x10, x11, [sp, #16 * 5]
	stp	x12, x13, [sp, #16 * 6]
	stp	x14, x15, [sp, #16 * 7]
	stp	x16, x17, [sp, #16 * 8]
	stp	x18, x19, [sp, #16 * 9]
	stp	x20, x21, [sp, #16 * 10]
	stp	x22, x23, [sp, #16 * 11]
	stp	x24, x25, [sp, #16 * 12]
	stp	x26, x27, [sp, #16 * 13]
	stp	x28, x29, [sp, #16 * 14]

	.if	\el == 0
	clear_gp_regs
	mrs	x21, sp_el0
	ldr_this_cpu	tsk, __entry_task, x20	// Ensure MDSCR_EL1.SS is clear,
	ldr	x19, [tsk, #TSK_TI_FLAGS]	// since we can unmask debug
	disable_step_tsk x19, x20		// exceptions when scheduling.

	apply_ssbd 1, x22, x23

	.else
	add	x21, sp, #S_FRAME_SIZE
	get_thread_info tsk
	/* Save the task's original addr_limit and set USER_DS */
	ldr	x20, [tsk, #TSK_TI_ADDR_LIMIT]
	str	x20, [sp, #S_ORIG_ADDR_LIMIT]
	mov	x20, #USER_DS
	str	x20, [tsk, #TSK_TI_ADDR_LIMIT]
	/* No need to reset PSTATE.UAO, hardware's already set it to 0 for us */
	.endif /* \el == 0 */
	mrs	x22, elr_el1
	mrs	x23, spsr_el1
	stp	lr, x21, [sp, #S_LR]

	/*
	 * In order to be able to dump the contents of struct pt_regs at the
	 * time the exception was taken (in case we attempt to walk the call
	 * stack later), chain it together with the stack frames.
	 */
	.if \el == 0
	stp	xzr, xzr, [sp, #S_STACKFRAME]
	.else
	stp	x29, x22, [sp, #S_STACKFRAME]
	.endif
	add	x29, sp, #S_STACKFRAME

#ifdef CONFIG_ARM64_SW_TTBR0_PAN
	/*
	 * Set the TTBR0 PAN bit in SPSR. When the exception is taken from
	 * EL0, there is no need to check the state of TTBR0_EL1 since
	 * accesses are always enabled.
	 * Note that the meaning of this bit differs from the ARMv8.1 PAN
	 * feature as all TTBR0_EL1 accesses are disabled, not just those to
	 * user mappings.
	 */
alternative_if ARM64_HAS_PAN
	b	1f				// skip TTBR0 PAN
alternative_else_nop_endif

	.if	\el != 0
	mrs	x21, ttbr0_el1
	tst	x21, #TTBR_ASID_MASK		// Check for the reserved ASID
	orr	x23, x23, #PSR_PAN_BIT		// Set the emulated PAN in the saved SPSR
	b.eq	1f				// TTBR0 access already disabled
	and	x23, x23, #~PSR_PAN_BIT		// Clear the emulated PAN in the saved SPSR
	.endif

	__uaccess_ttbr0_disable x21
1:
#endif

	stp	x22, x23, [sp, #S_PC]

	/* Not in a syscall by default (el0_svc overwrites for real syscall) */
	.if	\el == 0
	mov	w21, #NO_SYSCALL
	str	w21, [sp, #S_SYSCALLNO]
	.endif

	/*
	 * Set sp_el0 to current thread_info.
	 */
	.if	\el == 0
	msr	sp_el0, tsk
	.endif

	/*
	 * Registers that may be useful after this macro is invoked:
	 *
	 * x21 - aborted SP
	 * x22 - aborted PC
	 * x23 - aborted PSTATE
	*/
	.endm

参考资料

1.DDI0487D_b_armv8_arm.pdf
2.DEN0024A_v8_architecture_PG.pdf
3. https://www.jianshu.com/p/a9b5bc27ec83
4. Generic Interrupt Controller Architecture Specification

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

linux arm64 中断处理流程完整分析 (一)—— 中断向量表、中断处理流程汇编部分 的相关文章

  • 如何更改 Apache 服务器的根目录? [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 如何更改 Apache 服务器的文档根目录 我基本上想要localhost从 来 users spencer projects目录而不是
  • 使用 Grep 查找两个短语之间的文本块(包括短语)

    是否可以使用 grep 来高亮所有以以下内容开头的文本 mutablePath CGPathCreateMutable 并以以下内容结尾 CGPathAddPath skinMutablePath NULL mutablePath 这两个短
  • 进程退出后 POSIX 名称信号量不会释放

    我正在尝试使用 POSIX 命名信号量进行跨进程同步 我注意到进程死亡或退出后 信号量仍然被系统打开 在进程 打开它 死亡或退出后是否有办法使其关闭 释放 早期的讨论在这里 当将信号量递减至零的进程崩溃时 如何恢复信号量 https sta
  • 确定我可以向文件句柄写入多少内容;将数据从一个 FH 复制到另一个 FH

    如何确定是否可以将给定数量的字节写入文件句柄 实际上是套接字 或者 如何 取消读取 我从其他文件句柄读取的数据 我想要类似的东西 n how much can I write w handle n read r handle buf n a
  • NUMA 在虚拟内存中是如何表示的?

    有许多资源 https en wikipedia org wiki Non uniform memory access从硬件角度描述NUMA的架构性能影响 http practical tech com infrastructure num
  • 如何查询X11显示分辨率?

    这似乎是一个简单的问题 但我找不到答案 如何查询 通过 X11 存在哪些监视器及其分辨率 查看显示宏 http tronche com gui x xlib display display macros html and 屏幕宏 http
  • 在 Mac OSX 上交叉编译 x86_64-unknown-linux-gnu 失败

    我尝试将我的 Rust 项目之一编译到 x86 64 unknown linux gnu 目标 cargo build target x86 64 unknown linux gnu Compiling deployer v0 1 0 fi
  • 监控子进程的内存使用情况

    我有一个 Linux 守护进程 它分叉几个子进程并监视它们是否崩溃 根据需要重新启动 如果父进程可以监视子进程的内存使用情况 以检测内存泄漏并在超出一定大小时重新启动子进程 那就太好了 我怎样才能做到这一点 您应该能够从 proc PID
  • Capistrano 3 部署无法连接到 GitHub - 权限被拒绝(公钥)

    我使用 Capistrano v3 和 capistrano symfony gem 设置了以下部署脚本 我正在使用 Ubuntu 14 4 部署到 AWS EC2 实例 我正在连接从 AWS 下载的 pem 文件 我的deploy rb中
  • 在 unix 中编译 dhrystone 时出错

    我是使用基准测试和 makefile 的新手 我已经从下面的链接下载了 Dhrystone 基准测试 我正在尝试编译它 但我遇到了奇怪的错误 我尝试解决它 但没有成功 有人可以帮助我运行 dhrystone 基准测试吗 以下是我尝试编译的两
  • Mcrt1.o和Scrt1.o有什么用?

    我坚持使用以下两个文件 即 Mcrt1 o 和 Scrt1 o 谁能帮我知道这两个文件的用途 如何使用它 我们以 gcrt1 o 为例 在使用 pg 选项编译进行性能测试时非常有用 谢谢 表格的文件 crt o总是 C 运行时启动代码 大部
  • 警告:请求的映像平台 (linux/amd64) 与检测到的主机平台 (linux/arm64/v8) 不匹配

    警告 请求的映像平台 linux amd64 与检测到的主机平台 linux arm64 v8 不匹配 并且未请求特定平台 docker 来自守护程序的错误响应 无法选择具有功能的设备驱动程序 gpu 我在 mac 上尝试运行此命令时遇到此
  • 使用 plistBuddy 获取值数组

    var keychain access groups declare a val usr libexec PlistBuddy c Print var sample plist echo val echo val 0 Ouput Array
  • 当用户按下打印时运行脚本,并且在脚本结束之前不开始假脱机(linux,cups)

    我需要做的是结合用户按下打印来执行 python 程序 脚本 并且在该程序退出之前不要让打印作业假脱机 原因是打印驱动程序不是开源的 我需要更改用户设置 在本例中是部门 ID 和密码 通常是每个用户 但因为这是一个信息亭 具有相同帐户的不同
  • Awk - 计算两个文件之间的每个唯一值和匹配值

    我有两个文件 首先 我尝试获取第 4 列中每个唯一字段的计数 然后匹配第二个文件的第二列中的唯一字段值 File1 第 4 列的每个唯一值和 File2 第 2 列包含我需要在两个文件之间匹配的值 所以本质上 我试图 gt 如果 file2
  • Linux 上的 RTLD_LOCAL 和dynamic_cast

    我们有一个由应用程序中的一些共享库构成的插件 我们需要在应用程序运行时更新它 出于性能原因 我们在卸载旧插件之前加载并开始使用新插件 并且只有当所有线程都使用旧插件完成后 我们才卸载它 由于新插件和旧插件的库具有相同的符号 我们dlopen
  • 如何从 C 程序中获取 NIC 详细信息?

    我想要获取连接到我的计算机的所有 NIC 的以下详细信息 1 接口名称 例如eth0 2 接口编号 如Windows http answers yahoo com question index qid 20080517041705AAOmJ
  • 如何将后台作业的输出分配给 bash 变量?

    我想在 bash 中运行后台作业并将其结果分配给一个变量 我不喜欢使用临时文件 并且希望同时运行多个类似的后台任务 root root var echo hello world root root echo var hello world
  • linux下写入后崩溃

    如果我使用 write 将一些数据写入磁盘上的文件会发生什么 但我的应用程序在刷新之前崩溃了 如果没有系统故障 是否可以保证我的数据最终会刷新到磁盘 如果您正在使用write 并不是fwrite or std ostream write 那
  • 如何访问 mmaped /dev/mem 而不导致 Linux 内核崩溃?

    我有一个简单的程序 尝试访问用户空间中的物理内存 其中内核存储第一个结构页 在 64 位机器上 该地址是 内核虚拟地址 ffffea0000000000 物理地址 0000620000000000 我正在尝试通过用户空间中的 mmap 访问

随机推荐

  • LQR控制基本原理(包括Riccati方程具体推导过程)

    全状态反馈控制系统 状态反馈控制器 通过选择K xff0c 可以改变的特征值 xff0c 进而控制系统表现 LQR控制器 最优控制 xff0c 其本质就是让系统以某种最小的代价来让系统运行 xff0c 当这个代价被定义为二次泛函 xff0c
  • 运行VINS-Fusion时找不到vins_node节点的问题解决

    问题 xff1a 在执行 rosrun vins vins node span class token operator span span class token operator span catkin ws span class to
  • Faster RCNN(Pytorch版本)代码及理论笔记

    文章目录 前言一 Faster RCNN整体流程二 PASCAL VOC2012数据集1 简介2 下载方式3 文件结构及含义 三 加载数据集四 数据预处理1 流程2 标准化数据3 缩放4 将图片处理为统一尺寸5 数据预处理的输入输出 五 B
  • K8S 网络CNI

    1 简介 CNI 容器网络接口 Container Network Interface xff1a 由Google和Core OS主导制定的容器网络标准 xff0c 它仅仅是一个接口 xff0c 具体的功能由各个网络插件自己去实现 xff1
  • 二叉树-前-中-后序遍历

    二叉树 二叉树概念 xff1a 1 空树 2 非空 xff1a 根节点 xff0c 根节点的左子树 xff0c 根节点的右子树组成 注意 xff01 注意 xff01 时刻记得二叉树是根 xff0c 根的左子树 xff0c 根的右子树 xf
  • 变量的声明与定义&&内部函数与外部函数

    1 变量的声明与定义 对于函数 声明部分是对有关标识符 xff08 变量 函数 结构体 xff09 的属性进行声明 xff1b 函数的声明是函数的原型 xff0c 而函数的定义是对函数功能的定义 对被调函数的声明是放在主调函数的声明部分 x
  • 《Java面向对象编程(阿里云大学)》笔记(文档+思维导图)

    课程链接 xff1a https edu aliyun com course 1011 xff08 还是建议去看课程 xff0c 笔记仅供参考 由于文中的所有内容均为手敲 xff0c 并且有些代码并未验证 xff0c 因此如有错误 xff0
  • 《JDBC数据库开发进阶(阿里云大学》笔记(文档+思维导图)

    第1章 xff1a 事务处理 课时1 xff1a 事务的四大特性 xff08 ACID xff09 课时2 xff1a MySQL中开启和关闭事务 课时3 xff1a JDBC中完成事务处理 在JDBC中处理事务 xff0c 都是通过Con
  • PyCharm使用教程 --- 7、使用PyCharm进行DeBug调试

    很多新手朋友对PyCharm的使用无从下手 xff0c 于是花费了一点时间整理这份PyCharm操作手册 xff0c 完整PDF下载 xff1a 终于写完了 xff01 PyCharm操作手册 V1 0版本 PDF下载 目录如下 xff1a
  • FreeRTOS中断和任务之间的队列,自定义串口通讯协议

    本文提供这样一种方法 xff1a FreeRTOS中串口接收数据中断 xff0c 然后通过队列将数据传递给任务A xff0c 在任务A中对数据进行处理 xff0c 串口使用的通讯协议为自定义 依次给出了串口的初始化 中断服务函数 任务A x
  • 适用于FreeRTOS初学者,FreeRTOS整体知识框架

    写在前面 xff1a 因为实际使用需求 xff0c 学习了一段时间FreeRTOS 从FreeRTOS的市场占有率来看 xff0c 网上的资料应该很多 xff0c 但是在学习过程中尤其是遇到问题的时候 xff0c 发现真正有用的资料并不多
  • 串口通信float型数据的处理和发送;大端小端;联合体union占用字节大小;结构体的定义

    在介绍float型数据的处理和发送之前 xff0c 先介绍一下大端和小端以及联合体的大小分析 一 什么是大端小端 xff1f 如何测试你的CPU是大端还是小端 xff1f 1 大端小端 xff1a 小端 xff1a 采用小端模式的CPU对操
  • Python中以下划线开头的标识符

    1 以单下划线开头的变量 例如 foo代表禁止外部访问的类成员 xff0c 需通过类提供的接口进行访问 xff0c 不能用 34 from xxx import 34 导入 2 以双下划线开头的变量 例如 foo xff0c 代表类的私有成
  • 【CentOS 7】命令行安装GNOME、KDE图形界面(转载)

    目录 正文 一 进入 root 模式 二 安装 X 窗口系统 三 安装图形界面软件 GNOME 四 更新系统的默认运行级别 正文 CentOS 7 默认是没有图形化界面的 xff0c 但我们很多人在习惯了 Windows 的图形化界面之后
  • Git子模块使用教程

    Git子模块 1 问题背景 随着产品的日益增多 xff0c 各个产品之间的业务功能会出现高度的相同性 xff0c 比如产品A有串口的接收功能 xff0c 产品B也有相同的串口功能 xff0c 这类功能我们可以写成一个通用的串口接收模块 这样
  • K8S Flannel

    1 简介 Flannel 由CoreOS开发 xff0c 用于解决docker集群跨主机通讯的覆盖网络 overlay network xff0c 它的主要思路是 xff1a 预先留出一个网段 xff0c 每个主机使用其中一部分 xff0c
  • 阿里云服务器VNC使用步骤

    1 控制台设置 2 VNC桌面连接设置 yum安装太难 xff0c 不建议 分两步 xff1a 1 安装yum 2 安装VNC ubuntu 16 04中安装yum 在Ubuntu系统中按住 xff1a ctrl 43 alt 43 T 打
  • vscode 配置git

    下载git https git scm com 安装时 xff0c 直接默认所有选项安装 然后打开git安装目录 找到如下路径 打开vscode 点击文件 找到 首选项 点击设置 在搜索框搜索 git path 编辑settings jso
  • Intel D405 运行环境——Realsense-viewer

    第一章 Intel D405 运行环境 Realsense viewer 文章目录 第一章 Intel D405 运行环境 Realsense viewer一 开盲盒二 ubuntu环境下的realsense viewer安装 一 开盲盒
  • linux arm64 中断处理流程完整分析 (一)—— 中断向量表、中断处理流程汇编部分

    中断流程老生常谈 xff0c 但我一直以来也只是知道中断过来之后 xff0c 会保护现场 xff0c 跳到中断向量表 xff0c 执行中断 xff0c 恢复现场 xff0c 然后返回 至于更多细节 xff0c 就不得而知了 这篇文章旨在把更