pci: add pci test device

This adds a new device that we can use for testing PCI PIO and MMIO, with and
 without ioeventfd in different configurations.  FAST_MMIO will be added if/when
 kvm supports it.  Also included are minor cleanups in kvm APIs that it needs.
 
 Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1.4.13 (GNU/Linux)
 
 iQEcBAABAgAGBQJRbIJQAAoJECgfDbjSjVRpQuoH/RfMHb6YYfsuwJKUsjCKxhdr
 695YqNsBLmh7E/+wr1dwUsKrMGSF97VSGAIPeX0u4wwP6jrORhA9iycCevXYlh/S
 O7RTcePqVEQrwnMX5rOAEWGARVzg4hAT8i4Pdza1A+gBvaO/WLZIVJfUOHBAZNL7
 2TTDymfixipErcTcxckITHfaShn9ajZgt/Yo8oVX70VqklWU+OEU/tYEXmvTC0H3
 bTuTU3vpeAlCubF0AHHZqWA9g7myrKMCxwv4LWx7gmQGXoyQesy4s5C9KMrld1On
 RovLw0REbtjB2xGjAj3g82ESK5eoi295Th/E7Fu1NJNYyDyfhxB7/cnbRa+Wpsg=
 =jvZE
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'mst/tags/for_anthony' into staging

pci: add pci test device

This adds a new device that we can use for testing PCI PIO and MMIO, with and
without ioeventfd in different configurations.  FAST_MMIO will be added if/when
kvm supports it.  Also included are minor cleanups in kvm APIs that it needs.

Signed-off-by: Michael S. Tsirkin <mst@redhat.com>

# gpg: Signature made Mon 15 Apr 2013 05:42:24 PM CDT using RSA key ID D28D5469
# gpg: Can't check signature: public key not found

# By Michael S. Tsirkin
# Via Michael S. Tsirkin
* mst/tags/for_anthony:
  pci: add pci test device
  kvm: support non datamatch ioeventfd
  kvm: support any size for pio eventfd
  kvm: remove unused APIs

Message-id: cover.1366272004.git.mst@redhat.com
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
This commit is contained in:
Anthony Liguori 2013-04-19 08:38:05 -05:00
commit 5dff24beba
8 changed files with 422 additions and 79 deletions

View File

@ -24,3 +24,4 @@ CONFIG_SERIAL=y
CONFIG_SERIAL_PCI=y
CONFIG_IPACK=y
CONFIG_WDT_IB6300ESB=y
CONFIG_PCI_TESTDEV=y

View File

@ -0,0 +1,26 @@
pci-test is a device used for testing low level IO
device implements up to two BARs: BAR0 and BAR1.
Each BAR can be memory or IO. Guests must detect
BAR type and act accordingly.
Each BAR size is up to 4K bytes.
Each BAR starts with the following header:
typedef struct PCITestDevHdr {
uint8_t test; <- write-only, starts a given test number
uint8_t width_type; <- read-only, type and width of access for a given test.
1,2,4 for byte,word or long write.
any other value if test not supported on this BAR
uint8_t pad0[2];
uint32_t offset; <- read-only, offset in this BAR for a given test
uint32_t data; <- read-only, data to use for a given test
uint32_t count; <- for debugging. number of writes detected.
uint8_t name[]; <- for debugging. 0-terminated ASCII string.
} PCITestDevHdr;
All registers are little endian.
device is expected to always implement tests 0 to N on each BAR, and to add new
tests with higher numbers. In this way a guest can scan test numbers until it
detects an access type that it does not support on this BAR, then stop.

View File

@ -4,6 +4,7 @@ common-obj-$(CONFIG_TMP105) += tmp105.o
common-obj-$(CONFIG_ISA_DEBUG) += debugexit.o
common-obj-$(CONFIG_SGA) += sga.o
common-obj-$(CONFIG_ISA_TESTDEV) += pc-testdev.o
common-obj-$(CONFIG_PCI_TESTDEV) += pci-testdev.o
obj-$(CONFIG_VMPORT) += vmport.o

325
hw/misc/pci-testdev.c Normal file
View File

@ -0,0 +1,325 @@
/*
* QEMU PCI test device
*
* Copyright (c) 2012 Red Hat Inc.
* Author: Michael S. Tsirkin <mst@redhat.com>
*
* 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.
*
* 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/hw.h"
#include "hw/pci/pci.h"
#include "qemu/event_notifier.h"
#include "qemu/osdep.h"
typedef struct PCITestDevHdr {
uint8_t test;
uint8_t width;
uint8_t pad0[2];
uint32_t offset;
uint8_t data;
uint8_t pad1[3];
uint32_t count;
uint8_t name[];
} PCITestDevHdr;
typedef struct IOTest {
MemoryRegion *mr;
EventNotifier notifier;
bool hasnotifier;
unsigned size;
bool match_data;
PCITestDevHdr *hdr;
unsigned bufsize;
} IOTest;
#define IOTEST_DATAMATCH 0xFA
#define IOTEST_NOMATCH 0xCE
#define IOTEST_IOSIZE 128
#define IOTEST_MEMSIZE 2048
static const char *iotest_test[] = {
"no-eventfd",
"wildcard-eventfd",
"datamatch-eventfd"
};
static const char *iotest_type[] = {
"mmio",
"portio"
};
#define IOTEST_TEST(i) (iotest_test[((i) % ARRAY_SIZE(iotest_test))])
#define IOTEST_TYPE(i) (iotest_type[((i) / ARRAY_SIZE(iotest_test))])
#define IOTEST_MAX_TEST (ARRAY_SIZE(iotest_test))
#define IOTEST_MAX_TYPE (ARRAY_SIZE(iotest_type))
#define IOTEST_MAX (IOTEST_MAX_TEST * IOTEST_MAX_TYPE)
enum {
IOTEST_ACCESS_NAME,
IOTEST_ACCESS_DATA,
IOTEST_ACCESS_MAX,
};
#define IOTEST_ACCESS_TYPE uint8_t
#define IOTEST_ACCESS_WIDTH (sizeof(uint8_t))
typedef struct PCITestDevState {
PCIDevice dev;
MemoryRegion mmio;
MemoryRegion portio;
IOTest *tests;
int current;
} PCITestDevState;
#define IOTEST_IS_MEM(i) (strcmp(IOTEST_TYPE(i), "portio"))
#define IOTEST_REGION(d, i) (IOTEST_IS_MEM(i) ? &(d)->mmio : &(d)->portio)
#define IOTEST_SIZE(i) (IOTEST_IS_MEM(i) ? IOTEST_MEMSIZE : IOTEST_IOSIZE)
#define IOTEST_PCI_BAR(i) (IOTEST_IS_MEM(i) ? PCI_BASE_ADDRESS_SPACE_MEMORY : \
PCI_BASE_ADDRESS_SPACE_IO)
static int pci_testdev_start(IOTest *test)
{
test->hdr->count = 0;
if (!test->hasnotifier) {
return 0;
}
event_notifier_test_and_clear(&test->notifier);
memory_region_add_eventfd(test->mr,
le32_to_cpu(test->hdr->offset),
test->size,
test->match_data,
test->hdr->data,
&test->notifier);
return 0;
}
static void pci_testdev_stop(IOTest *test)
{
if (!test->hasnotifier) {
return;
}
memory_region_del_eventfd(test->mr,
le32_to_cpu(test->hdr->offset),
test->size,
test->match_data,
test->hdr->data,
&test->notifier);
}
static void
pci_testdev_reset(PCITestDevState *d)
{
if (d->current == -1) {
return;
}
pci_testdev_stop(&d->tests[d->current]);
d->current = -1;
}
static void pci_testdev_inc(IOTest *test, unsigned inc)
{
uint32_t c = le32_to_cpu(test->hdr->count);
test->hdr->count = cpu_to_le32(c + inc);
}
static void
pci_testdev_write(void *opaque, hwaddr addr, uint64_t val,
unsigned size, int type)
{
PCITestDevState *d = opaque;
IOTest *test;
int t, r;
if (addr == offsetof(PCITestDevHdr, test)) {
pci_testdev_reset(d);
if (val >= IOTEST_MAX_TEST) {
return;
}
t = type * IOTEST_MAX_TEST + val;
r = pci_testdev_start(&d->tests[t]);
if (r < 0) {
return;
}
d->current = t;
return;
}
if (d->current < 0) {
return;
}
test = &d->tests[d->current];
if (addr != le32_to_cpu(test->hdr->offset)) {
return;
}
if (test->match_data && test->size != size) {
return;
}
if (test->match_data && val != test->hdr->data) {
return;
}
pci_testdev_inc(test, 1);
}
static uint64_t
pci_testdev_read(void *opaque, hwaddr addr, unsigned size)
{
PCITestDevState *d = opaque;
const char *buf;
IOTest *test;
if (d->current < 0) {
return 0;
}
test = &d->tests[d->current];
buf = (const char *)test->hdr;
if (addr + size >= test->bufsize) {
return 0;
}
if (test->hasnotifier) {
event_notifier_test_and_clear(&test->notifier);
}
return buf[addr];
}
static void
pci_testdev_mmio_write(void *opaque, hwaddr addr, uint64_t val,
unsigned size)
{
pci_testdev_write(opaque, addr, val, size, 0);
}
static void
pci_testdev_pio_write(void *opaque, hwaddr addr, uint64_t val,
unsigned size)
{
pci_testdev_write(opaque, addr, val, size, 1);
}
static const MemoryRegionOps pci_testdev_mmio_ops = {
.read = pci_testdev_read,
.write = pci_testdev_mmio_write,
.endianness = DEVICE_LITTLE_ENDIAN,
.impl = {
.min_access_size = 1,
.max_access_size = 1,
},
};
static const MemoryRegionOps pci_testdev_pio_ops = {
.read = pci_testdev_read,
.write = pci_testdev_pio_write,
.endianness = DEVICE_LITTLE_ENDIAN,
.impl = {
.min_access_size = 1,
.max_access_size = 1,
},
};
static int pci_testdev_init(PCIDevice *pci_dev)
{
PCITestDevState *d = DO_UPCAST(PCITestDevState, dev, pci_dev);
uint8_t *pci_conf;
char *name;
int r, i;
pci_conf = d->dev.config;
pci_conf[PCI_INTERRUPT_PIN] = 0; /* no interrupt pin */
memory_region_init_io(&d->mmio, &pci_testdev_mmio_ops, d,
"pci-testdev-mmio", IOTEST_MEMSIZE * 2);
memory_region_init_io(&d->portio, &pci_testdev_pio_ops, d,
"pci-testdev-portio", IOTEST_IOSIZE * 2);
pci_register_bar(&d->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &d->mmio);
pci_register_bar(&d->dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &d->portio);
d->current = -1;
d->tests = g_malloc0(IOTEST_MAX * sizeof *d->tests);
for (i = 0; i < IOTEST_MAX; ++i) {
IOTest *test = &d->tests[i];
name = g_strdup_printf("%s-%s", IOTEST_TYPE(i), IOTEST_TEST(i));
test->bufsize = sizeof(PCITestDevHdr) + strlen(name) + 1;
test->hdr = g_malloc0(test->bufsize);
memcpy(test->hdr->name, name, strlen(name) + 1);
g_free(name);
test->hdr->offset = cpu_to_le32(IOTEST_SIZE(i) + i * IOTEST_ACCESS_WIDTH);
test->size = IOTEST_ACCESS_WIDTH;
test->match_data = strcmp(IOTEST_TEST(i), "wildcard-eventfd");
test->hdr->test = i;
test->hdr->data = test->match_data ? IOTEST_DATAMATCH : IOTEST_NOMATCH;
test->hdr->width = IOTEST_ACCESS_WIDTH;
test->mr = IOTEST_REGION(d, i);
if (!strcmp(IOTEST_TEST(i), "no-eventfd")) {
test->hasnotifier = false;
continue;
}
r = event_notifier_init(&test->notifier, 0);
assert(r >= 0);
test->hasnotifier = true;
}
return 0;
}
static void
pci_testdev_uninit(PCIDevice *dev)
{
PCITestDevState *d = DO_UPCAST(PCITestDevState, dev, dev);
int i;
pci_testdev_reset(d);
for (i = 0; i < IOTEST_MAX; ++i) {
if (d->tests[i].hasnotifier) {
event_notifier_cleanup(&d->tests[i].notifier);
}
g_free(d->tests[i].hdr);
}
g_free(d->tests);
memory_region_destroy(&d->mmio);
memory_region_destroy(&d->portio);
}
static void qdev_pci_testdev_reset(DeviceState *dev)
{
PCITestDevState *d = DO_UPCAST(PCITestDevState, dev.qdev, dev);
pci_testdev_reset(d);
}
static void pci_testdev_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
k->init = pci_testdev_init;
k->exit = pci_testdev_uninit;
k->vendor_id = PCI_VENDOR_ID_REDHAT;
k->device_id = PCI_DEVICE_ID_REDHAT_TEST;
k->revision = 0x00;
k->class_id = PCI_CLASS_OTHERS;
dc->desc = "PCI Test Device";
dc->reset = qdev_pci_testdev_reset;
}
static const TypeInfo pci_testdev_info = {
.name = "pci-testdev",
.parent = TYPE_PCI_DEVICE,
.instance_size = sizeof(PCITestDevState),
.class_init = pci_testdev_class_init,
};
static void pci_testdev_register_types(void)
{
type_register_static(&pci_testdev_info);
}
type_init(pci_testdev_register_types)

View File

@ -85,6 +85,7 @@
#define PCI_DEVICE_ID_REDHAT_SERIAL 0x0002
#define PCI_DEVICE_ID_REDHAT_SERIAL2 0x0003
#define PCI_DEVICE_ID_REDHAT_SERIAL4 0x0004
#define PCI_DEVICE_ID_REDHAT_TEST 0x0005
#define PCI_DEVICE_ID_REDHAT_QXL 0x0100
#define FMT_PCIBUS PRIx64

View File

@ -283,10 +283,6 @@ int kvm_physical_memory_addr_from_host(KVMState *s, void *ram_addr,
#endif
#endif
int kvm_set_ioeventfd_mmio(int fd, uint32_t adr, uint32_t val, bool assign,
uint32_t size);
int kvm_set_ioeventfd_pio_word(int fd, uint16_t adr, uint16_t val, bool assign);
int kvm_irqchip_add_msi_route(KVMState *s, MSIMessage msg);
int kvm_irqchip_update_msi_route(KVMState *s, int virq, MSIMessage msg);

133
kvm-all.c
View File

@ -500,6 +500,66 @@ int kvm_check_extension(KVMState *s, unsigned int extension)
return ret;
}
static int kvm_set_ioeventfd_mmio(int fd, uint32_t addr, uint32_t val,
bool assign, uint32_t size, bool datamatch)
{
int ret;
struct kvm_ioeventfd iofd;
iofd.datamatch = datamatch ? val : 0;
iofd.addr = addr;
iofd.len = size;
iofd.flags = 0;
iofd.fd = fd;
if (!kvm_enabled()) {
return -ENOSYS;
}
if (datamatch) {
iofd.flags |= KVM_IOEVENTFD_FLAG_DATAMATCH;
}
if (!assign) {
iofd.flags |= KVM_IOEVENTFD_FLAG_DEASSIGN;
}
ret = kvm_vm_ioctl(kvm_state, KVM_IOEVENTFD, &iofd);
if (ret < 0) {
return -errno;
}
return 0;
}
static int kvm_set_ioeventfd_pio(int fd, uint16_t addr, uint16_t val,
bool assign, uint32_t size, bool datamatch)
{
struct kvm_ioeventfd kick = {
.datamatch = datamatch ? val : 0,
.addr = addr,
.flags = KVM_IOEVENTFD_FLAG_PIO,
.len = size,
.fd = fd,
};
int r;
if (!kvm_enabled()) {
return -ENOSYS;
}
if (datamatch) {
kick.flags |= KVM_IOEVENTFD_FLAG_DATAMATCH;
}
if (!assign) {
kick.flags |= KVM_IOEVENTFD_FLAG_DEASSIGN;
}
r = kvm_vm_ioctl(kvm_state, KVM_IOEVENTFD, &kick);
if (r < 0) {
return r;
}
return 0;
}
static int kvm_check_many_ioeventfds(void)
{
/* Userspace can use ioeventfd for io notification. This requires a host
@ -517,7 +577,7 @@ static int kvm_check_many_ioeventfds(void)
if (ioeventfds[i] < 0) {
break;
}
ret = kvm_set_ioeventfd_pio_word(ioeventfds[i], 0, i, true);
ret = kvm_set_ioeventfd_pio(ioeventfds[i], 0, i, true, 2, true);
if (ret < 0) {
close(ioeventfds[i]);
break;
@ -528,7 +588,7 @@ static int kvm_check_many_ioeventfds(void)
ret = i == ARRAY_SIZE(ioeventfds);
while (i-- > 0) {
kvm_set_ioeventfd_pio_word(ioeventfds[i], 0, i, false);
kvm_set_ioeventfd_pio(ioeventfds[i], 0, i, false, 2, true);
close(ioeventfds[i]);
}
return ret;
@ -748,10 +808,8 @@ static void kvm_mem_ioeventfd_add(MemoryListener *listener,
int fd = event_notifier_get_fd(e);
int r;
assert(match_data && section->size <= 8);
r = kvm_set_ioeventfd_mmio(fd, section->offset_within_address_space,
data, true, section->size);
data, true, section->size, match_data);
if (r < 0) {
abort();
}
@ -766,7 +824,7 @@ static void kvm_mem_ioeventfd_del(MemoryListener *listener,
int r;
r = kvm_set_ioeventfd_mmio(fd, section->offset_within_address_space,
data, false, section->size);
data, false, section->size, match_data);
if (r < 0) {
abort();
}
@ -780,10 +838,8 @@ static void kvm_io_ioeventfd_add(MemoryListener *listener,
int fd = event_notifier_get_fd(e);
int r;
assert(match_data && section->size == 2);
r = kvm_set_ioeventfd_pio_word(fd, section->offset_within_address_space,
data, true);
r = kvm_set_ioeventfd_pio(fd, section->offset_within_address_space,
data, true, section->size, match_data);
if (r < 0) {
abort();
}
@ -798,8 +854,8 @@ static void kvm_io_ioeventfd_del(MemoryListener *listener,
int fd = event_notifier_get_fd(e);
int r;
r = kvm_set_ioeventfd_pio_word(fd, section->offset_within_address_space,
data, false);
r = kvm_set_ioeventfd_pio(fd, section->offset_within_address_space,
data, false, section->size, match_data);
if (r < 0) {
abort();
}
@ -1967,59 +2023,6 @@ int kvm_set_signal_mask(CPUArchState *env, const sigset_t *sigset)
return r;
}
int kvm_set_ioeventfd_mmio(int fd, uint32_t addr, uint32_t val, bool assign,
uint32_t size)
{
int ret;
struct kvm_ioeventfd iofd;
iofd.datamatch = val;
iofd.addr = addr;
iofd.len = size;
iofd.flags = KVM_IOEVENTFD_FLAG_DATAMATCH;
iofd.fd = fd;
if (!kvm_enabled()) {
return -ENOSYS;
}
if (!assign) {
iofd.flags |= KVM_IOEVENTFD_FLAG_DEASSIGN;
}
ret = kvm_vm_ioctl(kvm_state, KVM_IOEVENTFD, &iofd);
if (ret < 0) {
return -errno;
}
return 0;
}
int kvm_set_ioeventfd_pio_word(int fd, uint16_t addr, uint16_t val, bool assign)
{
struct kvm_ioeventfd kick = {
.datamatch = val,
.addr = addr,
.len = 2,
.flags = KVM_IOEVENTFD_FLAG_DATAMATCH | KVM_IOEVENTFD_FLAG_PIO,
.fd = fd,
};
int r;
if (!kvm_enabled()) {
return -ENOSYS;
}
if (!assign) {
kick.flags |= KVM_IOEVENTFD_FLAG_DEASSIGN;
}
r = kvm_vm_ioctl(kvm_state, KVM_IOEVENTFD, &kick);
if (r < 0) {
return r;
}
return 0;
}
int kvm_on_sigbus_vcpu(CPUState *cpu, int code, void *addr)
{
return kvm_arch_on_sigbus_vcpu(cpu, code, addr);

View File

@ -101,16 +101,6 @@ int kvm_set_signal_mask(CPUArchState *env, const sigset_t *sigset)
}
#endif
int kvm_set_ioeventfd_pio_word(int fd, uint16_t addr, uint16_t val, bool assign)
{
return -ENOSYS;
}
int kvm_set_ioeventfd_mmio(int fd, uint32_t adr, uint32_t val, bool assign, uint32_t len)
{
return -ENOSYS;
}
int kvm_on_sigbus_vcpu(CPUState *cpu, int code, void *addr)
{
return 1;