[libvirt] [PATCH 4/7] qemu: Add monitor APIs to fetch CPUID data from QEMU

Jiri Denemark jdenemar at redhat.com
Tue Jul 23 16:11:33 UTC 2013


---
 src/qemu/qemu_monitor.c                            |  21 +++
 src/qemu/qemu_monitor.h                            |   3 +
 src/qemu/qemu_monitor_json.c                       | 162 +++++++++++++++++++++
 src/qemu/qemu_monitor_json.h                       |   6 +
 tests/Makefile.am                                  |   1 +
 .../qemumonitorjson-getcpu-empty.data              |   2 +
 .../qemumonitorjson-getcpu-empty.json              |  46 ++++++
 .../qemumonitorjson-getcpu-filtered.data           |   4 +
 .../qemumonitorjson-getcpu-filtered.json           |  46 ++++++
 .../qemumonitorjson-getcpu-full.data               |   4 +
 .../qemumonitorjson-getcpu-full.json               |  46 ++++++
 .../qemumonitorjson-getcpu-host.data               |   5 +
 .../qemumonitorjson-getcpu-host.json               |  45 ++++++
 tests/qemumonitorjsontest.c                        |  74 ++++++++++
 14 files changed, 465 insertions(+)
 create mode 100644 tests/qemumonitorjsondata/qemumonitorjson-getcpu-empty.data
 create mode 100644 tests/qemumonitorjsondata/qemumonitorjson-getcpu-empty.json
 create mode 100644 tests/qemumonitorjsondata/qemumonitorjson-getcpu-filtered.data
 create mode 100644 tests/qemumonitorjsondata/qemumonitorjson-getcpu-filtered.json
 create mode 100644 tests/qemumonitorjsondata/qemumonitorjson-getcpu-full.data
 create mode 100644 tests/qemumonitorjsondata/qemumonitorjson-getcpu-full.json
 create mode 100644 tests/qemumonitorjsondata/qemumonitorjson-getcpu-host.data
 create mode 100644 tests/qemumonitorjsondata/qemumonitorjson-getcpu-host.json

diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c
index 0b73411..695cf19 100644
--- a/src/qemu/qemu_monitor.c
+++ b/src/qemu/qemu_monitor.c
@@ -3839,3 +3839,24 @@ qemuMonitorGetDeviceAliases(qemuMonitorPtr mon,
 
     return qemuMonitorJSONGetDeviceAliases(mon, aliases);
 }
+
+virCPUDefPtr
+qemuMonitorGetCPU(qemuMonitorPtr mon,
+                  virArch arch)
+{
+    VIR_DEBUG("mon=%p, arch=%s", mon, virArchToString(arch));
+
+    if (!mon) {
+        virReportError(VIR_ERR_INVALID_ARG, "%s",
+                       _("monitor must not be NULL"));
+        return NULL;
+    }
+
+    if (!mon->json) {
+        virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
+                       _("JSON monitor is required"));
+        return NULL;
+    }
+
+    return qemuMonitorJSONGetCPU(mon, arch);
+}
diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h
index 4a55501..8a312bd 100644
--- a/src/qemu/qemu_monitor.h
+++ b/src/qemu/qemu_monitor.h
@@ -717,6 +717,9 @@ int qemuMonitorDetachCharDev(qemuMonitorPtr mon,
 int qemuMonitorGetDeviceAliases(qemuMonitorPtr mon,
                                 char ***aliases);
 
+virCPUDefPtr qemuMonitorGetCPU(qemuMonitorPtr mon,
+                               virArch arch);
+
 /**
  * When running two dd process and using <> redirection, we need a
  * shell that will not truncate files.  These two strings serve that
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c
index 12f7e69..617bfdf 100644
--- a/src/qemu/qemu_monitor_json.c
+++ b/src/qemu/qemu_monitor_json.c
@@ -42,6 +42,7 @@
 #include "virerror.h"
 #include "virjson.h"
 #include "virstring.h"
+#include "cpu/cpu_x86.h"
 
 #ifdef WITH_DTRACE_PROBES
 # include "libvirt_qemu_probes.h"
@@ -49,6 +50,7 @@
 
 #define VIR_FROM_THIS VIR_FROM_QEMU
 
+#define QOM_CPU_PATH  "/machine/unattached/device[0]"
 
 #define LINE_ENDING "\r\n"
 
@@ -5453,3 +5455,163 @@ cleanup:
     VIR_FREE(paths);
     return ret;
 }
+
+
+static int
+qemuMonitorJSONParseCPUFeatureWord(virJSONValuePtr data,
+                                   struct cpuX86cpuid *cpuid)
+{
+    const char *reg;
+    unsigned long long fun;
+    unsigned long long features;
+
+    memset(cpuid, 0, sizeof(*cpuid));
+
+    if (!(reg = virJSONValueObjectGetString(data, "cpuid-register"))) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("missing cpuid-register in CPU data"));
+        return -1;
+    }
+    if (virJSONValueObjectGetNumberUlong(data, "cpuid-input-eax", &fun)) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("missing or invalid cpuid-input-eax in CPU data"));
+        return -1;
+    }
+    if (virJSONValueObjectGetNumberUlong(data, "features", &features) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("missing or invalid features in CPU data"));
+        return -1;
+    }
+
+    cpuid->function = fun;
+    if (STREQ(reg, "EAX")) {
+        cpuid->eax = features;
+    } else if (STREQ(reg, "EBX")) {
+        cpuid->ebx = features;
+    } else if (STREQ(reg, "ECX")) {
+        cpuid->ecx = features;
+    } else if (STREQ(reg, "EDX")) {
+        cpuid->edx = features;
+    } else {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("unknown CPU register '%s'"), reg);
+        return -1;
+    }
+
+    return 0;
+}
+
+virCPUDataPtr
+qemuMonitorJSONGetCPUData(qemuMonitorPtr mon,
+                          const char *property)
+{
+    virJSONValuePtr cmd;
+    virJSONValuePtr reply = NULL;
+    virJSONValuePtr data;
+    virCPUDataPtr cpuData = NULL;
+    struct cpuX86Data *x86Data = NULL;
+    struct cpuX86cpuid cpuid;
+    size_t i;
+    int ret;
+    int n;
+
+    if (!(cmd = qemuMonitorJSONMakeCommand("qom-get",
+                                           "s:path", QOM_CPU_PATH,
+                                           "s:property", property,
+                                           NULL)))
+        return NULL;
+
+    ret = qemuMonitorJSONCommand(mon, cmd, &reply);
+
+    if (ret == 0)
+        ret = qemuMonitorJSONCheckError(cmd, reply);
+
+    if (ret < 0)
+        goto cleanup;
+
+    if (!(data = virJSONValueObjectGet(reply, "return"))) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("qom-get reply was missing return data"));
+        goto cleanup;
+    }
+
+    if ((n = virJSONValueArraySize(data)) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("%s CPU property did not return an array"),
+                       property);
+        goto cleanup;
+    }
+
+    if (VIR_ALLOC(x86Data) < 0)
+        goto cleanup;
+
+    for (i = 0; i < n; i++) {
+        if (qemuMonitorJSONParseCPUFeatureWord(virJSONValueArrayGet(data, i),
+                                               &cpuid) < 0 ||
+            x86DataAddCpuid(x86Data, &cpuid) < 0)
+            goto cleanup;
+    }
+
+    cpuData = x86MakeCPUData(VIR_ARCH_X86_64, &x86Data);
+
+cleanup:
+    virJSONValueFree(cmd);
+    virJSONValueFree(reply);
+    x86DataFree(x86Data);
+    return cpuData;
+}
+
+static int
+qemuMonitorJSONGetCPUVendor(qemuMonitorPtr mon,
+                            virCPUDataPtr data)
+{
+    qemuMonitorJSONObjectProperty prop;
+    int ret;
+
+    prop.type = QEMU_MONITOR_OBJECT_PROPERTY_STRING;
+    if (qemuMonitorJSONGetObjectProperty(mon, QOM_CPU_PATH,
+                                         "vendor", &prop) < 0)
+        return -1;
+
+    ret = x86DataSetVendor(data->data.x86, prop.val.str);
+
+    VIR_FREE(prop.val.str);
+    return ret;
+}
+
+virCPUDefPtr
+qemuMonitorJSONGetCPU(qemuMonitorPtr mon,
+                      virArch arch)
+{
+    virCPUDefPtr cpu;
+    virCPUDataPtr data = NULL;
+
+    if (arch != VIR_ARCH_I686 && arch != VIR_ARCH_X86_64) {
+        virReportError(VIR_ERR_OPERATION_UNSUPPORTED,
+                       _("cannot get CPU data for arch %s"),
+                       virArchToString(arch));
+        return NULL;
+    }
+
+    if (VIR_ALLOC(cpu) < 0)
+        return NULL;
+
+    cpu->arch = arch;
+    cpu->type = VIR_CPU_TYPE_HOST;
+
+    if (!(data = qemuMonitorJSONGetCPUData(mon, "feature-words")) ||
+        qemuMonitorJSONGetCPUVendor(mon, data) < 0)
+        goto error;
+
+    if (cpuDecode(cpu, data, NULL, 0, NULL) < 0)
+        goto error;
+
+cleanup:
+    cpuDataFree(data);
+    return cpu;
+
+error:
+    virCPUDefFree(cpu);
+    cpu = NULL;
+    goto cleanup;
+}
diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h
index 51cf19c..ffc8d68 100644
--- a/src/qemu/qemu_monitor_json.h
+++ b/src/qemu/qemu_monitor_json.h
@@ -29,6 +29,7 @@
 
 # include "qemu_monitor.h"
 # include "virbitmap.h"
+# include "cpu/cpu.h"
 
 int qemuMonitorJSONIOProcess(qemuMonitorPtr mon,
                              const char *data,
@@ -426,4 +427,9 @@ int qemuMonitorJSONDetachCharDev(qemuMonitorPtr mon,
 int qemuMonitorJSONGetDeviceAliases(qemuMonitorPtr mon,
                                     char ***aliases);
 
+virCPUDataPtr qemuMonitorJSONGetCPUData(qemuMonitorPtr mon,
+                                        const char *property);
+virCPUDefPtr qemuMonitorJSONGetCPU(qemuMonitorPtr mon,
+                                   virArch arch);
+
 #endif /* QEMU_MONITOR_JSON_H */
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 5ea806e..8343ce1 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -81,6 +81,7 @@ EXTRA_DIST =		\
 	oomtrace.pl \
 	qemuhelpdata \
 	qemuhotplugtestdata \
+	qemumonitorjsondata \
 	qemuxml2argvdata \
 	qemuxml2xmloutdata \
 	qemuxmlnsdata \
diff --git a/tests/qemumonitorjsondata/qemumonitorjson-getcpu-empty.data b/tests/qemumonitorjsondata/qemumonitorjson-getcpu-empty.data
new file mode 100644
index 0000000..2b3c111
--- /dev/null
+++ b/tests/qemumonitorjsondata/qemumonitorjson-getcpu-empty.data
@@ -0,0 +1,2 @@
+<cpudata arch='x86'>
+</cpudata>
diff --git a/tests/qemumonitorjsondata/qemumonitorjson-getcpu-empty.json b/tests/qemumonitorjsondata/qemumonitorjson-getcpu-empty.json
new file mode 100644
index 0000000..36db555
--- /dev/null
+++ b/tests/qemumonitorjsondata/qemumonitorjson-getcpu-empty.json
@@ -0,0 +1,46 @@
+{
+    "return": [
+        {
+            "cpuid-register": "EDX",
+            "cpuid-input-eax": 2147483658,
+            "features": 0
+        },
+        {
+            "cpuid-register": "EAX",
+            "cpuid-input-eax": 1073741825,
+            "features": 0
+        },
+        {
+            "cpuid-register": "EDX",
+            "cpuid-input-eax": 3221225473,
+            "features": 0
+        },
+        {
+            "cpuid-register": "ECX",
+            "cpuid-input-eax": 2147483649,
+            "features": 0
+        },
+        {
+            "cpuid-register": "EDX",
+            "cpuid-input-eax": 2147483649,
+            "features": 0
+        },
+        {
+            "cpuid-register": "EBX",
+            "cpuid-input-ecx": 0,
+            "cpuid-input-eax": 7,
+            "features": 0
+        },
+        {
+            "cpuid-register": "ECX",
+            "cpuid-input-eax": 1,
+            "features": 0
+        },
+        {
+            "cpuid-register": "EDX",
+            "cpuid-input-eax": 1,
+            "features": 0
+        }
+    ],
+    "id": "libvirt-6"
+}
diff --git a/tests/qemumonitorjsondata/qemumonitorjson-getcpu-filtered.data b/tests/qemumonitorjsondata/qemumonitorjson-getcpu-filtered.data
new file mode 100644
index 0000000..57431e2
--- /dev/null
+++ b/tests/qemumonitorjsondata/qemumonitorjson-getcpu-filtered.data
@@ -0,0 +1,4 @@
+<cpudata arch='x86'>
+  <cpuid function='0x00000001' eax='0x00000000' ebx='0x00000000' ecx='0x00401000' edx='0x00000000'/>
+  <cpuid function='0x00000007' eax='0x00000000' ebx='0x00000fb9' ecx='0x00000000' edx='0x00000000'/>
+</cpudata>
diff --git a/tests/qemumonitorjsondata/qemumonitorjson-getcpu-filtered.json b/tests/qemumonitorjsondata/qemumonitorjson-getcpu-filtered.json
new file mode 100644
index 0000000..13352f9
--- /dev/null
+++ b/tests/qemumonitorjsondata/qemumonitorjson-getcpu-filtered.json
@@ -0,0 +1,46 @@
+{
+    "return": [
+        {
+            "cpuid-register": "EDX",
+            "cpuid-input-eax": 2147483658,
+            "features": 0
+        },
+        {
+            "cpuid-register": "EAX",
+            "cpuid-input-eax": 1073741825,
+            "features": 0
+        },
+        {
+            "cpuid-register": "EDX",
+            "cpuid-input-eax": 3221225473,
+            "features": 0
+        },
+        {
+            "cpuid-register": "ECX",
+            "cpuid-input-eax": 2147483649,
+            "features": 0
+        },
+        {
+            "cpuid-register": "EDX",
+            "cpuid-input-eax": 2147483649,
+            "features": 0
+        },
+        {
+            "cpuid-register": "EBX",
+            "cpuid-input-ecx": 0,
+            "cpuid-input-eax": 7,
+            "features": 4025
+        },
+        {
+            "cpuid-register": "ECX",
+            "cpuid-input-eax": 1,
+            "features": 4198400
+        },
+        {
+            "cpuid-register": "EDX",
+            "cpuid-input-eax": 1,
+            "features": 0
+        }
+    ],
+    "id": "libvirt-7"
+}
diff --git a/tests/qemumonitorjsondata/qemumonitorjson-getcpu-full.data b/tests/qemumonitorjsondata/qemumonitorjson-getcpu-full.data
new file mode 100644
index 0000000..6e3af3c
--- /dev/null
+++ b/tests/qemumonitorjsondata/qemumonitorjson-getcpu-full.data
@@ -0,0 +1,4 @@
+<cpudata arch='x86'>
+  <cpuid function='0x00000001' eax='0x00000000' ebx='0x00000000' ecx='0x97ba2223' edx='0x078bfbfd'/>
+  <cpuid function='0x80000001' eax='0x00000000' ebx='0x00000000' ecx='0x00000001' edx='0x28100800'/>
+</cpudata>
diff --git a/tests/qemumonitorjsondata/qemumonitorjson-getcpu-full.json b/tests/qemumonitorjsondata/qemumonitorjson-getcpu-full.json
new file mode 100644
index 0000000..29c00b4
--- /dev/null
+++ b/tests/qemumonitorjsondata/qemumonitorjson-getcpu-full.json
@@ -0,0 +1,46 @@
+{
+    "return": [
+        {
+            "cpuid-register": "EDX",
+            "cpuid-input-eax": 2147483658,
+            "features": 0
+        },
+        {
+            "cpuid-register": "EAX",
+            "cpuid-input-eax": 1073741825,
+            "features": 16777275
+        },
+        {
+            "cpuid-register": "EDX",
+            "cpuid-input-eax": 3221225473,
+            "features": 0
+        },
+        {
+            "cpuid-register": "ECX",
+            "cpuid-input-eax": 2147483649,
+            "features": 1
+        },
+        {
+            "cpuid-register": "EDX",
+            "cpuid-input-eax": 2147483649,
+            "features": 672139264
+        },
+        {
+            "cpuid-register": "EBX",
+            "cpuid-input-ecx": 0,
+            "cpuid-input-eax": 7,
+            "features": 0
+        },
+        {
+            "cpuid-register": "ECX",
+            "cpuid-input-eax": 1,
+            "features": 2545558051
+        },
+        {
+            "cpuid-register": "EDX",
+            "cpuid-input-eax": 1,
+            "features": 126614525
+        }
+    ],
+    "id": "libvirt-6"
+}
diff --git a/tests/qemumonitorjsondata/qemumonitorjson-getcpu-host.data b/tests/qemumonitorjsondata/qemumonitorjson-getcpu-host.data
new file mode 100644
index 0000000..bc3087d
--- /dev/null
+++ b/tests/qemumonitorjsondata/qemumonitorjson-getcpu-host.data
@@ -0,0 +1,5 @@
+<cpudata arch='x86'>
+  <cpuid function='0x00000001' eax='0x00000000' ebx='0x00000000' ecx='0x97ba2223' edx='0x0f8bfbff'/>
+  <cpuid function='0x00000007' eax='0x00000000' ebx='0x00000002' ecx='0x00000000' edx='0x00000000'/>
+  <cpuid function='0x80000001' eax='0x00000000' ebx='0x00000000' ecx='0x00000001' edx='0x2993fbff'/>
+</cpudata>
diff --git a/tests/qemumonitorjsondata/qemumonitorjson-getcpu-host.json b/tests/qemumonitorjsondata/qemumonitorjson-getcpu-host.json
new file mode 100644
index 0000000..b5fb9f3
--- /dev/null
+++ b/tests/qemumonitorjsondata/qemumonitorjson-getcpu-host.json
@@ -0,0 +1,45 @@
+{
+    "return": [
+        {
+            "cpuid-register": "EDX",
+            "cpuid-input-eax": 2147483658,
+            "features": 0
+        },
+        {
+            "cpuid-register": "EAX",
+            "cpuid-input-eax": 1073741825,
+            "features": 16777339
+        },
+        {
+            "cpuid-register": "EDX",
+            "cpuid-input-eax": 3221225473,
+            "features": 0
+        },
+        {
+            "cpuid-register": "ECX",
+            "cpuid-input-eax": 2147483649,
+            "features": 1
+        },
+        {
+            "cpuid-register": "EDX",
+            "cpuid-input-eax": 2147483649,
+            "features": 697564159
+        },
+        {
+            "cpuid-register": "EBX",
+            "cpuid-input-ecx": 0,
+            "cpuid-input-eax": 7,
+            "features": 2
+        },
+        {
+            "cpuid-register": "ECX",
+            "cpuid-input-eax": 1,
+            "features": 2545558051
+        },
+        {
+            "cpuid-register": "EDX",
+            "cpuid-input-eax": 1,
+            "features": 260832255
+        }
+    ]
+}
diff --git a/tests/qemumonitorjsontest.c b/tests/qemumonitorjsontest.c
index 9e66059..aec16bb 100644
--- a/tests/qemumonitorjsontest.c
+++ b/tests/qemumonitorjsontest.c
@@ -27,6 +27,7 @@
 #include "virthread.h"
 #include "virerror.h"
 #include "virstring.h"
+#include "cpu/cpu.h"
 
 
 #define VIR_FROM_THIS VIR_FROM_NONE
@@ -943,6 +944,66 @@ cleanup:
 }
 
 
+struct testCPUData {
+    const char *name;
+    const char *property;
+    const virDomainXMLOptionPtr xmlopt;
+};
+
+static int
+testQemuMonitorJSONGetCPUData(const void *opaque)
+{
+    const struct testCPUData *data = opaque;
+    qemuMonitorTestPtr test = qemuMonitorTestNew(true, data->xmlopt);
+    virCPUDataPtr cpuData = NULL;
+    char *jsonFile = NULL;
+    char *dataFile = NULL;
+    char *jsonStr = NULL;
+    char *expected = NULL;
+    char *actual = NULL;
+    int ret = -1;
+
+    if (!test)
+        return -1;
+
+    if (virAsprintf(&jsonFile,
+                    "%s/qemumonitorjsondata/qemumonitorjson-getcpu-%s.json",
+                    abs_srcdir, data->name) < 0 ||
+        virAsprintf(&dataFile,
+                    "%s/qemumonitorjsondata/qemumonitorjson-getcpu-%s.data",
+                    abs_srcdir, data->name) < 0)
+        goto cleanup;
+
+    if (virtTestLoadFile(jsonFile, &jsonStr) < 0 ||
+        virtTestLoadFile(dataFile, &expected) < 0)
+        goto cleanup;
+
+    if (qemuMonitorTestAddItem(test, "qom-get", jsonStr) < 0)
+        goto cleanup;
+
+    cpuData = qemuMonitorJSONGetCPUData(qemuMonitorTestGetMonitor(test),
+                                        data->property);
+    if (!cpuData || !(actual = cpuDataFormat(cpuData)))
+        goto cleanup;
+
+    if (STRNEQ(expected, actual)) {
+        virtTestDifference(stderr, expected, actual);
+        goto cleanup;
+    }
+
+    ret = 0;
+cleanup:
+    VIR_FREE(jsonFile);
+    VIR_FREE(dataFile);
+    VIR_FREE(jsonStr);
+    VIR_FREE(expected);
+    VIR_FREE(actual);
+    cpuDataFree(cpuData);
+    qemuMonitorTestFree(test);
+    return ret;
+}
+
+
 static int
 mymain(void)
 {
@@ -964,6 +1025,14 @@ mymain(void)
     if (virtTestRun(# name, 1, testQemuMonitorJSON ## name, xmlopt) < 0) \
         ret = -1
 
+#define DO_TEST_CPU_DATA(name, property) \
+    do {                                                                    \
+        struct testCPUData data = { name, property, xmlopt };               \
+        const char *label = "GetCPU(" property ", " name ")";               \
+        if (virtTestRun(label, 1, testQemuMonitorJSONGetCPUData, &data) < 0)\
+            ret = -1;                                                       \
+    } while (0)
+
     DO_TEST(GetStatus);
     DO_TEST(GetVersion);
     DO_TEST(GetMachines);
@@ -978,6 +1047,11 @@ mymain(void)
     DO_TEST(SetObjectProperty);
     DO_TEST(GetDeviceAliases);
 
+    DO_TEST_CPU_DATA("host", "feature-words");
+    DO_TEST_CPU_DATA("full", "feature-words");
+    DO_TEST_CPU_DATA("filtered", "filtered-features");
+    DO_TEST_CPU_DATA("empty", "filtered-features");
+
     virObjectUnref(xmlopt);
 
     return (ret == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
-- 
1.8.3.2




More information about the libvir-list mailing list