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