diff options
author | Mark A. Greer <mgreer@mvista.com> | 2006-09-19 00:05:08 -0400 |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2006-09-20 01:09:58 -0400 |
commit | b2c5f61920eeee9c4e78698de4fde4586fe5ae79 (patch) | |
tree | 53927c324413786b34db4f0e79cd0aa436d3f930 /arch | |
parent | a4dc7ff08915a2035aa6d6decc53fa1deaa410bb (diff) |
[POWERPC] Start arch/powerpc/boot code reorganization
This abstracts the operations used in the bootwrapper, and defines
the operations needed for the bootwrapper to run on an OF platform.
The operations have been divided up into platform ops (platform_ops),
firmware ops (fw_ops), device tree ops (dt_ops), and console ops
(console_ops).
The proper operations will be hooked up at runtime to provide the
functionality that you need.
Signed-off-by: Mark A. Greer <mgreer@mvista.com>
Signed-off-by: Paul Mackerras <paulus@samba.org>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/powerpc/boot/Makefile | 3 | ||||
-rw-r--r-- | arch/powerpc/boot/flatdevtree.h | 46 | ||||
-rw-r--r-- | arch/powerpc/boot/main.c | 250 | ||||
-rw-r--r-- | arch/powerpc/boot/of.c (renamed from arch/powerpc/boot/prom.c) | 144 | ||||
-rw-r--r-- | arch/powerpc/boot/ops.h | 100 | ||||
-rw-r--r-- | arch/powerpc/boot/prom.h | 41 | ||||
-rw-r--r-- | arch/powerpc/boot/stdio.c | 4 | ||||
-rw-r--r-- | arch/powerpc/boot/stdio.h | 8 | ||||
-rw-r--r-- | arch/powerpc/boot/types.h | 23 |
9 files changed, 441 insertions, 178 deletions
diff --git a/arch/powerpc/boot/Makefile b/arch/powerpc/boot/Makefile index afc776f821e5..e73774136b55 100644 --- a/arch/powerpc/boot/Makefile +++ b/arch/powerpc/boot/Makefile | |||
@@ -36,7 +36,8 @@ zliblinuxheader := zlib.h zconf.h zutil.h | |||
36 | $(addprefix $(obj)/,$(zlib) main.o): $(addprefix $(obj)/,$(zliblinuxheader)) $(addprefix $(obj)/,$(zlibheader)) | 36 | $(addprefix $(obj)/,$(zlib) main.o): $(addprefix $(obj)/,$(zliblinuxheader)) $(addprefix $(obj)/,$(zlibheader)) |
37 | #$(addprefix $(obj)/,main.o): $(addprefix $(obj)/,zlib.h) | 37 | #$(addprefix $(obj)/,main.o): $(addprefix $(obj)/,zlib.h) |
38 | 38 | ||
39 | src-boot := crt0.S string.S prom.c stdio.c main.c div64.S | 39 | src-boot-$(CONFIG_PPC_MULTIPLATFORM) := of.c |
40 | src-boot := crt0.S string.S stdio.c main.c div64.S $(src-boot-y) | ||
40 | src-boot += $(zlib) | 41 | src-boot += $(zlib) |
41 | src-boot := $(addprefix $(obj)/, $(src-boot)) | 42 | src-boot := $(addprefix $(obj)/, $(src-boot)) |
42 | obj-boot := $(addsuffix .o, $(basename $(src-boot))) | 43 | obj-boot := $(addsuffix .o, $(basename $(src-boot))) |
diff --git a/arch/powerpc/boot/flatdevtree.h b/arch/powerpc/boot/flatdevtree.h new file mode 100644 index 000000000000..761c8dc84008 --- /dev/null +++ b/arch/powerpc/boot/flatdevtree.h | |||
@@ -0,0 +1,46 @@ | |||
1 | /* | ||
2 | * This program is free software; you can redistribute it and/or modify | ||
3 | * it under the terms of the GNU General Public License as published by | ||
4 | * the Free Software Foundation; either version 2 of the License, or | ||
5 | * (at your option) any later version. | ||
6 | * | ||
7 | * This program is distributed in the hope that it will be useful, | ||
8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
10 | * GNU General Public License for more details. | ||
11 | * | ||
12 | * You should have received a copy of the GNU General Public License | ||
13 | * along with this program; if not, write to the Free Software | ||
14 | * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
15 | */ | ||
16 | |||
17 | #ifndef FLATDEVTREE_H | ||
18 | #define FLATDEVTREE_H | ||
19 | |||
20 | #include "types.h" | ||
21 | |||
22 | /* Definitions used by the flattened device tree */ | ||
23 | #define OF_DT_HEADER 0xd00dfeed /* marker */ | ||
24 | #define OF_DT_BEGIN_NODE 0x1 /* Start of node, full name */ | ||
25 | #define OF_DT_END_NODE 0x2 /* End node */ | ||
26 | #define OF_DT_PROP 0x3 /* Property: name off, size, content */ | ||
27 | #define OF_DT_NOP 0x4 /* nop */ | ||
28 | #define OF_DT_END 0x9 | ||
29 | |||
30 | #define OF_DT_VERSION 0x10 | ||
31 | |||
32 | struct boot_param_header { | ||
33 | u32 magic; /* magic word OF_DT_HEADER */ | ||
34 | u32 totalsize; /* total size of DT block */ | ||
35 | u32 off_dt_struct; /* offset to structure */ | ||
36 | u32 off_dt_strings; /* offset to strings */ | ||
37 | u32 off_mem_rsvmap; /* offset to memory reserve map */ | ||
38 | u32 version; /* format version */ | ||
39 | u32 last_comp_version; /* last compatible version */ | ||
40 | /* version 2 fields below */ | ||
41 | u32 boot_cpuid_phys; /* Physical CPU id we're booting on */ | ||
42 | /* version 3 fields below */ | ||
43 | u32 dt_strings_size; /* size of the DT strings block */ | ||
44 | }; | ||
45 | |||
46 | #endif /* FLATDEVTREE_H */ | ||
diff --git a/arch/powerpc/boot/main.c b/arch/powerpc/boot/main.c index b66634c9ea34..d719bb9333d1 100644 --- a/arch/powerpc/boot/main.c +++ b/arch/powerpc/boot/main.c | |||
@@ -14,17 +14,12 @@ | |||
14 | #include "page.h" | 14 | #include "page.h" |
15 | #include "string.h" | 15 | #include "string.h" |
16 | #include "stdio.h" | 16 | #include "stdio.h" |
17 | #include "prom.h" | ||
18 | #include "zlib.h" | 17 | #include "zlib.h" |
18 | #include "ops.h" | ||
19 | #include "flatdevtree.h" | ||
19 | 20 | ||
20 | extern void flush_cache(void *, unsigned long); | 21 | extern void flush_cache(void *, unsigned long); |
21 | 22 | ||
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 | extern char _start[]; | 23 | extern char _start[]; |
29 | extern char __bss_start[]; | 24 | extern char __bss_start[]; |
30 | extern char _end[]; | 25 | extern char _end[]; |
@@ -33,14 +28,6 @@ extern char _vmlinux_end[]; | |||
33 | extern char _initrd_start[]; | 28 | extern char _initrd_start[]; |
34 | extern char _initrd_end[]; | 29 | extern char _initrd_end[]; |
35 | 30 | ||
36 | /* A buffer that may be edited by tools operating on a zImage binary so as to | ||
37 | * edit the command line passed to vmlinux (by setting /chosen/bootargs). | ||
38 | * The buffer is put in it's own section so that tools may locate it easier. | ||
39 | */ | ||
40 | static char builtin_cmdline[512] | ||
41 | __attribute__((section("__builtin_cmdline"))); | ||
42 | |||
43 | |||
44 | struct addr_range { | 31 | struct addr_range { |
45 | unsigned long addr; | 32 | unsigned long addr; |
46 | unsigned long size; | 33 | unsigned long size; |
@@ -51,21 +38,16 @@ static struct addr_range vmlinuz; | |||
51 | static struct addr_range initrd; | 38 | static struct addr_range initrd; |
52 | 39 | ||
53 | static unsigned long elfoffset; | 40 | static unsigned long elfoffset; |
41 | static int is_64bit; | ||
54 | 42 | ||
55 | static char scratch[46912]; /* scratch space for gunzip, from zlib_inflate_workspacesize() */ | 43 | /* scratch space for gunzip; 46912 is from zlib_inflate_workspacesize() */ |
44 | static char scratch[46912]; | ||
56 | static char elfheader[256]; | 45 | static char elfheader[256]; |
57 | 46 | ||
58 | 47 | typedef void (*kernel_entry_t)(unsigned long, unsigned long, void *); | |
59 | typedef void (*kernel_entry_t)( unsigned long, | ||
60 | unsigned long, | ||
61 | void *, | ||
62 | void *); | ||
63 | |||
64 | 48 | ||
65 | #undef DEBUG | 49 | #undef DEBUG |
66 | 50 | ||
67 | static unsigned long claim_base; | ||
68 | |||
69 | #define HEAD_CRC 2 | 51 | #define HEAD_CRC 2 |
70 | #define EXTRA_FIELD 4 | 52 | #define EXTRA_FIELD 4 |
71 | #define ORIG_NAME 8 | 53 | #define ORIG_NAME 8 |
@@ -123,24 +105,6 @@ static void gunzip(void *dst, int dstlen, unsigned char *src, int *lenp) | |||
123 | zlib_inflateEnd(&s); | 105 | zlib_inflateEnd(&s); |
124 | } | 106 | } |
125 | 107 | ||
126 | static unsigned long try_claim(unsigned long size) | ||
127 | { | ||
128 | unsigned long addr = 0; | ||
129 | |||
130 | for(; claim_base < RAM_END; claim_base += ONE_MB) { | ||
131 | #ifdef DEBUG | ||
132 | printf(" trying: 0x%08lx\n\r", claim_base); | ||
133 | #endif | ||
134 | addr = (unsigned long)claim(claim_base, size, 0); | ||
135 | if ((void *)addr != (void *)-1) | ||
136 | break; | ||
137 | } | ||
138 | if (addr == 0) | ||
139 | return 0; | ||
140 | claim_base = PAGE_ALIGN(claim_base + size); | ||
141 | return addr; | ||
142 | } | ||
143 | |||
144 | static int is_elf64(void *hdr) | 108 | static int is_elf64(void *hdr) |
145 | { | 109 | { |
146 | Elf64_Ehdr *elf64 = hdr; | 110 | Elf64_Ehdr *elf64 = hdr; |
@@ -169,16 +133,7 @@ static int is_elf64(void *hdr) | |||
169 | vmlinux.size = (unsigned long)elf64ph->p_filesz + elfoffset; | 133 | vmlinux.size = (unsigned long)elf64ph->p_filesz + elfoffset; |
170 | vmlinux.memsize = (unsigned long)elf64ph->p_memsz + elfoffset; | 134 | vmlinux.memsize = (unsigned long)elf64ph->p_memsz + elfoffset; |
171 | 135 | ||
172 | #if defined(PROG_START) | 136 | is_64bit = 1; |
173 | /* | ||
174 | * Maintain a "magic" minimum address. This keeps some older | ||
175 | * firmware platforms running. | ||
176 | */ | ||
177 | |||
178 | if (claim_base < PROG_START) | ||
179 | claim_base = PROG_START; | ||
180 | #endif | ||
181 | |||
182 | return 1; | 137 | return 1; |
183 | } | 138 | } |
184 | 139 | ||
@@ -212,47 +167,9 @@ static int is_elf32(void *hdr) | |||
212 | return 1; | 167 | return 1; |
213 | } | 168 | } |
214 | 169 | ||
215 | void export_cmdline(void* chosen_handle) | 170 | static void prep_kernel(unsigned long *a1, unsigned long *a2) |
216 | { | ||
217 | int len; | ||
218 | char cmdline[2] = { 0, 0 }; | ||
219 | |||
220 | if (builtin_cmdline[0] == 0) | ||
221 | return; | ||
222 | |||
223 | len = getprop(chosen_handle, "bootargs", cmdline, sizeof(cmdline)); | ||
224 | if (len > 0 && cmdline[0] != 0) | ||
225 | return; | ||
226 | |||
227 | setprop(chosen_handle, "bootargs", builtin_cmdline, | ||
228 | strlen(builtin_cmdline) + 1); | ||
229 | } | ||
230 | |||
231 | |||
232 | void start(unsigned long a1, unsigned long a2, void *promptr, void *sp) | ||
233 | { | 171 | { |
234 | int len; | 172 | int len; |
235 | kernel_entry_t kernel_entry; | ||
236 | |||
237 | memset(__bss_start, 0, _end - __bss_start); | ||
238 | |||
239 | prom = (int (*)(void *)) promptr; | ||
240 | chosen_handle = finddevice("/chosen"); | ||
241 | if (chosen_handle == (void *) -1) | ||
242 | exit(); | ||
243 | if (getprop(chosen_handle, "stdout", &stdout, sizeof(stdout)) != 4) | ||
244 | exit(); | ||
245 | |||
246 | printf("\n\rzImage starting: loaded at 0x%p (sp: 0x%p)\n\r", _start, sp); | ||
247 | |||
248 | /* | ||
249 | * The first available claim_base must be above the end of the | ||
250 | * the loaded kernel wrapper file (_start to _end includes the | ||
251 | * initrd image if it is present) and rounded up to a nice | ||
252 | * 1 MB boundary for good measure. | ||
253 | */ | ||
254 | |||
255 | claim_base = _ALIGN_UP((unsigned long)_end, ONE_MB); | ||
256 | 173 | ||
257 | vmlinuz.addr = (unsigned long)_vmlinux_start; | 174 | vmlinuz.addr = (unsigned long)_vmlinux_start; |
258 | vmlinuz.size = (unsigned long)(_vmlinux_end - _vmlinux_start); | 175 | vmlinuz.size = (unsigned long)(_vmlinux_end - _vmlinux_start); |
@@ -263,43 +180,51 @@ void start(unsigned long a1, unsigned long a2, void *promptr, void *sp) | |||
263 | gunzip(elfheader, sizeof(elfheader), | 180 | gunzip(elfheader, sizeof(elfheader), |
264 | (unsigned char *)vmlinuz.addr, &len); | 181 | (unsigned char *)vmlinuz.addr, &len); |
265 | } else | 182 | } else |
266 | memcpy(elfheader, (const void *)vmlinuz.addr, sizeof(elfheader)); | 183 | memcpy(elfheader, (const void *)vmlinuz.addr, |
184 | sizeof(elfheader)); | ||
267 | 185 | ||
268 | if (!is_elf64(elfheader) && !is_elf32(elfheader)) { | 186 | if (!is_elf64(elfheader) && !is_elf32(elfheader)) { |
269 | printf("Error: not a valid PPC32 or PPC64 ELF file!\n\r"); | 187 | printf("Error: not a valid PPC32 or PPC64 ELF file!\n\r"); |
270 | exit(); | 188 | exit(); |
271 | } | 189 | } |
190 | if (platform_ops.image_hdr) | ||
191 | platform_ops.image_hdr(elfheader); | ||
272 | 192 | ||
273 | /* We need to claim the memsize plus the file offset since gzip | 193 | /* We need to alloc the memsize plus the file offset since gzip |
274 | * will expand the header (file offset), then the kernel, then | 194 | * will expand the header (file offset), then the kernel, then |
275 | * possible rubbish we don't care about. But the kernel bss must | 195 | * possible rubbish we don't care about. But the kernel bss must |
276 | * be claimed (it will be zero'd by the kernel itself) | 196 | * be claimed (it will be zero'd by the kernel itself) |
277 | */ | 197 | */ |
278 | printf("Allocating 0x%lx bytes for kernel ...\n\r", vmlinux.memsize); | 198 | printf("Allocating 0x%lx bytes for kernel ...\n\r", vmlinux.memsize); |
279 | vmlinux.addr = try_claim(vmlinux.memsize); | 199 | vmlinux.addr = (unsigned long)malloc(vmlinux.memsize); |
280 | if (vmlinux.addr == 0) { | 200 | if (vmlinux.addr == 0) { |
281 | printf("Can't allocate memory for kernel image !\n\r"); | 201 | printf("Can't allocate memory for kernel image !\n\r"); |
282 | exit(); | 202 | exit(); |
283 | } | 203 | } |
284 | 204 | ||
285 | /* | 205 | /* |
286 | * Now we try to claim memory for the initrd (and copy it there) | 206 | * Now we try to alloc memory for the initrd (and copy it there) |
287 | */ | 207 | */ |
288 | initrd.size = (unsigned long)(_initrd_end - _initrd_start); | 208 | initrd.size = (unsigned long)(_initrd_end - _initrd_start); |
289 | initrd.memsize = initrd.size; | 209 | initrd.memsize = initrd.size; |
290 | if ( initrd.size > 0 ) { | 210 | if ( initrd.size > 0 ) { |
291 | printf("Allocating 0x%lx bytes for initrd ...\n\r", initrd.size); | 211 | printf("Allocating 0x%lx bytes for initrd ...\n\r", |
292 | initrd.addr = try_claim(initrd.size); | 212 | initrd.size); |
213 | initrd.addr = (unsigned long)malloc((u32)initrd.size); | ||
293 | if (initrd.addr == 0) { | 214 | if (initrd.addr == 0) { |
294 | printf("Can't allocate memory for initial ramdisk !\n\r"); | 215 | printf("Can't allocate memory for initial " |
216 | "ramdisk !\n\r"); | ||
295 | exit(); | 217 | exit(); |
296 | } | 218 | } |
297 | a1 = initrd.addr; | 219 | *a1 = initrd.addr; |
298 | a2 = initrd.size; | 220 | *a2 = initrd.size; |
299 | printf("initial ramdisk moving 0x%lx <- 0x%lx (0x%lx bytes)\n\r", | 221 | printf("initial ramdisk moving 0x%lx <- 0x%lx " |
300 | initrd.addr, (unsigned long)_initrd_start, initrd.size); | 222 | "(0x%lx bytes)\n\r", initrd.addr, |
301 | memmove((void *)initrd.addr, (void *)_initrd_start, initrd.size); | 223 | (unsigned long)_initrd_start, initrd.size); |
302 | printf("initrd head: 0x%lx\n\r", *((unsigned long *)initrd.addr)); | 224 | memmove((void *)initrd.addr, (void *)_initrd_start, |
225 | initrd.size); | ||
226 | printf("initrd head: 0x%lx\n\r", | ||
227 | *((unsigned long *)initrd.addr)); | ||
303 | } | 228 | } |
304 | 229 | ||
305 | /* Eventually gunzip the kernel */ | 230 | /* Eventually gunzip the kernel */ |
@@ -311,11 +236,10 @@ void start(unsigned long a1, unsigned long a2, void *promptr, void *sp) | |||
311 | (unsigned char *)vmlinuz.addr, &len); | 236 | (unsigned char *)vmlinuz.addr, &len); |
312 | printf("done 0x%lx bytes\n\r", len); | 237 | printf("done 0x%lx bytes\n\r", len); |
313 | } else { | 238 | } else { |
314 | memmove((void *)vmlinux.addr,(void *)vmlinuz.addr,vmlinuz.size); | 239 | memmove((void *)vmlinux.addr,(void *)vmlinuz.addr, |
240 | vmlinuz.size); | ||
315 | } | 241 | } |
316 | 242 | ||
317 | export_cmdline(chosen_handle); | ||
318 | |||
319 | /* Skip over the ELF header */ | 243 | /* Skip over the ELF header */ |
320 | #ifdef DEBUG | 244 | #ifdef DEBUG |
321 | printf("... skipping 0x%lx bytes of ELF header\n\r", | 245 | printf("... skipping 0x%lx bytes of ELF header\n\r", |
@@ -324,23 +248,107 @@ void start(unsigned long a1, unsigned long a2, void *promptr, void *sp) | |||
324 | vmlinux.addr += elfoffset; | 248 | vmlinux.addr += elfoffset; |
325 | 249 | ||
326 | flush_cache((void *)vmlinux.addr, vmlinux.size); | 250 | flush_cache((void *)vmlinux.addr, vmlinux.size); |
251 | } | ||
327 | 252 | ||
328 | kernel_entry = (kernel_entry_t)vmlinux.addr; | 253 | void __attribute__ ((weak)) ft_init(void *dt_blob) |
329 | #ifdef DEBUG | 254 | { |
330 | printf( "kernel:\n\r" | 255 | } |
331 | " entry addr = 0x%lx\n\r" | ||
332 | " a1 = 0x%lx,\n\r" | ||
333 | " a2 = 0x%lx,\n\r" | ||
334 | " prom = 0x%lx,\n\r" | ||
335 | " bi_recs = 0x%lx,\n\r", | ||
336 | (unsigned long)kernel_entry, a1, a2, | ||
337 | (unsigned long)prom, NULL); | ||
338 | #endif | ||
339 | 256 | ||
340 | kernel_entry(a1, a2, prom, NULL); | 257 | /* A buffer that may be edited by tools operating on a zImage binary so as to |
258 | * edit the command line passed to vmlinux (by setting /chosen/bootargs). | ||
259 | * The buffer is put in it's own section so that tools may locate it easier. | ||
260 | */ | ||
261 | static char builtin_cmdline[COMMAND_LINE_SIZE] | ||
262 | __attribute__((__section__("__builtin_cmdline"))); | ||
341 | 263 | ||
342 | printf("Error: Linux kernel returned to zImage bootloader!\n\r"); | 264 | static void get_cmdline(char *buf, int size) |
265 | { | ||
266 | void *devp; | ||
267 | int len = strlen(builtin_cmdline); | ||
343 | 268 | ||
344 | exit(); | 269 | buf[0] = '\0'; |
270 | |||
271 | if (len > 0) { /* builtin_cmdline overrides dt's /chosen/bootargs */ | ||
272 | len = min(len, size-1); | ||
273 | strncpy(buf, builtin_cmdline, len); | ||
274 | buf[len] = '\0'; | ||
275 | } | ||
276 | else if ((devp = finddevice("/chosen"))) | ||
277 | getprop(devp, "bootargs", buf, size); | ||
278 | } | ||
279 | |||
280 | static void set_cmdline(char *buf) | ||
281 | { | ||
282 | void *devp; | ||
283 | |||
284 | if ((devp = finddevice("/chosen"))) | ||
285 | setprop(devp, "bootargs", buf, strlen(buf) + 1); | ||
345 | } | 286 | } |
346 | 287 | ||
288 | /* Section where ft can be tacked on after zImage is built */ | ||
289 | union blobspace { | ||
290 | struct boot_param_header hdr; | ||
291 | char space[8*1024]; | ||
292 | } dt_blob __attribute__((__section__("__builtin_ft"))); | ||
293 | |||
294 | struct platform_ops platform_ops; | ||
295 | struct dt_ops dt_ops; | ||
296 | struct console_ops console_ops; | ||
297 | |||
298 | void start(unsigned long a1, unsigned long a2, void *promptr, void *sp) | ||
299 | { | ||
300 | int have_dt = 0; | ||
301 | kernel_entry_t kentry; | ||
302 | char cmdline[COMMAND_LINE_SIZE]; | ||
303 | |||
304 | memset(__bss_start, 0, _end - __bss_start); | ||
305 | memset(&platform_ops, 0, sizeof(platform_ops)); | ||
306 | memset(&dt_ops, 0, sizeof(dt_ops)); | ||
307 | memset(&console_ops, 0, sizeof(console_ops)); | ||
308 | |||
309 | /* Override the dt_ops and device tree if there was an flat dev | ||
310 | * tree attached to the zImage. | ||
311 | */ | ||
312 | if (dt_blob.hdr.magic == OF_DT_HEADER) { | ||
313 | have_dt = 1; | ||
314 | ft_init(&dt_blob); | ||
315 | } | ||
316 | |||
317 | if (platform_init(promptr)) | ||
318 | exit(); | ||
319 | if (console_ops.open && (console_ops.open() < 0)) | ||
320 | exit(); | ||
321 | if (platform_ops.fixups) | ||
322 | platform_ops.fixups(); | ||
323 | |||
324 | printf("\n\rzImage starting: loaded at 0x%p (sp: 0x%p)\n\r", | ||
325 | _start, sp); | ||
326 | |||
327 | prep_kernel(&a1, &a2); | ||
328 | |||
329 | /* If cmdline came from zimage wrapper or if we can edit the one | ||
330 | * in the dt, print it out and edit it, if possible. | ||
331 | */ | ||
332 | if ((strlen(builtin_cmdline) > 0) || console_ops.edit_cmdline) { | ||
333 | get_cmdline(cmdline, COMMAND_LINE_SIZE); | ||
334 | printf("\n\rLinux/PowerPC load: %s", cmdline); | ||
335 | if (console_ops.edit_cmdline) | ||
336 | console_ops.edit_cmdline(cmdline, COMMAND_LINE_SIZE); | ||
337 | printf("\n\r"); | ||
338 | set_cmdline(cmdline); | ||
339 | } | ||
340 | |||
341 | if (console_ops.close) | ||
342 | console_ops.close(); | ||
343 | |||
344 | kentry = (kernel_entry_t) vmlinux.addr; | ||
345 | if (have_dt) | ||
346 | kentry(dt_ops.ft_addr(), 0, NULL); | ||
347 | else | ||
348 | /* XXX initrd addr/size should be passed in properties */ | ||
349 | kentry(a1, a2, promptr); | ||
350 | |||
351 | /* console closed so printf below may not work */ | ||
352 | printf("Error: Linux kernel returned to zImage boot wrapper!\n\r"); | ||
353 | exit(); | ||
354 | } | ||
diff --git a/arch/powerpc/boot/prom.c b/arch/powerpc/boot/of.c index fa0057736f6b..fd99f789a37b 100644 --- a/arch/powerpc/boot/prom.c +++ b/arch/powerpc/boot/of.c | |||
@@ -8,15 +8,29 @@ | |||
8 | */ | 8 | */ |
9 | #include <stdarg.h> | 9 | #include <stdarg.h> |
10 | #include <stddef.h> | 10 | #include <stddef.h> |
11 | #include "types.h" | ||
12 | #include "elf.h" | ||
11 | #include "string.h" | 13 | #include "string.h" |
12 | #include "stdio.h" | 14 | #include "stdio.h" |
13 | #include "prom.h" | 15 | #include "page.h" |
16 | #include "ops.h" | ||
14 | 17 | ||
15 | int (*prom)(void *); | 18 | typedef void *ihandle; |
16 | phandle chosen_handle; | 19 | typedef void *phandle; |
17 | ihandle stdout; | ||
18 | 20 | ||
19 | int call_prom(const char *service, int nargs, int nret, ...) | 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, ...) | ||
20 | { | 34 | { |
21 | int i; | 35 | int i; |
22 | struct prom_args { | 36 | struct prom_args { |
@@ -45,7 +59,7 @@ int call_prom(const char *service, int nargs, int nret, ...) | |||
45 | return (nret > 0)? args.args[nargs]: 0; | 59 | return (nret > 0)? args.args[nargs]: 0; |
46 | } | 60 | } |
47 | 61 | ||
48 | int call_prom_ret(const char *service, int nargs, int nret, | 62 | static int call_prom_ret(const char *service, int nargs, int nret, |
49 | unsigned int *rets, ...) | 63 | unsigned int *rets, ...) |
50 | { | 64 | { |
51 | int i; | 65 | int i; |
@@ -79,11 +93,6 @@ int call_prom_ret(const char *service, int nargs, int nret, | |||
79 | return (nret > 0)? args.args[nargs]: 0; | 93 | return (nret > 0)? args.args[nargs]: 0; |
80 | } | 94 | } |
81 | 95 | ||
82 | int write(void *handle, void *ptr, int nb) | ||
83 | { | ||
84 | return call_prom("write", 3, 1, handle, ptr, nb); | ||
85 | } | ||
86 | |||
87 | /* | 96 | /* |
88 | * Older OF's require that when claiming a specific range of addresses, | 97 | * Older OF's require that when claiming a specific range of addresses, |
89 | * we claim the physical space in the /memory node and the virtual | 98 | * we claim the physical space in the /memory node and the virtual |
@@ -142,7 +151,7 @@ static int check_of_version(void) | |||
142 | return 1; | 151 | return 1; |
143 | } | 152 | } |
144 | 153 | ||
145 | void *claim(unsigned long virt, unsigned long size, unsigned long align) | 154 | static void *claim(unsigned long virt, unsigned long size, unsigned long align) |
146 | { | 155 | { |
147 | int ret; | 156 | int ret; |
148 | unsigned int result; | 157 | unsigned int result; |
@@ -151,7 +160,7 @@ void *claim(unsigned long virt, unsigned long size, unsigned long align) | |||
151 | need_map = check_of_version(); | 160 | need_map = check_of_version(); |
152 | if (align || !need_map) | 161 | if (align || !need_map) |
153 | return (void *) call_prom("claim", 3, 1, virt, size, align); | 162 | return (void *) call_prom("claim", 3, 1, virt, size, align); |
154 | 163 | ||
155 | ret = call_prom_ret("call-method", 5, 2, &result, "claim", memory, | 164 | ret = call_prom_ret("call-method", 5, 2, &result, "claim", memory, |
156 | align, size, virt); | 165 | align, size, virt); |
157 | if (ret != 0 || result == -1) | 166 | if (ret != 0 || result == -1) |
@@ -163,3 +172,112 @@ void *claim(unsigned long virt, unsigned long size, unsigned long align) | |||
163 | 0x12, size, virt, virt); | 172 | 0x12, size, virt, virt); |
164 | return (void *) virt; | 173 | return (void *) virt; |
165 | } | 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 | } | ||
diff --git a/arch/powerpc/boot/ops.h b/arch/powerpc/boot/ops.h new file mode 100644 index 000000000000..135eb4bb03b4 --- /dev/null +++ b/arch/powerpc/boot/ops.h | |||
@@ -0,0 +1,100 @@ | |||
1 | /* | ||
2 | * Global definition of all the bootwrapper operations. | ||
3 | * | ||
4 | * Author: Mark A. Greer <mgreer@mvista.com> | ||
5 | * | ||
6 | * 2006 (c) MontaVista Software, Inc. This file is licensed under | ||
7 | * the terms of the GNU General Public License version 2. This program | ||
8 | * is licensed "as is" without any warranty of any kind, whether express | ||
9 | * or implied. | ||
10 | */ | ||
11 | #ifndef _PPC_BOOT_OPS_H_ | ||
12 | #define _PPC_BOOT_OPS_H_ | ||
13 | |||
14 | #include "types.h" | ||
15 | |||
16 | #define COMMAND_LINE_SIZE 512 | ||
17 | #define MAX_PATH_LEN 256 | ||
18 | #define MAX_PROP_LEN 256 /* What should this be? */ | ||
19 | |||
20 | /* Platform specific operations */ | ||
21 | struct platform_ops { | ||
22 | void (*fixups)(void); | ||
23 | void (*image_hdr)(const void *); | ||
24 | void * (*malloc)(u32 size); | ||
25 | void (*free)(void *ptr, u32 size); | ||
26 | void (*exit)(void); | ||
27 | }; | ||
28 | extern struct platform_ops platform_ops; | ||
29 | |||
30 | /* Device Tree operations */ | ||
31 | struct dt_ops { | ||
32 | void * (*finddevice)(const char *name); | ||
33 | int (*getprop)(const void *node, const char *name, void *buf, | ||
34 | const int buflen); | ||
35 | int (*setprop)(const void *node, const char *name, | ||
36 | const void *buf, const int buflen); | ||
37 | u64 (*translate_addr)(const char *path, const u32 *in_addr, | ||
38 | const u32 addr_len); | ||
39 | unsigned long (*ft_addr)(void); | ||
40 | }; | ||
41 | extern struct dt_ops dt_ops; | ||
42 | |||
43 | /* Console operations */ | ||
44 | struct console_ops { | ||
45 | int (*open)(void); | ||
46 | void (*write)(char *buf, int len); | ||
47 | void (*edit_cmdline)(char *buf, int len); | ||
48 | void (*close)(void); | ||
49 | void *data; | ||
50 | }; | ||
51 | extern struct console_ops console_ops; | ||
52 | |||
53 | /* Serial console operations */ | ||
54 | struct serial_console_data { | ||
55 | int (*open)(void); | ||
56 | void (*putc)(unsigned char c); | ||
57 | unsigned char (*getc)(void); | ||
58 | u8 (*tstc)(void); | ||
59 | void (*close)(void); | ||
60 | }; | ||
61 | |||
62 | extern int platform_init(void *promptr); | ||
63 | extern void simple_alloc_init(void); | ||
64 | extern void ft_init(void *dt_blob); | ||
65 | extern int serial_console_init(void); | ||
66 | |||
67 | static inline void *finddevice(const char *name) | ||
68 | { | ||
69 | return (dt_ops.finddevice) ? dt_ops.finddevice(name) : NULL; | ||
70 | } | ||
71 | |||
72 | static inline int getprop(void *devp, const char *name, void *buf, int buflen) | ||
73 | { | ||
74 | return (dt_ops.getprop) ? dt_ops.getprop(devp, name, buf, buflen) : -1; | ||
75 | } | ||
76 | |||
77 | static inline int setprop(void *devp, const char *name, void *buf, int buflen) | ||
78 | { | ||
79 | return (dt_ops.setprop) ? dt_ops.setprop(devp, name, buf, buflen) : -1; | ||
80 | } | ||
81 | |||
82 | static inline void *malloc(u32 size) | ||
83 | { | ||
84 | return (platform_ops.malloc) ? platform_ops.malloc(size) : NULL; | ||
85 | } | ||
86 | |||
87 | static inline void free(void *ptr, u32 size) | ||
88 | { | ||
89 | if (platform_ops.free) | ||
90 | platform_ops.free(ptr, size); | ||
91 | } | ||
92 | |||
93 | static inline void exit(void) | ||
94 | { | ||
95 | if (platform_ops.exit) | ||
96 | platform_ops.exit(); | ||
97 | for(;;); | ||
98 | } | ||
99 | |||
100 | #endif /* _PPC_BOOT_OPS_H_ */ | ||
diff --git a/arch/powerpc/boot/prom.h b/arch/powerpc/boot/prom.h deleted file mode 100644 index a57b184c564f..000000000000 --- a/arch/powerpc/boot/prom.h +++ /dev/null | |||
@@ -1,41 +0,0 @@ | |||
1 | #ifndef _PPC_BOOT_PROM_H_ | ||
2 | #define _PPC_BOOT_PROM_H_ | ||
3 | |||
4 | typedef void *phandle; | ||
5 | typedef void *ihandle; | ||
6 | |||
7 | extern int (*prom) (void *); | ||
8 | extern phandle chosen_handle; | ||
9 | extern ihandle stdout; | ||
10 | |||
11 | int call_prom(const char *service, int nargs, int nret, ...); | ||
12 | int call_prom_ret(const char *service, int nargs, int nret, | ||
13 | unsigned int *rets, ...); | ||
14 | |||
15 | extern int write(void *handle, void *ptr, int nb); | ||
16 | extern void *claim(unsigned long virt, unsigned long size, unsigned long aln); | ||
17 | |||
18 | static inline void exit(void) | ||
19 | { | ||
20 | call_prom("exit", 0, 0); | ||
21 | } | ||
22 | |||
23 | static inline phandle finddevice(const char *name) | ||
24 | { | ||
25 | return (phandle) call_prom("finddevice", 1, 1, name); | ||
26 | } | ||
27 | |||
28 | static inline int getprop(void *phandle, const char *name, | ||
29 | void *buf, int buflen) | ||
30 | { | ||
31 | return call_prom("getprop", 4, 1, phandle, name, buf, buflen); | ||
32 | } | ||
33 | |||
34 | |||
35 | static inline int setprop(void *phandle, const char *name, | ||
36 | void *buf, int buflen) | ||
37 | { | ||
38 | return call_prom("setprop", 4, 1, phandle, name, buf, buflen); | ||
39 | } | ||
40 | |||
41 | #endif /* _PPC_BOOT_PROM_H_ */ | ||
diff --git a/arch/powerpc/boot/stdio.c b/arch/powerpc/boot/stdio.c index b5aa522f8b77..6d5f6382e1ce 100644 --- a/arch/powerpc/boot/stdio.c +++ b/arch/powerpc/boot/stdio.c | |||
@@ -10,7 +10,7 @@ | |||
10 | #include <stddef.h> | 10 | #include <stddef.h> |
11 | #include "string.h" | 11 | #include "string.h" |
12 | #include "stdio.h" | 12 | #include "stdio.h" |
13 | #include "prom.h" | 13 | #include "ops.h" |
14 | 14 | ||
15 | size_t strnlen(const char * s, size_t count) | 15 | size_t strnlen(const char * s, size_t count) |
16 | { | 16 | { |
@@ -320,6 +320,6 @@ printf(const char *fmt, ...) | |||
320 | va_start(args, fmt); | 320 | va_start(args, fmt); |
321 | n = vsprintf(sprint_buf, fmt, args); | 321 | n = vsprintf(sprint_buf, fmt, args); |
322 | va_end(args); | 322 | va_end(args); |
323 | write(stdout, sprint_buf, n); | 323 | console_ops.write(sprint_buf, n); |
324 | return n; | 324 | return n; |
325 | } | 325 | } |
diff --git a/arch/powerpc/boot/stdio.h b/arch/powerpc/boot/stdio.h index eb9e16c87aef..73b8a91bfb34 100644 --- a/arch/powerpc/boot/stdio.h +++ b/arch/powerpc/boot/stdio.h | |||
@@ -1,8 +1,16 @@ | |||
1 | #ifndef _PPC_BOOT_STDIO_H_ | 1 | #ifndef _PPC_BOOT_STDIO_H_ |
2 | #define _PPC_BOOT_STDIO_H_ | 2 | #define _PPC_BOOT_STDIO_H_ |
3 | 3 | ||
4 | #include <stdarg.h> | ||
5 | |||
6 | #define ENOMEM 12 /* Out of Memory */ | ||
7 | #define EINVAL 22 /* Invalid argument */ | ||
8 | #define ENOSPC 28 /* No space left on device */ | ||
9 | |||
4 | extern int printf(const char *fmt, ...); | 10 | extern int printf(const char *fmt, ...); |
5 | 11 | ||
12 | #define fprintf(fmt, args...) printf(args) | ||
13 | |||
6 | extern int sprintf(char *buf, const char *fmt, ...); | 14 | extern int sprintf(char *buf, const char *fmt, ...); |
7 | 15 | ||
8 | extern int vsprintf(char *buf, const char *fmt, va_list args); | 16 | extern int vsprintf(char *buf, const char *fmt, va_list args); |
diff --git a/arch/powerpc/boot/types.h b/arch/powerpc/boot/types.h new file mode 100644 index 000000000000..79d26e708677 --- /dev/null +++ b/arch/powerpc/boot/types.h | |||
@@ -0,0 +1,23 @@ | |||
1 | #ifndef _TYPES_H_ | ||
2 | #define _TYPES_H_ | ||
3 | |||
4 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) | ||
5 | |||
6 | typedef unsigned char u8; | ||
7 | typedef unsigned short u16; | ||
8 | typedef unsigned int u32; | ||
9 | typedef unsigned long long u64; | ||
10 | |||
11 | #define min(x,y) ({ \ | ||
12 | typeof(x) _x = (x); \ | ||
13 | typeof(y) _y = (y); \ | ||
14 | (void) (&_x == &_y); \ | ||
15 | _x < _y ? _x : _y; }) | ||
16 | |||
17 | #define max(x,y) ({ \ | ||
18 | typeof(x) _x = (x); \ | ||
19 | typeof(y) _y = (y); \ | ||
20 | (void) (&_x == &_y); \ | ||
21 | _x > _y ? _x : _y; }) | ||
22 | |||
23 | #endif /* _TYPES_H_ */ | ||