diff options
Diffstat (limited to 'arch/powerpc/boot/of.c')
-rw-r--r-- | arch/powerpc/boot/of.c | 283 |
1 files changed, 283 insertions, 0 deletions
diff --git a/arch/powerpc/boot/of.c b/arch/powerpc/boot/of.c new file mode 100644 index 000000000000..fd99f789a37b --- /dev/null +++ b/arch/powerpc/boot/of.c | |||
@@ -0,0 +1,283 @@ | |||
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(u32 size) | ||
177 | { | ||
178 | unsigned long addr = 0; | ||
179 | static u8 first_time = 1; | ||
180 | |||
181 | if (first_time) { | ||
182 | claim_base = _ALIGN_UP((unsigned long)_end, ONE_MB); | ||
183 | first_time = 0; | ||
184 | } | ||
185 | |||
186 | for(; claim_base < RAM_END; claim_base += ONE_MB) { | ||
187 | #ifdef DEBUG | ||
188 | printf(" trying: 0x%08lx\n\r", claim_base); | ||
189 | #endif | ||
190 | addr = (unsigned long)claim(claim_base, size, 0); | ||
191 | if ((void *)addr != (void *)-1) | ||
192 | break; | ||
193 | } | ||
194 | if (addr == 0) | ||
195 | return NULL; | ||
196 | claim_base = PAGE_ALIGN(claim_base + size); | ||
197 | return (void *)addr; | ||
198 | } | ||
199 | |||
200 | static void of_image_hdr(const void *hdr) | ||
201 | { | ||
202 | const Elf64_Ehdr *elf64 = hdr; | ||
203 | |||
204 | if (elf64->e_ident[EI_CLASS] == ELFCLASS64) { | ||
205 | /* | ||
206 | * Maintain a "magic" minimum address. This keeps some older | ||
207 | * firmware platforms running. | ||
208 | */ | ||
209 | if (claim_base < PROG_START) | ||
210 | claim_base = PROG_START; | ||
211 | } | ||
212 | } | ||
213 | |||
214 | static void of_exit(void) | ||
215 | { | ||
216 | call_prom("exit", 0, 0); | ||
217 | } | ||
218 | |||
219 | /* | ||
220 | * OF device tree routines | ||
221 | */ | ||
222 | static void *of_finddevice(const char *name) | ||
223 | { | ||
224 | return (phandle) call_prom("finddevice", 1, 1, name); | ||
225 | } | ||
226 | |||
227 | static int of_getprop(const void *phandle, const char *name, void *buf, | ||
228 | const int buflen) | ||
229 | { | ||
230 | return call_prom("getprop", 4, 1, phandle, name, buf, buflen); | ||
231 | } | ||
232 | |||
233 | static int of_setprop(const void *phandle, const char *name, const void *buf, | ||
234 | const int buflen) | ||
235 | { | ||
236 | return call_prom("setprop", 4, 1, phandle, name, buf, buflen); | ||
237 | } | ||
238 | |||
239 | /* | ||
240 | * OF console routines | ||
241 | */ | ||
242 | static void *of_stdout_handle; | ||
243 | |||
244 | static int of_console_open(void) | ||
245 | { | ||
246 | void *devp; | ||
247 | |||
248 | if (((devp = finddevice("/chosen")) != NULL) | ||
249 | && (getprop(devp, "stdout", &of_stdout_handle, | ||
250 | sizeof(of_stdout_handle)) | ||
251 | == sizeof(of_stdout_handle))) | ||
252 | return 0; | ||
253 | |||
254 | return -1; | ||
255 | } | ||
256 | |||
257 | static void of_console_write(char *buf, int len) | ||
258 | { | ||
259 | call_prom("write", 3, 1, of_stdout_handle, buf, len); | ||
260 | } | ||
261 | |||
262 | int platform_init(void *promptr) | ||
263 | { | ||
264 | platform_ops.fixups = NULL; | ||
265 | platform_ops.image_hdr = of_image_hdr; | ||
266 | platform_ops.malloc = of_try_claim; | ||
267 | platform_ops.free = NULL; | ||
268 | platform_ops.exit = of_exit; | ||
269 | |||
270 | dt_ops.finddevice = of_finddevice; | ||
271 | dt_ops.getprop = of_getprop; | ||
272 | dt_ops.setprop = of_setprop; | ||
273 | dt_ops.translate_addr = NULL; | ||
274 | |||
275 | console_ops.open = of_console_open; | ||
276 | console_ops.write = of_console_write; | ||
277 | console_ops.edit_cmdline = NULL; | ||
278 | console_ops.close = NULL; | ||
279 | console_ops.data = NULL; | ||
280 | |||
281 | prom = (int (*)(void *))promptr; | ||
282 | return 0; | ||
283 | } | ||