ARM64 体系结构与内核实现

ARM64 是当今服务器、移动设备与嵌入式平台的主流指令集架构。本文从异常级别 EL0 到 EL3 与 AArch64 系统寄存器体系出发,深入剖析四级页表结构与地址翻译流程,详解设备树 DTS 语法与内核 of 系列接口、FDT 传递与 PSCI 固件调用约定、GICv3 中断控制器架构、cache 一致性与内存屏障语义,最后给出交叉编译与 QEMU 调试的完整实践路径,并与 x86_64 架构逐项对比。

ARM64(AArch64)是 ARMv8-A 引入的 64 位执行状态,如今已从手机、嵌入式设备一路走到服务器与云端。它拥有一套与 x86_64 截然不同的特权模型:异常级别(Exception Level)、系统寄存器(System Register)访问、设备树描述硬件、PSCI 完成电源管理。理解这些机制,是读懂 arch/arm64/ 下数万行内核代码的前提。

本文从异常级别与通用寄存器讲起,逐层剖析页表结构与地址翻译、设备树与内核解析路径、FDT 传递与 PSCI 固件接口、GICv3 中断控制器、cache 一致性与内存屏障,最后落到交叉编译、QEMU 调试以及 ARM64 与 x86_64 的逐项差异对比。


一、异常级别与寄存器体系

1.1 EL0 到 EL3 四级权限模型

ARMv8-A 定义了四个异常级别(Exception Level),级别越高权限越大:

异常级别典型用途说明
EL0用户态应用程序无特权,不能访问系统寄存器
EL1操作系统内核Linux 内核运行于此,可配置 MMU
EL2HypervisorKVM 运行于此,管理虚拟机
EL3Secure Monitor负责安全世界与非安全世界切换

切换遵循「异常只能由低向高、返回只能由高向低」的原则。EL0 执行 SVC 陷入 EL1,EL1 执行 HVC 进入 EL2,需要切换安全世界时用 SMC 进入 EL3。

1.2 通用寄存器与系统寄存器

AArch64 提供 31 个 64 位通用寄存器 x0x30,低 32 位视图为 w0w30:

  • x0~x7:参数与返回值传递;x8:系统调用号
  • x9~x15:临时寄存器;x16/x17:过程内调用寄存器
  • x19~x28:被调用者保存寄存器
  • x29:帧指针 FP;x30:链接寄存器 LR

除通用寄存器外,还有一组系统寄存器,均需通过 MRS/MSR 访问:

寄存器作用
SP栈指针,EL0/EL1 各有独立的 SP_EL0/SP_EL1
SPSR_ELx保存异常发生前的 PSTATE
ELR_ELx保存异常返回地址
SCTLR_EL1系统控制寄存器,控制 MMU、cache、对齐检查
TCR_EL1翻译控制寄存器,配置页表粒度与 VA 位宽
TTBR0_EL1/TTBR1_EL1用户态与内核态页表基址
VBAR_EL1异常向量表基址

1.3 PSTATE 与异常向量表

PSTATE 不是单个寄存器,而是一组状态位,包括条件标志 N/Z/C/V、中断屏蔽位 D/A/I/F、当前异常级别 EL、栈指针选择位 SP。

mrs x0, daif          // 读取中断屏蔽位
mrs x1, CurrentEL     // 读取当前异常级别(位于 bits[3:2])
lsr x1, x1, #2        // 右移得到 0/1/2/3
msr daifset, #2       // 关闭 IRQ
msr daifclr, #2       // 打开 IRQ

异常向量表由 VBAR_EL1 指向,共 16 个表项(4 类异常 x 4 种来源),每项固定 128 字节。Linux 在 arch/arm64/kernel/entry.S 中定义 vectors 符号,用 kernel_ventry 宏展开。


二、页表与地址翻译机制

2.1 翻译粒度与页表级数

ARM64 支持三种翻译粒度(Translation Granule),由 TCR_EL1 的 TG0/TG1 选择:

粒度单个 PTE 覆盖范围常见页表级数适用场景
4KB512 项 x 8 字节 = 4KB4 级通用 Linux 内核默认
16KB2048 项 x 8 字节 = 16KB4 级部分移动平台
64KB8192 项 x 8 字节 = 64KB3 级大内存服务器、减少 TLB miss

4KB 粒度下每级 9 位索引,四级共 36 位,加页内偏移 12 位,构成 48 位虚拟地址。Linux 默认 VA_BITS=48,也可选 52 位。

2.2 TTBR0 与 TTBR1 的地址空间划分

ARM64 采用「高地址内核、低地址用户」的划分:

0x0000_0000_0000_0000  用户空间 (TTBR0),VA_BITS=48 时上限 0x0000_FFFF_FFFF_FFFF
0xFFFF_0000_0000_0000  内核空间 (TTBR1),线性映射 + vmalloc
0xFFFF_FFFF_FFFF_FFFF  地址空间顶端

虚拟地址最高位为 0 时用 TTBR0_EL1,为 1 时用 TTBR1_EL1。这一硬件级自动切换意味着系统调用陷入内核时无需切换页表基址,只需保证内核映射在 TTBR1 中。

2.3 TCR_EL1 关键字段

TCR_EL1 是地址翻译的核心配置寄存器:

字段含义
T0SZTTBR0 覆盖位数 = 64 - T0SZ,用户态通常 48
T1SZTTBR1 覆盖位数,内核态通常 48
TG0TTBR0 翻译粒度(0b00=4KB, 0b01=64KB, 0b10=16KB)
TG1TTBR1 翻译粒度(0b01=16KB, 0b10=4KB, 0b11=64KB)
IPS中间物理地址位数(32/36/40/42/44/48/52)
SH0/SH1页表遍历的可共享性(Non-shareable/Outer/Inner)
mrs x0, tcr_el1
bic x1, x0, #0x7          // 清除 TG0 位
orr x1, x1, #0x0          // TG0 = 0b000 (4KB)
msr tcr_el1, x1
isb                       // 系统寄存器写入后需要 ISB 同步

2.4 AT 与 TLBI 指令

地址翻译与 TLB 维护是内核内存管理的关键操作:

at s1e1r, x0              // 以 EL1 权限翻译 x0 中的地址,不产生访存
mrs x1, par_el1           // 读取结果,bit0=1 表示翻译失败
tlbi vmalle1              // 失效 EL1 全部 TLB 项
tlbi vae1, x0             // 失效指定 VA 的 TLB 项
dsb ish                   // 等待失效操作完成
isb                       // 同步指令流水

AT 指令在 KVM 阶段二翻译、页错误诊断与内核自检中广泛使用,无需真正访存即可获知虚拟地址的物理映射。

2.5 mmap 地址空间布局

48 位 VA 的典型 arm64 系统上,用户进程布局为:低地址是保留区与代码段(.text),向上是数据段、堆、共享库映射区与向下增长的匿名 mmap 区,栈位于 0x7f... 附近向下增长,栈顶附近是 vdso/vvar。mmap 返回的地址通常落在 0x7f... 段,而 0xffff... 内核映射区用户态不可访问。


三、设备树与内核设备描述

3.1 DTS 语法基础

设备树源文件(Device Tree Source)用节点与属性描述硬件:

/dts-v1/;
/ {
    compatible = "linux,dummy-virt";
    #address-cells = <2>;
    #size-cells = <2>;

    cpus {
        #address-cells = <1>;
        #size-cells = <0>;
        cpu@0 {
            device_type = "cpu";
            compatible = "arm,cortex-a57";
            reg = <0x0>;
            enable-method = "psci";
        };
    };

    memory@40000000 {
        device_type = "memory";
        reg = <0x0 0x40000000 0x0 0x80000000>;
    };

    uart@9000000 {
        compatible = "arm,pl011", "arm,primecell";
        reg = <0x0 0x09000000 0x0 0x1000>;
        interrupts = <0x0 0x21 0x4>;
        clocks = <&clk_uart>;
    };
};

关键概念:compatible 是驱动匹配字符串,从最具体到最通用排列;reg 描述寄存器地址与长度,格式由父节点的 #address-cells/#size-cells 决定;phandle 通过 &label 引用节点;interrupts 给出中断号与触发类型(0x4 表示 level-high)。

3.2 编译与反编译

dtc -I dts -O dtb -o board.dtb board.dts    # 编译 DTS 为二进制 DTB
dtc -I dtb -O dts -o board.dts board.dtb    # 反编译 DTB 回 DTS
fdtdump board.dtb | head -20                # 查看 DTB 头部信息

3.3 运行时访问:/proc/device-tree

内核启动时解析 DTB 并构建 device_node 树,用户态可通过 /proc/device-tree 浏览:

ls /proc/device-tree/                                   # 查看根节点
cat /proc/device-tree/compatible | tr '\0' '\n'         # 读取 compatible
hexdump -C /proc/device-tree/uart@9000000/reg           # 查看寄存器范围

3.4 内核解析接口 of_*

驱动通过 of_* 系列 API 读取设备树信息:

#include <linux/of.h>
#include <linux/of_irq.h>

static int my_probe(struct platform_device *pdev)
{
    struct device *dev = &pdev->dev;
    struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    int irq = platform_get_irq(pdev, 0);
    u32 freq;

    if (!res)
        return -ENODEV;
    if (irq < 0)
        return irq;
    if (of_property_read_u32(dev->of_node, "clock-frequency", &freq))
        freq = 24000000;

    dev_info(dev, "mmio=%pR irq=%d freq=%u\n", res, irq, freq);
    return 0;
}

static const struct of_device_id my_match[] = {
    { .compatible = "vendor,my-uart" },
    { }
};
MODULE_DEVICE_TABLE(of, my_match);

3.5 Overlay 动态修改

设备树 overlay 允许运行时向已有设备树追加节点,常用于 FPGA 加载后的外设热添加:

mkdir /sys/kernel/config/device-tree/overlays/mydev
cat mydev.dtbo > /sys/kernel/config/device-tree/overlays/mydev/dtbo
rmdir /sys/kernel/config/device-tree/overlays/mydev   # 移除 overlay

四、启动流程与 PSCI 固件接口

4.1 从 _start 到 start_kernel

arm64 内核启动路径位于 arch/arm64/kernel/head.S,入口符号是 _start:

_start → stext
  ├─ 关闭 MMU、配置栈、保存 x0~x3(FDT 指针在 x0)
  ├─ __create_page_tables     建立恒等映射与内核映射
  ├─ __cpu_setup              配置 TCR/MAIR 等系统寄存器
  ├─ __primary_switch         开启 MMU
  └─ __primary_switched → start_kernel   C 语言入口

启动时寄存器约定(见 Documentation/arch/arm64/booting.rst):

寄存器含义
x0物理地址指向 FDT(设备树二进制)
x1/x2/x3保留,必须为 0
PC内核镜像物理入口

4.2 FDT 的传递与解析

Bootloader 把 DTB 放在内存某处,并把其物理地址写入 x0。内核在 setup_arch() 中完成解析:

void __init setup_arch(char **cmdline_p)
{
    setup_machine_fdt(__fdt_pointer);   // 解析 FDT
    parse_early_param();
    bootmem_init();
    paging_init();
}

解析过程提取 bootargs 作为内核命令行、memory 节点构建内存模型、cpus 节点决定 CPU 数量与 enable-method。

4.3 PSCI 调用约定

PSCI(Power State Coordination Interface)是 ARM 定义的电源管理固件接口,通过 SMC 或 HVC 调用:

功能 ID名称作用
0x84000000PSCI_VERSION查询 PSCI 版本
0xC4000003CPU_ON上电指定 CPU
0xC4000004AFFINITY_INFO查询 CPU 状态
0x84000008SYSTEM_OFF关机
0x84000009SYSTEM_RESET重启

CPU_ON 的调用约定为:x0 放功能 ID 0xC4000003,x1 放目标 CPU 的 MPIDR 亲和性,x2 放次核入口物理地址,x3 放上下文标识,随后执行 SMC #0,x0 返回状态码(0 表示 SUCCESS)。内核在 drivers/firmware/psci/psci.c 中封装 psci_ops,smp_boot_secondary() 最终调用 psci_cpu_on() 唤醒从核。

4.4 从核启动与热插拔

从核上电后从内核提供的 secondary_entry 开始执行,需自行完成 MMU 配置、栈初始化,然后加入调度器。CPU 热插拔同样走 PSCI 的 CPU_OFF/CPU_ON 路径:

echo 0 > /sys/devices/system/cpu/cpu1/online   # 下线一个 CPU
echo 1 > /sys/devices/system/cpu/cpu1/online   # 重新上线

五、中断控制器 GIC

5.1 GICv2 与 GICv3 架构差异

特性GICv2GICv3
分发器GICD(MMIO)GICD(MMIO)
CPU 接口GICC(MMIO)ICC_* 系统寄存器 + GICR
中断数上限1020960(SPI)+ 每核 32 个 SGI/PPI
亲和性路由不支持支持 IROUTER 按 MPIDR 路由
虚拟化GICv2 vGIC原生支持 vLPI

GICv3 把 CPU 接口从 MMIO 改为系统寄存器访问,显著降低了中断处理延迟。

5.2 中断类型:SGI、PPI、SPI

类型中断号范围含义
SGI0~15软件生成中断(核间通信)
PPI16~31每核私有外设中断(如 arch timer)
SPI32~1019共享外设中断(如网卡、UART)
LPI8192 起基于 ITS 的消息型中断(GICv3/v4)

5.3 GICv3 系统寄存器

GICv3 的 CPU 接口通过 ICC_* 系统寄存器操作:

mrs x0, ICC_PMR_EL1       // 读取当前中断优先级阈值
mrs x0, ICC_IAR1_EL1      // 确认中断(读取 IAR)
and x1, x0, #0xffffff     // 提取中断号
msr ICC_EOIR1_EL1, x0     // 结束中断(写入 EOIR)
mov x0, #1
msr ICC_IGRPEN1_EL1, x0   // 使能组 1 中断

分发器 GICD 侧通过 MMIO 配置,GICD_ISENABLER<n> 偏移为 0x100 + 4*n,向其中写入 BIT(irq % 32) 即可使能对应 SPI。

5.4 irqchip 驱动与 /proc/interrupts

内核通过 irqchip 子系统注册 GIC 驱动,drivers/irqchip/irq-gic-v3.c 是 GICv3 的实现。中断控制器在设备树中描述:

intc: interrupt-controller@8000000 {
    compatible = "arm,gic-v3";
    reg = <0x0 0x08000000 0x0 0x10000>,    // GICD
          <0x0 0x080a0000 0x0 0xf60000>;   // GICR
    interrupt-controller;
    #interrupt-cells = <3>;
    interrupts = <0x1 0x9 0x4>;            // 维护中断
};
cat /proc/interrupts
#  11:       1234          0     GICv3  27 Level     arch_timer
#  13:          0       2048     GICv3  33 Level     eth0
# IPI0:       512        640     Rescheduling interrupts

六、Cache 一致性与内存屏障

6.1 PoU 与 PoC 概念

ARM 用两个「一致性点」描述 cache 层级关系:PoU(Point of Unification) 是某个 PE 的指令 cache 与数据 cache 达成一致的层级;PoC(Point of Coherency) 是系统中所有观察者(含 DMA 设备)看到一致数据的层级。DMA 设备通常不参与 cache 一致性,驱动必须显式维护:

dma_sync_single_for_device(dev, dma_handle, size, DMA_TO_DEVICE);   // 写回供设备读
dma_sync_single_for_cpu(dev, dma_handle, size, DMA_FROM_DEVICE);    // 失效供 CPU 读

6.2 数据 cache 与指令 cache 的维护

dc civac, x0              // 按 VA 清理并失效数据 cache
dc cvac, x0               // 按 VA 清理数据 cache(写回)
ic ivau, x0               // 按 VA 失效指令 cache

内核封装了 flush_icache_range()、__dma_flush_area() 等接口。自修改代码(如 JIT、kprobes)必须调用 flush_icache_range(),否则新写入的指令可能仍在数据 cache 中而未被取指单元看到。

6.3 DSB、DMB 与 ISB

指令语义典型用途
DMB数据内存屏障,保证屏障前后的访存顺序共享数据同步
DSB数据同步屏障,等待所有显式访存完成页表修改后、cache 操作后
ISB指令同步屏障,刷新流水线并重新取指系统寄存器写入后

ISB 在系统寄存器写入后几乎必需——CPU 可能在旧配置下继续执行若干条指令,ISB 强制其丢弃已预取的指令。

6.4 Linux 内存屏障到 ARM64 的映射

Linux APIARM64 实现说明
smp_mb()DMB ISH全屏障,Inner Shareable 域
smp_rmb()DMB ISHLD读屏障
smp_wmb()DMB ISHST写屏障
mb()DMB SY全系统屏障
barrier()空(编译器屏障)仅阻止编译器重排

ARM64 的内存模型是弱一致的(Weakly Ordered),CPU 可以自由重排访存顺序,因此并发代码必须显式使用屏障或原子操作。

6.5 非一致性设备与 dma_alloc_coherent

// 方案一:分配一致性内存,CPU 与设备共享同一份数据
void *buf = dma_alloc_coherent(dev, size, &dma_handle, GFP_KERNEL);
dma_free_coherent(dev, size, buf, dma_handle);

// 方案二:流式 DMA 映射,显式同步
dma_addr_t h = dma_map_single(dev, buf, size, DMA_TO_DEVICE);
dma_unmap_single(dev, h, size, DMA_TO_DEVICE);

在具备 CCI/CCN 互连一致性单元且设备标记为 dma-coherent 的平台上,硬件保证一致性,软件无需显式同步;否则必须依靠上述 API。


七、交叉编译与 QEMU 调试

7.1 工具链准备

sudo apt install gcc-aarch64-linux-gnu binutils-aarch64-linux-gnu \
                 qemu-system-arm device-tree-compiler
aarch64-linux-gnu-gcc --version     # 验证工具链

7.2 配置与编译内核

export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-
make defconfig                      # 生成默认配置
make -j$(nproc) Image.gz            # 多核并行编译
make -j$(nproc) dtbs

编译产物 arch/arm64/boot/Image.gz 即为可启动内核镜像。

7.3 QEMU 启动 virt 平台

QEMU 的 virt 机器类型模拟一套标准 ARM64 平台,自带设备树:

qemu-system-aarch64 \
  -machine virt \
  -cpu cortex-a57 \
  -smp 4 -m 2G \
  -kernel arch/arm64/boot/Image.gz \
  -append "console=ttyAMA0 root=/dev/vda rw" \
  -nographic

-machine virt 是通用虚拟平台,包含 GICv3、PL011 UART、virtio 设备;-cpu cortex-a57 指定 CPU 模型(也可用 max 启用全部特性);-nographic 把串口重定向到终端。

7.4 用 gdb 调试内核

# 终端一:启动 QEMU 并暂停,等待 gdb 连接
qemu-system-aarch64 -machine virt -cpu cortex-a57 -smp 1 -m 1G \
  -kernel arch/arm64/boot/Image.gz \
  -append "console=ttyAMA0 nokaslr" \
  -nographic -s -S

# 终端二:启动 gdb 连接
gdb-multiarch vmlinux
(gdb) target remote :1234
(gdb) break start_kernel
(gdb) continue
(gdb) info registers x0 x1

-s 等价于 -gdb tcp::1234,-S 表示启动时冻结 CPU。必须加 nokaslr,否则断点地址与符号表不匹配。


八、ARM64 与 x86_64 架构对比

8.1 核心差异总表

维度ARM64x86_64
指令集定长 32 位 RISC变长 CISC
通用寄存器31 个 64 位16 个 64 位
特权级EL0~EL3 四级Ring 0~3 四级
内存模型弱一致(Weakly Ordered)强一致(TSO)
页表粒度4KB/16KB/64KB仅 4KB(可叠加 2MB/1GB 大页)
页表级数3~4 级4 级(5 级可选)
页表基址TTBR0/TTBR1 分离CR3 单一基址
中断控制器GICAPIC / IOAPIC / MSI
启动描述设备树 DTBACPI / MP Table
系统寄存器MRS/MSR 专用指令RDMSR/WRMSR
系统调用SVC #0,号在 x8syscall,号在 rax
页表刷新TLBI 指令INVLPG 指令

8.2 原子指令与自旋锁

ARM64 的原子操作依赖独占访存指令对:

ldaxr x1, [x0]            // Load-Acquire Exclusive
add x1, x1, #1            // 修改 x1
stlxr w2, x1, [x0]        // Store-Release Exclusive,w2 返回 0 表示成功
cbnz w2, retry            // 失败则重试

Linux 的 atomic_inc() 在 arm64 上即展开为上述序列。相比之下 x86_64 的 LOCK XADD 由硬件保证原子性,无需重试循环,但在高竞争场景下 ARM64 的 LL/SC 方案可能表现更好。

8.3 等待与事件机制

指令语义
WFE等待事件,可被 SEV 或其他事件唤醒
WFI等待中断,进入低功耗状态
SEV向所有 PE 发送事件,唤醒处于 WFE 的核
SEVL向本核发送事件(用于循环首轮不阻塞)

SEV/WFE 常用于自旋锁的忙等优化与核间通信,Linux 的 cpu_relax() 在 arm64 上即展开为 yield。

8.4 常见排查清单

  1. 启动无输出:确认 console=ttyAMA0 与实际 UART 一致,检查 DTB 中 chosen/stdout-path
  2. 从核未上线:核对 enable-method 是否为 psci,检查 PSCI 版本与 CPU_ON 返回码
  3. 中断不触发:cat /proc/interrupts 观察计数,检查 GIC 节点 interrupt-cells 与 interrupt-parent
  4. DMA 数据错乱:确认设备是否需要 dma_alloc_coherent,检查 dma-coherent 属性
  5. 页表异常:dmesg | grep -i "translation fault",配合 AT 指令手工验证翻译结果
  6. cache 相关随机崩溃:检查自修改代码路径是否调用 flush_icache_range()

相关阅读

  • https://plumephp.com/os-boot-process/ —— 内核启动全流程,与本文 ARM64 启动路径互为补充
  • https://plumephp.com/os-interrupts/ —— 中断处理框架,GIC 驱动如何接入通用中断子系统
  • https://plumephp.com/os-virtual-memory/ —— 虚拟内存抽象层,页表结构与地址翻译的通用机制

延伸阅读

  1. Linux Kernel Documentation: Documentation/arch/arm64/(booting.rst、memory.rst、cpu-feature-registers.rst)
  2. ARM Architecture Reference Manual ARMv8, for ARMv8-A architecture profile(DDI 0487)
  3. Device Tree Specification, Release v0.4(devicetree.org)
  4. Robert Love, “Linux Kernel Development”(3rd Edition)— 内核整体架构与内存管理章节
  5. Linux 源码:arch/arm64/kernel/head.S、arch/arm64/mm/mmu.c、drivers/irqchip/irq-gic-v3.c

# ============================================================
# 完整可运行示例:交叉编译 ARM64 内核并在 QEMU virt 上启动
# 适用:Debian/Ubuntu x86_64 主机
# ============================================================
set -e

WORK=$HOME/arm64-lab
KERNEL_SRC=$WORK/linux
mkdir -p "$WORK"

echo "=== 步骤 1: 安装工具链 ==="
sudo apt update
sudo apt install -y gcc-aarch64-linux-gnu binutils-aarch64-linux-gnu \
                    qemu-system-arm device-tree-compiler bc flex bison \
                    libssl-dev libelf-dev
aarch64-linux-gnu-gcc --version | head -1

echo "=== 步骤 2: 获取内核源码 ==="
[ -d "$KERNEL_SRC" ] || git clone --depth 1 \
    https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git "$KERNEL_SRC"
cd "$KERNEL_SRC"

echo "=== 步骤 3: 配置并编译 ==="
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-
make defconfig
scripts/config --enable CONFIG_SERIAL_AMBA_PL011
scripts/config --enable CONFIG_SERIAL_AMBA_PL011_CONSOLE
scripts/config --enable CONFIG_ARM_ARCH_TIMER
make -j"$(nproc)" Image.gz

echo "=== 步骤 4: 制作最小 initramfs ==="
cd "$WORK"
mkdir -p initramfs/{bin,dev,proc,sys}
cp /bin/busybox initramfs/bin/ 2>/dev/null || true
cat > initramfs/init <<'EOF'
#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
echo "--- /proc/interrupts ---"
cat /proc/interrupts
echo "--- compatible ---"
tr '\0' '\n' < /proc/device-tree/compatible
echo "ARM64 QEMU 启动成功"
exec sh
EOF
chmod +x initramfs/init
cd initramfs && find . | cpio -o -H newc 2>/dev/null | gzip > ../initramfs.cpio.gz && cd ..

echo "=== 步骤 5: 在 QEMU 中启动内核 ==="
qemu-system-aarch64 \
    -machine virt -cpu cortex-a57 -smp 4 -m 2G \
    -kernel "$KERNEL_SRC/arch/arm64/boot/Image.gz" \
    -initrd "$WORK/initramfs.cpio.gz" \
    -append "console=ttyAMA0 rdinit=/init nokaslr" \
    -nographic

echo "=== 步骤 6: 调试模式(另开终端)==="
echo "  qemu-system-aarch64 -machine virt -cpu cortex-a57 -m 1G \\"
echo "    -kernel $KERNEL_SRC/arch/arm64/boot/Image.gz \\"
echo "    -append 'console=ttyAMA0 nokaslr' -nographic -s -S"
echo "  gdb-multiarch $KERNEL_SRC/vmlinux -ex 'target remote :1234'"

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「os」更多文章

  1. 内核网络栈:sk_buff、NAPI 与 XDP
  2. eBPF 开发实战:CO-RE 与 libbpf
  3. 内存回收机制:LRU、水位与 OOM