2. ARM64架构基础(上)

在将 Linux 内核调度器移植到 ARM64 平台之前,必须深入理解 ARM64 架构的核心基础。本章聚焦于 AArch64 执行状态、异常级别模型以及寄存器组体系,这些是操作系统运行和上下文切换的硬件基石。

2.1 AArch64 执行状态

ARMv8 架构引入了两种执行状态:AArch64 和 AArch32。调度器移植主要面向 AArch64 状态,它提供了 64 位虚拟地址空间和全新的异常模型。

特性AArch64AArch32
寄存器宽度64 位通用寄存器 (X0-X30)32 位通用寄存器 (R0-R15)
指令集A64 指令集(固定 32 位长度)A32/T32 指令集(可变长度)
虚拟地址空间最大 48 位(可扩展至 52 位)最大 32 位
异常级别EL0 - EL3(4 个级别)PL0 - PL2(3 个级别)
兼容性原生 64 位应用向后兼容 ARMv7 32 位应用
调度器视角: AArch64 状态下的 64 位寄存器使得内核可以管理更大的进程地址空间,同时异常级别的清晰划分让调度器能够在 EL1(内核态)安全地执行上下文切换,而用户进程运行在 EL0。
AArch64 与 AArch32 执行状态对比 AArch64 状态 • 64 位通用寄存器 (X0-X30) • 64 位 PC/SP • 异常级别: EL0-EL3 • 虚拟地址: 48 位 • 指令集: A64 (固定 32-bit) • 调度器运行在 EL1 AArch32 状态 • 32 位通用寄存器 (R0-R15) • 32 位 PC/SP • 特权级别: PL0-PL2 • 虚拟地址: 32 位 • 指令集: A32/T32 • 兼容 ARMv7 遗留应用

2.2 异常级别(Exception Levels: EL0 - EL3)

ARM64 架构定义了 4 个异常级别,数字越大特权越高。调度器主要工作在 EL1,而用户进程运行在 EL0。异常级别的设计为操作系统提供了强大的隔离和保护机制。

异常级别名称运行内容调度器相关
EL0用户态 (User)用户应用程序进程上下文被调度,无权访问内核数据结构
EL1内核态 (Kernel)操作系统内核调度器主代码运行于此,执行上下文切换
EL2虚拟机监视器 (Hypervisor)虚拟化层 (KVM, Xen)调度虚拟 CPU,管理虚拟机调度
EL3安全监视器 (Secure Monitor)安全固件 (ATF, TrustZone)切换安全/非安全世界,调度器不直接涉及
关键概念: 异常级别之间的切换通过异常(Exception)或返回指令(ERET)实现。当用户进程(EL0)触发系统调用或中断时,CPU 会提升到 EL1 执行内核代码,调度器借此机会进行进程选择与切换。
ARM64 异常级别层次结构 EL3 - 安全监视器 (Secure Monitor) 安全固件 / ATF / TrustZone 切换 EL2 - 虚拟机监视器 (Hypervisor) KVM / Xen / 虚拟 CPU 调度 EL1 - 内核态 (Kernel) Linux 内核 / 调度器主代码 / 上下文切换 EL0 - 用户态 (User) 用户应用程序 / 进程

2.3 寄存器组

ARM64 架构的寄存器组是调度器进行上下文切换的核心操作对象。理解寄存器分类和用途,是编写正确且高效的 switch_to 汇编代码的前提。

2.3.1 通用寄存器(X0 - X30)

AArch64 提供了 31 个 64 位通用寄存器 X0 - X30。每个寄存器也可作为 32 位使用(W0 - W30),低 32 位有效,高 32 位清零。

寄存器别名用途说明调度器上下文保存
X0 - X7参数寄存器传递函数参数和返回值需要保存(调用者保存)
X8间接结果寄存器返回大结构体指针需要保存
X9 - X15临时寄存器函数内部临时变量需要保存(调用者保存)
X16 - X17IP0, IP1过程内调用临时寄存器需要保存
X18平台寄存器平台特定用途(如 TCB)通常不保存
X19 - X28被调用者保存寄存器函数必须保存并恢复必须保存
X29FP (帧指针)栈帧基址指针必须保存
X30LR (链接寄存器)函数返回地址必须保存
调度器上下文切换要点: 在 switch_to 汇编中,必须保存并恢复 X19-X30、SP、PC(通过 LR 和栈操作)。X0-X18 由编译器生成的调用者保存代码处理,调度器无需显式保存。

2.3.2 特殊寄存器

特殊寄存器控制 CPU 状态、异常处理、内存管理和系统配置。调度器移植需要重点关注以下寄存器:

寄存器全称功能描述调度器相关性
SP_EL0 / SP_EL1栈指针不同异常级别的栈指针切换进程时需切换 SP_EL0(用户栈)
ELR_EL1异常链接寄存器异常返回地址保存被中断进程的 PC
SPSR_EL1保存的程序状态寄存器异常发生时的 PSTATE保存 CPU 状态标志(条件码、中断掩码)
TPIDR_EL1硬件线程 ID 寄存器存储 per-CPU 变量基址调度器用于快速访问当前 CPU 数据
TTBR0_EL1 / TTBR1_EL1页表基址寄存器用户/内核地址空间页表进程切换时需切换 TTBR0_EL1
CONTEXTIDR_EL1上下文 ID 寄存器标识当前进程 ASIDTLB 管理,调度器写入新进程 ASID
调度器上下文切换涉及的寄存器 通用寄存器 (X0-X30) X0-X7 : 参数/返回值 (调用者保存) X8 : 间接结果寄存器 X9-X15 : 临时寄存器 (调用者保存) X16-X17: IP0/IP1 过程内调用 X18 : 平台寄存器 X19-X28: 被调用者保存 (必须保存) X29 (FP) : 帧指针 (必须保存) X30 (LR) : 链接寄存器 (必须保存) 特殊寄存器 SP_EL0 / SP_EL1 : 栈指针 ELR_EL1 : 异常返回地址 (PC) SPSR_EL1 : 保存的程序状态 TPIDR_EL1 : per-CPU 变量基址 TTBR0_EL1 : 用户页表 (进程切换) TTBR1_EL1 : 内核页表 CONTEXTIDR_EL1 : 上下文 ID (ASID) ... 其他系统寄存器 红色高亮区域为调度器上下文切换必须保存/恢复的寄存器

2.4 上下文切换的寄存器保存策略

在 Linux 内核调度器中,switch_to 宏负责保存当前进程的寄存器状态并恢复下一个进程的状态。以下是 ARM64 平台上下文切换的核心汇编逻辑:

/*
 * switch_to 核心流程 (arch/arm64/kernel/entry.S)
 * 参数:
 *   x0 - prev (当前进程 task_struct)
 *   x1 - next (下一个进程 task_struct)
 *   x2 - last (返回时保存 prev 的地址)
 */
ENTRY(cpu_switch_to)
    // 保存被调用者保存寄存器 (callee-saved)
    stp x19, x20, [x8, #16 * 0]   // 保存到 prev->thread.cpu_context
    stp x21, x22, [x8, #16 * 1]
    stp x23, x24, [x8, #16 * 2]
    stp x25, x26, [x8, #16 * 3]
    stp x27, x28, [x8, #16 * 4]
    stp x29, x30, [x8, #16 * 5]   // FP 和 LR

    // 保存栈指针
    mov x9, sp
    str x9, [x8, #16 * 6]         // 保存 SP

    // 切换 TTBR0_EL1 (用户地址空间)
    ldr x10, [x1, #TSK_TI_TTBR0]  // 从 next->thread_info 加载页表
    msr ttbr0_el1, x10
    isb                             // 指令同步屏障

    // 恢复下一个进程的寄存器
    ldp x19, x20, [x9, #16 * 0]   // 从 next->thread.cpu_context 恢复
    ldp x21, x22, [x9, #16 * 1]
    ldp x23, x24, [x9, #16 * 2]
    ldp x25, x26, [x9, #16 * 3]
    ldp x27, x28, [x9, #16 * 4]
    ldp x29, x30, [x9, #16 * 5]   // 恢复 FP 和 LR

    // 恢复栈指针
    ldr x10, [x9, #16 * 6]
    mov sp, x10

    // 返回,此时 LR 指向新进程的返回地址
    ret
ENDPROC(cpu_switch_to)
移植要点: 上述代码展示了 ARM64 上下文切换的核心。注意 TTBR0_EL1 的切换必须在恢复寄存器之前完成,以确保新进程的用户空间页表生效。同时,isb 指令保证了页表切换的可见性。

2.5 本章小结

  • AArch64 执行状态 是 ARM64 的原生 64 位模式,提供 64 位寄存器、48 位虚拟地址和 4 级异常模型。
  • 异常级别 EL0-EL3 为调度器提供了清晰的隔离边界:调度器运行在 EL1,用户进程在 EL0,虚拟化在 EL2,安全固件在 EL3。
  • 通用寄存器 X0-X30 中,X19-X30 和 SP 是上下文切换必须保存的寄存器,X0-X18 由调用者负责。
  • 特殊寄存器 如 TTBR0_EL1、ELR_EL1、SPSR_EL1 和 TPIDR_EL1 在进程切换和异常处理中起关键作用。
  • 理解这些硬件基础是编写正确、高效的调度器移植代码的前提,后续章节将深入调度器数据结构与算法在 ARM64 上的具体实现。