grub-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH] xnu


From: phcoder
Subject: [PATCH] xnu
Date: Wed, 15 Apr 2009 00:46:30 +0200
User-agent: Thunderbird 2.0.0.21 (X11/20090409)

Hello. Here is my xnu patch. Tested on i386-pc, i386-efi and x86_64-efi. On non-efi it needs efiemu otherwise you need only include/grub/autoefi.h file from efiemu patch. To resume xnu from hibernation do:
xnu_resume <hibernation file>
Note: you don't need efiemu in this case
How to boot xnu:
<efiemu if not on efi platform>
[on pc only:] vbe_mode=0xYYY # desired vga mode
fsb=133.3 # your fsb frequency
xnu_kernel <kernel> <command line>
<insert modules>
boot
Modules can be inserted one of the following ways:
1) xnu_mkext <mkext file>
2) xnu_kext extension.kext
3) xnu_kext extension.kext/Info.plist extension.kext/extension
4) xnu_kextdir <directory containing extensions>
It's also possible to execute these commands multiple times
The most typical case is
xnu_kernel /mach_kernel rd=disk0sX
xnu_mkext /System/Library/Extensions.mkext


If you need to add values to device tree the command
xnu_devtree <devtree file>
This file uses the following format:
   valuename:valuedata;
   keyname{
     contents
   }
   keyname, valuename and valuedata are in hex.

If you need to adda ramdisk execute
xnu_ramdisk <ramdisk file>
ramdisk will be exposed as /dev/md0 which may be used as boot device with rd=md0.

The areas which need more work (every help is welcome):
1) testing on different platforms
2) Detect "device-properties" value in device tree. There are several trivial values in device tree present in boot.efi but not in grub2. These ones are easy to add and AFAIK don't change anything. But the value "device-properties" is difficult. I already know it's format but not where the values come from. It contains info about gfx and sound card which may not work if this value is missing. The current workaround is xnu_devtree command with a dump of device-properties
3) autodetect fsb frequency
4) Support video splash
5) Define and use an unified interface to retrieve video information uniformly across platforms 6) Boot by UUID. I know how to do it but need md5 for it which is a part of pending luks patch
7) Scripts for automatic creating of grub.cfg entries
8) Support for prelinked kernel. It's compressed and I have yet looked how to decompress it (seems it's compressed with something called lzss)
9) Use claimmap once available (see my multiboot on efi patch)
10) Better collaboration with memory management once advanced mm's available
11) Resume from encrypted hibernation

--

Regards
Vladimir 'phcoder' Serbinenko
diff --git a/conf/i386-efi.rmk b/conf/i386-efi.rmk
index 09c8470..33bb023 100644
--- a/conf/i386-efi.rmk
+++ b/conf/i386-efi.rmk
@@ -194,5 +194,12 @@ fixvideo_mod_SOURCES = commands/efi/fixvideo.c
 fixvideo_mod_CFLAGS = $(COMMON_CFLAGS)
 fixvideo_mod_LDFLAGS = $(COMMON_LDFLAGS)
 
+pkglib_MODULES += xnu.mod
+xnu_mod_SOURCES = loader/xnu_resume.c loader/i386/xnu.c loader/i386/efi/xnu.c\
+        loader/macho.c loader/xnu.c loader/i386/xnu_helper.S
+xnu_mod_CFLAGS = $(COMMON_CFLAGS) -Werror -Wall
+xnu_mod_LDFLAGS = $(COMMON_LDFLAGS)
+xnu_mod_ASFLAGS = $(COMMON_ASFLAGS)
+
 include $(srcdir)/conf/i386.mk
 include $(srcdir)/conf/common.mk
diff --git a/conf/i386-pc.rmk b/conf/i386-pc.rmk
index 7a6d79a..b7d4c77 100644
--- a/conf/i386-pc.rmk
+++ b/conf/i386-pc.rmk
@@ -225,6 +225,13 @@ linux_mod_SOURCES = loader/i386/linux.c
 linux_mod_CFLAGS = $(COMMON_CFLAGS)
 linux_mod_LDFLAGS = $(COMMON_LDFLAGS)
 
+pkglib_MODULES += xnu.mod
+xnu_mod_SOURCES = loader/xnu_resume.c loader/i386/xnu.c loader/i386/pc/xnu.c\
+        loader/macho.c loader/xnu.c loader/i386/xnu_helper.S
+xnu_mod_CFLAGS = $(COMMON_CFLAGS) -Werror -Wall
+xnu_mod_LDFLAGS = $(COMMON_LDFLAGS)
+xnu_mod_ASFLAGS = $(COMMON_ASFLAGS)
+
 #
 # Only arch dependant part of normal.mod will be here. Common part for
 # all architecures of normal.mod is at start and should be kept at sync
diff --git a/conf/x86_64-efi.rmk b/conf/x86_64-efi.rmk
index 59237c1..2fb3cf9 100644
--- a/conf/x86_64-efi.rmk
+++ b/conf/x86_64-efi.rmk
@@ -197,5 +197,12 @@ fixvideo_mod_SOURCES = commands/efi/fixvideo.c
 fixvideo_mod_CFLAGS = $(COMMON_CFLAGS)
 fixvideo_mod_LDFLAGS = $(COMMON_LDFLAGS)
 
+pkglib_MODULES += xnu.mod
+xnu_mod_SOURCES = loader/xnu_resume.c loader/i386/xnu.c loader/i386/efi/xnu.c\
+        loader/macho.c loader/xnu.c loader/i386/xnu_helper.S
+xnu_mod_CFLAGS = $(COMMON_CFLAGS) -Werror -Wall
+xnu_mod_LDFLAGS = $(COMMON_LDFLAGS)
+xnu_mod_ASFLAGS = $(COMMON_ASFLAGS)
+
 include $(srcdir)/conf/i386.mk
 include $(srcdir)/conf/common.mk
diff --git a/include/grub/efi/efi.h b/include/grub/efi/efi.h
index 8c277c0..916f9d6 100644
--- a/include/grub/efi/efi.h
+++ b/include/grub/efi/efi.h
@@ -56,6 +56,7 @@ EXPORT_FUNC(grub_efi_get_device_path) (grub_efi_handle_t 
handle);
 int EXPORT_FUNC(grub_efi_exit_boot_services) (grub_efi_uintn_t map_key);
 void EXPORT_FUNC (grub_reboot) (void);
 void EXPORT_FUNC (grub_halt) (void);
+int EXPORT_FUNC (grub_efi_finish_boot_services) (void);
 
 void grub_efi_mm_init (void);
 void grub_efi_mm_fini (void);
diff --git a/include/grub/i386/macho.h b/include/grub/i386/macho.h
new file mode 100644
index 0000000..61e72a7
--- /dev/null
+++ b/include/grub/i386/macho.h
@@ -0,0 +1,11 @@
+#define GRUB_MACHO_CPUTYPE_IS_HOST32(x) ((x)==0x00000007)
+#define GRUB_MACHO_CPUTYPE_IS_HOST64(x) ((x)==0x01000007)
+
+struct grub_macho_thread32
+{
+  grub_uint32_t cmd;
+  grub_uint32_t cmdsize;
+  grub_uint8_t unknown1[48];
+  grub_uint32_t entry_point;
+  grub_uint8_t unknown2[20];
+} __attribute__ ((packed));
diff --git a/include/grub/i386/xnu.h b/include/grub/i386/xnu.h
new file mode 100644
index 0000000..435b947
--- /dev/null
+++ b/include/grub/i386/xnu.h
@@ -0,0 +1,58 @@
+#ifndef GRUB_CPU_XNU_H
+#define GRUB_CPU_XNU_H 1
+
+#define GRUB_XNU_PAGESIZE 4096
+typedef grub_uint32_t grub_xnu_ptr_t;
+
+struct grub_xnu_boot_params
+{
+  grub_uint16_t verminor; 
+  grub_uint16_t vermajor; 
+  /* Command line passed to xnu. */
+  grub_uint8_t cmdline[1024]; 
+
+  /* Later are the same as EFI's get_memory_map (). */
+  grub_xnu_ptr_t efi_mmap;               
+  grub_uint32_t efi_mmap_size;          
+  grub_uint32_t efi_mem_desc_size;      
+  grub_uint32_t efi_mem_desc_version;   
+
+  /* Later are video parameters. */
+  grub_xnu_ptr_t lfb_base;
+#define GRUB_XNU_VIDEO_SPLASH 1
+#define GRUB_XNU_VIDEO_TEXT_IN_VIDEO 2
+  grub_uint32_t lfb_mode; 
+  grub_uint32_t lfb_line_len;
+  grub_uint32_t lfb_width;
+  grub_uint32_t lfb_height;
+  grub_uint32_t lfb_depth;
+
+  /* Pointer to device tree and its len. */
+  grub_xnu_ptr_t devtree;
+  grub_uint32_t devtreelen;
+  
+  /* First used address by kernel or boot structures. */
+  grub_xnu_ptr_t heap_start;
+  /* Last used address by kernel or boot structures minus previous value. */  
+  grub_uint32_t heap_size;
+  
+  /* First memory page containing runtime code or data. */
+  grub_uint32_t efi_runtime_first_page;
+  /* First memory page containing runtime code or data minus previous value. */
+  grub_uint32_t efi_runtime_npages;
+  grub_uint32_t efi_system_table;
+  /* Size of grub_efi_uintn_t in bits. */
+  grub_uint8_t efi_uintnbits;
+} __attribute__ ((packed));
+#define GRUB_XNU_BOOTARGS_VERMINOR 4
+#define GRUB_XNU_BOOTARGS_VERMAJOR 1
+
+grub_err_t grub_xnu_launch (void);
+extern grub_uint32_t grub_xnu_entry_point;
+extern grub_uint32_t grub_xnu_stack;
+extern grub_uint32_t grub_xnu_arg1;
+extern char grub_xnu_cmdline[1024];
+grub_err_t grub_xnu_boot (void);
+grub_err_t grub_cpu_xnu_fill_devicetree (void);
+grub_err_t grub_xnu_set_video (struct grub_xnu_boot_params *bootparams_relloc);
+#endif
diff --git a/include/grub/macho.h b/include/grub/macho.h
new file mode 100644
index 0000000..7dfd54e
--- /dev/null
+++ b/include/grub/macho.h
@@ -0,0 +1,107 @@
+/*
+ *  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/>.
+ */
+
+#ifndef GRUB_MACHO_H
+#define GRUB_MACHO_H 1
+#include <grub/types.h>
+
+/* Multi-architecture header. Always in big-endian. */
+struct grub_macho_fat_header
+{
+  grub_uint32_t magic;
+  grub_uint32_t nfat_arch;
+} __attribute__ ((packed));
+#define GRUB_MACHO_FAT_MAGIC 0xcafebabe
+
+typedef grub_uint32_t grub_macho_cpu_type_t;
+typedef grub_uint32_t grub_macho_cpu_subtype_t;
+
+/* Architecture descriptor. Always in big-endian. */
+struct grub_macho_fat_arch
+{
+  grub_macho_cpu_type_t cputype;
+  grub_macho_cpu_subtype_t cpusubtype;
+  grub_uint32_t offset;
+  grub_uint32_t size;
+  grub_uint32_t align;
+} __attribute__ ((packed));;
+
+/* File header for 32-bit. Always in native-endian. */
+struct grub_macho_header32
+{
+#define GRUB_MACHO_MAGIC32 0xfeedface
+  grub_uint32_t magic;
+  grub_macho_cpu_type_t cputype;
+  grub_macho_cpu_subtype_t cpusubtype;
+  grub_uint32_t filetype;
+  grub_uint32_t ncmds;
+  grub_uint32_t sizeofcmds;
+  grub_uint32_t flags;
+} __attribute__ ((packed));
+
+/* File header for 64-bit. Always in native-endian. */
+struct grub_macho_header64
+{
+#define GRUB_MACHO_MAGIC64 0xfeedfacf
+  grub_uint32_t magic;
+  grub_macho_cpu_type_t cputype;
+  grub_macho_cpu_subtype_t cpusubtype;
+  grub_uint32_t filetype;
+  grub_uint32_t ncmds;
+  grub_uint32_t sizeofcmds;
+  grub_uint32_t flags;
+  grub_uint32_t reserved;
+} __attribute__ ((packed));
+
+/* Convenience union. What do we need to load to identify the file type. */
+union grub_macho_filestart
+{
+  struct grub_macho_fat_header fat;
+  struct grub_macho_header32 thin32;
+  struct grub_macho_header64 thin64;  
+} __attribute__ ((packed));
+
+/* Common header of Mach-O commands. */
+struct grub_macho_cmd
+{
+  grub_uint32_t cmd;
+  grub_uint32_t cmdsize;
+} __attribute__ ((packed));
+
+typedef grub_uint32_t grub_macho_vmprot_t;
+
+/* 32-bit segment command. */
+struct grub_macho_segment32
+{
+#define GRUB_MACHO_CMD_SEGMENT32  1
+  grub_uint32_t cmd;
+  grub_uint32_t cmdsize;
+  grub_uint8_t segname[16];
+  grub_uint32_t vmaddr;
+  grub_uint32_t vmsize;
+  grub_uint32_t fileoff;
+  grub_uint32_t filesize;
+  grub_macho_vmprot_t maxprot;
+  grub_macho_vmprot_t initprot;
+  grub_uint32_t nsects;
+  grub_uint32_t flags;
+} __attribute__ ((packed));
+
+#define GRUB_MACHO_CMD_THREAD     5
+
+#endif
diff --git a/include/grub/machoload.h b/include/grub/machoload.h
new file mode 100644
index 0000000..572496f
--- /dev/null
+++ b/include/grub/machoload.h
@@ -0,0 +1,62 @@
+/*
+ *  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/>.
+ */
+
+#ifndef GRUB_MACHOLOAD_HEADER
+#define GRUB_MACHOLOAD_HEADER  1
+
+#include <grub/err.h>
+#include <grub/elf.h>
+#include <grub/file.h>
+#include <grub/symbol.h>
+#include <grub/types.h>
+
+struct grub_macho_file
+{
+  grub_file_t file;
+  grub_ssize_t offset32;
+  grub_ssize_t end32;
+  int ncmds32;
+  grub_size_t cmdsize32;
+  grub_uint8_t *cmds32;
+  grub_ssize_t offset64;
+  grub_ssize_t end64;
+  int ncmds64;
+  grub_size_t cmdsize64;
+  grub_uint8_t *cmds64;
+};
+typedef struct grub_macho_file *grub_macho_t;
+
+grub_macho_t grub_macho_open (const char *);
+grub_macho_t grub_macho_file (grub_file_t);
+grub_err_t grub_macho_close (grub_macho_t);
+
+int grub_macho_contains_macho32 (grub_macho_t);
+grub_err_t grub_macho32_size (grub_macho_t macho, grub_addr_t *segments_start,
+                             grub_addr_t *segments_end, int flags);
+grub_uint32_t grub_macho32_get_entry_point (grub_macho_t macho);
+
+/* Ignore BSS segments when loading. */
+#define GRUB_MACHO_NOBSS 0x1
+grub_err_t grub_macho32_load (grub_macho_t macho, char *offset, int flags);
+
+/* Like filesize and file_read but take only 32-bit part 
+   for current architecture. */
+grub_size_t grub_macho32_filesize (grub_macho_t macho);
+grub_err_t grub_macho32_readfile (grub_macho_t macho, void *dest);
+
+#endif /* ! GRUB_MACHOLOAD_HEADER */
diff --git a/include/grub/x86_64/macho.h b/include/grub/x86_64/macho.h
new file mode 100644
index 0000000..165b8da
--- /dev/null
+++ b/include/grub/x86_64/macho.h
@@ -0,0 +1 @@
+#include <grub/i386/macho.h>
diff --git a/include/grub/x86_64/xnu.h b/include/grub/x86_64/xnu.h
new file mode 100644
index 0000000..ae61733
--- /dev/null
+++ b/include/grub/x86_64/xnu.h
@@ -0,0 +1 @@
+#include <grub/i386/xnu.h>
diff --git a/include/grub/xnu.h b/include/grub/xnu.h
new file mode 100644
index 0000000..08ae49b
--- /dev/null
+++ b/include/grub/xnu.h
@@ -0,0 +1,105 @@
+/*
+ *  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/>.
+ */
+
+#ifndef GRUB_XNU_H
+#define GRUB_XNU_H 1
+
+/* Header of a hibernation image. */
+struct grub_xnu_hibernate_header
+{
+  /* Size of the image. Notice that file containing image is usually bigger. */
+  grub_uint64_t image_size;
+  grub_uint8_t unknown1[8];
+  /* Where to copy lauchcode?*/
+  grub_uint32_t launchcode_target_page;
+  /* How many pages of launchcode? */
+  grub_uint32_t launchcode_numpages;
+  /* Where to jump? */
+  grub_uint32_t entry_point;
+  /* %esp at start. */
+  grub_uint32_t stack;
+  grub_uint8_t unknown2[44];
+#define GRUB_XNU_HIBERNATE_MAGIC 0x73696d65  
+  grub_uint32_t magic;
+  grub_uint8_t unknown3[28];
+  /* This value is non-zero if page is encrypted. Unsupported. */
+  grub_uint64_t encoffset;
+  grub_uint8_t unknown4[360];
+  /* The size of additional header used to locate image without parsing FS.
+     Used only to skip it.
+   */
+  grub_uint32_t extmapsize;
+} __attribute__ ((packed));
+
+/* In-memory structure for temporary keeping device tree. */
+struct grub_xnu_devtree_key
+{
+  char *name; 
+  int datasize; /* -1 for not leaves. */
+  union
+  {
+    struct grub_xnu_devtree_key *first_child;
+    void *data;
+  };
+  struct grub_xnu_devtree_key *next;
+};
+
+/* A structure used in memory-map values. */
+struct 
+grub_xnu_extdesc
+{
+  grub_uint32_t addr;
+  grub_uint32_t size;
+} __attribute__ ((packed));
+
+/* Header describing extension in the memory. */
+struct grub_xnu_extheader
+{
+  grub_uint32_t infoplistaddr;
+  grub_uint32_t infoplistsize;
+  grub_uint32_t binaryaddr;
+  grub_uint32_t binarysize;
+} __attribute__ ((packed));
+
+struct grub_xnu_devtree_key *grub_xnu_create_key (struct grub_xnu_devtree_key 
**parent,
+                                                 char *name);
+
+extern struct grub_xnu_devtree_key *grub_xnu_devtree_root;
+
+void grub_xnu_free_devtree (struct grub_xnu_devtree_key *cur);
+
+grub_err_t grub_xnu_writetree_toheap (void **start, grub_size_t *size);
+struct grub_xnu_devtree_key *grub_xnu_create_value (struct 
grub_xnu_devtree_key **parent,
+                                                   char *name);
+
+void grub_xnu_lock (void);
+void grub_xnu_unlock (void);
+grub_err_t grub_xnu_resume (char *imagename);
+struct grub_xnu_devtree_key *grub_xnu_find_key (struct grub_xnu_devtree_key 
*parent,
+                                               char *name);
+grub_err_t grub_xnu_align_heap (int align);
+grub_err_t grub_xnu_scan_dir_for_kexts (char *dirname, char *osbundlerequired, 
+                                       int maxrecursion);
+grub_err_t grub_xnu_load_kext_from_dir (char *dirname, char *osbundlerequired, 
+                                       int maxrecursion);
+void *grub_xnu_heap_malloc (int size);
+extern grub_uint32_t grub_xnu_heap_real_start;
+extern grub_size_t grub_xnu_heap_size;
+extern char *grub_xnu_heap_start;
+extern grub_addr_t grub_xnu_heap_will_be_at;
+#endif
diff --git a/kern/efi/efi.c b/kern/efi/efi.c
index 9c9a400..075732a 100644
--- a/kern/efi/efi.c
+++ b/kern/efi/efi.c
@@ -734,3 +734,26 @@ grub_efi_print_device_path (grub_efi_device_path_t *dp)
       dp = (grub_efi_device_path_t *) ((char *) dp + len);
     }
 }
+
+int
+grub_efi_finish_boot_services (void)
+{
+  grub_efi_uintn_t mmap_size = 0;
+  grub_efi_uintn_t map_key;
+  grub_efi_uintn_t desc_size;
+  grub_efi_uint32_t desc_version;
+  void *mmap_buf = 0;
+
+  if (grub_efi_get_memory_map (&mmap_size, mmap_buf, &map_key,
+                              &desc_size, &desc_version) < 0)
+    return 0;
+
+  mmap_buf = grub_malloc (mmap_size);
+  
+  if (grub_efi_get_memory_map (&mmap_size, mmap_buf, &map_key,
+                              &desc_size, &desc_version) <= 0)
+    return 0;
+
+  return  grub_efi_exit_boot_services (map_key);
+}
+
diff --git a/kern/efi/mm.c b/kern/efi/mm.c
index 35b12ab..4635776 100644
--- a/kern/efi/mm.c
+++ b/kern/efi/mm.c
@@ -47,7 +47,7 @@ static struct allocated_page *allocated_pages = 0;
 
 /* The minimum and maximum heap size for GRUB itself.  */
 #define MIN_HEAP_SIZE  0x100000
-#define MAX_HEAP_SIZE  (16 * 0x100000)
+#define MAX_HEAP_SIZE  (1600 * 0x100000)
 
 
 /* Allocate pages. Return the pointer to the first of allocated pages.  */
diff --git a/loader/i386/efi/xnu.c b/loader/i386/efi/xnu.c
new file mode 100644
index 0000000..5885c37
--- /dev/null
+++ b/loader/i386/efi/xnu.c
@@ -0,0 +1,177 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2009  Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/env.h>
+#include <grub/xnu.h>
+#include <grub/cpu/xnu.h>
+#include <grub/efi/api.h>
+#include <grub/efi/efi.h>
+#include <grub/efi/uga_draw.h>
+#include <grub/pci.h>
+#include <grub/misc.h>
+
+/* Setup video for xnu. Big parts are copied from linux.c. */
+
+static grub_efi_guid_t uga_draw_guid = GRUB_EFI_UGA_DRAW_GUID;
+
+#define RGB_MASK       0xffffff
+#define RGB_MAGIC      0x121314
+#define LINE_MIN       800
+#define LINE_MAX       4096
+#define FBTEST_STEP    (0x10000 >> 2)
+#define FBTEST_COUNT   8
+
+static int
+find_line_len (grub_uint32_t *fb_base, grub_uint32_t *line_len)
+{
+  grub_uint32_t *base = (grub_uint32_t *) (grub_target_addr_t) *fb_base;
+  int i;
+
+  for (i = 0; i < FBTEST_COUNT; i++, base += FBTEST_STEP)
+    {
+      if ((*base & RGB_MASK) == RGB_MAGIC)
+       {
+         int j;
+
+         for (j = LINE_MIN; j <= LINE_MAX; j++)
+           {
+             if ((base[j] & RGB_MASK) == RGB_MAGIC)
+               {
+                 *fb_base = (grub_uint32_t) (grub_target_addr_t) base;
+                 *line_len = j << 2;
+
+                 return 1;
+               }
+           }
+
+         break;
+       }
+    }
+
+  return 0;
+}
+
+static int
+find_framebuf (grub_uint32_t *fb_base, grub_uint32_t *line_len)
+{
+  int found = 0;
+
+  auto int NESTED_FUNC_ATTR find_card (int bus, int dev, int func,
+                                      grub_pci_id_t pciid);
+
+  int NESTED_FUNC_ATTR find_card (int bus, int dev, int func,
+                                 grub_pci_id_t pciid)
+    {
+      grub_pci_address_t addr;
+
+      addr = grub_pci_make_address (bus, dev, func, 2);
+      if (grub_pci_read (addr) >> 24 == 0x3)
+       {
+         int i;
+
+         grub_printf ("Display controller: %d:%d.%d\nDevice id: %x\n",
+                      bus, dev, func, pciid);
+         addr += 8;
+         for (i = 0; i < 6; i++, addr += 4)
+           {
+             grub_uint32_t old_bar1, old_bar2, type;
+             grub_uint64_t base64;
+
+             old_bar1 = grub_pci_read (addr);
+             if ((! old_bar1) || (old_bar1 & GRUB_PCI_ADDR_SPACE_IO))
+               continue;
+
+             type = old_bar1 & GRUB_PCI_ADDR_MEM_TYPE_MASK;
+             if (type == GRUB_PCI_ADDR_MEM_TYPE_64)
+               {
+                 if (i == 5)
+                   break;
+
+                 old_bar2 = grub_pci_read (addr + 4);
+               }
+             else
+               old_bar2 = 0;
+
+             base64 = old_bar2;
+             base64 <<= 32;
+             base64 |= (old_bar1 & GRUB_PCI_ADDR_MEM_MASK);
+
+             grub_printf ("%s(%d): 0x%llx\n",
+                          ((old_bar1 & GRUB_PCI_ADDR_MEM_PREFETCH) ?
+                           "VMEM" : "MMIO"), i,
+                          (unsigned long long) base64);
+
+             if ((old_bar1 & GRUB_PCI_ADDR_MEM_PREFETCH) && (! found))
+               {
+                 *fb_base = base64;
+                 if (find_line_len (fb_base, line_len))
+                   found++;
+               }
+
+             if (type == GRUB_PCI_ADDR_MEM_TYPE_64)
+               {
+                 i++;
+                 addr += 4;
+               }
+           }
+       }
+
+      return found;
+    }
+
+  grub_pci_iterate (find_card);
+  return found;
+}
+
+grub_err_t
+grub_xnu_set_video (struct grub_xnu_boot_params *params)
+{
+  grub_efi_uga_draw_protocol_t *c;
+  grub_uint32_t width, height, depth, rate, pixel, fb_base, line_len;
+  int ret;
+
+  c = grub_efi_locate_protocol (&uga_draw_guid, 0);
+  if (! c)
+    return grub_error (GRUB_ERR_IO, "Couldn't find UGADraw");
+
+  if (efi_call_5 (c->get_mode, c, &width, &height, &depth, &rate))
+    return grub_error (GRUB_ERR_IO, "Couldn't retrieve video mode");
+
+  grub_printf ("Video mode: address@hidden", width, height, depth, rate);
+
+  grub_efi_set_text_mode (0);
+  pixel = RGB_MAGIC;
+  efi_call_10 (c->blt, c, (struct grub_efi_uga_pixel *) &pixel,
+              GRUB_EFI_UGA_VIDEO_FILL, 0, 0, 0, 0, 1, height, 0);
+  ret = find_framebuf (&fb_base, &line_len);
+  grub_efi_set_text_mode (1);
+
+  if (! ret)
+    return grub_error (GRUB_ERR_IO, "Can\'t find frame buffer address\n");
+
+  grub_printf ("Frame buffer base: 0x%x\n", fb_base);
+  grub_printf ("Video line length: %d\n", line_len);
+
+  params->lfb_width = width;
+  params->lfb_height = height;
+  params->lfb_depth = depth;
+  params->lfb_line_len = line_len;
+
+  params->lfb_base = fb_base;
+  return GRUB_ERR_NONE;
+}
diff --git a/loader/i386/pc/xnu.c b/loader/i386/pc/xnu.c
new file mode 100644
index 0000000..2c78b2b
--- /dev/null
+++ b/loader/i386/pc/xnu.c
@@ -0,0 +1,67 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2009  Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/env.h>
+#include <grub/misc.h>
+#include <grub/xnu.h>
+#include <grub/cpu/xnu.h>
+#include <grub/machine/vbe.h>
+#include <grub/machine/vga.h>
+
+/* Setup video for xnu. */
+
+grub_err_t
+grub_xnu_set_video (struct grub_xnu_boot_params *bootparams_relloc)
+{
+  grub_uint32_t use_mode = GRUB_VBE_DEFAULT_VIDEO_MODE;
+  char *modevar;
+  grub_err_t err;
+  struct grub_vbe_mode_info_block mode_info;
+
+  /* Check existence of vbe_mode environment variable.  */
+  modevar = grub_env_get ("vbe_mode");
+  if (modevar != 0)
+    {
+      unsigned long value;
+
+      value = grub_strtoul (modevar, 0, 0);
+      if (grub_errno == GRUB_ERR_NONE)
+       use_mode = value;
+      else
+       grub_errno = GRUB_ERR_NONE;
+    }
+  
+  if (use_mode < 0x100)
+    return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, 
+                      "booting xnu in text mode isn't supported yet");
+  err = grub_vbe_set_video_mode (use_mode, &mode_info);
+  if (err != GRUB_ERR_NONE)
+    return err;
+
+  /* FIXME: setting non-32bit color depth results in strange screens. */
+  if (use_mode >= 0x100)
+    {
+      bootparams_relloc->lfb_base = mode_info.phys_base_addr;
+      bootparams_relloc->lfb_mode = GRUB_XNU_VIDEO_TEXT_IN_VIDEO; 
+      bootparams_relloc->lfb_line_len = mode_info.bytes_per_scan_line;
+      bootparams_relloc->lfb_width = mode_info.x_resolution;
+      bootparams_relloc->lfb_height = mode_info.y_resolution;
+      bootparams_relloc->lfb_depth = mode_info.bits_per_pixel;
+    }
+  return GRUB_ERR_NONE;
+}
diff --git a/loader/i386/xnu.c b/loader/i386/xnu.c
new file mode 100644
index 0000000..487b1d1
--- /dev/null
+++ b/loader/i386/xnu.c
@@ -0,0 +1,471 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2009  Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/env.h>
+#include <grub/file.h>
+#include <grub/disk.h>
+#include <grub/xnu.h>
+#include <grub/cpu/xnu.h>
+#include <grub/mm.h>
+#include <grub/loader.h>
+#include <grub/cpu/loader.h>
+#include <grub/autoefi.h>
+#include <grub/i386/tsc.h>
+#include <grub/i386/pit.h>
+#include <grub/misc.h>
+
+char grub_xnu_cmdline[1024];
+
+/* Aliases set for some tables. */
+struct tbl_alias
+{
+  grub_efi_guid_t guid;
+  char *name;
+};
+
+struct tbl_alias table_aliases[] =
+  {
+    {GRUB_EFI_ACPI_20_TABLE_GUID, "ACPI_20"},
+    {GRUB_EFI_ACPI_TABLE_GUID, "ACPI"},
+  };
+
+/* The following function is used to be able to debug xnu loader 
+   with grub-emu. */
+#ifdef GRUB_UTIL
+grub_err_t 
+grub_xnu_launch (void)
+{
+  grub_printf ("Fake launch %x:%p:%p", grub_xnu_entry_point, grub_xnu_arg1,
+              grub_xnu_stack);
+  grub_getkey ();
+  return 0;
+}
+#endif
+
+static int
+utf16_strlen (grub_uint16_t *in)
+{
+  int i;
+  for (i = 0; in[i]; i++);
+  return i;
+}
+
+/* Read frequency from a string in MHz and return it in Hz. */
+static grub_uint64_t
+readfrequency (const char *str)
+{
+  grub_uint64_t num = 0;
+  int mul = 1000000;
+  int found = 0;
+
+  while (*str)
+    {
+      unsigned long digit;
+      
+      digit = grub_tolower (*str) - '0';
+      if (digit > 9)
+       break;
+
+      found = 1;
+
+      num = num * 10 + digit;
+      str++;
+    }
+  num *= 1000000;
+  if (*str == '.')
+    {
+      str++;
+      while (*str)
+       {
+         unsigned long digit;
+         
+         digit = grub_tolower (*str) - '0';
+         if (digit > 9)
+           break;
+
+         found = 1;
+
+         mul /= 10;
+         num = num + mul * digit;
+         str++;
+       }
+    }
+  if (! found)
+    return 0;
+
+  return num;
+}
+
+/* FIXME: autodetect FSB. */
+static grub_uint64_t
+guessfsb (void)
+{
+  const grub_uint64_t sane_value = 100000000;
+  return sane_value;
+}
+
+/* Fill device tree. */
+/* FIXME: some entries may be platform-agnostic. Move them to loader/xnu.c. */
+grub_err_t
+grub_cpu_xnu_fill_devicetree (void)
+{
+  struct grub_xnu_devtree_key *efikey;
+  struct grub_xnu_devtree_key *cfgtablekey;
+  struct grub_xnu_devtree_key *curval;
+  struct grub_xnu_devtree_key *runtimesrvkey;
+  struct grub_xnu_devtree_key *platformkey;
+  unsigned i, j;
+
+  /* The value "model". */
+  /* FIXME: may this value be sometimes different? */
+  curval = grub_xnu_create_value (&grub_xnu_devtree_root, "model");
+  if (! curval)
+    return grub_errno;
+  curval->datasize = sizeof ("ACPI");
+  curval->data = grub_strdup ("ACPI");
+  curval = grub_xnu_create_value (&grub_xnu_devtree_root, "compatible");
+  if (! curval)
+    return grub_errno;
+  curval->datasize = sizeof ("ACPI");
+  curval->data = grub_strdup ("ACPI");
+
+  /* The key "efi". */
+  efikey = grub_xnu_create_key (&grub_xnu_devtree_root, "efi");
+  if (! efikey)
+    return grub_errno;
+
+  /* Information about firmware. */
+  curval = grub_xnu_create_value (&(efikey->first_child), "firmware-revision");
+  if (! curval)
+    return grub_errno;
+  curval->datasize = (SYSTEM_TABLE_SIZEOF (firmware_revision));
+  curval->data = grub_malloc (curval->datasize);
+  if (! curval->data)
+    return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't create device tree");
+  grub_memcpy (curval->data, (SYSTEM_TABLE_VAR(firmware_revision)),
+              curval->datasize);
+
+  curval = grub_xnu_create_value (&(efikey->first_child), "firmware-vendor");
+  if (! curval)
+    return grub_errno;
+  curval->datasize = 
+    2 * (utf16_strlen (SYSTEM_TABLE_PTR (firmware_vendor)) + 1);
+  curval->data = grub_malloc (curval->datasize);
+  if (! curval->data)
+    return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't create device tree");
+  grub_memcpy (curval->data, SYSTEM_TABLE_PTR (firmware_vendor),
+              curval->datasize);
+
+  curval = grub_xnu_create_value (&(efikey->first_child), "firmware-abi");
+  if (! curval)
+    return grub_errno;
+  curval->datasize = sizeof ("EFI32");
+  curval->data = grub_malloc (curval->datasize);
+  if (! curval->data)
+    return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't create device tree");
+  if (SIZEOF_OF_UINTN == 4)
+    grub_memcpy (curval->data, "EFI32", curval->datasize);
+  else
+    grub_memcpy (curval->data, "EFI64", curval->datasize);
+
+  /* The key "platform". */
+  platformkey = grub_xnu_create_key (&(efikey->first_child), 
+                                    "platform");
+  if (! platformkey)
+    return grub_errno;
+
+  /* Pass FSB frequency to the kernel. */
+  curval = grub_xnu_create_value (&(platformkey->first_child), "FSBFrequency");
+  if (! curval)
+    return grub_errno;
+  curval->datasize = sizeof (grub_uint64_t);
+  curval->data = grub_malloc (curval->datasize);
+  if (!curval->data)
+    return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't create device tree");
+
+  /* First see if user supplies the value. */
+  char *fsbvar = grub_env_get ("fsb");
+  if (! fsbvar)
+    *((grub_uint64_t *) curval->data) = 0; 
+  else
+    *((grub_uint64_t *) curval->data) = readfrequency (fsbvar);
+  /* Try autodetect. */
+  if (! *((grub_uint64_t *) curval->data))
+    *((grub_uint64_t *) curval->data) = guessfsb (); 
+
+  cfgtablekey = grub_xnu_create_key (&(efikey->first_child), 
+                                    "configuration-table");
+  if (!cfgtablekey)
+    return grub_errno;
+
+  /* Fill "configuration-table" key. */
+  for (i = 0; i < SYSTEM_TABLE (num_table_entries); i++)
+    {
+      void *ptr;
+      struct grub_xnu_devtree_key *curkey;
+      grub_efi_guid_t guid;
+      char guidbuf[64];
+
+      /* Retrieve current key. */
+#ifdef GRUB_MACHINE_EFI
+      {
+       ptr = (void *) 
+         grub_efi_system_table->configuration_table[i].vendor_table;
+       guid = grub_efi_system_table->configuration_table[i].vendor_guid;
+      }
+#else 
+      if (SIZEOF_OF_UINTN == 4)
+       {
+         ptr = UINT_TO_PTR (((grub_efiemu_configuration_table32_t *)
+                             SYSTEM_TABLE_PTR (configuration_table))[i]
+                            .vendor_table);
+         guid =
+           ((grub_efiemu_configuration_table32_t *)
+            SYSTEM_TABLE_PTR (configuration_table))[i].vendor_guid;
+       }
+      else
+       {
+         ptr = UINT_TO_PTR (((grub_efiemu_configuration_table64_t *)
+                             SYSTEM_TABLE_PTR (configuration_table))[i]
+                            .vendor_table);
+         guid = 
+           ((grub_efiemu_configuration_table64_t *)
+            SYSTEM_TABLE_PTR (configuration_table))[i].vendor_guid;
+       }
+#endif
+
+      /* The name of key for new table. */
+      grub_sprintf (guidbuf, "%08x-%04x-%04x-%02x%02x-",
+                   guid.data1, guid.data2, guid.data3, guid.data4[0],
+                   guid.data4[1]);
+      for (j = 2; j < 8; j++)
+       grub_sprintf (guidbuf + grub_strlen (guidbuf), "%02x", guid.data4[j]);
+      /* For some reason GUID has to be in uppercase. */
+      for (j = 0; guidbuf[j] ; j++)
+       if (guidbuf[j] >= 'a' && guidbuf[j] <= 'f')
+         guidbuf[j] += 'A' - 'a';
+      curkey = grub_xnu_create_key (&(cfgtablekey->first_child), guidbuf);
+      if (! curkey)
+       return grub_errno;
+
+      curval = grub_xnu_create_value (&(curkey->first_child), "guid");
+      if (! curval)
+       return grub_errno;
+      curval->datasize = sizeof (guid);
+      curval->data = grub_malloc (curval->datasize);
+      if (! curval->data)
+       return grub_error (GRUB_ERR_OUT_OF_MEMORY, 
+                          "couldn't create device tree");
+      grub_memcpy (curval->data, &guid, curval->datasize);
+
+      /* The value "table". */
+      curval = grub_xnu_create_value (&(curkey->first_child), "table");
+      if (! curval)
+       return grub_errno;
+      curval->datasize = SIZEOF_OF_UINTN;
+      curval->data = grub_malloc (curval->datasize);
+      if (! curval->data)
+       return grub_error (GRUB_ERR_OUT_OF_MEMORY, 
+                          "couldn't create device tree");
+      if (SIZEOF_OF_UINTN == 4)
+       *((grub_uint32_t *)curval->data) = PTR_TO_UINT32 (ptr);
+      else
+       *((grub_uint64_t *)curval->data) = PTR_TO_UINT64 (ptr);
+
+      /* Create alias. */
+      for (j = 0; j < sizeof (table_aliases) / sizeof (table_aliases[0]); j++)
+       if (grub_memcmp (&table_aliases[j].guid, &guid, sizeof (guid)) == 0)
+         break;
+      if (j != sizeof (table_aliases) / sizeof (table_aliases[0]))
+       {
+         curval = grub_xnu_create_value (&(curkey->first_child), "alias");
+         if (!curval)
+           return grub_errno;
+         curval->datasize = grub_strlen (table_aliases[j].name) + 1;
+         curval->data = grub_malloc (curval->datasize);
+         if (!curval->data)
+           return grub_error (GRUB_ERR_OUT_OF_MEMORY, 
+                              "couldn't create device tree");
+         grub_memcpy (curval->data, table_aliases[j].name, curval->datasize);
+       }
+    }
+  
+  /* Create and fill "runtime-services" key. */
+  runtimesrvkey = grub_xnu_create_key (&(efikey->first_child), 
+                                      "runtime-services");
+  if (! runtimesrvkey)
+    return grub_errno;
+  curval = grub_xnu_create_value (&(runtimesrvkey->first_child), "table");
+  if (! curval)
+    return grub_errno;
+  curval->datasize = SIZEOF_OF_UINTN;
+  curval->data = grub_malloc (curval->datasize);
+  if (! curval->data)
+    return grub_error (GRUB_ERR_OUT_OF_MEMORY, 
+                      "couldn't create device tree");
+  if (SIZEOF_OF_UINTN == 4)
+    *((grub_uint32_t *) curval->data) 
+      = PTR_TO_UINT32 (SYSTEM_TABLE_PTR (runtime_services));
+  else
+    *((grub_uint64_t *) curval->data) 
+      = PTR_TO_UINT64 (SYSTEM_TABLE_PTR (runtime_services));
+  
+  return GRUB_ERR_NONE;
+}
+
+/* Boot xnu. */
+grub_err_t 
+grub_xnu_boot (void)
+{
+  struct grub_xnu_boot_params *bootparams_relloc;
+  grub_off_t bootparams_relloc_off;
+  grub_off_t mmap_relloc_off;
+  grub_err_t err;
+  grub_efi_uintn_t memory_map_size = 0;
+  grub_efi_memory_descriptor_t *memory_map;
+  grub_efi_uintn_t map_key = 0;
+  grub_efi_uintn_t descriptor_size = 0;
+  grub_efi_uint32_t descriptor_version = 0;
+  grub_uint64_t firstruntimeaddr, lastruntimeaddr;
+  void *devtree;
+  grub_size_t devtreelen;
+  int i;
+
+  /* Page-align to avoid following parts to be inadvertently freed. */
+  if ((err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE)))
+    return err;
+
+  /* Pass memory map to kernel. */
+  memory_map_size = 0;
+  memory_map = 0;
+  map_key = 0;
+  descriptor_size = 0;
+  descriptor_version = 0;
+
+  if (grub_autoefi_get_memory_map (&memory_map_size, memory_map,
+                                  &map_key, &descriptor_size,
+                                  &descriptor_version) < 0)
+    return grub_errno;
+
+  memory_map = grub_xnu_heap_malloc (memory_map_size);
+  if (! memory_map)
+    return grub_errno;
+
+  if (grub_autoefi_get_memory_map (&memory_map_size, memory_map,
+                                  &map_key, &descriptor_size,
+                                  &descriptor_version) <= 0)
+    return grub_errno;
+  mmap_relloc_off = (grub_uint8_t *) memory_map 
+    - (grub_uint8_t *) grub_xnu_heap_start;
+
+  firstruntimeaddr = (grub_uint64_t) (-1); 
+  lastruntimeaddr = 0;
+  for (i = 0; (unsigned) i < memory_map_size / descriptor_size; i++)
+    {
+      grub_efi_memory_descriptor_t *curdesc = (grub_efi_memory_descriptor_t *) 
+       ((char *) memory_map + descriptor_size * i);
+
+      /* Some EFI implementations set physical_start to 0 which 
+        causes XNU crash. */
+      curdesc->virtual_start = curdesc->physical_start;
+
+      if (curdesc->type == GRUB_EFI_RUNTIME_SERVICES_DATA
+         || curdesc->type == GRUB_EFI_RUNTIME_SERVICES_CODE)
+       {
+         if (firstruntimeaddr > curdesc->physical_start)
+           firstruntimeaddr = curdesc->physical_start;
+         if (lastruntimeaddr < curdesc->physical_start
+             + curdesc->num_pages * 4096)
+           lastruntimeaddr = curdesc->physical_start
+             + curdesc->num_pages * 4096;
+       }
+    }
+
+  /* Relocate the boot parameters to heap. */
+  bootparams_relloc = grub_xnu_heap_malloc (sizeof (*bootparams_relloc));
+  if (! bootparams_relloc)
+    return grub_errno;
+  bootparams_relloc_off = (grub_uint8_t *) bootparams_relloc 
+    - (grub_uint8_t *) grub_xnu_heap_start;
+  if ((err = grub_xnu_writetree_toheap (&devtree, &devtreelen)))
+    return err;
+  bootparams_relloc = (struct grub_xnu_boot_params *)
+    (bootparams_relloc_off + (grub_uint8_t *) grub_xnu_heap_start);
+
+  grub_memcpy (bootparams_relloc->cmdline, grub_xnu_cmdline, 
+              sizeof (bootparams_relloc->cmdline));
+
+  bootparams_relloc->devtree = PTR_TO_UINT32 (devtree);
+  bootparams_relloc->devtreelen = devtreelen;
+
+  bootparams_relloc->heap_start = PTR_TO_UINT32 (grub_xnu_heap_start);
+  bootparams_relloc->heap_size = grub_xnu_heap_size;
+
+  bootparams_relloc->efi_mmap 
+    = PTR_TO_UINT32 ((grub_uint8_t *)grub_xnu_heap_start + mmap_relloc_off);
+  bootparams_relloc->efi_mmap_size = memory_map_size;          
+  bootparams_relloc->efi_mem_desc_size = descriptor_size;      
+  bootparams_relloc->efi_mem_desc_version = descriptor_version;   
+
+  bootparams_relloc->efi_runtime_first_page = firstruntimeaddr 
+    / GRUB_XNU_PAGESIZE;
+  bootparams_relloc->efi_runtime_npages 
+    = ((lastruntimeaddr + GRUB_XNU_PAGESIZE - 1) / GRUB_XNU_PAGESIZE) 
+    - (firstruntimeaddr / GRUB_XNU_PAGESIZE);
+  bootparams_relloc->efi_uintnbits = SIZEOF_OF_UINTN * 8;
+  bootparams_relloc->efi_system_table 
+    = PTR_TO_UINT32 (grub_autoefi_system_table);
+
+  bootparams_relloc->verminor = GRUB_XNU_BOOTARGS_VERMINOR;  
+  bootparams_relloc->vermajor = GRUB_XNU_BOOTARGS_VERMAJOR;
+
+  /* Set video. */
+  err = grub_xnu_set_video (bootparams_relloc);
+  if (err != GRUB_ERR_NONE)
+    {
+      grub_print_error ();
+      grub_errno = GRUB_ERR_NONE;
+      grub_printf ("Using fake framebuffer\n");
+
+      /* Setup pseudo 800x600x32 framebuffer. */
+      bootparams_relloc->lfb_mode = GRUB_XNU_VIDEO_TEXT_IN_VIDEO; 
+      bootparams_relloc->lfb_width = 800;
+      bootparams_relloc->lfb_height = 600;
+      bootparams_relloc->lfb_depth = 32;
+      bootparams_relloc->lfb_line_len = 3200;
+      bootparams_relloc->lfb_base = bootparams_relloc->heap_size 
+       + grub_xnu_heap_will_be_at;
+      bootparams_relloc->heap_size += 1920000;
+    }
+
+  /* Parameters for asm helper. */
+  grub_xnu_stack = bootparams_relloc->heap_start 
+    + bootparams_relloc->heap_size + GRUB_XNU_PAGESIZE;
+  grub_xnu_arg1 = (long) bootparams_relloc;
+  grub_xnu_entry_point = PTR_TO_UINT32
+    (grub_xnu_heap_start + grub_xnu_entry_point);
+  grub_dprintf ("xnu", "eip=%x\n", grub_xnu_entry_point);
+  grub_dprintf ("xnu", "launch=%p\n", grub_xnu_launch);
+
+  grub_autoefi_finish_boot_services ();
+
+  grub_xnu_launch ();
+
+  /* Never reaches here. */
+  return 0;
+}
diff --git a/loader/i386/xnu_helper.S b/loader/i386/xnu_helper.S
new file mode 100644
index 0000000..0a1c129
--- /dev/null
+++ b/loader/i386/xnu_helper.S
@@ -0,0 +1,88 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2009  Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/symbol.h>
+       
+       .p2align        2       /* force 4-byte alignment */
+
+FUNCTION(grub_xnu_launch)
+       cli
+       
+#ifdef __x86_64__
+       /* Switch to compatibility mode. */
+
+       lgdt gdtdesc
+
+       jmp cont1
+cont1:
+       .code32
+       
+       /* Disable paging. */
+       mov %cr0, %eax
+       and $0x7fffffff, %eax
+       mov %eax, %cr0
+       
+       /* Disable amd64. */
+       mov $0xc0000080, %ecx
+       rdmsr
+       and $0xfffffeff, %eax
+       wrmsr
+
+       jmp cont2
+cont2: 
+#endif
+
+       .code32
+
+       /* Registers on XNU boot: eip, esp and eax. */
+       /* mov imm32, %ecx */
+       .byte   0xb9
+VARIABLE (grub_xnu_entry_point)
+       .long 0
+       /* mov imm32, %eax */
+       .byte   0xb8
+VARIABLE (grub_xnu_arg1)
+       .long 0
+       /* mov imm32, %ebx */
+       .byte   0xbb
+VARIABLE (grub_xnu_stack)
+       .long 0
+       
+       movl %ebx, %esp
+       jmp *%ecx
+
+#ifdef __x86_64__
+       /* GDT. Copied from loader/i386/linux.c. */
+       .p2align 4
+gdt:   
+       /* NULL.  */
+       .byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+       
+       /* Reserved.  */
+       .byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+       
+       /* Code segment.  */
+       .byte 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x9A, 0xCF, 0x00
+       
+       /* Data segment.  */
+       .byte 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x92, 0xCF, 0x00
+
+gdtdesc:       
+       .word 32
+       .quad gdt
+#endif
\ No newline at end of file
diff --git a/loader/macho.c b/loader/macho.c
new file mode 100644
index 0000000..da081a2
--- /dev/null
+++ b/loader/macho.c
@@ -0,0 +1,395 @@
+/* macho.c - load Mach-O files. */
+/*
+ *  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/>.
+ */
+
+/* This Mach-O loader is incomplete and can load only non-relocatable segments.
+   This is however enough to boot xnu (otool -l and Mach-O specs for more 
info).
+*/
+
+#include <grub/err.h>
+#include <grub/macho.h>
+#include <grub/cpu/macho.h>
+#include <grub/machoload.h>
+#include <grub/file.h>
+#include <grub/gzio.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+
+#define min(a,b) (((a) < (b)) ? (a) : (b))
+
+/* 32-bit. */
+
+int
+grub_macho_contains_macho32 (grub_macho_t macho)
+{
+  return macho->offset32 != -1;
+}
+
+static void
+grub_macho_parse32 (grub_macho_t macho)
+{
+  struct grub_macho_header32 head;
+
+  /* Is there any candidate at all? */
+  if (macho->offset32 == -1)
+    return;
+
+  /* Read header and check magic*/
+  if (grub_file_seek (macho->file, macho->offset32) == (grub_off_t) -1
+      || grub_file_read (macho->file, (char *) &head, sizeof (head)) 
+      != sizeof(head))
+    {
+      grub_error (GRUB_ERR_READ_ERROR, "Cannot read Mach-O header.");  
+      macho->offset32 = -1;
+      return;
+    }
+  if (head.magic != GRUB_MACHO_MAGIC32)
+    {
+      grub_error (GRUB_ERR_BAD_OS, "Invalid Mach-O 32-bit header.");  
+      macho->offset32 = -1;
+      return;
+    }
+
+  /* Read commands. */
+  macho->ncmds32 = head.ncmds;
+  macho->cmdsize32 = head.sizeofcmds;
+  macho->cmds32 = grub_malloc(macho->cmdsize32);
+  if (! macho->cmds32)
+    {
+      grub_error (GRUB_ERR_OUT_OF_MEMORY, "not enough memory to read 
commands");
+      return;
+    }
+  if (grub_file_read (macho->file, (char *) macho->cmds32, 
+                     (grub_size_t) macho->cmdsize32) 
+      != (grub_ssize_t) macho->cmdsize32)
+    {
+      grub_error (GRUB_ERR_READ_ERROR, "Cannot read Mach-O header.");  
+      macho->offset32 = -1;
+    }
+}
+
+typedef int NESTED_FUNC_ATTR (*grub_macho_iter_hook_t)
+(grub_macho_t , struct grub_macho_cmd *, 
+              void *);
+
+static grub_err_t
+grub_macho32_cmds_iterate (grub_macho_t macho,
+                          grub_macho_iter_hook_t hook,
+                          void *hook_arg)
+{
+  grub_uint8_t *hdrs = macho->cmds32;
+  int i;
+  if (! macho->cmds32)
+    return grub_error (GRUB_ERR_BAD_OS, "Couldn't find 32-bit Mach-O");
+  for (i = 0; i < macho->ncmds32; i++)
+    {
+      struct grub_macho_cmd *hdr = (struct grub_macho_cmd *) hdrs;
+      if (hook (macho, hdr, hook_arg))
+       break;
+      hdrs += hdr->cmdsize;
+    }
+
+  return grub_errno;
+}
+
+grub_size_t
+grub_macho32_filesize (grub_macho_t macho)
+{
+  if (grub_macho_contains_macho32 (macho))
+    return macho->end32 - macho->offset32;
+  return 0;
+}
+
+grub_err_t
+grub_macho32_readfile (grub_macho_t macho, void *dest)
+{
+  grub_ssize_t read;
+  if (! grub_macho_contains_macho32 (macho))
+    return grub_error (GRUB_ERR_BAD_OS, 
+                      "Couldn't read arcitecture-specific part");
+
+  if (grub_file_seek (macho->file, macho->offset32) == (grub_off_t) -1)
+    {
+      grub_error_push ();
+      return grub_error (GRUB_ERR_BAD_OS,
+                        "Invalid offset in program header.");
+    }
+
+  read = grub_file_read (macho->file, dest, 
+                        macho->end32 - macho->offset32);
+  if (read != (grub_ssize_t) (macho->end32 - macho->offset32))
+    {
+      grub_error_push ();
+      return grub_error (GRUB_ERR_BAD_OS, 
+                        "Couldn't read arcitecture-specific part");
+    }
+  return GRUB_ERR_NONE;
+}
+
+/* Calculate the amount of memory spanned by the segments. */
+grub_err_t
+grub_macho32_size (grub_macho_t macho, grub_addr_t *segments_start,
+                  grub_addr_t *segments_end, int flags)
+{
+  int nr_phdrs = 0;
+
+  /* Run through the program headers to calculate the total memory size we
+     should claim.  */
+  auto int NESTED_FUNC_ATTR calcsize (grub_macho_t _macho, 
+                                     struct grub_macho_cmd *phdr, void *_arg);
+  int NESTED_FUNC_ATTR calcsize (grub_macho_t UNUSED _macho, 
+                                struct grub_macho_cmd *hdr0, void UNUSED *_arg)
+    {
+      struct grub_macho_segment32 *hdr = (struct grub_macho_segment32 *) hdr0;
+      if (hdr->cmd != GRUB_MACHO_CMD_SEGMENT32)
+       return 0;
+      if (! hdr->filesize && (flags & GRUB_MACHO_NOBSS))
+       return 0;
+
+      nr_phdrs++;
+      if (hdr->vmaddr < *segments_start)
+       *segments_start = hdr->vmaddr;
+      if (hdr->vmaddr + hdr->vmsize > *segments_end)
+       *segments_end = hdr->vmaddr + hdr->vmsize;
+      return 0;
+    }
+
+  *segments_start = (grub_uint32_t) -1;
+  *segments_end = 0;
+
+  grub_macho32_cmds_iterate (macho, calcsize, 0);
+
+  if (nr_phdrs == 0)
+    return grub_error (GRUB_ERR_BAD_OS, "No program headers present");
+
+  if (*segments_end < *segments_start)
+    /* Very bad addresses.  */
+    return grub_error (GRUB_ERR_BAD_OS, "Bad program header load addresses");
+
+  return GRUB_ERR_NONE;
+}
+
+/* Load every loadable segment into memory specified by `_load_hook'.  */
+grub_err_t
+grub_macho32_load (grub_macho_t macho, char *offset, int flags)
+{
+  grub_err_t err = 0;
+  auto int NESTED_FUNC_ATTR do_load(grub_macho_t _macho, 
+                              struct grub_macho_cmd *hdr0, 
+                              void UNUSED *_arg);
+  int NESTED_FUNC_ATTR do_load(grub_macho_t _macho, 
+                              struct grub_macho_cmd *hdr0, 
+                              void UNUSED *_arg)
+  {
+    struct grub_macho_segment32 *hdr = (struct grub_macho_segment32 *) hdr0;
+
+    if (hdr->cmd != GRUB_MACHO_CMD_SEGMENT32)
+      return 0;
+    
+    if (! hdr->filesize && (flags & GRUB_MACHO_NOBSS))
+      return 0;
+    if (! hdr->vmsize)
+      return 0;
+    
+    if (grub_file_seek (_macho->file, hdr->fileoff 
+                       + _macho->offset32) == (grub_off_t) -1)
+      {
+       grub_error_push ();
+       grub_error (GRUB_ERR_BAD_OS,
+                   "Invalid offset in program header.");
+       return 1;
+      }
+    
+    if (hdr->filesize)
+      {
+       grub_ssize_t read;
+       read = grub_file_read (_macho->file, offset + hdr->vmaddr, 
+                                  min (hdr->filesize, hdr->vmsize));
+       if (read != (grub_ssize_t) min (hdr->filesize, hdr->vmsize))
+         {
+           /* XXX How can we free memory from `load_hook'? */
+           grub_error_push ();
+           err=grub_error (GRUB_ERR_BAD_OS,
+                           "Couldn't read segment from file: "
+                           "wanted 0x%lx bytes; read 0x%lx bytes.",
+                           hdr->filesize, read);
+           return 1;
+         }
+      }
+    
+    if (hdr->filesize < hdr->vmsize)
+      grub_memset (offset + hdr->vmaddr + hdr->filesize,
+                  0, hdr->vmsize - hdr->filesize);
+    return 0;
+  }
+
+  grub_macho32_cmds_iterate (macho, do_load, 0);
+
+  return err;
+}
+
+grub_uint32_t
+grub_macho32_get_entry_point (grub_macho_t macho)
+{
+  grub_uint32_t entry_point = 0;
+  auto int NESTED_FUNC_ATTR hook(grub_macho_t _macho, 
+                              struct grub_macho_cmd *hdr, 
+                              void UNUSED *_arg);
+  int NESTED_FUNC_ATTR hook(grub_macho_t UNUSED _macho, 
+                              struct grub_macho_cmd *hdr, 
+                              void UNUSED *_arg)
+  {
+    if (hdr->cmd == GRUB_MACHO_CMD_THREAD)
+      entry_point = ((struct grub_macho_thread32 *) hdr)->entry_point;
+    return 0;
+  }
+  grub_macho32_cmds_iterate (macho, hook, 0);
+  return entry_point;
+}
+
+
+grub_err_t
+grub_macho_close (grub_macho_t macho)
+{
+  grub_file_t file = macho->file;
+
+  grub_free (macho->cmds32);
+  grub_free (macho->cmds64);
+
+  grub_free (macho);
+
+  if (file)
+    grub_file_close (file);
+
+  return grub_errno;
+}
+
+grub_macho_t
+grub_macho_file (grub_file_t file)
+{
+  grub_macho_t macho;
+  union grub_macho_filestart filestart;
+
+  macho = grub_malloc (sizeof (*macho));
+  if (! macho)
+    return 0;
+
+  macho->file = file;
+  macho->offset32 = -1;
+  macho->offset64 = -1;
+  macho->end32 = -1;
+  macho->end64 = -1;
+  macho->cmds32 = 0;
+  macho->cmds64 = 0;
+
+  if (grub_file_seek (macho->file, 0) == (grub_off_t) -1)
+    goto fail;
+
+  if (grub_file_read (macho->file, (char *) &filestart, sizeof (filestart))
+      != sizeof (filestart))
+    {
+      grub_error_push ();
+      grub_error (GRUB_ERR_READ_ERROR, "Cannot read Mach-O header.");
+      goto fail;
+    }
+
+  /* Is it a fat file? */
+  if (filestart.fat.magic == grub_be_to_cpu32 (GRUB_MACHO_FAT_MAGIC))
+    {
+      struct grub_macho_fat_arch *archs;
+      int i, narchs;
+
+      /* Load architecture description. */
+      narchs = grub_be_to_cpu32 (filestart.fat.nfat_arch);
+      if (grub_file_seek (macho->file, sizeof (struct grub_macho_fat_header)) 
+         == (grub_off_t) -1)
+       goto fail;
+      archs = grub_malloc (sizeof (struct grub_macho_fat_arch) * narchs);
+      if (!archs)
+       goto fail;
+      if (grub_file_read (macho->file, (char *) archs, 
+                         sizeof (struct grub_macho_fat_arch) * narchs)
+         != (grub_ssize_t)sizeof(struct grub_macho_fat_arch) * narchs)
+       {
+         grub_free (archs);
+         grub_error_push ();
+         grub_error (GRUB_ERR_READ_ERROR, "Cannot read Mach-O header.");
+         goto fail;
+       }
+
+      for (i = 0; i < narchs; i++)
+       {
+         if (GRUB_MACHO_CPUTYPE_IS_HOST32 
+             (grub_be_to_cpu32 (archs[i].cputype)))
+           {
+             macho->offset32 = grub_be_to_cpu32 (archs[i].offset);
+             macho->end32 = grub_be_to_cpu32 (archs[i].offset) 
+               + grub_be_to_cpu32 (archs[i].size);
+           }
+         if (GRUB_MACHO_CPUTYPE_IS_HOST64
+             (grub_be_to_cpu32 (archs[i].cputype)))
+           {
+             macho->offset64 = grub_be_to_cpu32 (archs[i].offset);
+             macho->end64 = grub_be_to_cpu32 (archs[i].offset) 
+               + grub_be_to_cpu32 (archs[i].size);
+           }
+       }
+      grub_free (archs);
+    }
+
+  /* Is it a thin 32-bit file? */
+  if (filestart.thin32.magic == GRUB_MACHO_MAGIC32)
+    {
+      macho->offset32 = 0;
+      macho->end32 = grub_file_size (file);
+    }
+
+  /* Is it a thin 64-bit file? */
+  if (filestart.thin64.magic == GRUB_MACHO_MAGIC64)
+    {
+      macho->offset64 = 0;
+      macho->end64 = grub_file_size (file);
+    }
+
+  grub_macho_parse32 (macho);
+  /* FIXME: implement 64-bit.*/
+  /*  grub_macho_parse64 (macho); */
+  
+  return macho;
+
+fail:
+  grub_macho_close (macho);
+  return 0;
+}
+
+grub_macho_t
+grub_macho_open (const char *name)
+{
+  grub_file_t file;
+  grub_macho_t macho;
+
+  file = grub_gzfile_open (name, 1);
+  if (! file)
+    return 0;
+
+  macho = grub_macho_file (file);
+  if (! macho)
+    grub_file_close (file);
+
+  return macho;
+}
diff --git a/loader/xnu.c b/loader/xnu.c
new file mode 100644
index 0000000..6a4e0f5
--- /dev/null
+++ b/loader/xnu.c
@@ -0,0 +1,1376 @@
+/*
+ *  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/file.h>
+#include <grub/xnu.h>
+#include <grub/cpu/xnu.h>
+#include <grub/mm.h>
+#include <grub/dl.h>
+#include <grub/loader.h>
+#include <grub/cpu/loader.h>
+#include <grub/machoload.h>
+#include <grub/macho.h>
+#include <grub/cpu/macho.h>
+#include <grub/gzio.h>
+#include <grub/command.h>
+#include <grub/misc.h>
+
+struct grub_xnu_devtree_key *grub_xnu_devtree_root = 0;
+static int driverspackagenum = 0;
+static int driversnum = 0;
+char *grub_xnu_heap_start = 0;
+grub_addr_t grub_xnu_heap_will_be_at = 0;
+grub_size_t grub_xnu_heap_size = 0;
+
+/* Allocate heap by 32MB-blocks. */
+#define GRUB_XNU_HEAP_ALLOC_BLOCK 0x2000000
+
+static grub_err_t
+grub_xnu_register_memory (char *prefix, int *suffix, 
+                         void *addr, grub_size_t size);
+void *
+grub_xnu_heap_malloc (int size)
+{
+  void *val;
+
+#if 0
+  /* This way booting is faster but less reliable. 
+     Once we have advanced mm second way will be as fast as this one. */
+  val = grub_xnu_heap_start = (char *) 0x100000;
+#else
+  int oldblknum, newblknum;
+
+  /* The page after the heap is used for stack. Ensure it's usable. */
+  if (grub_xnu_heap_size)
+    oldblknum = (grub_xnu_heap_size + GRUB_XNU_PAGESIZE 
+                + GRUB_XNU_HEAP_ALLOC_BLOCK - 1) / GRUB_XNU_HEAP_ALLOC_BLOCK;
+  else
+    oldblknum = 0;
+  newblknum = (grub_xnu_heap_size + size + GRUB_XNU_PAGESIZE 
+              + GRUB_XNU_HEAP_ALLOC_BLOCK - 1) / GRUB_XNU_HEAP_ALLOC_BLOCK;
+  if (oldblknum != newblknum)
+    /* FIXME: instruct realloc to allocate at 1MB if possible once 
+       advanced mm is ready. */
+    val = grub_realloc (grub_xnu_heap_start, 
+                       newblknum * GRUB_XNU_HEAP_ALLOC_BLOCK);
+  else
+    val = grub_xnu_heap_start;
+  if (! val)
+    {
+      grub_error (GRUB_ERR_OUT_OF_MEMORY, 
+                 "not enough space on xnu memory heap");
+      return 0;
+    }
+  grub_xnu_heap_start = val;
+#endif
+
+  val = (char *) grub_xnu_heap_start + grub_xnu_heap_size;
+  grub_xnu_heap_size += size;
+  grub_dprintf ("xnu", "val=%p\n", val);
+  return (char *) val;
+}
+
+/* Make sure next block of the heap will be aligned. 
+   Please notice: aligned are pointers AFTER relocation 
+   and not the current ones. */
+grub_err_t
+grub_xnu_align_heap (int align)
+{
+  int align_overhead = align - grub_xnu_heap_size % align;
+  if (align_overhead == align)
+    return GRUB_ERR_NONE;
+  if (! grub_xnu_heap_malloc (align_overhead))
+    return grub_errno;
+  return GRUB_ERR_NONE;
+}
+
+/* Free subtree pointed by CUR. */
+void
+grub_xnu_free_devtree (struct grub_xnu_devtree_key *cur)
+{
+  struct grub_xnu_devtree_key *d;
+  while (cur)
+    {
+      grub_free (cur->name);
+      if (cur->datasize == -1)
+       grub_xnu_free_devtree (cur->first_child);
+      else if (cur->data)
+       grub_free (cur->data);
+      d = cur->next;
+      grub_free (cur);      
+      cur = d;
+    }  
+}
+
+/* Compute the size of device tree in xnu format. */
+static grub_size_t
+grub_xnu_writetree_get_size (struct grub_xnu_devtree_key *start, char *name)
+{
+  grub_size_t ret;
+  struct grub_xnu_devtree_key *cur;
+  
+  /* Key header. */
+  ret = 2 * sizeof (grub_uint32_t);
+
+  /* "name" value. */
+  ret += 32 + sizeof (grub_uint32_t)
+    + grub_strlen (name) + 4 
+    - (grub_strlen (name) % 4);
+
+  for (cur = start; cur; cur = cur->next)
+    if (cur->datasize != -1)
+      {
+       int align_overhead;
+       
+       align_overhead = 4 - (cur->datasize % 4);
+       if (align_overhead == 4)
+         align_overhead = 0;
+       ret += 32 + sizeof (grub_uint32_t) + cur->datasize + align_overhead;
+      }
+    else
+      ret += grub_xnu_writetree_get_size (cur->first_child, cur->name);
+  return ret;
+}
+
+/* Write devtree in XNU format at curptr assuming the head is named NAME.*/
+static void *
+grub_xnu_writetree_toheap_real (void *curptr, 
+                               struct grub_xnu_devtree_key *start, char *name)
+{
+  struct grub_xnu_devtree_key *cur;
+  int nkeys = 0, nvals = 0;
+  for (cur = start; cur; cur = cur->next)
+    {
+      if (cur->datasize == -1)
+       nkeys++;
+      else
+       nvals++;
+    }
+  /* For the name. */
+  nvals++; 
+  
+  *((grub_uint32_t *) curptr) = nvals;
+  curptr = ((grub_uint32_t *) curptr) + 1;
+  *((grub_uint32_t *) curptr) = nkeys;
+  curptr = ((grub_uint32_t *) curptr) + 1;
+
+  /* First comes "name" value. */
+  grub_memset (curptr, 0, 32);
+  grub_memcpy (curptr, "name", 4);
+  curptr = ((grub_uint8_t *) curptr) + 32;
+  *((grub_uint32_t *)curptr) = grub_strlen (name) + 1;
+  curptr = ((grub_uint32_t *) curptr) + 1;
+  grub_memcpy (curptr, name, grub_strlen (name));
+  curptr = ((grub_uint8_t *) curptr) + grub_strlen (name);
+  grub_memset (curptr, 0, 4 - (grub_strlen (name) % 4));
+  curptr = ((grub_uint8_t *) curptr) + (4 - (grub_strlen (name) % 4));
+
+  /* Then the other values. */
+  for (cur = start; cur; cur = cur->next)
+    if (cur->datasize != -1)
+      {
+       int align_overhead;
+       
+       align_overhead = 4 - (cur->datasize % 4);
+       if (align_overhead == 4)
+         align_overhead = 0;
+       grub_memset (curptr, 0, 32);
+       grub_strncpy (curptr, cur->name, 31);
+       curptr = ((grub_uint8_t *) curptr) + 32;
+       *((grub_uint32_t *) curptr) = cur->datasize;
+       curptr = ((grub_uint32_t *) curptr) + 1;
+       grub_memcpy (curptr, cur->data, cur->datasize);
+       curptr = ((grub_uint8_t *) curptr) + cur->datasize;
+       grub_memset (curptr, 0, align_overhead);
+       curptr = ((grub_uint8_t *) curptr) + align_overhead;
+      }
+  
+  /* And then the keys. Recursively use this function. */
+  for (cur = start; cur; cur = cur->next)
+    if (cur->datasize == -1)
+      if (!(curptr = grub_xnu_writetree_toheap_real (curptr,
+                                                    cur->first_child, 
+                                                    cur->name)))
+       return 0;
+  return curptr;
+}
+
+grub_err_t
+grub_xnu_writetree_toheap (void **start, grub_size_t *size)
+{
+  struct grub_xnu_devtree_key *chosen;
+  struct grub_xnu_devtree_key *memorymap;
+  struct grub_xnu_devtree_key *driverkey, *cur;
+  struct grub_xnu_extdesc *extdesc;
+  grub_err_t err;
+  
+  err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE);
+  if (err)
+    return err;
+  
+  /* Device tree itself is in the memory map of device tree. */
+
+  /* Create a dummy value in memory-map. */
+  chosen = grub_xnu_create_key (&grub_xnu_devtree_root, "chosen");
+  if (! chosen)
+    return grub_errno;
+  memorymap = grub_xnu_create_key (&(chosen->first_child), "memory-map");
+  if (! memorymap)
+    return grub_errno;
+  driverkey = (struct grub_xnu_devtree_key *) grub_malloc (sizeof 
(*driverkey));
+  if (! driverkey)
+    return grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't write device tree");
+  driverkey->name = grub_strdup ("DeviceTree");
+  if (! driverkey->name)
+    return grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't write device tree");
+  driverkey->datasize = sizeof (*extdesc);
+  driverkey->next = memorymap->first_child;
+  memorymap->first_child = driverkey;
+  driverkey->data = extdesc 
+    = (struct grub_xnu_extdesc *) grub_malloc (sizeof (*extdesc));
+  if (! driverkey->data)
+    return grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't write device tree");
+
+  /* Allocate the space based on the size with dummy value. */
+  *size = grub_xnu_writetree_get_size (grub_xnu_devtree_root, "/");
+  *start = grub_xnu_heap_malloc (*size);
+
+  /* Put real data in the dummy. */
+  extdesc->addr = PTR_TO_UINT32 (*start);
+  extdesc->size = (grub_uint32_t) *size;
+
+  /* Relocate memory map. */
+  grub_xnu_register_memory ("DeviceTree", 0, *start, *size);
+  for (cur = memorymap->first_child; cur; cur = cur->next)
+    {
+      void *payload;
+      extdesc = cur->data;
+      if (cur->datasize != sizeof (*extdesc))
+       continue;
+      /* RAMDisk is announced by its relocated and not original address. */
+      if (grub_strcmp (cur->name, "RAMDisk") == 0)
+       {
+         extdesc->addr = extdesc->addr + grub_xnu_heap_will_be_at;
+         continue;
+       }
+      payload = extdesc->addr + grub_xnu_heap_start;
+      extdesc->addr = PTR_TO_UINT32(payload);
+
+      /* If it's an extension also relocate the header. */
+      if (grub_memcmp (cur->name, "Driver-", sizeof ("Driver-") - 1) == 0)
+       {
+         struct grub_xnu_extheader *exthead 
+           = (struct grub_xnu_extheader *) payload;
+         if (exthead->infoplistaddr)
+           exthead->infoplistaddr = PTR_TO_UINT32 (exthead->infoplistaddr 
+                                                   + grub_xnu_heap_start);
+         if (exthead->binaryaddr)
+           exthead->binaryaddr = PTR_TO_UINT32 (exthead->binaryaddr 
+                                                + grub_xnu_heap_start);
+       }
+    }
+
+  /* Write the tree to heap. */
+  grub_xnu_writetree_toheap_real (*start, grub_xnu_devtree_root, "/");
+  return GRUB_ERR_NONE;
+}
+
+/* Find a key or value in parent key. */
+struct grub_xnu_devtree_key *
+grub_xnu_find_key (struct grub_xnu_devtree_key *parent, char *name)
+{
+  struct grub_xnu_devtree_key *cur;
+  for (cur = parent; cur; cur = cur->next)
+    if (grub_strcmp (cur->name, name) == 0)
+      return cur;
+  return 0;
+}
+
+struct grub_xnu_devtree_key *
+grub_xnu_create_key (struct grub_xnu_devtree_key **parent, char *name)
+{
+  struct grub_xnu_devtree_key *ret;
+  ret = grub_xnu_find_key (*parent, name);
+  if (ret)
+    return ret;
+  ret = (struct grub_xnu_devtree_key *) grub_malloc (sizeof (*ret));
+  if (! ret)
+    {
+      grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't create key %s", name);
+      return 0;
+    }
+  ret->name = grub_strdup (name);
+  if (! ret->name)
+    {
+      grub_free (ret);
+      grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't create key %s", name);
+      return 0;
+    }
+  ret->datasize = -1;
+  ret->first_child = 0;
+  ret->next = *parent;
+  *parent = ret;
+  return ret;
+}
+
+struct grub_xnu_devtree_key *
+grub_xnu_create_value (struct grub_xnu_devtree_key **parent, char *name)
+{
+  struct grub_xnu_devtree_key *ret;
+  ret = grub_xnu_find_key (*parent, name);
+  if (ret)
+    {
+      if (ret->datasize == -1)
+       grub_xnu_free_devtree (ret->first_child);
+      else if (ret->datasize)
+       grub_free (ret->data);
+      ret->datasize = 0;
+      ret->data = 0;
+      return ret;
+    }
+  ret = (struct grub_xnu_devtree_key *) grub_malloc (sizeof (*ret));
+  if (! ret)
+    {
+      grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't create value %s", name);
+      return 0;
+    }
+  ret->name = grub_strdup (name);
+  if (! ret->name)
+    {
+      grub_free (ret);
+      grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't create value %s", name);
+      return 0;
+    }
+  ret->datasize = 0;
+  ret->data = 0;
+  ret->next = *parent;
+  *parent = ret;
+  return ret;
+}
+
+static grub_err_t
+grub_xnu_unload (void)
+{
+  grub_xnu_free_devtree (grub_xnu_devtree_root);
+  grub_xnu_devtree_root = 0;
+
+  /* Free loaded image. */
+  driversnum = 0;
+  driverspackagenum = 0;
+  grub_free (grub_xnu_heap_start);
+  grub_xnu_heap_start = 0;
+  grub_xnu_heap_size = 0;
+  grub_xnu_unlock ();
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_cmd_xnu_kernel (grub_command_t cmd __attribute__ ((unused)),
+                    int argc, char *args[])
+{
+  grub_err_t err;
+  grub_macho_t macho;
+  grub_addr_t startcode, endcode;
+  int i;
+  char *ptr, *loadaddr;
+
+  if (argc < 1)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required");
+
+  grub_xnu_unload ();
+
+  macho = grub_macho_open (args[0]);
+  if (! macho)
+    return grub_errno;
+  if (! grub_macho_contains_macho32 (macho))
+    {
+      grub_macho_close (macho);
+      return grub_error (GRUB_ERR_BAD_OS, 
+                        "Kernel doesn't contain suitable architecture");
+    }
+
+  err = grub_macho32_size (macho, &startcode, &endcode, GRUB_MACHO_NOBSS);
+  if (err)
+    {
+      grub_macho_close (macho);
+      grub_xnu_unload ();
+      return err;
+    }
+
+  grub_dprintf ("xnu", "endcode = %lx, startcode = %lx\n", 
+               (unsigned long) endcode, (unsigned long) startcode);
+
+  loadaddr = grub_xnu_heap_malloc (endcode - startcode);
+  grub_xnu_heap_will_be_at = startcode;
+
+  if (! loadaddr)
+    {
+      grub_macho_close (macho);
+      grub_xnu_unload ();
+      return grub_error (GRUB_ERR_OUT_OF_MEMORY, 
+                        "not enough memory to load kernel");
+    }
+
+  /* Load kernel. */
+  err = grub_macho32_load (macho, loadaddr - startcode, GRUB_MACHO_NOBSS);
+  if (err)
+    {
+      grub_macho_close (macho);
+      grub_xnu_unload ();
+      return err;
+    }
+
+  grub_xnu_entry_point = grub_macho32_get_entry_point (macho);
+  if (! grub_xnu_entry_point)
+    {
+      grub_macho_close (macho);
+      grub_xnu_unload ();
+      return grub_error (GRUB_ERR_BAD_OS, "couldn't find entry point");
+    }
+  grub_xnu_entry_point -= startcode;
+
+  grub_macho_close (macho);
+
+  err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE);
+  if (err)
+    {
+      grub_xnu_unload ();
+      return err;
+    }
+
+  /* Copy parameters to kernel command line. */
+  ptr = grub_xnu_cmdline;
+  for (i = 1; i < argc; i++)
+    {
+      if (ptr + grub_strlen (args[i]) + 1 
+         >= grub_xnu_cmdline + sizeof (grub_xnu_cmdline))
+       break;
+      grub_memcpy (ptr, args[i], grub_strlen (args[i]));
+      ptr += grub_strlen (args[i]);
+      *ptr = ' ';
+      ptr++;
+    }
+
+  /* Replace last space by '\0'. */
+  if (ptr != grub_xnu_cmdline)
+    *(ptr - 1) = 0;  
+
+  err = grub_cpu_xnu_fill_devicetree ();
+  if (err)
+    return err;
+
+  grub_loader_set (grub_xnu_boot, grub_xnu_unload, 0);    
+
+  grub_xnu_lock ();
+  return 0;
+}
+
+/* Register a memory in a memory map under name PREFIXSUFFIX 
+   and increment SUFFIX. */
+static grub_err_t
+grub_xnu_register_memory (char *prefix, int *suffix, 
+                         void *addr, grub_size_t size)
+{
+  struct grub_xnu_devtree_key *chosen;
+  struct grub_xnu_devtree_key *memorymap;
+  struct grub_xnu_devtree_key *driverkey;
+  struct grub_xnu_extdesc *extdesc;
+
+  if (! grub_xnu_heap_size)
+    return grub_error (GRUB_ERR_BAD_OS, "no xnu kernel loaded");
+
+  chosen = grub_xnu_create_key (&grub_xnu_devtree_root, "chosen");
+  if (! chosen)
+    return grub_errno;
+  memorymap = grub_xnu_create_key (&(chosen->first_child), "memory-map");
+  if (! memorymap)
+    return grub_errno;
+
+  driverkey = (struct grub_xnu_devtree_key *) grub_malloc (sizeof 
(*driverkey));
+  if (! driverkey)
+    return grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't register memory");
+  if (suffix)
+    {
+      driverkey->name = grub_malloc (grub_strlen (prefix) + 10);
+      if (!driverkey->name)
+       return grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't register memory");
+      grub_sprintf (driverkey->name, "%s%d", prefix, (*suffix)++);
+    }
+  else
+    driverkey->name = grub_strdup (prefix);
+  if (! driverkey->name)
+    return grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't register extension");
+  driverkey->datasize = sizeof (*extdesc);
+  driverkey->next = memorymap->first_child;
+  memorymap->first_child = driverkey;
+  driverkey->data = extdesc 
+    = (struct grub_xnu_extdesc *) grub_malloc (sizeof (*extdesc));
+  if (! driverkey->data)
+    return grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't register extension");
+  extdesc->addr = ((grub_uint8_t *) addr 
+                  - (grub_uint8_t *) grub_xnu_heap_start);
+  extdesc->size = (grub_uint32_t) size;
+  return GRUB_ERR_NONE;
+}
+
+/* Load .kext. */
+static grub_err_t
+grub_xnu_load_driver (char *infoplistname, grub_file_t binaryfile)
+{
+  grub_macho_t macho;
+  grub_err_t err;
+  grub_file_t infoplist;
+  struct grub_xnu_extheader *exthead;
+  int neededspace = sizeof (*exthead);
+  char *buf;
+  grub_size_t infoplistsize = 0, machosize = 0;
+
+  if (! grub_xnu_heap_size)
+    return grub_error (GRUB_ERR_BAD_OS, "no xnu kernel loaded");
+  
+  /* Compute the needed space. */
+  if (binaryfile)
+    {
+      macho = grub_macho_file (binaryfile);
+      if (! macho || ! grub_macho_contains_macho32 (macho))
+       {
+         if (macho)
+           grub_macho_close (macho);
+         return grub_error (GRUB_ERR_BAD_OS, 
+                            "Extension doesn't contain suitable architecture");
+       }
+      machosize = grub_macho32_filesize (macho);
+      neededspace += machosize;
+    }
+  else
+    macho = 0;
+
+  if (infoplistname)
+    infoplist = grub_gzfile_open (infoplistname, 1);
+  else
+    infoplist = 0;
+  grub_errno = GRUB_ERR_NONE;
+  if (infoplist)
+    {
+      infoplistsize = grub_file_size (infoplist);
+      neededspace += infoplistsize + 1;
+    }
+  else
+    infoplistsize = 0;
+
+  /* Allocate the space. */
+  err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE);
+  if (err)
+    return err;
+  buf = grub_xnu_heap_malloc (neededspace);
+
+  exthead = (struct grub_xnu_extheader *) buf;
+  grub_memset (exthead, 0, sizeof (*exthead));
+  buf += sizeof (*exthead);
+
+  /* Load the binary. */
+  if (macho)
+    {
+      exthead->binaryaddr = (buf - grub_xnu_heap_start);
+      exthead->binarysize = machosize;
+      if ((err = grub_macho32_readfile (macho, buf)))
+       {
+         grub_macho_close (macho);
+         return err;
+       }
+      grub_macho_close (macho);
+      buf += machosize;
+    }
+  grub_errno = GRUB_ERR_NONE;
+
+  /* Load the plist. */
+  if (infoplist)
+    {
+      exthead->infoplistaddr = (buf - grub_xnu_heap_start);
+      exthead->infoplistsize = infoplistsize + 1;
+      if (grub_file_read (infoplist, buf, infoplistsize)
+         != (grub_ssize_t) (infoplistsize))
+       {
+         grub_file_close (infoplist);
+         grub_error_push ();
+         return grub_error (GRUB_ERR_BAD_OS, "Couldn't read file %s: ",
+                            infoplistname);
+       }
+      grub_file_close (infoplist);
+      buf[infoplistsize] = 0;
+    }
+  grub_errno = GRUB_ERR_NONE;
+
+  /* Announce to kernel */
+  return grub_xnu_register_memory ("Driver-", &driversnum, exthead,
+                                  neededspace);  
+}
+
+/* Load mkext. */
+static grub_err_t
+grub_cmd_xnu_mkext (grub_command_t cmd __attribute__ ((unused)),
+                   int argc, char *args[])
+{
+  grub_file_t file;
+  void *loadto;
+  grub_err_t err;
+  grub_off_t readoff = 0;
+  grub_ssize_t readlen = -1;
+  struct grub_macho_fat_header head;
+  struct grub_macho_fat_arch *archs;
+  int narchs, i;
+
+  if (argc != 1)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required");
+
+  if (! grub_xnu_heap_size)
+    return grub_error (GRUB_ERR_BAD_OS, "no xnu kernel loaded");
+
+  file = grub_gzfile_open (args[0], 1);
+  if (! file)
+    return grub_error (GRUB_ERR_FILE_NOT_FOUND, 
+                      "Couldn't load driver package");
+
+  /* Sometimes caches are fat binary. Errgh. */
+  if (grub_file_read (file, (char *) &head, sizeof (head))
+      != (grub_ssize_t) (sizeof (head)))
+    {
+      /* I don't know the internal structure of package but 
+        can hardly imagine a valid package shorter than 20 bytes. */
+      grub_file_close (file);
+      grub_error_push ();
+      return grub_error (GRUB_ERR_BAD_OS, "Couldn't read file %s", args[0]);
+    }
+
+  /* Find the corresponding architecture. */
+  if (grub_be_to_cpu32 (head.magic) == GRUB_MACHO_FAT_MAGIC)
+    {
+      narchs = grub_be_to_cpu32 (head.nfat_arch);
+      archs = grub_malloc (sizeof (struct grub_macho_fat_arch) * narchs);
+      if (! archs)
+       {
+         grub_file_close (file);
+         grub_error_push ();
+         return grub_error (GRUB_ERR_OUT_OF_MEMORY, 
+                            "Couldn't read file %s", args[0]);
+      
+       }
+      if (grub_file_read (file, (char *) archs, 
+                         sizeof (struct grub_macho_fat_arch) * narchs)
+         != (grub_ssize_t) sizeof(struct grub_macho_fat_arch) * narchs)
+       {
+         grub_free (archs);
+         grub_error_push ();
+         return grub_error (GRUB_ERR_READ_ERROR, "Cannot read fat header.");
+       }
+      for (i = 0; i < narchs; i++)
+       {
+         if (GRUB_MACHO_CPUTYPE_IS_HOST32 
+             (grub_be_to_cpu32 (archs[i].cputype)))
+           {
+             readoff = grub_be_to_cpu32 (archs[i].offset);
+             readlen = grub_be_to_cpu32 (archs[i].size);
+           }
+       }
+      grub_free (archs);
+    }
+  else
+    {
+      /* It's a flat file. Some sane people still exist. */
+      readoff = 0;
+      readlen = grub_file_size (file);
+    }
+
+  if (readlen == -1)
+    {
+      grub_file_close (file);
+      return grub_error (GRUB_ERR_BAD_OS, "no suitable architecture is found");
+    }
+
+  /* Allocate space. */
+  err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE);
+  if (err)
+    {
+      grub_file_close (file);
+      return err;
+    }
+
+  loadto = grub_xnu_heap_malloc (readlen);
+  if (! loadto)
+    {
+      grub_file_close (file);
+      return grub_errno;
+    }
+
+  /* Read the file. */
+  grub_file_seek (file, readoff);
+  if (grub_file_read (file, loadto, readlen) != (grub_ssize_t) (readlen))
+    {
+      grub_file_close (file);
+      grub_error_push ();
+      return grub_error (GRUB_ERR_BAD_OS, "Couldn't read file %s", args[0]);
+    }
+  grub_file_close (file);
+
+  /* Pass it to kernel. */
+  return grub_xnu_register_memory ("DriversPackage-", &driverspackagenum,
+                                  loadto, readlen);  
+}
+
+static grub_err_t
+grub_cmd_xnu_ramdisk (grub_command_t cmd __attribute__ ((unused)),
+                     int argc, char *args[])
+{
+  grub_file_t file;
+  void *loadto;
+  grub_err_t err;
+  grub_size_t size;
+
+  if (argc != 1)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required");
+
+  if (! grub_xnu_heap_size)
+    return grub_error (GRUB_ERR_BAD_OS, "no xnu kernel loaded");
+
+  file = grub_gzfile_open (args[0], 1);
+  if (! file)
+    return grub_error (GRUB_ERR_FILE_NOT_FOUND, 
+                      "Couldn't load ramdisk");
+
+  err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE);
+  if (err)
+    return err;
+
+  size = grub_file_size (file);
+  
+  loadto = grub_xnu_heap_malloc (size);
+  if (! loadto)
+    return grub_errno;
+  if (grub_file_read (file, loadto, size)
+      != (grub_ssize_t) (size))
+    {
+      grub_file_close (file);
+      grub_error_push ();
+      return grub_error (GRUB_ERR_BAD_OS, "Couldn't read file %s", args[0]);
+    }
+  return grub_xnu_register_memory ("RAMDisk", 0, loadto, size);  
+}
+
+/* Parse a devtree file. It uses the following format: 
+   valuename:valuedata;
+   keyname{
+     contents
+   }
+   keyname, valuename and valuedata are in hex.
+ */
+static char *
+grub_xnu_parse_devtree (struct grub_xnu_devtree_key **parent,
+                       char *start, char *end)
+{
+  char *ptr, *ptr2;
+  char *name, *data;
+  int namelen, datalen, i;
+  for (ptr = start; ptr && ptr < end; )
+    {
+      if (grub_isspace (*ptr))
+       {
+         ptr++;
+         continue;
+       }
+      if (*ptr == '}')
+       return ptr + 1;
+      namelen = 0;
+      
+      /* Parse the name. */
+      for (ptr2 = ptr; ptr2 < end && (grub_isspace (*ptr2) 
+                                     || (*ptr2 >= '0' && *ptr2 <= '9')
+                                     || (*ptr2 >= 'a' && *ptr2 <= 'f')
+                                     || (*ptr2 >= 'A' && *ptr2 <= 'F')); 
+          ptr2++)
+       if (! grub_isspace (*ptr2))
+         namelen++;
+      if (ptr2 == end)
+       return 0;
+      namelen /= 2;
+      name = grub_malloc (namelen + 1);
+      if (!name)
+       return 0;
+      for (i = 0; i < 2 * namelen; i++)
+       {
+         int hex = 0;
+         while (grub_isspace (*ptr))
+           ptr++;
+         if (*ptr >= '0' && *ptr <= '9')
+           hex = *ptr - '0';
+         if (*ptr >= 'a' && *ptr <= 'f')
+           hex = *ptr - 'a' + 10;
+         if (*ptr >= 'A' && *ptr <= 'F')
+           hex = *ptr - 'A' + 10;
+         
+         if (i % 2 == 0)
+           name[i / 2] = hex << 4;
+         else
+           name[i / 2] |= hex;
+         ptr++;
+       }
+      name [namelen] = 0;
+      while (grub_isspace (*ptr))
+       ptr++;
+
+      /* If it describes a key recursively invoke the function. */
+      if (*ptr == '{')
+       {
+         struct grub_xnu_devtree_key *newkey 
+           = grub_xnu_create_key (parent, name);
+         grub_free (name);
+         if (! newkey)
+           return 0;
+         ptr = grub_xnu_parse_devtree (&(newkey->first_child), ptr + 1, end);
+         continue;
+       }
+
+      /* Parse the data. */
+      if (*ptr != ':')
+       return 0;
+      ptr++;
+      datalen = 0;
+      for (ptr2 = ptr; ptr2 < end && (grub_isspace (*ptr2) 
+                                     || (*ptr2 >= '0' && *ptr2 <= '9')
+                                     || (*ptr2 >= 'a' && *ptr2 <= 'f')
+                                     || (*ptr2 >= 'A' && *ptr2 <= 'F')); 
+          ptr2++)
+       if (! grub_isspace (*ptr2))
+         datalen++;
+      if (ptr2 == end)
+       return 0;
+      datalen /= 2;
+      data = grub_malloc (datalen);
+      if (! data)
+       return 0;
+      for (i = 0; i < 2 * datalen; i++)
+       {
+         int hex = 0;
+         while (grub_isspace (*ptr))
+           ptr++;
+         if (*ptr >= '0' && *ptr <= '9')
+           hex = *ptr - '0';
+         if (*ptr >= 'a' && *ptr <= 'f')
+           hex = *ptr - 'a' + 10;
+         if (*ptr >= 'A' && *ptr <= 'F')
+           hex = *ptr - 'A' + 10;
+         
+         if (i % 2 == 0)
+           data[i / 2] = hex << 4;
+         else
+           data[i / 2] |= hex;
+         ptr++;
+       }
+      while (ptr < end && grub_isspace (*ptr))
+       ptr++;
+      {
+       struct grub_xnu_devtree_key *newkey 
+         = grub_xnu_create_value (parent, name);
+       grub_free (name);
+       if (! newkey)
+         return 0;
+       newkey->datasize = datalen;
+       newkey->data = data;
+      }
+      if (*ptr != ';')
+       return 0;
+      ptr++;
+    }
+  if (ptr >= end && *parent != grub_xnu_devtree_root)
+    return 0;
+  return ptr;
+}
+
+/* Returns true if the kext should be loaded according to plist 
+   and osbundlereq. Also fill BINNAME. */
+static int
+grub_xnu_check_os_bundle_required (char *plistname, char *osbundlereq,
+                                  char **binname)
+{
+  grub_file_t file;
+  char *buf = 0, *tagstart = 0, *ptr1 = 0, *keyptr = 0;
+  char *stringptr = 0, *ptr2 = 0;
+  grub_size_t size;
+  int depth = 0;
+  int ret;
+  int osbundlekeyfound = 0, binnamekeyfound = 0;
+  if (binname)
+    *binname = 0;
+
+  file = grub_gzfile_open (plistname, 1);
+  if (! file)
+    {
+      grub_file_close (file); 
+      grub_error_push ();
+      grub_error (GRUB_ERR_BAD_OS, "Couldn't read file %s", plistname);
+      return 0;
+    }
+
+  size = grub_file_size (file);
+  buf = grub_malloc (size);
+  if (! buf)
+    {
+      grub_file_close (file); 
+      grub_error_push ();
+      grub_error (GRUB_ERR_OUT_OF_MEMORY, "Couldn't read file %s", plistname);
+      return 0;
+    }
+  if (grub_file_read (file, buf, size) != (grub_ssize_t) (size))
+    {
+      grub_file_close (file); 
+      grub_error_push ();
+      grub_error (GRUB_ERR_BAD_OS, "Couldn't read file %s", plistname);
+      return 0;
+    }
+  grub_file_close (file); 
+
+  /* Set the return value for the case when no OSBundleRequired tag is found. 
*/
+  if (osbundlereq)
+    ret = grub_strword (osbundlereq, "all") || grub_strword (osbundlereq, "-");
+  else
+    ret = 1;
+  
+  /* Parse plist. It's quite dirty and inextensible but does its job. */
+  for (ptr1 = buf; ptr1 < buf + size; ptr1++)
+    switch (*ptr1)
+      {
+      case '<':
+       tagstart = ptr1;
+       *ptr1 = 0;
+       if (keyptr && depth == 4 
+           && grub_strcmp (keyptr, "OSBundleRequired") == 0)
+         osbundlekeyfound = 1;
+       if (keyptr && depth == 4 && 
+           grub_strcmp (keyptr, "CFBundleExecutable") == 0)
+         binnamekeyfound = 1;
+       if (stringptr && osbundlekeyfound && osbundlereq && depth == 4)
+         {
+           for (ptr2 = stringptr; *ptr2; ptr2++)
+             *ptr2 = grub_tolower (*ptr2);
+           ret = grub_strword (osbundlereq, stringptr) 
+             || grub_strword (osbundlereq, "all");
+         }
+       if (stringptr && binnamekeyfound && binname && depth == 4)
+         {
+           if (*binname)
+             grub_free (*binname);
+           *binname = grub_strdup (stringptr);
+         }
+
+       *ptr1 = '<';
+       keyptr = 0;
+       stringptr = 0;
+       break;
+      case '>':
+       if (! tagstart)
+         {
+           grub_free (buf);
+           grub_error (GRUB_ERR_BAD_OS, "can't parse %s", plistname);
+           return 0;
+         }
+       *ptr1 = 0;
+       if (tagstart[1] == '?' || ptr1[-1] == '/')
+         {
+           osbundlekeyfound = 0;
+           *ptr1 = '>';
+           break;
+         }
+       if (depth == 3 && grub_strcmp (tagstart + 1, "key") == 0)
+         keyptr = ptr1 + 1;
+       if (depth == 3 && grub_strcmp (tagstart + 1, "string") == 0)
+         stringptr = ptr1 + 1; 
+       else if (grub_strcmp (tagstart + 1, "/key") != 0)
+         {
+           osbundlekeyfound = 0;
+           binnamekeyfound = 0;
+         }
+       *ptr1 = '>';
+       
+       if (tagstart[1] == '/')
+         depth--;
+       else
+         depth++;
+       break;
+      }
+  grub_free (buf);
+
+  return ret; 
+}
+
+/* Load all loadable kexts placed under DIRNAME and matching OSBUNDLEREQUIRED 
*/
+grub_err_t
+grub_xnu_scan_dir_for_kexts (char *dirname, char *osbundlerequired, 
+                            int maxrecursion)
+{
+  grub_device_t dev;
+  char *device_name;
+  grub_fs_t fs;
+  const char *path;
+
+  auto int load_hook (const char *filename, 
+                     const struct grub_dirhook_info *info);
+  int load_hook (const char *filename, const struct grub_dirhook_info *info)
+  {
+    char *newdirname;
+    if (! info->dir)
+      return 0;
+    if (filename[0] == '.')
+      return 0;
+
+    if (grub_strlen (filename) < 5 ||
+       grub_memcmp (filename + grub_strlen (filename) - 5, ".kext", 5) != 0)
+      return 0;
+
+    newdirname 
+      = grub_malloc (grub_strlen (dirname) + grub_strlen (filename) + 2);
+
+    /* It's a .kext. Try to load it. */
+    if (newdirname)
+      {
+       grub_strcpy (newdirname, dirname);
+       newdirname[grub_strlen (newdirname) + 1] = 0;
+       newdirname[grub_strlen (newdirname)] = '/';
+       grub_strcpy (newdirname + grub_strlen (newdirname), filename);
+       grub_xnu_load_kext_from_dir (newdirname, osbundlerequired, 
+                                    maxrecursion);
+       if (grub_errno == GRUB_ERR_BAD_OS)
+         grub_errno = GRUB_ERR_NONE;
+       grub_free (newdirname);
+      }
+    return 0;
+  }
+
+  if (! grub_xnu_heap_size)
+    return grub_error (GRUB_ERR_BAD_OS, "no xnu kernel loaded");
+
+  device_name = grub_file_get_device_name (dirname);
+  dev = grub_device_open (device_name);
+  if (dev)
+    {
+      fs = grub_fs_probe (dev);
+      path = grub_strchr (dirname, ')');
+      if (! path)
+       path = dirname;
+      else
+       path++;
+
+      if (fs)
+       (fs->dir) (dev, path, load_hook);
+      grub_device_close (dev);
+    }
+  grub_free (device_name);
+
+  return GRUB_ERR_NONE;
+}
+
+/* Load extension DIRNAME. (extensions are directoris in xnu) */
+grub_err_t
+grub_xnu_load_kext_from_dir (char *dirname, char *osbundlerequired, 
+                            int maxrecursion)
+{
+  grub_device_t dev;
+  char *plistname = 0;
+  char *newdirname;
+  char *newpath;
+  char *device_name;
+  grub_fs_t fs;
+  const char *path;
+  char *binsuffix;
+  int usemacos = 0;
+  grub_file_t binfile;
+
+  auto int load_hook (const char *filename, 
+                     const struct grub_dirhook_info *info);
+
+  int load_hook (const char *filename, const struct grub_dirhook_info *info)
+  {
+    if (grub_strlen (filename) > 15)
+      return 0;
+    grub_strcpy (newdirname + grub_strlen (dirname) + 1, filename); 
+
+    /* If the kext contains directory "Contents" all real stuff is in 
+       this directory. */
+    if (info->dir && grub_strcasecmp (filename, "Contents") == 0)
+      grub_xnu_load_kext_from_dir (newdirname, osbundlerequired,
+                                  maxrecursion - 1);
+
+    /* Directory "Plugins" contains nested kexts. */
+    if (info->dir && grub_strcasecmp (filename, "Plugins") == 0)
+      grub_xnu_scan_dir_for_kexts (newdirname, osbundlerequired, 
+                                  maxrecursion - 1);
+
+    /* Directory "MacOS" contains executable, otherwise executable is 
+       on the top. */
+    if (info->dir && grub_strcasecmp (filename, "MacOS") == 0)
+      usemacos = 1;
+    
+    /* Info.plist is the file which governs our future actions. */
+    if (! info->dir && grub_strcasecmp (filename, "Info.plist") == 0 
+       && ! plistname)
+      plistname = grub_strdup (newdirname);
+    return 0;
+  }
+
+  newdirname = grub_malloc (grub_strlen (dirname) + 20);
+  if (! newdirname)
+    return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't allocate buffer");
+  grub_strcpy (newdirname, dirname);
+  newdirname[grub_strlen (dirname)] = '/';
+  newdirname[grub_strlen (dirname) + 1] = 0;  
+  device_name = grub_file_get_device_name (dirname);
+  dev = grub_device_open (device_name);
+  if (dev)
+    {
+      fs = grub_fs_probe (dev);
+      path = grub_strchr (dirname, ')');
+      if (! path)
+       path = dirname;
+      else
+       path++;
+
+      newpath = grub_strchr (newdirname, ')');
+      if (! newpath)
+       newpath = newdirname;
+      else
+       newpath++;
+      
+      /* Look at the directory. */
+      if (fs)
+       (fs->dir) (dev, path, load_hook);
+
+      if (plistname && grub_xnu_check_os_bundle_required 
+         (plistname, osbundlerequired, &binsuffix))
+       {
+         if (binsuffix)
+           {
+             /* Open the binary. */
+             char *binname = grub_malloc (grub_strlen (dirname) 
+                                          + grub_strlen (binsuffix) 
+                                          + sizeof ("/MacOS/"));
+             grub_strcpy (binname, dirname);
+             if (usemacos)
+               grub_strcpy (binname + grub_strlen (binname), "/MacOS/");
+             else
+               grub_strcpy (binname + grub_strlen (binname), "/");
+             grub_strcpy (binname + grub_strlen (binname), binsuffix);
+             grub_dprintf ("xnu", "%s:%s\n", plistname, binname);
+             binfile = grub_gzfile_open (binname, 1);
+             if (! binfile) 
+               grub_errno = GRUB_ERR_NONE;
+
+             /* Load the extension. */       
+             grub_xnu_load_driver (plistname, binfile);
+             grub_free (binname);
+             grub_free (binsuffix);
+           }
+         else
+           {
+             grub_dprintf ("xnu", "%s:0\n", plistname);
+             grub_xnu_load_driver (plistname, 0);
+           }
+       }
+      grub_free (plistname);
+      grub_device_close (dev);
+    }
+  grub_free (device_name);
+
+  return GRUB_ERR_NONE;
+}
+
+/* Load devtree file. */
+static grub_err_t
+grub_cmd_xnu_devtree (grub_command_t cmd __attribute__ ((unused)),
+                     int argc, char *args[])
+{
+  grub_file_t file;
+  char *data, *endret;
+  grub_size_t datalen;
+
+  if (argc != 1)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "Filename required");
+
+  if (! grub_xnu_heap_size)
+    return grub_error (GRUB_ERR_BAD_OS, "no xnu kernel loaded");
+
+  /* Load the file. */
+  file = grub_gzfile_open (args[0], 1);
+  if (! file)
+    return grub_error (GRUB_ERR_FILE_NOT_FOUND, "Couldn't load device tree");
+  datalen = grub_file_size (file);
+  data = grub_malloc (datalen + 1);
+  if (! data)
+    {
+      grub_file_close (file);
+      return grub_error (GRUB_ERR_OUT_OF_MEMORY, 
+                        "Could load device tree into memory");
+    }
+  if (grub_file_read (file, data, datalen) != (grub_ssize_t) datalen)
+    {
+      grub_file_close (file); 
+      grub_free (data);
+      grub_error_push ();
+      return grub_error (GRUB_ERR_BAD_OS, "Couldn't read file %s", args[0]);
+    }
+  grub_file_close (file);
+  data[datalen] = 0;
+
+  /* Parse the file. */
+  endret = grub_xnu_parse_devtree (&grub_xnu_devtree_root,
+                                  data, data + datalen);
+  grub_free (data);
+
+  if (! endret)
+    return grub_error (GRUB_ERR_BAD_OS, "Couldn't parse devtree");
+
+  return GRUB_ERR_NONE;
+}
+
+static int locked=0;
+static grub_dl_t my_mod;
+
+/* Load the kext. */
+static grub_err_t
+grub_cmd_xnu_kext (grub_command_t cmd __attribute__ ((unused)),
+                  int argc, char *args[])
+{
+  grub_file_t binfile = 0;
+  if (argc == 2)
+    {
+      /* User explicitely specified plist and binary. */
+      if (grub_strcmp (args[1], "-") != 0)
+       {
+         binfile = grub_gzfile_open (args[1], 1);
+         if (! binfile)
+           {
+             grub_error (GRUB_ERR_BAD_OS, "can't open file");
+             return GRUB_ERR_NONE;
+           }
+       }
+      return grub_xnu_load_driver (grub_strcmp (args[0], "-") ? args[0] : 0,
+                                  binfile);    
+    }
+
+  /* load kext normally. */
+  if (argc == 1)
+    return grub_xnu_load_kext_from_dir (args[0], 0, 10);
+
+  return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required");
+}
+
+/* Load a directory containing kexts. */
+static grub_err_t
+grub_cmd_xnu_kextdir (grub_command_t cmd __attribute__ ((unused)),
+                     int argc, char *args[])
+{
+  if (argc != 1 && argc != 2)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "directory name required");
+
+  if (argc == 1)
+    return grub_xnu_scan_dir_for_kexts (args[0], 
+                                       "console,root,local-root,network-root",
+                                       10);
+  else
+    {
+      char *osbundlerequired = grub_strdup (args[1]), *ptr;
+      grub_err_t err;
+      if (! osbundlerequired)
+       return grub_error (GRUB_ERR_OUT_OF_MEMORY, 
+                          "couldn't allocate string temporary space");
+      for (ptr = osbundlerequired; *ptr; ptr++)
+       *ptr = grub_tolower (*ptr);
+      err = grub_xnu_scan_dir_for_kexts (args[0], osbundlerequired, 10);
+      grub_free (osbundlerequired);
+      return err;
+    }
+}
+
+#ifndef GRUB_UTIL
+static grub_err_t
+grub_cmd_xnu_resume (grub_command_t cmd __attribute__ ((unused)),
+                    int argc, char *args[])
+{
+  if (argc != 1)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required");
+  
+  return grub_xnu_resume (args[0]);
+}
+#endif
+
+void
+grub_xnu_lock ()
+{
+#ifndef GRUB_UTIL
+  if (!locked)
+    grub_dl_ref (my_mod);
+#endif
+  locked = 1;
+}
+
+void
+grub_xnu_unlock ()
+{
+#ifndef GRUB_UTIL
+  if (locked)
+    grub_dl_unref (my_mod);
+#endif
+  locked = 0;
+}
+
+static grub_command_t cmd_kernel, cmd_mkext, cmd_kext, cmd_kextdir, 
+  cmd_ramdisk, cmd_devtree, cmd_resume;
+
+GRUB_MOD_INIT(xnu)
+{
+  (void) mod;                  /* To stop warning. */
+  cmd_kernel = grub_register_command ("xnu_kernel", grub_cmd_xnu_kernel, 0,
+                                     "load a xnu kernel");
+  cmd_mkext = grub_register_command ("xnu_mkext", grub_cmd_xnu_mkext, 0,
+                                    "Load XNU extension package.");
+  cmd_kext = grub_register_command ("xnu_kext", grub_cmd_xnu_kext, 0,
+                                   "Load XNU extension.");
+  cmd_kextdir = grub_register_command ("xnu_kextdir", grub_cmd_xnu_kextdir, 
+                                      "xnu_kextdir DIRECTORY 
[OSBundleRequired]",
+                                      "Load XNU extension directory");
+  cmd_ramdisk = grub_register_command ("xnu_ramdisk", grub_cmd_xnu_ramdisk, 0,
+                                      "Load XNU ramdisk. "
+                                      "It will be seen as md0");
+  cmd_devtree = grub_register_command ("xnu_devtree", grub_cmd_xnu_devtree, 0,
+                                      "Load XNU devtree");
+#ifndef GRUB_UTIL
+  cmd_resume = grub_register_command ("xnu_resume", grub_cmd_xnu_resume, 
+                                     0, "Load XNU hibernate image.");
+#endif
+  my_mod=mod;
+}
+
+GRUB_MOD_FINI(xnu)
+{
+#ifndef GRUB_UTIL
+  grub_unregister_command (cmd_resume);
+#endif
+  grub_unregister_command (cmd_mkext);
+  grub_unregister_command (cmd_kext);
+  grub_unregister_command (cmd_kextdir);
+  grub_unregister_command (cmd_devtree);
+  grub_unregister_command (cmd_ramdisk);
+  grub_unregister_command (cmd_kernel);
+}
diff --git a/loader/xnu_resume.c b/loader/xnu_resume.c
new file mode 100644
index 0000000..24dd77b
--- /dev/null
+++ b/loader/xnu_resume.c
@@ -0,0 +1,134 @@
+/*
+ *  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/normal.h>
+#include <grub/dl.h>
+#include <grub/file.h>
+#include <grub/disk.h>
+#include <grub/misc.h>
+#include <grub/xnu.h>
+#include <grub/cpu/xnu.h>
+#include <grub/mm.h>
+#include <grub/loader.h>
+
+static void *grub_xnu_hibernate_image;
+
+static grub_err_t
+grub_xnu_resume_unload (void)
+{
+  /* Free loaded image */
+  if (grub_xnu_hibernate_image)
+    grub_free (grub_xnu_hibernate_image);
+  grub_xnu_hibernate_image = 0;
+  grub_xnu_unlock ();
+  return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_xnu_resume (char *imagename)
+{
+  grub_file_t file;
+  grub_size_t total_header_size;
+  struct grub_xnu_hibernate_header hibhead;
+  void *buf;
+
+#if GRUB_CPU_SIZEOF_VOID_P == 8
+  grub_uint64_t codedest;
+  grub_uint64_t codesize;
+#else
+  grub_uint32_t codedest;
+  grub_uint32_t codesize;
+#endif
+
+  file = grub_file_open (imagename);
+  if (! file)
+    return 0;
+  
+  /* Read the header. */
+  if (grub_file_read (file, (char *) &hibhead, sizeof (hibhead))
+      !=sizeof (hibhead))
+    {
+      grub_file_close (file);
+      return grub_error (GRUB_ERR_READ_ERROR, 
+                        "cannot read the hibernate header");
+    }
+
+  /* Check the header. */
+  if (hibhead.magic != GRUB_XNU_HIBERNATE_MAGIC)
+    {
+      grub_file_close (file);
+      return grub_error (GRUB_ERR_BAD_OS, 
+                        "hibernate header has incorrect magic number");
+    }
+  if (hibhead.encoffset)
+    {
+      grub_file_close (file);
+      return grub_error (GRUB_ERR_BAD_OS, 
+                        "encrypted images aren't supported yet");
+    }
+
+  codedest = hibhead.launchcode_target_page;
+  codedest *= GRUB_XNU_PAGESIZE;
+  codesize = hibhead.launchcode_numpages;
+  codesize *= GRUB_XNU_PAGESIZE;
+
+  /* FIXME: check that codedest..codedest+codesize is available. */
+
+  /* Calculate total size before pages to copy. */
+  total_header_size = hibhead.extmapsize + sizeof (hibhead);
+
+  /* Unload image if any. */
+  if (grub_xnu_hibernate_image)
+    grub_free (grub_xnu_hibernate_image);
+
+  /* Try to allocate necessary space. 
+     FIXME: mm isn't good enough yet to handle huge allocations.
+   */
+  grub_xnu_hibernate_image = buf = grub_malloc (hibhead.image_size);
+  if (! buf)
+    {
+      grub_file_close (file);
+      return grub_error (GRUB_ERR_OUT_OF_MEMORY, 
+                        "not enough memory to load image");
+    }
+
+  /* Read image. */
+  if (grub_file_seek (file, 0) == (grub_off_t)-1 
+      || grub_file_read (file, buf, hibhead.image_size)
+      != (grub_ssize_t) hibhead.image_size)
+    {
+      grub_file_close (file);
+      return grub_error (GRUB_ERR_READ_ERROR, "Cannot read resume image.");
+    }
+  grub_file_close (file);
+
+  /* Move starting pages to appropriate location. */
+  memcpy ((void *) codedest, ((grub_uint8_t *) buf) + total_header_size, 
+         codesize);
+
+  /* Setup variables needed by asm helper. */
+  grub_xnu_stack = (codedest + hibhead.stack);
+  grub_xnu_entry_point = (codedest + hibhead.entry_point);
+  grub_xnu_arg1 = (long) buf;
+
+  /* We're ready now. */
+  grub_loader_set (grub_xnu_launch, grub_xnu_resume_unload, 0);
+  /* Prevent module from unloading. */
+  grub_xnu_lock ();
+  return GRUB_ERR_NONE;
+}

reply via email to

[Prev in Thread] Current Thread [Next in Thread]