162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) Paul Mackerras 1997. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci#include <stddef.h> 662306a36Sopenharmony_ci#include "types.h" 762306a36Sopenharmony_ci#include "elf.h" 862306a36Sopenharmony_ci#include "string.h" 962306a36Sopenharmony_ci#include "stdio.h" 1062306a36Sopenharmony_ci#include "page.h" 1162306a36Sopenharmony_ci#include "ops.h" 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include "of.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_citypedef u32 prom_arg_t; 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/* The following structure is used to communicate with open firmware. 1862306a36Sopenharmony_ci * All arguments in and out are in big endian format. */ 1962306a36Sopenharmony_cistruct prom_args { 2062306a36Sopenharmony_ci __be32 service; /* Address of service name string. */ 2162306a36Sopenharmony_ci __be32 nargs; /* Number of input arguments. */ 2262306a36Sopenharmony_ci __be32 nret; /* Number of output arguments. */ 2362306a36Sopenharmony_ci __be32 args[10]; /* Input/output arguments. */ 2462306a36Sopenharmony_ci}; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#ifdef __powerpc64__ 2762306a36Sopenharmony_ciextern int prom(void *); 2862306a36Sopenharmony_ci#else 2962306a36Sopenharmony_cistatic int (*prom) (void *); 3062306a36Sopenharmony_ci#endif 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_civoid of_init(void *promptr) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci#ifndef __powerpc64__ 3562306a36Sopenharmony_ci prom = (int (*)(void *))promptr; 3662306a36Sopenharmony_ci#endif 3762306a36Sopenharmony_ci} 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#define ADDR(x) (u32)(unsigned long)(x) 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ciint of_call_prom(const char *service, int nargs, int nret, ...) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci int i; 4462306a36Sopenharmony_ci struct prom_args args; 4562306a36Sopenharmony_ci va_list list; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci args.service = cpu_to_be32(ADDR(service)); 4862306a36Sopenharmony_ci args.nargs = cpu_to_be32(nargs); 4962306a36Sopenharmony_ci args.nret = cpu_to_be32(nret); 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci va_start(list, nret); 5262306a36Sopenharmony_ci for (i = 0; i < nargs; i++) 5362306a36Sopenharmony_ci args.args[i] = cpu_to_be32(va_arg(list, prom_arg_t)); 5462306a36Sopenharmony_ci va_end(list); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci for (i = 0; i < nret; i++) 5762306a36Sopenharmony_ci args.args[nargs+i] = 0; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci if (prom(&args) < 0) 6062306a36Sopenharmony_ci return PROM_ERROR; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci return (nret > 0) ? be32_to_cpu(args.args[nargs]) : 0; 6362306a36Sopenharmony_ci} 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic int of_call_prom_ret(const char *service, int nargs, int nret, 6662306a36Sopenharmony_ci prom_arg_t *rets, ...) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci int i; 6962306a36Sopenharmony_ci struct prom_args args; 7062306a36Sopenharmony_ci va_list list; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci args.service = cpu_to_be32(ADDR(service)); 7362306a36Sopenharmony_ci args.nargs = cpu_to_be32(nargs); 7462306a36Sopenharmony_ci args.nret = cpu_to_be32(nret); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci va_start(list, rets); 7762306a36Sopenharmony_ci for (i = 0; i < nargs; i++) 7862306a36Sopenharmony_ci args.args[i] = cpu_to_be32(va_arg(list, prom_arg_t)); 7962306a36Sopenharmony_ci va_end(list); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci for (i = 0; i < nret; i++) 8262306a36Sopenharmony_ci args.args[nargs+i] = 0; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci if (prom(&args) < 0) 8562306a36Sopenharmony_ci return PROM_ERROR; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci if (rets != NULL) 8862306a36Sopenharmony_ci for (i = 1; i < nret; ++i) 8962306a36Sopenharmony_ci rets[i-1] = be32_to_cpu(args.args[nargs+i]); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci return (nret > 0) ? be32_to_cpu(args.args[nargs]) : 0; 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci/* returns true if s2 is a prefix of s1 */ 9562306a36Sopenharmony_cistatic int string_match(const char *s1, const char *s2) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci for (; *s2; ++s2) 9862306a36Sopenharmony_ci if (*s1++ != *s2) 9962306a36Sopenharmony_ci return 0; 10062306a36Sopenharmony_ci return 1; 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci/* 10462306a36Sopenharmony_ci * Older OF's require that when claiming a specific range of addresses, 10562306a36Sopenharmony_ci * we claim the physical space in the /memory node and the virtual 10662306a36Sopenharmony_ci * space in the chosen mmu node, and then do a map operation to 10762306a36Sopenharmony_ci * map virtual to physical. 10862306a36Sopenharmony_ci */ 10962306a36Sopenharmony_cistatic int need_map = -1; 11062306a36Sopenharmony_cistatic ihandle chosen_mmu; 11162306a36Sopenharmony_cistatic ihandle memory; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic int check_of_version(void) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci phandle oprom, chosen; 11662306a36Sopenharmony_ci char version[64]; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci oprom = of_finddevice("/openprom"); 11962306a36Sopenharmony_ci if (oprom == (phandle) -1) 12062306a36Sopenharmony_ci return 0; 12162306a36Sopenharmony_ci if (of_getprop(oprom, "model", version, sizeof(version)) <= 0) 12262306a36Sopenharmony_ci return 0; 12362306a36Sopenharmony_ci version[sizeof(version)-1] = 0; 12462306a36Sopenharmony_ci printf("OF version = '%s'\r\n", version); 12562306a36Sopenharmony_ci if (!string_match(version, "Open Firmware, 1.") 12662306a36Sopenharmony_ci && !string_match(version, "FirmWorks,3.")) 12762306a36Sopenharmony_ci return 0; 12862306a36Sopenharmony_ci chosen = of_finddevice("/chosen"); 12962306a36Sopenharmony_ci if (chosen == (phandle) -1) { 13062306a36Sopenharmony_ci chosen = of_finddevice("/chosen@0"); 13162306a36Sopenharmony_ci if (chosen == (phandle) -1) { 13262306a36Sopenharmony_ci printf("no chosen\n"); 13362306a36Sopenharmony_ci return 0; 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci if (of_getprop(chosen, "mmu", &chosen_mmu, sizeof(chosen_mmu)) <= 0) { 13762306a36Sopenharmony_ci printf("no mmu\n"); 13862306a36Sopenharmony_ci return 0; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci memory = of_call_prom("open", 1, 1, "/memory"); 14162306a36Sopenharmony_ci if (memory == PROM_ERROR) { 14262306a36Sopenharmony_ci memory = of_call_prom("open", 1, 1, "/memory@0"); 14362306a36Sopenharmony_ci if (memory == PROM_ERROR) { 14462306a36Sopenharmony_ci printf("no memory node\n"); 14562306a36Sopenharmony_ci return 0; 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci printf("old OF detected\r\n"); 14962306a36Sopenharmony_ci return 1; 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ciunsigned int of_claim(unsigned long virt, unsigned long size, 15362306a36Sopenharmony_ci unsigned long align) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci int ret; 15662306a36Sopenharmony_ci prom_arg_t result; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci if (need_map < 0) 15962306a36Sopenharmony_ci need_map = check_of_version(); 16062306a36Sopenharmony_ci if (align || !need_map) 16162306a36Sopenharmony_ci return of_call_prom("claim", 3, 1, virt, size, align); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci ret = of_call_prom_ret("call-method", 5, 2, &result, "claim", memory, 16462306a36Sopenharmony_ci align, size, virt); 16562306a36Sopenharmony_ci if (ret != 0 || result == -1) 16662306a36Sopenharmony_ci return -1; 16762306a36Sopenharmony_ci ret = of_call_prom_ret("call-method", 5, 2, &result, "claim", chosen_mmu, 16862306a36Sopenharmony_ci align, size, virt); 16962306a36Sopenharmony_ci /* 0x12 == coherent + read/write */ 17062306a36Sopenharmony_ci ret = of_call_prom("call-method", 6, 1, "map", chosen_mmu, 17162306a36Sopenharmony_ci 0x12, size, virt, virt); 17262306a36Sopenharmony_ci return virt; 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_civoid *of_vmlinux_alloc(unsigned long size) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci unsigned long start = (unsigned long)_start, end = (unsigned long)_end; 17862306a36Sopenharmony_ci unsigned long addr; 17962306a36Sopenharmony_ci void *p; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci /* With some older POWER4 firmware we need to claim the area the kernel 18262306a36Sopenharmony_ci * will reside in. Newer firmwares don't need this so we just ignore 18362306a36Sopenharmony_ci * the return value. 18462306a36Sopenharmony_ci */ 18562306a36Sopenharmony_ci addr = (unsigned long) of_claim(start, end - start, 0); 18662306a36Sopenharmony_ci printf("Trying to claim from 0x%lx to 0x%lx (0x%lx) got %lx\r\n", 18762306a36Sopenharmony_ci start, end, end - start, addr); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci p = malloc(size); 19062306a36Sopenharmony_ci if (!p) 19162306a36Sopenharmony_ci fatal("Can't allocate memory for kernel image!\n\r"); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci return p; 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_civoid of_exit(void) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci of_call_prom("exit", 0, 0); 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci/* 20262306a36Sopenharmony_ci * OF device tree routines 20362306a36Sopenharmony_ci */ 20462306a36Sopenharmony_civoid *of_finddevice(const char *name) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci return (void *) (unsigned long) of_call_prom("finddevice", 1, 1, name); 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ciint of_getprop(const void *phandle, const char *name, void *buf, 21062306a36Sopenharmony_ci const int buflen) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci return of_call_prom("getprop", 4, 1, phandle, name, buf, buflen); 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ciint of_setprop(const void *phandle, const char *name, const void *buf, 21662306a36Sopenharmony_ci const int buflen) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci return of_call_prom("setprop", 4, 1, phandle, name, buf, buflen); 21962306a36Sopenharmony_ci} 220