xref: /kvmtool/powerpc/spapr_rtas.c (revision 2aa76b2616bcace1d668a879d834679dfa00dc8c)
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