]> Git Repo - qemu.git/blob - tests/libqos/pci.c
libqos: Implement mmio accessors in terms of mem{read,write}
[qemu.git] / tests / libqos / pci.c
1 /*
2  * libqos PCI bindings
3  *
4  * Copyright IBM, Corp. 2012-2013
5  *
6  * Authors:
7  *  Anthony Liguori   <[email protected]>
8  *
9  * This work is licensed under the terms of the GNU GPL, version 2 or later.
10  * See the COPYING file in the top-level directory.
11  */
12
13 #include "qemu/osdep.h"
14 #include "libqos/pci.h"
15
16 #include "hw/pci/pci_regs.h"
17 #include "qemu/host-utils.h"
18
19 void qpci_device_foreach(QPCIBus *bus, int vendor_id, int device_id,
20                          void (*func)(QPCIDevice *dev, int devfn, void *data),
21                          void *data)
22 {
23     int slot;
24
25     for (slot = 0; slot < 32; slot++) {
26         int fn;
27
28         for (fn = 0; fn < 8; fn++) {
29             QPCIDevice *dev;
30
31             dev = qpci_device_find(bus, QPCI_DEVFN(slot, fn));
32             if (!dev) {
33                 continue;
34             }
35
36             if (vendor_id != -1 &&
37                 qpci_config_readw(dev, PCI_VENDOR_ID) != vendor_id) {
38                 g_free(dev);
39                 continue;
40             }
41
42             if (device_id != -1 &&
43                 qpci_config_readw(dev, PCI_DEVICE_ID) != device_id) {
44                 g_free(dev);
45                 continue;
46             }
47
48             func(dev, QPCI_DEVFN(slot, fn), data);
49         }
50     }
51 }
52
53 QPCIDevice *qpci_device_find(QPCIBus *bus, int devfn)
54 {
55     QPCIDevice *dev;
56
57     dev = g_malloc0(sizeof(*dev));
58     dev->bus = bus;
59     dev->devfn = devfn;
60
61     if (qpci_config_readw(dev, PCI_VENDOR_ID) == 0xFFFF) {
62         g_free(dev);
63         return NULL;
64     }
65
66     return dev;
67 }
68
69 void qpci_device_enable(QPCIDevice *dev)
70 {
71     uint16_t cmd;
72
73     /* FIXME -- does this need to be a bus callout? */
74     cmd = qpci_config_readw(dev, PCI_COMMAND);
75     cmd |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;
76     qpci_config_writew(dev, PCI_COMMAND, cmd);
77
78     /* Verify the bits are now set. */
79     cmd = qpci_config_readw(dev, PCI_COMMAND);
80     g_assert_cmphex(cmd & PCI_COMMAND_IO, ==, PCI_COMMAND_IO);
81     g_assert_cmphex(cmd & PCI_COMMAND_MEMORY, ==, PCI_COMMAND_MEMORY);
82     g_assert_cmphex(cmd & PCI_COMMAND_MASTER, ==, PCI_COMMAND_MASTER);
83 }
84
85 uint8_t qpci_find_capability(QPCIDevice *dev, uint8_t id)
86 {
87     uint8_t cap;
88     uint8_t addr = qpci_config_readb(dev, PCI_CAPABILITY_LIST);
89
90     do {
91         cap = qpci_config_readb(dev, addr);
92         if (cap != id) {
93             addr = qpci_config_readb(dev, addr + PCI_CAP_LIST_NEXT);
94         }
95     } while (cap != id && addr != 0);
96
97     return addr;
98 }
99
100 void qpci_msix_enable(QPCIDevice *dev)
101 {
102     uint8_t addr;
103     uint16_t val;
104     uint32_t table;
105     uint8_t bir_table;
106     uint8_t bir_pba;
107     void *offset;
108
109     addr = qpci_find_capability(dev, PCI_CAP_ID_MSIX);
110     g_assert_cmphex(addr, !=, 0);
111
112     val = qpci_config_readw(dev, addr + PCI_MSIX_FLAGS);
113     qpci_config_writew(dev, addr + PCI_MSIX_FLAGS, val | PCI_MSIX_FLAGS_ENABLE);
114
115     table = qpci_config_readl(dev, addr + PCI_MSIX_TABLE);
116     bir_table = table & PCI_MSIX_FLAGS_BIRMASK;
117     offset = qpci_iomap(dev, bir_table, NULL);
118     dev->msix_table = offset + (table & ~PCI_MSIX_FLAGS_BIRMASK);
119
120     table = qpci_config_readl(dev, addr + PCI_MSIX_PBA);
121     bir_pba = table & PCI_MSIX_FLAGS_BIRMASK;
122     if (bir_pba != bir_table) {
123         offset = qpci_iomap(dev, bir_pba, NULL);
124     }
125     dev->msix_pba = offset + (table & ~PCI_MSIX_FLAGS_BIRMASK);
126
127     g_assert(dev->msix_table != NULL);
128     g_assert(dev->msix_pba != NULL);
129     dev->msix_enabled = true;
130 }
131
132 void qpci_msix_disable(QPCIDevice *dev)
133 {
134     uint8_t addr;
135     uint16_t val;
136
137     g_assert(dev->msix_enabled);
138     addr = qpci_find_capability(dev, PCI_CAP_ID_MSIX);
139     g_assert_cmphex(addr, !=, 0);
140     val = qpci_config_readw(dev, addr + PCI_MSIX_FLAGS);
141     qpci_config_writew(dev, addr + PCI_MSIX_FLAGS,
142                                                 val & ~PCI_MSIX_FLAGS_ENABLE);
143
144     qpci_iounmap(dev, dev->msix_table);
145     qpci_iounmap(dev, dev->msix_pba);
146     dev->msix_enabled = 0;
147     dev->msix_table = NULL;
148     dev->msix_pba = NULL;
149 }
150
151 bool qpci_msix_pending(QPCIDevice *dev, uint16_t entry)
152 {
153     uint32_t pba_entry;
154     uint8_t bit_n = entry % 32;
155     void *addr = dev->msix_pba + (entry / 32) * PCI_MSIX_ENTRY_SIZE / 4;
156
157     g_assert(dev->msix_enabled);
158     pba_entry = qpci_io_readl(dev, addr);
159     qpci_io_writel(dev, addr, pba_entry & ~(1 << bit_n));
160     return (pba_entry & (1 << bit_n)) != 0;
161 }
162
163 bool qpci_msix_masked(QPCIDevice *dev, uint16_t entry)
164 {
165     uint8_t addr;
166     uint16_t val;
167     void *vector_addr = dev->msix_table + (entry * PCI_MSIX_ENTRY_SIZE);
168
169     g_assert(dev->msix_enabled);
170     addr = qpci_find_capability(dev, PCI_CAP_ID_MSIX);
171     g_assert_cmphex(addr, !=, 0);
172     val = qpci_config_readw(dev, addr + PCI_MSIX_FLAGS);
173
174     if (val & PCI_MSIX_FLAGS_MASKALL) {
175         return true;
176     } else {
177         return (qpci_io_readl(dev, vector_addr + PCI_MSIX_ENTRY_VECTOR_CTRL)
178                                             & PCI_MSIX_ENTRY_CTRL_MASKBIT) != 0;
179     }
180 }
181
182 uint16_t qpci_msix_table_size(QPCIDevice *dev)
183 {
184     uint8_t addr;
185     uint16_t control;
186
187     addr = qpci_find_capability(dev, PCI_CAP_ID_MSIX);
188     g_assert_cmphex(addr, !=, 0);
189
190     control = qpci_config_readw(dev, addr + PCI_MSIX_FLAGS);
191     return (control & PCI_MSIX_FLAGS_QSIZE) + 1;
192 }
193
194 uint8_t qpci_config_readb(QPCIDevice *dev, uint8_t offset)
195 {
196     return dev->bus->config_readb(dev->bus, dev->devfn, offset);
197 }
198
199 uint16_t qpci_config_readw(QPCIDevice *dev, uint8_t offset)
200 {
201     return dev->bus->config_readw(dev->bus, dev->devfn, offset);
202 }
203
204 uint32_t qpci_config_readl(QPCIDevice *dev, uint8_t offset)
205 {
206     return dev->bus->config_readl(dev->bus, dev->devfn, offset);
207 }
208
209
210 void qpci_config_writeb(QPCIDevice *dev, uint8_t offset, uint8_t value)
211 {
212     dev->bus->config_writeb(dev->bus, dev->devfn, offset, value);
213 }
214
215 void qpci_config_writew(QPCIDevice *dev, uint8_t offset, uint16_t value)
216 {
217     dev->bus->config_writew(dev->bus, dev->devfn, offset, value);
218 }
219
220 void qpci_config_writel(QPCIDevice *dev, uint8_t offset, uint32_t value)
221 {
222     dev->bus->config_writel(dev->bus, dev->devfn, offset, value);
223 }
224
225
226 uint8_t qpci_io_readb(QPCIDevice *dev, void *data)
227 {
228     uintptr_t addr = (uintptr_t)data;
229
230     if (addr < QPCI_PIO_LIMIT) {
231         return dev->bus->pio_readb(dev->bus, addr);
232     } else {
233         uint8_t val;
234         dev->bus->memread(dev->bus, addr, &val, sizeof(val));
235         return val;
236     }
237 }
238
239 uint16_t qpci_io_readw(QPCIDevice *dev, void *data)
240 {
241     uintptr_t addr = (uintptr_t)data;
242
243     if (addr < QPCI_PIO_LIMIT) {
244         return dev->bus->pio_readw(dev->bus, addr);
245     } else {
246         uint16_t val;
247         dev->bus->memread(dev->bus, addr, &val, sizeof(val));
248         return le16_to_cpu(val);
249     }
250 }
251
252 uint32_t qpci_io_readl(QPCIDevice *dev, void *data)
253 {
254     uintptr_t addr = (uintptr_t)data;
255
256     if (addr < QPCI_PIO_LIMIT) {
257         return dev->bus->pio_readl(dev->bus, addr);
258     } else {
259         uint32_t val;
260         dev->bus->memread(dev->bus, addr, &val, sizeof(val));
261         return le32_to_cpu(val);
262     }
263 }
264
265 void qpci_io_writeb(QPCIDevice *dev, void *data, uint8_t value)
266 {
267     uintptr_t addr = (uintptr_t)data;
268
269     if (addr < QPCI_PIO_LIMIT) {
270         dev->bus->pio_writeb(dev->bus, addr, value);
271     } else {
272         dev->bus->memwrite(dev->bus, addr, &value, sizeof(value));
273     }
274 }
275
276 void qpci_io_writew(QPCIDevice *dev, void *data, uint16_t value)
277 {
278     uintptr_t addr = (uintptr_t)data;
279
280     if (addr < QPCI_PIO_LIMIT) {
281         dev->bus->pio_writew(dev->bus, addr, value);
282     } else {
283         value = cpu_to_le16(value);
284         dev->bus->memwrite(dev->bus, addr, &value, sizeof(value));
285     }
286 }
287
288 void qpci_io_writel(QPCIDevice *dev, void *data, uint32_t value)
289 {
290     uintptr_t addr = (uintptr_t)data;
291
292     if (addr < QPCI_PIO_LIMIT) {
293         dev->bus->pio_writel(dev->bus, addr, value);
294     } else {
295         value = cpu_to_le32(value);
296         dev->bus->memwrite(dev->bus, addr, &value, sizeof(value));
297     }
298 }
299
300 void qpci_memread(QPCIDevice *dev, void *data, void *buf, size_t len)
301 {
302     uintptr_t addr = (uintptr_t)data;
303
304     g_assert(addr >= QPCI_PIO_LIMIT);
305     dev->bus->memread(dev->bus, addr, buf, len);
306 }
307
308 void qpci_memwrite(QPCIDevice *dev, void *data, const void *buf, size_t len)
309 {
310     uintptr_t addr = (uintptr_t)data;
311
312     g_assert(addr >= QPCI_PIO_LIMIT);
313     dev->bus->memwrite(dev->bus, addr, buf, len);
314 }
315
316 void *qpci_iomap(QPCIDevice *dev, int barno, uint64_t *sizeptr)
317 {
318     QPCIBus *bus = dev->bus;
319     static const int bar_reg_map[] = {
320         PCI_BASE_ADDRESS_0, PCI_BASE_ADDRESS_1, PCI_BASE_ADDRESS_2,
321         PCI_BASE_ADDRESS_3, PCI_BASE_ADDRESS_4, PCI_BASE_ADDRESS_5,
322     };
323     int bar_reg;
324     uint32_t addr, size;
325     uint32_t io_type;
326     uint64_t loc;
327
328     g_assert(barno >= 0 && barno <= 5);
329     bar_reg = bar_reg_map[barno];
330
331     qpci_config_writel(dev, bar_reg, 0xFFFFFFFF);
332     addr = qpci_config_readl(dev, bar_reg);
333
334     io_type = addr & PCI_BASE_ADDRESS_SPACE;
335     if (io_type == PCI_BASE_ADDRESS_SPACE_IO) {
336         addr &= PCI_BASE_ADDRESS_IO_MASK;
337     } else {
338         addr &= PCI_BASE_ADDRESS_MEM_MASK;
339     }
340
341     g_assert(addr); /* Must have *some* size bits */
342
343     size = 1U << ctz32(addr);
344     if (sizeptr) {
345         *sizeptr = size;
346     }
347
348     if (io_type == PCI_BASE_ADDRESS_SPACE_IO) {
349         loc = QEMU_ALIGN_UP(bus->pio_alloc_ptr, size);
350
351         g_assert(loc >= bus->pio_alloc_ptr);
352         g_assert(loc + size <= QPCI_PIO_LIMIT); /* Keep PIO below 64kiB */
353
354         bus->pio_alloc_ptr = loc + size;
355
356         qpci_config_writel(dev, bar_reg, loc | PCI_BASE_ADDRESS_SPACE_IO);
357     } else {
358         loc = QEMU_ALIGN_UP(bus->mmio_alloc_ptr, size);
359
360         /* Check for space */
361         g_assert(loc >= bus->mmio_alloc_ptr);
362         g_assert(loc + size <= bus->mmio_limit);
363
364         bus->mmio_alloc_ptr = loc + size;
365
366         qpci_config_writel(dev, bar_reg, loc);
367     }
368
369     return (void *)(uintptr_t)loc;
370 }
371
372 void qpci_iounmap(QPCIDevice *dev, void *data)
373 {
374     /* FIXME */
375 }
376
377 void *qpci_legacy_iomap(QPCIDevice *dev, uint16_t addr)
378 {
379     return (void *)(uintptr_t)addr;
380 }
381
382 void qpci_plug_device_test(const char *driver, const char *id,
383                            uint8_t slot, const char *opts)
384 {
385     QDict *response;
386     char *cmd;
387
388     cmd = g_strdup_printf("{'execute': 'device_add',"
389                           " 'arguments': {"
390                           "   'driver': '%s',"
391                           "   'addr': '%d',"
392                           "   %s%s"
393                           "   'id': '%s'"
394                           "}}", driver, slot,
395                           opts ? opts : "", opts ? "," : "",
396                           id);
397     response = qmp(cmd);
398     g_free(cmd);
399     g_assert(response);
400     g_assert(!qdict_haskey(response, "error"));
401     QDECREF(response);
402 }
This page took 0.044237 seconds and 4 git commands to generate.