Linux arm64异常简介和系统调用过程

文章目录

  • 一、异常简介
    • 1.1 Exception levels
    • 1.2 异常类型
  • 二、系统调用简介
    • 2.1 SVC指令
    • 2.2 VBAR
    • 2.3 系统调用保存现场
    • 2.4 系统调用返回
  • 三、Linux 内核分析
  • 参考资料

一、异常简介

在ARM64体系架构中,异常是处理器在执行指令时可能遇到的不寻常情况或事件。这些异常可以是由软件或硬件引发的。ARM64体系架构定义了一套异常模型,用于处理和响应这些异常情况。

1.1 Exception levels

Armv8-A体系结构定义了一组异常级别,EL0到EL3,其中:

● 如果ELn是异常级别,则n的值增加表示软件执行权限增加。
● 在EL0执行称为无特权执行。EL0为非特权模块,应用层。
● 在EL1执行称为特权特权执行。EL1为特权模块,操作系统内核层。
● EL2提供对虚拟化的支持。
● EL3支持在两种安全状态(安全状态和非安全状态)之间切换。

EL0 Applications.
EL1 OS kernel and associated functions that are typically described as privileged.
EL2 Hypervisor.
EL3 Secure monitor.

一个armv8系统实现可能不包括所有的Exception级别。所有实现都必须包括EL0和EL1。EL2和EL3是可选的。

从上面可看到armv8最大支持EL0~EL3四个exception level,EL0的execution privilege最低,EL3的execution privilege最高。当发生异常的时候,系统的exception会迁移到更高的exception level或者维持不变,但是绝不会降低。此外,不会有任何的异常会去到EL0。

1.2 异常类型

在Linux ARM64架构中,同步异常和异步异常与处理器的异常处理机制和中断控制器有关。

(1)同步异常(Synchronous Exceptions):
在ARM64架构中,同步异常与当前指令的执行直接相关,它们在指令执行期间同步地引发。常见的同步异常包括:

●未定义指令异常:当尝试执行在ARM64架构中未定义的指令时,会引发该异常。包括在不适当的异常级别执行指令、禁用的指令、以及未被分配的指令位模式。

●非法执行状态异常:当PSTATE.IL(非法执行状态)设置为1时,尝试执行指令会引发该异常。

●堆栈指针(SP)未对齐异常:当堆栈指针未按照对齐要求进行对齐时,会触发该异常。

●程序计数器(PC)未对齐异常:当尝试执行具有错误对齐的指令时,会引发该异常。

●引发异常的指令:特定指令(如SVC、HVC或SMC)可能触发异常。

●被捕获指令异常:当尝试执行被定义为在较高异常级别被捕获的指令时,会引发该异常。

●内存相关异常:由内存地址转换系统引发的异常,包括与指令执行或内存访问相关的指令异常和数据异常。

●数据地址未对齐异常:当尝试使用错误对齐的地址访问内存时,会触发该异常。

●调试异常:与调试相关的各种异常,例如断点指令异常、断点异常、观测点异常、向量捕获异常和软件单步异常。

●被捕获的浮点异常(如果支持):在支持浮点异常捕获的实现中,当发生被捕获的IEEE浮点异常时,会引发该异常。

对于同步异常,Linux内核会根据异常类型执行相应的异常处理程序,例如调用适当的异常处理函数、进行错误处理或发送信号给相关进程。

(2)异步异常(Asynchronous Exceptions):
在ARM64架构中,异步异常是与当前指令执行无直接关联的异常,它们以异步的方式引发。常见的异步异常包括:

在Armv8-A架构中,被带入AArch64状态的异步异常也被称为中断。中断分为两种类型:
物理中断(Physical interrupts):这些是从处理器外部发送给处理器的信号。它们包括:

SError:系统错误中断。
IRQ:普通中断请求。
FIQ:快速中断请求。

虚拟中断(Virtual interrupts):软件在EL2执行时可以启用和挂起的中断。虚拟中断从EL0或EL1传递到EL1。
虚拟中断的名称与物理中断对应:

vSError:虚拟系统错误中断。
vIRQ:虚拟普通中断请求。
vFIQ:虚拟快速中断请求。

对于异步异常,ARM64架构中的处理方式通常是由中断控制器来管理和处理。中断控制器会根据中断优先级和配置,将异步事件传递给处理器,并触发相应的中断处理程序。

二、系统调用简介

2.1 SVC指令

系统调用属于异常的同步软件异常。

系统调用是通过执行SVC、HVC或SMC指令触发的,这里我们指讨论SVC指令:
默认情况下,执行SVC指令会生成一个Supervisor Call,这是一个针对EL1的同步异常。这为在EL0执行的软件提供了一种调用在EL1执行的操作系统或其他软件的机制。

在ARM64体系架构中EL0是用户空间,EL1是内核空间。

SVC指令提供了在不同执行级别(EL)之间进行通信和交互的机制。通过执行SVC指令,EL0中的用户空间程序可以请求EL1中运行的操作系统或其他软件执行特权操作。

SVC Generate exception targeting Exception level 1
Supervisor Call causes an exception to be taken to EL1.

2.2 VBAR

当处理器处于使用AArch64执行状态时,当处理器执行到一个异常级别(Exception level)引发异常时,执行将被强制转移到异常向量(exception vector)所指示的地址上。异常向量表(vector table)位于该异常级别的内存中,占据一系列以字对齐的地址。

在ARMv8-A体系结构中,每个异常级别都有一个关联的向量基地址寄存器(Vector Base Address Register,VBAR),它定义了该异常级别对应的异常基地址。向量基地址决定了异常向量表在特定异常级别下的起始地址。当异常发生时,处理器使用与当前异常级别相关联的VBAR寄存器来查找相应的向量表,并跳转到该表中对应的异常向量。

通过为每个异常级别使用独立的向量表和VBAR寄存器,ARMv8-A体系结构在不同特权级别下的异常处理中提供了灵活性和定制性。该机制能够高效处理异常和中断,确保在不同情况下的适当处理和恢复。

处理器在用户空间EL0执行系统调用时,即执行svc指令,发生了异常,处理器跳转和执行相关的异常处理指令。异常相关的处理指令存储在一个表中,即异常向量表。对于ARM64体系架构,EL1对应一个异常向量表,其地址存放在向量基址寄存器(VBAR_EL1, Vector Base Address Register )中。

对于AArch64状态的异常,the vector table提供以下信息:
以下是关于异常的一些信息
(1)异常类型:

— Synchronous exception.
— SError.
— IRQ.
— FIQ

(2)异常级别和相关信息:
异常发生的异常级别:指示异常发生的特权级别,例如EL0(用户级别)、EL1(操作系统级别)等。
正在使用的堆栈指针:用于跟踪异常处理期间的堆栈操作。
寄存器文件的状态:指示异常发生时寄存器文件中各个寄存器的值和状态。
在这里插入图片描述
说明:
(1)这个表有四个异常条目,每个异常条目有四种异常类型:同步异常(Synchronous exception),IRQ,FIQ和SError。
(2)每一个异常入口占用0x80 bytes空间,每一个异常入口可以放置多32条指令。ARMv8指令集支持64位指令集,但每一条指令的位宽是32位,而不是64位。

0x80(128) / 4 = 32 

四个异常条目:
(1)如果发生异常并不会导致exception level切换,并且使用的栈指针是SP_EL0,对应着第一个异常条目。
(2)如果发生异常并不会导致exception level切换,并且使用的栈指针是SP_EL1/2/3,对应着第二个异常条目。
(3)如果发生异常会导致exception level切换,并且比目的exception level低一级的exception level运行在AARCH64模式,对应着第三个异常条目。
(4)如果发生异常会导致exception level切换,并且比目的exception level低一级的exception level运行在AARCH32模式,对应着第四个异常条目。

对于AARCH64模式异常指令svc,处理器会导致exception level切换,异常等级从EL0切换到EL1,对应着第三个异常条目:如果发生异常会导致exception level切换,并且比目的exception level低一级的exception level运行在AARCH64模式,那么使用第三个异常条目。

Lower Exception level, where the implemented level immediately lower than the target level is using AArch64.b

对于异常指令svc属于同步异常(Synchronous exception),因此svc异常处理器会跳转到 VBAR_EL1 + 0x400 地址处的异常向量中。

2.3 系统调用保存现场

当系统调用从用户态到内核态的时候,首先要做的第一件事情,就是将用户态运行过程中的 CPU 上下文保存起来,其实主要就是保存在这个结构的寄存器变量里。这样当从内核系统调用返回的时候,才能让进程在刚才的地方接着运行下去。

应用层程序执行svc系统调用指令时,会将用户态此时所有通用寄存器的状态保存起来,以便从系统调用返回时恢复状态。将进程用户态此时所有通用寄存器的状态保存在该进程的内核栈中的pt_regs栈框中。

在内核栈的最高地址端,存放的是另一个结构 pt_regs,这个结构体保存着进程从应用层进入到内核层时,用户态寄存器的状态。
struct pt_regs结构体:

/** This struct defines the way the registers are stored on the stack during an* exception. Note that sizeof(struct pt_regs) has to be a multiple of 16 (for* stack alignment). struct user_pt_regs must form a prefix of struct pt_regs.*/
struct pt_regs {union {struct user_pt_regs user_regs;struct {u64 regs[31];u64 sp;u64 pc;u64 pstate;};};u64 orig_x0;
#ifdef __AARCH64EB__u32 unused2;s32 syscallno;
#elses32 syscallno;u32 unused2;
#endifu64 orig_addr_limit;/* Only valid when ARM64_HAS_IRQ_PRIO_MASKING is enabled. */u64 pmr_save;u64 stackframe[2];
};

如下图所示:
在这里插入图片描述
关于进程内核栈请参考:Linux 进程管理之内核栈和struct pt_regs

2.4 系统调用返回

当操作系统的异常处理(这里只描述svc异常)完成后,执行一条ERET指令就可以从异常返回。寄存器ELR_ELx存放svc指令异常返回的地址,发生系统调用svc指令时,系统肯定是在用户空间的地址,将svc指令的下一条指令的地址保存在寄存器ELR_ELx中,当系统调用返回时,即执行ERET指令时,返回svc异常指令现场,从寄存器ELR_ELx取出svc指令的下一条指令的地址,执行该指令。执行ERET指令时会从寄存器ELR_ELx恢复PC指针。

三、Linux 内核分析

(1)汇编入口:

// linux-5.4.18/arch/arm64/kernel/entry.S/** Exception vectors.*/.pushsection ".entry.text", "ax".align	11
ENTRY(vectors)kernel_ventry	1, sync_invalid			// Synchronous EL1tkernel_ventry	1, irq_invalid			// IRQ EL1tkernel_ventry	1, fiq_invalid			// FIQ EL1tkernel_ventry	1, error_invalid		// Error EL1tkernel_ventry	1, sync				// Synchronous EL1hkernel_ventry	1, irq				// IRQ EL1hkernel_ventry	1, fiq_invalid			// FIQ EL1hkernel_ventry	1, error			// Error EL1hkernel_ventry	0, sync				// Synchronous 64-bit EL0kernel_ventry	0, irq				// IRQ 64-bit EL0kernel_ventry	0, fiq_invalid			// FIQ 64-bit EL0kernel_ventry	0, error			// Error 64-bit EL0#ifdef CONFIG_COMPATkernel_ventry	0, sync_compat, 32		// Synchronous 32-bit EL0kernel_ventry	0, irq_compat, 32		// IRQ 32-bit EL0kernel_ventry	0, fiq_invalid_compat, 32	// FIQ 32-bit EL0kernel_ventry	0, error_compat, 32		// Error 32-bit EL0
#elsekernel_ventry	0, sync_invalid, 32		// Synchronous 32-bit EL0kernel_ventry	0, irq_invalid, 32		// IRQ 32-bit EL0kernel_ventry	0, fiq_invalid, 32		// FIQ 32-bit EL0kernel_ventry	0, error_invalid, 32		// Error 32-bit EL0
#endif
END(vectors)

Linux 内核使用vectors作为异常向量表,刚好和arm官方手册Vector Base Address Register (VBAR)的相对应。

前面说到对于同步异常指令svc,处理器会跳转到 VBAR_EL1 + 0x400 地址处的异常向量中。
VBAR_EL1 存放的是vectors的基地址。

一个表项128字节,十六进制即 0x80。

偏移异常描述
Current Exception level with SP_EL0
0x00kernel_ventry 1, sync_invalid // Synchronous EL1t
0x80kernel_ventry 1, irq_invalid // IRQ EL1t
0x100kernel_ventry 1, fiq_invalid // FIQ EL1t
0x180kernel_ventry 1, error_invalid // Error EL1t
Current Exception level with SP_ELx, x>0
0x200kernel_ventry 1, sync // Synchronous EL1h
0x280kernel_ventry 1, irq // IRQ EL1h
0x300kernel_ventry 1, fiq_invalid // FIQ EL1h
0x380kernel_ventry 1, error // Error EL1h
Lower Exception level, where the implemented level immediately lower than the target level is using AArch64
0x400kernel_ventry 0, sync // Synchronous 64-bit EL0
0x480kernel_ventry 0, irq // IRQ 64-bit EL0
0x500kernel_ventry 0, fiq_invalid // FIQ 64-bit EL0
0x580kernel_ventry 0, error // Error 64-bit EL0
Lower Exception level, where the implemented level immediately lower than the target level is using AArch32
0x600kernel_ventry 0, sync_invalid, 32 // Synchronous 32-bit EL0
0x680kernel_ventry 0, irq_invalid, 32 // IRQ 32-bit EL0
0x700kernel_ventry 0, fiq_invalid, 32 // FIQ 32-bit EL0
0x780kernel_ventry 0, error_invalid, 32 // Error 32-bit EL0

vectors的基地址 + 0x400 =

kernel_ventry	0, sync				// Synchronous 64-bit EL0
// linux-5.4.18/arch/arm64/kernel/entry.S/** EL0 mode handlers.*/.align	6
el0_sync:kernel_entry 0mrs	x25, esr_el1			// read the syndrome registerlsr	x24, x25, #ESR_ELx_EC_SHIFT	// exception classcmp	x24, #ESR_ELx_EC_SVC64		// SVC in 64-bit stateb.eq	el0_svccmp	x24, #ESR_ELx_EC_DABT_LOW	// data abort in EL0b.eq	el0_dacmp	x24, #ESR_ELx_EC_IABT_LOW	// instruction abort in EL0b.eq	el0_ia......

在el0_sync汇编函数中,首先通过kernel_entry保存异常现场。然后从esr_el1寄存器中读取异常类型(EC),当异常类型为ESR_ELx_EC_SVC64是,跳转到 el0_svc 汇编函数。

// linux-5.4.18/arch/arm64/kernel/entry.S/** SVC handler.*/.align	6
el0_svc:gic_prio_kentry_setup tmp=x1mov	x0, spbl	el0_svc_handlerb	ret_to_user
ENDPROC(el0_svc)

el0_svc 汇编函数跳转到 el0_svc_handler 函数。

(2)C语言入口

// linux-5.4.18/arch/arm64/kernel/syscall.casmlinkage void el0_svc_handler(struct pt_regs *regs)
{el0_svc_common(regs, regs->regs[8], __NR_syscalls, sys_call_table);
}
// linux-5.4.18/arch/arm64/include/asm/ptrace.h/** This struct defines the way the registers are stored on the stack during an* exception. Note that sizeof(struct pt_regs) has to be a multiple of 16 (for* stack alignment). struct user_pt_regs must form a prefix of struct pt_regs.*/
struct pt_regs {union {struct user_pt_regs user_regs;struct {u64 regs[31];u64 sp;u64 pc;u64 pstate;};};u64 orig_x0;......
};
// linux-5.4.18/include/uapi/asm-generic/unistd.h#define __NR_syscalls 436
// linux-5.4.18/arch/arm64/include/asm/syscall.htypedef long (*syscall_fn_t)(const struct pt_regs *regs);// linux-5.4.18/arch/arm64/kernel/sys.cconst syscall_fn_t sys_call_table[__NR_syscalls] = {[0 ... __NR_syscalls - 1] = __arm64_sys_ni_syscall,
#include <asm/unistd.h>
};
static void el0_svc_common(struct pt_regs *regs, int scno, int sc_nr,const syscall_fn_t syscall_table[])
{regs->orig_x0 = regs->regs[0];regs->syscallno = scno;invoke_syscall(regs, scno, sc_nr, syscall_table);}
static void invoke_syscall(struct pt_regs *regs, unsigned int scno,unsigned int sc_nr,const syscall_fn_t syscall_table[])
{long ret;if (scno < sc_nr) {syscall_fn_t syscall_fn;syscall_fn = syscall_table[array_index_nospec(scno, sc_nr)];ret = __invoke_syscall(regs, syscall_fn);}regs->regs[0] = ret;
}
static long __invoke_syscall(struct pt_regs *regs, syscall_fn_t syscall_fn)
{return syscall_fn(regs);
}

(3)流程简介

el0 svc-->el1 vectors-->kernel_ventry	0, sync				// Synchronous 64-bit EL0-->el0_sync-->el0_svc-->el0_svc_handler-->el0_svc_common-->invoke_syscall-->__invoke_syscall-->syscall_fn(regs)

从系统调用表中sys_call_table根据系统调用号取出对应的系统调用回调函数,然后去执行对应的系统调用回调函数。

参考资料

Linux 5.4.18
armv8手册

https://blog.csdn.net/luteresa/article/details/120263414
https://zhuanlan.zhihu.com/p/578252899
http://www.wowotech.net/238.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/177629.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CountDownLatch使用

常用于多线程场景&#xff0c;待多线程都结束后方可继续主线程逻辑处理 CodeConstant 常量类 import java.util.HashMap; import java.util.Map;public class CodeConstant {public static final Map<String, Map<String, String>> CODE new HashMap<>();…

Android 启动优化案例-WebView非预期初始化排查

作者&#xff1a;邹阿涛涛涛涛涛涛 去年年底做启动优化时&#xff0c;有个比较好玩的 case 给大家分享下&#xff0c;希望大家能从我的分享里 get 到我在做一些问题排查修复时是怎么看上去又low又土又高效的。 1. 现象 在我们使用 Perfetto 进行app 启动过程性能观测时&#…

2023年【电工(高级)】考试报名及电工(高级)考试试卷

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2023年【电工&#xff08;高级&#xff09;】考试报名及电工&#xff08;高级&#xff09;考试试卷&#xff0c;包含电工&#xff08;高级&#xff09;考试报名答案和解析及电工&#xff08;高级&#xff09;考试试卷…

isomorphic-fetch库代码示例

isomorphic-fetch库的爬虫程序。 typescript // 引入isomorphic-fetch库 import fetch from isomorphic-fetch; // 设置 const proxy ; // 定义视频URL const url ; // 使用fetch获取视频数据 fetch(url, { method: GET, headers: { Accept: application/json, …

2023 PostgreSQL 数据库生态大会:解读拓数派大数据计算系统及其云存储底座

11月3日-5日&#xff0c;由中国开源软件推进联盟 PostgreSQL 分会主办的中国 PostgreSQL 数据库生态大会在北京中科院软件所隆重举行。大会以”极速进化融合新生”为主题&#xff0c;从线下会场和线上直播两种方式展开&#xff0c;邀请了数十位院士、教授、高管和社群专家&…

YOLOv8优化策略:全新的聚焦式线性注意力模块Focused Linear Attention | ICCV2023

🚀🚀🚀本文改进:深入分析了现有线性注意力方法的缺陷,并提出了一个全新的聚焦的线性注意力模块(Focused Linear Attention),同时具有高效性和很强的模型表达能力。 🚀🚀🚀YOLOv8改进专栏:http://t.csdnimg.cn/hGhVK 学姐带你学习YOLOv8,从入门到创新,轻轻…

Cesium 展示——坐标间的转换

文章目录 需求1. 点击位置会后获取的地球坐标2. 笛卡尔坐标(Cartesian3)3. 地理坐标系分析转换关系如下需求 坐标间的转换 1. 点击位置会后获取的地球坐标 Cesium点击位置会后获取的地球坐标。 2. 笛卡尔坐标(Cartesian3) 笛卡尔坐标系中,表示一个在 x 轴上、y轴上、…

Fedora Linux 39 正式版官宣 11 月 发布

导读Fedora Linux 39 正式版此前宣布将于 10 月底发布&#xff0c;不过这款 Linux 发行版面临了一些延期&#xff0c;今天开发团队声称&#xff0c;Fedora Linux 39 正式版将于 11 月 7 日发布。 过查询得知&#xff0c;在近日的 "Go / No-Go" 会议上&#xff0c;开…

轻量封装WebGPU渲染系统示例<28>- MRT纹理(源码)

当前示例源码github地址: https://github.com/vilyLei/voxwebgpu/blob/feature/rendering/src/voxgpu/sample/MRT.ts 当前示例运行效果: 此示例基于此渲染系统实现&#xff0c;当前示例TypeScript源码如下: const colorRTTTex { diffuse: { uuid: "colorRTT", rtt…

若依前后分离版框架下Springboot java引入Mqtt接受发送消息

**这只是其中一种而且是粗浅的接、发消息。 同步机制还要跟搞物联网的同事沟通确认去看看能不能实现 或者是设备比较多的情况下 不会去使用同步机制 首先pom文件 引入依赖 ** <dependency><groupId>org.eclipse.paho</groupId><artifactId>org.eclipse…

全方位移动机器人 Stanley 轨迹跟踪 Gazebo 仿真

全方位移动机器人 Stanley 轨迹跟踪 Gazebo 仿真 本来打算今天出去跑一下 GPS&#xff0c;但是下雨&#xff0c;作罢 添加参考轨迹信息 以下三个功能包不需要修改&#xff1a; mrobot&#xff1a;在 Rviz 和 Gazebo 中仿真机器人cmd_to_mrobot&#xff1a;运动学解算&#…

最佳实践-使用Github Actions来构建跨平台容器镜像

公众号「架构成长指南」&#xff0c;专注于生产实践、云原生、分布式系统、大数据技术分享。 前言 最近在写K8s的相关系列文章&#xff0c;因为有涉及到镜像构建&#xff0c;发现在Mac m1的Arm架构下构建的部分镜像&#xff0c;没法在X86架构下使用&#xff0c;不兼容。 尝试…