/* * cputest.c: Test the libvirtd internal CPU APIs * * Copyright (C) 2010-2014 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 * . * * Author: Jiri Denemark */ #include #include #include #include #include #include #include #include "internal.h" #include "virxml.h" #include "viralloc.h" #include "virbuffer.h" #include "testutils.h" #include "cpu_conf.h" #include "cpu/cpu.h" #include "cpu/cpu_map.h" #include "virstring.h" #if WITH_QEMU && WITH_YAJL # include "testutilsqemu.h" # include "qemumonitortestutils.h" # define __QEMU_CAPSPRIV_H_ALLOW__ # include "qemu/qemu_capspriv.h" # undef __QEMU_CAPSPRIV_H_ALLOW__ #endif #define VIR_FROM_THIS VIR_FROM_CPU enum cpuTestBoolWithError { FAIL = -1, NO = 0, YES = 1 }; struct data { virArch arch; const char *host; const char *name; virDomainCapsCPUModelsPtr models; const char *modelsName; unsigned int flags; int result; }; #if WITH_QEMU && WITH_YAJL static virQEMUDriver driver; #endif static virCPUDefPtr cpuTestLoadXML(virArch arch, const char *name) { char *xml = NULL; xmlDocPtr doc = NULL; xmlXPathContextPtr ctxt = NULL; virCPUDefPtr cpu = NULL; if (virAsprintf(&xml, "%s/cputestdata/%s-%s.xml", abs_srcdir, virArchToString(arch), name) < 0) goto cleanup; if (!(doc = virXMLParseFileCtxt(xml, &ctxt))) goto cleanup; virCPUDefParseXML(ctxt, NULL, VIR_CPU_TYPE_AUTO, &cpu); cleanup: xmlXPathFreeContext(ctxt); xmlFreeDoc(doc); VIR_FREE(xml); return cpu; } static virCPUDefPtr * cpuTestLoadMultiXML(virArch arch, const char *name, unsigned int *count) { char *xml = NULL; xmlDocPtr doc = NULL; xmlXPathContextPtr ctxt = NULL; xmlNodePtr *nodes = NULL; virCPUDefPtr *cpus = NULL; int n; size_t i; if (virAsprintf(&xml, "%s/cputestdata/%s-%s.xml", abs_srcdir, virArchToString(arch), name) < 0) goto cleanup; if (!(doc = virXMLParseFileCtxt(xml, &ctxt))) goto cleanup; n = virXPathNodeSet("/cpuTest/cpu", ctxt, &nodes); if (n <= 0 || (VIR_ALLOC_N(cpus, n) < 0)) { fprintf(stderr, "\nNo /cpuTest/cpu elements found in %s\n", xml); goto cleanup; } for (i = 0; i < n; i++) { ctxt->node = nodes[i]; if (virCPUDefParseXML(ctxt, NULL, VIR_CPU_TYPE_HOST, &cpus[i]) < 0) goto cleanup_cpus; } *count = n; cleanup: VIR_FREE(xml); VIR_FREE(nodes); xmlXPathFreeContext(ctxt); xmlFreeDoc(doc); return cpus; cleanup_cpus: for (i = 0; i < n; i++) virCPUDefFree(cpus[i]); VIR_FREE(cpus); goto cleanup; } static int cpuTestCompareXML(virArch arch, virCPUDef *cpu, const char *name) { char *xml = NULL; char *actual = NULL; int ret = -1; if (virAsprintf(&xml, "%s/cputestdata/%s-%s.xml", abs_srcdir, virArchToString(arch), name) < 0) goto cleanup; if (!(actual = virCPUDefFormat(cpu, NULL))) goto cleanup; if (virTestCompareToFile(actual, xml) < 0) goto cleanup; ret = 0; cleanup: VIR_FREE(xml); VIR_FREE(actual); return ret; } static const char * cpuTestCompResStr(virCPUCompareResult result) { switch (result) { case VIR_CPU_COMPARE_ERROR: return "ERROR"; case VIR_CPU_COMPARE_INCOMPATIBLE: return "INCOMPATIBLE"; case VIR_CPU_COMPARE_IDENTICAL: return "IDENTICAL"; case VIR_CPU_COMPARE_SUPERSET: return "SUPERSET"; case VIR_CPU_COMPARE_LAST: break; } return "unknown"; } static const char * cpuTestBoolWithErrorStr(enum cpuTestBoolWithError result) { switch (result) { case FAIL: return "FAIL"; case NO: return "NO"; case YES: return "YES"; } return "unknown"; } static int cpuTestCompare(const void *arg) { const struct data *data = arg; int ret = -1; virCPUDefPtr host = NULL; virCPUDefPtr cpu = NULL; virCPUCompareResult result; if (!(host = cpuTestLoadXML(data->arch, data->host)) || !(cpu = cpuTestLoadXML(data->arch, data->name))) goto cleanup; result = virCPUCompare(host->arch, host, cpu, false); if (data->result == VIR_CPU_COMPARE_ERROR) virResetLastError(); if (data->result != result) { VIR_TEST_VERBOSE("\nExpected result %s, got %s\n", cpuTestCompResStr(data->result), cpuTestCompResStr(result)); /* Pad to line up with test name ... in virTestRun */ VIR_TEST_VERBOSE("%74s", "... "); goto cleanup; } ret = 0; cleanup: virCPUDefFree(host); virCPUDefFree(cpu); return ret; } static int cpuTestGuestCPU(const void *arg) { const struct data *data = arg; int ret = -2; virCPUDefPtr host = NULL; virCPUDefPtr cpu = NULL; virCPUCompareResult cmpResult; virBuffer buf = VIR_BUFFER_INITIALIZER; char *result = NULL; if (!(host = cpuTestLoadXML(data->arch, data->host)) || !(cpu = cpuTestLoadXML(data->arch, data->name))) goto cleanup; if (virCPUConvertLegacy(host->arch, cpu) < 0) goto cleanup; cmpResult = virCPUCompare(host->arch, host, cpu, false); if (cmpResult == VIR_CPU_COMPARE_ERROR || cmpResult == VIR_CPU_COMPARE_INCOMPATIBLE) { ret = -1; goto cleanup; } if (virCPUUpdate(host->arch, cpu, host) < 0 || virCPUTranslate(host->arch, cpu, data->models) < 0) { ret = -1; goto cleanup; } virBufferAsprintf(&buf, "%s+%s", data->host, data->name); if (data->modelsName) virBufferAsprintf(&buf, ",%s", data->modelsName); virBufferAddLit(&buf, "-result"); if (virBufferError(&buf)) { virBufferFreeAndReset(&buf); goto cleanup; } result = virBufferContentAndReset(&buf); if (cpuTestCompareXML(data->arch, cpu, result) < 0) goto cleanup; ret = 0; cleanup: VIR_FREE(result); virCPUDefFree(host); virCPUDefFree(cpu); if (ret == data->result) { /* We got the result we expected, whether it was * a success or a failure */ virResetLastError(); ret = 0; } else { VIR_TEST_VERBOSE("\nExpected result %d, got %d\n", data->result, ret); /* Pad to line up with test name ... in virTestRun */ VIR_TEST_VERBOSE("%74s", "... "); ret = -1; } return ret; } static int cpuTestBaseline(const void *arg) { const struct data *data = arg; int ret = -1; virCPUDefPtr *cpus = NULL; virCPUDefPtr baseline = NULL; unsigned int ncpus = 0; char *result = NULL; const char *suffix; size_t i; if (!(cpus = cpuTestLoadMultiXML(data->arch, data->name, &ncpus))) goto cleanup; baseline = cpuBaseline(cpus, ncpus, NULL, !!(data->flags & VIR_CONNECT_BASELINE_CPU_MIGRATABLE)); if (baseline && (data->flags & VIR_CONNECT_BASELINE_CPU_EXPAND_FEATURES) && virCPUExpandFeatures(data->arch, baseline) < 0) { virCPUDefFree(baseline); baseline = NULL; } if (data->result < 0) { virResetLastError(); if (!baseline) { ret = 0; } else { VIR_TEST_VERBOSE("\n%-70s... ", "cpuBaseline was expected to fail but it succeeded"); } goto cleanup; } if (!baseline) goto cleanup; if (data->flags & VIR_CONNECT_BASELINE_CPU_EXPAND_FEATURES) suffix = "expanded"; else if (data->flags & VIR_CONNECT_BASELINE_CPU_MIGRATABLE) suffix = "migratable"; else suffix = "result"; if (virAsprintf(&result, "%s-%s", data->name, suffix) < 0) goto cleanup; if (cpuTestCompareXML(data->arch, baseline, result) < 0) goto cleanup; for (i = 0; i < ncpus; i++) { virCPUCompareResult cmp; cmp = virCPUCompare(cpus[i]->arch, cpus[i], baseline, false); if (cmp != VIR_CPU_COMPARE_SUPERSET && cmp != VIR_CPU_COMPARE_IDENTICAL) { VIR_TEST_VERBOSE("\nbaseline CPU is incompatible with CPU %zu\n", i); VIR_TEST_VERBOSE("%74s", "... "); ret = -1; goto cleanup; } } ret = 0; cleanup: if (cpus) { for (i = 0; i < ncpus; i++) virCPUDefFree(cpus[i]); VIR_FREE(cpus); } virCPUDefFree(baseline); VIR_FREE(result); return ret; } static int cpuTestUpdate(const void *arg) { const struct data *data = arg; int ret = -1; virCPUDefPtr host = NULL; virCPUDefPtr migHost = NULL; virCPUDefPtr cpu = NULL; char *result = NULL; if (!(host = cpuTestLoadXML(data->arch, data->host)) || !(cpu = cpuTestLoadXML(data->arch, data->name))) goto cleanup; if (!(migHost = virCPUCopyMigratable(data->arch, host))) goto cleanup; if (virCPUUpdate(host->arch, cpu, migHost) < 0) goto cleanup; if (virAsprintf(&result, "%s+%s", data->host, data->name) < 0) goto cleanup; ret = cpuTestCompareXML(data->arch, cpu, result); cleanup: virCPUDefFree(host); virCPUDefFree(cpu); virCPUDefFree(migHost); VIR_FREE(result); return ret; } static int cpuTestHasFeature(const void *arg) { const struct data *data = arg; int ret = -1; virCPUDefPtr host = NULL; virCPUDataPtr hostData = NULL; int result; if (!(host = cpuTestLoadXML(data->arch, data->host))) goto cleanup; if (cpuEncode(host->arch, host, NULL, &hostData, NULL, NULL, NULL, NULL) < 0) goto cleanup; result = virCPUCheckFeature(host->arch, host, data->name); if (data->result == result) result = virCPUDataCheckFeature(hostData, data->name); if (data->result == -1) virResetLastError(); if (data->result != result) { VIR_TEST_VERBOSE("\nExpected result %s, got %s\n", cpuTestBoolWithErrorStr(data->result), cpuTestBoolWithErrorStr(result)); /* Pad to line up with test name ... in virTestRun */ VIR_TEST_VERBOSE("%74s", "... "); goto cleanup; } ret = 0; cleanup: virCPUDataFree(hostData); virCPUDefFree(host); return ret; } typedef enum { /* No JSON data from QEMU. */ JSON_NONE, /* Only a reply from query-cpu-model-expansion QMP command. */ JSON_HOST, /* Replies from both query-cpu-model-expansion and query-cpu-definitions * QMP commands. */ JSON_MODELS, /* Same as JSON_MODELS, but the reply from query-cpu-definitions has to * be parsed for providing the correct result. This happens when the * CPU model detected by libvirt has non-empty unavailable-features array * in query-cpu-definitions reply or when the CPU model detected from CPUID * differs from the one we get from QEMU and we need to translate them for * comparison. Such tests require QEMU driver to be enabled. */ JSON_MODELS_REQUIRED, } cpuTestCPUIDJson; #if WITH_QEMU && WITH_YAJL static virQEMUCapsPtr cpuTestMakeQEMUCaps(const struct data *data) { virQEMUCapsPtr qemuCaps = NULL; qemuMonitorTestPtr testMon = NULL; qemuMonitorCPUModelInfoPtr model = NULL; char *json = NULL; if (virAsprintf(&json, "%s/cputestdata/%s-cpuid-%s.json", abs_srcdir, virArchToString(data->arch), data->host) < 0) goto error; if (!(testMon = qemuMonitorTestNewFromFile(json, driver.xmlopt, true))) goto error; if (qemuMonitorGetCPUModelExpansion(qemuMonitorTestGetMonitor(testMon), QEMU_MONITOR_CPU_MODEL_EXPANSION_STATIC, "host", true, &model) < 0) goto error; if (!(qemuCaps = virQEMUCapsNew())) goto error; virQEMUCapsSet(qemuCaps, QEMU_CAPS_KVM); if (data->flags == JSON_MODELS || data->flags == JSON_MODELS_REQUIRED) virQEMUCapsSet(qemuCaps, QEMU_CAPS_QUERY_CPU_DEFINITIONS); virQEMUCapsSetArch(qemuCaps, data->arch); virQEMUCapsSetCPUModelInfo(qemuCaps, VIR_DOMAIN_VIRT_KVM, model); model = NULL; if (virQEMUCapsProbeQMPCPUDefinitions(qemuCaps, qemuMonitorTestGetMonitor(testMon), false) < 0) goto error; cleanup: qemuMonitorCPUModelInfoFree(model); qemuMonitorTestFree(testMon); VIR_FREE(json); return qemuCaps; error: virObjectUnref(qemuCaps); qemuCaps = NULL; goto cleanup; } static int cpuTestGetCPUModels(const struct data *data, virDomainCapsCPUModelsPtr *models) { virQEMUCapsPtr qemuCaps; *models = NULL; if (data->flags != JSON_MODELS && data->flags != JSON_MODELS_REQUIRED) return 0; if (!(qemuCaps = cpuTestMakeQEMUCaps(data))) return -1; *models = virQEMUCapsGetCPUDefinitions(qemuCaps, VIR_DOMAIN_VIRT_KVM); virObjectRef(*models); virObjectUnref(qemuCaps); return 0; } #else /* if WITH_QEMU && WITH_YAJL */ static int cpuTestGetCPUModels(const struct data *data, virDomainCapsCPUModelsPtr *models) { *models = NULL; if (data->flags == JSON_MODELS_REQUIRED) return EXIT_AM_SKIP; return 0; } #endif static int cpuTestCPUID(bool guest, const void *arg) { const struct data *data = arg; int ret = -1; virCPUDataPtr hostData = NULL; char *hostFile = NULL; char *host = NULL; virCPUDefPtr cpu = NULL; char *result = NULL; virDomainCapsCPUModelsPtr models = NULL; if (virAsprintf(&hostFile, "%s/cputestdata/%s-cpuid-%s.xml", abs_srcdir, virArchToString(data->arch), data->host) < 0) goto cleanup; if (virTestLoadFile(hostFile, &host) < 0 || !(hostData = virCPUDataParse(host))) goto cleanup; if (VIR_ALLOC(cpu) < 0) goto cleanup; cpu->arch = hostData->arch; if (guest) { cpu->type = VIR_CPU_TYPE_GUEST; cpu->match = VIR_CPU_MATCH_EXACT; cpu->fallback = VIR_CPU_FALLBACK_FORBID; } else { cpu->type = VIR_CPU_TYPE_HOST; } if (guest) { int rc; rc = cpuTestGetCPUModels(data, &models); if (rc != 0) { ret = rc; goto cleanup; } } if (cpuDecode(cpu, hostData, models) < 0) goto cleanup; if (virAsprintf(&result, "cpuid-%s-%s", data->host, guest ? "guest" : "host") < 0) goto cleanup; ret = cpuTestCompareXML(data->arch, cpu, result); cleanup: VIR_FREE(hostFile); VIR_FREE(host); virCPUDataFree(hostData); virCPUDefFree(cpu); VIR_FREE(result); virObjectUnref(models); return ret; } static int cpuTestHostCPUID(const void *arg) { return cpuTestCPUID(false, arg); } static int cpuTestGuestCPUID(const void *arg) { return cpuTestCPUID(true, arg); } static int cpuTestUpdateLiveCompare(virArch arch, virCPUDefPtr actual, virCPUDefPtr expected) { size_t i, j; int ret = 0; if (virCPUExpandFeatures(arch, actual) < 0 || virCPUExpandFeatures(arch, expected) < 0) return -1; if (STRNEQ(actual->model, expected->model)) { VIR_TEST_VERBOSE("Actual CPU model '%s', expected '%s'\n", actual->model, expected->model); return -1; } i = j = 0; while (i < actual->nfeatures || j < expected->nfeatures) { virCPUFeatureDefPtr featAct = NULL; virCPUFeatureDefPtr featExp = NULL; int cmp; if (i < actual->nfeatures) featAct = actual->features + i; if (j < expected->nfeatures) featExp = expected->features + j; /* * Act < Exp => cmp < 0 (missing entry in Exp) * Act = Exp => cmp = 0 * Act > Exp => cmp > 0 (missing entry in Act) * * NULL > name for any name != NULL */ if (featAct && featExp) cmp = strcmp(featAct->name, featExp->name); else cmp = featExp ? 1 : -1; if (cmp <= 0) i++; if (cmp >= 0) j++; /* Possible combinations of cmp, featAct->policy, and featExp->policy: * cmp Act Exp result * --------------------------------- * 0 dis dis ok * 0 dis req missing * 0 req dis extra * 0 req req ok * --------------------------------- * - dis X ok # ignoring extra disabled features * - req X extra * --------------------------------- * + X dis extra * + X req missing */ if ((cmp == 0 && featAct->policy == VIR_CPU_FEATURE_DISABLE && featExp->policy == VIR_CPU_FEATURE_REQUIRE) || (cmp > 0 && featExp->policy == VIR_CPU_FEATURE_REQUIRE)) { VIR_TEST_VERBOSE("Actual CPU lacks feature '%s'\n", featExp->name); ret = -1; continue; } if ((cmp == 0 && featAct->policy == VIR_CPU_FEATURE_REQUIRE && featExp->policy == VIR_CPU_FEATURE_DISABLE) || (cmp < 0 && featAct->policy == VIR_CPU_FEATURE_REQUIRE) || (cmp > 0 && featExp->policy == VIR_CPU_FEATURE_DISABLE)) { VIR_TEST_VERBOSE("Actual CPU has extra feature '%s'\n", cmp <= 0 ? featAct->name : featExp->name); ret = -1; } } return ret; } static int cpuTestUpdateLive(const void *arg) { const struct data *data = arg; char *cpuFile = NULL; virCPUDefPtr cpu = NULL; char *enabledFile = NULL; char *enabled = NULL; virCPUDataPtr enabledData = NULL; char *disabledFile = NULL; char *disabled = NULL; virCPUDataPtr disabledData = NULL; char *expectedFile = NULL; virCPUDefPtr expected = NULL; virDomainCapsCPUModelsPtr hvModels = NULL; virDomainCapsCPUModelsPtr models = NULL; int ret = -1; if (virAsprintf(&cpuFile, "cpuid-%s-guest", data->host) < 0 || !(cpu = cpuTestLoadXML(data->arch, cpuFile))) goto cleanup; if (virAsprintf(&enabledFile, "%s/cputestdata/%s-cpuid-%s-enabled.xml", abs_srcdir, virArchToString(data->arch), data->host) < 0 || virTestLoadFile(enabledFile, &enabled) < 0 || !(enabledData = virCPUDataParse(enabled))) goto cleanup; if (virAsprintf(&disabledFile, "%s/cputestdata/%s-cpuid-%s-disabled.xml", abs_srcdir, virArchToString(data->arch), data->host) < 0 || virTestLoadFile(disabledFile, &disabled) < 0 || !(disabledData = virCPUDataParse(disabled))) goto cleanup; if (virAsprintf(&expectedFile, "cpuid-%s-json", data->host) < 0 || !(expected = cpuTestLoadXML(data->arch, expectedFile))) goto cleanup; /* In case the host CPU signature does not exactly match any CPU model from * cpu_map.xml, the CPU model we detect from CPUID may differ from the one * we compute by asking QEMU. Since this test expands both CPU models and * compares their features, we can try to translate the 'actual' CPU to * use the CPU model from 'expected'. */ if (STRNEQ(cpu->model, expected->model)) { virDomainCapsCPUModelPtr hvModel; char **blockers = NULL; virDomainCapsCPUUsable usable = VIR_DOMCAPS_CPU_USABLE_UNKNOWN; int rc; if (!(models = virDomainCapsCPUModelsNew(0))) goto cleanup; rc = cpuTestGetCPUModels(data, &hvModels); if (rc != 0) { ret = rc; goto cleanup; } hvModel = virDomainCapsCPUModelsGet(hvModels, expected->model); if (hvModel) { blockers = hvModel->blockers; usable = hvModel->usable; } if (virDomainCapsCPUModelsAdd(models, expected->model, -1, usable, blockers) < 0) goto cleanup; cpu->fallback = VIR_CPU_FALLBACK_ALLOW; ignore_value(virCPUTranslate(data->arch, cpu, models)); cpu->fallback = VIR_CPU_FALLBACK_FORBID; } if (virCPUUpdateLive(data->arch, cpu, enabledData, disabledData) < 0) goto cleanup; ret = cpuTestUpdateLiveCompare(data->arch, cpu, expected); cleanup: VIR_FREE(cpuFile); virCPUDefFree(cpu); VIR_FREE(enabledFile); VIR_FREE(enabled); virCPUDataFree(enabledData); VIR_FREE(disabledFile); VIR_FREE(disabled); virCPUDataFree(disabledData); VIR_FREE(expectedFile); virCPUDefFree(expected); virObjectUnref(hvModels); virObjectUnref(models); return ret; } #if WITH_QEMU && WITH_YAJL static int cpuTestJSONCPUID(const void *arg) { const struct data *data = arg; virQEMUCapsPtr qemuCaps = NULL; virCPUDefPtr cpu = NULL; char *result = NULL; int ret = -1; if (virAsprintf(&result, "cpuid-%s-json", data->host) < 0) goto cleanup; if (!(qemuCaps = cpuTestMakeQEMUCaps(data))) goto cleanup; if (VIR_ALLOC(cpu) < 0) goto cleanup; cpu->arch = data->arch; cpu->type = VIR_CPU_TYPE_GUEST; cpu->match = VIR_CPU_MATCH_EXACT; cpu->fallback = VIR_CPU_FALLBACK_FORBID; if (virQEMUCapsInitCPUModel(qemuCaps, VIR_DOMAIN_VIRT_KVM, cpu, false) != 0) goto cleanup; ret = cpuTestCompareXML(data->arch, cpu, result); cleanup: virObjectUnref(qemuCaps); virCPUDefFree(cpu); VIR_FREE(result); return ret; } #endif static const char *model486_list[] = { "486", NULL }; static const char *nomodel_list[] = { "nomodel", NULL }; static const char *models_list[] = { "qemu64", "core2duo", "Nehalem", NULL }; static const char *haswell_list[] = { "SandyBridge", "Haswell", NULL }; static const char *ppc_models_list[] = { "POWER6", "POWER7", "POWER8", NULL }; static virDomainCapsCPUModelsPtr cpuTestInitModels(const char **list) { virDomainCapsCPUModelsPtr cpus; const char **model; if (!(cpus = virDomainCapsCPUModelsNew(0))) return NULL; for (model = list; *model; model++) { if (virDomainCapsCPUModelsAdd(cpus, *model, -1, VIR_DOMCAPS_CPU_USABLE_UNKNOWN, NULL) < 0) goto error; } return cpus; error: virObjectUnref(cpus); return NULL; } static int mymain(void) { virDomainCapsCPUModelsPtr model486 = NULL; virDomainCapsCPUModelsPtr nomodel = NULL; virDomainCapsCPUModelsPtr models = NULL; virDomainCapsCPUModelsPtr haswell = NULL; virDomainCapsCPUModelsPtr ppc_models = NULL; int ret = 0; #if WITH_QEMU && WITH_YAJL if (qemuTestDriverInit(&driver) < 0) return EXIT_FAILURE; virEventRegisterDefaultImpl(); #endif if (!(model486 = cpuTestInitModels(model486_list)) || !(nomodel = cpuTestInitModels(nomodel_list)) || !(models = cpuTestInitModels(models_list)) || !(haswell = cpuTestInitModels(haswell_list)) || !(ppc_models = cpuTestInitModels(ppc_models_list))) { ret = -1; goto cleanup; } #define DO_TEST(arch, api, name, host, cpu, \ models, flags, result) \ do { \ struct data data = { \ arch, host, cpu, models, \ models == NULL ? NULL : #models, \ flags, result \ }; \ char *testLabel; \ char *tmp; \ \ tmp = virTestLogContentAndReset(); \ VIR_FREE(tmp); \ \ if (virAsprintf(&testLabel, "%s(%s): %s", \ #api, virArchToString(arch), name) < 0) { \ ret = -1; \ break; \ } \ \ if (virTestRun(testLabel, api, &data) < 0) { \ if (virTestGetDebug()) { \ char *log; \ if ((log = virTestLogContentAndReset()) && \ strlen(log) > 0) \ VIR_TEST_DEBUG("\n%s\n", log); \ VIR_FREE(log); \ } \ ret = -1; \ } \ \ VIR_FREE(testLabel); \ } while (0) #define DO_TEST_COMPARE(arch, host, cpu, result) \ DO_TEST(arch, cpuTestCompare, \ host "/" cpu " (" #result ")", \ host, cpu, NULL, 0, result) #define DO_TEST_UPDATE_ONLY(arch, host, cpu) \ DO_TEST(arch, cpuTestUpdate, \ cpu " on " host, \ host, cpu, NULL, 0, 0) #define DO_TEST_UPDATE(arch, host, cpu, result) \ do { \ DO_TEST_UPDATE_ONLY(arch, host, cpu); \ DO_TEST_COMPARE(arch, host, host "+" cpu, result); \ } while (0) #define DO_TEST_BASELINE(arch, name, flags, result) \ do { \ const char *suffix = ""; \ char *label; \ if ((flags) & VIR_CONNECT_BASELINE_CPU_EXPAND_FEATURES) \ suffix = " (expanded)"; \ if ((flags) & VIR_CONNECT_BASELINE_CPU_MIGRATABLE) \ suffix = " (migratable)"; \ if (virAsprintf(&label, "%s%s", name, suffix) < 0) { \ ret = -1; \ } else { \ DO_TEST(arch, cpuTestBaseline, label, NULL, \ "baseline-" name, NULL, flags, result); \ } \ VIR_FREE(label); \ } while (0) #define DO_TEST_HASFEATURE(arch, host, feature, result) \ DO_TEST(arch, cpuTestHasFeature, \ host "/" feature " (" #result ")", \ host, feature, NULL, 0, result) #define DO_TEST_GUESTCPU(arch, host, cpu, models, result) \ DO_TEST(arch, cpuTestGuestCPU, \ host "/" cpu " (" #models ")", \ host, cpu, models, 0, result) #if WITH_QEMU && WITH_YAJL # define DO_TEST_JSON(arch, host, json) \ do { \ if (json == JSON_MODELS) { \ DO_TEST(arch, cpuTestGuestCPUID, host, host, \ NULL, NULL, 0, 0); \ } \ if (json != JSON_NONE) { \ DO_TEST(arch, cpuTestJSONCPUID, host, host, \ NULL, NULL, json, 0); \ } \ } while (0) #else # define DO_TEST_JSON(arch, host, json) #endif #define DO_TEST_CPUID(arch, host, json) \ do { \ DO_TEST(arch, cpuTestHostCPUID, host, host, \ NULL, NULL, 0, 0); \ DO_TEST(arch, cpuTestGuestCPUID, host, host, \ NULL, NULL, json, 0); \ DO_TEST_JSON(arch, host, json); \ if (json != JSON_NONE) { \ DO_TEST(arch, cpuTestUpdateLive, host, host, \ NULL, NULL, json, 0); \ } \ } while (0) /* host to host comparison */ DO_TEST_COMPARE(VIR_ARCH_X86_64, "host", "host", VIR_CPU_COMPARE_IDENTICAL); DO_TEST_COMPARE(VIR_ARCH_X86_64, "host", "host-better", VIR_CPU_COMPARE_INCOMPATIBLE); DO_TEST_COMPARE(VIR_ARCH_X86_64, "host", "host-worse", VIR_CPU_COMPARE_SUPERSET); DO_TEST_COMPARE(VIR_ARCH_X86_64, "host", "host-amd-fake", VIR_CPU_COMPARE_INCOMPATIBLE); DO_TEST_COMPARE(VIR_ARCH_X86_64, "host", "host-incomp-arch", VIR_CPU_COMPARE_INCOMPATIBLE); DO_TEST_COMPARE(VIR_ARCH_X86_64, "host", "host-no-vendor", VIR_CPU_COMPARE_IDENTICAL); DO_TEST_COMPARE(VIR_ARCH_X86_64, "host-no-vendor", "host", VIR_CPU_COMPARE_INCOMPATIBLE); DO_TEST_COMPARE(VIR_ARCH_PPC64, "host", "host", VIR_CPU_COMPARE_IDENTICAL); DO_TEST_COMPARE(VIR_ARCH_PPC64, "host", "host-better", VIR_CPU_COMPARE_INCOMPATIBLE); DO_TEST_COMPARE(VIR_ARCH_PPC64, "host", "host-worse", VIR_CPU_COMPARE_INCOMPATIBLE); DO_TEST_COMPARE(VIR_ARCH_PPC64, "host", "host-incomp-arch", VIR_CPU_COMPARE_INCOMPATIBLE); DO_TEST_COMPARE(VIR_ARCH_PPC64, "host", "host-no-vendor", VIR_CPU_COMPARE_IDENTICAL); DO_TEST_COMPARE(VIR_ARCH_PPC64, "host-no-vendor", "host", VIR_CPU_COMPARE_INCOMPATIBLE); /* guest to host comparison */ DO_TEST_COMPARE(VIR_ARCH_X86_64, "host", "bogus-model", VIR_CPU_COMPARE_ERROR); DO_TEST_COMPARE(VIR_ARCH_X86_64, "host", "bogus-feature", VIR_CPU_COMPARE_ERROR); DO_TEST_COMPARE(VIR_ARCH_X86_64, "host", "min", VIR_CPU_COMPARE_SUPERSET); DO_TEST_COMPARE(VIR_ARCH_X86_64, "host", "pentium3", VIR_CPU_COMPARE_SUPERSET); DO_TEST_COMPARE(VIR_ARCH_X86_64, "host", "exact", VIR_CPU_COMPARE_SUPERSET); DO_TEST_COMPARE(VIR_ARCH_X86_64, "host", "exact-forbid", VIR_CPU_COMPARE_INCOMPATIBLE); DO_TEST_COMPARE(VIR_ARCH_X86_64, "host", "exact-forbid-extra", VIR_CPU_COMPARE_SUPERSET); DO_TEST_COMPARE(VIR_ARCH_X86_64, "host", "exact-disable", VIR_CPU_COMPARE_SUPERSET); DO_TEST_COMPARE(VIR_ARCH_X86_64, "host", "exact-disable2", VIR_CPU_COMPARE_SUPERSET); DO_TEST_COMPARE(VIR_ARCH_X86_64, "host", "exact-disable-extra", VIR_CPU_COMPARE_SUPERSET); DO_TEST_COMPARE(VIR_ARCH_X86_64, "host", "exact-require", VIR_CPU_COMPARE_SUPERSET); DO_TEST_COMPARE(VIR_ARCH_X86_64, "host", "exact-require-extra", VIR_CPU_COMPARE_INCOMPATIBLE); DO_TEST_COMPARE(VIR_ARCH_X86_64, "host", "exact-force", VIR_CPU_COMPARE_SUPERSET); DO_TEST_COMPARE(VIR_ARCH_X86_64, "host", "strict", VIR_CPU_COMPARE_INCOMPATIBLE); DO_TEST_COMPARE(VIR_ARCH_X86_64, "host", "strict-full", VIR_CPU_COMPARE_IDENTICAL); DO_TEST_COMPARE(VIR_ARCH_X86_64, "host", "strict-disable", VIR_CPU_COMPARE_IDENTICAL); DO_TEST_COMPARE(VIR_ARCH_X86_64, "host", "strict-force-extra", VIR_CPU_COMPARE_IDENTICAL); DO_TEST_COMPARE(VIR_ARCH_X86_64, "host", "guest", VIR_CPU_COMPARE_SUPERSET); DO_TEST_COMPARE(VIR_ARCH_X86_64, "host", "pentium3-amd", VIR_CPU_COMPARE_INCOMPATIBLE); DO_TEST_COMPARE(VIR_ARCH_X86_64, "host-amd", "pentium3-amd", VIR_CPU_COMPARE_SUPERSET); DO_TEST_COMPARE(VIR_ARCH_X86_64, "host-worse", "penryn-force", VIR_CPU_COMPARE_IDENTICAL); DO_TEST_COMPARE(VIR_ARCH_X86_64, "host-SandyBridge", "exact-force-Haswell", VIR_CPU_COMPARE_IDENTICAL); DO_TEST_COMPARE(VIR_ARCH_PPC64, "host", "guest-strict", VIR_CPU_COMPARE_IDENTICAL); DO_TEST_COMPARE(VIR_ARCH_PPC64, "host", "guest-exact", VIR_CPU_COMPARE_INCOMPATIBLE); DO_TEST_COMPARE(VIR_ARCH_PPC64, "host", "guest-legacy", VIR_CPU_COMPARE_IDENTICAL); DO_TEST_COMPARE(VIR_ARCH_PPC64, "host", "guest-legacy-incompatible", VIR_CPU_COMPARE_INCOMPATIBLE); DO_TEST_COMPARE(VIR_ARCH_PPC64, "host", "guest-legacy-invalid", VIR_CPU_COMPARE_ERROR); DO_TEST_COMPARE(VIR_ARCH_PPC64, "host", "guest-compat-none", VIR_CPU_COMPARE_IDENTICAL); DO_TEST_COMPARE(VIR_ARCH_PPC64, "host", "guest-compat-valid", VIR_CPU_COMPARE_IDENTICAL); DO_TEST_COMPARE(VIR_ARCH_PPC64, "host", "guest-compat-invalid", VIR_CPU_COMPARE_ERROR); DO_TEST_COMPARE(VIR_ARCH_PPC64, "host", "guest-compat-incompatible", VIR_CPU_COMPARE_INCOMPATIBLE); /* guest updates for migration * automatically compares host CPU with the result */ DO_TEST_UPDATE(VIR_ARCH_X86_64, "host", "min", VIR_CPU_COMPARE_IDENTICAL); DO_TEST_UPDATE(VIR_ARCH_X86_64, "host", "pentium3", VIR_CPU_COMPARE_IDENTICAL); DO_TEST_UPDATE(VIR_ARCH_X86_64, "host", "guest", VIR_CPU_COMPARE_SUPERSET); DO_TEST_UPDATE(VIR_ARCH_X86_64, "host", "host-model", VIR_CPU_COMPARE_IDENTICAL); DO_TEST_UPDATE(VIR_ARCH_X86_64, "host", "host-model-nofallback", VIR_CPU_COMPARE_IDENTICAL); DO_TEST_UPDATE(VIR_ARCH_X86_64, "host-invtsc", "host-model", VIR_CPU_COMPARE_SUPERSET); DO_TEST_UPDATE_ONLY(VIR_ARCH_X86_64, "host", "host-passthrough"); DO_TEST_UPDATE_ONLY(VIR_ARCH_X86_64, "host", "host-passthrough-features"); DO_TEST_UPDATE(VIR_ARCH_PPC64, "host", "guest", VIR_CPU_COMPARE_IDENTICAL); DO_TEST_UPDATE(VIR_ARCH_PPC64, "host", "guest-nofallback", VIR_CPU_COMPARE_INCOMPATIBLE); DO_TEST_UPDATE(VIR_ARCH_PPC64, "host", "guest-legacy", VIR_CPU_COMPARE_IDENTICAL); DO_TEST_UPDATE(VIR_ARCH_PPC64, "host", "guest-legacy-incompatible", VIR_CPU_COMPARE_INCOMPATIBLE); DO_TEST_UPDATE(VIR_ARCH_PPC64, "host", "guest-legacy-invalid", VIR_CPU_COMPARE_ERROR); DO_TEST_UPDATE(VIR_ARCH_PPC64, "host", "guest-compat-none", VIR_CPU_COMPARE_IDENTICAL); DO_TEST_UPDATE(VIR_ARCH_PPC64, "host", "guest-compat-valid", VIR_CPU_COMPARE_IDENTICAL); DO_TEST_UPDATE(VIR_ARCH_PPC64, "host", "guest-compat-invalid", VIR_CPU_COMPARE_ERROR); DO_TEST_UPDATE(VIR_ARCH_PPC64, "host", "guest-compat-incompatible", VIR_CPU_COMPARE_INCOMPATIBLE); /* computing baseline CPUs */ DO_TEST_BASELINE(VIR_ARCH_X86_64, "incompatible-vendors", 0, -1); DO_TEST_BASELINE(VIR_ARCH_X86_64, "no-vendor", 0, 0); DO_TEST_BASELINE(VIR_ARCH_X86_64, "some-vendors", 0, 0); DO_TEST_BASELINE(VIR_ARCH_X86_64, "1", 0, 0); DO_TEST_BASELINE(VIR_ARCH_X86_64, "2", 0, 0); DO_TEST_BASELINE(VIR_ARCH_X86_64, "3", 0, 0); DO_TEST_BASELINE(VIR_ARCH_X86_64, "3", VIR_CONNECT_BASELINE_CPU_EXPAND_FEATURES, 0); DO_TEST_BASELINE(VIR_ARCH_X86_64, "4", 0, 0); DO_TEST_BASELINE(VIR_ARCH_X86_64, "4", VIR_CONNECT_BASELINE_CPU_EXPAND_FEATURES, 0); DO_TEST_BASELINE(VIR_ARCH_X86_64, "5", 0, 0); DO_TEST_BASELINE(VIR_ARCH_X86_64, "5", VIR_CONNECT_BASELINE_CPU_EXPAND_FEATURES, 0); DO_TEST_BASELINE(VIR_ARCH_X86_64, "6", 0, 0); DO_TEST_BASELINE(VIR_ARCH_X86_64, "6", VIR_CONNECT_BASELINE_CPU_MIGRATABLE, 0); DO_TEST_BASELINE(VIR_ARCH_X86_64, "7", 0, 0); DO_TEST_BASELINE(VIR_ARCH_X86_64, "8", 0, 0); DO_TEST_BASELINE(VIR_ARCH_PPC64, "incompatible-vendors", 0, -1); DO_TEST_BASELINE(VIR_ARCH_PPC64, "no-vendor", 0, 0); DO_TEST_BASELINE(VIR_ARCH_PPC64, "incompatible-models", 0, -1); DO_TEST_BASELINE(VIR_ARCH_PPC64, "same-model", 0, 0); DO_TEST_BASELINE(VIR_ARCH_PPC64, "legacy", 0, -1); /* CPU features */ DO_TEST_HASFEATURE(VIR_ARCH_X86_64, "host", "vmx", YES); DO_TEST_HASFEATURE(VIR_ARCH_X86_64, "host", "lm", YES); DO_TEST_HASFEATURE(VIR_ARCH_X86_64, "host", "sse4.1", YES); DO_TEST_HASFEATURE(VIR_ARCH_X86_64, "host", "3dnowext", NO); DO_TEST_HASFEATURE(VIR_ARCH_X86_64, "host", "skinit", NO); DO_TEST_HASFEATURE(VIR_ARCH_X86_64, "host", "foo", FAIL); /* computing guest data and decoding the data into a guest CPU XML */ DO_TEST_GUESTCPU(VIR_ARCH_X86_64, "host", "guest", NULL, 0); DO_TEST_GUESTCPU(VIR_ARCH_X86_64, "host-better", "pentium3", NULL, 0); DO_TEST_GUESTCPU(VIR_ARCH_X86_64, "host-worse", "guest", NULL, 0); DO_TEST_GUESTCPU(VIR_ARCH_X86_64, "host", "strict-force-extra", NULL, 0); DO_TEST_GUESTCPU(VIR_ARCH_X86_64, "host", "penryn-force", NULL, 0); DO_TEST_GUESTCPU(VIR_ARCH_X86_64, "host", "guest", model486, 0); DO_TEST_GUESTCPU(VIR_ARCH_X86_64, "host", "guest", models, 0); DO_TEST_GUESTCPU(VIR_ARCH_X86_64, "host", "guest", nomodel, -1); DO_TEST_GUESTCPU(VIR_ARCH_X86_64, "host", "guest-nofallback", models, -1); DO_TEST_GUESTCPU(VIR_ARCH_X86_64, "host", "host+host-model", models, 0); DO_TEST_GUESTCPU(VIR_ARCH_X86_64, "host", "host+host-model-nofallback", models, -1); DO_TEST_GUESTCPU(VIR_ARCH_X86_64, "host-Haswell-noTSX", "Haswell", haswell, 0); DO_TEST_GUESTCPU(VIR_ARCH_X86_64, "host-Haswell-noTSX", "Haswell-noTSX", haswell, 0); DO_TEST_GUESTCPU(VIR_ARCH_X86_64, "host-Haswell-noTSX", "Haswell-noTSX-nofallback", haswell, -1); DO_TEST_GUESTCPU(VIR_ARCH_X86_64, "host-Haswell-noTSX", "Haswell-noTSX", NULL, 0); DO_TEST_GUESTCPU(VIR_ARCH_PPC64, "host", "guest", ppc_models, 0); DO_TEST_GUESTCPU(VIR_ARCH_PPC64, "host", "guest-nofallback", ppc_models, -1); DO_TEST_GUESTCPU(VIR_ARCH_PPC64, "host", "guest-legacy", ppc_models, 0); DO_TEST_GUESTCPU(VIR_ARCH_PPC64, "host", "guest-legacy-incompatible", ppc_models, -1); DO_TEST_GUESTCPU(VIR_ARCH_PPC64, "host", "guest-legacy-invalid", ppc_models, -1); DO_TEST_CPUID(VIR_ARCH_X86_64, "A10-5800K", JSON_HOST); DO_TEST_CPUID(VIR_ARCH_X86_64, "Atom-D510", JSON_NONE); DO_TEST_CPUID(VIR_ARCH_X86_64, "Atom-N450", JSON_NONE); DO_TEST_CPUID(VIR_ARCH_X86_64, "Core-i5-2500", JSON_HOST); DO_TEST_CPUID(VIR_ARCH_X86_64, "Core-i5-2540M", JSON_MODELS); DO_TEST_CPUID(VIR_ARCH_X86_64, "Core-i5-4670T", JSON_HOST); DO_TEST_CPUID(VIR_ARCH_X86_64, "Core-i5-6600", JSON_HOST); DO_TEST_CPUID(VIR_ARCH_X86_64, "Core-i7-2600", JSON_HOST); DO_TEST_CPUID(VIR_ARCH_X86_64, "Core-i7-2600-xsaveopt", JSON_MODELS_REQUIRED); DO_TEST_CPUID(VIR_ARCH_X86_64, "Core-i7-3520M", JSON_NONE); DO_TEST_CPUID(VIR_ARCH_X86_64, "Core-i7-3740QM", JSON_HOST); DO_TEST_CPUID(VIR_ARCH_X86_64, "Core-i7-3770", JSON_HOST); DO_TEST_CPUID(VIR_ARCH_X86_64, "Core-i7-4600U", JSON_HOST); DO_TEST_CPUID(VIR_ARCH_X86_64, "Core-i7-4510U", JSON_HOST); DO_TEST_CPUID(VIR_ARCH_X86_64, "Core-i7-5600U", JSON_HOST); DO_TEST_CPUID(VIR_ARCH_X86_64, "Core-i7-5600U-arat", JSON_HOST); DO_TEST_CPUID(VIR_ARCH_X86_64, "Core2-E6850", JSON_HOST); DO_TEST_CPUID(VIR_ARCH_X86_64, "Core2-Q9500", JSON_NONE); DO_TEST_CPUID(VIR_ARCH_X86_64, "EPYC-7601-32-Core", JSON_HOST); DO_TEST_CPUID(VIR_ARCH_X86_64, "FX-8150", JSON_NONE); DO_TEST_CPUID(VIR_ARCH_X86_64, "Opteron-1352", JSON_NONE); DO_TEST_CPUID(VIR_ARCH_X86_64, "Opteron-2350", JSON_HOST); DO_TEST_CPUID(VIR_ARCH_X86_64, "Opteron-6234", JSON_HOST); DO_TEST_CPUID(VIR_ARCH_X86_64, "Opteron-6282", JSON_NONE); DO_TEST_CPUID(VIR_ARCH_X86_64, "Pentium-P6100", JSON_NONE); DO_TEST_CPUID(VIR_ARCH_X86_64, "Phenom-B95", JSON_HOST); DO_TEST_CPUID(VIR_ARCH_X86_64, "Ryzen-7-1800X-Eight-Core", JSON_HOST); DO_TEST_CPUID(VIR_ARCH_X86_64, "Xeon-5110", JSON_NONE); DO_TEST_CPUID(VIR_ARCH_X86_64, "Xeon-E3-1245-v5", JSON_MODELS); DO_TEST_CPUID(VIR_ARCH_X86_64, "Xeon-E5-2630-v3", JSON_HOST); DO_TEST_CPUID(VIR_ARCH_X86_64, "Xeon-E5-2650-v3", JSON_HOST); DO_TEST_CPUID(VIR_ARCH_X86_64, "Xeon-E7-4820", JSON_HOST); DO_TEST_CPUID(VIR_ARCH_X86_64, "Xeon-E7-4830", JSON_MODELS_REQUIRED); DO_TEST_CPUID(VIR_ARCH_X86_64, "Xeon-E7-8890-v3", JSON_MODELS); DO_TEST_CPUID(VIR_ARCH_X86_64, "Xeon-Gold-6148", JSON_HOST); DO_TEST_CPUID(VIR_ARCH_X86_64, "Xeon-W3520", JSON_HOST); DO_TEST_CPUID(VIR_ARCH_X86_64, "Xeon-X5460", JSON_NONE); cleanup: #if WITH_QEMU && WITH_YAJL qemuTestDriverFree(&driver); #endif virObjectUnref(model486); virObjectUnref(nomodel); virObjectUnref(models); virObjectUnref(haswell); virObjectUnref(ppc_models); return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } VIR_TEST_MAIN(mymain)