| /* |
| * 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 <assert.h> |
| #include <string.h> |
| #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 |
| |
| #define ALIGN(x, y) (((x)+(y)-1) & ~((y)-1)) |
| |
| #ifdef KVM_CAP_IRQ_ROUTING |
| 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); |
| } |
| } |
| #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); |
| } |
| |
| int kvm_device_intx_set_mask(KVMState *s, uint32_t dev_id, bool masked) |
| { |
| struct kvm_assigned_pci_dev assigned_dev; |
| |
| assigned_dev.assigned_dev_id = dev_id; |
| assigned_dev.flags = masked ? KVM_DEV_ASSIGN_MASK_INTX : 0; |
| return kvm_vm_ioctl(s, KVM_ASSIGN_SET_INTX_MASK, &assigned_dev); |
| } |
| |
| #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_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_get_irq_route_gsi(void) |
| { |
| #ifdef KVM_CAP_IRQ_ROUTING |
| 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; |
| #else |
| return -ENOSYS; |
| #endif |
| } |
| |
| #ifdef KVM_CAP_IRQ_ROUTING |
| 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; |
| } |
| #endif |
| |
| int kvm_msi_message_add(KVMMsiMessage *msg) |
| { |
| #ifdef KVM_CAP_IRQ_ROUTING |
| 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); |
| kvm_add_routing_entry(kvm_state, &e); |
| return 0; |
| #else |
| return -ENOSYS; |
| #endif |
| } |
| |
| int kvm_msi_message_del(KVMMsiMessage *msg) |
| { |
| #ifdef KVM_CAP_IRQ_ROUTING |
| struct kvm_irq_routing_entry e; |
| |
| kvm_msi_routing_entry(&e, msg); |
| return kvm_del_routing_entry(&e); |
| #else |
| return -ENOSYS; |
| #endif |
| } |
| |
| int kvm_msi_message_update(KVMMsiMessage *old, KVMMsiMessage *new) |
| { |
| #ifdef KVM_CAP_IRQ_ROUTING |
| 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; |
| #else |
| return -ENOSYS; |
| #endif |
| } |
| |
| |
| #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 |
| |
| #if !defined(TARGET_I386) |
| void kvm_arch_init_irq_routing(KVMState *s) |
| { |
| } |
| #endif |