持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第16天,点击查看活动详情
缺页异常处理依赖于处理器的架构,因此缺页异常的底层处理流程实现在内核代码中特定于架构的部分中。下面以ARM64为例来介绍缺页异常的底层处理流程。 在ARM64架构里把异常分成同步异常和异步异常两种。通常异步异常指的是中断,而同步异常指的是异常。重点介绍ARM64中的同步异常。
当处理器有异常发生时,处理器会首先跳转到ARM64的异常向量表中。Linux 5.0内核关于异常向量表的描述在arch/arm64/kernel/entry.S汇编文件中。
<arch/arm64/kernel/entry.S>
/*
* 异常向量表
*/
.pushsection ".entry.text", "ax"
.align 11
ENTRY(vectors)
# 使用SP0栈寄存器的当前异常类型的异常向量表
kernel_ventry 1, sync_invalid
kernel_ventry 1, irq_invalid
kernel_ventry 1, fiq_invalid
kernel_ventry 1, error_invalid
# 使用SPx栈寄存器的当前异常等级类型的异常向量表
kernel_ventry 1, sync
kernel_ventry 1, irq
kernel_ventry 1, fiq_invalid
kernel_ventry 1, error
# AArch64状态下低异常等级类型的异常向量表
kernel_ventry 0, sync
kernel_ventry 0, irq
kernel_ventry 0, fiq_invalid
kernel_ventry 0, error
# AArch32状态下低异常等级类型的异常向量表
kernel_ventry 0, sync_compat, 32
kernel_ventry 0, irq_compat, 32
kernel_ventry 0, fiq_invalid_compat, 32
kernel_ventry 0, error_compat, 32
END(vectors)
因此对于异常,程序会跳转到el1_sync函数中。
<arch/arm64/kernel/entry.S>
el1_sync:
kernel_entry 1
mrs x1, esr_el1
lsr x24, x1, #ESR_ELx_EC_SHIFT
cmp x24, #ESR_ELx_EC_DABT_CUR
b.eq el1_da
cmp x24, #ESR_ELx_EC_IABT_CUR
b.eq el1_ia
cmp x24, #ESR_ELx_EC_SYS64
b.eq el1_undef
cmp x24, #ESR_ELx_EC_SP_ALIGN
b.eq el1_sp_pc
cmp x24, #ESR_ELx_EC_PC_ALIGN
b.eq el1_sp_pc
cmp x24, #ESR_ELx_EC_UNKNOWN
b.eq el1_undef
cmp x24, #ESR_ELx_EC_BREAKPT_CUR
b.ge el1_dbg
b el1_i
ARMv8架构中有一个与访问失效相关的寄存器——异常综合信息寄存器(Exception SyndromeRegister,ESR)
ESR的结构:
ESR寄存器一共包含如下4个字段。
- Bit[63:32]:保留的位。
- Bit[31:26]:表示异常类型(Exception Class,EC),这个字段指示发生异常的类型,同时用来索引ISS字段(Bit[24:0])。
- Bit 25:IL,表示同步异常的指令长度。
- Bit[24:0]:具体的异常指令编码(Instruction Specific Syndrome,ISS)。这个异常指令编码依赖不同的异常类型,不同的异常类型有不同的编码格式。
回到el1_sync汇编函数中,首先读取esr_el1寄存器,然后根据EC字段进行简单分类和判断,Linux内核会优先处理如下类型。
- ESR_ELx_EC_DABT_CUR:发生在EL1的数据异常。
- ESR_ELx_EC_IABT_CUR:发生在EL1的指令异常。
- ESR_ELx_EC_SYS64:在AArch64状态下,执行MSR、MRS或者系统指令产生的异常。
- ESR_ELx_EC_SP_ALIGN:SP对齐时发生的异常。
- ESR_ELx_EC_PC_ALIGN:PC指针对齐时发生的异常
- ESR_ELx_EC_UNKNOWN:发生在EL1的未知异常。
- ESR_ELx_EC_BREAKPT_CUR:调试时发生的异常。
对于剩余的情况,统一处理。 我们以发生在EL1的数据异常为例,处理函数是el1_da汇编函数。