qemu/scripts/cpu-x86-uarch-abi.py
<<
>>
Prefs
   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 import qmp
  10import sys
  11
  12if len(sys.argv) != 1:
  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]
  69cmd = sys.argv[2]
  70shell = qmp.QEMUMonitorProtocol(sock)
  71shell.connect()
  72
  73models = shell.cmd("query-cpu-definitions")
  74
  75# These QMP props don't correspond to CPUID fatures
  76# so ignore them
  77skip = [
  78    "family",
  79    "min-level",
  80    "min-xlevel",
  81    "vendor",
  82    "model",
  83    "model-id",
  84    "stepping",
  85]
  86
  87names = []
  88
  89for model in models["return"]:
  90    if "alias-of" in model:
  91        continue
  92    names.append(model["name"])
  93
  94models = {}
  95
  96for name in sorted(names):
  97    cpu = shell.cmd("query-cpu-model-expansion",
  98                     { "type": "static",
  99                       "model": { "name": name }})
 100
 101    got = {}
 102    for (feature, present) in cpu["return"]["model"]["props"].items():
 103        if present and feature not in skip:
 104            got[feature] = True
 105
 106    if name in ["host", "max", "base"]:
 107        continue
 108
 109    models[name] = {
 110        # Dict of all present features in this CPU model
 111        "features": got,
 112
 113        # Whether each x86-64 ABI level is satisfied
 114        "levels": [False, False, False, False],
 115
 116        # Number of extra CPUID features compared to the x86-64 ABI level
 117        "distance":[-1, -1, -1, -1],
 118
 119        # CPUID features present in model, but not in ABI level
 120        "delta":[[], [], [], []],
 121
 122        # CPUID features in ABI level but not present in model
 123        "missing": [[], [], [], []],
 124    }
 125
 126
 127# Calculate whether the CPU models satisfy each ABI level
 128for name in models.keys():
 129    for level in range(len(levels)):
 130        got = set(models[name]["features"])
 131        want = set(levels[level])
 132        missing = want - got
 133        match = True
 134        if len(missing) > 0:
 135            match = False
 136        models[name]["levels"][level] = match
 137        models[name]["missing"][level] = missing
 138
 139# Cache list of CPU models satisfying each ABI level
 140abi_models = [
 141    [],
 142    [],
 143    [],
 144    [],
 145]
 146
 147for name in models.keys():
 148    for level in range(len(levels)):
 149        if models[name]["levels"][level]:
 150            abi_models[level].append(name)
 151
 152
 153for level in range(len(abi_models)):
 154    # Find the union of features in all CPU models satisfying this ABI
 155    allfeatures = {}
 156    for name in abi_models[level]:
 157        for feat in models[name]["features"]:
 158            allfeatures[feat] = True
 159
 160    # Find the intersection of features in all CPU models satisfying this ABI
 161    commonfeatures = []
 162    for feat in allfeatures:
 163        present = True
 164        for name in models.keys():
 165            if not models[name]["levels"][level]:
 166                continue
 167            if feat not in models[name]["features"]:
 168                present = False
 169        if present:
 170            commonfeatures.append(feat)
 171
 172    # Determine how many extra features are present compared to the lowest
 173    # common denominator
 174    for name in models.keys():
 175        if not models[name]["levels"][level]:
 176            continue
 177
 178        delta = set(models[name]["features"].keys()) - set(commonfeatures)
 179        models[name]["distance"][level] = len(delta)
 180        models[name]["delta"][level] = delta
 181
 182def print_uarch_abi_csv():
 183    print("# Automatically generated from '%s'" % __file__)
 184    print("Model,baseline,v2,v3,v4")
 185    for name in models.keys():
 186        print(name, end="")
 187        for level in range(len(levels)):
 188            if models[name]["levels"][level]:
 189                print(",✅", end="")
 190            else:
 191                print(",", end="")
 192        print()
 193
 194print_uarch_abi_csv()
 195