virpcimock.c 29.3 KB
Newer Older
M
Michal Privoznik 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/*
 * Copyright (C) 2013 Red Hat, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library.  If not, see
 * <http://www.gnu.org/licenses/>.
 */

#include <config.h>

21
#if defined(__linux__) || defined(__FreeBSD__)
22
# include "virmock.h"
M
Michal Privoznik 已提交
23 24 25 26
# include <unistd.h>
# include <fcntl.h>
# include <sys/stat.h>
# include <stdarg.h>
27
# include <dirent.h>
M
Michal Privoznik 已提交
28 29 30 31
# include "viralloc.h"
# include "virstring.h"
# include "virfile.h"

32 33
static int (*real_access)(const char *path, int mode);
static int (*real_open)(const char *path, int flags, ...);
34
# ifdef __GLIBC__
M
Michal Privoznik 已提交
35
static int (*real___open_2)(const char *path, int flags);
36
# endif /* ! __GLIBC__ */
37 38
static int (*real_close)(int fd);
static DIR * (*real_opendir)(const char *name);
39
static char *(*real_virFileCanonicalizePath)(const char *path);
M
Michal Privoznik 已提交
40

41
static char *fakerootdir;
M
Michal Privoznik 已提交
42

43 44 45 46 47 48 49
/* To add a new mocked prefix in virpcimock:
 * - add the prefix here as a define to make it easier to track what we
 * are mocking;
 * - add it to the 'pathPrefixIsMocked()' helper;
 * - (optional) edit 'getrealpath()' if you need the resulting mocked
 * path to be different than <fakerootdir>/path
 */
50
# define SYSFS_PCI_PREFIX "/sys/bus/pci/"
51 52
# define SYSFS_KERNEL_PREFIX "/sys/kernel/"
# define DEV_VFIO_PREFIX "/dev/vfio/"
M
Michal Privoznik 已提交
53

54 55 56 57 58 59 60 61 62
# define STDERR(...) \
    fprintf(stderr, "%s %zu: ", __FUNCTION__, (size_t) __LINE__); \
    fprintf(stderr, __VA_ARGS__); \
    fprintf(stderr, "\n"); \

# define ABORT(...) \
    do { \
        STDERR(__VA_ARGS__); \
        abort(); \
M
Michal Privoznik 已提交
63 64
    } while (0)

65
# define ABORT_OOM() \
M
Michal Privoznik 已提交
66 67 68 69 70
    ABORT("Out of memory")
/*
 * The plan:
 *
 * Mock some file handling functions. Redirect them into a stub tree passed via
71
 * LIBVIRT_FAKE_ROOT_DIR env variable. All files and links within stub tree is
72 73 74 75 76 77 78 79 80 81 82 83
 * created by us. There are some actions that we must take if some special
 * files are written to. Here's the list of files we watch:
 *
 * /sys/bus/pci/drivers/<driver>/bind
 *   Check if driver supports the device and bind driver to it (create symlink
 *   called 'driver' pointing to the /sys/but/pci/drivers/<driver>).
 *   Data in format "DDDD:BB:DD.F" (Domain:Bus:Device.Function).
 *
 * /sys/bus/pci/drivers/<driver>/unbind
 *   Unbind driver from the device.
 *   Data in format "DDDD:BB:DD.F" (Domain:Bus:Device.Function).
 *
84 85 86 87
 * /sys/bus/pci/drivers_probe
 *   Probe for a driver that handles the specified device.
 *   Data in format "DDDD:BB:DD.F" (Domain:Bus:Device.Function).
 *
88 89 90 91 92
 * /sys/bus/pci/devices/<device>/driver_override
 *   Name of a driver that overrides preferred driver can be written
 *   here. The device will be attached to it on drivers_probe event.
 *   Writing an empty string (or "\n") clears the override.
 *
93 94 95 96
 * As a little hack, we are not mocking write to these files, but close()
 * instead. The advantage is we don't need any self growing array to hold the
 * partial writes and construct them back. We can let all the writes finish,
 * and then just read the file content back.
M
Michal Privoznik 已提交
97 98 99 100 101 102 103 104
 */

/*
 *
 * Functions to model kernel behavior
 *
 */

105 106 107 108 109 110 111
struct pciDriver {
    char *name;
    int *vendor;        /* List of vendor:device IDs the driver can handle */
    int *device;
    size_t len;            /* @len is used for both @vendor and @device */
};

112 113 114 115 116
struct pciIommuGroup {
    int iommu;
    size_t nDevicesBoundToVFIO; /* Indicates the devices in the group */
};

117 118 119 120 121 122 123 124
struct pciDeviceAddress {
    unsigned int domain;
    unsigned int bus;
    unsigned int device;
    unsigned int function;
};
# define ADDR_STR_FMT "%04x:%02x:%02x.%d"

M
Michal Privoznik 已提交
125
struct pciDevice {
126
    struct pciDeviceAddress addr;
M
Michal Privoznik 已提交
127 128
    int vendor;
    int device;
129
    int klass;
A
Andrea Bolognani 已提交
130
    int iommuGroup;
131
    const char *physfn;
132 133 134 135 136 137
    struct pciDriver *driver;   /* Driver attached. NULL if attached to no driver */
};

struct fdCallback {
    int fd;
    char *path;
M
Michal Privoznik 已提交
138 139 140
};

struct pciDevice **pciDevices = NULL;
141
size_t nPCIDevices = 0;
M
Michal Privoznik 已提交
142

143
struct pciDriver **pciDrivers = NULL;
144
size_t nPCIDrivers = 0;
145

146 147 148
struct pciIommuGroup **pciIommuGroups = NULL;
size_t npciIommuGroups = 0;

149 150 151
struct fdCallback *callbacks = NULL;
size_t nCallbacks = 0;

M
Michal Privoznik 已提交
152 153
static void init_env(void);

154
static int pci_device_autobind(struct pciDevice *dev);
M
Michal Privoznik 已提交
155
static void pci_device_new_from_stub(const struct pciDevice *data);
156
static struct pciDevice *pci_device_find_by_id(struct pciDeviceAddress const *addr);
157 158
static struct pciDevice *pci_device_find_by_content(const char *path);

159
static void pci_driver_new(const char *name, ...);
160 161
static struct pciDriver *pci_driver_find_by_dev(struct pciDevice *dev);
static struct pciDriver *pci_driver_find_by_path(const char *path);
162
static struct pciDriver *pci_driver_find_by_driver_override(struct pciDevice *dev);
163 164 165 166 167
static int pci_driver_bind(struct pciDriver *driver, struct pciDevice *dev);
static int pci_driver_unbind(struct pciDriver *driver, struct pciDevice *dev);
static int pci_driver_handle_change(int fd, const char *path);
static int pci_driver_handle_bind(const char *path);
static int pci_driver_handle_unbind(const char *path);
M
Michal Privoznik 已提交
168 169 170 171 172 173 174 175


/*
 * Helper functions
 */
static void
make_file(const char *path,
          const char *name,
176 177
          const char *value,
          ssize_t len)
M
Michal Privoznik 已提交
178 179
{
    int fd = -1;
180
    g_autofree char *filepath = NULL;
181 182
    if (value && len == -1)
        len = strlen(value);
M
Michal Privoznik 已提交
183

184
    filepath = g_strdup_printf("%s/%s", path, name);
M
Michal Privoznik 已提交
185

186
    if ((fd = real_open(filepath, O_CREAT|O_WRONLY, 0666)) < 0)
M
Michal Privoznik 已提交
187 188
        ABORT("Unable to open: %s", filepath);

189
    if (value && safewrite(fd, value, len) != len)
M
Michal Privoznik 已提交
190 191 192 193 194
        ABORT("Unable to write: %s", filepath);

    VIR_FORCE_CLOSE(fd);
}

195 196 197 198
static void
make_dir(const char *path,
         const char *name)
{
199
    g_autofree char *dirpath = NULL;
200

201
    dirpath = g_strdup_printf("%s/%s", path, name);
202 203 204 205 206

    if (virFileMakePath(dirpath) < 0)
        ABORT("Unable to create: %s", dirpath);
}

A
Andrea Bolognani 已提交
207 208 209 210 211
static void
make_symlink(const char *path,
          const char *name,
          const char *target)
{
212
    g_autofree char *filepath = NULL;
A
Andrea Bolognani 已提交
213

214
    filepath = g_strdup_printf("%s/%s", path, name);
A
Andrea Bolognani 已提交
215 216 217 218 219

    if (symlink(target, filepath) < 0)
        ABORT("Unable to create symlink filepath -> target");
}

220 221 222
static int
pci_read_file(const char *path,
              char *buf,
223 224
              size_t buf_size,
              bool truncate)
225 226 227
{
    int ret = -1;
    int fd = -1;
228
    g_autofree char *newpath = NULL;
229

230
    newpath = g_strdup_printf("%s/%s", fakerootdir, path);
231

232
    if ((fd = real_open(newpath, O_RDWR)) < 0)
233 234 235 236 237 238 239 240
        goto cleanup;

    bzero(buf, buf_size);
    if (saferead(fd, buf, buf_size - 1) < 0) {
        STDERR("Unable to read from %s", newpath);
        goto cleanup;
    }

241 242
    if (truncate &&
        ftruncate(fd, 0) < 0)
243 244 245
        goto cleanup;

    ret = 0;
246
 cleanup:
247
    real_close(fd);
248 249 250
    return ret;
}

251 252 253 254 255 256 257 258
static bool
pathPrefixIsMocked(const char *path)
{
    return STRPREFIX(path, SYSFS_PCI_PREFIX) ||
           STRPREFIX(path, SYSFS_KERNEL_PREFIX) ||
           STRPREFIX(path, DEV_VFIO_PREFIX);
}

M
Michal Privoznik 已提交
259 260 261 262
static int
getrealpath(char **newpath,
            const char *path)
{
263 264
    if (!fakerootdir && pathPrefixIsMocked(path))
        init_env();
M
Michal Privoznik 已提交
265

266
    if (STRPREFIX(path, SYSFS_PCI_PREFIX)) {
267 268 269
        *newpath = g_strdup_printf("%s/sys/bus/pci/%s",
                                   fakerootdir,
                                   path + strlen(SYSFS_PCI_PREFIX));
270
    } else if (pathPrefixIsMocked(path)) {
271 272 273
        *newpath = g_strdup_printf("%s/%s",
                                   fakerootdir,
                                   path);
M
Michal Privoznik 已提交
274
    } else {
275
        *newpath = g_strdup(path);
M
Michal Privoznik 已提交
276 277 278 279 280
    }

    return 0;
}

281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307
static bool
find_fd(int fd, size_t *indx)
{
    size_t i;

    for (i = 0; i < nCallbacks; i++) {
        if (callbacks[i].fd == fd) {
            if (indx)
                *indx = i;
            return true;
        }
    }

    return false;
}

static int
add_fd(int fd, const char *path)
{
    size_t i;

    if (find_fd(fd, &i)) {
        struct fdCallback cb = callbacks[i];
        ABORT("FD %d %s already present in the array as %d %s",
              fd, path, cb.fd, cb.path);
    }

308
    if (VIR_REALLOC_N_QUIET(callbacks, nCallbacks + 1) < 0) {
309
        errno = ENOMEM;
310
        return -1;
311 312
    }

313
    callbacks[nCallbacks].path = g_strdup(path);
314
    callbacks[nCallbacks++].fd = fd;
315 316

    return 0;
317 318 319 320 321 322 323 324 325 326 327
}

static int
remove_fd(int fd)
{
    size_t i;

    if (find_fd(fd, &i)) {
        struct fdCallback cb = callbacks[i];

        if (pci_driver_handle_change(cb.fd, cb.path) < 0)
328
            return -1;
329 330 331 332

        VIR_FREE(cb.path);
        if (VIR_DELETE_ELEMENT(callbacks, i, nCallbacks) < 0) {
            errno = EINVAL;
333
            return -1;
334 335 336
        }
    }

337
    return 0;
338 339
}

M
Michal Privoznik 已提交
340 341 342 343

/*
 * PCI Device functions
 */
344 345 346 347 348
static char *
pci_address_format(struct pciDeviceAddress const *addr)
{
    char *ret;

349 350 351
    ret = g_strdup_printf(ADDR_STR_FMT,
                          addr->domain, addr->bus,
                          addr->device, addr->function);
352 353 354 355 356 357 358 359 360 361 362 363 364 365 366
    return ret;
}

static int
pci_address_parse(struct pciDeviceAddress *addr,
                  const char *buf)
{
    if (sscanf(buf, ADDR_STR_FMT,
               &addr->domain, &addr->bus,
               &addr->device, &addr->function) != 4)
        return -1;
    return 0;
}


367 368 369 370 371 372 373
static char *
pci_device_get_path(const struct pciDevice *dev,
                    const char *file,
                    bool faked)
{
    char *ret = NULL;
    const char *prefix = "";
374
    g_autofree char *devid = NULL;
375 376 377 378

    if (faked)
        prefix = fakerootdir;

379 380 381
    if (!(devid = pci_address_format(&dev->addr)))
        return NULL;

382 383 384
    /* PCI devices really do live under /sys/devices/pciDDDD:BB
     * and then they are just symlinked to /sys/bus/pci/devices/
     */
385
    if (file) {
386 387 388
        ret = g_strdup_printf("%s/sys/devices/pci%04x:%02x/%s/%s",
                              prefix, dev->addr.domain, dev->addr.bus,
                              devid, file);
389
    } else {
390 391 392
        ret = g_strdup_printf("%s/sys/devices/pci%04x:%02x/%s",
                              prefix, dev->addr.domain, dev->addr.bus,
                              devid);
393 394 395 396 397 398
    }

    return ret;
}


399 400 401 402
static void
pci_device_create_iommu(const struct pciDevice *dev,
                        const char *devid)
{
403
    struct pciIommuGroup *iommuGroup;
404
    g_autofree char *iommuPath = NULL;
405
    char tmp[256];
406
    size_t i;
407

408 409
    iommuPath = g_strdup_printf("%s/sys/kernel/iommu_groups/%d/devices/",
                                fakerootdir, dev->iommuGroup);
410 411 412 413

    if (virFileMakePath(iommuPath) < 0)
        ABORT("Unable to create: %s", iommuPath);

414 415 416
    if (g_snprintf(tmp, sizeof(tmp),
                   "../../../../devices/pci%04x:%02x/%s",
                   dev->addr.domain, dev->addr.bus, devid) < 0) {
417 418 419 420
        ABORT("@tmp overflow");
    }

    make_symlink(iommuPath, devid, tmp);
421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438

    /* pci_device_create_iommu can be called more than one for the
     * same iommuGroup. Bail out here if the iommuGroup was already
     * created beforehand. */
    for (i = 0; i < npciIommuGroups; i++) {
        if (pciIommuGroups[i]->iommu == dev->iommuGroup)
            return;
    }

    if (VIR_ALLOC_QUIET(iommuGroup) < 0)
        ABORT_OOM();

    iommuGroup->iommu = dev->iommuGroup;
    iommuGroup->nDevicesBoundToVFIO = 0; /* No device bound to VFIO by default */

    if (VIR_APPEND_ELEMENT_QUIET(pciIommuGroups, npciIommuGroups,
                                 iommuGroup) < 0)
        ABORT_OOM();
439 440 441
}


M
Michal Privoznik 已提交
442 443 444 445
static void
pci_device_new_from_stub(const struct pciDevice *data)
{
    struct pciDevice *dev;
446 447 448 449
    g_autofree char *devpath = NULL;
    g_autofree char *devsympath = NULL;
    g_autofree char *id = NULL;
    g_autofree char *devid = NULL;
450
    char *c;
451
    g_autofree char *configSrc = NULL;
A
Andrea Bolognani 已提交
452
    char tmp[256];
453
    struct stat sb;
454
    bool configSrcExists = false;
M
Michal Privoznik 已提交
455

456
    if (!(devid = pci_address_format(&data->addr)))
457 458
        ABORT_OOM();

459 460
    id = g_strdup(devid);

461 462 463 464 465 466 467 468 469 470 471
    /* Replace ':' with '-' to create the config filename from the
     * device ID. The device ID cannot be used directly as filename
     * because it contains ':' and Windows does not allow ':' in
     * filenames. */
    c = strchr(id, ':');

    while (c) {
        *c = '-';
        c = strchr(c, ':');
    }

472
    if (VIR_ALLOC_QUIET(dev) < 0)
M
Michal Privoznik 已提交
473 474
        ABORT_OOM();

475 476
    configSrc = g_strdup_printf("%s/virpcitestdata/%s.config", abs_srcdir, id);

M
Michal Privoznik 已提交
477 478
    memcpy(dev, data, sizeof(*dev));

479 480 481
    if (!(devpath = pci_device_get_path(dev, NULL, true)))
        ABORT_OOM();

M
Michal Privoznik 已提交
482 483 484
    if (virFileMakePath(devpath) < 0)
        ABORT("Unable to create: %s", devpath);

485
    if (stat(configSrc, &sb) == 0)
486 487
        configSrcExists = true;

488 489
    /* If there is a config file for the device within virpcitestdata dir,
     * symlink it. Otherwise create a dummy config file. */
490
    if (configSrcExists) {
491 492 493 494 495
        /* On success, copy @configSrc into the destination (a copy,
         * rather than a symlink, is required since we write into the
         * file, and parallel VPATH builds must not stomp on the
         * original; besides, 'make distcheck' requires the original
         * to be read-only */
496
        g_autofree char *buf = NULL;
497 498 499 500 501 502
        ssize_t len;

        if ((len = virFileReadAll(configSrc, 4096, &buf)) < 0)
            ABORT("Unable to read config file '%s'", configSrc);

        make_file(devpath, "config", buf, len);
503 504 505
    } else {
        /* If there's no config data in the virpcitestdata dir, create a dummy
         * config file */
506
        make_file(devpath, "config", "some dummy config", -1);
507
    }
M
Michal Privoznik 已提交
508

509
    if (g_snprintf(tmp, sizeof(tmp),  "0x%.4x", dev->vendor) < 0)
M
Michal Privoznik 已提交
510
        ABORT("@tmp overflow");
511
    make_file(devpath, "vendor", tmp, -1);
M
Michal Privoznik 已提交
512

513
    if (g_snprintf(tmp, sizeof(tmp),  "0x%.4x", dev->device) < 0)
M
Michal Privoznik 已提交
514
        ABORT("@tmp overflow");
515
    make_file(devpath, "device", tmp, -1);
M
Michal Privoznik 已提交
516

517
    if (g_snprintf(tmp, sizeof(tmp),  "0x%.4x", dev->klass) < 0)
518 519 520
        ABORT("@tmp overflow");
    make_file(devpath, "class", tmp, -1);

521 522
    make_file(devpath, "driver_override", NULL, -1);

523
    pci_device_create_iommu(dev, devid);
A
Andrea Bolognani 已提交
524

525 526
    if (g_snprintf(tmp, sizeof(tmp),
                   "../../../kernel/iommu_groups/%d", dev->iommuGroup) < 0) {
A
Andrea Bolognani 已提交
527 528 529 530
        ABORT("@tmp overflow");
    }
    make_symlink(devpath, "iommu_group", tmp);

531 532 533
    if (g_snprintf(tmp, sizeof(tmp),
                   "../../../devices/pci%04x:%02x/%s",
                   dev->addr.domain, dev->addr.bus, devid) < 0) {
534 535 536
        ABORT("@tmp overflow");
    }

537
    devsympath = g_strdup_printf("%s" SYSFS_PCI_PREFIX "devices", fakerootdir);
538 539 540

    make_symlink(devsympath, devid, tmp);

541
    if (dev->physfn) {
542 543 544
        if (g_snprintf(tmp, sizeof(tmp),
                       "%s%s/devices/%s", fakerootdir,
                       SYSFS_PCI_PREFIX, dev->physfn) < 0) {
545 546 547 548 549
            ABORT("@tmp overflow");
        }
        make_symlink(devpath, "physfn", tmp);
    }

550
    if (pci_device_autobind(dev) < 0)
551
        ABORT("Unable to bind: %s", devid);
552

553
    if (VIR_APPEND_ELEMENT_QUIET(pciDevices, nPCIDevices, dev) < 0)
M
Michal Privoznik 已提交
554 555 556
        ABORT_OOM();
}

557
static struct pciDevice *
558
pci_device_find_by_id(struct pciDeviceAddress const *addr)
559 560
{
    size_t i;
561
    for (i = 0; i < nPCIDevices; i++) {
562 563
        struct pciDevice *dev = pciDevices[i];

564
        if (!memcmp(&dev->addr, addr, sizeof(*addr)))
565 566 567 568 569 570 571 572 573 574
            return dev;
    }

    return NULL;
}

static struct pciDevice *
pci_device_find_by_content(const char *path)
{
    char tmp[32];
575
    struct pciDeviceAddress addr;
576

577 578
    if (pci_read_file(path, tmp, sizeof(tmp), true) < 0 ||
        pci_address_parse(&addr, tmp) < 0)
579 580
        return NULL;

581
    return pci_device_find_by_id(&addr);
582 583 584 585 586
}

static int
pci_device_autobind(struct pciDevice *dev)
{
587 588 589 590
    struct pciDriver *driver = pci_driver_find_by_driver_override(dev);

    if (!driver)
        driver = pci_driver_find_by_dev(dev);
591 592 593 594 595 596 597 598 599

    if (!driver) {
        /* No driver found. Nothing to do */
        return 0;
    }

    return pci_driver_bind(driver, dev);
}

600 601 602
static int
pci_vfio_release_iommu(struct pciDevice *device)
{
603
    g_autofree char *vfiopath = NULL;
604 605 606 607 608 609 610 611 612 613 614 615 616 617
    size_t i = 0;

    for (i = 0; i < npciIommuGroups; i++) {
        if (device->iommuGroup != pciIommuGroups[i]->iommu)
            continue;

        if (pciIommuGroups[i]->nDevicesBoundToVFIO == 0) {
            errno = EXDEV;
            return -1;
        }

        pciIommuGroups[i]->nDevicesBoundToVFIO--;

        if (!pciIommuGroups[i]->nDevicesBoundToVFIO) {
618 619 620
            vfiopath = g_strdup_printf("%s/dev/vfio/%d",
                                       fakerootdir,
                                       device->iommuGroup);
621 622 623 624 625 626 627 628 629 630 631 632 633

            if (unlink(vfiopath) < 0)
                return -1;
        }
        break;
    }

    return 0;
}

static int
pci_vfio_lock_iommu(struct pciDevice *device)
{
634
    g_autofree char *vfiopath = NULL;
635 636 637 638 639 640 641 642 643
    int ret = -1;
    size_t i = 0;
    int fd = -1;

    for (i = 0; i < npciIommuGroups; i++) {
        if (device->iommuGroup != pciIommuGroups[i]->iommu)
            continue;

        if (pciIommuGroups[i]->nDevicesBoundToVFIO == 0) {
644 645 646
            vfiopath = g_strdup_printf("%s/dev/vfio/%d",
                                       fakerootdir,
                                       device->iommuGroup);
647 648 649 650 651 652 653 654 655 656 657 658 659 660 661
            if ((fd = real_open(vfiopath, O_CREAT)) < 0)
                goto cleanup;

        }

        pciIommuGroups[i]->nDevicesBoundToVFIO++;
        break;
    }

    ret = 0;
 cleanup:
    if (fd != -1)
        real_close(fd);
    return ret;
}
662 663 664 665

/*
 * PCI Driver functions
 */
666 667 668 669 670 671 672 673 674 675 676 677
static char *
pci_driver_get_path(const struct pciDriver *driver,
                    const char *file,
                    bool faked)
{
    char *ret = NULL;
    const char *prefix = "";

    if (faked)
        prefix = fakerootdir;

    if (file) {
678 679
        ret = g_strdup_printf("%s" SYSFS_PCI_PREFIX "drivers/%s/%s",
                              prefix, driver->name, file);
680
    } else {
681 682
        ret = g_strdup_printf("%s" SYSFS_PCI_PREFIX "drivers/%s",
                              prefix, driver->name);
683 684 685 686 687 688
    }

    return ret;
}


689
static void
690
pci_driver_new(const char *name, ...)
691 692 693 694
{
    struct pciDriver *driver;
    va_list args;
    int vendor, device;
695
    g_autofree char *driverpath = NULL;
696

697 698 699 700
    if (VIR_ALLOC_QUIET(driver) < 0)
        ABORT_OOM();
    driver->name = g_strdup(name);
    if (!(driverpath = pci_driver_get_path(driver, NULL, true)))
701 702 703 704 705
        ABORT_OOM();

    if (virFileMakePath(driverpath) < 0)
        ABORT("Unable to create: %s", driverpath);

706
    va_start(args, name);
707 708 709 710 711 712 713 714 715 716 717 718 719 720

    while ((vendor = va_arg(args, int)) != -1) {
        if ((device = va_arg(args, int)) == -1)
            ABORT("Invalid vendor device pair for driver %s", name);

        if (VIR_REALLOC_N_QUIET(driver->vendor, driver->len + 1) < 0 ||
            VIR_REALLOC_N_QUIET(driver->device, driver->len + 1) < 0)
            ABORT_OOM();

        driver->vendor[driver->len] = vendor;
        driver->device[driver->len] = device;
        driver->len++;
    }

721 722
    va_end(args);

723 724
    make_file(driverpath, "bind", NULL, -1);
    make_file(driverpath, "unbind", NULL, -1);
725

726
    if (VIR_APPEND_ELEMENT_QUIET(pciDrivers, nPCIDrivers, driver) < 0)
727 728 729 730 731 732 733 734
        ABORT_OOM();
}

static struct pciDriver *
pci_driver_find_by_dev(struct pciDevice *dev)
{
    size_t i;

735
    for (i = 0; i < nPCIDrivers; i++) {
736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753
        struct pciDriver *driver = pciDrivers[i];
        size_t j;

        for (j = 0; j < driver->len; j++) {
            if (driver->vendor[j] == dev->vendor &&
                driver->device[j] == dev->device)
                return driver;
        }
    }

    return NULL;
}

static struct pciDriver *
pci_driver_find_by_path(const char *path)
{
    size_t i;

754
    for (i = 0; i < nPCIDrivers; i++) {
755 756 757 758 759 760 761 762 763
        struct pciDriver *driver = pciDrivers[i];

        if (strstr(path, driver->name))
            return driver;
    }

    return NULL;
}

764 765 766
static struct pciDriver *
pci_driver_find_by_driver_override(struct pciDevice *dev)
{
767
    g_autofree char *path = NULL;
768 769 770
    char tmp[32];
    size_t i;

771
    if (!(path = pci_device_get_path(dev, "driver_override", false)))
772 773 774 775 776 777 778 779 780 781 782 783 784 785 786
        return NULL;

    if (pci_read_file(path, tmp, sizeof(tmp), false) < 0)
        return NULL;

    for (i = 0; i < nPCIDrivers; i++) {
        struct pciDriver *driver = pciDrivers[i];

        if (STREQ(tmp, driver->name))
            return driver;
    }

    return NULL;
}

787 788 789 790
static int
pci_driver_bind(struct pciDriver *driver,
                struct pciDevice *dev)
{
791 792 793
    g_autofree char *devid = NULL;
    g_autofree char *devpath = NULL;
    g_autofree char *driverpath = NULL;
794

795 796
    if (dev->driver) {
        /* Device already bound */
797
        errno = ENODEV;
798
        return -1;
799 800 801
    }

    /* Make symlink under device tree */
802
    if (!(devpath = pci_device_get_path(dev, "driver", true)) ||
803
        !(driverpath = pci_driver_get_path(driver, NULL, true))) {
804
        errno = ENOMEM;
805
        return -1;
806 807 808
    }

    if (symlink(driverpath, devpath) < 0)
809
        return -1;
810 811 812 813

    /* Make symlink under driver tree */
    VIR_FREE(devpath);
    VIR_FREE(driverpath);
814 815 816
    if (!(devid = pci_address_format(&dev->addr)) ||
        !(devpath = pci_device_get_path(dev, NULL, true)) ||
        !(driverpath = pci_driver_get_path(driver, devid, true))) {
817
        errno = ENOMEM;
818
        return -1;
819 820 821
    }

    if (symlink(devpath, driverpath) < 0)
822
        return -1;
823

824 825 826 827
    if (STREQ(driver->name, "vfio-pci") &&
        pci_vfio_lock_iommu(dev) < 0)
        return -1;

828
    dev->driver = driver;
829
    return 0;
830 831 832 833 834 835
}

static int
pci_driver_unbind(struct pciDriver *driver,
                  struct pciDevice *dev)
{
836 837 838
    g_autofree char *devid = NULL;
    g_autofree char *devpath = NULL;
    g_autofree char *driverpath = NULL;
839

840 841
    if (dev->driver != driver) {
        /* Device not bound to the @driver */
842
        errno = ENODEV;
843
        return -1;
844 845 846
    }

    /* Make symlink under device tree */
847 848 849
    if (!(devid = pci_address_format(&dev->addr)) ||
        !(devpath = pci_device_get_path(dev, "driver", true)) ||
        !(driverpath = pci_driver_get_path(driver, devid, true))) {
850
        errno = ENOMEM;
851
        return -1;
852 853 854 855
    }

    if (unlink(devpath) < 0 ||
        unlink(driverpath) < 0)
856
        return -1;
857

858 859 860 861
    if (STREQ(driver->name, "vfio-pci") &&
        pci_vfio_release_iommu(dev) < 0)
        return -1;

862
    dev->driver = NULL;
863
    return 0;
864 865
}

866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881
static int
pci_driver_handle_drivers_probe(const char *path)
{
    struct pciDevice *dev;

    if (!(dev = pci_device_find_by_content(path))) {
        errno = ENODEV;
        return -1;
    }

    if (dev->driver)
        return 0;

    return pci_device_autobind(dev);
}

882
static int
J
Ján Tomko 已提交
883
pci_driver_handle_change(int fd G_GNUC_UNUSED, const char *path)
884 885
{
    int ret;
886
    g_autofree char *file = g_path_get_basename(path);
887

888
    if (STREQ(file, "bind"))
889
        ret = pci_driver_handle_bind(path);
890
    else if (STREQ(file, "unbind"))
891
        ret = pci_driver_handle_unbind(path);
892 893
    else if (STREQ(file, "drivers_probe"))
        ret = pci_driver_handle_drivers_probe(path);
894 895
    else if (STREQ(file, "driver_override"))
        ret = 0; /* nada */
896
    else
897 898 899 900 901 902 903 904 905 906
        ABORT("Not handled write to: %s", path);
    return ret;
}

static int
pci_driver_handle_bind(const char *path)
{
    struct pciDevice *dev = pci_device_find_by_content(path);
    struct pciDriver *driver = pci_driver_find_by_path(path);

907
    if (!driver || !dev) {
908
        /* No driver, no device or failing driver requested */
909
        errno = ENODEV;
910
        return -1;
911 912
    }

913
    return pci_driver_bind(driver, dev);
914 915 916 917 918 919 920
}

static int
pci_driver_handle_unbind(const char *path)
{
    struct pciDevice *dev = pci_device_find_by_content(path);

921
    if (!dev || !dev->driver) {
922
        /* No device, device not binded or failing driver requested */
923
        errno = ENODEV;
924
        return -1;
925 926
    }

927
    return pci_driver_unbind(dev->driver, dev);
928
}
929

M
Michal Privoznik 已提交
930 931 932 933 934 935 936

/*
 * Functions to load the symbols and init the environment
 */
static void
init_syms(void)
{
937
    if (real_access)
M
Michal Privoznik 已提交
938 939
        return;

940 941
    VIR_MOCK_REAL_INIT(access);
    VIR_MOCK_REAL_INIT(open);
942
# ifdef __GLIBC__
M
Michal Privoznik 已提交
943
    VIR_MOCK_REAL_INIT(__open_2);
944
# endif /* ! __GLIBC__ */
945 946
    VIR_MOCK_REAL_INIT(close);
    VIR_MOCK_REAL_INIT(opendir);
947
    VIR_MOCK_REAL_INIT(virFileCanonicalizePath);
M
Michal Privoznik 已提交
948 949 950 951 952
}

static void
init_env(void)
{
953
    g_autofree char *tmp = NULL;
954

955 956
    if (!(fakerootdir = getenv("LIBVIRT_FAKE_ROOT_DIR")))
        ABORT("Missing LIBVIRT_FAKE_ROOT_DIR env variable\n");
M
Michal Privoznik 已提交
957

958
    tmp = g_strdup_printf("%s%s", fakerootdir, SYSFS_PCI_PREFIX);
959

960 961
    if (virFileMakePath(tmp) < 0)
        ABORT("Unable to create: %s", tmp);
962

963 964
    make_dir(tmp, "devices");
    make_dir(tmp, "drivers");
965
    make_file(tmp, "drivers_probe", NULL, -1);
966

967 968
    /* Create /dev/vfio/ dir and /dev/vfio/vfio file */
    VIR_FREE(tmp);
969
    tmp = g_strdup_printf("%s/dev/vfio", fakerootdir);
970 971 972 973 974 975

    if (virFileMakePath(tmp) < 0)
        ABORT("Unable to create: %s", tmp);

    make_file(tmp, "vfio", NULL, -1);

976
# define MAKE_PCI_DRIVER(name, ...) \
977
    pci_driver_new(name, __VA_ARGS__, -1, -1)
978 979 980

    MAKE_PCI_DRIVER("iwlwifi", 0x8086, 0x0044);
    MAKE_PCI_DRIVER("i915", 0x8086, 0x0046, 0x8086, 0x0047);
981
    MAKE_PCI_DRIVER("vfio-pci", -1, -1);
982
    MAKE_PCI_DRIVER("nvme", 0x1cc1, 0x8201);
983

984
# define MAKE_PCI_DEVICE(Id, Vendor, Device, IommuGroup, ...) \
985
    do { \
986
        struct pciDevice dev = {.vendor = Vendor, \
987 988
                                .device = Device, \
                                .iommuGroup = IommuGroup, __VA_ARGS__}; \
989 990
        if (pci_address_parse(&dev.addr, Id) < 0) \
            ABORT("Unable to parse PCI address " Id); \
991
        pci_device_new_from_stub(&dev); \
M
Michal Privoznik 已提交
992 993
    } while (0)

994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008
    MAKE_PCI_DEVICE("0000:00:00.0", 0x8086, 0x0044, 0);
    MAKE_PCI_DEVICE("0000:00:01.0", 0x8086, 0x0044, 1);
    MAKE_PCI_DEVICE("0000:00:02.0", 0x8086, 0x0046, 2);
    MAKE_PCI_DEVICE("0000:00:03.0", 0x8086, 0x0048, 3);
    MAKE_PCI_DEVICE("0001:00:00.0", 0x1014, 0x03b9, 4, .klass = 0x060400);
    MAKE_PCI_DEVICE("0001:01:00.0", 0x8086, 0x105e, 5);
    MAKE_PCI_DEVICE("0001:01:00.1", 0x8086, 0x105e, 5);
    MAKE_PCI_DEVICE("0005:80:00.0", 0x10b5, 0x8112, 6, .klass = 0x060400);
    MAKE_PCI_DEVICE("0005:90:01.0", 0x1033, 0x0035, 7);
    MAKE_PCI_DEVICE("0005:90:01.1", 0x1033, 0x0035, 7);
    MAKE_PCI_DEVICE("0005:90:01.2", 0x1033, 0x00e0, 7);
    MAKE_PCI_DEVICE("0005:90:01.3", 0x1033, 0x00e0, 7);
    MAKE_PCI_DEVICE("0000:0a:01.0", 0x8086, 0x0047, 8);
    MAKE_PCI_DEVICE("0000:0a:02.0", 0x8286, 0x0048, 8);
    MAKE_PCI_DEVICE("0000:0a:03.0", 0x8386, 0x0048, 8);
1009 1010 1011 1012 1013 1014 1015 1016 1017
    MAKE_PCI_DEVICE("0000:06:12.0", 0x8086, 0x0047, 9);
    MAKE_PCI_DEVICE("0000:06:12.1", 0x8086, 0x0047, 10,
                    .physfn = "0000:06:12.0"); /* Virtual Function */
    MAKE_PCI_DEVICE("0000:06:12.2", 0x8086, 0x0047, 11,
                    .physfn = "0000:06:12.0"); /* Virtual Function */
    MAKE_PCI_DEVICE("0021:de:1f.0", 0x8086, 0x0047, 12);
    MAKE_PCI_DEVICE("0021:de:1f.1", 0x8086, 0x0047, 13,
                    .physfn = "0021:de:1f.0"); /* Virtual Function */

1018 1019
    MAKE_PCI_DEVICE("0000:01:00.0", 0x1cc1, 0x8201, 14, .klass = 0x010802);
    MAKE_PCI_DEVICE("0000:02:00.0", 0x1cc1, 0x8201, 15, .klass = 0x010802);
M
Michal Privoznik 已提交
1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031
}


/*
 *
 * Mocked functions
 *
 */

int
access(const char *path, int mode)
{
1032
    g_autofree char *newpath = NULL;
M
Michal Privoznik 已提交
1033 1034 1035

    init_syms();

1036
    if (getrealpath(&newpath, path) < 0)
1037 1038
        return -1;

1039
    return real_access(newpath, mode);
M
Michal Privoznik 已提交
1040 1041 1042
}


1043 1044
static int
virMockStatRedirect(const char *path, char **newpath)
1045
{
1046 1047 1048
    if (getrealpath(newpath, path) < 0)
        return -1;

1049
    return 0;
1050 1051 1052
}


M
Michal Privoznik 已提交
1053 1054 1055 1056
int
open(const char *path, int flags, ...)
{
    int ret;
1057
    g_autofree char *newpath = NULL;
M
Michal Privoznik 已提交
1058 1059 1060

    init_syms();

1061
    if (getrealpath(&newpath, path) < 0)
M
Michal Privoznik 已提交
1062 1063 1064 1065 1066 1067
        return -1;

    if (flags & O_CREAT) {
        va_list ap;
        mode_t mode;
        va_start(ap, flags);
1068
        mode = (mode_t) va_arg(ap, int);
M
Michal Privoznik 已提交
1069
        va_end(ap);
1070
        ret = real_open(newpath, flags, mode);
M
Michal Privoznik 已提交
1071
    } else {
1072
        ret = real_open(newpath, flags);
M
Michal Privoznik 已提交
1073
    }
1074 1075 1076

    /* Catch both: /sys/bus/pci/drivers/... and
     * /sys/bus/pci/device/.../driver/... */
1077
    if (ret >= 0 && STRPREFIX(path, SYSFS_PCI_PREFIX) &&
1078
        strstr(path, "driver") && add_fd(ret, path) < 0) {
1079
        real_close(ret);
1080 1081 1082
        ret = -1;
    }

M
Michal Privoznik 已提交
1083 1084 1085
    return ret;
}

M
Michal Privoznik 已提交
1086

1087
# ifdef __GLIBC__
1088 1089 1090 1091 1092
/* in some cases this function may not be present in headers, so we need
 * a declaration to silence the complier */
int
__open_2(const char *path, int flags);

M
Michal Privoznik 已提交
1093 1094 1095
int
__open_2(const char *path, int flags)
{
1096
    g_autofree char *newpath = NULL;
M
Michal Privoznik 已提交
1097 1098 1099 1100
    int ret;

    init_syms();

1101
    if (getrealpath(&newpath, path) < 0)
M
Michal Privoznik 已提交
1102 1103
        return -1;

1104
    ret = real___open_2(newpath, flags);
M
Michal Privoznik 已提交
1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115

    /* Catch both: /sys/bus/pci/drivers/... and
     * /sys/bus/pci/device/.../driver/... */
    if (ret >= 0 && STRPREFIX(path, SYSFS_PCI_PREFIX) &&
        strstr(path, "driver") && add_fd(ret, path) < 0) {
        real_close(ret);
        ret = -1;
    }

    return ret;
}
1116
# endif /* ! __GLIBC__ */
M
Michal Privoznik 已提交
1117

1118 1119 1120
DIR *
opendir(const char *path)
{
1121
    g_autofree char *newpath = NULL;
1122 1123 1124

    init_syms();

1125
    if (getrealpath(&newpath, path) < 0)
1126 1127
        return NULL;

1128
    return real_opendir(newpath);
1129 1130
}

1131 1132 1133 1134 1135
int
close(int fd)
{
    if (remove_fd(fd) < 0)
        return -1;
1136
    return real_close(fd);
1137
}
1138 1139 1140 1141

char *
virFileCanonicalizePath(const char *path)
{
1142
    g_autofree char *newpath = NULL;
1143 1144 1145

    init_syms();

1146
    if (getrealpath(&newpath, path) < 0)
1147
        return NULL;
1148

1149
    return real_virFileCanonicalizePath(newpath);
1150
}
1151 1152 1153

# include "virmockstathelpers.c"

M
Michal Privoznik 已提交
1154
#else
1155
/* Nothing to override on this platform */
M
Michal Privoznik 已提交
1156
#endif