ARMv8-A Exception Handling

本节来详细描述下ARMv8-A下的异常处理。

异常的概述:

当设备正在愉快的执行时候,此时发生了一个异常,处理器就必须暂停当前的任务,转而去处理发生的异常。当异常处理完毕后,处理就会发回到被打断的程序继续执行。

此图就是描述异常的处理流程。

异常的分类:

异常分为同步异常和异步异常,而这两种异常的区别是:

同步异常:如果异常的产生是通过执行指令,而且返回结果提供了产生异常的详细信息,则此类异常称为同步异常(Synchronous Exceprions)

异步异常:如果异常不是通过指令执行产生,而且返回结果不提供产线异常的详细信息,则此类异常为异步异常(asynchronous exceptions)

异步异常(Asynchronous Exceptions)

  • 中断
    • IRQ(normal priority interrupt)
    • FIQ(Fast Interrupt)
    • 其中FIQ的优先级比IRQ的优先级高。这两种中断类型都是通过pin脚连接到中断控制器(GIC),当外部触发一个中断的时候,中断控制器就会仲裁和转发此中断到相应的CPU
  • SError(System Error)

同步异常(Synchronous Exceptions)

同步异常包含的类型很多,比如第一次申请内存,当去写或者读的时候就会触发一次page fault,此类异常就属于同步异常

  • 从MMU发出的指令abort。 当cpu尝试去执行一块没有执行权限的内存区域,则会发生指令abort
  • 从MMU发出的数据abort。 当cpu尝试去写一块只读的内存区域,则会发生data abort
  • SP和PC的对齐检查
  • 同步外部异常
  • 未分配的异常:当第一次访问申请内存时,cpu会通过mmu去寻找虚拟地址对应的物理地址,而此时因为此虚拟地址没有对应的物理地址,则会发生未分配的异常
  • debug时发生的异常
  • Services Calls(SVC,SMC,HVC)

SVC,HVC,SMC指令

  • SVC(Supervisor Call)
    • 当用户空间通过系统调用陷入到内核空间的时候,则最终会通过SVC指令进入到内核空间
  • HVC(Hypervisor Call)
    • 当在ARMv8-A架构下,normal world, EL1尝试去访问EL2的时候,则会陷入到虚拟化层的,其中是通过HVC指令
  • SMC(Secure Moniter Call)
    • 用于切换noramal world 和 secure world使用。

异常处理涉及到的寄存器

我们先来看下异常处理的整个过程

当异常发生时,硬件会自动做如下几个步骤:

  • 将PSTATE的状态保存到SPSR_ELn寄存器中,根据当前所在的EL级别来保存到对应的SPSR寄存器中。如果当前的异常发生在EL1,则将PSTATE的状态保存到SPSR_EL1中
  • 将PC的值存储在ELR_ELn寄存器中,也是根据当前所处的EL级别。
  • 以上两个步骤是硬件自动保存的,不需要软件的干涉
  • 假设当前是一个IRQ中断,软件上也会保存一些现场的。
  • 当异常处理完毕后,则会将之前保存的值恢复
  • SPSR_ELn的值恢复到PSTATE中,ELR_ELn恢复到PC

PSTATE,SPSR已经在上一篇文章Process Status中做过描述了,此处不做说明了。

当一个异常发生时,如果确定异常的类型

当异常发生时,我们可以从ESR_ELn寄存器中获取对应的异常状态。

  • Bits[31:26]  用来确定异常的类型,Exception class
  • Bit[25]: 用来确定异常指令的长度,0代表16位异常指令,1代表32位异常
  • Bits[24:0]: 用来确定具体的异常,每种异常类型独立定义此字段

比如:

ARM64 异常向量表

这四组在EL1的实现为,也就是linux内核的实现为:

424/*
425 * Exception vectors.
426 */
427 .pushsection ".entry.text", "ax"
428
429 .align  11
430ENTRY(vectors)
431 kernel_ventry   1, sync_invalid         // Synchronous EL1t
432 kernel_ventry   1, irq_invalid          // IRQ EL1t
433 kernel_ventry   1, fiq_invalid          // FIQ EL1t
434 kernel_ventry   1, error_invalid        // Error EL1t
435
436 kernel_ventry   1, sync             // Synchronous EL1h
437 kernel_ventry   1, irq              // IRQ EL1h
438 kernel_ventry   1, fiq_invalid          // FIQ EL1h
439 kernel_ventry   1, error            // Error EL1h
440
441 kernel_ventry   0, sync             // Synchronous 64-bit EL0
442 kernel_ventry   0, irq              // IRQ 64-bit EL0
443 kernel_ventry   0, fiq_invalid          // FIQ 64-bit EL0
444 kernel_ventry   0, error            // Error 64-bit EL0
445
446#ifdef CONFIG_COMPAT
447 kernel_ventry   0, sync_compat, 32      // Synchronous 32-bit EL0
448 kernel_ventry   0, irq_compat, 32       // IRQ 32-bit EL0
449 kernel_ventry   0, fiq_invalid_compat, 32   // FIQ 32-bit EL0
450 kernel_ventry   0, error_compat, 32     // Error 32-bit EL0
451#else
452 kernel_ventry   0, sync_invalid, 32     // Synchronous 32-bit EL0
453 kernel_ventry   0, irq_invalid, 32      // IRQ 32-bit EL0
454 kernel_ventry   0, fiq_invalid, 32      // FIQ 32-bit EL0
455 kernel_ventry   0, error_invalid, 32        // Error 32-bit EL0
456#endif
457END(vectors)

分为四组,其中第一组是invaild的。

  • 如果异常级别是EL1,也就是Current EL,则偏移地址为0x200
  • 如果异常类型是64位应用程序,异常级别是EL0发生,则偏移地址为0x400
  • 如果异常类型是32位应用程序,异常级别是EL0发生,则偏移地址为0x600
发布了187 篇原创文章 · 获赞 108 · 访问量 37万+

猜你喜欢

转载自blog.csdn.net/longwang155069/article/details/105228737