Search
Write a publication
Pull to refresh
475.54
YADRO
Тут про железо и инженерную культуру

Как создать простейшую модель 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.

Tags:
Total votes 1: ↑1 and ↓0+1
Comments0

Articles

Information

Website
yadro.com
Registered
Founded
Employees
5,001–10,000 employees
Location
Россия
Representative
Ульяна Соловьева