* Code cleanup:
[kernel/u-boot.git] / lib_mips / mips_linux.c
1 /*
2  * (C) Copyright 2003
3  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
4  *
5  * See file CREDITS for list of people who contributed to this
6  * project.
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 <image.h>
27 #include <zlib.h>
28 #include <asm/byteorder.h>
29 #include <asm/addrspace.h>
30
31 #define LINUX_MAX_ENVS          256
32 #define LINUX_MAX_ARGS          256
33
34 #ifdef CONFIG_SHOW_BOOT_PROGRESS
35 # include <status_led.h>
36 # define SHOW_BOOT_PROGRESS(arg)        show_boot_progress(arg)
37 #else
38 # define SHOW_BOOT_PROGRESS(arg)
39 #endif
40
41 extern image_header_t header;           /* from cmd_bootm.c */
42
43 extern int do_reset (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
44
45 static int      linux_argc;
46 static char **  linux_argv;
47
48 static char **  linux_env;
49 static char *   linux_env_p;
50 static int      linux_env_idx;
51
52 static void linux_params_init (ulong start, char * commandline);
53 static void linux_env_set (char * env_name, char * env_val);
54
55
56 void do_bootm_linux(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[],
57                 ulong addr, ulong *len_ptr, int   verify)
58 {
59     DECLARE_GLOBAL_DATA_PTR;
60
61     ulong len = 0, checksum;
62     ulong initrd_start, initrd_end;
63     ulong data;
64     void (*theKernel)(int, char **, char **, int *);
65     image_header_t *hdr = &header;
66     char *commandline = getenv("bootargs");
67     char env_buf[12];
68
69     theKernel = (void (*)(int, char **, char **, int *))ntohl(hdr->ih_ep);
70
71     /*
72      * Check if there is an initrd image
73      */
74     if (argc >= 3) {
75         SHOW_BOOT_PROGRESS (9);
76
77         addr = simple_strtoul(argv[2], NULL, 16);
78
79         printf ("## Loading Ramdisk Image at %08lx ...\n", addr);
80
81         /* Copy header so we can blank CRC field for re-calculation */
82         memcpy (&header, (char *)addr, sizeof(image_header_t));
83
84         if (ntohl(hdr->ih_magic) != IH_MAGIC) {
85             printf ("Bad Magic Number\n");
86             SHOW_BOOT_PROGRESS (-10);
87             do_reset (cmdtp, flag, argc, argv);
88         }
89
90         data = (ulong)&header;
91         len  = sizeof(image_header_t);
92
93         checksum = ntohl(hdr->ih_hcrc);
94         hdr->ih_hcrc = 0;
95
96         if (crc32 (0, (char *)data, len) != checksum) {
97             printf ("Bad Header Checksum\n");
98             SHOW_BOOT_PROGRESS (-11);
99             do_reset (cmdtp, flag, argc, argv);
100         }
101
102         SHOW_BOOT_PROGRESS (10);
103
104         print_image_hdr (hdr);
105
106         data = addr + sizeof(image_header_t);
107         len  = ntohl(hdr->ih_size);
108
109         if (verify) {
110             ulong csum = 0;
111
112             printf ("   Verifying Checksum ... ");
113             csum = crc32 (0, (char *)data, len);
114             if (csum != ntohl(hdr->ih_dcrc)) {
115                 printf ("Bad Data CRC\n");
116                 SHOW_BOOT_PROGRESS (-12);
117                 do_reset (cmdtp, flag, argc, argv);
118             }
119             printf ("OK\n");
120         }
121
122         SHOW_BOOT_PROGRESS (11);
123
124         if ((hdr->ih_os   != IH_OS_LINUX)       ||
125             (hdr->ih_arch != IH_CPU_MIPS)       ||
126             (hdr->ih_type != IH_TYPE_RAMDISK)   ) {
127             printf ("No Linux MIPS Ramdisk Image\n");
128             SHOW_BOOT_PROGRESS (-13);
129             do_reset (cmdtp, flag, argc, argv);
130         }
131
132         /*
133          * Now check if we have a multifile image
134          */
135     } else if ((hdr->ih_type==IH_TYPE_MULTI) && (len_ptr[1])) {
136         ulong tail    = ntohl(len_ptr[0]) % 4;
137         int i;
138
139         SHOW_BOOT_PROGRESS (13);
140
141         /* skip kernel length and terminator */
142         data = (ulong)(&len_ptr[2]);
143         /* skip any additional image length fields */
144         for (i=1; len_ptr[i]; ++i)
145           data += 4;
146         /* add kernel length, and align */
147         data += ntohl(len_ptr[0]);
148         if (tail) {
149             data += 4 - tail;
150         }
151
152         len   = ntohl(len_ptr[1]);
153
154     } else {
155         /*
156          * no initrd image
157          */
158         SHOW_BOOT_PROGRESS (14);
159
160         data = 0;
161     }
162
163 #ifdef  DEBUG
164     if (!data) {
165         printf ("No initrd\n");
166     }
167 #endif
168
169     if (data) {
170         initrd_start = data;
171         initrd_end   = initrd_start + len;
172     } else {
173         initrd_start = 0;
174         initrd_end = 0;
175     }
176
177     SHOW_BOOT_PROGRESS (15);
178
179 #ifdef DEBUG
180     printf ("## Transferring control to Linux (at address %08lx) ...\n",
181             (ulong)theKernel);
182 #endif
183
184     linux_params_init (PHYSADDR(gd->bd->bi_boot_params), commandline);
185
186     sprintf (env_buf, "%lu", gd->ram_size >> 20);
187     linux_env_set ("memsize", env_buf);
188
189     sprintf (env_buf, "0x%08X", (uint)PHYSADDR(initrd_start));
190     linux_env_set ("initrd_start", env_buf);
191
192     sprintf (env_buf, "0x%X", (uint)(initrd_end - initrd_start));
193     linux_env_set ("initrd_size", env_buf);
194
195     sprintf (env_buf, "0x%08X", (uint)(gd->bd->bi_flashstart));
196     linux_env_set ("flash_start", env_buf);
197
198     sprintf (env_buf, "0x%X", (uint)(gd->bd->bi_flashsize));
199     linux_env_set ("flash_size", env_buf);
200
201     /* we assume that the kernel is in place */
202     printf("\nStarting kernel ...\n\n");
203
204     theKernel(linux_argc, linux_argv, linux_env, 0);
205 }
206
207 static void linux_params_init (ulong start, char * line)
208 {
209     char * next, * quote, * argp;
210
211     linux_argc = 1;
212     linux_argv = (char **) start;
213     linux_argv[0] = 0;
214     argp = (char *)(linux_argv + LINUX_MAX_ARGS);
215
216     next = line;
217
218     while (line && *line && linux_argc < LINUX_MAX_ARGS)
219     {
220         quote = strchr (line, '"');
221         next = strchr (line, ' ');
222
223         while (next != NULL && quote != NULL && quote < next)
224         {
225             /* we found a left quote before the next blank
226              * now we have to find the matching right quote
227              */
228             next = strchr (quote + 1, '"');
229             if (next != NULL)
230             {
231                 quote = strchr (next + 1, '"');
232                 next = strchr (next + 1, ' ');
233             }
234         }
235
236         if (next == NULL)
237         {
238             next = line + strlen (line);
239         }
240
241         linux_argv [linux_argc] = argp;
242         memcpy (argp, line, next - line);
243         argp [next - line] = 0;
244
245         argp += next - line + 1;
246         linux_argc ++;
247
248         if (*next) next ++;
249
250         line = next;
251     }
252
253     linux_env = (char **)(((ulong)argp + 15) & ~15);
254     linux_env [0] = 0;
255     linux_env_p = (char *)(linux_env + LINUX_MAX_ENVS);
256     linux_env_idx = 0;
257 }
258
259 static void linux_env_set (char * env_name, char * env_val)
260 {
261     if (linux_env_idx < LINUX_MAX_ENVS - 1)
262     {
263         linux_env [linux_env_idx] = linux_env_p;
264
265         strcpy (linux_env_p, env_name);
266         linux_env_p += strlen (env_name);
267
268         strcpy (linux_env_p, "=");
269         linux_env_p += 1;
270
271         strcpy (linux_env_p, env_val);
272         linux_env_p += strlen (env_val);
273
274         linux_env_p ++;
275         linux_env [++ linux_env_idx] = 0;
276     }
277 }