root / hw / misc / pci-testdev.c @ a8aec295
History | View | Annotate | Download (8.8 kB)
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, &pci_testdev_mmio_ops, d, |
240 |
"pci-testdev-mmio", IOTEST_MEMSIZE * 2); |
241 |
memory_region_init_io(&d->portio, &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) |