xref: /kvm-unit-tests/lib/powerpc/rtas.c (revision 2c96b77ec9d3b1fcec7525174e23a6240ee05949)
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 int rtas_call(int token, int nargs, int nret, int *outputs, ...)
91 {
92 	va_list list;
93 	int ret, i;
94 
95 	spin_lock(&rtas_lock);
96 
97 	rtas_args.token = cpu_to_be32(token);
98 	rtas_args.nargs = cpu_to_be32(nargs);
99 	rtas_args.nret = cpu_to_be32(nret);
100 	rtas_args.rets = &rtas_args.args[nargs];
101 
102 	va_start(list, outputs);
103 	for (i = 0; i < nargs; ++i)
104 		rtas_args.args[i] = cpu_to_be32(va_arg(list, u32));
105 	va_end(list);
106 
107 	for (i = 0; i < nret; ++i)
108 		rtas_args.rets[i] = 0;
109 
110 	enter_rtas(__pa(&rtas_args));
111 
112 	if (nret > 1 && outputs != NULL)
113 		for (i = 0; i < nret - 1; ++i)
114 			outputs[i] = be32_to_cpu(rtas_args.rets[i + 1]);
115 
116 	ret = nret > 0 ? be32_to_cpu(rtas_args.rets[0]) : 0;
117 
118 	spin_unlock(&rtas_lock);
119 	return ret;
120 }
121 
122 void rtas_power_off(void)
123 {
124 	uint32_t token;
125 	int ret;
126 
127 	ret = rtas_token("power-off", &token);
128 	if (ret) {
129 		puts("RTAS power-off not available\n");
130 		return;
131 	}
132 
133 	ret = rtas_call(token, 2, 1, NULL, -1, -1);
134 	printf("RTAS power-off returned %d\n", ret);
135 }
136