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 
26 static void list_add_feat(const char *name, void *opaque);
27 
28 static 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 
49 struct CpuDefinitionInfoListData {
50     CpuDefinitionInfoList *list;
51     S390CPUModel *model;
52 };
53 
54 static 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 
87 CpuDefinitionInfoList *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 
101 static 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 
168 static void qdict_add_disabled_feat(const char *name, void *opaque)
169 {
170     qdict_put_bool(opaque, name, false);
171 }
172 
173 static 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 */
179 static 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 
218 CpuModelExpansionInfo *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 
248 static 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 
255 CpuModelCompareInfo *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 
331 CpuModelBaselineInfo *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 
397 void 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