qemu/target/s390x/cpu_models_sysemu.c
<<
>>
Prefs
   1/*
   2 * CPU models for s390x - System Emulation-only
   3 *
   4 * Copyright 2016 IBM Corp.
   5 *
   6 * Author(s): David Hildenbrand <dahi@linux.vnet.ibm.com>
   7 *
   8 * This work is licensed under the terms of the GNU GPL, version 2 or (at
   9 * your option) any later version. See the COPYING file in the top-level
  10 * directory.
  11 */
  12
  13#include "qemu/osdep.h"
  14#include "cpu.h"
  15#include "s390x-internal.h"
  16#include "kvm/kvm_s390x.h"
  17#include "sysemu/kvm.h"
  18#include "sysemu/tcg.h"
  19#include "qapi/error.h"
  20#include "qapi/visitor.h"
  21#include "qapi/qmp/qerror.h"
  22#include "qapi/qobject-input-visitor.h"
  23#include "qapi/qmp/qdict.h"
  24#include "qapi/qapi-commands-machine-target.h"
  25
  26static void list_add_feat(const char *name, void *opaque);
  27
  28static void check_unavailable_features(const S390CPUModel *max_model,
  29                                       const S390CPUModel *model,
  30                                       strList **unavailable)
  31{
  32    S390FeatBitmap missing;
  33
  34    /* check general model compatibility */
  35    if (max_model->def->gen < model->def->gen ||
  36        (max_model->def->gen == model->def->gen &&
  37         max_model->def->ec_ga < model->def->ec_ga)) {
  38        list_add_feat("type", unavailable);
  39    }
  40
  41    /* detect missing features if any to properly report them */
  42    bitmap_andnot(missing, model->features, max_model->features,
  43                  S390_FEAT_MAX);
  44    if (!bitmap_empty(missing, S390_FEAT_MAX)) {
  45        s390_feat_bitmap_to_ascii(missing, unavailable, list_add_feat);
  46    }
  47}
  48
  49struct CpuDefinitionInfoListData {
  50    CpuDefinitionInfoList *list;
  51    S390CPUModel *model;
  52};
  53
  54static void create_cpu_model_list(ObjectClass *klass, void *opaque)
  55{
  56    struct CpuDefinitionInfoListData *cpu_list_data = opaque;
  57    CpuDefinitionInfoList **cpu_list = &cpu_list_data->list;
  58    CpuDefinitionInfo *info;
  59    char *name = g_strdup(object_class_get_name(klass));
  60    S390CPUClass *scc = S390_CPU_CLASS(klass);
  61
  62    /* strip off the -s390x-cpu */
  63    g_strrstr(name, "-" TYPE_S390_CPU)[0] = 0;
  64    info = g_new0(CpuDefinitionInfo, 1);
  65    info->name = name;
  66    info->has_migration_safe = true;
  67    info->migration_safe = scc->is_migration_safe;
  68    info->q_static = scc->is_static;
  69    info->q_typename = g_strdup(object_class_get_name(klass));
  70    /* check for unavailable features */
  71    if (cpu_list_data->model) {
  72        Object *obj;
  73        S390CPU *sc;
  74        obj = object_new_with_class(klass);
  75        sc = S390_CPU(obj);
  76        if (sc->model) {
  77            info->has_unavailable_features = true;
  78            check_unavailable_features(cpu_list_data->model, sc->model,
  79                                       &info->unavailable_features);
  80        }
  81        object_unref(obj);
  82    }
  83
  84    QAPI_LIST_PREPEND(*cpu_list, info);
  85}
  86
  87CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp)
  88{
  89    struct CpuDefinitionInfoListData list_data = {
  90        .list = NULL,
  91    };
  92
  93    list_data.model = get_max_cpu_model(NULL);
  94
  95    object_class_foreach(create_cpu_model_list, TYPE_S390_CPU, false,
  96                         &list_data);
  97
  98    return list_data.list;
  99}
 100
 101static void cpu_model_from_info(S390CPUModel *model, const CpuModelInfo *info,
 102                                Error **errp)
 103{
 104    Error *err = NULL;
 105    const QDict *qdict = NULL;
 106    const QDictEntry *e;
 107    Visitor *visitor;
 108    ObjectClass *oc;
 109    S390CPU *cpu;
 110    Object *obj;
 111
 112    if (info->props) {
 113        qdict = qobject_to(QDict, info->props);
 114        if (!qdict) {
 115            error_setg(errp, QERR_INVALID_PARAMETER_TYPE, "props", "dict");
 116            return;
 117        }
 118    }
 119
 120    oc = cpu_class_by_name(TYPE_S390_CPU, info->name);
 121    if (!oc) {
 122        error_setg(errp, "The CPU definition \'%s\' is unknown.", info->name);
 123        return;
 124    }
 125    if (S390_CPU_CLASS(oc)->kvm_required && !kvm_enabled()) {
 126        error_setg(errp, "The CPU definition '%s' requires KVM", info->name);
 127        return;
 128    }
 129    obj = object_new_with_class(oc);
 130    cpu = S390_CPU(obj);
 131
 132    if (!cpu->model) {
 133        error_setg(errp, "Details about the host CPU model are not available, "
 134                         "it cannot be used.");
 135        object_unref(obj);
 136        return;
 137    }
 138
 139    if (qdict) {
 140        visitor = qobject_input_visitor_new(info->props);
 141        if (!visit_start_struct(visitor, NULL, NULL, 0, errp)) {
 142            visit_free(visitor);
 143            object_unref(obj);
 144            return;
 145        }
 146        for (e = qdict_first(qdict); e; e = qdict_next(qdict, e)) {
 147            if (!object_property_set(obj, e->key, visitor, &err)) {
 148                break;
 149            }
 150        }
 151        if (!err) {
 152            visit_check_struct(visitor, &err);
 153        }
 154        visit_end_struct(visitor, NULL);
 155        visit_free(visitor);
 156        if (err) {
 157            error_propagate(errp, err);
 158            object_unref(obj);
 159            return;
 160        }
 161    }
 162
 163    /* copy the model and throw the cpu away */
 164    memcpy(model, cpu->model, sizeof(*model));
 165    object_unref(obj);
 166}
 167
 168static void qdict_add_disabled_feat(const char *name, void *opaque)
 169{
 170    qdict_put_bool(opaque, name, false);
 171}
 172
 173static void qdict_add_enabled_feat(const char *name, void *opaque)
 174{
 175    qdict_put_bool(opaque, name, true);
 176}
 177
 178/* convert S390CPUDef into a static CpuModelInfo */
 179static void cpu_info_from_model(CpuModelInfo *info, const S390CPUModel *model,
 180                                bool delta_changes)
 181{
 182    QDict *qdict = qdict_new();
 183    S390FeatBitmap bitmap;
 184
 185    /* always fallback to the static base model */
 186    info->name = g_strdup_printf("%s-base", model->def->name);
 187
 188    if (delta_changes) {
 189        /* features deleted from the base feature set */
 190        bitmap_andnot(bitmap, model->def->base_feat, model->features,
 191                      S390_FEAT_MAX);
 192        if (!bitmap_empty(bitmap, S390_FEAT_MAX)) {
 193            s390_feat_bitmap_to_ascii(bitmap, qdict, qdict_add_disabled_feat);
 194        }
 195
 196        /* features added to the base feature set */
 197        bitmap_andnot(bitmap, model->features, model->def->base_feat,
 198                      S390_FEAT_MAX);
 199        if (!bitmap_empty(bitmap, S390_FEAT_MAX)) {
 200            s390_feat_bitmap_to_ascii(bitmap, qdict, qdict_add_enabled_feat);
 201        }
 202    } else {
 203        /* expand all features */
 204        s390_feat_bitmap_to_ascii(model->features, qdict,
 205                                  qdict_add_enabled_feat);
 206        bitmap_complement(bitmap, model->features, S390_FEAT_MAX);
 207        s390_feat_bitmap_to_ascii(bitmap, qdict, qdict_add_disabled_feat);
 208    }
 209
 210    if (!qdict_size(qdict)) {
 211        qobject_unref(qdict);
 212    } else {
 213        info->props = QOBJECT(qdict);
 214        info->has_props = true;
 215    }
 216}
 217
 218CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type,
 219                                                      CpuModelInfo *model,
 220                                                      Error **errp)
 221{
 222    Error *err = NULL;
 223    CpuModelExpansionInfo *expansion_info = NULL;
 224    S390CPUModel s390_model;
 225    bool delta_changes = false;
 226
 227    /* convert it to our internal representation */
 228    cpu_model_from_info(&s390_model, model, &err);
 229    if (err) {
 230        error_propagate(errp, err);
 231        return NULL;
 232    }
 233
 234    if (type == CPU_MODEL_EXPANSION_TYPE_STATIC) {
 235        delta_changes = true;
 236    } else if (type != CPU_MODEL_EXPANSION_TYPE_FULL) {
 237        error_setg(errp, "The requested expansion type is not supported.");
 238        return NULL;
 239    }
 240
 241    /* convert it back to a static representation */
 242    expansion_info = g_new0(CpuModelExpansionInfo, 1);
 243    expansion_info->model = g_malloc0(sizeof(*expansion_info->model));
 244    cpu_info_from_model(expansion_info->model, &s390_model, delta_changes);
 245    return expansion_info;
 246}
 247
 248static void list_add_feat(const char *name, void *opaque)
 249{
 250    strList **last = (strList **) opaque;
 251
 252    QAPI_LIST_PREPEND(*last, g_strdup(name));
 253}
 254
 255CpuModelCompareInfo *qmp_query_cpu_model_comparison(CpuModelInfo *infoa,
 256                                                     CpuModelInfo *infob,
 257                                                     Error **errp)
 258{
 259    Error *err = NULL;
 260    CpuModelCompareResult feat_result, gen_result;
 261    CpuModelCompareInfo *compare_info;
 262    S390FeatBitmap missing, added;
 263    S390CPUModel modela, modelb;
 264
 265    /* convert both models to our internal representation */
 266    cpu_model_from_info(&modela, infoa, &err);
 267    if (err) {
 268        error_propagate(errp, err);
 269        return NULL;
 270    }
 271    cpu_model_from_info(&modelb, infob, &err);
 272    if (err) {
 273        error_propagate(errp, err);
 274        return NULL;
 275    }
 276    compare_info = g_new0(CpuModelCompareInfo, 1);
 277
 278    /* check the cpu generation and ga level */
 279    if (modela.def->gen == modelb.def->gen) {
 280        if (modela.def->ec_ga == modelb.def->ec_ga) {
 281            /* ec and corresponding bc are identical */
 282            gen_result = CPU_MODEL_COMPARE_RESULT_IDENTICAL;
 283        } else if (modela.def->ec_ga < modelb.def->ec_ga) {
 284            gen_result = CPU_MODEL_COMPARE_RESULT_SUBSET;
 285        } else {
 286            gen_result = CPU_MODEL_COMPARE_RESULT_SUPERSET;
 287        }
 288    } else if (modela.def->gen < modelb.def->gen) {
 289        gen_result = CPU_MODEL_COMPARE_RESULT_SUBSET;
 290    } else {
 291        gen_result = CPU_MODEL_COMPARE_RESULT_SUPERSET;
 292    }
 293    if (gen_result != CPU_MODEL_COMPARE_RESULT_IDENTICAL) {
 294        /* both models cannot be made identical */
 295        list_add_feat("type", &compare_info->responsible_properties);
 296    }
 297
 298    /* check the feature set */
 299    if (bitmap_equal(modela.features, modelb.features, S390_FEAT_MAX)) {
 300        feat_result = CPU_MODEL_COMPARE_RESULT_IDENTICAL;
 301    } else {
 302        bitmap_andnot(missing, modela.features, modelb.features, S390_FEAT_MAX);
 303        s390_feat_bitmap_to_ascii(missing,
 304                                  &compare_info->responsible_properties,
 305                                  list_add_feat);
 306        bitmap_andnot(added, modelb.features, modela.features, S390_FEAT_MAX);
 307        s390_feat_bitmap_to_ascii(added, &compare_info->responsible_properties,
 308                                  list_add_feat);
 309        if (bitmap_empty(missing, S390_FEAT_MAX)) {
 310            feat_result = CPU_MODEL_COMPARE_RESULT_SUBSET;
 311        } else if (bitmap_empty(added, S390_FEAT_MAX)) {
 312            feat_result = CPU_MODEL_COMPARE_RESULT_SUPERSET;
 313        } else {
 314            feat_result = CPU_MODEL_COMPARE_RESULT_INCOMPATIBLE;
 315        }
 316    }
 317
 318    /* combine the results */
 319    if (gen_result == feat_result) {
 320        compare_info->result = gen_result;
 321    } else if (feat_result == CPU_MODEL_COMPARE_RESULT_IDENTICAL) {
 322        compare_info->result = gen_result;
 323    } else if (gen_result == CPU_MODEL_COMPARE_RESULT_IDENTICAL) {
 324        compare_info->result = feat_result;
 325    } else {
 326        compare_info->result = CPU_MODEL_COMPARE_RESULT_INCOMPATIBLE;
 327    }
 328    return compare_info;
 329}
 330
 331CpuModelBaselineInfo *qmp_query_cpu_model_baseline(CpuModelInfo *infoa,
 332                                                    CpuModelInfo *infob,
 333                                                    Error **errp)
 334{
 335    Error *err = NULL;
 336    CpuModelBaselineInfo *baseline_info;
 337    S390CPUModel modela, modelb, model;
 338    uint16_t cpu_type;
 339    uint8_t max_gen_ga;
 340    uint8_t max_gen;
 341
 342    /* convert both models to our internal representation */
 343    cpu_model_from_info(&modela, infoa, &err);
 344    if (err) {
 345        error_propagate(errp, err);
 346        return NULL;
 347    }
 348
 349    cpu_model_from_info(&modelb, infob, &err);
 350    if (err) {
 351        error_propagate(errp, err);
 352        return NULL;
 353    }
 354
 355    /* features both models support */
 356    bitmap_and(model.features, modela.features, modelb.features, S390_FEAT_MAX);
 357
 358    /* detect the maximum model not regarding features */
 359    if (modela.def->gen == modelb.def->gen) {
 360        if (modela.def->type == modelb.def->type) {
 361            cpu_type = modela.def->type;
 362        } else {
 363            cpu_type = 0;
 364        }
 365        max_gen = modela.def->gen;
 366        max_gen_ga = MIN(modela.def->ec_ga, modelb.def->ec_ga);
 367    } else if (modela.def->gen > modelb.def->gen) {
 368        cpu_type = modelb.def->type;
 369        max_gen = modelb.def->gen;
 370        max_gen_ga = modelb.def->ec_ga;
 371    } else {
 372        cpu_type = modela.def->type;
 373        max_gen = modela.def->gen;
 374        max_gen_ga = modela.def->ec_ga;
 375    }
 376
 377    model.def = s390_find_cpu_def(cpu_type, max_gen, max_gen_ga,
 378                                  model.features);
 379
 380    /* models without early base features (esan3) are bad */
 381    if (!model.def) {
 382        error_setg(errp, "No compatible CPU model could be created as"
 383                   " important base features are disabled");
 384        return NULL;
 385    }
 386
 387    /* strip off features not part of the max model */
 388    bitmap_and(model.features, model.features, model.def->full_feat,
 389               S390_FEAT_MAX);
 390
 391    baseline_info = g_new0(CpuModelBaselineInfo, 1);
 392    baseline_info->model = g_malloc0(sizeof(*baseline_info->model));
 393    cpu_info_from_model(baseline_info->model, &model, true);
 394    return baseline_info;
 395}
 396
 397void apply_cpu_model(const S390CPUModel *model, Error **errp)
 398{
 399    Error *err = NULL;
 400    static S390CPUModel applied_model;
 401    static bool applied;
 402
 403    /*
 404     * We have the same model for all VCPUs. KVM can only be configured before
 405     * any VCPUs are defined in KVM.
 406     */
 407    if (applied) {
 408        if (model && memcmp(&applied_model, model, sizeof(S390CPUModel))) {
 409            error_setg(errp, "Mixed CPU models are not supported on s390x.");
 410        }
 411        return;
 412    }
 413
 414    if (kvm_enabled()) {
 415        kvm_s390_apply_cpu_model(model, &err);
 416        if (err) {
 417            error_propagate(errp, err);
 418            return;
 419        }
 420    }
 421
 422    applied = true;
 423    if (model) {
 424        applied_model = *model;
 425    }
 426}
 427