xref: /openbmc/qemu/scripts/cpu-x86-uarch-abi.py (revision 6c3a9247)
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.aqmp.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["return"]:
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["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
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