qemu: Add monitor APIs to fetch CPUID data from QEMU

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@redhat.com>
This commit is contained in:
Jiri Denemark 2013-07-22 13:07:23 +02:00 committed by Peter Krempa
parent f80a11c921
commit 3afde0756f
10 changed files with 348 additions and 0 deletions

View File

@ -3927,3 +3927,34 @@ qemuMonitorSetDomainLog(qemuMonitorPtr mon, int logfd)
return 0;
}
/**
* qemuMonitorJSONGetGuestCPU:
* @mon: Pointer to the monitor
* @arch: arch of the guest
*
* Retrieve the definition of the guest CPU from a running qemu instance.
*
* Returns the cpu definition object. On error returns NULL.
*/
virCPUDataPtr
qemuMonitorGetGuestCPU(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 qemuMonitorJSONGetGuestCPU(mon, arch);
}

View File

@ -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,9 @@ int qemuMonitorGetDeviceAliases(qemuMonitorPtr mon,
int qemuMonitorSetDomainLog(qemuMonitorPtr mon, int logfd);
virCPUDataPtr qemuMonitorGetGuestCPU(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

View File

@ -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,134 @@ cleanup:
VIR_FREE(paths);
return ret;
}
static int
qemuMonitorJSONParseCPUx86FeatureWord(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) < 0) {
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;
}
static virCPUDataPtr
qemuMonitorJSONGetCPUx86Data(qemuMonitorPtr mon,
const char *property)
{
virJSONValuePtr cmd;
virJSONValuePtr reply = NULL;
virJSONValuePtr data;
virCPUx86Data *x86Data = NULL;
virCPUx86CPUID cpuid;
size_t i;
virCPUDataPtr ret = NULL;
int n;
if (!(cmd = qemuMonitorJSONMakeCommand("qom-get",
"s:path", QOM_CPU_PATH,
"s:property", property,
NULL)))
return NULL;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckError(cmd, reply))
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 (qemuMonitorJSONParseCPUx86FeatureWord(virJSONValueArrayGet(data, i),
&cpuid) < 0 ||
virCPUx86DataAddCPUID(x86Data, &cpuid) < 0)
goto cleanup;
}
if (!(ret = virCPUx86MakeData(VIR_ARCH_X86_64, &x86Data)))
goto cleanup;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
virCPUx86DataFree(x86Data);
return ret;
}
/**
* qemuMonitorJSONGetGuestCPU:
* @mon: Pointer to the monitor
* @arch: arch of the guest
*
* Retrieve the definition of the guest CPU from a running qemu instance.
*
* Returns the cpu definition object. On error returns NULL.
*/
virCPUDataPtr
qemuMonitorJSONGetGuestCPU(qemuMonitorPtr mon,
virArch arch)
{
switch (arch) {
case VIR_ARCH_X86_64:
case VIR_ARCH_I686:
return qemuMonitorJSONGetCPUx86Data(mon, "feature-words");
default:
virReportError(VIR_ERR_INTERNAL_ERROR,
_("CPU definition retrieval isn't supported for '%s'"),
virArchToString(arch));
return NULL;
}
}

View File

@ -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,5 @@ int qemuMonitorJSONDetachCharDev(qemuMonitorPtr mon,
int qemuMonitorJSONGetDeviceAliases(qemuMonitorPtr mon,
char ***aliases);
virCPUDataPtr qemuMonitorJSONGetGuestCPU(qemuMonitorPtr mon, virArch arch);
#endif /* QEMU_MONITOR_JSON_H */

View File

@ -89,6 +89,7 @@ EXTRA_DIST = \
qemucapabilitiesdata \
qemuhelpdata \
qemuhotplugtestdata \
qemumonitorjsondata \
qemuxml2argvdata \
qemuxml2xmloutdata \
qemuxmlnsdata \

View File

@ -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>

View File

@ -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"
}

View File

@ -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>

View File

@ -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
}
]
}

View File

@ -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,69 @@ cleanup:
return ret;
}
struct testCPUData {
const char *name;
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;
if (!(cpuData = qemuMonitorJSONGetGuestCPU(qemuMonitorTestGetMonitor(test),
VIR_ARCH_X86_64)))
goto cleanup;
if (!(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 +2055,14 @@ mymain(void)
if (virtTestRun(# name, testQemuMonitorJSON ## name, &simpleFunc) < 0) \
ret = -1
#define DO_TEST_CPU_DATA(name) \
do { \
struct testCPUData data = { name, xmlopt }; \
const char *label = "GetCPUData(" name ")"; \
if (virtTestRun(label, testQemuMonitorJSONGetCPUData, &data) < 0) \
ret = -1; \
} while (0)
DO_TEST(GetStatus);
DO_TEST(GetVersion);
DO_TEST(GetMachines);
@ -2055,6 +2127,9 @@ mymain(void)
DO_TEST(qemuMonitorJSONGetVirtType);
DO_TEST(qemuMonitorJSONSendKey);
DO_TEST_CPU_DATA("host");
DO_TEST_CPU_DATA("full");
virObjectUnref(xmlopt);
return (ret == 0) ? EXIT_SUCCESS : EXIT_FAILURE;