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 19163bed77SCédric Le Goater typedef u32 prom_arg_t; 20163bed77SCédric Le Goater 21fed23ed7SCédric Le Goater /* The following structure is used to communicate with open firmware. 22fed23ed7SCédric Le Goater * All arguments in and out are in big endian format. */ 23fed23ed7SCédric Le Goater struct prom_args { 24fed23ed7SCédric Le Goater __be32 service; /* Address of service name string. */ 25fed23ed7SCédric Le Goater __be32 nargs; /* Number of input arguments. */ 26fed23ed7SCédric Le Goater __be32 nret; /* Number of output arguments. */ 27fed23ed7SCédric Le Goater __be32 args[10]; /* Input/output arguments. */ 28fed23ed7SCédric Le Goater }; 29fed23ed7SCédric Le Goater 302e601613SDavid Gibson static int (*prom) (void *); 312e601613SDavid Gibson 322e601613SDavid Gibson void of_init(void *promptr) 332e601613SDavid Gibson { 342e601613SDavid Gibson prom = (int (*)(void *))promptr; 352e601613SDavid Gibson } 362e601613SDavid Gibson 37fed23ed7SCédric Le Goater #define ADDR(x) (u32)(unsigned long)(x) 38fed23ed7SCédric Le Goater 392e601613SDavid Gibson int of_call_prom(const char *service, int nargs, int nret, ...) 402e601613SDavid Gibson { 412e601613SDavid Gibson int i; 42fed23ed7SCédric Le Goater struct prom_args args; 432e601613SDavid Gibson va_list list; 442e601613SDavid Gibson 45*926e6940SCédric Le Goater args.service = cpu_to_be32(ADDR(service)); 46*926e6940SCédric Le Goater args.nargs = cpu_to_be32(nargs); 47*926e6940SCédric Le Goater args.nret = cpu_to_be32(nret); 482e601613SDavid Gibson 492e601613SDavid Gibson va_start(list, nret); 502e601613SDavid Gibson for (i = 0; i < nargs; i++) 51*926e6940SCédric Le Goater args.args[i] = cpu_to_be32(va_arg(list, prom_arg_t)); 522e601613SDavid Gibson va_end(list); 532e601613SDavid Gibson 542e601613SDavid Gibson for (i = 0; i < nret; i++) 552e601613SDavid Gibson args.args[nargs+i] = 0; 562e601613SDavid Gibson 572e601613SDavid Gibson if (prom(&args) < 0) 582e601613SDavid Gibson return -1; 592e601613SDavid Gibson 60*926e6940SCédric Le Goater return (nret > 0) ? be32_to_cpu(args.args[nargs]) : 0; 612e601613SDavid Gibson } 622e601613SDavid Gibson 632e601613SDavid Gibson static int of_call_prom_ret(const char *service, int nargs, int nret, 64163bed77SCédric Le Goater prom_arg_t *rets, ...) 652e601613SDavid Gibson { 662e601613SDavid Gibson int i; 67fed23ed7SCédric Le Goater struct prom_args args; 682e601613SDavid Gibson va_list list; 692e601613SDavid Gibson 70*926e6940SCédric Le Goater args.service = cpu_to_be32(ADDR(service)); 71*926e6940SCédric Le Goater args.nargs = cpu_to_be32(nargs); 72*926e6940SCédric Le Goater args.nret = cpu_to_be32(nret); 732e601613SDavid Gibson 742e601613SDavid Gibson va_start(list, rets); 752e601613SDavid Gibson for (i = 0; i < nargs; i++) 76*926e6940SCédric Le Goater args.args[i] = cpu_to_be32(va_arg(list, prom_arg_t)); 772e601613SDavid Gibson va_end(list); 782e601613SDavid Gibson 792e601613SDavid Gibson for (i = 0; i < nret; i++) 802e601613SDavid Gibson args.args[nargs+i] = 0; 812e601613SDavid Gibson 822e601613SDavid Gibson if (prom(&args) < 0) 832e601613SDavid Gibson return -1; 842e601613SDavid Gibson 852e601613SDavid Gibson if (rets != (void *) 0) 862e601613SDavid Gibson for (i = 1; i < nret; ++i) 87*926e6940SCédric Le Goater rets[i-1] = be32_to_cpu(args.args[nargs+i]); 882e601613SDavid Gibson 89*926e6940SCédric Le Goater return (nret > 0) ? be32_to_cpu(args.args[nargs]) : 0; 902e601613SDavid Gibson } 912e601613SDavid Gibson 922e601613SDavid Gibson /* returns true if s2 is a prefix of s1 */ 932e601613SDavid Gibson static int string_match(const char *s1, const char *s2) 942e601613SDavid Gibson { 952e601613SDavid Gibson for (; *s2; ++s2) 962e601613SDavid Gibson if (*s1++ != *s2) 972e601613SDavid Gibson return 0; 982e601613SDavid Gibson return 1; 992e601613SDavid Gibson } 1002e601613SDavid Gibson 1012e601613SDavid Gibson /* 1022e601613SDavid Gibson * Older OF's require that when claiming a specific range of addresses, 1032e601613SDavid Gibson * we claim the physical space in the /memory node and the virtual 1042e601613SDavid Gibson * space in the chosen mmu node, and then do a map operation to 1052e601613SDavid Gibson * map virtual to physical. 1062e601613SDavid Gibson */ 1072e601613SDavid Gibson static int need_map = -1; 1082e601613SDavid Gibson static ihandle chosen_mmu; 1092e601613SDavid Gibson static phandle memory; 1102e601613SDavid Gibson 1112e601613SDavid Gibson static int check_of_version(void) 1122e601613SDavid Gibson { 1132e601613SDavid Gibson phandle oprom, chosen; 1142e601613SDavid Gibson char version[64]; 1152e601613SDavid Gibson 11608464712SDavid Gibson oprom = of_finddevice("/openprom"); 1172e601613SDavid Gibson if (oprom == (phandle) -1) 1182e601613SDavid Gibson return 0; 11908464712SDavid Gibson if (of_getprop(oprom, "model", version, sizeof(version)) <= 0) 1202e601613SDavid Gibson return 0; 1212e601613SDavid Gibson version[sizeof(version)-1] = 0; 1222e601613SDavid Gibson printf("OF version = '%s'\r\n", version); 1232e601613SDavid Gibson if (!string_match(version, "Open Firmware, 1.") 1242e601613SDavid Gibson && !string_match(version, "FirmWorks,3.")) 1252e601613SDavid Gibson return 0; 12608464712SDavid Gibson chosen = of_finddevice("/chosen"); 1272e601613SDavid Gibson if (chosen == (phandle) -1) { 12808464712SDavid Gibson chosen = of_finddevice("/chosen@0"); 1292e601613SDavid Gibson if (chosen == (phandle) -1) { 1302e601613SDavid Gibson printf("no chosen\n"); 1312e601613SDavid Gibson return 0; 1322e601613SDavid Gibson } 1332e601613SDavid Gibson } 13408464712SDavid Gibson if (of_getprop(chosen, "mmu", &chosen_mmu, sizeof(chosen_mmu)) <= 0) { 1352e601613SDavid Gibson printf("no mmu\n"); 1362e601613SDavid Gibson return 0; 1372e601613SDavid Gibson } 1382e601613SDavid Gibson memory = (ihandle) of_call_prom("open", 1, 1, "/memory"); 1392e601613SDavid Gibson if (memory == (ihandle) -1) { 1402e601613SDavid Gibson memory = (ihandle) of_call_prom("open", 1, 1, "/memory@0"); 1412e601613SDavid Gibson if (memory == (ihandle) -1) { 1422e601613SDavid Gibson printf("no memory node\n"); 1432e601613SDavid Gibson return 0; 1442e601613SDavid Gibson } 1452e601613SDavid Gibson } 1462e601613SDavid Gibson printf("old OF detected\r\n"); 1472e601613SDavid Gibson return 1; 1482e601613SDavid Gibson } 1492e601613SDavid Gibson 1502e601613SDavid Gibson void *of_claim(unsigned long virt, unsigned long size, unsigned long align) 1512e601613SDavid Gibson { 1522e601613SDavid Gibson int ret; 153163bed77SCédric Le Goater prom_arg_t result; 1542e601613SDavid Gibson 1552e601613SDavid Gibson if (need_map < 0) 1562e601613SDavid Gibson need_map = check_of_version(); 1572e601613SDavid Gibson if (align || !need_map) 1582e601613SDavid Gibson return (void *) of_call_prom("claim", 3, 1, virt, size, align); 1592e601613SDavid Gibson 1602e601613SDavid Gibson ret = of_call_prom_ret("call-method", 5, 2, &result, "claim", memory, 1612e601613SDavid Gibson align, size, virt); 1622e601613SDavid Gibson if (ret != 0 || result == -1) 1632e601613SDavid Gibson return (void *) -1; 1642e601613SDavid Gibson ret = of_call_prom_ret("call-method", 5, 2, &result, "claim", chosen_mmu, 1652e601613SDavid Gibson align, size, virt); 1662e601613SDavid Gibson /* 0x12 == coherent + read/write */ 1672e601613SDavid Gibson ret = of_call_prom("call-method", 6, 1, "map", chosen_mmu, 1682e601613SDavid Gibson 0x12, size, virt, virt); 1692e601613SDavid Gibson return (void *) virt; 1702e601613SDavid Gibson } 1712e601613SDavid Gibson 17208464712SDavid Gibson void *of_vmlinux_alloc(unsigned long size) 17308464712SDavid Gibson { 1749b09c6d9STony Breeds unsigned long start = (unsigned long)_start, end = (unsigned long)_end; 1759b09c6d9STony Breeds void *addr; 1769b09c6d9STony Breeds void *p; 17708464712SDavid Gibson 1789b09c6d9STony Breeds /* With some older POWER4 firmware we need to claim the area the kernel 1799b09c6d9STony Breeds * will reside in. Newer firmwares don't need this so we just ignore 1809b09c6d9STony Breeds * the return value. 1819b09c6d9STony Breeds */ 1829b09c6d9STony Breeds addr = of_claim(start, end - start, 0); 1839b09c6d9STony Breeds printf("Trying to claim from 0x%lx to 0x%lx (0x%lx) got %p\r\n", 1849b09c6d9STony Breeds start, end, end - start, addr); 1859b09c6d9STony Breeds 1869b09c6d9STony Breeds p = malloc(size); 18708464712SDavid Gibson if (!p) 18808464712SDavid Gibson fatal("Can't allocate memory for kernel image!\n\r"); 18908464712SDavid Gibson 19008464712SDavid Gibson return p; 19108464712SDavid Gibson } 19208464712SDavid Gibson 1932e601613SDavid Gibson void of_exit(void) 1942e601613SDavid Gibson { 1952e601613SDavid Gibson of_call_prom("exit", 0, 0); 1962e601613SDavid Gibson } 19708464712SDavid Gibson 19808464712SDavid Gibson /* 19908464712SDavid Gibson * OF device tree routines 20008464712SDavid Gibson */ 20108464712SDavid Gibson void *of_finddevice(const char *name) 20208464712SDavid Gibson { 20308464712SDavid Gibson return (phandle) of_call_prom("finddevice", 1, 1, name); 20408464712SDavid Gibson } 20508464712SDavid Gibson 20608464712SDavid Gibson int of_getprop(const void *phandle, const char *name, void *buf, 20708464712SDavid Gibson const int buflen) 20808464712SDavid Gibson { 20908464712SDavid Gibson return of_call_prom("getprop", 4, 1, phandle, name, buf, buflen); 21008464712SDavid Gibson } 21108464712SDavid Gibson 21208464712SDavid Gibson int of_setprop(const void *phandle, const char *name, const void *buf, 21308464712SDavid Gibson const int buflen) 21408464712SDavid Gibson { 21508464712SDavid Gibson return of_call_prom("setprop", 4, 1, phandle, name, buf, buflen); 21608464712SDavid Gibson } 217