]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - packages/hal/arm/arch/v2_0/src/redboot_linux_exec.c
Initial revision
[karo-tx-redboot.git] / packages / hal / arm / arch / v2_0 / src / redboot_linux_exec.c
1 //==========================================================================
2 //
3 //      redboot_linux_boot.c
4 //
5 //      RedBoot command to boot Linux on ARM platforms
6 //
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
13 //
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.
17 //
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
21 // for more details.
22 //
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.
26 //
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.
33 //
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.
36 //
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####
42 //
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.
48 //
49 //####OTHERCOPYRIGHTEND####
50 //==========================================================================
51 //#####DESCRIPTIONBEGIN####
52 //
53 // Author(s):    gthomas
54 // Contributors: gthomas, jskov,
55 //               Russell King <rmk@arm.linux.org.uk>
56 // Date:         2001-02-20
57 // Purpose:      
58 // Description:  
59 //              
60 // This code is part of RedBoot (tm).
61 //
62 //####DESCRIPTIONEND####
63 //
64 //==========================================================================
65
66 #include <pkgconf/hal.h>
67 #include <redboot.h>
68
69 #ifdef CYGPKG_IO_ETH_DRIVERS
70 #include <cyg/io/eth/eth_drv.h>            // Logical driver interfaces
71 #endif
72
73 #include <cyg/hal/hal_intr.h>
74 #include <cyg/hal/hal_cache.h>
75 #include CYGHWR_MEMORY_LAYOUT_H
76
77 #include <cyg/hal/hal_io.h>
78
79 #ifndef CYGARC_PHYSICAL_ADDRESS
80 # error
81 # define CYGARC_PHYSICAL_ADDRESS(x) (x)
82 #endif
83
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
88
89 // Exported CLI function(s)
90 static void do_exec(int argc, char *argv[]);
91 RedBoot_cmd("exec", 
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>]",
96             do_exec
97     );
98
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
103
104 #define __CYGARC_GET_CTLREG \
105               "   mrc p15,0,r0,c1,c0,0\n"
106
107 #define __CYGARC_CLR_MMU_BITS         \
108   "   bic r0,r0,#0xd\n"               \
109   "   bic r0,r0,#0x1000\n"            \
110
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"
115 #else
116 #define __CYGARC_CLR_MMU_BITS_X       \
117   "   bic r0,r0,#0xd\n"               \
118   "   bic r0,r0,#0x1000\n"            \
119   "   orr r0,r0,#0x80\n"
120 #endif
121
122 #define __CYGARC_SET_CTLREG(__paddr__) \
123   "   mcr p15,0,r0,c1,c0,0\n"          \
124   "   mov pc," #__paddr__ "\n"
125
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__)
132
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__)
139
140 #endif  // CYGARC_HAL_MMU_OFF
141
142 //
143 // Parameter info for Linux kernel
144 //   ** C A U T I O N **  This setup must match "asm-arm/setup.h"
145 //
146 // Info is passed at a fixed location, using a sequence of tagged
147 // data entries.
148 //
149
150 typedef unsigned long  u32;
151 typedef unsigned short u16;
152 typedef unsigned char  u8;
153
154 //=========================================================================
155 //  From Linux <asm-arm/setup.h>
156
157 #define ATAG_NONE       0x00000000
158 struct tag_header {
159     u32 size;    // Size of tag (hdr+data) in *longwords*
160     u32 tag;
161 };
162
163 #define ATAG_CORE       0x54410001
164 struct tag_core {
165     u32 flags;          /* bit 0 = read-only */
166     u32 pagesize;
167     u32 rootdev;
168 };
169
170 #define ATAG_MEM                0x54410002
171 struct tag_mem32 {
172     u32 size;
173     u32 start;
174 };
175
176 #define ATAG_VIDEOTEXT  0x54410003
177 struct tag_videotext {
178     u8   x;
179     u8   y;
180     u16  video_page;
181     u8   video_mode;
182     u8   video_cols;
183     u16  video_ega_bx;
184     u8   video_lines;
185     u8   video_isvga;
186     u16  video_points;
187 };
188
189 #define ATAG_RAMDISK    0x54410004
190 struct tag_ramdisk {
191     u32 flags;          /* b0 = load, b1 = prompt */
192     u32 size;
193     u32 start;
194 };
195
196 /*
197  * this one accidentally used virtual addresses - as such,
198  * its deprecated.
199  */
200 #define ATAG_INITRD     0x54410005
201
202 /* describes where the compressed ramdisk image lives (physical address) */
203 #define ATAG_INITRD2    0x54420005
204 struct tag_initrd {
205     u32 start;
206     u32 size;
207 };
208
209 #define ATAG_SERIAL     0x54410006
210 struct tag_serialnr {
211     u32 low;
212     u32 high;
213 };
214
215 #define ATAG_REVISION   0x54410007
216 struct tag_revision {
217     u32 rev;
218 };
219
220 #define ATAG_VIDEOLFB   0x54410008
221 struct tag_videolfb {
222     u16 lfb_width;
223     u16 lfb_height;
224     u16 lfb_depth;
225     u16 lfb_linelength;
226     u32 lfb_base;
227     u32 lfb_size;
228     u8  red_size;
229     u8  red_pos;
230     u8  green_size;
231     u8  green_pos;
232     u8  blue_size;
233     u8  blue_pos;
234     u8  rsvd_size;
235     u8  rsvd_pos;
236 };
237
238 #define ATAG_CMDLINE    0x54410009
239 struct tag_cmdline {
240     char cmdline[1];
241 };
242
243 #define ATAG_ACORN      0x41000101
244 struct tag_acorn {
245     u32 memc_control_reg;
246     u32 vram_pages;
247     u8 sounddefault;
248     u8 adfsdrives;
249 };
250
251 #define ATAG_MEMCLK     0x41000402
252 struct tag_memclk {
253     u32 fmemclk;
254 };
255
256 struct tag {
257     struct tag_header hdr;
258     union {
259         struct tag_core         core;
260         struct tag_mem32        mem;
261         struct tag_videotext    videotext;
262         struct tag_ramdisk      ramdisk;
263         struct tag_initrd       initrd;
264         struct tag_serialnr     serialnr;
265         struct tag_revision     revision;
266         struct tag_videolfb     videolfb;
267         struct tag_cmdline      cmdline;
268
269         /*
270          * Acorn specific
271          */
272         struct tag_acorn        acorn;
273
274         /*
275          * DC21285 specific
276          */
277         struct tag_memclk       memclk;
278     } u;
279 };
280
281 // End of inclusion from <asm-arm/setup.h>
282 //=========================================================================
283
284 // Default memory layout - can be overridden by platform, typically in
285 // <cyg/hal/plf_io.h>
286 #ifndef CYGHWR_REDBOOT_LINUX_ATAG_MEM
287 #define CYGHWR_REDBOOT_LINUX_ATAG_MEM(_p_)                                                      \
288     CYG_MACRO_START                                                                             \
289     /* Next ATAG_MEM. */                                                                        \
290     _p_->hdr.size = (sizeof(struct tag_mem32) + sizeof(struct tag_header))/sizeof(long);        \
291     _p_->hdr.tag = ATAG_MEM;                                                                    \
292     /* Round up so there's only one bit set in the memory size.                                 \
293      * Don't double it if it's already a power of two, though.                                  \
294      */                                                                                         \
295     _p_->u.mem.size  = 1<<hal_msbindex(CYGMEM_REGION_ram_SIZE);                                 \
296     if (_p_->u.mem.size < CYGMEM_REGION_ram_SIZE)                                               \
297             _p_->u.mem.size <<= 1;                                                              \
298     _p_->u.mem.start = CYGARC_PHYSICAL_ADDRESS(CYGMEM_REGION_ram);                              \
299     CYG_MACRO_END
300 #endif
301
302
303 // Round up a quantity to a longword (32 bit) length
304 #define ROUNDUP(n) (((n)+3)&~3)
305
306 static void 
307 do_exec(int argc, char *argv[])
308 {
309     unsigned long entry;
310     unsigned long target;
311     unsigned long oldints;
312     bool wait_time_set;
313     int  wait_time, res, num_opts;
314     bool base_addr_set, length_set, cmd_line_set;
315     bool ramdisk_addr_set, ramdisk_size_set;
316     unsigned long base_addr, length;
317     unsigned long ramdisk_addr, ramdisk_size;
318     struct option_info opts[7];
319     char line[8];
320     char *cmd_line;
321     struct tag *params = (struct tag *)CYGHWR_REDBOOT_ARM_LINUX_TAGS_ADDRESS;
322 #ifdef CYGHWR_REDBOOT_LINUX_EXEC_X_SWITCH
323     bool swap_endian;
324     extern char __xtramp_start__[], __xtramp_end__[];
325 #endif
326     extern char __tramp_start__[], __tramp_end__[];
327
328     // Check to see if a valid image has been loaded
329     if (entry_address == (unsigned long)NO_MEMORY) {
330         diag_printf("Can't execute Linux - invalid entry address\n");
331         return;
332     }
333     // Default physical entry point for Linux is kernel base.
334     entry = (unsigned long)CYGHWR_REDBOOT_ARM_LINUX_EXEC_ADDRESS;
335     target = (unsigned long)CYGHWR_REDBOOT_ARM_LINUX_EXEC_ADDRESS;
336
337     base_addr = load_address;
338     length = load_address_end - load_address;
339     // Round length up to the next quad word
340     length = (length + 3) & ~0x3;
341
342     ramdisk_size = 4096*1024;
343     init_opts(&opts[0], 'w', true, OPTION_ARG_TYPE_NUM, 
344               &wait_time, (bool *)&wait_time_set, "wait timeout");
345     init_opts(&opts[1], 'b', true, OPTION_ARG_TYPE_NUM, 
346               &base_addr, (bool *)&base_addr_set, "base address");
347     init_opts(&opts[2], 'l', true, OPTION_ARG_TYPE_NUM, 
348               &length, (bool *)&length_set, "length");
349     init_opts(&opts[3], 'c', true, OPTION_ARG_TYPE_STR, 
350               &cmd_line, (bool *)&cmd_line_set, "kernel command line");
351     init_opts(&opts[4], 'r', true, OPTION_ARG_TYPE_NUM, 
352               &ramdisk_addr, (bool *)&ramdisk_addr_set, "ramdisk_addr");
353     init_opts(&opts[5], 's', true, OPTION_ARG_TYPE_NUM, 
354               &ramdisk_size, (bool *)&ramdisk_size_set, "ramdisk_size");
355     init_opts(&opts[6], 't', true, OPTION_ARG_TYPE_NUM,
356               &target, 0, "[physical] target address");
357     num_opts = 7;
358 #ifdef CYGHWR_REDBOOT_LINUX_EXEC_X_SWITCH
359     init_opts(&opts[num_opts], 'x', false, OPTION_ARG_TYPE_FLG, 
360               &swap_endian, 0, "swap endianess");
361     ++num_opts;
362 #endif
363     if (!scan_opts(argc, argv, 1, opts, num_opts, &entry, OPTION_ARG_TYPE_NUM, "[physical] starting address"))
364     {
365         return;
366     }
367
368     // Set up parameters to pass to kernel
369
370     // CORE tag must be present & first
371     params->hdr.size = (sizeof(struct tag_core) + sizeof(struct tag_header))/sizeof(long);
372     params->hdr.tag = ATAG_CORE;
373     params->u.core.flags = 0;
374     params->u.core.pagesize = 0;
375     params->u.core.rootdev = 0;
376     params = (struct tag *)((long *)params + params->hdr.size);
377
378     // Fill in the details of the memory layout
379     CYGHWR_REDBOOT_LINUX_ATAG_MEM(params);
380
381     params = (struct tag *)((long *)params + params->hdr.size);
382     if (ramdisk_addr_set) {
383         params->hdr.size = (sizeof(struct tag_initrd) + sizeof(struct tag_header))/sizeof(long);
384         params->hdr.tag = ATAG_INITRD2;
385         params->u.initrd.start = CYGARC_PHYSICAL_ADDRESS(ramdisk_addr);
386         params->u.initrd.size = ramdisk_size;
387         params = (struct tag *)((long *)params + params->hdr.size);
388     }
389     if (cmd_line_set) {
390         params->hdr.size = (ROUNDUP(strlen(cmd_line)) + sizeof(struct tag_header))/sizeof(long);
391         params->hdr.tag = ATAG_CMDLINE;
392         strcpy(params->u.cmdline.cmdline, cmd_line);
393         params = (struct tag *)((long *)params + params->hdr.size);
394     }
395     // Mark end of parameter list
396     params->hdr.size = 0;
397     params->hdr.tag = ATAG_NONE;
398
399     if (wait_time_set) {
400         int script_timeout_ms = wait_time * 1000;
401 #ifdef CYGFUN_REDBOOT_BOOT_SCRIPT
402         unsigned char *hold_script = script;
403         script = (unsigned char *)0;
404 #endif
405         diag_printf("About to start execution of image at %p, entry point %p - abort with ^C within %d seconds\n",
406                     (void *)target, (void *)entry, wait_time);
407         while (script_timeout_ms >= CYGNUM_REDBOOT_CLI_IDLE_TIMEOUT) {
408             res = _rb_gets(line, sizeof(line), CYGNUM_REDBOOT_CLI_IDLE_TIMEOUT);
409             if (res == _GETS_CTRLC) {
410 #ifdef CYGFUN_REDBOOT_BOOT_SCRIPT
411                 script = hold_script;  // Re-enable script
412 #endif
413                 return;
414             }
415             script_timeout_ms -= CYGNUM_REDBOOT_CLI_IDLE_TIMEOUT;
416         }
417     }
418     if (!base_addr_set) {
419         if ((base_addr == 0) || (length == 0)) {
420             // Probably not valid - don't try it
421             diag_printf("Base address unknown - use \"-b\" option\n");
422             return;
423         }
424         diag_printf("Using base address %p and length %p\n",
425                     (void*)base_addr, (void*)length);
426     } else if (base_addr_set && !length_set) {
427         diag_printf("Length required for non-standard base address\n");
428         return;
429     }
430
431 #ifdef CYGPKG_IO_ETH_DRIVERS
432     eth_drv_stop();
433 #endif
434
435     HAL_DISABLE_INTERRUPTS(oldints);
436     HAL_DCACHE_SYNC();
437     HAL_ICACHE_DISABLE();
438     HAL_DCACHE_DISABLE();
439     HAL_DCACHE_SYNC();
440     HAL_ICACHE_INVALIDATE_ALL();
441     HAL_DCACHE_INVALIDATE_ALL();
442
443     // Tricky code. We are currently running with the MMU on and the
444     // memory map possibly convoluted from 1-1.  The trampoline code
445     // between labels __tramp_start__ and __tramp_end__ must be copied
446     // to RAM and then executed at the non-mapped address.
447     // 
448     // This magic was created in order to be able to execute standard
449     // Linux kernels with as little change/perturberance as possible.
450
451 #ifdef CYGHWR_REDBOOT_LINUX_EXEC_X_SWITCH
452     if (swap_endian) {
453         // copy the trampline code
454         memcpy((char *)CYGHWR_REDBOOT_ARM_TRAMPOLINE_ADDRESS,
455                __xtramp_start__,
456                __xtramp_end__ - __xtramp_start__);
457
458         asm volatile (
459             CYGARC_HAL_MMU_OFF_X(%5)
460             "__xtramp_start__:\n"
461             " cmp %1,%4;\n"       // Default kernel load address. Relocate
462             " beq 2f;\n"          // kernel image there if necessary, and
463             " cmp %2,#0;\n"       // if size is non-zero
464             " beq 2f;\n"
465             "1:\n"
466             " ldr r0,[%1],#4;\n"
467             " eor %5, r0, r0, ror #16;\n"
468             " bic %5, %5, #0x00ff0000;\n"
469             " mov r0, r0, ror #8;\n"
470             " eor r0, r0, %5, lsr #8;\n"
471             " str r0,[%4],#4;\n"
472             " subs %2,%2,#4;\n"
473             " bne 1b;\n"
474             "2:\n"
475             " mov r0,#0;\n"       // Set board type
476             " mov r1,%3;\n"       // Machine type
477             " mov r2,%6;\n"       // Kernel parameters
478             " mov pc,%0;\n"       // Jump to kernel
479             "__xtramp_end__:\n"
480             : : 
481             "r"(entry),
482             "r"(CYGARC_PHYSICAL_ADDRESS(base_addr)),
483             "r"(length),
484             "r"(CYGHWR_REDBOOT_ARM_MACHINE_TYPE),
485             "r"(CYGHWR_REDBOOT_ARM_LINUX_EXEC_ADDRESS),
486             "r"(CYGARC_PHYSICAL_ADDRESS(CYGHWR_REDBOOT_ARM_TRAMPOLINE_ADDRESS)),
487             "r"(CYGARC_PHYSICAL_ADDRESS(CYGHWR_REDBOOT_ARM_LINUX_TAGS_ADDRESS))
488             : "r0", "r1"
489             );
490     }
491 #endif // CYGHWR_REDBOOT_LINUX_EXEC_X_SWITCH
492
493     // copy the trampline code
494     memcpy((char *)CYGHWR_REDBOOT_ARM_TRAMPOLINE_ADDRESS,
495            __tramp_start__,
496            __tramp_end__ - __tramp_start__);
497
498     asm volatile (
499         CYGARC_HAL_MMU_OFF(%5)
500         "__tramp_start__:\n"
501         " cmp %1,%4;\n"       // Default kernel load address. Relocate
502         " beq 2f;\n"          // kernel image there if necessary, and
503         " cmp %2,#0;\n"       // if size is non-zero
504         " beq 2f;\n"
505         "1:\n"
506         " ldr r0,[%1],#4;\n"
507         " str r0,[%4],#4;\n"
508         " subs %2,%2,#4;\n"
509         " bne 1b;\n"
510         "2:\n"
511         " mov r0,#0;\n"       // Set board type
512         " mov r1,%3;\n"       // Machine type
513         " mov r2,%6;\n"       // Kernel parameters
514         " mov pc,%0;\n"       // Jump to kernel
515         "__tramp_end__:\n"
516         : : 
517         "r"(entry),
518         "r"(CYGARC_PHYSICAL_ADDRESS(base_addr)),
519         "r"(length),
520         "r"(CYGHWR_REDBOOT_ARM_MACHINE_TYPE),
521         "r"(target),
522         "r"(CYGARC_PHYSICAL_ADDRESS(CYGHWR_REDBOOT_ARM_TRAMPOLINE_ADDRESS)),
523         "r"(CYGARC_PHYSICAL_ADDRESS(CYGHWR_REDBOOT_ARM_LINUX_TAGS_ADDRESS))
524         : "r0", "r1"
525         );
526 }
527       
528 #endif // HAL_PLATFORM_MACHINE_TYPE - otherwise we do not support this stuff...
529
530 // EOF redboot_linux_exec.c