From: Jiri Denemark <jdenemar(a)redhat.com>
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 <pkrempa(a)redhat.com>
---
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 31de857..8b293c8 100644
--- a/src/cpu/cpu.c
+++ b/src/cpu/cpu.c
@@ -442,6 +442,47 @@ cpuHasFeature(const virCPUDataPtr data,
return driver->hasFeature(data, feature);
}
+char *
+cpuDataFormat(const virCPUDataPtr 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 2f0c8c7..5e343a3 100644
--- a/src/cpu/cpu.h
+++ b/src/cpu/cpu.h
@@ -93,6 +93,11 @@ typedef int
(*cpuArchHasFeature) (const virCPUDataPtr data,
const char *feature);
+typedef char *
+(*cpuArchDataFormat)(const virCPUDataPtr data);
+
+typedef virCPUDataPtr
+(*cpuArchDataParse) (const char *xmlStr);
struct cpuArchDriver {
const char *name;
@@ -107,6 +112,8 @@ struct cpuArchDriver {
cpuArchBaseline baseline;
cpuArchUpdate update;
cpuArchHasFeature hasFeature;
+ cpuArchDataFormat dataFormat;
+ cpuArchDataParse dataParse;
};
@@ -178,4 +185,11 @@ cpuModelIsAllowed(const char *model,
extern int
cpuGetModels(const char *arch, char ***models);
+/* cpuDataFormat and cpuDataParse are implemented for unit tests only and
+ * have no real-life usage
+ */
+char *cpuDataFormat(const virCPUDataPtr 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 3d5e16f..3ab03fc 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 virCPUDataPtr data)
+{
+ struct data_iterator iter = DATA_ITERATOR_INIT(data->data.x86);
+ struct cpuX86cpuid *cpuid;
+ virBuffer buf = VIR_BUFFER_INITIALIZER;
+
+ virBufferAddLit(&buf, "<cpudata arch='x86'>\n");
+ while ((cpuid = x86DataCpuidNext(&iter))) {
+ virBufferAsprintf(&buf,
+ " <cpuid function='0x%08x'"
+ " eax='0x%08x' ebx='0x%08x'"
+ " ecx='0x%08x' edx='0x%08x'/>\n",
+ cpuid->function,
+ cpuid->eax, cpuid->ebx, cpuid->ecx, cpuid->edx);
+ }
+ virBufferAddLit(&buf, "</cpudata>\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
@@ -1956,4 +2047,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 844ca29..7d90b85 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -720,7 +720,9 @@ cpuBaseline;
cpuBaselineXML;
cpuCompare;
cpuCompareXML;
+cpuDataFormat;
cpuDataFree;
+cpuDataParse;
cpuDecode;
cpuEncode;
cpuGetModels;
--
1.8.3.2