]>
Commit | Line | Data |
---|---|---|
33face6b DG |
1 | /* |
2 | * QEMU PowerPC pSeries Logical Partition capabilities handling | |
3 | * | |
4 | * Copyright (c) 2017 David Gibson, Red Hat Inc. | |
5 | * | |
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
7 | * of this software and associated documentation files (the "Software"), to deal | |
8 | * in the Software without restriction, including without limitation the rights | |
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
10 | * copies of the Software, and to permit persons to whom the Software is | |
11 | * furnished to do so, subject to the following conditions: | |
12 | * | |
13 | * The above copyright notice and this permission notice shall be included in | |
14 | * all copies or substantial portions of the Software. | |
15 | * | |
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
22 | * THE SOFTWARE. | |
23 | */ | |
24 | #include "qemu/osdep.h" | |
be85537d | 25 | #include "qemu/error-report.h" |
33face6b DG |
26 | #include "qapi/error.h" |
27 | #include "qapi/visitor.h" | |
ee76a09f DG |
28 | #include "sysemu/hw_accel.h" |
29 | #include "target/ppc/cpu.h" | |
30 | #include "cpu-models.h" | |
31 | #include "kvm_ppc.h" | |
33face6b DG |
32 | |
33 | #include "hw/ppc/spapr.h" | |
34 | ||
35 | typedef struct sPAPRCapabilityInfo { | |
36 | const char *name; | |
37 | const char *description; | |
4e5fe368 | 38 | int index; |
33face6b | 39 | |
4e5fe368 SJS |
40 | /* Getter and Setter Function Pointers */ |
41 | ObjectPropertyAccessor *get; | |
42 | ObjectPropertyAccessor *set; | |
43 | const char *type; | |
33face6b | 44 | /* Make sure the virtual hardware can support this capability */ |
4e5fe368 | 45 | void (*apply)(sPAPRMachineState *spapr, uint8_t val, Error **errp); |
33face6b DG |
46 | } sPAPRCapabilityInfo; |
47 | ||
4e5fe368 SJS |
48 | static void spapr_cap_get_bool(Object *obj, Visitor *v, const char *name, |
49 | void *opaque, Error **errp) | |
50 | { | |
51 | sPAPRCapabilityInfo *cap = opaque; | |
52 | sPAPRMachineState *spapr = SPAPR_MACHINE(obj); | |
53 | bool value = spapr_get_cap(spapr, cap->index) == SPAPR_CAP_ON; | |
54 | ||
55 | visit_type_bool(v, name, &value, errp); | |
56 | } | |
57 | ||
58 | static void spapr_cap_set_bool(Object *obj, Visitor *v, const char *name, | |
59 | void *opaque, Error **errp) | |
ee76a09f | 60 | { |
4e5fe368 SJS |
61 | sPAPRCapabilityInfo *cap = opaque; |
62 | sPAPRMachineState *spapr = SPAPR_MACHINE(obj); | |
63 | bool value; | |
64 | Error *local_err = NULL; | |
65 | ||
66 | visit_type_bool(v, name, &value, &local_err); | |
67 | if (local_err) { | |
68 | error_propagate(errp, local_err); | |
69 | return; | |
70 | } | |
71 | ||
72 | spapr->cmd_line_caps[cap->index] = true; | |
73 | spapr->eff.caps[cap->index] = value ? SPAPR_CAP_ON : SPAPR_CAP_OFF; | |
74 | } | |
75 | ||
8f38eaf8 SJS |
76 | static void spapr_cap_get_tristate(Object *obj, Visitor *v, const char *name, |
77 | void *opaque, Error **errp) | |
6898aed7 SJS |
78 | { |
79 | sPAPRCapabilityInfo *cap = opaque; | |
80 | sPAPRMachineState *spapr = SPAPR_MACHINE(obj); | |
81 | char *val = NULL; | |
82 | uint8_t value = spapr_get_cap(spapr, cap->index); | |
83 | ||
84 | switch (value) { | |
85 | case SPAPR_CAP_BROKEN: | |
86 | val = g_strdup("broken"); | |
87 | break; | |
88 | case SPAPR_CAP_WORKAROUND: | |
89 | val = g_strdup("workaround"); | |
90 | break; | |
91 | case SPAPR_CAP_FIXED: | |
92 | val = g_strdup("fixed"); | |
93 | break; | |
94 | default: | |
95 | error_setg(errp, "Invalid value (%d) for cap-%s", value, cap->name); | |
96 | return; | |
97 | } | |
98 | ||
99 | visit_type_str(v, name, &val, errp); | |
100 | g_free(val); | |
101 | } | |
102 | ||
8f38eaf8 SJS |
103 | static void spapr_cap_set_tristate(Object *obj, Visitor *v, const char *name, |
104 | void *opaque, Error **errp) | |
6898aed7 SJS |
105 | { |
106 | sPAPRCapabilityInfo *cap = opaque; | |
107 | sPAPRMachineState *spapr = SPAPR_MACHINE(obj); | |
108 | char *val; | |
109 | Error *local_err = NULL; | |
110 | uint8_t value; | |
111 | ||
112 | visit_type_str(v, name, &val, &local_err); | |
113 | if (local_err) { | |
114 | error_propagate(errp, local_err); | |
115 | return; | |
116 | } | |
117 | ||
118 | if (!strcasecmp(val, "broken")) { | |
119 | value = SPAPR_CAP_BROKEN; | |
120 | } else if (!strcasecmp(val, "workaround")) { | |
121 | value = SPAPR_CAP_WORKAROUND; | |
122 | } else if (!strcasecmp(val, "fixed")) { | |
123 | value = SPAPR_CAP_FIXED; | |
124 | } else { | |
125 | error_setg(errp, "Invalid capability mode \"%s\" for cap-%s", val, | |
126 | cap->name); | |
127 | goto out; | |
128 | } | |
129 | ||
130 | spapr->cmd_line_caps[cap->index] = true; | |
131 | spapr->eff.caps[cap->index] = value; | |
132 | out: | |
133 | g_free(val); | |
134 | } | |
135 | ||
4e5fe368 SJS |
136 | static void cap_htm_apply(sPAPRMachineState *spapr, uint8_t val, Error **errp) |
137 | { | |
138 | if (!val) { | |
139 | /* TODO: We don't support disabling htm yet */ | |
140 | return; | |
141 | } | |
ee76a09f DG |
142 | if (tcg_enabled()) { |
143 | error_setg(errp, | |
144 | "No Transactional Memory support in TCG, try cap-htm=off"); | |
145 | } else if (kvm_enabled() && !kvmppc_has_cap_htm()) { | |
146 | error_setg(errp, | |
147 | "KVM implementation does not support Transactional Memory, try cap-htm=off" | |
148 | ); | |
149 | } | |
150 | } | |
151 | ||
4e5fe368 | 152 | static void cap_vsx_apply(sPAPRMachineState *spapr, uint8_t val, Error **errp) |
29386642 DG |
153 | { |
154 | PowerPCCPU *cpu = POWERPC_CPU(first_cpu); | |
155 | CPUPPCState *env = &cpu->env; | |
156 | ||
4e5fe368 SJS |
157 | if (!val) { |
158 | /* TODO: We don't support disabling vsx yet */ | |
159 | return; | |
160 | } | |
29386642 DG |
161 | /* Allowable CPUs in spapr_cpu_core.c should already have gotten |
162 | * rid of anything that doesn't do VMX */ | |
163 | g_assert(env->insns_flags & PPC_ALTIVEC); | |
164 | if (!(env->insns_flags2 & PPC2_VSX)) { | |
165 | error_setg(errp, "VSX support not available, try cap-vsx=off"); | |
166 | } | |
167 | } | |
168 | ||
4e5fe368 | 169 | static void cap_dfp_apply(sPAPRMachineState *spapr, uint8_t val, Error **errp) |
2d1fb9bc DG |
170 | { |
171 | PowerPCCPU *cpu = POWERPC_CPU(first_cpu); | |
172 | CPUPPCState *env = &cpu->env; | |
173 | ||
4e5fe368 SJS |
174 | if (!val) { |
175 | /* TODO: We don't support disabling dfp yet */ | |
176 | return; | |
177 | } | |
2d1fb9bc DG |
178 | if (!(env->insns_flags2 & PPC2_DFP)) { |
179 | error_setg(errp, "DFP support not available, try cap-dfp=off"); | |
180 | } | |
181 | } | |
182 | ||
8f38eaf8 SJS |
183 | static void cap_safe_cache_apply(sPAPRMachineState *spapr, uint8_t val, |
184 | Error **errp) | |
185 | { | |
186 | if (tcg_enabled() && val) { | |
187 | /* TODO - for now only allow broken for TCG */ | |
188 | error_setg(errp, "Requested safe cache capability level not supported by tcg, try a different value for cap-cfpc"); | |
189 | } else if (kvm_enabled() && (val > kvmppc_get_cap_safe_cache())) { | |
190 | error_setg(errp, "Requested safe cache capability level not supported by kvm, try a different value for cap-cfpc"); | |
191 | } | |
192 | } | |
193 | ||
09114fd8 SJS |
194 | static void cap_safe_bounds_check_apply(sPAPRMachineState *spapr, uint8_t val, |
195 | Error **errp) | |
196 | { | |
197 | if (tcg_enabled() && val) { | |
198 | /* TODO - for now only allow broken for TCG */ | |
199 | error_setg(errp, "Requested safe bounds check capability level not supported by tcg, try a different value for cap-sbbc"); | |
200 | } else if (kvm_enabled() && (val > kvmppc_get_cap_safe_bounds_check())) { | |
201 | error_setg(errp, "Requested safe bounds check capability level not supported by kvm, try a different value for cap-sbbc"); | |
202 | } | |
203 | } | |
204 | ||
4be8d4e7 SJS |
205 | static void cap_safe_indirect_branch_apply(sPAPRMachineState *spapr, |
206 | uint8_t val, Error **errp) | |
207 | { | |
208 | if (tcg_enabled() && val) { | |
209 | /* TODO - for now only allow broken for TCG */ | |
210 | error_setg(errp, "Requested safe indirect branch capability level not supported by tcg, try a different value for cap-ibs"); | |
211 | } else if (kvm_enabled() && (val > kvmppc_get_cap_safe_indirect_branch())) { | |
212 | error_setg(errp, "Requested safe indirect branch capability level not supported by kvm, try a different value for cap-ibs"); | |
213 | } | |
214 | } | |
215 | ||
8f38eaf8 | 216 | #define VALUE_DESC_TRISTATE " (broken, workaround, fixed)" |
4e5fe368 SJS |
217 | |
218 | sPAPRCapabilityInfo capability_table[SPAPR_CAP_NUM] = { | |
219 | [SPAPR_CAP_HTM] = { | |
ee76a09f DG |
220 | .name = "htm", |
221 | .description = "Allow Hardware Transactional Memory (HTM)", | |
4e5fe368 SJS |
222 | .index = SPAPR_CAP_HTM, |
223 | .get = spapr_cap_get_bool, | |
224 | .set = spapr_cap_set_bool, | |
225 | .type = "bool", | |
226 | .apply = cap_htm_apply, | |
ee76a09f | 227 | }, |
4e5fe368 | 228 | [SPAPR_CAP_VSX] = { |
29386642 DG |
229 | .name = "vsx", |
230 | .description = "Allow Vector Scalar Extensions (VSX)", | |
4e5fe368 SJS |
231 | .index = SPAPR_CAP_VSX, |
232 | .get = spapr_cap_get_bool, | |
233 | .set = spapr_cap_set_bool, | |
234 | .type = "bool", | |
235 | .apply = cap_vsx_apply, | |
29386642 | 236 | }, |
4e5fe368 | 237 | [SPAPR_CAP_DFP] = { |
2d1fb9bc DG |
238 | .name = "dfp", |
239 | .description = "Allow Decimal Floating Point (DFP)", | |
4e5fe368 SJS |
240 | .index = SPAPR_CAP_DFP, |
241 | .get = spapr_cap_get_bool, | |
242 | .set = spapr_cap_set_bool, | |
243 | .type = "bool", | |
244 | .apply = cap_dfp_apply, | |
2d1fb9bc | 245 | }, |
8f38eaf8 SJS |
246 | [SPAPR_CAP_CFPC] = { |
247 | .name = "cfpc", | |
248 | .description = "Cache Flush on Privilege Change" VALUE_DESC_TRISTATE, | |
249 | .index = SPAPR_CAP_CFPC, | |
250 | .get = spapr_cap_get_tristate, | |
251 | .set = spapr_cap_set_tristate, | |
252 | .type = "string", | |
253 | .apply = cap_safe_cache_apply, | |
254 | }, | |
09114fd8 SJS |
255 | [SPAPR_CAP_SBBC] = { |
256 | .name = "sbbc", | |
257 | .description = "Speculation Barrier Bounds Checking" VALUE_DESC_TRISTATE, | |
258 | .index = SPAPR_CAP_SBBC, | |
259 | .get = spapr_cap_get_tristate, | |
260 | .set = spapr_cap_set_tristate, | |
261 | .type = "string", | |
262 | .apply = cap_safe_bounds_check_apply, | |
263 | }, | |
4be8d4e7 SJS |
264 | [SPAPR_CAP_IBS] = { |
265 | .name = "ibs", | |
266 | .description = "Indirect Branch Serialisation" VALUE_DESC_TRISTATE, | |
267 | .index = SPAPR_CAP_IBS, | |
268 | .get = spapr_cap_get_tristate, | |
269 | .set = spapr_cap_set_tristate, | |
270 | .type = "string", | |
271 | .apply = cap_safe_indirect_branch_apply, | |
272 | }, | |
33face6b DG |
273 | }; |
274 | ||
275 | static sPAPRCapabilities default_caps_with_cpu(sPAPRMachineState *spapr, | |
276 | CPUState *cs) | |
277 | { | |
278 | sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr); | |
ee76a09f | 279 | PowerPCCPU *cpu = POWERPC_CPU(cs); |
33face6b DG |
280 | sPAPRCapabilities caps; |
281 | ||
282 | caps = smc->default_caps; | |
283 | ||
ee76a09f DG |
284 | if (!ppc_check_compat(cpu, CPU_POWERPC_LOGICAL_2_07, |
285 | 0, spapr->max_compat_pvr)) { | |
4e5fe368 | 286 | caps.caps[SPAPR_CAP_HTM] = SPAPR_CAP_OFF; |
ee76a09f | 287 | } |
33face6b | 288 | |
29386642 DG |
289 | if (!ppc_check_compat(cpu, CPU_POWERPC_LOGICAL_2_06, |
290 | 0, spapr->max_compat_pvr)) { | |
4e5fe368 SJS |
291 | caps.caps[SPAPR_CAP_VSX] = SPAPR_CAP_OFF; |
292 | caps.caps[SPAPR_CAP_DFP] = SPAPR_CAP_OFF; | |
29386642 DG |
293 | } |
294 | ||
33face6b DG |
295 | return caps; |
296 | } | |
297 | ||
4e5fe368 | 298 | int spapr_caps_pre_load(void *opaque) |
be85537d DG |
299 | { |
300 | sPAPRMachineState *spapr = opaque; | |
301 | ||
4e5fe368 SJS |
302 | /* Set to default so we can tell if this came in with the migration */ |
303 | spapr->mig = spapr->def; | |
304 | return 0; | |
305 | } | |
306 | ||
307 | int spapr_caps_pre_save(void *opaque) | |
308 | { | |
309 | sPAPRMachineState *spapr = opaque; | |
310 | ||
311 | spapr->mig = spapr->eff; | |
312 | return 0; | |
be85537d DG |
313 | } |
314 | ||
315 | /* This has to be called from the top-level spapr post_load, not the | |
316 | * caps specific one. Otherwise it wouldn't be called when the source | |
317 | * caps are all defaults, which could still conflict with overridden | |
318 | * caps on the destination */ | |
319 | int spapr_caps_post_migration(sPAPRMachineState *spapr) | |
320 | { | |
be85537d DG |
321 | int i; |
322 | bool ok = true; | |
4e5fe368 | 323 | sPAPRCapabilities dstcaps = spapr->eff; |
be85537d DG |
324 | sPAPRCapabilities srccaps; |
325 | ||
326 | srccaps = default_caps_with_cpu(spapr, first_cpu); | |
4e5fe368 SJS |
327 | for (i = 0; i < SPAPR_CAP_NUM; i++) { |
328 | /* If not default value then assume came in with the migration */ | |
329 | if (spapr->mig.caps[i] != spapr->def.caps[i]) { | |
330 | srccaps.caps[i] = spapr->mig.caps[i]; | |
331 | } | |
332 | } | |
be85537d | 333 | |
4e5fe368 | 334 | for (i = 0; i < SPAPR_CAP_NUM; i++) { |
be85537d DG |
335 | sPAPRCapabilityInfo *info = &capability_table[i]; |
336 | ||
4e5fe368 SJS |
337 | if (srccaps.caps[i] > dstcaps.caps[i]) { |
338 | error_report("cap-%s higher level (%d) in incoming stream than on destination (%d)", | |
339 | info->name, srccaps.caps[i], dstcaps.caps[i]); | |
be85537d DG |
340 | ok = false; |
341 | } | |
342 | ||
4e5fe368 SJS |
343 | if (srccaps.caps[i] < dstcaps.caps[i]) { |
344 | warn_report("cap-%s lower level (%d) in incoming stream than on destination (%d)", | |
345 | info->name, srccaps.caps[i], dstcaps.caps[i]); | |
be85537d DG |
346 | } |
347 | } | |
348 | ||
be85537d DG |
349 | return ok ? 0 : -EINVAL; |
350 | } | |
351 | ||
1f63ebaa | 352 | /* Used to generate the migration field and needed function for a spapr cap */ |
8c5909c4 SJS |
353 | #define SPAPR_CAP_MIG_STATE(sname, cap) \ |
354 | static bool spapr_cap_##sname##_needed(void *opaque) \ | |
1f63ebaa SJS |
355 | { \ |
356 | sPAPRMachineState *spapr = opaque; \ | |
357 | \ | |
8c5909c4 SJS |
358 | return spapr->cmd_line_caps[cap] && \ |
359 | (spapr->eff.caps[cap] != \ | |
360 | spapr->def.caps[cap]); \ | |
1f63ebaa SJS |
361 | } \ |
362 | \ | |
8c5909c4 SJS |
363 | const VMStateDescription vmstate_spapr_cap_##sname = { \ |
364 | .name = "spapr/cap/" #sname, \ | |
1f63ebaa SJS |
365 | .version_id = 1, \ |
366 | .minimum_version_id = 1, \ | |
8c5909c4 | 367 | .needed = spapr_cap_##sname##_needed, \ |
1f63ebaa | 368 | .fields = (VMStateField[]) { \ |
8c5909c4 | 369 | VMSTATE_UINT8(mig.caps[cap], \ |
1f63ebaa SJS |
370 | sPAPRMachineState), \ |
371 | VMSTATE_END_OF_LIST() \ | |
372 | }, \ | |
be85537d DG |
373 | } |
374 | ||
8c5909c4 SJS |
375 | SPAPR_CAP_MIG_STATE(htm, SPAPR_CAP_HTM); |
376 | SPAPR_CAP_MIG_STATE(vsx, SPAPR_CAP_VSX); | |
377 | SPAPR_CAP_MIG_STATE(dfp, SPAPR_CAP_DFP); | |
378 | SPAPR_CAP_MIG_STATE(cfpc, SPAPR_CAP_CFPC); | |
379 | SPAPR_CAP_MIG_STATE(sbbc, SPAPR_CAP_SBBC); | |
380 | SPAPR_CAP_MIG_STATE(ibs, SPAPR_CAP_IBS); | |
33face6b | 381 | |
4e5fe368 | 382 | void spapr_caps_reset(sPAPRMachineState *spapr) |
33face6b | 383 | { |
4e5fe368 | 384 | sPAPRCapabilities default_caps; |
33face6b DG |
385 | int i; |
386 | ||
4e5fe368 SJS |
387 | /* First compute the actual set of caps we're running with.. */ |
388 | default_caps = default_caps_with_cpu(spapr, first_cpu); | |
389 | ||
390 | for (i = 0; i < SPAPR_CAP_NUM; i++) { | |
391 | /* Store the defaults */ | |
392 | spapr->def.caps[i] = default_caps.caps[i]; | |
393 | /* If not set on the command line then apply the default value */ | |
394 | if (!spapr->cmd_line_caps[i]) { | |
395 | spapr->eff.caps[i] = default_caps.caps[i]; | |
396 | } | |
33face6b DG |
397 | } |
398 | ||
4e5fe368 SJS |
399 | /* .. then apply those caps to the virtual hardware */ |
400 | ||
401 | for (i = 0; i < SPAPR_CAP_NUM; i++) { | |
402 | sPAPRCapabilityInfo *info = &capability_table[i]; | |
33face6b | 403 | |
4e5fe368 SJS |
404 | /* |
405 | * If the apply function can't set the desired level and thinks it's | |
406 | * fatal, it should cause that. | |
407 | */ | |
408 | info->apply(spapr, spapr->eff.caps[i], &error_fatal); | |
33face6b | 409 | } |
33face6b DG |
410 | } |
411 | ||
412 | void spapr_caps_add_properties(sPAPRMachineClass *smc, Error **errp) | |
413 | { | |
414 | Error *local_err = NULL; | |
415 | ObjectClass *klass = OBJECT_CLASS(smc); | |
416 | int i; | |
417 | ||
418 | for (i = 0; i < ARRAY_SIZE(capability_table); i++) { | |
419 | sPAPRCapabilityInfo *cap = &capability_table[i]; | |
420 | const char *name = g_strdup_printf("cap-%s", cap->name); | |
4e5fe368 | 421 | char *desc; |
33face6b | 422 | |
4e5fe368 SJS |
423 | object_class_property_add(klass, name, cap->type, |
424 | cap->get, cap->set, | |
425 | NULL, cap, &local_err); | |
33face6b DG |
426 | if (local_err) { |
427 | error_propagate(errp, local_err); | |
428 | return; | |
429 | } | |
430 | ||
895d5cd6 | 431 | desc = g_strdup_printf("%s", cap->description); |
4e5fe368 SJS |
432 | object_class_property_set_description(klass, name, desc, &local_err); |
433 | g_free(desc); | |
33face6b DG |
434 | if (local_err) { |
435 | error_propagate(errp, local_err); | |
436 | return; | |
437 | } | |
438 | } | |
439 | } |