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