blob: 80cc0770c49f18e8e0449476ba4efd2a5c0c2dfc [file] [log] [blame]
/*
* 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
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;
}