Merge mainline into mbivid

This commit is contained in:
Vladimir 'phcoder' Serbinenko 2010-09-16 00:22:49 +02:00
commit 9ba27423f5
931 changed files with 122070 additions and 40781 deletions

1991
grub-core/loader/i386/bsd.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,6 @@
#define SUFFIX(x) x ## 32
#define GRUB_TARGET_WORDSIZE 32
#define OBJSYM 0
#include <grub/types.h>
typedef grub_uint32_t grub_freebsd_addr_t;
#include "bsdXX.c"

View file

@ -0,0 +1,6 @@
#define SUFFIX(x) x ## 64
#define GRUB_TARGET_WORDSIZE 64
#define OBJSYM 1
#include <grub/types.h>
typedef grub_uint64_t grub_freebsd_addr_t;
#include "bsdXX.c"

View file

@ -0,0 +1,620 @@
#include <grub/loader.h>
#include <grub/i386/bsd.h>
#include <grub/mm.h>
#include <grub/elf.h>
#include <grub/misc.h>
#include <grub/i386/relocator.h>
#define ALIGN_PAGE(a) ALIGN_UP (a, 4096)
static inline grub_err_t
load (grub_file_t file, void *where, grub_off_t off, grub_size_t size)
{
if (grub_file_seek (file, off) == (grub_off_t) -1)
return grub_errno;
if (grub_file_read (file, where, size) != (grub_ssize_t) size)
{
if (grub_errno)
return grub_errno;
else
return grub_error (GRUB_ERR_BAD_OS, "file is truncated");
}
return GRUB_ERR_NONE;
}
static inline grub_err_t
read_headers (grub_file_t file, Elf_Ehdr *e, char **shdr)
{
if (grub_file_seek (file, 0) == (grub_off_t) -1)
return grub_errno;
if (grub_file_read (file, (char *) e, sizeof (*e)) != sizeof (*e))
{
if (grub_errno)
return grub_errno;
else
return grub_error (GRUB_ERR_BAD_OS, "file is too short");
}
if (e->e_ident[EI_MAG0] != ELFMAG0
|| e->e_ident[EI_MAG1] != ELFMAG1
|| e->e_ident[EI_MAG2] != ELFMAG2
|| e->e_ident[EI_MAG3] != ELFMAG3
|| e->e_ident[EI_VERSION] != EV_CURRENT
|| e->e_version != EV_CURRENT)
return grub_error (GRUB_ERR_BAD_OS, "invalid arch independent ELF magic");
if (e->e_ident[EI_CLASS] != SUFFIX (ELFCLASS))
return grub_error (GRUB_ERR_BAD_OS, "invalid arch dependent ELF magic");
*shdr = grub_malloc (e->e_shnum * e->e_shentsize);
if (! *shdr)
return grub_errno;
if (grub_file_seek (file, e->e_shoff) == (grub_off_t) -1)
return grub_errno;
if (grub_file_read (file, *shdr, e->e_shnum * e->e_shentsize)
!= e->e_shnum * e->e_shentsize)
{
if (grub_errno)
return grub_errno;
else
return grub_error (GRUB_ERR_BAD_OS, "file is truncated");
}
return GRUB_ERR_NONE;
}
/* On i386 FreeBSD uses "elf module" approarch for 32-bit variant
and "elf obj module" for 64-bit variant. However it may differ on other
platforms. So I keep both versions. */
#if OBJSYM
grub_err_t
SUFFIX (grub_freebsd_load_elfmodule_obj) (struct grub_relocator *relocator,
grub_file_t file, int argc,
char *argv[], grub_addr_t *kern_end)
{
Elf_Ehdr e;
Elf_Shdr *s;
char *shdr = 0;
grub_addr_t curload, module;
grub_err_t err;
grub_size_t chunk_size = 0;
void *chunk_src;
err = read_headers (file, &e, &shdr);
if (err)
return err;
curload = module = ALIGN_PAGE (*kern_end);
for (s = (Elf_Shdr *) shdr; s < (Elf_Shdr *) ((char *) shdr
+ e.e_shnum * e.e_shentsize);
s = (Elf_Shdr *) ((char *) s + e.e_shentsize))
{
if (s->sh_size == 0)
continue;
if (s->sh_addralign)
chunk_size = ALIGN_UP (chunk_size + *kern_end, s->sh_addralign)
- *kern_end;
chunk_size += s->sh_size;
}
{
grub_relocator_chunk_t ch;
err = grub_relocator_alloc_chunk_addr (relocator, &ch,
module, chunk_size);
if (err)
return err;
chunk_src = get_virtual_current_address (ch);
}
for (s = (Elf_Shdr *) shdr; s < (Elf_Shdr *) ((char *) shdr
+ e.e_shnum * e.e_shentsize);
s = (Elf_Shdr *) ((char *) s + e.e_shentsize))
{
if (s->sh_size == 0)
continue;
if (s->sh_addralign)
curload = ALIGN_UP (curload, s->sh_addralign);
s->sh_addr = curload;
grub_dprintf ("bsd", "loading section to %x, size %d, align %d\n",
(unsigned) curload, (int) s->sh_size,
(int) s->sh_addralign);
switch (s->sh_type)
{
default:
case SHT_PROGBITS:
err = load (file, (grub_uint8_t *) chunk_src + curload - *kern_end,
s->sh_offset, s->sh_size);
if (err)
return err;
break;
case SHT_NOBITS:
grub_memset ((grub_uint8_t *) chunk_src + curload - *kern_end, 0,
s->sh_size);
break;
}
curload += s->sh_size;
}
*kern_end = ALIGN_PAGE (curload);
err = grub_freebsd_add_meta_module (argv[0], FREEBSD_MODTYPE_ELF_MODULE_OBJ,
argc - 1, argv + 1, module,
curload - module);
if (! err)
err = grub_bsd_add_meta (FREEBSD_MODINFO_METADATA
| FREEBSD_MODINFOMD_ELFHDR,
&e, sizeof (e));
if (! err)
err = grub_bsd_add_meta (FREEBSD_MODINFO_METADATA
| FREEBSD_MODINFOMD_SHDR,
shdr, e.e_shnum * e.e_shentsize);
return err;
}
#else
grub_err_t
SUFFIX (grub_freebsd_load_elfmodule) (struct grub_relocator *relocator,
grub_file_t file, int argc, char *argv[],
grub_addr_t *kern_end)
{
Elf_Ehdr e;
Elf_Shdr *s;
char *shdr = 0;
grub_addr_t curload, module;
grub_err_t err;
grub_size_t chunk_size = 0;
void *chunk_src;
err = read_headers (file, &e, &shdr);
if (err)
return err;
curload = module = ALIGN_PAGE (*kern_end);
for (s = (Elf_Shdr *) shdr; s < (Elf_Shdr *) ((char *) shdr
+ e.e_shnum * e.e_shentsize);
s = (Elf_Shdr *) ((char *) s + e.e_shentsize))
{
if (s->sh_size == 0)
continue;
if (! (s->sh_flags & SHF_ALLOC))
continue;
if (chunk_size < s->sh_addr + s->sh_size)
chunk_size = s->sh_addr + s->sh_size;
}
{
grub_relocator_chunk_t ch;
err = grub_relocator_alloc_chunk_addr (relocator, &ch,
module, chunk_size);
if (err)
return err;
chunk_src = get_virtual_current_address (ch);
}
for (s = (Elf_Shdr *) shdr; s < (Elf_Shdr *) ((char *) shdr
+ e.e_shnum * e.e_shentsize);
s = (Elf_Shdr *) ((char *) s + e.e_shentsize))
{
if (s->sh_size == 0)
continue;
if (! (s->sh_flags & SHF_ALLOC))
continue;
grub_dprintf ("bsd", "loading section to %x, size %d, align %d\n",
(unsigned) curload, (int) s->sh_size,
(int) s->sh_addralign);
switch (s->sh_type)
{
default:
case SHT_PROGBITS:
err = load (file, (grub_uint8_t *) chunk_src + module
+ s->sh_addr - *kern_end,
s->sh_offset, s->sh_size);
if (err)
return err;
break;
case SHT_NOBITS:
grub_memset ((grub_uint8_t *) chunk_src + module
+ s->sh_addr - *kern_end, 0, s->sh_size);
break;
}
if (curload < module + s->sh_addr + s->sh_size)
curload = module + s->sh_addr + s->sh_size;
}
load (file, UINT_TO_PTR (module), 0, sizeof (e));
if (curload < module + sizeof (e))
curload = module + sizeof (e);
load (file, UINT_TO_PTR (curload), e.e_shoff,
e.e_shnum * e.e_shentsize);
e.e_shoff = curload - module;
curload += e.e_shnum * e.e_shentsize;
load (file, UINT_TO_PTR (curload), e.e_phoff,
e.e_phnum * e.e_phentsize);
e.e_phoff = curload - module;
curload += e.e_phnum * e.e_phentsize;
*kern_end = curload;
grub_freebsd_add_meta_module (argv[0], FREEBSD_MODTYPE_ELF_MODULE,
argc - 1, argv + 1, module,
curload - module);
return SUFFIX (grub_freebsd_load_elf_meta) (relocator, file, kern_end);
}
#endif
grub_err_t
SUFFIX (grub_freebsd_load_elf_meta) (struct grub_relocator *relocator,
grub_file_t file, grub_addr_t *kern_end)
{
grub_err_t err;
Elf_Ehdr e;
Elf_Shdr *s;
char *shdr = 0;
unsigned symoff, stroff, symsize, strsize;
grub_freebsd_addr_t symstart, symend, symentsize, dynamic;
Elf_Sym *sym;
void *sym_chunk;
grub_uint8_t *curload;
grub_freebsd_addr_t symtarget;
const char *str;
unsigned i;
grub_size_t chunk_size;
err = read_headers (file, &e, &shdr);
if (err)
return err;
err = grub_bsd_add_meta (FREEBSD_MODINFO_METADATA |
FREEBSD_MODINFOMD_ELFHDR, &e,
sizeof (e));
if (err)
return err;
for (s = (Elf_Shdr *) shdr; s < (Elf_Shdr *) (shdr
+ e.e_shnum * e.e_shentsize);
s = (Elf_Shdr *) ((char *) s + e.e_shentsize))
if (s->sh_type == SHT_SYMTAB)
break;
if (s >= (Elf_Shdr *) ((char *) shdr
+ e.e_shnum * e.e_shentsize))
return grub_error (GRUB_ERR_BAD_OS, "no symbol table");
symoff = s->sh_offset;
symsize = s->sh_size;
symentsize = s->sh_entsize;
s = (Elf_Shdr *) (shdr + e.e_shentsize * s->sh_link);
stroff = s->sh_offset;
strsize = s->sh_size;
chunk_size = ALIGN_UP (symsize + strsize, sizeof (grub_freebsd_addr_t))
+ 2 * sizeof (grub_freebsd_addr_t);
symtarget = ALIGN_UP (*kern_end, sizeof (grub_freebsd_addr_t));
{
grub_relocator_chunk_t ch;
err = grub_relocator_alloc_chunk_addr (relocator, &ch,
symtarget, chunk_size);
if (err)
return err;
sym_chunk = get_virtual_current_address (ch);
}
symstart = symtarget;
symend = symstart + chunk_size;
curload = sym_chunk;
*((grub_freebsd_addr_t *) curload) = symsize;
curload += sizeof (grub_freebsd_addr_t);
if (grub_file_seek (file, symoff) == (grub_off_t) -1)
return grub_errno;
sym = (Elf_Sym *) curload;
if (grub_file_read (file, curload, symsize) != (grub_ssize_t) symsize)
{
if (! grub_errno)
return grub_error (GRUB_ERR_BAD_OS, "invalid ELF");
return grub_errno;
}
curload += symsize;
*((grub_freebsd_addr_t *) curload) = strsize;
curload += sizeof (grub_freebsd_addr_t);
if (grub_file_seek (file, stroff) == (grub_off_t) -1)
return grub_errno;
str = (char *) curload;
if (grub_file_read (file, curload, strsize) != (grub_ssize_t) strsize)
{
if (! grub_errno)
return grub_error (GRUB_ERR_BAD_OS, "invalid ELF");
return grub_errno;
}
for (i = 0;
i * symentsize < symsize;
i++, sym = (Elf_Sym *) ((char *) sym + symentsize))
{
const char *name = str + sym->st_name;
if (grub_strcmp (name, "_DYNAMIC") == 0)
break;
}
if (i * symentsize < symsize)
{
dynamic = sym->st_value;
grub_dprintf ("bsd", "dynamic = %llx\n", (unsigned long long) dynamic);
err = grub_bsd_add_meta (FREEBSD_MODINFO_METADATA |
FREEBSD_MODINFOMD_DYNAMIC, &dynamic,
sizeof (dynamic));
if (err)
return err;
}
err = grub_bsd_add_meta (FREEBSD_MODINFO_METADATA |
FREEBSD_MODINFOMD_SSYM, &symstart,
sizeof (symstart));
if (err)
return err;
err = grub_bsd_add_meta (FREEBSD_MODINFO_METADATA |
FREEBSD_MODINFOMD_ESYM, &symend,
sizeof (symend));
if (err)
return err;
*kern_end = ALIGN_PAGE (symend);
return GRUB_ERR_NONE;
}
grub_err_t
SUFFIX (grub_netbsd_load_elf_meta) (struct grub_relocator *relocator,
grub_file_t file, grub_addr_t *kern_end)
{
grub_err_t err;
Elf_Ehdr e;
Elf_Shdr *s, *symsh, *strsh;
char *shdr;
unsigned symsize, strsize;
Elf_Sym *sym;
void *sym_chunk;
grub_uint8_t *curload;
const char *str;
grub_size_t chunk_size;
Elf_Ehdr *e2;
struct grub_netbsd_btinfo_symtab symtab;
grub_addr_t symtarget;
err = read_headers (file, &e, &shdr);
if (err)
return err;
for (s = (Elf_Shdr *) shdr; s < (Elf_Shdr *) (shdr
+ e.e_shnum * e.e_shentsize);
s = (Elf_Shdr *) ((char *) s + e.e_shentsize))
if (s->sh_type == SHT_SYMTAB)
break;
if (s >= (Elf_Shdr *) ((char *) shdr
+ e.e_shnum * e.e_shentsize))
return GRUB_ERR_NONE;
symsize = s->sh_size;
symsh = s;
s = (Elf_Shdr *) (shdr + e.e_shentsize * s->sh_link);
strsize = s->sh_size;
strsh = s;
chunk_size = ALIGN_UP (symsize, sizeof (grub_freebsd_addr_t))
+ ALIGN_UP (strsize, sizeof (grub_freebsd_addr_t))
+ sizeof (e) + e.e_shnum * e.e_shentsize;
symtarget = ALIGN_UP (*kern_end, sizeof (grub_freebsd_addr_t));
{
grub_relocator_chunk_t ch;
err = grub_relocator_alloc_chunk_addr (relocator, &ch,
symtarget, chunk_size);
if (err)
return err;
sym_chunk = get_virtual_current_address (ch);
}
symtab.nsyms = 1;
symtab.ssyms = symtarget;
symtab.esyms = symtarget + chunk_size;
curload = sym_chunk;
e2 = (Elf_Ehdr *) curload;
grub_memcpy (curload, &e, sizeof (e));
e2->e_phoff = 0;
e2->e_phnum = 0;
e2->e_phentsize = 0;
e2->e_shstrndx = 0;
e2->e_shoff = sizeof (e);
curload += sizeof (e);
for (s = (Elf_Shdr *) shdr; s < (Elf_Shdr *) (shdr
+ e.e_shnum * e.e_shentsize);
s = (Elf_Shdr *) ((char *) s + e.e_shentsize))
{
Elf_Shdr *s2;
s2 = (Elf_Shdr *) curload;
grub_memcpy (curload, s, e.e_shentsize);
if (s == symsh)
s2->sh_offset = sizeof (e) + e.e_shnum * e.e_shentsize;
else if (s == strsh)
s2->sh_offset = ALIGN_UP (symsize, sizeof (grub_freebsd_addr_t))
+ sizeof (e) + e.e_shnum * e.e_shentsize;
else
s2->sh_offset = 0;
s2->sh_addr = s2->sh_offset;
curload += e.e_shentsize;
}
if (grub_file_seek (file, symsh->sh_offset) == (grub_off_t) -1)
return grub_errno;
sym = (Elf_Sym *) curload;
if (grub_file_read (file, curload, symsize) != (grub_ssize_t) symsize)
{
if (! grub_errno)
return grub_error (GRUB_ERR_BAD_OS, "invalid ELF");
return grub_errno;
}
curload += ALIGN_UP (symsize, sizeof (grub_freebsd_addr_t));
if (grub_file_seek (file, strsh->sh_offset) == (grub_off_t) -1)
return grub_errno;
str = (char *) curload;
if (grub_file_read (file, curload, strsize) != (grub_ssize_t) strsize)
{
if (! grub_errno)
return grub_error (GRUB_ERR_BAD_OS, "invalid ELF");
return grub_errno;
}
err = grub_bsd_add_meta (NETBSD_BTINFO_SYMTAB,
&symtab,
sizeof (symtab));
if (err)
return err;
*kern_end = ALIGN_PAGE (symtarget + chunk_size);
return GRUB_ERR_NONE;
}
grub_err_t
SUFFIX(grub_openbsd_find_ramdisk) (grub_file_t file,
grub_addr_t kern_start,
void *kern_chunk_src,
struct grub_openbsd_ramdisk_descriptor *desc)
{
unsigned symoff, stroff, symsize, strsize, symentsize;
{
grub_err_t err;
Elf_Ehdr e;
Elf_Shdr *s;
char *shdr;
err = read_headers (file, &e, &shdr);
if (err)
return err;
for (s = (Elf_Shdr *) shdr; s < (Elf_Shdr *) (shdr
+ e.e_shnum * e.e_shentsize);
s = (Elf_Shdr *) ((char *) s + e.e_shentsize))
if (s->sh_type == SHT_SYMTAB)
break;
if (s >= (Elf_Shdr *) ((char *) shdr + e.e_shnum * e.e_shentsize))
{
grub_free (shdr);
return GRUB_ERR_NONE;
}
symsize = s->sh_size;
symentsize = s->sh_entsize;
symoff = s->sh_offset;
s = (Elf_Shdr *) (shdr + e.e_shentsize * s->sh_link);
stroff = s->sh_offset;
strsize = s->sh_size;
grub_free (shdr);
}
{
Elf_Sym *syms, *sym, *imagesym = NULL, *sizesym = NULL;
unsigned i;
char *strs;
syms = grub_malloc (symsize);
if (!syms)
return grub_errno;
if (grub_file_seek (file, symoff) == (grub_off_t) -1)
{
grub_free (syms);
return grub_errno;
}
if (grub_file_read (file, syms, symsize) != (grub_ssize_t) symsize)
{
grub_free (syms);
if (! grub_errno)
return grub_error (GRUB_ERR_BAD_OS, "invalid ELF");
return grub_errno;
}
strs = grub_malloc (strsize);
if (!strs)
{
grub_free (syms);
return grub_errno;
}
if (grub_file_seek (file, stroff) == (grub_off_t) -1)
return grub_errno;
if (grub_file_read (file, strs, strsize) != (grub_ssize_t) strsize)
{
grub_free (syms);
grub_free (strs);
if (! grub_errno)
return grub_error (GRUB_ERR_BAD_OS, "invalid ELF");
return grub_errno;
}
for (i = 0, sym = syms; i < symsize / symentsize;
i++, sym = (Elf_Sym *) ((char *) sym + symentsize))
{
if (ELF_ST_TYPE (sym->st_info) != STT_OBJECT)
continue;
if (!sym->st_name)
continue;
if (grub_strcmp (strs + sym->st_name, "rd_root_image") == 0)
imagesym = sym;
if (grub_strcmp (strs + sym->st_name, "rd_root_size") == 0)
sizesym = sym;
if (imagesym && sizesym)
break;
}
if (!imagesym || !sizesym)
{
grub_free (syms);
grub_free (strs);
return GRUB_ERR_NONE;
}
if (sizeof (*desc->size) != sizesym->st_size)
{
grub_free (syms);
grub_free (strs);
return grub_error (GRUB_ERR_BAD_OS, "unexpected size of rd_root_size");
}
desc->max_size = imagesym->st_size;
desc->target = (imagesym->st_value & 0xFFFFFF) - kern_start
+ (grub_uint8_t *) kern_chunk_src;
desc->size = (grub_uint32_t *) ((sizesym->st_value & 0xFFFFFF) - kern_start
+ (grub_uint8_t *) kern_chunk_src);
grub_free (syms);
grub_free (strs);
return GRUB_ERR_NONE;
}
}

View file

@ -0,0 +1,45 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 1999,2000,2001,2002,2003,2005,2006,2007,2008, 2009 Free Software Foundation, Inc.
*
* GRUB 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 3 of the License, or
* (at your option) any later version.
*
* GRUB 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 GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/symbol.h>
.p2align 2
.code32
/*
* Use cdecl calling convention for *BSD kernels.
*/
FUNCTION(grub_unix_real_boot)
/* Interrupts should be disabled. */
cli
/* Discard `grub_unix_real_boot' return address. */
popl %eax
/* Fetch `entry' address ... */
popl %eax
/*
* ... and put our return address in its place. The kernel will
* ignore it, but it expects %esp to point to it.
*/
call *%eax

View file

@ -0,0 +1,93 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
* Copyright (C) 2009 Free Software Foundation, Inc.
*
* GRUB 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 3 of the License, or
* (at your option) any later version.
*
* GRUB 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 GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
/* Based on the code from FreeBSD originally distributed under the
following terms: */
/*-
* Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
static void
fill_bsd64_pagetable (grub_uint8_t *src, grub_addr_t target)
{
grub_uint64_t *pt2, *pt3, *pt4;
grub_addr_t pt2t, pt3t, pt4t;
int i;
#define PG_V 0x001
#define PG_RW 0x002
#define PG_U 0x004
#define PG_PS 0x080
pt4 = (grub_uint64_t *) src;
pt3 = (grub_uint64_t *) (src + 4096);
pt2 = (grub_uint64_t *) (src + 8192);
pt4t = target;
pt3t = target + 4096;
pt2t = target + 8192;
grub_memset (src, 0, 4096 * 3);
/*
* This is kinda brutal, but every single 1GB VM memory segment points to
* the same first 1GB of physical memory. But it is how BSD expects
* it to be.
*/
for (i = 0; i < 512; i++)
{
/* Each slot of the level 4 pages points to the same level 3 page */
pt4[i] = (grub_addr_t) pt3t;
pt4[i] |= PG_V | PG_RW | PG_U;
/* Each slot of the level 3 pages points to the same level 2 page */
pt3[i] = (grub_addr_t) pt2t;
pt3[i] |= PG_V | PG_RW | PG_U;
/* The level 2 page slots are mapped with 2MB pages for 1GB. */
pt2[i] = i * (2 * 1024 * 1024);
pt2[i] |= PG_V | PG_RW | PG_PS | PG_U;
}
}

View file

@ -0,0 +1,124 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (c) 2003 Peter Wemm <peter@FreeBSD.org>
* Copyright (C) 2009 Free Software Foundation, Inc.
*
* GRUB 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 3 of the License, or
* (at your option) any later version.
*
* GRUB 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 GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
/* Based on the code from FreeBSD originally distributed under the
following terms: */
/*-
* Copyright (c) 2003 Peter Wemm <peter@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#define MSR_EFER 0xc0000080
#define EFER_LME 0x00000100
#define CR4_PAE 0x00000020
#define CR4_PSE 0x00000010
#define CR0_PG 0x80000000
#include <grub/symbol.h>
.p2align 2
.code32
VARIABLE(grub_bsd64_trampoline_start)
/* Discard `grub_unix_real_boot' return address. */
popl %eax
/* entry */
popl %edi
/* entry_hi */
popl %esi
cli
/* Turn on EFER.LME. */
movl $MSR_EFER, %ecx
rdmsr
orl $EFER_LME, %eax
wrmsr
/* Turn on PAE. */
movl %cr4, %eax
orl $(CR4_PAE | CR4_PSE), %eax
movl %eax, %cr4
/* Set %cr3 for PT4. */
popl %eax
movl %eax, %cr3
/* Push a dummy return address. */
pushl %eax
/* Turn on paging (implicitly sets EFER.LMA). */
movl %cr0, %eax
orl $CR0_PG, %eax
movl %eax, %cr0
/* Now we're in compatibility mode. set %cs for long mode. */
/* lgdt */
.byte 0x0f
.byte 0x01
.byte 0x15
VARIABLE (grub_bsd64_trampoline_gdt)
.long 0x0
/* ljmp */
.byte 0xea
VARIABLE (grub_bsd64_trampoline_selfjump)
.long 0x0
.word 0x08
.code64
bsd64_longmode:
/* We're still running V=P, jump to entry point. */
movl %esi, %eax
salq $32, %rax
orq %rdi, %rax
pushq %rax
ret
VARIABLE(grub_bsd64_trampoline_end)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,178 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2009 Free Software Foundation, Inc.
*
* GRUB 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 3 of the License, or
* (at your option) any later version.
*
* GRUB 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 GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/env.h>
#include <grub/xnu.h>
#include <grub/cpu/xnu.h>
#include <grub/efi/api.h>
#include <grub/efi/efi.h>
#include <grub/efi/uga_draw.h>
#include <grub/pci.h>
#include <grub/misc.h>
/* Setup video for xnu. Big parts are copied from linux.c. */
static grub_efi_guid_t uga_draw_guid = GRUB_EFI_UGA_DRAW_GUID;
#define RGB_MASK 0xffffff
#define RGB_MAGIC 0x121314
#define LINE_MIN 800
#define LINE_MAX 4096
#define FBTEST_STEP (0x10000 >> 2)
#define FBTEST_COUNT 8
static int
find_line_len (grub_uint32_t *fb_base, grub_uint32_t *line_len)
{
grub_uint32_t *base = (grub_uint32_t *) (grub_target_addr_t) *fb_base;
int i;
for (i = 0; i < FBTEST_COUNT; i++, base += FBTEST_STEP)
{
if ((*base & RGB_MASK) == RGB_MAGIC)
{
int j;
for (j = LINE_MIN; j <= LINE_MAX; j++)
{
if ((base[j] & RGB_MASK) == RGB_MAGIC)
{
*fb_base = (grub_uint32_t) (grub_target_addr_t) base;
*line_len = j << 2;
return 1;
}
}
break;
}
}
return 0;
}
static int
find_framebuf (grub_uint32_t *fb_base, grub_uint32_t *line_len)
{
int found = 0;
auto int NESTED_FUNC_ATTR find_card (grub_pci_device_t dev,
grub_pci_id_t pciid);
int NESTED_FUNC_ATTR find_card (grub_pci_device_t dev,
grub_pci_id_t pciid)
{
grub_pci_address_t addr;
addr = grub_pci_make_address (dev, 2);
if (grub_pci_read (addr) >> 24 == 0x3)
{
int i;
grub_printf ("Display controller: %d:%d.%d\nDevice id: %x\n",
grub_pci_get_bus (dev), grub_pci_get_device (dev),
grub_pci_get_function (dev), pciid);
addr += 8;
for (i = 0; i < 6; i++, addr += 4)
{
grub_uint32_t old_bar1, old_bar2, type;
grub_uint64_t base64;
old_bar1 = grub_pci_read (addr);
if ((! old_bar1) || (old_bar1 & GRUB_PCI_ADDR_SPACE_IO))
continue;
type = old_bar1 & GRUB_PCI_ADDR_MEM_TYPE_MASK;
if (type == GRUB_PCI_ADDR_MEM_TYPE_64)
{
if (i == 5)
break;
old_bar2 = grub_pci_read (addr + 4);
}
else
old_bar2 = 0;
base64 = old_bar2;
base64 <<= 32;
base64 |= (old_bar1 & GRUB_PCI_ADDR_MEM_MASK);
grub_printf ("%s(%d): 0x%llx\n",
((old_bar1 & GRUB_PCI_ADDR_MEM_PREFETCH) ?
"VMEM" : "MMIO"), i,
(unsigned long long) base64);
if ((old_bar1 & GRUB_PCI_ADDR_MEM_PREFETCH) && (! found))
{
*fb_base = base64;
if (find_line_len (fb_base, line_len))
found++;
}
if (type == GRUB_PCI_ADDR_MEM_TYPE_64)
{
i++;
addr += 4;
}
}
}
return found;
}
grub_pci_iterate (find_card);
return found;
}
grub_err_t
grub_xnu_set_video (struct grub_xnu_boot_params *params)
{
grub_efi_uga_draw_protocol_t *c;
grub_uint32_t width, height, depth, rate, pixel, fb_base, line_len;
int ret;
c = grub_efi_locate_protocol (&uga_draw_guid, 0);
if (! c)
return grub_error (GRUB_ERR_IO, "couldn't find UGADraw");
if (efi_call_5 (c->get_mode, c, &width, &height, &depth, &rate))
return grub_error (GRUB_ERR_IO, "couldn't retrieve video mode");
grub_printf ("Video mode: %ux%u-%u@%u\n", width, height, depth, rate);
grub_efi_set_text_mode (0);
pixel = RGB_MAGIC;
efi_call_10 (c->blt, c, (struct grub_efi_uga_pixel *) &pixel,
GRUB_EFI_UGA_VIDEO_FILL, 0, 0, 0, 0, 1, height, 0);
ret = find_framebuf (&fb_base, &line_len);
grub_efi_set_text_mode (1);
if (! ret)
return grub_error (GRUB_ERR_IO, "can\'t find frame buffer address");
grub_printf ("Frame buffer base: 0x%x\n", fb_base);
grub_printf ("Video line length: %d\n", line_len);
params->lfb_width = width;
params->lfb_height = height;
params->lfb_depth = depth;
params->lfb_line_len = line_len;
params->lfb_mode = GRUB_XNU_VIDEO_TEXT_IN_VIDEO;
params->lfb_base = fb_base;
return GRUB_ERR_NONE;
}

View file

@ -0,0 +1,311 @@
/* linux.c - boot Linux zImage or bzImage */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2007,2008,2009,2010 Free Software Foundation, Inc.
*
* GRUB 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 3 of the License, or
* (at your option) any later version.
*
* GRUB 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 GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/loader.h>
#include <grub/machine/loader.h>
#include <grub/machine/memory.h>
#include <grub/file.h>
#include <grub/err.h>
#include <grub/disk.h>
#include <grub/misc.h>
#include <grub/types.h>
#include <grub/mm.h>
#include <grub/dl.h>
#include <grub/env.h>
#include <grub/term.h>
#include <grub/cpu/linux.h>
#include <grub/ieee1275/ieee1275.h>
#include <grub/command.h>
#include <grub/i18n.h>
#define GRUB_OFW_LINUX_PARAMS_ADDR 0x90000
#define GRUB_OFW_LINUX_KERNEL_ADDR 0x100000
#define GRUB_OFW_LINUX_INITRD_ADDR 0x800000
#define GRUB_OFW_LINUX_CL_OFFSET 0x1e00
#define GRUB_OFW_LINUX_CL_LENGTH 0x100
static grub_dl_t my_mod;
static grub_size_t kernel_size;
static char *kernel_addr, *kernel_cmdline;
static grub_size_t initrd_size;
static grub_err_t
grub_linux_unload (void)
{
grub_free (kernel_cmdline);
grub_free (kernel_addr);
kernel_cmdline = 0;
kernel_addr = 0;
initrd_size = 0;
grub_dl_unref (my_mod);
return GRUB_ERR_NONE;
}
/*
static int
grub_ieee1275_debug (void)
{
struct enter_args
{
struct grub_ieee1275_common_hdr common;
}
args;
INIT_IEEE1275_COMMON (&args.common, "enter", 0, 0);
if (IEEE1275_CALL_ENTRY_FN (&args) == -1)
return -1;
return 0;
}
*/
static grub_err_t
grub_linux_boot (void)
{
struct linux_kernel_params *params;
struct linux_kernel_header *lh;
char *prot_code;
char *bootpath;
grub_ssize_t len;
bootpath = grub_env_get ("root");
if (bootpath)
grub_ieee1275_set_property (grub_ieee1275_chosen,
"bootpath", bootpath,
grub_strlen (bootpath) + 1,
&len);
params = (struct linux_kernel_params *) GRUB_OFW_LINUX_PARAMS_ADDR;
lh = (struct linux_kernel_header *) params;
grub_memset ((char *) params, 0, GRUB_OFW_LINUX_CL_OFFSET);
params->alt_mem = grub_mmap_get_upper () >> 10;
params->ext_mem = params->alt_mem;
lh->cmd_line_ptr = (char *)
(GRUB_OFW_LINUX_PARAMS_ADDR + GRUB_OFW_LINUX_CL_OFFSET);
params->cl_magic = GRUB_LINUX_CL_MAGIC;
params->cl_offset = GRUB_OFW_LINUX_CL_OFFSET;
{
grub_term_output_t term;
int found = 0;
FOR_ACTIVE_TERM_OUTPUTS(term)
if (grub_strcmp (term->name, "ofconsole") == 0)
{
grub_uint16_t pos = grub_term_getxy (term);
params->video_cursor_x = pos >> 8;
params->video_cursor_y = pos & 0xff;
params->video_width = grub_term_width (term);
params->video_height = grub_term_height (term);
found = 1;
break;
}
if (!found)
{
params->video_cursor_x = 0;
params->video_cursor_y = 0;
params->video_width = 80;
params->video_height = 25;
}
}
params->font_size = 16;
params->ofw_signature = GRUB_LINUX_OFW_SIGNATURE;
params->ofw_num_items = 1;
params->ofw_cif_handler = (grub_uint32_t) grub_ieee1275_entry_fn;
params->ofw_idt = 0;
if (initrd_size)
{
lh->type_of_loader = 1;
lh->ramdisk_image = GRUB_OFW_LINUX_INITRD_ADDR;
lh->ramdisk_size = initrd_size;
}
if (kernel_cmdline)
grub_strcpy (lh->cmd_line_ptr, kernel_cmdline);
prot_code = (char *) GRUB_OFW_LINUX_KERNEL_ADDR;
grub_memcpy (prot_code, kernel_addr, kernel_size);
asm volatile ("movl %0, %%esi" : : "m" (params));
asm volatile ("movl %%esi, %%esp" : : );
asm volatile ("movl %0, %%ecx" : : "m" (prot_code));
asm volatile ("xorl %%ebx, %%ebx" : : );
asm volatile ("jmp *%%ecx" : : );
return GRUB_ERR_NONE;
}
static grub_err_t
grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
int argc, char *argv[])
{
grub_file_t file = 0;
struct linux_kernel_header lh;
grub_uint8_t setup_sects;
grub_size_t real_size, prot_size;
int i;
char *dest;
grub_dl_ref (my_mod);
if (argc == 0)
{
grub_error (GRUB_ERR_BAD_ARGUMENT, "no kernel specified");
goto fail;
}
file = grub_file_open (argv[0]);
if (! file)
goto fail;
if (grub_file_read (file, &lh, sizeof (lh)) != sizeof (lh))
{
grub_error (GRUB_ERR_READ_ERROR, "cannot read the Linux header");
goto fail;
}
if ((lh.boot_flag != grub_cpu_to_le16 (0xaa55)) ||
(lh.header != grub_cpu_to_le32 (GRUB_LINUX_MAGIC_SIGNATURE)))
{
grub_error (GRUB_ERR_BAD_OS, "invalid magic number");
goto fail;
}
setup_sects = lh.setup_sects;
if (! setup_sects)
setup_sects = GRUB_LINUX_DEFAULT_SETUP_SECTS;
real_size = setup_sects << GRUB_DISK_SECTOR_BITS;
prot_size = grub_file_size (file) - real_size - GRUB_DISK_SECTOR_SIZE;
grub_printf (" [Linux-%s, setup=0x%x, size=0x%x]\n",
"bzImage", real_size, prot_size);
grub_file_seek (file, real_size + GRUB_DISK_SECTOR_SIZE);
if (grub_errno)
goto fail;
kernel_cmdline = grub_malloc (GRUB_OFW_LINUX_CL_LENGTH);
if (! kernel_cmdline)
goto fail;
dest = kernel_cmdline;
for (i = 1;
i < argc
&& dest + grub_strlen (argv[i]) + 1 < (kernel_cmdline
+ GRUB_OFW_LINUX_CL_LENGTH);
i++)
{
*dest++ = ' ';
dest = grub_stpcpy (dest, argv[i]);
}
kernel_addr = grub_malloc (prot_size);
if (! kernel_addr)
goto fail;
kernel_size = prot_size;
if (grub_file_read (file, kernel_addr, prot_size) != (int) prot_size)
grub_error (GRUB_ERR_FILE_READ_ERROR, "couldn't read file");
if (grub_errno == GRUB_ERR_NONE)
grub_loader_set (grub_linux_boot, grub_linux_unload, 1);
fail:
if (file)
grub_file_close (file);
if (grub_errno != GRUB_ERR_NONE)
{
grub_free (kernel_cmdline);
grub_free (kernel_addr);
kernel_cmdline = 0;
kernel_addr = 0;
grub_dl_unref (my_mod);
}
return grub_errno;
}
static grub_err_t
grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
int argc, char *argv[])
{
grub_file_t file = 0;
if (argc == 0)
{
grub_error (GRUB_ERR_BAD_ARGUMENT, "no module specified");
goto fail;
}
if (! kernel_addr)
{
grub_error (GRUB_ERR_BAD_ARGUMENT, "you need to load the kernel first");
goto fail;
}
file = grub_file_open (argv[0]);
if (! file)
goto fail;
initrd_size = grub_file_size (file);
if (grub_file_read (file, (void *) GRUB_OFW_LINUX_INITRD_ADDR,
initrd_size) != (int) initrd_size)
{
grub_error (GRUB_ERR_FILE_READ_ERROR, "couldn't read file");
goto fail;
}
fail:
if (file)
grub_file_close (file);
return grub_errno;
}
static grub_command_t cmd_linux, cmd_initrd;
GRUB_MOD_INIT(linux)
{
cmd_linux = grub_register_command ("linux", grub_cmd_linux,
0, N_("Load Linux."));
cmd_initrd = grub_register_command ("initrd", grub_cmd_initrd,
0, N_("Load initrd."));
my_mod = mod;
}
GRUB_MOD_FINI(linux)
{
grub_unregister_command (cmd_linux);
grub_unregister_command (cmd_initrd);
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,129 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2009 Free Software Foundation, Inc.
*
* GRUB 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 3 of the License, or
* (at your option) any later version.
*
* GRUB 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 GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/symbol.h>
.p2align 4 /* force 16-byte alignment */
VARIABLE(grub_linux_trampoline_start)
cli
/* %rdi contains protected memory start and %rsi
contains real memory start. */
mov %rsi, %rbx
call base
base:
pop %rsi
#ifdef APPLE_CC
lea (cont1 - base) (%esi, 1), %rax
mov %eax, (jump_vector - base) (%esi, 1)
lea (gdt - base) (%esi, 1), %rax
mov %rax, (gdtaddr - base) (%esi, 1)
/* Switch to compatibility mode. */
lidt (idtdesc - base) (%esi, 1)
lgdt (gdtdesc - base) (%esi, 1)
/* Update %cs. Thanks to David Miller for pointing this mistake out. */
ljmp *(jump_vector - base) (%esi, 1)
#else
lea (cont1 - base) (%rsi, 1), %rax
mov %eax, (jump_vector - base) (%rsi, 1)
lea (gdt - base) (%rsi, 1), %rax
mov %rax, (gdtaddr - base) (%rsi, 1)
/* Switch to compatibility mode. */
lidt (idtdesc - base) (%rsi, 1)
lgdt (gdtdesc - base) (%rsi, 1)
/* Update %cs. Thanks to David Miller for pointing this mistake out. */
ljmp *(jump_vector - base) (%rsi, 1)
#endif
cont1:
.code32
/* Update other registers. */
mov $0x18, %eax
mov %eax, %ds
mov %eax, %es
mov %eax, %fs
mov %eax, %gs
mov %eax, %ss
/* Disable paging. */
mov %cr0, %eax
and $0x7fffffff, %eax
mov %eax, %cr0
/* Disable amd64. */
mov $0xc0000080, %ecx
rdmsr
and $0xfffffeff, %eax
wrmsr
/* Turn off PAE. */
movl %cr4, %eax
and $0xffffffcf, %eax
mov %eax, %cr4
jmp cont2
cont2:
.code32
mov %ebx, %esi
jmp *%edi
/* GDT. */
.p2align 4
gdt:
/* NULL. */
.byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
/* Reserved. */
.byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
/* Code segment. */
.byte 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x9A, 0xCF, 0x00
/* Data segment. */
.byte 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x92, 0xCF, 0x00
gdtdesc:
.word 31
gdtaddr:
.quad gdt
idtdesc:
.word 0
idtaddr:
.quad 0
.p2align 4
jump_vector:
/* Jump location. Is filled by the code */
.long 0
.long 0x10
VARIABLE(grub_linux_trampoline_end)

View file

@ -0,0 +1,674 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2007,2008,2009 Free Software Foundation, Inc.
*
* GRUB 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 3 of the License, or
* (at your option) any later version.
*
* GRUB 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 GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/machine/memory.h>
#include <grub/memory.h>
#ifdef GRUB_MACHINE_PCBIOS
#include <grub/machine/biosnum.h>
#endif
#include <grub/multiboot.h>
#include <grub/cpu/relocator.h>
#include <grub/disk.h>
#include <grub/device.h>
#include <grub/partition.h>
#include <grub/mm.h>
#include <grub/misc.h>
#include <grub/env.h>
#include <grub/relocator.h>
#include <grub/video.h>
#include <grub/file.h>
/* The bits in the required part of flags field we don't support. */
#define UNSUPPORTED_FLAGS 0x0000fff8
struct module
{
struct module *next;
grub_addr_t start;
grub_size_t size;
char *cmdline;
int cmdline_size;
};
struct module *modules, *modules_last;
static grub_size_t cmdline_size;
static grub_size_t total_modcmd;
static unsigned modcnt;
static char *cmdline = NULL;
static grub_uint32_t bootdev;
static int bootdev_set;
static grub_size_t elf_sec_num, elf_sec_entsize;
static unsigned elf_sec_shstrndx;
static void *elf_sections;
grub_err_t
grub_multiboot_load (grub_file_t file)
{
char *buffer;
grub_ssize_t len;
struct multiboot_header *header;
grub_err_t err;
buffer = grub_malloc (MULTIBOOT_SEARCH);
if (!buffer)
return grub_errno;
len = grub_file_read (file, buffer, MULTIBOOT_SEARCH);
if (len < 32)
{
grub_free (buffer);
return grub_error (GRUB_ERR_BAD_OS, "file too small");
}
/* Look for the multiboot header in the buffer. The header should
be at least 12 bytes and aligned on a 4-byte boundary. */
for (header = (struct multiboot_header *) buffer;
((char *) header <= buffer + len - 12) || (header = 0);
header = (struct multiboot_header *) ((char *) header + MULTIBOOT_HEADER_ALIGN))
{
if (header->magic == MULTIBOOT_HEADER_MAGIC
&& !(header->magic + header->flags + header->checksum))
break;
}
if (header == 0)
{
grub_free (buffer);
return grub_error (GRUB_ERR_BAD_ARGUMENT, "no multiboot header found");
}
if (header->flags & UNSUPPORTED_FLAGS)
{
grub_free (buffer);
return grub_error (GRUB_ERR_UNKNOWN_OS,
"unsupported flag: 0x%x", header->flags);
}
if (header->flags & MULTIBOOT_AOUT_KLUDGE)
{
int offset = ((char *) header - buffer -
(header->header_addr - header->load_addr));
int load_size = ((header->load_end_addr == 0) ? file->size - offset :
header->load_end_addr - header->load_addr);
grub_size_t code_size;
void *source;
grub_relocator_chunk_t ch;
if (header->bss_end_addr)
code_size = (header->bss_end_addr - header->load_addr);
else
code_size = load_size;
err = grub_relocator_alloc_chunk_addr (grub_multiboot_relocator,
&ch, header->load_addr,
code_size);
if (err)
{
grub_dprintf ("multiboot_loader", "Error loading aout kludge\n");
grub_free (buffer);
return err;
}
source = get_virtual_current_address (ch);
if ((grub_file_seek (file, offset)) == (grub_off_t) -1)
{
grub_free (buffer);
return grub_errno;
}
grub_file_read (file, source, load_size);
if (grub_errno)
{
grub_free (buffer);
return grub_errno;
}
if (header->bss_end_addr)
grub_memset ((grub_uint32_t *) source + load_size, 0,
header->bss_end_addr - header->load_addr - load_size);
grub_multiboot_payload_eip = header->entry_addr;
}
else
{
err = grub_multiboot_load_elf (file, buffer);
if (err)
{
grub_free (buffer);
return err;
}
}
if (header->flags & MULTIBOOT_VIDEO_MODE)
{
switch (header->mode_type)
{
case 1:
err = grub_multiboot_set_console (GRUB_MULTIBOOT_CONSOLE_EGA_TEXT,
GRUB_MULTIBOOT_CONSOLE_EGA_TEXT
| GRUB_MULTIBOOT_CONSOLE_FRAMEBUFFER,
0, 0, 0, 0);
break;
case 0:
err = grub_multiboot_set_console (GRUB_MULTIBOOT_CONSOLE_FRAMEBUFFER,
GRUB_MULTIBOOT_CONSOLE_EGA_TEXT
| GRUB_MULTIBOOT_CONSOLE_FRAMEBUFFER,
header->width, header->height,
header->depth, 0);
break;
default:
err = grub_error (GRUB_ERR_BAD_OS,
"unsupported graphical mode type %d",
header->mode_type);
break;
}
}
else
err = grub_multiboot_set_console (GRUB_MULTIBOOT_CONSOLE_EGA_TEXT,
GRUB_MULTIBOOT_CONSOLE_EGA_TEXT,
0, 0, 0, 0);
return err;
}
static grub_size_t
grub_multiboot_get_mbi_size (void)
{
return sizeof (struct multiboot_info) + ALIGN_UP (cmdline_size, 4)
+ modcnt * sizeof (struct multiboot_mod_list) + total_modcmd
+ ALIGN_UP (sizeof(PACKAGE_STRING), 4)
+ grub_get_multiboot_mmap_count () * sizeof (struct multiboot_mmap_entry)
+ elf_sec_entsize * elf_sec_num
#if HAS_VBE
+ sizeof (struct grub_vbe_info_block)
+ sizeof (struct grub_vbe_mode_info_block)
#endif
+ 256 * sizeof (struct multiboot_color);
}
/* Fill previously allocated Multiboot mmap. */
static void
grub_fill_multiboot_mmap (struct multiboot_mmap_entry *first_entry)
{
struct multiboot_mmap_entry *mmap_entry = (struct multiboot_mmap_entry *) first_entry;
auto int NESTED_FUNC_ATTR hook (grub_uint64_t, grub_uint64_t, grub_uint32_t);
int NESTED_FUNC_ATTR hook (grub_uint64_t addr, grub_uint64_t size, grub_uint32_t type)
{
mmap_entry->addr = addr;
mmap_entry->len = size;
switch (type)
{
case GRUB_MACHINE_MEMORY_AVAILABLE:
mmap_entry->type = MULTIBOOT_MEMORY_AVAILABLE;
break;
#ifdef GRUB_MACHINE_MEMORY_ACPI_RECLAIMABLE
case GRUB_MACHINE_MEMORY_ACPI_RECLAIMABLE:
mmap_entry->type = MULTIBOOT_MEMORY_ACPI_RECLAIMABLE;
break;
#endif
#ifdef GRUB_MACHINE_MEMORY_NVS
case GRUB_MACHINE_MEMORY_NVS:
mmap_entry->type = MULTIBOOT_MEMORY_NVS;
break;
#endif
default:
mmap_entry->type = MULTIBOOT_MEMORY_RESERVED;
break;
}
mmap_entry->size = sizeof (struct multiboot_mmap_entry) - sizeof (mmap_entry->size);
mmap_entry++;
return 0;
}
grub_mmap_iterate (hook);
}
#if HAS_VBE
static grub_err_t
fill_vbe_info (struct multiboot_info *mbi, grub_uint8_t *ptrorig,
grub_uint32_t ptrdest, int fill_generic)
{
grub_vbe_status_t status;
grub_uint32_t vbe_mode;
void *scratch = (void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR;
struct grub_vbe_mode_info_block *mode_info;
status = grub_vbe_bios_get_controller_info (scratch);
if (status != GRUB_VBE_STATUS_OK)
return grub_error (GRUB_ERR_IO, "Can't get controller info.");
mbi->vbe_control_info = ptrdest;
grub_memcpy (ptrorig, scratch, sizeof (struct grub_vbe_info_block));
ptrorig += sizeof (struct grub_vbe_info_block);
ptrdest += sizeof (struct grub_vbe_info_block);
status = grub_vbe_bios_get_mode (scratch);
vbe_mode = *(grub_uint32_t *) scratch;
if (status != GRUB_VBE_STATUS_OK)
return grub_error (GRUB_ERR_IO, "can't get VBE mode");
mbi->vbe_mode = vbe_mode;
mode_info = (struct grub_vbe_mode_info_block *) ptrorig;
mbi->vbe_mode_info = ptrdest;
/* get_mode_info isn't available for mode 3. */
if (vbe_mode == 3)
{
grub_memset (mode_info, 0, sizeof (struct grub_vbe_mode_info_block));
mode_info->memory_model = GRUB_VBE_MEMORY_MODEL_TEXT;
mode_info->x_resolution = 80;
mode_info->y_resolution = 25;
}
else
{
status = grub_vbe_bios_get_mode_info (vbe_mode, scratch);
if (status != GRUB_VBE_STATUS_OK)
return grub_error (GRUB_ERR_IO, "can't get mode info");
grub_memcpy (mode_info, scratch,
sizeof (struct grub_vbe_mode_info_block));
}
ptrorig += sizeof (struct grub_vbe_mode_info_block);
ptrdest += sizeof (struct grub_vbe_mode_info_block);
/* FIXME: retrieve those. */
mbi->vbe_interface_seg = 0;
mbi->vbe_interface_off = 0;
mbi->vbe_interface_len = 0;
mbi->flags |= MULTIBOOT_INFO_VBE_INFO;
if (fill_generic && mode_info->memory_model == GRUB_VBE_MEMORY_MODEL_TEXT)
{
mbi->framebuffer_addr = 0xb8000;
mbi->framebuffer_pitch = 2 * mode_info->x_resolution;
mbi->framebuffer_width = mode_info->x_resolution;
mbi->framebuffer_height = mode_info->y_resolution;
mbi->framebuffer_bpp = 16;
mbi->framebuffer_type = MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT;
mbi->flags |= MULTIBOOT_INFO_FRAMEBUFFER_INFO;
}
return GRUB_ERR_NONE;
}
#endif
static grub_err_t
retrieve_video_parameters (struct multiboot_info *mbi,
grub_uint8_t *ptrorig, grub_uint32_t ptrdest)
{
grub_err_t err;
struct grub_video_mode_info mode_info;
void *framebuffer;
grub_video_driver_id_t driv_id;
struct grub_video_palette_data palette[256];
err = grub_multiboot_set_video_mode ();
if (err)
{
grub_print_error ();
grub_errno = GRUB_ERR_NONE;
}
grub_video_get_palette (0, ARRAY_SIZE (palette), palette);
driv_id = grub_video_get_driver_id ();
#if GRUB_MACHINE_HAS_VGA_TEXT
if (driv_id == GRUB_VIDEO_DRIVER_NONE)
return fill_vbe_info (mbi, ptrorig, ptrdest, 1);
#else
if (driv_id == GRUB_VIDEO_DRIVER_NONE)
return GRUB_ERR_NONE;
#endif
err = grub_video_get_info_and_fini (&mode_info, &framebuffer);
if (err)
return err;
mbi->framebuffer_addr = (grub_addr_t) framebuffer;
mbi->framebuffer_pitch = mode_info.pitch;
mbi->framebuffer_width = mode_info.width;
mbi->framebuffer_height = mode_info.height;
mbi->framebuffer_bpp = mode_info.bpp;
if (mode_info.mode_type & GRUB_VIDEO_MODE_TYPE_INDEX_COLOR)
{
struct multiboot_color *mb_palette;
unsigned i;
mbi->framebuffer_type = MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED;
mbi->framebuffer_palette_addr = ptrdest;
mbi->framebuffer_palette_num_colors = mode_info.number_of_colors;
if (mbi->framebuffer_palette_num_colors > ARRAY_SIZE (palette))
mbi->framebuffer_palette_num_colors = ARRAY_SIZE (palette);
mb_palette = (struct multiboot_color *) ptrorig;
for (i = 0; i < mbi->framebuffer_palette_num_colors; i++)
{
mb_palette[i].red = palette[i].r;
mb_palette[i].green = palette[i].g;
mb_palette[i].blue = palette[i].b;
}
ptrorig += mbi->framebuffer_palette_num_colors
* sizeof (struct multiboot_color);
ptrdest += mbi->framebuffer_palette_num_colors
* sizeof (struct multiboot_color);
}
else
{
mbi->framebuffer_type = MULTIBOOT_FRAMEBUFFER_TYPE_RGB;
mbi->framebuffer_red_field_position = mode_info.red_field_pos;
mbi->framebuffer_red_mask_size = mode_info.red_mask_size;
mbi->framebuffer_green_field_position = mode_info.green_field_pos;
mbi->framebuffer_green_mask_size = mode_info.green_mask_size;
mbi->framebuffer_blue_field_position = mode_info.blue_field_pos;
mbi->framebuffer_blue_mask_size = mode_info.blue_mask_size;
}
mbi->flags |= MULTIBOOT_INFO_FRAMEBUFFER_INFO;
#if GRUB_MACHINE_HAS_VBE
if (driv_id == GRUB_VIDEO_DRIVER_VBE)
return fill_vbe_info (mbi, ptrorig, ptrdest, 0);
#endif
return GRUB_ERR_NONE;
}
grub_err_t
grub_multiboot_make_mbi (grub_uint32_t *target)
{
struct multiboot_info *mbi;
struct multiboot_mod_list *modlist;
unsigned i;
struct module *cur;
grub_size_t mmap_size;
grub_uint8_t *ptrorig;
grub_addr_t ptrdest;
grub_err_t err;
grub_size_t bufsize;
grub_relocator_chunk_t ch;
bufsize = grub_multiboot_get_mbi_size ();
err = grub_relocator_alloc_chunk_align (grub_multiboot_relocator, &ch,
0, 0xffffffff - bufsize,
bufsize, 4,
GRUB_RELOCATOR_PREFERENCE_NONE);
if (err)
return err;
ptrorig = get_virtual_current_address (ch);
ptrdest = (grub_addr_t) get_virtual_current_address (ch);
*target = ptrdest;
mbi = (struct multiboot_info *) ptrorig;
ptrorig += sizeof (*mbi);
ptrdest += sizeof (*mbi);
grub_memset (mbi, 0, sizeof (*mbi));
grub_memcpy (ptrorig, cmdline, cmdline_size);
mbi->flags |= MULTIBOOT_INFO_CMDLINE;
mbi->cmdline = ptrdest;
ptrorig += ALIGN_UP (cmdline_size, 4);
ptrdest += ALIGN_UP (cmdline_size, 4);
grub_memcpy (ptrorig, PACKAGE_STRING, sizeof(PACKAGE_STRING));
mbi->flags |= MULTIBOOT_INFO_BOOT_LOADER_NAME;
mbi->boot_loader_name = ptrdest;
ptrorig += ALIGN_UP (sizeof(PACKAGE_STRING), 4);
ptrdest += ALIGN_UP (sizeof(PACKAGE_STRING), 4);
if (modcnt)
{
mbi->flags |= MULTIBOOT_INFO_MODS;
mbi->mods_addr = ptrdest;
mbi->mods_count = modcnt;
modlist = (struct multiboot_mod_list *) ptrorig;
ptrorig += modcnt * sizeof (struct multiboot_mod_list);
ptrdest += modcnt * sizeof (struct multiboot_mod_list);
for (i = 0, cur = modules; i < modcnt; i++, cur = cur->next)
{
modlist[i].mod_start = cur->start;
modlist[i].mod_end = modlist[i].mod_start + cur->size;
modlist[i].cmdline = ptrdest;
grub_memcpy (ptrorig, cur->cmdline, cur->cmdline_size);
ptrorig += ALIGN_UP (cur->cmdline_size, 4);
ptrdest += ALIGN_UP (cur->cmdline_size, 4);
}
}
else
{
mbi->mods_addr = 0;
mbi->mods_count = 0;
}
mmap_size = grub_get_multiboot_mmap_count ()
* sizeof (struct multiboot_mmap_entry);
grub_fill_multiboot_mmap ((struct multiboot_mmap_entry *) ptrorig);
mbi->mmap_length = mmap_size;
mbi->mmap_addr = ptrdest;
mbi->flags |= MULTIBOOT_INFO_MEM_MAP;
ptrorig += mmap_size;
ptrdest += mmap_size;
/* Convert from bytes to kilobytes. */
mbi->mem_lower = grub_mmap_get_lower () / 1024;
mbi->mem_upper = grub_mmap_get_upper () / 1024;
mbi->flags |= MULTIBOOT_INFO_MEMORY;
if (bootdev_set)
{
mbi->boot_device = bootdev;
mbi->flags |= MULTIBOOT_INFO_BOOTDEV;
}
if (elf_sec_num)
{
mbi->u.elf_sec.addr = ptrdest;
grub_memcpy (ptrorig, elf_sections, elf_sec_entsize * elf_sec_num);
mbi->u.elf_sec.num = elf_sec_num;
mbi->u.elf_sec.size = elf_sec_entsize;
mbi->u.elf_sec.shndx = elf_sec_shstrndx;
mbi->flags |= MULTIBOOT_INFO_ELF_SHDR;
}
err = retrieve_video_parameters (mbi, ptrorig, ptrdest);
if (err)
{
grub_print_error ();
grub_errno = GRUB_ERR_NONE;
}
#if GRUB_MACHINE_HAS_VBE
ptrorig += sizeof (struct grub_vbe_info_block);
ptrdest += sizeof (struct grub_vbe_info_block);
ptrorig += sizeof (struct grub_vbe_mode_info_block);
ptrdest += sizeof (struct grub_vbe_mode_info_block);
#endif
return GRUB_ERR_NONE;
}
void
grub_multiboot_add_elfsyms (grub_size_t num, grub_size_t entsize,
unsigned shndx, void *data)
{
elf_sec_num = num;
elf_sec_shstrndx = shndx;
elf_sec_entsize = entsize;
elf_sections = data;
}
void
grub_multiboot_free_mbi (void)
{
struct module *cur, *next;
cmdline_size = 0;
total_modcmd = 0;
modcnt = 0;
grub_free (cmdline);
cmdline = NULL;
bootdev_set = 0;
for (cur = modules; cur; cur = next)
{
next = cur->next;
grub_free (cur->cmdline);
grub_free (cur);
}
modules = NULL;
modules_last = NULL;
grub_free (elf_sections);
elf_sections = NULL;
elf_sec_entsize = 0;
elf_sec_num = 0;
}
grub_err_t
grub_multiboot_init_mbi (int argc, char *argv[])
{
grub_ssize_t len = 0;
char *p;
int i;
grub_multiboot_free_mbi ();
for (i = 0; i < argc; i++)
len += grub_strlen (argv[i]) + 1;
if (len == 0)
len = 1;
cmdline = p = grub_malloc (len);
if (! cmdline)
return grub_errno;
cmdline_size = len;
for (i = 0; i < argc; i++)
{
p = grub_stpcpy (p, argv[i]);
*(p++) = ' ';
}
/* Remove the space after the last word. */
if (p != cmdline)
p--;
*p = '\0';
return GRUB_ERR_NONE;
}
grub_err_t
grub_multiboot_add_module (grub_addr_t start, grub_size_t size,
int argc, char *argv[])
{
struct module *newmod;
char *p;
grub_ssize_t len = 0;
int i;
newmod = grub_malloc (sizeof (*newmod));
if (!newmod)
return grub_errno;
newmod->start = start;
newmod->size = size;
for (i = 0; i < argc; i++)
len += grub_strlen (argv[i]) + 1;
if (len == 0)
len = 1;
newmod->cmdline = p = grub_malloc (len);
if (! newmod->cmdline)
{
grub_free (newmod);
return grub_errno;
}
newmod->cmdline_size = len;
total_modcmd += ALIGN_UP (len, 4);
for (i = 0; i < argc; i++)
{
p = grub_stpcpy (p, argv[i]);
*(p++) = ' ';
}
/* Remove the space after the last word. */
if (p != newmod->cmdline)
p--;
*p = '\0';
if (modules_last)
modules_last->next = newmod;
else
{
modules = newmod;
modules_last->next = NULL;
}
modules_last = newmod;
modcnt++;
return GRUB_ERR_NONE;
}
void
grub_multiboot_set_bootdev (void)
{
grub_uint32_t biosdev, slice = ~0, part = ~0;
grub_device_t dev;
#ifdef GRUB_MACHINE_PCBIOS
biosdev = grub_get_root_biosnumber ();
#else
biosdev = 0xffffffff;
#endif
if (biosdev == 0xffffffff)
return;
dev = grub_device_open (0);
if (dev && dev->disk && dev->disk->partition)
{
if (dev->disk->partition->parent)
{
part = dev->disk->partition->number;
slice = dev->disk->partition->parent->number;
}
else
slice = dev->disk->partition->number;
}
if (dev)
grub_device_close (dev);
bootdev = ((biosdev & 0xff) << 24) | ((slice & 0xff) << 16)
| ((part & 0xff) << 8) | 0xff;
bootdev_set = 1;
}

View file

@ -0,0 +1,174 @@
/* chainloader.c - boot another boot loader */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2002,2004,2007,2009,2010 Free Software Foundation, Inc.
*
* GRUB 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 3 of the License, or
* (at your option) any later version.
*
* GRUB 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 GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/loader.h>
#include <grub/machine/loader.h>
#include <grub/machine/chainloader.h>
#include <grub/file.h>
#include <grub/err.h>
#include <grub/device.h>
#include <grub/disk.h>
#include <grub/misc.h>
#include <grub/types.h>
#include <grub/partition.h>
#include <grub/machine/memory.h>
#include <grub/dl.h>
#include <grub/command.h>
#include <grub/msdos_partition.h>
#include <grub/machine/biosnum.h>
#include <grub/cpu/floppy.h>
#include <grub/i18n.h>
#include <grub/video.h>
#include <grub/mm.h>
static grub_dl_t my_mod;
static int boot_drive;
static void *boot_part_addr;
static grub_err_t
grub_chainloader_boot (void)
{
grub_video_set_mode ("text", 0, 0);
grub_chainloader_real_boot (boot_drive, boot_part_addr);
/* Never reach here. */
return GRUB_ERR_NONE;
}
static grub_err_t
grub_chainloader_unload (void)
{
grub_dl_unref (my_mod);
return GRUB_ERR_NONE;
}
static void
grub_chainloader_cmd (const char *filename, grub_chainloader_flags_t flags)
{
grub_file_t file = 0;
grub_uint16_t signature;
grub_device_t dev;
int drive = -1;
void *part_addr = 0;
grub_dl_ref (my_mod);
grub_file_filter_disable_compression ();
file = grub_file_open (filename);
if (! file)
goto fail;
/* Read the first block. */
if (grub_file_read (file, (void *) 0x7C00, GRUB_DISK_SECTOR_SIZE)
!= GRUB_DISK_SECTOR_SIZE)
{
if (grub_errno == GRUB_ERR_NONE)
grub_error (GRUB_ERR_BAD_OS, "too small");
goto fail;
}
/* Check the signature. */
signature = *((grub_uint16_t *) (0x7C00 + GRUB_DISK_SECTOR_SIZE - 2));
if (signature != grub_le_to_cpu16 (0xaa55)
&& ! (flags & GRUB_CHAINLOADER_FORCE))
{
grub_error (GRUB_ERR_BAD_OS, "invalid signature");
goto fail;
}
grub_file_close (file);
/* Obtain the partition table from the root device. */
drive = grub_get_root_biosnumber ();
dev = grub_device_open (0);
if (dev && dev->disk && dev->disk->partition)
{
grub_disk_t disk = dev->disk;
if (disk)
{
grub_partition_t p = disk->partition;
if (p && grub_strcmp (p->partmap->name, "msdos") == 0)
{
disk->partition = p->parent;
grub_disk_read (disk, p->offset, 446, 64,
(void *) GRUB_MEMORY_MACHINE_PART_TABLE_ADDR);
part_addr = (void *) (GRUB_MEMORY_MACHINE_PART_TABLE_ADDR
+ (p->index << 4));
disk->partition = p;
}
}
}
if (dev)
grub_device_close (dev);
/* Ignore errors. Perhaps it's not fatal. */
grub_errno = GRUB_ERR_NONE;
boot_drive = drive;
boot_part_addr = part_addr;
grub_loader_set (grub_chainloader_boot, grub_chainloader_unload, 1);
return;
fail:
if (file)
grub_file_close (file);
grub_dl_unref (my_mod);
}
static grub_err_t
grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
int argc, char *argv[])
{
grub_chainloader_flags_t flags = 0;
if (argc > 0 && grub_strcmp (argv[0], "--force") == 0)
{
flags |= GRUB_CHAINLOADER_FORCE;
argc--;
argv++;
}
if (argc == 0)
return grub_error (GRUB_ERR_BAD_ARGUMENT, "no file specified");
else
grub_chainloader_cmd (argv[0], flags);
return grub_errno;
}
static grub_command_t cmd;
GRUB_MOD_INIT(chainloader)
{
cmd = grub_register_command ("chainloader", grub_cmd_chainloader,
0, N_("Load another boot loader."));
my_mod = mod;
}
GRUB_MOD_FINI(chainloader)
{
grub_unregister_command (cmd);
}

View file

@ -0,0 +1,455 @@
/* linux.c - boot Linux zImage or bzImage */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2007,2008,2009,2010 Free Software Foundation, Inc.
*
* GRUB 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 3 of the License, or
* (at your option) any later version.
*
* GRUB 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 GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/loader.h>
#include <grub/machine/loader.h>
#include <grub/file.h>
#include <grub/err.h>
#include <grub/device.h>
#include <grub/disk.h>
#include <grub/misc.h>
#include <grub/types.h>
#include <grub/machine/memory.h>
#include <grub/dl.h>
#include <grub/cpu/linux.h>
#include <grub/command.h>
#include <grub/i18n.h>
#include <grub/mm.h>
#include <grub/cpu/relocator.h>
#include <grub/video.h>
#include <grub/i386/floppy.h>
#define GRUB_LINUX_CL_OFFSET 0x9000
#define GRUB_LINUX_CL_END_OFFSET 0x90FF
static grub_dl_t my_mod;
static grub_size_t linux_mem_size;
static int loaded;
static struct grub_relocator *relocator = NULL;
static grub_addr_t grub_linux_real_target;
static char *grub_linux_real_chunk;
static grub_size_t grub_linux16_prot_size;
static grub_err_t
grub_linux16_boot (void)
{
grub_uint16_t segment;
struct grub_relocator16_state state;
segment = grub_linux_real_target >> 4;
state.gs = state.fs = state.es = state.ds = state.ss = segment;
state.sp = GRUB_LINUX_SETUP_STACK;
state.cs = segment + 0x20;
state.ip = 0;
grub_video_set_mode ("text", 0, 0);
grub_stop_floppy ();
return grub_relocator16_boot (relocator, state);
}
static grub_err_t
grub_linux_unload (void)
{
grub_dl_unref (my_mod);
loaded = 0;
grub_relocator_unload (relocator);
relocator = NULL;
return GRUB_ERR_NONE;
}
static grub_err_t
grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
int argc, char *argv[])
{
grub_file_t file = 0;
struct linux_kernel_header lh;
grub_uint8_t setup_sects;
grub_size_t real_size;
grub_ssize_t len;
int i;
char *dest;
char *grub_linux_prot_chunk;
int grub_linux_is_bzimage;
grub_addr_t grub_linux_prot_target;
grub_err_t err;
grub_dl_ref (my_mod);
if (argc == 0)
{
grub_error (GRUB_ERR_BAD_ARGUMENT, "no kernel specified");
goto fail;
}
file = grub_file_open (argv[0]);
if (! file)
goto fail;
if (grub_file_read (file, &lh, sizeof (lh)) != sizeof (lh))
{
grub_error (GRUB_ERR_READ_ERROR, "cannot read the Linux header");
goto fail;
}
if (lh.boot_flag != grub_cpu_to_le16 (0xaa55))
{
grub_error (GRUB_ERR_BAD_OS, "invalid magic number");
goto fail;
}
if (lh.setup_sects > GRUB_LINUX_MAX_SETUP_SECTS)
{
grub_error (GRUB_ERR_BAD_OS, "too many setup sectors");
goto fail;
}
grub_linux_is_bzimage = 0;
setup_sects = lh.setup_sects;
linux_mem_size = 0;
if (lh.header == grub_cpu_to_le32 (GRUB_LINUX_MAGIC_SIGNATURE)
&& grub_le_to_cpu16 (lh.version) >= 0x0200)
{
grub_linux_is_bzimage = (lh.loadflags & GRUB_LINUX_FLAG_BIG_KERNEL);
lh.type_of_loader = GRUB_LINUX_BOOT_LOADER_TYPE;
/* Put the real mode part at as a high location as possible. */
grub_linux_real_target = grub_mmap_get_lower ()
- GRUB_LINUX_SETUP_MOVE_SIZE;
/* But it must not exceed the traditional area. */
if (grub_linux_real_target > GRUB_LINUX_OLD_REAL_MODE_ADDR)
grub_linux_real_target = GRUB_LINUX_OLD_REAL_MODE_ADDR;
if (grub_le_to_cpu16 (lh.version) >= 0x0201)
{
lh.heap_end_ptr = grub_cpu_to_le16 (GRUB_LINUX_HEAP_END_OFFSET);
lh.loadflags |= GRUB_LINUX_FLAG_CAN_USE_HEAP;
}
if (grub_le_to_cpu16 (lh.version) >= 0x0202)
lh.cmd_line_ptr = grub_linux_real_target + GRUB_LINUX_CL_OFFSET;
else
{
lh.cl_magic = grub_cpu_to_le16 (GRUB_LINUX_CL_MAGIC);
lh.cl_offset = grub_cpu_to_le16 (GRUB_LINUX_CL_OFFSET);
lh.setup_move_size = grub_cpu_to_le16 (GRUB_LINUX_SETUP_MOVE_SIZE);
}
}
else
{
/* Your kernel is quite old... */
lh.cl_magic = grub_cpu_to_le16 (GRUB_LINUX_CL_MAGIC);
lh.cl_offset = grub_cpu_to_le16 (GRUB_LINUX_CL_OFFSET);
setup_sects = GRUB_LINUX_DEFAULT_SETUP_SECTS;
grub_linux_real_target = GRUB_LINUX_OLD_REAL_MODE_ADDR;
}
/* If SETUP_SECTS is not set, set it to the default (4). */
if (! setup_sects)
setup_sects = GRUB_LINUX_DEFAULT_SETUP_SECTS;
real_size = setup_sects << GRUB_DISK_SECTOR_BITS;
grub_linux16_prot_size = grub_file_size (file)
- real_size - GRUB_DISK_SECTOR_SIZE;
if (! grub_linux_is_bzimage
&& GRUB_LINUX_ZIMAGE_ADDR + grub_linux16_prot_size
> grub_linux_real_target)
{
grub_error (GRUB_ERR_BAD_OS, "too big zImage (0x%x > 0x%x), use bzImage instead",
(char *) GRUB_LINUX_ZIMAGE_ADDR + grub_linux16_prot_size,
(grub_size_t) grub_linux_real_target);
goto fail;
}
if (grub_linux_real_target + GRUB_LINUX_SETUP_MOVE_SIZE
> grub_mmap_get_lower ())
{
grub_error (GRUB_ERR_OUT_OF_RANGE,
"too small lower memory (0x%x > 0x%x)",
grub_linux_real_target + GRUB_LINUX_SETUP_MOVE_SIZE,
(int) grub_mmap_get_lower ());
goto fail;
}
grub_printf (" [Linux-%s, setup=0x%x, size=0x%x]\n",
grub_linux_is_bzimage ? "bzImage" : "zImage", real_size,
grub_linux16_prot_size);
relocator = grub_relocator_new ();
if (!relocator)
goto fail;
for (i = 1; i < argc; i++)
if (grub_memcmp (argv[i], "vga=", 4) == 0)
{
/* Video mode selection support. */
grub_uint16_t vid_mode;
char *val = argv[i] + 4;
if (grub_strcmp (val, "normal") == 0)
vid_mode = GRUB_LINUX_VID_MODE_NORMAL;
else if (grub_strcmp (val, "ext") == 0)
vid_mode = GRUB_LINUX_VID_MODE_EXTENDED;
else if (grub_strcmp (val, "ask") == 0)
vid_mode = GRUB_LINUX_VID_MODE_ASK;
else
vid_mode = (grub_uint16_t) grub_strtoul (val, 0, 0);
if (grub_errno)
goto fail;
lh.vid_mode = grub_cpu_to_le16 (vid_mode);
}
else if (grub_memcmp (argv[i], "mem=", 4) == 0)
{
char *val = argv[i] + 4;
linux_mem_size = grub_strtoul (val, &val, 0);
if (grub_errno)
{
grub_errno = GRUB_ERR_NONE;
linux_mem_size = 0;
}
else
{
int shift = 0;
switch (grub_tolower (val[0]))
{
case 'g':
shift += 10;
case 'm':
shift += 10;
case 'k':
shift += 10;
default:
break;
}
/* Check an overflow. */
if (linux_mem_size > (~0UL >> shift))
linux_mem_size = 0;
else
linux_mem_size <<= shift;
}
}
{
grub_relocator_chunk_t ch;
err = grub_relocator_alloc_chunk_addr (relocator, &ch,
grub_linux_real_target,
GRUB_LINUX_SETUP_MOVE_SIZE);
if (err)
return err;
grub_linux_real_chunk = get_virtual_current_address (ch);
}
/* Put the real mode code at the temporary address. */
grub_memmove (grub_linux_real_chunk, &lh, sizeof (lh));
len = real_size + GRUB_DISK_SECTOR_SIZE - sizeof (lh);
if (grub_file_read (file, grub_linux_real_chunk + sizeof (lh), len) != len)
{
grub_error (GRUB_ERR_FILE_READ_ERROR, "couldn't read file");
goto fail;
}
if (lh.header != grub_cpu_to_le32 (GRUB_LINUX_MAGIC_SIGNATURE)
|| grub_le_to_cpu16 (lh.version) < 0x0200)
/* Clear the heap space. */
grub_memset (grub_linux_real_chunk
+ ((setup_sects + 1) << GRUB_DISK_SECTOR_BITS),
0,
((GRUB_LINUX_MAX_SETUP_SECTS - setup_sects - 1)
<< GRUB_DISK_SECTOR_BITS));
/* Specify the boot file. */
dest = grub_stpcpy (grub_linux_real_chunk + GRUB_LINUX_CL_OFFSET,
"BOOT_IMAGE=");
dest = grub_stpcpy (dest, argv[0]);
/* Copy kernel parameters. */
for (i = 1;
i < argc
&& dest + grub_strlen (argv[i]) + 1 < (grub_linux_real_chunk
+ GRUB_LINUX_CL_END_OFFSET);
i++)
{
*dest++ = ' ';
dest = grub_stpcpy (dest, argv[i]);
}
if (grub_linux_is_bzimage)
grub_linux_prot_target = GRUB_LINUX_BZIMAGE_ADDR;
else
grub_linux_prot_target = GRUB_LINUX_ZIMAGE_ADDR;
{
grub_relocator_chunk_t ch;
err = grub_relocator_alloc_chunk_addr (relocator, &ch,
grub_linux_prot_target,
grub_linux16_prot_size);
if (err)
return err;
grub_linux_prot_chunk = get_virtual_current_address (ch);
}
len = grub_linux16_prot_size;
if (grub_file_read (file, grub_linux_prot_chunk, grub_linux16_prot_size)
!= (grub_ssize_t) grub_linux16_prot_size)
grub_error (GRUB_ERR_FILE_READ_ERROR, "couldn't read file");
if (grub_errno == GRUB_ERR_NONE)
{
grub_loader_set (grub_linux16_boot, grub_linux_unload, 0);
loaded = 1;
}
fail:
if (file)
grub_file_close (file);
if (grub_errno != GRUB_ERR_NONE)
{
grub_dl_unref (my_mod);
loaded = 0;
grub_relocator_unload (relocator);
}
return grub_errno;
}
static grub_err_t
grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
int argc, char *argv[])
{
grub_file_t file = 0;
grub_ssize_t size;
grub_addr_t addr_max, addr_min;
struct linux_kernel_header *lh;
grub_uint8_t *initrd_chunk;
grub_addr_t initrd_addr;
grub_err_t err;
if (argc == 0)
{
grub_error (GRUB_ERR_BAD_ARGUMENT, "no module specified");
goto fail;
}
if (!loaded)
{
grub_error (GRUB_ERR_BAD_ARGUMENT, "you need to load the kernel first");
goto fail;
}
lh = (struct linux_kernel_header *) grub_linux_real_chunk;
if (!(lh->header == grub_cpu_to_le32 (GRUB_LINUX_MAGIC_SIGNATURE)
&& grub_le_to_cpu16 (lh->version) >= 0x0200))
{
grub_error (GRUB_ERR_BAD_OS, "the kernel is too old for initrd");
goto fail;
}
/* Get the highest address available for the initrd. */
if (grub_le_to_cpu16 (lh->version) >= 0x0203)
{
addr_max = grub_cpu_to_le32 (lh->initrd_addr_max);
/* XXX in reality, Linux specifies a bogus value, so
it is necessary to make sure that ADDR_MAX does not exceed
0x3fffffff. */
if (addr_max > GRUB_LINUX_INITRD_MAX_ADDRESS)
addr_max = GRUB_LINUX_INITRD_MAX_ADDRESS;
}
else
addr_max = GRUB_LINUX_INITRD_MAX_ADDRESS;
if (linux_mem_size != 0 && linux_mem_size < addr_max)
addr_max = linux_mem_size;
/* Linux 2.3.xx has a bug in the memory range check, so avoid
the last page.
Linux 2.2.xx has a bug in the memory range check, which is
worse than that of Linux 2.3.xx, so avoid the last 64kb. */
addr_max -= 0x10000;
addr_min = GRUB_LINUX_BZIMAGE_ADDR + grub_linux16_prot_size;
grub_file_filter_disable_compression ();
file = grub_file_open (argv[0]);
if (!file)
goto fail;
size = grub_file_size (file);
{
grub_relocator_chunk_t ch;
err = grub_relocator_alloc_chunk_align (relocator, &ch,
addr_min, addr_max - size,
size, 0x1000,
GRUB_RELOCATOR_PREFERENCE_HIGH);
if (err)
return err;
initrd_chunk = get_virtual_current_address (ch);
initrd_addr = get_physical_target_address (ch);
}
if (grub_file_read (file, initrd_chunk, size) != size)
{
grub_error (GRUB_ERR_FILE_READ_ERROR, "couldn't read file");
goto fail;
}
lh->ramdisk_image = initrd_addr;
lh->ramdisk_size = size;
fail:
if (file)
grub_file_close (file);
return grub_errno;
}
static grub_command_t cmd_linux, cmd_initrd;
GRUB_MOD_INIT(linux16)
{
cmd_linux =
grub_register_command ("linux16", grub_cmd_linux,
0, N_("Load Linux."));
cmd_initrd =
grub_register_command ("initrd16", grub_cmd_initrd,
0, N_("Load initrd."));
my_mod = mod;
}
GRUB_MOD_FINI(linux16)
{
grub_unregister_command (cmd_linux);
grub_unregister_command (cmd_initrd);
}

View file

@ -0,0 +1,157 @@
/* chainloader.c - boot another boot loader */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2002,2004,2007,2009,2010 Free Software Foundation, Inc.
*
* GRUB 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 3 of the License, or
* (at your option) any later version.
*
* GRUB 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 GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/loader.h>
#include <grub/file.h>
#include <grub/err.h>
#include <grub/device.h>
#include <grub/disk.h>
#include <grub/misc.h>
#include <grub/types.h>
#include <grub/partition.h>
#include <grub/dl.h>
#include <grub/command.h>
#include <grub/machine/biosnum.h>
#include <grub/i18n.h>
#include <grub/video.h>
#include <grub/mm.h>
#include <grub/cpu/relocator.h>
static grub_dl_t my_mod;
static struct grub_relocator *rel;
static grub_uint32_t edx = 0xffffffff;
#define GRUB_NTLDR_SEGMENT 0x2000
static grub_err_t
grub_ntldr_boot (void)
{
struct grub_relocator16_state state = {
.cs = GRUB_NTLDR_SEGMENT,
.ip = 0,
.ds = 0,
.es = 0,
.fs = 0,
.gs = 0,
.ss = 0,
.sp = 0x7c00,
.edx = edx
};
grub_video_set_mode ("text", 0, 0);
return grub_relocator16_boot (rel, state);
}
static grub_err_t
grub_ntldr_unload (void)
{
grub_relocator_unload (rel);
rel = NULL;
grub_dl_unref (my_mod);
return GRUB_ERR_NONE;
}
static grub_err_t
grub_cmd_ntldr (grub_command_t cmd __attribute__ ((unused)),
int argc, char *argv[])
{
grub_file_t file = 0;
grub_err_t err;
void *bs, *ntldr;
grub_size_t ntldrsize;
grub_device_t dev;
if (argc == 0)
return grub_error (GRUB_ERR_BAD_ARGUMENT, "no file specified");
grub_dl_ref (my_mod);
rel = grub_relocator_new ();
if (!rel)
goto fail;
file = grub_file_open (argv[0]);
if (! file)
goto fail;
{
grub_relocator_chunk_t ch;
err = grub_relocator_alloc_chunk_addr (rel, &ch, 0x7C00,
GRUB_DISK_SECTOR_SIZE);
if (err)
goto fail;
bs = get_virtual_current_address (ch);
}
edx = grub_get_root_biosnumber ();
dev = grub_device_open (0);
if (dev && dev->disk)
{
err = grub_disk_read (dev->disk, 0, 0, GRUB_DISK_SECTOR_SIZE, bs);
if (err)
{
grub_device_close (dev);
goto fail;
}
}
if (dev)
grub_device_close (dev);
ntldrsize = grub_file_size (file);
{
grub_relocator_chunk_t ch;
err = grub_relocator_alloc_chunk_addr (rel, &ch, GRUB_NTLDR_SEGMENT << 4,
ntldrsize);
if (err)
goto fail;
ntldr = get_virtual_current_address (ch);
}
if (grub_file_read (file, ntldr, ntldrsize)
!= (grub_ssize_t) ntldrsize)
goto fail;
grub_loader_set (grub_ntldr_boot, grub_ntldr_unload, 1);
return GRUB_ERR_NONE;
fail:
if (file)
grub_file_close (file);
grub_ntldr_unload ();
return grub_errno;
}
static grub_command_t cmd;
GRUB_MOD_INIT(ntldr)
{
cmd = grub_register_command ("ntldr", grub_cmd_ntldr,
0, N_("Load NTLDR or BootMGR."));
my_mod = mod;
}
GRUB_MOD_FINI(ntldr)
{
grub_unregister_command (cmd);
}

View file

@ -0,0 +1,110 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2009 Free Software Foundation, Inc.
*
* GRUB 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 3 of the License, or
* (at your option) any later version.
*
* GRUB 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 GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/env.h>
#include <grub/misc.h>
#include <grub/xnu.h>
#include <grub/mm.h>
#include <grub/cpu/xnu.h>
#include <grub/video_fb.h>
#define min(a,b) (((a) < (b)) ? (a) : (b))
#define max(a,b) (((a) > (b)) ? (a) : (b))
#define DEFAULT_VIDEO_MODE "1024x768x32,800x600x32,640x480x32"
static int NESTED_FUNC_ATTR video_hook (grub_video_adapter_t p __attribute__ ((unused)),
struct grub_video_mode_info *info)
{
if (info->mode_type & GRUB_VIDEO_MODE_TYPE_PURE_TEXT)
return 0;
return 1;
}
/* Setup video for xnu. */
grub_err_t
grub_xnu_set_video (struct grub_xnu_boot_params *params)
{
struct grub_video_mode_info mode_info;
int ret;
char *tmp, *modevar;
void *framebuffer;
grub_err_t err;
modevar = grub_env_get ("gfxpayload");
if (! modevar || *modevar == 0)
err = grub_video_set_mode (DEFAULT_VIDEO_MODE, video_hook);
else
{
tmp = grub_malloc (grub_strlen (modevar)
+ sizeof (DEFAULT_VIDEO_MODE) + 1);
if (! tmp)
return grub_error (GRUB_ERR_OUT_OF_MEMORY,
"couldn't allocate temporary storag");
grub_sprintf (tmp, "%s;" DEFAULT_VIDEO_MODE, modevar);
err = grub_video_set_mode (tmp, video_hook);
grub_free (tmp);
}
if (err)
return err;
if (grub_xnu_bitmap)
{
int x, y;
x = mode_info.width - grub_xnu_bitmap->mode_info.width;
x /= 2;
y = mode_info.height - grub_xnu_bitmap->mode_info.height;
y /= 2;
err = grub_video_blit_bitmap (grub_xnu_bitmap,
GRUB_VIDEO_BLIT_REPLACE,
x > 0 ? x : 0,
y > 0 ? y : 0,
x < 0 ? -x : 0,
y < 0 ? -y : 0,
min (grub_xnu_bitmap->mode_info.width,
mode_info.width),
min (grub_xnu_bitmap->mode_info.height,
mode_info.height));
if (err)
{
grub_print_error ();
grub_errno = GRUB_ERR_NONE;
grub_xnu_bitmap = 0;
}
err = GRUB_ERR_NONE;
}
ret = grub_video_get_info_and_fini (&mode_info, &framebuffer);
if (ret)
return grub_error (GRUB_ERR_IO, "couldn't retrieve video parameters");
params->lfb_width = mode_info.width;
params->lfb_height = mode_info.height;
params->lfb_depth = mode_info.bpp;
params->lfb_line_len = mode_info.pitch;
params->lfb_base = PTR_TO_UINT32 (framebuffer);
params->lfb_mode = grub_xnu_bitmap
? GRUB_XNU_VIDEO_SPLASH : GRUB_XNU_VIDEO_TEXT_IN_VIDEO;
return GRUB_ERR_NONE;
}

1135
grub-core/loader/i386/xnu.c Normal file

File diff suppressed because it is too large Load diff