12e601613SDavid Gibson /* 22e601613SDavid Gibson * Copyright (C) Paul Mackerras 1997. 32e601613SDavid Gibson * 42e601613SDavid Gibson * This program is free software; you can redistribute it and/or 52e601613SDavid Gibson * modify it under the terms of the GNU General Public License 62e601613SDavid Gibson * as published by the Free Software Foundation; either version 72e601613SDavid Gibson * 2 of the License, or (at your option) any later version. 82e601613SDavid Gibson */ 92e601613SDavid Gibson #include <stddef.h> 102e601613SDavid Gibson #include "types.h" 112e601613SDavid Gibson #include "elf.h" 122e601613SDavid Gibson #include "string.h" 132e601613SDavid Gibson #include "stdio.h" 142e601613SDavid Gibson #include "page.h" 152e601613SDavid Gibson #include "ops.h" 162e601613SDavid Gibson 172e601613SDavid Gibson #include "of.h" 182e601613SDavid Gibson 192e601613SDavid Gibson static int (*prom) (void *); 202e601613SDavid Gibson 212e601613SDavid Gibson void of_init(void *promptr) 222e601613SDavid Gibson { 232e601613SDavid Gibson prom = (int (*)(void *))promptr; 242e601613SDavid Gibson } 252e601613SDavid Gibson 262e601613SDavid Gibson int of_call_prom(const char *service, int nargs, int nret, ...) 272e601613SDavid Gibson { 282e601613SDavid Gibson int i; 292e601613SDavid Gibson struct prom_args { 302e601613SDavid Gibson const char *service; 312e601613SDavid Gibson int nargs; 322e601613SDavid Gibson int nret; 332e601613SDavid Gibson unsigned int args[12]; 342e601613SDavid Gibson } args; 352e601613SDavid Gibson va_list list; 362e601613SDavid Gibson 372e601613SDavid Gibson args.service = service; 382e601613SDavid Gibson args.nargs = nargs; 392e601613SDavid Gibson args.nret = nret; 402e601613SDavid Gibson 412e601613SDavid Gibson va_start(list, nret); 422e601613SDavid Gibson for (i = 0; i < nargs; i++) 432e601613SDavid Gibson args.args[i] = va_arg(list, unsigned int); 442e601613SDavid Gibson va_end(list); 452e601613SDavid Gibson 462e601613SDavid Gibson for (i = 0; i < nret; i++) 472e601613SDavid Gibson args.args[nargs+i] = 0; 482e601613SDavid Gibson 492e601613SDavid Gibson if (prom(&args) < 0) 502e601613SDavid Gibson return -1; 512e601613SDavid Gibson 522e601613SDavid Gibson return (nret > 0)? args.args[nargs]: 0; 532e601613SDavid Gibson } 542e601613SDavid Gibson 552e601613SDavid Gibson static int of_call_prom_ret(const char *service, int nargs, int nret, 562e601613SDavid Gibson unsigned int *rets, ...) 572e601613SDavid Gibson { 582e601613SDavid Gibson int i; 592e601613SDavid Gibson struct prom_args { 602e601613SDavid Gibson const char *service; 612e601613SDavid Gibson int nargs; 622e601613SDavid Gibson int nret; 632e601613SDavid Gibson unsigned int args[12]; 642e601613SDavid Gibson } args; 652e601613SDavid Gibson va_list list; 662e601613SDavid Gibson 672e601613SDavid Gibson args.service = service; 682e601613SDavid Gibson args.nargs = nargs; 692e601613SDavid Gibson args.nret = nret; 702e601613SDavid Gibson 712e601613SDavid Gibson va_start(list, rets); 722e601613SDavid Gibson for (i = 0; i < nargs; i++) 732e601613SDavid Gibson args.args[i] = va_arg(list, unsigned int); 742e601613SDavid Gibson va_end(list); 752e601613SDavid Gibson 762e601613SDavid Gibson for (i = 0; i < nret; i++) 772e601613SDavid Gibson args.args[nargs+i] = 0; 782e601613SDavid Gibson 792e601613SDavid Gibson if (prom(&args) < 0) 802e601613SDavid Gibson return -1; 812e601613SDavid Gibson 822e601613SDavid Gibson if (rets != (void *) 0) 832e601613SDavid Gibson for (i = 1; i < nret; ++i) 842e601613SDavid Gibson rets[i-1] = args.args[nargs+i]; 852e601613SDavid Gibson 862e601613SDavid Gibson return (nret > 0)? args.args[nargs]: 0; 872e601613SDavid Gibson } 882e601613SDavid Gibson 892e601613SDavid Gibson /* returns true if s2 is a prefix of s1 */ 902e601613SDavid Gibson static int string_match(const char *s1, const char *s2) 912e601613SDavid Gibson { 922e601613SDavid Gibson for (; *s2; ++s2) 932e601613SDavid Gibson if (*s1++ != *s2) 942e601613SDavid Gibson return 0; 952e601613SDavid Gibson return 1; 962e601613SDavid Gibson } 972e601613SDavid Gibson 982e601613SDavid Gibson /* 992e601613SDavid Gibson * Older OF's require that when claiming a specific range of addresses, 1002e601613SDavid Gibson * we claim the physical space in the /memory node and the virtual 1012e601613SDavid Gibson * space in the chosen mmu node, and then do a map operation to 1022e601613SDavid Gibson * map virtual to physical. 1032e601613SDavid Gibson */ 1042e601613SDavid Gibson static int need_map = -1; 1052e601613SDavid Gibson static ihandle chosen_mmu; 1062e601613SDavid Gibson static phandle memory; 1072e601613SDavid Gibson 1082e601613SDavid Gibson static int check_of_version(void) 1092e601613SDavid Gibson { 1102e601613SDavid Gibson phandle oprom, chosen; 1112e601613SDavid Gibson char version[64]; 1122e601613SDavid Gibson 113*08464712SDavid Gibson oprom = of_finddevice("/openprom"); 1142e601613SDavid Gibson if (oprom == (phandle) -1) 1152e601613SDavid Gibson return 0; 116*08464712SDavid Gibson if (of_getprop(oprom, "model", version, sizeof(version)) <= 0) 1172e601613SDavid Gibson return 0; 1182e601613SDavid Gibson version[sizeof(version)-1] = 0; 1192e601613SDavid Gibson printf("OF version = '%s'\r\n", version); 1202e601613SDavid Gibson if (!string_match(version, "Open Firmware, 1.") 1212e601613SDavid Gibson && !string_match(version, "FirmWorks,3.")) 1222e601613SDavid Gibson return 0; 123*08464712SDavid Gibson chosen = of_finddevice("/chosen"); 1242e601613SDavid Gibson if (chosen == (phandle) -1) { 125*08464712SDavid Gibson chosen = of_finddevice("/chosen@0"); 1262e601613SDavid Gibson if (chosen == (phandle) -1) { 1272e601613SDavid Gibson printf("no chosen\n"); 1282e601613SDavid Gibson return 0; 1292e601613SDavid Gibson } 1302e601613SDavid Gibson } 131*08464712SDavid Gibson if (of_getprop(chosen, "mmu", &chosen_mmu, sizeof(chosen_mmu)) <= 0) { 1322e601613SDavid Gibson printf("no mmu\n"); 1332e601613SDavid Gibson return 0; 1342e601613SDavid Gibson } 1352e601613SDavid Gibson memory = (ihandle) of_call_prom("open", 1, 1, "/memory"); 1362e601613SDavid Gibson if (memory == (ihandle) -1) { 1372e601613SDavid Gibson memory = (ihandle) of_call_prom("open", 1, 1, "/memory@0"); 1382e601613SDavid Gibson if (memory == (ihandle) -1) { 1392e601613SDavid Gibson printf("no memory node\n"); 1402e601613SDavid Gibson return 0; 1412e601613SDavid Gibson } 1422e601613SDavid Gibson } 1432e601613SDavid Gibson printf("old OF detected\r\n"); 1442e601613SDavid Gibson return 1; 1452e601613SDavid Gibson } 1462e601613SDavid Gibson 1472e601613SDavid Gibson void *of_claim(unsigned long virt, unsigned long size, unsigned long align) 1482e601613SDavid Gibson { 1492e601613SDavid Gibson int ret; 1502e601613SDavid Gibson unsigned int result; 1512e601613SDavid Gibson 1522e601613SDavid Gibson if (need_map < 0) 1532e601613SDavid Gibson need_map = check_of_version(); 1542e601613SDavid Gibson if (align || !need_map) 1552e601613SDavid Gibson return (void *) of_call_prom("claim", 3, 1, virt, size, align); 1562e601613SDavid Gibson 1572e601613SDavid Gibson ret = of_call_prom_ret("call-method", 5, 2, &result, "claim", memory, 1582e601613SDavid Gibson align, size, virt); 1592e601613SDavid Gibson if (ret != 0 || result == -1) 1602e601613SDavid Gibson return (void *) -1; 1612e601613SDavid Gibson ret = of_call_prom_ret("call-method", 5, 2, &result, "claim", chosen_mmu, 1622e601613SDavid Gibson align, size, virt); 1632e601613SDavid Gibson /* 0x12 == coherent + read/write */ 1642e601613SDavid Gibson ret = of_call_prom("call-method", 6, 1, "map", chosen_mmu, 1652e601613SDavid Gibson 0x12, size, virt, virt); 1662e601613SDavid Gibson return (void *) virt; 1672e601613SDavid Gibson } 1682e601613SDavid Gibson 169*08464712SDavid Gibson void *of_vmlinux_alloc(unsigned long size) 170*08464712SDavid Gibson { 171*08464712SDavid Gibson void *p = malloc(size); 172*08464712SDavid Gibson 173*08464712SDavid Gibson if (!p) 174*08464712SDavid Gibson fatal("Can't allocate memory for kernel image!\n\r"); 175*08464712SDavid Gibson 176*08464712SDavid Gibson return p; 177*08464712SDavid Gibson } 178*08464712SDavid Gibson 1792e601613SDavid Gibson void of_exit(void) 1802e601613SDavid Gibson { 1812e601613SDavid Gibson of_call_prom("exit", 0, 0); 1822e601613SDavid Gibson } 183*08464712SDavid Gibson 184*08464712SDavid Gibson /* 185*08464712SDavid Gibson * OF device tree routines 186*08464712SDavid Gibson */ 187*08464712SDavid Gibson void *of_finddevice(const char *name) 188*08464712SDavid Gibson { 189*08464712SDavid Gibson return (phandle) of_call_prom("finddevice", 1, 1, name); 190*08464712SDavid Gibson } 191*08464712SDavid Gibson 192*08464712SDavid Gibson int of_getprop(const void *phandle, const char *name, void *buf, 193*08464712SDavid Gibson const int buflen) 194*08464712SDavid Gibson { 195*08464712SDavid Gibson return of_call_prom("getprop", 4, 1, phandle, name, buf, buflen); 196*08464712SDavid Gibson } 197*08464712SDavid Gibson 198*08464712SDavid Gibson int of_setprop(const void *phandle, const char *name, const void *buf, 199*08464712SDavid Gibson const int buflen) 200*08464712SDavid Gibson { 201*08464712SDavid Gibson return of_call_prom("setprop", 4, 1, phandle, name, buf, buflen); 202*08464712SDavid Gibson } 203