xref: /kvm-unit-tests/lib/powerpc/rtas.c (revision 2f9ce69e911e000aef74bae10921da1d066979cc)
1*2f9ce69eSAndrew Jones /*
2*2f9ce69eSAndrew Jones  * powerpc RTAS
3*2f9ce69eSAndrew Jones  *
4*2f9ce69eSAndrew Jones  * Copyright (C) 2016, Red Hat Inc, Andrew Jones <drjones@redhat.com>
5*2f9ce69eSAndrew Jones  *
6*2f9ce69eSAndrew Jones  * This work is licensed under the terms of the GNU LGPL, version 2.
7*2f9ce69eSAndrew Jones  */
8*2f9ce69eSAndrew Jones #include <libcflat.h>
9*2f9ce69eSAndrew Jones #include <libfdt/libfdt.h>
10*2f9ce69eSAndrew Jones #include <devicetree.h>
11*2f9ce69eSAndrew Jones #include <asm/spinlock.h>
12*2f9ce69eSAndrew Jones #include <asm/hcall.h>
13*2f9ce69eSAndrew Jones #include <asm/io.h>
14*2f9ce69eSAndrew Jones #include <asm/rtas.h>
15*2f9ce69eSAndrew Jones 
16*2f9ce69eSAndrew Jones extern void enter_rtas(unsigned long);
17*2f9ce69eSAndrew Jones 
18*2f9ce69eSAndrew Jones unsigned long rtas_entry;
19*2f9ce69eSAndrew Jones static struct rtas_args rtas_args;
20*2f9ce69eSAndrew Jones static struct spinlock rtas_lock;
21*2f9ce69eSAndrew Jones 
22*2f9ce69eSAndrew Jones static int rtas_node(void)
23*2f9ce69eSAndrew Jones {
24*2f9ce69eSAndrew Jones 	int node = fdt_path_offset(dt_fdt(), "/rtas");
25*2f9ce69eSAndrew Jones 
26*2f9ce69eSAndrew Jones 	if (node < 0) {
27*2f9ce69eSAndrew Jones 		printf("%s: /rtas: %s\n", __func__, fdt_strerror(node));
28*2f9ce69eSAndrew Jones 		abort();
29*2f9ce69eSAndrew Jones 	}
30*2f9ce69eSAndrew Jones 
31*2f9ce69eSAndrew Jones 	return node;
32*2f9ce69eSAndrew Jones }
33*2f9ce69eSAndrew Jones 
34*2f9ce69eSAndrew Jones void rtas_init(void)
35*2f9ce69eSAndrew Jones {
36*2f9ce69eSAndrew Jones 	bool broken_sc1 = hcall_have_broken_sc1();
37*2f9ce69eSAndrew Jones 	int node = rtas_node(), len, words, i;
38*2f9ce69eSAndrew Jones 	const struct fdt_property *prop;
39*2f9ce69eSAndrew Jones 	u32 *data, *insns;
40*2f9ce69eSAndrew Jones 
41*2f9ce69eSAndrew Jones 	if (!dt_available()) {
42*2f9ce69eSAndrew Jones 		printf("%s: No device tree!\n", __func__);
43*2f9ce69eSAndrew Jones 		abort();
44*2f9ce69eSAndrew Jones 	}
45*2f9ce69eSAndrew Jones 
46*2f9ce69eSAndrew Jones 	prop = fdt_get_property(dt_fdt(), node,
47*2f9ce69eSAndrew Jones 				"linux,rtas-entry", &len);
48*2f9ce69eSAndrew Jones 	if (!prop) {
49*2f9ce69eSAndrew Jones 		printf("%s: /rtas/linux,rtas-entry: %s\n",
50*2f9ce69eSAndrew Jones 				__func__, fdt_strerror(len));
51*2f9ce69eSAndrew Jones 		abort();
52*2f9ce69eSAndrew Jones 	}
53*2f9ce69eSAndrew Jones 	data = (u32 *)prop->data;
54*2f9ce69eSAndrew Jones 	rtas_entry = (unsigned long)fdt32_to_cpu(*data);
55*2f9ce69eSAndrew Jones 	insns = (u32 *)rtas_entry;
56*2f9ce69eSAndrew Jones 
57*2f9ce69eSAndrew Jones 	prop = fdt_get_property(dt_fdt(), node, "rtas-size", &len);
58*2f9ce69eSAndrew Jones 	if (!prop) {
59*2f9ce69eSAndrew Jones 		printf("%s: /rtas/rtas-size: %s\n",
60*2f9ce69eSAndrew Jones 				__func__, fdt_strerror(len));
61*2f9ce69eSAndrew Jones 		abort();
62*2f9ce69eSAndrew Jones 	}
63*2f9ce69eSAndrew Jones 	data = (u32 *)prop->data;
64*2f9ce69eSAndrew Jones 	words = (int)fdt32_to_cpu(*data)/4;
65*2f9ce69eSAndrew Jones 
66*2f9ce69eSAndrew Jones 	for (i = 0; i < words; ++i) {
67*2f9ce69eSAndrew Jones 		if (broken_sc1 && insns[i] == cpu_to_be32(SC1))
68*2f9ce69eSAndrew Jones 			insns[i] = cpu_to_be32(SC1_REPLACEMENT);
69*2f9ce69eSAndrew Jones 	}
70*2f9ce69eSAndrew Jones }
71*2f9ce69eSAndrew Jones 
72*2f9ce69eSAndrew Jones int rtas_token(const char *service)
73*2f9ce69eSAndrew Jones {
74*2f9ce69eSAndrew Jones 	const struct fdt_property *prop;
75*2f9ce69eSAndrew Jones 	u32 *token;
76*2f9ce69eSAndrew Jones 
77*2f9ce69eSAndrew Jones 	prop = fdt_get_property(dt_fdt(), rtas_node(), service, NULL);
78*2f9ce69eSAndrew Jones 	if (prop) {
79*2f9ce69eSAndrew Jones 		token = (u32 *)prop->data;
80*2f9ce69eSAndrew Jones 		return fdt32_to_cpu(*token);
81*2f9ce69eSAndrew Jones 	}
82*2f9ce69eSAndrew Jones 	return RTAS_UNKNOWN_SERVICE;
83*2f9ce69eSAndrew Jones }
84*2f9ce69eSAndrew Jones 
85*2f9ce69eSAndrew Jones int rtas_call(int token, int nargs, int nret, int *outputs, ...)
86*2f9ce69eSAndrew Jones {
87*2f9ce69eSAndrew Jones 	va_list list;
88*2f9ce69eSAndrew Jones 	int ret, i;
89*2f9ce69eSAndrew Jones 
90*2f9ce69eSAndrew Jones 	spin_lock(&rtas_lock);
91*2f9ce69eSAndrew Jones 
92*2f9ce69eSAndrew Jones 	rtas_args.token = cpu_to_be32(token);
93*2f9ce69eSAndrew Jones 	rtas_args.nargs = cpu_to_be32(nargs);
94*2f9ce69eSAndrew Jones 	rtas_args.nret = cpu_to_be32(nret);
95*2f9ce69eSAndrew Jones 	rtas_args.rets = &rtas_args.args[nargs];
96*2f9ce69eSAndrew Jones 
97*2f9ce69eSAndrew Jones 	va_start(list, outputs);
98*2f9ce69eSAndrew Jones 	for (i = 0; i < nargs; ++i)
99*2f9ce69eSAndrew Jones 		rtas_args.args[i] = cpu_to_be32(va_arg(list, u32));
100*2f9ce69eSAndrew Jones 	va_end(list);
101*2f9ce69eSAndrew Jones 
102*2f9ce69eSAndrew Jones 	for (i = 0; i < nret; ++i)
103*2f9ce69eSAndrew Jones 		rtas_args.rets[i] = 0;
104*2f9ce69eSAndrew Jones 
105*2f9ce69eSAndrew Jones 	enter_rtas(__pa(&rtas_args));
106*2f9ce69eSAndrew Jones 
107*2f9ce69eSAndrew Jones 	if (nret > 1 && outputs != NULL)
108*2f9ce69eSAndrew Jones 		for (i = 0; i < nret - 1; ++i)
109*2f9ce69eSAndrew Jones 			outputs[i] = be32_to_cpu(rtas_args.rets[i + 1]);
110*2f9ce69eSAndrew Jones 
111*2f9ce69eSAndrew Jones 	ret = nret > 0 ? be32_to_cpu(rtas_args.rets[0]) : 0;
112*2f9ce69eSAndrew Jones 
113*2f9ce69eSAndrew Jones 	spin_unlock(&rtas_lock);
114*2f9ce69eSAndrew Jones 	return ret;
115*2f9ce69eSAndrew Jones }
116*2f9ce69eSAndrew Jones 
117*2f9ce69eSAndrew Jones void rtas_power_off(void)
118*2f9ce69eSAndrew Jones {
119*2f9ce69eSAndrew Jones 	int ret = rtas_call(rtas_token("power-off"), 2, 1, NULL, -1, -1);
120*2f9ce69eSAndrew Jones 	printf("RTAS power-off returned %d\n", ret);
121*2f9ce69eSAndrew Jones }
122