arm cortex-m架构 SVC指令详解以及SVC异常在freertos的应用

news2024/11/16 8:50:12

1. 前置知识

本文基于arm cortex-m架构描述, 关于arm cortex-m的一些基础知识可以参考我另外几篇文章:

  • arm cortex-m 架构简述
  • arm异常处理分析
  • c语言函数调用规范-基于arm 分析

2 SVC指令

2.1 SVC指令位域表示

在这里插入图片描述

  • bit15 - bit12:条件码(Condition Code),用于控制指令的条件执行(EQ,NE,… )。
  • bit11- bit8:固定编码0111,用于标识这是一个SVC指令。
  • bit7- bit0:8位立即数(imm8),svc指令一般用于实现系统调用,imm8用来指定系统调用号。

2.2 SVC指令用途简述

  • ARM中的SVC(Supervisor Call)指令,一般用于实现系统调用, 在执行SVC指令时会触发一个SVC异常。
  • 在cortex-m架构中,异常/中断服务 总是处于总是处于 Privileged(特权) 模式,系统调用也是利用这一特性从用户态 进入 内核态 ,然后根据系统调用号执行特定的系统调用。 关于cortex-m架构和中断的其他信息可以参考我的另外两篇文章:
    • arm cortex-m 架构简述
    • arm异常处理分析
  • 站在硬件的角度SVC指令就是实现了一个软件中断,SVC异常和其他异常处理并无不同,硬件并没有做类似 传递系统调用号 这样的操作,系统调用号imm8也只是和指令码"打包"在一起放在代码段,在进入内核态之后 获取系统调用号 的操作是由 软件完成 的。

2.3 SVC使用示例

  • SVC中断触发示例
    SVC #0x01  ; 执行SVC指令,SVC号为0x01
    SVC #0x02  ; 执行SVC指令,SVC号为0x02
    
  • SVC中断服务函数示例
    • 我在代码中加了注释,如果还是不理解,可以看看我 第一节 所推荐的文章。
    SVC_Handler:
    		; TST指令解释:
    		;	将 "LR" 与 "立即数4" 进行 "按位与" 操作
    		;	结果更新条件标志寄存器(CPSR)
    		; 	注:在发生异常时 LR 存储的是 EXC_RETURN , 这条代码主要看在发生异常时使用的是哪个栈,MSP或者PSP
    	TST LR, #4
    		; 依据TST LR, #4 的执行结果选择将 MSP或PSP加载到R0中
    		; ITE: "If-Then-Else" 的缩写,用于创建一个条件执行块(IT块)。
    		; EQ: 条件码,表示 "Equal"(相等),即当条件码寄存器(CPSR)的零标志(Z)被设置时,条件为真。
    		; 用C语言描述一下下面三行汇编:
    		; if(Z) {
    		; 	MRS R0, MSP
    		; } else {
    		; 	MRS R0, PSP
    		; }
    		; 举一反三: ITTE: "If-Then-Then-Else" 
    	ITE EQ
    	MRS R0, MSP		; LR&4 或者说 EXC_RETURN&4  为 0 ,说明在在执行SVC异常时使用的栈是 MSP
    	MRS R0, PSP     ; LR&4 或者说 EXC_RETURN&4  为 0 ,说明在在执行SVC异常时使用的栈是 PSP
    		;  获取返回地址 (原理是与发生异常时硬件压栈的顺序相关)
    		;  这里获得返回地址的原因是为了定位产生异常前执行的最后一条指令,也就是SVC指令
    	LDR R1, [R0, #24] 
    		; 获取SVC指令的低8位,也就是系统调用号,返回地址的上一条就是SVC指令,
    	LDRB R1, [R1, #-2] 
    		; 依据不同的系统调用号执行不同的系统调用
    	CMP R1, #0x01
    	BEQ SVC_01_Handler
    	CMP R1, #0x02
        BEQ SVC_02_Handler
    
    SVC_01_Handler:
    		; 处理SVC号为0x01的服务请求
    
    SVC_02_Handler:
    		; 处理SVC号为0x02的服务请求
    

3. SVC指令在freertos中的应用

freertos只使用了一次 SVC指令 也可以理解为 freertos中只实现了一个系统调用,且只使用一次,就是在完成任务创建后发起的第一次任务调度时使用。

3.1 在freertos中SVC中断的触发位置

  • freertos使用 SVC指令是在 prvPortStartFirstTask 函数中使用,目的是开始第一次调度,发生在创建任务完成后的第一次调度,我会为这段代码加上注释,如果还存在看不懂的情况,建议先看看我的其他文章或者自行查阅资料学习。
  • 在freertos中的调用关系是:
    		vTaskStartScheduler		--->
    ---> 	xPortStartScheduler		--->
    --->	prvPortStartFirstTask
    
    static void prvPortStartFirstTask( void )
    {
    	__asm volatile (
    	/* 
    	 *	- 0xE000ED08是 SCB 模块 VTOR 寄存器的地址
    	 *	- 这句汇编目的是把 0xE000ED08(VTOR的地址) 保存到 R0 寄存器
    	 *	- 执行完这句指令(伪指令),后 R0 寄存器保存的是 0xE000ED08(VTOR的地址)
    	 */
        	" ldr r0, =0xE000ED08   \n"
    	/* 
    	 *	- 将 0xE000ED08 地址的值(中断向量表的地址) 放到 R0 寄存器中
    	 *	- 执行完这句指令,后 R0 寄存器保存的是 中断向量表的地址
    	 */
        	" ldr r0, [r0]          \n"
    	/* 
    	 *	- 将 中断向量表 第一项的值 放到 R0 寄存器中
    	 *	- 中断向量表第一个字保存的 是栈顶地址(cortex-m是满减栈,栈顶即栈的开始)
    	 *	- 初始转态下使用的是MSP
    	 *	- 执行完这句指令,后 R0 寄存器保存的是栈顶地址
    	 */
        	" ldr r0, [r0]          \n"
     	/* 
    	 *	- 将 R0的值写入 MSP 中。
    	 *	- 此时 R0 内存储的是 栈顶地址,这一步其实就是把 MSP 中已经存好的内容完全销毁。
    	 *	- 因为在这段代码中,在执行完SVC指令后,会由调度器完全接管代码,这里的SVC异常永远也不会返回
    	 */
        	" msr msp, r0           \n"
        /* 
    	 *	- 将 control 寄存器清零
    	 *	- control 寄存器有三位,分别是:
    	 *		. FPCA	- 标志位,用于指示在前文中是否使用过 FPU(浮点运算单元),如果使用过,dang发生异常硬件压栈的时候,会基于此位决定是否保存浮点运算单元上下文,又此时使用的SVC异常是不会返回的,所以也不必保存,保存了也是浪费空间。
    	 *			0:前文没有使用浮点运算单元,产生异常时硬件不用保存浮点上下文
    	 *			1:前文使用了浮点运算单元,产生异常时硬件要保存浮点上下文
    	 *		. SPSEL - 控制位,控制在 Thread mode 下使用 MSP 还是 PSP , Handler mode 一定使用 PSP
    	 *			0: MSP
    	 *			1: PSP
    	 *		. nPRIV - 控制位,控制在 Thread mode 下是 Privileged(特权级) 还是 Unprivileged(非特权级) , Handler mode 下一定是 Privileged(特权级)
    	 *			0: Privileged(特权级)
    	 *			1: Unprivileged(非特权级)
    	 */
        	" mov r0, #0            \n"
        	" msr control, r0       \n"
        /* 开启全局中断 */
        	" cpsie i               \n"	
        /* 开启fpu */
        	" cpsie f               \n"	
        /* 数据同步隔离,确保上面的配置生效 */
        	" dsb                   \n"	
        /* 指令同步隔离,清空流水线 */
        	" isb                   \n" 
        /* 触发0号系统调用,永远也不会返回, 从此处开始代码由freertos全面接管 */	
        	" svc 0                 \n"
        /* svc 不会返回,永远也不会运行到这里 */
        	" nop                   \n"
        /* 这是一条伪指令,由汇编器处理,目的是告诉汇编器,把字面量池(常量)放到这个位置, 避免字面量池离使用它的指令太远,超出了寻址范围而导致错误。ARM的ldr指令寻址范围有限*/
        	" .ltorg                \n"
        );
    }
    

3.2 freertos SVC中断服务函数解析

  • 我会为这段代码加上注释,如果还存在看不懂的情况,建议先看看我的其他文章或者自行查阅资料学习。
    void vPortSVCHandler( void )
    {
       /*
    	* 思考:在汇编中能用C语言的变量吗,为什么,C语言中的变量和符号在汇编中代表了什么
    	* 答案:应考虑如下几点
    	* 	- C语言和汇编在整个编译过程中是在不同的阶段进行的
    	* 	- C语言是在 编译阶段 进行处理的,C语言会被编译为汇编
    	* 	- 汇编 是在汇编阶段进行的,会生成可重定位的目标文件,最终会在 链接阶段 由链接器为所有符号分配 绝对地址。
    	* 	- 我们在写汇编时,会用一些符号代表地址,那些符号会由 链接器 最终分配绝对地址。
    	* 	- c语言中定义的 变量名 函数名 在经过 编译器 编译为 汇编 之后,那些变量名, 函数名都代表一个地址,最后由 链接器 分配绝对地址。
    	* 	所以,在汇编中使用的C语言符号,可以理解为那个变量或者函数的 地址。
     	*/
       /*
    	* 先看一下最后一句汇编 "pxCurrentTCBConst2: .word pxCurrentTCB             \n"
    	* 	- pxCurrentTCB:在c语言中定义,它是一个指针,一个描述 任务 的结构体指针
    	* 	- 在汇编中使用 pxCurrentTCB 这个符号, 就相当于使用这个符号的地址,在这里可以理解为一个二重指针
    	* 	- 上述汇编可以这么理解:
    	* 		. 使用 .word 分配一个字的空间,用来放 “下一个要执行的任务结构体” 指针的指针(二重指针).
    	* 		. 将这个二重指针 使用符号 pxCurrentTCBConst2 表示
     	*/
    		__asm volatile (
    		/* 
    		 * - 将要执行任务的结构体 的 指针的指针(二重指针) 保存到 R3寄存器 
    		*/
         	"   ldr r3, pxCurrentTCBConst2      \n"
         	/* 
         	 * - 将要执行任务的结构体 的 指针(一重指针) 保存到 R1寄存器 
         	 * - 也可以理解为将 要执行任务的结构体 的第一个元素的地址保存到 R1 寄存器
         	 * - 结构体第一个元素 保存将要执行任务的栈指针
         	 * - 所以这一句是将 栈指针(SP) 的地址(是栈指针的存放地址,而不是栈指针) 放到R1中。
         	 */
        	"   ldr r1, [r3]                    \n"
        	/* 
        	 * - 将栈指针 SP 保存到 R0寄存器 
        	 */
        	"   ldr r0, [r1]                    \n"
        	/* 
        	 * - 将栈指针中的地址按顺序弹栈(要执行的任务的栈)到{r4-r11, r14}
        	 * - 这里是要弹栈的内容是 创建任务时 伪造的上下文 (此处先不展开,后面讲到任务的创建过程会展开来讲)
        	 */
        	"   ldmia r0!, {r4-r11, r14}        \n"
        	/* - 将 PSP 指向弹完栈后的地址,这个地址正好是硬件压栈的栈低,在SVC异常退出时,还会进行一次硬件弹栈 
        	 * - 事实上,在第一次启动的任务硬件要弹的栈也是在 创建任务时伪造的,后面讲到任务的创建过程会展开来讲。
        	 */
        	"   msr psp, r0                     \n"
        	/* 指令同步隔离,清空流水线 */
        	"   isb                             \n"
        	/* 将basepri寄存器设为0,打开所有中断 */
        	"   mov r0, #0                      \n"
        	"   msr basepri, r0                 \n"
        	/* 
        	 * - 异常返回, 这时r14(LR)中存放的是一个 EXC_RETURN 值,这个值是在创建任务时伪造的。
        	 * - EXC_RETURN 的值决定了异常返回后使用的栈(即任务要使用的栈),创建任务时伪造EXC_RETURN一定要注意:
        	 * 		.bit2 一定要设置为1,要任务返回PSP
        	 *  	.从 "   msr psp, r0                     \n" 这句汇编也能看出来,freertos希望任务栈都使用PSP.
        	 * - 伪造栈的内容会在后面将创建任务时讲
        	 */
        	"   bx r14                          \n"
        	"                                   \n"
        	"   .align 4                        \n"
        	"pxCurrentTCBConst2: .word pxCurrentTCB             \n"
        	);
    }
    

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JVM-JAVA-类加载过程

JVM源码 类加载到 JVM 的过程通过 java 命令执行代码的流程 类加载到 JVM 的过程 在运行一个 main 函数启动程序是,首先需要类加载起把主类加载到 JVM 中 通过 java 命令执行代码的流程 loadClass的类加载过程有如下几步: 类被加载到方法区中后主要包…

【数据结构与算法】二叉树的基本概念

文章目录 二叉树的基本概念定义二叉树的性质 几种特殊的二叉树满二叉树完全二叉树二叉排序树平衡二叉树正则二叉树 二叉树的存储结构顺序存储结构链式存储结构 二叉树的基本概念 定义 二叉树是一种特殊的树形结构,其特点是每个结点至多只有两颗子树,并…

前端生成海报图技术选型与问题解决

作者:vivo 互联网大前端团队 - Tian Yuhan 本篇文章主要聚焦海报图分享这个形式,探讨纯前端在H5&小程序内,合成海报到下载到本地、分享至社交平台整个流程中可能遇到的问题,以及如何解决。 一、引言 绝大多数的电商平台都会…

Hive 基本操作

1.启动Hadoop集群 2.将学生信息上传到/bigdata/hive/hive_stu目录下 查看测试数据 3.进入hive,切换到db_test库(如没有,可以先创建 create database db_test)

网络工程从头做-1

网络工程从头做-1 自下而上,从接入交换机开始网络的配置和规划 实验拓扑: 实验步骤: 1.完成基本配置 1.1 PC端IP地址信息配置略 1.2 接入层交换机S1配置 [Huawei]sys S1 [S1]undo in [S1]vlan b 10 20 [S1]int e0/0/1 [S1-Ethernet0/0/1]p l…

618大促,消费者用行动警告国产手机,超过3000都不买了

618大促已过了三分之一,除了iPhone成为大赢家之外,国产手机也面临了一个新问题,那就是消费者对于超过3000元的国产旗舰手机都不太接受了,他们选购国产旗舰手机也只买3000元以下的了。 某电商的热销榜TOP10显示,进入榜单…

【Redis数据库百万字详解】数据类型

文章目录 一、字符串类型概述1.1、数据类型1.2、字符串简介1.3、字符串应用场景 二、字符串命令三、哈希类型概述3.1、哈希介绍3.2、哈希类型应用场景3.3、哈希命令 四、列表类型概述4.1、列表简介4.2、使用场景4.3、列表命令 五、集合概述5.1、集合简介5.2、使用场景5.3、集合…

基于FPGA的图像一维FFT变换IFFT逆变换verilog实现,包含tb测试文件和MATLAB辅助验证

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 fpga仿真结果 matlab调用FPGA的仿真结果进行图像显示 2.算法运行软件版本 vivado2019.2 matlab2022a 3.部分核心程序 ......................…

ElementUI的Table组件在无数据情况下让“暂无数据”文本居中显示

::v-deep .el-table__empty-block {width: 100%;min-width: 100%;max-width: 100%; }

什么是事务性电子邮件

事务性电子邮件(Transactional Emails)是企业或组织针对客户或会员的特定活动或操作而发送的电邮。这类邮件的目的是通知用户关于账户状态、订单处理、密码重置、服务更新等重要个人信息。事务性邮件旨在辅助完成特定的业务流程或任务,确保用…

47、Flink 的 Data Source 原理

1.Data Source 原理 a)核心组件 一个数据 source 包括三个核心组件:分片(Splits)、分片枚举器(SplitEnumerator) 以及 源阅读器(SourceReader)。 分片(Split&#xff…

Xcode设置cocoapods库的最低兼容版本

目录 前言 1.使用cocoapods遇到的问题 2.解决办法 1.用法解释 1. config.build_settings: 2.IPHONEOS_DEPLOYMENT_TARGET 2.使用实例 3.注意事项 1.一致性 2.pod版本 前言 这篇文章主要是介绍如何设置cocoapods三方库如何设置最低兼容的版本。 1.使用cocoapods遇到的…

LitCTF2024部分wp

litctf wp 第一次ak了web和misc,非常激动,感谢lictf给我这个机会 最终成果 全靠队里的密码逆向✌带飞。一个人就砍了近一半的分数 这里是我们队的wp web exx 题目名反过来就是xxe,考察xxe,查看登录的数据包 发现传的就是xml…

ast.js是什么?

在devtools分析网站时,出现了ast.js的页面。那么,什么是ast.js?它有什么用? 经查询,AST是抽象语法树(Abstract Syntax Tree)也称为AST语法树,指的是源代码语法所对应的树状结构。也就是说&…

wandb安装与使用 —— 用于跟踪、可视化和协作机器学习实验的工具

文章目录 一、wandb简介二、wandb注册与登陆(网页) —— 若登录,则支持在线功能三、wandb安装与登陆(命令行) —— 若不登录,则只保留离线功能四、函数详解4.1、wandb.init() —— 初始化一个新的 wandb 实…

AI图书推荐:《如何利用ChatGPT在线赚钱》

这本书《如何利用ChatGPT在线赚钱》($100m ChatGPT_ How To Make Money Online With ChatGPT -- Sharp, Biily -- 2023 )主要阐述如何利用ChatGPT这一强大的语言模型工具在互联网上创造收入。 以下是各章节内容的概要: **引言** - 介绍了Chat…

批量处理文件名:高效替换与插入文字,优化文件管理新策略,轻松提升工作效率

文件管理成为我们工作中不可或缺的一环。面对大量的文件,如何高效、精准地处理文件名,成为了提升工作效率的关键。今天,我们向您推荐一款能够轻松实现文件名批量处理的神器——文件批量改名高手,助您优化文件管理,提升…

GPT-4 Turbo 和 GPT-4 的区别

引言 人工智能(AI)领域的发展日新月异,OpenAI 的 GPT 系列模型一直是这一领域的佼佼者。GPT-4 和 GPT-4 Turbo 是目前市场上最先进的语言模型之一。本文将详细探讨 GPT-4 和 GPT-4 Turbo 之间的区别,以帮助用户更好地理解和选择适…

白酒:全球化背景下产地白酒的国际竞争与合作

在全球化背景下,云仓酒庄豪迈白酒作为中国白酒的品牌之一,面临着国际竞争与合作的机遇与挑战。国际市场竞争的激烈以及消费者需求的多样化,要求云仓酒庄豪迈白酒不断提升品质、拓展市场以及加强国际合作,以提升品牌竞争力和市场份…

QQ号码采集器

寅甲QQ号码采集软件, 一款采集QQ号、QQ邮件地址,采集QQ群成员、QQ好友的软件。可以按关键词采集,如可以按地区、年龄、血型、生日、职业等采集。采集速度非常快且操作很简单。