⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 boot.c

📁 i386的bootloader源码grub
💻 C
📖 第 1 页 / 共 2 页
字号:
/* boot.c - load and bootstrap a kernel *//* *  GRUB  --  GRand Unified Bootloader *  Copyright (C) 1999,2000,2001,2002,2003,2004  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 + SECTOR_SIZE is greater than	     MULTIBOOT_SEARCH, so the data may have been read partially.  */	  if (data_len + SECTOR_SIZE <= 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++);		    /* Old Linux kernels have problems determining the amount of	       the available memory.  To work around this problem, we add	       the "mem" option to the kernel command line.  This has its	       own drawbacks because newer kernels can determine the	       memory map more accurately.  Boot protocol 2.03, which	       appeared in Linux 2.4.18, provides a pointer to the kernel	       version string, so we could check it.  But since kernel	       2.4.18 and newer are known to detect memory reliably, boot	       protocol 2.03 already implies that the kernel is new	       enough.  The "mem" option is added if neither of the	       following conditions is met:	       1) The "mem" option is already present.	       2) The "kernel" command is used with "--no-mem-option".	       3) GNU GRUB is configured not to pass the "mem" option.	       4) The kernel supports boot protocol 2.03 or newer.  */	    if (! grub_strstr (arg, "mem=")		&& ! (load_flags & KERNEL_LOAD_NO_MEM_OPTION)		&& lh->version < 0x0203		/* kernel version < 2.4.18 */		&& 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

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -