diff --git a/ChangeLog b/ChangeLog index 700161efe..0f4df9067 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2013-11-09 Vladimir Serbinenko + + Add new ports: i386-xen and x86_64-xen. This allows running GRUB in + XEN PV environment and load kernels. + 2013-11-09 Vladimir Serbinenko * grub-core/loader/i386/multiboot_mbi.c: Handle space in command line. diff --git a/conf/Makefile.common b/conf/Makefile.common index 921e57682..0dc26db55 100644 --- a/conf/Makefile.common +++ b/conf/Makefile.common @@ -23,6 +23,9 @@ if COND_arm LDFLAGS_PLATFORM = -Wl,--wrap=__clear_cache endif +#FIXME: discover and check XEN headers +CPPFLAGS_XEN = -I/usr/include + # Other options CPPFLAGS_DEFAULT = -DGRUB_FILE=\"$(subst $(srcdir)/,,$<)\" diff --git a/configure.ac b/configure.ac index ea38f090f..3232a8089 100644 --- a/configure.ac +++ b/configure.ac @@ -133,6 +133,7 @@ fi case "$target_cpu"-"$platform" in x86_64-efi) ;; x86_64-emu) ;; + x86_64-xen) ;; x86_64-*) target_cpu=i386 ;; powerpc64-ieee1275) target_cpu=powerpc ;; esac @@ -141,6 +142,8 @@ esac case "$target_cpu"-"$platform" in i386-efi) ;; x86_64-efi) ;; + i386-xen) ;; + x86_64-xen) ;; i386-pc) ;; i386-multiboot) ;; i386-coreboot) ;; @@ -193,6 +196,7 @@ case "$platform" in coreboot) machine_CPPFLAGS="$machine_CPPFLAGS -DGRUB_MACHINE_COREBOOT=1" ;; multiboot) machine_CPPFLAGS="$machine_CPPFLAGS -DGRUB_MACHINE_MULTIBOOT=1" ;; efi) machine_CPPFLAGS="$machine_CPPFLAGS -DGRUB_MACHINE_EFI=1" ;; + xen) machine_CPPFLAGS="$machine_CPPFLAGS -DGRUB_MACHINE_XEN=1" ;; ieee1275) machine_CPPFLAGS="$machine_CPPFLAGS -DGRUB_MACHINE_IEEE1275=1" ;; uboot) machine_CPPFLAGS="$machine_CPPFLAGS -DGRUB_MACHINE_UBOOT=1" ;; qemu) machine_CPPFLAGS="$machine_CPPFLAGS -DGRUB_MACHINE_QEMU=1" ;; @@ -1380,6 +1384,8 @@ AM_CONDITIONAL([COND_i386_ieee1275], [test x$target_cpu = xi386 -a x$platform = AM_CONDITIONAL([COND_i386_coreboot], [test x$target_cpu = xi386 -a x$platform = xcoreboot]) AM_CONDITIONAL([COND_i386_multiboot], [test x$target_cpu = xi386 -a x$platform = xmultiboot]) AM_CONDITIONAL([COND_x86_64_efi], [test x$target_cpu = xx86_64 -a x$platform = xefi]) +AM_CONDITIONAL([COND_i386_xen], [test x$target_cpu = xi386 -a x$platform = xxen]) +AM_CONDITIONAL([COND_x86_64_xen], [test x$target_cpu = xx86_64 -a x$platform = xxen]) AM_CONDITIONAL([COND_mips_loongson], [test x$target_cpu = xmipsel -a x$platform = xloongson]) AM_CONDITIONAL([COND_mips_qemu_mips], [test "(" x$target_cpu = xmips -o x$target_cpu = xmipsel ")" -a x$platform = xqemu_mips]) AM_CONDITIONAL([COND_mips_arc], [test "(" x$target_cpu = xmips -o x$target_cpu = xmipsel ")" -a x$platform = xarc]) diff --git a/docs/grub.texi b/docs/grub.texi index d1cf73d2e..ead8fbd55 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -2652,6 +2652,7 @@ Heavily limited platforms: Lightly limited platforms: @itemize +@item *-xen: limited only by adress space and RAM size. @item i386-qemu: kernel.img (.text + .data + .bss) is limited by 392704 bytes. (core.img would be limited by ROM size but it's unlimited on qemu @item All EFI platforms: limited by contiguous RAM size and possibly firmware bugs @@ -2707,9 +2708,9 @@ by a digit, like @samp{fd0}, or @samp{cd}. AHCI, PATA (ata), crypto, USB use the name of driver followed by a number. Memdisk and host are limited to one disk and so it's refered just by driver name. -RAID (md), ofdisk (ieee1275 and nand), LVM (lvm), LDM and arcdisk (arc) use -intrinsic name of disk prefixed by driver name. Additionally just ``nand'' -refers to the disk aliased as ``nand''. +RAID (md), ofdisk (ieee1275 and nand), LVM (lvm), LDM, virtio (vdsk) +and arcdisk (arc) use intrinsic name of disk prefixed by driver name. +Additionally just ``nand'' refers to the disk aliased as ``nand''. Conflicts are solved by suffixing a number if necessarry. Commas need to be escaped. Loopback uses whatever name specified to @command{loopback} command. diff --git a/gentpl.py b/gentpl.py index 05f3f969b..5f0bba522 100644 --- a/gentpl.py +++ b/gentpl.py @@ -21,6 +21,7 @@ GRUB_PLATFORMS = [ "emu", "i386_pc", "i386_efi", "i386_qemu", "i386_coreboot", "i386_multiboot", "i386_ieee1275", "x86_64_efi", + "i386_xen", "x86_64_xen", "mips_loongson", "sparc64_ieee1275", "powerpc_ieee1275", "mips_arc", "ia64_efi", "mips_qemu_mips", "arm_uboot", "arm_efi" ] @@ -42,6 +43,7 @@ GROUPS["arm"] = [ "arm_uboot", "arm_efi" ] GROUPS["efi"] = [ "i386_efi", "x86_64_efi", "ia64_efi", "arm_efi" ] GROUPS["ieee1275"] = [ "i386_ieee1275", "sparc64_ieee1275", "powerpc_ieee1275" ] GROUPS["uboot"] = [ "arm_uboot" ] +GROUPS["xen"] = [ "i386_xen", "x86_64_xen" ] # emu is a special case so many core functionality isn't needed on this platform GROUPS["noemu"] = GRUB_PLATFORMS[:]; GROUPS["noemu"].remove("emu") @@ -49,7 +51,7 @@ GROUPS["noemu"] = GRUB_PLATFORMS[:]; GROUPS["noemu"].remove("emu") # Groups based on hardware features GROUPS["cmos"] = GROUPS["x86"][:] + ["mips_loongson", "mips_qemu_mips", "sparc64_ieee1275", "powerpc_ieee1275"] -GROUPS["cmos"].remove("i386_efi"); GROUPS["cmos"].remove("x86_64_efi") +GROUPS["cmos"].remove("i386_efi"); GROUPS["cmos"].remove("x86_64_efi"); GROUPS["pci"] = GROUPS["x86"] + ["mips_loongson"] GROUPS["usb"] = GROUPS["pci"] @@ -59,7 +61,7 @@ GROUPS["videomodules"] = GRUB_PLATFORMS[:]; for i in GROUPS["videoinkernel"]: GROUPS["videomodules"].remove(i) # Similar for terminfo -GROUPS["terminfoinkernel"] = [ "emu", "mips_loongson", "mips_arc", "mips_qemu_mips" ] + GROUPS["ieee1275"] + GROUPS["uboot"]; +GROUPS["terminfoinkernel"] = [ "emu", "mips_loongson", "mips_arc", "mips_qemu_mips" ] + GROUPS["xen"] + GROUPS["ieee1275"] + GROUPS["uboot"]; GROUPS["terminfomodule"] = GRUB_PLATFORMS[:]; for i in GROUPS["terminfoinkernel"]: GROUPS["terminfomodule"].remove(i) diff --git a/grub-core/Makefile.am b/grub-core/Makefile.am index ea11716e6..980acf346 100644 --- a/grub-core/Makefile.am +++ b/grub-core/Makefile.am @@ -130,6 +130,24 @@ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/lib/arg.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/tsc.h endif +if COND_i386_xen +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/xen.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/xen/hypercall.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/terminfo.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/extcmd.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/lib/arg.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/loader.h +endif + +if COND_x86_64_xen +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/xen.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/x86_64/xen/hypercall.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/terminfo.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/extcmd.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/lib/arg.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/loader.h +endif + if COND_x86_64_efi KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/efi.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/disk.h diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index a053f203e..20951aab7 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -59,6 +59,11 @@ kernel = { ia64_efi_ldflags = '-Wl,-r,-d'; ia64_efi_stripflags = '--strip-unneeded -K start -R .note -R .comment -R .note.gnu.gold-version'; + x86_64_xen_ldflags = '-Wl,-Ttext=0'; + x86_64_xen_cppflags = '$(CPPFLAGS_XEN)'; + i386_xen_ldflags = '-Wl,-Ttext=0'; + i386_xen_cppflags = '$(CPPFLAGS_XEN)'; + arm_efi_ldflags = '-Wl,-r,-d'; arm_efi_stripflags = '--strip-unneeded -K start -R .note -R .comment -R .note.gnu.gold-version'; @@ -90,6 +95,8 @@ kernel = { i386_pc_startup = kern/i386/pc/startup.S; i386_efi_startup = kern/i386/efi/startup.S; x86_64_efi_startup = kern/x86_64/efi/startup.S; + i386_xen_startup = kern/i386/xen/startup.S; + x86_64_xen_startup = kern/x86_64/xen/startup.S; i386_qemu_startup = kern/i386/qemu/startup.S; i386_ieee1275_startup = kern/i386/ieee1275/startup.S; i386_coreboot_startup = kern/i386/coreboot/startup.S; @@ -146,6 +153,7 @@ kernel = { terminfoinkernel = lib/arg.c; i386 = kern/i386/dl.c; + i386_xen = kern/i386/dl.c; i386_coreboot = kern/i386/coreboot/init.c; i386_multiboot = kern/i386/coreboot/init.c; @@ -164,11 +172,20 @@ kernel = { i386_efi = kern/i386/efi/init.c; i386_efi = bus/pci.c; - x86_64_efi = kern/x86_64/dl.c; + x86_64 = kern/x86_64/dl.c; + x86_64_xen = kern/x86_64/dl.c; x86_64_efi = kern/x86_64/efi/callwrap.S; x86_64_efi = kern/i386/efi/init.c; x86_64_efi = bus/pci.c; + xen = kern/i386/tsc.c; + x86_64_xen = kern/x86_64/xen/hypercall.S; + i386_xen = kern/i386/xen/hypercall.S; + xen = kern/xen/init.c; + xen = term/xen/console.c; + xen = disk/xen/xendisk.c; + xen = commands/boot.c; + ia64_efi = kern/ia64/efi/startup.S; ia64_efi = kern/ia64/efi/init.c; ia64_efi = kern/ia64/dl.c; @@ -578,6 +595,14 @@ module = { enable = mips_arc; }; +module = { + name = lsxen; + common = commands/xen/lsxen.c; + cppflags = '$(CPPFLAGS_XEN)'; + + enable = xen; +}; + module = { name = check_nt_hiberfil; common = commands/i386/nthibr.c; @@ -745,8 +770,10 @@ module = { module = { name = cpuid; - x86 = commands/i386/cpuid.c; + common = commands/i386/cpuid.c; enable = x86; + enable = i386_xen; + enable = x86_64_xen; }; module = { @@ -803,6 +830,8 @@ module = { i386_multiboot = lib/i386/halt.c; i386_coreboot = lib/i386/halt.c; i386_qemu = lib/i386/halt.c; + xen = lib/xen/halt.c; + xen_cppflags = '$(CPPFLAGS_XEN)'; efi = lib/efi/halt.c; ieee1275 = lib/ieee1275/halt.c; emu = lib/emu/halt.c; @@ -821,6 +850,8 @@ module = { mips_arc = lib/mips/arc/reboot.c; mips_loongson = lib/mips/loongson/reboot.c; mips_qemu_mips = lib/mips/qemu_mips/reboot.c; + xen = lib/xen/reboot.c; + xen_cppflags = '$(CPPFLAGS_XEN)'; uboot = lib/uboot/reboot.c; common = commands/reboot.c; }; @@ -1444,6 +1475,11 @@ module = { common = io/gzio.c; }; +module = { + name = offsetio; + common = io/offset.c; +}; + module = { name = bufio; common = io/bufio.c; @@ -1477,13 +1513,21 @@ module = { x86 = lib/i386/relocator64.S; i386 = lib/i386/relocator_asm.S; x86_64 = lib/x86_64/relocator_asm.S; + i386_xen = lib/i386/relocator_asm.S; + x86_64_xen = lib/x86_64/relocator_asm.S; x86 = lib/i386/relocator.c; + x86 = lib/i386/relocator_common_c.c; ieee1275 = lib/ieee1275/relocator.c; efi = lib/efi/relocator.c; mips = lib/mips/relocator_asm.S; mips = lib/mips/relocator.c; powerpc = lib/powerpc/relocator_asm.S; powerpc = lib/powerpc/relocator.c; + xen = lib/xen/relocator.c; + i386_xen = lib/i386/xen/relocator.S; + x86_64_xen = lib/x86_64/xen/relocator.S; + xen = lib/i386/relocator_common_c.c; + xen_cppflags = '$(CPPFLAGS_XEN)'; extra_dist = lib/i386/relocator_common.S; extra_dist = kern/powerpc/cache_flush.S; @@ -1491,6 +1535,7 @@ module = { enable = mips; enable = powerpc; enable = x86; + enable = xen; }; module = { @@ -1502,6 +1547,8 @@ module = { powerpc_ieee1275 = lib/ieee1275/datetime.c; sparc64_ieee1275 = lib/ieee1275/cmos.c; powerpc_ieee1275 = lib/ieee1275/cmos.c; + xen = lib/xen/datetime.c; + xen_cppflags = '$(CPPFLAGS_XEN)'; mips_arc = lib/arc/datetime.c; enable = noemu; @@ -1594,6 +1641,12 @@ module = { module = { name = linux; x86 = loader/i386/linux.c; + xen = loader/i386/xen.c; + xen = loader/i386/xen_file.c; + xen = loader/i386/xen_file32.c; + xen = loader/i386/xen_file64.c; + extra_dist = loader/i386/xen_fileXX.c; + xen_cppflags = '$(CPPFLAGS_XEN)'; i386_pc = lib/i386/pc/vesa_modes_table.c; mips = loader/mips/linux.c; powerpc_ieee1275 = loader/powerpc/ieee1275/linux.c; @@ -2015,11 +2068,13 @@ module = { emu = lib/i386/pc/vesa_modes_table.c; i386_efi = lib/i386/pc/vesa_modes_table.c; x86_64_efi = lib/i386/pc/vesa_modes_table.c; + xen = lib/i386/pc/vesa_modes_table.c; enable = i386_pc; enable = i386_efi; enable = x86_64_efi; enable = emu; + enable = xen; }; module = { @@ -2053,8 +2108,12 @@ module = { module = { name = backtrace; x86 = lib/i386/backtrace.c; + i386_xen = lib/i386/backtrace.c; + x86_64_xen = lib/i386/backtrace.c; common = lib/backtrace.c; enable = x86; + enable = i386_xen; + enable = x86_64_xen; }; module = { diff --git a/grub-core/commands/boot.c b/grub-core/commands/boot.c index e0148c7f6..bbca81e94 100644 --- a/grub-core/commands/boot.c +++ b/grub-core/commands/boot.c @@ -146,8 +146,7 @@ grub_loader_boot (void) return grub_error (GRUB_ERR_NO_KERNEL, N_("you need to load the kernel first")); - if (grub_loader_flags & GRUB_LOADER_FLAG_NORETURN) - grub_machine_fini (); + grub_machine_fini (grub_loader_flags); for (cur = preboots_head; cur; cur = cur->next) { diff --git a/grub-core/commands/nativedisk.c b/grub-core/commands/nativedisk.c index 714aa11bd..33b6b99ea 100644 --- a/grub-core/commands/nativedisk.c +++ b/grub-core/commands/nativedisk.c @@ -75,6 +75,7 @@ get_uuid (const char *name, char **uuid, int getnative) /* Native disks. */ case GRUB_DISK_DEVICE_ATA_ID: case GRUB_DISK_DEVICE_SCSI_ID: + case GRUB_DISK_DEVICE_XEN: if (getnative) break; diff --git a/grub-core/commands/xen/lsxen.c b/grub-core/commands/xen/lsxen.c new file mode 100644 index 000000000..841549587 --- /dev/null +++ b/grub-core/commands/xen/lsxen.c @@ -0,0 +1,90 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2011 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 . + */ + +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static int +hook (const char *dir, void *hook_data __attribute__ ((unused))) +{ + grub_printf ("%s\n", dir); + return 0; +} + +static grub_err_t +grub_cmd_lsxen (grub_command_t cmd __attribute__ ((unused)), + int argc, char **args) +{ + char *dir; + grub_err_t err; + char *buf; + + if (argc >= 1) + return grub_xenstore_dir (args[0], hook, NULL); + + buf = grub_xenstore_get_file ("domid", NULL); + if (!buf) + return grub_errno; + dir = grub_xasprintf ("/local/domain/%s", buf); + grub_free (buf); + err = grub_xenstore_dir (dir, hook, NULL); + grub_free (dir); + return err; +} + +static grub_err_t +grub_cmd_catxen (grub_command_t cmd __attribute__ ((unused)), + int argc, char **args) +{ + const char *dir = "domid"; + char *buf; + + if (argc >= 1) + dir = args[0]; + + buf = grub_xenstore_get_file (dir, NULL); + if (!buf) + return grub_errno; + grub_xputs (buf); + grub_xputs ("\n"); + grub_free (buf); + return GRUB_ERR_NONE; + +} + +static grub_command_t cmd_ls, cmd_cat; + +GRUB_MOD_INIT (lsxen) +{ + cmd_ls = grub_register_command ("xen_ls", grub_cmd_lsxen, "[DIR]", + N_("List XEN storage.")); + cmd_cat = grub_register_command ("xen_cat", grub_cmd_catxen, "[DIR]", + N_("List XEN storage.")); +} + +GRUB_MOD_FINI (lsxen) +{ + grub_unregister_command (cmd_ls); + grub_unregister_command (cmd_cat); +} diff --git a/grub-core/disk/xen/xendisk.c b/grub-core/disk/xen/xendisk.c new file mode 100644 index 000000000..c44984807 --- /dev/null +++ b/grub-core/disk/xen/xendisk.c @@ -0,0 +1,453 @@ +/* + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct virtdisk +{ + int handle; + char *fullname; + char *backend_dir; + char *frontend_dir; + struct blkif_sring *shared_page; + struct blkif_front_ring ring; + grub_xen_grant_t grant; + grub_xen_evtchn_t evtchn; + void *dma_page; + grub_xen_grant_t dma_grant; +}; + +#define xen_wmb() mb() +#define xen_mb() mb() + +static struct virtdisk *virtdisks; +static grub_size_t vdiskcnt; + +static int +grub_virtdisk_iterate (grub_disk_dev_iterate_hook_t hook, void *hook_data, + grub_disk_pull_t pull) +{ + grub_size_t i; + + if (pull != GRUB_DISK_PULL_NONE) + return 0; + + for (i = 0; i < vdiskcnt; i++) + if (hook (virtdisks[i].fullname, hook_data)) + return 1; + return 0; +} + +static grub_err_t +grub_virtdisk_open (const char *name, grub_disk_t disk) +{ + grub_size_t i; + grub_uint32_t secsize; + char fdir[200]; + char *buf; + + for (i = 0; i < vdiskcnt; i++) + if (grub_strcmp (name, virtdisks[i].fullname) == 0) + break; + if (i == vdiskcnt) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a virtdisk"); + disk->data = &virtdisks[i]; + disk->id = i; + + grub_snprintf (fdir, sizeof (fdir), "%s/sectors", virtdisks[i].backend_dir); + buf = grub_xenstore_get_file (fdir, NULL); + if (!buf) + return grub_errno; + disk->total_sectors = grub_strtoull (buf, 0, 10); + if (grub_errno) + return grub_errno; + + grub_snprintf (fdir, sizeof (fdir), "%s/sector-size", + virtdisks[i].backend_dir); + buf = grub_xenstore_get_file (fdir, NULL); + if (!buf) + return grub_errno; + secsize = grub_strtoull (buf, 0, 10); + if (grub_errno) + return grub_errno; + + if ((secsize & (secsize - 1)) || !secsize || secsize < 512 + || secsize > GRUB_XEN_PAGE_SIZE) + return grub_error (GRUB_ERR_IO, "unsupported sector size %d", secsize); + + for (disk->log_sector_size = 0; + (1U << disk->log_sector_size) < secsize; disk->log_sector_size++); + + disk->total_sectors >>= disk->log_sector_size - 9; + + return GRUB_ERR_NONE; +} + +static void +grub_virtdisk_close (grub_disk_t disk __attribute__ ((unused))) +{ +} + +static grub_err_t +grub_virtdisk_read (grub_disk_t disk, grub_disk_addr_t sector, + grub_size_t size, char *buf) +{ + struct virtdisk *data = disk->data; + + while (size) + { + grub_size_t cur; + struct blkif_request *req; + struct blkif_response *resp; + int sta = 0; + struct evtchn_send send; + cur = size; + if (cur > (unsigned) (GRUB_XEN_PAGE_SIZE >> disk->log_sector_size)) + cur = GRUB_XEN_PAGE_SIZE >> disk->log_sector_size; + while (RING_FULL (&data->ring)) + grub_xen_sched_op (SCHEDOP_yield, 0); + req = RING_GET_REQUEST (&data->ring, data->ring.req_prod_pvt); + req->operation = BLKIF_OP_READ; + req->nr_segments = 1; + req->handle = data->handle; + req->id = 0; + req->sector_number = sector << (disk->log_sector_size - 9); + req->seg[0].gref = data->dma_grant; + req->seg[0].first_sect = 0; + req->seg[0].last_sect = (cur << (disk->log_sector_size - 9)) - 1; + data->ring.req_prod_pvt++; + RING_PUSH_REQUESTS (&data->ring); + mb (); + send.port = data->evtchn; + grub_xen_event_channel_op (EVTCHNOP_send, &send); + + while (!RING_HAS_UNCONSUMED_RESPONSES (&data->ring)) + { + grub_xen_sched_op (SCHEDOP_yield, 0); + mb (); + } + while (1) + { + int wtd; + RING_FINAL_CHECK_FOR_RESPONSES (&data->ring, wtd); + if (!wtd) + break; + resp = RING_GET_RESPONSE (&data->ring, data->ring.rsp_cons); + data->ring.rsp_cons++; + if (resp->status) + sta = resp->status; + } + if (sta) + return grub_error (GRUB_ERR_IO, "read failed"); + grub_memcpy (buf, data->dma_page, cur << disk->log_sector_size); + size -= cur; + sector += cur; + buf += cur << disk->log_sector_size; + } + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_virtdisk_write (grub_disk_t disk, grub_disk_addr_t sector, + grub_size_t size, const char *buf) +{ + struct virtdisk *data = disk->data; + + while (size) + { + grub_size_t cur; + struct blkif_request *req; + struct blkif_response *resp; + int sta = 0; + struct evtchn_send send; + cur = size; + if (cur > (unsigned) (GRUB_XEN_PAGE_SIZE >> disk->log_sector_size)) + cur = GRUB_XEN_PAGE_SIZE >> disk->log_sector_size; + + grub_memcpy (data->dma_page, buf, cur << disk->log_sector_size); + + while (RING_FULL (&data->ring)) + grub_xen_sched_op (SCHEDOP_yield, 0); + req = RING_GET_REQUEST (&data->ring, data->ring.req_prod_pvt); + req->operation = BLKIF_OP_WRITE; + req->nr_segments = 1; + req->handle = data->handle; + req->id = 0; + req->sector_number = sector << (disk->log_sector_size - 9); + req->seg[0].gref = data->dma_grant; + req->seg[0].first_sect = 0; + req->seg[0].last_sect = (cur << (disk->log_sector_size - 9)) - 1; + data->ring.req_prod_pvt++; + RING_PUSH_REQUESTS (&data->ring); + mb (); + send.port = data->evtchn; + grub_xen_event_channel_op (EVTCHNOP_send, &send); + + while (!RING_HAS_UNCONSUMED_RESPONSES (&data->ring)) + { + grub_xen_sched_op (SCHEDOP_yield, 0); + mb (); + } + while (1) + { + int wtd; + RING_FINAL_CHECK_FOR_RESPONSES (&data->ring, wtd); + if (!wtd) + break; + resp = RING_GET_RESPONSE (&data->ring, data->ring.rsp_cons); + data->ring.rsp_cons++; + if (resp->status) + sta = resp->status; + } + if (sta) + return grub_error (GRUB_ERR_IO, "write failed"); + size -= cur; + sector += cur; + buf += cur << disk->log_sector_size; + } + return GRUB_ERR_NONE; +} + +static struct grub_disk_dev grub_virtdisk_dev = { + .name = "xen", + .id = GRUB_DISK_DEVICE_XEN, + .iterate = grub_virtdisk_iterate, + .open = grub_virtdisk_open, + .close = grub_virtdisk_close, + .read = grub_virtdisk_read, + .write = grub_virtdisk_write, + .next = 0 +}; + +static int +count (const char *dir __attribute__ ((unused)), void *data) +{ + grub_size_t *ctr = data; + (*ctr)++; + + return 0; +} + +static int +fill (const char *dir, void *data) +{ + grub_size_t *ctr = data; + domid_t dom; + /* "dir" is just a number, at most 19 characters. */ + char fdir[200]; + char num[20]; + grub_err_t err; + void *buf; + struct evtchn_alloc_unbound alloc_unbound; + + /* Shouldn't happen unles some hotplug happened. */ + if (vdiskcnt >= *ctr) + return 1; + virtdisks[vdiskcnt].handle = grub_strtoul (dir, 0, 10); + if (grub_errno) + { + grub_errno = 0; + return 0; + } + virtdisks[vdiskcnt].fullname = 0; + virtdisks[vdiskcnt].backend_dir = 0; + + grub_snprintf (fdir, sizeof (fdir), "device/vbd/%s/backend", dir); + virtdisks[vdiskcnt].backend_dir = grub_xenstore_get_file (fdir, NULL); + if (!virtdisks[vdiskcnt].backend_dir) + goto out_fail_1; + + grub_snprintf (fdir, sizeof (fdir), "%s/dev", + virtdisks[vdiskcnt].backend_dir); + buf = grub_xenstore_get_file (fdir, NULL); + if (!buf) + { + grub_errno = 0; + virtdisks[vdiskcnt].fullname = grub_xasprintf ("xenid/%s", dir); + } + else + { + virtdisks[vdiskcnt].fullname = grub_xasprintf ("xen/%s", (char *) buf); + grub_free (buf); + } + if (!virtdisks[vdiskcnt].fullname) + goto out_fail_1; + + grub_snprintf (fdir, sizeof (fdir), "device/vbd/%s/backend-id", dir); + buf = grub_xenstore_get_file (fdir, NULL); + if (!buf) + goto out_fail_1; + + dom = grub_strtoul (buf, 0, 10); + grub_free (buf); + if (grub_errno) + goto out_fail_1; + + virtdisks[vdiskcnt].shared_page = + grub_xen_alloc_shared_page (dom, &virtdisks[vdiskcnt].grant); + if (!virtdisks[vdiskcnt].shared_page) + goto out_fail_1; + + virtdisks[vdiskcnt].dma_page = + grub_xen_alloc_shared_page (dom, &virtdisks[vdiskcnt].dma_grant); + if (!virtdisks[vdiskcnt].dma_page) + goto out_fail_2; + + alloc_unbound.dom = DOMID_SELF; + alloc_unbound.remote_dom = dom; + + grub_xen_event_channel_op (EVTCHNOP_alloc_unbound, &alloc_unbound); + virtdisks[vdiskcnt].evtchn = alloc_unbound.port; + + SHARED_RING_INIT (virtdisks[vdiskcnt].shared_page); + FRONT_RING_INIT (&virtdisks[vdiskcnt].ring, virtdisks[vdiskcnt].shared_page, + GRUB_XEN_PAGE_SIZE); + + grub_snprintf (fdir, sizeof (fdir), "device/vbd/%s/ring-ref", dir); + grub_snprintf (num, sizeof (num), "%u", virtdisks[vdiskcnt].grant); + err = grub_xenstore_write_file (fdir, num, grub_strlen (num)); + if (err) + goto out_fail_3; + + grub_snprintf (fdir, sizeof (fdir), "device/vbd/%s/event-channel", dir); + grub_snprintf (num, sizeof (num), "%u", virtdisks[vdiskcnt].evtchn); + err = grub_xenstore_write_file (fdir, num, grub_strlen (num)); + if (err) + goto out_fail_3; + + grub_snprintf (fdir, sizeof (fdir), "device/vbd/%s/protocol", dir); + err = grub_xenstore_write_file (fdir, XEN_IO_PROTO_ABI_NATIVE, + grub_strlen (XEN_IO_PROTO_ABI_NATIVE)); + if (err) + goto out_fail_3; + + struct gnttab_dump_table dt; + dt.dom = DOMID_SELF; + grub_xen_grant_table_op (GNTTABOP_dump_table, (void *) &dt, 1); + + grub_snprintf (fdir, sizeof (fdir), "device/vbd/%s/state", dir); + err = grub_xenstore_write_file (fdir, "3", 1); + if (err) + goto out_fail_3; + + while (1) + { + grub_snprintf (fdir, sizeof (fdir), "%s/state", + virtdisks[vdiskcnt].backend_dir); + buf = grub_xenstore_get_file (fdir, NULL); + if (!buf) + goto out_fail_3; + if (grub_strcmp (buf, "2") != 0) + break; + grub_free (buf); + grub_xen_sched_op (SCHEDOP_yield, 0); + } + grub_dprintf ("xen", "state=%s\n", (char *) buf); + grub_free (buf); + + grub_snprintf (fdir, sizeof (fdir), "device/vbd/%s", dir); + + virtdisks[vdiskcnt].frontend_dir = grub_strdup (fdir); + + vdiskcnt++; + return 0; + +out_fail_3: + grub_xen_free_shared_page (virtdisks[vdiskcnt].dma_page); +out_fail_2: + grub_xen_free_shared_page (virtdisks[vdiskcnt].shared_page); +out_fail_1: + grub_free (virtdisks[vdiskcnt].backend_dir); + grub_free (virtdisks[vdiskcnt].fullname); + + grub_errno = 0; + return 0; +} + +void +grub_xendisk_init (void) +{ + grub_size_t ctr = 0; + if (grub_xenstore_dir ("device/vbd", count, &ctr)) + grub_errno = 0; + + if (!ctr) + return; + + virtdisks = grub_malloc (ctr * sizeof (virtdisks[0])); + if (!virtdisks) + return; + if (grub_xenstore_dir ("device/vbd", fill, &ctr)) + grub_errno = 0; + + grub_disk_dev_register (&grub_virtdisk_dev); +} + +void +grub_xendisk_fini (void) +{ + char fdir[200]; + unsigned i; + + for (i = 0; i < vdiskcnt; i++) + { + char *buf; + struct evtchn_close close_op = {.port = virtdisks[i].evtchn }; + + grub_snprintf (fdir, sizeof (fdir), "%s/state", + virtdisks[i].frontend_dir); + grub_xenstore_write_file (fdir, "6", 1); + + while (1) + { + grub_snprintf (fdir, sizeof (fdir), "%s/state", + virtdisks[i].backend_dir); + buf = grub_xenstore_get_file (fdir, NULL); + grub_dprintf ("xen", "state=%s\n", (char *) buf); + + if (!buf || grub_strcmp (buf, "6") == 0) + break; + grub_free (buf); + grub_xen_sched_op (SCHEDOP_yield, 0); + } + grub_free (buf); + + grub_snprintf (fdir, sizeof (fdir), "%s/ring-ref", + virtdisks[i].frontend_dir); + grub_xenstore_write_file (fdir, NULL, 0); + + grub_snprintf (fdir, sizeof (fdir), "%s/event-channel", + virtdisks[i].frontend_dir); + grub_xenstore_write_file (fdir, NULL, 0); + + grub_xen_free_shared_page (virtdisks[i].dma_page); + grub_xen_free_shared_page (virtdisks[i].shared_page); + + grub_xen_event_channel_op (EVTCHNOP_close, &close_op); + } +} diff --git a/grub-core/fs/cbfs.c b/grub-core/fs/cbfs.c index 0530c9b8a..93a38003e 100644 --- a/grub-core/fs/cbfs.c +++ b/grub-core/fs/cbfs.c @@ -267,7 +267,8 @@ grub_cbfs_close (grub_file_t file) return grub_errno; } -#if (defined (__i386__) || defined (__x86_64__)) && !defined (GRUB_UTIL) && !defined (GRUB_MACHINE_EMU) +#if (defined (__i386__) || defined (__x86_64__)) && !defined (GRUB_UTIL) \ + && !defined (GRUB_MACHINE_EMU) && !defined (GRUB_MACHINE_XEN) static char *cbfsdisk_addr; static grub_off_t cbfsdisk_size = 0; @@ -375,7 +376,7 @@ static struct grub_fs grub_cbfs_fs = { GRUB_MOD_INIT (cbfs) { -#if (defined (__i386__) || defined (__x86_64__)) && !defined (GRUB_UTIL) && !defined (GRUB_MACHINE_EMU) +#if (defined (__i386__) || defined (__x86_64__)) && !defined (GRUB_UTIL) && !defined (GRUB_MACHINE_EMU) && !defined (GRUB_MACHINE_XEN) init_cbfsdisk (); #endif grub_fs_register (&grub_cbfs_fs); @@ -384,7 +385,7 @@ GRUB_MOD_INIT (cbfs) GRUB_MOD_FINI (cbfs) { grub_fs_unregister (&grub_cbfs_fs); -#if (defined (__i386__) || defined (__x86_64__)) && !defined (GRUB_UTIL) && !defined (GRUB_MACHINE_EMU) +#if (defined (__i386__) || defined (__x86_64__)) && !defined (GRUB_UTIL) && !defined (GRUB_MACHINE_EMU) && !defined (GRUB_MACHINE_XEN) fini_cbfsdisk (); #endif } diff --git a/grub-core/io/offset.c b/grub-core/io/offset.c new file mode 100644 index 000000000..ebed0ebe6 --- /dev/null +++ b/grub-core/io/offset.c @@ -0,0 +1,111 @@ +/* + * 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 . + */ + +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +struct grub_offset_file +{ + grub_file_t parent; + grub_off_t off; +}; + +static grub_ssize_t +grub_offset_read (grub_file_t file, char *buf, grub_size_t len) +{ + struct grub_offset_file *data = file->data; + if (grub_file_seek (data->parent, data->off + file->offset) == (grub_off_t) -1) + return -1; + return grub_file_read (data->parent, buf, len); +} + +static grub_err_t +grub_offset_close (grub_file_t file) +{ + struct grub_offset_file *data = file->data; + + if (data->parent) + grub_file_close (data->parent); + + /* No need to close the same device twice. */ + file->device = 0; + + return 0; +} + +static struct grub_fs grub_offset_fs = { + .name = "offset", + .dir = 0, + .open = 0, + .read = grub_offset_read, + .close = grub_offset_close, + .label = 0, + .next = 0 +}; + +void +grub_file_offset_close (grub_file_t file) +{ + struct grub_offset_file *off_data = file->data; + off_data->parent = NULL; + grub_file_close (file); +} + +grub_file_t +grub_file_offset_open (grub_file_t parent, grub_off_t start, grub_off_t size) +{ + struct grub_offset_file *off_data; + grub_file_t off_file, last_off_file; + grub_file_filter_id_t filter; + + off_file = grub_zalloc (sizeof (*off_file)); + off_data = grub_zalloc (sizeof (*off_data)); + if (!off_file || !off_data) + { + grub_free (off_file); + grub_free (off_data); + return 0; + } + + off_data->off = start; + off_data->parent = parent; + + off_file->device = parent->device; + off_file->data = off_data; + off_file->fs = &grub_offset_fs; + off_file->size = size; + + last_off_file = NULL; + for (filter = GRUB_FILE_FILTER_COMPRESSION_FIRST; + off_file && filter <= GRUB_FILE_FILTER_COMPRESSION_LAST; filter++) + if (grub_file_filters_enabled[filter]) + { + last_off_file = off_file; + off_file = grub_file_filters_enabled[filter] (off_file, parent->name); + } + + if (!off_file) + { + off_data->parent = NULL; + grub_file_close (last_off_file); + return 0; + } + return off_file; +} diff --git a/grub-core/kern/emu/main.c b/grub-core/kern/emu/main.c index 87ae256f9..8696e84b9 100644 --- a/grub-core/kern/emu/main.c +++ b/grub-core/kern/emu/main.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #include "progname.h" @@ -75,9 +76,10 @@ grub_machine_get_bootlocation (char **device, char **path) } void -grub_machine_fini (void) +grub_machine_fini (int flags) { - grub_console_fini (); + if (flags & GRUB_LOADER_FLAG_NORETURN) + grub_console_fini (); } diff --git a/grub-core/kern/i386/coreboot/init.c b/grub-core/kern/i386/coreboot/init.c index 777d7489c..6b150b47d 100644 --- a/grub-core/kern/i386/coreboot/init.c +++ b/grub-core/kern/i386/coreboot/init.c @@ -130,8 +130,9 @@ grub_machine_get_bootlocation (char **device __attribute__ ((unused)), } void -grub_machine_fini (void) +grub_machine_fini (int flags) { - grub_vga_text_fini (); + if (flags & GRUB_LOADER_FLAG_NORETURN) + grub_vga_text_fini (); grub_stop_floppy (); } diff --git a/grub-core/kern/i386/efi/init.c b/grub-core/kern/i386/efi/init.c index 6bd8f3e87..7616e629d 100644 --- a/grub-core/kern/i386/efi/init.c +++ b/grub-core/kern/i386/efi/init.c @@ -35,7 +35,8 @@ grub_machine_init (void) } void -grub_machine_fini (void) +grub_machine_fini (int flags) { - grub_efi_fini (); + if (flags & GRUB_LOADER_FLAG_NORETURN) + grub_efi_fini (); } diff --git a/grub-core/kern/i386/pc/init.c b/grub-core/kern/i386/pc/init.c index 5b685040a..2319adf12 100644 --- a/grub-core/kern/i386/pc/init.c +++ b/grub-core/kern/i386/pc/init.c @@ -239,8 +239,9 @@ grub_machine_init (void) } void -grub_machine_fini (void) +grub_machine_fini (int flags) { - grub_console_fini (); + if (flags & GRUB_LOADER_FLAG_NORETURN) + grub_console_fini (); grub_stop_floppy (); } diff --git a/grub-core/kern/i386/qemu/init.c b/grub-core/kern/i386/qemu/init.c index cad6c40a2..e05ccee96 100644 --- a/grub-core/kern/i386/qemu/init.c +++ b/grub-core/kern/i386/qemu/init.c @@ -277,8 +277,9 @@ grub_machine_get_bootlocation (char **device __attribute__ ((unused)), } void -grub_machine_fini (void) +grub_machine_fini (int flags) { - grub_vga_text_fini (); + if (flags & GRUB_LOADER_FLAG_NORETURN) + grub_vga_text_fini (); grub_stop_floppy (); } diff --git a/grub-core/kern/i386/tsc.c b/grub-core/kern/i386/tsc.c index 608136b3d..3a4cae601 100644 --- a/grub-core/kern/i386/tsc.c +++ b/grub-core/kern/i386/tsc.c @@ -25,7 +25,11 @@ #include #include #include +#ifdef GRUB_MACHINE_XEN +#include +#else #include +#endif #include /* This defines the value TSC had at the epoch (that is, when we calibrated it). */ @@ -65,6 +69,8 @@ grub_cpu_is_tsc_supported (void) return (d & (1 << 4)) != 0; } +#ifndef GRUB_MACHINE_XEN + static void grub_pit_wait (grub_uint16_t tics) { @@ -92,6 +98,7 @@ grub_pit_wait (grub_uint16_t tics) & ~ (GRUB_PIT_SPK_DATA | GRUB_PIT_SPK_TMR2), GRUB_PIT_SPEAKER_PORT); } +#endif static grub_uint64_t grub_tsc_get_time_ms (void) @@ -103,6 +110,7 @@ grub_tsc_get_time_ms (void) return ((al * grub_tsc_rate) >> 32) + ah * grub_tsc_rate; } +#ifndef GRUB_MACHINE_XEN /* Calibrate the TSC based on the RTC. */ static void calibrate_tsc (void) @@ -116,10 +124,22 @@ calibrate_tsc (void) grub_tsc_rate = grub_divmod64 ((55ULL << 32), end_tsc - tsc_boot_time, 0); } +#endif void grub_tsc_init (void) { +#ifdef GRUB_MACHINE_XEN + grub_uint64_t t; + tsc_boot_time = grub_get_tsc (); + t = grub_xen_shared_info->vcpu_info[0].time.tsc_to_system_mul; + if (grub_xen_shared_info->vcpu_info[0].time.tsc_shift > 0) + t <<= grub_xen_shared_info->vcpu_info[0].time.tsc_shift; + else + t >>= -grub_xen_shared_info->vcpu_info[0].time.tsc_shift; + grub_tsc_rate = grub_divmod64 (t, 1000000, 0); + grub_install_get_time_ms (grub_tsc_get_time_ms); +#else if (grub_cpu_is_tsc_supported ()) { calibrate_tsc (); @@ -133,4 +153,5 @@ grub_tsc_init (void) grub_fatal ("no TSC found"); #endif } +#endif } diff --git a/grub-core/kern/i386/xen/hypercall.S b/grub-core/kern/i386/xen/hypercall.S new file mode 100644 index 000000000..aa47cac2c --- /dev/null +++ b/grub-core/kern/i386/xen/hypercall.S @@ -0,0 +1,43 @@ +/* hypercall.S - wrappers for Xen hypercalls */ +/* + * 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 . + */ + +#include +#include + +FUNCTION(grub_xen_hypercall) + pushl %ebp + movl %esp, %ebp + pushl %esi + pushl %edi + pushl %ebx + + /* call number already in %eax. */ + /* %edx -> %ebx*/ + /* %ecx -> %ecx*/ + movl %edx, %ebx + movl 8(%ebp), %edx + movl 12(%ebp), %esi + movl 16(%ebp), %edi + movl 20(%ebp), %ebp + int $0x82 + popl %ebx + popl %edi + popl %esi + popl %ebp + retl $16 diff --git a/grub-core/kern/i386/xen/startup.S b/grub-core/kern/i386/xen/startup.S new file mode 100644 index 000000000..fbe8300a7 --- /dev/null +++ b/grub-core/kern/i386/xen/startup.S @@ -0,0 +1,38 @@ +/* startup.S - bootstrap GRUB itself */ +/* + * 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 . + */ + +#include +#include + + .file "startup.S" + .text + .globl start, _start + .code32 + +start: +_start: + leal LOCAL(stack_end), %esp + movl %esi, EXT_C(grub_xen_start_page_addr) + + call EXT_C(grub_main) + /* Doesn't return. */ + + .bss + .space (1 << 22) +LOCAL(stack_end): diff --git a/grub-core/kern/ia64/efi/init.c b/grub-core/kern/ia64/efi/init.c index 7fa6fbf71..c514b0a1f 100644 --- a/grub-core/kern/ia64/efi/init.c +++ b/grub-core/kern/ia64/efi/init.c @@ -68,9 +68,10 @@ grub_machine_init (void) } void -grub_machine_fini (void) +grub_machine_fini (int flags) { - grub_efi_fini (); + if (flags & GRUB_LOADER_FLAG_NORETURN) + grub_efi_fini (); } void diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c index ce8eadbd3..acf73703c 100644 --- a/grub-core/kern/ieee1275/init.c +++ b/grub-core/kern/ieee1275/init.c @@ -298,10 +298,13 @@ grub_machine_init (void) } void -grub_machine_fini (void) +grub_machine_fini (int flags) { - grub_ofdisk_fini (); - grub_console_fini (); + if (flags & GRUB_LOADER_FLAG_NORETURN) + { + grub_ofdisk_fini (); + grub_console_fini (); + } } grub_uint64_t diff --git a/grub-core/kern/mips/arc/init.c b/grub-core/kern/mips/arc/init.c index ddee814c5..f12026e95 100644 --- a/grub-core/kern/mips/arc/init.c +++ b/grub-core/kern/mips/arc/init.c @@ -259,7 +259,7 @@ grub_machine_init (void) } void -grub_machine_fini (void) +grub_machine_fini (int flags __attribute__ ((unused))) { } diff --git a/grub-core/kern/mips/loongson/init.c b/grub-core/kern/mips/loongson/init.c index 3ed1ded61..d2f579588 100644 --- a/grub-core/kern/mips/loongson/init.c +++ b/grub-core/kern/mips/loongson/init.c @@ -217,7 +217,7 @@ grub_machine_init (void) } void -grub_machine_fini (void) +grub_machine_fini (int flags __attribute__ ((unused))) { } diff --git a/grub-core/kern/mips/qemu_mips/init.c b/grub-core/kern/mips/qemu_mips/init.c index 6a5af2a93..2ea2eb4fd 100644 --- a/grub-core/kern/mips/qemu_mips/init.c +++ b/grub-core/kern/mips/qemu_mips/init.c @@ -70,7 +70,7 @@ grub_machine_init (void) } void -grub_machine_fini (void) +grub_machine_fini (int flags __attribute__ ((unused))) { } diff --git a/grub-core/kern/x86_64/xen/hypercall.S b/grub-core/kern/x86_64/xen/hypercall.S new file mode 100644 index 000000000..9b04db6a0 --- /dev/null +++ b/grub-core/kern/x86_64/xen/hypercall.S @@ -0,0 +1,53 @@ +/* hypercall.S - wrappers for Xen hypercalls */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2011 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 . + */ + +#include +#include + +FUNCTION(grub_xen_sched_op) + movq $__HYPERVISOR_sched_op, %rax + syscall + ret + +FUNCTION(grub_xen_event_channel_op) + movq $__HYPERVISOR_event_channel_op, %rax + syscall + ret + +FUNCTION(grub_xen_update_va_mapping) + movq $__HYPERVISOR_update_va_mapping, %rax + syscall + ret + +FUNCTION(grub_xen_mmuext_op) + movq %rcx, %r10 + movq $__HYPERVISOR_mmuext_op, %rax + syscall + ret + +FUNCTION(grub_xen_grant_table_op) + movq $__HYPERVISOR_grant_table_op, %rax + syscall + ret + +FUNCTION(grub_xen_mmu_update) + movq %rcx, %r10 + movq $__HYPERVISOR_mmu_update, %rax + syscall + ret diff --git a/grub-core/kern/x86_64/xen/startup.S b/grub-core/kern/x86_64/xen/startup.S new file mode 100644 index 000000000..7217ee57b --- /dev/null +++ b/grub-core/kern/x86_64/xen/startup.S @@ -0,0 +1,38 @@ +/* startup.S - bootstrap GRUB itself */ +/* + * 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 . + */ + +#include +#include + + .file "startup.S" + .text + .globl start, _start + .code64 + +start: +_start: + leaq LOCAL(stack_end), %rsp + movq %rsi, EXT_C(grub_xen_start_page_addr)(%rip) + + call EXT_C(grub_main) + /* Doesn't return. */ + + .bss + .space (1 << 22) +LOCAL(stack_end): diff --git a/grub-core/kern/xen/init.c b/grub-core/kern/xen/init.c new file mode 100644 index 000000000..3bfd99fc9 --- /dev/null +++ b/grub-core/kern/xen/init.c @@ -0,0 +1,571 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2011 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +grub_addr_t grub_modbase; +struct start_info *grub_xen_start_page_addr; +volatile struct xencons_interface *grub_xen_xcons; +volatile struct shared_info *grub_xen_shared_info; +volatile struct xenstore_domain_interface *grub_xen_xenstore; +volatile grant_entry_v2_t *grub_xen_grant_table; +static const grub_size_t total_grants = + GRUB_XEN_PAGE_SIZE / sizeof (grub_xen_grant_table[0]); +grub_size_t grub_xen_n_allocated_shared_pages; + +static grub_xen_mfn_t +grub_xen_ptr2mfn (void *ptr) +{ + grub_xen_mfn_t *mfn_list = + (grub_xen_mfn_t *) grub_xen_start_page_addr->mfn_list; + return mfn_list[(grub_addr_t) ptr >> GRUB_XEN_LOG_PAGE_SIZE]; +} + +void * +grub_xen_alloc_shared_page (domid_t dom, grub_xen_grant_t * grnum) +{ + void *ret; + grub_xen_mfn_t mfn; + volatile grant_entry_v2_t *entry; + + /* Avoid 0. */ + for (entry = grub_xen_grant_table; + entry < grub_xen_grant_table + total_grants; entry++) + if (!entry->hdr.flags) + break; + + if (entry == grub_xen_grant_table + total_grants) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of grant entries"); + return NULL; + } + ret = grub_memalign (GRUB_XEN_PAGE_SIZE, GRUB_XEN_PAGE_SIZE); + if (!ret) + return NULL; + mfn = grub_xen_ptr2mfn (ret); + entry->full_page.pad0 = 0; + entry->full_page.frame = mfn; + entry->full_page.hdr.domid = dom; + mb (); + entry->full_page.hdr.flags = GTF_permit_access; + mb (); + *grnum = entry - grub_xen_grant_table; + grub_xen_n_allocated_shared_pages++; + return ret; +} + +void +grub_xen_free_shared_page (void *ptr) +{ + grub_xen_mfn_t mfn; + volatile grant_entry_v2_t *entry; + + mfn = grub_xen_ptr2mfn (ptr); + for (entry = grub_xen_grant_table + 1; + entry < grub_xen_grant_table + total_grants; entry++) + if (entry->hdr.flags && entry->full_page.frame == mfn) + { + mb (); + entry->hdr.flags = 0; + mb (); + entry->full_page.frame = 0; + mb (); + } + grub_xen_n_allocated_shared_pages--; +} + +void +grub_machine_get_bootlocation (char **device __attribute__ ((unused)), + char **path __attribute__ ((unused))) +{ +} + +static grub_uint8_t window[GRUB_XEN_PAGE_SIZE] + __attribute__ ((aligned (GRUB_XEN_PAGE_SIZE))); + +#ifdef __x86_64__ +#define NUMBER_OF_LEVELS 4 +#else +#define NUMBER_OF_LEVELS 3 +#endif + +#define LOG_POINTERS_PER_PAGE 9 +#define POINTERS_PER_PAGE (1 << LOG_POINTERS_PER_PAGE) + +void +grub_xen_store_send (const void *buf_, grub_size_t len) +{ + const grub_uint8_t *buf = buf_; + struct evtchn_send send; + int event_sent = 0; + while (len) + { + grub_size_t avail, inbuf; + grub_size_t prod, cons; + mb (); + prod = grub_xen_xenstore->req_prod; + cons = grub_xen_xenstore->req_cons; + if (prod >= cons + sizeof (grub_xen_xenstore->req)) + { + if (!event_sent) + { + send.port = grub_xen_start_page_addr->store_evtchn; + grub_xen_event_channel_op (EVTCHNOP_send, &send); + event_sent = 1; + } + grub_xen_sched_op (SCHEDOP_yield, 0); + continue; + } + event_sent = 0; + avail = cons + sizeof (grub_xen_xenstore->req) - prod; + inbuf = (~prod & (sizeof (grub_xen_xenstore->req) - 1)) + 1; + if (avail > inbuf) + avail = inbuf; + if (avail > len) + avail = len; + grub_memcpy ((void *) &grub_xen_xenstore->req[prod & (sizeof (grub_xen_xenstore->req) - 1)], + buf, avail); + buf += avail; + len -= avail; + mb (); + grub_xen_xenstore->req_prod += avail; + mb (); + if (!event_sent) + { + send.port = grub_xen_start_page_addr->store_evtchn; + grub_xen_event_channel_op (EVTCHNOP_send, &send); + event_sent = 1; + } + grub_xen_sched_op (SCHEDOP_yield, 0); + } +} + +void +grub_xen_store_recv (void *buf_, grub_size_t len) +{ + grub_uint8_t *buf = buf_; + struct evtchn_send send; + int event_sent = 0; + while (len) + { + grub_size_t avail, inbuf; + grub_size_t prod, cons; + mb (); + prod = grub_xen_xenstore->rsp_prod; + cons = grub_xen_xenstore->rsp_cons; + if (prod <= cons) + { + if (!event_sent) + { + send.port = grub_xen_start_page_addr->store_evtchn; + grub_xen_event_channel_op (EVTCHNOP_send, &send); + event_sent = 1; + } + grub_xen_sched_op (SCHEDOP_yield, 0); + continue; + } + event_sent = 0; + avail = prod - cons; + inbuf = (~cons & (sizeof (grub_xen_xenstore->req) - 1)) + 1; + if (avail > inbuf) + avail = inbuf; + if (avail > len) + avail = len; + grub_memcpy (buf, + (void *) &grub_xen_xenstore->rsp[cons & (sizeof (grub_xen_xenstore->rsp) - 1)], + avail); + buf += avail; + len -= avail; + mb (); + grub_xen_xenstore->rsp_cons += avail; + mb (); + if (!event_sent) + { + send.port = grub_xen_start_page_addr->store_evtchn; + grub_xen_event_channel_op(EVTCHNOP_send, &send); + event_sent = 1; + } + grub_xen_sched_op(SCHEDOP_yield, 0); + } +} + +void * +grub_xenstore_get_file (const char *dir, grub_size_t *len) +{ + struct xsd_sockmsg msg; + char *buf; + grub_size_t dirlen = grub_strlen (dir) + 1; + + if (len) + *len = 0; + + grub_memset (&msg, 0, sizeof (msg)); + msg.type = XS_READ; + msg.len = dirlen; + grub_xen_store_send (&msg, sizeof (msg)); + grub_xen_store_send (dir, dirlen); + grub_xen_store_recv (&msg, sizeof (msg)); + buf = grub_malloc (msg.len + 1); + if (!buf) + return NULL; + grub_dprintf ("xen", "msg type = %d, len = %d\n", msg.type, msg.len); + grub_xen_store_recv (buf, msg.len); + buf[msg.len] = '\0'; + if (msg.type == XS_ERROR) + { + grub_error (GRUB_ERR_IO, "couldn't read xenstorage `%s': %s", dir, buf); + grub_free (buf); + return NULL; + } + if (len) + *len = msg.len; + return buf; +} + +grub_err_t +grub_xenstore_write_file (const char *dir, const void *buf, grub_size_t len) +{ + struct xsd_sockmsg msg; + grub_size_t dirlen = grub_strlen (dir) + 1; + char *resp; + + grub_memset (&msg, 0, sizeof (msg)); + msg.type = XS_WRITE; + msg.len = dirlen + len + 1; + grub_xen_store_send (&msg, sizeof (msg)); + grub_xen_store_send (dir, dirlen); + grub_xen_store_send (buf, len); + grub_xen_store_send ("", 1); + grub_xen_store_recv (&msg, sizeof (msg)); + resp = grub_malloc (msg.len + 1); + if (!resp) + return grub_errno; + grub_dprintf ("xen", "msg type = %d, len = %d\n", msg.type, msg.len); + grub_xen_store_recv (resp, msg.len); + resp[msg.len] = '\0'; + if (msg.type == XS_ERROR) + { + grub_dprintf ("xen", "error = %s\n", resp); + grub_error (GRUB_ERR_IO, "couldn't read xenstorage `%s': %s", + dir, resp); + grub_free (resp); + return grub_errno; + } + grub_free (resp); + return GRUB_ERR_NONE; +} + +/* FIXME: error handling. */ +grub_err_t +grub_xenstore_dir (const char *dir, + int (*hook) (const char *dir, void *hook_data), + void *hook_data) +{ + struct xsd_sockmsg msg; + char *buf; + char *ptr; + grub_size_t dirlen = grub_strlen (dir) + 1; + + grub_memset (&msg, 0, sizeof (msg)); + msg.type = XS_DIRECTORY; + msg.len = dirlen; + grub_xen_store_send (&msg, sizeof (msg)); + grub_xen_store_send (dir, dirlen); + grub_xen_store_recv (&msg, sizeof (msg)); + buf = grub_malloc (msg.len + 1); + if (!buf) + return grub_errno; + grub_dprintf ("xen", "msg type = %d, len = %d\n", msg.type, msg.len); + grub_xen_store_recv (buf, msg.len); + buf[msg.len] = '\0'; + if (msg.type == XS_ERROR) + { + grub_err_t err; + err = grub_error (GRUB_ERR_IO, "couldn't read xenstorage `%s': %s", + dir, buf); + grub_free (buf); + return err; + } + for (ptr = buf; ptr < buf + msg.len; ptr += grub_strlen (ptr) + 1) + if (hook (ptr, hook_data)) + break; + grub_free (buf); + return grub_errno; +} + +unsigned long gntframe = 0; + +#define MAX_N_UNUSABLE_PAGES 4 + +static int +grub_xen_is_page_usable (grub_xen_mfn_t mfn) +{ + if (mfn == grub_xen_start_page_addr->console.domU.mfn) + return 0; + if (mfn == grub_xen_start_page_addr->shared_info) + return 0; + if (mfn == grub_xen_start_page_addr->store_mfn) + return 0; + if (mfn == gntframe) + return 0; + return 1; +} + +static grub_uint64_t +page2offset (grub_uint64_t page) +{ + return page << 12; +} + +static void +map_all_pages (void) +{ + grub_uint64_t total_pages = grub_xen_start_page_addr->nr_pages; + grub_uint64_t i, j; + grub_xen_mfn_t *mfn_list = + (grub_xen_mfn_t *) grub_xen_start_page_addr->mfn_list; + grub_uint64_t *pg = (grub_uint64_t *) window; + grub_uint64_t oldpgstart, oldpgend; + struct gnttab_setup_table gnttab_setup; + struct gnttab_set_version gnttab_setver; + grub_size_t n_unusable_pages = 0; + struct mmu_update m2p_updates[2 * MAX_N_UNUSABLE_PAGES]; + + grub_memset (&gnttab_setver, 0, sizeof (gnttab_setver)); + + gnttab_setver.version = 2; + grub_xen_grant_table_op (GNTTABOP_set_version, &gnttab_setver, 1); + + grub_memset (&gnttab_setup, 0, sizeof (gnttab_setup)); + gnttab_setup.dom = DOMID_SELF; + gnttab_setup.nr_frames = 1; + gnttab_setup.frame_list.p = &gntframe; + + grub_xen_grant_table_op (GNTTABOP_setup_table, &gnttab_setup, 1); + + for (j = 0; j < total_pages - n_unusable_pages; j++) + while (!grub_xen_is_page_usable (mfn_list[j])) + { + grub_xen_mfn_t t; + if (n_unusable_pages >= MAX_N_UNUSABLE_PAGES) + { + struct sched_shutdown arg; + arg.reason = SHUTDOWN_crash; + grub_xen_sched_op (SCHEDOP_shutdown, &arg); + while (1); + } + t = mfn_list[j]; + mfn_list[j] = mfn_list[total_pages - n_unusable_pages - 1]; + mfn_list[total_pages - n_unusable_pages - 1] = t; + + m2p_updates[2 * n_unusable_pages].ptr + = page2offset (mfn_list[j]) | MMU_MACHPHYS_UPDATE; + m2p_updates[2 * n_unusable_pages].val = j; + m2p_updates[2 * n_unusable_pages + 1].ptr + = page2offset (mfn_list[total_pages - n_unusable_pages - 1]) + | MMU_MACHPHYS_UPDATE; + m2p_updates[2 * n_unusable_pages + 1].val = total_pages + - n_unusable_pages - 1; + + n_unusable_pages++; + } + + grub_xen_mmu_update (m2p_updates, 2 * n_unusable_pages, NULL, DOMID_SELF); + + total_pages += 4; + + grub_uint64_t lx[NUMBER_OF_LEVELS], nlx; + grub_uint64_t paging_start = total_pages - 4 - n_unusable_pages, curpage; + + for (nlx = total_pages, i = 0; i < (unsigned) NUMBER_OF_LEVELS; i++) + { + nlx = (nlx + POINTERS_PER_PAGE - 1) >> LOG_POINTERS_PER_PAGE; + /* PAE wants all 4 root directories present. */ +#ifdef __i386__ + if (i == 1) + nlx = 4; +#endif + lx[i] = nlx; + paging_start -= nlx; + } + + oldpgstart = grub_xen_start_page_addr->pt_base >> 12; + oldpgend = oldpgstart + grub_xen_start_page_addr->nr_pt_frames; + + curpage = paging_start; + + int l; + + for (l = NUMBER_OF_LEVELS - 1; l >= 1; l--) + { + for (i = 0; i < lx[l]; i++) + { + grub_xen_update_va_mapping (&window, + page2offset (mfn_list[curpage + i]) | 7, + UVMF_INVLPG); + grub_memset (&window, 0, sizeof (window)); + + for (j = i * POINTERS_PER_PAGE; + j < (i + 1) * POINTERS_PER_PAGE && j < lx[l - 1]; j++) + pg[j - i * POINTERS_PER_PAGE] = + page2offset (mfn_list[curpage + lx[l] + j]) +#ifdef __x86_64__ + | 4 +#endif + | 3; + } + curpage += lx[l]; + } + + for (i = 0; i < lx[0]; i++) + { + grub_xen_update_va_mapping (&window, + page2offset (mfn_list[curpage + i]) | 7, + UVMF_INVLPG); + grub_memset (&window, 0, sizeof (window)); + + for (j = i * POINTERS_PER_PAGE; + j < (i + 1) * POINTERS_PER_PAGE && j < total_pages; j++) + if (j < paging_start && !(j >= oldpgstart && j < oldpgend)) + pg[j - i * POINTERS_PER_PAGE] = page2offset (mfn_list[j]) | 0x7; + else if (j < grub_xen_start_page_addr->nr_pages) + pg[j - i * POINTERS_PER_PAGE] = page2offset (mfn_list[j]) | 5; + else if (j == grub_xen_start_page_addr->nr_pages) + { + pg[j - i * POINTERS_PER_PAGE] = + page2offset (grub_xen_start_page_addr->console.domU.mfn) | 7; + grub_xen_xcons = (void *) (grub_addr_t) page2offset (j); + } + else if (j == grub_xen_start_page_addr->nr_pages + 1) + { + pg[j - i * POINTERS_PER_PAGE] = + grub_xen_start_page_addr->shared_info | 7; + grub_xen_shared_info = (void *) (grub_addr_t) page2offset (j); + } + else if (j == grub_xen_start_page_addr->nr_pages + 2) + { + pg[j - i * POINTERS_PER_PAGE] = + page2offset (grub_xen_start_page_addr->store_mfn) | 7; + grub_xen_xenstore = (void *) (grub_addr_t) page2offset (j); + } + else if (j == grub_xen_start_page_addr->nr_pages + 3) + { + pg[j - i * POINTERS_PER_PAGE] = page2offset (gntframe) | 7; + grub_xen_grant_table = (void *) (grub_addr_t) page2offset (j); + } + } + + grub_xen_update_va_mapping (&window, 0, UVMF_INVLPG); + + mmuext_op_t op[3]; + + op[0].cmd = MMUEXT_PIN_L1_TABLE + (NUMBER_OF_LEVELS - 1); + op[0].arg1.mfn = mfn_list[paging_start]; + op[1].cmd = MMUEXT_NEW_BASEPTR; + op[1].arg1.mfn = mfn_list[paging_start]; + op[2].cmd = MMUEXT_UNPIN_TABLE; + op[2].arg1.mfn = mfn_list[oldpgstart]; + + grub_xen_mmuext_op (op, 3, NULL, DOMID_SELF); + + for (i = oldpgstart; i < oldpgend; i++) + grub_xen_update_va_mapping ((void *) (grub_addr_t) page2offset (i), + page2offset (mfn_list[i]) | 7, UVMF_INVLPG); + void *new_start_page, *new_mfn_list; + new_start_page = (void *) (grub_addr_t) page2offset (paging_start - 1); + grub_memcpy (new_start_page, grub_xen_start_page_addr, 4096); + grub_xen_start_page_addr = new_start_page; + new_mfn_list = (void *) (grub_addr_t) + page2offset (paging_start - 1 + - ((grub_xen_start_page_addr->nr_pages + * sizeof (grub_uint64_t) + 4095) / 4096)); + grub_memcpy (new_mfn_list, mfn_list, grub_xen_start_page_addr->nr_pages + * sizeof (grub_uint64_t)); + grub_xen_start_page_addr->pt_base = page2offset (paging_start); + grub_xen_start_page_addr->mfn_list = (grub_addr_t) new_mfn_list; + + grub_addr_t heap_start = grub_modules_get_end (); + grub_addr_t heap_end = (grub_addr_t) new_mfn_list; + + grub_mm_init_region ((void *) heap_start, heap_end - heap_start); +} + +extern char _end[]; + +void +grub_machine_init (void) +{ +#ifdef __i386__ + grub_xen_vm_assist (VMASST_CMD_enable, VMASST_TYPE_pae_extended_cr3); +#endif + + grub_modbase = ALIGN_UP ((grub_addr_t) _end + + GRUB_KERNEL_MACHINE_MOD_GAP, + GRUB_KERNEL_MACHINE_MOD_ALIGN); + + map_all_pages (); + + grub_console_init (); + + grub_tsc_init (); + + grub_xendisk_init (); + + grub_boot_init (); +} + +void +grub_exit (void) +{ + struct sched_shutdown arg; + + arg.reason = SHUTDOWN_poweroff; + grub_xen_sched_op (SCHEDOP_shutdown, &arg); + while (1); +} + +void +grub_machine_fini (int flags __attribute__ ((unused))) +{ + grub_xendisk_fini (); + grub_boot_fini (); +} + +grub_err_t +grub_machine_mmap_iterate (grub_memory_hook_t hook, void *hook_data) +{ + grub_uint64_t total_pages = grub_xen_start_page_addr->nr_pages; + grub_uint64_t usable_pages = grub_xen_start_page_addr->pt_base >> 12; + if (hook (0, page2offset (usable_pages), GRUB_MEMORY_AVAILABLE, hook_data)) + return GRUB_ERR_NONE; + + hook (page2offset (usable_pages), page2offset (total_pages - usable_pages), + GRUB_MEMORY_RESERVED, hook_data); + + return GRUB_ERR_NONE; +} diff --git a/grub-core/lib/i386/reboot.c b/grub-core/lib/i386/reboot.c index 0587f1477..a234244dc 100644 --- a/grub-core/lib/i386/reboot.c +++ b/grub-core/lib/i386/reboot.c @@ -58,4 +58,3 @@ grub_reboot (void) while (1); } - diff --git a/grub-core/lib/i386/relocator.c b/grub-core/lib/i386/relocator.c index 0170eed39..d2a1b27ae 100644 --- a/grub-core/lib/i386/relocator.c +++ b/grub-core/lib/i386/relocator.c @@ -28,19 +28,6 @@ #include #include -extern grub_uint8_t grub_relocator_forward_start; -extern grub_uint8_t grub_relocator_forward_end; -extern grub_uint8_t grub_relocator_backward_start; -extern grub_uint8_t grub_relocator_backward_end; - -extern void *grub_relocator_backward_dest; -extern void *grub_relocator_backward_src; -extern grub_size_t grub_relocator_backward_chunk_size; - -extern void *grub_relocator_forward_dest; -extern void *grub_relocator_forward_src; -extern grub_size_t grub_relocator_forward_chunk_size; - extern grub_uint8_t grub_relocator16_start; extern grub_uint8_t grub_relocator16_end; extern grub_uint16_t grub_relocator16_cs; @@ -85,75 +72,6 @@ extern struct grub_i386_idt grub_relocator16_idt; #define RELOCATOR_SIZEOF(x) (&grub_relocator##x##_end - &grub_relocator##x##_start) -grub_size_t grub_relocator_align = 1; -grub_size_t grub_relocator_forward_size; -grub_size_t grub_relocator_backward_size; -#ifdef __x86_64__ -grub_size_t grub_relocator_jumper_size = 12; -#else -grub_size_t grub_relocator_jumper_size = 7; -#endif - -void -grub_cpu_relocator_init (void) -{ - grub_relocator_forward_size = RELOCATOR_SIZEOF(_forward); - grub_relocator_backward_size = RELOCATOR_SIZEOF(_backward); -} - -void -grub_cpu_relocator_jumper (void *rels, grub_addr_t addr) -{ - grub_uint8_t *ptr; - ptr = rels; -#ifdef __x86_64__ - /* movq imm64, %rax (for relocator) */ - *(grub_uint8_t *) ptr = 0x48; - ptr++; - *(grub_uint8_t *) ptr = 0xb8; - ptr++; - *(grub_uint64_t *) ptr = addr; - ptr += sizeof (grub_uint64_t); -#else - /* movl imm32, %eax (for relocator) */ - *(grub_uint8_t *) ptr = 0xb8; - ptr++; - *(grub_uint32_t *) ptr = addr; - ptr += sizeof (grub_uint32_t); -#endif - /* jmp $eax/$rax */ - *(grub_uint8_t *) ptr = 0xff; - ptr++; - *(grub_uint8_t *) ptr = 0xe0; - ptr++; -} - -void -grub_cpu_relocator_backward (void *ptr, void *src, void *dest, - grub_size_t size) -{ - grub_relocator_backward_dest = dest; - grub_relocator_backward_src = src; - grub_relocator_backward_chunk_size = size; - - grub_memmove (ptr, - &grub_relocator_backward_start, - RELOCATOR_SIZEOF (_backward)); -} - -void -grub_cpu_relocator_forward (void *ptr, void *src, void *dest, - grub_size_t size) -{ - grub_relocator_forward_dest = dest; - grub_relocator_forward_src = src; - grub_relocator_forward_chunk_size = size; - - grub_memmove (ptr, - &grub_relocator_forward_start, - RELOCATOR_SIZEOF (_forward)); -} - grub_err_t grub_relocator32_boot (struct grub_relocator *rel, struct grub_relocator32_state state, diff --git a/grub-core/lib/i386/relocator_common_c.c b/grub-core/lib/i386/relocator_common_c.c new file mode 100644 index 000000000..7be609b73 --- /dev/null +++ b/grub-core/lib/i386/relocator_common_c.c @@ -0,0 +1,109 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2009-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 . + */ + +#include +#include + +#include +#include +#include + +#include +#include + +extern grub_uint8_t grub_relocator_forward_start; +extern grub_uint8_t grub_relocator_forward_end; +extern grub_uint8_t grub_relocator_backward_start; +extern grub_uint8_t grub_relocator_backward_end; + +extern void *grub_relocator_backward_dest; +extern void *grub_relocator_backward_src; +extern grub_size_t grub_relocator_backward_chunk_size; + +extern void *grub_relocator_forward_dest; +extern void *grub_relocator_forward_src; +extern grub_size_t grub_relocator_forward_chunk_size; + +#define RELOCATOR_SIZEOF(x) (&grub_relocator##x##_end - &grub_relocator##x##_start) + +grub_size_t grub_relocator_align = 1; +grub_size_t grub_relocator_forward_size; +grub_size_t grub_relocator_backward_size; +#ifdef __x86_64__ +grub_size_t grub_relocator_jumper_size = 12; +#else +grub_size_t grub_relocator_jumper_size = 7; +#endif + +void +grub_cpu_relocator_init (void) +{ + grub_relocator_forward_size = RELOCATOR_SIZEOF (_forward); + grub_relocator_backward_size = RELOCATOR_SIZEOF (_backward); +} + +void +grub_cpu_relocator_jumper (void *rels, grub_addr_t addr) +{ + grub_uint8_t *ptr; + ptr = rels; +#ifdef __x86_64__ + /* movq imm64, %rax (for relocator) */ + *(grub_uint8_t *) ptr = 0x48; + ptr++; + *(grub_uint8_t *) ptr = 0xb8; + ptr++; + *(grub_uint64_t *) ptr = addr; + ptr += sizeof (grub_uint64_t); +#else + /* movl imm32, %eax (for relocator) */ + *(grub_uint8_t *) ptr = 0xb8; + ptr++; + *(grub_uint32_t *) ptr = addr; + ptr += sizeof (grub_uint32_t); +#endif + /* jmp $eax/$rax */ + *(grub_uint8_t *) ptr = 0xff; + ptr++; + *(grub_uint8_t *) ptr = 0xe0; + ptr++; +} + +void +grub_cpu_relocator_backward (void *ptr, void *src, void *dest, + grub_size_t size) +{ + grub_relocator_backward_dest = dest; + grub_relocator_backward_src = src; + grub_relocator_backward_chunk_size = size; + + grub_memmove (ptr, + &grub_relocator_backward_start, RELOCATOR_SIZEOF (_backward)); +} + +void +grub_cpu_relocator_forward (void *ptr, void *src, void *dest, + grub_size_t size) +{ + grub_relocator_forward_dest = dest; + grub_relocator_forward_src = src; + grub_relocator_forward_chunk_size = size; + + grub_memmove (ptr, + &grub_relocator_forward_start, RELOCATOR_SIZEOF (_forward)); +} diff --git a/grub-core/lib/i386/xen/relocator.S b/grub-core/lib/i386/xen/relocator.S new file mode 100644 index 000000000..a1677db9f --- /dev/null +++ b/grub-core/lib/i386/xen/relocator.S @@ -0,0 +1,146 @@ +/* + * 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 . + */ + +#include +#include + + .p2align 4 /* force 16-byte alignment */ + +VARIABLE(grub_relocator_xen_remap_start) +LOCAL(base): + /* mov imm32, %ebx */ + .byte 0xbb +VARIABLE(grub_relocator_xen_remapper_virt) + .long 0 + + /* mov imm32, %ecx */ + .byte 0xb9 +VARIABLE(grub_relocator_xen_remapper_map) + .long 0 + + /* mov imm32, %edx */ + .byte 0xba +VARIABLE(grub_relocator_xen_remapper_map_high) + .long 0 + + movl $2, %esi + movl $__HYPERVISOR_update_va_mapping, %eax + int $0x82 + + addl $(LOCAL(cont) - LOCAL(base)), %ebx + + jmp *%ebx + +LOCAL(cont): + + /* mov imm32, %ecx */ + .byte 0xb9 +VARIABLE(grub_relocator_xen_paging_size) + .long 0 + + /* mov imm32, %ebx */ + .byte 0xbb +VARIABLE(grub_relocator_xen_paging_start) + .long 0 + + /* mov imm32, %eax */ + .byte 0xb8 +VARIABLE(grub_relocator_xen_mfn_list) + .long 0 + + movl %eax, %edi +1: + movl %ecx, %ebp + movl 0(%edi), %ecx + movl %ecx, %edx + shll $12, %ecx + shrl $20, %edx + orl $5, %ecx + movl $2, %esi + movl $__HYPERVISOR_update_va_mapping, %eax + int $0x82 + + movl %ebp, %ecx + addl $4, %edi + addl $4096, %ebx + + loop 1b + + /* mov imm32, %ebx */ + .byte 0xbb +VARIABLE(grub_relocator_xen_mmu_op_addr) + .long 0 + movl $3, %ecx + movl $0, %edx + movl $0x7FF0, %esi + movl $__HYPERVISOR_mmuext_op, %eax + int $0x82 + + /* mov imm32, %eax */ + .byte 0xb8 +VARIABLE(grub_relocator_xen_remap_continue) + .long 0 + + jmp *%eax + +VARIABLE(grub_relocator_xen_mmu_op) + .space 256 + +VARIABLE(grub_relocator_xen_remap_end) + + +VARIABLE(grub_relocator_xen_start) + /* mov imm32, %eax */ + .byte 0xb8 +VARIABLE(grub_relocator_xen_remapper_virt2) + .long 0 + + movl %eax, %edi + + xorl %ecx, %ecx + xorl %edx, %edx + + movl $2, %esi + movl $__HYPERVISOR_update_va_mapping, %eax + int $0x82 + + + /* mov imm32, %eax */ + .byte 0xb8 +VARIABLE(grub_relocator_xen_stack) + .long 0 + + movl %eax, %esp + + /* mov imm32, %eax */ + .byte 0xb8 +VARIABLE(grub_relocator_xen_start_info) + .long 0 + + movl %eax, %esi + + cld + + /* mov imm32, %eax */ + .byte 0xb8 +VARIABLE(grub_relocator_xen_entry_point) + .long 0 + + jmp *%eax + +VARIABLE(grub_relocator_xen_end) diff --git a/grub-core/lib/legacy_parse.c b/grub-core/lib/legacy_parse.c index 4dc887ad2..300d8b961 100644 --- a/grub-core/lib/legacy_parse.c +++ b/grub-core/lib/legacy_parse.c @@ -250,6 +250,9 @@ static struct legacy_command legacy_commands[] = {"pause", "echo %s; if ! sleep -i 60; then return; fi\n", NULL, 0, 1, {TYPE_REST_VERBATIM}, 0, "[MESSAGE ...]", "Print MESSAGE, then wait until a key is pressed."}, + {"print", "echo %s\n", NULL, 0, 1, + {TYPE_REST_VERBATIM}, 0, + "[MESSAGE ...]", "Print MESSAGE."}, /* FIXME: quit unsupported. */ /* FIXME: rarp unsupported. */ {"read", "read_dword %s\n", NULL, 0, 1, {TYPE_INT}, 0, "ADDR", diff --git a/grub-core/lib/relocator.c b/grub-core/lib/relocator.c index b86a6c0dc..c86c5e886 100644 --- a/grub-core/lib/relocator.c +++ b/grub-core/lib/relocator.c @@ -1432,7 +1432,7 @@ grub_relocator_alloc_chunk_align (struct grub_relocator *rel, #ifdef GRUB_MACHINE_EFI grub_efi_mmap_iterate (grub_relocator_alloc_chunk_align_iter, &ctx, avoid_efi_boot_services); -#elif defined (__powerpc__) +#elif defined (__powerpc__) || defined (GRUB_MACHINE_XEN) (void) avoid_efi_boot_services; grub_machine_mmap_iterate (grub_relocator_alloc_chunk_align_iter, &ctx); #else diff --git a/grub-core/lib/x86_64/xen/relocator.S b/grub-core/lib/x86_64/xen/relocator.S new file mode 100644 index 000000000..28ad4c766 --- /dev/null +++ b/grub-core/lib/x86_64/xen/relocator.S @@ -0,0 +1,154 @@ +/* + * 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 . + */ + +#include +#include + + .p2align 4 /* force 16-byte alignment */ + +VARIABLE(grub_relocator_xen_remap_start) +LOCAL(base): + /* mov imm64, %rax */ + .byte 0x48 + .byte 0xb8 +VARIABLE(grub_relocator_xen_remapper_virt) + .quad 0 + + movq %rax, %rdi + + /* mov imm64, %rax */ + .byte 0x48 + .byte 0xb8 +VARIABLE(grub_relocator_xen_remapper_map) + .quad 0 + + movq %rax, %rsi + + movq $2, %rdx + movq $__HYPERVISOR_update_va_mapping, %rax + syscall + + addq $(LOCAL(cont) - LOCAL(base)), %rdi + + jmp *%rdi + +LOCAL(cont): + + /* mov imm64, %rcx */ + .byte 0x48 + .byte 0xb9 +VARIABLE(grub_relocator_xen_paging_size) + .quad 0 + + /* mov imm64, %rax */ + .byte 0x48 + .byte 0xb8 +VARIABLE(grub_relocator_xen_paging_start) + .quad 0 + + movq %rax, %rdi + + /* mov imm64, %rax */ + .byte 0x48 + .byte 0xb8 +VARIABLE(grub_relocator_xen_mfn_list) + .quad 0 + + movq %rax, %rsi +1: + movq %rsi, %rbx + movq 0(%rsi), %rsi + shlq $12, %rsi + orq $5, %rsi + movq $2, %rdx + movq %rcx, %r9 + movq $__HYPERVISOR_update_va_mapping, %rax + syscall + + movq %r9, %rcx + addq $8, %rbx + addq $4096, %rdi + movq %rbx, %rsi + + loop 1b + + leaq EXT_C(grub_relocator_xen_mmu_op) (%rip), %rdi + movq $3, %rsi + movq $0, %rdx + movq $0x7FF0, %r10 + movq $__HYPERVISOR_mmuext_op, %rax + syscall + + /* mov imm64, %rax */ + .byte 0x48 + .byte 0xb8 +VARIABLE(grub_relocator_xen_remap_continue) + .quad 0 + + jmp *%rax + +VARIABLE(grub_relocator_xen_mmu_op) + .space 256 + +VARIABLE(grub_relocator_xen_remap_end) + + +VARIABLE(grub_relocator_xen_start) + /* mov imm64, %rax */ + .byte 0x48 + .byte 0xb8 +VARIABLE(grub_relocator_xen_remapper_virt2) + .quad 0 + + movq %rax, %rdi + + xorq %rax, %rax + movq %rax, %rsi + + movq $2, %rdx + movq $__HYPERVISOR_update_va_mapping, %rax + syscall + + + /* mov imm64, %rax */ + .byte 0x48 + .byte 0xb8 +VARIABLE(grub_relocator_xen_stack) + .quad 0 + + movq %rax, %rsp + + /* mov imm64, %rax */ + .byte 0x48 + .byte 0xb8 +VARIABLE(grub_relocator_xen_start_info) + .quad 0 + + movq %rax, %rsi + + cld + + /* mov imm64, %rax */ + .byte 0x48 + .byte 0xb8 +VARIABLE(grub_relocator_xen_entry_point) + .quad 0 + + jmp *%rax + +VARIABLE(grub_relocator_xen_end) diff --git a/grub-core/lib/xen/datetime.c b/grub-core/lib/xen/datetime.c new file mode 100644 index 000000000..d96176ec6 --- /dev/null +++ b/grub-core/lib/xen/datetime.c @@ -0,0 +1,40 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2011 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 . + */ + +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +grub_err_t +grub_get_datetime (struct grub_datetime *datetime) +{ + long long nix; + nix = (grub_xen_shared_info->wc_sec + + grub_divmod64 (grub_xen_shared_info->vcpu_info[0].time.system_time, 1000000000, 0)); + grub_unixtime2datetime (nix, datetime); + return GRUB_ERR_NONE; +} + +grub_err_t +grub_set_datetime (struct grub_datetime *datetime __attribute__ ((unused))) +{ + return grub_error (GRUB_ERR_IO, "setting time isn't supported"); +} diff --git a/grub-core/lib/xen/halt.c b/grub-core/lib/xen/halt.c new file mode 100644 index 000000000..2aceead6e --- /dev/null +++ b/grub-core/lib/xen/halt.c @@ -0,0 +1,32 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2011 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 . + */ + +#include +#include +#include +#include + +void +grub_halt (void) +{ + struct sched_shutdown arg; + + arg.reason = SHUTDOWN_poweroff; + grub_xen_sched_op (SCHEDOP_shutdown, &arg); + for (;;); +} diff --git a/grub-core/lib/xen/reboot.c b/grub-core/lib/xen/reboot.c new file mode 100644 index 000000000..fd7609a7d --- /dev/null +++ b/grub-core/lib/xen/reboot.c @@ -0,0 +1,32 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2011 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 . + */ + +#include +#include +#include +#include + +void +grub_reboot (void) +{ + struct sched_shutdown arg; + + arg.reason = SHUTDOWN_reboot; + grub_xen_sched_op (SCHEDOP_shutdown, &arg); + for (;;); +} diff --git a/grub-core/lib/xen/relocator.c b/grub-core/lib/xen/relocator.c new file mode 100644 index 000000000..8f427d341 --- /dev/null +++ b/grub-core/lib/xen/relocator.c @@ -0,0 +1,125 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include + +#include +#include +#include +#include + +#include +#include + +typedef grub_addr_t grub_xen_reg_t; + +extern grub_uint8_t grub_relocator_xen_start; +extern grub_uint8_t grub_relocator_xen_end; +extern grub_uint8_t grub_relocator_xen_remap_start; +extern grub_uint8_t grub_relocator_xen_remap_end; +extern grub_xen_reg_t grub_relocator_xen_stack; +extern grub_xen_reg_t grub_relocator_xen_start_info; +extern grub_xen_reg_t grub_relocator_xen_entry_point; +extern grub_xen_reg_t grub_relocator_xen_paging_start; +extern grub_xen_reg_t grub_relocator_xen_paging_size; +extern grub_xen_reg_t grub_relocator_xen_remapper_virt; +extern grub_xen_reg_t grub_relocator_xen_remapper_virt2; +extern grub_xen_reg_t grub_relocator_xen_remapper_map; +extern grub_xen_reg_t grub_relocator_xen_mfn_list; +extern grub_xen_reg_t grub_relocator_xen_remap_continue; +#ifdef __i386__ +extern grub_xen_reg_t grub_relocator_xen_mmu_op_addr; +extern grub_xen_reg_t grub_relocator_xen_remapper_map_high; +#endif +extern mmuext_op_t grub_relocator_xen_mmu_op[3]; + +#define RELOCATOR_SIZEOF(x) (&grub_relocator##x##_end - &grub_relocator##x##_start) + +grub_err_t +grub_relocator_xen_boot (struct grub_relocator *rel, + struct grub_relocator_xen_state state, + grub_uint64_t remapper_pfn, + grub_addr_t remapper_virt, + grub_uint64_t trampoline_pfn, + grub_addr_t trampoline_virt) +{ + grub_err_t err; + void *relst; + grub_relocator_chunk_t ch, ch_tramp; + grub_xen_mfn_t *mfn_list = + (grub_xen_mfn_t *) grub_xen_start_page_addr->mfn_list; + + err = grub_relocator_alloc_chunk_addr (rel, &ch, remapper_pfn << 12, + RELOCATOR_SIZEOF (_xen_remap)); + if (err) + return err; + err = grub_relocator_alloc_chunk_addr (rel, &ch_tramp, trampoline_pfn << 12, + RELOCATOR_SIZEOF (_xen)); + if (err) + return err; + + grub_relocator_xen_stack = state.stack; + grub_relocator_xen_start_info = state.start_info; + grub_relocator_xen_entry_point = state.entry_point; + grub_relocator_xen_paging_start = state.paging_start << 12; + grub_relocator_xen_paging_size = state.paging_size; + grub_relocator_xen_remapper_virt = remapper_virt; + grub_relocator_xen_remapper_virt2 = remapper_virt; + grub_relocator_xen_remap_continue = trampoline_virt; + + grub_relocator_xen_remapper_map = (mfn_list[remapper_pfn] << 12) | 5; +#ifdef __i386__ + grub_relocator_xen_remapper_map_high = (mfn_list[remapper_pfn] >> 20); + grub_relocator_xen_mmu_op_addr = (char *) &grub_relocator_xen_mmu_op + - (char *) &grub_relocator_xen_remap_start + remapper_virt; +#endif + + grub_relocator_xen_mfn_list = state.mfn_list + + state.paging_start * sizeof (grub_addr_t); + + grub_memset (grub_relocator_xen_mmu_op, 0, + sizeof (grub_relocator_xen_mmu_op)); +#ifdef __i386__ + grub_relocator_xen_mmu_op[0].cmd = MMUEXT_PIN_L3_TABLE; +#else + grub_relocator_xen_mmu_op[0].cmd = MMUEXT_PIN_L4_TABLE; +#endif + grub_relocator_xen_mmu_op[0].arg1.mfn = mfn_list[state.paging_start]; + grub_relocator_xen_mmu_op[1].cmd = MMUEXT_NEW_BASEPTR; + grub_relocator_xen_mmu_op[1].arg1.mfn = mfn_list[state.paging_start]; + grub_relocator_xen_mmu_op[2].cmd = MMUEXT_UNPIN_TABLE; + grub_relocator_xen_mmu_op[2].arg1.mfn = + mfn_list[grub_xen_start_page_addr->pt_base >> 12]; + + grub_memmove (get_virtual_current_address (ch), + &grub_relocator_xen_remap_start, + RELOCATOR_SIZEOF (_xen_remap)); + grub_memmove (get_virtual_current_address (ch_tramp), + &grub_relocator_xen_start, RELOCATOR_SIZEOF (_xen)); + + err = grub_relocator_prepare_relocs (rel, get_physical_target_address (ch), + &relst, NULL); + if (err) + return err; + + ((void (*)(void)) relst) (); + + /* Not reached. */ + return GRUB_ERR_NONE; +} diff --git a/grub-core/loader/i386/xen.c b/grub-core/loader/i386/xen.c new file mode 100644 index 000000000..91b66f8f5 --- /dev/null +++ b/grub-core/loader/i386/xen.c @@ -0,0 +1,712 @@ +/* + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static struct grub_relocator *relocator = NULL; +static grub_uint64_t max_addr; +static grub_dl_t my_mod; +static int loaded = 0; +static struct start_info next_start; +static void *kern_chunk_src; +static struct grub_xen_file_info xen_inf; +static struct xen_multiboot_mod_list *xen_module_info_page; +static grub_uint64_t modules_target_start; +static grub_size_t n_modules; + +#define PAGE_SIZE 4096 +#define MAX_MODULES (PAGE_SIZE / sizeof (struct xen_multiboot_mod_list)) +#define PAGE_SHIFT 12 +#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; +} + +#ifdef __x86_64__ +#define NUMBER_OF_LEVELS 4 +#define INTERMEDIATE_OR 7 +#else +#define NUMBER_OF_LEVELS 3 +#define INTERMEDIATE_OR 3 +#endif + +static grub_uint64_t +get_pgtable_size (grub_uint64_t total_pages, grub_uint64_t virt_base) +{ + if (!virt_base) + total_pages++; + grub_uint64_t ret = 0; + grub_uint64_t ll = total_pages; + int i; + for (i = 0; i < NUMBER_OF_LEVELS; i++) + { + ll = (ll + POINTERS_PER_PAGE - 1) >> LOG_POINTERS_PER_PAGE; + /* PAE wants all 4 root directories present. */ +#ifdef __i386__ + if (i == 1) + ll = 4; +#endif + ret += ll; + } + for (i = 1; i < NUMBER_OF_LEVELS; i++) + if (virt_base >> (PAGE_SHIFT + i * LOG_POINTERS_PER_PAGE)) + ret++; + return ret; +} + +static void +generate_page_table (grub_uint64_t *where, grub_uint64_t paging_start, + grub_uint64_t total_pages, grub_uint64_t virt_base, + grub_xen_mfn_t *mfn_list) +{ + if (!virt_base) + total_pages++; + + grub_uint64_t lx[NUMBER_OF_LEVELS], lxs[NUMBER_OF_LEVELS]; + grub_uint64_t nlx, nls, sz = 0; + int l; + + nlx = total_pages; + nls = virt_base >> PAGE_SHIFT; + for (l = 0; l < NUMBER_OF_LEVELS; l++) + { + nlx = (nlx + POINTERS_PER_PAGE - 1) >> LOG_POINTERS_PER_PAGE; + /* PAE wants all 4 root directories present. */ +#ifdef __i386__ + if (l == 1) + nlx = 4; +#endif + lx[l] = nlx; + sz += lx[l]; + lxs[l] = nls & (POINTERS_PER_PAGE - 1); + if (nls && l != 0) + sz++; + nls >>= LOG_POINTERS_PER_PAGE; + } + + grub_uint64_t lp; + grub_uint64_t j; + grub_uint64_t *pg = (grub_uint64_t *) where; + int pr = 0; + + grub_memset (pg, 0, sz * PAGE_SIZE); + + lp = paging_start + lx[NUMBER_OF_LEVELS - 1]; + for (l = NUMBER_OF_LEVELS - 1; l >= 1; l--) + { + if (lxs[l] || pr) + pg[0] = page2offset (mfn_list[lp++]) | INTERMEDIATE_OR; + if (pr) + pg += POINTERS_PER_PAGE; + for (j = 0; j < lx[l - 1]; j++) + pg[j + lxs[l]] = page2offset (mfn_list[lp++]) | INTERMEDIATE_OR; + pg += lx[l] * POINTERS_PER_PAGE; + if (lxs[l]) + pr = 1; + } + + if (lxs[0] || pr) + pg[0] = page2offset (mfn_list[total_pages]) | 5; + if (pr) + pg += POINTERS_PER_PAGE; + + for (j = 0; j < total_pages; j++) + { + if (j >= paging_start && j < lp) + pg[j + lxs[0]] = page2offset (mfn_list[j]) | 5; + else + pg[j + lxs[0]] = page2offset (mfn_list[j]) | 7; + } +} + +static grub_err_t +set_mfns (grub_xen_mfn_t * new_mfn_list, 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 (new_mfn_list[i] == grub_xen_start_page_addr->console.domU.mfn) + cn_pfn = i; + if (new_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 = new_mfn_list[pfn]; + new_mfn_list[pfn] = new_mfn_list[cn_pfn]; + new_mfn_list[cn_pfn] = t; + t = new_mfn_list[pfn + 1]; + new_mfn_list[pfn + 1] = new_mfn_list[st_pfn]; + new_mfn_list[st_pfn] = t; + + m2p_updates[0].ptr = page2offset (new_mfn_list[pfn]) | MMU_MACHPHYS_UPDATE; + m2p_updates[0].val = pfn; + m2p_updates[1].ptr = + page2offset (new_mfn_list[pfn + 1]) | MMU_MACHPHYS_UPDATE; + m2p_updates[1].val = pfn + 1; + m2p_updates[2].ptr = + page2offset (new_mfn_list[cn_pfn]) | MMU_MACHPHYS_UPDATE; + m2p_updates[2].val = cn_pfn; + m2p_updates[3].ptr = + page2offset (new_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_boot (void) +{ + struct grub_relocator_xen_state state; + grub_relocator_chunk_t ch; + grub_err_t err; + grub_size_t pgtsize; + struct start_info *nst; + grub_uint64_t nr_info_pages; + grub_uint64_t nr_pages, nr_pt_pages, nr_need_pages; + struct gnttab_set_version gnttab_setver; + grub_xen_mfn_t *new_mfn_list; + grub_size_t i; + + if (grub_xen_n_allocated_shared_pages) + return grub_error (GRUB_ERR_BUG, "active grants"); + + state.mfn_list = max_addr; + next_start.mfn_list = max_addr + xen_inf.virt_base; + next_start.first_p2m_pfn = max_addr >> PAGE_SHIFT; /* Is this right? */ + pgtsize = sizeof (grub_xen_mfn_t) * grub_xen_start_page_addr->nr_pages; + err = grub_relocator_alloc_chunk_addr (relocator, &ch, max_addr, pgtsize); + next_start.nr_p2m_frames = (pgtsize + PAGE_SIZE - 1) >> PAGE_SHIFT; + if (err) + return err; + new_mfn_list = get_virtual_current_address (ch); + grub_memcpy (new_mfn_list, + (void *) grub_xen_start_page_addr->mfn_list, pgtsize); + max_addr = ALIGN_UP (max_addr + pgtsize, PAGE_SIZE); + + err = grub_relocator_alloc_chunk_addr (relocator, &ch, + max_addr, sizeof (next_start)); + if (err) + return err; + state.start_info = max_addr + xen_inf.virt_base; + nst = get_virtual_current_address (ch); + max_addr = ALIGN_UP (max_addr + sizeof (next_start), PAGE_SIZE); + + next_start.nr_pages = grub_xen_start_page_addr->nr_pages; + grub_memcpy (next_start.magic, grub_xen_start_page_addr->magic, + sizeof (next_start.magic)); + next_start.store_mfn = grub_xen_start_page_addr->store_mfn; + next_start.store_evtchn = grub_xen_start_page_addr->store_evtchn; + next_start.console.domU = grub_xen_start_page_addr->console.domU; + next_start.shared_info = grub_xen_start_page_addr->shared_info; + + err = set_mfns (new_mfn_list, max_addr >> PAGE_SHIFT); + if (err) + return err; + max_addr += 2 * PAGE_SIZE; + + next_start.pt_base = max_addr + xen_inf.virt_base; + state.paging_start = max_addr >> PAGE_SHIFT; + + nr_info_pages = max_addr >> PAGE_SHIFT; + nr_pages = nr_info_pages; + + while (1) + { + nr_pages = ALIGN_UP (nr_pages, (ALIGN_SIZE >> PAGE_SHIFT)); + nr_pt_pages = get_pgtable_size (nr_pages, xen_inf.virt_base); + nr_need_pages = + nr_info_pages + nr_pt_pages + + ((ADDITIONAL_SIZE + STACK_SIZE) >> PAGE_SHIFT); + if (nr_pages >= nr_need_pages) + break; + nr_pages = nr_need_pages; + } + + grub_dprintf ("xen", "bootstrap domain %llx+%llx\n", + (unsigned long long) xen_inf.virt_base, + (unsigned long long) page2offset (nr_pages)); + + err = grub_relocator_alloc_chunk_addr (relocator, &ch, + max_addr, page2offset (nr_pt_pages)); + if (err) + return err; + + generate_page_table (get_virtual_current_address (ch), + max_addr >> PAGE_SHIFT, nr_pages, + xen_inf.virt_base, new_mfn_list); + + max_addr += page2offset (nr_pt_pages); + state.stack = max_addr + STACK_SIZE + xen_inf.virt_base; + state.entry_point = xen_inf.entry_point; + + next_start.nr_p2m_frames += nr_pt_pages; + next_start.nr_pt_frames = nr_pt_pages; + state.paging_size = nr_pt_pages; + + *nst = 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 (relocator, state, nr_pages, + xen_inf.virt_base < + PAGE_SIZE ? page2offset (nr_pages) : 0, + nr_pages - 1, + page2offset (nr_pages - 1) + + xen_inf.virt_base); +} + +static grub_err_t +grub_xen_unload (void) +{ + grub_dl_unref (my_mod); + loaded = 0; + 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; + + if (argc == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); + + grub_loader_unset (); + + grub_memset (&next_start, 0, sizeof (next_start)); + + xen_module_info_page = NULL; + n_modules = 0; + + grub_create_loader_cmdline (argc - 1, argv + 1, + (char *) next_start.cmd_line, + sizeof (next_start.cmd_line) - 1); + + file = grub_file_open (argv[0]); + if (!file) + return grub_errno; + + elf = grub_xen_file (file); + if (!elf) + goto fail; + + err = grub_xen_get_info (elf, &xen_inf); + if (err) + goto fail; +#ifdef __x86_64__ + if (xen_inf.arch != GRUB_XEN_FILE_X86_64) +#else + if (xen_inf.arch != GRUB_XEN_FILE_I386_PAE + && xen_inf.arch != GRUB_XEN_FILE_I386_PAE_BIMODE) +#endif + { + grub_error (GRUB_ERR_BAD_OS, "incompatible architecture: %d", + xen_inf.arch); + goto fail; + } + + if (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_inf.virt_base, + (unsigned long long) xen_inf.entry_point); + + relocator = grub_relocator_new (); + if (!relocator) + goto fail; + + grub_relocator_chunk_t ch; + grub_addr_t kern_start = xen_inf.kern_start - xen_inf.paddr_offset; + grub_addr_t kern_end = xen_inf.kern_end - xen_inf.paddr_offset; + + if (xen_inf.has_hypercall_page) + { + grub_dprintf ("xen", "hypercall page at 0x%llx\n", + (unsigned long long) xen_inf.hypercall_page); + if (xen_inf.hypercall_page - xen_inf.virt_base < kern_start) + kern_start = xen_inf.hypercall_page - xen_inf.virt_base; + + if (xen_inf.hypercall_page - xen_inf.virt_base + PAGE_SIZE > kern_end) + kern_end = xen_inf.hypercall_page - xen_inf.virt_base + PAGE_SIZE; + } + + max_addr = ALIGN_UP (kern_end, PAGE_SIZE); + + err = grub_relocator_alloc_chunk_addr (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_inf.paddr_offset); + grub_dprintf ("xen", "kern_start = 0x%llx, kern_end = 0x%llx\n", + (unsigned long long) xen_inf.kern_start, + (unsigned long long) xen_inf.kern_end); + + err = grub_elfXX_load (elf, argv[0], + (grub_uint8_t *) kern_chunk_src - kern_start + - xen_inf.paddr_offset, 0, 0, 0); + + if (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_inf.hypercall_page - xen_inf.virt_base - + kern_start, i); + } + + if (err) + goto fail; + + grub_dl_ref (my_mod); + loaded = 1; + + grub_loader_set (grub_xen_boot, grub_xen_unload, 0); + loaded = 1; + + goto fail; + +fail: + + if (elf) + grub_elf_close (elf); + else if (file) + grub_file_close (file); + + if (grub_errno != GRUB_ERR_NONE) + loaded = 0; + + 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_err_t err; + struct grub_linux_initrd_context initrd_ctx; + grub_relocator_chunk_t ch; + + 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 (next_start.mod_start || next_start.mod_len) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, N_("initrd already loaded")); + 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 (relocator, &ch, max_addr, size); + if (err) + return err; + + if (grub_initrd_load (&initrd_ctx, argv, + get_virtual_current_address (ch))) + goto fail; + } + + next_start.mod_start = max_addr + xen_inf.virt_base; + next_start.mod_len = size; + + max_addr = ALIGN_UP (max_addr + size, PAGE_SIZE); + + grub_dprintf ("xen", "Initrd, addr=0x%x, size=0x%x\n", + (unsigned) next_start.mod_start, (unsigned) 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 (!loaded) + { + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("you need to load the kernel first")); + } + + if ((next_start.mod_start || next_start.mod_len) && !xen_module_info_page) + { + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("initrd already loaded")); + } + + /* Leave one space for terminator. */ + if (n_modules >= MAX_MODULES - 1) + { + return grub_error (GRUB_ERR_BAD_ARGUMENT, "too many modules"); + } + + if (!xen_module_info_page) + { + n_modules = 0; + max_addr = ALIGN_UP (max_addr, PAGE_SIZE); + modules_target_start = max_addr; + next_start.mod_start = max_addr + xen_inf.virt_base; + next_start.flags |= SIF_MULTIBOOT_MOD; + + err = grub_relocator_alloc_chunk_addr (relocator, &ch, + max_addr, MAX_MODULES + * + sizeof (xen_module_info_page + [0])); + if (err) + return err; + xen_module_info_page = get_virtual_current_address (ch); + grub_memset (xen_module_info_page, 0, MAX_MODULES + * sizeof (xen_module_info_page[0])); + max_addr += MAX_MODULES * sizeof (xen_module_info_page[0]); + } + + max_addr = ALIGN_UP (max_addr, PAGE_SIZE); + + if (nounzip) + grub_file_filter_disable_compression (); + file = grub_file_open (argv[0]); + 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 (relocator, &ch, + max_addr, cmdline_len); + if (err) + goto fail; + + grub_create_loader_cmdline (argc - 1, argv + 1, + get_virtual_current_address (ch), cmdline_len); + + xen_module_info_page[n_modules].cmdline = max_addr - modules_target_start; + max_addr = ALIGN_UP (max_addr + cmdline_len, PAGE_SIZE); + + if (size) + { + err = grub_relocator_alloc_chunk_addr (relocator, &ch, 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; + } + } + next_start.mod_len = max_addr + size - modules_target_start; + xen_module_info_page[n_modules].mod_start = max_addr - modules_target_start; + xen_module_info_page[n_modules].mod_end = + max_addr + size - modules_target_start; + + n_modules++; + grub_dprintf ("xen", "module, addr=0x%x, size=0x%x\n", + (unsigned) max_addr, (unsigned) size); + max_addr = ALIGN_UP (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); +} diff --git a/grub-core/loader/i386/xen_file.c b/grub-core/loader/i386/xen_file.c new file mode 100644 index 000000000..fb7f692b2 --- /dev/null +++ b/grub-core/loader/i386/xen_file.c @@ -0,0 +1,91 @@ +/* + * 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 . + */ + +#include +#include + +grub_elf_t +grub_xen_file (grub_file_t file) +{ + grub_elf_t elf; + struct linux_kernel_header lh; + grub_file_t off_file; + + elf = grub_elf_file (file, file->name); + if (elf) + return elf; + grub_errno = GRUB_ERR_NONE; + + if (grub_file_seek (file, 0) == (grub_off_t) -1) + goto fail; + + if (grub_file_read (file, &lh, sizeof (lh)) != sizeof (lh)) + goto fail; + + if (lh.boot_flag != grub_cpu_to_le16 (0xaa55) + || lh.header != grub_cpu_to_le32 (GRUB_LINUX_MAGIC_SIGNATURE) + || grub_le_to_cpu16 (lh.version) < 0x0208) + { + grub_error (GRUB_ERR_BAD_OS, "version too old for xen boot"); + return NULL; + } + + if (lh.payload_length < 4) + { + grub_error (GRUB_ERR_BAD_OS, "payload too short"); + return NULL; + } + + grub_dprintf ("xen", "found bzimage payload 0x%llx-0x%llx\n", + (unsigned long long) (lh.setup_sects + 1) * 512 + + lh.payload_offset, + (unsigned long long) lh.payload_length - 4); + + off_file = grub_file_offset_open (file, (lh.setup_sects + 1) * 512 + + lh.payload_offset, + lh.payload_length - 4); + if (!off_file) + goto fail; + + elf = grub_elf_file (off_file, file->name); + if (elf) + return elf; + grub_file_offset_close (off_file); + +fail: + grub_error (GRUB_ERR_BAD_OS, "not xen image"); + return NULL; +} + +grub_err_t +grub_xen_get_info (grub_elf_t elf, struct grub_xen_file_info * xi) +{ + grub_memset (xi, 0, sizeof (*xi)); + + if (grub_elf_is_elf64 (elf)) + { + xi->arch = GRUB_XEN_FILE_X86_64; + return grub_xen_get_info64 (elf, xi); + } + if (grub_elf_is_elf32 (elf)) + { + xi->arch = GRUB_XEN_FILE_I386; + return grub_xen_get_info32 (elf, xi); + } + return grub_error (GRUB_ERR_BAD_OS, "unknown ELF type"); +} diff --git a/grub-core/loader/i386/xen_file32.c b/grub-core/loader/i386/xen_file32.c new file mode 100644 index 000000000..340d44569 --- /dev/null +++ b/grub-core/loader/i386/xen_file32.c @@ -0,0 +1,7 @@ +#define GRUB_TARGET_WORDSIZE 32 +#define XX 32 +#define grub_le_to_cpu_addr grub_le_to_cpu32 +#define ehdrXX ehdr32 +#define grub_xen_get_infoXX grub_xen_get_info32 +#define FOR_ELF_PHDRS FOR_ELF32_PHDRS +#include "xen_fileXX.c" diff --git a/grub-core/loader/i386/xen_file64.c b/grub-core/loader/i386/xen_file64.c new file mode 100644 index 000000000..c41049369 --- /dev/null +++ b/grub-core/loader/i386/xen_file64.c @@ -0,0 +1,7 @@ +#define GRUB_TARGET_WORDSIZE 64 +#define XX 64 +#define grub_le_to_cpu_addr grub_le_to_cpu64 +#define ehdrXX ehdr64 +#define grub_xen_get_infoXX grub_xen_get_info64 +#define FOR_ELF_PHDRS FOR_ELF64_PHDRS +#include "xen_fileXX.c" diff --git a/grub-core/loader/i386/xen_fileXX.c b/grub-core/loader/i386/xen_fileXX.c new file mode 100644 index 000000000..ea0656c09 --- /dev/null +++ b/grub-core/loader/i386/xen_fileXX.c @@ -0,0 +1,344 @@ +/* + * 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 . + */ + +#include + +static grub_err_t +parse_xen_guest (grub_elf_t elf, struct grub_xen_file_info *xi, + grub_off_t off, grub_size_t sz) +{ + char *buf; + char *ptr; + int has_paddr = 0; + if (grub_file_seek (elf->file, off) == (grub_off_t) -1) + return grub_errno; + buf = grub_malloc (sz); + if (!buf) + return grub_errno; + + if (grub_file_read (elf->file, buf, sz) != (grub_ssize_t) sz) + { + if (grub_errno) + return grub_errno; + return grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), + elf->file->name); + } + xi->has_xen_guest = 1; + for (ptr = buf; ptr && ptr - buf < (grub_ssize_t) sz; + ptr = grub_strchr (ptr, ','), (ptr ? ptr++ : 0)) + { + if (grub_strncmp (ptr, "PAE=no,", sizeof ("PAE=no,") - 1) == 0) + { + if (xi->arch != GRUB_XEN_FILE_I386 + && xi->arch != GRUB_XEN_FILE_I386_PAE + && xi->arch != GRUB_XEN_FILE_I386_PAE_BIMODE) + continue; + xi->arch = GRUB_XEN_FILE_I386; + continue; + } + + if (grub_strncmp (ptr, "PAE=yes,", sizeof ("PAE=yes,") - 1) == 0) + { + if (xi->arch != GRUB_XEN_FILE_I386 + && xi->arch != GRUB_XEN_FILE_I386_PAE + && xi->arch != GRUB_XEN_FILE_I386_PAE_BIMODE) + continue; + xi->arch = GRUB_XEN_FILE_I386_PAE; + continue; + } + + if (grub_strncmp (ptr, "PAE=yes[extended-cr3],", + sizeof ("PAE=yes[extended-cr3],") - 1) == 0) + { + if (xi->arch != GRUB_XEN_FILE_I386 + && xi->arch != GRUB_XEN_FILE_I386_PAE + && xi->arch != GRUB_XEN_FILE_I386_PAE_BIMODE) + continue; + xi->arch = GRUB_XEN_FILE_I386_PAE; + xi->extended_cr3 = 1; + continue; + } + + if (grub_strncmp (ptr, "PAE=bimodal,", sizeof ("PAE=bimodal,") - 1) == 0) + { + if (xi->arch != GRUB_XEN_FILE_I386 + && xi->arch != GRUB_XEN_FILE_I386_PAE + && xi->arch != GRUB_XEN_FILE_I386_PAE_BIMODE) + continue; + xi->arch = GRUB_XEN_FILE_I386_PAE_BIMODE; + continue; + } + + if (grub_strncmp (ptr, "PAE=bimodal[extended-cr3],", + sizeof ("PAE=bimodal[extended-cr3],") - 1) == 0) + { + if (xi->arch != GRUB_XEN_FILE_I386 + && xi->arch != GRUB_XEN_FILE_I386_PAE + && xi->arch != GRUB_XEN_FILE_I386_PAE_BIMODE) + continue; + xi->arch = GRUB_XEN_FILE_I386_PAE_BIMODE; + xi->extended_cr3 = 1; + continue; + } + + if (grub_strncmp (ptr, "PAE=yes,bimodal,", sizeof ("PAE=yes,bimodal,") - 1) == 0) + { + if (xi->arch != GRUB_XEN_FILE_I386 + && xi->arch != GRUB_XEN_FILE_I386_PAE + && xi->arch != GRUB_XEN_FILE_I386_PAE_BIMODE) + continue; + xi->arch = GRUB_XEN_FILE_I386_PAE_BIMODE; + continue; + } + + if (grub_strncmp (ptr, "PAE=yes[extended-cr3],bimodal,", + sizeof ("PAE=yes[extended-cr3],bimodal,") - 1) == 0) + { + if (xi->arch != GRUB_XEN_FILE_I386 + && xi->arch != GRUB_XEN_FILE_I386_PAE + && xi->arch != GRUB_XEN_FILE_I386_PAE_BIMODE) + continue; + xi->arch = GRUB_XEN_FILE_I386_PAE_BIMODE; + xi->extended_cr3 = 1; + continue; + } + + if (grub_strncmp (ptr, "VIRT_BASE=", sizeof ("VIRT_BASE=") - 1) == 0) + { + xi->virt_base = grub_strtoull (ptr + sizeof ("VIRT_BASE=") - 1, &ptr, 16); + if (grub_errno) + return grub_errno; + continue; + } + if (grub_strncmp (ptr, "VIRT_ENTRY=", sizeof ("VIRT_ENTRY=") - 1) == 0) + { + xi->entry_point = grub_strtoull (ptr + sizeof ("VIRT_ENTRY=") - 1, &ptr, 16); + if (grub_errno) + return grub_errno; + continue; + } + if (grub_strncmp (ptr, "HYPERCALL_PAGE=", sizeof ("HYPERCALL_PAGE=") - 1) == 0) + { + xi->hypercall_page = grub_strtoull (ptr + sizeof ("HYPERCALL_PAGE=") - 1, &ptr, 16); + xi->has_hypercall_page = 1; + if (grub_errno) + return grub_errno; + continue; + } + if (grub_strncmp (ptr, "ELF_PADDR_OFFSET=", sizeof ("ELF_PADDR_OFFSET=") - 1) == 0) + { + xi->paddr_offset = grub_strtoull (ptr + sizeof ("ELF_PADDR_OFFSET=") - 1, &ptr, 16); + has_paddr = 1; + if (grub_errno) + return grub_errno; + continue; + } + } + if (xi->has_hypercall_page) + xi->hypercall_page = (xi->hypercall_page << 12) + xi->virt_base; + if (!has_paddr) + xi->paddr_offset = xi->virt_base; + return GRUB_ERR_NONE; +} + +static grub_err_t +parse_note (grub_elf_t elf, struct grub_xen_file_info *xi, + grub_off_t off, grub_size_t sz) +{ + grub_uint32_t *buf; + grub_uint32_t *ptr; + if (grub_file_seek (elf->file, off) == (grub_off_t) -1) + return grub_errno; + buf = grub_malloc (sz); + if (!buf) + return grub_errno; + + if (grub_file_read (elf->file, buf, sz) != (grub_ssize_t) sz) + { + if (grub_errno) + return grub_errno; + return grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), + elf->file->name); + } + for (ptr = buf; ptr - buf < (grub_ssize_t) (sz / sizeof (grub_uint32_t));) + { + Elf_Nhdr *nh = (Elf_Nhdr *) ptr; + char *name; + grub_uint32_t *desc; + grub_uint32_t namesz, descsz; + ptr += sizeof (*nh) / sizeof (grub_uint32_t); + name = (char *) ptr; + namesz = grub_le_to_cpu32 (nh->n_namesz); + descsz = grub_le_to_cpu32 (nh->n_descsz); + ptr += (namesz + 3) / 4; + desc = ptr; + ptr += (grub_le_to_cpu32 (nh->n_descsz) + 3) / 4; + if ((namesz < 3) || grub_memcmp (name, "Xen", namesz == 3 ? 3 : 4) != 0) + continue; + xi->has_note = 1; + switch (nh->n_type) + { + case 1: + xi->entry_point = grub_le_to_cpu_addr (*(Elf_Addr *) desc); + break; + case 2: + xi->hypercall_page = grub_le_to_cpu_addr (*(Elf_Addr *) desc); + xi->has_hypercall_page = 1; + break; + case 3: + xi->virt_base = grub_le_to_cpu_addr (*(Elf_Addr *) desc); + break; + case 4: + xi->paddr_offset = grub_le_to_cpu_addr (*(Elf_Addr *) desc); + break; + case 5: + grub_dprintf ("xen", "xenversion = `%s'\n", (char *) desc); + break; + case 6: + grub_dprintf ("xen", "name = `%s'\n", (char *) desc); + break; + case 7: + grub_dprintf ("xen", "version = `%s'\n", (char *) desc); + break; + case 8: + if (descsz < 7 + || grub_memcmp (desc, "generic", descsz == 7 ? 7 : 8) != 0) + return grub_error (GRUB_ERR_BAD_OS, "invalid loader"); + break; + /* PAE */ + case 9: + grub_dprintf ("xen", "pae = `%s', %d, %d\n", (char *) desc, + xi->arch, descsz); + if (xi->arch != GRUB_XEN_FILE_I386 + && xi->arch != GRUB_XEN_FILE_I386_PAE + && xi->arch != GRUB_XEN_FILE_I386_PAE_BIMODE) + break; + if (descsz >= 3 && grub_memcmp (desc, "yes", + descsz == 3 ? 3 : 4) == 0) + { + xi->extended_cr3 = 1; + xi->arch = GRUB_XEN_FILE_I386_PAE; + } + if (descsz >= 7 && grub_memcmp (desc, "bimodal", + descsz == 7 ? 7 : 8) == 0) + { + xi->extended_cr3 = 1; + xi->arch = GRUB_XEN_FILE_I386_PAE_BIMODE; + } + if (descsz >= 11 && grub_memcmp (desc, "yes,bimodal", + descsz == 11 ? 11 : 12) == 0) + { + xi->extended_cr3 = 1; + xi->arch = GRUB_XEN_FILE_I386_PAE_BIMODE; + } + if (descsz >= 2 && grub_memcmp (desc, "no", + descsz == 2 ? 2 : 3) == 0) + xi->arch = GRUB_XEN_FILE_I386; + break; + default: + grub_dprintf ("xen", "unknown note type %d\n", nh->n_type); + break; + } + } + return GRUB_ERR_NONE; +} + +grub_err_t +grub_xen_get_infoXX (grub_elf_t elf, struct grub_xen_file_info *xi) +{ + Elf_Shdr *s, *s0; + grub_size_t shnum = elf->ehdr.ehdrXX.e_shnum; + grub_size_t shentsize = elf->ehdr.ehdrXX.e_shentsize; + grub_size_t shsize = shnum * shentsize; + grub_off_t stroff; + grub_err_t err; + Elf_Phdr *phdr; + + xi->kern_end = 0; + xi->kern_start = ~0; + xi->entry_point = elf->ehdr.ehdrXX.e_entry; + + /* FIXME: check note. */ + FOR_ELF_PHDRS (elf, phdr) + { + Elf_Addr paddr; + + if (phdr->p_type == PT_NOTE) + { + err = parse_note (elf, xi, phdr->p_offset, phdr->p_filesz); + if (err) + return err; + } + + if (phdr->p_type != PT_LOAD) + continue; + + paddr = phdr->p_paddr; + + if (paddr < xi->kern_start) + xi->kern_start = paddr; + + if (paddr + phdr->p_memsz > xi->kern_end) + xi->kern_end = paddr + phdr->p_memsz; + } + + if (xi->has_note) + return GRUB_ERR_NONE; + + if (!shnum || !shentsize) + return grub_error (GRUB_ERR_BAD_OS, "no XEN note"); + + s0 = grub_malloc (shsize); + if (!s0) + return grub_errno; + + if (grub_file_seek (elf->file, elf->ehdr.ehdrXX.e_shoff) == (grub_off_t) -1) + return grub_errno; + + if (grub_file_read (elf->file, s0, shsize) != (grub_ssize_t) shsize) + { + if (grub_errno) + return grub_errno; + return grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), + elf->file->name); + } + + s = (Elf_Shdr *) ((char *) s0 + elf->ehdr.ehdrXX.e_shstrndx * shentsize); + stroff = s->sh_offset; + + for (s = s0; s < (Elf_Shdr *) ((char *) s0 + shnum * shentsize); + s = (Elf_Shdr *) ((char *) s + shentsize)) + { + char name[sizeof("__xen_guest")]; + grub_memset (name, 0, sizeof (name)); + if (grub_file_seek (elf->file, stroff + s->sh_name) == (grub_off_t) -1) + return grub_errno; + + if (grub_file_read (elf->file, name, sizeof (name)) != (grub_ssize_t) sizeof (name)) + { + if (grub_errno) + return grub_errno; + continue; + } + if (grub_memcmp (name, "__xen_guest", + sizeof("__xen_guest")) != 0) + continue; + return parse_xen_guest (elf, xi, s->sh_offset, s->sh_size); + } + return grub_error (GRUB_ERR_BAD_OS, "no XEN note found"); +} diff --git a/grub-core/term/xen/console.c b/grub-core/term/xen/console.c new file mode 100644 index 000000000..a1f15f71a --- /dev/null +++ b/grub-core/term/xen/console.c @@ -0,0 +1,122 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2011 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +static int +readkey (struct grub_term_input *term __attribute__ ((unused))) +{ + grub_size_t prod, cons; + int r; + mb (); + prod = grub_xen_xcons->in_prod; + cons = grub_xen_xcons->in_cons; + if (prod <= cons) + return -1; + r = grub_xen_xcons->in[cons]; + cons++; + mb (); + grub_xen_xcons->in_cons = cons; + return r; +} + +static int signal_sent = 1; + +static void +refresh (struct grub_term_output *term __attribute__ ((unused))) +{ + struct evtchn_send send; + send.port = grub_xen_start_page_addr->console.domU.evtchn; + grub_xen_event_channel_op (EVTCHNOP_send, &send); + signal_sent = 1; + while (grub_xen_xcons->out_prod != grub_xen_xcons->out_cons) + { + grub_xen_sched_op (SCHEDOP_yield, 0); + } +} + +static void +put (struct grub_term_output *term __attribute__ ((unused)), const int c) +{ + grub_size_t prod, cons; + + while (1) + { + mb (); + prod = grub_xen_xcons->out_prod; + cons = grub_xen_xcons->out_cons; + if (prod < cons + sizeof (grub_xen_xcons->out)) + break; + if (!signal_sent) + refresh (term); + grub_xen_sched_op (SCHEDOP_yield, 0); + } + grub_xen_xcons->out[prod++ & (sizeof (grub_xen_xcons->out) - 1)] = c; + mb (); + grub_xen_xcons->out_prod = prod; + signal_sent = 0; +} + + +struct grub_terminfo_input_state grub_console_terminfo_input = { + .readkey = readkey +}; + +struct grub_terminfo_output_state grub_console_terminfo_output = { + .put = put, + .size = {80, 24} +}; + +static struct grub_term_input grub_console_term_input = { + .name = "console", + .init = 0, + .getkey = grub_terminfo_getkey, + .data = &grub_console_terminfo_input +}; + +static struct grub_term_output grub_console_term_output = { + .name = "console", + .init = 0, + .putchar = grub_terminfo_putchar, + .getxy = grub_terminfo_getxy, + .getwh = grub_terminfo_getwh, + .gotoxy = grub_terminfo_gotoxy, + .cls = grub_terminfo_cls, + .refresh = refresh, + .setcolorstate = grub_terminfo_setcolorstate, + .setcursor = grub_terminfo_setcursor, + .flags = GRUB_TERM_CODE_TYPE_ASCII, + .data = &grub_console_terminfo_output, +}; + + +void +grub_console_init (void) +{ + grub_term_register_input ("console", &grub_console_term_input); + grub_term_register_output ("console", &grub_console_term_output); + + grub_terminfo_init (); + grub_terminfo_output_register (&grub_console_term_output, "vt100-color"); +} diff --git a/include/grub/disk.h b/include/grub/disk.h index 3e61c9642..b385af826 100644 --- a/include/grub/disk.h +++ b/include/grub/disk.h @@ -48,6 +48,7 @@ enum grub_disk_dev_id GRUB_DISK_DEVICE_PROCFS_ID, GRUB_DISK_DEVICE_CBFSDISK_ID, GRUB_DISK_DEVICE_UBOOTDISK_ID, + GRUB_DISK_DEVICE_XEN, }; struct grub_disk; diff --git a/include/grub/elf.h b/include/grub/elf.h index 708cd6a95..f64d6a891 100644 --- a/include/grub/elf.h +++ b/include/grub/elf.h @@ -2454,7 +2454,9 @@ typedef Elf32_Addr Elf32_Conflict; #if GRUB_TARGET_WORDSIZE == 32 typedef Elf32_Addr Elf_Addr; +typedef Elf32_Nhdr Elf_Nhdr; typedef Elf32_Ehdr Elf_Ehdr; +typedef Elf32_Phdr Elf_Phdr; typedef Elf32_Half Elf_Half; typedef Elf32_Off Elf_Off; typedef Elf32_Rel Elf_Rel; @@ -2477,7 +2479,9 @@ typedef Elf32_Xword Elf_Xword; #elif GRUB_TARGET_WORDSIZE == 64 typedef Elf64_Addr Elf_Addr; +typedef Elf64_Nhdr Elf_Nhdr; typedef Elf64_Ehdr Elf_Ehdr; +typedef Elf64_Phdr Elf_Phdr; typedef Elf64_Half Elf_Half; typedef Elf64_Off Elf_Off; typedef Elf64_Rel Elf_Rel; diff --git a/include/grub/file.h b/include/grub/file.h index f8ae8d9a8..739488cbe 100644 --- a/include/grub/file.h +++ b/include/grub/file.h @@ -158,4 +158,10 @@ grub_file_seekable (const grub_file_t file) return !file->not_easily_seekable; } +grub_file_t +grub_file_offset_open (grub_file_t parent, grub_off_t start, + grub_off_t size); +void +grub_file_offset_close (grub_file_t file); + #endif /* ! GRUB_FILE_HEADER */ diff --git a/include/grub/i386/xen/hypercall.h b/include/grub/i386/xen/hypercall.h new file mode 100644 index 000000000..0152b2b2f --- /dev/null +++ b/include/grub/i386/xen/hypercall.h @@ -0,0 +1,85 @@ +/* + * 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 . + */ + +#ifndef GRUB_XEN_CPU_HYPERCALL_HEADER +#define GRUB_XEN_CPU_HYPERCALL_HEADER 1 + +#include + +int +EXPORT_FUNC (grub_xen_hypercall) (grub_uint32_t callno, grub_uint32_t a0, + grub_uint32_t a1, grub_uint32_t a2, + grub_uint32_t a3, grub_uint32_t a4, + grub_uint32_t a5) +__attribute__ ((regparm (3), stdcall)); + +static inline int +grub_xen_sched_op (int cmd, void *arg) +{ + return grub_xen_hypercall (__HYPERVISOR_sched_op, cmd, (grub_uint32_t) arg, + 0, 0, 0, 0); +} + +static inline int +grub_xen_mmu_update (const struct mmu_update *reqs, + unsigned count, unsigned *done_out, unsigned foreigndom) +{ + return grub_xen_hypercall (__HYPERVISOR_mmu_update, (grub_uint32_t) reqs, + (grub_uint32_t) count, (grub_uint32_t) done_out, + (grub_uint32_t) foreigndom, 0, 0); +} + +static inline int +grub_xen_mmuext_op (mmuext_op_t * ops, + unsigned int count, + unsigned int *pdone, unsigned int foreigndom) +{ + return grub_xen_hypercall (__HYPERVISOR_mmuext_op, (grub_uint32_t) ops, + count, (grub_uint32_t) pdone, foreigndom, 0, 0); +} + +static inline int +grub_xen_event_channel_op (int op, void *arg) +{ + return grub_xen_hypercall (__HYPERVISOR_event_channel_op, op, + (grub_uint32_t) arg, 0, 0, 0, 0); +} + + +static inline int +grub_xen_update_va_mapping (void *addr, uint64_t pte, uint32_t flags) +{ + return grub_xen_hypercall (__HYPERVISOR_update_va_mapping, + (grub_uint32_t) addr, pte, pte >> 32, flags, 0, + 0); +} + +static inline int +grub_xen_grant_table_op (int a, void *b, int c) +{ + return grub_xen_hypercall (__HYPERVISOR_grant_table_op, a, + (grub_uint32_t) b, c, 0, 0, 0); +} + +static inline int +grub_xen_vm_assist (int cmd, int type) +{ + return grub_xen_hypercall (__HYPERVISOR_vm_assist, cmd, type, 0, 0, 0, 0); +} + +#endif diff --git a/include/grub/i386/xen/memory.h b/include/grub/i386/xen/memory.h new file mode 100644 index 000000000..e69de29bb diff --git a/include/grub/kernel.h b/include/grub/kernel.h index 52105fcce..a5067ef79 100644 --- a/include/grub/kernel.h +++ b/include/grub/kernel.h @@ -78,7 +78,7 @@ struct grub_module_info64 #if defined (GRUB_MACHINE_PCBIOS) || defined (GRUB_MACHINE_COREBOOT) \ || defined (GRUB_MACHINE_MULTIBOOT) || defined (GRUB_MACHINE_MIPS_QEMU_MIPS) \ || defined (GRUB_MACHINE_MIPS_LOONGSON) || defined (GRUB_MACHINE_ARC) \ - || defined (__sparc__) || defined (GRUB_MACHINE_UBOOT) + || defined (__sparc__) || defined (GRUB_MACHINE_UBOOT) || defined (GRUB_MACHINE_XEN) /* FIXME: stack is between 2 heap regions. Move it. */ #define GRUB_KERNEL_PRELOAD_SPACE_REUSABLE 1 #endif @@ -114,7 +114,7 @@ void grub_main (void) __attribute__ ((noreturn)); void grub_machine_init (void); /* The machine-specific finalization. */ -void EXPORT_FUNC(grub_machine_fini) (void); +void EXPORT_FUNC(grub_machine_fini) (int flags); /* The machine-specific prefix initialization. */ void diff --git a/include/grub/offsets.h b/include/grub/offsets.h index 08e490c96..0a7b89a6d 100644 --- a/include/grub/offsets.h +++ b/include/grub/offsets.h @@ -99,7 +99,12 @@ #define GRUB_KERNEL_I386_COREBOOT_MOD_ALIGN 0x1 #define GRUB_KERNEL_I386_MULTIBOOT_MOD_ALIGN GRUB_KERNEL_I386_COREBOOT_MOD_ALIGN +#define GRUB_KERNEL_X86_64_XEN_MOD_ALIGN 0x8 +#define GRUB_KERNEL_I386_XEN_MOD_ALIGN 0x8 + /* Non-zero value is only needed for PowerMacs. */ +#define GRUB_KERNEL_X86_64_XEN_MOD_GAP 0x0 +#define GRUB_KERNEL_I386_XEN_MOD_GAP 0x0 #define GRUB_KERNEL_I386_IEEE1275_MOD_GAP 0x0 #define GRUB_KERNEL_I386_COREBOOT_MOD_GAP 0x0 #define GRUB_KERNEL_SPARC64_IEEE1275_MOD_GAP 0x0 diff --git a/include/grub/x86_64/xen/hypercall.h b/include/grub/x86_64/xen/hypercall.h new file mode 100644 index 000000000..780db4d61 --- /dev/null +++ b/include/grub/x86_64/xen/hypercall.h @@ -0,0 +1,34 @@ +/* + * 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 . + */ + +#ifndef GRUB_XEN_CPU_HYPERCALL_HEADER +#define GRUB_XEN_CPU_HYPERCALL_HEADER 1 + +int EXPORT_FUNC (grub_xen_sched_op) (int cmd, void *arg); +int grub_xen_update_va_mapping (void *addr, uint64_t pte, uint64_t flags); +int grub_xen_event_channel_op (int op, void *arg); + +int grub_xen_mmuext_op (mmuext_op_t * ops, + unsigned int count, + unsigned int *pdone, unsigned int foreigndom); +int EXPORT_FUNC (grub_xen_mmu_update) (const struct mmu_update * reqs, + unsigned count, unsigned *done_out, + unsigned foreigndom); +int EXPORT_FUNC (grub_xen_grant_table_op) (int, void *, int); + +#endif diff --git a/include/grub/xen.h b/include/grub/xen.h new file mode 100644 index 000000000..062c95d9f --- /dev/null +++ b/include/grub/xen.h @@ -0,0 +1,93 @@ +/* + * 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 . + */ + +#ifndef GRUB_XEN_HEADER +#define GRUB_XEN_HEADER 1 + +#define __XEN_INTERFACE_VERSION__ 0x0003020a + +#ifdef ASM_FILE +#define __ASSEMBLY__ +#include +#else + +#include +#include +#include + +#ifndef GRUB_SYMBOL_GENERATOR +#include +#include + +#include +#include +#include +#include +#include +#include +#endif + +#include + +extern grub_size_t EXPORT_VAR (grub_xen_n_allocated_shared_pages); + + +#define GRUB_XEN_LOG_PAGE_SIZE 12 +#define GRUB_XEN_PAGE_SIZE (1 << GRUB_XEN_LOG_PAGE_SIZE) + +extern volatile struct xencons_interface *grub_xen_xcons; +extern volatile struct shared_info *EXPORT_VAR (grub_xen_shared_info); +extern volatile struct xenstore_domain_interface *grub_xen_xenstore; +extern volatile grant_entry_v2_t *grub_xen_grant_table; + +void EXPORT_FUNC (grub_xen_store_send) (const void *buf_, grub_size_t len); +void EXPORT_FUNC (grub_xen_store_recv) (void *buf_, grub_size_t len); +grub_err_t +EXPORT_FUNC (grub_xenstore_dir) (const char *dir, + int (*hook) (const char *dir, + void *hook_data), + void *hook_data); +void *EXPORT_FUNC (grub_xenstore_get_file) (const char *dir, + grub_size_t * len); +grub_err_t EXPORT_FUNC (grub_xenstore_write_file) (const char *dir, + const void *buf, + grub_size_t len); + +typedef unsigned int grub_xen_grant_t; + +void *EXPORT_FUNC (grub_xen_alloc_shared_page) (domid_t dom, + grub_xen_grant_t * grnum); +void EXPORT_FUNC (grub_xen_free_shared_page) (void *ptr); + +#define mb() asm volatile("mfence;sfence;" : : : "memory"); +extern struct start_info *EXPORT_VAR (grub_xen_start_page_addr); + +void grub_console_init (void); + +void grub_xendisk_fini (void); +void grub_xendisk_init (void); + +#ifdef __x86_64__ +typedef grub_uint64_t grub_xen_mfn_t; +#else +typedef grub_uint32_t grub_xen_mfn_t; +#endif +typedef unsigned int grub_xen_evtchn_t; +#endif + +#endif diff --git a/include/grub/xen/relocator.h b/include/grub/xen/relocator.h new file mode 100644 index 000000000..ae45dce21 --- /dev/null +++ b/include/grub/xen/relocator.h @@ -0,0 +1,44 @@ +/* + * 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 . + */ + +#ifndef GRUB_RELOCATOR_XEN_HEADER +#define GRUB_RELOCATOR_XEN_HEADER 1 + +#include +#include +#include + +struct grub_relocator_xen_state +{ + grub_addr_t start_info; + grub_addr_t paging_start; + grub_addr_t paging_size; + grub_addr_t mfn_list; + grub_addr_t stack; + grub_addr_t entry_point; +}; + +grub_err_t +grub_relocator_xen_boot (struct grub_relocator *rel, + struct grub_relocator_xen_state state, + grub_uint64_t remapper_pfn, + grub_addr_t remapper_virt, + grub_uint64_t trampoline_pfn, + grub_addr_t trampoline_virt); + +#endif diff --git a/include/grub/xen_file.h b/include/grub/xen_file.h new file mode 100644 index 000000000..4b2ccba78 --- /dev/null +++ b/include/grub/xen_file.h @@ -0,0 +1,54 @@ +/* + * 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 . + */ + +#ifndef GRUB_XEN_FILE_HEADER +#define GRUB_XEN_FILE_HEADER 1 + +#include +#include +#include + +grub_elf_t grub_xen_file (grub_file_t file); + +struct grub_xen_file_info +{ + grub_uint64_t kern_start, kern_end; + grub_uint64_t virt_base; + grub_uint64_t entry_point; + grub_uint64_t hypercall_page; + grub_uint64_t paddr_offset; + int has_hypercall_page; + int has_note; + int has_xen_guest; + int extended_cr3; + enum + { + GRUB_XEN_FILE_I386 = 1, + GRUB_XEN_FILE_I386_PAE = 2, + GRUB_XEN_FILE_I386_PAE_BIMODE = 3, + GRUB_XEN_FILE_X86_64 = 4 + } arch; +}; + +grub_err_t +grub_xen_get_info32 (grub_elf_t elf, struct grub_xen_file_info *xi); +grub_err_t +grub_xen_get_info64 (grub_elf_t elf, struct grub_xen_file_info *xi); +grub_err_t grub_xen_get_info (grub_elf_t elf, struct grub_xen_file_info *xi); + +#endif diff --git a/util/grub-mkimagexx.c b/util/grub-mkimagexx.c index fea553c14..acfa71f1d 100644 --- a/util/grub-mkimagexx.c +++ b/util/grub-mkimagexx.c @@ -25,6 +25,7 @@ # define ELFCLASSXX ELFCLASS32 # define Elf_Ehdr Elf32_Ehdr # define Elf_Phdr Elf32_Phdr +# define Elf_Nhdr Elf32_Nhdr # define Elf_Addr Elf32_Addr # define Elf_Sym Elf32_Sym # define Elf_Off Elf32_Off @@ -37,11 +38,13 @@ # define ELF_R_SYM(val) ELF32_R_SYM(val) # define ELF_R_TYPE(val) ELF32_R_TYPE(val) # define ELF_ST_TYPE(val) ELF32_ST_TYPE(val) +#define XEN_NOTE_SIZE 132 #elif defined(MKIMAGE_ELF64) # define SUFFIX(x) x ## 64 # define ELFCLASSXX ELFCLASS64 # define Elf_Ehdr Elf64_Ehdr # define Elf_Phdr Elf64_Phdr +# define Elf_Nhdr Elf64_Nhdr # define Elf_Addr Elf64_Addr # define Elf_Sym Elf64_Sym # define Elf_Off Elf64_Off @@ -54,12 +57,315 @@ # define ELF_R_SYM(val) ELF64_R_SYM(val) # define ELF_R_TYPE(val) ELF64_R_TYPE(val) # define ELF_ST_TYPE(val) ELF64_ST_TYPE(val) +#define XEN_NOTE_SIZE 120 #else #error "I'm confused" #endif static Elf_Addr SUFFIX (entry_point); +static void +SUFFIX (generate_elf) (const struct grub_install_image_target_desc *image_target, + int note, char **core_img, size_t *core_size, + Elf_Addr target_addr, grub_size_t align, + size_t kernel_size, size_t bss_size) +{ + char *elf_img; + size_t program_size; + Elf_Ehdr *ehdr; + Elf_Phdr *phdr; + Elf_Shdr *shdr; + int header_size, footer_size = 0; + int phnum = 1; + int shnum = 4; + int string_size = sizeof (".text") + sizeof ("mods") + 1; + + if (image_target->id != IMAGE_LOONGSON_ELF) + phnum += 2; + + if (note) + { + phnum++; + footer_size += sizeof (struct grub_ieee1275_note); + } + if (image_target->id == IMAGE_XEN) + { + phnum++; + shnum++; + string_size += sizeof (".xen"); + footer_size += XEN_NOTE_SIZE; + } + header_size = ALIGN_UP (sizeof (*ehdr) + phnum * sizeof (*phdr) + + shnum * sizeof (*shdr) + string_size, align); + + program_size = ALIGN_ADDR (*core_size); + + elf_img = xmalloc (program_size + header_size + footer_size); + memset (elf_img, 0, program_size + header_size); + memcpy (elf_img + header_size, *core_img, *core_size); + ehdr = (void *) elf_img; + phdr = (void *) (elf_img + sizeof (*ehdr)); + shdr = (void *) (elf_img + sizeof (*ehdr) + phnum * sizeof (*phdr)); + memcpy (ehdr->e_ident, ELFMAG, SELFMAG); + ehdr->e_ident[EI_CLASS] = ELFCLASSXX; + if (!image_target->bigendian) + ehdr->e_ident[EI_DATA] = ELFDATA2LSB; + else + ehdr->e_ident[EI_DATA] = ELFDATA2MSB; + ehdr->e_ident[EI_VERSION] = EV_CURRENT; + ehdr->e_ident[EI_OSABI] = ELFOSABI_NONE; + ehdr->e_type = grub_host_to_target16 (ET_EXEC); + ehdr->e_machine = grub_host_to_target16 (image_target->elf_target); + ehdr->e_version = grub_host_to_target32 (EV_CURRENT); + + ehdr->e_phoff = grub_host_to_target32 ((char *) phdr - (char *) ehdr); + ehdr->e_phentsize = grub_host_to_target16 (sizeof (*phdr)); + ehdr->e_phnum = grub_host_to_target16 (phnum); + + ehdr->e_shoff = grub_host_to_target32 ((grub_uint8_t *) shdr + - (grub_uint8_t *) ehdr); + if (image_target->id == IMAGE_LOONGSON_ELF) + ehdr->e_shentsize = grub_host_to_target16 (0); + else + ehdr->e_shentsize = grub_host_to_target16 (sizeof (Elf_Shdr)); + ehdr->e_shnum = grub_host_to_target16 (shnum); + ehdr->e_shstrndx = grub_host_to_target16 (1); + + ehdr->e_ehsize = grub_host_to_target16 (sizeof (*ehdr)); + + phdr->p_type = grub_host_to_target32 (PT_LOAD); + phdr->p_offset = grub_host_to_target32 (header_size); + phdr->p_flags = grub_host_to_target32 (PF_R | PF_W | PF_X); + + ehdr->e_entry = grub_host_to_target32 (target_addr); + phdr->p_vaddr = grub_host_to_target32 (target_addr); + phdr->p_paddr = grub_host_to_target32 (target_addr); + phdr->p_align = grub_host_to_target32 (align > image_target->link_align ? align : image_target->link_align); + if (image_target->id == IMAGE_LOONGSON_ELF) + ehdr->e_flags = grub_host_to_target32 (0x1000 | EF_MIPS_NOREORDER + | EF_MIPS_PIC | EF_MIPS_CPIC); + else + ehdr->e_flags = 0; + if (image_target->id == IMAGE_LOONGSON_ELF) + { + phdr->p_filesz = grub_host_to_target32 (*core_size); + phdr->p_memsz = grub_host_to_target32 (*core_size); + } + else + { + grub_uint32_t target_addr_mods; + phdr->p_filesz = grub_host_to_target32 (kernel_size); + phdr->p_memsz = grub_host_to_target32 (kernel_size + bss_size); + + phdr++; + phdr->p_type = grub_host_to_target32 (PT_GNU_STACK); + phdr->p_offset = grub_host_to_target32 (header_size + kernel_size); + phdr->p_paddr = phdr->p_vaddr = phdr->p_filesz = phdr->p_memsz = 0; + phdr->p_flags = grub_host_to_target32 (PF_R | PF_W | PF_X); + phdr->p_align = grub_host_to_target32 (image_target->link_align); + + phdr++; + phdr->p_type = grub_host_to_target32 (PT_LOAD); + phdr->p_offset = grub_host_to_target32 (header_size + kernel_size); + phdr->p_flags = grub_host_to_target32 (PF_R | PF_W | PF_X); + phdr->p_filesz = phdr->p_memsz + = grub_host_to_target32 (*core_size - kernel_size); + + target_addr_mods = ALIGN_UP (target_addr + kernel_size + bss_size + + image_target->mod_gap, + image_target->mod_align); + phdr->p_vaddr = grub_host_to_target_addr (target_addr_mods); + phdr->p_paddr = grub_host_to_target_addr (target_addr_mods); + phdr->p_align = grub_host_to_target32 (image_target->link_align); + } + + if (image_target->id == IMAGE_XEN) + { + char *note_start = (elf_img + program_size + header_size); + Elf_Nhdr *note_ptr; + char *ptr = (char *) note_start; + + grub_util_info ("adding XEN NOTE segment"); + + /* Guest OS. */ + note_ptr = (Elf_Nhdr *) ptr; + note_ptr->n_namesz = grub_host_to_target32 (sizeof (GRUB_XEN_NOTE_NAME)); + note_ptr->n_descsz = grub_host_to_target32 (sizeof (PACKAGE_NAME)); + note_ptr->n_type = grub_host_to_target32 (6); + ptr += sizeof (Elf_Nhdr); + memcpy (ptr, GRUB_XEN_NOTE_NAME, sizeof (GRUB_XEN_NOTE_NAME)); + ptr += ALIGN_UP (sizeof (GRUB_XEN_NOTE_NAME), 4); + memcpy (ptr, PACKAGE_NAME, sizeof (PACKAGE_NAME)); + ptr += ALIGN_UP (sizeof (PACKAGE_NAME), 4); + + /* Loader. */ + note_ptr = (Elf_Nhdr *) ptr; + note_ptr->n_namesz = grub_host_to_target32 (sizeof (GRUB_XEN_NOTE_NAME)); + note_ptr->n_descsz = grub_host_to_target32 (sizeof ("generic")); + note_ptr->n_type = grub_host_to_target32 (8); + ptr += sizeof (Elf_Nhdr); + memcpy (ptr, GRUB_XEN_NOTE_NAME, sizeof (GRUB_XEN_NOTE_NAME)); + ptr += ALIGN_UP (sizeof (GRUB_XEN_NOTE_NAME), 4); + memcpy (ptr, "generic", sizeof ("generic")); + ptr += ALIGN_UP (sizeof ("generic"), 4); + + /* Version. */ + note_ptr = (Elf_Nhdr *) ptr; + note_ptr->n_namesz = grub_host_to_target32 (sizeof (GRUB_XEN_NOTE_NAME)); + note_ptr->n_descsz = grub_host_to_target32 (sizeof ("xen-3.0")); + note_ptr->n_type = grub_host_to_target32 (5); + ptr += sizeof (Elf_Nhdr); + memcpy (ptr, GRUB_XEN_NOTE_NAME, sizeof (GRUB_XEN_NOTE_NAME)); + ptr += ALIGN_UP (sizeof (GRUB_XEN_NOTE_NAME), 4); + memcpy (ptr, "xen-3.0", sizeof ("xen-3.0")); + ptr += ALIGN_UP (sizeof ("xen-3.0"), 4); + + /* Entry. */ + note_ptr = (Elf_Nhdr *) ptr; + note_ptr->n_namesz = grub_host_to_target32 (sizeof (GRUB_XEN_NOTE_NAME)); + note_ptr->n_descsz = grub_host_to_target32 (image_target->voidp_sizeof); + note_ptr->n_type = grub_host_to_target32 (1); + ptr += sizeof (Elf_Nhdr); + memcpy (ptr, GRUB_XEN_NOTE_NAME, sizeof (GRUB_XEN_NOTE_NAME)); + ptr += ALIGN_UP (sizeof (GRUB_XEN_NOTE_NAME), 4); + memset (ptr, 0, image_target->voidp_sizeof); + ptr += image_target->voidp_sizeof; + + /* Virt base. */ + note_ptr = (Elf_Nhdr *) ptr; + note_ptr->n_namesz = grub_host_to_target32 (sizeof (GRUB_XEN_NOTE_NAME)); + note_ptr->n_descsz = grub_host_to_target32 (image_target->voidp_sizeof); + note_ptr->n_type = grub_host_to_target32 (3); + ptr += sizeof (Elf_Nhdr); + memcpy (ptr, GRUB_XEN_NOTE_NAME, sizeof (GRUB_XEN_NOTE_NAME)); + ptr += ALIGN_UP (sizeof (GRUB_XEN_NOTE_NAME), 4); + memset (ptr, 0, image_target->voidp_sizeof); + ptr += image_target->voidp_sizeof; + + /* PAE. */ + if (image_target->elf_target == EM_386) + { + note_ptr = (Elf_Nhdr *) ptr; + note_ptr->n_namesz = grub_host_to_target32 (sizeof (GRUB_XEN_NOTE_NAME)); + note_ptr->n_descsz = grub_host_to_target32 (sizeof ("yes,bimodal")); + note_ptr->n_type = grub_host_to_target32 (9); + ptr += sizeof (Elf_Nhdr); + memcpy (ptr, GRUB_XEN_NOTE_NAME, sizeof (GRUB_XEN_NOTE_NAME)); + ptr += ALIGN_UP (sizeof (GRUB_XEN_NOTE_NAME), 4); + memcpy (ptr, "yes", sizeof ("yes")); + ptr += ALIGN_UP (sizeof ("yes"), 4); + } + + assert (XEN_NOTE_SIZE == (ptr - note_start)); + + phdr++; + phdr->p_type = grub_host_to_target32 (PT_NOTE); + phdr->p_flags = grub_host_to_target32 (PF_R); + phdr->p_align = grub_host_to_target32 (image_target->voidp_sizeof); + phdr->p_vaddr = 0; + phdr->p_paddr = 0; + phdr->p_filesz = grub_host_to_target32 (XEN_NOTE_SIZE); + phdr->p_memsz = 0; + phdr->p_offset = grub_host_to_target32 (header_size + program_size); + } + + if (note) + { + int note_size = sizeof (struct grub_ieee1275_note); + struct grub_ieee1275_note *note_ptr = (struct grub_ieee1275_note *) + (elf_img + program_size + header_size); + + grub_util_info ("adding CHRP NOTE segment"); + + note_ptr->header.n_namesz = grub_host_to_target32 (sizeof (GRUB_IEEE1275_NOTE_NAME)); + note_ptr->header.n_descsz = grub_host_to_target32 (note_size); + note_ptr->header.n_type = grub_host_to_target32 (GRUB_IEEE1275_NOTE_TYPE); + strcpy (note_ptr->name, GRUB_IEEE1275_NOTE_NAME); + note_ptr->descriptor.real_mode = grub_host_to_target32 (0xffffffff); + note_ptr->descriptor.real_base = grub_host_to_target32 (0x00c00000); + note_ptr->descriptor.real_size = grub_host_to_target32 (0xffffffff); + note_ptr->descriptor.virt_base = grub_host_to_target32 (0xffffffff); + note_ptr->descriptor.virt_size = grub_host_to_target32 (0xffffffff); + note_ptr->descriptor.load_base = grub_host_to_target32 (0x00004000); + + phdr++; + phdr->p_type = grub_host_to_target32 (PT_NOTE); + phdr->p_flags = grub_host_to_target32 (PF_R); + phdr->p_align = grub_host_to_target32 (image_target->voidp_sizeof); + phdr->p_vaddr = 0; + phdr->p_paddr = 0; + phdr->p_filesz = grub_host_to_target32 (note_size); + phdr->p_memsz = 0; + phdr->p_offset = grub_host_to_target32 (header_size + program_size); + } + + { + char *str_start = (elf_img + sizeof (*ehdr) + phnum * sizeof (*phdr) + + shnum * sizeof (*shdr)); + char *ptr = str_start + 1; + + shdr++; + + shdr->sh_name = grub_host_to_target32 (0); + shdr->sh_type = grub_host_to_target32 (SHT_STRTAB); + shdr->sh_addr = grub_host_to_target_addr (0); + shdr->sh_offset = grub_host_to_target_addr (str_start - elf_img); + shdr->sh_size = grub_host_to_target32 (string_size); + shdr->sh_link = grub_host_to_target32 (0); + shdr->sh_info = grub_host_to_target32 (0); + shdr->sh_addralign = grub_host_to_target32 (align); + shdr->sh_entsize = grub_host_to_target32 (0); + shdr++; + + memcpy (ptr, ".text", sizeof (".text")); + + shdr->sh_name = grub_host_to_target32 (ptr - str_start); + ptr += sizeof (".text"); + shdr->sh_type = grub_host_to_target32 (SHT_PROGBITS); + shdr->sh_addr = grub_host_to_target_addr (target_addr); + shdr->sh_offset = grub_host_to_target_addr (header_size); + shdr->sh_size = grub_host_to_target32 (kernel_size); + shdr->sh_link = grub_host_to_target32 (0); + shdr->sh_info = grub_host_to_target32 (0); + shdr->sh_addralign = grub_host_to_target32 (align); + shdr->sh_entsize = grub_host_to_target32 (0); + shdr++; + + memcpy (ptr, "mods", sizeof ("mods")); + shdr->sh_name = grub_host_to_target32 (ptr - str_start); + ptr += sizeof ("mods"); + shdr->sh_type = grub_host_to_target32 (SHT_PROGBITS); + shdr->sh_addr = grub_host_to_target_addr (target_addr + kernel_size); + shdr->sh_offset = grub_host_to_target_addr (header_size + kernel_size); + shdr->sh_size = grub_host_to_target32 (*core_size - kernel_size); + shdr->sh_link = grub_host_to_target32 (0); + shdr->sh_info = grub_host_to_target32 (0); + shdr->sh_addralign = grub_host_to_target32 (image_target->voidp_sizeof); + shdr->sh_entsize = grub_host_to_target32 (0); + shdr++; + + if (image_target->id == IMAGE_XEN) + { + memcpy (ptr, ".xen", sizeof (".xen")); + shdr->sh_name = grub_host_to_target32 (ptr - str_start); + ptr += sizeof (".xen"); + shdr->sh_type = grub_host_to_target32 (SHT_PROGBITS); + shdr->sh_addr = grub_host_to_target_addr (target_addr + kernel_size); + shdr->sh_offset = grub_host_to_target_addr (program_size + header_size); + shdr->sh_size = grub_host_to_target32 (XEN_NOTE_SIZE); + shdr->sh_link = grub_host_to_target32 (0); + shdr->sh_info = grub_host_to_target32 (0); + shdr->sh_addralign = grub_host_to_target32 (image_target->voidp_sizeof); + shdr->sh_entsize = grub_host_to_target32 (0); + shdr++; + } + } + + free (*core_img); + *core_img = elf_img; + *core_size = program_size + header_size + footer_size; +} + /* Relocate symbols; note that this function overwrites the symbol table. Return the address of a start symbol. */ static Elf_Addr @@ -1095,6 +1401,7 @@ SUFFIX (load_image) (const char *kernel_path, size_t *exec_size, #undef ELFCLASSXX #undef Elf_Ehdr #undef Elf_Phdr +#undef Elf_Nhdr #undef Elf_Shdr #undef Elf_Addr #undef Elf_Sym @@ -1107,3 +1414,4 @@ SUFFIX (load_image) (const char *kernel_path, size_t *exec_size, #undef Elf_Half #undef Elf_Section #undef ELF_ST_TYPE +#undef XEN_NOTE_SIZE diff --git a/util/mkimage.c b/util/mkimage.c index 1d2c7eac1..c71f1fdcc 100644 --- a/util/mkimage.c +++ b/util/mkimage.c @@ -66,7 +66,7 @@ struct grub_install_image_target_desc IMAGE_I386_IEEE1275, IMAGE_LOONGSON_ELF, IMAGE_QEMU, IMAGE_PPC, IMAGE_YEELOONG_FLASH, IMAGE_FULOONG2F_FLASH, IMAGE_I386_PC_PXE, IMAGE_MIPS_ARC, - IMAGE_QEMU_MIPS_FLASH, IMAGE_UBOOT + IMAGE_QEMU_MIPS_FLASH, IMAGE_UBOOT, IMAGE_XEN } id; enum { @@ -240,6 +240,44 @@ static const struct grub_install_image_target_desc image_targets[] = .pe_target = GRUB_PE32_MACHINE_X86_64, .elf_target = EM_X86_64, }, + { + .dirname = "i386-xen", + .names = { "i386-xen", NULL }, + .voidp_sizeof = 4, + .bigendian = 0, + .id = IMAGE_XEN, + .flags = PLATFORM_FLAGS_NONE, + .total_module_size = TARGET_NO_FIELD, + .decompressor_compressed_size = TARGET_NO_FIELD, + .decompressor_uncompressed_size = TARGET_NO_FIELD, + .decompressor_uncompressed_addr = TARGET_NO_FIELD, + .section_align = 1, + .vaddr_offset = 0, + .link_addr = 0, + .elf_target = EM_386, + .mod_gap = GRUB_KERNEL_I386_XEN_MOD_GAP, + .mod_align = GRUB_KERNEL_I386_XEN_MOD_ALIGN, + .link_align = 4 + }, + { + .dirname = "x86_64-xen", + .names = { "x86_64-xen", NULL }, + .voidp_sizeof = 8, + .bigendian = 0, + .id = IMAGE_XEN, + .flags = PLATFORM_FLAGS_NONE, + .total_module_size = TARGET_NO_FIELD, + .decompressor_compressed_size = TARGET_NO_FIELD, + .decompressor_uncompressed_size = TARGET_NO_FIELD, + .decompressor_uncompressed_addr = TARGET_NO_FIELD, + .section_align = 1, + .vaddr_offset = 0, + .link_addr = 0, + .elf_target = EM_X86_64, + .mod_gap = GRUB_KERNEL_X86_64_XEN_MOD_GAP, + .mod_align = GRUB_KERNEL_X86_64_XEN_MOD_ALIGN, + .link_align = 8 + }, { .dirname = "mipsel-loongson", .names = { "mipsel-yeeloong-flash", NULL }, @@ -618,14 +656,6 @@ grub_target_to_host_real (const struct grub_install_image_target_desc *image_tar /* These structures are defined according to the CHRP binding to IEEE1275, "Client Program Format" section. */ -struct grub_ieee1275_note_hdr -{ - grub_uint32_t namesz; - grub_uint32_t descsz; - grub_uint32_t type; - char name[sizeof (GRUB_IEEE1275_NOTE_NAME)]; -}; - struct grub_ieee1275_note_desc { grub_uint32_t real_mode; @@ -638,10 +668,13 @@ struct grub_ieee1275_note_desc struct grub_ieee1275_note { - struct grub_ieee1275_note_hdr header; + Elf32_Nhdr header; + char name[ALIGN_UP(sizeof (GRUB_IEEE1275_NOTE_NAME), 4)]; struct grub_ieee1275_note_desc descriptor; }; +#define GRUB_XEN_NOTE_NAME "Xen" + #define grub_target_to_host(val) grub_target_to_host_real(image_target, (val)) #include @@ -903,6 +936,8 @@ grub_install_generate_image (const char *dir, const char *prefix, kernel_img = load_image64 (kernel_path, &exec_size, &kernel_size, &bss_size, total_module_size, &start_address, &rel_section, &reloc_size, &align, image_target); + if (image_target->id == IMAGE_XEN && align < 4096) + align = 4096; if ((image_target->flags & PLATFORM_FLAGS_DECOMPRESSORS) && (image_target->total_module_size != TARGET_NO_FIELD)) @@ -1135,6 +1170,7 @@ grub_install_generate_image (const char *dir, const char *prefix, case IMAGE_EFI: case IMAGE_MIPS_ARC: case IMAGE_QEMU_MIPS_FLASH: + case IMAGE_XEN: break; case IMAGE_SPARC64_AOUT: case IMAGE_SPARC64_RAW: @@ -1700,65 +1736,11 @@ grub_install_generate_image (const char *dir, const char *prefix, break; case IMAGE_LOONGSON_ELF: case IMAGE_PPC: + case IMAGE_XEN: case IMAGE_COREBOOT: case IMAGE_I386_IEEE1275: { - char *elf_img; - size_t program_size; - Elf32_Ehdr *ehdr; - Elf32_Phdr *phdr; - grub_uint32_t target_addr; - int header_size, footer_size = 0; - int phnum = 1; - - if (image_target->id != IMAGE_LOONGSON_ELF) - phnum += 2; - - if (note) - { - phnum++; - footer_size += sizeof (struct grub_ieee1275_note); - } - header_size = ALIGN_ADDR (sizeof (*ehdr) + phnum * sizeof (*phdr)); - - program_size = ALIGN_ADDR (core_size); - - elf_img = xmalloc (program_size + header_size + footer_size); - memset (elf_img, 0, program_size + header_size); - memcpy (elf_img + header_size, core_img, core_size); - ehdr = (void *) elf_img; - phdr = (void *) (elf_img + sizeof (*ehdr)); - memcpy (ehdr->e_ident, ELFMAG, SELFMAG); - ehdr->e_ident[EI_CLASS] = ELFCLASS32; - if (!image_target->bigendian) - ehdr->e_ident[EI_DATA] = ELFDATA2LSB; - else - ehdr->e_ident[EI_DATA] = ELFDATA2MSB; - ehdr->e_ident[EI_VERSION] = EV_CURRENT; - ehdr->e_ident[EI_OSABI] = ELFOSABI_NONE; - ehdr->e_type = grub_host_to_target16 (ET_EXEC); - ehdr->e_machine = grub_host_to_target16 (image_target->elf_target); - ehdr->e_version = grub_host_to_target32 (EV_CURRENT); - - ehdr->e_phoff = grub_host_to_target32 ((char *) phdr - (char *) ehdr); - ehdr->e_phentsize = grub_host_to_target16 (sizeof (*phdr)); - ehdr->e_phnum = grub_host_to_target16 (phnum); - - /* No section headers. */ - ehdr->e_shoff = grub_host_to_target32 (0); - if (image_target->id == IMAGE_LOONGSON_ELF) - ehdr->e_shentsize = grub_host_to_target16 (0); - else - ehdr->e_shentsize = grub_host_to_target16 (sizeof (Elf32_Shdr)); - ehdr->e_shnum = grub_host_to_target16 (0); - ehdr->e_shstrndx = grub_host_to_target16 (0); - - ehdr->e_ehsize = grub_host_to_target16 (sizeof (*ehdr)); - - phdr->p_type = grub_host_to_target32 (PT_LOAD); - phdr->p_offset = grub_host_to_target32 (header_size); - phdr->p_flags = grub_host_to_target32 (PF_R | PF_W | PF_X); - + grub_uint64_t target_addr; if (image_target->id == IMAGE_LOONGSON_ELF) { if (comp == GRUB_COMPRESSION_NONE) @@ -1769,84 +1751,12 @@ grub_install_generate_image (const char *dir, const char *prefix, } else target_addr = image_target->link_addr; - ehdr->e_entry = grub_host_to_target32 (target_addr); - phdr->p_vaddr = grub_host_to_target32 (target_addr); - phdr->p_paddr = grub_host_to_target32 (target_addr); - phdr->p_align = grub_host_to_target32 (align > image_target->link_align ? align : image_target->link_align); - if (image_target->id == IMAGE_LOONGSON_ELF) - ehdr->e_flags = grub_host_to_target32 (0x1000 | EF_MIPS_NOREORDER - | EF_MIPS_PIC | EF_MIPS_CPIC); + if (image_target->voidp_sizeof == 4) + generate_elf32 (image_target, note, &core_img, &core_size, + target_addr, align, kernel_size, bss_size); else - ehdr->e_flags = 0; - if (image_target->id == IMAGE_LOONGSON_ELF) - { - phdr->p_filesz = grub_host_to_target32 (core_size); - phdr->p_memsz = grub_host_to_target32 (core_size); - } - else - { - grub_uint32_t target_addr_mods; - phdr->p_filesz = grub_host_to_target32 (kernel_size); - phdr->p_memsz = grub_host_to_target32 (kernel_size + bss_size); - - phdr++; - phdr->p_type = grub_host_to_target32 (PT_GNU_STACK); - phdr->p_offset = grub_host_to_target32 (header_size + kernel_size); - phdr->p_paddr = phdr->p_vaddr = phdr->p_filesz = phdr->p_memsz = 0; - phdr->p_flags = grub_host_to_target32 (PF_R | PF_W | PF_X); - phdr->p_align = grub_host_to_target32 (image_target->link_align); - - phdr++; - phdr->p_type = grub_host_to_target32 (PT_LOAD); - phdr->p_offset = grub_host_to_target32 (header_size + kernel_size); - phdr->p_flags = grub_host_to_target32 (PF_R | PF_W | PF_X); - phdr->p_filesz = phdr->p_memsz - = grub_host_to_target32 (core_size - kernel_size); - - if (image_target->id == IMAGE_COREBOOT) - target_addr_mods = GRUB_KERNEL_I386_COREBOOT_MODULES_ADDR; - else - target_addr_mods = ALIGN_UP (target_addr + kernel_size + bss_size - + image_target->mod_gap, - image_target->mod_align); - phdr->p_vaddr = grub_host_to_target32 (target_addr_mods); - phdr->p_paddr = grub_host_to_target32 (target_addr_mods); - phdr->p_align = grub_host_to_target32 (image_target->link_align); - } - - if (note) - { - int note_size = sizeof (struct grub_ieee1275_note); - struct grub_ieee1275_note *note_ptr = (struct grub_ieee1275_note *) - (elf_img + program_size + header_size); - - grub_util_info ("adding CHRP NOTE segment"); - - note_ptr->header.namesz = grub_host_to_target32 (sizeof (GRUB_IEEE1275_NOTE_NAME)); - note_ptr->header.descsz = grub_host_to_target32 (note_size); - note_ptr->header.type = grub_host_to_target32 (GRUB_IEEE1275_NOTE_TYPE); - strcpy (note_ptr->header.name, GRUB_IEEE1275_NOTE_NAME); - note_ptr->descriptor.real_mode = grub_host_to_target32 (0xffffffff); - note_ptr->descriptor.real_base = grub_host_to_target32 (0x00c00000); - note_ptr->descriptor.real_size = grub_host_to_target32 (0xffffffff); - note_ptr->descriptor.virt_base = grub_host_to_target32 (0xffffffff); - note_ptr->descriptor.virt_size = grub_host_to_target32 (0xffffffff); - note_ptr->descriptor.load_base = grub_host_to_target32 (0x00004000); - - phdr++; - phdr->p_type = grub_host_to_target32 (PT_NOTE); - phdr->p_flags = grub_host_to_target32 (PF_R); - phdr->p_align = grub_host_to_target32 (image_target->voidp_sizeof); - phdr->p_vaddr = 0; - phdr->p_paddr = 0; - phdr->p_filesz = grub_host_to_target32 (note_size); - phdr->p_memsz = 0; - phdr->p_offset = grub_host_to_target32 (header_size + program_size); - } - - free (core_img); - core_img = elf_img; - core_size = program_size + header_size + footer_size; + generate_elf64 (image_target, note, &core_img, &core_size, + target_addr, align, kernel_size, bss_size); } break; }