Harmonyos内核源码分析(寄存器)|ARM37寄存器一气呵成,不再神秘明确ARM系列寄存器是基于ARM720T.pdf文档的。阅读本文前,推荐阅读鸿蒙内核源码分析(总目录)arm系统系列。寄存器的本质。神秘莫测,如梦如雾多年。揭开本质后,发现寄存器是一个32位的存储空间,只是一个int变量,但它的厉害之处在于使用频率极高,让人不敢相信怎么做,再复杂再牛逼应用程序,无论是电商、游戏还是直播,都变成了有限的十几个寄存器来玩,简直太神奇了。这篇文章会把寄存器的数量和作用解释清楚,至于如何把复杂的上层程序变成这几十个寄存器来玩?这是编译器的问题,不在讨论范围之内。ARM7有37个寄存器,详见示意图:这些寄存器不能同时显示。哪些寄存器可供使用由处理器指令状态和工作模式指定。图中有一一对应关系。其中,31个通用32位寄存器被系统和用户模式复用,其余5个为异常(或称为特权中的R8_*~R14_*的寄存器)称为模式专用寄存器。请注意,r8和r8_fiq是两个不同的寄存器。名称前缀便于记忆,易于管理,并表现出同级别的概念理解。这样就组成了31●其中r13寄存器用于SP寄存器,它始终指向栈顶,因为每种工作模式都有独立的运行栈,所以有独立的寄存器来记住各自的栈顶。●同理,r14寄存器是用于LR模式切换时保存切换位置的寄存器,也是独立存在的,说明在模式间跳回时不需要重新赋值r14_*,只需跳出时保存。系统和用户态共享r13(sp)和r14(lr)寄存器,所以在每个子函数的栈帧中,必须保存上一个调用它的函数的SP和LR值,并且这两个寄存器的值执行完成后必须从栈帧中恢复,否则无法定义回去后从哪里开始计算偏移位置。r15(pc)寄存器指向代码段。所有模式重用的原因是它是共享的。一段代码,不管你跑不跑,代码段就在哪儿,不增不减。●6个状态寄存器,包括CPSR(1)和SPSR_*(5),主要用于自运行或模式切换后的各种状态保存。●CPSR:程序状态寄存器(ccurrentprogramstatusregister)(当前程序状态寄存器),在任何处理器模式下都可以访问CPSR使得异常返回后可以恢复异常发生时的工作状态。当特定的异常中断发生时,该寄存器用于存放当前程序状态寄存器的内容。当异常中断退出时,可以使用SPSR恢复CPSR。七种工作模式关于工作模式在鸿蒙内核源码解析(总目录)的工作模式中有详细介绍,大家可以自己去查看。这是一个简短的描述。下图摘自ARM720T.pdf第43页,在ARM系统中,CPU工作在以下七种模式:●用户模式(usr):属于普通用户模式,不能直接切换到其他模式,以及ARM处理器的正常程序执行状态。●快速中断模式(fiq):支持高速数据传输和通道处理,FIQ异常响应时进入该模式●外部中断模式(irq):用于一般中断处理,IRQ异常响应时进入该模式●管理模式(svc):操作系统保护模式,当系统复位和软件中断响应时进入该模式(由系统调用触发执行软中断SWI命令)数据访问终止模式(abt):数据或指令预取时进入该模式被终止,可以用来处理内存故障,实现虚拟内存,内存保护。●系统模式(sys):以特权运行操作系统任务,类似于用户模式,但具有直接切换到其他模式等特权●未定义指令中止模式(und):处理未定义指令陷阱,当未定义时进入该模式执行指令,可用于支持硬件协处理器的软件仿真。除用户模式外,其余6种工作模式均属于特权模式。特权模式中除系统模式外的其他5种模式称为异常模式。大多数程序运行在用户态。进入特权模式是为了处理中断、异常,或者访问受保护的系统资源●硬件特权级别:系统模式>异常模式>用户模式??●快速中断(fiq)和慢速中断(irq)的区别:快速中断期间禁止中断加工。每种模式都有自己独立的入口和独立的运行栈空间。该系列的CPU已经介绍过,只要提供入口函数和运行空间,CPU就可以工作。入口函数解决了指令来源的问题,运行空间解决了指令运行的地方问题。而在多核的情况下,每个CPU核的每个特权模式都有自己独立的栈空间。注意是特权模式下的栈空间,用户模式下的栈空间由用户(应用)程序提供。R0寄存器R0是寄存器中的王牌,被称为一号寄存器,是通用寄存器中使用频率最高的,随便翻翻汇编代码就能看到它的影子。鸿蒙开机第一条跳转指令为r0=0reset_vector://鸿蒙开机代码/*clearregisterTPIDRPRW*/movr0,#0@r0=0mcrp15,0,r0,c13,c0,4@c0,c13=0,C13为processidentifiermeaningseepage64ofARM720T.PDF/*dosomeearlycpusetup:i/dcachedisable,mmudisabled*/@disableMMU,i/dcachemrcp15,0,r0,c1,c0,0@r0=c1,c1寄存器详细解释见page64bicr0,#(1<<12)@位清除指令,清除r0的第11位bicr0,#(1<<2|1<<0)@清除第0位和第2位,禁用MMU和缓存0位:MMUenable/disable2bits:Cacheenable/disablemcrp15,0,r0,c1,c0,0@c1=r0看自旋锁的汇编代码。这些代码已在本系列中进行了详细说明。大家可以自行前往鸿蒙内核源码解析(总目录)查看。movr1,#1@r1=11:@loop的作用,因为SEV是广播事件。lock->rawLock的值不一定改变了ldrexr2,[r0]@r0=&lock->rawLock,即r2=lock->rawLock当cmpr2、#0@r2和0不等于wfene@时,表示资源被占用,CPU核心进入休眠状态。写入r2=0cmpeqr2,#0@然后比较r2是否等于0,若相等则获取锁bne1b@若不相等则继续进入循环dmb@使用DMB指令隔离保证数据inbuffer中已经执行到RAMbxlr@这时候肯定已经获得了锁,跳回调用ArchSpinLock函数R0潜移默化的做了两件事,凸显了它的重要性:第一个参数由R0保存,当然第二个参数是保存到R1●函数的返回值统一交给R0保管,比如a->b,b执行完后会把返回值返回给r0,返回给a后,a会取来自r0的值,无论得到什么,都会认为是b的返回值。默认情况下,只有r0被识别为保存返回值,这是规则。具体看一个C函数及其汇编,系列里已经讲过了,大家可以自己看。//+++++++++++++square(c->assembly)++++++++++++++++++++++++intsquare(inta,intb){returna*b;}square(int,int):subsp,sp,#8@sp负8,表示为square分配栈空间,只用2个栈空间完成计算strr0,[sp,#4]@第一个参数入栈strr1,[sp]@第二个参数入栈入栈ldrr1,[sp,#4]@取第一个参数给r1ldrr2,[sp]@取第二个参数给r2mulr0,r1,r2@执行a*b给R0,返回值的工作一直在交给R0的addsp,sp,#8@函数执行完毕,待释放的栈空间bxlr@subroutine返回,相当于movpc,lr,即跳转到调用处//++++++++++++fp(c->程序集)++++++++++++++++++++++++intfp(intb){inta=1;returnssquare(a+b,a+b);}fp(int):push{r11,lr}@r11(fp)/lr入栈,保存调用者mainmovr11的位置,使用sp@r11保存sp值,函数栈的起始位置subsp,sp,#8@sp减8,表示为fp分配栈空间,只用2个栈空间就可以完成计算strr0,[sp,#4]@先把参数值存入SP+4,此时存入r0把参数movr0,#1@r0=1strr0,[sp]@把SPldrr0的位置存1,[sp]@把SP的值给R0ldrr1,[sp,#4]@把SP+4给R1addr1,r0,r1@的值执行r1=a+bmovr0,r1@r0=r1,用r0,r1传递参数blsquare(int,int)@firstmovlr,pc然后movpcsquare(int,int)movsp,r11@函数执行后需要释放申请的栈空间pop{r11,lr}@弹出r11和lr,lr是特殊标号,弹出时会自动复制到lr寄存器中这段代码是也适合理解后面的寄存器。R7寄存器为什么要单独说R7寄存器呢,因为它偶尔会作为一个特殊的寄存器来使用。内核为上层应用程序提供了数百个系统调用函数。当发生系统调用时,在CPU工作模式切换过程中,R7寄存器中一直保存着系统调用号,通过系统调用号可以查询到对应的注册函数。系统调用章节有详细的流程描述,这里只列出部分代码//4个参数的系统调用底层处理staticinlinelong__syscall4(longn,longa,longb,longc,longd){registerlonga7__asm__("a7")=n;//在R7寄存器中保存系统调用号registerlonga0__asm__("a0")=a;//R0registerlonga1__asm__("a1")=b;//R1registerlonga2__asm__("a2")=c;//R2registerlonga3__asm__("a3")=d;//R3__asm_syscall("r"(a7),"0"(a0),"r"(a1),"r"(a2),"r"(a3))}//切换后到SVC模式,汇编代码调用C语言实现的系统调用的统一入口LITE_OS_SEC_TEXTUINT32*OsArmA32SyscallHandle(UINT32*regs){UINT32ret;UINT8nArgs;UINTPTRhandle;UINT32cmd=regs[REG_R7];//从R7寄存器获取系统调用号handle=g_syscallHandle[cmd];//查询系统调用的注册函数//...}fp(R11)寄存器R11:可以作为一个通用寄存器,在特定的编译选项开启的情况下可以作为帧指针寄存器FP,用于实现栈回溯功能。GNU编译器(gcc)默认使用R11作为存放变量的通用寄存器,所以默认不能使用FP的stacktraceback。功能。为了支持调用栈解析功能,需要在编译参数中加入-fno-omit-frame-pointer选项,提示编译器使用R11作为FP。FP寄存器(FramePoint),帧指针寄存器,指向当前函数的父函数的栈帧起始地址。使用该寄存器可以得到父函数的栈帧,从栈帧中获取父函数的FP,可以得到祖父函数的栈帧。以此类推,可以追溯程序调用栈,得到函数之间的调用关系。在鸿蒙内核R11中,使用了FP寄存器。SP(R13)寄存器SP:栈指针寄存器(stackpointer),指向函数栈的栈顶,所以fp和sp划定了函数栈的范围,函数除了动态申请的内存需要在外面玩,剩下的可以在这个空间里玩。在鸿蒙内核R13中,使用了SP寄存器。LR(R14)寄存器的异常发生会中断程序的正常运行,并将控制流转移到相应的异常处理(异常响应)。在处理完一些异常(fiq、irq)事件后,系统还希望回到异常发生时中断的源程序断点,继续完成源程序的执行(异常返回),这就需要解决记录源程序的断点位置,以便返回正确的异常。类似于子程序调用和返回。当在主程序中调用子程序时(通过子程序调用指令),也需要在主程序中记录调用点的位置,以便日后子程序的返回。LR:链接寄存器(linkedpointer),用于解决以上问题,ARM处理器使用R14记录断点和调用点,即R14作为返回链接寄存器(LR),保证你知道你在哪里来自的位置被中断以便继续执行。在鸿蒙内核R14中,使用了LR寄存器。PC(R15)寄存器pc:程序计数器寄存器(ProgramCounterRegister)从代码段中取指令,指向代码段的具体位置。PC寄存器是与arm流水线结构相关的设计,在后续的流水线章节会详细介绍,敬请关注。在鸿蒙内核R15中,作为PC寄存器使用。CPSRregisterCPSR(currentprogramstatusregister)当前程序状态寄存器CPSR有四个8位区域:标志域(F)、状态域(S)、扩展域(X)、控制域(C)32位程序状态寄存器可分为4个字段:1.Bits[31:24]是条件标志字段,用f表示;2、bit[23:16]为状态位域,用s表示;3、bit[15:8]为扩展位域,用x表示;4.Bit[7:0]为控制位域,用c表示;CPSR不同于其他寄存器。其他的寄存器是用来存放数据的,整个寄存器是有意义的。CPSR寄存器是按位工作的,即它的每一位都有特殊的含义,记录特定的信息。CPSR的低8位(包括I、F、T和M[4:0])称为控制位,不能被程序修改,除非CPU运行在特权模式下,程序才能修改控制位位N、Z、C和V是条件代码标志。它们的内容可以通过算术或逻辑运算的结果来改变,可以判断某条指令是否被执行!重要的!●CPSR的第31位为N,符号标志。记录相关指令执行后结果是否为负。如果为负数N=1,如果为非负数N=0。●CPSR的第30位为Z,0标志。记录相关指令的结果是否为0。如果结果为0,则Z=1。如果结果不为0,则Z=0。●CPSR的第29位为C,进位标志(Carry)。通常,对无符号数执行操作。加法运算:当运算结果产生进位(无符号数溢出)时,C=1,否则C=0。减法运算(包括CMP):当运算过程中产生借位(无符号数溢出)时,C=0,否则C=1。●CPSR的第28位为V,溢出标志(Overflow)。在进行有符号运算时,如果超出了机器可以识别的范围,就称为溢出。MSR{Condition}程序状态寄存器(CPSRorSPSR)_
