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