domaincapstest.c 13.4 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 21 22
/*
 * Copyright (C) Red Hat, Inc. 2014
 *
 * 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>

#include "testutils.h"
#include "domain_capabilities.h"
23 24
#include "virfilewrapper.h"
#include "configmake.h"
M
Michal Privoznik 已提交
25 26 27 28


#define VIR_FROM_THIS VIR_FROM_NONE

29
#if WITH_QEMU || WITH_BHYVE
30
static int G_GNUC_NULL_TERMINATED
31 32 33 34 35 36 37 38
fillStringValues(virDomainCapsStringValuesPtr values, ...)
{
    int ret = 0;
    va_list list;
    const char *str;

    va_start(list, values);
    while ((str = va_arg(list, const char *))) {
39
        if (VIR_REALLOC_N(values->values, values->nvalues + 1) < 0) {
40 41 42
            ret = -1;
            break;
        }
43
        values->values[values->nvalues] = g_strdup(str);
44 45 46 47 48 49
        values->nvalues++;
    }
    va_end(list);

    return ret;
}
50
#endif /* WITH_QEMU || WITH_BHYVE */
51

J
Jiri Denemark 已提交
52
#if WITH_QEMU
53
# include "testutilsqemu.h"
P
Pavel Hrdina 已提交
54
# include "testutilshostcpus.h"
55

56
static int
57
fakeHostCPU(virArch arch)
58
{
59
    g_autoptr(virCPUDef) cpu = NULL;
60

P
Pavel Hrdina 已提交
61
    if (!(cpu = testUtilsHostCpusGetDefForArch(arch))) {
62 63 64 65 66 67
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       "cannot fake host CPU for arch %s",
                       virArchToString(arch));
        return -1;
    }

68
    qemuTestSetHostCPU(NULL, arch, cpu);
69 70 71 72

    return 0;
}

73
static int
74
fillQemuCaps(virDomainCapsPtr domCaps,
J
Jiri Denemark 已提交
75
             const char *name,
J
Jiri Denemark 已提交
76
             const char *arch,
77
             const char *machine,
J
Jiri Denemark 已提交
78
             virQEMUDriverConfigPtr cfg)
79
{
J
Jiri Denemark 已提交
80 81 82
    int ret = -1;
    char *path = NULL;
    virQEMUCapsPtr qemuCaps = NULL;
83
    virDomainCapsLoaderPtr loader = &domCaps->os.loader;
84
    virDomainVirtType virtType;
85

86
    if (fakeHostCPU(domCaps->arch) < 0)
87 88
        goto cleanup;

89
    path = g_strdup_printf("%s/%s.%s.xml", TEST_QEMU_CAPS_PATH, name, arch);
90
    if (!(qemuCaps = qemuTestParseCapabilitiesArch(domCaps->arch, path)))
J
Jiri Denemark 已提交
91 92
        goto cleanup;

93 94 95 96 97
    if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_KVM))
        virtType = VIR_DOMAIN_VIRT_KVM;
    else
        virtType = VIR_DOMAIN_VIRT_QEMU;

98 99
    if (machine) {
        VIR_FREE(domCaps->machine);
100
        domCaps->machine = g_strdup(virQEMUCapsGetCanonicalMachine(qemuCaps, virtType, machine));
101
    }
102

103
    if (!domCaps->machine)
104
        domCaps->machine = g_strdup(virQEMUCapsGetPreferredMachine(qemuCaps, virtType));
105

106
    if (virQEMUCapsFillDomainCaps(qemuCaps, domCaps->arch, domCaps,
107
                                  false,
108
                                  cfg->firmwares,
109
                                  cfg->nfirmwares) < 0)
J
Jiri Denemark 已提交
110
        goto cleanup;
111

M
Michal Privoznik 已提交
112
    /* The function above tries to query host's VFIO capabilities by calling
113 114 115 116 117 118
     * qemuHostdevHostSupportsPassthroughVFIO() which, however, can't be
     * successfully mocked as they are not exposed as internal APIs. Therefore,
     * instead of mocking set the expected values here by hand. */
    VIR_DOMAIN_CAPS_ENUM_SET(domCaps->hostdev.pciBackend,
                             VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT,
                             VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO);
119

J
Jiri Denemark 已提交
120 121
    /* As of f05b6a918e28 we are expecting to see OVMF_CODE.fd file which
     * may not exists everywhere. */
122 123 124 125
    while (loader->values.nvalues)
        VIR_FREE(loader->values.values[--loader->values.nvalues]);

    if (fillStringValues(&loader->values,
126
                         "/usr/share/AAVMF/AAVMF_CODE.fd",
127
                         "/usr/share/AAVMF/AAVMF32_CODE.fd",
128 129
                         "/usr/share/OVMF/OVMF_CODE.fd",
                         NULL) < 0)
M
Michal Privoznik 已提交
130 131
        goto cleanup;

J
Jiri Denemark 已提交
132
    ret = 0;
M
Michal Privoznik 已提交
133
 cleanup:
J
Jiri Denemark 已提交
134 135
    virObjectUnref(qemuCaps);
    VIR_FREE(path);
136
    return ret;
M
Michal Privoznik 已提交
137
}
J
Jiri Denemark 已提交
138 139 140
#endif /* WITH_QEMU */


141 142 143 144 145 146 147 148 149 150 151 152 153 154
#ifdef WITH_LIBXL
# include "testutilsxen.h"

static int
fillXenCaps(virDomainCapsPtr domCaps)
{
    virFirmwarePtr *firmwares;
    int ret = -1;

    if (VIR_ALLOC_N(firmwares, 2) < 0)
        return ret;

    if (VIR_ALLOC(firmwares[0]) < 0 || VIR_ALLOC(firmwares[1]) < 0)
        goto cleanup;
155 156
    firmwares[0]->name = g_strdup("/usr/lib/xen/boot/hvmloader");
    firmwares[1]->name = g_strdup("/usr/lib/xen/boot/ovmf.bin");
157 158 159 160 161 162 163 164 165 166 167 168

    if (libxlMakeDomainCapabilities(domCaps, firmwares, 2) < 0)
        goto cleanup;

    ret = 0;

 cleanup:
    virFirmwareFreeList(firmwares, 2);
    return ret;
}
#endif /* WITH_LIBXL */

169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
#ifdef WITH_BHYVE
# include "bhyve/bhyve_capabilities.h"

static int
fillBhyveCaps(virDomainCapsPtr domCaps, unsigned int *bhyve_caps)
{
    virDomainCapsStringValuesPtr firmwares = NULL;
    int ret = -1;

    if (VIR_ALLOC(firmwares) < 0)
        return -1;

    if (fillStringValues(firmwares, "/foo/bar", "/foo/baz", NULL) < 0)
        goto cleanup;

    if (virBhyveDomainCapsFill(domCaps, *bhyve_caps, firmwares) < 0)
        goto cleanup;

    ret = 0;
 cleanup:
    VIR_FREE(firmwares);
    return ret;
}
#endif /* WITH_BHYVE */
193

J
Jiri Denemark 已提交
194 195 196
enum testCapsType {
    CAPS_NONE,
    CAPS_QEMU,
197
    CAPS_LIBXL,
198
    CAPS_BHYVE,
J
Jiri Denemark 已提交
199
};
M
Michal Privoznik 已提交
200

J
Jiri Denemark 已提交
201 202 203
struct testData {
    const char *name;
    const char *emulator;
M
Michal Privoznik 已提交
204
    const char *machine;
J
Jiri Denemark 已提交
205
    const char *arch;
M
Michal Privoznik 已提交
206
    virDomainVirtType type;
J
Jiri Denemark 已提交
207 208 209
    enum testCapsType capsType;
    const char *capsName;
    void *capsOpaque;
M
Michal Privoznik 已提交
210 211 212 213 214
};

static int
test_virDomainCapsFormat(const void *opaque)
{
J
Jiri Denemark 已提交
215
    const struct testData *data = opaque;
M
Michal Privoznik 已提交
216 217 218 219 220
    virDomainCapsPtr domCaps = NULL;
    char *path = NULL;
    char *domCapsXML = NULL;
    int ret = -1;

221
    path = g_strdup_printf("%s/domaincapsdata/%s.xml", abs_srcdir, data->name);
M
Michal Privoznik 已提交
222

J
Jiri Denemark 已提交
223 224
    if (!(domCaps = virDomainCapsNew(data->emulator, data->machine,
                                     virArchFromString(data->arch),
J
Jiri Denemark 已提交
225
                                     data->type)))
M
Michal Privoznik 已提交
226 227
        goto cleanup;

J
Jiri Denemark 已提交
228 229 230 231 232 233
    switch (data->capsType) {
    case CAPS_NONE:
        break;

    case CAPS_QEMU:
#if WITH_QEMU
234
        if (fillQemuCaps(domCaps, data->capsName, data->arch, data->machine,
235
                         data->capsOpaque) < 0)
J
Jiri Denemark 已提交
236
            goto cleanup;
237 238 239 240 241 242 243
#endif
        break;

    case CAPS_LIBXL:
#if WITH_LIBXL
        if (fillXenCaps(domCaps) < 0)
            goto cleanup;
244 245 246 247 248 249
#endif
        break;
    case CAPS_BHYVE:
#if WITH_BHYVE
        if (fillBhyveCaps(domCaps, data->capsOpaque) < 0)
            goto cleanup;
J
Jiri Denemark 已提交
250 251 252 253
#endif
        break;
    }

M
Michal Privoznik 已提交
254 255 256
    if (!(domCapsXML = virDomainCapsFormat(domCaps)))
        goto cleanup;

257
    if (virTestCompareToFile(domCapsXML, path) < 0)
M
Michal Privoznik 已提交
258 259 260 261 262 263 264 265 266 267
        goto cleanup;

    ret = 0;
 cleanup:
    VIR_FREE(domCapsXML);
    VIR_FREE(path);
    virObjectUnref(domCaps);
    return ret;
}

268 269 270

#if WITH_QEMU

271
static int
272 273 274 275 276
doTestQemuInternal(const char *version,
                   const char *machine,
                   const char *arch,
                   virDomainVirtType type,
                   void *opaque)
277 278
{
    g_autofree char *name = NULL;
279 280
    g_autofree char *capsName = NULL;
    g_autofree char *emulator = NULL;
281

282 283 284 285 286 287 288
    name = g_strdup_printf("qemu_%s%s%s%s.%s",
                           version,
                           (type == VIR_DOMAIN_VIRT_QEMU ? "-tcg" : ""),
                           (machine ? "-" : ""), (machine ? machine : ""),
                           arch);
    capsName = g_strdup_printf("caps_%s", version);
    emulator = g_strdup_printf("/usr/bin/qemu-system-%s", arch);
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306

    struct testData data = {
        .name = name,
        .emulator = emulator,
        .machine = machine,
        .arch = arch,
        .type = type,
        .capsType = CAPS_QEMU,
        .capsName = capsName,
        .capsOpaque = opaque,
    };

    if (virTestRun(name, test_virDomainCapsFormat, &data) < 0)
        return -1;

    return 0;
}

307
static int
308 309 310
doTestQemu(const char *inputDir G_GNUC_UNUSED,
           const char *prefix G_GNUC_UNUSED,
           const char *version,
311
           const char *arch,
312
           const char *suffix G_GNUC_UNUSED,
313 314
           void *opaque)
{
315 316
    int ret = 0;

317 318 319 320 321 322 323 324
    if (STREQ(arch, "x86_64")) {
        /* For x86_64 we test three combinations:
         *
         *   - KVM with default machine
         *   - KVM with Q35 machine
         *   - TCG with default machine
         */
        if (doTestQemuInternal(version, NULL, arch,
325 326 327 328 329 330 331 332 333 334
                               VIR_DOMAIN_VIRT_KVM, opaque) < 0)
            ret = -1;

        if (doTestQemuInternal(version, "q35", arch,
                               VIR_DOMAIN_VIRT_KVM, opaque) < 0)
            ret = -1;

        if (doTestQemuInternal(version, NULL, arch,
                               VIR_DOMAIN_VIRT_QEMU, opaque) < 0)
            ret = -1;
335 336 337 338 339 340 341
    } else if (STREQ(arch, "aarch64")) {
        /* For aarch64 we test two combinations:
         *
         *   - KVM with default machine
         *   - KVM with virt machine
         */
        if (doTestQemuInternal(version, NULL, arch,
342 343 344 345 346 347
                               VIR_DOMAIN_VIRT_KVM, opaque) < 0)
            ret = -1;

        if (doTestQemuInternal(version, "virt", arch,
                               VIR_DOMAIN_VIRT_KVM, opaque) < 0)
            ret = -1;
348 349 350 351 352 353
    } else if (STRPREFIX(arch, "riscv")) {
        /* Unfortunately we have to skip RISC-V at the moment */
        return 0;
    } else {
        if (doTestQemuInternal(version, NULL, arch,
                               VIR_DOMAIN_VIRT_KVM, opaque) < 0)
354
            ret = -1;
355 356
    }

357
    return ret;
358 359
}

360 361
#endif

M
Michal Privoznik 已提交
362 363 364 365 366
static int
mymain(void)
{
    int ret = 0;

367 368 369 370
#if WITH_BHYVE
    unsigned int bhyve_caps = 0;
#endif

J
Jiri Denemark 已提交
371
#if WITH_QEMU
372 373
    virQEMUDriverConfigPtr cfg = virQEMUDriverConfigNew(false);

374 375
    if (!cfg)
        return EXIT_FAILURE;
J
Jiri Denemark 已提交
376 377
#endif

378 379 380 381 382 383 384 385 386 387 388 389
#define DO_TEST(Name, Emulator, Machine, Arch, Type, CapsType) \
    do { \
        struct testData data = { \
            .name = Name, \
            .emulator = Emulator, \
            .machine = Machine, \
            .arch = Arch, \
            .type = Type, \
            .capsType = CapsType, \
        }; \
        if (virTestRun(Name, test_virDomainCapsFormat, &data) < 0) \
            ret = -1; \
J
Jiri Denemark 已提交
390
    } while (0)
391

392 393 394 395 396 397 398 399 400 401 402 403
#define DO_TEST_LIBXL(Name, Emulator, Machine, Arch, Type) \
    do { \
        struct testData data = { \
            .name = Name, \
            .emulator = Emulator, \
            .machine = Machine, \
            .arch = Arch, \
            .type = Type, \
            .capsType = CAPS_LIBXL, \
        }; \
        if (virTestRun(Name, test_virDomainCapsFormat, &data) < 0) \
            ret = -1; \
404 405
    } while (0)

406
#define DO_TEST_BHYVE(Name, Emulator, BhyveCaps, Type) \
407 408
    do { \
        char *name = NULL; \
409
        name = g_strdup_printf("bhyve_%s.x86_64", Name); \
410 411 412 413 414 415 416 417 418 419 420
        struct testData data = { \
            .name = name, \
            .emulator = Emulator, \
            .arch = "x86_64", \
            .type = Type, \
            .capsType = CAPS_BHYVE, \
            .capsOpaque = BhyveCaps, \
        }; \
        if (virTestRun(name, test_virDomainCapsFormat, &data) < 0) \
            ret = -1; \
        VIR_FREE(name); \
421 422
    } while (0)

423 424 425
    DO_TEST("empty", "/bin/emulatorbin", "my-machine-type",
            "x86_64", VIR_DOMAIN_VIRT_KVM, CAPS_NONE);

J
Jiri Denemark 已提交
426 427
#if WITH_QEMU

428 429 430 431 432 433 434
    virFileWrapperAddPrefix(SYSCONFDIR "/qemu/firmware",
                            abs_srcdir "/qemufirmwaredata/etc/qemu/firmware");
    virFileWrapperAddPrefix(PREFIX "/share/qemu/firmware",
                            abs_srcdir "/qemufirmwaredata/usr/share/qemu/firmware");
    virFileWrapperAddPrefix("/home/user/.config/qemu/firmware",
                            abs_srcdir "/qemufirmwaredata/home/user/.config/qemu/firmware");

435
    if (testQemuCapsIterate(".xml", doTestQemu, cfg) < 0)
436
        ret = -1;
437

438 439 440 441 442 443 444 445 446 447 448 449 450 451 452
    /*
     * Run "tests/qemucapsprobe /path/to/qemu/binary >foo.replies"
     * to generate updated or new *.replies data files.
     *
     * If you manually edit replies files you can run
     * "tests/qemucapsfixreplies foo.replies" to fix the replies ids.
     *
     * Once a replies file has been generated and tweaked if necessary,
     * you can drop it into tests/qemucapabilitiesdata/ (with a sensible
     * name - look at what's already there for inspiration) and test
     * programs will automatically pick it up.
     *
     * To generate the corresponding output files after a new replies
     * file has been added, run "VIR_TEST_REGENERATE_OUTPUT=1 make check".
     */
453

P
Pavel Hrdina 已提交
454 455
    virObjectUnref(cfg);

456
    virFileWrapperClearPrefixes();
457

458 459
#endif /* WITH_QEMU */

460 461
#if WITH_LIBXL

462
    DO_TEST_LIBXL("libxl-xenpv", "/usr/bin/qemu-system-x86_64",
463
                  "xenpv", "x86_64", VIR_DOMAIN_VIRT_XEN);
464
    DO_TEST_LIBXL("libxl-xenfv", "/usr/bin/qemu-system-x86_64",
465 466 467 468
                  "xenfv", "x86_64", VIR_DOMAIN_VIRT_XEN);

#endif /* WITH_LIBXL */

469 470 471 472 473 474 475 476 477 478
#if WITH_BHYVE
    DO_TEST_BHYVE("basic", "/usr/sbin/bhyve", &bhyve_caps, VIR_DOMAIN_VIRT_BHYVE);

    bhyve_caps |= BHYVE_CAP_LPC_BOOTROM;
    DO_TEST_BHYVE("uefi", "/usr/sbin/bhyve", &bhyve_caps, VIR_DOMAIN_VIRT_BHYVE);

    bhyve_caps |= BHYVE_CAP_FBUF;
    DO_TEST_BHYVE("fbuf", "/usr/sbin/bhyve", &bhyve_caps, VIR_DOMAIN_VIRT_BHYVE);
#endif /* WITH_BHYVE */

M
Michal Privoznik 已提交
479 480 481
    return ret;
}

482
#if WITH_QEMU
483
VIR_TEST_MAIN_PRELOAD(mymain,
484 485
                      VIR_TEST_MOCK("domaincaps"),
                      VIR_TEST_MOCK("qemucpu"))
486
#else
487
VIR_TEST_MAIN_PRELOAD(mymain, VIR_TEST_MOCK("domaincaps"))
488
#endif