/home/tnishinaga/TechMEMO

日々行ったこと、面白かったことを書き留めます。

QEMUのARM Virtマシンのペリフェラルメモリマップについてのメモ

QEMUのARM Virtマシンのペリフェラルメモリマップは何処を見ればいいんだっけ......とよく忘れるので、パスと見るべきところをメモします。

マシンを定義しているファイルはこれ。

qemu/virt.c at master · qemu/qemu · GitHub

メモリマップは125行目辺り MemMapEntry a15memmap[] にかかれています。 以下、該当コードを引用します。

static const MemMapEntry a15memmap[] = {
    /* Space up to 0x8000000 is reserved for a boot ROM */
    [VIRT_FLASH] =              {          0, 0x08000000 },
    [VIRT_CPUPERIPHS] =         { 0x08000000, 0x00020000 },
    /* GIC distributor and CPU interfaces sit inside the CPU peripheral space */
    [VIRT_GIC_DIST] =           { 0x08000000, 0x00010000 },
    [VIRT_GIC_CPU] =            { 0x08010000, 0x00010000 },
    [VIRT_GIC_V2M] =            { 0x08020000, 0x00001000 },
    /* The space in between here is reserved for GICv3 CPU/vCPU/HYP */
    [VIRT_GIC_ITS] =            { 0x08080000, 0x00020000 },
    /* This redistributor space allows up to 2*64kB*123 CPUs */
    [VIRT_GIC_REDIST] =         { 0x080A0000, 0x00F60000 },
    [VIRT_UART] =               { 0x09000000, 0x00001000 },
    [VIRT_RTC] =                { 0x09010000, 0x00001000 },
    [VIRT_FW_CFG] =             { 0x09020000, 0x00000018 },
    [VIRT_GPIO] =               { 0x09030000, 0x00001000 },
    [VIRT_SECURE_UART] =        { 0x09040000, 0x00001000 },
    [VIRT_MMIO] =               { 0x0a000000, 0x00000200 },
    /* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */
    [VIRT_PLATFORM_BUS] =       { 0x0c000000, 0x02000000 },
    [VIRT_SECURE_MEM] =         { 0x0e000000, 0x01000000 },
    [VIRT_PCIE_MMIO] =          { 0x10000000, 0x2eff0000 },
    [VIRT_PCIE_PIO] =           { 0x3eff0000, 0x00010000 },
    [VIRT_PCIE_ECAM] =          { 0x3f000000, 0x01000000 },
    [VIRT_MEM] =                { 0x40000000, RAMLIMIT_BYTES },
    /* Second PCIe window, 512GB wide at the 512GB boundary */
    [VIRT_PCIE_MMIO_HIGH] =   { 0x8000000000ULL, 0x8000000000ULL },
};

どのペリフェラルが使われているかはここからはわからないので、memmapの1列目の文字列から探していく必要があります。

例えばUARTなら、VIRT_UARTで検索をすると1422行目あたりに create_uart 関数を呼び出しているところが見つかります。

create_uart(vms, pic, VIRT_UART, sysmem, serial_hds[0]);

この create_uart 関数の定義は644行目あたりにあり、このコードを見ていくと頭の方にペリフェラルの名前が書いてあるので、PL011が使われていることがわかります。

static void create_uart(const VirtMachineState *vms, qemu_irq *pic, int uart,
                        MemoryRegion *mem, Chardev *chr)
{
    char *nodename;
    hwaddr base = vms->memmap[uart].base;
    hwaddr size = vms->memmap[uart].size;
    int irq = vms->irqmap[uart];
    const char compat[] = "arm,pl011\0arm,primecell";

後はペリフェラルの資料を探して読んでドライバを実装すればOKです。

おしまい。