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 #include "libfdt.h" 19be76823fSMatt Evans 20be76823fSMatt Evans #include "spapr.h" 21be76823fSMatt Evans 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); 44*dc4fc6b6SMichael 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); 55*dc4fc6b6SMichael Ellerman 56*dc4fc6b6SMichael Ellerman if (vcpu->kvm->cfg.active_console == CONSOLE_HV) 57*dc4fc6b6SMichael Ellerman term_putc(&c, 1, 0); 58*dc4fc6b6SMichael Ellerman 59be76823fSMatt Evans rtas_st(vcpu->kvm, rets, 0, 0); 60be76823fSMatt Evans } 61be76823fSMatt Evans 62be76823fSMatt Evans static void rtas_get_term_char(struct kvm_cpu *vcpu, 63be76823fSMatt Evans uint32_t token, uint32_t nargs, 64be76823fSMatt Evans target_ulong args, 65be76823fSMatt Evans uint32_t nret, target_ulong rets) 66be76823fSMatt Evans { 67be76823fSMatt Evans int c; 68*dc4fc6b6SMichael Ellerman 69*dc4fc6b6SMichael Ellerman if (vcpu->kvm->cfg.active_console == CONSOLE_HV && term_readable(0) && 70*dc4fc6b6SMichael Ellerman (c = term_getc(vcpu->kvm, 0)) >= 0) { 71be76823fSMatt Evans rtas_st(vcpu->kvm, rets, 0, 0); 72be76823fSMatt Evans rtas_st(vcpu->kvm, rets, 1, c); 73be76823fSMatt Evans } else { 74be76823fSMatt Evans rtas_st(vcpu->kvm, rets, 0, -2); 75be76823fSMatt Evans } 76be76823fSMatt Evans } 77be76823fSMatt Evans #endif 78be76823fSMatt Evans 79be76823fSMatt Evans static void rtas_get_time_of_day(struct kvm_cpu *vcpu, 80be76823fSMatt Evans uint32_t token, uint32_t nargs, 81be76823fSMatt Evans target_ulong args, 82be76823fSMatt Evans uint32_t nret, target_ulong rets) 83be76823fSMatt Evans { 84be76823fSMatt Evans struct tm tm; 85be76823fSMatt Evans time_t tnow; 86be76823fSMatt Evans 87be76823fSMatt Evans if (nret != 8) { 88be76823fSMatt Evans rtas_st(vcpu->kvm, rets, 0, -3); 89be76823fSMatt Evans return; 90be76823fSMatt Evans } 91be76823fSMatt Evans 92be76823fSMatt Evans tnow = time(NULL); 93be76823fSMatt Evans /* Guest time is currently not offset in any way. */ 94be76823fSMatt Evans gmtime_r(&tnow, &tm); 95be76823fSMatt Evans 96be76823fSMatt Evans rtas_st(vcpu->kvm, rets, 0, 0); /* Success */ 97be76823fSMatt Evans rtas_st(vcpu->kvm, rets, 1, tm.tm_year + 1900); 98be76823fSMatt Evans rtas_st(vcpu->kvm, rets, 2, tm.tm_mon + 1); 99be76823fSMatt Evans rtas_st(vcpu->kvm, rets, 3, tm.tm_mday); 100be76823fSMatt Evans rtas_st(vcpu->kvm, rets, 4, tm.tm_hour); 101be76823fSMatt Evans rtas_st(vcpu->kvm, rets, 5, tm.tm_min); 102be76823fSMatt Evans rtas_st(vcpu->kvm, rets, 6, tm.tm_sec); 103be76823fSMatt Evans rtas_st(vcpu->kvm, rets, 7, 0); 104be76823fSMatt Evans } 105be76823fSMatt Evans 106be76823fSMatt Evans static void rtas_set_time_of_day(struct kvm_cpu *vcpu, 107be76823fSMatt Evans uint32_t token, uint32_t nargs, 108be76823fSMatt Evans target_ulong args, 109be76823fSMatt Evans uint32_t nret, target_ulong rets) 110be76823fSMatt Evans { 111be76823fSMatt Evans pr_warning("%s called; TOD set ignored.\n", __FUNCTION__); 112be76823fSMatt Evans } 113be76823fSMatt Evans 114be76823fSMatt Evans static void rtas_power_off(struct kvm_cpu *vcpu, 115be76823fSMatt Evans uint32_t token, uint32_t nargs, target_ulong args, 116be76823fSMatt Evans uint32_t nret, target_ulong rets) 117be76823fSMatt Evans { 118be76823fSMatt Evans if (nargs != 2 || nret != 1) { 119be76823fSMatt Evans rtas_st(vcpu->kvm, rets, 0, -3); 120be76823fSMatt Evans return; 121be76823fSMatt Evans } 122*dc4fc6b6SMichael Ellerman kvm_cpu__reboot(vcpu->kvm); 123be76823fSMatt Evans } 124be76823fSMatt Evans 125be76823fSMatt Evans static void rtas_query_cpu_stopped_state(struct kvm_cpu *vcpu, 126be76823fSMatt Evans uint32_t token, uint32_t nargs, 127be76823fSMatt Evans target_ulong args, 128be76823fSMatt Evans uint32_t nret, target_ulong rets) 129be76823fSMatt Evans { 130be76823fSMatt Evans if (nargs != 1 || nret != 2) { 131be76823fSMatt Evans rtas_st(vcpu->kvm, rets, 0, -3); 132be76823fSMatt Evans return; 133be76823fSMatt Evans } 134be76823fSMatt Evans 135be76823fSMatt Evans /* 136be76823fSMatt Evans * Can read id = rtas_ld(vcpu->kvm, args, 0), but 137be76823fSMatt Evans * we currently start all CPUs. So just return true. 138be76823fSMatt Evans */ 139be76823fSMatt Evans rtas_st(vcpu->kvm, rets, 0, 0); 140be76823fSMatt Evans rtas_st(vcpu->kvm, rets, 1, 2); 141be76823fSMatt Evans } 142be76823fSMatt Evans 143be76823fSMatt Evans static void rtas_start_cpu(struct kvm_cpu *vcpu, 144be76823fSMatt Evans uint32_t token, uint32_t nargs, 145be76823fSMatt Evans target_ulong args, 146be76823fSMatt Evans uint32_t nret, target_ulong rets) 147be76823fSMatt Evans { 148be76823fSMatt Evans die(__FUNCTION__); 149be76823fSMatt Evans } 150be76823fSMatt Evans 151be76823fSMatt Evans target_ulong spapr_rtas_call(struct kvm_cpu *vcpu, 152be76823fSMatt Evans uint32_t token, uint32_t nargs, target_ulong args, 153be76823fSMatt Evans uint32_t nret, target_ulong rets) 154be76823fSMatt Evans { 155be76823fSMatt Evans if ((token >= TOKEN_BASE) 156be76823fSMatt Evans && ((token - TOKEN_BASE) < TOKEN_MAX)) { 157be76823fSMatt Evans struct rtas_call *call = rtas_table + (token - TOKEN_BASE); 158be76823fSMatt Evans 159be76823fSMatt Evans if (call->fn) { 160be76823fSMatt Evans call->fn(vcpu, token, nargs, args, nret, rets); 161be76823fSMatt Evans return H_SUCCESS; 162be76823fSMatt Evans } 163be76823fSMatt Evans } 164be76823fSMatt Evans 165be76823fSMatt Evans /* 166be76823fSMatt Evans * HACK: Some Linux early debug code uses RTAS display-character, 167be76823fSMatt Evans * but assumes the token value is 0xa (which it is on some real 168be76823fSMatt Evans * machines) without looking it up in the device tree. This 169be76823fSMatt Evans * special case makes this work 170be76823fSMatt Evans */ 171be76823fSMatt Evans if (token == 0xa) { 172be76823fSMatt Evans rtas_display_character(vcpu, 0xa, nargs, args, nret, rets); 173be76823fSMatt Evans return H_SUCCESS; 174be76823fSMatt Evans } 175be76823fSMatt Evans 176be76823fSMatt Evans hcall_dprintf("Unknown RTAS token 0x%x\n", token); 177be76823fSMatt Evans rtas_st(vcpu->kvm, rets, 0, -3); 178be76823fSMatt Evans return H_PARAMETER; 179be76823fSMatt Evans } 180be76823fSMatt Evans 181be76823fSMatt Evans void spapr_rtas_register(const char *name, spapr_rtas_fn fn) 182be76823fSMatt Evans { 183be76823fSMatt Evans assert(rtas_next < (rtas_table + TOKEN_MAX)); 184be76823fSMatt Evans 185be76823fSMatt Evans rtas_next->name = name; 186be76823fSMatt Evans rtas_next->fn = fn; 187be76823fSMatt Evans 188be76823fSMatt Evans rtas_next++; 189be76823fSMatt Evans } 190be76823fSMatt Evans 191be76823fSMatt Evans /* 192be76823fSMatt Evans * This is called from the context of an open /rtas node, in order to add 193be76823fSMatt Evans * properties for the rtas call tokens. 194be76823fSMatt Evans */ 195be76823fSMatt Evans int spapr_rtas_fdt_setup(struct kvm *kvm, void *fdt) 196be76823fSMatt Evans { 197be76823fSMatt Evans int ret; 198be76823fSMatt Evans int i; 199be76823fSMatt Evans 200be76823fSMatt Evans for (i = 0; i < TOKEN_MAX; i++) { 201be76823fSMatt Evans struct rtas_call *call = &rtas_table[i]; 202be76823fSMatt Evans 203be76823fSMatt Evans if (!call->fn) { 204be76823fSMatt Evans continue; 205be76823fSMatt Evans } 206be76823fSMatt Evans 207be76823fSMatt Evans ret = fdt_property_cell(fdt, call->name, i + TOKEN_BASE); 208be76823fSMatt Evans 209be76823fSMatt Evans if (ret < 0) { 210be76823fSMatt Evans pr_warning("Couldn't add rtas token for %s: %s\n", 211be76823fSMatt Evans call->name, fdt_strerror(ret)); 212be76823fSMatt Evans return ret; 213be76823fSMatt Evans } 214be76823fSMatt Evans 215be76823fSMatt Evans } 216be76823fSMatt Evans return 0; 217be76823fSMatt Evans } 218be76823fSMatt Evans 219be76823fSMatt Evans void register_core_rtas(void) 220be76823fSMatt Evans { 221be76823fSMatt Evans spapr_rtas_register("display-character", rtas_display_character); 222be76823fSMatt Evans spapr_rtas_register("get-time-of-day", rtas_get_time_of_day); 223be76823fSMatt Evans spapr_rtas_register("set-time-of-day", rtas_set_time_of_day); 224be76823fSMatt Evans spapr_rtas_register("power-off", rtas_power_off); 225be76823fSMatt Evans spapr_rtas_register("query-cpu-stopped-state", 226be76823fSMatt Evans rtas_query_cpu_stopped_state); 227be76823fSMatt Evans spapr_rtas_register("start-cpu", rtas_start_cpu); 228be76823fSMatt Evans #ifdef RTAS_CONSOLE 229be76823fSMatt Evans /* These are unused: We do console I/O via hcalls, not rtas. */ 230be76823fSMatt Evans spapr_rtas_register("put-term-char", rtas_put_term_char); 231be76823fSMatt Evans spapr_rtas_register("get-term-char", rtas_get_term_char); 232be76823fSMatt Evans #endif 233be76823fSMatt Evans } 234