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