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