linux ARM64 异常

news/2025/3/21 10:45:54/

linux 的系统调用是通过指令陷入不同异常级别实现的。arm64 架构的 cpu 的异常级别结构如下:

在上图中,用户层运行在 EL0 也就是异常级别 0,Linux 内核运行在 EL1 也就是异常级别 1,安全可信操
作系统运行在异常级别 2:EL2,安全监控模块运行在异常级别 3:EL3。
上述提到的用户层和内核大家都熟悉,为了介绍异常级别的完整性,我们简单说一下这里的 EL2 和 EL3,
这两个异常级别实际为了实现 TEE(Trusted Execution Environment)即可信执行环境的一种硬件架构,在 ARM 上
称之为 TrustZone,TrustZone 是 ARM 针对消费电子设备设计的一种硬件架构,其目的是为消费电子产品构建
一个安全框架来抵御各种可能的攻击。TrustZone 在概念上将 SoC 的硬件和软件资源划分为安全(Secure World)
和非安全(Normal World)两个世界,所有需要保密的操作在安全世界执行(如指纹识别、密码处理、数据加解密、
安全认证等),其余操作在非安全世界执行(如用户操作系统、各种应用程序等),安全世界和非安全世界通过
一个名为 Monitor Mode 的模式进行转换。这里了解一下就行,针对于系统调用我们涉及不到这个。
介绍完异常级别,下面介绍一下在 ARM64 下,是如何陷入不同运行级别的:
1)  异常级别 0 使用 svc( Supervisor Call)指令陷入异常级别 1
2)  异常级别 1 使用 hvc(Hypervisor Call)指令陷入异常级别 2
3)  异常级别 2 使用 smc(Secure Monitor Call)指令陷入异常级别 3
系统调用就是运行在异常级别 0 的用户程序,通过使用 svc 指令陷入异常级别 1,来完成的陷入动作,之后
会根据异常向量表,执行异常向量服务程序。本文以 ARM64 为例来看异常向量表的配置,内核在
arch/arm64/kernel/entry.S 汇编代码中设置了异常向量表。
在介绍异常向量表之前,先介绍一下异常。在 ARM64 体系结构中,异常分为同步异常和异步异常。同步异
常是试图执行指令时生成的异常,或是作为指令的执行结果生成的异常。同步异常包括如下。
1)  系统调用。异常级别 0 使用 svc( Supervisor Call)指令陷入异常级别 1,异常级别 1 使用 hvc
(Hypervisor Call)指令陷入异常级别 2,异常级别 2 使用 smc(Secure Monitor Call)指令陷入异常级
别 3。
2)  数据中止,即访问数据时的页错误异常,虚拟地址没有映射到物理地址,或者没有写权限。
3)  指令中止,即取指令时的页错误异常,虚拟地址没有映射到物理地址,或者没有执行权限。
4)  栈指针或指令地址没有对齐。
5)  没有定义的指令。
6)  调试异常。
异步异常不是由正在执行的指令生成的,和正在执行的指令没有关联。异步异常包括以下。
1)  中断( normal priority interrupt, IRQ),即普通优先级的中断。
2)  快速中断( fast interrupt, FIQ),即高优先级的中断。

3)  系统错误( System Error, SError),是由硬件错误触发的异常,例如最常见的是把脏数据从缓存行写
回内存时触发异步的数据中止异常。

/*
* Exception vectors.
*/
.pushsection ".entry.text", "ax"
.align 11
ENTRY(vectors)
kernel_ventry 1, sync_invalid //异常级别 1 生成的同步异常,使用栈指针寄存器 SP_EL0
kernel_ventry 1, irq_invalid //异常级别 1 生成的中断,使用栈指针寄存器 SP_EL0
kernel_ventry 1, fiq_invalid //异常级别 1 生成的快速中断,使用栈指针寄存器 SP_EL0
kernel_ventry 1, error_invalid //异常级别 1 生成的系统错误,使用栈指针寄存器 SP_EL0
kernel_ventry 1, sync //异常级别 1 生成的同步异常,使用栈指针寄存器 SP_EL1
kernel_ventry 1, irq //异常级别 1 生成的中断,使用栈指针寄存器 SP_EL1
kernel_ventry 1, fiq_invalid //异常级别 1 生成的快速中断,使用栈指针寄存器 SP_EL1
kernel_ventry 1, error_invalid //异常级别 1 生成的系统错误,使用栈指针寄存器 SP_EL1
kernel_ventry 0, sync //64 位应用程序在异常级别 0 生成的同步异常
kernel_ventry 0, irq // 64 位应用程序在异常级别 0 生成的中断
kernel_ventry 0, fiq_invalid // 64 位应用程序在异常级别 0 生成的快速中断
kernel_ventry 0, error_invalid //64 位应用程序在异常级别 0 生成的系统错误
#ifdef CONFIG_COMPAT
kernel_ventry 0, sync_compat, 32 //32 位应用程序在异常级别 0 生成的同步异常
kernel_ventry 0, irq_compat, 32 // 32 位应用程序在异常级别 0 生成的中断
kernel_ventry 0, fiq_invalid_compat, 32 // 32 位应用程序在异常级别 0 生成的快速中断
kernel_ventry 0, error_invalid_compat, 32 // 32 位应用程序在异常级别 0 生成的系统错误
#else
kernel_ventry 0, sync_invalid, 32 //32 位应用程序在异常级别 0 生成的同步异常
kernel_ventry 0, irq_invalid, 32 // 32 位应用程序在异常级别 0 生成的中断
kernel_ventry 0, fiq_invalid, 32 // 32 位应用程序在异常级别 0 生成的快速中断
kernel_ventry 0, error_invalid, 32 // 32 位应用程序在异常级别 0 生成的系统错误
#endif
END(vectors)

上面的代码进一步展开,就是设置不同 mode 下的异常向量表,异常可以分为 4 组,每组异常有 4 个,所以
这里一共会设置 16 个 entry。4 组异常分别对应 4 种情况下发生异常时的处理。以下是 4 个异常向量 entry 的含
义:
1)  同步异常
2)  中断
3)  快速中断
4)  系统错误

kernel_ventry 是一个宏,参数是跳转标号,即异常处理程序的标号,宏的定义如下(/arch/arm64/kernel/entry.S):
.macro kernel_ventry, el, label, regsize = 64
.align 7
sub sp, sp, #S_FRAME_SIZE //将 sp 预留一个 fram_size, 这个 size 就是 struct pt_regs 的
大小
#ifdef CONFIG_VMAP_STACK
//....这里省略掉检查栈溢出的代码
#endif
b el\()\el\()_\label // 跳转到对应级别的异常处理函数
.endm
“ .align 7”表示把下一条指令的地址对齐到 2^7,即对齐到 128; 对于向量表 vectors 中的 kernel_entry 0, sync,
则 b el\()\el\()_\label 跳转到 el0_sync 函数。 其中 0 表示的是从哪个异常模式产生的,比如是 user->kernel 就是
0., kernel->kernel 就是 1。
实现的函数有 el1_sync,el1_irq,el0_sync,el0_irq,el1_sync_compat,el1_irq_compat 其余都是 invalid。从
异常级别 1 的异常向量表可以看出如下内容。
1)  有些异常向量的跳转标号带有“invalid”,说明内核不支持这些异常,例如内核不支持 ARM64 处
理器的快速中断。
2)  对于内核模式(异常级别 1)生成的异常, Linux 内核选择使用异常级别 1 的栈指针寄存器。
3)  对于内核模式(异常级别 1)生成的同步异常,入口是 el1_sync。
4)  如果处理器处在内核模式(异常级别 1),中断的入口是 el1_irq。
5)  对于 64 位应用程序在用户模式(异常级别 0)下生成的同步异常,入口是 el0_sync。
6)  如果处理器正在用户模式(异常级别 0)下执行 64 位应用程序,中断的入口是 el0_irq。
7)  对于 32 位应用程序在用户模式(异常级别 0)下生成的同步异常,入口是 el0_sync_compat。
8)  如果处理器正在用户模式(异常级别 0)下执行 32 位应用程序,中断的入口是 el0_irq_compat。
前面设置了异常向量表,我们来进一步查看 SVC mode 的处理。当系统调用时 CPU 会切换到 SVC mode,并
跳转到对应的地址去运行。kernel 中会配置两个 SVC Handler,分别对应这 SVC_32/SVC_64 两种 mode,32bit
程序和 64bit 程序执行系统调用会跳转到两个不同的 handler 去执行。


http://www.ppmy.cn/news/1282297.html

相关文章

H266/VVC帧间预测编码技术概述

帧间预测编码简述 帧间预测利用视频时间域的相关性,使用邻近已编码图像像素值预测当前图像的像素值,能有效去除视频时域冗余。 目前主要的视频编码标准中,帧间预测都采用基于块的运动补偿技术,不同的编码标准有不同的分块方式。 …

软件系统测试报告基础知识分享,专业的CMA、CNAS软件测试公司推荐

软件系统测试是将已经集成好的软件系统,作为整个基于计算机系统的一个元素,与计算机硬件、外设、某些支持软件、数据和人员等其他系统元素结合在一起,在实际运行(使用)的环境下,对计算机系统进行一系列的组装测试和确认测试。 系…

【使用Ubuntu编译FFmpeg生成Android动态库/静态库】

环境 我这里使用windows里的wsl2的ubuntu,使用物理机或者vmware,vbox之类的安装的ubuntu理论上也可以. gcc编译使用的环境如下: Ndk使用17 FFmpeg使用4.0.2. clang编译使用的环境如下: Ndk使用21.4 FFmpeg使用6.1 FFmpeg下载地址:https://ffmpeg.org/…

Blender动画怎么云渲染?一分钟学会渲染速度提升数十倍

1.Blender为什么需要渲染? Blender是一款功能强大且备受欢迎的开源3D建模和渲染软件,为设计师、艺术家和动画制作人提供了无尽的创作可能。随着其视觉质量和功能的不断提高和完善,Blender项目的渲染时间也显著增加,越来越多的设计…

图扑铸造传统工业发展新高度——可视化输煤系统

火力发电是现代社会电力发展的主力军,在提出建设和谐社会、发展循环经济的大背景下,我们在提高火电技术的方向上要着重考虑电力对环境的影响,对不可再生能源的影响。输煤系统作为电厂外围尤为重要的系统,一直为电厂锅炉提供燃料&a…

C#与php自定义数据流传输

C#与php自定义数据流传输 介绍一、客户端与服务器数据传输流程图客户端发送数据给服务器:服务器返回数据给客户端: 二、自定义数据流C#版本数据流PHP版本数据流 三、数据传输测试1.在Unity中创建一个C#脚本NetWorkManager.cs2.服务器www目录创建StreamTe…

OSG三维渲染引擎编程学习之一百:“第十一章:OSG粒子” 之 “11.1 粒子的主要模块”

目录 第十一章 OSG粒子 11.1 粒子的主要模块 (1)放射极(osgParticle::Emitter) (2)粒子系统(osgParticle::ParticleSystem

【华为OD题库-110】反转每对括号间的子串-java

题目 给出一个字符串s(仅含有小写英文字母和括号)。 请你按照从括号内到外的顺序,逐层反转每对匹配括号中的字符串,并返回最终的结果。注意,您的结果中不应包含任何括号。 示例1: 输入: s “(abcd)” 输出: “dcba” 示例2: 输入: s “(u(l…