一、ARM64 linux异常向量表的初始化
在start_kernel函数开始执行之前是用汇编语言代码初始化CPU,其中非常主要的就是将异常向量表的基地址配置到VBAR_EL1寄存器中,从arch/arm64/kernel/head.S中可以找到如下代码:这段代码不仅配置了异常向量表,还配置了0号进程的内核堆栈和进程描述符。



这段汇编语言配置了0号进程的堆栈和进程描述符(0号进程的定义在init/init_task.c文件中),即将init_task地址保存在x4中,进入init_cpu_task中,将init_task偏移TSK_STACK(stack指针)加载到x5,将x5+THREAD_SIZE作为内核堆栈的栈底,linux内核中的堆栈是从高地址向低地址增长的;接着0号进程的进程描述符init_task保存到SP_EL0寄存器,注意因为当前处于EL1权限级别,所以这里SP实际上是SP_EL1寄存器。
接下来的两行汇编语言代码是将异常向量表的基地址配置到VBAR_EL1寄存器中,这样发生异常时,CPU可以根据异常的类型和VBAR_EL1寄存器计算出异常处理入口地址。ARM64架构中的中断向量表包含16个kernel_ventry,分为4组,每组包含4个,参见arch/arm64/kernel/entry.S文件,摘录代码如下,4组kernel_ventry以空行隔开。


这4个组是根据发生异常时是否发生异常级别切换,以及使用的堆栈指针的不同来划分的。
异常类型 | 触发场景 | 向量表入口示例 | 栈指针(SP)模式 | 执行模式 | 对应处理函数 |
---|---|---|---|---|---|
**同步异常(Sync)** | 系统调用、缺页、指令错误23 | kernel_ventry 1, t, 64, sync |
SP_EL0 (EL1t) | 64位EL1内核态 | el1_sync |
IRQ中断 | 外设中断(如定时器、网卡)35 | kernel_ventry 0, t, 64, irq |
SP_EL0 (EL0t) | 64位用户态(EL0) | el0_irq |
FIQ中断 | 高优先级中断(通常未启用)23 | kernel_ventry 1, h, 64, fiq |
SP_EL1 (EL1h) | 64位EL1内核态 | el1_fiq_invalid |
**SError(Error)** | 系统总线错误、ECC故障23 | kernel_ventry 0, t, 32, error |
SP_EL0 (EL0t) | 32位用户态(EL0) | el0_error |