diff options
Diffstat (limited to 'hw/kvm')
-rw-r--r-- | hw/kvm/Makefile.objs | 1 | ||||
-rw-r--r-- | hw/kvm/apic.c | 209 | ||||
-rw-r--r-- | hw/kvm/arm_gic.c | 167 | ||||
-rw-r--r-- | hw/kvm/clock.c | 143 | ||||
-rw-r--r-- | hw/kvm/clock.h | 24 | ||||
-rw-r--r-- | hw/kvm/i8254.c | 317 | ||||
-rw-r--r-- | hw/kvm/i8259.c | 138 | ||||
-rw-r--r-- | hw/kvm/ioapic.c | 165 | ||||
-rw-r--r-- | hw/kvm/pci-assign.c | 1924 |
9 files changed, 0 insertions, 3088 deletions
diff --git a/hw/kvm/Makefile.objs b/hw/kvm/Makefile.objs deleted file mode 100644 index f620d7ff85..0000000000 --- a/hw/kvm/Makefile.objs +++ /dev/null @@ -1 +0,0 @@ -obj-$(CONFIG_KVM) += clock.o apic.o i8259.o ioapic.o i8254.o pci-assign.o diff --git a/hw/kvm/apic.c b/hw/kvm/apic.c deleted file mode 100644 index d994ea7c97..0000000000 --- a/hw/kvm/apic.c +++ /dev/null @@ -1,209 +0,0 @@ -/* - * KVM in-kernel APIC support - * - * Copyright (c) 2011 Siemens AG - * - * Authors: - * Jan Kiszka <jan.kiszka@siemens.com> - * - * This work is licensed under the terms of the GNU GPL version 2. - * See the COPYING file in the top-level directory. - */ -#include "hw/apic_internal.h" -#include "hw/pci/msi.h" -#include "sysemu/kvm.h" - -static inline void kvm_apic_set_reg(struct kvm_lapic_state *kapic, - int reg_id, uint32_t val) -{ - *((uint32_t *)(kapic->regs + (reg_id << 4))) = val; -} - -static inline uint32_t kvm_apic_get_reg(struct kvm_lapic_state *kapic, - int reg_id) -{ - return *((uint32_t *)(kapic->regs + (reg_id << 4))); -} - -void kvm_put_apic_state(DeviceState *d, struct kvm_lapic_state *kapic) -{ - APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d); - int i; - - memset(kapic, 0, sizeof(*kapic)); - kvm_apic_set_reg(kapic, 0x2, s->id << 24); - kvm_apic_set_reg(kapic, 0x8, s->tpr); - kvm_apic_set_reg(kapic, 0xd, s->log_dest << 24); - kvm_apic_set_reg(kapic, 0xe, s->dest_mode << 28 | 0x0fffffff); - kvm_apic_set_reg(kapic, 0xf, s->spurious_vec); - for (i = 0; i < 8; i++) { - kvm_apic_set_reg(kapic, 0x10 + i, s->isr[i]); - kvm_apic_set_reg(kapic, 0x18 + i, s->tmr[i]); - kvm_apic_set_reg(kapic, 0x20 + i, s->irr[i]); - } - kvm_apic_set_reg(kapic, 0x28, s->esr); - kvm_apic_set_reg(kapic, 0x30, s->icr[0]); - kvm_apic_set_reg(kapic, 0x31, s->icr[1]); - for (i = 0; i < APIC_LVT_NB; i++) { - kvm_apic_set_reg(kapic, 0x32 + i, s->lvt[i]); - } - kvm_apic_set_reg(kapic, 0x38, s->initial_count); - kvm_apic_set_reg(kapic, 0x3e, s->divide_conf); -} - -void kvm_get_apic_state(DeviceState *d, struct kvm_lapic_state *kapic) -{ - APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d); - int i, v; - - s->id = kvm_apic_get_reg(kapic, 0x2) >> 24; - s->tpr = kvm_apic_get_reg(kapic, 0x8); - s->arb_id = kvm_apic_get_reg(kapic, 0x9); - s->log_dest = kvm_apic_get_reg(kapic, 0xd) >> 24; - s->dest_mode = kvm_apic_get_reg(kapic, 0xe) >> 28; - s->spurious_vec = kvm_apic_get_reg(kapic, 0xf); - for (i = 0; i < 8; i++) { - s->isr[i] = kvm_apic_get_reg(kapic, 0x10 + i); - s->tmr[i] = kvm_apic_get_reg(kapic, 0x18 + i); - s->irr[i] = kvm_apic_get_reg(kapic, 0x20 + i); - } - s->esr = kvm_apic_get_reg(kapic, 0x28); - s->icr[0] = kvm_apic_get_reg(kapic, 0x30); - s->icr[1] = kvm_apic_get_reg(kapic, 0x31); - for (i = 0; i < APIC_LVT_NB; i++) { - s->lvt[i] = kvm_apic_get_reg(kapic, 0x32 + i); - } - s->initial_count = kvm_apic_get_reg(kapic, 0x38); - s->divide_conf = kvm_apic_get_reg(kapic, 0x3e); - - v = (s->divide_conf & 3) | ((s->divide_conf >> 1) & 4); - s->count_shift = (v + 1) & 7; - - s->initial_count_load_time = qemu_get_clock_ns(vm_clock); - apic_next_timer(s, s->initial_count_load_time); -} - -static void kvm_apic_set_base(APICCommonState *s, uint64_t val) -{ - s->apicbase = val; -} - -static void kvm_apic_set_tpr(APICCommonState *s, uint8_t val) -{ - s->tpr = (val & 0x0f) << 4; -} - -static uint8_t kvm_apic_get_tpr(APICCommonState *s) -{ - return s->tpr >> 4; -} - -static void kvm_apic_enable_tpr_reporting(APICCommonState *s, bool enable) -{ - struct kvm_tpr_access_ctl ctl = { - .enabled = enable - }; - - kvm_vcpu_ioctl(CPU(s->cpu), KVM_TPR_ACCESS_REPORTING, &ctl); -} - -static void kvm_apic_vapic_base_update(APICCommonState *s) -{ - struct kvm_vapic_addr vapid_addr = { - .vapic_addr = s->vapic_paddr, - }; - int ret; - - ret = kvm_vcpu_ioctl(CPU(s->cpu), KVM_SET_VAPIC_ADDR, &vapid_addr); - if (ret < 0) { - fprintf(stderr, "KVM: setting VAPIC address failed (%s)\n", - strerror(-ret)); - abort(); - } -} - -static void do_inject_external_nmi(void *data) -{ - APICCommonState *s = data; - CPUState *cpu = CPU(s->cpu); - uint32_t lvt; - int ret; - - cpu_synchronize_state(&s->cpu->env); - - lvt = s->lvt[APIC_LVT_LINT1]; - if (!(lvt & APIC_LVT_MASKED) && ((lvt >> 8) & 7) == APIC_DM_NMI) { - ret = kvm_vcpu_ioctl(cpu, KVM_NMI); - if (ret < 0) { - fprintf(stderr, "KVM: injection failed, NMI lost (%s)\n", - strerror(-ret)); - } - } -} - -static void kvm_apic_external_nmi(APICCommonState *s) -{ - run_on_cpu(CPU(s->cpu), do_inject_external_nmi, s); -} - -static uint64_t kvm_apic_mem_read(void *opaque, hwaddr addr, - unsigned size) -{ - return ~(uint64_t)0; -} - -static void kvm_apic_mem_write(void *opaque, hwaddr addr, - uint64_t data, unsigned size) -{ - MSIMessage msg = { .address = addr, .data = data }; - int ret; - - ret = kvm_irqchip_send_msi(kvm_state, msg); - if (ret < 0) { - fprintf(stderr, "KVM: injection failed, MSI lost (%s)\n", - strerror(-ret)); - } -} - -static const MemoryRegionOps kvm_apic_io_ops = { - .read = kvm_apic_mem_read, - .write = kvm_apic_mem_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void kvm_apic_init(APICCommonState *s) -{ - memory_region_init_io(&s->io_memory, &kvm_apic_io_ops, s, "kvm-apic-msi", - MSI_SPACE_SIZE); - - if (kvm_has_gsi_routing()) { - msi_supported = true; - } -} - -static void kvm_apic_class_init(ObjectClass *klass, void *data) -{ - APICCommonClass *k = APIC_COMMON_CLASS(klass); - - k->init = kvm_apic_init; - k->set_base = kvm_apic_set_base; - k->set_tpr = kvm_apic_set_tpr; - k->get_tpr = kvm_apic_get_tpr; - k->enable_tpr_reporting = kvm_apic_enable_tpr_reporting; - k->vapic_base_update = kvm_apic_vapic_base_update; - k->external_nmi = kvm_apic_external_nmi; -} - -static const TypeInfo kvm_apic_info = { - .name = "kvm-apic", - .parent = TYPE_APIC_COMMON, - .instance_size = sizeof(APICCommonState), - .class_init = kvm_apic_class_init, -}; - -static void kvm_apic_register_types(void) -{ - type_register_static(&kvm_apic_info); -} - -type_init(kvm_apic_register_types) diff --git a/hw/kvm/arm_gic.c b/hw/kvm/arm_gic.c deleted file mode 100644 index 22b40b4f84..0000000000 --- a/hw/kvm/arm_gic.c +++ /dev/null @@ -1,167 +0,0 @@ -/* - * ARM Generic Interrupt Controller using KVM in-kernel support - * - * Copyright (c) 2012 Linaro Limited - * Written by Peter Maydell - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see <http://www.gnu.org/licenses/>. - */ - -#include "hw/sysbus.h" -#include "sysemu/kvm.h" -#include "kvm_arm.h" -#include "hw/arm_gic_internal.h" - -#define TYPE_KVM_ARM_GIC "kvm-arm-gic" -#define KVM_ARM_GIC(obj) \ - OBJECT_CHECK(GICState, (obj), TYPE_KVM_ARM_GIC) -#define KVM_ARM_GIC_CLASS(klass) \ - OBJECT_CLASS_CHECK(KVMARMGICClass, (klass), TYPE_KVM_ARM_GIC) -#define KVM_ARM_GIC_GET_CLASS(obj) \ - OBJECT_GET_CLASS(KVMARMGICClass, (obj), TYPE_KVM_ARM_GIC) - -typedef struct KVMARMGICClass { - ARMGICCommonClass parent_class; - DeviceRealize parent_realize; - void (*parent_reset)(DeviceState *dev); -} KVMARMGICClass; - -static void kvm_arm_gic_set_irq(void *opaque, int irq, int level) -{ - /* Meaning of the 'irq' parameter: - * [0..N-1] : external interrupts - * [N..N+31] : PPI (internal) interrupts for CPU 0 - * [N+32..N+63] : PPI (internal interrupts for CPU 1 - * ... - * Convert this to the kernel's desired encoding, which - * has separate fields in the irq number for type, - * CPU number and interrupt number. - */ - GICState *s = (GICState *)opaque; - int kvm_irq, irqtype, cpu; - - if (irq < (s->num_irq - GIC_INTERNAL)) { - /* External interrupt. The kernel numbers these like the GIC - * hardware, with external interrupt IDs starting after the - * internal ones. - */ - irqtype = KVM_ARM_IRQ_TYPE_SPI; - cpu = 0; - irq += GIC_INTERNAL; - } else { - /* Internal interrupt: decode into (cpu, interrupt id) */ - irqtype = KVM_ARM_IRQ_TYPE_PPI; - irq -= (s->num_irq - GIC_INTERNAL); - cpu = irq / GIC_INTERNAL; - irq %= GIC_INTERNAL; - } - kvm_irq = (irqtype << KVM_ARM_IRQ_TYPE_SHIFT) - | (cpu << KVM_ARM_IRQ_VCPU_SHIFT) | irq; - - kvm_set_irq(kvm_state, kvm_irq, !!level); -} - -static void kvm_arm_gic_put(GICState *s) -{ - /* TODO: there isn't currently a kernel interface to set the GIC state */ -} - -static void kvm_arm_gic_get(GICState *s) -{ - /* TODO: there isn't currently a kernel interface to get the GIC state */ -} - -static void kvm_arm_gic_reset(DeviceState *dev) -{ - GICState *s = ARM_GIC_COMMON(dev); - KVMARMGICClass *kgc = KVM_ARM_GIC_GET_CLASS(s); - - kgc->parent_reset(dev); - kvm_arm_gic_put(s); -} - -static void kvm_arm_gic_realize(DeviceState *dev, Error **errp) -{ - int i; - GICState *s = KVM_ARM_GIC(dev); - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - KVMARMGICClass *kgc = KVM_ARM_GIC_GET_CLASS(s); - - kgc->parent_realize(dev, errp); - if (error_is_set(errp)) { - return; - } - - i = s->num_irq - GIC_INTERNAL; - /* For the GIC, also expose incoming GPIO lines for PPIs for each CPU. - * GPIO array layout is thus: - * [0..N-1] SPIs - * [N..N+31] PPIs for CPU 0 - * [N+32..N+63] PPIs for CPU 1 - * ... - */ - i += (GIC_INTERNAL * s->num_cpu); - qdev_init_gpio_in(dev, kvm_arm_gic_set_irq, i); - /* We never use our outbound IRQ lines but provide them so that - * we maintain the same interface as the non-KVM GIC. - */ - for (i = 0; i < s->num_cpu; i++) { - sysbus_init_irq(sbd, &s->parent_irq[i]); - } - /* Distributor */ - memory_region_init_reservation(&s->iomem, "kvm-gic_dist", 0x1000); - sysbus_init_mmio(sbd, &s->iomem); - kvm_arm_register_device(&s->iomem, - (KVM_ARM_DEVICE_VGIC_V2 << KVM_ARM_DEVICE_ID_SHIFT) - | KVM_VGIC_V2_ADDR_TYPE_DIST); - /* CPU interface for current core. Unlike arm_gic, we don't - * provide the "interface for core #N" memory regions, because - * cores with a VGIC don't have those. - */ - memory_region_init_reservation(&s->cpuiomem[0], "kvm-gic_cpu", 0x1000); - sysbus_init_mmio(sbd, &s->cpuiomem[0]); - kvm_arm_register_device(&s->cpuiomem[0], - (KVM_ARM_DEVICE_VGIC_V2 << KVM_ARM_DEVICE_ID_SHIFT) - | KVM_VGIC_V2_ADDR_TYPE_CPU); -} - -static void kvm_arm_gic_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - ARMGICCommonClass *agcc = ARM_GIC_COMMON_CLASS(klass); - KVMARMGICClass *kgc = KVM_ARM_GIC_CLASS(klass); - - agcc->pre_save = kvm_arm_gic_get; - agcc->post_load = kvm_arm_gic_put; - kgc->parent_realize = dc->realize; - kgc->parent_reset = dc->reset; - dc->realize = kvm_arm_gic_realize; - dc->reset = kvm_arm_gic_reset; - dc->no_user = 1; -} - -static const TypeInfo kvm_arm_gic_info = { - .name = TYPE_KVM_ARM_GIC, - .parent = TYPE_ARM_GIC_COMMON, - .instance_size = sizeof(GICState), - .class_init = kvm_arm_gic_class_init, - .class_size = sizeof(KVMARMGICClass), -}; - -static void kvm_arm_gic_register_types(void) -{ - type_register_static(&kvm_arm_gic_info); -} - -type_init(kvm_arm_gic_register_types) diff --git a/hw/kvm/clock.c b/hw/kvm/clock.c deleted file mode 100644 index fa40e283f7..0000000000 --- a/hw/kvm/clock.c +++ /dev/null @@ -1,143 +0,0 @@ -/* - * QEMU KVM support, paravirtual clock device - * - * Copyright (C) 2011 Siemens AG - * - * Authors: - * Jan Kiszka <jan.kiszka@siemens.com> - * - * This work is licensed under the terms of the GNU GPL version 2. - * See the COPYING file in the top-level directory. - * - * Contributions after 2012-01-13 are licensed under the terms of the - * GNU GPL, version 2 or (at your option) any later version. - */ - -#include "qemu-common.h" -#include "sysemu/sysemu.h" -#include "sysemu/kvm.h" -#include "hw/sysbus.h" -#include "hw/kvm/clock.h" - -#include <linux/kvm.h> -#include <linux/kvm_para.h> - -typedef struct KVMClockState { - SysBusDevice busdev; - uint64_t clock; - bool clock_valid; -} KVMClockState; - -static void kvmclock_pre_save(void *opaque) -{ - KVMClockState *s = opaque; - struct kvm_clock_data data; - int ret; - - if (s->clock_valid) { - return; - } - ret = kvm_vm_ioctl(kvm_state, KVM_GET_CLOCK, &data); - if (ret < 0) { - fprintf(stderr, "KVM_GET_CLOCK failed: %s\n", strerror(ret)); - data.clock = 0; - } - s->clock = data.clock; - /* - * If the VM is stopped, declare the clock state valid to avoid re-reading - * it on next vmsave (which would return a different value). Will be reset - * when the VM is continued. - */ - s->clock_valid = !runstate_is_running(); -} - -static int kvmclock_post_load(void *opaque, int version_id) -{ - KVMClockState *s = opaque; - struct kvm_clock_data data; - - data.clock = s->clock; - data.flags = 0; - return kvm_vm_ioctl(kvm_state, KVM_SET_CLOCK, &data); -} - -static void kvmclock_vm_state_change(void *opaque, int running, - RunState state) -{ - KVMClockState *s = opaque; - CPUArchState *penv = first_cpu; - int cap_clock_ctrl = kvm_check_extension(kvm_state, KVM_CAP_KVMCLOCK_CTRL); - int ret; - - if (running) { - s->clock_valid = false; - - if (!cap_clock_ctrl) { - return; - } - for (penv = first_cpu; penv != NULL; penv = penv->next_cpu) { - ret = kvm_vcpu_ioctl(ENV_GET_CPU(penv), KVM_KVMCLOCK_CTRL, 0); - if (ret) { - if (ret != -EINVAL) { - fprintf(stderr, "%s: %s\n", __func__, strerror(-ret)); - } - return; - } - } - } -} - -static int kvmclock_init(SysBusDevice *dev) -{ - KVMClockState *s = FROM_SYSBUS(KVMClockState, dev); - - qemu_add_vm_change_state_handler(kvmclock_vm_state_change, s); - return 0; -} - -static const VMStateDescription kvmclock_vmsd = { - .name = "kvmclock", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .pre_save = kvmclock_pre_save, - .post_load = kvmclock_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT64(clock, KVMClockState), - VMSTATE_END_OF_LIST() - } -}; - -static void kvmclock_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = kvmclock_init; - dc->no_user = 1; - dc->vmsd = &kvmclock_vmsd; -} - -static const TypeInfo kvmclock_info = { - .name = "kvmclock", - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(KVMClockState), - .class_init = kvmclock_class_init, -}; - -/* Note: Must be called after VCPU initialization. */ -void kvmclock_create(void) -{ - if (kvm_enabled() && - first_cpu->cpuid_kvm_features & ((1ULL << KVM_FEATURE_CLOCKSOURCE) | - (1ULL << KVM_FEATURE_CLOCKSOURCE2))) { - sysbus_create_simple("kvmclock", -1, NULL); - } -} - -static void kvmclock_register_types(void) -{ - type_register_static(&kvmclock_info); -} - -type_init(kvmclock_register_types) diff --git a/hw/kvm/clock.h b/hw/kvm/clock.h deleted file mode 100644 index 252ea13461..0000000000 --- a/hw/kvm/clock.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * QEMU KVM support, paravirtual clock device - * - * Copyright (C) 2011 Siemens AG - * - * Authors: - * Jan Kiszka <jan.kiszka@siemens.com> - * - * This work is licensed under the terms of the GNU GPL version 2. - * See the COPYING file in the top-level directory. - * - */ - -#ifdef CONFIG_KVM - -void kvmclock_create(void); - -#else /* CONFIG_KVM */ - -static inline void kvmclock_create(void) -{ -} - -#endif /* !CONFIG_KVM */ diff --git a/hw/kvm/i8254.c b/hw/kvm/i8254.c deleted file mode 100644 index 04ad649b0e..0000000000 --- a/hw/kvm/i8254.c +++ /dev/null @@ -1,317 +0,0 @@ -/* - * KVM in-kernel PIT (i8254) support - * - * Copyright (c) 2003-2004 Fabrice Bellard - * Copyright (c) 2012 Jan Kiszka, Siemens AG - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#include "qemu/timer.h" -#include "sysemu/sysemu.h" -#include "hw/i8254.h" -#include "hw/i8254_internal.h" -#include "sysemu/kvm.h" - -#define KVM_PIT_REINJECT_BIT 0 - -#define CALIBRATION_ROUNDS 3 - -typedef struct KVMPITState { - PITCommonState pit; - LostTickPolicy lost_tick_policy; - bool vm_stopped; - int64_t kernel_clock_offset; -} KVMPITState; - -static int64_t abs64(int64_t v) -{ - return v < 0 ? -v : v; -} - -static void kvm_pit_update_clock_offset(KVMPITState *s) -{ - int64_t offset, clock_offset; - struct timespec ts; - int i; - - /* - * Measure the delta between CLOCK_MONOTONIC, the base used for - * kvm_pit_channel_state::count_load_time, and vm_clock. Take the - * minimum of several samples to filter out scheduling noise. - */ - clock_offset = INT64_MAX; - for (i = 0; i < CALIBRATION_ROUNDS; i++) { - offset = qemu_get_clock_ns(vm_clock); - clock_gettime(CLOCK_MONOTONIC, &ts); - offset -= ts.tv_nsec; - offset -= (int64_t)ts.tv_sec * 1000000000; - if (abs64(offset) < abs64(clock_offset)) { - clock_offset = offset; - } - } - s->kernel_clock_offset = clock_offset; -} - -static void kvm_pit_get(PITCommonState *pit) -{ - KVMPITState *s = DO_UPCAST(KVMPITState, pit, pit); - struct kvm_pit_state2 kpit; - struct kvm_pit_channel_state *kchan; - struct PITChannelState *sc; - int i, ret; - - /* No need to re-read the state if VM is stopped. */ - if (s->vm_stopped) { - return; - } - - if (kvm_has_pit_state2()) { - ret = kvm_vm_ioctl(kvm_state, KVM_GET_PIT2, &kpit); - if (ret < 0) { - fprintf(stderr, "KVM_GET_PIT2 failed: %s\n", strerror(ret)); - abort(); - } - pit->channels[0].irq_disabled = kpit.flags & KVM_PIT_FLAGS_HPET_LEGACY; - } else { - /* - * kvm_pit_state2 is superset of kvm_pit_state struct, - * so we can use it for KVM_GET_PIT as well. - */ - ret = kvm_vm_ioctl(kvm_state, KVM_GET_PIT, &kpit); - if (ret < 0) { - fprintf(stderr, "KVM_GET_PIT failed: %s\n", strerror(ret)); - abort(); - } - } - for (i = 0; i < 3; i++) { - kchan = &kpit.channels[i]; - sc = &pit->channels[i]; - sc->count = kchan->count; - sc->latched_count = kchan->latched_count; - sc->count_latched = kchan->count_latched; - sc->status_latched = kchan->status_latched; - sc->status = kchan->status; - sc->read_state = kchan->read_state; - sc->write_state = kchan->write_state; - sc->write_latch = kchan->write_latch; - sc->rw_mode = kchan->rw_mode; - sc->mode = kchan->mode; - sc->bcd = kchan->bcd; - sc->gate = kchan->gate; - sc->count_load_time = kchan->count_load_time + s->kernel_clock_offset; - } - - sc = &pit->channels[0]; - sc->next_transition_time = - pit_get_next_transition_time(sc, sc->count_load_time); -} - -static void kvm_pit_put(PITCommonState *pit) -{ - KVMPITState *s = DO_UPCAST(KVMPITState, pit, pit); - struct kvm_pit_state2 kpit; - struct kvm_pit_channel_state *kchan; - struct PITChannelState *sc; - int i, ret; - - /* The offset keeps changing as long as the VM is stopped. */ - if (s->vm_stopped) { - kvm_pit_update_clock_offset(s); - } - - kpit.flags = pit->channels[0].irq_disabled ? KVM_PIT_FLAGS_HPET_LEGACY : 0; - for (i = 0; i < 3; i++) { - kchan = &kpit.channels[i]; - sc = &pit->channels[i]; - kchan->count = sc->count; - kchan->latched_count = sc->latched_count; - kchan->count_latched = sc->count_latched; - kchan->status_latched = sc->status_latched; - kchan->status = sc->status; - kchan->read_state = sc->read_state; - kchan->write_state = sc->write_state; - kchan->write_latch = sc->write_latch; - kchan->rw_mode = sc->rw_mode; - kchan->mode = sc->mode; - kchan->bcd = sc->bcd; - kchan->gate = sc->gate; - kchan->count_load_time = sc->count_load_time - s->kernel_clock_offset; - } - - ret = kvm_vm_ioctl(kvm_state, - kvm_has_pit_state2() ? KVM_SET_PIT2 : KVM_SET_PIT, - &kpit); - if (ret < 0) { - fprintf(stderr, "%s failed: %s\n", - kvm_has_pit_state2() ? "KVM_SET_PIT2" : "KVM_SET_PIT", - strerror(ret)); - abort(); - } -} - -static void kvm_pit_set_gate(PITCommonState *s, PITChannelState *sc, int val) -{ - kvm_pit_get(s); - - switch (sc->mode) { - default: - case 0: - case 4: - /* XXX: just disable/enable counting */ - break; - case 1: - case 2: - case 3: - case 5: - if (sc->gate < val) { - /* restart counting on rising edge */ - sc->count_load_time = qemu_get_clock_ns(vm_clock); - } - break; - } - sc->gate = val; - - kvm_pit_put(s); -} - -static void kvm_pit_get_channel_info(PITCommonState *s, PITChannelState *sc, - PITChannelInfo *info) -{ - kvm_pit_get(s); - - pit_get_channel_info_common(s, sc, info); -} - -static void kvm_pit_reset(DeviceState *dev) -{ - PITCommonState *s = DO_UPCAST(PITCommonState, dev.qdev, dev); - - pit_reset_common(s); - - kvm_pit_put(s); -} - -static void kvm_pit_irq_control(void *opaque, int n, int enable) -{ - PITCommonState *pit = opaque; - PITChannelState *s = &pit->channels[0]; - - kvm_pit_get(pit); - - s->irq_disabled = !enable; - - kvm_pit_put(pit); -} - -static void kvm_pit_vm_state_change(void *opaque, int running, - RunState state) -{ - KVMPITState *s = opaque; - - if (running) { - kvm_pit_update_clock_offset(s); - s->vm_stopped = false; - } else { - kvm_pit_update_clock_offset(s); - kvm_pit_get(&s->pit); - s->vm_stopped = true; - } -} - -static int kvm_pit_initfn(PITCommonState *pit) -{ - KVMPITState *s = DO_UPCAST(KVMPITState, pit, pit); - struct kvm_pit_config config = { - .flags = 0, - }; - int ret; - - if (kvm_check_extension(kvm_state, KVM_CAP_PIT2)) { - ret = kvm_vm_ioctl(kvm_state, KVM_CREATE_PIT2, &config); - } else { - ret = kvm_vm_ioctl(kvm_state, KVM_CREATE_PIT); - } - if (ret < 0) { - fprintf(stderr, "Create kernel PIC irqchip failed: %s\n", - strerror(ret)); - return ret; - } - switch (s->lost_tick_policy) { - case LOST_TICK_DELAY: - break; /* enabled by default */ - case LOST_TICK_DISCARD: - if (kvm_check_extension(kvm_state, KVM_CAP_REINJECT_CONTROL)) { - struct kvm_reinject_control control = { .pit_reinject = 0 }; - - ret = kvm_vm_ioctl(kvm_state, KVM_REINJECT_CONTROL, &control); - if (ret < 0) { - fprintf(stderr, - "Can't disable in-kernel PIT reinjection: %s\n", - strerror(ret)); - return ret; - } - } - break; - default: - return -EINVAL; - } - - memory_region_init_reservation(&pit->ioports, "kvm-pit", 4); - - qdev_init_gpio_in(&pit->dev.qdev, kvm_pit_irq_control, 1); - - qemu_add_vm_change_state_handler(kvm_pit_vm_state_change, s); - - return 0; -} - -static Property kvm_pit_properties[] = { - DEFINE_PROP_HEX32("iobase", KVMPITState, pit.iobase, -1), - DEFINE_PROP_LOSTTICKPOLICY("lost_tick_policy", KVMPITState, - lost_tick_policy, LOST_TICK_DELAY), - DEFINE_PROP_END_OF_LIST(), -}; - -static void kvm_pit_class_init(ObjectClass *klass, void *data) -{ - PITCommonClass *k = PIT_COMMON_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - k->init = kvm_pit_initfn; - k->set_channel_gate = kvm_pit_set_gate; - k->get_channel_info = kvm_pit_get_channel_info; - k->pre_save = kvm_pit_get; - k->post_load = kvm_pit_put; - dc->reset = kvm_pit_reset; - dc->props = kvm_pit_properties; -} - -static const TypeInfo kvm_pit_info = { - .name = "kvm-pit", - .parent = TYPE_PIT_COMMON, - .instance_size = sizeof(KVMPITState), - .class_init = kvm_pit_class_init, -}; - -static void kvm_pit_register(void) -{ - type_register_static(&kvm_pit_info); -} - -type_init(kvm_pit_register) diff --git a/hw/kvm/i8259.c b/hw/kvm/i8259.c deleted file mode 100644 index 5ae8b6819b..0000000000 --- a/hw/kvm/i8259.c +++ /dev/null @@ -1,138 +0,0 @@ -/* - * KVM in-kernel PIC (i8259) support - * - * Copyright (c) 2011 Siemens AG - * - * Authors: - * Jan Kiszka <jan.kiszka@siemens.com> - * - * This work is licensed under the terms of the GNU GPL version 2. - * See the COPYING file in the top-level directory. - */ -#include "hw/i8259_internal.h" -#include "hw/apic_internal.h" -#include "sysemu/kvm.h" - -static void kvm_pic_get(PICCommonState *s) -{ - struct kvm_irqchip chip; - struct kvm_pic_state *kpic; - int ret; - - chip.chip_id = s->master ? KVM_IRQCHIP_PIC_MASTER : KVM_IRQCHIP_PIC_SLAVE; - ret = kvm_vm_ioctl(kvm_state, KVM_GET_IRQCHIP, &chip); - if (ret < 0) { - fprintf(stderr, "KVM_GET_IRQCHIP failed: %s\n", strerror(ret)); - abort(); - } - - kpic = &chip.chip.pic; - - s->last_irr = kpic->last_irr; - s->irr = kpic->irr; - s->imr = kpic->imr; - s->isr = kpic->isr; - s->priority_add = kpic->priority_add; - s->irq_base = kpic->irq_base; - s->read_reg_select = kpic->read_reg_select; - s->poll = kpic->poll; - s->special_mask = kpic->special_mask; - s->init_state = kpic->init_state; - s->auto_eoi = kpic->auto_eoi; - s->rotate_on_auto_eoi = kpic->rotate_on_auto_eoi; - s->special_fully_nested_mode = kpic->special_fully_nested_mode; - s->init4 = kpic->init4; - s->elcr = kpic->elcr; - s->elcr_mask = kpic->elcr_mask; -} - -static void kvm_pic_put(PICCommonState *s) -{ - struct kvm_irqchip chip; - struct kvm_pic_state *kpic; - int ret; - - chip.chip_id = s->master ? KVM_IRQCHIP_PIC_MASTER : KVM_IRQCHIP_PIC_SLAVE; - - kpic = &chip.chip.pic; - - kpic->last_irr = s->last_irr; - kpic->irr = s->irr; - kpic->imr = s->imr; - kpic->isr = s->isr; - kpic->priority_add = s->priority_add; - kpic->irq_base = s->irq_base; - kpic->read_reg_select = s->read_reg_select; - kpic->poll = s->poll; - kpic->special_mask = s->special_mask; - kpic->init_state = s->init_state; - kpic->auto_eoi = s->auto_eoi; - kpic->rotate_on_auto_eoi = s->rotate_on_auto_eoi; - kpic->special_fully_nested_mode = s->special_fully_nested_mode; - kpic->init4 = s->init4; - kpic->elcr = s->elcr; - kpic->elcr_mask = s->elcr_mask; - - ret = kvm_vm_ioctl(kvm_state, KVM_SET_IRQCHIP, &chip); - if (ret < 0) { - fprintf(stderr, "KVM_GET_IRQCHIP failed: %s\n", strerror(ret)); - abort(); - } -} - -static void kvm_pic_reset(DeviceState *dev) -{ - PICCommonState *s = DO_UPCAST(PICCommonState, dev.qdev, dev); - - s->elcr = 0; - pic_reset_common(s); - - kvm_pic_put(s); -} - -static void kvm_pic_set_irq(void *opaque, int irq, int level) -{ - int delivered; - - delivered = kvm_set_irq(kvm_state, irq, level); - apic_report_irq_delivered(delivered); -} - -static void kvm_pic_init(PICCommonState *s) -{ - memory_region_init_reservation(&s->base_io, "kvm-pic", 2); - memory_region_init_reservation(&s->elcr_io, "kvm-elcr", 1); -} - -qemu_irq *kvm_i8259_init(ISABus *bus) -{ - i8259_init_chip("kvm-i8259", bus, true); - i8259_init_chip("kvm-i8259", bus, false); - - return qemu_allocate_irqs(kvm_pic_set_irq, NULL, ISA_NUM_IRQS); -} - -static void kvm_i8259_class_init(ObjectClass *klass, void *data) -{ - PICCommonClass *k = PIC_COMMON_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->reset = kvm_pic_reset; - k->init = kvm_pic_init; - k->pre_save = kvm_pic_get; - k->post_load = kvm_pic_put; -} - -static const TypeInfo kvm_i8259_info = { - .name = "kvm-i8259", - .parent = TYPE_PIC_COMMON, - .instance_size = sizeof(PICCommonState), - .class_init = kvm_i8259_class_init, -}; - -static void kvm_pic_register_types(void) -{ - type_register_static(&kvm_i8259_info); -} - -type_init(kvm_pic_register_types) diff --git a/hw/kvm/ioapic.c b/hw/kvm/ioapic.c deleted file mode 100644 index 23877d4259..0000000000 --- a/hw/kvm/ioapic.c +++ /dev/null @@ -1,165 +0,0 @@ -/* - * KVM in-kernel IOPIC support - * - * Copyright (c) 2011 Siemens AG - * - * Authors: - * Jan Kiszka <jan.kiszka@siemens.com> - * - * This work is licensed under the terms of the GNU GPL version 2. - * See the COPYING file in the top-level directory. - */ - -#include "hw/pc.h" -#include "hw/ioapic_internal.h" -#include "hw/apic_internal.h" -#include "sysemu/kvm.h" - -/* PC Utility function */ -void kvm_pc_setup_irq_routing(bool pci_enabled) -{ - KVMState *s = kvm_state; - int i; - - if (kvm_check_extension(s, KVM_CAP_IRQ_ROUTING)) { - for (i = 0; i < 8; ++i) { - if (i == 2) { - continue; - } - kvm_irqchip_add_irq_route(s, i, KVM_IRQCHIP_PIC_MASTER, i); - } - for (i = 8; i < 16; ++i) { - kvm_irqchip_add_irq_route(s, i, KVM_IRQCHIP_PIC_SLAVE, i - 8); - } - if (pci_enabled) { - for (i = 0; i < 24; ++i) { - if (i == 0) { - kvm_irqchip_add_irq_route(s, i, KVM_IRQCHIP_IOAPIC, 2); - } else if (i != 2) { - kvm_irqchip_add_irq_route(s, i, KVM_IRQCHIP_IOAPIC, i); - } - } - } - } -} - -void kvm_pc_gsi_handler(void *opaque, int n, int level) -{ - GSIState *s = opaque; - - if (n < ISA_NUM_IRQS) { - /* Kernel will forward to both PIC and IOAPIC */ - qemu_set_irq(s->i8259_irq[n], level); - } else { - qemu_set_irq(s->ioapic_irq[n], level); - } -} - -typedef struct KVMIOAPICState KVMIOAPICState; - -struct KVMIOAPICState { - IOAPICCommonState ioapic; - uint32_t kvm_gsi_base; -}; - -static void kvm_ioapic_get(IOAPICCommonState *s) -{ - struct kvm_irqchip chip; - struct kvm_ioapic_state *kioapic; - int ret, i; - - chip.chip_id = KVM_IRQCHIP_IOAPIC; - ret = kvm_vm_ioctl(kvm_state, KVM_GET_IRQCHIP, &chip); - if (ret < 0) { - fprintf(stderr, "KVM_GET_IRQCHIP failed: %s\n", strerror(ret)); - abort(); - } - - kioapic = &chip.chip.ioapic; - - s->id = kioapic->id; - s->ioregsel = kioapic->ioregsel; - s->irr = kioapic->irr; - for (i = 0; i < IOAPIC_NUM_PINS; i++) { - s->ioredtbl[i] = kioapic->redirtbl[i].bits; - } -} - -static void kvm_ioapic_put(IOAPICCommonState *s) -{ - struct kvm_irqchip chip; - struct kvm_ioapic_state *kioapic; - int ret, i; - - chip.chip_id = KVM_IRQCHIP_IOAPIC; - kioapic = &chip.chip.ioapic; - - kioapic->id = s->id; - kioapic->ioregsel = s->ioregsel; - kioapic->base_address = s->busdev.mmio[0].addr; - kioapic->irr = s->irr; - for (i = 0; i < IOAPIC_NUM_PINS; i++) { - kioapic->redirtbl[i].bits = s->ioredtbl[i]; - } - - ret = kvm_vm_ioctl(kvm_state, KVM_SET_IRQCHIP, &chip); - if (ret < 0) { - fprintf(stderr, "KVM_GET_IRQCHIP failed: %s\n", strerror(ret)); - abort(); - } -} - -static void kvm_ioapic_reset(DeviceState *dev) -{ - IOAPICCommonState *s = DO_UPCAST(IOAPICCommonState, busdev.qdev, dev); - - ioapic_reset_common(dev); - kvm_ioapic_put(s); -} - -static void kvm_ioapic_set_irq(void *opaque, int irq, int level) -{ - KVMIOAPICState *s = opaque; - int delivered; - - delivered = kvm_set_irq(kvm_state, s->kvm_gsi_base + irq, level); - apic_report_irq_delivered(delivered); -} - -static void kvm_ioapic_init(IOAPICCommonState *s, int instance_no) -{ - memory_region_init_reservation(&s->io_memory, "kvm-ioapic", 0x1000); - - qdev_init_gpio_in(&s->busdev.qdev, kvm_ioapic_set_irq, IOAPIC_NUM_PINS); -} - -static Property kvm_ioapic_properties[] = { - DEFINE_PROP_UINT32("gsi_base", KVMIOAPICState, kvm_gsi_base, 0), - DEFINE_PROP_END_OF_LIST() -}; - -static void kvm_ioapic_class_init(ObjectClass *klass, void *data) -{ - IOAPICCommonClass *k = IOAPIC_COMMON_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - k->init = kvm_ioapic_init; - k->pre_save = kvm_ioapic_get; - k->post_load = kvm_ioapic_put; - dc->reset = kvm_ioapic_reset; - dc->props = kvm_ioapic_properties; -} - -static const TypeInfo kvm_ioapic_info = { - .name = "kvm-ioapic", - .parent = TYPE_IOAPIC_COMMON, - .instance_size = sizeof(KVMIOAPICState), - .class_init = kvm_ioapic_class_init, -}; - -static void kvm_ioapic_register_types(void) -{ - type_register_static(&kvm_ioapic_info); -} - -type_init(kvm_ioapic_register_types) diff --git a/hw/kvm/pci-assign.c b/hw/kvm/pci-assign.c deleted file mode 100644 index da64b5b86f..0000000000 --- a/hw/kvm/pci-assign.c +++ /dev/null @@ -1,1924 +0,0 @@ -/* - * Copyright (c) 2007, Neocleus Corporation. - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - * - * Assign a PCI device from the host to a guest VM. - * - * This implementation uses the classic device assignment interface of KVM - * and is only available on x86 hosts. It is expected to be obsoleted by VFIO - * based device assignment. - * - * Adapted for KVM (qemu-kvm) by Qumranet. QEMU version was based on qemu-kvm - * revision 4144fe9d48. See its repository for the history. - * - * Copyright (c) 2007, Neocleus, Alex Novik (alex@neocleus.com) - * Copyright (c) 2007, Neocleus, Guy Zana (guy@neocleus.com) - * Copyright (C) 2008, Qumranet, Amit Shah (amit.shah@qumranet.com) - * Copyright (C) 2008, Red Hat, Amit Shah (amit.shah@redhat.com) - * Copyright (C) 2008, IBM, Muli Ben-Yehuda (muli@il.ibm.com) - */ -#include <stdio.h> -#include <unistd.h> -#include <sys/io.h> -#include <sys/mman.h> -#include <sys/types.h> -#include <sys/stat.h> -#include "hw/hw.h" -#include "hw/pc.h" -#include "qemu/error-report.h" -#include "ui/console.h" -#include "hw/loader.h" -#include "monitor/monitor.h" -#include "qemu/range.h" -#include "sysemu/sysemu.h" -#include "hw/pci/pci.h" -#include "hw/pci/msi.h" -#include "kvm_i386.h" - -#define MSIX_PAGE_SIZE 0x1000 - -/* From linux/ioport.h */ -#define IORESOURCE_IO 0x00000100 /* Resource type */ -#define IORESOURCE_MEM 0x00000200 -#define IORESOURCE_IRQ 0x00000400 -#define IORESOURCE_DMA 0x00000800 -#define IORESOURCE_PREFETCH 0x00002000 /* No side effects */ -#define IORESOURCE_MEM_64 0x00100000 - -//#define DEVICE_ASSIGNMENT_DEBUG - -#ifdef DEVICE_ASSIGNMENT_DEBUG -#define DEBUG(fmt, ...) \ - do { \ - fprintf(stderr, "%s: " fmt, __func__ , __VA_ARGS__); \ - } while (0) -#else -#define DEBUG(fmt, ...) -#endif - -typedef struct PCIRegion { - int type; /* Memory or port I/O */ - int valid; - uint64_t base_addr; - uint64_t size; /* size of the region */ - int resource_fd; -} PCIRegion; - -typedef struct PCIDevRegions { - uint8_t bus, dev, func; /* Bus inside domain, device and function */ - int irq; /* IRQ number */ - uint16_t region_number; /* number of active regions */ - - /* Port I/O or MMIO Regions */ - PCIRegion regions[PCI_NUM_REGIONS - 1]; - int config_fd; -} PCIDevRegions; - -typedef struct AssignedDevRegion { - MemoryRegion container; - MemoryRegion real_iomem; - union { - uint8_t *r_virtbase; /* mmapped access address for memory regions */ - uint32_t r_baseport; /* the base guest port for I/O regions */ - } u; - pcibus_t e_size; /* emulated size of region in bytes */ - pcibus_t r_size; /* real size of region in bytes */ - PCIRegion *region; -} AssignedDevRegion; - -#define ASSIGNED_DEVICE_PREFER_MSI_BIT 0 -#define ASSIGNED_DEVICE_SHARE_INTX_BIT 1 - -#define ASSIGNED_DEVICE_PREFER_MSI_MASK (1 << ASSIGNED_DEVICE_PREFER_MSI_BIT) -#define ASSIGNED_DEVICE_SHARE_INTX_MASK (1 << ASSIGNED_DEVICE_SHARE_INTX_BIT) - -typedef struct MSIXTableEntry { - uint32_t addr_lo; - uint32_t addr_hi; - uint32_t data; - uint32_t ctrl; -} MSIXTableEntry; - -typedef enum AssignedIRQType { - ASSIGNED_IRQ_NONE = 0, - ASSIGNED_IRQ_INTX_HOST_INTX, - ASSIGNED_IRQ_INTX_HOST_MSI, - ASSIGNED_IRQ_MSI, - ASSIGNED_IRQ_MSIX -} AssignedIRQType; - -typedef struct AssignedDevice { - PCIDevice dev; - PCIHostDeviceAddress host; - uint32_t dev_id; - uint32_t features; - int intpin; - AssignedDevRegion v_addrs[PCI_NUM_REGIONS - 1]; - PCIDevRegions real_device; - PCIINTxRoute intx_route; - AssignedIRQType assigned_irq_type; - struct { -#define ASSIGNED_DEVICE_CAP_MSI (1 << 0) -#define ASSIGNED_DEVICE_CAP_MSIX (1 << 1) - uint32_t available; -#define ASSIGNED_DEVICE_MSI_ENABLED (1 << 0) -#define ASSIGNED_DEVICE_MSIX_ENABLED (1 << 1) -#define ASSIGNED_DEVICE_MSIX_MASKED (1 << 2) - uint32_t state; - } cap; - uint8_t emulate_config_read[PCI_CONFIG_SPACE_SIZE]; - uint8_t emulate_config_write[PCI_CONFIG_SPACE_SIZE]; - int msi_virq_nr; - int *msi_virq; - MSIXTableEntry *msix_table; - hwaddr msix_table_addr; - uint16_t msix_max; - MemoryRegion mmio; - char *configfd_name; - int32_t bootindex; -} AssignedDevice; - -static void assigned_dev_update_irq_routing(PCIDevice *dev); - -static void assigned_dev_load_option_rom(AssignedDevice *dev); - -static void assigned_dev_unregister_msix_mmio(AssignedDevice *dev); - -static uint64_t assigned_dev_ioport_rw(AssignedDevRegion *dev_region, - hwaddr addr, int size, - uint64_t *data) -{ - uint64_t val = 0; - int fd = dev_region->region->resource_fd; - - if (fd >= 0) { - if (data) { - DEBUG("pwrite data=%" PRIx64 ", size=%d, e_phys=" TARGET_FMT_plx - ", addr="TARGET_FMT_plx"\n", *data, size, addr, addr); - if (pwrite(fd, data, size, addr) != size) { - error_report("%s - pwrite failed %s", - __func__, strerror(errno)); - } - } else { - if (pread(fd, &val, size, addr) != size) { - error_report("%s - pread failed %s", - __func__, strerror(errno)); - val = (1UL << (size * 8)) - 1; - } - DEBUG("pread val=%" PRIx64 ", size=%d, e_phys=" TARGET_FMT_plx - ", addr=" TARGET_FMT_plx "\n", val, size, addr, addr); - } - } else { - uint32_t port = addr + dev_region->u.r_baseport; - - if (data) { - DEBUG("out data=%" PRIx64 ", size=%d, e_phys=" TARGET_FMT_plx - ", host=%x\n", *data, size, addr, port); - switch (size) { - case 1: - outb(*data, port); - break; - case 2: - outw(*data, port); - break; - case 4: - outl(*data, port); - break; - } - } else { - switch (size) { - case 1: - val = inb(port); - break; - case 2: - val = inw(port); - break; - case 4: - val = inl(port); - break; - } - DEBUG("in data=%" PRIx64 ", size=%d, e_phys=" TARGET_FMT_plx - ", host=%x\n", val, size, addr, port); - } - } - return val; -} - -static void assigned_dev_ioport_write(void *opaque, hwaddr addr, - uint64_t data, unsigned size) -{ - assigned_dev_ioport_rw(opaque, addr, size, &data); -} - -static uint64_t assigned_dev_ioport_read(void *opaque, - hwaddr addr, unsigned size) -{ - return assigned_dev_ioport_rw(opaque, addr, size, NULL); -} - -static uint32_t slow_bar_readb(void *opaque, hwaddr addr) -{ - AssignedDevRegion *d = opaque; - uint8_t *in = d->u.r_virtbase + addr; - uint32_t r; - - r = *in; - DEBUG("slow_bar_readl addr=0x" TARGET_FMT_plx " val=0x%08x\n", addr, r); - - return r; -} - -static uint32_t slow_bar_readw(void *opaque, hwaddr addr) -{ - AssignedDevRegion *d = opaque; - uint16_t *in = (uint16_t *)(d->u.r_virtbase + addr); - uint32_t r; - - r = *in; - DEBUG("slow_bar_readl addr=0x" TARGET_FMT_plx " val=0x%08x\n", addr, r); - - return r; -} - -static uint32_t slow_bar_readl(void *opaque, hwaddr addr) -{ - AssignedDevRegion *d = opaque; - uint32_t *in = (uint32_t *)(d->u.r_virtbase + addr); - uint32_t r; - - r = *in; - DEBUG("slow_bar_readl addr=0x" TARGET_FMT_plx " val=0x%08x\n", addr, r); - - return r; -} - -static void slow_bar_writeb(void *opaque, hwaddr addr, uint32_t val) -{ - AssignedDevRegion *d = opaque; - uint8_t *out = d->u.r_virtbase + addr; - - DEBUG("slow_bar_writeb addr=0x" TARGET_FMT_plx " val=0x%02x\n", addr, val); - *out = val; -} - -static void slow_bar_writew(void *opaque, hwaddr addr, uint32_t val) -{ - AssignedDevRegion *d = opaque; - uint16_t *out = (uint16_t *)(d->u.r_virtbase + addr); - - DEBUG("slow_bar_writew addr=0x" TARGET_FMT_plx " val=0x%04x\n", addr, val); - *out = val; -} - -static void slow_bar_writel(void *opaque, hwaddr addr, uint32_t val) -{ - AssignedDevRegion *d = opaque; - uint32_t *out = (uint32_t *)(d->u.r_virtbase + addr); - - DEBUG("slow_bar_writel addr=0x" TARGET_FMT_plx " val=0x%08x\n", addr, val); - *out = val; -} - -static const MemoryRegionOps slow_bar_ops = { - .old_mmio = { - .read = { slow_bar_readb, slow_bar_readw, slow_bar_readl, }, - .write = { slow_bar_writeb, slow_bar_writew, slow_bar_writel, }, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void assigned_dev_iomem_setup(PCIDevice *pci_dev, int region_num, - pcibus_t e_size) -{ - AssignedDevice *r_dev = DO_UPCAST(AssignedDevice, dev, pci_dev); - AssignedDevRegion *region = &r_dev->v_addrs[region_num]; - PCIRegion *real_region = &r_dev->real_device.regions[region_num]; - - if (e_size > 0) { - memory_region_init(®ion->container, "assigned-dev-container", - e_size); - memory_region_add_subregion(®ion->container, 0, ®ion->real_iomem); - - /* deal with MSI-X MMIO page */ - if (real_region->base_addr <= r_dev->msix_table_addr && - real_region->base_addr + real_region->size > - r_dev->msix_table_addr) { - uint64_t offset = r_dev->msix_table_addr - real_region->base_addr; - - memory_region_add_subregion_overlap(®ion->container, - offset, - &r_dev->mmio, - 1); - } - } -} - -static const MemoryRegionOps assigned_dev_ioport_ops = { - .read = assigned_dev_ioport_read, - .write = assigned_dev_ioport_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void assigned_dev_ioport_setup(PCIDevice *pci_dev, int region_num, - pcibus_t size) -{ - AssignedDevice *r_dev = DO_UPCAST(AssignedDevice, dev, pci_dev); - AssignedDevRegion *region = &r_dev->v_addrs[region_num]; - - region->e_size = size; - memory_region_init(®ion->container, "assigned-dev-container", size); - memory_region_init_io(®ion->real_iomem, &assigned_dev_ioport_ops, - r_dev->v_addrs + region_num, - "assigned-dev-iomem", size); - memory_region_add_subregion(®ion->container, 0, ®ion->real_iomem); -} - -static uint32_t assigned_dev_pci_read(PCIDevice *d, int pos, int len) -{ - AssignedDevice *pci_dev = DO_UPCAST(AssignedDevice, dev, d); - uint32_t val; - ssize_t ret; - int fd = pci_dev->real_device.config_fd; - -again: - ret = pread(fd, &val, len, pos); - if (ret != len) { - if ((ret < 0) && (errno == EINTR || errno == EAGAIN)) { - goto again; - } - - hw_error("pci read failed, ret = %zd errno = %d\n", ret, errno); - } - - return val; -} - -static uint8_t assigned_dev_pci_read_byte(PCIDevice *d, int pos) -{ - return (uint8_t)assigned_dev_pci_read(d, pos, 1); -} - -static void assigned_dev_pci_write(PCIDevice *d, int pos, uint32_t val, int len) -{ - AssignedDevice *pci_dev = DO_UPCAST(AssignedDevice, dev, d); - ssize_t ret; - int fd = pci_dev->real_device.config_fd; - -again: - ret = pwrite(fd, &val, len, pos); - if (ret != len) { - if ((ret < 0) && (errno == EINTR || errno == EAGAIN)) { - goto again; - } - - hw_error("pci write failed, ret = %zd errno = %d\n", ret, errno); - } -} - -static void assigned_dev_emulate_config_read(AssignedDevice *dev, - uint32_t offset, uint32_t len) -{ - memset(dev->emulate_config_read + offset, 0xff, len); -} - -static void assigned_dev_direct_config_read(AssignedDevice *dev, - uint32_t offset, uint32_t len) -{ - memset(dev->emulate_config_read + offset, 0, len); -} - -static void assigned_dev_direct_config_write(AssignedDevice *dev, - uint32_t offset, uint32_t len) -{ - memset(dev->emulate_config_write + offset, 0, len); -} - -static uint8_t pci_find_cap_offset(PCIDevice *d, uint8_t cap, uint8_t start) -{ - int id; - int max_cap = 48; - int pos = start ? start : PCI_CAPABILITY_LIST; - int status; - - status = assigned_dev_pci_read_byte(d, PCI_STATUS); - if ((status & PCI_STATUS_CAP_LIST) == 0) { - return 0; - } - - while (max_cap--) { - pos = assigned_dev_pci_read_byte(d, pos); - if (pos < 0x40) { - break; - } - - pos &= ~3; - id = assigned_dev_pci_read_byte(d, pos + PCI_CAP_LIST_ID); - - if (id == 0xff) { - break; - } - if (id == cap) { - return pos; - } - - pos += PCI_CAP_LIST_NEXT; - } - return 0; -} - -static int assigned_dev_register_regions(PCIRegion *io_regions, - unsigned long regions_num, - AssignedDevice *pci_dev) -{ - uint32_t i; - PCIRegion *cur_region = io_regions; - - for (i = 0; i < regions_num; i++, cur_region++) { - if (!cur_region->valid) { - continue; - } - - /* handle memory io regions */ - if (cur_region->type & IORESOURCE_MEM) { - int t = PCI_BASE_ADDRESS_SPACE_MEMORY; - if (cur_region->type & IORESOURCE_PREFETCH) { - t |= PCI_BASE_ADDRESS_MEM_PREFETCH; - } - if (cur_region->type & IORESOURCE_MEM_64) { - t |= PCI_BASE_ADDRESS_MEM_TYPE_64; - } - - /* map physical memory */ - pci_dev->v_addrs[i].u.r_virtbase = mmap(NULL, cur_region->size, - PROT_WRITE | PROT_READ, - MAP_SHARED, - cur_region->resource_fd, - (off_t)0); - - if (pci_dev->v_addrs[i].u.r_virtbase == MAP_FAILED) { - pci_dev->v_addrs[i].u.r_virtbase = NULL; - error_report("%s: Error: Couldn't mmap 0x%" PRIx64 "!", - __func__, cur_region->base_addr); - return -1; - } - - pci_dev->v_addrs[i].r_size = cur_region->size; - pci_dev->v_addrs[i].e_size = 0; - - /* add offset */ - pci_dev->v_addrs[i].u.r_virtbase += - (cur_region->base_addr & 0xFFF); - - if (cur_region->size & 0xFFF) { - error_report("PCI region %d at address 0x%" PRIx64 " has " - "size 0x%" PRIx64 ", which is not a multiple of " - "4K. You might experience some performance hit " - "due to that.", - i, cur_region->base_addr, cur_region->size); - memory_region_init_io(&pci_dev->v_addrs[i].real_iomem, - &slow_bar_ops, &pci_dev->v_addrs[i], - "assigned-dev-slow-bar", - cur_region->size); - } else { - void *virtbase = pci_dev->v_addrs[i].u.r_virtbase; - char name[32]; - snprintf(name, sizeof(name), "%s.bar%d", - object_get_typename(OBJECT(pci_dev)), i); - memory_region_init_ram_ptr(&pci_dev->v_addrs[i].real_iomem, - name, cur_region->size, - virtbase); - vmstate_register_ram(&pci_dev->v_addrs[i].real_iomem, - &pci_dev->dev.qdev); - } - - assigned_dev_iomem_setup(&pci_dev->dev, i, cur_region->size); - pci_register_bar((PCIDevice *) pci_dev, i, t, - &pci_dev->v_addrs[i].container); - continue; - } else { - /* handle port io regions */ - uint32_t val; - int ret; - - /* Test kernel support for ioport resource read/write. Old - * kernels return EIO. New kernels only allow 1/2/4 byte reads - * so should return EINVAL for a 3 byte read */ - ret = pread(pci_dev->v_addrs[i].region->resource_fd, &val, 3, 0); - if (ret >= 0) { - error_report("Unexpected return from I/O port read: %d", ret); - abort(); - } else if (errno != EINVAL) { - error_report("Kernel doesn't support ioport resource " - "access, hiding this region."); - close(pci_dev->v_addrs[i].region->resource_fd); - cur_region->valid = 0; - continue; - } - - pci_dev->v_addrs[i].u.r_baseport = cur_region->base_addr; - pci_dev->v_addrs[i].r_size = cur_region->size; - pci_dev->v_addrs[i].e_size = 0; - - assigned_dev_ioport_setup(&pci_dev->dev, i, cur_region->size); - pci_register_bar((PCIDevice *) pci_dev, i, - PCI_BASE_ADDRESS_SPACE_IO, - &pci_dev->v_addrs[i].container); - } - } - - /* success */ - return 0; -} - -static int get_real_id(const char *devpath, const char *idname, uint16_t *val) -{ - FILE *f; - char name[128]; - long id; - - snprintf(name, sizeof(name), "%s%s", devpath, idname); - f = fopen(name, "r"); - if (f == NULL) { - error_report("%s: %s: %m", __func__, name); - return -1; - } - if (fscanf(f, "%li\n", &id) == 1) { - *val = id; - } else { - return -1; - } - fclose(f); - - return 0; -} - -static int get_real_vendor_id(const char *devpath, uint16_t *val) -{ - return get_real_id(devpath, "vendor", val); -} - -static int get_real_device_id(const char *devpath, uint16_t *val) -{ - return get_real_id(devpath, "device", val); -} - -static int get_real_device(AssignedDevice *pci_dev, uint16_t r_seg, - uint8_t r_bus, uint8_t r_dev, uint8_t r_func) -{ - char dir[128], name[128]; - int fd, r = 0, v; - FILE *f; - uint64_t start, end, size, flags; - uint16_t id; - PCIRegion *rp; - PCIDevRegions *dev = &pci_dev->real_device; - - dev->region_number = 0; - - snprintf(dir, sizeof(dir), "/sys/bus/pci/devices/%04x:%02x:%02x.%x/", - r_seg, r_bus, r_dev, r_func); - - snprintf(name, sizeof(name), "%sconfig", dir); - - if (pci_dev->configfd_name && *pci_dev->configfd_name) { - dev->config_fd = monitor_handle_fd_param(cur_mon, pci_dev->configfd_name); - if (dev->config_fd < 0) { - return 1; - } - } else { - dev->config_fd = open(name, O_RDWR); - - if (dev->config_fd == -1) { - error_report("%s: %s: %m", __func__, name); - return 1; - } - } -again: - r = read(dev->config_fd, pci_dev->dev.config, - pci_config_size(&pci_dev->dev)); - if (r < 0) { - if (errno == EINTR || errno == EAGAIN) { - goto again; - } - error_report("%s: read failed, errno = %d", __func__, errno); - } - - /* Restore or clear multifunction, this is always controlled by qemu */ - if (pci_dev->dev.cap_present & QEMU_PCI_CAP_MULTIFUNCTION) { - pci_dev->dev.config[PCI_HEADER_TYPE] |= PCI_HEADER_TYPE_MULTI_FUNCTION; - } else { - pci_dev->dev.config[PCI_HEADER_TYPE] &= ~PCI_HEADER_TYPE_MULTI_FUNCTION; - } - - /* Clear host resource mapping info. If we choose not to register a - * BAR, such as might be the case with the option ROM, we can get - * confusing, unwritable, residual addresses from the host here. */ - memset(&pci_dev->dev.config[PCI_BASE_ADDRESS_0], 0, 24); - memset(&pci_dev->dev.config[PCI_ROM_ADDRESS], 0, 4); - - snprintf(name, sizeof(name), "%sresource", dir); - - f = fopen(name, "r"); - if (f == NULL) { - error_report("%s: %s: %m", __func__, name); - return 1; - } - - for (r = 0; r < PCI_ROM_SLOT; r++) { - if (fscanf(f, "%" SCNi64 " %" SCNi64 " %" SCNi64 "\n", - &start, &end, &flags) != 3) { - break; - } - - rp = dev->regions + r; - rp->valid = 0; - rp->resource_fd = -1; - size = end - start + 1; - flags &= IORESOURCE_IO | IORESOURCE_MEM | IORESOURCE_PREFETCH - | IORESOURCE_MEM_64; - if (size == 0 || (flags & ~IORESOURCE_PREFETCH) == 0) { - continue; - } - if (flags & IORESOURCE_MEM) { - flags &= ~IORESOURCE_IO; - } else { - flags &= ~IORESOURCE_PREFETCH; - } - snprintf(name, sizeof(name), "%sresource%d", dir, r); - fd = open(name, O_RDWR); - if (fd == -1) { - continue; - } - rp->resource_fd = fd; - - rp->type = flags; - rp->valid = 1; - rp->base_addr = start; - rp->size = size; - pci_dev->v_addrs[r].region = rp; - DEBUG("region %d size %" PRIu64 " start 0x%" PRIx64 - " type %d resource_fd %d\n", - r, rp->size, start, rp->type, rp->resource_fd); - } - - fclose(f); - - /* read and fill vendor ID */ - v = get_real_vendor_id(dir, &id); - if (v) { - return 1; - } - pci_dev->dev.config[0] = id & 0xff; - pci_dev->dev.config[1] = (id & 0xff00) >> 8; - - /* read and fill device ID */ - v = get_real_device_id(dir, &id); - if (v) { - return 1; - } - pci_dev->dev.config[2] = id & 0xff; - pci_dev->dev.config[3] = (id & 0xff00) >> 8; - - pci_word_test_and_clear_mask(pci_dev->emulate_config_write + PCI_COMMAND, - PCI_COMMAND_MASTER | PCI_COMMAND_INTX_DISABLE); - - dev->region_number = r; - return 0; -} - -static void free_msi_virqs(AssignedDevice *dev) -{ - int i; - - for (i = 0; i < dev->msi_virq_nr; i++) { - if (dev->msi_virq[i] >= 0) { - kvm_irqchip_release_virq(kvm_state, dev->msi_virq[i]); - dev->msi_virq[i] = -1; - } - } - g_free(dev->msi_virq); - dev->msi_virq = NULL; - dev->msi_virq_nr = 0; -} - -static void free_assigned_device(AssignedDevice *dev) -{ - int i; - - if (dev->cap.available & ASSIGNED_DEVICE_CAP_MSIX) { - assigned_dev_unregister_msix_mmio(dev); - } - for (i = 0; i < dev->real_device.region_number; i++) { - PCIRegion *pci_region = &dev->real_device.regions[i]; - AssignedDevRegion *region = &dev->v_addrs[i]; - - if (!pci_region->valid) { - continue; - } - if (pci_region->type & IORESOURCE_IO) { - if (region->u.r_baseport) { - memory_region_del_subregion(®ion->container, - ®ion->real_iomem); - memory_region_destroy(®ion->real_iomem); - memory_region_destroy(®ion->container); - } - } else if (pci_region->type & IORESOURCE_MEM) { - if (region->u.r_virtbase) { - memory_region_del_subregion(®ion->container, - ®ion->real_iomem); - - /* Remove MSI-X table subregion */ - if (pci_region->base_addr <= dev->msix_table_addr && - pci_region->base_addr + pci_region->size > - dev->msix_table_addr) { - memory_region_del_subregion(®ion->container, - &dev->mmio); - } - - memory_region_destroy(®ion->real_iomem); - memory_region_destroy(®ion->container); - if (munmap(region->u.r_virtbase, - (pci_region->size + 0xFFF) & 0xFFFFF000)) { - error_report("Failed to unmap assigned device region: %s", - strerror(errno)); - } - } - } - if (pci_region->resource_fd >= 0) { - close(pci_region->resource_fd); - } - } - - if (dev->real_device.config_fd >= 0) { - close(dev->real_device.config_fd); - } - - free_msi_virqs(dev); -} - -static void assign_failed_examine(AssignedDevice *dev) -{ - char name[PATH_MAX], dir[PATH_MAX], driver[PATH_MAX] = {}, *ns; - uint16_t vendor_id, device_id; - int r; - - snprintf(dir, sizeof(dir), "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/", - dev->host.domain, dev->host.bus, dev->host.slot, - dev->host.function); - - snprintf(name, sizeof(name), "%sdriver", dir); - - r = readlink(name, driver, sizeof(driver)); - if ((r <= 0) || r >= sizeof(driver)) { - goto fail; - } - - ns = strrchr(driver, '/'); - if (!ns) { - goto fail; - } - - ns++; - - if (get_real_vendor_id(dir, &vendor_id) || - get_real_device_id(dir, &device_id)) { - goto fail; - } - - error_report("*** The driver '%s' is occupying your device " - "%04x:%02x:%02x.%x.", - ns, dev->host.domain, dev->host.bus, dev->host.slot, - dev->host.function); - error_report("***"); - error_report("*** You can try the following commands to free it:"); - error_report("***"); - error_report("*** $ echo \"%04x %04x\" > /sys/bus/pci/drivers/pci-stub/" - "new_id", vendor_id, device_id); - error_report("*** $ echo \"%04x:%02x:%02x.%x\" > /sys/bus/pci/drivers/" - "%s/unbind", - dev->host.domain, dev->host.bus, dev->host.slot, - dev->host.function, ns); - error_report("*** $ echo \"%04x:%02x:%02x.%x\" > /sys/bus/pci/drivers/" - "pci-stub/bind", - dev->host.domain, dev->host.bus, dev->host.slot, - dev->host.function); - error_report("*** $ echo \"%04x %04x\" > /sys/bus/pci/drivers/pci-stub" - "/remove_id", vendor_id, device_id); - error_report("***"); - - return; - -fail: - error_report("Couldn't find out why."); -} - -static int assign_device(AssignedDevice *dev) -{ - uint32_t flags = KVM_DEV_ASSIGN_ENABLE_IOMMU; - int r; - - /* Only pass non-zero PCI segment to capable module */ - if (!kvm_check_extension(kvm_state, KVM_CAP_PCI_SEGMENT) && - dev->host.domain) { - error_report("Can't assign device inside non-zero PCI segment " - "as this KVM module doesn't support it."); - return -ENODEV; - } - - if (!kvm_check_extension(kvm_state, KVM_CAP_IOMMU)) { - error_report("No IOMMU found. Unable to assign device \"%s\"", - dev->dev.qdev.id); - return -ENODEV; - } - - if (dev->features & ASSIGNED_DEVICE_SHARE_INTX_MASK && - kvm_has_intx_set_mask()) { - flags |= KVM_DEV_ASSIGN_PCI_2_3; - } - - r = kvm_device_pci_assign(kvm_state, &dev->host, flags, &dev->dev_id); - if (r < 0) { - error_report("Failed to assign device \"%s\" : %s", - dev->dev.qdev.id, strerror(-r)); - - switch (r) { - case -EBUSY: - assign_failed_examine(dev); - break; - default: - break; - } - } - return r; -} - -static bool check_irqchip_in_kernel(void) -{ - if (kvm_irqchip_in_kernel()) { - return true; - } - error_report("pci-assign: error: requires KVM with in-kernel irqchip " - "enabled"); - return false; -} - -static int assign_intx(AssignedDevice *dev) -{ - AssignedIRQType new_type; - PCIINTxRoute intx_route; - bool intx_host_msi; - int r; - - /* Interrupt PIN 0 means don't use INTx */ - if (assigned_dev_pci_read_byte(&dev->dev, PCI_INTERRUPT_PIN) == 0) { - pci_device_set_intx_routing_notifier(&dev->dev, NULL); - return 0; - } - - if (!check_irqchip_in_kernel()) { - return -ENOTSUP; - } - - pci_device_set_intx_routing_notifier(&dev->dev, - assigned_dev_update_irq_routing); - - intx_route = pci_device_route_intx_to_irq(&dev->dev, dev->intpin); - assert(intx_route.mode != PCI_INTX_INVERTED); - - if (!pci_intx_route_changed(&dev->intx_route, &intx_route)) { - return 0; - } - - switch (dev->assigned_irq_type) { - case ASSIGNED_IRQ_INTX_HOST_INTX: - case ASSIGNED_IRQ_INTX_HOST_MSI: - intx_host_msi = dev->assigned_irq_type == ASSIGNED_IRQ_INTX_HOST_MSI; - r = kvm_device_intx_deassign(kvm_state, dev->dev_id, intx_host_msi); - break; - case ASSIGNED_IRQ_MSI: - r = kvm_device_msi_deassign(kvm_state, dev->dev_id); - break; - case ASSIGNED_IRQ_MSIX: - r = kvm_device_msix_deassign(kvm_state, dev->dev_id); - break; - default: - r = 0; - break; - } - if (r) { - perror("assign_intx: deassignment of previous interrupt failed"); - } - dev->assigned_irq_type = ASSIGNED_IRQ_NONE; - - if (intx_route.mode == PCI_INTX_DISABLED) { - dev->intx_route = intx_route; - return 0; - } - -retry: - if (dev->features & ASSIGNED_DEVICE_PREFER_MSI_MASK && - dev->cap.available & ASSIGNED_DEVICE_CAP_MSI) { - intx_host_msi = true; - new_type = ASSIGNED_IRQ_INTX_HOST_MSI; - } else { - intx_host_msi = false; - new_type = ASSIGNED_IRQ_INTX_HOST_INTX; - } - - r = kvm_device_intx_assign(kvm_state, dev->dev_id, intx_host_msi, - intx_route.irq); - if (r < 0) { - if (r == -EIO && !(dev->features & ASSIGNED_DEVICE_PREFER_MSI_MASK) && - dev->cap.available & ASSIGNED_DEVICE_CAP_MSI) { - /* Retry with host-side MSI. There might be an IRQ conflict and - * either the kernel or the device doesn't support sharing. */ - error_report("Host-side INTx sharing not supported, " - "using MSI instead"); - error_printf("Some devices do not work properly in this mode.\n"); - dev->features |= ASSIGNED_DEVICE_PREFER_MSI_MASK; - goto retry; - } - error_report("Failed to assign irq for \"%s\": %s", - dev->dev.qdev.id, strerror(-r)); - error_report("Perhaps you are assigning a device " - "that shares an IRQ with another device?"); - return r; - } - - dev->intx_route = intx_route; - dev->assigned_irq_type = new_type; - return r; -} - -static void deassign_device(AssignedDevice *dev) -{ - int r; - - r = kvm_device_pci_deassign(kvm_state, dev->dev_id); - assert(r == 0); -} - -/* The pci config space got updated. Check if irq numbers have changed - * for our devices - */ -static void assigned_dev_update_irq_routing(PCIDevice *dev) -{ - AssignedDevice *assigned_dev = DO_UPCAST(AssignedDevice, dev, dev); - Error *err = NULL; - int r; - - r = assign_intx(assigned_dev); - if (r < 0) { - qdev_unplug(&dev->qdev, &err); - assert(!err); - } -} - -static void assigned_dev_update_msi(PCIDevice *pci_dev) -{ - AssignedDevice *assigned_dev = DO_UPCAST(AssignedDevice, dev, pci_dev); - uint8_t ctrl_byte = pci_get_byte(pci_dev->config + pci_dev->msi_cap + - PCI_MSI_FLAGS); - int r; - - /* Some guests gratuitously disable MSI even if they're not using it, - * try to catch this by only deassigning irqs if the guest is using - * MSI or intends to start. */ - if (assigned_dev->assigned_irq_type == ASSIGNED_IRQ_MSI || - (ctrl_byte & PCI_MSI_FLAGS_ENABLE)) { - r = kvm_device_msi_deassign(kvm_state, assigned_dev->dev_id); - /* -ENXIO means no assigned irq */ - if (r && r != -ENXIO) { - perror("assigned_dev_update_msi: deassign irq"); - } - - free_msi_virqs(assigned_dev); - - assigned_dev->assigned_irq_type = ASSIGNED_IRQ_NONE; - pci_device_set_intx_routing_notifier(pci_dev, NULL); - } - - if (ctrl_byte & PCI_MSI_FLAGS_ENABLE) { - MSIMessage msg = msi_get_message(pci_dev, 0); - int virq; - - virq = kvm_irqchip_add_msi_route(kvm_state, msg); - if (virq < 0) { - perror("assigned_dev_update_msi: kvm_irqchip_add_msi_route"); - return; - } - - assigned_dev->msi_virq = g_malloc(sizeof(*assigned_dev->msi_virq)); - assigned_dev->msi_virq_nr = 1; - assigned_dev->msi_virq[0] = virq; - if (kvm_device_msi_assign(kvm_state, assigned_dev->dev_id, virq) < 0) { - perror("assigned_dev_update_msi: kvm_device_msi_assign"); - } - - assigned_dev->intx_route.mode = PCI_INTX_DISABLED; - assigned_dev->intx_route.irq = -1; - assigned_dev->assigned_irq_type = ASSIGNED_IRQ_MSI; - } else { - assign_intx(assigned_dev); - } -} - -static bool assigned_dev_msix_masked(MSIXTableEntry *entry) -{ - return (entry->ctrl & cpu_to_le32(0x1)) != 0; -} - -/* - * When MSI-X is first enabled the vector table typically has all the - * vectors masked, so we can't use that as the obvious test to figure out - * how many vectors to initially enable. Instead we look at the data field - * because this is what worked for pci-assign for a long time. This makes - * sure the physical MSI-X state tracks the guest's view, which is important - * for some VF/PF and PF/fw communication channels. - */ -static bool assigned_dev_msix_skipped(MSIXTableEntry *entry) -{ - return !entry->data; -} - -static int assigned_dev_update_msix_mmio(PCIDevice *pci_dev) -{ - AssignedDevice *adev = DO_UPCAST(AssignedDevice, dev, pci_dev); - uint16_t entries_nr = 0; - int i, r = 0; - MSIXTableEntry *entry = adev->msix_table; - MSIMessage msg; - - /* Get the usable entry number for allocating */ - for (i = 0; i < adev->msix_max; i++, entry++) { - if (assigned_dev_msix_skipped(entry)) { - continue; - } - entries_nr++; - } - - DEBUG("MSI-X entries: %d\n", entries_nr); - - /* It's valid to enable MSI-X with all entries masked */ - if (!entries_nr) { - return 0; - } - - r = kvm_device_msix_init_vectors(kvm_state, adev->dev_id, entries_nr); - if (r != 0) { - error_report("fail to set MSI-X entry number for MSIX! %s", - strerror(-r)); - return r; - } - - free_msi_virqs(adev); - - adev->msi_virq_nr = adev->msix_max; - adev->msi_virq = g_malloc(adev->msix_max * sizeof(*adev->msi_virq)); - - entry = adev->msix_table; - for (i = 0; i < adev->msix_max; i++, entry++) { - adev->msi_virq[i] = -1; - - if (assigned_dev_msix_skipped(entry)) { - continue; - } - - msg.address = entry->addr_lo | ((uint64_t)entry->addr_hi << 32); - msg.data = entry->data; - r = kvm_irqchip_add_msi_route(kvm_state, msg); - if (r < 0) { - return r; - } - adev->msi_virq[i] = r; - - DEBUG("MSI-X vector %d, gsi %d, addr %08x_%08x, data %08x\n", i, - r, entry->addr_hi, entry->addr_lo, entry->data); - - r = kvm_device_msix_set_vector(kvm_state, adev->dev_id, i, - adev->msi_virq[i]); - if (r) { - error_report("fail to set MSI-X entry! %s", strerror(-r)); - break; - } - } - - return r; -} - -static void assigned_dev_update_msix(PCIDevice *pci_dev) -{ - AssignedDevice *assigned_dev = DO_UPCAST(AssignedDevice, dev, pci_dev); - uint16_t ctrl_word = pci_get_word(pci_dev->config + pci_dev->msix_cap + - PCI_MSIX_FLAGS); - int r; - - /* Some guests gratuitously disable MSIX even if they're not using it, - * try to catch this by only deassigning irqs if the guest is using - * MSIX or intends to start. */ - if ((assigned_dev->assigned_irq_type == ASSIGNED_IRQ_MSIX) || - (ctrl_word & PCI_MSIX_FLAGS_ENABLE)) { - r = kvm_device_msix_deassign(kvm_state, assigned_dev->dev_id); - /* -ENXIO means no assigned irq */ - if (r && r != -ENXIO) { - perror("assigned_dev_update_msix: deassign irq"); - } - - free_msi_virqs(assigned_dev); - - assigned_dev->assigned_irq_type = ASSIGNED_IRQ_NONE; - pci_device_set_intx_routing_notifier(pci_dev, NULL); - } - - if (ctrl_word & PCI_MSIX_FLAGS_ENABLE) { - if (assigned_dev_update_msix_mmio(pci_dev) < 0) { - perror("assigned_dev_update_msix_mmio"); - return; - } - - if (assigned_dev->msi_virq_nr > 0) { - if (kvm_device_msix_assign(kvm_state, assigned_dev->dev_id) < 0) { - perror("assigned_dev_enable_msix: assign irq"); - return; - } - } - assigned_dev->intx_route.mode = PCI_INTX_DISABLED; - assigned_dev->intx_route.irq = -1; - assigned_dev->assigned_irq_type = ASSIGNED_IRQ_MSIX; - } else { - assign_intx(assigned_dev); - } -} - -static uint32_t assigned_dev_pci_read_config(PCIDevice *pci_dev, - uint32_t address, int len) -{ - AssignedDevice *assigned_dev = DO_UPCAST(AssignedDevice, dev, pci_dev); - uint32_t virt_val = pci_default_read_config(pci_dev, address, len); - uint32_t real_val, emulate_mask, full_emulation_mask; - - emulate_mask = 0; - memcpy(&emulate_mask, assigned_dev->emulate_config_read + address, len); - emulate_mask = le32_to_cpu(emulate_mask); - - full_emulation_mask = 0xffffffff >> (32 - len * 8); - - if (emulate_mask != full_emulation_mask) { - real_val = assigned_dev_pci_read(pci_dev, address, len); - return (virt_val & emulate_mask) | (real_val & ~emulate_mask); - } else { - return virt_val; - } -} - -static void assigned_dev_pci_write_config(PCIDevice *pci_dev, uint32_t address, - uint32_t val, int len) -{ - AssignedDevice *assigned_dev = DO_UPCAST(AssignedDevice, dev, pci_dev); - uint16_t old_cmd = pci_get_word(pci_dev->config + PCI_COMMAND); - uint32_t emulate_mask, full_emulation_mask; - int ret; - - pci_default_write_config(pci_dev, address, val, len); - - if (kvm_has_intx_set_mask() && - range_covers_byte(address, len, PCI_COMMAND + 1)) { - bool intx_masked = (pci_get_word(pci_dev->config + PCI_COMMAND) & - PCI_COMMAND_INTX_DISABLE); - - if (intx_masked != !!(old_cmd & PCI_COMMAND_INTX_DISABLE)) { - ret = kvm_device_intx_set_mask(kvm_state, assigned_dev->dev_id, - intx_masked); - if (ret) { - perror("assigned_dev_pci_write_config: set intx mask"); - } - } - } - if (assigned_dev->cap.available & ASSIGNED_DEVICE_CAP_MSI) { - if (range_covers_byte(address, len, - pci_dev->msi_cap + PCI_MSI_FLAGS)) { - assigned_dev_update_msi(pci_dev); - } - } - if (assigned_dev->cap.available & ASSIGNED_DEVICE_CAP_MSIX) { - if (range_covers_byte(address, len, - pci_dev->msix_cap + PCI_MSIX_FLAGS + 1)) { - assigned_dev_update_msix(pci_dev); - } - } - - emulate_mask = 0; - memcpy(&emulate_mask, assigned_dev->emulate_config_write + address, len); - emulate_mask = le32_to_cpu(emulate_mask); - - full_emulation_mask = 0xffffffff >> (32 - len * 8); - - if (emulate_mask != full_emulation_mask) { - if (emulate_mask) { - val &= ~emulate_mask; - val |= assigned_dev_pci_read(pci_dev, address, len) & emulate_mask; - } - assigned_dev_pci_write(pci_dev, address, val, len); - } -} - -static void assigned_dev_setup_cap_read(AssignedDevice *dev, uint32_t offset, - uint32_t len) -{ - assigned_dev_direct_config_read(dev, offset, len); - assigned_dev_emulate_config_read(dev, offset + PCI_CAP_LIST_NEXT, 1); -} - -static int assigned_device_pci_cap_init(PCIDevice *pci_dev) -{ - AssignedDevice *dev = DO_UPCAST(AssignedDevice, dev, pci_dev); - PCIRegion *pci_region = dev->real_device.regions; - int ret, pos; - - /* Clear initial capabilities pointer and status copied from hw */ - pci_set_byte(pci_dev->config + PCI_CAPABILITY_LIST, 0); - pci_set_word(pci_dev->config + PCI_STATUS, - pci_get_word(pci_dev->config + PCI_STATUS) & - ~PCI_STATUS_CAP_LIST); - - /* Expose MSI capability - * MSI capability is the 1st capability in capability config */ - pos = pci_find_cap_offset(pci_dev, PCI_CAP_ID_MSI, 0); - if (pos != 0 && kvm_check_extension(kvm_state, KVM_CAP_ASSIGN_DEV_IRQ)) { - if (!check_irqchip_in_kernel()) { - return -ENOTSUP; - } - dev->cap.available |= ASSIGNED_DEVICE_CAP_MSI; - /* Only 32-bit/no-mask currently supported */ - ret = pci_add_capability(pci_dev, PCI_CAP_ID_MSI, pos, 10); - if (ret < 0) { - return ret; - } - pci_dev->msi_cap = pos; - - pci_set_word(pci_dev->config + pos + PCI_MSI_FLAGS, - pci_get_word(pci_dev->config + pos + PCI_MSI_FLAGS) & - PCI_MSI_FLAGS_QMASK); - pci_set_long(pci_dev->config + pos + PCI_MSI_ADDRESS_LO, 0); - pci_set_word(pci_dev->config + pos + PCI_MSI_DATA_32, 0); - - /* Set writable fields */ - pci_set_word(pci_dev->wmask + pos + PCI_MSI_FLAGS, - PCI_MSI_FLAGS_QSIZE | PCI_MSI_FLAGS_ENABLE); - pci_set_long(pci_dev->wmask + pos + PCI_MSI_ADDRESS_LO, 0xfffffffc); - pci_set_word(pci_dev->wmask + pos + PCI_MSI_DATA_32, 0xffff); - } - /* Expose MSI-X capability */ - pos = pci_find_cap_offset(pci_dev, PCI_CAP_ID_MSIX, 0); - if (pos != 0 && kvm_device_msix_supported(kvm_state)) { - int bar_nr; - uint32_t msix_table_entry; - - if (!check_irqchip_in_kernel()) { - return -ENOTSUP; - } - dev->cap.available |= ASSIGNED_DEVICE_CAP_MSIX; - ret = pci_add_capability(pci_dev, PCI_CAP_ID_MSIX, pos, 12); - if (ret < 0) { - return ret; - } - pci_dev->msix_cap = pos; - - pci_set_word(pci_dev->config + pos + PCI_MSIX_FLAGS, - pci_get_word(pci_dev->config + pos + PCI_MSIX_FLAGS) & - PCI_MSIX_FLAGS_QSIZE); - - /* Only enable and function mask bits are writable */ - pci_set_word(pci_dev->wmask + pos + PCI_MSIX_FLAGS, - PCI_MSIX_FLAGS_ENABLE | PCI_MSIX_FLAGS_MASKALL); - - msix_table_entry = pci_get_long(pci_dev->config + pos + PCI_MSIX_TABLE); - bar_nr = msix_table_entry & PCI_MSIX_FLAGS_BIRMASK; - msix_table_entry &= ~PCI_MSIX_FLAGS_BIRMASK; - dev->msix_table_addr = pci_region[bar_nr].base_addr + msix_table_entry; - dev->msix_max = pci_get_word(pci_dev->config + pos + PCI_MSIX_FLAGS); - dev->msix_max &= PCI_MSIX_FLAGS_QSIZE; - dev->msix_max += 1; - } - - /* Minimal PM support, nothing writable, device appears to NAK changes */ - pos = pci_find_cap_offset(pci_dev, PCI_CAP_ID_PM, 0); - if (pos) { - uint16_t pmc; - - ret = pci_add_capability(pci_dev, PCI_CAP_ID_PM, pos, PCI_PM_SIZEOF); - if (ret < 0) { - return ret; - } - - assigned_dev_setup_cap_read(dev, pos, PCI_PM_SIZEOF); - - pmc = pci_get_word(pci_dev->config + pos + PCI_CAP_FLAGS); - pmc &= (PCI_PM_CAP_VER_MASK | PCI_PM_CAP_DSI); - pci_set_word(pci_dev->config + pos + PCI_CAP_FLAGS, pmc); - - /* assign_device will bring the device up to D0, so we don't need - * to worry about doing that ourselves here. */ - pci_set_word(pci_dev->config + pos + PCI_PM_CTRL, - PCI_PM_CTRL_NO_SOFT_RESET); - - pci_set_byte(pci_dev->config + pos + PCI_PM_PPB_EXTENSIONS, 0); - pci_set_byte(pci_dev->config + pos + PCI_PM_DATA_REGISTER, 0); - } - - pos = pci_find_cap_offset(pci_dev, PCI_CAP_ID_EXP, 0); - if (pos) { - uint8_t version, size = 0; - uint16_t type, devctl, lnksta; - uint32_t devcap, lnkcap; - - version = pci_get_byte(pci_dev->config + pos + PCI_EXP_FLAGS); - version &= PCI_EXP_FLAGS_VERS; - if (version == 1) { - size = 0x14; - } else if (version == 2) { - /* - * Check for non-std size, accept reduced size to 0x34, - * which is what bcm5761 implemented, violating the - * PCIe v3.0 spec that regs should exist and be read as 0, - * not optionally provided and shorten the struct size. - */ - size = MIN(0x3c, PCI_CONFIG_SPACE_SIZE - pos); - if (size < 0x34) { - error_report("%s: Invalid size PCIe cap-id 0x%x", - __func__, PCI_CAP_ID_EXP); - return -EINVAL; - } else if (size != 0x3c) { - error_report("WARNING, %s: PCIe cap-id 0x%x has " - "non-standard size 0x%x; std size should be 0x3c", - __func__, PCI_CAP_ID_EXP, size); - } - } else if (version == 0) { - uint16_t vid, did; - vid = pci_get_word(pci_dev->config + PCI_VENDOR_ID); - did = pci_get_word(pci_dev->config + PCI_DEVICE_ID); - if (vid == PCI_VENDOR_ID_INTEL && did == 0x10ed) { - /* - * quirk for Intel 82599 VF with invalid PCIe capability - * version, should really be version 2 (same as PF) - */ - size = 0x3c; - } - } - - if (size == 0) { - error_report("%s: Unsupported PCI express capability version %d", - __func__, version); - return -EINVAL; - } - - ret = pci_add_capability(pci_dev, PCI_CAP_ID_EXP, pos, size); - if (ret < 0) { - return ret; - } - - assigned_dev_setup_cap_read(dev, pos, size); - - type = pci_get_word(pci_dev->config + pos + PCI_EXP_FLAGS); - type = (type & PCI_EXP_FLAGS_TYPE) >> 4; - if (type != PCI_EXP_TYPE_ENDPOINT && - type != PCI_EXP_TYPE_LEG_END && type != PCI_EXP_TYPE_RC_END) { - error_report("Device assignment only supports endpoint assignment," - " device type %d", type); - return -EINVAL; - } - - /* capabilities, pass existing read-only copy - * PCI_EXP_FLAGS_IRQ: updated by hardware, should be direct read */ - - /* device capabilities: hide FLR */ - devcap = pci_get_long(pci_dev->config + pos + PCI_EXP_DEVCAP); - devcap &= ~PCI_EXP_DEVCAP_FLR; - pci_set_long(pci_dev->config + pos + PCI_EXP_DEVCAP, devcap); - - /* device control: clear all error reporting enable bits, leaving - * only a few host values. Note, these are - * all writable, but not passed to hw. - */ - devctl = pci_get_word(pci_dev->config + pos + PCI_EXP_DEVCTL); - devctl = (devctl & (PCI_EXP_DEVCTL_READRQ | PCI_EXP_DEVCTL_PAYLOAD)) | - PCI_EXP_DEVCTL_RELAX_EN | PCI_EXP_DEVCTL_NOSNOOP_EN; - pci_set_word(pci_dev->config + pos + PCI_EXP_DEVCTL, devctl); - devctl = PCI_EXP_DEVCTL_BCR_FLR | PCI_EXP_DEVCTL_AUX_PME; - pci_set_word(pci_dev->wmask + pos + PCI_EXP_DEVCTL, ~devctl); - - /* Clear device status */ - pci_set_word(pci_dev->config + pos + PCI_EXP_DEVSTA, 0); - - /* Link capabilities, expose links and latencues, clear reporting */ - lnkcap = pci_get_long(pci_dev->config + pos + PCI_EXP_LNKCAP); - lnkcap &= (PCI_EXP_LNKCAP_SLS | PCI_EXP_LNKCAP_MLW | - PCI_EXP_LNKCAP_ASPMS | PCI_EXP_LNKCAP_L0SEL | - PCI_EXP_LNKCAP_L1EL); - pci_set_long(pci_dev->config + pos + PCI_EXP_LNKCAP, lnkcap); - - /* Link control, pass existing read-only copy. Should be writable? */ - - /* Link status, only expose current speed and width */ - lnksta = pci_get_word(pci_dev->config + pos + PCI_EXP_LNKSTA); - lnksta &= (PCI_EXP_LNKSTA_CLS | PCI_EXP_LNKSTA_NLW); - pci_set_word(pci_dev->config + pos + PCI_EXP_LNKSTA, lnksta); - - if (version >= 2) { - /* Slot capabilities, control, status - not needed for endpoints */ - pci_set_long(pci_dev->config + pos + PCI_EXP_SLTCAP, 0); - pci_set_word(pci_dev->config + pos + PCI_EXP_SLTCTL, 0); - pci_set_word(pci_dev->config + pos + PCI_EXP_SLTSTA, 0); - - /* Root control, capabilities, status - not needed for endpoints */ - pci_set_word(pci_dev->config + pos + PCI_EXP_RTCTL, 0); - pci_set_word(pci_dev->config + pos + PCI_EXP_RTCAP, 0); - pci_set_long(pci_dev->config + pos + PCI_EXP_RTSTA, 0); - - /* Device capabilities/control 2, pass existing read-only copy */ - /* Link control 2, pass existing read-only copy */ - } - } - - pos = pci_find_cap_offset(pci_dev, PCI_CAP_ID_PCIX, 0); - if (pos) { - uint16_t cmd; - uint32_t status; - - /* Only expose the minimum, 8 byte capability */ - ret = pci_add_capability(pci_dev, PCI_CAP_ID_PCIX, pos, 8); - if (ret < 0) { - return ret; - } - - assigned_dev_setup_cap_read(dev, pos, 8); - - /* Command register, clear upper bits, including extended modes */ - cmd = pci_get_word(pci_dev->config + pos + PCI_X_CMD); - cmd &= (PCI_X_CMD_DPERR_E | PCI_X_CMD_ERO | PCI_X_CMD_MAX_READ | - PCI_X_CMD_MAX_SPLIT); - pci_set_word(pci_dev->config + pos + PCI_X_CMD, cmd); - - /* Status register, update with emulated PCI bus location, clear - * error bits, leave the rest. */ - status = pci_get_long(pci_dev->config + pos + PCI_X_STATUS); - status &= ~(PCI_X_STATUS_BUS | PCI_X_STATUS_DEVFN); - status |= (pci_bus_num(pci_dev->bus) << 8) | pci_dev->devfn; - status &= ~(PCI_X_STATUS_SPL_DISC | PCI_X_STATUS_UNX_SPL | - PCI_X_STATUS_SPL_ERR); - pci_set_long(pci_dev->config + pos + PCI_X_STATUS, status); - } - - pos = pci_find_cap_offset(pci_dev, PCI_CAP_ID_VPD, 0); - if (pos) { - /* Direct R/W passthrough */ - ret = pci_add_capability(pci_dev, PCI_CAP_ID_VPD, pos, 8); - if (ret < 0) { - return ret; - } - - assigned_dev_setup_cap_read(dev, pos, 8); - - /* direct write for cap content */ - assigned_dev_direct_config_write(dev, pos + 2, 6); - } - - /* Devices can have multiple vendor capabilities, get them all */ - for (pos = 0; (pos = pci_find_cap_offset(pci_dev, PCI_CAP_ID_VNDR, pos)); - pos += PCI_CAP_LIST_NEXT) { - uint8_t len = pci_get_byte(pci_dev->config + pos + PCI_CAP_FLAGS); - /* Direct R/W passthrough */ - ret = pci_add_capability(pci_dev, PCI_CAP_ID_VNDR, pos, len); - if (ret < 0) { - return ret; - } - - assigned_dev_setup_cap_read(dev, pos, len); - - /* direct write for cap content */ - assigned_dev_direct_config_write(dev, pos + 2, len - 2); - } - - /* If real and virtual capability list status bits differ, virtualize the - * access. */ - if ((pci_get_word(pci_dev->config + PCI_STATUS) & PCI_STATUS_CAP_LIST) != - (assigned_dev_pci_read_byte(pci_dev, PCI_STATUS) & - PCI_STATUS_CAP_LIST)) { - dev->emulate_config_read[PCI_STATUS] |= PCI_STATUS_CAP_LIST; - } - - return 0; -} - -static uint64_t -assigned_dev_msix_mmio_read(void *opaque, hwaddr addr, - unsigned size) -{ - AssignedDevice *adev = opaque; - uint64_t val; - - memcpy(&val, (void *)((uint8_t *)adev->msix_table + addr), size); - - return val; -} - -static void assigned_dev_msix_mmio_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - AssignedDevice *adev = opaque; - PCIDevice *pdev = &adev->dev; - uint16_t ctrl; - MSIXTableEntry orig; - int i = addr >> 4; - - if (i >= adev->msix_max) { - return; /* Drop write */ - } - - ctrl = pci_get_word(pdev->config + pdev->msix_cap + PCI_MSIX_FLAGS); - - DEBUG("write to MSI-X table offset 0x%lx, val 0x%lx\n", addr, val); - - if (ctrl & PCI_MSIX_FLAGS_ENABLE) { - orig = adev->msix_table[i]; - } - - memcpy((uint8_t *)adev->msix_table + addr, &val, size); - - if (ctrl & PCI_MSIX_FLAGS_ENABLE) { - MSIXTableEntry *entry = &adev->msix_table[i]; - - if (!assigned_dev_msix_masked(&orig) && - assigned_dev_msix_masked(entry)) { - /* - * Vector masked, disable it - * - * XXX It's not clear if we can or should actually attempt - * to mask or disable the interrupt. KVM doesn't have - * support for pending bits and kvm_assign_set_msix_entry - * doesn't modify the device hardware mask. Interrupts - * while masked are simply not injected to the guest, so - * are lost. Can we get away with always injecting an - * interrupt on unmask? - */ - } else if (assigned_dev_msix_masked(&orig) && - !assigned_dev_msix_masked(entry)) { - /* Vector unmasked */ - if (i >= adev->msi_virq_nr || adev->msi_virq[i] < 0) { - /* Previously unassigned vector, start from scratch */ - assigned_dev_update_msix(pdev); - return; - } else { - /* Update an existing, previously masked vector */ - MSIMessage msg; - int ret; - - msg.address = entry->addr_lo | - ((uint64_t)entry->addr_hi << 32); - msg.data = entry->data; - - ret = kvm_irqchip_update_msi_route(kvm_state, - adev->msi_virq[i], msg); - if (ret) { - error_report("Error updating irq routing entry (%d)", ret); - } - } - } - } -} - -static const MemoryRegionOps assigned_dev_msix_mmio_ops = { - .read = assigned_dev_msix_mmio_read, - .write = assigned_dev_msix_mmio_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 8, - }, - .impl = { - .min_access_size = 4, - .max_access_size = 8, - }, -}; - -static void assigned_dev_msix_reset(AssignedDevice *dev) -{ - MSIXTableEntry *entry; - int i; - - if (!dev->msix_table) { - return; - } - - memset(dev->msix_table, 0, MSIX_PAGE_SIZE); - - for (i = 0, entry = dev->msix_table; i < dev->msix_max; i++, entry++) { - entry->ctrl = cpu_to_le32(0x1); /* Masked */ - } -} - -static int assigned_dev_register_msix_mmio(AssignedDevice *dev) -{ - dev->msix_table = mmap(NULL, MSIX_PAGE_SIZE, PROT_READ|PROT_WRITE, - MAP_ANONYMOUS|MAP_PRIVATE, 0, 0); - if (dev->msix_table == MAP_FAILED) { - error_report("fail allocate msix_table! %s", strerror(errno)); - return -EFAULT; - } - - assigned_dev_msix_reset(dev); - - memory_region_init_io(&dev->mmio, &assigned_dev_msix_mmio_ops, dev, - "assigned-dev-msix", MSIX_PAGE_SIZE); - return 0; -} - -static void assigned_dev_unregister_msix_mmio(AssignedDevice *dev) -{ - if (!dev->msix_table) { - return; - } - - memory_region_destroy(&dev->mmio); - - if (munmap(dev->msix_table, MSIX_PAGE_SIZE) == -1) { - error_report("error unmapping msix_table! %s", strerror(errno)); - } - dev->msix_table = NULL; -} - -static const VMStateDescription vmstate_assigned_device = { - .name = "pci-assign", - .unmigratable = 1, -}; - -static void reset_assigned_device(DeviceState *dev) -{ - PCIDevice *pci_dev = DO_UPCAST(PCIDevice, qdev, dev); - AssignedDevice *adev = DO_UPCAST(AssignedDevice, dev, pci_dev); - char reset_file[64]; - const char reset[] = "1"; - int fd, ret; - - /* - * If a guest is reset without being shutdown, MSI/MSI-X can still - * be running. We want to return the device to a known state on - * reset, so disable those here. We especially do not want MSI-X - * enabled since it lives in MMIO space, which is about to get - * disabled. - */ - if (adev->assigned_irq_type == ASSIGNED_IRQ_MSIX) { - uint16_t ctrl = pci_get_word(pci_dev->config + - pci_dev->msix_cap + PCI_MSIX_FLAGS); - - pci_set_word(pci_dev->config + pci_dev->msix_cap + PCI_MSIX_FLAGS, - ctrl & ~PCI_MSIX_FLAGS_ENABLE); - assigned_dev_update_msix(pci_dev); - } else if (adev->assigned_irq_type == ASSIGNED_IRQ_MSI) { - uint8_t ctrl = pci_get_byte(pci_dev->config + - pci_dev->msi_cap + PCI_MSI_FLAGS); - - pci_set_byte(pci_dev->config + pci_dev->msi_cap + PCI_MSI_FLAGS, - ctrl & ~PCI_MSI_FLAGS_ENABLE); - assigned_dev_update_msi(pci_dev); - } - - snprintf(reset_file, sizeof(reset_file), - "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/reset", - adev->host.domain, adev->host.bus, adev->host.slot, - adev->host.function); - - /* - * Issue a device reset via pci-sysfs. Note that we use write(2) here - * and ignore the return value because some kernels have a bug that - * returns 0 rather than bytes written on success, sending us into an - * infinite retry loop using other write mechanisms. - */ - fd = open(reset_file, O_WRONLY); - if (fd != -1) { - ret = write(fd, reset, strlen(reset)); - (void)ret; - close(fd); - } - - /* - * When a 0 is written to the bus master register, the device is logically - * disconnected from the PCI bus. This avoids further DMA transfers. - */ - assigned_dev_pci_write_config(pci_dev, PCI_COMMAND, 0, 1); -} - -static int assigned_initfn(struct PCIDevice *pci_dev) -{ - AssignedDevice *dev = DO_UPCAST(AssignedDevice, dev, pci_dev); - uint8_t e_intx; - int r; - - if (!kvm_enabled()) { - error_report("pci-assign: error: requires KVM support"); - return -1; - } - - if (!dev->host.domain && !dev->host.bus && !dev->host.slot && - !dev->host.function) { - error_report("pci-assign: error: no host device specified"); - return -1; - } - - /* - * Set up basic config space access control. Will be further refined during - * device initialization. - */ - assigned_dev_emulate_config_read(dev, 0, PCI_CONFIG_SPACE_SIZE); - assigned_dev_direct_config_read(dev, PCI_STATUS, 2); - assigned_dev_direct_config_read(dev, PCI_REVISION_ID, 1); - assigned_dev_direct_config_read(dev, PCI_CLASS_PROG, 3); - assigned_dev_direct_config_read(dev, PCI_CACHE_LINE_SIZE, 1); - assigned_dev_direct_config_read(dev, PCI_LATENCY_TIMER, 1); - assigned_dev_direct_config_read(dev, PCI_BIST, 1); - assigned_dev_direct_config_read(dev, PCI_CARDBUS_CIS, 4); - assigned_dev_direct_config_read(dev, PCI_SUBSYSTEM_VENDOR_ID, 2); - assigned_dev_direct_config_read(dev, PCI_SUBSYSTEM_ID, 2); - assigned_dev_direct_config_read(dev, PCI_CAPABILITY_LIST + 1, 7); - assigned_dev_direct_config_read(dev, PCI_MIN_GNT, 1); - assigned_dev_direct_config_read(dev, PCI_MAX_LAT, 1); - memcpy(dev->emulate_config_write, dev->emulate_config_read, - sizeof(dev->emulate_config_read)); - - if (get_real_device(dev, dev->host.domain, dev->host.bus, - dev->host.slot, dev->host.function)) { - error_report("pci-assign: Error: Couldn't get real device (%s)!", - dev->dev.qdev.id); - goto out; - } - - if (assigned_device_pci_cap_init(pci_dev) < 0) { - goto out; - } - - /* intercept MSI-X entry page in the MMIO */ - if (dev->cap.available & ASSIGNED_DEVICE_CAP_MSIX) { - if (assigned_dev_register_msix_mmio(dev)) { - goto out; - } - } - - /* handle real device's MMIO/PIO BARs */ - if (assigned_dev_register_regions(dev->real_device.regions, - dev->real_device.region_number, - dev)) { - goto out; - } - - /* handle interrupt routing */ - e_intx = dev->dev.config[PCI_INTERRUPT_PIN] - 1; - dev->intpin = e_intx; - dev->intx_route.mode = PCI_INTX_DISABLED; - dev->intx_route.irq = -1; - - /* assign device to guest */ - r = assign_device(dev); - if (r < 0) { - goto out; - } - - /* assign legacy INTx to the device */ - r = assign_intx(dev); - if (r < 0) { - goto assigned_out; - } - - assigned_dev_load_option_rom(dev); - - add_boot_device_path(dev->bootindex, &pci_dev->qdev, NULL); - - return 0; - -assigned_out: - deassign_device(dev); -out: - free_assigned_device(dev); - return -1; -} - -static void assigned_exitfn(struct PCIDevice *pci_dev) -{ - AssignedDevice *dev = DO_UPCAST(AssignedDevice, dev, pci_dev); - - deassign_device(dev); - free_assigned_device(dev); -} - -static Property assigned_dev_properties[] = { - DEFINE_PROP_PCI_HOST_DEVADDR("host", AssignedDevice, host), - DEFINE_PROP_BIT("prefer_msi", AssignedDevice, features, - ASSIGNED_DEVICE_PREFER_MSI_BIT, false), - DEFINE_PROP_BIT("share_intx", AssignedDevice, features, - ASSIGNED_DEVICE_SHARE_INTX_BIT, true), - DEFINE_PROP_INT32("bootindex", AssignedDevice, bootindex, -1), - DEFINE_PROP_STRING("configfd", AssignedDevice, configfd_name), - DEFINE_PROP_END_OF_LIST(), -}; - -static void assign_class_init(ObjectClass *klass, void *data) -{ - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - k->init = assigned_initfn; - k->exit = assigned_exitfn; - k->config_read = assigned_dev_pci_read_config; - k->config_write = assigned_dev_pci_write_config; - dc->props = assigned_dev_properties; - dc->vmsd = &vmstate_assigned_device; - dc->reset = reset_assigned_device; - dc->desc = "KVM-based PCI passthrough"; -} - -static const TypeInfo assign_info = { - .name = "kvm-pci-assign", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(AssignedDevice), - .class_init = assign_class_init, -}; - -static void assign_register_types(void) -{ - type_register_static(&assign_info); -} - -type_init(assign_register_types) - -/* - * Scan the assigned devices for the devices that have an option ROM, and then - * load the corresponding ROM data to RAM. If an error occurs while loading an - * option ROM, we just ignore that option ROM and continue with the next one. - */ -static void assigned_dev_load_option_rom(AssignedDevice *dev) -{ - char name[32], rom_file[64]; - FILE *fp; - uint8_t val; - struct stat st; - void *ptr; - - /* If loading ROM from file, pci handles it */ - if (dev->dev.romfile || !dev->dev.rom_bar) { - return; - } - - snprintf(rom_file, sizeof(rom_file), - "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/rom", - dev->host.domain, dev->host.bus, dev->host.slot, - dev->host.function); - - if (stat(rom_file, &st)) { - return; - } - - if (access(rom_file, F_OK)) { - error_report("pci-assign: Insufficient privileges for %s", rom_file); - return; - } - - /* Write "1" to the ROM file to enable it */ - fp = fopen(rom_file, "r+"); - if (fp == NULL) { - return; - } - val = 1; - if (fwrite(&val, 1, 1, fp) != 1) { - goto close_rom; - } - fseek(fp, 0, SEEK_SET); - - snprintf(name, sizeof(name), "%s.rom", - object_get_typename(OBJECT(dev))); - memory_region_init_ram(&dev->dev.rom, name, st.st_size); - vmstate_register_ram(&dev->dev.rom, &dev->dev.qdev); - ptr = memory_region_get_ram_ptr(&dev->dev.rom); - memset(ptr, 0xff, st.st_size); - - if (!fread(ptr, 1, st.st_size, fp)) { - error_report("pci-assign: Cannot read from host %s", rom_file); - error_printf("Device option ROM contents are probably invalid " - "(check dmesg).\nSkip option ROM probe with rombar=0, " - "or load from file with romfile=\n"); - memory_region_destroy(&dev->dev.rom); - goto close_rom; - } - - pci_register_bar(&dev->dev, PCI_ROM_SLOT, 0, &dev->dev.rom); - dev->dev.has_rom = true; -close_rom: - /* Write "0" to disable ROM */ - fseek(fp, 0, SEEK_SET); - val = 0; - if (!fwrite(&val, 1, 1, fp)) { - DEBUG("%s\n", "Failed to disable pci-sysfs rom file"); - } - fclose(fp); -} |