非常向量表
一、非常(Exception)非常是理解CPU运转最主要的一个知识点,险些每种处理器都支持特定非常处理,中断是非常中的一种。 有时候我们衡量一个操作系统的时候实时性便是看os最短相应中断韶光以及单位韶光内相应中断次数。
在ARM体系构造中,存在7种非常处理。当非常发生时,处理器会把PC设置为一个特定的存储器地址。这一地址放在被称为向量表(vector table)的特定地址范围内,向量表的入口是一些跳转指令,跳转到专门处理某个非常或中断的子程序。

要进入非常模式,一定要有非常源,ARM规定有7种非常源:
非常源 描述 Reset 上电时实行 Undef 当流水线中的某个造孽指令到达实行状态时实行 SWI 当一个软中断指令被实行完的时候实行 Prefetch 当一个指令被从内存中预取时,由于某种缘故原由而失落败,如果它能到达实行状态这个非常才会产生 Data 如果一个预取指令试图存取一个造孽的内存单元,这时非常产生 IRQ 常日的中断 FIQ 快速中断
reset复位非常当CPU刚上电时或按下reset重启键之后进入该非常,该非常在管理模式下处理。
irq/fiq一样平常/快速中断要求CPU和外部设备是分别独立的硬件实行单元,CPU对全部设备进行管理和资源调度处理,CPU要想知道外部设备的运行状态,要么CPU定时的去查看外部设备特定寄存器,要么让外部设备在涌现须要CPU干涉处理时“打断”CPU,让它来处理外部设备的要求,毫无疑问第二种办法更合理,可以让CPU“专心”去事情,这里的“打断”操作就叫做中断要求,根据要求的紧急情形,中断要求分一样平常中断和快速中断,快速中断具有最高中断优先级和最小的中断延迟,常日用于处理高速数据传输及通道的中数据规复处理,如DMA等,绝大部分外设利用一样平常中断要求。
预取指令中止非常该非常发生在CPU流水线取指阶段,如果目标指令地址是非法地址进入该非常,该非常在中止非常模式下处理。
未定义指令非常该非常发生在流水线技能里的译码阶段,如果当前指令不能被识别为有效指令,产生未定义指令非常,该非常在未定义非常模式下处理。
软件中断指令(swi)非常该非常是运用程序自己调用时产生的,用于用户程序申请访问硬件资源时,例如:printf()打印函数,要将用户数据打印到显示器上,用户程序要想实现打印必须申请利用显示器,而用户程序又没有外设硬件的利用权,只能通过利用软件中断指令切换到内核态,通过操作系统内核代码来访问外设硬件,内核态是事情在特权模式下,操作系统在特权模式下完成将用户数据打印到显示器上。这样做的目的无非是为了保护操作系统的安全和硬件资源的合理利用,该非常在管理模式下处理。
数据中止访问非常 该非常发生在要访问数据地址不存在或者为造孽地址时,该非常在中止非常模式下处理。2. ARM的非常优先级Reset→Dataabort→FIQ→IRQ→Prefetchabort→Undefinedinstruction/SWI。
3. FIQ 比 IRQ快的缘故原由fiq 比 irq 的优先级高FIQ 向量位于向量表的最末端,非常处理不须要跳转FIQ 比 IRQ 多5个私有的寄存器(r8-r12),在中断操作时,压栈出栈操作的少。三、非常发生的硬件操作
非常发生后,ARM核的操作步骤可以总结为4大步3小步。
1. 4大步3小步保存实行状态:将CPSR复制到发生的非常模式下SPSR中;模式切换:CPSR模式位逼迫设置为与非常类型相对应的值,处理器进入到ARM实行模式,禁止所有IRQ中断,当进入FIQ快速中断模式时禁止FIQ中断;保存返回地址:将下一条指令的地址(被打断程序)保存在LR(非常模式下LR_excep)中。跳入非常向量表:逼迫设置PC的值为相应非常向量地址,跳转到非常处理程序中。2. 步骤详解保存实行状态当出路序的实行状态是保存在CPSR里面的,非常发生时,要保存当前的CPSR里的实行状态到非常模式里的SPSR里,将来非常返回时,规复回CPSR,规复实行状态。
模式切换硬件自动根据当前的非常类型,将非常码写入CPSR里的M[4:0]模式位,这样CPU就进入了对应非常模式下。不管是在ARM状态下还是在THUMB状态下发生非常,都会自动切换到ARM状态下进行非常的处理,这是由硬件自动完成的,将CPSR[5] 设置为 0。同时,CPU会关闭中断IRQ(设置CPSR 寄存器I位),防止中断进入,如果当前是快速中断FIQ非常,关闭快速中断(设置CPSR寄存器F位)。
保存返回地址当出路序被非常打断,切换到非常处理程序里,非常处理完之后,返回当前被打断模式连续实行,因此必须要保存当前实行指令的下一条指令的地址到LR_excep(非常模式下LR,并不存在LR_excep寄存器,为方便读者理解加上_excep,以下道理相同),由于非常模式不同以及ARM内核采取流水线技能,非常处理程序里要根据非常模式打算返回地址。
跳入非常向量表该操作是CPU硬件自动完成的,当非常发生时,CPU逼迫将PC的值修正为一个固定内存地址,这个固定地址叫做非常向量。
四、非常向量表非常向量表是一段特定内存地址空间,每种ARM非常对应一个字长空间(4Bytes),恰好是一条32位指令长度,当非常发生时,CPU逼迫将PC的值设置为当前非常对应的固定内存地址。
1. 非常向量表:非常向量表
跳入非常向量表操作是非常发生时,硬件自动完成的,剩下的非常处理任务完备交给了程序员。由上表可知,非常向量是一个固定的内存地址,我们可以通过向该地址处写一条跳转指令,让它跳向我们自己定义的非常处理程序的入口,就可以完成非常处理了。
非常向量表
正是由于非常向量表的存在,才让硬件非常处理和程序员自定义处理程序有机联系起来。非常向量表里0x00000000地址处是reset复位非常,之以是它为0地址,是由于CPU在上电时自动从0地址处加载指令,由此可见将复位非常安装在此地址处也是前后结合起来设计的,不得不感叹CPU设计师的伟大,其后面分别是别的7种非常向量,每种非常向量都霸占四个字节,恰好是一条指令的大小,末了一个非常是快速中断非常,将其安装在此也有它的意义,在0x0000001C地址处可以直接存放快速中断的处理程序,不用设置跳转指令,这样可以节省一个时钟周期,加快快速中断处理韶光。
存储器映射地址0x00000000是为向量表保留的。在有些处理器中,向量表可以选择定位在高地址0xFFFF0000处【可以通过协处理器指令配置】,当今操作系统为了掌握内存访问权限,常日会开启虚拟内存,开启了虚拟内存之后,内存的开始空间常日为内核进程空间,和页表空间,非常向量表不能再安装在0地址处了。
比如Cortex-A8系统中支持通过设置CP15的C12寄存器将非常向量表的首地址放置在任意地址。
2. 安装非常向量表我们可以通过大略的利用下面的指令来安装非常向量表:
breset;跳入reset处理程序bHandleUndef;跳入未定义处理程序bHandSWI;跳入软中断处理程序bHandPrefetchAbt;跳入预取指令处理程序bHandDataAbt;跳入数据访问中止处理程序bHandNoUsed;跳入未利用程序bHandleIRQ;跳入中断处理程序bHandleFIQ;跳入快速中断处理程序
常日安装完非常向量表,跳到我们自己定义的处理程序入口,这时我们还没有保存被打断程序的现场,因此在非常处理程序的入口里先要保存打断程序现场。
3. 保存实行现场非常处理程序最开始,要保存被打断程序的实行现场,程序的实行现场无非便是保存当前操作寄存器里的数据,可以通过下面的栈操作指令实现保存现场:
STMFDSP_excep!,{R0–R12,LR_excep}
注:LR_abt,SP_excep分别为对应非常模式下LR和SP,为方便读者理解加上_abt
须要把稳的是,在跳转到非常处理程序入口时,已经切换到对应非常模式下了,因此这里的SP是非常模式下的SP_excep了,以是被打断程序现场(寄存器数据)是保存在非常模式下的栈里,上述指令将R0~R12全部都保存到了非常模式栈,末了将修正完的被打断程序返回地址入栈保存,之以是保存该返回地址便是将来可以通过类似:MOV PC, LR的指令,返回用户程序连续实行。
非常发生后,要针对非常类型进行处理,因此,每种非常都有自己的非常处理程序,中断非常处理过程通过下节的系统中断处理来进行剖析。
五、非常处理的返回非常处理完成之后,返回被打断程序连续实行,详细操作如下:
规复被打断程序运行时寄存器数据规复程序运行时状态CPSR通过进入非常时保存的返回地址,返回到被打断程序连续实行1. 非常返回地址一条指令的实行分为:取指,译码,实行三个紧张阶段, CPU由于利用流水线技能,造成当前实行指令的地址该当是PC – 8(32位机一条指令四个字节),那么实行指令的下条指令该当是PC – 4。在非常发生时,CPU自动会将将PC – 4 的值保存到LR里,但是该值是否精确还要看非常类型才能决定。
各模式的返回地址解释如下:
一样平常/快速中断要求:快速中断要乞降一样平常中断要求返回处理是一样的。常日处理器实行完当前指令后,查询FIQ/IRQ中断引脚,并查看是否许可FIQ/IRQ中断,如果某个中断引脚有效,并且系统许可该中断产生,处理器将产生FIQ/IRQ非常中断,当FIQ/IRQ非常中断产生时,程序计数器pc的值已经更新,它指向当前指令后面第3条指令(对付ARM指令,它指向当前指令地址加12字节的位置;对付Thumb指令,它指向当前指令地址加6字节的位置),当FIQ/IRQ非常中断产生时,处理器将值(pc-4)保存到FIQ/IRQ非常模式下的寄存器lr_irq/lr_irq中,它指向当前指令之后的第2条指令,因此精确返回地址可以通过下面指令算出:
SUBSPC,LR_irq,#4;一样平常中断SUBSPC,LR_fiq,#4;快速中断
注:LR_irq/LR_fiq分别为一样平常中断和快速中断非常模式下LR,并不存在LR_xxx寄存器,为方便读者理解加上_xxx,下同。
预取指中止非常:在指令预取时,如果目标地址是非法的,该指令被标记成有问题的指令,这时,流水线上该指令之前的指令连续实行,当实行到该被标记成有问题的指令时,处理器产生指令预取中止非常中断。发生指令预取非常中断时,程序要返回到该有问题的指令处,重新读取并实行该指令,因此指令预取中止非常中断该当返回到产生该指令预取中止非常中断的指令处,而不是当前指令的下一条指令。
指令预取中止非常中断由当前实行的指令在ALU里实行时产生,当指令预取中止非常中断发生时,程序计数器pc的值还未更新,它指向当前指令后面第2条指令(对付ARM指令,它指向当前指令地址加8字节的位置;对付Thumb指令,它指向当前指令地址加4字节的位置)。此时处理器将值(pc-4)保存到lr_abt中,它指向当前指令的下一条指令,以是返回操作可以通过下面指令实现:
SUBSPC,LR_abt,#4
未定义指令非常:
未定义指令非常中断由当前实行的指令在ALU里实行时产生,当未定义指令非常中断产生时,程序计数器pc的值还未更新,它指向当前指令后面第2条指令(对付ARM指令,它指向当前指令地址加8字节的位置;对付Thumb指令,它指向当前指令地址加4字节的位置),当未定义指令非常中断发生时,处理器将值(pc-4)保存到lr_und中,此时(pc-4)指向当前指令的下一条指令,以是从未定义指令非常中断返回可以通过如下指令来实现:
MOVPC,LR_und
软中断指令(SWI)非常:
SWI非常中断和未定义非常中断指令一样,也是由当前实行的指令在ALU里实行时产生,当SWI指令实行时,pc的值还未更新,它指向当前指令后面第2条指令(对付ARM指令,它指向当前指令地址加8字节的位置;对付Thumb指令,它指向当前指令地址加4字节的位置),当未定义指令非常中断发生时,处理器将值(pc-4)保存到lr_svc中,此时(pc-4)指向当前指令的下一条指令,以是从SWI非常中断处理返回的实现方法与从未定义指令非常中断处理返回一样:
MOVPC,LR_svc
数据中止非常:
发生数据访问非常中断时,程序要返回到该有问题的指令处,重新访问该数据,因此数据访问非常中断该当返回到产生该数据访问中止非常中断的指令处,而不是当前指令的下一条指令。 数据访问非常中断由当前实行的指令在ALU里实行时产生,当数据访问非常中断发生时,程序计数器pc的值已经更新,它指向当前指令后面第3条指令(对付ARM指令,它指向当前指令地址加12字节的位置;对付Thumb指令,它指向当前指令地址加6字节的位置)。此时处理器将值(pc-4)保存到lr_abt中,它指向当前指令后面第2条指令,以是返回操作可以通过下面指令实现:
SUBSPC,LR_abt,#8
上述每一种非常发生时,其返回地址都要根据详细非常类型进行重新修复返回地址,「再次强调下,被打断程序的返回地址保存在对应非常模式下的LR_excep里」。
2. 模式规复非常发生后,进入非常处理程序时,将用户程序寄存器R0~R12里的数据保存在了非常模式下栈里面,非常处理完返回时,要将栈里保存的的数据再规复回原来R0~R12里。
毫无疑问在非常处理过程中必须要担保非常处理入口和出口时栈指针SP_excep要一样,否则规复到R0~R12里的数据禁绝确,返回被打断程序时实行现场不一致,涌现问题,虽然将实行现场规复了,但是此时还是在非常模式下,CPSR里的状态是非常模式下状态。
因此要规复SPSR_excep里的保存状态到CPSR里,SPSR_excep是被打断程序实行时的状态,在规复SPSR_excep到CPSR的同时,CPU的模式和状态从非常模式切换回了被打断程序实行时的模式和状态。
此刻程序现场规复了,状态也规复了,但PC里的值仍旧指向非常模式下的地址空间,我们要让CPU连续实行被打断程序,因此要再手动改变PC的值为进入非常时的返回地址,该地址在非常处理入口时已经打算好,直接将PC = LR_excep即可。
上述操作可以一步一步实现,但是常日我们可以通过一条指令实现上述全部操作:
LDMFDSP_excp!,{r0-r12,pc}^
注:SP_excep为对应非常模式下SP,^符号表示规复SPSR_excep到CPSR。
六、非常与模式关系reset非常进入SVC模式fiq快速中断要求非常进入快中断模式,支持高速数传输及通道处理(FIQ非常相应时进入此模式)irq中断要求非常进入中断模式,用于通用中断处理,(IRQ非常相应时进入此模式)prefetch预取指中止,数据中止非常进入中止模式,用于支持虚拟内存和/或存储器保护undef未定义指令非常进入未定义模式,支持硬件协处理器的软件仿真(未定义指令非常相应时进入此模式)swi软件中断,复位非常进入管理模式,操作系统保护代码(系统复位和软件中断相应时进入此模式)七、irq中断非常1.中断的观点什么是中断,我们从一个生活的例子引入。我们正在家中看书,溘然电话铃响了,你放下书本,去接电话,和来电话的人交谈,然后放下电话,回来连续看你的书。这便是生活中的"中断"的征象,也便是正常的事情过程被外部的事宜打断了。
在处理器中,所谓中断,是一个过程,即CPU在正常实行程序的过程中,碰着外部/内部的紧急事宜须要处理,暂时中断(中止)当出路序的实行,而转去为事宜做事,待做事完毕,再返回到停息处(断点)连续实行原来的程序。为事宜做事的程序称为中断做事程序或中断处理程序。
严格地说,上面的描述是针对硬件事宜引起的中断而言的。用软件方法也可以引起中断,即事先在程序中安排分外的指令,CPU实行到该类指令时,转去实行相应的一段预先安排好的程序,然后再返回来实行原来的程序,这可称为软中断。把软中断考虑进去,可给中断再下一个定义:中断是一个过程,是CPU在实行当出路序的过程中因硬件或软件的缘故原由插入了另一段程序运行的过程。因硬件缘故原由引起的中断过程的涌现是不可预测的,即随机的,而软中断是事先安排的。
2. 中断处理流程中断非常发生时,全体处理流程:
中断流程
如上图所示:
实行实行到0x30000008时产生中断cpu实行4大步3小步1)保存CPSR到SPSR_irq2)根据非常类型,设置模式标识位CPSR[4:0],CPU实行状态CPSR[5]:T位=0和关闭中断3)设置返回地址LR=0x300000104)将PC指向对应的非常向量表地址[中断IRQ:0x00000018]
进入到非常向量表后实行 b 指令,跳转到非常处理函数非常处理函数须要实行以下操作
1)改动返回地址SUBSPC,LR_irq,#4,即0x3000000C2)保存现场寄存器3)跳入中断处理函数isr_proccess(),实行中断处理程序4)规复现场寄存器5)返回现场PC=LR
程序又回到0x3000000C位置,连续实行
关于中断更详细的讲解,会在后续文章中详细讲解,请关注 [一口Linux]。
八、swi非常SWI指令SWI指令的格式为:
SWI{条件}24位的立即数
SWI指令用于产生软件中断,以便用户程序能调用操作系统的系统例程。操作系统在SWI的非常处理程序中供应相应的系统做事,指令中24位的立即数指定用户程序调用系统例程的类型,干系参数通过通用寄存器通报,当指令中24位的立即数被忽略时,用户程序调用系统例程的类型由通用寄存器R0的内容决定,同时,参数通过其他通用寄存器通报。
举例:
SWI0x02;该指令调用操作系统编号位02的系统例程。
BKPT指令
BKPT指令的格式为: BKPT 16位的立即数 BKPT指令产生软件断点中断,可用于程序的调试。
举例以下是一个包含非常向量表的代码,程序值填写了reset非常和swi非常的入口,其他入口地址可以用空指令nop添补在这个位置。
areafirst,code,readonlycode32entry;定义的非常向量表vectorbreset_handler;跳转到reset_handlernopbswi_handler;SWI指令非常跳转的地址nopnopnopnopnopswi_handler;swihandlercode;非常处理首先要压栈保存处理器现场mrsr0,cpsrbicr0,r0,#0x1forrr0,r0,#0x10msrcpsr_c,r0;ldrr0,[lr,#-4];得到SWI指令的机器码,lr前面那个指令是swi指令,下标在该指令中;bicr0,r0,#0xff000000;通过机器码得到SWINUMBERmovspc,lr;lr>pc且spsr->cpsr返回SVC->USERreset_handler;初始化SVC模式堆栈ldrsp,=0x40001000;修合法前的模式从SVC模式改变为USER模式mrsr0,cpsrbicr0,r0,#0x1forrr0,r0,#0x10msrcpsr_c,r0;初始化USER模式堆栈ldrsp,=0x40000800movr0,#1;USERSWIswi5;openAPPUSER这条语句由用户程序自己出发非常;不雅观察并记录比拟指令实行前后的PCLRCPSRSPSRSP的变革;并思考非常产生后处理器硬件自动发生了那些变革addr1,r0,r0stopbstopend
运行过程如下所示:
swi指令实行
紧张是把稳不雅观察swi实行前和实行后,模式的变革,大家可以按照4大步3小步来剖析。
swi指令实行前后比拟
如何同时跳转并切换模式?从swi非常返回时,我们须要实行两个动作:
将spsr拷贝回cpsr,pc = lr 跳转回原来的位置这两个动作都必须要实行,但是如果分步实行的话,spsr拷贝回去后,当前模式就变回了usr模式,那么对应的lr的值就变成了lr_usr,此时的值0x0【之前没有实行过bl指令】,那怎么跳转会去呢? 我们可以用以下命令
movspc,lr
此命令同时实行两个动作:
pc=lrcpsr=spsr返回SVC->USER
从而实现了同时跳转并切换模式。 如果入口已经利用ldm压栈可以用一下指令回答:
LDMFDSP_excp!,{r0-r12,pc}^
拜会第五章。
如何获取软中断号?要获取swi指令的中断号,我们只能从swi的机器码中得到对应的值,swi指令格式
而要想得到swi这条指令的内容,就要先找到这条指令的地址, 而lr的值是swi这条指令的下一条指令的地址,以是我们可以通过以下代码得到软中断号。ldrr0,[lr,#-4];得到SWI指令的机器码,lr前面那个指令是swi指令,下标在该指令中bicr0,r0,#0xff000000;通过机器码得到SWINUMBER
系统调用于swi系统调用
linux的运用程序有很多的系统调用,比如open,read,socket等实际上会触发swi非常,触发系统调用sys_open,sys_read等,内核根据swi的值来实行详细的操作。
每个别系调用都有自己惟一的编号,系统调用函数的标识符在以下文件定义:
linux/arch/arm/kernel/calls.S
内容如下:
/0/CALL(sys_restart_syscall)CALL(sys_exit)CALL(sys_fork)CALL(sys_read)CALL(sys_write)…………/375/CALL(sys_setns)CALL(sys_process_vm_readv)CALL(sys_process_vm_writev)CALL(sys_kcmp)CALL(sys_finit_module)#ifndefsyscalls_counted.equsyscalls_padding,((NR_syscalls+3)&~3)-NR_syscalls#definesyscalls_counted#endif.reptsyscalls_paddingCALL(sys_ni_syscall).endr
SWI代码片段剖析
搜索下vector_swi,找到入口函数
arch\arm\kernel\entry-common.S
.align5ENTRY(vector_swi)@保存现场subsp,sp,#S_FRAME_SIZEstmiasp,{r0-r12}@Callingr0-r12addr8,sp,#S_PCstmdbr8,{sp,lr}^@Callingsp,lrmrsr8,spsr@calledfromnon-FIQmode,sook.strlr,[sp,#S_PC]@SavecallingPCstrr8,[sp,#S_PSR]@SaveCPSRstrr0,[sp,#S_OLD_R0]@SaveOLD_R0zero_fp@得到swi的指令地址,确保是swi指令ldrscno,[lr,#-4]@getSWIinstructionA710(andip,scno,#0x0f000000@checkforSWI)A710(teqip,#0x0f000000)A710(bne.Larm710bug)@tbl即是数组表基地址get_thread_infotskadrtbl,sys_call_table@loadsyscalltablepointerldrip,[tsk,#TI_FLAGS]@checkforsyscalltracing@打消高8位bicscno,scno,#0xff000000@maskoffSWIop-code@#define__NR_SYSCALL_BASE0x900000这里swi的值实际上是0x9000000x900001...以是要打消这个高位的9eorscno,scno,#__NR_SYSCALL_BASE@checkOSnumber@根据索引号,去tbl这个数组中调用函数@tbl:数组表基地址,scno:要调用的sys_write()的索引值lsl#2:左移2位,一个函数指针霸占4个字节cmpscno,#NR_syscalls@checkuppersyscalllimitadrlr,ret_fast_syscall@returnaddressldrccpc,[tbl,scno,lsl#2]@callsys_routine
这里首先得到swi这条指令的内容,swi指令位于lr-4,缘故原由如下图
andip,scno,#0x0f000000