Skip to content

Latest commit

 

History

History
218 lines (135 loc) · 6.99 KB

3-中断异常与系统调用.md

File metadata and controls

218 lines (135 loc) · 6.99 KB

中断异常与系统调用

注意:从EL0EL1的三种方法就是异常中断系统调用

1. 中断、异常

1.1 通用概念

中断(Interrupt)

  • 外部硬件设备所产生的信号

  • 异步:产生原因和当前执行指令无关,如程序被磁盘读打断

异常(Exception)

  • 软件的程序执行而产生的事件

  • 包括系统调用(System Call

    • 用户程序请求操作系统提供服务
  • 同步:产生和当前执行或试图执行的指令相关

(中断是被外来的打断的,异常时内部自己发生的,但是arm里所有都叫异常,中断叫异步异常,异常叫同步异常)

1.2 不同体系结构术语的对应关系

通用概念 产生 原因 AArch64 x86-64
中断 硬件 异步 异步异常 (重置/中断) 中断 (可屏蔽/不可屏蔽)
异常 软件 同步 同步异常 (终止/异常指令) 异常 (Fault/Trap/Abort)

AArch64

异步异常

  • 重置(Reset)

    • 最高级别的异常,用以执行代码初始化CPU核心
    • 由系统首次上电或控制软件、Watchdog等触发
  • 中断(Interrupt)

    • CPU外部的信号触发,打断当前执行
    • 如计时器中断、键盘中断等

同步异常

  • 中止(Abort)
    • 失败的指令获取或数据访问
    • 如访问不可读的内存地址等
  • 异常产生指令(Exception generating instructions)
    • SVC:用户程序 -> 操作系统
    • HVC:客户系统 -> 虚拟机管理器
    • SMC:Normal World -> Secure World

x86-64

  • 中断(设备产生、异步)
    • 可屏蔽:设备产生的信号,通过中断控制器与处理器相连,可被 暂时屏蔽(如,键盘、网络事件)
    • 不可屏蔽:一些关键硬件的崩溃(如,内存校验错误)
  • 异常(软件产生、同步)
    • 错误(Fault): 如缺页异常(可恢复)、段错误(不可恢复)等
    • 陷阱(Trap): 无需恢复,如断点(int 3)、系统调用(int 80)
    • 中止(Abort): 严重的错误,不可恢复(机器检查)

1.3 中断注意事项

  • 中断处理没有进程上下文
    • 中断(和异常相比)和具体的某条指令无关
    • 也和中断时正在跑的进程、用户程序无关
    • 中断处理handler不能睡眠

约束

  1. 不能睡眠,也不能调用可能会睡眠的任务
  2. 不能调用schedule()调
  3. 不能释放信号或调用可能睡眠的操作
  4. 不能和用户地址空间交换数据

1.4 中断和异常的处理

中断与异常的处理使用同一套机制,差异仅在选择handler中提现

中断异常处理流程aarch64中断处理

中断和异常处理必做事项

  1. 进入中断或异常时
    • 需保存处理器状态,方便之后恢复执行
    • 需准备好在高特权级下进行执行的环境
    • 需选择合适的异常处理器代码进行执行
    • 需保证用户态和内核态之间的隔离
  2. 处理时
    • 需获得关于异常的信息,如系统调用参数、错误原因等
  3. 返回时
    • 需恢复处理器状态,返回低特权级,继续正常执行流

AArch64的中断和异常处理

  1. 发生 – 信息保存

    • 异常或中断发生后,硬件会将错误码和部分上下文信息存储在寄存器中
      • 处理器状态(PSTATE)-> Saved Program Status Register (SPSR_EL1
      • 当前指令地址(PC)-> Exception Link Register(ELR_EL1)
      • 异常发生原因 ->
        • Serror与异常:Exception Syndrome Register(ESR_EL1)
        • 中断:GIC中的寄存器(使用MMIO读取)
    • 安全性问题
      • 上述寄存器均不可在用户态(EL0)中访问
  2. 发生 – 进入EL1

    • 硬件会适当修改处理器状态(PSTATE),进入EL1执行
    • 问题:栈内存的安全性
      • 进入EL1级别后,栈指针(SP)会自动换用SP_EL1
      • 从而实现用户栈->内核栈
      • 如需在EL1下使用SP_EL0作为栈指针,可配置SPSel寄存器
  3. 寻找handler的代码

    使用异常向量表

    • 每个异常级别存在独立的异常向量表(分级,x86-64不分级)

    • 表项为异常向量(Exception Vector):是处理异常或跳转到异常handler的小段汇编代码

    • 异常向量表的地址位于VBAR_EL1寄存器中

    • 选择表项取决于

      • 异常类型(同步、IRQ、FIQ、Serror)
      • 异常发生的特权级
      • 异常发生时的处理器状态(使用的栈指针/运行状态)
      aarch64异常向量表
  4. 返回(Exception Return)

    • ELR_EL1 -> PC,恢复PC状态
    • SPSR_EL1 -> PSTATE,恢复处理器状态
    • 降至EL0,硬件自动使用SP_EL0作为栈指针
    • 恢复执行

x86-64的中断和异常处理

  1. 进入异常
    • 硬件会将上下文信息和错误码存储在内核栈上
  2. 用异常向量表寻找handler
    • 不分级
    • 异常向量表中存handler的地址
  3. iret返回
  • 恢复程序上下文
  • 从内核态返回用户态
  • 继续执行用户程

aarch64区别

  1. x86-64信息都存栈上,而aarch64都存在寄存器里
  2. x86-64不分级

2. 系统调用

系统调用与安全

  • AArch64使用寄存器传参,个数有限
    • 如ChCore的系统调用支持使用寄存器X0-X7最多8个参数
  • 若系统调用需要更多参数如何处理?
    • 使用结构体打包参数,并将结构体的指针作为参数
  • 问题:内存安全性
    • 作为参数的指针必须经过检测!
    • 指向NULL -> kernel crash
    • 指向内核内存 -> 安全漏

用户指针检测

  • 完备的指针检测十分耗时
    • 需要遍历用户进程的所有合法内存区域进行检测
    • 合法区域由链表(vma)管理
  • Linux解决方法:非全面检查
    • Linux仅初步检测用户指针是否属于对应进程的用户内存区域的最大可能 边界
    • 即使通过初步检测,用户指针仍然可能非法(如指向尚未分配的栈空间 等)
    • 直接将非法的指针交给内核使用会导致内核出现页错误,内核态的页错误通常意味着bug,内核会打印异常信息并中止用户进程
    • Linux采用了一些复杂机制来防止这一情况发生

处理用户指针问题

  • 内核代码仅使用特定代码片段访问用户指针(如copy_from_user)
    • 由访问用户指针而导致内核内存错误的代码段是确定的
    • 或者可以该页表,设为read-only
  • 当内核发生页异常(Page Fault)时,内核会检查异常发生的PC
    • 若异常发生的PC属于访问用户指针的代码段,Linux尝试对其进行修复
    • 若不属于,则报告问题并终止用户程序