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" 28 #include "sysemu/sysemu.h" 29 #include "sysemu/char.h" 30 #include "hw/qdev.h" 31 #include "sysemu/device_tree.h" 32 33 #include "hw/ppc/spapr.h" 34 #include "hw/ppc/spapr_vio.h" 35 #include "qapi-event.h" 36 37 #include <libfdt.h> 38 39 static void rtas_display_character(PowerPCCPU *cpu, sPAPREnvironment *spapr, 40 uint32_t token, uint32_t nargs, 41 target_ulong args, 42 uint32_t nret, target_ulong rets) 43 { 44 uint8_t c = rtas_ld(args, 0); 45 VIOsPAPRDevice *sdev = vty_lookup(spapr, 0); 46 47 if (!sdev) { 48 rtas_st(rets, 0, RTAS_OUT_HW_ERROR); 49 } else { 50 vty_putchars(sdev, &c, sizeof(c)); 51 rtas_st(rets, 0, RTAS_OUT_SUCCESS); 52 } 53 } 54 55 static void rtas_power_off(PowerPCCPU *cpu, sPAPREnvironment *spapr, 56 uint32_t token, uint32_t nargs, target_ulong args, 57 uint32_t nret, target_ulong rets) 58 { 59 if (nargs != 2 || nret != 1) { 60 rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); 61 return; 62 } 63 qemu_system_shutdown_request(); 64 rtas_st(rets, 0, RTAS_OUT_SUCCESS); 65 } 66 67 static void rtas_system_reboot(PowerPCCPU *cpu, sPAPREnvironment *spapr, 68 uint32_t token, uint32_t nargs, 69 target_ulong args, 70 uint32_t nret, target_ulong rets) 71 { 72 if (nargs != 0 || nret != 1) { 73 rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); 74 return; 75 } 76 qemu_system_reset_request(); 77 rtas_st(rets, 0, RTAS_OUT_SUCCESS); 78 } 79 80 static void rtas_query_cpu_stopped_state(PowerPCCPU *cpu_, 81 sPAPREnvironment *spapr, 82 uint32_t token, uint32_t nargs, 83 target_ulong args, 84 uint32_t nret, target_ulong rets) 85 { 86 target_ulong id; 87 PowerPCCPU *cpu; 88 89 if (nargs != 1 || nret != 2) { 90 rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); 91 return; 92 } 93 94 id = rtas_ld(args, 0); 95 cpu = ppc_get_vcpu_by_dt_id(id); 96 if (cpu != NULL) { 97 if (CPU(cpu)->halted) { 98 rtas_st(rets, 1, 0); 99 } else { 100 rtas_st(rets, 1, 2); 101 } 102 103 rtas_st(rets, 0, RTAS_OUT_SUCCESS); 104 return; 105 } 106 107 /* Didn't find a matching cpu */ 108 rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); 109 } 110 111 static void rtas_start_cpu(PowerPCCPU *cpu_, sPAPREnvironment *spapr, 112 uint32_t token, uint32_t nargs, 113 target_ulong args, 114 uint32_t nret, target_ulong rets) 115 { 116 target_ulong id, start, r3; 117 PowerPCCPU *cpu; 118 119 if (nargs != 3 || nret != 1) { 120 rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); 121 return; 122 } 123 124 id = rtas_ld(args, 0); 125 start = rtas_ld(args, 1); 126 r3 = rtas_ld(args, 2); 127 128 cpu = ppc_get_vcpu_by_dt_id(id); 129 if (cpu != NULL) { 130 CPUState *cs = CPU(cpu); 131 CPUPPCState *env = &cpu->env; 132 133 if (!cs->halted) { 134 rtas_st(rets, 0, RTAS_OUT_HW_ERROR); 135 return; 136 } 137 138 /* This will make sure qemu state is up to date with kvm, and 139 * mark it dirty so our changes get flushed back before the 140 * new cpu enters */ 141 kvm_cpu_synchronize_state(cs); 142 143 env->msr = (1ULL << MSR_SF) | (1ULL << MSR_ME); 144 env->nip = start; 145 env->gpr[3] = r3; 146 cs->halted = 0; 147 148 qemu_cpu_kick(cs); 149 150 rtas_st(rets, 0, RTAS_OUT_SUCCESS); 151 return; 152 } 153 154 /* Didn't find a matching cpu */ 155 rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); 156 } 157 158 static void rtas_stop_self(PowerPCCPU *cpu, sPAPREnvironment *spapr, 159 uint32_t token, uint32_t nargs, 160 target_ulong args, 161 uint32_t nret, target_ulong rets) 162 { 163 CPUState *cs = CPU(cpu); 164 CPUPPCState *env = &cpu->env; 165 166 cs->halted = 1; 167 cpu_exit(cs); 168 /* 169 * While stopping a CPU, the guest calls H_CPPR which 170 * effectively disables interrupts on XICS level. 171 * However decrementer interrupts in TCG can still 172 * wake the CPU up so here we disable interrupts in MSR 173 * as well. 174 * As rtas_start_cpu() resets the whole MSR anyway, there is 175 * no need to bother with specific bits, we just clear it. 176 */ 177 env->msr = 0; 178 } 179 180 static void rtas_ibm_get_system_parameter(PowerPCCPU *cpu, 181 sPAPREnvironment *spapr, 182 uint32_t token, uint32_t nargs, 183 target_ulong args, 184 uint32_t nret, target_ulong rets) 185 { 186 target_ulong parameter = rtas_ld(args, 0); 187 target_ulong buffer = rtas_ld(args, 1); 188 target_ulong length = rtas_ld(args, 2); 189 target_ulong ret = RTAS_OUT_SUCCESS; 190 191 switch (parameter) { 192 case RTAS_SYSPARM_SPLPAR_CHARACTERISTICS: { 193 char *param_val = g_strdup_printf("MaxEntCap=%d,MaxPlatProcs=%d", 194 max_cpus, smp_cpus); 195 rtas_st_buffer(buffer, length, (uint8_t *)param_val, strlen(param_val)); 196 g_free(param_val); 197 break; 198 } 199 case RTAS_SYSPARM_DIAGNOSTICS_RUN_MODE: { 200 uint8_t param_val = DIAGNOSTICS_RUN_MODE_DISABLED; 201 202 rtas_st_buffer(buffer, length, ¶m_val, sizeof(param_val)); 203 break; 204 } 205 case RTAS_SYSPARM_UUID: 206 rtas_st_buffer(buffer, length, qemu_uuid, (qemu_uuid_set ? 16 : 0)); 207 break; 208 default: 209 ret = RTAS_OUT_NOT_SUPPORTED; 210 } 211 212 rtas_st(rets, 0, ret); 213 } 214 215 static void rtas_ibm_set_system_parameter(PowerPCCPU *cpu, 216 sPAPREnvironment *spapr, 217 uint32_t token, uint32_t nargs, 218 target_ulong args, 219 uint32_t nret, target_ulong rets) 220 { 221 target_ulong parameter = rtas_ld(args, 0); 222 target_ulong ret = RTAS_OUT_NOT_SUPPORTED; 223 224 switch (parameter) { 225 case RTAS_SYSPARM_SPLPAR_CHARACTERISTICS: 226 case RTAS_SYSPARM_DIAGNOSTICS_RUN_MODE: 227 case RTAS_SYSPARM_UUID: 228 ret = RTAS_OUT_NOT_AUTHORIZED; 229 break; 230 } 231 232 rtas_st(rets, 0, ret); 233 } 234 235 static void rtas_ibm_os_term(PowerPCCPU *cpu, 236 sPAPREnvironment *spapr, 237 uint32_t token, uint32_t nargs, 238 target_ulong args, 239 uint32_t nret, target_ulong rets) 240 { 241 target_ulong ret = 0; 242 243 qapi_event_send_guest_panicked(GUEST_PANIC_ACTION_PAUSE, &error_abort); 244 245 rtas_st(rets, 0, ret); 246 } 247 248 static void rtas_set_power_level(PowerPCCPU *cpu, sPAPREnvironment *spapr, 249 uint32_t token, uint32_t nargs, 250 target_ulong args, uint32_t nret, 251 target_ulong rets) 252 { 253 int32_t power_domain; 254 255 if (nargs != 2 || nret != 2) { 256 rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); 257 return; 258 } 259 260 /* we currently only use a single, "live insert" powerdomain for 261 * hotplugged/dlpar'd resources, so the power is always live/full (100) 262 */ 263 power_domain = rtas_ld(args, 0); 264 if (power_domain != -1) { 265 rtas_st(rets, 0, RTAS_OUT_NOT_SUPPORTED); 266 return; 267 } 268 269 rtas_st(rets, 0, RTAS_OUT_SUCCESS); 270 rtas_st(rets, 1, 100); 271 } 272 273 static void rtas_get_power_level(PowerPCCPU *cpu, sPAPREnvironment *spapr, 274 uint32_t token, uint32_t nargs, 275 target_ulong args, uint32_t nret, 276 target_ulong rets) 277 { 278 int32_t power_domain; 279 280 if (nargs != 1 || nret != 2) { 281 rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); 282 return; 283 } 284 285 /* we currently only use a single, "live insert" powerdomain for 286 * hotplugged/dlpar'd resources, so the power is always live/full (100) 287 */ 288 power_domain = rtas_ld(args, 0); 289 if (power_domain != -1) { 290 rtas_st(rets, 0, RTAS_OUT_NOT_SUPPORTED); 291 return; 292 } 293 294 rtas_st(rets, 0, RTAS_OUT_SUCCESS); 295 rtas_st(rets, 1, 100); 296 } 297 298 static struct rtas_call { 299 const char *name; 300 spapr_rtas_fn fn; 301 } rtas_table[RTAS_TOKEN_MAX - RTAS_TOKEN_BASE]; 302 303 target_ulong spapr_rtas_call(PowerPCCPU *cpu, sPAPREnvironment *spapr, 304 uint32_t token, uint32_t nargs, target_ulong args, 305 uint32_t nret, target_ulong rets) 306 { 307 if ((token >= RTAS_TOKEN_BASE) && (token < RTAS_TOKEN_MAX)) { 308 struct rtas_call *call = rtas_table + (token - RTAS_TOKEN_BASE); 309 310 if (call->fn) { 311 call->fn(cpu, spapr, token, nargs, args, nret, rets); 312 return H_SUCCESS; 313 } 314 } 315 316 /* HACK: Some Linux early debug code uses RTAS display-character, 317 * but assumes the token value is 0xa (which it is on some real 318 * machines) without looking it up in the device tree. This 319 * special case makes this work */ 320 if (token == 0xa) { 321 rtas_display_character(cpu, spapr, 0xa, nargs, args, nret, rets); 322 return H_SUCCESS; 323 } 324 325 hcall_dprintf("Unknown RTAS token 0x%x\n", token); 326 rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); 327 return H_PARAMETER; 328 } 329 330 void spapr_rtas_register(int token, const char *name, spapr_rtas_fn fn) 331 { 332 if (!((token >= RTAS_TOKEN_BASE) && (token < RTAS_TOKEN_MAX))) { 333 fprintf(stderr, "RTAS invalid token 0x%x\n", token); 334 exit(1); 335 } 336 337 token -= RTAS_TOKEN_BASE; 338 if (rtas_table[token].name) { 339 fprintf(stderr, "RTAS call \"%s\" is registered already as 0x%x\n", 340 rtas_table[token].name, token); 341 exit(1); 342 } 343 344 rtas_table[token].name = name; 345 rtas_table[token].fn = fn; 346 } 347 348 int spapr_rtas_device_tree_setup(void *fdt, hwaddr rtas_addr, 349 hwaddr rtas_size) 350 { 351 int ret; 352 int i; 353 354 ret = fdt_add_mem_rsv(fdt, rtas_addr, rtas_size); 355 if (ret < 0) { 356 fprintf(stderr, "Couldn't add RTAS reserve entry: %s\n", 357 fdt_strerror(ret)); 358 return ret; 359 } 360 361 ret = qemu_fdt_setprop_cell(fdt, "/rtas", "linux,rtas-base", 362 rtas_addr); 363 if (ret < 0) { 364 fprintf(stderr, "Couldn't add linux,rtas-base property: %s\n", 365 fdt_strerror(ret)); 366 return ret; 367 } 368 369 ret = qemu_fdt_setprop_cell(fdt, "/rtas", "linux,rtas-entry", 370 rtas_addr); 371 if (ret < 0) { 372 fprintf(stderr, "Couldn't add linux,rtas-entry property: %s\n", 373 fdt_strerror(ret)); 374 return ret; 375 } 376 377 ret = qemu_fdt_setprop_cell(fdt, "/rtas", "rtas-size", 378 rtas_size); 379 if (ret < 0) { 380 fprintf(stderr, "Couldn't add rtas-size property: %s\n", 381 fdt_strerror(ret)); 382 return ret; 383 } 384 385 for (i = 0; i < RTAS_TOKEN_MAX - RTAS_TOKEN_BASE; i++) { 386 struct rtas_call *call = &rtas_table[i]; 387 388 if (!call->name) { 389 continue; 390 } 391 392 ret = qemu_fdt_setprop_cell(fdt, "/rtas", call->name, 393 i + RTAS_TOKEN_BASE); 394 if (ret < 0) { 395 fprintf(stderr, "Couldn't add rtas token for %s: %s\n", 396 call->name, fdt_strerror(ret)); 397 return ret; 398 } 399 400 } 401 return 0; 402 } 403 404 static void core_rtas_register_types(void) 405 { 406 spapr_rtas_register(RTAS_DISPLAY_CHARACTER, "display-character", 407 rtas_display_character); 408 spapr_rtas_register(RTAS_POWER_OFF, "power-off", rtas_power_off); 409 spapr_rtas_register(RTAS_SYSTEM_REBOOT, "system-reboot", 410 rtas_system_reboot); 411 spapr_rtas_register(RTAS_QUERY_CPU_STOPPED_STATE, "query-cpu-stopped-state", 412 rtas_query_cpu_stopped_state); 413 spapr_rtas_register(RTAS_START_CPU, "start-cpu", rtas_start_cpu); 414 spapr_rtas_register(RTAS_STOP_SELF, "stop-self", rtas_stop_self); 415 spapr_rtas_register(RTAS_IBM_GET_SYSTEM_PARAMETER, 416 "ibm,get-system-parameter", 417 rtas_ibm_get_system_parameter); 418 spapr_rtas_register(RTAS_IBM_SET_SYSTEM_PARAMETER, 419 "ibm,set-system-parameter", 420 rtas_ibm_set_system_parameter); 421 spapr_rtas_register(RTAS_IBM_OS_TERM, "ibm,os-term", 422 rtas_ibm_os_term); 423 spapr_rtas_register(RTAS_SET_POWER_LEVEL, "set-power-level", 424 rtas_set_power_level); 425 spapr_rtas_register(RTAS_GET_POWER_LEVEL, "get-power-level", 426 rtas_get_power_level); 427 } 428 429 type_init(core_rtas_register_types) 430