xref: /kvm-unit-tests/lib/powerpc/rtas.c (revision 2565dce15eb32678f45a2b126ce374b634eb2b6c)
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>
122f9ce69eSAndrew Jones #include <asm/hcall.h>
132f9ce69eSAndrew Jones #include <asm/io.h>
142f9ce69eSAndrew Jones #include <asm/rtas.h>
152f9ce69eSAndrew Jones 
162f9ce69eSAndrew Jones extern void enter_rtas(unsigned long);
172f9ce69eSAndrew Jones 
182f9ce69eSAndrew Jones unsigned long rtas_entry;
192f9ce69eSAndrew Jones static struct rtas_args rtas_args;
202f9ce69eSAndrew Jones static struct spinlock rtas_lock;
212f9ce69eSAndrew Jones 
222f9ce69eSAndrew Jones static int rtas_node(void)
232f9ce69eSAndrew Jones {
242f9ce69eSAndrew Jones 	int node = fdt_path_offset(dt_fdt(), "/rtas");
252f9ce69eSAndrew Jones 
262f9ce69eSAndrew Jones 	if (node < 0) {
272f9ce69eSAndrew Jones 		printf("%s: /rtas: %s\n", __func__, fdt_strerror(node));
282f9ce69eSAndrew Jones 		abort();
292f9ce69eSAndrew Jones 	}
302f9ce69eSAndrew Jones 
312f9ce69eSAndrew Jones 	return node;
322f9ce69eSAndrew Jones }
332f9ce69eSAndrew Jones 
342f9ce69eSAndrew Jones void rtas_init(void)
352f9ce69eSAndrew Jones {
362f9ce69eSAndrew Jones 	bool broken_sc1 = hcall_have_broken_sc1();
372f9ce69eSAndrew Jones 	int node = rtas_node(), len, words, i;
382f9ce69eSAndrew Jones 	const struct fdt_property *prop;
392f9ce69eSAndrew Jones 	u32 *data, *insns;
402f9ce69eSAndrew Jones 
412f9ce69eSAndrew Jones 	if (!dt_available()) {
422f9ce69eSAndrew Jones 		printf("%s: No device tree!\n", __func__);
432f9ce69eSAndrew Jones 		abort();
442f9ce69eSAndrew Jones 	}
452f9ce69eSAndrew Jones 
462f9ce69eSAndrew Jones 	prop = fdt_get_property(dt_fdt(), node,
472f9ce69eSAndrew Jones 				"linux,rtas-entry", &len);
482f9ce69eSAndrew Jones 	if (!prop) {
492f9ce69eSAndrew Jones 		printf("%s: /rtas/linux,rtas-entry: %s\n",
502f9ce69eSAndrew Jones 				__func__, fdt_strerror(len));
512f9ce69eSAndrew Jones 		abort();
522f9ce69eSAndrew Jones 	}
532f9ce69eSAndrew Jones 	data = (u32 *)prop->data;
542f9ce69eSAndrew Jones 	rtas_entry = (unsigned long)fdt32_to_cpu(*data);
552f9ce69eSAndrew Jones 	insns = (u32 *)rtas_entry;
562f9ce69eSAndrew Jones 
572f9ce69eSAndrew Jones 	prop = fdt_get_property(dt_fdt(), node, "rtas-size", &len);
582f9ce69eSAndrew Jones 	if (!prop) {
592f9ce69eSAndrew Jones 		printf("%s: /rtas/rtas-size: %s\n",
602f9ce69eSAndrew Jones 				__func__, fdt_strerror(len));
612f9ce69eSAndrew Jones 		abort();
622f9ce69eSAndrew Jones 	}
632f9ce69eSAndrew Jones 	data = (u32 *)prop->data;
642f9ce69eSAndrew Jones 	words = (int)fdt32_to_cpu(*data)/4;
652f9ce69eSAndrew Jones 
662f9ce69eSAndrew Jones 	for (i = 0; i < words; ++i) {
672f9ce69eSAndrew Jones 		if (broken_sc1 && insns[i] == cpu_to_be32(SC1))
682f9ce69eSAndrew Jones 			insns[i] = cpu_to_be32(SC1_REPLACEMENT);
692f9ce69eSAndrew Jones 	}
702f9ce69eSAndrew Jones }
712f9ce69eSAndrew Jones 
72*2565dce1SThomas Huth int rtas_token(const char *service, uint32_t *token)
732f9ce69eSAndrew Jones {
742f9ce69eSAndrew Jones 	const struct fdt_property *prop;
75*2565dce1SThomas Huth 	u32 *data;
76*2565dce1SThomas Huth 
77*2565dce1SThomas Huth 	if (!dt_available())
78*2565dce1SThomas Huth 		return RTAS_UNKNOWN_SERVICE;
792f9ce69eSAndrew Jones 
802f9ce69eSAndrew Jones 	prop = fdt_get_property(dt_fdt(), rtas_node(), service, NULL);
81*2565dce1SThomas Huth 	if (!prop)
822f9ce69eSAndrew Jones 		return RTAS_UNKNOWN_SERVICE;
83*2565dce1SThomas Huth 
84*2565dce1SThomas Huth 	data = (u32 *)prop->data;
85*2565dce1SThomas Huth 	*token = fdt32_to_cpu(*data);
86*2565dce1SThomas Huth 
87*2565dce1SThomas Huth 	return 0;
882f9ce69eSAndrew Jones }
892f9ce69eSAndrew Jones 
902f9ce69eSAndrew Jones int rtas_call(int token, int nargs, int nret, int *outputs, ...)
912f9ce69eSAndrew Jones {
922f9ce69eSAndrew Jones 	va_list list;
932f9ce69eSAndrew Jones 	int ret, i;
942f9ce69eSAndrew Jones 
952f9ce69eSAndrew Jones 	spin_lock(&rtas_lock);
962f9ce69eSAndrew Jones 
972f9ce69eSAndrew Jones 	rtas_args.token = cpu_to_be32(token);
982f9ce69eSAndrew Jones 	rtas_args.nargs = cpu_to_be32(nargs);
992f9ce69eSAndrew Jones 	rtas_args.nret = cpu_to_be32(nret);
1002f9ce69eSAndrew Jones 	rtas_args.rets = &rtas_args.args[nargs];
1012f9ce69eSAndrew Jones 
1022f9ce69eSAndrew Jones 	va_start(list, outputs);
1032f9ce69eSAndrew Jones 	for (i = 0; i < nargs; ++i)
1042f9ce69eSAndrew Jones 		rtas_args.args[i] = cpu_to_be32(va_arg(list, u32));
1052f9ce69eSAndrew Jones 	va_end(list);
1062f9ce69eSAndrew Jones 
1072f9ce69eSAndrew Jones 	for (i = 0; i < nret; ++i)
1082f9ce69eSAndrew Jones 		rtas_args.rets[i] = 0;
1092f9ce69eSAndrew Jones 
1102f9ce69eSAndrew Jones 	enter_rtas(__pa(&rtas_args));
1112f9ce69eSAndrew Jones 
1122f9ce69eSAndrew Jones 	if (nret > 1 && outputs != NULL)
1132f9ce69eSAndrew Jones 		for (i = 0; i < nret - 1; ++i)
1142f9ce69eSAndrew Jones 			outputs[i] = be32_to_cpu(rtas_args.rets[i + 1]);
1152f9ce69eSAndrew Jones 
1162f9ce69eSAndrew Jones 	ret = nret > 0 ? be32_to_cpu(rtas_args.rets[0]) : 0;
1172f9ce69eSAndrew Jones 
1182f9ce69eSAndrew Jones 	spin_unlock(&rtas_lock);
1192f9ce69eSAndrew Jones 	return ret;
1202f9ce69eSAndrew Jones }
1212f9ce69eSAndrew Jones 
1222f9ce69eSAndrew Jones void rtas_power_off(void)
1232f9ce69eSAndrew Jones {
124*2565dce1SThomas Huth 	uint32_t token;
125*2565dce1SThomas Huth 	int ret;
126*2565dce1SThomas Huth 
127*2565dce1SThomas Huth 	ret = rtas_token("power-off", &token);
128*2565dce1SThomas Huth 	if (ret) {
129*2565dce1SThomas Huth 		puts("RTAS power-off not available\n");
130*2565dce1SThomas Huth 		return;
131*2565dce1SThomas Huth 	}
132*2565dce1SThomas Huth 
133*2565dce1SThomas Huth 	ret = rtas_call(token, 2, 1, NULL, -1, -1);
1342f9ce69eSAndrew Jones 	printf("RTAS power-off returned %d\n", ret);
1352f9ce69eSAndrew Jones }
136