ARM-SWI 和未定义指令异常中断处理程序的返回(七)

文章目录

    • 处理流程
    • 示例
    • 代码实现
      • SWI
      • 未定义指令
    • 附录源码

处理流程

SWI 和未定义指令异常中断是由当前执行的指令自身产生的,当 SWI 和未定义指令异常中断产生时,程序计数器的 PC 的值还未更新,它指向当前指令后面第 2 条指令(对于 ARM 指令来说,它指向当前指令地址加 8 个字节的位置;对于 Thumb 指令来说,它指向当前指令地址加 4 个字节的位置)。
当 SWI 和未定义指令异常中断发生时,处理器将 PC-4 保存到异常模式下的 LR_mode 寄存器中。这时 LR_mode 中的值即为PC-4 ,即指向当前指令的第一条指令。因此返回操作直接将 LR_mode(指向当前指令的下一条指令) 的值赋给 PC 即可,可以通过下面的指令实现:

MOVS PC, LR   // 注意 S ,指定了 S 意味着同时将SPSR 拷贝到 CPSR

该指令将寄存器 LR 中的值,复制到程序计数器 PC 中,实现程序返回,同时将 SPSR_mode 寄存器内容复制到当前程序计数器 CPSR 中。
当异常中断处理程序中使用了数据栈时,可以通过下面的指令在异常中断处理程序时保存被中断程序的执行现场,在退出异常中断处理程序时恢复被中断程序的执行现场。异常中断处理程序中使用的数据栈由用户提供。

stmdb sp!, {r0-r12, lr}   // 保存现场 r0-r12 - reg_list
// user code
ldmia sp!, {r0-r12, pc}^  // 恢复现场 r0-r12 - reg_list

在上述指令中,reg_list 是异常中断处理程序中使用的寄存器列表。标识符 ^ 指示将 SPSR_mode 寄存器内容复制到 CPSR 寄存器中,该指令只能在特权模式下使用。

示例

在这里插入图片描述

  • 假设在 instruction+0 指令执行完成后发生 swi/undef 异常
  • 此时 PC 值 未更新还是指向 instruction+2
  • 进入 swi/undef 异常后,LR_mode = PC -4,所以此时 LR 指向 instruction+1
  • swi/undef 异常处理完成之后,程序需要返回到 instruction+1 处执行,所以将此时的 LR_mode 寄存器的值赋给 PC 即可

代码实现

SWI

异常处理程序

    .align 2.arm.weak SVC_Handler.type SVC_Handler, %function
SVC_Handler:/* 执行到这里之前:* 1. lr_svc 保存有被中断模式中的下一条即将执行的指令的地址* 2. SPSR_svc 保存有被中断模式的 CPSR* 3. CPSR 中的 M4-M0 被设置为 10011, 进入到 svc 模式* 4. 跳到 0x08 的地方执行程序*//* 保存现场 *//* 在 swi 异常处理函数中有可能会修改 r0-r12, 所以先保存 *//* lr 是异常处理完后的返回地址, 也要保存 */stmdb sp!, {r0-r12, lr}.if 0mov r4, lr /* LR 由硬件自动保存软中断指令下一条指令的地址 *//* 处理swi异常 */mrs r0, cpsrldr r1, =swi_stringbl printExceptionsub r0, r4, #4 /* LR 为硬件自动保存 SWI xxx 指令的下一条指令地址,LR – 4 就是 SWI 指令地址,由 SWI 指令编码知识可知,SWI 指令低 24 位保存有软中断号 */bl printSWIVal
.elseldr r4,[lr, #-4] /* LR 由硬件自动保存软中断指令下一条指令的地址,指令地址存放的是指令的操作码,将操作码取出,低 24 位置,即为软中断号 */bic r0, r4, #0xff000000 /* LR 为硬件自动保存 SWI xxx 指令的下一条指令地址,LR – 4 就是 SWI 指令地址,由 SWI 指令编码知识可知,SWI 指令低 24 位保存有软中断号 */push {r0}/* 处理swi异常 */mrs r0, cpsrldr r1, =swi_stringbl printExceptionpop {r0}bl printSWIVal
.endif/* 恢复现场 */ldmia sp!, {r0-r12, pc}^  /* ^ 会把 spsr 的值恢复到 cpsr 里 */swi_string:.string "swi exception"

触发 swi

swi_code:swi 0x123  /* 执行此命令, 触发SWI异常, 进入0x8执行 */

C 语言中将软中断号打印出来

void printSWIVal(unsigned int *pSWI)
{swi_flag = 1;printf("SWI val = 0x%x\r\n", *pSWI & ~0xff000000);
}

未定义指令

异常处理程序

    .align 2.arm.weak Undefined_Handler.type Undefined_Handler, %function
Undefined_Handler:/* 执行到这里之前:* 1. lr_und 保存有被中断模式中的下一条即将执行的指令的地址* 2. SPSR_und 保存有被中断模式的CPSR* 3. CPSR 中的 M4-M0 被设置为 11011, 进入到 und 模式* 4. 跳到 0x4 的地方执行程序*//* 在 und 异常处理函数中有可能会修改 r0-r12, 所以先保存 *//* lr 是异常处理完后的返回地址, 也要保存 */stmdb sp!, {r0-r12, lr}/* 保存现场 *//* 处理 und 异常 */mrs r0, cpsrldr r1, =und_stringbl printException/* 恢复现场 */ldmia sp!, {r0-r12, pc}^  /* ^ 会把 spsr 的值恢复到 cpsr 里 */und_string:.string "undefined instruction exception"

触发未定义指令异常

und_code:nop              /* for debug */.word 0xeeadc0de /* undefine instruction */nop              /* for debug */.word 0xFFFFFFFF /* undefine instruction */nop              /* for debug */ldr   r0, =mainbx    r0

C 语言中将异常字符串打印出来

void printException(unsigned int cpsr, char *str)
{excep_flag = 1;printf("Exception! cpsr is 0x%x\r\n", cpsr);
}

附录源码

.equ Mode_USR,        0x10
.equ Mode_FIQ,        0x11
.equ Mode_IRQ,        0x12
.equ Mode_SVC,        0x13
.equ Mode_MON,        0x16
.equ Mode_ABT,        0x17
.equ Mode_HYP,        0x1A
.equ Mode_UND,        0x1B
.equ Mode_SYS,        0x1F.equ Stack_size,      0x400
.equ Stack_Start,     0x80200000.equ Mode_USR_Stack,  Stack_Start + Stack_size
.equ Mode_FIQ_Stack,  Mode_USR_Stack + Stack_size
.equ Mode_IRQ_Stack,  Mode_FIQ_Stack + Stack_size
.equ Mode_SVC_Stack,  Mode_IRQ_Stack + Stack_size
.equ Mode_MON_Stack,  Mode_SVC_Stack + Stack_size
.equ Mode_ABT_Stack,  Mode_MON_Stack + Stack_size
.equ Mode_HYP_Stack,  Mode_ABT_Stack + Stack_size
.equ Mode_UND_Stack,  Mode_HYP_Stack + Stack_size
.equ Mode_SYS_Stack,  Mode_UND_Stack + Stack_size/* 定义一个 .isr_vector 段,链接脚本中定义相应的段来存放该段的数据 */.section .isr_vector, "a".arm.align 2.globl __isr_vector
__isr_vector:ldr     pc, =Reset_Handler           /* Reset                  */ldr     pc, =Undefined_Handler       /* Undefined instructions */ldr     pc, =SVC_Handler             /* Supervisor Call        */ldr     pc, =PrefAbort_Handler       /* Prefetch abort         */ldr     pc, =DataAbort_Handler       /* Data abort             */.word   0                            /* RESERVED               */ldr     pc, =IRQ_Handler             /* IRQ interrupt          */ldr     pc, =FIQ_Handler             /* FIQ interrupt          *//* 余下的指令的放在 .text 中,所以用 .text 指定段 */
/* Reset Handler */.text.arm.align 2.globl   Reset_Handler.weak    Reset_Handler.type    Reset_Handler, %function
Reset_Handler:cpsid i /* 关闭全局中断 *//* 关闭I,DCache和MMU* 采取读-改-写的方式。*/mrc p15, 0, r0, c1, c0, 0     /* 读取 CP15 的 C1 寄存器到 R0 中 */bic r0,  r0, #(0x1 << 12)     /* 清除 C1 寄存器的  bit12 位(I位),关闭 I Cache */bic r0,  r0, #(0x1 <<  2)     /* 清除 C1 寄存器的 bit2 (C位),关闭 D Cache */bic r0,  r0, #0x2             /* 清除 C1 寄存器的 bit1 (A位),关闭对齐 */bic r0,  r0, #(0x1 << 11)     /* 清除 C1 寄存器的 bit11 (Z位),关闭分支预测 */bic r0,  r0, #0x1             /* 清除 C1 寄存器的 bit0 (M位),关闭 MMU */mcr p15, 0, r0, c1, c0, 0     /* 将 r0 寄存器中的值写入到 CP15 的 C1 寄存器中 */#if 0/* 汇编版本设置中断向量表偏移 */ldr r0, =0x80000000dsbisbmcr p15, 0, r0, c12, c0, 0dsbisb
#endif/* 模式切换并设置 sp 地址 */cps     #Mode_FIQldr     sp, =Mode_FIQ_Stackcps     #Mode_IRQldr     sp, =Mode_IRQ_Stackcps     #Mode_SVCldr     sp, =Mode_SVC_Stackcps     #Mode_MONldr     sp, =Mode_MON_Stackcps     #Mode_ABTldr     sp, =Mode_ABT_Stackcps     #Mode_HYPldr     sp, =Mode_HYP_Stackcps     #Mode_UNDldr     sp, =Mode_UND_Stack/* sys mode and user have common sp register */cps     #Mode_SYSldr     sp, =Mode_SYS_Stackcpsie i /* 打开全局中断 *//* clear bss */ldr r1, =__bss_start__ldr r2, =__bss_end__mov    r0, #0
bss_loop:cmp     r1, r2itt     ltstrlt   r0, [r1], #4blt    bss_loop/* 使能IRQ中断 */mrs r0, cpsr        /* 读取cpsr寄存器值到r0中 */bic r0, r0, #0x80   /* 将r0寄存器中bit7清零,也就是CPSR中的I位清零,表示允许IRQ中断 */msr cpsr, r0        /* 将r0重新写入到cpsr中 */swi_code:swi 0x123  /* 执行此命令, 触发SWI异常, 进入0x8执行 */und_code:nop              /* for debug */.word 0xeeadc0de /* undefine instruction */nop              /* for debug */.word 0xFFFFFFFF /* undefine instruction */nop              /* for debug */ldr   r0, =mainbx    r0.align 2.arm.weak Undefined_Handler.type Undefined_Handler, %function
Undefined_Handler:/* 执行到这里之前:* 1. lr_und 保存有被中断模式中的下一条即将执行的指令的地址* 2. SPSR_und 保存有被中断模式的CPSR* 3. CPSR 中的 M4-M0 被设置为 11011, 进入到 und 模式* 4. 跳到 0x4 的地方执行程序*//* 在 und 异常处理函数中有可能会修改 r0-r12, 所以先保存 *//* lr 是异常处理完后的返回地址, 也要保存 */stmdb sp!, {r0-r12, lr}/* 保存现场 *//* 处理 und 异常 */mrs r0, cpsrldr r1, =und_stringbl printException/* 恢复现场 */ldmia sp!, {r0-r12, pc}^  /* ^ 会把 spsr 的值恢复到 cpsr 里 */und_string:.string "undefined instruction exception".align 2.arm.weak SVC_Handler.type SVC_Handler, %function
SVC_Handler:/* 执行到这里之前:* 1. lr_svc 保存有被中断模式中的下一条即将执行的指令的地址* 2. SPSR_svc 保存有被中断模式的 CPSR* 3. CPSR 中的 M4-M0 被设置为 10011, 进入到 svc 模式* 4. 跳到 0x08 的地方执行程序*//* 保存现场 *//* 在 swi 异常处理函数中有可能会修改 r0-r12, 所以先保存 *//* lr 是异常处理完后的返回地址, 也要保存 */stmdb sp!, {r0-r12, lr}.if 0mov r4, lr /* LR 由硬件自动保存软中断指令下一条指令的地址 *//* 处理swi异常 */mrs r0, cpsrldr r1, =swi_stringbl printExceptionsub r0, r4, #4 /* LR 为硬件自动保存 SWI xxx 指令的下一条指令地址,LR – 4 就是 SWI 指令地址,由 SWI 指令编码知识可知,SWI 指令低 24 位保存有软中断号 */bl printSWIVal
.elseldr r4,[lr, #-4] /* LR 由硬件自动保存软中断指令下一条指令的地址,指令地址存放的是指令的操作码,将操作码取出,低 24 位置,即为软中断号 */bic r0, r4, #0xff000000 /* LR 为硬件自动保存 SWI xxx 指令的下一条指令地址,LR – 4 就是 SWI 指令地址,由 SWI 指令编码知识可知,SWI 指令低 24 位保存有软中断号 */push {r0}/* 处理swi异常 */mrs r0, cpsrldr r1, =swi_stringbl printExceptionpop {r0}bl printSWIVal
.endif/* 恢复现场 */ldmia sp!, {r0-r12, pc}^  /* ^ 会把 spsr 的值恢复到 cpsr 里 */swi_string:.string "swi exception".align 2.arm.weak PrefAbort_Handler.type PrefAbort_Handler, %function
PrefAbort_Handler:ldr r0, =PrefAbort_Handlerbx r0.align 2.arm.weak DataAbort_Handler.type DataAbort_Handler, %function
DataAbort_Handler:ldr r0, =DataAbort_Handlerbx r0.align 2.arm.weak IRQ_Handler.type IRQ_Handler, %functionIRQ_Handler:push {lr}                   /* 保存lr地址 */push {r0-r3, r12}           /* 保存r0-r3,r12寄存器 */mrs r0, spsr                /* 读取spsr寄存器 */push {r0}                   /* 保存spsr寄存器 */.if 0 /* it always return 0, i don't know why */mrc p15, 4, r1, c15, c0, 0 /* 从CP15的C0寄存器内的值到R1寄存器中* 参考文档ARM Cortex-A(armV7)编程手册V4.0.pdf P49* Cortex-A7 Technical ReferenceManua.pdf P68 P138*/
.elseldr r1, =0x00a00000
.endifadd r1, r1, #0X2000         /* GIC基地址加0X2000,也就是GIC的CPU接口端基地址 */ldr r0, [r1, #0XC]          /* GIC的CPU接口端基地址加0X0C就是GICC_IAR寄存器,* GICC_IAR寄存器保存这当前发生中断的中断号,我们要根据* 这个中断号来绝对调用哪个中断服务函数*/push {r0, r1}               /* 保存r0,r1 */cps #0x13                   /* 进入SVC模式,允许其他中断再次进去 */push {lr}                   /* 保存SVC模式的lr寄存器 */ldr r2, =system_irqhandler	/* 加载C语言中断处理函数到r2寄存器中*/blx r2                      /* 运行C语言中断处理函数,带有一个参数,保存在R0寄存器中 */pop {lr}                    /* 执行完C语言中断服务函数,lr出栈 */cps #0x12                   /* 进入IRQ模式 */pop {r0, r1}str r0, [r1, #0X10]         /* 中断执行完成,写EOIR */pop {r0}msr spsr_cxsf, r0           /* 恢复spsr */pop {r0-r3, r12}            /* r0-r3,r12出栈 */pop {lr}                    /* lr出栈 */subs pc, lr, #4             /* 将lr-4赋给pc */.align 2.arm.weak FIQ_Handler.type FIQ_Handler, %function
FIQ_Handler:ldr r0, =FIQ_Handlerbx r0.end

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

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

相关文章

【算法题】动态规划中级阶段之最长回文子串、括号生成、跳跃游戏

动态规划中级阶段 前言一、最长回文子串1.1、思路1.2、代码实现 二、括号生成2.1、思路2.2、代码实现 三、跳跃游戏 II3.2、思路3.2、代码实现 总结 前言 动态规划&#xff08;Dynamic Programming&#xff0c;简称 DP&#xff09;是一种解决多阶段决策过程最优化问题的方法。…

香港大学推出创新科技教育基金,拟支持Web3和生成式AI等领域教学

区块链技术是近年来备受关注的领域之一,其应用范围已经涵盖了金融、医疗、物流等众多行业。而随着区块链技术的不断发展和完善&#xff0c;越来越多的企业和机构开始将其应用到实际生产和业务中。作为其中一个重要的应用领域&#xff0c;金融领域也成为了区块链技术的重要应用场…

3-css高级特效-1

01-平面转换 简介 作用&#xff1a;为元素添加动态效果&#xff0c;一般与过渡配合使用 概念&#xff1a;改变盒子在平面内的形态&#xff08;位移、旋转、缩放、倾斜&#xff09; 平面转换也叫 2D 转换&#xff0c;属性是 transform 平移 transform: translate(X轴移动距…

Linux——进程通信之共享内存

目录 一. 回顾上文 二.共享内存 1.定义 2.特点&#xff1a; 3.实现步骤&#xff1a; 如下为成功链接共享内存使用权的完整步骤&#xff1a; 4.函数介绍 4.1shmget函数 4.1.2参数介绍 4.2ftok函数&#xff1a; 4.2.1参数介绍 关于ftok(); shmget();函数的代码实验…

【吃透网络安全】2023软考网络管理员考点网络安全(三)计算机系统安全评估

涉及知识点 计算机系统安全评估准则&#xff0c;计算机系统安全评估历史&#xff0c;软考网络管理员常考知识点&#xff0c;软考网络管理员网络安全&#xff0c;网络管理员考点汇总。 后面还有更多续篇希望大家能给个赞哈&#xff0c;这边提供个快捷入口&#xff01; 第一节…

Java版工程项目管理系统源码 工程项目源码

数 据 库&#xff1a; MySQL 开发语言&#xff1a; Java 开发工具&#xff1a; MyEclipse 源码类型&#xff1a; WebForm 以甲方项目管理为中心&#xff0c;包括项目启动、计划、执行、控制与收尾阶段的全生命周期管理&#xff0c;并对范围、预…

【数据库课设】图书馆资源管理系统 源码+流程图+结构设计(借还图书 逾期罚款 图书管理 读者管理 信息查询)python实现

文章目录 一 实现功能&#xff08;1&#xff09;管理员功能&#xff08;2&#xff09;读者功能 二 数据流图三 概念结构设计四 文件目录五 源码&#xff1a;main.py六 运行截图 一 实现功能 &#xff08;1&#xff09;管理员功能 一个管理员编号对应一个密码&#xff0c;且需…

drone、gogs、docker与项目集成实现自动化部署

目录 前言项目目录结构目录结构测试文件 文件内容Dockerfilerun.shdrone.yml 测试打包部署中查看容器访问项目成功 常见问题Gogs 推送 URL 被解析到默认禁用的本地网络地址1、drone登录没有权限2、cannot ping the remote server3、推送代码以后不能自动clone4、maven编译报错F…

网络安全合规-网络安全工程师(一)

网络安全工程师的工作种类很多&#xff0c;当前这个图片说明了具体的工程师的工作种类有哪些&#xff0c;列举了一下。 互联网时代网络及科技迅速发展&#xff0c;随之而来的首要问题就是网络安全&#xff0c;因为已经出现的网络问题带来了巨大的经济损失&#xff0c;甚至各种…

设计模式-工厂模式

工厂模式 1、工厂模式简介2、工厂模式实现 1、工厂模式简介 工厂模式(Factory Pattern)是最常见的设计模式&#xff0c;该模式属于创建型模式&#xff0c;它提供了一种简单、快速、高效且安全创建对象的方式。工厂模式在接口中定义了创建对象的方法&#xff0c;而将创建对象的具…

设计模式->观察者设计模式和订阅者发布者设计模式的区别

设计模式->观察者设计模式和订阅者发布者设计模式的区别 一、先复习一下观察者设计模式的相关定义,优点,以及缺点1.定义观察者模式的三个典型例子 2.优点3.缺点4.观察者设计模式的主要角色5.代码举例完整代码 二、回答问题:观察者设计模式和订阅者发布者设计模式的区别 一、…

雅迪渐进、小牛徐行,两轮电动车“尖子生”竞争加剧

配图来自Canva可画 2023年中旬&#xff0c;两轮电动车品牌陆续上新车&#xff0c;对外展示品牌实力和创新力。 5月9日&#xff0c;哈啰智能电动车发布“极智系列”三款搭载了哈啰图灵T30智能平台的新车&#xff1b;5月10日&#xff0c;九号公司发布全新设计的E系列电摩顶配车型…