aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
Diffstat (limited to 'hw')
-rw-r--r--hw/block/nvme.c6
-rw-r--r--hw/bt/hci-csr.c67
-rw-r--r--hw/char/escc.c30
-rw-r--r--hw/char/etraxfs_ser.c27
-rw-r--r--hw/char/lm32_juart.c17
-rw-r--r--hw/char/lm32_uart.c28
-rw-r--r--hw/char/milkymist-uart.c10
-rw-r--r--hw/core/Makefile.objs1
-rw-r--r--hw/core/bus.c251
-rw-r--r--hw/core/qdev.c222
-rw-r--r--hw/cris/axis_dev88.c4
-rw-r--r--hw/ide/ahci.c6
-rw-r--r--hw/ide/core.c20
-rw-r--r--hw/ide/internal.h6
-rw-r--r--hw/ide/macio.c2
-rw-r--r--hw/lm32/lm32.h19
-rw-r--r--hw/lm32/lm32_boards.c9
-rw-r--r--hw/lm32/milkymist-hw.h4
-rw-r--r--hw/lm32/milkymist.c4
-rw-r--r--hw/misc/ivshmem.c5
-rw-r--r--hw/net/spapr_llan.c45
-rw-r--r--hw/ppc/spapr.c20
-rw-r--r--hw/ppc/spapr_hcall.c14
-rw-r--r--hw/ppc/spapr_iommu.c60
-rw-r--r--hw/ppc/spapr_pci.c14
-rw-r--r--hw/s390x/s390-skeys.c26
-rw-r--r--hw/scsi/megasas.c6
-rw-r--r--hw/scsi/mptsas.c9
-rw-r--r--hw/scsi/scsi-disk.c415
-rw-r--r--hw/scsi/scsi-generic.c12
-rw-r--r--hw/scsi/vmw_pvscsi.c24
-rw-r--r--hw/vfio/common.c140
-rw-r--r--hw/vfio/pci-quirks.c643
-rw-r--r--hw/vfio/pci.c151
-rw-r--r--hw/vfio/pci.h8
-rw-r--r--hw/virtio/vhost-user.c25
36 files changed, 1764 insertions, 586 deletions
diff --git a/hw/block/nvme.c b/hw/block/nvme.c
index 173988ee84..9faad29fad 100644
--- a/hw/block/nvme.c
+++ b/hw/block/nvme.c
@@ -239,7 +239,7 @@ static uint16_t nvme_rw(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd,
uint8_t lba_index = NVME_ID_NS_FLBAS_INDEX(ns->id_ns.flbas);
uint8_t data_shift = ns->id_ns.lbaf[lba_index].ds;
uint64_t data_size = (uint64_t)nlb << data_shift;
- uint64_t aio_slba = slba << (data_shift - BDRV_SECTOR_BITS);
+ uint64_t data_offset = slba << data_shift;
int is_write = rw->opcode == NVME_CMD_WRITE ? 1 : 0;
enum BlockAcctType acct = is_write ? BLOCK_ACCT_WRITE : BLOCK_ACCT_READ;
@@ -258,8 +258,8 @@ static uint16_t nvme_rw(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd,
req->has_sg = true;
dma_acct_start(n->conf.blk, &req->acct, &req->qsg, acct);
req->aiocb = is_write ?
- dma_blk_write(n->conf.blk, &req->qsg, aio_slba, nvme_rw_cb, req) :
- dma_blk_read(n->conf.blk, &req->qsg, aio_slba, nvme_rw_cb, req);
+ dma_blk_write(n->conf.blk, &req->qsg, data_offset, nvme_rw_cb, req) :
+ dma_blk_read(n->conf.blk, &req->qsg, data_offset, nvme_rw_cb, req);
return NVME_NO_COMPLETE;
}
diff --git a/hw/bt/hci-csr.c b/hw/bt/hci-csr.c
index e6b8998253..d688372ca3 100644
--- a/hw/bt/hci-csr.c
+++ b/hw/bt/hci-csr.c
@@ -39,9 +39,14 @@ struct csrhci_s {
int out_size;
uint8_t outfifo[FIFO_LEN * 2];
uint8_t inpkt[FIFO_LEN];
+ enum {
+ CSR_HDR_LEN,
+ CSR_DATA_LEN,
+ CSR_DATA
+ } in_state;
int in_len;
int in_hdr;
- int in_data;
+ int in_needed;
QEMUTimer *out_tm;
int64_t baud_delay;
@@ -296,38 +301,60 @@ static int csrhci_data_len(const uint8_t *pkt)
exit(-1);
}
+static void csrhci_ready_for_next_inpkt(struct csrhci_s *s)
+{
+ s->in_state = CSR_HDR_LEN;
+ s->in_len = 0;
+ s->in_needed = 2;
+ s->in_hdr = INT_MAX;
+}
+
static int csrhci_write(struct CharDriverState *chr,
const uint8_t *buf, int len)
{
struct csrhci_s *s = (struct csrhci_s *) chr->opaque;
- int plen = s->in_len;
+ int total = 0;
if (!s->enable)
return 0;
- s->in_len += len;
- memcpy(s->inpkt + plen, buf, len);
+ for (;;) {
+ int cnt = MIN(len, s->in_needed - s->in_len);
+ if (cnt) {
+ memcpy(s->inpkt + s->in_len, buf, cnt);
+ s->in_len += cnt;
+ buf += cnt;
+ len -= cnt;
+ total += cnt;
+ }
+
+ if (s->in_len < s->in_needed) {
+ break;
+ }
- while (1) {
- if (s->in_len >= 2 && plen < 2)
+ if (s->in_state == CSR_HDR_LEN) {
s->in_hdr = csrhci_header_len(s->inpkt) + 1;
+ assert(s->in_hdr >= s->in_needed);
+ s->in_needed = s->in_hdr;
+ s->in_state = CSR_DATA_LEN;
+ continue;
+ }
- if (s->in_len >= s->in_hdr && plen < s->in_hdr)
- s->in_data = csrhci_data_len(s->inpkt) + s->in_hdr;
+ if (s->in_state == CSR_DATA_LEN) {
+ s->in_needed += csrhci_data_len(s->inpkt);
+ /* hci_acl_hdr could specify more than 4096 bytes, so assert. */
+ assert(s->in_needed <= sizeof(s->inpkt));
+ s->in_state = CSR_DATA;
+ continue;
+ }
- if (s->in_len >= s->in_data) {
+ if (s->in_state == CSR_DATA) {
csrhci_in_packet(s, s->inpkt);
-
- memmove(s->inpkt, s->inpkt + s->in_len, s->in_len - s->in_data);
- s->in_len -= s->in_data;
- s->in_hdr = INT_MAX;
- s->in_data = INT_MAX;
- plen = 0;
- } else
- break;
+ csrhci_ready_for_next_inpkt(s);
+ }
}
- return len;
+ return total;
}
static void csrhci_out_hci_packet_event(void *opaque,
@@ -389,11 +416,9 @@ static void csrhci_reset(struct csrhci_s *s)
{
s->out_len = 0;
s->out_size = FIFO_LEN;
- s->in_len = 0;
+ csrhci_ready_for_next_inpkt(s);
s->baud_delay = NANOSECONDS_PER_SECOND;
s->enable = 0;
- s->in_hdr = INT_MAX;
- s->in_data = INT_MAX;
s->modem_state = 0;
/* After a while... (but sooner than 10ms) */
diff --git a/hw/char/escc.c b/hw/char/escc.c
index 7bf09a0077..8e6a7df465 100644
--- a/hw/char/escc.c
+++ b/hw/char/escc.c
@@ -983,9 +983,10 @@ void slavio_serial_ms_kbd_init(hwaddr base, qemu_irq irq,
sysbus_mmio_map(s, 0, base);
}
-static int escc_init1(SysBusDevice *dev)
+static void escc_init1(Object *obj)
{
- ESCCState *s = ESCC(dev);
+ ESCCState *s = ESCC(obj);
+ SysBusDevice *dev = SYS_BUS_DEVICE(obj);
unsigned int i;
s->chn[0].disabled = s->disabled;
@@ -994,17 +995,26 @@ static int escc_init1(SysBusDevice *dev)
sysbus_init_irq(dev, &s->chn[i].irq);
s->chn[i].chn = 1 - i;
s->chn[i].clock = s->frequency / 2;
- if (s->chn[i].chr) {
- qemu_chr_add_handlers(s->chn[i].chr, serial_can_receive,
- serial_receive1, serial_event, &s->chn[i]);
- }
}
s->chn[0].otherchn = &s->chn[1];
s->chn[1].otherchn = &s->chn[0];
- memory_region_init_io(&s->mmio, OBJECT(s), &escc_mem_ops, s, "escc",
+ memory_region_init_io(&s->mmio, obj, &escc_mem_ops, s, "escc",
ESCC_SIZE << s->it_shift);
sysbus_init_mmio(dev, &s->mmio);
+}
+
+static void escc_realize(DeviceState *dev, Error **errp)
+{
+ ESCCState *s = ESCC(dev);
+ unsigned int i;
+
+ for (i = 0; i < 2; i++) {
+ if (s->chn[i].chr) {
+ qemu_chr_add_handlers(s->chn[i].chr, serial_can_receive,
+ serial_receive1, serial_event, &s->chn[i]);
+ }
+ }
if (s->chn[0].type == mouse) {
qemu_add_mouse_event_handler(sunmouse_event, &s->chn[0], 0,
@@ -1014,8 +1024,6 @@ static int escc_init1(SysBusDevice *dev)
s->chn[1].hs = qemu_input_handler_register((DeviceState *)(&s->chn[1]),
&sunkbd_handler);
}
-
- return 0;
}
static Property escc_properties[] = {
@@ -1032,10 +1040,9 @@ static Property escc_properties[] = {
static void escc_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
- k->init = escc_init1;
dc->reset = escc_reset;
+ dc->realize = escc_realize;
dc->vmsd = &vmstate_escc;
dc->props = escc_properties;
set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
@@ -1045,6 +1052,7 @@ static const TypeInfo escc_info = {
.name = TYPE_ESCC,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(ESCCState),
+ .instance_init = escc_init1,
.class_init = escc_class_init,
};
diff --git a/hw/char/etraxfs_ser.c b/hw/char/etraxfs_ser.c
index 146b387e7e..04ca04fe2c 100644
--- a/hw/char/etraxfs_ser.c
+++ b/hw/char/etraxfs_ser.c
@@ -159,6 +159,11 @@ static const MemoryRegionOps ser_ops = {
}
};
+static Property etraxfs_ser_properties[] = {
+ DEFINE_PROP_CHR("chardev", ETRAXSerial, chr),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
static void serial_receive(void *opaque, const uint8_t *buf, int size)
{
ETRAXSerial *s = opaque;
@@ -209,40 +214,42 @@ static void etraxfs_ser_reset(DeviceState *d)
}
-static int etraxfs_ser_init(SysBusDevice *dev)
+static void etraxfs_ser_init(Object *obj)
{
- ETRAXSerial *s = ETRAX_SERIAL(dev);
+ ETRAXSerial *s = ETRAX_SERIAL(obj);
+ SysBusDevice *dev = SYS_BUS_DEVICE(obj);
sysbus_init_irq(dev, &s->irq);
- memory_region_init_io(&s->mmio, OBJECT(s), &ser_ops, s,
+ memory_region_init_io(&s->mmio, obj, &ser_ops, s,
"etraxfs-serial", R_MAX * 4);
sysbus_init_mmio(dev, &s->mmio);
+}
+
+static void etraxfs_ser_realize(DeviceState *dev, Error **errp)
+{
+ ETRAXSerial *s = ETRAX_SERIAL(dev);
- /* FIXME use a qdev chardev prop instead of qemu_char_get_next_serial() */
- s->chr = qemu_char_get_next_serial();
if (s->chr) {
qemu_chr_add_handlers(s->chr,
serial_can_receive, serial_receive,
serial_event, s);
}
- return 0;
}
static void etraxfs_ser_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
- k->init = etraxfs_ser_init;
dc->reset = etraxfs_ser_reset;
- /* Reason: init() method uses qemu_char_get_next_serial() */
- dc->cannot_instantiate_with_device_add_yet = true;
+ dc->props = etraxfs_ser_properties;
+ dc->realize = etraxfs_ser_realize;
}
static const TypeInfo etraxfs_ser_info = {
.name = TYPE_ETRAX_FS_SERIAL,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(ETRAXSerial),
+ .instance_init = etraxfs_ser_init,
.class_init = etraxfs_ser_class_init,
};
diff --git a/hw/char/lm32_juart.c b/hw/char/lm32_juart.c
index 5bf8acfe8f..28c2cf702d 100644
--- a/hw/char/lm32_juart.c
+++ b/hw/char/lm32_juart.c
@@ -114,17 +114,13 @@ static void juart_reset(DeviceState *d)
s->jrx = 0;
}
-static int lm32_juart_init(SysBusDevice *dev)
+static void lm32_juart_realize(DeviceState *dev, Error **errp)
{
LM32JuartState *s = LM32_JUART(dev);
- /* FIXME use a qdev chardev prop instead of qemu_char_get_next_serial() */
- s->chr = qemu_char_get_next_serial();
if (s->chr) {
qemu_chr_add_handlers(s->chr, juart_can_rx, juart_rx, juart_event, s);
}
-
- return 0;
}
static const VMStateDescription vmstate_lm32_juart = {
@@ -138,16 +134,19 @@ static const VMStateDescription vmstate_lm32_juart = {
}
};
+static Property lm32_juart_properties[] = {
+ DEFINE_PROP_CHR("chardev", LM32JuartState, chr),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
static void lm32_juart_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
- k->init = lm32_juart_init;
dc->reset = juart_reset;
dc->vmsd = &vmstate_lm32_juart;
- /* Reason: init() method uses qemu_char_get_next_serial() */
- dc->cannot_instantiate_with_device_add_yet = true;
+ dc->props = lm32_juart_properties;
+ dc->realize = lm32_juart_realize;
}
static const TypeInfo lm32_juart_info = {
diff --git a/hw/char/lm32_uart.c b/hw/char/lm32_uart.c
index 036813d0f3..b5c760dda3 100644
--- a/hw/char/lm32_uart.c
+++ b/hw/char/lm32_uart.c
@@ -249,23 +249,25 @@ static void uart_reset(DeviceState *d)
s->regs[R_LSR] = LSR_THRE | LSR_TEMT;
}
-static int lm32_uart_init(SysBusDevice *dev)
+static void lm32_uart_init(Object *obj)
{
- LM32UartState *s = LM32_UART(dev);
+ LM32UartState *s = LM32_UART(obj);
+ SysBusDevice *dev = SYS_BUS_DEVICE(obj);
sysbus_init_irq(dev, &s->irq);
- memory_region_init_io(&s->iomem, OBJECT(s), &uart_ops, s,
+ memory_region_init_io(&s->iomem, obj, &uart_ops, s,
"uart", R_MAX * 4);
sysbus_init_mmio(dev, &s->iomem);
+}
+
+static void lm32_uart_realize(DeviceState *dev, Error **errp)
+{
+ LM32UartState *s = LM32_UART(dev);
- /* FIXME use a qdev chardev prop instead of qemu_char_get_next_serial() */
- s->chr = qemu_char_get_next_serial();
if (s->chr) {
qemu_chr_add_handlers(s->chr, uart_can_rx, uart_rx, uart_event, s);
}
-
- return 0;
}
static const VMStateDescription vmstate_lm32_uart = {
@@ -278,22 +280,26 @@ static const VMStateDescription vmstate_lm32_uart = {
}
};
+static Property lm32_uart_properties[] = {
+ DEFINE_PROP_CHR("chardev", LM32UartState, chr),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
static void lm32_uart_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
- k->init = lm32_uart_init;
dc->reset = uart_reset;
dc->vmsd = &vmstate_lm32_uart;
- /* Reason: init() method uses qemu_char_get_next_serial() */
- dc->cannot_instantiate_with_device_add_yet = true;
+ dc->props = lm32_uart_properties;
+ dc->realize = lm32_uart_realize;
}
static const TypeInfo lm32_uart_info = {
.name = TYPE_LM32_UART,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(LM32UartState),
+ .instance_init = lm32_uart_init,
.class_init = lm32_uart_class_init,
};
diff --git a/hw/char/milkymist-uart.c b/hw/char/milkymist-uart.c
index 03b36b2236..72f8484668 100644
--- a/hw/char/milkymist-uart.c
+++ b/hw/char/milkymist-uart.c
@@ -200,8 +200,6 @@ static void milkymist_uart_realize(DeviceState *dev, Error **errp)
{
MilkymistUartState *s = MILKYMIST_UART(dev);
- /* FIXME use a qdev chardev prop instead of qemu_char_get_next_serial() */
- s->chr = qemu_char_get_next_serial();
if (s->chr) {
qemu_chr_add_handlers(s->chr, uart_can_rx, uart_rx, uart_event, s);
}
@@ -229,6 +227,11 @@ static const VMStateDescription vmstate_milkymist_uart = {
}
};
+static Property milkymist_uart_properties[] = {
+ DEFINE_PROP_CHR("chardev", MilkymistUartState, chr),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
static void milkymist_uart_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
@@ -236,8 +239,7 @@ static void milkymist_uart_class_init(ObjectClass *klass, void *data)
dc->realize = milkymist_uart_realize;
dc->reset = milkymist_uart_reset;
dc->vmsd = &vmstate_milkymist_uart;
- /* Reason: realize() method uses qemu_char_get_next_serial() */
- dc->cannot_instantiate_with_device_add_yet = true;
+ dc->props = milkymist_uart_properties;
}
static const TypeInfo milkymist_uart_info = {
diff --git a/hw/core/Makefile.objs b/hw/core/Makefile.objs
index 70951d4137..82a9ef84f8 100644
--- a/hw/core/Makefile.objs
+++ b/hw/core/Makefile.objs
@@ -1,5 +1,6 @@
# core qdev-related obj files, also used by *-user:
common-obj-y += qdev.o qdev-properties.o
+common-obj-y += bus.o
common-obj-y += fw-path-provider.o
# irq.o needed for qdev GPIO handling:
common-obj-y += irq.o
diff --git a/hw/core/bus.c b/hw/core/bus.c
new file mode 100644
index 0000000000..3e3f8ac740
--- /dev/null
+++ b/hw/core/bus.c
@@ -0,0 +1,251 @@
+/*
+ * Dynamic device configuration and creation -- buses.
+ *
+ * Copyright (c) 2009 CodeSourcery
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "hw/qdev.h"
+#include "qapi/error.h"
+
+static void qbus_set_hotplug_handler_internal(BusState *bus, Object *handler,
+ Error **errp)
+{
+
+ object_property_set_link(OBJECT(bus), OBJECT(handler),
+ QDEV_HOTPLUG_HANDLER_PROPERTY, errp);
+}
+
+void qbus_set_hotplug_handler(BusState *bus, DeviceState *handler, Error **errp)
+{
+ qbus_set_hotplug_handler_internal(bus, OBJECT(handler), errp);
+}
+
+void qbus_set_bus_hotplug_handler(BusState *bus, Error **errp)
+{
+ qbus_set_hotplug_handler_internal(bus, OBJECT(bus), errp);
+}
+
+int qbus_walk_children(BusState *bus,
+ qdev_walkerfn *pre_devfn, qbus_walkerfn *pre_busfn,
+ qdev_walkerfn *post_devfn, qbus_walkerfn *post_busfn,
+ void *opaque)
+{
+ BusChild *kid;
+ int err;
+
+ if (pre_busfn) {
+ err = pre_busfn(bus, opaque);
+ if (err) {
+ return err;
+ }
+ }
+
+ QTAILQ_FOREACH(kid, &bus->children, sibling) {
+ err = qdev_walk_children(kid->child,
+ pre_devfn, pre_busfn,
+ post_devfn, post_busfn, opaque);
+ if (err < 0) {
+ return err;
+ }
+ }
+
+ if (post_busfn) {
+ err = post_busfn(bus, opaque);
+ if (err) {
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+static void qbus_realize(BusState *bus, DeviceState *parent, const char *name)
+{
+ const char *typename = object_get_typename(OBJECT(bus));
+ BusClass *bc;
+ char *buf;
+ int i, len, bus_id;
+
+ bus->parent = parent;
+
+ if (name) {
+ bus->name = g_strdup(name);
+ } else if (bus->parent && bus->parent->id) {
+ /* parent device has id -> use it plus parent-bus-id for bus name */
+ bus_id = bus->parent->num_child_bus;
+
+ len = strlen(bus->parent->id) + 16;
+ buf = g_malloc(len);
+ snprintf(buf, len, "%s.%d", bus->parent->id, bus_id);
+ bus->name = buf;
+ } else {
+ /* no id -> use lowercase bus type plus global bus-id for bus name */
+ bc = BUS_GET_CLASS(bus);
+ bus_id = bc->automatic_ids++;
+
+ len = strlen(typename) + 16;
+ buf = g_malloc(len);
+ len = snprintf(buf, len, "%s.%d", typename, bus_id);
+ for (i = 0; i < len; i++) {
+ buf[i] = qemu_tolower(buf[i]);
+ }
+ bus->name = buf;
+ }
+
+ if (bus->parent) {
+ QLIST_INSERT_HEAD(&bus->parent->child_bus, bus, sibling);
+ bus->parent->num_child_bus++;
+ object_property_add_child(OBJECT(bus->parent), bus->name, OBJECT(bus), NULL);
+ object_unref(OBJECT(bus));
+ } else if (bus != sysbus_get_default()) {
+ /* TODO: once all bus devices are qdevified,
+ only reset handler for main_system_bus should be registered here. */
+ qemu_register_reset(qbus_reset_all_fn, bus);
+ }
+}
+
+static void bus_unparent(Object *obj)
+{
+ BusState *bus = BUS(obj);
+ BusChild *kid;
+
+ while ((kid = QTAILQ_FIRST(&bus->children)) != NULL) {
+ DeviceState *dev = kid->child;
+ object_unparent(OBJECT(dev));
+ }
+ if (bus->parent) {
+ QLIST_REMOVE(bus, sibling);
+ bus->parent->num_child_bus--;
+ bus->parent = NULL;
+ } else {
+ assert(bus != sysbus_get_default()); /* main_system_bus is never freed */
+ qemu_unregister_reset(qbus_reset_all_fn, bus);
+ }
+}
+
+void qbus_create_inplace(void *bus, size_t size, const char *typename,
+ DeviceState *parent, const char *name)
+{
+ object_initialize(bus, size, typename);
+ qbus_realize(bus, parent, name);
+}
+
+BusState *qbus_create(const char *typename, DeviceState *parent, const char *name)
+{
+ BusState *bus;
+
+ bus = BUS(object_new(typename));
+ qbus_realize(bus, parent, name);
+
+ return bus;
+}
+
+static bool bus_get_realized(Object *obj, Error **errp)
+{
+ BusState *bus = BUS(obj);
+
+ return bus->realized;
+}
+
+static void bus_set_realized(Object *obj, bool value, Error **errp)
+{
+ BusState *bus = BUS(obj);
+ BusClass *bc = BUS_GET_CLASS(bus);
+ BusChild *kid;
+ Error *local_err = NULL;
+
+ if (value && !bus->realized) {
+ if (bc->realize) {
+ bc->realize(bus, &local_err);
+ }
+
+ /* TODO: recursive realization */
+ } else if (!value && bus->realized) {
+ QTAILQ_FOREACH(kid, &bus->children, sibling) {
+ DeviceState *dev = kid->child;
+ object_property_set_bool(OBJECT(dev), false, "realized",
+ &local_err);
+ if (local_err != NULL) {
+ break;
+ }
+ }
+ if (bc->unrealize && local_err == NULL) {
+ bc->unrealize(bus, &local_err);
+ }
+ }
+
+ if (local_err != NULL) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ bus->realized = value;
+}
+
+static void qbus_initfn(Object *obj)
+{
+ BusState *bus = BUS(obj);
+
+ QTAILQ_INIT(&bus->children);
+ object_property_add_link(obj, QDEV_HOTPLUG_HANDLER_PROPERTY,
+ TYPE_HOTPLUG_HANDLER,
+ (Object **)&bus->hotplug_handler,
+ object_property_allow_set_link,
+ OBJ_PROP_LINK_UNREF_ON_RELEASE,
+ NULL);
+ object_property_add_bool(obj, "realized",
+ bus_get_realized, bus_set_realized, NULL);
+}
+
+static char *default_bus_get_fw_dev_path(DeviceState *dev)
+{
+ return g_strdup(object_get_typename(OBJECT(dev)));
+}
+
+static void bus_class_init(ObjectClass *class, void *data)
+{
+ BusClass *bc = BUS_CLASS(class);
+
+ class->unparent = bus_unparent;
+ bc->get_fw_dev_path = default_bus_get_fw_dev_path;
+}
+
+static void qbus_finalize(Object *obj)
+{
+ BusState *bus = BUS(obj);
+
+ g_free((char *)bus->name);
+}
+
+static const TypeInfo bus_info = {
+ .name = TYPE_BUS,
+ .parent = TYPE_OBJECT,
+ .instance_size = sizeof(BusState),
+ .abstract = true,
+ .class_size = sizeof(BusClass),
+ .instance_init = qbus_initfn,
+ .instance_finalize = qbus_finalize,
+ .class_init = bus_class_init,
+};
+
+static void bus_register_types(void)
+{
+ type_register_static(&bus_info);
+}
+
+type_init(bus_register_types)
diff --git a/hw/core/qdev.c b/hw/core/qdev.c
index db41aa1f26..853162b670 100644
--- a/hw/core/qdev.c
+++ b/hw/core/qdev.c
@@ -109,24 +109,6 @@ void qdev_set_parent_bus(DeviceState *dev, BusState *bus)
bus_add_child(bus, dev);
}
-static void qbus_set_hotplug_handler_internal(BusState *bus, Object *handler,
- Error **errp)
-{
-
- object_property_set_link(OBJECT(bus), OBJECT(handler),
- QDEV_HOTPLUG_HANDLER_PROPERTY, errp);
-}
-
-void qbus_set_hotplug_handler(BusState *bus, DeviceState *handler, Error **errp)
-{
- qbus_set_hotplug_handler_internal(bus, OBJECT(handler), errp);
-}
-
-void qbus_set_bus_hotplug_handler(BusState *bus, Error **errp)
-{
- qbus_set_hotplug_handler_internal(bus, OBJECT(bus), errp);
-}
-
/* Create a new device. This only initializes the device state
structure and allows properties to be set. The device still needs
to be realized. See qdev-core.h. */
@@ -595,40 +577,6 @@ BusState *qdev_get_child_bus(DeviceState *dev, const char *name)
return NULL;
}
-int qbus_walk_children(BusState *bus,
- qdev_walkerfn *pre_devfn, qbus_walkerfn *pre_busfn,
- qdev_walkerfn *post_devfn, qbus_walkerfn *post_busfn,
- void *opaque)
-{
- BusChild *kid;
- int err;
-
- if (pre_busfn) {
- err = pre_busfn(bus, opaque);
- if (err) {
- return err;
- }
- }
-
- QTAILQ_FOREACH(kid, &bus->children, sibling) {
- err = qdev_walk_children(kid->child,
- pre_devfn, pre_busfn,
- post_devfn, post_busfn, opaque);
- if (err < 0) {
- return err;
- }
- }
-
- if (post_busfn) {
- err = post_busfn(bus, opaque);
- if (err) {
- return err;
- }
- }
-
- return 0;
-}
-
int qdev_walk_children(DeviceState *dev,
qdev_walkerfn *pre_devfn, qbus_walkerfn *pre_busfn,
qdev_walkerfn *post_devfn, qbus_walkerfn *post_busfn,
@@ -685,129 +633,6 @@ DeviceState *qdev_find_recursive(BusState *bus, const char *id)
return NULL;
}
-static void qbus_realize(BusState *bus, DeviceState *parent, const char *name)
-{
- const char *typename = object_get_typename(OBJECT(bus));
- BusClass *bc;
- char *buf;
- int i, len, bus_id;
-
- bus->parent = parent;
-
- if (name) {
- bus->name = g_strdup(name);
- } else if (bus->parent && bus->parent->id) {
- /* parent device has id -> use it plus parent-bus-id for bus name */
- bus_id = bus->parent->num_child_bus;
-
- len = strlen(bus->parent->id) + 16;
- buf = g_malloc(len);
- snprintf(buf, len, "%s.%d", bus->parent->id, bus_id);
- bus->name = buf;
- } else {
- /* no id -> use lowercase bus type plus global bus-id for bus name */
- bc = BUS_GET_CLASS(bus);
- bus_id = bc->automatic_ids++;
-
- len = strlen(typename) + 16;
- buf = g_malloc(len);
- len = snprintf(buf, len, "%s.%d", typename, bus_id);
- for (i = 0; i < len; i++) {
- buf[i] = qemu_tolower(buf[i]);
- }
- bus->name = buf;
- }
-
- if (bus->parent) {
- QLIST_INSERT_HEAD(&bus->parent->child_bus, bus, sibling);
- bus->parent->num_child_bus++;
- object_property_add_child(OBJECT(bus->parent), bus->name, OBJECT(bus), NULL);
- object_unref(OBJECT(bus));
- } else if (bus != sysbus_get_default()) {
- /* TODO: once all bus devices are qdevified,
- only reset handler for main_system_bus should be registered here. */
- qemu_register_reset(qbus_reset_all_fn, bus);
- }
-}
-
-static void bus_unparent(Object *obj)
-{
- BusState *bus = BUS(obj);
- BusChild *kid;
-
- while ((kid = QTAILQ_FIRST(&bus->children)) != NULL) {
- DeviceState *dev = kid->child;
- object_unparent(OBJECT(dev));
- }
- if (bus->parent) {
- QLIST_REMOVE(bus, sibling);
- bus->parent->num_child_bus--;
- bus->parent = NULL;
- } else {
- assert(bus != sysbus_get_default()); /* main_system_bus is never freed */
- qemu_unregister_reset(qbus_reset_all_fn, bus);
- }
-}
-
-static bool bus_get_realized(Object *obj, Error **errp)
-{
- BusState *bus = BUS(obj);
-
- return bus->realized;
-}
-
-static void bus_set_realized(Object *obj, bool value, Error **errp)
-{
- BusState *bus = BUS(obj);
- BusClass *bc = BUS_GET_CLASS(bus);
- BusChild *kid;
- Error *local_err = NULL;
-
- if (value && !bus->realized) {
- if (bc->realize) {
- bc->realize(bus, &local_err);
- }
-
- /* TODO: recursive realization */
- } else if (!value && bus->realized) {
- QTAILQ_FOREACH(kid, &bus->children, sibling) {
- DeviceState *dev = kid->child;
- object_property_set_bool(OBJECT(dev), false, "realized",
- &local_err);
- if (local_err != NULL) {
- break;
- }
- }
- if (bc->unrealize && local_err == NULL) {
- bc->unrealize(bus, &local_err);
- }
- }
-
- if (local_err != NULL) {
- error_propagate(errp, local_err);
- return;
- }
-
- bus->realized = value;
-}
-
-void qbus_create_inplace(void *bus, size_t size, const char *typename,
- DeviceState *parent, const char *name)
-{
- object_initialize(bus, size, typename);
- qbus_realize(bus, parent, name);
-}
-
-BusState *qbus_create(const char *typename, DeviceState *parent, const char *name)
-{
- BusState *bus;
-
- bus = BUS(object_new(typename));
- qbus_realize(bus, parent, name);
-
- return bus;
-}
-
static char *bus_get_fw_dev_path(BusState *bus, DeviceState *dev)
{
BusClass *bc = BUS_GET_CLASS(bus);
@@ -1315,55 +1140,8 @@ static const TypeInfo device_type_info = {
.class_size = sizeof(DeviceClass),
};
-static void qbus_initfn(Object *obj)
-{
- BusState *bus = BUS(obj);
-
- QTAILQ_INIT(&bus->children);
- object_property_add_link(obj, QDEV_HOTPLUG_HANDLER_PROPERTY,
- TYPE_HOTPLUG_HANDLER,
- (Object **)&bus->hotplug_handler,
- object_property_allow_set_link,
- OBJ_PROP_LINK_UNREF_ON_RELEASE,
- NULL);
- object_property_add_bool(obj, "realized",
- bus_get_realized, bus_set_realized, NULL);
-}
-
-static char *default_bus_get_fw_dev_path(DeviceState *dev)
-{
- return g_strdup(object_get_typename(OBJECT(dev)));
-}
-
-static void bus_class_init(ObjectClass *class, void *data)
-{
- BusClass *bc = BUS_CLASS(class);
-
- class->unparent = bus_unparent;
- bc->get_fw_dev_path = default_bus_get_fw_dev_path;
-}
-
-static void qbus_finalize(Object *obj)
-{
- BusState *bus = BUS(obj);
-
- g_free((char *)bus->name);
-}
-
-static const TypeInfo bus_info = {
- .name = TYPE_BUS,
- .parent = TYPE_OBJECT,
- .instance_size = sizeof(BusState),
- .abstract = true,
- .class_size = sizeof(BusClass),
- .instance_init = qbus_initfn,
- .instance_finalize = qbus_finalize,
- .class_init = bus_class_init,
-};
-
static void qdev_register_types(void)
{
- type_register_static(&bus_info);
type_register_static(&device_type_info);
}
diff --git a/hw/cris/axis_dev88.c b/hw/cris/axis_dev88.c
index 9f58658741..60df8877c1 100644
--- a/hw/cris/axis_dev88.c
+++ b/hw/cris/axis_dev88.c
@@ -37,6 +37,7 @@
#include "sysemu/block-backend.h"
#include "exec/address-spaces.h"
#include "sysemu/qtest.h"
+#include "sysemu/sysemu.h"
#define D(x)
#define DNAND(x)
@@ -341,8 +342,7 @@ void axisdev88_init(MachineState *machine)
sysbus_create_varargs("etraxfs,timer", 0x3005e000, irq[0x1b], nmi[1], NULL);
for (i = 0; i < 4; i++) {
- sysbus_create_simple("etraxfs,serial", 0x30026000 + i * 0x2000,
- irq[0x14 + i]);
+ etraxfs_ser_create(0x30026000 + i * 0x2000, irq[0x14 + i], serial_hds[i]);
}
if (kernel_filename) {
diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c
index f244bc01c9..502d4f1c7b 100644
--- a/hw/ide/ahci.c
+++ b/hw/ide/ahci.c
@@ -1006,7 +1006,8 @@ static void execute_ncq_command(NCQTransferState *ncq_tfs)
dma_acct_start(ide_state->blk, &ncq_tfs->acct,
&ncq_tfs->sglist, BLOCK_ACCT_READ);
ncq_tfs->aiocb = dma_blk_read(ide_state->blk, &ncq_tfs->sglist,
- ncq_tfs->lba, ncq_cb, ncq_tfs);
+ ncq_tfs->lba << BDRV_SECTOR_BITS,
+ ncq_cb, ncq_tfs);
break;
case WRITE_FPDMA_QUEUED:
DPRINTF(port, "NCQ writing %d sectors to LBA %"PRId64", tag %d\n",
@@ -1018,7 +1019,8 @@ static void execute_ncq_command(NCQTransferState *ncq_tfs)
dma_acct_start(ide_state->blk, &ncq_tfs->acct,
&ncq_tfs->sglist, BLOCK_ACCT_WRITE);
ncq_tfs->aiocb = dma_blk_write(ide_state->blk, &ncq_tfs->sglist,
- ncq_tfs->lba, ncq_cb, ncq_tfs);
+ ncq_tfs->lba << BDRV_SECTOR_BITS,
+ ncq_cb, ncq_tfs);
break;
default:
DPRINTF(port, "error: unsupported NCQ command (0x%02x) received\n",
diff --git a/hw/ide/core.c b/hw/ide/core.c
index fe2bfba489..029f6b9b12 100644
--- a/hw/ide/core.c
+++ b/hw/ide/core.c
@@ -441,13 +441,14 @@ static void ide_issue_trim_cb(void *opaque, int ret)
}
}
-BlockAIOCB *ide_issue_trim(BlockBackend *blk,
- int64_t offset, QEMUIOVector *qiov, BdrvRequestFlags flags,
- BlockCompletionFunc *cb, void *opaque)
+BlockAIOCB *ide_issue_trim(
+ int64_t offset, QEMUIOVector *qiov,
+ BlockCompletionFunc *cb, void *cb_opaque, void *opaque)
{
+ BlockBackend *blk = opaque;
TrimAIOCB *iocb;
- iocb = blk_aio_get(&trim_aiocb_info, blk, cb, opaque);
+ iocb = blk_aio_get(&trim_aiocb_info, blk, cb, cb_opaque);
iocb->blk = blk;
iocb->bh = qemu_bh_new(ide_trim_bh_cb, iocb);
iocb->ret = 0;
@@ -799,6 +800,7 @@ static void ide_dma_cb(void *opaque, int ret)
IDEState *s = opaque;
int n;
int64_t sector_num;
+ uint64_t offset;
bool stay_active = false;
if (ret == -ECANCELED) {
@@ -859,18 +861,20 @@ static void ide_dma_cb(void *opaque, int ret)
return;
}
+ offset = sector_num << BDRV_SECTOR_BITS;
switch (s->dma_cmd) {
case IDE_DMA_READ:
- s->bus->dma->aiocb = dma_blk_read(s->blk, &s->sg, sector_num,
+ s->bus->dma->aiocb = dma_blk_read(s->blk, &s->sg, offset,
ide_dma_cb, s);
break;
case IDE_DMA_WRITE:
- s->bus->dma->aiocb = dma_blk_write(s->blk, &s->sg, sector_num,
+ s->bus->dma->aiocb = dma_blk_write(s->blk, &s->sg, offset,
ide_dma_cb, s);
break;
case IDE_DMA_TRIM:
- s->bus->dma->aiocb = dma_blk_io(s->blk, &s->sg, sector_num,
- ide_issue_trim, ide_dma_cb, s,
+ s->bus->dma->aiocb = dma_blk_io(blk_get_aio_context(s->blk),
+ &s->sg, offset,
+ ide_issue_trim, s->blk, ide_dma_cb, s,
DMA_DIRECTION_TO_DEVICE);
break;
default:
diff --git a/hw/ide/internal.h b/hw/ide/internal.h
index ceb9e5994a..773928af77 100644
--- a/hw/ide/internal.h
+++ b/hw/ide/internal.h
@@ -613,9 +613,9 @@ void ide_transfer_start(IDEState *s, uint8_t *buf, int size,
EndTransferFunc *end_transfer_func);
void ide_transfer_stop(IDEState *s);
void ide_set_inactive(IDEState *s, bool more);
-BlockAIOCB *ide_issue_trim(BlockBackend *blk,
- int64_t offset, QEMUIOVector *qiov, BdrvRequestFlags flags,
- BlockCompletionFunc *cb, void *opaque);
+BlockAIOCB *ide_issue_trim(
+ int64_t offset, QEMUIOVector *qiov,
+ BlockCompletionFunc *cb, void *cb_opaque, void *opaque);
BlockAIOCB *ide_buffered_readv(IDEState *s, int64_t sector_num,
QEMUIOVector *iov, int nb_sectors,
BlockCompletionFunc *cb, void *opaque);
diff --git a/hw/ide/macio.c b/hw/ide/macio.c
index d7d9c0ff3a..42ad68a1c0 100644
--- a/hw/ide/macio.c
+++ b/hw/ide/macio.c
@@ -230,7 +230,7 @@ static void pmac_dma_trim(BlockBackend *blk,
s->io_buffer_index += io->len;
io->len = 0;
- s->bus->dma->aiocb = ide_issue_trim(blk, offset, &io->iov, 0, cb, io);
+ s->bus->dma->aiocb = ide_issue_trim(offset, &io->iov, cb, io, blk);
}
static void pmac_ide_atapi_transfer_cb(void *opaque, int ret)
diff --git a/hw/lm32/lm32.h b/hw/lm32/lm32.h
index 18aa6fdc15..e338bfeae5 100644
--- a/hw/lm32/lm32.h
+++ b/hw/lm32/lm32.h
@@ -16,14 +16,31 @@ static inline DeviceState *lm32_pic_init(qemu_irq cpu_irq)
return dev;
}
-static inline DeviceState *lm32_juart_init(void)
+static inline DeviceState *lm32_juart_init(CharDriverState *chr)
{
DeviceState *dev;
dev = qdev_create(NULL, TYPE_LM32_JUART);
+ qdev_prop_set_chr(dev, "chardev", chr);
qdev_init_nofail(dev);
return dev;
}
+static inline DeviceState *lm32_uart_create(hwaddr addr,
+ qemu_irq irq,
+ CharDriverState *chr)
+{
+ DeviceState *dev;
+ SysBusDevice *s;
+
+ dev = qdev_create(NULL, "lm32-uart");
+ s = SYS_BUS_DEVICE(dev);
+ qdev_prop_set_chr(dev, "chardev", chr);
+ qdev_init_nofail(dev);
+ sysbus_mmio_map(s, 0, addr);
+ sysbus_connect_irq(s, 0, irq);
+ return dev;
+}
+
#endif
diff --git a/hw/lm32/lm32_boards.c b/hw/lm32/lm32_boards.c
index c0290560fc..8f0c3079d6 100644
--- a/hw/lm32/lm32_boards.c
+++ b/hw/lm32/lm32_boards.c
@@ -31,6 +31,7 @@
#include "lm32_hwsetup.h"
#include "lm32.h"
#include "exec/address-spaces.h"
+#include "sysemu/sysemu.h"
typedef struct {
LM32CPU *cpu;
@@ -131,12 +132,12 @@ static void lm32_evr_init(MachineState *machine)
irq[i] = qdev_get_gpio_in(env->pic_state, i);
}
- sysbus_create_simple("lm32-uart", uart0_base, irq[uart0_irq]);
+ lm32_uart_create(uart0_base, irq[uart0_irq], serial_hds[0]);
sysbus_create_simple("lm32-timer", timer0_base, irq[timer0_irq]);
sysbus_create_simple("lm32-timer", timer1_base, irq[timer1_irq]);
/* make sure juart isn't the first chardev */
- env->juart_state = lm32_juart_init();
+ env->juart_state = lm32_juart_init(serial_hds[1]);
reset_info->bootstrap_pc = flash_base;
@@ -232,13 +233,13 @@ static void lm32_uclinux_init(MachineState *machine)
irq[i] = qdev_get_gpio_in(env->pic_state, i);
}
- sysbus_create_simple("lm32-uart", uart0_base, irq[uart0_irq]);
+ lm32_uart_create(uart0_base, irq[uart0_irq], serial_hds[0]);
sysbus_create_simple("lm32-timer", timer0_base, irq[timer0_irq]);
sysbus_create_simple("lm32-timer", timer1_base, irq[timer1_irq]);
sysbus_create_simple("lm32-timer", timer2_base, irq[timer2_irq]);
/* make sure juart isn't the first chardev */
- env->juart_state = lm32_juart_init();
+ env->juart_state = lm32_juart_init(serial_hds[1]);
reset_info->bootstrap_pc = flash_base;
diff --git a/hw/lm32/milkymist-hw.h b/hw/lm32/milkymist-hw.h
index f857d2846f..eb6a3a2559 100644
--- a/hw/lm32/milkymist-hw.h
+++ b/hw/lm32/milkymist-hw.h
@@ -5,11 +5,13 @@
#include "net/net.h"
static inline DeviceState *milkymist_uart_create(hwaddr base,
- qemu_irq irq)
+ qemu_irq irq,
+ CharDriverState *chr)
{
DeviceState *dev;
dev = qdev_create(NULL, "milkymist-uart");
+ qdev_prop_set_chr(dev, "chardev", chr);
qdev_init_nofail(dev);
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base);
sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq);
diff --git a/hw/lm32/milkymist.c b/hw/lm32/milkymist.c
index 1abdf6e2e6..5cae0f19dd 100644
--- a/hw/lm32/milkymist.c
+++ b/hw/lm32/milkymist.c
@@ -159,7 +159,7 @@ milkymist_init(MachineState *machine)
}
g_free(bios_filename);
- milkymist_uart_create(0x60000000, irq[0]);
+ milkymist_uart_create(0x60000000, irq[0], serial_hds[0]);
milkymist_sysctl_create(0x60001000, irq[1], irq[2], irq[3],
80000000, 0x10014d31, 0x0000041f, 0x00000001);
milkymist_hpdmc_create(0x60002000);
@@ -175,7 +175,7 @@ milkymist_init(MachineState *machine)
0x20000000, 0x1000, 0x20020000, 0x2000);
/* make sure juart isn't the first chardev */
- env->juart_state = lm32_juart_init();
+ env->juart_state = lm32_juart_init(serial_hds[1]);
if (kernel_filename) {
uint64_t entry;
diff --git a/hw/misc/ivshmem.c b/hw/misc/ivshmem.c
index e40f23bfc2..90be9f7617 100644
--- a/hw/misc/ivshmem.c
+++ b/hw/misc/ivshmem.c
@@ -33,7 +33,6 @@
#include "sysemu/hostmem.h"
#include "sysemu/qtest.h"
#include "qapi/visitor.h"
-#include "exec/ram_addr.h"
#include "hw/misc/ivshmem.h"
@@ -533,7 +532,7 @@ static void process_msg_shmem(IVShmemState *s, int fd, Error **errp)
}
memory_region_init_ram_ptr(&s->server_bar2, OBJECT(s),
"ivshmem.bar2", size, ptr);
- qemu_set_ram_fd(memory_region_get_ram_addr(&s->server_bar2), fd);
+ memory_region_set_fd(&s->server_bar2, fd);
s->ivshmem_bar2 = &s->server_bar2;
}
@@ -940,7 +939,7 @@ static void ivshmem_exit(PCIDevice *dev)
strerror(errno));
}
- fd = qemu_get_ram_fd(memory_region_get_ram_addr(s->ivshmem_bar2));
+ fd = memory_region_get_fd(s->ivshmem_bar2);
close(fd);
}
diff --git a/hw/net/spapr_llan.c b/hw/net/spapr_llan.c
index a8266f8ec7..8b2eebd4e3 100644
--- a/hw/net/spapr_llan.c
+++ b/hw/net/spapr_llan.c
@@ -110,6 +110,7 @@ typedef struct VIOsPAPRVLANDevice {
hwaddr buf_list;
uint32_t add_buf_ptr, use_buf_ptr, rx_bufs;
hwaddr rxq_ptr;
+ QEMUTimer *rxp_timer;
uint32_t compat_flags; /* Compatability flags for migration */
RxBufPool *rx_pool[RX_MAX_POOLS]; /* Receive buffer descriptor pools */
} VIOsPAPRVLANDevice;
@@ -122,6 +123,21 @@ static int spapr_vlan_can_receive(NetClientState *nc)
}
/**
+ * The last 8 bytes of the receive buffer list page (that has been
+ * supplied by the guest with the H_REGISTER_LOGICAL_LAN call) contain
+ * a counter for frames that have been dropped because there was no
+ * suitable receive buffer available. This function is used to increase
+ * this counter by one.
+ */
+static void spapr_vlan_record_dropped_rx_frame(VIOsPAPRVLANDevice *dev)
+{
+ uint64_t cnt;
+
+ cnt = vio_ldq(&dev->sdev, dev->buf_list + 4096 - 8);
+ vio_stq(&dev->sdev, dev->buf_list + 4096 - 8, cnt + 1);
+}
+
+/**
* Get buffer descriptor from one of our receive buffer pools
*/
static vlan_bd_t spapr_vlan_get_rx_bd_from_pool(VIOsPAPRVLANDevice *dev,
@@ -206,7 +222,8 @@ static ssize_t spapr_vlan_receive(NetClientState *nc, const uint8_t *buf,
}
if (!dev->rx_bufs) {
- return -1;
+ spapr_vlan_record_dropped_rx_frame(dev);
+ return 0;
}
if (dev->compat_flags & SPAPRVLAN_FLAG_RX_BUF_POOLS) {
@@ -215,7 +232,8 @@ static ssize_t spapr_vlan_receive(NetClientState *nc, const uint8_t *buf,
bd = spapr_vlan_get_rx_bd_from_page(dev, size);
}
if (!bd) {
- return -1;
+ spapr_vlan_record_dropped_rx_frame(dev);
+ return 0;
}
dev->rx_bufs--;
@@ -266,6 +284,13 @@ static NetClientInfo net_spapr_vlan_info = {
.receive = spapr_vlan_receive,
};
+static void spapr_vlan_flush_rx_queue(void *opaque)
+{
+ VIOsPAPRVLANDevice *dev = opaque;
+
+ qemu_flush_queued_packets(qemu_get_queue(dev->nic));
+}
+
static void spapr_vlan_reset_rx_pool(RxBufPool *rxp)
{
/*
@@ -302,6 +327,9 @@ static void spapr_vlan_realize(VIOsPAPRDevice *sdev, Error **errp)
dev->nic = qemu_new_nic(&net_spapr_vlan_info, &dev->nicconf,
object_get_typename(OBJECT(sdev)), sdev->qdev.id, dev);
qemu_format_nic_info_str(qemu_get_queue(dev->nic), dev->nicconf.macaddr.a);
+
+ dev->rxp_timer = timer_new_us(QEMU_CLOCK_VIRTUAL, spapr_vlan_flush_rx_queue,
+ dev);
}
static void spapr_vlan_instance_init(Object *obj)
@@ -332,6 +360,11 @@ static void spapr_vlan_instance_finalize(Object *obj)
dev->rx_pool[i] = NULL;
}
}
+
+ if (dev->rxp_timer) {
+ timer_del(dev->rxp_timer);
+ timer_free(dev->rxp_timer);
+ }
}
void spapr_vlan_create(VIOsPAPRBus *bus, NICInfo *nd)
@@ -629,7 +662,13 @@ static target_ulong h_add_logical_lan_buffer(PowerPCCPU *cpu,
dev->rx_bufs++;
- qemu_flush_queued_packets(qemu_get_queue(dev->nic));
+ /*
+ * Give guest some more time to add additional RX buffers before we
+ * flush the receive queue, so that e.g. fragmented IP packets can
+ * be passed to the guest in one go later (instead of passing single
+ * fragments if there is only one receive buffer available).
+ */
+ timer_mod(dev->rxp_timer, qemu_clock_get_us(QEMU_CLOCK_VIRTUAL) + 500);
return H_SUCCESS;
}
diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
index add68acfef..44e401ae99 100644
--- a/hw/ppc/spapr.c
+++ b/hw/ppc/spapr.c
@@ -1842,6 +1842,10 @@ static void ppc_spapr_init(MachineState *machine)
exit(1);
}
spapr->rtas_size = get_image_size(filename);
+ if (spapr->rtas_size < 0) {
+ error_report("Could not get size of LPAR rtas '%s'", filename);
+ exit(1);
+ }
spapr->rtas_blob = g_malloc(spapr->rtas_size);
if (load_image_size(filename, spapr->rtas_blob, spapr->rtas_size) < 0) {
error_report("Could not load LPAR rtas '%s'", filename);
@@ -2132,15 +2136,6 @@ static void spapr_add_lmbs(DeviceState *dev, uint64_t addr, uint64_t size,
int i, fdt_offset, fdt_size;
void *fdt;
- /*
- * Check for DRC connectors and send hotplug notification to the
- * guest only in case of hotplugged memory. This allows cold plugged
- * memory to be specified at boot time.
- */
- if (!dev->hotplugged) {
- return;
- }
-
for (i = 0; i < nr_lmbs; i++) {
drc = spapr_dr_connector_by_id(SPAPR_DR_CONNECTOR_TYPE_LMB,
addr/SPAPR_MEMORY_BLOCK_SIZE);
@@ -2154,7 +2149,12 @@ static void spapr_add_lmbs(DeviceState *dev, uint64_t addr, uint64_t size,
drck->attach(drc, dev, fdt, fdt_offset, !dev->hotplugged, errp);
addr += SPAPR_MEMORY_BLOCK_SIZE;
}
- spapr_hotplug_req_add_by_count(SPAPR_DR_CONNECTOR_TYPE_LMB, nr_lmbs);
+ /* send hotplug notification to the
+ * guest only in case of hotplugged memory
+ */
+ if (dev->hotplugged) {
+ spapr_hotplug_req_add_by_count(SPAPR_DR_CONNECTOR_TYPE_LMB, nr_lmbs);
+ }
}
static void spapr_memory_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c
index feb3629664..9a3f4ecc1e 100644
--- a/hw/ppc/spapr_hcall.c
+++ b/hw/ppc/spapr_hcall.c
@@ -186,6 +186,7 @@ static RemoveResult remove_hpte(PowerPCCPU *cpu, target_ulong ptex,
static target_ulong h_remove(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target_ulong opcode, target_ulong *args)
{
+ CPUPPCState *env = &cpu->env;
target_ulong flags = args[0];
target_ulong pte_index = args[1];
target_ulong avpn = args[2];
@@ -196,6 +197,7 @@ static target_ulong h_remove(PowerPCCPU *cpu, sPAPRMachineState *spapr,
switch (ret) {
case REMOVE_SUCCESS:
+ check_tlb_flush(env);
return H_SUCCESS;
case REMOVE_NOT_FOUND:
@@ -232,7 +234,9 @@ static target_ulong h_remove(PowerPCCPU *cpu, sPAPRMachineState *spapr,
static target_ulong h_bulk_remove(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target_ulong opcode, target_ulong *args)
{
+ CPUPPCState *env = &cpu->env;
int i;
+ target_ulong rc = H_SUCCESS;
for (i = 0; i < H_BULK_REMOVE_MAX_BATCH; i++) {
target_ulong *tsh = &args[i*2];
@@ -265,14 +269,18 @@ static target_ulong h_bulk_remove(PowerPCCPU *cpu, sPAPRMachineState *spapr,
break;
case REMOVE_PARM:
- return H_PARAMETER;
+ rc = H_PARAMETER;
+ goto exit;
case REMOVE_HW:
- return H_HARDWARE;
+ rc = H_HARDWARE;
+ goto exit;
}
}
+ exit:
+ check_tlb_flush(env);
- return H_SUCCESS;
+ return rc;
}
static target_ulong h_protect(PowerPCCPU *cpu, sPAPRMachineState *spapr,
diff --git a/hw/ppc/spapr_iommu.c b/hw/ppc/spapr_iommu.c
index 722db91ffa..96bb0181a7 100644
--- a/hw/ppc/spapr_iommu.c
+++ b/hw/ppc/spapr_iommu.c
@@ -76,6 +76,37 @@ static IOMMUAccessFlags spapr_tce_iommu_access_flags(uint64_t tce)
}
}
+static uint64_t *spapr_tce_alloc_table(uint32_t liobn,
+ uint32_t page_shift,
+ uint32_t nb_table,
+ int *fd,
+ bool need_vfio)
+{
+ uint64_t *table = NULL;
+ uint64_t window_size = (uint64_t)nb_table << page_shift;
+
+ if (kvm_enabled() && !(window_size >> 32)) {
+ table = kvmppc_create_spapr_tce(liobn, window_size, fd, need_vfio);
+ }
+
+ if (!table) {
+ *fd = -1;
+ table = g_malloc0(nb_table * sizeof(uint64_t));
+ }
+
+ trace_spapr_iommu_new_table(liobn, table, *fd);
+
+ return table;
+}
+
+static void spapr_tce_free_table(uint64_t *table, int fd, uint32_t nb_table)
+{
+ if (!kvm_enabled() ||
+ (kvmppc_remove_spapr_tce(table, fd, nb_table) != 0)) {
+ g_free(table);
+ }
+}
+
/* Called from RCU critical section */
static IOMMUTLBEntry spapr_tce_translate_iommu(MemoryRegion *iommu, hwaddr addr,
bool is_write)
@@ -142,21 +173,13 @@ static MemoryRegionIOMMUOps spapr_iommu_ops = {
static int spapr_tce_table_realize(DeviceState *dev)
{
sPAPRTCETable *tcet = SPAPR_TCE_TABLE(dev);
- uint64_t window_size = (uint64_t)tcet->nb_table << tcet->page_shift;
-
- if (kvm_enabled() && !(window_size >> 32)) {
- tcet->table = kvmppc_create_spapr_tce(tcet->liobn,
- window_size,
- &tcet->fd,
- tcet->need_vfio);
- }
- if (!tcet->table) {
- size_t table_size = tcet->nb_table * sizeof(uint64_t);
- tcet->table = g_malloc0(table_size);
- }
-
- trace_spapr_iommu_new_table(tcet->liobn, tcet, tcet->table, tcet->fd);
+ tcet->fd = -1;
+ tcet->table = spapr_tce_alloc_table(tcet->liobn,
+ tcet->page_shift,
+ tcet->nb_table,
+ &tcet->fd,
+ tcet->need_vfio);
memory_region_init_iommu(&tcet->iommu, OBJECT(dev), &spapr_iommu_ops,
"iommu-spapr",
@@ -242,11 +265,8 @@ static void spapr_tce_table_unrealize(DeviceState *dev, Error **errp)
QLIST_REMOVE(tcet, list);
- if (!kvm_enabled() ||
- (kvmppc_remove_spapr_tce(tcet->table, tcet->fd,
- tcet->nb_table) != 0)) {
- g_free(tcet->table);
- }
+ spapr_tce_free_table(tcet->table, tcet->fd, tcet->nb_table);
+ tcet->fd = -1;
}
MemoryRegion *spapr_tce_get_iommu(sPAPRTCETable *tcet)
@@ -278,7 +298,7 @@ static target_ulong put_tce_emu(sPAPRTCETable *tcet, target_ulong ioba,
tcet->table[index] = tce;
entry.target_as = &address_space_memory,
- entry.iova = ioba & page_mask;
+ entry.iova = (ioba - tcet->bus_offset) & page_mask;
entry.translated_addr = tce & page_mask;
entry.addr_mask = ~page_mask;
entry.perm = spapr_tce_iommu_access_flags(tce);
diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c
index e55b505c96..856aec7f51 100644
--- a/hw/ppc/spapr_pci.c
+++ b/hw/ppc/spapr_pci.c
@@ -1093,13 +1093,11 @@ static void spapr_phb_add_pci_device(sPAPRDRConnector *drc,
spapr_tce_set_need_vfio(tcet, true);
}
- if (dev->hotplugged) {
- fdt = create_device_tree(&fdt_size);
- fdt_start_offset = spapr_create_pci_child_dt(phb, pdev, fdt, 0);
- if (!fdt_start_offset) {
- error_setg(errp, "Failed to create pci child device tree node");
- goto out;
- }
+ fdt = create_device_tree(&fdt_size);
+ fdt_start_offset = spapr_create_pci_child_dt(phb, pdev, fdt, 0);
+ if (!fdt_start_offset) {
+ error_setg(errp, "Failed to create pci child device tree node");
+ goto out;
}
drck->attach(drc, DEVICE(pdev),
@@ -1816,7 +1814,7 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb,
_FDT(fdt_setprop(fdt, bus_off, "interrupt-map", &interrupt_map,
sizeof(interrupt_map)));
- tcet = spapr_tce_find_by_liobn(SPAPR_PCI_LIOBN(phb->index, 0));
+ tcet = spapr_tce_find_by_liobn(phb->dma_liobn);
if (!tcet) {
return -1;
}
diff --git a/hw/s390x/s390-skeys.c b/hw/s390x/s390-skeys.c
index d772cfc7ea..e2d4e1af79 100644
--- a/hw/s390x/s390-skeys.c
+++ b/hw/s390x/s390-skeys.c
@@ -47,15 +47,11 @@ void s390_skeys_init(void)
qdev_init_nofail(DEVICE(obj));
}
-static void write_keys(QEMUFile *f, uint8_t *keys, uint64_t startgfn,
+static void write_keys(FILE *f, uint8_t *keys, uint64_t startgfn,
uint64_t count, Error **errp)
{
uint64_t curpage = startgfn;
uint64_t maxpage = curpage + count - 1;
- const char *fmt = "page=%03" PRIx64 ": key(%d) => ACC=%X, FP=%d, REF=%d,"
- " ch=%d, reserved=%d\n";
- char buf[128];
- int len;
for (; curpage <= maxpage; curpage++) {
uint8_t acc = (*keys & 0xF0) >> 4;
@@ -64,10 +60,9 @@ static void write_keys(QEMUFile *f, uint8_t *keys, uint64_t startgfn,
int ch = (*keys & 0x02);
int res = (*keys & 0x01);
- len = snprintf(buf, sizeof(buf), fmt, curpage,
- *keys, acc, fp, ref, ch, res);
- assert(len < sizeof(buf));
- qemu_put_buffer(f, (uint8_t *)buf, len);
+ fprintf(f, "page=%03" PRIx64 ": key(%d) => ACC=%X, FP=%d, REF=%d,"
+ " ch=%d, reserved=%d\n",
+ curpage, *keys, acc, fp, ref, ch, res);
keys++;
}
}
@@ -116,7 +111,8 @@ void qmp_dump_skeys(const char *filename, Error **errp)
vaddr cur_gfn = 0;
uint8_t *buf;
int ret;
- QEMUFile *f;
+ int fd;
+ FILE *f;
/* Quick check to see if guest is using storage keys*/
if (!skeyclass->skeys_enabled(ss)) {
@@ -125,8 +121,14 @@ void qmp_dump_skeys(const char *filename, Error **errp)
return;
}
- f = qemu_fopen(filename, "wb");
+ fd = qemu_open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
+ if (fd < 0) {
+ error_setg_file_open(errp, errno, filename);
+ return;
+ }
+ f = fdopen(fd, "wb");
if (!f) {
+ close(fd);
error_setg_file_open(errp, errno, filename);
return;
}
@@ -162,7 +164,7 @@ out_free:
error_propagate(errp, lerr);
g_free(buf);
out:
- qemu_fclose(f);
+ fclose(f);
}
static void qemu_s390_skeys_init(Object *obj)
diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c
index a63a581550..cc66d36186 100644
--- a/hw/scsi/megasas.c
+++ b/hw/scsi/megasas.c
@@ -650,7 +650,9 @@ static int megasas_init_firmware(MegasasState *s, MegasasCmd *cmd)
pa_hi = le32_to_cpu(initq->pi_addr_hi);
s->producer_pa = ((uint64_t) pa_hi << 32) | pa_lo;
s->reply_queue_head = ldl_le_pci_dma(pcid, s->producer_pa);
+ s->reply_queue_head %= MEGASAS_MAX_FRAMES;
s->reply_queue_tail = ldl_le_pci_dma(pcid, s->consumer_pa);
+ s->reply_queue_tail %= MEGASAS_MAX_FRAMES;
flags = le32_to_cpu(initq->flags);
if (flags & MFI_QUEUE_FLAG_CONTEXT64) {
s->flags |= MEGASAS_MASK_USE_QUEUE64;
@@ -1293,7 +1295,7 @@ static int megasas_dcmd_ld_get_info(MegasasState *s, MegasasCmd *cmd)
static int megasas_dcmd_cfg_read(MegasasState *s, MegasasCmd *cmd)
{
- uint8_t data[4096];
+ uint8_t data[4096] = { 0 };
struct mfi_config_data *info;
int num_pd_disks = 0, array_offset, ld_offset;
BusChild *kid;
@@ -1446,7 +1448,7 @@ static int megasas_dcmd_set_properties(MegasasState *s, MegasasCmd *cmd)
dcmd_size);
return MFI_STAT_INVALID_PARAMETER;
}
- dma_buf_write((uint8_t *)&info, cmd->iov_size, &cmd->qsg);
+ dma_buf_write((uint8_t *)&info, dcmd_size, &cmd->qsg);
trace_megasas_dcmd_unsupported(cmd->index, cmd->iov_size);
return MFI_STAT_OK;
}
diff --git a/hw/scsi/mptsas.c b/hw/scsi/mptsas.c
index 499c1465ae..be88e161a9 100644
--- a/hw/scsi/mptsas.c
+++ b/hw/scsi/mptsas.c
@@ -754,11 +754,6 @@ static void mptsas_fetch_request(MPTSASState *s)
hwaddr addr;
int size;
- if (s->state != MPI_IOC_STATE_OPERATIONAL) {
- mptsas_set_fault(s, MPI_IOCSTATUS_INVALID_STATE);
- return;
- }
-
/* Read the message header from the guest first. */
addr = s->host_mfa_high_addr | MPTSAS_FIFO_GET(s, request_post);
pci_dma_read(pci, addr, req, sizeof(hdr));
@@ -789,6 +784,10 @@ static void mptsas_fetch_requests(void *opaque)
{
MPTSASState *s = opaque;
+ if (s->state != MPI_IOC_STATE_OPERATIONAL) {
+ mptsas_set_fault(s, MPI_IOCSTATUS_INVALID_STATE);
+ return;
+ }
while (!MPTSAS_FIFO_EMPTY(s, request_post)) {
mptsas_fetch_request(s);
}
diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c
index ce89c98b4e..ace65e0720 100644
--- a/hw/scsi/scsi-disk.c
+++ b/hw/scsi/scsi-disk.c
@@ -53,7 +53,21 @@ do { printf("scsi-disk: " fmt , ## __VA_ARGS__); } while (0)
#define DEFAULT_MAX_UNMAP_SIZE (1 << 30) /* 1 GB */
#define DEFAULT_MAX_IO_SIZE INT_MAX /* 2 GB - 1 block */
-typedef struct SCSIDiskState SCSIDiskState;
+#define TYPE_SCSI_DISK_BASE "scsi-disk-base"
+
+#define SCSI_DISK_BASE(obj) \
+ OBJECT_CHECK(SCSIDiskState, (obj), TYPE_SCSI_DISK_BASE)
+#define SCSI_DISK_BASE_CLASS(klass) \
+ OBJECT_CLASS_CHECK(SCSIDiskClass, (klass), TYPE_SCSI_DISK_BASE)
+#define SCSI_DISK_BASE_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(SCSIDiskClass, (obj), TYPE_SCSI_DISK_BASE)
+
+typedef struct SCSIDiskClass {
+ SCSIDeviceClass parent_class;
+ DMAIOFunc *dma_readv;
+ DMAIOFunc *dma_writev;
+ bool (*need_fua_emulation)(SCSICommand *cmd);
+} SCSIDiskClass;
typedef struct SCSIDiskReq {
SCSIRequest req;
@@ -62,16 +76,18 @@ typedef struct SCSIDiskReq {
uint32_t sector_count;
uint32_t buflen;
bool started;
+ bool need_fua_emulation;
struct iovec iov;
QEMUIOVector qiov;
BlockAcctCookie acct;
+ unsigned char *status;
} SCSIDiskReq;
#define SCSI_DISK_F_REMOVABLE 0
#define SCSI_DISK_F_DPOFUA 1
#define SCSI_DISK_F_NO_REMOVABLE_DEVOPS 2
-struct SCSIDiskState
+typedef struct SCSIDiskState
{
SCSIDevice qdev;
uint32_t features;
@@ -88,7 +104,7 @@ struct SCSIDiskState
char *product;
bool tray_open;
bool tray_locked;
-};
+} SCSIDiskState;
static int scsi_handle_rw_error(SCSIDiskReq *r, int error, bool acct_failed);
@@ -161,6 +177,29 @@ static void scsi_disk_load_request(QEMUFile *f, SCSIRequest *req)
qemu_iovec_init_external(&r->qiov, &r->iov, 1);
}
+static bool scsi_disk_req_check_error(SCSIDiskReq *r, int ret, bool acct_failed)
+{
+ if (r->req.io_canceled) {
+ scsi_req_cancel_complete(&r->req);
+ return true;
+ }
+
+ if (ret < 0) {
+ return scsi_handle_rw_error(r, -ret, acct_failed);
+ }
+
+ if (r->status && *r->status) {
+ if (acct_failed) {
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
+ block_acct_failed(blk_get_stats(s->qdev.conf.blk), &r->acct);
+ }
+ scsi_req_complete(&r->req, *r->status);
+ return true;
+ }
+
+ return false;
+}
+
static void scsi_aio_complete(void *opaque, int ret)
{
SCSIDiskReq *r = (SCSIDiskReq *)opaque;
@@ -168,17 +207,10 @@ static void scsi_aio_complete(void *opaque, int ret)
assert(r->req.aiocb != NULL);
r->req.aiocb = NULL;
- if (r->req.io_canceled) {
- scsi_req_cancel_complete(&r->req);
+ if (scsi_disk_req_check_error(r, ret, true)) {
goto done;
}
- if (ret < 0) {
- if (scsi_handle_rw_error(r, -ret, true)) {
- goto done;
- }
- }
-
block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
scsi_req_complete(&r->req, GOOD);
@@ -217,13 +249,9 @@ static void scsi_write_do_fua(SCSIDiskReq *r)
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
assert(r->req.aiocb == NULL);
+ assert(!r->req.io_canceled);
- if (r->req.io_canceled) {
- scsi_req_cancel_complete(&r->req);
- goto done;
- }
-
- if (scsi_is_cmd_fua(&r->req.cmd)) {
+ if (r->need_fua_emulation) {
block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct, 0,
BLOCK_ACCT_FLUSH);
r->req.aiocb = blk_aio_flush(s->qdev.conf.blk, scsi_aio_complete, r);
@@ -231,26 +259,16 @@ static void scsi_write_do_fua(SCSIDiskReq *r)
}
scsi_req_complete(&r->req, GOOD);
-
-done:
scsi_req_unref(&r->req);
}
static void scsi_dma_complete_noio(SCSIDiskReq *r, int ret)
{
assert(r->req.aiocb == NULL);
-
- if (r->req.io_canceled) {
- scsi_req_cancel_complete(&r->req);
+ if (scsi_disk_req_check_error(r, ret, false)) {
goto done;
}
- if (ret < 0) {
- if (scsi_handle_rw_error(r, -ret, false)) {
- goto done;
- }
- }
-
r->sector += r->sector_count;
r->sector_count = 0;
if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
@@ -288,17 +306,10 @@ static void scsi_read_complete(void * opaque, int ret)
assert(r->req.aiocb != NULL);
r->req.aiocb = NULL;
- if (r->req.io_canceled) {
- scsi_req_cancel_complete(&r->req);
+ if (scsi_disk_req_check_error(r, ret, true)) {
goto done;
}
- if (ret < 0) {
- if (scsi_handle_rw_error(r, -ret, true)) {
- goto done;
- }
- }
-
block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
DPRINTF("Data ready tag=0x%x len=%zd\n", r->req.tag, r->qiov.size);
@@ -315,35 +326,29 @@ done:
static void scsi_do_read(SCSIDiskReq *r, int ret)
{
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
+ SCSIDiskClass *sdc = (SCSIDiskClass *) object_get_class(OBJECT(s));
assert (r->req.aiocb == NULL);
-
- if (r->req.io_canceled) {
- scsi_req_cancel_complete(&r->req);
+ if (scsi_disk_req_check_error(r, ret, false)) {
goto done;
}
- if (ret < 0) {
- if (scsi_handle_rw_error(r, -ret, false)) {
- goto done;
- }
- }
-
/* The request is used as the AIO opaque value, so add a ref. */
scsi_req_ref(&r->req);
if (r->req.sg) {
dma_acct_start(s->qdev.conf.blk, &r->acct, r->req.sg, BLOCK_ACCT_READ);
r->req.resid -= r->req.sg->size;
- r->req.aiocb = dma_blk_read(s->qdev.conf.blk, r->req.sg, r->sector,
- scsi_dma_complete, r);
+ r->req.aiocb = dma_blk_io(blk_get_aio_context(s->qdev.conf.blk),
+ r->req.sg, r->sector << BDRV_SECTOR_BITS,
+ sdc->dma_readv, r, scsi_dma_complete, r,
+ DMA_DIRECTION_FROM_DEVICE);
} else {
scsi_init_iovec(r, SCSI_DMA_BUF_SIZE);
block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct,
r->qiov.size, BLOCK_ACCT_READ);
- r->req.aiocb = blk_aio_preadv(s->qdev.conf.blk,
- r->sector << BDRV_SECTOR_BITS, &r->qiov,
- 0, scsi_read_complete, r);
+ r->req.aiocb = sdc->dma_readv(r->sector, &r->qiov,
+ scsi_read_complete, r, r);
}
done:
@@ -398,7 +403,7 @@ static void scsi_read_data(SCSIRequest *req)
first = !r->started;
r->started = true;
- if (first && scsi_is_cmd_fua(&r->req.cmd)) {
+ if (first && r->need_fua_emulation) {
block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct, 0,
BLOCK_ACCT_FLUSH);
r->req.aiocb = blk_aio_flush(s->qdev.conf.blk, scsi_do_read_cb, r);
@@ -455,18 +460,10 @@ static void scsi_write_complete_noio(SCSIDiskReq *r, int ret)
uint32_t n;
assert (r->req.aiocb == NULL);
-
- if (r->req.io_canceled) {
- scsi_req_cancel_complete(&r->req);
+ if (scsi_disk_req_check_error(r, ret, false)) {
goto done;
}
- if (ret < 0) {
- if (scsi_handle_rw_error(r, -ret, false)) {
- goto done;
- }
- }
-
n = r->qiov.size / 512;
r->sector += n;
r->sector_count -= n;
@@ -503,6 +500,7 @@ static void scsi_write_data(SCSIRequest *req)
{
SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
+ SCSIDiskClass *sdc = (SCSIDiskClass *) object_get_class(OBJECT(s));
/* No data transfer may already be in progress */
assert(r->req.aiocb == NULL);
@@ -539,14 +537,15 @@ static void scsi_write_data(SCSIRequest *req)
if (r->req.sg) {
dma_acct_start(s->qdev.conf.blk, &r->acct, r->req.sg, BLOCK_ACCT_WRITE);
r->req.resid -= r->req.sg->size;
- r->req.aiocb = dma_blk_write(s->qdev.conf.blk, r->req.sg, r->sector,
- scsi_dma_complete, r);
+ r->req.aiocb = dma_blk_io(blk_get_aio_context(s->qdev.conf.blk),
+ r->req.sg, r->sector << BDRV_SECTOR_BITS,
+ sdc->dma_writev, r, scsi_dma_complete, r,
+ DMA_DIRECTION_TO_DEVICE);
} else {
block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct,
r->qiov.size, BLOCK_ACCT_WRITE);
- r->req.aiocb = blk_aio_pwritev(s->qdev.conf.blk,
- r->sector << BDRV_SECTOR_BITS, &r->qiov,
- 0, scsi_write_complete, r);
+ r->req.aiocb = sdc->dma_writev(r->sector << BDRV_SECTOR_BITS, &r->qiov,
+ scsi_write_complete, r, r);
}
}
@@ -1598,18 +1597,10 @@ static void scsi_unmap_complete_noio(UnmapCBData *data, int ret)
uint32_t nb_sectors;
assert(r->req.aiocb == NULL);
-
- if (r->req.io_canceled) {
- scsi_req_cancel_complete(&r->req);
+ if (scsi_disk_req_check_error(r, ret, false)) {
goto done;
}
- if (ret < 0) {
- if (scsi_handle_rw_error(r, -ret, false)) {
- goto done;
- }
- }
-
if (data->count > 0) {
sector_num = ldq_be_p(&data->inbuf[0]);
nb_sectors = ldl_be_p(&data->inbuf[8]) & 0xffffffffULL;
@@ -1709,17 +1700,10 @@ static void scsi_write_same_complete(void *opaque, int ret)
assert(r->req.aiocb != NULL);
r->req.aiocb = NULL;
- if (r->req.io_canceled) {
- scsi_req_cancel_complete(&r->req);
+ if (scsi_disk_req_check_error(r, ret, true)) {
goto done;
}
- if (ret < 0) {
- if (scsi_handle_rw_error(r, -ret, true)) {
- goto done;
- }
- }
-
block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
data->nb_sectors -= data->iov.iov_len / 512;
@@ -1778,7 +1762,7 @@ static void scsi_disk_emulate_write_same(SCSIDiskReq *r, uint8_t *inbuf)
block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct,
nb_sectors * s->qdev.blocksize,
BLOCK_ACCT_WRITE);
- r->req.aiocb = blk_aio_write_zeroes(s->qdev.conf.blk,
+ r->req.aiocb = blk_aio_pwrite_zeroes(s->qdev.conf.blk,
r->req.cmd.lba * s->qdev.blocksize,
nb_sectors * s->qdev.blocksize,
flags, scsi_aio_complete, r);
@@ -2136,6 +2120,7 @@ static int32_t scsi_disk_dma_command(SCSIRequest *req, uint8_t *buf)
{
SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
+ SCSIDiskClass *sdc = (SCSIDiskClass *) object_get_class(OBJECT(s));
uint32_t len;
uint8_t command;
@@ -2194,6 +2179,7 @@ static int32_t scsi_disk_dma_command(SCSIRequest *req, uint8_t *buf)
scsi_check_condition(r, SENSE_CODE(LBA_OUT_OF_RANGE));
return 0;
}
+ r->need_fua_emulation = sdc->need_fua_emulation(&r->req.cmd);
if (r->sector_count == 0) {
scsi_req_complete(&r->req, GOOD);
}
@@ -2576,16 +2562,145 @@ static void scsi_block_realize(SCSIDevice *dev, Error **errp)
scsi_generic_read_device_identification(&s->qdev);
}
+typedef struct SCSIBlockReq {
+ SCSIDiskReq req;
+ sg_io_hdr_t io_header;
+
+ /* Selected bytes of the original CDB, copied into our own CDB. */
+ uint8_t cmd, cdb1, group_number;
+
+ /* CDB passed to SG_IO. */
+ uint8_t cdb[16];
+} SCSIBlockReq;
+
+static BlockAIOCB *scsi_block_do_sgio(SCSIBlockReq *req,
+ int64_t offset, QEMUIOVector *iov,
+ int direction,
+ BlockCompletionFunc *cb, void *opaque)
+{
+ sg_io_hdr_t *io_header = &req->io_header;
+ SCSIDiskReq *r = &req->req;
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
+ int nb_logical_blocks;
+ uint64_t lba;
+ BlockAIOCB *aiocb;
+
+ /* This is not supported yet. It can only happen if the guest does
+ * reads and writes that are not aligned to one logical sectors
+ * _and_ cover multiple MemoryRegions.
+ */
+ assert(offset % s->qdev.blocksize == 0);
+ assert(iov->size % s->qdev.blocksize == 0);
+
+ io_header->interface_id = 'S';
+
+ /* The data transfer comes from the QEMUIOVector. */
+ io_header->dxfer_direction = direction;
+ io_header->dxfer_len = iov->size;
+ io_header->dxferp = (void *)iov->iov;
+ io_header->iovec_count = iov->niov;
+ assert(io_header->iovec_count == iov->niov); /* no overflow! */
+
+ /* Build a new CDB with the LBA and length patched in, in case
+ * DMA helpers split the transfer in multiple segments. Do not
+ * build a CDB smaller than what the guest wanted, and only build
+ * a larger one if strictly necessary.
+ */
+ io_header->cmdp = req->cdb;
+ lba = offset / s->qdev.blocksize;
+ nb_logical_blocks = io_header->dxfer_len / s->qdev.blocksize;
+
+ if ((req->cmd >> 5) == 0 && lba <= 0x1ffff) {
+ /* 6-byte CDB */
+ stl_be_p(&req->cdb[0], lba | (req->cmd << 24));
+ req->cdb[4] = nb_logical_blocks;
+ req->cdb[5] = 0;
+ io_header->cmd_len = 6;
+ } else if ((req->cmd >> 5) <= 1 && lba <= 0xffffffffULL) {
+ /* 10-byte CDB */
+ req->cdb[0] = (req->cmd & 0x1f) | 0x20;
+ req->cdb[1] = req->cdb1;
+ stl_be_p(&req->cdb[2], lba);
+ req->cdb[6] = req->group_number;
+ stw_be_p(&req->cdb[7], nb_logical_blocks);
+ req->cdb[9] = 0;
+ io_header->cmd_len = 10;
+ } else if ((req->cmd >> 5) != 4 && lba <= 0xffffffffULL) {
+ /* 12-byte CDB */
+ req->cdb[0] = (req->cmd & 0x1f) | 0xA0;
+ req->cdb[1] = req->cdb1;
+ stl_be_p(&req->cdb[2], lba);
+ stl_be_p(&req->cdb[6], nb_logical_blocks);
+ req->cdb[10] = req->group_number;
+ req->cdb[11] = 0;
+ io_header->cmd_len = 12;
+ } else {
+ /* 16-byte CDB */
+ req->cdb[0] = (req->cmd & 0x1f) | 0x80;
+ req->cdb[1] = req->cdb1;
+ stq_be_p(&req->cdb[2], lba);
+ stl_be_p(&req->cdb[10], nb_logical_blocks);
+ req->cdb[14] = req->group_number;
+ req->cdb[15] = 0;
+ io_header->cmd_len = 16;
+ }
+
+ /* The rest is as in scsi-generic.c. */
+ io_header->mx_sb_len = sizeof(r->req.sense);
+ io_header->sbp = r->req.sense;
+ io_header->timeout = UINT_MAX;
+ io_header->usr_ptr = r;
+ io_header->flags |= SG_FLAG_DIRECT_IO;
+
+ aiocb = blk_aio_ioctl(s->qdev.conf.blk, SG_IO, io_header, cb, opaque);
+ assert(aiocb != NULL);
+ return aiocb;
+}
+
+static bool scsi_block_no_fua(SCSICommand *cmd)
+{
+ return false;
+}
+
+static BlockAIOCB *scsi_block_dma_readv(int64_t offset,
+ QEMUIOVector *iov,
+ BlockCompletionFunc *cb, void *cb_opaque,
+ void *opaque)
+{
+ SCSIBlockReq *r = opaque;
+ return scsi_block_do_sgio(r, offset, iov,
+ SG_DXFER_FROM_DEV, cb, cb_opaque);
+}
+
+static BlockAIOCB *scsi_block_dma_writev(int64_t offset,
+ QEMUIOVector *iov,
+ BlockCompletionFunc *cb, void *cb_opaque,
+ void *opaque)
+{
+ SCSIBlockReq *r = opaque;
+ return scsi_block_do_sgio(r, offset, iov,
+ SG_DXFER_TO_DEV, cb, cb_opaque);
+}
+
static bool scsi_block_is_passthrough(SCSIDiskState *s, uint8_t *buf)
{
switch (buf[0]) {
+ case VERIFY_10:
+ case VERIFY_12:
+ case VERIFY_16:
+ /* Check if BYTCHK == 0x01 (data-out buffer contains data
+ * for the number of logical blocks specified in the length
+ * field). For other modes, do not use scatter/gather operation.
+ */
+ if ((buf[1] & 6) != 2) {
+ return false;
+ }
+ break;
+
case READ_6:
case READ_10:
case READ_12:
case READ_16:
- case VERIFY_10:
- case VERIFY_12:
- case VERIFY_16:
case WRITE_6:
case WRITE_10:
case WRITE_12:
@@ -2593,21 +2708,8 @@ static bool scsi_block_is_passthrough(SCSIDiskState *s, uint8_t *buf)
case WRITE_VERIFY_10:
case WRITE_VERIFY_12:
case WRITE_VERIFY_16:
- /* If we are not using O_DIRECT, we might read stale data from the
- * host cache if writes were made using other commands than these
- * ones (such as WRITE SAME or EXTENDED COPY, etc.). So, without
- * O_DIRECT everything must go through SG_IO.
- */
- if (!(blk_get_flags(s->qdev.conf.blk) & BDRV_O_NOCACHE)) {
- break;
- }
-
- /* MMC writing cannot be done via pread/pwrite, because it sometimes
+ /* MMC writing cannot be done via DMA helpers, because it sometimes
* involves writing beyond the maximum LBA or to negative LBA (lead-in).
- * And once you do these writes, reading from the block device is
- * unreliable, too. It is even possible that reads deliver random data
- * from the host page cache (this is probably a Linux bug).
- *
* We might use scsi_disk_dma_reqops as long as no writing commands are
* seen, but performance usually isn't paramount on optical media. So,
* just make scsi-block operate the same as scsi-generic for them.
@@ -2625,6 +2727,54 @@ static bool scsi_block_is_passthrough(SCSIDiskState *s, uint8_t *buf)
}
+static int32_t scsi_block_dma_command(SCSIRequest *req, uint8_t *buf)
+{
+ SCSIBlockReq *r = (SCSIBlockReq *)req;
+ r->cmd = req->cmd.buf[0];
+ switch (r->cmd >> 5) {
+ case 0:
+ /* 6-byte CDB. */
+ r->cdb1 = r->group_number = 0;
+ break;
+ case 1:
+ /* 10-byte CDB. */
+ r->cdb1 = req->cmd.buf[1];
+ r->group_number = req->cmd.buf[6];
+ case 4:
+ /* 12-byte CDB. */
+ r->cdb1 = req->cmd.buf[1];
+ r->group_number = req->cmd.buf[10];
+ break;
+ case 5:
+ /* 16-byte CDB. */
+ r->cdb1 = req->cmd.buf[1];
+ r->group_number = req->cmd.buf[14];
+ break;
+ default:
+ abort();
+ }
+
+ if (r->cdb1 & 0xe0) {
+ /* Protection information is not supported. */
+ scsi_check_condition(&r->req, SENSE_CODE(INVALID_FIELD));
+ return 0;
+ }
+
+ r->req.status = &r->io_header.status;
+ return scsi_disk_dma_command(req, buf);
+}
+
+static const SCSIReqOps scsi_block_dma_reqops = {
+ .size = sizeof(SCSIBlockReq),
+ .free_req = scsi_free_request,
+ .send_command = scsi_block_dma_command,
+ .read_data = scsi_read_data,
+ .write_data = scsi_write_data,
+ .get_buf = scsi_get_buf,
+ .load_request = scsi_disk_load_request,
+ .save_request = scsi_disk_save_request,
+};
+
static SCSIRequest *scsi_block_new_request(SCSIDevice *d, uint32_t tag,
uint32_t lun, uint8_t *buf,
void *hba_private)
@@ -2635,7 +2785,7 @@ static SCSIRequest *scsi_block_new_request(SCSIDevice *d, uint32_t tag,
return scsi_req_alloc(&scsi_generic_req_ops, &s->qdev, tag, lun,
hba_private);
} else {
- return scsi_req_alloc(&scsi_disk_dma_reqops, &s->qdev, tag, lun,
+ return scsi_req_alloc(&scsi_block_dma_reqops, &s->qdev, tag, lun,
hba_private);
}
}
@@ -2654,6 +2804,46 @@ static int scsi_block_parse_cdb(SCSIDevice *d, SCSICommand *cmd,
#endif
+static
+BlockAIOCB *scsi_dma_readv(int64_t offset, QEMUIOVector *iov,
+ BlockCompletionFunc *cb, void *cb_opaque,
+ void *opaque)
+{
+ SCSIDiskReq *r = opaque;
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
+ return blk_aio_preadv(s->qdev.conf.blk, offset, iov, 0, cb, cb_opaque);
+}
+
+static
+BlockAIOCB *scsi_dma_writev(int64_t offset, QEMUIOVector *iov,
+ BlockCompletionFunc *cb, void *cb_opaque,
+ void *opaque)
+{
+ SCSIDiskReq *r = opaque;
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
+ return blk_aio_pwritev(s->qdev.conf.blk, offset, iov, 0, cb, cb_opaque);
+}
+
+static void scsi_disk_base_class_initfn(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SCSIDiskClass *sdc = SCSI_DISK_BASE_CLASS(klass);
+
+ dc->fw_name = "disk";
+ dc->reset = scsi_disk_reset;
+ sdc->dma_readv = scsi_dma_readv;
+ sdc->dma_writev = scsi_dma_writev;
+ sdc->need_fua_emulation = scsi_is_cmd_fua;
+}
+
+static const TypeInfo scsi_disk_base_info = {
+ .name = TYPE_SCSI_DISK_BASE,
+ .parent = TYPE_SCSI_DEVICE,
+ .class_init = scsi_disk_base_class_initfn,
+ .instance_size = sizeof(SCSIDiskState),
+ .class_size = sizeof(SCSIDiskClass),
+};
+
#define DEFINE_SCSI_DISK_PROPERTIES() \
DEFINE_BLOCK_PROPERTIES(SCSIDiskState, qdev.conf), \
DEFINE_PROP_STRING("ver", SCSIDiskState, version), \
@@ -2701,17 +2891,14 @@ static void scsi_hd_class_initfn(ObjectClass *klass, void *data)
sc->realize = scsi_hd_realize;
sc->alloc_req = scsi_new_request;
sc->unit_attention_reported = scsi_disk_unit_attention_reported;
- dc->fw_name = "disk";
dc->desc = "virtual SCSI disk";
- dc->reset = scsi_disk_reset;
dc->props = scsi_hd_properties;
dc->vmsd = &vmstate_scsi_disk_state;
}
static const TypeInfo scsi_hd_info = {
.name = "scsi-hd",
- .parent = TYPE_SCSI_DEVICE,
- .instance_size = sizeof(SCSIDiskState),
+ .parent = TYPE_SCSI_DISK_BASE,
.class_init = scsi_hd_class_initfn,
};
@@ -2733,17 +2920,14 @@ static void scsi_cd_class_initfn(ObjectClass *klass, void *data)
sc->realize = scsi_cd_realize;
sc->alloc_req = scsi_new_request;
sc->unit_attention_reported = scsi_disk_unit_attention_reported;
- dc->fw_name = "disk";
dc->desc = "virtual SCSI CD-ROM";
- dc->reset = scsi_disk_reset;
dc->props = scsi_cd_properties;
dc->vmsd = &vmstate_scsi_disk_state;
}
static const TypeInfo scsi_cd_info = {
.name = "scsi-cd",
- .parent = TYPE_SCSI_DEVICE,
- .instance_size = sizeof(SCSIDiskState),
+ .parent = TYPE_SCSI_DISK_BASE,
.class_init = scsi_cd_class_initfn,
};
@@ -2757,21 +2941,22 @@ static void scsi_block_class_initfn(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass);
+ SCSIDiskClass *sdc = SCSI_DISK_BASE_CLASS(klass);
sc->realize = scsi_block_realize;
sc->alloc_req = scsi_block_new_request;
sc->parse_cdb = scsi_block_parse_cdb;
- dc->fw_name = "disk";
+ sdc->dma_readv = scsi_block_dma_readv;
+ sdc->dma_writev = scsi_block_dma_writev;
+ sdc->need_fua_emulation = scsi_block_no_fua;
dc->desc = "SCSI block device passthrough";
- dc->reset = scsi_disk_reset;
dc->props = scsi_block_properties;
dc->vmsd = &vmstate_scsi_disk_state;
}
static const TypeInfo scsi_block_info = {
.name = "scsi-block",
- .parent = TYPE_SCSI_DEVICE,
- .instance_size = sizeof(SCSIDiskState),
+ .parent = TYPE_SCSI_DISK_BASE,
.class_init = scsi_block_class_initfn,
};
#endif
@@ -2809,13 +2994,13 @@ static void scsi_disk_class_initfn(ObjectClass *klass, void *data)
static const TypeInfo scsi_disk_info = {
.name = "scsi-disk",
- .parent = TYPE_SCSI_DEVICE,
- .instance_size = sizeof(SCSIDiskState),
+ .parent = TYPE_SCSI_DISK_BASE,
.class_init = scsi_disk_class_initfn,
};
static void scsi_disk_register_types(void)
{
+ type_register_static(&scsi_disk_base_info);
type_register_static(&scsi_hd_info);
type_register_static(&scsi_cd_info);
#ifdef __linux__
diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c
index 7459465f60..71372a8383 100644
--- a/hw/scsi/scsi-generic.c
+++ b/hw/scsi/scsi-generic.c
@@ -222,6 +222,18 @@ static void scsi_read_complete(void * opaque, int ret)
r->buf[3] |= 0x80;
}
}
+ if (s->type == TYPE_DISK &&
+ r->req.cmd.buf[0] == INQUIRY &&
+ r->req.cmd.buf[2] == 0xb0) {
+ uint32_t max_xfer_len = blk_get_max_transfer_length(s->conf.blk);
+ if (max_xfer_len) {
+ stl_be_p(&r->buf[8], max_xfer_len);
+ /* Also take care of the opt xfer len. */
+ if (ldl_be_p(&r->buf[12]) > max_xfer_len) {
+ stl_be_p(&r->buf[12], max_xfer_len);
+ }
+ }
+ }
scsi_req_data(&r->req, len);
scsi_req_unref(&r->req);
}
diff --git a/hw/scsi/vmw_pvscsi.c b/hw/scsi/vmw_pvscsi.c
index f67b5bf7d3..2d7528d1dd 100644
--- a/hw/scsi/vmw_pvscsi.c
+++ b/hw/scsi/vmw_pvscsi.c
@@ -153,7 +153,7 @@ pvscsi_log2(uint32_t input)
return log;
}
-static void
+static int
pvscsi_ring_init_data(PVSCSIRingInfo *m, PVSCSICmdDescSetupRings *ri)
{
int i;
@@ -161,6 +161,10 @@ pvscsi_ring_init_data(PVSCSIRingInfo *m, PVSCSICmdDescSetupRings *ri)
uint32_t req_ring_size, cmp_ring_size;
m->rs_pa = ri->ringsStatePPN << VMW_PAGE_SHIFT;
+ if ((ri->reqRingNumPages > PVSCSI_SETUP_RINGS_MAX_NUM_PAGES)
+ || (ri->cmpRingNumPages > PVSCSI_SETUP_RINGS_MAX_NUM_PAGES)) {
+ return -1;
+ }
req_ring_size = ri->reqRingNumPages * PVSCSI_MAX_NUM_REQ_ENTRIES_PER_PAGE;
cmp_ring_size = ri->cmpRingNumPages * PVSCSI_MAX_NUM_CMP_ENTRIES_PER_PAGE;
txr_len_log2 = pvscsi_log2(req_ring_size - 1);
@@ -192,15 +196,20 @@ pvscsi_ring_init_data(PVSCSIRingInfo *m, PVSCSICmdDescSetupRings *ri)
/* Flush ring state page changes */
smp_wmb();
+
+ return 0;
}
-static void
+static int
pvscsi_ring_init_msg(PVSCSIRingInfo *m, PVSCSICmdDescSetupMsgRing *ri)
{
int i;
uint32_t len_log2;
uint32_t ring_size;
+ if (ri->numPages > PVSCSI_SETUP_MSG_RING_MAX_NUM_PAGES) {
+ return -1;
+ }
ring_size = ri->numPages * PVSCSI_MAX_NUM_MSG_ENTRIES_PER_PAGE;
len_log2 = pvscsi_log2(ring_size - 1);
@@ -220,6 +229,8 @@ pvscsi_ring_init_msg(PVSCSIRingInfo *m, PVSCSICmdDescSetupMsgRing *ri)
/* Flush ring state page changes */
smp_wmb();
+
+ return 0;
}
static void
@@ -770,7 +781,10 @@ pvscsi_on_cmd_setup_rings(PVSCSIState *s)
trace_pvscsi_on_cmd_arrived("PVSCSI_CMD_SETUP_RINGS");
pvscsi_dbg_dump_tx_rings_config(rc);
- pvscsi_ring_init_data(&s->rings, rc);
+ if (pvscsi_ring_init_data(&s->rings, rc) < 0) {
+ return PVSCSI_COMMAND_PROCESSING_FAILED;
+ }
+
s->rings_info_valid = TRUE;
return PVSCSI_COMMAND_PROCESSING_SUCCEEDED;
}
@@ -850,7 +864,9 @@ pvscsi_on_cmd_setup_msg_ring(PVSCSIState *s)
}
if (s->rings_info_valid) {
- pvscsi_ring_init_msg(&s->rings, rc);
+ if (pvscsi_ring_init_msg(&s->rings, rc) < 0) {
+ return PVSCSI_COMMAND_PROCESSING_FAILED;
+ }
s->msg_ring_info_valid = TRUE;
}
return sizeof(PVSCSICmdDescSetupMsgRing) / sizeof(uint32_t);
diff --git a/hw/vfio/common.c b/hw/vfio/common.c
index 88154a1f03..e51ed3a348 100644
--- a/hw/vfio/common.c
+++ b/hw/vfio/common.c
@@ -260,14 +260,20 @@ static void vfio_iommu_map_notify(Notifier *n, void *data)
VFIOGuestIOMMU *giommu = container_of(n, VFIOGuestIOMMU, n);
VFIOContainer *container = giommu->container;
IOMMUTLBEntry *iotlb = data;
+ hwaddr iova = iotlb->iova + giommu->iommu_offset;
MemoryRegion *mr;
hwaddr xlat;
hwaddr len = iotlb->addr_mask + 1;
void *vaddr;
int ret;
- trace_vfio_iommu_map_notify(iotlb->iova,
- iotlb->iova + iotlb->addr_mask);
+ trace_vfio_iommu_map_notify(iova, iova + iotlb->addr_mask);
+
+ if (iotlb->target_as != &address_space_memory) {
+ error_report("Wrong target AS \"%s\", only system memory is allowed",
+ iotlb->target_as->name ? iotlb->target_as->name : "none");
+ return;
+ }
/*
* The IOMMU TLB entry we have just covers translation through
@@ -294,21 +300,21 @@ static void vfio_iommu_map_notify(Notifier *n, void *data)
if ((iotlb->perm & IOMMU_RW) != IOMMU_NONE) {
vaddr = memory_region_get_ram_ptr(mr) + xlat;
- ret = vfio_dma_map(container, iotlb->iova,
+ ret = vfio_dma_map(container, iova,
iotlb->addr_mask + 1, vaddr,
!(iotlb->perm & IOMMU_WO) || mr->readonly);
if (ret) {
error_report("vfio_dma_map(%p, 0x%"HWADDR_PRIx", "
"0x%"HWADDR_PRIx", %p) = %d (%m)",
- container, iotlb->iova,
+ container, iova,
iotlb->addr_mask + 1, vaddr, ret);
}
} else {
- ret = vfio_dma_unmap(container, iotlb->iova, iotlb->addr_mask + 1);
+ ret = vfio_dma_unmap(container, iova, iotlb->addr_mask + 1);
if (ret) {
error_report("vfio_dma_unmap(%p, 0x%"HWADDR_PRIx", "
"0x%"HWADDR_PRIx") = %d (%m)",
- container, iotlb->iova,
+ container, iova,
iotlb->addr_mask + 1, ret);
}
}
@@ -380,6 +386,8 @@ static void vfio_listener_region_add(MemoryListener *listener,
*/
giommu = g_malloc0(sizeof(*giommu));
giommu->iommu = section->mr;
+ giommu->iommu_offset = section->offset_within_address_space -
+ section->offset_within_region;
giommu->container = container;
giommu->n.notify = vfio_iommu_map_notify;
QLIST_INSERT_HEAD(&container->giommu_list, giommu, giommu_next);
@@ -433,6 +441,7 @@ static void vfio_listener_region_del(MemoryListener *listener,
{
VFIOContainer *container = container_of(listener, VFIOContainer, listener);
hwaddr iova, end;
+ Int128 llend, llsize;
int ret;
if (vfio_listener_skipped_section(section)) {
@@ -471,21 +480,25 @@ static void vfio_listener_region_del(MemoryListener *listener,
}
iova = TARGET_PAGE_ALIGN(section->offset_within_address_space);
- end = (section->offset_within_address_space + int128_get64(section->size)) &
- TARGET_PAGE_MASK;
+ llend = int128_make64(section->offset_within_address_space);
+ llend = int128_add(llend, section->size);
+ llend = int128_and(llend, int128_exts64(TARGET_PAGE_MASK));
- if (iova >= end) {
+ if (int128_ge(int128_make64(iova), llend)) {
return;
}
+ end = int128_get64(int128_sub(llend, int128_one()));
+
+ llsize = int128_sub(llend, int128_make64(iova));
- trace_vfio_listener_region_del(iova, end - 1);
+ trace_vfio_listener_region_del(iova, end);
- ret = vfio_dma_unmap(container, iova, end - iova);
+ ret = vfio_dma_unmap(container, iova, int128_get64(llsize));
memory_region_unref(section->mr);
if (ret) {
error_report("vfio_dma_unmap(%p, 0x%"HWADDR_PRIx", "
"0x%"HWADDR_PRIx") = %d (%m)",
- container, iova, end - iova, ret);
+ container, iova, int128_get64(llsize), ret);
}
}
@@ -499,6 +512,54 @@ static void vfio_listener_release(VFIOContainer *container)
memory_listener_unregister(&container->listener);
}
+static struct vfio_info_cap_header *
+vfio_get_region_info_cap(struct vfio_region_info *info, uint16_t id)
+{
+ struct vfio_info_cap_header *hdr;
+ void *ptr = info;
+
+ if (!(info->flags & VFIO_REGION_INFO_FLAG_CAPS)) {
+ return NULL;
+ }
+
+ for (hdr = ptr + info->cap_offset; hdr != ptr; hdr = ptr + hdr->next) {
+ if (hdr->id == id) {
+ return hdr;
+ }
+ }
+
+ return NULL;
+}
+
+static void vfio_setup_region_sparse_mmaps(VFIORegion *region,
+ struct vfio_region_info *info)
+{
+ struct vfio_info_cap_header *hdr;
+ struct vfio_region_info_cap_sparse_mmap *sparse;
+ int i;
+
+ hdr = vfio_get_region_info_cap(info, VFIO_REGION_INFO_CAP_SPARSE_MMAP);
+ if (!hdr) {
+ return;
+ }
+
+ sparse = container_of(hdr, struct vfio_region_info_cap_sparse_mmap, header);
+
+ trace_vfio_region_sparse_mmap_header(region->vbasedev->name,
+ region->nr, sparse->nr_areas);
+
+ region->nr_mmaps = sparse->nr_areas;
+ region->mmaps = g_new0(VFIOMmap, region->nr_mmaps);
+
+ for (i = 0; i < region->nr_mmaps; i++) {
+ region->mmaps[i].offset = sparse->areas[i].offset;
+ region->mmaps[i].size = sparse->areas[i].size;
+ trace_vfio_region_sparse_mmap_entry(i, region->mmaps[i].offset,
+ region->mmaps[i].offset +
+ region->mmaps[i].size);
+ }
+}
+
int vfio_region_setup(Object *obj, VFIODevice *vbasedev, VFIORegion *region,
int index, const char *name)
{
@@ -525,11 +586,14 @@ int vfio_region_setup(Object *obj, VFIODevice *vbasedev, VFIORegion *region,
region->flags & VFIO_REGION_INFO_FLAG_MMAP &&
!(region->size & ~qemu_real_host_page_mask)) {
- region->nr_mmaps = 1;
- region->mmaps = g_new0(VFIOMmap, region->nr_mmaps);
+ vfio_setup_region_sparse_mmaps(region, info);
- region->mmaps[0].offset = 0;
- region->mmaps[0].size = region->size;
+ if (!region->nr_mmaps) {
+ region->nr_mmaps = 1;
+ region->mmaps = g_new0(VFIOMmap, region->nr_mmaps);
+ region->mmaps[0].offset = 0;
+ region->mmaps[0].size = region->size;
+ }
}
}
@@ -1089,16 +1153,60 @@ int vfio_get_region_info(VFIODevice *vbasedev, int index,
*info = g_malloc0(argsz);
(*info)->index = index;
+retry:
(*info)->argsz = argsz;
if (ioctl(vbasedev->fd, VFIO_DEVICE_GET_REGION_INFO, *info)) {
g_free(*info);
+ *info = NULL;
return -errno;
}
+ if ((*info)->argsz > argsz) {
+ argsz = (*info)->argsz;
+ *info = g_realloc(*info, argsz);
+
+ goto retry;
+ }
+
return 0;
}
+int vfio_get_dev_region_info(VFIODevice *vbasedev, uint32_t type,
+ uint32_t subtype, struct vfio_region_info **info)
+{
+ int i;
+
+ for (i = 0; i < vbasedev->num_regions; i++) {
+ struct vfio_info_cap_header *hdr;
+ struct vfio_region_info_cap_type *cap_type;
+
+ if (vfio_get_region_info(vbasedev, i, info)) {
+ continue;
+ }
+
+ hdr = vfio_get_region_info_cap(*info, VFIO_REGION_INFO_CAP_TYPE);
+ if (!hdr) {
+ g_free(*info);
+ continue;
+ }
+
+ cap_type = container_of(hdr, struct vfio_region_info_cap_type, header);
+
+ trace_vfio_get_dev_region(vbasedev->name, i,
+ cap_type->type, cap_type->subtype);
+
+ if (cap_type->type == type && cap_type->subtype == subtype) {
+ return 0;
+ }
+
+ g_free(*info);
+ }
+
+ *info = NULL;
+ return -ENODEV;
+}
+
/*
* Interfaces for IBM EEH (Enhanced Error Handling)
*/
diff --git a/hw/vfio/pci-quirks.c b/hw/vfio/pci-quirks.c
index 49ecf1172a..35d32b78f4 100644
--- a/hw/vfio/pci-quirks.c
+++ b/hw/vfio/pci-quirks.c
@@ -11,9 +11,12 @@
*/
#include "qemu/osdep.h"
+#include "qemu/error-report.h"
+#include "qemu/range.h"
+#include "qapi/error.h"
+#include "hw/nvram/fw_cfg.h"
#include "pci.h"
#include "trace.h"
-#include "qemu/range.h"
/* Use uin32_t for vendor & device so PCI_ANY_ID expands and cannot match hw */
static bool vfio_pci_is(VFIOPCIDevice *vdev, uint32_t vendor, uint32_t device)
@@ -962,6 +965,643 @@ static void vfio_probe_rtl8168_bar2_quirk(VFIOPCIDevice *vdev, int nr)
}
/*
+ * Intel IGD support
+ *
+ * Obviously IGD is not a discrete device, this is evidenced not only by it
+ * being integrated into the CPU, but by the various chipset and BIOS
+ * dependencies that it brings along with it. Intel is trying to move away
+ * from this and Broadwell and newer devices can run in what Intel calls
+ * "Universal Pass-Through" mode, or UPT. Theoretically in UPT mode, nothing
+ * more is required beyond assigning the IGD device to a VM. There are
+ * however support limitations to this mode. It only supports IGD as a
+ * secondary graphics device in the VM and it doesn't officially support any
+ * physical outputs.
+ *
+ * The code here attempts to enable what we'll call legacy mode assignment,
+ * IGD retains most of the capabilities we expect for it to have on bare
+ * metal. To enable this mode, the IGD device must be assigned to the VM
+ * at PCI address 00:02.0, it must have a ROM, it very likely needs VGA
+ * support, we must have VM BIOS support for reserving and populating some
+ * of the required tables, and we need to tweak the chipset with revisions
+ * and IDs and an LPC/ISA bridge device. The intention is to make all of
+ * this happen automatically by installing the device at the correct VM PCI
+ * bus address. If any of the conditions are not met, we cross our fingers
+ * and hope the user knows better.
+ *
+ * NB - It is possible to enable physical outputs in UPT mode by supplying
+ * an OpRegion table. We don't do this by default because the guest driver
+ * behaves differently if an OpRegion is provided and no monitor is attached
+ * vs no OpRegion and a monitor being attached or not. Effectively, if a
+ * headless setup is desired, the OpRegion gets in the way of that.
+ */
+
+/*
+ * This presumes the device is already known to be an Intel VGA device, so we
+ * take liberties in which device ID bits match which generation. This should
+ * not be taken as an indication that all the devices are supported, or even
+ * supportable, some of them don't even support VT-d.
+ * See linux:include/drm/i915_pciids.h for IDs.
+ */
+static int igd_gen(VFIOPCIDevice *vdev)
+{
+ if ((vdev->device_id & 0xfff) == 0xa84) {
+ return 8; /* Broxton */
+ }
+
+ switch (vdev->device_id & 0xff00) {
+ /* Old, untested, unavailable, unknown */
+ case 0x0000:
+ case 0x2500:
+ case 0x2700:
+ case 0x2900:
+ case 0x2a00:
+ case 0x2e00:
+ case 0x3500:
+ case 0xa000:
+ return -1;
+ /* SandyBridge, IvyBridge, ValleyView, Haswell */
+ case 0x0100:
+ case 0x0400:
+ case 0x0a00:
+ case 0x0c00:
+ case 0x0d00:
+ case 0x0f00:
+ return 6;
+ /* BroadWell, CherryView, SkyLake, KabyLake */
+ case 0x1600:
+ case 0x1900:
+ case 0x2200:
+ case 0x5900:
+ return 8;
+ }
+
+ return 8; /* Assume newer is compatible */
+}
+
+typedef struct VFIOIGDQuirk {
+ struct VFIOPCIDevice *vdev;
+ uint32_t index;
+} VFIOIGDQuirk;
+
+#define IGD_GMCH 0x50 /* Graphics Control Register */
+#define IGD_BDSM 0x5c /* Base Data of Stolen Memory */
+#define IGD_ASLS 0xfc /* ASL Storage Register */
+
+/*
+ * The OpRegion includes the Video BIOS Table, which seems important for
+ * telling the driver what sort of outputs it has. Without this, the device
+ * may work in the guest, but we may not get output. This also requires BIOS
+ * support to reserve and populate a section of guest memory sufficient for
+ * the table and to write the base address of that memory to the ASLS register
+ * of the IGD device.
+ */
+int vfio_pci_igd_opregion_init(VFIOPCIDevice *vdev,
+ struct vfio_region_info *info)
+{
+ int ret;
+
+ vdev->igd_opregion = g_malloc0(info->size);
+ ret = pread(vdev->vbasedev.fd, vdev->igd_opregion,
+ info->size, info->offset);
+ if (ret != info->size) {
+ error_report("vfio: Error reading IGD OpRegion");
+ g_free(vdev->igd_opregion);
+ vdev->igd_opregion = NULL;
+ return -EINVAL;
+ }
+
+ /*
+ * Provide fw_cfg with a copy of the OpRegion which the VM firmware is to
+ * allocate 32bit reserved memory for, copy these contents into, and write
+ * the reserved memory base address to the device ASLS register at 0xFC.
+ * Alignment of this reserved region seems flexible, but using a 4k page
+ * alignment seems to work well. This interface assumes a single IGD
+ * device, which may be at VM address 00:02.0 in legacy mode or another
+ * address in UPT mode.
+ *
+ * NB, there may be future use cases discovered where the VM should have
+ * direct interaction with the host OpRegion, in which case the write to
+ * the ASLS register would trigger MemoryRegion setup to enable that.
+ */
+ fw_cfg_add_file(fw_cfg_find(), "etc/igd-opregion",
+ vdev->igd_opregion, info->size);
+
+ trace_vfio_pci_igd_opregion_enabled(vdev->vbasedev.name);
+
+ pci_set_long(vdev->pdev.config + IGD_ASLS, 0);
+ pci_set_long(vdev->pdev.wmask + IGD_ASLS, ~0);
+ pci_set_long(vdev->emulated_config_bits + IGD_ASLS, ~0);
+
+ return 0;
+}
+
+/*
+ * The rather short list of registers that we copy from the host devices.
+ * The LPC/ISA bridge values are definitely needed to support the vBIOS, the
+ * host bridge values may or may not be needed depending on the guest OS.
+ * Since we're only munging revision and subsystem values on the host bridge,
+ * we don't require our own device. The LPC/ISA bridge needs to be our very
+ * own though.
+ */
+typedef struct {
+ uint8_t offset;
+ uint8_t len;
+} IGDHostInfo;
+
+static const IGDHostInfo igd_host_bridge_infos[] = {
+ {PCI_REVISION_ID, 2},
+ {PCI_SUBSYSTEM_VENDOR_ID, 2},
+ {PCI_SUBSYSTEM_ID, 2},
+};
+
+static const IGDHostInfo igd_lpc_bridge_infos[] = {
+ {PCI_VENDOR_ID, 2},
+ {PCI_DEVICE_ID, 2},
+ {PCI_REVISION_ID, 2},
+ {PCI_SUBSYSTEM_VENDOR_ID, 2},
+ {PCI_SUBSYSTEM_ID, 2},
+};
+
+static int vfio_pci_igd_copy(VFIOPCIDevice *vdev, PCIDevice *pdev,
+ struct vfio_region_info *info,
+ const IGDHostInfo *list, int len)
+{
+ int i, ret;
+
+ for (i = 0; i < len; i++) {
+ ret = pread(vdev->vbasedev.fd, pdev->config + list[i].offset,
+ list[i].len, info->offset + list[i].offset);
+ if (ret != list[i].len) {
+ error_report("IGD copy failed: %m");
+ return -errno;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Stuff a few values into the host bridge.
+ */
+static int vfio_pci_igd_host_init(VFIOPCIDevice *vdev,
+ struct vfio_region_info *info)
+{
+ PCIBus *bus;
+ PCIDevice *host_bridge;
+ int ret;
+
+ bus = pci_device_root_bus(&vdev->pdev);
+ host_bridge = pci_find_device(bus, 0, PCI_DEVFN(0, 0));
+
+ if (!host_bridge) {
+ error_report("Can't find host bridge");
+ return -ENODEV;
+ }
+
+ ret = vfio_pci_igd_copy(vdev, host_bridge, info, igd_host_bridge_infos,
+ ARRAY_SIZE(igd_host_bridge_infos));
+ if (!ret) {
+ trace_vfio_pci_igd_host_bridge_enabled(vdev->vbasedev.name);
+ }
+
+ return ret;
+}
+
+/*
+ * IGD LPC/ISA bridge support code. The vBIOS needs this, but we can't write
+ * arbitrary values into just any bridge, so we must create our own. We try
+ * to handle if the user has created it for us, which they might want to do
+ * to enable multifuction so we don't occupy the whole PCI slot.
+ */
+static void vfio_pci_igd_lpc_bridge_realize(PCIDevice *pdev, Error **errp)
+{
+ if (pdev->devfn != PCI_DEVFN(0x1f, 0)) {
+ error_setg(errp, "VFIO dummy ISA/LPC bridge must have address 1f.0");
+ }
+}
+
+static void vfio_pci_igd_lpc_bridge_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+ dc->desc = "VFIO dummy ISA/LPC bridge for IGD assignment";
+ dc->hotpluggable = false;
+ k->realize = vfio_pci_igd_lpc_bridge_realize;
+ k->class_id = PCI_CLASS_BRIDGE_ISA;
+}
+
+static TypeInfo vfio_pci_igd_lpc_bridge_info = {
+ .name = "vfio-pci-igd-lpc-bridge",
+ .parent = TYPE_PCI_DEVICE,
+ .class_init = vfio_pci_igd_lpc_bridge_class_init,
+};
+
+static void vfio_pci_igd_register_types(void)
+{
+ type_register_static(&vfio_pci_igd_lpc_bridge_info);
+}
+
+type_init(vfio_pci_igd_register_types)
+
+static int vfio_pci_igd_lpc_init(VFIOPCIDevice *vdev,
+ struct vfio_region_info *info)
+{
+ PCIDevice *lpc_bridge;
+ int ret;
+
+ lpc_bridge = pci_find_device(pci_device_root_bus(&vdev->pdev),
+ 0, PCI_DEVFN(0x1f, 0));
+ if (!lpc_bridge) {
+ lpc_bridge = pci_create_simple(pci_device_root_bus(&vdev->pdev),
+ PCI_DEVFN(0x1f, 0), "vfio-pci-igd-lpc-bridge");
+ }
+
+ ret = vfio_pci_igd_copy(vdev, lpc_bridge, info, igd_lpc_bridge_infos,
+ ARRAY_SIZE(igd_lpc_bridge_infos));
+ if (!ret) {
+ trace_vfio_pci_igd_lpc_bridge_enabled(vdev->vbasedev.name);
+ }
+
+ return ret;
+}
+
+/*
+ * IGD Gen8 and newer support up to 8MB for the GTT and use a 64bit PTE
+ * entry, older IGDs use 2MB and 32bit. Each PTE maps a 4k page. Therefore
+ * we either have 2M/4k * 4 = 2k or 8M/4k * 8 = 16k as the maximum iobar index
+ * for programming the GTT.
+ *
+ * See linux:include/drm/i915_drm.h for shift and mask values.
+ */
+static int vfio_igd_gtt_max(VFIOPCIDevice *vdev)
+{
+ uint32_t gmch = vfio_pci_read_config(&vdev->pdev, IGD_GMCH, sizeof(gmch));
+ int ggms, gen = igd_gen(vdev);
+
+ gmch = vfio_pci_read_config(&vdev->pdev, IGD_GMCH, sizeof(gmch));
+ ggms = (gmch >> (gen < 8 ? 8 : 6)) & 0x3;
+ if (gen > 6) {
+ ggms = 1 << ggms;
+ }
+
+ ggms *= 1024 * 1024;
+
+ return (ggms / (4 * 1024)) * (gen < 8 ? 4 : 8);
+}
+
+/*
+ * The IGD ROM will make use of stolen memory (GGMS) for support of VESA modes.
+ * Somehow the host stolen memory range is used for this, but how the ROM gets
+ * it is a mystery, perhaps it's hardcoded into the ROM. Thankfully though, it
+ * reprograms the GTT through the IOBAR where we can trap it and transpose the
+ * programming to the VM allocated buffer. That buffer gets reserved by the VM
+ * firmware via the fw_cfg entry added below. Here we're just monitoring the
+ * IOBAR address and data registers to detect a write sequence targeting the
+ * GTTADR. This code is developed by observed behavior and doesn't have a
+ * direct spec reference, unfortunately.
+ */
+static uint64_t vfio_igd_quirk_data_read(void *opaque,
+ hwaddr addr, unsigned size)
+{
+ VFIOIGDQuirk *igd = opaque;
+ VFIOPCIDevice *vdev = igd->vdev;
+
+ igd->index = ~0;
+
+ return vfio_region_read(&vdev->bars[4].region, addr + 4, size);
+}
+
+static void vfio_igd_quirk_data_write(void *opaque, hwaddr addr,
+ uint64_t data, unsigned size)
+{
+ VFIOIGDQuirk *igd = opaque;
+ VFIOPCIDevice *vdev = igd->vdev;
+ uint64_t val = data;
+ int gen = igd_gen(vdev);
+
+ /*
+ * Programming the GGMS starts at index 0x1 and uses every 4th index (ie.
+ * 0x1, 0x5, 0x9, 0xd,...). For pre-Gen8 each 4-byte write is a whole PTE
+ * entry, with 0th bit enable set. For Gen8 and up, PTEs are 64bit, so
+ * entries 0x5 & 0xd are the high dword, in our case zero. Each PTE points
+ * to a 4k page, which we translate to a page from the VM allocated region,
+ * pointed to by the BDSM register. If this is not set, we fail.
+ *
+ * We trap writes to the full configured GTT size, but we typically only
+ * see the vBIOS writing up to (nearly) the 1MB barrier. In fact it often
+ * seems to miss the last entry for an even 1MB GTT. Doing a gratuitous
+ * write of that last entry does work, but is hopefully unnecessary since
+ * we clear the previous GTT on initialization.
+ */
+ if ((igd->index % 4 == 1) && igd->index < vfio_igd_gtt_max(vdev)) {
+ if (gen < 8 || (igd->index % 8 == 1)) {
+ uint32_t base;
+
+ base = pci_get_long(vdev->pdev.config + IGD_BDSM);
+ if (!base) {
+ hw_error("vfio-igd: Guest attempted to program IGD GTT before "
+ "BIOS reserved stolen memory. Unsupported BIOS?");
+ }
+
+ val = base | (data & ((1 << 20) - 1));
+ } else {
+ val = 0; /* upper 32bits of pte, we only enable below 4G PTEs */
+ }
+
+ trace_vfio_pci_igd_bar4_write(vdev->vbasedev.name,
+ igd->index, data, val);
+ }
+
+ vfio_region_write(&vdev->bars[4].region, addr + 4, val, size);
+
+ igd->index = ~0;
+}
+
+static const MemoryRegionOps vfio_igd_data_quirk = {
+ .read = vfio_igd_quirk_data_read,
+ .write = vfio_igd_quirk_data_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static uint64_t vfio_igd_quirk_index_read(void *opaque,
+ hwaddr addr, unsigned size)
+{
+ VFIOIGDQuirk *igd = opaque;
+ VFIOPCIDevice *vdev = igd->vdev;
+
+ igd->index = ~0;
+
+ return vfio_region_read(&vdev->bars[4].region, addr, size);
+}
+
+static void vfio_igd_quirk_index_write(void *opaque, hwaddr addr,
+ uint64_t data, unsigned size)
+{
+ VFIOIGDQuirk *igd = opaque;
+ VFIOPCIDevice *vdev = igd->vdev;
+
+ igd->index = data;
+
+ vfio_region_write(&vdev->bars[4].region, addr, data, size);
+}
+
+static const MemoryRegionOps vfio_igd_index_quirk = {
+ .read = vfio_igd_quirk_index_read,
+ .write = vfio_igd_quirk_index_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr)
+{
+ struct vfio_region_info *rom = NULL, *opregion = NULL,
+ *host = NULL, *lpc = NULL;
+ VFIOQuirk *quirk;
+ VFIOIGDQuirk *igd;
+ PCIDevice *lpc_bridge;
+ int i, ret, ggms_mb, gms_mb = 0, gen;
+ uint64_t *bdsm_size;
+ uint32_t gmch;
+ uint16_t cmd_orig, cmd;
+
+ /*
+ * This must be an Intel VGA device at address 00:02.0 for us to even
+ * consider enabling legacy mode. The vBIOS has dependencies on the
+ * PCI bus address.
+ */
+ if (!vfio_pci_is(vdev, PCI_VENDOR_ID_INTEL, PCI_ANY_ID) ||
+ !vfio_is_vga(vdev) || nr != 4 ||
+ &vdev->pdev != pci_find_device(pci_device_root_bus(&vdev->pdev),
+ 0, PCI_DEVFN(0x2, 0))) {
+ return;
+ }
+
+ /*
+ * We need to create an LPC/ISA bridge at PCI bus address 00:1f.0 that we
+ * can stuff host values into, so if there's already one there and it's not
+ * one we can hack on, legacy mode is no-go. Sorry Q35.
+ */
+ lpc_bridge = pci_find_device(pci_device_root_bus(&vdev->pdev),
+ 0, PCI_DEVFN(0x1f, 0));
+ if (lpc_bridge && !object_dynamic_cast(OBJECT(lpc_bridge),
+ "vfio-pci-igd-lpc-bridge")) {
+ error_report("IGD device %s cannot support legacy mode due to existing "
+ "devices at address 1f.0", vdev->vbasedev.name);
+ return;
+ }
+
+ /*
+ * IGD is not a standard, they like to change their specs often. We
+ * only attempt to support back to SandBridge and we hope that newer
+ * devices maintain compatibility with generation 8.
+ */
+ gen = igd_gen(vdev);
+ if (gen != 6 && gen != 8) {
+ error_report("IGD device %s is unsupported in legacy mode, "
+ "try SandyBridge or newer", vdev->vbasedev.name);
+ return;
+ }
+
+ /*
+ * Most of what we're doing here is to enable the ROM to run, so if
+ * there's no ROM, there's no point in setting up this quirk.
+ * NB. We only seem to get BIOS ROMs, so a UEFI VM would need CSM support.
+ */
+ ret = vfio_get_region_info(&vdev->vbasedev,
+ VFIO_PCI_ROM_REGION_INDEX, &rom);
+ if ((ret || !rom->size) && !vdev->pdev.romfile) {
+ error_report("IGD device %s has no ROM, legacy mode disabled",
+ vdev->vbasedev.name);
+ goto out;
+ }
+
+ /*
+ * Ignore the hotplug corner case, mark the ROM failed, we can't
+ * create the devices we need for legacy mode in the hotplug scenario.
+ */
+ if (vdev->pdev.qdev.hotplugged) {
+ error_report("IGD device %s hotplugged, ROM disabled, "
+ "legacy mode disabled", vdev->vbasedev.name);
+ vdev->rom_read_failed = true;
+ goto out;
+ }
+
+ /*
+ * Check whether we have all the vfio device specific regions to
+ * support legacy mode (added in Linux v4.6). If not, bail.
+ */
+ ret = vfio_get_dev_region_info(&vdev->vbasedev,
+ VFIO_REGION_TYPE_PCI_VENDOR_TYPE | PCI_VENDOR_ID_INTEL,
+ VFIO_REGION_SUBTYPE_INTEL_IGD_OPREGION, &opregion);
+ if (ret) {
+ error_report("IGD device %s does not support OpRegion access,"
+ "legacy mode disabled", vdev->vbasedev.name);
+ goto out;
+ }
+
+ ret = vfio_get_dev_region_info(&vdev->vbasedev,
+ VFIO_REGION_TYPE_PCI_VENDOR_TYPE | PCI_VENDOR_ID_INTEL,
+ VFIO_REGION_SUBTYPE_INTEL_IGD_HOST_CFG, &host);
+ if (ret) {
+ error_report("IGD device %s does not support host bridge access,"
+ "legacy mode disabled", vdev->vbasedev.name);
+ goto out;
+ }
+
+ ret = vfio_get_dev_region_info(&vdev->vbasedev,
+ VFIO_REGION_TYPE_PCI_VENDOR_TYPE | PCI_VENDOR_ID_INTEL,
+ VFIO_REGION_SUBTYPE_INTEL_IGD_LPC_CFG, &lpc);
+ if (ret) {
+ error_report("IGD device %s does not support LPC bridge access,"
+ "legacy mode disabled", vdev->vbasedev.name);
+ goto out;
+ }
+
+ gmch = vfio_pci_read_config(&vdev->pdev, IGD_GMCH, 4);
+
+ /*
+ * If IGD VGA Disable is clear (expected) and VGA is not already enabled,
+ * try to enable it. Probably shouldn't be using legacy mode without VGA,
+ * but also no point in us enabling VGA if disabled in hardware.
+ */
+ if (!(gmch & 0x2) && !vdev->vga && vfio_populate_vga(vdev)) {
+ error_report("IGD device %s failed to enable VGA access, "
+ "legacy mode disabled", vdev->vbasedev.name);
+ goto out;
+ }
+
+ /* Create our LPC/ISA bridge */
+ ret = vfio_pci_igd_lpc_init(vdev, lpc);
+ if (ret) {
+ error_report("IGD device %s failed to create LPC bridge, "
+ "legacy mode disabled", vdev->vbasedev.name);
+ goto out;
+ }
+
+ /* Stuff some host values into the VM PCI host bridge */
+ ret = vfio_pci_igd_host_init(vdev, host);
+ if (ret) {
+ error_report("IGD device %s failed to modify host bridge, "
+ "legacy mode disabled", vdev->vbasedev.name);
+ goto out;
+ }
+
+ /* Setup OpRegion access */
+ ret = vfio_pci_igd_opregion_init(vdev, opregion);
+ if (ret) {
+ error_report("IGD device %s failed to setup OpRegion, "
+ "legacy mode disabled", vdev->vbasedev.name);
+ goto out;
+ }
+
+ /* Setup our quirk to munge GTT addresses to the VM allocated buffer */
+ quirk = g_malloc0(sizeof(*quirk));
+ quirk->mem = g_new0(MemoryRegion, 2);
+ quirk->nr_mem = 2;
+ igd = quirk->data = g_malloc0(sizeof(*igd));
+ igd->vdev = vdev;
+ igd->index = ~0;
+
+ memory_region_init_io(&quirk->mem[0], OBJECT(vdev), &vfio_igd_index_quirk,
+ igd, "vfio-igd-index-quirk", 4);
+ memory_region_add_subregion_overlap(vdev->bars[nr].region.mem,
+ 0, &quirk->mem[0], 1);
+
+ memory_region_init_io(&quirk->mem[1], OBJECT(vdev), &vfio_igd_data_quirk,
+ igd, "vfio-igd-data-quirk", 4);
+ memory_region_add_subregion_overlap(vdev->bars[nr].region.mem,
+ 4, &quirk->mem[1], 1);
+
+ QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
+
+ /* Determine the size of stolen memory needed for GTT */
+ ggms_mb = (gmch >> (gen < 8 ? 8 : 6)) & 0x3;
+ if (gen > 6) {
+ ggms_mb = 1 << ggms_mb;
+ }
+
+ /*
+ * Assume we have no GMS memory, but allow it to be overrided by device
+ * option (experimental). The spec doesn't actually allow zero GMS when
+ * when IVD (IGD VGA Disable) is clear, but the claim is that it's unused,
+ * so let's not waste VM memory for it.
+ */
+ gmch &= ~((gen < 8 ? 0x1f : 0xff) << (gen < 8 ? 3 : 8));
+
+ if (vdev->igd_gms) {
+ if (vdev->igd_gms <= 0x10) {
+ gms_mb = vdev->igd_gms * 32;
+ gmch |= vdev->igd_gms << (gen < 8 ? 3 : 8);
+ } else {
+ error_report("Unsupported IGD GMS value 0x%x", vdev->igd_gms);
+ vdev->igd_gms = 0;
+ }
+ }
+
+ /*
+ * Request reserved memory for stolen memory via fw_cfg. VM firmware
+ * must allocate a 1MB aligned reserved memory region below 4GB with
+ * the requested size (in bytes) for use by the Intel PCI class VGA
+ * device at VM address 00:02.0. The base address of this reserved
+ * memory region must be written to the device BDSM regsiter at PCI
+ * config offset 0x5C.
+ */
+ bdsm_size = g_malloc(sizeof(*bdsm_size));
+ *bdsm_size = cpu_to_le64((ggms_mb + gms_mb) * 1024 * 1024);
+ fw_cfg_add_file(fw_cfg_find(), "etc/igd-bdsm-size",
+ bdsm_size, sizeof(*bdsm_size));
+
+ /* GMCH is read-only, emulated */
+ pci_set_long(vdev->pdev.config + IGD_GMCH, gmch);
+ pci_set_long(vdev->pdev.wmask + IGD_GMCH, 0);
+ pci_set_long(vdev->emulated_config_bits + IGD_GMCH, ~0);
+
+ /* BDSM is read-write, emulated. The BIOS needs to be able to write it */
+ pci_set_long(vdev->pdev.config + IGD_BDSM, 0);
+ pci_set_long(vdev->pdev.wmask + IGD_BDSM, ~0);
+ pci_set_long(vdev->emulated_config_bits + IGD_BDSM, ~0);
+
+ /*
+ * This IOBAR gives us access to GTTADR, which allows us to write to
+ * the GTT itself. So let's go ahead and write zero to all the GTT
+ * entries to avoid spurious DMA faults. Be sure I/O access is enabled
+ * before talking to the device.
+ */
+ if (pread(vdev->vbasedev.fd, &cmd_orig, sizeof(cmd_orig),
+ vdev->config_offset + PCI_COMMAND) != sizeof(cmd_orig)) {
+ error_report("IGD device %s - failed to read PCI command register",
+ vdev->vbasedev.name);
+ }
+
+ cmd = cmd_orig | PCI_COMMAND_IO;
+
+ if (pwrite(vdev->vbasedev.fd, &cmd, sizeof(cmd),
+ vdev->config_offset + PCI_COMMAND) != sizeof(cmd)) {
+ error_report("IGD device %s - failed to write PCI command register",
+ vdev->vbasedev.name);
+ }
+
+ for (i = 1; i < vfio_igd_gtt_max(vdev); i += 4) {
+ vfio_region_write(&vdev->bars[4].region, 0, i, 4);
+ vfio_region_write(&vdev->bars[4].region, 4, 0, 4);
+ }
+
+ if (pwrite(vdev->vbasedev.fd, &cmd_orig, sizeof(cmd_orig),
+ vdev->config_offset + PCI_COMMAND) != sizeof(cmd_orig)) {
+ error_report("IGD device %s - failed to restore PCI command register",
+ vdev->vbasedev.name);
+ }
+
+ trace_vfio_pci_igd_bdsm_enabled(vdev->vbasedev.name, ggms_mb + gms_mb);
+
+out:
+ g_free(rom);
+ g_free(opregion);
+ g_free(host);
+ g_free(lpc);
+}
+
+/*
* Common quirk probe entry points.
*/
void vfio_vga_quirk_setup(VFIOPCIDevice *vdev)
@@ -1010,6 +1650,7 @@ void vfio_bar_quirk_setup(VFIOPCIDevice *vdev, int nr)
vfio_probe_nvidia_bar5_quirk(vdev, nr);
vfio_probe_nvidia_bar0_quirk(vdev, nr);
vfio_probe_rtl8168_bar2_quirk(vdev, nr);
+ vfio_probe_igd_bar4_quirk(vdev, nr);
}
void vfio_bar_quirk_exit(VFIOPCIDevice *vdev, int nr)
diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c
index d091d8cf0e..deab0c601a 100644
--- a/hw/vfio/pci.c
+++ b/hw/vfio/pci.c
@@ -1440,8 +1440,6 @@ static void vfio_bar_setup(VFIOPCIDevice *vdev, int nr)
vdev->vbasedev.name, nr);
}
- vfio_bar_quirk_setup(vdev, nr);
-
pci_register_bar(&vdev->pdev, nr, type, bar->region.mem);
}
@@ -1452,29 +1450,6 @@ static void vfio_bars_setup(VFIOPCIDevice *vdev)
for (i = 0; i < PCI_ROM_SLOT; i++) {
vfio_bar_setup(vdev, i);
}
-
- if (vdev->vga) {
- memory_region_init_io(&vdev->vga->region[QEMU_PCI_VGA_MEM].mem,
- OBJECT(vdev), &vfio_vga_ops,
- &vdev->vga->region[QEMU_PCI_VGA_MEM],
- "vfio-vga-mmio@0xa0000",
- QEMU_PCI_VGA_MEM_SIZE);
- memory_region_init_io(&vdev->vga->region[QEMU_PCI_VGA_IO_LO].mem,
- OBJECT(vdev), &vfio_vga_ops,
- &vdev->vga->region[QEMU_PCI_VGA_IO_LO],
- "vfio-vga-io@0x3b0",
- QEMU_PCI_VGA_IO_LO_SIZE);
- memory_region_init_io(&vdev->vga->region[QEMU_PCI_VGA_IO_HI].mem,
- OBJECT(vdev), &vfio_vga_ops,
- &vdev->vga->region[QEMU_PCI_VGA_IO_HI],
- "vfio-vga-io@0x3c0",
- QEMU_PCI_VGA_IO_HI_SIZE);
-
- pci_register_vga(&vdev->pdev, &vdev->vga->region[QEMU_PCI_VGA_MEM].mem,
- &vdev->vga->region[QEMU_PCI_VGA_IO_LO].mem,
- &vdev->vga->region[QEMU_PCI_VGA_IO_HI].mem);
- vfio_vga_quirk_setup(vdev);
- }
}
static void vfio_bars_exit(VFIOPCIDevice *vdev)
@@ -2061,42 +2036,61 @@ int vfio_populate_vga(VFIOPCIDevice *vdev)
struct vfio_region_info *reg_info;
int ret;
- if (vbasedev->num_regions > VFIO_PCI_VGA_REGION_INDEX) {
- ret = vfio_get_region_info(vbasedev,
- VFIO_PCI_VGA_REGION_INDEX, &reg_info);
- if (ret) {
- return ret;
- }
+ ret = vfio_get_region_info(vbasedev, VFIO_PCI_VGA_REGION_INDEX, &reg_info);
+ if (ret) {
+ return ret;
+ }
- if (!(reg_info->flags & VFIO_REGION_INFO_FLAG_READ) ||
- !(reg_info->flags & VFIO_REGION_INFO_FLAG_WRITE) ||
- reg_info->size < 0xbffff + 1) {
- error_report("vfio: Unexpected VGA info, flags 0x%lx, size 0x%lx",
- (unsigned long)reg_info->flags,
- (unsigned long)reg_info->size);
- g_free(reg_info);
- return -EINVAL;
- }
+ if (!(reg_info->flags & VFIO_REGION_INFO_FLAG_READ) ||
+ !(reg_info->flags & VFIO_REGION_INFO_FLAG_WRITE) ||
+ reg_info->size < 0xbffff + 1) {
+ error_report("vfio: Unexpected VGA info, flags 0x%lx, size 0x%lx",
+ (unsigned long)reg_info->flags,
+ (unsigned long)reg_info->size);
+ g_free(reg_info);
+ return -EINVAL;
+ }
- vdev->vga = g_new0(VFIOVGA, 1);
+ vdev->vga = g_new0(VFIOVGA, 1);
- vdev->vga->fd_offset = reg_info->offset;
- vdev->vga->fd = vdev->vbasedev.fd;
+ vdev->vga->fd_offset = reg_info->offset;
+ vdev->vga->fd = vdev->vbasedev.fd;
- g_free(reg_info);
+ g_free(reg_info);
- vdev->vga->region[QEMU_PCI_VGA_MEM].offset = QEMU_PCI_VGA_MEM_BASE;
- vdev->vga->region[QEMU_PCI_VGA_MEM].nr = QEMU_PCI_VGA_MEM;
- QLIST_INIT(&vdev->vga->region[QEMU_PCI_VGA_MEM].quirks);
+ vdev->vga->region[QEMU_PCI_VGA_MEM].offset = QEMU_PCI_VGA_MEM_BASE;
+ vdev->vga->region[QEMU_PCI_VGA_MEM].nr = QEMU_PCI_VGA_MEM;
+ QLIST_INIT(&vdev->vga->region[QEMU_PCI_VGA_MEM].quirks);
- vdev->vga->region[QEMU_PCI_VGA_IO_LO].offset = QEMU_PCI_VGA_IO_LO_BASE;
- vdev->vga->region[QEMU_PCI_VGA_IO_LO].nr = QEMU_PCI_VGA_IO_LO;
- QLIST_INIT(&vdev->vga->region[QEMU_PCI_VGA_IO_LO].quirks);
+ memory_region_init_io(&vdev->vga->region[QEMU_PCI_VGA_MEM].mem,
+ OBJECT(vdev), &vfio_vga_ops,
+ &vdev->vga->region[QEMU_PCI_VGA_MEM],
+ "vfio-vga-mmio@0xa0000",
+ QEMU_PCI_VGA_MEM_SIZE);
- vdev->vga->region[QEMU_PCI_VGA_IO_HI].offset = QEMU_PCI_VGA_IO_HI_BASE;
- vdev->vga->region[QEMU_PCI_VGA_IO_HI].nr = QEMU_PCI_VGA_IO_HI;
- QLIST_INIT(&vdev->vga->region[QEMU_PCI_VGA_IO_HI].quirks);
- }
+ vdev->vga->region[QEMU_PCI_VGA_IO_LO].offset = QEMU_PCI_VGA_IO_LO_BASE;
+ vdev->vga->region[QEMU_PCI_VGA_IO_LO].nr = QEMU_PCI_VGA_IO_LO;
+ QLIST_INIT(&vdev->vga->region[QEMU_PCI_VGA_IO_LO].quirks);
+
+ memory_region_init_io(&vdev->vga->region[QEMU_PCI_VGA_IO_LO].mem,
+ OBJECT(vdev), &vfio_vga_ops,
+ &vdev->vga->region[QEMU_PCI_VGA_IO_LO],
+ "vfio-vga-io@0x3b0",
+ QEMU_PCI_VGA_IO_LO_SIZE);
+
+ vdev->vga->region[QEMU_PCI_VGA_IO_HI].offset = QEMU_PCI_VGA_IO_HI_BASE;
+ vdev->vga->region[QEMU_PCI_VGA_IO_HI].nr = QEMU_PCI_VGA_IO_HI;
+ QLIST_INIT(&vdev->vga->region[QEMU_PCI_VGA_IO_HI].quirks);
+
+ memory_region_init_io(&vdev->vga->region[QEMU_PCI_VGA_IO_HI].mem,
+ OBJECT(vdev), &vfio_vga_ops,
+ &vdev->vga->region[QEMU_PCI_VGA_IO_HI],
+ "vfio-vga-io@0x3c0",
+ QEMU_PCI_VGA_IO_HI_SIZE);
+
+ pci_register_vga(&vdev->pdev, &vdev->vga->region[QEMU_PCI_VGA_MEM].mem,
+ &vdev->vga->region[QEMU_PCI_VGA_IO_LO].mem,
+ &vdev->vga->region[QEMU_PCI_VGA_IO_HI].mem);
return 0;
}
@@ -2398,7 +2392,7 @@ static int vfio_initfn(PCIDevice *pdev)
ssize_t len;
struct stat st;
int groupid;
- int ret;
+ int i, ret;
if (!vdev->vbasedev.sysfsdev) {
vdev->vbasedev.sysfsdev =
@@ -2560,6 +2554,43 @@ static int vfio_initfn(PCIDevice *pdev)
goto out_teardown;
}
+ if (vdev->vga) {
+ vfio_vga_quirk_setup(vdev);
+ }
+
+ for (i = 0; i < PCI_ROM_SLOT; i++) {
+ vfio_bar_quirk_setup(vdev, i);
+ }
+
+ if (!vdev->igd_opregion &&
+ vdev->features & VFIO_FEATURE_ENABLE_IGD_OPREGION) {
+ struct vfio_region_info *opregion;
+
+ if (vdev->pdev.qdev.hotplugged) {
+ error_report("Cannot support IGD OpRegion feature on hotplugged "
+ "device %s", vdev->vbasedev.name);
+ ret = -EINVAL;
+ goto out_teardown;
+ }
+
+ ret = vfio_get_dev_region_info(&vdev->vbasedev,
+ VFIO_REGION_TYPE_PCI_VENDOR_TYPE | PCI_VENDOR_ID_INTEL,
+ VFIO_REGION_SUBTYPE_INTEL_IGD_OPREGION, &opregion);
+ if (ret) {
+ error_report("Device %s does not support requested IGD OpRegion "
+ "feature", vdev->vbasedev.name);
+ goto out_teardown;
+ }
+
+ ret = vfio_pci_igd_opregion_init(vdev, opregion);
+ g_free(opregion);
+ if (ret) {
+ error_report("Device %s IGD OpRegion initialization failed",
+ vdev->vbasedev.name);
+ goto out_teardown;
+ }
+ }
+
/* QEMU emulates all of MSI & MSIX */
if (pdev->cap_present & QEMU_PCI_CAP_MSIX) {
memset(vdev->emulated_config_bits + pdev->msix_cap, 0xff,
@@ -2603,6 +2634,13 @@ static void vfio_instance_finalize(Object *obj)
vfio_bars_finalize(vdev);
g_free(vdev->emulated_config_bits);
g_free(vdev->rom);
+ /*
+ * XXX Leaking igd_opregion is not an oversight, we can't remove the
+ * fw_cfg entry therefore leaking this allocation seems like the safest
+ * option.
+ *
+ * g_free(vdev->igd_opregion);
+ */
vfio_put_device(vdev);
vfio_put_group(group);
}
@@ -2677,6 +2715,8 @@ static Property vfio_pci_dev_properties[] = {
VFIO_FEATURE_ENABLE_VGA_BIT, false),
DEFINE_PROP_BIT("x-req", VFIOPCIDevice, features,
VFIO_FEATURE_ENABLE_REQ_BIT, true),
+ DEFINE_PROP_BIT("x-igd-opregion", VFIOPCIDevice, features,
+ VFIO_FEATURE_ENABLE_IGD_OPREGION_BIT, false),
DEFINE_PROP_BOOL("x-no-mmap", VFIOPCIDevice, vbasedev.no_mmap, false),
DEFINE_PROP_BOOL("x-no-kvm-intx", VFIOPCIDevice, no_kvm_intx, false),
DEFINE_PROP_BOOL("x-no-kvm-msi", VFIOPCIDevice, no_kvm_msi, false),
@@ -2687,6 +2727,7 @@ static Property vfio_pci_dev_properties[] = {
sub_vendor_id, PCI_ANY_ID),
DEFINE_PROP_UINT32("x-pci-sub-device-id", VFIOPCIDevice,
sub_device_id, PCI_ANY_ID),
+ DEFINE_PROP_UINT32("x-igd-gms", VFIOPCIDevice, igd_gms, 0),
/*
* TODO - support passed fds... is this necessary?
* DEFINE_PROP_STRING("vfiofd", VFIOPCIDevice, vfiofd_name),
diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h
index 3976f68549..b3eb0d838e 100644
--- a/hw/vfio/pci.h
+++ b/hw/vfio/pci.h
@@ -115,6 +115,7 @@ typedef struct VFIOPCIDevice {
int interrupt; /* Current interrupt type */
VFIOBAR bars[PCI_NUM_REGIONS - 1]; /* No ROM */
VFIOVGA *vga; /* 0xa0000, 0x3b0, 0x3c0 */
+ void *igd_opregion;
PCIHostDeviceAddress host;
EventNotifier err_notifier;
EventNotifier req_notifier;
@@ -128,7 +129,11 @@ typedef struct VFIOPCIDevice {
#define VFIO_FEATURE_ENABLE_VGA (1 << VFIO_FEATURE_ENABLE_VGA_BIT)
#define VFIO_FEATURE_ENABLE_REQ_BIT 1
#define VFIO_FEATURE_ENABLE_REQ (1 << VFIO_FEATURE_ENABLE_REQ_BIT)
+#define VFIO_FEATURE_ENABLE_IGD_OPREGION_BIT 2
+#define VFIO_FEATURE_ENABLE_IGD_OPREGION \
+ (1 << VFIO_FEATURE_ENABLE_IGD_OPREGION_BIT)
int32_t bootindex;
+ uint32_t igd_gms;
uint8_t pm_cap;
bool has_vga;
bool pci_aer;
@@ -159,4 +164,7 @@ void vfio_setup_resetfn_quirk(VFIOPCIDevice *vdev);
int vfio_populate_vga(VFIOPCIDevice *vdev);
+int vfio_pci_igd_opregion_init(VFIOPCIDevice *vdev,
+ struct vfio_region_info *info);
+
#endif /* HW_VFIO_VFIO_PCI_H */
diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c
index 5914e85107..495e09fd4e 100644
--- a/hw/virtio/vhost-user.c
+++ b/hw/virtio/vhost-user.c
@@ -17,7 +17,6 @@
#include "sysemu/kvm.h"
#include "qemu/error-report.h"
#include "qemu/sockets.h"
-#include "exec/ram_addr.h"
#include "migration/migration.h"
#include <sys/ioctl.h>
@@ -247,18 +246,18 @@ static int vhost_user_set_mem_table(struct vhost_dev *dev,
for (i = 0; i < dev->mem->nregions; ++i) {
struct vhost_memory_region *reg = dev->mem->regions + i;
- ram_addr_t ram_addr;
+ ram_addr_t offset;
+ MemoryRegion *mr;
assert((uintptr_t)reg->userspace_addr == reg->userspace_addr);
- qemu_ram_addr_from_host((void *)(uintptr_t)reg->userspace_addr,
- &ram_addr);
- fd = qemu_get_ram_fd(ram_addr);
+ mr = memory_region_from_host((void *)(uintptr_t)reg->userspace_addr,
+ &offset);
+ fd = memory_region_get_fd(mr);
if (fd > 0) {
msg.payload.memory.regions[fd_num].userspace_addr = reg->userspace_addr;
msg.payload.memory.regions[fd_num].memory_size = reg->memory_size;
msg.payload.memory.regions[fd_num].guest_phys_addr = reg->guest_phys_addr;
- msg.payload.memory.regions[fd_num].mmap_offset = reg->userspace_addr -
- (uintptr_t) qemu_get_ram_block_host_ptr(ram_addr);
+ msg.payload.memory.regions[fd_num].mmap_offset = offset;
assert(fd_num < VHOST_MEMORY_MAX_NREGIONS);
fds[fd_num++] = fd;
}
@@ -616,17 +615,15 @@ static bool vhost_user_can_merge(struct vhost_dev *dev,
uint64_t start1, uint64_t size1,
uint64_t start2, uint64_t size2)
{
- ram_addr_t ram_addr;
+ ram_addr_t offset;
int mfd, rfd;
MemoryRegion *mr;
- mr = qemu_ram_addr_from_host((void *)(uintptr_t)start1, &ram_addr);
- assert(mr);
- mfd = qemu_get_ram_fd(ram_addr);
+ mr = memory_region_from_host((void *)(uintptr_t)start1, &offset);
+ mfd = memory_region_get_fd(mr);
- mr = qemu_ram_addr_from_host((void *)(uintptr_t)start2, &ram_addr);
- assert(mr);
- rfd = qemu_get_ram_fd(ram_addr);
+ mr = memory_region_from_host((void *)(uintptr_t)start2, &offset);
+ rfd = memory_region_get_fd(mr);
return mfd == rfd;
}