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 19*fed23ed7SCédric Le Goater /* The following structure is used to communicate with open firmware. 20*fed23ed7SCédric Le Goater * All arguments in and out are in big endian format. */ 21*fed23ed7SCédric Le Goater struct prom_args { 22*fed23ed7SCédric Le Goater __be32 service; /* Address of service name string. */ 23*fed23ed7SCédric Le Goater __be32 nargs; /* Number of input arguments. */ 24*fed23ed7SCédric Le Goater __be32 nret; /* Number of output arguments. */ 25*fed23ed7SCédric Le Goater __be32 args[10]; /* Input/output arguments. */ 26*fed23ed7SCédric Le Goater }; 27*fed23ed7SCédric Le Goater 282e601613SDavid Gibson static int (*prom) (void *); 292e601613SDavid Gibson 302e601613SDavid Gibson void of_init(void *promptr) 312e601613SDavid Gibson { 322e601613SDavid Gibson prom = (int (*)(void *))promptr; 332e601613SDavid Gibson } 342e601613SDavid Gibson 35*fed23ed7SCédric Le Goater #define ADDR(x) (u32)(unsigned long)(x) 36*fed23ed7SCédric Le Goater 372e601613SDavid Gibson int of_call_prom(const char *service, int nargs, int nret, ...) 382e601613SDavid Gibson { 392e601613SDavid Gibson int i; 40*fed23ed7SCédric Le Goater struct prom_args args; 412e601613SDavid Gibson va_list list; 422e601613SDavid Gibson 43*fed23ed7SCédric Le Goater args.service = ADDR(service); 442e601613SDavid Gibson args.nargs = nargs; 452e601613SDavid Gibson args.nret = nret; 462e601613SDavid Gibson 472e601613SDavid Gibson va_start(list, nret); 482e601613SDavid Gibson for (i = 0; i < nargs; i++) 492e601613SDavid Gibson args.args[i] = va_arg(list, unsigned int); 502e601613SDavid Gibson va_end(list); 512e601613SDavid Gibson 522e601613SDavid Gibson for (i = 0; i < nret; i++) 532e601613SDavid Gibson args.args[nargs+i] = 0; 542e601613SDavid Gibson 552e601613SDavid Gibson if (prom(&args) < 0) 562e601613SDavid Gibson return -1; 572e601613SDavid Gibson 582e601613SDavid Gibson return (nret > 0)? args.args[nargs]: 0; 592e601613SDavid Gibson } 602e601613SDavid Gibson 612e601613SDavid Gibson static int of_call_prom_ret(const char *service, int nargs, int nret, 622e601613SDavid Gibson unsigned int *rets, ...) 632e601613SDavid Gibson { 642e601613SDavid Gibson int i; 65*fed23ed7SCédric Le Goater struct prom_args args; 662e601613SDavid Gibson va_list list; 672e601613SDavid Gibson 68*fed23ed7SCédric Le Goater args.service = ADDR(service); 692e601613SDavid Gibson args.nargs = nargs; 702e601613SDavid Gibson args.nret = nret; 712e601613SDavid Gibson 722e601613SDavid Gibson va_start(list, rets); 732e601613SDavid Gibson for (i = 0; i < nargs; i++) 742e601613SDavid Gibson args.args[i] = va_arg(list, unsigned int); 752e601613SDavid Gibson va_end(list); 762e601613SDavid Gibson 772e601613SDavid Gibson for (i = 0; i < nret; i++) 782e601613SDavid Gibson args.args[nargs+i] = 0; 792e601613SDavid Gibson 802e601613SDavid Gibson if (prom(&args) < 0) 812e601613SDavid Gibson return -1; 822e601613SDavid Gibson 832e601613SDavid Gibson if (rets != (void *) 0) 842e601613SDavid Gibson for (i = 1; i < nret; ++i) 852e601613SDavid Gibson rets[i-1] = args.args[nargs+i]; 862e601613SDavid Gibson 872e601613SDavid Gibson return (nret > 0)? args.args[nargs]: 0; 882e601613SDavid Gibson } 892e601613SDavid Gibson 902e601613SDavid Gibson /* returns true if s2 is a prefix of s1 */ 912e601613SDavid Gibson static int string_match(const char *s1, const char *s2) 922e601613SDavid Gibson { 932e601613SDavid Gibson for (; *s2; ++s2) 942e601613SDavid Gibson if (*s1++ != *s2) 952e601613SDavid Gibson return 0; 962e601613SDavid Gibson return 1; 972e601613SDavid Gibson } 982e601613SDavid Gibson 992e601613SDavid Gibson /* 1002e601613SDavid Gibson * Older OF's require that when claiming a specific range of addresses, 1012e601613SDavid Gibson * we claim the physical space in the /memory node and the virtual 1022e601613SDavid Gibson * space in the chosen mmu node, and then do a map operation to 1032e601613SDavid Gibson * map virtual to physical. 1042e601613SDavid Gibson */ 1052e601613SDavid Gibson static int need_map = -1; 1062e601613SDavid Gibson static ihandle chosen_mmu; 1072e601613SDavid Gibson static phandle memory; 1082e601613SDavid Gibson 1092e601613SDavid Gibson static int check_of_version(void) 1102e601613SDavid Gibson { 1112e601613SDavid Gibson phandle oprom, chosen; 1122e601613SDavid Gibson char version[64]; 1132e601613SDavid Gibson 11408464712SDavid Gibson oprom = of_finddevice("/openprom"); 1152e601613SDavid Gibson if (oprom == (phandle) -1) 1162e601613SDavid Gibson return 0; 11708464712SDavid Gibson if (of_getprop(oprom, "model", version, sizeof(version)) <= 0) 1182e601613SDavid Gibson return 0; 1192e601613SDavid Gibson version[sizeof(version)-1] = 0; 1202e601613SDavid Gibson printf("OF version = '%s'\r\n", version); 1212e601613SDavid Gibson if (!string_match(version, "Open Firmware, 1.") 1222e601613SDavid Gibson && !string_match(version, "FirmWorks,3.")) 1232e601613SDavid Gibson return 0; 12408464712SDavid Gibson chosen = of_finddevice("/chosen"); 1252e601613SDavid Gibson if (chosen == (phandle) -1) { 12608464712SDavid Gibson chosen = of_finddevice("/chosen@0"); 1272e601613SDavid Gibson if (chosen == (phandle) -1) { 1282e601613SDavid Gibson printf("no chosen\n"); 1292e601613SDavid Gibson return 0; 1302e601613SDavid Gibson } 1312e601613SDavid Gibson } 13208464712SDavid Gibson if (of_getprop(chosen, "mmu", &chosen_mmu, sizeof(chosen_mmu)) <= 0) { 1332e601613SDavid Gibson printf("no mmu\n"); 1342e601613SDavid Gibson return 0; 1352e601613SDavid Gibson } 1362e601613SDavid Gibson memory = (ihandle) of_call_prom("open", 1, 1, "/memory"); 1372e601613SDavid Gibson if (memory == (ihandle) -1) { 1382e601613SDavid Gibson memory = (ihandle) of_call_prom("open", 1, 1, "/memory@0"); 1392e601613SDavid Gibson if (memory == (ihandle) -1) { 1402e601613SDavid Gibson printf("no memory node\n"); 1412e601613SDavid Gibson return 0; 1422e601613SDavid Gibson } 1432e601613SDavid Gibson } 1442e601613SDavid Gibson printf("old OF detected\r\n"); 1452e601613SDavid Gibson return 1; 1462e601613SDavid Gibson } 1472e601613SDavid Gibson 1482e601613SDavid Gibson void *of_claim(unsigned long virt, unsigned long size, unsigned long align) 1492e601613SDavid Gibson { 1502e601613SDavid Gibson int ret; 1512e601613SDavid Gibson unsigned int result; 1522e601613SDavid Gibson 1532e601613SDavid Gibson if (need_map < 0) 1542e601613SDavid Gibson need_map = check_of_version(); 1552e601613SDavid Gibson if (align || !need_map) 1562e601613SDavid Gibson return (void *) of_call_prom("claim", 3, 1, virt, size, align); 1572e601613SDavid Gibson 1582e601613SDavid Gibson ret = of_call_prom_ret("call-method", 5, 2, &result, "claim", memory, 1592e601613SDavid Gibson align, size, virt); 1602e601613SDavid Gibson if (ret != 0 || result == -1) 1612e601613SDavid Gibson return (void *) -1; 1622e601613SDavid Gibson ret = of_call_prom_ret("call-method", 5, 2, &result, "claim", chosen_mmu, 1632e601613SDavid Gibson align, size, virt); 1642e601613SDavid Gibson /* 0x12 == coherent + read/write */ 1652e601613SDavid Gibson ret = of_call_prom("call-method", 6, 1, "map", chosen_mmu, 1662e601613SDavid Gibson 0x12, size, virt, virt); 1672e601613SDavid Gibson return (void *) virt; 1682e601613SDavid Gibson } 1692e601613SDavid Gibson 17008464712SDavid Gibson void *of_vmlinux_alloc(unsigned long size) 17108464712SDavid Gibson { 1729b09c6d9STony Breeds unsigned long start = (unsigned long)_start, end = (unsigned long)_end; 1739b09c6d9STony Breeds void *addr; 1749b09c6d9STony Breeds void *p; 17508464712SDavid Gibson 1769b09c6d9STony Breeds /* With some older POWER4 firmware we need to claim the area the kernel 1779b09c6d9STony Breeds * will reside in. Newer firmwares don't need this so we just ignore 1789b09c6d9STony Breeds * the return value. 1799b09c6d9STony Breeds */ 1809b09c6d9STony Breeds addr = of_claim(start, end - start, 0); 1819b09c6d9STony Breeds printf("Trying to claim from 0x%lx to 0x%lx (0x%lx) got %p\r\n", 1829b09c6d9STony Breeds start, end, end - start, addr); 1839b09c6d9STony Breeds 1849b09c6d9STony Breeds p = malloc(size); 18508464712SDavid Gibson if (!p) 18608464712SDavid Gibson fatal("Can't allocate memory for kernel image!\n\r"); 18708464712SDavid Gibson 18808464712SDavid Gibson return p; 18908464712SDavid Gibson } 19008464712SDavid Gibson 1912e601613SDavid Gibson void of_exit(void) 1922e601613SDavid Gibson { 1932e601613SDavid Gibson of_call_prom("exit", 0, 0); 1942e601613SDavid Gibson } 19508464712SDavid Gibson 19608464712SDavid Gibson /* 19708464712SDavid Gibson * OF device tree routines 19808464712SDavid Gibson */ 19908464712SDavid Gibson void *of_finddevice(const char *name) 20008464712SDavid Gibson { 20108464712SDavid Gibson return (phandle) of_call_prom("finddevice", 1, 1, name); 20208464712SDavid Gibson } 20308464712SDavid Gibson 20408464712SDavid Gibson int of_getprop(const void *phandle, const char *name, void *buf, 20508464712SDavid Gibson const int buflen) 20608464712SDavid Gibson { 20708464712SDavid Gibson return of_call_prom("getprop", 4, 1, phandle, name, buf, buflen); 20808464712SDavid Gibson } 20908464712SDavid Gibson 21008464712SDavid Gibson int of_setprop(const void *phandle, const char *name, const void *buf, 21108464712SDavid Gibson const int buflen) 21208464712SDavid Gibson { 21308464712SDavid Gibson return of_call_prom("setprop", 4, 1, phandle, name, buf, buflen); 21408464712SDavid Gibson } 215