summaryrefslogtreecommitdiffstats
path: root/hw
diff options
context:
space:
mode:
Diffstat (limited to 'hw')
-rw-r--r--hw/9pfs/9p-synth.c12
-rw-r--r--hw/char/Kconfig3
-rw-r--r--hw/char/goldfish_tty.c285
-rw-r--r--hw/char/meson.build2
-rw-r--r--hw/char/trace-events10
-rw-r--r--hw/intc/Kconfig6
-rw-r--r--hw/intc/goldfish_pic.c219
-rw-r--r--hw/intc/m68k_irqc.c119
-rw-r--r--hw/intc/meson.build2
-rw-r--r--hw/intc/trace-events8
-rw-r--r--hw/isa/Kconfig1
-rw-r--r--hw/m68k/Kconfig9
-rw-r--r--hw/m68k/meson.build1
-rw-r--r--hw/m68k/virt.c313
-rw-r--r--hw/mem/meson.build1
-rw-r--r--hw/mem/sparse-mem.c151
-rw-r--r--hw/misc/Kconfig3
-rw-r--r--hw/misc/meson.build3
-rw-r--r--hw/misc/trace-events7
-rw-r--r--hw/misc/virt_ctrl.c151
-rw-r--r--hw/ppc/spapr_iommu.c5
-rw-r--r--hw/s390x/s390-pci-vfio.c9
-rw-r--r--hw/timer/i8254.c2
-rw-r--r--hw/usb/Kconfig13
-rw-r--r--hw/usb/bus.c39
-rw-r--r--hw/usb/dev-audio.c1
-rw-r--r--hw/usb/dev-serial.c2
-rw-r--r--hw/usb/dev-storage-bot.c63
-rw-r--r--hw/usb/dev-storage-classic.c156
-rw-r--r--hw/usb/dev-storage.c226
-rw-r--r--hw/usb/hcd-uhci.c83
-rw-r--r--hw/usb/hcd-uhci.h93
-rw-r--r--hw/usb/meson.build5
-rw-r--r--hw/usb/u2f.c1
-rw-r--r--hw/usb/vt82c686-uhci-pci.c43
-rw-r--r--hw/vfio/common.c103
-rw-r--r--hw/vfio/migration.c63
-rw-r--r--hw/vfio/pci-quirks.c14
-rw-r--r--hw/vfio/pci.c24
-rw-r--r--hw/vfio/pci.h2
-rw-r--r--hw/vfio/trace-events2
41 files changed, 1810 insertions, 445 deletions
diff --git a/hw/9pfs/9p-synth.c b/hw/9pfs/9p-synth.c
index 7eb210ffa8..473ef914b0 100644
--- a/hw/9pfs/9p-synth.c
+++ b/hw/9pfs/9p-synth.c
@@ -79,11 +79,11 @@ int qemu_v9fs_synth_mkdir(V9fsSynthNode *parent, int mode,
if (!parent) {
parent = &synth_root;
}
- qemu_mutex_lock(&synth_mutex);
+ QEMU_LOCK_GUARD(&synth_mutex);
QLIST_FOREACH(tmp, &parent->child, sibling) {
if (!strcmp(tmp->name, name)) {
ret = EEXIST;
- goto err_out;
+ return ret;
}
}
/* Add the name */
@@ -94,8 +94,6 @@ int qemu_v9fs_synth_mkdir(V9fsSynthNode *parent, int mode,
node->attr, node->attr->inode);
*result = node;
ret = 0;
-err_out:
- qemu_mutex_unlock(&synth_mutex);
return ret;
}
@@ -116,11 +114,11 @@ int qemu_v9fs_synth_add_file(V9fsSynthNode *parent, int mode,
parent = &synth_root;
}
- qemu_mutex_lock(&synth_mutex);
+ QEMU_LOCK_GUARD(&synth_mutex);
QLIST_FOREACH(tmp, &parent->child, sibling) {
if (!strcmp(tmp->name, name)) {
ret = EEXIST;
- goto err_out;
+ return ret;
}
}
/* Add file type and remove write bits */
@@ -136,8 +134,6 @@ int qemu_v9fs_synth_add_file(V9fsSynthNode *parent, int mode,
pstrcpy(node->name, sizeof(node->name), name);
QLIST_INSERT_HEAD_RCU(&parent->child, node, sibling);
ret = 0;
-err_out:
- qemu_mutex_unlock(&synth_mutex);
return ret;
}
diff --git a/hw/char/Kconfig b/hw/char/Kconfig
index f6f4fffd1b..4cf36ac637 100644
--- a/hw/char/Kconfig
+++ b/hw/char/Kconfig
@@ -64,3 +64,6 @@ config MCHP_PFSOC_MMUART
config SIFIVE_UART
bool
+
+config GOLDFISH_TTY
+ bool
diff --git a/hw/char/goldfish_tty.c b/hw/char/goldfish_tty.c
new file mode 100644
index 0000000000..8365a18761
--- /dev/null
+++ b/hw/char/goldfish_tty.c
@@ -0,0 +1,285 @@
+/*
+ * SPDX-License-Identifer: GPL-2.0-or-later
+ *
+ * Goldfish TTY
+ *
+ * (c) 2020 Laurent Vivier <laurent@vivier.eu>
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "hw/irq.h"
+#include "hw/qdev-properties-system.h"
+#include "hw/sysbus.h"
+#include "migration/vmstate.h"
+#include "chardev/char-fe.h"
+#include "qemu/log.h"
+#include "trace.h"
+#include "exec/address-spaces.h"
+#include "hw/char/goldfish_tty.h"
+
+#define GOLDFISH_TTY_VERSION 1
+
+/* registers */
+
+enum {
+ REG_PUT_CHAR = 0x00,
+ REG_BYTES_READY = 0x04,
+ REG_CMD = 0x08,
+ REG_DATA_PTR = 0x10,
+ REG_DATA_LEN = 0x14,
+ REG_DATA_PTR_HIGH = 0x18,
+ REG_VERSION = 0x20,
+};
+
+/* commands */
+
+enum {
+ CMD_INT_DISABLE = 0x00,
+ CMD_INT_ENABLE = 0x01,
+ CMD_WRITE_BUFFER = 0x02,
+ CMD_READ_BUFFER = 0x03,
+};
+
+static uint64_t goldfish_tty_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ GoldfishTTYState *s = opaque;
+ uint64_t value = 0;
+
+ switch (addr) {
+ case REG_BYTES_READY:
+ value = fifo8_num_used(&s->rx_fifo);
+ break;
+ case REG_VERSION:
+ value = GOLDFISH_TTY_VERSION;
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP,
+ "%s: unimplemented register read 0x%02"HWADDR_PRIx"\n",
+ __func__, addr);
+ break;
+ }
+
+ trace_goldfish_tty_read(s, addr, size, value);
+
+ return value;
+}
+
+static void goldfish_tty_cmd(GoldfishTTYState *s, uint32_t cmd)
+{
+ uint32_t to_copy;
+ uint8_t *buf;
+ uint8_t data_out[GOLFISH_TTY_BUFFER_SIZE];
+ int len;
+ uint64_t ptr;
+
+ switch (cmd) {
+ case CMD_INT_DISABLE:
+ if (s->int_enabled) {
+ if (!fifo8_is_empty(&s->rx_fifo)) {
+ qemu_set_irq(s->irq, 0);
+ }
+ s->int_enabled = false;
+ }
+ break;
+ case CMD_INT_ENABLE:
+ if (!s->int_enabled) {
+ if (!fifo8_is_empty(&s->rx_fifo)) {
+ qemu_set_irq(s->irq, 1);
+ }
+ s->int_enabled = true;
+ }
+ break;
+ case CMD_WRITE_BUFFER:
+ len = s->data_len;
+ ptr = s->data_ptr;
+ while (len) {
+ to_copy = MIN(GOLFISH_TTY_BUFFER_SIZE, len);
+
+ address_space_rw(&address_space_memory, ptr,
+ MEMTXATTRS_UNSPECIFIED, data_out, to_copy, 0);
+ qemu_chr_fe_write_all(&s->chr, data_out, to_copy);
+
+ len -= to_copy;
+ ptr += to_copy;
+ }
+ break;
+ case CMD_READ_BUFFER:
+ len = s->data_len;
+ ptr = s->data_ptr;
+ while (len && !fifo8_is_empty(&s->rx_fifo)) {
+ buf = (uint8_t *)fifo8_pop_buf(&s->rx_fifo, len, &to_copy);
+ address_space_rw(&address_space_memory, ptr,
+ MEMTXATTRS_UNSPECIFIED, buf, to_copy, 1);
+
+ len -= to_copy;
+ ptr += to_copy;
+ }
+ if (s->int_enabled && fifo8_is_empty(&s->rx_fifo)) {
+ qemu_set_irq(s->irq, 0);
+ }
+ break;
+ }
+}
+
+static void goldfish_tty_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ GoldfishTTYState *s = opaque;
+ unsigned char c;
+
+ trace_goldfish_tty_write(s, addr, size, value);
+
+ switch (addr) {
+ case REG_PUT_CHAR:
+ c = value;
+ qemu_chr_fe_write_all(&s->chr, &c, sizeof(c));
+ break;
+ case REG_CMD:
+ goldfish_tty_cmd(s, value);
+ break;
+ case REG_DATA_PTR:
+ s->data_ptr = value;
+ break;
+ case REG_DATA_PTR_HIGH:
+ s->data_ptr = deposit64(s->data_ptr, 32, 32, value);
+ break;
+ case REG_DATA_LEN:
+ s->data_len = value;
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP,
+ "%s: unimplemented register write 0x%02"HWADDR_PRIx"\n",
+ __func__, addr);
+ break;
+ }
+}
+
+static const MemoryRegionOps goldfish_tty_ops = {
+ .read = goldfish_tty_read,
+ .write = goldfish_tty_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid.max_access_size = 4,
+ .impl.max_access_size = 4,
+ .impl.min_access_size = 4,
+};
+
+static int goldfish_tty_can_receive(void *opaque)
+{
+ GoldfishTTYState *s = opaque;
+ int available = fifo8_num_free(&s->rx_fifo);
+
+ trace_goldfish_tty_can_receive(s, available);
+
+ return available;
+}
+
+static void goldfish_tty_receive(void *opaque, const uint8_t *buffer, int size)
+{
+ GoldfishTTYState *s = opaque;
+
+ trace_goldfish_tty_receive(s, size);
+
+ g_assert(size <= fifo8_num_free(&s->rx_fifo));
+
+ fifo8_push_all(&s->rx_fifo, buffer, size);
+
+ if (s->int_enabled && !fifo8_is_empty(&s->rx_fifo)) {
+ qemu_set_irq(s->irq, 1);
+ }
+}
+
+static void goldfish_tty_reset(DeviceState *dev)
+{
+ GoldfishTTYState *s = GOLDFISH_TTY(dev);
+
+ trace_goldfish_tty_reset(s);
+
+ fifo8_reset(&s->rx_fifo);
+ s->int_enabled = false;
+ s->data_ptr = 0;
+ s->data_len = 0;
+}
+
+static void goldfish_tty_realize(DeviceState *dev, Error **errp)
+{
+ GoldfishTTYState *s = GOLDFISH_TTY(dev);
+
+ trace_goldfish_tty_realize(s);
+
+ fifo8_create(&s->rx_fifo, GOLFISH_TTY_BUFFER_SIZE);
+ memory_region_init_io(&s->iomem, OBJECT(s), &goldfish_tty_ops, s,
+ "goldfish_tty", 0x24);
+
+ if (qemu_chr_fe_backend_connected(&s->chr)) {
+ qemu_chr_fe_set_handlers(&s->chr, goldfish_tty_can_receive,
+ goldfish_tty_receive, NULL, NULL,
+ s, NULL, true);
+ }
+}
+
+static void goldfish_tty_unrealize(DeviceState *dev)
+{
+ GoldfishTTYState *s = GOLDFISH_TTY(dev);
+
+ trace_goldfish_tty_unrealize(s);
+
+ fifo8_destroy(&s->rx_fifo);
+}
+
+static const VMStateDescription vmstate_goldfish_tty = {
+ .name = "goldfish_tty",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(data_len, GoldfishTTYState),
+ VMSTATE_UINT64(data_ptr, GoldfishTTYState),
+ VMSTATE_BOOL(int_enabled, GoldfishTTYState),
+ VMSTATE_FIFO8(rx_fifo, GoldfishTTYState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static Property goldfish_tty_properties[] = {
+ DEFINE_PROP_CHR("chardev", GoldfishTTYState, chr),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void goldfish_tty_instance_init(Object *obj)
+{
+ SysBusDevice *dev = SYS_BUS_DEVICE(obj);
+ GoldfishTTYState *s = GOLDFISH_TTY(obj);
+
+ trace_goldfish_tty_instance_init(s);
+
+ sysbus_init_mmio(dev, &s->iomem);
+ sysbus_init_irq(dev, &s->irq);
+}
+
+static void goldfish_tty_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+
+ device_class_set_props(dc, goldfish_tty_properties);
+ dc->reset = goldfish_tty_reset;
+ dc->realize = goldfish_tty_realize;
+ dc->unrealize = goldfish_tty_unrealize;
+ dc->vmsd = &vmstate_goldfish_tty;
+ set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
+}
+
+static const TypeInfo goldfish_tty_info = {
+ .name = TYPE_GOLDFISH_TTY,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .class_init = goldfish_tty_class_init,
+ .instance_init = goldfish_tty_instance_init,
+ .instance_size = sizeof(GoldfishTTYState),
+};
+
+static void goldfish_tty_register_types(void)
+{
+ type_register_static(&goldfish_tty_info);
+}
+
+type_init(goldfish_tty_register_types)
diff --git a/hw/char/meson.build b/hw/char/meson.build
index 7ba38dbd96..da5bb8b762 100644
--- a/hw/char/meson.build
+++ b/hw/char/meson.build
@@ -39,3 +39,5 @@ specific_ss.add(when: 'CONFIG_HTIF', if_true: files('riscv_htif.c'))
specific_ss.add(when: 'CONFIG_TERMINAL3270', if_true: files('terminal3270.c'))
specific_ss.add(when: 'CONFIG_VIRTIO', if_true: files('virtio-serial-bus.c'))
specific_ss.add(when: 'CONFIG_PSERIES', if_true: files('spapr_vty.c'))
+
+specific_ss.add(when: 'CONFIG_GOLDFISH_TTY', if_true: files('goldfish_tty.c'))
diff --git a/hw/char/trace-events b/hw/char/trace-events
index 81026f6612..76d52938ea 100644
--- a/hw/char/trace-events
+++ b/hw/char/trace-events
@@ -20,6 +20,16 @@ virtio_console_flush_buf(unsigned int port, size_t len, ssize_t ret) "port %u, i
virtio_console_chr_read(unsigned int port, int size) "port %u, size %d"
virtio_console_chr_event(unsigned int port, int event) "port %u, event %d"
+# goldfish_tty.c
+goldfish_tty_read(void *dev, unsigned int addr, unsigned int size, uint64_t value) "tty: %p reg: 0x%02x size: %d value: 0x%"PRIx64
+goldfish_tty_write(void *dev, unsigned int addr, unsigned int size, uint64_t value) "tty: %p reg: 0x%02x size: %d value: 0x%"PRIx64
+goldfish_tty_can_receive(void *dev, unsigned int available) "tty: %p available: %u"
+goldfish_tty_receive(void *dev, unsigned int size) "tty: %p size: %u"
+goldfish_tty_reset(void *dev) "tty: %p"
+goldfish_tty_realize(void *dev) "tty: %p"
+goldfish_tty_unrealize(void *dev) "tty: %p"
+goldfish_tty_instance_init(void *dev) "tty: %p"
+
# grlib_apbuart.c
grlib_apbuart_event(int event) "event:%d"
grlib_apbuart_writel_unknown(uint64_t addr, uint32_t value) "addr 0x%"PRIx64" value 0x%x"
diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig
index 66bf0b90b4..f4694088a4 100644
--- a/hw/intc/Kconfig
+++ b/hw/intc/Kconfig
@@ -67,3 +67,9 @@ config SIFIVE_CLINT
config SIFIVE_PLIC
bool
+
+config GOLDFISH_PIC
+ bool
+
+config M68K_IRQC
+ bool
diff --git a/hw/intc/goldfish_pic.c b/hw/intc/goldfish_pic.c
new file mode 100644
index 0000000000..e3b43a69f1
--- /dev/null
+++ b/hw/intc/goldfish_pic.c
@@ -0,0 +1,219 @@
+/*
+ * SPDX-License-Identifer: GPL-2.0-or-later
+ *
+ * Goldfish PIC
+ *
+ * (c) 2020 Laurent Vivier <laurent@vivier.eu>
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "hw/irq.h"
+#include "hw/qdev-properties.h"
+#include "hw/sysbus.h"
+#include "migration/vmstate.h"
+#include "monitor/monitor.h"
+#include "qemu/log.h"
+#include "trace.h"
+#include "hw/intc/intc.h"
+#include "hw/intc/goldfish_pic.h"
+
+/* registers */
+
+enum {
+ REG_STATUS = 0x00,
+ REG_IRQ_PENDING = 0x04,
+ REG_IRQ_DISABLE_ALL = 0x08,
+ REG_DISABLE = 0x0c,
+ REG_ENABLE = 0x10,
+};
+
+static bool goldfish_pic_get_statistics(InterruptStatsProvider *obj,
+ uint64_t **irq_counts,
+ unsigned int *nb_irqs)
+{
+ GoldfishPICState *s = GOLDFISH_PIC(obj);
+
+ *irq_counts = s->stats_irq_count;
+ *nb_irqs = ARRAY_SIZE(s->stats_irq_count);
+ return true;
+}
+
+static void goldfish_pic_print_info(InterruptStatsProvider *obj, Monitor *mon)
+{
+ GoldfishPICState *s = GOLDFISH_PIC(obj);
+ monitor_printf(mon, "goldfish-pic.%d: pending=0x%08x enabled=0x%08x\n",
+ s->idx, s->pending, s->enabled);
+}
+
+static void goldfish_pic_update(GoldfishPICState *s)
+{
+ if (s->pending & s->enabled) {
+ qemu_irq_raise(s->irq);
+ } else {
+ qemu_irq_lower(s->irq);
+ }
+}
+
+static void goldfish_irq_request(void *opaque, int irq, int level)
+{
+ GoldfishPICState *s = opaque;
+
+ trace_goldfish_irq_request(s, s->idx, irq, level);
+
+ if (level) {
+ s->pending |= 1 << irq;
+ s->stats_irq_count[irq]++;
+ } else {
+ s->pending &= ~(1 << irq);
+ }
+ goldfish_pic_update(s);
+}
+
+static uint64_t goldfish_pic_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ GoldfishPICState *s = opaque;
+ uint64_t value = 0;
+
+ switch (addr) {
+ case REG_STATUS:
+ /* The number of pending interrupts (0 to 32) */
+ value = ctpop32(s->pending & s->enabled);
+ break;
+ case REG_IRQ_PENDING:
+ /* The pending interrupt mask */
+ value = s->pending & s->enabled;
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP,
+ "%s: unimplemented register read 0x%02"HWADDR_PRIx"\n",
+ __func__, addr);
+ break;
+ }
+
+ trace_goldfish_pic_read(s, s->idx, addr, size, value);
+
+ return value;
+}
+
+static void goldfish_pic_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ GoldfishPICState *s = opaque;
+
+ trace_goldfish_pic_write(s, s->idx, addr, size, value);
+
+ switch (addr) {
+ case REG_IRQ_DISABLE_ALL:
+ s->enabled = 0;
+ s->pending = 0;
+ break;
+ case REG_DISABLE:
+ s->enabled &= ~value;
+ break;
+ case REG_ENABLE:
+ s->enabled |= value;
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP,
+ "%s: unimplemented register write 0x%02"HWADDR_PRIx"\n",
+ __func__, addr);
+ break;
+ }
+ goldfish_pic_update(s);
+}
+
+static const MemoryRegionOps goldfish_pic_ops = {
+ .read = goldfish_pic_read,
+ .write = goldfish_pic_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid.max_access_size = 4,
+ .impl.min_access_size = 4,
+ .impl.max_access_size = 4,
+};
+
+static void goldfish_pic_reset(DeviceState *dev)
+{
+ GoldfishPICState *s = GOLDFISH_PIC(dev);
+ int i;
+
+ trace_goldfish_pic_reset(s, s->idx);
+ s->pending = 0;
+ s->enabled = 0;
+
+ for (i = 0; i < ARRAY_SIZE(s->stats_irq_count); i++) {
+ s->stats_irq_count[i] = 0;
+ }
+}
+
+static void goldfish_pic_realize(DeviceState *dev, Error **errp)
+{
+ GoldfishPICState *s = GOLDFISH_PIC(dev);
+
+ trace_goldfish_pic_realize(s, s->idx);
+
+ memory_region_init_io(&s->iomem, OBJECT(s), &goldfish_pic_ops, s,
+ "goldfish_pic", 0x24);
+}
+
+static const VMStateDescription vmstate_goldfish_pic = {
+ .name = "goldfish_pic",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(pending, GoldfishPICState),
+ VMSTATE_UINT32(enabled, GoldfishPICState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void goldfish_pic_instance_init(Object *obj)
+{
+ SysBusDevice *dev = SYS_BUS_DEVICE(obj);
+ GoldfishPICState *s = GOLDFISH_PIC(obj);
+
+ trace_goldfish_pic_instance_init(s);
+
+ sysbus_init_mmio(dev, &s->iomem);
+ sysbus_init_irq(dev, &s->irq);
+
+ qdev_init_gpio_in(DEVICE(obj), goldfish_irq_request, GOLDFISH_PIC_IRQ_NB);
+}
+
+static Property goldfish_pic_properties[] = {
+ DEFINE_PROP_UINT8("index", GoldfishPICState, idx, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void goldfish_pic_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+ InterruptStatsProviderClass *ic = INTERRUPT_STATS_PROVIDER_CLASS(oc);
+
+ dc->reset = goldfish_pic_reset;
+ dc->realize = goldfish_pic_realize;
+ dc->vmsd = &vmstate_goldfish_pic;
+ ic->get_statistics = goldfish_pic_get_statistics;
+ ic->print_info = goldfish_pic_print_info;
+ device_class_set_props(dc, goldfish_pic_properties);
+}
+
+static const TypeInfo goldfish_pic_info = {
+ .name = TYPE_GOLDFISH_PIC,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .class_init = goldfish_pic_class_init,
+ .instance_init = goldfish_pic_instance_init,
+ .instance_size = sizeof(GoldfishPICState),
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_INTERRUPT_STATS_PROVIDER },
+ { }
+ },
+};
+
+static void goldfish_pic_register_types(void)
+{
+ type_register_static(&goldfish_pic_info);
+}
+
+type_init(goldfish_pic_register_types)
diff --git a/hw/intc/m68k_irqc.c b/hw/intc/m68k_irqc.c
new file mode 100644
index 0000000000..2133d2a698
--- /dev/null
+++ b/hw/intc/m68k_irqc.c
@@ -0,0 +1,119 @@
+/*
+ * SPDX-License-Identifer: GPL-2.0-or-later
+ *
+ * QEMU Motorola 680x0 IRQ Controller
+ *
+ * (c) 2020 Laurent Vivier <laurent@vivier.eu>
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "cpu.h"
+#include "migration/vmstate.h"
+#include "monitor/monitor.h"
+#include "hw/nmi.h"
+#include "hw/intc/intc.h"
+#include "hw/intc/m68k_irqc.h"
+
+
+static bool m68k_irqc_get_statistics(InterruptStatsProvider *obj,
+ uint64_t **irq_counts, unsigned int *nb_irqs)
+{
+ M68KIRQCState *s = M68K_IRQC(obj);
+
+ *irq_counts = s->stats_irq_count;
+ *nb_irqs = ARRAY_SIZE(s->stats_irq_count);
+ return true;
+}
+
+static void m68k_irqc_print_info(InterruptStatsProvider *obj, Monitor *mon)
+{
+ M68KIRQCState *s = M68K_IRQC(obj);
+ monitor_printf(mon, "m68k-irqc: ipr=0x%x\n", s->ipr);
+}
+
+static void m68k_set_irq(void *opaque, int irq, int level)
+{
+ M68KIRQCState *s = opaque;
+ M68kCPU *cpu = M68K_CPU(first_cpu);
+ int i;
+
+ if (level) {
+ s->ipr |= 1 << irq;
+ s->stats_irq_count[irq]++;
+ } else {
+ s->ipr &= ~(1 << irq);
+ }
+
+ for (i = M68K_IRQC_LEVEL_7; i >= M68K_IRQC_LEVEL_1; i--) {
+ if ((s->ipr >> i) & 1) {
+ m68k_set_irq_level(cpu, i + 1, i + M68K_IRQC_AUTOVECTOR_BASE);
+ return;
+ }
+ }
+ m68k_set_irq_level(cpu, 0, 0);
+}
+
+static void m68k_irqc_reset(DeviceState *d)
+{
+ M68KIRQCState *s = M68K_IRQC(d);
+ int i;
+
+ s->ipr = 0;
+ for (i = 0; i < ARRAY_SIZE(s->stats_irq_count); i++) {
+ s->stats_irq_count[i] = 0;
+ }
+}
+
+static void m68k_irqc_instance_init(Object *obj)
+{
+ qdev_init_gpio_in(DEVICE(obj), m68k_set_irq, M68K_IRQC_LEVEL_NUM);
+}
+
+static void m68k_nmi(NMIState *n, int cpu_index, Error **errp)
+{
+ m68k_set_irq(n, M68K_IRQC_LEVEL_7, 1);
+}
+
+static const VMStateDescription vmstate_m68k_irqc = {
+ .name = "m68k-irqc",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8(ipr, M68KIRQCState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void m68k_irqc_class_init(ObjectClass *oc, void *data)
+ {
+ DeviceClass *dc = DEVICE_CLASS(oc);
+ NMIClass *nc = NMI_CLASS(oc);
+ InterruptStatsProviderClass *ic = INTERRUPT_STATS_PROVIDER_CLASS(oc);
+
+ nc->nmi_monitor_handler = m68k_nmi;
+ dc->reset = m68k_irqc_reset;
+ dc->vmsd = &vmstate_m68k_irqc;
+ ic->get_statistics = m68k_irqc_get_statistics;
+ ic->print_info = m68k_irqc_print_info;
+}
+
+static const TypeInfo m68k_irqc_type_info = {
+ .name = TYPE_M68K_IRQC,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(M68KIRQCState),
+ .instance_init = m68k_irqc_instance_init,
+ .class_init = m68k_irqc_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_NMI },
+ { TYPE_INTERRUPT_STATS_PROVIDER },
+ { }
+ },
+};
+
+static void q800_irq_register_types(void)
+{
+ type_register_static(&m68k_irqc_type_info);
+}
+
+type_init(q800_irq_register_types);
diff --git a/hw/intc/meson.build b/hw/intc/meson.build
index 8df3656419..1c299039f6 100644
--- a/hw/intc/meson.build
+++ b/hw/intc/meson.build
@@ -57,3 +57,5 @@ specific_ss.add(when: 'CONFIG_PSERIES', if_true: files('xics_spapr.c', 'spapr_xi
specific_ss.add(when: 'CONFIG_XIVE', if_true: files('xive.c'))
specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_XIVE'],
if_true: files('spapr_xive_kvm.c'))
+specific_ss.add(when: 'CONFIG_GOLDFISH_PIC', if_true: files('goldfish_pic.c'))
+specific_ss.add(when: 'CONFIG_M68K_IRQC', if_true: files('m68k_irqc.c'))
diff --git a/hw/intc/trace-events b/hw/intc/trace-events
index 45ddaf48df..c9ab17234b 100644
--- a/hw/intc/trace-events
+++ b/hw/intc/trace-events
@@ -239,3 +239,11 @@ xive_end_source_read(uint8_t end_blk, uint32_t end_idx, uint64_t addr) "END 0x%x
# pnv_xive.c
pnv_xive_ic_hw_trigger(uint64_t addr, uint64_t val) "@0x%"PRIx64" val=0x%"PRIx64
+
+# goldfish_pic.c
+goldfish_irq_request(void *dev, int idx, int irq, int level) "pic: %p goldfish-irq.%d irq: %d level: %d"
+goldfish_pic_read(void *dev, int idx, unsigned int addr, unsigned int size, uint64_t value) "pic: %p goldfish-irq.%d reg: 0x%02x size: %d value: 0x%"PRIx64
+goldfish_pic_write(void *dev, int idx, unsigned int addr, unsigned int size, uint64_t value) "pic: %p goldfish-irq.%d reg: 0x%02x size: %d value: 0x%"PRIx64
+goldfish_pic_reset(void *dev, int idx) "pic: %p goldfish-irq.%d"
+goldfish_pic_realize(void *dev, int idx) "pic: %p goldfish-irq.%d"
+goldfish_pic_instance_init(void *dev) "pic: %p goldfish-irq"
diff --git a/hw/isa/Kconfig b/hw/isa/Kconfig
index c7f07854f7..2691eae2f0 100644
--- a/hw/isa/Kconfig
+++ b/hw/isa/Kconfig
@@ -47,6 +47,7 @@ config VT82C686
select ACPI_SMBUS
select SERIAL_ISA
select FDC
+ select USB_UHCI
config SMC37C669
bool
diff --git a/hw/m68k/Kconfig b/hw/m68k/Kconfig
index 60d7bcfb8f..f839f8a030 100644
--- a/hw/m68k/Kconfig
+++ b/hw/m68k/Kconfig
@@ -23,3 +23,12 @@ config Q800
select ESP
select DP8393X
select OR_IRQ
+
+config M68K_VIRT
+ bool
+ select M68K_IRQC
+ select VIRT_CTRL
+ select GOLDFISH_PIC
+ select GOLDFISH_TTY
+ select GOLDFISH_RTC
+ select VIRTIO_MMIO
diff --git a/hw/m68k/meson.build b/hw/m68k/meson.build
index ca0044c652..31248641d3 100644
--- a/hw/m68k/meson.build
+++ b/hw/m68k/meson.build
@@ -3,5 +3,6 @@ m68k_ss.add(when: 'CONFIG_AN5206', if_true: files('an5206.c', 'mcf5206.c'))
m68k_ss.add(when: 'CONFIG_MCF5208', if_true: files('mcf5208.c', 'mcf_intc.c'))
m68k_ss.add(when: 'CONFIG_NEXTCUBE', if_true: files('next-kbd.c', 'next-cube.c'))
m68k_ss.add(when: 'CONFIG_Q800', if_true: files('q800.c'))
+m68k_ss.add(when: 'CONFIG_M68K_VIRT', if_true: files('virt.c'))
hw_arch += {'m68k': m68k_ss}
diff --git a/hw/m68k/virt.c b/hw/m68k/virt.c
new file mode 100644
index 0000000000..e9a5d4c69b
--- /dev/null
+++ b/hw/m68k/virt.c
@@ -0,0 +1,313 @@
+/*
+ * SPDX-License-Identifer: GPL-2.0-or-later
+ *
+ * QEMU Vitual M68K Machine
+ *
+ * (c) 2020 Laurent Vivier <laurent@vivier.eu>
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/units.h"
+#include "qemu-common.h"
+#include "sysemu/sysemu.h"
+#include "cpu.h"
+#include "hw/hw.h"
+#include "hw/boards.h"
+#include "hw/irq.h"
+#include "hw/qdev-properties.h"
+#include "elf.h"
+#include "hw/loader.h"
+#include "ui/console.h"
+#include "exec/address-spaces.h"
+#include "hw/sysbus.h"
+#include "standard-headers/asm-m68k/bootinfo.h"
+#include "standard-headers/asm-m68k/bootinfo-virt.h"
+#include "bootinfo.h"
+#include "net/net.h"
+#include "qapi/error.h"
+#include "sysemu/qtest.h"
+#include "sysemu/runstate.h"
+#include "sysemu/reset.h"
+
+#include "hw/intc/m68k_irqc.h"
+#include "hw/misc/virt_ctrl.h"
+#include "hw/char/goldfish_tty.h"
+#include "hw/rtc/goldfish_rtc.h"
+#include "hw/intc/goldfish_pic.h"
+#include "hw/virtio/virtio-mmio.h"
+#include "hw/virtio/virtio-blk.h"
+
+/*
+ * 6 goldfish-pic for CPU IRQ #1 to IRQ #6
+ * CPU IRQ #1 -> PIC #1
+ * IRQ #1 to IRQ #31 -> unused
+ * IRQ #32 -> goldfish-tty
+ * CPU IRQ #2 -> PIC #2
+ * IRQ #1 to IRQ #32 -> virtio-mmio from 1 to 32
+ * CPU IRQ #3 -> PIC #3
+ * IRQ #1 to IRQ #32 -> virtio-mmio from 33 to 64
+ * CPU IRQ #4 -> PIC #4
+ * IRQ #1 to IRQ #32 -> virtio-mmio from 65 to 96
+ * CPU IRQ #5 -> PIC #5
+ * IRQ #1 to IRQ #32 -> virtio-mmio from 97 to 128
+ * CPU IRQ #6 -> PIC #6
+ * IRQ #1 -> goldfish-rtc
+ * IRQ #2 to IRQ #32 -> unused
+ * CPU IRQ #7 -> NMI
+ */
+
+#define PIC_IRQ_BASE(num) (8 + (num - 1) * 32)
+#define PIC_IRQ(num, irq) (PIC_IRQ_BASE(num) + irq - 1)
+#define PIC_GPIO(pic_irq) (qdev_get_gpio_in(pic_dev[(pic_irq - 8) / 32], \
+ (pic_irq - 8) % 32))
+
+#define VIRT_GF_PIC_MMIO_BASE 0xff000000 /* MMIO: 0xff000000 - 0xff005fff */
+#define VIRT_GF_PIC_IRQ_BASE 1 /* IRQ: #1 -> #6 */
+#define VIRT_GF_PIC_NB 6
+
+/* 2 goldfish-rtc (and timer) */
+#define VIRT_GF_RTC_MMIO_BASE 0xff006000 /* MMIO: 0xff006000 - 0xff007fff */
+#define VIRT_GF_RTC_IRQ_BASE PIC_IRQ(6, 1) /* PIC: #6, IRQ: #1 */
+#define VIRT_GF_RTC_NB 2
+
+/* 1 goldfish-tty */
+#define VIRT_GF_TTY_MMIO_BASE 0xff008000 /* MMIO: 0xff008000 - 0xff008fff */
+#define VIRT_GF_TTY_IRQ_BASE PIC_IRQ(1, 32) /* PIC: #1, IRQ: #32 */
+
+/* 1 virt-ctrl */
+#define VIRT_CTRL_MMIO_BASE 0xff009000 /* MMIO: 0xff009000 - 0xff009fff */
+#define VIRT_CTRL_IRQ_BASE PIC_IRQ(1, 1) /* PIC: #1, IRQ: #1 */
+
+/*
+ * virtio-mmio size is 0x200 bytes
+ * we use 4 goldfish-pic to attach them,
+ * we can attach 32 virtio devices / goldfish-pic
+ * -> we can manage 32 * 4 = 128 virtio devices
+ */
+#define VIRT_VIRTIO_MMIO_BASE 0xff010000 /* MMIO: 0xff010000 - 0xff01ffff */
+#define VIRT_VIRTIO_IRQ_BASE PIC_IRQ(2, 1) /* PIC: 2, 3, 4, 5, IRQ: ALL */
+
+static void main_cpu_reset(void *opaque)
+{
+ M68kCPU *cpu = opaque;
+ CPUState *cs = CPU(cpu);
+
+ cpu_reset(cs);
+ cpu->env.aregs[7] = ldl_phys(cs->as, 0);
+ cpu->env.pc = ldl_phys(cs->as, 4);
+}
+
+static void virt_init(MachineState *machine)
+{
+ M68kCPU *cpu = NULL;
+ int32_t kernel_size;
+ uint64_t elf_entry;
+ ram_addr_t initrd_base;
+ int32_t initrd_size;
+ ram_addr_t ram_size = machine->ram_size;
+ const char *kernel_filename = machine->kernel_filename;
+ const char *initrd_filename = machine->initrd_filename;
+ const char *kernel_cmdline = machine->kernel_cmdline;
+ hwaddr parameters_base;
+ DeviceState *dev;
+ DeviceState *irqc_dev;
+ DeviceState *pic_dev[VIRT_GF_PIC_NB];
+ SysBusDevice *sysbus;
+ hwaddr io_base;
+ int i;
+
+ if (ram_size > 3399672 * KiB) {
+ /*
+ * The physical memory can be up to 4 GiB - 16 MiB, but linux
+ * kernel crashes after this limit (~ 3.2 GiB)
+ */
+ error_report("Too much memory for this machine: %" PRId64 " KiB, "
+ "maximum 3399672 KiB", ram_size / KiB);
+ exit(1);
+ }
+
+ /* init CPUs */
+ cpu = M68K_CPU(cpu_create(machine->cpu_type));
+ qemu_register_reset(main_cpu_reset, cpu);
+
+ /* RAM */
+ memory_region_add_subregion(get_system_memory(), 0, machine->ram);
+
+ /* IRQ Controller */
+
+ irqc_dev = qdev_new(TYPE_M68K_IRQC);
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(irqc_dev), &error_fatal);
+
+ /*
+ * 6 goldfish-pic
+ *
+ * map: 0xff000000 - 0xff006fff = 28 KiB
+ * IRQ: #1 (lower priority) -> #6 (higher priority)
+ *
+ */
+ io_base = VIRT_GF_PIC_MMIO_BASE;
+ for (i = 0; i < VIRT_GF_PIC_NB; i++) {
+ pic_dev[i] = qdev_new(TYPE_GOLDFISH_PIC);
+ sysbus = SYS_BUS_DEVICE(pic_dev[i]);
+ qdev_prop_set_uint8(pic_dev[i], "index", i);
+ sysbus_realize_and_unref(sysbus, &error_fatal);
+
+ sysbus_mmio_map(sysbus, 0, io_base);
+ sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(irqc_dev, i));
+
+ io_base += 0x1000;
+ }
+
+ /* goldfish-rtc */
+ io_base = VIRT_GF_RTC_MMIO_BASE;
+ for (i = 0; i < VIRT_GF_RTC_NB; i++) {
+ dev = qdev_new(TYPE_GOLDFISH_RTC);
+ sysbus = SYS_BUS_DEVICE(dev);
+ sysbus_realize_and_unref(sysbus, &error_fatal);
+ sysbus_mmio_map(sysbus, 0, io_base);
+ sysbus_connect_irq(sysbus, 0, PIC_GPIO(VIRT_GF_RTC_IRQ_BASE + i));
+
+ io_base += 0x1000;
+ }
+
+ /* goldfish-tty */
+ dev = qdev_new(TYPE_GOLDFISH_TTY);
+ sysbus = SYS_BUS_DEVICE(dev);
+ qdev_prop_set_chr(dev, "chardev", serial_hd(0));
+ sysbus_realize_and_unref(sysbus, &error_fatal);
+ sysbus_mmio_map(sysbus, 0, VIRT_GF_TTY_MMIO_BASE);
+ sysbus_connect_irq(sysbus, 0, PIC_GPIO(VIRT_GF_TTY_IRQ_BASE));
+
+ /* virt controller */
+ dev = qdev_new(TYPE_VIRT_CTRL);
+ sysbus = SYS_BUS_DEVICE(dev);
+ sysbus_realize_and_unref(sysbus, &error_fatal);
+ sysbus_mmio_map(sysbus, 0, VIRT_CTRL_MMIO_BASE);
+ sysbus_connect_irq(sysbus, 0, PIC_GPIO(VIRT_CTRL_IRQ_BASE));
+
+ /* virtio-mmio */
+ io_base = VIRT_VIRTIO_MMIO_BASE;
+ for (i = 0; i < 128; i++) {
+ dev = qdev_new(TYPE_VIRTIO_MMIO);
+ qdev_prop_set_bit(dev, "force-legacy", false);
+ sysbus = SYS_BUS_DEVICE(dev);
+ sysbus_realize_and_unref(sysbus, &error_fatal);
+ sysbus_connect_irq(sysbus, 0, PIC_GPIO(VIRT_VIRTIO_IRQ_BASE + i));
+ sysbus_mmio_map(sysbus, 0, io_base);
+ io_base += 0x200;
+ }
+
+ if (kernel_filename) {
+ CPUState *cs = CPU(cpu);
+ uint64_t high;
+
+ kernel_size = load_elf(kernel_filename, NULL, NULL, NULL,
+ &elf_entry, NULL, &high, NULL, 1,
+ EM_68K, 0, 0);
+ if (kernel_size < 0) {
+ error_report("could not load kernel '%s'", kernel_filename);
+ exit(1);
+ }
+ stl_phys(cs->as, 4, elf_entry); /* reset initial PC */
+ parameters_base = (high + 1) & ~1;
+
+ BOOTINFO1(cs->as, parameters_base, BI_MACHTYPE, MACH_VIRT);
+ BOOTINFO1(cs->as, parameters_base, BI_FPUTYPE, FPU_68040);
+ BOOTINFO1(cs->as, parameters_base, BI_MMUTYPE, MMU_68040);
+ BOOTINFO1(cs->as, parameters_base, BI_CPUTYPE, CPU_68040);
+ BOOTINFO2(cs->as, parameters_base, BI_MEMCHUNK, 0, ram_size);
+
+ BOOTINFO1(cs->as, parameters_base, BI_VIRT_QEMU_VERSION,
+ ((QEMU_VERSION_MAJOR << 24) | (QEMU_VERSION_MINOR << 16) |
+ (QEMU_VERSION_MICRO << 8)));
+ BOOTINFO2(cs->as, parameters_base, BI_VIRT_GF_PIC_BASE,
+ VIRT_GF_PIC_MMIO_BASE, VIRT_GF_PIC_IRQ_BASE);
+ BOOTINFO2(cs->as, parameters_base, BI_VIRT_GF_RTC_BASE,
+ VIRT_GF_RTC_MMIO_BASE, VIRT_GF_RTC_IRQ_BASE);
+ BOOTINFO2(cs->as, parameters_base, BI_VIRT_GF_TTY_BASE,
+ VIRT_GF_TTY_MMIO_BASE, VIRT_GF_TTY_IRQ_BASE);
+ BOOTINFO2(cs->as, parameters_base, BI_VIRT_CTRL_BASE,
+ VIRT_CTRL_MMIO_BASE, VIRT_CTRL_IRQ_BASE);
+ BOOTINFO2(cs->as, parameters_base, BI_VIRT_VIRTIO_BASE,
+ VIRT_VIRTIO_MMIO_BASE, VIRT_VIRTIO_IRQ_BASE);
+
+ if (kernel_cmdline) {
+ BOOTINFOSTR(cs->as, parameters_base, BI_COMMAND_LINE,
+ kernel_cmdline);
+ }
+
+ /* load initrd */
+ if (initrd_filename) {
+ initrd_size = get_image_size(initrd_filename);
+ if (initrd_size < 0) {
+ error_report("could not load initial ram disk '%s'",
+ initrd_filename);
+ exit(1);
+ }
+
+ initrd_base = (ram_size - initrd_size) & TARGET_PAGE_MASK;
+ load_image_targphys(initrd_filename, initrd_base,
+ ram_size - initrd_base);
+ BOOTINFO2(cs->as, parameters_base, BI_RAMDISK, initrd_base,
+ initrd_size);
+ } else {
+ initrd_base = 0;
+ initrd_size = 0;
+ }
+ BOOTINFO0(cs->as, parameters_base, BI_LAST);
+ }
+}
+
+static void virt_machine_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+ mc->desc = "QEMU M68K Virtual Machine";
+ mc->init = virt_init;
+ mc->default_cpu_type = M68K_CPU_TYPE_NAME("m68040");
+ mc->max_cpus = 1;
+ mc->no_floppy = 1;
+ mc->no_parallel = 1;
+ mc->default_ram_id = "m68k_virt.ram";
+}
+
+static const TypeInfo virt_machine_info = {
+ .name = MACHINE_TYPE_NAME("virt"),
+ .parent = TYPE_MACHINE,
+ .abstract = true,
+ .class_init = virt_machine_class_init,
+};
+
+static void virt_machine_register_types(void)
+{
+ type_register_static(&virt_machine_info);
+}
+
+type_init(virt_machine_register_types)
+
+#define DEFINE_VIRT_MACHINE(major, minor, latest) \
+ static void virt_##major##_##minor##_class_init(ObjectClass *oc, \
+ void *data) \
+ { \
+ MachineClass *mc = MACHINE_CLASS(oc); \
+ virt_machine_##major##_##minor##_options(mc); \
+ mc->desc = "QEMU " # major "." # minor " M68K Virtual Machine"; \
+ if (latest) { \
+ mc->alias = "virt"; \
+ } \
+ } \
+ static const TypeInfo machvirt_##major##_##minor##_info = { \
+ .name = MACHINE_TYPE_NAME("virt-" # major "." # minor), \
+ .parent = MACHINE_TYPE_NAME("virt"), \
+ .class_init = virt_##major##_##minor##_class_init, \
+ }; \
+ static void machvirt_machine_##major##_##minor##_init(void) \
+ { \
+ type_register_static(&machvirt_##major##_##minor##_info); \
+ } \
+ type_init(machvirt_machine_##major##_##minor##_init);
+
+static void virt_machine_6_0_options(MachineClass *mc)
+{
+}
+DEFINE_VIRT_MACHINE(6, 0, true)
diff --git a/hw/mem/meson.build b/hw/mem/meson.build
index 0d22f2b572..ef79e04678 100644
--- a/hw/mem/meson.build
+++ b/hw/mem/meson.build
@@ -1,5 +1,6 @@
mem_ss = ss.source_set()
mem_ss.add(files('memory-device.c'))
+mem_ss.add(when: 'CONFIG_FUZZ', if_true: files('sparse-mem.c'))
mem_ss.add(when: 'CONFIG_DIMM', if_true: files('pc-dimm.c'))
mem_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_mc.c'))
mem_ss.add(when: 'CONFIG_NVDIMM', if_true: files('nvdimm.c'))
diff --git a/hw/mem/sparse-mem.c b/hw/mem/sparse-mem.c
new file mode 100644
index 0000000000..a13ac74dd9
--- /dev/null
+++ b/hw/mem/sparse-mem.c
@@ -0,0 +1,151 @@
+/*
+ * A sparse memory device. Useful for fuzzing
+ *
+ * Copyright Red Hat Inc., 2021
+ *
+ * Authors:
+ * Alexander Bulekov <alxndr@bu.edu>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+
+#include "exec/address-spaces.h"
+#include "hw/qdev-properties.h"
+#include "hw/sysbus.h"
+#include "qapi/error.h"
+#include "qemu/units.h"
+#include "sysemu/qtest.h"
+#include "hw/mem/sparse-mem.h"
+
+#define SPARSE_MEM(obj) OBJECT_CHECK(SparseMemState, (obj), TYPE_SPARSE_MEM)
+#define SPARSE_BLOCK_SIZE 0x1000
+
+typedef struct SparseMemState {
+ SysBusDevice parent_obj;
+ MemoryRegion mmio;
+ uint64_t baseaddr;
+ uint64_t length;
+ uint64_t size_used;
+ uint64_t maxsize;
+ GHashTable *mapped;
+} SparseMemState;
+
+typedef struct sparse_mem_block {
+ uint8_t data[SPARSE_BLOCK_SIZE];
+} sparse_mem_block;
+
+static uint64_t sparse_mem_read(void *opaque, hwaddr addr, unsigned int size)
+{
+ SparseMemState *s = opaque;
+ uint64_t ret = 0;
+ size_t pfn = addr / SPARSE_BLOCK_SIZE;
+ size_t offset = addr % SPARSE_BLOCK_SIZE;
+ sparse_mem_block *block;
+
+ block = g_hash_table_lookup(s->mapped, (void *)pfn);
+ if (block) {
+ assert(offset + size <= sizeof(block->data));
+ memcpy(&ret, block->data + offset, size);
+ }
+ return ret;
+}
+
+static void sparse_mem_write(void *opaque, hwaddr addr, uint64_t v,
+ unsigned int size)
+{
+ SparseMemState *s = opaque;
+ size_t pfn = addr / SPARSE_BLOCK_SIZE;
+ size_t offset = addr % SPARSE_BLOCK_SIZE;
+ sparse_mem_block *block;
+
+ if (!g_hash_table_lookup(s->mapped, (void *)pfn) &&
+ s->size_used + SPARSE_BLOCK_SIZE < s->maxsize && v) {
+ g_hash_table_insert(s->mapped, (void *)pfn,
+ g_new0(sparse_mem_block, 1));
+ s->size_used += sizeof(block->data);
+ }
+ block = g_hash_table_lookup(s->mapped, (void *)pfn);
+ if (!block) {
+ return;
+ }
+
+ assert(offset + size <= sizeof(block->data));
+
+ memcpy(block->data + offset, &v, size);
+
+}
+
+static const MemoryRegionOps sparse_mem_ops = {
+ .read = sparse_mem_read,
+ .write = sparse_mem_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 1,
+ .max_access_size = 8,
+ .unaligned = false,
+ },
+};
+
+static Property sparse_mem_properties[] = {
+ /* The base address of the memory */
+ DEFINE_PROP_UINT64("baseaddr", SparseMemState, baseaddr, 0x0),
+ /* The length of the sparse memory region */
+ DEFINE_PROP_UINT64("length", SparseMemState, length, UINT64_MAX),
+ /* Max amount of actual memory that can be used to back the sparse memory */
+ DEFINE_PROP_UINT64("maxsize", SparseMemState, maxsize, 10 * MiB),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+MemoryRegion *sparse_mem_init(uint64_t addr, uint64_t length)
+{
+ DeviceState *dev;
+
+ dev = qdev_new(TYPE_SPARSE_MEM);
+ qdev_prop_set_uint64(dev, "baseaddr", addr);
+ qdev_prop_set_uint64(dev, "length", length);
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
+ sysbus_mmio_map_overlap(SYS_BUS_DEVICE(dev), 0, addr, -10000);
+ return &SPARSE_MEM(dev)->mmio;
+}
+
+static void sparse_mem_realize(DeviceState *dev, Error **errp)
+{
+ SparseMemState *s = SPARSE_MEM(dev);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+
+ if (!qtest_enabled()) {
+ error_setg(errp, "sparse_mem device should only be used "
+ "for testing with QTest");
+ return;
+ }
+
+ assert(s->baseaddr + s->length > s->baseaddr);
+
+ s->mapped = g_hash_table_new(NULL, NULL);
+ memory_region_init_io(&s->mmio, OBJECT(s), &sparse_mem_ops, s,
+ "sparse-mem", s->length);
+ sysbus_init_mmio(sbd, &s->mmio);
+}
+
+static void sparse_mem_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ device_class_set_props(dc, sparse_mem_properties);
+
+ dc->desc = "Sparse Memory Device";
+ dc->realize = sparse_mem_realize;
+}
+
+static const TypeInfo sparse_mem_types[] = {
+ {
+ .name = TYPE_SPARSE_MEM,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(SparseMemState),
+ .class_init = sparse_mem_class_init,
+ },
+};
+DEFINE_TYPES(sparse_mem_types);
diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index 5426b9b1a1..c71ed25820 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -183,4 +183,7 @@ config SIFIVE_U_OTP
config SIFIVE_U_PRCI
bool
+config VIRT_CTRL
+ bool
+
source macio/Kconfig
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index 7a2b0d031a..21034dc60a 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -24,6 +24,9 @@ softmmu_ss.add(when: 'CONFIG_ARM11SCU', if_true: files('arm11scu.c'))
# Mac devices
softmmu_ss.add(when: 'CONFIG_MOS6522', if_true: files('mos6522.c'))
+# virt devices
+softmmu_ss.add(when: 'CONFIG_VIRT_CTRL', if_true: files('virt_ctrl.c'))
+
# RISC-V devices
softmmu_ss.add(when: 'CONFIG_MCHP_PFSOC_DMC', if_true: files('mchp_pfsoc_dmc.c'))
softmmu_ss.add(when: 'CONFIG_MCHP_PFSOC_IOSCB', if_true: files('mchp_pfsoc_ioscb.c'))
diff --git a/hw/misc/trace-events b/hw/misc/trace-events
index fa422727ae..d0a89eb059 100644
--- a/hw/misc/trace-events
+++ b/hw/misc/trace-events
@@ -255,3 +255,10 @@ pca955x_gpio_change(const char *description, unsigned id, unsigned prev_state, u
bcm2835_cprman_read(uint64_t offset, uint64_t value) "offset:0x%" PRIx64 " value:0x%" PRIx64
bcm2835_cprman_write(uint64_t offset, uint64_t value) "offset:0x%" PRIx64 " value:0x%" PRIx64
bcm2835_cprman_write_invalid_magic(uint64_t offset, uint64_t value) "offset:0x%" PRIx64 " value:0x%" PRIx64
+
+# virt_ctrl.c
+virt_ctrl_read(void *dev, unsigned int addr, unsigned int size, uint64_t value) "ctrl: %p reg: 0x%02x size: %d value: 0x%"PRIx64
+virt_ctrl_write(void *dev, unsigned int addr, unsigned int size, uint64_t value) "ctrl: %p reg: 0x%02x size: %d value: 0x%"PRIx64
+virt_ctrl_reset(void *dev) "ctrl: %p"
+virt_ctrl_realize(void *dev) "ctrl: %p"
+virt_ctrl_instance_init(void *dev) "ctrl: %p"
diff --git a/hw/misc/virt_ctrl.c b/hw/misc/virt_ctrl.c
new file mode 100644
index 0000000000..2ea01bd7a1
--- /dev/null
+++ b/hw/misc/virt_ctrl.c
@@ -0,0 +1,151 @@
+/*
+ * SPDX-License-Identifer: GPL-2.0-or-later
+ *
+ * Virt system Controller
+ */
+
+#include "qemu/osdep.h"
+#include "hw/irq.h"
+#include "hw/qdev-properties.h"
+#include "hw/sysbus.h"
+#include "migration/vmstate.h"
+#include "qemu/log.h"
+#include "trace.h"
+#include "sysemu/runstate.h"
+#include "hw/misc/virt_ctrl.h"
+
+enum {
+ REG_FEATURES = 0x00,
+ REG_CMD = 0x04,
+};
+
+#define FEAT_POWER_CTRL 0x00000001
+
+enum {
+ CMD_NOOP,
+ CMD_RESET,
+ CMD_HALT,
+ CMD_PANIC,
+};
+
+static uint64_t virt_ctrl_read(void *opaque, hwaddr addr, unsigned size)
+{
+ VirtCtrlState *s = opaque;
+ uint64_t value = 0;
+
+ switch (addr) {
+ case REG_FEATURES:
+ value = FEAT_POWER_CTRL;
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP,
+ "%s: unimplemented register read 0x%02"HWADDR_PRIx"\n",
+ __func__, addr);
+ break;
+ }
+
+ trace_virt_ctrl_write(s, addr, size, value);
+
+ return value;
+}
+
+static void virt_ctrl_write(void *opaque, hwaddr addr, uint64_t value,
+ unsigned size)
+{
+ VirtCtrlState *s = opaque;
+
+ trace_virt_ctrl_write(s, addr, size, value);
+
+ switch (addr) {
+ case REG_CMD:
+ switch (value) {
+ case CMD_NOOP:
+ break;
+ case CMD_RESET:
+ qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
+ break;
+ case CMD_HALT:
+ qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
+ break;
+ case CMD_PANIC:
+ qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_PANIC);
+ break;
+ }
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP,
+ "%s: unimplemented register write 0x%02"HWADDR_PRIx"\n",
+ __func__, addr);
+ break;
+ }
+}
+
+static const MemoryRegionOps virt_ctrl_ops = {
+ .read = virt_ctrl_read,
+ .write = virt_ctrl_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid.max_access_size = 4,
+ .impl.max_access_size = 4,
+};
+
+static void virt_ctrl_reset(DeviceState *dev)
+{
+ VirtCtrlState *s = VIRT_CTRL(dev);
+
+ trace_virt_ctrl_reset(s);
+}
+
+static void virt_ctrl_realize(DeviceState *dev, Error **errp)
+{
+ VirtCtrlState *s = VIRT_CTRL(dev);
+
+ trace_virt_ctrl_instance_init(s);
+
+ memory_region_init_io(&s->iomem, OBJECT(s), &virt_ctrl_ops, s,
+ "virt-ctrl", 0x100);
+}
+
+static const VMStateDescription vmstate_virt_ctrl = {
+ .name = "virt-ctrl",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(irq_enabled, VirtCtrlState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void virt_ctrl_instance_init(Object *obj)
+{
+ SysBusDevice *dev = SYS_BUS_DEVICE(obj);
+ VirtCtrlState *s = VIRT_CTRL(obj);
+
+ trace_virt_ctrl_instance_init(s);
+
+ sysbus_init_mmio(dev, &s->iomem);
+ sysbus_init_irq(dev, &s->irq);
+}
+
+static void virt_ctrl_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+
+ dc->reset = virt_ctrl_reset;
+ dc->realize = virt_ctrl_realize;
+ dc->vmsd = &vmstate_virt_ctrl;
+}
+
+static const TypeInfo virt_ctrl_info = {
+ .name = TYPE_VIRT_CTRL,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .class_init = virt_ctrl_class_init,
+ .instance_init = virt_ctrl_instance_init,
+ .instance_size = sizeof(VirtCtrlState),
+};
+
+static void virt_ctrl_register_types(void)
+{
+ type_register_static(&virt_ctrl_info);
+}
+
+type_init(virt_ctrl_register_types)
diff --git a/hw/ppc/spapr_iommu.c b/hw/ppc/spapr_iommu.c
index 30352df00e..24537ffcbd 100644
--- a/hw/ppc/spapr_iommu.c
+++ b/hw/ppc/spapr_iommu.c
@@ -212,6 +212,11 @@ static int spapr_tce_notify_flag_changed(IOMMUMemoryRegion *iommu,
{
struct SpaprTceTable *tbl = container_of(iommu, SpaprTceTable, iommu);
+ if (new & IOMMU_NOTIFIER_DEVIOTLB_UNMAP) {
+ error_setg(errp, "spart_tce does not support dev-iotlb yet");
+ return -EINVAL;
+ }
+
if (old == IOMMU_NOTIFIER_NONE && new != IOMMU_NOTIFIER_NONE) {
spapr_tce_set_need_vfio(tbl, true);
} else if (old != IOMMU_NOTIFIER_NONE && new == IOMMU_NOTIFIER_NONE) {
diff --git a/hw/s390x/s390-pci-vfio.c b/hw/s390x/s390-pci-vfio.c
index ead4f222d5..2a153fa8c9 100644
--- a/hw/s390x/s390-pci-vfio.c
+++ b/hw/s390x/s390-pci-vfio.c
@@ -29,14 +29,11 @@
*/
bool s390_pci_update_dma_avail(int fd, unsigned int *avail)
{
- g_autofree struct vfio_iommu_type1_info *info;
- uint32_t argsz;
+ uint32_t argsz = sizeof(struct vfio_iommu_type1_info);
+ g_autofree struct vfio_iommu_type1_info *info = g_malloc0(argsz);
assert(avail);
- argsz = sizeof(struct vfio_iommu_type1_info);
- info = g_malloc0(argsz);
-
/*
* If the specified argsz is not large enough to contain all capabilities
* it will be updated upon return from the ioctl. Retry until we have
@@ -230,7 +227,7 @@ static void s390_pci_read_pfip(S390PCIBusDevice *pbdev,
*/
void s390_pci_get_clp_info(S390PCIBusDevice *pbdev)
{
- g_autofree struct vfio_device_info *info;
+ g_autofree struct vfio_device_info *info = NULL;
VFIOPCIDevice *vfio_pci;
uint32_t argsz;
int fd;
diff --git a/hw/timer/i8254.c b/hw/timer/i8254.c
index c01ee2c72a..c8388ea432 100644
--- a/hw/timer/i8254.c
+++ b/hw/timer/i8254.c
@@ -324,7 +324,7 @@ static void pit_post_load(PITCommonState *s)
{
PITChannelState *sc = &s->channels[0];
- if (sc->next_transition_time != -1) {
+ if (sc->next_transition_time != -1 && !sc->irq_disabled) {
timer_mod(sc->irq_timer, sc->next_transition_time);
} else {
timer_del(sc->irq_timer);
diff --git a/hw/usb/Kconfig b/hw/usb/Kconfig
index 40093d7ea6..53f8283ffd 100644
--- a/hw/usb/Kconfig
+++ b/hw/usb/Kconfig
@@ -66,11 +66,22 @@ config USB_TABLET_WACOM
default y
depends on USB
+config USB_STORAGE_CORE
+ bool
+ depends on USB
+ select SCSI
+
+config USB_STORAGE_CLASSIC
+ bool
+ default y
+ depends on USB
+ select USB_STORAGE_CORE
+
config USB_STORAGE_BOT
bool
default y
depends on USB
- select SCSI
+ select USB_STORAGE_CORE
config USB_STORAGE_UAS
bool
diff --git a/hw/usb/bus.c b/hw/usb/bus.c
index 064f94e9c3..07083349f5 100644
--- a/hw/usb/bus.c
+++ b/hw/usb/bus.c
@@ -19,8 +19,6 @@ static void usb_qdev_unrealize(DeviceState *qdev);
static Property usb_props[] = {
DEFINE_PROP_STRING("port", USBDevice, port_path),
DEFINE_PROP_STRING("serial", USBDevice, serial),
- DEFINE_PROP_BIT("full-path", USBDevice, flags,
- USB_DEV_FLAG_FULL_PATH, true),
DEFINE_PROP_BIT("msos-desc", USBDevice, flags,
USB_DEV_FLAG_MSOS_DESC_ENABLE, true),
DEFINE_PROP_STRING("pcap", USBDevice, pcap_filename),
@@ -312,13 +310,13 @@ typedef struct LegacyUSBFactory
{
const char *name;
const char *usbdevice_name;
- USBDevice *(*usbdevice_init)(const char *params);
+ USBDevice *(*usbdevice_init)(void);
} LegacyUSBFactory;
static GSList *legacy_usb_factory;
void usb_legacy_register(const char *typename, const char *usbdevice_name,
- USBDevice *(*usbdevice_init)(const char *params))
+ USBDevice *(*usbdevice_init)(void))
{
if (usbdevice_name) {
LegacyUSBFactory *f = g_malloc0(sizeof(*f));
@@ -596,11 +594,8 @@ static char *usb_get_dev_path(DeviceState *qdev)
{
USBDevice *dev = USB_DEVICE(qdev);
DeviceState *hcd = qdev->parent_bus->parent;
- char *id = NULL;
+ char *id = qdev_get_dev_path(hcd);
- if (dev->flags & (1 << USB_DEV_FLAG_FULL_PATH)) {
- id = qdev_get_dev_path(hcd);
- }
if (id) {
char *ret = g_strdup_printf("%s/%s", id, dev->port->path);
g_free(id);
@@ -663,27 +658,17 @@ void hmp_info_usb(Monitor *mon, const QDict *qdict)
}
/* handle legacy -usbdevice cmd line option */
-USBDevice *usbdevice_create(const char *cmdline)
+USBDevice *usbdevice_create(const char *driver)
{
USBBus *bus = usb_bus_find(-1 /* any */);
LegacyUSBFactory *f = NULL;
Error *err = NULL;
GSList *i;
- char driver[32];
- const char *params;
- int len;
USBDevice *dev;
- params = strchr(cmdline,':');
- if (params) {
- params++;
- len = params - cmdline;
- if (len > sizeof(driver))
- len = sizeof(driver);
- pstrcpy(driver, len, cmdline);
- } else {
- params = "";
- pstrcpy(driver, sizeof(driver), cmdline);
+ if (strchr(driver, ':')) {
+ error_report("usbdevice parameters are not supported anymore");
+ return NULL;
}
for (i = legacy_usb_factory; i; i = i->next) {
@@ -707,15 +692,7 @@ USBDevice *usbdevice_create(const char *cmdline)
return NULL;
}
- if (f->usbdevice_init) {
- dev = f->usbdevice_init(params);
- } else {
- if (*params) {
- error_report("usbdevice %s accepts no params", driver);
- return NULL;
- }
- dev = usb_new(f->name);
- }
+ dev = f->usbdevice_init ? f->usbdevice_init() : usb_new(f->name);
if (!dev) {
error_report("Failed to create USB device '%s'", f->name);
return NULL;
diff --git a/hw/usb/dev-audio.c b/hw/usb/dev-audio.c
index e1486f81e0..f5cb246792 100644
--- a/hw/usb/dev-audio.c
+++ b/hw/usb/dev-audio.c
@@ -1024,7 +1024,6 @@ static const TypeInfo usb_audio_info = {
static void usb_audio_register_types(void)
{
type_register_static(&usb_audio_info);
- usb_legacy_register(TYPE_USB_AUDIO, "audio", NULL);
}
type_init(usb_audio_register_types)
diff --git a/hw/usb/dev-serial.c b/hw/usb/dev-serial.c
index b58c4eb908..63047d79cf 100644
--- a/hw/usb/dev-serial.c
+++ b/hw/usb/dev-serial.c
@@ -614,7 +614,7 @@ static void usb_serial_realize(USBDevice *dev, Error **errp)
s->intr = usb_ep_get(dev, USB_TOKEN_IN, 1);
}
-static USBDevice *usb_braille_init(const char *unused)
+static USBDevice *usb_braille_init(void)
{
USBDevice *dev;
Chardev *cdrv;
diff --git a/hw/usb/dev-storage-bot.c b/hw/usb/dev-storage-bot.c
new file mode 100644
index 0000000000..6aad026d11
--- /dev/null
+++ b/hw/usb/dev-storage-bot.c
@@ -0,0 +1,63 @@
+/*
+ * USB Mass Storage Device emulation
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licensed under the LGPL.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/typedefs.h"
+#include "qapi/error.h"
+#include "hw/usb.h"
+#include "hw/usb/desc.h"
+#include "hw/usb/msd.h"
+
+static const struct SCSIBusInfo usb_msd_scsi_info_bot = {
+ .tcq = false,
+ .max_target = 0,
+ .max_lun = 15,
+
+ .transfer_data = usb_msd_transfer_data,
+ .complete = usb_msd_command_complete,
+ .cancel = usb_msd_request_cancelled,
+ .load_request = usb_msd_load_request,
+};
+
+static void usb_msd_bot_realize(USBDevice *dev, Error **errp)
+{
+ MSDState *s = USB_STORAGE_DEV(dev);
+ DeviceState *d = DEVICE(dev);
+
+ usb_desc_create_serial(dev);
+ usb_desc_init(dev);
+ if (d->hotplugged) {
+ s->dev.auto_attach = 0;
+ }
+
+ scsi_bus_new(&s->bus, sizeof(s->bus), DEVICE(dev),
+ &usb_msd_scsi_info_bot, NULL);
+ usb_msd_handle_reset(dev);
+}
+
+static void usb_msd_class_bot_initfn(ObjectClass *klass, void *data)
+{
+ USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
+
+ uc->realize = usb_msd_bot_realize;
+ uc->attached_settable = true;
+}
+
+static const TypeInfo bot_info = {
+ .name = "usb-bot",
+ .parent = TYPE_USB_STORAGE,
+ .class_init = usb_msd_class_bot_initfn,
+};
+
+static void register_types(void)
+{
+ type_register_static(&bot_info);
+}
+
+type_init(register_types)
diff --git a/hw/usb/dev-storage-classic.c b/hw/usb/dev-storage-classic.c
new file mode 100644
index 0000000000..00cb34b22f
--- /dev/null
+++ b/hw/usb/dev-storage-classic.c
@@ -0,0 +1,156 @@
+/*
+ * USB Mass Storage Device emulation
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licensed under the LGPL.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/typedefs.h"
+#include "qapi/error.h"
+#include "qapi/visitor.h"
+#include "hw/usb.h"
+#include "hw/usb/desc.h"
+#include "hw/usb/msd.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/block-backend.h"
+
+static const struct SCSIBusInfo usb_msd_scsi_info_storage = {
+ .tcq = false,
+ .max_target = 0,
+ .max_lun = 0,
+
+ .transfer_data = usb_msd_transfer_data,
+ .complete = usb_msd_command_complete,
+ .cancel = usb_msd_request_cancelled,
+ .load_request = usb_msd_load_request,
+};
+
+static void usb_msd_storage_realize(USBDevice *dev, Error **errp)
+{
+ MSDState *s = USB_STORAGE_DEV(dev);
+ BlockBackend *blk = s->conf.blk;
+ SCSIDevice *scsi_dev;
+
+ if (!blk) {
+ error_setg(errp, "drive property not set");
+ return;
+ }
+
+ if (!blkconf_blocksizes(&s->conf, errp)) {
+ return;
+ }
+
+ if (!blkconf_apply_backend_options(&s->conf, !blk_supports_write_perm(blk),
+ true, errp)) {
+ return;
+ }
+
+ /*
+ * Hack alert: this pretends to be a block device, but it's really
+ * a SCSI bus that can serve only a single device, which it
+ * creates automatically. But first it needs to detach from its
+ * blockdev, or else scsi_bus_legacy_add_drive() dies when it
+ * attaches again. We also need to take another reference so that
+ * blk_detach_dev() doesn't free blk while we still need it.
+ *
+ * The hack is probably a bad idea.
+ */
+ blk_ref(blk);
+ blk_detach_dev(blk, DEVICE(s));
+ s->conf.blk = NULL;
+
+ usb_desc_create_serial(dev);
+ usb_desc_init(dev);
+ scsi_bus_new(&s->bus, sizeof(s->bus), DEVICE(dev),
+ &usb_msd_scsi_info_storage, NULL);
+ scsi_dev = scsi_bus_legacy_add_drive(&s->bus, blk, 0, !!s->removable,
+ s->conf.bootindex, s->conf.share_rw,
+ s->conf.rerror, s->conf.werror,
+ dev->serial,
+ errp);
+ blk_unref(blk);
+ if (!scsi_dev) {
+ return;
+ }
+ usb_msd_handle_reset(dev);
+ s->scsi_dev = scsi_dev;
+}
+
+static Property msd_properties[] = {
+ DEFINE_BLOCK_PROPERTIES(MSDState, conf),
+ DEFINE_BLOCK_ERROR_PROPERTIES(MSDState, conf),
+ DEFINE_PROP_BOOL("removable", MSDState, removable, false),
+ DEFINE_PROP_BOOL("commandlog", MSDState, commandlog, false),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void usb_msd_class_storage_initfn(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
+
+ uc->realize = usb_msd_storage_realize;
+ device_class_set_props(dc, msd_properties);
+}
+
+static void usb_msd_get_bootindex(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ USBDevice *dev = USB_DEVICE(obj);
+ MSDState *s = USB_STORAGE_DEV(dev);
+
+ visit_type_int32(v, name, &s->conf.bootindex, errp);
+}
+
+static void usb_msd_set_bootindex(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ USBDevice *dev = USB_DEVICE(obj);
+ MSDState *s = USB_STORAGE_DEV(dev);
+ int32_t boot_index;
+ Error *local_err = NULL;
+
+ if (!visit_type_int32(v, name, &boot_index, errp)) {
+ return;
+ }
+ /* check whether bootindex is present in fw_boot_order list */
+ check_boot_index(boot_index, &local_err);
+ if (local_err) {
+ goto out;
+ }
+ /* change bootindex to a new one */
+ s->conf.bootindex = boot_index;
+
+ if (s->scsi_dev) {
+ object_property_set_int(OBJECT(s->scsi_dev), "bootindex", boot_index,
+ &error_abort);
+ }
+
+out:
+ error_propagate(errp, local_err);
+}
+
+static void usb_msd_instance_init(Object *obj)
+{
+ object_property_add(obj, "bootindex", "int32",
+ usb_msd_get_bootindex,
+ usb_msd_set_bootindex, NULL, NULL);
+ object_property_set_int(obj, "bootindex", -1, NULL);
+}
+
+static const TypeInfo msd_info = {
+ .name = "usb-storage",
+ .parent = TYPE_USB_STORAGE,
+ .class_init = usb_msd_class_storage_initfn,
+ .instance_init = usb_msd_instance_init,
+};
+
+static void register_types(void)
+{
+ type_register_static(&msd_info);
+}
+
+type_init(register_types)
diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c
index a5f76fc001..dca62d544f 100644
--- a/hw/usb/dev-storage.c
+++ b/hw/usb/dev-storage.c
@@ -14,13 +14,11 @@
#include "qemu/option.h"
#include "qemu/config-file.h"
#include "hw/usb.h"
+#include "hw/usb/msd.h"
#include "desc.h"
#include "hw/qdev-properties.h"
#include "hw/scsi/scsi.h"
#include "migration/vmstate.h"
-#include "sysemu/sysemu.h"
-#include "sysemu/block-backend.h"
-#include "qapi/visitor.h"
#include "qemu/cutils.h"
#include "qom/object.h"
#include "trace.h"
@@ -29,43 +27,6 @@
#define MassStorageReset 0xff
#define GetMaxLun 0xfe
-enum USBMSDMode {
- USB_MSDM_CBW, /* Command Block. */
- USB_MSDM_DATAOUT, /* Transfer data to device. */
- USB_MSDM_DATAIN, /* Transfer data from device. */
- USB_MSDM_CSW /* Command Status. */
-};
-
-struct usb_msd_csw {
- uint32_t sig;
- uint32_t tag;
- uint32_t residue;
- uint8_t status;
-};
-
-struct MSDState {
- USBDevice dev;
- enum USBMSDMode mode;
- uint32_t scsi_off;
- uint32_t scsi_len;
- uint32_t data_len;
- struct usb_msd_csw csw;
- SCSIRequest *req;
- SCSIBus bus;
- /* For async completion. */
- USBPacket *packet;
- /* usb-storage only */
- BlockConf conf;
- bool removable;
- bool commandlog;
- SCSIDevice *scsi_dev;
-};
-typedef struct MSDState MSDState;
-
-#define TYPE_USB_STORAGE "usb-storage-dev"
-DECLARE_INSTANCE_CHECKER(MSDState, USB_STORAGE_DEV,
- TYPE_USB_STORAGE)
-
struct usb_msd_cbw {
uint32_t sig;
uint32_t tag;
@@ -259,7 +220,7 @@ static void usb_msd_packet_complete(MSDState *s)
usb_packet_complete(&s->dev, p);
}
-static void usb_msd_transfer_data(SCSIRequest *req, uint32_t len)
+void usb_msd_transfer_data(SCSIRequest *req, uint32_t len)
{
MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);
USBPacket *p = s->packet;
@@ -277,7 +238,7 @@ static void usb_msd_transfer_data(SCSIRequest *req, uint32_t len)
}
}
-static void usb_msd_command_complete(SCSIRequest *req, size_t resid)
+void usb_msd_command_complete(SCSIRequest *req, size_t resid)
{
MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);
USBPacket *p = s->packet;
@@ -320,7 +281,7 @@ static void usb_msd_command_complete(SCSIRequest *req, size_t resid)
s->req = NULL;
}
-static void usb_msd_request_cancelled(SCSIRequest *req)
+void usb_msd_request_cancelled(SCSIRequest *req)
{
MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);
@@ -337,7 +298,7 @@ static void usb_msd_request_cancelled(SCSIRequest *req)
}
}
-static void usb_msd_handle_reset(USBDevice *dev)
+void usb_msd_handle_reset(USBDevice *dev)
{
MSDState *s = (MSDState *)dev;
@@ -352,6 +313,7 @@ static void usb_msd_handle_reset(USBDevice *dev)
usb_msd_packet_complete(s);
}
+ memset(&s->csw, 0, sizeof(s->csw));
s->mode = USB_MSDM_CBW;
}
@@ -565,7 +527,7 @@ static void usb_msd_handle_data(USBDevice *dev, USBPacket *p)
}
}
-static void *usb_msd_load_request(QEMUFile *f, SCSIRequest *req)
+void *usb_msd_load_request(QEMUFile *f, SCSIRequest *req)
{
MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);
@@ -576,95 +538,6 @@ static void *usb_msd_load_request(QEMUFile *f, SCSIRequest *req)
return NULL;
}
-static const struct SCSIBusInfo usb_msd_scsi_info_storage = {
- .tcq = false,
- .max_target = 0,
- .max_lun = 0,
-
- .transfer_data = usb_msd_transfer_data,
- .complete = usb_msd_command_complete,
- .cancel = usb_msd_request_cancelled,
- .load_request = usb_msd_load_request,
-};
-
-static const struct SCSIBusInfo usb_msd_scsi_info_bot = {
- .tcq = false,
- .max_target = 0,
- .max_lun = 15,
-
- .transfer_data = usb_msd_transfer_data,
- .complete = usb_msd_command_complete,
- .cancel = usb_msd_request_cancelled,
- .load_request = usb_msd_load_request,
-};
-
-static void usb_msd_storage_realize(USBDevice *dev, Error **errp)
-{
- MSDState *s = USB_STORAGE_DEV(dev);
- BlockBackend *blk = s->conf.blk;
- SCSIDevice *scsi_dev;
-
- if (!blk) {
- error_setg(errp, "drive property not set");
- return;
- }
-
- if (!blkconf_blocksizes(&s->conf, errp)) {
- return;
- }
-
- if (!blkconf_apply_backend_options(&s->conf, !blk_supports_write_perm(blk),
- true, errp)) {
- return;
- }
-
- /*
- * Hack alert: this pretends to be a block device, but it's really
- * a SCSI bus that can serve only a single device, which it
- * creates automatically. But first it needs to detach from its
- * blockdev, or else scsi_bus_legacy_add_drive() dies when it
- * attaches again. We also need to take another reference so that
- * blk_detach_dev() doesn't free blk while we still need it.
- *
- * The hack is probably a bad idea.
- */
- blk_ref(blk);
- blk_detach_dev(blk, DEVICE(s));
- s->conf.blk = NULL;
-
- usb_desc_create_serial(dev);
- usb_desc_init(dev);
- scsi_bus_new(&s->bus, sizeof(s->bus), DEVICE(dev),
- &usb_msd_scsi_info_storage, NULL);
- scsi_dev = scsi_bus_legacy_add_drive(&s->bus, blk, 0, !!s->removable,
- s->conf.bootindex, s->conf.share_rw,
- s->conf.rerror, s->conf.werror,
- dev->serial,
- errp);
- blk_unref(blk);
- if (!scsi_dev) {
- return;
- }
- usb_msd_handle_reset(dev);
- s->scsi_dev = scsi_dev;
-}
-
-static void usb_msd_bot_realize(USBDevice *dev, Error **errp)
-{
- MSDState *s = USB_STORAGE_DEV(dev);
- DeviceState *d = DEVICE(dev);
-
- usb_desc_create_serial(dev);
- usb_desc_init(dev);
- if (d->hotplugged) {
- s->dev.auto_attach = 0;
- }
-
- scsi_bus_new(&s->bus, sizeof(s->bus), DEVICE(dev),
- &usb_msd_scsi_info_bot, NULL);
- usb_msd_handle_reset(dev);
-}
-
static const VMStateDescription vmstate_usb_msd = {
.name = "usb-storage",
.version_id = 1,
@@ -683,14 +556,6 @@ static const VMStateDescription vmstate_usb_msd = {
}
};
-static Property msd_properties[] = {
- DEFINE_BLOCK_PROPERTIES(MSDState, conf),
- DEFINE_BLOCK_ERROR_PROPERTIES(MSDState, conf),
- DEFINE_PROP_BOOL("removable", MSDState, removable, false),
- DEFINE_PROP_BOOL("commandlog", MSDState, commandlog, false),
- DEFINE_PROP_END_OF_LIST(),
-};
-
static void usb_msd_class_initfn_common(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
@@ -708,52 +573,6 @@ static void usb_msd_class_initfn_common(ObjectClass *klass, void *data)
dc->vmsd = &vmstate_usb_msd;
}
-static void usb_msd_class_storage_initfn(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
-
- uc->realize = usb_msd_storage_realize;
- device_class_set_props(dc, msd_properties);
-}
-
-static void usb_msd_get_bootindex(Object *obj, Visitor *v, const char *name,
- void *opaque, Error **errp)
-{
- USBDevice *dev = USB_DEVICE(obj);
- MSDState *s = USB_STORAGE_DEV(dev);
-
- visit_type_int32(v, name, &s->conf.bootindex, errp);
-}
-
-static void usb_msd_set_bootindex(Object *obj, Visitor *v, const char *name,
- void *opaque, Error **errp)
-{
- USBDevice *dev = USB_DEVICE(obj);
- MSDState *s = USB_STORAGE_DEV(dev);
- int32_t boot_index;
- Error *local_err = NULL;
-
- if (!visit_type_int32(v, name, &boot_index, errp)) {
- return;
- }
- /* check whether bootindex is present in fw_boot_order list */
- check_boot_index(boot_index, &local_err);
- if (local_err) {
- goto out;
- }
- /* change bootindex to a new one */
- s->conf.bootindex = boot_index;
-
- if (s->scsi_dev) {
- object_property_set_int(OBJECT(s->scsi_dev), "bootindex", boot_index,
- &error_abort);
- }
-
-out:
- error_propagate(errp, local_err);
-}
-
static const TypeInfo usb_storage_dev_type_info = {
.name = TYPE_USB_STORAGE,
.parent = TYPE_USB_DEVICE,
@@ -762,40 +581,9 @@ static const TypeInfo usb_storage_dev_type_info = {
.class_init = usb_msd_class_initfn_common,
};
-static void usb_msd_instance_init(Object *obj)
-{
- object_property_add(obj, "bootindex", "int32",
- usb_msd_get_bootindex,
- usb_msd_set_bootindex, NULL, NULL);
- object_property_set_int(obj, "bootindex", -1, NULL);
-}
-
-static void usb_msd_class_bot_initfn(ObjectClass *klass, void *data)
-{
- USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
-
- uc->realize = usb_msd_bot_realize;
- uc->attached_settable = true;
-}
-
-static const TypeInfo msd_info = {
- .name = "usb-storage",
- .parent = TYPE_USB_STORAGE,
- .class_init = usb_msd_class_storage_initfn,
- .instance_init = usb_msd_instance_init,
-};
-
-static const TypeInfo bot_info = {
- .name = "usb-bot",
- .parent = TYPE_USB_STORAGE,
- .class_init = usb_msd_class_bot_initfn,
-};
-
static void usb_msd_register_types(void)
{
type_register_static(&usb_storage_dev_type_info);
- type_register_static(&msd_info);
- type_register_static(&bot_info);
}
type_init(usb_msd_register_types)
diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c
index 5969eb86b3..0cb02a6432 100644
--- a/hw/usb/hcd-uhci.c
+++ b/hw/usb/hcd-uhci.c
@@ -40,6 +40,7 @@
#include "qemu/main-loop.h"
#include "qemu/module.h"
#include "qom/object.h"
+#include "hcd-uhci.h"
#define FRAME_TIMER_FREQ 1000
@@ -50,8 +51,6 @@
#define MAX_FRAMES_PER_TICK (QH_VALID / 2)
-#define NB_PORTS 2
-
enum {
TD_RESULT_STOP_FRAME = 10,
TD_RESULT_COMPLETE,
@@ -62,20 +61,8 @@ enum {
typedef struct UHCIState UHCIState;
typedef struct UHCIAsync UHCIAsync;
-typedef struct UHCIQueue UHCIQueue;
-typedef struct UHCIInfo UHCIInfo;
typedef struct UHCIPCIDeviceClass UHCIPCIDeviceClass;
-struct UHCIInfo {
- const char *name;
- uint16_t vendor_id;
- uint16_t device_id;
- uint8_t revision;
- uint8_t irq_pin;
- void (*realize)(PCIDevice *dev, Error **errp);
- bool unplug;
-};
-
struct UHCIPCIDeviceClass {
PCIDeviceClass parent_class;
UHCIInfo info;
@@ -107,43 +94,6 @@ struct UHCIQueue {
int8_t valid;
};
-typedef struct UHCIPort {
- USBPort port;
- uint16_t ctrl;
-} UHCIPort;
-
-struct UHCIState {
- PCIDevice dev;
- MemoryRegion io_bar;
- USBBus bus; /* Note unused when we're a companion controller */
- uint16_t cmd; /* cmd register */
- uint16_t status;
- uint16_t intr; /* interrupt enable register */
- uint16_t frnum; /* frame number */
- uint32_t fl_base_addr; /* frame list base address */
- uint8_t sof_timing;
- uint8_t status2; /* bit 0 and 1 are used to generate UHCI_STS_USBINT */
- int64_t expire_time;
- QEMUTimer *frame_timer;
- QEMUBH *bh;
- uint32_t frame_bytes;
- uint32_t frame_bandwidth;
- bool completions_only;
- UHCIPort ports[NB_PORTS];
-
- /* Interrupts that should be raised at the end of the current frame. */
- uint32_t pending_int_mask;
-
- /* Active packets */
- QTAILQ_HEAD(, UHCIQueue) queues;
- uint8_t num_ports_vmstate;
-
- /* Properties */
- char *masterbus;
- uint32_t firstport;
- uint32_t maxframes;
-};
-
typedef struct UHCI_TD {
uint32_t link;
uint32_t ctrl; /* see TD_CTRL_xxx */
@@ -160,10 +110,6 @@ static void uhci_async_cancel(UHCIAsync *async);
static void uhci_queue_fill(UHCIQueue *q, UHCI_TD *td);
static void uhci_resume(void *opaque);
-#define TYPE_UHCI "pci-uhci-usb"
-DECLARE_INSTANCE_CHECKER(UHCIState, UHCI,
- TYPE_UHCI)
-
static inline int32_t uhci_queue_token(UHCI_TD *td)
{
if ((td->token & (0xf << 15)) == 0) {
@@ -1213,7 +1159,7 @@ static USBPortOps uhci_port_ops = {
static USBBusOps uhci_bus_ops = {
};
-static void usb_uhci_common_realize(PCIDevice *dev, Error **errp)
+void usb_uhci_common_realize(PCIDevice *dev, Error **errp)
{
Error *err = NULL;
PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev);
@@ -1261,21 +1207,6 @@ static void usb_uhci_common_realize(PCIDevice *dev, Error **errp)
pci_register_bar(&s->dev, 4, PCI_BASE_ADDRESS_SPACE_IO, &s->io_bar);
}
-static void usb_uhci_vt82c686b_realize(PCIDevice *dev, Error **errp)
-{
- UHCIState *s = UHCI(dev);
- uint8_t *pci_conf = s->dev.config;
-
- /* USB misc control 1/2 */
- pci_set_long(pci_conf + 0x40,0x00001000);
- /* PM capability */
- pci_set_long(pci_conf + 0x80,0x00020001);
- /* USB legacy support */
- pci_set_long(pci_conf + 0xc0,0x00002000);
-
- usb_uhci_common_realize(dev, errp);
-}
-
static void usb_uhci_exit(PCIDevice *dev)
{
UHCIState *s = UHCI(dev);
@@ -1335,7 +1266,7 @@ static const TypeInfo uhci_pci_type_info = {
},
};
-static void uhci_data_class_init(ObjectClass *klass, void *data)
+void uhci_data_class_init(ObjectClass *klass, void *data)
{
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
DeviceClass *dc = DEVICE_CLASS(klass);
@@ -1373,14 +1304,6 @@ static UHCIInfo uhci_info[] = {
.irq_pin = 3,
.unplug = true,
},{
- .name = "vt82c686b-usb-uhci",
- .vendor_id = PCI_VENDOR_ID_VIA,
- .device_id = PCI_DEVICE_ID_VIA_UHCI,
- .revision = 0x01,
- .irq_pin = 3,
- .realize = usb_uhci_vt82c686b_realize,
- .unplug = true,
- },{
.name = "ich9-usb-uhci1", /* 00:1d.0 */
.vendor_id = PCI_VENDOR_ID_INTEL,
.device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI1,
diff --git a/hw/usb/hcd-uhci.h b/hw/usb/hcd-uhci.h
new file mode 100644
index 0000000000..e61d8fcb19
--- /dev/null
+++ b/hw/usb/hcd-uhci.h
@@ -0,0 +1,93 @@
+/*
+ * USB UHCI controller emulation
+ *
+ * Copyright (c) 2005 Fabrice Bellard
+ *
+ * Copyright (c) 2008 Max Krasnyansky
+ * Magor rewrite of the UHCI data structures parser and frame processor
+ * Support for fully async operation and multiple outstanding transactions
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef HW_USB_HCD_UHCI_H
+#define HW_USB_HCD_UHCI_H
+
+#include "exec/memory.h"
+#include "qemu/timer.h"
+#include "hw/pci/pci.h"
+#include "hw/usb.h"
+
+typedef struct UHCIQueue UHCIQueue;
+
+#define NB_PORTS 2
+
+typedef struct UHCIPort {
+ USBPort port;
+ uint16_t ctrl;
+} UHCIPort;
+
+typedef struct UHCIState {
+ PCIDevice dev;
+ MemoryRegion io_bar;
+ USBBus bus; /* Note unused when we're a companion controller */
+ uint16_t cmd; /* cmd register */
+ uint16_t status;
+ uint16_t intr; /* interrupt enable register */
+ uint16_t frnum; /* frame number */
+ uint32_t fl_base_addr; /* frame list base address */
+ uint8_t sof_timing;
+ uint8_t status2; /* bit 0 and 1 are used to generate UHCI_STS_USBINT */
+ int64_t expire_time;
+ QEMUTimer *frame_timer;
+ QEMUBH *bh;
+ uint32_t frame_bytes;
+ uint32_t frame_bandwidth;
+ bool completions_only;
+ UHCIPort ports[NB_PORTS];
+
+ /* Interrupts that should be raised at the end of the current frame. */
+ uint32_t pending_int_mask;
+
+ /* Active packets */
+ QTAILQ_HEAD(, UHCIQueue) queues;
+ uint8_t num_ports_vmstate;
+
+ /* Properties */
+ char *masterbus;
+ uint32_t firstport;
+ uint32_t maxframes;
+} UHCIState;
+
+#define TYPE_UHCI "pci-uhci-usb"
+DECLARE_INSTANCE_CHECKER(UHCIState, UHCI, TYPE_UHCI)
+
+typedef struct UHCIInfo {
+ const char *name;
+ uint16_t vendor_id;
+ uint16_t device_id;
+ uint8_t revision;
+ uint8_t irq_pin;
+ void (*realize)(PCIDevice *dev, Error **errp);
+ bool unplug;
+} UHCIInfo;
+
+void uhci_data_class_init(ObjectClass *klass, void *data);
+void usb_uhci_common_realize(PCIDevice *dev, Error **errp);
+
+#endif
diff --git a/hw/usb/meson.build b/hw/usb/meson.build
index 653192cff6..fb7a74e73a 100644
--- a/hw/usb/meson.build
+++ b/hw/usb/meson.build
@@ -32,6 +32,7 @@ softmmu_ss.add(when: 'CONFIG_USB_DWC3', if_true: files('hcd-dwc3.c'))
softmmu_ss.add(when: 'CONFIG_TUSB6010', if_true: files('tusb6010.c'))
softmmu_ss.add(when: 'CONFIG_IMX', if_true: files('chipidea.c'))
softmmu_ss.add(when: 'CONFIG_IMX_USBPHY', if_true: files('imx-usb-phy.c'))
+softmmu_ss.add(when: 'CONFIG_VT82C686', if_true: files('vt82c686-uhci-pci.c'))
specific_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files('xlnx-versal-usb2-ctrl-regs.c'))
specific_ss.add(when: 'CONFIG_XLNX_USB_SUBSYS', if_true: files('xlnx-usb-subsystem.c'))
@@ -39,7 +40,9 @@ specific_ss.add(when: 'CONFIG_XLNX_USB_SUBSYS', if_true: files('xlnx-usb-subsyst
softmmu_ss.add(when: 'CONFIG_USB', if_true: files('dev-hub.c'))
softmmu_ss.add(when: 'CONFIG_USB', if_true: files('dev-hid.c'))
softmmu_ss.add(when: 'CONFIG_USB_TABLET_WACOM', if_true: files('dev-wacom.c'))
-softmmu_ss.add(when: 'CONFIG_USB_STORAGE_BOT', if_true: files('dev-storage.c'))
+softmmu_ss.add(when: 'CONFIG_USB_STORAGE_CORE', if_true: files('dev-storage.c'))
+softmmu_ss.add(when: 'CONFIG_USB_STORAGE_BOT', if_true: files('dev-storage-bot.c'))
+softmmu_ss.add(when: 'CONFIG_USB_STORAGE_CLASSIC', if_true: files('dev-storage-classic.c'))
softmmu_ss.add(when: 'CONFIG_USB_STORAGE_UAS', if_true: files('dev-uas.c'))
softmmu_ss.add(when: 'CONFIG_USB_AUDIO', if_true: files('dev-audio.c'))
softmmu_ss.add(when: 'CONFIG_USB_SERIAL', if_true: files('dev-serial.c'))
diff --git a/hw/usb/u2f.c b/hw/usb/u2f.c
index bc09191f06..56001249a4 100644
--- a/hw/usb/u2f.c
+++ b/hw/usb/u2f.c
@@ -346,7 +346,6 @@ static const TypeInfo u2f_key_info = {
static void u2f_key_register_types(void)
{
type_register_static(&u2f_key_info);
- usb_legacy_register(TYPE_U2F_KEY, "u2f-key", NULL);
}
type_init(u2f_key_register_types)
diff --git a/hw/usb/vt82c686-uhci-pci.c b/hw/usb/vt82c686-uhci-pci.c
new file mode 100644
index 0000000000..b109c21603
--- /dev/null
+++ b/hw/usb/vt82c686-uhci-pci.c
@@ -0,0 +1,43 @@
+#include "qemu/osdep.h"
+#include "hcd-uhci.h"
+
+static void usb_uhci_vt82c686b_realize(PCIDevice *dev, Error **errp)
+{
+ UHCIState *s = UHCI(dev);
+ uint8_t *pci_conf = s->dev.config;
+
+ /* USB misc control 1/2 */
+ pci_set_long(pci_conf + 0x40, 0x00001000);
+ /* PM capability */
+ pci_set_long(pci_conf + 0x80, 0x00020001);
+ /* USB legacy support */
+ pci_set_long(pci_conf + 0xc0, 0x00002000);
+
+ usb_uhci_common_realize(dev, errp);
+}
+
+static UHCIInfo uhci_info[] = {
+ {
+ .name = "vt82c686b-usb-uhci",
+ .vendor_id = PCI_VENDOR_ID_VIA,
+ .device_id = PCI_DEVICE_ID_VIA_UHCI,
+ .revision = 0x01,
+ .irq_pin = 3,
+ .realize = usb_uhci_vt82c686b_realize,
+ .unplug = true,
+ }
+};
+
+static const TypeInfo vt82c686b_usb_uhci_type_info = {
+ .parent = TYPE_UHCI,
+ .name = "vt82c686b-usb-uhci",
+ .class_init = uhci_data_class_init,
+ .class_data = uhci_info,
+};
+
+static void vt82c686b_usb_uhci_register_types(void)
+{
+ type_register_static(&vt82c686b_usb_uhci_type_info);
+}
+
+type_init(vt82c686b_usb_uhci_register_types)
diff --git a/hw/vfio/common.c b/hw/vfio/common.c
index 6ff1daa763..ae5654fcdb 100644
--- a/hw/vfio/common.c
+++ b/hw/vfio/common.c
@@ -311,7 +311,7 @@ bool vfio_mig_active(void)
return true;
}
-static bool vfio_devices_all_saving(VFIOContainer *container)
+static bool vfio_devices_all_dirty_tracking(VFIOContainer *container)
{
VFIOGroup *group;
VFIODevice *vbasedev;
@@ -329,13 +329,8 @@ static bool vfio_devices_all_saving(VFIOContainer *container)
return false;
}
- if (migration->device_state & VFIO_DEVICE_STATE_SAVING) {
- if ((vbasedev->pre_copy_dirty_page_tracking == ON_OFF_AUTO_OFF)
- && (migration->device_state & VFIO_DEVICE_STATE_RUNNING)) {
- return false;
- }
- continue;
- } else {
+ if ((vbasedev->pre_copy_dirty_page_tracking == ON_OFF_AUTO_OFF)
+ && (migration->device_state & VFIO_DEVICE_STATE_RUNNING)) {
return false;
}
}
@@ -378,7 +373,7 @@ static int vfio_dma_unmap_bitmap(VFIOContainer *container,
{
struct vfio_iommu_type1_dma_unmap *unmap;
struct vfio_bitmap *bitmap;
- uint64_t pages = TARGET_PAGE_ALIGN(size) >> TARGET_PAGE_BITS;
+ uint64_t pages = REAL_HOST_PAGE_ALIGN(size) / qemu_real_host_page_size;
int ret;
unmap = g_malloc0(sizeof(*unmap) + sizeof(*bitmap));
@@ -390,12 +385,12 @@ static int vfio_dma_unmap_bitmap(VFIOContainer *container,
bitmap = (struct vfio_bitmap *)&unmap->data;
/*
- * cpu_physical_memory_set_dirty_lebitmap() expects pages in bitmap of
- * TARGET_PAGE_SIZE to mark those dirty. Hence set bitmap_pgsize to
- * TARGET_PAGE_SIZE.
+ * cpu_physical_memory_set_dirty_lebitmap() supports pages in bitmap of
+ * qemu_real_host_page_size to mark those dirty. Hence set bitmap_pgsize
+ * to qemu_real_host_page_size.
*/
- bitmap->pgsize = TARGET_PAGE_SIZE;
+ bitmap->pgsize = qemu_real_host_page_size;
bitmap->size = ROUND_UP(pages, sizeof(__u64) * BITS_PER_BYTE) /
BITS_PER_BYTE;
@@ -674,16 +669,17 @@ static void vfio_listener_region_add(MemoryListener *listener,
return;
}
- if (unlikely((section->offset_within_address_space & ~TARGET_PAGE_MASK) !=
- (section->offset_within_region & ~TARGET_PAGE_MASK))) {
+ if (unlikely((section->offset_within_address_space &
+ ~qemu_real_host_page_mask) !=
+ (section->offset_within_region & ~qemu_real_host_page_mask))) {
error_report("%s received unaligned region", __func__);
return;
}
- iova = TARGET_PAGE_ALIGN(section->offset_within_address_space);
+ iova = REAL_HOST_PAGE_ALIGN(section->offset_within_address_space);
llend = int128_make64(section->offset_within_address_space);
llend = int128_add(llend, section->size);
- llend = int128_and(llend, int128_exts64(TARGET_PAGE_MASK));
+ llend = int128_and(llend, int128_exts64(qemu_real_host_page_mask));
if (int128_ge(int128_make64(iova), llend)) {
return;
@@ -787,7 +783,7 @@ static void vfio_listener_region_add(MemoryListener *listener,
iommu_idx = memory_region_iommu_attrs_to_index(iommu_mr,
MEMTXATTRS_UNSPECIFIED);
iommu_notifier_init(&giommu->n, vfio_iommu_map_notify,
- IOMMU_NOTIFIER_ALL,
+ IOMMU_NOTIFIER_IOTLB_EVENTS,
section->offset_within_region,
int128_get64(llend),
iommu_idx);
@@ -892,8 +888,9 @@ static void vfio_listener_region_del(MemoryListener *listener,
return;
}
- if (unlikely((section->offset_within_address_space & ~TARGET_PAGE_MASK) !=
- (section->offset_within_region & ~TARGET_PAGE_MASK))) {
+ if (unlikely((section->offset_within_address_space &
+ ~qemu_real_host_page_mask) !=
+ (section->offset_within_region & ~qemu_real_host_page_mask))) {
error_report("%s received unaligned region", __func__);
return;
}
@@ -921,10 +918,10 @@ static void vfio_listener_region_del(MemoryListener *listener,
*/
}
- iova = TARGET_PAGE_ALIGN(section->offset_within_address_space);
+ iova = REAL_HOST_PAGE_ALIGN(section->offset_within_address_space);
llend = int128_make64(section->offset_within_address_space);
llend = int128_add(llend, section->size);
- llend = int128_and(llend, int128_exts64(TARGET_PAGE_MASK));
+ llend = int128_and(llend, int128_exts64(qemu_real_host_page_mask));
if (int128_ge(int128_make64(iova), llend)) {
return;
@@ -987,6 +984,40 @@ static void vfio_listener_region_del(MemoryListener *listener,
}
}
+static void vfio_set_dirty_page_tracking(VFIOContainer *container, bool start)
+{
+ int ret;
+ struct vfio_iommu_type1_dirty_bitmap dirty = {
+ .argsz = sizeof(dirty),
+ };
+
+ if (start) {
+ dirty.flags = VFIO_IOMMU_DIRTY_PAGES_FLAG_START;
+ } else {
+ dirty.flags = VFIO_IOMMU_DIRTY_PAGES_FLAG_STOP;
+ }
+
+ ret = ioctl(container->fd, VFIO_IOMMU_DIRTY_PAGES, &dirty);
+ if (ret) {
+ error_report("Failed to set dirty tracking flag 0x%x errno: %d",
+ dirty.flags, errno);
+ }
+}
+
+static void vfio_listener_log_global_start(MemoryListener *listener)
+{
+ VFIOContainer *container = container_of(listener, VFIOContainer, listener);
+
+ vfio_set_dirty_page_tracking(container, true);
+}
+
+static void vfio_listener_log_global_stop(MemoryListener *listener)
+{
+ VFIOContainer *container = container_of(listener, VFIOContainer, listener);
+
+ vfio_set_dirty_page_tracking(container, false);
+}
+
static int vfio_get_dirty_bitmap(VFIOContainer *container, uint64_t iova,
uint64_t size, ram_addr_t ram_addr)
{
@@ -1004,13 +1035,13 @@ static int vfio_get_dirty_bitmap(VFIOContainer *container, uint64_t iova,
range->size = size;
/*
- * cpu_physical_memory_set_dirty_lebitmap() expects pages in bitmap of
- * TARGET_PAGE_SIZE to mark those dirty. Hence set bitmap's pgsize to
- * TARGET_PAGE_SIZE.
+ * cpu_physical_memory_set_dirty_lebitmap() supports pages in bitmap of
+ * qemu_real_host_page_size to mark those dirty. Hence set bitmap's pgsize
+ * to qemu_real_host_page_size.
*/
- range->bitmap.pgsize = TARGET_PAGE_SIZE;
+ range->bitmap.pgsize = qemu_real_host_page_size;
- pages = TARGET_PAGE_ALIGN(range->size) >> TARGET_PAGE_BITS;
+ pages = REAL_HOST_PAGE_ALIGN(range->size) / qemu_real_host_page_size;
range->bitmap.size = ROUND_UP(pages, sizeof(__u64) * BITS_PER_BYTE) /
BITS_PER_BYTE;
range->bitmap.data = g_try_malloc0(range->bitmap.size);
@@ -1114,11 +1145,11 @@ static int vfio_sync_dirty_bitmap(VFIOContainer *container,
section->offset_within_region;
return vfio_get_dirty_bitmap(container,
- TARGET_PAGE_ALIGN(section->offset_within_address_space),
- int128_get64(section->size), ram_addr);
+ REAL_HOST_PAGE_ALIGN(section->offset_within_address_space),
+ int128_get64(section->size), ram_addr);
}
-static void vfio_listerner_log_sync(MemoryListener *listener,
+static void vfio_listener_log_sync(MemoryListener *listener,
MemoryRegionSection *section)
{
VFIOContainer *container = container_of(listener, VFIOContainer, listener);
@@ -1128,7 +1159,7 @@ static void vfio_listerner_log_sync(MemoryListener *listener,
return;
}
- if (vfio_devices_all_saving(container)) {
+ if (vfio_devices_all_dirty_tracking(container)) {
vfio_sync_dirty_bitmap(container, section);
}
}
@@ -1136,7 +1167,9 @@ static void vfio_listerner_log_sync(MemoryListener *listener,
static const MemoryListener vfio_memory_listener = {
.region_add = vfio_listener_region_add,
.region_del = vfio_listener_region_del,
- .log_sync = vfio_listerner_log_sync,
+ .log_global_start = vfio_listener_log_global_start,
+ .log_global_stop = vfio_listener_log_global_stop,
+ .log_sync = vfio_listener_log_sync,
};
static void vfio_listener_release(VFIOContainer *container)
@@ -1655,10 +1688,10 @@ static void vfio_get_iommu_info_migration(VFIOContainer *container,
header);
/*
- * cpu_physical_memory_set_dirty_lebitmap() expects pages in bitmap of
- * TARGET_PAGE_SIZE to mark those dirty.
+ * cpu_physical_memory_set_dirty_lebitmap() supports pages in bitmap of
+ * qemu_real_host_page_size to mark those dirty.
*/
- if (cap_mig->pgsize_bitmap & TARGET_PAGE_SIZE) {
+ if (cap_mig->pgsize_bitmap & qemu_real_host_page_size) {
container->dirty_pages_supported = true;
container->max_dirty_bitmap_size = cap_mig->max_dirty_bitmap_size;
container->dirty_pgsizes = cap_mig->pgsize_bitmap;
diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c
index 134bdccc4f..384576cfc0 100644
--- a/hw/vfio/migration.c
+++ b/hw/vfio/migration.c
@@ -395,40 +395,10 @@ static int vfio_load_device_config_state(QEMUFile *f, void *opaque)
return qemu_file_get_error(f);
}
-static int vfio_set_dirty_page_tracking(VFIODevice *vbasedev, bool start)
-{
- int ret;
- VFIOMigration *migration = vbasedev->migration;
- VFIOContainer *container = vbasedev->group->container;
- struct vfio_iommu_type1_dirty_bitmap dirty = {
- .argsz = sizeof(dirty),
- };
-
- if (start) {
- if (migration->device_state & VFIO_DEVICE_STATE_SAVING) {
- dirty.flags = VFIO_IOMMU_DIRTY_PAGES_FLAG_START;
- } else {
- return -EINVAL;
- }
- } else {
- dirty.flags = VFIO_IOMMU_DIRTY_PAGES_FLAG_STOP;
- }
-
- ret = ioctl(container->fd, VFIO_IOMMU_DIRTY_PAGES, &dirty);
- if (ret) {
- error_report("Failed to set dirty tracking flag 0x%x errno: %d",
- dirty.flags, errno);
- return -errno;
- }
- return ret;
-}
-
static void vfio_migration_cleanup(VFIODevice *vbasedev)
{
VFIOMigration *migration = vbasedev->migration;
- vfio_set_dirty_page_tracking(vbasedev, false);
-
if (migration->region.mmaps) {
vfio_region_unmap(&migration->region);
}
@@ -469,11 +439,6 @@ static int vfio_save_setup(QEMUFile *f, void *opaque)
return ret;
}
- ret = vfio_set_dirty_page_tracking(vbasedev, true);
- if (ret) {
- return ret;
- }
-
qemu_put_be64(f, VFIO_MIG_FLAG_END_OF_STATE);
ret = qemu_file_get_error(f);
@@ -575,11 +540,6 @@ static int vfio_save_complete_precopy(QEMUFile *f, void *opaque)
return ret;
}
- ret = vfio_save_device_config_state(f, opaque);
- if (ret) {
- return ret;
- }
-
ret = vfio_update_pending(vbasedev);
if (ret) {
return ret;
@@ -620,6 +580,19 @@ static int vfio_save_complete_precopy(QEMUFile *f, void *opaque)
return ret;
}
+static void vfio_save_state(QEMUFile *f, void *opaque)
+{
+ VFIODevice *vbasedev = opaque;
+ int ret;
+
+ ret = vfio_save_device_config_state(f, opaque);
+ if (ret) {
+ error_report("%s: Failed to save device config space",
+ vbasedev->name);
+ qemu_file_set_error(f, ret);
+ }
+}
+
static int vfio_load_setup(QEMUFile *f, void *opaque)
{
VFIODevice *vbasedev = opaque;
@@ -670,11 +643,7 @@ static int vfio_load_state(QEMUFile *f, void *opaque, int version_id)
switch (data) {
case VFIO_MIG_FLAG_DEV_CONFIG_STATE:
{
- ret = vfio_load_device_config_state(f, opaque);
- if (ret) {
- return ret;
- }
- break;
+ return vfio_load_device_config_state(f, opaque);
}
case VFIO_MIG_FLAG_DEV_SETUP_STATE:
{
@@ -720,6 +689,7 @@ static SaveVMHandlers savevm_vfio_handlers = {
.save_live_pending = vfio_save_pending,
.save_live_iterate = vfio_save_iterate,
.save_live_complete_precopy = vfio_save_complete_precopy,
+ .save_state = vfio_save_state,
.load_setup = vfio_load_setup,
.load_cleanup = vfio_load_cleanup,
.load_state = vfio_load_state,
@@ -857,7 +827,8 @@ static int vfio_migration_init(VFIODevice *vbasedev,
register_savevm_live(id, VMSTATE_INSTANCE_ID_ANY, 1, &savevm_vfio_handlers,
vbasedev);
- migration->vm_state = qemu_add_vm_change_state_handler(vfio_vmstate_change,
+ migration->vm_state = qdev_add_vm_change_state_handler(vbasedev->dev,
+ vfio_vmstate_change,
vbasedev);
migration->migration_state.notify = vfio_migration_state_notifier;
add_migration_state_change_notifier(&migration->migration_state);
diff --git a/hw/vfio/pci-quirks.c b/hw/vfio/pci-quirks.c
index c5c4c61d01..b90cf3d37c 100644
--- a/hw/vfio/pci-quirks.c
+++ b/hw/vfio/pci-quirks.c
@@ -44,19 +44,19 @@
static const struct {
uint32_t vendor;
uint32_t device;
-} romblacklist[] = {
+} rom_denylist[] = {
{ 0x14e4, 0x168e }, /* Broadcom BCM 57810 */
};
-bool vfio_blacklist_opt_rom(VFIOPCIDevice *vdev)
+bool vfio_opt_rom_in_denylist(VFIOPCIDevice *vdev)
{
int i;
- for (i = 0 ; i < ARRAY_SIZE(romblacklist); i++) {
- if (vfio_pci_is(vdev, romblacklist[i].vendor, romblacklist[i].device)) {
- trace_vfio_quirk_rom_blacklisted(vdev->vbasedev.name,
- romblacklist[i].vendor,
- romblacklist[i].device);
+ for (i = 0 ; i < ARRAY_SIZE(rom_denylist); i++) {
+ if (vfio_pci_is(vdev, rom_denylist[i].vendor, rom_denylist[i].device)) {
+ trace_vfio_quirk_rom_in_denylist(vdev->vbasedev.name,
+ rom_denylist[i].vendor,
+ rom_denylist[i].device);
return true;
}
}
diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c
index f74be78209..5c65aa0a98 100644
--- a/hw/vfio/pci.c
+++ b/hw/vfio/pci.c
@@ -569,6 +569,9 @@ static void vfio_msix_vector_release(PCIDevice *pdev, unsigned int nr)
static void vfio_msix_enable(VFIOPCIDevice *vdev)
{
+ PCIDevice *pdev = &vdev->pdev;
+ unsigned int nr, max_vec = 0;
+
vfio_disable_interrupts(vdev);
vdev->msi_vectors = g_new0(VFIOMSIVector, vdev->msix->entries);
@@ -587,11 +590,22 @@ static void vfio_msix_enable(VFIOPCIDevice *vdev)
* triggering to userspace, then immediately release the vector, leaving
* the physical device with no vectors enabled, but MSI-X enabled, just
* like the guest view.
+ * If there are already unmasked vectors (in migration resume phase and
+ * some guest startups) which will be enabled soon, we can allocate all
+ * of them here to avoid inefficiently disabling and enabling vectors
+ * repeatedly later.
*/
- vfio_msix_vector_do_use(&vdev->pdev, 0, NULL, NULL);
- vfio_msix_vector_release(&vdev->pdev, 0);
+ if (!pdev->msix_function_masked) {
+ for (nr = 0; nr < msix_nr_vectors_allocated(pdev); nr++) {
+ if (!msix_is_masked(pdev, nr)) {
+ max_vec = nr;
+ }
+ }
+ }
+ vfio_msix_vector_do_use(pdev, max_vec, NULL, NULL);
+ vfio_msix_vector_release(pdev, max_vec);
- if (msix_set_vector_notifiers(&vdev->pdev, vfio_msix_vector_use,
+ if (msix_set_vector_notifiers(pdev, vfio_msix_vector_use,
vfio_msix_vector_release, NULL)) {
error_report("vfio: msix_set_vector_notifiers failed");
}
@@ -900,7 +914,7 @@ static void vfio_pci_size_rom(VFIOPCIDevice *vdev)
if (vdev->pdev.romfile || !vdev->pdev.rom_bar) {
/* Since pci handles romfile, just print a message and return */
- if (vfio_blacklist_opt_rom(vdev) && vdev->pdev.romfile) {
+ if (vfio_opt_rom_in_denylist(vdev) && vdev->pdev.romfile) {
warn_report("Device at %s is known to cause system instability"
" issues during option rom execution",
vdev->vbasedev.name);
@@ -927,7 +941,7 @@ static void vfio_pci_size_rom(VFIOPCIDevice *vdev)
return;
}
- if (vfio_blacklist_opt_rom(vdev)) {
+ if (vfio_opt_rom_in_denylist(vdev)) {
if (dev->opts && qemu_opt_get(dev->opts, "rombar")) {
warn_report("Device at %s is known to cause system instability"
" issues during option rom execution",
diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h
index 1574ef983f..64777516d1 100644
--- a/hw/vfio/pci.h
+++ b/hw/vfio/pci.h
@@ -197,7 +197,7 @@ void vfio_pci_write_config(PCIDevice *pdev,
uint64_t vfio_vga_read(void *opaque, hwaddr addr, unsigned size);
void vfio_vga_write(void *opaque, hwaddr addr, uint64_t data, unsigned size);
-bool vfio_blacklist_opt_rom(VFIOPCIDevice *vdev);
+bool vfio_opt_rom_in_denylist(VFIOPCIDevice *vdev);
void vfio_vga_quirk_setup(VFIOPCIDevice *vdev);
void vfio_vga_quirk_exit(VFIOPCIDevice *vdev);
void vfio_vga_quirk_finalize(VFIOPCIDevice *vdev);
diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events
index c0e75f24b7..079f53acf2 100644
--- a/hw/vfio/trace-events
+++ b/hw/vfio/trace-events
@@ -49,7 +49,7 @@ vfio_pci_emulated_sub_vendor_id(const char *name, uint16_t val) "%s 0x%04x"
vfio_pci_emulated_sub_device_id(const char *name, uint16_t val) "%s 0x%04x"
# pci-quirks.c
-vfio_quirk_rom_blacklisted(const char *name, uint16_t vid, uint16_t did) "%s %04x:%04x"
+vfio_quirk_rom_in_denylist(const char *name, uint16_t vid, uint16_t did) "%s %04x:%04x"
vfio_quirk_generic_window_address_write(const char *name, const char * region_name, uint64_t data) "%s %s 0x%"PRIx64
vfio_quirk_generic_window_data_read(const char *name, const char * region_name, uint64_t data) "%s %s 0x%"PRIx64
vfio_quirk_generic_window_data_write(const char *name, const char * region_name, uint64_t data) "%s %s 0x%"PRIx64