cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

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


      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()