1 //==========================================================================
3 // redboot_linux_boot.c
5 // RedBoot command to boot Linux on ARM platforms
7 //==========================================================================
8 //####ECOSGPLCOPYRIGHTBEGIN####
9 // -------------------------------------------
10 // This file is part of eCos, the Embedded Configurable Operating System.
11 // Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004 Red Hat, Inc.
12 // Copyright (C) 2003, 2004 Gary Thomas
14 // eCos is free software; you can redistribute it and/or modify it under
15 // the terms of the GNU General Public License as published by the Free
16 // Software Foundation; either version 2 or (at your option) any later version.
18 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
19 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
20 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
23 // You should have received a copy of the GNU General Public License along
24 // with eCos; if not, write to the Free Software Foundation, Inc.,
25 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
27 // As a special exception, if other files instantiate templates or use macros
28 // or inline functions from this file, or you compile this file and link it
29 // with other works to produce a work based on this file, this file does not
30 // by itself cause the resulting work to be covered by the GNU General Public
31 // License. However the source code for this file must still be made available
32 // in accordance with section (3) of the GNU General Public License.
34 // This exception does not invalidate any other reasons why a work based on
35 // this file might be covered by the GNU General Public License.
37 // Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
38 // at http://sources.redhat.com/ecos/ecos-license/
39 // -------------------------------------------
40 //####ECOSGPLCOPYRIGHTEND####
41 //####OTHERCOPYRIGHTBEGIN####
43 // The structure definitions below are taken from include/asm-arm/setup.h in
44 // the Linux kernel, Copyright (C) 1997-1999 Russell King. Their presence
45 // here is for the express purpose of communication with the Linux kernel
46 // being booted and is considered 'fair use' by the original author and
47 // are included with his permission.
49 //####OTHERCOPYRIGHTEND####
50 //==========================================================================
51 //#####DESCRIPTIONBEGIN####
54 // Contributors: gthomas, jskov,
55 // Russell King <rmk@arm.linux.org.uk>
60 // This code is part of RedBoot (tm).
62 //####DESCRIPTIONEND####
64 //==========================================================================
66 #include <pkgconf/hal.h>
69 #ifdef CYGPKG_IO_ETH_DRIVERS
70 #include <cyg/io/eth/eth_drv.h> // Logical driver interfaces
73 #include <cyg/hal/hal_intr.h>
74 #include <cyg/hal/hal_cache.h>
75 #include CYGHWR_MEMORY_LAYOUT_H
77 #include <cyg/hal/hal_io.h>
79 #ifndef CYGARC_PHYSICAL_ADDRESS
81 # define CYGARC_PHYSICAL_ADDRESS(x) (x)
84 // FIXME: This should be a CDL variable, and CYGSEM_REDBOOT_ARM_LINUX_BOOT
85 // active_if CYGHWR_HAL_ARM_REDBOOT_MACHINE_TYPE>0
86 #ifdef HAL_PLATFORM_MACHINE_TYPE
87 #define CYGHWR_REDBOOT_ARM_MACHINE_TYPE HAL_PLATFORM_MACHINE_TYPE
89 // Exported CLI function(s)
90 static void do_exec(int argc, char *argv[]);
92 "Execute an image - with MMU off",
93 "[-w timeout] [-b <load addr> [-l <length>]]\n"
94 " [-r <ramdisk addr> [-s <ramdisk length>]]\n"
95 " [-c \"kernel command line\"] [-t <target> ] [<entry_point>]",
99 // CYGARC_HAL_MMU_OFF inserts code to turn off MMU and jump to a physical
100 // address. Some ARM implementations may need special handling and define
101 // their own version.
102 #ifndef CYGARC_HAL_MMU_OFF
104 #define __CYGARC_GET_CTLREG \
105 " mrc p15,0,r0,c1,c0,0\n"
107 #define __CYGARC_CLR_MMU_BITS \
108 " bic r0,r0,#0xd\n" \
109 " bic r0,r0,#0x1000\n" \
111 #ifdef CYGHWR_HAL_ARM_BIGENDIAN
112 #define __CYGARC_CLR_MMU_BITS_X \
113 " bic r0,r0,#0x8d\n" \
114 " bic r0,r0,#0x1000\n"
116 #define __CYGARC_CLR_MMU_BITS_X \
117 " bic r0,r0,#0xd\n" \
118 " bic r0,r0,#0x1000\n" \
122 #define __CYGARC_SET_CTLREG(__paddr__) \
123 " mcr p15,0,r0,c1,c0,0\n" \
124 " mov pc," #__paddr__ "\n"
126 #define CYGARC_HAL_MMU_OFF(__paddr__) \
127 " mcr p15,0,r0,c7,c10,4\n" \
128 " mcr p15,0,r0,c7,c7,0\n" \
129 __CYGARC_GET_CTLREG \
130 __CYGARC_CLR_MMU_BITS \
131 __CYGARC_SET_CTLREG(__paddr__)
133 #define CYGARC_HAL_MMU_OFF_X(__paddr__) \
134 " mcr p15,0,r0,c7,c10,4\n" \
135 " mcr p15,0,r0,c7,c7,0\n" \
136 __CYGARC_GET_CTLREG \
137 __CYGARC_CLR_MMU_BITS_X \
138 __CYGARC_SET_CTLREG(__paddr__)
140 #endif // CYGARC_HAL_MMU_OFF
142 #ifndef CYGARC_HAL_EXEC_FIXUP
143 #define CYGARC_HAL_EXEC_FIXUP() ""
146 // Parameter info for Linux kernel
147 // ** C A U T I O N ** This setup must match "asm-arm/setup.h"
149 // Info is passed at a fixed location, using a sequence of tagged
153 typedef unsigned long u32;
154 typedef unsigned short u16;
155 typedef unsigned char u8;
157 //=========================================================================
158 // From Linux <asm-arm/setup.h>
160 #define ATAG_NONE 0x00000000
162 u32 size; // Size of tag (hdr+data) in *longwords*
166 #define ATAG_CORE 0x54410001
168 u32 flags; /* bit 0 = read-only */
173 #define ATAG_MEM 0x54410002
179 #define ATAG_VIDEOTEXT 0x54410003
180 struct tag_videotext {
192 #define ATAG_RAMDISK 0x54410004
194 u32 flags; /* b0 = load, b1 = prompt */
200 * this one accidentally used virtual addresses - as such,
203 #define ATAG_INITRD 0x54410005
205 /* describes where the compressed ramdisk image lives (physical address) */
206 #define ATAG_INITRD2 0x54420005
212 #define ATAG_SERIAL 0x54410006
213 struct tag_serialnr {
218 #define ATAG_REVISION 0x54410007
219 struct tag_revision {
223 #define ATAG_VIDEOLFB 0x54410008
224 struct tag_videolfb {
241 #define ATAG_CMDLINE 0x54410009
246 #define ATAG_ACORN 0x41000101
248 u32 memc_control_reg;
254 #define ATAG_MEMCLK 0x41000402
260 struct tag_header hdr;
262 struct tag_core core;
263 struct tag_mem32 mem;
264 struct tag_videotext videotext;
265 struct tag_ramdisk ramdisk;
266 struct tag_initrd initrd;
267 struct tag_serialnr serialnr;
268 struct tag_revision revision;
269 struct tag_videolfb videolfb;
270 struct tag_cmdline cmdline;
275 struct tag_acorn acorn;
280 struct tag_memclk memclk;
284 // End of inclusion from <asm-arm/setup.h>
285 //=========================================================================
287 // Default memory layout - can be overridden by platform, typically in
288 // <cyg/hal/plf_io.h>
289 #ifndef CYGHWR_REDBOOT_LINUX_ATAG_MEM
290 #define CYGHWR_REDBOOT_LINUX_ATAG_MEM(_p_) \
292 /* Next ATAG_MEM. */ \
293 _p_->hdr.size = (sizeof(struct tag_mem32) + sizeof(struct tag_header))/sizeof(long); \
294 _p_->hdr.tag = ATAG_MEM; \
295 /* Round up so there's only one bit set in the memory size. \
296 * Don't double it if it's already a power of two, though. \
298 _p_->u.mem.size = 1<<hal_msbindex(CYGMEM_REGION_ram_SIZE); \
299 if (_p_->u.mem.size < CYGMEM_REGION_ram_SIZE) \
300 _p_->u.mem.size <<= 1; \
301 _p_->u.mem.start = CYGARC_PHYSICAL_ADDRESS(CYGMEM_REGION_ram); \
306 // Round up a quantity to a longword (32 bit) length
307 #define ROUNDUP(n) (((n) + 3) & ~3)
310 do_exec(int argc, char *argv[])
313 unsigned long target;
314 unsigned long oldints;
316 int wait_time, res, num_opts;
317 bool base_addr_set, length_set, cmd_line_set;
318 bool ramdisk_addr_set, ramdisk_size_set;
319 unsigned long base_addr, length;
320 unsigned long ramdisk_addr, ramdisk_size;
321 struct option_info opts[7];
324 struct tag *params = (struct tag *)CYGHWR_REDBOOT_ARM_LINUX_TAGS_ADDRESS;
325 #ifdef CYGHWR_REDBOOT_LINUX_EXEC_X_SWITCH
327 extern char __xtramp_start__[], __xtramp_end__[];
329 extern char __tramp_start__[], __tramp_end__[];
332 target = load_address;
333 entry = entry_address;
335 // Default physical entry point for Linux is kernel base.
336 entry = (unsigned long)CYGHWR_REDBOOT_ARM_LINUX_EXEC_ADDRESS;
337 target = (unsigned long)CYGHWR_REDBOOT_ARM_LINUX_EXEC_ADDRESS;
339 base_addr = load_address;
340 length = load_address_end - load_address;
341 // Round length up to the next quad word
342 length = (length + 3) & ~0x3;
344 ramdisk_size = 4096*1024;
345 init_opts(&opts[0], 'w', true, OPTION_ARG_TYPE_NUM,
346 &wait_time, &wait_time_set, "wait timeout");
347 init_opts(&opts[1], 'b', true, OPTION_ARG_TYPE_NUM,
348 &base_addr, &base_addr_set, "base address");
349 init_opts(&opts[2], 'l', true, OPTION_ARG_TYPE_NUM,
350 &length, &length_set, "length");
351 init_opts(&opts[3], 'c', true, OPTION_ARG_TYPE_STR,
352 &cmd_line, &cmd_line_set, "kernel command line");
353 init_opts(&opts[4], 'r', true, OPTION_ARG_TYPE_NUM,
354 &ramdisk_addr, &ramdisk_addr_set, "ramdisk_addr");
355 init_opts(&opts[5], 's', true, OPTION_ARG_TYPE_NUM,
356 &ramdisk_size, &ramdisk_size_set, "ramdisk_size");
357 init_opts(&opts[6], 't', true, OPTION_ARG_TYPE_NUM,
358 &target, 0, "[physical] target address");
360 #ifdef CYGHWR_REDBOOT_LINUX_EXEC_X_SWITCH
361 init_opts(&opts[num_opts], 'x', false, OPTION_ARG_TYPE_FLG,
362 &swap_endian, 0, "swap endianness");
365 if (!scan_opts(argc, argv, 1, opts, num_opts, &entry,
366 OPTION_ARG_TYPE_NUM, "[physical] starting address")) {
370 // Check to see if a valid image has been loaded
371 if (entry == (unsigned long)NO_MEMORY) {
372 diag_printf("Can't execute Linux - invalid entry address\n");
376 // Set up parameters to pass to kernel
378 // CORE tag must be present & first
379 params->hdr.size = (sizeof(struct tag_core) + sizeof(struct tag_header))/sizeof(long);
380 params->hdr.tag = ATAG_CORE;
381 params->u.core.flags = 0;
382 params->u.core.pagesize = 0;
383 params->u.core.rootdev = 0;
384 params = (struct tag *)((long *)params + params->hdr.size);
386 // Fill in the details of the memory layout
387 CYGHWR_REDBOOT_LINUX_ATAG_MEM(params);
389 params = (struct tag *)((long *)params + params->hdr.size);
390 if (ramdisk_addr_set) {
391 params->hdr.size = (sizeof(struct tag_initrd) + sizeof(struct tag_header))/sizeof(long);
392 params->hdr.tag = ATAG_INITRD2;
393 params->u.initrd.start = CYGARC_PHYSICAL_ADDRESS(ramdisk_addr);
394 params->u.initrd.size = ramdisk_size;
395 params = (struct tag *)((long *)params + params->hdr.size);
398 params->hdr.size = (ROUNDUP(strlen(cmd_line)) + sizeof(struct tag_header))/sizeof(long);
399 params->hdr.tag = ATAG_CMDLINE;
400 strcpy(params->u.cmdline.cmdline, cmd_line);
401 params = (struct tag *)((long *)params + params->hdr.size);
403 // Mark end of parameter list
404 params->hdr.size = 0;
405 params->hdr.tag = ATAG_NONE;
408 int script_timeout_ms = wait_time * 1000;
409 #ifdef CYGFUN_REDBOOT_BOOT_SCRIPT
410 unsigned char *hold_script = script;
413 diag_printf("About to start execution of image at %p, entry point %p - abort with ^C within %d seconds\n",
414 (void *)target, (void *)entry, wait_time);
415 while (script_timeout_ms >= CYGNUM_REDBOOT_CLI_IDLE_TIMEOUT) {
416 res = _rb_gets(line, sizeof(line), CYGNUM_REDBOOT_CLI_IDLE_TIMEOUT);
417 if (res == _GETS_CTRLC) {
418 #ifdef CYGFUN_REDBOOT_BOOT_SCRIPT
419 script = hold_script; // Re-enable script
423 script_timeout_ms -= CYGNUM_REDBOOT_CLI_IDLE_TIMEOUT;
426 if (!base_addr_set) {
427 if ((base_addr == 0) || (length == 0)) {
428 // Probably not valid - don't try it
429 diag_printf("Base address unknown - use \"-b\" option\n");
432 diag_printf("Using base address %p and length %p\n",
433 (void*)base_addr, (void*)length);
434 } else if (base_addr_set && !length_set) {
435 diag_printf("Length required for non-standard base address\n");
439 #ifdef CYGPKG_IO_ETH_DRIVERS
443 HAL_DISABLE_INTERRUPTS(oldints);
445 HAL_ICACHE_DISABLE();
446 HAL_DCACHE_DISABLE();
448 HAL_ICACHE_INVALIDATE_ALL();
449 HAL_DCACHE_INVALIDATE_ALL();
451 // Tricky code. We are currently running with the MMU on and the
452 // memory map possibly convoluted from 1-1. The trampoline code
453 // between labels __tramp_start__ and __tramp_end__ must be copied
454 // to RAM and then executed at the non-mapped address.
456 // This magic was created in order to be able to execute standard
457 // Linux kernels with as little change/perturberance as possible.
459 #ifdef CYGHWR_REDBOOT_LINUX_EXEC_X_SWITCH
461 // copy the trampoline code
462 memcpy((char *)CYGHWR_REDBOOT_ARM_TRAMPOLINE_ADDRESS,
464 __xtramp_end__ - __xtramp_start__);
467 CYGARC_HAL_MMU_OFF_X(%5)
468 "__xtramp_start__:\n"
469 " cmp %1,%4;\n" // Default kernel load address. Relocate
470 " beq 2f;\n" // kernel image there if necessary, and
471 " cmp %2,#0;\n" // if size is non-zero
475 " eor %5, r0, r0, ror #16;\n"
476 " bic %5, %5, #0x00ff0000;\n"
477 " mov r0, r0, ror #8;\n"
478 " eor r0, r0, %5, lsr #8;\n"
483 " mov r0,#0;\n" // Set board type
484 " mov r1,%3;\n" // Machine type
485 " mov r2,%6;\n" // Kernel parameters
486 " mov pc,%0;\n" // Jump to kernel
490 "r"(CYGARC_PHYSICAL_ADDRESS(base_addr)),
492 "r"(CYGHWR_REDBOOT_ARM_MACHINE_TYPE),
494 "r"(CYGARC_PHYSICAL_ADDRESS(CYGHWR_REDBOOT_ARM_TRAMPOLINE_ADDRESS)),
495 "r"(CYGARC_PHYSICAL_ADDRESS(CYGHWR_REDBOOT_ARM_LINUX_TAGS_ADDRESS))
499 #endif // CYGHWR_REDBOOT_LINUX_EXEC_X_SWITCH
501 // copy the trampoline code
502 memcpy((char *)CYGHWR_REDBOOT_ARM_TRAMPOLINE_ADDRESS,
504 __tramp_end__ - __tramp_start__);
507 CYGARC_HAL_MMU_OFF(%5)
509 " cmp %1,%4;\n" // Default kernel load address. Relocate
510 " beq 2f;\n" // kernel image there if necessary, and
511 " cmp %2,#0;\n" // if size is non-zero
519 CYGARC_HAL_EXEC_FIXUP()
520 " mov r0,#0;\n" // Set board type
521 " mov r1,%3;\n" // Machine type
522 " mov r2,%6;\n" // Kernel parameters
523 " mov pc,%0;\n" // Jump to kernel
527 "r"(CYGARC_PHYSICAL_ADDRESS(base_addr)),
529 "r"(CYGHWR_REDBOOT_ARM_MACHINE_TYPE),
531 "r"(CYGARC_PHYSICAL_ADDRESS(CYGHWR_REDBOOT_ARM_TRAMPOLINE_ADDRESS)),
532 "r"(CYGARC_PHYSICAL_ADDRESS(CYGHWR_REDBOOT_ARM_LINUX_TAGS_ADDRESS))
537 #endif // HAL_PLATFORM_MACHINE_TYPE - otherwise we do not support this stuff...
539 // EOF redboot_linux_exec.c