这里紧张是AArch32架构(即32位,后面就都简写成ARM了),相比拟较大略,入门必备。
ARM处理器模式在引入安全扩展之前,ARM有7中处理器模式,个中6种是特权模式,剩下一种为用户程序运行的非特权模式。特权模式下,可以做一些user模式下不能做的操作,比如mmu配置和cache操作。

TrustZone Security Extensions引入了独立于模式的两种安全状态,和一种新的Monitor模式。如此就有8种CPU模式了。
TrustZone Security Extensions的环境中,通过将所有的硬件和软件资源按设备来划分,来提升系统安全性。 在CPU处于Normal (Non-secure) state,其不能访问在Secure state下分配的内存。
当前处理器所处模式,由CPSR(当出路序状态寄存器)寄存器的值决定,改变cpu模式可以通过特权软件显示设置该寄存器,也可以由非常触发(发生非常后,CPU就会自动切换到对应的模式)。
嵌入式进阶教程分门别类整理好了,看的时候十分方便,由于内容较多,这里就截取一部分图吧。
须要的朋友私信【内核】即可领取
寄存器ARM架构供应16个32位的通用寄存器(R0-R15),R0-R14可用作普通的数据存储。
R15为PC寄存器,修正R15可以改变程序的实行流程。PC值指向当前实行指令的地址加8处,PC是有读写限定的。
R14也称LR(Link register),常日用于保存当前函数的返回地址(上一级函数),当别的暇时,也可以用作普通寄存器用。每种模式下都有自己独立(物理上)的R14。
R13也称SP,即用于保存堆栈的栈顶指针,当别的暇时,也可以用作普通寄存器用。每一种非常模式都有其自己独立的r13,它常日指向非常模式所专用的堆栈。当ARM进入非常模式的时候,程序就可以把一样平常通用寄存器压入堆栈,返回时再出栈,担保了各种模式下程序的状态的完全性。
程序也可以访问CPSR,SPSR是前一个实行模式下CPSR的副本。
虽然软件可以访问这些寄存器,但是不同模式下,部分寄存器实际对应的物理存储位置可能不同,也便是说,不同模式下,部分寄存器(除R0-7和R15外)实际物理上是不同(虽然对软件来说是透明的,软件看到的是相同的逻辑上的寄存器),这些寄存器只能在相应模式下才能访问。
Program Status Registers。CPSR用于存储:flags、当前CPU模式、中断禁用标记、当前CPU状态等。在user模式下,CPSR对应的寄存器为APSR(限定版本的CPSR)。
Coprocessor 15CP15,系统掌握协处理器,用于掌握Core的须要特性,包括c0-c15紧张的32位寄存器,这些寄存器常日也通过名称访问,如CP15.SCTLR
cache问题让程序实行的韶光变得不可知。
对付实时性哀求高的系统来说有点不可接管。
外设须要用up-to-date的数据,须要cache掌握。
其他tag占物理空间,但不打算在cache size内
invalidate操作:丧失落cache。
clean操作:将脏数据刷入内存,担保同等。
ARM64中包含如下几种类型的非常:
中断(Interrupts),便是我们平常理解的中断,紧张由外设触发,是范例的异步非常。 ARM64中紧张包括两种类型的中断:IRQ(普通中断)和FIQ(高优先级中断,处理更快)。 Linux内核中彷佛没有利用FIQ,还没有仔细看代码,详细不详述了。Aborts。 可能是同步或异步非常。包括指令非常(取指令时产生)、数据非常(读写内存数据时产生),可以由MMU产生(比如范例的缺页非常),也可以由外部存储系统产生(常日是硬件问题)。Reset。复位被视为一种分外的非常。Exception generating instructions。由非常触发指令触发的非常,比如Supervisor Call (SVC)、Hypervisor Call (HVC)、Secure monitor Call (SMC)非常级别(EL)ARM中,非常由级别之分,详细如下图所示,只要关注:
普通的用户程序处于EL0,级别最低
内核处于EL1,HyperV处于EL2,EL1-3属于特权级别。
非常处理Arm中的非常处理过程与X86比较相似,同样包括硬件自动完成部分和软件部分,同样须要设置中断向量,保存高下文,不同的非常类型的处理办法可能有细微差别。 这里不详述了。
须要关注:用户态(EL0)不能处理非常,当非常发生在用户态时,非常级别(EL)会发生切换,默认切换到EL1(内核态)。
中断向量表Arm64架构中的中断向量表有点特殊(相对付X86来说~),包含16个entry,这16个entry分为4组,每组包含4个entry,每组中的4个entry分别对应4种类型的非常:
SErrorFIQIRQSynchronous Aborts4个组的分类根据发生非常时是否发生非常级别切换、和利用的堆栈指针来差异。分别对应于如下4组:
非常发生在当前级别且利用SP_EL0(EL0级别对应的堆栈指针),即发生非常时不发生非常级别切换,可以大略理解为非常发生在内核态(EL1),且利用EL0级别对应的SP。 这种情形在Linux内核中未进行本色处理,直接进入bad_mode()流程。非常发生在当前级别且利用SP_ELx(ELx级别对应的堆栈指针,x可能为1、2、3),即发生非常时不发生非常级别切换,可以大略理解为非常发生在内核态(EL1),且利用EL1级别对应的SP。 这是比较常见的场景。非常发生在更低级别且在非常处理时利用AArch64模式。 可以大略理解为非常发生在用户态,且进入内核处理非常时,利用的是AArch64实行模式(非AArch32模式)。 这也是比较常见的场景。非常发生在更低级别且在非常处理时利用AArch32模式。 可以大略理解为非常发生在用户态,且进入内核处理非常时,利用的是AArch32实行模式(非AArch64模式)。 这中场景基本未做处理。代码剖析## 中断向量表
Linux内核中,中断向量表实现在entry.S文件中,代码如下:
/ Exception vectors. /.align11/el1代表内核态,el0代表用户态/ENTRY(vectors)ventryel1_sync_invalid// Synchronous EL1t ventryel1_irq_invalid// IRQ EL1tventryel1_fiq_invalid// FIQ EL1t/内核态System Error ,利用SP_EL0(用户态栈)/ventryel1_error_invalid// Error EL1tventryel1_sync// Synchronous EL1hventryel1_irq// IRQ EL1hventryel1_fiq_invalid// FIQ EL1h/内核态System Error ,利用SP_EL1(内核态栈)/ventryel1_error_invalid// Error EL1hventryel0_sync// Synchronous 64-bit EL0ventryel0_irq// IRQ 64-bit EL0ventryel0_fiq_invalid// FIQ 64-bit EL0/用户态System Error ,利用SP_EL1(内核态栈)/ventryel0_error_invalid// Error 64-bit EL0#ifdef CONFIG_COMPATventryel0_sync_compat// Synchronous 32-bit EL0ventryel0_irq_compat// IRQ 32-bit EL0ventryel0_fiq_invalid_compat// FIQ 32-bit EL0ventryel0_error_invalid_compat// Error 32-bit EL0#elseventryel0_sync_invalid// Synchronous 32-bit EL0ventryel0_irq_invalid// IRQ 32-bit EL0ventryel0_fiq_invalid// FIQ 32-bit EL0ventryel0_error_invalid// Error 32-bit EL0#endifEND(vectors)
可以明显看出分组和分类的情形。
invalid类处理带invalid后缀的向量都是Linux做未做进一步处理的向量,默认都会进入bad_mode()流程,解释这类非常Linux内核无法处理,只能上报给用户进程(用户态,sigkill或sigbus旗子暗记)或die(内核态)
带invalid后缀的向量终极都调用了inv_entry,inv_entry实现如下:
/ Invalid mode handlers / /Invalid类非常都在这里处理,统一调用bad_mode函数/.macroinv_entry, el, reason, regsize = 64kernel_entry el, \regsize/传入bad_mode的三个参数/movx0, sp/reason由上一级传入/movx1, #\reason/esr_el1是EL1(内核态)级的ESR(非常状态寄存器),用于记录非常的详细信息,详细内容解析须要参考硬件手册/mrsx2, esr_el1/调用bad_mode函数/bbad_mode.endm
调用bad_mode,是C函数,关照用户态进程或者panic。
/ bad_mode handles the impossible case in the exception vector. /asmlinkage void bad_mode(struct pt_regs regs, int reason, unsigned int esr){siginfo_t info;/获取非常时的PC指针/void __user pc = (void __user )instruction_pointer(regs);console_verbose();/打印非常信息,messages中可以看到。/pr_crit("Bad mode in %s handler detected, code 0x%08x -- %s\n",handler[reason], esr, esr_get_class_string(esr));/打印寄存器内容/__show_regs(regs);/如果发生在用户态,须要向其发送旗子暗记,这种情形下,发送SIGILL旗子暗记,以是就不会有core文件产生了/info.si_signo = SIGILL;info.si_errno = 0;info.si_code = ILL_ILLOPC;info.si_addr = pc;/给用户态进程发生旗子暗记,或者die然后panic/arm64_notify_die("Oops - bad mode", regs, &info, 0);}
arm64_notify_die:
void arm64_notify_die(const char str, struct pt_regs regs, struct siginfo info, int err){/如果发生非常的高下文处于用户态,则给相应的用户态进程发送旗子暗记/if (user_mode(regs)) {current->thread.fault_address = 0;current->thread.fault_code = err;force_sig_info(info->si_signo, info, current);} else {/如果是内核态,则直接die,终极会panic/die(str, regs, err);}}IRQ中断处理
场景的场景中(不考虑EL2和EL3),IRQ处理分两种情形:用户态发生的中断和内核态发生的中断,相应的中断处理接口分别为:
el0_syncel1_sync
相应代码如下:
el1_sync:
.align6el1_irq:/保存中断高下文/kernel_entry 1enable_dbg#ifdef CONFIG_TRACE_IRQFLAGSbltrace_hardirqs_off#endif/调用中断处理默认函数/irq_handler/如果支持抢占,处理稍繁芜/#ifdef CONFIG_PREEMPTget_thread_info tskldrw24, [tsk, #TI_PREEMPT]// get preempt countcbnzw24, 1f// preempt count != 0ldrx0, [tsk, #TI_FLAGS]// get flagstbzx0, #TIF_NEED_RESCHED, 1f// needs rescheduling?blel1_preempt1:#endif#ifdef CONFIG_TRACE_IRQFLAGSbltrace_hardirqs_on#endif/规复高下文/kernel_exit 1ENDPROC(el1_irq)
代码非常大略,紧张便是调用了irq_handler()函数,不做深入解析了,有兴趣可以自己再看看代码。
el0_sync处理类似,紧张差异在于:其涉及用户态和内核态的高下文切换和规复。
.align6el0_irq:kernel_entry 0el0_irq_naked:enable_dbg#ifdef CONFIG_TRACE_IRQFLAGSbltrace_hardirqs_off#endif/退出用户高下文/ct_user_exitirq_handler #ifdef CONFIG_TRACE_IRQFLAGSbltrace_hardirqs_on#endif/返回用户态/bret_to_userENDPROC(el0_irq)









