Как создать простейшую модель GPIO для QEMU
Предлагаю два варианта, которые я условно решил назвать MMIO и PCI. Последний — тоже MMIO, но в QEMU они добавляются разными путями. Начнем с сердца любой MMIO-модели — апертуры.
Апертура и адресное пространство
Как я упоминал в одной из своих статей, любое MMIO-устройство — это MemoryRegion с заданными шириной доступа и размером. Для того, чтобы он был виден CPU или другому устройству, такому как DMA, его нужно разместить в соответствующем адресном пространстве — например, пространстве, назначенном для cpu0:
0x0 0xffffffffffffffff |------|------|------|------|------|------|------|------| 0: [ address-space: cpu-memory-0 ] 0: [ address-space: memory ] 0x102000 0x1023ff 0: [ gpio ]
В любое время можно посмотреть существующие адресные пространства и регионы памяти в мониторе QEMU:
(qemu) info mtree [...] address-space: cpu-memory-0 address-space: memory 0000000000000000-ffffffffffffffff (prio 0, i/o): system 0000000000102000-00000000001023ff (prio 0, i/o): gpio [...]
Тогда в модели устройства нам нужно всего лишь создать такой регион и назначить ему соответствующие функции записи и чтения:
static const MemoryRegionOps mmio_mmio_ops = { .read = mmio_gpio_register_read_memory, .write = mmio_gpio_register_write_memory, .endianness = DEVICE_NATIVE_ENDIAN, .valid = { .min_access_size = 4, .max_access_size = 4, }, }; [...] memory_region_init_io(iomem, obj, &mmio_mmio_ops, s, "gpio", APERTURE_SIZE); [...]
Фактически это означает, что все семейство инструкций Load/Store будет вызывать mmio_gpio_register_read_memory()/mmio_gpio_register_write_memory() при совпадении адреса чтения/записи с адресом региона в адресном пространстве.
static uint64_t mmio_gpio_register_read_memory(void *opaque, hwaddr addr, unsigned size); static void mmio_gpio_register_write_memory(void *opaque, hwaddr addr, uint64_t value, unsigned size);
Передаваемые аргументы и возвращаемое значения интуитивно понятны. Отмечу, что hwaddr addr — это адрес относительно начала нашего региона, а не абсолютный адрес.
Нам остается лишь создать устройство и добавить его регион в файле машины:
gpio = qdev_new(TYPE_MMIO_GPIO); sysbus_mmio_map(SYS_BUS_DEVICE(gpio), 0, ADDRESS);
Почти десять лет назад Никита Шубин, ведущий инженер по разработке СнК в YADRO, сделал возможность чтения и записи GPIO для QEMU. Читайте первую часть трилогии о долгом пути до GPIO в QEMU.











