aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
Diffstat (limited to 'hw')
-rw-r--r--hw/9pfs/virtio-9p-local.c14
-rw-r--r--hw/ccid-card-passthru.c1
-rw-r--r--hw/eepro100.c10
-rw-r--r--hw/pci-hotplug.c3
-rw-r--r--hw/rtl8139.c14
-rw-r--r--hw/s390-virtio-bus.c5
-rw-r--r--hw/scsi-bus.c11
-rw-r--r--hw/scsi-generic.c5
-rw-r--r--hw/scsi.h2
-rw-r--r--hw/syborg_virtio.c4
-rw-r--r--hw/usb-bt.c3
-rw-r--r--hw/usb-bus.c29
-rw-r--r--hw/usb-ehci.c58
-rw-r--r--hw/usb-hub.c22
-rw-r--r--hw/usb-msd.c4
-rw-r--r--hw/virtio-blk.c7
-rw-r--r--hw/virtio-pci.c9
-rw-r--r--hw/virtio.c24
-rw-r--r--hw/virtio.h1
19 files changed, 142 insertions, 84 deletions
diff --git a/hw/9pfs/virtio-9p-local.c b/hw/9pfs/virtio-9p-local.c
index 7f1c089..99ef0cd 100644
--- a/hw/9pfs/virtio-9p-local.c
+++ b/hw/9pfs/virtio-9p-local.c
@@ -583,8 +583,7 @@ static int local_utimensat(FsContext *s, V9fsPath *fs_path,
char buffer[PATH_MAX];
char *path = fs_path->data;
- return qemu_utimensat(AT_FDCWD, rpath(s, path, buffer), buf,
- AT_SYMLINK_NOFOLLOW);
+ return qemu_utimens(rpath(s, path, buffer), buf);
}
static int local_remove(FsContext *ctx, const char *path)
@@ -694,6 +693,7 @@ static int local_ioc_getversion(FsContext *ctx, V9fsPath *path,
mode_t st_mode, uint64_t *st_gen)
{
int err;
+#ifdef FS_IOC_GETVERSION
V9fsFidOpenState fid_open;
/*
@@ -709,15 +709,22 @@ static int local_ioc_getversion(FsContext *ctx, V9fsPath *path,
}
err = ioctl(fid_open.fd, FS_IOC_GETVERSION, st_gen);
local_close(ctx, &fid_open);
+#else
+ err = -ENOTTY;
+#endif
return err;
}
static int local_init(FsContext *ctx)
{
- int err;
+ int err = 0;
struct statfs stbuf;
ctx->export_flags |= V9FS_PATHNAME_FSCONTEXT;
+#ifdef FS_IOC_GETVERSION
+ /*
+ * use ioc_getversion only if the iocl is definied
+ */
err = statfs(ctx->fs_root, &stbuf);
if (!err) {
switch (stbuf.f_type) {
@@ -729,6 +736,7 @@ static int local_init(FsContext *ctx)
break;
}
}
+#endif
return err;
}
diff --git a/hw/ccid-card-passthru.c b/hw/ccid-card-passthru.c
index 2cbc81b..9f51c6c 100644
--- a/hw/ccid-card-passthru.c
+++ b/hw/ccid-card-passthru.c
@@ -150,6 +150,7 @@ static void ccid_card_vscard_handle_message(PassthruState *card,
error_report("ATR size exceeds spec, ignoring");
ccid_card_vscard_send_error(card, scr_msg_header->reader_id,
VSC_GENERAL_ERROR);
+ break;
}
memcpy(card->atr, data, scr_msg_header->length);
card->atr_length = scr_msg_header->length;
diff --git a/hw/eepro100.c b/hw/eepro100.c
index 7d59e71..29ec5b4 100644
--- a/hw/eepro100.c
+++ b/hw/eepro100.c
@@ -976,7 +976,15 @@ static void eepro100_cu_command(EEPRO100State * s, uint8_t val)
case CU_STATSADDR:
/* Load dump counters address. */
s->statsaddr = e100_read_reg4(s, SCBPointer);
- TRACE(OTHER, logout("val=0x%02x (status address)\n", val));
+ TRACE(OTHER, logout("val=0x%02x (dump counters address)\n", val));
+ if (s->statsaddr & 3) {
+ /* Memory must be Dword aligned. */
+ logout("unaligned dump counters address\n");
+ /* Handling of misaligned addresses is undefined.
+ * Here we align the address by ignoring the lower bits. */
+ /* TODO: Test unaligned dump counter address on real hardware. */
+ s->statsaddr &= ~3;
+ }
break;
case CU_SHOWSTATS:
/* Dump statistical counters. */
diff --git a/hw/pci-hotplug.c b/hw/pci-hotplug.c
index b59be2a..12f61fe 100644
--- a/hw/pci-hotplug.c
+++ b/hw/pci-hotplug.c
@@ -91,7 +91,8 @@ static int scsi_hot_add(Monitor *mon, DeviceState *adapter,
*/
dinfo->unit = qemu_opt_get_number(dinfo->opts, "unit", -1);
dinfo->bus = scsibus->busnr;
- scsidev = scsi_bus_legacy_add_drive(scsibus, dinfo->bdrv, dinfo->unit, false);
+ scsidev = scsi_bus_legacy_add_drive(scsibus, dinfo->bdrv, dinfo->unit,
+ false, -1);
if (!scsidev) {
return -1;
}
diff --git a/hw/rtl8139.c b/hw/rtl8139.c
index 4c37993..aa8ed0a 100644
--- a/hw/rtl8139.c
+++ b/hw/rtl8139.c
@@ -1971,7 +1971,7 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s)
cplus_tx_ring_desc += 16 * descriptor;
DPRINTF("+++ C+ mode reading TX descriptor %d from host memory at "
- "%08x0x%08x = 0x"DMA_ADDR_FMT"\n", descriptor, s->TxAddr[1],
+ "%08x %08x = 0x"DMA_ADDR_FMT"\n", descriptor, s->TxAddr[1],
s->TxAddr[0], cplus_tx_ring_desc);
uint32_t val, txdw0,txdw1,txbufLO,txbufHI;
@@ -2713,8 +2713,6 @@ static void rtl8139_io_writeb(void *opaque, uint8_t addr, uint32_t val)
{
RTL8139State *s = opaque;
- addr &= 0xff;
-
switch (addr)
{
case MAC0 ... MAC0+5:
@@ -2800,8 +2798,6 @@ static void rtl8139_io_writew(void *opaque, uint8_t addr, uint32_t val)
{
RTL8139State *s = opaque;
- addr &= 0xfe;
-
switch (addr)
{
case IntrMask:
@@ -2900,8 +2896,6 @@ static void rtl8139_io_writel(void *opaque, uint8_t addr, uint32_t val)
{
RTL8139State *s = opaque;
- addr &= 0xfc;
-
switch (addr)
{
case RxMissed:
@@ -2969,8 +2963,6 @@ static uint32_t rtl8139_io_readb(void *opaque, uint8_t addr)
RTL8139State *s = opaque;
int ret;
- addr &= 0xff;
-
switch (addr)
{
case MAC0 ... MAC0+5:
@@ -3043,8 +3035,6 @@ static uint32_t rtl8139_io_readw(void *opaque, uint8_t addr)
RTL8139State *s = opaque;
uint32_t ret;
- addr &= 0xfe; /* mask lower bit */
-
switch (addr)
{
case IntrMask:
@@ -3120,8 +3110,6 @@ static uint32_t rtl8139_io_readl(void *opaque, uint8_t addr)
RTL8139State *s = opaque;
uint32_t ret;
- addr &= 0xfc; /* also mask low 2 bits */
-
switch (addr)
{
case RxMissed:
diff --git a/hw/s390-virtio-bus.c b/hw/s390-virtio-bus.c
index 0ce6406..c4b9a99 100644
--- a/hw/s390-virtio-bus.c
+++ b/hw/s390-virtio-bus.c
@@ -254,10 +254,7 @@ void s390_virtio_device_update_status(VirtIOS390Device *dev)
/* Update guest supported feature bitmap */
features = bswap32(ldl_be_phys(dev->feat_offs));
- if (vdev->set_features) {
- vdev->set_features(vdev, features);
- }
- vdev->guest_features = features;
+ virtio_set_features(vdev, features);
}
VirtIOS390Device *s390_virtio_bus_console(VirtIOS390Bus *bus)
diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c
index 3a2a7bb..64e709e 100644
--- a/hw/scsi-bus.c
+++ b/hw/scsi-bus.c
@@ -162,7 +162,7 @@ void scsi_qdev_register(SCSIDeviceInfo *info)
/* handle legacy '-drive if=scsi,...' cmd line args */
SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockDriverState *bdrv,
- int unit, bool removable)
+ int unit, bool removable, int bootindex)
{
const char *driver;
DeviceState *dev;
@@ -170,6 +170,9 @@ SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockDriverState *bdrv,
driver = bdrv_is_sg(bdrv) ? "scsi-generic" : "scsi-disk";
dev = qdev_create(&bus->qbus, driver);
qdev_prop_set_uint32(dev, "scsi-id", unit);
+ if (bootindex >= 0) {
+ qdev_prop_set_int32(dev, "bootindex", bootindex);
+ }
if (qdev_prop_exists(dev, "removable")) {
qdev_prop_set_bit(dev, "removable", removable);
}
@@ -195,7 +198,7 @@ int scsi_bus_legacy_handle_cmdline(SCSIBus *bus)
continue;
}
qemu_opts_loc_restore(dinfo->opts);
- if (!scsi_bus_legacy_add_drive(bus, dinfo->bdrv, unit, false)) {
+ if (!scsi_bus_legacy_add_drive(bus, dinfo->bdrv, unit, false, -1)) {
res = -1;
break;
}
@@ -1367,8 +1370,8 @@ static char *scsibus_get_fw_dev_path(DeviceState *dev)
SCSIDevice *d = DO_UPCAST(SCSIDevice, qdev, dev);
char path[100];
- snprintf(path, sizeof(path), "%s@%d,%d,%d", qdev_fw_name(dev),
- d->channel, d->id, d->lun);
+ snprintf(path, sizeof(path), "channel@%x/%s@%x,%x", d->channel,
+ qdev_fw_name(dev), d->id, d->lun);
return strdup(path);
}
diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c
index 9594cc1..e62044f 100644
--- a/hw/scsi-generic.c
+++ b/hw/scsi-generic.c
@@ -413,6 +413,10 @@ static int scsi_generic_initfn(SCSIDevice *s)
/* define device state */
s->type = scsiid.scsi_type;
DPRINTF("device type %d\n", s->type);
+ if (s->type == TYPE_DISK || s->type == TYPE_ROM) {
+ add_boot_device_path(s->conf.bootindex, &s->qdev, NULL);
+ }
+
switch (s->type) {
case TYPE_TAPE:
s->blocksize = get_stream_blocksize(s->conf.bs);
@@ -459,6 +463,7 @@ static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun,
static SCSIDeviceInfo scsi_generic_info = {
.qdev.name = "scsi-generic",
+ .qdev.fw_name = "disk",
.qdev.desc = "pass through generic scsi device (/dev/sg*)",
.qdev.size = sizeof(SCSIDevice),
.qdev.reset = scsi_generic_reset,
diff --git a/hw/scsi.h b/hw/scsi.h
index 61c64d5..ab6e952 100644
--- a/hw/scsi.h
+++ b/hw/scsi.h
@@ -128,7 +128,7 @@ static inline SCSIBus *scsi_bus_from_device(SCSIDevice *d)
}
SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockDriverState *bdrv,
- int unit, bool removable);
+ int unit, bool removable, int bootindex);
int scsi_bus_legacy_handle_cmdline(SCSIBus *bus);
/*
diff --git a/hw/syborg_virtio.c b/hw/syborg_virtio.c
index 3bb10b5..96a0ee6 100644
--- a/hw/syborg_virtio.c
+++ b/hw/syborg_virtio.c
@@ -132,9 +132,7 @@ static void syborg_virtio_writel(void *opaque, target_phys_addr_t offset,
}
switch (offset >> 2) {
case SYBORG_VIRTIO_GUEST_FEATURES:
- if (vdev->set_features)
- vdev->set_features(vdev, value);
- vdev->guest_features = value;
+ virtio_set_features(vdev, value);
break;
case SYBORG_VIRTIO_QUEUE_BASE:
if (value == 0)
diff --git a/hw/usb-bt.c b/hw/usb-bt.c
index 529fa33..f30eec1 100644
--- a/hw/usb-bt.c
+++ b/hw/usb-bt.c
@@ -528,6 +528,9 @@ USBDevice *usb_bt_init(HCIInfo *hci)
if (!hci)
return NULL;
dev = usb_create_simple(NULL /* FIXME */, "usb-bt-dongle");
+ if (!dev) {
+ return NULL;
+ }
s = DO_UPCAST(struct USBBtState, dev, dev);
s->dev.opaque = s;
diff --git a/hw/usb-bus.c b/hw/usb-bus.c
index 93f640d..8cafb76 100644
--- a/hw/usb-bus.c
+++ b/hw/usb-bus.c
@@ -9,6 +9,7 @@ static void usb_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent);
static char *usb_get_dev_path(DeviceState *dev);
static char *usb_get_fw_dev_path(DeviceState *qdev);
+static int usb_qdev_exit(DeviceState *qdev);
static struct BusInfo usb_bus_info = {
.name = "USB",
@@ -75,12 +76,23 @@ static int usb_qdev_init(DeviceState *qdev, DeviceInfo *base)
dev->auto_attach = 1;
QLIST_INIT(&dev->strings);
rc = usb_claim_port(dev);
- if (rc == 0) {
- rc = dev->info->init(dev);
+ if (rc != 0) {
+ goto err;
}
- if (rc == 0 && dev->auto_attach) {
+ rc = dev->info->init(dev);
+ if (rc != 0) {
+ goto err;
+ }
+ if (dev->auto_attach) {
rc = usb_device_attach(dev);
+ if (rc != 0) {
+ goto err;
+ }
}
+ return 0;
+
+err:
+ usb_qdev_exit(qdev);
return rc;
}
@@ -139,10 +151,17 @@ USBDevice *usb_create(USBBus *bus, const char *name)
USBDevice *usb_create_simple(USBBus *bus, const char *name)
{
USBDevice *dev = usb_create(bus, name);
+ int rc;
+
if (!dev) {
- hw_error("Failed to create USB device '%s'\n", name);
+ error_report("Failed to create USB device '%s'\n", name);
+ return NULL;
+ }
+ rc = qdev_init(&dev->qdev);
+ if (rc < 0) {
+ error_report("Failed to initialize USB device '%s'\n", name);
+ return NULL;
}
- qdev_init_nofail(&dev->qdev);
return dev;
}
diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c
index 3eea94d..a946e1d 100644
--- a/hw/usb-ehci.c
+++ b/hw/usb-ehci.c
@@ -437,37 +437,39 @@ struct EHCIState {
} while(0)
static const char *ehci_state_names[] = {
- [ EST_INACTIVE ] = "INACTIVE",
- [ EST_ACTIVE ] = "ACTIVE",
- [ EST_EXECUTING ] = "EXECUTING",
- [ EST_SLEEPING ] = "SLEEPING",
- [ EST_WAITLISTHEAD ] = "WAITLISTHEAD",
- [ EST_FETCHENTRY ] = "FETCH ENTRY",
- [ EST_FETCHQH ] = "FETCH QH",
- [ EST_FETCHITD ] = "FETCH ITD",
- [ EST_ADVANCEQUEUE ] = "ADVANCEQUEUE",
- [ EST_FETCHQTD ] = "FETCH QTD",
- [ EST_EXECUTE ] = "EXECUTE",
- [ EST_WRITEBACK ] = "WRITEBACK",
- [ EST_HORIZONTALQH ] = "HORIZONTALQH",
+ [EST_INACTIVE] = "INACTIVE",
+ [EST_ACTIVE] = "ACTIVE",
+ [EST_EXECUTING] = "EXECUTING",
+ [EST_SLEEPING] = "SLEEPING",
+ [EST_WAITLISTHEAD] = "WAITLISTHEAD",
+ [EST_FETCHENTRY] = "FETCH ENTRY",
+ [EST_FETCHQH] = "FETCH QH",
+ [EST_FETCHITD] = "FETCH ITD",
+ [EST_ADVANCEQUEUE] = "ADVANCEQUEUE",
+ [EST_FETCHQTD] = "FETCH QTD",
+ [EST_EXECUTE] = "EXECUTE",
+ [EST_WRITEBACK] = "WRITEBACK",
+ [EST_HORIZONTALQH] = "HORIZONTALQH",
};
static const char *ehci_mmio_names[] = {
- [ CAPLENGTH ] = "CAPLENGTH",
- [ HCIVERSION ] = "HCIVERSION",
- [ HCSPARAMS ] = "HCSPARAMS",
- [ HCCPARAMS ] = "HCCPARAMS",
- [ USBCMD ] = "USBCMD",
- [ USBSTS ] = "USBSTS",
- [ USBINTR ] = "USBINTR",
- [ FRINDEX ] = "FRINDEX",
- [ PERIODICLISTBASE ] = "P-LIST BASE",
- [ ASYNCLISTADDR ] = "A-LIST ADDR",
- [ PORTSC_BEGIN ] = "PORTSC #0",
- [ PORTSC_BEGIN + 4] = "PORTSC #1",
- [ PORTSC_BEGIN + 8] = "PORTSC #2",
- [ PORTSC_BEGIN + 12] = "PORTSC #3",
- [ CONFIGFLAG ] = "CONFIGFLAG",
+ [CAPLENGTH] = "CAPLENGTH",
+ [HCIVERSION] = "HCIVERSION",
+ [HCSPARAMS] = "HCSPARAMS",
+ [HCCPARAMS] = "HCCPARAMS",
+ [USBCMD] = "USBCMD",
+ [USBSTS] = "USBSTS",
+ [USBINTR] = "USBINTR",
+ [FRINDEX] = "FRINDEX",
+ [PERIODICLISTBASE] = "P-LIST BASE",
+ [ASYNCLISTADDR] = "A-LIST ADDR",
+ [PORTSC_BEGIN] = "PORTSC #0",
+ [PORTSC_BEGIN + 4] = "PORTSC #1",
+ [PORTSC_BEGIN + 8] = "PORTSC #2",
+ [PORTSC_BEGIN + 12] = "PORTSC #3",
+ [PORTSC_BEGIN + 16] = "PORTSC #4",
+ [PORTSC_BEGIN + 20] = "PORTSC #5",
+ [CONFIGFLAG] = "CONFIGFLAG",
};
static const char *nr2str(const char **n, size_t len, uint32_t nr)
diff --git a/hw/usb-hub.c b/hw/usb-hub.c
index 3eb0f1a..e195937 100644
--- a/hw/usb-hub.c
+++ b/hw/usb-hub.c
@@ -171,6 +171,8 @@ static void usb_hub_detach(USBPort *port1)
USBHubState *s = port1->opaque;
USBHubPort *port = &s->ports[port1->index];
+ usb_wakeup(&s->dev);
+
/* Let upstream know the device on this port is gone */
s->dev.port->ops->child_detach(s->dev.port, port1->dev);
@@ -220,7 +222,22 @@ static void usb_hub_complete(USBPort *port, USBPacket *packet)
static void usb_hub_handle_reset(USBDevice *dev)
{
- /* XXX: do it */
+ USBHubState *s = DO_UPCAST(USBHubState, dev, dev);
+ USBHubPort *port;
+ int i;
+
+ for (i = 0; i < NUM_PORTS; i++) {
+ port = s->ports + i;
+ port->wPortStatus = PORT_STAT_POWER;
+ port->wPortChange = 0;
+ if (port->port.dev && port->port.dev->attached) {
+ port->wPortStatus |= PORT_STAT_CONNECTION;
+ port->wPortChange |= PORT_STAT_C_CONNECTION;
+ if (port->port.dev->speed == USB_SPEED_LOW) {
+ port->wPortStatus |= PORT_STAT_LOW_SPEED;
+ }
+ }
+ }
}
static int usb_hub_handle_control(USBDevice *dev, USBPacket *p,
@@ -495,9 +512,8 @@ static int usb_hub_initfn(USBDevice *dev)
&port->port, s, i, &usb_hub_port_ops,
USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL);
usb_port_location(&port->port, dev->port, i+1);
- port->wPortStatus = PORT_STAT_POWER;
- port->wPortChange = 0;
}
+ usb_hub_handle_reset(dev);
return 0;
}
diff --git a/hw/usb-msd.c b/hw/usb-msd.c
index 68e3756..4c06950 100644
--- a/hw/usb-msd.c
+++ b/hw/usb-msd.c
@@ -546,7 +546,8 @@ static int usb_msd_initfn(USBDevice *dev)
usb_desc_init(dev);
scsi_bus_new(&s->bus, &s->dev.qdev, &usb_msd_scsi_info);
- s->scsi_dev = scsi_bus_legacy_add_drive(&s->bus, bs, 0, !!s->removable);
+ s->scsi_dev = scsi_bus_legacy_add_drive(&s->bus, bs, 0, !!s->removable,
+ s->conf.bootindex);
if (!s->scsi_dev) {
return -1;
}
@@ -562,7 +563,6 @@ static int usb_msd_initfn(USBDevice *dev)
}
}
- add_boot_device_path(s->conf.bootindex, &dev->qdev, "/disk@0,0");
return 0;
}
diff --git a/hw/virtio-blk.c b/hw/virtio-blk.c
index 19e89e7..d6d1f87 100644
--- a/hw/virtio-blk.c
+++ b/hw/virtio-blk.c
@@ -485,6 +485,7 @@ static void virtio_blk_update_config(VirtIODevice *vdev, uint8_t *config)
struct virtio_blk_config blkcfg;
uint64_t capacity;
int cylinders, heads, secs;
+ int blk_size = s->conf->logical_block_size;
bdrv_get_geometry(s->bs, &capacity);
bdrv_get_geometry_hint(s->bs, &cylinders, &heads, &secs);
@@ -492,14 +493,14 @@ static void virtio_blk_update_config(VirtIODevice *vdev, uint8_t *config)
stq_raw(&blkcfg.capacity, capacity);
stl_raw(&blkcfg.seg_max, 128 - 2);
stw_raw(&blkcfg.cylinders, cylinders);
+ stl_raw(&blkcfg.blk_size, blk_size);
+ stw_raw(&blkcfg.min_io_size, s->conf->min_io_size / blk_size);
+ stw_raw(&blkcfg.opt_io_size, s->conf->opt_io_size / blk_size);
blkcfg.heads = heads;
blkcfg.sectors = secs & ~s->sector_mask;
- blkcfg.blk_size = s->conf->logical_block_size;
blkcfg.size_max = 0;
blkcfg.physical_block_exp = get_physical_block_exp(s->conf);
blkcfg.alignment_offset = 0;
- blkcfg.min_io_size = s->conf->min_io_size / blkcfg.blk_size;
- blkcfg.opt_io_size = s->conf->opt_io_size / blkcfg.blk_size;
memcpy(config, &blkcfg, sizeof(struct virtio_blk_config));
}
diff --git a/hw/virtio-pci.c b/hw/virtio-pci.c
index ca5923c..64c6a94 100644
--- a/hw/virtio-pci.c
+++ b/hw/virtio-pci.c
@@ -285,14 +285,9 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val)
case VIRTIO_PCI_GUEST_FEATURES:
/* Guest does not negotiate properly? We have to assume nothing. */
if (val & (1 << VIRTIO_F_BAD_FEATURE)) {
- if (vdev->bad_features)
- val = proxy->host_features & vdev->bad_features(vdev);
- else
- val = 0;
+ val = vdev->bad_features ? vdev->bad_features(vdev) : 0;
}
- if (vdev->set_features)
- vdev->set_features(vdev, val);
- vdev->guest_features = val;
+ virtio_set_features(vdev, val);
break;
case VIRTIO_PCI_QUEUE_PFN:
pa = (target_phys_addr_t)val << VIRTIO_PCI_QUEUE_ADDR_SHIFT;
diff --git a/hw/virtio.c b/hw/virtio.c
index 7011b5b..81ecc40 100644
--- a/hw/virtio.c
+++ b/hw/virtio.c
@@ -763,12 +763,25 @@ void virtio_save(VirtIODevice *vdev, QEMUFile *f)
}
}
+int virtio_set_features(VirtIODevice *vdev, uint32_t val)
+{
+ uint32_t supported_features =
+ vdev->binding->get_features(vdev->binding_opaque);
+ bool bad = (val & ~supported_features) != 0;
+
+ val &= supported_features;
+ if (vdev->set_features) {
+ vdev->set_features(vdev, val);
+ }
+ vdev->guest_features = val;
+ return bad ? -1 : 0;
+}
+
int virtio_load(VirtIODevice *vdev, QEMUFile *f)
{
int num, i, ret;
uint32_t features;
- uint32_t supported_features =
- vdev->binding->get_features(vdev->binding_opaque);
+ uint32_t supported_features;
if (vdev->binding->load_config) {
ret = vdev->binding->load_config(vdev->binding_opaque, f);
@@ -780,14 +793,13 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f)
qemu_get_8s(f, &vdev->isr);
qemu_get_be16s(f, &vdev->queue_sel);
qemu_get_be32s(f, &features);
- if (features & ~supported_features) {
+
+ if (virtio_set_features(vdev, features) < 0) {
+ supported_features = vdev->binding->get_features(vdev->binding_opaque);
error_report("Features 0x%x unsupported. Allowed features: 0x%x",
features, supported_features);
return -1;
}
- if (vdev->set_features)
- vdev->set_features(vdev, features);
- vdev->guest_features = features;
vdev->config_len = qemu_get_be32(f);
qemu_get_buffer(f, vdev->config, vdev->config_len);
diff --git a/hw/virtio.h b/hw/virtio.h
index 2d18209..25f5564 100644
--- a/hw/virtio.h
+++ b/hw/virtio.h
@@ -185,6 +185,7 @@ void virtio_queue_set_vector(VirtIODevice *vdev, int n, uint16_t vector);
void virtio_set_status(VirtIODevice *vdev, uint8_t val);
void virtio_reset(void *opaque);
void virtio_update_irq(VirtIODevice *vdev);
+int virtio_set_features(VirtIODevice *vdev, uint32_t val);
void virtio_bind_device(VirtIODevice *vdev, const VirtIOBindings *binding,
void *opaque);