aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitmodules2
-rw-r--r--Makefile.objs5
-rw-r--r--Makefile.target6
-rw-r--r--blockdev.c6
-rwxr-xr-xconfigure26
-rw-r--r--hmp-commands.hx13
-rw-r--r--hw/acpi_piix4.c79
-rw-r--r--hw/device-assignment.c1969
-rw-r--r--hw/device-assignment.h33
-rw-r--r--hw/i8254_common.c2
-rw-r--r--hw/msi.c127
-rw-r--r--hw/msi.h1
-rw-r--r--hw/msix.c200
-rw-r--r--hw/msix.h2
-rw-r--r--hw/pc.c22
-rw-r--r--hw/pc.h7
-rw-r--r--hw/pc_piix.c15
-rw-r--r--hw/pci.c95
-rw-r--r--hw/pci.h24
-rw-r--r--hw/piix_pci.c10
-rw-r--r--hw/testdev.c154
-rw-r--r--hw/vga_int.h6
-rw-r--r--hw/virtio-pci.c76
-rw-r--r--kvm-all.c38
-rw-r--r--kvm-stub.c43
-rw-r--r--kvm.h30
-rw-r--r--monitor.c21
-rw-r--r--pc-bios/vgabios-cirrus.binbin35840 -> 35840 bytes
-rw-r--r--pc-bios/vgabios-stdvga.binbin40448 -> 40448 bytes
-rw-r--r--pc-bios/vgabios-vmware.binbin40448 -> 40448 bytes
-rw-r--r--pc-bios/vgabios.binbin40448 -> 40448 bytes
-rw-r--r--qemu-config.c4
-rw-r--r--qemu-kvm.c317
-rw-r--r--qemu-kvm.h112
-rw-r--r--qemu-options.hx16
m---------roms/vgabios0
-rwxr-xr-xscripts/qemu-kvm/make-release81
-rw-r--r--sysemu.h3
-rw-r--r--target-i386/cpu.h1
-rw-r--r--vl.c35
40 files changed, 3553 insertions, 28 deletions
diff --git a/.gitmodules b/.gitmodules
index eca876f85..533dcad8e 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,6 +1,6 @@
[submodule "roms/vgabios"]
path = roms/vgabios
- url = git://git.qemu.org/vgabios.git/
+ url = git://git.kernel.org/pub/scm/virt/kvm/vgabios.git/
[submodule "roms/seabios"]
path = roms/seabios
url = git://git.qemu.org/seabios.git/
diff --git a/Makefile.objs b/Makefile.objs
index 70c5c79a6..264f1fe5b 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -213,7 +213,7 @@ hw-obj-$(CONFIG_VIRTIO) += virtio-console.o
hw-obj-y += usb/libhw.o
hw-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o
hw-obj-y += fw_cfg.o
-hw-obj-$(CONFIG_PCI) += pci.o pci_bridge.o pci_bridge_dev.o
+hw-obj-$(CONFIG_PCI) += pci_bridge.o pci_bridge_dev.o
hw-obj-$(CONFIG_PCI) += msix.o msi.o
hw-obj-$(CONFIG_PCI) += shpc.o
hw-obj-$(CONFIG_PCI) += slotid_cap.o
@@ -240,7 +240,8 @@ hw-obj-$(CONFIG_USB_OHCI) += usb/hcd-ohci.o
hw-obj-$(CONFIG_USB_EHCI) += usb/hcd-ehci.o
hw-obj-$(CONFIG_USB_XHCI) += usb/hcd-xhci.o
hw-obj-$(CONFIG_FDC) += fdc.o
-hw-obj-$(CONFIG_ACPI) += acpi.o acpi_piix4.o
+# needs fixes for cpu hotplug, so moved to Makefile.target:
+# hw-obj-$(CONFIG_ACPI) += acpi.o acpi_piix4.o
hw-obj-$(CONFIG_APM) += pm_smbus.o apm.o
hw-obj-$(CONFIG_DMA) += dma.o
hw-obj-$(CONFIG_I82374) += i82374.o
diff --git a/Makefile.target b/Makefile.target
index 15829041c..eda863764 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -183,6 +183,7 @@ obj-y = arch_init.o cpus.o monitor.o machine.o gdbstub.o balloon.o ioport.o
# virtio has to be here due to weird dependency between PCI and virtio-net.
# need to fix this properly
obj-$(CONFIG_NO_PCI) += pci-stub.o
+obj-$(CONFIG_PCI) += pci.o
obj-$(CONFIG_VIRTIO) += virtio.o virtio-blk.o virtio-balloon.o virtio-net.o virtio-serial-bus.o
obj-$(CONFIG_VIRTIO) += virtio-scsi.o
obj-y += vhost_net.o
@@ -230,6 +231,10 @@ obj-i386-y += pc_piix.o
obj-i386-y += pc_sysfw.o
obj-i386-$(CONFIG_KVM) += kvm/clock.o kvm/apic.o kvm/i8259.o kvm/ioapic.o kvm/i8254.o
obj-i386-$(CONFIG_SPICE) += qxl.o qxl-logger.o qxl-render.o
+obj-i386-y += testdev.o
+obj-i386-y += acpi.o acpi_piix4.o
+
+obj-i386-$(CONFIG_KVM_DEVICE_ASSIGNMENT) += device-assignment.o
# shared objects
obj-ppc-y = ppc.o ppc_booke.o
@@ -285,6 +290,7 @@ obj-lm32-y += milkymist-vgafb.o
obj-lm32-y += framebuffer.o
obj-mips-y = mips_r4k.o mips_jazz.o mips_malta.o mips_mipssim.o
+obj-mips-y += acpi.o acpi_piix4.o
obj-mips-y += mips_addr.o mips_timer.o mips_int.o
obj-mips-y += gt64xxx.o mc146818rtc.o
obj-mips-$(CONFIG_FULONG) += bonito.o vt82c686.o mips_fulong2e.o
diff --git a/blockdev.c b/blockdev.c
index 67895b25d..0d20460f0 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -434,6 +434,12 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
return NULL;
}
+ if (qemu_opt_get(opts, "boot") != NULL) {
+ fprintf(stderr, "qemu-kvm: boot=on|off is deprecated and will be "
+ "ignored. Future versions will reject this parameter. Please "
+ "update your scripts.\n");
+ }
+
on_write_error = BLOCK_ERR_STOP_ENOSPC;
if ((buf = qemu_opt_get(opts, "werror")) != NULL) {
if (type != IF_IDE && type != IF_SCSI && type != IF_VIRTIO && type != IF_NONE) {
diff --git a/configure b/configure
index 72d16a495..c304f6808 100755
--- a/configure
+++ b/configure
@@ -102,7 +102,17 @@ cc_i386=i386-pc-linux-gnu-gcc
libs_qga=""
debug_info="yes"
-target_list=""
+target_list="x86_64-softmmu"
+
+kvm_version() {
+ local fname="$(dirname "$0")/KVM_VERSION"
+
+ if test -f "$fname"; then
+ cat "$fname"
+ else
+ echo "qemu-kvm-devel"
+ fi
+}
# Default value for a variable defining feature "foo".
# * foo="no" feature will only be used if --enable-foo arg is given
@@ -177,9 +187,10 @@ bsd_user="no"
guest_base=""
uname_release=""
mixemu="no"
+kvm_cap_device_assignment="yes"
aix="no"
blobs="yes"
-pkgversion=""
+pkgversion=" ($(kvm_version))"
pie=""
zero_malloc=""
trace_backend="nop"
@@ -700,6 +711,10 @@ for opt do
;;
--enable-tcg-interpreter) tcg_interpreter="yes"
;;
+ --disable-kvm-device-assignment) kvm_cap_device_assignment="no"
+ ;;
+ --enable-kvm-device-assignment) kvm_cap_device_assignment="yes"
+ ;;
--disable-cap-ng) cap_ng="no"
;;
--enable-cap-ng) cap_ng="yes"
@@ -1055,6 +1070,8 @@ echo " --disable-slirp disable SLIRP userspace network connectivity"
echo " --disable-kvm disable KVM acceleration support"
echo " --enable-kvm enable KVM acceleration support"
echo " --enable-tcg-interpreter enable TCG with bytecode interpreter (TCI)"
+echo " --disable-kvm-device-assignment disable KVM device assignment support"
+echo " --enable-kvm-device-assignment enable KVM device assignment support"
echo " --disable-nptl disable usermode NPTL support"
echo " --enable-nptl enable usermode NPTL support"
echo " --enable-system enable all system emulation targets"
@@ -3015,6 +3032,7 @@ echo "ATTR/XATTR support $attr"
echo "Install blobs $blobs"
echo "KVM support $kvm"
echo "TCG interpreter $tcg_interpreter"
+echo "KVM device assig. $kvm_cap_device_assignment"
echo "fdt support $fdt"
echo "preadv support $preadv"
echo "fdatasync $fdatasync"
@@ -3729,9 +3747,13 @@ case "$target_arch2" in
\( "$target_arch2" = "x86_64" -a "$cpu" = "i386" \) -o \
\( "$target_arch2" = "i386" -a "$cpu" = "x86_64" \) \) ; then
echo "CONFIG_KVM=y" >> $config_target_mak
+ echo "CONFIG_KVM_OPTIONS=y" >> $config_host_mak
if test "$vhost_net" = "yes" ; then
echo "CONFIG_VHOST_NET=y" >> $config_target_mak
fi
+ if test $kvm_cap_device_assignment = "yes" ; then
+ echo "CONFIG_KVM_DEVICE_ASSIGNMENT=y" >> $config_target_mak
+ fi
fi
esac
if test "$target_arch2" = "ppc64" -a "$fdt" = "yes"; then
diff --git a/hmp-commands.hx b/hmp-commands.hx
index 18cb415ac..969f51208 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -1268,6 +1268,19 @@ Set the encrypted device @var{device} password to @var{password}
ETEXI
{
+ .name = "cpu_set",
+ .args_type = "cpu:i,state:s",
+ .params = "cpu [online|offline]",
+ .help = "change cpu state",
+ .mhandler.cmd = do_cpu_set_nr,
+ },
+
+STEXI
+@item cpu_set @var{cpu} [online|offline]
+Set CPU @var{cpu} online or offline.
+ETEXI
+
+ {
.name = "set_password",
.args_type = "protocol:s,password:s,connected:s?",
.params = "protocol password action-if-connected",
diff --git a/hw/acpi_piix4.c b/hw/acpi_piix4.c
index 0345490ee..1fbc3149b 100644
--- a/hw/acpi_piix4.c
+++ b/hw/acpi_piix4.c
@@ -39,14 +39,20 @@
#define ACPI_DBG_IO_ADDR 0xb044
#define GPE_BASE 0xafe0
+#define PROC_BASE 0xaf00
#define GPE_LEN 4
#define PCI_UP_BASE 0xae00
#define PCI_DOWN_BASE 0xae04
#define PCI_EJ_BASE 0xae08
#define PCI_RMV_BASE 0xae0c
+#define PIIX4_CPU_HOTPLUG_STATUS 4
#define PIIX4_PCI_HOTPLUG_STATUS 2
+struct gpe_regs {
+ uint8_t cpus_sts[32];
+};
+
struct pci_status {
uint32_t up; /* deprecated, maintained for migration compatibility */
uint32_t down;
@@ -68,6 +74,7 @@ typedef struct PIIX4PMState {
Notifier machine_ready;
/* for pci hotplug */
+ struct gpe_regs gpe_cpu;
struct pci_status pci0_status;
uint32_t pci0_hotplug_enable;
uint32_t pci0_slot_device_present;
@@ -229,10 +236,9 @@ static int vmstate_acpi_post_load(void *opaque, int version_id)
{ \
.name = (stringify(_field)), \
.version_id = 0, \
- .num = GPE_LEN, \
.info = &vmstate_info_uint16, \
.size = sizeof(uint16_t), \
- .flags = VMS_ARRAY | VMS_POINTER, \
+ .flags = VMS_SINGLE | VMS_POINTER, \
.offset = vmstate_offset_pointer(_state, _field, uint8_t), \
}
@@ -374,11 +380,16 @@ static void piix4_pm_machine_ready(Notifier *n, void *opaque)
}
+static PIIX4PMState *global_piix4_pm_state; /* cpu hotadd */
+
static int piix4_pm_initfn(PCIDevice *dev)
{
PIIX4PMState *s = DO_UPCAST(PIIX4PMState, dev, dev);
uint8_t *pci_conf;
+ /* for cpu hotadd */
+ global_piix4_pm_state = s;
+
pci_conf = s->dev.config;
pci_conf[0x06] = 0x80;
pci_conf[0x07] = 0x02;
@@ -481,7 +492,16 @@ type_init(piix4_pm_register_types)
static uint32_t gpe_readb(void *opaque, uint32_t addr)
{
PIIX4PMState *s = opaque;
- uint32_t val = acpi_gpe_ioport_readb(&s->ar, addr);
+ uint32_t val = 0;
+ struct gpe_regs *g = &s->gpe_cpu;
+
+ switch (addr) {
+ case PROC_BASE ... PROC_BASE+31:
+ val = g->cpus_sts[addr - PROC_BASE];
+ break;
+ default:
+ val = acpi_gpe_ioport_readb(&s->ar, addr);
+ }
PIIX4_DPRINTF("gpe read %x == %x\n", addr, val);
return val;
@@ -540,16 +560,27 @@ static uint32_t pcirmv_read(void *opaque, uint32_t addr)
return s->pci0_hotplug_enable;
}
+extern const char *global_cpu_model;
+
static int piix4_device_hotplug(DeviceState *qdev, PCIDevice *dev,
PCIHotplugState state);
static void piix4_acpi_system_hot_add_init(PCIBus *bus, PIIX4PMState *s)
{
+ int i = 0, cpus = smp_cpus;
+
+ while (cpus > 0) {
+ s->gpe_cpu.cpus_sts[i++] = (cpus < 8) ? (1 << cpus) - 1 : 0xff;
+ cpus -= 8;
+ }
register_ioport_write(GPE_BASE, GPE_LEN, 1, gpe_writeb, s);
register_ioport_read(GPE_BASE, GPE_LEN, 1, gpe_readb, s);
acpi_gpe_blk(&s->ar, GPE_BASE);
+ register_ioport_write(PROC_BASE, 32, 1, gpe_writeb, s);
+ register_ioport_read(PROC_BASE, 32, 1, gpe_readb, s);
+
register_ioport_read(PCI_UP_BASE, 4, 4, pci_up_read, s);
register_ioport_read(PCI_DOWN_BASE, 4, 4, pci_down_read, s);
@@ -561,6 +592,48 @@ static void piix4_acpi_system_hot_add_init(PCIBus *bus, PIIX4PMState *s)
pci_bus_hotplug(bus, piix4_device_hotplug, &s->dev.qdev);
}
+#if defined(TARGET_I386)
+static void enable_processor(PIIX4PMState *s, int cpu)
+{
+ struct gpe_regs *g = &s->gpe_cpu;
+ ACPIGPE *gpe = &s->ar.gpe;
+
+ *gpe->sts = *gpe->sts | PIIX4_CPU_HOTPLUG_STATUS;
+ g->cpus_sts[cpu/8] |= (1 << (cpu%8));
+}
+
+static void disable_processor(PIIX4PMState *s, int cpu)
+{
+ struct gpe_regs *g = &s->gpe_cpu;
+ ACPIGPE *gpe = &s->ar.gpe;
+
+ *gpe->sts = *gpe->sts | PIIX4_CPU_HOTPLUG_STATUS;
+ g->cpus_sts[cpu/8] &= ~(1 << (cpu%8));
+}
+
+void qemu_system_cpu_hot_add(int cpu, int state)
+{
+ CPUArchState *env;
+ PIIX4PMState *s = global_piix4_pm_state;
+
+ if (state && !qemu_get_cpu(cpu)) {
+ env = pc_new_cpu(global_cpu_model);
+ if (!env) {
+ fprintf(stderr, "cpu %d creation failed\n", cpu);
+ return;
+ }
+ env->cpuid_apic_id = cpu;
+ }
+
+ if (state)
+ enable_processor(s, cpu);
+ else
+ disable_processor(s, cpu);
+
+ pm_update_sci(s);
+}
+#endif
+
static void enable_device(PIIX4PMState *s, int slot)
{
s->ar.gpe.sts[0] |= PIIX4_PCI_HOTPLUG_STATUS;
diff --git a/hw/device-assignment.c b/hw/device-assignment.c
new file mode 100644
index 000000000..92710027e
--- /dev/null
+++ b/hw/device-assignment.c
@@ -0,0 +1,1969 @@
+/*
+ * Copyright (c) 2007, Neocleus Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ *
+ *
+ * Assign a PCI device from the host to a guest VM.
+ *
+ * Adapted for KVM by Qumranet.
+ *
+ * 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 "qemu-kvm.h"
+#include "hw.h"
+#include "pc.h"
+#include "qemu-error.h"
+#include "console.h"
+#include "device-assignment.h"
+#include "loader.h"
+#include "monitor.h"
+#include "range.h"
+#include "sysemu.h"
+#include "pci.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 DEVICE_ASSIGNMENT_DEBUG 1 */
+
+#ifdef DEVICE_ASSIGNMENT_DEBUG
+#define DEBUG(fmt, ...) \
+ do { \
+ fprintf(stderr, "%s: " fmt, __func__ , __VA_ARGS__); \
+ } while (0)
+#else
+#define DEBUG(fmt, ...) do { } while(0)
+#endif
+
+typedef struct PCIHostDevice {
+ int seg;
+ int bus;
+ int dev;
+ int func;
+} PCIHostDevice;
+
+typedef struct {
+ int type; /* Memory or port I/O */
+ int valid;
+ uint32_t base_addr;
+ uint32_t size; /* size of the region */
+ int resource_fd;
+} PCIRegion;
+
+typedef struct {
+ 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 {
+ MemoryRegion container;
+ MemoryRegion real_iomem;
+ union {
+ void *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 {
+ uint32_t addr_lo;
+ uint32_t addr_hi;
+ uint32_t data;
+ uint32_t ctrl;
+} MSIXTableEntry;
+
+typedef struct AssignedDevice {
+ PCIDevice dev;
+ PCIHostDevice host;
+ uint32_t features;
+ int intpin;
+ uint8_t debug_flags;
+ AssignedDevRegion v_addrs[PCI_NUM_REGIONS - 1];
+ PCIDevRegions real_device;
+ int run;
+ int girq;
+ uint16_t h_segnr;
+ uint8_t h_busnr;
+ uint8_t h_devfn;
+ int irq_requested_type;
+ int bound;
+ 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 irq_entries_nr;
+ struct kvm_irq_routing_entry *entry;
+ MSIXTableEntry *msix_table;
+ target_phys_addr_t msix_table_addr;
+ uint16_t msix_max;
+ MemoryRegion mmio;
+ char *configfd_name;
+ int32_t bootindex;
+ QLIST_ENTRY(AssignedDevice) next;
+} AssignedDevice;
+
+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,
+ target_phys_addr_t 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=%lx, size=%d, e_phys=%lx, addr=%lx\n",
+ *data, size, addr, addr);
+ if (pwrite(fd, data, size, addr) != size) {
+ fprintf(stderr, "%s - pwrite failed %s\n",
+ __func__, strerror(errno));
+ }
+ } else {
+ if (pread(fd, &val, size, addr) != size) {
+ fprintf(stderr, "%s - pread failed %s\n",
+ __func__, strerror(errno));
+ val = (1UL << (size * 8)) - 1;
+ }
+ DEBUG("pread val=%lx, size=%d, e_phys=%lx, addr=%lx\n",
+ val, size, addr, addr);
+ }
+ } else {
+ uint32_t port = addr + dev_region->u.r_baseport;
+
+ if (data) {
+ DEBUG("out data=%lx, size=%d, e_phys=%lx, 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=%lx, size=%d, e_phys=%lx, host=%x\n",
+ val, size, addr, port);
+ }
+ }
+ return val;
+}
+
+static void assigned_dev_ioport_write(void *opaque, target_phys_addr_t addr,
+ uint64_t data, unsigned size)
+{
+ assigned_dev_ioport_rw(opaque, addr, size, &data);
+}
+
+static uint64_t assigned_dev_ioport_read(void *opaque,
+ target_phys_addr_t addr, unsigned size)
+{
+ return assigned_dev_ioport_rw(opaque, addr, size, NULL);
+}
+
+static uint32_t slow_bar_readb(void *opaque, target_phys_addr_t 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, target_phys_addr_t addr)
+{
+ AssignedDevRegion *d = opaque;
+ uint16_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_readl(void *opaque, target_phys_addr_t addr)
+{
+ AssignedDevRegion *d = opaque;
+ uint32_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 void slow_bar_writeb(void *opaque, target_phys_addr_t 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, target_phys_addr_t addr, uint32_t val)
+{
+ AssignedDevRegion *d = opaque;
+ uint16_t *out = 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, target_phys_addr_t addr, uint32_t val)
+{
+ AssignedDevRegion *d = opaque;
+ uint32_t *out = 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(&region->container, "assigned-dev-container",
+ e_size);
+ memory_region_add_subregion(&region->container, 0, &region->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) {
+ int offset = r_dev->msix_table_addr - real_region->base_addr;
+
+ memory_region_add_subregion_overlap(&region->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(&region->container, "assigned-dev-container", size);
+ memory_region_init_io(&region->real_iomem, &assigned_dev_ioport_ops,
+ r_dev->v_addrs + region_num,
+ "assigned-dev-iomem", size);
+ memory_region_add_subregion(&region->container, 0, &region->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;
+
+ fprintf(stderr, "%s: pread failed, ret = %zd errno = %d\n",
+ __func__, ret, errno);
+
+ exit(1);
+ }
+
+ 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;
+
+ fprintf(stderr, "%s: pwrite failed, ret = %zd errno = %d\n",
+ __func__, ret, errno);
+
+ exit(1);
+ }
+
+ return;
+}
+
+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 = cur_region->type & IORESOURCE_PREFETCH
+ ? PCI_BASE_ADDRESS_MEM_PREFETCH
+ : PCI_BASE_ADDRESS_SPACE_MEMORY;
+
+ /* 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;
+ fprintf(stderr, "%s: Error: Couldn't mmap 0x%x!"
+ "\n", __func__,
+ (uint32_t) (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) {
+ fprintf(stderr, "PCI region %d at address 0x%llx "
+ "has size 0x%x, which is not a multiple of 4K. "
+ "You might experience some performance hit "
+ "due to that.\n",
+ i, (unsigned long long)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) {
+ fprintf(stderr, "Unexpected return from I/O port read: %d\n",
+ ret);
+ abort();
+ } else if (errno != EINVAL) {
+ fprintf(stderr, "Kernel doesn't support ioport resource "
+ "access, hiding this region.\n");
+ 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) {
+ fprintf(stderr, "%s: %s: %m\n", __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;
+ unsigned long long 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) {
+ if (qemu_isdigit(pci_dev->configfd_name[0])) {
+ dev->config_fd = strtol(pci_dev->configfd_name, NULL, 0);
+ } else {
+ dev->config_fd = monitor_get_fd(cur_mon, pci_dev->configfd_name);
+ if (dev->config_fd < 0) {
+ fprintf(stderr, "%s: (%s) unkown\n", __func__,
+ pci_dev->configfd_name);
+ return 1;
+ }
+ }
+ } else {
+ dev->config_fd = open(name, O_RDWR);
+
+ if (dev->config_fd == -1) {
+ fprintf(stderr, "%s: %s: %m\n", __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;
+ fprintf(stderr, "%s: read failed, errno = %d\n", __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) {
+ fprintf(stderr, "%s: %s: %m\n", __func__, name);
+ return 1;
+ }
+
+ for (r = 0; r < PCI_ROM_SLOT; r++) {
+ if (fscanf(f, "%lli %lli %lli\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;
+ 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 %d start 0x%llx 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 QLIST_HEAD(, AssignedDevice) devs = QLIST_HEAD_INITIALIZER(devs);
+
+static void free_dev_irq_entries(AssignedDevice *dev)
+{
+ int i;
+
+ for (i = 0; i < dev->irq_entries_nr; i++) {
+ if (dev->entry[i].type) {
+ kvm_del_routing_entry(&dev->entry[i]);
+ }
+ }
+ g_free(dev->entry);
+ dev->entry = NULL;
+ dev->irq_entries_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) {
+ memory_region_del_subregion(&region->container,
+ &region->real_iomem);
+ memory_region_destroy(&region->real_iomem);
+ memory_region_destroy(&region->container);
+ } else if (pci_region->type & IORESOURCE_MEM) {
+ if (region->u.r_virtbase) {
+ memory_region_del_subregion(&region->container,
+ &region->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(&region->container,
+ &dev->mmio);
+ }
+
+ memory_region_destroy(&region->real_iomem);
+ memory_region_destroy(&region->container);
+ if (munmap(region->u.r_virtbase,
+ (pci_region->size + 0xFFF) & 0xFFFFF000)) {
+ fprintf(stderr,
+ "Failed to unmap assigned device region: %s\n",
+ 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_dev_irq_entries(dev);
+}
+
+static uint32_t calc_assigned_dev_id(AssignedDevice *dev)
+{
+ return (uint32_t)dev->h_segnr << 16 | (uint32_t)dev->h_busnr << 8 |
+ (uint32_t)dev->h_devfn;
+}
+
+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;
+
+ sprintf(dir, "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/",
+ dev->host.seg, dev->host.bus, dev->host.dev, dev->host.func);
+
+ sprintf(name, "%sdriver", dir);
+
+ r = readlink(name, driver, sizeof(driver));
+ if ((r <= 0) || r >= sizeof(driver) || !(ns = strrchr(driver, '/'))) {
+ goto fail;
+ }
+
+ ns++;
+
+ if (get_real_vendor_id(dir, &vendor_id) ||
+ get_real_device_id(dir, &device_id)) {
+ goto fail;
+ }
+
+ fprintf(stderr, "*** The driver '%s' is occupying your device "
+ "%04x:%02x:%02x.%x.\n",
+ ns, dev->host.seg, dev->host.bus, dev->host.dev, dev->host.func);
+ fprintf(stderr, "***\n");
+ fprintf(stderr, "*** You can try the following commands to free it:\n");
+ fprintf(stderr, "***\n");
+ fprintf(stderr, "*** $ echo \"%04x %04x\" > /sys/bus/pci/drivers/pci-stub/"
+ "new_id\n", vendor_id, device_id);
+ fprintf(stderr, "*** $ echo \"%04x:%02x:%02x.%x\" > /sys/bus/pci/drivers/"
+ "%s/unbind\n",
+ dev->host.seg, dev->host.bus, dev->host.dev, dev->host.func, ns);
+ fprintf(stderr, "*** $ echo \"%04x:%02x:%02x.%x\" > /sys/bus/pci/drivers/"
+ "pci-stub/bind\n",
+ dev->host.seg, dev->host.bus, dev->host.dev, dev->host.func);
+ fprintf(stderr, "*** $ echo \"%04x %04x\" > /sys/bus/pci/drivers/pci-stub"
+ "/remove_id\n", vendor_id, device_id);
+ fprintf(stderr, "***\n");
+
+ return;
+
+fail:
+ fprintf(stderr, "Couldn't find out why.\n");
+}
+
+static int assign_device(AssignedDevice *dev)
+{
+ struct kvm_assigned_pci_dev assigned_dev_data;
+ int r;
+
+ /* Only pass non-zero PCI segment to capable module */
+ if (!kvm_check_extension(kvm_state, KVM_CAP_PCI_SEGMENT) &&
+ dev->h_segnr) {
+ fprintf(stderr, "Can't assign device inside non-zero PCI segment "
+ "as this KVM module doesn't support it.\n");
+ return -ENODEV;
+ }
+
+ memset(&assigned_dev_data, 0, sizeof(assigned_dev_data));
+ assigned_dev_data.assigned_dev_id = calc_assigned_dev_id(dev);
+ assigned_dev_data.segnr = dev->h_segnr;
+ assigned_dev_data.busnr = dev->h_busnr;
+ assigned_dev_data.devfn = dev->h_devfn;
+
+ assigned_dev_data.flags = KVM_DEV_ASSIGN_ENABLE_IOMMU;
+ if (!kvm_check_extension(kvm_state, KVM_CAP_IOMMU)) {
+ fprintf(stderr, "No IOMMU found. Unable to assign device \"%s\"\n",
+ dev->dev.qdev.id);
+ return -ENODEV;
+ }
+
+ if (dev->features & ASSIGNED_DEVICE_SHARE_INTX_MASK &&
+ kvm_has_intx_set_mask()) {
+ assigned_dev_data.flags |= KVM_DEV_ASSIGN_PCI_2_3;
+ }
+
+ r = kvm_assign_pci_device(kvm_state, &assigned_dev_data);
+ if (r < 0) {
+ fprintf(stderr, "Failed to assign device \"%s\" : %s\n",
+ dev->dev.qdev.id, strerror(-r));
+
+ switch (r) {
+ case -EBUSY:
+ assign_failed_examine(dev);
+ break;
+ default:
+ break;
+ }
+ }
+ return r;
+}
+
+static int assign_irq(AssignedDevice *dev)
+{
+ struct kvm_assigned_irq assigned_irq_data;
+ int irq, r = 0;
+
+ /* Interrupt PIN 0 means don't use INTx */
+ if (assigned_dev_pci_read_byte(&dev->dev, PCI_INTERRUPT_PIN) == 0)
+ return 0;
+
+ irq = pci_map_irq(&dev->dev, dev->intpin);
+ irq = piix_get_irq(irq);
+
+ if (dev->girq == irq)
+ return r;
+
+ memset(&assigned_irq_data, 0, sizeof(assigned_irq_data));
+ assigned_irq_data.assigned_dev_id = calc_assigned_dev_id(dev);
+ assigned_irq_data.guest_irq = irq;
+ if (dev->irq_requested_type) {
+ assigned_irq_data.flags = dev->irq_requested_type;
+ r = kvm_deassign_irq(kvm_state, &assigned_irq_data);
+ if (r) {
+ perror("assign_irq: deassign");
+ }
+ dev->irq_requested_type = 0;
+ }
+
+retry:
+ assigned_irq_data.flags = KVM_DEV_IRQ_GUEST_INTX;
+ if (dev->features & ASSIGNED_DEVICE_PREFER_MSI_MASK &&
+ dev->cap.available & ASSIGNED_DEVICE_CAP_MSI)
+ assigned_irq_data.flags |= KVM_DEV_IRQ_HOST_MSI;
+ else
+ assigned_irq_data.flags |= KVM_DEV_IRQ_HOST_INTX;
+
+ r = kvm_assign_irq(kvm_state, &assigned_irq_data);
+ 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. */
+ fprintf(stderr,
+ "Host-side INTx sharing not supported, "
+ "using MSI instead.\n"
+ "Some devices do not to work properly in this mode.\n");
+ dev->features |= ASSIGNED_DEVICE_PREFER_MSI_MASK;
+ goto retry;
+ }
+ fprintf(stderr, "Failed to assign irq for \"%s\": %s\n",
+ dev->dev.qdev.id, strerror(-r));
+ fprintf(stderr, "Perhaps you are assigning a device "
+ "that shares an IRQ with another device?\n");
+ return r;
+ }
+
+ dev->girq = irq;
+ dev->irq_requested_type = assigned_irq_data.flags;
+ return r;
+}
+
+static void deassign_device(AssignedDevice *dev)
+{
+ struct kvm_assigned_pci_dev assigned_dev_data;
+ int r;
+
+ memset(&assigned_dev_data, 0, sizeof(assigned_dev_data));
+ assigned_dev_data.assigned_dev_id = calc_assigned_dev_id(dev);
+
+ r = kvm_deassign_pci_device(kvm_state, &assigned_dev_data);
+ if (r < 0)
+ fprintf(stderr, "Failed to deassign device \"%s\" : %s\n",
+ dev->dev.qdev.id, strerror(-r));
+}
+
+#if 0
+AssignedDevInfo *get_assigned_device(int pcibus, int slot)
+{
+ AssignedDevice *assigned_dev = NULL;
+ AssignedDevInfo *adev = NULL;
+
+ QLIST_FOREACH(adev, &adev_head, next) {
+ assigned_dev = adev->assigned_dev;
+ if (pci_bus_num(assigned_dev->dev.bus) == pcibus &&
+ PCI_SLOT(assigned_dev->dev.devfn) == slot)
+ return adev;
+ }
+
+ return NULL;
+}
+#endif
+
+/* The pci config space got updated. Check if irq numbers have changed
+ * for our devices
+ */
+void assigned_dev_update_irqs(void)
+{
+ AssignedDevice *dev, *next;
+ Error *err = NULL;
+ int r;
+
+ dev = QLIST_FIRST(&devs);
+ while (dev) {
+ next = QLIST_NEXT(dev, next);
+ if (dev->irq_requested_type & KVM_DEV_IRQ_HOST_INTX) {
+ r = assign_irq(dev);
+ if (r < 0) {
+ qdev_unplug(&dev->dev.qdev, &err);
+ assert(!err);
+ }
+ }
+ dev = next;
+ }
+}
+
+static void assigned_dev_update_msi(PCIDevice *pci_dev)
+{
+ struct kvm_assigned_irq assigned_irq_data;
+ 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;
+
+ memset(&assigned_irq_data, 0, sizeof assigned_irq_data);
+ assigned_irq_data.assigned_dev_id = calc_assigned_dev_id(assigned_dev);
+
+ /* 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->irq_requested_type & KVM_DEV_IRQ_GUEST_MSI) ||
+ (ctrl_byte & PCI_MSI_FLAGS_ENABLE)) {
+
+ assigned_irq_data.flags = assigned_dev->irq_requested_type;
+ free_dev_irq_entries(assigned_dev);
+ r = kvm_deassign_irq(kvm_state, &assigned_irq_data);
+ /* -ENXIO means no assigned irq */
+ if (r && r != -ENXIO)
+ perror("assigned_dev_update_msi: deassign irq");
+
+ assigned_dev->irq_requested_type = 0;
+ }
+
+ if (ctrl_byte & PCI_MSI_FLAGS_ENABLE) {
+ uint8_t *pos = pci_dev->config + pci_dev->msi_cap;
+
+ assigned_dev->entry = g_malloc0(sizeof(*(assigned_dev->entry)));
+ assigned_dev->entry->u.msi.address_lo =
+ pci_get_long(pos + PCI_MSI_ADDRESS_LO);
+ assigned_dev->entry->u.msi.address_hi = 0;
+ assigned_dev->entry->u.msi.data = pci_get_word(pos + PCI_MSI_DATA_32);
+ assigned_dev->entry->type = KVM_IRQ_ROUTING_MSI;
+ r = kvm_get_irq_route_gsi();
+ if (r < 0) {
+ perror("assigned_dev_update_msi: kvm_get_irq_route_gsi");
+ return;
+ }
+ assigned_dev->entry->gsi = r;
+
+ kvm_add_routing_entry(kvm_state, assigned_dev->entry);
+ if (kvm_irqchip_commit_routes(kvm_state) < 0) {
+ perror("assigned_dev_update_msi: kvm_commit_irq_routes");
+ assigned_dev->cap.state &= ~ASSIGNED_DEVICE_MSI_ENABLED;
+ return;
+ }
+ assigned_dev->irq_entries_nr = 1;
+
+ assigned_irq_data.guest_irq = assigned_dev->entry->gsi;
+ assigned_irq_data.flags = KVM_DEV_IRQ_HOST_MSI | KVM_DEV_IRQ_GUEST_MSI;
+ if (kvm_assign_irq(kvm_state, &assigned_irq_data) < 0) {
+ perror("assigned_dev_enable_msi: assign irq");
+ }
+
+ assigned_dev->girq = -1;
+ assigned_dev->irq_requested_type = assigned_irq_data.flags;
+ } else {
+ assign_irq(assigned_dev);
+ }
+}
+
+static bool msix_masked(MSIXTableEntry *entry)
+{
+ return (entry->ctrl & cpu_to_le32(0x1)) != 0;
+}
+
+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;
+ struct kvm_assigned_msix_nr msix_nr;
+ struct kvm_assigned_msix_entry msix_entry;
+ MSIXTableEntry *entry = adev->msix_table;
+
+ /* Get the usable entry number for allocating */
+ for (i = 0; i < adev->msix_max; i++, entry++) {
+ if (msix_masked(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;
+ }
+
+ msix_nr.assigned_dev_id = calc_assigned_dev_id(adev);
+ msix_nr.entry_nr = entries_nr;
+ r = kvm_assign_set_msix_nr(kvm_state, &msix_nr);
+ if (r != 0) {
+ fprintf(stderr, "fail to set MSI-X entry number for MSIX! %s\n",
+ strerror(-r));
+ return r;
+ }
+
+ free_dev_irq_entries(adev);
+
+ adev->irq_entries_nr = adev->msix_max;
+ adev->entry = g_malloc0(adev->msix_max * sizeof(*(adev->entry)));
+
+ msix_entry.assigned_dev_id = msix_nr.assigned_dev_id;
+ entry = adev->msix_table;
+ for (i = 0; i < adev->msix_max; i++, entry++) {
+ if (msix_masked(entry)) {
+ continue;
+ }
+
+ r = kvm_get_irq_route_gsi();
+ if (r < 0)
+ return r;
+
+ adev->entry[i].gsi = r;
+ adev->entry[i].type = KVM_IRQ_ROUTING_MSI;
+ adev->entry[i].flags = 0;
+ adev->entry[i].u.msi.address_lo = entry->addr_lo;
+ adev->entry[i].u.msi.address_hi = entry->addr_hi;
+ adev->entry[i].u.msi.data = entry->data;
+
+ DEBUG("MSI-X vector %d, gsi %d, addr %08x_%08x, data %08x\n", i,
+ r, entry->addr_hi, entry->addr_lo, entry->data);
+
+ kvm_add_routing_entry(kvm_state, &adev->entry[i]);
+
+ msix_entry.gsi = adev->entry[i].gsi;
+ msix_entry.entry = i;
+ r = kvm_assign_set_msix_entry(kvm_state, &msix_entry);
+ if (r) {
+ fprintf(stderr, "fail to set MSI-X entry! %s\n", strerror(-r));
+ break;
+ }
+ }
+
+ if (r == 0 && kvm_irqchip_commit_routes(kvm_state) < 0) {
+ perror("assigned_dev_update_msix_mmio: kvm_commit_irq_routes");
+ return -EINVAL;
+ }
+
+ return r;
+}
+
+static void assigned_dev_update_msix(PCIDevice *pci_dev)
+{
+ struct kvm_assigned_irq assigned_irq_data;
+ 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;
+
+ memset(&assigned_irq_data, 0, sizeof assigned_irq_data);
+ assigned_irq_data.assigned_dev_id = calc_assigned_dev_id(assigned_dev);
+
+ /* 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->irq_requested_type & KVM_DEV_IRQ_GUEST_MSIX) ||
+ (ctrl_word & PCI_MSIX_FLAGS_ENABLE)) {
+
+ assigned_irq_data.flags = assigned_dev->irq_requested_type;
+ free_dev_irq_entries(assigned_dev);
+ r = kvm_deassign_irq(kvm_state, &assigned_irq_data);
+ /* -ENXIO means no assigned irq */
+ if (r && r != -ENXIO)
+ perror("assigned_dev_update_msix: deassign irq");
+
+ assigned_dev->irq_requested_type = 0;
+ }
+
+ if (ctrl_word & PCI_MSIX_FLAGS_ENABLE) {
+ assigned_irq_data.flags = KVM_DEV_IRQ_HOST_MSIX |
+ KVM_DEV_IRQ_GUEST_MSIX;
+
+ if (assigned_dev_update_msix_mmio(pci_dev) < 0) {
+ perror("assigned_dev_update_msix_mmio");
+ return;
+ }
+
+ if (assigned_dev->irq_entries_nr) {
+ if (kvm_assign_irq(kvm_state, &assigned_irq_data) < 0) {
+ perror("assigned_dev_enable_msix: assign irq");
+ return;
+ }
+ }
+ assigned_dev->girq = -1;
+ assigned_dev->irq_requested_type = assigned_irq_data.flags;
+ } else {
+ assign_irq(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,
+ calc_assigned_dev_id(assigned_dev),
+ 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)) {
+ dev->cap.available |= ASSIGNED_DEVICE_CAP_MSI;
+ /* Only 32-bit/no-mask currently supported */
+ if ((ret = pci_add_capability(pci_dev, PCI_CAP_ID_MSI, pos, 10)) < 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);
+ /* Would really like to test kvm_check_extension(, KVM_CAP_DEVICE_MSIX),
+ * but the kernel doesn't expose it. Instead do a dummy call to
+ * KVM_ASSIGN_SET_MSIX_NR to see if it exists. */
+ if (pos != 0 && kvm_assign_set_msix_nr(kvm_state, NULL) == -EFAULT) {
+ int bar_nr;
+ uint32_t msix_table_entry;
+
+ dev->cap.available |= ASSIGNED_DEVICE_CAP_MSIX;
+ if ((ret = pci_add_capability(pci_dev, PCI_CAP_ID_MSIX, pos, 12)) < 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 */
+ if ((pos = pci_find_cap_offset(pci_dev, PCI_CAP_ID_PM, 0))) {
+ uint16_t pmc;
+ if ((ret = pci_add_capability(pci_dev, PCI_CAP_ID_PM, pos,
+ PCI_PM_SIZEOF)) < 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);
+ }
+
+ if ((pos = pci_find_cap_offset(pci_dev, PCI_CAP_ID_EXP, 0))) {
+ 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) {
+ fprintf(stderr,
+ "%s: Invalid size PCIe cap-id 0x%x \n",
+ __func__, PCI_CAP_ID_EXP);
+ return -EINVAL;
+ } else if (size != 0x3c) {
+ fprintf(stderr,
+ "WARNING, %s: PCIe cap-id 0x%x has "
+ "non-standard size 0x%x; std size should be 0x3c \n",
+ __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) {
+ fprintf(stderr,
+ "%s: Unsupported PCI express capability version %d\n",
+ __func__, version);
+ return -EINVAL;
+ }
+
+ if ((ret = pci_add_capability(pci_dev, PCI_CAP_ID_EXP,
+ pos, size)) < 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) {
+ fprintf(stderr,
+ "Device assignment only supports endpoint assignment, "
+ "device type %d\n", 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 */
+ }
+ }
+
+ if ((pos = pci_find_cap_offset(pci_dev, PCI_CAP_ID_PCIX, 0))) {
+ uint16_t cmd;
+ uint32_t status;
+
+ /* Only expose the minimum, 8 byte capability */
+ if ((ret = pci_add_capability(pci_dev, PCI_CAP_ID_PCIX, pos, 8)) < 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);
+ }
+
+ if ((pos = pci_find_cap_offset(pci_dev, PCI_CAP_ID_VPD, 0))) {
+ /* Direct R/W passthrough */
+ if ((ret = pci_add_capability(pci_dev, PCI_CAP_ID_VPD, pos, 8)) < 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 */
+ if ((ret = pci_add_capability(pci_dev, PCI_CAP_ID_VNDR,
+ pos, len)) < 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 msix_mmio_read(void *opaque, target_phys_addr_t addr,
+ unsigned size)
+{
+ AssignedDevice *adev = opaque;
+ uint64_t val;
+
+ memcpy(&val, (void *)((uint8_t *)adev->msix_table + addr), size);
+
+ return val;
+}
+
+static void msix_mmio_write(void *opaque, target_phys_addr_t 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((void *)((uint8_t *)adev->msix_table + addr), &val, size);
+
+ if (ctrl & PCI_MSIX_FLAGS_ENABLE) {
+ MSIXTableEntry *entry = &adev->msix_table[i];
+
+ if (!msix_masked(&orig) && 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 (msix_masked(&orig) && !msix_masked(entry)) {
+ /* Vector unmasked */
+ if (i >= adev->irq_entries_nr || !adev->entry[i].type) {
+ /* Previously unassigned vector, start from scratch */
+ assigned_dev_update_msix(pdev);
+ return;
+ } else {
+ /* Update an existing, previously masked vector */
+ struct kvm_irq_routing_entry orig = adev->entry[i];
+ int ret;
+
+ adev->entry[i].u.msi.address_lo = entry->addr_lo;
+ adev->entry[i].u.msi.address_hi = entry->addr_hi;
+ adev->entry[i].u.msi.data = entry->data;
+
+ ret = kvm_update_routing_entry(&orig, &adev->entry[i]);
+ if (ret) {
+ fprintf(stderr,
+ "Error updating irq routing entry (%d)\n", ret);
+ return;
+ }
+
+ ret = kvm_irqchip_commit_routes(kvm_state);
+ if (ret) {
+ fprintf(stderr,
+ "Error committing irq routes (%d)\n", ret);
+ return;
+ }
+ }
+ }
+ }
+}
+
+static const MemoryRegionOps msix_mmio_ops = {
+ .read = msix_mmio_read,
+ .write = 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 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) {
+ fprintf(stderr, "fail allocate msix_table! %s\n", strerror(errno));
+ return -EFAULT;
+ }
+
+ msix_reset(dev);
+
+ memory_region_init_io(&dev->mmio, &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) {
+ fprintf(stderr, "error unmapping msix_table! %s\n",
+ 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->irq_requested_type & KVM_DEV_IRQ_GUEST_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->irq_requested_type & KVM_DEV_IRQ_GUEST_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.seg, adev->host.bus, adev->host.dev, adev->host.func);
+
+ /*
+ * 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.seg && !dev->host.bus && !dev->host.dev && !dev->host.func) {
+ 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.seg, dev->host.bus,
+ dev->host.dev, dev->host.func)) {
+ 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[0x3d] - 1;
+ dev->intpin = e_intx;
+ dev->run = 0;
+ dev->girq = -1;
+ dev->h_segnr = dev->host.seg;
+ dev->h_busnr = dev->host.bus;
+ dev->h_devfn = PCI_DEVFN(dev->host.dev, dev->host.func);
+
+ /* assign device to guest */
+ r = assign_device(dev);
+ if (r < 0)
+ goto out;
+
+ /* assign irq for the device */
+ r = assign_irq(dev);
+ if (r < 0)
+ goto assigned_out;
+
+ assigned_dev_load_option_rom(dev);
+ QLIST_INSERT_HEAD(&devs, dev, next);
+
+ 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 int assigned_exitfn(struct PCIDevice *pci_dev)
+{
+ AssignedDevice *dev = DO_UPCAST(AssignedDevice, dev, pci_dev);
+
+ QLIST_REMOVE(dev, next);
+ deassign_device(dev);
+ free_assigned_device(dev);
+ return 0;
+}
+
+static int parse_hostaddr(DeviceState *dev, Property *prop, const char *str)
+{
+ PCIHostDevice *ptr = qdev_get_prop_ptr(dev, prop);
+ int rc;
+
+ rc = pci_parse_host_devaddr(str, &ptr->seg, &ptr->bus, &ptr->dev, &ptr->func);
+ if (rc != 0)
+ return -1;
+ return 0;
+}
+
+static int print_hostaddr(DeviceState *dev, Property *prop, char *dest, size_t len)
+{
+ PCIHostDevice *ptr = qdev_get_prop_ptr(dev, prop);
+
+ return snprintf(dest, len, "%02x:%02x.%x", ptr->bus, ptr->dev, ptr->func);
+}
+
+PropertyInfo qdev_prop_hostaddr = {
+ .name = "pci-hostaddr",
+ .parse = parse_hostaddr,
+ .print = print_hostaddr,
+};
+
+static Property da_properties[] =
+{
+ DEFINE_PROP("host", AssignedDevice, host, qdev_prop_hostaddr, PCIHostDevice),
+ 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 = da_properties;
+ dc->vmsd = &vmstate_assigned_device;
+ dc->reset = reset_assigned_device;
+}
+
+static TypeInfo assign_info = {
+ .name = "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.seg, dev->host.bus, dev->host.dev, dev->host.func);
+
+ if (stat(rom_file, &st)) {
+ return;
+ }
+
+ if (access(rom_file, F_OK)) {
+ fprintf(stderr, "pci-assign: Insufficient privileges for %s\n",
+ 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)) {
+ fprintf(stderr, "pci-assign: Cannot read from host %s\n"
+ "\tDevice option ROM contents are probably invalid "
+ "(check dmesg).\n\tSkip option ROM probe with rombar=0, "
+ "or load from file with romfile=\n", rom_file);
+ 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);
+}
diff --git a/hw/device-assignment.h b/hw/device-assignment.h
new file mode 100644
index 000000000..3fcb80400
--- /dev/null
+++ b/hw/device-assignment.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2007, Neocleus Corporation.
+ * Copyright (c) 2007, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * Data structures for storing PCI state
+ *
+ * Adapted to kvm by Qumranet
+ *
+ * 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)
+ */
+
+#ifndef __DEVICE_ASSIGNMENT_H__
+#define __DEVICE_ASSIGNMENT_H__
+
+void assigned_dev_update_irqs(void);
+
+#endif /* __DEVICE_ASSIGNMENT_H__ */
diff --git a/hw/i8254_common.c b/hw/i8254_common.c
index a03d7cd45..b01ad70d5 100644
--- a/hw/i8254_common.c
+++ b/hw/i8254_common.c
@@ -275,7 +275,7 @@ static const VMStateDescription vmstate_pit_common = {
.pre_save = pit_dispatch_pre_save,
.post_load = pit_dispatch_post_load,
.fields = (VMStateField[]) {
- VMSTATE_UINT32_V(channels[0].irq_disabled, PITCommonState, 3),
+ VMSTATE_UINT32(channels[0].irq_disabled, PITCommonState), /* qemu-kvm's v2 had 'flags' here */
VMSTATE_STRUCT_ARRAY(channels, PITCommonState, 3, 2,
vmstate_pit_channel, PITChannelState),
VMSTATE_INT64(channels[0].next_transition_time,
diff --git a/hw/msi.c b/hw/msi.c
index 5d6ceb6df..4fcf769d3 100644
--- a/hw/msi.c
+++ b/hw/msi.c
@@ -20,6 +20,7 @@
#include "msi.h"
#include "range.h"
+#include "kvm.h"
/* Eventually those constants should go to Linux pci_regs.h */
#define PCI_MSI_PENDING_32 0x10
@@ -112,6 +113,94 @@ bool msi_enabled(const PCIDevice *dev)
PCI_MSI_FLAGS_ENABLE);
}
+static void kvm_msi_message_from_vector(PCIDevice *dev, unsigned vector,
+ KVMMsiMessage *kmm)
+{
+ uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev));
+ bool msi64bit = flags & PCI_MSI_FLAGS_64BIT;
+ unsigned int nr_vectors = msi_nr_vectors(flags);
+
+ kmm->addr_lo = pci_get_long(dev->config + msi_address_lo_off(dev));
+ if (msi64bit) {
+ kmm->addr_hi = pci_get_long(dev->config + msi_address_hi_off(dev));
+ } else {
+ kmm->addr_hi = 0;
+ }
+
+ kmm->data = pci_get_word(dev->config + msi_data_off(dev, msi64bit));
+ if (nr_vectors > 1) {
+ kmm->data &= ~(nr_vectors - 1);
+ kmm->data |= vector;
+ }
+}
+
+static void kvm_msi_update(PCIDevice *dev)
+{
+ uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev));
+ unsigned int max_vectors = 1 <<
+ ((flags & PCI_MSI_FLAGS_QMASK) >> (ffs(PCI_MSI_FLAGS_QMASK) - 1));
+ unsigned int nr_vectors = msi_nr_vectors(flags);
+ KVMMsiMessage new_entry, *entry;
+ bool changed = false;
+ unsigned int vector;
+ int r;
+
+ for (vector = 0; vector < max_vectors; vector++) {
+ entry = dev->msi_irq_entries + vector;
+
+ if (vector >= nr_vectors) {
+ if (vector < dev->msi_entries_nr) {
+ kvm_msi_message_del(entry);
+ changed = true;
+ }
+ } else if (vector >= dev->msi_entries_nr) {
+ kvm_msi_message_from_vector(dev, vector, entry);
+ r = kvm_msi_message_add(entry);
+ if (r) {
+ fprintf(stderr, "%s: kvm_msi_add failed: %s\n", __func__,
+ strerror(-r));
+ exit(1);
+ }
+ changed = true;
+ } else {
+ kvm_msi_message_from_vector(dev, vector, &new_entry);
+ r = kvm_msi_message_update(entry, &new_entry);
+ if (r < 0) {
+ fprintf(stderr, "%s: kvm_update_msi failed: %s\n",
+ __func__, strerror(-r));
+ exit(1);
+ }
+ if (r > 0) {
+ *entry = new_entry;
+ changed = true;
+ }
+ }
+ }
+ dev->msi_entries_nr = nr_vectors;
+ if (changed) {
+ r = kvm_irqchip_commit_routes(kvm_state);
+ if (r) {
+ fprintf(stderr, "%s: kvm_commit_irq_routes failed: %s\n", __func__,
+ strerror(-r));
+ exit(1);
+ }
+ }
+}
+
+/* KVM specific MSI helpers */
+static void kvm_msi_free(PCIDevice *dev)
+{
+ unsigned int vector;
+
+ for (vector = 0; vector < dev->msi_entries_nr; ++vector) {
+ kvm_msi_message_del(&dev->msi_irq_entries[vector]);
+ }
+ if (dev->msi_entries_nr > 0) {
+ kvm_irqchip_commit_routes(kvm_state);
+ }
+ dev->msi_entries_nr = 0;
+}
+
int msi_init(struct PCIDevice *dev, uint8_t offset,
unsigned int nr_vectors, bool msi64bit, bool msi_per_vector_mask)
{
@@ -167,6 +256,12 @@ int msi_init(struct PCIDevice *dev, uint8_t offset,
pci_set_long(dev->wmask + msi_mask_off(dev, msi64bit),
0xffffffff >> (PCI_MSI_VECTORS_MAX - nr_vectors));
}
+
+ if (kvm_enabled() && kvm_irqchip_in_kernel()) {
+ dev->msi_irq_entries = g_malloc(nr_vectors *
+ sizeof(*dev->msix_irq_entries));
+ }
+
return config_offset;
}
@@ -180,6 +275,12 @@ void msi_uninit(struct PCIDevice *dev)
}
flags = pci_get_word(dev->config + msi_flags_off(dev));
cap_size = msi_cap_sizeof(flags);
+
+ if (kvm_enabled() && kvm_irqchip_in_kernel()) {
+ kvm_msi_free(dev);
+ g_free(dev->msi_irq_entries);
+ }
+
pci_del_capability(dev, PCI_CAP_ID_MSI, cap_size);
dev->cap_present &= ~QEMU_PCI_CAP_MSI;
@@ -191,6 +292,10 @@ void msi_reset(PCIDevice *dev)
uint16_t flags;
bool msi64bit;
+ if (kvm_enabled() && kvm_irqchip_in_kernel()) {
+ kvm_msi_free(dev);
+ }
+
flags = pci_get_word(dev->config + msi_flags_off(dev));
flags &= ~(PCI_MSI_FLAGS_QSIZE | PCI_MSI_FLAGS_ENABLE);
msi64bit = flags & PCI_MSI_FLAGS_64BIT;
@@ -240,6 +345,11 @@ void msi_notify(PCIDevice *dev, unsigned int vector)
return;
}
+ if (kvm_enabled() && kvm_irqchip_in_kernel()) {
+ kvm_irqchip_set_irq(kvm_state, dev->msi_irq_entries[vector].gsi, 1);
+ return;
+ }
+
if (msi64bit) {
address = pci_get_quad(dev->config + msi_address_lo_off(dev));
} else {
@@ -328,6 +438,10 @@ void msi_write_config(PCIDevice *dev, uint32_t addr, uint32_t val, int len)
pci_set_word(dev->config + msi_flags_off(dev), flags);
}
+ if (kvm_enabled() && kvm_irqchip_in_kernel()) {
+ kvm_msi_update(dev);
+ }
+
if (!msi_per_vector_mask) {
/* if per vector masking isn't supported,
there is no pending interrupt. */
@@ -358,3 +472,16 @@ unsigned int msi_nr_vectors_allocated(const PCIDevice *dev)
uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev));
return msi_nr_vectors(flags);
}
+
+void msi_post_load(PCIDevice *dev)
+{
+ uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev));
+
+ if (kvm_enabled() && dev->msi_irq_entries) {
+ kvm_msi_free(dev);
+
+ if (flags & PCI_MSI_FLAGS_ENABLE) {
+ kvm_msi_update(dev);
+ }
+ }
+}
diff --git a/hw/msi.h b/hw/msi.h
index 3040bb0b4..e5e821f5f 100644
--- a/hw/msi.h
+++ b/hw/msi.h
@@ -34,6 +34,7 @@ void msi_reset(PCIDevice *dev);
void msi_notify(PCIDevice *dev, unsigned int vector);
void msi_write_config(PCIDevice *dev, uint32_t addr, uint32_t val, int len);
unsigned int msi_nr_vectors_allocated(const PCIDevice *dev);
+void msi_post_load(PCIDevice *dev);
static inline bool msi_present(const PCIDevice *dev)
{
diff --git a/hw/msix.c b/hw/msix.c
index 3835eaaf2..5515a32d2 100644
--- a/hw/msix.c
+++ b/hw/msix.c
@@ -19,6 +19,7 @@
#include "msix.h"
#include "pci.h"
#include "range.h"
+#include "kvm.h"
#define MSIX_CAP_LENGTH 12
@@ -36,6 +37,93 @@
#define MSIX_MAX_ENTRIES 32
+/* KVM specific MSIX helpers */
+static void kvm_msix_free(PCIDevice *dev)
+{
+ int vector, changed = 0;
+
+ for (vector = 0; vector < dev->msix_entries_nr; ++vector) {
+ if (dev->msix_entry_used[vector]) {
+ kvm_msi_message_del(&dev->msix_irq_entries[vector]);
+ changed = 1;
+ }
+ }
+ if (changed) {
+ kvm_irqchip_commit_routes(kvm_state);
+ }
+}
+
+static void kvm_msix_message_from_vector(PCIDevice *dev, unsigned vector,
+ KVMMsiMessage *kmm)
+{
+ uint8_t *table_entry = dev->msix_table_page + vector * PCI_MSIX_ENTRY_SIZE;
+
+ kmm->addr_lo = pci_get_long(table_entry + PCI_MSIX_ENTRY_LOWER_ADDR);
+ kmm->addr_hi = pci_get_long(table_entry + PCI_MSIX_ENTRY_UPPER_ADDR);
+ kmm->data = pci_get_long(table_entry + PCI_MSIX_ENTRY_DATA);
+}
+
+static void kvm_msix_update(PCIDevice *dev, int vector,
+ int was_masked, int is_masked)
+{
+ KVMMsiMessage new_entry, *entry;
+ int mask_cleared = was_masked && !is_masked;
+ int r;
+
+ /* It is only legal to change an entry when it is masked. Therefore, it is
+ * enough to update the routing in kernel when mask is being cleared. */
+ if (!mask_cleared) {
+ return;
+ }
+ if (!dev->msix_entry_used[vector]) {
+ return;
+ }
+
+ entry = dev->msix_irq_entries + vector;
+ kvm_msix_message_from_vector(dev, vector, &new_entry);
+ r = kvm_msi_message_update(entry, &new_entry);
+ if (r < 0) {
+ fprintf(stderr, "%s: kvm_update_msix failed: %s\n", __func__,
+ strerror(-r));
+ exit(1);
+ }
+ if (r > 0) {
+ *entry = new_entry;
+ r = kvm_irqchip_commit_routes(kvm_state);
+ if (r) {
+ fprintf(stderr, "%s: kvm_commit_irq_routes failed: %s\n", __func__,
+ strerror(-r));
+ exit(1);
+ }
+ }
+}
+
+static int kvm_msix_vector_add(PCIDevice *dev, unsigned vector)
+{
+ KVMMsiMessage *kmm = dev->msix_irq_entries + vector;
+ int r;
+
+ kvm_msix_message_from_vector(dev, vector, kmm);
+ r = kvm_msi_message_add(kmm);
+ if (r < 0) {
+ fprintf(stderr, "%s: kvm_add_msix failed: %s\n", __func__, strerror(-r));
+ return r;
+ }
+
+ r = kvm_irqchip_commit_routes(kvm_state);
+ if (r < 0) {
+ fprintf(stderr, "%s: kvm_commit_irq_routes failed: %s\n", __func__, strerror(-r));
+ return r;
+ }
+ return 0;
+}
+
+static void kvm_msix_vector_del(PCIDevice *dev, unsigned vector)
+{
+ kvm_msi_message_del(&dev->msix_irq_entries[vector]);
+ kvm_irqchip_commit_routes(kvm_state);
+}
+
/* Add MSI-X capability to the config space for the device. */
/* Given a bar and its size, add MSI-X table on top of it
* and fill MSI-X capability in the config space.
@@ -137,6 +225,12 @@ static void msix_handle_mask_update(PCIDevice *dev, int vector, bool was_masked)
return;
}
+ if (dev->msix_mask_notifier) {
+ int ret;
+ ret = dev->msix_mask_notifier(dev, vector, is_masked);
+ assert(ret >= 0);
+ }
+
if (!is_masked && msix_is_pending(dev, vector)) {
msix_clr_pending(dev, vector);
msix_notify(dev, vector);
@@ -195,6 +289,9 @@ static void msix_mmio_write(void *opaque, target_phys_addr_t addr,
was_masked = msix_is_masked(dev, vector);
pci_set_long(dev->msix_table_page + offset, val);
+ if (kvm_enabled() && kvm_irqchip_in_kernel()) {
+ kvm_msix_update(dev, vector, was_masked, msix_is_masked(dev, vector));
+ }
msix_handle_mask_update(dev, vector, was_masked);
}
@@ -221,11 +318,18 @@ static void msix_mmio_setup(PCIDevice *d, MemoryRegion *bar)
static void msix_mask_all(struct PCIDevice *dev, unsigned nentries)
{
- int vector;
+ int vector, r;
for (vector = 0; vector < nentries; ++vector) {
unsigned offset =
vector * PCI_MSIX_ENTRY_SIZE + PCI_MSIX_ENTRY_VECTOR_CTRL;
+ int was_masked = msix_is_masked(dev, vector);
dev->msix_table_page[offset] |= PCI_MSIX_ENTRY_CTRL_MASKBIT;
+ if (was_masked != msix_is_masked(dev, vector) &&
+ dev->msix_mask_notifier) {
+ r = dev->msix_mask_notifier(dev, vector,
+ msix_is_masked(dev, vector));
+ assert(r >= 0);
+ }
}
}
@@ -244,6 +348,7 @@ int msix_init(struct PCIDevice *dev, unsigned short nentries,
if (nentries > MSIX_MAX_ENTRIES)
return -EINVAL;
+ dev->msix_mask_notifier = NULL;
dev->msix_entry_used = g_malloc0(MSIX_MAX_ENTRIES *
sizeof *dev->msix_entry_used);
@@ -258,6 +363,11 @@ int msix_init(struct PCIDevice *dev, unsigned short nentries,
if (ret)
goto err_config;
+ if (kvm_enabled() && kvm_irqchip_in_kernel()) {
+ dev->msix_irq_entries = g_malloc(nentries *
+ sizeof *dev->msix_irq_entries);
+ }
+
dev->cap_present |= QEMU_PCI_CAP_MSIX;
msix_mmio_setup(dev, bar);
return 0;
@@ -276,6 +386,10 @@ static void msix_free_irq_entries(PCIDevice *dev)
{
int vector;
+ if (kvm_enabled() && kvm_irqchip_in_kernel()) {
+ kvm_msix_free(dev);
+ }
+
for (vector = 0; vector < dev->msix_entries_nr; ++vector) {
dev->msix_entry_used[vector] = 0;
msix_clr_pending(dev, vector);
@@ -297,6 +411,8 @@ int msix_uninit(PCIDevice *dev, MemoryRegion *bar)
dev->msix_table_page = NULL;
g_free(dev->msix_entry_used);
dev->msix_entry_used = NULL;
+ g_free(dev->msix_irq_entries);
+ dev->msix_irq_entries = NULL;
dev->cap_present &= ~QEMU_PCI_CAP_MSIX;
return 0;
}
@@ -308,7 +424,6 @@ void msix_save(PCIDevice *dev, QEMUFile *f)
if (!(dev->cap_present & QEMU_PCI_CAP_MSIX)) {
return;
}
-
qemu_put_buffer(f, dev->msix_table_page, n * PCI_MSIX_ENTRY_SIZE);
qemu_put_buffer(f, dev->msix_table_page + MSIX_PAGE_PENDING, (n + 7) / 8);
}
@@ -363,6 +478,11 @@ void msix_notify(PCIDevice *dev, unsigned vector)
return;
}
+ if (kvm_enabled() && kvm_irqchip_in_kernel()) {
+ kvm_irqchip_set_irq(kvm_state, dev->msix_irq_entries[vector].gsi, 1);
+ return;
+ }
+
address = pci_get_quad(table_entry + PCI_MSIX_ENTRY_LOWER_ADDR);
data = pci_get_long(table_entry + PCI_MSIX_ENTRY_DATA);
stl_le_phys(address, data);
@@ -390,9 +510,17 @@ void msix_reset(PCIDevice *dev)
/* Mark vector as used. */
int msix_vector_use(PCIDevice *dev, unsigned vector)
{
+ int ret;
if (vector >= dev->msix_entries_nr)
return -EINVAL;
- dev->msix_entry_used[vector]++;
+ if (kvm_enabled() && kvm_irqchip_in_kernel() &&
+ !dev->msix_entry_used[vector]) {
+ ret = kvm_msix_vector_add(dev, vector);
+ if (ret) {
+ return ret;
+ }
+ }
+ ++dev->msix_entry_used[vector];
return 0;
}
@@ -405,6 +533,9 @@ void msix_vector_unuse(PCIDevice *dev, unsigned vector)
if (--dev->msix_entry_used[vector]) {
return;
}
+ if (kvm_enabled() && kvm_irqchip_in_kernel()) {
+ kvm_msix_vector_del(dev, vector);
+ }
msix_clr_pending(dev, vector);
}
@@ -414,3 +545,66 @@ void msix_unuse_all_vectors(PCIDevice *dev)
return;
msix_free_irq_entries(dev);
}
+
+/* Invoke the notifier if vector entry is used and unmasked. */
+static int msix_notify_if_unmasked(PCIDevice *dev, unsigned vector, int masked)
+{
+ assert(dev->msix_mask_notifier);
+ if (!dev->msix_entry_used[vector] || msix_is_masked(dev, vector)) {
+ return 0;
+ }
+ return dev->msix_mask_notifier(dev, vector, masked);
+}
+
+static int msix_set_mask_notifier_for_vector(PCIDevice *dev, unsigned vector)
+{
+ /* Notifier has been set. Invoke it on unmasked vectors. */
+ return msix_notify_if_unmasked(dev, vector, 0);
+}
+
+static int msix_unset_mask_notifier_for_vector(PCIDevice *dev, unsigned vector)
+{
+ /* Notifier will be unset. Invoke it to mask unmasked entries. */
+ return msix_notify_if_unmasked(dev, vector, 1);
+}
+
+int msix_set_mask_notifier(PCIDevice *dev, msix_mask_notifier_func f)
+{
+ int r, n;
+ assert(!dev->msix_mask_notifier);
+ dev->msix_mask_notifier = f;
+ for (n = 0; n < dev->msix_entries_nr; ++n) {
+ r = msix_set_mask_notifier_for_vector(dev, n);
+ if (r < 0) {
+ goto undo;
+ }
+ }
+ return 0;
+
+undo:
+ while (--n >= 0) {
+ msix_unset_mask_notifier_for_vector(dev, n);
+ }
+ dev->msix_mask_notifier = NULL;
+ return r;
+}
+
+int msix_unset_mask_notifier(PCIDevice *dev)
+{
+ int r, n;
+ assert(dev->msix_mask_notifier);
+ for (n = 0; n < dev->msix_entries_nr; ++n) {
+ r = msix_unset_mask_notifier_for_vector(dev, n);
+ if (r < 0) {
+ goto undo;
+ }
+ }
+ dev->msix_mask_notifier = NULL;
+ return 0;
+
+undo:
+ while (--n >= 0) {
+ msix_set_mask_notifier_for_vector(dev, n);
+ }
+ return r;
+}
diff --git a/hw/msix.h b/hw/msix.h
index 5aba22b85..a8661e1c0 100644
--- a/hw/msix.h
+++ b/hw/msix.h
@@ -29,4 +29,6 @@ void msix_notify(PCIDevice *dev, unsigned vector);
void msix_reset(PCIDevice *dev);
+int msix_set_mask_notifier(PCIDevice *dev, msix_mask_notifier_func);
+int msix_unset_mask_notifier(PCIDevice *dev);
#endif
diff --git a/hw/pc.c b/hw/pc.c
index e81a06c16..dc3193379 100644
--- a/hw/pc.c
+++ b/hw/pc.c
@@ -912,10 +912,14 @@ static DeviceState *apic_init(void *env, uint8_t apic_id)
apic_mapped = 1;
}
+#ifdef UPSTREAM_KVM
/* KVM does not support MSI yet. */
if (!kvm_irqchip_in_kernel()) {
msi_supported = true;
}
+#else
+ msi_supported = true;
+#endif
if (xen_msi_support()) {
msi_supported = true;
@@ -941,10 +945,18 @@ static void pc_cpu_reset(void *opaque)
env->halted = !cpu_is_bsp(env);
}
-static CPUX86State *pc_new_cpu(const char *cpu_model)
+CPUX86State *pc_new_cpu(const char *cpu_model)
{
CPUX86State *env;
+ if (cpu_model == NULL) {
+#ifdef TARGET_X86_64
+ cpu_model = "qemu64";
+#else
+ cpu_model = "qemu32";
+#endif
+ }
+
env = cpu_init(cpu_model);
if (!env) {
fprintf(stderr, "Unable to find x86 CPU definition\n");
@@ -963,14 +975,6 @@ void pc_cpus_init(const char *cpu_model)
int i;
/* init CPUs */
- if (cpu_model == NULL) {
-#ifdef TARGET_X86_64
- cpu_model = "qemu64";
-#else
- cpu_model = "qemu32";
-#endif
- }
-
for(i = 0; i < smp_cpus; i++) {
pc_new_cpu(cpu_model);
}
diff --git a/hw/pc.h b/hw/pc.h
index 74d3369a1..8ccf20217 100644
--- a/hw/pc.h
+++ b/hw/pc.h
@@ -149,6 +149,9 @@ void piix4_smbus_register_device(SMBusDevice *dev, uint8_t addr);
extern int no_hpet;
/* piix_pci.c */
+/* config space register for IRQ routing */
+#define PIIX_CONFIG_IRQ_ROUTE 0x60
+
struct PCII440FXState;
typedef struct PCII440FXState PCII440FXState;
@@ -168,6 +171,10 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state, int *piix_devfn,
extern PCIDevice *piix4_dev;
int piix4_init(PCIBus *bus, ISABus **isa_bus, int devfn);
+int piix_get_irq(int pin);
+
+int ipf_map_irq(PCIDevice *pci_dev, int irq_num);
+
/* vga.c */
enum vga_retrace_method {
VGA_RETRACE_DUMB,
diff --git a/hw/pc_piix.c b/hw/pc_piix.c
index a7aad4b02..4e8a280a4 100644
--- a/hw/pc_piix.c
+++ b/hw/pc_piix.c
@@ -52,6 +52,8 @@ static const int ide_iobase[MAX_IDE_BUS] = { 0x1f0, 0x170 };
static const int ide_iobase2[MAX_IDE_BUS] = { 0x3f6, 0x376 };
static const int ide_irq[MAX_IDE_BUS] = { 14, 15 };
+const char *global_cpu_model; /* cpu hotadd */
+
static void kvm_piix3_setup_irq_routing(bool pci_enabled)
{
#ifdef CONFIG_KVM
@@ -151,6 +153,8 @@ static void pc_init1(MemoryRegion *system_memory,
MemoryRegion *pci_memory;
MemoryRegion *rom_memory;
+ global_cpu_model = cpu_model;
+
pc_cpus_init(cpu_model);
if (kvmclock_enabled) {
@@ -241,7 +245,7 @@ static void pc_init1(MemoryRegion *system_memory,
if (!pci_enabled || (nd->model && strcmp(nd->model, "ne2k_isa") == 0))
pc_init_ne2k_isa(isa_bus, nd);
else
- pci_nic_init_nofail(nd, "e1000", NULL);
+ pci_nic_init_nofail(nd, "rtl8139", NULL);
}
ide_drive_get(hd, MAX_IDE_BUS);
@@ -358,6 +362,7 @@ static QEMUMachine pc_machine_v1_1 = {
.init = pc_init_pci,
.max_cpus = 255,
.is_default = 1,
+ .default_machine_opts = "accel=kvm,kernel_irqchip=on",
};
#define PC_COMPAT_1_0 \
@@ -388,6 +393,7 @@ static QEMUMachine pc_machine_v1_0 = {
.desc = "Standard PC",
.init = pc_init_pci,
.max_cpus = 255,
+ .default_machine_opts = "accel=kvm,kernel_irqchip=on",
.compat_props = (GlobalProperty[]) {
PC_COMPAT_1_0,
{ /* end of list */ }
@@ -402,6 +408,7 @@ static QEMUMachine pc_machine_v0_15 = {
.desc = "Standard PC",
.init = pc_init_pci,
.max_cpus = 255,
+ .default_machine_opts = "accel=kvm,kernel_irqchip=on",
.compat_props = (GlobalProperty[]) {
PC_COMPAT_0_15,
{ /* end of list */ }
@@ -433,6 +440,7 @@ static QEMUMachine pc_machine_v0_14 = {
.desc = "Standard PC",
.init = pc_init_pci,
.max_cpus = 255,
+ .default_machine_opts = "accel=kvm,kernel_irqchip=on",
.compat_props = (GlobalProperty[]) {
PC_COMPAT_0_14,
{
@@ -465,6 +473,7 @@ static QEMUMachine pc_machine_v0_13 = {
.desc = "Standard PC",
.init = pc_init_pci_no_kvmclock,
.max_cpus = 255,
+ .default_machine_opts = "accel=kvm,kernel_irqchip=on",
.compat_props = (GlobalProperty[]) {
PC_COMPAT_0_13,
{
@@ -501,6 +510,7 @@ static QEMUMachine pc_machine_v0_12 = {
.desc = "Standard PC",
.init = pc_init_pci_no_kvmclock,
.max_cpus = 255,
+ .default_machine_opts = "accel=kvm,kernel_irqchip=on",
.compat_props = (GlobalProperty[]) {
PC_COMPAT_0_12,
{
@@ -533,6 +543,7 @@ static QEMUMachine pc_machine_v0_11 = {
.desc = "Standard PC, qemu 0.11",
.init = pc_init_pci_no_kvmclock,
.max_cpus = 255,
+ .default_machine_opts = "accel=kvm,kernel_irqchip=on",
.compat_props = (GlobalProperty[]) {
PC_COMPAT_0_11,
{
@@ -553,6 +564,7 @@ static QEMUMachine pc_machine_v0_10 = {
.desc = "Standard PC, qemu 0.10",
.init = pc_init_pci_no_kvmclock,
.max_cpus = 255,
+ .default_machine_opts = "accel=kvm,kernel_irqchip=on",
.compat_props = (GlobalProperty[]) {
PC_COMPAT_0_11,
{
@@ -585,6 +597,7 @@ static QEMUMachine isapc_machine = {
.desc = "ISA-only PC",
.init = pc_init_isa,
.max_cpus = 1,
+ .default_machine_opts = "accel=kvm,kernel_irqchip=on",
.compat_props = (GlobalProperty[]) {
{
.driver = "pc-sysfw",
diff --git a/hw/pci.c b/hw/pci.c
index c1ebdde91..e4fefb311 100644
--- a/hw/pci.c
+++ b/hw/pci.c
@@ -29,8 +29,12 @@
#include "net.h"
#include "sysemu.h"
#include "loader.h"
+#include "hw/pc.h"
+#include "kvm.h"
+#include "device-assignment.h"
#include "range.h"
#include "qmp-commands.h"
+#include "msi.h"
//#define DEBUG_PCI
#ifdef DEBUG_PCI
@@ -351,6 +355,7 @@ static int get_pci_config_device(QEMUFile *f, void *pv, size_t size)
memcpy(s->config, config, size);
pci_update_mappings(s);
+ msi_post_load(s);
g_free(config);
return 0;
@@ -538,6 +543,83 @@ static int pci_parse_devaddr(const char *addr, int *domp, int *busp,
return 0;
}
+/*
+ * Parse device seg and bdf in device assignment command:
+ *
+ * -pcidevice host=[seg:]bus:dev.func
+ *
+ * Parse [seg:]<bus>:<slot>.<func> return -1 on error
+ */
+int pci_parse_host_devaddr(const char *addr, int *segp, int *busp,
+ int *slotp, int *funcp)
+{
+ const char *p;
+ char *e;
+ int val;
+ int seg = 0, bus = 0, slot = 0, func = 0;
+
+ /* parse optional seg */
+ p = addr;
+ val = 0;
+ while (1) {
+ p = strchr(p, ':');
+ if (p) {
+ val++;
+ p++;
+ } else
+ break;
+ }
+ if (val <= 0 || val > 2)
+ return -1;
+
+ p = addr;
+ if (val == 2) {
+ val = strtoul(p, &e, 16);
+ if (e == p)
+ return -1;
+ if (*e == ':') {
+ seg = val;
+ p = e + 1;
+ }
+ } else
+ seg = 0;
+
+
+ /* parse bdf */
+ val = strtoul(p, &e, 16);
+ if (e == p)
+ return -1;
+ if (*e == ':') {
+ bus = val;
+ p = e + 1;
+ val = strtoul(p, &e, 16);
+ if (e == p)
+ return -1;
+ if (*e == '.') {
+ slot = val;
+ p = e + 1;
+ val = strtoul(p, &e, 16);
+ if (e == p)
+ return -1;
+ func = val;
+ } else
+ return -1;
+ } else
+ return -1;
+
+ if (seg > 0xffff || bus > 0xff || slot > 0x1f || func > 0x7)
+ return -1;
+
+ if (*e)
+ return -1;
+
+ *segp = seg;
+ *busp = bus;
+ *slotp = slot;
+ *funcp = func;
+ return 0;
+}
+
int pci_read_devaddr(Monitor *mon, const char *addr, int *domp, int *busp,
unsigned *slotp)
{
@@ -1029,6 +1111,14 @@ void pci_default_write_config(PCIDevice *d, uint32_t addr, uint32_t val, int l)
d->config[addr + i] = (d->config[addr + i] & ~wmask) | (val & wmask);
d->config[addr + i] &= ~(val & w1cmask); /* W1C: Write 1 to Clear */
}
+
+#ifdef CONFIG_KVM_DEVICE_ASSIGNMENT
+ if (kvm_enabled() && kvm_irqchip_in_kernel() &&
+ addr >= PIIX_CONFIG_IRQ_ROUTE &&
+ addr < PIIX_CONFIG_IRQ_ROUTE + 4)
+ assigned_dev_update_irqs();
+#endif /* CONFIG_KVM_DEVICE_ASSIGNMENT */
+
if (ranges_overlap(addr, l, PCI_BASE_ADDRESS_0, 24) ||
ranges_overlap(addr, l, PCI_ROM_ADDRESS, 4) ||
ranges_overlap(addr, l, PCI_ROM_ADDRESS1, 4) ||
@@ -1059,6 +1149,11 @@ static void pci_set_irq(void *opaque, int irq_num, int level)
pci_change_irq_level(pci_dev, irq_num, change);
}
+int pci_map_irq(PCIDevice *pci_dev, int pin)
+{
+ return pci_dev->bus->map_irq(pci_dev, pin);
+}
+
/***********************************************************/
/* monitor info on PCI */
diff --git a/hw/pci.h b/hw/pci.h
index 8d0aa498e..a7d55caa6 100644
--- a/hw/pci.h
+++ b/hw/pci.h
@@ -6,6 +6,7 @@
#include "qdev.h"
#include "memory.h"
#include "dma.h"
+#include "kvm.h"
/* PCI includes legacy ISA access. */
#include "isa.h"
@@ -133,6 +134,9 @@ enum {
QEMU_PCI_CAP_SLOTID = (1 << QEMU_PCI_SLOTID_BITNR),
};
+typedef int (*msix_mask_notifier_func)(PCIDevice *, unsigned vector,
+ int masked);
+
#define TYPE_PCI_DEVICE "pci-device"
#define PCI_DEVICE(obj) \
OBJECT_CHECK(PCIDevice, (obj), TYPE_PCI_DEVICE)
@@ -243,12 +247,29 @@ struct PCIDevice {
bool has_rom;
MemoryRegion rom;
uint32_t rom_bar;
+
+ /* MSI entries */
+ int msi_entries_nr;
+ struct KVMMsiMessage *msi_irq_entries;
+
+ /* How much space does an MSIX table need. */
+ /* The spec requires giving the table structure
+ * a 4K aligned region all by itself. Align it to
+ * target pages so that drivers can do passthrough
+ * on the rest of the region. */
+ target_phys_addr_t msix_page_size;
+
+ KVMMsiMessage *msix_irq_entries;
+
+ msix_mask_notifier_func msix_mask_notifier;
};
void pci_register_bar(PCIDevice *pci_dev, int region_num,
uint8_t attr, MemoryRegion *memory);
pcibus_t pci_get_bar_addr(PCIDevice *pci_dev, int region_num);
+int pci_map_irq(PCIDevice *pci_dev, int pin);
+
int pci_add_capability(PCIDevice *pdev, uint8_t cap_id,
uint8_t offset, uint8_t size);
@@ -314,6 +335,9 @@ PCIBus *pci_get_bus_devfn(int *devfnp, const char *devaddr);
int pci_read_devaddr(Monitor *mon, const char *addr, int *domp, int *busp,
unsigned *slotp);
+int pci_parse_host_devaddr(const char *addr, int *segp, int *busp,
+ int *slotp, int *funcp);
+
void pci_device_deassert_intx(PCIDevice *dev);
static inline void
diff --git a/hw/piix_pci.c b/hw/piix_pci.c
index 09e84f59b..937a590cb 100644
--- a/hw/piix_pci.c
+++ b/hw/piix_pci.c
@@ -249,6 +249,8 @@ static int i440fx_initfn(PCIDevice *dev)
return 0;
}
+static PIIX3State *piix3_dev;
+
static PCIBus *i440fx_common_init(const char *device_name,
PCII440FXState **pi440fx_state,
int *piix3_devfn,
@@ -327,6 +329,7 @@ static PCIBus *i440fx_common_init(const char *device_name,
ram_size = 255;
(*pi440fx_state)->dev.config[0x57]=ram_size;
+ piix3_dev = piix3;
i440fx_update_memory_mappings(f);
return b;
@@ -412,6 +415,13 @@ static void piix3_write_config(PCIDevice *dev,
}
}
+int piix_get_irq(int pin)
+{
+ if (piix3_dev)
+ return piix3_dev->dev.config[0x60+pin];
+ return 0;
+}
+
static void piix3_write_config_xen(PCIDevice *dev,
uint32_t address, uint32_t val, int len)
{
diff --git a/hw/testdev.c b/hw/testdev.c
new file mode 100644
index 000000000..21125d54e
--- /dev/null
+++ b/hw/testdev.c
@@ -0,0 +1,154 @@
+#include <sys/mman.h>
+#include "hw.h"
+#include "qdev.h"
+#include "isa.h"
+
+struct testdev {
+ ISADevice dev;
+ MemoryRegion iomem;
+ CharDriverState *chr;
+};
+
+#define TYPE_TESTDEV "testdev"
+#define TESTDEV(obj) \
+ OBJECT_CHECK(struct testdev, (obj), TYPE_TESTDEV)
+
+static void test_device_serial_write(void *opaque, uint32_t addr, uint32_t data)
+{
+ struct testdev *dev = opaque;
+ uint8_t buf[1] = { data };
+
+ if (dev->chr) {
+ qemu_chr_fe_write(dev->chr, buf, 1);
+ }
+}
+
+static void test_device_exit(void *opaque, uint32_t addr, uint32_t data)
+{
+ exit(data);
+}
+
+static uint32_t test_device_memsize_read(void *opaque, uint32_t addr)
+{
+ return ram_size;
+}
+
+static void test_device_irq_line(void *opaque, uint32_t addr, uint32_t data)
+{
+ struct testdev *dev = opaque;
+
+ qemu_set_irq(isa_get_irq(&dev->dev, addr - 0x2000), !!data);
+}
+
+static uint32 test_device_ioport_data;
+
+static void test_device_ioport_write(void *opaque, uint32_t addr, uint32_t data)
+{
+ test_device_ioport_data = data;
+}
+
+static uint32_t test_device_ioport_read(void *opaque, uint32_t addr)
+{
+ return test_device_ioport_data;
+}
+
+static void test_device_flush_page(void *opaque, uint32_t addr, uint32_t data)
+{
+ target_phys_addr_t len = 4096;
+ void *a = cpu_physical_memory_map(data & ~0xffful, &len, 0);
+
+ mprotect(a, 4096, PROT_NONE);
+ mprotect(a, 4096, PROT_READ|PROT_WRITE);
+ cpu_physical_memory_unmap(a, len, 0, 0);
+}
+
+static char *iomem_buf;
+
+static uint32_t test_iomem_readb(void *opaque, target_phys_addr_t addr)
+{
+ return iomem_buf[addr];
+}
+
+static uint32_t test_iomem_readw(void *opaque, target_phys_addr_t addr)
+{
+ return *(uint16_t*)(iomem_buf + addr);
+}
+
+static uint32_t test_iomem_readl(void *opaque, target_phys_addr_t addr)
+{
+ return *(uint32_t*)(iomem_buf + addr);
+}
+
+static void test_iomem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ iomem_buf[addr] = val;
+}
+
+static void test_iomem_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ *(uint16_t*)(iomem_buf + addr) = val;
+}
+
+static void test_iomem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ *(uint32_t*)(iomem_buf + addr) = val;
+}
+
+static const MemoryRegionOps test_iomem_ops = {
+ .old_mmio = {
+ .read = { test_iomem_readb, test_iomem_readw, test_iomem_readl, },
+ .write = { test_iomem_writeb, test_iomem_writew, test_iomem_writel, },
+ },
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static int init_test_device(ISADevice *isa)
+{
+ struct testdev *dev = DO_UPCAST(struct testdev, dev, isa);
+
+ register_ioport_write(0xf1, 1, 1, test_device_serial_write, dev);
+ register_ioport_write(0xf4, 1, 4, test_device_exit, dev);
+ register_ioport_read(0xd1, 1, 4, test_device_memsize_read, dev);
+ register_ioport_read(0xe0, 1, 1, test_device_ioport_read, dev);
+ register_ioport_write(0xe0, 1, 1, test_device_ioport_write, dev);
+ register_ioport_read(0xe0, 1, 2, test_device_ioport_read, dev);
+ register_ioport_write(0xe0, 1, 2, test_device_ioport_write, dev);
+ register_ioport_read(0xe0, 1, 4, test_device_ioport_read, dev);
+ register_ioport_write(0xe0, 1, 4, test_device_ioport_write, dev);
+ register_ioport_write(0xe4, 1, 4, test_device_flush_page, dev);
+ register_ioport_write(0x2000, 24, 1, test_device_irq_line, NULL);
+ iomem_buf = g_malloc0(0x10000);
+ memory_region_init_io(&dev->iomem, &test_iomem_ops, dev,
+ "testdev", 0x10000);
+ memory_region_add_subregion(isa_address_space(&dev->dev), 0xff000000,
+ &dev->iomem);
+ return 0;
+}
+
+static Property testdev_isa_properties[] = {
+ DEFINE_PROP_CHR("chardev", struct testdev, chr),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void testdev_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ ISADeviceClass *k = ISA_DEVICE_CLASS(klass);
+
+ k->init = init_test_device;
+ dc->props = testdev_isa_properties;
+}
+
+static TypeInfo testdev_info = {
+ .name = "testdev",
+ .parent = TYPE_ISA_DEVICE,
+ .instance_size = sizeof(struct testdev),
+ .class_init = testdev_class_init,
+};
+
+static void testdev_register_types(void)
+{
+ type_register_static(&testdev_info);
+}
+
+type_init(testdev_register_types)
diff --git a/hw/vga_int.h b/hw/vga_int.h
index d244d8ff9..6f696b69b 100644
--- a/hw/vga_int.h
+++ b/hw/vga_int.h
@@ -31,8 +31,8 @@
/* bochs VBE support */
#define CONFIG_BOCHS_VBE
-#define VBE_DISPI_MAX_XRES 1600
-#define VBE_DISPI_MAX_YRES 1200
+#define VBE_DISPI_MAX_XRES 2560
+#define VBE_DISPI_MAX_YRES 1600
#define VBE_DISPI_MAX_BPP 32
#define VBE_DISPI_INDEX_ID 0x0
@@ -209,7 +209,7 @@ void vga_init_vbe(VGACommonState *s, MemoryRegion *address_space);
extern const uint8_t sr_mask[8];
extern const uint8_t gr_mask[16];
-#define VGA_RAM_SIZE (8192 * 1024)
+#define VGA_RAM_SIZE (16 * 1024 * 1024)
#define VGABIOS_FILENAME "vgabios.bin"
#define VGABIOS_CIRRUS_FILENAME "vgabios-cirrus.bin"
diff --git a/hw/virtio-pci.c b/hw/virtio-pci.c
index 79b86f1aa..5b643561e 100644
--- a/hw/virtio-pci.c
+++ b/hw/virtio-pci.c
@@ -539,6 +539,57 @@ static void virtio_pci_guest_notifier_read(void *opaque)
}
}
+static int virtio_pci_mask_vq(PCIDevice *dev, unsigned vector,
+ VirtQueue *vq, int masked)
+{
+ EventNotifier *notifier = virtio_queue_get_guest_notifier(vq);
+ int r = kvm_set_irqfd(dev->msix_irq_entries[vector].gsi,
+ event_notifier_get_fd(notifier),
+ !masked);
+ if (r < 0) {
+ return (r == -ENOSYS) ? 0 : r;
+ }
+ if (masked) {
+ qemu_set_fd_handler(event_notifier_get_fd(notifier),
+ virtio_pci_guest_notifier_read, NULL, vq);
+ } else {
+ qemu_set_fd_handler(event_notifier_get_fd(notifier),
+ NULL, NULL, NULL);
+ }
+ return 0;
+}
+
+static int virtio_pci_mask_notifier(PCIDevice *dev, unsigned vector,
+ int masked)
+{
+ VirtIOPCIProxy *proxy = container_of(dev, VirtIOPCIProxy, pci_dev);
+ VirtIODevice *vdev = proxy->vdev;
+ int r, n;
+
+ for (n = 0; n < VIRTIO_PCI_QUEUE_MAX; n++) {
+ if (!virtio_queue_get_num(vdev, n)) {
+ break;
+ }
+ if (virtio_queue_vector(vdev, n) != vector) {
+ continue;
+ }
+ r = virtio_pci_mask_vq(dev, vector, virtio_get_queue(vdev, n), masked);
+ if (r < 0) {
+ goto undo;
+ }
+ }
+ return 0;
+undo:
+ while (--n >= 0) {
+ if (virtio_queue_vector(vdev, n) != vector) {
+ continue;
+ }
+ virtio_pci_mask_vq(dev, vector, virtio_get_queue(vdev, n), !masked);
+ }
+ return r;
+}
+
+
static int virtio_pci_set_guest_notifier(void *opaque, int n, bool assign)
{
VirtIOPCIProxy *proxy = opaque;
@@ -555,6 +606,9 @@ static int virtio_pci_set_guest_notifier(void *opaque, int n, bool assign)
} else {
qemu_set_fd_handler(event_notifier_get_fd(notifier),
NULL, NULL, NULL);
+ /* Test and clear notifier before closing it,
+ * in case poll callback didn't have time to run. */
+ virtio_pci_guest_notifier_read(vq);
event_notifier_cleanup(notifier);
}
@@ -573,6 +627,13 @@ static int virtio_pci_set_guest_notifiers(void *opaque, bool assign)
VirtIODevice *vdev = proxy->vdev;
int r, n;
+ /* Must unset mask notifier while guest notifier
+ * is still assigned */
+ if (kvm_irqchip_in_kernel() && !assign) {
+ r = msix_unset_mask_notifier(&proxy->pci_dev);
+ assert(r >= 0);
+ }
+
for (n = 0; n < VIRTIO_PCI_QUEUE_MAX; n++) {
if (!virtio_queue_get_num(vdev, n)) {
break;
@@ -584,6 +645,16 @@ static int virtio_pci_set_guest_notifiers(void *opaque, bool assign)
}
}
+ /* Must set mask notifier after guest notifier
+ * has been assigned */
+ if (kvm_irqchip_in_kernel() && assign) {
+ r = msix_set_mask_notifier(&proxy->pci_dev,
+ virtio_pci_mask_notifier);
+ if (r < 0) {
+ goto assign_error;
+ }
+ }
+
return 0;
assign_error:
@@ -591,6 +662,11 @@ assign_error:
while (--n >= 0) {
virtio_pci_set_guest_notifier(opaque, n, !assign);
}
+
+ if (!assign) {
+ msix_set_mask_notifier(&proxy->pci_dev,
+ virtio_pci_mask_notifier);
+ }
return r;
}
diff --git a/kvm-all.c b/kvm-all.c
index 9b73ccfbe..467413315 100644
--- a/kvm-all.c
+++ b/kvm-all.c
@@ -78,6 +78,7 @@ struct KVMState
int pit_state2;
int xsave, xcrs;
int many_ioeventfds;
+ int intx_set_mask;
/* The man page (and posix) say ioctl numbers are signed int, but
* they're not. Linux, glibc and *BSD all treat ioctl numbers as
* unsigned, and treating them as signed here can break things */
@@ -889,8 +890,8 @@ static void kvm_init_irq_routing(KVMState *s)
kvm_arch_init_irq_routing(s);
}
-static void kvm_add_routing_entry(KVMState *s,
- struct kvm_irq_routing_entry *entry)
+void kvm_add_routing_entry(KVMState *s,
+ struct kvm_irq_routing_entry *entry)
{
struct kvm_irq_routing_entry *new;
int n, size;
@@ -939,6 +940,12 @@ int kvm_irqchip_commit_routes(KVMState *s)
static void kvm_init_irq_routing(KVMState *s)
{
}
+
+int kvm_irqchip_commit_routes(KVMState *s)
+{
+ return -ENOSYS;
+}
+
#endif /* !KVM_CAP_IRQ_ROUTING */
static int kvm_irqchip_create(KVMState *s)
@@ -1072,6 +1079,8 @@ int kvm_init(void)
s->pit_state2 = kvm_check_extension(s, KVM_CAP_PIT_STATE2);
#endif
+ s->intx_set_mask = kvm_check_extension(s, KVM_CAP_PCI_2_3);
+
ret = kvm_arch_init(s);
if (ret < 0) {
goto err;
@@ -1426,6 +1435,11 @@ int kvm_has_gsi_routing(void)
#endif
}
+int kvm_has_intx_set_mask(void)
+{
+ return kvm_state->intx_set_mask;
+}
+
int kvm_allows_irq0_override(void)
{
return !kvm_irqchip_in_kernel() || kvm_has_gsi_routing();
@@ -1698,6 +1712,23 @@ int kvm_set_ioeventfd_pio_word(int fd, uint16_t addr, uint16_t val, bool assign)
return 0;
}
+int kvm_set_irqfd(int gsi, int fd, bool assigned)
+{
+ struct kvm_irqfd irqfd = {
+ .fd = fd,
+ .gsi = gsi,
+ .flags = assigned ? 0 : KVM_IRQFD_FLAG_DEASSIGN,
+ };
+ int r;
+ if (!kvm_enabled() || !kvm_irqchip_in_kernel())
+ return -ENOSYS;
+
+ r = kvm_vm_ioctl(kvm_state, KVM_IRQFD, &irqfd);
+ if (r < 0)
+ return r;
+ return 0;
+}
+
int kvm_on_sigbus_vcpu(CPUArchState *env, int code, void *addr)
{
return kvm_arch_on_sigbus_vcpu(env, code, addr);
@@ -1707,3 +1738,6 @@ int kvm_on_sigbus(int code, void *addr)
{
return kvm_arch_on_sigbus(code, addr);
}
+
+#undef PAGE_SIZE
+#include "qemu-kvm.c"
diff --git a/kvm-stub.c b/kvm-stub.c
index 47c573d6f..b630322f4 100644
--- a/kvm-stub.c
+++ b/kvm-stub.c
@@ -16,6 +16,8 @@
#include "gdbstub.h"
#include "kvm.h"
+KVMState *kvm_state;
+
int kvm_init_vcpu(CPUArchState *env)
{
return -ENOSYS;
@@ -119,6 +121,42 @@ int kvm_set_ioeventfd_mmio(int fd, uint32_t adr, uint32_t val, bool assign, uint
return -ENOSYS;
}
+int kvm_has_gsi_routing(void)
+{
+ return 0;
+}
+
+int kvm_get_irq_route_gsi(void)
+{
+ return -ENOSYS;
+}
+
+int kvm_msi_message_add(KVMMsiMessage *msg)
+{
+ return -ENOSYS;
+}
+
+int kvm_msi_message_del(KVMMsiMessage *msg)
+{
+ return -ENOSYS;
+}
+
+int kvm_msi_message_update(KVMMsiMessage *old, KVMMsiMessage *new)
+{
+ return -ENOSYS;
+}
+
+int kvm_irqchip_commit_routes(KVMState *s)
+{
+ return -ENOSYS;
+}
+
+int kvm_irqchip_set_irq(KVMState *s, int irq, int level)
+{
+ assert(0);
+ return -ENOSYS;
+}
+
int kvm_on_sigbus_vcpu(CPUArchState *env, int code, void *addr)
{
return 1;
@@ -128,3 +166,8 @@ int kvm_on_sigbus(int code, void *addr)
{
return 1;
}
+
+int kvm_set_irqfd(int gsi, int fd, bool assigned)
+{
+ return -ENOSYS;
+}
diff --git a/kvm.h b/kvm.h
index 4ccae8c0c..cacbf3c14 100644
--- a/kvm.h
+++ b/kvm.h
@@ -54,9 +54,10 @@ int kvm_has_robust_singlestep(void);
int kvm_has_debugregs(void);
int kvm_has_xsave(void);
int kvm_has_xcrs(void);
-int kvm_has_pit_state2(void);
int kvm_has_many_ioeventfds(void);
+int kvm_has_pit_state2(void);
int kvm_has_gsi_routing(void);
+int kvm_has_intx_set_mask(void);
int kvm_allows_irq0_override(void);
@@ -85,6 +86,7 @@ int kvm_set_signal_mask(CPUArchState *env, const sigset_t *sigset);
int kvm_on_sigbus_vcpu(CPUArchState *env, int code, void *addr);
int kvm_on_sigbus(int code, void *addr);
+#endif /* NEED_CPU_H */
/* internal API */
@@ -96,6 +98,7 @@ int kvm_ioctl(KVMState *s, int type, ...);
int kvm_vm_ioctl(KVMState *s, int type, ...);
+#ifdef NEED_CPU_H
int kvm_vcpu_ioctl(CPUArchState *env, int type, ...);
/* Arch specific hooks */
@@ -211,5 +214,30 @@ int kvm_physical_memory_addr_from_host(KVMState *s, void *ram_addr,
int kvm_set_ioeventfd_mmio(int fd, uint32_t adr, uint32_t val, bool assign,
uint32_t size);
+int kvm_set_irqfd(int gsi, int fd, bool assigned);
+
int kvm_set_ioeventfd_pio_word(int fd, uint16_t adr, uint16_t val, bool assign);
+
+typedef struct KVMMsiMessage {
+ uint32_t gsi;
+ uint32_t addr_lo;
+ uint32_t addr_hi;
+ uint32_t data;
+} KVMMsiMessage;
+
+int kvm_get_irq_route_gsi(void);
+
+int kvm_msi_message_add(KVMMsiMessage *msg);
+int kvm_msi_message_del(KVMMsiMessage *msg);
+int kvm_msi_message_update(KVMMsiMessage *old, KVMMsiMessage *new);
+
+#ifndef NEED_CPU_H
+int kvm_irqchip_set_irq(KVMState *s, int irq, int level);
+int kvm_irqchip_commit_routes(KVMState *s);
+#endif
+
+#ifdef NEED_CPU_H
+#include "qemu-kvm.h"
+#endif
+
#endif
diff --git a/monitor.c b/monitor.c
index 12a6fe25a..71f43925a 100644
--- a/monitor.c
+++ b/monitor.c
@@ -779,6 +779,27 @@ static void do_info_registers(Monitor *mon)
#endif
}
+static void do_cpu_set_nr(Monitor *mon, const QDict *qdict)
+{
+ int state, value;
+ const char *status;
+
+ status = qdict_get_str(qdict, "state");
+ value = qdict_get_int(qdict, "cpu");
+
+ if (!strcmp(status, "online"))
+ state = 1;
+ else if (!strcmp(status, "offline"))
+ state = 0;
+ else {
+ monitor_printf(mon, "invalid status: %s\n", status);
+ return;
+ }
+#if defined(TARGET_I386) || defined(TARGET_X86_64)
+ qemu_system_cpu_hot_add(value, state);
+#endif
+}
+
static void do_info_jit(Monitor *mon)
{
dump_exec_info((FILE *)mon, monitor_fprintf);
diff --git a/pc-bios/vgabios-cirrus.bin b/pc-bios/vgabios-cirrus.bin
index 424dd0c70..7626ea551 100644
--- a/pc-bios/vgabios-cirrus.bin
+++ b/pc-bios/vgabios-cirrus.bin
Binary files differ
diff --git a/pc-bios/vgabios-stdvga.bin b/pc-bios/vgabios-stdvga.bin
index 5123c5fd0..1e4fc33cc 100644
--- a/pc-bios/vgabios-stdvga.bin
+++ b/pc-bios/vgabios-stdvga.bin
Binary files differ
diff --git a/pc-bios/vgabios-vmware.bin b/pc-bios/vgabios-vmware.bin
index 5e8c06b22..9633d9af9 100644
--- a/pc-bios/vgabios-vmware.bin
+++ b/pc-bios/vgabios-vmware.bin
Binary files differ
diff --git a/pc-bios/vgabios.bin b/pc-bios/vgabios.bin
index 892a2b537..d04033ea1 100644
--- a/pc-bios/vgabios.bin
+++ b/pc-bios/vgabios.bin
Binary files differ
diff --git a/qemu-config.c b/qemu-config.c
index be84a0347..5ba19e2aa 100644
--- a/qemu-config.c
+++ b/qemu-config.c
@@ -113,6 +113,10 @@ static QemuOptsList qemu_drive_opts = {
.name = "copy-on-read",
.type = QEMU_OPT_BOOL,
.help = "copy read data from backing file into image file",
+ },{
+ .name = "boot",
+ .type = QEMU_OPT_BOOL,
+ .help = "(deprecated, ignored)",
},
{ /* end of list */ }
},
diff --git a/qemu-kvm.c b/qemu-kvm.c
new file mode 100644
index 000000000..133143cce
--- /dev/null
+++ b/qemu-kvm.c
@@ -0,0 +1,317 @@
+/*
+ * qemu/kvm integration
+ *
+ * Copyright (C) 2006-2008 Qumranet Technologies
+ *
+ * Licensed under the terms of the GNU GPL version 2 or higher.
+ */
+#include "config.h"
+#include "config-host.h"
+
+#include <assert.h>
+#include <string.h>
+#include "hw/hw.h"
+#include "sysemu.h"
+#include "qemu-common.h"
+#include "console.h"
+#include "block.h"
+#include "compatfd.h"
+#include "gdbstub.h"
+#include "monitor.h"
+#include "cpus.h"
+
+#include "qemu-kvm.h"
+
+#define EXPECTED_KVM_API_VERSION 12
+
+#if EXPECTED_KVM_API_VERSION != KVM_API_VERSION
+#error libkvm: userspace and kernel version mismatch
+#endif
+
+#define ALIGN(x, y) (((x)+(y)-1) & ~((y)-1))
+
+#ifdef KVM_CAP_IRQ_ROUTING
+static inline void clear_gsi(KVMState *s, unsigned int gsi)
+{
+ uint32_t *bitmap = s->used_gsi_bitmap;
+
+ if (gsi < s->max_gsi) {
+ bitmap[gsi / 32] &= ~(1U << (gsi % 32));
+ } else {
+ DPRINTF("Invalid GSI %u\n", gsi);
+ }
+}
+#endif
+
+#ifdef KVM_CAP_DEVICE_ASSIGNMENT
+int kvm_assign_pci_device(KVMState *s,
+ struct kvm_assigned_pci_dev *assigned_dev)
+{
+ return kvm_vm_ioctl(s, KVM_ASSIGN_PCI_DEVICE, assigned_dev);
+}
+
+static int kvm_old_assign_irq(KVMState *s,
+ struct kvm_assigned_irq *assigned_irq)
+{
+ return kvm_vm_ioctl(s, KVM_ASSIGN_IRQ, assigned_irq);
+}
+
+int kvm_device_intx_set_mask(KVMState *s, uint32_t dev_id, bool masked)
+{
+ struct kvm_assigned_pci_dev assigned_dev;
+
+ assigned_dev.assigned_dev_id = dev_id;
+ assigned_dev.flags = masked ? KVM_DEV_ASSIGN_MASK_INTX : 0;
+ return kvm_vm_ioctl(s, KVM_ASSIGN_SET_INTX_MASK, &assigned_dev);
+}
+
+#ifdef KVM_CAP_ASSIGN_DEV_IRQ
+int kvm_assign_irq(KVMState *s, struct kvm_assigned_irq *assigned_irq)
+{
+ int ret;
+
+ ret = kvm_ioctl(s, KVM_CHECK_EXTENSION, KVM_CAP_ASSIGN_DEV_IRQ);
+ if (ret > 0) {
+ return kvm_vm_ioctl(s, KVM_ASSIGN_DEV_IRQ, assigned_irq);
+ }
+
+ return kvm_old_assign_irq(s, assigned_irq);
+}
+
+int kvm_deassign_irq(KVMState *s, struct kvm_assigned_irq *assigned_irq)
+{
+ return kvm_vm_ioctl(s, KVM_DEASSIGN_DEV_IRQ, assigned_irq);
+}
+#else
+int kvm_assign_irq(KVMState *s, struct kvm_assigned_irq *assigned_irq)
+{
+ return kvm_old_assign_irq(s, assigned_irq);
+}
+#endif
+#endif
+
+#ifdef KVM_CAP_DEVICE_DEASSIGNMENT
+int kvm_deassign_pci_device(KVMState *s,
+ struct kvm_assigned_pci_dev *assigned_dev)
+{
+ return kvm_vm_ioctl(s, KVM_DEASSIGN_PCI_DEVICE, assigned_dev);
+}
+#endif
+
+int kvm_del_routing_entry(struct kvm_irq_routing_entry *entry)
+{
+#ifdef KVM_CAP_IRQ_ROUTING
+ KVMState *s = kvm_state;
+ struct kvm_irq_routing_entry *e, *p;
+ int i, gsi, found = 0;
+
+ gsi = entry->gsi;
+
+ for (i = 0; i < s->irq_routes->nr; ++i) {
+ e = &s->irq_routes->entries[i];
+ if (e->type == entry->type && e->gsi == gsi) {
+ switch (e->type) {
+ case KVM_IRQ_ROUTING_IRQCHIP:{
+ if (e->u.irqchip.irqchip ==
+ entry->u.irqchip.irqchip
+ && e->u.irqchip.pin == entry->u.irqchip.pin) {
+ p = &s->irq_routes->entries[--s->irq_routes->nr];
+ *e = *p;
+ found = 1;
+ }
+ break;
+ }
+ case KVM_IRQ_ROUTING_MSI:{
+ if (e->u.msi.address_lo ==
+ entry->u.msi.address_lo
+ && e->u.msi.address_hi ==
+ entry->u.msi.address_hi
+ && e->u.msi.data == entry->u.msi.data) {
+ p = &s->irq_routes->entries[--s->irq_routes->nr];
+ *e = *p;
+ found = 1;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ if (found) {
+ /* If there are no other users of this GSI
+ * mark it available in the bitmap */
+ for (i = 0; i < s->irq_routes->nr; i++) {
+ e = &s->irq_routes->entries[i];
+ if (e->gsi == gsi)
+ break;
+ }
+ if (i == s->irq_routes->nr) {
+ clear_gsi(s, gsi);
+ }
+
+ return 0;
+ }
+ }
+ }
+ return -ESRCH;
+#else
+ return -ENOSYS;
+#endif
+}
+
+int kvm_update_routing_entry(struct kvm_irq_routing_entry *entry,
+ struct kvm_irq_routing_entry *newentry)
+{
+#ifdef KVM_CAP_IRQ_ROUTING
+ KVMState *s = kvm_state;
+ struct kvm_irq_routing_entry *e;
+ int i;
+
+ if (entry->gsi != newentry->gsi || entry->type != newentry->type) {
+ return -EINVAL;
+ }
+
+ for (i = 0; i < s->irq_routes->nr; ++i) {
+ e = &s->irq_routes->entries[i];
+ if (e->type != entry->type || e->gsi != entry->gsi) {
+ continue;
+ }
+ switch (e->type) {
+ case KVM_IRQ_ROUTING_IRQCHIP:
+ if (e->u.irqchip.irqchip == entry->u.irqchip.irqchip &&
+ e->u.irqchip.pin == entry->u.irqchip.pin) {
+ memcpy(&e->u.irqchip, &newentry->u.irqchip,
+ sizeof e->u.irqchip);
+ return 0;
+ }
+ break;
+ case KVM_IRQ_ROUTING_MSI:
+ if (e->u.msi.address_lo == entry->u.msi.address_lo &&
+ e->u.msi.address_hi == entry->u.msi.address_hi &&
+ e->u.msi.data == entry->u.msi.data) {
+ memcpy(&e->u.msi, &newentry->u.msi, sizeof e->u.msi);
+ return 0;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ return -ESRCH;
+#else
+ return -ENOSYS;
+#endif
+}
+
+int kvm_get_irq_route_gsi(void)
+{
+#ifdef KVM_CAP_IRQ_ROUTING
+ KVMState *s = kvm_state;
+ int i, bit;
+ uint32_t *buf = s->used_gsi_bitmap;
+
+ /* Return the lowest unused GSI in the bitmap */
+ for (i = 0; i < s->max_gsi / 32; i++) {
+ bit = ffs(~buf[i]);
+ if (!bit) {
+ continue;
+ }
+
+ return bit - 1 + i * 32;
+ }
+
+ return -ENOSPC;
+#else
+ return -ENOSYS;
+#endif
+}
+
+#ifdef KVM_CAP_IRQ_ROUTING
+static void kvm_msi_routing_entry(struct kvm_irq_routing_entry *e,
+ KVMMsiMessage *msg)
+
+{
+ e->gsi = msg->gsi;
+ e->type = KVM_IRQ_ROUTING_MSI;
+ e->flags = 0;
+ e->u.msi.address_lo = msg->addr_lo;
+ e->u.msi.address_hi = msg->addr_hi;
+ e->u.msi.data = msg->data;
+}
+#endif
+
+int kvm_msi_message_add(KVMMsiMessage *msg)
+{
+#ifdef KVM_CAP_IRQ_ROUTING
+ struct kvm_irq_routing_entry e;
+ int ret;
+
+ ret = kvm_get_irq_route_gsi();
+ if (ret < 0) {
+ return ret;
+ }
+ msg->gsi = ret;
+
+ kvm_msi_routing_entry(&e, msg);
+ kvm_add_routing_entry(kvm_state, &e);
+ return 0;
+#else
+ return -ENOSYS;
+#endif
+}
+
+int kvm_msi_message_del(KVMMsiMessage *msg)
+{
+#ifdef KVM_CAP_IRQ_ROUTING
+ struct kvm_irq_routing_entry e;
+
+ kvm_msi_routing_entry(&e, msg);
+ return kvm_del_routing_entry(&e);
+#else
+ return -ENOSYS;
+#endif
+}
+
+int kvm_msi_message_update(KVMMsiMessage *old, KVMMsiMessage *new)
+{
+#ifdef KVM_CAP_IRQ_ROUTING
+ struct kvm_irq_routing_entry e1, e2;
+ int ret;
+
+ new->gsi = old->gsi;
+ if (memcmp(old, new, sizeof(KVMMsiMessage)) == 0) {
+ return 0;
+ }
+
+ kvm_msi_routing_entry(&e1, old);
+ kvm_msi_routing_entry(&e2, new);
+
+ ret = kvm_update_routing_entry(&e1, &e2);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return 1;
+#else
+ return -ENOSYS;
+#endif
+}
+
+
+#ifdef KVM_CAP_DEVICE_MSIX
+int kvm_assign_set_msix_nr(KVMState *s, struct kvm_assigned_msix_nr *msix_nr)
+{
+ return kvm_vm_ioctl(s, KVM_ASSIGN_SET_MSIX_NR, msix_nr);
+}
+
+int kvm_assign_set_msix_entry(KVMState *s,
+ struct kvm_assigned_msix_entry *entry)
+{
+ return kvm_vm_ioctl(s, KVM_ASSIGN_SET_MSIX_ENTRY, entry);
+}
+#endif
+
+#if !defined(TARGET_I386)
+void kvm_arch_init_irq_routing(KVMState *s)
+{
+}
+#endif
diff --git a/qemu-kvm.h b/qemu-kvm.h
new file mode 100644
index 000000000..3ebbbb6e6
--- /dev/null
+++ b/qemu-kvm.h
@@ -0,0 +1,112 @@
+/*
+ * qemu/kvm integration
+ *
+ * Copyright (C) 2006-2008 Qumranet Technologies
+ *
+ * Licensed under the terms of the GNU GPL version 2 or higher.
+ */
+#ifndef THE_ORIGINAL_AND_TRUE_QEMU_KVM_H
+#define THE_ORIGINAL_AND_TRUE_QEMU_KVM_H
+
+#include "cpu.h"
+
+#include <signal.h>
+#include <stdlib.h>
+
+#ifdef CONFIG_KVM
+
+#include <stdint.h>
+
+#ifndef __user
+#define __user /* temporary, until installed via make headers_install */
+#endif
+
+#include <linux/kvm.h>
+
+#include <signal.h>
+
+/* FIXME: share this number with kvm */
+/* FIXME: or dynamically alloc/realloc regions */
+#define KVM_MAX_NUM_MEM_REGIONS 32u
+#define MAX_VCPUS 16
+
+#include "kvm.h"
+
+/*!
+ * \brief Notifies host kernel about a PCI device to be assigned to a guest
+ *
+ * Used for PCI device assignment, this function notifies the host
+ * kernel about the assigning of the physical PCI device to a guest.
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param assigned_dev Parameters, like bus, devfn number, etc
+ */
+int kvm_assign_pci_device(KVMState *s,
+ struct kvm_assigned_pci_dev *assigned_dev);
+
+/*!
+ * \brief Assign IRQ for an assigned device
+ *
+ * Used for PCI device assignment, this function assigns IRQ numbers for
+ * an physical device and guest IRQ handling.
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param assigned_irq Parameters, like dev id, host irq, guest irq, etc
+ */
+int kvm_assign_irq(KVMState *s, struct kvm_assigned_irq *assigned_irq);
+
+/*!
+ * \brief Deassign IRQ for an assigned device
+ *
+ * Used for PCI device assignment, this function deassigns IRQ numbers
+ * for an assigned device.
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param assigned_irq Parameters, like dev id, host irq, guest irq, etc
+ */
+int kvm_deassign_irq(KVMState *s, struct kvm_assigned_irq *assigned_irq);
+
+int kvm_device_intx_set_mask(KVMState *s, uint32_t dev_id, bool masked);
+
+/*!
+ * \brief Notifies host kernel about a PCI device to be deassigned from a guest
+ *
+ * Used for hot remove PCI device, this function notifies the host
+ * kernel about the deassigning of the physical PCI device from a guest.
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param assigned_dev Parameters, like bus, devfn number, etc
+ */
+int kvm_deassign_pci_device(KVMState *s,
+ struct kvm_assigned_pci_dev *assigned_dev);
+
+struct kvm_irq_routing_entry;
+
+void kvm_add_routing_entry(KVMState *s, struct kvm_irq_routing_entry *entry);
+
+/*!
+ * \brief Removes a routing from the temporary irq routing table
+ *
+ * Remove a routing to the temporary irq routing table. Nothing is
+ * committed to the running VM.
+ */
+int kvm_del_routing_entry(struct kvm_irq_routing_entry *entry);
+
+/*!
+ * \brief Updates a routing in the temporary irq routing table
+ *
+ * Update a routing in the temporary irq routing table
+ * with a new value. entry type and GSI can not be changed.
+ * Nothing is committed to the running VM.
+ */
+int kvm_update_routing_entry(struct kvm_irq_routing_entry *entry,
+ struct kvm_irq_routing_entry *newentry);
+
+
+int kvm_assign_set_msix_nr(KVMState *s, struct kvm_assigned_msix_nr *msix_nr);
+int kvm_assign_set_msix_entry(KVMState *s,
+ struct kvm_assigned_msix_entry *entry);
+
+#endif /* CONFIG_KVM */
+
+#endif
diff --git a/qemu-options.hx b/qemu-options.hx
index 8b662648a..125a4da2e 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -2743,6 +2743,22 @@ DEF("qtest-log", HAS_ARG, QEMU_OPTION_qtest_log,
"-qtest-log LOG specify tracing options\n",
QEMU_ARCH_ALL)
+DEF("no-kvm", 0, QEMU_OPTION_no_kvm,
+ "-no-kvm disable KVM hardware virtualization\n",
+ QEMU_ARCH_ALL)
+DEF("no-kvm-irqchip", 0, QEMU_OPTION_no_kvm_irqchip,
+ "-no-kvm-irqchip disable KVM kernel mode PIC/IOAPIC/LAPIC\n",
+ QEMU_ARCH_I386)
+DEF("no-kvm-pit", 0, QEMU_OPTION_no_kvm_pit,
+ "-no-kvm-pit disable KVM kernel mode PIT\n",
+ QEMU_ARCH_I386)
+DEF("no-kvm-pit-reinjection", 0, QEMU_OPTION_no_kvm_pit_reinjection,
+ "-no-kvm-pit-reinjection\n"
+ " disable KVM kernel mode PIT interrupt reinjection\n",
+ QEMU_ARCH_I386)
+HXCOMM -tdf is deprecated and ignored today
+DEF("tdf", 0, QEMU_OPTION_tdf, "", QEMU_ARCH_ALL)
+
HXCOMM This is the last statement. Insert new options before this line!
STEXI
@end table
diff --git a/roms/vgabios b/roms/vgabios
-Subproject 19ea12c230ded95928ecaef0db47a82231c2e48
+Subproject ca056d8e77a534f4f90548bc8cee166a378c145
diff --git a/scripts/qemu-kvm/make-release b/scripts/qemu-kvm/make-release
new file mode 100755
index 000000000..2d050fcc7
--- /dev/null
+++ b/scripts/qemu-kvm/make-release
@@ -0,0 +1,81 @@
+#!/bin/bash -e
+
+usage() {
+ echo "usage: $0 [--upload] [--formal] commit [name] [tarball] [user]"
+ exit 1
+}
+
+[[ -f ~/.kvmreleaserc ]] && . ~/.kvmreleaserc
+
+upload=
+formal=
+
+releasedir=~/sf-release
+[[ -z "$TMP" ]] && TMP="/tmp"
+tmpdir=`mktemp -d "$TMP/qemu-kvm-make-release.XXXXXXXXXX"`
+while [[ "$1" = -* ]]; do
+ opt="$1"
+ shift
+ case "$opt" in
+ --upload)
+ upload="yes"
+ ;;
+ --formal)
+ formal="yes"
+ ;;
+ *)
+ usage
+ ;;
+ esac
+done
+
+commit="$1"
+name="$2"
+
+if [[ -z "$commit" ]]; then
+ usage
+fi
+
+if [[ -z "$name" ]]; then
+ name="$commit"
+fi
+
+tarball="$3"
+if [[ -z "$tarball" ]]; then
+ tarball="$releasedir/$name.tar.gz"
+fi
+#strip trailing .gz if any
+tarball=${tarball/%.gz/}
+
+cd "$(dirname "$0")"/../..
+mkdir -p "$(dirname "$tarball")"
+git archive --prefix="$name/" --format=tar "$commit" > "$tarball"
+
+mtime=`git show --pretty=format:%ct "$commit""^{commit}" -- | head -n 1`
+tarargs="--owner=root --group=root"
+
+mkdir -p "$tmpdir/$name"
+git cat-file -p "${commit}:roms" | awk ' { print $4, $3 } ' \
+ > "$tmpdir/$name/EXTERNAL_DEPENDENCIES"
+touch -d "@$mtime" "$tmpdir/$name/EXTERNAL_DEPENDENCIES"
+tar -rf "$tarball" -C "$tmpdir" \
+ $tarargs \
+ "$name/EXTERNAL_DEPENDENCIES"
+rm -rf "$tmpdir"
+
+if [[ -n "$formal" ]]; then
+ mkdir -p "$tmpdir/$name"
+ echo "$name" > "$tmpdir/$name/KVM_VERSION"
+ touch -d "@$mtime" "$tmpdir/$name/KVM_VERSION"
+ tar -rf "$tarball" -C "$tmpdir" "$name/KVM_VERSION" \
+ $tarargs
+ rm -rf "$tmpdir"
+fi
+
+rm -f "$tarball.gz"
+gzip -9 "$tarball"
+tarball="$tarball.gz"
+
+if [[ -n "$upload" ]]; then
+ rsync --progress -h "$tarball" avik@frs.sourceforge.net:uploads/
+fi
diff --git a/sysemu.h b/sysemu.h
index bc2c78892..eb3da5a2b 100644
--- a/sysemu.h
+++ b/sysemu.h
@@ -149,6 +149,9 @@ extern int nb_option_roms;
extern const char *prom_envs[MAX_PROM_ENVS];
extern unsigned int nb_prom_envs;
+/* acpi */
+void qemu_system_cpu_hot_add(int cpu, int state);
+
/* pci-hotplug */
void pci_device_hot_add(Monitor *mon, const QDict *qdict);
int pci_drive_hot_add(Monitor *mon, const QDict *qdict,
diff --git a/target-i386/cpu.h b/target-i386/cpu.h
index 2460f6348..0ea47b5ed 100644
--- a/target-i386/cpu.h
+++ b/target-i386/cpu.h
@@ -942,6 +942,7 @@ void cpu_x86_update_cr4(CPUX86State *env, uint32_t new_cr4);
/* hw/pc.c */
void cpu_smm_update(CPUX86State *env);
uint64_t cpu_get_tsc(CPUX86State *env);
+CPUArchState *pc_new_cpu(const char *cpu_model);
/* used to debug */
#define X86_DUMP_FPU 0x0001 /* dump FPU state too */
diff --git a/vl.c b/vl.c
index 23ab3a34d..5d9fc551f 100644
--- a/vl.c
+++ b/vl.c
@@ -2980,6 +2980,37 @@ int main(int argc, char **argv, char **envp)
machine = machine_parse(optarg);
}
break;
+ case QEMU_OPTION_no_kvm:
+ olist = qemu_find_opts("machine");
+ qemu_opts_parse(olist, "accel=tcg", 0);
+ break;
+#ifdef CONFIG_KVM_OPTIONS
+ case QEMU_OPTION_no_kvm_irqchip: {
+ olist = qemu_find_opts("machine");
+ qemu_opts_parse(olist, "kernel_irqchip=off", 0);
+ break;
+ }
+ case QEMU_OPTION_no_kvm_pit: {
+ fprintf(stderr, "Warning: KVM PIT can no longer be disabled "
+ "separately.\n");
+ break;
+ }
+ case QEMU_OPTION_no_kvm_pit_reinjection: {
+ static GlobalProperty kvm_pit_lost_tick_policy[] = {
+ {
+ .driver = "kvm-pit",
+ .property = "lost_tick_policy",
+ .value = "discard",
+ },
+ { /* end of list */ }
+ };
+
+ fprintf(stderr, "Warning: option deprecated, use "
+ "lost_tick_policy property of kvm-pit instead.\n");
+ qdev_prop_register_global_list(kvm_pit_lost_tick_policy);
+ break;
+ }
+#endif
case QEMU_OPTION_usb:
usb_enabled = 1;
break;
@@ -3063,6 +3094,10 @@ int main(int argc, char **argv, char **envp)
case QEMU_OPTION_semihosting:
semihosting_enabled = 1;
break;
+ case QEMU_OPTION_tdf:
+ fprintf(stderr, "Warning: user space PIT time drift fix "
+ "is no longer supported.\n");
+ break;
case QEMU_OPTION_name:
qemu_name = g_strdup(optarg);
{