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

test-x86-cpuid-compat.c (14553B)


      1#include "qemu/osdep.h"
      2#include "qemu-common.h"
      3#include "qapi/qmp/qdict.h"
      4#include "qapi/qmp/qlist.h"
      5#include "qapi/qmp/qnum.h"
      6#include "qapi/qmp/qbool.h"
      7#include "libqtest-single.h"
      8
      9static char *get_cpu0_qom_path(void)
     10{
     11    QDict *resp;
     12    QList *ret;
     13    QDict *cpu0;
     14    char *path;
     15
     16    resp = qmp("{'execute': 'query-cpus-fast', 'arguments': {}}");
     17    g_assert(qdict_haskey(resp, "return"));
     18    ret = qdict_get_qlist(resp, "return");
     19
     20    cpu0 = qobject_to(QDict, qlist_peek(ret));
     21    path = g_strdup(qdict_get_str(cpu0, "qom-path"));
     22    qobject_unref(resp);
     23    return path;
     24}
     25
     26static QObject *qom_get(const char *path, const char *prop)
     27{
     28    QDict *resp = qmp("{ 'execute': 'qom-get',"
     29                      "  'arguments': { 'path': %s,"
     30                      "                 'property': %s } }",
     31                      path, prop);
     32    QObject *ret = qdict_get(resp, "return");
     33    qobject_ref(ret);
     34    qobject_unref(resp);
     35    return ret;
     36}
     37
     38static bool qom_get_bool(const char *path, const char *prop)
     39{
     40    QBool *value = qobject_to(QBool, qom_get(path, prop));
     41    bool b = qbool_get_bool(value);
     42
     43    qobject_unref(value);
     44    return b;
     45}
     46
     47typedef struct CpuidTestArgs {
     48    const char *cmdline;
     49    const char *property;
     50    int64_t expected_value;
     51} CpuidTestArgs;
     52
     53static void test_cpuid_prop(const void *data)
     54{
     55    const CpuidTestArgs *args = data;
     56    char *path;
     57    QNum *value;
     58    int64_t val;
     59
     60    qtest_start(args->cmdline);
     61    path = get_cpu0_qom_path();
     62    value = qobject_to(QNum, qom_get(path, args->property));
     63    g_assert(qnum_get_try_int(value, &val));
     64    g_assert_cmpint(val, ==, args->expected_value);
     65    qtest_end();
     66
     67    qobject_unref(value);
     68    g_free(path);
     69}
     70
     71static void add_cpuid_test(const char *name, const char *cmdline,
     72                           const char *property, int64_t expected_value)
     73{
     74    CpuidTestArgs *args = g_new0(CpuidTestArgs, 1);
     75    args->cmdline = cmdline;
     76    args->property = property;
     77    args->expected_value = expected_value;
     78    qtest_add_data_func(name, args, test_cpuid_prop);
     79}
     80
     81
     82/* Parameters to a add_feature_test() test case */
     83typedef struct FeatureTestArgs {
     84    /* cmdline to start QEMU */
     85    const char *cmdline;
     86    /*
     87     * cpuid-input-eax and cpuid-input-ecx values to look for,
     88     * in "feature-words" and "filtered-features" properties.
     89     */
     90    uint32_t in_eax, in_ecx;
     91    /* The register name to look for, in the X86CPUFeatureWordInfo array */
     92    const char *reg;
     93    /* The bit to check in X86CPUFeatureWordInfo.features */
     94    int bitnr;
     95    /* The expected value for the bit in (X86CPUFeatureWordInfo.features) */
     96    bool expected_value;
     97} FeatureTestArgs;
     98
     99/* Get the value for a feature word in a X86CPUFeatureWordInfo list */
    100static uint32_t get_feature_word(QList *features, uint32_t eax, uint32_t ecx,
    101                                 const char *reg)
    102{
    103    const QListEntry *e;
    104
    105    for (e = qlist_first(features); e; e = qlist_next(e)) {
    106        QDict *w = qobject_to(QDict, qlist_entry_obj(e));
    107        const char *rreg = qdict_get_str(w, "cpuid-register");
    108        uint32_t reax = qdict_get_int(w, "cpuid-input-eax");
    109        bool has_ecx = qdict_haskey(w, "cpuid-input-ecx");
    110        uint32_t recx = 0;
    111        int64_t val;
    112
    113        if (has_ecx) {
    114            recx = qdict_get_int(w, "cpuid-input-ecx");
    115        }
    116        if (eax == reax && (!has_ecx || ecx == recx) && !strcmp(rreg, reg)) {
    117            g_assert(qnum_get_try_int(qobject_to(QNum,
    118                                                 qdict_get(w, "features")),
    119                                      &val));
    120            return val;
    121        }
    122    }
    123    return 0;
    124}
    125
    126static void test_feature_flag(const void *data)
    127{
    128    const FeatureTestArgs *args = data;
    129    char *path;
    130    QList *present, *filtered;
    131    uint32_t value;
    132
    133    qtest_start(args->cmdline);
    134    path = get_cpu0_qom_path();
    135    present = qobject_to(QList, qom_get(path, "feature-words"));
    136    filtered = qobject_to(QList, qom_get(path, "filtered-features"));
    137    value = get_feature_word(present, args->in_eax, args->in_ecx, args->reg);
    138    value |= get_feature_word(filtered, args->in_eax, args->in_ecx, args->reg);
    139    qtest_end();
    140
    141    g_assert(!!(value & (1U << args->bitnr)) == args->expected_value);
    142
    143    qobject_unref(present);
    144    qobject_unref(filtered);
    145    g_free(path);
    146}
    147
    148/*
    149 * Add test case to ensure that a given feature flag is set in
    150 * either "feature-words" or "filtered-features", when running QEMU
    151 * using cmdline
    152 */
    153static FeatureTestArgs *add_feature_test(const char *name, const char *cmdline,
    154                                         uint32_t eax, uint32_t ecx,
    155                                         const char *reg, int bitnr,
    156                                         bool expected_value)
    157{
    158    FeatureTestArgs *args = g_new0(FeatureTestArgs, 1);
    159    args->cmdline = cmdline;
    160    args->in_eax = eax;
    161    args->in_ecx = ecx;
    162    args->reg = reg;
    163    args->bitnr = bitnr;
    164    args->expected_value = expected_value;
    165    qtest_add_data_func(name, args, test_feature_flag);
    166    return args;
    167}
    168
    169static void test_plus_minus_subprocess(void)
    170{
    171    char *path;
    172
    173    /* Rules:
    174     * 1)"-foo" overrides "+foo"
    175     * 2) "[+-]foo" overrides "foo=..."
    176     * 3) Old feature names with underscores (e.g. "sse4_2")
    177     *    should keep working
    178     *
    179     * Note: rules 1 and 2 are planned to be removed soon, and
    180     * should generate a warning.
    181     */
    182    qtest_start("-cpu pentium,-fpu,+fpu,-mce,mce=on,+cx8,cx8=off,+sse4_1,sse4_2=on");
    183    path = get_cpu0_qom_path();
    184
    185    g_assert_false(qom_get_bool(path, "fpu"));
    186    g_assert_false(qom_get_bool(path, "mce"));
    187    g_assert_true(qom_get_bool(path, "cx8"));
    188
    189    /* Test both the original and the alias feature names: */
    190    g_assert_true(qom_get_bool(path, "sse4-1"));
    191    g_assert_true(qom_get_bool(path, "sse4.1"));
    192
    193    g_assert_true(qom_get_bool(path, "sse4-2"));
    194    g_assert_true(qom_get_bool(path, "sse4.2"));
    195
    196    qtest_end();
    197    g_free(path);
    198}
    199
    200static void test_plus_minus(void)
    201{
    202    g_test_trap_subprocess("/x86/cpuid/parsing-plus-minus/subprocess", 0, 0);
    203    g_test_trap_assert_passed();
    204    g_test_trap_assert_stderr("*Ambiguous CPU model string. "
    205                              "Don't mix both \"-mce\" and \"mce=on\"*");
    206    g_test_trap_assert_stderr("*Ambiguous CPU model string. "
    207                              "Don't mix both \"+cx8\" and \"cx8=off\"*");
    208    g_test_trap_assert_stdout("");
    209}
    210
    211int main(int argc, char **argv)
    212{
    213    g_test_init(&argc, &argv, NULL);
    214
    215    g_test_add_func("/x86/cpuid/parsing-plus-minus/subprocess",
    216                    test_plus_minus_subprocess);
    217    g_test_add_func("/x86/cpuid/parsing-plus-minus", test_plus_minus);
    218
    219    /* Original level values for CPU models: */
    220    add_cpuid_test("x86/cpuid/phenom/level",
    221                   "-cpu phenom", "level", 5);
    222    add_cpuid_test("x86/cpuid/Conroe/level",
    223                   "-cpu Conroe", "level", 10);
    224    add_cpuid_test("x86/cpuid/SandyBridge/level",
    225                   "-cpu SandyBridge", "level", 0xd);
    226    add_cpuid_test("x86/cpuid/486/xlevel",
    227                   "-cpu 486", "xlevel", 0);
    228    add_cpuid_test("x86/cpuid/core2duo/xlevel",
    229                   "-cpu core2duo", "xlevel", 0x80000008);
    230    add_cpuid_test("x86/cpuid/phenom/xlevel",
    231                   "-cpu phenom", "xlevel", 0x8000001A);
    232    add_cpuid_test("x86/cpuid/athlon/xlevel",
    233                   "-cpu athlon", "xlevel", 0x80000008);
    234
    235    /* If level is not large enough, it should increase automatically: */
    236    /* CPUID[6].EAX: */
    237    add_cpuid_test("x86/cpuid/auto-level/phenom/arat",
    238                   "-cpu 486,arat=on", "level", 6);
    239    /* CPUID[EAX=7,ECX=0].EBX: */
    240    add_cpuid_test("x86/cpuid/auto-level/phenom/fsgsbase",
    241                   "-cpu phenom,fsgsbase=on", "level", 7);
    242    /* CPUID[EAX=7,ECX=0].ECX: */
    243    add_cpuid_test("x86/cpuid/auto-level/phenom/avx512vbmi",
    244                   "-cpu phenom,avx512vbmi=on", "level", 7);
    245    /* CPUID[EAX=0xd,ECX=1].EAX: */
    246    add_cpuid_test("x86/cpuid/auto-level/phenom/xsaveopt",
    247                   "-cpu phenom,xsaveopt=on", "level", 0xd);
    248    /* CPUID[8000_0001].EDX: */
    249    add_cpuid_test("x86/cpuid/auto-xlevel/486/3dnow",
    250                   "-cpu 486,3dnow=on", "xlevel", 0x80000001);
    251    /* CPUID[8000_0001].ECX: */
    252    add_cpuid_test("x86/cpuid/auto-xlevel/486/sse4a",
    253                   "-cpu 486,sse4a=on", "xlevel", 0x80000001);
    254    /* CPUID[8000_0007].EDX: */
    255    add_cpuid_test("x86/cpuid/auto-xlevel/486/invtsc",
    256                   "-cpu 486,invtsc=on", "xlevel", 0x80000007);
    257    /* CPUID[8000_000A].EDX: */
    258    add_cpuid_test("x86/cpuid/auto-xlevel/486/npt",
    259                   "-cpu 486,svm=on,npt=on", "xlevel", 0x8000000A);
    260    /* CPUID[C000_0001].EDX: */
    261    add_cpuid_test("x86/cpuid/auto-xlevel2/phenom/xstore",
    262                   "-cpu phenom,xstore=on", "xlevel2", 0xC0000001);
    263    /* SVM needs CPUID[0x8000000A] */
    264    add_cpuid_test("x86/cpuid/auto-xlevel/athlon/svm",
    265                   "-cpu athlon,svm=on", "xlevel", 0x8000000A);
    266
    267
    268    /* If level is already large enough, it shouldn't change: */
    269    add_cpuid_test("x86/cpuid/auto-level/SandyBridge/multiple",
    270                   "-cpu SandyBridge,arat=on,fsgsbase=on,avx512vbmi=on",
    271                   "level", 0xd);
    272    /* If level is explicitly set, it shouldn't change: */
    273    add_cpuid_test("x86/cpuid/auto-level/486/fixed/0xF",
    274                   "-cpu 486,level=0xF,arat=on,fsgsbase=on,avx512vbmi=on,xsaveopt=on",
    275                   "level", 0xF);
    276    add_cpuid_test("x86/cpuid/auto-level/486/fixed/2",
    277                   "-cpu 486,level=2,arat=on,fsgsbase=on,avx512vbmi=on,xsaveopt=on",
    278                   "level", 2);
    279    add_cpuid_test("x86/cpuid/auto-level/486/fixed/0",
    280                   "-cpu 486,level=0,arat=on,fsgsbase=on,avx512vbmi=on,xsaveopt=on",
    281                   "level", 0);
    282
    283    /* if xlevel is already large enough, it shouldn't change: */
    284    add_cpuid_test("x86/cpuid/auto-xlevel/phenom/3dnow",
    285                   "-cpu phenom,3dnow=on,sse4a=on,invtsc=on,npt=on,svm=on",
    286                   "xlevel", 0x8000001A);
    287    /* If xlevel is explicitly set, it shouldn't change: */
    288    add_cpuid_test("x86/cpuid/auto-xlevel/486/fixed/80000002",
    289                   "-cpu 486,xlevel=0x80000002,3dnow=on,sse4a=on,invtsc=on,npt=on,svm=on",
    290                   "xlevel", 0x80000002);
    291    add_cpuid_test("x86/cpuid/auto-xlevel/486/fixed/8000001A",
    292                   "-cpu 486,xlevel=0x8000001A,3dnow=on,sse4a=on,invtsc=on,npt=on,svm=on",
    293                   "xlevel", 0x8000001A);
    294    add_cpuid_test("x86/cpuid/auto-xlevel/phenom/fixed/0",
    295                   "-cpu 486,xlevel=0,3dnow=on,sse4a=on,invtsc=on,npt=on,svm=on",
    296                   "xlevel", 0);
    297
    298    /* if xlevel2 is already large enough, it shouldn't change: */
    299    add_cpuid_test("x86/cpuid/auto-xlevel2/486/fixed",
    300                   "-cpu 486,xlevel2=0xC0000002,xstore=on",
    301                   "xlevel2", 0xC0000002);
    302
    303    /* Check compatibility of old machine-types that didn't
    304     * auto-increase level/xlevel/xlevel2: */
    305
    306    add_cpuid_test("x86/cpuid/auto-level/pc-2.7",
    307                   "-machine pc-i440fx-2.7 -cpu 486,arat=on,avx512vbmi=on,xsaveopt=on",
    308                   "level", 1);
    309    add_cpuid_test("x86/cpuid/auto-xlevel/pc-2.7",
    310                   "-machine pc-i440fx-2.7 -cpu 486,3dnow=on,sse4a=on,invtsc=on,npt=on,svm=on",
    311                   "xlevel", 0);
    312    add_cpuid_test("x86/cpuid/auto-xlevel2/pc-2.7",
    313                   "-machine pc-i440fx-2.7 -cpu 486,xstore=on",
    314                   "xlevel2", 0);
    315    /*
    316     * QEMU 1.4.0 had auto-level enabled for CPUID[7], already,
    317     * and the compat code that sets default level shouldn't
    318     * disable the auto-level=7 code:
    319     */
    320    add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-1.4/off",
    321                   "-machine pc-i440fx-1.4 -cpu Nehalem",
    322                   "level", 2);
    323    add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-1.5/on",
    324                   "-machine pc-i440fx-1.4 -cpu Nehalem,smap=on",
    325                   "level", 7);
    326    add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-2.3/off",
    327                   "-machine pc-i440fx-2.3 -cpu Penryn",
    328                   "level", 4);
    329    add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-2.3/on",
    330                   "-machine pc-i440fx-2.3 -cpu Penryn,erms=on",
    331                   "level", 7);
    332    add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-2.9/off",
    333                   "-machine pc-i440fx-2.9 -cpu Conroe",
    334                   "level", 10);
    335    add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-2.9/on",
    336                   "-machine pc-i440fx-2.9 -cpu Conroe,erms=on",
    337                   "level", 10);
    338
    339    /*
    340     * xlevel doesn't have any feature that triggers auto-level
    341     * code on old machine-types.  Just check that the compat code
    342     * is working correctly:
    343     */
    344    add_cpuid_test("x86/cpuid/xlevel-compat/pc-i440fx-2.3",
    345                   "-machine pc-i440fx-2.3 -cpu SandyBridge",
    346                   "xlevel", 0x8000000a);
    347    add_cpuid_test("x86/cpuid/xlevel-compat/pc-i440fx-2.4/npt-off",
    348                   "-machine pc-i440fx-2.4 -cpu SandyBridge,",
    349                   "xlevel", 0x80000008);
    350    add_cpuid_test("x86/cpuid/xlevel-compat/pc-i440fx-2.4/npt-on",
    351                   "-machine pc-i440fx-2.4 -cpu SandyBridge,svm=on,npt=on",
    352                   "xlevel", 0x80000008);
    353
    354    /* Test feature parsing */
    355    add_feature_test("x86/cpuid/features/plus",
    356                     "-cpu 486,+arat",
    357                     6, 0, "EAX", 2, true);
    358    add_feature_test("x86/cpuid/features/minus",
    359                     "-cpu pentium,-mmx",
    360                     1, 0, "EDX", 23, false);
    361    add_feature_test("x86/cpuid/features/on",
    362                     "-cpu 486,arat=on",
    363                     6, 0, "EAX", 2, true);
    364    add_feature_test("x86/cpuid/features/off",
    365                     "-cpu pentium,mmx=off",
    366                     1, 0, "EDX", 23, false);
    367    add_feature_test("x86/cpuid/features/max-plus-invtsc",
    368                     "-cpu max,+invtsc",
    369                     0x80000007, 0, "EDX", 8, true);
    370    add_feature_test("x86/cpuid/features/max-invtsc-on",
    371                     "-cpu max,invtsc=on",
    372                     0x80000007, 0, "EDX", 8, true);
    373    add_feature_test("x86/cpuid/features/max-minus-mmx",
    374                     "-cpu max,-mmx",
    375                     1, 0, "EDX", 23, false);
    376    add_feature_test("x86/cpuid/features/max-invtsc-on,mmx=off",
    377                     "-cpu max,mmx=off",
    378                     1, 0, "EDX", 23, false);
    379
    380    return g_test_run();
    381}