Daniel P. Berrangé | 4e2f5f3 | 2021-06-07 14:58:42 +0100 | [diff] [blame] | 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 | |
John Snow | 37094b6 | 2022-03-30 13:28:10 -0400 | [diff] [blame] | 9 | from qemu.qmp.legacy import QEMUMonitorProtocol |
Daniel P. Berrangé | 4e2f5f3 | 2021-06-07 14:58:42 +0100 | [diff] [blame] | 10 | import sys |
| 11 | |
John Snow | 9922125 | 2022-01-10 18:28:57 -0500 | [diff] [blame] | 12 | if len(sys.argv) != 2: |
Daniel P. Berrangé | 4e2f5f3 | 2021-06-07 14:58:42 +0100 | [diff] [blame] | 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 |
| 20 | levels = [ |
| 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 | |
| 68 | sock = sys.argv[1] |
John Snow | 0665410 | 2022-01-10 18:28:58 -0500 | [diff] [blame] | 69 | shell = QEMUMonitorProtocol(sock) |
Daniel P. Berrangé | 4e2f5f3 | 2021-06-07 14:58:42 +0100 | [diff] [blame] | 70 | shell.connect() |
| 71 | |
Vladimir Sementsov-Ogievskiy | 684750a | 2023-10-06 18:41:15 +0300 | [diff] [blame] | 72 | models = shell.cmd("query-cpu-definitions") |
Daniel P. Berrangé | 4e2f5f3 | 2021-06-07 14:58:42 +0100 | [diff] [blame] | 73 | |
| 74 | # These QMP props don't correspond to CPUID fatures |
| 75 | # so ignore them |
| 76 | skip = [ |
| 77 | "family", |
| 78 | "min-level", |
| 79 | "min-xlevel", |
| 80 | "vendor", |
| 81 | "model", |
| 82 | "model-id", |
| 83 | "stepping", |
| 84 | ] |
| 85 | |
| 86 | names = [] |
| 87 | |
Vladimir Sementsov-Ogievskiy | 7f521b0 | 2023-10-06 18:41:13 +0300 | [diff] [blame] | 88 | for model in models: |
Daniel P. Berrangé | 4e2f5f3 | 2021-06-07 14:58:42 +0100 | [diff] [blame] | 89 | if "alias-of" in model: |
| 90 | continue |
| 91 | names.append(model["name"]) |
| 92 | |
| 93 | models = {} |
| 94 | |
| 95 | for name in sorted(names): |
Vladimir Sementsov-Ogievskiy | 684750a | 2023-10-06 18:41:15 +0300 | [diff] [blame] | 96 | cpu = shell.cmd("query-cpu-model-expansion", |
Zhao Liu | 3e7ebf5 | 2023-10-18 18:00:11 +0800 | [diff] [blame] | 97 | type="static", |
| 98 | model={ "name": name }) |
Daniel P. Berrangé | 4e2f5f3 | 2021-06-07 14:58:42 +0100 | [diff] [blame] | 99 | |
| 100 | got = {} |
Vladimir Sementsov-Ogievskiy | 7f521b0 | 2023-10-06 18:41:13 +0300 | [diff] [blame] | 101 | for (feature, present) in cpu["model"]["props"].items(): |
Daniel P. Berrangé | 4e2f5f3 | 2021-06-07 14:58:42 +0100 | [diff] [blame] | 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 |
| 127 | for 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 |
| 139 | abi_models = [ |
| 140 | [], |
| 141 | [], |
| 142 | [], |
| 143 | [], |
| 144 | ] |
| 145 | |
| 146 | for 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 | |
| 152 | for 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 | |
| 181 | def print_uarch_abi_csv(): |
Daniel P. Berrangé | 4e2f5f3 | 2021-06-07 14:58:42 +0100 | [diff] [blame] | 182 | print("Model,baseline,v2,v3,v4") |
| 183 | for name in models.keys(): |
| 184 | print(name, end="") |
| 185 | for level in range(len(levels)): |
| 186 | if models[name]["levels"][level]: |
| 187 | print(",✅", end="") |
| 188 | else: |
| 189 | print(",", end="") |
| 190 | print() |
| 191 | |
| 192 | print_uarch_abi_csv() |