3 * Supports bzImage, zImage and Image format.
5 * Based on work by Steve Gehlbach.
6 * Portions are taken from mkelfImage.
8 * 2003-09 by SONE Takeshi
12 #include "kernel/kernel.h"
13 #include "libopenbios/bindings.h"
14 #include "libopenbios/sys_info.h"
21 #define strtoull_with_suffix strtol
23 #define LINUX_PARAM_LOC 0x90000
24 #define COMMAND_LINE_LOC 0x91000
25 #define GDT_LOC 0x92000
26 #define STACK_LOC 0x93000
28 #define E820MAX 32 /* number of entries in E820MAP */
30 unsigned long long addr; /* start of memory segment */
31 unsigned long long size; /* size of memory segment */
32 unsigned long type; /* type of memory segment */
34 #define E820_RESERVED 2
35 #define E820_ACPI 3 /* usable as RAM once ACPI tables have been read */
39 /* The header of Linux/i386 kernel */
41 uint8_t reserved1[0x1f1]; /* 0x000 */
42 uint8_t setup_sects; /* 0x1f1 */
43 uint16_t root_flags; /* 0x1f2 */
44 uint8_t reserved2[6]; /* 0x1f4 */
45 uint16_t vid_mode; /* 0x1fa */
46 uint16_t root_dev; /* 0x1fc */
47 uint16_t boot_sector_magic; /* 0x1fe */
49 uint8_t reserved3[2]; /* 0x200 */
50 uint8_t header_magic[4]; /* 0x202 */
51 uint16_t protocol_version; /* 0x206 */
52 uint32_t realmode_swtch; /* 0x208 */
53 uint16_t start_sys; /* 0x20c */
54 uint16_t kver_addr; /* 0x20e */
55 uint8_t type_of_loader; /* 0x210 */
56 uint8_t loadflags; /* 0x211 */
57 uint16_t setup_move_size; /* 0x212 */
58 uint32_t code32_start; /* 0x214 */
59 uint32_t ramdisk_image; /* 0x218 */
60 uint32_t ramdisk_size; /* 0x21c */
61 uint8_t reserved4[4]; /* 0x220 */
63 uint16_t heap_end_ptr; /* 0x224 */
64 uint8_t reserved5[2]; /* 0x226 */
66 uint32_t cmd_line_ptr; /* 0x228 */
68 uint32_t initrd_addr_max; /* 0x22c */
69 } __attribute__ ((packed));
72 /* Paramters passed to 32-bit part of Linux
73 * This is another view of the structure above.. */
75 uint8_t orig_x; /* 0x00 */
76 uint8_t orig_y; /* 0x01 */
77 uint16_t ext_mem_k; /* 0x02 -- EXT_MEM_K sits here */
78 uint16_t orig_video_page; /* 0x04 */
79 uint8_t orig_video_mode; /* 0x06 */
80 uint8_t orig_video_cols; /* 0x07 */
81 uint16_t unused2; /* 0x08 */
82 uint16_t orig_video_ega_bx; /* 0x0a */
83 uint16_t unused3; /* 0x0c */
84 uint8_t orig_video_lines; /* 0x0e */
85 uint8_t orig_video_isVGA; /* 0x0f */
86 uint16_t orig_video_points; /* 0x10 */
88 /* VESA graphic mode -- linear frame buffer */
89 uint16_t lfb_width; /* 0x12 */
90 uint16_t lfb_height; /* 0x14 */
91 uint16_t lfb_depth; /* 0x16 */
92 uint32_t lfb_base; /* 0x18 */
93 uint32_t lfb_size; /* 0x1c */
94 uint16_t cl_magic; /* 0x20 */
95 #define CL_MAGIC_VALUE 0xA33F
96 uint16_t cl_offset; /* 0x22 */
97 uint16_t lfb_linelength; /* 0x24 */
98 uint8_t red_size; /* 0x26 */
99 uint8_t red_pos; /* 0x27 */
100 uint8_t green_size; /* 0x28 */
101 uint8_t green_pos; /* 0x29 */
102 uint8_t blue_size; /* 0x2a */
103 uint8_t blue_pos; /* 0x2b */
104 uint8_t rsvd_size; /* 0x2c */
105 uint8_t rsvd_pos; /* 0x2d */
106 uint16_t vesapm_seg; /* 0x2e */
107 uint16_t vesapm_off; /* 0x30 */
108 uint16_t pages; /* 0x32 */
109 uint8_t reserved4[12]; /* 0x34 -- 0x3f reserved for future expansion */
111 //struct apm_bios_info apm_bios_info; /* 0x40 */
112 uint8_t apm_bios_info[0x40];
113 //struct drive_info_struct drive_info; /* 0x80 */
114 uint8_t drive_info[0x20];
115 //struct sys_desc_table sys_desc_table; /* 0xa0 */
116 uint8_t sys_desc_table[0x140];
117 uint32_t alt_mem_k; /* 0x1e0 */
118 uint8_t reserved5[4]; /* 0x1e4 */
119 uint8_t e820_map_nr; /* 0x1e8 */
120 uint8_t reserved6[9]; /* 0x1e9 */
121 uint16_t mount_root_rdonly; /* 0x1f2 */
122 uint8_t reserved7[4]; /* 0x1f4 */
123 uint16_t ramdisk_flags; /* 0x1f8 */
124 #define RAMDISK_IMAGE_START_MASK 0x07FF
125 #define RAMDISK_PROMPT_FLAG 0x8000
126 #define RAMDISK_LOAD_FLAG 0x4000
127 uint8_t reserved8[2]; /* 0x1fa */
128 uint16_t orig_root_dev; /* 0x1fc */
129 uint8_t reserved9[1]; /* 0x1fe */
130 uint8_t aux_device_info; /* 0x1ff */
131 uint8_t reserved10[2]; /* 0x200 */
132 uint8_t param_block_signature[4]; /* 0x202 */
133 uint16_t param_block_version; /* 0x206 */
134 uint8_t reserved11[8]; /* 0x208 */
135 uint8_t loader_type; /* 0x210 */
136 #define LOADER_TYPE_LOADLIN 1
137 #define LOADER_TYPE_BOOTSECT_LOADER 2
138 #define LOADER_TYPE_SYSLINUX 3
139 #define LOADER_TYPE_ETHERBOOT 4
140 #define LOADER_TYPE_KERNEL 5
141 uint8_t loader_flags; /* 0x211 */
142 uint8_t reserved12[2]; /* 0x212 */
143 uint32_t kernel_start; /* 0x214 */
144 uint32_t initrd_start; /* 0x218 */
145 uint32_t initrd_size; /* 0x21c */
146 uint8_t reserved12_5[8]; /* 0x220 */
147 uint32_t cmd_line_ptr; /* 0x228 */
148 uint8_t reserved13[164]; /* 0x22c */
149 struct e820entry e820_map[E820MAX]; /* 0x2d0 */
150 uint8_t reserved16[688]; /* 0x550 */
151 #define COMMAND_LINE_SIZE 256
152 /* Command line is copied here by 32-bit i386/kernel/head.S.
153 * So I will follow the boot protocol, rather than putting it
154 * directly here. --ts1 */
155 uint8_t command_line[COMMAND_LINE_SIZE]; /* 0x800 */
156 uint8_t reserved17[1792]; /* 0x900 - 0x1000 */
159 uint64_t forced_memsize;
161 /* Load the first part the file and check if it's Linux */
162 static uint32_t load_linux_header(struct linux_header *hdr)
167 if (lfile_read(hdr, sizeof *hdr) != sizeof *hdr) {
168 debug("Can't read Linux header\n");
171 if (hdr->boot_sector_magic != 0xaa55) {
172 debug("Not a Linux kernel image\n");
176 /* Linux is found. Print some information */
177 if (memcmp(hdr->header_magic, "HdrS", 4) != 0) {
178 /* This may be floppy disk image or something.
179 * Perform a simple (incomplete) sanity check. */
180 if (hdr->setup_sects >= 16
181 || file_size() - (hdr->setup_sects<<9) >= 512<<10) {
182 debug("This looks like a bootdisk image but not like Linux...\n");
186 printf("Possible very old Linux");
187 /* This kernel does not even have a protocol version.
188 * Force the value. */
189 hdr->protocol_version = 0; /* pre-2.00 */
191 printf("Found Linux");
192 if (hdr->protocol_version >= 0x200 && hdr->kver_addr) {
194 file_seek(hdr->kver_addr + 0x200);
195 if (lfile_read(kver, sizeof kver) != 0) {
197 printf(" version %s", kver);
200 debug(" (protocol %#x)", hdr->protocol_version);
202 if (hdr->protocol_version >= 0x200) {
203 debug(" (loadflags %#x)", hdr->loadflags);
204 load_high = hdr->loadflags & 1;
208 kern_addr = 0x100000;
210 printf(" zImage or Image");
218 /* Set up parameters for 32-bit kernel */
220 init_linux_params(struct linux_params *params, struct linux_header *hdr)
222 debug("Setting up paramters at %#lx\n", virt_to_phys(params));
223 memset(params, 0, sizeof *params);
225 /* Copy some useful values from header */
226 params->mount_root_rdonly = hdr->root_flags;
227 params->orig_root_dev = hdr->root_dev;
230 * This assumes we have VGA in standard 80x25 text mode,
231 * just like our vga.c does.
232 * Cursor position is filled later to allow some more printf's. */
233 params->orig_video_mode = 3;
234 params->orig_video_cols = 80;
235 params->orig_video_lines = 25;
236 params->orig_video_isVGA = 1;
237 params->orig_video_points = 16;
239 params->loader_type = 0xff; /* Unregistered Linux loader */
244 set_memory_size(struct linux_params *params, struct sys_info *info)
249 struct e820entry *linux_map;
250 struct memrange *filo_map;
252 linux_map = params->e820_map;
253 filo_map = info->memrange;
254 for (i = 0; i < info->n_memranges; i++, linux_map++, filo_map++) {
256 /* Convert to BIOS e820 style */
257 linux_map->addr = filo_map->base;
258 linux_map->size = filo_map->size;
259 linux_map->type = E820_RAM;
260 debug("%016Lx - %016Lx\n", linux_map->addr,
261 linux_map->addr + linux_map->size);
262 params->e820_map_nr = i+1;
265 /* Find out top of RAM. XXX This ignores hole above 1MB */
266 end = filo_map->base + filo_map->size;
267 if (end < (1ULL << 32)) { /* don't count memory above 4GB */
269 ramtop = (uint32_t) end;
272 debug("ramtop=%#x\n", ramtop);
273 /* Size of memory above 1MB in KB */
274 params->alt_mem_k = (ramtop - (1<<20)) >> 10;
275 /* old style, 64MB max */
276 if (ramtop >= (64<<20))
277 params->ext_mem_k = (63<<10);
279 params->ext_mem_k = params->alt_mem_k;
280 debug("ext_mem_k=%d, alt_mem_k=%d\n", params->ext_mem_k, params->alt_mem_k);
285 * Some parameters, like initrd=<file>, are not passed to kernel,
286 * we are responsible to process them.
287 * Parameters for kernel are copied to kern_cmdline. Returns name of initrd.
289 static char *parse_command_line(const char *orig_cmdline, char *kern_cmdline)
291 const char *start, *sep, *end, *val;
307 debug("original command line: \"%s\"\n", orig_cmdline);
308 debug("kernel command line at %#lx\n", virt_to_phys(kern_cmdline));
310 start = orig_cmdline;
311 while (*start == ' ')
314 end = strchr(start, ' ');
316 end = start + strlen(start);
317 sep = strchr(start, '=');
318 if (!sep || sep > end)
321 if (len >= sizeof(name))
322 len = sizeof(name) - 1;
323 memcpy(name, start, len);
334 /* Only initrd= and mem= are handled here. vga= is not,
335 * which I believe is a paramter to the realmode part of Linux,
336 * which we don't execute. */
337 if (strcmp(name, "initrd") == 0) {
339 printf("Missing filename to initrd parameter\n");
341 initrd = malloc(len + 1);
342 memcpy(initrd, val, len);
344 debug("initrd=%s\n", initrd);
346 /* Don't pass this to kernel */
348 } else if (strcmp(name, "mem") == 0) {
350 printf("Missing value for mem parameter\n");
352 forced_memsize = strtoull_with_suffix(val, (char**)&val, 0);
353 if (forced_memsize == 0)
354 printf("Invalid mem option, ignored\n");
356 printf("Garbage after mem=<size>, ignored\n");
359 debug("mem=%Lu\n", forced_memsize);
361 /* mem= is for both loader and kernel */
367 /* Copy to kernel command line buffer */
369 kern_cmdline[k_len++] = ' '; /* put separator */
371 if (k_len + len >= COMMAND_LINE_SIZE) {
372 len = COMMAND_LINE_SIZE - k_len - 1;
374 printf("Kernel command line is too long; truncated to "
375 "%d bytes\n", COMMAND_LINE_SIZE-1);
379 memcpy(kern_cmdline + k_len, start, len);
384 while (*start == ' ')
387 kern_cmdline[k_len] = 0;
388 debug("kernel command line (%d bytes): \"%s\"\n", k_len, kern_cmdline);
393 /* Set command line location */
394 static void set_command_line_loc(struct linux_params *params,
395 struct linux_header *hdr)
397 if (hdr->protocol_version >= 0x202) {
399 params->cmd_line_ptr = COMMAND_LINE_LOC;
402 params->cl_magic = CL_MAGIC_VALUE;
403 params->cl_offset = COMMAND_LINE_LOC - LINUX_PARAM_LOC;
407 /* Load 32-bit part of kernel */
408 static int load_linux_kernel(struct linux_header *hdr, uint32_t kern_addr)
410 uint32_t kern_offset, kern_size;
412 if (hdr->setup_sects == 0)
413 hdr->setup_sects = 4;
414 kern_offset = (hdr->setup_sects + 1) * 512;
415 file_seek(kern_offset);
416 kern_size = file_size() - kern_offset;
417 debug("offset=%#x addr=%#x size=%#x\n", kern_offset, kern_addr, kern_size);
421 printf("Attempt to load up to end of device as kernel; "
422 "specify the image size\n");
427 printf("Loading kernel... ");
428 if (lfile_read(phys_to_virt(kern_addr), kern_size) != kern_size) {
429 printf("Can't read kernel\n");
437 static int load_initrd(struct linux_header *hdr, struct sys_info *info,
438 uint32_t kern_end, struct linux_params *params, const char *initrd_file)
441 uint32_t start, end, size;
443 extern char _start[], _end[];
445 if (!file_open(initrd_file)) {
446 printf("Can't open initrd: %s\n", initrd_file);
452 printf("Attempt to load up to end of device as initrd; "
453 "specify the image size\n");
461 /* Find out the kernel's restriction on how high the initrd can be
463 if (hdr->protocol_version >= 0x203)
464 max = hdr->initrd_addr_max;
466 max = 0x38000000; /* Hardcoded value for older kernels */
468 /* FILO itself is at the top of RAM. (relocated)
469 * So, try putting initrd just below us. */
470 end = virt_to_phys(_start);
474 /* If "mem=" option is given, we have to put the initrd within
475 * the specified range. */
476 if (forced_memsize) {
477 forced = forced_memsize;
480 /* If the "mem=" is lower, it's easy */
484 /* Otherwise, see if we can put it above us */
485 if (virt_to_phys(_end) + size <= forced)
486 end = forced; /* Ok */
491 start &= ~0xfff; /* page align */
494 debug("start=%#x end=%#x\n", start, end);
496 if (start < kern_end) {
497 printf("Initrd is too big to fit in memory\n");
501 printf("Loading initrd... ");
502 if (lfile_read(phys_to_virt(start), size) != size) {
503 printf("Can't read initrd\n");
508 params->initrd_start = start;
509 params->initrd_size = size;
514 static void hardware_setup(void)
519 /* Make sure any coprocessor is properly reset.. */
523 /* we're getting screwed again and again by this problem of the 8259.
524 * so we're going to leave this lying around for inclusion into
525 * crt0.S on an as-needed basis.
527 * well, that went ok, I hope. Now we have to reprogram the interrupts :-(
528 * we put them right after the intel-reserved hardware interrupts, at
529 * int 0x20-0x2F. There they won't mess up anything. Sadly IBM really
530 * messed this up with the original PC, and they haven't been able to
531 * rectify it afterwards. Thus the bios puts interrupts at 0x08-0x0f,
532 * which is used for the internal hardware interrupts as well. We just
533 * have to reprogram the 8259's, and it isn't fun.
536 outb(0x11, 0x20); /* initialization sequence to 8259A-1 */
537 outb(0x11, 0xA0); /* and to 8259A-2 */
539 outb(0x20, 0x21); /* start of hardware int's (0x20) */
540 outb(0x28, 0xA1); /* start of hardware int's 2 (0x28) */
542 outb(0x04, 0x21); /* 8259-1 is master */
543 outb(0x02, 0xA1); /* 8259-2 is slave */
545 outb(0x01, 0x21); /* 8086 mode for both */
548 outb(0xFF, 0xA1); /* mask off all interrupts for now */
549 outb(0xFB, 0x21); /* mask all irq's but irq2 which is cascaded */
553 static int start_linux(uint32_t kern_addr, struct linux_params *params)
555 struct segment_desc *linux_gdt;
557 //extern int cursor_x, cursor_y;
559 ctx = init_context(phys_to_virt(STACK_LOC), 4096, 0);
561 /* Linux expects GDT being in low memory */
562 linux_gdt = phys_to_virt(GDT_LOC);
563 memset(linux_gdt, 0, 13*sizeof(struct segment_desc));
564 /* Normal kernel code/data segments */
565 linux_gdt[2] = gdt[FLAT_CODE];
566 linux_gdt[3] = gdt[FLAT_DATA];
567 /* 2.6 kernel uses 12 and 13, but head.S uses backward-compatible
568 * segments (2 and 3), so it SHOULD not be a problem.
569 * However, some distro kernels (eg. RH9) with backported threading
570 * patch use 12 and 13 also when booting... */
571 linux_gdt[12] = gdt[FLAT_CODE];
572 linux_gdt[13] = gdt[FLAT_DATA];
573 ctx->gdt_base = GDT_LOC;
574 ctx->gdt_limit = 14*8-1;
582 /* Parameter location */
583 ctx->esi = virt_to_phys(params);
586 ctx->eip = kern_addr;
588 debug("eip=%#x\n", kern_addr);
589 printf("Jumping to entry point...\n");
592 /* Update VGA cursor position.
593 * This must be here because the printf changes the value! */
594 params->orig_x = cursor_x;
595 params->orig_y = cursor_y;
599 ctx = switch_to(ctx);
601 /* It's impossible but... */
602 printf("Returned with eax=%#x\n", ctx->eax);
607 int linux_load(struct sys_info *info, const char *file, const char *cmdline)
609 struct linux_header hdr;
610 struct linux_params *params;
611 uint32_t kern_addr, kern_size;
612 char *initrd_file = 0;
614 if (!file_open(file))
617 kern_addr = load_linux_header(&hdr);
619 return LOADER_NOT_SUPPORT;
621 params = phys_to_virt(LINUX_PARAM_LOC);
622 init_linux_params(params, &hdr);
623 set_memory_size(params, info);
624 initrd_file = parse_command_line(cmdline, phys_to_virt(COMMAND_LINE_LOC));
625 set_command_line_loc(params, &hdr);
627 kern_size = load_linux_kernel(&hdr, kern_addr);
628 if (kern_size == 0) {
635 if (load_initrd(&hdr, info, kern_addr+kern_size, params, initrd_file)
645 start_linux(kern_addr, params);