| 1 | /* |
|---|
| 2 | * This program is free software; you can redistribute it and/or modify |
|---|
| 3 | * it under the terms of the GNU General Public License as published by |
|---|
| 4 | * the Free Software Foundation; either version 2 of the License, or |
|---|
| 5 | * (at your option) any later version. |
|---|
| 6 | * |
|---|
| 7 | * This program is distributed in the hope that it will be useful, |
|---|
| 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 10 | * GNU General Public License for more details. |
|---|
| 11 | * |
|---|
| 12 | * You should have received a copy of the GNU General Public License |
|---|
| 13 | * along with this program; if not, write to the Free Software |
|---|
| 14 | * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|---|
| 15 | * |
|---|
| 16 | * Copyright (C) IBM Corp. 2005 |
|---|
| 17 | * |
|---|
| 18 | * Authors: Jimi Xenidis <jimix@watson.ibm.com> |
|---|
| 19 | */ |
|---|
| 20 | |
|---|
| 21 | #include "ofh.h" |
|---|
| 22 | #include <stdarg.h> |
|---|
| 23 | #include <xen/lib.h> |
|---|
| 24 | |
|---|
| 25 | /* |
|---|
| 26 | * 6.3.1 Access to the client interface functions |
|---|
| 27 | * This is the spec'd maximum |
|---|
| 28 | */ |
|---|
| 29 | #define PFW_MAXSRVCLEN 31 |
|---|
| 30 | |
|---|
| 31 | static u32 ofh_maxsrvclen; |
|---|
| 32 | |
|---|
| 33 | extern s32 debug(const char *fmt, ...); |
|---|
| 34 | |
|---|
| 35 | s32 debug(const char *fmt, ...) |
|---|
| 36 | { |
|---|
| 37 | s32 sz; |
|---|
| 38 | va_list ap; |
|---|
| 39 | char buf[512]; |
|---|
| 40 | va_start(ap, fmt); |
|---|
| 41 | sz = vsnprintf(buf, 512, fmt, ap); |
|---|
| 42 | va_end(ap); |
|---|
| 43 | ofh_cons_write(buf, sz, &sz); |
|---|
| 44 | |
|---|
| 45 | return sz; |
|---|
| 46 | } |
|---|
| 47 | |
|---|
| 48 | |
|---|
| 49 | |
|---|
| 50 | void |
|---|
| 51 | assprint(const char *expr, const char *file, int line, const char *fmt, ...) |
|---|
| 52 | { |
|---|
| 53 | char a[15]; |
|---|
| 54 | |
|---|
| 55 | a[0] = '\n'; |
|---|
| 56 | a[1] = '\n'; |
|---|
| 57 | a[2] = 'O'; |
|---|
| 58 | a[3] = 'F'; |
|---|
| 59 | a[4] = 'H'; |
|---|
| 60 | a[5] = ':'; |
|---|
| 61 | a[6] = 'A'; |
|---|
| 62 | a[7] = 'S'; |
|---|
| 63 | a[8] = 'S'; |
|---|
| 64 | a[9] = 'E'; |
|---|
| 65 | a[10] = 'R'; |
|---|
| 66 | a[11] = 'T'; |
|---|
| 67 | a[12] = '!'; |
|---|
| 68 | a[13] = '\n'; |
|---|
| 69 | a[14] = '\n'; |
|---|
| 70 | |
|---|
| 71 | s32 actual; |
|---|
| 72 | u32 t = 1; |
|---|
| 73 | volatile u32 *tp = &t; |
|---|
| 74 | |
|---|
| 75 | (void)expr; (void)file; (void)line; (void)fmt; |
|---|
| 76 | |
|---|
| 77 | ofh_cons_write(a, sizeof (a), &actual); |
|---|
| 78 | |
|---|
| 79 | /* maybe I can break out of this loop manually (like with a |
|---|
| 80 | * debugger) */ |
|---|
| 81 | while (*tp) { |
|---|
| 82 | continue; |
|---|
| 83 | } |
|---|
| 84 | } |
|---|
| 85 | |
|---|
| 86 | /* |
|---|
| 87 | * we use elf hash since it is pretty standard |
|---|
| 88 | */ |
|---|
| 89 | static u32 |
|---|
| 90 | of_hash(const char *s) |
|---|
| 91 | { |
|---|
| 92 | u32 hash = 0; |
|---|
| 93 | u32 hnib; |
|---|
| 94 | |
|---|
| 95 | if (s != NULL) { |
|---|
| 96 | while (*s != '\0') { |
|---|
| 97 | hash = (hash << 4) + *s++; |
|---|
| 98 | hnib = hash & 0xf0000000UL; |
|---|
| 99 | if (hnib != 0) { |
|---|
| 100 | hash ^= hnib >> 24; |
|---|
| 101 | } |
|---|
| 102 | hash &= ~hnib; |
|---|
| 103 | } |
|---|
| 104 | } |
|---|
| 105 | return hash; |
|---|
| 106 | } |
|---|
| 107 | |
|---|
| 108 | static void |
|---|
| 109 | ofh_service_init(ulong b) |
|---|
| 110 | { |
|---|
| 111 | ulong sz; |
|---|
| 112 | int i; |
|---|
| 113 | int j = 0; |
|---|
| 114 | struct ofh_srvc *o; |
|---|
| 115 | struct ofh_srvc *ofs[] = { |
|---|
| 116 | DRELA(&ofh_srvc[0], b), |
|---|
| 117 | DRELA(&ofh_isa_srvc[0], b), |
|---|
| 118 | NULL |
|---|
| 119 | }; |
|---|
| 120 | |
|---|
| 121 | j = 0; |
|---|
| 122 | while (ofs[j] != NULL) { |
|---|
| 123 | /* find the maximum string length for services */ |
|---|
| 124 | o = &ofs[j][0]; |
|---|
| 125 | while (o->ofs_name != NULL) { |
|---|
| 126 | const char *n; |
|---|
| 127 | |
|---|
| 128 | n = DRELA(&o->ofs_name[0], b); |
|---|
| 129 | /* fix it up so we don't have to fix it anymore */ |
|---|
| 130 | o->ofs_name = n; |
|---|
| 131 | |
|---|
| 132 | sz = strlen(n); |
|---|
| 133 | if (sz > *DRELA(&ofh_maxsrvclen, b)) { |
|---|
| 134 | *DRELA(&ofh_maxsrvclen, b) = sz; |
|---|
| 135 | } |
|---|
| 136 | o->ofs_hash = |
|---|
| 137 | of_hash(n); |
|---|
| 138 | ++i; |
|---|
| 139 | ++o; |
|---|
| 140 | } |
|---|
| 141 | ++j; |
|---|
| 142 | } |
|---|
| 143 | } |
|---|
| 144 | |
|---|
| 145 | |
|---|
| 146 | static void |
|---|
| 147 | ofh_cpu_init(ofdn_t chosen, ulong b) |
|---|
| 148 | { |
|---|
| 149 | static struct ofh_ihandle _ih_cpu_0; |
|---|
| 150 | void *mem = ofd_mem(b); |
|---|
| 151 | u32 ih = DRELA((ulong)&_ih_cpu_0, b); |
|---|
| 152 | struct ofh_ihandle *ihp = (struct ofh_ihandle *)((ulong)ih); |
|---|
| 153 | const char *cpu_type = DRELA((const char*)"cpu",b); |
|---|
| 154 | |
|---|
| 155 | ofdn_t cpu = ofd_node_find_by_prop(mem, OFD_ROOT, |
|---|
| 156 | DRELA((const char*)"device_type",b), |
|---|
| 157 | cpu_type, 4); |
|---|
| 158 | ihp->ofi_node = cpu; |
|---|
| 159 | ofd_prop_add(mem, chosen, DRELA((const char *)"cpu", b), |
|---|
| 160 | &ih, sizeof (ih)); |
|---|
| 161 | } |
|---|
| 162 | static s32 |
|---|
| 163 | mmu_translate(u32 nargs, u32 nrets, s32 argp[], s32 retp[], ulong b) |
|---|
| 164 | { |
|---|
| 165 | /* FIXME: need a little more here */ |
|---|
| 166 | nargs = nargs; |
|---|
| 167 | nrets = nrets; |
|---|
| 168 | argp = argp; |
|---|
| 169 | retp = retp; |
|---|
| 170 | b = b; |
|---|
| 171 | return OF_SUCCESS; |
|---|
| 172 | } |
|---|
| 173 | |
|---|
| 174 | static void |
|---|
| 175 | ofh_mmu_init(ofdn_t chosen, ulong b) |
|---|
| 176 | { |
|---|
| 177 | static struct ofh_methods _mmu_methods[] = { |
|---|
| 178 | { "translate", mmu_translate }, |
|---|
| 179 | { NULL, NULL}, |
|---|
| 180 | }; |
|---|
| 181 | static struct ofh_ihandle _ih_mmu = { |
|---|
| 182 | .ofi_methods = _mmu_methods, |
|---|
| 183 | }; |
|---|
| 184 | void *mem = ofd_mem(b); |
|---|
| 185 | u32 ih = DRELA((ulong)&_ih_mmu, b); |
|---|
| 186 | |
|---|
| 187 | ofd_prop_add(mem, chosen, DRELA((const char *)"mmu", b), |
|---|
| 188 | &ih, sizeof (ih)); |
|---|
| 189 | } |
|---|
| 190 | |
|---|
| 191 | static void |
|---|
| 192 | ofh_chosen_init(ulong b) |
|---|
| 193 | { |
|---|
| 194 | ofdn_t ph; |
|---|
| 195 | void *mem = ofd_mem(b); |
|---|
| 196 | |
|---|
| 197 | ph = ofd_node_find(mem, DRELA((const char *)"/chosen", b)); |
|---|
| 198 | |
|---|
| 199 | ofh_vty_init(ph, b); |
|---|
| 200 | ofh_cpu_init(ph, b); |
|---|
| 201 | ofh_mmu_init(ph, b); |
|---|
| 202 | } |
|---|
| 203 | |
|---|
| 204 | static void |
|---|
| 205 | ofh_options_init(ulong b) |
|---|
| 206 | { |
|---|
| 207 | void *mem = ofd_mem(b); |
|---|
| 208 | ofdn_t options; |
|---|
| 209 | u32 size = 1 << 20; |
|---|
| 210 | u32 base = b; |
|---|
| 211 | char buf[20]; |
|---|
| 212 | int i; |
|---|
| 213 | |
|---|
| 214 | |
|---|
| 215 | /* fixup the ihandle */ |
|---|
| 216 | options = ofd_node_find(mem, |
|---|
| 217 | DRELA((const char *)"options", b)); |
|---|
| 218 | |
|---|
| 219 | i = snprintf(buf, sizeof (buf), "0x%x", base); |
|---|
| 220 | ofd_prop_add(mem, options, DRELA((const char *)"real-base", b), |
|---|
| 221 | buf, i); |
|---|
| 222 | |
|---|
| 223 | i = snprintf(buf,sizeof (buf), "0x%x", size); |
|---|
| 224 | ofd_prop_add(mem, options, DRELA((const char *)"real-size", b), |
|---|
| 225 | buf, i); |
|---|
| 226 | } |
|---|
| 227 | |
|---|
| 228 | static void |
|---|
| 229 | ofh_init(ulong b) |
|---|
| 230 | { |
|---|
| 231 | ulong sz = (ulong)_end - (ulong)__bss_start; |
|---|
| 232 | /* clear bss */ |
|---|
| 233 | memset(__bss_start + b, 0, sz); |
|---|
| 234 | |
|---|
| 235 | ofh_service_init(b); |
|---|
| 236 | ofh_chosen_init(b); |
|---|
| 237 | ofh_options_init(b); |
|---|
| 238 | } |
|---|
| 239 | |
|---|
| 240 | static ofh_func_t * |
|---|
| 241 | ofh_lookup(const char *service, ulong b) |
|---|
| 242 | { |
|---|
| 243 | int j; |
|---|
| 244 | u32 hash; |
|---|
| 245 | struct ofh_srvc *o; |
|---|
| 246 | struct ofh_srvc *ofs[] = { |
|---|
| 247 | DRELA(&ofh_srvc[0], b), |
|---|
| 248 | DRELA(&ofh_isa_srvc[0], b), |
|---|
| 249 | NULL |
|---|
| 250 | }; |
|---|
| 251 | u32 sz; |
|---|
| 252 | |
|---|
| 253 | sz = *DRELA(&ofh_maxsrvclen, b); |
|---|
| 254 | |
|---|
| 255 | if (strnlen(service, sz + 1) > sz) { |
|---|
| 256 | return NULL; |
|---|
| 257 | } |
|---|
| 258 | |
|---|
| 259 | hash = of_hash(service); |
|---|
| 260 | |
|---|
| 261 | j = 0; |
|---|
| 262 | while (ofs[j] != NULL) { |
|---|
| 263 | /* yes this could be quicker */ |
|---|
| 264 | o = &ofs[j][0]; |
|---|
| 265 | while (o->ofs_name != NULL) { |
|---|
| 266 | if (o->ofs_hash == hash) { |
|---|
| 267 | const char *n = o->ofs_name; |
|---|
| 268 | if (strcmp(service, n) == 0) { |
|---|
| 269 | return o->ofs_func; |
|---|
| 270 | } |
|---|
| 271 | } |
|---|
| 272 | ++o; |
|---|
| 273 | } |
|---|
| 274 | ++j; |
|---|
| 275 | } |
|---|
| 276 | return NULL; |
|---|
| 277 | } |
|---|
| 278 | |
|---|
| 279 | s32 |
|---|
| 280 | ofh_nosup(u32 nargs __attribute__ ((unused)), |
|---|
| 281 | u32 nrets __attribute__ ((unused)), |
|---|
| 282 | s32 argp[] __attribute__ ((unused)), |
|---|
| 283 | s32 retp[] __attribute__ ((unused)), |
|---|
| 284 | ulong b __attribute__ ((unused))) |
|---|
| 285 | { |
|---|
| 286 | return OF_FAILURE; |
|---|
| 287 | } |
|---|
| 288 | |
|---|
| 289 | s32 |
|---|
| 290 | ofh_test_method(u32 nargs, u32 nrets, s32 argp[], s32 retp[], ulong b) |
|---|
| 291 | { |
|---|
| 292 | if (nargs == 2) { |
|---|
| 293 | if (nrets == 1) { |
|---|
| 294 | s32 *ap = DRELA(&ofh_active_package, b); |
|---|
| 295 | u32 service = (s32)argp[0]; |
|---|
| 296 | const char *method = (const char *)(ulong)argp[1]; |
|---|
| 297 | s32 *stat = &retp[0]; |
|---|
| 298 | |
|---|
| 299 | (void)ap; (void)service; (void)method; |
|---|
| 300 | |
|---|
| 301 | *stat = 0; |
|---|
| 302 | /* we do not do this yet */ |
|---|
| 303 | return OF_FAILURE; |
|---|
| 304 | } |
|---|
| 305 | } |
|---|
| 306 | return OF_FAILURE; |
|---|
| 307 | } |
|---|
| 308 | extern u32 _ofh_inited[0]; |
|---|
| 309 | extern u32 _ofh_lastarg[0]; |
|---|
| 310 | |
|---|
| 311 | s32 |
|---|
| 312 | ofh_handler(struct ofh_args *args, ulong b) |
|---|
| 313 | { |
|---|
| 314 | u32 *inited = (u32 *)DRELA(&_ofh_inited[0],b); |
|---|
| 315 | u32 *lastarg = (u32 *)DRELA(&_ofh_lastarg[0],b); |
|---|
| 316 | ofh_func_t *f; |
|---|
| 317 | |
|---|
| 318 | if (*inited == 0) { |
|---|
| 319 | ofh_init(b); |
|---|
| 320 | |
|---|
| 321 | if ((ulong)ofd_mem(b) < (ulong)_end + b) { |
|---|
| 322 | static const char msg[] = "PANIC: OFD and BSS collide\n"; |
|---|
| 323 | s32 dummy; |
|---|
| 324 | |
|---|
| 325 | ofh_cons_write(DRELA(&msg[0], b), sizeof (msg), &dummy); |
|---|
| 326 | for (;;); |
|---|
| 327 | } |
|---|
| 328 | |
|---|
| 329 | *inited = 1; |
|---|
| 330 | } |
|---|
| 331 | |
|---|
| 332 | *lastarg = (ulong)args; |
|---|
| 333 | |
|---|
| 334 | f = ofh_lookup((char *)((ulong)args->ofa_service), b); |
|---|
| 335 | |
|---|
| 336 | if (f == ((ofh_func_t *)~0UL)) { |
|---|
| 337 | /* do test */ |
|---|
| 338 | if (args->ofa_nargs == 1) { |
|---|
| 339 | if (args->ofa_nreturns == 1) { |
|---|
| 340 | char *name = (char *)(ulong)args->ofa_args[0]; |
|---|
| 341 | if (ofh_lookup(name, b) != NULL) { |
|---|
| 342 | args->ofa_args[args->ofa_nargs] = |
|---|
| 343 | OF_SUCCESS; |
|---|
| 344 | return OF_SUCCESS; |
|---|
| 345 | } |
|---|
| 346 | } |
|---|
| 347 | } |
|---|
| 348 | return OF_FAILURE; |
|---|
| 349 | |
|---|
| 350 | } else if (f != NULL) { |
|---|
| 351 | return leap(args->ofa_nargs, |
|---|
| 352 | args->ofa_nreturns, |
|---|
| 353 | args->ofa_args, |
|---|
| 354 | &args->ofa_args[args->ofa_nargs], |
|---|
| 355 | b, f); |
|---|
| 356 | } |
|---|
| 357 | return OF_FAILURE; |
|---|
| 358 | } |
|---|
| 359 | |
|---|
| 360 | /* |
|---|
| 361 | * The following code exists solely to run the handler code standalone |
|---|
| 362 | */ |
|---|
| 363 | void |
|---|
| 364 | __ofh_start(void) |
|---|
| 365 | { |
|---|
| 366 | s32 ret; |
|---|
| 367 | u32 of_stdout; |
|---|
| 368 | u32 ihandle; |
|---|
| 369 | char buf[1024]; |
|---|
| 370 | u32 args_buf[sizeof (struct ofh_args) + (sizeof (u32) * 10)]; |
|---|
| 371 | struct ofh_args *args; |
|---|
| 372 | |
|---|
| 373 | args = (struct ofh_args *)args_buf; |
|---|
| 374 | |
|---|
| 375 | args->ofa_service = (u32)"finddevice"; |
|---|
| 376 | args->ofa_nargs = 1; |
|---|
| 377 | args->ofa_nreturns = 1; |
|---|
| 378 | args->ofa_args[0] = (u32)"/"; |
|---|
| 379 | args->ofa_args[1] = -1; |
|---|
| 380 | ret = ofh_start(args); |
|---|
| 381 | |
|---|
| 382 | if (ret == OF_SUCCESS) { |
|---|
| 383 | args->ofa_service = (u32)"finddevice"; |
|---|
| 384 | args->ofa_nargs = 1; |
|---|
| 385 | args->ofa_nreturns = 1; |
|---|
| 386 | args->ofa_args[0] = (u32)"/chosen"; |
|---|
| 387 | args->ofa_args[1] = -1; |
|---|
| 388 | ret = ofh_start(args); |
|---|
| 389 | } |
|---|
| 390 | |
|---|
| 391 | if (ret == OF_SUCCESS) { |
|---|
| 392 | u32 phandle = args->ofa_args[1]; |
|---|
| 393 | |
|---|
| 394 | args->ofa_service = (u32)"getprop"; |
|---|
| 395 | args->ofa_nargs = 4; |
|---|
| 396 | args->ofa_nreturns = 1; |
|---|
| 397 | args->ofa_args[0] = phandle; |
|---|
| 398 | args->ofa_args[1] = (ulong)"stdout"; |
|---|
| 399 | args->ofa_args[2] = (ulong)&of_stdout; |
|---|
| 400 | args->ofa_args[3] = sizeof(of_stdout); |
|---|
| 401 | args->ofa_args[4] = -1; |
|---|
| 402 | ret = ofh_start(args); |
|---|
| 403 | } |
|---|
| 404 | |
|---|
| 405 | ihandle = *(u32 *)((ulong)args->ofa_args[2]); |
|---|
| 406 | |
|---|
| 407 | if (ret == OF_SUCCESS) { |
|---|
| 408 | /* instance to path */ |
|---|
| 409 | args->ofa_service = (u32)"instance-to-path"; |
|---|
| 410 | args->ofa_nargs = 3; |
|---|
| 411 | args->ofa_nreturns = 1; |
|---|
| 412 | args->ofa_args[0] = ihandle; |
|---|
| 413 | args->ofa_args[1] = (ulong)buf; |
|---|
| 414 | args->ofa_args[2] = sizeof (buf); |
|---|
| 415 | args->ofa_args[3] = -1; |
|---|
| 416 | ret = ofh_start(args); |
|---|
| 417 | |
|---|
| 418 | } |
|---|
| 419 | |
|---|
| 420 | if (ret == OF_SUCCESS) { |
|---|
| 421 | /* open rtas */ |
|---|
| 422 | args->ofa_service = (u32)"open"; |
|---|
| 423 | args->ofa_nargs = 1; |
|---|
| 424 | args->ofa_nreturns = 1; |
|---|
| 425 | args->ofa_args[0] = (u32)"/rtas"; |
|---|
| 426 | ret = ofh_start(args); |
|---|
| 427 | if (ret == OF_SUCCESS) { |
|---|
| 428 | u32 ir = args->ofa_args[1]; |
|---|
| 429 | args->ofa_service = (u32)"call-method"; |
|---|
| 430 | args->ofa_nargs = 3; |
|---|
| 431 | args->ofa_nreturns = 2; |
|---|
| 432 | args->ofa_args[0] = (ulong)"instantiate-rtas"; |
|---|
| 433 | args->ofa_args[1] = ir; |
|---|
| 434 | args->ofa_args[2] = (ulong)buf; |
|---|
| 435 | |
|---|
| 436 | ret = ofh_start(args); |
|---|
| 437 | } |
|---|
| 438 | } |
|---|
| 439 | |
|---|
| 440 | if (ret == OF_SUCCESS) { |
|---|
| 441 | const char msg[] = "This is a test"; |
|---|
| 442 | u32 msgsz = sizeof(msg) - 1; /* Includes \0 */ |
|---|
| 443 | |
|---|
| 444 | args->ofa_service = (u32)"write"; |
|---|
| 445 | args->ofa_nargs = 3; |
|---|
| 446 | args->ofa_nreturns = 1; |
|---|
| 447 | args->ofa_args[0] = ihandle; |
|---|
| 448 | args->ofa_args[1] = (ulong)msg; |
|---|
| 449 | args->ofa_args[2] = msgsz; |
|---|
| 450 | args->ofa_args[3] = -1; |
|---|
| 451 | ret = ofh_start(args); |
|---|
| 452 | } |
|---|
| 453 | |
|---|
| 454 | } |
|---|