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