This is the mail archive of the ecos-patches@sources.redhat.com mailing list for the eCos project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

redboot support for executing linux on i386


Hi,

This patch was posted here an age ago but rejected since it was large
enough to require a copyright assignment. We have now got this all done
so I'm resubmitting now.

This patch adds support for a redboot command which can boot a Linux
bzImage on i386. The command is called 'linux' rather than the more
usual 'exec' since on i386 it is rather more Linux specific than exec is
on other platforms (ARM say). 

The commands works by setting up the Linux parameter region the same as
would normally be done by the Linux real-mode setup code (which relies
on a BIOS), setting up the MMU as for Linux (GDT etc) and then jumping
to the kernel's protected mode entry point. This method has been used to
boot several different kernels from 2.4.18 right up to the current BK
head code this morning.

There are a couple of ChangeLogs

packages/hal/i386/pcmb/current/ChangeLog:

2004-10-29  Ian Campbell  <icampbell@arcom.com>

        * include/pcmb_serial.h, src/pcmb_screen.c: define
        cyg_hal_plf_screen_position() which can be used to get the current
        screen offset. This will be used by the Linux exec code to tell
        Linux where to start printing from.

packages/hal/i386/arch/current/ChangeLog:

2004-10-29  Ian Campbell  <icampbell@arcom.com>

        * cdl/hal_i386.cdl, src/redboot_linux_exec..c: Add a new RedBoot
	command "linux" which executes a Linux zImage or bzImage already
	loaded into RAM.

packages/hal/i386/pc/current/ChangeLog:

2004-10-29  Ian Campbell  <icampbell@arcom.com>

	* cdl/hal_i386_pc.cdl: Define CYGHWR_REDBOOT_I386_TRAMPOLINE_ADDRESS
	to enable booting Linux from RedBoot.

Cheers,
Ian.

-- 
Ian Campbell, Senior Design Engineer
                                        Web: http://www.arcom.com
Arcom, Clifton Road, 			Direct: +44 (0)1223 403 465
Cambridge CB1 7EA, United Kingdom	Phone:  +44 (0)1223 411 200


_____________________________________________________________________
The message in this transmission is sent in confidence for the attention of the addressee only and should not be disclosed to any other party. Unauthorised recipients are requested to preserve this confidentiality. Please advise the sender if the addressee is not resident at the receiving end.  Email to and from Arcom is automatically monitored for operational and lawful business reasons.

This message has been virus scanned by MessageLabs.
Index: packages/hal/i386/pcmb/current/src/pcmb_screen.c
===================================================================
RCS file: /var/cvs/ecos/packages/hal/i386/pcmb/current/src/pcmb_screen.c,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -p -u -r1.1.1.1 -r1.2
--- packages/hal/i386/pcmb/current/src/pcmb_screen.c	29 May 2003 10:54:25 -0000	1.1.1.1
+++ packages/hal/i386/pcmb/current/src/pcmb_screen.c	20 Oct 2004 16:09:24 -0000	1.2
@@ -844,6 +844,12 @@ void cyg_hal_plf_screen_init(void)
     CYGACC_CALL_IF_SET_CONSOLE_COMM(cur);
 }
 
+void cyg_hal_plf_screen_position(int *x, int *y)
+{
+    *x = XPos;
+    *y = YPos;
+}
+
 #endif //  defined(CYGSEM_HAL_VIRTUAL_VECTOR_DIAG)
        // || defined(CYGPRI_HAL_IMPLEMENTS_IF_SERVICES)
 
Index: packages/hal/i386/pcmb/current/include/pcmb_serial.h
===================================================================
RCS file: /var/cvs/ecos/packages/hal/i386/pcmb/current/include/pcmb_serial.h,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -u -p -u -r1.1.1.2 -r1.2
--- packages/hal/i386/pcmb/current/include/pcmb_serial.h	29 Aug 2003 10:10:45 -0000	1.1.1.2
+++ packages/hal/i386/pcmb/current/include/pcmb_serial.h	20 Oct 2004 16:09:24 -0000	1.2
@@ -88,6 +88,7 @@ __externC void cyg_hal_plf_serial_init(v
 #if CYGINT_HAL_I386_PCMB_SCREEN_SUPPORT > 0
 
 __externC void cyg_hal_plf_screen_init(void);
+__externC void cyg_hal_plf_screen_position(int *x, int *y);
 
 #endif
 
Index: packages/hal/i386/pc/current/cdl/hal_i386_pc.cdl
===================================================================
RCS file: /var/cvs/ecos/packages/hal/i386/pc/current/cdl/hal_i386_pc.cdl,v
retrieving revision 1.1.1.3
diff -u -p -u -r1.1.1.3 hal_i386_pc.cdl
--- packages/hal/i386/pc/current/cdl/hal_i386_pc.cdl	26 Aug 2004 09:41:11 -0000	1.1.1.3
+++ packages/hal/i386/pc/current/cdl/hal_i386_pc.cdl	29 Oct 2004 14:13:28 -0000
@@ -74,6 +74,8 @@ cdl_package CYGPKG_HAL_I386_PC {
         puts $::cdl_header "#define HAL_PLATFORM_EXTRA  \"\""
 	puts $::cdl_header "#include <pkgconf/hal_i386_pcmb.h>"
         puts $::cdl_header ""
+        puts $::cdl_header "#define CYGHWR_REDBOOT_I386_TRAMPOLINE_ADDRESS 0x7C000"
+        puts $::cdl_header ""
     }
 
     cdl_component CYG_HAL_STARTUP {
Index: packages/hal/i386/arch/current/cdl/hal_i386.cdl
===================================================================
RCS file: /var/cvs/ecos/packages/hal/i386/arch/current/cdl/hal_i386.cdl,v
retrieving revision 1.1.1.2
retrieving revision 1.3
diff -u -p -u -r1.1.1.2 -r1.3
--- packages/hal/i386/arch/current/cdl/hal_i386.cdl	2 Jul 2004 14:35:34 -0000	1.1.1.2
+++ packages/hal/i386/arch/current/cdl/hal_i386.cdl	2 Jul 2004 15:00:47 -0000	1.3
@@ -152,6 +152,30 @@ cdl_package CYGPKG_HAL_I386 {
 	}
     }
     
+    cdl_component CYGPKG_REDBOOT_I386_OPTIONS {
+        display       "Redboot for I386 options"
+        flavor        none
+        no_define
+        parent        CYGPKG_REDBOOT
+        active_if     CYGPKG_REDBOOT
+        description   "
+            This option lists the target's requirements for a valid Redboot
+            configuration."
+                                                                                                                               
+        cdl_component CYGPKG_REDBOOT_I386_LINUX_EXEC {
+            display        "Provide the exec command in RedBoot"
+            flavor         none
+            parent         CYGPKG_REDBOOT_I386_OPTIONS
+            active_if      CYGBLD_BUILD_REDBOOT_WITH_EXEC
+            description    "
+                This option contains requirements for booting linux
+                from RedBoot. The component is enabled/disabled from
+                RedBoots CDL."
+            compile -library=libextras.a redboot_linux_exec.c 
+	}
+ 
+    }
+ 
     cdl_option CYGBLD_LINKER_SCRIPT {
         display "Linker script"
         flavor data
Index: packages/hal/i386/arch/current/src/redboot_linux_exec.c
===================================================================
RCS file: packages/hal/i386/arch/current/src/redboot_linux_exec.c
diff -N packages/hal/i386/arch/current/src/redboot_linux_exec.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ packages/hal/i386/arch/current/src/redboot_linux_exec.c	29 Oct 2004 14:13:28 -0000
@@ -0,0 +1,356 @@
+#include <pkgconf/hal.h>
+#include <redboot.h>
+
+#ifdef CYGPKG_IO_PCI
+#include <cyg/io/pci.h>
+#endif
+
+#ifdef CYGPKG_IO_ETH_DRIVERS
+#include <cyg/io/eth/eth_drv.h>
+#endif
+ 
+#include <cyg/hal/hal_intr.h>
+#include <cyg/hal/hal_cache.h>
+#include CYGHWR_MEMORY_LAYOUT_H
+
+#include <cyg/hal/hal_io.h>
+
+/* 
+ * Code to launch a Linux image directly in protected mode.
+ *
+ * Jumps directly to the protected mode part of the kernel
+ */
+ 
+typedef void (*trampoline_func)(unsigned long base, unsigned long length, unsigned long entry);
+
+// Defines for the linux loader
+#define SETUP_SIZE_OFF 	497
+#define SECTSIZE       	512
+#define SETUP_VERSION  	0x0201
+#define SETUP_HIGH     	0x01
+#define BIG_SYSSEG     	0x10000
+#define DEF_BOOTLSEG	0x9020
+
+// From etherboot, this is the header to the image startup code
+// see Documentation/i386/boot.txt
+    /* Boot sector: bootsect.S */
+    /* VERSION: ALL */
+struct bootsect_header {
+    cyg_uint8		pad0[0x1f1];
+    cyg_uint8		setup_sects;
+    cyg_uint16		root_flags;	// If set, the root is mounted readonly
+    cyg_uint16		syssize;	// DO NOT USE - for bootsect.S use only
+    cyg_uint16		swap_dev;	// DO NOT USE - obsolete
+    cyg_uint16		ram_size;	// DO NOT USE - for bootsect.S use only
+    cyg_uint16		vid_mode;	// Video mode control
+    cyg_uint16		root_dev;	// Default root device number
+    cyg_uint16		boot_flag;	// 0xAA55 magic number
+} __attribute__((packed));
+
+    /* setup.S */
+    /* VERSION: 2.00+ */
+struct setup_header {
+    cyg_uint8 		jump[2];
+    cyg_uint8 		magic[4];     	// "HdrS"
+    cyg_uint16 		version;      	// >= 0x0201 for initrd
+    cyg_uint8 		realmode_swtch[4];
+    cyg_uint16 		start_sys_seg;
+    cyg_uint16 		kernel_version;
+    /* note: above part of header is compatible with loadlin-1.5 (header v1.5),*/
+    /*       must not change it */
+    cyg_uint8 		type_of_loader;
+    cyg_uint8 		loadflags;
+    cyg_uint16 		setup_move_size;
+    unsigned long 	code32_start;
+    unsigned long 	ramdisk_image;
+    unsigned long 	ramdisk_size;
+    unsigned long 	bootsect_kludge;
+    /* VERSION: 2.01+ */
+    cyg_uint16 		heap_end_ptr;
+    cyg_uint16 		pad1;
+    /* VERSION: 2.02+ */
+    unsigned long 	cmd_line_ptr;
+    /* VERSION: 2.03+ */
+    unsigned long 	initrd_addr_max;
+} __attribute__((packed));
+
+#define PARAM			0x90000
+#define PARAM_ORIG_X		*(cyg_uint8*) (PARAM+0x000)
+#define PARAM_ORIG_Y		*(cyg_uint8*) (PARAM+0x001)
+#define PARAM_EXT_MEM_K		*(cyg_uint16*)(PARAM+0x002)
+#define PARAM_ORIG_VIDEO_PAGE	*(cyg_uint16*)(PARAM+0x004)
+#define PARAM_ORIG_VIDEO_MODE	*(cyg_uint8*) (PARAM+0x006)
+#define PARAM_ORIG_VIDEO_COLS	*(cyg_uint8*) (PARAM+0x007)
+#define PARAM_ORIG_VIDEO_EGA_BX	*(cyg_uint16*)(PARAM+0x00a)
+#define PARAM_ORIG_VIDEO_LINES	*(cyg_uint8*) (PARAM+0x00E)
+#define PARAM_ORIG_VIDEO_ISVGA	*(cyg_uint8*) (PARAM+0x00F)
+#define PARAM_ORIG_VIDEO_POINTS	*(cyg_uint16*)(PARAM+0x010)
+
+#define PARAM_ALT_MEM_K		*(cyg_uint32*)(PARAM+0x1e0)
+#define PARAM_E820NR		*(cyg_uint8*) (PARAM+0x1e8)
+#define PARAM_VID_MODE		*(cyg_uint16*)(PARAM+0x1fa)
+#define PARAM_E820MAP		(struct e820entry*)(PARAM+0x2d0);
+#define PARAM_CMDLINE		(char *)(PARAM+0x3400)
+void
+do_linux(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 *cmd_line;
+    char line[8];
+    cyg_uint32 mem_size;
+    cyg_uint32 int15_e801;
+    extern char __tramp_start__[], __tramp_end__[];
+    trampoline_func trampoline = (trampoline_func)CYGHWR_REDBOOT_I386_TRAMPOLINE_ADDRESS;
+    struct bootsect_header *bs_header;
+    struct setup_header *s_header;
+    int setup_sects;
+    int xpos = 0, ypos = 0;
+
+    base_addr = load_address;
+    length = load_address_end - load_address;
+    // Round length up to the next quad word
+    length = (length + 3) & ~0x3;
+
+    ramdisk_size = 4096*1024;
+    init_opts(&opts[0], 'w', true, OPTION_ARG_TYPE_NUM,
+              &wait_time, &wait_time_set, "wait timeout");
+    init_opts(&opts[1], 'b', true, OPTION_ARG_TYPE_NUM,
+              &base_addr, &base_addr_set, "base address");
+    init_opts(&opts[2], 'l', true, OPTION_ARG_TYPE_NUM,
+              &length, &length_set, "length");
+    init_opts(&opts[3], 'c', true, OPTION_ARG_TYPE_STR,
+              &cmd_line, &cmd_line_set, "kernel command line");
+    init_opts(&opts[4], 'r', true, OPTION_ARG_TYPE_NUM,
+              &ramdisk_addr, &ramdisk_addr_set, "ramdisk_addr");
+    init_opts(&opts[5], 's', true, OPTION_ARG_TYPE_NUM,
+              &ramdisk_size, &ramdisk_size_set, "ramdisk_size");
+    if (!scan_opts(argc, argv, 1, opts, 6, 0, 0, "starting address"))
+    {
+        return;
+    }
+
+    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 boot linux kernel at %p - abort with ^C within %d seconds\n",
+                    (void *)base_addr, 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 && !length_set) {
+        diag_printf("Length required for non-standard base address\n");
+        return;
+    }
+
+    bs_header = (struct bootsect_header *)base_addr;
+    s_header = (struct setup_header *)(base_addr + SECTSIZE);
+
+    if (bs_header->boot_flag != 0xAA55) {
+        diag_printf("Bootsector magic not found (0x%04x @ 0x%04x)\n", bs_header->boot_flag, &bs_header->boot_flag);
+        return;
+    }
+    if (memcmp(s_header->magic,"HdrS",4) != 0) {
+        diag_printf("Linux header (HdrS) not found\n");
+        return;
+    }
+    if (s_header->version < SETUP_VERSION) {
+        diag_printf("Linux header version = 0x%04x. Needs to be at least 0x%04x\n",
+                    s_header->version, SETUP_VERSION);
+        return;
+    }
+
+    setup_sects = bs_header->setup_sects ? bs_header->setup_sects : 4;
+
+    entry = s_header->code32_start;
+    // + 1 for boot sector
+    base_addr += (setup_sects + 1 ) * SECTSIZE;
+    length -= (setup_sects + 1 ) * SECTSIZE;
+
+    mem_size = HAL_MEM_REAL_REGION_TOP(0x1000000);
+    mem_size >>= 10;   // convert from bytes to kilobytes.
+    // Result of int15 ax=0xe801
+    int15_e801 = mem_size - 1024 ; // 1M+ only
+
+    // Stop all network devices
+#ifdef CYGPKG_IO_ETH_DRIVERS
+    eth_drv_stop();
+#endif
+
+#ifdef CYGPKG_IO_PCI
+    cyg_pci_init();
+#endif
+
+#if CYGINT_HAL_I386_PCMB_SCREEN_SUPPORT > 0
+    cyg_hal_plf_screen_position(&xpos, &ypos);
+#endif
+
+    HAL_DISABLE_INTERRUPTS(oldints);
+    HAL_DCACHE_SYNC();
+    HAL_ICACHE_DISABLE();
+    HAL_DCACHE_DISABLE();
+    HAL_DCACHE_SYNC();
+    HAL_ICACHE_INVALIDATE_ALL();
+    HAL_DCACHE_INVALIDATE_ALL();
+
+    // Clear the data area
+    memset ( (void*)PARAM, 0, 512 );
+
+    if ( cmd_line_set )
+        strcpy( PARAM_CMDLINE, cmd_line );
+    else
+        strcpy( PARAM_CMDLINE, "auto");
+
+    memcpy((void*)(PARAM+SECTSIZE), s_header, sizeof(struct setup_header));
+    s_header = (struct setup_header*)(0x90000+SECTSIZE);
+
+    s_header->version = SETUP_VERSION;
+
+    // Command Line
+    s_header->cmd_line_ptr = 0x93400;
+ 
+    // Loader type
+    s_header->type_of_loader = 0xFF;
+
+    // Fill in the interesting bits of data area...
+    // ... Memory sizes
+    PARAM_EXT_MEM_K = int15_e801;
+    PARAM_ALT_MEM_K = int15_e801;
+
+    // ... No e820 map!
+    PARAM_E820NR = 0; 	// Length of map
+
+    // ... Video stuff
+    PARAM_ORIG_X = xpos;
+    PARAM_ORIG_Y = ypos;
+    PARAM_ORIG_VIDEO_MODE = 2;
+    PARAM_ORIG_VIDEO_COLS = 80;
+    PARAM_ORIG_VIDEO_LINES = 25;
+    PARAM_ORIG_VIDEO_ISVGA = 0;
+
+    // Copy trampoline to trampoline address
+    memcpy((char *)CYGHWR_REDBOOT_I386_TRAMPOLINE_ADDRESS,
+           __tramp_start__,
+           __tramp_end__ - __tramp_start__);
+
+    trampoline(base_addr, length, entry);
+
+#define _QUOTE_STRING(__x__) 	#__x__
+#define QUOTE_STRING(__x__)	_QUOTE_STRING(__x__)
+
+    asm volatile (
+        "__tramp_start__:\n"
+	"	push   %%ebp;\n"
+	"	mov    %%esp,%%ebp;\n"
+
+	/* STACK IS:
+         * OLD BP		0x4(%ebp)
+         * ENTRY		0x8(%ebp)
+         * LENGTH		0xC(%ebp)
+         * BASE ADDRESS		0x10(%ebp) */
+        
+	"	movl	0x10(%%ebp), %%ebx;\n"	/* Save entry point in EBX, because we overwrite the stack */
+
+	"	cli;\n"				/* no interrupts allowed ! */
+	
+	"	movb	$0x80, %%al;\n"		/* disable NMI for bootup */
+	"	outb	%%al, $0x70;\n"		/* sequence */
+
+	/* Copy GDT to RAM at 0x90400 */
+	"	movl	$(linux_gdt_end - linux_gdt), %%ecx;\n"	/* Length */
+	"	shrl	$2, %%ecx;\n"			/* Bytes -> Longs */
+	"	leal	linux_gdt, %%eax;\n"		/* Source */
+	"	movl	%%eax, %%esi;\n"
+	"	movl	$(0x90400), %%edi;\n"		/* Dest */
+	"1:\n"
+	"	lodsl;\n"
+	"	stosl;\n"
+	"	loop	1b;\n"
+
+	/* If necessary, copy linux image to correct location */
+	"	movl	0x8(%%ebp), %%esi;\n"		/* Source */
+	"	movl	%%ebx, %%edi;\n"		/* Destination (saved in EBX above) */
+	"	cmpl	%%edi, %%esi;\n"
+	"	je	2f;\n"
+	"	movl 	0xC(%%ebp), %%ecx;\n"		/* Length */
+	"	shrl	$2, %%ecx;\n"			/* Bytes to Longs */
+	"1:\n"
+	"	lodsl;\n"
+	"	stosl;\n"
+	"	loop	1b;\n"
+	"2:\n"
+	
+	/* Create a GDT descriptor at 0 and load it */
+	"	movl	$0x90000, %%esi;\n"
+	"	movw	$(linux_gdt_end - linux_gdt), %%ax;\n"
+        "	dec     %%ax;\n"
+	"	movw	%%ax,0;\n"
+	"	movl	$0x90400,%%eax;\n"
+	"	movl	%%eax,2;\n"
+	"	lgdt	0;\n"
+	
+	/* Reload segment registers */
+	"	mov	$(0x18), %%eax;\n"
+	"	movl 	%%eax, %%ds;\n"
+	"	movl 	%%eax, %%es;\n"
+	"	movl 	%%eax, %%fs;\n"
+	"	movl 	%%eax, %%gs;\n"
+	
+        /* Reload CS */
+        "	ljmp	$(0x10), $(1f - __tramp_start__ + " QUOTE_STRING(CYGHWR_REDBOOT_I386_TRAMPOLINE_ADDRESS) ");\n"
+	"1:\n"
+
+	/* Start kernel */
+        "	jmp	*%%ebx;\n"
+
+	".ALIGN 4, 0xCC;\n"
+
+	"__tramp_end__:\n"
+
+	/* Descriptor tables */
+	"linux_gdt:\n"
+	"	.word	0, 0, 0, 0;\n"			/* dummy */
+	"	.word	0, 0, 0, 0;\n"			/* unused */
+
+	"	.word	0xFFFF;\n"			/* 4Gb - (0x100000*0x1000 = 4Gb) */
+	"	.word	0;\n"				/* base address = 0 */
+	"	.word	0x9A00;\n"			/* code read/exec */
+	"	.word	0x00CF;\n"			/* granularity = 4096, 386 */
+							/*  (+5th nibble of limit) */
+
+	"	.word	0xFFFF;\n"			/* 4Gb - (0x100000*0x1000 = 4Gb) */
+	"	.word	0;\n"				/* base address = 0 */
+	"	.word	0x9200;\n"			/* data read/write */
+	"	.word	0x00CF;\n"			/* granularity = 4096, 386 */
+							/*  (+5th nibble of limit) */
+	"linux_gdt_end:\n"
+        : : : "eax", "ebx", "ecx");
+}
+
+RedBoot_cmd("linux",
+	    "Execute a Linux image",
+	    "[-w timeout] [-b <base address> [-l <image length>]]\n"
+	    "        [-r <ramdisk addr> [-s <ramdisk length>]]\n"
+            "        [-c \"kernel command line\"]",
+	    do_linux
+    );
+

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]