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