From 021b7fe27044b12828ce68e01f5d354f477999c5 Mon Sep 17 00:00:00 2001
From: Vladimir Serbinenko <phcoder@gmail.com>
Date: Sat, 9 Nov 2013 21:29:11 +0100
Subject: [PATCH 08/14] Add new ports: i386-xen and x86_64-xen. This allows
 running GRUB in XEN PV environment and load kernels.

---
 ChangeLog                               |   5 +
 conf/Makefile.common                    |   3 +
 configure.ac                            |   6 +
 gentpl.py                               |   6 +-
 grub-core/Makefile.am                   |  18 +
 grub-core/Makefile.core.def             |  63 ++-
 grub-core/commands/boot.c               |   3 +-
 grub-core/commands/xen/lsxen.c          |  90 ++++
 grub-core/disk/xen/xendisk.c            | 453 ++++++++++++++++++++
 grub-core/io/offset.c                   | 111 +++++
 grub-core/kern/emu/main.c               |   6 +-
 grub-core/kern/i386/coreboot/init.c     |   5 +-
 grub-core/kern/i386/efi/init.c          |   5 +-
 grub-core/kern/i386/pc/init.c           |   5 +-
 grub-core/kern/i386/tsc.c               |  21 +
 grub-core/kern/i386/xen/hypercall.S     |  43 ++
 grub-core/kern/i386/xen/startup.S       |  38 ++
 grub-core/kern/ia64/efi/init.c          |   5 +-
 grub-core/kern/ieee1275/init.c          |   9 +-
 grub-core/kern/mips/arc/init.c          |   2 +-
 grub-core/kern/mips/loongson/init.c     |   2 +-
 grub-core/kern/mips/qemu_mips/init.c    |   2 +-
 grub-core/kern/x86_64/xen/hypercall.S   |  53 +++
 grub-core/kern/x86_64/xen/startup.S     |  38 ++
 grub-core/kern/xen/init.c               | 571 +++++++++++++++++++++++++
 grub-core/lib/i386/reboot.c             |   1 -
 grub-core/lib/i386/relocator.c          |  82 ----
 grub-core/lib/i386/relocator_common_c.c | 109 +++++
 grub-core/lib/i386/xen/relocator.S      | 146 +++++++
 grub-core/lib/legacy_parse.c            |   3 +
 grub-core/lib/relocator.c               |   2 +-
 grub-core/lib/x86_64/xen/relocator.S    | 154 +++++++
 grub-core/lib/xen/datetime.c            |  40 ++
 grub-core/lib/xen/halt.c                |  32 ++
 grub-core/lib/xen/reboot.c              |  32 ++
 grub-core/lib/xen/relocator.c           | 125 ++++++
 grub-core/loader/i386/xen.c             | 712 ++++++++++++++++++++++++++++++++
 grub-core/loader/i386/xen_file.c        |  91 ++++
 grub-core/loader/i386/xen_file32.c      |   7 +
 grub-core/loader/i386/xen_file64.c      |   7 +
 grub-core/loader/i386/xen_fileXX.c      | 344 +++++++++++++++
 grub-core/term/xen/console.c            | 122 ++++++
 include/grub/disk.h                     |   1 +
 include/grub/elf.h                      |   4 +
 include/grub/file.h                     |   6 +
 include/grub/i386/xen/hypercall.h       |  85 ++++
 include/grub/i386/xen/memory.h          |   0
 include/grub/kernel.h                   |   4 +-
 include/grub/offsets.h                  |   5 +
 include/grub/x86_64/xen/hypercall.h     |  34 ++
 include/grub/xen.h                      |  93 +++++
 include/grub/xen/relocator.h            |  44 ++
 include/grub/xen_file.h                 |  54 +++
 util/grub-mkimage.c                     | 196 +++------
 util/grub-mkimagexx.c                   | 308 ++++++++++++++
 55 files changed, 4155 insertions(+), 251 deletions(-)
 create mode 100644 grub-core/commands/xen/lsxen.c
 create mode 100644 grub-core/disk/xen/xendisk.c
 create mode 100644 grub-core/io/offset.c
 create mode 100644 grub-core/kern/i386/xen/hypercall.S
 create mode 100644 grub-core/kern/i386/xen/startup.S
 create mode 100644 grub-core/kern/x86_64/xen/hypercall.S
 create mode 100644 grub-core/kern/x86_64/xen/startup.S
 create mode 100644 grub-core/kern/xen/init.c
 create mode 100644 grub-core/lib/i386/relocator_common_c.c
 create mode 100644 grub-core/lib/i386/xen/relocator.S
 create mode 100644 grub-core/lib/x86_64/xen/relocator.S
 create mode 100644 grub-core/lib/xen/datetime.c
 create mode 100644 grub-core/lib/xen/halt.c
 create mode 100644 grub-core/lib/xen/reboot.c
 create mode 100644 grub-core/lib/xen/relocator.c
 create mode 100644 grub-core/loader/i386/xen.c
 create mode 100644 grub-core/loader/i386/xen_file.c
 create mode 100644 grub-core/loader/i386/xen_file32.c
 create mode 100644 grub-core/loader/i386/xen_file64.c
 create mode 100644 grub-core/loader/i386/xen_fileXX.c
 create mode 100644 grub-core/term/xen/console.c
 create mode 100644 include/grub/i386/xen/hypercall.h
 create mode 100644 include/grub/i386/xen/memory.h
 create mode 100644 include/grub/x86_64/xen/hypercall.h
 create mode 100644 include/grub/xen.h
 create mode 100644 include/grub/xen/relocator.h
 create mode 100644 include/grub/xen_file.h

diff --git a/ChangeLog b/ChangeLog
index 1f9193e..357f39a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2013-11-09  Vladimir Serbinenko  <phcoder@gmail.com>
+
+	Add new ports: i386-xen and x86_64-xen. This allows running GRUB in
+	XEN PV environment and load kernels.
+
 2013-10-28  Vladimir Serbinenko  <phcoder@gmail.com>
 
 	Make / in btrfs refer to real root, not the default volume.
diff --git a/conf/Makefile.common b/conf/Makefile.common
index 71d03fe..df78f53 100644
--- a/conf/Makefile.common
+++ b/conf/Makefile.common
@@ -38,6 +38,9 @@ if COND_sparc64_ieee1275
   LDFLAGS_PLATFORM = -Wl,-melf64_sparc -mno-relax
 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 5ea7ffe..afd5104 100644
--- a/configure.ac
+++ b/configure.ac
@@ -108,6 +108,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
@@ -116,6 +117,8 @@ esac
 case "$target_cpu"-"$platform" in
   i386-efi) ;;
   x86_64-efi) ;;
+  i386-xen) ;;
+  x86_64-xen) ;;
   i386-pc) ;;
   i386-multiboot) ;;
   i386-coreboot) ;;
@@ -163,6 +166,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" ;;
   qemu)		machine_CPPFLAGS="$machine_CPPFLAGS -DGRUB_MACHINE_QEMU=1" ;;
   pc)		machine_CPPFLAGS="$machine_CPPFLAGS -DGRUB_MACHINE_PCBIOS=1" ;;
@@ -1107,6 +1111,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 -a x$platform = xarc])
diff --git a/gentpl.py b/gentpl.py
index 13a6081..edd52b8 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" ]
@@ -40,6 +41,7 @@ GROUPS["powerpc"]  = [ "powerpc_ieee1275" ]
 # Groups based on firmware
 GROUPS["efi"]  = [ "i386_efi", "x86_64_efi", "ia64_efi" ]
 GROUPS["ieee1275"]   = [ "i386_ieee1275", "sparc64_ieee1275", "powerpc_ieee1275" ]
+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")
@@ -47,7 +49,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"]
 
@@ -57,7 +59,7 @@ GROUPS["videomodules"]   = GRUB_PLATFORMS[:];
 for i in GROUPS["videoinkernel"]: GROUPS["videomodules"].remove(i)
 
 # Similar for terminfo
-GROUPS["terminfoinkernel"] = ["mips_loongson", "mips_arc", "mips_qemu_mips" ] + GROUPS["ieee1275"];
+GROUPS["terminfoinkernel"] = ["mips_loongson", "mips_arc", "mips_qemu_mips" ] + GROUPS["xen"] + GROUPS["ieee1275"];
 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 664c9ec..9ab8200 100644
--- a/grub-core/Makefile.am
+++ b/grub-core/Makefile.am
@@ -131,6 +131,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 69bdb67..13fc58c 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -45,6 +45,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)';
+
   i386_pc_ldflags          = '$(TARGET_IMG_LDFLAGS)';
   i386_pc_ldflags          = '$(TARGET_IMG_BASE_LDOPT),0x9000';
 
@@ -70,6 +75,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;
@@ -118,6 +125,7 @@ kernel = {
   terminfoinkernel = lib/arg.c;
 
   i386 = kern/i386/dl.c;
+  i386_xen = kern/i386/dl.c;
 
   i386_coreboot_multiboot_qemu = kern/i386/coreboot/init.c;
   i386_coreboot_multiboot_qemu = term/i386/pc/vga_text.c;
@@ -136,11 +144,20 @@ kernel = {
   i386_efi = bus/pci.c;
 
   x86_64_efi = kern/i386/tsc.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;
@@ -491,6 +508,14 @@ module = {
   enable = mips_arc;
 };
 
+module = {
+  name = lsxen;
+  common = commands/xen/lsxen.c;
+  cppflags = '$(CPPFLAGS_XEN)';
+
+  enable = xen;
+};
+
 library = {
   name = libgnulib.a;
   common = gnulib/regex.c;
@@ -598,8 +623,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 = {
@@ -651,6 +678,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;
@@ -667,6 +696,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)';
   common = commands/reboot.c;
 };
 
@@ -1251,6 +1282,11 @@ module = {
 };
 
 module = {
+  name = offsetio;
+  common = io/offset.c;
+};
+
+module = {
   name = bufio;
   common = io/bufio.c;
   enable = videomodules;
@@ -1281,13 +1317,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;
@@ -1295,6 +1339,7 @@ module = {
   enable = mips;
   enable = powerpc;
   enable = x86;
+  enable = xen;
 };
 
 module = {
@@ -1305,6 +1350,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;
@@ -1394,6 +1441,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;
@@ -1767,8 +1820,10 @@ module = {
   common = commands/legacycfg.c;
   common = lib/legacy_parse.c;
   emu = lib/i386/pc/vesa_modes_table.c;
+  xen = lib/i386/pc/vesa_modes_table.c;
   enable = i386_pc;
   enable = emu;
+  enable = xen;
 };
 
 module = {
@@ -1802,8 +1857,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 e0148c7..bbca81e 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/xen/lsxen.c b/grub-core/commands/xen/lsxen.c
new file mode 100644
index 0000000..8415495
--- /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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/dl.h>
+#include <grub/misc.h>
+#include <grub/command.h>
+#include <grub/i18n.h>
+#include <grub/xen.h>
+#include <grub/term.h>
+
+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 0000000..c449848
--- /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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/disk.h>
+#include <grub/dl.h>
+#include <grub/mm.h>
+#include <grub/types.h>
+#include <grub/misc.h>
+#include <grub/err.h>
+#include <grub/term.h>
+#include <grub/i18n.h>
+#include <grub/xen.h>
+#include <grub/time.h>
+#include <xen/io/blkif.h>
+
+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/io/offset.c b/grub-core/io/offset.c
new file mode 100644
index 0000000..ebed0eb
--- /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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/file.h>
+#include <grub/dl.h>
+
+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 0418aae..530d980 100644
--- a/grub-core/kern/emu/main.c
+++ b/grub-core/kern/emu/main.c
@@ -40,6 +40,7 @@
 #include <grub/env.h>
 #include <grub/partition.h>
 #include <grub/i18n.h>
+#include <grub/loader.h>
 
 #include "progname.h"
 #include <argp.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 539f342..c2bc7e2 100644
--- a/grub-core/kern/i386/coreboot/init.c
+++ b/grub-core/kern/i386/coreboot/init.c
@@ -122,8 +122,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 6bd8f3e..7616e62 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 7befb6b..6c0d79d 100644
--- a/grub-core/kern/i386/pc/init.c
+++ b/grub-core/kern/i386/pc/init.c
@@ -238,8 +238,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/tsc.c b/grub-core/kern/i386/tsc.c
index 9efd633..d8d1a2c 100644
--- a/grub-core/kern/i386/tsc.c
+++ b/grub-core/kern/i386/tsc.c
@@ -24,7 +24,11 @@
 #include <grub/time.h>
 #include <grub/misc.h>
 #include <grub/i386/tsc.h>
+#ifdef GRUB_MACHINE_XEN
+#include <grub/xen.h>
+#else
 #include <grub/i386/pit.h>
+#endif
 #include <grub/cpu/io.h>
 
 /* This defines the value TSC had at the epoch (that is, when we calibrated it). */
@@ -35,6 +39,8 @@ static grub_uint64_t tsc_boot_time;
    in 32-bit.  */
 grub_uint32_t grub_tsc_rate;
 
+#ifndef GRUB_MACHINE_XEN
+
 static void
 grub_pit_wait (grub_uint16_t tics)
 {
@@ -62,6 +68,7 @@ grub_pit_wait (grub_uint16_t tics)
 	     & ~ (GRUB_PIT_SPK_DATA | GRUB_PIT_SPK_TMR2),
              GRUB_PIT_SPEAKER_PORT);
 }
+#endif
 
 grub_uint64_t
 grub_tsc_get_time_ms (void)
@@ -73,6 +80,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)
@@ -86,10 +94,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 ();
@@ -103,4 +123,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 0000000..aa47cac
--- /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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/symbol.h>
+#include <grub/xen.h>
+
+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 0000000..fbe8300
--- /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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <grub/symbol.h>
+
+        .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 7fa6fbf..c514b0a 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 7d03a8a..23218bf 100644
--- a/grub-core/kern/ieee1275/init.c
+++ b/grub-core/kern/ieee1275/init.c
@@ -277,10 +277,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 ();
+    }
 }
 
 static grub_uint64_t
diff --git a/grub-core/kern/mips/arc/init.c b/grub-core/kern/mips/arc/init.c
index 44b4171..d90a476 100644
--- a/grub-core/kern/mips/arc/init.c
+++ b/grub-core/kern/mips/arc/init.c
@@ -168,7 +168,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 19f2d63..508f473 100644
--- a/grub-core/kern/mips/loongson/init.c
+++ b/grub-core/kern/mips/loongson/init.c
@@ -214,7 +214,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 782f17f..5410d7b 100644
--- a/grub-core/kern/mips/qemu_mips/init.c
+++ b/grub-core/kern/mips/qemu_mips/init.c
@@ -74,7 +74,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 0000000..9b04db6
--- /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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/symbol.h>
+#include <grub/xen.h>
+
+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 0000000..7217ee5
--- /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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <grub/symbol.h>
+
+        .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 0000000..3bfd99f
--- /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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/xen.h>
+#include <grub/term.h>
+#include <grub/misc.h>
+#include <grub/env.h>
+#include <grub/mm.h>
+#include <grub/kernel.h>
+#include <grub/offsets.h>
+#include <grub/memory.h>
+#include <grub/i386/tsc.h>
+#include <grub/term.h>
+#include <grub/loader.h>
+
+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 0587f14..a234244 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 df25b30..bfa4303 100644
--- a/grub-core/lib/i386/relocator.c
+++ b/grub-core/lib/i386/relocator.c
@@ -28,19 +28,6 @@
 #include <grub/i386/relocator_private.h>
 #include <grub/i386/pc/int.h>
 
-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;
@@ -84,75 +71,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 0000000..7be609b
--- /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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/mm.h>
+#include <grub/misc.h>
+
+#include <grub/types.h>
+#include <grub/err.h>
+#include <grub/term.h>
+
+#include <grub/relocator.h>
+#include <grub/relocator_private.h>
+
+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 0000000..a1677db
--- /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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/symbol.h>
+#include <grub/xen.h>
+
+	.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 775eaad..ab12d79 100644
--- a/grub-core/lib/legacy_parse.c
+++ b/grub-core/lib/legacy_parse.c
@@ -243,6 +243,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: rarp unsupported.  */
     {"read", "read_dword %s\n", NULL, 0, 1, {TYPE_INT}, 0, "ADDR",
      "Read a 32-bit value from memory at address ADDR and"
diff --git a/grub-core/lib/relocator.c b/grub-core/lib/relocator.c
index a45040a..13ac8dc 100644
--- a/grub-core/lib/relocator.c
+++ b/grub-core/lib/relocator.c
@@ -1409,7 +1409,7 @@ grub_relocator_alloc_chunk_align (struct grub_relocator *rel,
 
 #ifdef GRUB_MACHINE_EFI
     grub_efi_mmap_iterate (hook, avoid_efi_boot_services);
-#elif defined (__powerpc__)
+#elif defined (__powerpc__) || defined (GRUB_MACHINE_XEN)
     (void) avoid_efi_boot_services;
     grub_machine_mmap_iterate (hook);
 #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 0000000..28ad4c7
--- /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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/symbol.h>
+#include <grub/xen.h>
+
+	.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 0000000..d96176e
--- /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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/datetime.h>
+#include <grub/dl.h>
+#include <grub/misc.h>
+#include <grub/xen.h>
+
+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 0000000..2aceead
--- /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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/kernel.h>
+#include <grub/xen.h>
+
+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 0000000..fd7609a
--- /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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/kernel.h>
+#include <grub/xen.h>
+
+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 0000000..8f427d3
--- /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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/mm.h>
+#include <grub/misc.h>
+
+#include <grub/types.h>
+#include <grub/err.h>
+#include <grub/term.h>
+#include <grub/xen.h>
+
+#include <grub/xen/relocator.h>
+#include <grub/relocator_private.h>
+
+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 0000000..91b66f8
--- /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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/loader.h>
+#include <grub/memory.h>
+#include <grub/normal.h>
+#include <grub/file.h>
+#include <grub/disk.h>
+#include <grub/err.h>
+#include <grub/misc.h>
+#include <grub/types.h>
+#include <grub/dl.h>
+#include <grub/mm.h>
+#include <grub/term.h>
+#include <grub/cpu/linux.h>
+#include <grub/video.h>
+#include <grub/video_fb.h>
+#include <grub/command.h>
+#include <grub/xen/relocator.h>
+#include <grub/i18n.h>
+#include <grub/elf.h>
+#include <grub/elfload.h>
+#include <grub/lib/cmdline.h>
+#include <grub/xen.h>
+#include <grub/xen_file.h>
+#include <grub/linux.h>
+
+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 0000000..fb7f692
--- /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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/xen_file.h>
+#include <grub/i386/linux.h>
+
+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 0000000..340d445
--- /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 0000000..c410493
--- /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 0000000..ea0656c
--- /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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/xen_file.h>
+
+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 0000000..a1f15f7
--- /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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/term.h>
+#include <grub/types.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/time.h>
+#include <grub/terminfo.h>
+#include <grub/xen.h>
+
+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 096173d..61512c1 100644
--- a/include/grub/disk.h
+++ b/include/grub/disk.h
@@ -41,6 +41,7 @@ enum grub_disk_dev_id
     GRUB_DISK_DEVICE_CRYPTODISK_ID,
     GRUB_DISK_DEVICE_ARCDISK_ID,
     GRUB_DISK_DEVICE_HOSTDISK_ID,
+    GRUB_DISK_DEVICE_XEN,
   };
 
 struct grub_disk;
diff --git a/include/grub/elf.h b/include/grub/elf.h
index 9a75b52..3d8563f 100644
--- a/include/grub/elf.h
+++ b/include/grub/elf.h
@@ -2335,7 +2335,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;
@@ -2358,7 +2360,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 e08ac2e..c1e1c9d 100644
--- a/include/grub/file.h
+++ b/include/grub/file.h
@@ -127,4 +127,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 0000000..0152b2b
--- /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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GRUB_XEN_CPU_HYPERCALL_HEADER
+#define GRUB_XEN_CPU_HYPERCALL_HEADER 1
+
+#include <grub/types.h>
+
+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 0000000..e69de29
diff --git a/include/grub/kernel.h b/include/grub/kernel.h
index 6df2140..8f43d71 100644
--- a/include/grub/kernel.h
+++ b/include/grub/kernel.h
@@ -77,7 +77,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 (__sparc__) || defined (GRUB_MACHINE_XEN)
 /* FIXME: stack is between 2 heap regions. Move it.  */
 #define GRUB_KERNEL_PRELOAD_SPACE_REUSABLE 1
 #endif
@@ -113,7 +113,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 bce755d..56c679c 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 0000000..780db4d
--- /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 <http://www.gnu.org/licenses/>.
+ */
+
+#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 0000000..062c95d
--- /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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GRUB_XEN_HEADER
+#define GRUB_XEN_HEADER 1
+
+#define __XEN_INTERFACE_VERSION__ 0x0003020a
+
+#ifdef ASM_FILE
+#define __ASSEMBLY__
+#include <xen/xen.h>
+#else
+
+#include <grub/symbol.h>
+#include <grub/types.h>
+#include <grub/err.h>
+
+#ifndef GRUB_SYMBOL_GENERATOR
+#include <stdint.h>
+#include <xen/xen.h>
+
+#include <xen/sched.h>
+#include <xen/grant_table.h>
+#include <xen/io/console.h>
+#include <xen/io/xs_wire.h>
+#include <xen/io/xenbus.h>
+#include <xen/io/protocols.h>
+#endif
+
+#include <grub/cpu/xen/hypercall.h>
+
+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 0000000..ae45dce
--- /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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GRUB_RELOCATOR_XEN_HEADER
+#define GRUB_RELOCATOR_XEN_HEADER	1
+
+#include <grub/types.h>
+#include <grub/err.h>
+#include <grub/relocator.h>
+
+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 0000000..4b2ccba
--- /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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GRUB_XEN_FILE_HEADER
+#define GRUB_XEN_FILE_HEADER 1
+
+#include <grub/types.h>
+#include <grub/elf.h>
+#include <grub/elfload.h>
+
+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-mkimage.c b/util/grub-mkimage.c
index db144c3..a0b640f 100644
--- a/util/grub-mkimage.c
+++ b/util/grub-mkimage.c
@@ -69,7 +69,7 @@ struct image_target_desc
     IMAGE_SPARC64_AOUT, IMAGE_SPARC64_RAW, 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_QEMU_MIPS_FLASH, IMAGE_XEN
   } id;
   enum
     {
@@ -242,6 +242,44 @@ struct image_target_desc image_targets[] =
       .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 },
       .voidp_sizeof = 4,
@@ -541,14 +579,6 @@ grub_target_to_host_real (struct image_target_desc *image_target, grub_uint64_t
 /* 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;
@@ -561,10 +591,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 <grub/lib/LzmaEnc.h>
@@ -772,6 +805,8 @@ 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))
@@ -983,6 +1018,7 @@ 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:
@@ -1506,65 +1542,11 @@ 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 == COMPRESSION_NONE)
@@ -1575,84 +1557,12 @@ 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);
-	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);
-	  }
+	if (image_target->voidp_sizeof == 4)
+	  generate_elf32 (image_target, note, &core_img, &core_size,
+			  target_addr, align, kernel_size, bss_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;
     }
diff --git a/util/grub-mkimagexx.c b/util/grub-mkimagexx.c
index 476d05e..b8174f4 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,10 +57,313 @@
 # 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 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
@@ -1105,6 +1411,7 @@ SUFFIX (load_image) (const char *kernel_path, grub_size_t *exec_size,
 #undef ELFCLASSXX
 #undef Elf_Ehdr
 #undef Elf_Phdr
+#undef Elf_Nhdr
 #undef Elf_Shdr
 #undef Elf_Addr
 #undef Elf_Sym
@@ -1117,3 +1424,4 @@ SUFFIX (load_image) (const char *kernel_path, grub_size_t *exec_size,
 #undef Elf_Half
 #undef Elf_Section
 #undef ELF_ST_TYPE
+#undef XEN_NOTE_SIZE
-- 
1.8.1.4

