]>
Commit | Line | Data |
---|---|---|
39ac8455 DG |
1 | /* |
2 | * QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator | |
3 | * | |
4 | * Hypercall based emulated RTAS | |
5 | * | |
6 | * Copyright (c) 2010-2011 David Gibson, IBM Corporation. | |
7 | * | |
8 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
9 | * of this software and associated documentation files (the "Software"), to deal | |
10 | * in the Software without restriction, including without limitation the rights | |
11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
12 | * copies of the Software, and to permit persons to whom the Software is | |
13 | * furnished to do so, subject to the following conditions: | |
14 | * | |
15 | * The above copyright notice and this permission notice shall be included in | |
16 | * all copies or substantial portions of the Software. | |
17 | * | |
18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
21 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
24 | * THE SOFTWARE. | |
25 | * | |
26 | */ | |
27 | #include "cpu.h" | |
9c17d615 | 28 | #include "sysemu/sysemu.h" |
dccfcd0e | 29 | #include "sysemu/char.h" |
39ac8455 | 30 | #include "hw/qdev.h" |
9c17d615 | 31 | #include "sysemu/device_tree.h" |
db4ef288 | 32 | #include "sysemu/cpus.h" |
39ac8455 | 33 | |
0d09e41a PB |
34 | #include "hw/ppc/spapr.h" |
35 | #include "hw/ppc/spapr_vio.h" | |
e010ad8f | 36 | #include "qapi-event.h" |
e3943228 | 37 | #include "hw/boards.h" |
39ac8455 DG |
38 | |
39 | #include <libfdt.h> | |
8c8639df MD |
40 | #include "hw/ppc/spapr_drc.h" |
41 | ||
42 | /* #define DEBUG_SPAPR */ | |
43 | ||
44 | #ifdef DEBUG_SPAPR | |
45 | #define DPRINTF(fmt, ...) \ | |
46 | do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0) | |
47 | #else | |
48 | #define DPRINTF(fmt, ...) \ | |
49 | do { } while (0) | |
50 | #endif | |
51 | ||
28e02042 | 52 | static sPAPRConfigureConnectorState *spapr_ccs_find(sPAPRMachineState *spapr, |
46503c2b MR |
53 | uint32_t drc_index) |
54 | { | |
55 | sPAPRConfigureConnectorState *ccs = NULL; | |
56 | ||
57 | QTAILQ_FOREACH(ccs, &spapr->ccs_list, next) { | |
58 | if (ccs->drc_index == drc_index) { | |
59 | break; | |
60 | } | |
61 | } | |
62 | ||
63 | return ccs; | |
64 | } | |
65 | ||
28e02042 | 66 | static void spapr_ccs_add(sPAPRMachineState *spapr, |
46503c2b MR |
67 | sPAPRConfigureConnectorState *ccs) |
68 | { | |
69 | g_assert(!spapr_ccs_find(spapr, ccs->drc_index)); | |
70 | QTAILQ_INSERT_HEAD(&spapr->ccs_list, ccs, next); | |
71 | } | |
72 | ||
28e02042 | 73 | static void spapr_ccs_remove(sPAPRMachineState *spapr, |
46503c2b MR |
74 | sPAPRConfigureConnectorState *ccs) |
75 | { | |
76 | QTAILQ_REMOVE(&spapr->ccs_list, ccs, next); | |
77 | g_free(ccs); | |
78 | } | |
79 | ||
80 | void spapr_ccs_reset_hook(void *opaque) | |
81 | { | |
28e02042 | 82 | sPAPRMachineState *spapr = opaque; |
46503c2b MR |
83 | sPAPRConfigureConnectorState *ccs, *ccs_tmp; |
84 | ||
85 | QTAILQ_FOREACH_SAFE(ccs, &spapr->ccs_list, next, ccs_tmp) { | |
86 | spapr_ccs_remove(spapr, ccs); | |
87 | } | |
88 | } | |
39ac8455 | 89 | |
28e02042 | 90 | static void rtas_display_character(PowerPCCPU *cpu, sPAPRMachineState *spapr, |
821303f5 DG |
91 | uint32_t token, uint32_t nargs, |
92 | target_ulong args, | |
93 | uint32_t nret, target_ulong rets) | |
94 | { | |
95 | uint8_t c = rtas_ld(args, 0); | |
5f2e2ba2 | 96 | VIOsPAPRDevice *sdev = vty_lookup(spapr, 0); |
821303f5 DG |
97 | |
98 | if (!sdev) { | |
a64d325d | 99 | rtas_st(rets, 0, RTAS_OUT_HW_ERROR); |
821303f5 DG |
100 | } else { |
101 | vty_putchars(sdev, &c, sizeof(c)); | |
a64d325d | 102 | rtas_st(rets, 0, RTAS_OUT_SUCCESS); |
821303f5 DG |
103 | } |
104 | } | |
105 | ||
28e02042 | 106 | static void rtas_power_off(PowerPCCPU *cpu, sPAPRMachineState *spapr, |
821303f5 DG |
107 | uint32_t token, uint32_t nargs, target_ulong args, |
108 | uint32_t nret, target_ulong rets) | |
109 | { | |
110 | if (nargs != 2 || nret != 1) { | |
a64d325d | 111 | rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); |
821303f5 DG |
112 | return; |
113 | } | |
114 | qemu_system_shutdown_request(); | |
a64d325d | 115 | rtas_st(rets, 0, RTAS_OUT_SUCCESS); |
821303f5 DG |
116 | } |
117 | ||
28e02042 | 118 | static void rtas_system_reboot(PowerPCCPU *cpu, sPAPRMachineState *spapr, |
c821a43c DG |
119 | uint32_t token, uint32_t nargs, |
120 | target_ulong args, | |
121 | uint32_t nret, target_ulong rets) | |
122 | { | |
123 | if (nargs != 0 || nret != 1) { | |
a64d325d | 124 | rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); |
c821a43c DG |
125 | return; |
126 | } | |
127 | qemu_system_reset_request(); | |
a64d325d | 128 | rtas_st(rets, 0, RTAS_OUT_SUCCESS); |
c821a43c DG |
129 | } |
130 | ||
210b580b | 131 | static void rtas_query_cpu_stopped_state(PowerPCCPU *cpu_, |
28e02042 | 132 | sPAPRMachineState *spapr, |
a9f8ad8f DG |
133 | uint32_t token, uint32_t nargs, |
134 | target_ulong args, | |
135 | uint32_t nret, target_ulong rets) | |
136 | { | |
137 | target_ulong id; | |
0f20ba62 | 138 | PowerPCCPU *cpu; |
a9f8ad8f DG |
139 | |
140 | if (nargs != 1 || nret != 2) { | |
a64d325d | 141 | rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); |
a9f8ad8f DG |
142 | return; |
143 | } | |
144 | ||
145 | id = rtas_ld(args, 0); | |
0f20ba62 | 146 | cpu = ppc_get_vcpu_by_dt_id(id); |
05318a85 | 147 | if (cpu != NULL) { |
0f20ba62 | 148 | if (CPU(cpu)->halted) { |
a9f8ad8f DG |
149 | rtas_st(rets, 1, 0); |
150 | } else { | |
151 | rtas_st(rets, 1, 2); | |
152 | } | |
153 | ||
a64d325d | 154 | rtas_st(rets, 0, RTAS_OUT_SUCCESS); |
a9f8ad8f DG |
155 | return; |
156 | } | |
157 | ||
158 | /* Didn't find a matching cpu */ | |
a64d325d | 159 | rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); |
a9f8ad8f DG |
160 | } |
161 | ||
28e02042 | 162 | static void rtas_start_cpu(PowerPCCPU *cpu_, sPAPRMachineState *spapr, |
a9f8ad8f DG |
163 | uint32_t token, uint32_t nargs, |
164 | target_ulong args, | |
165 | uint32_t nret, target_ulong rets) | |
166 | { | |
167 | target_ulong id, start, r3; | |
0f20ba62 | 168 | PowerPCCPU *cpu; |
a9f8ad8f DG |
169 | |
170 | if (nargs != 3 || nret != 1) { | |
a64d325d | 171 | rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); |
a9f8ad8f DG |
172 | return; |
173 | } | |
174 | ||
175 | id = rtas_ld(args, 0); | |
176 | start = rtas_ld(args, 1); | |
177 | r3 = rtas_ld(args, 2); | |
178 | ||
0f20ba62 AK |
179 | cpu = ppc_get_vcpu_by_dt_id(id); |
180 | if (cpu != NULL) { | |
181 | CPUState *cs = CPU(cpu); | |
c67e216b | 182 | CPUPPCState *env = &cpu->env; |
a9f8ad8f | 183 | |
c67e216b | 184 | if (!cs->halted) { |
a64d325d | 185 | rtas_st(rets, 0, RTAS_OUT_HW_ERROR); |
a9f8ad8f DG |
186 | return; |
187 | } | |
188 | ||
048706d9 DG |
189 | /* This will make sure qemu state is up to date with kvm, and |
190 | * mark it dirty so our changes get flushed back before the | |
191 | * new cpu enters */ | |
dd1750d7 | 192 | kvm_cpu_synchronize_state(cs); |
048706d9 | 193 | |
a9f8ad8f DG |
194 | env->msr = (1ULL << MSR_SF) | (1ULL << MSR_ME); |
195 | env->nip = start; | |
196 | env->gpr[3] = r3; | |
c67e216b | 197 | cs->halted = 0; |
a9f8ad8f | 198 | |
c67e216b | 199 | qemu_cpu_kick(cs); |
a9f8ad8f | 200 | |
a64d325d | 201 | rtas_st(rets, 0, RTAS_OUT_SUCCESS); |
a9f8ad8f DG |
202 | return; |
203 | } | |
204 | ||
205 | /* Didn't find a matching cpu */ | |
a64d325d | 206 | rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); |
a9f8ad8f DG |
207 | } |
208 | ||
28e02042 | 209 | static void rtas_stop_self(PowerPCCPU *cpu, sPAPRMachineState *spapr, |
59760f2d AK |
210 | uint32_t token, uint32_t nargs, |
211 | target_ulong args, | |
212 | uint32_t nret, target_ulong rets) | |
213 | { | |
214 | CPUState *cs = CPU(cpu); | |
215 | CPUPPCState *env = &cpu->env; | |
216 | ||
217 | cs->halted = 1; | |
9102deda | 218 | qemu_cpu_kick(cs); |
59760f2d AK |
219 | /* |
220 | * While stopping a CPU, the guest calls H_CPPR which | |
221 | * effectively disables interrupts on XICS level. | |
222 | * However decrementer interrupts in TCG can still | |
223 | * wake the CPU up so here we disable interrupts in MSR | |
224 | * as well. | |
225 | * As rtas_start_cpu() resets the whole MSR anyway, there is | |
226 | * no need to bother with specific bits, we just clear it. | |
227 | */ | |
228 | env->msr = 0; | |
229 | } | |
230 | ||
3ada6b11 | 231 | static void rtas_ibm_get_system_parameter(PowerPCCPU *cpu, |
28e02042 | 232 | sPAPRMachineState *spapr, |
3ada6b11 AK |
233 | uint32_t token, uint32_t nargs, |
234 | target_ulong args, | |
235 | uint32_t nret, target_ulong rets) | |
236 | { | |
237 | target_ulong parameter = rtas_ld(args, 0); | |
238 | target_ulong buffer = rtas_ld(args, 1); | |
239 | target_ulong length = rtas_ld(args, 2); | |
3052d951 | 240 | target_ulong ret = RTAS_OUT_SUCCESS; |
3ada6b11 AK |
241 | |
242 | switch (parameter) { | |
3b50d897 | 243 | case RTAS_SYSPARM_SPLPAR_CHARACTERISTICS: { |
e3943228 SB |
244 | char *param_val = g_strdup_printf("MaxEntCap=%d," |
245 | "DesMem=%llu," | |
246 | "DesProcs=%d," | |
247 | "MaxPlatProcs=%d", | |
248 | max_cpus, | |
249 | current_machine->ram_size / M_BYTE, | |
250 | smp_cpus, | |
251 | max_cpus); | |
3b50d897 S |
252 | rtas_st_buffer(buffer, length, (uint8_t *)param_val, strlen(param_val)); |
253 | g_free(param_val); | |
254 | break; | |
255 | } | |
3052d951 S |
256 | case RTAS_SYSPARM_DIAGNOSTICS_RUN_MODE: { |
257 | uint8_t param_val = DIAGNOSTICS_RUN_MODE_DISABLED; | |
258 | ||
259 | rtas_st_buffer(buffer, length, ¶m_val, sizeof(param_val)); | |
3ada6b11 AK |
260 | break; |
261 | } | |
b907d7b0 S |
262 | case RTAS_SYSPARM_UUID: |
263 | rtas_st_buffer(buffer, length, qemu_uuid, (qemu_uuid_set ? 16 : 0)); | |
264 | break; | |
3052d951 S |
265 | default: |
266 | ret = RTAS_OUT_NOT_SUPPORTED; | |
267 | } | |
3ada6b11 AK |
268 | |
269 | rtas_st(rets, 0, ret); | |
270 | } | |
271 | ||
272 | static void rtas_ibm_set_system_parameter(PowerPCCPU *cpu, | |
28e02042 | 273 | sPAPRMachineState *spapr, |
3ada6b11 AK |
274 | uint32_t token, uint32_t nargs, |
275 | target_ulong args, | |
276 | uint32_t nret, target_ulong rets) | |
277 | { | |
278 | target_ulong parameter = rtas_ld(args, 0); | |
279 | target_ulong ret = RTAS_OUT_NOT_SUPPORTED; | |
280 | ||
281 | switch (parameter) { | |
3b50d897 | 282 | case RTAS_SYSPARM_SPLPAR_CHARACTERISTICS: |
3052d951 | 283 | case RTAS_SYSPARM_DIAGNOSTICS_RUN_MODE: |
b907d7b0 | 284 | case RTAS_SYSPARM_UUID: |
3ada6b11 AK |
285 | ret = RTAS_OUT_NOT_AUTHORIZED; |
286 | break; | |
287 | } | |
288 | ||
289 | rtas_st(rets, 0, ret); | |
290 | } | |
291 | ||
2e14072f | 292 | static void rtas_ibm_os_term(PowerPCCPU *cpu, |
28e02042 | 293 | sPAPRMachineState *spapr, |
2e14072f ND |
294 | uint32_t token, uint32_t nargs, |
295 | target_ulong args, | |
296 | uint32_t nret, target_ulong rets) | |
297 | { | |
298 | target_ulong ret = 0; | |
299 | ||
300 | qapi_event_send_guest_panicked(GUEST_PANIC_ACTION_PAUSE, &error_abort); | |
301 | ||
302 | rtas_st(rets, 0, ret); | |
303 | } | |
304 | ||
28e02042 | 305 | static void rtas_set_power_level(PowerPCCPU *cpu, sPAPRMachineState *spapr, |
094d2058 NF |
306 | uint32_t token, uint32_t nargs, |
307 | target_ulong args, uint32_t nret, | |
308 | target_ulong rets) | |
309 | { | |
310 | int32_t power_domain; | |
311 | ||
312 | if (nargs != 2 || nret != 2) { | |
313 | rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); | |
314 | return; | |
315 | } | |
316 | ||
317 | /* we currently only use a single, "live insert" powerdomain for | |
318 | * hotplugged/dlpar'd resources, so the power is always live/full (100) | |
319 | */ | |
320 | power_domain = rtas_ld(args, 0); | |
321 | if (power_domain != -1) { | |
322 | rtas_st(rets, 0, RTAS_OUT_NOT_SUPPORTED); | |
323 | return; | |
324 | } | |
325 | ||
326 | rtas_st(rets, 0, RTAS_OUT_SUCCESS); | |
327 | rtas_st(rets, 1, 100); | |
328 | } | |
329 | ||
28e02042 | 330 | static void rtas_get_power_level(PowerPCCPU *cpu, sPAPRMachineState *spapr, |
094d2058 NF |
331 | uint32_t token, uint32_t nargs, |
332 | target_ulong args, uint32_t nret, | |
333 | target_ulong rets) | |
334 | { | |
335 | int32_t power_domain; | |
336 | ||
337 | if (nargs != 1 || nret != 2) { | |
338 | rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); | |
339 | return; | |
340 | } | |
341 | ||
342 | /* we currently only use a single, "live insert" powerdomain for | |
343 | * hotplugged/dlpar'd resources, so the power is always live/full (100) | |
344 | */ | |
345 | power_domain = rtas_ld(args, 0); | |
346 | if (power_domain != -1) { | |
347 | rtas_st(rets, 0, RTAS_OUT_NOT_SUPPORTED); | |
348 | return; | |
349 | } | |
350 | ||
351 | rtas_st(rets, 0, RTAS_OUT_SUCCESS); | |
352 | rtas_st(rets, 1, 100); | |
353 | } | |
354 | ||
8c8639df MD |
355 | static bool sensor_type_is_dr(uint32_t sensor_type) |
356 | { | |
357 | switch (sensor_type) { | |
358 | case RTAS_SENSOR_TYPE_ISOLATION_STATE: | |
359 | case RTAS_SENSOR_TYPE_DR: | |
360 | case RTAS_SENSOR_TYPE_ALLOCATION_STATE: | |
361 | return true; | |
362 | } | |
363 | ||
364 | return false; | |
365 | } | |
366 | ||
28e02042 | 367 | static void rtas_set_indicator(PowerPCCPU *cpu, sPAPRMachineState *spapr, |
8c8639df MD |
368 | uint32_t token, uint32_t nargs, |
369 | target_ulong args, uint32_t nret, | |
370 | target_ulong rets) | |
371 | { | |
372 | uint32_t sensor_type; | |
373 | uint32_t sensor_index; | |
374 | uint32_t sensor_state; | |
0cb688d2 | 375 | uint32_t ret = RTAS_OUT_SUCCESS; |
8c8639df MD |
376 | sPAPRDRConnector *drc; |
377 | sPAPRDRConnectorClass *drck; | |
378 | ||
379 | if (nargs != 3 || nret != 1) { | |
0cb688d2 MR |
380 | ret = RTAS_OUT_PARAM_ERROR; |
381 | goto out; | |
8c8639df MD |
382 | } |
383 | ||
384 | sensor_type = rtas_ld(args, 0); | |
385 | sensor_index = rtas_ld(args, 1); | |
386 | sensor_state = rtas_ld(args, 2); | |
387 | ||
388 | if (!sensor_type_is_dr(sensor_type)) { | |
389 | goto out_unimplemented; | |
390 | } | |
391 | ||
392 | /* if this is a DR sensor we can assume sensor_index == drc_index */ | |
393 | drc = spapr_dr_connector_by_index(sensor_index); | |
394 | if (!drc) { | |
395 | DPRINTF("rtas_set_indicator: invalid sensor/DRC index: %xh\n", | |
396 | sensor_index); | |
0cb688d2 MR |
397 | ret = RTAS_OUT_PARAM_ERROR; |
398 | goto out; | |
8c8639df MD |
399 | } |
400 | drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); | |
401 | ||
402 | switch (sensor_type) { | |
403 | case RTAS_SENSOR_TYPE_ISOLATION_STATE: | |
46503c2b MR |
404 | /* if the guest is configuring a device attached to this |
405 | * DRC, we should reset the configuration state at this | |
406 | * point since it may no longer be reliable (guest released | |
407 | * device and needs to start over, or unplug occurred so | |
408 | * the FDT is no longer valid) | |
409 | */ | |
410 | if (sensor_state == SPAPR_DR_ISOLATION_STATE_ISOLATED) { | |
411 | sPAPRConfigureConnectorState *ccs = spapr_ccs_find(spapr, | |
412 | sensor_index); | |
413 | if (ccs) { | |
414 | spapr_ccs_remove(spapr, ccs); | |
415 | } | |
416 | } | |
0cb688d2 | 417 | ret = drck->set_isolation_state(drc, sensor_state); |
8c8639df MD |
418 | break; |
419 | case RTAS_SENSOR_TYPE_DR: | |
0cb688d2 | 420 | ret = drck->set_indicator_state(drc, sensor_state); |
8c8639df MD |
421 | break; |
422 | case RTAS_SENSOR_TYPE_ALLOCATION_STATE: | |
0cb688d2 | 423 | ret = drck->set_allocation_state(drc, sensor_state); |
8c8639df MD |
424 | break; |
425 | default: | |
426 | goto out_unimplemented; | |
427 | } | |
428 | ||
0cb688d2 MR |
429 | out: |
430 | rtas_st(rets, 0, ret); | |
8c8639df MD |
431 | return; |
432 | ||
433 | out_unimplemented: | |
434 | /* currently only DR-related sensors are implemented */ | |
435 | DPRINTF("rtas_set_indicator: sensor/indicator not implemented: %d\n", | |
436 | sensor_type); | |
437 | rtas_st(rets, 0, RTAS_OUT_NOT_SUPPORTED); | |
438 | } | |
439 | ||
28e02042 | 440 | static void rtas_get_sensor_state(PowerPCCPU *cpu, sPAPRMachineState *spapr, |
886445a6 MD |
441 | uint32_t token, uint32_t nargs, |
442 | target_ulong args, uint32_t nret, | |
443 | target_ulong rets) | |
444 | { | |
445 | uint32_t sensor_type; | |
446 | uint32_t sensor_index; | |
0cb688d2 | 447 | uint32_t sensor_state = 0; |
886445a6 MD |
448 | sPAPRDRConnector *drc; |
449 | sPAPRDRConnectorClass *drck; | |
0cb688d2 | 450 | uint32_t ret = RTAS_OUT_SUCCESS; |
886445a6 MD |
451 | |
452 | if (nargs != 2 || nret != 2) { | |
0cb688d2 MR |
453 | ret = RTAS_OUT_PARAM_ERROR; |
454 | goto out; | |
886445a6 MD |
455 | } |
456 | ||
457 | sensor_type = rtas_ld(args, 0); | |
458 | sensor_index = rtas_ld(args, 1); | |
459 | ||
460 | if (sensor_type != RTAS_SENSOR_TYPE_ENTITY_SENSE) { | |
461 | /* currently only DR-related sensors are implemented */ | |
462 | DPRINTF("rtas_get_sensor_state: sensor/indicator not implemented: %d\n", | |
463 | sensor_type); | |
0cb688d2 MR |
464 | ret = RTAS_OUT_NOT_SUPPORTED; |
465 | goto out; | |
886445a6 MD |
466 | } |
467 | ||
468 | drc = spapr_dr_connector_by_index(sensor_index); | |
469 | if (!drc) { | |
470 | DPRINTF("rtas_get_sensor_state: invalid sensor/DRC index: %xh\n", | |
471 | sensor_index); | |
0cb688d2 MR |
472 | ret = RTAS_OUT_PARAM_ERROR; |
473 | goto out; | |
886445a6 MD |
474 | } |
475 | drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); | |
0cb688d2 | 476 | ret = drck->entity_sense(drc, &sensor_state); |
886445a6 | 477 | |
0cb688d2 MR |
478 | out: |
479 | rtas_st(rets, 0, ret); | |
480 | rtas_st(rets, 1, sensor_state); | |
886445a6 MD |
481 | } |
482 | ||
46503c2b MR |
483 | /* configure-connector work area offsets, int32_t units for field |
484 | * indexes, bytes for field offset/len values. | |
485 | * | |
486 | * as documented by PAPR+ v2.7, 13.5.3.5 | |
487 | */ | |
488 | #define CC_IDX_NODE_NAME_OFFSET 2 | |
489 | #define CC_IDX_PROP_NAME_OFFSET 2 | |
490 | #define CC_IDX_PROP_LEN 3 | |
491 | #define CC_IDX_PROP_DATA_OFFSET 4 | |
492 | #define CC_VAL_DATA_OFFSET ((CC_IDX_PROP_DATA_OFFSET + 1) * 4) | |
493 | #define CC_WA_LEN 4096 | |
494 | ||
495 | static void rtas_ibm_configure_connector(PowerPCCPU *cpu, | |
28e02042 | 496 | sPAPRMachineState *spapr, |
46503c2b MR |
497 | uint32_t token, uint32_t nargs, |
498 | target_ulong args, uint32_t nret, | |
499 | target_ulong rets) | |
500 | { | |
501 | uint64_t wa_addr; | |
502 | uint64_t wa_offset; | |
503 | uint32_t drc_index; | |
504 | sPAPRDRConnector *drc; | |
505 | sPAPRDRConnectorClass *drck; | |
506 | sPAPRConfigureConnectorState *ccs; | |
507 | sPAPRDRCCResponse resp = SPAPR_DR_CC_RESPONSE_CONTINUE; | |
508 | int rc; | |
509 | const void *fdt; | |
510 | ||
511 | if (nargs != 2 || nret != 1) { | |
512 | rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); | |
513 | return; | |
514 | } | |
515 | ||
516 | wa_addr = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 0); | |
517 | ||
518 | drc_index = rtas_ld(wa_addr, 0); | |
519 | drc = spapr_dr_connector_by_index(drc_index); | |
520 | if (!drc) { | |
521 | DPRINTF("rtas_ibm_configure_connector: invalid DRC index: %xh\n", | |
522 | drc_index); | |
523 | rc = RTAS_OUT_PARAM_ERROR; | |
524 | goto out; | |
525 | } | |
526 | ||
527 | drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); | |
528 | fdt = drck->get_fdt(drc, NULL); | |
e6fc9568 BR |
529 | if (!fdt) { |
530 | DPRINTF("rtas_ibm_configure_connector: Missing FDT for DRC index: %xh\n", | |
531 | drc_index); | |
532 | rc = SPAPR_DR_CC_RESPONSE_NOT_CONFIGURABLE; | |
533 | goto out; | |
534 | } | |
46503c2b MR |
535 | |
536 | ccs = spapr_ccs_find(spapr, drc_index); | |
537 | if (!ccs) { | |
538 | ccs = g_new0(sPAPRConfigureConnectorState, 1); | |
539 | (void)drck->get_fdt(drc, &ccs->fdt_offset); | |
540 | ccs->drc_index = drc_index; | |
541 | spapr_ccs_add(spapr, ccs); | |
542 | } | |
543 | ||
544 | do { | |
545 | uint32_t tag; | |
546 | const char *name; | |
547 | const struct fdt_property *prop; | |
548 | int fdt_offset_next, prop_len; | |
549 | ||
550 | tag = fdt_next_tag(fdt, ccs->fdt_offset, &fdt_offset_next); | |
551 | ||
552 | switch (tag) { | |
553 | case FDT_BEGIN_NODE: | |
554 | ccs->fdt_depth++; | |
555 | name = fdt_get_name(fdt, ccs->fdt_offset, NULL); | |
556 | ||
557 | /* provide the name of the next OF node */ | |
558 | wa_offset = CC_VAL_DATA_OFFSET; | |
559 | rtas_st(wa_addr, CC_IDX_NODE_NAME_OFFSET, wa_offset); | |
560 | rtas_st_buffer_direct(wa_addr + wa_offset, CC_WA_LEN - wa_offset, | |
561 | (uint8_t *)name, strlen(name) + 1); | |
562 | resp = SPAPR_DR_CC_RESPONSE_NEXT_CHILD; | |
563 | break; | |
564 | case FDT_END_NODE: | |
565 | ccs->fdt_depth--; | |
566 | if (ccs->fdt_depth == 0) { | |
567 | /* done sending the device tree, don't need to track | |
568 | * the state anymore | |
569 | */ | |
570 | drck->set_configured(drc); | |
571 | spapr_ccs_remove(spapr, ccs); | |
572 | ccs = NULL; | |
573 | resp = SPAPR_DR_CC_RESPONSE_SUCCESS; | |
574 | } else { | |
575 | resp = SPAPR_DR_CC_RESPONSE_PREV_PARENT; | |
576 | } | |
577 | break; | |
578 | case FDT_PROP: | |
579 | prop = fdt_get_property_by_offset(fdt, ccs->fdt_offset, | |
580 | &prop_len); | |
581 | name = fdt_string(fdt, fdt32_to_cpu(prop->nameoff)); | |
582 | ||
583 | /* provide the name of the next OF property */ | |
584 | wa_offset = CC_VAL_DATA_OFFSET; | |
585 | rtas_st(wa_addr, CC_IDX_PROP_NAME_OFFSET, wa_offset); | |
586 | rtas_st_buffer_direct(wa_addr + wa_offset, CC_WA_LEN - wa_offset, | |
587 | (uint8_t *)name, strlen(name) + 1); | |
588 | ||
589 | /* provide the length and value of the OF property. data gets | |
590 | * placed immediately after NULL terminator of the OF property's | |
591 | * name string | |
592 | */ | |
593 | wa_offset += strlen(name) + 1, | |
594 | rtas_st(wa_addr, CC_IDX_PROP_LEN, prop_len); | |
595 | rtas_st(wa_addr, CC_IDX_PROP_DATA_OFFSET, wa_offset); | |
596 | rtas_st_buffer_direct(wa_addr + wa_offset, CC_WA_LEN - wa_offset, | |
597 | (uint8_t *)((struct fdt_property *)prop)->data, | |
598 | prop_len); | |
599 | resp = SPAPR_DR_CC_RESPONSE_NEXT_PROPERTY; | |
600 | break; | |
601 | case FDT_END: | |
602 | resp = SPAPR_DR_CC_RESPONSE_ERROR; | |
603 | default: | |
604 | /* keep seeking for an actionable tag */ | |
605 | break; | |
606 | } | |
607 | if (ccs) { | |
608 | ccs->fdt_offset = fdt_offset_next; | |
609 | } | |
610 | } while (resp == SPAPR_DR_CC_RESPONSE_CONTINUE); | |
611 | ||
612 | rc = resp; | |
613 | out: | |
614 | rtas_st(rets, 0, rc); | |
615 | } | |
616 | ||
39ac8455 DG |
617 | static struct rtas_call { |
618 | const char *name; | |
619 | spapr_rtas_fn fn; | |
3a3b8502 | 620 | } rtas_table[RTAS_TOKEN_MAX - RTAS_TOKEN_BASE]; |
39ac8455 | 621 | |
28e02042 | 622 | target_ulong spapr_rtas_call(PowerPCCPU *cpu, sPAPRMachineState *spapr, |
39ac8455 DG |
623 | uint32_t token, uint32_t nargs, target_ulong args, |
624 | uint32_t nret, target_ulong rets) | |
625 | { | |
3a3b8502 AK |
626 | if ((token >= RTAS_TOKEN_BASE) && (token < RTAS_TOKEN_MAX)) { |
627 | struct rtas_call *call = rtas_table + (token - RTAS_TOKEN_BASE); | |
39ac8455 DG |
628 | |
629 | if (call->fn) { | |
210b580b | 630 | call->fn(cpu, spapr, token, nargs, args, nret, rets); |
39ac8455 DG |
631 | return H_SUCCESS; |
632 | } | |
633 | } | |
634 | ||
821303f5 DG |
635 | /* HACK: Some Linux early debug code uses RTAS display-character, |
636 | * but assumes the token value is 0xa (which it is on some real | |
637 | * machines) without looking it up in the device tree. This | |
638 | * special case makes this work */ | |
639 | if (token == 0xa) { | |
210b580b | 640 | rtas_display_character(cpu, spapr, 0xa, nargs, args, nret, rets); |
821303f5 DG |
641 | return H_SUCCESS; |
642 | } | |
643 | ||
39ac8455 | 644 | hcall_dprintf("Unknown RTAS token 0x%x\n", token); |
a64d325d | 645 | rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); |
39ac8455 DG |
646 | return H_PARAMETER; |
647 | } | |
648 | ||
3a3b8502 | 649 | void spapr_rtas_register(int token, const char *name, spapr_rtas_fn fn) |
39ac8455 | 650 | { |
3a3b8502 AK |
651 | if (!((token >= RTAS_TOKEN_BASE) && (token < RTAS_TOKEN_MAX))) { |
652 | fprintf(stderr, "RTAS invalid token 0x%x\n", token); | |
653 | exit(1); | |
c89d5299 DG |
654 | } |
655 | ||
3a3b8502 AK |
656 | token -= RTAS_TOKEN_BASE; |
657 | if (rtas_table[token].name) { | |
658 | fprintf(stderr, "RTAS call \"%s\" is registered already as 0x%x\n", | |
659 | rtas_table[token].name, token); | |
660 | exit(1); | |
661 | } | |
39ac8455 | 662 | |
3a3b8502 AK |
663 | rtas_table[token].name = name; |
664 | rtas_table[token].fn = fn; | |
39ac8455 DG |
665 | } |
666 | ||
a8170e5e AK |
667 | int spapr_rtas_device_tree_setup(void *fdt, hwaddr rtas_addr, |
668 | hwaddr rtas_size) | |
39ac8455 DG |
669 | { |
670 | int ret; | |
671 | int i; | |
db4ef288 BR |
672 | uint32_t lrdr_capacity[5]; |
673 | MachineState *machine = MACHINE(qdev_get_machine()); | |
39ac8455 DG |
674 | |
675 | ret = fdt_add_mem_rsv(fdt, rtas_addr, rtas_size); | |
676 | if (ret < 0) { | |
677 | fprintf(stderr, "Couldn't add RTAS reserve entry: %s\n", | |
678 | fdt_strerror(ret)); | |
679 | return ret; | |
680 | } | |
681 | ||
5a4348d1 PC |
682 | ret = qemu_fdt_setprop_cell(fdt, "/rtas", "linux,rtas-base", |
683 | rtas_addr); | |
39ac8455 DG |
684 | if (ret < 0) { |
685 | fprintf(stderr, "Couldn't add linux,rtas-base property: %s\n", | |
686 | fdt_strerror(ret)); | |
687 | return ret; | |
688 | } | |
689 | ||
5a4348d1 PC |
690 | ret = qemu_fdt_setprop_cell(fdt, "/rtas", "linux,rtas-entry", |
691 | rtas_addr); | |
39ac8455 DG |
692 | if (ret < 0) { |
693 | fprintf(stderr, "Couldn't add linux,rtas-entry property: %s\n", | |
694 | fdt_strerror(ret)); | |
695 | return ret; | |
696 | } | |
697 | ||
5a4348d1 PC |
698 | ret = qemu_fdt_setprop_cell(fdt, "/rtas", "rtas-size", |
699 | rtas_size); | |
39ac8455 DG |
700 | if (ret < 0) { |
701 | fprintf(stderr, "Couldn't add rtas-size property: %s\n", | |
702 | fdt_strerror(ret)); | |
703 | return ret; | |
704 | } | |
705 | ||
3a3b8502 | 706 | for (i = 0; i < RTAS_TOKEN_MAX - RTAS_TOKEN_BASE; i++) { |
39ac8455 DG |
707 | struct rtas_call *call = &rtas_table[i]; |
708 | ||
d36b66f7 | 709 | if (!call->name) { |
39ac8455 DG |
710 | continue; |
711 | } | |
712 | ||
5a4348d1 | 713 | ret = qemu_fdt_setprop_cell(fdt, "/rtas", call->name, |
3a3b8502 | 714 | i + RTAS_TOKEN_BASE); |
39ac8455 DG |
715 | if (ret < 0) { |
716 | fprintf(stderr, "Couldn't add rtas token for %s: %s\n", | |
717 | call->name, fdt_strerror(ret)); | |
718 | return ret; | |
719 | } | |
720 | ||
721 | } | |
db4ef288 BR |
722 | |
723 | lrdr_capacity[0] = cpu_to_be32(((uint64_t)machine->maxram_size) >> 32); | |
724 | lrdr_capacity[1] = cpu_to_be32(machine->maxram_size & 0xffffffff); | |
725 | lrdr_capacity[2] = 0; | |
726 | lrdr_capacity[3] = cpu_to_be32(SPAPR_MEMORY_BLOCK_SIZE); | |
727 | lrdr_capacity[4] = cpu_to_be32(max_cpus/smp_threads); | |
728 | ret = qemu_fdt_setprop(fdt, "/rtas", "ibm,lrdr-capacity", lrdr_capacity, | |
729 | sizeof(lrdr_capacity)); | |
730 | if (ret < 0) { | |
731 | fprintf(stderr, "Couldn't add ibm,lrdr-capacity rtas property\n"); | |
732 | return ret; | |
733 | } | |
734 | ||
39ac8455 DG |
735 | return 0; |
736 | } | |
821303f5 | 737 | |
83f7d43a | 738 | static void core_rtas_register_types(void) |
821303f5 | 739 | { |
3a3b8502 AK |
740 | spapr_rtas_register(RTAS_DISPLAY_CHARACTER, "display-character", |
741 | rtas_display_character); | |
3a3b8502 AK |
742 | spapr_rtas_register(RTAS_POWER_OFF, "power-off", rtas_power_off); |
743 | spapr_rtas_register(RTAS_SYSTEM_REBOOT, "system-reboot", | |
744 | rtas_system_reboot); | |
745 | spapr_rtas_register(RTAS_QUERY_CPU_STOPPED_STATE, "query-cpu-stopped-state", | |
a9f8ad8f | 746 | rtas_query_cpu_stopped_state); |
3a3b8502 AK |
747 | spapr_rtas_register(RTAS_START_CPU, "start-cpu", rtas_start_cpu); |
748 | spapr_rtas_register(RTAS_STOP_SELF, "stop-self", rtas_stop_self); | |
749 | spapr_rtas_register(RTAS_IBM_GET_SYSTEM_PARAMETER, | |
750 | "ibm,get-system-parameter", | |
3ada6b11 | 751 | rtas_ibm_get_system_parameter); |
3a3b8502 AK |
752 | spapr_rtas_register(RTAS_IBM_SET_SYSTEM_PARAMETER, |
753 | "ibm,set-system-parameter", | |
3ada6b11 | 754 | rtas_ibm_set_system_parameter); |
2e14072f ND |
755 | spapr_rtas_register(RTAS_IBM_OS_TERM, "ibm,os-term", |
756 | rtas_ibm_os_term); | |
094d2058 NF |
757 | spapr_rtas_register(RTAS_SET_POWER_LEVEL, "set-power-level", |
758 | rtas_set_power_level); | |
759 | spapr_rtas_register(RTAS_GET_POWER_LEVEL, "get-power-level", | |
760 | rtas_get_power_level); | |
8c8639df MD |
761 | spapr_rtas_register(RTAS_SET_INDICATOR, "set-indicator", |
762 | rtas_set_indicator); | |
886445a6 MD |
763 | spapr_rtas_register(RTAS_GET_SENSOR_STATE, "get-sensor-state", |
764 | rtas_get_sensor_state); | |
46503c2b MR |
765 | spapr_rtas_register(RTAS_IBM_CONFIGURE_CONNECTOR, "ibm,configure-connector", |
766 | rtas_ibm_configure_connector); | |
821303f5 | 767 | } |
83f7d43a AF |
768 | |
769 | type_init(core_rtas_register_types) |