#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
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);
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");
}
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);
}
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();
#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
#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 */
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);
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) {
} 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);
}
}
}
}
}
+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;
}
break;
}
+
+ ioapic_update_kvm_routes(s);
}
static const MemoryRegionOps ioapic_io_ops = {
#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
#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)
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);
/**
* kvm_arch_irqchip_create:
* @KVMState: The KVMState pointer
+ * @MachineState: The MachineState pointer
*
* Allow architectures to create an in-kernel irq chip themselves.
*
* 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
KVMState *kvm_state;
bool kvm_kernel_irqchip;
+bool kvm_split_irqchip;
bool kvm_async_interrupts_allowed;
bool kvm_halt_in_kernel_allowed;
bool kvm_eventfds_allowed;
/* First probe and see if there's a arch-specific hook to create the
* in-kernel irqchip for us */
- ret = kvm_arch_irqchip_create(s);
+ ret = kvm_arch_irqchip_create(machine, s);
if (ret == 0) {
- ret = kvm_vm_ioctl(s, KVM_CREATE_IRQCHIP);
+ if (machine_kernel_irqchip_split(machine)) {
+ perror("Split IRQ chip mode not supported.");
+ exit(1);
+ } else {
+ ret = kvm_vm_ioctl(s, KVM_CREATE_IRQCHIP);
+ }
}
if (ret < 0) {
fprintf(stderr, "Create kernel irqchip failed: %s\n", strerror(-ret));
#include "qemu-common.h"
#include "sysemu/kvm.h"
-int kvm_arch_irqchip_create(KVMState *s)
+int kvm_arch_irqchip_create(MachineState *ms, KVMState *s)
{
return 0;
}
#include "internals.h"
#include "hw/arm/arm.h"
#include "exec/memattrs.h"
+#include "hw/boards.h"
const KVMCapabilityInfo kvm_arch_required_capabilities[] = {
KVM_CAP_LAST_INFO
{
}
-int kvm_arch_irqchip_create(KVMState *s)
+int kvm_arch_irqchip_create(MachineState *ms, KVMState *s)
{
+ if (machine_kernel_irqchip_split(ms)) {
+ perror("-machine kernel_irqchip=split is not supported on ARM.");
+ exit(1);
+ }
+
/* If we can create the VGIC using the newer device control API, we
* let the device do this when it initializes itself, otherwise we
* fall back to the old API */
APICCommonState *apic;
const char *apic_type = "apic";
- if (kvm_irqchip_in_kernel()) {
+ if (kvm_apic_in_kernel()) {
apic_type = "kvm-apic";
} else if (xen_enabled()) {
apic_type = "xen-apic";
#include "exec/ioport.h"
#include "standard-headers/asm-x86/hyperv.h"
#include "hw/pci/pci.h"
+#include "hw/pci/msi.h"
#include "migration/migration.h"
#include "exec/memattrs.h"
}
}
- if (!kvm_irqchip_in_kernel()) {
+ if (!kvm_pic_in_kernel()) {
qemu_mutex_lock_iothread();
}
}
}
- if (!kvm_irqchip_in_kernel()) {
+ if (!kvm_pic_in_kernel()) {
/* Try to inject an interrupt if the guest can accept it */
if (run->ready_for_interrupt_injection &&
(cpu->interrupt_request & CPU_INTERRUPT_HARD) &&
case KVM_EXIT_HYPERV:
ret = kvm_hv_handle_exit(cpu, &run->hyperv);
break;
+ case KVM_EXIT_IOAPIC_EOI:
+ ioapic_eoi_broadcast(run->eoi.vector);
+ ret = 0;
+ break;
default:
fprintf(stderr, "KVM: unknown exit reason %d\n", run->exit_reason);
ret = -1;
*/
kvm_msi_via_irqfd_allowed = true;
kvm_gsi_routing_allowed = true;
+
+ if (kvm_irqchip_is_split()) {
+ int i;
+
+ /* If the ioapic is in QEMU and the lapics are in KVM, reserve
+ MSI routes for signaling interrupts to the local apics. */
+ for (i = 0; i < IOAPIC_NUM_PINS; i++) {
+ struct MSIMessage msg = { 0x0, 0x0 };
+ if (kvm_irqchip_add_msi_route(s, msg, NULL) < 0) {
+ error_report("Could not enable split IRQ mode.");
+ exit(1);
+ }
+ }
+ }
+}
+
+int kvm_arch_irqchip_create(MachineState *ms, KVMState *s)
+{
+ int ret;
+ if (machine_kernel_irqchip_split(ms)) {
+ ret = kvm_vm_enable_cap(s, KVM_CAP_SPLIT_IRQCHIP, 0, 24);
+ if (ret) {
+ error_report("Could not enable split irqchip mode: %s\n",
+ strerror(-ret));
+ exit(1);
+ } else {
+ DPRINTF("Enabled KVM_CAP_SPLIT_IRQCHIP\n");
+ kvm_split_irqchip = true;
+ return 1;
+ }
+ } else {
+ return 0;
+ }
}
/* Classic KVM device assignment interface. Will remain x86 only. */
#include "sysemu/kvm.h"
+#define kvm_apic_in_kernel() (kvm_irqchip_in_kernel())
+
bool kvm_allows_irq0_override(void);
bool kvm_has_smm(void);
void kvm_synchronize_all_tsc(void);