1#!/usr/bin/python3 2# 3# SPDX-License-Identifier: GPL-2.0-or-later 4# 5# A script to generate a CSV file showing the x86_64 ABI 6# compatibility levels for each CPU model. 7# 8 9from qemu.qmp.legacy import QEMUMonitorProtocol 10import sys 11 12if len(sys.argv) != 2: 13 print("syntax: %s QMP-SOCK\n\n" % __file__ + 14 "Where QMP-SOCK points to a QEMU process such as\n\n" + 15 " # qemu-system-x86_64 -qmp unix:/tmp/qmp,server,nowait " + 16 "-display none -accel kvm", file=sys.stderr) 17 sys.exit(1) 18 19# Mandatory CPUID features for each microarch ABI level 20levels = [ 21 [ # x86-64 baseline 22 "cmov", 23 "cx8", 24 "fpu", 25 "fxsr", 26 "mmx", 27 "syscall", 28 "sse", 29 "sse2", 30 ], 31 [ # x86-64-v2 32 "cx16", 33 "lahf-lm", 34 "popcnt", 35 "pni", 36 "sse4.1", 37 "sse4.2", 38 "ssse3", 39 ], 40 [ # x86-64-v3 41 "avx", 42 "avx2", 43 "bmi1", 44 "bmi2", 45 "f16c", 46 "fma", 47 "abm", 48 "movbe", 49 ], 50 [ # x86-64-v4 51 "avx512f", 52 "avx512bw", 53 "avx512cd", 54 "avx512dq", 55 "avx512vl", 56 ], 57] 58 59# Assumes externally launched process such as 60# 61# qemu-system-x86_64 -qmp unix:/tmp/qmp,server,nowait -display none -accel kvm 62# 63# Note different results will be obtained with TCG, as 64# TCG masks out certain features otherwise present in 65# the CPU model definitions, as does KVM. 66 67 68sock = sys.argv[1] 69shell = QEMUMonitorProtocol(sock) 70shell.connect() 71 72models = shell.cmd("query-cpu-definitions") 73 74# These QMP props don't correspond to CPUID fatures 75# so ignore them 76skip = [ 77 "family", 78 "min-level", 79 "min-xlevel", 80 "vendor", 81 "model", 82 "model-id", 83 "stepping", 84] 85 86names = [] 87 88for model in models: 89 if "alias-of" in model: 90 continue 91 names.append(model["name"]) 92 93models = {} 94 95for name in sorted(names): 96 cpu = shell.cmd("query-cpu-model-expansion", 97 { "type": "static", 98 "model": { "name": name }}) 99 100 got = {} 101 for (feature, present) in cpu["model"]["props"].items(): 102 if present and feature not in skip: 103 got[feature] = True 104 105 if name in ["host", "max", "base"]: 106 continue 107 108 models[name] = { 109 # Dict of all present features in this CPU model 110 "features": got, 111 112 # Whether each x86-64 ABI level is satisfied 113 "levels": [False, False, False, False], 114 115 # Number of extra CPUID features compared to the x86-64 ABI level 116 "distance":[-1, -1, -1, -1], 117 118 # CPUID features present in model, but not in ABI level 119 "delta":[[], [], [], []], 120 121 # CPUID features in ABI level but not present in model 122 "missing": [[], [], [], []], 123 } 124 125 126# Calculate whether the CPU models satisfy each ABI level 127for name in models.keys(): 128 for level in range(len(levels)): 129 got = set(models[name]["features"]) 130 want = set(levels[level]) 131 missing = want - got 132 match = True 133 if len(missing) > 0: 134 match = False 135 models[name]["levels"][level] = match 136 models[name]["missing"][level] = missing 137 138# Cache list of CPU models satisfying each ABI level 139abi_models = [ 140 [], 141 [], 142 [], 143 [], 144] 145 146for name in models.keys(): 147 for level in range(len(levels)): 148 if models[name]["levels"][level]: 149 abi_models[level].append(name) 150 151 152for level in range(len(abi_models)): 153 # Find the union of features in all CPU models satisfying this ABI 154 allfeatures = {} 155 for name in abi_models[level]: 156 for feat in models[name]["features"]: 157 allfeatures[feat] = True 158 159 # Find the intersection of features in all CPU models satisfying this ABI 160 commonfeatures = [] 161 for feat in allfeatures: 162 present = True 163 for name in models.keys(): 164 if not models[name]["levels"][level]: 165 continue 166 if feat not in models[name]["features"]: 167 present = False 168 if present: 169 commonfeatures.append(feat) 170 171 # Determine how many extra features are present compared to the lowest 172 # common denominator 173 for name in models.keys(): 174 if not models[name]["levels"][level]: 175 continue 176 177 delta = set(models[name]["features"].keys()) - set(commonfeatures) 178 models[name]["distance"][level] = len(delta) 179 models[name]["delta"][level] = delta 180 181def print_uarch_abi_csv(): 182 print("# Automatically generated from '%s'" % __file__) 183 print("Model,baseline,v2,v3,v4") 184 for name in models.keys(): 185 print(name, end="") 186 for level in range(len(levels)): 187 if models[name]["levels"][level]: 188 print(",✅", end="") 189 else: 190 print(",", end="") 191 print() 192 193print_uarch_abi_csv() 194