1 //=============================================================================
3 // redboot_linux_exec.c
5 // Boot linux from RedBoot
7 //=============================================================================
8 //####ECOSGPLCOPYRIGHTBEGIN####
9 // -------------------------------------------
10 // This file is part of eCos, the Embedded Configurable Operating System.
11 // Copyright (C) 2005 eCosCentric
13 // eCos is free software; you can redistribute it and/or modify it under
14 // the terms of the GNU General Public License as published by the Free
15 // Software Foundation; either version 2 or (at your option) any later version.
17 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
18 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 // You should have received a copy of the GNU General Public License along
23 // with eCos; if not, write to the Free Software Foundation, Inc.,
24 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
26 // As a special exception, if other files instantiate templates or use macros
27 // or inline functions from this file, or you compile this file and link it
28 // with other works to produce a work based on this file, this file does not
29 // by itself cause the resulting work to be covered by the GNU General Public
30 // License. However the source code for this file must still be made available
31 // in accordance with section (3) of the GNU General Public License.
33 // This exception does not invalidate any other reasons why a work based on
34 // this file might be covered by the GNU General Public License.
36 // -------------------------------------------
37 //####ECOSGPLCOPYRIGHTEND####
38 //=============================================================================
39 //#####DESCRIPTIONBEGIN####
41 // Author(s): Ian Campbell
44 // Purpose: Boot Linux from Redboot
47 //####DESCRIPTIONEND####
49 //=============================================================================
51 #include <pkgconf/hal.h>
55 #include <cyg/io/pci.h>
58 #ifdef CYGPKG_IO_ETH_DRIVERS
59 #include <cyg/io/eth/eth_drv.h>
62 #include <cyg/hal/hal_intr.h>
63 #include <cyg/hal/hal_cache.h>
64 #include CYGHWR_MEMORY_LAYOUT_H
66 #include <cyg/hal/hal_io.h>
69 * Code to launch a Linux image directly in protected mode.
71 * Jumps directly to the protected mode part of the kernel
74 typedef void (*trampoline_func)
75 (unsigned long base, unsigned long length, unsigned long entry);
77 // Defines for the linux loader
78 #define SETUP_SIZE_OFF 497
80 #define SETUP_VERSION 0x0201
81 #define SETUP_HIGH 0x01
82 #define BIG_SYSSEG 0x10000
83 #define DEF_BOOTLSEG 0x9020
85 // From etherboot, this is the header to the image startup code
86 // see Documentation/i386/boot.txt
87 /* Boot sector: bootsect.S */
89 struct bootsect_header {
90 cyg_uint8 pad0[0x1f1];
91 cyg_uint8 setup_sects;
92 cyg_uint16 root_flags; // If set, the root is mounted readonly
93 cyg_uint16 syssize; // DO NOT USE - for bootsect.S use only
94 cyg_uint16 swap_dev; // DO NOT USE - obsolete
95 cyg_uint16 ram_size; // DO NOT USE - for bootsect.S use only
96 cyg_uint16 vid_mode; // Video mode control
97 cyg_uint16 root_dev; // Default root device number
98 cyg_uint16 boot_flag; // 0xAA55 magic number
99 } __attribute__((packed));
103 struct setup_header {
105 cyg_uint8 magic[4]; // "HdrS"
106 cyg_uint16 version; // >= 0x0201 for initrd
107 cyg_uint8 realmode_swtch[4];
108 cyg_uint16 start_sys_seg;
109 cyg_uint16 kernel_version;
110 /* note: above part of header is compatible with loadlin-1.5
111 * (header v1.5), must not change it */
112 cyg_uint8 type_of_loader;
114 cyg_uint16 setup_move_size;
115 unsigned long code32_start;
116 unsigned long ramdisk_image;
117 unsigned long ramdisk_size;
118 unsigned long bootsect_kludge;
120 cyg_uint16 heap_end_ptr;
123 unsigned long cmd_line_ptr;
125 unsigned long initrd_addr_max;
126 } __attribute__((packed));
128 #define PARAM 0x90000
129 #define PARAM_ORIG_X *(cyg_uint8*) (PARAM+0x000)
130 #define PARAM_ORIG_Y *(cyg_uint8*) (PARAM+0x001)
131 #define PARAM_EXT_MEM_K *(cyg_uint16*)(PARAM+0x002)
132 #define PARAM_ORIG_VIDEO_PAGE *(cyg_uint16*)(PARAM+0x004)
133 #define PARAM_ORIG_VIDEO_MODE *(cyg_uint8*) (PARAM+0x006)
134 #define PARAM_ORIG_VIDEO_COLS *(cyg_uint8*) (PARAM+0x007)
135 #define PARAM_ORIG_VIDEO_EGA_BX *(cyg_uint16*)(PARAM+0x00a)
136 #define PARAM_ORIG_VIDEO_LINES *(cyg_uint8*) (PARAM+0x00E)
137 #define PARAM_ORIG_VIDEO_ISVGA *(cyg_uint8*) (PARAM+0x00F)
138 #define PARAM_ORIG_VIDEO_POINTS *(cyg_uint16*)(PARAM+0x010)
140 #define PARAM_ALT_MEM_K *(cyg_uint32*)(PARAM+0x1e0)
141 #define PARAM_E820NR *(cyg_uint8*) (PARAM+0x1e8)
142 #define PARAM_VID_MODE *(cyg_uint16*)(PARAM+0x1fa)
143 #define PARAM_E820MAP (struct e820entry*)(PARAM+0x2d0);
144 #define PARAM_CMDLINE (char *)(PARAM+0x3400)
147 do_exec(int argc, char **argv)
150 unsigned long oldints;
153 bool base_addr_set, length_set, cmd_line_set;
154 bool ramdisk_addr_set, ramdisk_size_set;
155 unsigned long base_addr, length;
156 unsigned long ramdisk_addr, ramdisk_size;
157 struct option_info opts[6];
161 cyg_uint32 int15_e801;
162 extern char __tramp_start__[], __tramp_end__[];
163 trampoline_func trampoline =
164 (trampoline_func)CYGHWR_REDBOOT_I386_TRAMPOLINE_ADDRESS;
165 struct bootsect_header *bs_header;
166 struct setup_header *s_header;
168 int xpos = 0, ypos = 0;
170 base_addr = load_address;
171 length = load_address_end - load_address;
172 // Round length up to the next quad word
173 length = (length + 3) & ~0x3;
175 ramdisk_size = 4096*1024;
176 init_opts(&opts[0], 'w', true, OPTION_ARG_TYPE_NUM,
177 &wait_time, &wait_time_set, "wait timeout");
178 init_opts(&opts[1], 'b', true, OPTION_ARG_TYPE_NUM,
179 &base_addr, &base_addr_set, "base address");
180 init_opts(&opts[2], 'l', true, OPTION_ARG_TYPE_NUM,
181 &length, &length_set, "length");
182 init_opts(&opts[3], 'c', true, OPTION_ARG_TYPE_STR,
183 &cmd_line, &cmd_line_set, "kernel command line");
184 init_opts(&opts[4], 'r', true, OPTION_ARG_TYPE_NUM,
185 &ramdisk_addr, &ramdisk_addr_set, "ramdisk_addr");
186 init_opts(&opts[5], 's', true, OPTION_ARG_TYPE_NUM,
187 &ramdisk_size, &ramdisk_size_set, "ramdisk_size");
188 if (!scan_opts(argc, argv, 1, opts, 6, 0, 0, "starting address"))
194 int script_timeout_ms = wait_time * 1000;
195 #ifdef CYGFUN_REDBOOT_BOOT_SCRIPT
196 unsigned char *hold_script = script;
197 script = (unsigned char *)0;
199 diag_printf("About to boot linux kernel at %p - "
200 "abort with ^C within %d seconds\n",
201 (void *)base_addr, wait_time);
202 while (script_timeout_ms >= CYGNUM_REDBOOT_CLI_IDLE_TIMEOUT) {
203 res = _rb_gets(line, sizeof(line),
204 CYGNUM_REDBOOT_CLI_IDLE_TIMEOUT);
205 if (res == _GETS_CTRLC) {
206 #ifdef CYGFUN_REDBOOT_BOOT_SCRIPT
207 script = hold_script; // Re-enable script
211 script_timeout_ms -= CYGNUM_REDBOOT_CLI_IDLE_TIMEOUT;
215 if (base_addr_set && !length_set) {
216 diag_printf("Length required for non-standard base address\n");
220 bs_header = (struct bootsect_header *)base_addr;
221 s_header = (struct setup_header *)(base_addr + SECTSIZE);
223 if (bs_header->boot_flag != 0xAA55) {
224 diag_printf("Bootsector magic not found (0x%04x @ %4p)\n",
225 bs_header->boot_flag, &bs_header->boot_flag);
228 if (memcmp(s_header->magic,"HdrS",4) != 0) {
229 diag_printf("Linux header (HdrS) not found\n");
232 if (s_header->version < SETUP_VERSION) {
233 diag_printf("Linux header version = 0x%04x. "
234 "Needs to be at least 0x%04x\n",
235 s_header->version, SETUP_VERSION);
239 setup_sects = bs_header->setup_sects ? bs_header->setup_sects : 4;
241 entry = s_header->code32_start;
242 // + 1 for boot sector
243 base_addr += (setup_sects + 1 ) * SECTSIZE;
244 length -= (setup_sects + 1 ) * SECTSIZE;
246 mem_size = (cyg_uint32)HAL_MEM_REAL_REGION_TOP((cyg_uint8 *)0x1000000);
247 mem_size >>= 10; // convert from bytes to kilobytes.
248 // Result of int15 ax=0xe801
249 int15_e801 = mem_size - 1024 ; // 1M+ only
251 // Stop all network devices
252 #ifdef CYGPKG_IO_ETH_DRIVERS
260 #if CYGINT_HAL_I386_PCMB_SCREEN_SUPPORT > 0
261 cyg_hal_plf_screen_position(&xpos, &ypos);
264 HAL_DISABLE_INTERRUPTS(oldints);
266 HAL_ICACHE_DISABLE();
267 HAL_DCACHE_DISABLE();
269 HAL_ICACHE_INVALIDATE_ALL();
270 HAL_DCACHE_INVALIDATE_ALL();
272 // Clear the data area
273 memset ( (void*)PARAM, 0, 512 );
276 strcpy( PARAM_CMDLINE, cmd_line );
278 strcpy( PARAM_CMDLINE, "auto");
280 memcpy((void*)(PARAM+SECTSIZE), s_header, sizeof(struct setup_header));
281 s_header = (struct setup_header*)(0x90000+SECTSIZE);
283 s_header->version = SETUP_VERSION;
286 s_header->cmd_line_ptr = 0x93400;
289 s_header->type_of_loader = 0xFF;
291 // Fill in the interesting bits of data area...
293 PARAM_EXT_MEM_K = int15_e801;
294 PARAM_ALT_MEM_K = int15_e801;
297 PARAM_E820NR = 0; // Length of map
302 PARAM_ORIG_VIDEO_MODE = 2;
303 PARAM_ORIG_VIDEO_COLS = 80;
304 PARAM_ORIG_VIDEO_LINES = 25;
305 PARAM_ORIG_VIDEO_ISVGA = 0;
307 // Copy trampoline to trampoline address
308 memcpy((char *)CYGHWR_REDBOOT_I386_TRAMPOLINE_ADDRESS,
310 __tramp_end__ - __tramp_start__);
312 trampoline(base_addr, length, entry);
314 #define _QUOTE_STRING(__x__) #__x__
315 #define QUOTE_STRING(__x__) _QUOTE_STRING(__x__)
320 " mov %%esp,%%ebp;\n"
326 * BASE ADDRESS 0x10(%ebp) */
328 " movl 0x10(%%ebp), %%ebx;\n" /* Save entry point
333 " cli;\n" /* no interrupts allowed ! */
335 " movb $0x80, %%al;\n" /* disable NMI for bootup */
336 " outb %%al, $0x70;\n" /* sequence */
338 /* Copy GDT to RAM at 0x90400 */
339 " movl $(linux_gdt_end - linux_gdt), %%ecx;\n" /* Length */
340 " shrl $2, %%ecx;\n" /* Bytes -> Longs */
341 " leal linux_gdt, %%eax;\n" /* Source */
342 " movl %%eax, %%esi;\n"
343 " movl $(0x90400), %%edi;\n" /* Dest */
349 /* If necessary, copy linux image to correct location */
350 " movl 0x8(%%ebp), %%esi;\n" /* Source */
351 " movl %%ebx, %%edi;\n" /* Destination
355 " cmpl %%edi, %%esi;\n"
357 " movl 0xC(%%ebp), %%ecx;\n" /* Length */
358 " shrl $2, %%ecx;\n" /* Bytes to Longs */
365 /* Create a GDT descriptor at 0 and load it */
366 " movl $0x90000, %%esi;\n"
367 " movw $(linux_gdt_end - linux_gdt), %%ax;\n"
370 " movl $0x90400,%%eax;\n"
374 /* Reload segment registers */
375 " mov $(0x18), %%eax;\n"
376 " movl %%eax, %%ds;\n"
377 " movl %%eax, %%es;\n"
378 " movl %%eax, %%fs;\n"
379 " movl %%eax, %%gs;\n"
382 " ljmp $(0x10), $(1f - __tramp_start__ + "
383 QUOTE_STRING(CYGHWR_REDBOOT_I386_TRAMPOLINE_ADDRESS) ");\n"
393 /* Descriptor tables */
395 " .word 0, 0, 0, 0;\n" /* dummy */
396 " .word 0, 0, 0, 0;\n" /* unused */
397 " .word 0xFFFF;\n" /* 4Gb - (0x100000*0x1000
399 " .word 0;\n" /* base address = 0 */
400 " .word 0x9A00;\n" /* code read/exec */
401 " .word 0x00CF;\n" /* granularity = 4096, 386 */
402 /* (+5th nibble of limit) */
403 " .word 0xFFFF;\n" /* 4Gb - (0x100000*0x1000 = 4Gb) */
404 " .word 0;\n" /* base address = 0 */
405 " .word 0x9200;\n" /* data read/write */
406 " .word 0x00CF;\n" /* granularity = 4096, 386 */
407 /* (+5th nibble of limit) */
409 : : : "eax", "ebx", "ecx");
413 "Execute a Linux image",
414 "[-w timeout] [-b <base address> [-l <image length>]]\n"
415 " [-r <ramdisk addr> [-s <ramdisk length>]]\n"
416 " [-c \"kernel command line\"]",