]>
Commit | Line | Data |
---|---|---|
c4efe1ca AL |
1 | /* |
2 | * libqos PCI bindings | |
3 | * | |
4 | * Copyright IBM, Corp. 2012-2013 | |
5 | * | |
6 | * Authors: | |
7 | * Anthony Liguori <aliguori@us.ibm.com> | |
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 | ||
681c28a3 | 13 | #include "qemu/osdep.h" |
c4efe1ca AL |
14 | #include "libqos/pci.h" |
15 | ||
16 | #include "hw/pci/pci_regs.h" | |
c4efe1ca | 17 | |
c4efe1ca AL |
18 | void qpci_device_foreach(QPCIBus *bus, int vendor_id, int device_id, |
19 | void (*func)(QPCIDevice *dev, int devfn, void *data), | |
20 | void *data) | |
21 | { | |
22 | int slot; | |
23 | ||
24 | for (slot = 0; slot < 32; slot++) { | |
25 | int fn; | |
26 | ||
27 | for (fn = 0; fn < 8; fn++) { | |
28 | QPCIDevice *dev; | |
29 | ||
30 | dev = qpci_device_find(bus, QPCI_DEVFN(slot, fn)); | |
31 | if (!dev) { | |
32 | continue; | |
33 | } | |
34 | ||
35 | if (vendor_id != -1 && | |
36 | qpci_config_readw(dev, PCI_VENDOR_ID) != vendor_id) { | |
ea53854a | 37 | g_free(dev); |
c4efe1ca AL |
38 | continue; |
39 | } | |
40 | ||
41 | if (device_id != -1 && | |
42 | qpci_config_readw(dev, PCI_DEVICE_ID) != device_id) { | |
ea53854a | 43 | g_free(dev); |
c4efe1ca AL |
44 | continue; |
45 | } | |
46 | ||
47 | func(dev, QPCI_DEVFN(slot, fn), data); | |
48 | } | |
49 | } | |
50 | } | |
51 | ||
52 | QPCIDevice *qpci_device_find(QPCIBus *bus, int devfn) | |
53 | { | |
54 | QPCIDevice *dev; | |
55 | ||
56 | dev = g_malloc0(sizeof(*dev)); | |
57 | dev->bus = bus; | |
58 | dev->devfn = devfn; | |
59 | ||
60 | if (qpci_config_readw(dev, PCI_VENDOR_ID) == 0xFFFF) { | |
61 | g_free(dev); | |
62 | return NULL; | |
63 | } | |
64 | ||
65 | return dev; | |
66 | } | |
67 | ||
68 | void qpci_device_enable(QPCIDevice *dev) | |
69 | { | |
70 | uint16_t cmd; | |
71 | ||
72 | /* FIXME -- does this need to be a bus callout? */ | |
73 | cmd = qpci_config_readw(dev, PCI_COMMAND); | |
9f0332b8 | 74 | cmd |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; |
c4efe1ca | 75 | qpci_config_writew(dev, PCI_COMMAND, cmd); |
96d6d3ba JS |
76 | |
77 | /* Verify the bits are now set. */ | |
78 | cmd = qpci_config_readw(dev, PCI_COMMAND); | |
79 | g_assert_cmphex(cmd & PCI_COMMAND_IO, ==, PCI_COMMAND_IO); | |
80 | g_assert_cmphex(cmd & PCI_COMMAND_MEMORY, ==, PCI_COMMAND_MEMORY); | |
81 | g_assert_cmphex(cmd & PCI_COMMAND_MASTER, ==, PCI_COMMAND_MASTER); | |
c4efe1ca AL |
82 | } |
83 | ||
58368113 MM |
84 | uint8_t qpci_find_capability(QPCIDevice *dev, uint8_t id) |
85 | { | |
86 | uint8_t cap; | |
87 | uint8_t addr = qpci_config_readb(dev, PCI_CAPABILITY_LIST); | |
88 | ||
89 | do { | |
90 | cap = qpci_config_readb(dev, addr); | |
91 | if (cap != id) { | |
92 | addr = qpci_config_readb(dev, addr + PCI_CAP_LIST_NEXT); | |
93 | } | |
94 | } while (cap != id && addr != 0); | |
95 | ||
96 | return addr; | |
97 | } | |
98 | ||
99 | void qpci_msix_enable(QPCIDevice *dev) | |
100 | { | |
101 | uint8_t addr; | |
102 | uint16_t val; | |
103 | uint32_t table; | |
104 | uint8_t bir_table; | |
105 | uint8_t bir_pba; | |
106 | void *offset; | |
107 | ||
108 | addr = qpci_find_capability(dev, PCI_CAP_ID_MSIX); | |
109 | g_assert_cmphex(addr, !=, 0); | |
110 | ||
111 | val = qpci_config_readw(dev, addr + PCI_MSIX_FLAGS); | |
112 | qpci_config_writew(dev, addr + PCI_MSIX_FLAGS, val | PCI_MSIX_FLAGS_ENABLE); | |
113 | ||
114 | table = qpci_config_readl(dev, addr + PCI_MSIX_TABLE); | |
115 | bir_table = table & PCI_MSIX_FLAGS_BIRMASK; | |
116 | offset = qpci_iomap(dev, bir_table, NULL); | |
117 | dev->msix_table = offset + (table & ~PCI_MSIX_FLAGS_BIRMASK); | |
118 | ||
119 | table = qpci_config_readl(dev, addr + PCI_MSIX_PBA); | |
120 | bir_pba = table & PCI_MSIX_FLAGS_BIRMASK; | |
121 | if (bir_pba != bir_table) { | |
122 | offset = qpci_iomap(dev, bir_pba, NULL); | |
123 | } | |
124 | dev->msix_pba = offset + (table & ~PCI_MSIX_FLAGS_BIRMASK); | |
125 | ||
126 | g_assert(dev->msix_table != NULL); | |
127 | g_assert(dev->msix_pba != NULL); | |
128 | dev->msix_enabled = true; | |
129 | } | |
130 | ||
131 | void qpci_msix_disable(QPCIDevice *dev) | |
132 | { | |
133 | uint8_t addr; | |
134 | uint16_t val; | |
135 | ||
136 | g_assert(dev->msix_enabled); | |
137 | addr = qpci_find_capability(dev, PCI_CAP_ID_MSIX); | |
138 | g_assert_cmphex(addr, !=, 0); | |
139 | val = qpci_config_readw(dev, addr + PCI_MSIX_FLAGS); | |
140 | qpci_config_writew(dev, addr + PCI_MSIX_FLAGS, | |
141 | val & ~PCI_MSIX_FLAGS_ENABLE); | |
142 | ||
143 | qpci_iounmap(dev, dev->msix_table); | |
144 | qpci_iounmap(dev, dev->msix_pba); | |
145 | dev->msix_enabled = 0; | |
146 | dev->msix_table = NULL; | |
147 | dev->msix_pba = NULL; | |
148 | } | |
149 | ||
150 | bool qpci_msix_pending(QPCIDevice *dev, uint16_t entry) | |
151 | { | |
152 | uint32_t pba_entry; | |
153 | uint8_t bit_n = entry % 32; | |
154 | void *addr = dev->msix_pba + (entry / 32) * PCI_MSIX_ENTRY_SIZE / 4; | |
155 | ||
156 | g_assert(dev->msix_enabled); | |
157 | pba_entry = qpci_io_readl(dev, addr); | |
158 | qpci_io_writel(dev, addr, pba_entry & ~(1 << bit_n)); | |
159 | return (pba_entry & (1 << bit_n)) != 0; | |
160 | } | |
161 | ||
162 | bool qpci_msix_masked(QPCIDevice *dev, uint16_t entry) | |
163 | { | |
164 | uint8_t addr; | |
165 | uint16_t val; | |
166 | void *vector_addr = dev->msix_table + (entry * PCI_MSIX_ENTRY_SIZE); | |
167 | ||
168 | g_assert(dev->msix_enabled); | |
169 | addr = qpci_find_capability(dev, PCI_CAP_ID_MSIX); | |
170 | g_assert_cmphex(addr, !=, 0); | |
171 | val = qpci_config_readw(dev, addr + PCI_MSIX_FLAGS); | |
172 | ||
173 | if (val & PCI_MSIX_FLAGS_MASKALL) { | |
174 | return true; | |
175 | } else { | |
176 | return (qpci_io_readl(dev, vector_addr + PCI_MSIX_ENTRY_VECTOR_CTRL) | |
177 | & PCI_MSIX_ENTRY_CTRL_MASKBIT) != 0; | |
178 | } | |
179 | } | |
180 | ||
181 | uint16_t qpci_msix_table_size(QPCIDevice *dev) | |
182 | { | |
183 | uint8_t addr; | |
184 | uint16_t control; | |
185 | ||
186 | addr = qpci_find_capability(dev, PCI_CAP_ID_MSIX); | |
187 | g_assert_cmphex(addr, !=, 0); | |
188 | ||
189 | control = qpci_config_readw(dev, addr + PCI_MSIX_FLAGS); | |
190 | return (control & PCI_MSIX_FLAGS_QSIZE) + 1; | |
191 | } | |
192 | ||
c4efe1ca AL |
193 | uint8_t qpci_config_readb(QPCIDevice *dev, uint8_t offset) |
194 | { | |
195 | return dev->bus->config_readb(dev->bus, dev->devfn, offset); | |
196 | } | |
197 | ||
198 | uint16_t qpci_config_readw(QPCIDevice *dev, uint8_t offset) | |
199 | { | |
200 | return dev->bus->config_readw(dev->bus, dev->devfn, offset); | |
201 | } | |
202 | ||
203 | uint32_t qpci_config_readl(QPCIDevice *dev, uint8_t offset) | |
204 | { | |
205 | return dev->bus->config_readl(dev->bus, dev->devfn, offset); | |
206 | } | |
207 | ||
208 | ||
209 | void qpci_config_writeb(QPCIDevice *dev, uint8_t offset, uint8_t value) | |
210 | { | |
211 | dev->bus->config_writeb(dev->bus, dev->devfn, offset, value); | |
212 | } | |
213 | ||
214 | void qpci_config_writew(QPCIDevice *dev, uint8_t offset, uint16_t value) | |
215 | { | |
216 | dev->bus->config_writew(dev->bus, dev->devfn, offset, value); | |
217 | } | |
218 | ||
219 | void qpci_config_writel(QPCIDevice *dev, uint8_t offset, uint32_t value) | |
220 | { | |
ad489e93 | 221 | dev->bus->config_writel(dev->bus, dev->devfn, offset, value); |
c4efe1ca AL |
222 | } |
223 | ||
224 | ||
225 | uint8_t qpci_io_readb(QPCIDevice *dev, void *data) | |
226 | { | |
227 | return dev->bus->io_readb(dev->bus, data); | |
228 | } | |
229 | ||
230 | uint16_t qpci_io_readw(QPCIDevice *dev, void *data) | |
231 | { | |
232 | return dev->bus->io_readw(dev->bus, data); | |
233 | } | |
234 | ||
235 | uint32_t qpci_io_readl(QPCIDevice *dev, void *data) | |
236 | { | |
237 | return dev->bus->io_readl(dev->bus, data); | |
238 | } | |
239 | ||
240 | ||
241 | void qpci_io_writeb(QPCIDevice *dev, void *data, uint8_t value) | |
242 | { | |
243 | dev->bus->io_writeb(dev->bus, data, value); | |
244 | } | |
245 | ||
246 | void qpci_io_writew(QPCIDevice *dev, void *data, uint16_t value) | |
247 | { | |
248 | dev->bus->io_writew(dev->bus, data, value); | |
249 | } | |
250 | ||
251 | void qpci_io_writel(QPCIDevice *dev, void *data, uint32_t value) | |
252 | { | |
253 | dev->bus->io_writel(dev->bus, data, value); | |
254 | } | |
255 | ||
6ce7100e | 256 | void *qpci_iomap(QPCIDevice *dev, int barno, uint64_t *sizeptr) |
c4efe1ca | 257 | { |
6ce7100e | 258 | return dev->bus->iomap(dev->bus, dev, barno, sizeptr); |
c4efe1ca AL |
259 | } |
260 | ||
261 | void qpci_iounmap(QPCIDevice *dev, void *data) | |
262 | { | |
263 | dev->bus->iounmap(dev->bus, data); | |
264 | } | |
265 | ||
cf716b31 LV |
266 | void qpci_plug_device_test(const char *driver, const char *id, |
267 | uint8_t slot, const char *opts) | |
268 | { | |
269 | QDict *response; | |
270 | char *cmd; | |
271 | ||
272 | cmd = g_strdup_printf("{'execute': 'device_add'," | |
273 | " 'arguments': {" | |
274 | " 'driver': '%s'," | |
275 | " 'addr': '%d'," | |
276 | " %s%s" | |
277 | " 'id': '%s'" | |
278 | "}}", driver, slot, | |
279 | opts ? opts : "", opts ? "," : "", | |
280 | id); | |
281 | response = qmp(cmd); | |
282 | g_free(cmd); | |
283 | g_assert(response); | |
284 | g_assert(!qdict_haskey(response, "error")); | |
285 | QDECREF(response); | |
286 | } |