From 4e2f5f3a9db06e87a73eb60a7cc9754fc13596ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 7 Jun 2021 14:58:42 +0100 Subject: [PATCH] scripts: helper to generate x86_64 CPU ABI compat info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This script is what is used to generate the docs data table in: docs/system/cpu-models-x86-abi.csv It can be useful to run if adding new CPU models / versions and the csv needs updating. Signed-off-by: Daniel P. Berrangé Acked-by: Eduardo Habkost Message-Id: <20210607135843.196595-4-berrange@redhat.com> Signed-off-by: Eduardo Habkost --- scripts/cpu-x86-uarch-abi.py | 194 +++++++++++++++++++++++++++++++++++ 1 file changed, 194 insertions(+) create mode 100644 scripts/cpu-x86-uarch-abi.py diff --git a/scripts/cpu-x86-uarch-abi.py b/scripts/cpu-x86-uarch-abi.py new file mode 100644 index 0000000000..08acc52a81 --- /dev/null +++ b/scripts/cpu-x86-uarch-abi.py @@ -0,0 +1,194 @@ +#!/usr/bin/python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# A script to generate a CSV file showing the x86_64 ABI +# compatibility levels for each CPU model. +# + +from qemu import qmp +import sys + +if len(sys.argv) != 1: + print("syntax: %s QMP-SOCK\n\n" % __file__ + + "Where QMP-SOCK points to a QEMU process such as\n\n" + + " # qemu-system-x86_64 -qmp unix:/tmp/qmp,server,nowait " + + "-display none -accel kvm", file=sys.stderr) + sys.exit(1) + +# Mandatory CPUID features for each microarch ABI level +levels = [ + [ # x86-64 baseline + "cmov", + "cx8", + "fpu", + "fxsr", + "mmx", + "syscall", + "sse", + "sse2", + ], + [ # x86-64-v2 + "cx16", + "lahf-lm", + "popcnt", + "pni", + "sse4.1", + "sse4.2", + "ssse3", + ], + [ # x86-64-v3 + "avx", + "avx2", + "bmi1", + "bmi2", + "f16c", + "fma", + "abm", + "movbe", + ], + [ # x86-64-v4 + "avx512f", + "avx512bw", + "avx512cd", + "avx512dq", + "avx512vl", + ], +] + +# Assumes externally launched process such as +# +# qemu-system-x86_64 -qmp unix:/tmp/qmp,server,nowait -display none -accel kvm +# +# Note different results will be obtained with TCG, as +# TCG masks out certain features otherwise present in +# the CPU model definitions, as does KVM. + + +sock = sys.argv[1] +cmd = sys.argv[2] +shell = qmp.QEMUMonitorProtocol(sock) +shell.connect() + +models = shell.cmd("query-cpu-definitions") + +# These QMP props don't correspond to CPUID fatures +# so ignore them +skip = [ + "family", + "min-level", + "min-xlevel", + "vendor", + "model", + "model-id", + "stepping", +] + +names = [] + +for model in models["return"]: + if "alias-of" in model: + continue + names.append(model["name"]) + +models = {} + +for name in sorted(names): + cpu = shell.cmd("query-cpu-model-expansion", + { "type": "static", + "model": { "name": name }}) + + got = {} + for (feature, present) in cpu["return"]["model"]["props"].items(): + if present and feature not in skip: + got[feature] = True + + if name in ["host", "max", "base"]: + continue + + models[name] = { + # Dict of all present features in this CPU model + "features": got, + + # Whether each x86-64 ABI level is satisfied + "levels": [False, False, False, False], + + # Number of extra CPUID features compared to the x86-64 ABI level + "distance":[-1, -1, -1, -1], + + # CPUID features present in model, but not in ABI level + "delta":[[], [], [], []], + + # CPUID features in ABI level but not present in model + "missing": [[], [], [], []], + } + + +# Calculate whether the CPU models satisfy each ABI level +for name in models.keys(): + for level in range(len(levels)): + got = set(models[name]["features"]) + want = set(levels[level]) + missing = want - got + match = True + if len(missing) > 0: + match = False + models[name]["levels"][level] = match + models[name]["missing"][level] = missing + +# Cache list of CPU models satisfying each ABI level +abi_models = [ + [], + [], + [], + [], +] + +for name in models.keys(): + for level in range(len(levels)): + if models[name]["levels"][level]: + abi_models[level].append(name) + + +for level in range(len(abi_models)): + # Find the union of features in all CPU models satisfying this ABI + allfeatures = {} + for name in abi_models[level]: + for feat in models[name]["features"]: + allfeatures[feat] = True + + # Find the intersection of features in all CPU models satisfying this ABI + commonfeatures = [] + for feat in allfeatures: + present = True + for name in models.keys(): + if not models[name]["levels"][level]: + continue + if feat not in models[name]["features"]: + present = False + if present: + commonfeatures.append(feat) + + # Determine how many extra features are present compared to the lowest + # common denominator + for name in models.keys(): + if not models[name]["levels"][level]: + continue + + delta = set(models[name]["features"].keys()) - set(commonfeatures) + models[name]["distance"][level] = len(delta) + models[name]["delta"][level] = delta + +def print_uarch_abi_csv(): + print("# Automatically generated from '%s'" % __file__) + print("Model,baseline,v2,v3,v4") + for name in models.keys(): + print(name, end="") + for level in range(len(levels)): + if models[name]["levels"][level]: + print(",✅", end="") + else: + print(",", end="") + print() + +print_uarch_abi_csv()