]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - packages/hal/arm/arch/v2_0/src/redboot_linux_exec.c
ac7c7f1659206a7ca3f9fa6fd590bf81d754779b
[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 #ifndef CYGARC_HAL_EXEC_FIXUP
143 #define CYGARC_HAL_EXEC_FIXUP() ""
144 #endif
145 //
146 // Parameter info for Linux kernel
147 //       ** C A U T I O N **  This setup must match "asm-arm/setup.h"
148 //
149 // Info is passed at a fixed location, using a sequence of tagged
150 // data entries.
151 //
152
153 typedef unsigned long  u32;
154 typedef unsigned short u16;
155 typedef unsigned char  u8;
156
157 //=========================================================================
158 //      From Linux <asm-arm/setup.h>
159
160 #define ATAG_NONE       0x00000000
161 struct tag_header {
162         u32 size;        // Size of tag (hdr+data) in *longwords*
163         u32 tag;
164 };
165
166 #define ATAG_CORE       0x54410001
167 struct tag_core {
168         u32 flags;              /* bit 0 = read-only */
169         u32 pagesize;
170         u32 rootdev;
171 };
172
173 #define ATAG_MEM                0x54410002
174 struct tag_mem32 {
175         u32     size;
176         u32     start;
177 };
178
179 #define ATAG_VIDEOTEXT  0x54410003
180 struct tag_videotext {
181         u8       x;
182         u8       y;
183         u16      video_page;
184         u8       video_mode;
185         u8       video_cols;
186         u16      video_ega_bx;
187         u8       video_lines;
188         u8       video_isvga;
189         u16      video_points;
190 };
191
192 #define ATAG_RAMDISK    0x54410004
193 struct tag_ramdisk {
194         u32 flags;              /* b0 = load, b1 = prompt */
195         u32 size;
196         u32 start;
197 };
198
199 /*
200  * this one accidentally used virtual addresses - as such,
201  * its deprecated.
202  */
203 #define ATAG_INITRD     0x54410005
204
205 /* describes where the compressed ramdisk image lives (physical address) */
206 #define ATAG_INITRD2    0x54420005
207 struct tag_initrd {
208         u32 start;
209         u32 size;
210 };
211
212 #define ATAG_SERIAL     0x54410006
213 struct tag_serialnr {
214         u32 low;
215         u32 high;
216 };
217
218 #define ATAG_REVISION   0x54410007
219 struct tag_revision {
220         u32 rev;
221 };
222
223 #define ATAG_VIDEOLFB   0x54410008
224 struct tag_videolfb {
225         u16     lfb_width;
226         u16     lfb_height;
227         u16     lfb_depth;
228         u16     lfb_linelength;
229         u32     lfb_base;
230         u32     lfb_size;
231         u8      red_size;
232         u8      red_pos;
233         u8      green_size;
234         u8      green_pos;
235         u8      blue_size;
236         u8      blue_pos;
237         u8      rsvd_size;
238         u8      rsvd_pos;
239 };
240
241 #define ATAG_CMDLINE    0x54410009
242 struct tag_cmdline {
243         char cmdline[1];
244 };
245
246 #define ATAG_ACORN      0x41000101
247 struct tag_acorn {
248         u32 memc_control_reg;
249         u32 vram_pages;
250         u8 sounddefault;
251         u8 adfsdrives;
252 };
253
254 #define ATAG_MEMCLK     0x41000402
255 struct tag_memclk {
256         u32 fmemclk;
257 };
258
259 struct tag {
260         struct tag_header hdr;
261         union {
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;
271
272                 /*
273                  * Acorn specific
274                  */
275                 struct tag_acorn        acorn;
276
277                 /*
278                  * DC21285 specific
279                  */
280                 struct tag_memclk       memclk;
281         } u;
282 };
283
284 // End of inclusion from <asm-arm/setup.h>
285 //=========================================================================
286
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_)                                                                                                              \
291         CYG_MACRO_START                                                                                                                                                         \
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.                                                                      \
297          */                                                                                                                                                                                     \
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);                                                          \
302         CYG_MACRO_END
303 #endif
304
305
306 // Round up a quantity to a longword (32 bit) length
307 #define ROUNDUP(n) (((n) + 3) & ~3)
308
309 static void 
310 do_exec(int argc, char *argv[])
311 {
312         unsigned long entry;
313         unsigned long target;
314         unsigned long oldints;
315         bool wait_time_set;
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];
322         char line[8];
323         char *cmd_line;
324         struct tag *params = (struct tag *)CYGHWR_REDBOOT_ARM_LINUX_TAGS_ADDRESS;
325 #ifdef CYGHWR_REDBOOT_LINUX_EXEC_X_SWITCH
326         bool swap_endian;
327         extern char __xtramp_start__[], __xtramp_end__[];
328 #endif
329         extern char __tramp_start__[], __tramp_end__[];
330
331 #if 1
332         target = load_address;
333         entry = entry_address;
334 #else
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;
338 #endif
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;
343
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");
359         num_opts = 7;
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");
363         ++num_opts;
364 #endif
365         if (!scan_opts(argc, argv, 1, opts, num_opts, &entry,
366                                    OPTION_ARG_TYPE_NUM, "[physical] starting address")) {
367                 return;
368         }
369
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");
373                 return;
374         }
375
376         // Set up parameters to pass to kernel
377
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);
385
386         // Fill in the details of the memory layout
387         CYGHWR_REDBOOT_LINUX_ATAG_MEM(params);
388
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);
396         }
397         if (cmd_line_set) {
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);
402         }
403         // Mark end of parameter list
404         params->hdr.size = 0;
405         params->hdr.tag = ATAG_NONE;
406
407         if (wait_time_set) {
408                 int script_timeout_ms = wait_time * 1000;
409 #ifdef CYGFUN_REDBOOT_BOOT_SCRIPT
410                 unsigned char *hold_script = script;
411                 script = NULL;
412 #endif
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
420 #endif
421                                 return;
422                         }
423                         script_timeout_ms -= CYGNUM_REDBOOT_CLI_IDLE_TIMEOUT;
424                 }
425         }
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");
430                         return;
431                 }
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");
436                 return;
437         }
438
439 #ifdef CYGPKG_IO_ETH_DRIVERS
440         eth_drv_stop();
441 #endif
442
443         HAL_DISABLE_INTERRUPTS(oldints);
444         HAL_DCACHE_SYNC();
445         HAL_ICACHE_DISABLE();
446         HAL_DCACHE_DISABLE();
447         HAL_DCACHE_SYNC();
448         HAL_ICACHE_INVALIDATE_ALL();
449         HAL_DCACHE_INVALIDATE_ALL();
450
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.
455         // 
456         // This magic was created in order to be able to execute standard
457         // Linux kernels with as little change/perturberance as possible.
458
459 #ifdef CYGHWR_REDBOOT_LINUX_EXEC_X_SWITCH
460         if (swap_endian) {
461                 // copy the trampoline code
462                 memcpy((char *)CYGHWR_REDBOOT_ARM_TRAMPOLINE_ADDRESS,
463                    __xtramp_start__,
464                    __xtramp_end__ - __xtramp_start__);
465
466                 asm volatile (
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
472                                           " beq 2f;\n"
473                                           "1:\n"
474                                           " ldr r0,[%1],#4;\n"
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"
479                                           " str r0,[%4],#4;\n"
480                                           " subs %2,%2,#4;\n"
481                                           " bne 1b;\n"
482                                           "2:\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
487                                           "__xtramp_end__:\n"
488                                           : : 
489                                           "r"(entry),
490                                           "r"(CYGARC_PHYSICAL_ADDRESS(base_addr)),
491                                           "r"(length),
492                                           "r"(CYGHWR_REDBOOT_ARM_MACHINE_TYPE),
493                                           "r"(target),
494                                           "r"(CYGARC_PHYSICAL_ADDRESS(CYGHWR_REDBOOT_ARM_TRAMPOLINE_ADDRESS)),
495                                           "r"(CYGARC_PHYSICAL_ADDRESS(CYGHWR_REDBOOT_ARM_LINUX_TAGS_ADDRESS))
496                                           : "r0", "r1"
497                                           );
498         }
499 #endif // CYGHWR_REDBOOT_LINUX_EXEC_X_SWITCH
500
501         // copy the trampoline code
502         memcpy((char *)CYGHWR_REDBOOT_ARM_TRAMPOLINE_ADDRESS,
503                    __tramp_start__,
504                    __tramp_end__ - __tramp_start__);
505
506         asm volatile (
507                                   CYGARC_HAL_MMU_OFF(%5)
508                                   "__tramp_start__:\n"
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
512                                   " beq 2f;\n"
513                                   "1:\n"
514                                   " ldr r0,[%1],#4;\n"
515                                   " str r0,[%4],#4;\n"
516                                   " subs %2,%2,#4;\n"
517                                   " bne 1b;\n"
518                                   "2:\n"
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
524                                   "__tramp_end__:\n"
525                                   : : 
526                                   "r"(entry),
527                                   "r"(CYGARC_PHYSICAL_ADDRESS(base_addr)),
528                                   "r"(length),
529                                   "r"(CYGHWR_REDBOOT_ARM_MACHINE_TYPE),
530                                   "r"(target),
531                                   "r"(CYGARC_PHYSICAL_ADDRESS(CYGHWR_REDBOOT_ARM_TRAMPOLINE_ADDRESS)),
532                                   "r"(CYGARC_PHYSICAL_ADDRESS(CYGHWR_REDBOOT_ARM_LINUX_TAGS_ADDRESS))
533                                   : "r0", "r1"
534                                   );
535 }
536           
537 #endif // HAL_PLATFORM_MACHINE_TYPE - otherwise we do not support this stuff...
538
539 // EOF redboot_linux_exec.c