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/smp.h> 13 #include <asm/hcall.h> 14 #include <asm/io.h> 15 #include <asm/rtas.h> 16 17 extern void enter_rtas(unsigned long); 18 19 unsigned long rtas_entry; 20 static struct rtas_args rtas_args; 21 static struct spinlock rtas_lock; 22 23 static int rtas_node(void) 24 { 25 int node = fdt_path_offset(dt_fdt(), "/rtas"); 26 27 if (node < 0) { 28 printf("%s: /rtas: %s\n", __func__, fdt_strerror(node)); 29 abort(); 30 } 31 32 return node; 33 } 34 35 void rtas_init(void) 36 { 37 bool broken_sc1 = hcall_have_broken_sc1(); 38 int node = rtas_node(), len, words, i; 39 const struct fdt_property *prop; 40 u32 *data, *insns; 41 42 if (!dt_available()) { 43 printf("%s: No device tree!\n", __func__); 44 abort(); 45 } 46 47 prop = fdt_get_property(dt_fdt(), node, 48 "linux,rtas-entry", &len); 49 if (!prop) { 50 /* We don't have a qemu provided RTAS blob, enter_rtas 51 * will use H_RTAS directly */ 52 return; 53 } 54 data = (u32 *)prop->data; 55 rtas_entry = (unsigned long)fdt32_to_cpu(*data); 56 insns = (u32 *)rtas_entry; 57 58 prop = fdt_get_property(dt_fdt(), node, "rtas-size", &len); 59 if (!prop) { 60 printf("%s: /rtas/rtas-size: %s\n", 61 __func__, fdt_strerror(len)); 62 abort(); 63 } 64 data = (u32 *)prop->data; 65 words = (int)fdt32_to_cpu(*data)/4; 66 67 for (i = 0; i < words; ++i) { 68 if (broken_sc1 && insns[i] == cpu_to_be32(SC1)) 69 insns[i] = cpu_to_be32(SC1_REPLACEMENT); 70 } 71 } 72 73 int rtas_token(const char *service, uint32_t *token) 74 { 75 const struct fdt_property *prop; 76 u32 *data; 77 78 if (!dt_available()) 79 return RTAS_UNKNOWN_SERVICE; 80 81 prop = fdt_get_property(dt_fdt(), rtas_node(), service, NULL); 82 if (!prop) 83 return RTAS_UNKNOWN_SERVICE; 84 85 data = (u32 *)prop->data; 86 *token = fdt32_to_cpu(*data); 87 88 return 0; 89 } 90 91 static void __rtas_call(struct rtas_args *args) 92 { 93 enter_rtas(__pa(args)); 94 } 95 96 static int rtas_call_unlocked_va(struct rtas_args *args, 97 int token, int nargs, int nret, int *outputs, 98 va_list list) 99 { 100 int ret, i; 101 102 args->token = cpu_to_be32(token); 103 args->nargs = cpu_to_be32(nargs); 104 args->nret = cpu_to_be32(nret); 105 args->rets = &args->args[nargs]; 106 107 for (i = 0; i < nargs; ++i) 108 args->args[i] = cpu_to_be32(va_arg(list, u32)); 109 110 for (i = 0; i < nret; ++i) 111 args->rets[i] = 0; 112 113 __rtas_call(args); 114 115 if (nret > 1 && outputs != NULL) 116 for (i = 0; i < nret - 1; ++i) 117 outputs[i] = be32_to_cpu(args->rets[i + 1]); 118 119 ret = nret > 0 ? be32_to_cpu(args->rets[0]) : 0; 120 121 return ret; 122 } 123 124 int rtas_call_unlocked(struct rtas_args *args, int token, int nargs, int nret, int *outputs, ...) 125 { 126 va_list list; 127 int ret; 128 129 va_start(list, outputs); 130 ret = rtas_call_unlocked_va(args, token, nargs, nret, outputs, list); 131 va_end(list); 132 133 return ret; 134 } 135 136 int rtas_call(int token, int nargs, int nret, int *outputs, ...) 137 { 138 va_list list; 139 int ret; 140 141 assert_msg(!in_usermode(), "May not make RTAS call from user mode\n"); 142 143 spin_lock(&rtas_lock); 144 145 va_start(list, outputs); 146 ret = rtas_call_unlocked_va(&rtas_args, token, nargs, nret, outputs, list); 147 va_end(list); 148 149 spin_unlock(&rtas_lock); 150 151 return ret; 152 } 153 154 void rtas_stop_self(void) 155 { 156 struct rtas_args args; 157 uint32_t token; 158 int ret; 159 160 ret = rtas_token("stop-self", &token); 161 if (ret) { 162 puts("RTAS stop-self not available\n"); 163 return; 164 } 165 166 ret = rtas_call_unlocked(&args, token, 0, 1, NULL); 167 printf("RTAS stop-self returned %d\n", ret); 168 } 169 170 void rtas_power_off(void) 171 { 172 struct rtas_args args; 173 uint32_t token; 174 int ret; 175 176 ret = rtas_token("power-off", &token); 177 if (ret) { 178 puts("RTAS power-off not available\n"); 179 return; 180 } 181 182 ret = rtas_call_unlocked(&args, token, 2, 1, NULL, -1, -1); 183 printf("RTAS power-off returned %d\n", ret); 184 } 185