QEMU I2C设备模型开发与Linux驱动联合验证 - 第2节

2.1 QEMU 整体架构与设备模拟原理

QEMU 是一个通用的开源机器模拟器与虚拟化器。在嵌入式开发中,它能够模拟完整的硬件平台(SoC、内存、外设),使开发者可以在没有真实硬件的情况下编写和调试驱动。理解 QEMU 的架构是进行外设模拟进阶开发的基础。

2.1.1 QEMU 架构分层

QEMU 的架构可以抽象为三个核心层次:前端(Frontend)、中间层(TCG / 加速器) 和 后端(Backend / 设备模型)。

关键概念: QEMU 通过 动态二进制翻译(TCG) 将客户机指令翻译为主机指令,同时通过 设备模型(Device Model) 模拟外设行为。设备模型是驱动开发者最需要关注的层次。
QEMU 三层架构图 QEMU 核心架构分层 前端层:命令行接口 + 机器初始化 -machine, -cpu, -m, -drive, -device, -netdev 等参数解析 中间层:TCG 动态翻译 / KVM 加速器 指令翻译、内存管理单元(MMU)模拟、中断控制器路由 后端层:设备模型 + 总线模拟 I2C、SPI、UART、GPIO、Timer、DMA 等外设寄存器级模拟 驱动开发者关注点:后端设备模型中的寄存器读写、中断触发、DMA 传输

2.1.2 设备模拟原理:从寄存器到中断

QEMU 中的每个外设都是一个 QOM(QEMU Object Model) 对象。设备模拟的核心是:

  1. 寄存器映射(MemoryRegion):将设备寄存器区域映射到客户机物理地址空间。
  2. 读写回调(read/write handler):客户机对寄存器地址的读写操作会触发 QEMU 中的回调函数。
  3. 中断触发(qemu_set_irq):设备状态变化时,通过 GPIO 线向中断控制器发送中断信号。
  4. 总线协议模拟(如 I2C、SPI):在 QEMU 中实现总线时序,驱动通过操作总线控制器寄存器来间接控制外设。
提示: QEMU 的设备模拟是 寄存器级 的,而不是指令级或信号级。这意味着驱动对寄存器的读写会直接映射到 QEMU 设备模型的回调函数,从而实现高效模拟。
设备模拟数据流图 QEMU 设备模拟数据流 客户机驱动 写寄存器 客户机物理地址空间 MMU 翻译 MemoryRegion 回调 设备模型(DeviceState) read/write 回调 中断控制器(GIC/APIC) qemu_set_irq 驱动感知到的真实硬件行为 (寄存器读写 + 中断响应)

2.2 QEMU 命令行参数详解

QEMU 命令行参数是控制模拟行为的核心接口。对于嵌入式 I2C 设备驱动开发,以下参数最为关键。

2.2.1 核心机器参数

参数 示例 说明
-machine -machine virt 指定模拟的机器类型。ARM 常用 virt、vexpress-a9;RISC-V 常用 virt。
-cpu -cpu cortex-a72 指定 CPU 型号。影响指令集和功能特性。
-smp -smp 4 指定 CPU 核心数。多核调试时需注意中断亲和性。
-m -m 512M 指定客户机内存大小。I2C 设备通常不需要大内存。

2.2.2 设备与总线参数

参数 示例 说明
-device -device ds1338,address=0x68 挂载一个 I2C 从设备(如 RTC 芯片 ds1338)到总线上,并指定从机地址。
-bus -bus i2c-bus.0 显式指定设备挂载到哪条 I2C 总线。通常与 -device 配合使用。
-global -global ds1338.offset=0 设置设备属性。不同设备有不同属性,需查阅 QEMU 源码或文档。
注意: QEMU 中 I2C 设备的挂载顺序和总线编号会影响驱动中的设备树(DeviceTree)或 ACPI 枚举结果。务必使用 -device 明确指定 address 参数,避免地址冲突。

2.2.3 调试与监控参数

参数 示例 说明
-s -s 在 TCP 1234 端口开启 GDB 调试 stub。驱动开发者常用此参数进行单步调试。
-S -S 启动时暂停 CPU,等待 GDB 连接。配合 -s 使用。
-d -d in_asm,int,cpu_reset 开启 QEMU 内部日志。可输出指令流、中断、CPU 状态等。调试 I2C 时序时非常有用。
-D -D logfile.txt 将日志输出到文件,避免干扰终端。
-monitor -monitor stdio 开启 QEMU 监控台。可在运行时动态查询设备状态、内存、中断等。

2.2.4 完整启动示例(I2C 设备场景)

以下是一个典型的 QEMU 命令行,用于模拟一个挂载了 I2C RTC 设备的 ARM 平台:

qemu-system-aarch64 \
  -machine virt \
  -cpu cortex-a72 \
  -smp 1 \
  -m 256M \
  -kernel arch/arm64/boot/Image \
  -drive file=rootfs.ext4,format=raw,if=none,id=hd0 \
  -device virtio-blk-device,drive=hd0 \
  -device ds1338,address=0x68 \
  -device i2c-dummy,address=0x50 \
  -append "console=ttyAMA0 root=/dev/vda rw" \
  -nographic \
  -s -S \
  -monitor stdio \
  -d in_asm,int \
  -D qemu_trace.log
参数拆解:
  • -device ds1338,address=0x68:挂载一个模拟的 DS1338 RTC 芯片,I2C 地址为 0x68。
  • -device i2c-dummy,address=0x50:挂载一个 I2C EEPROM 模拟设备,地址 0x50,用于测试多设备场景。
  • -s -S:开启 GDB 调试,并暂停等待连接。
  • -d in_asm,int:记录指令流和中断事件,便于分析 I2C 驱动中断处理流程。
命令行参数与 QEMU 内部组件映射 命令行参数 → QEMU 内部组件映射 命令行参数 -machine virt -cpu cortex-a72 -device ds1338,addr=0x68 -device i2c-dummy,addr=0x50 -s -S -d in_asm,int -monitor stdio QEMU 内部组件 MachineClass (virt) ARMCPU (cortex-a72) I2CSlave (ds1338) I2CSlave (i2c-dummy) GDBStub (port 1234) TraceEvent (in_asm,int) 每个命令行参数最终对应 QEMU 内部的一个或多个 QOM 对象或服务组件

2.3 QEMU 设备模型与 QOM 对象系统

QEMU 的设备模型基于 QOM(QEMU Object Model) 构建。每个设备都是一个 DeviceState 对象,通过 TypeInfo 注册到类型系统。理解 QOM 有助于自定义 I2C 设备模拟。

2.3.1 QOM 核心概念

概念 说明 驱动开发关联
TypeInfo 设备类型的描述结构,包含名称、父类型、实例大小、初始化/析构函数。 自定义设备时需定义 TypeInfo。
DeviceState 所有设备的基类,包含属性、GPIO 线、总线接口等。 驱动通过设备树匹配的设备节点对应 DeviceState 实例。
MemoryRegion 描述设备占用的地址空间,提供读写回调。 I2C 控制器寄存器映射到 MemoryRegion。
BusState 总线对象,管理挂载的子设备。 I2C 总线是 BusState 的子类,负责路由 I2C 消息。

2.3.2 I2C 设备在 QOM 中的位置

在 QEMU 源码中,I2C 设备继承关系如下:

Object
 └── DeviceState
      └── I2CSlave (hw/i2c/i2c.h)
           ├── ds1338 (hw/rtc/ds1338.c)
           ├── lm832x (hw/input/lm832x.c)
           └── ... 其他 I2C 从设备

I2C 控制器(如 i2c-bus)则继承自 BusState,负责管理 I2CSlave 设备的挂载和消息传递。

实践要点: 当你在 QEMU 命令行使用 -device ds1338,address=0x68 时,QEMU 内部会:
  1. 查找 ds1338 对应的 TypeInfo。
  2. 实例化一个 I2CSlave 对象。
  3. 将其挂载到默认的 I2C 总线上(通常是 i2c-bus.0)。
  4. 设置从机地址为 0x68。
QOM I2C 设备继承关系 QOM I2C 设备继承关系 Object DeviceState I2CSlave BusState (I2C 总线) ds1338 | lm832x | 自定义 I2C 设备

2.4 本章小结

  • QEMU 架构分为前端、中间层(TCG/加速器)、后端(设备模型)三层,驱动开发者重点关注后端设备模型。
  • 设备模拟的核心是 MemoryRegion(寄存器映射)和 读写回调,I2C 设备在此基础上增加了总线协议模拟。
  • 命令行参数 -machine、-device、-s、-d 是 I2C 驱动开发中最常用的参数,需熟练掌握。
  • QOM 对象系统是 QEMU 设备模拟的基石,理解 I2CSlave 继承关系有助于后续自定义设备模拟。