1be76823fSMatt Evans /* 2be76823fSMatt Evans * SPAPR base RTAS calls 3be76823fSMatt Evans * 4be76823fSMatt Evans * Borrowed heavily from QEMU's spapr_rtas.c 5be76823fSMatt Evans * Copyright (c) 2010-2011 David Gibson, IBM Corporation. 6be76823fSMatt Evans * 7be76823fSMatt Evans * Modifications copyright 2011 Matt Evans <matt@ozlabs.org>, IBM Corporation. 8be76823fSMatt Evans * 9be76823fSMatt Evans * This program is free software; you can redistribute it and/or modify it 10be76823fSMatt Evans * under the terms of the GNU General Public License version 2 as published 11be76823fSMatt Evans * by the Free Software Foundation. 12be76823fSMatt Evans */ 13be76823fSMatt Evans 14be76823fSMatt Evans #include "kvm/kvm.h" 15be76823fSMatt Evans #include "kvm/kvm-cpu.h" 16be76823fSMatt Evans #include "kvm/util.h" 17be76823fSMatt Evans #include "kvm/term.h" 18be76823fSMatt Evans 19be76823fSMatt Evans #include "spapr.h" 20be76823fSMatt Evans 21*da4699adSAndre Przywara #include <libfdt.h> 22be76823fSMatt Evans #include <stdio.h> 23be76823fSMatt Evans #include <assert.h> 24be76823fSMatt Evans 25be76823fSMatt Evans #define TOKEN_BASE 0x2000 26be76823fSMatt Evans #define TOKEN_MAX 0x100 27be76823fSMatt Evans 28be76823fSMatt Evans #define RTAS_CONSOLE 29be76823fSMatt Evans 30be76823fSMatt Evans static struct rtas_call { 31be76823fSMatt Evans const char *name; 32be76823fSMatt Evans spapr_rtas_fn fn; 33be76823fSMatt Evans } rtas_table[TOKEN_MAX]; 34be76823fSMatt Evans 35be76823fSMatt Evans struct rtas_call *rtas_next = rtas_table; 36be76823fSMatt Evans 37be76823fSMatt Evans 38be76823fSMatt Evans static void rtas_display_character(struct kvm_cpu *vcpu, 39be76823fSMatt Evans uint32_t token, uint32_t nargs, 40be76823fSMatt Evans target_ulong args, 41be76823fSMatt Evans uint32_t nret, target_ulong rets) 42be76823fSMatt Evans { 43be76823fSMatt Evans char c = rtas_ld(vcpu->kvm, args, 0); 44dc4fc6b6SMichael Ellerman term_putc(&c, 1, 0); 45be76823fSMatt Evans rtas_st(vcpu->kvm, rets, 0, 0); 46be76823fSMatt Evans } 47be76823fSMatt Evans 48be76823fSMatt Evans #ifdef RTAS_CONSOLE 49be76823fSMatt Evans static void rtas_put_term_char(struct kvm_cpu *vcpu, 50be76823fSMatt Evans uint32_t token, uint32_t nargs, 51be76823fSMatt Evans target_ulong args, 52be76823fSMatt Evans uint32_t nret, target_ulong rets) 53be76823fSMatt Evans { 54be76823fSMatt Evans char c = rtas_ld(vcpu->kvm, args, 0); 55dc4fc6b6SMichael Ellerman 56dc4fc6b6SMichael Ellerman term_putc(&c, 1, 0); 57dc4fc6b6SMichael Ellerman 58be76823fSMatt Evans rtas_st(vcpu->kvm, rets, 0, 0); 59be76823fSMatt Evans } 60be76823fSMatt Evans 61be76823fSMatt Evans static void rtas_get_term_char(struct kvm_cpu *vcpu, 62be76823fSMatt Evans uint32_t token, uint32_t nargs, 63be76823fSMatt Evans target_ulong args, 64be76823fSMatt Evans uint32_t nret, target_ulong rets) 65be76823fSMatt Evans { 66be76823fSMatt Evans int c; 67dc4fc6b6SMichael Ellerman 68dc4fc6b6SMichael Ellerman if (vcpu->kvm->cfg.active_console == CONSOLE_HV && term_readable(0) && 69dc4fc6b6SMichael Ellerman (c = term_getc(vcpu->kvm, 0)) >= 0) { 70be76823fSMatt Evans rtas_st(vcpu->kvm, rets, 0, 0); 71be76823fSMatt Evans rtas_st(vcpu->kvm, rets, 1, c); 72be76823fSMatt Evans } else { 73be76823fSMatt Evans rtas_st(vcpu->kvm, rets, 0, -2); 74be76823fSMatt Evans } 75be76823fSMatt Evans } 76be76823fSMatt Evans #endif 77be76823fSMatt Evans 78be76823fSMatt Evans static void rtas_get_time_of_day(struct kvm_cpu *vcpu, 79be76823fSMatt Evans uint32_t token, uint32_t nargs, 80be76823fSMatt Evans target_ulong args, 81be76823fSMatt Evans uint32_t nret, target_ulong rets) 82be76823fSMatt Evans { 83be76823fSMatt Evans struct tm tm; 84be76823fSMatt Evans time_t tnow; 85be76823fSMatt Evans 86be76823fSMatt Evans if (nret != 8) { 87be76823fSMatt Evans rtas_st(vcpu->kvm, rets, 0, -3); 88be76823fSMatt Evans return; 89be76823fSMatt Evans } 90be76823fSMatt Evans 91be76823fSMatt Evans tnow = time(NULL); 92be76823fSMatt Evans /* Guest time is currently not offset in any way. */ 93be76823fSMatt Evans gmtime_r(&tnow, &tm); 94be76823fSMatt Evans 95be76823fSMatt Evans rtas_st(vcpu->kvm, rets, 0, 0); /* Success */ 96be76823fSMatt Evans rtas_st(vcpu->kvm, rets, 1, tm.tm_year + 1900); 97be76823fSMatt Evans rtas_st(vcpu->kvm, rets, 2, tm.tm_mon + 1); 98be76823fSMatt Evans rtas_st(vcpu->kvm, rets, 3, tm.tm_mday); 99be76823fSMatt Evans rtas_st(vcpu->kvm, rets, 4, tm.tm_hour); 100be76823fSMatt Evans rtas_st(vcpu->kvm, rets, 5, tm.tm_min); 101be76823fSMatt Evans rtas_st(vcpu->kvm, rets, 6, tm.tm_sec); 102be76823fSMatt Evans rtas_st(vcpu->kvm, rets, 7, 0); 103be76823fSMatt Evans } 104be76823fSMatt Evans 105be76823fSMatt Evans static void rtas_set_time_of_day(struct kvm_cpu *vcpu, 106be76823fSMatt Evans uint32_t token, uint32_t nargs, 107be76823fSMatt Evans target_ulong args, 108be76823fSMatt Evans uint32_t nret, target_ulong rets) 109be76823fSMatt Evans { 110be76823fSMatt Evans pr_warning("%s called; TOD set ignored.\n", __FUNCTION__); 111be76823fSMatt Evans } 112be76823fSMatt Evans 113be76823fSMatt Evans static void rtas_power_off(struct kvm_cpu *vcpu, 114be76823fSMatt Evans uint32_t token, uint32_t nargs, target_ulong args, 115be76823fSMatt Evans uint32_t nret, target_ulong rets) 116be76823fSMatt Evans { 117be76823fSMatt Evans if (nargs != 2 || nret != 1) { 118be76823fSMatt Evans rtas_st(vcpu->kvm, rets, 0, -3); 119be76823fSMatt Evans return; 120be76823fSMatt Evans } 121dc4fc6b6SMichael Ellerman kvm_cpu__reboot(vcpu->kvm); 122be76823fSMatt Evans } 123be76823fSMatt Evans 12464857d5dSMichael Ellerman static void rtas_system_reboot(struct kvm_cpu *vcpu, 12564857d5dSMichael Ellerman uint32_t token, uint32_t nargs, target_ulong args, 12664857d5dSMichael Ellerman uint32_t nret, target_ulong rets) 12764857d5dSMichael Ellerman { 12864857d5dSMichael Ellerman if (nargs != 0 || nret != 1) { 12964857d5dSMichael Ellerman rtas_st(vcpu->kvm, rets, 0, -3); 13064857d5dSMichael Ellerman return; 13164857d5dSMichael Ellerman } 13264857d5dSMichael Ellerman 13364857d5dSMichael Ellerman /* NB this actually halts the VM */ 13464857d5dSMichael Ellerman kvm_cpu__reboot(vcpu->kvm); 13564857d5dSMichael Ellerman } 13664857d5dSMichael Ellerman 137be76823fSMatt Evans static void rtas_query_cpu_stopped_state(struct kvm_cpu *vcpu, 138be76823fSMatt Evans uint32_t token, uint32_t nargs, 139be76823fSMatt Evans target_ulong args, 140be76823fSMatt Evans uint32_t nret, target_ulong rets) 141be76823fSMatt Evans { 142be76823fSMatt Evans if (nargs != 1 || nret != 2) { 143be76823fSMatt Evans rtas_st(vcpu->kvm, rets, 0, -3); 144be76823fSMatt Evans return; 145be76823fSMatt Evans } 146be76823fSMatt Evans 147be76823fSMatt Evans /* 148be76823fSMatt Evans * Can read id = rtas_ld(vcpu->kvm, args, 0), but 149be76823fSMatt Evans * we currently start all CPUs. So just return true. 150be76823fSMatt Evans */ 151be76823fSMatt Evans rtas_st(vcpu->kvm, rets, 0, 0); 152be76823fSMatt Evans rtas_st(vcpu->kvm, rets, 1, 2); 153be76823fSMatt Evans } 154be76823fSMatt Evans 155be76823fSMatt Evans static void rtas_start_cpu(struct kvm_cpu *vcpu, 156be76823fSMatt Evans uint32_t token, uint32_t nargs, 157be76823fSMatt Evans target_ulong args, 158be76823fSMatt Evans uint32_t nret, target_ulong rets) 159be76823fSMatt Evans { 160be76823fSMatt Evans die(__FUNCTION__); 161be76823fSMatt Evans } 162be76823fSMatt Evans 163be76823fSMatt Evans target_ulong spapr_rtas_call(struct kvm_cpu *vcpu, 164be76823fSMatt Evans uint32_t token, uint32_t nargs, target_ulong args, 165be76823fSMatt Evans uint32_t nret, target_ulong rets) 166be76823fSMatt Evans { 167be76823fSMatt Evans if ((token >= TOKEN_BASE) 168be76823fSMatt Evans && ((token - TOKEN_BASE) < TOKEN_MAX)) { 169be76823fSMatt Evans struct rtas_call *call = rtas_table + (token - TOKEN_BASE); 170be76823fSMatt Evans 171be76823fSMatt Evans if (call->fn) { 172be76823fSMatt Evans call->fn(vcpu, token, nargs, args, nret, rets); 173be76823fSMatt Evans return H_SUCCESS; 174be76823fSMatt Evans } 175be76823fSMatt Evans } 176be76823fSMatt Evans 177be76823fSMatt Evans /* 178be76823fSMatt Evans * HACK: Some Linux early debug code uses RTAS display-character, 179be76823fSMatt Evans * but assumes the token value is 0xa (which it is on some real 180be76823fSMatt Evans * machines) without looking it up in the device tree. This 181be76823fSMatt Evans * special case makes this work 182be76823fSMatt Evans */ 183be76823fSMatt Evans if (token == 0xa) { 184be76823fSMatt Evans rtas_display_character(vcpu, 0xa, nargs, args, nret, rets); 185be76823fSMatt Evans return H_SUCCESS; 186be76823fSMatt Evans } 187be76823fSMatt Evans 188be76823fSMatt Evans hcall_dprintf("Unknown RTAS token 0x%x\n", token); 189be76823fSMatt Evans rtas_st(vcpu->kvm, rets, 0, -3); 190be76823fSMatt Evans return H_PARAMETER; 191be76823fSMatt Evans } 192be76823fSMatt Evans 193be76823fSMatt Evans void spapr_rtas_register(const char *name, spapr_rtas_fn fn) 194be76823fSMatt Evans { 195be76823fSMatt Evans assert(rtas_next < (rtas_table + TOKEN_MAX)); 196be76823fSMatt Evans 197be76823fSMatt Evans rtas_next->name = name; 198be76823fSMatt Evans rtas_next->fn = fn; 199be76823fSMatt Evans 200be76823fSMatt Evans rtas_next++; 201be76823fSMatt Evans } 202be76823fSMatt Evans 203be76823fSMatt Evans /* 204be76823fSMatt Evans * This is called from the context of an open /rtas node, in order to add 205be76823fSMatt Evans * properties for the rtas call tokens. 206be76823fSMatt Evans */ 207be76823fSMatt Evans int spapr_rtas_fdt_setup(struct kvm *kvm, void *fdt) 208be76823fSMatt Evans { 209be76823fSMatt Evans int ret; 210be76823fSMatt Evans int i; 211be76823fSMatt Evans 212be76823fSMatt Evans for (i = 0; i < TOKEN_MAX; i++) { 213be76823fSMatt Evans struct rtas_call *call = &rtas_table[i]; 214be76823fSMatt Evans 215be76823fSMatt Evans if (!call->fn) { 216be76823fSMatt Evans continue; 217be76823fSMatt Evans } 218be76823fSMatt Evans 219be76823fSMatt Evans ret = fdt_property_cell(fdt, call->name, i + TOKEN_BASE); 220be76823fSMatt Evans 221be76823fSMatt Evans if (ret < 0) { 222be76823fSMatt Evans pr_warning("Couldn't add rtas token for %s: %s\n", 223be76823fSMatt Evans call->name, fdt_strerror(ret)); 224be76823fSMatt Evans return ret; 225be76823fSMatt Evans } 226be76823fSMatt Evans 227be76823fSMatt Evans } 228be76823fSMatt Evans return 0; 229be76823fSMatt Evans } 230be76823fSMatt Evans 231be76823fSMatt Evans void register_core_rtas(void) 232be76823fSMatt Evans { 233be76823fSMatt Evans spapr_rtas_register("display-character", rtas_display_character); 234be76823fSMatt Evans spapr_rtas_register("get-time-of-day", rtas_get_time_of_day); 235be76823fSMatt Evans spapr_rtas_register("set-time-of-day", rtas_set_time_of_day); 236be76823fSMatt Evans spapr_rtas_register("power-off", rtas_power_off); 23764857d5dSMichael Ellerman spapr_rtas_register("system-reboot", rtas_system_reboot); 238be76823fSMatt Evans spapr_rtas_register("query-cpu-stopped-state", 239be76823fSMatt Evans rtas_query_cpu_stopped_state); 240be76823fSMatt Evans spapr_rtas_register("start-cpu", rtas_start_cpu); 241be76823fSMatt Evans #ifdef RTAS_CONSOLE 242be76823fSMatt Evans /* These are unused: We do console I/O via hcalls, not rtas. */ 243be76823fSMatt Evans spapr_rtas_register("put-term-char", rtas_put_term_char); 244be76823fSMatt Evans spapr_rtas_register("get-term-char", rtas_get_term_char); 245be76823fSMatt Evans #endif 246be76823fSMatt Evans } 247