xref: /kvm-unit-tests/lib/powerpc/rtas.c (revision 95a9408860fc8dacb73e9b302fb96536f91d5ccf)
1 /*
2  * powerpc RTAS
3  *
4  * Copyright (C) 2016, Red Hat Inc, Andrew Jones <drjones@redhat.com>
5  *
6  * This work is licensed under the terms of the GNU LGPL, version 2.
7  */
8 #include <libcflat.h>
9 #include <libfdt/libfdt.h>
10 #include <devicetree.h>
11 #include <asm/spinlock.h>
12 #include <asm/hcall.h>
13 #include <asm/io.h>
14 #include <asm/rtas.h>
15 
16 extern void enter_rtas(unsigned long);
17 
18 unsigned long rtas_entry;
19 static struct rtas_args rtas_args;
20 static struct spinlock rtas_lock;
21 
22 static int rtas_node(void)
23 {
24 	int node = fdt_path_offset(dt_fdt(), "/rtas");
25 
26 	if (node < 0) {
27 		printf("%s: /rtas: %s\n", __func__, fdt_strerror(node));
28 		abort();
29 	}
30 
31 	return node;
32 }
33 
34 void rtas_init(void)
35 {
36 	bool broken_sc1 = hcall_have_broken_sc1();
37 	int node = rtas_node(), len, words, i;
38 	const struct fdt_property *prop;
39 	u32 *data, *insns;
40 
41 	if (!dt_available()) {
42 		printf("%s: No device tree!\n", __func__);
43 		abort();
44 	}
45 
46 	prop = fdt_get_property(dt_fdt(), node,
47 				"linux,rtas-entry", &len);
48 	if (!prop) {
49 		/* We don't have a qemu provided RTAS blob, enter_rtas
50 		 * will use H_RTAS directly */
51 		return;
52 	}
53 	data = (u32 *)prop->data;
54 	rtas_entry = (unsigned long)fdt32_to_cpu(*data);
55 	insns = (u32 *)rtas_entry;
56 
57 	prop = fdt_get_property(dt_fdt(), node, "rtas-size", &len);
58 	if (!prop) {
59 		printf("%s: /rtas/rtas-size: %s\n",
60 				__func__, fdt_strerror(len));
61 		abort();
62 	}
63 	data = (u32 *)prop->data;
64 	words = (int)fdt32_to_cpu(*data)/4;
65 
66 	for (i = 0; i < words; ++i) {
67 		if (broken_sc1 && insns[i] == cpu_to_be32(SC1))
68 			insns[i] = cpu_to_be32(SC1_REPLACEMENT);
69 	}
70 }
71 
72 int rtas_token(const char *service, uint32_t *token)
73 {
74 	const struct fdt_property *prop;
75 	u32 *data;
76 
77 	if (!dt_available())
78 		return RTAS_UNKNOWN_SERVICE;
79 
80 	prop = fdt_get_property(dt_fdt(), rtas_node(), service, NULL);
81 	if (!prop)
82 		return RTAS_UNKNOWN_SERVICE;
83 
84 	data = (u32 *)prop->data;
85 	*token = fdt32_to_cpu(*data);
86 
87 	return 0;
88 }
89 
90 static void __rtas_call(struct rtas_args *args)
91 {
92 	enter_rtas(__pa(args));
93 }
94 
95 static int rtas_call_unlocked_va(struct rtas_args *args,
96 			  int token, int nargs, int nret, int *outputs,
97 			  va_list list)
98 {
99 	int ret, i;
100 
101 	args->token = cpu_to_be32(token);
102 	args->nargs = cpu_to_be32(nargs);
103 	args->nret = cpu_to_be32(nret);
104 	args->rets = &args->args[nargs];
105 
106 	for (i = 0; i < nargs; ++i)
107 		args->args[i] = cpu_to_be32(va_arg(list, u32));
108 
109 	for (i = 0; i < nret; ++i)
110 		args->rets[i] = 0;
111 
112 	__rtas_call(args);
113 
114 	if (nret > 1 && outputs != NULL)
115 		for (i = 0; i < nret - 1; ++i)
116 			outputs[i] = be32_to_cpu(args->rets[i + 1]);
117 
118 	ret = nret > 0 ? be32_to_cpu(args->rets[0]) : 0;
119 
120 	return ret;
121 }
122 
123 int rtas_call_unlocked(struct rtas_args *args, int token, int nargs, int nret, int *outputs, ...)
124 {
125 	va_list list;
126 	int ret;
127 
128 	va_start(list, outputs);
129 	ret = rtas_call_unlocked_va(args, token, nargs, nret, outputs, list);
130 	va_end(list);
131 
132 	return ret;
133 }
134 
135 int rtas_call(int token, int nargs, int nret, int *outputs, ...)
136 {
137 	va_list list;
138 	int ret;
139 
140 	spin_lock(&rtas_lock);
141 
142 	va_start(list, outputs);
143 	ret = rtas_call_unlocked_va(&rtas_args, token, nargs, nret, outputs, list);
144 	va_end(list);
145 
146 	spin_unlock(&rtas_lock);
147 
148 	return ret;
149 }
150 
151 void rtas_stop_self(void)
152 {
153 	struct rtas_args args;
154 	uint32_t token;
155 	int ret;
156 
157 	ret = rtas_token("stop-self", &token);
158 	if (ret) {
159 		puts("RTAS stop-self not available\n");
160 		return;
161 	}
162 
163 	ret = rtas_call_unlocked(&args, token, 0, 1, NULL);
164 	printf("RTAS stop-self returned %d\n", ret);
165 }
166 
167 void rtas_power_off(void)
168 {
169 	struct rtas_args args;
170 	uint32_t token;
171 	int ret;
172 
173 	ret = rtas_token("power-off", &token);
174 	if (ret) {
175 		puts("RTAS power-off not available\n");
176 		return;
177 	}
178 
179 	ret = rtas_call_unlocked(&args, token, 2, 1, NULL, -1, -1);
180 	printf("RTAS power-off returned %d\n", ret);
181 }
182