/* * qemu/kvm integration * * Copyright (C) 2006-2008 Qumranet Technologies * * Licensed under the terms of the GNU GPL version 2 or higher. */ #include "config.h" #include "config-host.h" #include #include #include "hw/hw.h" #include "sysemu.h" #include "qemu-common.h" #include "console.h" #include "block.h" #include "compatfd.h" #include "gdbstub.h" #include "monitor.h" #include "cpus.h" #include "qemu-kvm.h" #define EXPECTED_KVM_API_VERSION 12 #if EXPECTED_KVM_API_VERSION != KVM_API_VERSION #error libkvm: userspace and kernel version mismatch #endif int kvm_irqchip = 1; int kvm_pit = 1; int kvm_pit_reinject = 1; int kvm_nested = 0; #define ALIGN(x, y) (((x)+(y)-1) & ~((y)-1)) static inline void set_gsi(KVMState *s, unsigned int gsi) { uint32_t *bitmap = s->used_gsi_bitmap; if (gsi < s->max_gsi) { bitmap[gsi / 32] |= 1U << (gsi % 32); } else { DPRINTF("Invalid GSI %u\n", gsi); } } static inline void clear_gsi(KVMState *s, unsigned int gsi) { uint32_t *bitmap = s->used_gsi_bitmap; if (gsi < s->max_gsi) { bitmap[gsi / 32] &= ~(1U << (gsi % 32)); } else { DPRINTF("Invalid GSI %u\n", gsi); } } static int kvm_init_irq_routing(KVMState *s) { #ifdef KVM_CAP_IRQ_ROUTING int r, gsi_count; gsi_count = kvm_check_extension(s, KVM_CAP_IRQ_ROUTING); if (gsi_count > 0) { int gsi_bits, i; /* Round up so we can search ints using ffs */ gsi_bits = ALIGN(gsi_count, 32); s->used_gsi_bitmap = qemu_mallocz(gsi_bits / 8); s->max_gsi = gsi_bits; /* Mark any over-allocated bits as already in use */ for (i = gsi_count; i < gsi_bits; i++) { set_gsi(s, i); } } s->irq_routes = qemu_mallocz(sizeof(*s->irq_routes)); s->nr_allocated_irq_routes = 0; r = kvm_arch_init_irq_routing(); if (r < 0) { return r; } #endif return 0; } int kvm_create_irqchip(KVMState *s) { #ifdef KVM_CAP_IRQCHIP int r; if (!kvm_irqchip || !kvm_check_extension(s, KVM_CAP_IRQCHIP)) { return 0; } r = kvm_vm_ioctl(s, KVM_CREATE_IRQCHIP); if (r < 0) { fprintf(stderr, "Create kernel PIC irqchip failed\n"); return r; } s->irqchip_inject_ioctl = KVM_IRQ_LINE; #if defined(KVM_CAP_IRQ_INJECT_STATUS) && defined(KVM_IRQ_LINE_STATUS) if (kvm_check_extension(s, KVM_CAP_IRQ_INJECT_STATUS)) { s->irqchip_inject_ioctl = KVM_IRQ_LINE_STATUS; } #endif s->irqchip_in_kernel = 1; r = kvm_init_irq_routing(s); if (r < 0) { return r; } #endif return 0; } #ifdef KVM_CAP_IRQCHIP int kvm_set_irq(int irq, int level, int *status) { struct kvm_irq_level event; int r; if (!kvm_state->irqchip_in_kernel) { return 0; } event.level = level; event.irq = irq; r = kvm_vm_ioctl(kvm_state, kvm_state->irqchip_inject_ioctl, &event); if (r < 0) { perror("kvm_set_irq"); } if (status) { #ifdef KVM_CAP_IRQ_INJECT_STATUS *status = (kvm_state->irqchip_inject_ioctl == KVM_IRQ_LINE) ? 1 : event.status; #else *status = 1; #endif } return 1; } int kvm_get_irqchip(KVMState *s, struct kvm_irqchip *chip) { int r; if (!s->irqchip_in_kernel) { return 0; } r = kvm_vm_ioctl(s, KVM_GET_IRQCHIP, chip); if (r < 0) { perror("kvm_get_irqchip\n"); } return r; } int kvm_set_irqchip(KVMState *s, struct kvm_irqchip *chip) { int r; if (!s->irqchip_in_kernel) { return 0; } r = kvm_vm_ioctl(s, KVM_SET_IRQCHIP, chip); if (r < 0) { perror("kvm_set_irqchip\n"); } return r; } #endif #ifdef KVM_CAP_DEVICE_ASSIGNMENT int kvm_assign_pci_device(KVMState *s, struct kvm_assigned_pci_dev *assigned_dev) { return kvm_vm_ioctl(s, KVM_ASSIGN_PCI_DEVICE, assigned_dev); } static int kvm_old_assign_irq(KVMState *s, struct kvm_assigned_irq *assigned_irq) { return kvm_vm_ioctl(s, KVM_ASSIGN_IRQ, assigned_irq); } #ifdef KVM_CAP_ASSIGN_DEV_IRQ int kvm_assign_irq(KVMState *s, struct kvm_assigned_irq *assigned_irq) { int ret; ret = kvm_ioctl(s, KVM_CHECK_EXTENSION, KVM_CAP_ASSIGN_DEV_IRQ); if (ret > 0) { return kvm_vm_ioctl(s, KVM_ASSIGN_DEV_IRQ, assigned_irq); } return kvm_old_assign_irq(s, assigned_irq); } int kvm_deassign_irq(KVMState *s, struct kvm_assigned_irq *assigned_irq) { return kvm_vm_ioctl(s, KVM_DEASSIGN_DEV_IRQ, assigned_irq); } #else int kvm_assign_irq(KVMState *s, struct kvm_assigned_irq *assigned_irq) { return kvm_old_assign_irq(s, assigned_irq); } #endif #endif #ifdef KVM_CAP_DEVICE_DEASSIGNMENT int kvm_deassign_pci_device(KVMState *s, struct kvm_assigned_pci_dev *assigned_dev) { return kvm_vm_ioctl(s, KVM_DEASSIGN_PCI_DEVICE, assigned_dev); } #endif int kvm_reinject_control(KVMState *s, int pit_reinject) { #ifdef KVM_CAP_REINJECT_CONTROL int r; struct kvm_reinject_control control; control.pit_reinject = pit_reinject; r = kvm_ioctl(s, KVM_CHECK_EXTENSION, KVM_CAP_REINJECT_CONTROL); if (r > 0) { return kvm_vm_ioctl(s, KVM_REINJECT_CONTROL, &control); } #endif return -ENOSYS; } int kvm_has_gsi_routing(void) { int r = 0; #ifdef KVM_CAP_IRQ_ROUTING r = kvm_check_extension(kvm_state, KVM_CAP_IRQ_ROUTING); #endif return r; } int kvm_clear_gsi_routes(void) { #ifdef KVM_CAP_IRQ_ROUTING kvm_state->irq_routes->nr = 0; return 0; #else return -EINVAL; #endif } int kvm_add_routing_entry(struct kvm_irq_routing_entry *entry) { #ifdef KVM_CAP_IRQ_ROUTING KVMState *s = kvm_state; struct kvm_irq_routing *z; struct kvm_irq_routing_entry *new; int n, size; if (s->irq_routes->nr == s->nr_allocated_irq_routes) { n = s->nr_allocated_irq_routes * 2; if (n < 64) { n = 64; } size = sizeof(struct kvm_irq_routing); size += n * sizeof(*new); z = realloc(s->irq_routes, size); if (!z) { return -ENOMEM; } s->nr_allocated_irq_routes = n; s->irq_routes = z; } n = s->irq_routes->nr++; new = &s->irq_routes->entries[n]; memset(new, 0, sizeof(*new)); new->gsi = entry->gsi; new->type = entry->type; new->flags = entry->flags; new->u = entry->u; set_gsi(s, entry->gsi); return 0; #else return -ENOSYS; #endif } int kvm_add_irq_route(int gsi, int irqchip, int pin) { #ifdef KVM_CAP_IRQ_ROUTING struct kvm_irq_routing_entry e; e.gsi = gsi; e.type = KVM_IRQ_ROUTING_IRQCHIP; e.flags = 0; e.u.irqchip.irqchip = irqchip; e.u.irqchip.pin = pin; return kvm_add_routing_entry(&e); #else return -ENOSYS; #endif } int kvm_del_routing_entry(struct kvm_irq_routing_entry *entry) { #ifdef KVM_CAP_IRQ_ROUTING KVMState *s = kvm_state; struct kvm_irq_routing_entry *e, *p; int i, gsi, found = 0; gsi = entry->gsi; for (i = 0; i < s->irq_routes->nr; ++i) { e = &s->irq_routes->entries[i]; if (e->type == entry->type && e->gsi == gsi) { switch (e->type) { case KVM_IRQ_ROUTING_IRQCHIP:{ if (e->u.irqchip.irqchip == entry->u.irqchip.irqchip && e->u.irqchip.pin == entry->u.irqchip.pin) { p = &s->irq_routes->entries[--s->irq_routes->nr]; *e = *p; found = 1; } break; } case KVM_IRQ_ROUTING_MSI:{ if (e->u.msi.address_lo == entry->u.msi.address_lo && e->u.msi.address_hi == entry->u.msi.address_hi && e->u.msi.data == entry->u.msi.data) { p = &s->irq_routes->entries[--s->irq_routes->nr]; *e = *p; found = 1; } break; } default: break; } if (found) { /* If there are no other users of this GSI * mark it available in the bitmap */ for (i = 0; i < s->irq_routes->nr; i++) { e = &s->irq_routes->entries[i]; if (e->gsi == gsi) break; } if (i == s->irq_routes->nr) { clear_gsi(s, gsi); } return 0; } } } return -ESRCH; #else return -ENOSYS; #endif } int kvm_update_routing_entry(struct kvm_irq_routing_entry *entry, struct kvm_irq_routing_entry *newentry) { #ifdef KVM_CAP_IRQ_ROUTING KVMState *s = kvm_state; struct kvm_irq_routing_entry *e; int i; if (entry->gsi != newentry->gsi || entry->type != newentry->type) { return -EINVAL; } for (i = 0; i < s->irq_routes->nr; ++i) { e = &s->irq_routes->entries[i]; if (e->type != entry->type || e->gsi != entry->gsi) { continue; } switch (e->type) { case KVM_IRQ_ROUTING_IRQCHIP: if (e->u.irqchip.irqchip == entry->u.irqchip.irqchip && e->u.irqchip.pin == entry->u.irqchip.pin) { memcpy(&e->u.irqchip, &newentry->u.irqchip, sizeof e->u.irqchip); return 0; } break; case KVM_IRQ_ROUTING_MSI: if (e->u.msi.address_lo == entry->u.msi.address_lo && e->u.msi.address_hi == entry->u.msi.address_hi && e->u.msi.data == entry->u.msi.data) { memcpy(&e->u.msi, &newentry->u.msi, sizeof e->u.msi); return 0; } break; default: break; } } return -ESRCH; #else return -ENOSYS; #endif } int kvm_del_irq_route(int gsi, int irqchip, int pin) { #ifdef KVM_CAP_IRQ_ROUTING struct kvm_irq_routing_entry e; e.gsi = gsi; e.type = KVM_IRQ_ROUTING_IRQCHIP; e.flags = 0; e.u.irqchip.irqchip = irqchip; e.u.irqchip.pin = pin; return kvm_del_routing_entry(&e); #else return -ENOSYS; #endif } int kvm_commit_irq_routes(void) { #ifdef KVM_CAP_IRQ_ROUTING KVMState *s = kvm_state; s->irq_routes->flags = 0; return kvm_vm_ioctl(s, KVM_SET_GSI_ROUTING, s->irq_routes); #else return -ENOSYS; #endif } int kvm_get_irq_route_gsi(void) { KVMState *s = kvm_state; int i, bit; uint32_t *buf = s->used_gsi_bitmap; /* Return the lowest unused GSI in the bitmap */ for (i = 0; i < s->max_gsi / 32; i++) { bit = ffs(~buf[i]); if (!bit) { continue; } return bit - 1 + i * 32; } return -ENOSPC; } static void kvm_msi_routing_entry(struct kvm_irq_routing_entry *e, KVMMsiMessage *msg) { e->gsi = msg->gsi; e->type = KVM_IRQ_ROUTING_MSI; e->flags = 0; e->u.msi.address_lo = msg->addr_lo; e->u.msi.address_hi = msg->addr_hi; e->u.msi.data = msg->data; } int kvm_msi_message_add(KVMMsiMessage *msg) { struct kvm_irq_routing_entry e; int ret; ret = kvm_get_irq_route_gsi(); if (ret < 0) { return ret; } msg->gsi = ret; kvm_msi_routing_entry(&e, msg); return kvm_add_routing_entry(&e); } int kvm_msi_message_del(KVMMsiMessage *msg) { struct kvm_irq_routing_entry e; kvm_msi_routing_entry(&e, msg); return kvm_del_routing_entry(&e); } int kvm_msi_message_update(KVMMsiMessage *old, KVMMsiMessage *new) { struct kvm_irq_routing_entry e1, e2; int ret; new->gsi = old->gsi; if (memcmp(old, new, sizeof(KVMMsiMessage)) == 0) { return 0; } kvm_msi_routing_entry(&e1, old); kvm_msi_routing_entry(&e2, new); ret = kvm_update_routing_entry(&e1, &e2); if (ret < 0) { return ret; } return 1; } #ifdef KVM_CAP_DEVICE_MSIX int kvm_assign_set_msix_nr(KVMState *s, struct kvm_assigned_msix_nr *msix_nr) { return kvm_vm_ioctl(s, KVM_ASSIGN_SET_MSIX_NR, msix_nr); } int kvm_assign_set_msix_entry(KVMState *s, struct kvm_assigned_msix_entry *entry) { return kvm_vm_ioctl(s, KVM_ASSIGN_SET_MSIX_ENTRY, entry); } #endif #ifdef TARGET_I386 void kvm_hpet_disable_kpit(void) { struct kvm_pit_state2 ps2; kvm_get_pit2(kvm_state, &ps2); ps2.flags |= KVM_PIT_FLAGS_HPET_LEGACY; kvm_set_pit2(kvm_state, &ps2); } void kvm_hpet_enable_kpit(void) { struct kvm_pit_state2 ps2; kvm_get_pit2(kvm_state, &ps2); ps2.flags &= ~KVM_PIT_FLAGS_HPET_LEGACY; kvm_set_pit2(kvm_state, &ps2); } #endif #if !defined(TARGET_I386) int kvm_arch_init_irq_routing(void) { return 0; } #endif #ifdef CONFIG_KVM_DEVICE_ASSIGNMENT typedef struct KVMIOPortRegion { unsigned long start; unsigned long size; int status; QLIST_ENTRY(KVMIOPortRegion) entry; } KVMIOPortRegion; static QLIST_HEAD(, KVMIOPortRegion) ioport_regions; static void do_set_ioport_access(void *data) { KVMIOPortRegion *region = data; bool enable = region->status > 0; int r; r = kvm_arch_set_ioport_access(region->start, region->size, enable); if (r < 0) { region->status = r; } else { region->status = 1; } } int kvm_add_ioport_region(unsigned long start, unsigned long size) { KVMIOPortRegion *region = qemu_mallocz(sizeof(KVMIOPortRegion)); CPUState *env; int r = 0; region->start = start; region->size = size; region->status = 1; QLIST_INSERT_HEAD(&ioport_regions, region, entry); if (qemu_system_is_ready()) { for (env = first_cpu; env != NULL; env = env->next_cpu) { run_on_cpu(env, do_set_ioport_access, region); if (region->status < 0) { r = region->status; kvm_remove_ioport_region(start, size); break; } } } return r; } int kvm_remove_ioport_region(unsigned long start, unsigned long size) { KVMIOPortRegion *region, *tmp; CPUState *env; int r = -ENOENT; QLIST_FOREACH_SAFE(region, &ioport_regions, entry, tmp) { if (region->start == start && region->size == size) { region->status = 0; } if (qemu_system_is_ready()) { for (env = first_cpu; env != NULL; env = env->next_cpu) { run_on_cpu(env, do_set_ioport_access, region); } } QLIST_REMOVE(region, entry); qemu_free(region); r = 0; } return r; } #endif /* CONFIG_KVM_DEVICE_ASSIGNMENT */ int kvm_update_ioport_access(CPUState *env) { #ifdef CONFIG_KVM_DEVICE_ASSIGNMENT KVMIOPortRegion *region; int r; assert(qemu_cpu_is_self(env)); QLIST_FOREACH(region, &ioport_regions, entry) { bool enable = region->status > 0; r = kvm_arch_set_ioport_access(region->start, region->size, enable); if (r < 0) { return r; } } #endif /* CONFIG_KVM_DEVICE_ASSIGNMENT */ return 0; }