aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--QMP/qmp-events.txt17
-rw-r--r--block/iscsi.c104
-rwxr-xr-xconfigure18
-rw-r--r--hw/i386/pc.c2
-rw-r--r--hw/misc/vfio.c782
-rw-r--r--hw/net/virtio-net.c133
-rw-r--r--hw/pci-bridge/i82801b11.c2
-rw-r--r--hw/pci/pci.c5
-rw-r--r--hw/pci/pci_bridge.c2
-rw-r--r--include/hw/i386/pc.h4
-rw-r--r--include/hw/pci/pci_bridge.h1
-rw-r--r--include/hw/pci/pci_ids.h2
-rw-r--r--include/hw/virtio/virtio-net.h1
-rw-r--r--include/hw/virtio/virtio.h2
-rw-r--r--include/monitor/monitor.h1
-rw-r--r--include/net/net.h3
-rw-r--r--monitor.c12
-rw-r--r--net/net.c48
-rw-r--r--qapi-schema.json76
-rw-r--r--qemu-char.c41
-rw-r--r--qmp-commands.hx70
-rw-r--r--qtest.c7
-rw-r--r--scripts/qapi-commands.py17
-rw-r--r--tests/Makefile7
-rw-r--r--tests/boot-order-test.c209
-rw-r--r--tests/fdc-test.c2
-rw-r--r--tests/fw_cfg-test.c2
-rw-r--r--tests/hd-geo-test.c8
-rw-r--r--tests/ide-test.c2
-rw-r--r--tests/libqos/fw_cfg-pc.c40
-rw-r--r--tests/libqos/fw_cfg-pc.h20
-rw-r--r--tests/libqos/fw_cfg.c55
-rw-r--r--tests/libqos/fw_cfg.h9
-rw-r--r--tests/libqos/malloc-pc.c2
-rw-r--r--tests/libqtest.c4
-rw-r--r--tests/libqtest.h12
36 files changed, 1238 insertions, 484 deletions
diff --git a/QMP/qmp-events.txt b/QMP/qmp-events.txt
index 24e804e948..39b6016460 100644
--- a/QMP/qmp-events.txt
+++ b/QMP/qmp-events.txt
@@ -172,6 +172,23 @@ Data:
},
"timestamp": { "seconds": 1265044230, "microseconds": 450486 } }
+NIC_RX_FILTER_CHANGED
+-----------------
+
+The event is emitted once until the query command is executed,
+the first event will always be emitted.
+
+Data:
+
+- "name": net client name (json-string)
+- "path": device path (json-string)
+
+{ "event": "NIC_RX_FILTER_CHANGED",
+ "data": { "name": "vnet0",
+ "path": "/machine/peripheral/vnet0/virtio-backend" },
+ "timestamp": { "seconds": 1368697518, "microseconds": 326866 } }
+}
+
RESET
-----
diff --git a/block/iscsi.c b/block/iscsi.c
index 0bbf0b18b4..5f28c6a2ea 100644
--- a/block/iscsi.c
+++ b/block/iscsi.c
@@ -32,6 +32,7 @@
#include "block/block_int.h"
#include "trace.h"
#include "block/scsi.h"
+#include "qemu/iov.h"
#include <iscsi/iscsi.h>
#include <iscsi/scsi-lowlevel.h>
@@ -61,8 +62,6 @@ typedef struct IscsiAIOCB {
int status;
int canceled;
int retries;
- size_t read_size;
- size_t read_offset;
int64_t sector_num;
int nb_sectors;
#ifdef __linux__
@@ -233,11 +232,28 @@ iscsi_aio_write16_cb(struct iscsi_context *iscsi, int status,
iscsi_schedule_bh(acb);
}
+static int64_t sector_lun2qemu(int64_t sector, IscsiLun *iscsilun)
+{
+ return sector * iscsilun->block_size / BDRV_SECTOR_SIZE;
+}
+
static int64_t sector_qemu2lun(int64_t sector, IscsiLun *iscsilun)
{
return sector * BDRV_SECTOR_SIZE / iscsilun->block_size;
}
+static bool is_request_lun_aligned(int64_t sector_num, int nb_sectors,
+ IscsiLun *iscsilun)
+{
+ if ((sector_num * BDRV_SECTOR_SIZE) % iscsilun->block_size ||
+ (nb_sectors * BDRV_SECTOR_SIZE) % iscsilun->block_size) {
+ error_report("iSCSI misaligned request: iscsilun->block_size %u, sector_num %ld, nb_sectors %d",
+ iscsilun->block_size, sector_num, nb_sectors);
+ return 0;
+ }
+ return 1;
+}
+
static int
iscsi_aio_writev_acb(IscsiAIOCB *acb)
{
@@ -285,7 +301,7 @@ iscsi_aio_writev_acb(IscsiAIOCB *acb)
lba = sector_qemu2lun(acb->sector_num, acb->iscsilun);
*(uint32_t *)&acb->task->cdb[2] = htonl(lba >> 32);
*(uint32_t *)&acb->task->cdb[6] = htonl(lba & 0xffffffff);
- num_sectors = size / acb->iscsilun->block_size;
+ num_sectors = sector_qemu2lun(acb->nb_sectors, acb->iscsilun);
*(uint32_t *)&acb->task->cdb[10] = htonl(num_sectors);
acb->task->expxferlen = size;
@@ -322,6 +338,10 @@ iscsi_aio_writev(BlockDriverState *bs, int64_t sector_num,
IscsiLun *iscsilun = bs->opaque;
IscsiAIOCB *acb;
+ if (!is_request_lun_aligned(sector_num, nb_sectors, iscsilun)) {
+ return NULL;
+ }
+
acb = qemu_aio_get(&iscsi_aiocb_info, bs, cb, opaque);
trace_iscsi_aio_writev(iscsilun->iscsi, sector_num, nb_sectors, opaque, acb);
@@ -379,6 +399,7 @@ static int
iscsi_aio_readv_acb(IscsiAIOCB *acb)
{
struct iscsi_context *iscsi = acb->iscsilun->iscsi;
+ size_t size;
uint64_t lba;
uint32_t num_sectors;
int ret;
@@ -391,20 +412,7 @@ iscsi_aio_readv_acb(IscsiAIOCB *acb)
acb->status = -EINPROGRESS;
acb->buf = NULL;
- /* If LUN blocksize is bigger than BDRV_BLOCK_SIZE a read from QEMU
- * may be misaligned to the LUN, so we may need to read some extra
- * data.
- */
- acb->read_offset = 0;
- if (acb->iscsilun->block_size > BDRV_SECTOR_SIZE) {
- uint64_t bdrv_offset = BDRV_SECTOR_SIZE * acb->sector_num;
-
- acb->read_offset = bdrv_offset % acb->iscsilun->block_size;
- }
-
- num_sectors = (acb->read_size + acb->iscsilun->block_size
- + acb->read_offset - 1)
- / acb->iscsilun->block_size;
+ size = acb->nb_sectors * BDRV_SECTOR_SIZE;
acb->task = malloc(sizeof(struct scsi_task));
if (acb->task == NULL) {
@@ -415,8 +423,9 @@ iscsi_aio_readv_acb(IscsiAIOCB *acb)
memset(acb->task, 0, sizeof(struct scsi_task));
acb->task->xfer_dir = SCSI_XFER_READ;
+ acb->task->expxferlen = size;
lba = sector_qemu2lun(acb->sector_num, acb->iscsilun);
- acb->task->expxferlen = acb->read_size;
+ num_sectors = sector_qemu2lun(acb->nb_sectors, acb->iscsilun);
switch (acb->iscsilun->type) {
case TYPE_DISK:
@@ -464,6 +473,10 @@ iscsi_aio_readv(BlockDriverState *bs, int64_t sector_num,
IscsiLun *iscsilun = bs->opaque;
IscsiAIOCB *acb;
+ if (!is_request_lun_aligned(sector_num, nb_sectors, iscsilun)) {
+ return NULL;
+ }
+
acb = qemu_aio_get(&iscsi_aiocb_info, bs, cb, opaque);
trace_iscsi_aio_readv(iscsilun->iscsi, sector_num, nb_sectors, opaque, acb);
@@ -471,7 +484,6 @@ iscsi_aio_readv(BlockDriverState *bs, int64_t sector_num,
acb->sector_num = sector_num;
acb->iscsilun = iscsilun;
acb->qiov = qiov;
- acb->read_size = BDRV_SECTOR_SIZE * (size_t)acb->nb_sectors;
acb->retries = ISCSI_CMD_RETRIES;
if (iscsi_aio_readv_acb(acb) != 0) {
@@ -651,6 +663,9 @@ iscsi_aio_ioctl_cb(struct iscsi_context *iscsi, int status,
{
IscsiAIOCB *acb = opaque;
+ g_free(acb->buf);
+ acb->buf = NULL;
+
if (acb->canceled != 0) {
return;
}
@@ -727,14 +742,30 @@ static BlockDriverAIOCB *iscsi_aio_ioctl(BlockDriverState *bs,
memcpy(&acb->task->cdb[0], acb->ioh->cmdp, acb->ioh->cmd_len);
acb->task->expxferlen = acb->ioh->dxfer_len;
+ data.size = 0;
if (acb->task->xfer_dir == SCSI_XFER_WRITE) {
- data.data = acb->ioh->dxferp;
- data.size = acb->ioh->dxfer_len;
+ if (acb->ioh->iovec_count == 0) {
+ data.data = acb->ioh->dxferp;
+ data.size = acb->ioh->dxfer_len;
+ } else {
+#if defined(LIBISCSI_FEATURE_IOVECTOR)
+ scsi_task_set_iov_out(acb->task,
+ (struct scsi_iovec *) acb->ioh->dxferp,
+ acb->ioh->iovec_count);
+#else
+ struct iovec *iov = (struct iovec *)acb->ioh->dxferp;
+
+ acb->buf = g_malloc(acb->ioh->dxfer_len);
+ data.data = acb->buf;
+ data.size = iov_to_buf(iov, acb->ioh->iovec_count, 0,
+ acb->buf, acb->ioh->dxfer_len);
+#endif
+ }
}
+
if (iscsi_scsi_command_async(iscsi, iscsilun->lun, acb->task,
iscsi_aio_ioctl_cb,
- (acb->task->xfer_dir == SCSI_XFER_WRITE) ?
- &data : NULL,
+ (data.size > 0) ? &data : NULL,
acb) != 0) {
scsi_free_scsi_task(acb->task);
qemu_aio_release(acb);
@@ -743,9 +774,26 @@ static BlockDriverAIOCB *iscsi_aio_ioctl(BlockDriverState *bs,
/* tell libiscsi to read straight into the buffer we got from ioctl */
if (acb->task->xfer_dir == SCSI_XFER_READ) {
- scsi_task_add_data_in_buffer(acb->task,
- acb->ioh->dxfer_len,
- acb->ioh->dxferp);
+ if (acb->ioh->iovec_count == 0) {
+ scsi_task_add_data_in_buffer(acb->task,
+ acb->ioh->dxfer_len,
+ acb->ioh->dxferp);
+ } else {
+#if defined(LIBISCSI_FEATURE_IOVECTOR)
+ scsi_task_set_iov_in(acb->task,
+ (struct scsi_iovec *) acb->ioh->dxferp,
+ acb->ioh->iovec_count);
+#else
+ int i;
+ for (i = 0; i < acb->ioh->iovec_count; i++) {
+ struct iovec *iov = (struct iovec *)acb->ioh->dxferp;
+
+ scsi_task_add_data_in_buffer(acb->task,
+ iov[i].iov_len,
+ iov[i].iov_base);
+ }
+#endif
+ }
}
iscsi_set_events(iscsilun);
@@ -1118,8 +1166,7 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags)
if ((ret = iscsi_readcapacity_sync(iscsilun)) != 0) {
goto out;
}
- bs->total_sectors = iscsilun->num_blocks *
- iscsilun->block_size / BDRV_SECTOR_SIZE ;
+ bs->total_sectors = sector_lun2qemu(iscsilun->num_blocks, iscsilun);
/* Medium changer or tape. We dont have any emulation for this so this must
* be sg ioctl compatible. We force it to be sg, otherwise qemu will try
@@ -1235,6 +1282,7 @@ static int iscsi_create(const char *filename, QEMUOptionParameter *options)
}
if (bs.total_sectors < total_size) {
ret = -ENOSPC;
+ goto out;
}
ret = 0;
diff --git a/configure b/configure
index cb0f870b95..9e1cd1975a 100755
--- a/configure
+++ b/configure
@@ -1697,19 +1697,23 @@ if test "$gtk" != "no"; then
vtepackage="vte"
vteversion="0.24.0"
fi
- if $pkg_config --exists "$gtkpackage >= $gtkversion" && \
- $pkg_config --exists "$vtepackage >= $vteversion"; then
+ if ! $pkg_config --exists "$gtkpackage >= $gtkversion"; then
+ if test "$gtk" = "yes" ; then
+ feature_not_found "gtk"
+ fi
+ gtk="no"
+ elif ! $pkg_config --exists "$vtepackage >= $vteversion"; then
+ if test "$gtk" = "yes" ; then
+ error_exit "libvte not found (required for gtk support)"
+ fi
+ gtk="no"
+ else
gtk_cflags=`$pkg_config --cflags $gtkpackage 2>/dev/null`
gtk_libs=`$pkg_config --libs $gtkpackage 2>/dev/null`
vte_cflags=`$pkg_config --cflags $vtepackage 2>/dev/null`
vte_libs=`$pkg_config --libs $vtepackage 2>/dev/null`
libs_softmmu="$gtk_libs $vte_libs $libs_softmmu"
gtk="yes"
- else
- if test "$gtk" = "yes" ; then
- feature_not_found "gtk"
- fi
- gtk="no"
fi
fi
diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index c5d8570af1..2a8756321b 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -1005,7 +1005,7 @@ typedef struct PcRomPciInfo {
static void pc_fw_cfg_guest_info(PcGuestInfo *guest_info)
{
PcRomPciInfo *info;
- if (!guest_info->has_pci_info) {
+ if (!guest_info->has_pci_info || !guest_info->fw_cfg) {
return;
}
diff --git a/hw/misc/vfio.c b/hw/misc/vfio.c
index 540c377f61..ad8ce770dc 100644
--- a/hw/misc/vfio.c
+++ b/hw/misc/vfio.c
@@ -59,8 +59,23 @@ typedef struct VFIOQuirk {
MemoryRegion mem;
struct VFIODevice *vdev;
QLIST_ENTRY(VFIOQuirk) next;
- uint32_t data;
- uint32_t data2;
+ struct {
+ uint32_t base_offset:TARGET_PAGE_BITS;
+ uint32_t address_offset:TARGET_PAGE_BITS;
+ uint32_t address_size:3;
+ uint32_t bar:3;
+
+ uint32_t address_match;
+ uint32_t address_mask;
+
+ uint32_t address_val:TARGET_PAGE_BITS;
+ uint32_t data_offset:TARGET_PAGE_BITS;
+ uint32_t data_size:3;
+
+ uint8_t flags;
+ uint8_t read_flags;
+ uint8_t write_flags;
+ } data;
} VFIOQuirk;
typedef struct VFIOBAR {
@@ -72,6 +87,8 @@ typedef struct VFIOBAR {
size_t size;
uint32_t flags; /* VFIO region flags (rd/wr/mmap) */
uint8_t nr; /* cache the BAR number for debug */
+ bool ioport;
+ bool mem64;
QLIST_HEAD(, VFIOQuirk) quirks;
} VFIOBAR;
@@ -158,6 +175,7 @@ typedef struct VFIODevice {
PCIHostDeviceAddress host;
QLIST_ENTRY(VFIODevice) next;
struct VFIOGroup *group;
+ EventNotifier err_notifier;
uint32_t features;
#define VFIO_FEATURE_ENABLE_VGA_BIT 0
#define VFIO_FEATURE_ENABLE_VGA (1 << VFIO_FEATURE_ENABLE_VGA_BIT)
@@ -165,6 +183,7 @@ typedef struct VFIODevice {
uint8_t pm_cap;
bool reset_works;
bool has_vga;
+ bool pci_aer;
} VFIODevice;
typedef struct VFIOGroup {
@@ -1097,251 +1116,315 @@ static const MemoryRegionOps vfio_vga_ops = {
* Device specific quirks
*/
-#define PCI_VENDOR_ID_ATI 0x1002
+/* Is range1 fully contained within range2? */
+static bool vfio_range_contained(uint64_t first1, uint64_t len1,
+ uint64_t first2, uint64_t len2) {
+ return (first1 >= first2 && first1 + len1 <= first2 + len2);
+}
-/*
- * Device 1002:68f9 (Advanced Micro Devices [AMD] nee ATI Cedar PRO [Radeon
- * HD 5450/6350]) reports the upper byte of the physical address of the
- * I/O port BAR4 through VGA register 0x3c3. The BAR is 256 bytes, so the
- * lower byte is known to be zero. Probing for this quirk reads 0xff from
- * port 0x3c3 on some devices so we store the physical address and replace
- * reads with the virtual address any time it matches. XXX Research when
- * to enable quirk.
- */
-static uint64_t vfio_ati_3c3_quirk_read(void *opaque,
- hwaddr addr, unsigned size)
+static bool vfio_flags_enabled(uint8_t flags, uint8_t mask)
+{
+ return (mask && (flags & mask) == mask);
+}
+
+static uint64_t vfio_generic_window_quirk_read(void *opaque,
+ hwaddr addr, unsigned size)
{
VFIOQuirk *quirk = opaque;
VFIODevice *vdev = quirk->vdev;
- PCIDevice *pdev = &vdev->pdev;
- uint64_t data = vfio_vga_read(&vdev->vga.region[QEMU_PCI_VGA_IO_HI],
- addr + 0x3, size);
+ uint64_t data;
+
+ if (vfio_flags_enabled(quirk->data.flags, quirk->data.read_flags) &&
+ ranges_overlap(addr, size,
+ quirk->data.data_offset, quirk->data.data_size)) {
+ hwaddr offset = addr - quirk->data.data_offset;
- if (data == quirk->data) {
- data = pci_get_byte(pdev->config + PCI_BASE_ADDRESS_4 + 1);
- DPRINTF("%s(0x3c3, 1) = 0x%"PRIx64"\n", __func__, data);
+ if (!vfio_range_contained(addr, size, quirk->data.data_offset,
+ quirk->data.data_size)) {
+ hw_error("%s: window data read not fully contained: %s\n",
+ __func__, memory_region_name(&quirk->mem));
+ }
+
+ data = vfio_pci_read_config(&vdev->pdev,
+ quirk->data.address_val + offset, size);
+
+ DPRINTF("%s read(%04x:%02x:%02x.%x:BAR%d+0x%"HWADDR_PRIx", %d) = 0x%"
+ PRIx64"\n", memory_region_name(&quirk->mem), vdev->host.domain,
+ vdev->host.bus, vdev->host.slot, vdev->host.function,
+ quirk->data.bar, addr, size, data);
+ } else {
+ data = vfio_bar_read(&vdev->bars[quirk->data.bar],
+ addr + quirk->data.base_offset, size);
}
return data;
}
-static const MemoryRegionOps vfio_ati_3c3_quirk = {
- .read = vfio_ati_3c3_quirk_read,
- .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
-static void vfio_vga_probe_ati_3c3_quirk(VFIODevice *vdev)
+static void vfio_generic_window_quirk_write(void *opaque, hwaddr addr,
+ uint64_t data, unsigned size)
{
- PCIDevice *pdev = &vdev->pdev;
- off_t physoffset = vdev->config_offset + PCI_BASE_ADDRESS_4;
- uint32_t physbar;
- VFIOQuirk *quirk;
+ VFIOQuirk *quirk = opaque;
+ VFIODevice *vdev = quirk->vdev;
- if (pci_get_word(pdev->config + PCI_VENDOR_ID) != PCI_VENDOR_ID_ATI ||
- vdev->bars[4].size < 256) {
- return;
- }
+ if (ranges_overlap(addr, size,
+ quirk->data.address_offset, quirk->data.address_size)) {
- /* Get I/O port BAR physical address */
- if (pread(vdev->fd, &physbar, 4, physoffset) != 4) {
- error_report("vfio: probe failed for ATI/AMD 0x3c3 quirk on device "
- "%04x:%02x:%02x.%x", vdev->host.domain,
- vdev->host.bus, vdev->host.slot, vdev->host.function);
- return;
+ if (addr != quirk->data.address_offset) {
+ hw_error("%s: offset write into address window: %s\n",
+ __func__, memory_region_name(&quirk->mem));
+ }
+
+ if ((data & ~quirk->data.address_mask) == quirk->data.address_match) {
+ quirk->data.flags |= quirk->data.write_flags |
+ quirk->data.read_flags;
+ quirk->data.address_val = data & quirk->data.address_mask;
+ } else {
+ quirk->data.flags &= ~(quirk->data.write_flags |
+ quirk->data.read_flags);
+ }
}
- quirk = g_malloc0(sizeof(*quirk));
- quirk->vdev = vdev;
- quirk->data = (physbar >> 8) & 0xff;
+ if (vfio_flags_enabled(quirk->data.flags, quirk->data.write_flags) &&
+ ranges_overlap(addr, size,
+ quirk->data.data_offset, quirk->data.data_size)) {
+ hwaddr offset = addr - quirk->data.data_offset;
- memory_region_init_io(&quirk->mem, OBJECT(vdev), &vfio_ati_3c3_quirk, quirk,
- "vfio-ati-3c3-quirk", 1);
- memory_region_add_subregion(&vdev->vga.region[QEMU_PCI_VGA_IO_HI].mem, 3,
- &quirk->mem);
+ if (!vfio_range_contained(addr, size, quirk->data.data_offset,
+ quirk->data.data_size)) {
+ hw_error("%s: window data write not fully contained: %s\n",
+ __func__, memory_region_name(&quirk->mem));
+ }
- QLIST_INSERT_HEAD(&vdev->vga.region[QEMU_PCI_VGA_IO_HI].quirks,
- quirk, next);
+ vfio_pci_write_config(&vdev->pdev,
+ quirk->data.address_val + offset, data, size);
+ DPRINTF("%s write(%04x:%02x:%02x.%x:BAR%d+0x%"HWADDR_PRIx", 0x%"
+ PRIx64", %d)\n", memory_region_name(&quirk->mem),
+ vdev->host.domain, vdev->host.bus, vdev->host.slot,
+ vdev->host.function, quirk->data.bar, addr, data, size);
+ return;
+ }
- DPRINTF("Enabled ATI/AMD quirk 0x3c3 for device %04x:%02x:%02x.%x\n",
- vdev->host.domain, vdev->host.bus, vdev->host.slot,
- vdev->host.function);
+ vfio_bar_write(&vdev->bars[quirk->data.bar],
+ addr + quirk->data.base_offset, data, size);
}
-/*
- * Device 1002:68f9 (Advanced Micro Devices [AMD] nee ATI Cedar PRO [Radeon
- * HD 5450/6350]) reports the physical address of MMIO BAR0 through a
- * write/read operation on I/O port BAR4. When uint32_t 0x4010 is written
- * to offset 0x0, the subsequent read from offset 0x4 returns the contents
- * of BAR0. Test for this quirk on all ATI/AMD devices. XXX - Note that
- * 0x10 is the offset of BAR0 in config sapce, is this a window to all of
- * config space?
- */
-static uint64_t vfio_ati_4010_quirk_read(void *opaque,
- hwaddr addr, unsigned size)
+static const MemoryRegionOps vfio_generic_window_quirk = {
+ .read = vfio_generic_window_quirk_read,
+ .write = vfio_generic_window_quirk_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static uint64_t vfio_generic_quirk_read(void *opaque,
+ hwaddr addr, unsigned size)
{
VFIOQuirk *quirk = opaque;
VFIODevice *vdev = quirk->vdev;
- PCIDevice *pdev = &vdev->pdev;
- uint64_t data = vfio_bar_read(&vdev->bars[4], addr, size);
+ hwaddr base = quirk->data.address_match & TARGET_PAGE_MASK;
+ hwaddr offset = quirk->data.address_match & ~TARGET_PAGE_MASK;
+ uint64_t data;
- if (addr == 4 && size == 4 && quirk->data) {
- data = pci_get_long(pdev->config + PCI_BASE_ADDRESS_0);
- DPRINTF("%s(BAR4+0x4) = 0x%"PRIx64"\n", __func__, data);
- }
+ if (vfio_flags_enabled(quirk->data.flags, quirk->data.read_flags) &&
+ ranges_overlap(addr, size, offset, quirk->data.address_mask + 1)) {
+ if (!vfio_range_contained(addr, size, offset,
+ quirk->data.address_mask + 1)) {
+ hw_error("%s: read not fully contained: %s\n",
+ __func__, memory_region_name(&quirk->mem));
+ }
- quirk->data = 0;
+ data = vfio_pci_read_config(&vdev->pdev, addr - offset, size);
+
+ DPRINTF("%s read(%04x:%02x:%02x.%x:BAR%d+0x%"HWADDR_PRIx", %d) = 0x%"
+ PRIx64"\n", memory_region_name(&quirk->mem), vdev->host.domain,
+ vdev->host.bus, vdev->host.slot, vdev->host.function,
+ quirk->data.bar, addr + base, size, data);
+ } else {
+ data = vfio_bar_read(&vdev->bars[quirk->data.bar], addr + base, size);
+ }
return data;
}
-static void vfio_ati_4010_quirk_write(void *opaque, hwaddr addr,
- uint64_t data, unsigned size)
+static void vfio_generic_quirk_write(void *opaque, hwaddr addr,
+ uint64_t data, unsigned size)
{
VFIOQuirk *quirk = opaque;
VFIODevice *vdev = quirk->vdev;
+ hwaddr base = quirk->data.address_match & TARGET_PAGE_MASK;
+ hwaddr offset = quirk->data.address_match & ~TARGET_PAGE_MASK;
+
+ if (vfio_flags_enabled(quirk->data.flags, quirk->data.write_flags) &&
+ ranges_overlap(addr, size, offset, quirk->data.address_mask + 1)) {
+ if (!vfio_range_contained(addr, size, offset,
+ quirk->data.address_mask + 1)) {
+ hw_error("%s: write not fully contained: %s\n",
+ __func__, memory_region_name(&quirk->mem));
+ }
+
+ vfio_pci_write_config(&vdev->pdev, addr - offset, data, size);
+
+ DPRINTF("%s write(%04x:%02x:%02x.%x:BAR%d+0x%"HWADDR_PRIx", 0x%"
+ PRIx64", %d)\n", memory_region_name(&quirk->mem),
+ vdev->host.domain, vdev->host.bus, vdev->host.slot,
+ vdev->host.function, quirk->data.bar, addr + base, data, size);
+ } else {
+ vfio_bar_write(&vdev->bars[quirk->data.bar], addr + base, data, size);
+ }
+}
- vfio_bar_write(&vdev->bars[4], addr, data, size);
+static const MemoryRegionOps vfio_generic_quirk = {
+ .read = vfio_generic_quirk_read,
+ .write = vfio_generic_quirk_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+#define PCI_VENDOR_ID_ATI 0x1002
+
+/*
+ * Radeon HD cards (HD5450 & HD7850) report the upper byte of the I/O port BAR
+ * through VGA register 0x3c3. On newer cards, the I/O port BAR is always
+ * BAR4 (older cards like the X550 used BAR1, but we don't care to support
+ * those). Note that on bare metal, a read of 0x3c3 doesn't always return the
+ * I/O port BAR address. Originally this was coded to return the virtual BAR
+ * address only if the physical register read returns the actual BAR address,
+ * but users have reported greater success if we return the virtual address
+ * unconditionally.
+ */
+static uint64_t vfio_ati_3c3_quirk_read(void *opaque,
+ hwaddr addr, unsigned size)
+{
+ VFIOQuirk *quirk = opaque;
+ VFIODevice *vdev = quirk->vdev;
+ uint64_t data = vfio_pci_read_config(&vdev->pdev,
+ PCI_BASE_ADDRESS_0 + (4 * 4) + 1,
+ size);
+ DPRINTF("%s(0x3c3, 1) = 0x%"PRIx64"\n", __func__, data);
- quirk->data = (addr == 0 && size == 4 && data == 0x4010) ? 1 : 0;
+ return data;
}
-static const MemoryRegionOps vfio_ati_4010_quirk = {
- .read = vfio_ati_4010_quirk_read,
- .write = vfio_ati_4010_quirk_write,
+static const MemoryRegionOps vfio_ati_3c3_quirk = {
+ .read = vfio_ati_3c3_quirk_read,
.endianness = DEVICE_LITTLE_ENDIAN,
};
-static void vfio_probe_ati_4010_quirk(VFIODevice *vdev, int nr)
+static void vfio_vga_probe_ati_3c3_quirk(VFIODevice *vdev)
{
PCIDevice *pdev = &vdev->pdev;
- off_t physoffset = vdev->config_offset + PCI_BASE_ADDRESS_0;
- uint32_t physbar0;
- uint64_t data;
VFIOQuirk *quirk;
- if (!vdev->has_vga || nr != 4 || !vdev->bars[0].size ||
- pci_get_word(pdev->config + PCI_VENDOR_ID) != PCI_VENDOR_ID_ATI) {
+ if (pci_get_word(pdev->config + PCI_VENDOR_ID) != PCI_VENDOR_ID_ATI) {
return;
}
- /* Get I/O port BAR physical address */
- if (pread(vdev->fd, &physbar0, 4, physoffset) != 4) {
- error_report("vfio: probe failed for ATI/AMD 0x4010 quirk on device "
- "%04x:%02x:%02x.%x", vdev->host.domain,
- vdev->host.bus, vdev->host.slot, vdev->host.function);
- return;
- }
-
- /* Write 0x4010 to I/O port BAR offset 0 */
- vfio_bar_write(&vdev->bars[4], 0, 0x4010, 4);
- /* Read back result */
- data = vfio_bar_read(&vdev->bars[4], 4, 4);
-
- /* If the register matches the physical address of BAR0, we need a quirk */
- if (data != physbar0) {
+ /*
+ * As long as the BAR is >= 256 bytes it will be aligned such that the
+ * lower byte is always zero. Filter out anything else, if it exists.
+ */
+ if (!vdev->bars[4].ioport || vdev->bars[4].size < 256) {
return;
}
quirk = g_malloc0(sizeof(*quirk));
quirk->vdev = vdev;
- memory_region_init_io(&quirk->mem, OBJECT(vdev), &vfio_ati_4010_quirk, quirk,
- "vfio-ati-4010-quirk", 8);
- memory_region_add_subregion_overlap(&vdev->bars[nr].mem, 0, &quirk->mem, 1);
+ memory_region_init_io(&quirk->mem, OBJECT(vdev), &vfio_ati_3c3_quirk, quirk,
+ "vfio-ati-3c3-quirk", 1);
+ memory_region_add_subregion(&vdev->vga.region[QEMU_PCI_VGA_IO_HI].mem,
+ 3 /* offset 3 bytes from 0x3c0 */, &quirk->mem);
- QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
+ QLIST_INSERT_HEAD(&vdev->vga.region[QEMU_PCI_VGA_IO_HI].quirks,
+ quirk, next);
- DPRINTF("Enabled ATI/AMD quirk 0x4010 for device %04x:%02x:%02x.%x\n",
+ DPRINTF("Enabled ATI/AMD quirk 0x3c3 BAR4for device %04x:%02x:%02x.%x\n",
vdev->host.domain, vdev->host.bus, vdev->host.slot,
vdev->host.function);
}
/*
- * Device 1002:5b63 (Advanced Micro Devices [AMD] nee ATI RV370 [Radeon X550])
- * retrieves the upper half of the MMIO BAR0 physical address by writing
- * 0xf10 to I/O port BAR1 offset 0 and reading the result from offset 6.
- * XXX - 0x10 is the offset of BAR0 in PCI config space, this could provide
- * full access to config space. Config space is little endian, so the data
- * register probably starts at 0x4.
+ * Newer ATI/AMD devices, including HD5450 and HD7850, have a window to PCI
+ * config space through MMIO BAR2 at offset 0x4000. Nothing seems to access
+ * the MMIO space directly, but a window to this space is provided through
+ * I/O port BAR4. Offset 0x0 is the address register and offset 0x4 is the
+ * data register. When the address is programmed to a range of 0x4000-0x4fff
+ * PCI configuration space is available. Experimentation seems to indicate
+ * that only read-only access is provided, but we drop writes when the window
+ * is enabled to config space nonetheless.
*/
-static uint64_t vfio_ati_f10_quirk_read(void *opaque,
- hwaddr addr, unsigned size)
+static void vfio_probe_ati_bar4_window_quirk(VFIODevice *vdev, int nr)
{
- VFIOQuirk *quirk = opaque;
- VFIODevice *vdev = quirk->vdev;
PCIDevice *pdev = &vdev->pdev;
- uint64_t data = vfio_bar_read(&vdev->bars[1], addr, size);
+ VFIOQuirk *quirk;
- if (addr == 6 && size == 2 && quirk->data) {
- data = pci_get_word(pdev->config + PCI_BASE_ADDRESS_0 + 2);
- DPRINTF("%s(BAR1+0x6) = 0x%"PRIx64"\n", __func__, data);
+ if (!vdev->has_vga || nr != 4 ||
+ pci_get_word(pdev->config + PCI_VENDOR_ID) != PCI_VENDOR_ID_ATI) {
+ return;
}
- quirk->data = 0;
-
- return data;
-}
-
-static void vfio_ati_f10_quirk_write(void *opaque, hwaddr addr,
- uint64_t data, unsigned size)
-{
- VFIOQuirk *quirk = opaque;
- VFIODevice *vdev = quirk->vdev;
+ quirk = g_malloc0(sizeof(*quirk));
+ quirk->vdev = vdev;
+ quirk->data.address_size = 4;
+ quirk->data.data_offset = 4;
+ quirk->data.data_size = 4;
+ quirk->data.address_match = 0x4000;
+ quirk->data.address_mask = PCIE_CONFIG_SPACE_SIZE - 1;
+ quirk->data.bar = nr;
+ quirk->data.read_flags = quirk->data.write_flags = 1;
+
+ memory_region_init_io(&quirk->mem, OBJECT(vdev),
+ &vfio_generic_window_quirk, quirk,
+ "vfio-ati-bar4-window-quirk", 8);
+ memory_region_add_subregion_overlap(&vdev->bars[nr].mem,
+ quirk->data.base_offset, &quirk->mem, 1);
- vfio_bar_write(&vdev->bars[1], addr, data, size);
+ QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
- quirk->data = (addr == 0 && size == 4 && data == 0xf10) ? 1 : 0;
+ DPRINTF("Enabled ATI/AMD BAR4 window quirk for device %04x:%02x:%02x.%x\n",
+ vdev->host.domain, vdev->host.bus, vdev->host.slot,
+ vdev->host.function);
}
-static const MemoryRegionOps vfio_ati_f10_quirk = {
- .read = vfio_ati_f10_quirk_read,
- .write = vfio_ati_f10_quirk_write,
- .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
-static void vfio_probe_ati_f10_quirk(VFIODevice *vdev, int nr)
+/*
+ * Trap the BAR2 MMIO window to config space as well.
+ */
+static void vfio_probe_ati_bar2_4000_quirk(VFIODevice *vdev, int nr)
{
PCIDevice *pdev = &vdev->pdev;
- off_t physoffset = vdev->config_offset + PCI_BASE_ADDRESS_0;
- uint32_t physbar0;
- uint64_t data;
VFIOQuirk *quirk;
- if (!vdev->has_vga || nr != 1 || !vdev->bars[0].size ||
+ /* Only enable on newer devices where BAR2 is 64bit */
+ if (!vdev->has_vga || nr != 2 || !vdev->bars[2].mem64 ||
pci_get_word(pdev->config + PCI_VENDOR_ID) != PCI_VENDOR_ID_ATI) {
return;
}
- /* Get I/O port BAR physical address */
- if (pread(vdev->fd, &physbar0, 4, physoffset) != 4) {
- error_report("vfio: probe failed for ATI/AMD 0xf10 quirk on device "
- "%04x:%02x:%02x.%x", vdev->host.domain,
- vdev->host.bus, vdev->host.slot, vdev->host.function);
- return;
- }
-
- vfio_bar_write(&vdev->bars[1], 0, 0xf10, 4);
- data = vfio_bar_read(&vdev->bars[1], 0x6, 2);
-
- /* If the register matches the physical address of BAR0, we need a quirk */
- if (data != (le32_to_cpu(physbar0) >> 16)) {
- return;
- }
-
quirk = g_malloc0(sizeof(*quirk));
quirk->vdev = vdev;
-
- memory_region_init_io(&quirk->mem, OBJECT(vdev), &vfio_ati_f10_quirk, quirk,
- "vfio-ati-f10-quirk", 8);
- memory_region_add_subregion_overlap(&vdev->bars[nr].mem, 0, &quirk->mem, 1);
+ quirk->data.flags = quirk->data.read_flags = quirk->data.write_flags = 1;
+ quirk->data.address_match = 0x4000;
+ quirk->data.address_mask = PCIE_CONFIG_SPACE_SIZE - 1;
+ quirk->data.bar = nr;
+
+ memory_region_init_io(&quirk->mem, OBJECT(vdev), &vfio_generic_quirk, quirk,
+ "vfio-ati-bar2-4000-quirk",
+ TARGET_PAGE_ALIGN(quirk->data.address_mask + 1));
+ memory_region_add_subregion_overlap(&vdev->bars[nr].mem,
+ quirk->data.address_match & TARGET_PAGE_MASK,
+ &quirk->mem, 1);
QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
- DPRINTF("Enabled ATI/AMD quirk 0xf10 for device %04x:%02x:%02x.%x\n",
+ DPRINTF("Enabled ATI/AMD BAR2 0x4000 quirk for device %04x:%02x:%02x.%x\n",
vdev->host.domain, vdev->host.bus, vdev->host.slot,
vdev->host.function);
}
+/*
+ * Older ATI/AMD cards like the X550 have a similar window to that above.
+ * I/O port BAR1 provides a window to a mirror of PCI config space located
+ * in BAR2 at offset 0xf00. We don't care to support such older cards, but
+ * note it for future reference.
+ */
+
#define PCI_VENDOR_ID_NVIDIA 0x10de
/*
@@ -1360,7 +1443,7 @@ static void vfio_probe_ati_f10_quirk(VFIODevice *vdev, int nr)
* that use the I/O port BAR5 window but it doesn't hurt to leave it.
*/
enum {
- NV_3D0_NONE,
+ NV_3D0_NONE = 0,
NV_3D0_SELECT,
NV_3D0_WINDOW,
NV_3D0_READ,
@@ -1374,14 +1457,14 @@ static uint64_t vfio_nvidia_3d0_quirk_read(void *opaque,
VFIODevice *vdev = quirk->vdev;
PCIDevice *pdev = &vdev->pdev;
uint64_t data = vfio_vga_read(&vdev->vga.region[QEMU_PCI_VGA_IO_HI],
- addr + 0x10, size);
+ addr + quirk->data.base_offset, size);
- if (quirk->data == NV_3D0_READ && addr == 0) {
- data = vfio_pci_read_config(pdev, quirk->data2, size);
+ if (quirk->data.flags == NV_3D0_READ && addr == quirk->data.data_offset) {
+ data = vfio_pci_read_config(pdev, quirk->data.address_val, size);
DPRINTF("%s(0x3d0, %d) = 0x%"PRIx64"\n", __func__, size, data);
}
- quirk->data = NV_3D0_NONE;
+ quirk->data.flags = NV_3D0_NONE;
return data;
}
@@ -1393,43 +1476,42 @@ static void vfio_nvidia_3d0_quirk_write(void *opaque, hwaddr addr,
VFIODevice *vdev = quirk->vdev;
PCIDevice *pdev = &vdev->pdev;
- switch (quirk->data) {
+ switch (quirk->data.flags) {
case NV_3D0_NONE:
- if (addr == 4 && data == 0x338) {
- quirk->data = NV_3D0_SELECT;
+ if (addr == quirk->data.address_offset && data == 0x338) {
+ quirk->data.flags = NV_3D0_SELECT;
}
break;
case NV_3D0_SELECT:
- quirk->data = NV_3D0_NONE;
- if (addr == 0 && (data & ~0xff) == 0x1800) {
- quirk->data = NV_3D0_WINDOW;
- quirk->data2 = data & 0xff;
+ quirk->data.flags = NV_3D0_NONE;
+ if (addr == quirk->data.data_offset &&
+ (data & ~quirk->data.address_mask) == quirk->data.address_match) {
+ quirk->data.flags = NV_3D0_WINDOW;
+ quirk->data.address_val = data & quirk->data.address_mask;
}
break;
case NV_3D0_WINDOW:
- quirk->data = NV_3D0_NONE;
- if (addr == 4) {
+ quirk->data.flags = NV_3D0_NONE;
+ if (addr == quirk->data.address_offset) {
if (data == 0x538) {
- quirk->data = NV_3D0_READ;
+ quirk->data.flags = NV_3D0_READ;
} else if (data == 0x738) {
- quirk->data = NV_3D0_WRITE;
+ quirk->data.flags = NV_3D0_WRITE;
}
}
break;
case NV_3D0_WRITE:
- quirk->data = NV_3D0_NONE;
- if (addr == 0) {
- vfio_pci_write_config(pdev, quirk->data2, data, size);
+ quirk->data.flags = NV_3D0_NONE;
+ if (addr == quirk->data.data_offset) {
+ vfio_pci_write_config(pdev, quirk->data.address_val, data, size);
DPRINTF("%s(0x3d0, 0x%"PRIx64", %d)\n", __func__, data, size);
return;
}
break;
- default:
- quirk->data = NV_3D0_NONE;
}
vfio_vga_write(&vdev->vga.region[QEMU_PCI_VGA_IO_HI],
- addr + 0x10, data, size);
+ addr + quirk->data.base_offset, data, size);
}
static const MemoryRegionOps vfio_nvidia_3d0_quirk = {
@@ -1450,11 +1532,18 @@ static void vfio_vga_probe_nvidia_3d0_quirk(VFIODevice *vdev)
quirk = g_malloc0(sizeof(*quirk));
quirk->vdev = vdev;
-
- memory_region_init_io(&quirk->mem, OBJECT(vdev), &vfio_nvidia_3d0_quirk, quirk,
- "vfio-nvidia-3d0-quirk", 6);
+ quirk->data.base_offset = 0x10;
+ quirk->data.address_offset = 4;
+ quirk->data.address_size = 2;
+ quirk->data.address_match = 0x1800;
+ quirk->data.address_mask = PCI_CONFIG_SPACE_SIZE - 1;
+ quirk->data.data_offset = 0;
+ quirk->data.data_size = 4;
+
+ memory_region_init_io(&quirk->mem, OBJECT(vdev), &vfio_nvidia_3d0_quirk,
+ quirk, "vfio-nvidia-3d0-quirk", 6);
memory_region_add_subregion(&vdev->vga.region[QEMU_PCI_VGA_IO_HI].mem,
- 0x10, &quirk->mem);
+ quirk->data.base_offset, &quirk->mem);
QLIST_INSERT_HEAD(&vdev->vga.region[QEMU_PCI_VGA_IO_HI].quirks,
quirk, next);
@@ -1478,76 +1567,46 @@ enum {
NV_BAR5_VALID = 0x7,
};
-static uint64_t vfio_nvidia_bar5_window_quirk_read(void *opaque,
- hwaddr addr, unsigned size)
-{
- VFIOQuirk *quirk = opaque;
- VFIODevice *vdev = quirk->vdev;
- uint64_t data = vfio_bar_read(&vdev->bars[5], addr, size);
-
- if (addr == 0xc && quirk->data == NV_BAR5_VALID) {
- data = vfio_pci_read_config(&vdev->pdev, quirk->data2, size);
- DPRINTF("%s(%04x:%02x:%02x.%x:BAR5+0x%"HWADDR_PRIx", %d) = 0x%"
- PRIx64"\n", __func__, vdev->host.domain, vdev->host.bus,
- vdev->host.slot, vdev->host.function, addr, size, data);
- }
-
- return data;
-}
-
static void vfio_nvidia_bar5_window_quirk_write(void *opaque, hwaddr addr,
uint64_t data, unsigned size)
{
VFIOQuirk *quirk = opaque;
- VFIODevice *vdev = quirk->vdev;
- /*
- * Use quirk->data to track enables and quirk->data2 for the offset
- */
switch (addr) {
case 0x0:
if (data & 0x1) {
- quirk->data |= NV_BAR5_MASTER;
+ quirk->data.flags |= NV_BAR5_MASTER;
} else {
- quirk->data &= ~NV_BAR5_MASTER;
+ quirk->data.flags &= ~NV_BAR5_MASTER;
}
break;
case 0x4:
if (data & 0x1) {
- quirk->data |= NV_BAR5_ENABLE;
+ quirk->data.flags |= NV_BAR5_ENABLE;
} else {
- quirk->data &= ~NV_BAR5_ENABLE;
+ quirk->data.flags &= ~NV_BAR5_ENABLE;
}
break;
case 0x8:
- if (quirk->data & NV_BAR5_MASTER) {
+ if (quirk->data.flags & NV_BAR5_MASTER) {
if ((data & ~0xfff) == 0x88000) {
- quirk->data |= NV_BAR5_ADDRESS;
- quirk->data2 = data & 0xfff;
+ quirk->data.flags |= NV_BAR5_ADDRESS;
+ quirk->data.address_val = data & 0xfff;
} else if ((data & ~0xff) == 0x1800) {
- quirk->data |= NV_BAR5_ADDRESS;
- quirk->data2 = data & 0xff;
+ quirk->data.flags |= NV_BAR5_ADDRESS;
+ quirk->data.address_val = data & 0xff;
} else {
- quirk->data &= ~NV_BAR5_ADDRESS;
+ quirk->data.flags &= ~NV_BAR5_ADDRESS;
}
}
break;
- case 0xc:
- if (quirk->data == NV_BAR5_VALID) {
- vfio_pci_write_config(&vdev->pdev, quirk->data2, data, size);
- DPRINTF("%s(%04x:%02x:%02x.%x:BAR5+0x%"HWADDR_PRIx", 0x%"
- PRIx64", %d)\n", __func__, vdev->host.domain,
- vdev->host.bus, vdev->host.slot, vdev->host.function,
- addr, data, size);
- return;
- }
}
- vfio_bar_write(&vdev->bars[5], addr, data, size);
+ vfio_generic_window_quirk_write(opaque, addr, data, size);
}
static const MemoryRegionOps vfio_nvidia_bar5_window_quirk = {
- .read = vfio_nvidia_bar5_window_quirk_read,
+ .read = vfio_generic_window_quirk_read,
.write = vfio_nvidia_bar5_window_quirk_write,
.valid.min_access_size = 4,
.endianness = DEVICE_LITTLE_ENDIAN,
@@ -1565,8 +1624,15 @@ static void vfio_probe_nvidia_bar5_window_quirk(VFIODevice *vdev, int nr)
quirk = g_malloc0(sizeof(*quirk));
quirk->vdev = vdev;
-
- memory_region_init_io(&quirk->mem, OBJECT(vdev), &vfio_nvidia_bar5_window_quirk, quirk,
+ quirk->data.read_flags = quirk->data.write_flags = NV_BAR5_VALID;
+ quirk->data.address_offset = 0x8;
+ quirk->data.address_size = 0; /* actually 4, but avoids generic code */
+ quirk->data.data_offset = 0xc;
+ quirk->data.data_size = 4;
+ quirk->data.bar = nr;
+
+ memory_region_init_io(&quirk->mem, OBJECT(vdev),
+ &vfio_nvidia_bar5_window_quirk, quirk,
"vfio-nvidia-bar5-window-quirk", 16);
memory_region_add_subregion_overlap(&vdev->bars[nr].mem, 0, &quirk->mem, 1);
@@ -1586,51 +1652,6 @@ static void vfio_probe_nvidia_bar5_window_quirk(VFIODevice *vdev, int nr)
*
* Here's offset 0x88000...
*/
-static uint64_t vfio_nvidia_bar0_88000_quirk_read(void *opaque,
- hwaddr addr, unsigned size)
-{
- VFIOQuirk *quirk = opaque;
- VFIODevice *vdev = quirk->vdev;
- hwaddr base = 0x88000 & TARGET_PAGE_MASK;
- hwaddr offset = 0x88000 & ~TARGET_PAGE_MASK;
- uint64_t data = vfio_bar_read(&vdev->bars[0], addr + base, size);
-
- if (ranges_overlap(addr, size, offset, PCI_CONFIG_SPACE_SIZE)) {
- data = vfio_pci_read_config(&vdev->pdev, addr - offset, size);
-
- DPRINTF("%s(%04x:%02x:%02x.%x:BAR0+0x%"HWADDR_PRIx", %d) = 0x%"
- PRIx64"\n", __func__, vdev->host.domain, vdev->host.bus,
- vdev->host.slot, vdev->host.function, addr + base, size, data);
- }
-
- return data;
-}
-
-static void vfio_nvidia_bar0_88000_quirk_write(void *opaque, hwaddr addr,
- uint64_t data, unsigned size)
-{
- VFIOQuirk *quirk = opaque;
- VFIODevice *vdev = quirk->vdev;
- hwaddr base = 0x88000 & TARGET_PAGE_MASK;
- hwaddr offset = 0x88000 & ~TARGET_PAGE_MASK;
-
- if (ranges_overlap(addr, size, offset, PCI_CONFIG_SPACE_SIZE)) {
- vfio_pci_write_config(&vdev->pdev, addr - offset, data, size);
-
- DPRINTF("%s(%04x:%02x:%02x.%x:BAR0+0x%"HWADDR_PRIx", 0x%"
- PRIx64", %d)\n", __func__, vdev->host.domain, vdev->host.bus,
- vdev->host.slot, vdev->host.function, addr + base, data, size);
- } else {
- vfio_bar_write(&vdev->bars[0], addr + base, data, size);
- }
-}
-
-static const MemoryRegionOps vfio_nvidia_bar0_88000_quirk = {
- .read = vfio_nvidia_bar0_88000_quirk_read,
- .write = vfio_nvidia_bar0_88000_quirk_write,
- .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
static void vfio_probe_nvidia_bar0_88000_quirk(VFIODevice *vdev, int nr)
{
PCIDevice *pdev = &vdev->pdev;
@@ -1643,13 +1664,17 @@ static void vfio_probe_nvidia_bar0_88000_quirk(VFIODevice *vdev, int nr)
quirk = g_malloc0(sizeof(*quirk));
quirk->vdev = vdev;
-
- memory_region_init_io(&quirk->mem, OBJECT(vdev), &vfio_nvidia_bar0_88000_quirk, quirk,
- "vfio-nvidia-bar0-88000-quirk",
- TARGET_PAGE_ALIGN(PCIE_CONFIG_SPACE_SIZE));
+ quirk->data.flags = quirk->data.read_flags = quirk->data.write_flags = 1;
+ quirk->data.address_match = 0x88000;
+ quirk->data.address_mask = PCIE_CONFIG_SPACE_SIZE - 1;
+ quirk->data.bar = nr;
+
+ memory_region_init_io(&quirk->mem, OBJECT(vdev), &vfio_generic_quirk,
+ quirk, "vfio-nvidia-bar0-88000-quirk",
+ TARGET_PAGE_ALIGN(quirk->data.address_mask + 1));
memory_region_add_subregion_overlap(&vdev->bars[nr].mem,
- 0x88000 & TARGET_PAGE_MASK,
- &quirk->mem, 1);
+ quirk->data.address_match & TARGET_PAGE_MASK,
+ &quirk->mem, 1);
QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
@@ -1661,51 +1686,6 @@ static void vfio_probe_nvidia_bar0_88000_quirk(VFIODevice *vdev, int nr)
/*
* And here's the same for BAR0 offset 0x1800...
*/
-static uint64_t vfio_nvidia_bar0_1800_quirk_read(void *opaque,
- hwaddr addr, unsigned size)
-{
- VFIOQuirk *quirk = opaque;
- VFIODevice *vdev = quirk->vdev;
- hwaddr base = 0x1800 & TARGET_PAGE_MASK;
- hwaddr offset = 0x1800 & ~TARGET_PAGE_MASK;
- uint64_t data = vfio_bar_read(&vdev->bars[0], addr + base, size);
-
- if (ranges_overlap(addr, size, offset, PCI_CONFIG_SPACE_SIZE)) {
- data = vfio_pci_read_config(&vdev->pdev, addr - offset, size);
-
- DPRINTF("%s(%04x:%02x:%02x.%x:BAR0+0x%"HWADDR_PRIx", %d) = 0x%"
- PRIx64"\n", __func__, vdev->host.domain, vdev->host.bus,
- vdev->host.slot, vdev->host.function, addr + base, size, data);
- }
-
- return data;
-}
-
-static void vfio_nvidia_bar0_1800_quirk_write(void *opaque, hwaddr addr,
- uint64_t data, unsigned size)
-{
- VFIOQuirk *quirk = opaque;
- VFIODevice *vdev = quirk->vdev;
- hwaddr base = 0x1800 & TARGET_PAGE_MASK;
- hwaddr offset = 0x1800 & ~TARGET_PAGE_MASK;
-
- if (ranges_overlap(addr, size, offset, PCI_CONFIG_SPACE_SIZE)) {
- vfio_pci_write_config(&vdev->pdev, addr - offset, data, size);
-
- DPRINTF("%s(%04x:%02x:%02x.%x:BAR0+0x%"HWADDR_PRIx", 0x%"
- PRIx64", %d)\n", __func__, vdev->host.domain, vdev->host.bus,
- vdev->host.slot, vdev->host.function, addr + base, data, size);
- } else {
- vfio_bar_write(&vdev->bars[0], addr + base, data, size);
- }
-}
-
-static const MemoryRegionOps vfio_nvidia_bar0_1800_quirk = {
- .read = vfio_nvidia_bar0_1800_quirk_read,
- .write = vfio_nvidia_bar0_1800_quirk_write,
- .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
static void vfio_probe_nvidia_bar0_1800_quirk(VFIODevice *vdev, int nr)
{
PCIDevice *pdev = &vdev->pdev;
@@ -1722,13 +1702,17 @@ static void vfio_probe_nvidia_bar0_1800_quirk(VFIODevice *vdev, int nr)
quirk = g_malloc0(sizeof(*quirk));
quirk->vdev = vdev;
+ quirk->data.flags = quirk->data.read_flags = quirk->data.write_flags = 1;
+ quirk->data.address_match = 0x1800;
+ quirk->data.address_mask = PCI_CONFIG_SPACE_SIZE - 1;
+ quirk->data.bar = nr;
- memory_region_init_io(&quirk->mem, OBJECT(vdev), &vfio_nvidia_bar0_1800_quirk, quirk,
+ memory_region_init_io(&quirk->mem, OBJECT(vdev), &vfio_generic_quirk, quirk,
"vfio-nvidia-bar0-1800-quirk",
- TARGET_PAGE_ALIGN(PCI_CONFIG_SPACE_SIZE));
+ TARGET_PAGE_ALIGN(quirk->data.address_mask + 1));
memory_region_add_subregion_overlap(&vdev->bars[nr].mem,
- 0x1800 & TARGET_PAGE_MASK,
- &quirk->mem, 1);
+ quirk->data.address_match & TARGET_PAGE_MASK,
+ &quirk->mem, 1);
QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
@@ -1768,8 +1752,8 @@ static void vfio_vga_quirk_teardown(VFIODevice *vdev)
static void vfio_bar_quirk_setup(VFIODevice *vdev, int nr)
{
- vfio_probe_ati_4010_quirk(vdev, nr);
- vfio_probe_ati_f10_quirk(vdev, nr);
+ vfio_probe_ati_bar4_window_quirk(vdev, nr);
+ vfio_probe_ati_bar2_4000_quirk(vdev, nr);
vfio_probe_nvidia_bar5_window_quirk(vdev, nr);
vfio_probe_nvidia_bar0_88000_quirk(vdev, nr);
vfio_probe_nvidia_bar0_1800_quirk(vdev, nr);
@@ -2270,11 +2254,14 @@ static void vfio_map_bar(VFIODevice *vdev, int nr)
}
pci_bar = le32_to_cpu(pci_bar);
- type = pci_bar & (pci_bar & PCI_BASE_ADDRESS_SPACE_IO ?
- ~PCI_BASE_ADDRESS_IO_MASK : ~PCI_BASE_ADDRESS_MEM_MASK);
+ bar->ioport = (pci_bar & PCI_BASE_ADDRESS_SPACE_IO);
+ bar->mem64 = bar->ioport ? 0 : (pci_bar & PCI_BASE_ADDRESS_MEM_TYPE_64);
+ type = pci_bar & (bar->ioport ? ~PCI_BASE_ADDRESS_IO_MASK :
+ ~PCI_BASE_ADDRESS_MEM_MASK);
/* A "slow" read/write mapping underlies all BARs */
- memory_region_init_io(&bar->mem, OBJECT(vdev), &vfio_bar_ops, bar, name, size);
+ memory_region_init_io(&bar->mem, OBJECT(vdev), &vfio_bar_ops,
+ bar, name, size);
pci_register_bar(&vdev->pdev, nr, type, &bar->mem);
/*
@@ -2781,6 +2768,7 @@ static int vfio_get_device(VFIOGroup *group, const char *name, VFIODevice *vdev)
{
struct vfio_device_info dev_info = { .argsz = sizeof(dev_info) };
struct vfio_region_info reg_info = { .argsz = sizeof(reg_info) };
+ struct vfio_irq_info irq_info = { .argsz = sizeof(irq_info) };
int ret, i;
ret = ioctl(group->fd, VFIO_GROUP_GET_DEVICE_FD, name);
@@ -2924,6 +2912,19 @@ static int vfio_get_device(VFIOGroup *group, const char *name, VFIODevice *vdev)
vdev->has_vga = true;
}
+ irq_info.index = VFIO_PCI_ERR_IRQ_INDEX;
+
+ ret = ioctl(vdev->fd, VFIO_DEVICE_GET_IRQ_INFO, &irq_info);
+ if (ret) {
+ /* This can fail for an old kernel or legacy PCI dev */
+ DPRINTF("VFIO_DEVICE_GET_IRQ_INFO failure ret=%d\n", ret);
+ ret = 0;
+ } else if (irq_info.count == 1) {
+ vdev->pci_aer = true;
+ } else {
+ error_report("vfio: Warning: "
+ "Could not enable error recovery for the device\n");
+ }
error:
if (ret) {
@@ -2946,6 +2947,113 @@ static void vfio_put_device(VFIODevice *vdev)
}
}
+static void vfio_err_notifier_handler(void *opaque)
+{
+ VFIODevice *vdev = opaque;
+
+ if (!event_notifier_test_and_clear(&vdev->err_notifier)) {
+ return;
+ }
+
+ /*
+ * TBD. Retrieve the error details and decide what action
+ * needs to be taken. One of the actions could be to pass
+ * the error to the guest and have the guest driver recover
+ * from the error. This requires that PCIe capabilities be
+ * exposed to the guest. For now, we just terminate the
+ * guest to contain the error.
+ */
+
+ error_report("%s (%04x:%02x:%02x.%x)"
+ "Unrecoverable error detected...\n"
+ "Please collect any data possible and then kill the guest",
+ __func__, vdev->host.domain, vdev->host.bus,
+ vdev->host.slot, vdev->host.function);
+
+ vm_stop(RUN_STATE_IO_ERROR);
+}
+
+/*
+ * Registers error notifier for devices supporting error recovery.
+ * If we encounter a failure in this function, we report an error
+ * and continue after disabling error recovery support for the
+ * device.
+ */
+static void vfio_register_err_notifier(VFIODevice *vdev)
+{
+ int ret;
+ int argsz;
+ struct vfio_irq_set *irq_set;
+ int32_t *pfd;
+
+ if (!vdev->pci_aer) {
+ return;
+ }
+
+ if (event_notifier_init(&vdev->err_notifier, 0)) {
+ error_report("vfio: Warning: "
+ "Unable to init event notifier for error detection\n");
+ vdev->pci_aer = false;
+ return;
+ }
+
+ 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_ERR_IRQ_INDEX;
+ irq_set->start = 0;
+ irq_set->count = 1;
+ pfd = (int32_t *)&irq_set->data;
+
+ *pfd = event_notifier_get_fd(&vdev->err_notifier);
+ qemu_set_fd_handler(*pfd, vfio_err_notifier_handler, NULL, vdev);
+
+ ret = ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, irq_set);
+ if (ret) {
+ error_report("vfio: Failed to set up error notification\n");
+ qemu_set_fd_handler(*pfd, NULL, NULL, vdev);
+ event_notifier_cleanup(&vdev->err_notifier);
+ vdev->pci_aer = false;
+ }
+ g_free(irq_set);
+}
+
+static void vfio_unregister_err_notifier(VFIODevice *vdev)
+{
+ int argsz;
+ struct vfio_irq_set *irq_set;
+ int32_t *pfd;
+ int ret;
+
+ if (!vdev->pci_aer) {
+ return;
+ }
+
+ 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_ERR_IRQ_INDEX;
+ irq_set->start = 0;
+ irq_set->count = 1;
+ pfd = (int32_t *)&irq_set->data;
+ *pfd = -1;
+
+ ret = ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, irq_set);
+ if (ret) {
+ error_report("vfio: Failed to de-assign error fd: %d\n", ret);
+ }
+ g_free(irq_set);
+ qemu_set_fd_handler(event_notifier_get_fd(&vdev->err_notifier),
+ NULL, NULL, vdev);
+ event_notifier_cleanup(&vdev->err_notifier);
+}
+
static int vfio_initfn(PCIDevice *pdev)
{
VFIODevice *pvdev, *vdev = DO_UPCAST(VFIODevice, pdev, pdev);
@@ -3078,6 +3186,7 @@ static int vfio_initfn(PCIDevice *pdev)
}
add_boot_device_path(vdev->bootindex, &pdev->qdev, NULL);
+ vfio_register_err_notifier(vdev);
return 0;
@@ -3097,6 +3206,7 @@ static void vfio_exitfn(PCIDevice *pdev)
VFIODevice *vdev = DO_UPCAST(VFIODevice, pdev, pdev);
VFIOGroup *group = vdev->group;
+ vfio_unregister_err_notifier(vdev);
pci_device_set_intx_routing_notifier(&vdev->pdev, NULL);
vfio_disable_interrupts(vdev);
if (vdev->intx.mmap_timer) {
diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
index 1ea95564a5..679f50c33a 100644
--- a/hw/net/virtio-net.c
+++ b/hw/net/virtio-net.c
@@ -21,6 +21,8 @@
#include "hw/virtio/virtio-net.h"
#include "net/vhost_net.h"
#include "hw/virtio/virtio-bus.h"
+#include "qapi/qmp/qjson.h"
+#include "monitor/monitor.h"
#define VIRTIO_NET_VM_VERSION 11
@@ -192,6 +194,105 @@ static void virtio_net_set_link_status(NetClientState *nc)
virtio_net_set_status(vdev, vdev->status);
}
+static void rxfilter_notify(NetClientState *nc)
+{
+ QObject *event_data;
+ VirtIONet *n = qemu_get_nic_opaque(nc);
+
+ if (nc->rxfilter_notify_enabled) {
+ if (n->netclient_name) {
+ event_data = qobject_from_jsonf("{ 'name': %s, 'path': %s }",
+ n->netclient_name,
+ object_get_canonical_path(OBJECT(n->qdev)));
+ } else {
+ event_data = qobject_from_jsonf("{ 'path': %s }",
+ object_get_canonical_path(OBJECT(n->qdev)));
+ }
+ monitor_protocol_event(QEVENT_NIC_RX_FILTER_CHANGED, event_data);
+ qobject_decref(event_data);
+
+ /* disable event notification to avoid events flooding */
+ nc->rxfilter_notify_enabled = 0;
+ }
+}
+
+static char *mac_strdup_printf(const uint8_t *mac)
+{
+ return g_strdup_printf("%.2x:%.2x:%.2x:%.2x:%.2x:%.2x", mac[0],
+ mac[1], mac[2], mac[3], mac[4], mac[5]);
+}
+
+static RxFilterInfo *virtio_net_query_rxfilter(NetClientState *nc)
+{
+ VirtIONet *n = qemu_get_nic_opaque(nc);
+ RxFilterInfo *info;
+ strList *str_list, *entry;
+ intList *int_list, *int_entry;
+ int i, j;
+
+ info = g_malloc0(sizeof(*info));
+ info->name = g_strdup(nc->name);
+ info->promiscuous = n->promisc;
+
+ if (n->nouni) {
+ info->unicast = RX_STATE_NONE;
+ } else if (n->alluni) {
+ info->unicast = RX_STATE_ALL;
+ } else {
+ info->unicast = RX_STATE_NORMAL;
+ }
+
+ if (n->nomulti) {
+ info->multicast = RX_STATE_NONE;
+ } else if (n->allmulti) {
+ info->multicast = RX_STATE_ALL;
+ } else {
+ info->multicast = RX_STATE_NORMAL;
+ }
+
+ info->broadcast_allowed = n->nobcast;
+ info->multicast_overflow = n->mac_table.multi_overflow;
+ info->unicast_overflow = n->mac_table.uni_overflow;
+
+ info->main_mac = mac_strdup_printf(n->mac);
+
+ str_list = NULL;
+ for (i = 0; i < n->mac_table.first_multi; i++) {
+ entry = g_malloc0(sizeof(*entry));
+ entry->value = mac_strdup_printf(n->mac_table.macs + i * ETH_ALEN);
+ entry->next = str_list;
+ str_list = entry;
+ }
+ info->unicast_table = str_list;
+
+ str_list = NULL;
+ for (i = n->mac_table.first_multi; i < n->mac_table.in_use; i++) {
+ entry = g_malloc0(sizeof(*entry));
+ entry->value = mac_strdup_printf(n->mac_table.macs + i * ETH_ALEN);
+ entry->next = str_list;
+ str_list = entry;
+ }
+ info->multicast_table = str_list;
+
+ int_list = NULL;
+ for (i = 0; i < MAX_VLAN >> 5; i++) {
+ for (j = 0; n->vlans[i] && j < 0x1f; j++) {
+ if (n->vlans[i] & (1U << j)) {
+ int_entry = g_malloc0(sizeof(*int_entry));
+ int_entry->value = (i << 5) + j;
+ int_entry->next = int_list;
+ int_list = int_entry;
+ }
+ }
+ }
+ info->vlan_table = int_list;
+
+ /* enable event notification after query */
+ nc->rxfilter_notify_enabled = 1;
+
+ return info;
+}
+
static void virtio_net_reset(VirtIODevice *vdev)
{
VirtIONet *n = VIRTIO_NET(vdev);
@@ -420,6 +521,7 @@ static int virtio_net_handle_rx_mode(VirtIONet *n, uint8_t cmd,
{
uint8_t on;
size_t s;
+ NetClientState *nc = qemu_get_queue(n->nic);
s = iov_to_buf(iov, iov_cnt, 0, &on, sizeof(on));
if (s != sizeof(on)) {
@@ -442,6 +544,8 @@ static int virtio_net_handle_rx_mode(VirtIONet *n, uint8_t cmd,
return VIRTIO_NET_ERR;
}
+ rxfilter_notify(nc);
+
return VIRTIO_NET_OK;
}
@@ -487,6 +591,7 @@ static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd,
{
struct virtio_net_ctrl_mac mac_data;
size_t s;
+ NetClientState *nc = qemu_get_queue(n->nic);
if (cmd == VIRTIO_NET_CTRL_MAC_ADDR_SET) {
if (iov_size(iov, iov_cnt) != sizeof(n->mac)) {
@@ -495,6 +600,8 @@ static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd,
s = iov_to_buf(iov, iov_cnt, 0, &n->mac, sizeof(n->mac));
assert(s == sizeof(n->mac));
qemu_format_nic_info_str(qemu_get_queue(n->nic), n->mac);
+ rxfilter_notify(nc);
+
return VIRTIO_NET_OK;
}
@@ -512,19 +619,19 @@ static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd,
sizeof(mac_data.entries));
mac_data.entries = ldl_p(&mac_data.entries);
if (s != sizeof(mac_data.entries)) {
- return VIRTIO_NET_ERR;
+ goto error;
}
iov_discard_front(&iov, &iov_cnt, s);
if (mac_data.entries * ETH_ALEN > iov_size(iov, iov_cnt)) {
- return VIRTIO_NET_ERR;
+ goto error;
}
if (mac_data.entries <= MAC_TABLE_ENTRIES) {
s = iov_to_buf(iov, iov_cnt, 0, n->mac_table.macs,
mac_data.entries * ETH_ALEN);
if (s != mac_data.entries * ETH_ALEN) {
- return VIRTIO_NET_ERR;
+ goto error;
}
n->mac_table.in_use += mac_data.entries;
} else {
@@ -539,27 +646,33 @@ static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd,
sizeof(mac_data.entries));
mac_data.entries = ldl_p(&mac_data.entries);
if (s != sizeof(mac_data.entries)) {
- return VIRTIO_NET_ERR;
+ goto error;
}
iov_discard_front(&iov, &iov_cnt, s);
if (mac_data.entries * ETH_ALEN != iov_size(iov, iov_cnt)) {
- return VIRTIO_NET_ERR;
+ goto error;
}
if (n->mac_table.in_use + mac_data.entries <= MAC_TABLE_ENTRIES) {
s = iov_to_buf(iov, iov_cnt, 0, n->mac_table.macs,
mac_data.entries * ETH_ALEN);
if (s != mac_data.entries * ETH_ALEN) {
- return VIRTIO_NET_ERR;
+ goto error;
}
n->mac_table.in_use += mac_data.entries;
} else {
n->mac_table.multi_overflow = 1;
}
+ rxfilter_notify(nc);
+
return VIRTIO_NET_OK;
+
+error:
+ rxfilter_notify(nc);
+ return VIRTIO_NET_ERR;
}
static int virtio_net_handle_vlan_table(VirtIONet *n, uint8_t cmd,
@@ -567,6 +680,7 @@ static int virtio_net_handle_vlan_table(VirtIONet *n, uint8_t cmd,
{
uint16_t vid;
size_t s;
+ NetClientState *nc = qemu_get_queue(n->nic);
s = iov_to_buf(iov, iov_cnt, 0, &vid, sizeof(vid));
vid = lduw_p(&vid);
@@ -584,6 +698,8 @@ static int virtio_net_handle_vlan_table(VirtIONet *n, uint8_t cmd,
else
return VIRTIO_NET_ERR;
+ rxfilter_notify(nc);
+
return VIRTIO_NET_OK;
}
@@ -1312,6 +1428,7 @@ static NetClientInfo net_virtio_info = {
.receive = virtio_net_receive,
.cleanup = virtio_net_cleanup,
.link_status_changed = virtio_net_set_link_status,
+ .query_rx_filter = virtio_net_query_rxfilter,
};
static bool virtio_net_guest_notifier_pending(VirtIODevice *vdev, int idx)
@@ -1373,6 +1490,7 @@ static int virtio_net_device_init(VirtIODevice *vdev)
DeviceState *qdev = DEVICE(vdev);
VirtIONet *n = VIRTIO_NET(vdev);
+ NetClientState *nc;
virtio_init(VIRTIO_DEVICE(n), "virtio-net", VIRTIO_ID_NET,
n->config_size);
@@ -1439,6 +1557,9 @@ static int virtio_net_device_init(VirtIODevice *vdev)
n->vlans = g_malloc0(MAX_VLAN >> 3);
+ nc = qemu_get_queue(n->nic);
+ nc->rxfilter_notify_enabled = 1;
+
n->qdev = qdev;
register_savevm(qdev, "virtio-net", -1, VIRTIO_NET_VM_VERSION,
virtio_net_save, virtio_net_load, n);
diff --git a/hw/pci-bridge/i82801b11.c b/hw/pci-bridge/i82801b11.c
index 5807a92d7f..b98bfb0664 100644
--- a/hw/pci-bridge/i82801b11.c
+++ b/hw/pci-bridge/i82801b11.c
@@ -69,7 +69,7 @@ static int i82801b11_bridge_initfn(PCIDevice *d)
if (rc < 0) {
goto err_bridge;
}
- pci_config_set_prog_interface(d->config, PCI_CLASS_BRDIGE_PCI_INF_SUB);
+ pci_config_set_prog_interface(d->config, PCI_CLASS_BRIDGE_PCI_INF_SUB);
return 0;
err_bridge:
diff --git a/hw/pci/pci.c b/hw/pci/pci.c
index dcc85ef0af..81cf5a958c 100644
--- a/hw/pci/pci.c
+++ b/hw/pci/pci.c
@@ -378,6 +378,7 @@ int pci_bus_num(PCIBus *s)
static int get_pci_config_device(QEMUFile *f, void *pv, size_t size)
{
PCIDevice *s = container_of(pv, PCIDevice, config);
+ PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(s);
uint8_t *config;
int i;
@@ -395,6 +396,10 @@ static int get_pci_config_device(QEMUFile *f, void *pv, size_t size)
memcpy(s->config, config, size);
pci_update_mappings(s);
+ if (pc->is_bridge) {
+ PCIBridge *b = container_of(s, PCIBridge, dev);
+ pci_bridge_update_mappings(b);
+ }
memory_region_set_enabled(&s->bus_master_enable_region,
pci_get_word(s->config + PCI_COMMAND)
diff --git a/hw/pci/pci_bridge.c b/hw/pci/pci_bridge.c
index ecdeab0d58..02a396b01c 100644
--- a/hw/pci/pci_bridge.c
+++ b/hw/pci/pci_bridge.c
@@ -224,7 +224,7 @@ static void pci_bridge_region_cleanup(PCIBridge *br, PCIBridgeWindows *w)
g_free(w);
}
-static void pci_bridge_update_mappings(PCIBridge *br)
+void pci_bridge_update_mappings(PCIBridge *br)
{
PCIBridgeWindows *w = br->windows;
diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h
index 61ff154c7d..7fb97b08a2 100644
--- a/include/hw/i386/pc.h
+++ b/include/hw/i386/pc.h
@@ -231,6 +231,10 @@ int e820_add_entry(uint64_t, uint64_t, uint32_t);
.driver = "Nehalem-" TYPE_X86_CPU,\
.property = "level",\
.value = stringify(2),\
+ },{\
+ .driver = "virtio-net-pci",\
+ .property = "any_layout",\
+ .value = "off",\
}
#define PC_COMPAT_1_4 \
diff --git a/include/hw/pci/pci_bridge.h b/include/hw/pci/pci_bridge.h
index 1868f7aea8..1d8f9973c7 100644
--- a/include/hw/pci/pci_bridge.h
+++ b/include/hw/pci/pci_bridge.h
@@ -37,6 +37,7 @@ PCIBus *pci_bridge_get_sec_bus(PCIBridge *br);
pcibus_t pci_bridge_get_base(const PCIDevice *bridge, uint8_t type);
pcibus_t pci_bridge_get_limit(const PCIDevice *bridge, uint8_t type);
+void pci_bridge_update_mappings(PCIBridge *br);
void pci_bridge_write_config(PCIDevice *d,
uint32_t address, uint32_t val, int len);
void pci_bridge_disable_base_limit(PCIDevice *dev);
diff --git a/include/hw/pci/pci_ids.h b/include/hw/pci/pci_ids.h
index 08f8161524..d7933bfd16 100644
--- a/include/hw/pci/pci_ids.h
+++ b/include/hw/pci/pci_ids.h
@@ -39,7 +39,7 @@
#define PCI_CLASS_BRIDGE_HOST 0x0600
#define PCI_CLASS_BRIDGE_ISA 0x0601
#define PCI_CLASS_BRIDGE_PCI 0x0604
-#define PCI_CLASS_BRDIGE_PCI_INF_SUB 0x01
+#define PCI_CLASS_BRIDGE_PCI_INF_SUB 0x01
#define PCI_CLASS_BRIDGE_OTHER 0x0680
#define PCI_CLASS_COMMUNICATION_SERIAL 0x0700
diff --git a/include/hw/virtio/virtio-net.h b/include/hw/virtio/virtio-net.h
index b315ac91a4..df60f16a3e 100644
--- a/include/hw/virtio/virtio-net.h
+++ b/include/hw/virtio/virtio-net.h
@@ -243,6 +243,7 @@ struct virtio_net_ctrl_mq {
#define DEFINE_VIRTIO_NET_FEATURES(_state, _field) \
DEFINE_VIRTIO_COMMON_FEATURES(_state, _field), \
+ DEFINE_PROP_BIT("any_layout", _state, _field, VIRTIO_F_ANY_LAYOUT, true), \
DEFINE_PROP_BIT("csum", _state, _field, VIRTIO_NET_F_CSUM, true), \
DEFINE_PROP_BIT("guest_csum", _state, _field, VIRTIO_NET_F_GUEST_CSUM, true), \
DEFINE_PROP_BIT("gso", _state, _field, VIRTIO_NET_F_GSO, true), \
diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h
index a6c5c5380c..5d1d2be295 100644
--- a/include/hw/virtio/virtio.h
+++ b/include/hw/virtio/virtio.h
@@ -43,6 +43,8 @@
/* We notify when the ring is completely used, even if the guest is suppressing
* callbacks */
#define VIRTIO_F_NOTIFY_ON_EMPTY 24
+/* Can the device handle any descriptor layout? */
+#define VIRTIO_F_ANY_LAYOUT 27
/* We support indirect buffer descriptors */
#define VIRTIO_RING_F_INDIRECT_DESC 28
/* The Guest publishes the used index for which it expects an interrupt
diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h
index 1a6cfcf687..1942cc42fe 100644
--- a/include/monitor/monitor.h
+++ b/include/monitor/monitor.h
@@ -41,6 +41,7 @@ typedef enum MonitorEvent {
QEVENT_BLOCK_JOB_READY,
QEVENT_DEVICE_DELETED,
QEVENT_DEVICE_TRAY_MOVED,
+ QEVENT_NIC_RX_FILTER_CHANGED,
QEVENT_SUSPEND,
QEVENT_SUSPEND_DISK,
QEVENT_WAKEUP,
diff --git a/include/net/net.h b/include/net/net.h
index 43d85a16eb..30e4b04066 100644
--- a/include/net/net.h
+++ b/include/net/net.h
@@ -49,6 +49,7 @@ typedef ssize_t (NetReceiveIOV)(NetClientState *, const struct iovec *, int);
typedef void (NetCleanup) (NetClientState *);
typedef void (LinkStatusChanged)(NetClientState *);
typedef void (NetClientDestructor)(NetClientState *);
+typedef RxFilterInfo *(QueryRxFilter)(NetClientState *);
typedef struct NetClientInfo {
NetClientOptionsKind type;
@@ -59,6 +60,7 @@ typedef struct NetClientInfo {
NetCanReceive *can_receive;
NetCleanup *cleanup;
LinkStatusChanged *link_status_changed;
+ QueryRxFilter *query_rx_filter;
NetPoll *poll;
} NetClientInfo;
@@ -74,6 +76,7 @@ struct NetClientState {
unsigned receive_disabled : 1;
NetClientDestructor *destructor;
unsigned int queue_index;
+ unsigned rxfilter_notify_enabled:1;
};
typedef struct NICState {
diff --git a/monitor.c b/monitor.c
index 2ba7876b56..6db2ba4d7a 100644
--- a/monitor.c
+++ b/monitor.c
@@ -189,6 +189,7 @@ struct Monitor {
int suspend_cnt;
bool skip_flush;
QString *outbuf;
+ guint watch;
ReadLineState *rs;
MonitorControl *mc;
CPUState *mon_cpu;
@@ -263,7 +264,10 @@ int monitor_read_password(Monitor *mon, ReadLineFunc *readline_func,
static gboolean monitor_unblocked(GIOChannel *chan, GIOCondition cond,
void *opaque)
{
- monitor_flush(opaque);
+ Monitor *mon = opaque;
+
+ mon->watch = 0;
+ monitor_flush(mon);
return FALSE;
}
@@ -294,7 +298,10 @@ void monitor_flush(Monitor *mon)
QDECREF(mon->outbuf);
mon->outbuf = tmp;
}
- qemu_chr_fe_add_watch(mon->chr, G_IO_OUT, monitor_unblocked, mon);
+ if (mon->watch == 0) {
+ mon->watch = qemu_chr_fe_add_watch(mon->chr, G_IO_OUT,
+ monitor_unblocked, mon);
+ }
}
}
@@ -490,6 +497,7 @@ static const char *monitor_event_names[] = {
[QEVENT_BLOCK_JOB_READY] = "BLOCK_JOB_READY",
[QEVENT_DEVICE_DELETED] = "DEVICE_DELETED",
[QEVENT_DEVICE_TRAY_MOVED] = "DEVICE_TRAY_MOVED",
+ [QEVENT_NIC_RX_FILTER_CHANGED] = "NIC_RX_FILTER_CHANGED",
[QEVENT_SUSPEND] = "SUSPEND",
[QEVENT_SUSPEND_DISK] = "SUSPEND_DISK",
[QEVENT_WAKEUP] = "WAKEUP",
diff --git a/net/net.c b/net/net.c
index 43a74e43ae..c0d61bf78b 100644
--- a/net/net.c
+++ b/net/net.c
@@ -961,6 +961,54 @@ void print_net_client(Monitor *mon, NetClientState *nc)
nc->info_str);
}
+RxFilterInfoList *qmp_query_rx_filter(bool has_name, const char *name,
+ Error **errp)
+{
+ NetClientState *nc;
+ RxFilterInfoList *filter_list = NULL, *last_entry = NULL;
+
+ QTAILQ_FOREACH(nc, &net_clients, next) {
+ RxFilterInfoList *entry;
+ RxFilterInfo *info;
+
+ if (has_name && strcmp(nc->name, name) != 0) {
+ continue;
+ }
+
+ /* only query rx-filter information of NIC */
+ if (nc->info->type != NET_CLIENT_OPTIONS_KIND_NIC) {
+ if (has_name) {
+ error_setg(errp, "net client(%s) isn't a NIC", name);
+ break;
+ }
+ continue;
+ }
+
+ if (nc->info->query_rx_filter) {
+ info = nc->info->query_rx_filter(nc);
+ entry = g_malloc0(sizeof(*entry));
+ entry->value = info;
+
+ if (!filter_list) {
+ filter_list = entry;
+ } else {
+ last_entry->next = entry;
+ }
+ last_entry = entry;
+ } else if (has_name) {
+ error_setg(errp, "net client(%s) doesn't support"
+ " rx-filter querying", name);
+ break;
+ }
+ }
+
+ if (filter_list == NULL && !error_is_set(errp) && has_name) {
+ error_setg(errp, "invalid net client name: %s", name);
+ }
+
+ return filter_list;
+}
+
void do_info_network(Monitor *mon, const QDict *qdict)
{
NetClientState *nc, *peer;
diff --git a/qapi-schema.json b/qapi-schema.json
index 7b9fef1bd1..8d33d527d3 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -3679,3 +3679,79 @@
'*cpuid-input-ecx': 'int',
'cpuid-register': 'X86CPURegister32',
'features': 'int' } }
+
+##
+# @RxState:
+#
+# Packets receiving state
+#
+# @normal: filter assigned packets according to the mac-table
+#
+# @none: don't receive any assigned packet
+#
+# @all: receive all assigned packets
+#
+# Since: 1.6
+##
+{ 'enum': 'RxState', 'data': [ 'normal', 'none', 'all' ] }
+
+##
+# @RxFilterInfo:
+#
+# Rx-filter information for a NIC.
+#
+# @name: net client name
+#
+# @promiscuous: whether promiscuous mode is enabled
+#
+# @multicast: multicast receive state
+#
+# @unicast: unicast receive state
+#
+# @broadcast-allowed: whether to receive broadcast
+#
+# @multicast-overflow: multicast table is overflowed or not
+#
+# @unicast-overflow: unicast table is overflowed or not
+#
+# @main-mac: the main macaddr string
+#
+# @vlan-table: a list of active vlan id
+#
+# @unicast-table: a list of unicast macaddr string
+#
+# @multicast-table: a list of multicast macaddr string
+#
+# Since 1.6
+##
+
+{ 'type': 'RxFilterInfo',
+ 'data': {
+ 'name': 'str',
+ 'promiscuous': 'bool',
+ 'multicast': 'RxState',
+ 'unicast': 'RxState',
+ 'broadcast-allowed': 'bool',
+ 'multicast-overflow': 'bool',
+ 'unicast-overflow': 'bool',
+ 'main-mac': 'str',
+ 'vlan-table': ['int'],
+ 'unicast-table': ['str'],
+ 'multicast-table': ['str'] }}
+
+##
+# @query-rx-filter:
+#
+# Return rx-filter information for all NICs (or for the given NIC).
+#
+# @name: #optional net client name
+#
+# Returns: list of @RxFilterInfo for all NICs (or for the given NIC).
+# Returns an error if the given @name doesn't exist, or given
+# NIC doesn't support rx-filter querying, or given net client
+# isn't a NIC.
+#
+# Since: 1.6
+##
+{ 'command': 'query-rx-filter', 'data': { '*name': 'str' },
+ 'returns': ['RxFilterInfo'] }
diff --git a/qemu-char.c b/qemu-char.c
index 800d6a62f9..c86ce4ba2e 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -720,35 +720,32 @@ static GIOChannel *io_channel_from_socket(int fd)
static int io_channel_send(GIOChannel *fd, const void *buf, size_t len)
{
- GIOStatus status;
- size_t offset;
+ size_t offset = 0;
+ GIOStatus status = G_IO_STATUS_NORMAL;
- offset = 0;
- while (offset < len) {
- gsize bytes_written;
+ while (offset < len && status == G_IO_STATUS_NORMAL) {
+ gsize bytes_written = 0;
status = g_io_channel_write_chars(fd, buf + offset, len - offset,
&bytes_written, NULL);
- if (status != G_IO_STATUS_NORMAL) {
- if (status == G_IO_STATUS_AGAIN) {
- /* If we've written any data, return a partial write. */
- if (offset) {
- break;
- }
- errno = EAGAIN;
- } else {
- errno = EINVAL;
- }
-
- return -1;
- } else if (status == G_IO_STATUS_EOF) {
- break;
- }
-
offset += bytes_written;
}
- return offset;
+ if (offset > 0) {
+ return offset;
+ }
+ switch (status) {
+ case G_IO_STATUS_NORMAL:
+ g_assert(len == 0);
+ return 0;
+ case G_IO_STATUS_AGAIN:
+ errno = EAGAIN;
+ return -1;
+ default:
+ break;
+ }
+ errno = EINVAL;
+ return -1;
}
#ifndef _WIN32
diff --git a/qmp-commands.hx b/qmp-commands.hx
index e075df423a..65a9e26423 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -346,7 +346,8 @@ Send keys to VM.
Arguments:
keys array:
- - "key": key sequence (a json-array of key enum values)
+ - "key": key sequence (a json-array of key union values,
+ union can be number or qcode enum)
- hold-time: time to delay key up events, milliseconds. Defaults to 100
(json-int, optional)
@@ -354,7 +355,9 @@ keys array:
Example:
-> { "execute": "send-key",
- "arguments": { 'keys': [ 'ctrl', 'alt', 'delete' ] } }
+ "arguments": { "keys": [ { "type": "qcode", "data": "ctrl" },
+ { "type": "qcode", "data": "alt" },
+ { "type": "qcode", "data": "delete" } ] } }
<- { "return": {} }
EQMP
@@ -3047,3 +3050,66 @@ Example:
<- { "return": {} }
EQMP
+ {
+ .name = "query-rx-filter",
+ .args_type = "name:s?",
+ .mhandler.cmd_new = qmp_marshal_input_query_rx_filter,
+ },
+
+SQMP
+query-rx-filter
+---------------
+
+Show rx-filter information.
+
+Returns a json-array of rx-filter information for all NICs (or for the
+given NIC), returning an error if the given NIC doesn't exist, or
+given NIC doesn't support rx-filter querying, or given net client
+isn't a NIC.
+
+The query will clear the event notification flag of each NIC, then qemu
+will start to emit event to QMP monitor.
+
+Each array entry contains the following:
+
+- "name": net client name (json-string)
+- "promiscuous": promiscuous mode is enabled (json-bool)
+- "multicast": multicast receive state (one of 'normal', 'none', 'all')
+- "unicast": unicast receive state (one of 'normal', 'none', 'all')
+- "broadcast-allowed": allow to receive broadcast (json-bool)
+- "multicast-overflow": multicast table is overflowed (json-bool)
+- "unicast-overflow": unicast table is overflowed (json-bool)
+- "main-mac": main macaddr string (json-string)
+- "vlan-table": a json-array of active vlan id
+- "unicast-table": a json-array of unicast macaddr string
+- "multicast-table": a json-array of multicast macaddr string
+
+Example:
+
+-> { "execute": "query-rx-filter", "arguments": { "name": "vnet0" } }
+<- { "return": [
+ {
+ "promiscuous": true,
+ "name": "vnet0",
+ "main-mac": "52:54:00:12:34:56",
+ "unicast": "normal",
+ "vlan-table": [
+ 4,
+ 0
+ ],
+ "unicast-table": [
+ ],
+ "multicast": "normal",
+ "multicast-overflow": false,
+ "unicast-overflow": false,
+ "multicast-table": [
+ "01:00:5e:00:00:01",
+ "33:33:00:00:00:01",
+ "33:33:ff:12:34:56"
+ ],
+ "broadcast-allowed": false
+ }
+ ]
+ }
+
+EQMP
diff --git a/qtest.c b/qtest.c
index 07a96120dc..74f1842c1e 100644
--- a/qtest.c
+++ b/qtest.c
@@ -472,7 +472,12 @@ static void qtest_event(void *opaque, int event)
switch (event) {
case CHR_EVENT_OPENED:
- qemu_system_reset(false);
+ /*
+ * We used to call qemu_system_reset() here, hoping we could
+ * use the same process for multiple tests that way. Never
+ * used. Injects an extra reset even when it's not used, and
+ * that can mess up tests, e.g. -boot once.
+ */
for (i = 0; i < ARRAY_SIZE(irq_levels); i++) {
irq_levels[i] = 0;
}
diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
index e06332bd55..b12b6964ef 100644
--- a/scripts/qapi-commands.py
+++ b/scripts/qapi-commands.py
@@ -128,12 +128,15 @@ bool has_%(argname)s = false;
def gen_visitor_input_block(args, obj, dealloc=False):
ret = ""
+ errparg = 'errp'
+
if len(args) == 0:
return ret
push_indent()
if dealloc:
+ errparg = 'NULL'
ret += mcgen('''
md = qapi_dealloc_visitor_new();
v = qapi_dealloc_get_visitor(md);
@@ -148,22 +151,22 @@ v = qmp_input_get_visitor(mi);
for argname, argtype, optional, structured in parse_args(args):
if optional:
ret += mcgen('''
-visit_start_optional(v, &has_%(c_name)s, "%(name)s", errp);
+visit_start_optional(v, &has_%(c_name)s, "%(name)s", %(errp)s);
if (has_%(c_name)s) {
''',
- c_name=c_var(argname), name=argname)
+ c_name=c_var(argname), name=argname, errp=errparg)
push_indent()
ret += mcgen('''
-%(visitor)s(v, &%(c_name)s, "%(name)s", errp);
+%(visitor)s(v, &%(c_name)s, "%(name)s", %(errp)s);
''',
c_name=c_var(argname), name=argname, argtype=argtype,
- visitor=type_visitor(argtype))
+ visitor=type_visitor(argtype), errp=errparg)
if optional:
pop_indent()
ret += mcgen('''
}
-visit_end_optional(v, errp);
-''')
+visit_end_optional(v, %(errp)s);
+''', errp=errparg)
if dealloc:
ret += mcgen('''
@@ -194,7 +197,7 @@ static void qmp_marshal_output_%(c_name)s(%(c_ret_type)s ret_in, QObject **ret_o
}
qmp_output_visitor_cleanup(mo);
v = qapi_dealloc_get_visitor(md);
- %(visitor)s(v, &ret_in, "unused", errp);
+ %(visitor)s(v, &ret_in, "unused", NULL);
qapi_dealloc_visitor_cleanup(md);
}
''',
diff --git a/tests/Makefile b/tests/Makefile
index 279d5f8307..425a9a8c4b 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -57,6 +57,7 @@ gcov-files-i386-y = hw/fdc.c
check-qtest-i386-y += tests/ide-test$(EXESUF)
check-qtest-i386-y += tests/hd-geo-test$(EXESUF)
gcov-files-i386-y += hw/hd-geometry.c
+check-qtest-i386-y += tests/boot-order-test$(EXESUF)
check-qtest-i386-y += tests/rtc-test$(EXESUF)
check-qtest-i386-y += tests/i440fx-test$(EXESUF)
check-qtest-i386-y += tests/fw_cfg-test$(EXESUF)
@@ -69,6 +70,8 @@ gcov-files-sparc-y += hw/m48t59.c
gcov-files-sparc64-y += hw/m48t59.c
check-qtest-arm-y = tests/tmp105-test$(EXESUF)
gcov-files-arm-y += hw/tmp105.c
+check-qtest-ppc-y += tests/boot-order-test$(EXESUF)
+check-qtest-ppc64-y += tests/boot-order-test$(EXESUF)
GENERATED_HEADERS += tests/test-qapi-types.h tests/test-qapi-visit.h tests/test-qmp-commands.h
@@ -125,7 +128,7 @@ tests/test-mul64$(EXESUF): tests/test-mul64.o libqemuutil.a
libqos-obj-y = tests/libqos/pci.o tests/libqos/fw_cfg.o
libqos-obj-y += tests/libqos/i2c.o
-libqos-pc-obj-y = $(libqos-obj-y) tests/libqos/pci-pc.o tests/libqos/fw_cfg-pc.o
+libqos-pc-obj-y = $(libqos-obj-y) tests/libqos/pci-pc.o
libqos-pc-obj-y += tests/libqos/malloc-pc.o
libqos-omap-obj-y = $(libqos-obj-y) tests/libqos/i2c-omap.o
@@ -134,6 +137,7 @@ tests/m48t59-test$(EXESUF): tests/m48t59-test.o
tests/fdc-test$(EXESUF): tests/fdc-test.o
tests/ide-test$(EXESUF): tests/ide-test.o $(libqos-pc-obj-y)
tests/hd-geo-test$(EXESUF): tests/hd-geo-test.o
+tests/boot-order-test$(EXESUF): tests/boot-order-test.o $(libqos-obj-y)
tests/tmp105-test$(EXESUF): tests/tmp105-test.o $(libqos-omap-obj-y)
tests/i440fx-test$(EXESUF): tests/i440fx-test.o $(libqos-pc-obj-y)
tests/fw_cfg-test$(EXESUF): tests/fw_cfg-test.o $(libqos-pc-obj-y)
@@ -226,3 +230,4 @@ check-block: $(patsubst %,check-%, $(check-block-y))
check: check-unit check-qtest
-include $(wildcard tests/*.d)
+-include $(wildcard tests/libqos/*.d)
diff --git a/tests/boot-order-test.c b/tests/boot-order-test.c
new file mode 100644
index 0000000000..4b233d0b24
--- /dev/null
+++ b/tests/boot-order-test.c
@@ -0,0 +1,209 @@
+/*
+ * Boot order test cases.
+ *
+ * Copyright (c) 2013 Red Hat Inc.
+ *
+ * Authors:
+ * Markus Armbruster <armbru@redhat.com>,
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include <string.h>
+#include <glib.h>
+#include "libqos/fw_cfg.h"
+#include "libqtest.h"
+
+#define NO_QEMU_PROTOS
+#include "hw/nvram/fw_cfg.h"
+#undef NO_QEMU_PROTOS
+
+typedef struct {
+ const char *args;
+ uint64_t expected_boot;
+ uint64_t expected_reboot;
+} boot_order_test;
+
+static void test_a_boot_order(const char *machine,
+ const char *test_args,
+ uint64_t (*read_boot_order)(void),
+ uint64_t expected_boot,
+ uint64_t expected_reboot)
+{
+ char *args;
+ uint64_t actual;
+
+ args = g_strdup_printf("-nodefaults -display none%s%s %s",
+ machine ? " -M " : "",
+ machine ?: "",
+ test_args);
+ qtest_start(args);
+ actual = read_boot_order();
+ g_assert_cmphex(actual, ==, expected_boot);
+ qmp("{ 'execute': 'system_reset' }");
+ /*
+ * system_reset only requests reset. We get a RESET event after
+ * the actual reset completes. Need to wait for that.
+ */
+ qmp(""); /* HACK: wait for event */
+ actual = read_boot_order();
+ g_assert_cmphex(actual, ==, expected_reboot);
+ qtest_quit(global_qtest);
+ g_free(args);
+}
+
+static void test_boot_orders(const char *machine,
+ uint64_t (*read_boot_order)(void),
+ const boot_order_test *tests)
+{
+ int i;
+
+ for (i = 0; tests[i].args; i++) {
+ test_a_boot_order(machine, tests[i].args,
+ read_boot_order,
+ tests[i].expected_boot,
+ tests[i].expected_reboot);
+ }
+}
+
+static uint8_t read_mc146818(uint16_t port, uint8_t reg)
+{
+ outb(port, reg);
+ return inb(port + 1);
+}
+
+static uint64_t read_boot_order_pc(void)
+{
+ uint8_t b1 = read_mc146818(0x70, 0x38);
+ uint8_t b2 = read_mc146818(0x70, 0x3d);
+
+ return b1 | (b2 << 8);
+}
+
+static const boot_order_test test_cases_pc[] = {
+ { "",
+ 0x1230, 0x1230 },
+ { "-no-fd-bootchk",
+ 0x1231, 0x1231 },
+ { "-boot c",
+ 0x0200, 0x0200 },
+ { "-boot nda",
+ 0x3410, 0x3410 },
+ { "-boot order=",
+ 0, 0 },
+ { "-boot order= -boot order=c",
+ 0x0200, 0x0200 },
+ { "-boot once=a",
+ 0x0100, 0x1230 },
+ { "-boot once=a -no-fd-bootchk",
+ 0x0101, 0x1231 },
+ { "-boot once=a,order=c",
+ 0x0100, 0x0200 },
+ { "-boot once=d -boot order=nda",
+ 0x0300, 0x3410 },
+ { "-boot once=a -boot once=b -boot once=c",
+ 0x0200, 0x1230 },
+ {}
+};
+
+static void test_pc_boot_order(void)
+{
+ test_boot_orders(NULL, read_boot_order_pc, test_cases_pc);
+}
+
+static uint8_t read_m48t59(uint64_t addr, uint16_t reg)
+{
+ writeb(addr, reg & 0xff);
+ writeb(addr + 1, reg >> 8);
+ return readb(addr + 3);
+}
+
+static uint64_t read_boot_order_prep(void)
+{
+ return read_m48t59(0x80000000 + 0x74, 0x34);
+}
+
+static const boot_order_test test_cases_prep[] = {
+ { "", 'c', 'c' },
+ { "-boot c", 'c', 'c' },
+ { "-boot d", 'd', 'd' },
+ {}
+};
+
+static void test_prep_boot_order(void)
+{
+ test_boot_orders("prep", read_boot_order_prep, test_cases_prep);
+}
+
+static uint64_t read_boot_order_pmac(void)
+{
+ QFWCFG *fw_cfg = mm_fw_cfg_init(0xf0000510);
+
+ return qfw_cfg_get_u16(fw_cfg, FW_CFG_BOOT_DEVICE);
+}
+
+static const boot_order_test test_cases_fw_cfg[] = {
+ { "", 'c', 'c' },
+ { "-boot c", 'c', 'c' },
+ { "-boot d", 'd', 'd' },
+ { "-boot once=d,order=c", 'd', 'c' },
+ {}
+};
+
+static void test_pmac_oldworld_boot_order(void)
+{
+ test_boot_orders("g3beige", read_boot_order_pmac, test_cases_fw_cfg);
+}
+
+static void test_pmac_newworld_boot_order(void)
+{
+ test_boot_orders("mac99", read_boot_order_pmac, test_cases_fw_cfg);
+}
+
+static uint64_t read_boot_order_sun4m(void)
+{
+ QFWCFG *fw_cfg = mm_fw_cfg_init(0xd00000510ULL);
+
+ return qfw_cfg_get_u16(fw_cfg, FW_CFG_BOOT_DEVICE);
+}
+
+static void test_sun4m_boot_order(void)
+{
+ test_boot_orders("SS-5", read_boot_order_sun4m, test_cases_fw_cfg);
+}
+
+static uint64_t read_boot_order_sun4u(void)
+{
+ QFWCFG *fw_cfg = io_fw_cfg_init(0x510);
+
+ return qfw_cfg_get_u16(fw_cfg, FW_CFG_BOOT_DEVICE);
+}
+
+static void test_sun4u_boot_order(void)
+{
+ test_boot_orders("sun4u", read_boot_order_sun4u, test_cases_fw_cfg);
+}
+
+int main(int argc, char *argv[])
+{
+ const char *arch = qtest_get_arch();
+
+ g_test_init(&argc, &argv, NULL);
+
+ if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
+ qtest_add_func("boot-order/pc", test_pc_boot_order);
+ } else if (strcmp(arch, "ppc") == 0 || strcmp(arch, "ppc64") == 0) {
+ qtest_add_func("boot-order/prep", test_prep_boot_order);
+ qtest_add_func("boot-order/pmac_oldworld",
+ test_pmac_oldworld_boot_order);
+ qtest_add_func("boot-order/pmac_newworld",
+ test_pmac_newworld_boot_order);
+ } else if (strcmp(arch, "sparc") == 0) {
+ qtest_add_func("boot-order/sun4m", test_sun4m_boot_order);
+ } else if (strcmp(arch, "sparc64") == 0) {
+ qtest_add_func("boot-order/sun4u", test_sun4u_boot_order);
+ }
+
+ return g_test_run();
+}
diff --git a/tests/fdc-test.c b/tests/fdc-test.c
index 4b0301da46..fd198dcf8b 100644
--- a/tests/fdc-test.c
+++ b/tests/fdc-test.c
@@ -556,7 +556,7 @@ int main(int argc, char **argv)
ret = g_test_run();
/* Cleanup */
- qtest_quit(global_qtest);
+ qtest_end();
unlink(test_image);
return ret;
diff --git a/tests/fw_cfg-test.c b/tests/fw_cfg-test.c
index c284c4d743..b86e49ab09 100644
--- a/tests/fw_cfg-test.c
+++ b/tests/fw_cfg-test.c
@@ -14,7 +14,7 @@
#include "libqtest.h"
#include "hw/nvram/fw_cfg.h"
-#include "libqos/fw_cfg-pc.h"
+#include "libqos/fw_cfg.h"
#include <string.h>
#include <glib.h>
diff --git a/tests/hd-geo-test.c b/tests/hd-geo-test.c
index 9a31e8587f..b72042e59d 100644
--- a/tests/hd-geo-test.c
+++ b/tests/hd-geo-test.c
@@ -244,7 +244,7 @@ static void test_ide_none(void)
setup_common(argv, ARRAY_SIZE(argv));
qtest_start(g_strjoinv(" ", argv));
test_cmos();
- qtest_quit(global_qtest);
+ qtest_end();
}
static void test_ide_mbr(bool use_device, MBRcontents mbr)
@@ -262,7 +262,7 @@ static void test_ide_mbr(bool use_device, MBRcontents mbr)
}
qtest_start(g_strjoinv(" ", argv));
test_cmos();
- qtest_quit(global_qtest);
+ qtest_end();
}
/*
@@ -334,7 +334,7 @@ static void test_ide_drive_user(const char *dev, bool trans)
g_free(opts);
qtest_start(g_strjoinv(" ", argv));
test_cmos();
- qtest_quit(global_qtest);
+ qtest_end();
}
/*
@@ -387,7 +387,7 @@ static void test_ide_drive_cd_0(void)
}
qtest_start(g_strjoinv(" ", argv));
test_cmos();
- qtest_quit(global_qtest);
+ qtest_end();
}
int main(int argc, char **argv)
diff --git a/tests/ide-test.c b/tests/ide-test.c
index 7e2eb9455a..7307f1d336 100644
--- a/tests/ide-test.c
+++ b/tests/ide-test.c
@@ -122,7 +122,7 @@ static void ide_test_start(const char *cmdline_fmt, ...)
static void ide_test_quit(void)
{
- qtest_quit(global_qtest);
+ qtest_end();
}
static QPCIDevice *get_pci_device(uint16_t *bmdma_base)
diff --git a/tests/libqos/fw_cfg-pc.c b/tests/libqos/fw_cfg-pc.c
deleted file mode 100644
index 613604db77..0000000000
--- a/tests/libqos/fw_cfg-pc.c
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * libqos fw_cfg support for PC
- *
- * Copyright IBM, Corp. 2012-2013
- *
- * Authors:
- * Anthony Liguori <aliguori@us.ibm.com>
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
- */
-
-#include "libqos/fw_cfg-pc.h"
-#include "libqtest.h"
-#include <glib.h>
-
-static void pc_fw_cfg_select(QFWCFG *fw_cfg, uint16_t key)
-{
- outw(0x510, key);
-}
-
-static void pc_fw_cfg_read(QFWCFG *fw_cfg, void *data, size_t len)
-{
- uint8_t *ptr = data;
- int i;
-
- for (i = 0; i < len; i++) {
- ptr[i] = inb(0x511);
- }
-}
-
-QFWCFG *pc_fw_cfg_init(void)
-{
- QFWCFG *fw_cfg = g_malloc0(sizeof(*fw_cfg));
-
- fw_cfg->select = pc_fw_cfg_select;
- fw_cfg->read = pc_fw_cfg_read;
-
- return fw_cfg;
-}
diff --git a/tests/libqos/fw_cfg-pc.h b/tests/libqos/fw_cfg-pc.h
deleted file mode 100644
index 444bd7975a..0000000000
--- a/tests/libqos/fw_cfg-pc.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * libqos fw_cfg support for PC
- *
- * Copyright IBM, Corp. 2012-2013
- *
- * Authors:
- * Anthony Liguori <aliguori@us.ibm.com>
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
- */
-
-#ifndef LIBQOS_FW_CFG_PC_H
-#define LIBQOS_FW_CFG_PC_H
-
-#include "libqos/fw_cfg.h"
-
-QFWCFG *pc_fw_cfg_init(void);
-
-#endif
diff --git a/tests/libqos/fw_cfg.c b/tests/libqos/fw_cfg.c
index e386ff7ba7..ef00fedf1a 100644
--- a/tests/libqos/fw_cfg.c
+++ b/tests/libqos/fw_cfg.c
@@ -2,15 +2,19 @@
* libqos fw_cfg support
*
* Copyright IBM, Corp. 2012-2013
+ * Copyright (C) 2013 Red Hat Inc.
*
* Authors:
* Anthony Liguori <aliguori@us.ibm.com>
+ * Markus Armbruster <armbru@redhat.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
+#include <glib.h>
#include "libqos/fw_cfg.h"
+#include "libqtest.h"
#include "qemu/bswap.h"
void qfw_cfg_select(QFWCFG *fw_cfg, uint16_t key)
@@ -50,3 +54,54 @@ uint64_t qfw_cfg_get_u64(QFWCFG *fw_cfg, uint16_t key)
return le64_to_cpu(value);
}
+static void mm_fw_cfg_select(QFWCFG *fw_cfg, uint16_t key)
+{
+ writew(fw_cfg->base, key);
+}
+
+static void mm_fw_cfg_read(QFWCFG *fw_cfg, void *data, size_t len)
+{
+ uint8_t *ptr = data;
+ int i;
+
+ for (i = 0; i < len; i++) {
+ ptr[i] = readb(fw_cfg->base + 2);
+ }
+}
+
+QFWCFG *mm_fw_cfg_init(uint64_t base)
+{
+ QFWCFG *fw_cfg = g_malloc0(sizeof(*fw_cfg));
+
+ fw_cfg->base = base;
+ fw_cfg->select = mm_fw_cfg_select;
+ fw_cfg->read = mm_fw_cfg_read;
+
+ return fw_cfg;
+}
+
+static void io_fw_cfg_select(QFWCFG *fw_cfg, uint16_t key)
+{
+ outw(fw_cfg->base, key);
+}
+
+static void io_fw_cfg_read(QFWCFG *fw_cfg, void *data, size_t len)
+{
+ uint8_t *ptr = data;
+ int i;
+
+ for (i = 0; i < len; i++) {
+ ptr[i] = inb(fw_cfg->base + 1);
+ }
+}
+
+QFWCFG *io_fw_cfg_init(uint16_t base)
+{
+ QFWCFG *fw_cfg = g_malloc0(sizeof(*fw_cfg));
+
+ fw_cfg->base = base;
+ fw_cfg->select = io_fw_cfg_select;
+ fw_cfg->read = io_fw_cfg_read;
+
+ return fw_cfg;
+}
diff --git a/tests/libqos/fw_cfg.h b/tests/libqos/fw_cfg.h
index 44fc42ba11..61b1548b4e 100644
--- a/tests/libqos/fw_cfg.h
+++ b/tests/libqos/fw_cfg.h
@@ -20,6 +20,7 @@ typedef struct QFWCFG QFWCFG;
struct QFWCFG
{
+ uint64_t base;
void (*select)(QFWCFG *fw_cfg, uint16_t key);
void (*read)(QFWCFG *fw_cfg, void *data, size_t len);
};
@@ -31,4 +32,12 @@ uint16_t qfw_cfg_get_u16(QFWCFG *fw_cfg, uint16_t key);
uint32_t qfw_cfg_get_u32(QFWCFG *fw_cfg, uint16_t key);
uint64_t qfw_cfg_get_u64(QFWCFG *fw_cfg, uint16_t key);
+QFWCFG *mm_fw_cfg_init(uint64_t base);
+QFWCFG *io_fw_cfg_init(uint16_t base);
+
+static inline QFWCFG *pc_fw_cfg_init(void)
+{
+ return io_fw_cfg_init(0x510);
+}
+
#endif
diff --git a/tests/libqos/malloc-pc.c b/tests/libqos/malloc-pc.c
index adc36c4731..db1496c667 100644
--- a/tests/libqos/malloc-pc.c
+++ b/tests/libqos/malloc-pc.c
@@ -11,7 +11,7 @@
*/
#include "libqos/malloc-pc.h"
-#include "libqos/fw_cfg-pc.h"
+#include "libqos/fw_cfg.h"
#define NO_QEMU_PROTOS
#include "hw/nvram/fw_cfg.h"
diff --git a/tests/libqtest.c b/tests/libqtest.c
index 879ffe91dc..bb82069f5c 100644
--- a/tests/libqtest.c
+++ b/tests/libqtest.c
@@ -171,12 +171,16 @@ void qtest_quit(QTestState *s)
waitpid(pid, &status, 0);
}
+ close(s->fd);
+ close(s->qmp_fd);
+ g_string_free(s->rx, true);
unlink(s->pid_file);
unlink(s->socket_path);
unlink(s->qmp_socket_path);
g_free(s->pid_file);
g_free(s->socket_path);
g_free(s->qmp_socket_path);
+ g_free(s);
}
static void socket_sendf(int fd, const char *fmt, va_list ap)
diff --git a/tests/libqtest.h b/tests/libqtest.h
index 437bda39f3..0f6aade092 100644
--- a/tests/libqtest.h
+++ b/tests/libqtest.h
@@ -17,6 +17,7 @@
#ifndef LIBQTEST_H
#define LIBQTEST_H
+#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdarg.h>
@@ -319,6 +320,17 @@ static inline QTestState *qtest_start(const char *args)
}
/**
+ * qtest_end:
+ *
+ * Shut down the QEMU process started by qtest_start().
+ */
+static inline void qtest_end(void)
+{
+ qtest_quit(global_qtest);
+ global_qtest = NULL;
+}
+
+/**
* qmp:
* @fmt...: QMP message to send to qemu
*