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 static void __rtas_call(struct rtas_args *args) 91 { 92 enter_rtas(__pa(args)); 93 } 94 95 static int rtas_call_unlocked_va(struct rtas_args *args, 96 int token, int nargs, int nret, int *outputs, 97 va_list list) 98 { 99 int ret, i; 100 101 args->token = cpu_to_be32(token); 102 args->nargs = cpu_to_be32(nargs); 103 args->nret = cpu_to_be32(nret); 104 args->rets = &args->args[nargs]; 105 106 for (i = 0; i < nargs; ++i) 107 args->args[i] = cpu_to_be32(va_arg(list, u32)); 108 109 for (i = 0; i < nret; ++i) 110 args->rets[i] = 0; 111 112 __rtas_call(args); 113 114 if (nret > 1 && outputs != NULL) 115 for (i = 0; i < nret - 1; ++i) 116 outputs[i] = be32_to_cpu(args->rets[i + 1]); 117 118 ret = nret > 0 ? be32_to_cpu(args->rets[0]) : 0; 119 120 return ret; 121 } 122 123 int rtas_call_unlocked(struct rtas_args *args, int token, int nargs, int nret, int *outputs, ...) 124 { 125 va_list list; 126 int ret; 127 128 va_start(list, outputs); 129 ret = rtas_call_unlocked_va(args, token, nargs, nret, outputs, list); 130 va_end(list); 131 132 return ret; 133 } 134 135 int rtas_call(int token, int nargs, int nret, int *outputs, ...) 136 { 137 va_list list; 138 int ret; 139 140 spin_lock(&rtas_lock); 141 142 va_start(list, outputs); 143 ret = rtas_call_unlocked_va(&rtas_args, token, nargs, nret, outputs, list); 144 va_end(list); 145 146 spin_unlock(&rtas_lock); 147 148 return ret; 149 } 150 151 void rtas_stop_self(void) 152 { 153 struct rtas_args args; 154 uint32_t token; 155 int ret; 156 157 ret = rtas_token("stop-self", &token); 158 if (ret) { 159 puts("RTAS stop-self not available\n"); 160 return; 161 } 162 163 ret = rtas_call_unlocked(&args, token, 0, 1, NULL); 164 printf("RTAS stop-self returned %d\n", ret); 165 } 166 167 void rtas_power_off(void) 168 { 169 struct rtas_args args; 170 uint32_t token; 171 int ret; 172 173 ret = rtas_token("power-off", &token); 174 if (ret) { 175 puts("RTAS power-off not available\n"); 176 return; 177 } 178 179 ret = rtas_call_unlocked(&args, token, 2, 1, NULL, -1, -1); 180 printf("RTAS power-off returned %d\n", ret); 181 } 182