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