📄 boot.c
字号:
/* boot.c - load and bootstrap a kernel *//* * GRUB -- GRand Unified Bootloader * Copyright (C) 1999,2000,2001,2002 Free Software Foundation, Inc. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. */#include "shared.h"#include "freebsd.h"#include "imgact_aout.h"#include "i386-elf.h"static int cur_addr;entry_func entry_addr;static struct mod_list mll[99];static int linux_mem_size;/* * The next two functions, 'load_image' and 'load_module', are the building * blocks of the multiboot loader component. They handle essentially all * of the gory details of loading in a bootable image and the modules. */kernel_tload_image (char *kernel, char *arg, kernel_t suggested_type, unsigned long load_flags){ int len, i, exec_type = 0, align_4k = 1; entry_func real_entry_addr = 0; kernel_t type = KERNEL_TYPE_NONE; unsigned long flags = 0, text_len = 0, data_len = 0, bss_len = 0; char *str = 0, *str2 = 0; struct linux_kernel_header *lh; union { struct multiboot_header *mb; struct exec *aout; Elf32_Ehdr *elf; } pu; /* presuming that MULTIBOOT_SEARCH is large enough to encompass an executable header */ unsigned char buffer[MULTIBOOT_SEARCH]; /* sets the header pointer to point to the beginning of the buffer by default */ pu.aout = (struct exec *) buffer; if (!grub_open (kernel)) return KERNEL_TYPE_NONE; if (!(len = grub_read (buffer, MULTIBOOT_SEARCH)) || len < 32) { grub_close (); if (!errnum) errnum = ERR_EXEC_FORMAT; return KERNEL_TYPE_NONE; } for (i = 0; i < len; i++) { if (MULTIBOOT_FOUND ((int) (buffer + i), len - i)) { flags = ((struct multiboot_header *) (buffer + i))->flags; if (flags & MULTIBOOT_UNSUPPORTED) { grub_close (); errnum = ERR_BOOT_FEATURES; return KERNEL_TYPE_NONE; } type = KERNEL_TYPE_MULTIBOOT; str2 = "Multiboot"; break; } } /* Use BUFFER as a linux kernel header, if the image is Linux zImage or bzImage. */ lh = (struct linux_kernel_header *) buffer; /* ELF loading supported if multiboot, FreeBSD and NetBSD. */ if ((type == KERNEL_TYPE_MULTIBOOT || pu.elf->e_ident[EI_OSABI] == ELFOSABI_FREEBSD || grub_strcmp (pu.elf->e_ident + EI_BRAND, "FreeBSD") == 0 || suggested_type == KERNEL_TYPE_NETBSD) && len > sizeof (Elf32_Ehdr) && BOOTABLE_I386_ELF ((*((Elf32_Ehdr *) buffer)))) { if (type == KERNEL_TYPE_MULTIBOOT) entry_addr = (entry_func) pu.elf->e_entry; else entry_addr = (entry_func) (pu.elf->e_entry & 0xFFFFFF); if (entry_addr < (entry_func) 0x100000) errnum = ERR_BELOW_1MB; /* don't want to deal with ELF program header at some random place in the file -- this generally won't happen */ if (pu.elf->e_phoff == 0 || pu.elf->e_phnum == 0 || ((pu.elf->e_phoff + (pu.elf->e_phentsize * pu.elf->e_phnum)) >= len)) errnum = ERR_EXEC_FORMAT; str = "elf"; if (type == KERNEL_TYPE_NONE) { /* At the moment, there is no way to identify a NetBSD ELF kernel, so rely on the suggested type by the user. */ if (suggested_type == KERNEL_TYPE_NETBSD) { str2 = "NetBSD"; type = suggested_type; } else { str2 = "FreeBSD"; type = KERNEL_TYPE_FREEBSD; } } } else if (flags & MULTIBOOT_AOUT_KLUDGE) { pu.mb = (struct multiboot_header *) (buffer + i); entry_addr = (entry_func) pu.mb->entry_addr; cur_addr = pu.mb->load_addr; /* first offset into file */ grub_seek (i - (pu.mb->header_addr - cur_addr)); /* If the load end address is zero, load the whole contents. */ if (! pu.mb->load_end_addr) pu.mb->load_end_addr = cur_addr + filemax; text_len = pu.mb->load_end_addr - cur_addr; data_len = 0; /* If the bss end address is zero, assume that there is no bss area. */ if (! pu.mb->bss_end_addr) pu.mb->bss_end_addr = pu.mb->load_end_addr; bss_len = pu.mb->bss_end_addr - pu.mb->load_end_addr; if (pu.mb->header_addr < pu.mb->load_addr || pu.mb->load_end_addr <= pu.mb->load_addr || pu.mb->bss_end_addr < pu.mb->load_end_addr || (pu.mb->header_addr - pu.mb->load_addr) > i) errnum = ERR_EXEC_FORMAT; if (cur_addr < 0x100000) errnum = ERR_BELOW_1MB; pu.aout = (struct exec *) buffer; exec_type = 2; str = "kludge"; } else if (len > sizeof (struct exec) && !N_BADMAG ((*(pu.aout)))) { entry_addr = (entry_func) pu.aout->a_entry; if (type == KERNEL_TYPE_NONE) { /* * If it doesn't have a Multiboot header, then presume * it is either a FreeBSD or NetBSD executable. If so, * then use a magic number of normal ordering, ZMAGIC to * determine if it is FreeBSD. * * This is all because freebsd and netbsd seem to require * masking out some address bits... differently for each * one... plus of course we need to know which booting * method to use. */ entry_addr = (entry_func) ((int) entry_addr & 0xFFFFFF); if (buffer[0] == 0xb && buffer[1] == 1) { type = KERNEL_TYPE_FREEBSD; cur_addr = (int) entry_addr; str2 = "FreeBSD"; } else { type = KERNEL_TYPE_NETBSD; cur_addr = (int) entry_addr & 0xF00000; if (N_GETMAGIC ((*(pu.aout))) != NMAGIC) align_4k = 0; str2 = "NetBSD"; } } /* first offset into file */ grub_seek (N_TXTOFF (*(pu.aout))); text_len = pu.aout->a_text; data_len = pu.aout->a_data; bss_len = pu.aout->a_bss; if (cur_addr < 0x100000) errnum = ERR_BELOW_1MB; exec_type = 1; str = "a.out"; } else if (lh->boot_flag == BOOTSEC_SIGNATURE && lh->setup_sects <= LINUX_MAX_SETUP_SECTS) { int big_linux = 0; int setup_sects = lh->setup_sects; if (lh->header == LINUX_MAGIC_SIGNATURE && lh->version >= 0x0200) { big_linux = (lh->loadflags & LINUX_FLAG_BIG_KERNEL); lh->type_of_loader = LINUX_BOOT_LOADER_TYPE; /* Put the real mode part at as a high location as possible. */ linux_data_real_addr = (char *) ((mbi.mem_lower << 10) - LINUX_SETUP_MOVE_SIZE); /* But it must not exceed the traditional area. */ if (linux_data_real_addr > (char *) LINUX_OLD_REAL_MODE_ADDR) linux_data_real_addr = (char *) LINUX_OLD_REAL_MODE_ADDR; if (lh->version >= 0x0201) { lh->heap_end_ptr = LINUX_HEAP_END_OFFSET; lh->loadflags |= LINUX_FLAG_CAN_USE_HEAP; } if (lh->version >= 0x0202) lh->cmd_line_ptr = linux_data_real_addr + LINUX_CL_OFFSET; else { lh->cl_magic = LINUX_CL_MAGIC; lh->cl_offset = LINUX_CL_OFFSET; lh->setup_move_size = LINUX_SETUP_MOVE_SIZE; } } else { /* Your kernel is quite old... */ lh->cl_magic = LINUX_CL_MAGIC; lh->cl_offset = LINUX_CL_OFFSET; setup_sects = LINUX_DEFAULT_SETUP_SECTS; linux_data_real_addr = (char *) LINUX_OLD_REAL_MODE_ADDR; } /* If SETUP_SECTS is not set, set it to the default (4). */ if (! setup_sects) setup_sects = LINUX_DEFAULT_SETUP_SECTS; data_len = setup_sects << 9; text_len = filemax - data_len - SECTOR_SIZE; linux_data_tmp_addr = (char *) LINUX_BZIMAGE_ADDR + text_len; if (! big_linux && text_len > linux_data_real_addr - (char *) LINUX_ZIMAGE_ADDR) { grub_printf (" linux 'zImage' kernel too big, try 'make bzImage'\n"); errnum = ERR_WONT_FIT; } else if (linux_data_real_addr + LINUX_SETUP_MOVE_SIZE > RAW_ADDR ((char *) (mbi.mem_lower << 10))) errnum = ERR_WONT_FIT; else { grub_printf (" [Linux-%s, setup=0x%x, size=0x%x]\n", (big_linux ? "bzImage" : "zImage"), data_len, text_len); /* Video mode selection support. What a mess! */ /* NOTE: Even the word "mess" is not still enough to represent how wrong and bad the Linux video support is, but I don't want to hear complaints from Linux fanatics any more. -okuji */ { char *vga; /* Find the substring "vga=". */ vga = grub_strstr (arg, "vga="); if (vga) { char *value = vga + 4; int vid_mode; /* Handle special strings. */ if (substring ("normal", value) < 1) vid_mode = LINUX_VID_MODE_NORMAL; else if (substring ("ext", value) < 1) vid_mode = LINUX_VID_MODE_EXTENDED; else if (substring ("ask", value) < 1) vid_mode = LINUX_VID_MODE_ASK; else if (safe_parse_maxint (&value, &vid_mode)) ; else { /* ERRNUM is already set inside the function safe_parse_maxint. */ grub_close (); return KERNEL_TYPE_NONE; } lh->vid_mode = vid_mode; } } /* Check the mem= option to limit memory used for initrd. */ { char *mem; mem = grub_strstr (arg, "mem="); if (mem) { char *value = mem + 4; safe_parse_maxint (&value, &linux_mem_size); switch (errnum) { case ERR_NUMBER_OVERFLOW: /* If an overflow occurs, use the maximum address for initrd instead. This is good, because MAXINT is greater than LINUX_INITRD_MAX_ADDRESS. */ linux_mem_size = LINUX_INITRD_MAX_ADDRESS; errnum = ERR_NONE; break; case ERR_NONE: { int shift = 0; switch (grub_tolower (*value)) { case 'g': shift += 10; case 'm': shift += 10; case 'k': shift += 10; default: break; } /* Check an overflow. */ if (linux_mem_size > (MAXINT >> shift)) linux_mem_size = LINUX_INITRD_MAX_ADDRESS; else linux_mem_size <<= shift; } break; default: linux_mem_size = 0; errnum = ERR_NONE; break; } } else linux_mem_size = 0; } /* It is possible that DATA_LEN is greater than MULTIBOOT_SEARCH, so the data may have been read partially. */ if (data_len <= MULTIBOOT_SEARCH) grub_memmove (linux_data_tmp_addr, buffer, data_len + SECTOR_SIZE); else { grub_memmove (linux_data_tmp_addr, buffer, MULTIBOOT_SEARCH); grub_read (linux_data_tmp_addr + MULTIBOOT_SEARCH, data_len + SECTOR_SIZE - MULTIBOOT_SEARCH); } if (lh->header != LINUX_MAGIC_SIGNATURE || lh->version < 0x0200) /* Clear the heap space. */ grub_memset (linux_data_tmp_addr + ((setup_sects + 1) << 9), 0, (64 - setup_sects - 1) << 9); /* Copy command-line plus memory hack to staging area. NOTE: Linux has a bug that it doesn't handle multiple spaces between two options and a space after a "mem=" option isn't removed correctly so the arguments to init could be like {"init", "", "", NULL}. This affects some not-very-clever shells. Thus, the code below does a trick to avoid the bug. That is, copy "mem=XXX" to the end of the command-line, and avoid to copy spaces unnecessarily. Hell. */ { char *src = skip_to (0, arg); char *dest = linux_data_tmp_addr + LINUX_CL_OFFSET; while (dest < linux_data_tmp_addr + LINUX_CL_END_OFFSET && *src) *(dest++) = *(src++); /* Add a mem option automatically only if the user doesn't specify it explicitly. */ if (! grub_strstr (arg, "mem=") && ! (load_flags & KERNEL_LOAD_NO_MEM_OPTION) && dest + 15 < linux_data_tmp_addr + LINUX_CL_END_OFFSET) { *dest++ = ' '; *dest++ = 'm'; *dest++ = 'e'; *dest++ = 'm'; *dest++ = '='; dest = convert_to_ascii (dest, 'u', (extended_memory + 0x400)); *dest++ = 'K'; } *dest = 0; } /* offset into file */ grub_seek (data_len + SECTOR_SIZE); cur_addr = (int) linux_data_tmp_addr + LINUX_SETUP_MOVE_SIZE; grub_read ((char *) LINUX_BZIMAGE_ADDR, text_len); if (errnum == ERR_NONE) { grub_close (); /* Sanity check. */ if (suggested_type != KERNEL_TYPE_NONE && ((big_linux && suggested_type != KERNEL_TYPE_BIG_LINUX) || (! big_linux && suggested_type != KERNEL_TYPE_LINUX))) { errnum = ERR_EXEC_FORMAT; return KERNEL_TYPE_NONE; } /* Ugly hack. */ linux_text_len = text_len; return big_linux ? KERNEL_TYPE_BIG_LINUX : KERNEL_TYPE_LINUX; } } } else /* no recognizable format */ errnum = ERR_EXEC_FORMAT; /* return if error */ if (errnum) { grub_close (); return KERNEL_TYPE_NONE; } /* fill the multiboot info structure */ mbi.cmdline = (int) arg; mbi.mods_count = 0; mbi.mods_addr = 0; mbi.boot_device = (current_drive << 24) | current_partition; mbi.flags &= ~(MB_INFO_MODS | MB_INFO_AOUT_SYMS | MB_INFO_ELF_SHDR); mbi.syms.a.tabsize = 0; mbi.syms.a.strsize = 0; mbi.syms.a.addr = 0; mbi.syms.a.pad = 0; printf (" [%s-%s", str2, str); str = ""; if (exec_type) /* can be loaded like a.out */ { if (flags & MULTIBOOT_AOUT_KLUDGE) str = "-and-data"; printf (", loadaddr=0x%x, text%s=0x%x", cur_addr, str, text_len); /* read text, then read data */ if (grub_read ((char *) RAW_ADDR (cur_addr), text_len) == text_len) { cur_addr += text_len; if (!(flags & MULTIBOOT_AOUT_KLUDGE)) { /* we have to align to a 4K boundary */ if (align_4k) cur_addr = (cur_addr + 0xFFF) & 0xFFFFF000; else printf (", C"); printf (", data=0x%x", data_len); if ((grub_read ((char *) RAW_ADDR (cur_addr), data_len)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -