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