Search code examples
interruptqemuarm64

Why my interrupt is not connected? qemu_irq_is_connected( ) returns false


I'm trying to make interrupt work for a device in qemu. The machnie name is ab21q, a modified version of arm64 virt machine, and the device name is ab21q_axpu.
Here are some relevant codes. I referenced pl011.c. (I switched temporarily back to qemu-5.1.0 for this test.)

==== hw/arm/ab21q.c

machab21q_init(MachineState *machine)
{
.... skip .... 
create_ab21q_axpu_device(vms, sysmem); // ab21q-axpu test
....
}

static void create_ab21q_axpu_device(const Ab21qMachineState *vms, MemoryRegion *mem)
{
    char *nodename;
    hwaddr base = vms->memmap[AB21Q_AXPU].base;
    hwaddr size = vms->memmap[AB21Q_AXPU].size;
    int irq = vms->irqmap[AB21Q_AXPU];
    const char compat[] = "ab21q-axpu";
    DeviceState *dev = qdev_new(TYPE_AB21Q_AXPU);
    SysBusDevice *s = SYS_BUS_DEVICE(dev);

    //sysbus_create_simple("ab21q-axpu", base, qdev_get_gpio_in(vms->gic, irq));
    sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
    memory_region_add_subregion(mem, base,
                                sysbus_mmio_get_region(s, 0));
    sysbus_connect_irq(s, 0, qdev_get_gpio_in(vms->gic, irq));

    nodename = g_strdup_printf("/ab21q_axpu@%" PRIx64, base);
    qemu_fdt_add_subnode(vms->fdt, nodename);
    qemu_fdt_setprop(vms->fdt, nodename, "compatible", compat, sizeof(compat));
    qemu_fdt_setprop_sized_cells(vms->fdt, nodename, "reg", 2, base, 2, size);
    qemu_fdt_setprop_cells(vms->fdt, nodename, "interrupts",
                           GIC_FDT_IRQ_TYPE_SPI, irq,
                           GIC_FDT_IRQ_FLAGS_LEVEL_HI);
    qemu_fdt_setprop_cell(vms->fdt, nodename, "interrupt-parent", vms->gic_phandle);

    g_free(nodename);
}

==== hw/misc/ab21q_axpu.c

static void ab21q_axpu_init(Object *obj)
{
    Ab21qAxpuState *s = AB21Q_AXPU(obj);
    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
    int i;

    memory_region_init_io(&s->iomem, OBJECT(s), &ab21q_axpu_ops, s,
                            TYPE_AB21Q_AXPU, 0x200000*64);
    sysbus_init_mmio(sbd, &s->iomem);
    sysbus_init_irq(sbd, &s->irq);

    s->init = 0;
    s->int_flag = 0;
    s->status = 0;

    s->id = CHIP_ID;
}

static void ab21q_axpu_realize(DeviceState *d, Error **errp)
{
    Ab21qAxpuState *s = AB21Q_AXPU(d);
    SysBusDevice *sbd = SYS_BUS_DEVICE(d);

    if (qemu_irq_is_connected(s->irq)) {printf("axpu irq connected!\n");}
    else { printf("axpu irq not connected!\n");}
}

static void ab21q_axpu_class_init(ObjectClass *klass, void *data)
{
    DeviceClass *dc = DEVICE_CLASS(klass);

    dc->realize = ab21q_axpu_realize;
}


static void ab21q_axpu_set_irq(Ab21qAxpuState *s, int irq)
{
    s->status = irq;
    qemu_set_irq(s->irq, 1);
}

static void ab21q_axpu_write(void *opaque, hwaddr offset, uint64_t value,
                          unsigned size)
{
Ab21qAxpuState *s = (Ab21qAxpuState *)opaque;

.... skip ... 
switch (offset) {

case TRIGGER_RUN:
.... 
        if (((uint64_t *)(s->ioctl_arg + *host_virt_offset_p))[0] == 0x1604) {
            s->int_flag = 1;
            ab21q_axpu_set_irq(s, INT_AXPU_RUN_FINISHED);
        }

The machine and the device works. (actually the device is a shared library that qemu links to) except the interrupt doesn't work even though qemu does set_irq. I checked with qemu_irq_is_connected in realize function. The pl011 case says pl011 irq connected!, but in my device I see axpu irq not connected!. So it's not related to device driver but qemu model itself. Could anyone find what is missing in the above code? Should I add something to acpi table(in ab21q-build-acpi.c)?

I tried adding in hw/arm/ab21q-build-acpi.c these lines. In function build_dsdt,

acpi_dsdt_add_axpu(scope, &memmap[AB21Q_AXPU],
                       (irqmap[AB21Q_AXPU] + ARM_SPI_BASE));

The acpi_dsdt_add_axpu function being

static void acpi_dsdt_add_axpu(Aml *scope, const MemMapEntry *uart_memmap,
                                           uint32_t irq)
{
    Aml *dev = aml_device("AXPU");
    aml_append(dev, aml_name_decl("_HID", aml_string("AXPU0011")));
    aml_append(dev, aml_name_decl("_UID", aml_int(0)));

    Aml *crs = aml_resource_template();
    aml_append(crs, aml_memory32_fixed(uart_memmap->base,
                                       uart_memmap->size, AML_READ_WRITE));
    aml_append(crs,
               aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH,
                             AML_EXCLUSIVE, &irq, 1));
    aml_append(dev, aml_name_decl("_CRS", crs));

    aml_append(scope, dev);
}

Inside the virtual machine (ubuntu 20.04), I did acpidump and converted it to .dsl files. The dsdt.dsl contains this entry.

Device (AXPU) { Name (_HID, "AXPU0011") // _HID: Hardware ID Name (_UID, Zero) // _UID: Unique ID Name (_CRS, ResourceTemplate () // _CRS: Current Resource Settings { Memory32Fixed (ReadWrite, 0x09100000, // Address Base 0x00080000, // Address Length ) Interrupt (ResourceConsumer, Level, ActiveHigh, Exclusive, ,, ) { 0x000000D0, } }) }

I'm not sure what I should fix in acpi table. Any comment or advice will be deeply appreciated.

Thank you!

Chan Kim


Solution

  • So I added some prints. This is the result.

    create_uart called!
    pl011_init called!
    pl011_realize called!
    pl011 irq not connected!
    now calling sysbus_connect_irq for pl011..
    now passed sysbus_connect_irq for pl011..
    pl011 irq connected!
    create_ab21q_axpu_device called!
    ab21q_axpu_init called!
    ab21q_axpu_realize called!
    axpu irq not connected!
    now calling sysbus_connect_irq for ab21q_axpu..
    now passed sysbus_connect_irq for ab21q_axpu..
    ab21q_axpu irq connected!
    

    So irq-connection-wise speaking, pl011 and ab21q_axpu is the same! For pl011, the irq was not connected either before the sysbus_connect_irq, but in the code above, I used qemu_irq_is_connected(s->irq)) by mistake and it gave me true (a false true). I fixed it to qemu_irq_is_connected(s->irq[0])) because it had 6 irq outputs and it returned false before the sysbus_connect_irq. After the sysbus_connect_irq, both ab21q_axpu and pl011's irq shown to have been connected. And of course I used qemu_irq_is_connected(AB21Q_AXPU(dev)->irq) or qemu_irq_is_connected(PL011(dev)->irq[0]) to check the irq connection inside the xxx_create functions.

    ADD : I later found the request_irq functino returned -EINVAL. so interrupt was not registerred correctly in the driver.