1 /* 2 * Copyright (C) Paul Mackerras 1997. 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License 6 * as published by the Free Software Foundation; either version 7 * 2 of the License, or (at your option) any later version. 8 */ 9 #include <stdarg.h> 10 #include <stddef.h> 11 #include "types.h" 12 #include "elf.h" 13 #include "string.h" 14 #include "stdio.h" 15 #include "page.h" 16 #include "ops.h" 17 18 typedef void *ihandle; 19 typedef void *phandle; 20 21 extern char _end[]; 22 23 /* Value picked to match that used by yaboot */ 24 #define PROG_START 0x01400000 /* only used on 64-bit systems */ 25 #define RAM_END (512<<20) /* Fixme: use OF */ 26 #define ONE_MB 0x100000 27 28 int (*prom) (void *); 29 30 31 static unsigned long claim_base; 32 33 static int call_prom(const char *service, int nargs, int nret, ...) 34 { 35 int i; 36 struct prom_args { 37 const char *service; 38 int nargs; 39 int nret; 40 unsigned int args[12]; 41 } args; 42 va_list list; 43 44 args.service = service; 45 args.nargs = nargs; 46 args.nret = nret; 47 48 va_start(list, nret); 49 for (i = 0; i < nargs; i++) 50 args.args[i] = va_arg(list, unsigned int); 51 va_end(list); 52 53 for (i = 0; i < nret; i++) 54 args.args[nargs+i] = 0; 55 56 if (prom(&args) < 0) 57 return -1; 58 59 return (nret > 0)? args.args[nargs]: 0; 60 } 61 62 static int call_prom_ret(const char *service, int nargs, int nret, 63 unsigned int *rets, ...) 64 { 65 int i; 66 struct prom_args { 67 const char *service; 68 int nargs; 69 int nret; 70 unsigned int args[12]; 71 } args; 72 va_list list; 73 74 args.service = service; 75 args.nargs = nargs; 76 args.nret = nret; 77 78 va_start(list, rets); 79 for (i = 0; i < nargs; i++) 80 args.args[i] = va_arg(list, unsigned int); 81 va_end(list); 82 83 for (i = 0; i < nret; i++) 84 args.args[nargs+i] = 0; 85 86 if (prom(&args) < 0) 87 return -1; 88 89 if (rets != (void *) 0) 90 for (i = 1; i < nret; ++i) 91 rets[i-1] = args.args[nargs+i]; 92 93 return (nret > 0)? args.args[nargs]: 0; 94 } 95 96 /* 97 * Older OF's require that when claiming a specific range of addresses, 98 * we claim the physical space in the /memory node and the virtual 99 * space in the chosen mmu node, and then do a map operation to 100 * map virtual to physical. 101 */ 102 static int need_map = -1; 103 static ihandle chosen_mmu; 104 static phandle memory; 105 106 /* returns true if s2 is a prefix of s1 */ 107 static int string_match(const char *s1, const char *s2) 108 { 109 for (; *s2; ++s2) 110 if (*s1++ != *s2) 111 return 0; 112 return 1; 113 } 114 115 static int check_of_version(void) 116 { 117 phandle oprom, chosen; 118 char version[64]; 119 120 oprom = finddevice("/openprom"); 121 if (oprom == (phandle) -1) 122 return 0; 123 if (getprop(oprom, "model", version, sizeof(version)) <= 0) 124 return 0; 125 version[sizeof(version)-1] = 0; 126 printf("OF version = '%s'\r\n", version); 127 if (!string_match(version, "Open Firmware, 1.") 128 && !string_match(version, "FirmWorks,3.")) 129 return 0; 130 chosen = finddevice("/chosen"); 131 if (chosen == (phandle) -1) { 132 chosen = finddevice("/chosen@0"); 133 if (chosen == (phandle) -1) { 134 printf("no chosen\n"); 135 return 0; 136 } 137 } 138 if (getprop(chosen, "mmu", &chosen_mmu, sizeof(chosen_mmu)) <= 0) { 139 printf("no mmu\n"); 140 return 0; 141 } 142 memory = (ihandle) call_prom("open", 1, 1, "/memory"); 143 if (memory == (ihandle) -1) { 144 memory = (ihandle) call_prom("open", 1, 1, "/memory@0"); 145 if (memory == (ihandle) -1) { 146 printf("no memory node\n"); 147 return 0; 148 } 149 } 150 printf("old OF detected\r\n"); 151 return 1; 152 } 153 154 static void *claim(unsigned long virt, unsigned long size, unsigned long align) 155 { 156 int ret; 157 unsigned int result; 158 159 if (need_map < 0) 160 need_map = check_of_version(); 161 if (align || !need_map) 162 return (void *) call_prom("claim", 3, 1, virt, size, align); 163 164 ret = call_prom_ret("call-method", 5, 2, &result, "claim", memory, 165 align, size, virt); 166 if (ret != 0 || result == -1) 167 return (void *) -1; 168 ret = call_prom_ret("call-method", 5, 2, &result, "claim", chosen_mmu, 169 align, size, virt); 170 /* 0x12 == coherent + read/write */ 171 ret = call_prom("call-method", 6, 1, "map", chosen_mmu, 172 0x12, size, virt, virt); 173 return (void *) virt; 174 } 175 176 static void *of_try_claim(unsigned long size) 177 { 178 unsigned long addr = 0; 179 180 if (claim_base == 0) 181 claim_base = _ALIGN_UP((unsigned long)_end, ONE_MB); 182 183 for(; claim_base < RAM_END; claim_base += ONE_MB) { 184 #ifdef DEBUG 185 printf(" trying: 0x%08lx\n\r", claim_base); 186 #endif 187 addr = (unsigned long)claim(claim_base, size, 0); 188 if ((void *)addr != (void *)-1) 189 break; 190 } 191 if (addr == 0) 192 return NULL; 193 claim_base = PAGE_ALIGN(claim_base + size); 194 return (void *)addr; 195 } 196 197 static void of_image_hdr(const void *hdr) 198 { 199 const Elf64_Ehdr *elf64 = hdr; 200 201 if (elf64->e_ident[EI_CLASS] == ELFCLASS64) { 202 /* 203 * Maintain a "magic" minimum address. This keeps some older 204 * firmware platforms running. 205 */ 206 if (claim_base < PROG_START) 207 claim_base = PROG_START; 208 } 209 } 210 211 static void *of_vmlinux_alloc(unsigned long size) 212 { 213 void *p = malloc(size); 214 215 if (!p) 216 fatal("Can't allocate memory for kernel image!\n\r"); 217 218 return p; 219 } 220 221 static void of_exit(void) 222 { 223 call_prom("exit", 0, 0); 224 } 225 226 /* 227 * OF device tree routines 228 */ 229 static void *of_finddevice(const char *name) 230 { 231 return (phandle) call_prom("finddevice", 1, 1, name); 232 } 233 234 static int of_getprop(const void *phandle, const char *name, void *buf, 235 const int buflen) 236 { 237 return call_prom("getprop", 4, 1, phandle, name, buf, buflen); 238 } 239 240 static int of_setprop(const void *phandle, const char *name, const void *buf, 241 const int buflen) 242 { 243 return call_prom("setprop", 4, 1, phandle, name, buf, buflen); 244 } 245 246 /* 247 * OF console routines 248 */ 249 static void *of_stdout_handle; 250 251 static int of_console_open(void) 252 { 253 void *devp; 254 255 if (((devp = finddevice("/chosen")) != NULL) 256 && (getprop(devp, "stdout", &of_stdout_handle, 257 sizeof(of_stdout_handle)) 258 == sizeof(of_stdout_handle))) 259 return 0; 260 261 return -1; 262 } 263 264 static void of_console_write(char *buf, int len) 265 { 266 call_prom("write", 3, 1, of_stdout_handle, buf, len); 267 } 268 269 void platform_init(unsigned long a1, unsigned long a2, void *promptr) 270 { 271 platform_ops.image_hdr = of_image_hdr; 272 platform_ops.malloc = of_try_claim; 273 platform_ops.exit = of_exit; 274 platform_ops.vmlinux_alloc = of_vmlinux_alloc; 275 276 dt_ops.finddevice = of_finddevice; 277 dt_ops.getprop = of_getprop; 278 dt_ops.setprop = of_setprop; 279 280 console_ops.open = of_console_open; 281 console_ops.write = of_console_write; 282 283 prom = (int (*)(void *))promptr; 284 loader_info.promptr = promptr; 285 if (a1 && a2 && a2 != 0xdeadbeef) { 286 loader_info.initrd_addr = a1; 287 loader_info.initrd_size = a2; 288 } 289 } 290