1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (C) Paul Mackerras 1997. 4 */ 5 #include <stddef.h> 6 #include "types.h" 7 #include "elf.h" 8 #include "string.h" 9 #include "stdio.h" 10 #include "page.h" 11 #include "ops.h" 12 13 #include "of.h" 14 15 typedef u32 prom_arg_t; 16 17 /* The following structure is used to communicate with open firmware. 18 * All arguments in and out are in big endian format. */ 19 struct prom_args { 20 __be32 service; /* Address of service name string. */ 21 __be32 nargs; /* Number of input arguments. */ 22 __be32 nret; /* Number of output arguments. */ 23 __be32 args[10]; /* Input/output arguments. */ 24 }; 25 26 #ifdef __powerpc64__ 27 extern int prom(void *); 28 #else 29 static int (*prom) (void *); 30 #endif 31 32 void of_init(void *promptr) 33 { 34 #ifndef __powerpc64__ 35 prom = (int (*)(void *))promptr; 36 #endif 37 } 38 39 #define ADDR(x) (u32)(unsigned long)(x) 40 41 int of_call_prom(const char *service, int nargs, int nret, ...) 42 { 43 int i; 44 struct prom_args args; 45 va_list list; 46 47 args.service = cpu_to_be32(ADDR(service)); 48 args.nargs = cpu_to_be32(nargs); 49 args.nret = cpu_to_be32(nret); 50 51 va_start(list, nret); 52 for (i = 0; i < nargs; i++) 53 args.args[i] = cpu_to_be32(va_arg(list, prom_arg_t)); 54 va_end(list); 55 56 for (i = 0; i < nret; i++) 57 args.args[nargs+i] = 0; 58 59 if (prom(&args) < 0) 60 return PROM_ERROR; 61 62 return (nret > 0) ? be32_to_cpu(args.args[nargs]) : 0; 63 } 64 65 static int of_call_prom_ret(const char *service, int nargs, int nret, 66 prom_arg_t *rets, ...) 67 { 68 int i; 69 struct prom_args args; 70 va_list list; 71 72 args.service = cpu_to_be32(ADDR(service)); 73 args.nargs = cpu_to_be32(nargs); 74 args.nret = cpu_to_be32(nret); 75 76 va_start(list, rets); 77 for (i = 0; i < nargs; i++) 78 args.args[i] = cpu_to_be32(va_arg(list, prom_arg_t)); 79 va_end(list); 80 81 for (i = 0; i < nret; i++) 82 args.args[nargs+i] = 0; 83 84 if (prom(&args) < 0) 85 return PROM_ERROR; 86 87 if (rets != NULL) 88 for (i = 1; i < nret; ++i) 89 rets[i-1] = be32_to_cpu(args.args[nargs+i]); 90 91 return (nret > 0) ? be32_to_cpu(args.args[nargs]) : 0; 92 } 93 94 /* returns true if s2 is a prefix of s1 */ 95 static int string_match(const char *s1, const char *s2) 96 { 97 for (; *s2; ++s2) 98 if (*s1++ != *s2) 99 return 0; 100 return 1; 101 } 102 103 /* 104 * Older OF's require that when claiming a specific range of addresses, 105 * we claim the physical space in the /memory node and the virtual 106 * space in the chosen mmu node, and then do a map operation to 107 * map virtual to physical. 108 */ 109 static int need_map = -1; 110 static ihandle chosen_mmu; 111 static ihandle memory; 112 113 static int check_of_version(void) 114 { 115 phandle oprom, chosen; 116 char version[64]; 117 118 oprom = of_finddevice("/openprom"); 119 if (oprom == (phandle) -1) 120 return 0; 121 if (of_getprop(oprom, "model", version, sizeof(version)) <= 0) 122 return 0; 123 version[sizeof(version)-1] = 0; 124 printf("OF version = '%s'\r\n", version); 125 if (!string_match(version, "Open Firmware, 1.") 126 && !string_match(version, "FirmWorks,3.")) 127 return 0; 128 chosen = of_finddevice("/chosen"); 129 if (chosen == (phandle) -1) { 130 chosen = of_finddevice("/chosen@0"); 131 if (chosen == (phandle) -1) { 132 printf("no chosen\n"); 133 return 0; 134 } 135 } 136 if (of_getprop(chosen, "mmu", &chosen_mmu, sizeof(chosen_mmu)) <= 0) { 137 printf("no mmu\n"); 138 return 0; 139 } 140 memory = of_call_prom("open", 1, 1, "/memory"); 141 if (memory == PROM_ERROR) { 142 memory = of_call_prom("open", 1, 1, "/memory@0"); 143 if (memory == PROM_ERROR) { 144 printf("no memory node\n"); 145 return 0; 146 } 147 } 148 printf("old OF detected\r\n"); 149 return 1; 150 } 151 152 unsigned int of_claim(unsigned long virt, unsigned long size, 153 unsigned long align) 154 { 155 int ret; 156 prom_arg_t result; 157 158 if (need_map < 0) 159 need_map = check_of_version(); 160 if (align || !need_map) 161 return of_call_prom("claim", 3, 1, virt, size, align); 162 163 ret = of_call_prom_ret("call-method", 5, 2, &result, "claim", memory, 164 align, size, virt); 165 if (ret != 0 || result == -1) 166 return -1; 167 ret = of_call_prom_ret("call-method", 5, 2, &result, "claim", chosen_mmu, 168 align, size, virt); 169 /* 0x12 == coherent + read/write */ 170 ret = of_call_prom("call-method", 6, 1, "map", chosen_mmu, 171 0x12, size, virt, virt); 172 return virt; 173 } 174 175 void *of_vmlinux_alloc(unsigned long size) 176 { 177 unsigned long start = (unsigned long)_start, end = (unsigned long)_end; 178 unsigned long addr; 179 void *p; 180 181 /* With some older POWER4 firmware we need to claim the area the kernel 182 * will reside in. Newer firmwares don't need this so we just ignore 183 * the return value. 184 */ 185 addr = (unsigned long) of_claim(start, end - start, 0); 186 printf("Trying to claim from 0x%lx to 0x%lx (0x%lx) got %lx\r\n", 187 start, end, end - start, addr); 188 189 p = malloc(size); 190 if (!p) 191 fatal("Can't allocate memory for kernel image!\n\r"); 192 193 return p; 194 } 195 196 void of_exit(void) 197 { 198 of_call_prom("exit", 0, 0); 199 } 200 201 /* 202 * OF device tree routines 203 */ 204 void *of_finddevice(const char *name) 205 { 206 return (void *) (unsigned long) of_call_prom("finddevice", 1, 1, name); 207 } 208 209 int of_getprop(const void *phandle, const char *name, void *buf, 210 const int buflen) 211 { 212 return of_call_prom("getprop", 4, 1, phandle, name, buf, buflen); 213 } 214 215 int of_setprop(const void *phandle, const char *name, const void *buf, 216 const int buflen) 217 { 218 return of_call_prom("setprop", 4, 1, phandle, name, buf, buflen); 219 } 220
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.