diff packages/hal/arm/arch/current/src/redboot_linux_exec.c @ 208:e0c0827131d1 ecos

Merge from eCos master repository on 2002-05-20-20:11:54-BST
author jlarmour
date Mon, 20 May 2002 22:19:26 +0000
parents
children d2c90368aeef
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/packages/hal/arm/arch/current/src/redboot_linux_exec.c
@@ -0,0 +1,411 @@
+//==========================================================================
+//
+//      redboot_linux_boot.c
+//
+//      RedBoot command to boot Linux on ARM platforms
+//
+//==========================================================================
+//####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
+//
+// eCos is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 2 or (at your option) any later version.
+//
+// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with eCos; if not, write to the Free Software Foundation, Inc.,
+// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+//
+// As a special exception, if other files instantiate templates or use macros
+// or inline functions from this file, or you compile this file and link it
+// with other works to produce a work based on this file, this file does not
+// by itself cause the resulting work to be covered by the GNU General Public
+// License. However the source code for this file must still be made available
+// in accordance with section (3) of the GNU General Public License.
+//
+// This exception does not invalidate any other reasons why a work based on
+// this file might be covered by the GNU General Public License.
+//
+// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
+// at http://sources.redhat.com/ecos/ecos-license
+// -------------------------------------------
+//####ECOSGPLCOPYRIGHTEND####
+//####OTHERCOPYRIGHTBEGIN####
+//
+//  The structure definitions below are taken from include/asm-arm/setup.h in
+//  the Linux kernel, Copyright (C) 1997-1999 Russell King. Their presence
+//  here is for the express purpose of communication with the Linux kernel
+//  being booted and is considered 'fair use' by the original author and
+//  are included with his permission.
+//
+//####OTHERCOPYRIGHTEND####
+//==========================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// Author(s):    gthomas
+// Contributors: gthomas, jskov,
+//               Russell King <rmk@arm.linux.org.uk>
+// Date:         2001-02-20
+// Purpose:      
+// Description:  
+//              
+// This code is part of RedBoot (tm).
+//
+//####DESCRIPTIONEND####
+//
+//==========================================================================
+
+#include <pkgconf/hal.h>
+#include <redboot.h>
+
+#include <cyg/hal/hal_intr.h>
+#include <cyg/hal/hal_cache.h>
+#include CYGHWR_MEMORY_LAYOUT_H
+
+#include <cyg/hal/hal_io.h>
+
+#ifndef CYGARC_PHYSICAL_ADDRESS
+# error
+# define CYGARC_PHYSICAL_ADDRESS(x) (x)
+#endif
+
+// FIXME: This should be a CDL variable, and CYGSEM_REDBOOT_ARM_LINUX_BOOT
+//        active_if  CYGHWR_HAL_ARM_REDBOOT_MACHINE_TYPE>0
+#ifdef HAL_PLATFORM_MACHINE_TYPE
+#define CYGHWR_REDBOOT_ARM_MACHINE_TYPE HAL_PLATFORM_MACHINE_TYPE
+
+// Exported CLI function(s)
+static void do_exec(int argc, char *argv[]);
+RedBoot_cmd("exec", 
+            "Execute an image - with MMU off", 
+            "[-w timeout] [-b <load addr> [-l <length>]]\n"
+            "        [-r <ramdisk addr> [-s <ramdisk length>]]\n"
+            "        [-c \"kernel command line\"] [<entry_point>]",
+            do_exec
+    );
+
+//
+// Parameter info for Linux kernel
+//   ** C A U T I O N **  This setup must match "asm-arm/setup.h"
+//
+// Info is passed at a fixed location, using a sequence of tagged
+// data entries.
+//
+
+typedef unsigned long  u32;
+typedef unsigned short u16;
+typedef unsigned char  u8;
+
+//=========================================================================
+//  From Linux <asm-arm/setup.h>
+
+#define ATAG_NONE	0x00000000
+struct tag_header {
+    u32 size;    // Size of tag (hdr+data) in *longwords*
+    u32 tag;
+};
+
+#define ATAG_CORE	0x54410001
+struct tag_core {
+    u32 flags;		/* bit 0 = read-only */
+    u32 pagesize;
+    u32 rootdev;
+};
+
+#define ATAG_MEM		0x54410002
+struct tag_mem32 {
+    u32	size;
+    u32	start;
+};
+
+#define ATAG_VIDEOTEXT	0x54410003
+struct tag_videotext {
+    u8	 x;
+    u8	 y;
+    u16	 video_page;
+    u8	 video_mode;
+    u8	 video_cols;
+    u16	 video_ega_bx;
+    u8	 video_lines;
+    u8	 video_isvga;
+    u16	 video_points;
+};
+
+#define ATAG_RAMDISK	0x54410004
+struct tag_ramdisk {
+    u32 flags;		/* b0 = load, b1 = prompt */
+    u32 size;
+    u32 start;
+};
+
+#define ATAG_INITRD	0x54410005
+struct tag_initrd {
+    u32 start;
+    u32 size;
+};
+
+#define ATAG_SERIAL	0x54410006
+struct tag_serialnr {
+    u32 low;
+    u32 high;
+};
+
+#define ATAG_REVISION	0x54410007
+struct tag_revision {
+    u32 rev;
+};
+
+#define ATAG_VIDEOLFB	0x54410008
+struct tag_videolfb {
+    u16	lfb_width;
+    u16	lfb_height;
+    u16	lfb_depth;
+    u16	lfb_linelength;
+    u32	lfb_base;
+    u32	lfb_size;
+    u8	red_size;
+    u8	red_pos;
+    u8	green_size;
+    u8	green_pos;
+    u8	blue_size;
+    u8	blue_pos;
+    u8	rsvd_size;
+    u8	rsvd_pos;
+};
+
+#define ATAG_CMDLINE	0x54410009
+struct tag_cmdline {
+    char cmdline[1];
+};
+
+#define ATAG_ACORN	0x41000101
+struct tag_acorn {
+    u32 memc_control_reg;
+    u32 vram_pages;
+    u8 sounddefault;
+    u8 adfsdrives;
+};
+
+#define ATAG_MEMCLK	0x41000402
+struct tag_memclk {
+    u32 fmemclk;
+};
+
+struct tag {
+    struct tag_header hdr;
+    union {
+        struct tag_core		core;
+        struct tag_mem32	mem;
+        struct tag_videotext	videotext;
+        struct tag_ramdisk	ramdisk;
+        struct tag_initrd	initrd;
+        struct tag_serialnr	serialnr;
+        struct tag_revision	revision;
+        struct tag_videolfb	videolfb;
+        struct tag_cmdline	cmdline;
+
+        /*
+         * Acorn specific
+         */
+        struct tag_acorn	acorn;
+
+        /*
+         * DC21285 specific
+         */
+        struct tag_memclk	memclk;
+    } u;
+};
+
+// End of inclusion from <asm-arm/setup.h>
+//=========================================================================
+
+// Round up a quantity to a longword (32 bit) length
+#define ROUNDUP(n) (((n)+3)&~3)
+
+static void 
+do_exec(int argc, char *argv[])
+{
+    unsigned long entry;
+    unsigned long oldints;
+    bool wait_time_set;
+    int  wait_time, res;
+    bool base_addr_set, length_set, cmd_line_set;
+    bool ramdisk_addr_set, ramdisk_size_set;
+    unsigned long base_addr, length;
+    unsigned long ramdisk_addr, ramdisk_size;
+    struct option_info opts[6];
+    char line[8];
+    char *cmd_line;
+    struct tag *params = (struct tag *)0x0100;
+
+    entry = (unsigned long)CYGARC_PHYSICAL_ADDRESS(CYGHWR_REDBOOT_ARM_LINUX_EXEC_ADDRESS);  // Default Linux execute address
+    base_addr = load_address;
+    length = load_address_end - load_address;
+    ramdisk_size = 4096*1024;
+    init_opts(&opts[0], 'w', true, OPTION_ARG_TYPE_NUM, 
+              (void **)&wait_time, (bool *)&wait_time_set, "wait timeout");
+    init_opts(&opts[1], 'b', true, OPTION_ARG_TYPE_NUM, 
+              (void **)&base_addr, (bool *)&base_addr_set, "base address");
+    init_opts(&opts[2], 'l', true, OPTION_ARG_TYPE_NUM, 
+              (void **)&length, (bool *)&length_set, "length");
+    init_opts(&opts[3], 'c', true, OPTION_ARG_TYPE_STR, 
+              (void **)&cmd_line, (bool *)&cmd_line_set, "kernel command line");
+    init_opts(&opts[4], 'r', true, OPTION_ARG_TYPE_NUM, 
+              (void **)&ramdisk_addr, (bool *)&ramdisk_addr_set, "ramdisk_addr");
+    init_opts(&opts[5], 's', true, OPTION_ARG_TYPE_NUM, 
+              (void **)&ramdisk_size, (bool *)&ramdisk_size_set, "ramdisk_size");
+    if (!scan_opts(argc, argv, 1, opts, 6, (void *)&entry, OPTION_ARG_TYPE_NUM, "[physical] starting address"))
+    {
+        return;
+    }
+
+    // Set up parameters to pass to kernel
+
+    // CORE tag must be present & first
+    params->hdr.size = (sizeof(struct tag_core) + sizeof(struct tag_header))/sizeof(long);
+    params->hdr.tag = ATAG_CORE;
+    params->u.core.flags = 0;
+    params->u.core.pagesize = 0;
+    params->u.core.rootdev = 0;
+    params = (struct tag *)((long *)params + params->hdr.size);
+    
+    /* Next ATAG_MEM. */
+    params->hdr.size = (sizeof(struct tag_mem32) + sizeof(struct tag_header))/sizeof(long);
+    params->hdr.tag = ATAG_MEM;
+    params->u.mem.start = CYGARC_PHYSICAL_ADDRESS(CYGMEM_REGION_ram);
+    /* Round up so there's only one bit set in the memory size.
+     * Don't double it if it's already a power of two, though.
+     */
+    params->u.mem.size  = 1<<hal_msbindex(CYGMEM_REGION_ram_SIZE);
+    if (params->u.mem.size < CYGMEM_REGION_ram_SIZE << 1)
+	    params->u.mem.size <<= 1;
+    params = (struct tag *)((long *)params + params->hdr.size);
+
+    if (ramdisk_addr_set) {
+        params->hdr.size = (sizeof(struct tag_initrd) + sizeof(struct tag_header))/sizeof(long);
+        params->hdr.tag = ATAG_INITRD;
+        params->u.initrd.start = CYGARC_PHYSICAL_ADDRESS(ramdisk_addr);
+        params->u.initrd.size = ramdisk_size;
+        params = (struct tag *)((long *)params + params->hdr.size);
+    }
+    if (cmd_line_set) {
+        params->hdr.size = (ROUNDUP(strlen(cmd_line)) + sizeof(struct tag_header))/sizeof(long);
+        params->hdr.tag = ATAG_CMDLINE;
+        strcpy(params->u.cmdline.cmdline, cmd_line);
+        params = (struct tag *)((long *)params + params->hdr.size);
+    }
+    // Mark end of parameter list
+    params->hdr.size = 0;
+    params->hdr.tag = ATAG_NONE;
+
+    if (wait_time_set) {
+        int script_timeout_ms = wait_time * 1000;
+#ifdef CYGFUN_REDBOOT_BOOT_SCRIPT
+        unsigned char *hold_script = script;
+        script = (unsigned char *)0;
+#endif
+        diag_printf("About to start execution at %p - abort with ^C within %d seconds\n",
+                    (void *)entry, wait_time);
+        while (script_timeout_ms >= CYGNUM_REDBOOT_CLI_IDLE_TIMEOUT) {
+            res = _rb_gets(line, sizeof(line), CYGNUM_REDBOOT_CLI_IDLE_TIMEOUT);
+            if (res == _GETS_CTRLC) {
+#ifdef CYGFUN_REDBOOT_BOOT_SCRIPT
+                script = hold_script;  // Re-enable script
+#endif
+                return;
+            }
+            script_timeout_ms -= CYGNUM_REDBOOT_CLI_IDLE_TIMEOUT;
+        }
+    }
+    if (!base_addr_set) {
+        diag_printf("Using base address %p and length %p\n",
+                    (void*)base_addr, (void*)length);
+    } else if (base_addr_set && !length_set) {
+        diag_printf("Length required for non-standard base address\n");
+        return;
+    }
+    HAL_DISABLE_INTERRUPTS(oldints);
+    HAL_DCACHE_SYNC();
+    HAL_ICACHE_DISABLE();
+    HAL_DCACHE_DISABLE();
+    HAL_DCACHE_SYNC();
+    HAL_ICACHE_INVALIDATE_ALL();
+    HAL_DCACHE_INVALIDATE_ALL();
+
+    // Tricky code.  We are currently running with the MMU on and the
+    // memory map convoluted from 1-1.  The trampoline code between
+    // labels 1 and 2 must be copied to RAM and then executed at the
+    // non-mapped address.  Then when the code turns off the MMU, the
+    // control flow is unchanged and control can be passed safely to
+    // the program to be executed.
+    // Note: This only works if phys=virt (or there are appropriate
+    //       double mapping of the memory). This should be rewritten
+    //       so a jump follows immediately after the MMU disable
+    //       operation.
+    // 
+    // This magic was created in order to
+    // be able to execute standard Linux kernels with as little
+    // change/perturberance as possible.
+    asm volatile ("\n"                  // Copy code between labels 1-2 to the
+                  " ldr r2,=1f;\n"      // trampoline address...
+                  " ldr r3,=2f;\n"
+                  " mov r5,%0;\n"
+                  "4:\n"
+                  " ldr r1,[r2],#4;\n"
+                  " str r1,[r5],#4;\n"
+                  " cmp r2,r3;\n"
+                  " bne 4b;\n"
+                  " nop;nop;nop;nop;nop;nop;nop;nop;\n"
+                  " mov r5,%1;\n"
+		  " mov r2,%2;\n"
+		  " mov r3,%3;\n"
+                  " mov r7,%4;\n"
+		  " mov r6,%5;\n"
+		  " mov pc,%0;\n"       //... and jump to it.
+                  " \n"
+                  "1:\n"
+                  " mrs r1,cpsr;\n"     // Put processor in IRQ mode
+                  " bic r0,r1,#0x1F;\n"
+                  " orr r0,r0,#0x12;\n"
+                  " msr cpsr,r0;\n"
+                  " msr spsr,r1;\n"
+                  " mov lr,r5;\n"       // Set kernel entry point
+                  " mov sp,r5;\n"       // Give the kernel a stack just below the entry point
+                  " mov r1,#0;\n"       // Turn off MMU and caches
+                  " mcr p15,0,r1,c1,c0;\n"
+                  " nop;nop;nop;\n"
+                  " cmp r2,r6;\n"       // Default kernel load address. Relocate
+                  " beq 10f;\n"         // kernel image there if necessary, and
+		  " cmp r3,#0;\n"       // if size is non-zero
+		  " beq 10f;\n"
+		  "05:\n"
+		  " ldr r4,[r2],#4;\n"
+		  " str r4,[r6],#4;\n"
+		  " sub r3,r3,#4;\n"
+		  " cmp r3,#0;\n"
+		  " bne 05b;\n"
+		  "10:\n"
+                  " mov r0,#0;\n"        // Set board type and start the kernel
+                  " mov r1,r7;\n" 
+                  " movs pc,lr;\n"
+                  "2:\n"
+                  : : 
+                  "r"(CYGARC_PHYSICAL_ADDRESS(CYGHWR_REDBOOT_ARM_TRAMPOLINE_ADDRESS)),
+		  "r"(entry),
+                  "r"(CYGARC_PHYSICAL_ADDRESS(base_addr)),
+                  "r"(length),
+                  "r"(CYGHWR_REDBOOT_ARM_MACHINE_TYPE),
+                  "r"(CYGHWR_REDBOOT_ARM_LINUX_EXEC_ADDRESS) : 
+                  "r7","r6","r5","r2","r3","r1");
+}
+
+#endif // HAL_PLATFORM_MACHINE_TYPE - otherwise we do not support this stuff...
+
+// EOF redboot_linux_exec.c