-
-
[原创] Qemu 源码分析(PCI篇)
-
发表于: 1天前 1173
-
几个月前写的文章了,翻飞书翻到了。可能结构会有点乱,主要是了解一下 PCI,有错误的话请各位大佬指正一下
PCI 设备通过 BAR(Base Address Register,基地址寄存器) 来定义自己的地址空间。一个标准的 PCI 设备最多可以有 6 个 BAR(BAR0-BAR5),每个 BAR 可以映射到两种不同类型的地址空间:
BAR(BASE Address Registers)用来确定设备所需要使用的内存和 I/O 空间的大小,也可以用来存放设备寄存器的地址。
设备可以申请两类的地址空间:
想象 PCI 总线就像是计算机内部的"高速公路系统":
内存和 I/O 设备共享同一个地址空间
端口映射 I/O 通常使用一种特殊的 CPU 指令,专门执行 I/O 操作
I/O 设备有一个与内存不同的地址空间,为了实现地址空间的隔离,要么在 CPU 物理接口上增加一个 I/O 引脚,要么增加一条专用的 I/O 总线。
这里用 edu PCI设备来做解释
EDU = Educational Device(教育用设备)这是QEMU官方提供的一个虚拟PCI设备,专门用于教学目的,帮助学生和开发者学习如何编写设备驱动程序。
FlatView的作用:
我们知道传入的参数为
所以access_fn间接调用memory_region_write_accessor
跳过阶段七(就是正常执行注册的函数)
在后面会仔细解释其含义和实现
一般 Device 与 Function 一起成为 deviceFn
正向和反向运算
deviceFn 为 PCIBus 设备数组的索引
直接查询源代码代码即可
常用的快速查询表

然后通过 lspci 获取 pci 设备信息

config文件 : PCI 配置空间(原始数据)
256 字节 (标准 PCI) 4096 字节 (PCIe)

BAR0 解析

解析
每行三个字段:起始地址 结束地址 标志位(最后一位表示类型:偶数=MMIO,奇数=PMIO)
这里可以看见仅仅是用了 BAR0
标识位解析
Bit 0:Region Type,总是为 0,用于区分此类型为 Memory
Bits 2-1:Locatable,为 0 时表示采用 32 位地址,为 2 时表示采用 64 位地址,为 1 时表示区间大小小于 1MB
Bit 3:Prefetchable,为 0 时表示关闭预取,为 1 时表示开启预取
Bits 31-4:Base Address,以 16 字节对齐基址
┌─────────────────────────────────────────────────────────────────┐│ QEMU 启动 - main() │└──────────────────────────┬──────────────────────────────────────┘ │ ▼┌─────────────────────────────────────────────────────────────────┐│ 机器初始化 - pc_init1() ││ (hw/i386/pc_piix.c) │└──────────────────────────┬──────────────────────────────────────┘ │ ▼┌─────────────────────────────────────────────────────────────────┐│ 1. 初始化内存区域 ││ - system_memory (系统内存) ││ - system_io (I/O 地址空间) ││ - pci_memory (PCI 地址空间) ││ ││ memory_region_init(pci_memory, NULL, "pci", UINT64_MAX) │└──────────────────────────┬──────────────────────────────────────┘ │ ▼┌─────────────────────────────────────────────────────────────────┐│ 2. 创建 PCI Host Bridge ││ phb = qdev_new(TYPE_I440FX_PCI_HOST_BRIDGE) ││ ││ 设置属性链接: ││ - RAM_MEM → ram_memory ││ - PCI_MEM → pci_memory ││ - SYSTEM_MEM → system_memory ││ - IO_MEM → system_io │└──────────────────────────┬──────────────────────────────────────┘ │ ▼┌─────────────────────────────────────────────────────────────────┐│ 3. Realize PCI Host Bridge ││ sysbus_realize_and_unref(SYS_BUS_DEVICE(phb)) ││ ││ 调用: i440fx_pcihost_realize() ││ (hw/pci-host/i440fx.c) │└──────────────────────────┬──────────────────────────────────────┘ │ ▼┌─────────────────────────────────────────────────────────────────┐│ 4. 映射 PCI 配置空间端口 ││ - 0xCF8: PCI 配置地址端口 (conf_mem) ││ - 0xCFC: PCI 配置数据端口 (data_mem) ││ ││ memory_region_add_subregion(io_memory, 0xcf8, conf_mem) ││ memory_region_add_subregion(io_memory, 0xcfc, data_mem) │└──────────────────────────┬──────────────────────────────────────┘ │ ▼┌─────────────────────────────────────────────────────────────────┐│ 5. 创建 PCI 根总线 ││ b = pci_root_bus_new(dev, NULL, pci_address_space, ││ io_memory, 0, TYPE_PCI_BUS) ││ ││ 调用: pci_root_bus_internal_init() ││ (hw/pci/pci.c) │└──────────────────────────┬──────────────────────────────────────┘ │ ▼┌─────────────────────────────────────────────────────────────────┐│ 6. 初始化 PCIBus 结构 ││ - bus->address_space_mem = mem ││ - bus->address_space_io = io ││ - bus->flags |= PCI_BUS_IS_ROOT ││ - bus->devices[256] = {NULL} // 设备数组 ││ ││ pci_host_bus_register(parent) │└──────────────────────────┬──────────────────────────────────────┘ │ ▼┌─────────────────────────────────────────────────────────────────┐│ 7. 创建 i440FX PCI 设备(设备号 0) ││ d = pci_create_simple(b, 0, TYPE_I440FX) ││ ││ - Vendor ID: 0x8086 (Intel) ││ - Device ID: 0x1237 (82441) ││ - Class: PCI_CLASS_BRIDGE_HOST │└──────────────────────────┬──────────────────────────────────────┘ │ ▼┌─────────────────────────────────────────────────────────────────┐│ 8. 设置内存映射 ││ - PCI Hole (3.5G ~ 4G) ││ - SMRAM 区域 ││ - PAM (可编程属性映射) ││ ││ pc_pci_as_mapping_init(system_memory, pci_address_space) │└──────────────────────────┬──────────────────────────────────────┘ │ ▼┌─────────────────────────────────────────────────────────────────┐│ 9. 设置中断路由 ││ pci_bus_map_irqs(pcibus, pc_pci_slot_get_pirq) ││ ││ 将 PCI INTx (A/B/C/D) 映射到 PIRQ │└──────────────────────────┬──────────────────────────────────────┘ │ ▼┌─────────────────────────────────────────────────────────────────┐│ 10. 创建南桥 PCI 设备 (PIIX3/PIIX4) ││ pci_dev = pci_new_multifunction(-1, TYPE_PIIX3) ││ ││ 包含: ││ - ISA Bridge ││ - IDE Controller ││ - USB Controller (可选) ││ - ACPI/Power Management │└──────────────────────────┬──────────────────────────────────────┘ │ ▼┌─────────────────────────────────────────────────────────────────┐│ 11. PCI 设备实现 (pci_realize_and_unref) ││ 调用: pci_qdev_realize() → PCIDeviceClass->realize() ││ ││ - 分配配置空间 (256/4096 字节) ││ - 初始化 BAR ││ - 设置中断 ││ - 添加到 bus->devices[devfn] │└──────────────────────────┬──────────────────────────────────────┘ │ ▼┌─────────────────────────────────────────────────────────────────┐│ 12. 创建其他 PCI 设备 ││ - VGA 显卡 ││ - 网卡 (e1000, rtl8139, virtio-net) ││ - 存储控制器 (virtio-blk, nvme) ││ - 声卡等 ││ ││ 通过命令行参数 (-device) 或机器默认配置创建 │└──────────────────────────┬──────────────────────────────────────┘ │ ▼┌─────────────────────────────────────────────────────────────────┐│ 13. PCI 初始化完成 ││ - PCI 总线就绪 ││ - 客户机可以通过 0xCF8/0xCFC 枚举设备 ││ - 设备 MMIO/PMIO 映射到地址空间 ││ - 中断路由配置完成 │└─────────────────────────────────────────────────────────────────┘┌─────────────────────────────────────────────────────────────────┐│ QEMU 启动 - main() │└──────────────────────────┬──────────────────────────────────────┘ │ ▼┌─────────────────────────────────────────────────────────────────┐│ 机器初始化 - pc_init1() ││ (hw/i386/pc_piix.c) │└──────────────────────────┬──────────────────────────────────────┘ │ ▼┌─────────────────────────────────────────────────────────────────┐│ 1. 初始化内存区域 ││ - system_memory (系统内存) ││ - system_io (I/O 地址空间) ││ - pci_memory (PCI 地址空间) ││ ││ memory_region_init(pci_memory, NULL, "pci", UINT64_MAX) │└──────────────────────────┬──────────────────────────────────────┘ │ ▼┌─────────────────────────────────────────────────────────────────┐│ 2. 创建 PCI Host Bridge ││ phb = qdev_new(TYPE_I440FX_PCI_HOST_BRIDGE) ││ ││ 设置属性链接: ││ - RAM_MEM → ram_memory ││ - PCI_MEM → pci_memory ││ - SYSTEM_MEM → system_memory ││ - IO_MEM → system_io │└──────────────────────────┬──────────────────────────────────────┘ │ ▼┌─────────────────────────────────────────────────────────────────┐│ 3. Realize PCI Host Bridge ││ sysbus_realize_and_unref(SYS_BUS_DEVICE(phb)) ││ ││ 调用: i440fx_pcihost_realize() ││ (hw/pci-host/i440fx.c) │└──────────────────────────┬──────────────────────────────────────┘ │ ▼┌─────────────────────────────────────────────────────────────────┐│ 4. 映射 PCI 配置空间端口 ││ - 0xCF8: PCI 配置地址端口 (conf_mem) ││ - 0xCFC: PCI 配置数据端口 (data_mem) ││ ││ memory_region_add_subregion(io_memory, 0xcf8, conf_mem) ││ memory_region_add_subregion(io_memory, 0xcfc, data_mem) │└──────────────────────────┬──────────────────────────────────────┘ │ ▼┌─────────────────────────────────────────────────────────────────┐│ 5. 创建 PCI 根总线 ││ b = pci_root_bus_new(dev, NULL, pci_address_space, ││ io_memory, 0, TYPE_PCI_BUS) ││ ││ 调用: pci_root_bus_internal_init() ││ (hw/pci/pci.c) │└──────────────────────────┬──────────────────────────────────────┘ │ ▼┌─────────────────────────────────────────────────────────────────┐│ 6. 初始化 PCIBus 结构 ││ - bus->address_space_mem = mem ││ - bus->address_space_io = io ││ - bus->flags |= PCI_BUS_IS_ROOT ││ - bus->devices[256] = {NULL} // 设备数组 ││ ││ pci_host_bus_register(parent) │└──────────────────────────┬──────────────────────────────────────┘ │ ▼┌─────────────────────────────────────────────────────────────────┐│ 7. 创建 i440FX PCI 设备(设备号 0) ││ d = pci_create_simple(b, 0, TYPE_I440FX) ││ ││ - Vendor ID: 0x8086 (Intel) ││ - Device ID: 0x1237 (82441) ││ - Class: PCI_CLASS_BRIDGE_HOST │└──────────────────────────┬──────────────────────────────────────┘ │ ▼┌─────────────────────────────────────────────────────────────────┐│ 8. 设置内存映射 ││ - PCI Hole (3.5G ~ 4G) ││ - SMRAM 区域 ││ - PAM (可编程属性映射) ││ ││ pc_pci_as_mapping_init(system_memory, pci_address_space) │└──────────────────────────┬──────────────────────────────────────┘ │ ▼┌─────────────────────────────────────────────────────────────────┐│ 9. 设置中断路由 ││ pci_bus_map_irqs(pcibus, pc_pci_slot_get_pirq) ││ ││ 将 PCI INTx (A/B/C/D) 映射到 PIRQ │└──────────────────────────┬──────────────────────────────────────┘ │ ▼┌─────────────────────────────────────────────────────────────────┐│ 10. 创建南桥 PCI 设备 (PIIX3/PIIX4) ││ pci_dev = pci_new_multifunction(-1, TYPE_PIIX3) ││ ││ 包含: ││ - ISA Bridge ││ - IDE Controller ││ - USB Controller (可选) ││ - ACPI/Power Management │└──────────────────────────┬──────────────────────────────────────┘ │ ▼┌─────────────────────────────────────────────────────────────────┐│ 11. PCI 设备实现 (pci_realize_and_unref) ││ 调用: pci_qdev_realize() → PCIDeviceClass->realize() ││ ││ - 分配配置空间 (256/4096 字节) ││ - 初始化 BAR ││ - 设置中断 ││ - 添加到 bus->devices[devfn] │└──────────────────────────┬──────────────────────────────────────┘ │ ▼┌─────────────────────────────────────────────────────────────────┐│ 12. 创建其他 PCI 设备 ││ - VGA 显卡 ││ - 网卡 (e1000, rtl8139, virtio-net) ││ - 存储控制器 (virtio-blk, nvme) ││ - 声卡等 ││ ││ 通过命令行参数 (-device) 或机器默认配置创建 │└──────────────────────────┬──────────────────────────────────────┘ │ ▼┌─────────────────────────────────────────────────────────────────┐│ 13. PCI 初始化完成 ││ - PCI 总线就绪 ││ - 客户机可以通过 0xCF8/0xCFC 枚举设备 ││ - 设备 MMIO/PMIO 映射到地址空间 ││ - 中断路由配置完成 │└─────────────────────────────────────────────────────────────────┘┌─────────────────────────────────────┐│ QEMU虚拟机 ││ ┌──────────────────────────────┐ ││ │ Guest Linux (虚拟机OS) │ ││ │ ┌────────────────────────┐ │ ││ │ │ 学生编写的EDU驱动 │ │ │ ← 学习目标│ │ └────────┬───────────────┘ │ ││ │ │ (MMIO/DMA/IRQ) │ ││ │ ┌────────▼───────────────┐ │ ││ │ │ EDU虚拟PCI设备 │ │ │ ← edu.c模拟的设备│ │ └────────────────────────┘ │ ││ └──────────────────────────────┘ │└─────────────────────────────────────┘┌─────────────────────────────────────┐│ QEMU虚拟机 ││ ┌──────────────────────────────┐ ││ │ Guest Linux (虚拟机OS) │ ││ │ ┌────────────────────────┐ │ ││ │ │ 学生编写的EDU驱动 │ │ │ ← 学习目标│ │ └────────┬───────────────┘ │ ││ │ │ (MMIO/DMA/IRQ) │ ││ │ ┌────────▼───────────────┐ │ ││ │ │ EDU虚拟PCI设备 │ │ │ ← edu.c模拟的设备│ │ └────────────────────────┘ │ ││ └──────────────────────────────┘ │└─────────────────────────────────────┘EDU设备包含4个教学模块:┌─────────────────────────────────────┐│ 1. 基础MMIO读写 ││ - 设备识别寄存器 (0x00) ││ - 活性检查寄存器 (0x04) ││ └→ 学习:基本的寄存器访问 │├─────────────────────────────────────┤│ 2. 异步计算 + 中断 ││ - 阶乘计算 (0x08) ││ - 状态寄存器 (0x20) ││ - 中断状态 (0x24) ││ └→ 学习:异步操作、中断处理 │├─────────────────────────────────────┤│ 3. 中断控制 ││ - 触发中断 (0x60) ││ - 清除中断 (0x64) ││ └→ 学习:中断管理、INTx/MSI │├─────────────────────────────────────┤│ 4. DMA传输 ││ - DMA源地址 (0x80) ││ - DMA目标地址 (0x88) ││ - DMA计数 (0x90) ││ - DMA命令 (0x98) ││ └→ 学习:DMA编程、内存管理 │└─────────────────────────────────────┘EDU设备包含4个教学模块:┌─────────────────────────────────────┐│ 1. 基础MMIO读写 ││ - 设备识别寄存器 (0x00) ││ - 活性检查寄存器 (0x04) ││ └→ 学习:基本的寄存器访问 │├─────────────────────────────────────┤│ 2. 异步计算 + 中断 ││ - 阶乘计算 (0x08) ││ - 状态寄存器 (0x20) ││ - 中断状态 (0x24) ││ └→ 学习:异步操作、中断处理 │├─────────────────────────────────────┤│ 3. 中断控制 ││ - 触发中断 (0x60) ││ - 清除中断 (0x64) ││ └→ 学习:中断管理、INTx/MSI │├─────────────────────────────────────┤│ 4. DMA传输 ││ - DMA源地址 (0x80) ││ - DMA目标地址 (0x88) ││ - DMA计数 (0x90) ││ - DMA命令 (0x98) ││ └→ 学习:DMA编程、内存管理 │└─────────────────────────────────────┘static const TypeInfo edu_types[] = { { .name = TYPE_PCI_EDU_DEVICE, .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(EduState), .instance_init = edu_instance_init, .class_init = edu_class_init, .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, }};DEFINE_TYPES(edu_types)static const TypeInfo edu_types[] = { { .name = TYPE_PCI_EDU_DEVICE, .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(EduState), .instance_init = edu_instance_init, .class_init = edu_class_init, .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, }};DEFINE_TYPES(edu_types)// 定义类型名称#define TYPE_PCI_EDU_DEVICE "edu"// TypeInfo结构体告诉QEMU:.name = "edu" // 设备类型名(命令行用 -device edu).parent = TYPE_PCI_DEVICE // 继承自PCIDevice.instance_size = sizeof(EduState) // 每个实例的内存大小.instance_init = edu_instance_init // 实例初始化函数.class_init = edu_class_init // 类初始化函数.interfaces = CONVENTIONAL_PCI_DEVICE // 实现传统PCI设备接口// 定义类型名称#define TYPE_PCI_EDU_DEVICE "edu"// TypeInfo结构体告诉QEMU:.name = "edu" // 设备类型名(命令行用 -device edu).parent = TYPE_PCI_DEVICE // 继承自PCIDevice.instance_size = sizeof(EduState) // 每个实例的内存大小.instance_init = edu_instance_init // 实例初始化函数.class_init = edu_class_init // 类初始化函数.interfaces = CONVENTIONAL_PCI_DEVICE // 实现传统PCI设备接口static void edu_class_init(ObjectClass *class, const void *data){ DeviceClass *dc = DEVICE_CLASS(class); PCIDeviceClass *k = PCI_DEVICE_CLASS(class); k->realize = pci_edu_realize; k->exit = pci_edu_uninit; k->vendor_id = PCI_VENDOR_ID_QEMU; k->device_id = 0x11e8; k->revision = 0x10; k->class_id = PCI_CLASS_OTHERS; set_bit(DEVICE_CATEGORY_MISC, dc->categories);}static void edu_class_init(ObjectClass *class, const void *data){ DeviceClass *dc = DEVICE_CLASS(class); PCIDeviceClass *k = PCI_DEVICE_CLASS(class); k->realize = pci_edu_realize; k->exit = pci_edu_uninit; k->vendor_id = PCI_VENDOR_ID_QEMU; k->device_id = 0x11e8; k->revision = 0x10; k->class_id = PCI_CLASS_OTHERS; set_bit(DEVICE_CATEGORY_MISC, dc->categories);}k->realize = pci_edu_realize; // 设备实现函数(相当于init)k->exit = pci_edu_uninit; // 设备退出函数(相当于cleanup)// PCI配置空间的值(用lspci可以看到)k->vendor_id = PCI_VENDOR_ID_QEMU; // 厂商ID: 0x1234k->device_id = 0x11e8; // 设备ID: 0x11e8k->revision = 0x10; // 版本号k->class_id = PCI_CLASS_OTHERS; // PCI设备类别k->realize = pci_edu_realize; // 设备实现函数(相当于init)k->exit = pci_edu_uninit; // 设备退出函数(相当于cleanup)// PCI配置空间的值(用lspci可以看到)k->vendor_id = PCI_VENDOR_ID_QEMU; // 厂商ID: 0x1234k->device_id = 0x11e8; // 设备ID: 0x11e8k->revision = 0x10; // 版本号k->class_id = PCI_CLASS_OTHERS; // PCI设备类别static void edu_instance_init(Object *obj){ EduState *edu = EDU(obj); edu->dma_mask = (1UL << 28) - 1; object_property_add_uint64_ptr(obj, "dma_mask", &edu->dma_mask, OBJ_PROP_FLAG_READWRITE);}static void edu_instance_init(Object *obj){ EduState *edu = EDU(obj); edu->dma_mask = (1UL << 28) - 1; object_property_add_uint64_ptr(obj, "dma_mask", &edu->dma_mask, OBJ_PROP_FLAG_READWRITE);}static void pci_edu_realize(PCIDevice *pdev, Error **errp){ EduState *edu = EDU(pdev); uint8_t *pci_conf = pdev->config; pci_config_set_interrupt_pin(pci_conf, 1); if (msi_init(pdev, 0, 1, true, false, errp)) { return; } timer_init_ms(&edu->dma_timer, QEMU_CLOCK_VIRTUAL, edu_dma_timer, edu); qemu_mutex_init(&edu->thr_mutex); qemu_cond_init(&edu->thr_cond); qemu_thread_create(&edu->thread, "edu", edu_fact_thread, edu, QEMU_THREAD_JOINABLE); memory_region_init_io(&edu->mmio, OBJECT(edu), &edu_mmio_ops, edu, "edu-mmio", 1 * MiB); pci_register_bar(pdev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &edu->mmio);}static void pci_edu_realize(PCIDevice *pdev, Error **errp){ EduState *edu = EDU(pdev); uint8_t *pci_conf = pdev->config; pci_config_set_interrupt_pin(pci_conf, 1); if (msi_init(pdev, 0, 1, true, false, errp)) { return; } timer_init_ms(&edu->dma_timer, QEMU_CLOCK_VIRTUAL, edu_dma_timer, edu); qemu_mutex_init(&edu->thr_mutex); qemu_cond_init(&edu->thr_cond); qemu_thread_create(&edu->thread, "edu", edu_fact_thread, edu, QEMU_THREAD_JOINABLE); memory_region_init_io(&edu->mmio, OBJECT(edu), &edu_mmio_ops, edu, "edu-mmio", 1 * MiB); pci_register_bar(pdev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &edu->mmio);}// 1. 配置PCI中断引脚(INTA#)pci_config_set_interrupt_pin(pci_conf, 1);// 2. 初始化MSI(Message Signaled Interrupts)支持// 参数:设备,偏移量,中断数量,支持64位地址,支持per-vector屏蔽msi_init(pdev, 0, 1, true, false, errp);// 3. 初始化DMA定时器(模拟DMA传输延迟)timer_init_ms(&edu->dma_timer, QEMU_CLOCK_VIRTUAL, edu_dma_timer, edu);// 4. 创建后台线程(计算阶乘)qemu_thread_create(&edu->thread, "edu", edu_fact_thread, edu, ...);// 5. 创建MMIO内存区域(这是核心!)memory_region_init_io( &edu->mmio, // MemoryRegion对象 OBJECT(edu), // 所有者 &edu_mmio_ops, // 读写操作回调 edu, // opaque指针 "edu-mmio", // 名称 1 * MiB // 大小:1MB);// 6. 注册为PCI BAR0(重要!)pci_register_bar( pdev, // PCI设备 0, // BAR编号:0 PCI_BASE_ADDRESS_SPACE_MEMORY, // MMIO类型(不是PMIO) &edu->mmio // 内存区域);// 1. 配置PCI中断引脚(INTA#)pci_config_set_interrupt_pin(pci_conf, 1);// 2. 初始化MSI(Message Signaled Interrupts)支持// 参数:设备,偏移量,中断数量,支持64位地址,支持per-vector屏蔽msi_init(pdev, 0, 1, true, false, errp);// 3. 初始化DMA定时器(模拟DMA传输延迟)timer_init_ms(&edu->dma_timer, QEMU_CLOCK_VIRTUAL, edu_dma_timer, edu);// 4. 创建后台线程(计算阶乘)qemu_thread_create(&edu->thread, "edu", edu_fact_thread, edu, ...);// 5. 创建MMIO内存区域(这是核心!)memory_region_init_io( &edu->mmio, // MemoryRegion对象 OBJECT(edu), // 所有者 &edu_mmio_ops, // 读写操作回调 edu, // opaque指针 "edu-mmio", // 名称 1 * MiB // 大小:1MB);// 6. 注册为PCI BAR0(重要!)pci_register_bar( pdev, // PCI设备 0, // BAR编号:0 PCI_BASE_ADDRESS_SPACE_MEMORY, // MMIO类型(不是PMIO) &edu->mmio // 内存区域);uint64_t (*read)(void *opaque, hwaddr addr, unsigned size);uint64_t (*read)(void *opaque, hwaddr addr, unsigned size);static const MemoryRegionOps edu_mmio_ops = { .read = edu_mmio_read, .write = edu_mmio_write, .endianness = DEVICE_NATIVE_ENDIAN, .valid = { .min_access_size = 4, .max_access_size = 8, }, .impl = { .min_access_size = 4, .max_access_size = 8, },};static const MemoryRegionOps edu_mmio_ops = { .read = edu_mmio_read, .write = edu_mmio_write, .endianness = DEVICE_NATIVE_ENDIAN, .valid = { .min_access_size = 4, .max_access_size = 8, }, .impl = { .min_access_size = 4, .max_access_size = 8, },};Guest: mov [0xFEBF1000], 1337 ↓ (VM Exit)KVM捕获访问 ↓传递给QEMU ↓QEMU内存系统查找对应的MemoryRegion ↓找到edu->mmio ↓调用 edu_mmio_write(edu, 0x00, 1337, 4) ↓设备逻辑处理Guest: mov [0xFEBF1000], 1337 ↓ (VM Exit)KVM捕获访问 ↓传递给QEMU ↓QEMU内存系统查找对应的MemoryRegion ↓找到edu->mmio ↓调用 edu_mmio_write(edu, 0x00, 1337, 4) ↓设备逻辑处理┌─────────────────────────────────────────────────────────────────────┐│ Guest OS (虚拟机) ││ ││ 用户驱动或应用程序 ││ │ ││ ▼ ││ uint32_t val = *(uint32_t*)(mmio_mem + 0x08); ││ │ ││ ▼ ││ CPU执行: MOV EAX, [0xFEBF1008] ← EDU设备BAR0 + 0x08 ││ │ │└───────────────────┼───────────────────────────────────────────────────┘ │ ▼ (VM Exit - EPT Violation 或 MMIO访问)┌─────────────────────────────────────────────────────────────────────┐│ Linux Kernel (Host) ││ ││ KVM模块 ││ │ ││ ▼ ││ 检测到Guest访问设备地址 (0xFEBF1008) ││ │ ││ ▼ ││ 填充 kvm_run 结构体: ││ run->exit_reason = KVM_EXIT_MMIO ││ run->mmio.phys_addr = 0xFEBF1008 ││ run->mmio.len = 4 ││ run->mmio.is_write = 0 ││ │ ││ ▼ ││ return to userspace (ioctl返回) ││ │└───────────────────┼───────────────────────────────────────────────────┘ │ ▼┌─────────────────────────────────────────────────────────────────────┐│ QEMU进程 (用户态) ││ ││ [1] kvm_cpu_exec() ││ ↓ ││ [2] run_ret = kvm_vcpu_ioctl(cpu, KVM_RUN, 0) ││ ↓ (ioctl阻塞,直到VM Exit) ││ [3] switch(run->exit_reason) ││ ↓ ││ [4] case KVM_EXIT_MMIO: ││ ↓ ││ [5] address_space_rw(&address_space_memory, ││ run->mmio.phys_addr, ││ attrs, ││ run->mmio.data, ││ run->mmio.len, ││ run->mmio.is_write) ││ ↓ ││ [6] address_space_read_full() ││ ↓ ││ [7] fv = address_space_to_flatview(as) ││ ↓ ││ [8] flatview_read(fv, addr, attrs, buf, len) ││ ↓ ││ [9] mr = flatview_translate(fv, addr, ...) ││ ↓ ││ [10] flatview_read_continue() ││ ↓ ││ [11] flatview_read_continue_step() ││ ↓ ││ [12] memory_region_dispatch_read(mr, mr_addr, &val, ...) ││ ↓ ││ [13] memory_region_dispatch_read1() ││ ↓ ││ [14] access_with_adjusted_size() ││ ↓ ││ [15] memory_region_read_accessor() ││ ↓ ││ [16] tmp = mr->ops->read(mr->opaque, addr, size) ││ ↓ ││ [17] edu_mmio_read(edu, 0x08, 4) ← 最终调用! ││ ↓ ││ [18] switch(addr) { case 0x08: return edu->fact; } ││ ↓ ││ [19] 返回值逐层返回到 run->mmio.data ││ ↓ ││ [20] VM Enter,Guest继续执行 ││ │└─────────────────────────────────────────────────────────────────────┘┌─────────────────────────────────────────────────────────────────────┐│ Guest OS (虚拟机) ││ ││ 用户驱动或应用程序 ││ │ ││ ▼ ││ uint32_t val = *(uint32_t*)(mmio_mem + 0x08); ││ │ ││ ▼ ││ CPU执行: MOV EAX, [0xFEBF1008] ← EDU设备BAR0 + 0x08 ││ │ │└───────────────────┼───────────────────────────────────────────────────┘ │ ▼ (VM Exit - EPT Violation 或 MMIO访问)┌─────────────────────────────────────────────────────────────────────┐│ Linux Kernel (Host) ││ ││ KVM模块 ││ │ ││ ▼ ││ 检测到Guest访问设备地址 (0xFEBF1008) ││ │ ││ ▼ ││ 填充 kvm_run 结构体: ││ run->exit_reason = KVM_EXIT_MMIO ││ run->mmio.phys_addr = 0xFEBF1008 ││ run->mmio.len = 4 ││ run->mmio.is_write = 0 ││ │ ││ ▼ ││ return to userspace (ioctl返回) ││ │└───────────────────┼───────────────────────────────────────────────────┘ │ ▼┌─────────────────────────────────────────────────────────────────────┐│ QEMU进程 (用户态) ││ ││ [1] kvm_cpu_exec() ││ ↓ ││ [2] run_ret = kvm_vcpu_ioctl(cpu, KVM_RUN, 0) ││ ↓ (ioctl阻塞,直到VM Exit) ││ [3] switch(run->exit_reason) ││ ↓ ││ [4] case KVM_EXIT_MMIO: ││ ↓ ││ [5] address_space_rw(&address_space_memory, ││ run->mmio.phys_addr, ││ attrs, ││ run->mmio.data, ││ run->mmio.len, ││ run->mmio.is_write) ││ ↓ ││ [6] address_space_read_full() ││ ↓ ││ [7] fv = address_space_to_flatview(as) ││ ↓ ││ [8] flatview_read(fv, addr, attrs, buf, len) ││ ↓ ││ [9] mr = flatview_translate(fv, addr, ...) ││ ↓ ││ [10] flatview_read_continue() ││ ↓ ││ [11] flatview_read_continue_step() ││ ↓ ││ [12] memory_region_dispatch_read(mr, mr_addr, &val, ...) ││ ↓ ││ [13] memory_region_dispatch_read1() ││ ↓ ││ [14] access_with_adjusted_size() ││ ↓ ││ [15] memory_region_read_accessor() ││ ↓ ││ [16] tmp = mr->ops->read(mr->opaque, addr, size) ││ ↓ ││ [17] edu_mmio_read(edu, 0x08, 4) ← 最终调用! ││ ↓ ││ [18] switch(addr) { case 0x08: return edu->fact; } ││ ↓ ││ [19] 返回值逐层返回到 run->mmio.data ││ ↓ ││ [20] VM Enter,Guest继续执行 ││ │└─────────────────────────────────────────────────────────────────────┘Guest vCPU KVM (Kernel) QEMU (Userspace) EDU Device │ │ │ │ │ MOV [0xFEBF1008] │ │ │ ├───────────────────>│ │ │ │ │ EPT Violation │ │ │ │ (设备地址) │ │ │ │ │ │ │ │ 填充kvm_run │ │ │ │ exit_reason=MMIO │ │ │ │ │ │ │ │ ioctl返回 │ │ │ ├─────────────────────>│ │ │ │ │ │ │ │ │ address_space_rw │ │ │ │ flatview_translate │ │ │ │ 查找MemoryRegion │ │ │ │ │ │ │ │ dispatch_read │ │ │ ├────────────────────>│ │ │ │ │ edu_mmio_read │ │ │ │ switch(0x08) │ │ │ │ return fact │ │ │<────────────────────┤ │ │ │ │ │ │ 数据写入run->mmio │ │ │ │ KVM_RUN ioctl │ │ │ │<─────────────────────┤ │ │ │ │ │ │ │ VM Enter │ │ │ │ 注入读取结果到EAX │ │ │<───────────────────┤ │ │ │ 继续执行 │ │ │ │ │ │ │Guest vCPU KVM (Kernel) QEMU (Userspace) EDU Device │ │ │ │ │ MOV [0xFEBF1008] │ │ │ ├───────────────────>│ │ │ │ │ EPT Violation │ │ │ │ (设备地址) │ │ │ │ │ │ │ │ 填充kvm_run │ │ │ │ exit_reason=MMIO │ │ │ │ │ │ │ │ ioctl返回 │ │ │ ├─────────────────────>│ │ │ │ │ │ │ │ │ address_space_rw │ │ │ │ flatview_translate │ │ │ │ 查找MemoryRegion │ │ │ │ │ │ │ │ dispatch_read │ │ │ ├────────────────────>│ │ │ │ │ edu_mmio_read │ │ │ │ switch(0x08) │ │ │ │ return fact │ │ │<────────────────────┤ │ │ │ │ │ │ 数据写入run->mmio │ │ │ │ KVM_RUN ioctl │ │ │ │<─────────────────────┤ │ │ │ │ │ │ │ VM Enter │ │ │ │ 注入读取结果到EAX │ │ │<───────────────────┤ │ │ │ 继续执行 │ │ │ │ │ │ │// 位置: accel/kvm/kvm-all.c:3154int kvm_cpu_exec(CPUState *cpu){ struct kvm_run *run = cpu->kvm_run; // 共享内存页 do { // 进入Guest执行 run_ret = kvm_vcpu_ioctl(cpu, KVM_RUN, 0); // [Step 2] // Guest发生VM Exit后返回 switch (run->exit_reason) { case KVM_EXIT_MMIO: // [Step 4] // 处理MMIO访问 address_space_rw(&address_space_memory, // [Step 5] run->mmio.phys_addr, // 0xFEBF1008 attrs, run->mmio.data, // 数据缓冲区 run->mmio.len, // 4字节 run->mmio.is_write); // 0 (读操作) break; } } while (ret == 0);}// 位置: accel/kvm/kvm-all.c:3154int kvm_cpu_exec(CPUState *cpu){ struct kvm_run *run = cpu->kvm_run; // 共享内存页 do { // 进入Guest执行 run_ret = kvm_vcpu_ioctl(cpu, KVM_RUN, 0); // [Step 2] // Guest发生VM Exit后返回 switch (run->exit_reason) { case KVM_EXIT_MMIO: // [Step 4] // 处理MMIO访问 address_space_rw(&address_space_memory, // [Step 5] run->mmio.phys_addr, // 0xFEBF1008 attrs, run->mmio.data, // 数据缓冲区 run->mmio.len, // 4字节 run->mmio.is_write); // 0 (读操作) break; } } while (ret == 0);}/* for KVM_RUN, returned by mmap(vcpu_fd, offset=0) */struct kvm_run { /* in */ __u8 request_interrupt_window; __u8 HINT_UNSAFE_IN_KVM(immediate_exit); __u8 padding1[6]; /* out */ __u32 exit_reason; __u8 ready_for_interrupt_injection; __u8 if_flag; __u16 flags; /* in (pre_kvm_run), out (post_kvm_run) */ __u64 cr8; __u64 apic_base; // ... // union 结构,这里简化一下,我们选择我们要用的表达。 struct { __u64 phys_addr; // Guest物理地址: 0xFEBF1008 __u8 data[8]; // 数据缓冲区 __u32 len; // 访问长度: 4 __u8 is_write; // 0=读, 1=写 } mmio;}/* for KVM_RUN, returned by mmap(vcpu_fd, offset=0) */struct kvm_run { /* in */ __u8 request_interrupt_window; __u8 HINT_UNSAFE_IN_KVM(immediate_exit); __u8 padding1[6]; /* out */ __u32 exit_reason; __u8 ready_for_interrupt_injection; __u8 if_flag; __u16 flags; /* in (pre_kvm_run), out (post_kvm_run) */ __u64 cr8; __u64 apic_base; // ... // union 结构,这里简化一下,我们选择我们要用的表达。 struct { __u64 phys_addr; // Guest物理地址: 0xFEBF1008 __u8 data[8]; // 数据缓冲区 __u32 len; // 访问长度: 4 __u8 is_write; // 0=读, 1=写 } mmio;}/* Called from RCU critical section */MemoryRegion *flatview_translate(FlatView *fv, hwaddr addr, hwaddr *xlat, hwaddr *plen, bool is_write, MemTxAttrs attrs){ MemoryRegion *mr; MemoryRegionSection section; AddressSpace *as = NULL; /* This can be MMIO, so setup MMIO bit. */ section = flatview_do_translate(fv, addr, xlat, plen, NULL, is_write, true, &as, attrs); mr = section.mr; if (xen_enabled() && memory_access_is_direct(mr, is_write, attrs)) { hwaddr page = ((addr & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE) - addr; *plen = MIN(page, *plen); } return mr;}/* Called from RCU critical section */MemoryRegion *flatview_translate(FlatView *fv, hwaddr addr, hwaddr *xlat, hwaddr *plen, bool is_write, MemTxAttrs attrs){ MemoryRegion *mr; MemoryRegionSection section; AddressSpace *as = NULL; /* This can be MMIO, so setup MMIO bit. */ section = flatview_do_translate(fv, addr, xlat, plen, NULL, is_write, true, &as, attrs); mr = section.mr; if (xen_enabled() && memory_access_is_direct(mr, is_write, attrs)) { hwaddr page = ((addr & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE) - addr; *plen = MIN(page, *plen); } return mr;}【层次化视图】MemoryRegion树形结构system_memory (容器)├── ram_below_4g (RAM, 0x00000000-0x7FFFFFFF, 2GB)├── pci_memory (容器, 0x80000000-0xFFFFFFFF)│ ├── vga_mmio (VGA设备, 0xA0000000, 16MB)│ ├── edu_mmio (EDU设备, 0xFEBF1000, 1MB) ← 我们的设备│ └── e1000_mmio (网卡, 0xFEBD0000, 128KB)└── ram_above_4g (RAM, 0x100000000+)【扁平化视图】FlatView - 线性地址映射FlatRange数组 (按地址排序):[0] 0x00000000-0x7FFFFFFF → ram_below_4g[1] 0x80000000-0x9FFFFFFF → (未分配)[2] 0xA0000000-0xA0FFFFFF → vga_mmio[3] 0xA1000000-0xFEBCFFFF → (未分配)[4] 0xFEBD0000-0xFEBEFFFF → e1000_mmio[5] 0xFEBF0000-0xFEBF0FFF → (gap)[6] 0xFEBF1000-0xFEBF1FFF → edu_mmio ← 查找目标[7] 0xFEBF2000-0xFFFFFFFF → (未分配)[8] 0x100000000-... → ram_above_4g【层次化视图】MemoryRegion树形结构system_memory (容器)├── ram_below_4g (RAM, 0x00000000-0x7FFFFFFF, 2GB)├── pci_memory (容器, 0x80000000-0xFFFFFFFF)│ ├── vga_mmio (VGA设备, 0xA0000000, 16MB)│ ├── edu_mmio (EDU设备, 0xFEBF1000, 1MB) ← 我们的设备│ └── e1000_mmio (网卡, 0xFEBD0000, 128KB)└── ram_above_4g (RAM, 0x100000000+)【扁平化视图】FlatView - 线性地址映射FlatRange数组 (按地址排序):[0] 0x00000000-0x7FFFFFFF → ram_below_4g[1] 0x80000000-0x9FFFFFFF → (未分配)[2] 0xA0000000-0xA0FFFFFF → vga_mmio[3] 0xA1000000-0xFEBCFFFF → (未分配)[4] 0xFEBD0000-0xFEBEFFFF → e1000_mmio[5] 0xFEBF0000-0xFEBF0FFF → (gap)[6] 0xFEBF1000-0xFEBF1FFF → edu_mmio ← 查找目标[7] 0xFEBF2000-0xFFFFFFFF → (未分配)[8] 0x100000000-... → ram_above_4gMemTxResult memory_region_dispatch_read(MemoryRegion *mr, hwaddr addr, uint64_t *pval, MemOp op, MemTxAttrs attrs){ unsigned size = memop_size(op); MemTxResult r; if (mr->alias) { return memory_region_dispatch_read(mr->alias, mr->alias_offset + addr, pval, op, attrs); } if (!memory_region_access_valid(mr, addr, size, false, attrs)) { *pval = unassigned_mem_read(mr, addr, size); return MEMTX_DECODE_ERROR; } r = memory_region_dispatch_read1(mr, addr, pval, size, attrs); adjust_endianness(mr, pval, op); return r;}static MemTxResult memory_region_dispatch_read1(MemoryRegion *mr, hwaddr addr, uint64_t *pval, unsigned size, MemTxAttrs attrs){ *pval = 0; if (mr->ops->read) { // [Step 14] 调用access_with_adjusted_size return access_with_adjusted_size(addr, pval, size, mr->ops->impl.min_access_size, mr->ops->impl.max_access_size, memory_region_read_accessor, mr, attrs); } else { return access_with_adjusted_size(addr, pval, size, mr->ops->impl.min_access_size, mr->ops->impl.max_access_size, memory_region_read_with_attrs_accessor, mr, attrs); }}MemTxResult memory_region_dispatch_read(MemoryRegion *mr, hwaddr addr, uint64_t *pval, MemOp op, MemTxAttrs attrs){ unsigned size = memop_size(op); MemTxResult r; if (mr->alias) { return memory_region_dispatch_read(mr->alias, mr->alias_offset + addr, pval, op, attrs); } if (!memory_region_access_valid(mr, addr, size, false, attrs)) { *pval = unassigned_mem_read(mr, addr, size); return MEMTX_DECODE_ERROR; } r = memory_region_dispatch_read1(mr, addr, pval, size, attrs); adjust_endianness(mr, pval, op); return r;}static MemTxResult memory_region_dispatch_read1(MemoryRegion *mr, hwaddr addr, uint64_t *pval, unsigned size, MemTxAttrs attrs){ *pval = 0; if (mr->ops->read) { // [Step 14] 调用access_with_adjusted_size return access_with_adjusted_size(addr, pval, size, mr->ops->impl.min_access_size, mr->ops->impl.max_access_size, memory_region_read_accessor, mr, attrs); } else { return access_with_adjusted_size(addr, pval, size, mr->ops->impl.min_access_size, mr->ops->impl.max_access_size, memory_region_read_with_attrs_accessor, mr, attrs); }}static MemTxResult access_with_adjusted_size(hwaddr addr, uint64_t *value, unsigned size, unsigned access_size_min, unsigned access_size_max, MemTxResult (*access_fn) (MemoryRegion *mr, hwaddr addr, uint64_t *value, unsigned size, signed shift, uint64_t mask, MemTxAttrs attrs), MemoryRegion *mr, MemTxAttrs attrs){ uint64_t access_mask; unsigned access_size; unsigned i; MemTxResult r = MEMTX_OK; bool reentrancy_guard_applied = false; if (!access_size_min) { access_size_min = 1; } if (!access_size_max) { access_size_max = 4; } /* Do not allow more than one simultaneous access to a device's IO Regions */ if (mr->dev && !mr->disable_reentrancy_guard && !mr->ram_device && !mr->ram && !mr->rom_device && !mr->readonly) { if (mr->dev->mem_reentrancy_guard.engaged_in_io) { warn_report_once("Blocked re-entrant IO on MemoryRegion: " "%s at addr: 0x%" HWADDR_PRIX, memory_region_name(mr), addr); return MEMTX_ACCESS_ERROR; } mr->dev->mem_reentrancy_guard.engaged_in_io = true; reentrancy_guard_applied = true; } /* FIXME: support unaligned access? */ // 处理访问大小调整 // 可能需要多次访问 access_size = MAX(MIN(size, access_size_max), access_size_min); access_mask = MAKE_64BIT_MASK(0, access_size * 8); if (devend_big_endian(mr->ops->endianness)) { for (i = 0; i < size; i += access_size) { // [Step 15] 调用访问器函数 r |= access_fn(mr, addr + i, value, access_size, (size - access_size - i) * 8, access_mask, attrs); } } else { for (i = 0; i < size; i += access_size) { r |= access_fn(mr, addr + i, value, access_size, i * 8, access_mask, attrs); } } if (mr->dev && reentrancy_guard_applied) { mr->dev->mem_reentrancy_guard.engaged_in_io = false; } return r;}static MemTxResult access_with_adjusted_size(hwaddr addr, uint64_t *value, unsigned size, unsigned access_size_min, unsigned access_size_max, MemTxResult (*access_fn) (MemoryRegion *mr, hwaddr addr, uint64_t *value, unsigned size, signed shift, uint64_t mask, MemTxAttrs attrs), MemoryRegion *mr, MemTxAttrs attrs){ uint64_t access_mask; unsigned access_size; unsigned i; MemTxResult r = MEMTX_OK; bool reentrancy_guard_applied = false; if (!access_size_min) { access_size_min = 1; } if (!access_size_max) { access_size_max = 4; } /* Do not allow more than one simultaneous access to a device's IO Regions */ if (mr->dev && !mr->disable_reentrancy_guard && !mr->ram_device && !mr->ram && !mr->rom_device && !mr->readonly) { if (mr->dev->mem_reentrancy_guard.engaged_in_io) { warn_report_once("Blocked re-entrant IO on MemoryRegion: " "%s at addr: 0x%" HWADDR_PRIX, memory_region_name(mr), addr); return MEMTX_ACCESS_ERROR; } mr->dev->mem_reentrancy_guard.engaged_in_io = true; reentrancy_guard_applied = true; } /* FIXME: support unaligned access? */ // 处理访问大小调整 // 可能需要多次访问 access_size = MAX(MIN(size, access_size_max), access_size_min); access_mask = MAKE_64BIT_MASK(0, access_size * 8); if (devend_big_endian(mr->ops->endianness)) { for (i = 0; i < size; i += access_size) { // [Step 15] 调用访问器函数 r |= access_fn(mr, addr + i, value, access_size,