 f424bc3312
			
		
	
	
		f424bc3312
		
	
	
	
	
		
			
			The RST doc include can't be made to skip the comment indicating the CPU CSV file is auto-generated when importing it. This comment line was previously manually removed from the generated output that was committed. Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
		
			
				
	
	
		
			193 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			193 lines
		
	
	
		
			4.7 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.qmp.legacy import QEMUMonitorProtocol
 | |
| import sys
 | |
| 
 | |
| if len(sys.argv) != 2:
 | |
|     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]
 | |
| shell = 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:
 | |
|     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["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("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()
 |