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é <berrange@redhat.com> Acked-by: Eduardo Habkost <ehabkost@redhat.com> Message-Id: <20210607135843.196595-4-berrange@redhat.com> Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
		
			
				
	
	
		
			195 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			195 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
#!/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()
 |