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
21da4699adSAndre 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
rtas_display_character(struct kvm_cpu * vcpu,uint32_t token,uint32_t nargs,target_ulong args,uint32_t nret,target_ulong rets)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
rtas_put_term_char(struct kvm_cpu * vcpu,uint32_t token,uint32_t nargs,target_ulong args,uint32_t nret,target_ulong rets)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
rtas_get_term_char(struct kvm_cpu * vcpu,uint32_t token,uint32_t nargs,target_ulong args,uint32_t nret,target_ulong rets)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
rtas_get_time_of_day(struct kvm_cpu * vcpu,uint32_t token,uint32_t nargs,target_ulong args,uint32_t nret,target_ulong rets)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
rtas_set_time_of_day(struct kvm_cpu * vcpu,uint32_t token,uint32_t nargs,target_ulong args,uint32_t nret,target_ulong rets)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
rtas_power_off(struct kvm_cpu * vcpu,uint32_t token,uint32_t nargs,target_ulong args,uint32_t nret,target_ulong rets)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 }
121*2aa76b26SWill Deacon kvm__reboot(vcpu->kvm);
122be76823fSMatt Evans }
123be76823fSMatt Evans
rtas_system_reboot(struct kvm_cpu * vcpu,uint32_t token,uint32_t nargs,target_ulong args,uint32_t nret,target_ulong rets)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 */
134*2aa76b26SWill Deacon kvm__reboot(vcpu->kvm);
13564857d5dSMichael Ellerman }
13664857d5dSMichael Ellerman
rtas_query_cpu_stopped_state(struct kvm_cpu * vcpu,uint32_t token,uint32_t nargs,target_ulong args,uint32_t nret,target_ulong rets)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
rtas_start_cpu(struct kvm_cpu * vcpu,uint32_t token,uint32_t nargs,target_ulong args,uint32_t nret,target_ulong rets)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
spapr_rtas_call(struct kvm_cpu * vcpu,uint32_t token,uint32_t nargs,target_ulong args,uint32_t nret,target_ulong rets)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
spapr_rtas_register(const char * name,spapr_rtas_fn fn)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 */
spapr_rtas_fdt_setup(struct kvm * kvm,void * fdt)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
register_core_rtas(void)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