]> git.ipfire.org Git - thirdparty/grub.git/commitdiff
crashing snow64
authorphcoder <phcoder@debian.easynote.phnet.easynote.phnet>
Wed, 2 Sep 2009 00:52:10 +0000 (02:52 +0200)
committerphcoder <phcoder@debian.easynote.phnet.easynote.phnet>
Wed, 2 Sep 2009 00:52:10 +0000 (02:52 +0200)
conf/i386-efi.rmk
conf/i386-pc.rmk
conf/x86_64-efi.rmk
include/grub/i386/macho.h
include/grub/macho.h
include/grub/machoload.h
loader/macho.c
loader/xnu.c

index 18a9a636b175e7d367dc29910fabecbce5beedbf..4eb3853f1381a219968c70a4018c5cc9a52eccb4 100644 (file)
@@ -196,8 +196,9 @@ 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_SOURCES = loader/xnu_resume.c loader/i386/xnu.c loader/i386/efi/xnu.c \
+       loader/macho32.c loader/macho64.c loader/macho.c loader/xnu.c   \
+       loader/i386/xnu_helper.S
 xnu_mod_CFLAGS = $(COMMON_CFLAGS)
 xnu_mod_LDFLAGS = $(COMMON_LDFLAGS)
 xnu_mod_ASFLAGS = $(COMMON_ASFLAGS)
index 8d7d9fca9c3ee51dfd985e6a6cb568149cd012a1..a617e1b782519b22ebfb21d4201996a297db005d 100644 (file)
@@ -242,8 +242,9 @@ 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_SOURCES = loader/xnu_resume.c loader/i386/xnu.c loader/i386/pc/xnu.c \
+        loader/macho32.c loader/macho64.c loader/macho.c loader/xnu.c  \
+        loader/i386/xnu_helper.S
 xnu_mod_CFLAGS = $(COMMON_CFLAGS)
 xnu_mod_LDFLAGS = $(COMMON_LDFLAGS)
 xnu_mod_ASFLAGS = $(COMMON_ASFLAGS)
index 1a4cf89edab4363bbecbd3f2a21cb70bc35956d0..5087465b46cde98fc3cd4ed6796e97038970c44d 100644 (file)
@@ -195,8 +195,9 @@ 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_SOURCES = loader/xnu_resume.c loader/i386/xnu.c loader/i386/efi/xnu.c \
+       loader/macho32.c loader/macho64.c loader/macho.c loader/xnu.c \
+       loader/i386/xnu_helper.S
 xnu_mod_CFLAGS = $(COMMON_CFLAGS)
 xnu_mod_LDFLAGS = $(COMMON_LDFLAGS)
 xnu_mod_ASFLAGS = $(COMMON_ASFLAGS)
index 61e72a71b6d0073309001282c123b7a4edd2ad55..f22c21190139f1803735ecec707e2a6b258ac91b 100644 (file)
@@ -1,3 +1,26 @@
+/*
+ *  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_CPU_MACHO_H
+#define GRUB_CPU_MACHO_H 1
+
+#include <grub/macho.h>
+
 #define GRUB_MACHO_CPUTYPE_IS_HOST32(x) ((x)==0x00000007)
 #define GRUB_MACHO_CPUTYPE_IS_HOST64(x) ((x)==0x01000007)
 
@@ -9,3 +32,15 @@ struct grub_macho_thread32
   grub_uint32_t entry_point;
   grub_uint8_t unknown2[20];
 } __attribute__ ((packed));
+
+
+struct grub_macho_thread64
+{
+  grub_uint32_t cmd;
+  grub_uint32_t cmdsize;
+  grub_uint8_t unknown1[0x88];
+  grub_uint64_t entry_point;
+  grub_uint8_t unknown2[0x20];
+} __attribute__ ((packed));
+
+#endif
index 0604100e90f34d37d1cd6e0ab7625a7e236e42cc..82145835f0d3361f084d3aeb3b2993cf7e05a168 100644 (file)
@@ -102,6 +102,23 @@ struct grub_macho_segment32
   grub_uint32_t flags;
 } __attribute__ ((packed));
 
+/* 64-bit segment command. */
+struct grub_macho_segment64
+{
+#define GRUB_MACHO_CMD_SEGMENT64  0x19
+  grub_uint32_t cmd;
+  grub_uint32_t cmdsize;
+  grub_uint8_t segname[16];
+  grub_uint64_t vmaddr;
+  grub_uint64_t vmsize;
+  grub_uint64_t fileoff;
+  grub_uint64_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
index a80bac68cbcead0455f134793b41ab322c111ce2..8410162fb606145779dc008b75b5927f50d012a1 100644 (file)
@@ -46,17 +46,28 @@ 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);
+grub_err_t grub_macho_size32 (grub_macho_t macho, grub_uint32_t *segments_start,
+                             grub_uint32_t *segments_end, int flags);
+grub_uint32_t grub_macho_get_entry_point32 (grub_macho_t macho);
+
+int grub_macho_contains_macho64 (grub_macho_t);
+grub_err_t grub_macho_size64 (grub_macho_t macho, grub_uint64_t *segments_start,
+                             grub_uint64_t *segments_end, int flags);
+grub_uint64_t grub_macho_get_entry_point64 (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);
+grub_err_t grub_macho_load32 (grub_macho_t macho, char *offset, int flags);
+grub_err_t grub_macho_load64 (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);
+grub_size_t grub_macho_filesize32 (grub_macho_t macho);
+grub_err_t grub_macho_readfile32 (grub_macho_t macho, void *dest);
+grub_size_t grub_macho_filesize64 (grub_macho_t macho);
+grub_err_t grub_macho_readfile64 (grub_macho_t macho, void *dest);
+
+void grub_macho_parse32 (grub_macho_t macho);
+void grub_macho_parse64 (grub_macho_t macho);
 
 #endif /* ! GRUB_MACHOLOAD_HEADER */
index bd460b8102e0a222f027164d9226bbed53a63221..a23f5b206ffe3d9a5b0f504cc68a43ba6d7a04bb 100644 (file)
 #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, &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, 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 architecture-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 architecture-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)
 {
@@ -367,8 +134,7 @@ grub_macho_file (grub_file_t file)
     }
 
   grub_macho_parse32 (macho);
-  /* FIXME: implement 64-bit.*/
-  /*  grub_macho_parse64 (macho); */
+  grub_macho_parse64 (macho);
 
   return macho;
 
index 1f29b69a13339f643b77adc3595819be531ad4f3..271a746e68a9f217149113c3ed286094f5872d35 100644 (file)
@@ -35,6 +35,7 @@
 struct grub_xnu_devtree_key *grub_xnu_devtree_root = 0;
 static int driverspackagenum = 0;
 static int driversnum = 0;
+static int is_64bit;
 
 /* Allocate heap by 32MB-blocks. */
 #define GRUB_XNU_HEAP_ALLOC_BLOCK 0x2000000
@@ -352,7 +353,7 @@ grub_cmd_xnu_kernel (grub_command_t cmd __attribute__ ((unused)),
 {
   grub_err_t err;
   grub_macho_t macho;
-  grub_addr_t startcode, endcode;
+  grub_uint32_t startcode, endcode;
   int i;
   char *ptr, *loadaddr;
 
@@ -368,10 +369,10 @@ grub_cmd_xnu_kernel (grub_command_t cmd __attribute__ ((unused)),
     {
       grub_macho_close (macho);
       return grub_error (GRUB_ERR_BAD_OS,
-                        "Kernel doesn't contain suitable architecture");
+                        "Kernel doesn't contain suitable 32-bit architecture");
     }
 
-  err = grub_macho32_size (macho, &startcode, &endcode, GRUB_MACHO_NOBSS);
+  err = grub_macho_size32 (macho, &startcode, &endcode, GRUB_MACHO_NOBSS);
   if (err)
     {
       grub_macho_close (macho);
@@ -394,7 +395,7 @@ grub_cmd_xnu_kernel (grub_command_t cmd __attribute__ ((unused)),
     }
 
   /* Load kernel. */
-  err = grub_macho32_load (macho, loadaddr - startcode, GRUB_MACHO_NOBSS);
+  err = grub_macho_load32 (macho, loadaddr - startcode, GRUB_MACHO_NOBSS);
   if (err)
     {
       grub_macho_close (macho);
@@ -402,7 +403,7 @@ grub_cmd_xnu_kernel (grub_command_t cmd __attribute__ ((unused)),
       return err;
     }
 
-  grub_xnu_entry_point = grub_macho32_get_entry_point (macho);
+  grub_xnu_entry_point = grub_macho_get_entry_point32 (macho);
   if (! grub_xnu_entry_point)
     {
       grub_macho_close (macho);
@@ -443,6 +444,113 @@ grub_cmd_xnu_kernel (grub_command_t cmd __attribute__ ((unused)),
   grub_loader_set (grub_xnu_boot, grub_xnu_unload, 0);
 
   grub_xnu_lock ();
+  is_64bit = 0;
+
+  return 0;
+}
+
+static grub_err_t
+grub_cmd_xnu_kernel64 (grub_command_t cmd __attribute__ ((unused)),
+                      int argc, char *args[])
+{
+  grub_err_t err;
+  grub_macho_t macho;
+  grub_uint64_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_macho64 (macho))
+    {
+      grub_macho_close (macho);
+      return grub_error (GRUB_ERR_BAD_OS,
+                        "Kernel doesn't contain suitable 64-bit architecture");
+    }
+
+  err = grub_macho_size64 (macho, &startcode, &endcode, GRUB_MACHO_NOBSS);
+  if (err)
+    {
+      grub_macho_close (macho);
+      grub_xnu_unload ();
+      return err;
+    }
+
+  startcode &= 0x0fffffff;
+  endcode &= 0x0fffffff;
+
+  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_macho_load64 (macho, loadaddr - startcode, GRUB_MACHO_NOBSS);
+  if (err)
+    {
+      grub_macho_close (macho);
+      grub_xnu_unload ();
+      return err;
+    }
+
+  grub_xnu_entry_point = grub_macho_get_entry_point64 (macho) & 0x0fffffff;
+  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 ();
+  is_64bit = 1;
+
   return 0;
 }
 
@@ -560,7 +668,10 @@ grub_xnu_load_driver (char *infoplistname, grub_file_t binaryfile)
          return grub_error (GRUB_ERR_BAD_OS,
                             "Extension doesn't contain suitable architecture");
        }
-      machosize = grub_macho32_filesize (macho);
+      if (is_64bit)
+       machosize = grub_macho_filesize64 (macho);
+      else
+       machosize = grub_macho_filesize32 (macho);
       neededspace += machosize;
     }
   else
@@ -595,7 +706,11 @@ grub_xnu_load_driver (char *infoplistname, grub_file_t binaryfile)
       exthead->binaryaddr = (buf - grub_xnu_heap_start)
        + grub_xnu_heap_will_be_at;
       exthead->binarysize = machosize;
-      if ((err = grub_macho32_readfile (macho, buf)))
+      if (is_64bit)
+       err = grub_macho_readfile64 (macho, buf);
+      else
+       err = grub_macho_readfile32 (macho, buf);
+      if (err)
        {
          grub_macho_close (macho);
          return err;
@@ -695,7 +810,13 @@ grub_cmd_xnu_mkext (grub_command_t cmd __attribute__ ((unused)),
        }
       for (i = 0; i < narchs; i++)
        {
-         if (GRUB_MACHO_CPUTYPE_IS_HOST32
+         if (!is_64bit && 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);
+           }
+         if (is_64bit && GRUB_MACHO_CPUTYPE_IS_HOST64
              (grub_be_to_cpu32 (archs[i].cputype)))
            {
              readoff = grub_be_to_cpu32 (archs[i].offset);
@@ -1363,13 +1484,16 @@ grub_xnu_unlock ()
   locked = 0;
 }
 
-static grub_command_t cmd_kernel, cmd_mkext, cmd_kext, cmd_kextdir,
-  cmd_ramdisk, cmd_devtree, cmd_resume, cmd_splash;
+static grub_command_t cmd_kernel64, cmd_kernel, cmd_mkext, cmd_kext;
+static grub_command_t cmd_kextdir, cmd_ramdisk, cmd_devtree, cmd_resume;
+static grub_command_t cmd_splash;
 
 GRUB_MOD_INIT(xnu)
 {
   cmd_kernel = grub_register_command ("xnu_kernel", grub_cmd_xnu_kernel, 0,
                                      "load a xnu kernel");
+  cmd_kernel64 = grub_register_command ("xnu_kernel64", grub_cmd_xnu_kernel64,
+                                       0, "load a 64-bit 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,
@@ -1403,5 +1527,6 @@ GRUB_MOD_FINI(xnu)
   grub_unregister_command (cmd_devtree);
   grub_unregister_command (cmd_ramdisk);
   grub_unregister_command (cmd_kernel);
+  grub_unregister_command (cmd_kernel64);
   grub_unregister_command (cmd_splash);
 }