From ba31d47b940dc748a291e9b68efcf84178ea6800 Mon Sep 17 00:00:00 2001 Message-Id: From: Jiri Denemark Date: Fri, 8 Nov 2013 12:33:17 +0100 Subject: [PATCH] cpu: Add support for loading and storing CPU data https://bugzilla.redhat.com/show_bug.cgi?id=1008989 This patch adds cpuDataFormat and cpuDataParse APIs to be used in unit tests for testing APIs that deal with virCPUData. In the x86 world, this means we can now store/load arbitrary CPUID data in the test suite to check correctness of CPU related APIs that could not be tested before. Signed-off-by: Peter Krempa (cherry picked from commit 376261d164b0872bf011e525b4f0f050e942ace5) Conflicts: src/cpu/cpu.h - virCPUGetModel was not backported (context) Signed-off-by: Jiri Denemark --- src/cpu/cpu.c | 41 ++++++++++++++ src/cpu/cpu.h | 14 +++++ src/cpu/cpu_x86.c | 135 +++++++++++++++++++++++++++++++++++++++-------- src/libvirt_private.syms | 2 + 4 files changed, 171 insertions(+), 21 deletions(-) diff --git a/src/cpu/cpu.c b/src/cpu/cpu.c index 4124354..7989b45 100644 --- a/src/cpu/cpu.c +++ b/src/cpu/cpu.c @@ -438,6 +438,47 @@ cpuHasFeature(const virCPUDataPtr data, return driver->hasFeature(data, feature); } +char * +cpuDataFormat(const virCPUData *data) +{ + struct cpuArchDriver *driver; + + VIR_DEBUG("data=%p", data); + + if (!(driver = cpuGetSubDriver(data->arch))) + return NULL; + + if (!driver->dataFormat) { + virReportError(VIR_ERR_NO_SUPPORT, + _("cannot format %s CPU data"), + virArchToString(data->arch)); + return NULL; + } + + return driver->dataFormat(data); +} + +virCPUDataPtr +cpuDataParse(virArch arch, + const char *xmlStr) +{ + struct cpuArchDriver *driver; + + VIR_DEBUG("arch=%s, xmlStr=%s", virArchToString(arch), xmlStr); + + if (!(driver = cpuGetSubDriver(arch))) + return NULL; + + if (!driver->dataParse) { + virReportError(VIR_ERR_NO_SUPPORT, + _("cannot parse %s CPU data"), + virArchToString(arch)); + return NULL; + } + + return driver->dataParse(xmlStr); +} + bool cpuModelIsAllowed(const char *model, const char **models, diff --git a/src/cpu/cpu.h b/src/cpu/cpu.h index 4003435..7417f92 100644 --- a/src/cpu/cpu.h +++ b/src/cpu/cpu.h @@ -91,6 +91,11 @@ typedef int (*cpuArchHasFeature) (const virCPUDataPtr data, const char *feature); +typedef char * +(*cpuArchDataFormat)(const virCPUData *data); + +typedef virCPUDataPtr +(*cpuArchDataParse) (const char *xmlStr); struct cpuArchDriver { const char *name; @@ -105,6 +110,8 @@ struct cpuArchDriver { cpuArchBaseline baseline; cpuArchUpdate update; cpuArchHasFeature hasFeature; + cpuArchDataFormat dataFormat; + cpuArchDataParse dataParse; }; @@ -171,4 +178,11 @@ cpuModelIsAllowed(const char *model, const char **models, unsigned int nmodels); +/* cpuDataFormat and cpuDataParse are implemented for unit tests only and + * have no real-life usage + */ +char *cpuDataFormat(const virCPUData *data); +virCPUDataPtr cpuDataParse(virArch arch, + const char *xmlStr); + #endif /* __VIR_CPU_H__ */ diff --git a/src/cpu/cpu_x86.c b/src/cpu/cpu_x86.c index a388f0f..0bc031b 100644 --- a/src/cpu/cpu_x86.c +++ b/src/cpu/cpu_x86.c @@ -666,12 +666,42 @@ x86FeatureNames(const struct x86_map *map, static int +x86ParseCPUID(xmlXPathContextPtr ctxt, + struct cpuX86cpuid *cpuid) +{ + unsigned long fun, eax, ebx, ecx, edx; + int ret_fun, ret_eax, ret_ebx, ret_ecx, ret_edx; + + memset(cpuid, 0, sizeof(*cpuid)); + + fun = eax = ebx = ecx = edx = 0; + ret_fun = virXPathULongHex("string(@function)", ctxt, &fun); + ret_eax = virXPathULongHex("string(@eax)", ctxt, &eax); + ret_ebx = virXPathULongHex("string(@ebx)", ctxt, &ebx); + ret_ecx = virXPathULongHex("string(@ecx)", ctxt, &ecx); + ret_edx = virXPathULongHex("string(@edx)", ctxt, &edx); + + if (ret_fun < 0 || ret_eax == -2 || ret_ebx == -2 + || ret_ecx == -2 || ret_edx == -2) + return -1; + + cpuid->function = fun; + cpuid->eax = eax; + cpuid->ebx = ebx; + cpuid->ecx = ecx; + cpuid->edx = edx; + return 0; +} + + +static int x86FeatureLoad(xmlXPathContextPtr ctxt, struct x86_map *map) { xmlNodePtr *nodes = NULL; xmlNodePtr ctxt_node = ctxt->node; struct x86_feature *feature; + struct cpuX86cpuid cpuid; int ret = 0; size_t i; int n; @@ -697,31 +727,13 @@ x86FeatureLoad(xmlXPathContextPtr ctxt, goto ignore; for (i = 0; i < n; i++) { - struct cpuX86cpuid cpuid; - unsigned long fun, eax, ebx, ecx, edx; - int ret_fun, ret_eax, ret_ebx, ret_ecx, ret_edx; - ctxt->node = nodes[i]; - fun = eax = ebx = ecx = edx = 0; - ret_fun = virXPathULongHex("string(@function)", ctxt, &fun); - ret_eax = virXPathULongHex("string(@eax)", ctxt, &eax); - ret_ebx = virXPathULongHex("string(@ebx)", ctxt, &ebx); - ret_ecx = virXPathULongHex("string(@ecx)", ctxt, &ecx); - ret_edx = virXPathULongHex("string(@edx)", ctxt, &edx); - - if (ret_fun < 0 || ret_eax == -2 || ret_ebx == -2 - || ret_ecx == -2 || ret_edx == -2) { + if (x86ParseCPUID(ctxt, &cpuid) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, - _("Invalid cpuid[%zu] in %s feature"), i, feature->name); + _("Invalid cpuid[%zu] in %s feature"), + i, feature->name); goto ignore; } - - cpuid.function = fun; - cpuid.eax = eax; - cpuid.ebx = ebx; - cpuid.ecx = ecx; - cpuid.edx = edx; - if (x86DataAddCpuid(feature->data, &cpuid)) goto error; } @@ -1124,6 +1136,85 @@ error: } +static char * +x86CPUDataFormat(const virCPUData *data) +{ + struct data_iterator iter = DATA_ITERATOR_INIT(data->data.x86); + struct cpuX86cpuid *cpuid; + virBuffer buf = VIR_BUFFER_INITIALIZER; + + virBufferAddLit(&buf, "\n"); + while ((cpuid = x86DataCpuidNext(&iter))) { + virBufferAsprintf(&buf, + " \n", + cpuid->function, + cpuid->eax, cpuid->ebx, cpuid->ecx, cpuid->edx); + } + virBufferAddLit(&buf, "\n"); + + if (virBufferError(&buf)) { + virBufferFreeAndReset(&buf); + virReportOOMError(); + return NULL; + } + + return virBufferContentAndReset(&buf); +} + + +static virCPUDataPtr +x86CPUDataParse(const char *xmlStr) +{ + xmlDocPtr xml = NULL; + xmlXPathContextPtr ctxt = NULL; + xmlNodePtr *nodes = NULL; + virCPUDataPtr cpuData = NULL; + struct cpuX86Data *data = NULL; + struct cpuX86cpuid cpuid; + size_t i; + int n; + + if (VIR_ALLOC(data) < 0) + goto cleanup; + + if (!(xml = virXMLParseStringCtxt(xmlStr, _("CPU data"), &ctxt))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot parse CPU data")); + goto cleanup; + } + ctxt->node = xmlDocGetRootElement(xml); + + n = virXPathNodeSet("/cpudata[@arch='x86']/data", ctxt, &nodes); + if (n < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("no x86 CPU data found")); + goto cleanup; + } + + for (i = 0; i < n; i++) { + ctxt->node = nodes[i]; + if (x86ParseCPUID(ctxt, &cpuid) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("failed to parse cpuid[%zu]"), i); + goto cleanup; + } + if (x86DataAddCpuid(data, &cpuid) < 0) + goto cleanup; + } + + cpuData = x86MakeCPUData(VIR_ARCH_X86_64, &data); + +cleanup: + VIR_FREE(nodes); + xmlXPathFreeContext(ctxt); + xmlFreeDoc(xml); + x86DataFree(data); + return cpuData; +} + + /* A helper macro to exit the cpu computation function without writing * redundant code: * MSG: error message @@ -1920,4 +2011,6 @@ struct cpuArchDriver cpuDriverX86 = { .baseline = x86Baseline, .update = x86Update, .hasFeature = x86HasFeature, + .dataFormat = x86CPUDataFormat, + .dataParse = x86CPUDataParse, }; diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index ca6275c..722178b 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -715,7 +715,9 @@ cpuBaseline; cpuBaselineXML; cpuCompare; cpuCompareXML; +cpuDataFormat; cpuDataFree; +cpuDataParse; cpuDecode; cpuEncode; cpuGuestData; -- 1.8.4.2