mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2025-01-03 03:25:20 +00:00
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:
parent
f80a11c921
commit
3afde0756f
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -89,6 +89,7 @@ EXTRA_DIST = \
|
||||
qemucapabilitiesdata \
|
||||
qemuhelpdata \
|
||||
qemuhotplugtestdata \
|
||||
qemumonitorjsondata \
|
||||
qemuxml2argvdata \
|
||||
qemuxml2xmloutdata \
|
||||
qemuxmlnsdata \
|
||||
|
@ -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>
|
46
tests/qemumonitorjsondata/qemumonitorjson-getcpu-full.json
Normal file
46
tests/qemumonitorjsondata/qemumonitorjson-getcpu-full.json
Normal 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"
|
||||
}
|
@ -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>
|
45
tests/qemumonitorjsondata/qemumonitorjson-getcpu-host.json
Normal file
45
tests/qemumonitorjsondata/qemumonitorjson-getcpu-host.json
Normal 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
|
||||
}
|
||||
]
|
||||
}
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user