122773d60SMichael S. Tsirkin /* 222773d60SMichael S. Tsirkin * QEMU PCI test device 322773d60SMichael S. Tsirkin * 422773d60SMichael S. Tsirkin * Copyright (c) 2012 Red Hat Inc. 522773d60SMichael S. Tsirkin * Author: Michael S. Tsirkin <mst@redhat.com> 622773d60SMichael S. Tsirkin * 722773d60SMichael S. Tsirkin * This program is free software; you can redistribute it and/or modify 822773d60SMichael S. Tsirkin * it under the terms of the GNU General Public License as published by 922773d60SMichael S. Tsirkin * the Free Software Foundation; either version 2 of the License, or 1022773d60SMichael S. Tsirkin * (at your option) any later version. 1122773d60SMichael S. Tsirkin * 1222773d60SMichael S. Tsirkin * This program is distributed in the hope that it will be useful, 1322773d60SMichael S. Tsirkin * but WITHOUT ANY WARRANTY; without even the implied warranty of 1422773d60SMichael S. Tsirkin * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1522773d60SMichael S. Tsirkin * GNU General Public License for more details. 1622773d60SMichael S. Tsirkin * 1722773d60SMichael S. Tsirkin * You should have received a copy of the GNU General Public License along 1822773d60SMichael S. Tsirkin * with this program; if not, see <http://www.gnu.org/licenses/>. 1922773d60SMichael S. Tsirkin */ 2022773d60SMichael S. Tsirkin #include "hw/hw.h" 2122773d60SMichael S. Tsirkin #include "hw/pci/pci.h" 2222773d60SMichael S. Tsirkin #include "qemu/event_notifier.h" 2322773d60SMichael S. Tsirkin #include "qemu/osdep.h" 2422773d60SMichael S. Tsirkin 2522773d60SMichael S. Tsirkin typedef struct PCITestDevHdr { 2622773d60SMichael S. Tsirkin uint8_t test; 2722773d60SMichael S. Tsirkin uint8_t width; 2822773d60SMichael S. Tsirkin uint8_t pad0[2]; 2922773d60SMichael S. Tsirkin uint32_t offset; 3022773d60SMichael S. Tsirkin uint8_t data; 3122773d60SMichael S. Tsirkin uint8_t pad1[3]; 3222773d60SMichael S. Tsirkin uint32_t count; 3322773d60SMichael S. Tsirkin uint8_t name[]; 3422773d60SMichael S. Tsirkin } PCITestDevHdr; 3522773d60SMichael S. Tsirkin 3622773d60SMichael S. Tsirkin typedef struct IOTest { 3722773d60SMichael S. Tsirkin MemoryRegion *mr; 3822773d60SMichael S. Tsirkin EventNotifier notifier; 3922773d60SMichael S. Tsirkin bool hasnotifier; 4022773d60SMichael S. Tsirkin unsigned size; 4122773d60SMichael S. Tsirkin bool match_data; 4222773d60SMichael S. Tsirkin PCITestDevHdr *hdr; 4322773d60SMichael S. Tsirkin unsigned bufsize; 4422773d60SMichael S. Tsirkin } IOTest; 4522773d60SMichael S. Tsirkin 4622773d60SMichael S. Tsirkin #define IOTEST_DATAMATCH 0xFA 4722773d60SMichael S. Tsirkin #define IOTEST_NOMATCH 0xCE 4822773d60SMichael S. Tsirkin 4922773d60SMichael S. Tsirkin #define IOTEST_IOSIZE 128 5022773d60SMichael S. Tsirkin #define IOTEST_MEMSIZE 2048 5122773d60SMichael S. Tsirkin 5222773d60SMichael S. Tsirkin static const char *iotest_test[] = { 5322773d60SMichael S. Tsirkin "no-eventfd", 5422773d60SMichael S. Tsirkin "wildcard-eventfd", 5522773d60SMichael S. Tsirkin "datamatch-eventfd" 5622773d60SMichael S. Tsirkin }; 5722773d60SMichael S. Tsirkin 5822773d60SMichael S. Tsirkin static const char *iotest_type[] = { 5922773d60SMichael S. Tsirkin "mmio", 6022773d60SMichael S. Tsirkin "portio" 6122773d60SMichael S. Tsirkin }; 6222773d60SMichael S. Tsirkin 6322773d60SMichael S. Tsirkin #define IOTEST_TEST(i) (iotest_test[((i) % ARRAY_SIZE(iotest_test))]) 6422773d60SMichael S. Tsirkin #define IOTEST_TYPE(i) (iotest_type[((i) / ARRAY_SIZE(iotest_test))]) 6522773d60SMichael S. Tsirkin #define IOTEST_MAX_TEST (ARRAY_SIZE(iotest_test)) 6622773d60SMichael S. Tsirkin #define IOTEST_MAX_TYPE (ARRAY_SIZE(iotest_type)) 6722773d60SMichael S. Tsirkin #define IOTEST_MAX (IOTEST_MAX_TEST * IOTEST_MAX_TYPE) 6822773d60SMichael S. Tsirkin 6922773d60SMichael S. Tsirkin enum { 7022773d60SMichael S. Tsirkin IOTEST_ACCESS_NAME, 7122773d60SMichael S. Tsirkin IOTEST_ACCESS_DATA, 7222773d60SMichael S. Tsirkin IOTEST_ACCESS_MAX, 7322773d60SMichael S. Tsirkin }; 7422773d60SMichael S. Tsirkin 7522773d60SMichael S. Tsirkin #define IOTEST_ACCESS_TYPE uint8_t 7622773d60SMichael S. Tsirkin #define IOTEST_ACCESS_WIDTH (sizeof(uint8_t)) 7722773d60SMichael S. Tsirkin 7822773d60SMichael S. Tsirkin typedef struct PCITestDevState { 796d27a409SAndreas Färber /*< private >*/ 806d27a409SAndreas Färber PCIDevice parent_obj; 816d27a409SAndreas Färber /*< public >*/ 826d27a409SAndreas Färber 8322773d60SMichael S. Tsirkin MemoryRegion mmio; 8422773d60SMichael S. Tsirkin MemoryRegion portio; 8522773d60SMichael S. Tsirkin IOTest *tests; 8622773d60SMichael S. Tsirkin int current; 8722773d60SMichael S. Tsirkin } PCITestDevState; 8822773d60SMichael S. Tsirkin 8940108d0aSPeter Crosthwaite #define TYPE_PCI_TEST_DEV "pci-testdev" 9040108d0aSPeter Crosthwaite 9140108d0aSPeter Crosthwaite #define PCI_TEST_DEV(obj) \ 9240108d0aSPeter Crosthwaite OBJECT_CHECK(PCITestDevState, (obj), TYPE_PCI_TEST_DEV) 9340108d0aSPeter Crosthwaite 9422773d60SMichael S. Tsirkin #define IOTEST_IS_MEM(i) (strcmp(IOTEST_TYPE(i), "portio")) 9522773d60SMichael S. Tsirkin #define IOTEST_REGION(d, i) (IOTEST_IS_MEM(i) ? &(d)->mmio : &(d)->portio) 9622773d60SMichael S. Tsirkin #define IOTEST_SIZE(i) (IOTEST_IS_MEM(i) ? IOTEST_MEMSIZE : IOTEST_IOSIZE) 9722773d60SMichael S. Tsirkin #define IOTEST_PCI_BAR(i) (IOTEST_IS_MEM(i) ? PCI_BASE_ADDRESS_SPACE_MEMORY : \ 9822773d60SMichael S. Tsirkin PCI_BASE_ADDRESS_SPACE_IO) 9922773d60SMichael S. Tsirkin 10022773d60SMichael S. Tsirkin static int pci_testdev_start(IOTest *test) 10122773d60SMichael S. Tsirkin { 10222773d60SMichael S. Tsirkin test->hdr->count = 0; 10322773d60SMichael S. Tsirkin if (!test->hasnotifier) { 10422773d60SMichael S. Tsirkin return 0; 10522773d60SMichael S. Tsirkin } 10622773d60SMichael S. Tsirkin event_notifier_test_and_clear(&test->notifier); 10722773d60SMichael S. Tsirkin memory_region_add_eventfd(test->mr, 10822773d60SMichael S. Tsirkin le32_to_cpu(test->hdr->offset), 10922773d60SMichael S. Tsirkin test->size, 11022773d60SMichael S. Tsirkin test->match_data, 11122773d60SMichael S. Tsirkin test->hdr->data, 11222773d60SMichael S. Tsirkin &test->notifier); 11322773d60SMichael S. Tsirkin return 0; 11422773d60SMichael S. Tsirkin } 11522773d60SMichael S. Tsirkin 11622773d60SMichael S. Tsirkin static void pci_testdev_stop(IOTest *test) 11722773d60SMichael S. Tsirkin { 11822773d60SMichael S. Tsirkin if (!test->hasnotifier) { 11922773d60SMichael S. Tsirkin return; 12022773d60SMichael S. Tsirkin } 12122773d60SMichael S. Tsirkin memory_region_del_eventfd(test->mr, 12222773d60SMichael S. Tsirkin le32_to_cpu(test->hdr->offset), 12322773d60SMichael S. Tsirkin test->size, 12422773d60SMichael S. Tsirkin test->match_data, 12522773d60SMichael S. Tsirkin test->hdr->data, 12622773d60SMichael S. Tsirkin &test->notifier); 12722773d60SMichael S. Tsirkin } 12822773d60SMichael S. Tsirkin 12922773d60SMichael S. Tsirkin static void 13022773d60SMichael S. Tsirkin pci_testdev_reset(PCITestDevState *d) 13122773d60SMichael S. Tsirkin { 13222773d60SMichael S. Tsirkin if (d->current == -1) { 13322773d60SMichael S. Tsirkin return; 13422773d60SMichael S. Tsirkin } 13522773d60SMichael S. Tsirkin pci_testdev_stop(&d->tests[d->current]); 13622773d60SMichael S. Tsirkin d->current = -1; 13722773d60SMichael S. Tsirkin } 13822773d60SMichael S. Tsirkin 13922773d60SMichael S. Tsirkin static void pci_testdev_inc(IOTest *test, unsigned inc) 14022773d60SMichael S. Tsirkin { 14122773d60SMichael S. Tsirkin uint32_t c = le32_to_cpu(test->hdr->count); 14222773d60SMichael S. Tsirkin test->hdr->count = cpu_to_le32(c + inc); 14322773d60SMichael S. Tsirkin } 14422773d60SMichael S. Tsirkin 14522773d60SMichael S. Tsirkin static void 14622773d60SMichael S. Tsirkin pci_testdev_write(void *opaque, hwaddr addr, uint64_t val, 14722773d60SMichael S. Tsirkin unsigned size, int type) 14822773d60SMichael S. Tsirkin { 14922773d60SMichael S. Tsirkin PCITestDevState *d = opaque; 15022773d60SMichael S. Tsirkin IOTest *test; 15122773d60SMichael S. Tsirkin int t, r; 15222773d60SMichael S. Tsirkin 15322773d60SMichael S. Tsirkin if (addr == offsetof(PCITestDevHdr, test)) { 15422773d60SMichael S. Tsirkin pci_testdev_reset(d); 15522773d60SMichael S. Tsirkin if (val >= IOTEST_MAX_TEST) { 15622773d60SMichael S. Tsirkin return; 15722773d60SMichael S. Tsirkin } 15822773d60SMichael S. Tsirkin t = type * IOTEST_MAX_TEST + val; 15922773d60SMichael S. Tsirkin r = pci_testdev_start(&d->tests[t]); 16022773d60SMichael S. Tsirkin if (r < 0) { 16122773d60SMichael S. Tsirkin return; 16222773d60SMichael S. Tsirkin } 16322773d60SMichael S. Tsirkin d->current = t; 16422773d60SMichael S. Tsirkin return; 16522773d60SMichael S. Tsirkin } 16622773d60SMichael S. Tsirkin if (d->current < 0) { 16722773d60SMichael S. Tsirkin return; 16822773d60SMichael S. Tsirkin } 16922773d60SMichael S. Tsirkin test = &d->tests[d->current]; 17022773d60SMichael S. Tsirkin if (addr != le32_to_cpu(test->hdr->offset)) { 17122773d60SMichael S. Tsirkin return; 17222773d60SMichael S. Tsirkin } 17322773d60SMichael S. Tsirkin if (test->match_data && test->size != size) { 17422773d60SMichael S. Tsirkin return; 17522773d60SMichael S. Tsirkin } 17622773d60SMichael S. Tsirkin if (test->match_data && val != test->hdr->data) { 17722773d60SMichael S. Tsirkin return; 17822773d60SMichael S. Tsirkin } 17922773d60SMichael S. Tsirkin pci_testdev_inc(test, 1); 18022773d60SMichael S. Tsirkin } 18122773d60SMichael S. Tsirkin 18222773d60SMichael S. Tsirkin static uint64_t 18322773d60SMichael S. Tsirkin pci_testdev_read(void *opaque, hwaddr addr, unsigned size) 18422773d60SMichael S. Tsirkin { 18522773d60SMichael S. Tsirkin PCITestDevState *d = opaque; 18622773d60SMichael S. Tsirkin const char *buf; 18722773d60SMichael S. Tsirkin IOTest *test; 18822773d60SMichael S. Tsirkin if (d->current < 0) { 18922773d60SMichael S. Tsirkin return 0; 19022773d60SMichael S. Tsirkin } 19122773d60SMichael S. Tsirkin test = &d->tests[d->current]; 19222773d60SMichael S. Tsirkin buf = (const char *)test->hdr; 19322773d60SMichael S. Tsirkin if (addr + size >= test->bufsize) { 19422773d60SMichael S. Tsirkin return 0; 19522773d60SMichael S. Tsirkin } 19622773d60SMichael S. Tsirkin if (test->hasnotifier) { 19722773d60SMichael S. Tsirkin event_notifier_test_and_clear(&test->notifier); 19822773d60SMichael S. Tsirkin } 19922773d60SMichael S. Tsirkin return buf[addr]; 20022773d60SMichael S. Tsirkin } 20122773d60SMichael S. Tsirkin 20222773d60SMichael S. Tsirkin static void 20322773d60SMichael S. Tsirkin pci_testdev_mmio_write(void *opaque, hwaddr addr, uint64_t val, 20422773d60SMichael S. Tsirkin unsigned size) 20522773d60SMichael S. Tsirkin { 20622773d60SMichael S. Tsirkin pci_testdev_write(opaque, addr, val, size, 0); 20722773d60SMichael S. Tsirkin } 20822773d60SMichael S. Tsirkin 20922773d60SMichael S. Tsirkin static void 21022773d60SMichael S. Tsirkin pci_testdev_pio_write(void *opaque, hwaddr addr, uint64_t val, 21122773d60SMichael S. Tsirkin unsigned size) 21222773d60SMichael S. Tsirkin { 21322773d60SMichael S. Tsirkin pci_testdev_write(opaque, addr, val, size, 1); 21422773d60SMichael S. Tsirkin } 21522773d60SMichael S. Tsirkin 21622773d60SMichael S. Tsirkin static const MemoryRegionOps pci_testdev_mmio_ops = { 21722773d60SMichael S. Tsirkin .read = pci_testdev_read, 21822773d60SMichael S. Tsirkin .write = pci_testdev_mmio_write, 21922773d60SMichael S. Tsirkin .endianness = DEVICE_LITTLE_ENDIAN, 22022773d60SMichael S. Tsirkin .impl = { 22122773d60SMichael S. Tsirkin .min_access_size = 1, 22222773d60SMichael S. Tsirkin .max_access_size = 1, 22322773d60SMichael S. Tsirkin }, 22422773d60SMichael S. Tsirkin }; 22522773d60SMichael S. Tsirkin 22622773d60SMichael S. Tsirkin static const MemoryRegionOps pci_testdev_pio_ops = { 22722773d60SMichael S. Tsirkin .read = pci_testdev_read, 22822773d60SMichael S. Tsirkin .write = pci_testdev_pio_write, 22922773d60SMichael S. Tsirkin .endianness = DEVICE_LITTLE_ENDIAN, 23022773d60SMichael S. Tsirkin .impl = { 23122773d60SMichael S. Tsirkin .min_access_size = 1, 23222773d60SMichael S. Tsirkin .max_access_size = 1, 23322773d60SMichael S. Tsirkin }, 23422773d60SMichael S. Tsirkin }; 23522773d60SMichael S. Tsirkin 23622773d60SMichael S. Tsirkin static int pci_testdev_init(PCIDevice *pci_dev) 23722773d60SMichael S. Tsirkin { 23840108d0aSPeter Crosthwaite PCITestDevState *d = PCI_TEST_DEV(pci_dev); 23922773d60SMichael S. Tsirkin uint8_t *pci_conf; 24022773d60SMichael S. Tsirkin char *name; 24122773d60SMichael S. Tsirkin int r, i; 24222773d60SMichael S. Tsirkin 2436d27a409SAndreas Färber pci_conf = pci_dev->config; 24422773d60SMichael S. Tsirkin 24522773d60SMichael S. Tsirkin pci_conf[PCI_INTERRUPT_PIN] = 0; /* no interrupt pin */ 24622773d60SMichael S. Tsirkin 2473c161542SPaolo Bonzini memory_region_init_io(&d->mmio, OBJECT(d), &pci_testdev_mmio_ops, d, 24822773d60SMichael S. Tsirkin "pci-testdev-mmio", IOTEST_MEMSIZE * 2); 2493c161542SPaolo Bonzini memory_region_init_io(&d->portio, OBJECT(d), &pci_testdev_pio_ops, d, 25022773d60SMichael S. Tsirkin "pci-testdev-portio", IOTEST_IOSIZE * 2); 2516d27a409SAndreas Färber pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &d->mmio); 2526d27a409SAndreas Färber pci_register_bar(pci_dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &d->portio); 25322773d60SMichael S. Tsirkin 25422773d60SMichael S. Tsirkin d->current = -1; 25522773d60SMichael S. Tsirkin d->tests = g_malloc0(IOTEST_MAX * sizeof *d->tests); 25622773d60SMichael S. Tsirkin for (i = 0; i < IOTEST_MAX; ++i) { 25722773d60SMichael S. Tsirkin IOTest *test = &d->tests[i]; 25822773d60SMichael S. Tsirkin name = g_strdup_printf("%s-%s", IOTEST_TYPE(i), IOTEST_TEST(i)); 25922773d60SMichael S. Tsirkin test->bufsize = sizeof(PCITestDevHdr) + strlen(name) + 1; 26022773d60SMichael S. Tsirkin test->hdr = g_malloc0(test->bufsize); 26122773d60SMichael S. Tsirkin memcpy(test->hdr->name, name, strlen(name) + 1); 26222773d60SMichael S. Tsirkin g_free(name); 26322773d60SMichael S. Tsirkin test->hdr->offset = cpu_to_le32(IOTEST_SIZE(i) + i * IOTEST_ACCESS_WIDTH); 26422773d60SMichael S. Tsirkin test->size = IOTEST_ACCESS_WIDTH; 26522773d60SMichael S. Tsirkin test->match_data = strcmp(IOTEST_TEST(i), "wildcard-eventfd"); 26622773d60SMichael S. Tsirkin test->hdr->test = i; 26722773d60SMichael S. Tsirkin test->hdr->data = test->match_data ? IOTEST_DATAMATCH : IOTEST_NOMATCH; 26822773d60SMichael S. Tsirkin test->hdr->width = IOTEST_ACCESS_WIDTH; 26922773d60SMichael S. Tsirkin test->mr = IOTEST_REGION(d, i); 27022773d60SMichael S. Tsirkin if (!strcmp(IOTEST_TEST(i), "no-eventfd")) { 27122773d60SMichael S. Tsirkin test->hasnotifier = false; 27222773d60SMichael S. Tsirkin continue; 27322773d60SMichael S. Tsirkin } 27422773d60SMichael S. Tsirkin r = event_notifier_init(&test->notifier, 0); 27522773d60SMichael S. Tsirkin assert(r >= 0); 27622773d60SMichael S. Tsirkin test->hasnotifier = true; 27722773d60SMichael S. Tsirkin } 27822773d60SMichael S. Tsirkin 27922773d60SMichael S. Tsirkin return 0; 28022773d60SMichael S. Tsirkin } 28122773d60SMichael S. Tsirkin 28222773d60SMichael S. Tsirkin static void 28322773d60SMichael S. Tsirkin pci_testdev_uninit(PCIDevice *dev) 28422773d60SMichael S. Tsirkin { 28540108d0aSPeter Crosthwaite PCITestDevState *d = PCI_TEST_DEV(dev); 28622773d60SMichael S. Tsirkin int i; 28722773d60SMichael S. Tsirkin 28822773d60SMichael S. Tsirkin pci_testdev_reset(d); 28922773d60SMichael S. Tsirkin for (i = 0; i < IOTEST_MAX; ++i) { 29022773d60SMichael S. Tsirkin if (d->tests[i].hasnotifier) { 29122773d60SMichael S. Tsirkin event_notifier_cleanup(&d->tests[i].notifier); 29222773d60SMichael S. Tsirkin } 29322773d60SMichael S. Tsirkin g_free(d->tests[i].hdr); 29422773d60SMichael S. Tsirkin } 29522773d60SMichael S. Tsirkin g_free(d->tests); 29622773d60SMichael S. Tsirkin memory_region_destroy(&d->mmio); 29722773d60SMichael S. Tsirkin memory_region_destroy(&d->portio); 29822773d60SMichael S. Tsirkin } 29922773d60SMichael S. Tsirkin 30022773d60SMichael S. Tsirkin static void qdev_pci_testdev_reset(DeviceState *dev) 30122773d60SMichael S. Tsirkin { 30240108d0aSPeter Crosthwaite PCITestDevState *d = PCI_TEST_DEV(dev); 30322773d60SMichael S. Tsirkin pci_testdev_reset(d); 30422773d60SMichael S. Tsirkin } 30522773d60SMichael S. Tsirkin 30622773d60SMichael S. Tsirkin static void pci_testdev_class_init(ObjectClass *klass, void *data) 30722773d60SMichael S. Tsirkin { 30822773d60SMichael S. Tsirkin DeviceClass *dc = DEVICE_CLASS(klass); 30922773d60SMichael S. Tsirkin PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); 31022773d60SMichael S. Tsirkin 31122773d60SMichael S. Tsirkin k->init = pci_testdev_init; 31222773d60SMichael S. Tsirkin k->exit = pci_testdev_uninit; 31322773d60SMichael S. Tsirkin k->vendor_id = PCI_VENDOR_ID_REDHAT; 31422773d60SMichael S. Tsirkin k->device_id = PCI_DEVICE_ID_REDHAT_TEST; 31522773d60SMichael S. Tsirkin k->revision = 0x00; 31622773d60SMichael S. Tsirkin k->class_id = PCI_CLASS_OTHERS; 31722773d60SMichael S. Tsirkin dc->desc = "PCI Test Device"; 318*125ee0edSMarcel Apfelbaum set_bit(DEVICE_CATEGORY_MISC, dc->categories); 31922773d60SMichael S. Tsirkin dc->reset = qdev_pci_testdev_reset; 32022773d60SMichael S. Tsirkin } 32122773d60SMichael S. Tsirkin 32222773d60SMichael S. Tsirkin static const TypeInfo pci_testdev_info = { 32340108d0aSPeter Crosthwaite .name = TYPE_PCI_TEST_DEV, 32422773d60SMichael S. Tsirkin .parent = TYPE_PCI_DEVICE, 32522773d60SMichael S. Tsirkin .instance_size = sizeof(PCITestDevState), 32622773d60SMichael S. Tsirkin .class_init = pci_testdev_class_init, 32722773d60SMichael S. Tsirkin }; 32822773d60SMichael S. Tsirkin 32922773d60SMichael S. Tsirkin static void pci_testdev_register_types(void) 33022773d60SMichael S. Tsirkin { 33122773d60SMichael S. Tsirkin type_register_static(&pci_testdev_info); 33222773d60SMichael S. Tsirkin } 33322773d60SMichael S. Tsirkin 33422773d60SMichael S. Tsirkin type_init(pci_testdev_register_types) 335