This adds an option to use libcpuinfo [1] as data source for libvirt's list of x86 cpu features. This is purely optional and does not change the script's behavior if libcpuinfo is not installed. libcpuinfo is a cross-vendor, cross-architecture source for CPU related information that has the capability to replace libvirt's dependence on qemu's cpu feature list. [1] https://gitlab.com/twiederh/libcpuinfo Signed-off-by: Tim Wiederhake <twiederh@xxxxxxxxxx> --- src/cpu_map/libcpuinfo_aliases.xml | 75 +++++++++++++++++++++++++ src/cpu_map/sync_qemu_features_i386.py | 77 +++++++++++++++++++++++--- 2 files changed, 145 insertions(+), 7 deletions(-) create mode 100644 src/cpu_map/libcpuinfo_aliases.xml diff --git a/src/cpu_map/libcpuinfo_aliases.xml b/src/cpu_map/libcpuinfo_aliases.xml new file mode 100644 index 0000000000..75d243fead --- /dev/null +++ b/src/cpu_map/libcpuinfo_aliases.xml @@ -0,0 +1,75 @@ +<!-- + libvirt uses slightly different names for some cpu features than other + software does. Install this file into libcpuinfo's alias directory + (e.g. /usr/share/libcpuinfo/aliases/libvirt.xml) to have libcpuinfo + automatically translate feature names into the names libvirt uses. +--> + +<external_aliases> + <external_alias> + <type>feature</type> + <canonical>pclmulqdq</canonical> + <name>pclmuldq</name> + <domain>libvirt</domain> + </external_alias> + <external_alias> + <type>feature</type> + <canonical>ds-cpl</canonical> + <name>ds_cpl</name> + <domain>libvirt</domain> + </external_alias> + <external_alias> + <type>feature</type> + <canonical>sse4-1</canonical> + <name>sse4.1</name> + <domain>libvirt</domain> + </external_alias> + <external_alias> + <type>feature</type> + <canonical>sse4-2</canonical> + <name>sse4.2</name> + <domain>libvirt</domain> + </external_alias> + <external_alias> + <type>feature</type> + <canonical>tsc-adjust</canonical> + <name>tsc_adjust</name> + <domain>libvirt</domain> + </external_alias> + <external_alias> + <type>feature</type> + <canonical>lahf-lm</canonical> + <name>lahf_lm</name> + <domain>libvirt</domain> + </external_alias> + <external_alias> + <type>feature</type> + <canonical>cmp-legacy</canonical> + <name>cmp_legacy</name> + <domain>libvirt</domain> + </external_alias> + <external_alias> + <type>feature</type> + <canonical>nodeid-msr</canonical> + <name>nodeid_msr</name> + <domain>libvirt</domain> + </external_alias> + <external_alias> + <type>feature</type> + <canonical>perfctr-core</canonical> + <name>perfctr_core</name> + <domain>libvirt</domain> + </external_alias> + <external_alias> + <type>feature</type> + <canonical>perfctr-nb</canonical> + <name>perfctr_nb</name> + <domain>libvirt</domain> + </external_alias> + <external_alias> + <type>feature</type> + <canonical>fxsr-opt</canonical> + <name>fxsr_opt</name> + <domain>libvirt</domain> + </external_alias> +</external_aliases> diff --git a/src/cpu_map/sync_qemu_features_i386.py b/src/cpu_map/sync_qemu_features_i386.py index e4b1f7275a..3b3ad5a643 100755 --- a/src/cpu_map/sync_qemu_features_i386.py +++ b/src/cpu_map/sync_qemu_features_i386.py @@ -4,6 +4,11 @@ import argparse import os import re +try: + import pycpuinfo +except ImportError: + pycpuinfo = None + # features in qemu that we do not want in libvirt FEATURES_IGNORE = ( @@ -22,6 +27,7 @@ FEATURES_IGNORE = ( "kvm-steal-time", "kvmclock", "kvmclock-stable-bit", + "kvmclock2", "xstore", "xstore-en", @@ -295,6 +301,53 @@ def add_feature_qemu(query, data): add_feature_cpuid(eax, ecx, reg, bit, name) +def add_features_cpuinfo(): + def decode_bit(value): + for i in range(0, 64): + if value == (1 << i): + return i + + def decode_cpuid(v): + if v[0] != 0 and v[1] == 0 and v[2] == 0 and v[3] == 0: + reg, val = "eax", v[0] + if v[0] == 0 and v[1] != 0 and v[2] == 0 and v[3] == 0: + reg, val = "ebx", v[1] + if v[0] == 0 and v[1] == 0 and v[2] != 0 and v[3] == 0: + reg, val = "ecx", v[2] + if v[0] == 0 and v[1] == 0 and v[2] == 0 and v[3] != 0: + reg, val = "edx", v[3] + + return reg, decode_bit(val) + + x86 = pycpuinfo.Family.find("x86", "") + + for feature in pycpuinfo.features(): + if feature.family() != x86: + continue + + if list(feature.features()): + continue + + name = feature.name("libvirt") + if name in FEATURES_IGNORE: + continue + + cpuid = feature.extra_x86_cpuid() + if cpuid: + eax = cpuid[0] + ecx = cpuid[1] + if ecx == pycpuinfo.x86.CPUINFO_X86_CPUID_ECX_NONE: + ecx = None + reg, bit = decode_cpuid(cpuid[2:]) + add_feature_cpuid(eax, ecx, reg, bit, name) + + msr = feature.extra_x86_msr() + if msr: + index = msr[0] + bit = decode_bit(msr[1] | (msr[2] << 32)) + add_feature_msr(index, bit, name) + + # read the `feature_word_info` struct from qemu's cpu.c into a list of strings def read_cpu_c(path): pattern_comment = re.compile("/\\*.*?\\*/") @@ -450,6 +503,12 @@ def main(): nargs="?", type=os.path.realpath, ) + if pycpuinfo: + parser.add_argument( + "--libcpuinfo", + help="Use libcpuinfo as data source instead", + action="store_true", + ) parser.add_argument( "--output", "-o", @@ -459,14 +518,18 @@ def main(): ) args = parser.parse_args() - if not os.path.isdir(args.qemu): - parser.print_help() - exit("qemu source directory not found") + if pycpuinfo and args.libcpuinfo: + add_features_cpuinfo() + else: + if not os.path.isdir(args.qemu): + parser.print_help() + exit("qemu source directory not found") + + read_headers(args.qemu) + lines = read_cpu_c(args.qemu) + parse_feature_words(lines) + add_extra_features() - read_headers(args.qemu) - lines = read_cpu_c(args.qemu) - parse_feature_words(lines) - add_extra_features() write_output(args.output) print( -- 2.43.0