18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) Paul Mackerras 1997. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci#include <stddef.h> 68c2ecf20Sopenharmony_ci#include "types.h" 78c2ecf20Sopenharmony_ci#include "elf.h" 88c2ecf20Sopenharmony_ci#include "string.h" 98c2ecf20Sopenharmony_ci#include "stdio.h" 108c2ecf20Sopenharmony_ci#include "page.h" 118c2ecf20Sopenharmony_ci#include "ops.h" 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include "of.h" 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_citypedef u32 prom_arg_t; 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci/* The following structure is used to communicate with open firmware. 188c2ecf20Sopenharmony_ci * All arguments in and out are in big endian format. */ 198c2ecf20Sopenharmony_cistruct prom_args { 208c2ecf20Sopenharmony_ci __be32 service; /* Address of service name string. */ 218c2ecf20Sopenharmony_ci __be32 nargs; /* Number of input arguments. */ 228c2ecf20Sopenharmony_ci __be32 nret; /* Number of output arguments. */ 238c2ecf20Sopenharmony_ci __be32 args[10]; /* Input/output arguments. */ 248c2ecf20Sopenharmony_ci}; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#ifdef __powerpc64__ 278c2ecf20Sopenharmony_ciextern int prom(void *); 288c2ecf20Sopenharmony_ci#else 298c2ecf20Sopenharmony_cistatic int (*prom) (void *); 308c2ecf20Sopenharmony_ci#endif 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_civoid of_init(void *promptr) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci#ifndef __powerpc64__ 358c2ecf20Sopenharmony_ci prom = (int (*)(void *))promptr; 368c2ecf20Sopenharmony_ci#endif 378c2ecf20Sopenharmony_ci} 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define ADDR(x) (u32)(unsigned long)(x) 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ciint of_call_prom(const char *service, int nargs, int nret, ...) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci int i; 448c2ecf20Sopenharmony_ci struct prom_args args; 458c2ecf20Sopenharmony_ci va_list list; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci args.service = cpu_to_be32(ADDR(service)); 488c2ecf20Sopenharmony_ci args.nargs = cpu_to_be32(nargs); 498c2ecf20Sopenharmony_ci args.nret = cpu_to_be32(nret); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci va_start(list, nret); 528c2ecf20Sopenharmony_ci for (i = 0; i < nargs; i++) 538c2ecf20Sopenharmony_ci args.args[i] = cpu_to_be32(va_arg(list, prom_arg_t)); 548c2ecf20Sopenharmony_ci va_end(list); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci for (i = 0; i < nret; i++) 578c2ecf20Sopenharmony_ci args.args[nargs+i] = 0; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci if (prom(&args) < 0) 608c2ecf20Sopenharmony_ci return PROM_ERROR; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci return (nret > 0) ? be32_to_cpu(args.args[nargs]) : 0; 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic int of_call_prom_ret(const char *service, int nargs, int nret, 668c2ecf20Sopenharmony_ci prom_arg_t *rets, ...) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci int i; 698c2ecf20Sopenharmony_ci struct prom_args args; 708c2ecf20Sopenharmony_ci va_list list; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci args.service = cpu_to_be32(ADDR(service)); 738c2ecf20Sopenharmony_ci args.nargs = cpu_to_be32(nargs); 748c2ecf20Sopenharmony_ci args.nret = cpu_to_be32(nret); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci va_start(list, rets); 778c2ecf20Sopenharmony_ci for (i = 0; i < nargs; i++) 788c2ecf20Sopenharmony_ci args.args[i] = cpu_to_be32(va_arg(list, prom_arg_t)); 798c2ecf20Sopenharmony_ci va_end(list); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci for (i = 0; i < nret; i++) 828c2ecf20Sopenharmony_ci args.args[nargs+i] = 0; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci if (prom(&args) < 0) 858c2ecf20Sopenharmony_ci return PROM_ERROR; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci if (rets != NULL) 888c2ecf20Sopenharmony_ci for (i = 1; i < nret; ++i) 898c2ecf20Sopenharmony_ci rets[i-1] = be32_to_cpu(args.args[nargs+i]); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci return (nret > 0) ? be32_to_cpu(args.args[nargs]) : 0; 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci/* returns true if s2 is a prefix of s1 */ 958c2ecf20Sopenharmony_cistatic int string_match(const char *s1, const char *s2) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci for (; *s2; ++s2) 988c2ecf20Sopenharmony_ci if (*s1++ != *s2) 998c2ecf20Sopenharmony_ci return 0; 1008c2ecf20Sopenharmony_ci return 1; 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci/* 1048c2ecf20Sopenharmony_ci * Older OF's require that when claiming a specific range of addresses, 1058c2ecf20Sopenharmony_ci * we claim the physical space in the /memory node and the virtual 1068c2ecf20Sopenharmony_ci * space in the chosen mmu node, and then do a map operation to 1078c2ecf20Sopenharmony_ci * map virtual to physical. 1088c2ecf20Sopenharmony_ci */ 1098c2ecf20Sopenharmony_cistatic int need_map = -1; 1108c2ecf20Sopenharmony_cistatic ihandle chosen_mmu; 1118c2ecf20Sopenharmony_cistatic ihandle memory; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic int check_of_version(void) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci phandle oprom, chosen; 1168c2ecf20Sopenharmony_ci char version[64]; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci oprom = of_finddevice("/openprom"); 1198c2ecf20Sopenharmony_ci if (oprom == (phandle) -1) 1208c2ecf20Sopenharmony_ci return 0; 1218c2ecf20Sopenharmony_ci if (of_getprop(oprom, "model", version, sizeof(version)) <= 0) 1228c2ecf20Sopenharmony_ci return 0; 1238c2ecf20Sopenharmony_ci version[sizeof(version)-1] = 0; 1248c2ecf20Sopenharmony_ci printf("OF version = '%s'\r\n", version); 1258c2ecf20Sopenharmony_ci if (!string_match(version, "Open Firmware, 1.") 1268c2ecf20Sopenharmony_ci && !string_match(version, "FirmWorks,3.")) 1278c2ecf20Sopenharmony_ci return 0; 1288c2ecf20Sopenharmony_ci chosen = of_finddevice("/chosen"); 1298c2ecf20Sopenharmony_ci if (chosen == (phandle) -1) { 1308c2ecf20Sopenharmony_ci chosen = of_finddevice("/chosen@0"); 1318c2ecf20Sopenharmony_ci if (chosen == (phandle) -1) { 1328c2ecf20Sopenharmony_ci printf("no chosen\n"); 1338c2ecf20Sopenharmony_ci return 0; 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci if (of_getprop(chosen, "mmu", &chosen_mmu, sizeof(chosen_mmu)) <= 0) { 1378c2ecf20Sopenharmony_ci printf("no mmu\n"); 1388c2ecf20Sopenharmony_ci return 0; 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci memory = of_call_prom("open", 1, 1, "/memory"); 1418c2ecf20Sopenharmony_ci if (memory == PROM_ERROR) { 1428c2ecf20Sopenharmony_ci memory = of_call_prom("open", 1, 1, "/memory@0"); 1438c2ecf20Sopenharmony_ci if (memory == PROM_ERROR) { 1448c2ecf20Sopenharmony_ci printf("no memory node\n"); 1458c2ecf20Sopenharmony_ci return 0; 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci printf("old OF detected\r\n"); 1498c2ecf20Sopenharmony_ci return 1; 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ciunsigned int of_claim(unsigned long virt, unsigned long size, 1538c2ecf20Sopenharmony_ci unsigned long align) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci int ret; 1568c2ecf20Sopenharmony_ci prom_arg_t result; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci if (need_map < 0) 1598c2ecf20Sopenharmony_ci need_map = check_of_version(); 1608c2ecf20Sopenharmony_ci if (align || !need_map) 1618c2ecf20Sopenharmony_ci return of_call_prom("claim", 3, 1, virt, size, align); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci ret = of_call_prom_ret("call-method", 5, 2, &result, "claim", memory, 1648c2ecf20Sopenharmony_ci align, size, virt); 1658c2ecf20Sopenharmony_ci if (ret != 0 || result == -1) 1668c2ecf20Sopenharmony_ci return -1; 1678c2ecf20Sopenharmony_ci ret = of_call_prom_ret("call-method", 5, 2, &result, "claim", chosen_mmu, 1688c2ecf20Sopenharmony_ci align, size, virt); 1698c2ecf20Sopenharmony_ci /* 0x12 == coherent + read/write */ 1708c2ecf20Sopenharmony_ci ret = of_call_prom("call-method", 6, 1, "map", chosen_mmu, 1718c2ecf20Sopenharmony_ci 0x12, size, virt, virt); 1728c2ecf20Sopenharmony_ci return virt; 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_civoid *of_vmlinux_alloc(unsigned long size) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci unsigned long start = (unsigned long)_start, end = (unsigned long)_end; 1788c2ecf20Sopenharmony_ci unsigned long addr; 1798c2ecf20Sopenharmony_ci void *p; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci /* With some older POWER4 firmware we need to claim the area the kernel 1828c2ecf20Sopenharmony_ci * will reside in. Newer firmwares don't need this so we just ignore 1838c2ecf20Sopenharmony_ci * the return value. 1848c2ecf20Sopenharmony_ci */ 1858c2ecf20Sopenharmony_ci addr = (unsigned long) of_claim(start, end - start, 0); 1868c2ecf20Sopenharmony_ci printf("Trying to claim from 0x%lx to 0x%lx (0x%lx) got %lx\r\n", 1878c2ecf20Sopenharmony_ci start, end, end - start, addr); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci p = malloc(size); 1908c2ecf20Sopenharmony_ci if (!p) 1918c2ecf20Sopenharmony_ci fatal("Can't allocate memory for kernel image!\n\r"); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci return p; 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_civoid of_exit(void) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci of_call_prom("exit", 0, 0); 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci/* 2028c2ecf20Sopenharmony_ci * OF device tree routines 2038c2ecf20Sopenharmony_ci */ 2048c2ecf20Sopenharmony_civoid *of_finddevice(const char *name) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci return (void *) (unsigned long) of_call_prom("finddevice", 1, 1, name); 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ciint of_getprop(const void *phandle, const char *name, void *buf, 2108c2ecf20Sopenharmony_ci const int buflen) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci return of_call_prom("getprop", 4, 1, phandle, name, buf, buflen); 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ciint of_setprop(const void *phandle, const char *name, const void *buf, 2168c2ecf20Sopenharmony_ci const int buflen) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci return of_call_prom("setprop", 4, 1, phandle, name, buf, buflen); 2198c2ecf20Sopenharmony_ci} 220