[2163] 2009-05-03 Vladimir Serbinenko <[email protected]>

Vladimir Serbinenko <[email protected]>
Newsgroups gmane.comp.boot-loaders.grub.cvs
Message-ID <[email protected]>
Revision: 2163
          http://svn.sv.gnu.org/viewvc/?view=rev&root=grub&revision=2163
Author:   phcoder
Date:     2009-05-02 23:19:20 +0000 (Sat, 02 May 2009)
Log Message:
-----------
2009-05-03  Vladimir Serbinenko  <[email protected]>

	xnu support

	* conf/i386-efi.rmk (kernel_mod_HEADERS): added i386/pit.h
	(pkglib_MODULES): add xnu.mod
	(xnu_mod_SOURCES): new variable
	(xnu_mod_CFLAGS): likewise
	(xnu_mod_LDFLAGS): likewise
	(xnu_mod_ASFLAGS): likewise
	* conf/i386-pc.rmk: likewise
	* conf/x86_64-efi.rmk: likewise
	* include/grub/efi/efi.h (grub_efi_finish_boot_services): 
	new declaration
	* include/grub/i386/macho.h: new file
	* include/grub/i386/xnu.h: likewise
	* include/grub/macho.h: likewise
	* include/grub/machoload.h: likewise
	* include/grub/x86_64/macho.h: likewise
	* include/grub/x86_64/xnu.h: likewise
	* include/grub/xnu.h: likewise
	* kern/efi/efi.c (grub_efi_finish_boot_services): new function
	* kern/efi/mm.c (MAX_HEAP_SIZE): increase
	* loader/i386/efi/xnu.c: new file
	* loader/i386/pc/xnu.c: likewise
	* loader/i386/xnu.c: likewise
	* loader/i386/xnu_helper.S: likewise
	* loader/macho.c: likewise
	* loader/xnu.c: likewise
	* loader/xnu_resume.c: likewise
	* util/grub-dumpdevtree: likewise
	* include/grub/i386/pit.h: include grub/err.h
	(grub_pit_wait): export
	* util/grub.d/30_os-prober.in: support Darwin/Mac OS X
	

Modified Paths:
--------------
    trunk/grub2/ChangeLog
    trunk/grub2/conf/i386-efi.rmk
    trunk/grub2/conf/i386-pc.rmk
    trunk/grub2/conf/x86_64-efi.rmk
    trunk/grub2/include/grub/efi/efi.h
    trunk/grub2/include/grub/i386/pit.h
    trunk/grub2/kern/efi/efi.c
    trunk/grub2/kern/efi/mm.c
    trunk/grub2/util/grub.d/30_os-prober.in

Added Paths:
-----------
    trunk/grub2/include/grub/i386/macho.h
    trunk/grub2/include/grub/i386/xnu.h
    trunk/grub2/include/grub/macho.h
    trunk/grub2/include/grub/machoload.h
    trunk/grub2/include/grub/x86_64/macho.h
    trunk/grub2/include/grub/x86_64/xnu.h
    trunk/grub2/include/grub/xnu.h
    trunk/grub2/loader/i386/efi/xnu.c
    trunk/grub2/loader/i386/pc/xnu.c
    trunk/grub2/loader/i386/xnu.c
    trunk/grub2/loader/i386/xnu_helper.S
    trunk/grub2/loader/macho.c
    trunk/grub2/loader/xnu.c
    trunk/grub2/loader/xnu_resume.c
    trunk/grub2/util/grub-dumpdevtree

Modified: trunk/grub2/ChangeLog
===================================================================
--- trunk/grub2/ChangeLog	2009-05-02 22:40:21 UTC (rev 2162)
+++ trunk/grub2/ChangeLog	2009-05-02 23:19:20 UTC (rev 2163)
@@ -1,3 +1,38 @@
+2009-05-03  Vladimir Serbinenko  <[email protected]>
+
+	xnu support
+
+	* conf/i386-efi.rmk (kernel_mod_HEADERS): added i386/pit.h
+	(pkglib_MODULES): add xnu.mod
+	(xnu_mod_SOURCES): new variable
+	(xnu_mod_CFLAGS): likewise
+	(xnu_mod_LDFLAGS): likewise
+	(xnu_mod_ASFLAGS): likewise
+	* conf/i386-pc.rmk: likewise
+	* conf/x86_64-efi.rmk: likewise
+	* include/grub/efi/efi.h (grub_efi_finish_boot_services): 
+	new declaration
+	* include/grub/i386/macho.h: new file
+	* include/grub/i386/xnu.h: likewise
+	* include/grub/macho.h: likewise
+	* include/grub/machoload.h: likewise
+	* include/grub/x86_64/macho.h: likewise
+	* include/grub/x86_64/xnu.h: likewise
+	* include/grub/xnu.h: likewise
+	* kern/efi/efi.c (grub_efi_finish_boot_services): new function
+	* kern/efi/mm.c (MAX_HEAP_SIZE): increase
+	* loader/i386/efi/xnu.c: new file
+	* loader/i386/pc/xnu.c: likewise
+	* loader/i386/xnu.c: likewise
+	* loader/i386/xnu_helper.S: likewise
+	* loader/macho.c: likewise
+	* loader/xnu.c: likewise
+	* loader/xnu_resume.c: likewise
+	* util/grub-dumpdevtree: likewise
+	* include/grub/i386/pit.h: include grub/err.h
+	(grub_pit_wait): export
+	* util/grub.d/30_os-prober.in: support Darwin/Mac OS X
+	
 2009-05-02  Vladimir Serbinenko  <[email protected]>
 
 	Efiemu

Modified: trunk/grub2/conf/i386-efi.rmk
===================================================================
--- trunk/grub2/conf/i386-efi.rmk	2009-05-02 22:40:21 UTC (rev 2162)
+++ trunk/grub2/conf/i386-efi.rmk	2009-05-02 23:19:20 UTC (rev 2163)
@@ -101,7 +101,7 @@
 kernel_mod_HEADERS = boot.h cache.h device.h disk.h dl.h elf.h elfload.h \
 	env.h err.h file.h fs.h kernel.h loader.h misc.h mm.h net.h parser.h \
 	partition.h pc_partition.h reader.h symbol.h term.h time.h types.h \
-	efi/efi.h efi/time.h efi/disk.h list.h handler.h command.h
+	efi/efi.h efi/time.h efi/disk.h i386/pit.h list.h handler.h command.h
 kernel_mod_CFLAGS = $(COMMON_CFLAGS)
 kernel_mod_ASFLAGS = $(COMMON_ASFLAGS)
 kernel_mod_LDFLAGS = $(COMMON_LDFLAGS)
@@ -187,5 +187,12 @@
 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

Modified: trunk/grub2/conf/i386-pc.rmk
===================================================================
--- trunk/grub2/conf/i386-pc.rmk	2009-05-02 22:40:21 UTC (rev 2162)
+++ trunk/grub2/conf/i386-pc.rmk	2009-05-02 23:19:20 UTC (rev 2163)
@@ -62,7 +62,7 @@
 	partition.h pc_partition.h reader.h symbol.h term.h time.h types.h \
 	machine/biosdisk.h machine/boot.h machine/console.h machine/init.h \
 	machine/memory.h machine/loader.h machine/vga.h machine/vbe.h \
-	machine/kernel.h machine/pxe.h list.h handler.h command.h
+	machine/kernel.h machine/pxe.h i386/pit.h list.h handler.h command.h
 kernel_img_CFLAGS = $(COMMON_CFLAGS)
 kernel_img_ASFLAGS = $(COMMON_ASFLAGS)
 kernel_img_LDFLAGS = $(COMMON_LDFLAGS) $(TARGET_IMG_LDFLAGS) -Wl,-Ttext,$(GRUB_MEMORY_MACHINE_LINK_ADDR) $(COMMON_CFLAGS)
@@ -229,6 +229,13 @@
 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)
+
 # For reboot.mod.
 reboot_mod_SOURCES = commands/reboot.c
 reboot_mod_CFLAGS = $(COMMON_CFLAGS)

Modified: trunk/grub2/conf/x86_64-efi.rmk
===================================================================
--- trunk/grub2/conf/x86_64-efi.rmk	2009-05-02 22:40:21 UTC (rev 2162)
+++ trunk/grub2/conf/x86_64-efi.rmk	2009-05-02 23:19:20 UTC (rev 2163)
@@ -99,8 +99,8 @@
 kernel_mod_HEADERS = boot.h cache.h device.h disk.h dl.h elf.h elfload.h \
 	env.h err.h file.h fs.h kernel.h loader.h misc.h mm.h net.h parser.h \
 	partition.h pc_partition.h reader.h symbol.h term.h time.h types.h \
-	efi/efi.h efi/time.h efi/disk.h machine/loader.h list.h handler.h \
-	command.h
+	efi/efi.h efi/time.h efi/disk.h machine/loader.h i386/pit.h list.h \
+	handler.h command.h
 kernel_mod_CFLAGS = $(COMMON_CFLAGS)
 kernel_mod_ASFLAGS = $(COMMON_ASFLAGS)
 kernel_mod_LDFLAGS = $(COMMON_LDFLAGS)
@@ -186,4 +186,11 @@
 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/common.mk

Modified: trunk/grub2/include/grub/efi/efi.h
===================================================================
--- trunk/grub2/include/grub/efi/efi.h	2009-05-02 22:40:21 UTC (rev 2162)
+++ trunk/grub2/include/grub/efi/efi.h	2009-05-02 23:19:20 UTC (rev 2163)
@@ -56,6 +56,7 @@
 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);

Added: trunk/grub2/include/grub/i386/macho.h
===================================================================
--- trunk/grub2/include/grub/i386/macho.h	                        (rev 0)
+++ trunk/grub2/include/grub/i386/macho.h	2009-05-02 23:19:20 UTC (rev 2163)
@@ -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));

Modified: trunk/grub2/include/grub/i386/pit.h
===================================================================
--- trunk/grub2/include/grub/i386/pit.h	2009-05-02 22:40:21 UTC (rev 2162)
+++ trunk/grub2/include/grub/i386/pit.h	2009-05-02 23:19:20 UTC (rev 2163)
@@ -20,7 +20,8 @@
 #define KERNEL_CPU_PIT_HEADER   1
 
 #include <grub/types.h>
+#include <grub/err.h>
 
-extern void grub_pit_wait (grub_uint16_t tics);
+void EXPORT_FUNC(grub_pit_wait) (grub_uint16_t tics);
 
 #endif /* ! KERNEL_CPU_PIT_HEADER */

Added: trunk/grub2/include/grub/i386/xnu.h
===================================================================
--- trunk/grub2/include/grub/i386/xnu.h	                        (rev 0)
+++ trunk/grub2/include/grub/i386/xnu.h	2009-05-02 23:19:20 UTC (rev 2163)
@@ -0,0 +1,60 @@
+#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
+
+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);
+extern grub_uint32_t grub_xnu_heap_will_be_at;
+extern grub_uint8_t grub_xnu_launcher_start[];
+extern grub_uint8_t grub_xnu_launcher_end[];
+#endif

Added: trunk/grub2/include/grub/macho.h
===================================================================
--- trunk/grub2/include/grub/macho.h	                        (rev 0)
+++ trunk/grub2/include/grub/macho.h	2009-05-02 23:19:20 UTC (rev 2163)
@@ -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

Added: trunk/grub2/include/grub/machoload.h
===================================================================
--- trunk/grub2/include/grub/machoload.h	                        (rev 0)
+++ trunk/grub2/include/grub/machoload.h	2009-05-02 23:19:20 UTC (rev 2163)
@@ -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 */

Added: trunk/grub2/include/grub/x86_64/macho.h
===================================================================
--- trunk/grub2/include/grub/x86_64/macho.h	                        (rev 0)
+++ trunk/grub2/include/grub/x86_64/macho.h	2009-05-02 23:19:20 UTC (rev 2163)
@@ -0,0 +1 @@
+#include <grub/i386/macho.h>

Added: trunk/grub2/include/grub/x86_64/xnu.h
===================================================================
--- trunk/grub2/include/grub/x86_64/xnu.h	                        (rev 0)
+++ trunk/grub2/include/grub/x86_64/xnu.h	2009-05-02 23:19:20 UTC (rev 2163)
@@ -0,0 +1 @@
+#include <grub/i386/xnu.h>

Added: trunk/grub2/include/grub/xnu.h
===================================================================
--- trunk/grub2/include/grub/xnu.h	                        (rev 0)
+++ trunk/grub2/include/grub/xnu.h	2009-05-02 23:19:20 UTC (rev 2163)
@@ -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_XNU_H
+#define GRUB_XNU_H 1
+
+#include <grub/bitmap.h>
+
+/* 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 struct grub_video_bitmap *grub_xnu_bitmap;
+#endif

Modified: trunk/grub2/kern/efi/efi.c
===================================================================
--- trunk/grub2/kern/efi/efi.c	2009-05-02 22:40:21 UTC (rev 2162)
+++ trunk/grub2/kern/efi/efi.c	2009-05-02 23:19:20 UTC (rev 2163)
@@ -734,3 +734,26 @@
       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);
+}
+

Modified: trunk/grub2/kern/efi/mm.c
===================================================================
--- trunk/grub2/kern/efi/mm.c	2009-05-02 22:40:21 UTC (rev 2162)
+++ trunk/grub2/kern/efi/mm.c	2009-05-02 23:19:20 UTC (rev 2163)
@@ -47,7 +47,7 @@
 
 /* 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.  */

Added: trunk/grub2/loader/i386/efi/xnu.c
===================================================================
--- trunk/grub2/loader/i386/efi/xnu.c	                        (rev 0)
+++ trunk/grub2/loader/i386/efi/xnu.c	2009-05-02 23:19:20 UTC (rev 2163)
@@ -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: %ux%u-%u@%u\n", 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_mode = GRUB_XNU_VIDEO_TEXT_IN_VIDEO;
+  params->lfb_base = fb_base;
+  return GRUB_ERR_NONE;
+}

Added: trunk/grub2/loader/i386/pc/xnu.c
===================================================================
--- trunk/grub2/loader/i386/pc/xnu.c	                        (rev 0)
+++ trunk/grub2/loader/i386/pc/xnu.c	2009-05-02 23:19:20 UTC (rev 2163)
@@ -0,0 +1,80 @@
+/*
+ *  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>
+
+#define min(a,b) (((a) < (b)) ? (a) : (b))
+#define max(a,b) (((a) > (b)) ? (a) : (b))
+
+/* Setup video for xnu. */
+grub_err_t
+grub_xnu_set_video (struct grub_xnu_boot_params *params)
+{
+  struct grub_video_mode_info mode_info;
+  struct grub_video_render_target *render_target;
+  int ret;
+  int x,y;
+  grub_err_t err;
+
+  ret = grub_video_get_info (&mode_info);
+  if (ret)
+    return grub_error (GRUB_ERR_IO, "couldn't retrieve video parameters");
+
+  ret = grub_video_get_active_render_target (&render_target);
+  if (ret)
+    return grub_error (GRUB_ERR_IO, "couldn't retrieve video parameters");
+
+  err = GRUB_ERR_NONE;
+  x = mode_info.width - grub_xnu_bitmap->mode_info.width;
+  x /= 2;
+  y = mode_info.height - grub_xnu_bitmap->mode_info.height;
+  y /= 2;
+  err = grub_video_blit_bitmap (grub_xnu_bitmap,
+				GRUB_VIDEO_BLIT_REPLACE,
+				x > 0 ? x : 0,
+				y > 0 ? y : 0,
+				x < 0 ? -x : 0,
+				y < 0 ? -y : 0,
+				min (grub_xnu_bitmap->mode_info.width, 
+				     mode_info.width), 
+				min (grub_xnu_bitmap->mode_info.height, 
+				     mode_info.height));
+  if (err)
+    {
+      grub_print_error ();
+      grub_errno = GRUB_ERR_NONE;
+      grub_xnu_bitmap = 0;
+    }
+
+  params->lfb_width = mode_info.width;
+  params->lfb_height = mode_info.height;
+  params->lfb_depth = mode_info.bpp;
+  params->lfb_line_len = mode_info.pitch;
+
+  params->lfb_base = PTR_TO_UINT32 (render_target->data);
+  params->lfb_mode = grub_xnu_bitmap 
+    ? GRUB_XNU_VIDEO_SPLASH : GRUB_XNU_VIDEO_TEXT_IN_VIDEO; 
+
+  return GRUB_ERR_NONE;
+}
+

Added: trunk/grub2/loader/i386/xnu.c
===================================================================
--- trunk/grub2/loader/i386/xnu.c	                        (rev 0)
+++ trunk/grub2/loader/i386/xnu.c	2009-05-02 23:19:20 UTC (rev 2163)
@@ -0,0 +1,539 @@
+/*
+ *  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/autoefi.h>
+#include <grub/i386/tsc.h>
+#include <grub/i386/pit.h>
+#include <grub/misc.h>
+#include <grub/term.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
+static 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;
+}
+#else
+static void (*grub_xnu_launch) (void) = 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;
+}
+
+/* Thanks to Kabyl for precious information about Intel architecture. */
+static grub_uint64_t
+guessfsb (void)
+{
+  const grub_uint64_t sane_value = 100000000;
+  grub_uint32_t manufacturer[3], max_cpuid, capabilities, msrlow;
+  grub_uint64_t start_tsc;
+  grub_uint64_t end_tsc;
+  grub_uint64_t tsc_ticks_per_ms;
+
+  if (! grub_cpu_is_cpuid_supported ())
+    return sane_value;
+  asm volatile ("movl $0, %%eax\n"
+		"cpuid"
+		: "=a" (max_cpuid), "=b" (manufacturer[0]), 
+		  "=d" (manufacturer[1]), "=c" (manufacturer[2]));
+
+  /* Only Intel for now is done. */
+  if (grub_memcmp (manufacturer, "GenuineIntel", 12) != 0)
+    return sane_value;
+
+  /* Check Speedstep. */
+  if (max_cpuid < 1)
+    return sane_value;
+
+  asm volatile ("movl $1, %%eax\n"
+		"cpuid"
+		: "=c" (capabilities):
+		: "%eax", "%ebx", "%edx");
+
+  if (! (capabilities & (1 << 7)))
+    return sane_value;
+
+  /* Calibrate the TSC rate. */
+
+  start_tsc = grub_get_tsc ();
+  grub_pit_wait (0xffff);
+  end_tsc = grub_get_tsc ();
+
+  tsc_ticks_per_ms = grub_divmod64 (end_tsc - start_tsc, 55, 0);
+
+  /* Read the multiplier. */
+  asm volatile ("movl $0x198, %%ecx\n"
+		"rdmsr"
+		: "=d" (msrlow)
+		:
+		: "%ecx", "%eax");
+
+  return grub_divmod64 (2000 * tsc_ticks_per_ms, 
+			((msrlow >> 7) & 0x3e) + ((msrlow >> 14) & 1), 0);
+}
+
+/* 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;
+  grub_err_t err;
+
+  err = grub_autoefi_prepare ();
+  if (err)
+    return err;
+
+  /* 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 (); 
+  grub_dprintf ("xnu", "fsb autodetected as %llu\n", 
+		(unsigned long long) *((grub_uint64_t *) curval->data));
+
+  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. */
+  err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE);
+  if (err)
+    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;
+  err = grub_xnu_writetree_toheap (&devtree, &devtreelen);
+  if (err)
+    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 = ((char *) devtree - grub_xnu_heap_start) 
+    + grub_xnu_heap_will_be_at;
+  bootparams_relloc->devtreelen = devtreelen;
+
+  bootparams_relloc->heap_start = grub_xnu_heap_will_be_at;
+  bootparams_relloc->heap_size = grub_xnu_heap_size;
+
+  bootparams_relloc->efi_mmap = grub_xnu_heap_will_be_at + 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;
+
+  /* Parameters for asm helper. */
+  grub_xnu_stack = bootparams_relloc->heap_start 
+    + bootparams_relloc->heap_size + GRUB_XNU_PAGESIZE;
+  grub_xnu_arg1 = bootparams_relloc_off + grub_xnu_heap_will_be_at;
+#ifndef GRUB_UTIL
+  grub_xnu_launch = (void (*) (void)) 
+    (grub_xnu_heap_start + grub_xnu_heap_size);
+#endif
+  grub_dprintf ("xnu", "eip=%x\n", grub_xnu_entry_point);
+  grub_dprintf ("xnu", "launch=%p\n", grub_xnu_launch);
+
+  const char *debug = grub_env_get ("debug");
+    
+  if (debug && (grub_strword (debug, "all") || grub_strword (debug, "xnu")))
+    {
+      grub_printf ("Press any key to launch xnu\n");
+      grub_getkey ();
+    }
+  
+  /* Set video. */
+  err = grub_xnu_set_video (bootparams_relloc);
+  if (err != GRUB_ERR_NONE)
+    {
+      grub_print_error ();
+      grub_errno = GRUB_ERR_NONE;
+      grub_printf ("Booting in blind mode\n");
+
+      bootparams_relloc->lfb_mode = 0;
+      bootparams_relloc->lfb_width = 0;
+      bootparams_relloc->lfb_height = 0;
+      bootparams_relloc->lfb_depth = 0;
+      bootparams_relloc->lfb_line_len = 0;
+      bootparams_relloc->lfb_base = 0;
+    }
+
+  grub_memcpy (grub_xnu_heap_start + grub_xnu_heap_size, 
+	       grub_xnu_launcher_start, 
+	       grub_xnu_launcher_end - grub_xnu_launcher_start);
+
+
+  if (! grub_autoefi_finish_boot_services ())
+    return grub_error (GRUB_ERR_IO, "can't exit boot services");
+
+  grub_xnu_launch ();
+
+  /* Never reaches here. */
+  return 0;
+}

Added: trunk/grub2/loader/i386/xnu_helper.S
===================================================================
--- trunk/grub2/loader/i386/xnu_helper.S	                        (rev 0)
+++ trunk/grub2/loader/i386/xnu_helper.S	2009-05-02 23:19:20 UTC (rev 2163)
@@ -0,0 +1,192 @@
+/*
+ *  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	4	/* force 16-byte alignment */
+
+VARIABLE(grub_xnu_launcher_start)
+base:	
+	cli
+	
+#ifndef __x86_64__
+	/* mov imm32, %eax */
+	.byte	0xb8
+VARIABLE(grub_xnu_heap_will_be_at)
+	.long 0
+	mov %eax, %edi
+	
+	/* mov imm32, %eax */
+	.byte	0xb8
+VARIABLE(grub_xnu_heap_start)
+	.long 0
+	mov %eax, %esi
+	
+	/* mov imm32, %ecx */
+	.byte	0xb9
+VARIABLE(grub_xnu_heap_size)
+	.long 0
+	mov %edi, %eax
+	add %ecx, %eax
+	/* %rax now contains our starting position after relocation. */
+	/* One more page to copy: ourselves. */
+	add $0x403, %ecx
+	shr $2, %ecx
+
+	/* Forward copy.  */
+	cld
+	rep
+	movsl
+
+	mov %eax, %esi
+	add $(cont0-base), %eax
+	jmp *%eax
+cont0:	
+#else
+	xorq %rax, %rax
+	
+	/* mov imm32, %eax */
+	.byte	0xb8
+VARIABLE(grub_xnu_heap_will_be_at)
+	.long 0
+	mov %rax, %rdi
+	
+	/* mov imm32, %rax */
+	.byte	0x48
+	.byte	0xb8
+VARIABLE(grub_xnu_heap_start)
+	.long 0
+	.long 0
+	mov %rax, %rsi
+	
+	/* mov imm32, %rcx */
+	.byte	0x48
+	.byte	0xb9
+VARIABLE(grub_xnu_heap_size)
+	.long 0
+	.long 0
+	mov %rdi, %rax
+	add %rcx, %rax
+	/* %rax now contains our starting position after relocation. */
+	/* One more page to copy: ourselves. */
+	add $0x403, %rcx
+	shr $2, %rcx
+
+	/* Forward copy.  */
+	cld
+	rep
+	movsl
+
+	mov %rax, %rsi
+	add $(cont0-base), %rax
+	jmp *%rax
+
+cont0:	
+
+	lea (cont1-base)(%rsi, 1), %rax
+	mov %eax, (jump_vector-base)(%rsi,1)
+
+	lea (gdt-base)(%rsi, 1), %rax
+	mov %rax, (gdt_addr-base)(%rsi,1)
+	
+	/* Switch to compatibility mode. */
+
+	lgdt (gdtdesc-base)(%rsi,1)
+	
+	/* Update %cs. Thanks to David Miller for pointing this mistake out. */
+	ljmp *(jump_vector-base)(%rsi,1)
+cont1:
+	.code32
+
+	/* Update other registers. */
+	mov $0x18, %eax
+	mov %eax, %ds 
+	mov %eax, %es
+	mov %eax, %fs
+	mov %eax, %gs
+	mov %eax, %ss
+
+	/* Disable paging. */
+	mov %cr0, %eax
+	and $0x7fffffff, %eax
+	mov %eax, %cr0
+
+	/* Disable amd64. */
+	mov $0xc0000080, %ecx
+	rdmsr
+	and $0xfffffeff, %eax
+	wrmsr
+
+	/* Turn off PAE. */
+	movl %cr4, %eax
+	and $0xffffffcf, %eax
+	mov %eax, %cr4
+
+	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 31
+gdt_addr:	
+	/* Filled by the code. */
+	.quad 0
+	
+	.p2align 4
+jump_vector:
+	/* Jump location. Is filled by the code */
+	.long 0
+	.long 0x10	
+#endif
+VARIABLE(grub_xnu_launcher_end)

Added: trunk/grub2/loader/macho.c
===================================================================
--- trunk/grub2/loader/macho.c	                        (rev 0)
+++ trunk/grub2/loader/macho.c	2009-05-02 23:19:20 UTC (rev 2163)
@@ -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;
+}

Added: trunk/grub2/loader/xnu.c
===================================================================
--- trunk/grub2/loader/xnu.c	                        (rev 0)
+++ trunk/grub2/loader/xnu.c	2009-05-02 23:19:20 UTC (rev 2163)
@@ -0,0 +1,1368 @@
+/* xnu.c - load xnu kernel. Thanks to Florian Idelberger for all the 
+   time he spent testing this
+ */
+/*
+ *  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/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;
+
+/* 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;
+  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 + GRUB_XNU_PAGESIZE 
+				 - *size % GRUB_XNU_PAGESIZE);
+
+  /* Put real data in the dummy. */
+  extdesc->addr = (char *) *start - grub_xnu_heap_start 
+    + grub_xnu_heap_will_be_at;
+  extdesc->size = (grub_uint32_t) *size;
+
+  /* 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_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_xnu_heap_will_be_at + 
+    ((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)
+	+ grub_xnu_heap_will_be_at;
+      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) 
+	+ grub_xnu_heap_will_be_at;
+      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;
+    }
+}
+
+struct grub_video_bitmap *grub_xnu_bitmap = 0;
+
+static grub_err_t
+grub_cmd_xnu_splash (grub_command_t cmd __attribute__ ((unused)),
+		     int argc, char *args[])
+{
+  grub_err_t err;
+  if (argc != 1)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required");  
+  
+  err = grub_video_bitmap_load (&grub_xnu_bitmap, args[0]);
+  if (err)
+    grub_xnu_bitmap = 0;
+  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, cmd_splash;
+
+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");
+  cmd_splash = grub_register_command ("xnu_splash", grub_cmd_xnu_splash, 0,
+				      "Load a splash image for XNU");
+
+#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);
+  grub_unregister_command (cmd_splash);
+}

Added: trunk/grub2/loader/xnu_resume.c
===================================================================
--- trunk/grub2/loader/xnu_resume.c	                        (rev 0)
+++ trunk/grub2/loader/xnu_resume.c	2009-05-02 23:19:20 UTC (rev 2163)
@@ -0,0 +1,136 @@
+/*
+ *  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;
+  char *buf, *codetmp;
+
+  grub_uint32_t codedest;
+  grub_uint32_t codesize;
+
+  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);
+
+  codetmp = grub_memalign (GRUB_XNU_PAGESIZE, codesize + GRUB_XNU_PAGESIZE);
+  /* Setup variables needed by asm helper. */
+  grub_xnu_heap_will_be_at = codedest;
+  grub_xnu_heap_start = codetmp;
+  grub_xnu_heap_size = codesize;
+  grub_xnu_stack = (codedest + hibhead.stack);
+  grub_xnu_entry_point = (codedest + hibhead.entry_point);
+  grub_xnu_arg1 = (long) buf;
+
+  /* Prepare asm helper. */
+  grub_memcpy (codetmp, ((grub_uint8_t *) buf) + total_header_size, codesize);
+  grub_memcpy (codetmp + codesize, grub_xnu_launcher_start, 
+	       grub_xnu_launcher_end - grub_xnu_launcher_start);  
+
+  /* We're ready now. */
+  grub_loader_set ((grub_err_t (*) (void)) (codetmp + codesize), 
+		   grub_xnu_resume_unload, 0);
+
+  /* Prevent module from unloading. */
+  grub_xnu_lock ();
+  return GRUB_ERR_NONE;
+}

Added: trunk/grub2/util/grub-dumpdevtree
===================================================================
--- trunk/grub2/util/grub-dumpdevtree	                        (rev 0)
+++ trunk/grub2/util/grub-dumpdevtree	2009-05-02 23:19:20 UTC (rev 2163)
@@ -0,0 +1,3 @@
+echo "656669{	6465766963652d70726f70657274696573:"
+ioreg -lw0 -p IODeviceTree -n efi -r -x |grep device-properties | sed 's/.*<//;s/>.*//;'
+echo ";}"

Modified: trunk/grub2/util/grub.d/30_os-prober.in
===================================================================
--- trunk/grub2/util/grub.d/30_os-prober.in	2009-05-02 22:40:21 UTC (rev 2162)
+++ trunk/grub2/util/grub.d/30_os-prober.in	2009-05-02 23:19:20 UTC (rev 2163)
@@ -82,6 +82,58 @@
 EOF
       done
     ;;
+    macosx)
+      OSXROOT="`grub-probe --target=drive --device ${DEVICE} 2> /dev/null`"
+      # FIXME: use UUID
+      OSXDISK=disk"`echo ${OSXROOT} | awk -F , '{ print $1 ; }' | sed 's/(hd//;'`"s"`echo ${OSXROOT} | awk -F , '{ print $2 ; }' | sed 's/)//;'`"
+        cat << EOF
+menuentry "${LONGNAME} (on ${DEVICE})" {
+	set root=${OSXROOT}
+        insmod vbe
+        insmod gfxterm
+        gfxmode="1024x768x32;800x600x32"
+        terminal_output gfxterm
+        do_resume=0
+        if [ /var/vm/sleepimage -nt10 / ]; then
+           if xnu_resume /var/vm/sleepimage; then
+             do_resume=1
+           fi
+        fi
+        if [ \$do_resume == 0 ]; then
+           if [ -f /Extra/DSDT.aml ]; then
+              acpi -e /Extra/DSDT.aml
+           fi
+           xnu_kernel /mach_kernel rd=$OSXDISK
+           if [ /System/Library/Extensions.mkext -nt /System/Library/Extensions ]; then
+              xnu_mkext /System/Library/Extensions.mkext
+           else
+              xnu_kextdir /System/Library/Extensions
+           fi
+           if [ -f /Extra/Extensions.mkext ]; then
+              xnu_mkext /Extra/Extensions.mkext
+           fi
+           if [ -d /Extra/Extensions ]; then
+              xnu_kextdir /Extra/Extensions
+           fi
+           if [ -f /Extra/devtree.txt ]; then
+              xnu_devtree /Extra/devtree.txt
+           fi
+           if [ -f /Extra/splash.jpg ]; then
+              insmod jpeg
+              xnu_splash /Extra/splash.jpg
+           fi
+           if [ -f /Extra/splash.png ]; then
+              insmod png
+              xnu_splash /Extra/splash.png
+           fi
+           if [ -f /Extra/splash.tga ]; then
+              insmod tga
+              xnu_splash /Extra/splash.tga
+           fi
+        fi
+}
+EOF
+    ;;
     hurd|*)
       echo "  ${LONGNAME} is not yet supported by grub-mkconfig." >&2
     ;;
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.