xref: /openbmc/qemu/hw/misc/pci-testdev.c (revision 99d46107)
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 "qemu/osdep.h"
21 #include "hw/hw.h"
22 #include "hw/pci/pci.h"
23 #include "qemu/event_notifier.h"
24 #include "sysemu/kvm.h"
25 
26 typedef struct PCITestDevHdr {
27     uint8_t test;
28     uint8_t width;
29     uint8_t pad0[2];
30     uint32_t offset;
31     uint8_t data;
32     uint8_t pad1[3];
33     uint32_t count;
34     uint8_t name[];
35 } PCITestDevHdr;
36 
37 typedef struct IOTest {
38     MemoryRegion *mr;
39     EventNotifier notifier;
40     bool hasnotifier;
41     unsigned size;
42     bool match_data;
43     PCITestDevHdr *hdr;
44     unsigned bufsize;
45 } IOTest;
46 
47 #define IOTEST_DATAMATCH 0xFA
48 #define IOTEST_NOMATCH   0xCE
49 
50 #define IOTEST_IOSIZE 128
51 #define IOTEST_MEMSIZE 2048
52 
53 static const char *iotest_test[] = {
54     "no-eventfd",
55     "wildcard-eventfd",
56     "datamatch-eventfd"
57 };
58 
59 static const char *iotest_type[] = {
60     "mmio",
61     "portio"
62 };
63 
64 #define IOTEST_TEST(i) (iotest_test[((i) % ARRAY_SIZE(iotest_test))])
65 #define IOTEST_TYPE(i) (iotest_type[((i) / ARRAY_SIZE(iotest_test))])
66 #define IOTEST_MAX_TEST (ARRAY_SIZE(iotest_test))
67 #define IOTEST_MAX_TYPE (ARRAY_SIZE(iotest_type))
68 #define IOTEST_MAX (IOTEST_MAX_TEST * IOTEST_MAX_TYPE)
69 
70 enum {
71     IOTEST_ACCESS_NAME,
72     IOTEST_ACCESS_DATA,
73     IOTEST_ACCESS_MAX,
74 };
75 
76 #define IOTEST_ACCESS_TYPE uint8_t
77 #define IOTEST_ACCESS_WIDTH (sizeof(uint8_t))
78 
79 typedef struct PCITestDevState {
80     /*< private >*/
81     PCIDevice parent_obj;
82     /*< public >*/
83 
84     MemoryRegion mmio;
85     MemoryRegion portio;
86     IOTest *tests;
87     int current;
88 
89     uint64_t membar_size;
90     MemoryRegion membar;
91 } PCITestDevState;
92 
93 #define TYPE_PCI_TEST_DEV "pci-testdev"
94 
95 #define PCI_TEST_DEV(obj) \
96     OBJECT_CHECK(PCITestDevState, (obj), TYPE_PCI_TEST_DEV)
97 
98 #define IOTEST_IS_MEM(i) (strcmp(IOTEST_TYPE(i), "portio"))
99 #define IOTEST_REGION(d, i) (IOTEST_IS_MEM(i) ?  &(d)->mmio : &(d)->portio)
100 #define IOTEST_SIZE(i) (IOTEST_IS_MEM(i) ? IOTEST_MEMSIZE : IOTEST_IOSIZE)
101 #define IOTEST_PCI_BAR(i) (IOTEST_IS_MEM(i) ? PCI_BASE_ADDRESS_SPACE_MEMORY : \
102                            PCI_BASE_ADDRESS_SPACE_IO)
103 
104 static int pci_testdev_start(IOTest *test)
105 {
106     test->hdr->count = 0;
107     if (!test->hasnotifier) {
108         return 0;
109     }
110     event_notifier_test_and_clear(&test->notifier);
111     memory_region_add_eventfd(test->mr,
112                               le32_to_cpu(test->hdr->offset),
113                               test->size,
114                               test->match_data,
115                               test->hdr->data,
116                               &test->notifier);
117     return 0;
118 }
119 
120 static void pci_testdev_stop(IOTest *test)
121 {
122     if (!test->hasnotifier) {
123         return;
124     }
125     memory_region_del_eventfd(test->mr,
126                               le32_to_cpu(test->hdr->offset),
127                               test->size,
128                               test->match_data,
129                               test->hdr->data,
130                               &test->notifier);
131 }
132 
133 static void
134 pci_testdev_reset(PCITestDevState *d)
135 {
136     if (d->current == -1) {
137         return;
138     }
139     pci_testdev_stop(&d->tests[d->current]);
140     d->current = -1;
141 }
142 
143 static void pci_testdev_inc(IOTest *test, unsigned inc)
144 {
145     uint32_t c = le32_to_cpu(test->hdr->count);
146     test->hdr->count = cpu_to_le32(c + inc);
147 }
148 
149 static void
150 pci_testdev_write(void *opaque, hwaddr addr, uint64_t val,
151                   unsigned size, int type)
152 {
153     PCITestDevState *d = opaque;
154     IOTest *test;
155     int t, r;
156 
157     if (addr == offsetof(PCITestDevHdr, test)) {
158         pci_testdev_reset(d);
159         if (val >= IOTEST_MAX_TEST) {
160             return;
161         }
162         t = type * IOTEST_MAX_TEST + val;
163         r = pci_testdev_start(&d->tests[t]);
164         if (r < 0) {
165             return;
166         }
167         d->current = t;
168         return;
169     }
170     if (d->current < 0) {
171         return;
172     }
173     test = &d->tests[d->current];
174     if (addr != le32_to_cpu(test->hdr->offset)) {
175         return;
176     }
177     if (test->match_data && test->size != size) {
178         return;
179     }
180     if (test->match_data && val != test->hdr->data) {
181         return;
182     }
183     pci_testdev_inc(test, 1);
184 }
185 
186 static uint64_t
187 pci_testdev_read(void *opaque, hwaddr addr, unsigned size)
188 {
189     PCITestDevState *d = opaque;
190     const char *buf;
191     IOTest *test;
192     if (d->current < 0) {
193         return 0;
194     }
195     test = &d->tests[d->current];
196     buf = (const char *)test->hdr;
197     if (addr + size >= test->bufsize) {
198         return 0;
199     }
200     if (test->hasnotifier) {
201         event_notifier_test_and_clear(&test->notifier);
202     }
203     return buf[addr];
204 }
205 
206 static void
207 pci_testdev_mmio_write(void *opaque, hwaddr addr, uint64_t val,
208                        unsigned size)
209 {
210     pci_testdev_write(opaque, addr, val, size, 0);
211 }
212 
213 static void
214 pci_testdev_pio_write(void *opaque, hwaddr addr, uint64_t val,
215                        unsigned size)
216 {
217     pci_testdev_write(opaque, addr, val, size, 1);
218 }
219 
220 static const MemoryRegionOps pci_testdev_mmio_ops = {
221     .read = pci_testdev_read,
222     .write = pci_testdev_mmio_write,
223     .endianness = DEVICE_LITTLE_ENDIAN,
224     .impl = {
225         .min_access_size = 1,
226         .max_access_size = 1,
227     },
228 };
229 
230 static const MemoryRegionOps pci_testdev_pio_ops = {
231     .read = pci_testdev_read,
232     .write = pci_testdev_pio_write,
233     .endianness = DEVICE_LITTLE_ENDIAN,
234     .impl = {
235         .min_access_size = 1,
236         .max_access_size = 1,
237     },
238 };
239 
240 static void pci_testdev_realize(PCIDevice *pci_dev, Error **errp)
241 {
242     PCITestDevState *d = PCI_TEST_DEV(pci_dev);
243     uint8_t *pci_conf;
244     char *name;
245     int r, i;
246     bool fastmmio = kvm_ioeventfd_any_length_enabled();
247 
248     pci_conf = pci_dev->config;
249 
250     pci_conf[PCI_INTERRUPT_PIN] = 0; /* no interrupt pin */
251 
252     memory_region_init_io(&d->mmio, OBJECT(d), &pci_testdev_mmio_ops, d,
253                           "pci-testdev-mmio", IOTEST_MEMSIZE * 2);
254     memory_region_init_io(&d->portio, OBJECT(d), &pci_testdev_pio_ops, d,
255                           "pci-testdev-portio", IOTEST_IOSIZE * 2);
256     pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &d->mmio);
257     pci_register_bar(pci_dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &d->portio);
258 
259     if (d->membar_size) {
260         memory_region_init(&d->membar, OBJECT(d), "pci-testdev-membar",
261                            d->membar_size);
262         pci_register_bar(pci_dev, 2,
263                          PCI_BASE_ADDRESS_SPACE_MEMORY |
264                          PCI_BASE_ADDRESS_MEM_PREFETCH |
265                          PCI_BASE_ADDRESS_MEM_TYPE_64,
266                          &d->membar);
267     }
268 
269     d->current = -1;
270     d->tests = g_malloc0(IOTEST_MAX * sizeof *d->tests);
271     for (i = 0; i < IOTEST_MAX; ++i) {
272         IOTest *test = &d->tests[i];
273         name = g_strdup_printf("%s-%s", IOTEST_TYPE(i), IOTEST_TEST(i));
274         test->bufsize = sizeof(PCITestDevHdr) + strlen(name) + 1;
275         test->hdr = g_malloc0(test->bufsize);
276         memcpy(test->hdr->name, name, strlen(name) + 1);
277         g_free(name);
278         test->hdr->offset = cpu_to_le32(IOTEST_SIZE(i) + i * IOTEST_ACCESS_WIDTH);
279         test->match_data = strcmp(IOTEST_TEST(i), "wildcard-eventfd");
280         if (fastmmio && IOTEST_IS_MEM(i) && !test->match_data) {
281             test->size = 0;
282         } else {
283             test->size = IOTEST_ACCESS_WIDTH;
284         }
285         test->hdr->test = i;
286         test->hdr->data = test->match_data ? IOTEST_DATAMATCH : IOTEST_NOMATCH;
287         test->hdr->width = IOTEST_ACCESS_WIDTH;
288         test->mr = IOTEST_REGION(d, i);
289         if (!strcmp(IOTEST_TEST(i), "no-eventfd")) {
290             test->hasnotifier = false;
291             continue;
292         }
293         r = event_notifier_init(&test->notifier, 0);
294         assert(r >= 0);
295         test->hasnotifier = true;
296     }
297 }
298 
299 static void
300 pci_testdev_uninit(PCIDevice *dev)
301 {
302     PCITestDevState *d = PCI_TEST_DEV(dev);
303     int i;
304 
305     pci_testdev_reset(d);
306     for (i = 0; i < IOTEST_MAX; ++i) {
307         if (d->tests[i].hasnotifier) {
308             event_notifier_cleanup(&d->tests[i].notifier);
309         }
310         g_free(d->tests[i].hdr);
311     }
312     g_free(d->tests);
313 }
314 
315 static void qdev_pci_testdev_reset(DeviceState *dev)
316 {
317     PCITestDevState *d = PCI_TEST_DEV(dev);
318     pci_testdev_reset(d);
319 }
320 
321 static Property pci_testdev_properties[] = {
322     DEFINE_PROP_SIZE("membar", PCITestDevState, membar_size, 0),
323     DEFINE_PROP_END_OF_LIST(),
324 };
325 
326 static void pci_testdev_class_init(ObjectClass *klass, void *data)
327 {
328     DeviceClass *dc = DEVICE_CLASS(klass);
329     PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
330 
331     k->realize = pci_testdev_realize;
332     k->exit = pci_testdev_uninit;
333     k->vendor_id = PCI_VENDOR_ID_REDHAT;
334     k->device_id = PCI_DEVICE_ID_REDHAT_TEST;
335     k->revision = 0x00;
336     k->class_id = PCI_CLASS_OTHERS;
337     dc->desc = "PCI Test Device";
338     set_bit(DEVICE_CATEGORY_MISC, dc->categories);
339     dc->reset = qdev_pci_testdev_reset;
340     dc->props = pci_testdev_properties;
341 }
342 
343 static const TypeInfo pci_testdev_info = {
344     .name          = TYPE_PCI_TEST_DEV,
345     .parent        = TYPE_PCI_DEVICE,
346     .instance_size = sizeof(PCITestDevState),
347     .class_init    = pci_testdev_class_init,
348     .interfaces = (InterfaceInfo[]) {
349         { INTERFACE_CONVENTIONAL_PCI_DEVICE },
350         { },
351     },
352 };
353 
354 static void pci_testdev_register_types(void)
355 {
356     type_register_static(&pci_testdev_info);
357 }
358 
359 type_init(pci_testdev_register_types)
360