Mercurial > ecos
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
