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