Mercurial > ecos
view 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 source
//========================================================================== // // 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
