📄 mboot.c
字号:
/* * mboot.c * * Loader for Multiboot-compliant kernels and modules. * * Copyright (C) 2005 Tim Deegan <Tim.Deegan@cl.cam.ac.uk> * Parts based on GNU GRUB, Copyright (C) 2000 Free Software Foundation, Inc. * Parts based on SYSLINUX, Copyright (C) 1994-2005 H. Peter Anvin. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */#include <stdio.h>#include <stdlib.h>#include <stddef.h>#include <string.h>#include <malloc.h>#include <consoles.h>#include <zlib.h>#include <com32.h>#include "i386-elf.h"#include "mb_info.h"#include "mb_header.h"#include <klibc/compiler.h> /* For __constructor */#define MIN(_x, _y) (((_x)<(_y))?(_x):(_y))#define MAX(_x, _y) (((_x)>(_y))?(_x):(_y))/* Define this for some more printout */#undef DEBUG/* Memory magic numbers */#define STACK_SIZE 0x20000 /* XXX Could be much smaller */#define MALLOC_SIZE 0x100000 /* XXX Could be much smaller */#define MIN_RUN_ADDR 0x10000 /* Lowest address we'll consider using */#define MEM_HOLE_START 0xa0000 /* Memory hole runs from 640k ... */#define MEM_HOLE_END 0x100000 /* ... to 1MB */#define X86_PAGE_SIZE 0x1000size_t __stack_size = STACK_SIZE; /* How much stack we'll use */extern void *__mem_end; /* Start of malloc() heap */extern char _end[]; /* End of static data *//* Pointer to free memory for loading into: load area is between here * and section_addr */static char *next_load_addr;/* Memory map for run-time */typedef struct section section_t;struct section { size_t dest; /* Start of run-time allocation */ char *src; /* Current location of data for memmove(), * or NULL for bzero() */ size_t size; /* Length of allocation */};static char *section_addr;static int section_count;static size_t max_run_addr; /* Highest address we'll consider using */static size_t next_mod_run_addr; /* Where the next module will be put *//* File loads are in units of this much */#define LOAD_CHUNK 0x20000/* Layout of the input to the 32-bit lidt instruction */struct lidt_operand { unsigned int limit:16; unsigned int base:32;} __attribute__((packed));/* Magic strings */static const char version_string[] = "COM32 Multiboot loader v0.1";static const char copyright_string[] = "Copyright (C) 2005 Tim Deegan.";static const char module_separator[] = "---";/* * Start of day magic, run from __start during library init. */static void __constructor check_version(void) /* Check the SYSLINUX version. Docs say we should be OK from v2.08, * but in fact we crash on anything below v2.12 (when libc came in). */{ com32sys_t regs_in, regs_out; const char *p, *too_old = "Fatal: SYSLINUX image is too old; " "mboot.c32 needs at least version 2.12.\r\n"; memset(®s_in, 0, sizeof(regs_in)); regs_in.eax.l = 0x0001; /* "Get version" */ __intcall(0x22, ®s_in, ®s_out); if (regs_out.ecx.w[0] >= 0x020c) return; /* Pointless: on older versions this print fails too. :( */ for (p = too_old ; *p ; p++) { memset(®s_in, 0, sizeof(regs_in)); regs_in.eax.b[1] = 0x02; /* "Write character" */ regs_in.edx.b[0] = *p; __intcall(0x21, ®s_in, ®s_out); } __intcall(0x20, ®s_in, ®s_out); /* "Terminate program" */}static void __constructor grab_memory(void) /* Runs before init_memory_arena() (com32/lib/malloc.c) to let * the malloc() code know how much space it's allowed to use. * We don't use malloc() directly, but some of the library code * does (zlib, for example). */ { /* Find the stack pointer */ register char * sp; asm volatile("movl %%esp, %0" : "=r" (sp)); /* Initialize the allocation of *run-time* memory: don't let ourselves * overwrite the stack during the relocation later. */ max_run_addr = (size_t) sp - (MALLOC_SIZE + STACK_SIZE); /* Move the end-of-memory marker: malloc() will use only memory * above __mem_end and below the stack. We will load files starting * at the old __mem_end and working towards the new one, and allocate * section descriptors at the top of that area, working down. */ next_load_addr = __mem_end; section_addr = sp - (MALLOC_SIZE + STACK_SIZE); section_count = 0; /* But be careful not to move it the wrong direction if memory is * tight. Instead we'll fail more gracefully later, when we try to * load a file and find that next_load_addr > section_addr. */ __mem_end = MAX(section_addr, next_load_addr);}/* * Run-time memory map functions: allocating and recording allocations. */static int cmp_sections(const void *a, const void *b) /* For sorting section descriptors by destination address */{ const section_t *sa = a; const section_t *sb = b; if (sa->dest < sb->dest) return -1; if (sa->dest > sb->dest) return 1; return 0;}static void add_section(size_t dest, char *src, size_t size) /* Adds something to the list of sections to relocate. */{ section_t *sec;#ifdef DEBUG printf("SECTION: %#8.8x --> %#8.8x (%#x)\n", (size_t) src, dest, size);#endif section_addr -= sizeof (section_t); if (section_addr < next_load_addr) { printf("Fatal: out of memory allocating section descriptor.\n"); exit(1); } sec = (section_t *) section_addr; section_count++; sec->src = src; sec->dest = dest; sec->size = size; /* Keep the list sorted */ qsort(sec, section_count, sizeof (section_t), cmp_sections);}static size_t place_low_section(size_t size, size_t align) /* Find a space in the run-time memory map, below 640K */{ int i; size_t start; section_t *sections = (section_t *) section_addr; start = MIN_RUN_ADDR; start = (start + (align-1)) & ~(align-1); /* Section list is sorted by destination, so can do this in one pass */ for (i = 0; i < section_count; i++) { if (sections[i].dest < start + size) { /* Hit the bottom of this section */ start = sections[i].dest + sections[i].size; start = (start + (align-1)) & ~(align-1); } } if (start + size < MEM_HOLE_START) return start; else return 0;}static size_t place_module_section(size_t size, size_t align) /* Find a space in the run-time memory map for this module. */{ /* Ideally we'd run through the sections looking for a free space * like place_low_section() does, but some OSes (Xen, at least) * assume that the bootloader has loaded all the modules * consecutively, above the kernel. So, what we actually do is * keep a pointer to the highest address allocated so far, and * always allocate modules there. */ size_t start = next_mod_run_addr; start = (start + (align-1)) & ~(align-1); if (start + size > max_run_addr) return 0; next_mod_run_addr = start + size; return start;}static void place_kernel_section(size_t start, size_t size) /* Allocate run-time space for part of the kernel, checking for * sanity. We assume the kernel isn't broken enough to have * overlapping segments. */{ /* We always place modules above the kernel */ next_mod_run_addr = MAX(next_mod_run_addr, start + size); if (start > max_run_addr || start + size > max_run_addr) { /* Overruns the end of memory */ printf("Fatal: kernel loads too high (%#8.8x+%#x > %#8.8x).\n", start, size, max_run_addr); exit(1); } if (start >= MEM_HOLE_END) { /* Above the memory hole: easy */#ifdef DEBUG printf("Placed kernel section (%#8.8x+%#x)\n", start, size);#endif return; } if (start >= MEM_HOLE_START) { /* In the memory hole. Not so good */ printf("Fatal: kernel load address (%#8.8x) is in the memory hole.\n", start); exit(1); } if (start + size > MEM_HOLE_START) { /* Too big for low memory */ printf("Fatal: kernel (%#8.8x+%#x) runs into the memory hole.\n", start, size); exit(1); } if (start < MIN_RUN_ADDR) { /* Loads too low */ printf("Fatal: kernel load address (%#8.8x) is too low (<%#8.8x).\n", start, MIN_RUN_ADDR); exit(1); } /* Kernel loads below the memory hole: OK */#ifdef DEBUG printf("Placed kernel section (%#8.8x+%#x)\n", start, size);#endif}static void reorder_sections(void) /* Reorders sections into a safe order, where no relocation * overwrites the source of a later one. */{ section_t *secs = (section_t *) section_addr; section_t tmp; int i, j, tries;#ifdef DEBUG printf("Relocations:\n"); for (i = 0; i < section_count ; i++) { printf(" %#8.8x --> %#8.8x (%#x)\n", (size_t)secs[i].src, secs[i].dest, secs[i].size); }#endif for (i = 0; i < section_count; i++) { tries = 0; scan_again: for (j = i + 1 ; j < section_count; j++) { if (secs[j].src != NULL && secs[i].dest + secs[i].size > (size_t) secs[j].src && secs[i].dest < (size_t) secs[j].src + secs[j].size) { /* Would overwrite the source of the later move */ if (++tries > section_count) { /* Deadlock! */ /* XXX Try to break deadlocks? */ printf("Fatal: circular dependence in relocations.\n"); exit(1); } /* Swap these sections (using struct copies) */ tmp = secs[i]; secs[i] = secs[j]; secs[j] = tmp; /* Start scanning again from the new secs[i]... */ goto scan_again; } } }#ifdef DEBUG printf("Relocations:\n"); for (i = 0; i < section_count ; i++) { printf(" %#8.8x --> %#8.8x (%#x)\n", (size_t)secs[i].src, secs[i].dest, secs[i].size); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -