* Patch by Nicolas Lacressonniere, 11 Jun 2003:
[platform/kernel/u-boot.git] / lib_arm / armlinux.c
1 /*
2  * (C) Copyright 2002
3  * Sysgo Real-Time Solutions, GmbH <www.elinos.com>
4  * Marius Groeger <mgroeger@sysgo.de>
5  *
6  * Copyright (C) 2001  Erik Mouw (J.A.K.Mouw@its.tudelft.nl)
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  */
23
24 #include <common.h>
25 #include <command.h>
26 #include <cmd_boot.h>
27 #include <image.h>
28 #include <zlib.h>
29 #include <asm/byteorder.h>
30 #ifdef CONFIG_HAS_DATAFLASH
31 #include <dataflash.h>
32 #endif
33
34 #include <asm/setup.h>
35 #define tag_size(type)  ((sizeof(struct tag_header) + sizeof(struct type)) >> 2)
36 #define tag_next(t)     ((struct tag *)((u32 *)(t) + (t)->hdr.size))
37
38 #if defined (CONFIG_SETUP_MEMORY_TAGS) || \
39     defined (CONFIG_CMDLINE_TAG) || \
40     defined (CONFIG_INITRD_TAG) || \
41     defined (CONFIG_VFD)
42 static void setup_start_tag(bd_t *bd);
43 # ifdef CONFIG_SETUP_MEMORY_TAGS
44 static void setup_memory_tags(bd_t *bd);
45 # endif
46 static void setup_commandline_tag(bd_t *bd, char *commandline);
47 #if 0
48 static void setup_ramdisk_tag(bd_t *bd);
49 #endif
50 # ifdef CONFIG_INITRD_TAG
51 static void setup_initrd_tag(bd_t *bd, ulong initrd_start, ulong initrd_end);
52 # endif
53 static void setup_end_tag(bd_t *bd);
54 # if defined (CONFIG_VFD)
55 static void setup_videolfb_tag(gd_t *gd);
56 # endif
57
58
59 static struct tag *params;
60 #endif /* CONFIG_SETUP_MEMORY_TAGS || CONFIG_CMDLINE_TAG || CONFIG_INITRD_TAG */
61
62 #ifdef CONFIG_SHOW_BOOT_PROGRESS
63 # include <status_led.h>
64 # define SHOW_BOOT_PROGRESS(arg)        show_boot_progress(arg)
65 #else
66 # define SHOW_BOOT_PROGRESS(arg)
67 #endif
68
69 extern image_header_t header;           /* from cmd_bootm.c */
70
71
72 void do_bootm_linux(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[],
73                 ulong addr, ulong *len_ptr, int   verify)
74 {
75         DECLARE_GLOBAL_DATA_PTR;
76
77     ulong len = 0, checksum;
78     ulong initrd_start, initrd_end;
79     ulong data;
80     void (*theKernel)(int zero, int arch);
81     image_header_t *hdr = &header;
82     bd_t *bd = gd->bd;
83 #ifdef CONFIG_CMDLINE_TAG
84     char *commandline = getenv("bootargs");
85 #endif
86
87     theKernel = (void (*)(int, int))ntohl(hdr->ih_ep);
88
89     /*
90      * Check if there is an initrd image
91      */
92     if (argc >= 3) {
93         SHOW_BOOT_PROGRESS (9);
94
95         addr = simple_strtoul(argv[2], NULL, 16);
96
97         printf ("## Loading Ramdisk Image at %08lx ...\n", addr);
98
99         /* Copy header so we can blank CRC field for re-calculation */
100 #ifdef CONFIG_HAS_DATAFLASH
101         if (addr_dataflash(addr)){
102                 read_dataflash(addr, sizeof(image_header_t), (char *)&header);
103         } else
104 #endif  
105         memcpy (&header, (char *)addr, sizeof(image_header_t));
106
107         if (ntohl(hdr->ih_magic) != IH_MAGIC) {
108             printf ("Bad Magic Number\n");
109             SHOW_BOOT_PROGRESS (-10);
110             do_reset (cmdtp, flag, argc, argv);
111         }
112
113         data = (ulong)&header;
114         len  = sizeof(image_header_t);
115
116         checksum = ntohl(hdr->ih_hcrc);
117         hdr->ih_hcrc = 0;
118
119         if (crc32 (0, (char *)data, len) != checksum) {
120             printf ("Bad Header Checksum\n");
121             SHOW_BOOT_PROGRESS (-11);
122             do_reset (cmdtp, flag, argc, argv);
123         }
124
125         SHOW_BOOT_PROGRESS (10);
126
127         print_image_hdr (hdr);
128
129         data = addr + sizeof(image_header_t);
130         len  = ntohl(hdr->ih_size);
131
132 #ifdef CONFIG_HAS_DATAFLASH
133         if (addr_dataflash(addr)){
134                 read_dataflash(data, len, (char *)CFG_LOAD_ADDR);
135                 data = CFG_LOAD_ADDR;
136         }
137 #endif
138
139         if (verify) {
140             ulong csum = 0;
141
142             printf ("   Verifying Checksum ... ");
143             csum = crc32 (0, (char *)data, len);
144             if (csum != ntohl(hdr->ih_dcrc)) {
145                 printf ("Bad Data CRC\n");
146                 SHOW_BOOT_PROGRESS (-12);
147                 do_reset (cmdtp, flag, argc, argv);
148             }
149             printf ("OK\n");
150         }
151
152         SHOW_BOOT_PROGRESS (11);
153
154         if ((hdr->ih_os   != IH_OS_LINUX)       ||
155             (hdr->ih_arch != IH_CPU_ARM)        ||
156             (hdr->ih_type != IH_TYPE_RAMDISK)   ) {
157             printf ("No Linux ARM Ramdisk Image\n");
158             SHOW_BOOT_PROGRESS (-13);
159             do_reset (cmdtp, flag, argc, argv);
160         }
161
162         /*
163          * Now check if we have a multifile image
164          */
165     } else if ((hdr->ih_type==IH_TYPE_MULTI) && (len_ptr[1])) {
166         ulong tail    = ntohl(len_ptr[0]) % 4;
167         int i;
168
169         SHOW_BOOT_PROGRESS (13);
170
171         /* skip kernel length and terminator */
172         data = (ulong)(&len_ptr[2]);
173         /* skip any additional image length fields */
174         for (i=1; len_ptr[i]; ++i)
175           data += 4;
176         /* add kernel length, and align */
177         data += ntohl(len_ptr[0]);
178         if (tail) {
179             data += 4 - tail;
180         }
181
182         len   = ntohl(len_ptr[1]);
183
184     } else {
185         /*
186          * no initrd image
187          */
188         SHOW_BOOT_PROGRESS (14);
189
190         data = 0;
191     }
192
193 #ifdef  DEBUG
194     if (!data) {
195         printf ("No initrd\n");
196     }
197 #endif
198
199     if (data) {
200         initrd_start = data;
201         initrd_end   = initrd_start + len;
202     } else {
203         initrd_start = 0;
204         initrd_end = 0;
205     }
206
207     SHOW_BOOT_PROGRESS (15);
208
209 #ifdef DEBUG
210     printf ("## Transferring control to Linux (at address %08lx) ...\n",
211             (ulong)theKernel);
212 #endif
213
214 #if defined (CONFIG_SETUP_MEMORY_TAGS) || \
215     defined (CONFIG_CMDLINE_TAG) || \
216     defined (CONFIG_INITRD_TAG) || \
217     defined (CONFIG_VFD)
218     setup_start_tag(bd);
219 #ifdef CONFIG_SETUP_MEMORY_TAGS
220     setup_memory_tags(bd);
221 #endif
222 #ifdef CONFIG_CMDLINE_TAG
223     setup_commandline_tag(bd, commandline);
224 #endif
225 #ifdef CONFIG_INITRD_TAG
226     setup_initrd_tag(bd, initrd_start, initrd_end);
227 #endif
228 #if 0
229     setup_ramdisk_tag(bd);
230 #endif
231 #if defined (CONFIG_VFD)
232     setup_videolfb_tag(gd);
233 #endif
234     setup_end_tag(bd);
235 #endif
236
237     /* we assume that the kernel is in place */
238     printf("\nStarting kernel ...\n\n");
239
240     cleanup_before_linux();
241
242     theKernel(0, bd->bi_arch_number);
243 }
244
245
246 #if defined (CONFIG_SETUP_MEMORY_TAGS) || \
247     defined (CONFIG_CMDLINE_TAG) || \
248     defined (CONFIG_INITRD_TAG) || \
249     defined (CONFIG_VFD)
250 static void setup_start_tag(bd_t *bd)
251 {
252     params = (struct tag *)bd->bi_boot_params;
253
254     params->hdr.tag = ATAG_CORE;
255     params->hdr.size = tag_size(tag_core);
256
257     params->u.core.flags = 0;
258     params->u.core.pagesize = 0;
259     params->u.core.rootdev = 0;
260
261     params = tag_next(params);
262 }
263
264
265 #ifdef CONFIG_SETUP_MEMORY_TAGS
266 static void setup_memory_tags(bd_t *bd)
267 {
268     int i;
269
270     for(i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
271         params->hdr.tag = ATAG_MEM;
272         params->hdr.size = tag_size(tag_mem32);
273
274         params->u.mem.start = bd->bi_dram[i].start;
275         params->u.mem.size = bd->bi_dram[i].size;
276
277         params = tag_next(params);
278     }
279 }
280 #endif  /* CONFIG_SETUP_MEMORY_TAGS */
281
282
283 static void setup_commandline_tag(bd_t *bd, char *commandline)
284 {
285     char *p;
286
287     /* eat leading white space */
288     for(p = commandline; *p == ' '; p++)
289       ;
290
291     /* skip non-existent command lines so the kernel will still
292      * use its default command line.
293      */
294     if(*p == '\0')
295       return;
296
297     params->hdr.tag = ATAG_CMDLINE;
298     params->hdr.size = (sizeof(struct tag_header) + strlen(p) + 1 + 4) >> 2;
299
300     strcpy(params->u.cmdline.cmdline, p);
301
302     params = tag_next(params);
303 }
304
305
306 #ifndef ATAG_INITRD2
307 #define ATAG_INITRD2    0x54420005
308 #endif
309
310 #ifdef CONFIG_INITRD_TAG
311 static void setup_initrd_tag(bd_t *bd, ulong initrd_start, ulong initrd_end)
312 {
313     /* an ATAG_INITRD node tells the kernel where the compressed
314      * ramdisk can be found. ATAG_RDIMG is a better name, actually.
315      */
316     params->hdr.tag = ATAG_INITRD2;
317     params->hdr.size = tag_size(tag_initrd);
318
319     params->u.initrd.start = initrd_start;
320     params->u.initrd.size = initrd_end - initrd_start;
321
322     params = tag_next(params);
323 }
324 #endif  /* CONFIG_INITRD_TAG */
325
326
327 #if 0
328 static void setup_ramdisk_tag(bd_t *bd)
329 {
330     /* an ATAG_RAMDISK node tells the kernel how large the
331      * decompressed ramdisk will become.
332      */
333     params->hdr.tag = ATAG_RAMDISK;
334     params->hdr.size = tag_size(tag_ramdisk);
335
336     params->u.ramdisk.start = 0;
337     /*params->u.ramdisk.size = RAMDISK_SIZE; */
338     params->u.ramdisk.flags = 1;        /* automatically load ramdisk */
339
340     params = tag_next(params);
341 }
342 #endif /* 0 */
343
344 #if defined (CONFIG_VFD)
345 static void setup_videolfb_tag(gd_t *gd)
346 {
347     /* An ATAG_VIDEOLFB node tells the kernel where and how large
348      * the framebuffer for video was allocated (among other things).
349      * Note that a _physical_ address is passed !
350      *
351      * We only use it to pass the address and size, the other entries
352      * in the tag_videolfb are not of interest.
353      */
354     params->hdr.tag = ATAG_VIDEOLFB;
355     params->hdr.size = tag_size(tag_videolfb);
356
357     params->u.videolfb.lfb_base = (u32)gd->fb_base;
358     /* 7168 = 256*4*56/8 - actually 2 pages (8192 bytes) are allocated */
359     params->u.videolfb.lfb_size = 7168;
360
361     params = tag_next(params);
362 }
363 #endif
364
365 static void setup_end_tag(bd_t *bd)
366 {
367     params->hdr.tag = ATAG_NONE;
368     params->hdr.size = 0;
369 }
370
371 #endif /* CONFIG_SETUP_MEMORY_TAGS || CONFIG_CMDLINE_TAG || CONFIG_INITRD_TAG */