]>
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; | |
38 | uint64_t flag; | |
39 | ||
40 | /* Make sure the virtual hardware can support this capability */ | |
41 | void (*allow)(sPAPRMachineState *spapr, Error **errp); | |
42 | ||
43 | /* If possible, tell the virtual hardware not to allow the cap to | |
44 | * be used at all */ | |
45 | void (*disallow)(sPAPRMachineState *spapr, Error **errp); | |
46 | } sPAPRCapabilityInfo; | |
47 | ||
ee76a09f DG |
48 | static void cap_htm_allow(sPAPRMachineState *spapr, Error **errp) |
49 | { | |
50 | if (tcg_enabled()) { | |
51 | error_setg(errp, | |
52 | "No Transactional Memory support in TCG, try cap-htm=off"); | |
53 | } else if (kvm_enabled() && !kvmppc_has_cap_htm()) { | |
54 | error_setg(errp, | |
55 | "KVM implementation does not support Transactional Memory, try cap-htm=off" | |
56 | ); | |
57 | } | |
58 | } | |
59 | ||
29386642 DG |
60 | static void cap_vsx_allow(sPAPRMachineState *spapr, Error **errp) |
61 | { | |
62 | PowerPCCPU *cpu = POWERPC_CPU(first_cpu); | |
63 | CPUPPCState *env = &cpu->env; | |
64 | ||
65 | /* Allowable CPUs in spapr_cpu_core.c should already have gotten | |
66 | * rid of anything that doesn't do VMX */ | |
67 | g_assert(env->insns_flags & PPC_ALTIVEC); | |
68 | if (!(env->insns_flags2 & PPC2_VSX)) { | |
69 | error_setg(errp, "VSX support not available, try cap-vsx=off"); | |
70 | } | |
71 | } | |
72 | ||
33face6b | 73 | static sPAPRCapabilityInfo capability_table[] = { |
ee76a09f DG |
74 | { |
75 | .name = "htm", | |
76 | .description = "Allow Hardware Transactional Memory (HTM)", | |
77 | .flag = SPAPR_CAP_HTM, | |
78 | .allow = cap_htm_allow, | |
79 | /* TODO: add cap_htm_disallow */ | |
80 | }, | |
29386642 DG |
81 | { |
82 | .name = "vsx", | |
83 | .description = "Allow Vector Scalar Extensions (VSX)", | |
84 | .flag = SPAPR_CAP_VSX, | |
85 | .allow = cap_vsx_allow, | |
86 | /* TODO: add cap_vsx_disallow */ | |
87 | }, | |
33face6b DG |
88 | }; |
89 | ||
90 | static sPAPRCapabilities default_caps_with_cpu(sPAPRMachineState *spapr, | |
91 | CPUState *cs) | |
92 | { | |
93 | sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr); | |
ee76a09f | 94 | PowerPCCPU *cpu = POWERPC_CPU(cs); |
33face6b DG |
95 | sPAPRCapabilities caps; |
96 | ||
97 | caps = smc->default_caps; | |
98 | ||
ee76a09f DG |
99 | if (!ppc_check_compat(cpu, CPU_POWERPC_LOGICAL_2_07, |
100 | 0, spapr->max_compat_pvr)) { | |
101 | caps.mask &= ~SPAPR_CAP_HTM; | |
102 | } | |
33face6b | 103 | |
29386642 DG |
104 | if (!ppc_check_compat(cpu, CPU_POWERPC_LOGICAL_2_06, |
105 | 0, spapr->max_compat_pvr)) { | |
106 | caps.mask &= ~SPAPR_CAP_VSX; | |
107 | } | |
108 | ||
33face6b DG |
109 | return caps; |
110 | } | |
111 | ||
be85537d DG |
112 | static bool spapr_caps_needed(void *opaque) |
113 | { | |
114 | sPAPRMachineState *spapr = opaque; | |
115 | ||
116 | return (spapr->forced_caps.mask != 0) || (spapr->forbidden_caps.mask != 0); | |
117 | } | |
118 | ||
119 | /* This has to be called from the top-level spapr post_load, not the | |
120 | * caps specific one. Otherwise it wouldn't be called when the source | |
121 | * caps are all defaults, which could still conflict with overridden | |
122 | * caps on the destination */ | |
123 | int spapr_caps_post_migration(sPAPRMachineState *spapr) | |
124 | { | |
125 | uint64_t allcaps = 0; | |
126 | int i; | |
127 | bool ok = true; | |
128 | sPAPRCapabilities dstcaps = spapr->effective_caps; | |
129 | sPAPRCapabilities srccaps; | |
130 | ||
131 | srccaps = default_caps_with_cpu(spapr, first_cpu); | |
132 | srccaps.mask |= spapr->mig_forced_caps.mask; | |
133 | srccaps.mask &= ~spapr->mig_forbidden_caps.mask; | |
134 | ||
135 | for (i = 0; i < ARRAY_SIZE(capability_table); i++) { | |
136 | sPAPRCapabilityInfo *info = &capability_table[i]; | |
137 | ||
138 | allcaps |= info->flag; | |
139 | ||
140 | if ((srccaps.mask & info->flag) && !(dstcaps.mask & info->flag)) { | |
141 | error_report("cap-%s=on in incoming stream, but off in destination", | |
142 | info->name); | |
143 | ok = false; | |
144 | } | |
145 | ||
146 | if (!(srccaps.mask & info->flag) && (dstcaps.mask & info->flag)) { | |
147 | warn_report("cap-%s=off in incoming stream, but on in destination", | |
148 | info->name); | |
149 | } | |
150 | } | |
151 | ||
152 | if (spapr->mig_forced_caps.mask & ~allcaps) { | |
153 | error_report( | |
154 | "Unknown capabilities 0x%"PRIx64" enabled in incoming stream", | |
155 | spapr->mig_forced_caps.mask & ~allcaps); | |
156 | ok = false; | |
157 | } | |
158 | if (spapr->mig_forbidden_caps.mask & ~allcaps) { | |
159 | warn_report( | |
160 | "Unknown capabilities 0x%"PRIx64" disabled in incoming stream", | |
161 | spapr->mig_forbidden_caps.mask & ~allcaps); | |
162 | } | |
163 | ||
164 | return ok ? 0 : -EINVAL; | |
165 | } | |
166 | ||
167 | static int spapr_caps_pre_save(void *opaque) | |
168 | { | |
169 | sPAPRMachineState *spapr = opaque; | |
170 | ||
171 | spapr->mig_forced_caps = spapr->forced_caps; | |
172 | spapr->mig_forbidden_caps = spapr->forbidden_caps; | |
173 | return 0; | |
174 | } | |
175 | ||
176 | static int spapr_caps_pre_load(void *opaque) | |
177 | { | |
178 | sPAPRMachineState *spapr = opaque; | |
179 | ||
180 | spapr->mig_forced_caps = spapr_caps(0); | |
181 | spapr->mig_forbidden_caps = spapr_caps(0); | |
182 | return 0; | |
183 | } | |
184 | ||
185 | const VMStateDescription vmstate_spapr_caps = { | |
186 | .name = "spapr/caps", | |
187 | .version_id = 1, | |
188 | .minimum_version_id = 1, | |
189 | .needed = spapr_caps_needed, | |
190 | .pre_save = spapr_caps_pre_save, | |
191 | .pre_load = spapr_caps_pre_load, | |
192 | .fields = (VMStateField[]) { | |
193 | VMSTATE_UINT64(mig_forced_caps.mask, sPAPRMachineState), | |
194 | VMSTATE_UINT64(mig_forbidden_caps.mask, sPAPRMachineState), | |
195 | VMSTATE_END_OF_LIST() | |
196 | }, | |
197 | }; | |
198 | ||
33face6b DG |
199 | void spapr_caps_reset(sPAPRMachineState *spapr) |
200 | { | |
201 | Error *local_err = NULL; | |
202 | sPAPRCapabilities caps; | |
203 | int i; | |
204 | ||
205 | /* First compute the actual set of caps we're running with.. */ | |
206 | caps = default_caps_with_cpu(spapr, first_cpu); | |
207 | ||
be85537d DG |
208 | /* Remove unnecessary forced/forbidden bits (this will help us |
209 | * with migration) */ | |
210 | spapr->forced_caps.mask &= ~caps.mask; | |
211 | spapr->forbidden_caps.mask &= caps.mask; | |
212 | ||
33face6b DG |
213 | caps.mask |= spapr->forced_caps.mask; |
214 | caps.mask &= ~spapr->forbidden_caps.mask; | |
215 | ||
216 | spapr->effective_caps = caps; | |
217 | ||
218 | /* .. then apply those caps to the virtual hardware */ | |
219 | ||
220 | for (i = 0; i < ARRAY_SIZE(capability_table); i++) { | |
221 | sPAPRCapabilityInfo *info = &capability_table[i]; | |
222 | ||
223 | if (spapr->effective_caps.mask & info->flag) { | |
224 | /* Failure to allow a cap is fatal - if the guest doesn't | |
225 | * have it, we'll be supplying an incorrect environment */ | |
226 | if (info->allow) { | |
227 | info->allow(spapr, &error_fatal); | |
228 | } | |
229 | } else { | |
230 | /* Failure to enforce a cap is only a warning. The guest | |
231 | * shouldn't be using it, since it's not advertised, so it | |
232 | * doesn't get to complain about weird behaviour if it | |
233 | * goes ahead anyway */ | |
234 | if (info->disallow) { | |
235 | info->disallow(spapr, &local_err); | |
236 | } | |
237 | if (local_err) { | |
238 | warn_report_err(local_err); | |
239 | local_err = NULL; | |
240 | } | |
241 | } | |
242 | } | |
243 | } | |
244 | ||
245 | static void spapr_cap_get(Object *obj, Visitor *v, const char *name, | |
246 | void *opaque, Error **errp) | |
247 | { | |
248 | sPAPRCapabilityInfo *cap = opaque; | |
249 | sPAPRMachineState *spapr = SPAPR_MACHINE(obj); | |
250 | bool value = spapr_has_cap(spapr, cap->flag); | |
251 | ||
252 | /* TODO: Could this get called before effective_caps is finalized | |
253 | * in spapr_caps_reset()? */ | |
254 | ||
255 | visit_type_bool(v, name, &value, errp); | |
256 | } | |
257 | ||
258 | static void spapr_cap_set(Object *obj, Visitor *v, const char *name, | |
259 | void *opaque, Error **errp) | |
260 | { | |
261 | sPAPRCapabilityInfo *cap = opaque; | |
262 | sPAPRMachineState *spapr = SPAPR_MACHINE(obj); | |
263 | bool value; | |
264 | Error *local_err = NULL; | |
265 | ||
266 | visit_type_bool(v, name, &value, &local_err); | |
267 | if (local_err) { | |
268 | error_propagate(errp, local_err); | |
269 | return; | |
270 | } | |
271 | ||
272 | if (value) { | |
273 | spapr->forced_caps.mask |= cap->flag; | |
274 | } else { | |
275 | spapr->forbidden_caps.mask |= cap->flag; | |
276 | } | |
277 | } | |
278 | ||
279 | void spapr_caps_validate(sPAPRMachineState *spapr, Error **errp) | |
280 | { | |
281 | uint64_t allcaps = 0; | |
282 | int i; | |
283 | ||
284 | for (i = 0; i < ARRAY_SIZE(capability_table); i++) { | |
285 | g_assert((allcaps & capability_table[i].flag) == 0); | |
286 | allcaps |= capability_table[i].flag; | |
287 | } | |
288 | ||
289 | g_assert((spapr->forced_caps.mask & ~allcaps) == 0); | |
290 | g_assert((spapr->forbidden_caps.mask & ~allcaps) == 0); | |
291 | ||
292 | if (spapr->forced_caps.mask & spapr->forbidden_caps.mask) { | |
293 | error_setg(errp, "Some sPAPR capabilities set both on and off"); | |
294 | return; | |
295 | } | |
33face6b DG |
296 | } |
297 | ||
298 | void spapr_caps_add_properties(sPAPRMachineClass *smc, Error **errp) | |
299 | { | |
300 | Error *local_err = NULL; | |
301 | ObjectClass *klass = OBJECT_CLASS(smc); | |
302 | int i; | |
303 | ||
304 | for (i = 0; i < ARRAY_SIZE(capability_table); i++) { | |
305 | sPAPRCapabilityInfo *cap = &capability_table[i]; | |
306 | const char *name = g_strdup_printf("cap-%s", cap->name); | |
307 | ||
308 | object_class_property_add(klass, name, "bool", | |
309 | spapr_cap_get, spapr_cap_set, NULL, | |
310 | cap, &local_err); | |
311 | if (local_err) { | |
312 | error_propagate(errp, local_err); | |
313 | return; | |
314 | } | |
315 | ||
316 | object_class_property_set_description(klass, name, cap->description, | |
317 | &local_err); | |
318 | if (local_err) { | |
319 | error_propagate(errp, local_err); | |
320 | return; | |
321 | } | |
322 | } | |
323 | } |