xref: /openbmc/qemu/hw/xen/xen-host-pci-device.c (revision 911a4efd)
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-common.h"
10 #include "xen-host-pci-device.h"
11 
12 #define XEN_HOST_PCI_MAX_EXT_CAP \
13     ((PCIE_CONFIG_SPACE_SIZE - PCI_CONFIG_SPACE_SIZE) / (PCI_CAP_SIZEOF + 4))
14 
15 #ifdef XEN_HOST_PCI_DEVICE_DEBUG
16 #  define XEN_HOST_PCI_LOG(f, a...) fprintf(stderr, "%s: " f, __func__, ##a)
17 #else
18 #  define XEN_HOST_PCI_LOG(f, a...) (void)0
19 #endif
20 
21 /*
22  * from linux/ioport.h
23  * IO resources have these defined flags.
24  */
25 #define IORESOURCE_BITS         0x000000ff      /* Bus-specific bits */
26 
27 #define IORESOURCE_TYPE_BITS    0x00000f00      /* Resource type */
28 #define IORESOURCE_IO           0x00000100
29 #define IORESOURCE_MEM          0x00000200
30 
31 #define IORESOURCE_PREFETCH     0x00001000      /* No side effects */
32 #define IORESOURCE_MEM_64       0x00100000
33 
34 static void 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     assert(rc >= 0 && rc < size);
42 }
43 
44 
45 /* This size should be enough to read the first 7 lines of a resource file */
46 #define XEN_HOST_PCI_RESOURCE_BUFFER_SIZE 400
47 static void xen_host_pci_get_resource(XenHostPCIDevice *d, Error **errp)
48 {
49     int i, rc, fd;
50     char path[PATH_MAX];
51     char buf[XEN_HOST_PCI_RESOURCE_BUFFER_SIZE];
52     unsigned long long start, end, flags, size;
53     char *endptr, *s;
54     uint8_t type;
55 
56     xen_host_pci_sysfs_path(d, "resource", path, sizeof(path));
57 
58     fd = open(path, O_RDONLY);
59     if (fd == -1) {
60         error_setg_file_open(errp, errno, path);
61         return;
62     }
63 
64     do {
65         rc = read(fd, &buf, sizeof(buf) - 1);
66         if (rc < 0 && errno != EINTR) {
67             error_setg_errno(errp, errno, "read err");
68             goto out;
69         }
70     } while (rc < 0);
71     buf[rc] = 0;
72 
73     s = buf;
74     for (i = 0; i < PCI_NUM_REGIONS; i++) {
75         type = 0;
76 
77         start = strtoll(s, &endptr, 16);
78         if (*endptr != ' ' || s == endptr) {
79             break;
80         }
81         s = endptr + 1;
82         end = strtoll(s, &endptr, 16);
83         if (*endptr != ' ' || s == endptr) {
84             break;
85         }
86         s = endptr + 1;
87         flags = strtoll(s, &endptr, 16);
88         if (*endptr != '\n' || s == endptr) {
89             break;
90         }
91         s = endptr + 1;
92 
93         if (start) {
94             size = end - start + 1;
95         } else {
96             size = 0;
97         }
98 
99         if (flags & IORESOURCE_IO) {
100             type |= XEN_HOST_PCI_REGION_TYPE_IO;
101         }
102         if (flags & IORESOURCE_MEM) {
103             type |= XEN_HOST_PCI_REGION_TYPE_MEM;
104         }
105         if (flags & IORESOURCE_PREFETCH) {
106             type |= XEN_HOST_PCI_REGION_TYPE_PREFETCH;
107         }
108         if (flags & IORESOURCE_MEM_64) {
109             type |= XEN_HOST_PCI_REGION_TYPE_MEM_64;
110         }
111 
112         if (i < PCI_ROM_SLOT) {
113             d->io_regions[i].base_addr = start;
114             d->io_regions[i].size = size;
115             d->io_regions[i].type = type;
116             d->io_regions[i].bus_flags = flags & IORESOURCE_BITS;
117         } else {
118             d->rom.base_addr = start;
119             d->rom.size = size;
120             d->rom.type = type;
121             d->rom.bus_flags = flags & IORESOURCE_BITS;
122         }
123     }
124 
125     if (i != PCI_NUM_REGIONS) {
126         error_setg(errp, "Invalid format or input too short: %s", buf);
127     }
128 
129 out:
130     close(fd);
131 }
132 
133 /* This size should be enough to read a long from a file */
134 #define XEN_HOST_PCI_GET_VALUE_BUFFER_SIZE 22
135 static void xen_host_pci_get_value(XenHostPCIDevice *d, const char *name,
136                                    unsigned int *pvalue, int base, Error **errp)
137 {
138     char path[PATH_MAX];
139     char buf[XEN_HOST_PCI_GET_VALUE_BUFFER_SIZE];
140     int fd, rc;
141     unsigned long value;
142     const char *endptr;
143 
144     xen_host_pci_sysfs_path(d, name, path, sizeof(path));
145 
146     fd = open(path, O_RDONLY);
147     if (fd == -1) {
148         error_setg_file_open(errp, errno, path);
149         return;
150     }
151 
152     do {
153         rc = read(fd, &buf, sizeof(buf) - 1);
154         if (rc < 0 && errno != EINTR) {
155             error_setg_errno(errp, errno, "read err");
156             goto out;
157         }
158     } while (rc < 0);
159 
160     buf[rc] = 0;
161     rc = qemu_strtoul(buf, &endptr, base, &value);
162     if (!rc) {
163         assert(value <= UINT_MAX);
164         *pvalue = value;
165     } else {
166         error_setg_errno(errp, -rc, "failed to parse value '%s'", buf);
167     }
168 
169 out:
170     close(fd);
171 }
172 
173 static inline void xen_host_pci_get_hex_value(XenHostPCIDevice *d,
174                                               const char *name,
175                                               unsigned int *pvalue,
176                                               Error **errp)
177 {
178     xen_host_pci_get_value(d, name, pvalue, 16, errp);
179 }
180 
181 static inline void xen_host_pci_get_dec_value(XenHostPCIDevice *d,
182                                               const char *name,
183                                               unsigned int *pvalue,
184                                               Error **errp)
185 {
186     xen_host_pci_get_value(d, name, pvalue, 10, errp);
187 }
188 
189 static bool xen_host_pci_dev_is_virtfn(XenHostPCIDevice *d)
190 {
191     char path[PATH_MAX];
192     struct stat buf;
193 
194     xen_host_pci_sysfs_path(d, "physfn", path, sizeof(path));
195 
196     return !stat(path, &buf);
197 }
198 
199 static void xen_host_pci_config_open(XenHostPCIDevice *d, Error **errp)
200 {
201     char path[PATH_MAX];
202 
203     xen_host_pci_sysfs_path(d, "config", path, sizeof(path));
204 
205     d->config_fd = open(path, O_RDWR);
206     if (d->config_fd == -1) {
207         error_setg_file_open(errp, errno, path);
208     }
209 }
210 
211 static int xen_host_pci_config_read(XenHostPCIDevice *d,
212                                     int pos, void *buf, int len)
213 {
214     int rc;
215 
216     do {
217         rc = pread(d->config_fd, buf, len, pos);
218     } while (rc < 0 && (errno == EINTR || errno == EAGAIN));
219     if (rc != len) {
220         return -errno;
221     }
222     return 0;
223 }
224 
225 static int xen_host_pci_config_write(XenHostPCIDevice *d,
226                                      int pos, const void *buf, int len)
227 {
228     int rc;
229 
230     do {
231         rc = pwrite(d->config_fd, buf, len, pos);
232     } while (rc < 0 && (errno == EINTR || errno == EAGAIN));
233     if (rc != len) {
234         return -errno;
235     }
236     return 0;
237 }
238 
239 
240 int xen_host_pci_get_byte(XenHostPCIDevice *d, int pos, uint8_t *p)
241 {
242     uint8_t buf;
243     int rc = xen_host_pci_config_read(d, pos, &buf, 1);
244     if (!rc) {
245         *p = buf;
246     }
247     return rc;
248 }
249 
250 int xen_host_pci_get_word(XenHostPCIDevice *d, int pos, uint16_t *p)
251 {
252     uint16_t buf;
253     int rc = xen_host_pci_config_read(d, pos, &buf, 2);
254     if (!rc) {
255         *p = le16_to_cpu(buf);
256     }
257     return rc;
258 }
259 
260 int xen_host_pci_get_long(XenHostPCIDevice *d, int pos, uint32_t *p)
261 {
262     uint32_t buf;
263     int rc = xen_host_pci_config_read(d, pos, &buf, 4);
264     if (!rc) {
265         *p = le32_to_cpu(buf);
266     }
267     return rc;
268 }
269 
270 int xen_host_pci_get_block(XenHostPCIDevice *d, int pos, uint8_t *buf, int len)
271 {
272     return xen_host_pci_config_read(d, pos, buf, len);
273 }
274 
275 int xen_host_pci_set_byte(XenHostPCIDevice *d, int pos, uint8_t data)
276 {
277     return xen_host_pci_config_write(d, pos, &data, 1);
278 }
279 
280 int xen_host_pci_set_word(XenHostPCIDevice *d, int pos, uint16_t data)
281 {
282     data = cpu_to_le16(data);
283     return xen_host_pci_config_write(d, pos, &data, 2);
284 }
285 
286 int xen_host_pci_set_long(XenHostPCIDevice *d, int pos, uint32_t data)
287 {
288     data = cpu_to_le32(data);
289     return xen_host_pci_config_write(d, pos, &data, 4);
290 }
291 
292 int xen_host_pci_set_block(XenHostPCIDevice *d, int pos, uint8_t *buf, int len)
293 {
294     return xen_host_pci_config_write(d, pos, buf, len);
295 }
296 
297 int xen_host_pci_find_ext_cap_offset(XenHostPCIDevice *d, uint32_t cap)
298 {
299     uint32_t header = 0;
300     int max_cap = XEN_HOST_PCI_MAX_EXT_CAP;
301     int pos = PCI_CONFIG_SPACE_SIZE;
302 
303     do {
304         if (xen_host_pci_get_long(d, pos, &header)) {
305             break;
306         }
307         /*
308          * If we have no capabilities, this is indicated by cap ID,
309          * cap version and next pointer all being 0.
310          */
311         if (header == 0) {
312             break;
313         }
314 
315         if (PCI_EXT_CAP_ID(header) == cap) {
316             return pos;
317         }
318 
319         pos = PCI_EXT_CAP_NEXT(header);
320         if (pos < PCI_CONFIG_SPACE_SIZE) {
321             break;
322         }
323 
324         max_cap--;
325     } while (max_cap > 0);
326 
327     return -1;
328 }
329 
330 void xen_host_pci_device_get(XenHostPCIDevice *d, uint16_t domain,
331                              uint8_t bus, uint8_t dev, uint8_t func,
332                              Error **errp)
333 {
334     unsigned int v;
335     Error *err = NULL;
336 
337     d->config_fd = -1;
338     d->domain = domain;
339     d->bus = bus;
340     d->dev = dev;
341     d->func = func;
342 
343     xen_host_pci_config_open(d, &err);
344     if (err) {
345         goto error;
346     }
347 
348     xen_host_pci_get_resource(d, &err);
349     if (err) {
350         goto error;
351     }
352 
353     xen_host_pci_get_hex_value(d, "vendor", &v, &err);
354     if (err) {
355         goto error;
356     }
357     d->vendor_id = v;
358 
359     xen_host_pci_get_hex_value(d, "device", &v, &err);
360     if (err) {
361         goto error;
362     }
363     d->device_id = v;
364 
365     xen_host_pci_get_dec_value(d, "irq", &v, &err);
366     if (err) {
367         goto error;
368     }
369     d->irq = v;
370 
371     xen_host_pci_get_hex_value(d, "class", &v, &err);
372     if (err) {
373         goto error;
374     }
375     d->class_code = v;
376 
377     d->is_virtfn = xen_host_pci_dev_is_virtfn(d);
378 
379     return;
380 
381 error:
382     error_propagate(errp, err);
383 
384     if (d->config_fd >= 0) {
385         close(d->config_fd);
386         d->config_fd = -1;
387     }
388 }
389 
390 bool xen_host_pci_device_closed(XenHostPCIDevice *d)
391 {
392     return d->config_fd == -1;
393 }
394 
395 void xen_host_pci_device_put(XenHostPCIDevice *d)
396 {
397     if (d->config_fd >= 0) {
398         close(d->config_fd);
399         d->config_fd = -1;
400     }
401 }
402