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