This is the mail archive of the
ecos-patches@sources.redhat.com
mailing list for the eCos project.
redboot support for executing linux on i386
- From: Ian Campbell <icampbell at arcom dot com>
- To: eCos Patches <ecos-patches at sources dot redhat dot com>
- Date: Fri, 29 Oct 2004 15:16:37 +0100
- Subject: redboot support for executing linux on i386
- Organization: Arcom Control Systems
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
+ );
+