From 17322e551861a5bc4dd464a7c5399204dc8c7caa Mon Sep 17 00:00:00 2001 From: Joao Martins Date: Wed, 20 Jul 2016 20:08:47 +0100 Subject: [PATCH] libxl: describe host cpu features based on hwcaps Parse libxl_hwcap accounting for versions since Xen 4.4 - Xen 4.7. libxl_hwcaps is a set of cpuid leaves output that is described in [0] or [1] in Xen 4.7. This is a collection of CPUID leaves that we version in libvirt whenever feature words are reordered or added. Thus we keep the common ones in one struct and others for each version. Since libxl_hwcaps doesn't appear to have a stable format across all supported versions thus we need to keep track of changes as a compromise until it's exported in xen libxl API. We don't fail in initializing the driver in case parsing of hwcaps failed for that reason. In addition, change the notation on PAE feature such that is easier to read which bit it corresponds. [0] xen/include/asm-x86/cpufeature.h [1] xen/include/public/arch-x86/cpufeatureset.h Signed-off-by: Joao Martins --- src/libxl/libxl_capabilities.c | 126 ++++++++++++++++++++++++++++++++- 1 file changed, 123 insertions(+), 3 deletions(-) diff --git a/src/libxl/libxl_capabilities.c b/src/libxl/libxl_capabilities.c index f71fd3bd6f..a1c0a8775a 100644 --- a/src/libxl/libxl_capabilities.c +++ b/src/libxl/libxl_capabilities.c @@ -36,6 +36,8 @@ #include "domain_capabilities.h" #include "vircommand.h" #include "libxl_capabilities.h" +#include "cpu/cpu_x86.h" +#include "cpu/cpu_x86_data.h" #define VIR_FROM_THIS VIR_FROM_LIBXL @@ -43,8 +45,13 @@ VIR_LOG_INIT("libxl.libxl_capabilities"); /* see xen-unstable.hg/xen/include/asm-x86/cpufeature.h */ -#define LIBXL_X86_FEATURE_PAE_MASK 0x40 +#define LIBXL_X86_FEATURE_PAE_MASK (1 << 6) +#define LIBXL_X86_FEATURE_LM_MASK (1 << 29) +enum libxlHwcapVersion { + LIBXL_HWCAP_V0 = 0, /* for Xen 4.4 .. 4.6 */ + LIBXL_HWCAP_V1, /* for Xen 4.7 and up */ +}; struct guest_arch { virArch arch; @@ -57,17 +64,107 @@ struct guest_arch { #define XEN_CAP_REGEX "(xen|hvm)-[[:digit:]]+\\.[[:digit:]]+-(aarch64|armv7l|x86_32|x86_64|ia64|powerpc64)(p|be)?" +static int +libxlCapsAddCPUID(virCPUx86Data *data, virCPUx86CPUID *cpuid, ssize_t ncaps) +{ + size_t i; + + for (i = 0; i < ncaps; i++) { + virCPUx86CPUID *c = &cpuid[i]; + + if (virCPUx86DataAddCPUID(data, c) < 0) { + VIR_DEBUG("Failed to add CPUID(%x,%x)", c->eax_in, c->ecx_in); + return -1; + } + } + + return 0; +} + +/* + * The words represented in physinfo.hw_cap are host CPUID (sub) leafs. + * Position of these hasn't changed much up until Xen 4.7 with a rework + * on how CPUID is handled internally. As a side-effect it got normalized + * and also added more feature words. Although cannot be relied upon as + * stable interface, and hence we version changes in position of the features + * across all supported versions of the libxl driver until libxl exposes a + * stable representation of these capabilities. Fortunately not a lot of + * variation happened so it's still trivial to keep track of these leafs + * to describe host CPU in libvirt capabilities. v0 stands for Xen 4.4 + * up to 4.6, while v1 is meant for Xen 4.7, as depicted in the table below: + * + * | v0 (Xen 4.4 - 4.6) | v1 (Xen >= 4.7) | + * --------------------------------------------- + * word 0 | CPUID.00000001.EDX | CPUID.00000001.EDX | + * word 1 | CPUID.80000001.EDX | CPUID.00000001.ECX | + * word 2 | CPUID.80860001 | CPUID.80000001.EDX | + * word 3 | - Linux - | CPUID.80000001.ECX | + * word 4 | CPUID.00000001.ECX | CPUID.0000000D:1.EAX | + * word 5 | CPUID.C0000001 | CPUID.00000007:0.EBX | + * word 6 | CPUID.80000001.ECX | CPUID.00000007:0.ECX | + * word 7 | CPUID.00000007.EBX | CPUID.80000007.EDX | + * word 8 | - Non existent - | CPUID.80000008.EBX | + * + */ +static virCPUDataPtr +libxlCapsNodeData(virCPUDefPtr cpu, libxl_hwcap hwcap, + enum libxlHwcapVersion version) +{ + ssize_t ncaps; + virCPUDataPtr cpudata = NULL; + virCPUx86Data data = VIR_CPU_X86_DATA_INIT; + virCPUx86CPUID cpuid[] = { + { .eax_in = 0x00000001, + .edx = hwcap[0] }, + { .eax_in = 0x00000001, + .ecx = (version > LIBXL_HWCAP_V0 ? hwcap[1] : hwcap[4]) }, + { .eax_in = 0x80000001, + .edx = (version > LIBXL_HWCAP_V0 ? hwcap[2] : hwcap[1]) }, + { .eax_in = 0x80000001, + .ecx = (version > LIBXL_HWCAP_V0 ? hwcap[3] : hwcap[6]) }, + { .eax_in = 0x00000007, + .ebx = (version > LIBXL_HWCAP_V0 ? hwcap[5] : hwcap[7]) }, + }; + virCPUx86CPUID cpuid_ver1[] = { + { .eax_in = 0x0000000D, .ecx_in = 1U, .eax = hwcap[4] }, + { .eax_in = 0x00000007, .ecx_in = 0U, .ecx = hwcap[6] }, + { .eax_in = 0x80000007, .ecx_in = 0U, .edx = hwcap[7] }, + }; + + ncaps = ARRAY_CARDINALITY(cpuid); + if (libxlCapsAddCPUID(&data, cpuid, ncaps) < 0) + goto error; + + ncaps = ARRAY_CARDINALITY(cpuid_ver1); + if (version > LIBXL_HWCAP_V0 && + libxlCapsAddCPUID(&data, cpuid_ver1, ncaps) < 0) + goto error; + + cpudata = virCPUx86MakeData(cpu->arch, &data); + + error: + virCPUx86DataClear(&data); + return cpudata; +} + /* hw_caps is an array of 32-bit words whose meaning is listed in * xen-unstable.hg/xen/include/asm-x86/cpufeature.h. Each feature * is defined in the form X*32+Y, corresponding to the Y'th bit in * the X'th 32-bit word of hw_cap. */ static int -libxlCapsInitCPU(virCapsPtr caps, libxl_physinfo *phy_info) +libxlCapsInitCPU(virCapsPtr caps, libxl_physinfo *phy_info, + enum libxlHwcapVersion version) { + virCPUDataPtr data = NULL; virCPUDefPtr cpu = NULL; int ret = -1; int host_pae; + int host_lm; + + /* On ARM hw_cap vector is zeroed out but not on x86 */ + if (!phy_info->hw_cap[0]) + return 0; if (VIR_ALLOC(cpu) < 0) goto error; @@ -77,6 +174,13 @@ libxlCapsInitCPU(virCapsPtr caps, libxl_physinfo *phy_info) virCapabilitiesAddHostFeature(caps, "pae") < 0) goto error; + host_lm = (phy_info->hw_cap[version > LIBXL_HWCAP_V0 ? 2 : 1] + & LIBXL_X86_FEATURE_LM_MASK); + if (host_lm) + cpu->arch = VIR_ARCH_X86_64; + else + cpu->arch = VIR_ARCH_I686; + cpu->type = VIR_CPU_TYPE_HOST; cpu->cores = phy_info->cores_per_socket; cpu->threads = phy_info->threads_per_core; @@ -85,7 +189,14 @@ libxlCapsInitCPU(virCapsPtr caps, libxl_physinfo *phy_info) ret = 0; + if (!(data = libxlCapsNodeData(cpu, phy_info->hw_cap, version)) || + cpuDecode(cpu, data, NULL, 0, NULL) < 0) { + VIR_WARN("Failed to initialize host cpu features"); + goto error; + } + cleanup: + cpuDataFree(data); return ret; @@ -97,6 +208,8 @@ libxlCapsInitCPU(virCapsPtr caps, libxl_physinfo *phy_info) static int libxlCapsInitHost(libxl_ctx *ctx, virCapsPtr caps) { + const libxl_version_info *ver_info; + enum libxlHwcapVersion version; libxl_physinfo phy_info; if (libxl_get_physinfo(ctx, &phy_info) != 0) { @@ -105,7 +218,14 @@ libxlCapsInitHost(libxl_ctx *ctx, virCapsPtr caps) return -1; } - if (libxlCapsInitCPU(caps, &phy_info) < 0) + if ((ver_info = libxl_get_version_info(ctx)) == NULL) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Failed to get version info from libxenlight")); + return -1; + } + + version = (ver_info->xen_version_minor >= 7); + if (libxlCapsInitCPU(caps, &phy_info, version) < 0) return -1; if (virCapabilitiesSetNetPrefix(caps, LIBXL_GENERATED_PREFIX_XEN) < 0)