]> git.ipfire.org Git - people/ms/u-boot.git/blob - lib_m68k/m68k_linux.c
ColdFire: Add M54455EVB for MCF5445x
[people/ms/u-boot.git] / lib_m68k / m68k_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 <bzlib.h>
29 #include <environment.h>
30 #include <asm/byteorder.h>
31
32 DECLARE_GLOBAL_DATA_PTR;
33
34 #define PHYSADDR(x) x
35
36 #define LINUX_MAX_ENVS 256
37 #define LINUX_MAX_ARGS 256
38
39 #ifdef CONFIG_SHOW_BOOT_PROGRESS
40 # include <status_led.h>
41 # define SHOW_BOOT_PROGRESS(arg) show_boot_progress(arg)
42 #else
43 # define SHOW_BOOT_PROGRESS(arg)
44 #endif
45
46 extern image_header_t header;
47
48 void do_bootm_linux(cmd_tbl_t * cmdtp, int flag,
49 int argc, char *argv[],
50 ulong addr, ulong * len_ptr, int verify)
51 {
52 ulong sp;
53 ulong len, checksum;
54 ulong initrd_start, initrd_end;
55 ulong cmd_start, cmd_end;
56 ulong initrd_high;
57 ulong data;
58 int initrd_copy_to_ram = 1;
59 char *cmdline;
60 char *s;
61 bd_t *kbd;
62 void (*kernel) (bd_t *, ulong, ulong, ulong, ulong);
63 image_header_t *hdr = &header;
64
65 if ((s = getenv("initrd_high")) != NULL) {
66 /* a value of "no" or a similar string will act like 0,
67 * turning the "load high" feature off. This is intentional.
68 */
69 initrd_high = simple_strtoul(s, NULL, 16);
70 if (initrd_high == ~0)
71 initrd_copy_to_ram = 0;
72 } else { /* not set, no restrictions to load high */
73 initrd_high = ~0;
74 }
75
76 #ifdef CONFIG_LOGBUFFER
77 kbd = gd->bd;
78 /* Prevent initrd from overwriting logbuffer */
79 if (initrd_high < (kbd->bi_memsize - LOGBUFF_LEN - LOGBUFF_OVERHEAD))
80 initrd_high = kbd->bi_memsize - LOGBUFF_LEN - LOGBUFF_OVERHEAD;
81 debug("## Logbuffer at 0x%08lX ", kbd->bi_memsize - LOGBUFF_LEN);
82 #endif
83
84 /*
85 * Booting a (Linux) kernel image
86 *
87 * Allocate space for command line and board info - the
88 * address should be as high as possible within the reach of
89 * the kernel (see CFG_BOOTMAPSZ settings), but in unused
90 * memory, which means far enough below the current stack
91 * pointer.
92 */
93 asm("movel %%a7, %%d0\n"
94 "movel %%d0, %0\n": "=d"(sp): :"%d0");
95 debug("## Current stack ends at 0x%08lX ", sp);
96
97 sp -= 2048; /* just to be sure */
98 if (sp > CFG_BOOTMAPSZ)
99 sp = CFG_BOOTMAPSZ;
100 sp &= ~0xF;
101
102 debug("=> set upper limit to 0x%08lX\n", sp);
103
104 cmdline = (char *)((sp - CFG_BARGSIZE) & ~0xF);
105 kbd = (bd_t *) (((ulong) cmdline - sizeof(bd_t)) & ~0xF);
106
107 if ((s = getenv("bootargs")) == NULL)
108 s = "";
109
110 strcpy(cmdline, s);
111
112 cmd_start = (ulong) & cmdline[0];
113 cmd_end = cmd_start + strlen(cmdline);
114
115 *kbd = *(gd->bd);
116
117 #ifdef DEBUG
118 printf("## cmdline at 0x%08lX ... 0x%08lX\n", cmd_start, cmd_end);
119
120 do_bdinfo(NULL, 0, 0, NULL);
121 #endif
122
123 if ((s = getenv("clocks_in_mhz")) != NULL) {
124 /* convert all clock information to MHz */
125 kbd->bi_intfreq /= 1000000L;
126 kbd->bi_busfreq /= 1000000L;
127 }
128
129 kernel =
130 (void (*)(bd_t *, ulong, ulong, ulong, ulong))ntohl(hdr->ih_ep);
131
132 /*
133 * Check if there is an initrd image
134 */
135
136 if (argc >= 3) {
137 debug("Not skipping initrd\n");
138 SHOW_BOOT_PROGRESS(9);
139
140 addr = simple_strtoul(argv[2], NULL, 16);
141
142 printf("## Loading RAMDisk Image at %08lx ...\n", addr);
143
144 /* Copy header so we can blank CRC field for re-calculation */
145 memmove(&header, (char *)addr, sizeof(image_header_t));
146
147 if (ntohl(hdr->ih_magic) != IH_MAGIC) {
148 puts("Bad Magic Number\n");
149 SHOW_BOOT_PROGRESS(-10);
150 do_reset(cmdtp, flag, argc, argv);
151 }
152
153 data = (ulong) & header;
154 len = sizeof(image_header_t);
155
156 checksum = ntohl(hdr->ih_hcrc);
157 hdr->ih_hcrc = 0;
158
159 if (crc32(0, (uchar *) data, len) != checksum) {
160 puts("Bad Header Checksum\n");
161 SHOW_BOOT_PROGRESS(-11);
162 do_reset(cmdtp, flag, argc, argv);
163 }
164
165 SHOW_BOOT_PROGRESS(10);
166
167 print_image_hdr(hdr);
168
169 data = addr + sizeof(image_header_t);
170 len = ntohl(hdr->ih_size);
171
172 if (verify) {
173 ulong csum = 0;
174 #if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG)
175 ulong cdata = data, edata = cdata + len;
176 #endif /* CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG */
177
178 puts(" Verifying Checksum ... ");
179
180 #if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG)
181
182 while (cdata < edata) {
183 ulong chunk = edata - cdata;
184
185 if (chunk > CHUNKSZ)
186 chunk = CHUNKSZ;
187 csum = crc32(csum, (uchar *) cdata, chunk);
188 cdata += chunk;
189
190 WATCHDOG_RESET();
191 }
192 #else /* !(CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG) */
193 csum = crc32(0, (uchar *) data, len);
194 #endif /* CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG */
195
196 if (csum != ntohl(hdr->ih_dcrc)) {
197 puts("Bad Data CRC\n");
198 SHOW_BOOT_PROGRESS(-12);
199 do_reset(cmdtp, flag, argc, argv);
200 }
201 puts("OK\n");
202 }
203
204 SHOW_BOOT_PROGRESS(11);
205
206 if ((hdr->ih_os != IH_OS_LINUX) ||
207 (hdr->ih_arch != IH_CPU_M68K) ||
208 (hdr->ih_type != IH_TYPE_RAMDISK)) {
209 puts("No Linux ColdFire Ramdisk Image\n");
210 SHOW_BOOT_PROGRESS(-13);
211 do_reset(cmdtp, flag, argc, argv);
212 }
213
214 /*
215 * Now check if we have a multifile image
216 */
217 } else if ((hdr->ih_type == IH_TYPE_MULTI) && (len_ptr[1])) {
218 u_long tail = ntohl(len_ptr[0]) % 4;
219 int i;
220
221 SHOW_BOOT_PROGRESS(13);
222
223 /* skip kernel length and terminator */
224 data = (ulong) (&len_ptr[2]);
225 /* skip any additional image length fields */
226 for (i = 1; len_ptr[i]; ++i)
227 data += 4;
228 /* add kernel length, and align */
229 data += ntohl(len_ptr[0]);
230 if (tail) {
231 data += 4 - tail;
232 }
233
234 len = ntohl(len_ptr[1]);
235
236 } else {
237 /*
238 * no initrd image
239 */
240 SHOW_BOOT_PROGRESS(14);
241
242 len = data = 0;
243 }
244
245 if (!data) {
246 debug("No initrd\n");
247 }
248
249 if (data) {
250 if (!initrd_copy_to_ram) { /* zero-copy ramdisk support */
251 initrd_start = data;
252 initrd_end = initrd_start + len;
253 } else {
254 initrd_start = (ulong) kbd - len;
255 initrd_start &= ~(4096 - 1); /* align on page */
256
257 if (initrd_high) {
258 ulong nsp;
259
260 /*
261 * the inital ramdisk does not need to be within
262 * CFG_BOOTMAPSZ as it is not accessed until after
263 * the mm system is initialised.
264 *
265 * do the stack bottom calculation again and see if
266 * the initrd will fit just below the monitor stack
267 * bottom without overwriting the area allocated
268 * above for command line args and board info.
269 */
270 asm("movel %%a7, %%d0\n"
271 "movel %%d0, %0\n": "=d"(nsp): :"%d0");
272 nsp -= 2048; /* just to be sure */
273 nsp &= ~0xF;
274
275 if (nsp > initrd_high) /* limit as specified */
276 nsp = initrd_high;
277
278 nsp -= len;
279 nsp &= ~(4096 - 1); /* align on page */
280
281 if (nsp >= sp)
282 initrd_start = nsp;
283 }
284
285 SHOW_BOOT_PROGRESS(12);
286
287 debug
288 ("## initrd at 0x%08lX ... 0x%08lX (len=%ld=0x%lX)\n",
289 data, data + len - 1, len, len);
290
291 initrd_end = initrd_start + len;
292 printf(" Loading Ramdisk to %08lx, end %08lx ... ",
293 initrd_start, initrd_end);
294 #if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG)
295 {
296 size_t l = len;
297 void *to = (void *)initrd_start;
298 void *from = (void *)data;
299
300 while (l > 0) {
301 size_t tail =
302 (l > CHUNKSZ) ? CHUNKSZ : l;
303 WATCHDOG_RESET();
304 memmove(to, from, tail);
305 to += tail;
306 from += tail;
307 l -= tail;
308 }
309 }
310 #else /* !(CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG) */
311 memmove((void *)initrd_start, (void *)data, len);
312 #endif /* CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG */
313 puts("OK\n");
314 }
315 } else {
316 initrd_start = 0;
317 initrd_end = 0;
318 }
319
320 debug("## Transferring control to Linux (at address %08lx) ...\n",
321 (ulong) kernel);
322
323 SHOW_BOOT_PROGRESS(15);
324
325 /*
326 * Linux Kernel Parameters (passing board info data):
327 * r3: ptr to board info data
328 * r4: initrd_start or 0 if no initrd
329 * r5: initrd_end - unused if r4 is 0
330 * r6: Start of command line string
331 * r7: End of command line string
332 */
333 (*kernel) (kbd, initrd_start, initrd_end, cmd_start, cmd_end);
334 /* does not return */
335 }