]>
Commit | Line | Data |
---|---|---|
4e2f5f3a DB |
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 | ||
37094b6d | 9 | from qemu.qmp.legacy import QEMUMonitorProtocol |
4e2f5f3a DB |
10 | import sys |
11 | ||
99221256 | 12 | if len(sys.argv) != 2: |
4e2f5f3a DB |
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] | |
0665410d | 69 | shell = QEMUMonitorProtocol(sock) |
4e2f5f3a DB |
70 | shell.connect() |
71 | ||
72 | models = shell.cmd("query-cpu-definitions") | |
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 | ||
88 | for model in models["return"]: | |
89 | if "alias-of" in model: | |
90 | continue | |
91 | names.append(model["name"]) | |
92 | ||
93 | models = {} | |
94 | ||
95 | for 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["return"]["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 | |
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(): | |
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 | ||
193 | print_uarch_abi_csv() |