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 ihandle 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 = of_call_prom("open", 1, 1, "/memory"); 139 if (memory == PROM_ERROR) { 140 memory = of_call_prom("open", 1, 1, "/memory@0"); 141 if (memory == PROM_ERROR) { 142 printf("no memory node\n"); 143 return 0; 144 } 145 } 146 printf("old OF detected\r\n"); 147 return 1; 148 } 149 150 unsigned int of_claim(unsigned long virt, unsigned long size, 151 unsigned long align) 152 { 153 int ret; 154 prom_arg_t result; 155 156 if (need_map < 0) 157 need_map = check_of_version(); 158 if (align || !need_map) 159 return of_call_prom("claim", 3, 1, virt, size, align); 160 161 ret = of_call_prom_ret("call-method", 5, 2, &result, "claim", memory, 162 align, size, virt); 163 if (ret != 0 || result == -1) 164 return -1; 165 ret = of_call_prom_ret("call-method", 5, 2, &result, "claim", chosen_mmu, 166 align, size, virt); 167 /* 0x12 == coherent + read/write */ 168 ret = of_call_prom("call-method", 6, 1, "map", chosen_mmu, 169 0x12, size, virt, virt); 170 return virt; 171 } 172 173 void *of_vmlinux_alloc(unsigned long size) 174 { 175 unsigned long start = (unsigned long)_start, end = (unsigned long)_end; 176 unsigned long addr; 177 void *p; 178 179 /* With some older POWER4 firmware we need to claim the area the kernel 180 * will reside in. Newer firmwares don't need this so we just ignore 181 * the return value. 182 */ 183 addr = (unsigned long) of_claim(start, end - start, 0); 184 printf("Trying to claim from 0x%lx to 0x%lx (0x%lx) got %lx\r\n", 185 start, end, end - start, addr); 186 187 p = malloc(size); 188 if (!p) 189 fatal("Can't allocate memory for kernel image!\n\r"); 190 191 return p; 192 } 193 194 void of_exit(void) 195 { 196 of_call_prom("exit", 0, 0); 197 } 198 199 /* 200 * OF device tree routines 201 */ 202 void *of_finddevice(const char *name) 203 { 204 return (phandle) of_call_prom("finddevice", 1, 1, name); 205 } 206 207 int of_getprop(const void *phandle, const char *name, void *buf, 208 const int buflen) 209 { 210 return of_call_prom("getprop", 4, 1, phandle, name, buf, buflen); 211 } 212 213 int of_setprop(const void *phandle, const char *name, const void *buf, 214 const int buflen) 215 { 216 return of_call_prom("setprop", 4, 1, phandle, name, buf, buflen); 217 } 218