qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

cpu-x86-uarch-abi.py (4880B)


      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 
      9 from qemu.qmp.legacy import QEMUMonitorProtocol
     10 import sys
     11 
     12 if 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
     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]
     69 shell = QEMUMonitorProtocol(sock)
     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()