1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | #include "qemu-common.h" |
10 | #include "xen-host-pci-device.h" |
11 | |
12 | #define XEN_HOST_PCI_MAX_EXT_CAP((0x1000 - 0x100) / (4 + 4)) \ |
13 | ((PCIE_CONFIG_SPACE_SIZE0x1000 - PCI_CONFIG_SPACE_SIZE0x100) / (PCI_CAP_SIZEOF4 + 4)) |
14 | |
15 | #ifdef XEN_HOST_PCI_DEVICE_DEBUG |
16 | # define XEN_HOST_PCI_LOG(f, a...)(void)0 fprintf(stderrstderr, "%s: " f, __func__, ##a) |
17 | #else |
18 | # define XEN_HOST_PCI_LOG(f, a...)(void)0 (void)0 |
19 | #endif |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | #define IORESOURCE_BITS0x000000ff 0x000000ff /* Bus-specific bits */ |
26 | |
27 | #define IORESOURCE_TYPE_BITS0x00000f00 0x00000f00 /* Resource type */ |
28 | #define IORESOURCE_IO0x00000100 0x00000100 |
29 | #define IORESOURCE_MEM0x00000200 0x00000200 |
30 | |
31 | #define IORESOURCE_PREFETCH0x00001000 0x00001000 /* No side effects */ |
32 | #define IORESOURCE_MEM_640x00100000 0x00100000 |
33 | |
34 | static int xen_host_pci_sysfs_path(const XenHostPCIDevice *d, |
35 | const char *name, char *buf, ssize_t size) |
36 | { |
37 | int rc; |
38 | |
39 | rc = snprintf(buf, size, "/sys/bus/pci/devices/%04x:%02x:%02x.%d/%s", |
40 | d->domain, d->bus, d->dev, d->func, name); |
41 | |
42 | if (rc >= size || rc < 0) { |
| 8 | | Assuming 'rc' is < 'size' | |
|
| |
| |
43 | |
44 | return -ENODEV19; |
45 | } |
46 | return 0; |
47 | } |
48 | |
49 | |
50 | |
51 | #define XEN_HOST_PCI_RESOURCE_BUFFER_SIZE400 400 |
52 | static int xen_host_pci_get_resource(XenHostPCIDevice *d) |
53 | { |
54 | int i, rc, fd; |
55 | char path[PATH_MAX4096]; |
56 | char buf[XEN_HOST_PCI_RESOURCE_BUFFER_SIZE400]; |
57 | unsigned long long start, end, flags, size; |
58 | char *endptr, *s; |
59 | uint8_t type; |
60 | |
61 | rc = xen_host_pci_sysfs_path(d, "resource", path, sizeof (path)); |
62 | if (rc) { |
63 | return rc; |
64 | } |
65 | fd = open(path, O_RDONLY00); |
66 | if (fd == -1) { |
67 | XEN_HOST_PCI_LOG("Error: Can't open %s: %s\n", path, strerror(errno))(void)0; |
68 | return -errno(*__errno_location ()); |
69 | } |
70 | |
71 | do { |
72 | rc = read(fd, &buf, sizeof (buf) - 1); |
73 | if (rc < 0 && errno(*__errno_location ()) != EINTR4) { |
74 | rc = -errno(*__errno_location ()); |
75 | goto out; |
76 | } |
77 | } while (rc < 0); |
78 | buf[rc] = 0; |
79 | rc = 0; |
80 | |
81 | s = buf; |
82 | for (i = 0; i < PCI_NUM_REGIONS7; i++) { |
83 | type = 0; |
84 | |
85 | start = strtoll(s, &endptr, 16); |
86 | if (*endptr != ' ' || s == endptr) { |
87 | break; |
88 | } |
89 | s = endptr + 1; |
90 | end = strtoll(s, &endptr, 16); |
91 | if (*endptr != ' ' || s == endptr) { |
92 | break; |
93 | } |
94 | s = endptr + 1; |
95 | flags = strtoll(s, &endptr, 16); |
96 | if (*endptr != '\n' || s == endptr) { |
97 | break; |
98 | } |
99 | s = endptr + 1; |
100 | |
101 | if (start) { |
102 | size = end - start + 1; |
103 | } else { |
104 | size = 0; |
105 | } |
106 | |
107 | if (flags & IORESOURCE_IO0x00000100) { |
108 | type |= XEN_HOST_PCI_REGION_TYPE_IO; |
109 | } |
110 | if (flags & IORESOURCE_MEM0x00000200) { |
111 | type |= XEN_HOST_PCI_REGION_TYPE_MEM; |
112 | } |
113 | if (flags & IORESOURCE_PREFETCH0x00001000) { |
114 | type |= XEN_HOST_PCI_REGION_TYPE_PREFETCH; |
115 | } |
116 | if (flags & IORESOURCE_MEM_640x00100000) { |
117 | type |= XEN_HOST_PCI_REGION_TYPE_MEM_64; |
118 | } |
119 | |
120 | if (i < PCI_ROM_SLOT6) { |
121 | d->io_regions[i].base_addr = start; |
122 | d->io_regions[i].size = size; |
123 | d->io_regions[i].type = type; |
124 | d->io_regions[i].bus_flags = flags & IORESOURCE_BITS0x000000ff; |
125 | } else { |
126 | d->rom.base_addr = start; |
127 | d->rom.size = size; |
128 | d->rom.type = type; |
129 | d->rom.bus_flags = flags & IORESOURCE_BITS0x000000ff; |
130 | } |
131 | } |
132 | if (i != PCI_NUM_REGIONS7) { |
133 | |
134 | rc = -ENODEV19; |
135 | } |
136 | |
137 | out: |
138 | close(fd); |
139 | return rc; |
140 | } |
141 | |
142 | |
143 | #define XEN_HOST_PCI_GET_VALUE_BUFFER_SIZE22 22 |
144 | static int xen_host_pci_get_value(XenHostPCIDevice *d, const char *name, |
145 | unsigned int *pvalue, int base) |
146 | { |
147 | char path[PATH_MAX4096]; |
148 | char buf[XEN_HOST_PCI_GET_VALUE_BUFFER_SIZE22]; |
149 | int fd, rc; |
150 | unsigned long value; |
151 | char *endptr; |
152 | |
153 | rc = xen_host_pci_sysfs_path(d, name, path, sizeof (path)); |
| 7 | | Calling 'xen_host_pci_sysfs_path' | |
|
| 11 | | Returning from 'xen_host_pci_sysfs_path' | |
|
154 | if (rc) { |
| |
155 | return rc; |
156 | } |
157 | fd = open(path, O_RDONLY00); |
158 | if (fd == -1) { |
| |
159 | XEN_HOST_PCI_LOG("Error: Can't open %s: %s\n", path, strerror(errno))(void)0; |
160 | return -errno(*__errno_location ()); |
161 | } |
162 | do { |
163 | rc = read(fd, &buf, sizeof (buf) - 1); |
164 | if (rc < 0 && errno(*__errno_location ()) != EINTR4) { |
165 | rc = -errno(*__errno_location ()); |
166 | goto out; |
167 | } |
168 | } while (rc < 0); |
169 | buf[rc] = 0; |
170 | value = strtol(buf, &endptr, base); |
171 | if (endptr == buf || *endptr != '\n') { |
172 | rc = -1; |
173 | } else if ((value == LONG_MIN(-9223372036854775807L -1L) || value == LONG_MAX9223372036854775807L) && errno(*__errno_location ()) == ERANGE34) { |
174 | rc = -errno(*__errno_location ()); |
175 | } else { |
176 | rc = 0; |
177 | *pvalue = value; |
178 | } |
179 | out: |
180 | close(fd); |
181 | return rc; |
182 | } |
183 | |
184 | static inline int xen_host_pci_get_hex_value(XenHostPCIDevice *d, |
185 | const char *name, |
186 | unsigned int *pvalue) |
187 | { |
188 | return xen_host_pci_get_value(d, name, pvalue, 16); |
| 6 | | Calling 'xen_host_pci_get_value' | |
|
| 14 | | Returning from 'xen_host_pci_get_value' | |
|
189 | } |
190 | |
191 | static inline int xen_host_pci_get_dec_value(XenHostPCIDevice *d, |
192 | const char *name, |
193 | unsigned int *pvalue) |
194 | { |
195 | return xen_host_pci_get_value(d, name, pvalue, 10); |
196 | } |
197 | |
198 | static bool_Bool xen_host_pci_dev_is_virtfn(XenHostPCIDevice *d) |
199 | { |
200 | char path[PATH_MAX4096]; |
201 | struct stat buf; |
202 | |
203 | if (xen_host_pci_sysfs_path(d, "physfn", path, sizeof (path))) { |
204 | return false0; |
205 | } |
206 | return !stat(path, &buf); |
207 | } |
208 | |
209 | static int xen_host_pci_config_open(XenHostPCIDevice *d) |
210 | { |
211 | char path[PATH_MAX4096]; |
212 | int rc; |
213 | |
214 | rc = xen_host_pci_sysfs_path(d, "config", path, sizeof (path)); |
215 | if (rc) { |
216 | return rc; |
217 | } |
218 | d->config_fd = open(path, O_RDWR02); |
219 | if (d->config_fd < 0) { |
220 | return -errno(*__errno_location ()); |
221 | } |
222 | return 0; |
223 | } |
224 | |
225 | static int xen_host_pci_config_read(XenHostPCIDevice *d, |
226 | int pos, void *buf, int len) |
227 | { |
228 | int rc; |
229 | |
230 | do { |
231 | rc = pread(d->config_fd, buf, len, pos); |
232 | } while (rc < 0 && (errno(*__errno_location ()) == EINTR4 || errno(*__errno_location ()) == EAGAIN11)); |
233 | if (rc != len) { |
234 | return -errno(*__errno_location ()); |
235 | } |
236 | return 0; |
237 | } |
238 | |
239 | static int xen_host_pci_config_write(XenHostPCIDevice *d, |
240 | int pos, const void *buf, int len) |
241 | { |
242 | int rc; |
243 | |
244 | do { |
245 | rc = pwrite(d->config_fd, buf, len, pos); |
246 | } while (rc < 0 && (errno(*__errno_location ()) == EINTR4 || errno(*__errno_location ()) == EAGAIN11)); |
247 | if (rc != len) { |
248 | return -errno(*__errno_location ()); |
249 | } |
250 | return 0; |
251 | } |
252 | |
253 | |
254 | int xen_host_pci_get_byte(XenHostPCIDevice *d, int pos, uint8_t *p) |
255 | { |
256 | uint8_t buf; |
257 | int rc = xen_host_pci_config_read(d, pos, &buf, 1); |
258 | if (!rc) { |
259 | *p = buf; |
260 | } |
261 | return rc; |
262 | } |
263 | |
264 | int xen_host_pci_get_word(XenHostPCIDevice *d, int pos, uint16_t *p) |
265 | { |
266 | uint16_t buf; |
267 | int rc = xen_host_pci_config_read(d, pos, &buf, 2); |
268 | if (!rc) { |
269 | *p = le16_to_cpu(buf); |
270 | } |
271 | return rc; |
272 | } |
273 | |
274 | int xen_host_pci_get_long(XenHostPCIDevice *d, int pos, uint32_t *p) |
275 | { |
276 | uint32_t buf; |
277 | int rc = xen_host_pci_config_read(d, pos, &buf, 4); |
278 | if (!rc) { |
279 | *p = le32_to_cpu(buf); |
280 | } |
281 | return rc; |
282 | } |
283 | |
284 | int xen_host_pci_get_block(XenHostPCIDevice *d, int pos, uint8_t *buf, int len) |
285 | { |
286 | return xen_host_pci_config_read(d, pos, buf, len); |
287 | } |
288 | |
289 | int xen_host_pci_set_byte(XenHostPCIDevice *d, int pos, uint8_t data) |
290 | { |
291 | return xen_host_pci_config_write(d, pos, &data, 1); |
292 | } |
293 | |
294 | int xen_host_pci_set_word(XenHostPCIDevice *d, int pos, uint16_t data) |
295 | { |
296 | data = cpu_to_le16(data); |
297 | return xen_host_pci_config_write(d, pos, &data, 2); |
298 | } |
299 | |
300 | int xen_host_pci_set_long(XenHostPCIDevice *d, int pos, uint32_t data) |
301 | { |
302 | data = cpu_to_le32(data); |
303 | return xen_host_pci_config_write(d, pos, &data, 4); |
304 | } |
305 | |
306 | int xen_host_pci_set_block(XenHostPCIDevice *d, int pos, uint8_t *buf, int len) |
307 | { |
308 | return xen_host_pci_config_write(d, pos, buf, len); |
309 | } |
310 | |
311 | int xen_host_pci_find_ext_cap_offset(XenHostPCIDevice *d, uint32_t cap) |
312 | { |
313 | uint32_t header = 0; |
314 | int max_cap = XEN_HOST_PCI_MAX_EXT_CAP((0x1000 - 0x100) / (4 + 4)); |
315 | int pos = PCI_CONFIG_SPACE_SIZE0x100; |
316 | |
317 | do { |
318 | if (xen_host_pci_get_long(d, pos, &header)) { |
319 | break; |
320 | } |
321 | |
322 | |
323 | |
324 | |
325 | if (header == 0) { |
326 | break; |
327 | } |
328 | |
329 | if (PCI_EXT_CAP_ID(header)(header & 0x0000ffff) == cap) { |
330 | return pos; |
331 | } |
332 | |
333 | pos = PCI_EXT_CAP_NEXT(header)((header >> 20) & 0xffc); |
334 | if (pos < PCI_CONFIG_SPACE_SIZE0x100) { |
335 | break; |
336 | } |
337 | |
338 | max_cap--; |
339 | } while (max_cap > 0); |
340 | |
341 | return -1; |
342 | } |
343 | |
344 | int xen_host_pci_device_get(XenHostPCIDevice *d, uint16_t domain, |
345 | uint8_t bus, uint8_t dev, uint8_t func) |
346 | { |
347 | unsigned int v; |
| 1 | 'v' declared without an initial value | |
|
348 | int rc = 0; |
349 | |
350 | d->config_fd = -1; |
351 | d->domain = domain; |
352 | d->bus = bus; |
353 | d->dev = dev; |
354 | d->func = func; |
355 | |
356 | rc = xen_host_pci_config_open(d); |
357 | if (rc) { |
| |
358 | goto error; |
359 | } |
360 | rc = xen_host_pci_get_resource(d); |
361 | if (rc) { |
| |
| |
362 | goto error; |
363 | } |
364 | rc = xen_host_pci_get_hex_value(d, "vendor", &v); |
| 5 | | Calling 'xen_host_pci_get_hex_value' | |
|
| 15 | | Returning from 'xen_host_pci_get_hex_value' | |
|
365 | if (rc) { |
| |
| |
366 | goto error; |
367 | } |
368 | d->vendor_id = v; |
| 18 | | Assigned value is garbage or undefined |
|
369 | rc = xen_host_pci_get_hex_value(d, "device", &v); |
370 | if (rc) { |
371 | goto error; |
372 | } |
373 | d->device_id = v; |
374 | rc = xen_host_pci_get_dec_value(d, "irq", &v); |
375 | if (rc) { |
376 | goto error; |
377 | } |
378 | d->irq = v; |
379 | d->is_virtfn = xen_host_pci_dev_is_virtfn(d); |
380 | |
381 | return 0; |
382 | error: |
383 | if (d->config_fd >= 0) { |
384 | close(d->config_fd); |
385 | d->config_fd = -1; |
386 | } |
387 | return rc; |
388 | } |
389 | |
390 | void xen_host_pci_device_put(XenHostPCIDevice *d) |
391 | { |
392 | if (d->config_fd >= 0) { |
393 | close(d->config_fd); |
394 | d->config_fd = -1; |
395 | } |
396 | } |