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 |
| EL2 | Hypervisor | KVM 运行于此,管理虚拟机 |
| EL3 | Secure 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 覆盖范围 | 常见页表级数 | 适用场景 |
|---|---|---|---|
| 4KB | 512 项 x 8 字节 = 4KB | 4 级 | 通用 Linux 内核默认 |
| 16KB | 2048 项 x 8 字节 = 16KB | 4 级 | 部分移动平台 |
| 64KB | 8192 项 x 8 字节 = 64KB | 3 级 | 大内存服务器、减少 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 是地址翻译的核心配置寄存器:
| 字段 | 含义 |
|---|---|
T0SZ | TTBR0 覆盖位数 = 64 - T0SZ,用户态通常 48 |
T1SZ | TTBR1 覆盖位数,内核态通常 48 |
TG0 | TTBR0 翻译粒度(0b00=4KB, 0b01=64KB, 0b10=16KB) |
TG1 | TTBR1 翻译粒度(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 | 名称 | 作用 |
|---|---|---|
0x84000000 | PSCI_VERSION | 查询 PSCI 版本 |
0xC4000003 | CPU_ON | 上电指定 CPU |
0xC4000004 | AFFINITY_INFO | 查询 CPU 状态 |
0x84000008 | SYSTEM_OFF | 关机 |
0x84000009 | SYSTEM_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 架构差异
| 特性 | GICv2 | GICv3 |
|---|---|---|
| 分发器 | GICD(MMIO) | GICD(MMIO) |
| CPU 接口 | GICC(MMIO) | ICC_* 系统寄存器 + GICR |
| 中断数上限 | 1020 | 960(SPI)+ 每核 32 个 SGI/PPI |
| 亲和性路由 | 不支持 | 支持 IROUTER 按 MPIDR 路由 |
| 虚拟化 | GICv2 vGIC | 原生支持 vLPI |
GICv3 把 CPU 接口从 MMIO 改为系统寄存器访问,显著降低了中断处理延迟。
5.2 中断类型:SGI、PPI、SPI
| 类型 | 中断号范围 | 含义 |
|---|---|---|
| SGI | 0~15 | 软件生成中断(核间通信) |
| PPI | 16~31 | 每核私有外设中断(如 arch timer) |
| SPI | 32~1019 | 共享外设中断(如网卡、UART) |
| LPI | 8192 起 | 基于 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 API | ARM64 实现 | 说明 |
|---|---|---|
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 核心差异总表
| 维度 | ARM64 | x86_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 单一基址 |
| 中断控制器 | GIC | APIC / IOAPIC / MSI |
| 启动描述 | 设备树 DTB | ACPI / MP Table |
| 系统寄存器 | MRS/MSR 专用指令 | RDMSR/WRMSR |
| 系统调用 | SVC #0,号在 x8 | syscall,号在 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 常见排查清单
- 启动无输出:确认
console=ttyAMA0与实际 UART 一致,检查 DTB 中chosen/stdout-path - 从核未上线:核对
enable-method是否为psci,检查 PSCI 版本与CPU_ON返回码 - 中断不触发:
cat /proc/interrupts观察计数,检查 GIC 节点interrupt-cells与interrupt-parent - DMA 数据错乱:确认设备是否需要
dma_alloc_coherent,检查dma-coherent属性 - 页表异常:
dmesg | grep -i "translation fault",配合AT指令手工验证翻译结果 - cache 相关随机崩溃:检查自修改代码路径是否调用
flush_icache_range()
相关阅读
- https://plumephp.com/os-boot-process/ —— 内核启动全流程,与本文 ARM64 启动路径互为补充
- https://plumephp.com/os-interrupts/ —— 中断处理框架,GIC 驱动如何接入通用中断子系统
- https://plumephp.com/os-virtual-memory/ —— 虚拟内存抽象层,页表结构与地址翻译的通用机制
延伸阅读
- Linux Kernel Documentation:
Documentation/arch/arm64/(booting.rst、memory.rst、cpu-feature-registers.rst) - ARM Architecture Reference Manual ARMv8, for ARMv8-A architecture profile(DDI 0487)
- Device Tree Specification, Release v0.4(devicetree.org)
- Robert Love, “Linux Kernel Development”(3rd Edition)— 内核整体架构与内存管理章节
- 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'"
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。