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