QEMU ARM interrupt system architecture

QEMU interrupt system use GPIO to implement interrupt system. We can understand it by a simple model:

Device.[GPIO_OUT] ->[GPIO_IN].GIC.[GPIO_OUT]->[GPIO_IN].core

 

GPIO_IN IRQ is created by qdev_init_gpio_in()

GPIO_OUT IRQ is initialized by sysbus_init_irq()->qdev_init_gpio_out_named(). Attention, GPIO_OUT IRQ is not created. Actually it is just a IRQ pointer, and the IRQ pointer will point to the GPIO_IN IRQ which connected. sysbus_connect_irq() connects GPIO_OUT and GPIO_IN IRQ.

 

Here let's take vexpress-a9 as the example. 

ARM core:

ARM core has irq,fiq,virq,vfiq for GPIO_IN IRQ. they are created at:

arm_cpu_initfn()->qdev_init_gpio_in(DEVICE(cpu), arm_cpu_set_irq, 4);

GIC:

GIC has 96 GPIO_IN IRQs and 4 GPIO_OUT IRQs, and they are created at:

arm_gic_realize()->gic_init_irqs_and_mmio()

void gic_init_irqs_and_mmio(GICState *s, qemu_irq_handler handler,
                            const MemoryRegionOps *ops,
                            const MemoryRegionOps *virt_ops)
{
    SysBusDevice *sbd = SYS_BUS_DEVICE(s);
    int i = s->num_irq - GIC_INTERNAL;

    /* For the GIC, also expose incoming GPIO lines for PPIs for each CPU.
     * GPIO array layout is thus:
     *  [0..N-1] SPIs
     *  [N..N+31] PPIs for CPU 0
     *  [N+32..N+63] PPIs for CPU 1
     *   ...
     */
    i += (GIC_INTERNAL * s->num_cpu);
    qdev_init_gpio_in(DEVICE(s), handler, i); // GPIO_IN IRQs

    // GPIO_OUT IRQs 
    for (i = 0; i < s->num_cpu; i++) {
        sysbus_init_irq(sbd, &s->parent_irq[i]);
    }
    for (i = 0; i < s->num_cpu; i++) {
        sysbus_init_irq(sbd, &s->parent_fiq[i]);
    }
    for (i = 0; i < s->num_cpu; i++) {
        sysbus_init_irq(sbd, &s->parent_virq[i]);
    }
    for (i = 0; i < s->num_cpu; i++) {
        sysbus_init_irq(sbd, &s->parent_vfiq[i]);
    }
    ....
}

GICState is GIC state logic. We can see it has parent_irq[],parent_fiq[],parent_virq[],parent_vfiq[] 4 GPIO_OUT IRQs.

Actually they are all IRQ pointers. They will point to core's 4 GPIO_IN IRQs. Where do them connect? in init_cpus().

static void init_cpus(MachineState *ms, const char *cpu_type,
                      const char *privdev, hwaddr periphbase,
                      qemu_irq *pic, bool secure, bool virt)
{
    DeviceState *dev;
    SysBusDevice *busdev;
    .....
    .....
    dev = qdev_create(NULL, privdev);
    busdev = SYS_BUS_DEVICE(dev); /*here busdev is a9mpcore_priv*/
    .....
    for (n = 0; n < smp_cpus; n++) {
        DeviceState *cpudev = DEVICE(qemu_get_cpu(n));

        sysbus_connect_irq(busdev, n, qdev_get_gpio_in(cpudev, ARM_CPU_IRQ));
        sysbus_connect_irq(busdev, n + smp_cpus,
                           qdev_get_gpio_in(cpudev, ARM_CPU_FIQ));
        sysbus_connect_irq(busdev, n + 2 * smp_cpus,
                           qdev_get_gpio_in(cpudev, ARM_CPU_VIRQ));
        sysbus_connect_irq(busdev, n + 3 * smp_cpus,
                           qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ));
    }
    .....
}

sysbus_connect_irq() connect device's GPIO_OUT IRQ to CPU's GPIO_IN IRQ. But here device is a9mpcore_priv, not GIT.

How does it work? 

In a9mp_priv_realize(), there is a IRQ pass code:

sysbus_pass_irq(sbd, gicbusdev); 

Here sbd presents a9mpcore_priv, gicbusdev presents GIC. This function make them connect, and a9mpcore_priv can use GIC's GPIO_OUT IRQ. 

Device:

let's take UART as example.

Device need initialize GPIO_OUT IRQ and connect to GIC GPIO_IN IRQ.

Initialzation at:

static void pl011_init(Object *obj)
{
    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
    PL011State *s = PL011(obj);
    int i;

    .....
    for (i = 0; i < ARRAY_SIZE(s->irq); i++) {
        sysbus_init_irq(sbd, &s->irq[i]);
        printf("alexxie:%s, uart irq at %p\n",__func__,s->irq[i]);
    }
    .....
}

Connection at:

static void vexpress_common_init(MachineState *machine)
{
    qemu_irq pic[64];
    pl011_create(map[VE_UART0], pic[5], serial_hd(0));
    pl011_create(map[VE_UART1], pic[6], serial_hd(1));
    pl011_create(map[VE_UART2], pic[7], serial_hd(2));
    pl011_create(map[VE_UART3], pic[8], serial_hd(3));
}

map[] is address map of the vexpress. pic[] is the GIC GPIO_IN IRQ. 

Let's read pl011_create firstly, then back to pic[].

static inline DeviceState *pl011_create(hwaddr addr,
                                        qemu_irq irq,
                                        Chardev *chr)
{
    DeviceState *dev;
    SysBusDevice *s;
    PL011State *uart_s ;
    dev = qdev_create(NULL, "pl011");
    s = SYS_BUS_DEVICE(dev);
    .....
    sysbus_connect_irq(s, 0, irq); //connect irq
    uart_s = PL011(dev);
    printf("alexdebug:%s, uart irq is at %p,%p,%p,%p,%p,%p\n",__func__,uart_s->irq[0],uart_s->irq[1],uart_s->irq[2],uart_s->irq[3],uart_s->irq[4],uart_s->irq[5]);
    return dev;
}

Here we can see sysbus_connect_irq() is called to connect pl011 to irq. irq is from pic[5],pic[6],pic[7] and pic[8].

How do we know pic[] is from GIC GPIO_IN? let's check the code.

static void vexpress_common_init(MachineState *machine)
{
    VexpressMachineClass *vmc = VEXPRESS_MACHINE_GET_CLASS(machine);
    VEDBoardInfo *daughterboard = vmc->daughterboard;
    qemu_irq pic[64];
    .....
    daughterboard->init(vms, machine->ram_size, machine->cpu_type, pic);
    .....
}

daughterboard->init is a9_daughterboard_init()

static void a9_daughterboard_init(const VexpressMachineState *vms,
                                  ram_addr_t ram_size,
                                  const char *cpu_type,
                                  qemu_irq *pic)
{

    /* 0x1e000000 A9MPCore (SCU) private memory region */
    init_cpus(machine, cpu_type, TYPE_A9MPCORE_PRIV, 0x1e000000, pic,
              vms->secure, vms->virt);
}

keep dive into init_cpus()

static void init_cpus(MachineState *ms, const char *cpu_type,
                      const char *privdev, hwaddr periphbase,
                      qemu_irq *pic, bool secure, bool virt)
{
    DeviceState *dev;
    .....
    dev = qdev_create(NULL, privdev);
    .....
    {
    a9_s = A9MPCORE_PRIV(dev);
    printf("alexdebug:%s, GIC parent_irq at %p\n",__func__,a9_s->gic.parent_irq[0]);
    }
    for (n = 0; n < 64; n++) {
    	 DeviceState* gicdev=DEVICE(&a9_s->gic);
        pic[n] = qdev_get_gpio_in(gicdev, n); //get gic gpio_in
        //pic[n] = qdev_get_gpio_in(dev, n);
        printf("alexxie:%s, pic[%d] at %p\n",__func__,n,pic[n]);
    }
    .....
}

OK, here we can see pic[] is from GIC by function qdev_get_gpio_in(). You may noticed the code is changed by me. Yes, the origin code pic[] is from a9mpcore_priv and a9mpcore_priv will call GIC. I modified it because I want to try if I can make it simpler to remove one layer of GPIO. it works. So the code is current look.

OK, to here, the question is answered.

上一篇:hue使用http请求的方式访问接口


下一篇:如何安装thinkphp