1 /*
2 * Copyright (C) 2011 Citrix Ltd.
3 *
4 * This work is licensed under the terms of the GNU GPL, version 2. See
5 * the COPYING file in the top-level directory.
6 *
7 */
8
9 #include "qemu/osdep.h"
10 #include "qapi/error.h"
11 #include "qemu/cutils.h"
12 #include "hw/xen/xen-legacy-backend.h"
13 #include "hw/xen/xen-bus-helper.h"
14 #include "xen-host-pci-device.h"
15
16 #define XEN_HOST_PCI_MAX_EXT_CAP \
17 ((PCIE_CONFIG_SPACE_SIZE - PCI_CONFIG_SPACE_SIZE) / (PCI_CAP_SIZEOF + 4))
18
19 #ifdef XEN_HOST_PCI_DEVICE_DEBUG
20 # define XEN_HOST_PCI_LOG(f, a...) fprintf(stderr, "%s: " f, __func__, ##a)
21 #else
22 # define XEN_HOST_PCI_LOG(f, a...) (void)0
23 #endif
24
25 /*
26 * from linux/ioport.h
27 * IO resources have these defined flags.
28 */
29 #define IORESOURCE_BITS 0x000000ff /* Bus-specific bits */
30
31 #define IORESOURCE_TYPE_BITS 0x00000f00 /* Resource type */
32 #define IORESOURCE_IO 0x00000100
33 #define IORESOURCE_MEM 0x00000200
34
35 #define IORESOURCE_PREFETCH 0x00001000 /* No side effects */
36 #define IORESOURCE_MEM_64 0x00100000
37
38 /*
39 * Non-passthrough (dom0) accesses are local PCI devices and use the given BDF
40 * Passthough (stubdom) accesses are through PV frontend PCI device. Those
41 * either have a BDF identical to the backend's BDF (xen-backend.passthrough=1)
42 * or a local virtual BDF (xen-backend.passthrough=0)
43 *
44 * We are always given the backend's BDF and need to lookup the appropriate
45 * local BDF for sysfs access.
46 */
xen_host_pci_fill_local_addr(XenHostPCIDevice * d,Error ** errp)47 static void xen_host_pci_fill_local_addr(XenHostPCIDevice *d, Error **errp)
48 {
49 unsigned int num_devs, len, i;
50 unsigned int domain, bus, dev, func;
51 char *be_path = NULL;
52 char path[16];
53
54 be_path = qemu_xen_xs_read(xenstore, 0, "device/pci/0/backend", &len);
55 if (!be_path) {
56 error_setg(errp, "Failed to read device/pci/0/backend");
57 goto out;
58 }
59
60 if (xs_node_scanf(xenstore, 0, be_path, "num_devs", NULL,
61 "%d", &num_devs) != 1) {
62 error_setg(errp, "Failed to read or parse %s/num_devs", be_path);
63 goto out;
64 }
65
66 for (i = 0; i < num_devs; i++) {
67 snprintf(path, sizeof(path), "dev-%d", i);
68 if (xs_node_scanf(xenstore, 0, be_path, path, NULL,
69 "%x:%x:%x.%x", &domain, &bus, &dev, &func) != 4) {
70 error_setg(errp, "Failed to read or parse %s/%s", be_path, path);
71 goto out;
72 }
73 if (domain != d->domain ||
74 bus != d->bus ||
75 dev != d->dev ||
76 func != d->func)
77 continue;
78 snprintf(path, sizeof(path), "vdev-%d", i);
79 if (xs_node_scanf(xenstore, 0, be_path, path, NULL,
80 "%x:%x:%x.%x", &domain, &bus, &dev, &func) != 4) {
81 error_setg(errp, "Failed to read or parse %s/%s", be_path, path);
82 goto out;
83 }
84 d->local_domain = domain;
85 d->local_bus = bus;
86 d->local_dev = dev;
87 d->local_func = func;
88 goto out;
89 }
90 error_setg(errp, "Failed to find PCI device %x:%x:%x.%x in xenstore",
91 d->domain, d->bus, d->dev, d->func);
92
93 out:
94 free(be_path);
95 }
96
xen_host_pci_sysfs_path(const XenHostPCIDevice * d,const char * name,char * buf,ssize_t size)97 static void xen_host_pci_sysfs_path(const XenHostPCIDevice *d,
98 const char *name, char *buf, ssize_t size)
99 {
100 int rc;
101
102 rc = snprintf(buf, size, "/sys/bus/pci/devices/%04x:%02x:%02x.%d/%s",
103 d->local_domain, d->local_bus, d->local_dev, d->local_func,
104 name);
105 assert(rc >= 0 && rc < size);
106 }
107
108
109 /* This size should be enough to read the first 7 lines of a resource file */
110 #define XEN_HOST_PCI_RESOURCE_BUFFER_SIZE 400
xen_host_pci_get_resource(XenHostPCIDevice * d,Error ** errp)111 static void xen_host_pci_get_resource(XenHostPCIDevice *d, Error **errp)
112 {
113 int i, rc, fd;
114 char path[PATH_MAX];
115 char buf[XEN_HOST_PCI_RESOURCE_BUFFER_SIZE];
116 unsigned long long start, end, flags, size;
117 char *endptr, *s;
118 uint8_t type;
119
120 xen_host_pci_sysfs_path(d, "resource", path, sizeof(path));
121
122 fd = open(path, O_RDONLY);
123 if (fd == -1) {
124 error_setg_file_open(errp, errno, path);
125 return;
126 }
127
128 do {
129 rc = read(fd, &buf, sizeof(buf) - 1);
130 if (rc < 0 && errno != EINTR) {
131 error_setg_errno(errp, errno, "read err");
132 goto out;
133 }
134 } while (rc < 0);
135 buf[rc] = 0;
136
137 s = buf;
138 for (i = 0; i < PCI_NUM_REGIONS; i++) {
139 type = 0;
140
141 start = strtoll(s, &endptr, 16);
142 if (*endptr != ' ' || s == endptr) {
143 break;
144 }
145 s = endptr + 1;
146 end = strtoll(s, &endptr, 16);
147 if (*endptr != ' ' || s == endptr) {
148 break;
149 }
150 s = endptr + 1;
151 flags = strtoll(s, &endptr, 16);
152 if (*endptr != '\n' || s == endptr) {
153 break;
154 }
155 s = endptr + 1;
156
157 if (start) {
158 size = end - start + 1;
159 } else {
160 size = 0;
161 }
162
163 if (flags & IORESOURCE_IO) {
164 type |= XEN_HOST_PCI_REGION_TYPE_IO;
165 }
166 if (flags & IORESOURCE_MEM) {
167 type |= XEN_HOST_PCI_REGION_TYPE_MEM;
168 }
169 if (flags & IORESOURCE_PREFETCH) {
170 type |= XEN_HOST_PCI_REGION_TYPE_PREFETCH;
171 }
172 if (flags & IORESOURCE_MEM_64) {
173 type |= XEN_HOST_PCI_REGION_TYPE_MEM_64;
174 }
175
176 if (i < PCI_ROM_SLOT) {
177 d->io_regions[i].base_addr = start;
178 d->io_regions[i].size = size;
179 d->io_regions[i].type = type;
180 d->io_regions[i].bus_flags = flags & IORESOURCE_BITS;
181 } else {
182 d->rom.base_addr = start;
183 d->rom.size = size;
184 d->rom.type = type;
185 d->rom.bus_flags = flags & IORESOURCE_BITS;
186 }
187 }
188
189 if (i != PCI_NUM_REGIONS) {
190 error_setg(errp, "Invalid format or input too short: %s", buf);
191 }
192
193 out:
194 close(fd);
195 }
196
197 /* This size should be enough to read a long from a file */
198 #define XEN_HOST_PCI_GET_VALUE_BUFFER_SIZE 22
xen_host_pci_get_value(XenHostPCIDevice * d,const char * name,unsigned int * pvalue,int base,Error ** errp)199 static void xen_host_pci_get_value(XenHostPCIDevice *d, const char *name,
200 unsigned int *pvalue, int base, Error **errp)
201 {
202 char path[PATH_MAX];
203 char buf[XEN_HOST_PCI_GET_VALUE_BUFFER_SIZE];
204 int fd, rc;
205 unsigned long value;
206 const char *endptr;
207
208 xen_host_pci_sysfs_path(d, name, path, sizeof(path));
209
210 fd = open(path, O_RDONLY);
211 if (fd == -1) {
212 error_setg_file_open(errp, errno, path);
213 return;
214 }
215
216 do {
217 rc = read(fd, &buf, sizeof(buf) - 1);
218 if (rc < 0 && errno != EINTR) {
219 error_setg_errno(errp, errno, "read err");
220 goto out;
221 }
222 } while (rc < 0);
223
224 buf[rc] = 0;
225 rc = qemu_strtoul(buf, &endptr, base, &value);
226 if (!rc) {
227 assert(value <= UINT_MAX);
228 *pvalue = value;
229 } else {
230 error_setg_errno(errp, -rc, "failed to parse value '%s'", buf);
231 }
232
233 out:
234 close(fd);
235 }
236
xen_host_pci_get_hex_value(XenHostPCIDevice * d,const char * name,unsigned int * pvalue,Error ** errp)237 static inline void xen_host_pci_get_hex_value(XenHostPCIDevice *d,
238 const char *name,
239 unsigned int *pvalue,
240 Error **errp)
241 {
242 xen_host_pci_get_value(d, name, pvalue, 16, errp);
243 }
244
xen_host_pci_get_dec_value(XenHostPCIDevice * d,const char * name,unsigned int * pvalue,Error ** errp)245 static inline void xen_host_pci_get_dec_value(XenHostPCIDevice *d,
246 const char *name,
247 unsigned int *pvalue,
248 Error **errp)
249 {
250 xen_host_pci_get_value(d, name, pvalue, 10, errp);
251 }
252
xen_host_pci_dev_is_virtfn(XenHostPCIDevice * d)253 static bool xen_host_pci_dev_is_virtfn(XenHostPCIDevice *d)
254 {
255 char path[PATH_MAX];
256 struct stat buf;
257
258 xen_host_pci_sysfs_path(d, "physfn", path, sizeof(path));
259
260 return !stat(path, &buf);
261 }
262
xen_host_pci_config_open(XenHostPCIDevice * d,Error ** errp)263 static void xen_host_pci_config_open(XenHostPCIDevice *d, Error **errp)
264 {
265 char path[PATH_MAX];
266
267 xen_host_pci_sysfs_path(d, "config", path, sizeof(path));
268
269 d->config_fd = open(path, O_RDWR);
270 if (d->config_fd == -1) {
271 error_setg_file_open(errp, errno, path);
272 }
273 }
274
xen_host_pci_config_read(XenHostPCIDevice * d,int pos,void * buf,int len)275 static int xen_host_pci_config_read(XenHostPCIDevice *d,
276 int pos, void *buf, int len)
277 {
278 int rc;
279
280 do {
281 rc = pread(d->config_fd, buf, len, pos);
282 } while (rc < 0 && (errno == EINTR || errno == EAGAIN));
283 if (rc != len) {
284 return -errno;
285 }
286 return 0;
287 }
288
xen_host_pci_config_write(XenHostPCIDevice * d,int pos,const void * buf,int len)289 static int xen_host_pci_config_write(XenHostPCIDevice *d,
290 int pos, const void *buf, int len)
291 {
292 int rc;
293
294 do {
295 rc = pwrite(d->config_fd, buf, len, pos);
296 } while (rc < 0 && (errno == EINTR || errno == EAGAIN));
297 if (rc != len) {
298 return -errno;
299 }
300 return 0;
301 }
302
303
xen_host_pci_get_byte(XenHostPCIDevice * d,int pos,uint8_t * p)304 int xen_host_pci_get_byte(XenHostPCIDevice *d, int pos, uint8_t *p)
305 {
306 uint8_t buf;
307 int rc = xen_host_pci_config_read(d, pos, &buf, 1);
308 if (!rc) {
309 *p = buf;
310 }
311 return rc;
312 }
313
xen_host_pci_get_word(XenHostPCIDevice * d,int pos,uint16_t * p)314 int xen_host_pci_get_word(XenHostPCIDevice *d, int pos, uint16_t *p)
315 {
316 uint16_t buf;
317 int rc = xen_host_pci_config_read(d, pos, &buf, 2);
318 if (!rc) {
319 *p = le16_to_cpu(buf);
320 }
321 return rc;
322 }
323
xen_host_pci_get_long(XenHostPCIDevice * d,int pos,uint32_t * p)324 int xen_host_pci_get_long(XenHostPCIDevice *d, int pos, uint32_t *p)
325 {
326 uint32_t buf;
327 int rc = xen_host_pci_config_read(d, pos, &buf, 4);
328 if (!rc) {
329 *p = le32_to_cpu(buf);
330 }
331 return rc;
332 }
333
xen_host_pci_get_block(XenHostPCIDevice * d,int pos,uint8_t * buf,int len)334 int xen_host_pci_get_block(XenHostPCIDevice *d, int pos, uint8_t *buf, int len)
335 {
336 return xen_host_pci_config_read(d, pos, buf, len);
337 }
338
xen_host_pci_set_byte(XenHostPCIDevice * d,int pos,uint8_t data)339 int xen_host_pci_set_byte(XenHostPCIDevice *d, int pos, uint8_t data)
340 {
341 return xen_host_pci_config_write(d, pos, &data, 1);
342 }
343
xen_host_pci_set_word(XenHostPCIDevice * d,int pos,uint16_t data)344 int xen_host_pci_set_word(XenHostPCIDevice *d, int pos, uint16_t data)
345 {
346 data = cpu_to_le16(data);
347 return xen_host_pci_config_write(d, pos, &data, 2);
348 }
349
xen_host_pci_set_long(XenHostPCIDevice * d,int pos,uint32_t data)350 int xen_host_pci_set_long(XenHostPCIDevice *d, int pos, uint32_t data)
351 {
352 data = cpu_to_le32(data);
353 return xen_host_pci_config_write(d, pos, &data, 4);
354 }
355
xen_host_pci_set_block(XenHostPCIDevice * d,int pos,uint8_t * buf,int len)356 int xen_host_pci_set_block(XenHostPCIDevice *d, int pos, uint8_t *buf, int len)
357 {
358 return xen_host_pci_config_write(d, pos, buf, len);
359 }
360
xen_host_pci_find_ext_cap_offset(XenHostPCIDevice * d,uint32_t cap)361 int xen_host_pci_find_ext_cap_offset(XenHostPCIDevice *d, uint32_t cap)
362 {
363 uint32_t header = 0;
364 int max_cap = XEN_HOST_PCI_MAX_EXT_CAP;
365 int pos = PCI_CONFIG_SPACE_SIZE;
366
367 do {
368 if (xen_host_pci_get_long(d, pos, &header)) {
369 break;
370 }
371 /*
372 * If we have no capabilities, this is indicated by cap ID,
373 * cap version and next pointer all being 0.
374 */
375 if (header == 0) {
376 break;
377 }
378
379 if (PCI_EXT_CAP_ID(header) == cap) {
380 return pos;
381 }
382
383 pos = PCI_EXT_CAP_NEXT(header);
384 if (pos < PCI_CONFIG_SPACE_SIZE) {
385 break;
386 }
387
388 max_cap--;
389 } while (max_cap > 0);
390
391 return -1;
392 }
393
xen_host_pci_device_get(XenHostPCIDevice * d,uint16_t domain,uint8_t bus,uint8_t dev,uint8_t func,Error ** errp)394 void xen_host_pci_device_get(XenHostPCIDevice *d, uint16_t domain,
395 uint8_t bus, uint8_t dev, uint8_t func,
396 Error **errp)
397 {
398 ERRP_GUARD();
399 unsigned int v;
400
401 d->config_fd = -1;
402 d->domain = domain;
403 d->bus = bus;
404 d->dev = dev;
405 d->func = func;
406
407 if (xen_is_stubdomain) {
408 xen_host_pci_fill_local_addr(d, errp);
409 if (*errp) {
410 goto error;
411 }
412 } else {
413 d->local_domain = d->domain;
414 d->local_bus = d->bus;
415 d->local_dev = d->dev;
416 d->local_func = d->func;
417 }
418
419 xen_host_pci_config_open(d, errp);
420 if (*errp) {
421 goto error;
422 }
423
424 xen_host_pci_get_resource(d, errp);
425 if (*errp) {
426 goto error;
427 }
428
429 xen_host_pci_get_hex_value(d, "vendor", &v, errp);
430 if (*errp) {
431 goto error;
432 }
433 d->vendor_id = v;
434
435 xen_host_pci_get_hex_value(d, "device", &v, errp);
436 if (*errp) {
437 goto error;
438 }
439 d->device_id = v;
440
441 xen_host_pci_get_dec_value(d, "irq", &v, errp);
442 if (*errp) {
443 goto error;
444 }
445 d->irq = v;
446
447 xen_host_pci_get_hex_value(d, "class", &v, errp);
448 if (*errp) {
449 goto error;
450 }
451 d->class_code = v;
452
453 d->is_virtfn = xen_host_pci_dev_is_virtfn(d);
454
455 return;
456
457 error:
458
459 if (d->config_fd >= 0) {
460 close(d->config_fd);
461 d->config_fd = -1;
462 }
463 }
464
xen_host_pci_device_closed(XenHostPCIDevice * d)465 bool xen_host_pci_device_closed(XenHostPCIDevice *d)
466 {
467 return d->config_fd == -1;
468 }
469
xen_host_pci_device_put(XenHostPCIDevice * d)470 void xen_host_pci_device_put(XenHostPCIDevice *d)
471 {
472 if (d->config_fd >= 0) {
473 close(d->config_fd);
474 d->config_fd = -1;
475 }
476 }
477