]>
Commit | Line | Data |
---|---|---|
3b542549 BR |
1 | /* |
2 | * sPAPR CPU core device, acts as container of CPU thread devices. | |
3 | * | |
4 | * Copyright (C) 2016 Bharata B Rao <bharata@linux.vnet.ibm.com> | |
5 | * | |
6 | * This work is licensed under the terms of the GNU GPL, version 2 or later. | |
7 | * See the COPYING file in the top-level directory. | |
8 | */ | |
9 | #include "hw/cpu/core.h" | |
10 | #include "hw/ppc/spapr_cpu_core.h" | |
fcf5ef2a | 11 | #include "target/ppc/cpu.h" |
3b542549 BR |
12 | #include "hw/ppc/spapr.h" |
13 | #include "hw/boards.h" | |
14 | #include "qapi/error.h" | |
a9c94277 | 15 | #include "sysemu/cpus.h" |
e57ca75c | 16 | #include "sysemu/kvm.h" |
fcf5ef2a | 17 | #include "target/ppc/kvm_ppc.h" |
afd10a0f | 18 | #include "hw/ppc/ppc.h" |
fcf5ef2a | 19 | #include "target/ppc/mmu-hash64.h" |
a9c94277 | 20 | #include "sysemu/numa.h" |
e57ca75c | 21 | #include "qemu/error-report.h" |
afd10a0f | 22 | |
7843c0d6 DG |
23 | void spapr_cpu_parse_features(sPAPRMachineState *spapr) |
24 | { | |
25 | /* | |
26 | * Backwards compatibility hack: | |
27 | * | |
28 | * CPUs had a "compat=" property which didn't make sense for | |
29 | * anything except pseries. It was replaced by "max-cpu-compat" | |
30 | * machine option. This supports old command lines like | |
31 | * -cpu POWER8,compat=power7 | |
32 | * By stripping the compat option and applying it to the machine | |
33 | * before passing it on to the cpu level parser. | |
34 | */ | |
35 | gchar **inpieces; | |
36 | int i, j; | |
37 | gchar *compat_str = NULL; | |
38 | ||
39 | inpieces = g_strsplit(MACHINE(spapr)->cpu_model, ",", 0); | |
40 | ||
41 | /* inpieces[0] is the actual model string */ | |
42 | i = 1; | |
43 | j = 1; | |
44 | while (inpieces[i]) { | |
45 | if (g_str_has_prefix(inpieces[i], "compat=")) { | |
46 | /* in case of multiple compat= options */ | |
47 | g_free(compat_str); | |
48 | compat_str = inpieces[i]; | |
49 | } else { | |
50 | j++; | |
51 | } | |
52 | ||
53 | i++; | |
54 | /* Excise compat options from list */ | |
55 | inpieces[j] = inpieces[i]; | |
56 | } | |
57 | ||
58 | if (compat_str) { | |
59 | char *val = compat_str + strlen("compat="); | |
60 | gchar *newprops = g_strjoinv(",", inpieces); | |
61 | ||
62 | object_property_set_str(OBJECT(spapr), val, "max-cpu-compat", | |
63 | &error_fatal); | |
64 | ||
65 | ppc_cpu_parse_features(newprops); | |
66 | g_free(newprops); | |
67 | } else { | |
68 | ppc_cpu_parse_features(MACHINE(spapr)->cpu_model); | |
69 | } | |
70 | ||
71 | g_strfreev(inpieces); | |
72 | } | |
73 | ||
afd10a0f BR |
74 | static void spapr_cpu_reset(void *opaque) |
75 | { | |
76 | sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); | |
77 | PowerPCCPU *cpu = opaque; | |
78 | CPUState *cs = CPU(cpu); | |
79 | CPUPPCState *env = &cpu->env; | |
80 | ||
81 | cpu_reset(cs); | |
82 | ||
83 | /* All CPUs start halted. CPU0 is unhalted from the machine level | |
84 | * reset code and the rest are explicitly started up by the guest | |
85 | * using an RTAS call */ | |
86 | cs->halted = 1; | |
87 | ||
88 | env->spr[SPR_HIOR] = 0; | |
89 | ||
e57ca75c DG |
90 | /* |
91 | * This is a hack for the benefit of KVM PR - it abuses the SDR1 | |
92 | * slot in kvm_sregs to communicate the userspace address of the | |
93 | * HPT | |
94 | */ | |
95 | if (kvm_enabled()) { | |
96 | env->spr[SPR_SDR1] = (target_ulong)(uintptr_t)spapr->htab | |
97 | | (spapr->htab_shift - 18); | |
98 | if (kvmppc_put_books_sregs(cpu) < 0) { | |
99 | error_report("Unable to update SDR1 in KVM"); | |
100 | exit(1); | |
101 | } | |
102 | } | |
afd10a0f BR |
103 | } |
104 | ||
6f4b5c3e BR |
105 | static void spapr_cpu_destroy(PowerPCCPU *cpu) |
106 | { | |
6f4b5c3e BR |
107 | qemu_unregister_reset(spapr_cpu_reset, cpu); |
108 | } | |
109 | ||
0c86d0fd DG |
110 | static void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu, |
111 | Error **errp) | |
afd10a0f BR |
112 | { |
113 | CPUPPCState *env = &cpu->env; | |
114 | ||
115 | /* Set time-base frequency to 512 MHz */ | |
116 | cpu_ppc_tb_init(env, SPAPR_TIMEBASE_FREQ); | |
117 | ||
118 | /* Enable PAPR mode in TCG or KVM */ | |
b7b0b1f1 | 119 | cpu_ppc_set_papr(cpu, PPC_VIRTUAL_HYPERVISOR(spapr)); |
afd10a0f | 120 | |
afd10a0f | 121 | qemu_register_reset(spapr_cpu_reset, cpu); |
af81cf32 | 122 | spapr_cpu_reset(cpu); |
afd10a0f | 123 | } |
3b542549 | 124 | |
94a94e4c BR |
125 | /* |
126 | * Return the sPAPR CPU core type for @model which essentially is the CPU | |
127 | * model specified with -cpu cmdline option. | |
128 | */ | |
129 | char *spapr_get_cpu_core_type(const char *model) | |
130 | { | |
131 | char *core_type; | |
132 | gchar **model_pieces = g_strsplit(model, ",", 2); | |
c5354f54 IM |
133 | gchar *cpu_model = g_ascii_strdown(model_pieces[0], -1); |
134 | g_strfreev(model_pieces); | |
94a94e4c | 135 | |
c5354f54 | 136 | core_type = g_strdup_printf("%s-" TYPE_SPAPR_CPU_CORE, cpu_model); |
4babfaf0 TH |
137 | |
138 | /* Check whether it exists or whether we have to look up an alias name */ | |
139 | if (!object_class_by_name(core_type)) { | |
140 | const char *realmodel; | |
141 | ||
142 | g_free(core_type); | |
e17a8779 | 143 | core_type = NULL; |
c5354f54 | 144 | realmodel = ppc_cpu_lookup_alias(cpu_model); |
4babfaf0 | 145 | if (realmodel) { |
e17a8779 | 146 | core_type = spapr_get_cpu_core_type(realmodel); |
4babfaf0 | 147 | } |
4babfaf0 | 148 | } |
c5354f54 | 149 | g_free(cpu_model); |
4babfaf0 | 150 | |
94a94e4c BR |
151 | return core_type; |
152 | } | |
153 | ||
f844616b | 154 | static void spapr_cpu_core_unrealizefn(DeviceState *dev, Error **errp) |
6f4b5c3e BR |
155 | { |
156 | sPAPRCPUCore *sc = SPAPR_CPU_CORE(OBJECT(dev)); | |
7ebaf795 BR |
157 | sPAPRCPUCoreClass *scc = SPAPR_CPU_CORE_GET_CLASS(OBJECT(dev)); |
158 | const char *typename = object_class_get_name(scc->cpu_class); | |
6f4b5c3e | 159 | size_t size = object_type_get_instance_size(typename); |
6f4b5c3e | 160 | CPUCore *cc = CPU_CORE(dev); |
6f4b5c3e BR |
161 | int i; |
162 | ||
163 | for (i = 0; i < cc->nr_threads; i++) { | |
164 | void *obj = sc->threads + i * size; | |
165 | DeviceState *dev = DEVICE(obj); | |
166 | CPUState *cs = CPU(dev); | |
167 | PowerPCCPU *cpu = POWERPC_CPU(cs); | |
168 | ||
169 | spapr_cpu_destroy(cpu); | |
8f37e54e | 170 | object_unparent(cpu->intc); |
6f4b5c3e BR |
171 | cpu_remove_sync(cs); |
172 | object_unparent(obj); | |
173 | } | |
8a1eb71b | 174 | g_free(sc->threads); |
6f4b5c3e BR |
175 | } |
176 | ||
7093645a | 177 | static void spapr_cpu_core_realize_child(Object *child, Error **errp) |
3b542549 | 178 | { |
7093645a | 179 | Error *local_err = NULL; |
3b542549 BR |
180 | sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); |
181 | CPUState *cs = CPU(child); | |
182 | PowerPCCPU *cpu = POWERPC_CPU(cs); | |
6595ab31 | 183 | Object *obj; |
5bc8d26d | 184 | |
9ed65663 | 185 | object_property_set_bool(child, true, "realized", &local_err); |
5bc8d26d | 186 | if (local_err) { |
c8a98293 | 187 | goto error; |
5bc8d26d | 188 | } |
3b542549 | 189 | |
9ed65663 | 190 | spapr_cpu_init(spapr, cpu, &local_err); |
f11235b9 | 191 | if (local_err) { |
c8a98293 | 192 | goto error; |
3b542549 BR |
193 | } |
194 | ||
9ed65663 GK |
195 | obj = object_new(spapr->icp_type); |
196 | object_property_add_child(child, "icp", obj, &error_abort); | |
197 | object_unref(obj); | |
198 | object_property_add_const_link(obj, ICP_PROP_XICS, OBJECT(spapr), | |
199 | &error_abort); | |
200 | object_property_add_const_link(obj, ICP_PROP_CPU, child, &error_abort); | |
201 | object_property_set_bool(obj, true, "realized", &local_err); | |
f11235b9 | 202 | if (local_err) { |
6595ab31 | 203 | goto free_icp; |
3b542549 | 204 | } |
5bc8d26d | 205 | |
c8a98293 GK |
206 | return; |
207 | ||
6595ab31 | 208 | free_icp: |
c8a98293 | 209 | object_unparent(obj); |
6595ab31 | 210 | error: |
c8a98293 | 211 | error_propagate(errp, local_err); |
3b542549 BR |
212 | } |
213 | ||
214 | static void spapr_cpu_core_realize(DeviceState *dev, Error **errp) | |
215 | { | |
7cca3e46 | 216 | sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); |
3b542549 | 217 | sPAPRCPUCore *sc = SPAPR_CPU_CORE(OBJECT(dev)); |
7ebaf795 | 218 | sPAPRCPUCoreClass *scc = SPAPR_CPU_CORE_GET_CLASS(OBJECT(dev)); |
3b542549 | 219 | CPUCore *cc = CPU_CORE(OBJECT(dev)); |
7ebaf795 | 220 | const char *typename = object_class_get_name(scc->cpu_class); |
3b542549 BR |
221 | size_t size = object_type_get_instance_size(typename); |
222 | Error *local_err = NULL; | |
7093645a BR |
223 | void *obj; |
224 | int i, j; | |
3b542549 | 225 | |
2363d5ee TH |
226 | if (!object_dynamic_cast(qdev_get_machine(), TYPE_SPAPR_MACHINE)) { |
227 | error_setg(errp, "spapr-cpu-core needs a pseries machine"); | |
228 | return; | |
229 | } | |
230 | ||
3b542549 BR |
231 | sc->threads = g_malloc0(size * cc->nr_threads); |
232 | for (i = 0; i < cc->nr_threads; i++) { | |
233 | char id[32]; | |
b63578bd | 234 | CPUState *cs; |
15f8b142 | 235 | PowerPCCPU *cpu; |
b63578bd | 236 | |
7093645a | 237 | obj = sc->threads + i * size; |
3b542549 BR |
238 | |
239 | object_initialize(obj, size, typename); | |
b63578bd | 240 | cs = CPU(obj); |
15f8b142 | 241 | cpu = POWERPC_CPU(cs); |
b63578bd | 242 | cs->cpu_index = cc->core_id + i; |
7cca3e46 SB |
243 | cpu->vcpu_id = (cc->core_id * spapr->vsmt / smp_threads) + i; |
244 | if (kvm_enabled() && !kvm_vcpu_id_is_valid(cpu->vcpu_id)) { | |
245 | error_setg(&local_err, "Can't create CPU with id %d in KVM", | |
246 | cpu->vcpu_id); | |
247 | error_append_hint(&local_err, "Adjust the number of cpus to %d " | |
248 | "or try to raise the number of threads per core\n", | |
249 | cpu->vcpu_id * smp_threads / spapr->vsmt); | |
250 | goto err; | |
251 | } | |
252 | ||
17b7c39e | 253 | |
722387e7 | 254 | /* Set NUMA node for the threads belonged to core */ |
15f8b142 | 255 | cpu->node_id = sc->node_id; |
17b7c39e | 256 | |
3b542549 BR |
257 | snprintf(id, sizeof(id), "thread[%d]", i); |
258 | object_property_add_child(OBJECT(sc), id, obj, &local_err); | |
259 | if (local_err) { | |
260 | goto err; | |
261 | } | |
8e758dee | 262 | object_unref(obj); |
3b542549 | 263 | } |
7093645a BR |
264 | |
265 | for (j = 0; j < cc->nr_threads; j++) { | |
266 | obj = sc->threads + j * size; | |
267 | ||
268 | spapr_cpu_core_realize_child(obj, &local_err); | |
269 | if (local_err) { | |
270 | goto err; | |
271 | } | |
3b542549 | 272 | } |
7093645a | 273 | return; |
3b542549 BR |
274 | |
275 | err: | |
dde35bc9 | 276 | while (--i >= 0) { |
3b542549 BR |
277 | obj = sc->threads + i * size; |
278 | object_unparent(obj); | |
3b542549 BR |
279 | } |
280 | g_free(sc->threads); | |
281 | error_propagate(errp, local_err); | |
282 | } | |
283 | ||
7ebaf795 | 284 | static const char *spapr_core_models[] = { |
4babfaf0 | 285 | /* 970 */ |
7ebaf795 | 286 | "970_v2.2", |
ff461b8d | 287 | |
4babfaf0 | 288 | /* 970MP variants */ |
7ebaf795 | 289 | "970mp_v1.0", |
7ebaf795 | 290 | "970mp_v1.1", |
470f2157 | 291 | |
4babfaf0 | 292 | /* POWER5+ */ |
c5354f54 | 293 | "power5+_v2.1", |
ff461b8d | 294 | |
4babfaf0 | 295 | /* POWER7 */ |
c5354f54 | 296 | "power7_v2.3", |
3b542549 | 297 | |
4babfaf0 | 298 | /* POWER7+ */ |
c5354f54 | 299 | "power7+_v2.1", |
3b542549 | 300 | |
4babfaf0 | 301 | /* POWER8 */ |
c5354f54 | 302 | "power8_v2.0", |
3b542549 | 303 | |
4babfaf0 | 304 | /* POWER8E */ |
c5354f54 | 305 | "power8e_v2.1", |
3b542549 | 306 | |
4babfaf0 | 307 | /* POWER8NVL */ |
c5354f54 | 308 | "power8nvl_v1.0", |
24d8e565 SJS |
309 | |
310 | /* POWER9 */ | |
c5354f54 | 311 | "power9_v1.0", |
3b542549 BR |
312 | }; |
313 | ||
0b8497f0 IM |
314 | static Property spapr_cpu_core_properties[] = { |
315 | DEFINE_PROP_INT32("node-id", sPAPRCPUCore, node_id, CPU_UNSET_NUMA_NODE_ID), | |
316 | DEFINE_PROP_END_OF_LIST() | |
317 | }; | |
318 | ||
7ebaf795 | 319 | void spapr_cpu_core_class_init(ObjectClass *oc, void *data) |
3b542549 | 320 | { |
7ebaf795 BR |
321 | DeviceClass *dc = DEVICE_CLASS(oc); |
322 | sPAPRCPUCoreClass *scc = SPAPR_CPU_CORE_CLASS(oc); | |
323 | ||
324 | dc->realize = spapr_cpu_core_realize; | |
f844616b | 325 | dc->unrealize = spapr_cpu_core_unrealizefn; |
0b8497f0 | 326 | dc->props = spapr_cpu_core_properties; |
7ebaf795 BR |
327 | scc->cpu_class = cpu_class_by_name(TYPE_POWERPC_CPU, data); |
328 | g_assert(scc->cpu_class); | |
3b542549 BR |
329 | } |
330 | ||
331 | static const TypeInfo spapr_cpu_core_type_info = { | |
332 | .name = TYPE_SPAPR_CPU_CORE, | |
333 | .parent = TYPE_CPU_CORE, | |
334 | .abstract = true, | |
335 | .instance_size = sizeof(sPAPRCPUCore), | |
7ebaf795 | 336 | .class_size = sizeof(sPAPRCPUCoreClass), |
3b542549 BR |
337 | }; |
338 | ||
339 | static void spapr_cpu_core_register_types(void) | |
340 | { | |
7ebaf795 | 341 | int i; |
3b542549 BR |
342 | |
343 | type_register_static(&spapr_cpu_core_type_info); | |
7ebaf795 BR |
344 | |
345 | for (i = 0; i < ARRAY_SIZE(spapr_core_models); i++) { | |
346 | TypeInfo type_info = { | |
347 | .parent = TYPE_SPAPR_CPU_CORE, | |
348 | .instance_size = sizeof(sPAPRCPUCore), | |
349 | .class_init = spapr_cpu_core_class_init, | |
350 | .class_data = (void *) spapr_core_models[i], | |
351 | }; | |
352 | ||
353 | type_info.name = g_strdup_printf("%s-" TYPE_SPAPR_CPU_CORE, | |
354 | spapr_core_models[i]); | |
355 | type_register(&type_info); | |
356 | g_free((void *)type_info.name); | |
3b542549 BR |
357 | } |
358 | } | |
359 | ||
360 | type_init(spapr_cpu_core_register_types) |