1N/A/* boot.c - load and bootstrap a kernel */
1N/A/*
1N/A * GRUB -- GRand Unified Bootloader
1N/A * Copyright (C) 1999,2000,2001,2002,2003,2004 Free Software Foundation, Inc.
1N/A *
1N/A * This program is free software; you can redistribute it and/or modify
1N/A * it under the terms of the GNU General Public License as published by
1N/A * the Free Software Foundation; either version 2 of the License, or
1N/A * (at your option) any later version.
1N/A *
1N/A * This program is distributed in the hope that it will be useful,
1N/A * but WITHOUT ANY WARRANTY; without even the implied warranty of
1N/A * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1N/A * GNU General Public License for more details.
1N/A *
1N/A * You should have received a copy of the GNU General Public License
1N/A * along with this program; if not, write to the Free Software
1N/A * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
1N/A */
1N/A
1N/A
1N/A#include "shared.h"
1N/A
1N/A#include "freebsd.h"
1N/A#include "imgact_aout.h"
1N/A#include "i386-elf.h"
1N/A
1N/Astatic int cur_addr;
1N/Aentry_func entry_addr;
1N/Astatic struct mod_list mll[99];
1N/Astatic int linux_mem_size;
1N/A
1N/A/*
1N/A * The next two functions, 'load_image' and 'load_module', are the building
1N/A * blocks of the multiboot loader component. They handle essentially all
1N/A * of the gory details of loading in a bootable image and the modules.
1N/A */
1N/A
1N/Akernel_t
1N/Aload_image (char *kernel, char *arg, kernel_t suggested_type,
1N/A unsigned long load_flags)
1N/A{
1N/A int len, i, exec_type = 0, align_4k = 1;
1N/A entry_func real_entry_addr = 0;
1N/A kernel_t type = KERNEL_TYPE_NONE;
1N/A unsigned long flags = 0, text_len = 0, data_len = 0, bss_len = 0;
1N/A char *str = 0, *str2 = 0;
1N/A struct linux_kernel_header *lh;
1N/A union
1N/A {
1N/A struct multiboot_header *mb;
1N/A struct exec *aout;
1N/A Elf32_Ehdr *elf;
1N/A }
1N/A pu;
1N/A /* presuming that MULTIBOOT_SEARCH is large enough to encompass an
1N/A executable header */
1N/A unsigned char buffer[MULTIBOOT_SEARCH];
1N/A
1N/A /* sets the header pointer to point to the beginning of the
1N/A buffer by default */
1N/A pu.aout = (struct exec *) buffer;
1N/A
1N/A if (!grub_open (kernel))
1N/A return KERNEL_TYPE_NONE;
1N/A
1N/A if (!(len = grub_read (buffer, MULTIBOOT_SEARCH)) || len < 32)
1N/A {
1N/A grub_close ();
1N/A
1N/A if (!errnum)
1N/A errnum = ERR_EXEC_FORMAT;
1N/A
1N/A return KERNEL_TYPE_NONE;
1N/A }
1N/A
1N/A for (i = 0; i < len; i++)
1N/A {
1N/A if (MULTIBOOT_FOUND ((int) (buffer + i), len - i))
1N/A {
1N/A flags = ((struct multiboot_header *) (buffer + i))->flags;
1N/A if (flags & MULTIBOOT_UNSUPPORTED)
1N/A {
1N/A grub_close ();
1N/A errnum = ERR_BOOT_FEATURES;
1N/A return KERNEL_TYPE_NONE;
1N/A }
1N/A type = KERNEL_TYPE_MULTIBOOT;
1N/A str2 = "Multiboot";
1N/A break;
1N/A }
1N/A }
1N/A
1N/A /* Use BUFFER as a linux kernel header, if the image is Linux zImage
1N/A or bzImage. */
1N/A lh = (struct linux_kernel_header *) buffer;
1N/A
1N/A /* ELF loading supported if multiboot, FreeBSD and NetBSD. */
1N/A if ((type == KERNEL_TYPE_MULTIBOOT
1N/A || pu.elf->e_ident[EI_OSABI] == ELFOSABI_FREEBSD
1N/A || grub_strcmp (pu.elf->e_ident + EI_BRAND, "FreeBSD") == 0
1N/A || suggested_type == KERNEL_TYPE_NETBSD)
1N/A && len > sizeof (Elf32_Ehdr)
1N/A && BOOTABLE_I386_ELF ((*((Elf32_Ehdr *) buffer))))
1N/A {
1N/A if (type == KERNEL_TYPE_MULTIBOOT)
1N/A entry_addr = (entry_func) pu.elf->e_entry;
1N/A else
1N/A entry_addr = (entry_func) (pu.elf->e_entry & 0xFFFFFF);
1N/A
1N/A if (entry_addr < (entry_func) 0x100000)
1N/A errnum = ERR_BELOW_1MB;
1N/A
1N/A /* don't want to deal with ELF program header at some random
1N/A place in the file -- this generally won't happen */
1N/A if (pu.elf->e_phoff == 0 || pu.elf->e_phnum == 0
1N/A || ((pu.elf->e_phoff + (pu.elf->e_phentsize * pu.elf->e_phnum))
1N/A >= len))
1N/A errnum = ERR_EXEC_FORMAT;
1N/A str = "elf";
1N/A
1N/A if (type == KERNEL_TYPE_NONE)
1N/A {
1N/A /* At the moment, there is no way to identify a NetBSD ELF
1N/A kernel, so rely on the suggested type by the user. */
1N/A if (suggested_type == KERNEL_TYPE_NETBSD)
1N/A {
1N/A str2 = "NetBSD";
1N/A type = suggested_type;
1N/A }
1N/A else
1N/A {
1N/A str2 = "FreeBSD";
1N/A type = KERNEL_TYPE_FREEBSD;
1N/A }
1N/A }
1N/A }
1N/A else if (flags & MULTIBOOT_AOUT_KLUDGE)
1N/A {
1N/A pu.mb = (struct multiboot_header *) (buffer + i);
1N/A entry_addr = (entry_func) pu.mb->entry_addr;
1N/A cur_addr = pu.mb->load_addr;
1N/A /* first offset into file */
1N/A grub_seek (i - (pu.mb->header_addr - cur_addr));
1N/A
1N/A /* If the load end address is zero, load the whole contents. */
1N/A if (! pu.mb->load_end_addr)
1N/A pu.mb->load_end_addr = cur_addr + filemax;
1N/A
1N/A text_len = pu.mb->load_end_addr - cur_addr;
1N/A data_len = 0;
1N/A
1N/A /* If the bss end address is zero, assume that there is no bss area. */
1N/A if (! pu.mb->bss_end_addr)
1N/A pu.mb->bss_end_addr = pu.mb->load_end_addr;
1N/A
1N/A bss_len = pu.mb->bss_end_addr - pu.mb->load_end_addr;
1N/A
1N/A if (pu.mb->header_addr < pu.mb->load_addr
1N/A || pu.mb->load_end_addr <= pu.mb->load_addr
1N/A || pu.mb->bss_end_addr < pu.mb->load_end_addr
1N/A || (pu.mb->header_addr - pu.mb->load_addr) > i)
1N/A errnum = ERR_EXEC_FORMAT;
1N/A
1N/A if (cur_addr < 0x100000)
1N/A errnum = ERR_BELOW_1MB;
1N/A
1N/A pu.aout = (struct exec *) buffer;
1N/A exec_type = 2;
1N/A str = "kludge";
1N/A }
1N/A else if (len > sizeof (struct exec) && !N_BADMAG ((*(pu.aout))))
1N/A {
1N/A entry_addr = (entry_func) pu.aout->a_entry;
1N/A
1N/A if (type == KERNEL_TYPE_NONE)
1N/A {
1N/A /*
1N/A * If it doesn't have a Multiboot header, then presume
1N/A * it is either a FreeBSD or NetBSD executable. If so,
1N/A * then use a magic number of normal ordering, ZMAGIC to
1N/A * determine if it is FreeBSD.
1N/A *
1N/A * This is all because freebsd and netbsd seem to require
1N/A * masking out some address bits... differently for each
1N/A * one... plus of course we need to know which booting
1N/A * method to use.
1N/A */
1N/A entry_addr = (entry_func) ((int) entry_addr & 0xFFFFFF);
1N/A
1N/A if (buffer[0] == 0xb && buffer[1] == 1)
1N/A {
1N/A type = KERNEL_TYPE_FREEBSD;
1N/A cur_addr = (int) entry_addr;
1N/A str2 = "FreeBSD";
1N/A }
1N/A else
1N/A {
1N/A type = KERNEL_TYPE_NETBSD;
1N/A cur_addr = (int) entry_addr & 0xF00000;
1N/A if (N_GETMAGIC ((*(pu.aout))) != NMAGIC)
1N/A align_4k = 0;
1N/A str2 = "NetBSD";
1N/A }
1N/A }
1N/A
1N/A /* first offset into file */
1N/A grub_seek (N_TXTOFF (*(pu.aout)));
1N/A text_len = pu.aout->a_text;
1N/A data_len = pu.aout->a_data;
1N/A bss_len = pu.aout->a_bss;
1N/A
1N/A if (cur_addr < 0x100000)
1N/A errnum = ERR_BELOW_1MB;
1N/A
1N/A exec_type = 1;
1N/A str = "a.out";
1N/A }
1N/A else if (lh->boot_flag == BOOTSEC_SIGNATURE
1N/A && lh->setup_sects <= LINUX_MAX_SETUP_SECTS)
1N/A {
1N/A int big_linux = 0;
1N/A int setup_sects = lh->setup_sects;
1N/A
1N/A if (lh->header == LINUX_MAGIC_SIGNATURE && lh->version >= 0x0200)
1N/A {
1N/A big_linux = (lh->loadflags & LINUX_FLAG_BIG_KERNEL);
1N/A lh->type_of_loader = LINUX_BOOT_LOADER_TYPE;
1N/A
1N/A /* Put the real mode part at as a high location as possible. */
1N/A linux_data_real_addr
1N/A = (char *) ((mbi.mem_lower << 10) - LINUX_SETUP_MOVE_SIZE);
1N/A /* But it must not exceed the traditional area. */
1N/A if (linux_data_real_addr > (char *) LINUX_OLD_REAL_MODE_ADDR)
1N/A linux_data_real_addr = (char *) LINUX_OLD_REAL_MODE_ADDR;
1N/A
1N/A if (lh->version >= 0x0201)
1N/A {
1N/A lh->heap_end_ptr = LINUX_HEAP_END_OFFSET;
1N/A lh->loadflags |= LINUX_FLAG_CAN_USE_HEAP;
1N/A }
1N/A
1N/A if (lh->version >= 0x0202)
1N/A lh->cmd_line_ptr = linux_data_real_addr + LINUX_CL_OFFSET;
1N/A else
1N/A {
1N/A lh->cl_magic = LINUX_CL_MAGIC;
1N/A lh->cl_offset = LINUX_CL_OFFSET;
1N/A lh->setup_move_size = LINUX_SETUP_MOVE_SIZE;
1N/A }
1N/A }
1N/A else
1N/A {
1N/A /* Your kernel is quite old... */
1N/A lh->cl_magic = LINUX_CL_MAGIC;
1N/A lh->cl_offset = LINUX_CL_OFFSET;
1N/A
1N/A setup_sects = LINUX_DEFAULT_SETUP_SECTS;
1N/A
1N/A linux_data_real_addr = (char *) LINUX_OLD_REAL_MODE_ADDR;
1N/A }
1N/A
1N/A /* If SETUP_SECTS is not set, set it to the default (4). */
1N/A if (! setup_sects)
1N/A setup_sects = LINUX_DEFAULT_SETUP_SECTS;
1N/A
1N/A data_len = setup_sects << 9;
1N/A text_len = filemax - data_len - SECTOR_SIZE;
1N/A
1N/A linux_data_tmp_addr = (char *) LINUX_BZIMAGE_ADDR + text_len;
1N/A
1N/A if (! big_linux
1N/A && text_len > linux_data_real_addr - (char *) LINUX_ZIMAGE_ADDR)
1N/A {
1N/A grub_printf (" linux 'zImage' kernel too big, try 'make bzImage'\n");
1N/A errnum = ERR_WONT_FIT;
1N/A }
1N/A else if (linux_data_real_addr + LINUX_SETUP_MOVE_SIZE
1N/A > RAW_ADDR ((char *) (mbi.mem_lower << 10)))
1N/A errnum = ERR_WONT_FIT;
1N/A else
1N/A {
1N/A grub_printf (" [Linux-%s, setup=0x%x, size=0x%x]\n",
1N/A (big_linux ? "bzImage" : "zImage"), data_len, text_len);
1N/A
1N/A /* Video mode selection support. What a mess! */
1N/A /* NOTE: Even the word "mess" is not still enough to
1N/A represent how wrong and bad the Linux video support is,
1N/A but I don't want to hear complaints from Linux fanatics
1N/A any more. -okuji */
1N/A {
1N/A char *vga;
1N/A
1N/A /* Find the substring "vga=". */
1N/A vga = grub_strstr (arg, "vga=");
1N/A if (vga)
1N/A {
1N/A char *value = vga + 4;
1N/A int vid_mode;
1N/A
1N/A /* Handle special strings. */
1N/A if (substring ("normal", value) < 1)
1N/A vid_mode = LINUX_VID_MODE_NORMAL;
1N/A else if (substring ("ext", value) < 1)
1N/A vid_mode = LINUX_VID_MODE_EXTENDED;
1N/A else if (substring ("ask", value) < 1)
1N/A vid_mode = LINUX_VID_MODE_ASK;
1N/A else if (safe_parse_maxint (&value, &vid_mode))
1N/A ;
1N/A else
1N/A {
1N/A /* ERRNUM is already set inside the function
1N/A safe_parse_maxint. */
1N/A grub_close ();
1N/A return KERNEL_TYPE_NONE;
1N/A }
1N/A
1N/A lh->vid_mode = vid_mode;
1N/A }
1N/A }
1N/A
1N/A /* Check the mem= option to limit memory used for initrd. */
1N/A {
1N/A char *mem;
1N/A
1N/A mem = grub_strstr (arg, "mem=");
1N/A if (mem)
1N/A {
1N/A char *value = mem + 4;
1N/A
1N/A safe_parse_maxint (&value, &linux_mem_size);
1N/A switch (errnum)
1N/A {
1N/A case ERR_NUMBER_OVERFLOW:
1N/A /* If an overflow occurs, use the maximum address for
1N/A initrd instead. This is good, because MAXINT is
1N/A greater than LINUX_INITRD_MAX_ADDRESS. */
1N/A linux_mem_size = LINUX_INITRD_MAX_ADDRESS;
1N/A errnum = ERR_NONE;
1N/A break;
1N/A
1N/A case ERR_NONE:
1N/A {
1N/A int shift = 0;
1N/A
1N/A switch (grub_tolower (*value))
1N/A {
1N/A case 'g':
1N/A shift += 10;
1N/A case 'm':
1N/A shift += 10;
1N/A case 'k':
1N/A shift += 10;
1N/A default:
1N/A break;
1N/A }
1N/A
1N/A /* Check an overflow. */
1N/A if (linux_mem_size > (MAXINT >> shift))
1N/A linux_mem_size = LINUX_INITRD_MAX_ADDRESS;
1N/A else
1N/A linux_mem_size <<= shift;
1N/A }
1N/A break;
1N/A
1N/A default:
1N/A linux_mem_size = 0;
1N/A errnum = ERR_NONE;
1N/A break;
1N/A }
1N/A }
1N/A else
1N/A linux_mem_size = 0;
1N/A }
1N/A
1N/A /* It is possible that DATA_LEN + SECTOR_SIZE is greater than
1N/A MULTIBOOT_SEARCH, so the data may have been read partially. */
1N/A if (data_len + SECTOR_SIZE <= MULTIBOOT_SEARCH)
1N/A grub_memmove (linux_data_tmp_addr, buffer,
1N/A data_len + SECTOR_SIZE);
1N/A else
1N/A {
1N/A grub_memmove (linux_data_tmp_addr, buffer, MULTIBOOT_SEARCH);
1N/A grub_read (linux_data_tmp_addr + MULTIBOOT_SEARCH,
1N/A data_len + SECTOR_SIZE - MULTIBOOT_SEARCH);
1N/A }
1N/A
1N/A if (lh->header != LINUX_MAGIC_SIGNATURE ||
1N/A lh->version < 0x0200)
1N/A /* Clear the heap space. */
1N/A grub_memset (linux_data_tmp_addr + ((setup_sects + 1) << 9),
1N/A 0,
1N/A (64 - setup_sects - 1) << 9);
1N/A
1N/A /* Copy command-line plus memory hack to staging area.
1N/A NOTE: Linux has a bug that it doesn't handle multiple spaces
1N/A between two options and a space after a "mem=" option isn't
1N/A removed correctly so the arguments to init could be like
1N/A {"init", "", "", NULL}. This affects some not-very-clever
1N/A shells. Thus, the code below does a trick to avoid the bug.
1N/A That is, copy "mem=XXX" to the end of the command-line, and
1N/A avoid to copy spaces unnecessarily. Hell. */
1N/A {
1N/A char *src = skip_to (0, arg);
1N/A char *dest = linux_data_tmp_addr + LINUX_CL_OFFSET;
1N/A
1N/A while (dest < linux_data_tmp_addr + LINUX_CL_END_OFFSET && *src)
1N/A *(dest++) = *(src++);
1N/A
1N/A /* Old Linux kernels have problems determining the amount of
1N/A the available memory. To work around this problem, we add
1N/A the "mem" option to the kernel command line. This has its
1N/A own drawbacks because newer kernels can determine the
1N/A memory map more accurately. Boot protocol 2.03, which
1N/A appeared in Linux 2.4.18, provides a pointer to the kernel
1N/A version string, so we could check it. But since kernel
1N/A 2.4.18 and newer are known to detect memory reliably, boot
1N/A protocol 2.03 already implies that the kernel is new
1N/A enough. The "mem" option is added if neither of the
1N/A following conditions is met:
1N/A 1) The "mem" option is already present.
1N/A 2) The "kernel" command is used with "--no-mem-option".
1N/A 3) GNU GRUB is configured not to pass the "mem" option.
1N/A 4) The kernel supports boot protocol 2.03 or newer. */
1N/A if (! grub_strstr (arg, "mem=")
1N/A && ! (load_flags & KERNEL_LOAD_NO_MEM_OPTION)
1N/A && lh->version < 0x0203 /* kernel version < 2.4.18 */
1N/A && dest + 15 < linux_data_tmp_addr + LINUX_CL_END_OFFSET)
1N/A {
1N/A *dest++ = ' ';
1N/A *dest++ = 'm';
1N/A *dest++ = 'e';
1N/A *dest++ = 'm';
1N/A *dest++ = '=';
1N/A
1N/A dest = convert_to_ascii (dest, 'u', (extended_memory + 0x400));
1N/A *dest++ = 'K';
1N/A }
1N/A
1N/A *dest = 0;
1N/A }
1N/A
1N/A /* offset into file */
1N/A grub_seek (data_len + SECTOR_SIZE);
1N/A
1N/A cur_addr = (int) linux_data_tmp_addr + LINUX_SETUP_MOVE_SIZE;
1N/A grub_read ((char *) LINUX_BZIMAGE_ADDR, text_len);
1N/A
1N/A if (errnum == ERR_NONE)
1N/A {
1N/A grub_close ();
1N/A
1N/A /* Sanity check. */
1N/A if (suggested_type != KERNEL_TYPE_NONE
1N/A && ((big_linux && suggested_type != KERNEL_TYPE_BIG_LINUX)
1N/A || (! big_linux && suggested_type != KERNEL_TYPE_LINUX)))
1N/A {
1N/A errnum = ERR_EXEC_FORMAT;
1N/A return KERNEL_TYPE_NONE;
1N/A }
1N/A
1N/A /* Ugly hack. */
1N/A linux_text_len = text_len;
1N/A
1N/A return big_linux ? KERNEL_TYPE_BIG_LINUX : KERNEL_TYPE_LINUX;
1N/A }
1N/A }
1N/A }
1N/A else /* no recognizable format */
1N/A errnum = ERR_EXEC_FORMAT;
1N/A
1N/A /* return if error */
1N/A if (errnum)
1N/A {
1N/A grub_close ();
1N/A return KERNEL_TYPE_NONE;
1N/A }
1N/A
1N/A /* fill the multiboot info structure */
1N/A mbi.cmdline = (int) arg;
1N/A mbi.mods_count = 0;
1N/A mbi.mods_addr = 0;
1N/A mbi.boot_device = (current_drive << 24) | current_partition;
1N/A mbi.flags &= ~(MB_INFO_MODS | MB_INFO_AOUT_SYMS | MB_INFO_ELF_SHDR);
1N/A mbi.syms.a.tabsize = 0;
1N/A mbi.syms.a.strsize = 0;
1N/A mbi.syms.a.addr = 0;
1N/A mbi.syms.a.pad = 0;
1N/A
1N/A printf (" [%s-%s", str2, str);
1N/A
1N/A str = "";
1N/A
1N/A if (exec_type) /* can be loaded like a.out */
1N/A {
1N/A if (flags & MULTIBOOT_AOUT_KLUDGE)
1N/A str = "-and-data";
1N/A
1N/A printf (", loadaddr=0x%x, text%s=0x%x", cur_addr, str, text_len);
1N/A
1N/A /* read text, then read data */
1N/A if (grub_read ((char *) RAW_ADDR (cur_addr), text_len) == text_len)
1N/A {
1N/A cur_addr += text_len;
1N/A
1N/A if (!(flags & MULTIBOOT_AOUT_KLUDGE))
1N/A {
1N/A /* we have to align to a 4K boundary */
1N/A if (align_4k)
1N/A cur_addr = (cur_addr + 0xFFF) & 0xFFFFF000;
1N/A else
1N/A printf (", C");
1N/A
1N/A printf (", data=0x%x", data_len);
1N/A
1N/A if ((grub_read ((char *) RAW_ADDR (cur_addr), data_len)
1N/A != data_len)
1N/A && !errnum)
1N/A errnum = ERR_EXEC_FORMAT;
1N/A cur_addr += data_len;
1N/A }
1N/A
1N/A if (!errnum)
1N/A {
1N/A memset ((char *) RAW_ADDR (cur_addr), 0, bss_len);
1N/A cur_addr += bss_len;
1N/A
1N/A printf (", bss=0x%x", bss_len);
1N/A }
1N/A }
1N/A else if (!errnum)
1N/A errnum = ERR_EXEC_FORMAT;
1N/A
1N/A if (!errnum && pu.aout->a_syms
1N/A && pu.aout->a_syms < (filemax - filepos))
1N/A {
1N/A int symtab_err, orig_addr = cur_addr;
1N/A
1N/A /* we should align to a 4K boundary here for good measure */
1N/A if (align_4k)
1N/A cur_addr = (cur_addr + 0xFFF) & 0xFFFFF000;
1N/A
1N/A mbi.syms.a.addr = cur_addr;
1N/A
1N/A *((int *) RAW_ADDR (cur_addr)) = pu.aout->a_syms;
1N/A cur_addr += sizeof (int);
1N/A
1N/A printf (", symtab=0x%x", pu.aout->a_syms);
1N/A
1N/A if (grub_read ((char *) RAW_ADDR (cur_addr), pu.aout->a_syms)
1N/A == pu.aout->a_syms)
1N/A {
1N/A cur_addr += pu.aout->a_syms;
1N/A mbi.syms.a.tabsize = pu.aout->a_syms;
1N/A
1N/A if (grub_read ((char *) &i, sizeof (int)) == sizeof (int))
1N/A {
1N/A *((int *) RAW_ADDR (cur_addr)) = i;
1N/A cur_addr += sizeof (int);
1N/A
1N/A mbi.syms.a.strsize = i;
1N/A
1N/A i -= sizeof (int);
1N/A
1N/A printf (", strtab=0x%x", i);
1N/A
1N/A symtab_err = (grub_read ((char *) RAW_ADDR (cur_addr), i)
1N/A != i);
1N/A cur_addr += i;
1N/A }
1N/A else
1N/A symtab_err = 1;
1N/A }
1N/A else
1N/A symtab_err = 1;
1N/A
1N/A if (symtab_err)
1N/A {
1N/A printf ("(bad)");
1N/A cur_addr = orig_addr;
1N/A mbi.syms.a.tabsize = 0;
1N/A mbi.syms.a.strsize = 0;
1N/A mbi.syms.a.addr = 0;
1N/A }
1N/A else
1N/A mbi.flags |= MB_INFO_AOUT_SYMS;
1N/A }
1N/A }
1N/A else
1N/A /* ELF executable */
1N/A {
1N/A unsigned loaded = 0, memaddr, memsiz, filesiz;
1N/A Elf32_Phdr *phdr;
1N/A
1N/A /* reset this to zero for now */
1N/A cur_addr = 0;
1N/A
1N/A /* scan for program segments */
1N/A for (i = 0; i < pu.elf->e_phnum; i++)
1N/A {
1N/A phdr = (Elf32_Phdr *)
1N/A (pu.elf->e_phoff + ((int) buffer)
1N/A + (pu.elf->e_phentsize * i));
1N/A if (phdr->p_type == PT_LOAD)
1N/A {
1N/A /* offset into file */
1N/A grub_seek (phdr->p_offset);
1N/A filesiz = phdr->p_filesz;
1N/A
1N/A if (type == KERNEL_TYPE_FREEBSD || type == KERNEL_TYPE_NETBSD)
1N/A memaddr = RAW_ADDR (phdr->p_paddr & 0xFFFFFF);
1N/A else
1N/A memaddr = RAW_ADDR (phdr->p_paddr);
1N/A
1N/A memsiz = phdr->p_memsz;
1N/A if (memaddr < RAW_ADDR (0x100000))
1N/A errnum = ERR_BELOW_1MB;
1N/A
1N/A /* If the memory range contains the entry address, get the
1N/A physical address here. */
1N/A if (type == KERNEL_TYPE_MULTIBOOT
1N/A && (unsigned) entry_addr >= phdr->p_vaddr
1N/A && (unsigned) entry_addr < phdr->p_vaddr + memsiz)
1N/A real_entry_addr = (entry_func) ((unsigned) entry_addr
1N/A + memaddr - phdr->p_vaddr);
1N/A
1N/A /* make sure we only load what we're supposed to! */
1N/A if (filesiz > memsiz)
1N/A filesiz = memsiz;
1N/A /* mark memory as used */
1N/A if (cur_addr < memaddr + memsiz)
1N/A cur_addr = memaddr + memsiz;
1N/A printf (", <0x%x:0x%x:0x%x>", memaddr, filesiz,
1N/A memsiz - filesiz);
1N/A /* increment number of segments */
1N/A loaded++;
1N/A
1N/A /* load the segment */
1N/A if (memcheck (memaddr, memsiz)
1N/A && grub_read ((char *) memaddr, filesiz) == filesiz)
1N/A {
1N/A if (memsiz > filesiz)
1N/A memset ((char *) (memaddr + filesiz), 0, memsiz - filesiz);
1N/A }
1N/A else
1N/A break;
1N/A }
1N/A }
1N/A
1N/A if (! errnum)
1N/A {
1N/A if (! loaded)
1N/A errnum = ERR_EXEC_FORMAT;
1N/A else
1N/A {
1N/A /* Load ELF symbols. */
1N/A Elf32_Shdr *shdr = NULL;
1N/A int tab_size, sec_size;
1N/A int symtab_err = 0;
1N/A
1N/A mbi.syms.e.num = pu.elf->e_shnum;
1N/A mbi.syms.e.size = pu.elf->e_shentsize;
1N/A mbi.syms.e.shndx = pu.elf->e_shstrndx;
1N/A
1N/A /* We should align to a 4K boundary here for good measure. */
1N/A if (align_4k)
1N/A cur_addr = (cur_addr + 0xFFF) & 0xFFFFF000;
1N/A
1N/A tab_size = pu.elf->e_shentsize * pu.elf->e_shnum;
1N/A
1N/A grub_seek (pu.elf->e_shoff);
1N/A /*
1N/A * Should not need to call RAW_ADDR; cur_addr is already
1N/A * adjusted to account for grub_scratch_mem.
1N/A * XXX Linux might calculate cur_addr differently.
1N/A */
1N/A if (grub_read ((char *) (cur_addr), tab_size)
1N/A == tab_size)
1N/A {
1N/A mbi.syms.e.addr = cur_addr;
1N/A shdr = (Elf32_Shdr *) mbi.syms.e.addr;
1N/A cur_addr += tab_size;
1N/A
1N/A printf (", shtab=0x%x", cur_addr);
1N/A
1N/A for (i = 0; i < mbi.syms.e.num; i++)
1N/A {
1N/A /* This section is a loaded section,
1N/A so we don't care. */
1N/A if (shdr[i].sh_addr != 0)
1N/A continue;
1N/A
1N/A /* This section is empty, so we don't care. */
1N/A if (shdr[i].sh_size == 0)
1N/A continue;
1N/A
1N/A /* Align the section to a sh_addralign bits boundary. */
1N/A cur_addr = ((cur_addr + shdr[i].sh_addralign) &
1N/A - (int) shdr[i].sh_addralign);
1N/A
1N/A grub_seek (shdr[i].sh_offset);
1N/A
1N/A sec_size = shdr[i].sh_size;
1N/A
1N/A /*
1N/A * Should not need to call RAW_ADDR; cur_addr is already
1N/A * adjusted to account for grub_scratch_mem.
1N/A * XXX Linux might calculate cur_addr differently.
1N/A */
1N/A if (! (memcheck (cur_addr, sec_size)
1N/A && (grub_read ((char *) (cur_addr),
1N/A sec_size)
1N/A == sec_size)))
1N/A {
1N/A symtab_err = 1;
1N/A break;
1N/A }
1N/A
1N/A shdr[i].sh_addr = cur_addr;
1N/A cur_addr += sec_size;
1N/A }
1N/A }
1N/A else
1N/A symtab_err = 1;
1N/A
1N/A if (mbi.syms.e.addr < RAW_ADDR(0x10000))
1N/A symtab_err = 1;
1N/A
1N/A if (symtab_err)
1N/A {
1N/A printf ("(bad)");
1N/A mbi.syms.e.num = 0;
1N/A mbi.syms.e.size = 0;
1N/A mbi.syms.e.addr = 0;
1N/A mbi.syms.e.shndx = 0;
1N/A cur_addr = 0;
1N/A }
1N/A else
1N/A mbi.flags |= MB_INFO_ELF_SHDR;
1N/A }
1N/A }
1N/A }
1N/A
1N/A if (! errnum)
1N/A {
1N/A grub_printf (", entry=0x%x]\n", (unsigned) entry_addr);
1N/A
1N/A /* If the entry address is physically different from that of the ELF
1N/A header, correct it here. */
1N/A if (real_entry_addr)
1N/A entry_addr = real_entry_addr;
1N/A }
1N/A else
1N/A {
1N/A putchar ('\n');
1N/A type = KERNEL_TYPE_NONE;
1N/A }
1N/A
1N/A grub_close ();
1N/A
1N/A /* Sanity check. */
1N/A if (suggested_type != KERNEL_TYPE_NONE && suggested_type != type)
1N/A {
1N/A errnum = ERR_EXEC_FORMAT;
1N/A return KERNEL_TYPE_NONE;
1N/A }
1N/A
1N/A return type;
1N/A}
1N/A
1N/Aint
1N/Aload_module (char *module, char *arg)
1N/A{
1N/A int len;
1N/A
1N/A /* if we are supposed to load on 4K boundaries */
1N/A cur_addr = (cur_addr + 0xFFF) & 0xFFFFF000;
1N/A
1N/A if (!grub_open (module))
1N/A return 0;
1N/A
1N/A len = grub_read ((char *) cur_addr, -1);
1N/A if (! len)
1N/A {
1N/A grub_close ();
1N/A return 0;
1N/A }
1N/A
1N/A printf (" [Multiboot-module @ 0x%x, 0x%x bytes]\n", cur_addr, len);
1N/A
1N/A /* these two simply need to be set if any modules are loaded at all */
1N/A mbi.flags |= MB_INFO_MODS;
1N/A mbi.mods_addr = (int) mll;
1N/A
1N/A mll[mbi.mods_count].cmdline = (int) arg;
1N/A mll[mbi.mods_count].mod_start = cur_addr;
1N/A cur_addr += len;
1N/A mll[mbi.mods_count].mod_end = cur_addr;
1N/A mll[mbi.mods_count].pad = 0;
1N/A
1N/A /* increment number of modules included */
1N/A mbi.mods_count++;
1N/A
1N/A grub_close ();
1N/A return 1;
1N/A}
1N/A
1N/Aint
1N/Aload_initrd (char *initrd)
1N/A{
1N/A int len;
1N/A unsigned long moveto;
1N/A unsigned long max_addr;
1N/A struct linux_kernel_header *lh
1N/A = (struct linux_kernel_header *) (cur_addr - LINUX_SETUP_MOVE_SIZE);
1N/A
1N/A#ifndef NO_DECOMPRESSION
1N/A no_decompression = 1;
1N/A#endif
1N/A
1N/A if (! grub_open (initrd))
1N/A goto fail;
1N/A
1N/A len = grub_read ((char *) cur_addr, -1);
1N/A if (! len)
1N/A {
1N/A grub_close ();
1N/A goto fail;
1N/A }
1N/A
1N/A if (linux_mem_size)
1N/A moveto = linux_mem_size;
1N/A else
1N/A moveto = (mbi.mem_upper + 0x400) << 10;
1N/A
1N/A moveto = (moveto - len) & 0xfffff000;
1N/A max_addr = (lh->header == LINUX_MAGIC_SIGNATURE && lh->version >= 0x0203
1N/A ? lh->initrd_addr_max : LINUX_INITRD_MAX_ADDRESS);
1N/A if (moveto + len >= max_addr)
1N/A moveto = (max_addr - len) & 0xfffff000;
1N/A
1N/A /* XXX: Linux 2.3.xx has a bug in the memory range check, so avoid
1N/A the last page.
1N/A XXX: Linux 2.2.xx has a bug in the memory range check, which is
1N/A worse than that of Linux 2.3.xx, so avoid the last 64kb. *sigh* */
1N/A moveto -= 0x10000;
1N/A memmove ((void *) RAW_ADDR (moveto), (void *) cur_addr, len);
1N/A
1N/A printf (" [Linux-initrd @ 0x%x, 0x%x bytes]\n", moveto, len);
1N/A
1N/A /* FIXME: Should check if the kernel supports INITRD. */
1N/A lh->ramdisk_image = RAW_ADDR (moveto);
1N/A lh->ramdisk_size = len;
1N/A
1N/A grub_close ();
1N/A
1N/A fail:
1N/A
1N/A#ifndef NO_DECOMPRESSION
1N/A no_decompression = 0;
1N/A#endif
1N/A
1N/A return ! errnum;
1N/A}
1N/A
1N/A
1N/A#ifdef GRUB_UTIL
1N/A/* Dummy function to fake the *BSD boot. */
1N/Astatic void
1N/Absd_boot_entry (int flags, int bootdev, int sym_start, int sym_end,
1N/A int mem_upper, int mem_lower)
1N/A{
1N/A stop ();
1N/A}
1N/A#endif
1N/A
1N/A
1N/A/*
1N/A * All "*_boot" commands depend on the images being loaded into memory
1N/A * correctly, the variables in this file being set up correctly, and
1N/A * the root partition being set in the 'saved_drive' and 'saved_partition'
1N/A * variables.
1N/A */
1N/A
1N/A
1N/Avoid
1N/Absd_boot (kernel_t type, int bootdev, char *arg)
1N/A{
1N/A char *str;
1N/A int clval = 0, i;
1N/A struct bootinfo bi;
1N/A
1N/A#ifdef GRUB_UTIL
1N/A entry_addr = (entry_func) bsd_boot_entry;
1N/A#else
1N/A stop_floppy ();
1N/A#endif
1N/A
1N/A while (*(++arg) && *arg != ' ');
1N/A str = arg;
1N/A while (*str)
1N/A {
1N/A if (*str == '-')
1N/A {
1N/A while (*str && *str != ' ')
1N/A {
1N/A if (*str == 'C')
1N/A clval |= RB_CDROM;
1N/A if (*str == 'a')
1N/A clval |= RB_ASKNAME;
1N/A if (*str == 'b')
1N/A clval |= RB_HALT;
1N/A if (*str == 'c')
1N/A clval |= RB_CONFIG;
1N/A if (*str == 'd')
1N/A clval |= RB_KDB;
1N/A if (*str == 'D')
1N/A clval |= RB_MULTIPLE;
1N/A if (*str == 'g')
1N/A clval |= RB_GDB;
1N/A if (*str == 'h')
1N/A clval |= RB_SERIAL;
1N/A if (*str == 'm')
1N/A clval |= RB_MUTE;
1N/A if (*str == 'r')
1N/A clval |= RB_DFLTROOT;
1N/A if (*str == 's')
1N/A clval |= RB_SINGLE;
1N/A if (*str == 'v')
1N/A clval |= RB_VERBOSE;
1N/A str++;
1N/A }
1N/A continue;
1N/A }
1N/A str++;
1N/A }
1N/A
1N/A if (type == KERNEL_TYPE_FREEBSD)
1N/A {
1N/A clval |= RB_BOOTINFO;
1N/A
1N/A bi.bi_version = BOOTINFO_VERSION;
1N/A
1N/A *arg = 0;
1N/A while ((--arg) > (char *) MB_CMDLINE_BUF && *arg != '/');
1N/A if (*arg == '/')
1N/A bi.bi_kernelname = arg + 1;
1N/A else
1N/A bi.bi_kernelname = 0;
1N/A
1N/A bi.bi_nfs_diskless = 0;
1N/A bi.bi_n_bios_used = 0; /* this field is apparently unused */
1N/A
1N/A for (i = 0; i < N_BIOS_GEOM; i++)
1N/A {
1N/A struct geometry geom;
1N/A
1N/A /* XXX Should check the return value. */
1N/A get_diskinfo (i + 0x80, &geom);
1N/A /* FIXME: If HEADS or SECTORS is greater than 255, then this will
1N/A break the geometry information. That is a drawback of BSD
1N/A but not of GRUB. */
1N/A bi.bi_bios_geom[i] = (((geom.cylinders - 1) << 16)
1N/A + (((geom.heads - 1) & 0xff) << 8)
1N/A + (geom.sectors & 0xff));
1N/A }
1N/A
1N/A bi.bi_size = sizeof (struct bootinfo);
1N/A bi.bi_memsizes_valid = 1;
1N/A bi.bi_bios_dev = saved_drive;
1N/A bi.bi_basemem = mbi.mem_lower;
1N/A bi.bi_extmem = extended_memory;
1N/A
1N/A if (mbi.flags & MB_INFO_AOUT_SYMS)
1N/A {
1N/A bi.bi_symtab = mbi.syms.a.addr;
1N/A bi.bi_esymtab = mbi.syms.a.addr + 4
1N/A + mbi.syms.a.tabsize + mbi.syms.a.strsize;
1N/A }
1N/A#if 0
1N/A else if (mbi.flags & MB_INFO_ELF_SHDR)
1N/A {
1N/A /* FIXME: Should check if a symbol table exists and, if exists,
1N/A pass the table to BI. */
1N/A }
1N/A#endif
1N/A else
1N/A {
1N/A bi.bi_symtab = 0;
1N/A bi.bi_esymtab = 0;
1N/A }
1N/A
1N/A /* call entry point */
1N/A (*entry_addr) (clval, bootdev, 0, 0, 0, ((int) (&bi)));
1N/A }
1N/A else
1N/A {
1N/A /*
1N/A * We now pass the various bootstrap parameters to the loaded
1N/A * image via the argument list.
1N/A *
1N/A * This is the official list:
1N/A *
1N/A * arg0 = 8 (magic)
1N/A * arg1 = boot flags
1N/A * arg2 = boot device
1N/A * arg3 = start of symbol table (0 if not loaded)
1N/A * arg4 = end of symbol table (0 if not loaded)
1N/A * arg5 = transfer address from image
1N/A * arg6 = transfer address for next image pointer
1N/A * arg7 = conventional memory size (640)
1N/A * arg8 = extended memory size (8196)
1N/A *
1N/A * ...in actuality, we just pass the parameters used by the kernel.
1N/A */
1N/A
1N/A /* call entry point */
1N/A unsigned long end_mark;
1N/A
1N/A if (mbi.flags & MB_INFO_AOUT_SYMS)
1N/A end_mark = (mbi.syms.a.addr + 4
1N/A + mbi.syms.a.tabsize + mbi.syms.a.strsize);
1N/A else
1N/A /* FIXME: it should be mbi.syms.e.size. */
1N/A end_mark = 0;
1N/A
1N/A (*entry_addr) (clval, bootdev, 0, end_mark,
1N/A extended_memory, mbi.mem_lower);
1N/A }
1N/A}