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