grub/grub-core/loader/i386/bsd.c
Peter Jones d5a32255de misc: Make grub_strtol() "end" pointers have safer const qualifiers
Currently the string functions grub_strtol(), grub_strtoul(), and
grub_strtoull() don't declare the "end" pointer in such a way as to
require the pointer itself or the character array to be immutable to the
implementation, nor does the C standard do so in its similar functions,
though it does require us not to change any of it.

The typical declarations of these functions follow this pattern:

long
strtol(const char * restrict nptr, char ** restrict endptr, int base);

Much of the reason for this is historic, and a discussion of that
follows below, after the explanation of this change.  (GRUB currently
does not include the "restrict" qualifiers, and we name the arguments a
bit differently.)

The implementation is semantically required to treat the character array
as immutable, but such accidental modifications aren't stopped by the
compiler, and the semantics for both the callers and the implementation
of these functions are sometimes also helped by adding that requirement.

This patch changes these declarations to follow this pattern instead:

long
strtol(const char * restrict nptr,
       const char ** const restrict endptr,
       int base);

This means that if any modification to these functions accidentally
introduces either an errant modification to the underlying character
array, or an accidental assignment to endptr rather than *endptr, the
compiler should generate an error.  (The two uses of "restrict" in this
case basically mean strtol() isn't allowed to modify the character array
by going through *endptr, and endptr isn't allowed to point inside the
array.)

It also means the typical use case changes to:

  char *s = ...;
  const char *end;
  long l;

  l = strtol(s, &end, 10);

Or even:

  const char *p = str;
  while (p && *p) {
	  long l = strtol(p, &p, 10);
	  ...
  }

This fixes 26 places where we discard our attempts at treating the data
safely by doing:

  const char *p = str;
  long l;

  l = strtol(p, (char **)&ptr, 10);

It also adds 5 places where we do:

  char *p = str;
  while (p && *p) {
	  long l = strtol(p, (const char ** const)&p, 10);
	  ...
	  /* more calls that need p not to be pointer-to-const */
  }

While moderately distasteful, this is a better problem to have.

With one minor exception, I have tested that all of this compiles
without relevant warnings or errors, and that /much/ of it behaves
correctly, with gcc 9 using 'gcc -W -Wall -Wextra'.  The one exception
is the changes in grub-core/osdep/aros/hostdisk.c , which I have no idea
how to build.

Because the C standard defined type-qualifiers in a way that can be
confusing, in the past there's been a slow but fairly regular stream of
churn within our patches, which add and remove the const qualifier in many
of the users of these functions.  This change should help avoid that in
the future, and in order to help ensure this, I've added an explanation
in misc.h so that when someone does get a compiler warning about a type
error, they have the fix at hand.

The reason we don't have "const" in these calls in the standard is
purely anachronistic: C78 (de facto) did not have type qualifiers in the
syntax, and the "const" type qualifier was added for C89 (I think; it
may have been later).  strtol() appears to date from 4.3BSD in 1986,
which means it could not be added to those functions in the standard
without breaking compatibility, which is usually avoided.

The syntax chosen for type qualifiers is what has led to the churn
regarding usage of const, and is especially confusing on string
functions due to the lack of a string type.  Quoting from C99, the
syntax is:

 declarator:
  pointer[opt] direct-declarator
 direct-declarator:
  identifier
  ( declarator )
  direct-declarator [ type-qualifier-list[opt] assignment-expression[opt] ]
  ...
  direct-declarator [ type-qualifier-list[opt] * ]
  ...
 pointer:
  * type-qualifier-list[opt]
  * type-qualifier-list[opt] pointer
 type-qualifier-list:
  type-qualifier
  type-qualifier-list type-qualifier
 ...
 type-qualifier:
  const
  restrict
  volatile

So the examples go like:

const char foo;			// immutable object
const char *foo;		// mutable pointer to object
char * const foo;		// immutable pointer to mutable object
const char * const foo;		// immutable pointer to immutable object
const char const * const foo; 	// XXX extra const keyword in the middle
const char * const * const foo; // immutable pointer to immutable
				//   pointer to immutable object
const char ** const foo;	// immutable pointer to mutable pointer
				//   to immutable object

Making const left-associative for * and right-associative for everything
else may not have been the best choice ever, but here we are, and the
inevitable result is people using trying to use const (as they should!),
putting it at the wrong place, fighting with the compiler for a bit, and
then either removing it or typecasting something in a bad way.  I won't
go into describing restrict, but its syntax has exactly the same issue
as with const.

Anyway, the last example above actually represents the *behavior* that's
required of strtol()-like functions, so that's our choice for the "end"
pointer.

Signed-off-by: Peter Jones <pjones@redhat.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
2020-02-28 12:41:29 +01:00

2184 lines
53 KiB
C

/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 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/i386/bsd.h>
#include <grub/i386/cpuid.h>
#include <grub/memory.h>
#include <grub/i386/memory.h>
#include <grub/file.h>
#include <grub/err.h>
#include <grub/dl.h>
#include <grub/mm.h>
#include <grub/elfload.h>
#include <grub/env.h>
#include <grub/misc.h>
#include <grub/aout.h>
#include <grub/command.h>
#include <grub/extcmd.h>
#include <grub/i18n.h>
#include <grub/ns8250.h>
#include <grub/bsdlabel.h>
#include <grub/crypto.h>
#include <grub/verify.h>
#ifdef GRUB_MACHINE_PCBIOS
#include <grub/machine/int.h>
#endif
GRUB_MOD_LICENSE ("GPLv3+");
#include <grub/video.h>
#ifdef GRUB_MACHINE_PCBIOS
#include <grub/machine/biosnum.h>
#endif
#ifdef GRUB_MACHINE_EFI
#include <grub/efi/efi.h>
#define NETBSD_DEFAULT_VIDEO_MODE "800x600"
#else
#define NETBSD_DEFAULT_VIDEO_MODE "text"
#include <grub/i386/pc/vbe.h>
#endif
#include <grub/video.h>
#include <grub/disk.h>
#include <grub/device.h>
#include <grub/partition.h>
#include <grub/relocator.h>
#include <grub/i386/relocator.h>
#define ALIGN_DWORD(a) ALIGN_UP (a, 4)
#define ALIGN_QWORD(a) ALIGN_UP (a, 8)
#define ALIGN_VAR(a) ((is_64bit) ? (ALIGN_QWORD(a)) : (ALIGN_DWORD(a)))
#define ALIGN_PAGE(a) ALIGN_UP (a, 4096)
static int kernel_type = KERNEL_TYPE_NONE;
static grub_dl_t my_mod;
static grub_addr_t entry, entry_hi, kern_start, kern_end;
static void *kern_chunk_src;
static grub_uint32_t bootflags;
static int is_elf_kernel, is_64bit;
static grub_uint32_t openbsd_root;
static struct grub_relocator *relocator = NULL;
static struct grub_openbsd_ramdisk_descriptor openbsd_ramdisk;
struct bsd_tag
{
struct bsd_tag *next;
grub_size_t len;
grub_uint32_t type;
union {
grub_uint8_t a;
grub_uint16_t b;
grub_uint32_t c;
} data[0];
};
static struct bsd_tag *tags, *tags_last;
struct netbsd_module
{
struct netbsd_module *next;
struct grub_netbsd_btinfo_module mod;
};
static struct netbsd_module *netbsd_mods, *netbsd_mods_last;
static const struct grub_arg_option freebsd_opts[] =
{
{"dual", 'D', 0, N_("Display output on all consoles."), 0, 0},
{"serial", 'h', 0, N_("Use serial console."), 0, 0},
{"askname", 'a', 0, N_("Ask for file name to reboot from."), 0, 0},
{"cdrom", 'C', 0, N_("Use CD-ROM as root."), 0, 0},
{"config", 'c', 0, N_("Invoke user configuration routing."), 0, 0},
{"kdb", 'd', 0, N_("Enter in KDB on boot."), 0, 0},
{"gdb", 'g', 0, N_("Use GDB remote debugger instead of DDB."), 0, 0},
{"mute", 'm', 0, N_("Disable all boot output."), 0, 0},
{"nointr", 'n', 0, "", 0, 0},
{"pause", 'p', 0, N_("Wait for keypress after every line of output."), 0, 0},
{"quiet", 'q', 0, "", 0, 0},
{"dfltroot", 'r', 0, N_("Use compiled-in root device."), 0, 0},
{"single", 's', 0, N_("Boot into single mode."), 0, 0},
{"verbose", 'v', 0, N_("Boot with verbose messages."), 0, 0},
{0, 0, 0, 0, 0, 0}
};
static const grub_uint32_t freebsd_flags[] =
{
FREEBSD_RB_DUAL, FREEBSD_RB_SERIAL, FREEBSD_RB_ASKNAME,
FREEBSD_RB_CDROM, FREEBSD_RB_CONFIG, FREEBSD_RB_KDB,
FREEBSD_RB_GDB, FREEBSD_RB_MUTE, FREEBSD_RB_NOINTR,
FREEBSD_RB_PAUSE, FREEBSD_RB_QUIET, FREEBSD_RB_DFLTROOT,
FREEBSD_RB_SINGLE, FREEBSD_RB_VERBOSE, 0
};
static const struct grub_arg_option openbsd_opts[] =
{
{"askname", 'a', 0, N_("Ask for file name to reboot from."), 0, 0},
{"halt", 'b', 0, N_("Don't reboot, just halt."), 0, 0},
{"config", 'c', 0, N_("Change configured devices."), 0, 0},
{"single", 's', 0, N_("Boot into single mode."), 0, 0},
{"kdb", 'd', 0, N_("Enter in KDB on boot."), 0, 0},
{"root", 'r', 0, N_("Set root device."), "wdXY", ARG_TYPE_STRING},
{"serial", 'h', GRUB_ARG_OPTION_OPTIONAL,
N_("Use serial console."),
/* TRANSLATORS: "com" is static and not to be translated. It refers to
serial ports e.g. com1.
*/
N_("comUNIT[,SPEED]"), ARG_TYPE_STRING},
{0, 0, 0, 0, 0, 0}
};
static const grub_uint32_t openbsd_flags[] =
{
OPENBSD_RB_ASKNAME, OPENBSD_RB_HALT, OPENBSD_RB_CONFIG,
OPENBSD_RB_SINGLE, OPENBSD_RB_KDB, 0
};
#define OPENBSD_ROOT_ARG (ARRAY_SIZE (openbsd_flags) - 1)
#define OPENBSD_SERIAL_ARG (ARRAY_SIZE (openbsd_flags))
static const struct grub_arg_option netbsd_opts[] =
{
{"no-smp", '1', 0, N_("Disable SMP."), 0, 0},
{"no-acpi", '2', 0, N_("Disable ACPI."), 0, 0},
{"askname", 'a', 0, N_("Ask for file name to reboot from."), 0, 0},
{"halt", 'b', 0, N_("Don't reboot, just halt."), 0, 0},
{"config", 'c', 0, N_("Change configured devices."), 0, 0},
{"kdb", 'd', 0, N_("Enter in KDB on boot."), 0, 0},
{"miniroot", 'm', 0, "", 0, 0},
{"quiet", 'q', 0, N_("Don't display boot diagnostic messages."), 0, 0},
{"single", 's', 0, N_("Boot into single mode."), 0, 0},
{"verbose", 'v', 0, N_("Boot with verbose messages."), 0, 0},
{"debug", 'x', 0, N_("Boot with debug messages."), 0, 0},
{"silent", 'z', 0, N_("Suppress normal output (warnings remain)."), 0, 0},
{"root", 'r', 0, N_("Set root device."), N_("DEVICE"), ARG_TYPE_STRING},
{"serial", 'h', GRUB_ARG_OPTION_OPTIONAL,
N_("Use serial console."),
/* TRANSLATORS: "com" is static and not to be translated. It refers to
serial ports e.g. com1.
*/
N_("[ADDR|comUNIT][,SPEED]"), ARG_TYPE_STRING},
{0, 0, 0, 0, 0, 0}
};
static const grub_uint32_t netbsd_flags[] =
{
NETBSD_AB_NOSMP, NETBSD_AB_NOACPI, NETBSD_RB_ASKNAME,
NETBSD_RB_HALT, NETBSD_RB_USERCONFIG, NETBSD_RB_KDB,
NETBSD_RB_MINIROOT, NETBSD_AB_QUIET, NETBSD_RB_SINGLE,
NETBSD_AB_VERBOSE, NETBSD_AB_DEBUG, NETBSD_AB_SILENT, 0
};
#define NETBSD_ROOT_ARG (ARRAY_SIZE (netbsd_flags) - 1)
#define NETBSD_SERIAL_ARG (ARRAY_SIZE (netbsd_flags))
static void
grub_bsd_get_device (grub_uint32_t * biosdev,
grub_uint32_t * unit,
grub_uint32_t * slice, grub_uint32_t * part)
{
grub_device_t dev;
#ifdef GRUB_MACHINE_PCBIOS
*biosdev = grub_get_root_biosnumber () & 0xff;
#else
*biosdev = 0xff;
#endif
*unit = (*biosdev & 0x7f);
*slice = 0xff;
*part = 0xff;
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 + 1;
}
else
*slice = dev->disk->partition->number + 1;
}
if (dev)
grub_device_close (dev);
}
static grub_err_t
grub_bsd_add_meta_ptr (grub_uint32_t type, void **ptr, grub_uint32_t len)
{
struct bsd_tag *newtag;
newtag = grub_malloc (len + sizeof (struct bsd_tag));
if (!newtag)
return grub_errno;
newtag->len = len;
newtag->type = type;
newtag->next = NULL;
*ptr = newtag->data;
if (kernel_type == KERNEL_TYPE_FREEBSD
&& type == (FREEBSD_MODINFO_METADATA | FREEBSD_MODINFOMD_SMAP))
{
struct bsd_tag *p;
for (p = tags;
p && p->type != (FREEBSD_MODINFO_METADATA
| FREEBSD_MODINFOMD_KERNEND);
p = p->next);
if (p)
{
newtag->next = p->next;
p->next = newtag;
if (newtag->next == NULL)
tags_last = newtag;
return GRUB_ERR_NONE;
}
}
if (tags_last)
tags_last->next = newtag;
else
tags = newtag;
tags_last = newtag;
return GRUB_ERR_NONE;
}
grub_err_t
grub_bsd_add_meta (grub_uint32_t type, const void *data, grub_uint32_t len)
{
grub_err_t err;
void *ptr;
err = grub_bsd_add_meta_ptr (type, &ptr, len);
if (err)
return err;
if (len)
grub_memcpy (ptr, data, len);
return GRUB_ERR_NONE;
}
struct grub_e820_mmap
{
grub_uint64_t addr;
grub_uint64_t size;
grub_uint32_t type;
} GRUB_PACKED;
#define GRUB_E820_RAM 1
#define GRUB_E820_RESERVED 2
#define GRUB_E820_ACPI 3
#define GRUB_E820_NVS 4
#define GRUB_E820_BADRAM 5
#define GRUB_E820_COREBOOT_TABLES 0x10
/* Context for generate_e820_mmap. */
struct generate_e820_mmap_ctx
{
int count;
struct grub_e820_mmap *mmap;
struct grub_e820_mmap prev, cur;
};
/* Helper for generate_e820_mmap. */
static int
generate_e820_mmap_iter (grub_uint64_t addr, grub_uint64_t size,
grub_memory_type_t type, void *data)
{
struct generate_e820_mmap_ctx *ctx = data;
ctx->cur.addr = addr;
ctx->cur.size = size;
if (type == GRUB_MEMORY_COREBOOT_TABLES
&& addr == 0)
/* Nowadays the tables at 0 don't contain anything important but
*BSD needs the memory at 0 for own needs.
*/
type = GRUB_E820_RAM;
ctx->cur.type = type;
/* Merge regions if possible. */
if (ctx->count && ctx->cur.type == ctx->prev.type
&& ctx->cur.addr == ctx->prev.addr + ctx->prev.size)
{
ctx->prev.size += ctx->cur.size;
if (ctx->mmap)
ctx->mmap[-1] = ctx->prev;
}
else
{
if (ctx->mmap)
*ctx->mmap++ = ctx->cur;
ctx->prev = ctx->cur;
ctx->count++;
}
if (kernel_type == KERNEL_TYPE_OPENBSD && ctx->prev.addr < 0x100000
&& ctx->prev.addr + ctx->prev.size > 0x100000)
{
ctx->cur.addr = 0x100000;
ctx->cur.size = ctx->prev.addr + ctx->prev.size - 0x100000;
ctx->cur.type = ctx->prev.type;
ctx->prev.size = 0x100000 - ctx->prev.addr;
if (ctx->mmap)
{
ctx->mmap[-1] = ctx->prev;
ctx->mmap[0] = ctx->cur;
ctx->mmap++;
}
ctx->prev = ctx->cur;
ctx->count++;
}
return 0;
}
static void
generate_e820_mmap (grub_size_t *len, grub_size_t *cnt, void *buf)
{
struct generate_e820_mmap_ctx ctx = {
.count = 0,
.mmap = buf
};
grub_mmap_iterate (generate_e820_mmap_iter, &ctx);
if (len)
*len = ctx.count * sizeof (struct grub_e820_mmap);
*cnt = ctx.count;
return;
}
static grub_err_t
grub_bsd_add_mmap (void)
{
grub_size_t len, cnt;
void *buf = NULL, *buf0;
generate_e820_mmap (&len, &cnt, buf);
if (kernel_type == KERNEL_TYPE_NETBSD)
len += sizeof (grub_uint32_t);
if (kernel_type == KERNEL_TYPE_OPENBSD)
len += sizeof (struct grub_e820_mmap);
buf = grub_malloc (len);
if (!buf)
return grub_errno;
buf0 = buf;
if (kernel_type == KERNEL_TYPE_NETBSD)
{
*(grub_uint32_t *) buf = cnt;
buf = ((grub_uint32_t *) buf + 1);
}
generate_e820_mmap (NULL, &cnt, buf);
if (kernel_type == KERNEL_TYPE_OPENBSD)
grub_memset ((grub_uint8_t *) buf + len - sizeof (struct grub_e820_mmap), 0,
sizeof (struct grub_e820_mmap));
grub_dprintf ("bsd", "%u entries in smap\n", (unsigned) cnt);
if (kernel_type == KERNEL_TYPE_NETBSD)
grub_bsd_add_meta (NETBSD_BTINFO_MEMMAP, buf0, len);
else if (kernel_type == KERNEL_TYPE_OPENBSD)
grub_bsd_add_meta (OPENBSD_BOOTARG_MMAP, buf0, len);
else
grub_bsd_add_meta (FREEBSD_MODINFO_METADATA |
FREEBSD_MODINFOMD_SMAP, buf0, len);
grub_free (buf0);
return grub_errno;
}
grub_err_t
grub_freebsd_add_meta_module (const char *filename, const char *type,
int argc, char **argv,
grub_addr_t addr, grub_uint32_t size)
{
const char *name;
grub_err_t err;
name = grub_strrchr (filename, '/');
if (name)
name++;
else
name = filename;
if (grub_strcmp (type, "/boot/zfs/zpool.cache") == 0)
name = "/boot/zfs/zpool.cache";
if (grub_bsd_add_meta (FREEBSD_MODINFO_NAME, name, grub_strlen (name) + 1))
return grub_errno;
if (is_64bit)
{
grub_uint64_t addr64 = addr, size64 = size;
if (grub_bsd_add_meta (FREEBSD_MODINFO_TYPE, type, grub_strlen (type) + 1)
|| grub_bsd_add_meta (FREEBSD_MODINFO_ADDR, &addr64, sizeof (addr64))
|| grub_bsd_add_meta (FREEBSD_MODINFO_SIZE, &size64, sizeof (size64)))
return grub_errno;
}
else
{
if (grub_bsd_add_meta (FREEBSD_MODINFO_TYPE, type, grub_strlen (type) + 1)
|| grub_bsd_add_meta (FREEBSD_MODINFO_ADDR, &addr, sizeof (addr))
|| grub_bsd_add_meta (FREEBSD_MODINFO_SIZE, &size, sizeof (size)))
return grub_errno;
}
if (argc)
{
int i, n;
n = 0;
for (i = 0; i < argc; i++)
{
n += grub_strlen (argv[i]) + 1;
}
if (n)
{
void *cmdline;
char *p;
if (grub_bsd_add_meta_ptr (FREEBSD_MODINFO_ARGS, &cmdline, n))
return grub_errno;
p = cmdline;
for (i = 0; i < argc; i++)
{
grub_strcpy (p, argv[i]);
p += grub_strlen (argv[i]);
*(p++) = ' ';
}
*p = 0;
err = grub_verify_string (cmdline, GRUB_VERIFY_MODULE_CMDLINE);
if (err)
return err;
}
}
return GRUB_ERR_NONE;
}
static void
grub_freebsd_list_modules (void)
{
struct bsd_tag *tag;
grub_printf (" %-18s %-18s%14s%14s\n", _("name"), _("type"), _("addr"),
_("size"));
for (tag = tags; tag; tag = tag->next)
{
switch (tag->type)
{
case FREEBSD_MODINFO_NAME:
case FREEBSD_MODINFO_TYPE:
grub_printf (" %-18s", (char *) tag->data);
break;
case FREEBSD_MODINFO_ADDR:
{
grub_uint32_t addr;
addr = *((grub_uint32_t *) tag->data);
grub_printf (" 0x%08x", addr);
break;
}
case FREEBSD_MODINFO_SIZE:
{
grub_uint32_t len;
len = *((grub_uint32_t *) tag->data);
grub_printf (" 0x%08x\n", len);
}
}
}
}
static grub_err_t
grub_netbsd_add_meta_module (char *filename, grub_uint32_t type,
grub_addr_t addr, grub_uint32_t size)
{
char *name;
struct netbsd_module *mod;
name = grub_strrchr (filename, '/');
if (name)
name++;
else
name = filename;
mod = grub_zalloc (sizeof (*mod));
if (!mod)
return grub_errno;
grub_strncpy (mod->mod.name, name, sizeof (mod->mod.name) - 1);
mod->mod.addr = addr;
mod->mod.type = type;
mod->mod.size = size;
if (netbsd_mods_last)
netbsd_mods_last->next = mod;
else
netbsd_mods = mod;
netbsd_mods_last = mod;
return GRUB_ERR_NONE;
}
static void
grub_netbsd_list_modules (void)
{
struct netbsd_module *mod;
grub_printf (" %-18s%14s%14s%14s\n", _("name"), _("type"), _("addr"),
_("size"));
for (mod = netbsd_mods; mod; mod = mod->next)
grub_printf (" %-18s 0x%08x 0x%08x 0x%08x", mod->mod.name,
mod->mod.type, mod->mod.addr, mod->mod.size);
}
/* This function would be here but it's under different license. */
#include "bsd_pagetable.c"
static grub_uint32_t freebsd_bootdev, freebsd_biosdev;
static grub_uint64_t freebsd_zfsguid;
static void
freebsd_get_zfs (void)
{
grub_device_t dev;
grub_fs_t fs;
char *uuid;
grub_err_t err;
dev = grub_device_open (0);
if (!dev)
return;
fs = grub_fs_probe (dev);
if (!fs)
return;
if (!fs->fs_uuid || grub_strcmp (fs->name, "zfs") != 0)
return;
err = fs->fs_uuid (dev, &uuid);
if (err)
return;
if (!uuid)
return;
freebsd_zfsguid = grub_strtoull (uuid, 0, 16);
grub_free (uuid);
}
static grub_err_t
grub_freebsd_boot (void)
{
struct grub_freebsd_bootinfo bi;
grub_uint8_t *p, *p0;
grub_addr_t p_target;
grub_size_t p_size = 0;
grub_err_t err;
grub_size_t tag_buf_len = 0;
struct grub_env_var *var;
grub_memset (&bi, 0, sizeof (bi));
bi.version = FREEBSD_BOOTINFO_VERSION;
bi.length = sizeof (bi);
bi.boot_device = freebsd_biosdev;
p_size = 0;
FOR_SORTED_ENV (var)
if ((grub_memcmp (var->name, "kFreeBSD.", sizeof("kFreeBSD.") - 1) == 0) && (var->name[sizeof("kFreeBSD.") - 1]))
{
p_size += grub_strlen (&var->name[sizeof("kFreeBSD.") - 1]);
p_size++;
p_size += grub_strlen (var->value) + 1;
}
if (p_size)
p_size = ALIGN_PAGE (kern_end + p_size + 1) - kern_end;
if (is_elf_kernel)
{
struct bsd_tag *tag;
err = grub_bsd_add_mmap ();
if (err)
return err;
err = grub_bsd_add_meta (FREEBSD_MODINFO_END, 0, 0);
if (err)
return err;
tag_buf_len = 0;
for (tag = tags; tag; tag = tag->next)
tag_buf_len = ALIGN_VAR (tag_buf_len
+ sizeof (struct freebsd_tag_header)
+ tag->len);
p_size = ALIGN_PAGE (kern_end + p_size + tag_buf_len) - kern_end;
}
if (is_64bit)
p_size += 4096 * 3;
{
grub_relocator_chunk_t ch;
err = grub_relocator_alloc_chunk_addr (relocator, &ch,
kern_end, p_size);
if (err)
return err;
p = get_virtual_current_address (ch);
}
p_target = kern_end;
p0 = p;
kern_end += p_size;
FOR_SORTED_ENV (var)
if ((grub_memcmp (var->name, "kFreeBSD.", sizeof("kFreeBSD.") - 1) == 0) && (var->name[sizeof("kFreeBSD.") - 1]))
{
grub_strcpy ((char *) p, &var->name[sizeof("kFreeBSD.") - 1]);
p += grub_strlen ((char *) p);
*(p++) = '=';
grub_strcpy ((char *) p, var->value);
p += grub_strlen ((char *) p) + 1;
}
if (p != p0)
{
*(p++) = 0;
bi.environment = p_target;
}
if (is_elf_kernel)
{
grub_uint8_t *p_tag = p;
struct bsd_tag *tag;
for (tag = tags; tag; tag = tag->next)
{
struct freebsd_tag_header *head
= (struct freebsd_tag_header *) p_tag;
head->type = tag->type;
head->len = tag->len;
p_tag += sizeof (struct freebsd_tag_header);
switch (tag->type)
{
case FREEBSD_MODINFO_METADATA | FREEBSD_MODINFOMD_HOWTO:
if (is_64bit)
*(grub_uint64_t *) p_tag = bootflags;
else
*(grub_uint32_t *) p_tag = bootflags;
break;
case FREEBSD_MODINFO_METADATA | FREEBSD_MODINFOMD_ENVP:
if (is_64bit)
*(grub_uint64_t *) p_tag = bi.environment;
else
*(grub_uint32_t *) p_tag = bi.environment;
break;
case FREEBSD_MODINFO_METADATA | FREEBSD_MODINFOMD_KERNEND:
if (is_64bit)
*(grub_uint64_t *) p_tag = kern_end;
else
*(grub_uint32_t *) p_tag = kern_end;
break;
default:
grub_memcpy (p_tag, tag->data, tag->len);
break;
}
p_tag += tag->len;
p_tag = ALIGN_VAR (p_tag - p) + p;
}
bi.tags = (p - p0) + p_target;
p = (ALIGN_PAGE ((p_tag - p0) + p_target) - p_target) + p0;
}
bi.kern_end = kern_end;
grub_video_set_mode ("text", 0, 0);
if (is_64bit)
{
struct grub_relocator64_state state;
grub_uint8_t *pagetable;
grub_uint32_t *stack;
grub_addr_t stack_target;
{
grub_relocator_chunk_t ch;
err = grub_relocator_alloc_chunk_align (relocator, &ch,
0x10000, 0x90000,
3 * sizeof (grub_uint32_t)
+ sizeof (bi), 4,
GRUB_RELOCATOR_PREFERENCE_NONE,
0);
if (err)
return err;
stack = get_virtual_current_address (ch);
stack_target = get_physical_target_address (ch);
}
#ifdef GRUB_MACHINE_EFI
err = grub_efi_finish_boot_services (NULL, NULL, NULL, NULL, NULL);
if (err)
return err;
#endif
pagetable = p;
fill_bsd64_pagetable (pagetable, (pagetable - p0) + p_target);
state.cr3 = (pagetable - p0) + p_target;
state.rsp = stack_target;
state.rip = (((grub_uint64_t) entry_hi) << 32) | entry;
stack[0] = entry;
stack[1] = bi.tags;
stack[2] = kern_end;
return grub_relocator64_boot (relocator, state, 0, 0x40000000);
}
else
{
struct grub_relocator32_state state;
grub_uint32_t *stack;
grub_addr_t stack_target;
{
grub_relocator_chunk_t ch;
err = grub_relocator_alloc_chunk_align (relocator, &ch,
0x10000, 0x90000,
9 * sizeof (grub_uint32_t)
+ sizeof (bi), 4,
GRUB_RELOCATOR_PREFERENCE_NONE,
0);
if (err)
return err;
stack = get_virtual_current_address (ch);
stack_target = get_physical_target_address (ch);
}
#ifdef GRUB_MACHINE_EFI
err = grub_efi_finish_boot_services (NULL, NULL, NULL, NULL, NULL);
if (err)
return err;
#endif
grub_memcpy (&stack[9], &bi, sizeof (bi));
state.eip = entry;
state.esp = stack_target;
state.ebp = stack_target;
stack[0] = entry; /* "Return" address. */
stack[1] = bootflags | FREEBSD_RB_BOOTINFO;
stack[2] = freebsd_bootdev;
stack[3] = freebsd_zfsguid ? 4 : 0;
stack[4] = freebsd_zfsguid;
stack[5] = freebsd_zfsguid >> 32;
stack[6] = stack_target + 9 * sizeof (grub_uint32_t);
stack[7] = bi.tags;
stack[8] = kern_end;
return grub_relocator32_boot (relocator, state, 0);
}
/* Not reached. */
return GRUB_ERR_NONE;
}
static grub_err_t
grub_openbsd_boot (void)
{
grub_uint32_t *stack;
struct grub_relocator32_state state;
void *curarg, *buf0, *arg0;
grub_addr_t buf_target;
grub_err_t err;
grub_size_t tag_buf_len;
err = grub_bsd_add_mmap ();
if (err)
return err;
#ifdef GRUB_MACHINE_PCBIOS
{
struct grub_bios_int_registers regs;
regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
regs.ebx = 0;
regs.ecx = 0;
regs.eax = 0xb101;
regs.es = 0;
regs.edi = 0;
regs.edx = 0;
grub_bios_interrupt (0x1a, &regs);
if (regs.edx == 0x20494350)
{
struct grub_openbsd_bootarg_pcibios pcibios;
pcibios.characteristics = regs.eax & 0xff;
pcibios.revision = regs.ebx & 0xffff;
pcibios.pm_entry = regs.edi;
pcibios.last_bus = regs.ecx & 0xff;
grub_bsd_add_meta (OPENBSD_BOOTARG_PCIBIOS, &pcibios,
sizeof (pcibios));
}
}
#endif
{
struct bsd_tag *tag;
tag_buf_len = 0;
for (tag = tags; tag; tag = tag->next)
tag_buf_len = ALIGN_VAR (tag_buf_len
+ sizeof (struct grub_openbsd_bootargs)
+ tag->len);
}
buf_target = GRUB_BSD_TEMP_BUFFER - 9 * sizeof (grub_uint32_t);
{
grub_relocator_chunk_t ch;
err = grub_relocator_alloc_chunk_addr (relocator, &ch, buf_target,
tag_buf_len
+ sizeof (struct grub_openbsd_bootargs)
+ 9 * sizeof (grub_uint32_t));
if (err)
return err;
buf0 = get_virtual_current_address (ch);
}
stack = (grub_uint32_t *) buf0;
arg0 = curarg = stack + 9;
{
struct bsd_tag *tag;
struct grub_openbsd_bootargs *head;
for (tag = tags; tag; tag = tag->next)
{
head = curarg;
head->ba_type = tag->type;
head->ba_size = tag->len + sizeof (*head);
curarg = head + 1;
grub_memcpy (curarg, tag->data, tag->len);
curarg = (grub_uint8_t *) curarg + tag->len;
head->ba_next = (grub_uint8_t *) curarg - (grub_uint8_t *) buf0
+ buf_target;
}
head = curarg;
head->ba_type = OPENBSD_BOOTARG_END;
head->ba_size = 0;
head->ba_next = 0;
}
grub_video_set_mode ("text", 0, 0);
#ifdef GRUB_MACHINE_EFI
err = grub_efi_finish_boot_services (NULL, NULL, NULL, NULL, NULL);
if (err)
return err;
#endif
state.eip = entry;
state.ebp = state.esp
= ((grub_uint8_t *) stack - (grub_uint8_t *) buf0) + buf_target;
stack[0] = entry;
stack[1] = bootflags;
stack[2] = openbsd_root;
stack[3] = OPENBSD_BOOTARG_APIVER;
stack[4] = 0;
stack[5] = grub_mmap_get_upper () >> 10;
stack[6] = grub_mmap_get_lower () >> 10;
stack[7] = (grub_uint8_t *) curarg - (grub_uint8_t *) arg0;
stack[8] = ((grub_uint8_t *) arg0 - (grub_uint8_t *) buf0) + buf_target;
return grub_relocator32_boot (relocator, state, 0);
}
static grub_err_t
grub_netbsd_setup_video (void)
{
struct grub_video_mode_info mode_info;
void *framebuffer;
const char *modevar;
struct grub_netbsd_btinfo_framebuf params;
grub_err_t err;
grub_video_driver_id_t driv_id;
modevar = grub_env_get ("gfxpayload");
/* Now all graphical modes are acceptable.
May change in future if we have modes without framebuffer. */
if (modevar && *modevar != 0)
{
char *tmp;
tmp = grub_xasprintf ("%s;" NETBSD_DEFAULT_VIDEO_MODE, modevar);
if (! tmp)
return grub_errno;
err = grub_video_set_mode (tmp, 0, 0);
grub_free (tmp);
}
else
err = grub_video_set_mode (NETBSD_DEFAULT_VIDEO_MODE, 0, 0);
if (err)
return err;
driv_id = grub_video_get_driver_id ();
if (driv_id == GRUB_VIDEO_DRIVER_NONE)
return GRUB_ERR_NONE;
err = grub_video_get_info_and_fini (&mode_info, &framebuffer);
if (err)
return err;
params.width = mode_info.width;
params.height = mode_info.height;
params.bpp = mode_info.bpp;
params.pitch = mode_info.pitch;
params.flags = 0;
params.fbaddr = (grub_addr_t) framebuffer;
params.red_mask_size = mode_info.red_mask_size;
params.red_field_pos = mode_info.red_field_pos;
params.green_mask_size = mode_info.green_mask_size;
params.green_field_pos = mode_info.green_field_pos;
params.blue_mask_size = mode_info.blue_mask_size;
params.blue_field_pos = mode_info.blue_field_pos;
#ifdef GRUB_MACHINE_PCBIOS
/* VESA packed modes may come with zeroed mask sizes, which need
to be set here according to DAC Palette width. If we don't,
this results in Linux displaying a black screen. */
if (mode_info.bpp <= 8 && driv_id == GRUB_VIDEO_DRIVER_VBE)
{
struct grub_vbe_info_block controller_info;
int status;
int width = 8;
status = grub_vbe_bios_get_controller_info (&controller_info);
if (status == GRUB_VBE_STATUS_OK &&
(controller_info.capabilities & GRUB_VBE_CAPABILITY_DACWIDTH))
status = grub_vbe_bios_set_dac_palette_width (&width);
if (status != GRUB_VBE_STATUS_OK)
/* 6 is default after mode reset. */
width = 6;
params.red_mask_size = params.green_mask_size
= params.blue_mask_size = width;
}
#endif
err = grub_bsd_add_meta (NETBSD_BTINFO_FRAMEBUF, &params, sizeof (params));
return err;
}
static grub_err_t
grub_netbsd_add_modules (void)
{
struct netbsd_module *mod;
unsigned modcnt = 0;
struct grub_netbsd_btinfo_modules *mods;
unsigned i;
grub_err_t err;
for (mod = netbsd_mods; mod; mod = mod->next)
modcnt++;
mods = grub_malloc (sizeof (*mods) + sizeof (mods->mods[0]) * modcnt);
if (!mods)
return grub_errno;
mods->num = modcnt;
mods->last_addr = kern_end;
for (mod = netbsd_mods, i = 0; mod; i++, mod = mod->next)
mods->mods[i] = mod->mod;
err = grub_bsd_add_meta (NETBSD_BTINFO_MODULES, mods,
sizeof (*mods) + sizeof (mods->mods[0]) * modcnt);
grub_free (mods);
return err;
}
/*
* Adds NetBSD bootinfo bootdisk and bootwedge. The partition identified
* in these bootinfo fields is the root device.
*/
static void
grub_netbsd_add_boot_disk_and_wedge (void)
{
grub_device_t dev;
grub_disk_t disk;
grub_partition_t part;
grub_uint32_t biosdev;
grub_uint32_t partmapsector;
union {
grub_uint64_t raw[GRUB_DISK_SECTOR_SIZE / 8];
struct grub_partition_bsd_disk_label label;
} buf;
if (GRUB_MD_MD5->mdlen > GRUB_CRYPTO_MAX_MDLEN)
{
grub_error (GRUB_ERR_BUG, "mdlen too long");
return;
}
dev = grub_device_open (0);
if (! (dev && dev->disk && dev->disk->partition))
goto fail;
disk = dev->disk;
part = disk->partition;
if (disk->dev && disk->dev->id == GRUB_DISK_DEVICE_BIOSDISK_ID)
biosdev = (grub_uint32_t) disk->id & 0xff;
else
biosdev = 0xff;
/* Absolute sector of the partition map describing this partition. */
partmapsector = grub_partition_get_start (part->parent) + part->offset;
disk->partition = part->parent;
if (grub_disk_read (disk, part->offset, 0, GRUB_DISK_SECTOR_SIZE, buf.raw)
!= GRUB_ERR_NONE)
goto fail;
disk->partition = part;
/* Fill bootwedge. */
{
struct grub_netbsd_btinfo_bootwedge biw;
grub_uint8_t hash[GRUB_CRYPTO_MAX_MDLEN];
grub_memset (&biw, 0, sizeof (biw));
biw.biosdev = biosdev;
biw.startblk = grub_partition_get_start (part);
biw.nblks = part->len;
biw.matchblk = partmapsector;
biw.matchnblks = 1;
grub_crypto_hash (GRUB_MD_MD5, hash,
buf.raw, GRUB_DISK_SECTOR_SIZE);
grub_memcpy (biw.matchhash, hash, 16);
grub_bsd_add_meta (NETBSD_BTINFO_BOOTWEDGE, &biw, sizeof (biw));
}
/* Fill bootdisk. */
{
struct grub_netbsd_btinfo_bootdisk bid;
grub_memset (&bid, 0, sizeof (bid));
/* Check for a NetBSD disk label. */
if (part->partmap != NULL &&
(grub_strcmp (part->partmap->name, "netbsd") == 0 ||
(part->parent == NULL && grub_strcmp (part->partmap->name, "bsd") == 0)))
{
bid.labelsector = partmapsector;
bid.label.type = buf.label.type;
bid.label.checksum = buf.label.checksum;
grub_memcpy (bid.label.packname, buf.label.packname, 16);
}
else
{
bid.labelsector = -1;
}
bid.biosdev = biosdev;
bid.partition = part->number;
grub_bsd_add_meta (NETBSD_BTINFO_BOOTDISK, &bid, sizeof (bid));
}
fail:
if (dev)
grub_device_close (dev);
}
static grub_err_t
grub_netbsd_boot (void)
{
struct grub_netbsd_bootinfo *bootinfo;
void *curarg, *arg0;
grub_addr_t arg_target, stack_target;
grub_uint32_t *stack;
grub_err_t err;
struct grub_relocator32_state state;
grub_size_t tag_buf_len = 0;
int tag_count = 0;
err = grub_bsd_add_mmap ();
if (err)
return err;
err = grub_netbsd_setup_video ();
if (err)
{
grub_print_error ();
grub_puts_ (N_("Booting in blind mode"));
grub_errno = GRUB_ERR_NONE;
}
err = grub_netbsd_add_modules ();
if (err)
return err;
#ifdef GRUB_MACHINE_EFI
err = grub_bsd_add_meta (NETBSD_BTINFO_EFI,
&grub_efi_system_table,
sizeof (grub_efi_system_table));
if (err)
return err;
#endif
{
struct bsd_tag *tag;
tag_buf_len = 0;
for (tag = tags; tag; tag = tag->next)
{
tag_buf_len = ALIGN_VAR (tag_buf_len
+ sizeof (struct grub_netbsd_btinfo_common)
+ tag->len);
tag_count++;
}
}
arg_target = kern_end;
{
grub_relocator_chunk_t ch;
err = grub_relocator_alloc_chunk_addr (relocator, &ch,
arg_target, tag_buf_len
+ sizeof (struct grub_netbsd_bootinfo)
+ tag_count * sizeof (grub_uint32_t));
if (err)
return err;
curarg = get_virtual_current_address (ch);
}
arg0 = curarg;
bootinfo = (void *) ((grub_uint8_t *) arg0 + tag_buf_len);
{
struct bsd_tag *tag;
unsigned i;
bootinfo->bi_count = tag_count;
for (tag = tags, i = 0; tag; i++, tag = tag->next)
{
struct grub_netbsd_btinfo_common *head = curarg;
bootinfo->bi_data[i] = ((grub_uint8_t *) curarg - (grub_uint8_t *) arg0)
+ arg_target;
head->type = tag->type;
head->len = tag->len + sizeof (*head);
curarg = head + 1;
grub_memcpy (curarg, tag->data, tag->len);
curarg = (grub_uint8_t *) curarg + tag->len;
}
}
{
grub_relocator_chunk_t ch;
err = grub_relocator_alloc_chunk_align (relocator, &ch, 0x10000, 0x90000,
7 * sizeof (grub_uint32_t), 4,
GRUB_RELOCATOR_PREFERENCE_NONE,
0);
if (err)
return err;
stack = get_virtual_current_address (ch);
stack_target = get_physical_target_address (ch);
}
#ifdef GRUB_MACHINE_EFI
err = grub_efi_finish_boot_services (NULL, NULL, NULL, NULL, NULL);
if (err)
return err;
#endif
state.eip = entry;
state.esp = stack_target;
state.ebp = stack_target;
stack[0] = entry;
stack[1] = bootflags;
stack[2] = 0;
stack[3] = ((grub_uint8_t *) bootinfo - (grub_uint8_t *) arg0) + arg_target;
stack[4] = 0;
stack[5] = grub_mmap_get_upper () >> 10;
stack[6] = grub_mmap_get_lower () >> 10;
return grub_relocator32_boot (relocator, state, 0);
}
static grub_err_t
grub_bsd_unload (void)
{
struct bsd_tag *tag, *next;
for (tag = tags; tag; tag = next)
{
next = tag->next;
grub_free (tag);
}
tags = NULL;
tags_last = NULL;
kernel_type = KERNEL_TYPE_NONE;
grub_dl_unref (my_mod);
grub_relocator_unload (relocator);
relocator = NULL;
return GRUB_ERR_NONE;
}
static grub_err_t
grub_bsd_load_aout (grub_file_t file, const char *filename)
{
grub_addr_t load_addr, load_end;
int ofs, align_page;
union grub_aout_header ah;
grub_err_t err;
grub_size_t bss_size;
if ((grub_file_seek (file, 0)) == (grub_off_t) - 1)
return grub_errno;
if (grub_file_read (file, &ah, sizeof (ah)) != sizeof (ah))
{
if (!grub_errno)
grub_error (GRUB_ERR_READ_ERROR, N_("premature end of file %s"),
filename);
return grub_errno;
}
if (grub_aout_get_type (&ah) != AOUT_TYPE_AOUT32)
return grub_error (GRUB_ERR_BAD_OS, "invalid a.out header");
entry = ah.aout32.a_entry & 0xFFFFFF;
if (AOUT_GETMAGIC (ah.aout32) == AOUT32_ZMAGIC)
{
load_addr = entry;
ofs = 0x1000;
align_page = 0;
}
else
{
load_addr = entry & 0xF00000;
ofs = sizeof (struct grub_aout32_header);
align_page = 1;
}
if (load_addr < 0x100000)
return grub_error (GRUB_ERR_BAD_OS, "load address below 1M");
kern_start = load_addr;
load_end = kern_end = load_addr + ah.aout32.a_text + ah.aout32.a_data;
if (align_page)
kern_end = ALIGN_PAGE (kern_end);
if (ah.aout32.a_bss)
{
kern_end += ah.aout32.a_bss;
if (align_page)
kern_end = ALIGN_PAGE (kern_end);
bss_size = kern_end - load_end;
}
else
bss_size = 0;
{
grub_relocator_chunk_t ch;
err = grub_relocator_alloc_chunk_addr (relocator, &ch,
kern_start, kern_end - kern_start);
if (err)
return err;
kern_chunk_src = get_virtual_current_address (ch);
}
return grub_aout_load (file, ofs, kern_chunk_src,
ah.aout32.a_text + ah.aout32.a_data,
bss_size);
}
static grub_err_t
grub_bsd_load_elf (grub_elf_t elf, const char *filename)
{
grub_err_t err;
kern_end = 0;
kern_start = ~0;
if (grub_elf_is_elf32 (elf))
{
grub_relocator_chunk_t ch;
Elf32_Phdr *phdr;
entry = elf->ehdr.ehdr32.e_entry & 0xFFFFFFF;
FOR_ELF32_PHDRS (elf, phdr)
{
Elf32_Addr paddr;
if (phdr->p_type != PT_LOAD
&& phdr->p_type != PT_DYNAMIC)
continue;
paddr = phdr->p_paddr & 0xFFFFFFF;
if (paddr < kern_start)
kern_start = paddr;
if (paddr + phdr->p_memsz > kern_end)
kern_end = paddr + phdr->p_memsz;
}
if (grub_errno)
return grub_errno;
err = grub_relocator_alloc_chunk_addr (relocator, &ch,
kern_start, kern_end - kern_start);
if (err)
return err;
kern_chunk_src = get_virtual_current_address (ch);
err = grub_elf32_load (elf, filename, (grub_uint8_t *) kern_chunk_src - kern_start, GRUB_ELF_LOAD_FLAGS_LOAD_PT_DYNAMIC | GRUB_ELF_LOAD_FLAGS_28BITS, 0, 0);
if (err)
return err;
if (kernel_type != KERNEL_TYPE_OPENBSD)
return GRUB_ERR_NONE;
return grub_openbsd_find_ramdisk32 (elf->file, filename, kern_start,
kern_chunk_src, &openbsd_ramdisk);
}
else if (grub_elf_is_elf64 (elf))
{
Elf64_Phdr *phdr;
is_64bit = 1;
if (! grub_cpuid_has_longmode)
return grub_error (GRUB_ERR_BAD_OS, "your CPU does not implement AMD64 architecture");
/* FreeBSD has 64-bit entry point. */
if (kernel_type == KERNEL_TYPE_FREEBSD)
{
entry = elf->ehdr.ehdr64.e_entry & 0xffffffff;
entry_hi = (elf->ehdr.ehdr64.e_entry >> 32) & 0xffffffff;
}
else
{
entry = elf->ehdr.ehdr64.e_entry & 0x0fffffff;
entry_hi = 0;
}
FOR_ELF64_PHDRS (elf, phdr)
{
Elf64_Addr paddr;
if (phdr->p_type != PT_LOAD
&& phdr->p_type != PT_DYNAMIC)
continue;
paddr = phdr->p_paddr & 0xFFFFFFF;
if (paddr < kern_start)
kern_start = paddr;
if (paddr + phdr->p_memsz > kern_end)
kern_end = paddr + phdr->p_memsz;
}
if (grub_errno)
return grub_errno;
grub_dprintf ("bsd", "kern_start = %lx, kern_end = %lx\n",
(unsigned long) kern_start, (unsigned long) kern_end);
{
grub_relocator_chunk_t ch;
err = grub_relocator_alloc_chunk_addr (relocator, &ch, kern_start,
kern_end - kern_start);
if (err)
return err;
kern_chunk_src = get_virtual_current_address (ch);
}
err = grub_elf64_load (elf, filename,
(grub_uint8_t *) kern_chunk_src - kern_start, GRUB_ELF_LOAD_FLAGS_LOAD_PT_DYNAMIC | GRUB_ELF_LOAD_FLAGS_28BITS, 0, 0);
if (err)
return err;
if (kernel_type != KERNEL_TYPE_OPENBSD)
return GRUB_ERR_NONE;
return grub_openbsd_find_ramdisk64 (elf->file, filename, kern_start,
kern_chunk_src, &openbsd_ramdisk);
}
else
return grub_error (GRUB_ERR_BAD_OS, N_("invalid arch-dependent ELF magic"));
}
static grub_err_t
grub_bsd_load (int argc, char *argv[])
{
grub_file_t file;
grub_elf_t elf;
grub_dl_ref (my_mod);
grub_loader_unset ();
grub_memset (&openbsd_ramdisk, 0, sizeof (openbsd_ramdisk));
if (argc == 0)
{
grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
goto fail;
}
file = grub_file_open (argv[0], GRUB_FILE_TYPE_BSD_KERNEL);
if (!file)
goto fail;
relocator = grub_relocator_new ();
if (!relocator)
{
grub_file_close (file);
goto fail;
}
elf = grub_elf_file (file, argv[0]);
if (elf)
{
is_elf_kernel = 1;
grub_bsd_load_elf (elf, argv[0]);
grub_elf_close (elf);
}
else
{
is_elf_kernel = 0;
grub_errno = 0;
grub_bsd_load_aout (file, argv[0]);
grub_file_close (file);
}
kern_end = ALIGN_PAGE (kern_end);
fail:
if (grub_errno != GRUB_ERR_NONE)
grub_dl_unref (my_mod);
return grub_errno;
}
static grub_uint32_t
grub_bsd_parse_flags (const struct grub_arg_list *state,
const grub_uint32_t * flags)
{
grub_uint32_t result = 0;
unsigned i;
for (i = 0; flags[i]; i++)
if (state[i].set)
result |= flags[i];
return result;
}
static grub_err_t
grub_cmd_freebsd (grub_extcmd_context_t ctxt, int argc, char *argv[])
{
kernel_type = KERNEL_TYPE_FREEBSD;
bootflags = grub_bsd_parse_flags (ctxt->state, freebsd_flags);
if (grub_bsd_load (argc, argv) == GRUB_ERR_NONE)
{
grub_uint32_t unit, slice, part;
kern_end = ALIGN_PAGE (kern_end);
if (is_elf_kernel)
{
grub_err_t err;
grub_uint64_t data = 0;
grub_file_t file;
int len = is_64bit ? 8 : 4;
err = grub_freebsd_add_meta_module (argv[0], is_64bit
? FREEBSD_MODTYPE_KERNEL64
: FREEBSD_MODTYPE_KERNEL,
argc - 1, argv + 1,
kern_start,
kern_end - kern_start);
if (err)
return err;
file = grub_file_open (argv[0], GRUB_FILE_TYPE_BSD_KERNEL);
if (! file)
return grub_errno;
if (is_64bit)
err = grub_freebsd_load_elf_meta64 (relocator, file, argv[0],
&kern_end);
else
err = grub_freebsd_load_elf_meta32 (relocator, file, argv[0],
&kern_end);
if (err)
return err;
err = grub_bsd_add_meta (FREEBSD_MODINFO_METADATA |
FREEBSD_MODINFOMD_HOWTO, &data, 4);
if (err)
return err;
err = grub_bsd_add_meta (FREEBSD_MODINFO_METADATA |
FREEBSD_MODINFOMD_ENVP, &data, len);
if (err)
return err;
err = grub_bsd_add_meta (FREEBSD_MODINFO_METADATA |
FREEBSD_MODINFOMD_KERNEND, &data, len);
if (err)
return err;
}
grub_bsd_get_device (&freebsd_biosdev, &unit, &slice, &part);
freebsd_zfsguid = 0;
if (!is_64bit)
freebsd_get_zfs ();
grub_print_error ();
freebsd_bootdev = (FREEBSD_B_DEVMAGIC + ((slice + 1) << FREEBSD_B_SLICESHIFT) +
(unit << FREEBSD_B_UNITSHIFT) + (part << FREEBSD_B_PARTSHIFT));
grub_loader_set (grub_freebsd_boot, grub_bsd_unload, 0);
}
return grub_errno;
}
static const char *types[] = {
[0] = "wd",
[2] = "fd",
[4] = "sd",
[6] = "cd",
[14] = "vnd",
[17] = "rd"
};
static grub_err_t
grub_cmd_openbsd (grub_extcmd_context_t ctxt, int argc, char *argv[])
{
grub_uint32_t bootdev;
kernel_type = KERNEL_TYPE_OPENBSD;
bootflags = grub_bsd_parse_flags (ctxt->state, openbsd_flags);
if (ctxt->state[OPENBSD_ROOT_ARG].set)
{
const char *arg = ctxt->state[OPENBSD_ROOT_ARG].arg;
unsigned type, unit, part;
for (type = 0; type < ARRAY_SIZE (types); type++)
if (types[type]
&& grub_strncmp (arg, types[type],
grub_strlen (types[type])) == 0)
{
arg += grub_strlen (types[type]);
break;
}
if (type == ARRAY_SIZE (types))
return grub_error (GRUB_ERR_BAD_ARGUMENT,
"unknown disk type name");
unit = grub_strtoul (arg, &arg, 10);
if (! (arg && *arg >= 'a' && *arg <= 'z'))
return grub_error (GRUB_ERR_BAD_ARGUMENT,
"only device specifications of form "
"<type><number><lowercase letter> are supported");
part = *arg - 'a';
bootdev = (OPENBSD_B_DEVMAGIC | (type << OPENBSD_B_TYPESHIFT)
| (unit << OPENBSD_B_UNITSHIFT)
| (part << OPENBSD_B_PARTSHIFT));
}
else
bootdev = 0;
if (ctxt->state[OPENBSD_SERIAL_ARG].set)
{
struct grub_openbsd_bootarg_console serial;
const char *ptr;
unsigned port = 0;
unsigned speed = 9600;
grub_memset (&serial, 0, sizeof (serial));
if (ctxt->state[OPENBSD_SERIAL_ARG].arg)
{
ptr = ctxt->state[OPENBSD_SERIAL_ARG].arg;
if (grub_memcmp (ptr, "com", sizeof ("com") - 1) != 0)
return grub_error (GRUB_ERR_BAD_ARGUMENT,
"only com0-com3 are supported");
ptr += sizeof ("com") - 1;
port = grub_strtoul (ptr, &ptr, 0);
if (port >= 4)
return grub_error (GRUB_ERR_BAD_ARGUMENT,
"only com0-com3 are supported");
if (*ptr == ',')
{
ptr++;
speed = grub_strtoul (ptr, &ptr, 0);
if (grub_errno)
return grub_errno;
}
}
serial.device = (GRUB_OPENBSD_COM_MAJOR << 8) | port;
serial.speed = speed;
serial.addr = grub_ns8250_hw_get_port (port);
grub_bsd_add_meta (OPENBSD_BOOTARG_CONSOLE, &serial, sizeof (serial));
bootflags |= OPENBSD_RB_SERCONS;
}
else
{
struct grub_openbsd_bootarg_console serial;
grub_memset (&serial, 0, sizeof (serial));
serial.device = (GRUB_OPENBSD_VGA_MAJOR << 8);
serial.addr = 0xffffffff;
grub_bsd_add_meta (OPENBSD_BOOTARG_CONSOLE, &serial, sizeof (serial));
bootflags &= ~OPENBSD_RB_SERCONS;
}
if (grub_bsd_load (argc, argv) == GRUB_ERR_NONE)
{
grub_loader_set (grub_openbsd_boot, grub_bsd_unload, 0);
openbsd_root = bootdev;
}
return grub_errno;
}
static grub_err_t
grub_cmd_netbsd (grub_extcmd_context_t ctxt, int argc, char *argv[])
{
grub_err_t err;
kernel_type = KERNEL_TYPE_NETBSD;
bootflags = grub_bsd_parse_flags (ctxt->state, netbsd_flags);
if (grub_bsd_load (argc, argv) == GRUB_ERR_NONE)
{
if (is_elf_kernel)
{
grub_file_t file;
file = grub_file_open (argv[0], GRUB_FILE_TYPE_BSD_KERNEL);
if (! file)
return grub_errno;
if (is_64bit)
err = grub_netbsd_load_elf_meta64 (relocator, file, argv[0], &kern_end);
else
err = grub_netbsd_load_elf_meta32 (relocator, file, argv[0], &kern_end);
if (err)
return err;
}
{
char bootpath[GRUB_NETBSD_MAX_BOOTPATH_LEN];
char *name;
name = grub_strrchr (argv[0], '/');
if (name)
name++;
else
name = argv[0];
grub_memset (bootpath, 0, sizeof (bootpath));
grub_strncpy (bootpath, name, sizeof (bootpath) - 1);
grub_bsd_add_meta (NETBSD_BTINFO_BOOTPATH, bootpath, sizeof (bootpath));
}
if (ctxt->state[NETBSD_ROOT_ARG].set)
{
char root[GRUB_NETBSD_MAX_ROOTDEVICE_LEN];
grub_memset (root, 0, sizeof (root));
grub_strncpy (root, ctxt->state[NETBSD_ROOT_ARG].arg,
sizeof (root) - 1);
grub_bsd_add_meta (NETBSD_BTINFO_ROOTDEVICE, root, sizeof (root));
}
if (ctxt->state[NETBSD_SERIAL_ARG].set)
{
struct grub_netbsd_btinfo_serial serial;
const char *ptr;
grub_memset (&serial, 0, sizeof (serial));
grub_strcpy (serial.devname, "com");
serial.addr = grub_ns8250_hw_get_port (0);
serial.speed = 9600;
if (ctxt->state[NETBSD_SERIAL_ARG].arg)
{
ptr = ctxt->state[NETBSD_SERIAL_ARG].arg;
if (grub_memcmp (ptr, "com", sizeof ("com") - 1) == 0)
{
ptr += sizeof ("com") - 1;
serial.addr
= grub_ns8250_hw_get_port (grub_strtoul (ptr, &ptr, 0));
}
else
serial.addr = grub_strtoul (ptr, &ptr, 0);
if (grub_errno)
return grub_errno;
if (*ptr == ',')
{
ptr++;
serial.speed = grub_strtoul (ptr, &ptr, 0);
if (grub_errno)
return grub_errno;
}
}
grub_bsd_add_meta (NETBSD_BTINFO_CONSOLE, &serial, sizeof (serial));
}
else
{
struct grub_netbsd_btinfo_serial cons;
grub_memset (&cons, 0, sizeof (cons));
grub_strcpy (cons.devname, "pc");
grub_bsd_add_meta (NETBSD_BTINFO_CONSOLE, &cons, sizeof (cons));
}
grub_netbsd_add_boot_disk_and_wedge ();
grub_loader_set (grub_netbsd_boot, grub_bsd_unload, 0);
}
return grub_errno;
}
static grub_err_t
grub_cmd_freebsd_loadenv (grub_command_t cmd __attribute__ ((unused)),
int argc, char *argv[])
{
grub_file_t file = 0;
char *buf = 0, *curr, *next;
int len;
if (! grub_loader_is_loaded ())
return grub_error (GRUB_ERR_BAD_ARGUMENT,
N_("you need to load the kernel first"));
if (kernel_type != KERNEL_TYPE_FREEBSD)
return grub_error (GRUB_ERR_BAD_ARGUMENT,
"only FreeBSD supports environment");
if (argc == 0)
{
grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
goto fail;
}
file = grub_file_open (argv[0], GRUB_FILE_TYPE_FREEBSD_ENV);
if ((!file) || (!file->size))
goto fail;
len = file->size;
buf = grub_malloc (len + 1);
if (!buf)
goto fail;
if (grub_file_read (file, buf, len) != len)
goto fail;
buf[len] = 0;
next = buf;
while (next)
{
char *p;
curr = next;
next = grub_strchr (curr, '\n');
if (next)
{
p = next - 1;
while (p > curr)
{
if ((*p != '\r') && (*p != ' ') && (*p != '\t'))
break;
p--;
}
if ((p > curr) && (*p == '"'))
p--;
*(p + 1) = 0;
next++;
}
if (*curr == '#')
continue;
p = grub_strchr (curr, '=');
if (!p)
continue;
*(p++) = 0;
if (*curr)
{
char *name;
if (*p == '"')
p++;
name = grub_xasprintf ("kFreeBSD.%s", curr);
if (!name)
goto fail;
if (grub_env_set (name, p))
{
grub_free (name);
goto fail;
}
grub_free (name);
}
}
fail:
grub_free (buf);
if (file)
grub_file_close (file);
return grub_errno;
}
static grub_err_t
grub_cmd_freebsd_module (grub_command_t cmd __attribute__ ((unused)),
int argc, char *argv[])
{
grub_file_t file = 0;
int modargc;
char **modargv;
const char *type;
grub_err_t err;
void *src;
if (! grub_loader_is_loaded ())
return grub_error (GRUB_ERR_BAD_ARGUMENT,
N_("you need to load the kernel first"));
if (kernel_type != KERNEL_TYPE_FREEBSD)
return grub_error (GRUB_ERR_BAD_ARGUMENT, "no FreeBSD loaded");
if (!is_elf_kernel)
return grub_error (GRUB_ERR_BAD_ARGUMENT,
"only ELF kernel supports module");
/* List the current modules if no parameter. */
if (!argc)
{
grub_freebsd_list_modules ();
return 0;
}
file = grub_file_open (argv[0], GRUB_FILE_TYPE_FREEBSD_MODULE);
if ((!file) || (!file->size))
goto fail;
{
grub_relocator_chunk_t ch;
err = grub_relocator_alloc_chunk_addr (relocator, &ch, kern_end,
file->size);
if (err)
goto fail;
src = get_virtual_current_address (ch);
}
grub_file_read (file, src, file->size);
if (grub_errno)
goto fail;
modargc = argc - 1;
modargv = argv + 1;
if (modargc && (! grub_memcmp (modargv[0], "type=", 5)))
{
type = &modargv[0][5];
modargc--;
modargv++;
}
else
type = FREEBSD_MODTYPE_RAW;
err = grub_freebsd_add_meta_module (argv[0], type, modargc, modargv,
kern_end, file->size);
if (err)
goto fail;
kern_end = ALIGN_PAGE (kern_end + file->size);
fail:
if (file)
grub_file_close (file);
return grub_errno;
}
static grub_err_t
grub_netbsd_module_load (char *filename, grub_uint32_t type)
{
grub_file_t file = 0;
void *src;
grub_err_t err;
file = grub_file_open (filename, GRUB_FILE_TYPE_NETBSD_MODULE);
if ((!file) || (!file->size))
goto fail;
{
grub_relocator_chunk_t ch;
err = grub_relocator_alloc_chunk_addr (relocator, &ch, kern_end,
file->size);
if (err)
goto fail;
src = get_virtual_current_address (ch);
}
grub_file_read (file, src, file->size);
if (grub_errno)
goto fail;
err = grub_netbsd_add_meta_module (filename, type, kern_end, file->size);
if (err)
goto fail;
kern_end = ALIGN_PAGE (kern_end + file->size);
fail:
if (file)
grub_file_close (file);
return grub_errno;
}
static grub_err_t
grub_cmd_netbsd_module (grub_command_t cmd,
int argc, char *argv[])
{
grub_uint32_t type;
if (! grub_loader_is_loaded ())
return grub_error (GRUB_ERR_BAD_ARGUMENT,
N_("you need to load the kernel first"));
if (kernel_type != KERNEL_TYPE_NETBSD)
return grub_error (GRUB_ERR_BAD_ARGUMENT, "no NetBSD loaded");
if (!is_elf_kernel)
return grub_error (GRUB_ERR_BAD_ARGUMENT,
"only ELF kernel supports module");
/* List the current modules if no parameter. */
if (!argc)
{
grub_netbsd_list_modules ();
return 0;
}
if (grub_strcmp (cmd->name, "knetbsd_module_elf") == 0)
type = GRUB_NETBSD_MODULE_ELF;
else
type = GRUB_NETBSD_MODULE_RAW;
return grub_netbsd_module_load (argv[0], type);
}
static grub_err_t
grub_cmd_freebsd_module_elf (grub_command_t cmd __attribute__ ((unused)),
int argc, char *argv[])
{
grub_file_t file = 0;
grub_err_t err;
if (! grub_loader_is_loaded ())
return grub_error (GRUB_ERR_BAD_ARGUMENT,
N_("you need to load the kernel first"));
if (kernel_type != KERNEL_TYPE_FREEBSD)
return grub_error (GRUB_ERR_BAD_ARGUMENT,
"only FreeBSD supports module");
if (! is_elf_kernel)
return grub_error (GRUB_ERR_BAD_ARGUMENT,
"only ELF kernel supports module");
/* List the current modules if no parameter. */
if (! argc)
{
grub_freebsd_list_modules ();
return 0;
}
file = grub_file_open (argv[0], GRUB_FILE_TYPE_FREEBSD_MODULE_ELF);
if (!file)
return grub_errno;
if (!file->size)
{
grub_file_close (file);
return grub_errno;
}
if (is_64bit)
err = grub_freebsd_load_elfmodule_obj64 (relocator, file,
argc, argv, &kern_end);
else
err = grub_freebsd_load_elfmodule32 (relocator, file,
argc, argv, &kern_end);
grub_file_close (file);
return err;
}
static grub_err_t
grub_cmd_openbsd_ramdisk (grub_command_t cmd __attribute__ ((unused)),
int argc, char *args[])
{
grub_file_t file;
grub_size_t size;
if (argc != 1)
return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
if (! grub_loader_is_loaded ())
return grub_error (GRUB_ERR_BAD_ARGUMENT,
N_("you need to load the kernel first"));
if (kernel_type != KERNEL_TYPE_OPENBSD)
return grub_error (GRUB_ERR_BAD_OS, "no kOpenBSD loaded");
if (!openbsd_ramdisk.max_size)
return grub_error (GRUB_ERR_BAD_OS, "your kOpenBSD doesn't support ramdisk");
file = grub_file_open (args[0], GRUB_FILE_TYPE_OPENBSD_RAMDISK);
if (! file)
return grub_errno;
size = grub_file_size (file);
if (size > openbsd_ramdisk.max_size)
{
grub_file_close (file);
return grub_error (GRUB_ERR_BAD_OS, "your kOpenBSD supports ramdisk only"
" up to %u bytes, however you supplied a %u bytes one",
openbsd_ramdisk.max_size, size);
}
if (grub_file_read (file, openbsd_ramdisk.target, size)
!= (grub_ssize_t) (size))
{
grub_file_close (file);
if (!grub_errno)
grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), args[0]);
return grub_errno;
}
grub_memset (openbsd_ramdisk.target + size, 0,
openbsd_ramdisk.max_size - size);
*openbsd_ramdisk.size = ALIGN_UP (size, 512);
return GRUB_ERR_NONE;
}
static grub_extcmd_t cmd_freebsd, cmd_openbsd, cmd_netbsd;
static grub_command_t cmd_freebsd_loadenv, cmd_freebsd_module;
static grub_command_t cmd_netbsd_module, cmd_freebsd_module_elf;
static grub_command_t cmd_netbsd_module_elf, cmd_openbsd_ramdisk;
GRUB_MOD_INIT (bsd)
{
/* Net and OpenBSD kernels are often compressed. */
grub_dl_load ("gzio");
cmd_freebsd = grub_register_extcmd ("kfreebsd", grub_cmd_freebsd, 0,
N_("FILE"), N_("Load kernel of FreeBSD."),
freebsd_opts);
cmd_openbsd = grub_register_extcmd ("kopenbsd", grub_cmd_openbsd, 0,
N_("FILE"), N_("Load kernel of OpenBSD."),
openbsd_opts);
cmd_netbsd = grub_register_extcmd ("knetbsd", grub_cmd_netbsd, 0,
N_("FILE"), N_("Load kernel of NetBSD."),
netbsd_opts);
cmd_freebsd_loadenv =
grub_register_command ("kfreebsd_loadenv", grub_cmd_freebsd_loadenv,
0, N_("Load FreeBSD env."));
cmd_freebsd_module =
grub_register_command ("kfreebsd_module", grub_cmd_freebsd_module,
0, N_("Load FreeBSD kernel module."));
cmd_netbsd_module =
grub_register_command ("knetbsd_module", grub_cmd_netbsd_module,
0, N_("Load NetBSD kernel module."));
cmd_netbsd_module_elf =
grub_register_command ("knetbsd_module_elf", grub_cmd_netbsd_module,
0, N_("Load NetBSD kernel module (ELF)."));
cmd_freebsd_module_elf =
grub_register_command ("kfreebsd_module_elf", grub_cmd_freebsd_module_elf,
0, N_("Load FreeBSD kernel module (ELF)."));
cmd_openbsd_ramdisk = grub_register_command ("kopenbsd_ramdisk",
grub_cmd_openbsd_ramdisk, 0,
/* TRANSLATORS: ramdisk isn't identifier,
it can be translated. */
N_("Load kOpenBSD ramdisk."));
my_mod = mod;
}
GRUB_MOD_FINI (bsd)
{
grub_unregister_extcmd (cmd_freebsd);
grub_unregister_extcmd (cmd_openbsd);
grub_unregister_extcmd (cmd_netbsd);
grub_unregister_command (cmd_freebsd_loadenv);
grub_unregister_command (cmd_freebsd_module);
grub_unregister_command (cmd_netbsd_module);
grub_unregister_command (cmd_freebsd_module_elf);
grub_unregister_command (cmd_netbsd_module_elf);
grub_unregister_command (cmd_openbsd_ramdisk);
grub_bsd_unload ();
}