]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - arch/arm/lib/bootm.c
08c11b7c0d058158fe87cafea510a125efc31f4f
[karo-tx-uboot.git] / arch / arm / lib / bootm.c
1 /* Copyright (C) 2011
2  * Corscience GmbH & Co. KG - Simon Schwarz <schwarz@corscience.de>
3  *  - Added prep subcommand support
4  *  - Reorganized source - modeled after powerpc version
5  *
6  * (C) Copyright 2002
7  * Sysgo Real-Time Solutions, GmbH <www.elinos.com>
8  * Marius Groeger <mgroeger@sysgo.de>
9  *
10  * Copyright (C) 2001  Erik Mouw (J.A.K.Mouw@its.tudelft.nl)
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
25  *
26  */
27
28 #include <common.h>
29 #include <command.h>
30 #include <image.h>
31 #include <u-boot/zlib.h>
32 #include <asm/byteorder.h>
33 #include <libfdt.h>
34 #include <fdt_support.h>
35 #include <asm/bootm.h>
36 #include <linux/compiler.h>
37
38 DECLARE_GLOBAL_DATA_PTR;
39
40 static struct tag *params;
41
42 static ulong get_sp(void)
43 {
44         ulong ret;
45
46         asm("mov %0, sp" : "=r"(ret) : );
47         return ret;
48 }
49
50 void arch_lmb_reserve(struct lmb *lmb)
51 {
52         ulong sp;
53
54         /*
55          * Booting a (Linux) kernel image
56          *
57          * Allocate space for command line and board info - the
58          * address should be as high as possible within the reach of
59          * the kernel (see CONFIG_SYS_BOOTMAPSZ settings), but in unused
60          * memory, which means far enough below the current stack
61          * pointer.
62          */
63         sp = get_sp();
64         debug("## Current stack ends at 0x%08lx ", sp);
65
66         /* adjust sp by 4K to be safe */
67         sp -= 4096;
68         lmb_reserve(lmb, sp,
69                     gd->bd->bi_dram[0].start + gd->bd->bi_dram[0].size - sp);
70 }
71
72 #ifdef CONFIG_OF_LIBFDT
73 static int fixup_memory_node(void *blob)
74 {
75         bd_t    *bd = gd->bd;
76         int bank;
77         u64 start[CONFIG_NR_DRAM_BANKS];
78         u64 size[CONFIG_NR_DRAM_BANKS];
79
80         for (bank = 0; bank < CONFIG_NR_DRAM_BANKS; bank++) {
81                 start[bank] = bd->bi_dram[bank].start;
82                 size[bank] = bd->bi_dram[bank].size;
83         }
84
85         return fdt_fixup_memory_banks(blob, start, size, CONFIG_NR_DRAM_BANKS);
86 }
87 #endif
88
89 static void announce_and_cleanup(void)
90 {
91         printf("\nStarting kernel ...\n\n");
92         bootstage_mark_name(BOOTSTAGE_ID_BOOTM_HANDOFF, "start_kernel");
93 #ifdef CONFIG_BOOTSTAGE_FDT
94         bootstage_fdt_add_report();
95 #endif
96 #ifdef CONFIG_BOOTSTAGE_REPORT
97         bootstage_report();
98 #endif
99
100 #ifdef CONFIG_USB_DEVICE
101         udc_disconnect();
102 #endif
103         cleanup_before_linux();
104 }
105
106 static void setup_start_tag (bd_t *bd)
107 {
108         params = (struct tag *)bd->bi_boot_params;
109
110         params->hdr.tag = ATAG_CORE;
111         params->hdr.size = tag_size (tag_core);
112
113         params->u.core.flags = 0;
114         params->u.core.pagesize = 0;
115         params->u.core.rootdev = 0;
116
117         params = tag_next (params);
118 }
119
120 static void setup_memory_tags(bd_t *bd)
121 {
122         int i;
123
124         for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
125                 params->hdr.tag = ATAG_MEM;
126                 params->hdr.size = tag_size (tag_mem32);
127
128                 params->u.mem.start = bd->bi_dram[i].start;
129                 params->u.mem.size = bd->bi_dram[i].size;
130
131                 params = tag_next (params);
132         }
133 }
134
135 static void setup_commandline_tag(bd_t *bd, char *commandline)
136 {
137         char *p;
138
139         if (!commandline)
140                 return;
141
142         /* eat leading white space */
143         for (p = commandline; *p == ' '; p++);
144
145         /* skip non-existent command lines so the kernel will still
146          * use its default command line.
147          */
148         if (*p == '\0')
149                 return;
150
151         params->hdr.tag = ATAG_CMDLINE;
152         params->hdr.size =
153                 (sizeof (struct tag_header) + strlen (p) + 1 + 4) >> 2;
154
155         strcpy (params->u.cmdline.cmdline, p);
156
157         params = tag_next (params);
158 }
159
160 static void setup_initrd_tag(bd_t *bd, ulong initrd_start, ulong initrd_end)
161 {
162         /* an ATAG_INITRD node tells the kernel where the compressed
163          * ramdisk can be found. ATAG_RDIMG is a better name, actually.
164          */
165         params->hdr.tag = ATAG_INITRD2;
166         params->hdr.size = tag_size (tag_initrd);
167
168         params->u.initrd.start = initrd_start;
169         params->u.initrd.size = initrd_end - initrd_start;
170
171         params = tag_next (params);
172 }
173
174 static void setup_serial_tag(struct tag **tmp)
175 {
176         struct tag *params = *tmp;
177         struct tag_serialnr serialnr;
178
179         get_board_serial(&serialnr);
180         params->hdr.tag = ATAG_SERIAL;
181         params->hdr.size = tag_size (tag_serialnr);
182         params->u.serialnr.low = serialnr.low;
183         params->u.serialnr.high= serialnr.high;
184         params = tag_next (params);
185         *tmp = params;
186 }
187
188 static void setup_revision_tag(struct tag **in_params)
189 {
190         u32 rev = 0;
191
192         rev = get_board_rev();
193         params->hdr.tag = ATAG_REVISION;
194         params->hdr.size = tag_size (tag_revision);
195         params->u.revision.rev = rev;
196         params = tag_next (params);
197 }
198
199 static void setup_end_tag(bd_t *bd)
200 {
201         params->hdr.tag = ATAG_NONE;
202         params->hdr.size = 0;
203 }
204
205 #ifdef CONFIG_OF_LIBFDT
206 static int create_fdt(bootm_headers_t *images)
207 {
208         ulong of_size = images->ft_len;
209         char **of_flat_tree = &images->ft_addr;
210         ulong *initrd_start = &images->initrd_start;
211         ulong *initrd_end = &images->initrd_end;
212         struct lmb *lmb = &images->lmb;
213         ulong rd_len;
214         int ret;
215
216         debug("using: FDT\n");
217
218         boot_fdt_add_mem_rsv_regions(lmb, *of_flat_tree);
219
220         rd_len = images->rd_end - images->rd_start;
221         ret = boot_ramdisk_high(lmb, images->rd_start, rd_len,
222                         initrd_start, initrd_end);
223         if (ret)
224                 return ret;
225
226         ret = boot_relocate_fdt(lmb, of_flat_tree, &of_size);
227         if (ret)
228                 return ret;
229
230         fdt_chosen(*of_flat_tree, 1);
231         fixup_memory_node(*of_flat_tree);
232         fdt_fixup_ethernet(*of_flat_tree);
233         fdt_initrd(*of_flat_tree, *initrd_start, *initrd_end, 1);
234 #ifdef CONFIG_OF_BOARD_SETUP
235         ft_board_setup(*of_flat_tree, gd->bd);
236 #endif
237
238         return 0;
239 }
240 #endif
241
242 __weak void setup_board_tags(struct tag **in_params) {}
243
244 /* Subcommand: PREP */
245 static void boot_prep_linux(bootm_headers_t *images)
246 {
247         char *commandline = getenv("bootargs");
248
249         if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len) {
250 #ifdef CONFIG_OF_LIBFDT
251                 debug("using: FDT\n");
252                 if (create_fdt(images)) {
253                         printf("FDT creation failed! hanging...");
254                         hang();
255                 }
256 #endif
257         } else if (BOOTM_ENABLE_TAGS) {
258                 debug("using: ATAGS\n");
259                 setup_start_tag(gd->bd);
260                 if (BOOTM_ENABLE_SERIAL_TAG)
261                         setup_serial_tag(&params);
262                 if (BOOTM_ENABLE_CMDLINE_TAG)
263                         setup_commandline_tag(gd->bd, commandline);
264                 if (BOOTM_ENABLE_REVISION_TAG)
265                         setup_revision_tag(&params);
266                 if (BOOTM_ENABLE_MEMORY_TAGS)
267                         setup_memory_tags(gd->bd);
268                 if (BOOTM_ENABLE_INITRD_TAG) {
269                         if (images->rd_start && images->rd_end) {
270                                 setup_initrd_tag(gd->bd, images->rd_start,
271                                                  images->rd_end);
272                         }
273                 }
274                 setup_board_tags(&params);
275                 setup_end_tag(gd->bd);
276         } else {
277                 printf("FDT and ATAGS support not compiled in - hanging\n");
278                 hang();
279         }
280 }
281
282 /* Subcommand: GO */
283 static void boot_jump_linux(bootm_headers_t *images)
284 {
285         unsigned long machid = gd->bd->bi_arch_number;
286         char *s;
287         void (*kernel_entry)(int zero, int arch, uint params);
288         unsigned long r2;
289
290         kernel_entry = (void (*)(int, int, uint))images->ep;
291
292         s = getenv("machid");
293         if (s) {
294                 strict_strtoul(s, 16, &machid);
295                 printf("Using machid 0x%lx from environment\n", machid);
296         }
297
298         debug("## Transferring control to Linux (at address %08lx)" \
299                 "...\n", (ulong) kernel_entry);
300         bootstage_mark(BOOTSTAGE_ID_RUN_OS);
301         announce_and_cleanup();
302
303         if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len)
304                 r2 = (unsigned long)images->ft_addr;
305         else
306                 r2 = gd->bd->bi_boot_params;
307
308         kernel_entry(0, machid, r2);
309 }
310
311 /* Main Entry point for arm bootm implementation
312  *
313  * Modeled after the powerpc implementation
314  * DIFFERENCE: Instead of calling prep and go at the end
315  * they are called if subcommand is equal 0.
316  */
317 int do_bootm_linux(int flag, int argc, char *argv[], bootm_headers_t *images)
318 {
319         /* No need for those on ARM */
320         if (flag & BOOTM_STATE_OS_BD_T || flag & BOOTM_STATE_OS_CMDLINE)
321                 return -1;
322
323         if (flag & BOOTM_STATE_OS_PREP) {
324                 boot_prep_linux(images);
325                 return 0;
326         }
327
328         if (flag & BOOTM_STATE_OS_GO) {
329                 boot_jump_linux(images);
330                 return 0;
331         }
332
333         boot_prep_linux(images);
334         boot_jump_linux(images);
335         return 0;
336 }
337
338 #ifdef CONFIG_CMD_BOOTZ
339
340 struct zimage_header {
341         uint32_t        code[9];
342         uint32_t        zi_magic;
343         uint32_t        zi_start;
344         uint32_t        zi_end;
345 };
346
347 #define LINUX_ARM_ZIMAGE_MAGIC  0x016f2818
348
349 int bootz_setup(void *image, void **start, void **end)
350 {
351         struct zimage_header *zi = (struct zimage_header *)image;
352
353         if (zi->zi_magic != LINUX_ARM_ZIMAGE_MAGIC) {
354                 puts("Bad Linux ARM zImage magic!\n");
355                 return 1;
356         }
357
358         *start = (void *)zi->zi_start;
359         *end = (void *)zi->zi_end;
360
361         debug("Kernel image @ 0x%08x [ 0x%08x - 0x%08x ]\n",
362                 (uint32_t)image, (uint32_t)*start, (uint32_t)*end);
363
364         return 0;
365 }
366 #endif  /* CONFIG_CMD_BOOTZ */