mirror of
https://github.com/ventoy/Ventoy.git
synced 2025-12-08 08:42:47 -05:00
copy files need mod for loader: Drop argv[] argument in grub_initrd_load()
Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
This commit is contained in:
509
GRUB2/MOD_SRC/grub-2.04/grub-core/loader/arm/linux.c
Normal file
509
GRUB2/MOD_SRC/grub-2.04/grub-core/loader/arm/linux.c
Normal file
@@ -0,0 +1,509 @@
|
||||
/* linux.c - boot Linux */
|
||||
/*
|
||||
* GRUB -- GRand Unified Bootloader
|
||||
* Copyright (C) 2013 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/dl.h>
|
||||
#include <grub/fdt.h>
|
||||
#include <grub/file.h>
|
||||
#include <grub/loader.h>
|
||||
#include <grub/mm.h>
|
||||
#include <grub/misc.h>
|
||||
#include <grub/command.h>
|
||||
#include <grub/cache.h>
|
||||
#include <grub/cpu/linux.h>
|
||||
#include <grub/lib/cmdline.h>
|
||||
#include <grub/linux.h>
|
||||
#include <grub/verify.h>
|
||||
|
||||
GRUB_MOD_LICENSE ("GPLv3+");
|
||||
|
||||
static grub_dl_t my_mod;
|
||||
|
||||
static grub_addr_t initrd_start;
|
||||
static grub_addr_t initrd_end;
|
||||
|
||||
static grub_addr_t linux_addr;
|
||||
static grub_size_t linux_size;
|
||||
|
||||
static char *linux_args;
|
||||
|
||||
static grub_uint32_t machine_type;
|
||||
static const void *current_fdt;
|
||||
|
||||
typedef void (*kernel_entry_t) (int, unsigned long, void *);
|
||||
|
||||
#define LINUX_PHYS_OFFSET (0x00008000)
|
||||
#define LINUX_INITRD_PHYS_OFFSET (LINUX_PHYS_OFFSET + 0x03000000)
|
||||
#define LINUX_FDT_PHYS_OFFSET (LINUX_INITRD_PHYS_OFFSET - 0x10000)
|
||||
|
||||
static grub_size_t
|
||||
get_atag_size (const grub_uint32_t *atag)
|
||||
{
|
||||
const grub_uint32_t *atag0 = atag;
|
||||
while (atag[0] && atag[1])
|
||||
atag += atag[0];
|
||||
return atag - atag0;
|
||||
}
|
||||
|
||||
/*
|
||||
* linux_prepare_fdt():
|
||||
* Prepares a loaded FDT for being passed to Linux.
|
||||
* Merges in command line parameters and sets up initrd addresses.
|
||||
*/
|
||||
static grub_err_t
|
||||
linux_prepare_atag (void *target_atag)
|
||||
{
|
||||
const grub_uint32_t *atag_orig = (const grub_uint32_t *) current_fdt;
|
||||
grub_uint32_t *tmp_atag, *to;
|
||||
const grub_uint32_t *from;
|
||||
grub_size_t tmp_size;
|
||||
grub_size_t arg_size = grub_strlen (linux_args);
|
||||
char *cmdline_orig = NULL;
|
||||
grub_size_t cmdline_orig_len = 0;
|
||||
|
||||
/* some place for cmdline, initrd and terminator. */
|
||||
tmp_size = get_atag_size (atag_orig) + 20 + (arg_size) / 4;
|
||||
tmp_atag = grub_malloc (tmp_size * sizeof (grub_uint32_t));
|
||||
if (!tmp_atag)
|
||||
return grub_errno;
|
||||
|
||||
for (from = atag_orig, to = tmp_atag; from[0] && from[1];
|
||||
from += from[0])
|
||||
switch (from[1])
|
||||
{
|
||||
case 0x54410004:
|
||||
case 0x54410005:
|
||||
case 0x54420005:
|
||||
break;
|
||||
case 0x54410009:
|
||||
if (*(char *) (from + 2))
|
||||
{
|
||||
cmdline_orig = (char *) (from + 2);
|
||||
cmdline_orig_len = grub_strlen (cmdline_orig) + 1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
grub_memcpy (to, from, sizeof (grub_uint32_t) * from[0]);
|
||||
to += from[0];
|
||||
break;
|
||||
}
|
||||
|
||||
grub_dprintf ("linux", "linux inherited args: '%s'\n",
|
||||
cmdline_orig ? : "");
|
||||
grub_dprintf ("linux", "linux_args: '%s'\n", linux_args);
|
||||
|
||||
/* Generate and set command line */
|
||||
to[0] = 3 + (arg_size + cmdline_orig_len) / 4;
|
||||
to[1] = 0x54410009;
|
||||
if (cmdline_orig)
|
||||
{
|
||||
grub_memcpy ((char *) to + 8, cmdline_orig, cmdline_orig_len - 1);
|
||||
*((char *) to + 8 + cmdline_orig_len - 1) = ' ';
|
||||
}
|
||||
grub_memcpy ((char *) to + 8 + cmdline_orig_len, linux_args, arg_size);
|
||||
grub_memset ((char *) to + 8 + cmdline_orig_len + arg_size, 0,
|
||||
4 - ((arg_size + cmdline_orig_len) & 3));
|
||||
to += to[0];
|
||||
|
||||
if (initrd_start && initrd_end)
|
||||
{
|
||||
/*
|
||||
* We're using physical addresses, so even if we have LPAE, we're
|
||||
* restricted to a 32-bit address space.
|
||||
*/
|
||||
grub_dprintf ("loader", "Initrd @ 0x%08x-0x%08x\n",
|
||||
initrd_start, initrd_end);
|
||||
|
||||
to[0] = 4;
|
||||
to[1] = 0x54420005;
|
||||
to[2] = initrd_start;
|
||||
to[3] = initrd_end - initrd_start;
|
||||
to += 4;
|
||||
}
|
||||
|
||||
to[0] = 0;
|
||||
to[1] = 0;
|
||||
to += 2;
|
||||
|
||||
/* Copy updated FDT to its launch location */
|
||||
grub_memcpy (target_atag, tmp_atag, sizeof (grub_uint32_t) * (to - tmp_atag));
|
||||
grub_free (tmp_atag);
|
||||
|
||||
grub_dprintf ("loader", "ATAG updated for Linux boot\n");
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
/*
|
||||
* linux_prepare_fdt():
|
||||
* Prepares a loaded FDT for being passed to Linux.
|
||||
* Merges in command line parameters and sets up initrd addresses.
|
||||
*/
|
||||
static grub_err_t
|
||||
linux_prepare_fdt (void *target_fdt)
|
||||
{
|
||||
int node;
|
||||
int retval;
|
||||
int tmp_size;
|
||||
void *tmp_fdt;
|
||||
|
||||
tmp_size = grub_fdt_get_totalsize (current_fdt) + 0x100 + grub_strlen (linux_args);
|
||||
tmp_fdt = grub_malloc (tmp_size);
|
||||
if (!tmp_fdt)
|
||||
return grub_errno;
|
||||
|
||||
grub_memcpy (tmp_fdt, current_fdt, grub_fdt_get_totalsize (current_fdt));
|
||||
grub_fdt_set_totalsize (tmp_fdt, tmp_size);
|
||||
|
||||
/* Find or create '/chosen' node */
|
||||
node = grub_fdt_find_subnode (tmp_fdt, 0, "chosen");
|
||||
if (node < 0)
|
||||
{
|
||||
grub_dprintf ("linux", "No 'chosen' node in FDT - creating.\n");
|
||||
node = grub_fdt_add_subnode (tmp_fdt, 0, "chosen");
|
||||
if (node < 0)
|
||||
goto failure;
|
||||
}
|
||||
|
||||
grub_dprintf ("linux", "linux_args: '%s'\n", linux_args);
|
||||
|
||||
/* Generate and set command line */
|
||||
retval = grub_fdt_set_prop (tmp_fdt, node, "bootargs", linux_args,
|
||||
grub_strlen (linux_args) + 1);
|
||||
if (retval)
|
||||
goto failure;
|
||||
|
||||
if (initrd_start && initrd_end)
|
||||
{
|
||||
/*
|
||||
* We're using physical addresses, so even if we have LPAE, we're
|
||||
* restricted to a 32-bit address space.
|
||||
*/
|
||||
grub_dprintf ("loader", "Initrd @ 0x%08x-0x%08x\n",
|
||||
initrd_start, initrd_end);
|
||||
|
||||
retval = grub_fdt_set_prop32 (tmp_fdt, node, "linux,initrd-start",
|
||||
initrd_start);
|
||||
if (retval)
|
||||
goto failure;
|
||||
retval = grub_fdt_set_prop32 (tmp_fdt, node, "linux,initrd-end",
|
||||
initrd_end);
|
||||
if (retval)
|
||||
goto failure;
|
||||
}
|
||||
|
||||
/* Copy updated FDT to its launch location */
|
||||
grub_memcpy (target_fdt, tmp_fdt, tmp_size);
|
||||
grub_free (tmp_fdt);
|
||||
|
||||
grub_dprintf ("loader", "FDT updated for Linux boot\n");
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
|
||||
failure:
|
||||
grub_free (tmp_fdt);
|
||||
return grub_error (GRUB_ERR_BAD_ARGUMENT, "unable to prepare FDT");
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
linux_boot (void)
|
||||
{
|
||||
kernel_entry_t linuxmain;
|
||||
int fdt_valid, atag_valid;
|
||||
void *target_fdt = 0;
|
||||
|
||||
fdt_valid = (current_fdt && grub_fdt_check_header_nosize (current_fdt) == 0);
|
||||
atag_valid = ((((const grub_uint16_t *) current_fdt)[3] & ~3) == 0x5440
|
||||
&& *((const grub_uint32_t *) current_fdt));
|
||||
grub_dprintf ("loader", "atag: %p, %x, %x, %s, %s\n",
|
||||
current_fdt,
|
||||
((const grub_uint16_t *) current_fdt)[3],
|
||||
*((const grub_uint32_t *) current_fdt),
|
||||
(const char *) current_fdt,
|
||||
(const char *) current_fdt + 1);
|
||||
|
||||
if (!fdt_valid && machine_type == GRUB_ARM_MACHINE_TYPE_FDT)
|
||||
return grub_error (GRUB_ERR_FILE_NOT_FOUND,
|
||||
N_("device tree must be supplied (see `devicetree' command)"));
|
||||
|
||||
grub_arch_sync_caches ((void *) linux_addr, linux_size);
|
||||
|
||||
grub_dprintf ("loader", "Kernel at: 0x%x\n", linux_addr);
|
||||
|
||||
if (fdt_valid || atag_valid)
|
||||
{
|
||||
#ifdef GRUB_MACHINE_EFI
|
||||
grub_size_t size;
|
||||
if (fdt_valid)
|
||||
size = grub_fdt_get_totalsize (current_fdt);
|
||||
else
|
||||
size = 4 * get_atag_size (current_fdt);
|
||||
size += grub_strlen (linux_args) + 256;
|
||||
target_fdt = grub_efi_allocate_loader_memory (LINUX_FDT_PHYS_OFFSET, size);
|
||||
if (!target_fdt)
|
||||
return grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
|
||||
#else
|
||||
target_fdt = (void *) LINUX_FDT_ADDRESS;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (fdt_valid)
|
||||
{
|
||||
grub_err_t err;
|
||||
|
||||
err = linux_prepare_fdt (target_fdt);
|
||||
if (err)
|
||||
return err;
|
||||
grub_dprintf ("loader", "FDT @ %p\n", target_fdt);
|
||||
}
|
||||
else if (atag_valid)
|
||||
{
|
||||
grub_err_t err;
|
||||
|
||||
err = linux_prepare_atag (target_fdt);
|
||||
if (err)
|
||||
return err;
|
||||
grub_dprintf ("loader", "ATAG @ %p\n", target_fdt);
|
||||
}
|
||||
|
||||
grub_dprintf ("loader", "Jumping to Linux...\n");
|
||||
|
||||
/* Boot the kernel.
|
||||
* Arguments to kernel:
|
||||
* r0 - 0
|
||||
* r1 - machine type
|
||||
* r2 - address of DTB
|
||||
*/
|
||||
linuxmain = (kernel_entry_t) linux_addr;
|
||||
|
||||
grub_arm_disable_caches_mmu ();
|
||||
|
||||
linuxmain (0, machine_type, target_fdt);
|
||||
|
||||
return grub_error (GRUB_ERR_BAD_OS, "Linux call returned");
|
||||
}
|
||||
|
||||
/*
|
||||
* Only support zImage, so no relocations necessary
|
||||
*/
|
||||
static grub_err_t
|
||||
linux_load (const char *filename, grub_file_t file)
|
||||
{
|
||||
struct linux_arm_kernel_header *lh;
|
||||
int size;
|
||||
|
||||
size = grub_file_size (file);
|
||||
|
||||
linux_addr = LINUX_ADDRESS;
|
||||
grub_dprintf ("loader", "Loading Linux to 0x%08x\n",
|
||||
(grub_addr_t) linux_addr);
|
||||
|
||||
if (grub_file_read (file, (void *) linux_addr, size) != size)
|
||||
{
|
||||
if (!grub_errno)
|
||||
grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
|
||||
filename);
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
lh = (void *) linux_addr;
|
||||
|
||||
if ((grub_size_t) size > sizeof (*lh) &&
|
||||
lh->magic == GRUB_LINUX_ARM_MAGIC_SIGNATURE)
|
||||
;
|
||||
else if (size > 0x8000 && *(grub_uint32_t *) (linux_addr) == 0xea000006
|
||||
&& machine_type == GRUB_ARM_MACHINE_TYPE_RASPBERRY_PI)
|
||||
grub_memmove ((void *) linux_addr, (void *) (linux_addr + 0x8000),
|
||||
size - 0x8000);
|
||||
else
|
||||
return grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("invalid zImage"));
|
||||
|
||||
linux_size = size;
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
linux_unload (void)
|
||||
{
|
||||
grub_dl_unref (my_mod);
|
||||
|
||||
grub_free (linux_args);
|
||||
linux_args = NULL;
|
||||
|
||||
initrd_start = initrd_end = 0;
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
|
||||
int argc, char *argv[])
|
||||
{
|
||||
int size;
|
||||
grub_err_t err;
|
||||
grub_file_t file;
|
||||
grub_dl_ref (my_mod);
|
||||
|
||||
if (argc == 0)
|
||||
return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
|
||||
|
||||
file = grub_file_open (argv[0], GRUB_FILE_TYPE_LINUX_KERNEL);
|
||||
if (!file)
|
||||
goto fail;
|
||||
|
||||
err = linux_load (argv[0], file);
|
||||
grub_file_close (file);
|
||||
if (err)
|
||||
goto fail;
|
||||
|
||||
grub_loader_set (linux_boot, linux_unload, 0);
|
||||
|
||||
size = grub_loader_cmdline_size (argc, argv);
|
||||
linux_args = grub_malloc (size + sizeof (LINUX_IMAGE));
|
||||
if (!linux_args)
|
||||
{
|
||||
grub_loader_unset();
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Create kernel command line. */
|
||||
grub_memcpy (linux_args, LINUX_IMAGE, sizeof (LINUX_IMAGE));
|
||||
err = grub_create_loader_cmdline (argc, argv,
|
||||
linux_args + sizeof (LINUX_IMAGE) - 1, size,
|
||||
GRUB_VERIFY_KERNEL_CMDLINE);
|
||||
if (err)
|
||||
goto fail;
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
|
||||
fail:
|
||||
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;
|
||||
grub_size_t size = 0;
|
||||
struct grub_linux_initrd_context initrd_ctx = { 0, 0, 0 };
|
||||
|
||||
if (argc == 0)
|
||||
return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
|
||||
|
||||
file = grub_file_open (argv[0], GRUB_FILE_TYPE_LINUX_INITRD);
|
||||
if (!file)
|
||||
return grub_errno;
|
||||
|
||||
if (grub_initrd_init (argc, argv, &initrd_ctx))
|
||||
goto fail;
|
||||
|
||||
size = grub_get_initrd_size (&initrd_ctx);
|
||||
|
||||
initrd_start = LINUX_INITRD_ADDRESS;
|
||||
|
||||
grub_dprintf ("loader", "Loading initrd to 0x%08x\n",
|
||||
(grub_addr_t) initrd_start);
|
||||
|
||||
if (grub_initrd_load (&initrd_ctx, argv, (void *) initrd_start))
|
||||
goto fail;
|
||||
|
||||
initrd_end = initrd_start + size;
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
|
||||
fail:
|
||||
grub_file_close (file);
|
||||
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
load_dtb (grub_file_t dtb, int size)
|
||||
{
|
||||
void *new_fdt = grub_zalloc (size);
|
||||
if (!new_fdt)
|
||||
return grub_errno;
|
||||
grub_dprintf ("loader", "Loading device tree to %p\n",
|
||||
new_fdt);
|
||||
if ((grub_file_read (dtb, new_fdt, size) != size)
|
||||
|| (grub_fdt_check_header (new_fdt, size) != 0))
|
||||
{
|
||||
grub_free (new_fdt);
|
||||
return grub_error (GRUB_ERR_BAD_OS, N_("invalid device tree"));
|
||||
}
|
||||
|
||||
grub_fdt_set_totalsize (new_fdt, size);
|
||||
current_fdt = new_fdt;
|
||||
/*
|
||||
* We've successfully loaded an FDT, so any machine type passed
|
||||
* from firmware is now obsolete.
|
||||
*/
|
||||
machine_type = GRUB_ARM_MACHINE_TYPE_FDT;
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_cmd_devicetree (grub_command_t cmd __attribute__ ((unused)),
|
||||
int argc, char *argv[])
|
||||
{
|
||||
grub_file_t dtb;
|
||||
int size;
|
||||
|
||||
if (argc != 1)
|
||||
return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
|
||||
|
||||
dtb = grub_file_open (argv[0], GRUB_FILE_TYPE_DEVICE_TREE_IMAGE);
|
||||
if (!dtb)
|
||||
return grub_errno;
|
||||
|
||||
size = grub_file_size (dtb);
|
||||
if (size == 0)
|
||||
grub_error (GRUB_ERR_BAD_OS, "empty file");
|
||||
else
|
||||
load_dtb (dtb, size);
|
||||
grub_file_close (dtb);
|
||||
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
static grub_command_t cmd_linux, cmd_initrd, cmd_devicetree;
|
||||
|
||||
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."));
|
||||
cmd_devicetree = grub_register_command ("devicetree", grub_cmd_devicetree,
|
||||
/* TRANSLATORS: DTB stands for device tree blob. */
|
||||
0, N_("Load DTB file."));
|
||||
my_mod = mod;
|
||||
current_fdt = (const void *) grub_arm_firmware_get_boot_data ();
|
||||
machine_type = grub_arm_firmware_get_machine_type ();
|
||||
}
|
||||
|
||||
GRUB_MOD_FINI (linux)
|
||||
{
|
||||
grub_unregister_command (cmd_linux);
|
||||
grub_unregister_command (cmd_initrd);
|
||||
grub_unregister_command (cmd_devicetree);
|
||||
}
|
||||
490
GRUB2/MOD_SRC/grub-2.04/grub-core/loader/i386/pc/linux.c
Normal file
490
GRUB2/MOD_SRC/grub-2.04/grub-core/loader/i386/pc/linux.c
Normal file
@@ -0,0 +1,490 @@
|
||||
/* 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/file.h>
|
||||
#include <grub/err.h>
|
||||
#include <grub/device.h>
|
||||
#include <grub/disk.h>
|
||||
#include <grub/misc.h>
|
||||
#include <grub/types.h>
|
||||
#include <grub/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>
|
||||
#include <grub/lib/cmdline.h>
|
||||
#include <grub/linux.h>
|
||||
|
||||
GRUB_MOD_LICENSE ("GPLv3+");
|
||||
|
||||
#define GRUB_LINUX_CL_OFFSET 0x9000
|
||||
|
||||
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_size_t maximal_cmdline_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;
|
||||
state.a20 = 1;
|
||||
|
||||
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 int
|
||||
target_hook (grub_uint64_t addr, grub_uint64_t size, grub_memory_type_t type,
|
||||
void *data)
|
||||
{
|
||||
grub_uint64_t *result = data;
|
||||
grub_uint64_t candidate;
|
||||
|
||||
if (type != GRUB_MEMORY_AVAILABLE)
|
||||
return 0;
|
||||
if (addr >= 0xa0000)
|
||||
return 0;
|
||||
if (addr + size >= 0xa0000)
|
||||
size = 0xa0000 - addr;
|
||||
|
||||
/* Put the real mode part at as a high location as possible. */
|
||||
candidate = addr + size - (GRUB_LINUX_CL_OFFSET + maximal_cmdline_size);
|
||||
/* But it must not exceed the traditional area. */
|
||||
if (candidate > GRUB_LINUX_OLD_REAL_MODE_ADDR)
|
||||
candidate = GRUB_LINUX_OLD_REAL_MODE_ADDR;
|
||||
if (candidate < addr)
|
||||
return 0;
|
||||
|
||||
if (candidate > *result || *result == (grub_uint64_t) -1)
|
||||
*result = candidate;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static grub_addr_t
|
||||
grub_find_real_target (void)
|
||||
{
|
||||
grub_uint64_t result = (grub_uint64_t) -1;
|
||||
|
||||
grub_mmap_iterate (target_hook, &result);
|
||||
return result;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
|
||||
int argc, char *argv[])
|
||||
{
|
||||
grub_file_t file = 0;
|
||||
struct linux_i386_kernel_header lh;
|
||||
grub_uint8_t setup_sects;
|
||||
grub_size_t real_size;
|
||||
grub_ssize_t len;
|
||||
int i;
|
||||
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, N_("filename expected"));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
file = grub_file_open (argv[0], GRUB_FILE_TYPE_LINUX_KERNEL);
|
||||
if (! file)
|
||||
goto fail;
|
||||
|
||||
if (grub_file_read (file, &lh, sizeof (lh)) != sizeof (lh))
|
||||
{
|
||||
if (!grub_errno)
|
||||
grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
|
||||
argv[0]);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (lh.boot_flag != grub_cpu_to_le16_compile_time (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;
|
||||
|
||||
maximal_cmdline_size = 256;
|
||||
|
||||
if (lh.header == grub_cpu_to_le32_compile_time (GRUB_LINUX_I386_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;
|
||||
|
||||
if (grub_le_to_cpu16 (lh.version) >= 0x0206)
|
||||
maximal_cmdline_size = grub_le_to_cpu32 (lh.cmdline_size) + 1;
|
||||
|
||||
grub_linux_real_target = grub_find_real_target ();
|
||||
if (grub_linux_real_target == (grub_addr_t)-1)
|
||||
{
|
||||
grub_error (GRUB_ERR_OUT_OF_RANGE,
|
||||
"no appropriate low memory found");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (grub_le_to_cpu16 (lh.version) >= 0x0201)
|
||||
{
|
||||
lh.heap_end_ptr = grub_cpu_to_le16_compile_time (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_compile_time (GRUB_LINUX_CL_MAGIC);
|
||||
lh.cl_offset = grub_cpu_to_le16_compile_time (GRUB_LINUX_CL_OFFSET);
|
||||
lh.setup_move_size = grub_cpu_to_le16_compile_time (GRUB_LINUX_CL_OFFSET
|
||||
+ maximal_cmdline_size);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Your kernel is quite old... */
|
||||
lh.cl_magic = grub_cpu_to_le16_compile_time (GRUB_LINUX_CL_MAGIC);
|
||||
lh.cl_offset = grub_cpu_to_le16_compile_time (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;
|
||||
}
|
||||
|
||||
grub_dprintf ("linux", "[Linux-%s, setup=0x%x, size=0x%x]\n",
|
||||
grub_linux_is_bzimage ? "bzImage" : "zImage",
|
||||
(unsigned) real_size,
|
||||
(unsigned) grub_linux16_prot_size);
|
||||
|
||||
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;
|
||||
/* Fallthrough. */
|
||||
case 'm':
|
||||
shift += 10;
|
||||
/* Fallthrough. */
|
||||
case 'k':
|
||||
shift += 10;
|
||||
/* Fallthrough. */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check an overflow. */
|
||||
if (linux_mem_size > (~0UL >> shift))
|
||||
linux_mem_size = 0;
|
||||
else
|
||||
linux_mem_size <<= shift;
|
||||
}
|
||||
}
|
||||
|
||||
relocator = grub_relocator_new ();
|
||||
if (!relocator)
|
||||
goto fail;
|
||||
|
||||
{
|
||||
grub_relocator_chunk_t ch;
|
||||
err = grub_relocator_alloc_chunk_addr (relocator, &ch,
|
||||
grub_linux_real_target,
|
||||
GRUB_LINUX_CL_OFFSET
|
||||
+ maximal_cmdline_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)
|
||||
{
|
||||
if (!grub_errno)
|
||||
grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
|
||||
argv[0]);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (lh.header != grub_cpu_to_le32_compile_time (GRUB_LINUX_I386_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));
|
||||
|
||||
/* Create kernel command line. */
|
||||
grub_memcpy ((char *)grub_linux_real_chunk + GRUB_LINUX_CL_OFFSET,
|
||||
LINUX_IMAGE, sizeof (LINUX_IMAGE));
|
||||
err = grub_create_loader_cmdline (argc, argv,
|
||||
(char *)grub_linux_real_chunk
|
||||
+ GRUB_LINUX_CL_OFFSET + sizeof (LINUX_IMAGE) - 1,
|
||||
maximal_cmdline_size
|
||||
- (sizeof (LINUX_IMAGE) - 1),
|
||||
GRUB_VERIFY_KERNEL_CMDLINE);
|
||||
if (err)
|
||||
goto fail;
|
||||
|
||||
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, len) != len && !grub_errno)
|
||||
grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
|
||||
argv[0]);
|
||||
|
||||
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_size_t size = 0;
|
||||
grub_addr_t addr_max, addr_min;
|
||||
struct linux_i386_kernel_header *lh;
|
||||
grub_uint8_t *initrd_chunk;
|
||||
grub_addr_t initrd_addr;
|
||||
grub_err_t err;
|
||||
struct grub_linux_initrd_context initrd_ctx = { 0, 0, 0 };
|
||||
|
||||
if (argc == 0)
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!loaded)
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_ARGUMENT, N_("you need to load the kernel first"));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
lh = (struct linux_i386_kernel_header *) grub_linux_real_chunk;
|
||||
|
||||
if (!(lh->header == grub_cpu_to_le32_compile_time (GRUB_LINUX_I386_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;
|
||||
|
||||
if (grub_initrd_init (argc, argv, &initrd_ctx))
|
||||
goto fail;
|
||||
|
||||
size = grub_get_initrd_size (&initrd_ctx);
|
||||
|
||||
{
|
||||
grub_relocator_chunk_t ch;
|
||||
err = grub_relocator_alloc_chunk_align (relocator, &ch,
|
||||
addr_min, addr_max - size,
|
||||
size, 0x1000,
|
||||
GRUB_RELOCATOR_PREFERENCE_HIGH, 0);
|
||||
if (err)
|
||||
return err;
|
||||
initrd_chunk = get_virtual_current_address (ch);
|
||||
initrd_addr = get_physical_target_address (ch);
|
||||
}
|
||||
|
||||
if (grub_initrd_load (&initrd_ctx, argv, initrd_chunk))
|
||||
goto fail;
|
||||
|
||||
lh->ramdisk_image = initrd_addr;
|
||||
lh->ramdisk_size = size;
|
||||
|
||||
fail:
|
||||
grub_initrd_close (&initrd_ctx);
|
||||
|
||||
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);
|
||||
}
|
||||
978
GRUB2/MOD_SRC/grub-2.04/grub-core/loader/i386/xen.c
Normal file
978
GRUB2/MOD_SRC/grub-2.04/grub-core/loader/i386/xen.c
Normal file
@@ -0,0 +1,978 @@
|
||||
/*
|
||||
* GRUB -- GRand Unified Bootloader
|
||||
* Copyright (C) 2013 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/memory.h>
|
||||
#include <grub/normal.h>
|
||||
#include <grub/file.h>
|
||||
#include <grub/disk.h>
|
||||
#include <grub/err.h>
|
||||
#include <grub/misc.h>
|
||||
#include <grub/types.h>
|
||||
#include <grub/dl.h>
|
||||
#include <grub/mm.h>
|
||||
#include <grub/term.h>
|
||||
#include <grub/cpu/linux.h>
|
||||
#include <grub/video.h>
|
||||
#include <grub/video_fb.h>
|
||||
#include <grub/command.h>
|
||||
#include <grub/xen/relocator.h>
|
||||
#include <grub/i18n.h>
|
||||
#include <grub/elf.h>
|
||||
#include <grub/elfload.h>
|
||||
#include <grub/lib/cmdline.h>
|
||||
#include <grub/xen.h>
|
||||
#include <grub/xen_file.h>
|
||||
#include <grub/linux.h>
|
||||
#include <grub/i386/memory.h>
|
||||
#include <grub/verify.h>
|
||||
|
||||
GRUB_MOD_LICENSE ("GPLv3+");
|
||||
|
||||
#ifdef __x86_64__
|
||||
#define NUMBER_OF_LEVELS 4
|
||||
#define INTERMEDIATE_OR (GRUB_PAGE_PRESENT | GRUB_PAGE_RW | GRUB_PAGE_USER)
|
||||
#define VIRT_MASK 0x0000ffffffffffffULL
|
||||
#else
|
||||
#define NUMBER_OF_LEVELS 3
|
||||
#define INTERMEDIATE_OR (GRUB_PAGE_PRESENT | GRUB_PAGE_RW)
|
||||
#define VIRT_MASK 0x00000000ffffffffULL
|
||||
#define HYPERVISOR_PUD_ADDRESS 0xc0000000ULL
|
||||
#endif
|
||||
|
||||
struct grub_xen_mapping_lvl {
|
||||
grub_uint64_t virt_start;
|
||||
grub_uint64_t virt_end;
|
||||
grub_uint64_t pfn_start;
|
||||
grub_uint64_t n_pt_pages;
|
||||
};
|
||||
|
||||
struct grub_xen_mapping {
|
||||
grub_uint64_t *where;
|
||||
struct grub_xen_mapping_lvl area;
|
||||
struct grub_xen_mapping_lvl lvls[NUMBER_OF_LEVELS];
|
||||
};
|
||||
|
||||
struct xen_loader_state {
|
||||
struct grub_relocator *relocator;
|
||||
struct grub_relocator_xen_state state;
|
||||
struct start_info next_start;
|
||||
struct grub_xen_file_info xen_inf;
|
||||
grub_xen_mfn_t *virt_mfn_list;
|
||||
struct start_info *virt_start_info;
|
||||
grub_xen_mfn_t console_pfn;
|
||||
grub_uint64_t max_addr;
|
||||
grub_uint64_t pgtbl_end;
|
||||
struct xen_multiboot_mod_list *module_info_page;
|
||||
grub_uint64_t modules_target_start;
|
||||
grub_size_t n_modules;
|
||||
struct grub_xen_mapping *map_reloc;
|
||||
struct grub_xen_mapping mappings[XEN_MAX_MAPPINGS];
|
||||
int n_mappings;
|
||||
int loaded;
|
||||
};
|
||||
|
||||
static struct xen_loader_state xen_state;
|
||||
|
||||
static grub_dl_t my_mod;
|
||||
|
||||
#define PAGE_SIZE (1UL << PAGE_SHIFT)
|
||||
#define MAX_MODULES (PAGE_SIZE / sizeof (struct xen_multiboot_mod_list))
|
||||
#define STACK_SIZE 1048576
|
||||
#define ADDITIONAL_SIZE (1 << 19)
|
||||
#define ALIGN_SIZE (1 << 22)
|
||||
#define LOG_POINTERS_PER_PAGE 9
|
||||
#define POINTERS_PER_PAGE (1 << LOG_POINTERS_PER_PAGE)
|
||||
|
||||
static grub_uint64_t
|
||||
page2offset (grub_uint64_t page)
|
||||
{
|
||||
return page << PAGE_SHIFT;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
get_pgtable_size (grub_uint64_t from, grub_uint64_t to, grub_uint64_t pfn)
|
||||
{
|
||||
struct grub_xen_mapping *map, *map_cmp;
|
||||
grub_uint64_t mask, bits;
|
||||
int i, m;
|
||||
|
||||
if (xen_state.n_mappings == XEN_MAX_MAPPINGS)
|
||||
return grub_error (GRUB_ERR_BUG, "too many mapped areas");
|
||||
|
||||
grub_dprintf ("xen", "get_pgtable_size %d from=%llx, to=%llx, pfn=%llx\n",
|
||||
xen_state.n_mappings, (unsigned long long) from,
|
||||
(unsigned long long) to, (unsigned long long) pfn);
|
||||
|
||||
map = xen_state.mappings + xen_state.n_mappings;
|
||||
grub_memset (map, 0, sizeof (*map));
|
||||
|
||||
map->area.virt_start = from & VIRT_MASK;
|
||||
map->area.virt_end = (to - 1) & VIRT_MASK;
|
||||
map->area.n_pt_pages = 0;
|
||||
|
||||
for (i = NUMBER_OF_LEVELS - 1; i >= 0; i--)
|
||||
{
|
||||
map->lvls[i].pfn_start = pfn + map->area.n_pt_pages;
|
||||
if (i == NUMBER_OF_LEVELS - 1)
|
||||
{
|
||||
if (xen_state.n_mappings == 0)
|
||||
{
|
||||
map->lvls[i].virt_start = 0;
|
||||
map->lvls[i].virt_end = VIRT_MASK;
|
||||
map->lvls[i].n_pt_pages = 1;
|
||||
map->area.n_pt_pages++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
bits = PAGE_SHIFT + (i + 1) * LOG_POINTERS_PER_PAGE;
|
||||
mask = (1ULL << bits) - 1;
|
||||
map->lvls[i].virt_start = map->area.virt_start & ~mask;
|
||||
map->lvls[i].virt_end = map->area.virt_end | mask;
|
||||
#ifdef __i386__
|
||||
/* PAE wants last root directory present. */
|
||||
if (i == 1 && to <= HYPERVISOR_PUD_ADDRESS && xen_state.n_mappings == 0)
|
||||
map->lvls[i].virt_end = VIRT_MASK;
|
||||
#endif
|
||||
for (m = 0; m < xen_state.n_mappings; m++)
|
||||
{
|
||||
map_cmp = xen_state.mappings + m;
|
||||
if (map_cmp->lvls[i].virt_start == map_cmp->lvls[i].virt_end)
|
||||
continue;
|
||||
if (map->lvls[i].virt_start >= map_cmp->lvls[i].virt_start &&
|
||||
map->lvls[i].virt_end <= map_cmp->lvls[i].virt_end)
|
||||
{
|
||||
map->lvls[i].virt_start = 0;
|
||||
map->lvls[i].virt_end = 0;
|
||||
break;
|
||||
}
|
||||
if (map->lvls[i].virt_start >= map_cmp->lvls[i].virt_start &&
|
||||
map->lvls[i].virt_start <= map_cmp->lvls[i].virt_end)
|
||||
map->lvls[i].virt_start = map_cmp->lvls[i].virt_end + 1;
|
||||
if (map->lvls[i].virt_end >= map_cmp->lvls[i].virt_start &&
|
||||
map->lvls[i].virt_end <= map_cmp->lvls[i].virt_end)
|
||||
map->lvls[i].virt_end = map_cmp->lvls[i].virt_start - 1;
|
||||
}
|
||||
if (map->lvls[i].virt_start < map->lvls[i].virt_end)
|
||||
map->lvls[i].n_pt_pages =
|
||||
((map->lvls[i].virt_end - map->lvls[i].virt_start) >> bits) + 1;
|
||||
map->area.n_pt_pages += map->lvls[i].n_pt_pages;
|
||||
grub_dprintf ("xen", "get_pgtable_size level %d: virt %llx-%llx %d pts\n",
|
||||
i, (unsigned long long) map->lvls[i].virt_start,
|
||||
(unsigned long long) map->lvls[i].virt_end,
|
||||
(int) map->lvls[i].n_pt_pages);
|
||||
}
|
||||
|
||||
grub_dprintf ("xen", "get_pgtable_size return: %d page tables\n",
|
||||
(int) map->area.n_pt_pages);
|
||||
|
||||
xen_state.state.paging_start[xen_state.n_mappings] = pfn;
|
||||
xen_state.state.paging_size[xen_state.n_mappings] = map->area.n_pt_pages;
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
static grub_uint64_t *
|
||||
get_pg_table_virt (int mapping, int level)
|
||||
{
|
||||
grub_uint64_t pfn;
|
||||
struct grub_xen_mapping *map;
|
||||
|
||||
map = xen_state.mappings + mapping;
|
||||
pfn = map->lvls[level].pfn_start - map->lvls[NUMBER_OF_LEVELS - 1].pfn_start;
|
||||
return map->where + pfn * POINTERS_PER_PAGE;
|
||||
}
|
||||
|
||||
static grub_uint64_t
|
||||
get_pg_table_prot (int level, grub_uint64_t pfn)
|
||||
{
|
||||
int m;
|
||||
grub_uint64_t pfn_s, pfn_e;
|
||||
|
||||
if (level > 0)
|
||||
return INTERMEDIATE_OR;
|
||||
for (m = 0; m < xen_state.n_mappings; m++)
|
||||
{
|
||||
pfn_s = xen_state.mappings[m].lvls[NUMBER_OF_LEVELS - 1].pfn_start;
|
||||
pfn_e = xen_state.mappings[m].area.n_pt_pages + pfn_s;
|
||||
if (pfn >= pfn_s && pfn < pfn_e)
|
||||
return GRUB_PAGE_PRESENT | GRUB_PAGE_USER;
|
||||
}
|
||||
return GRUB_PAGE_PRESENT | GRUB_PAGE_RW | GRUB_PAGE_USER;
|
||||
}
|
||||
|
||||
static void
|
||||
generate_page_table (grub_xen_mfn_t *mfn_list)
|
||||
{
|
||||
int l, m1, m2;
|
||||
long p, p_s, p_e;
|
||||
grub_uint64_t start, end, pfn;
|
||||
grub_uint64_t *pg;
|
||||
struct grub_xen_mapping_lvl *lvl;
|
||||
|
||||
for (m1 = 0; m1 < xen_state.n_mappings; m1++)
|
||||
grub_memset (xen_state.mappings[m1].where, 0,
|
||||
xen_state.mappings[m1].area.n_pt_pages * PAGE_SIZE);
|
||||
|
||||
for (l = NUMBER_OF_LEVELS - 1; l >= 0; l--)
|
||||
{
|
||||
for (m1 = 0; m1 < xen_state.n_mappings; m1++)
|
||||
{
|
||||
start = xen_state.mappings[m1].lvls[l].virt_start;
|
||||
end = xen_state.mappings[m1].lvls[l].virt_end;
|
||||
pg = get_pg_table_virt(m1, l);
|
||||
for (m2 = 0; m2 < xen_state.n_mappings; m2++)
|
||||
{
|
||||
lvl = (l > 0) ? xen_state.mappings[m2].lvls + l - 1
|
||||
: &xen_state.mappings[m2].area;
|
||||
if (l > 0 && lvl->n_pt_pages == 0)
|
||||
continue;
|
||||
if (lvl->virt_start >= end || lvl->virt_end <= start)
|
||||
continue;
|
||||
p_s = (grub_max (start, lvl->virt_start) - start) >>
|
||||
(PAGE_SHIFT + l * LOG_POINTERS_PER_PAGE);
|
||||
p_e = (grub_min (end, lvl->virt_end) - start) >>
|
||||
(PAGE_SHIFT + l * LOG_POINTERS_PER_PAGE);
|
||||
pfn = ((grub_max (start, lvl->virt_start) - lvl->virt_start) >>
|
||||
(PAGE_SHIFT + l * LOG_POINTERS_PER_PAGE)) + lvl->pfn_start;
|
||||
grub_dprintf ("xen", "write page table entries level %d pg %p "
|
||||
"mapping %d/%d index %lx-%lx pfn %llx\n",
|
||||
l, pg, m1, m2, p_s, p_e, (unsigned long long) pfn);
|
||||
for (p = p_s; p <= p_e; p++)
|
||||
{
|
||||
pg[p] = page2offset (mfn_list[pfn]) |
|
||||
get_pg_table_prot (l, pfn);
|
||||
pfn++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
set_mfns (grub_xen_mfn_t pfn)
|
||||
{
|
||||
grub_xen_mfn_t i, t;
|
||||
grub_xen_mfn_t cn_pfn = -1, st_pfn = -1;
|
||||
struct mmu_update m2p_updates[4];
|
||||
|
||||
|
||||
for (i = 0; i < grub_xen_start_page_addr->nr_pages; i++)
|
||||
{
|
||||
if (xen_state.virt_mfn_list[i] ==
|
||||
grub_xen_start_page_addr->console.domU.mfn)
|
||||
cn_pfn = i;
|
||||
if (xen_state.virt_mfn_list[i] == grub_xen_start_page_addr->store_mfn)
|
||||
st_pfn = i;
|
||||
}
|
||||
if (cn_pfn == (grub_xen_mfn_t)-1)
|
||||
return grub_error (GRUB_ERR_BUG, "no console");
|
||||
if (st_pfn == (grub_xen_mfn_t)-1)
|
||||
return grub_error (GRUB_ERR_BUG, "no store");
|
||||
t = xen_state.virt_mfn_list[pfn];
|
||||
xen_state.virt_mfn_list[pfn] = xen_state.virt_mfn_list[cn_pfn];
|
||||
xen_state.virt_mfn_list[cn_pfn] = t;
|
||||
t = xen_state.virt_mfn_list[pfn + 1];
|
||||
xen_state.virt_mfn_list[pfn + 1] = xen_state.virt_mfn_list[st_pfn];
|
||||
xen_state.virt_mfn_list[st_pfn] = t;
|
||||
|
||||
m2p_updates[0].ptr =
|
||||
page2offset (xen_state.virt_mfn_list[pfn]) | MMU_MACHPHYS_UPDATE;
|
||||
m2p_updates[0].val = pfn;
|
||||
m2p_updates[1].ptr =
|
||||
page2offset (xen_state.virt_mfn_list[pfn + 1]) | MMU_MACHPHYS_UPDATE;
|
||||
m2p_updates[1].val = pfn + 1;
|
||||
m2p_updates[2].ptr =
|
||||
page2offset (xen_state.virt_mfn_list[cn_pfn]) | MMU_MACHPHYS_UPDATE;
|
||||
m2p_updates[2].val = cn_pfn;
|
||||
m2p_updates[3].ptr =
|
||||
page2offset (xen_state.virt_mfn_list[st_pfn]) | MMU_MACHPHYS_UPDATE;
|
||||
m2p_updates[3].val = st_pfn;
|
||||
|
||||
grub_xen_mmu_update (m2p_updates, 4, NULL, DOMID_SELF);
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_xen_p2m_alloc (void)
|
||||
{
|
||||
grub_relocator_chunk_t ch;
|
||||
grub_size_t p2msize, p2malloc;
|
||||
grub_err_t err;
|
||||
struct grub_xen_mapping *map;
|
||||
|
||||
if (xen_state.virt_mfn_list)
|
||||
return GRUB_ERR_NONE;
|
||||
|
||||
map = xen_state.mappings + xen_state.n_mappings;
|
||||
p2msize = ALIGN_UP (sizeof (grub_xen_mfn_t) *
|
||||
grub_xen_start_page_addr->nr_pages, PAGE_SIZE);
|
||||
if (xen_state.xen_inf.has_p2m_base)
|
||||
{
|
||||
err = get_pgtable_size (xen_state.xen_inf.p2m_base,
|
||||
xen_state.xen_inf.p2m_base + p2msize,
|
||||
(xen_state.max_addr + p2msize) >> PAGE_SHIFT);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
map->area.pfn_start = xen_state.max_addr >> PAGE_SHIFT;
|
||||
p2malloc = p2msize + page2offset (map->area.n_pt_pages);
|
||||
xen_state.n_mappings++;
|
||||
xen_state.next_start.mfn_list = xen_state.xen_inf.p2m_base;
|
||||
xen_state.next_start.first_p2m_pfn = map->area.pfn_start;
|
||||
xen_state.next_start.nr_p2m_frames = p2malloc >> PAGE_SHIFT;
|
||||
}
|
||||
else
|
||||
{
|
||||
xen_state.next_start.mfn_list =
|
||||
xen_state.max_addr + xen_state.xen_inf.virt_base;
|
||||
p2malloc = p2msize;
|
||||
}
|
||||
|
||||
xen_state.state.mfn_list = xen_state.max_addr;
|
||||
err = grub_relocator_alloc_chunk_addr (xen_state.relocator, &ch,
|
||||
xen_state.max_addr, p2malloc);
|
||||
if (err)
|
||||
return err;
|
||||
xen_state.virt_mfn_list = get_virtual_current_address (ch);
|
||||
if (xen_state.xen_inf.has_p2m_base)
|
||||
map->where = (grub_uint64_t *) xen_state.virt_mfn_list +
|
||||
p2msize / sizeof (grub_uint64_t);
|
||||
grub_memcpy (xen_state.virt_mfn_list,
|
||||
(void *) grub_xen_start_page_addr->mfn_list, p2msize);
|
||||
xen_state.max_addr += p2malloc;
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_xen_special_alloc (void)
|
||||
{
|
||||
grub_relocator_chunk_t ch;
|
||||
grub_err_t err;
|
||||
|
||||
if (xen_state.virt_start_info)
|
||||
return GRUB_ERR_NONE;
|
||||
|
||||
err = grub_relocator_alloc_chunk_addr (xen_state.relocator, &ch,
|
||||
xen_state.max_addr,
|
||||
sizeof (xen_state.next_start));
|
||||
if (err)
|
||||
return err;
|
||||
xen_state.state.start_info = xen_state.max_addr + xen_state.xen_inf.virt_base;
|
||||
xen_state.virt_start_info = get_virtual_current_address (ch);
|
||||
xen_state.max_addr =
|
||||
ALIGN_UP (xen_state.max_addr + sizeof (xen_state.next_start), PAGE_SIZE);
|
||||
xen_state.console_pfn = xen_state.max_addr >> PAGE_SHIFT;
|
||||
xen_state.max_addr += 2 * PAGE_SIZE;
|
||||
|
||||
xen_state.next_start.nr_pages = grub_xen_start_page_addr->nr_pages;
|
||||
grub_memcpy (xen_state.next_start.magic, grub_xen_start_page_addr->magic,
|
||||
sizeof (xen_state.next_start.magic));
|
||||
xen_state.next_start.store_mfn = grub_xen_start_page_addr->store_mfn;
|
||||
xen_state.next_start.store_evtchn = grub_xen_start_page_addr->store_evtchn;
|
||||
xen_state.next_start.console.domU = grub_xen_start_page_addr->console.domU;
|
||||
xen_state.next_start.shared_info = grub_xen_start_page_addr->shared_info;
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_xen_pt_alloc (void)
|
||||
{
|
||||
grub_relocator_chunk_t ch;
|
||||
grub_err_t err;
|
||||
grub_uint64_t nr_info_pages;
|
||||
grub_uint64_t nr_need_pages;
|
||||
grub_uint64_t try_virt_end;
|
||||
struct grub_xen_mapping *map;
|
||||
|
||||
if (xen_state.pgtbl_end)
|
||||
return GRUB_ERR_NONE;
|
||||
|
||||
map = xen_state.mappings + xen_state.n_mappings;
|
||||
xen_state.map_reloc = map + 1;
|
||||
|
||||
xen_state.next_start.pt_base =
|
||||
xen_state.max_addr + xen_state.xen_inf.virt_base;
|
||||
nr_info_pages = xen_state.max_addr >> PAGE_SHIFT;
|
||||
nr_need_pages = nr_info_pages;
|
||||
|
||||
while (1)
|
||||
{
|
||||
try_virt_end = ALIGN_UP (xen_state.xen_inf.virt_base +
|
||||
page2offset (nr_need_pages) +
|
||||
ADDITIONAL_SIZE + STACK_SIZE, ALIGN_SIZE);
|
||||
|
||||
err = get_pgtable_size (xen_state.xen_inf.virt_base, try_virt_end,
|
||||
nr_info_pages);
|
||||
if (err)
|
||||
return err;
|
||||
xen_state.n_mappings++;
|
||||
|
||||
/* Map the relocator page either at virtual 0 or after end of area. */
|
||||
nr_need_pages = nr_info_pages + map->area.n_pt_pages;
|
||||
if (xen_state.xen_inf.virt_base)
|
||||
err = get_pgtable_size (0, PAGE_SIZE, nr_need_pages);
|
||||
else
|
||||
err = get_pgtable_size (try_virt_end, try_virt_end + PAGE_SIZE,
|
||||
nr_need_pages);
|
||||
if (err)
|
||||
return err;
|
||||
nr_need_pages += xen_state.map_reloc->area.n_pt_pages;
|
||||
|
||||
if (xen_state.xen_inf.virt_base + page2offset (nr_need_pages) <=
|
||||
try_virt_end)
|
||||
break;
|
||||
|
||||
xen_state.n_mappings--;
|
||||
}
|
||||
|
||||
xen_state.n_mappings++;
|
||||
nr_need_pages = map->area.n_pt_pages + xen_state.map_reloc->area.n_pt_pages;
|
||||
err = grub_relocator_alloc_chunk_addr (xen_state.relocator, &ch,
|
||||
xen_state.max_addr,
|
||||
page2offset (nr_need_pages));
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
map->where = get_virtual_current_address (ch);
|
||||
map->area.pfn_start = 0;
|
||||
xen_state.max_addr += page2offset (nr_need_pages);
|
||||
xen_state.state.stack =
|
||||
xen_state.max_addr + STACK_SIZE + xen_state.xen_inf.virt_base;
|
||||
xen_state.next_start.nr_pt_frames = nr_need_pages;
|
||||
xen_state.max_addr = try_virt_end - xen_state.xen_inf.virt_base;
|
||||
xen_state.pgtbl_end = xen_state.max_addr >> PAGE_SHIFT;
|
||||
xen_state.map_reloc->where = (grub_uint64_t *) ((char *) map->where +
|
||||
page2offset (map->area.n_pt_pages));
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
/* Allocate all not yet allocated areas mapped by initial page tables. */
|
||||
static grub_err_t
|
||||
grub_xen_alloc_boot_data (void)
|
||||
{
|
||||
grub_err_t err;
|
||||
|
||||
if (!xen_state.xen_inf.has_p2m_base)
|
||||
{
|
||||
err = grub_xen_p2m_alloc ();
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
err = grub_xen_special_alloc ();
|
||||
if (err)
|
||||
return err;
|
||||
err = grub_xen_pt_alloc ();
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_xen_boot (void)
|
||||
{
|
||||
grub_err_t err;
|
||||
grub_uint64_t nr_pages;
|
||||
struct gnttab_set_version gnttab_setver;
|
||||
grub_size_t i;
|
||||
|
||||
if (grub_xen_n_allocated_shared_pages)
|
||||
return grub_error (GRUB_ERR_BUG, "active grants");
|
||||
|
||||
err = grub_xen_alloc_boot_data ();
|
||||
if (err)
|
||||
return err;
|
||||
if (xen_state.xen_inf.has_p2m_base)
|
||||
{
|
||||
err = grub_xen_p2m_alloc ();
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
err = set_mfns (xen_state.console_pfn);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
nr_pages = xen_state.max_addr >> PAGE_SHIFT;
|
||||
|
||||
grub_dprintf ("xen", "bootstrap domain %llx+%llx\n",
|
||||
(unsigned long long) xen_state.xen_inf.virt_base,
|
||||
(unsigned long long) page2offset (nr_pages));
|
||||
|
||||
xen_state.map_reloc->area.pfn_start = nr_pages;
|
||||
generate_page_table (xen_state.virt_mfn_list);
|
||||
|
||||
xen_state.state.entry_point = xen_state.xen_inf.entry_point;
|
||||
|
||||
*xen_state.virt_start_info = xen_state.next_start;
|
||||
|
||||
grub_memset (&gnttab_setver, 0, sizeof (gnttab_setver));
|
||||
|
||||
gnttab_setver.version = 1;
|
||||
grub_xen_grant_table_op (GNTTABOP_set_version, &gnttab_setver, 1);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE (grub_xen_shared_info->evtchn_pending); i++)
|
||||
grub_xen_shared_info->evtchn_pending[i] = 0;
|
||||
|
||||
return grub_relocator_xen_boot (xen_state.relocator, xen_state.state, nr_pages,
|
||||
xen_state.xen_inf.virt_base <
|
||||
PAGE_SIZE ? page2offset (nr_pages) : 0,
|
||||
xen_state.pgtbl_end - 1,
|
||||
page2offset (xen_state.pgtbl_end - 1) +
|
||||
xen_state.xen_inf.virt_base);
|
||||
}
|
||||
|
||||
static void
|
||||
grub_xen_reset (void)
|
||||
{
|
||||
grub_relocator_unload (xen_state.relocator);
|
||||
|
||||
grub_memset (&xen_state, 0, sizeof (xen_state));
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_xen_unload (void)
|
||||
{
|
||||
grub_xen_reset ();
|
||||
grub_dl_unref (my_mod);
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
#define HYPERCALL_INTERFACE_SIZE 32
|
||||
|
||||
#ifdef __x86_64__
|
||||
static grub_uint8_t template[] =
|
||||
{
|
||||
0x51, /* push %rcx */
|
||||
0x41, 0x53, /* push %r11 */
|
||||
0x48, 0xc7, 0xc0, 0xbb, 0xaa, 0x00, 0x00, /* mov $0xaabb,%rax */
|
||||
0x0f, 0x05, /* syscall */
|
||||
0x41, 0x5b, /* pop %r11 */
|
||||
0x59, /* pop %rcx */
|
||||
0xc3 /* ret */
|
||||
};
|
||||
|
||||
static grub_uint8_t template_iret[] =
|
||||
{
|
||||
0x51, /* push %rcx */
|
||||
0x41, 0x53, /* push %r11 */
|
||||
0x50, /* push %rax */
|
||||
0x48, 0xc7, 0xc0, 0x17, 0x00, 0x00, 0x00, /* mov $0x17,%rax */
|
||||
0x0f, 0x05 /* syscall */
|
||||
};
|
||||
#define CALLNO_OFFSET 6
|
||||
#else
|
||||
|
||||
static grub_uint8_t template[] =
|
||||
{
|
||||
0xb8, 0xbb, 0xaa, 0x00, 0x00, /* mov imm32, %eax */
|
||||
0xcd, 0x82, /* int $0x82 */
|
||||
0xc3 /* ret */
|
||||
};
|
||||
|
||||
static grub_uint8_t template_iret[] =
|
||||
{
|
||||
0x50, /* push %eax */
|
||||
0xb8, 0x17, 0x00, 0x00, 0x00, /* mov $0x17,%eax */
|
||||
0xcd, 0x82, /* int $0x82 */
|
||||
};
|
||||
#define CALLNO_OFFSET 1
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
static void
|
||||
set_hypercall_interface (grub_uint8_t *tgt, unsigned callno)
|
||||
{
|
||||
if (callno == 0x17)
|
||||
{
|
||||
grub_memcpy (tgt, template_iret, ARRAY_SIZE (template_iret));
|
||||
grub_memset (tgt + ARRAY_SIZE (template_iret), 0xcc,
|
||||
HYPERCALL_INTERFACE_SIZE - ARRAY_SIZE (template_iret));
|
||||
return;
|
||||
}
|
||||
grub_memcpy (tgt, template, ARRAY_SIZE (template));
|
||||
grub_memset (tgt + ARRAY_SIZE (template), 0xcc,
|
||||
HYPERCALL_INTERFACE_SIZE - ARRAY_SIZE (template));
|
||||
tgt[CALLNO_OFFSET] = callno & 0xff;
|
||||
tgt[CALLNO_OFFSET + 1] = callno >> 8;
|
||||
}
|
||||
|
||||
#ifdef __x86_64__
|
||||
#define grub_elfXX_load grub_elf64_load
|
||||
#else
|
||||
#define grub_elfXX_load grub_elf32_load
|
||||
#endif
|
||||
|
||||
static grub_err_t
|
||||
grub_cmd_xen (grub_command_t cmd __attribute__ ((unused)),
|
||||
int argc, char *argv[])
|
||||
{
|
||||
grub_file_t file;
|
||||
grub_elf_t elf;
|
||||
grub_err_t err;
|
||||
void *kern_chunk_src;
|
||||
grub_relocator_chunk_t ch;
|
||||
grub_addr_t kern_start;
|
||||
grub_addr_t kern_end;
|
||||
|
||||
if (argc == 0)
|
||||
return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
|
||||
|
||||
/* Call grub_loader_unset early to avoid it being called by grub_loader_set */
|
||||
grub_loader_unset ();
|
||||
|
||||
grub_xen_reset ();
|
||||
|
||||
err = grub_create_loader_cmdline (argc - 1, argv + 1,
|
||||
(char *) xen_state.next_start.cmd_line,
|
||||
sizeof (xen_state.next_start.cmd_line) - 1,
|
||||
GRUB_VERIFY_KERNEL_CMDLINE);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
file = grub_file_open (argv[0], GRUB_FILE_TYPE_LINUX_KERNEL);
|
||||
if (!file)
|
||||
return grub_errno;
|
||||
|
||||
elf = grub_xen_file (file);
|
||||
if (!elf)
|
||||
goto fail;
|
||||
|
||||
err = grub_xen_get_info (elf, &xen_state.xen_inf);
|
||||
if (err)
|
||||
goto fail;
|
||||
#ifdef __x86_64__
|
||||
if (xen_state.xen_inf.arch != GRUB_XEN_FILE_X86_64)
|
||||
#else
|
||||
if (xen_state.xen_inf.arch != GRUB_XEN_FILE_I386_PAE
|
||||
&& xen_state.xen_inf.arch != GRUB_XEN_FILE_I386_PAE_BIMODE)
|
||||
#endif
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_OS, "incompatible architecture: %d",
|
||||
xen_state.xen_inf.arch);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (xen_state.xen_inf.virt_base & (PAGE_SIZE - 1))
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_OS, "unaligned virt_base");
|
||||
goto fail;
|
||||
}
|
||||
grub_dprintf ("xen", "virt_base = %llx, entry = %llx\n",
|
||||
(unsigned long long) xen_state.xen_inf.virt_base,
|
||||
(unsigned long long) xen_state.xen_inf.entry_point);
|
||||
|
||||
xen_state.relocator = grub_relocator_new ();
|
||||
if (!xen_state.relocator)
|
||||
goto fail;
|
||||
|
||||
kern_start = xen_state.xen_inf.kern_start - xen_state.xen_inf.paddr_offset;
|
||||
kern_end = xen_state.xen_inf.kern_end - xen_state.xen_inf.paddr_offset;
|
||||
|
||||
if (xen_state.xen_inf.has_hypercall_page)
|
||||
{
|
||||
grub_dprintf ("xen", "hypercall page at 0x%llx\n",
|
||||
(unsigned long long) xen_state.xen_inf.hypercall_page);
|
||||
kern_start = grub_min (kern_start, xen_state.xen_inf.hypercall_page -
|
||||
xen_state.xen_inf.virt_base);
|
||||
kern_end = grub_max (kern_end, xen_state.xen_inf.hypercall_page -
|
||||
xen_state.xen_inf.virt_base + PAGE_SIZE);
|
||||
}
|
||||
|
||||
xen_state.max_addr = ALIGN_UP (kern_end, PAGE_SIZE);
|
||||
|
||||
err = grub_relocator_alloc_chunk_addr (xen_state.relocator, &ch, kern_start,
|
||||
kern_end - kern_start);
|
||||
if (err)
|
||||
goto fail;
|
||||
kern_chunk_src = get_virtual_current_address (ch);
|
||||
|
||||
grub_dprintf ("xen", "paddr_offset = 0x%llx\n",
|
||||
(unsigned long long) xen_state.xen_inf.paddr_offset);
|
||||
grub_dprintf ("xen", "kern_start = 0x%llx, kern_end = 0x%llx\n",
|
||||
(unsigned long long) xen_state.xen_inf.kern_start,
|
||||
(unsigned long long) xen_state.xen_inf.kern_end);
|
||||
|
||||
err = grub_elfXX_load (elf, argv[0],
|
||||
(grub_uint8_t *) kern_chunk_src - kern_start
|
||||
- xen_state.xen_inf.paddr_offset, 0, 0, 0);
|
||||
|
||||
if (xen_state.xen_inf.has_hypercall_page)
|
||||
{
|
||||
unsigned i;
|
||||
for (i = 0; i < PAGE_SIZE / HYPERCALL_INTERFACE_SIZE; i++)
|
||||
set_hypercall_interface ((grub_uint8_t *) kern_chunk_src +
|
||||
i * HYPERCALL_INTERFACE_SIZE +
|
||||
xen_state.xen_inf.hypercall_page -
|
||||
xen_state.xen_inf.virt_base - kern_start, i);
|
||||
}
|
||||
|
||||
if (err)
|
||||
goto fail;
|
||||
|
||||
grub_dl_ref (my_mod);
|
||||
xen_state.loaded = 1;
|
||||
|
||||
grub_loader_set (grub_xen_boot, grub_xen_unload, 0);
|
||||
|
||||
goto fail;
|
||||
|
||||
fail:
|
||||
/* grub_errno might be clobbered by further calls, save the error reason. */
|
||||
err = grub_errno;
|
||||
|
||||
if (elf)
|
||||
grub_elf_close (elf);
|
||||
else if (file)
|
||||
grub_file_close (file);
|
||||
|
||||
if (err != GRUB_ERR_NONE)
|
||||
grub_xen_reset ();
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
|
||||
int argc, char *argv[])
|
||||
{
|
||||
grub_size_t size = 0;
|
||||
grub_err_t err;
|
||||
struct grub_linux_initrd_context initrd_ctx = { 0, 0, 0 };
|
||||
grub_relocator_chunk_t ch;
|
||||
|
||||
if (argc == 0)
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!xen_state.loaded)
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_ARGUMENT,
|
||||
N_("you need to load the kernel first"));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (xen_state.next_start.mod_start || xen_state.next_start.mod_len)
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_ARGUMENT, N_("initrd already loaded"));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (xen_state.xen_inf.unmapped_initrd)
|
||||
{
|
||||
err = grub_xen_alloc_boot_data ();
|
||||
if (err)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (grub_initrd_init (argc, argv, &initrd_ctx))
|
||||
goto fail;
|
||||
|
||||
size = grub_get_initrd_size (&initrd_ctx);
|
||||
|
||||
if (size)
|
||||
{
|
||||
err = grub_relocator_alloc_chunk_addr (xen_state.relocator, &ch,
|
||||
xen_state.max_addr, size);
|
||||
if (err)
|
||||
goto fail;
|
||||
|
||||
if (grub_initrd_load (&initrd_ctx, argv,
|
||||
get_virtual_current_address (ch)))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
xen_state.next_start.mod_len = size;
|
||||
|
||||
if (xen_state.xen_inf.unmapped_initrd)
|
||||
{
|
||||
xen_state.next_start.flags |= SIF_MOD_START_PFN;
|
||||
xen_state.next_start.mod_start = xen_state.max_addr >> PAGE_SHIFT;
|
||||
}
|
||||
else
|
||||
xen_state.next_start.mod_start =
|
||||
xen_state.max_addr + xen_state.xen_inf.virt_base;
|
||||
|
||||
grub_dprintf ("xen", "Initrd, addr=0x%x, size=0x%x\n",
|
||||
(unsigned) (xen_state.max_addr + xen_state.xen_inf.virt_base),
|
||||
(unsigned) size);
|
||||
|
||||
xen_state.max_addr = ALIGN_UP (xen_state.max_addr + size, PAGE_SIZE);
|
||||
|
||||
fail:
|
||||
grub_initrd_close (&initrd_ctx);
|
||||
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_cmd_module (grub_command_t cmd __attribute__ ((unused)),
|
||||
int argc, char *argv[])
|
||||
{
|
||||
grub_size_t size = 0;
|
||||
grub_err_t err;
|
||||
grub_relocator_chunk_t ch;
|
||||
grub_size_t cmdline_len;
|
||||
int nounzip = 0;
|
||||
grub_file_t file;
|
||||
|
||||
if (argc == 0)
|
||||
return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
|
||||
|
||||
if (grub_strcmp (argv[0], "--nounzip") == 0)
|
||||
{
|
||||
argv++;
|
||||
argc--;
|
||||
nounzip = 1;
|
||||
}
|
||||
|
||||
if (argc == 0)
|
||||
return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
|
||||
|
||||
if (!xen_state.loaded)
|
||||
{
|
||||
return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
||||
N_("you need to load the kernel first"));
|
||||
}
|
||||
|
||||
if ((xen_state.next_start.mod_start || xen_state.next_start.mod_len) &&
|
||||
!xen_state.module_info_page)
|
||||
{
|
||||
return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("initrd already loaded"));
|
||||
}
|
||||
|
||||
/* Leave one space for terminator. */
|
||||
if (xen_state.n_modules >= MAX_MODULES - 1)
|
||||
{
|
||||
return grub_error (GRUB_ERR_BAD_ARGUMENT, "too many modules");
|
||||
}
|
||||
|
||||
if (!xen_state.module_info_page)
|
||||
{
|
||||
xen_state.xen_inf.unmapped_initrd = 0;
|
||||
xen_state.n_modules = 0;
|
||||
xen_state.max_addr = ALIGN_UP (xen_state.max_addr, PAGE_SIZE);
|
||||
xen_state.modules_target_start = xen_state.max_addr;
|
||||
xen_state.next_start.mod_start =
|
||||
xen_state.max_addr + xen_state.xen_inf.virt_base;
|
||||
xen_state.next_start.flags |= SIF_MULTIBOOT_MOD;
|
||||
|
||||
err = grub_relocator_alloc_chunk_addr (xen_state.relocator, &ch,
|
||||
xen_state.max_addr, MAX_MODULES
|
||||
*
|
||||
sizeof (xen_state.module_info_page
|
||||
[0]));
|
||||
if (err)
|
||||
return err;
|
||||
xen_state.module_info_page = get_virtual_current_address (ch);
|
||||
grub_memset (xen_state.module_info_page, 0, MAX_MODULES
|
||||
* sizeof (xen_state.module_info_page[0]));
|
||||
xen_state.max_addr +=
|
||||
MAX_MODULES * sizeof (xen_state.module_info_page[0]);
|
||||
}
|
||||
|
||||
xen_state.max_addr = ALIGN_UP (xen_state.max_addr, PAGE_SIZE);
|
||||
|
||||
file = grub_file_open (argv[0], GRUB_FILE_TYPE_LINUX_INITRD |
|
||||
(nounzip ? GRUB_FILE_TYPE_NO_DECOMPRESS : GRUB_FILE_TYPE_NONE));
|
||||
if (!file)
|
||||
return grub_errno;
|
||||
size = grub_file_size (file);
|
||||
|
||||
cmdline_len = grub_loader_cmdline_size (argc - 1, argv + 1);
|
||||
|
||||
err = grub_relocator_alloc_chunk_addr (xen_state.relocator, &ch,
|
||||
xen_state.max_addr, cmdline_len);
|
||||
if (err)
|
||||
goto fail;
|
||||
|
||||
err = grub_create_loader_cmdline (argc - 1, argv + 1,
|
||||
get_virtual_current_address (ch), cmdline_len,
|
||||
GRUB_VERIFY_MODULE_CMDLINE);
|
||||
if (err)
|
||||
goto fail;
|
||||
|
||||
xen_state.module_info_page[xen_state.n_modules].cmdline =
|
||||
xen_state.max_addr - xen_state.modules_target_start;
|
||||
xen_state.max_addr = ALIGN_UP (xen_state.max_addr + cmdline_len, PAGE_SIZE);
|
||||
|
||||
if (size)
|
||||
{
|
||||
err = grub_relocator_alloc_chunk_addr (xen_state.relocator, &ch,
|
||||
xen_state.max_addr, size);
|
||||
if (err)
|
||||
goto fail;
|
||||
if (grub_file_read (file, get_virtual_current_address (ch), size)
|
||||
!= (grub_ssize_t) size)
|
||||
{
|
||||
if (!grub_errno)
|
||||
grub_error (GRUB_ERR_FILE_READ_ERROR,
|
||||
N_("premature end of file %s"), argv[0]);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
xen_state.next_start.mod_len =
|
||||
xen_state.max_addr + size - xen_state.modules_target_start;
|
||||
xen_state.module_info_page[xen_state.n_modules].mod_start =
|
||||
xen_state.max_addr - xen_state.modules_target_start;
|
||||
xen_state.module_info_page[xen_state.n_modules].mod_end =
|
||||
xen_state.max_addr + size - xen_state.modules_target_start;
|
||||
|
||||
xen_state.n_modules++;
|
||||
grub_dprintf ("xen", "module, addr=0x%x, size=0x%x\n",
|
||||
(unsigned) xen_state.max_addr, (unsigned) size);
|
||||
xen_state.max_addr = ALIGN_UP (xen_state.max_addr + size, PAGE_SIZE);
|
||||
|
||||
|
||||
fail:
|
||||
grub_file_close (file);
|
||||
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
static grub_command_t cmd_xen, cmd_initrd, cmd_module, cmd_multiboot;
|
||||
|
||||
GRUB_MOD_INIT (xen)
|
||||
{
|
||||
cmd_xen = grub_register_command ("linux", grub_cmd_xen,
|
||||
0, N_("Load Linux."));
|
||||
cmd_multiboot = grub_register_command ("multiboot", grub_cmd_xen,
|
||||
0, N_("Load Linux."));
|
||||
cmd_initrd = grub_register_command ("initrd", grub_cmd_initrd,
|
||||
0, N_("Load initrd."));
|
||||
cmd_module = grub_register_command ("module", grub_cmd_module,
|
||||
0, N_("Load module."));
|
||||
my_mod = mod;
|
||||
}
|
||||
|
||||
GRUB_MOD_FINI (xen)
|
||||
{
|
||||
grub_unregister_command (cmd_xen);
|
||||
grub_unregister_command (cmd_initrd);
|
||||
grub_unregister_command (cmd_multiboot);
|
||||
grub_unregister_command (cmd_module);
|
||||
}
|
||||
607
GRUB2/MOD_SRC/grub-2.04/grub-core/loader/ia64/efi/linux.c
Normal file
607
GRUB2/MOD_SRC/grub-2.04/grub-core/loader/ia64/efi/linux.c
Normal file
@@ -0,0 +1,607 @@
|
||||
/*
|
||||
* GRUB -- GRand Unified Bootloader
|
||||
* Copyright (C) 2008,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/disk.h>
|
||||
#include <grub/err.h>
|
||||
#include <grub/misc.h>
|
||||
#include <grub/types.h>
|
||||
#include <grub/command.h>
|
||||
#include <grub/dl.h>
|
||||
#include <grub/mm.h>
|
||||
#include <grub/cache.h>
|
||||
#include <grub/kernel.h>
|
||||
#include <grub/efi/api.h>
|
||||
#include <grub/efi/efi.h>
|
||||
#include <grub/elf.h>
|
||||
#include <grub/i18n.h>
|
||||
#include <grub/env.h>
|
||||
#include <grub/linux.h>
|
||||
#include <grub/verify.h>
|
||||
|
||||
GRUB_MOD_LICENSE ("GPLv3+");
|
||||
|
||||
#pragma GCC diagnostic ignored "-Wcast-align"
|
||||
|
||||
#define ALIGN_MIN (256*1024*1024)
|
||||
|
||||
#define GRUB_ELF_SEARCH 1024
|
||||
|
||||
#define BOOT_PARAM_SIZE 16384
|
||||
|
||||
struct ia64_boot_param
|
||||
{
|
||||
grub_uint64_t command_line; /* physical address of command line. */
|
||||
grub_uint64_t efi_systab; /* physical address of EFI system table */
|
||||
grub_uint64_t efi_memmap; /* physical address of EFI memory map */
|
||||
grub_uint64_t efi_memmap_size; /* size of EFI memory map */
|
||||
grub_uint64_t efi_memdesc_size; /* size of an EFI memory map descriptor */
|
||||
grub_uint32_t efi_memdesc_version; /* memory descriptor version */
|
||||
struct
|
||||
{
|
||||
grub_uint16_t num_cols; /* number of columns on console output dev */
|
||||
grub_uint16_t num_rows; /* number of rows on console output device */
|
||||
grub_uint16_t orig_x; /* cursor's x position */
|
||||
grub_uint16_t orig_y; /* cursor's y position */
|
||||
} console_info;
|
||||
grub_uint64_t fpswa; /* physical address of the fpswa interface */
|
||||
grub_uint64_t initrd_start;
|
||||
grub_uint64_t initrd_size;
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
grub_uint32_t revision;
|
||||
grub_uint32_t reserved;
|
||||
void *fpswa;
|
||||
} fpswa_interface_t;
|
||||
static fpswa_interface_t *fpswa;
|
||||
|
||||
#define NEXT_MEMORY_DESCRIPTOR(desc, size) \
|
||||
((grub_efi_memory_descriptor_t *) ((char *) (desc) + (size)))
|
||||
|
||||
static grub_dl_t my_mod;
|
||||
|
||||
static int loaded;
|
||||
|
||||
/* Kernel base and size. */
|
||||
static void *kernel_mem;
|
||||
static grub_efi_uintn_t kernel_pages;
|
||||
static grub_uint64_t entry;
|
||||
|
||||
/* Initrd base and size. */
|
||||
static void *initrd_mem;
|
||||
static grub_efi_uintn_t initrd_pages;
|
||||
static grub_efi_uintn_t initrd_size;
|
||||
|
||||
static struct ia64_boot_param *boot_param;
|
||||
static grub_efi_uintn_t boot_param_pages;
|
||||
|
||||
static inline grub_size_t
|
||||
page_align (grub_size_t size)
|
||||
{
|
||||
return (size + (1 << 12) - 1) & (~((1 << 12) - 1));
|
||||
}
|
||||
|
||||
static void
|
||||
query_fpswa (void)
|
||||
{
|
||||
grub_efi_handle_t fpswa_image;
|
||||
grub_efi_boot_services_t *bs;
|
||||
grub_efi_status_t status;
|
||||
grub_efi_uintn_t size;
|
||||
static const grub_efi_guid_t fpswa_protocol =
|
||||
{ 0xc41b6531, 0x97b9, 0x11d3,
|
||||
{0x9a, 0x29, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d} };
|
||||
|
||||
if (fpswa != NULL)
|
||||
return;
|
||||
|
||||
size = sizeof(grub_efi_handle_t);
|
||||
|
||||
bs = grub_efi_system_table->boot_services;
|
||||
status = bs->locate_handle (GRUB_EFI_BY_PROTOCOL,
|
||||
(void *) &fpswa_protocol,
|
||||
NULL, &size, &fpswa_image);
|
||||
if (status != GRUB_EFI_SUCCESS)
|
||||
{
|
||||
grub_printf ("%s\n", _("Could not locate FPSWA driver"));
|
||||
return;
|
||||
}
|
||||
status = bs->handle_protocol (fpswa_image,
|
||||
(void *) &fpswa_protocol, (void *) &fpswa);
|
||||
if (status != GRUB_EFI_SUCCESS)
|
||||
{
|
||||
grub_printf ("%s\n",
|
||||
_("FPSWA protocol wasn't able to find the interface"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
free_pages (void)
|
||||
{
|
||||
if (kernel_mem)
|
||||
{
|
||||
grub_efi_free_pages ((grub_addr_t) kernel_mem, kernel_pages);
|
||||
kernel_mem = 0;
|
||||
}
|
||||
|
||||
if (initrd_mem)
|
||||
{
|
||||
grub_efi_free_pages ((grub_addr_t) initrd_mem, initrd_pages);
|
||||
initrd_mem = 0;
|
||||
}
|
||||
|
||||
if (boot_param)
|
||||
{
|
||||
/* Free bootparam. */
|
||||
grub_efi_free_pages ((grub_efi_physical_address_t) boot_param,
|
||||
boot_param_pages);
|
||||
boot_param = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void *
|
||||
allocate_pages (grub_uint64_t align, grub_uint64_t size_pages,
|
||||
grub_uint64_t nobase)
|
||||
{
|
||||
grub_uint64_t size;
|
||||
grub_efi_uintn_t desc_size;
|
||||
grub_efi_memory_descriptor_t *mmap, *mmap_end;
|
||||
grub_efi_uintn_t mmap_size, tmp_mmap_size;
|
||||
grub_efi_memory_descriptor_t *desc;
|
||||
void *mem = NULL;
|
||||
|
||||
size = size_pages << 12;
|
||||
|
||||
mmap_size = grub_efi_find_mmap_size ();
|
||||
if (!mmap_size)
|
||||
return 0;
|
||||
|
||||
/* Read the memory map temporarily, to find free space. */
|
||||
mmap = grub_malloc (mmap_size);
|
||||
if (! mmap)
|
||||
return 0;
|
||||
|
||||
tmp_mmap_size = mmap_size;
|
||||
if (grub_efi_get_memory_map (&tmp_mmap_size, mmap, 0, &desc_size, 0) <= 0)
|
||||
{
|
||||
grub_error (GRUB_ERR_IO, "cannot get memory map");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
mmap_end = NEXT_MEMORY_DESCRIPTOR (mmap, tmp_mmap_size);
|
||||
|
||||
/* First, find free pages for the real mode code
|
||||
and the memory map buffer. */
|
||||
for (desc = mmap;
|
||||
desc < mmap_end;
|
||||
desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size))
|
||||
{
|
||||
grub_uint64_t start, end;
|
||||
grub_uint64_t aligned_start;
|
||||
|
||||
if (desc->type != GRUB_EFI_CONVENTIONAL_MEMORY)
|
||||
continue;
|
||||
|
||||
start = desc->physical_start;
|
||||
end = start + (desc->num_pages << 12);
|
||||
/* Align is a power of 2. */
|
||||
aligned_start = (start + align - 1) & ~(align - 1);
|
||||
if (aligned_start + size > end)
|
||||
continue;
|
||||
if (aligned_start == nobase)
|
||||
aligned_start += align;
|
||||
if (aligned_start + size > end)
|
||||
continue;
|
||||
mem = grub_efi_allocate_fixed (aligned_start, size_pages);
|
||||
if (! mem)
|
||||
{
|
||||
grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate memory");
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (! mem)
|
||||
{
|
||||
grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate memory");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
grub_free (mmap);
|
||||
return mem;
|
||||
|
||||
fail:
|
||||
grub_free (mmap);
|
||||
free_pages ();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
set_boot_param_console (void)
|
||||
{
|
||||
grub_efi_simple_text_output_interface_t *conout;
|
||||
grub_efi_uintn_t cols, rows;
|
||||
|
||||
conout = grub_efi_system_table->con_out;
|
||||
if (conout->query_mode (conout, conout->mode->mode, &cols, &rows)
|
||||
!= GRUB_EFI_SUCCESS)
|
||||
return;
|
||||
|
||||
grub_dprintf ("linux",
|
||||
"Console info: cols=%lu rows=%lu x=%u y=%u\n",
|
||||
cols, rows,
|
||||
conout->mode->cursor_column, conout->mode->cursor_row);
|
||||
|
||||
boot_param->console_info.num_cols = cols;
|
||||
boot_param->console_info.num_rows = rows;
|
||||
boot_param->console_info.orig_x = conout->mode->cursor_column;
|
||||
boot_param->console_info.orig_y = conout->mode->cursor_row;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_linux_boot (void)
|
||||
{
|
||||
grub_efi_uintn_t mmap_size;
|
||||
grub_efi_uintn_t map_key;
|
||||
grub_efi_uintn_t desc_size;
|
||||
grub_efi_uint32_t desc_version;
|
||||
grub_efi_memory_descriptor_t *mmap_buf;
|
||||
grub_err_t err;
|
||||
|
||||
/* FPSWA. */
|
||||
query_fpswa ();
|
||||
boot_param->fpswa = (grub_uint64_t)fpswa;
|
||||
|
||||
/* Initrd. */
|
||||
boot_param->initrd_start = (grub_uint64_t)initrd_mem;
|
||||
boot_param->initrd_size = (grub_uint64_t)initrd_size;
|
||||
|
||||
set_boot_param_console ();
|
||||
|
||||
grub_dprintf ("linux", "Jump to %016lx\n", entry);
|
||||
|
||||
/* MDT.
|
||||
Must be done after grub_machine_fini because map_key is used by
|
||||
exit_boot_services. */
|
||||
mmap_size = grub_efi_find_mmap_size ();
|
||||
if (! mmap_size)
|
||||
return grub_errno;
|
||||
mmap_buf = grub_efi_allocate_any_pages (page_align (mmap_size) >> 12);
|
||||
if (! mmap_buf)
|
||||
return grub_error (GRUB_ERR_IO, "cannot allocate memory map");
|
||||
err = grub_efi_finish_boot_services (&mmap_size, mmap_buf, &map_key,
|
||||
&desc_size, &desc_version);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
boot_param->efi_memmap = (grub_uint64_t)mmap_buf;
|
||||
boot_param->efi_memmap_size = mmap_size;
|
||||
boot_param->efi_memdesc_size = desc_size;
|
||||
boot_param->efi_memdesc_version = desc_version;
|
||||
|
||||
/* See you next boot. */
|
||||
asm volatile ("mov r28=%1; br.sptk.few %0" :: "b"(entry),"r"(boot_param));
|
||||
|
||||
/* Never reach here. */
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_linux_unload (void)
|
||||
{
|
||||
free_pages ();
|
||||
grub_dl_unref (my_mod);
|
||||
loaded = 0;
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_load_elf64 (grub_file_t file, void *buffer, const char *filename)
|
||||
{
|
||||
Elf64_Ehdr *ehdr = (Elf64_Ehdr *) buffer;
|
||||
Elf64_Phdr *phdr;
|
||||
int i;
|
||||
grub_uint64_t low_addr;
|
||||
grub_uint64_t high_addr;
|
||||
grub_uint64_t align;
|
||||
grub_uint64_t reloc_offset;
|
||||
const char *relocate;
|
||||
|
||||
if (ehdr->e_ident[EI_MAG0] != ELFMAG0
|
||||
|| ehdr->e_ident[EI_MAG1] != ELFMAG1
|
||||
|| ehdr->e_ident[EI_MAG2] != ELFMAG2
|
||||
|| ehdr->e_ident[EI_MAG3] != ELFMAG3
|
||||
|| ehdr->e_ident[EI_DATA] != ELFDATA2LSB)
|
||||
return grub_error(GRUB_ERR_UNKNOWN_OS,
|
||||
N_("invalid arch-independent ELF magic"));
|
||||
|
||||
if (ehdr->e_ident[EI_CLASS] != ELFCLASS64
|
||||
|| ehdr->e_version != EV_CURRENT
|
||||
|| ehdr->e_machine != EM_IA_64)
|
||||
return grub_error (GRUB_ERR_UNKNOWN_OS,
|
||||
N_("invalid arch-dependent ELF magic"));
|
||||
|
||||
if (ehdr->e_type != ET_EXEC)
|
||||
return grub_error (GRUB_ERR_UNKNOWN_OS,
|
||||
N_("this ELF file is not of the right type"));
|
||||
|
||||
/* FIXME: Should we support program headers at strange locations? */
|
||||
if (ehdr->e_phoff + ehdr->e_phnum * ehdr->e_phentsize > GRUB_ELF_SEARCH)
|
||||
return grub_error (GRUB_ERR_BAD_OS, "program header at a too high offset");
|
||||
|
||||
entry = ehdr->e_entry;
|
||||
|
||||
/* Compute low, high and align addresses. */
|
||||
low_addr = ~0UL;
|
||||
high_addr = 0;
|
||||
align = 0;
|
||||
for (i = 0; i < ehdr->e_phnum; i++)
|
||||
{
|
||||
phdr = (Elf64_Phdr *) ((char *) buffer + ehdr->e_phoff
|
||||
+ i * ehdr->e_phentsize);
|
||||
if (phdr->p_type == PT_LOAD)
|
||||
{
|
||||
if (phdr->p_paddr < low_addr)
|
||||
low_addr = phdr->p_paddr;
|
||||
if (phdr->p_paddr + phdr->p_memsz > high_addr)
|
||||
high_addr = phdr->p_paddr + phdr->p_memsz;
|
||||
if (phdr->p_align > align)
|
||||
align = phdr->p_align;
|
||||
}
|
||||
}
|
||||
|
||||
if (align < ALIGN_MIN)
|
||||
align = ALIGN_MIN;
|
||||
|
||||
if (high_addr == 0)
|
||||
return grub_error (GRUB_ERR_BAD_OS, "no program entries");
|
||||
|
||||
kernel_pages = page_align (high_addr - low_addr) >> 12;
|
||||
|
||||
/* Undocumented on purpose. */
|
||||
relocate = grub_env_get ("linux_relocate");
|
||||
if (!relocate || grub_strcmp (relocate, "force") != 0)
|
||||
{
|
||||
kernel_mem = grub_efi_allocate_fixed (low_addr, kernel_pages);
|
||||
reloc_offset = 0;
|
||||
}
|
||||
/* Try to relocate. */
|
||||
if (! kernel_mem && (!relocate || grub_strcmp (relocate, "off") != 0))
|
||||
{
|
||||
kernel_mem = allocate_pages (align, kernel_pages, low_addr);
|
||||
if (kernel_mem)
|
||||
{
|
||||
reloc_offset = (grub_uint64_t)kernel_mem - low_addr;
|
||||
grub_dprintf ("linux", " Relocated at %p (offset=%016lx)\n",
|
||||
kernel_mem, reloc_offset);
|
||||
entry += reloc_offset;
|
||||
}
|
||||
}
|
||||
if (! kernel_mem)
|
||||
return grub_error (GRUB_ERR_OUT_OF_MEMORY,
|
||||
"cannot allocate memory for OS");
|
||||
|
||||
/* Load every loadable segment in memory. */
|
||||
for (i = 0; i < ehdr->e_phnum; i++)
|
||||
{
|
||||
phdr = (Elf64_Phdr *) ((char *) buffer + ehdr->e_phoff
|
||||
+ i * ehdr->e_phentsize);
|
||||
if (phdr->p_type == PT_LOAD)
|
||||
{
|
||||
grub_dprintf ("linux", " [paddr=%lx load=%lx memsz=%08lx "
|
||||
"off=%lx flags=%x]\n",
|
||||
phdr->p_paddr, phdr->p_paddr + reloc_offset,
|
||||
phdr->p_memsz, phdr->p_offset, phdr->p_flags);
|
||||
|
||||
if (grub_file_seek (file, phdr->p_offset) == (grub_off_t)-1)
|
||||
return grub_errno;
|
||||
|
||||
if (grub_file_read (file, (void *) (phdr->p_paddr + reloc_offset),
|
||||
phdr->p_filesz)
|
||||
!= (grub_ssize_t) phdr->p_filesz)
|
||||
{
|
||||
if (!grub_errno)
|
||||
grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
|
||||
filename);
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
if (phdr->p_filesz < phdr->p_memsz)
|
||||
grub_memset
|
||||
((char *)(phdr->p_paddr + reloc_offset + phdr->p_filesz),
|
||||
0, phdr->p_memsz - phdr->p_filesz);
|
||||
|
||||
/* Sync caches if necessary. */
|
||||
if (phdr->p_flags & PF_X)
|
||||
grub_arch_sync_caches
|
||||
((void *)(phdr->p_paddr + reloc_offset), phdr->p_memsz);
|
||||
}
|
||||
}
|
||||
loaded = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
|
||||
int argc, char *argv[])
|
||||
{
|
||||
grub_file_t file = 0;
|
||||
char buffer[GRUB_ELF_SEARCH];
|
||||
char *cmdline, *p;
|
||||
grub_ssize_t len;
|
||||
int i;
|
||||
|
||||
grub_dl_ref (my_mod);
|
||||
|
||||
grub_loader_unset ();
|
||||
|
||||
if (argc == 0)
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
file = grub_file_open (argv[0], GRUB_FILE_TYPE_LINUX_KERNEL);
|
||||
if (! file)
|
||||
goto fail;
|
||||
|
||||
len = grub_file_read (file, buffer, sizeof (buffer));
|
||||
if (len < (grub_ssize_t) sizeof (Elf64_Ehdr))
|
||||
{
|
||||
if (!grub_errno)
|
||||
grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
|
||||
argv[0]);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
grub_dprintf ("linux", "Loading linux: %s\n", argv[0]);
|
||||
|
||||
if (grub_load_elf64 (file, buffer, argv[0]))
|
||||
goto fail;
|
||||
|
||||
len = sizeof("BOOT_IMAGE=") + 8;
|
||||
for (i = 0; i < argc; i++)
|
||||
len += grub_strlen (argv[i]) + 1;
|
||||
len += sizeof (struct ia64_boot_param) + 512; /* Room for extensions. */
|
||||
boot_param_pages = page_align (len) >> 12;
|
||||
boot_param = grub_efi_allocate_any_pages (boot_param_pages);
|
||||
if (boot_param == 0)
|
||||
{
|
||||
grub_error (GRUB_ERR_OUT_OF_MEMORY,
|
||||
"cannot allocate memory for bootparams");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
grub_memset (boot_param, 0, len);
|
||||
cmdline = ((char *)(boot_param + 1)) + 256;
|
||||
|
||||
/* Build cmdline. */
|
||||
p = grub_stpcpy (cmdline, "BOOT_IMAGE");
|
||||
for (i = 0; i < argc; i++)
|
||||
{
|
||||
*p++ = ' ';
|
||||
p = grub_stpcpy (p, argv[i]);
|
||||
}
|
||||
cmdline[10] = '=';
|
||||
|
||||
*p = '\0';
|
||||
|
||||
if (grub_verify_string (cmdline, GRUB_VERIFY_KERNEL_CMDLINE))
|
||||
goto fail;
|
||||
|
||||
boot_param->command_line = (grub_uint64_t) cmdline;
|
||||
boot_param->efi_systab = (grub_uint64_t) grub_efi_system_table;
|
||||
|
||||
grub_errno = GRUB_ERR_NONE;
|
||||
|
||||
grub_loader_set (grub_linux_boot, grub_linux_unload, 0);
|
||||
|
||||
fail:
|
||||
if (file)
|
||||
grub_file_close (file);
|
||||
|
||||
if (grub_errno != GRUB_ERR_NONE)
|
||||
{
|
||||
grub_efi_free_pages ((grub_efi_physical_address_t) boot_param,
|
||||
boot_param_pages);
|
||||
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[])
|
||||
{
|
||||
struct grub_linux_initrd_context initrd_ctx = { 0, 0, 0 };
|
||||
|
||||
if (argc == 0)
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (! loaded)
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_ARGUMENT, N_("you need to load the kernel first"));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (grub_initrd_init (argc, argv, &initrd_ctx))
|
||||
goto fail;
|
||||
|
||||
initrd_size = grub_get_initrd_size (&initrd_ctx);
|
||||
grub_dprintf ("linux", "Loading initrd\n");
|
||||
|
||||
initrd_pages = (page_align (initrd_size) >> 12);
|
||||
initrd_mem = grub_efi_allocate_any_pages (initrd_pages);
|
||||
if (! initrd_mem)
|
||||
{
|
||||
grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate pages");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
grub_dprintf ("linux", "[addr=0x%lx, size=0x%lx]\n",
|
||||
(grub_uint64_t) initrd_mem, initrd_size);
|
||||
|
||||
if (grub_initrd_load (&initrd_ctx, argv, initrd_mem))
|
||||
goto fail;
|
||||
fail:
|
||||
grub_initrd_close (&initrd_ctx);
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_cmd_fpswa (grub_command_t cmd __attribute__ ((unused)),
|
||||
int argc __attribute__((unused)),
|
||||
char *argv[] __attribute__((unused)))
|
||||
{
|
||||
query_fpswa ();
|
||||
if (fpswa == NULL)
|
||||
grub_puts_ (N_("No FPSWA found"));
|
||||
else
|
||||
grub_printf (_("FPSWA revision: %x\n"), fpswa->revision);
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
static grub_command_t cmd_linux, cmd_initrd, cmd_fpswa;
|
||||
|
||||
GRUB_MOD_INIT(linux)
|
||||
{
|
||||
cmd_linux = grub_register_command ("linux", grub_cmd_linux,
|
||||
N_("FILE [ARGS...]"), N_("Load Linux."));
|
||||
|
||||
cmd_initrd = grub_register_command ("initrd", grub_cmd_initrd,
|
||||
N_("FILE"), N_("Load initrd."));
|
||||
|
||||
cmd_fpswa = grub_register_command ("fpswa", grub_cmd_fpswa,
|
||||
"", N_("Display FPSWA version."));
|
||||
|
||||
my_mod = mod;
|
||||
}
|
||||
|
||||
GRUB_MOD_FINI(linux)
|
||||
{
|
||||
grub_unregister_command (cmd_linux);
|
||||
grub_unregister_command (cmd_initrd);
|
||||
grub_unregister_command (cmd_fpswa);
|
||||
}
|
||||
511
GRUB2/MOD_SRC/grub-2.04/grub-core/loader/mips/linux.c
Normal file
511
GRUB2/MOD_SRC/grub-2.04/grub-core/loader/mips/linux.c
Normal file
@@ -0,0 +1,511 @@
|
||||
/* linux.c - boot Linux */
|
||||
/*
|
||||
* GRUB -- GRand Unified Bootloader
|
||||
* Copyright (C) 2003,2004,2005,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/elf.h>
|
||||
#include <grub/elfload.h>
|
||||
#include <grub/loader.h>
|
||||
#include <grub/dl.h>
|
||||
#include <grub/mm.h>
|
||||
#include <grub/misc.h>
|
||||
#include <grub/command.h>
|
||||
#include <grub/mips/relocator.h>
|
||||
#include <grub/memory.h>
|
||||
#include <grub/i18n.h>
|
||||
#include <grub/lib/cmdline.h>
|
||||
#include <grub/linux.h>
|
||||
|
||||
GRUB_MOD_LICENSE ("GPLv3+");
|
||||
|
||||
#pragma GCC diagnostic ignored "-Wcast-align"
|
||||
|
||||
/* For frequencies. */
|
||||
#include <grub/machine/time.h>
|
||||
|
||||
#ifdef GRUB_MACHINE_MIPS_LOONGSON
|
||||
#include <grub/pci.h>
|
||||
#include <grub/machine/kernel.h>
|
||||
|
||||
const char loongson_machtypes[][60] =
|
||||
{
|
||||
[GRUB_ARCH_MACHINE_YEELOONG] = "machtype=lemote-yeeloong-2f-8.9inches",
|
||||
[GRUB_ARCH_MACHINE_FULOONG2F] = "machtype=lemote-fuloong-2f-box",
|
||||
[GRUB_ARCH_MACHINE_FULOONG2E] = "machtype=lemote-fuloong-2e-unknown"
|
||||
};
|
||||
#endif
|
||||
|
||||
static grub_dl_t my_mod;
|
||||
|
||||
static int loaded;
|
||||
|
||||
static grub_size_t linux_size;
|
||||
|
||||
static struct grub_relocator *relocator;
|
||||
static grub_uint8_t *playground;
|
||||
static grub_addr_t target_addr, entry_addr;
|
||||
#ifdef GRUB_MACHINE_MIPS_QEMU_MIPS
|
||||
static char *params;
|
||||
#else
|
||||
static int linux_argc;
|
||||
static grub_off_t argv_off;
|
||||
#ifdef GRUB_MACHINE_MIPS_LOONGSON
|
||||
static grub_off_t envp_off;
|
||||
#endif
|
||||
static grub_off_t rd_addr_arg_off, rd_size_arg_off;
|
||||
#endif
|
||||
static int initrd_loaded = 0;
|
||||
|
||||
static grub_err_t
|
||||
grub_linux_boot (void)
|
||||
{
|
||||
struct grub_relocator32_state state;
|
||||
|
||||
grub_memset (&state, 0, sizeof (state));
|
||||
|
||||
/* Boot the kernel. */
|
||||
state.gpr[1] = entry_addr;
|
||||
|
||||
#ifdef GRUB_MACHINE_MIPS_QEMU_MIPS
|
||||
{
|
||||
grub_err_t err;
|
||||
grub_relocator_chunk_t ch;
|
||||
grub_uint32_t *memsize;
|
||||
grub_uint32_t *magic;
|
||||
char *str;
|
||||
|
||||
err = grub_relocator_alloc_chunk_addr (relocator, &ch,
|
||||
((16 << 20) - 264),
|
||||
grub_strlen (params) + 1 + 8);
|
||||
if (err)
|
||||
return err;
|
||||
memsize = get_virtual_current_address (ch);
|
||||
magic = memsize + 1;
|
||||
*memsize = grub_mmap_get_lower ();
|
||||
*magic = 0x12345678;
|
||||
str = (char *) (magic + 1);
|
||||
grub_strcpy (str, params);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef GRUB_MACHINE_MIPS_QEMU_MIPS
|
||||
state.gpr[4] = linux_argc;
|
||||
state.gpr[5] = target_addr + argv_off;
|
||||
#ifdef GRUB_MACHINE_MIPS_LOONGSON
|
||||
state.gpr[6] = target_addr + envp_off;
|
||||
#else
|
||||
state.gpr[6] = 0;
|
||||
#endif
|
||||
state.gpr[7] = 0;
|
||||
#endif
|
||||
state.jumpreg = 1;
|
||||
grub_relocator32_boot (relocator, state);
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_linux_unload (void)
|
||||
{
|
||||
grub_relocator_unload (relocator);
|
||||
grub_dl_unref (my_mod);
|
||||
|
||||
#ifdef GRUB_MACHINE_MIPS_QEMU_MIPS
|
||||
grub_free (params);
|
||||
params = 0;
|
||||
#endif
|
||||
|
||||
loaded = 0;
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_linux_load32 (grub_elf_t elf, const char *filename,
|
||||
void **extra_mem, grub_size_t extra_size)
|
||||
{
|
||||
Elf32_Addr base;
|
||||
int extraoff;
|
||||
grub_err_t err;
|
||||
|
||||
/* Linux's entry point incorrectly contains a virtual address. */
|
||||
entry_addr = elf->ehdr.ehdr32.e_entry;
|
||||
|
||||
linux_size = grub_elf32_size (elf, &base, 0);
|
||||
if (linux_size == 0)
|
||||
return grub_errno;
|
||||
target_addr = base;
|
||||
/* Pad it; the kernel scribbles over memory beyond its load address. */
|
||||
linux_size += 0x100000;
|
||||
linux_size = ALIGN_UP (base + linux_size, 4) - base;
|
||||
extraoff = linux_size;
|
||||
linux_size += extra_size;
|
||||
|
||||
relocator = grub_relocator_new ();
|
||||
if (!relocator)
|
||||
return grub_errno;
|
||||
|
||||
{
|
||||
grub_relocator_chunk_t ch;
|
||||
err = grub_relocator_alloc_chunk_addr (relocator, &ch,
|
||||
target_addr & 0x1fffffff,
|
||||
linux_size);
|
||||
if (err)
|
||||
return err;
|
||||
playground = get_virtual_current_address (ch);
|
||||
}
|
||||
|
||||
*extra_mem = playground + extraoff;
|
||||
|
||||
/* Now load the segments into the area we claimed. */
|
||||
return grub_elf32_load (elf, filename, playground - base, GRUB_ELF_LOAD_FLAGS_NONE, 0, 0);
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_linux_load64 (grub_elf_t elf, const char *filename,
|
||||
void **extra_mem, grub_size_t extra_size)
|
||||
{
|
||||
Elf64_Addr base;
|
||||
int extraoff;
|
||||
grub_err_t err;
|
||||
|
||||
/* Linux's entry point incorrectly contains a virtual address. */
|
||||
entry_addr = elf->ehdr.ehdr64.e_entry;
|
||||
|
||||
linux_size = grub_elf64_size (elf, &base, 0);
|
||||
if (linux_size == 0)
|
||||
return grub_errno;
|
||||
target_addr = base;
|
||||
/* Pad it; the kernel scribbles over memory beyond its load address. */
|
||||
linux_size += 0x100000;
|
||||
linux_size = ALIGN_UP (base + linux_size, 4) - base;
|
||||
extraoff = linux_size;
|
||||
linux_size += extra_size;
|
||||
|
||||
relocator = grub_relocator_new ();
|
||||
if (!relocator)
|
||||
return grub_errno;
|
||||
|
||||
{
|
||||
grub_relocator_chunk_t ch;
|
||||
err = grub_relocator_alloc_chunk_addr (relocator, &ch,
|
||||
target_addr & 0x1fffffff,
|
||||
linux_size);
|
||||
if (err)
|
||||
return err;
|
||||
playground = get_virtual_current_address (ch);
|
||||
}
|
||||
|
||||
*extra_mem = playground + extraoff;
|
||||
|
||||
/* Now load the segments into the area we claimed. */
|
||||
return grub_elf64_load (elf, filename, playground - base, GRUB_ELF_LOAD_FLAGS_NONE, 0, 0);
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
|
||||
int argc, char *argv[])
|
||||
{
|
||||
grub_elf_t elf = 0;
|
||||
int size;
|
||||
void *extra = NULL;
|
||||
#ifndef GRUB_MACHINE_MIPS_QEMU_MIPS
|
||||
int i;
|
||||
grub_uint32_t *linux_argv;
|
||||
char *linux_args;
|
||||
#endif
|
||||
grub_err_t err;
|
||||
#ifdef GRUB_MACHINE_MIPS_LOONGSON
|
||||
char *linux_envs;
|
||||
grub_uint32_t *linux_envp;
|
||||
#endif
|
||||
|
||||
if (argc == 0)
|
||||
return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
|
||||
|
||||
elf = grub_elf_open (argv[0], GRUB_FILE_TYPE_LINUX_KERNEL);
|
||||
if (! elf)
|
||||
return grub_errno;
|
||||
|
||||
if (elf->ehdr.ehdr32.e_type != ET_EXEC)
|
||||
{
|
||||
grub_elf_close (elf);
|
||||
return grub_error (GRUB_ERR_UNKNOWN_OS,
|
||||
N_("this ELF file is not of the right type"));
|
||||
}
|
||||
|
||||
/* Release the previously used memory. */
|
||||
grub_loader_unset ();
|
||||
loaded = 0;
|
||||
|
||||
#ifdef GRUB_MACHINE_MIPS_QEMU_MIPS
|
||||
size = 0;
|
||||
#else
|
||||
/* For arguments. */
|
||||
linux_argc = argc;
|
||||
#ifdef GRUB_MACHINE_MIPS_LOONGSON
|
||||
linux_argc++;
|
||||
#endif
|
||||
/* Main arguments. */
|
||||
size = (linux_argc) * sizeof (grub_uint32_t);
|
||||
/* Initrd address and size. */
|
||||
size += 2 * sizeof (grub_uint32_t);
|
||||
/* NULL terminator. */
|
||||
size += sizeof (grub_uint32_t);
|
||||
|
||||
/* First argument is always "a0". */
|
||||
size += ALIGN_UP (sizeof ("a0"), 4);
|
||||
/* Normal arguments. */
|
||||
for (i = 1; i < argc; i++)
|
||||
size += ALIGN_UP (grub_strlen (argv[i]) + 1, 4);
|
||||
#ifdef GRUB_MACHINE_MIPS_LOONGSON
|
||||
size += ALIGN_UP (sizeof (loongson_machtypes[0]), 4);
|
||||
#endif
|
||||
|
||||
/* rd arguments. */
|
||||
size += ALIGN_UP (sizeof ("rd_start=0xXXXXXXXXXXXXXXXX"), 4);
|
||||
size += ALIGN_UP (sizeof ("rd_size=0xXXXXXXXXXXXXXXXX"), 4);
|
||||
|
||||
/* For the environment. */
|
||||
size += sizeof (grub_uint32_t);
|
||||
size += 4 * sizeof (grub_uint32_t);
|
||||
size += ALIGN_UP (sizeof ("memsize=XXXXXXXXXXXXXXXXXXXX"), 4)
|
||||
+ ALIGN_UP (sizeof ("highmemsize=XXXXXXXXXXXXXXXXXXXX"), 4)
|
||||
+ ALIGN_UP (sizeof ("busclock=XXXXXXXXXX"), 4)
|
||||
+ ALIGN_UP (sizeof ("cpuclock=XXXXXXXXXX"), 4);
|
||||
#endif
|
||||
|
||||
if (grub_elf_is_elf32 (elf))
|
||||
err = grub_linux_load32 (elf, argv[0], &extra, size);
|
||||
else
|
||||
if (grub_elf_is_elf64 (elf))
|
||||
err = grub_linux_load64 (elf, argv[0], &extra, size);
|
||||
else
|
||||
err = grub_error (GRUB_ERR_BAD_OS, N_("invalid arch-dependent ELF magic"));
|
||||
|
||||
grub_elf_close (elf);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
#ifdef GRUB_MACHINE_MIPS_QEMU_MIPS
|
||||
/* Create kernel command line. */
|
||||
size = grub_loader_cmdline_size(argc, argv);
|
||||
params = grub_malloc (size + sizeof (LINUX_IMAGE));
|
||||
if (! params)
|
||||
{
|
||||
grub_linux_unload ();
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
grub_memcpy (params, LINUX_IMAGE, sizeof (LINUX_IMAGE));
|
||||
grub_create_loader_cmdline (argc, argv, params + sizeof (LINUX_IMAGE) - 1,
|
||||
size, GRUB_VERIFY_KERNEL_CMDLINE);
|
||||
#else
|
||||
linux_argv = extra;
|
||||
argv_off = (grub_uint8_t *) linux_argv - (grub_uint8_t *) playground;
|
||||
extra = linux_argv + (linux_argc + 1 + 2);
|
||||
linux_args = extra;
|
||||
|
||||
grub_memcpy (linux_args, "a0", sizeof ("a0"));
|
||||
*linux_argv = (grub_uint8_t *) linux_args - (grub_uint8_t *) playground
|
||||
+ target_addr;
|
||||
linux_argv++;
|
||||
linux_args += ALIGN_UP (sizeof ("a0"), 4);
|
||||
|
||||
char *params = linux_args;
|
||||
|
||||
#ifdef GRUB_MACHINE_MIPS_LOONGSON
|
||||
{
|
||||
unsigned mtype = grub_arch_machine;
|
||||
if (mtype >= ARRAY_SIZE (loongson_machtypes))
|
||||
mtype = 0;
|
||||
/* In Loongson platform, it is the responsibility of the bootloader/firmware
|
||||
to supply the OS kernel with machine type information. */
|
||||
grub_memcpy (linux_args, loongson_machtypes[mtype],
|
||||
sizeof (loongson_machtypes[mtype]));
|
||||
*linux_argv = (grub_uint8_t *) linux_args - (grub_uint8_t *) playground
|
||||
+ target_addr;
|
||||
linux_argv++;
|
||||
linux_args += ALIGN_UP (sizeof (loongson_machtypes[mtype]), 4);
|
||||
}
|
||||
#endif
|
||||
|
||||
for (i = 1; i < argc; i++)
|
||||
{
|
||||
grub_memcpy (linux_args, argv[i], grub_strlen (argv[i]) + 1);
|
||||
*linux_argv = (grub_uint8_t *) linux_args - (grub_uint8_t *) playground
|
||||
+ target_addr;
|
||||
linux_argv++;
|
||||
linux_args += ALIGN_UP (grub_strlen (argv[i]) + 1, 4);
|
||||
}
|
||||
|
||||
*linux_args = '\0';
|
||||
|
||||
err = grub_verify_string (params, GRUB_VERIFY_KERNEL_CMDLINE);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Reserve space for rd arguments. */
|
||||
rd_addr_arg_off = (grub_uint8_t *) linux_args - (grub_uint8_t *) playground;
|
||||
linux_args += ALIGN_UP (sizeof ("rd_start=0xXXXXXXXXXXXXXXXX"), 4);
|
||||
*linux_argv = 0;
|
||||
linux_argv++;
|
||||
|
||||
rd_size_arg_off = (grub_uint8_t *) linux_args - (grub_uint8_t *) playground;
|
||||
linux_args += ALIGN_UP (sizeof ("rd_size=0xXXXXXXXXXXXXXXXX"), 4);
|
||||
*linux_argv = 0;
|
||||
linux_argv++;
|
||||
|
||||
*linux_argv = 0;
|
||||
|
||||
extra = linux_args;
|
||||
|
||||
#ifdef GRUB_MACHINE_MIPS_LOONGSON
|
||||
linux_envp = extra;
|
||||
envp_off = (grub_uint8_t *) linux_envp - (grub_uint8_t *) playground;
|
||||
linux_envs = (char *) (linux_envp + 5);
|
||||
grub_snprintf (linux_envs, sizeof ("memsize=XXXXXXXXXXXXXXXXXXXX"),
|
||||
"memsize=%lld",
|
||||
(unsigned long long) grub_mmap_get_lower () >> 20);
|
||||
linux_envp[0] = (grub_uint8_t *) linux_envs - (grub_uint8_t *) playground
|
||||
+ target_addr;
|
||||
linux_envs += ALIGN_UP (grub_strlen (linux_envs) + 1, 4);
|
||||
grub_snprintf (linux_envs, sizeof ("highmemsize=XXXXXXXXXXXXXXXXXXXX"),
|
||||
"highmemsize=%lld",
|
||||
(unsigned long long) grub_mmap_get_upper () >> 20);
|
||||
linux_envp[1] = (grub_uint8_t *) linux_envs - (grub_uint8_t *) playground
|
||||
+ target_addr;
|
||||
linux_envs += ALIGN_UP (grub_strlen (linux_envs) + 1, 4);
|
||||
|
||||
grub_snprintf (linux_envs, sizeof ("busclock=XXXXXXXXXX"),
|
||||
"busclock=%d", grub_arch_busclock);
|
||||
linux_envp[2] = (grub_uint8_t *) linux_envs - (grub_uint8_t *) playground
|
||||
+ target_addr;
|
||||
linux_envs += ALIGN_UP (grub_strlen (linux_envs) + 1, 4);
|
||||
grub_snprintf (linux_envs, sizeof ("cpuclock=XXXXXXXXXX"),
|
||||
"cpuclock=%d", grub_arch_cpuclock);
|
||||
linux_envp[3] = (grub_uint8_t *) linux_envs - (grub_uint8_t *) playground
|
||||
+ target_addr;
|
||||
linux_envs += ALIGN_UP (grub_strlen (linux_envs) + 1, 4);
|
||||
|
||||
linux_envp[4] = 0;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
grub_loader_set (grub_linux_boot, grub_linux_unload, 1);
|
||||
initrd_loaded = 0;
|
||||
loaded = 1;
|
||||
grub_dl_ref (my_mod);
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
|
||||
int argc, char *argv[])
|
||||
{
|
||||
grub_size_t size = 0;
|
||||
void *initrd_src;
|
||||
grub_addr_t initrd_dest;
|
||||
grub_err_t err;
|
||||
struct grub_linux_initrd_context initrd_ctx = { 0, 0, 0 };
|
||||
|
||||
if (argc == 0)
|
||||
return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
|
||||
|
||||
if (!loaded)
|
||||
return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("you need to load the kernel first"));
|
||||
|
||||
if (initrd_loaded)
|
||||
return grub_error (GRUB_ERR_BAD_ARGUMENT, "only one initrd command can be issued.");
|
||||
|
||||
if (grub_initrd_init (argc, argv, &initrd_ctx))
|
||||
goto fail;
|
||||
|
||||
size = grub_get_initrd_size (&initrd_ctx);
|
||||
|
||||
{
|
||||
grub_relocator_chunk_t ch;
|
||||
|
||||
err = grub_relocator_alloc_chunk_align (relocator, &ch,
|
||||
(target_addr & 0x1fffffff)
|
||||
+ linux_size + 0x10000,
|
||||
(0x10000000 - size),
|
||||
size, 0x10000,
|
||||
GRUB_RELOCATOR_PREFERENCE_NONE, 0);
|
||||
|
||||
if (err)
|
||||
goto fail;
|
||||
initrd_src = get_virtual_current_address (ch);
|
||||
initrd_dest = get_physical_target_address (ch) | 0x80000000;
|
||||
}
|
||||
|
||||
if (grub_initrd_load (&initrd_ctx, argv, initrd_src))
|
||||
goto fail;
|
||||
|
||||
#ifdef GRUB_MACHINE_MIPS_QEMU_MIPS
|
||||
{
|
||||
char *tmp;
|
||||
tmp = grub_xasprintf ("%s rd_start=0x%" PRIxGRUB_ADDR
|
||||
" rd_size=0x%" PRIxGRUB_ADDR, params,
|
||||
initrd_dest, size);
|
||||
if (!tmp)
|
||||
goto fail;
|
||||
grub_free (params);
|
||||
params = tmp;
|
||||
}
|
||||
#else
|
||||
grub_snprintf ((char *) playground + rd_addr_arg_off,
|
||||
sizeof ("rd_start=0xXXXXXXXXXXXXXXXX"), "rd_start=0x%llx",
|
||||
(unsigned long long) initrd_dest);
|
||||
((grub_uint32_t *) (playground + argv_off))[linux_argc]
|
||||
= target_addr + rd_addr_arg_off;
|
||||
linux_argc++;
|
||||
|
||||
grub_snprintf ((char *) playground + rd_size_arg_off,
|
||||
sizeof ("rd_size=0xXXXXXXXXXXXXXXXXX"), "rd_size=0x%llx",
|
||||
(unsigned long long) size);
|
||||
((grub_uint32_t *) (playground + argv_off))[linux_argc]
|
||||
= target_addr + rd_size_arg_off;
|
||||
linux_argc++;
|
||||
#endif
|
||||
|
||||
initrd_loaded = 1;
|
||||
|
||||
fail:
|
||||
grub_initrd_close (&initrd_ctx);
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,393 @@
|
||||
/* linux.c - boot Linux */
|
||||
/*
|
||||
* GRUB -- GRand Unified Bootloader
|
||||
* Copyright (C) 2003,2004,2005,2007,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/elf.h>
|
||||
#include <grub/elfload.h>
|
||||
#include <grub/loader.h>
|
||||
#include <grub/dl.h>
|
||||
#include <grub/mm.h>
|
||||
#include <grub/misc.h>
|
||||
#include <grub/ieee1275/ieee1275.h>
|
||||
#include <grub/command.h>
|
||||
#include <grub/i18n.h>
|
||||
#include <grub/memory.h>
|
||||
#include <grub/lib/cmdline.h>
|
||||
#include <grub/cache.h>
|
||||
#include <grub/linux.h>
|
||||
|
||||
GRUB_MOD_LICENSE ("GPLv3+");
|
||||
|
||||
#define ELF32_LOADMASK (0xc0000000UL)
|
||||
#define ELF64_LOADMASK (0xc000000000000000ULL)
|
||||
|
||||
static grub_dl_t my_mod;
|
||||
|
||||
static int loaded;
|
||||
|
||||
static grub_addr_t initrd_addr;
|
||||
static grub_size_t initrd_size;
|
||||
|
||||
static grub_addr_t linux_addr;
|
||||
static grub_addr_t linux_entry;
|
||||
static grub_size_t linux_size;
|
||||
|
||||
static char *linux_args;
|
||||
|
||||
typedef void (*kernel_entry_t) (void *, unsigned long, int (void *),
|
||||
unsigned long, unsigned long);
|
||||
|
||||
/* Context for grub_linux_claimmap_iterate. */
|
||||
struct grub_linux_claimmap_iterate_ctx
|
||||
{
|
||||
grub_addr_t target;
|
||||
grub_size_t size;
|
||||
grub_size_t align;
|
||||
grub_addr_t found_addr;
|
||||
};
|
||||
|
||||
/* Helper for grub_linux_claimmap_iterate. */
|
||||
static int
|
||||
alloc_mem (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type,
|
||||
void *data)
|
||||
{
|
||||
struct grub_linux_claimmap_iterate_ctx *ctx = data;
|
||||
|
||||
grub_uint64_t end = addr + len;
|
||||
addr = ALIGN_UP (addr, ctx->align);
|
||||
ctx->target = ALIGN_UP (ctx->target, ctx->align);
|
||||
|
||||
/* Target above the memory chunk. */
|
||||
if (type != GRUB_MEMORY_AVAILABLE || ctx->target > end)
|
||||
return 0;
|
||||
|
||||
/* Target inside the memory chunk. */
|
||||
if (ctx->target >= addr && ctx->target < end &&
|
||||
ctx->size <= end - ctx->target)
|
||||
{
|
||||
if (grub_claimmap (ctx->target, ctx->size) == GRUB_ERR_NONE)
|
||||
{
|
||||
ctx->found_addr = ctx->target;
|
||||
return 1;
|
||||
}
|
||||
grub_print_error ();
|
||||
}
|
||||
/* Target below the memory chunk. */
|
||||
if (ctx->target < addr && addr + ctx->size <= end)
|
||||
{
|
||||
if (grub_claimmap (addr, ctx->size) == GRUB_ERR_NONE)
|
||||
{
|
||||
ctx->found_addr = addr;
|
||||
return 1;
|
||||
}
|
||||
grub_print_error ();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static grub_addr_t
|
||||
grub_linux_claimmap_iterate (grub_addr_t target, grub_size_t size,
|
||||
grub_size_t align)
|
||||
{
|
||||
struct grub_linux_claimmap_iterate_ctx ctx = {
|
||||
.target = target,
|
||||
.size = size,
|
||||
.align = align,
|
||||
.found_addr = (grub_addr_t) -1
|
||||
};
|
||||
|
||||
if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_FORCE_CLAIM))
|
||||
{
|
||||
grub_uint64_t addr = target;
|
||||
if (addr < GRUB_IEEE1275_STATIC_HEAP_START
|
||||
+ GRUB_IEEE1275_STATIC_HEAP_LEN)
|
||||
addr = GRUB_IEEE1275_STATIC_HEAP_START
|
||||
+ GRUB_IEEE1275_STATIC_HEAP_LEN;
|
||||
addr = ALIGN_UP (addr, align);
|
||||
if (grub_claimmap (addr, size) == GRUB_ERR_NONE)
|
||||
return addr;
|
||||
return (grub_addr_t) -1;
|
||||
}
|
||||
|
||||
|
||||
grub_machine_mmap_iterate (alloc_mem, &ctx);
|
||||
|
||||
return ctx.found_addr;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_linux_boot (void)
|
||||
{
|
||||
kernel_entry_t linuxmain;
|
||||
grub_ssize_t actual;
|
||||
|
||||
grub_arch_sync_caches ((void *) linux_addr, linux_size);
|
||||
/* Set the command line arguments. */
|
||||
grub_ieee1275_set_property (grub_ieee1275_chosen, "bootargs", linux_args,
|
||||
grub_strlen (linux_args) + 1, &actual);
|
||||
|
||||
grub_dprintf ("loader", "Entry point: 0x%x\n", linux_entry);
|
||||
grub_dprintf ("loader", "Initrd at: 0x%x, size 0x%x\n", initrd_addr,
|
||||
initrd_size);
|
||||
grub_dprintf ("loader", "Boot arguments: %s\n", linux_args);
|
||||
grub_dprintf ("loader", "Jumping to Linux...\n");
|
||||
|
||||
/* Boot the kernel. */
|
||||
linuxmain = (kernel_entry_t) linux_entry;
|
||||
linuxmain ((void *) initrd_addr, initrd_size, grub_ieee1275_entry_fn, 0, 0);
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_linux_release_mem (void)
|
||||
{
|
||||
grub_free (linux_args);
|
||||
linux_args = 0;
|
||||
|
||||
if (linux_addr && grub_ieee1275_release (linux_addr, linux_size))
|
||||
return grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot release memory");
|
||||
|
||||
if (initrd_addr && grub_ieee1275_release (initrd_addr, initrd_size))
|
||||
return grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot release memory");
|
||||
|
||||
linux_addr = 0;
|
||||
initrd_addr = 0;
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_linux_unload (void)
|
||||
{
|
||||
grub_err_t err;
|
||||
|
||||
err = grub_linux_release_mem ();
|
||||
grub_dl_unref (my_mod);
|
||||
|
||||
loaded = 0;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_linux_load32 (grub_elf_t elf, const char *filename)
|
||||
{
|
||||
Elf32_Addr base_addr;
|
||||
grub_addr_t seg_addr;
|
||||
grub_uint32_t align;
|
||||
grub_uint32_t offset;
|
||||
Elf32_Addr entry;
|
||||
|
||||
linux_size = grub_elf32_size (elf, &base_addr, &align);
|
||||
if (linux_size == 0)
|
||||
return grub_errno;
|
||||
/* Pad it; the kernel scribbles over memory beyond its load address. */
|
||||
linux_size += 0x100000;
|
||||
|
||||
/* Linux's entry point incorrectly contains a virtual address. */
|
||||
entry = elf->ehdr.ehdr32.e_entry & ~ELF32_LOADMASK;
|
||||
|
||||
/* Linux's incorrectly contains a virtual address. */
|
||||
base_addr &= ~ELF32_LOADMASK;
|
||||
offset = entry - base_addr;
|
||||
|
||||
/* On some systems, firmware occupies the memory we're trying to use.
|
||||
* Happily, Linux can be loaded anywhere (it relocates itself). Iterate
|
||||
* until we find an open area. */
|
||||
seg_addr = grub_linux_claimmap_iterate (base_addr & ~ELF32_LOADMASK, linux_size, align);
|
||||
if (seg_addr == (grub_addr_t) -1)
|
||||
return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't claim memory");
|
||||
|
||||
linux_entry = seg_addr + offset;
|
||||
linux_addr = seg_addr;
|
||||
|
||||
/* Now load the segments into the area we claimed. */
|
||||
return grub_elf32_load (elf, filename, (void *) (seg_addr - base_addr), GRUB_ELF_LOAD_FLAGS_30BITS, 0, 0);
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_linux_load64 (grub_elf_t elf, const char *filename)
|
||||
{
|
||||
Elf64_Addr base_addr;
|
||||
grub_addr_t seg_addr;
|
||||
grub_uint64_t align;
|
||||
grub_uint64_t offset;
|
||||
Elf64_Addr entry;
|
||||
|
||||
linux_size = grub_elf64_size (elf, &base_addr, &align);
|
||||
if (linux_size == 0)
|
||||
return grub_errno;
|
||||
/* Pad it; the kernel scribbles over memory beyond its load address. */
|
||||
linux_size += 0x100000;
|
||||
|
||||
base_addr &= ~ELF64_LOADMASK;
|
||||
entry = elf->ehdr.ehdr64.e_entry & ~ELF64_LOADMASK;
|
||||
offset = entry - base_addr;
|
||||
/* Linux's incorrectly contains a virtual address. */
|
||||
|
||||
/* On some systems, firmware occupies the memory we're trying to use.
|
||||
* Happily, Linux can be loaded anywhere (it relocates itself). Iterate
|
||||
* until we find an open area. */
|
||||
seg_addr = grub_linux_claimmap_iterate (base_addr & ~ELF64_LOADMASK, linux_size, align);
|
||||
if (seg_addr == (grub_addr_t) -1)
|
||||
return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't claim memory");
|
||||
|
||||
linux_entry = seg_addr + offset;
|
||||
linux_addr = seg_addr;
|
||||
|
||||
/* Now load the segments into the area we claimed. */
|
||||
return grub_elf64_load (elf, filename, (void *) (grub_addr_t) (seg_addr - base_addr), GRUB_ELF_LOAD_FLAGS_62BITS, 0, 0);
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
|
||||
int argc, char *argv[])
|
||||
{
|
||||
grub_elf_t elf = 0;
|
||||
int size;
|
||||
|
||||
grub_dl_ref (my_mod);
|
||||
|
||||
if (argc == 0)
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
|
||||
goto out;
|
||||
}
|
||||
|
||||
elf = grub_elf_open (argv[0], GRUB_FILE_TYPE_LINUX_KERNEL);
|
||||
if (! elf)
|
||||
goto out;
|
||||
|
||||
if (elf->ehdr.ehdr32.e_type != ET_EXEC && elf->ehdr.ehdr32.e_type != ET_DYN)
|
||||
{
|
||||
grub_error (GRUB_ERR_UNKNOWN_OS,
|
||||
N_("this ELF file is not of the right type"));
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Release the previously used memory. */
|
||||
grub_loader_unset ();
|
||||
|
||||
if (grub_elf_is_elf32 (elf))
|
||||
grub_linux_load32 (elf, argv[0]);
|
||||
else
|
||||
if (grub_elf_is_elf64 (elf))
|
||||
grub_linux_load64 (elf, argv[0]);
|
||||
else
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("invalid arch-dependent ELF magic"));
|
||||
goto out;
|
||||
}
|
||||
|
||||
size = grub_loader_cmdline_size(argc, argv);
|
||||
linux_args = grub_malloc (size + sizeof (LINUX_IMAGE));
|
||||
if (! linux_args)
|
||||
goto out;
|
||||
|
||||
/* Create kernel command line. */
|
||||
grub_memcpy (linux_args, LINUX_IMAGE, sizeof (LINUX_IMAGE));
|
||||
if (grub_create_loader_cmdline (argc, argv, linux_args + sizeof (LINUX_IMAGE) - 1,
|
||||
size, GRUB_VERIFY_KERNEL_CMDLINE))
|
||||
goto out;
|
||||
|
||||
out:
|
||||
|
||||
if (elf)
|
||||
grub_elf_close (elf);
|
||||
|
||||
if (grub_errno != GRUB_ERR_NONE)
|
||||
{
|
||||
grub_linux_release_mem ();
|
||||
grub_dl_unref (my_mod);
|
||||
loaded = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
grub_loader_set (grub_linux_boot, grub_linux_unload, 1);
|
||||
initrd_addr = 0;
|
||||
loaded = 1;
|
||||
}
|
||||
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
|
||||
int argc, char *argv[])
|
||||
{
|
||||
grub_size_t size = 0;
|
||||
grub_addr_t first_addr;
|
||||
grub_addr_t addr;
|
||||
struct grub_linux_initrd_context initrd_ctx = { 0, 0, 0 };
|
||||
|
||||
if (argc == 0)
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!loaded)
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_ARGUMENT, N_("you need to load the kernel first"));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (grub_initrd_init (argc, argv, &initrd_ctx))
|
||||
goto fail;
|
||||
|
||||
size = grub_get_initrd_size (&initrd_ctx);
|
||||
|
||||
first_addr = linux_addr + linux_size;
|
||||
|
||||
/* Attempt to claim at a series of addresses until successful in
|
||||
the same way that grub_rescue_cmd_linux does. */
|
||||
addr = grub_linux_claimmap_iterate (first_addr, size, 0x100000);
|
||||
if (addr == (grub_addr_t) -1)
|
||||
goto fail;
|
||||
|
||||
grub_dprintf ("loader", "Loading initrd at 0x%x, size 0x%x\n", addr, size);
|
||||
|
||||
if (grub_initrd_load (&initrd_ctx, argv, (void *) addr))
|
||||
goto fail;
|
||||
|
||||
initrd_addr = addr;
|
||||
initrd_size = size;
|
||||
|
||||
fail:
|
||||
grub_initrd_close (&initrd_ctx);
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,521 @@
|
||||
/* linux.c - boot Linux */
|
||||
/*
|
||||
* GRUB -- GRand Unified Bootloader
|
||||
* Copyright (C) 2003, 2004, 2005, 2007, 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/elf.h>
|
||||
#include <grub/elfload.h>
|
||||
#include <grub/loader.h>
|
||||
#include <grub/dl.h>
|
||||
#include <grub/mm.h>
|
||||
#include <grub/misc.h>
|
||||
#include <grub/ieee1275/ieee1275.h>
|
||||
#include <grub/command.h>
|
||||
#include <grub/i18n.h>
|
||||
#include <grub/memory.h>
|
||||
#include <grub/lib/cmdline.h>
|
||||
#include <grub/linux.h>
|
||||
|
||||
GRUB_MOD_LICENSE ("GPLv3+");
|
||||
|
||||
static grub_dl_t my_mod;
|
||||
|
||||
static int loaded;
|
||||
|
||||
/* /virtual-memory/translations property layout */
|
||||
struct grub_ieee1275_translation {
|
||||
grub_uint64_t vaddr;
|
||||
grub_uint64_t size;
|
||||
grub_uint64_t data;
|
||||
};
|
||||
|
||||
static struct grub_ieee1275_translation *of_trans;
|
||||
static int of_num_trans;
|
||||
|
||||
static grub_addr_t phys_base;
|
||||
static grub_addr_t grub_phys_start;
|
||||
static grub_addr_t grub_phys_end;
|
||||
|
||||
static grub_addr_t initrd_addr;
|
||||
static grub_addr_t initrd_paddr;
|
||||
static grub_size_t initrd_size;
|
||||
|
||||
static Elf64_Addr linux_entry;
|
||||
static grub_addr_t linux_addr;
|
||||
static grub_addr_t linux_paddr;
|
||||
static grub_size_t linux_size;
|
||||
|
||||
static char *linux_args;
|
||||
|
||||
struct linux_bootstr_info {
|
||||
int len, valid;
|
||||
char buf[];
|
||||
};
|
||||
|
||||
struct linux_hdrs {
|
||||
/* All HdrS versions support these fields. */
|
||||
unsigned int start_insns[2];
|
||||
char magic[4]; /* "HdrS" */
|
||||
unsigned int linux_kernel_version; /* LINUX_VERSION_CODE */
|
||||
unsigned short hdrs_version;
|
||||
unsigned short root_flags;
|
||||
unsigned short root_dev;
|
||||
unsigned short ram_flags;
|
||||
unsigned int __deprecated_ramdisk_image;
|
||||
unsigned int ramdisk_size;
|
||||
|
||||
/* HdrS versions 0x0201 and higher only */
|
||||
char *reboot_command;
|
||||
|
||||
/* HdrS versions 0x0202 and higher only */
|
||||
struct linux_bootstr_info *bootstr_info;
|
||||
|
||||
/* HdrS versions 0x0301 and higher only */
|
||||
unsigned long ramdisk_image;
|
||||
};
|
||||
|
||||
static grub_err_t
|
||||
grub_linux_boot (void)
|
||||
{
|
||||
struct linux_bootstr_info *bp;
|
||||
struct linux_hdrs *hp;
|
||||
grub_addr_t addr;
|
||||
|
||||
hp = (struct linux_hdrs *) linux_addr;
|
||||
|
||||
/* Any pointer we dereference in the kernel image must be relocated
|
||||
to where we actually loaded the kernel. */
|
||||
addr = (grub_addr_t) hp->bootstr_info;
|
||||
addr += (linux_addr - linux_entry);
|
||||
bp = (struct linux_bootstr_info *) addr;
|
||||
|
||||
/* Set the command line arguments, unless the kernel has been
|
||||
built with a fixed CONFIG_CMDLINE. */
|
||||
if (!bp->valid)
|
||||
{
|
||||
int len = grub_strlen (linux_args) + 1;
|
||||
if (bp->len < len)
|
||||
len = bp->len;
|
||||
grub_memcpy(bp->buf, linux_args, len);
|
||||
bp->buf[len-1] = '\0';
|
||||
bp->valid = 1;
|
||||
}
|
||||
|
||||
if (initrd_addr)
|
||||
{
|
||||
/* The kernel expects the physical address, adjusted relative
|
||||
to the lowest address advertised in "/memory"'s available
|
||||
property.
|
||||
|
||||
The history of this is that back when the kernel only supported
|
||||
specifying a 32-bit ramdisk address, this was the way to still
|
||||
be able to specify the ramdisk physical address even if memory
|
||||
started at some place above 4GB.
|
||||
|
||||
The magic 0x400000 is KERNBASE, I have no idea why SILO adds
|
||||
that term into the address, but it does and thus we have to do
|
||||
it too as this is what the kernel expects. */
|
||||
hp->ramdisk_image = initrd_paddr - phys_base + 0x400000;
|
||||
hp->ramdisk_size = initrd_size;
|
||||
}
|
||||
|
||||
grub_dprintf ("loader", "Entry point: 0x%lx\n", linux_addr);
|
||||
grub_dprintf ("loader", "Initrd at: 0x%lx, size 0x%lx\n", initrd_addr,
|
||||
initrd_size);
|
||||
grub_dprintf ("loader", "Boot arguments: %s\n", linux_args);
|
||||
grub_dprintf ("loader", "Jumping to Linux...\n");
|
||||
|
||||
/* Boot the kernel. */
|
||||
asm volatile ("ldx %0, %%o4\n"
|
||||
"ldx %1, %%o6\n"
|
||||
"ldx %2, %%o5\n"
|
||||
"mov %%g0, %%o0\n"
|
||||
"mov %%g0, %%o2\n"
|
||||
"mov %%g0, %%o3\n"
|
||||
"jmp %%o5\n"
|
||||
"mov %%g0, %%o1\n": :
|
||||
"m"(grub_ieee1275_entry_fn),
|
||||
"m"(grub_ieee1275_original_stack),
|
||||
"m"(linux_addr));
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_linux_release_mem (void)
|
||||
{
|
||||
grub_free (linux_args);
|
||||
linux_args = 0;
|
||||
linux_addr = 0;
|
||||
initrd_addr = 0;
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_linux_unload (void)
|
||||
{
|
||||
grub_err_t err;
|
||||
|
||||
err = grub_linux_release_mem ();
|
||||
grub_dl_unref (my_mod);
|
||||
|
||||
loaded = 0;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
#define FOUR_MB (4 * 1024 * 1024)
|
||||
|
||||
/* Context for alloc_phys. */
|
||||
struct alloc_phys_ctx
|
||||
{
|
||||
grub_addr_t size;
|
||||
grub_addr_t ret;
|
||||
};
|
||||
|
||||
/* Helper for alloc_phys. */
|
||||
static int
|
||||
alloc_phys_choose (grub_uint64_t addr, grub_uint64_t len,
|
||||
grub_memory_type_t type, void *data)
|
||||
{
|
||||
struct alloc_phys_ctx *ctx = data;
|
||||
grub_addr_t end = addr + len;
|
||||
|
||||
if (type != GRUB_MEMORY_AVAILABLE)
|
||||
return 0;
|
||||
|
||||
addr = ALIGN_UP (addr, FOUR_MB);
|
||||
if (addr + ctx->size >= end)
|
||||
return 0;
|
||||
|
||||
/* OBP available region contains grub. Start at grub_phys_end. */
|
||||
/* grub_phys_start does not start at the beginning of the memory region */
|
||||
if ((grub_phys_start >= addr && grub_phys_end < end) ||
|
||||
(addr > grub_phys_start && addr < grub_phys_end))
|
||||
{
|
||||
addr = ALIGN_UP (grub_phys_end, FOUR_MB);
|
||||
if (addr + ctx->size >= end)
|
||||
return 0;
|
||||
}
|
||||
|
||||
grub_dprintf("loader",
|
||||
"addr = 0x%lx grub_phys_start = 0x%lx grub_phys_end = 0x%lx\n",
|
||||
addr, grub_phys_start, grub_phys_end);
|
||||
|
||||
if (loaded)
|
||||
{
|
||||
grub_addr_t linux_end = ALIGN_UP (linux_paddr + linux_size, FOUR_MB);
|
||||
|
||||
if (addr >= linux_paddr && addr < linux_end)
|
||||
{
|
||||
addr = linux_end;
|
||||
if (addr + ctx->size >= end)
|
||||
return 0;
|
||||
}
|
||||
if ((addr + ctx->size) >= linux_paddr
|
||||
&& (addr + ctx->size) < linux_end)
|
||||
{
|
||||
addr = linux_end;
|
||||
if (addr + ctx->size >= end)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
ctx->ret = addr;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static grub_addr_t
|
||||
alloc_phys (grub_addr_t size)
|
||||
{
|
||||
struct alloc_phys_ctx ctx = {
|
||||
.size = size,
|
||||
.ret = (grub_addr_t) -1
|
||||
};
|
||||
|
||||
grub_machine_mmap_iterate (alloc_phys_choose, &ctx);
|
||||
|
||||
return ctx.ret;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_linux_load64 (grub_elf_t elf, const char *filename)
|
||||
{
|
||||
grub_addr_t off, paddr, base;
|
||||
int ret;
|
||||
|
||||
linux_entry = elf->ehdr.ehdr64.e_entry;
|
||||
linux_addr = 0x40004000;
|
||||
off = 0x4000;
|
||||
linux_size = grub_elf64_size (elf, 0, 0);
|
||||
if (linux_size == 0)
|
||||
return grub_errno;
|
||||
|
||||
grub_dprintf ("loader", "Attempting to claim at 0x%lx, size 0x%lx.\n",
|
||||
linux_addr, linux_size);
|
||||
|
||||
paddr = alloc_phys (linux_size + off);
|
||||
if (paddr == (grub_addr_t) -1)
|
||||
return grub_error (GRUB_ERR_OUT_OF_MEMORY,
|
||||
"couldn't allocate physical memory");
|
||||
ret = grub_ieee1275_map (paddr, linux_addr - off,
|
||||
linux_size + off, IEEE1275_MAP_DEFAULT);
|
||||
if (ret)
|
||||
return grub_error (GRUB_ERR_OUT_OF_MEMORY,
|
||||
"couldn't map physical memory");
|
||||
|
||||
grub_dprintf ("loader", "Loading Linux at vaddr 0x%lx, paddr 0x%lx, size 0x%lx\n",
|
||||
linux_addr, paddr, linux_size);
|
||||
|
||||
linux_paddr = paddr;
|
||||
|
||||
base = linux_entry - off;
|
||||
|
||||
/* Now load the segments into the area we claimed. */
|
||||
return grub_elf64_load (elf, filename, (void *) (linux_addr - off - base), GRUB_ELF_LOAD_FLAGS_NONE, 0, 0);
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
|
||||
int argc, char *argv[])
|
||||
{
|
||||
grub_file_t file = 0;
|
||||
grub_elf_t elf = 0;
|
||||
int size;
|
||||
|
||||
grub_dl_ref (my_mod);
|
||||
|
||||
if (argc == 0)
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
|
||||
goto out;
|
||||
}
|
||||
|
||||
file = grub_file_open (argv[0], GRUB_FILE_TYPE_LINUX_KERNEL);
|
||||
if (!file)
|
||||
goto out;
|
||||
|
||||
elf = grub_elf_file (file, argv[0]);
|
||||
if (! elf)
|
||||
goto out;
|
||||
|
||||
if (elf->ehdr.ehdr32.e_type != ET_EXEC)
|
||||
{
|
||||
grub_error (GRUB_ERR_UNKNOWN_OS,
|
||||
N_("this ELF file is not of the right type"));
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Release the previously used memory. */
|
||||
grub_loader_unset ();
|
||||
|
||||
if (grub_elf_is_elf64 (elf))
|
||||
grub_linux_load64 (elf, argv[0]);
|
||||
else
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("invalid arch-dependent ELF magic"));
|
||||
goto out;
|
||||
}
|
||||
|
||||
size = grub_loader_cmdline_size(argc, argv);
|
||||
|
||||
linux_args = grub_malloc (size + sizeof (LINUX_IMAGE));
|
||||
if (! linux_args)
|
||||
goto out;
|
||||
|
||||
/* Create kernel command line. */
|
||||
grub_memcpy (linux_args, LINUX_IMAGE, sizeof (LINUX_IMAGE));
|
||||
if (grub_create_loader_cmdline (argc, argv, linux_args + sizeof (LINUX_IMAGE) - 1,
|
||||
size, GRUB_VERIFY_KERNEL_CMDLINE))
|
||||
goto out;
|
||||
|
||||
out:
|
||||
if (elf)
|
||||
grub_elf_close (elf);
|
||||
else if (file)
|
||||
grub_file_close (file);
|
||||
|
||||
if (grub_errno != GRUB_ERR_NONE)
|
||||
{
|
||||
grub_linux_release_mem ();
|
||||
grub_dl_unref (my_mod);
|
||||
loaded = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
grub_loader_set (grub_linux_boot, grub_linux_unload, 1);
|
||||
initrd_addr = 0;
|
||||
loaded = 1;
|
||||
}
|
||||
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
|
||||
int argc, char *argv[])
|
||||
{
|
||||
grub_size_t size = 0;
|
||||
grub_addr_t paddr;
|
||||
grub_addr_t addr;
|
||||
int ret;
|
||||
struct grub_linux_initrd_context initrd_ctx = { 0, 0, 0 };
|
||||
|
||||
if (argc == 0)
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!loaded)
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_ARGUMENT, N_("you need to load the kernel first"));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (grub_initrd_init (argc, argv, &initrd_ctx))
|
||||
goto fail;
|
||||
|
||||
size = grub_get_initrd_size (&initrd_ctx);
|
||||
|
||||
addr = 0x60000000;
|
||||
|
||||
paddr = alloc_phys (size);
|
||||
if (paddr == (grub_addr_t) -1)
|
||||
{
|
||||
grub_error (GRUB_ERR_OUT_OF_MEMORY,
|
||||
"couldn't allocate physical memory");
|
||||
goto fail;
|
||||
}
|
||||
ret = grub_ieee1275_map (paddr, addr, size, IEEE1275_MAP_DEFAULT);
|
||||
if (ret)
|
||||
{
|
||||
grub_error (GRUB_ERR_OUT_OF_MEMORY,
|
||||
"couldn't map physical memory");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
grub_dprintf ("loader", "Loading initrd at vaddr 0x%lx, paddr 0x%lx, size 0x%lx\n",
|
||||
addr, paddr, size);
|
||||
|
||||
if (grub_initrd_load (&initrd_ctx, argv, (void *) addr))
|
||||
goto fail;
|
||||
|
||||
initrd_addr = addr;
|
||||
initrd_paddr = paddr;
|
||||
initrd_size = size;
|
||||
|
||||
fail:
|
||||
grub_initrd_close (&initrd_ctx);
|
||||
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
/* Helper for determine_phys_base. */
|
||||
static int
|
||||
get_physbase (grub_uint64_t addr, grub_uint64_t len __attribute__ ((unused)),
|
||||
grub_memory_type_t type, void *data __attribute__ ((unused)))
|
||||
{
|
||||
if (type != GRUB_MEMORY_AVAILABLE)
|
||||
return 0;
|
||||
if (addr < phys_base)
|
||||
phys_base = addr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
determine_phys_base (void)
|
||||
{
|
||||
phys_base = ~(grub_uint64_t) 0;
|
||||
grub_machine_mmap_iterate (get_physbase, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
fetch_translations (void)
|
||||
{
|
||||
grub_ieee1275_phandle_t node;
|
||||
grub_ssize_t actual;
|
||||
int i;
|
||||
|
||||
if (grub_ieee1275_finddevice ("/virtual-memory", &node))
|
||||
{
|
||||
grub_printf ("Cannot find /virtual-memory node.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (grub_ieee1275_get_property_length (node, "translations", &actual))
|
||||
{
|
||||
grub_printf ("Cannot find /virtual-memory/translations size.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
of_trans = grub_malloc (actual);
|
||||
if (!of_trans)
|
||||
{
|
||||
grub_printf ("Cannot allocate translations buffer.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (grub_ieee1275_get_property (node, "translations", of_trans, actual, &actual))
|
||||
{
|
||||
grub_printf ("Cannot fetch /virtual-memory/translations property.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
of_num_trans = actual / sizeof(struct grub_ieee1275_translation);
|
||||
|
||||
for (i = 0; i < of_num_trans; i++)
|
||||
{
|
||||
struct grub_ieee1275_translation *p = &of_trans[i];
|
||||
|
||||
if (p->vaddr == 0x2000)
|
||||
{
|
||||
grub_addr_t phys, tte = p->data;
|
||||
|
||||
phys = tte & ~(0xff00000000001fffULL);
|
||||
|
||||
grub_phys_start = phys;
|
||||
grub_phys_end = grub_phys_start + p->size;
|
||||
grub_dprintf ("loader", "Grub lives at phys_start[%lx] phys_end[%lx]\n",
|
||||
(unsigned long) grub_phys_start,
|
||||
(unsigned long) grub_phys_end);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static grub_command_t cmd_linux, cmd_initrd;
|
||||
|
||||
GRUB_MOD_INIT(linux)
|
||||
{
|
||||
determine_phys_base ();
|
||||
fetch_translations ();
|
||||
|
||||
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);
|
||||
}
|
||||
24
GRUB2/MOD_SRC/grub-2.04/include/grub/linux.h
Normal file
24
GRUB2/MOD_SRC/grub-2.04/include/grub/linux.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#include <grub/file.h>
|
||||
|
||||
struct grub_linux_initrd_component;
|
||||
|
||||
struct grub_linux_initrd_context
|
||||
{
|
||||
int nfiles;
|
||||
struct grub_linux_initrd_component *components;
|
||||
grub_size_t size;
|
||||
};
|
||||
|
||||
grub_err_t
|
||||
grub_initrd_init (int argc, char *argv[],
|
||||
struct grub_linux_initrd_context *ctx);
|
||||
|
||||
grub_size_t
|
||||
grub_get_initrd_size (struct grub_linux_initrd_context *ctx);
|
||||
|
||||
void
|
||||
grub_initrd_close (struct grub_linux_initrd_context *initrd_ctx);
|
||||
|
||||
grub_err_t
|
||||
grub_initrd_load (struct grub_linux_initrd_context *initrd_ctx,
|
||||
char *argv[], void *target);
|
||||
Reference in New Issue
Block a user