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>
67 #include <cyg/hal/pcmb_serial.h>
70 * Code to launch a Linux image directly in protected mode.
72 * Jumps directly to the protected mode part of the kernel
75 typedef void (*trampoline_func)
76 (unsigned long base, unsigned long length, unsigned long entry);
78 // Defines for the linux loader
79 #define SETUP_SIZE_OFF 497
81 #define SETUP_VERSION 0x0201
82 #define SETUP_HIGH 0x01
83 #define BIG_SYSSEG 0x10000
84 #define DEF_BOOTLSEG 0x9020
86 // From etherboot, this is the header to the image startup code
87 // see Documentation/i386/boot.txt
88 /* Boot sector: bootsect.S */
90 struct bootsect_header {
91 cyg_uint8 pad0[0x1f1];
92 cyg_uint8 setup_sects;
93 cyg_uint16 root_flags; // If set, the root is mounted readonly
94 cyg_uint16 syssize; // DO NOT USE - for bootsect.S use only
95 cyg_uint16 swap_dev; // DO NOT USE - obsolete
96 cyg_uint16 ram_size; // DO NOT USE - for bootsect.S use only
97 cyg_uint16 vid_mode; // Video mode control
98 cyg_uint16 root_dev; // Default root device number
99 cyg_uint16 boot_flag; // 0xAA55 magic number
100 } __attribute__((packed));
104 struct setup_header {
106 cyg_uint8 magic[4]; // "HdrS"
107 cyg_uint16 version; // >= 0x0201 for initrd
108 cyg_uint8 realmode_swtch[4];
109 cyg_uint16 start_sys_seg;
110 cyg_uint16 kernel_version;
111 /* note: above part of header is compatible with loadlin-1.5
112 * (header v1.5), must not change it */
113 cyg_uint8 type_of_loader;
115 cyg_uint16 setup_move_size;
116 unsigned long code32_start;
117 unsigned long ramdisk_image;
118 unsigned long ramdisk_size;
119 unsigned long bootsect_kludge;
121 cyg_uint16 heap_end_ptr;
124 unsigned long cmd_line_ptr;
126 unsigned long initrd_addr_max;
127 } __attribute__((packed));
129 #define PARAM 0x90000
130 #define PARAM_ORIG_X *(cyg_uint8*) (PARAM+0x000)
131 #define PARAM_ORIG_Y *(cyg_uint8*) (PARAM+0x001)
132 #define PARAM_EXT_MEM_K *(cyg_uint16*)(PARAM+0x002)
133 #define PARAM_ORIG_VIDEO_PAGE *(cyg_uint16*)(PARAM+0x004)
134 #define PARAM_ORIG_VIDEO_MODE *(cyg_uint8*) (PARAM+0x006)
135 #define PARAM_ORIG_VIDEO_COLS *(cyg_uint8*) (PARAM+0x007)
136 #define PARAM_ORIG_VIDEO_EGA_BX *(cyg_uint16*)(PARAM+0x00a)
137 #define PARAM_ORIG_VIDEO_LINES *(cyg_uint8*) (PARAM+0x00E)
138 #define PARAM_ORIG_VIDEO_ISVGA *(cyg_uint8*) (PARAM+0x00F)
139 #define PARAM_ORIG_VIDEO_POINTS *(cyg_uint16*)(PARAM+0x010)
141 #define PARAM_ALT_MEM_K *(cyg_uint32*)(PARAM+0x1e0)
142 #define PARAM_E820NR *(cyg_uint8*) (PARAM+0x1e8)
143 #define PARAM_VID_MODE *(cyg_uint16*)(PARAM+0x1fa)
144 #define PARAM_E820MAP (struct e820entry*)(PARAM+0x2d0);
145 #define PARAM_CMDLINE (char *)(PARAM+0x3400)
148 do_exec(int argc, char **argv)
151 unsigned long oldints;
154 bool base_addr_set, length_set, cmd_line_set;
155 bool ramdisk_addr_set, ramdisk_size_set;
156 unsigned long base_addr, length;
157 unsigned long ramdisk_addr, ramdisk_size;
158 struct option_info opts[6];
162 cyg_uint32 int15_e801;
163 extern char __tramp_start__[], __tramp_end__[];
164 trampoline_func trampoline =
165 (trampoline_func)CYGHWR_REDBOOT_I386_TRAMPOLINE_ADDRESS;
166 struct bootsect_header *bs_header;
167 struct setup_header *s_header;
169 int xpos = 0, ypos = 0;
171 base_addr = load_address;
172 length = load_address_end - load_address;
173 // Round length up to the next quad word
174 length = (length + 3) & ~0x3;
176 ramdisk_size = 4096*1024;
177 init_opts(&opts[0], 'w', true, OPTION_ARG_TYPE_NUM,
178 &wait_time, &wait_time_set, "wait timeout");
179 init_opts(&opts[1], 'b', true, OPTION_ARG_TYPE_NUM,
180 &base_addr, &base_addr_set, "base address");
181 init_opts(&opts[2], 'l', true, OPTION_ARG_TYPE_NUM,
182 &length, &length_set, "length");
183 init_opts(&opts[3], 'c', true, OPTION_ARG_TYPE_STR,
184 &cmd_line, &cmd_line_set, "kernel command line");
185 init_opts(&opts[4], 'r', true, OPTION_ARG_TYPE_NUM,
186 &ramdisk_addr, &ramdisk_addr_set, "ramdisk_addr");
187 init_opts(&opts[5], 's', true, OPTION_ARG_TYPE_NUM,
188 &ramdisk_size, &ramdisk_size_set, "ramdisk_size");
189 if (!scan_opts(argc, argv, 1, opts, 6, 0, 0, "starting address"))
195 int script_timeout_ms = wait_time * 1000;
196 #ifdef CYGFUN_REDBOOT_BOOT_SCRIPT
197 unsigned char *hold_script = script;
198 script = (unsigned char *)0;
200 diag_printf("About to boot linux kernel at %p - "
201 "abort with ^C within %d seconds\n",
202 (void *)base_addr, wait_time);
203 while (script_timeout_ms >= CYGNUM_REDBOOT_CLI_IDLE_TIMEOUT) {
204 res = _rb_gets(line, sizeof(line),
205 CYGNUM_REDBOOT_CLI_IDLE_TIMEOUT);
206 if (res == _GETS_CTRLC) {
207 #ifdef CYGFUN_REDBOOT_BOOT_SCRIPT
208 script = hold_script; // Re-enable script
212 script_timeout_ms -= CYGNUM_REDBOOT_CLI_IDLE_TIMEOUT;
216 if (base_addr_set && !length_set) {
217 diag_printf("Length required for non-standard base address\n");
221 bs_header = (struct bootsect_header *)base_addr;
222 s_header = (struct setup_header *)(base_addr + SECTSIZE);
224 if (bs_header->boot_flag != 0xAA55) {
225 diag_printf("Bootsector magic not found (0x%04x @ %4p)\n",
226 bs_header->boot_flag, &bs_header->boot_flag);
229 if (memcmp(s_header->magic,"HdrS",4) != 0) {
230 diag_printf("Linux header (HdrS) not found\n");
233 if (s_header->version < SETUP_VERSION) {
234 diag_printf("Linux header version = 0x%04x. "
235 "Needs to be at least 0x%04x\n",
236 s_header->version, SETUP_VERSION);
240 setup_sects = bs_header->setup_sects ? bs_header->setup_sects : 4;
242 entry = s_header->code32_start;
243 // + 1 for boot sector
244 base_addr += (setup_sects + 1 ) * SECTSIZE;
245 length -= (setup_sects + 1 ) * SECTSIZE;
247 mem_size = (cyg_uint32)HAL_MEM_REAL_REGION_TOP((cyg_uint8 *)0x1000000);
248 mem_size >>= 10; // convert from bytes to kilobytes.
249 // Result of int15 ax=0xe801
250 int15_e801 = mem_size - 1024 ; // 1M+ only
252 // Stop all network devices
253 #ifdef CYGPKG_IO_ETH_DRIVERS
261 #if CYGINT_HAL_I386_PCMB_SCREEN_SUPPORT > 0
262 cyg_hal_plf_screen_position(&xpos, &ypos);
265 HAL_DISABLE_INTERRUPTS(oldints);
267 HAL_ICACHE_DISABLE();
268 HAL_DCACHE_DISABLE();
270 HAL_ICACHE_INVALIDATE_ALL();
271 HAL_DCACHE_INVALIDATE_ALL();
273 // Clear the data area
274 memset ( (void*)PARAM, 0, 512 );
277 strcpy( PARAM_CMDLINE, cmd_line );
279 strcpy( PARAM_CMDLINE, "auto");
281 memcpy((void*)(PARAM+SECTSIZE), s_header, sizeof(struct setup_header));
282 s_header = (struct setup_header*)(0x90000+SECTSIZE);
284 s_header->version = SETUP_VERSION;
287 s_header->cmd_line_ptr = 0x93400;
290 s_header->type_of_loader = 0xFF;
292 // Fill in the interesting bits of data area...
294 PARAM_EXT_MEM_K = int15_e801;
295 PARAM_ALT_MEM_K = int15_e801;
298 PARAM_E820NR = 0; // Length of map
303 PARAM_ORIG_VIDEO_MODE = 2;
304 PARAM_ORIG_VIDEO_COLS = 80;
305 PARAM_ORIG_VIDEO_LINES = 25;
306 PARAM_ORIG_VIDEO_ISVGA = 0;
308 // Copy trampoline to trampoline address
309 memcpy((char *)CYGHWR_REDBOOT_I386_TRAMPOLINE_ADDRESS,
311 __tramp_end__ - __tramp_start__);
313 trampoline(base_addr, length, entry);
315 #define _QUOTE_STRING(__x__) #__x__
316 #define QUOTE_STRING(__x__) _QUOTE_STRING(__x__)
321 " mov %%esp,%%ebp;\n"
327 * BASE ADDRESS 0x10(%ebp) */
329 " movl 0x10(%%ebp), %%ebx;\n" /* Save entry point
334 " cli;\n" /* no interrupts allowed ! */
336 " movb $0x80, %%al;\n" /* disable NMI for bootup */
337 " outb %%al, $0x70;\n" /* sequence */
339 /* Copy GDT to RAM at 0x90400 */
340 " movl $(linux_gdt_end - linux_gdt), %%ecx;\n" /* Length */
341 " shrl $2, %%ecx;\n" /* Bytes -> Longs */
342 " leal linux_gdt, %%eax;\n" /* Source */
343 " movl %%eax, %%esi;\n"
344 " movl $(0x90400), %%edi;\n" /* Dest */
350 /* If necessary, copy linux image to correct location */
351 " movl 0x8(%%ebp), %%esi;\n" /* Source */
352 " movl %%ebx, %%edi;\n" /* Destination
356 " cmpl %%edi, %%esi;\n"
358 " movl 0xC(%%ebp), %%ecx;\n" /* Length */
359 " shrl $2, %%ecx;\n" /* Bytes to Longs */
366 /* Create a GDT descriptor at 0 and load it */
367 " movl $0x90000, %%esi;\n"
368 " movw $(linux_gdt_end - linux_gdt), %%ax;\n"
371 " movl $0x90400,%%eax;\n"
375 /* Reload segment registers */
376 " mov $(0x18), %%eax;\n"
377 " movl %%eax, %%ds;\n"
378 " movl %%eax, %%es;\n"
379 " movl %%eax, %%fs;\n"
380 " movl %%eax, %%gs;\n"
383 " ljmp $(0x10), $(1f - __tramp_start__ + "
384 QUOTE_STRING(CYGHWR_REDBOOT_I386_TRAMPOLINE_ADDRESS) ");\n"
394 /* Descriptor tables */
396 " .word 0, 0, 0, 0;\n" /* dummy */
397 " .word 0, 0, 0, 0;\n" /* unused */
398 " .word 0xFFFF;\n" /* 4Gb - (0x100000*0x1000
400 " .word 0;\n" /* base address = 0 */
401 " .word 0x9A00;\n" /* code read/exec */
402 " .word 0x00CF;\n" /* granularity = 4096, 386 */
403 /* (+5th nibble of limit) */
404 " .word 0xFFFF;\n" /* 4Gb - (0x100000*0x1000 = 4Gb) */
405 " .word 0;\n" /* base address = 0 */
406 " .word 0x9200;\n" /* data read/write */
407 " .word 0x00CF;\n" /* granularity = 4096, 386 */
408 /* (+5th nibble of limit) */
410 : : : "eax", "ebx", "ecx");
414 "Execute a Linux image",
415 "[-w timeout] [-b <base address> [-l <image length>]]\n"
416 " [-r <ramdisk addr> [-s <ramdisk length>]]\n"
417 " [-c \"kernel command line\"]",