1 /* 2 * QEMU PCI test device 3 * 4 * Copyright (c) 2012 Red Hat Inc. 5 * Author: Michael S. Tsirkin <mst@redhat.com> 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; either version 2 of the License, or 10 * (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License along 18 * with this program; if not, see <http://www.gnu.org/licenses/>. 19 */ 20 #include "hw/hw.h" 21 #include "hw/pci/pci.h" 22 #include "qemu/event_notifier.h" 23 #include "qemu/osdep.h" 24 25 typedef struct PCITestDevHdr { 26 uint8_t test; 27 uint8_t width; 28 uint8_t pad0[2]; 29 uint32_t offset; 30 uint8_t data; 31 uint8_t pad1[3]; 32 uint32_t count; 33 uint8_t name[]; 34 } PCITestDevHdr; 35 36 typedef struct IOTest { 37 MemoryRegion *mr; 38 EventNotifier notifier; 39 bool hasnotifier; 40 unsigned size; 41 bool match_data; 42 PCITestDevHdr *hdr; 43 unsigned bufsize; 44 } IOTest; 45 46 #define IOTEST_DATAMATCH 0xFA 47 #define IOTEST_NOMATCH 0xCE 48 49 #define IOTEST_IOSIZE 128 50 #define IOTEST_MEMSIZE 2048 51 52 static const char *iotest_test[] = { 53 "no-eventfd", 54 "wildcard-eventfd", 55 "datamatch-eventfd" 56 }; 57 58 static const char *iotest_type[] = { 59 "mmio", 60 "portio" 61 }; 62 63 #define IOTEST_TEST(i) (iotest_test[((i) % ARRAY_SIZE(iotest_test))]) 64 #define IOTEST_TYPE(i) (iotest_type[((i) / ARRAY_SIZE(iotest_test))]) 65 #define IOTEST_MAX_TEST (ARRAY_SIZE(iotest_test)) 66 #define IOTEST_MAX_TYPE (ARRAY_SIZE(iotest_type)) 67 #define IOTEST_MAX (IOTEST_MAX_TEST * IOTEST_MAX_TYPE) 68 69 enum { 70 IOTEST_ACCESS_NAME, 71 IOTEST_ACCESS_DATA, 72 IOTEST_ACCESS_MAX, 73 }; 74 75 #define IOTEST_ACCESS_TYPE uint8_t 76 #define IOTEST_ACCESS_WIDTH (sizeof(uint8_t)) 77 78 typedef struct PCITestDevState { 79 PCIDevice dev; 80 MemoryRegion mmio; 81 MemoryRegion portio; 82 IOTest *tests; 83 int current; 84 } PCITestDevState; 85 86 #define IOTEST_IS_MEM(i) (strcmp(IOTEST_TYPE(i), "portio")) 87 #define IOTEST_REGION(d, i) (IOTEST_IS_MEM(i) ? &(d)->mmio : &(d)->portio) 88 #define IOTEST_SIZE(i) (IOTEST_IS_MEM(i) ? IOTEST_MEMSIZE : IOTEST_IOSIZE) 89 #define IOTEST_PCI_BAR(i) (IOTEST_IS_MEM(i) ? PCI_BASE_ADDRESS_SPACE_MEMORY : \ 90 PCI_BASE_ADDRESS_SPACE_IO) 91 92 static int pci_testdev_start(IOTest *test) 93 { 94 test->hdr->count = 0; 95 if (!test->hasnotifier) { 96 return 0; 97 } 98 event_notifier_test_and_clear(&test->notifier); 99 memory_region_add_eventfd(test->mr, 100 le32_to_cpu(test->hdr->offset), 101 test->size, 102 test->match_data, 103 test->hdr->data, 104 &test->notifier); 105 return 0; 106 } 107 108 static void pci_testdev_stop(IOTest *test) 109 { 110 if (!test->hasnotifier) { 111 return; 112 } 113 memory_region_del_eventfd(test->mr, 114 le32_to_cpu(test->hdr->offset), 115 test->size, 116 test->match_data, 117 test->hdr->data, 118 &test->notifier); 119 } 120 121 static void 122 pci_testdev_reset(PCITestDevState *d) 123 { 124 if (d->current == -1) { 125 return; 126 } 127 pci_testdev_stop(&d->tests[d->current]); 128 d->current = -1; 129 } 130 131 static void pci_testdev_inc(IOTest *test, unsigned inc) 132 { 133 uint32_t c = le32_to_cpu(test->hdr->count); 134 test->hdr->count = cpu_to_le32(c + inc); 135 } 136 137 static void 138 pci_testdev_write(void *opaque, hwaddr addr, uint64_t val, 139 unsigned size, int type) 140 { 141 PCITestDevState *d = opaque; 142 IOTest *test; 143 int t, r; 144 145 if (addr == offsetof(PCITestDevHdr, test)) { 146 pci_testdev_reset(d); 147 if (val >= IOTEST_MAX_TEST) { 148 return; 149 } 150 t = type * IOTEST_MAX_TEST + val; 151 r = pci_testdev_start(&d->tests[t]); 152 if (r < 0) { 153 return; 154 } 155 d->current = t; 156 return; 157 } 158 if (d->current < 0) { 159 return; 160 } 161 test = &d->tests[d->current]; 162 if (addr != le32_to_cpu(test->hdr->offset)) { 163 return; 164 } 165 if (test->match_data && test->size != size) { 166 return; 167 } 168 if (test->match_data && val != test->hdr->data) { 169 return; 170 } 171 pci_testdev_inc(test, 1); 172 } 173 174 static uint64_t 175 pci_testdev_read(void *opaque, hwaddr addr, unsigned size) 176 { 177 PCITestDevState *d = opaque; 178 const char *buf; 179 IOTest *test; 180 if (d->current < 0) { 181 return 0; 182 } 183 test = &d->tests[d->current]; 184 buf = (const char *)test->hdr; 185 if (addr + size >= test->bufsize) { 186 return 0; 187 } 188 if (test->hasnotifier) { 189 event_notifier_test_and_clear(&test->notifier); 190 } 191 return buf[addr]; 192 } 193 194 static void 195 pci_testdev_mmio_write(void *opaque, hwaddr addr, uint64_t val, 196 unsigned size) 197 { 198 pci_testdev_write(opaque, addr, val, size, 0); 199 } 200 201 static void 202 pci_testdev_pio_write(void *opaque, hwaddr addr, uint64_t val, 203 unsigned size) 204 { 205 pci_testdev_write(opaque, addr, val, size, 1); 206 } 207 208 static const MemoryRegionOps pci_testdev_mmio_ops = { 209 .read = pci_testdev_read, 210 .write = pci_testdev_mmio_write, 211 .endianness = DEVICE_LITTLE_ENDIAN, 212 .impl = { 213 .min_access_size = 1, 214 .max_access_size = 1, 215 }, 216 }; 217 218 static const MemoryRegionOps pci_testdev_pio_ops = { 219 .read = pci_testdev_read, 220 .write = pci_testdev_pio_write, 221 .endianness = DEVICE_LITTLE_ENDIAN, 222 .impl = { 223 .min_access_size = 1, 224 .max_access_size = 1, 225 }, 226 }; 227 228 static int pci_testdev_init(PCIDevice *pci_dev) 229 { 230 PCITestDevState *d = DO_UPCAST(PCITestDevState, dev, pci_dev); 231 uint8_t *pci_conf; 232 char *name; 233 int r, i; 234 235 pci_conf = d->dev.config; 236 237 pci_conf[PCI_INTERRUPT_PIN] = 0; /* no interrupt pin */ 238 239 memory_region_init_io(&d->mmio, OBJECT(d), &pci_testdev_mmio_ops, d, 240 "pci-testdev-mmio", IOTEST_MEMSIZE * 2); 241 memory_region_init_io(&d->portio, OBJECT(d), &pci_testdev_pio_ops, d, 242 "pci-testdev-portio", IOTEST_IOSIZE * 2); 243 pci_register_bar(&d->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &d->mmio); 244 pci_register_bar(&d->dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &d->portio); 245 246 d->current = -1; 247 d->tests = g_malloc0(IOTEST_MAX * sizeof *d->tests); 248 for (i = 0; i < IOTEST_MAX; ++i) { 249 IOTest *test = &d->tests[i]; 250 name = g_strdup_printf("%s-%s", IOTEST_TYPE(i), IOTEST_TEST(i)); 251 test->bufsize = sizeof(PCITestDevHdr) + strlen(name) + 1; 252 test->hdr = g_malloc0(test->bufsize); 253 memcpy(test->hdr->name, name, strlen(name) + 1); 254 g_free(name); 255 test->hdr->offset = cpu_to_le32(IOTEST_SIZE(i) + i * IOTEST_ACCESS_WIDTH); 256 test->size = IOTEST_ACCESS_WIDTH; 257 test->match_data = strcmp(IOTEST_TEST(i), "wildcard-eventfd"); 258 test->hdr->test = i; 259 test->hdr->data = test->match_data ? IOTEST_DATAMATCH : IOTEST_NOMATCH; 260 test->hdr->width = IOTEST_ACCESS_WIDTH; 261 test->mr = IOTEST_REGION(d, i); 262 if (!strcmp(IOTEST_TEST(i), "no-eventfd")) { 263 test->hasnotifier = false; 264 continue; 265 } 266 r = event_notifier_init(&test->notifier, 0); 267 assert(r >= 0); 268 test->hasnotifier = true; 269 } 270 271 return 0; 272 } 273 274 static void 275 pci_testdev_uninit(PCIDevice *dev) 276 { 277 PCITestDevState *d = DO_UPCAST(PCITestDevState, dev, dev); 278 int i; 279 280 pci_testdev_reset(d); 281 for (i = 0; i < IOTEST_MAX; ++i) { 282 if (d->tests[i].hasnotifier) { 283 event_notifier_cleanup(&d->tests[i].notifier); 284 } 285 g_free(d->tests[i].hdr); 286 } 287 g_free(d->tests); 288 memory_region_destroy(&d->mmio); 289 memory_region_destroy(&d->portio); 290 } 291 292 static void qdev_pci_testdev_reset(DeviceState *dev) 293 { 294 PCITestDevState *d = DO_UPCAST(PCITestDevState, dev.qdev, dev); 295 pci_testdev_reset(d); 296 } 297 298 static void pci_testdev_class_init(ObjectClass *klass, void *data) 299 { 300 DeviceClass *dc = DEVICE_CLASS(klass); 301 PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); 302 303 k->init = pci_testdev_init; 304 k->exit = pci_testdev_uninit; 305 k->vendor_id = PCI_VENDOR_ID_REDHAT; 306 k->device_id = PCI_DEVICE_ID_REDHAT_TEST; 307 k->revision = 0x00; 308 k->class_id = PCI_CLASS_OTHERS; 309 dc->desc = "PCI Test Device"; 310 dc->reset = qdev_pci_testdev_reset; 311 } 312 313 static const TypeInfo pci_testdev_info = { 314 .name = "pci-testdev", 315 .parent = TYPE_PCI_DEVICE, 316 .instance_size = sizeof(PCITestDevState), 317 .class_init = pci_testdev_class_init, 318 }; 319 320 static void pci_testdev_register_types(void) 321 { 322 type_register_static(&pci_testdev_info); 323 } 324 325 type_init(pci_testdev_register_types) 326