Be excrutiatingly correct with inline assembly syntax
[syslinux.git] / com32 / modules / mboot.c
blob45f297fd9634a552fd65d5e36ff5cc3525d51e1c
1 /*
2 * mboot.c
4 * Loader for Multiboot-compliant kernels and modules.
6 * Copyright (C) 2005 Tim Deegan <Tim.Deegan@cl.cam.ac.uk>
7 * Parts based on GNU GRUB, Copyright (C) 2000 Free Software Foundation, Inc.
8 * Parts based on SYSLINUX, Copyright (C) 1994-2005 H. Peter Anvin.
9 * Thanks to Ram Yalamanchili for the ELF section-header loading.
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License as
13 * published by the Free Software Foundation; either version 2 of the
14 * License, or (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
24 * 02111-1307, USA.
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <stddef.h>
32 #include <string.h>
33 #include <malloc.h>
34 #include <consoles.h>
35 #include <zlib.h>
36 #include <com32.h>
38 #include "i386-elf.h"
39 #include "mb_info.h"
40 #include "mb_header.h"
42 #include <klibc/compiler.h> /* For __constructor */
44 #define MIN(_x, _y) (((_x)<(_y))?(_x):(_y))
45 #define MAX(_x, _y) (((_x)>(_y))?(_x):(_y))
47 /* Define this for some more printout */
48 #undef DEBUG
50 /* Memory magic numbers */
51 #define STACK_SIZE 0x20000 /* XXX Could be much smaller */
52 #define MALLOC_SIZE 0x100000 /* XXX Could be much smaller */
53 #define MIN_RUN_ADDR 0x10000 /* Lowest address we'll consider using */
54 #define MEM_HOLE_START 0xa0000 /* Memory hole runs from 640k ... */
55 #define MEM_HOLE_END 0x100000 /* ... to 1MB */
56 #define X86_PAGE_SIZE 0x1000
58 size_t __stack_size = STACK_SIZE; /* How much stack we'll use */
59 extern void *__mem_end; /* Start of malloc() heap */
60 extern char _end[]; /* End of static data */
62 /* Pointer to free memory for loading into: load area is between here
63 * and section_addr */
64 static char *next_load_addr;
66 /* Memory map for run-time */
67 typedef struct section section_t;
68 struct section {
69 size_t dest; /* Start of run-time allocation */
70 char *src; /* Current location of data for memmove(),
71 * or NULL for bzero() */
72 size_t size; /* Length of allocation */
74 static char *section_addr;
75 static int section_count;
77 static size_t max_run_addr; /* Highest address we'll consider using */
78 static size_t next_mod_run_addr; /* Where the next module will be put */
80 /* File loads are in units of this much */
81 #define LOAD_CHUNK 0x20000
83 /* Layout of the input to the 32-bit lidt instruction */
84 struct lidt_operand {
85 unsigned int limit:16;
86 unsigned int base:32;
87 } __attribute__((packed));
89 /* Magic strings */
90 static const char version_string[] = "COM32 Multiboot loader v0.2";
91 static const char copyright_string[] = "Copyright (C) 2005-2006 Tim Deegan.";
92 static const char module_separator[] = "---";
96 * Start of day magic, run from __start during library init.
99 static void __constructor check_version(void)
100 /* Check the SYSLINUX version. Docs say we should be OK from v2.08,
101 * but in fact we crash on anything below v2.12 (when libc came in). */
103 com32sys_t regs_in, regs_out;
104 const char *p, *too_old = "Fatal: SYSLINUX image is too old; "
105 "mboot.c32 needs at least version 2.12.\r\n";
107 memset(&regs_in, 0, sizeof(regs_in));
108 regs_in.eax.l = 0x0001; /* "Get version" */
109 __intcall(0x22, &regs_in, &regs_out);
110 if (regs_out.ecx.w[0] >= 0x020c) return;
112 /* Pointless: on older versions this print fails too. :( */
113 for (p = too_old ; *p ; p++) {
114 memset(&regs_in, 0, sizeof(regs_in));
115 regs_in.eax.b[1] = 0x02; /* "Write character" */
116 regs_in.edx.b[0] = *p;
117 __intcall(0x21, &regs_in, &regs_out);
120 __intcall(0x20, &regs_in, &regs_out); /* "Terminate program" */
124 static void __constructor grab_memory(void)
125 /* Runs before init_memory_arena() (com32/lib/malloc.c) to let
126 * the malloc() code know how much space it's allowed to use.
127 * We don't use malloc() directly, but some of the library code
128 * does (zlib, for example). */
130 /* Find the stack pointer */
131 register char * sp;
132 asm volatile("movl %%esp, %0" : "=r" (sp));
134 /* Initialize the allocation of *run-time* memory: don't let ourselves
135 * overwrite the stack during the relocation later. */
136 max_run_addr = (size_t) sp - (MALLOC_SIZE + STACK_SIZE);
138 /* Move the end-of-memory marker: malloc() will use only memory
139 * above __mem_end and below the stack. We will load files starting
140 * at the old __mem_end and working towards the new one, and allocate
141 * section descriptors at the top of that area, working down. */
142 next_load_addr = __mem_end;
143 section_addr = sp - (MALLOC_SIZE + STACK_SIZE);
144 section_count = 0;
146 /* But be careful not to move it the wrong direction if memory is
147 * tight. Instead we'll fail more gracefully later, when we try to
148 * load a file and find that next_load_addr > section_addr. */
149 __mem_end = MAX(section_addr, next_load_addr);
156 * Run-time memory map functions: allocating and recording allocations.
159 static int cmp_sections(const void *a, const void *b)
160 /* For sorting section descriptors by destination address */
162 const section_t *sa = a;
163 const section_t *sb = b;
164 if (sa->dest < sb->dest) return -1;
165 if (sa->dest > sb->dest) return 1;
166 return 0;
170 static void add_section(size_t dest, char *src, size_t size)
171 /* Adds something to the list of sections to relocate. */
173 section_t *sec;
175 #ifdef DEBUG
176 printf("SECTION: %#8.8x --> %#8.8x (%#x)\n", (size_t) src, dest, size);
177 #endif
179 section_addr -= sizeof (section_t);
180 if (section_addr < next_load_addr) {
181 printf("Fatal: out of memory allocating section descriptor.\n");
182 exit(1);
184 sec = (section_t *) section_addr;
185 section_count++;
187 sec->src = src;
188 sec->dest = dest;
189 sec->size = size;
191 /* Keep the list sorted */
192 qsort(sec, section_count, sizeof (section_t), cmp_sections);
196 static size_t place_low_section(size_t size, size_t align)
197 /* Find a space in the run-time memory map, below 640K */
199 int i;
200 size_t start;
201 section_t *sections = (section_t *) section_addr;
203 start = MIN_RUN_ADDR;
204 start = (start + (align-1)) & ~(align-1);
206 /* Section list is sorted by destination, so can do this in one pass */
207 for (i = 0; i < section_count; i++) {
208 if (sections[i].dest < start + size) {
209 /* Hit the bottom of this section */
210 start = sections[i].dest + sections[i].size;
211 start = (start + (align-1)) & ~(align-1);
214 if (start + size < MEM_HOLE_START) return start;
215 else return 0;
219 static size_t place_module_section(size_t size, size_t align)
220 /* Find a space in the run-time memory map for this module. */
222 /* Ideally we'd run through the sections looking for a free space
223 * like place_low_section() does, but some OSes (Xen, at least)
224 * assume that the bootloader has loaded all the modules
225 * consecutively, above the kernel. So, what we actually do is
226 * keep a pointer to the highest address allocated so far, and
227 * always allocate modules there. */
229 size_t start = next_mod_run_addr;
230 start = (start + (align-1)) & ~(align-1);
232 if (start + size > max_run_addr) return 0;
234 next_mod_run_addr = start + size;
235 return start;
239 static void place_kernel_section(size_t start, size_t size)
240 /* Allocate run-time space for part of the kernel, checking for
241 * sanity. We assume the kernel isn't broken enough to have
242 * overlapping segments. */
244 /* We always place modules above the kernel */
245 next_mod_run_addr = MAX(next_mod_run_addr, start + size);
247 if (start > max_run_addr || start + size > max_run_addr) {
248 /* Overruns the end of memory */
249 printf("Fatal: kernel loads too high (%#8.8x+%#x > %#8.8x).\n",
250 start, size, max_run_addr);
251 exit(1);
253 if (start >= MEM_HOLE_END) {
254 /* Above the memory hole: easy */
255 #ifdef DEBUG
256 printf("Placed kernel section (%#8.8x+%#x)\n", start, size);
257 #endif
258 return;
260 if (start >= MEM_HOLE_START) {
261 /* In the memory hole. Not so good */
262 printf("Fatal: kernel load address (%#8.8x) is in the memory hole.\n",
263 start);
264 exit(1);
266 if (start + size > MEM_HOLE_START) {
267 /* Too big for low memory */
268 printf("Fatal: kernel (%#8.8x+%#x) runs into the memory hole.\n",
269 start, size);
270 exit(1);
272 if (start < MIN_RUN_ADDR) {
273 /* Loads too low */
274 printf("Fatal: kernel load address (%#8.8x) is too low (<%#8.8x).\n",
275 start, MIN_RUN_ADDR);
276 exit(1);
278 /* Kernel loads below the memory hole: OK */
279 #ifdef DEBUG
280 printf("Placed kernel section (%#8.8x+%#x)\n", start, size);
281 #endif
285 static void reorder_sections(void)
286 /* Reorders sections into a safe order, where no relocation
287 * overwrites the source of a later one. */
289 section_t *secs = (section_t *) section_addr;
290 section_t tmp;
291 int i, j, tries;
293 #ifdef DEBUG
294 printf("Relocations:\n");
295 for (i = 0; i < section_count ; i++) {
296 printf(" %#8.8x --> %#8.8x (%#x)\n",
297 (size_t)secs[i].src, secs[i].dest, secs[i].size);
299 #endif
301 for (i = 0; i < section_count; i++) {
302 tries = 0;
303 scan_again:
304 for (j = i + 1 ; j < section_count; j++) {
305 if (secs[j].src != NULL
306 && secs[i].dest + secs[i].size > (size_t) secs[j].src
307 && secs[i].dest < (size_t) secs[j].src + secs[j].size) {
308 /* Would overwrite the source of the later move */
309 if (++tries > section_count) {
310 /* Deadlock! */
311 /* XXX Try to break deadlocks? */
312 printf("Fatal: circular dependence in relocations.\n");
313 exit(1);
315 /* Swap these sections (using struct copies) */
316 tmp = secs[i]; secs[i] = secs[j]; secs[j] = tmp;
317 /* Start scanning again from the new secs[i]... */
318 goto scan_again;
323 #ifdef DEBUG
324 printf("Relocations:\n");
325 for (i = 0; i < section_count ; i++) {
326 printf(" %#8.8x --> %#8.8x (%#x)\n",
327 (size_t)secs[i].src, secs[i].dest, secs[i].size);
329 #endif
333 static void init_mmap(struct multiboot_info *mbi)
334 /* Get a full memory map from the BIOS to pass to the kernel. */
336 com32sys_t regs_in, regs_out;
337 struct AddrRangeDesc *e820;
338 int e820_slots;
339 size_t mem_lower, mem_upper, run_addr, mmap_size;
340 register size_t sp;
342 /* Default values for mem_lower and mem_upper in case the BIOS won't
343 * tell us: 640K, and all memory up to the stack. */
344 asm volatile("movl %%esp, %0" : "=r" (sp));
345 mem_upper = (sp - MEM_HOLE_END) / 1024;
346 mem_lower = (MEM_HOLE_START) / 1024;
348 #ifdef DEBUG
349 printf("Requesting memory map from BIOS:\n");
350 #endif
352 /* Ask the BIOS for the full memory map of the machine. We'll
353 * build it in Multiboot format (i.e. with size fields) in the
354 * bounce buffer, and then allocate some high memory to keep it in
355 * until boot time. */
356 e820 = __com32.cs_bounce;
357 e820_slots = 0;
358 regs_out.ebx.l = 0;
360 while(((void *)(e820 + 1)) < __com32.cs_bounce + __com32.cs_bounce_size)
362 memset(e820, 0, sizeof (*e820));
363 memset(&regs_in, 0, sizeof regs_in);
364 e820->size = sizeof(*e820) - sizeof(e820->size);
366 /* Ask the BIOS to fill in this descriptor */
367 regs_in.eax.l = 0xe820; /* "Get system memory map" */
368 regs_in.ebx.l = regs_out.ebx.l; /* Continuation value from last call */
369 regs_in.ecx.l = 20; /* Size of buffer to write into */
370 regs_in.edx.l = 0x534d4150; /* "SMAP" */
371 regs_in.es = SEG(&e820->BaseAddr);
372 regs_in.edi.w[0] = OFFS(&e820->BaseAddr);
373 __intcall(0x15, &regs_in, &regs_out);
375 if ((regs_out.eflags.l & EFLAGS_CF) != 0 && regs_out.ebx.l != 0)
376 break; /* End of map */
378 if (((regs_out.eflags.l & EFLAGS_CF) != 0 && regs_out.ebx.l == 0)
379 || (regs_out.eax.l != 0x534d4150))
381 /* Error */
382 printf("Error %x reading E820 memory map: %s.\n",
383 (int) regs_out.eax.b[0],
384 (regs_out.eax.b[0] == 0x80) ? "invalid command" :
385 (regs_out.eax.b[0] == 0x86) ? "not supported" :
386 "unknown error");
387 break;
390 /* Success */
391 #ifdef DEBUG
392 printf(" %#16.16Lx -- %#16.16Lx : ",
393 e820->BaseAddr, e820->BaseAddr + e820->Length);
394 switch (e820->Type) {
395 case 1: printf("Available\n"); break;
396 case 2: printf("Reserved\n"); break;
397 case 3: printf("ACPI Reclaim\n"); break;
398 case 4: printf("ACPI NVS\n"); break;
399 default: printf("? (Reserved)\n"); break;
401 #endif
403 if (e820->Type == 1) {
404 if (e820->BaseAddr == 0) {
405 mem_lower = MIN(MEM_HOLE_START, e820->Length) / 1024;
406 } else if (e820->BaseAddr == MEM_HOLE_END) {
407 mem_upper = MIN(0xfff00000, e820->Length) / 1024;
411 /* Move to next slot */
412 e820++;
413 e820_slots++;
415 /* Done? */
416 if (regs_out.ebx.l == 0)
417 break;
420 /* Record the simple information in the MBI */
421 mbi->flags |= MB_INFO_MEMORY;
422 mbi->mem_lower = mem_lower;
423 mbi->mem_upper = mem_upper;
425 /* Record the full memory map in the MBI */
426 if (e820_slots != 0) {
427 mmap_size = e820_slots * sizeof(*e820);
428 /* Where will it live at run time? */
429 run_addr = place_low_section(mmap_size, 1);
430 if (run_addr == 0) {
431 printf("Fatal: can't find space for the e820 mmap.\n");
432 exit(1);
434 /* Where will it live now? */
435 e820 = (struct AddrRangeDesc *) next_load_addr;
436 if (next_load_addr + mmap_size > section_addr) {
437 printf("Fatal: out of memory storing the e820 mmap.\n");
438 exit(1);
440 next_load_addr += mmap_size;
441 /* Copy it out of the bounce buffer */
442 memcpy(e820, __com32.cs_bounce, mmap_size);
443 /* Remember to copy it again at run time */
444 add_section(run_addr, (char *) e820, mmap_size);
445 /* Record it in the MBI */
446 mbi->flags |= MB_INFO_MEM_MAP;
447 mbi->mmap_length = mmap_size;
448 mbi->mmap_addr = run_addr;
456 * Code for loading and parsing files.
459 static void load_file(char *filename, char **startp, size_t *sizep)
460 /* Load a file into memory. Returns where it is and how big via
461 * startp and sizep */
463 gzFile fp;
464 char *start;
465 int bsize;
467 printf("Loading %s.", filename);
469 start = next_load_addr;
470 startp[0] = start;
471 sizep[0] = 0;
473 /* Open the file */
474 if ((fp = gzopen(filename, "r")) == NULL) {
475 printf("\nFatal: cannot open %s\n", filename);
476 exit(1);
479 while (next_load_addr + LOAD_CHUNK <= section_addr) {
480 bsize = gzread(fp, next_load_addr, LOAD_CHUNK);
481 printf("%s",".");
483 if (bsize < 0) {
484 printf("\nFatal: read error in %s\n", filename);
485 gzclose(fp);
486 exit(1);
489 next_load_addr += bsize;
490 sizep[0] += bsize;
492 if (bsize < LOAD_CHUNK) {
493 printf("%s","\n");
494 gzclose(fp);
495 return;
499 /* Running out of memory. Try and use up the last bit */
500 if (section_addr > next_load_addr) {
501 bsize = gzread(fp, next_load_addr, section_addr - next_load_addr);
502 printf("%s",".");
503 } else {
504 bsize = 0;
507 if (bsize < 0) {
508 gzclose(fp);
509 printf("\nFatal: read error in %s\n", filename);
510 exit(1);
513 next_load_addr += bsize;
514 sizep[0] += bsize;
516 if (!gzeof(fp)) {
517 gzclose(fp);
518 printf("\nFatal: out of memory reading %s\n", filename);
519 exit(1);
522 printf("%s","\n");
523 gzclose(fp);
524 return;
528 static size_t load_kernel(struct multiboot_info *mbi, char *cmdline)
529 /* Load a multiboot/elf32 kernel and allocate run-time memory for it.
530 * Returns the kernel's entry address. */
532 unsigned int i;
533 char *load_addr; /* Where the image was loaded */
534 size_t load_size; /* How big it is */
535 char *seg_addr; /* Where a segment was loaded */
536 size_t seg_size, bss_size; /* How big it is */
537 size_t run_addr, run_size; /* Where it should be put */
538 size_t shdr_run_addr;
539 char *p;
540 Elf32_Ehdr *ehdr;
541 Elf32_Phdr *phdr;
542 Elf32_Shdr *shdr;
543 struct multiboot_header *mbh;
545 printf("Kernel: %s\n", cmdline);
547 load_addr = 0;
548 load_size = 0;
549 p = strchr(cmdline, ' ');
550 if (p != NULL) *p = 0;
551 load_file(cmdline, &load_addr, &load_size);
552 if (load_size < 12) {
553 printf("Fatal: %s is too short to be a multiboot kernel.",
554 cmdline);
555 exit(1);
557 if (p != NULL) *p = ' ';
560 /* Look for a multiboot header in the first 8k of the file */
561 for (i = 0; i <= MIN(load_size - 12, MULTIBOOT_SEARCH - 12); i += 4)
563 mbh = (struct multiboot_header *)(load_addr + i);
564 if (mbh->magic != MULTIBOOT_MAGIC
565 || ((mbh->magic+mbh->flags+mbh->checksum) & 0xffffffff))
567 /* Not a multiboot header */
568 continue;
570 if (mbh->flags & (MULTIBOOT_UNSUPPORTED | MULTIBOOT_VIDEO_MODE)) {
571 /* Requires options we don't support */
572 printf("Fatal: Kernel requires multiboot options "
573 "that I don't support: %#x.\n",
574 mbh->flags & (MULTIBOOT_UNSUPPORTED|MULTIBOOT_VIDEO_MODE));
575 exit(1);
578 /* This kernel will do: figure out where all the pieces will live */
580 if (mbh->flags & MULTIBOOT_AOUT_KLUDGE) {
582 /* Use the offsets in the multiboot header */
583 #ifdef DEBUG
584 printf("Using multiboot header.\n");
585 #endif
587 /* Where is the code in the loaded file? */
588 seg_addr = ((char *)mbh) - (mbh->header_addr - mbh->load_addr);
590 /* How much code is there? */
591 run_addr = mbh->load_addr;
592 if (mbh->load_end_addr != 0)
593 seg_size = mbh->load_end_addr - mbh->load_addr;
594 else
595 seg_size = load_size - (seg_addr - load_addr);
597 /* How much memory will it take up? */
598 if (mbh->bss_end_addr != 0)
599 run_size = mbh->bss_end_addr - mbh->load_addr;
600 else
601 run_size = seg_size;
603 if (seg_size > run_size) {
604 printf("Fatal: can't put %i bytes of kernel into %i bytes "
605 "of memory.\n", seg_size, run_size);
606 exit(1);
608 if (seg_addr + seg_size > load_addr + load_size) {
609 printf("Fatal: multiboot load segment runs off the "
610 "end of the file.\n");
611 exit(1);
614 /* Does it fit where it wants to be? */
615 place_kernel_section(run_addr, run_size);
617 /* Put it on the relocation list */
618 if (seg_size < run_size) {
619 /* Set up the kernel BSS too */
620 if (seg_size > 0)
621 add_section(run_addr, seg_addr, seg_size);
622 bss_size = run_size - seg_size;
623 add_section(run_addr + seg_size, NULL, bss_size);
624 } else {
625 /* No BSS */
626 add_section(run_addr, seg_addr, run_size);
629 /* Done. */
630 return mbh->entry_addr;
632 } else {
634 /* Now look for an ELF32 header */
635 ehdr = (Elf32_Ehdr *)load_addr;
636 if (*(unsigned long *)ehdr != 0x464c457f
637 || ehdr->e_ident[EI_DATA] != ELFDATA2LSB
638 || ehdr->e_ident[EI_CLASS] != ELFCLASS32
639 || ehdr->e_machine != EM_386)
641 printf("Fatal: kernel has neither ELF32/x86 nor multiboot load"
642 " headers.\n");
643 exit(1);
645 if (ehdr->e_phoff + ehdr->e_phnum*sizeof (*phdr) > load_size) {
646 printf("Fatal: malformed ELF header overruns EOF.\n");
647 exit(1);
649 if (ehdr->e_phnum <= 0) {
650 printf("Fatal: ELF kernel has no program headers.\n");
651 exit(1);
654 #ifdef DEBUG
655 printf("Using ELF header.\n");
656 #endif
658 if (ehdr->e_type != ET_EXEC
659 || ehdr->e_version != EV_CURRENT
660 || ehdr->e_phentsize != sizeof (Elf32_Phdr)) {
661 printf("Warning: funny-looking ELF header.\n");
663 phdr = (Elf32_Phdr *)(load_addr + ehdr->e_phoff);
665 /* Obey the program headers to load the kernel */
666 for(i = 0; i < ehdr->e_phnum; i++) {
668 /* How much is in this segment? */
669 run_size = phdr[i].p_memsz;
670 if (phdr[i].p_type != PT_LOAD)
671 seg_size = 0;
672 else
673 seg_size = (size_t)phdr[i].p_filesz;
675 /* Where is it in the loaded file? */
676 seg_addr = load_addr + phdr[i].p_offset;
677 if (seg_addr + seg_size > load_addr + load_size) {
678 printf("Fatal: ELF load segment runs off the "
679 "end of the file.\n");
680 exit(1);
683 /* Skip segments that don't take up any memory */
684 if (run_size == 0) continue;
686 /* Place the segment where it wants to be */
687 run_addr = phdr[i].p_paddr;
688 place_kernel_section(run_addr, run_size);
690 /* Put it on the relocation list */
691 if (seg_size < run_size) {
692 /* Set up the kernel BSS too */
693 if (seg_size > 0)
694 add_section(run_addr, seg_addr, seg_size);
695 bss_size = run_size - seg_size;
696 add_section(run_addr + seg_size, NULL, bss_size);
697 } else {
698 /* No BSS */
699 add_section(run_addr, seg_addr, run_size);
703 if (ehdr->e_shoff != 0) {
704 #ifdef DEBUG
705 printf("Loading ELF section table.\n");
706 #endif
707 /* Section Header */
708 shdr = (Elf32_Shdr *)(load_addr + ehdr->e_shoff);
710 /* Section Header Table size */
711 run_size = ehdr->e_shentsize * ehdr->e_shnum;
712 shdr_run_addr = place_module_section(run_size, 0x1000);
713 if (shdr_run_addr == 0) {
714 printf("Warning: Not enough memory to load the "
715 "section table.\n");
716 return ehdr->e_entry;
718 add_section(shdr_run_addr, (void*) shdr, run_size);
720 /* Load section tables not loaded thru program segments */
721 for (i = 0; i < ehdr->e_shnum; i++) {
722 /* This case is when this section is already included in
723 * program header or it's 0 size, so no need to load */
724 if (shdr[i].sh_addr != 0 || !shdr[i].sh_size)
725 continue;
727 if (shdr[i].sh_addralign == 0)
728 shdr[i].sh_addralign = 1;
730 run_addr = place_module_section(shdr[i].sh_size,
731 shdr[i].sh_addralign);
732 if (run_addr == 0) {
733 printf("Warning: Not enough memory to load "
734 "section %d.\n", i);
735 return ehdr->e_entry;
737 shdr[i].sh_addr = run_addr;
738 add_section(run_addr,
739 (void*) (shdr[i].sh_offset + load_addr),
740 shdr[i].sh_size);
743 mbi->flags |= MB_INFO_ELF_SHDR;
744 mbi->syms.e.num = ehdr->e_shnum;
745 mbi->syms.e.size = ehdr->e_shentsize;
746 mbi->syms.e.shndx = ehdr->e_shstrndx;
747 mbi->syms.e.addr = shdr_run_addr;
748 #ifdef DEBUG
749 printf("Section information: shnum: %lu, entSize: %lu, "
750 "shstrndx: %lu, addr: 0x%lx\n",
751 mbi->syms.e.num, mbi->syms.e.size,
752 mbi->syms.e.shndx, mbi->syms.e.addr);
753 #endif
756 /* Done! */
757 return ehdr->e_entry;
761 /* This is not a multiboot kernel */
762 printf("Fatal: not a multiboot kernel.\n");
763 exit(1);
768 static void load_module(struct mod_list *mod, char *cmdline)
769 /* Load a multiboot module and allocate a memory area for it */
771 char *load_addr, *p;
772 size_t load_size, run_addr;
774 printf("Module: %s\n", cmdline);
776 load_addr = 0;
777 load_size = 0;
778 p = strchr(cmdline, ' ');
779 if (p != NULL) *p = 0;
780 load_file(cmdline, &load_addr, &load_size);
781 if (p != NULL) *p = ' ';
783 /* Decide where it's going to live */
784 run_addr = place_module_section(load_size, X86_PAGE_SIZE);
785 if (run_addr == 0) {
786 printf("Fatal: can't find space for this module.\n");
787 exit(1);
789 add_section(run_addr, load_addr, load_size);
791 /* Remember where we put it */
792 mod->mod_start = run_addr;
793 mod->mod_end = run_addr + load_size;
794 mod->pad = 0;
796 #ifdef DEBUG
797 printf("Placed module (%#8.8x+%#x)\n", run_addr, load_size);
798 #endif
805 * Code for shuffling sections into place and booting the new kernel
808 static void trampoline_start(section_t *secs, int sec_count,
809 size_t mbi_run_addr, size_t entry)
810 /* Final shuffle-and-boot code. Running on the stack; no external code
811 * or data can be relied on. */
813 int i;
814 struct lidt_operand idt;
816 /* SYSLINUX has set up SS, DS and ES as 32-bit 0--4G data segments,
817 * but doesn't specify FS and GS. Multiboot wants them all to be
818 * the same, so we'd better do that before we overwrite the GDT. */
819 asm volatile("movl %ds, %ecx; movl %ecx, %fs; movl %ecx, %gs");
821 /* Turn off interrupts */
822 asm volatile("cli");
824 /* SYSLINUX has set up an IDT at 0x100000 that does all the
825 * comboot calls, and we're about to overwrite it. The Multiboot
826 * spec says that the kernel must set up its own IDT before turning
827 * on interrupts, but it's still entitled to use BIOS calls, so we'll
828 * put the IDT back to the BIOS one at the base of memory. */
829 idt.base = 0;
830 idt.limit = 0x800;
831 asm volatile("lidt %0" : : "m" (idt));
833 /* Now, shuffle the sections */
834 for (i = 0; i < sec_count; i++) {
835 if (secs[i].src == NULL) {
836 /* asm bzero() code from com32/lib/memset.c */
837 char *q = (char *) secs[i].dest;
838 size_t nl = secs[i].size >> 2;
839 asm volatile("cld ; rep ; stosl ; movl %3,%0 ; rep ; stosb"
840 : "+c" (nl), "+D" (q)
841 : "a" (0x0U), "r" (secs[i].size & 3));
842 } else {
843 /* asm memmove() code from com32/lib/memmove.c */
844 const char *p = secs[i].src;
845 char *q = (char *) secs[i].dest;
846 size_t n = secs[i].size;
847 if ( q < p ) {
848 asm volatile("cld ; rep ; movsb"
849 : "+c" (n), "+S" (p), "+D" (q));
850 } else {
851 p += (n-1);
852 q += (n-1);
853 asm volatile("std ; rep ; movsb"
854 : "+c" (n), "+S" (p), "+D" (q));
859 /* Now set up the last tiny bit of Multiboot environment.
860 * A20 is already enabled.
861 * CR0 already has PG cleared and PE set.
862 * EFLAGS already has VM and IF cleared.
863 * ESP is the kernels' problem.
864 * GDTR is the kernel's problem.
865 * CS is already a 32-bit, 0--4G code segments.
866 * DS, ES, FS and GS are already 32-bit, 0--4G data segments.
868 * EAX must be 0x2badb002 and EBX must point to the MBI when we jump. */
870 asm volatile ("jmp %*%2"
871 : : "a" (0x2badb002), "b" (mbi_run_addr), "cdSDm" (entry));
873 static void trampoline_end(void) {}
876 static void boot(size_t mbi_run_addr, size_t entry)
877 /* Tidy up SYSLINUX, shuffle memory and boot the kernel */
879 com32sys_t regs;
880 section_t *tr_sections;
881 void (*trampoline)(section_t *, int, size_t, size_t);
882 size_t trampoline_size;
884 /* Make sure the relocations are safe. */
885 reorder_sections();
887 /* Copy the shuffle-and-boot code and the array of relocations
888 * onto the memory we previously used for malloc() heap. This is
889 * safe because it's not the source or the destination of any
890 * copies, and there'll be no more library calls after the copy. */
892 tr_sections = ((section_t *) section_addr) + section_count;
893 trampoline = (void *) (tr_sections + section_count);
894 trampoline_size = (void *)&trampoline_end - (void *)&trampoline_start;
896 #ifdef DEBUG
897 printf("tr_sections: %p\n"
898 "trampoline: %p\n"
899 "trampoline_size: %#8.8x\n"
900 "max_run_addr: %#8.8x\n",
901 tr_sections, trampoline, trampoline_size, max_run_addr);
902 #endif
904 printf("Booting: MBI=%#8.8x, entry=%#8.8x\n", mbi_run_addr, entry);
906 memmove(tr_sections, section_addr, section_count * sizeof (section_t));
907 memmove(trampoline, trampoline_start, trampoline_size);
909 /* Tell SYSLINUX to clean up */
910 memset(&regs, 0, sizeof regs);
911 regs.eax.l = 0x000c; /* "Perform final cleanup" */
912 regs.edx.l = 0; /* "Normal cleanup" */
913 __intcall(0x22, &regs, NULL);
915 /* Into the unknown */
916 trampoline(tr_sections, section_count, mbi_run_addr, entry);
920 int main(int argc, char **argv)
921 /* Parse the command-line and invoke loaders */
923 struct multiboot_info *mbi;
924 struct mod_list *modp;
925 int modules;
926 int mbi_reloc_offset;
927 char *p;
928 size_t mbi_run_addr, mbi_size, entry;
929 int i;
931 /* Say hello */
932 console_ansi_std();
933 printf("%s. %s\n", version_string, copyright_string);
935 if (argc < 2 || !strcmp(argv[1], module_separator)) {
936 printf("Fatal: No kernel filename!\n");
937 exit(1);
940 #ifdef DEBUG
941 printf("_end: %p\n"
942 "argv[1]: %p\n"
943 "next_load_addr: %p\n"
944 "section_addr %p\n"
945 "__mem_end: %p\n"
946 "argv[0]: %p\n",
947 &_end, argv[1], next_load_addr, section_addr, __mem_end, argv[0]);
948 #endif
950 /* How much space will the MBI need? */
951 modules = 0;
952 mbi_size = sizeof(struct multiboot_info) + strlen(version_string) + 5;
953 for (i = 1 ; i < argc ; i++) {
954 if (!strcmp(argv[i], module_separator)) {
955 modules++;
956 mbi_size += sizeof(struct mod_list) + 1;
957 } else {
958 mbi_size += strlen(argv[i]) + 1;
962 /* Allocate space in the load buffer for the MBI, all the command
963 * lines, and all the module details. */
964 mbi = (struct multiboot_info *)next_load_addr;
965 next_load_addr += mbi_size;
966 if (next_load_addr > section_addr) {
967 printf("Fatal: out of memory allocating for boot metadata.\n");
968 exit(1);
970 memset(mbi, 0, sizeof (struct multiboot_info));
971 p = (char *)(mbi + 1);
972 mbi->flags = MB_INFO_CMDLINE | MB_INFO_BOOT_LOADER_NAME;
974 /* Figure out the memory map.
975 * N.B. Must happen before place_section() is called */
976 init_mmap(mbi);
978 mbi_run_addr = place_low_section(mbi_size, 4);
979 if (mbi_run_addr == 0) {
980 printf("Fatal: can't find space for the MBI!\n");
981 exit(1);
983 mbi_reloc_offset = (size_t)mbi - mbi_run_addr;
984 add_section(mbi_run_addr, (void *)mbi, mbi_size);
986 /* Module info structs */
987 modp = (struct mod_list *) (((size_t)p + 3) & ~3);
988 if (modules > 0) mbi->flags |= MB_INFO_MODS;
989 mbi->mods_count = modules;
990 mbi->mods_addr = ((size_t)modp) - mbi_reloc_offset;
991 p = (char *)(modp + modules);
993 /* Command lines: first kernel, then modules */
994 mbi->cmdline = ((size_t)p) - mbi_reloc_offset;
995 modules = 0;
996 for (i = 1 ; i < argc ; i++) {
997 if (!strcmp(argv[i], module_separator)) {
998 *p++ = '\0';
999 modp[modules++].cmdline = ((size_t)p) - mbi_reloc_offset;
1000 } else {
1001 strcpy(p, argv[i]);
1002 p += strlen(argv[i]);
1003 *p++ = ' ';
1006 *p++ = '\0';
1008 /* Bootloader ID */
1009 strcpy(p, version_string);
1010 mbi->boot_loader_name = ((size_t)p) - mbi_reloc_offset;
1011 p += strlen(version_string) + 1;
1013 /* Now, do all the loading, and boot it */
1014 entry = load_kernel(mbi, (char *)(mbi->cmdline + mbi_reloc_offset));
1015 for (i=0; i<modules; i++) {
1016 load_module(&(modp[i]), (char *)(modp[i].cmdline + mbi_reloc_offset));
1018 boot(mbi_run_addr, entry);
1020 return 1;
1024 * EOF