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