From: Jiri Denemark <jdenemar(a)redhat.com>
The qemu monitor supports retrieval of actual CPUID bits presented to
the guest using QMP monitor. Add APIs to extract these information and
tests for them.
Signed-off-by: Peter Krempa <pkrempa(a)redhat.com>
---
src/qemu/qemu_monitor.c | 21 ++++
src/qemu/qemu_monitor.h | 3 +
src/qemu/qemu_monitor_json.c | 107 +++++++++++++++++++++
src/qemu/qemu_monitor_json.h | 3 +
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 | 5 +
.../qemumonitorjson-getcpu-full.json | 46 +++++++++
.../qemumonitorjson-getcpu-host.data | 6 ++
.../qemumonitorjson-getcpu-host.json | 45 +++++++++
tests/qemumonitorjsontest.c | 76 +++++++++++++++
14 files changed, 411 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 2bafe28..f1556d8 100644
--- a/src/qemu/qemu_monitor.c
+++ b/src/qemu/qemu_monitor.c
@@ -3926,3 +3926,24 @@ qemuMonitorSetDomainLog(qemuMonitorPtr mon, int logfd)
return 0;
}
+
+
+virCPUDataPtr
+qemuMonitorGetGuestCPU(qemuMonitorPtr mon)
+{
+ VIR_DEBUG("mon=%p", mon);
+
+ 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 qemuMonitorJSONGetCPUData(mon, "feature-words");
+}
diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h
index 06ba7e8..65ade56 100644
--- a/src/qemu/qemu_monitor.h
+++ b/src/qemu/qemu_monitor.h
@@ -32,6 +32,7 @@
# include "virhash.h"
# include "virjson.h"
# include "device_conf.h"
+# include "cpu/cpu.h"
typedef struct _qemuMonitor qemuMonitor;
typedef qemuMonitor *qemuMonitorPtr;
@@ -763,6 +764,8 @@ int qemuMonitorGetDeviceAliases(qemuMonitorPtr mon,
int qemuMonitorSetDomainLog(qemuMonitorPtr mon, int logfd);
+virCPUDataPtr qemuMonitorGetGuestCPU(qemuMonitorPtr mon);
+
/**
* 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 05f8aa6..3a5c2ad 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"
@@ -5454,3 +5456,108 @@ cleanup:
VIR_FREE(paths);
return ret;
}
+
+
+static int
+qemuMonitorJSONParseCPUFeatureWord(virJSONValuePtr data,
+ virCPUx86CPUID *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;
+ virCPUx86Data *x86Data = NULL;
+ virCPUx86CPUID 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 ||
+ virCPUx86DataAddCPUID(x86Data, &cpuid) < 0)
+ goto cleanup;
+ }
+
+ cpuData = virCPUx86MakeData(VIR_ARCH_X86_64, &x86Data);
+
+cleanup:
+ virJSONValueFree(cmd);
+ virJSONValueFree(reply);
+ virCPUx86DataFree(x86Data);
+ return cpuData;
+}
diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h
index 51cf19c..25f1fe6 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,6 @@ int qemuMonitorJSONDetachCharDev(qemuMonitorPtr mon,
int qemuMonitorJSONGetDeviceAliases(qemuMonitorPtr mon,
char ***aliases);
+virCPUDataPtr qemuMonitorJSONGetCPUData(qemuMonitorPtr mon,
+ const char *property);
#endif /* QEMU_MONITOR_JSON_H */
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 250cd8c..9ec4fcb 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -85,6 +85,7 @@ EXTRA_DIST = \
qemucapabilitiesdata \
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..bba8d31
--- /dev/null
+++ b/tests/qemumonitorjsondata/qemumonitorjson-getcpu-full.data
@@ -0,0 +1,5 @@
+<cpudata arch='x86'>
+ <cpuid function='0x00000001' eax='0x00000000'
ebx='0x00000000' ecx='0x97ba2223' edx='0x078bfbfd'/>
+ <cpuid function='0x40000001' eax='0x0100003b'
ebx='0x00000000' ecx='0x00000000' edx='0x00000000'/>
+ <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..98b9e7c
--- /dev/null
+++ b/tests/qemumonitorjsondata/qemumonitorjson-getcpu-host.data
@@ -0,0 +1,6 @@
+<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='0x40000001' eax='0x0100007b'
ebx='0x00000000' 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 de907ae..7ca3fb0 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
@@ -1958,6 +1959,68 @@ cleanup:
return ret;
}
+
+struct testCPUData {
+ const char *name;
+ const char *property;
+ virDomainXMLOptionPtr xmlopt;
+};
+
+
+static int
+testQemuMonitorJSONGetCPUData(const void *opaque)
+{
+ const struct testCPUData *data = opaque;
+ qemuMonitorTestPtr test = qemuMonitorTestNewSimple(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)
{
@@ -1991,6 +2054,14 @@ mymain(void)
if (virtTestRun(# name, testQemuMonitorJSON ## name, &simpleFunc) < 0) \
ret = -1
+#define DO_TEST_CPU_DATA(name, property) \
+ do { \
+ struct testCPUData data = { name, property, xmlopt }; \
+ const char *label = "GetCPUData(" property ", " name
")"; \
+ if (virtTestRun(label, testQemuMonitorJSONGetCPUData, &data) < 0) \
+ ret = -1; \
+ } while (0)
+
DO_TEST(GetStatus);
DO_TEST(GetVersion);
DO_TEST(GetMachines);
@@ -2055,6 +2126,11 @@ mymain(void)
DO_TEST(qemuMonitorJSONGetVirtType);
DO_TEST(qemuMonitorJSONSendKey);
+ 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