Merge remote-tracking branch 'mdroth/qga-pull-10-9-12' into staging
* mdroth/qga-pull-10-9-12:
qemu-ga: ga_open_pidfile(): add new line to pidfile
qemu-ga: use state dir from CONFIG_QEMU_LOCALSTATEDIR
configure: add --localstatedir
diff --git a/MAINTAINERS b/MAINTAINERS
index 78d4ff2..f1f9250 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -268,6 +268,7 @@
F: hw/xilinx_zynq.c
F: hw/zynq_slcr.c
F: hw/cadence_*
+F: hw/xilinx_spips.c
CRIS Machines
-------------
@@ -517,6 +518,12 @@
S: Odd Fixes
F: hw/lsi53c895a.c
+SSI
+M: Peter Crosthwaite <peter.crosthwaite@petalogix.com>
+S: Maintained
+F: hw/ssi.*
+F: hw/m25p80.c
+
USB
M: Gerd Hoffmann <kraxel@redhat.com>
S: Maintained
@@ -565,6 +572,7 @@
F: hw/xilinx_ethlite.c
F: hw/xilinx_timer.c
F: hw/xilinx.h
+F: hw/xilinx_spi.c
Subsystems
----------
diff --git a/configure b/configure
index 6600d88..77b2462 100755
--- a/configure
+++ b/configure
@@ -2721,20 +2721,14 @@
EOF
spice_cflags=$($pkg_config --cflags spice-protocol spice-server 2>/dev/null)
spice_libs=$($pkg_config --libs spice-protocol spice-server 2>/dev/null)
- if $pkg_config --atleast-version=0.8.2 spice-server >/dev/null 2>&1 && \
- $pkg_config --atleast-version=0.8.1 spice-protocol > /dev/null 2>&1 && \
+ if $pkg_config --atleast-version=0.12.0 spice-server >/dev/null 2>&1 && \
+ $pkg_config --atleast-version=0.12.2 spice-protocol > /dev/null 2>&1 && \
compile_prog "$spice_cflags" "$spice_libs" ; then
spice="yes"
libs_softmmu="$libs_softmmu $spice_libs"
QEMU_CFLAGS="$QEMU_CFLAGS $spice_cflags"
spice_protocol_version=$($pkg_config --modversion spice-protocol)
spice_server_version=$($pkg_config --modversion spice-server)
- if $pkg_config --atleast-version=0.12.0 spice-protocol >/dev/null 2>&1; then
- spice_qxl_io_monitors_config_async="yes"
- fi
- if $pkg_config --atleast-version=0.12.2 spice-protocol > /dev/null 2>&1; then
- spice_qxl_client_monitors_config="yes"
- fi
else
if test "$spice" = "yes" ; then
feature_not_found "spice"
@@ -3481,14 +3475,6 @@
echo "CONFIG_SPICE=y" >> $config_host_mak
fi
-if test "$spice_qxl_io_monitors_config_async" = "yes" ; then
- echo "CONFIG_QXL_IO_MONITORS_CONFIG_ASYNC=y" >> $config_host_mak
-fi
-
-if test "$spice_qxl_client_monitors_config" = "yes" ; then
- echo "CONFIG_QXL_CLIENT_MONITORS_CONFIG=y" >> $config_host_mak
-fi
-
if test "$smartcard" = "yes" ; then
echo "CONFIG_SMARTCARD=y" >> $config_host_mak
fi
diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
index f335a72..2f1a5c9 100644
--- a/default-configs/arm-softmmu.mak
+++ b/default-configs/arm-softmmu.mak
@@ -22,6 +22,7 @@
CONFIG_MAX111X=y
CONFIG_SSI=y
CONFIG_SSI_SD=y
+CONFIG_SSI_M25P80=y
CONFIG_LAN9118=y
CONFIG_SMC91C111=y
CONFIG_DS1338=y
diff --git a/default-configs/microblaze-softmmu.mak b/default-configs/microblaze-softmmu.mak
index 64c9485..2f442e5 100644
--- a/default-configs/microblaze-softmmu.mak
+++ b/default-configs/microblaze-softmmu.mak
@@ -5,3 +5,5 @@
CONFIG_SERIAL=y
CONFIG_XILINX=y
CONFIG_XILINX_AXI=y
+CONFIG_SSI=y
+CONFIG_SSI_M25P80=y
diff --git a/default-configs/microblazeel-softmmu.mak b/default-configs/microblazeel-softmmu.mak
index a962276..af9a3cd 100644
--- a/default-configs/microblazeel-softmmu.mak
+++ b/default-configs/microblazeel-softmmu.mak
@@ -5,3 +5,5 @@
CONFIG_SERIAL=y
CONFIG_XILINX=y
CONFIG_XILINX_AXI=y
+CONFIG_SSI=y
+CONFIG_SSI_M25P80=y
diff --git a/hw/Makefile.objs b/hw/Makefile.objs
index b59c749..854faa9 100644
--- a/hw/Makefile.objs
+++ b/hw/Makefile.objs
@@ -35,6 +35,7 @@
common-obj-$(CONFIG_SMARTCARD) += ccid-card-passthru.o
common-obj-$(CONFIG_SMARTCARD_NSS) += ccid-card-emulated.o
common-obj-$(CONFIG_I8259) += i8259_common.o i8259.o
+common-obj-y += fifo.o
# PPC devices
common-obj-$(CONFIG_PREP_PCI) += prep_pci.o
@@ -173,6 +174,7 @@
common-obj-y += scsi-generic.o scsi-bus.o
common-obj-y += hid.o
common-obj-$(CONFIG_SSI) += ssi.o
+common-obj-$(CONFIG_SSI_M25P80) += m25p80.o
common-obj-$(CONFIG_SSI_SD) += ssi-sd.o
common-obj-$(CONFIG_SD) += sd.o
common-obj-y += bt.o bt-l2cap.o bt-sdp.o bt-hci.o bt-hid.o
diff --git a/hw/ads7846.c b/hw/ads7846.c
index 41c7f10..2ea9e55 100644
--- a/hw/ads7846.c
+++ b/hw/ads7846.c
@@ -119,11 +119,12 @@
static const VMStateDescription vmstate_ads7846 = {
.name = "ads7846",
- .version_id = 0,
- .minimum_version_id = 0,
- .minimum_version_id_old = 0,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
.post_load = ads7856_post_load,
.fields = (VMStateField[]) {
+ VMSTATE_SSI_SLAVE(ssidev, ADS7846State),
VMSTATE_INT32_ARRAY(input, ADS7846State, 8),
VMSTATE_INT32(noise, ADS7846State),
VMSTATE_INT32(cycle, ADS7846State),
diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 2b39fb3..6d049e7 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -1,6 +1,7 @@
obj-y = integratorcp.o versatilepb.o arm_pic.o
obj-y += arm_boot.o
obj-y += xilinx_zynq.o zynq_slcr.o
+obj-y += xilinx_spips.o
obj-y += arm_gic.o arm_gic_common.o
obj-y += realview_gic.o realview.o arm_sysctl.o arm11mpcore.o a9mpcore.o
obj-y += exynos4210_gic.o exynos4210_combiner.o exynos4210.o
diff --git a/hw/e1000.c b/hw/e1000.c
index ec3a7c4..63fee10 100644
--- a/hw/e1000.c
+++ b/hw/e1000.c
@@ -1079,11 +1079,23 @@
return version_id == 1;
}
+static int e1000_post_load(void *opaque, int version_id)
+{
+ E1000State *s = opaque;
+
+ /* nc.link_down can't be migrated, so infer link_down according
+ * to link status bit in mac_reg[STATUS] */
+ s->nic->nc.link_down = (s->mac_reg[STATUS] & E1000_STATUS_LU) == 0;
+
+ return 0;
+}
+
static const VMStateDescription vmstate_e1000 = {
.name = "e1000",
.version_id = 2,
.minimum_version_id = 1,
.minimum_version_id_old = 1,
+ .post_load = e1000_post_load,
.fields = (VMStateField []) {
VMSTATE_PCI_DEVICE(dev, E1000State),
VMSTATE_UNUSED_TEST(is_version_1, 4), /* was instance id */
diff --git a/hw/fifo.c b/hw/fifo.c
new file mode 100644
index 0000000..68a955a
--- /dev/null
+++ b/hw/fifo.c
@@ -0,0 +1,78 @@
+/*
+ * Generic FIFO component, implemented as a circular buffer.
+ *
+ * Copyright (c) 2012 Peter A. G. Crosthwaite
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "fifo.h"
+
+void fifo8_create(Fifo8 *fifo, uint32_t capacity)
+{
+ fifo->data = g_new(uint8_t, capacity);
+ fifo->capacity = capacity;
+ fifo->head = 0;
+ fifo->num = 0;
+}
+
+void fifo8_destroy(Fifo8 *fifo)
+{
+ g_free(fifo->data);
+}
+
+void fifo8_push(Fifo8 *fifo, uint8_t data)
+{
+ if (fifo->num == fifo->capacity) {
+ abort();
+ }
+ fifo->data[(fifo->head + fifo->num) % fifo->capacity] = data;
+ fifo->num++;
+}
+
+uint8_t fifo8_pop(Fifo8 *fifo)
+{
+ uint8_t ret;
+
+ if (fifo->num == 0) {
+ abort();
+ }
+ ret = fifo->data[fifo->head++];
+ fifo->head %= fifo->capacity;
+ fifo->num--;
+ return ret;
+}
+
+void fifo8_reset(Fifo8 *fifo)
+{
+ fifo->num = 0;
+}
+
+bool fifo8_is_empty(Fifo8 *fifo)
+{
+ return (fifo->num == 0);
+}
+
+bool fifo8_is_full(Fifo8 *fifo)
+{
+ return (fifo->num == fifo->capacity);
+}
+
+const VMStateDescription vmstate_fifo8 = {
+ .name = "Fifo8",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_VBUFFER_UINT32(data, Fifo8, 1, NULL, 0, capacity),
+ VMSTATE_UINT32(head, Fifo8),
+ VMSTATE_UINT32(num, Fifo8),
+ VMSTATE_END_OF_LIST()
+ }
+};
diff --git a/hw/fifo.h b/hw/fifo.h
new file mode 100644
index 0000000..f23890a
--- /dev/null
+++ b/hw/fifo.h
@@ -0,0 +1,99 @@
+#ifndef FIFO_H
+#define FIFO_H
+
+#include "hw.h"
+
+typedef struct {
+ /* All fields are private */
+ uint8_t *data;
+ uint32_t capacity;
+ uint32_t head;
+ uint32_t num;
+} Fifo8;
+
+/**
+ * fifo8_create:
+ * @fifo: struct Fifo8 to initialise with new FIFO
+ * @capacity: capacity of the newly created FIFO
+ *
+ * Create a FIFO of the specified size. Clients should call fifo8_destroy()
+ * when finished using the fifo. The FIFO is initially empty.
+ */
+
+void fifo8_create(Fifo8 *fifo, uint32_t capacity);
+
+/**
+ * fifo8_destroy:
+ * @fifo: FIFO to cleanup
+ *
+ * Cleanup a FIFO created with fifo8_create(). Frees memory created for FIFO
+ *storage. The FIFO is no longer usable after this has been called.
+ */
+
+void fifo8_destroy(Fifo8 *fifo);
+
+/**
+ * fifo8_push:
+ * @fifo: FIFO to push to
+ * @data: data byte to push
+ *
+ * Push a data byte to the FIFO. Behaviour is undefined if the FIFO is full.
+ * Clients are responsible for checking for fullness using fifo8_is_full().
+ */
+
+void fifo8_push(Fifo8 *fifo, uint8_t data);
+
+/**
+ * fifo8_pop:
+ * @fifo: fifo to pop from
+ *
+ * Pop a data byte from the FIFO. Behaviour is undefined if the FIFO is empty.
+ * Clients are responsible for checking for emptyness using fifo8_is_empty().
+ *
+ * Returns: The popped data byte.
+ */
+
+uint8_t fifo8_pop(Fifo8 *fifo);
+
+/**
+ * fifo8_reset:
+ * @fifo: FIFO to reset
+ *
+ * Reset a FIFO. All data is discarded and the FIFO is emptied.
+ */
+
+void fifo8_reset(Fifo8 *fifo);
+
+/**
+ * fifo8_is_empty:
+ * @fifo: FIFO to check
+ *
+ * Check if a FIFO is empty.
+ *
+ * Returns: True if the fifo is empty, false otherwise.
+ */
+
+bool fifo8_is_empty(Fifo8 *fifo);
+
+/**
+ * fifo8_is_full:
+ * @fifo: FIFO to check
+ *
+ * Check if a FIFO is full.
+ *
+ * Returns: True if the fifo is full, false otherwise.
+ */
+
+bool fifo8_is_full(Fifo8 *fifo);
+
+extern const VMStateDescription vmstate_fifo8;
+
+#define VMSTATE_FIFO8(_field, _state) { \
+ .name = (stringify(_field)), \
+ .size = sizeof(Fifo8), \
+ .vmsd = &vmstate_fifo8, \
+ .flags = VMS_STRUCT, \
+ .offset = vmstate_offset_value(_state, _field, Fifo8), \
+}
+
+#endif /* FIFO_H */
diff --git a/hw/irq.c b/hw/irq.c
index d413a0b..f4e2a78 100644
--- a/hw/irq.c
+++ b/hw/irq.c
@@ -38,24 +38,37 @@
irq->handler(irq->opaque, irq->n, level);
}
-qemu_irq *qemu_allocate_irqs(qemu_irq_handler handler, void *opaque, int n)
+qemu_irq *qemu_extend_irqs(qemu_irq *old, int n_old, qemu_irq_handler handler,
+ void *opaque, int n)
{
qemu_irq *s;
struct IRQState *p;
int i;
- s = (qemu_irq *)g_malloc0(sizeof(qemu_irq) * n);
- p = (struct IRQState *)g_malloc0(sizeof(struct IRQState) * n);
- for (i = 0; i < n; i++) {
- p->handler = handler;
- p->opaque = opaque;
- p->n = i;
+ if (!old) {
+ n_old = 0;
+ }
+ s = old ? g_renew(qemu_irq, old, n + n_old) : g_new(qemu_irq, n);
+ p = old ? g_renew(struct IRQState, s[0], n + n_old) :
+ g_new(struct IRQState, n);
+ for (i = 0; i < n + n_old; i++) {
+ if (i >= n_old) {
+ p->handler = handler;
+ p->opaque = opaque;
+ p->n = i;
+ }
s[i] = p;
p++;
}
return s;
}
+qemu_irq *qemu_allocate_irqs(qemu_irq_handler handler, void *opaque, int n)
+{
+ return qemu_extend_irqs(NULL, 0, handler, opaque, n);
+}
+
+
void qemu_free_irqs(qemu_irq *s)
{
g_free(s[0]);
diff --git a/hw/irq.h b/hw/irq.h
index 56c55f0..e640c10 100644
--- a/hw/irq.h
+++ b/hw/irq.h
@@ -23,8 +23,17 @@
qemu_set_irq(irq, 0);
}
-/* Returns an array of N IRQs. */
+/* Returns an array of N IRQs. Each IRQ is assigned the argument handler and
+ * opaque data.
+ */
qemu_irq *qemu_allocate_irqs(qemu_irq_handler handler, void *opaque, int n);
+
+/* Extends an Array of IRQs. Old IRQs have their handlers and opaque data
+ * preserved. New IRQs are assigned the argument handler and opaque data.
+ */
+qemu_irq *qemu_extend_irqs(qemu_irq *old, int n_old, qemu_irq_handler handler,
+ void *opaque, int n);
+
void qemu_free_irqs(qemu_irq *s);
/* Returns a new IRQ with opposite polarity. */
diff --git a/hw/m25p80.c b/hw/m25p80.c
new file mode 100644
index 0000000..9a56de8
--- /dev/null
+++ b/hw/m25p80.c
@@ -0,0 +1,598 @@
+/*
+ * ST M25P80 emulator. Emulate all SPI flash devices based on the m25p80 command
+ * set. Known devices table current as of Jun/2012 and taken from linux.
+ * See drivers/mtd/devices/m25p80.c.
+ *
+ * Copyright (C) 2011 Edgar E. Iglesias <edgar.iglesias@gmail.com>
+ * Copyright (C) 2012 Peter A. G. Crosthwaite <peter.crosthwaite@petalogix.com>
+ * Copyright (C) 2012 PetaLogix
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) a later version of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hw.h"
+#include "blockdev.h"
+#include "ssi.h"
+#include "devices.h"
+
+#ifdef M25P80_ERR_DEBUG
+#define DB_PRINT(...) do { \
+ fprintf(stderr, ": %s: ", __func__); \
+ fprintf(stderr, ## __VA_ARGS__); \
+ } while (0);
+#else
+ #define DB_PRINT(...)
+#endif
+
+/* Fields for FlashPartInfo->flags */
+
+/* erase capabilities */
+#define ER_4K 1
+#define ER_32K 2
+/* set to allow the page program command to write 0s back to 1. Useful for
+ * modelling EEPROM with SPI flash command set
+ */
+#define WR_1 0x100
+
+typedef struct FlashPartInfo {
+ const char *part_name;
+ /* jedec code. (jedec >> 16) & 0xff is the 1st byte, >> 8 the 2nd etc */
+ uint32_t jedec;
+ /* extended jedec code */
+ uint16_t ext_jedec;
+ /* there is confusion between manufacturers as to what a sector is. In this
+ * device model, a "sector" is the size that is erased by the ERASE_SECTOR
+ * command (opcode 0xd8).
+ */
+ uint32_t sector_size;
+ uint32_t n_sectors;
+ uint32_t page_size;
+ uint8_t flags;
+} FlashPartInfo;
+
+/* adapted from linux */
+
+#define INFO(_part_name, _jedec, _ext_jedec, _sector_size, _n_sectors, _flags)\
+ .part_name = (_part_name),\
+ .jedec = (_jedec),\
+ .ext_jedec = (_ext_jedec),\
+ .sector_size = (_sector_size),\
+ .n_sectors = (_n_sectors),\
+ .page_size = 256,\
+ .flags = (_flags),\
+
+static const FlashPartInfo known_devices[] = {
+ /* Atmel -- some are (confusingly) marketed as "DataFlash" */
+ { INFO("at25fs010", 0x1f6601, 0, 32 << 10, 4, ER_4K) },
+ { INFO("at25fs040", 0x1f6604, 0, 64 << 10, 8, ER_4K) },
+
+ { INFO("at25df041a", 0x1f4401, 0, 64 << 10, 8, ER_4K) },
+ { INFO("at25df321a", 0x1f4701, 0, 64 << 10, 64, ER_4K) },
+ { INFO("at25df641", 0x1f4800, 0, 64 << 10, 128, ER_4K) },
+
+ { INFO("at26f004", 0x1f0400, 0, 64 << 10, 8, ER_4K) },
+ { INFO("at26df081a", 0x1f4501, 0, 64 << 10, 16, ER_4K) },
+ { INFO("at26df161a", 0x1f4601, 0, 64 << 10, 32, ER_4K) },
+ { INFO("at26df321", 0x1f4700, 0, 64 << 10, 64, ER_4K) },
+
+ /* EON -- en25xxx */
+ { INFO("en25f32", 0x1c3116, 0, 64 << 10, 64, ER_4K) },
+ { INFO("en25p32", 0x1c2016, 0, 64 << 10, 64, 0) },
+ { INFO("en25q32b", 0x1c3016, 0, 64 << 10, 64, 0) },
+ { INFO("en25p64", 0x1c2017, 0, 64 << 10, 128, 0) },
+
+ /* Intel/Numonyx -- xxxs33b */
+ { INFO("160s33b", 0x898911, 0, 64 << 10, 32, 0) },
+ { INFO("320s33b", 0x898912, 0, 64 << 10, 64, 0) },
+ { INFO("640s33b", 0x898913, 0, 64 << 10, 128, 0) },
+
+ /* Macronix */
+ { INFO("mx25l4005a", 0xc22013, 0, 64 << 10, 8, ER_4K) },
+ { INFO("mx25l8005", 0xc22014, 0, 64 << 10, 16, 0) },
+ { INFO("mx25l1606e", 0xc22015, 0, 64 << 10, 32, ER_4K) },
+ { INFO("mx25l3205d", 0xc22016, 0, 64 << 10, 64, 0) },
+ { INFO("mx25l6405d", 0xc22017, 0, 64 << 10, 128, 0) },
+ { INFO("mx25l12805d", 0xc22018, 0, 64 << 10, 256, 0) },
+ { INFO("mx25l12855e", 0xc22618, 0, 64 << 10, 256, 0) },
+ { INFO("mx25l25635e", 0xc22019, 0, 64 << 10, 512, 0) },
+ { INFO("mx25l25655e", 0xc22619, 0, 64 << 10, 512, 0) },
+
+ /* Spansion -- single (large) sector size only, at least
+ * for the chips listed here (without boot sectors).
+ */
+ { INFO("s25sl004a", 0x010212, 0, 64 << 10, 8, 0) },
+ { INFO("s25sl008a", 0x010213, 0, 64 << 10, 16, 0) },
+ { INFO("s25sl016a", 0x010214, 0, 64 << 10, 32, 0) },
+ { INFO("s25sl032a", 0x010215, 0, 64 << 10, 64, 0) },
+ { INFO("s25sl032p", 0x010215, 0x4d00, 64 << 10, 64, ER_4K) },
+ { INFO("s25sl064a", 0x010216, 0, 64 << 10, 128, 0) },
+ { INFO("s25fl256s0", 0x010219, 0x4d00, 256 << 10, 128, 0) },
+ { INFO("s25fl256s1", 0x010219, 0x4d01, 64 << 10, 512, 0) },
+ { INFO("s25fl512s", 0x010220, 0x4d00, 256 << 10, 256, 0) },
+ { INFO("s70fl01gs", 0x010221, 0x4d00, 256 << 10, 256, 0) },
+ { INFO("s25sl12800", 0x012018, 0x0300, 256 << 10, 64, 0) },
+ { INFO("s25sl12801", 0x012018, 0x0301, 64 << 10, 256, 0) },
+ { INFO("s25fl129p0", 0x012018, 0x4d00, 256 << 10, 64, 0) },
+ { INFO("s25fl129p1", 0x012018, 0x4d01, 64 << 10, 256, 0) },
+ { INFO("s25fl016k", 0xef4015, 0, 64 << 10, 32, ER_4K | ER_32K) },
+ { INFO("s25fl064k", 0xef4017, 0, 64 << 10, 128, ER_4K | ER_32K) },
+
+ /* SST -- large erase sizes are "overlays", "sectors" are 4<< 10 */
+ { INFO("sst25vf040b", 0xbf258d, 0, 64 << 10, 8, ER_4K) },
+ { INFO("sst25vf080b", 0xbf258e, 0, 64 << 10, 16, ER_4K) },
+ { INFO("sst25vf016b", 0xbf2541, 0, 64 << 10, 32, ER_4K) },
+ { INFO("sst25vf032b", 0xbf254a, 0, 64 << 10, 64, ER_4K) },
+ { INFO("sst25wf512", 0xbf2501, 0, 64 << 10, 1, ER_4K) },
+ { INFO("sst25wf010", 0xbf2502, 0, 64 << 10, 2, ER_4K) },
+ { INFO("sst25wf020", 0xbf2503, 0, 64 << 10, 4, ER_4K) },
+ { INFO("sst25wf040", 0xbf2504, 0, 64 << 10, 8, ER_4K) },
+
+ /* ST Microelectronics -- newer production may have feature updates */
+ { INFO("m25p05", 0x202010, 0, 32 << 10, 2, 0) },
+ { INFO("m25p10", 0x202011, 0, 32 << 10, 4, 0) },
+ { INFO("m25p20", 0x202012, 0, 64 << 10, 4, 0) },
+ { INFO("m25p40", 0x202013, 0, 64 << 10, 8, 0) },
+ { INFO("m25p80", 0x202014, 0, 64 << 10, 16, 0) },
+ { INFO("m25p16", 0x202015, 0, 64 << 10, 32, 0) },
+ { INFO("m25p32", 0x202016, 0, 64 << 10, 64, 0) },
+ { INFO("m25p64", 0x202017, 0, 64 << 10, 128, 0) },
+ { INFO("m25p128", 0x202018, 0, 256 << 10, 64, 0) },
+
+ { INFO("m45pe10", 0x204011, 0, 64 << 10, 2, 0) },
+ { INFO("m45pe80", 0x204014, 0, 64 << 10, 16, 0) },
+ { INFO("m45pe16", 0x204015, 0, 64 << 10, 32, 0) },
+
+ { INFO("m25pe80", 0x208014, 0, 64 << 10, 16, 0) },
+ { INFO("m25pe16", 0x208015, 0, 64 << 10, 32, ER_4K) },
+
+ { INFO("m25px32", 0x207116, 0, 64 << 10, 64, ER_4K) },
+ { INFO("m25px32-s0", 0x207316, 0, 64 << 10, 64, ER_4K) },
+ { INFO("m25px32-s1", 0x206316, 0, 64 << 10, 64, ER_4K) },
+ { INFO("m25px64", 0x207117, 0, 64 << 10, 128, 0) },
+
+ /* Winbond -- w25x "blocks" are 64k, "sectors" are 4KiB */
+ { INFO("w25x10", 0xef3011, 0, 64 << 10, 2, ER_4K) },
+ { INFO("w25x20", 0xef3012, 0, 64 << 10, 4, ER_4K) },
+ { INFO("w25x40", 0xef3013, 0, 64 << 10, 8, ER_4K) },
+ { INFO("w25x80", 0xef3014, 0, 64 << 10, 16, ER_4K) },
+ { INFO("w25x16", 0xef3015, 0, 64 << 10, 32, ER_4K) },
+ { INFO("w25x32", 0xef3016, 0, 64 << 10, 64, ER_4K) },
+ { INFO("w25q32", 0xef4016, 0, 64 << 10, 64, ER_4K) },
+ { INFO("w25x64", 0xef3017, 0, 64 << 10, 128, ER_4K) },
+ { INFO("w25q64", 0xef4017, 0, 64 << 10, 128, ER_4K) },
+
+ /* Numonyx -- n25q128 */
+ { INFO("n25q128", 0x20ba18, 0, 64 << 10, 256, 0) },
+
+ { },
+};
+
+typedef enum {
+ NOP = 0,
+ PP = 0x2,
+ READ = 0x3,
+ WRDI = 0x4,
+ RDSR = 0x5,
+ WREN = 0x6,
+ FAST_READ = 0xb,
+ ERASE_4K = 0x20,
+ ERASE_32K = 0x52,
+ ERASE_SECTOR = 0xd8,
+ JEDEC_READ = 0x9f,
+ BULK_ERASE = 0xc7,
+} FlashCMD;
+
+typedef enum {
+ STATE_IDLE,
+ STATE_PAGE_PROGRAM,
+ STATE_READ,
+ STATE_COLLECTING_DATA,
+ STATE_READING_DATA,
+} CMDState;
+
+typedef struct Flash {
+ SSISlave ssidev;
+ uint32_t r;
+
+ BlockDriverState *bdrv;
+
+ uint8_t *storage;
+ uint32_t size;
+ int page_size;
+
+ uint8_t state;
+ uint8_t data[16];
+ uint32_t len;
+ uint32_t pos;
+ uint8_t needed_bytes;
+ uint8_t cmd_in_progress;
+ uint64_t cur_addr;
+ bool write_enable;
+
+ int64_t dirty_page;
+
+ char *part_name;
+ const FlashPartInfo *pi;
+
+} Flash;
+
+static void bdrv_sync_complete(void *opaque, int ret)
+{
+ /* do nothing. Masters do not directly interact with the backing store,
+ * only the working copy so no mutexing required.
+ */
+}
+
+static void flash_sync_page(Flash *s, int page)
+{
+ if (s->bdrv) {
+ int bdrv_sector, nb_sectors;
+ QEMUIOVector iov;
+
+ bdrv_sector = (page * s->pi->page_size) / BDRV_SECTOR_SIZE;
+ nb_sectors = DIV_ROUND_UP(s->pi->page_size, BDRV_SECTOR_SIZE);
+ qemu_iovec_init(&iov, 1);
+ qemu_iovec_add(&iov, s->storage + bdrv_sector * BDRV_SECTOR_SIZE,
+ nb_sectors * BDRV_SECTOR_SIZE);
+ bdrv_aio_writev(s->bdrv, bdrv_sector, &iov, nb_sectors,
+ bdrv_sync_complete, NULL);
+ }
+}
+
+static inline void flash_sync_area(Flash *s, int64_t off, int64_t len)
+{
+ int64_t start, end, nb_sectors;
+ QEMUIOVector iov;
+
+ if (!s->bdrv) {
+ return;
+ }
+
+ assert(!(len % BDRV_SECTOR_SIZE));
+ start = off / BDRV_SECTOR_SIZE;
+ end = (off + len) / BDRV_SECTOR_SIZE;
+ nb_sectors = end - start;
+ qemu_iovec_init(&iov, 1);
+ qemu_iovec_add(&iov, s->storage + (start * BDRV_SECTOR_SIZE),
+ nb_sectors * BDRV_SECTOR_SIZE);
+ bdrv_aio_writev(s->bdrv, start, &iov, nb_sectors, bdrv_sync_complete, NULL);
+}
+
+static void flash_erase(Flash *s, int offset, FlashCMD cmd)
+{
+ uint32_t len;
+ uint8_t capa_to_assert = 0;
+
+ switch (cmd) {
+ case ERASE_4K:
+ len = 4 << 10;
+ capa_to_assert = ER_4K;
+ break;
+ case ERASE_32K:
+ len = 32 << 10;
+ capa_to_assert = ER_32K;
+ break;
+ case ERASE_SECTOR:
+ len = s->pi->sector_size;
+ break;
+ case BULK_ERASE:
+ len = s->size;
+ break;
+ default:
+ abort();
+ }
+
+ DB_PRINT("offset = %#x, len = %d\n", offset, len);
+ if ((s->pi->flags & capa_to_assert) != capa_to_assert) {
+ hw_error("m25p80: %dk erase size not supported by device\n", len);
+ }
+
+ if (!s->write_enable) {
+ DB_PRINT("erase with write protect!\n");
+ return;
+ }
+ memset(s->storage + offset, 0xff, len);
+ flash_sync_area(s, offset, len);
+}
+
+static inline void flash_sync_dirty(Flash *s, int64_t newpage)
+{
+ if (s->dirty_page >= 0 && s->dirty_page != newpage) {
+ flash_sync_page(s, s->dirty_page);
+ s->dirty_page = newpage;
+ }
+}
+
+static inline
+void flash_write8(Flash *s, uint64_t addr, uint8_t data)
+{
+ int64_t page = addr / s->pi->page_size;
+ uint8_t prev = s->storage[s->cur_addr];
+
+ if (!s->write_enable) {
+ DB_PRINT("write with write protect!\n");
+ }
+
+ if ((prev ^ data) & data) {
+ DB_PRINT("programming zero to one! addr=%lx %x -> %x\n",
+ addr, prev, data);
+ }
+
+ if (s->pi->flags & WR_1) {
+ s->storage[s->cur_addr] = data;
+ } else {
+ s->storage[s->cur_addr] &= data;
+ }
+
+ flash_sync_dirty(s, page);
+ s->dirty_page = page;
+}
+
+static void complete_collecting_data(Flash *s)
+{
+ s->cur_addr = s->data[0] << 16;
+ s->cur_addr |= s->data[1] << 8;
+ s->cur_addr |= s->data[2];
+
+ switch (s->cmd_in_progress) {
+ case PP:
+ s->state = STATE_PAGE_PROGRAM;
+ break;
+ case READ:
+ case FAST_READ:
+ s->state = STATE_READ;
+ break;
+ case ERASE_4K:
+ case ERASE_32K:
+ case ERASE_SECTOR:
+ flash_erase(s, s->cur_addr, s->cmd_in_progress);
+ break;
+ default:
+ break;
+ }
+}
+
+static void decode_new_cmd(Flash *s, uint32_t value)
+{
+ s->cmd_in_progress = value;
+ DB_PRINT("decoded new command:%x\n", value);
+
+ switch (value) {
+
+ case ERASE_4K:
+ case ERASE_32K:
+ case ERASE_SECTOR:
+ case READ:
+ case PP:
+ s->needed_bytes = 3;
+ s->pos = 0;
+ s->len = 0;
+ s->state = STATE_COLLECTING_DATA;
+ break;
+
+ case FAST_READ:
+ s->needed_bytes = 4;
+ s->pos = 0;
+ s->len = 0;
+ s->state = STATE_COLLECTING_DATA;
+ break;
+
+ case WRDI:
+ s->write_enable = false;
+ break;
+ case WREN:
+ s->write_enable = true;
+ break;
+
+ case RDSR:
+ s->data[0] = (!!s->write_enable) << 1;
+ s->pos = 0;
+ s->len = 1;
+ s->state = STATE_READING_DATA;
+ break;
+
+ case JEDEC_READ:
+ DB_PRINT("populated jedec code\n");
+ s->data[0] = (s->pi->jedec >> 16) & 0xff;
+ s->data[1] = (s->pi->jedec >> 8) & 0xff;
+ s->data[2] = s->pi->jedec & 0xff;
+ if (s->pi->ext_jedec) {
+ s->data[3] = (s->pi->ext_jedec >> 8) & 0xff;
+ s->data[4] = s->pi->ext_jedec & 0xff;
+ s->len = 5;
+ } else {
+ s->len = 3;
+ }
+ s->pos = 0;
+ s->state = STATE_READING_DATA;
+ break;
+
+ case BULK_ERASE:
+ if (s->write_enable) {
+ DB_PRINT("chip erase\n");
+ flash_erase(s, 0, BULK_ERASE);
+ } else {
+ DB_PRINT("chip erase with write protect!\n");
+ }
+ break;
+ case NOP:
+ break;
+ default:
+ DB_PRINT("Unknown cmd %x\n", value);
+ break;
+ }
+}
+
+static int m25p80_cs(SSISlave *ss, bool select)
+{
+ Flash *s = FROM_SSI_SLAVE(Flash, ss);
+
+ if (select) {
+ s->len = 0;
+ s->pos = 0;
+ s->state = STATE_IDLE;
+ flash_sync_dirty(s, -1);
+ }
+
+ DB_PRINT("%sselect\n", select ? "de" : "");
+
+ return 0;
+}
+
+static uint32_t m25p80_transfer8(SSISlave *ss, uint32_t tx)
+{
+ Flash *s = FROM_SSI_SLAVE(Flash, ss);
+ uint32_t r = 0;
+
+ switch (s->state) {
+
+ case STATE_PAGE_PROGRAM:
+ DB_PRINT("page program cur_addr=%lx data=%x\n", s->cur_addr,
+ (uint8_t)tx);
+ flash_write8(s, s->cur_addr, (uint8_t)tx);
+ s->cur_addr++;
+ break;
+
+ case STATE_READ:
+ r = s->storage[s->cur_addr];
+ DB_PRINT("READ 0x%lx=%x\n", s->cur_addr, r);
+ s->cur_addr = (s->cur_addr + 1) % s->size;
+ break;
+
+ case STATE_COLLECTING_DATA:
+ s->data[s->len] = (uint8_t)tx;
+ s->len++;
+
+ if (s->len == s->needed_bytes) {
+ complete_collecting_data(s);
+ }
+ break;
+
+ case STATE_READING_DATA:
+ r = s->data[s->pos];
+ s->pos++;
+ if (s->pos == s->len) {
+ s->pos = 0;
+ s->state = STATE_IDLE;
+ }
+ break;
+
+ default:
+ case STATE_IDLE:
+ decode_new_cmd(s, (uint8_t)tx);
+ break;
+ }
+
+ return r;
+}
+
+static int m25p80_init(SSISlave *ss)
+{
+ DriveInfo *dinfo;
+ Flash *s = FROM_SSI_SLAVE(Flash, ss);
+ const FlashPartInfo *i;
+
+ if (!s->part_name) { /* default to actual m25p80 if no partname given */
+ s->part_name = (char *)"m25p80";
+ }
+
+ i = known_devices;
+ for (i = known_devices;; i++) {
+ assert(i);
+ if (!i->part_name) {
+ fprintf(stderr, "Unknown SPI flash part: \"%s\"\n", s->part_name);
+ return 1;
+ } else if (!strcmp(i->part_name, s->part_name)) {
+ s->pi = i;
+ break;
+ }
+ }
+
+ s->size = s->pi->sector_size * s->pi->n_sectors;
+ s->dirty_page = -1;
+ s->storage = qemu_blockalign(s->bdrv, s->size);
+
+ dinfo = drive_get_next(IF_MTD);
+
+ if (dinfo && dinfo->bdrv) {
+ DB_PRINT("Binding to IF_MTD drive\n");
+ s->bdrv = dinfo->bdrv;
+ /* FIXME: Move to late init */
+ if (bdrv_read(s->bdrv, 0, s->storage, DIV_ROUND_UP(s->size,
+ BDRV_SECTOR_SIZE))) {
+ fprintf(stderr, "Failed to initialize SPI flash!\n");
+ return 1;
+ }
+ } else {
+ memset(s->storage, 0xFF, s->size);
+ }
+
+ return 0;
+}
+
+static void m25p80_pre_save(void *opaque)
+{
+ flash_sync_dirty((Flash *)opaque, -1);
+}
+
+static const VMStateDescription vmstate_m25p80 = {
+ .name = "xilinx_spi",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .pre_save = m25p80_pre_save,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8(state, Flash),
+ VMSTATE_UINT8_ARRAY(data, Flash, 16),
+ VMSTATE_UINT32(len, Flash),
+ VMSTATE_UINT32(pos, Flash),
+ VMSTATE_UINT8(needed_bytes, Flash),
+ VMSTATE_UINT8(cmd_in_progress, Flash),
+ VMSTATE_UINT64(cur_addr, Flash),
+ VMSTATE_BOOL(write_enable, Flash),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static Property m25p80_properties[] = {
+ DEFINE_PROP_STRING("partname", Flash, part_name),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void m25p80_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SSISlaveClass *k = SSI_SLAVE_CLASS(klass);
+
+ k->init = m25p80_init;
+ k->transfer = m25p80_transfer8;
+ k->set_cs = m25p80_cs;
+ k->cs_polarity = SSI_CS_LOW;
+ dc->props = m25p80_properties;
+ dc->vmsd = &vmstate_m25p80;
+}
+
+static const TypeInfo m25p80_info = {
+ .name = "m25p80",
+ .parent = TYPE_SSI_SLAVE,
+ .instance_size = sizeof(Flash),
+ .class_init = m25p80_class_init,
+};
+
+static void m25p80_register_types(void)
+{
+ type_register_static(&m25p80_info);
+}
+
+type_init(m25p80_register_types)
diff --git a/hw/max111x.c b/hw/max111x.c
index 706d89f..67640f1 100644
--- a/hw/max111x.c
+++ b/hw/max111x.c
@@ -99,10 +99,11 @@
static const VMStateDescription vmstate_max111x = {
.name = "max111x",
- .version_id = 0,
- .minimum_version_id = 0,
- .minimum_version_id_old = 0,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
.fields = (VMStateField[]) {
+ VMSTATE_SSI_SLAVE(ssidev, MAX111xState),
VMSTATE_UINT8(tb1, MAX111xState),
VMSTATE_UINT8(rb2, MAX111xState),
VMSTATE_UINT8(rb3, MAX111xState),
diff --git a/hw/microblaze/Makefile.objs b/hw/microblaze/Makefile.objs
index 274d2c5..3028e65 100644
--- a/hw/microblaze/Makefile.objs
+++ b/hw/microblaze/Makefile.objs
@@ -1,6 +1,7 @@
obj-y = petalogix_s3adsp1800_mmu.o
obj-y += petalogix_ml605_mmu.o
obj-y += microblaze_boot.o
+obj-y += xilinx_spi.o
obj-y += microblaze_pic_cpu.o
obj-y += xilinx_ethlite.o
diff --git a/hw/pc_piix.c b/hw/pc_piix.c
index fd5898f..82364ab 100644
--- a/hw/pc_piix.c
+++ b/hw/pc_piix.c
@@ -371,6 +371,14 @@
.driver = "ivshmem",\
.property = "use64",\
.value = "0",\
+ },{\
+ .driver = "qxl",\
+ .property = "revision",\
+ .value = stringify(3),\
+ },{\
+ .driver = "qxl-vga",\
+ .property = "revision",\
+ .value = stringify(3),\
}
static QEMUMachine pc_machine_v1_2 = {
diff --git a/hw/petalogix_ml605_mmu.c b/hw/petalogix_ml605_mmu.c
index dced648..b9bfbed 100644
--- a/hw/petalogix_ml605_mmu.c
+++ b/hw/petalogix_ml605_mmu.c
@@ -36,6 +36,7 @@
#include "blockdev.h"
#include "pc.h"
#include "exec-memory.h"
+#include "ssi.h"
#include "microblaze_boot.h"
#include "microblaze_pic_cpu.h"
@@ -47,6 +48,8 @@
#define BINARY_DEVICE_TREE_FILE "petalogix-ml605.dtb"
+#define NUM_SPI_FLASHES 4
+
#define MEMORY_BASEADDR 0x50000000
#define FLASH_BASEADDR 0x86000000
#define INTC_BASEADDR 0x81800000
@@ -79,6 +82,7 @@
MemoryRegion *address_space_mem = get_system_memory();
DeviceState *dev, *dma, *eth0;
MicroBlazeCPU *cpu;
+ SysBusDevice *busdev;
CPUMBState *env;
DriveInfo *dinfo;
int i;
@@ -139,6 +143,29 @@
xilinx_axiethernetdma_init(dma, STREAM_SLAVE(eth0),
0x84600000, irq[1], irq[0], 100 * 1000000);
+ {
+ SSIBus *spi;
+
+ dev = qdev_create(NULL, "xlnx.xps-spi");
+ qdev_prop_set_uint8(dev, "num-ss-bits", NUM_SPI_FLASHES);
+ qdev_init_nofail(dev);
+ busdev = sysbus_from_qdev(dev);
+ sysbus_mmio_map(busdev, 0, 0x40a00000);
+ sysbus_connect_irq(busdev, 0, irq[4]);
+
+ spi = (SSIBus *)qdev_get_child_bus(dev, "spi");
+
+ for (i = 0; i < NUM_SPI_FLASHES; i++) {
+ qemu_irq cs_line;
+
+ dev = ssi_create_slave_no_init(spi, "m25p80");
+ qdev_prop_set_string(dev, "partname", "n25q128");
+ qdev_init_nofail(dev);
+ cs_line = qdev_get_gpio_in(dev, 0);
+ sysbus_connect_irq(busdev, i+1, cs_line);
+ }
+ }
+
microblaze_load_kernel(cpu, ddr_base, ram_size, BINARY_DEVICE_TREE_FILE,
machine_cpu_reset);
diff --git a/hw/qdev.c b/hw/qdev.c
index b6e9207..9b9aba3 100644
--- a/hw/qdev.c
+++ b/hw/qdev.c
@@ -285,9 +285,9 @@
void qdev_init_gpio_in(DeviceState *dev, qemu_irq_handler handler, int n)
{
- assert(dev->num_gpio_in == 0);
- dev->num_gpio_in = n;
- dev->gpio_in = qemu_allocate_irqs(handler, dev, n);
+ dev->gpio_in = qemu_extend_irqs(dev->gpio_in, dev->num_gpio_in, handler,
+ dev, n);
+ dev->num_gpio_in += n;
}
void qdev_init_gpio_out(DeviceState *dev, qemu_irq *pins, int n)
diff --git a/hw/qxl-render.c b/hw/qxl-render.c
index e2e3fe2..b66c168 100644
--- a/hw/qxl-render.c
+++ b/hw/qxl-render.c
@@ -99,7 +99,6 @@
{
VGACommonState *vga = &qxl->vga;
int i;
- DisplaySurface *surface = vga->ds->surface;
if (qxl->guest_primary.resized) {
qxl->guest_primary.resized = 0;
@@ -112,9 +111,6 @@
qxl->guest_primary.qxl_stride,
qxl->guest_primary.bytes_pp,
qxl->guest_primary.bits_pp);
- }
- if (surface->width != qxl->guest_primary.surface.width ||
- surface->height != qxl->guest_primary.surface.height) {
if (qxl->guest_primary.qxl_stride > 0) {
qemu_free_displaysurface(vga->ds);
qemu_create_displaysurface_from(qxl->guest_primary.surface.width,
diff --git a/hw/qxl.c b/hw/qxl.c
index 33169f3..1d16863 100644
--- a/hw/qxl.c
+++ b/hw/qxl.c
@@ -29,11 +29,6 @@
#include "qxl.h"
-#ifndef CONFIG_QXL_IO_MONITORS_CONFIG_ASYNC
-/* spice-protocol is too old, add missing definitions */
-#define QXL_IO_MONITORS_CONFIG_ASYNC (QXL_IO_FLUSH_RELEASE + 1)
-#endif
-
/*
* NOTE: SPICE_RING_PROD_ITEM accesses memory on the pci bar and as
* such can be changed by the guest, so to avoid a guest trigerrable
@@ -262,9 +257,6 @@
static void qxl_spice_monitors_config_async(PCIQXLDevice *qxl, int replay)
{
trace_qxl_spice_monitors_config(qxl->id);
-/* 0x000b01 == 0.11.1 */
-#if SPICE_SERVER_VERSION >= 0x000b01 && \
- defined(CONFIG_QXL_IO_MONITORS_CONFIG_ASYNC)
if (replay) {
/*
* don't use QXL_COOKIE_TYPE_IO:
@@ -286,10 +278,6 @@
(uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO,
QXL_IO_MONITORS_CONFIG_ASYNC));
}
-#else
- fprintf(stderr, "qxl: too old spice-protocol/spice-server for "
- "QXL_IO_MONITORS_CONFIG_ASYNC\n");
-#endif
}
void qxl_spice_reset_image_cache(PCIQXLDevice *qxl)
@@ -948,8 +936,6 @@
}
}
-#if SPICE_SERVER_VERSION >= 0x000b04
-
/* called from spice server thread context only */
static void interface_set_client_capabilities(QXLInstance *sin,
uint8_t client_present,
@@ -971,11 +957,6 @@
qxl_send_events(qxl, QXL_INTERRUPT_CLIENT);
}
-#endif
-
-#if defined(CONFIG_QXL_CLIENT_MONITORS_CONFIG) \
- && SPICE_SERVER_VERSION >= 0x000b05
-
static uint32_t qxl_crc32(const uint8_t *p, unsigned len)
{
/*
@@ -1044,7 +1025,6 @@
qxl_send_events(qxl, QXL_INTERRUPT_CLIENT_MONITORS_CONFIG);
return 1;
}
-#endif
static const QXLInterface qxl_interface = {
.base.type = SPICE_INTERFACE_QXL,
@@ -1067,13 +1047,8 @@
.flush_resources = interface_flush_resources,
.async_complete = interface_async_complete,
.update_area_complete = interface_update_area_complete,
-#if SPICE_SERVER_VERSION >= 0x000b04
.set_client_capabilities = interface_set_client_capabilities,
-#endif
-#if SPICE_SERVER_VERSION >= 0x000b05 && \
- defined(CONFIG_QXL_CLIENT_MONITORS_CONFIG)
.client_monitors_config = interface_client_monitors_config,
-#endif
};
static void qxl_enter_vga_mode(PCIQXLDevice *d)
@@ -1461,12 +1436,12 @@
qxl_async_io async = QXL_SYNC;
uint32_t orig_io_port = io_port;
- if (d->guest_bug && !io_port == QXL_IO_RESET) {
+ if (d->guest_bug && io_port != QXL_IO_RESET) {
return;
}
if (d->revision <= QXL_REVISION_STABLE_V10 &&
- io_port >= QXL_IO_FLUSH_SURFACES_ASYNC) {
+ io_port > QXL_IO_FLUSH_RELEASE) {
qxl_set_guest_bug(d, "unsupported io %d for revision %d\n",
io_port, d->revision);
return;
@@ -1547,20 +1522,13 @@
if (d->ram->update_surface > d->ssd.num_surfaces) {
qxl_set_guest_bug(d, "QXL_IO_UPDATE_AREA: invalid surface id %d\n",
d->ram->update_surface);
- return;
+ break;
}
- if (update.left >= update.right || update.top >= update.bottom) {
+ if (update.left >= update.right || update.top >= update.bottom ||
+ update.left < 0 || update.top < 0) {
qxl_set_guest_bug(d,
"QXL_IO_UPDATE_AREA: invalid area (%ux%u)x(%ux%u)\n",
update.left, update.top, update.right, update.bottom);
- return;
- }
-
- if (update.left < 0 || update.top < 0 || update.left >= update.right ||
- update.top >= update.bottom) {
- qxl_set_guest_bug(d, "QXL_IO_UPDATE_AREA: "
- "invalid area(%d,%d,%d,%d)\n", update.left,
- update.right, update.top, update.bottom);
break;
}
if (async == QXL_ASYNC) {
@@ -1811,7 +1779,7 @@
static void qxl_dirty_surfaces(PCIQXLDevice *qxl)
{
- intptr_t vram_start;
+ uintptr_t vram_start;
int i;
if (qxl->mode != QXL_MODE_NATIVE && qxl->mode != QXL_MODE_COMPAT) {
@@ -1822,7 +1790,7 @@
qxl_set_dirty(&qxl->vga.vram, qxl->shadow_rom.draw_area_offset,
qxl->shadow_rom.surface0_area_size);
- vram_start = (intptr_t)memory_region_get_ram_ptr(&qxl->vram_bar);
+ vram_start = (uintptr_t)memory_region_get_ram_ptr(&qxl->vram_bar);
/* dirty the off-screen surfaces */
for (i = 0; i < qxl->ssd.num_surfaces; i++) {
@@ -1854,7 +1822,6 @@
RunState state)
{
PCIQXLDevice *qxl = opaque;
- qemu_spice_vm_change_state_handler(&qxl->ssd, running, state);
if (running) {
/*
@@ -1971,14 +1938,10 @@
pci_device_rev = QXL_REVISION_STABLE_V10;
io_size = 32; /* PCI region size must be pow2 */
break;
-/* 0x000b01 == 0.11.1 */
-#if SPICE_SERVER_VERSION >= 0x000b01 && \
- defined(CONFIG_QXL_IO_MONITORS_CONFIG_ASYNC)
case 4: /* qxl-4 */
pci_device_rev = QXL_REVISION_STABLE_V12;
io_size = msb_mask(QXL_IO_RANGE_SIZE * 2 - 1);
break;
-#endif
default:
error_report("Invalid revision %d for qxl device (max %d)",
qxl->revision, QXL_DEFAULT_REVISION);
@@ -2044,7 +2007,11 @@
qxl->ssd.qxl.base.sif = &qxl_interface.base;
qxl->ssd.qxl.id = qxl->id;
- qemu_spice_add_interface(&qxl->ssd.qxl.base);
+ if (qemu_spice_add_interface(&qxl->ssd.qxl.base) != 0) {
+ error_report("qxl interface %d.%d not supported by spice-server\n",
+ SPICE_INTERFACE_QXL_MAJOR, SPICE_INTERFACE_QXL_MINOR);
+ return -1;
+ }
qemu_add_vm_change_state_handler(qxl_vm_change_state_handler, qxl);
init_pipe_signaling(qxl);
diff --git a/hw/qxl.h b/hw/qxl.h
index 5553824..e583cfb 100644
--- a/hw/qxl.h
+++ b/hw/qxl.h
@@ -129,12 +129,7 @@
} \
} while (0)
-#if 0
-/* spice-server 0.12 is still in development */
#define QXL_DEFAULT_REVISION QXL_REVISION_STABLE_V12
-#else
-#define QXL_DEFAULT_REVISION QXL_REVISION_STABLE_V10
-#endif
/* qxl.c */
void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL phys, int group_id);
diff --git a/hw/rtl8139.c b/hw/rtl8139.c
index b7c82ee..6b28fea 100644
--- a/hw/rtl8139.c
+++ b/hw/rtl8139.c
@@ -167,7 +167,7 @@
PCIErr = 0x8000,
PCSTimeout = 0x4000,
RxFIFOOver = 0x40,
- RxUnderrun = 0x20,
+ RxUnderrun = 0x20, /* Packet Underrun / Link Change */
RxOverflow = 0x10,
TxErr = 0x08,
TxOK = 0x04,
@@ -3003,7 +3003,8 @@
break;
case MediaStatus:
- ret = 0xd0;
+ /* The LinkDown bit of MediaStatus is inverse with link status */
+ ret = 0xd0 | (~s->BasicModeStatus & 0x04);
DPRINTF("MediaStatus read 0x%x\n", ret);
break;
@@ -3258,6 +3259,10 @@
s->cplus_enabled = s->CpCmd != 0;
}
+ /* nc.link_down can't be migrated, so infer link_down according
+ * to link status bit in BasicModeStatus */
+ s->nic->nc.link_down = (s->BasicModeStatus & 0x04) == 0;
+
return 0;
}
@@ -3449,12 +3454,27 @@
qemu_del_net_client(&s->nic->nc);
}
+static void rtl8139_set_link_status(NetClientState *nc)
+{
+ RTL8139State *s = DO_UPCAST(NICState, nc, nc)->opaque;
+
+ if (nc->link_down) {
+ s->BasicModeStatus &= ~0x04;
+ } else {
+ s->BasicModeStatus |= 0x04;
+ }
+
+ s->IntrStatus |= RxUnderrun;
+ rtl8139_update_irq(s);
+}
+
static NetClientInfo net_rtl8139_info = {
.type = NET_CLIENT_OPTIONS_KIND_NIC,
.size = sizeof(NICState),
.can_receive = rtl8139_can_receive,
.receive = rtl8139_receive,
.cleanup = rtl8139_cleanup,
+ .link_status_changed = rtl8139_set_link_status,
};
static int pci_rtl8139_init(PCIDevice *dev)
diff --git a/hw/spitz.c b/hw/spitz.c
index 20e7835..24346dc 100644
--- a/hw/spitz.c
+++ b/hw/spitz.c
@@ -1083,10 +1083,11 @@
static const VMStateDescription vmstate_corgi_ssp_regs = {
.name = "corgi-ssp",
- .version_id = 1,
- .minimum_version_id = 1,
- .minimum_version_id_old = 1,
+ .version_id = 2,
+ .minimum_version_id = 2,
+ .minimum_version_id_old = 2,
.fields = (VMStateField []) {
+ VMSTATE_SSI_SLAVE(ssidev, CorgiSSPState),
VMSTATE_UINT32_ARRAY(enable, CorgiSSPState, 3),
VMSTATE_END_OF_LIST(),
}
@@ -1115,6 +1116,7 @@
.minimum_version_id = 1,
.minimum_version_id_old = 1,
.fields = (VMStateField []) {
+ VMSTATE_SSI_SLAVE(ssidev, SpitzLCDTG),
VMSTATE_UINT32(bl_intensity, SpitzLCDTG),
VMSTATE_UINT32(bl_power, SpitzLCDTG),
VMSTATE_END_OF_LIST(),
diff --git a/hw/ssd0323.c b/hw/ssd0323.c
index b101c51..9c42d64 100644
--- a/hw/ssd0323.c
+++ b/hw/ssd0323.c
@@ -279,6 +279,7 @@
static void ssd0323_save(QEMUFile *f, void *opaque)
{
+ SSISlave *ss = SSI_SLAVE(opaque);
ssd0323_state *s = (ssd0323_state *)opaque;
int i;
@@ -296,10 +297,13 @@
qemu_put_be32(f, s->remap);
qemu_put_be32(f, s->mode);
qemu_put_buffer(f, s->framebuffer, sizeof(s->framebuffer));
+
+ qemu_put_be32(f, ss->cs);
}
static int ssd0323_load(QEMUFile *f, void *opaque, int version_id)
{
+ SSISlave *ss = SSI_SLAVE(opaque);
ssd0323_state *s = (ssd0323_state *)opaque;
int i;
@@ -321,6 +325,8 @@
s->mode = qemu_get_be32(f);
qemu_get_buffer(f, s->framebuffer, sizeof(s->framebuffer));
+ ss->cs = qemu_get_be32(f);
+
return 0;
}
@@ -348,6 +354,7 @@
k->init = ssd0323_init;
k->transfer = ssd0323_transfer;
+ k->cs_polarity = SSI_CS_HIGH;
}
static TypeInfo ssd0323_info = {
diff --git a/hw/ssi-sd.c b/hw/ssi-sd.c
index b519bdb..c5505ee 100644
--- a/hw/ssi-sd.c
+++ b/hw/ssi-sd.c
@@ -197,6 +197,7 @@
static void ssi_sd_save(QEMUFile *f, void *opaque)
{
+ SSISlave *ss = SSI_SLAVE(opaque);
ssi_sd_state *s = (ssi_sd_state *)opaque;
int i;
@@ -209,10 +210,13 @@
qemu_put_be32(f, s->arglen);
qemu_put_be32(f, s->response_pos);
qemu_put_be32(f, s->stopping);
+
+ qemu_put_be32(f, ss->cs);
}
static int ssi_sd_load(QEMUFile *f, void *opaque, int version_id)
{
+ SSISlave *ss = SSI_SLAVE(opaque);
ssi_sd_state *s = (ssi_sd_state *)opaque;
int i;
@@ -229,6 +233,8 @@
s->response_pos = qemu_get_be32(f);
s->stopping = qemu_get_be32(f);
+ ss->cs = qemu_get_be32(f);
+
return 0;
}
@@ -250,6 +256,7 @@
k->init = ssi_sd_init;
k->transfer = ssi_sd_transfer;
+ k->cs_polarity = SSI_CS_LOW;
}
static TypeInfo ssi_sd_info = {
diff --git a/hw/ssi.c b/hw/ssi.c
index e5f14a0..2b56357 100644
--- a/hw/ssi.c
+++ b/hw/ssi.c
@@ -2,6 +2,8 @@
* QEMU Synchronous Serial Interface support
*
* Copyright (c) 2009 CodeSourcery.
+ * Copyright (c) 2012 Peter A.G. Crosthwaite (peter.crosthwaite@petalogix.com)
+ * Copyright (c) 2012 PetaLogix Pty Ltd.
* Written by Paul Brook
*
* This code is licensed under the GNU GPL v2.
@@ -25,17 +27,40 @@
.instance_size = sizeof(SSIBus),
};
+static void ssi_cs_default(void *opaque, int n, int level)
+{
+ SSISlave *s = SSI_SLAVE(opaque);
+ bool cs = !!level;
+ assert(n == 0);
+ if (s->cs != cs) {
+ SSISlaveClass *ssc = SSI_SLAVE_GET_CLASS(s);
+ if (ssc->set_cs) {
+ ssc->set_cs(s, cs);
+ }
+ }
+ s->cs = cs;
+}
+
+static uint32_t ssi_transfer_raw_default(SSISlave *dev, uint32_t val)
+{
+ SSISlaveClass *ssc = SSI_SLAVE_GET_CLASS(dev);
+
+ if ((dev->cs && ssc->cs_polarity == SSI_CS_HIGH) ||
+ (!dev->cs && ssc->cs_polarity == SSI_CS_LOW) ||
+ ssc->cs_polarity == SSI_CS_NONE) {
+ return ssc->transfer(dev, val);
+ }
+ return 0;
+}
+
static int ssi_slave_init(DeviceState *dev)
{
SSISlave *s = SSI_SLAVE(dev);
SSISlaveClass *ssc = SSI_SLAVE_GET_CLASS(s);
- SSIBus *bus;
- BusChild *kid;
- bus = FROM_QBUS(SSIBus, qdev_get_parent_bus(dev));
- kid = QTAILQ_FIRST(&bus->qbus.children);
- if (kid->child != dev || QTAILQ_NEXT(kid, sibling) != NULL) {
- hw_error("Too many devices on SSI bus");
+ if (ssc->transfer_raw == ssi_transfer_raw_default &&
+ ssc->cs_polarity != SSI_CS_NONE) {
+ qdev_init_gpio_in(&s->qdev, ssi_cs_default, 1);
}
return ssc->init(s);
@@ -43,9 +68,14 @@
static void ssi_slave_class_init(ObjectClass *klass, void *data)
{
+ SSISlaveClass *ssc = SSI_SLAVE_CLASS(klass);
DeviceClass *dc = DEVICE_CLASS(klass);
+
dc->init = ssi_slave_init;
dc->bus_type = TYPE_SSI_BUS;
+ if (!ssc->transfer_raw) {
+ ssc->transfer_raw = ssi_transfer_raw_default;
+ }
}
static TypeInfo ssi_slave_info = {
@@ -56,10 +86,15 @@
.abstract = true,
};
+DeviceState *ssi_create_slave_no_init(SSIBus *bus, const char *name)
+{
+ return qdev_create(&bus->qbus, name);
+}
+
DeviceState *ssi_create_slave(SSIBus *bus, const char *name)
{
- DeviceState *dev;
- dev = qdev_create(&bus->qbus, name);
+ DeviceState *dev = ssi_create_slave_no_init(bus, name);
+
qdev_init_nofail(dev);
return dev;
}
@@ -74,18 +109,29 @@
uint32_t ssi_transfer(SSIBus *bus, uint32_t val)
{
BusChild *kid;
- SSISlave *slave;
SSISlaveClass *ssc;
+ uint32_t r = 0;
- kid = QTAILQ_FIRST(&bus->qbus.children);
- if (!kid) {
- return 0;
+ QTAILQ_FOREACH(kid, &bus->qbus.children, sibling) {
+ SSISlave *slave = SSI_SLAVE(kid->child);
+ ssc = SSI_SLAVE_GET_CLASS(slave);
+ r |= ssc->transfer_raw(slave, val);
}
- slave = SSI_SLAVE(kid->child);
- ssc = SSI_SLAVE_GET_CLASS(slave);
- return ssc->transfer(slave, val);
+
+ return r;
}
+const VMStateDescription vmstate_ssi_slave = {
+ .name = "SSISlave",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_BOOL(cs, SSISlave),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static void ssi_slave_register_types(void)
{
type_register_static(&ssi_bus_info);
@@ -93,3 +139,36 @@
}
type_init(ssi_slave_register_types)
+
+typedef struct SSIAutoConnectArg {
+ qemu_irq **cs_linep;
+ SSIBus *bus;
+} SSIAutoConnectArg;
+
+static int ssi_auto_connect_slave(Object *child, void *opaque)
+{
+ SSIAutoConnectArg *arg = opaque;
+ SSISlave *dev = (SSISlave *)object_dynamic_cast(child, TYPE_SSI_SLAVE);
+ qemu_irq cs_line;
+
+ if (!dev) {
+ return 0;
+ }
+
+ cs_line = qdev_get_gpio_in(DEVICE(dev), 0);
+ qdev_set_parent_bus(DEVICE(dev), &arg->bus->qbus);
+ **arg->cs_linep = cs_line;
+ (*arg->cs_linep)++;
+ return 0;
+}
+
+void ssi_auto_connect_slaves(DeviceState *parent, qemu_irq *cs_line,
+ SSIBus *bus)
+{
+ SSIAutoConnectArg arg = {
+ .cs_linep = &cs_line,
+ .bus = bus
+ };
+
+ object_child_foreach(OBJECT(parent), ssi_auto_connect_slave, &arg);
+}
diff --git a/hw/ssi.h b/hw/ssi.h
index 06657d7..a05d60b 100644
--- a/hw/ssi.h
+++ b/hw/ssi.h
@@ -23,28 +23,70 @@
#define SSI_SLAVE_GET_CLASS(obj) \
OBJECT_GET_CLASS(SSISlaveClass, (obj), TYPE_SSI_SLAVE)
+typedef enum {
+ SSI_CS_NONE = 0,
+ SSI_CS_LOW,
+ SSI_CS_HIGH,
+} SSICSMode;
+
/* Slave devices. */
typedef struct SSISlaveClass {
DeviceClass parent_class;
int (*init)(SSISlave *dev);
+
+ /* if you have standard or no CS behaviour, just override transfer.
+ * This is called when the device cs is active (true by default).
+ */
uint32_t (*transfer)(SSISlave *dev, uint32_t val);
+ /* called when the CS line changes. Optional, devices only need to implement
+ * this if they have side effects associated with the cs line (beyond
+ * tristating the txrx lines).
+ */
+ int (*set_cs)(SSISlave *dev, bool select);
+ /* define whether or not CS exists and is active low/high */
+ SSICSMode cs_polarity;
+
+ /* if you have non-standard CS behaviour override this to take control
+ * of the CS behaviour at the device level. transfer, set_cs, and
+ * cs_polarity are unused if this is overwritten. Transfer_raw will
+ * always be called for the device for every txrx access to the parent bus
+ */
+ uint32_t (*transfer_raw)(SSISlave *dev, uint32_t val);
} SSISlaveClass;
struct SSISlave {
DeviceState qdev;
+
+ /* Chip select state */
+ bool cs;
};
#define SSI_SLAVE_FROM_QDEV(dev) DO_UPCAST(SSISlave, qdev, dev)
#define FROM_SSI_SLAVE(type, dev) DO_UPCAST(type, ssidev, dev)
+extern const VMStateDescription vmstate_ssi_slave;
+
+#define VMSTATE_SSI_SLAVE(_field, _state) { \
+ .name = (stringify(_field)), \
+ .size = sizeof(SSISlave), \
+ .vmsd = &vmstate_ssi_slave, \
+ .flags = VMS_STRUCT, \
+ .offset = vmstate_offset_value(_state, _field, SSISlave), \
+}
+
DeviceState *ssi_create_slave(SSIBus *bus, const char *name);
+DeviceState *ssi_create_slave_no_init(SSIBus *bus, const char *name);
/* Master interface. */
SSIBus *ssi_create_bus(DeviceState *parent, const char *name);
uint32_t ssi_transfer(SSIBus *bus, uint32_t val);
+/* Automatically connect all children nodes a spi controller as slaves */
+void ssi_auto_connect_slaves(DeviceState *parent, qemu_irq *cs_lines,
+ SSIBus *bus);
+
/* max111x.c */
void max111x_set_input(DeviceState *dev, int line, uint8_t value);
diff --git a/hw/stellaris.c b/hw/stellaris.c
index 562fbbf..353ca4c 100644
--- a/hw/stellaris.c
+++ b/hw/stellaris.c
@@ -1154,57 +1154,6 @@
return 0;
}
-/* Some boards have both an OLED controller and SD card connected to
- the same SSI port, with the SD card chip select connected to a
- GPIO pin. Technically the OLED chip select is connected to the SSI
- Fss pin. We do not bother emulating that as both devices should
- never be selected simultaneously, and our OLED controller ignores stray
- 0xff commands that occur when deselecting the SD card. */
-
-typedef struct {
- SSISlave ssidev;
- qemu_irq irq;
- int current_dev;
- SSIBus *bus[2];
-} stellaris_ssi_bus_state;
-
-static void stellaris_ssi_bus_select(void *opaque, int irq, int level)
-{
- stellaris_ssi_bus_state *s = (stellaris_ssi_bus_state *)opaque;
-
- s->current_dev = level;
-}
-
-static uint32_t stellaris_ssi_bus_transfer(SSISlave *dev, uint32_t val)
-{
- stellaris_ssi_bus_state *s = FROM_SSI_SLAVE(stellaris_ssi_bus_state, dev);
-
- return ssi_transfer(s->bus[s->current_dev], val);
-}
-
-static const VMStateDescription vmstate_stellaris_ssi_bus = {
- .name = "stellaris_ssi_bus",
- .version_id = 1,
- .minimum_version_id = 1,
- .minimum_version_id_old = 1,
- .fields = (VMStateField[]) {
- VMSTATE_INT32(current_dev, stellaris_ssi_bus_state),
- VMSTATE_END_OF_LIST()
- }
-};
-
-static int stellaris_ssi_bus_init(SSISlave *dev)
-{
- stellaris_ssi_bus_state *s = FROM_SSI_SLAVE(stellaris_ssi_bus_state, dev);
-
- s->bus[0] = ssi_create_bus(&dev->qdev, "ssi0");
- s->bus[1] = ssi_create_bus(&dev->qdev, "ssi1");
- qdev_init_gpio_in(&dev->qdev, stellaris_ssi_bus_select, 1);
-
- vmstate_register(&dev->qdev, -1, &vmstate_stellaris_ssi_bus, s);
- return 0;
-}
-
/* Board init. */
static stellaris_board_info stellaris_boards[] = {
{ "LM3S811EVB",
@@ -1305,19 +1254,25 @@
if (board->dc2 & (1 << 4)) {
dev = sysbus_create_simple("pl022", 0x40008000, pic[7]);
if (board->peripherals & BP_OLED_SSI) {
- DeviceState *mux;
void *bus;
+ DeviceState *sddev;
+ DeviceState *ssddev;
+ /* Some boards have both an OLED controller and SD card connected to
+ * the same SSI port, with the SD card chip select connected to a
+ * GPIO pin. Technically the OLED chip select is connected to the
+ * SSI Fss pin. We do not bother emulating that as both devices
+ * should never be selected simultaneously, and our OLED controller
+ * ignores stray 0xff commands that occur when deselecting the SD
+ * card.
+ */
bus = qdev_get_child_bus(dev, "ssi");
- mux = ssi_create_slave(bus, "evb6965-ssi");
- gpio_out[GPIO_D][0] = qdev_get_gpio_in(mux, 0);
- bus = qdev_get_child_bus(mux, "ssi0");
- ssi_create_slave(bus, "ssi-sd");
-
- bus = qdev_get_child_bus(mux, "ssi1");
- dev = ssi_create_slave(bus, "ssd0323");
- gpio_out[GPIO_C][7] = qdev_get_gpio_in(dev, 0);
+ sddev = ssi_create_slave(bus, "ssi-sd");
+ ssddev = ssi_create_slave(bus, "ssd0323");
+ gpio_out[GPIO_D][0] = qemu_irq_split(qdev_get_gpio_in(sddev, 0),
+ qdev_get_gpio_in(ssddev, 0));
+ gpio_out[GPIO_C][7] = qdev_get_gpio_in(ssddev, 1);
/* Make sure the select pin is high. */
qemu_irq_raise(gpio_out[GPIO_D][0]);
@@ -1394,21 +1349,6 @@
machine_init(stellaris_machine_init);
-static void stellaris_ssi_bus_class_init(ObjectClass *klass, void *data)
-{
- SSISlaveClass *k = SSI_SLAVE_CLASS(klass);
-
- k->init = stellaris_ssi_bus_init;
- k->transfer = stellaris_ssi_bus_transfer;
-}
-
-static TypeInfo stellaris_ssi_bus_info = {
- .name = "evb6965-ssi",
- .parent = TYPE_SSI_SLAVE,
- .instance_size = sizeof(stellaris_ssi_bus_state),
- .class_init = stellaris_ssi_bus_class_init,
-};
-
static void stellaris_i2c_class_init(ObjectClass *klass, void *data)
{
SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
@@ -1456,7 +1396,6 @@
type_register_static(&stellaris_i2c_info);
type_register_static(&stellaris_gptm_info);
type_register_static(&stellaris_adc_info);
- type_register_static(&stellaris_ssi_bus_info);
}
type_init(stellaris_register_types)
diff --git a/hw/vfio_pci.c b/hw/vfio_pci.c
index a1eeced..639371e 100644
--- a/hw/vfio_pci.c
+++ b/hw/vfio_pci.c
@@ -33,9 +33,11 @@
#include "memory.h"
#include "msi.h"
#include "msix.h"
+#include "pci.h"
+#include "qemu-common.h"
#include "qemu-error.h"
+#include "qemu-queue.h"
#include "range.h"
-#include "vfio_pci_int.h"
/* #define DEBUG_VFIO */
#ifdef DEBUG_VFIO
@@ -46,6 +48,99 @@
do { } while (0)
#endif
+typedef struct VFIOBAR {
+ off_t fd_offset; /* offset of BAR within device fd */
+ int fd; /* device fd, allows us to pass VFIOBAR as opaque data */
+ MemoryRegion mem; /* slow, read/write access */
+ MemoryRegion mmap_mem; /* direct mapped access */
+ void *mmap;
+ size_t size;
+ uint32_t flags; /* VFIO region flags (rd/wr/mmap) */
+ uint8_t nr; /* cache the BAR number for debug */
+} VFIOBAR;
+
+typedef struct VFIOINTx {
+ bool pending; /* interrupt pending */
+ bool kvm_accel; /* set when QEMU bypass through KVM enabled */
+ uint8_t pin; /* which pin to pull for qemu_set_irq */
+ EventNotifier interrupt; /* eventfd triggered on interrupt */
+ EventNotifier unmask; /* eventfd for unmask on QEMU bypass */
+ PCIINTxRoute route; /* routing info for QEMU bypass */
+ uint32_t mmap_timeout; /* delay to re-enable mmaps after interrupt */
+ QEMUTimer *mmap_timer; /* enable mmaps after periods w/o interrupts */
+} VFIOINTx;
+
+struct VFIODevice;
+
+typedef struct VFIOMSIVector {
+ EventNotifier interrupt; /* eventfd triggered on interrupt */
+ struct VFIODevice *vdev; /* back pointer to device */
+ int virq; /* KVM irqchip route for QEMU bypass */
+ bool use;
+} VFIOMSIVector;
+
+enum {
+ VFIO_INT_NONE = 0,
+ VFIO_INT_INTx = 1,
+ VFIO_INT_MSI = 2,
+ VFIO_INT_MSIX = 3,
+};
+
+struct VFIOGroup;
+
+typedef struct VFIOContainer {
+ int fd; /* /dev/vfio/vfio, empowered by the attached groups */
+ struct {
+ /* enable abstraction to support various iommu backends */
+ union {
+ MemoryListener listener; /* Used by type1 iommu */
+ };
+ void (*release)(struct VFIOContainer *);
+ } iommu_data;
+ QLIST_HEAD(, VFIOGroup) group_list;
+ QLIST_ENTRY(VFIOContainer) next;
+} VFIOContainer;
+
+/* Cache of MSI-X setup plus extra mmap and memory region for split BAR map */
+typedef struct VFIOMSIXInfo {
+ uint8_t table_bar;
+ uint8_t pba_bar;
+ uint16_t entries;
+ uint32_t table_offset;
+ uint32_t pba_offset;
+ MemoryRegion mmap_mem;
+ void *mmap;
+} VFIOMSIXInfo;
+
+typedef struct VFIODevice {
+ PCIDevice pdev;
+ int fd;
+ VFIOINTx intx;
+ unsigned int config_size;
+ off_t config_offset; /* Offset of config space region within device fd */
+ unsigned int rom_size;
+ off_t rom_offset; /* Offset of ROM region within device fd */
+ int msi_cap_size;
+ VFIOMSIVector *msi_vectors;
+ VFIOMSIXInfo *msix;
+ int nr_vectors; /* Number of MSI/MSIX vectors currently in use */
+ int interrupt; /* Current interrupt type */
+ VFIOBAR bars[PCI_NUM_REGIONS - 1]; /* No ROM */
+ PCIHostDeviceAddress host;
+ QLIST_ENTRY(VFIODevice) next;
+ struct VFIOGroup *group;
+ bool reset_works;
+} VFIODevice;
+
+typedef struct VFIOGroup {
+ int fd;
+ int groupid;
+ VFIOContainer *container;
+ QLIST_HEAD(, VFIODevice) device_list;
+ QLIST_ENTRY(VFIOGroup) next;
+ QLIST_ENTRY(VFIOGroup) container_next;
+} VFIOGroup;
+
#define MSIX_CAP_LENGTH 12
static QLIST_HEAD(, VFIOContainer)
@@ -72,8 +167,6 @@
};
ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, &irq_set);
-
- vdev->interrupt = VFIO_INT_NONE;
}
/*
@@ -92,6 +185,34 @@
ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, &irq_set);
}
+/*
+ * Disabling BAR mmaping can be slow, but toggling it around INTx can
+ * also be a huge overhead. We try to get the best of both worlds by
+ * waiting until an interrupt to disable mmaps (subsequent transitions
+ * to the same state are effectively no overhead). If the interrupt has
+ * been serviced and the time gap is long enough, we re-enable mmaps for
+ * performance. This works well for things like graphics cards, which
+ * may not use their interrupt at all and are penalized to an unusable
+ * level by read/write BAR traps. Other devices, like NICs, have more
+ * regular interrupts and see much better latency by staying in non-mmap
+ * mode. We therefore set the default mmap_timeout such that a ping
+ * is just enough to keep the mmap disabled. Users can experiment with
+ * other options with the x-intx-mmap-timeout-ms parameter (a value of
+ * zero disables the timer).
+ */
+static void vfio_intx_mmap_enable(void *opaque)
+{
+ VFIODevice *vdev = opaque;
+
+ if (vdev->intx.pending) {
+ qemu_mod_timer(vdev->intx.mmap_timer,
+ qemu_get_clock_ms(vm_clock) + vdev->intx.mmap_timeout);
+ return;
+ }
+
+ vfio_mmap_set_enabled(vdev, true);
+}
+
static void vfio_intx_interrupt(void *opaque)
{
VFIODevice *vdev = opaque;
@@ -106,6 +227,11 @@
vdev->intx.pending = true;
qemu_set_irq(vdev->pdev.irq[vdev->intx.pin], 1);
+ vfio_mmap_set_enabled(vdev, false);
+ if (vdev->intx.mmap_timeout) {
+ qemu_mod_timer(vdev->intx.mmap_timer,
+ qemu_get_clock_ms(vm_clock) + vdev->intx.mmap_timeout);
+ }
}
static void vfio_eoi(VFIODevice *vdev)
@@ -122,26 +248,14 @@
vfio_unmask_intx(vdev);
}
-typedef struct QEMU_PACKED VFIOIRQSetFD {
- struct vfio_irq_set irq_set;
- int32_t fd;
-} VFIOIRQSetFD;
-
static int vfio_enable_intx(VFIODevice *vdev)
{
- VFIOIRQSetFD irq_set_fd = {
- .irq_set = {
- .argsz = sizeof(irq_set_fd),
- .flags = VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_TRIGGER,
- .index = VFIO_PCI_INTX_IRQ_INDEX,
- .start = 0,
- .count = 1,
- },
- };
uint8_t pin = vfio_pci_read_config(&vdev->pdev, PCI_INTERRUPT_PIN, 1);
- int ret;
+ int ret, argsz;
+ struct vfio_irq_set *irq_set;
+ int32_t *pfd;
- if (vdev->intx.disabled || !pin) {
+ if (!pin) {
return 0;
}
@@ -154,24 +268,28 @@
return ret;
}
- irq_set_fd.fd = event_notifier_get_fd(&vdev->intx.interrupt);
- qemu_set_fd_handler(irq_set_fd.fd, vfio_intx_interrupt, NULL, vdev);
+ argsz = sizeof(*irq_set) + sizeof(*pfd);
- if (ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, &irq_set_fd)) {
+ irq_set = g_malloc0(argsz);
+ irq_set->argsz = argsz;
+ irq_set->flags = VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_TRIGGER;
+ irq_set->index = VFIO_PCI_INTX_IRQ_INDEX;
+ irq_set->start = 0;
+ irq_set->count = 1;
+ pfd = (int32_t *)&irq_set->data;
+
+ *pfd = event_notifier_get_fd(&vdev->intx.interrupt);
+ qemu_set_fd_handler(*pfd, vfio_intx_interrupt, NULL, vdev);
+
+ ret = ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, irq_set);
+ g_free(irq_set);
+ if (ret) {
error_report("vfio: Error: Failed to setup INTx fd: %m\n");
+ qemu_set_fd_handler(*pfd, NULL, NULL, vdev);
+ event_notifier_cleanup(&vdev->intx.interrupt);
return -errno;
}
- /*
- * Disable mmaps so we can trap on BAR accesses. We interpret any
- * access as a response to an interrupt and unmask the physical
- * device. The device will re-assert if the interrupt is still
- * pending. We'll likely retrigger on the host multiple times per
- * guest interrupt, but without EOI notification it's better than
- * nothing. Acceleration paths through KVM will avoid this.
- */
- vfio_mmap_set_enabled(vdev, false);
-
vdev->interrupt = VFIO_INT_INTx;
DPRINTF("%s(%04x:%02x:%02x.%x)\n", __func__, vdev->host.domain,
@@ -184,6 +302,7 @@
{
int fd;
+ qemu_del_timer(vdev->intx.mmap_timer);
vfio_disable_irqindex(vdev, VFIO_PCI_INTX_IRQ_INDEX);
vdev->intx.pending = false;
qemu_set_irq(vdev->pdev.irq[vdev->intx.pin], 0);
@@ -254,10 +373,6 @@
g_free(irq_set);
- if (!ret) {
- vdev->interrupt = msix ? VFIO_INT_MSIX : VFIO_INT_MSI;
- }
-
return ret;
}
@@ -272,15 +387,6 @@
vdev->host.domain, vdev->host.bus, vdev->host.slot,
vdev->host.function, nr);
- if (vdev->interrupt != VFIO_INT_MSIX) {
- vfio_disable_interrupts(vdev);
- }
-
- if (!vdev->msi_vectors) {
- vdev->msi_vectors = g_malloc0(vdev->msix->entries *
- sizeof(VFIOMSIVector));
- }
-
vector = &vdev->msi_vectors[nr];
vector->vdev = vdev;
vector->use = true;
@@ -313,43 +419,35 @@
* increase them as needed.
*/
if (vdev->nr_vectors < nr + 1) {
- int i;
-
vfio_disable_irqindex(vdev, VFIO_PCI_MSIX_IRQ_INDEX);
vdev->nr_vectors = nr + 1;
ret = vfio_enable_vectors(vdev, true);
if (ret) {
error_report("vfio: failed to enable vectors, %d\n", ret);
}
-
- /* We don't know if we've missed interrupts in the interim... */
- for (i = 0; i < vdev->msix->entries; i++) {
- if (vdev->msi_vectors[i].use) {
- msix_notify(&vdev->pdev, i);
- }
- }
} else {
- VFIOIRQSetFD irq_set_fd = {
- .irq_set = {
- .argsz = sizeof(irq_set_fd),
- .flags = VFIO_IRQ_SET_DATA_EVENTFD |
- VFIO_IRQ_SET_ACTION_TRIGGER,
- .index = VFIO_PCI_MSIX_IRQ_INDEX,
- .start = nr,
- .count = 1,
- },
- .fd = event_notifier_get_fd(&vector->interrupt),
- };
- ret = ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, &irq_set_fd);
+ int argsz;
+ struct vfio_irq_set *irq_set;
+ int32_t *pfd;
+
+ argsz = sizeof(*irq_set) + sizeof(*pfd);
+
+ irq_set = g_malloc0(argsz);
+ irq_set->argsz = argsz;
+ irq_set->flags = VFIO_IRQ_SET_DATA_EVENTFD |
+ VFIO_IRQ_SET_ACTION_TRIGGER;
+ irq_set->index = VFIO_PCI_MSIX_IRQ_INDEX;
+ irq_set->start = nr;
+ irq_set->count = 1;
+ pfd = (int32_t *)&irq_set->data;
+
+ *pfd = event_notifier_get_fd(&vector->interrupt);
+
+ ret = ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, irq_set);
+ g_free(irq_set);
if (ret) {
error_report("vfio: failed to modify vector, %d\n", ret);
}
-
- /*
- * If we were connected to the hardware PBA we could skip this,
- * until then, a spurious interrupt is better than starvation.
- */
- msix_notify(&vdev->pdev, nr);
}
return 0;
@@ -359,17 +457,9 @@
{
VFIODevice *vdev = DO_UPCAST(VFIODevice, pdev, pdev);
VFIOMSIVector *vector = &vdev->msi_vectors[nr];
- VFIOIRQSetFD irq_set_fd = {
- .irq_set = {
- .argsz = sizeof(irq_set_fd),
- .flags = VFIO_IRQ_SET_DATA_EVENTFD |
- VFIO_IRQ_SET_ACTION_TRIGGER,
- .index = VFIO_PCI_MSIX_IRQ_INDEX,
- .start = nr,
- .count = 1,
- },
- .fd = -1,
- };
+ int argsz;
+ struct vfio_irq_set *irq_set;
+ int32_t *pfd;
DPRINTF("%s(%04x:%02x:%02x.%x) vector %d released\n", __func__,
vdev->host.domain, vdev->host.bus, vdev->host.slot,
@@ -381,7 +471,23 @@
* bouncing through userspace and let msix.c drop it? Not sure.
*/
msix_vector_unuse(pdev, nr);
- ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, &irq_set_fd);
+
+ argsz = sizeof(*irq_set) + sizeof(*pfd);
+
+ irq_set = g_malloc0(argsz);
+ irq_set->argsz = argsz;
+ irq_set->flags = VFIO_IRQ_SET_DATA_EVENTFD |
+ VFIO_IRQ_SET_ACTION_TRIGGER;
+ irq_set->index = VFIO_PCI_MSIX_IRQ_INDEX;
+ irq_set->start = nr;
+ irq_set->count = 1;
+ pfd = (int32_t *)&irq_set->data;
+
+ *pfd = -1;
+
+ ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, irq_set);
+
+ g_free(irq_set);
if (vector->virq < 0) {
qemu_set_fd_handler(event_notifier_get_fd(&vector->interrupt),
@@ -419,18 +525,21 @@
return msg;
}
-/* So should this */
-static void msi_set_qsize(PCIDevice *pdev, uint8_t size)
+static void vfio_enable_msix(VFIODevice *vdev)
{
- uint8_t *config = pdev->config + pdev->msi_cap;
- uint16_t flags;
+ vfio_disable_interrupts(vdev);
- flags = pci_get_word(config + PCI_MSI_FLAGS);
- flags = le16_to_cpu(flags);
- flags &= ~PCI_MSI_FLAGS_QSIZE;
- flags |= (size & 0x7) << 4;
- flags = cpu_to_le16(flags);
- pci_set_word(config + PCI_MSI_FLAGS, flags);
+ vdev->msi_vectors = g_malloc0(vdev->msix->entries * sizeof(VFIOMSIVector));
+
+ vdev->interrupt = VFIO_INT_MSIX;
+
+ if (msix_set_vector_notifiers(&vdev->pdev, vfio_msix_vector_use,
+ vfio_msix_vector_release)) {
+ error_report("vfio: msix_set_vector_notifiers failed\n");
+ }
+
+ DPRINTF("%s(%04x:%02x:%02x.%x)\n", __func__, vdev->host.domain,
+ vdev->host.bus, vdev->host.slot, vdev->host.function);
}
static void vfio_enable_msi(VFIODevice *vdev)
@@ -503,19 +612,43 @@
return;
}
- msi_set_qsize(&vdev->pdev, vdev->nr_vectors);
+ vdev->interrupt = VFIO_INT_MSI;
DPRINTF("%s(%04x:%02x:%02x.%x) Enabled %d MSI vectors\n", __func__,
vdev->host.domain, vdev->host.bus, vdev->host.slot,
vdev->host.function, vdev->nr_vectors);
}
-static void vfio_disable_msi_x(VFIODevice *vdev, bool msix)
+static void vfio_disable_msi_common(VFIODevice *vdev)
+{
+ g_free(vdev->msi_vectors);
+ vdev->msi_vectors = NULL;
+ vdev->nr_vectors = 0;
+ vdev->interrupt = VFIO_INT_NONE;
+
+ vfio_enable_intx(vdev);
+}
+
+static void vfio_disable_msix(VFIODevice *vdev)
+{
+ msix_unset_vector_notifiers(&vdev->pdev);
+
+ if (vdev->nr_vectors) {
+ vfio_disable_irqindex(vdev, VFIO_PCI_MSIX_IRQ_INDEX);
+ }
+
+ vfio_disable_msi_common(vdev);
+
+ DPRINTF("%s(%04x:%02x:%02x.%x, msi%s)\n", __func__,
+ vdev->host.domain, vdev->host.bus, vdev->host.slot,
+ vdev->host.function, msix ? "x" : "");
+}
+
+static void vfio_disable_msi(VFIODevice *vdev)
{
int i;
- vfio_disable_irqindex(vdev, msix ? VFIO_PCI_MSIX_IRQ_INDEX :
- VFIO_PCI_MSI_IRQ_INDEX);
+ vfio_disable_irqindex(vdev, VFIO_PCI_MSI_IRQ_INDEX);
for (i = 0; i < vdev->nr_vectors; i++) {
VFIOMSIVector *vector = &vdev->msi_vectors[i];
@@ -534,26 +667,13 @@
NULL, NULL, NULL);
}
- if (msix) {
- msix_vector_unuse(&vdev->pdev, i);
- }
-
event_notifier_cleanup(&vector->interrupt);
}
- g_free(vdev->msi_vectors);
- vdev->msi_vectors = NULL;
- vdev->nr_vectors = 0;
+ vfio_disable_msi_common(vdev);
- if (!msix) {
- msi_set_qsize(&vdev->pdev, 0); /* Actually still means 1 vector */
- }
-
- DPRINTF("%s(%04x:%02x:%02x.%x, msi%s)\n", __func__,
- vdev->host.domain, vdev->host.bus, vdev->host.slot,
- vdev->host.function, msix ? "x" : "");
-
- vfio_enable_intx(vdev);
+ DPRINTF("%s(%04x:%02x:%02x.%x)\n", __func__, vdev->host.domain,
+ vdev->host.bus, vdev->host.slot, vdev->host.function);
}
/*
@@ -601,7 +721,7 @@
* which access will service the interrupt, so we're potentially
* getting quite a few host interrupts per guest interrupt.
*/
- vfio_eoi(DO_UPCAST(VFIODevice, bars[bar->nr], bar));
+ vfio_eoi(container_of(bar, VFIODevice, bars[bar->nr]));
}
static uint64_t vfio_bar_read(void *opaque,
@@ -641,7 +761,7 @@
__func__, bar->nr, addr, size, data);
/* Same as write above */
- vfio_eoi(DO_UPCAST(VFIODevice, bars[bar->nr], bar));
+ vfio_eoi(container_of(bar, VFIODevice, bars[bar->nr]));
return data;
}
@@ -739,7 +859,7 @@
if (!was_enabled && is_enabled) {
vfio_enable_msi(vdev);
} else if (was_enabled && !is_enabled) {
- vfio_disable_msi_x(vdev, false);
+ vfio_disable_msi(vdev);
}
}
@@ -752,9 +872,9 @@
is_enabled = msix_enabled(pdev);
if (!was_enabled && is_enabled) {
- /* vfio_msix_vector_use handles this automatically */
+ vfio_enable_msix(vdev);
} else if (was_enabled && !is_enabled) {
- vfio_disable_msi_x(vdev, true);
+ vfio_disable_msix(vdev);
}
}
}
@@ -762,29 +882,6 @@
/*
* DMA - Mapping and unmapping for the "type1" IOMMU interface used on x86
*/
-static int vfio_dma_map(VFIOContainer *container, target_phys_addr_t iova,
- ram_addr_t size, void *vaddr, bool readonly)
-{
- struct vfio_iommu_type1_dma_map map = {
- .argsz = sizeof(map),
- .flags = VFIO_DMA_MAP_FLAG_READ,
- .vaddr = (__u64)(intptr_t)vaddr,
- .iova = iova,
- .size = size,
- };
-
- if (!readonly) {
- map.flags |= VFIO_DMA_MAP_FLAG_WRITE;
- }
-
- if (ioctl(container->fd, VFIO_IOMMU_MAP_DMA, &map)) {
- DPRINTF("VFIO_MAP_DMA: %d\n", -errno);
- return -errno;
- }
-
- return 0;
-}
-
static int vfio_dma_unmap(VFIOContainer *container,
target_phys_addr_t iova, ram_addr_t size)
{
@@ -803,6 +900,36 @@
return 0;
}
+static int vfio_dma_map(VFIOContainer *container, target_phys_addr_t iova,
+ ram_addr_t size, void *vaddr, bool readonly)
+{
+ struct vfio_iommu_type1_dma_map map = {
+ .argsz = sizeof(map),
+ .flags = VFIO_DMA_MAP_FLAG_READ,
+ .vaddr = (__u64)(uintptr_t)vaddr,
+ .iova = iova,
+ .size = size,
+ };
+
+ if (!readonly) {
+ map.flags |= VFIO_DMA_MAP_FLAG_WRITE;
+ }
+
+ /*
+ * Try the mapping, if it fails with EBUSY, unmap the region and try
+ * again. This shouldn't be necessary, but we sometimes see it in
+ * the the VGA ROM space.
+ */
+ if (ioctl(container->fd, VFIO_IOMMU_MAP_DMA, &map) == 0 ||
+ (errno == EBUSY && vfio_dma_unmap(container, iova, size) == 0 &&
+ ioctl(container->fd, VFIO_IOMMU_MAP_DMA, &map) == 0)) {
+ return 0;
+ }
+
+ DPRINTF("VFIO_MAP_DMA: %d\n", -errno);
+ return -errno;
+}
+
static void vfio_listener_dummy1(MemoryListener *listener)
{
/* We don't do batching (begin/commit) or care about logging */
@@ -942,10 +1069,10 @@
vfio_disable_intx(vdev);
break;
case VFIO_INT_MSI:
- vfio_disable_msi_x(vdev, false);
+ vfio_disable_msi(vdev);
break;
case VFIO_INT_MSIX:
- vfio_disable_msi_x(vdev, true);
+ vfio_disable_msix(vdev);
break;
}
}
@@ -956,14 +1083,6 @@
bool msi_64bit, msi_maskbit;
int ret, entries;
- /*
- * TODO: don't peek into msi_supported, let msi_init fail and
- * check for ENOTSUP
- */
- if (!msi_supported) {
- return 0;
- }
-
if (pread(vdev->fd, &ctrl, sizeof(ctrl),
vdev->config_offset + pos + PCI_CAP_FLAGS) != sizeof(ctrl)) {
return -errno;
@@ -979,6 +1098,9 @@
ret = msi_init(&vdev->pdev, pos, entries, msi_64bit, msi_maskbit);
if (ret < 0) {
+ if (ret == -ENOTSUP) {
+ return 0;
+ }
error_report("vfio: msi_init failed\n");
return ret;
}
@@ -1045,33 +1167,19 @@
{
int ret;
- /*
- * TODO: don't peek into msi_supported, let msix_init fail and
- * check for ENOTSUP
- */
- if (!msi_supported) {
- return 0;
- }
-
ret = msix_init(&vdev->pdev, vdev->msix->entries,
&vdev->bars[vdev->msix->table_bar].mem,
vdev->msix->table_bar, vdev->msix->table_offset,
&vdev->bars[vdev->msix->pba_bar].mem,
vdev->msix->pba_bar, vdev->msix->pba_offset, pos);
if (ret < 0) {
+ if (ret == -ENOTSUP) {
+ return 0;
+ }
error_report("vfio: msix_init failed\n");
return ret;
}
- ret = msix_set_vector_notifiers(&vdev->pdev, vfio_msix_vector_use,
- vfio_msix_vector_release);
- if (ret) {
- error_report("vfio: msix_set_vector_notifiers failed %d\n", ret);
- msix_uninit(&vdev->pdev, &vdev->bars[vdev->msix->table_bar].mem,
- &vdev->bars[vdev->msix->pba_bar].mem);
- return ret;
- }
-
return 0;
}
@@ -1080,12 +1188,6 @@
msi_uninit(&vdev->pdev);
if (vdev->msix) {
- /* FIXME: Why can't unset just silently do nothing?? */
- if (vdev->pdev.msix_vector_use_notifier &&
- vdev->pdev.msix_vector_release_notifier) {
- msix_unset_vector_notifiers(&vdev->pdev);
- }
-
msix_uninit(&vdev->pdev, &vdev->bars[vdev->msix->table_bar].mem,
&vdev->bars[vdev->msix->pba_bar].mem);
}
@@ -1766,17 +1868,8 @@
}
if (vfio_pci_read_config(&vdev->pdev, PCI_INTERRUPT_PIN, 1)) {
- if (vdev->intx.intx && strcmp(vdev->intx.intx, "off")) {
- error_report("vfio: Unknown option x-intx=%s, "
- "valid options: \"off\".\n", vdev->intx.intx);
- ret = -EINVAL;
- goto out_teardown;
- }
-
- if (vdev->intx.intx && !strcmp(vdev->intx.intx, "off")) {
- vdev->intx.disabled = true;
- }
-
+ vdev->intx.mmap_timer = qemu_new_timer_ms(vm_clock,
+ vfio_intx_mmap_enable, vdev);
ret = vfio_enable_intx(vdev);
if (ret) {
goto out_teardown;
@@ -1802,6 +1895,9 @@
pci_device_set_intx_routing_notifier(&vdev->pdev, NULL);
vfio_disable_interrupts(vdev);
+ if (vdev->intx.mmap_timer) {
+ qemu_free_timer(vdev->intx.mmap_timer);
+ }
vfio_teardown_msi(vdev);
vfio_unmap_bars(vdev);
vfio_put_device(vdev);
@@ -1812,21 +1908,37 @@
{
PCIDevice *pdev = DO_UPCAST(PCIDevice, qdev, dev);
VFIODevice *vdev = DO_UPCAST(VFIODevice, pdev, pdev);
+ uint16_t cmd;
- if (!vdev->reset_works) {
- return;
+ DPRINTF("%s(%04x:%02x:%02x.%x)\n", __func__, vdev->host.domain,
+ vdev->host.bus, vdev->host.slot, vdev->host.function);
+
+ vfio_disable_interrupts(vdev);
+
+ /*
+ * Stop any ongoing DMA by disconecting I/O, MMIO, and bus master.
+ * Also put INTx Disable in known state.
+ */
+ cmd = vfio_pci_read_config(pdev, PCI_COMMAND, 2);
+ cmd &= ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER |
+ PCI_COMMAND_INTX_DISABLE);
+ vfio_pci_write_config(pdev, PCI_COMMAND, cmd, 2);
+
+ if (vdev->reset_works) {
+ if (ioctl(vdev->fd, VFIO_DEVICE_RESET)) {
+ error_report("vfio: Error unable to reset physical device "
+ "(%04x:%02x:%02x.%x): %m\n", vdev->host.domain,
+ vdev->host.bus, vdev->host.slot, vdev->host.function);
+ }
}
- if (ioctl(vdev->fd, VFIO_DEVICE_RESET)) {
- error_report("vfio: Error unable to reset physical device "
- "(%04x:%02x:%02x.%x): %m\n", vdev->host.domain,
- vdev->host.bus, vdev->host.slot, vdev->host.function);
- }
+ vfio_enable_intx(vdev);
}
static Property vfio_pci_dev_properties[] = {
DEFINE_PROP_PCI_HOST_DEVADDR("host", VFIODevice, host),
- DEFINE_PROP_STRING("x-intx", VFIODevice, intx.intx),
+ DEFINE_PROP_UINT32("x-intx-mmap-timeout-ms", VFIODevice,
+ intx.mmap_timeout, 1100),
/*
* TODO - support passed fds... is this necessary?
* DEFINE_PROP_STRING("vfiofd", VFIODevice, vfiofd_name),
diff --git a/hw/vfio_pci_int.h b/hw/vfio_pci_int.h
deleted file mode 100644
index 3812d8d..0000000
--- a/hw/vfio_pci_int.h
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * vfio based device assignment support
- *
- * Copyright Red Hat, Inc. 2012
- *
- * Authors:
- * Alex Williamson <alex.williamson@redhat.com>
- *
- * This work is licensed under the terms of the GNU GPL, version 2. See
- * the COPYING file in the top-level directory.
- */
-
-#ifndef HW_VFIO_PCI_INT_H
-#define HW_VFIO_PCI_INT_H
-
-#include "qemu-common.h"
-#include "qemu-queue.h"
-#include "pci.h"
-#include "event_notifier.h"
-
-typedef struct VFIOBAR {
- off_t fd_offset; /* offset of BAR within device fd */
- int fd; /* device fd, allows us to pass VFIOBAR as opaque data */
- MemoryRegion mem; /* slow, read/write access */
- MemoryRegion mmap_mem; /* direct mapped access */
- void *mmap;
- size_t size;
- uint32_t flags; /* VFIO region flags (rd/wr/mmap) */
- uint8_t nr; /* cache the BAR number for debug */
-} VFIOBAR;
-
-typedef struct VFIOINTx {
- bool pending; /* interrupt pending */
- bool kvm_accel; /* set when QEMU bypass through KVM enabled */
- uint8_t pin; /* which pin to pull for qemu_set_irq */
- EventNotifier interrupt; /* eventfd triggered on interrupt */
- EventNotifier unmask; /* eventfd for unmask on QEMU bypass */
- PCIINTxRoute route; /* routing info for QEMU bypass */
- bool disabled;
- char *intx;
-} VFIOINTx;
-
-struct VFIODevice;
-
-typedef struct VFIOMSIVector {
- EventNotifier interrupt; /* eventfd triggered on interrupt */
- struct VFIODevice *vdev; /* back pointer to device */
- int virq; /* KVM irqchip route for QEMU bypass */
- bool use;
-} VFIOMSIVector;
-
-enum {
- VFIO_INT_NONE = 0,
- VFIO_INT_INTx = 1,
- VFIO_INT_MSI = 2,
- VFIO_INT_MSIX = 3,
-};
-
-struct VFIOGroup;
-
-typedef struct VFIOContainer {
- int fd; /* /dev/vfio/vfio, empowered by the attached groups */
- struct {
- /* enable abstraction to support various iommu backends */
- union {
- MemoryListener listener; /* Used by type1 iommu */
- };
- void (*release)(struct VFIOContainer *);
- } iommu_data;
- QLIST_HEAD(, VFIOGroup) group_list;
- QLIST_ENTRY(VFIOContainer) next;
-} VFIOContainer;
-
-/* Cache of MSI-X setup plus extra mmap and memory region for split BAR map */
-typedef struct VFIOMSIXInfo {
- uint8_t table_bar;
- uint8_t pba_bar;
- uint16_t entries;
- uint32_t table_offset;
- uint32_t pba_offset;
- MemoryRegion mmap_mem;
- void *mmap;
-} VFIOMSIXInfo;
-
-typedef struct VFIODevice {
- PCIDevice pdev;
- int fd;
- VFIOINTx intx;
- unsigned int config_size;
- off_t config_offset; /* Offset of config space region within device fd */
- unsigned int rom_size;
- off_t rom_offset; /* Offset of ROM region within device fd */
- int msi_cap_size;
- VFIOMSIVector *msi_vectors;
- VFIOMSIXInfo *msix;
- int nr_vectors; /* Number of MSI/MSIX vectors currently in use */
- int interrupt; /* Current interrupt type */
- VFIOBAR bars[PCI_NUM_REGIONS - 1]; /* No ROM */
- PCIHostDeviceAddress host;
- QLIST_ENTRY(VFIODevice) next;
- struct VFIOGroup *group;
- bool reset_works;
-} VFIODevice;
-
-typedef struct VFIOGroup {
- int fd;
- int groupid;
- VFIOContainer *container;
- QLIST_HEAD(, VFIODevice) device_list;
- QLIST_ENTRY(VFIOGroup) next;
- QLIST_ENTRY(VFIOGroup) container_next;
-} VFIOGroup;
-
-#endif /* HW_VFIO_PCI_INT_H */
diff --git a/hw/virtio-net.c b/hw/virtio-net.c
index 247d7be..8342391 100644
--- a/hw/virtio-net.c
+++ b/hw/virtio-net.c
@@ -973,6 +973,11 @@
}
}
n->mac_table.first_multi = i;
+
+ /* nc.link_down can't be migrated, so infer link_down according
+ * to link status bit in n->status */
+ n->nic->nc.link_down = (n->status & VIRTIO_NET_S_LINK_UP) == 0;
+
return 0;
}
diff --git a/hw/xilinx_spi.c b/hw/xilinx_spi.c
new file mode 100644
index 0000000..5cdf967
--- /dev/null
+++ b/hw/xilinx_spi.c
@@ -0,0 +1,385 @@
+/*
+ * QEMU model of the Xilinx SPI Controller
+ *
+ * Copyright (C) 2010 Edgar E. Iglesias.
+ * Copyright (C) 2012 Peter A. G. Crosthwaite <peter.crosthwaite@petalogix.com>
+ * Copyright (C) 2012 PetaLogix
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "sysbus.h"
+#include "sysemu.h"
+#include "qemu-log.h"
+#include "fifo.h"
+
+#include "ssi.h"
+
+#ifdef XILINX_SPI_ERR_DEBUG
+#define DB_PRINT(...) do { \
+ fprintf(stderr, ": %s: ", __func__); \
+ fprintf(stderr, ## __VA_ARGS__); \
+ } while (0);
+#else
+ #define DB_PRINT(...)
+#endif
+
+#define R_DGIER (0x1c / 4)
+#define R_DGIER_IE (1 << 31)
+
+#define R_IPISR (0x20 / 4)
+#define IRQ_DRR_NOT_EMPTY (1 << (31 - 23))
+#define IRQ_DRR_OVERRUN (1 << (31 - 26))
+#define IRQ_DRR_FULL (1 << (31 - 27))
+#define IRQ_TX_FF_HALF_EMPTY (1 << 6)
+#define IRQ_DTR_UNDERRUN (1 << 3)
+#define IRQ_DTR_EMPTY (1 << (31 - 29))
+
+#define R_IPIER (0x28 / 4)
+#define R_SRR (0x40 / 4)
+#define R_SPICR (0x60 / 4)
+#define R_SPICR_TXFF_RST (1 << 5)
+#define R_SPICR_RXFF_RST (1 << 6)
+#define R_SPICR_MTI (1 << 8)
+
+#define R_SPISR (0x64 / 4)
+#define SR_TX_FULL (1 << 3)
+#define SR_TX_EMPTY (1 << 2)
+#define SR_RX_FULL (1 << 1)
+#define SR_RX_EMPTY (1 << 0)
+
+#define R_SPIDTR (0x68 / 4)
+#define R_SPIDRR (0x6C / 4)
+#define R_SPISSR (0x70 / 4)
+#define R_TX_FF_OCY (0x74 / 4)
+#define R_RX_FF_OCY (0x78 / 4)
+#define R_MAX (0x7C / 4)
+
+#define FIFO_CAPACITY 256
+
+typedef struct XilinxSPI {
+ SysBusDevice busdev;
+ MemoryRegion mmio;
+
+ qemu_irq irq;
+ int irqline;
+
+ uint8_t num_cs;
+ qemu_irq *cs_lines;
+
+ SSIBus *spi;
+
+ Fifo8 rx_fifo;
+ Fifo8 tx_fifo;
+
+ uint32_t regs[R_MAX];
+} XilinxSPI;
+
+static void txfifo_reset(XilinxSPI *s)
+{
+ fifo8_reset(&s->tx_fifo);
+
+ s->regs[R_SPISR] &= ~SR_TX_FULL;
+ s->regs[R_SPISR] |= SR_TX_EMPTY;
+}
+
+static void rxfifo_reset(XilinxSPI *s)
+{
+ fifo8_reset(&s->rx_fifo);
+
+ s->regs[R_SPISR] |= SR_RX_EMPTY;
+ s->regs[R_SPISR] &= ~SR_RX_FULL;
+}
+
+static void xlx_spi_update_cs(XilinxSPI *s)
+{
+ int i;
+
+ for (i = 0; i < s->num_cs; ++i) {
+ qemu_set_irq(s->cs_lines[i], !(~s->regs[R_SPISSR] & 1 << i));
+ }
+}
+
+static void xlx_spi_update_irq(XilinxSPI *s)
+{
+ uint32_t pending;
+
+ s->regs[R_IPISR] |=
+ (!fifo8_is_empty(&s->rx_fifo) ? IRQ_DRR_NOT_EMPTY : 0) |
+ (fifo8_is_full(&s->rx_fifo) ? IRQ_DRR_FULL : 0);
+
+ pending = s->regs[R_IPISR] & s->regs[R_IPIER];
+
+ pending = pending && (s->regs[R_DGIER] & R_DGIER_IE);
+ pending = !!pending;
+
+ /* This call lies right in the data paths so don't call the
+ irq chain unless things really changed. */
+ if (pending != s->irqline) {
+ s->irqline = pending;
+ DB_PRINT("irq_change of state %d ISR:%x IER:%X\n",
+ pending, s->regs[R_IPISR], s->regs[R_IPIER]);
+ qemu_set_irq(s->irq, pending);
+ }
+
+}
+
+static void xlx_spi_do_reset(XilinxSPI *s)
+{
+ memset(s->regs, 0, sizeof s->regs);
+
+ rxfifo_reset(s);
+ txfifo_reset(s);
+
+ s->regs[R_SPISSR] = ~0;
+ xlx_spi_update_irq(s);
+ xlx_spi_update_cs(s);
+}
+
+static void xlx_spi_reset(DeviceState *d)
+{
+ xlx_spi_do_reset(DO_UPCAST(XilinxSPI, busdev.qdev, d));
+}
+
+static inline int spi_master_enabled(XilinxSPI *s)
+{
+ return !(s->regs[R_SPICR] & R_SPICR_MTI);
+}
+
+static void spi_flush_txfifo(XilinxSPI *s)
+{
+ uint32_t tx;
+ uint32_t rx;
+
+ while (!fifo8_is_empty(&s->tx_fifo)) {
+ tx = (uint32_t)fifo8_pop(&s->tx_fifo);
+ DB_PRINT("data tx:%x\n", tx);
+ rx = ssi_transfer(s->spi, tx);
+ DB_PRINT("data rx:%x\n", rx);
+ if (fifo8_is_full(&s->rx_fifo)) {
+ s->regs[R_IPISR] |= IRQ_DRR_OVERRUN;
+ } else {
+ fifo8_push(&s->rx_fifo, (uint8_t)rx);
+ if (fifo8_is_full(&s->rx_fifo)) {
+ s->regs[R_SPISR] |= SR_RX_FULL;
+ s->regs[R_IPISR] |= IRQ_DRR_FULL;
+ }
+ }
+
+ s->regs[R_SPISR] &= ~SR_RX_EMPTY;
+ s->regs[R_SPISR] &= ~SR_TX_FULL;
+ s->regs[R_SPISR] |= SR_TX_EMPTY;
+
+ s->regs[R_IPISR] |= IRQ_DTR_EMPTY;
+ s->regs[R_IPISR] |= IRQ_DRR_NOT_EMPTY;
+ }
+
+}
+
+static uint64_t
+spi_read(void *opaque, target_phys_addr_t addr, unsigned int size)
+{
+ XilinxSPI *s = opaque;
+ uint32_t r = 0;
+
+ addr >>= 2;
+ switch (addr) {
+ case R_SPIDRR:
+ if (fifo8_is_empty(&s->rx_fifo)) {
+ DB_PRINT("Read from empty FIFO!\n");
+ return 0xdeadbeef;
+ }
+
+ s->regs[R_SPISR] &= ~SR_RX_FULL;
+ r = fifo8_pop(&s->rx_fifo);
+ if (fifo8_is_empty(&s->rx_fifo)) {
+ s->regs[R_SPISR] |= SR_RX_EMPTY;
+ }
+ break;
+
+ case R_SPISR:
+ r = s->regs[addr];
+ break;
+
+ default:
+ if (addr < ARRAY_SIZE(s->regs)) {
+ r = s->regs[addr];
+ }
+ break;
+
+ }
+ DB_PRINT("addr=" TARGET_FMT_plx " = %x\n", addr * 4, r);
+ xlx_spi_update_irq(s);
+ return r;
+}
+
+static void
+spi_write(void *opaque, target_phys_addr_t addr,
+ uint64_t val64, unsigned int size)
+{
+ XilinxSPI *s = opaque;
+ uint32_t value = val64;
+
+ DB_PRINT("addr=" TARGET_FMT_plx " = %x\n", addr, value);
+ addr >>= 2;
+ switch (addr) {
+ case R_SRR:
+ if (value != 0xa) {
+ DB_PRINT("Invalid write to SRR %x\n", value);
+ } else {
+ xlx_spi_do_reset(s);
+ }
+ break;
+
+ case R_SPIDTR:
+ s->regs[R_SPISR] &= ~SR_TX_EMPTY;
+ fifo8_push(&s->tx_fifo, (uint8_t)value);
+ if (fifo8_is_full(&s->tx_fifo)) {
+ s->regs[R_SPISR] |= SR_TX_FULL;
+ }
+ if (!spi_master_enabled(s)) {
+ goto done;
+ } else {
+ DB_PRINT("DTR and master enabled\n");
+ }
+ spi_flush_txfifo(s);
+ break;
+
+ case R_SPISR:
+ DB_PRINT("Invalid write to SPISR %x\n", value);
+ break;
+
+ case R_IPISR:
+ /* Toggle the bits. */
+ s->regs[addr] ^= value;
+ break;
+
+ /* Slave Select Register. */
+ case R_SPISSR:
+ s->regs[addr] = value;
+ xlx_spi_update_cs(s);
+ break;
+
+ case R_SPICR:
+ /* FIXME: reset irq and sr state to empty queues. */
+ if (value & R_SPICR_RXFF_RST) {
+ rxfifo_reset(s);
+ }
+
+ if (value & R_SPICR_TXFF_RST) {
+ txfifo_reset(s);
+ }
+ value &= ~(R_SPICR_RXFF_RST | R_SPICR_TXFF_RST);
+ s->regs[addr] = value;
+
+ if (!(value & R_SPICR_MTI)) {
+ spi_flush_txfifo(s);
+ }
+ break;
+
+ default:
+ if (addr < ARRAY_SIZE(s->regs)) {
+ s->regs[addr] = value;
+ }
+ break;
+ }
+
+done:
+ xlx_spi_update_irq(s);
+}
+
+static const MemoryRegionOps spi_ops = {
+ .read = spi_read,
+ .write = spi_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4
+ }
+};
+
+static int xilinx_spi_init(SysBusDevice *dev)
+{
+ int i;
+ XilinxSPI *s = FROM_SYSBUS(typeof(*s), dev);
+
+ DB_PRINT("\n");
+
+ s->spi = ssi_create_bus(&dev->qdev, "spi");
+
+ sysbus_init_irq(dev, &s->irq);
+ s->cs_lines = g_new(qemu_irq, s->num_cs);
+ ssi_auto_connect_slaves(DEVICE(s), s->cs_lines, s->spi);
+ for (i = 0; i < s->num_cs; ++i) {
+ sysbus_init_irq(dev, &s->cs_lines[i]);
+ }
+
+ memory_region_init_io(&s->mmio, &spi_ops, s, "xilinx-spi", R_MAX * 4);
+ sysbus_init_mmio(dev, &s->mmio);
+
+ s->irqline = -1;
+
+ fifo8_create(&s->tx_fifo, FIFO_CAPACITY);
+ fifo8_create(&s->rx_fifo, FIFO_CAPACITY);
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_xilinx_spi = {
+ .name = "xilinx_spi",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_FIFO8(tx_fifo, XilinxSPI),
+ VMSTATE_FIFO8(rx_fifo, XilinxSPI),
+ VMSTATE_UINT32_ARRAY(regs, XilinxSPI, R_MAX),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static Property xilinx_spi_properties[] = {
+ DEFINE_PROP_UINT8("num-ss-bits", XilinxSPI, num_cs, 1),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void xilinx_spi_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = xilinx_spi_init;
+ dc->reset = xlx_spi_reset;
+ dc->props = xilinx_spi_properties;
+ dc->vmsd = &vmstate_xilinx_spi;
+}
+
+static TypeInfo xilinx_spi_info = {
+ .name = "xlnx.xps-spi",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(XilinxSPI),
+ .class_init = xilinx_spi_class_init,
+};
+
+static void xilinx_spi_register_types(void)
+{
+ type_register_static(&xilinx_spi_info);
+}
+
+type_init(xilinx_spi_register_types)
diff --git a/hw/xilinx_spips.c b/hw/xilinx_spips.c
new file mode 100644
index 0000000..f64a782
--- /dev/null
+++ b/hw/xilinx_spips.c
@@ -0,0 +1,354 @@
+/*
+ * QEMU model of the Xilinx Zynq SPI controller
+ *
+ * Copyright (c) 2012 Peter A. G. Crosthwaite
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "sysbus.h"
+#include "sysemu.h"
+#include "ptimer.h"
+#include "qemu-log.h"
+#include "fifo.h"
+#include "ssi.h"
+
+#ifdef XILINX_SPIPS_ERR_DEBUG
+#define DB_PRINT(...) do { \
+ fprintf(stderr, ": %s: ", __func__); \
+ fprintf(stderr, ## __VA_ARGS__); \
+ } while (0);
+#else
+ #define DB_PRINT(...)
+#endif
+
+/* config register */
+#define R_CONFIG (0x00 / 4)
+#define MODEFAIL_GEN_EN (1 << 17)
+#define MAN_START_COM (1 << 16)
+#define MAN_START_EN (1 << 15)
+#define MANUAL_CS (1 << 14)
+#define CS (0xF << 10)
+#define CS_SHIFT (10)
+#define PERI_SEL (1 << 9)
+#define REF_CLK (1 << 8)
+#define FIFO_WIDTH (3 << 6)
+#define BAUD_RATE_DIV (7 << 3)
+#define CLK_PH (1 << 2)
+#define CLK_POL (1 << 1)
+#define MODE_SEL (1 << 0)
+
+/* interrupt mechanism */
+#define R_INTR_STATUS (0x04 / 4)
+#define R_INTR_EN (0x08 / 4)
+#define R_INTR_DIS (0x0C / 4)
+#define R_INTR_MASK (0x10 / 4)
+#define IXR_TX_FIFO_UNDERFLOW (1 << 6)
+#define IXR_RX_FIFO_FULL (1 << 5)
+#define IXR_RX_FIFO_NOT_EMPTY (1 << 4)
+#define IXR_TX_FIFO_FULL (1 << 3)
+#define IXR_TX_FIFO_NOT_FULL (1 << 2)
+#define IXR_TX_FIFO_MODE_FAIL (1 << 1)
+#define IXR_RX_FIFO_OVERFLOW (1 << 0)
+#define IXR_ALL ((IXR_TX_FIFO_UNDERFLOW<<1)-1)
+
+#define R_EN (0x14 / 4)
+#define R_DELAY (0x18 / 4)
+#define R_TX_DATA (0x1C / 4)
+#define R_RX_DATA (0x20 / 4)
+#define R_SLAVE_IDLE_COUNT (0x24 / 4)
+#define R_TX_THRES (0x28 / 4)
+#define R_RX_THRES (0x2C / 4)
+#define R_MOD_ID (0xFC / 4)
+
+#define R_MAX (R_MOD_ID+1)
+
+/* size of TXRX FIFOs */
+#define NUM_CS_LINES 4
+#define RXFF_A 32
+#define TXFF_A 32
+
+typedef struct {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+ qemu_irq irq;
+ int irqline;
+
+ qemu_irq cs_lines[NUM_CS_LINES];
+ SSIBus *spi;
+
+ Fifo8 rx_fifo;
+ Fifo8 tx_fifo;
+
+ uint32_t regs[R_MAX];
+} XilinxSPIPS;
+
+static void xilinx_spips_update_cs_lines(XilinxSPIPS *s)
+{
+ int i;
+ bool found = false;
+ int field = s->regs[R_CONFIG] >> CS_SHIFT;
+
+ for (i = 0; i < NUM_CS_LINES; i++) {
+ if (~field & (1 << i) && !found) {
+ found = true;
+ DB_PRINT("selecting slave %d\n", i);
+ qemu_set_irq(s->cs_lines[i], 0);
+ } else {
+ qemu_set_irq(s->cs_lines[i], 1);
+ }
+ }
+}
+
+static void xilinx_spips_update_ixr(XilinxSPIPS *s)
+{
+ /* These are set/cleared as they occur */
+ s->regs[R_INTR_STATUS] &= (IXR_TX_FIFO_UNDERFLOW | IXR_RX_FIFO_OVERFLOW |
+ IXR_TX_FIFO_MODE_FAIL);
+ /* these are pure functions of fifo state, set them here */
+ s->regs[R_INTR_STATUS] |=
+ (fifo8_is_full(&s->rx_fifo) ? IXR_RX_FIFO_FULL : 0) |
+ (s->rx_fifo.num >= s->regs[R_RX_THRES] ? IXR_RX_FIFO_NOT_EMPTY : 0) |
+ (fifo8_is_full(&s->tx_fifo) ? IXR_TX_FIFO_FULL : 0) |
+ (s->tx_fifo.num < s->regs[R_TX_THRES] ? IXR_TX_FIFO_NOT_FULL : 0);
+ /* drive external interrupt pin */
+ int new_irqline = !!(s->regs[R_INTR_MASK] & s->regs[R_INTR_STATUS] &
+ IXR_ALL);
+ if (new_irqline != s->irqline) {
+ s->irqline = new_irqline;
+ qemu_set_irq(s->irq, s->irqline);
+ }
+}
+
+static void xilinx_spips_reset(DeviceState *d)
+{
+ XilinxSPIPS *s = DO_UPCAST(XilinxSPIPS, busdev.qdev, d);
+
+ int i;
+ for (i = 0; i < R_MAX; i++) {
+ s->regs[i] = 0;
+ }
+
+ fifo8_reset(&s->rx_fifo);
+ fifo8_reset(&s->rx_fifo);
+ /* non zero resets */
+ s->regs[R_CONFIG] |= MODEFAIL_GEN_EN;
+ s->regs[R_SLAVE_IDLE_COUNT] = 0xFF;
+ s->regs[R_TX_THRES] = 1;
+ s->regs[R_RX_THRES] = 1;
+ /* FIXME: move magic number definition somewhere sensible */
+ s->regs[R_MOD_ID] = 0x01090106;
+ xilinx_spips_update_ixr(s);
+ xilinx_spips_update_cs_lines(s);
+}
+
+static void xilinx_spips_flush_txfifo(XilinxSPIPS *s)
+{
+ for (;;) {
+ uint32_t r;
+ uint8_t value;
+
+ if (fifo8_is_empty(&s->tx_fifo)) {
+ s->regs[R_INTR_STATUS] |= IXR_TX_FIFO_UNDERFLOW;
+ break;
+ } else {
+ value = fifo8_pop(&s->tx_fifo);
+ }
+
+ r = ssi_transfer(s->spi, (uint32_t)value);
+ DB_PRINT("tx = %02x rx = %02x\n", value, r);
+ if (fifo8_is_full(&s->rx_fifo)) {
+ s->regs[R_INTR_STATUS] |= IXR_RX_FIFO_OVERFLOW;
+ DB_PRINT("rx FIFO overflow");
+ } else {
+ fifo8_push(&s->rx_fifo, (uint8_t)r);
+ }
+ }
+ xilinx_spips_update_ixr(s);
+}
+
+static uint64_t xilinx_spips_read(void *opaque, target_phys_addr_t addr,
+ unsigned size)
+{
+ XilinxSPIPS *s = opaque;
+ uint32_t mask = ~0;
+ uint32_t ret;
+
+ addr >>= 2;
+ switch (addr) {
+ case R_CONFIG:
+ mask = 0x0002FFFF;
+ break;
+ case R_INTR_STATUS:
+ case R_INTR_MASK:
+ mask = IXR_ALL;
+ break;
+ case R_EN:
+ mask = 0x1;
+ break;
+ case R_SLAVE_IDLE_COUNT:
+ mask = 0xFF;
+ break;
+ case R_MOD_ID:
+ mask = 0x01FFFFFF;
+ break;
+ case R_INTR_EN:
+ case R_INTR_DIS:
+ case R_TX_DATA:
+ mask = 0;
+ break;
+ case R_RX_DATA:
+ ret = (uint32_t)fifo8_pop(&s->rx_fifo);
+ DB_PRINT("addr=" TARGET_FMT_plx " = %x\n", addr * 4, ret);
+ xilinx_spips_update_ixr(s);
+ return ret;
+ }
+ DB_PRINT("addr=" TARGET_FMT_plx " = %x\n", addr * 4, s->regs[addr] & mask);
+ return s->regs[addr] & mask;
+
+}
+
+static void xilinx_spips_write(void *opaque, target_phys_addr_t addr,
+ uint64_t value, unsigned size)
+{
+ int mask = ~0;
+ int man_start_com = 0;
+ XilinxSPIPS *s = opaque;
+
+ DB_PRINT("addr=" TARGET_FMT_plx " = %x\n", addr, (unsigned)value);
+ addr >>= 2;
+ switch (addr) {
+ case R_CONFIG:
+ mask = 0x0002FFFF;
+ if (value & MAN_START_COM) {
+ man_start_com = 1;
+ }
+ break;
+ case R_INTR_STATUS:
+ mask = IXR_ALL;
+ s->regs[R_INTR_STATUS] &= ~(mask & value);
+ goto no_reg_update;
+ case R_INTR_DIS:
+ mask = IXR_ALL;
+ s->regs[R_INTR_MASK] &= ~(mask & value);
+ goto no_reg_update;
+ case R_INTR_EN:
+ mask = IXR_ALL;
+ s->regs[R_INTR_MASK] |= mask & value;
+ goto no_reg_update;
+ case R_EN:
+ mask = 0x1;
+ break;
+ case R_SLAVE_IDLE_COUNT:
+ mask = 0xFF;
+ break;
+ case R_RX_DATA:
+ case R_INTR_MASK:
+ case R_MOD_ID:
+ mask = 0;
+ break;
+ case R_TX_DATA:
+ fifo8_push(&s->tx_fifo, (uint8_t)value);
+ goto no_reg_update;
+ }
+ s->regs[addr] = (s->regs[addr] & ~mask) | (value & mask);
+no_reg_update:
+ if (man_start_com) {
+ xilinx_spips_flush_txfifo(s);
+ }
+ xilinx_spips_update_ixr(s);
+ xilinx_spips_update_cs_lines(s);
+}
+
+static const MemoryRegionOps spips_ops = {
+ .read = xilinx_spips_read,
+ .write = xilinx_spips_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static int xilinx_spips_init(SysBusDevice *dev)
+{
+ XilinxSPIPS *s = FROM_SYSBUS(typeof(*s), dev);
+ int i;
+
+ DB_PRINT("inited device model\n");
+
+ s->spi = ssi_create_bus(&dev->qdev, "spi");
+
+ ssi_auto_connect_slaves(DEVICE(s), s->cs_lines, s->spi);
+ sysbus_init_irq(dev, &s->irq);
+ for (i = 0; i < NUM_CS_LINES; ++i) {
+ sysbus_init_irq(dev, &s->cs_lines[i]);
+ }
+
+ memory_region_init_io(&s->iomem, &spips_ops, s, "spi", R_MAX*4);
+ sysbus_init_mmio(dev, &s->iomem);
+
+ s->irqline = -1;
+
+ fifo8_create(&s->rx_fifo, RXFF_A);
+ fifo8_create(&s->tx_fifo, TXFF_A);
+
+ return 0;
+}
+
+static int xilinx_spips_post_load(void *opaque, int version_id)
+{
+ xilinx_spips_update_ixr((XilinxSPIPS *)opaque);
+ xilinx_spips_update_cs_lines((XilinxSPIPS *)opaque);
+ return 0;
+}
+
+static const VMStateDescription vmstate_xilinx_spips = {
+ .name = "xilinx_spips",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .post_load = xilinx_spips_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_FIFO8(tx_fifo, XilinxSPIPS),
+ VMSTATE_FIFO8(rx_fifo, XilinxSPIPS),
+ VMSTATE_UINT32_ARRAY(regs, XilinxSPIPS, R_MAX),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void xilinx_spips_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+ sdc->init = xilinx_spips_init;
+ dc->reset = xilinx_spips_reset;
+ dc->vmsd = &vmstate_xilinx_spips;
+}
+
+static const TypeInfo xilinx_spips_info = {
+ .name = "xilinx,spips",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(XilinxSPIPS),
+ .class_init = xilinx_spips_class_init,
+};
+
+static void xilinx_spips_register_types(void)
+{
+ type_register_static(&xilinx_spips_info);
+}
+
+type_init(xilinx_spips_register_types)
diff --git a/hw/xilinx_zynq.c b/hw/xilinx_zynq.c
index 7e6c273..fd46ba2 100644
--- a/hw/xilinx_zynq.c
+++ b/hw/xilinx_zynq.c
@@ -24,6 +24,9 @@
#include "flash.h"
#include "blockdev.h"
#include "loader.h"
+#include "ssi.h"
+
+#define NUM_SPI_FLASHES 4
#define FLASH_SIZE (64 * 1024 * 1024)
#define FLASH_SECTOR_SIZE (128 * 1024)
@@ -46,6 +49,34 @@
sysbus_connect_irq(s, 0, irq);
}
+static inline void zynq_init_spi_flashes(uint32_t base_addr, qemu_irq irq)
+{
+ DeviceState *dev;
+ SysBusDevice *busdev;
+ SSIBus *spi;
+ int i;
+
+ dev = qdev_create(NULL, "xilinx,spips");
+ qdev_init_nofail(dev);
+ busdev = sysbus_from_qdev(dev);
+ sysbus_mmio_map(busdev, 0, base_addr);
+ sysbus_connect_irq(busdev, 0, irq);
+
+ spi = (SSIBus *)qdev_get_child_bus(dev, "spi");
+
+ for (i = 0; i < NUM_SPI_FLASHES; ++i) {
+ qemu_irq cs_line;
+
+ dev = ssi_create_slave_no_init(spi, "m25p80");
+ qdev_prop_set_string(dev, "partname", "n25q128");
+ qdev_init_nofail(dev);
+
+ cs_line = qdev_get_gpio_in(dev, 0);
+ sysbus_connect_irq(busdev, i+1, cs_line);
+ }
+
+}
+
static void zynq_init(ram_addr_t ram_size, const char *boot_device,
const char *kernel_filename, const char *kernel_cmdline,
const char *initrd_filename, const char *cpu_model)
@@ -113,6 +144,9 @@
pic[n] = qdev_get_gpio_in(dev, n);
}
+ zynq_init_spi_flashes(0xE0006000, pic[58-IRQ_OFFSET]);
+ zynq_init_spi_flashes(0xE0007000, pic[81-IRQ_OFFSET]);
+
sysbus_create_simple("cadence_uart", 0xE0000000, pic[59-IRQ_OFFSET]);
sysbus_create_simple("cadence_uart", 0xE0001000, pic[82-IRQ_OFFSET]);
diff --git a/hw/z2.c b/hw/z2.c
index 289cee9..076fad2 100644
--- a/hw/z2.c
+++ b/hw/z2.c
@@ -161,10 +161,11 @@
static VMStateDescription vmstate_zipit_lcd_state = {
.name = "zipit-lcd",
- .version_id = 1,
- .minimum_version_id = 1,
- .minimum_version_id_old = 1,
+ .version_id = 2,
+ .minimum_version_id = 2,
+ .minimum_version_id_old = 2,
.fields = (VMStateField[]) {
+ VMSTATE_SSI_SLAVE(ssidev, ZipitLCD),
VMSTATE_INT32(selected, ZipitLCD),
VMSTATE_INT32(enabled, ZipitLCD),
VMSTATE_BUFFER(buf, ZipitLCD),
diff --git a/net.c b/net.c
index a187a7b..ae4bc0d 100644
--- a/net.c
+++ b/net.c
@@ -21,17 +21,14 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-#include "net.h"
-
#include "config-host.h"
-#include "net/tap.h"
-#include "net/socket.h"
-#include "net/dump.h"
-#include "net/slirp.h"
-#include "net/vde.h"
+#include "net.h"
+#include "net/clients.h"
#include "net/hub.h"
+#include "net/slirp.h"
#include "net/util.h"
+
#include "monitor.h"
#include "qemu-common.h"
#include "qemu_socket.h"
diff --git a/net/socket.h b/net/clients.h
similarity index 62%
rename from net/socket.h
rename to net/clients.h
index 3f8a092..c58cc60 100644
--- a/net/socket.h
+++ b/net/clients.h
@@ -21,13 +21,35 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-#ifndef QEMU_NET_SOCKET_H
-#define QEMU_NET_SOCKET_H
+#ifndef QEMU_NET_CLIENTS_H
+#define QEMU_NET_CLIENTS_H
#include "net.h"
#include "qapi-types.h"
+int net_init_dump(const NetClientOptions *opts, const char *name,
+ NetClientState *peer);
+
+#ifdef CONFIG_SLIRP
+int net_init_slirp(const NetClientOptions *opts, const char *name,
+ NetClientState *peer);
+#endif
+
+int net_init_hubport(const NetClientOptions *opts, const char *name,
+ NetClientState *peer);
+
int net_init_socket(const NetClientOptions *opts, const char *name,
NetClientState *peer);
-#endif /* QEMU_NET_SOCKET_H */
+int net_init_tap(const NetClientOptions *opts, const char *name,
+ NetClientState *peer);
+
+int net_init_bridge(const NetClientOptions *opts, const char *name,
+ NetClientState *peer);
+
+#ifdef CONFIG_VDE
+int net_init_vde(const NetClientOptions *opts, const char *name,
+ NetClientState *peer);
+#endif
+
+#endif /* QEMU_NET_CLIENTS_H */
diff --git a/net/dump.c b/net/dump.c
index 004231d..e0a5d74 100644
--- a/net/dump.c
+++ b/net/dump.c
@@ -22,7 +22,7 @@
* THE SOFTWARE.
*/
-#include "dump.h"
+#include "clients.h"
#include "qemu-common.h"
#include "qemu-error.h"
#include "qemu-log.h"
diff --git a/net/dump.h b/net/dump.h
deleted file mode 100644
index 33f152b..0000000
--- a/net/dump.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * QEMU System Emulator
- *
- * Copyright (c) 2003-2008 Fabrice Bellard
- *
- * 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 QEMU_NET_DUMP_H
-#define QEMU_NET_DUMP_H
-
-#include "net.h"
-#include "qapi-types.h"
-
-int net_init_dump(const NetClientOptions *opts, const char *name,
- NetClientState *peer);
-
-#endif /* QEMU_NET_DUMP_H */
diff --git a/net/hub.c b/net/hub.c
index 650a8b4..be41301 100644
--- a/net/hub.c
+++ b/net/hub.c
@@ -14,6 +14,7 @@
#include "monitor.h"
#include "net.h"
+#include "clients.h"
#include "hub.h"
#include "iov.h"
diff --git a/net/hub.h b/net/hub.h
index 26a1ade..4cbfdb1 100644
--- a/net/hub.h
+++ b/net/hub.h
@@ -17,8 +17,6 @@
#include "qemu-common.h"
-int net_init_hubport(const NetClientOptions *opts, const char *name,
- NetClientState *peer);
NetClientState *net_hub_add_port(int hub_id, const char *name);
NetClientState *net_hub_find_client_by_name(int hub_id, const char *name);
void net_hub_info(Monitor *mon);
diff --git a/net/slirp.c b/net/slirp.c
index 8db66ea..bf86a44 100644
--- a/net/slirp.c
+++ b/net/slirp.c
@@ -30,7 +30,8 @@
#include <sys/wait.h>
#endif
#include "net.h"
-#include "net/hub.h"
+#include "clients.h"
+#include "hub.h"
#include "monitor.h"
#include "qemu_socket.h"
#include "slirp/libslirp.h"
diff --git a/net/slirp.h b/net/slirp.h
index 5f685c4..2ca09b6 100644
--- a/net/slirp.h
+++ b/net/slirp.h
@@ -31,9 +31,6 @@
#ifdef CONFIG_SLIRP
-int net_init_slirp(const NetClientOptions *opts, const char *name,
- NetClientState *peer);
-
void net_slirp_hostfwd_add(Monitor *mon, const QDict *qdict);
void net_slirp_hostfwd_remove(Monitor *mon, const QDict *qdict);
diff --git a/net/socket.c b/net/socket.c
index f3d7878..b75d567 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -21,11 +21,10 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-#include "net/socket.h"
-
#include "config-host.h"
#include "net.h"
+#include "clients.h"
#include "monitor.h"
#include "qemu-char.h"
#include "qemu-common.h"
diff --git a/net/tap-win32.c b/net/tap-win32.c
index c0ea954..f1801e2 100644
--- a/net/tap-win32.c
+++ b/net/tap-win32.c
@@ -26,7 +26,7 @@
* distribution); if not, see <http://www.gnu.org/licenses/>.
*/
-#include "net/tap.h"
+#include "tap.h"
#include "qemu-common.h"
#include "net.h"
diff --git a/net/tap.c b/net/tap.c
index a88ae8f..df89caa 100644
--- a/net/tap.c
+++ b/net/tap.c
@@ -23,7 +23,7 @@
* THE SOFTWARE.
*/
-#include "net/tap.h"
+#include "tap.h"
#include "config-host.h"
@@ -34,6 +34,7 @@
#include <net/if.h>
#include "net.h"
+#include "clients.h"
#include "monitor.h"
#include "sysemu.h"
#include "qemu-char.h"
diff --git a/net/tap.h b/net/tap.h
index 0fb018c..d44d83a 100644
--- a/net/tap.h
+++ b/net/tap.h
@@ -32,9 +32,6 @@
#define DEFAULT_NETWORK_SCRIPT "/etc/qemu-ifup"
#define DEFAULT_NETWORK_DOWN_SCRIPT "/etc/qemu-ifdown"
-int net_init_tap(const NetClientOptions *opts, const char *name,
- NetClientState *peer);
-
int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required);
ssize_t tap_read_packet(int tapfd, uint8_t *buf, int maxlen);
@@ -58,7 +55,4 @@
struct vhost_net;
struct vhost_net *tap_get_vhost_net(NetClientState *nc);
-int net_init_bridge(const NetClientOptions *opts, const char *name,
- NetClientState *peer);
-
#endif /* QEMU_NET_TAP_H */
diff --git a/net/vde.c b/net/vde.c
index b91a6c7..275bda9 100644
--- a/net/vde.c
+++ b/net/vde.c
@@ -21,13 +21,12 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-#include "net/vde.h"
-
#include "config-host.h"
#include <libvdeplug.h>
#include "net.h"
+#include "clients.h"
#include "qemu-char.h"
#include "qemu-common.h"
#include "qemu-option.h"
diff --git a/net/vde.h b/net/vde.h
deleted file mode 100644
index 6ce6698..0000000
--- a/net/vde.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * QEMU System Emulator
- *
- * Copyright (c) 2003-2008 Fabrice Bellard
- *
- * 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 QEMU_NET_VDE_H
-#define QEMU_NET_VDE_H
-
-#include "qemu-common.h"
-#include "qapi-types.h"
-
-#ifdef CONFIG_VDE
-
-int net_init_vde(const NetClientOptions *opts, const char *name,
- NetClientState *peer);
-
-#endif /* CONFIG_VDE */
-
-#endif /* QEMU_NET_VDE_H */
diff --git a/ui/spice-core.c b/ui/spice-core.c
index ba0d0bd..5147365 100644
--- a/ui/spice-core.c
+++ b/ui/spice-core.c
@@ -223,7 +223,6 @@
client = qdict_new();
server = qdict_new();
-#ifdef SPICE_CHANNEL_EVENT_FLAG_ADDR_EXT
if (info->flags & SPICE_CHANNEL_EVENT_FLAG_ADDR_EXT) {
add_addr_info(client, (struct sockaddr *)&info->paddr_ext,
info->plen_ext);
@@ -232,12 +231,7 @@
} else {
error_report("spice: %s, extended address is expected",
__func__);
-#endif
- add_addr_info(client, &info->paddr, info->plen);
- add_addr_info(server, &info->laddr, info->llen);
-#ifdef SPICE_CHANNEL_EVENT_FLAG_ADDR_EXT
}
-#endif
if (event == SPICE_CHANNEL_EVENT_INITIALIZED) {
qdict_put(server, "auth", qstring_from_str(auth));
@@ -276,7 +270,6 @@
.channel_event = channel_event,
};
-#ifdef SPICE_INTERFACE_MIGRATION
typedef struct SpiceMigration {
SpiceMigrateInstance sin;
struct {
@@ -313,7 +306,6 @@
monitor_protocol_event(QEVENT_SPICE_MIGRATE_COMPLETED, NULL);
spice_migration_completed = true;
}
-#endif
/* config string parsing */
@@ -393,17 +385,13 @@
chan = g_malloc0(sizeof(*chan));
chan->value = g_malloc0(sizeof(*chan->value));
-#ifdef SPICE_CHANNEL_EVENT_FLAG_ADDR_EXT
if (item->info->flags & SPICE_CHANNEL_EVENT_FLAG_ADDR_EXT) {
paddr = (struct sockaddr *)&item->info->paddr_ext;
plen = item->info->plen_ext;
} else {
-#endif
paddr = &item->info->paddr;
plen = item->info->plen;
-#ifdef SPICE_CHANNEL_EVENT_FLAG_ADDR_EXT
}
-#endif
getnameinfo(paddr, plen,
host, sizeof(host), port, sizeof(port),
@@ -473,13 +461,10 @@
info->tls_port = tls_port;
}
-#if SPICE_SERVER_VERSION >= 0x000a03 /* 0.10.3 */
info->mouse_mode = spice_server_is_server_mouse(spice_server) ?
SPICE_QUERY_MOUSE_MODE_SERVER :
SPICE_QUERY_MOUSE_MODE_CLIENT;
-#else
- info->mouse_mode = SPICE_QUERY_MOUSE_MODE_UNKNOWN;
-#endif
+
/* for compatibility with the original command */
info->has_channels = true;
info->channels = qmp_query_spice_channels();
@@ -492,19 +477,11 @@
MigrationState *s = data;
if (migration_is_active(s)) {
-#ifdef SPICE_INTERFACE_MIGRATION
spice_server_migrate_start(spice_server);
-#endif
} else if (migration_has_finished(s)) {
-#ifndef SPICE_INTERFACE_MIGRATION
- spice_server_migrate_switch(spice_server);
- monitor_protocol_event(QEVENT_SPICE_MIGRATE_COMPLETED, NULL);
- spice_migration_completed = true;
-#else
spice_server_migrate_end(spice_server, true);
} else if (migration_has_failed(s)) {
spice_server_migrate_end(spice_server, false);
-#endif
}
}
@@ -513,16 +490,11 @@
MonitorCompletion *cb, void *opaque)
{
int ret;
-#ifdef SPICE_INTERFACE_MIGRATION
+
spice_migrate.connect_complete.cb = cb;
spice_migrate.connect_complete.opaque = opaque;
ret = spice_server_migrate_connect(spice_server, hostname,
port, tls_port, subject);
-#else
- ret = spice_server_migrate_info(spice_server, hostname,
- port, tls_port, subject);
- cb(opaque, NULL);
-#endif
return ret;
}
@@ -561,7 +533,6 @@
static void vm_change_state_handler(void *opaque, int running,
RunState state)
{
-#if SPICE_SERVER_VERSION >= 0x000b02 /* 0.11.2 */
if (running) {
qemu_spice_display_start();
spice_server_vm_start(spice_server);
@@ -569,7 +540,6 @@
spice_server_vm_stop(spice_server);
qemu_spice_display_stop();
}
-#endif
}
void qemu_spice_init(void)
@@ -585,9 +555,7 @@
int port, tls_port, len, addr_flags;
spice_image_compression_t compression;
spice_wan_compression_t wan_compr;
-#if SPICE_SERVER_VERSION >= 0x000b02 /* 0.11.2 */
bool seamless_migration;
-#endif
qemu_thread_get_self(&me);
@@ -672,16 +640,11 @@
spice_server_set_ticket(spice_server, password, 0, 0, 0);
}
if (qemu_opt_get_bool(opts, "sasl", 0)) {
-#if SPICE_SERVER_VERSION >= 0x000900 /* 0.9.0 */
if (spice_server_set_sasl_appname(spice_server, "qemu") == -1 ||
spice_server_set_sasl(spice_server, 1) == -1) {
error_report("spice: failed to enable sasl");
exit(1);
}
-#else
- error_report("spice: sasl is not available (spice >= 0.9 required)");
- exit(1);
-#endif
}
if (qemu_opt_get_bool(opts, "disable-ticketing", 0)) {
auth = "none";
@@ -726,15 +689,11 @@
qemu_opt_foreach(opts, add_channel, &tls_port, 0);
-#if SPICE_SERVER_VERSION >= 0x000a02 /* 0.10.2 */
spice_server_set_name(spice_server, qemu_name);
spice_server_set_uuid(spice_server, qemu_uuid);
-#endif
-#if SPICE_SERVER_VERSION >= 0x000b02 /* 0.11.2 */
seamless_migration = qemu_opt_get_bool(opts, "seamless-migration", 0);
spice_server_set_seamless_migration(spice_server, seamless_migration);
-#endif
if (0 != spice_server_init(spice_server, &core_interface)) {
error_report("failed to initialize spice server");
exit(1);
@@ -743,11 +702,9 @@
migration_state.notify = migration_state_notifier;
add_migration_state_change_notifier(&migration_state);
-#ifdef SPICE_INTERFACE_MIGRATION
spice_migrate.sin.base.sif = &migrate_interface.base;
spice_migrate.connect_complete.cb = NULL;
qemu_spice_add_interface(&spice_migrate.sin.base);
-#endif
qemu_spice_input_init();
qemu_spice_audio_init();
@@ -815,15 +772,11 @@
int qemu_spice_display_add_client(int csock, int skipauth, int tls)
{
-#if SPICE_SERVER_VERSION >= 0x000a01
if (tls) {
return spice_server_add_ssl_client(spice_server, csock, skipauth);
} else {
return spice_server_add_client(spice_server, csock, skipauth);
}
-#else
- return -1;
-#endif
}
static void spice_register_config(void)
diff --git a/ui/spice-display.c b/ui/spice-display.c
index 50fbefb..b61764f 100644
--- a/ui/spice-display.c
+++ b/ui/spice-display.c
@@ -126,21 +126,6 @@
ssd->worker->wakeup(ssd->worker);
}
-#if SPICE_SERVER_VERSION < 0x000b02 /* before 0.11.2 */
-static void qemu_spice_start(SimpleSpiceDisplay *ssd)
-{
- trace_qemu_spice_start(ssd->qxl.id);
- ssd->worker->start(ssd->worker);
-}
-
-static void qemu_spice_stop(SimpleSpiceDisplay *ssd)
-{
- trace_qemu_spice_stop(ssd->qxl.id);
- ssd->worker->stop(ssd->worker);
-}
-
-#else
-
static int spice_display_is_running;
void qemu_spice_display_start(void)
@@ -153,15 +138,9 @@
spice_display_is_running = false;
}
-#endif
-
int qemu_spice_display_is_running(SimpleSpiceDisplay *ssd)
{
-#if SPICE_SERVER_VERSION < 0x000b02 /* before 0.11.2 */
- return ssd->running;
-#else
return spice_display_is_running;
-#endif
}
static void qemu_spice_create_one_update(SimpleSpiceDisplay *ssd,
@@ -364,22 +343,6 @@
qemu_spice_destroy_primary_surface(ssd, 0, QXL_SYNC);
}
-void qemu_spice_vm_change_state_handler(void *opaque, int running,
- RunState state)
-{
-#if SPICE_SERVER_VERSION < 0x000b02 /* before 0.11.2 */
- SimpleSpiceDisplay *ssd = opaque;
-
- if (running) {
- ssd->running = true;
- qemu_spice_start(ssd);
- } else {
- qemu_spice_stop(ssd);
- ssd->running = false;
- }
-#endif
-}
-
void qemu_spice_display_init_common(SimpleSpiceDisplay *ssd, DisplayState *ds)
{
ssd->ds = ds;
@@ -623,7 +586,6 @@
qemu_spice_add_interface(&sdpy.qxl.base);
assert(sdpy.worker);
- qemu_add_vm_change_state_handler(qemu_spice_vm_change_state_handler, &sdpy);
qemu_spice_create_host_memslot(&sdpy);
qemu_spice_create_host_primary(&sdpy);
}
diff --git a/ui/spice-display.h b/ui/spice-display.h
index dea41c1..d766927 100644
--- a/ui/spice-display.h
+++ b/ui/spice-display.h
@@ -83,9 +83,6 @@
QXLRect dirty;
int notify;
-#if SPICE_SERVER_VERSION < 0x000b02 /* before 0.11.2 */
- int running;
-#endif
/*
* All struct members below this comment can be accessed from
@@ -133,8 +130,6 @@
void qemu_spice_destroy_primary_surface(SimpleSpiceDisplay *ssd,
uint32_t id, qxl_async_io async);
void qemu_spice_wakeup(SimpleSpiceDisplay *ssd);
-#if SPICE_SERVER_VERSION >= 0x000b02 /* before 0.11.2 */
void qemu_spice_display_start(void);
void qemu_spice_display_stop(void);
-#endif
int qemu_spice_display_is_running(SimpleSpiceDisplay *ssd);