Commit 15eafc2e authored by Paolo Bonzini's avatar Paolo Bonzini
Browse files

kvm: x86: add support for KVM_CAP_SPLIT_IRQCHIP



This patch adds support for split IRQ chip mode. When
KVM_CAP_SPLIT_IRQCHIP is enabled:

    1.) The PIC, PIT, and IOAPIC are implemented in userspace while
    the LAPIC is implemented by KVM.

    2.) The software IOAPIC delivers interrupts to the KVM LAPIC via
    kvm_set_irq. Interrupt delivery is configured via the MSI routing
    table, for which routes are reserved in target-i386/kvm.c then
    configured in hw/intc/ioapic.c

    3.) KVM delivers IOAPIC EOIs via a new exit KVM_EXIT_IOAPIC_EOI,
    which is handled in target-i386/kvm.c and relayed to the software
    IOAPIC via ioapic_eoi_broadcast.

Signed-off-by: default avatarMatt Gingell <gingell@google.com>
Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
parent 32c18a2d
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -65,6 +65,7 @@
#include "hw/mem/pc-dimm.h"
#include "qapi/visitor.h"
#include "qapi-visit.h"
#include "qom/cpu.h"

/* debug PC/ISA interrupts */
//#define DEBUG_IRQ
@@ -1517,7 +1518,7 @@ void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi,
    qemu_register_boot_set(pc_boot_set, *rtc_state);

    if (!xen_enabled()) {
        if (kvm_irqchip_in_kernel()) {
        if (kvm_pit_in_kernel()) {
            pit = kvm_pit_init(isa_bus, 0x40);
        } else {
            pit = pit_init(isa_bus, 0x40, pit_isa_irq, pit_alt_irq);
@@ -1592,7 +1593,7 @@ void ioapic_init_gsi(GSIState *gsi_state, const char *parent_name)
    SysBusDevice *d;
    unsigned int i;

    if (kvm_irqchip_in_kernel()) {
    if (kvm_ioapic_in_kernel()) {
        dev = qdev_create(NULL, "kvm-ioapic");
    } else {
        dev = qdev_create(NULL, "ioapic");
+2 −2
Original line number Diff line number Diff line
@@ -182,7 +182,7 @@ static void pc_init1(MachineState *machine,
    }

    gsi_state = g_malloc0(sizeof(*gsi_state));
    if (kvm_irqchip_in_kernel()) {
    if (kvm_ioapic_in_kernel()) {
        kvm_pc_setup_irq_routing(pci_enabled);
        gsi = qemu_allocate_irqs(kvm_pc_gsi_handler, gsi_state,
                                 GSI_NUM_PINS);
@@ -206,7 +206,7 @@ static void pc_init1(MachineState *machine,
    }
    isa_bus_irqs(isa_bus, gsi);

    if (kvm_irqchip_in_kernel()) {
    if (kvm_pic_in_kernel()) {
        i8259 = kvm_i8259_init(isa_bus);
    } else if (xen_enabled()) {
        i8259 = xen_interrupt_controller_init();
+66 −2
Original line number Diff line number Diff line
@@ -25,6 +25,8 @@
#include "hw/i386/pc.h"
#include "hw/i386/ioapic.h"
#include "hw/i386/ioapic_internal.h"
#include "include/hw/pci/msi.h"
#include "sysemu/kvm.h"

//#define DEBUG_IOAPIC

@@ -35,6 +37,10 @@
#define DPRINTF(fmt, ...)
#endif

#define APIC_DELIVERY_MODE_SHIFT 8
#define APIC_POLARITY_SHIFT 14
#define APIC_TRIG_MODE_SHIFT 15

static IOAPICCommonState *ioapics[MAX_IOAPICS];

/* global variable from ioapic_common.c */
@@ -54,6 +60,8 @@ static void ioapic_service(IOAPICCommonState *s)
    for (i = 0; i < IOAPIC_NUM_PINS; i++) {
        mask = 1 << i;
        if (s->irr & mask) {
            int coalesce = 0;

            entry = s->ioredtbl[i];
            if (!(entry & IOAPIC_LVT_MASKED)) {
                trig_mode = ((entry >> IOAPIC_LVT_TRIGGER_MODE_SHIFT) & 1);
@@ -64,6 +72,7 @@ static void ioapic_service(IOAPICCommonState *s)
                if (trig_mode == IOAPIC_TRIGGER_EDGE) {
                    s->irr &= ~mask;
                } else {
                    coalesce = s->ioredtbl[i] & IOAPIC_LVT_REMOTE_IRR;
                    s->ioredtbl[i] |= IOAPIC_LVT_REMOTE_IRR;
                }
                if (delivery_mode == IOAPIC_DM_EXTINT) {
@@ -71,8 +80,23 @@ static void ioapic_service(IOAPICCommonState *s)
                } else {
                    vector = entry & IOAPIC_VECTOR_MASK;
                }
                apic_deliver_irq(dest, dest_mode, delivery_mode,
                                 vector, trig_mode);
#ifdef CONFIG_KVM
                if (kvm_irqchip_is_split()) {
                    if (trig_mode == IOAPIC_TRIGGER_EDGE) {
                        kvm_set_irq(kvm_state, i, 1);
                        kvm_set_irq(kvm_state, i, 0);
                    } else {
                        if (!coalesce) {
                            kvm_set_irq(kvm_state, i, 1);
                        }
                    }
                    continue;
                }
#else
                (void)coalesce;
#endif
                apic_deliver_irq(dest, dest_mode, delivery_mode, vector,
                                 trig_mode);
            }
        }
    }
@@ -116,6 +140,44 @@ static void ioapic_set_irq(void *opaque, int vector, int level)
    }
}

static void ioapic_update_kvm_routes(IOAPICCommonState *s)
{
#ifdef CONFIG_KVM
    int i;

    if (kvm_irqchip_is_split()) {
        for (i = 0; i < IOAPIC_NUM_PINS; i++) {
            uint64_t entry = s->ioredtbl[i];
            uint8_t trig_mode;
            uint8_t delivery_mode;
            uint8_t dest;
            uint8_t dest_mode;
            uint64_t pin_polarity;
            MSIMessage msg;

            trig_mode = ((entry >> IOAPIC_LVT_TRIGGER_MODE_SHIFT) & 1);
            dest = entry >> IOAPIC_LVT_DEST_SHIFT;
            dest_mode = (entry >> IOAPIC_LVT_DEST_MODE_SHIFT) & 1;
            pin_polarity = (entry >> IOAPIC_LVT_TRIGGER_MODE_SHIFT) & 1;
            delivery_mode =
                (entry >> IOAPIC_LVT_DELIV_MODE_SHIFT) & IOAPIC_DM_MASK;

            msg.address = APIC_DEFAULT_ADDRESS;
            msg.address |= dest_mode << 2;
            msg.address |= dest << 12;

            msg.data = entry & IOAPIC_VECTOR_MASK;
            msg.data |= delivery_mode << APIC_DELIVERY_MODE_SHIFT;
            msg.data |= pin_polarity << APIC_POLARITY_SHIFT;
            msg.data |= trig_mode << APIC_TRIG_MODE_SHIFT;

            kvm_irqchip_update_msi_route(kvm_state, i, msg, NULL);
        }
        kvm_irqchip_commit_routes(kvm_state);
    }
#endif
}

void ioapic_eoi_broadcast(int vector)
{
    IOAPICCommonState *s;
@@ -229,6 +291,8 @@ ioapic_mem_write(void *opaque, hwaddr addr, uint64_t val,
        }
        break;
    }

    ioapic_update_kvm_routes(s);
}

static const MemoryRegionOps ioapic_io_ops = {
+13 −0
Original line number Diff line number Diff line
@@ -20,6 +20,19 @@

#define HPET_INTCAP "hpet-intcap"

#ifdef CONFIG_KVM
#define kvm_pit_in_kernel() \
    (kvm_irqchip_in_kernel() && !kvm_irqchip_is_split())
#define kvm_pic_in_kernel()  \
    (kvm_irqchip_in_kernel() && !kvm_irqchip_is_split())
#define kvm_ioapic_in_kernel() \
    (kvm_irqchip_in_kernel() && !kvm_irqchip_is_split())
#else
#define kvm_pit_in_kernel()      0
#define kvm_pic_in_kernel()      0
#define kvm_ioapic_in_kernel()   0
#endif

/**
 * PCMachineState:
 * @acpi_dev: link to ACPI PM device that performs ACPI hotplug handling
+5 −1
Original line number Diff line number Diff line
@@ -174,6 +174,7 @@ extern bool kvm_ioeventfd_any_length_allowed;
#else
#define kvm_enabled()           (0)
#define kvm_irqchip_in_kernel() (false)
#define kvm_irqchip_is_split() (false)
#define kvm_async_interrupts_enabled() (false)
#define kvm_halt_in_kernel() (false)
#define kvm_eventfds_enabled() (false)
@@ -317,6 +318,8 @@ MemTxAttrs kvm_arch_post_run(CPUState *cpu, struct kvm_run *run);

int kvm_arch_handle_exit(CPUState *cpu, struct kvm_run *run);

int kvm_arch_handle_ioapic_eoi(CPUState *cpu, struct kvm_run *run);

int kvm_arch_process_async_events(CPUState *cpu);

int kvm_arch_get_registers(CPUState *cpu);
@@ -484,6 +487,7 @@ void kvm_init_irq_routing(KVMState *s);
/**
 * kvm_arch_irqchip_create:
 * @KVMState: The KVMState pointer
 * @MachineState: The MachineState pointer
 *
 * Allow architectures to create an in-kernel irq chip themselves.
 *
@@ -491,7 +495,7 @@ void kvm_init_irq_routing(KVMState *s);
 *            0: irq chip was not created
 *          > 0: irq chip was created
 */
int kvm_arch_irqchip_create(KVMState *s);
int kvm_arch_irqchip_create(MachineState *ms, KVMState *s);

/**
 * kvm_set_one_reg - set a register value in KVM via KVM_SET_ONE_REG ioctl
Loading