qemu/hw/xen/xen-host-pci-device.c
<<
>>
Prefs
   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    unsigned int v;
 337    Error *err = NULL;
 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, &err);
 346    if (err) {
 347        goto error;
 348    }
 349
 350    xen_host_pci_get_resource(d, &err);
 351    if (err) {
 352        goto error;
 353    }
 354
 355    xen_host_pci_get_hex_value(d, "vendor", &v, &err);
 356    if (err) {
 357        goto error;
 358    }
 359    d->vendor_id = v;
 360
 361    xen_host_pci_get_hex_value(d, "device", &v, &err);
 362    if (err) {
 363        goto error;
 364    }
 365    d->device_id = v;
 366
 367    xen_host_pci_get_dec_value(d, "irq", &v, &err);
 368    if (err) {
 369        goto error;
 370    }
 371    d->irq = v;
 372
 373    xen_host_pci_get_hex_value(d, "class", &v, &err);
 374    if (err) {
 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    error_propagate(errp, err);
 385
 386    if (d->config_fd >= 0) {
 387        close(d->config_fd);
 388        d->config_fd = -1;
 389    }
 390}
 391
 392bool xen_host_pci_device_closed(XenHostPCIDevice *d)
 393{
 394    return d->config_fd == -1;
 395}
 396
 397void xen_host_pci_device_put(XenHostPCIDevice *d)
 398{
 399    if (d->config_fd >= 0) {
 400        close(d->config_fd);
 401        d->config_fd = -1;
 402    }
 403}
 404