QEMU 模拟外设的原理

QEMU 主要是实现了 CPU 核的模拟,可以读写某个地址。 QEMU 的模拟外设的原理很简单:硬件即内存。 要在 QEMU 上模拟某个外设,思路就是:

  • CPU 读某个地址时,QEMU 模拟外设的行为,把数据返回给 CPU
  • CPU 写某个地址时,QEMU 获得数据,用来模拟外设的行为。 即:要模拟外设备,我们只需要针对外设的地址提供对应的读写函数即可。

以 GPIO 为例:

Responsive Image

QEMU 为GPIO内存地址提供读写回调函数,

static void sifive_gpio_write(void *opaque, hwaddr offset, uint64_t value, unsigned int size)

static uint64_t sifive_gpio_read(void *opaque, hwaddr offset, unsigned int size)

给外设地址提供读写函数

怎么描述某段地址:基地址、大小?如何给这段地址提供读写函数呢?这段地址设置好后,如何添加进system_memory去?有 2 种方法。

法 1:memory_region_init_io/memory_region_add_subregionSIFIVE_UART为例,

memory_region_init_io(&s->mmio, NULL, &uart_ops, s,
                        TYPE_SIFIVE_UART, 0x2000);
memory_region_add_subregion(address_space, base, &s->mmio);

memory_region_init_io函数初始化iomem,读写函数,大小。 memory_region_add_subregion函数s->iomem指定了基地址,并添加进system_memory中。 以后,客户机上的程序读写这块地址时,就会导致对应的读写函数被调用。

法 2:memory_region_init_io/sysbus_init_mmio/sysbus_mmio_mapSIFIVE_GPIO为例,

memory_region_init_io(&s->mmio, OBJECT(dev), &gpio_ops, s, TYPE_SIFIVE_GPIO, SIFIVE_GPIO_SIZE);

sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio);

memory_region_init_io函数初始化iomem,读写函数,大小。 sysbus_init_mmiommin传给设备;

sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio), 0, memmap[SIFIVE_E_DEV_GPIO0].base);

sysbus_mmio_map从设备中吧mmio添加进system_memory并指定基地址。