|
|
1.1 root 1: /*
2: * Linux/i386 loader
3: * Supports bzImage, zImage and Image format.
4: *
5: * Based on work by Steve Gehlbach.
6: * Portions are taken from mkelfImage.
7: *
8: * 2003-09 by SONE Takeshi
9: */
10:
11: #include "config.h"
12: #include "kernel/kernel.h"
13: #include "libopenbios/bindings.h"
14: #include "libopenbios/sys_info.h"
15: #include "context.h"
16: #include "segment.h"
17: #include "libc/diskio.h"
18: #include "boot.h"
19:
20: #define printf printk
21: #define debug printk
22: #define strtoull_with_suffix strtol
23:
24: #define LINUX_PARAM_LOC 0x90000
25: #define COMMAND_LINE_LOC 0x91000
26: #define GDT_LOC 0x92000
27: #define STACK_LOC 0x93000
28:
29: #define E820MAX 32 /* number of entries in E820MAP */
30: struct e820entry {
31: unsigned long long addr; /* start of memory segment */
32: unsigned long long size; /* size of memory segment */
33: unsigned long type; /* type of memory segment */
34: #define E820_RAM 1
35: #define E820_RESERVED 2
36: #define E820_ACPI 3 /* usable as RAM once ACPI tables have been read */
37: #define E820_NVS 4
38: };
39:
40: /* The header of Linux/i386 kernel */
41: struct linux_header {
42: uint8_t reserved1[0x1f1]; /* 0x000 */
43: uint8_t setup_sects; /* 0x1f1 */
44: uint16_t root_flags; /* 0x1f2 */
45: uint8_t reserved2[6]; /* 0x1f4 */
46: uint16_t vid_mode; /* 0x1fa */
47: uint16_t root_dev; /* 0x1fc */
48: uint16_t boot_sector_magic; /* 0x1fe */
49: /* 2.00+ */
50: uint8_t reserved3[2]; /* 0x200 */
51: uint8_t header_magic[4]; /* 0x202 */
52: uint16_t protocol_version; /* 0x206 */
53: uint32_t realmode_swtch; /* 0x208 */
54: uint16_t start_sys; /* 0x20c */
55: uint16_t kver_addr; /* 0x20e */
56: uint8_t type_of_loader; /* 0x210 */
57: uint8_t loadflags; /* 0x211 */
58: uint16_t setup_move_size; /* 0x212 */
59: uint32_t code32_start; /* 0x214 */
60: uint32_t ramdisk_image; /* 0x218 */
61: uint32_t ramdisk_size; /* 0x21c */
62: uint8_t reserved4[4]; /* 0x220 */
63: /* 2.01+ */
64: uint16_t heap_end_ptr; /* 0x224 */
65: uint8_t reserved5[2]; /* 0x226 */
66: /* 2.02+ */
67: uint32_t cmd_line_ptr; /* 0x228 */
68: /* 2.03+ */
69: uint32_t initrd_addr_max; /* 0x22c */
70: } __attribute__ ((packed));
71:
72:
73: /* Paramters passed to 32-bit part of Linux
74: * This is another view of the structure above.. */
75: struct linux_params {
76: uint8_t orig_x; /* 0x00 */
77: uint8_t orig_y; /* 0x01 */
78: uint16_t ext_mem_k; /* 0x02 -- EXT_MEM_K sits here */
79: uint16_t orig_video_page; /* 0x04 */
80: uint8_t orig_video_mode; /* 0x06 */
81: uint8_t orig_video_cols; /* 0x07 */
82: uint16_t unused2; /* 0x08 */
83: uint16_t orig_video_ega_bx; /* 0x0a */
84: uint16_t unused3; /* 0x0c */
85: uint8_t orig_video_lines; /* 0x0e */
86: uint8_t orig_video_isVGA; /* 0x0f */
87: uint16_t orig_video_points; /* 0x10 */
88:
89: /* VESA graphic mode -- linear frame buffer */
90: uint16_t lfb_width; /* 0x12 */
91: uint16_t lfb_height; /* 0x14 */
92: uint16_t lfb_depth; /* 0x16 */
93: uint32_t lfb_base; /* 0x18 */
94: uint32_t lfb_size; /* 0x1c */
95: uint16_t cl_magic; /* 0x20 */
96: #define CL_MAGIC_VALUE 0xA33F
97: uint16_t cl_offset; /* 0x22 */
98: uint16_t lfb_linelength; /* 0x24 */
99: uint8_t red_size; /* 0x26 */
100: uint8_t red_pos; /* 0x27 */
101: uint8_t green_size; /* 0x28 */
102: uint8_t green_pos; /* 0x29 */
103: uint8_t blue_size; /* 0x2a */
104: uint8_t blue_pos; /* 0x2b */
105: uint8_t rsvd_size; /* 0x2c */
106: uint8_t rsvd_pos; /* 0x2d */
107: uint16_t vesapm_seg; /* 0x2e */
108: uint16_t vesapm_off; /* 0x30 */
109: uint16_t pages; /* 0x32 */
110: uint8_t reserved4[12]; /* 0x34 -- 0x3f reserved for future expansion */
111:
112: //struct apm_bios_info apm_bios_info; /* 0x40 */
113: uint8_t apm_bios_info[0x40];
114: //struct drive_info_struct drive_info; /* 0x80 */
115: uint8_t drive_info[0x20];
116: //struct sys_desc_table sys_desc_table; /* 0xa0 */
117: uint8_t sys_desc_table[0x140];
118: uint32_t alt_mem_k; /* 0x1e0 */
119: uint8_t reserved5[4]; /* 0x1e4 */
120: uint8_t e820_map_nr; /* 0x1e8 */
121: uint8_t reserved6[9]; /* 0x1e9 */
122: uint16_t mount_root_rdonly; /* 0x1f2 */
123: uint8_t reserved7[4]; /* 0x1f4 */
124: uint16_t ramdisk_flags; /* 0x1f8 */
125: #define RAMDISK_IMAGE_START_MASK 0x07FF
126: #define RAMDISK_PROMPT_FLAG 0x8000
127: #define RAMDISK_LOAD_FLAG 0x4000
128: uint8_t reserved8[2]; /* 0x1fa */
129: uint16_t orig_root_dev; /* 0x1fc */
130: uint8_t reserved9[1]; /* 0x1fe */
131: uint8_t aux_device_info; /* 0x1ff */
132: uint8_t reserved10[2]; /* 0x200 */
133: uint8_t param_block_signature[4]; /* 0x202 */
134: uint16_t param_block_version; /* 0x206 */
135: uint8_t reserved11[8]; /* 0x208 */
136: uint8_t loader_type; /* 0x210 */
137: #define LOADER_TYPE_LOADLIN 1
138: #define LOADER_TYPE_BOOTSECT_LOADER 2
139: #define LOADER_TYPE_SYSLINUX 3
140: #define LOADER_TYPE_ETHERBOOT 4
141: #define LOADER_TYPE_KERNEL 5
142: uint8_t loader_flags; /* 0x211 */
143: uint8_t reserved12[2]; /* 0x212 */
144: uint32_t kernel_start; /* 0x214 */
145: uint32_t initrd_start; /* 0x218 */
146: uint32_t initrd_size; /* 0x21c */
147: uint8_t reserved12_5[8]; /* 0x220 */
148: uint32_t cmd_line_ptr; /* 0x228 */
149: uint8_t reserved13[164]; /* 0x22c */
150: struct e820entry e820_map[E820MAX]; /* 0x2d0 */
151: uint8_t reserved16[688]; /* 0x550 */
152: #define COMMAND_LINE_SIZE 256
153: /* Command line is copied here by 32-bit i386/kernel/head.S.
154: * So I will follow the boot protocol, rather than putting it
155: * directly here. --ts1 */
156: uint8_t command_line[COMMAND_LINE_SIZE]; /* 0x800 */
157: uint8_t reserved17[1792]; /* 0x900 - 0x1000 */
158: };
159:
160: static uint64_t forced_memsize;
161: static int fd;
162:
163: static unsigned long file_size(void)
164: {
165: long long fpos, fsize;
166:
167: /* Save current position */
168: fpos = tell(fd);
169:
170: /* Go to end of file and get position */
171: seek_io(fd, -1);
172: fsize = tell(fd);
173:
174: /* Go back to old position */
175: seek_io(fd, 0);
176: seek_io(fd, fpos);
177:
178: return fsize;
179: }
180:
181: /* Load the first part the file and check if it's Linux */
182: static uint32_t load_linux_header(struct linux_header *hdr)
183: {
184: int load_high;
185: uint32_t kern_addr;
186:
187: if (read_io(fd, hdr, sizeof *hdr) != sizeof *hdr) {
188: debug("Can't read Linux header\n");
189: return 0;
190: }
191: if (hdr->boot_sector_magic != 0xaa55) {
192: debug("Not a Linux kernel image\n");
193: return 0;
194: }
195:
196: /* Linux is found. Print some information */
197: if (memcmp(hdr->header_magic, "HdrS", 4) != 0) {
198: /* This may be floppy disk image or something.
199: * Perform a simple (incomplete) sanity check. */
200: if (hdr->setup_sects >= 16
201: || file_size() - (hdr->setup_sects<<9) >= 512<<10) {
202: debug("This looks like a bootdisk image but not like Linux...\n");
203: return 0;
204: }
205:
206: printf("Possible very old Linux");
207: /* This kernel does not even have a protocol version.
208: * Force the value. */
209: hdr->protocol_version = 0; /* pre-2.00 */
210: } else
211: printf("Found Linux");
212: if (hdr->protocol_version >= 0x200 && hdr->kver_addr) {
213: char kver[256];
214: seek_io(fd, hdr->kver_addr + 0x200);
215: if (read_io(fd, kver, sizeof kver) != 0) {
216: kver[255] = 0;
217: printf(" version %s", kver);
218: }
219: }
220: debug(" (protocol %#x)", hdr->protocol_version);
221: load_high = 0;
222: if (hdr->protocol_version >= 0x200) {
223: debug(" (loadflags %#x)", hdr->loadflags);
224: load_high = hdr->loadflags & 1;
225: }
226: if (load_high) {
227: printf(" bzImage");
228: kern_addr = 0x100000;
229: } else {
230: printf(" zImage or Image");
231: kern_addr = 0x1000;
232: }
233: printf(".\n");
234:
235: return kern_addr;
236: }
237:
238: /* Set up parameters for 32-bit kernel */
239: static void
240: init_linux_params(struct linux_params *params, struct linux_header *hdr)
241: {
242: debug("Setting up paramters at %#lx\n", virt_to_phys(params));
243: memset(params, 0, sizeof *params);
244:
245: /* Copy some useful values from header */
246: params->mount_root_rdonly = hdr->root_flags;
247: params->orig_root_dev = hdr->root_dev;
248:
249: /* Video parameters.
250: * This assumes we have VGA in standard 80x25 text mode,
251: * just like our vga.c does.
252: * Cursor position is filled later to allow some more printf's. */
253: params->orig_video_mode = 3;
254: params->orig_video_cols = 80;
255: params->orig_video_lines = 25;
256: params->orig_video_isVGA = 1;
257: params->orig_video_points = 16;
258:
259: params->loader_type = 0xff; /* Unregistered Linux loader */
260: }
261:
262: /* Memory map */
263: static void
264: set_memory_size(struct linux_params *params, struct sys_info *info)
265: {
266: int i;
267: uint64_t end;
268: uint32_t ramtop = 0;
269: struct e820entry *linux_map;
270: struct memrange *filo_map;
271:
272: linux_map = params->e820_map;
273: filo_map = info->memrange;
274: for (i = 0; i < info->n_memranges; i++, linux_map++, filo_map++) {
275: if (i < E820MAX) {
276: /* Convert to BIOS e820 style */
277: linux_map->addr = filo_map->base;
278: linux_map->size = filo_map->size;
279: linux_map->type = E820_RAM;
280: debug("%016Lx - %016Lx\n", linux_map->addr,
281: linux_map->addr + linux_map->size);
282: params->e820_map_nr = i+1;
283: }
284:
285: /* Find out top of RAM. XXX This ignores hole above 1MB */
286: end = filo_map->base + filo_map->size;
287: if (end < (1ULL << 32)) { /* don't count memory above 4GB */
288: if (end > ramtop)
289: ramtop = (uint32_t) end;
290: }
291: }
292: debug("ramtop=%#x\n", ramtop);
293: /* Size of memory above 1MB in KB */
294: params->alt_mem_k = (ramtop - (1<<20)) >> 10;
295: /* old style, 64MB max */
296: if (ramtop >= (64<<20))
297: params->ext_mem_k = (63<<10);
298: else
299: params->ext_mem_k = params->alt_mem_k;
300: debug("ext_mem_k=%d, alt_mem_k=%d\n", params->ext_mem_k, params->alt_mem_k);
301: }
302:
303: /*
304: * Parse command line
305: * Some parameters, like initrd=<file>, are not passed to kernel,
306: * we are responsible to process them.
307: * Parameters for kernel are copied to kern_cmdline. Returns name of initrd.
308: */
309: static char *parse_command_line(const char *orig_cmdline, char *kern_cmdline)
310: {
311: const char *start, *sep, *end, *val;
312: char name[64];
313: int len;
314: int k_len;
315: int to_kern;
316: char *initrd = NULL;
317: int toolong = 0;
318:
319: forced_memsize = 0;
320:
321: if (!orig_cmdline) {
322: *kern_cmdline = 0;
323: return NULL;
324: }
325:
326: k_len = 0;
327: debug("original command line: \"%s\"\n", orig_cmdline);
328: debug("kernel command line at %#lx\n", virt_to_phys(kern_cmdline));
329:
330: start = orig_cmdline;
331: while (*start == ' ')
332: start++;
333: while (*start) {
334: end = strchr(start, ' ');
335: if (!end)
336: end = start + strlen(start);
337: sep = strchr(start, '=');
338: if (!sep || sep > end)
339: sep = end;
340: len = sep - start;
341: if (len >= sizeof(name))
342: len = sizeof(name) - 1;
343: memcpy(name, start, len);
344: name[len] = 0;
345:
346: if (*sep == '=') {
347: val = sep + 1;
348: len = end - val;
349: } else {
350: val = NULL;
351: len = 0;
352: }
353:
354: /* Only initrd= and mem= are handled here. vga= is not,
355: * which I believe is a paramter to the realmode part of Linux,
356: * which we don't execute. */
357: if (strcmp(name, "initrd") == 0) {
358: if (!val)
359: printf("Missing filename to initrd parameter\n");
360: else {
361: initrd = malloc(len + 1);
362: memcpy(initrd, val, len);
363: initrd[len] = 0;
364: debug("initrd=%s\n", initrd);
365: }
366: /* Don't pass this to kernel */
367: to_kern = 0;
368: } else if (strcmp(name, "mem") == 0) {
369: if (!val)
370: printf("Missing value for mem parameter\n");
371: else {
372: forced_memsize = strtoull_with_suffix(val, (char**)&val, 0);
373: if (forced_memsize == 0)
374: printf("Invalid mem option, ignored\n");
375: if (val != end) {
376: printf("Garbage after mem=<size>, ignored\n");
377: forced_memsize = 0;
378: }
379: debug("mem=%Lu\n", forced_memsize);
380: }
381: /* mem= is for both loader and kernel */
382: to_kern = 1;
383: } else
384: to_kern = 1;
385:
386: if (to_kern) {
387: /* Copy to kernel command line buffer */
388: if (k_len != 0)
389: kern_cmdline[k_len++] = ' '; /* put separator */
390: len = end - start;
391: if (k_len + len >= COMMAND_LINE_SIZE) {
392: len = COMMAND_LINE_SIZE - k_len - 1;
393: if (!toolong) {
394: printf("Kernel command line is too long; truncated to "
395: "%d bytes\n", COMMAND_LINE_SIZE-1);
396: toolong = 1;
397: }
398: }
399: memcpy(kern_cmdline + k_len, start, len);
400: k_len += len;
401: }
402:
403: start = end;
404: while (*start == ' ')
405: start++;
406: }
407: kern_cmdline[k_len] = 0;
408: debug("kernel command line (%d bytes): \"%s\"\n", k_len, kern_cmdline);
409:
410: return initrd;
411: }
412:
413: /* Set command line location */
414: static void set_command_line_loc(struct linux_params *params,
415: struct linux_header *hdr)
416: {
417: if (hdr->protocol_version >= 0x202) {
418: /* new style */
419: params->cmd_line_ptr = COMMAND_LINE_LOC;
420: } else {
421: /* old style */
422: params->cl_magic = CL_MAGIC_VALUE;
423: params->cl_offset = COMMAND_LINE_LOC - LINUX_PARAM_LOC;
424: }
425: }
426:
427: /* Load 32-bit part of kernel */
428: static int load_linux_kernel(struct linux_header *hdr, uint32_t kern_addr)
429: {
430: uint32_t kern_offset, kern_size;
431:
432: if (hdr->setup_sects == 0)
433: hdr->setup_sects = 4;
434: kern_offset = (hdr->setup_sects + 1) * 512;
435: seek_io(fd, kern_offset);
436: kern_size = file_size() - kern_offset;
437: debug("offset=%#x addr=%#x size=%#x\n", kern_offset, kern_addr, kern_size);
438:
439: #if 0
440: if (using_devsize) {
441: printf("Attempt to load up to end of device as kernel; "
442: "specify the image size\n");
443: return 0;
444: }
445: #endif
446:
447: printf("Loading kernel... ");
448: if (read_io(fd, phys_to_virt(kern_addr), kern_size) != kern_size) {
449: printf("Can't read kernel\n");
450: return 0;
451: }
452: printf("ok\n");
453:
454: return kern_size;
455: }
456:
457: static int load_initrd(struct linux_header *hdr, struct sys_info *info,
458: uint32_t kern_end, struct linux_params *params, const char *initrd_file)
459: {
460: uint32_t max;
461: uint32_t start, end, size;
462: uint64_t forced;
463:
464: fd = open_io(initrd_file);
465: if (fd == -1) {
466: printf("Can't open initrd: %s\n", initrd_file);
467: return -1;
468: }
469:
470: #if 0
471: if (using_devsize) {
472: printf("Attempt to load up to end of device as initrd; "
473: "specify the image size\n");
474: return -1;
475: }
476: #endif
477:
478: size = file_size();
479:
480:
481: /* Find out the kernel's restriction on how high the initrd can be
482: * placed */
483: if (hdr->protocol_version >= 0x203)
484: max = hdr->initrd_addr_max;
485: else
486: max = 0x38000000; /* Hardcoded value for older kernels */
487:
488: /* FILO itself is at the top of RAM. (relocated)
489: * So, try putting initrd just below us. */
490: end = virt_to_phys(_start);
491: if (end > max)
492: end = max;
493:
494: /* If "mem=" option is given, we have to put the initrd within
495: * the specified range. */
496: if (forced_memsize) {
497: forced = forced_memsize;
498: if (forced > max)
499: forced = max;
500: /* If the "mem=" is lower, it's easy */
501: if (forced <= end)
502: end = forced;
503: else {
504: /* Otherwise, see if we can put it above us */
505: if (virt_to_phys(_end) + size <= forced)
506: end = forced; /* Ok */
507: }
508: }
509:
510: start = end - size;
511: start &= ~0xfff; /* page align */
512: end = start + size;
513:
514: debug("start=%#x end=%#x\n", start, end);
515:
516: if (start < kern_end) {
517: printf("Initrd is too big to fit in memory\n");
518: return -1;
519: }
520:
521: printf("Loading initrd... ");
522: if (read_io(fd, phys_to_virt(start), size) != size) {
523: printf("Can't read initrd\n");
524: return -1;
525: }
526: printf("ok\n");
527:
528: params->initrd_start = start;
529: params->initrd_size = size;
530:
531: close_io(fd);
532:
533: return 0;
534: }
535:
536: static void hardware_setup(void)
537: {
538: /* Disable nmi */
539: outb(0x80, 0x70);
540:
541: /* Make sure any coprocessor is properly reset.. */
542: outb(0, 0xf0);
543: outb(0, 0xf1);
544:
545: /* we're getting screwed again and again by this problem of the 8259.
546: * so we're going to leave this lying around for inclusion into
547: * crt0.S on an as-needed basis.
548: *
549: * well, that went ok, I hope. Now we have to reprogram the interrupts :-(
550: * we put them right after the intel-reserved hardware interrupts, at
551: * int 0x20-0x2F. There they won't mess up anything. Sadly IBM really
552: * messed this up with the original PC, and they haven't been able to
553: * rectify it afterwards. Thus the bios puts interrupts at 0x08-0x0f,
554: * which is used for the internal hardware interrupts as well. We just
555: * have to reprogram the 8259's, and it isn't fun.
556: */
557:
558: outb(0x11, 0x20); /* initialization sequence to 8259A-1 */
559: outb(0x11, 0xA0); /* and to 8259A-2 */
560:
561: outb(0x20, 0x21); /* start of hardware int's (0x20) */
562: outb(0x28, 0xA1); /* start of hardware int's 2 (0x28) */
563:
564: outb(0x04, 0x21); /* 8259-1 is master */
565: outb(0x02, 0xA1); /* 8259-2 is slave */
566:
567: outb(0x01, 0x21); /* 8086 mode for both */
568: outb(0x01, 0xA1);
569:
570: outb(0xFF, 0xA1); /* mask off all interrupts for now */
571: outb(0xFB, 0x21); /* mask all irq's but irq2 which is cascaded */
572: }
573:
574: /* Start Linux */
575: static int start_linux(uint32_t kern_addr, struct linux_params *params)
576: {
577: struct segment_desc *linux_gdt;
578: struct context *ctx;
579: //extern int cursor_x, cursor_y;
580:
581: ctx = init_context(phys_to_virt(STACK_LOC), 4096, 0);
582:
583: /* Linux expects GDT being in low memory */
584: linux_gdt = phys_to_virt(GDT_LOC);
585: memset(linux_gdt, 0, 13*sizeof(struct segment_desc));
586: /* Normal kernel code/data segments */
587: linux_gdt[2] = gdt[FLAT_CODE];
588: linux_gdt[3] = gdt[FLAT_DATA];
589: /* 2.6 kernel uses 12 and 13, but head.S uses backward-compatible
590: * segments (2 and 3), so it SHOULD not be a problem.
591: * However, some distro kernels (eg. RH9) with backported threading
592: * patch use 12 and 13 also when booting... */
593: linux_gdt[12] = gdt[FLAT_CODE];
594: linux_gdt[13] = gdt[FLAT_DATA];
595: ctx->gdt_base = GDT_LOC;
596: ctx->gdt_limit = 14*8-1;
597: ctx->cs = 0x10;
598: ctx->ds = 0x18;
599: ctx->es = 0x18;
600: ctx->fs = 0x18;
601: ctx->gs = 0x18;
602: ctx->ss = 0x18;
603:
604: /* Parameter location */
605: ctx->esi = virt_to_phys(params);
606:
607: /* Entry point */
608: ctx->eip = kern_addr;
609:
610: debug("eip=%#x\n", kern_addr);
611: printf("Jumping to entry point...\n");
612:
613: #ifdef VGA_CONSOLE
614: /* Update VGA cursor position.
615: * This must be here because the printf changes the value! */
616: params->orig_x = cursor_x;
617: params->orig_y = cursor_y;
618: #endif
619:
620: /* Go... */
621: ctx = switch_to(ctx);
622:
623: /* It's impossible but... */
624: printf("Returned with eax=%#x\n", ctx->eax);
625:
626: return ctx->eax;
627: }
628:
629: int linux_load(struct sys_info *info, const char *file, const char *cmdline)
630: {
631: struct linux_header hdr;
632: struct linux_params *params;
633: uint32_t kern_addr, kern_size;
634: char *initrd_file = NULL;
635:
636: fd = open_io(file);
637: if (fd == -1) {
638: return -1;
639: }
640:
641: kern_addr = load_linux_header(&hdr);
642: if (kern_addr == 0)
643: return LOADER_NOT_SUPPORT;
644:
645: params = phys_to_virt(LINUX_PARAM_LOC);
646: init_linux_params(params, &hdr);
647: set_memory_size(params, info);
648: initrd_file = parse_command_line(cmdline, phys_to_virt(COMMAND_LINE_LOC));
649: set_command_line_loc(params, &hdr);
650:
651: kern_size = load_linux_kernel(&hdr, kern_addr);
652: if (kern_size == 0) {
653: if (initrd_file)
654: free(initrd_file);
655: return -1;
656: }
657:
658: if (initrd_file) {
659: if (load_initrd(&hdr, info, kern_addr+kern_size, params, initrd_file)
660: != 0) {
661: free(initrd_file);
662: return -1;
663: }
664: free(initrd_file);
665: }
666:
667: hardware_setup();
668:
669: start_linux(kern_addr, params);
670: return 0;
671: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.