1*2874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 22e601613SDavid Gibson /* 32e601613SDavid Gibson * Copyright (C) Paul Mackerras 1997. 42e601613SDavid Gibson */ 52e601613SDavid Gibson #include <stddef.h> 62e601613SDavid Gibson #include "types.h" 72e601613SDavid Gibson #include "elf.h" 82e601613SDavid Gibson #include "string.h" 92e601613SDavid Gibson #include "stdio.h" 102e601613SDavid Gibson #include "page.h" 112e601613SDavid Gibson #include "ops.h" 122e601613SDavid Gibson 132e601613SDavid Gibson #include "of.h" 142e601613SDavid Gibson 15163bed77SCédric Le Goater typedef u32 prom_arg_t; 16163bed77SCédric Le Goater 17fed23ed7SCédric Le Goater /* The following structure is used to communicate with open firmware. 18fed23ed7SCédric Le Goater * All arguments in and out are in big endian format. */ 19fed23ed7SCédric Le Goater struct prom_args { 20fed23ed7SCédric Le Goater __be32 service; /* Address of service name string. */ 21fed23ed7SCédric Le Goater __be32 nargs; /* Number of input arguments. */ 22fed23ed7SCédric Le Goater __be32 nret; /* Number of output arguments. */ 23fed23ed7SCédric Le Goater __be32 args[10]; /* Input/output arguments. */ 24fed23ed7SCédric Le Goater }; 25fed23ed7SCédric Le Goater 2693d39210SCédric Le Goater #ifdef __powerpc64__ 2793d39210SCédric Le Goater extern int prom(void *); 2893d39210SCédric Le Goater #else 292e601613SDavid Gibson static int (*prom) (void *); 3093d39210SCédric Le Goater #endif 312e601613SDavid Gibson 322e601613SDavid Gibson void of_init(void *promptr) 332e601613SDavid Gibson { 3493d39210SCédric Le Goater #ifndef __powerpc64__ 352e601613SDavid Gibson prom = (int (*)(void *))promptr; 3693d39210SCédric Le Goater #endif 372e601613SDavid Gibson } 382e601613SDavid Gibson 39fed23ed7SCédric Le Goater #define ADDR(x) (u32)(unsigned long)(x) 40fed23ed7SCédric Le Goater 412e601613SDavid Gibson int of_call_prom(const char *service, int nargs, int nret, ...) 422e601613SDavid Gibson { 432e601613SDavid Gibson int i; 44fed23ed7SCédric Le Goater struct prom_args args; 452e601613SDavid Gibson va_list list; 462e601613SDavid Gibson 47926e6940SCédric Le Goater args.service = cpu_to_be32(ADDR(service)); 48926e6940SCédric Le Goater args.nargs = cpu_to_be32(nargs); 49926e6940SCédric Le Goater args.nret = cpu_to_be32(nret); 502e601613SDavid Gibson 512e601613SDavid Gibson va_start(list, nret); 522e601613SDavid Gibson for (i = 0; i < nargs; i++) 53926e6940SCédric Le Goater args.args[i] = cpu_to_be32(va_arg(list, prom_arg_t)); 542e601613SDavid Gibson va_end(list); 552e601613SDavid Gibson 562e601613SDavid Gibson for (i = 0; i < nret; i++) 572e601613SDavid Gibson args.args[nargs+i] = 0; 582e601613SDavid Gibson 592e601613SDavid Gibson if (prom(&args) < 0) 609cc36bb0SCédric Le Goater return PROM_ERROR; 612e601613SDavid Gibson 62926e6940SCédric Le Goater return (nret > 0) ? be32_to_cpu(args.args[nargs]) : 0; 632e601613SDavid Gibson } 642e601613SDavid Gibson 652e601613SDavid Gibson static int of_call_prom_ret(const char *service, int nargs, int nret, 66163bed77SCédric Le Goater prom_arg_t *rets, ...) 672e601613SDavid Gibson { 682e601613SDavid Gibson int i; 69fed23ed7SCédric Le Goater struct prom_args args; 702e601613SDavid Gibson va_list list; 712e601613SDavid Gibson 72926e6940SCédric Le Goater args.service = cpu_to_be32(ADDR(service)); 73926e6940SCédric Le Goater args.nargs = cpu_to_be32(nargs); 74926e6940SCédric Le Goater args.nret = cpu_to_be32(nret); 752e601613SDavid Gibson 762e601613SDavid Gibson va_start(list, rets); 772e601613SDavid Gibson for (i = 0; i < nargs; i++) 78926e6940SCédric Le Goater args.args[i] = cpu_to_be32(va_arg(list, prom_arg_t)); 792e601613SDavid Gibson va_end(list); 802e601613SDavid Gibson 812e601613SDavid Gibson for (i = 0; i < nret; i++) 822e601613SDavid Gibson args.args[nargs+i] = 0; 832e601613SDavid Gibson 842e601613SDavid Gibson if (prom(&args) < 0) 859cc36bb0SCédric Le Goater return PROM_ERROR; 862e601613SDavid Gibson 879cc36bb0SCédric Le Goater if (rets != NULL) 882e601613SDavid Gibson for (i = 1; i < nret; ++i) 89926e6940SCédric Le Goater rets[i-1] = be32_to_cpu(args.args[nargs+i]); 902e601613SDavid Gibson 91926e6940SCédric Le Goater return (nret > 0) ? be32_to_cpu(args.args[nargs]) : 0; 922e601613SDavid Gibson } 932e601613SDavid Gibson 942e601613SDavid Gibson /* returns true if s2 is a prefix of s1 */ 952e601613SDavid Gibson static int string_match(const char *s1, const char *s2) 962e601613SDavid Gibson { 972e601613SDavid Gibson for (; *s2; ++s2) 982e601613SDavid Gibson if (*s1++ != *s2) 992e601613SDavid Gibson return 0; 1002e601613SDavid Gibson return 1; 1012e601613SDavid Gibson } 1022e601613SDavid Gibson 1032e601613SDavid Gibson /* 1042e601613SDavid Gibson * Older OF's require that when claiming a specific range of addresses, 1052e601613SDavid Gibson * we claim the physical space in the /memory node and the virtual 1062e601613SDavid Gibson * space in the chosen mmu node, and then do a map operation to 1072e601613SDavid Gibson * map virtual to physical. 1082e601613SDavid Gibson */ 1092e601613SDavid Gibson static int need_map = -1; 1102e601613SDavid Gibson static ihandle chosen_mmu; 11164130109SCédric Le Goater static ihandle memory; 1122e601613SDavid Gibson 1132e601613SDavid Gibson static int check_of_version(void) 1142e601613SDavid Gibson { 1152e601613SDavid Gibson phandle oprom, chosen; 1162e601613SDavid Gibson char version[64]; 1172e601613SDavid Gibson 11808464712SDavid Gibson oprom = of_finddevice("/openprom"); 1192e601613SDavid Gibson if (oprom == (phandle) -1) 1202e601613SDavid Gibson return 0; 12108464712SDavid Gibson if (of_getprop(oprom, "model", version, sizeof(version)) <= 0) 1222e601613SDavid Gibson return 0; 1232e601613SDavid Gibson version[sizeof(version)-1] = 0; 1242e601613SDavid Gibson printf("OF version = '%s'\r\n", version); 1252e601613SDavid Gibson if (!string_match(version, "Open Firmware, 1.") 1262e601613SDavid Gibson && !string_match(version, "FirmWorks,3.")) 1272e601613SDavid Gibson return 0; 12808464712SDavid Gibson chosen = of_finddevice("/chosen"); 1292e601613SDavid Gibson if (chosen == (phandle) -1) { 13008464712SDavid Gibson chosen = of_finddevice("/chosen@0"); 1312e601613SDavid Gibson if (chosen == (phandle) -1) { 1322e601613SDavid Gibson printf("no chosen\n"); 1332e601613SDavid Gibson return 0; 1342e601613SDavid Gibson } 1352e601613SDavid Gibson } 13608464712SDavid Gibson if (of_getprop(chosen, "mmu", &chosen_mmu, sizeof(chosen_mmu)) <= 0) { 1372e601613SDavid Gibson printf("no mmu\n"); 1382e601613SDavid Gibson return 0; 1392e601613SDavid Gibson } 14064130109SCédric Le Goater memory = of_call_prom("open", 1, 1, "/memory"); 14164130109SCédric Le Goater if (memory == PROM_ERROR) { 14264130109SCédric Le Goater memory = of_call_prom("open", 1, 1, "/memory@0"); 14364130109SCédric Le Goater if (memory == PROM_ERROR) { 1442e601613SDavid Gibson printf("no memory node\n"); 1452e601613SDavid Gibson return 0; 1462e601613SDavid Gibson } 1472e601613SDavid Gibson } 1482e601613SDavid Gibson printf("old OF detected\r\n"); 1492e601613SDavid Gibson return 1; 1502e601613SDavid Gibson } 1512e601613SDavid Gibson 152034e55e6SCédric Le Goater unsigned int of_claim(unsigned long virt, unsigned long size, 153034e55e6SCédric Le Goater unsigned long align) 1542e601613SDavid Gibson { 1552e601613SDavid Gibson int ret; 156163bed77SCédric Le Goater prom_arg_t result; 1572e601613SDavid Gibson 1582e601613SDavid Gibson if (need_map < 0) 1592e601613SDavid Gibson need_map = check_of_version(); 1602e601613SDavid Gibson if (align || !need_map) 161034e55e6SCédric Le Goater return of_call_prom("claim", 3, 1, virt, size, align); 1622e601613SDavid Gibson 1632e601613SDavid Gibson ret = of_call_prom_ret("call-method", 5, 2, &result, "claim", memory, 1642e601613SDavid Gibson align, size, virt); 1652e601613SDavid Gibson if (ret != 0 || result == -1) 166034e55e6SCédric Le Goater return -1; 1672e601613SDavid Gibson ret = of_call_prom_ret("call-method", 5, 2, &result, "claim", chosen_mmu, 1682e601613SDavid Gibson align, size, virt); 1692e601613SDavid Gibson /* 0x12 == coherent + read/write */ 1702e601613SDavid Gibson ret = of_call_prom("call-method", 6, 1, "map", chosen_mmu, 1712e601613SDavid Gibson 0x12, size, virt, virt); 172034e55e6SCédric Le Goater return virt; 1732e601613SDavid Gibson } 1742e601613SDavid Gibson 17508464712SDavid Gibson void *of_vmlinux_alloc(unsigned long size) 17608464712SDavid Gibson { 1779b09c6d9STony Breeds unsigned long start = (unsigned long)_start, end = (unsigned long)_end; 178034e55e6SCédric Le Goater unsigned long addr; 1799b09c6d9STony Breeds void *p; 18008464712SDavid Gibson 1819b09c6d9STony Breeds /* With some older POWER4 firmware we need to claim the area the kernel 1829b09c6d9STony Breeds * will reside in. Newer firmwares don't need this so we just ignore 1839b09c6d9STony Breeds * the return value. 1849b09c6d9STony Breeds */ 185034e55e6SCédric Le Goater addr = (unsigned long) of_claim(start, end - start, 0); 186034e55e6SCédric Le Goater printf("Trying to claim from 0x%lx to 0x%lx (0x%lx) got %lx\r\n", 1879b09c6d9STony Breeds start, end, end - start, addr); 1889b09c6d9STony Breeds 1899b09c6d9STony Breeds p = malloc(size); 19008464712SDavid Gibson if (!p) 19108464712SDavid Gibson fatal("Can't allocate memory for kernel image!\n\r"); 19208464712SDavid Gibson 19308464712SDavid Gibson return p; 19408464712SDavid Gibson } 19508464712SDavid Gibson 1962e601613SDavid Gibson void of_exit(void) 1972e601613SDavid Gibson { 1982e601613SDavid Gibson of_call_prom("exit", 0, 0); 1992e601613SDavid Gibson } 20008464712SDavid Gibson 20108464712SDavid Gibson /* 20208464712SDavid Gibson * OF device tree routines 20308464712SDavid Gibson */ 20408464712SDavid Gibson void *of_finddevice(const char *name) 20508464712SDavid Gibson { 206b636031aSCédric Le Goater return (void *) (unsigned long) of_call_prom("finddevice", 1, 1, name); 20708464712SDavid Gibson } 20808464712SDavid Gibson 20908464712SDavid Gibson int of_getprop(const void *phandle, const char *name, void *buf, 21008464712SDavid Gibson const int buflen) 21108464712SDavid Gibson { 21208464712SDavid Gibson return of_call_prom("getprop", 4, 1, phandle, name, buf, buflen); 21308464712SDavid Gibson } 21408464712SDavid Gibson 21508464712SDavid Gibson int of_setprop(const void *phandle, const char *name, const void *buf, 21608464712SDavid Gibson const int buflen) 21708464712SDavid Gibson { 21808464712SDavid Gibson return of_call_prom("setprop", 4, 1, phandle, name, buf, buflen); 21908464712SDavid Gibson } 220