xref: /kvm-unit-tests/lib/powerpc/rtas.c (revision 93c847c1e5cbe266496ee66dc83dcfa24c401c96)
12f9ce69eSAndrew Jones /*
22f9ce69eSAndrew Jones  * powerpc RTAS
32f9ce69eSAndrew Jones  *
42f9ce69eSAndrew Jones  * Copyright (C) 2016, Red Hat Inc, Andrew Jones <drjones@redhat.com>
52f9ce69eSAndrew Jones  *
62f9ce69eSAndrew Jones  * This work is licensed under the terms of the GNU LGPL, version 2.
72f9ce69eSAndrew Jones  */
82f9ce69eSAndrew Jones #include <libcflat.h>
92f9ce69eSAndrew Jones #include <libfdt/libfdt.h>
102f9ce69eSAndrew Jones #include <devicetree.h>
112f9ce69eSAndrew Jones #include <asm/spinlock.h>
12*93c847c1SNicholas Piggin #include <asm/smp.h>
132f9ce69eSAndrew Jones #include <asm/hcall.h>
142f9ce69eSAndrew Jones #include <asm/io.h>
152f9ce69eSAndrew Jones #include <asm/rtas.h>
162f9ce69eSAndrew Jones 
172f9ce69eSAndrew Jones extern void enter_rtas(unsigned long);
182f9ce69eSAndrew Jones 
192f9ce69eSAndrew Jones unsigned long rtas_entry;
202f9ce69eSAndrew Jones static struct rtas_args rtas_args;
212f9ce69eSAndrew Jones static struct spinlock rtas_lock;
222f9ce69eSAndrew Jones 
rtas_node(void)232f9ce69eSAndrew Jones static int rtas_node(void)
242f9ce69eSAndrew Jones {
252f9ce69eSAndrew Jones 	int node = fdt_path_offset(dt_fdt(), "/rtas");
262f9ce69eSAndrew Jones 
272f9ce69eSAndrew Jones 	if (node < 0) {
282f9ce69eSAndrew Jones 		printf("%s: /rtas: %s\n", __func__, fdt_strerror(node));
292f9ce69eSAndrew Jones 		abort();
302f9ce69eSAndrew Jones 	}
312f9ce69eSAndrew Jones 
322f9ce69eSAndrew Jones 	return node;
332f9ce69eSAndrew Jones }
342f9ce69eSAndrew Jones 
rtas_init(void)352f9ce69eSAndrew Jones void rtas_init(void)
362f9ce69eSAndrew Jones {
372f9ce69eSAndrew Jones 	bool broken_sc1 = hcall_have_broken_sc1();
382f9ce69eSAndrew Jones 	int node = rtas_node(), len, words, i;
392f9ce69eSAndrew Jones 	const struct fdt_property *prop;
402f9ce69eSAndrew Jones 	u32 *data, *insns;
412f9ce69eSAndrew Jones 
422f9ce69eSAndrew Jones 	if (!dt_available()) {
432f9ce69eSAndrew Jones 		printf("%s: No device tree!\n", __func__);
442f9ce69eSAndrew Jones 		abort();
452f9ce69eSAndrew Jones 	}
462f9ce69eSAndrew Jones 
472f9ce69eSAndrew Jones 	prop = fdt_get_property(dt_fdt(), node,
482f9ce69eSAndrew Jones 				"linux,rtas-entry", &len);
492f9ce69eSAndrew Jones 	if (!prop) {
50141b4584SDavid Gibson 		/* We don't have a qemu provided RTAS blob, enter_rtas
51141b4584SDavid Gibson 		 * will use H_RTAS directly */
52141b4584SDavid Gibson 		return;
532f9ce69eSAndrew Jones 	}
542f9ce69eSAndrew Jones 	data = (u32 *)prop->data;
552f9ce69eSAndrew Jones 	rtas_entry = (unsigned long)fdt32_to_cpu(*data);
562f9ce69eSAndrew Jones 	insns = (u32 *)rtas_entry;
572f9ce69eSAndrew Jones 
582f9ce69eSAndrew Jones 	prop = fdt_get_property(dt_fdt(), node, "rtas-size", &len);
592f9ce69eSAndrew Jones 	if (!prop) {
602f9ce69eSAndrew Jones 		printf("%s: /rtas/rtas-size: %s\n",
612f9ce69eSAndrew Jones 				__func__, fdt_strerror(len));
622f9ce69eSAndrew Jones 		abort();
632f9ce69eSAndrew Jones 	}
642f9ce69eSAndrew Jones 	data = (u32 *)prop->data;
652f9ce69eSAndrew Jones 	words = (int)fdt32_to_cpu(*data)/4;
662f9ce69eSAndrew Jones 
672f9ce69eSAndrew Jones 	for (i = 0; i < words; ++i) {
682f9ce69eSAndrew Jones 		if (broken_sc1 && insns[i] == cpu_to_be32(SC1))
692f9ce69eSAndrew Jones 			insns[i] = cpu_to_be32(SC1_REPLACEMENT);
702f9ce69eSAndrew Jones 	}
712f9ce69eSAndrew Jones }
722f9ce69eSAndrew Jones 
rtas_token(const char * service,uint32_t * token)732565dce1SThomas Huth int rtas_token(const char *service, uint32_t *token)
742f9ce69eSAndrew Jones {
752f9ce69eSAndrew Jones 	const struct fdt_property *prop;
762565dce1SThomas Huth 	u32 *data;
772565dce1SThomas Huth 
782565dce1SThomas Huth 	if (!dt_available())
792565dce1SThomas Huth 		return RTAS_UNKNOWN_SERVICE;
802f9ce69eSAndrew Jones 
812f9ce69eSAndrew Jones 	prop = fdt_get_property(dt_fdt(), rtas_node(), service, NULL);
822565dce1SThomas Huth 	if (!prop)
832f9ce69eSAndrew Jones 		return RTAS_UNKNOWN_SERVICE;
842565dce1SThomas Huth 
852565dce1SThomas Huth 	data = (u32 *)prop->data;
862565dce1SThomas Huth 	*token = fdt32_to_cpu(*data);
872565dce1SThomas Huth 
882565dce1SThomas Huth 	return 0;
892f9ce69eSAndrew Jones }
902f9ce69eSAndrew Jones 
__rtas_call(struct rtas_args * args)91789a8e69SNicholas Piggin static void __rtas_call(struct rtas_args *args)
922f9ce69eSAndrew Jones {
93789a8e69SNicholas Piggin 	enter_rtas(__pa(args));
94789a8e69SNicholas Piggin }
95789a8e69SNicholas Piggin 
rtas_call_unlocked_va(struct rtas_args * args,int token,int nargs,int nret,int * outputs,va_list list)96789a8e69SNicholas Piggin static int rtas_call_unlocked_va(struct rtas_args *args,
97789a8e69SNicholas Piggin 			  int token, int nargs, int nret, int *outputs,
98789a8e69SNicholas Piggin 			  va_list list)
99789a8e69SNicholas Piggin {
1002f9ce69eSAndrew Jones 	int ret, i;
1012f9ce69eSAndrew Jones 
102789a8e69SNicholas Piggin 	args->token = cpu_to_be32(token);
103789a8e69SNicholas Piggin 	args->nargs = cpu_to_be32(nargs);
104789a8e69SNicholas Piggin 	args->nret = cpu_to_be32(nret);
105789a8e69SNicholas Piggin 	args->rets = &args->args[nargs];
1062f9ce69eSAndrew Jones 
1072f9ce69eSAndrew Jones 	for (i = 0; i < nargs; ++i)
108789a8e69SNicholas Piggin 		args->args[i] = cpu_to_be32(va_arg(list, u32));
1092f9ce69eSAndrew Jones 
1102f9ce69eSAndrew Jones 	for (i = 0; i < nret; ++i)
111789a8e69SNicholas Piggin 		args->rets[i] = 0;
1122f9ce69eSAndrew Jones 
113789a8e69SNicholas Piggin 	__rtas_call(args);
1142f9ce69eSAndrew Jones 
1152f9ce69eSAndrew Jones 	if (nret > 1 && outputs != NULL)
1162f9ce69eSAndrew Jones 		for (i = 0; i < nret - 1; ++i)
117789a8e69SNicholas Piggin 			outputs[i] = be32_to_cpu(args->rets[i + 1]);
1182f9ce69eSAndrew Jones 
119789a8e69SNicholas Piggin 	ret = nret > 0 ? be32_to_cpu(args->rets[0]) : 0;
120789a8e69SNicholas Piggin 
121789a8e69SNicholas Piggin 	return ret;
122789a8e69SNicholas Piggin }
123789a8e69SNicholas Piggin 
rtas_call_unlocked(struct rtas_args * args,int token,int nargs,int nret,int * outputs,...)124789a8e69SNicholas Piggin int rtas_call_unlocked(struct rtas_args *args, int token, int nargs, int nret, int *outputs, ...)
125789a8e69SNicholas Piggin {
126789a8e69SNicholas Piggin 	va_list list;
127789a8e69SNicholas Piggin 	int ret;
128789a8e69SNicholas Piggin 
129789a8e69SNicholas Piggin 	va_start(list, outputs);
130789a8e69SNicholas Piggin 	ret = rtas_call_unlocked_va(args, token, nargs, nret, outputs, list);
131789a8e69SNicholas Piggin 	va_end(list);
132789a8e69SNicholas Piggin 
133789a8e69SNicholas Piggin 	return ret;
134789a8e69SNicholas Piggin }
135789a8e69SNicholas Piggin 
rtas_call(int token,int nargs,int nret,int * outputs,...)136789a8e69SNicholas Piggin int rtas_call(int token, int nargs, int nret, int *outputs, ...)
137789a8e69SNicholas Piggin {
138789a8e69SNicholas Piggin 	va_list list;
139789a8e69SNicholas Piggin 	int ret;
140789a8e69SNicholas Piggin 
141*93c847c1SNicholas Piggin 	assert_msg(!in_usermode(), "May not make RTAS call from user mode\n");
142*93c847c1SNicholas Piggin 
143789a8e69SNicholas Piggin 	spin_lock(&rtas_lock);
144789a8e69SNicholas Piggin 
145789a8e69SNicholas Piggin 	va_start(list, outputs);
146789a8e69SNicholas Piggin 	ret = rtas_call_unlocked_va(&rtas_args, token, nargs, nret, outputs, list);
147789a8e69SNicholas Piggin 	va_end(list);
1482f9ce69eSAndrew Jones 
1492f9ce69eSAndrew Jones 	spin_unlock(&rtas_lock);
150789a8e69SNicholas Piggin 
1512f9ce69eSAndrew Jones 	return ret;
1522f9ce69eSAndrew Jones }
1532f9ce69eSAndrew Jones 
rtas_stop_self(void)154789a8e69SNicholas Piggin void rtas_stop_self(void)
155789a8e69SNicholas Piggin {
156789a8e69SNicholas Piggin 	struct rtas_args args;
157789a8e69SNicholas Piggin 	uint32_t token;
158789a8e69SNicholas Piggin 	int ret;
159789a8e69SNicholas Piggin 
160789a8e69SNicholas Piggin 	ret = rtas_token("stop-self", &token);
161789a8e69SNicholas Piggin 	if (ret) {
162789a8e69SNicholas Piggin 		puts("RTAS stop-self not available\n");
163789a8e69SNicholas Piggin 		return;
164789a8e69SNicholas Piggin 	}
165789a8e69SNicholas Piggin 
166789a8e69SNicholas Piggin 	ret = rtas_call_unlocked(&args, token, 0, 1, NULL);
167789a8e69SNicholas Piggin 	printf("RTAS stop-self returned %d\n", ret);
168789a8e69SNicholas Piggin }
169789a8e69SNicholas Piggin 
rtas_power_off(void)1702f9ce69eSAndrew Jones void rtas_power_off(void)
1712f9ce69eSAndrew Jones {
172789a8e69SNicholas Piggin 	struct rtas_args args;
1732565dce1SThomas Huth 	uint32_t token;
1742565dce1SThomas Huth 	int ret;
1752565dce1SThomas Huth 
1762565dce1SThomas Huth 	ret = rtas_token("power-off", &token);
1772565dce1SThomas Huth 	if (ret) {
1782565dce1SThomas Huth 		puts("RTAS power-off not available\n");
1792565dce1SThomas Huth 		return;
1802565dce1SThomas Huth 	}
1812565dce1SThomas Huth 
182789a8e69SNicholas Piggin 	ret = rtas_call_unlocked(&args, token, 2, 1, NULL, -1, -1);
1832f9ce69eSAndrew Jones 	printf("RTAS power-off returned %d\n", ret);
1842f9ce69eSAndrew Jones }
185