* [PATCH] xnu
@ 2009-04-14 22:46 phcoder
2009-04-17 23:22 ` Joey Korkames
` (2 more replies)
0 siblings, 3 replies; 13+ messages in thread
From: phcoder @ 2009-04-14 22:46 UTC (permalink / raw)
To: The development of GRUB 2
[-- Attachment #1: Type: text/plain, Size: 2455 bytes --]
Hello. Here is my xnu patch. Tested on i386-pc, i386-efi and x86_64-efi.
On non-efi it needs efiemu otherwise you need only
include/grub/autoefi.h file from efiemu patch. To resume xnu from
hibernation do:
xnu_resume <hibernation file>
Note: you don't need efiemu in this case
How to boot xnu:
<efiemu if not on efi platform>
[on pc only:] vbe_mode=0xYYY # desired vga mode
fsb=133.3 # your fsb frequency
xnu_kernel <kernel> <command line>
<insert modules>
boot
Modules can be inserted one of the following ways:
1) xnu_mkext <mkext file>
2) xnu_kext extension.kext
3) xnu_kext extension.kext/Info.plist extension.kext/extension
4) xnu_kextdir <directory containing extensions>
It's also possible to execute these commands multiple times
The most typical case is
xnu_kernel /mach_kernel rd=disk0sX
xnu_mkext /System/Library/Extensions.mkext
If you need to add values to device tree the command
xnu_devtree <devtree file>
This file uses the following format:
valuename:valuedata;
keyname{
contents
}
keyname, valuename and valuedata are in hex.
If you need to adda ramdisk execute
xnu_ramdisk <ramdisk file>
ramdisk will be exposed as /dev/md0 which may be used as boot device
with rd=md0.
The areas which need more work (every help is welcome):
1) testing on different platforms
2) Detect "device-properties" value in device tree. There are several
trivial values in device tree present in boot.efi but not in grub2.
These ones are easy to add and AFAIK don't change anything. But the
value "device-properties" is difficult. I already know it's format but
not where the values come from. It contains info about gfx and sound
card which may not work if this value is missing. The current workaround
is xnu_devtree command with a dump of device-properties
3) autodetect fsb frequency
4) Support video splash
5) Define and use an unified interface to retrieve video information
uniformly across platforms
6) Boot by UUID. I know how to do it but need md5 for it which is a part
of pending luks patch
7) Scripts for automatic creating of grub.cfg entries
8) Support for prelinked kernel. It's compressed and I have yet looked
how to decompress it (seems it's compressed with something called lzss)
9) Use claimmap once available (see my multiboot on efi patch)
10) Better collaboration with memory management once advanced mm's available
11) Resume from encrypted hibernation
--
Regards
Vladimir 'phcoder' Serbinenko
[-- Attachment #2: xnu.diff --]
[-- Type: text/x-patch, Size: 94263 bytes --]
diff --git a/conf/i386-efi.rmk b/conf/i386-efi.rmk
index 09c8470..33bb023 100644
--- a/conf/i386-efi.rmk
+++ b/conf/i386-efi.rmk
@@ -194,5 +194,12 @@ fixvideo_mod_SOURCES = commands/efi/fixvideo.c
fixvideo_mod_CFLAGS = $(COMMON_CFLAGS)
fixvideo_mod_LDFLAGS = $(COMMON_LDFLAGS)
+pkglib_MODULES += xnu.mod
+xnu_mod_SOURCES = loader/xnu_resume.c loader/i386/xnu.c loader/i386/efi/xnu.c\
+ loader/macho.c loader/xnu.c loader/i386/xnu_helper.S
+xnu_mod_CFLAGS = $(COMMON_CFLAGS) -Werror -Wall
+xnu_mod_LDFLAGS = $(COMMON_LDFLAGS)
+xnu_mod_ASFLAGS = $(COMMON_ASFLAGS)
+
include $(srcdir)/conf/i386.mk
include $(srcdir)/conf/common.mk
diff --git a/conf/i386-pc.rmk b/conf/i386-pc.rmk
index 7a6d79a..b7d4c77 100644
--- a/conf/i386-pc.rmk
+++ b/conf/i386-pc.rmk
@@ -225,6 +225,13 @@ linux_mod_SOURCES = loader/i386/linux.c
linux_mod_CFLAGS = $(COMMON_CFLAGS)
linux_mod_LDFLAGS = $(COMMON_LDFLAGS)
+pkglib_MODULES += xnu.mod
+xnu_mod_SOURCES = loader/xnu_resume.c loader/i386/xnu.c loader/i386/pc/xnu.c\
+ loader/macho.c loader/xnu.c loader/i386/xnu_helper.S
+xnu_mod_CFLAGS = $(COMMON_CFLAGS) -Werror -Wall
+xnu_mod_LDFLAGS = $(COMMON_LDFLAGS)
+xnu_mod_ASFLAGS = $(COMMON_ASFLAGS)
+
#
# Only arch dependant part of normal.mod will be here. Common part for
# all architecures of normal.mod is at start and should be kept at sync
diff --git a/conf/x86_64-efi.rmk b/conf/x86_64-efi.rmk
index 59237c1..2fb3cf9 100644
--- a/conf/x86_64-efi.rmk
+++ b/conf/x86_64-efi.rmk
@@ -197,5 +197,12 @@ fixvideo_mod_SOURCES = commands/efi/fixvideo.c
fixvideo_mod_CFLAGS = $(COMMON_CFLAGS)
fixvideo_mod_LDFLAGS = $(COMMON_LDFLAGS)
+pkglib_MODULES += xnu.mod
+xnu_mod_SOURCES = loader/xnu_resume.c loader/i386/xnu.c loader/i386/efi/xnu.c\
+ loader/macho.c loader/xnu.c loader/i386/xnu_helper.S
+xnu_mod_CFLAGS = $(COMMON_CFLAGS) -Werror -Wall
+xnu_mod_LDFLAGS = $(COMMON_LDFLAGS)
+xnu_mod_ASFLAGS = $(COMMON_ASFLAGS)
+
include $(srcdir)/conf/i386.mk
include $(srcdir)/conf/common.mk
diff --git a/include/grub/efi/efi.h b/include/grub/efi/efi.h
index 8c277c0..916f9d6 100644
--- a/include/grub/efi/efi.h
+++ b/include/grub/efi/efi.h
@@ -56,6 +56,7 @@ EXPORT_FUNC(grub_efi_get_device_path) (grub_efi_handle_t handle);
int EXPORT_FUNC(grub_efi_exit_boot_services) (grub_efi_uintn_t map_key);
void EXPORT_FUNC (grub_reboot) (void);
void EXPORT_FUNC (grub_halt) (void);
+int EXPORT_FUNC (grub_efi_finish_boot_services) (void);
void grub_efi_mm_init (void);
void grub_efi_mm_fini (void);
diff --git a/include/grub/i386/macho.h b/include/grub/i386/macho.h
new file mode 100644
index 0000000..61e72a7
--- /dev/null
+++ b/include/grub/i386/macho.h
@@ -0,0 +1,11 @@
+#define GRUB_MACHO_CPUTYPE_IS_HOST32(x) ((x)==0x00000007)
+#define GRUB_MACHO_CPUTYPE_IS_HOST64(x) ((x)==0x01000007)
+
+struct grub_macho_thread32
+{
+ grub_uint32_t cmd;
+ grub_uint32_t cmdsize;
+ grub_uint8_t unknown1[48];
+ grub_uint32_t entry_point;
+ grub_uint8_t unknown2[20];
+} __attribute__ ((packed));
diff --git a/include/grub/i386/xnu.h b/include/grub/i386/xnu.h
new file mode 100644
index 0000000..435b947
--- /dev/null
+++ b/include/grub/i386/xnu.h
@@ -0,0 +1,58 @@
+#ifndef GRUB_CPU_XNU_H
+#define GRUB_CPU_XNU_H 1
+
+#define GRUB_XNU_PAGESIZE 4096
+typedef grub_uint32_t grub_xnu_ptr_t;
+
+struct grub_xnu_boot_params
+{
+ grub_uint16_t verminor;
+ grub_uint16_t vermajor;
+ /* Command line passed to xnu. */
+ grub_uint8_t cmdline[1024];
+
+ /* Later are the same as EFI's get_memory_map (). */
+ grub_xnu_ptr_t efi_mmap;
+ grub_uint32_t efi_mmap_size;
+ grub_uint32_t efi_mem_desc_size;
+ grub_uint32_t efi_mem_desc_version;
+
+ /* Later are video parameters. */
+ grub_xnu_ptr_t lfb_base;
+#define GRUB_XNU_VIDEO_SPLASH 1
+#define GRUB_XNU_VIDEO_TEXT_IN_VIDEO 2
+ grub_uint32_t lfb_mode;
+ grub_uint32_t lfb_line_len;
+ grub_uint32_t lfb_width;
+ grub_uint32_t lfb_height;
+ grub_uint32_t lfb_depth;
+
+ /* Pointer to device tree and its len. */
+ grub_xnu_ptr_t devtree;
+ grub_uint32_t devtreelen;
+
+ /* First used address by kernel or boot structures. */
+ grub_xnu_ptr_t heap_start;
+ /* Last used address by kernel or boot structures minus previous value. */
+ grub_uint32_t heap_size;
+
+ /* First memory page containing runtime code or data. */
+ grub_uint32_t efi_runtime_first_page;
+ /* First memory page containing runtime code or data minus previous value. */
+ grub_uint32_t efi_runtime_npages;
+ grub_uint32_t efi_system_table;
+ /* Size of grub_efi_uintn_t in bits. */
+ grub_uint8_t efi_uintnbits;
+} __attribute__ ((packed));
+#define GRUB_XNU_BOOTARGS_VERMINOR 4
+#define GRUB_XNU_BOOTARGS_VERMAJOR 1
+
+grub_err_t grub_xnu_launch (void);
+extern grub_uint32_t grub_xnu_entry_point;
+extern grub_uint32_t grub_xnu_stack;
+extern grub_uint32_t grub_xnu_arg1;
+extern char grub_xnu_cmdline[1024];
+grub_err_t grub_xnu_boot (void);
+grub_err_t grub_cpu_xnu_fill_devicetree (void);
+grub_err_t grub_xnu_set_video (struct grub_xnu_boot_params *bootparams_relloc);
+#endif
diff --git a/include/grub/macho.h b/include/grub/macho.h
new file mode 100644
index 0000000..7dfd54e
--- /dev/null
+++ b/include/grub/macho.h
@@ -0,0 +1,107 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GRUB_MACHO_H
+#define GRUB_MACHO_H 1
+#include <grub/types.h>
+
+/* Multi-architecture header. Always in big-endian. */
+struct grub_macho_fat_header
+{
+ grub_uint32_t magic;
+ grub_uint32_t nfat_arch;
+} __attribute__ ((packed));
+#define GRUB_MACHO_FAT_MAGIC 0xcafebabe
+
+typedef grub_uint32_t grub_macho_cpu_type_t;
+typedef grub_uint32_t grub_macho_cpu_subtype_t;
+
+/* Architecture descriptor. Always in big-endian. */
+struct grub_macho_fat_arch
+{
+ grub_macho_cpu_type_t cputype;
+ grub_macho_cpu_subtype_t cpusubtype;
+ grub_uint32_t offset;
+ grub_uint32_t size;
+ grub_uint32_t align;
+} __attribute__ ((packed));;
+
+/* File header for 32-bit. Always in native-endian. */
+struct grub_macho_header32
+{
+#define GRUB_MACHO_MAGIC32 0xfeedface
+ grub_uint32_t magic;
+ grub_macho_cpu_type_t cputype;
+ grub_macho_cpu_subtype_t cpusubtype;
+ grub_uint32_t filetype;
+ grub_uint32_t ncmds;
+ grub_uint32_t sizeofcmds;
+ grub_uint32_t flags;
+} __attribute__ ((packed));
+
+/* File header for 64-bit. Always in native-endian. */
+struct grub_macho_header64
+{
+#define GRUB_MACHO_MAGIC64 0xfeedfacf
+ grub_uint32_t magic;
+ grub_macho_cpu_type_t cputype;
+ grub_macho_cpu_subtype_t cpusubtype;
+ grub_uint32_t filetype;
+ grub_uint32_t ncmds;
+ grub_uint32_t sizeofcmds;
+ grub_uint32_t flags;
+ grub_uint32_t reserved;
+} __attribute__ ((packed));
+
+/* Convenience union. What do we need to load to identify the file type. */
+union grub_macho_filestart
+{
+ struct grub_macho_fat_header fat;
+ struct grub_macho_header32 thin32;
+ struct grub_macho_header64 thin64;
+} __attribute__ ((packed));
+
+/* Common header of Mach-O commands. */
+struct grub_macho_cmd
+{
+ grub_uint32_t cmd;
+ grub_uint32_t cmdsize;
+} __attribute__ ((packed));
+
+typedef grub_uint32_t grub_macho_vmprot_t;
+
+/* 32-bit segment command. */
+struct grub_macho_segment32
+{
+#define GRUB_MACHO_CMD_SEGMENT32 1
+ grub_uint32_t cmd;
+ grub_uint32_t cmdsize;
+ grub_uint8_t segname[16];
+ grub_uint32_t vmaddr;
+ grub_uint32_t vmsize;
+ grub_uint32_t fileoff;
+ grub_uint32_t filesize;
+ grub_macho_vmprot_t maxprot;
+ grub_macho_vmprot_t initprot;
+ grub_uint32_t nsects;
+ grub_uint32_t flags;
+} __attribute__ ((packed));
+
+#define GRUB_MACHO_CMD_THREAD 5
+
+#endif
diff --git a/include/grub/machoload.h b/include/grub/machoload.h
new file mode 100644
index 0000000..572496f
--- /dev/null
+++ b/include/grub/machoload.h
@@ -0,0 +1,62 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GRUB_MACHOLOAD_HEADER
+#define GRUB_MACHOLOAD_HEADER 1
+
+#include <grub/err.h>
+#include <grub/elf.h>
+#include <grub/file.h>
+#include <grub/symbol.h>
+#include <grub/types.h>
+
+struct grub_macho_file
+{
+ grub_file_t file;
+ grub_ssize_t offset32;
+ grub_ssize_t end32;
+ int ncmds32;
+ grub_size_t cmdsize32;
+ grub_uint8_t *cmds32;
+ grub_ssize_t offset64;
+ grub_ssize_t end64;
+ int ncmds64;
+ grub_size_t cmdsize64;
+ grub_uint8_t *cmds64;
+};
+typedef struct grub_macho_file *grub_macho_t;
+
+grub_macho_t grub_macho_open (const char *);
+grub_macho_t grub_macho_file (grub_file_t);
+grub_err_t grub_macho_close (grub_macho_t);
+
+int grub_macho_contains_macho32 (grub_macho_t);
+grub_err_t grub_macho32_size (grub_macho_t macho, grub_addr_t *segments_start,
+ grub_addr_t *segments_end, int flags);
+grub_uint32_t grub_macho32_get_entry_point (grub_macho_t macho);
+
+/* Ignore BSS segments when loading. */
+#define GRUB_MACHO_NOBSS 0x1
+grub_err_t grub_macho32_load (grub_macho_t macho, char *offset, int flags);
+
+/* Like filesize and file_read but take only 32-bit part
+ for current architecture. */
+grub_size_t grub_macho32_filesize (grub_macho_t macho);
+grub_err_t grub_macho32_readfile (grub_macho_t macho, void *dest);
+
+#endif /* ! GRUB_MACHOLOAD_HEADER */
diff --git a/include/grub/x86_64/macho.h b/include/grub/x86_64/macho.h
new file mode 100644
index 0000000..165b8da
--- /dev/null
+++ b/include/grub/x86_64/macho.h
@@ -0,0 +1 @@
+#include <grub/i386/macho.h>
diff --git a/include/grub/x86_64/xnu.h b/include/grub/x86_64/xnu.h
new file mode 100644
index 0000000..ae61733
--- /dev/null
+++ b/include/grub/x86_64/xnu.h
@@ -0,0 +1 @@
+#include <grub/i386/xnu.h>
diff --git a/include/grub/xnu.h b/include/grub/xnu.h
new file mode 100644
index 0000000..08ae49b
--- /dev/null
+++ b/include/grub/xnu.h
@@ -0,0 +1,105 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GRUB_XNU_H
+#define GRUB_XNU_H 1
+
+/* Header of a hibernation image. */
+struct grub_xnu_hibernate_header
+{
+ /* Size of the image. Notice that file containing image is usually bigger. */
+ grub_uint64_t image_size;
+ grub_uint8_t unknown1[8];
+ /* Where to copy lauchcode?*/
+ grub_uint32_t launchcode_target_page;
+ /* How many pages of launchcode? */
+ grub_uint32_t launchcode_numpages;
+ /* Where to jump? */
+ grub_uint32_t entry_point;
+ /* %esp at start. */
+ grub_uint32_t stack;
+ grub_uint8_t unknown2[44];
+#define GRUB_XNU_HIBERNATE_MAGIC 0x73696d65
+ grub_uint32_t magic;
+ grub_uint8_t unknown3[28];
+ /* This value is non-zero if page is encrypted. Unsupported. */
+ grub_uint64_t encoffset;
+ grub_uint8_t unknown4[360];
+ /* The size of additional header used to locate image without parsing FS.
+ Used only to skip it.
+ */
+ grub_uint32_t extmapsize;
+} __attribute__ ((packed));
+
+/* In-memory structure for temporary keeping device tree. */
+struct grub_xnu_devtree_key
+{
+ char *name;
+ int datasize; /* -1 for not leaves. */
+ union
+ {
+ struct grub_xnu_devtree_key *first_child;
+ void *data;
+ };
+ struct grub_xnu_devtree_key *next;
+};
+
+/* A structure used in memory-map values. */
+struct
+grub_xnu_extdesc
+{
+ grub_uint32_t addr;
+ grub_uint32_t size;
+} __attribute__ ((packed));
+
+/* Header describing extension in the memory. */
+struct grub_xnu_extheader
+{
+ grub_uint32_t infoplistaddr;
+ grub_uint32_t infoplistsize;
+ grub_uint32_t binaryaddr;
+ grub_uint32_t binarysize;
+} __attribute__ ((packed));
+
+struct grub_xnu_devtree_key *grub_xnu_create_key (struct grub_xnu_devtree_key **parent,
+ char *name);
+
+extern struct grub_xnu_devtree_key *grub_xnu_devtree_root;
+
+void grub_xnu_free_devtree (struct grub_xnu_devtree_key *cur);
+
+grub_err_t grub_xnu_writetree_toheap (void **start, grub_size_t *size);
+struct grub_xnu_devtree_key *grub_xnu_create_value (struct grub_xnu_devtree_key **parent,
+ char *name);
+
+void grub_xnu_lock (void);
+void grub_xnu_unlock (void);
+grub_err_t grub_xnu_resume (char *imagename);
+struct grub_xnu_devtree_key *grub_xnu_find_key (struct grub_xnu_devtree_key *parent,
+ char *name);
+grub_err_t grub_xnu_align_heap (int align);
+grub_err_t grub_xnu_scan_dir_for_kexts (char *dirname, char *osbundlerequired,
+ int maxrecursion);
+grub_err_t grub_xnu_load_kext_from_dir (char *dirname, char *osbundlerequired,
+ int maxrecursion);
+void *grub_xnu_heap_malloc (int size);
+extern grub_uint32_t grub_xnu_heap_real_start;
+extern grub_size_t grub_xnu_heap_size;
+extern char *grub_xnu_heap_start;
+extern grub_addr_t grub_xnu_heap_will_be_at;
+#endif
diff --git a/kern/efi/efi.c b/kern/efi/efi.c
index 9c9a400..075732a 100644
--- a/kern/efi/efi.c
+++ b/kern/efi/efi.c
@@ -734,3 +734,26 @@ grub_efi_print_device_path (grub_efi_device_path_t *dp)
dp = (grub_efi_device_path_t *) ((char *) dp + len);
}
}
+
+int
+grub_efi_finish_boot_services (void)
+{
+ grub_efi_uintn_t mmap_size = 0;
+ grub_efi_uintn_t map_key;
+ grub_efi_uintn_t desc_size;
+ grub_efi_uint32_t desc_version;
+ void *mmap_buf = 0;
+
+ if (grub_efi_get_memory_map (&mmap_size, mmap_buf, &map_key,
+ &desc_size, &desc_version) < 0)
+ return 0;
+
+ mmap_buf = grub_malloc (mmap_size);
+
+ if (grub_efi_get_memory_map (&mmap_size, mmap_buf, &map_key,
+ &desc_size, &desc_version) <= 0)
+ return 0;
+
+ return grub_efi_exit_boot_services (map_key);
+}
+
diff --git a/kern/efi/mm.c b/kern/efi/mm.c
index 35b12ab..4635776 100644
--- a/kern/efi/mm.c
+++ b/kern/efi/mm.c
@@ -47,7 +47,7 @@ static struct allocated_page *allocated_pages = 0;
/* The minimum and maximum heap size for GRUB itself. */
#define MIN_HEAP_SIZE 0x100000
-#define MAX_HEAP_SIZE (16 * 0x100000)
+#define MAX_HEAP_SIZE (1600 * 0x100000)
/* Allocate pages. Return the pointer to the first of allocated pages. */
diff --git a/loader/i386/efi/xnu.c b/loader/i386/efi/xnu.c
new file mode 100644
index 0000000..5885c37
--- /dev/null
+++ b/loader/i386/efi/xnu.c
@@ -0,0 +1,177 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/env.h>
+#include <grub/xnu.h>
+#include <grub/cpu/xnu.h>
+#include <grub/efi/api.h>
+#include <grub/efi/efi.h>
+#include <grub/efi/uga_draw.h>
+#include <grub/pci.h>
+#include <grub/misc.h>
+
+/* Setup video for xnu. Big parts are copied from linux.c. */
+
+static grub_efi_guid_t uga_draw_guid = GRUB_EFI_UGA_DRAW_GUID;
+
+#define RGB_MASK 0xffffff
+#define RGB_MAGIC 0x121314
+#define LINE_MIN 800
+#define LINE_MAX 4096
+#define FBTEST_STEP (0x10000 >> 2)
+#define FBTEST_COUNT 8
+
+static int
+find_line_len (grub_uint32_t *fb_base, grub_uint32_t *line_len)
+{
+ grub_uint32_t *base = (grub_uint32_t *) (grub_target_addr_t) *fb_base;
+ int i;
+
+ for (i = 0; i < FBTEST_COUNT; i++, base += FBTEST_STEP)
+ {
+ if ((*base & RGB_MASK) == RGB_MAGIC)
+ {
+ int j;
+
+ for (j = LINE_MIN; j <= LINE_MAX; j++)
+ {
+ if ((base[j] & RGB_MASK) == RGB_MAGIC)
+ {
+ *fb_base = (grub_uint32_t) (grub_target_addr_t) base;
+ *line_len = j << 2;
+
+ return 1;
+ }
+ }
+
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int
+find_framebuf (grub_uint32_t *fb_base, grub_uint32_t *line_len)
+{
+ int found = 0;
+
+ auto int NESTED_FUNC_ATTR find_card (int bus, int dev, int func,
+ grub_pci_id_t pciid);
+
+ int NESTED_FUNC_ATTR find_card (int bus, int dev, int func,
+ grub_pci_id_t pciid)
+ {
+ grub_pci_address_t addr;
+
+ addr = grub_pci_make_address (bus, dev, func, 2);
+ if (grub_pci_read (addr) >> 24 == 0x3)
+ {
+ int i;
+
+ grub_printf ("Display controller: %d:%d.%d\nDevice id: %x\n",
+ bus, dev, func, pciid);
+ addr += 8;
+ for (i = 0; i < 6; i++, addr += 4)
+ {
+ grub_uint32_t old_bar1, old_bar2, type;
+ grub_uint64_t base64;
+
+ old_bar1 = grub_pci_read (addr);
+ if ((! old_bar1) || (old_bar1 & GRUB_PCI_ADDR_SPACE_IO))
+ continue;
+
+ type = old_bar1 & GRUB_PCI_ADDR_MEM_TYPE_MASK;
+ if (type == GRUB_PCI_ADDR_MEM_TYPE_64)
+ {
+ if (i == 5)
+ break;
+
+ old_bar2 = grub_pci_read (addr + 4);
+ }
+ else
+ old_bar2 = 0;
+
+ base64 = old_bar2;
+ base64 <<= 32;
+ base64 |= (old_bar1 & GRUB_PCI_ADDR_MEM_MASK);
+
+ grub_printf ("%s(%d): 0x%llx\n",
+ ((old_bar1 & GRUB_PCI_ADDR_MEM_PREFETCH) ?
+ "VMEM" : "MMIO"), i,
+ (unsigned long long) base64);
+
+ if ((old_bar1 & GRUB_PCI_ADDR_MEM_PREFETCH) && (! found))
+ {
+ *fb_base = base64;
+ if (find_line_len (fb_base, line_len))
+ found++;
+ }
+
+ if (type == GRUB_PCI_ADDR_MEM_TYPE_64)
+ {
+ i++;
+ addr += 4;
+ }
+ }
+ }
+
+ return found;
+ }
+
+ grub_pci_iterate (find_card);
+ return found;
+}
+
+grub_err_t
+grub_xnu_set_video (struct grub_xnu_boot_params *params)
+{
+ grub_efi_uga_draw_protocol_t *c;
+ grub_uint32_t width, height, depth, rate, pixel, fb_base, line_len;
+ int ret;
+
+ c = grub_efi_locate_protocol (&uga_draw_guid, 0);
+ if (! c)
+ return grub_error (GRUB_ERR_IO, "Couldn't find UGADraw");
+
+ if (efi_call_5 (c->get_mode, c, &width, &height, &depth, &rate))
+ return grub_error (GRUB_ERR_IO, "Couldn't retrieve video mode");
+
+ grub_printf ("Video mode: %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_base = fb_base;
+ return GRUB_ERR_NONE;
+}
diff --git a/loader/i386/pc/xnu.c b/loader/i386/pc/xnu.c
new file mode 100644
index 0000000..2c78b2b
--- /dev/null
+++ b/loader/i386/pc/xnu.c
@@ -0,0 +1,67 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/env.h>
+#include <grub/misc.h>
+#include <grub/xnu.h>
+#include <grub/cpu/xnu.h>
+#include <grub/machine/vbe.h>
+#include <grub/machine/vga.h>
+
+/* Setup video for xnu. */
+
+grub_err_t
+grub_xnu_set_video (struct grub_xnu_boot_params *bootparams_relloc)
+{
+ grub_uint32_t use_mode = GRUB_VBE_DEFAULT_VIDEO_MODE;
+ char *modevar;
+ grub_err_t err;
+ struct grub_vbe_mode_info_block mode_info;
+
+ /* Check existence of vbe_mode environment variable. */
+ modevar = grub_env_get ("vbe_mode");
+ if (modevar != 0)
+ {
+ unsigned long value;
+
+ value = grub_strtoul (modevar, 0, 0);
+ if (grub_errno == GRUB_ERR_NONE)
+ use_mode = value;
+ else
+ grub_errno = GRUB_ERR_NONE;
+ }
+
+ if (use_mode < 0x100)
+ return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+ "booting xnu in text mode isn't supported yet");
+ err = grub_vbe_set_video_mode (use_mode, &mode_info);
+ if (err != GRUB_ERR_NONE)
+ return err;
+
+ /* FIXME: setting non-32bit color depth results in strange screens. */
+ if (use_mode >= 0x100)
+ {
+ bootparams_relloc->lfb_base = mode_info.phys_base_addr;
+ bootparams_relloc->lfb_mode = GRUB_XNU_VIDEO_TEXT_IN_VIDEO;
+ bootparams_relloc->lfb_line_len = mode_info.bytes_per_scan_line;
+ bootparams_relloc->lfb_width = mode_info.x_resolution;
+ bootparams_relloc->lfb_height = mode_info.y_resolution;
+ bootparams_relloc->lfb_depth = mode_info.bits_per_pixel;
+ }
+ return GRUB_ERR_NONE;
+}
diff --git a/loader/i386/xnu.c b/loader/i386/xnu.c
new file mode 100644
index 0000000..487b1d1
--- /dev/null
+++ b/loader/i386/xnu.c
@@ -0,0 +1,471 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/env.h>
+#include <grub/file.h>
+#include <grub/disk.h>
+#include <grub/xnu.h>
+#include <grub/cpu/xnu.h>
+#include <grub/mm.h>
+#include <grub/loader.h>
+#include <grub/cpu/loader.h>
+#include <grub/autoefi.h>
+#include <grub/i386/tsc.h>
+#include <grub/i386/pit.h>
+#include <grub/misc.h>
+
+char grub_xnu_cmdline[1024];
+
+/* Aliases set for some tables. */
+struct tbl_alias
+{
+ grub_efi_guid_t guid;
+ char *name;
+};
+
+struct tbl_alias table_aliases[] =
+ {
+ {GRUB_EFI_ACPI_20_TABLE_GUID, "ACPI_20"},
+ {GRUB_EFI_ACPI_TABLE_GUID, "ACPI"},
+ };
+
+/* The following function is used to be able to debug xnu loader
+ with grub-emu. */
+#ifdef GRUB_UTIL
+grub_err_t
+grub_xnu_launch (void)
+{
+ grub_printf ("Fake launch %x:%p:%p", grub_xnu_entry_point, grub_xnu_arg1,
+ grub_xnu_stack);
+ grub_getkey ();
+ return 0;
+}
+#endif
+
+static int
+utf16_strlen (grub_uint16_t *in)
+{
+ int i;
+ for (i = 0; in[i]; i++);
+ return i;
+}
+
+/* Read frequency from a string in MHz and return it in Hz. */
+static grub_uint64_t
+readfrequency (const char *str)
+{
+ grub_uint64_t num = 0;
+ int mul = 1000000;
+ int found = 0;
+
+ while (*str)
+ {
+ unsigned long digit;
+
+ digit = grub_tolower (*str) - '0';
+ if (digit > 9)
+ break;
+
+ found = 1;
+
+ num = num * 10 + digit;
+ str++;
+ }
+ num *= 1000000;
+ if (*str == '.')
+ {
+ str++;
+ while (*str)
+ {
+ unsigned long digit;
+
+ digit = grub_tolower (*str) - '0';
+ if (digit > 9)
+ break;
+
+ found = 1;
+
+ mul /= 10;
+ num = num + mul * digit;
+ str++;
+ }
+ }
+ if (! found)
+ return 0;
+
+ return num;
+}
+
+/* FIXME: autodetect FSB. */
+static grub_uint64_t
+guessfsb (void)
+{
+ const grub_uint64_t sane_value = 100000000;
+ return sane_value;
+}
+
+/* Fill device tree. */
+/* FIXME: some entries may be platform-agnostic. Move them to loader/xnu.c. */
+grub_err_t
+grub_cpu_xnu_fill_devicetree (void)
+{
+ struct grub_xnu_devtree_key *efikey;
+ struct grub_xnu_devtree_key *cfgtablekey;
+ struct grub_xnu_devtree_key *curval;
+ struct grub_xnu_devtree_key *runtimesrvkey;
+ struct grub_xnu_devtree_key *platformkey;
+ unsigned i, j;
+
+ /* The value "model". */
+ /* FIXME: may this value be sometimes different? */
+ curval = grub_xnu_create_value (&grub_xnu_devtree_root, "model");
+ if (! curval)
+ return grub_errno;
+ curval->datasize = sizeof ("ACPI");
+ curval->data = grub_strdup ("ACPI");
+ curval = grub_xnu_create_value (&grub_xnu_devtree_root, "compatible");
+ if (! curval)
+ return grub_errno;
+ curval->datasize = sizeof ("ACPI");
+ curval->data = grub_strdup ("ACPI");
+
+ /* The key "efi". */
+ efikey = grub_xnu_create_key (&grub_xnu_devtree_root, "efi");
+ if (! efikey)
+ return grub_errno;
+
+ /* Information about firmware. */
+ curval = grub_xnu_create_value (&(efikey->first_child), "firmware-revision");
+ if (! curval)
+ return grub_errno;
+ curval->datasize = (SYSTEM_TABLE_SIZEOF (firmware_revision));
+ curval->data = grub_malloc (curval->datasize);
+ if (! curval->data)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't create device tree");
+ grub_memcpy (curval->data, (SYSTEM_TABLE_VAR(firmware_revision)),
+ curval->datasize);
+
+ curval = grub_xnu_create_value (&(efikey->first_child), "firmware-vendor");
+ if (! curval)
+ return grub_errno;
+ curval->datasize =
+ 2 * (utf16_strlen (SYSTEM_TABLE_PTR (firmware_vendor)) + 1);
+ curval->data = grub_malloc (curval->datasize);
+ if (! curval->data)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't create device tree");
+ grub_memcpy (curval->data, SYSTEM_TABLE_PTR (firmware_vendor),
+ curval->datasize);
+
+ curval = grub_xnu_create_value (&(efikey->first_child), "firmware-abi");
+ if (! curval)
+ return grub_errno;
+ curval->datasize = sizeof ("EFI32");
+ curval->data = grub_malloc (curval->datasize);
+ if (! curval->data)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't create device tree");
+ if (SIZEOF_OF_UINTN == 4)
+ grub_memcpy (curval->data, "EFI32", curval->datasize);
+ else
+ grub_memcpy (curval->data, "EFI64", curval->datasize);
+
+ /* The key "platform". */
+ platformkey = grub_xnu_create_key (&(efikey->first_child),
+ "platform");
+ if (! platformkey)
+ return grub_errno;
+
+ /* Pass FSB frequency to the kernel. */
+ curval = grub_xnu_create_value (&(platformkey->first_child), "FSBFrequency");
+ if (! curval)
+ return grub_errno;
+ curval->datasize = sizeof (grub_uint64_t);
+ curval->data = grub_malloc (curval->datasize);
+ if (!curval->data)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't create device tree");
+
+ /* First see if user supplies the value. */
+ char *fsbvar = grub_env_get ("fsb");
+ if (! fsbvar)
+ *((grub_uint64_t *) curval->data) = 0;
+ else
+ *((grub_uint64_t *) curval->data) = readfrequency (fsbvar);
+ /* Try autodetect. */
+ if (! *((grub_uint64_t *) curval->data))
+ *((grub_uint64_t *) curval->data) = guessfsb ();
+
+ cfgtablekey = grub_xnu_create_key (&(efikey->first_child),
+ "configuration-table");
+ if (!cfgtablekey)
+ return grub_errno;
+
+ /* Fill "configuration-table" key. */
+ for (i = 0; i < SYSTEM_TABLE (num_table_entries); i++)
+ {
+ void *ptr;
+ struct grub_xnu_devtree_key *curkey;
+ grub_efi_guid_t guid;
+ char guidbuf[64];
+
+ /* Retrieve current key. */
+#ifdef GRUB_MACHINE_EFI
+ {
+ ptr = (void *)
+ grub_efi_system_table->configuration_table[i].vendor_table;
+ guid = grub_efi_system_table->configuration_table[i].vendor_guid;
+ }
+#else
+ if (SIZEOF_OF_UINTN == 4)
+ {
+ ptr = UINT_TO_PTR (((grub_efiemu_configuration_table32_t *)
+ SYSTEM_TABLE_PTR (configuration_table))[i]
+ .vendor_table);
+ guid =
+ ((grub_efiemu_configuration_table32_t *)
+ SYSTEM_TABLE_PTR (configuration_table))[i].vendor_guid;
+ }
+ else
+ {
+ ptr = UINT_TO_PTR (((grub_efiemu_configuration_table64_t *)
+ SYSTEM_TABLE_PTR (configuration_table))[i]
+ .vendor_table);
+ guid =
+ ((grub_efiemu_configuration_table64_t *)
+ SYSTEM_TABLE_PTR (configuration_table))[i].vendor_guid;
+ }
+#endif
+
+ /* The name of key for new table. */
+ grub_sprintf (guidbuf, "%08x-%04x-%04x-%02x%02x-",
+ guid.data1, guid.data2, guid.data3, guid.data4[0],
+ guid.data4[1]);
+ for (j = 2; j < 8; j++)
+ grub_sprintf (guidbuf + grub_strlen (guidbuf), "%02x", guid.data4[j]);
+ /* For some reason GUID has to be in uppercase. */
+ for (j = 0; guidbuf[j] ; j++)
+ if (guidbuf[j] >= 'a' && guidbuf[j] <= 'f')
+ guidbuf[j] += 'A' - 'a';
+ curkey = grub_xnu_create_key (&(cfgtablekey->first_child), guidbuf);
+ if (! curkey)
+ return grub_errno;
+
+ curval = grub_xnu_create_value (&(curkey->first_child), "guid");
+ if (! curval)
+ return grub_errno;
+ curval->datasize = sizeof (guid);
+ curval->data = grub_malloc (curval->datasize);
+ if (! curval->data)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "couldn't create device tree");
+ grub_memcpy (curval->data, &guid, curval->datasize);
+
+ /* The value "table". */
+ curval = grub_xnu_create_value (&(curkey->first_child), "table");
+ if (! curval)
+ return grub_errno;
+ curval->datasize = SIZEOF_OF_UINTN;
+ curval->data = grub_malloc (curval->datasize);
+ if (! curval->data)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "couldn't create device tree");
+ if (SIZEOF_OF_UINTN == 4)
+ *((grub_uint32_t *)curval->data) = PTR_TO_UINT32 (ptr);
+ else
+ *((grub_uint64_t *)curval->data) = PTR_TO_UINT64 (ptr);
+
+ /* Create alias. */
+ for (j = 0; j < sizeof (table_aliases) / sizeof (table_aliases[0]); j++)
+ if (grub_memcmp (&table_aliases[j].guid, &guid, sizeof (guid)) == 0)
+ break;
+ if (j != sizeof (table_aliases) / sizeof (table_aliases[0]))
+ {
+ curval = grub_xnu_create_value (&(curkey->first_child), "alias");
+ if (!curval)
+ return grub_errno;
+ curval->datasize = grub_strlen (table_aliases[j].name) + 1;
+ curval->data = grub_malloc (curval->datasize);
+ if (!curval->data)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "couldn't create device tree");
+ grub_memcpy (curval->data, table_aliases[j].name, curval->datasize);
+ }
+ }
+
+ /* Create and fill "runtime-services" key. */
+ runtimesrvkey = grub_xnu_create_key (&(efikey->first_child),
+ "runtime-services");
+ if (! runtimesrvkey)
+ return grub_errno;
+ curval = grub_xnu_create_value (&(runtimesrvkey->first_child), "table");
+ if (! curval)
+ return grub_errno;
+ curval->datasize = SIZEOF_OF_UINTN;
+ curval->data = grub_malloc (curval->datasize);
+ if (! curval->data)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "couldn't create device tree");
+ if (SIZEOF_OF_UINTN == 4)
+ *((grub_uint32_t *) curval->data)
+ = PTR_TO_UINT32 (SYSTEM_TABLE_PTR (runtime_services));
+ else
+ *((grub_uint64_t *) curval->data)
+ = PTR_TO_UINT64 (SYSTEM_TABLE_PTR (runtime_services));
+
+ return GRUB_ERR_NONE;
+}
+
+/* Boot xnu. */
+grub_err_t
+grub_xnu_boot (void)
+{
+ struct grub_xnu_boot_params *bootparams_relloc;
+ grub_off_t bootparams_relloc_off;
+ grub_off_t mmap_relloc_off;
+ grub_err_t err;
+ grub_efi_uintn_t memory_map_size = 0;
+ grub_efi_memory_descriptor_t *memory_map;
+ grub_efi_uintn_t map_key = 0;
+ grub_efi_uintn_t descriptor_size = 0;
+ grub_efi_uint32_t descriptor_version = 0;
+ grub_uint64_t firstruntimeaddr, lastruntimeaddr;
+ void *devtree;
+ grub_size_t devtreelen;
+ int i;
+
+ /* Page-align to avoid following parts to be inadvertently freed. */
+ if ((err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE)))
+ return err;
+
+ /* Pass memory map to kernel. */
+ memory_map_size = 0;
+ memory_map = 0;
+ map_key = 0;
+ descriptor_size = 0;
+ descriptor_version = 0;
+
+ if (grub_autoefi_get_memory_map (&memory_map_size, memory_map,
+ &map_key, &descriptor_size,
+ &descriptor_version) < 0)
+ return grub_errno;
+
+ memory_map = grub_xnu_heap_malloc (memory_map_size);
+ if (! memory_map)
+ return grub_errno;
+
+ if (grub_autoefi_get_memory_map (&memory_map_size, memory_map,
+ &map_key, &descriptor_size,
+ &descriptor_version) <= 0)
+ return grub_errno;
+ mmap_relloc_off = (grub_uint8_t *) memory_map
+ - (grub_uint8_t *) grub_xnu_heap_start;
+
+ firstruntimeaddr = (grub_uint64_t) (-1);
+ lastruntimeaddr = 0;
+ for (i = 0; (unsigned) i < memory_map_size / descriptor_size; i++)
+ {
+ grub_efi_memory_descriptor_t *curdesc = (grub_efi_memory_descriptor_t *)
+ ((char *) memory_map + descriptor_size * i);
+
+ /* Some EFI implementations set physical_start to 0 which
+ causes XNU crash. */
+ curdesc->virtual_start = curdesc->physical_start;
+
+ if (curdesc->type == GRUB_EFI_RUNTIME_SERVICES_DATA
+ || curdesc->type == GRUB_EFI_RUNTIME_SERVICES_CODE)
+ {
+ if (firstruntimeaddr > curdesc->physical_start)
+ firstruntimeaddr = curdesc->physical_start;
+ if (lastruntimeaddr < curdesc->physical_start
+ + curdesc->num_pages * 4096)
+ lastruntimeaddr = curdesc->physical_start
+ + curdesc->num_pages * 4096;
+ }
+ }
+
+ /* Relocate the boot parameters to heap. */
+ bootparams_relloc = grub_xnu_heap_malloc (sizeof (*bootparams_relloc));
+ if (! bootparams_relloc)
+ return grub_errno;
+ bootparams_relloc_off = (grub_uint8_t *) bootparams_relloc
+ - (grub_uint8_t *) grub_xnu_heap_start;
+ if ((err = grub_xnu_writetree_toheap (&devtree, &devtreelen)))
+ return err;
+ bootparams_relloc = (struct grub_xnu_boot_params *)
+ (bootparams_relloc_off + (grub_uint8_t *) grub_xnu_heap_start);
+
+ grub_memcpy (bootparams_relloc->cmdline, grub_xnu_cmdline,
+ sizeof (bootparams_relloc->cmdline));
+
+ bootparams_relloc->devtree = PTR_TO_UINT32 (devtree);
+ bootparams_relloc->devtreelen = devtreelen;
+
+ bootparams_relloc->heap_start = PTR_TO_UINT32 (grub_xnu_heap_start);
+ bootparams_relloc->heap_size = grub_xnu_heap_size;
+
+ bootparams_relloc->efi_mmap
+ = PTR_TO_UINT32 ((grub_uint8_t *)grub_xnu_heap_start + mmap_relloc_off);
+ bootparams_relloc->efi_mmap_size = memory_map_size;
+ bootparams_relloc->efi_mem_desc_size = descriptor_size;
+ bootparams_relloc->efi_mem_desc_version = descriptor_version;
+
+ bootparams_relloc->efi_runtime_first_page = firstruntimeaddr
+ / GRUB_XNU_PAGESIZE;
+ bootparams_relloc->efi_runtime_npages
+ = ((lastruntimeaddr + GRUB_XNU_PAGESIZE - 1) / GRUB_XNU_PAGESIZE)
+ - (firstruntimeaddr / GRUB_XNU_PAGESIZE);
+ bootparams_relloc->efi_uintnbits = SIZEOF_OF_UINTN * 8;
+ bootparams_relloc->efi_system_table
+ = PTR_TO_UINT32 (grub_autoefi_system_table);
+
+ bootparams_relloc->verminor = GRUB_XNU_BOOTARGS_VERMINOR;
+ bootparams_relloc->vermajor = GRUB_XNU_BOOTARGS_VERMAJOR;
+
+ /* Set video. */
+ err = grub_xnu_set_video (bootparams_relloc);
+ if (err != GRUB_ERR_NONE)
+ {
+ grub_print_error ();
+ grub_errno = GRUB_ERR_NONE;
+ grub_printf ("Using fake framebuffer\n");
+
+ /* Setup pseudo 800x600x32 framebuffer. */
+ bootparams_relloc->lfb_mode = GRUB_XNU_VIDEO_TEXT_IN_VIDEO;
+ bootparams_relloc->lfb_width = 800;
+ bootparams_relloc->lfb_height = 600;
+ bootparams_relloc->lfb_depth = 32;
+ bootparams_relloc->lfb_line_len = 3200;
+ bootparams_relloc->lfb_base = bootparams_relloc->heap_size
+ + grub_xnu_heap_will_be_at;
+ bootparams_relloc->heap_size += 1920000;
+ }
+
+ /* Parameters for asm helper. */
+ grub_xnu_stack = bootparams_relloc->heap_start
+ + bootparams_relloc->heap_size + GRUB_XNU_PAGESIZE;
+ grub_xnu_arg1 = (long) bootparams_relloc;
+ grub_xnu_entry_point = PTR_TO_UINT32
+ (grub_xnu_heap_start + grub_xnu_entry_point);
+ grub_dprintf ("xnu", "eip=%x\n", grub_xnu_entry_point);
+ grub_dprintf ("xnu", "launch=%p\n", grub_xnu_launch);
+
+ grub_autoefi_finish_boot_services ();
+
+ grub_xnu_launch ();
+
+ /* Never reaches here. */
+ return 0;
+}
diff --git a/loader/i386/xnu_helper.S b/loader/i386/xnu_helper.S
new file mode 100644
index 0000000..0a1c129
--- /dev/null
+++ b/loader/i386/xnu_helper.S
@@ -0,0 +1,88 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/symbol.h>
+
+ .p2align 2 /* force 4-byte alignment */
+
+FUNCTION(grub_xnu_launch)
+ cli
+
+#ifdef __x86_64__
+ /* Switch to compatibility mode. */
+
+ lgdt gdtdesc
+
+ jmp cont1
+cont1:
+ .code32
+
+ /* Disable paging. */
+ mov %cr0, %eax
+ and $0x7fffffff, %eax
+ mov %eax, %cr0
+
+ /* Disable amd64. */
+ mov $0xc0000080, %ecx
+ rdmsr
+ and $0xfffffeff, %eax
+ wrmsr
+
+ jmp cont2
+cont2:
+#endif
+
+ .code32
+
+ /* Registers on XNU boot: eip, esp and eax. */
+ /* mov imm32, %ecx */
+ .byte 0xb9
+VARIABLE (grub_xnu_entry_point)
+ .long 0
+ /* mov imm32, %eax */
+ .byte 0xb8
+VARIABLE (grub_xnu_arg1)
+ .long 0
+ /* mov imm32, %ebx */
+ .byte 0xbb
+VARIABLE (grub_xnu_stack)
+ .long 0
+
+ movl %ebx, %esp
+ jmp *%ecx
+
+#ifdef __x86_64__
+ /* GDT. Copied from loader/i386/linux.c. */
+ .p2align 4
+gdt:
+ /* NULL. */
+ .byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+
+ /* Reserved. */
+ .byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+
+ /* Code segment. */
+ .byte 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x9A, 0xCF, 0x00
+
+ /* Data segment. */
+ .byte 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x92, 0xCF, 0x00
+
+gdtdesc:
+ .word 32
+ .quad gdt
+#endif
\ No newline at end of file
diff --git a/loader/macho.c b/loader/macho.c
new file mode 100644
index 0000000..da081a2
--- /dev/null
+++ b/loader/macho.c
@@ -0,0 +1,395 @@
+/* macho.c - load Mach-O files. */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* This Mach-O loader is incomplete and can load only non-relocatable segments.
+ This is however enough to boot xnu (otool -l and Mach-O specs for more info).
+*/
+
+#include <grub/err.h>
+#include <grub/macho.h>
+#include <grub/cpu/macho.h>
+#include <grub/machoload.h>
+#include <grub/file.h>
+#include <grub/gzio.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+
+#define min(a,b) (((a) < (b)) ? (a) : (b))
+
+/* 32-bit. */
+
+int
+grub_macho_contains_macho32 (grub_macho_t macho)
+{
+ return macho->offset32 != -1;
+}
+
+static void
+grub_macho_parse32 (grub_macho_t macho)
+{
+ struct grub_macho_header32 head;
+
+ /* Is there any candidate at all? */
+ if (macho->offset32 == -1)
+ return;
+
+ /* Read header and check magic*/
+ if (grub_file_seek (macho->file, macho->offset32) == (grub_off_t) -1
+ || grub_file_read (macho->file, (char *) &head, sizeof (head))
+ != sizeof(head))
+ {
+ grub_error (GRUB_ERR_READ_ERROR, "Cannot read Mach-O header.");
+ macho->offset32 = -1;
+ return;
+ }
+ if (head.magic != GRUB_MACHO_MAGIC32)
+ {
+ grub_error (GRUB_ERR_BAD_OS, "Invalid Mach-O 32-bit header.");
+ macho->offset32 = -1;
+ return;
+ }
+
+ /* Read commands. */
+ macho->ncmds32 = head.ncmds;
+ macho->cmdsize32 = head.sizeofcmds;
+ macho->cmds32 = grub_malloc(macho->cmdsize32);
+ if (! macho->cmds32)
+ {
+ grub_error (GRUB_ERR_OUT_OF_MEMORY, "not enough memory to read commands");
+ return;
+ }
+ if (grub_file_read (macho->file, (char *) macho->cmds32,
+ (grub_size_t) macho->cmdsize32)
+ != (grub_ssize_t) macho->cmdsize32)
+ {
+ grub_error (GRUB_ERR_READ_ERROR, "Cannot read Mach-O header.");
+ macho->offset32 = -1;
+ }
+}
+
+typedef int NESTED_FUNC_ATTR (*grub_macho_iter_hook_t)
+(grub_macho_t , struct grub_macho_cmd *,
+ void *);
+
+static grub_err_t
+grub_macho32_cmds_iterate (grub_macho_t macho,
+ grub_macho_iter_hook_t hook,
+ void *hook_arg)
+{
+ grub_uint8_t *hdrs = macho->cmds32;
+ int i;
+ if (! macho->cmds32)
+ return grub_error (GRUB_ERR_BAD_OS, "Couldn't find 32-bit Mach-O");
+ for (i = 0; i < macho->ncmds32; i++)
+ {
+ struct grub_macho_cmd *hdr = (struct grub_macho_cmd *) hdrs;
+ if (hook (macho, hdr, hook_arg))
+ break;
+ hdrs += hdr->cmdsize;
+ }
+
+ return grub_errno;
+}
+
+grub_size_t
+grub_macho32_filesize (grub_macho_t macho)
+{
+ if (grub_macho_contains_macho32 (macho))
+ return macho->end32 - macho->offset32;
+ return 0;
+}
+
+grub_err_t
+grub_macho32_readfile (grub_macho_t macho, void *dest)
+{
+ grub_ssize_t read;
+ if (! grub_macho_contains_macho32 (macho))
+ return grub_error (GRUB_ERR_BAD_OS,
+ "Couldn't read arcitecture-specific part");
+
+ if (grub_file_seek (macho->file, macho->offset32) == (grub_off_t) -1)
+ {
+ grub_error_push ();
+ return grub_error (GRUB_ERR_BAD_OS,
+ "Invalid offset in program header.");
+ }
+
+ read = grub_file_read (macho->file, dest,
+ macho->end32 - macho->offset32);
+ if (read != (grub_ssize_t) (macho->end32 - macho->offset32))
+ {
+ grub_error_push ();
+ return grub_error (GRUB_ERR_BAD_OS,
+ "Couldn't read arcitecture-specific part");
+ }
+ return GRUB_ERR_NONE;
+}
+
+/* Calculate the amount of memory spanned by the segments. */
+grub_err_t
+grub_macho32_size (grub_macho_t macho, grub_addr_t *segments_start,
+ grub_addr_t *segments_end, int flags)
+{
+ int nr_phdrs = 0;
+
+ /* Run through the program headers to calculate the total memory size we
+ should claim. */
+ auto int NESTED_FUNC_ATTR calcsize (grub_macho_t _macho,
+ struct grub_macho_cmd *phdr, void *_arg);
+ int NESTED_FUNC_ATTR calcsize (grub_macho_t UNUSED _macho,
+ struct grub_macho_cmd *hdr0, void UNUSED *_arg)
+ {
+ struct grub_macho_segment32 *hdr = (struct grub_macho_segment32 *) hdr0;
+ if (hdr->cmd != GRUB_MACHO_CMD_SEGMENT32)
+ return 0;
+ if (! hdr->filesize && (flags & GRUB_MACHO_NOBSS))
+ return 0;
+
+ nr_phdrs++;
+ if (hdr->vmaddr < *segments_start)
+ *segments_start = hdr->vmaddr;
+ if (hdr->vmaddr + hdr->vmsize > *segments_end)
+ *segments_end = hdr->vmaddr + hdr->vmsize;
+ return 0;
+ }
+
+ *segments_start = (grub_uint32_t) -1;
+ *segments_end = 0;
+
+ grub_macho32_cmds_iterate (macho, calcsize, 0);
+
+ if (nr_phdrs == 0)
+ return grub_error (GRUB_ERR_BAD_OS, "No program headers present");
+
+ if (*segments_end < *segments_start)
+ /* Very bad addresses. */
+ return grub_error (GRUB_ERR_BAD_OS, "Bad program header load addresses");
+
+ return GRUB_ERR_NONE;
+}
+
+/* Load every loadable segment into memory specified by `_load_hook'. */
+grub_err_t
+grub_macho32_load (grub_macho_t macho, char *offset, int flags)
+{
+ grub_err_t err = 0;
+ auto int NESTED_FUNC_ATTR do_load(grub_macho_t _macho,
+ struct grub_macho_cmd *hdr0,
+ void UNUSED *_arg);
+ int NESTED_FUNC_ATTR do_load(grub_macho_t _macho,
+ struct grub_macho_cmd *hdr0,
+ void UNUSED *_arg)
+ {
+ struct grub_macho_segment32 *hdr = (struct grub_macho_segment32 *) hdr0;
+
+ if (hdr->cmd != GRUB_MACHO_CMD_SEGMENT32)
+ return 0;
+
+ if (! hdr->filesize && (flags & GRUB_MACHO_NOBSS))
+ return 0;
+ if (! hdr->vmsize)
+ return 0;
+
+ if (grub_file_seek (_macho->file, hdr->fileoff
+ + _macho->offset32) == (grub_off_t) -1)
+ {
+ grub_error_push ();
+ grub_error (GRUB_ERR_BAD_OS,
+ "Invalid offset in program header.");
+ return 1;
+ }
+
+ if (hdr->filesize)
+ {
+ grub_ssize_t read;
+ read = grub_file_read (_macho->file, offset + hdr->vmaddr,
+ min (hdr->filesize, hdr->vmsize));
+ if (read != (grub_ssize_t) min (hdr->filesize, hdr->vmsize))
+ {
+ /* XXX How can we free memory from `load_hook'? */
+ grub_error_push ();
+ err=grub_error (GRUB_ERR_BAD_OS,
+ "Couldn't read segment from file: "
+ "wanted 0x%lx bytes; read 0x%lx bytes.",
+ hdr->filesize, read);
+ return 1;
+ }
+ }
+
+ if (hdr->filesize < hdr->vmsize)
+ grub_memset (offset + hdr->vmaddr + hdr->filesize,
+ 0, hdr->vmsize - hdr->filesize);
+ return 0;
+ }
+
+ grub_macho32_cmds_iterate (macho, do_load, 0);
+
+ return err;
+}
+
+grub_uint32_t
+grub_macho32_get_entry_point (grub_macho_t macho)
+{
+ grub_uint32_t entry_point = 0;
+ auto int NESTED_FUNC_ATTR hook(grub_macho_t _macho,
+ struct grub_macho_cmd *hdr,
+ void UNUSED *_arg);
+ int NESTED_FUNC_ATTR hook(grub_macho_t UNUSED _macho,
+ struct grub_macho_cmd *hdr,
+ void UNUSED *_arg)
+ {
+ if (hdr->cmd == GRUB_MACHO_CMD_THREAD)
+ entry_point = ((struct grub_macho_thread32 *) hdr)->entry_point;
+ return 0;
+ }
+ grub_macho32_cmds_iterate (macho, hook, 0);
+ return entry_point;
+}
+
+
+grub_err_t
+grub_macho_close (grub_macho_t macho)
+{
+ grub_file_t file = macho->file;
+
+ grub_free (macho->cmds32);
+ grub_free (macho->cmds64);
+
+ grub_free (macho);
+
+ if (file)
+ grub_file_close (file);
+
+ return grub_errno;
+}
+
+grub_macho_t
+grub_macho_file (grub_file_t file)
+{
+ grub_macho_t macho;
+ union grub_macho_filestart filestart;
+
+ macho = grub_malloc (sizeof (*macho));
+ if (! macho)
+ return 0;
+
+ macho->file = file;
+ macho->offset32 = -1;
+ macho->offset64 = -1;
+ macho->end32 = -1;
+ macho->end64 = -1;
+ macho->cmds32 = 0;
+ macho->cmds64 = 0;
+
+ if (grub_file_seek (macho->file, 0) == (grub_off_t) -1)
+ goto fail;
+
+ if (grub_file_read (macho->file, (char *) &filestart, sizeof (filestart))
+ != sizeof (filestart))
+ {
+ grub_error_push ();
+ grub_error (GRUB_ERR_READ_ERROR, "Cannot read Mach-O header.");
+ goto fail;
+ }
+
+ /* Is it a fat file? */
+ if (filestart.fat.magic == grub_be_to_cpu32 (GRUB_MACHO_FAT_MAGIC))
+ {
+ struct grub_macho_fat_arch *archs;
+ int i, narchs;
+
+ /* Load architecture description. */
+ narchs = grub_be_to_cpu32 (filestart.fat.nfat_arch);
+ if (grub_file_seek (macho->file, sizeof (struct grub_macho_fat_header))
+ == (grub_off_t) -1)
+ goto fail;
+ archs = grub_malloc (sizeof (struct grub_macho_fat_arch) * narchs);
+ if (!archs)
+ goto fail;
+ if (grub_file_read (macho->file, (char *) archs,
+ sizeof (struct grub_macho_fat_arch) * narchs)
+ != (grub_ssize_t)sizeof(struct grub_macho_fat_arch) * narchs)
+ {
+ grub_free (archs);
+ grub_error_push ();
+ grub_error (GRUB_ERR_READ_ERROR, "Cannot read Mach-O header.");
+ goto fail;
+ }
+
+ for (i = 0; i < narchs; i++)
+ {
+ if (GRUB_MACHO_CPUTYPE_IS_HOST32
+ (grub_be_to_cpu32 (archs[i].cputype)))
+ {
+ macho->offset32 = grub_be_to_cpu32 (archs[i].offset);
+ macho->end32 = grub_be_to_cpu32 (archs[i].offset)
+ + grub_be_to_cpu32 (archs[i].size);
+ }
+ if (GRUB_MACHO_CPUTYPE_IS_HOST64
+ (grub_be_to_cpu32 (archs[i].cputype)))
+ {
+ macho->offset64 = grub_be_to_cpu32 (archs[i].offset);
+ macho->end64 = grub_be_to_cpu32 (archs[i].offset)
+ + grub_be_to_cpu32 (archs[i].size);
+ }
+ }
+ grub_free (archs);
+ }
+
+ /* Is it a thin 32-bit file? */
+ if (filestart.thin32.magic == GRUB_MACHO_MAGIC32)
+ {
+ macho->offset32 = 0;
+ macho->end32 = grub_file_size (file);
+ }
+
+ /* Is it a thin 64-bit file? */
+ if (filestart.thin64.magic == GRUB_MACHO_MAGIC64)
+ {
+ macho->offset64 = 0;
+ macho->end64 = grub_file_size (file);
+ }
+
+ grub_macho_parse32 (macho);
+ /* FIXME: implement 64-bit.*/
+ /* grub_macho_parse64 (macho); */
+
+ return macho;
+
+fail:
+ grub_macho_close (macho);
+ return 0;
+}
+
+grub_macho_t
+grub_macho_open (const char *name)
+{
+ grub_file_t file;
+ grub_macho_t macho;
+
+ file = grub_gzfile_open (name, 1);
+ if (! file)
+ return 0;
+
+ macho = grub_macho_file (file);
+ if (! macho)
+ grub_file_close (file);
+
+ return macho;
+}
diff --git a/loader/xnu.c b/loader/xnu.c
new file mode 100644
index 0000000..6a4e0f5
--- /dev/null
+++ b/loader/xnu.c
@@ -0,0 +1,1376 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/file.h>
+#include <grub/xnu.h>
+#include <grub/cpu/xnu.h>
+#include <grub/mm.h>
+#include <grub/dl.h>
+#include <grub/loader.h>
+#include <grub/cpu/loader.h>
+#include <grub/machoload.h>
+#include <grub/macho.h>
+#include <grub/cpu/macho.h>
+#include <grub/gzio.h>
+#include <grub/command.h>
+#include <grub/misc.h>
+
+struct grub_xnu_devtree_key *grub_xnu_devtree_root = 0;
+static int driverspackagenum = 0;
+static int driversnum = 0;
+char *grub_xnu_heap_start = 0;
+grub_addr_t grub_xnu_heap_will_be_at = 0;
+grub_size_t grub_xnu_heap_size = 0;
+
+/* Allocate heap by 32MB-blocks. */
+#define GRUB_XNU_HEAP_ALLOC_BLOCK 0x2000000
+
+static grub_err_t
+grub_xnu_register_memory (char *prefix, int *suffix,
+ void *addr, grub_size_t size);
+void *
+grub_xnu_heap_malloc (int size)
+{
+ void *val;
+
+#if 0
+ /* This way booting is faster but less reliable.
+ Once we have advanced mm second way will be as fast as this one. */
+ val = grub_xnu_heap_start = (char *) 0x100000;
+#else
+ int oldblknum, newblknum;
+
+ /* The page after the heap is used for stack. Ensure it's usable. */
+ if (grub_xnu_heap_size)
+ oldblknum = (grub_xnu_heap_size + GRUB_XNU_PAGESIZE
+ + GRUB_XNU_HEAP_ALLOC_BLOCK - 1) / GRUB_XNU_HEAP_ALLOC_BLOCK;
+ else
+ oldblknum = 0;
+ newblknum = (grub_xnu_heap_size + size + GRUB_XNU_PAGESIZE
+ + GRUB_XNU_HEAP_ALLOC_BLOCK - 1) / GRUB_XNU_HEAP_ALLOC_BLOCK;
+ if (oldblknum != newblknum)
+ /* FIXME: instruct realloc to allocate at 1MB if possible once
+ advanced mm is ready. */
+ val = grub_realloc (grub_xnu_heap_start,
+ newblknum * GRUB_XNU_HEAP_ALLOC_BLOCK);
+ else
+ val = grub_xnu_heap_start;
+ if (! val)
+ {
+ grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "not enough space on xnu memory heap");
+ return 0;
+ }
+ grub_xnu_heap_start = val;
+#endif
+
+ val = (char *) grub_xnu_heap_start + grub_xnu_heap_size;
+ grub_xnu_heap_size += size;
+ grub_dprintf ("xnu", "val=%p\n", val);
+ return (char *) val;
+}
+
+/* Make sure next block of the heap will be aligned.
+ Please notice: aligned are pointers AFTER relocation
+ and not the current ones. */
+grub_err_t
+grub_xnu_align_heap (int align)
+{
+ int align_overhead = align - grub_xnu_heap_size % align;
+ if (align_overhead == align)
+ return GRUB_ERR_NONE;
+ if (! grub_xnu_heap_malloc (align_overhead))
+ return grub_errno;
+ return GRUB_ERR_NONE;
+}
+
+/* Free subtree pointed by CUR. */
+void
+grub_xnu_free_devtree (struct grub_xnu_devtree_key *cur)
+{
+ struct grub_xnu_devtree_key *d;
+ while (cur)
+ {
+ grub_free (cur->name);
+ if (cur->datasize == -1)
+ grub_xnu_free_devtree (cur->first_child);
+ else if (cur->data)
+ grub_free (cur->data);
+ d = cur->next;
+ grub_free (cur);
+ cur = d;
+ }
+}
+
+/* Compute the size of device tree in xnu format. */
+static grub_size_t
+grub_xnu_writetree_get_size (struct grub_xnu_devtree_key *start, char *name)
+{
+ grub_size_t ret;
+ struct grub_xnu_devtree_key *cur;
+
+ /* Key header. */
+ ret = 2 * sizeof (grub_uint32_t);
+
+ /* "name" value. */
+ ret += 32 + sizeof (grub_uint32_t)
+ + grub_strlen (name) + 4
+ - (grub_strlen (name) % 4);
+
+ for (cur = start; cur; cur = cur->next)
+ if (cur->datasize != -1)
+ {
+ int align_overhead;
+
+ align_overhead = 4 - (cur->datasize % 4);
+ if (align_overhead == 4)
+ align_overhead = 0;
+ ret += 32 + sizeof (grub_uint32_t) + cur->datasize + align_overhead;
+ }
+ else
+ ret += grub_xnu_writetree_get_size (cur->first_child, cur->name);
+ return ret;
+}
+
+/* Write devtree in XNU format at curptr assuming the head is named NAME.*/
+static void *
+grub_xnu_writetree_toheap_real (void *curptr,
+ struct grub_xnu_devtree_key *start, char *name)
+{
+ struct grub_xnu_devtree_key *cur;
+ int nkeys = 0, nvals = 0;
+ for (cur = start; cur; cur = cur->next)
+ {
+ if (cur->datasize == -1)
+ nkeys++;
+ else
+ nvals++;
+ }
+ /* For the name. */
+ nvals++;
+
+ *((grub_uint32_t *) curptr) = nvals;
+ curptr = ((grub_uint32_t *) curptr) + 1;
+ *((grub_uint32_t *) curptr) = nkeys;
+ curptr = ((grub_uint32_t *) curptr) + 1;
+
+ /* First comes "name" value. */
+ grub_memset (curptr, 0, 32);
+ grub_memcpy (curptr, "name", 4);
+ curptr = ((grub_uint8_t *) curptr) + 32;
+ *((grub_uint32_t *)curptr) = grub_strlen (name) + 1;
+ curptr = ((grub_uint32_t *) curptr) + 1;
+ grub_memcpy (curptr, name, grub_strlen (name));
+ curptr = ((grub_uint8_t *) curptr) + grub_strlen (name);
+ grub_memset (curptr, 0, 4 - (grub_strlen (name) % 4));
+ curptr = ((grub_uint8_t *) curptr) + (4 - (grub_strlen (name) % 4));
+
+ /* Then the other values. */
+ for (cur = start; cur; cur = cur->next)
+ if (cur->datasize != -1)
+ {
+ int align_overhead;
+
+ align_overhead = 4 - (cur->datasize % 4);
+ if (align_overhead == 4)
+ align_overhead = 0;
+ grub_memset (curptr, 0, 32);
+ grub_strncpy (curptr, cur->name, 31);
+ curptr = ((grub_uint8_t *) curptr) + 32;
+ *((grub_uint32_t *) curptr) = cur->datasize;
+ curptr = ((grub_uint32_t *) curptr) + 1;
+ grub_memcpy (curptr, cur->data, cur->datasize);
+ curptr = ((grub_uint8_t *) curptr) + cur->datasize;
+ grub_memset (curptr, 0, align_overhead);
+ curptr = ((grub_uint8_t *) curptr) + align_overhead;
+ }
+
+ /* And then the keys. Recursively use this function. */
+ for (cur = start; cur; cur = cur->next)
+ if (cur->datasize == -1)
+ if (!(curptr = grub_xnu_writetree_toheap_real (curptr,
+ cur->first_child,
+ cur->name)))
+ return 0;
+ return curptr;
+}
+
+grub_err_t
+grub_xnu_writetree_toheap (void **start, grub_size_t *size)
+{
+ struct grub_xnu_devtree_key *chosen;
+ struct grub_xnu_devtree_key *memorymap;
+ struct grub_xnu_devtree_key *driverkey, *cur;
+ struct grub_xnu_extdesc *extdesc;
+ grub_err_t err;
+
+ err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE);
+ if (err)
+ return err;
+
+ /* Device tree itself is in the memory map of device tree. */
+
+ /* Create a dummy value in memory-map. */
+ chosen = grub_xnu_create_key (&grub_xnu_devtree_root, "chosen");
+ if (! chosen)
+ return grub_errno;
+ memorymap = grub_xnu_create_key (&(chosen->first_child), "memory-map");
+ if (! memorymap)
+ return grub_errno;
+ driverkey = (struct grub_xnu_devtree_key *) grub_malloc (sizeof (*driverkey));
+ if (! driverkey)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't write device tree");
+ driverkey->name = grub_strdup ("DeviceTree");
+ if (! driverkey->name)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't write device tree");
+ driverkey->datasize = sizeof (*extdesc);
+ driverkey->next = memorymap->first_child;
+ memorymap->first_child = driverkey;
+ driverkey->data = extdesc
+ = (struct grub_xnu_extdesc *) grub_malloc (sizeof (*extdesc));
+ if (! driverkey->data)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't write device tree");
+
+ /* Allocate the space based on the size with dummy value. */
+ *size = grub_xnu_writetree_get_size (grub_xnu_devtree_root, "/");
+ *start = grub_xnu_heap_malloc (*size);
+
+ /* Put real data in the dummy. */
+ extdesc->addr = PTR_TO_UINT32 (*start);
+ extdesc->size = (grub_uint32_t) *size;
+
+ /* Relocate memory map. */
+ grub_xnu_register_memory ("DeviceTree", 0, *start, *size);
+ for (cur = memorymap->first_child; cur; cur = cur->next)
+ {
+ void *payload;
+ extdesc = cur->data;
+ if (cur->datasize != sizeof (*extdesc))
+ continue;
+ /* RAMDisk is announced by its relocated and not original address. */
+ if (grub_strcmp (cur->name, "RAMDisk") == 0)
+ {
+ extdesc->addr = extdesc->addr + grub_xnu_heap_will_be_at;
+ continue;
+ }
+ payload = extdesc->addr + grub_xnu_heap_start;
+ extdesc->addr = PTR_TO_UINT32(payload);
+
+ /* If it's an extension also relocate the header. */
+ if (grub_memcmp (cur->name, "Driver-", sizeof ("Driver-") - 1) == 0)
+ {
+ struct grub_xnu_extheader *exthead
+ = (struct grub_xnu_extheader *) payload;
+ if (exthead->infoplistaddr)
+ exthead->infoplistaddr = PTR_TO_UINT32 (exthead->infoplistaddr
+ + grub_xnu_heap_start);
+ if (exthead->binaryaddr)
+ exthead->binaryaddr = PTR_TO_UINT32 (exthead->binaryaddr
+ + grub_xnu_heap_start);
+ }
+ }
+
+ /* Write the tree to heap. */
+ grub_xnu_writetree_toheap_real (*start, grub_xnu_devtree_root, "/");
+ return GRUB_ERR_NONE;
+}
+
+/* Find a key or value in parent key. */
+struct grub_xnu_devtree_key *
+grub_xnu_find_key (struct grub_xnu_devtree_key *parent, char *name)
+{
+ struct grub_xnu_devtree_key *cur;
+ for (cur = parent; cur; cur = cur->next)
+ if (grub_strcmp (cur->name, name) == 0)
+ return cur;
+ return 0;
+}
+
+struct grub_xnu_devtree_key *
+grub_xnu_create_key (struct grub_xnu_devtree_key **parent, char *name)
+{
+ struct grub_xnu_devtree_key *ret;
+ ret = grub_xnu_find_key (*parent, name);
+ if (ret)
+ return ret;
+ ret = (struct grub_xnu_devtree_key *) grub_malloc (sizeof (*ret));
+ if (! ret)
+ {
+ grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't create key %s", name);
+ return 0;
+ }
+ ret->name = grub_strdup (name);
+ if (! ret->name)
+ {
+ grub_free (ret);
+ grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't create key %s", name);
+ return 0;
+ }
+ ret->datasize = -1;
+ ret->first_child = 0;
+ ret->next = *parent;
+ *parent = ret;
+ return ret;
+}
+
+struct grub_xnu_devtree_key *
+grub_xnu_create_value (struct grub_xnu_devtree_key **parent, char *name)
+{
+ struct grub_xnu_devtree_key *ret;
+ ret = grub_xnu_find_key (*parent, name);
+ if (ret)
+ {
+ if (ret->datasize == -1)
+ grub_xnu_free_devtree (ret->first_child);
+ else if (ret->datasize)
+ grub_free (ret->data);
+ ret->datasize = 0;
+ ret->data = 0;
+ return ret;
+ }
+ ret = (struct grub_xnu_devtree_key *) grub_malloc (sizeof (*ret));
+ if (! ret)
+ {
+ grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't create value %s", name);
+ return 0;
+ }
+ ret->name = grub_strdup (name);
+ if (! ret->name)
+ {
+ grub_free (ret);
+ grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't create value %s", name);
+ return 0;
+ }
+ ret->datasize = 0;
+ ret->data = 0;
+ ret->next = *parent;
+ *parent = ret;
+ return ret;
+}
+
+static grub_err_t
+grub_xnu_unload (void)
+{
+ grub_xnu_free_devtree (grub_xnu_devtree_root);
+ grub_xnu_devtree_root = 0;
+
+ /* Free loaded image. */
+ driversnum = 0;
+ driverspackagenum = 0;
+ grub_free (grub_xnu_heap_start);
+ grub_xnu_heap_start = 0;
+ grub_xnu_heap_size = 0;
+ grub_xnu_unlock ();
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_cmd_xnu_kernel (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *args[])
+{
+ grub_err_t err;
+ grub_macho_t macho;
+ grub_addr_t startcode, endcode;
+ int i;
+ char *ptr, *loadaddr;
+
+ if (argc < 1)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required");
+
+ grub_xnu_unload ();
+
+ macho = grub_macho_open (args[0]);
+ if (! macho)
+ return grub_errno;
+ if (! grub_macho_contains_macho32 (macho))
+ {
+ grub_macho_close (macho);
+ return grub_error (GRUB_ERR_BAD_OS,
+ "Kernel doesn't contain suitable architecture");
+ }
+
+ err = grub_macho32_size (macho, &startcode, &endcode, GRUB_MACHO_NOBSS);
+ if (err)
+ {
+ grub_macho_close (macho);
+ grub_xnu_unload ();
+ return err;
+ }
+
+ grub_dprintf ("xnu", "endcode = %lx, startcode = %lx\n",
+ (unsigned long) endcode, (unsigned long) startcode);
+
+ loadaddr = grub_xnu_heap_malloc (endcode - startcode);
+ grub_xnu_heap_will_be_at = startcode;
+
+ if (! loadaddr)
+ {
+ grub_macho_close (macho);
+ grub_xnu_unload ();
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "not enough memory to load kernel");
+ }
+
+ /* Load kernel. */
+ err = grub_macho32_load (macho, loadaddr - startcode, GRUB_MACHO_NOBSS);
+ if (err)
+ {
+ grub_macho_close (macho);
+ grub_xnu_unload ();
+ return err;
+ }
+
+ grub_xnu_entry_point = grub_macho32_get_entry_point (macho);
+ if (! grub_xnu_entry_point)
+ {
+ grub_macho_close (macho);
+ grub_xnu_unload ();
+ return grub_error (GRUB_ERR_BAD_OS, "couldn't find entry point");
+ }
+ grub_xnu_entry_point -= startcode;
+
+ grub_macho_close (macho);
+
+ err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE);
+ if (err)
+ {
+ grub_xnu_unload ();
+ return err;
+ }
+
+ /* Copy parameters to kernel command line. */
+ ptr = grub_xnu_cmdline;
+ for (i = 1; i < argc; i++)
+ {
+ if (ptr + grub_strlen (args[i]) + 1
+ >= grub_xnu_cmdline + sizeof (grub_xnu_cmdline))
+ break;
+ grub_memcpy (ptr, args[i], grub_strlen (args[i]));
+ ptr += grub_strlen (args[i]);
+ *ptr = ' ';
+ ptr++;
+ }
+
+ /* Replace last space by '\0'. */
+ if (ptr != grub_xnu_cmdline)
+ *(ptr - 1) = 0;
+
+ err = grub_cpu_xnu_fill_devicetree ();
+ if (err)
+ return err;
+
+ grub_loader_set (grub_xnu_boot, grub_xnu_unload, 0);
+
+ grub_xnu_lock ();
+ return 0;
+}
+
+/* Register a memory in a memory map under name PREFIXSUFFIX
+ and increment SUFFIX. */
+static grub_err_t
+grub_xnu_register_memory (char *prefix, int *suffix,
+ void *addr, grub_size_t size)
+{
+ struct grub_xnu_devtree_key *chosen;
+ struct grub_xnu_devtree_key *memorymap;
+ struct grub_xnu_devtree_key *driverkey;
+ struct grub_xnu_extdesc *extdesc;
+
+ if (! grub_xnu_heap_size)
+ return grub_error (GRUB_ERR_BAD_OS, "no xnu kernel loaded");
+
+ chosen = grub_xnu_create_key (&grub_xnu_devtree_root, "chosen");
+ if (! chosen)
+ return grub_errno;
+ memorymap = grub_xnu_create_key (&(chosen->first_child), "memory-map");
+ if (! memorymap)
+ return grub_errno;
+
+ driverkey = (struct grub_xnu_devtree_key *) grub_malloc (sizeof (*driverkey));
+ if (! driverkey)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't register memory");
+ if (suffix)
+ {
+ driverkey->name = grub_malloc (grub_strlen (prefix) + 10);
+ if (!driverkey->name)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't register memory");
+ grub_sprintf (driverkey->name, "%s%d", prefix, (*suffix)++);
+ }
+ else
+ driverkey->name = grub_strdup (prefix);
+ if (! driverkey->name)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't register extension");
+ driverkey->datasize = sizeof (*extdesc);
+ driverkey->next = memorymap->first_child;
+ memorymap->first_child = driverkey;
+ driverkey->data = extdesc
+ = (struct grub_xnu_extdesc *) grub_malloc (sizeof (*extdesc));
+ if (! driverkey->data)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't register extension");
+ extdesc->addr = ((grub_uint8_t *) addr
+ - (grub_uint8_t *) grub_xnu_heap_start);
+ extdesc->size = (grub_uint32_t) size;
+ return GRUB_ERR_NONE;
+}
+
+/* Load .kext. */
+static grub_err_t
+grub_xnu_load_driver (char *infoplistname, grub_file_t binaryfile)
+{
+ grub_macho_t macho;
+ grub_err_t err;
+ grub_file_t infoplist;
+ struct grub_xnu_extheader *exthead;
+ int neededspace = sizeof (*exthead);
+ char *buf;
+ grub_size_t infoplistsize = 0, machosize = 0;
+
+ if (! grub_xnu_heap_size)
+ return grub_error (GRUB_ERR_BAD_OS, "no xnu kernel loaded");
+
+ /* Compute the needed space. */
+ if (binaryfile)
+ {
+ macho = grub_macho_file (binaryfile);
+ if (! macho || ! grub_macho_contains_macho32 (macho))
+ {
+ if (macho)
+ grub_macho_close (macho);
+ return grub_error (GRUB_ERR_BAD_OS,
+ "Extension doesn't contain suitable architecture");
+ }
+ machosize = grub_macho32_filesize (macho);
+ neededspace += machosize;
+ }
+ else
+ macho = 0;
+
+ if (infoplistname)
+ infoplist = grub_gzfile_open (infoplistname, 1);
+ else
+ infoplist = 0;
+ grub_errno = GRUB_ERR_NONE;
+ if (infoplist)
+ {
+ infoplistsize = grub_file_size (infoplist);
+ neededspace += infoplistsize + 1;
+ }
+ else
+ infoplistsize = 0;
+
+ /* Allocate the space. */
+ err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE);
+ if (err)
+ return err;
+ buf = grub_xnu_heap_malloc (neededspace);
+
+ exthead = (struct grub_xnu_extheader *) buf;
+ grub_memset (exthead, 0, sizeof (*exthead));
+ buf += sizeof (*exthead);
+
+ /* Load the binary. */
+ if (macho)
+ {
+ exthead->binaryaddr = (buf - grub_xnu_heap_start);
+ exthead->binarysize = machosize;
+ if ((err = grub_macho32_readfile (macho, buf)))
+ {
+ grub_macho_close (macho);
+ return err;
+ }
+ grub_macho_close (macho);
+ buf += machosize;
+ }
+ grub_errno = GRUB_ERR_NONE;
+
+ /* Load the plist. */
+ if (infoplist)
+ {
+ exthead->infoplistaddr = (buf - grub_xnu_heap_start);
+ exthead->infoplistsize = infoplistsize + 1;
+ if (grub_file_read (infoplist, buf, infoplistsize)
+ != (grub_ssize_t) (infoplistsize))
+ {
+ grub_file_close (infoplist);
+ grub_error_push ();
+ return grub_error (GRUB_ERR_BAD_OS, "Couldn't read file %s: ",
+ infoplistname);
+ }
+ grub_file_close (infoplist);
+ buf[infoplistsize] = 0;
+ }
+ grub_errno = GRUB_ERR_NONE;
+
+ /* Announce to kernel */
+ return grub_xnu_register_memory ("Driver-", &driversnum, exthead,
+ neededspace);
+}
+
+/* Load mkext. */
+static grub_err_t
+grub_cmd_xnu_mkext (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *args[])
+{
+ grub_file_t file;
+ void *loadto;
+ grub_err_t err;
+ grub_off_t readoff = 0;
+ grub_ssize_t readlen = -1;
+ struct grub_macho_fat_header head;
+ struct grub_macho_fat_arch *archs;
+ int narchs, i;
+
+ if (argc != 1)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required");
+
+ if (! grub_xnu_heap_size)
+ return grub_error (GRUB_ERR_BAD_OS, "no xnu kernel loaded");
+
+ file = grub_gzfile_open (args[0], 1);
+ if (! file)
+ return grub_error (GRUB_ERR_FILE_NOT_FOUND,
+ "Couldn't load driver package");
+
+ /* Sometimes caches are fat binary. Errgh. */
+ if (grub_file_read (file, (char *) &head, sizeof (head))
+ != (grub_ssize_t) (sizeof (head)))
+ {
+ /* I don't know the internal structure of package but
+ can hardly imagine a valid package shorter than 20 bytes. */
+ grub_file_close (file);
+ grub_error_push ();
+ return grub_error (GRUB_ERR_BAD_OS, "Couldn't read file %s", args[0]);
+ }
+
+ /* Find the corresponding architecture. */
+ if (grub_be_to_cpu32 (head.magic) == GRUB_MACHO_FAT_MAGIC)
+ {
+ narchs = grub_be_to_cpu32 (head.nfat_arch);
+ archs = grub_malloc (sizeof (struct grub_macho_fat_arch) * narchs);
+ if (! archs)
+ {
+ grub_file_close (file);
+ grub_error_push ();
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "Couldn't read file %s", args[0]);
+
+ }
+ if (grub_file_read (file, (char *) archs,
+ sizeof (struct grub_macho_fat_arch) * narchs)
+ != (grub_ssize_t) sizeof(struct grub_macho_fat_arch) * narchs)
+ {
+ grub_free (archs);
+ grub_error_push ();
+ return grub_error (GRUB_ERR_READ_ERROR, "Cannot read fat header.");
+ }
+ for (i = 0; i < narchs; i++)
+ {
+ if (GRUB_MACHO_CPUTYPE_IS_HOST32
+ (grub_be_to_cpu32 (archs[i].cputype)))
+ {
+ readoff = grub_be_to_cpu32 (archs[i].offset);
+ readlen = grub_be_to_cpu32 (archs[i].size);
+ }
+ }
+ grub_free (archs);
+ }
+ else
+ {
+ /* It's a flat file. Some sane people still exist. */
+ readoff = 0;
+ readlen = grub_file_size (file);
+ }
+
+ if (readlen == -1)
+ {
+ grub_file_close (file);
+ return grub_error (GRUB_ERR_BAD_OS, "no suitable architecture is found");
+ }
+
+ /* Allocate space. */
+ err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE);
+ if (err)
+ {
+ grub_file_close (file);
+ return err;
+ }
+
+ loadto = grub_xnu_heap_malloc (readlen);
+ if (! loadto)
+ {
+ grub_file_close (file);
+ return grub_errno;
+ }
+
+ /* Read the file. */
+ grub_file_seek (file, readoff);
+ if (grub_file_read (file, loadto, readlen) != (grub_ssize_t) (readlen))
+ {
+ grub_file_close (file);
+ grub_error_push ();
+ return grub_error (GRUB_ERR_BAD_OS, "Couldn't read file %s", args[0]);
+ }
+ grub_file_close (file);
+
+ /* Pass it to kernel. */
+ return grub_xnu_register_memory ("DriversPackage-", &driverspackagenum,
+ loadto, readlen);
+}
+
+static grub_err_t
+grub_cmd_xnu_ramdisk (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *args[])
+{
+ grub_file_t file;
+ void *loadto;
+ grub_err_t err;
+ grub_size_t size;
+
+ if (argc != 1)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required");
+
+ if (! grub_xnu_heap_size)
+ return grub_error (GRUB_ERR_BAD_OS, "no xnu kernel loaded");
+
+ file = grub_gzfile_open (args[0], 1);
+ if (! file)
+ return grub_error (GRUB_ERR_FILE_NOT_FOUND,
+ "Couldn't load ramdisk");
+
+ err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE);
+ if (err)
+ return err;
+
+ size = grub_file_size (file);
+
+ loadto = grub_xnu_heap_malloc (size);
+ if (! loadto)
+ return grub_errno;
+ if (grub_file_read (file, loadto, size)
+ != (grub_ssize_t) (size))
+ {
+ grub_file_close (file);
+ grub_error_push ();
+ return grub_error (GRUB_ERR_BAD_OS, "Couldn't read file %s", args[0]);
+ }
+ return grub_xnu_register_memory ("RAMDisk", 0, loadto, size);
+}
+
+/* Parse a devtree file. It uses the following format:
+ valuename:valuedata;
+ keyname{
+ contents
+ }
+ keyname, valuename and valuedata are in hex.
+ */
+static char *
+grub_xnu_parse_devtree (struct grub_xnu_devtree_key **parent,
+ char *start, char *end)
+{
+ char *ptr, *ptr2;
+ char *name, *data;
+ int namelen, datalen, i;
+ for (ptr = start; ptr && ptr < end; )
+ {
+ if (grub_isspace (*ptr))
+ {
+ ptr++;
+ continue;
+ }
+ if (*ptr == '}')
+ return ptr + 1;
+ namelen = 0;
+
+ /* Parse the name. */
+ for (ptr2 = ptr; ptr2 < end && (grub_isspace (*ptr2)
+ || (*ptr2 >= '0' && *ptr2 <= '9')
+ || (*ptr2 >= 'a' && *ptr2 <= 'f')
+ || (*ptr2 >= 'A' && *ptr2 <= 'F'));
+ ptr2++)
+ if (! grub_isspace (*ptr2))
+ namelen++;
+ if (ptr2 == end)
+ return 0;
+ namelen /= 2;
+ name = grub_malloc (namelen + 1);
+ if (!name)
+ return 0;
+ for (i = 0; i < 2 * namelen; i++)
+ {
+ int hex = 0;
+ while (grub_isspace (*ptr))
+ ptr++;
+ if (*ptr >= '0' && *ptr <= '9')
+ hex = *ptr - '0';
+ if (*ptr >= 'a' && *ptr <= 'f')
+ hex = *ptr - 'a' + 10;
+ if (*ptr >= 'A' && *ptr <= 'F')
+ hex = *ptr - 'A' + 10;
+
+ if (i % 2 == 0)
+ name[i / 2] = hex << 4;
+ else
+ name[i / 2] |= hex;
+ ptr++;
+ }
+ name [namelen] = 0;
+ while (grub_isspace (*ptr))
+ ptr++;
+
+ /* If it describes a key recursively invoke the function. */
+ if (*ptr == '{')
+ {
+ struct grub_xnu_devtree_key *newkey
+ = grub_xnu_create_key (parent, name);
+ grub_free (name);
+ if (! newkey)
+ return 0;
+ ptr = grub_xnu_parse_devtree (&(newkey->first_child), ptr + 1, end);
+ continue;
+ }
+
+ /* Parse the data. */
+ if (*ptr != ':')
+ return 0;
+ ptr++;
+ datalen = 0;
+ for (ptr2 = ptr; ptr2 < end && (grub_isspace (*ptr2)
+ || (*ptr2 >= '0' && *ptr2 <= '9')
+ || (*ptr2 >= 'a' && *ptr2 <= 'f')
+ || (*ptr2 >= 'A' && *ptr2 <= 'F'));
+ ptr2++)
+ if (! grub_isspace (*ptr2))
+ datalen++;
+ if (ptr2 == end)
+ return 0;
+ datalen /= 2;
+ data = grub_malloc (datalen);
+ if (! data)
+ return 0;
+ for (i = 0; i < 2 * datalen; i++)
+ {
+ int hex = 0;
+ while (grub_isspace (*ptr))
+ ptr++;
+ if (*ptr >= '0' && *ptr <= '9')
+ hex = *ptr - '0';
+ if (*ptr >= 'a' && *ptr <= 'f')
+ hex = *ptr - 'a' + 10;
+ if (*ptr >= 'A' && *ptr <= 'F')
+ hex = *ptr - 'A' + 10;
+
+ if (i % 2 == 0)
+ data[i / 2] = hex << 4;
+ else
+ data[i / 2] |= hex;
+ ptr++;
+ }
+ while (ptr < end && grub_isspace (*ptr))
+ ptr++;
+ {
+ struct grub_xnu_devtree_key *newkey
+ = grub_xnu_create_value (parent, name);
+ grub_free (name);
+ if (! newkey)
+ return 0;
+ newkey->datasize = datalen;
+ newkey->data = data;
+ }
+ if (*ptr != ';')
+ return 0;
+ ptr++;
+ }
+ if (ptr >= end && *parent != grub_xnu_devtree_root)
+ return 0;
+ return ptr;
+}
+
+/* Returns true if the kext should be loaded according to plist
+ and osbundlereq. Also fill BINNAME. */
+static int
+grub_xnu_check_os_bundle_required (char *plistname, char *osbundlereq,
+ char **binname)
+{
+ grub_file_t file;
+ char *buf = 0, *tagstart = 0, *ptr1 = 0, *keyptr = 0;
+ char *stringptr = 0, *ptr2 = 0;
+ grub_size_t size;
+ int depth = 0;
+ int ret;
+ int osbundlekeyfound = 0, binnamekeyfound = 0;
+ if (binname)
+ *binname = 0;
+
+ file = grub_gzfile_open (plistname, 1);
+ if (! file)
+ {
+ grub_file_close (file);
+ grub_error_push ();
+ grub_error (GRUB_ERR_BAD_OS, "Couldn't read file %s", plistname);
+ return 0;
+ }
+
+ size = grub_file_size (file);
+ buf = grub_malloc (size);
+ if (! buf)
+ {
+ grub_file_close (file);
+ grub_error_push ();
+ grub_error (GRUB_ERR_OUT_OF_MEMORY, "Couldn't read file %s", plistname);
+ return 0;
+ }
+ if (grub_file_read (file, buf, size) != (grub_ssize_t) (size))
+ {
+ grub_file_close (file);
+ grub_error_push ();
+ grub_error (GRUB_ERR_BAD_OS, "Couldn't read file %s", plistname);
+ return 0;
+ }
+ grub_file_close (file);
+
+ /* Set the return value for the case when no OSBundleRequired tag is found. */
+ if (osbundlereq)
+ ret = grub_strword (osbundlereq, "all") || grub_strword (osbundlereq, "-");
+ else
+ ret = 1;
+
+ /* Parse plist. It's quite dirty and inextensible but does its job. */
+ for (ptr1 = buf; ptr1 < buf + size; ptr1++)
+ switch (*ptr1)
+ {
+ case '<':
+ tagstart = ptr1;
+ *ptr1 = 0;
+ if (keyptr && depth == 4
+ && grub_strcmp (keyptr, "OSBundleRequired") == 0)
+ osbundlekeyfound = 1;
+ if (keyptr && depth == 4 &&
+ grub_strcmp (keyptr, "CFBundleExecutable") == 0)
+ binnamekeyfound = 1;
+ if (stringptr && osbundlekeyfound && osbundlereq && depth == 4)
+ {
+ for (ptr2 = stringptr; *ptr2; ptr2++)
+ *ptr2 = grub_tolower (*ptr2);
+ ret = grub_strword (osbundlereq, stringptr)
+ || grub_strword (osbundlereq, "all");
+ }
+ if (stringptr && binnamekeyfound && binname && depth == 4)
+ {
+ if (*binname)
+ grub_free (*binname);
+ *binname = grub_strdup (stringptr);
+ }
+
+ *ptr1 = '<';
+ keyptr = 0;
+ stringptr = 0;
+ break;
+ case '>':
+ if (! tagstart)
+ {
+ grub_free (buf);
+ grub_error (GRUB_ERR_BAD_OS, "can't parse %s", plistname);
+ return 0;
+ }
+ *ptr1 = 0;
+ if (tagstart[1] == '?' || ptr1[-1] == '/')
+ {
+ osbundlekeyfound = 0;
+ *ptr1 = '>';
+ break;
+ }
+ if (depth == 3 && grub_strcmp (tagstart + 1, "key") == 0)
+ keyptr = ptr1 + 1;
+ if (depth == 3 && grub_strcmp (tagstart + 1, "string") == 0)
+ stringptr = ptr1 + 1;
+ else if (grub_strcmp (tagstart + 1, "/key") != 0)
+ {
+ osbundlekeyfound = 0;
+ binnamekeyfound = 0;
+ }
+ *ptr1 = '>';
+
+ if (tagstart[1] == '/')
+ depth--;
+ else
+ depth++;
+ break;
+ }
+ grub_free (buf);
+
+ return ret;
+}
+
+/* Load all loadable kexts placed under DIRNAME and matching OSBUNDLEREQUIRED */
+grub_err_t
+grub_xnu_scan_dir_for_kexts (char *dirname, char *osbundlerequired,
+ int maxrecursion)
+{
+ grub_device_t dev;
+ char *device_name;
+ grub_fs_t fs;
+ const char *path;
+
+ auto int load_hook (const char *filename,
+ const struct grub_dirhook_info *info);
+ int load_hook (const char *filename, const struct grub_dirhook_info *info)
+ {
+ char *newdirname;
+ if (! info->dir)
+ return 0;
+ if (filename[0] == '.')
+ return 0;
+
+ if (grub_strlen (filename) < 5 ||
+ grub_memcmp (filename + grub_strlen (filename) - 5, ".kext", 5) != 0)
+ return 0;
+
+ newdirname
+ = grub_malloc (grub_strlen (dirname) + grub_strlen (filename) + 2);
+
+ /* It's a .kext. Try to load it. */
+ if (newdirname)
+ {
+ grub_strcpy (newdirname, dirname);
+ newdirname[grub_strlen (newdirname) + 1] = 0;
+ newdirname[grub_strlen (newdirname)] = '/';
+ grub_strcpy (newdirname + grub_strlen (newdirname), filename);
+ grub_xnu_load_kext_from_dir (newdirname, osbundlerequired,
+ maxrecursion);
+ if (grub_errno == GRUB_ERR_BAD_OS)
+ grub_errno = GRUB_ERR_NONE;
+ grub_free (newdirname);
+ }
+ return 0;
+ }
+
+ if (! grub_xnu_heap_size)
+ return grub_error (GRUB_ERR_BAD_OS, "no xnu kernel loaded");
+
+ device_name = grub_file_get_device_name (dirname);
+ dev = grub_device_open (device_name);
+ if (dev)
+ {
+ fs = grub_fs_probe (dev);
+ path = grub_strchr (dirname, ')');
+ if (! path)
+ path = dirname;
+ else
+ path++;
+
+ if (fs)
+ (fs->dir) (dev, path, load_hook);
+ grub_device_close (dev);
+ }
+ grub_free (device_name);
+
+ return GRUB_ERR_NONE;
+}
+
+/* Load extension DIRNAME. (extensions are directoris in xnu) */
+grub_err_t
+grub_xnu_load_kext_from_dir (char *dirname, char *osbundlerequired,
+ int maxrecursion)
+{
+ grub_device_t dev;
+ char *plistname = 0;
+ char *newdirname;
+ char *newpath;
+ char *device_name;
+ grub_fs_t fs;
+ const char *path;
+ char *binsuffix;
+ int usemacos = 0;
+ grub_file_t binfile;
+
+ auto int load_hook (const char *filename,
+ const struct grub_dirhook_info *info);
+
+ int load_hook (const char *filename, const struct grub_dirhook_info *info)
+ {
+ if (grub_strlen (filename) > 15)
+ return 0;
+ grub_strcpy (newdirname + grub_strlen (dirname) + 1, filename);
+
+ /* If the kext contains directory "Contents" all real stuff is in
+ this directory. */
+ if (info->dir && grub_strcasecmp (filename, "Contents") == 0)
+ grub_xnu_load_kext_from_dir (newdirname, osbundlerequired,
+ maxrecursion - 1);
+
+ /* Directory "Plugins" contains nested kexts. */
+ if (info->dir && grub_strcasecmp (filename, "Plugins") == 0)
+ grub_xnu_scan_dir_for_kexts (newdirname, osbundlerequired,
+ maxrecursion - 1);
+
+ /* Directory "MacOS" contains executable, otherwise executable is
+ on the top. */
+ if (info->dir && grub_strcasecmp (filename, "MacOS") == 0)
+ usemacos = 1;
+
+ /* Info.plist is the file which governs our future actions. */
+ if (! info->dir && grub_strcasecmp (filename, "Info.plist") == 0
+ && ! plistname)
+ plistname = grub_strdup (newdirname);
+ return 0;
+ }
+
+ newdirname = grub_malloc (grub_strlen (dirname) + 20);
+ if (! newdirname)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't allocate buffer");
+ grub_strcpy (newdirname, dirname);
+ newdirname[grub_strlen (dirname)] = '/';
+ newdirname[grub_strlen (dirname) + 1] = 0;
+ device_name = grub_file_get_device_name (dirname);
+ dev = grub_device_open (device_name);
+ if (dev)
+ {
+ fs = grub_fs_probe (dev);
+ path = grub_strchr (dirname, ')');
+ if (! path)
+ path = dirname;
+ else
+ path++;
+
+ newpath = grub_strchr (newdirname, ')');
+ if (! newpath)
+ newpath = newdirname;
+ else
+ newpath++;
+
+ /* Look at the directory. */
+ if (fs)
+ (fs->dir) (dev, path, load_hook);
+
+ if (plistname && grub_xnu_check_os_bundle_required
+ (plistname, osbundlerequired, &binsuffix))
+ {
+ if (binsuffix)
+ {
+ /* Open the binary. */
+ char *binname = grub_malloc (grub_strlen (dirname)
+ + grub_strlen (binsuffix)
+ + sizeof ("/MacOS/"));
+ grub_strcpy (binname, dirname);
+ if (usemacos)
+ grub_strcpy (binname + grub_strlen (binname), "/MacOS/");
+ else
+ grub_strcpy (binname + grub_strlen (binname), "/");
+ grub_strcpy (binname + grub_strlen (binname), binsuffix);
+ grub_dprintf ("xnu", "%s:%s\n", plistname, binname);
+ binfile = grub_gzfile_open (binname, 1);
+ if (! binfile)
+ grub_errno = GRUB_ERR_NONE;
+
+ /* Load the extension. */
+ grub_xnu_load_driver (plistname, binfile);
+ grub_free (binname);
+ grub_free (binsuffix);
+ }
+ else
+ {
+ grub_dprintf ("xnu", "%s:0\n", plistname);
+ grub_xnu_load_driver (plistname, 0);
+ }
+ }
+ grub_free (plistname);
+ grub_device_close (dev);
+ }
+ grub_free (device_name);
+
+ return GRUB_ERR_NONE;
+}
+
+/* Load devtree file. */
+static grub_err_t
+grub_cmd_xnu_devtree (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *args[])
+{
+ grub_file_t file;
+ char *data, *endret;
+ grub_size_t datalen;
+
+ if (argc != 1)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "Filename required");
+
+ if (! grub_xnu_heap_size)
+ return grub_error (GRUB_ERR_BAD_OS, "no xnu kernel loaded");
+
+ /* Load the file. */
+ file = grub_gzfile_open (args[0], 1);
+ if (! file)
+ return grub_error (GRUB_ERR_FILE_NOT_FOUND, "Couldn't load device tree");
+ datalen = grub_file_size (file);
+ data = grub_malloc (datalen + 1);
+ if (! data)
+ {
+ grub_file_close (file);
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "Could load device tree into memory");
+ }
+ if (grub_file_read (file, data, datalen) != (grub_ssize_t) datalen)
+ {
+ grub_file_close (file);
+ grub_free (data);
+ grub_error_push ();
+ return grub_error (GRUB_ERR_BAD_OS, "Couldn't read file %s", args[0]);
+ }
+ grub_file_close (file);
+ data[datalen] = 0;
+
+ /* Parse the file. */
+ endret = grub_xnu_parse_devtree (&grub_xnu_devtree_root,
+ data, data + datalen);
+ grub_free (data);
+
+ if (! endret)
+ return grub_error (GRUB_ERR_BAD_OS, "Couldn't parse devtree");
+
+ return GRUB_ERR_NONE;
+}
+
+static int locked=0;
+static grub_dl_t my_mod;
+
+/* Load the kext. */
+static grub_err_t
+grub_cmd_xnu_kext (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *args[])
+{
+ grub_file_t binfile = 0;
+ if (argc == 2)
+ {
+ /* User explicitely specified plist and binary. */
+ if (grub_strcmp (args[1], "-") != 0)
+ {
+ binfile = grub_gzfile_open (args[1], 1);
+ if (! binfile)
+ {
+ grub_error (GRUB_ERR_BAD_OS, "can't open file");
+ return GRUB_ERR_NONE;
+ }
+ }
+ return grub_xnu_load_driver (grub_strcmp (args[0], "-") ? args[0] : 0,
+ binfile);
+ }
+
+ /* load kext normally. */
+ if (argc == 1)
+ return grub_xnu_load_kext_from_dir (args[0], 0, 10);
+
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required");
+}
+
+/* Load a directory containing kexts. */
+static grub_err_t
+grub_cmd_xnu_kextdir (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *args[])
+{
+ if (argc != 1 && argc != 2)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "directory name required");
+
+ if (argc == 1)
+ return grub_xnu_scan_dir_for_kexts (args[0],
+ "console,root,local-root,network-root",
+ 10);
+ else
+ {
+ char *osbundlerequired = grub_strdup (args[1]), *ptr;
+ grub_err_t err;
+ if (! osbundlerequired)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "couldn't allocate string temporary space");
+ for (ptr = osbundlerequired; *ptr; ptr++)
+ *ptr = grub_tolower (*ptr);
+ err = grub_xnu_scan_dir_for_kexts (args[0], osbundlerequired, 10);
+ grub_free (osbundlerequired);
+ return err;
+ }
+}
+
+#ifndef GRUB_UTIL
+static grub_err_t
+grub_cmd_xnu_resume (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *args[])
+{
+ if (argc != 1)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required");
+
+ return grub_xnu_resume (args[0]);
+}
+#endif
+
+void
+grub_xnu_lock ()
+{
+#ifndef GRUB_UTIL
+ if (!locked)
+ grub_dl_ref (my_mod);
+#endif
+ locked = 1;
+}
+
+void
+grub_xnu_unlock ()
+{
+#ifndef GRUB_UTIL
+ if (locked)
+ grub_dl_unref (my_mod);
+#endif
+ locked = 0;
+}
+
+static grub_command_t cmd_kernel, cmd_mkext, cmd_kext, cmd_kextdir,
+ cmd_ramdisk, cmd_devtree, cmd_resume;
+
+GRUB_MOD_INIT(xnu)
+{
+ (void) mod; /* To stop warning. */
+ cmd_kernel = grub_register_command ("xnu_kernel", grub_cmd_xnu_kernel, 0,
+ "load a xnu kernel");
+ cmd_mkext = grub_register_command ("xnu_mkext", grub_cmd_xnu_mkext, 0,
+ "Load XNU extension package.");
+ cmd_kext = grub_register_command ("xnu_kext", grub_cmd_xnu_kext, 0,
+ "Load XNU extension.");
+ cmd_kextdir = grub_register_command ("xnu_kextdir", grub_cmd_xnu_kextdir,
+ "xnu_kextdir DIRECTORY [OSBundleRequired]",
+ "Load XNU extension directory");
+ cmd_ramdisk = grub_register_command ("xnu_ramdisk", grub_cmd_xnu_ramdisk, 0,
+ "Load XNU ramdisk. "
+ "It will be seen as md0");
+ cmd_devtree = grub_register_command ("xnu_devtree", grub_cmd_xnu_devtree, 0,
+ "Load XNU devtree");
+#ifndef GRUB_UTIL
+ cmd_resume = grub_register_command ("xnu_resume", grub_cmd_xnu_resume,
+ 0, "Load XNU hibernate image.");
+#endif
+ my_mod=mod;
+}
+
+GRUB_MOD_FINI(xnu)
+{
+#ifndef GRUB_UTIL
+ grub_unregister_command (cmd_resume);
+#endif
+ grub_unregister_command (cmd_mkext);
+ grub_unregister_command (cmd_kext);
+ grub_unregister_command (cmd_kextdir);
+ grub_unregister_command (cmd_devtree);
+ grub_unregister_command (cmd_ramdisk);
+ grub_unregister_command (cmd_kernel);
+}
diff --git a/loader/xnu_resume.c b/loader/xnu_resume.c
new file mode 100644
index 0000000..24dd77b
--- /dev/null
+++ b/loader/xnu_resume.c
@@ -0,0 +1,134 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/normal.h>
+#include <grub/dl.h>
+#include <grub/file.h>
+#include <grub/disk.h>
+#include <grub/misc.h>
+#include <grub/xnu.h>
+#include <grub/cpu/xnu.h>
+#include <grub/mm.h>
+#include <grub/loader.h>
+
+static void *grub_xnu_hibernate_image;
+
+static grub_err_t
+grub_xnu_resume_unload (void)
+{
+ /* Free loaded image */
+ if (grub_xnu_hibernate_image)
+ grub_free (grub_xnu_hibernate_image);
+ grub_xnu_hibernate_image = 0;
+ grub_xnu_unlock ();
+ return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_xnu_resume (char *imagename)
+{
+ grub_file_t file;
+ grub_size_t total_header_size;
+ struct grub_xnu_hibernate_header hibhead;
+ void *buf;
+
+#if GRUB_CPU_SIZEOF_VOID_P == 8
+ grub_uint64_t codedest;
+ grub_uint64_t codesize;
+#else
+ grub_uint32_t codedest;
+ grub_uint32_t codesize;
+#endif
+
+ file = grub_file_open (imagename);
+ if (! file)
+ return 0;
+
+ /* Read the header. */
+ if (grub_file_read (file, (char *) &hibhead, sizeof (hibhead))
+ !=sizeof (hibhead))
+ {
+ grub_file_close (file);
+ return grub_error (GRUB_ERR_READ_ERROR,
+ "cannot read the hibernate header");
+ }
+
+ /* Check the header. */
+ if (hibhead.magic != GRUB_XNU_HIBERNATE_MAGIC)
+ {
+ grub_file_close (file);
+ return grub_error (GRUB_ERR_BAD_OS,
+ "hibernate header has incorrect magic number");
+ }
+ if (hibhead.encoffset)
+ {
+ grub_file_close (file);
+ return grub_error (GRUB_ERR_BAD_OS,
+ "encrypted images aren't supported yet");
+ }
+
+ codedest = hibhead.launchcode_target_page;
+ codedest *= GRUB_XNU_PAGESIZE;
+ codesize = hibhead.launchcode_numpages;
+ codesize *= GRUB_XNU_PAGESIZE;
+
+ /* FIXME: check that codedest..codedest+codesize is available. */
+
+ /* Calculate total size before pages to copy. */
+ total_header_size = hibhead.extmapsize + sizeof (hibhead);
+
+ /* Unload image if any. */
+ if (grub_xnu_hibernate_image)
+ grub_free (grub_xnu_hibernate_image);
+
+ /* Try to allocate necessary space.
+ FIXME: mm isn't good enough yet to handle huge allocations.
+ */
+ grub_xnu_hibernate_image = buf = grub_malloc (hibhead.image_size);
+ if (! buf)
+ {
+ grub_file_close (file);
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "not enough memory to load image");
+ }
+
+ /* Read image. */
+ if (grub_file_seek (file, 0) == (grub_off_t)-1
+ || grub_file_read (file, buf, hibhead.image_size)
+ != (grub_ssize_t) hibhead.image_size)
+ {
+ grub_file_close (file);
+ return grub_error (GRUB_ERR_READ_ERROR, "Cannot read resume image.");
+ }
+ grub_file_close (file);
+
+ /* Move starting pages to appropriate location. */
+ memcpy ((void *) codedest, ((grub_uint8_t *) buf) + total_header_size,
+ codesize);
+
+ /* Setup variables needed by asm helper. */
+ grub_xnu_stack = (codedest + hibhead.stack);
+ grub_xnu_entry_point = (codedest + hibhead.entry_point);
+ grub_xnu_arg1 = (long) buf;
+
+ /* We're ready now. */
+ grub_loader_set (grub_xnu_launch, grub_xnu_resume_unload, 0);
+ /* Prevent module from unloading. */
+ grub_xnu_lock ();
+ return GRUB_ERR_NONE;
+}
^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [PATCH] xnu
2009-04-14 22:46 [PATCH] xnu phcoder
@ 2009-04-17 23:22 ` Joey Korkames
2009-04-17 23:40 ` Vladimir Serbinenko
2009-04-18 18:52 ` Vladimir Serbinenko
2009-04-27 19:42 ` Vladimir 'phcoder' Serbinenko
2 siblings, 1 reply; 13+ messages in thread
From: Joey Korkames @ 2009-04-17 23:22 UTC (permalink / raw)
To: The development of GRUB 2
I have your whole patch series applied and have been trying to use this to
boot the PureDarwin 9 kernel (on various x86 32 & 64 bit machines):
menuentry "PureDarwin 9 (phcoder efiemu)" {
#efiemu_loadcore /osstore/STAGE1a/grub2/efiemu32.o
efiemu_loadcore /osstore/STAGE1a/grub2/efiemu64.o
efiemu_pnvram /osstore/STAGE2/DARWIN/pnvram_vars
efiemu_acpi
efiemu_prepare
set fsb=667
xnu_kernel /osstore/STAGE2/DARWIN/live/mach_kernel -v -s rd=md0
xnu_mkext /osstore/STAGE2/DARWIN/live/System/Library/Extensions.mkext
xnu_ramdisk /osstore/STAGE2/DARWIN/darwin1.img
}
where darwin files are from:
http://xref.puredarwin.org/puredarwinnano0df46.tbz2
where pnvram_vars is a blank file and darwin1.img is an disk image with an hfs partition in it (still working the layout of that fs per
http://www.insanelymac.com/forum/index.php?showtopic=144128&p=1023069&#entry1023069)
I doesn't matter if I set gfxterm/vbe_mode/fsb or not, the best I get is an immediately panic'ed kernel.
I can only see the output on a CRT monitor of a 2CPU(x4 core) EM64T server (black text on a _dark_ blue background - very hard to read).
A Xen HVM freezes up and the text (if any) is invisible, as well as on several laptop LCDs (hard to really test these platforms since can't "see" the crash).
It seems xnu_mkext works because the output differs when that line is ommited (complains abount not having an /Extensions for ACPI)
Do you have any pointers to improve the video output?
-joey
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH] xnu
2009-04-17 23:22 ` Joey Korkames
@ 2009-04-17 23:40 ` Vladimir Serbinenko
2009-04-17 23:55 ` Vladimir Serbinenko
2009-04-18 1:01 ` Joey Korkames
0 siblings, 2 replies; 13+ messages in thread
From: Vladimir Serbinenko @ 2009-04-17 23:40 UTC (permalink / raw)
To: The development of GRUB 2
[-- Attachment #1: Type: text/plain, Size: 2491 bytes --]
On Sat, Apr 18, 2009 at 1:22 AM, Joey Korkames
<joey+lists@kidfixit.com<joey%2Blists@kidfixit.com>
> wrote:
> I have your whole patch series applied and have been trying to use this to
> boot the PureDarwin 9 kernel (on various x86 32 & 64 bit machines):
>
> menuentry "PureDarwin 9 (phcoder efiemu)" {
> #efiemu_loadcore /osstore/STAGE1a/grub2/efiemu32.o
> efiemu_loadcore /osstore/STAGE1a/grub2/efiemu64.o
efiemu64.o works on x64 machines only. efiemu32.o works everywhere and I
recommends using it.
>
> efiemu_pnvram /osstore/STAGE2/DARWIN/pnvram_vars
> efiemu_acpi
This command is eliminated in the last patch.
>
> efiemu_prepare
> set fsb=667
> xnu_kernel /osstore/STAGE2/DARWIN/live/mach_kernel -v -s rd=md0
> xnu_mkext /osstore/STAGE2/DARWIN/live/System/Library/Extensions.mkext
> xnu_ramdisk /osstore/STAGE2/DARWIN/darwin1.img
>
> }
>
> where darwin files are from:
> http://xref.puredarwin.org/puredarwinnano0df46.tbz2
>
> where pnvram_vars is a blank file
You may also just do efiemu_pnvram with no file as an argument
> and darwin1.img is an disk image with an hfs partition in it (still working
> the layout of that fs per
> http://www.insanelymac.com/forum/index.php?showtopic=144128&p=1023069&#entry1023069
> )
>
I haven't checked you image but notice it must be a pure filesystem
>
> I doesn't matter if I set gfxterm/vbe_mode/fsb or not, the best I get is an
> immediately panic'ed kernel.
> I can only see the output on a CRT monitor of a 2CPU(x4 core) EM64T server
> (black text on a _dark_ blue background - very hard to read).
>
> A Xen HVM freezes up and the text (if any) is invisible, as well as on
> several laptop LCDs (hard to really test these platforms since can't "see"
> the crash).
>
> It seems xnu_mkext works because the output differs when that line is
> ommited (complains abount not having an /Extensions for ACPI)
>
> Do you have any pointers to improve the video output?
>
Unfortunately there is no consensus yet as to how graphics should be for now
it's the following interface:
[on pc only:] vbe_mode=0xYYY # desired vga mode
The blind mode in this patch doesn't work correctly, you are likely to get
a mysterious panic due to memory overwrite
I have done some improvements on the patch and will send it after few tests
>
> -joey
>
>
>
> _______________________________________________
> Grub-devel mailing list
> Grub-devel@gnu.org
> http://lists.gnu.org/mailman/listinfo/grub-devel
>
[-- Attachment #2: Type: text/html, Size: 4023 bytes --]
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH] xnu
2009-04-17 23:40 ` Vladimir Serbinenko
@ 2009-04-17 23:55 ` Vladimir Serbinenko
2009-04-18 1:01 ` Joey Korkames
1 sibling, 0 replies; 13+ messages in thread
From: Vladimir Serbinenko @ 2009-04-17 23:55 UTC (permalink / raw)
To: The development of GRUB 2
[-- Attachment #1: Type: text/plain, Size: 286 bytes --]
>
> xnu_kernel /osstore/STAGE2/DARWIN/live/mach_kernel -v -s rd=md0
>>
> -v option controls the booter and not the kernel. It's totally ignored by
grub which always boots the kernel in verbose mode. In future I'll add a
command to load the splash which will switch to splash mode
[-- Attachment #2: Type: text/html, Size: 666 bytes --]
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH] xnu
2009-04-17 23:40 ` Vladimir Serbinenko
2009-04-17 23:55 ` Vladimir Serbinenko
@ 2009-04-18 1:01 ` Joey Korkames
1 sibling, 0 replies; 13+ messages in thread
From: Joey Korkames @ 2009-04-18 1:01 UTC (permalink / raw)
To: The development of GRUB 2
Thanks for the comments. After reading those and peeking around your code, I
have a much better config that allos xnu.mod to give proper panic text:
#http://grub.enbug.org/gfxterm
loadfont /osstore/STAGE1a/grub2/unifont.pf2
set gfxmode="800x600x32"
set vbe_mode=0x115
terminal_output gfxterm
thanks
-joey
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH] xnu
2009-04-14 22:46 [PATCH] xnu phcoder
2009-04-17 23:22 ` Joey Korkames
@ 2009-04-18 18:52 ` Vladimir Serbinenko
2009-04-24 0:41 ` Joey Korkames
2009-04-27 19:42 ` Vladimir 'phcoder' Serbinenko
2 siblings, 1 reply; 13+ messages in thread
From: Vladimir Serbinenko @ 2009-04-18 18:52 UTC (permalink / raw)
To: The development of GRUB 2
[-- Attachment #1.1: Type: text/plain, Size: 3034 bytes --]
New improved version. Numerous bugfixes. Thanks to Florian Idelberger for
the time he spent on testing and to David Miller for pointing a nasty
mistake out. Also I added fsb autodetection for intel cpus (thanks to Kabyl
for the information about Intel speedstep) but I tested fsb autodetect only
on my own laptop so it may or may not work in general. Parameter fsb= is
still present and overrides autodetect
On Wed, Apr 15, 2009 at 12:46 AM, phcoder <phcoder@gmail.com> wrote:
> Hello. Here is my xnu patch. Tested on i386-pc, i386-efi and x86_64-efi. On
> non-efi it needs efiemu otherwise you need only include/grub/autoefi.h file
> from efiemu patch. To resume xnu from hibernation do:
> xnu_resume <hibernation file>
> Note: you don't need efiemu in this case
> How to boot xnu:
> <efiemu if not on efi platform>
> [on pc only:] vbe_mode=0xYYY # desired vga mode
> fsb=133.3 # your fsb frequency
> xnu_kernel <kernel> <command line>
> <insert modules>
> boot
> Modules can be inserted one of the following ways:
> 1) xnu_mkext <mkext file>
> 2) xnu_kext extension.kext
> 3) xnu_kext extension.kext/Info.plist extension.kext/extension
> 4) xnu_kextdir <directory containing extensions>
> It's also possible to execute these commands multiple times
> The most typical case is
> xnu_kernel /mach_kernel rd=disk0sX
> xnu_mkext /System/Library/Extensions.mkext
>
>
> If you need to add values to device tree the command
> xnu_devtree <devtree file>
> This file uses the following format:
> valuename:valuedata;
> keyname{
> contents
> }
> keyname, valuename and valuedata are in hex.
>
> If you need to adda ramdisk execute
> xnu_ramdisk <ramdisk file>
> ramdisk will be exposed as /dev/md0 which may be used as boot device with
> rd=md0.
>
> The areas which need more work (every help is welcome):
> 1) testing on different platforms
> 2) Detect "device-properties" value in device tree. There are several
> trivial values in device tree present in boot.efi but not in grub2. These
> ones are easy to add and AFAIK don't change anything. But the value
> "device-properties" is difficult. I already know it's format but not where
> the values come from. It contains info about gfx and sound card which may
> not work if this value is missing. The current workaround is xnu_devtree
> command with a dump of device-properties
> 3) autodetect fsb frequency
> 4) Support video splash
> 5) Define and use an unified interface to retrieve video information
> uniformly across platforms
> 6) Boot by UUID. I know how to do it but need md5 for it which is a part of
> pending luks patch
> 7) Scripts for automatic creating of grub.cfg entries
> 8) Support for prelinked kernel. It's compressed and I have yet looked how
> to decompress it (seems it's compressed with something called lzss)
> 9) Use claimmap once available (see my multiboot on efi patch)
> 10) Better collaboration with memory management once advanced mm's
> available
> 11) Resume from encrypted hibernation
>
> --
>
> Regards
> Vladimir 'phcoder' Serbinenko
>
[-- Attachment #1.2: Type: text/html, Size: 3571 bytes --]
[-- Attachment #2: xnu.diff --]
[-- Type: text/x-diff, Size: 98023 bytes --]
diff --git a/conf/i386-efi.rmk b/conf/i386-efi.rmk
index 09c8470..683dda3 100644
--- a/conf/i386-efi.rmk
+++ b/conf/i386-efi.rmk
@@ -98,7 +98,7 @@ kernel_mod_SOURCES = kern/i386/efi/startup.S kern/main.c kern/device.c \
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 rescue.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)
@@ -194,5 +194,12 @@ fixvideo_mod_SOURCES = commands/efi/fixvideo.c
fixvideo_mod_CFLAGS = $(COMMON_CFLAGS)
fixvideo_mod_LDFLAGS = $(COMMON_LDFLAGS)
+pkglib_MODULES += xnu.mod
+xnu_mod_SOURCES = loader/xnu_resume.c loader/i386/xnu.c loader/i386/efi/xnu.c\
+ loader/macho.c loader/xnu.c loader/i386/xnu_helper.S
+xnu_mod_CFLAGS = $(COMMON_CFLAGS) -Werror -Wall
+xnu_mod_LDFLAGS = $(COMMON_LDFLAGS)
+xnu_mod_ASFLAGS = $(COMMON_ASFLAGS)
+
include $(srcdir)/conf/i386.mk
include $(srcdir)/conf/common.mk
diff --git a/conf/i386-pc.rmk b/conf/i386-pc.rmk
index 7a6d79a..2dadcc8 100644
--- a/conf/i386-pc.rmk
+++ b/conf/i386-pc.rmk
@@ -61,7 +61,7 @@ kernel_img_HEADERS = boot.h cache.h device.h disk.h dl.h elf.h elfload.h \
partition.h pc_partition.h rescue.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)
@@ -225,6 +225,13 @@ linux_mod_SOURCES = loader/i386/linux.c
linux_mod_CFLAGS = $(COMMON_CFLAGS)
linux_mod_LDFLAGS = $(COMMON_LDFLAGS)
+pkglib_MODULES += xnu.mod
+xnu_mod_SOURCES = loader/xnu_resume.c loader/i386/xnu.c loader/i386/pc/xnu.c\
+ loader/macho.c loader/xnu.c loader/i386/xnu_helper.S
+xnu_mod_CFLAGS = $(COMMON_CFLAGS) -Werror -Wall
+xnu_mod_LDFLAGS = $(COMMON_LDFLAGS)
+xnu_mod_ASFLAGS = $(COMMON_ASFLAGS)
+
#
# Only arch dependant part of normal.mod will be here. Common part for
# all architecures of normal.mod is at start and should be kept at sync
diff --git a/conf/x86_64-efi.rmk b/conf/x86_64-efi.rmk
index 59237c1..199aed7 100644
--- a/conf/x86_64-efi.rmk
+++ b/conf/x86_64-efi.rmk
@@ -100,7 +100,7 @@ kernel_mod_SOURCES = kern/x86_64/efi/startup.S kern/x86_64/efi/callwrap.S \
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 rescue.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 \
+ 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)
@@ -197,5 +197,12 @@ fixvideo_mod_SOURCES = commands/efi/fixvideo.c
fixvideo_mod_CFLAGS = $(COMMON_CFLAGS)
fixvideo_mod_LDFLAGS = $(COMMON_LDFLAGS)
+pkglib_MODULES += xnu.mod
+xnu_mod_SOURCES = loader/xnu_resume.c loader/i386/xnu.c loader/i386/efi/xnu.c\
+ loader/macho.c loader/xnu.c loader/i386/xnu_helper.S
+xnu_mod_CFLAGS = $(COMMON_CFLAGS) -Werror -Wall
+xnu_mod_LDFLAGS = $(COMMON_LDFLAGS)
+xnu_mod_ASFLAGS = $(COMMON_ASFLAGS)
+
include $(srcdir)/conf/i386.mk
include $(srcdir)/conf/common.mk
diff --git a/include/grub/efi/efi.h b/include/grub/efi/efi.h
index 8c277c0..916f9d6 100644
--- a/include/grub/efi/efi.h
+++ b/include/grub/efi/efi.h
@@ -56,6 +56,7 @@ EXPORT_FUNC(grub_efi_get_device_path) (grub_efi_handle_t handle);
int EXPORT_FUNC(grub_efi_exit_boot_services) (grub_efi_uintn_t map_key);
void EXPORT_FUNC (grub_reboot) (void);
void EXPORT_FUNC (grub_halt) (void);
+int EXPORT_FUNC (grub_efi_finish_boot_services) (void);
void grub_efi_mm_init (void);
void grub_efi_mm_fini (void);
diff --git a/include/grub/i386/macho.h b/include/grub/i386/macho.h
new file mode 100644
index 0000000..61e72a7
--- /dev/null
+++ b/include/grub/i386/macho.h
@@ -0,0 +1,11 @@
+#define GRUB_MACHO_CPUTYPE_IS_HOST32(x) ((x)==0x00000007)
+#define GRUB_MACHO_CPUTYPE_IS_HOST64(x) ((x)==0x01000007)
+
+struct grub_macho_thread32
+{
+ grub_uint32_t cmd;
+ grub_uint32_t cmdsize;
+ grub_uint8_t unknown1[48];
+ grub_uint32_t entry_point;
+ grub_uint8_t unknown2[20];
+} __attribute__ ((packed));
diff --git a/include/grub/i386/pit.h b/include/grub/i386/pit.h
index 0da271d..ff9b9a6 100644
--- a/include/grub/i386/pit.h
+++ b/include/grub/i386/pit.h
@@ -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 */
diff --git a/include/grub/i386/xnu.h b/include/grub/i386/xnu.h
new file mode 100644
index 0000000..435b947
--- /dev/null
+++ b/include/grub/i386/xnu.h
@@ -0,0 +1,58 @@
+#ifndef GRUB_CPU_XNU_H
+#define GRUB_CPU_XNU_H 1
+
+#define GRUB_XNU_PAGESIZE 4096
+typedef grub_uint32_t grub_xnu_ptr_t;
+
+struct grub_xnu_boot_params
+{
+ grub_uint16_t verminor;
+ grub_uint16_t vermajor;
+ /* Command line passed to xnu. */
+ grub_uint8_t cmdline[1024];
+
+ /* Later are the same as EFI's get_memory_map (). */
+ grub_xnu_ptr_t efi_mmap;
+ grub_uint32_t efi_mmap_size;
+ grub_uint32_t efi_mem_desc_size;
+ grub_uint32_t efi_mem_desc_version;
+
+ /* Later are video parameters. */
+ grub_xnu_ptr_t lfb_base;
+#define GRUB_XNU_VIDEO_SPLASH 1
+#define GRUB_XNU_VIDEO_TEXT_IN_VIDEO 2
+ grub_uint32_t lfb_mode;
+ grub_uint32_t lfb_line_len;
+ grub_uint32_t lfb_width;
+ grub_uint32_t lfb_height;
+ grub_uint32_t lfb_depth;
+
+ /* Pointer to device tree and its len. */
+ grub_xnu_ptr_t devtree;
+ grub_uint32_t devtreelen;
+
+ /* First used address by kernel or boot structures. */
+ grub_xnu_ptr_t heap_start;
+ /* Last used address by kernel or boot structures minus previous value. */
+ grub_uint32_t heap_size;
+
+ /* First memory page containing runtime code or data. */
+ grub_uint32_t efi_runtime_first_page;
+ /* First memory page containing runtime code or data minus previous value. */
+ grub_uint32_t efi_runtime_npages;
+ grub_uint32_t efi_system_table;
+ /* Size of grub_efi_uintn_t in bits. */
+ grub_uint8_t efi_uintnbits;
+} __attribute__ ((packed));
+#define GRUB_XNU_BOOTARGS_VERMINOR 4
+#define GRUB_XNU_BOOTARGS_VERMAJOR 1
+
+grub_err_t grub_xnu_launch (void);
+extern grub_uint32_t grub_xnu_entry_point;
+extern grub_uint32_t grub_xnu_stack;
+extern grub_uint32_t grub_xnu_arg1;
+extern char grub_xnu_cmdline[1024];
+grub_err_t grub_xnu_boot (void);
+grub_err_t grub_cpu_xnu_fill_devicetree (void);
+grub_err_t grub_xnu_set_video (struct grub_xnu_boot_params *bootparams_relloc);
+#endif
diff --git a/include/grub/macho.h b/include/grub/macho.h
new file mode 100644
index 0000000..7dfd54e
--- /dev/null
+++ b/include/grub/macho.h
@@ -0,0 +1,107 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GRUB_MACHO_H
+#define GRUB_MACHO_H 1
+#include <grub/types.h>
+
+/* Multi-architecture header. Always in big-endian. */
+struct grub_macho_fat_header
+{
+ grub_uint32_t magic;
+ grub_uint32_t nfat_arch;
+} __attribute__ ((packed));
+#define GRUB_MACHO_FAT_MAGIC 0xcafebabe
+
+typedef grub_uint32_t grub_macho_cpu_type_t;
+typedef grub_uint32_t grub_macho_cpu_subtype_t;
+
+/* Architecture descriptor. Always in big-endian. */
+struct grub_macho_fat_arch
+{
+ grub_macho_cpu_type_t cputype;
+ grub_macho_cpu_subtype_t cpusubtype;
+ grub_uint32_t offset;
+ grub_uint32_t size;
+ grub_uint32_t align;
+} __attribute__ ((packed));;
+
+/* File header for 32-bit. Always in native-endian. */
+struct grub_macho_header32
+{
+#define GRUB_MACHO_MAGIC32 0xfeedface
+ grub_uint32_t magic;
+ grub_macho_cpu_type_t cputype;
+ grub_macho_cpu_subtype_t cpusubtype;
+ grub_uint32_t filetype;
+ grub_uint32_t ncmds;
+ grub_uint32_t sizeofcmds;
+ grub_uint32_t flags;
+} __attribute__ ((packed));
+
+/* File header for 64-bit. Always in native-endian. */
+struct grub_macho_header64
+{
+#define GRUB_MACHO_MAGIC64 0xfeedfacf
+ grub_uint32_t magic;
+ grub_macho_cpu_type_t cputype;
+ grub_macho_cpu_subtype_t cpusubtype;
+ grub_uint32_t filetype;
+ grub_uint32_t ncmds;
+ grub_uint32_t sizeofcmds;
+ grub_uint32_t flags;
+ grub_uint32_t reserved;
+} __attribute__ ((packed));
+
+/* Convenience union. What do we need to load to identify the file type. */
+union grub_macho_filestart
+{
+ struct grub_macho_fat_header fat;
+ struct grub_macho_header32 thin32;
+ struct grub_macho_header64 thin64;
+} __attribute__ ((packed));
+
+/* Common header of Mach-O commands. */
+struct grub_macho_cmd
+{
+ grub_uint32_t cmd;
+ grub_uint32_t cmdsize;
+} __attribute__ ((packed));
+
+typedef grub_uint32_t grub_macho_vmprot_t;
+
+/* 32-bit segment command. */
+struct grub_macho_segment32
+{
+#define GRUB_MACHO_CMD_SEGMENT32 1
+ grub_uint32_t cmd;
+ grub_uint32_t cmdsize;
+ grub_uint8_t segname[16];
+ grub_uint32_t vmaddr;
+ grub_uint32_t vmsize;
+ grub_uint32_t fileoff;
+ grub_uint32_t filesize;
+ grub_macho_vmprot_t maxprot;
+ grub_macho_vmprot_t initprot;
+ grub_uint32_t nsects;
+ grub_uint32_t flags;
+} __attribute__ ((packed));
+
+#define GRUB_MACHO_CMD_THREAD 5
+
+#endif
diff --git a/include/grub/machoload.h b/include/grub/machoload.h
new file mode 100644
index 0000000..572496f
--- /dev/null
+++ b/include/grub/machoload.h
@@ -0,0 +1,62 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GRUB_MACHOLOAD_HEADER
+#define GRUB_MACHOLOAD_HEADER 1
+
+#include <grub/err.h>
+#include <grub/elf.h>
+#include <grub/file.h>
+#include <grub/symbol.h>
+#include <grub/types.h>
+
+struct grub_macho_file
+{
+ grub_file_t file;
+ grub_ssize_t offset32;
+ grub_ssize_t end32;
+ int ncmds32;
+ grub_size_t cmdsize32;
+ grub_uint8_t *cmds32;
+ grub_ssize_t offset64;
+ grub_ssize_t end64;
+ int ncmds64;
+ grub_size_t cmdsize64;
+ grub_uint8_t *cmds64;
+};
+typedef struct grub_macho_file *grub_macho_t;
+
+grub_macho_t grub_macho_open (const char *);
+grub_macho_t grub_macho_file (grub_file_t);
+grub_err_t grub_macho_close (grub_macho_t);
+
+int grub_macho_contains_macho32 (grub_macho_t);
+grub_err_t grub_macho32_size (grub_macho_t macho, grub_addr_t *segments_start,
+ grub_addr_t *segments_end, int flags);
+grub_uint32_t grub_macho32_get_entry_point (grub_macho_t macho);
+
+/* Ignore BSS segments when loading. */
+#define GRUB_MACHO_NOBSS 0x1
+grub_err_t grub_macho32_load (grub_macho_t macho, char *offset, int flags);
+
+/* Like filesize and file_read but take only 32-bit part
+ for current architecture. */
+grub_size_t grub_macho32_filesize (grub_macho_t macho);
+grub_err_t grub_macho32_readfile (grub_macho_t macho, void *dest);
+
+#endif /* ! GRUB_MACHOLOAD_HEADER */
diff --git a/include/grub/x86_64/macho.h b/include/grub/x86_64/macho.h
new file mode 100644
index 0000000..165b8da
--- /dev/null
+++ b/include/grub/x86_64/macho.h
@@ -0,0 +1 @@
+#include <grub/i386/macho.h>
diff --git a/include/grub/x86_64/xnu.h b/include/grub/x86_64/xnu.h
new file mode 100644
index 0000000..ae61733
--- /dev/null
+++ b/include/grub/x86_64/xnu.h
@@ -0,0 +1 @@
+#include <grub/i386/xnu.h>
diff --git a/include/grub/xnu.h b/include/grub/xnu.h
new file mode 100644
index 0000000..08ae49b
--- /dev/null
+++ b/include/grub/xnu.h
@@ -0,0 +1,105 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GRUB_XNU_H
+#define GRUB_XNU_H 1
+
+/* Header of a hibernation image. */
+struct grub_xnu_hibernate_header
+{
+ /* Size of the image. Notice that file containing image is usually bigger. */
+ grub_uint64_t image_size;
+ grub_uint8_t unknown1[8];
+ /* Where to copy lauchcode?*/
+ grub_uint32_t launchcode_target_page;
+ /* How many pages of launchcode? */
+ grub_uint32_t launchcode_numpages;
+ /* Where to jump? */
+ grub_uint32_t entry_point;
+ /* %esp at start. */
+ grub_uint32_t stack;
+ grub_uint8_t unknown2[44];
+#define GRUB_XNU_HIBERNATE_MAGIC 0x73696d65
+ grub_uint32_t magic;
+ grub_uint8_t unknown3[28];
+ /* This value is non-zero if page is encrypted. Unsupported. */
+ grub_uint64_t encoffset;
+ grub_uint8_t unknown4[360];
+ /* The size of additional header used to locate image without parsing FS.
+ Used only to skip it.
+ */
+ grub_uint32_t extmapsize;
+} __attribute__ ((packed));
+
+/* In-memory structure for temporary keeping device tree. */
+struct grub_xnu_devtree_key
+{
+ char *name;
+ int datasize; /* -1 for not leaves. */
+ union
+ {
+ struct grub_xnu_devtree_key *first_child;
+ void *data;
+ };
+ struct grub_xnu_devtree_key *next;
+};
+
+/* A structure used in memory-map values. */
+struct
+grub_xnu_extdesc
+{
+ grub_uint32_t addr;
+ grub_uint32_t size;
+} __attribute__ ((packed));
+
+/* Header describing extension in the memory. */
+struct grub_xnu_extheader
+{
+ grub_uint32_t infoplistaddr;
+ grub_uint32_t infoplistsize;
+ grub_uint32_t binaryaddr;
+ grub_uint32_t binarysize;
+} __attribute__ ((packed));
+
+struct grub_xnu_devtree_key *grub_xnu_create_key (struct grub_xnu_devtree_key **parent,
+ char *name);
+
+extern struct grub_xnu_devtree_key *grub_xnu_devtree_root;
+
+void grub_xnu_free_devtree (struct grub_xnu_devtree_key *cur);
+
+grub_err_t grub_xnu_writetree_toheap (void **start, grub_size_t *size);
+struct grub_xnu_devtree_key *grub_xnu_create_value (struct grub_xnu_devtree_key **parent,
+ char *name);
+
+void grub_xnu_lock (void);
+void grub_xnu_unlock (void);
+grub_err_t grub_xnu_resume (char *imagename);
+struct grub_xnu_devtree_key *grub_xnu_find_key (struct grub_xnu_devtree_key *parent,
+ char *name);
+grub_err_t grub_xnu_align_heap (int align);
+grub_err_t grub_xnu_scan_dir_for_kexts (char *dirname, char *osbundlerequired,
+ int maxrecursion);
+grub_err_t grub_xnu_load_kext_from_dir (char *dirname, char *osbundlerequired,
+ int maxrecursion);
+void *grub_xnu_heap_malloc (int size);
+extern grub_uint32_t grub_xnu_heap_real_start;
+extern grub_size_t grub_xnu_heap_size;
+extern char *grub_xnu_heap_start;
+extern grub_addr_t grub_xnu_heap_will_be_at;
+#endif
diff --git a/kern/efi/efi.c b/kern/efi/efi.c
index 9c9a400..25007e3 100644
--- a/kern/efi/efi.c
+++ b/kern/efi/efi.c
@@ -734,3 +734,26 @@ grub_efi_print_device_path (grub_efi_device_path_t *dp)
dp = (grub_efi_device_path_t *) ((char *) dp + len);
}
}
+
+int
+grub_efi_finish_boot_services (void)
+{
+ grub_efi_uintn_t mmap_size = 0;
+ grub_efi_uintn_t map_key;
+ grub_efi_uintn_t desc_size;
+ grub_efi_uint32_t desc_version;
+ void *mmap_buf = 0;
+
+ if (grub_efi_get_memory_map (&mmap_size, mmap_buf, &map_key,
+ &desc_size, &desc_version) < 0)
+ return 0;
+
+ mmap_buf = grub_malloc (mmap_size);
+
+ if (grub_efi_get_memory_map (&mmap_size, mmap_buf, &map_key,
+ &desc_size, &desc_version) <= 0)
+ return 0;
+
+ return grub_efi_exit_boot_services (map_key);
+}
+
diff --git a/kern/efi/mm.c b/kern/efi/mm.c
index 35b12ab..4635776 100644
--- a/kern/efi/mm.c
+++ b/kern/efi/mm.c
@@ -47,7 +47,7 @@ static struct allocated_page *allocated_pages = 0;
/* The minimum and maximum heap size for GRUB itself. */
#define MIN_HEAP_SIZE 0x100000
-#define MAX_HEAP_SIZE (16 * 0x100000)
+#define MAX_HEAP_SIZE (1600 * 0x100000)
/* Allocate pages. Return the pointer to the first of allocated pages. */
diff --git a/loader/i386/efi/xnu.c b/loader/i386/efi/xnu.c
new file mode 100644
index 0000000..5085cdb
--- /dev/null
+++ b/loader/i386/efi/xnu.c
@@ -0,0 +1,177 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/env.h>
+#include <grub/xnu.h>
+#include <grub/cpu/xnu.h>
+#include <grub/efi/api.h>
+#include <grub/efi/efi.h>
+#include <grub/efi/uga_draw.h>
+#include <grub/pci.h>
+#include <grub/misc.h>
+
+/* Setup video for xnu. Big parts are copied from linux.c. */
+
+static grub_efi_guid_t uga_draw_guid = GRUB_EFI_UGA_DRAW_GUID;
+
+#define RGB_MASK 0xffffff
+#define RGB_MAGIC 0x121314
+#define LINE_MIN 800
+#define LINE_MAX 4096
+#define FBTEST_STEP (0x10000 >> 2)
+#define FBTEST_COUNT 8
+
+static int
+find_line_len (grub_uint32_t *fb_base, grub_uint32_t *line_len)
+{
+ grub_uint32_t *base = (grub_uint32_t *) (grub_target_addr_t) *fb_base;
+ int i;
+
+ for (i = 0; i < FBTEST_COUNT; i++, base += FBTEST_STEP)
+ {
+ if ((*base & RGB_MASK) == RGB_MAGIC)
+ {
+ int j;
+
+ for (j = LINE_MIN; j <= LINE_MAX; j++)
+ {
+ if ((base[j] & RGB_MASK) == RGB_MAGIC)
+ {
+ *fb_base = (grub_uint32_t) (grub_target_addr_t) base;
+ *line_len = j << 2;
+
+ return 1;
+ }
+ }
+
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int
+find_framebuf (grub_uint32_t *fb_base, grub_uint32_t *line_len)
+{
+ int found = 0;
+
+ auto int NESTED_FUNC_ATTR find_card (int bus, int dev, int func,
+ grub_pci_id_t pciid);
+
+ int NESTED_FUNC_ATTR find_card (int bus, int dev, int func,
+ grub_pci_id_t pciid)
+ {
+ grub_pci_address_t addr;
+
+ addr = grub_pci_make_address (bus, dev, func, 2);
+ if (grub_pci_read (addr) >> 24 == 0x3)
+ {
+ int i;
+
+ grub_printf ("Display controller: %d:%d.%d\nDevice id: %x\n",
+ bus, dev, func, pciid);
+ addr += 8;
+ for (i = 0; i < 6; i++, addr += 4)
+ {
+ grub_uint32_t old_bar1, old_bar2, type;
+ grub_uint64_t base64;
+
+ old_bar1 = grub_pci_read (addr);
+ if ((! old_bar1) || (old_bar1 & GRUB_PCI_ADDR_SPACE_IO))
+ continue;
+
+ type = old_bar1 & GRUB_PCI_ADDR_MEM_TYPE_MASK;
+ if (type == GRUB_PCI_ADDR_MEM_TYPE_64)
+ {
+ if (i == 5)
+ break;
+
+ old_bar2 = grub_pci_read (addr + 4);
+ }
+ else
+ old_bar2 = 0;
+
+ base64 = old_bar2;
+ base64 <<= 32;
+ base64 |= (old_bar1 & GRUB_PCI_ADDR_MEM_MASK);
+
+ grub_printf ("%s(%d): 0x%llx\n",
+ ((old_bar1 & GRUB_PCI_ADDR_MEM_PREFETCH) ?
+ "VMEM" : "MMIO"), i,
+ (unsigned long long) base64);
+
+ if ((old_bar1 & GRUB_PCI_ADDR_MEM_PREFETCH) && (! found))
+ {
+ *fb_base = base64;
+ if (find_line_len (fb_base, line_len))
+ found++;
+ }
+
+ if (type == GRUB_PCI_ADDR_MEM_TYPE_64)
+ {
+ i++;
+ addr += 4;
+ }
+ }
+ }
+
+ return found;
+ }
+
+ grub_pci_iterate (find_card);
+ return found;
+}
+
+grub_err_t
+grub_xnu_set_video (struct grub_xnu_boot_params *params)
+{
+ grub_efi_uga_draw_protocol_t *c;
+ grub_uint32_t width, height, depth, rate, pixel, fb_base, line_len;
+ int ret;
+
+ c = grub_efi_locate_protocol (&uga_draw_guid, 0);
+ if (! c)
+ return grub_error (GRUB_ERR_IO, "Couldn't find UGADraw");
+
+ if (efi_call_5 (c->get_mode, c, &width, &height, &depth, &rate))
+ return grub_error (GRUB_ERR_IO, "Couldn't retrieve video mode");
+
+ grub_printf ("Video mode: %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;
+}
diff --git a/loader/i386/pc/xnu.c b/loader/i386/pc/xnu.c
new file mode 100644
index 0000000..2c78b2b
--- /dev/null
+++ b/loader/i386/pc/xnu.c
@@ -0,0 +1,67 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/env.h>
+#include <grub/misc.h>
+#include <grub/xnu.h>
+#include <grub/cpu/xnu.h>
+#include <grub/machine/vbe.h>
+#include <grub/machine/vga.h>
+
+/* Setup video for xnu. */
+
+grub_err_t
+grub_xnu_set_video (struct grub_xnu_boot_params *bootparams_relloc)
+{
+ grub_uint32_t use_mode = GRUB_VBE_DEFAULT_VIDEO_MODE;
+ char *modevar;
+ grub_err_t err;
+ struct grub_vbe_mode_info_block mode_info;
+
+ /* Check existence of vbe_mode environment variable. */
+ modevar = grub_env_get ("vbe_mode");
+ if (modevar != 0)
+ {
+ unsigned long value;
+
+ value = grub_strtoul (modevar, 0, 0);
+ if (grub_errno == GRUB_ERR_NONE)
+ use_mode = value;
+ else
+ grub_errno = GRUB_ERR_NONE;
+ }
+
+ if (use_mode < 0x100)
+ return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+ "booting xnu in text mode isn't supported yet");
+ err = grub_vbe_set_video_mode (use_mode, &mode_info);
+ if (err != GRUB_ERR_NONE)
+ return err;
+
+ /* FIXME: setting non-32bit color depth results in strange screens. */
+ if (use_mode >= 0x100)
+ {
+ bootparams_relloc->lfb_base = mode_info.phys_base_addr;
+ bootparams_relloc->lfb_mode = GRUB_XNU_VIDEO_TEXT_IN_VIDEO;
+ bootparams_relloc->lfb_line_len = mode_info.bytes_per_scan_line;
+ bootparams_relloc->lfb_width = mode_info.x_resolution;
+ bootparams_relloc->lfb_height = mode_info.y_resolution;
+ bootparams_relloc->lfb_depth = mode_info.bits_per_pixel;
+ }
+ return GRUB_ERR_NONE;
+}
diff --git a/loader/i386/xnu.c b/loader/i386/xnu.c
new file mode 100644
index 0000000..ba82c50
--- /dev/null
+++ b/loader/i386/xnu.c
@@ -0,0 +1,516 @@
+/*
+ * 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
+grub_err_t
+grub_xnu_launch (void)
+{
+ grub_printf ("Fake launch %x:%p:%p", grub_xnu_entry_point, grub_xnu_arg1,
+ grub_xnu_stack);
+ grub_getkey ();
+ return 0;
+}
+#endif
+
+static int
+utf16_strlen (grub_uint16_t *in)
+{
+ int i;
+ for (i = 0; in[i]; i++);
+ return i;
+}
+
+/* Read frequency from a string in MHz and return it in Hz. */
+static grub_uint64_t
+readfrequency (const char *str)
+{
+ grub_uint64_t num = 0;
+ int mul = 1000000;
+ int found = 0;
+
+ while (*str)
+ {
+ unsigned long digit;
+
+ digit = grub_tolower (*str) - '0';
+ if (digit > 9)
+ break;
+
+ found = 1;
+
+ num = num * 10 + digit;
+ str++;
+ }
+ num *= 1000000;
+ if (*str == '.')
+ {
+ str++;
+ while (*str)
+ {
+ unsigned long digit;
+
+ digit = grub_tolower (*str) - '0';
+ if (digit > 9)
+ break;
+
+ found = 1;
+
+ mul /= 10;
+ num = num + mul * digit;
+ str++;
+ }
+ }
+ if (! found)
+ return 0;
+
+ return num;
+}
+
+/* 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;
+
+ /* 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 = PTR_TO_UINT32 (devtree);
+ bootparams_relloc->devtreelen = devtreelen;
+
+ bootparams_relloc->heap_start = PTR_TO_UINT32 (grub_xnu_heap_start);
+ bootparams_relloc->heap_size = grub_xnu_heap_size;
+
+ bootparams_relloc->efi_mmap
+ = PTR_TO_UINT32 ((grub_uint8_t *)grub_xnu_heap_start + mmap_relloc_off);
+ bootparams_relloc->efi_mmap_size = memory_map_size;
+ bootparams_relloc->efi_mem_desc_size = descriptor_size;
+ bootparams_relloc->efi_mem_desc_version = descriptor_version;
+
+ bootparams_relloc->efi_runtime_first_page = firstruntimeaddr
+ / GRUB_XNU_PAGESIZE;
+ bootparams_relloc->efi_runtime_npages
+ = ((lastruntimeaddr + GRUB_XNU_PAGESIZE - 1) / GRUB_XNU_PAGESIZE)
+ - (firstruntimeaddr / GRUB_XNU_PAGESIZE);
+ bootparams_relloc->efi_uintnbits = SIZEOF_OF_UINTN * 8;
+ bootparams_relloc->efi_system_table
+ = PTR_TO_UINT32 (grub_autoefi_system_table);
+
+ bootparams_relloc->verminor = GRUB_XNU_BOOTARGS_VERMINOR;
+ bootparams_relloc->vermajor = GRUB_XNU_BOOTARGS_VERMAJOR;
+
+ /* Set video. */
+ err = grub_xnu_set_video (bootparams_relloc);
+ if (err != GRUB_ERR_NONE)
+ {
+ grub_print_error ();
+ grub_errno = GRUB_ERR_NONE;
+ grub_printf ("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;
+ }
+
+ /* Parameters for asm helper. */
+ grub_xnu_stack = bootparams_relloc->heap_start
+ + bootparams_relloc->heap_size + GRUB_XNU_PAGESIZE;
+ grub_xnu_arg1 = (long) bootparams_relloc;
+ grub_xnu_entry_point = PTR_TO_UINT32
+ (grub_xnu_heap_start + grub_xnu_entry_point);
+ grub_dprintf ("xnu", "eip=%x\n", grub_xnu_entry_point);
+ grub_dprintf ("xnu", "launch=%p\n", grub_xnu_launch);
+ 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;
+}
diff --git a/loader/i386/xnu_helper.S b/loader/i386/xnu_helper.S
new file mode 100644
index 0000000..1504227
--- /dev/null
+++ b/loader/i386/xnu_helper.S
@@ -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/>.
+ */
+
+#include <grub/symbol.h>
+
+ .p2align 2 /* force 4-byte alignment */
+
+FUNCTION(grub_xnu_launch)
+ cli
+
+#ifdef __x86_64__
+ /* Switch to compatibility mode. */
+
+ lgdt gdtdesc
+
+ /* Update %cs. Thanks to David Miller for pointing this mistake out. */
+ ljmp *jump_vector
+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
+ .quad gdt
+
+ .p2align 4
+jump_vector:
+ .long cont1
+ .long 0x10
+#endif
\ No newline at end of file
diff --git a/loader/macho.c b/loader/macho.c
new file mode 100644
index 0000000..da081a2
--- /dev/null
+++ b/loader/macho.c
@@ -0,0 +1,395 @@
+/* macho.c - load Mach-O files. */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* This Mach-O loader is incomplete and can load only non-relocatable segments.
+ This is however enough to boot xnu (otool -l and Mach-O specs for more info).
+*/
+
+#include <grub/err.h>
+#include <grub/macho.h>
+#include <grub/cpu/macho.h>
+#include <grub/machoload.h>
+#include <grub/file.h>
+#include <grub/gzio.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+
+#define min(a,b) (((a) < (b)) ? (a) : (b))
+
+/* 32-bit. */
+
+int
+grub_macho_contains_macho32 (grub_macho_t macho)
+{
+ return macho->offset32 != -1;
+}
+
+static void
+grub_macho_parse32 (grub_macho_t macho)
+{
+ struct grub_macho_header32 head;
+
+ /* Is there any candidate at all? */
+ if (macho->offset32 == -1)
+ return;
+
+ /* Read header and check magic*/
+ if (grub_file_seek (macho->file, macho->offset32) == (grub_off_t) -1
+ || grub_file_read (macho->file, (char *) &head, sizeof (head))
+ != sizeof(head))
+ {
+ grub_error (GRUB_ERR_READ_ERROR, "Cannot read Mach-O header.");
+ macho->offset32 = -1;
+ return;
+ }
+ if (head.magic != GRUB_MACHO_MAGIC32)
+ {
+ grub_error (GRUB_ERR_BAD_OS, "Invalid Mach-O 32-bit header.");
+ macho->offset32 = -1;
+ return;
+ }
+
+ /* Read commands. */
+ macho->ncmds32 = head.ncmds;
+ macho->cmdsize32 = head.sizeofcmds;
+ macho->cmds32 = grub_malloc(macho->cmdsize32);
+ if (! macho->cmds32)
+ {
+ grub_error (GRUB_ERR_OUT_OF_MEMORY, "not enough memory to read commands");
+ return;
+ }
+ if (grub_file_read (macho->file, (char *) macho->cmds32,
+ (grub_size_t) macho->cmdsize32)
+ != (grub_ssize_t) macho->cmdsize32)
+ {
+ grub_error (GRUB_ERR_READ_ERROR, "Cannot read Mach-O header.");
+ macho->offset32 = -1;
+ }
+}
+
+typedef int NESTED_FUNC_ATTR (*grub_macho_iter_hook_t)
+(grub_macho_t , struct grub_macho_cmd *,
+ void *);
+
+static grub_err_t
+grub_macho32_cmds_iterate (grub_macho_t macho,
+ grub_macho_iter_hook_t hook,
+ void *hook_arg)
+{
+ grub_uint8_t *hdrs = macho->cmds32;
+ int i;
+ if (! macho->cmds32)
+ return grub_error (GRUB_ERR_BAD_OS, "Couldn't find 32-bit Mach-O");
+ for (i = 0; i < macho->ncmds32; i++)
+ {
+ struct grub_macho_cmd *hdr = (struct grub_macho_cmd *) hdrs;
+ if (hook (macho, hdr, hook_arg))
+ break;
+ hdrs += hdr->cmdsize;
+ }
+
+ return grub_errno;
+}
+
+grub_size_t
+grub_macho32_filesize (grub_macho_t macho)
+{
+ if (grub_macho_contains_macho32 (macho))
+ return macho->end32 - macho->offset32;
+ return 0;
+}
+
+grub_err_t
+grub_macho32_readfile (grub_macho_t macho, void *dest)
+{
+ grub_ssize_t read;
+ if (! grub_macho_contains_macho32 (macho))
+ return grub_error (GRUB_ERR_BAD_OS,
+ "Couldn't read arcitecture-specific part");
+
+ if (grub_file_seek (macho->file, macho->offset32) == (grub_off_t) -1)
+ {
+ grub_error_push ();
+ return grub_error (GRUB_ERR_BAD_OS,
+ "Invalid offset in program header.");
+ }
+
+ read = grub_file_read (macho->file, dest,
+ macho->end32 - macho->offset32);
+ if (read != (grub_ssize_t) (macho->end32 - macho->offset32))
+ {
+ grub_error_push ();
+ return grub_error (GRUB_ERR_BAD_OS,
+ "Couldn't read arcitecture-specific part");
+ }
+ return GRUB_ERR_NONE;
+}
+
+/* Calculate the amount of memory spanned by the segments. */
+grub_err_t
+grub_macho32_size (grub_macho_t macho, grub_addr_t *segments_start,
+ grub_addr_t *segments_end, int flags)
+{
+ int nr_phdrs = 0;
+
+ /* Run through the program headers to calculate the total memory size we
+ should claim. */
+ auto int NESTED_FUNC_ATTR calcsize (grub_macho_t _macho,
+ struct grub_macho_cmd *phdr, void *_arg);
+ int NESTED_FUNC_ATTR calcsize (grub_macho_t UNUSED _macho,
+ struct grub_macho_cmd *hdr0, void UNUSED *_arg)
+ {
+ struct grub_macho_segment32 *hdr = (struct grub_macho_segment32 *) hdr0;
+ if (hdr->cmd != GRUB_MACHO_CMD_SEGMENT32)
+ return 0;
+ if (! hdr->filesize && (flags & GRUB_MACHO_NOBSS))
+ return 0;
+
+ nr_phdrs++;
+ if (hdr->vmaddr < *segments_start)
+ *segments_start = hdr->vmaddr;
+ if (hdr->vmaddr + hdr->vmsize > *segments_end)
+ *segments_end = hdr->vmaddr + hdr->vmsize;
+ return 0;
+ }
+
+ *segments_start = (grub_uint32_t) -1;
+ *segments_end = 0;
+
+ grub_macho32_cmds_iterate (macho, calcsize, 0);
+
+ if (nr_phdrs == 0)
+ return grub_error (GRUB_ERR_BAD_OS, "No program headers present");
+
+ if (*segments_end < *segments_start)
+ /* Very bad addresses. */
+ return grub_error (GRUB_ERR_BAD_OS, "Bad program header load addresses");
+
+ return GRUB_ERR_NONE;
+}
+
+/* Load every loadable segment into memory specified by `_load_hook'. */
+grub_err_t
+grub_macho32_load (grub_macho_t macho, char *offset, int flags)
+{
+ grub_err_t err = 0;
+ auto int NESTED_FUNC_ATTR do_load(grub_macho_t _macho,
+ struct grub_macho_cmd *hdr0,
+ void UNUSED *_arg);
+ int NESTED_FUNC_ATTR do_load(grub_macho_t _macho,
+ struct grub_macho_cmd *hdr0,
+ void UNUSED *_arg)
+ {
+ struct grub_macho_segment32 *hdr = (struct grub_macho_segment32 *) hdr0;
+
+ if (hdr->cmd != GRUB_MACHO_CMD_SEGMENT32)
+ return 0;
+
+ if (! hdr->filesize && (flags & GRUB_MACHO_NOBSS))
+ return 0;
+ if (! hdr->vmsize)
+ return 0;
+
+ if (grub_file_seek (_macho->file, hdr->fileoff
+ + _macho->offset32) == (grub_off_t) -1)
+ {
+ grub_error_push ();
+ grub_error (GRUB_ERR_BAD_OS,
+ "Invalid offset in program header.");
+ return 1;
+ }
+
+ if (hdr->filesize)
+ {
+ grub_ssize_t read;
+ read = grub_file_read (_macho->file, offset + hdr->vmaddr,
+ min (hdr->filesize, hdr->vmsize));
+ if (read != (grub_ssize_t) min (hdr->filesize, hdr->vmsize))
+ {
+ /* XXX How can we free memory from `load_hook'? */
+ grub_error_push ();
+ err=grub_error (GRUB_ERR_BAD_OS,
+ "Couldn't read segment from file: "
+ "wanted 0x%lx bytes; read 0x%lx bytes.",
+ hdr->filesize, read);
+ return 1;
+ }
+ }
+
+ if (hdr->filesize < hdr->vmsize)
+ grub_memset (offset + hdr->vmaddr + hdr->filesize,
+ 0, hdr->vmsize - hdr->filesize);
+ return 0;
+ }
+
+ grub_macho32_cmds_iterate (macho, do_load, 0);
+
+ return err;
+}
+
+grub_uint32_t
+grub_macho32_get_entry_point (grub_macho_t macho)
+{
+ grub_uint32_t entry_point = 0;
+ auto int NESTED_FUNC_ATTR hook(grub_macho_t _macho,
+ struct grub_macho_cmd *hdr,
+ void UNUSED *_arg);
+ int NESTED_FUNC_ATTR hook(grub_macho_t UNUSED _macho,
+ struct grub_macho_cmd *hdr,
+ void UNUSED *_arg)
+ {
+ if (hdr->cmd == GRUB_MACHO_CMD_THREAD)
+ entry_point = ((struct grub_macho_thread32 *) hdr)->entry_point;
+ return 0;
+ }
+ grub_macho32_cmds_iterate (macho, hook, 0);
+ return entry_point;
+}
+
+
+grub_err_t
+grub_macho_close (grub_macho_t macho)
+{
+ grub_file_t file = macho->file;
+
+ grub_free (macho->cmds32);
+ grub_free (macho->cmds64);
+
+ grub_free (macho);
+
+ if (file)
+ grub_file_close (file);
+
+ return grub_errno;
+}
+
+grub_macho_t
+grub_macho_file (grub_file_t file)
+{
+ grub_macho_t macho;
+ union grub_macho_filestart filestart;
+
+ macho = grub_malloc (sizeof (*macho));
+ if (! macho)
+ return 0;
+
+ macho->file = file;
+ macho->offset32 = -1;
+ macho->offset64 = -1;
+ macho->end32 = -1;
+ macho->end64 = -1;
+ macho->cmds32 = 0;
+ macho->cmds64 = 0;
+
+ if (grub_file_seek (macho->file, 0) == (grub_off_t) -1)
+ goto fail;
+
+ if (grub_file_read (macho->file, (char *) &filestart, sizeof (filestart))
+ != sizeof (filestart))
+ {
+ grub_error_push ();
+ grub_error (GRUB_ERR_READ_ERROR, "Cannot read Mach-O header.");
+ goto fail;
+ }
+
+ /* Is it a fat file? */
+ if (filestart.fat.magic == grub_be_to_cpu32 (GRUB_MACHO_FAT_MAGIC))
+ {
+ struct grub_macho_fat_arch *archs;
+ int i, narchs;
+
+ /* Load architecture description. */
+ narchs = grub_be_to_cpu32 (filestart.fat.nfat_arch);
+ if (grub_file_seek (macho->file, sizeof (struct grub_macho_fat_header))
+ == (grub_off_t) -1)
+ goto fail;
+ archs = grub_malloc (sizeof (struct grub_macho_fat_arch) * narchs);
+ if (!archs)
+ goto fail;
+ if (grub_file_read (macho->file, (char *) archs,
+ sizeof (struct grub_macho_fat_arch) * narchs)
+ != (grub_ssize_t)sizeof(struct grub_macho_fat_arch) * narchs)
+ {
+ grub_free (archs);
+ grub_error_push ();
+ grub_error (GRUB_ERR_READ_ERROR, "Cannot read Mach-O header.");
+ goto fail;
+ }
+
+ for (i = 0; i < narchs; i++)
+ {
+ if (GRUB_MACHO_CPUTYPE_IS_HOST32
+ (grub_be_to_cpu32 (archs[i].cputype)))
+ {
+ macho->offset32 = grub_be_to_cpu32 (archs[i].offset);
+ macho->end32 = grub_be_to_cpu32 (archs[i].offset)
+ + grub_be_to_cpu32 (archs[i].size);
+ }
+ if (GRUB_MACHO_CPUTYPE_IS_HOST64
+ (grub_be_to_cpu32 (archs[i].cputype)))
+ {
+ macho->offset64 = grub_be_to_cpu32 (archs[i].offset);
+ macho->end64 = grub_be_to_cpu32 (archs[i].offset)
+ + grub_be_to_cpu32 (archs[i].size);
+ }
+ }
+ grub_free (archs);
+ }
+
+ /* Is it a thin 32-bit file? */
+ if (filestart.thin32.magic == GRUB_MACHO_MAGIC32)
+ {
+ macho->offset32 = 0;
+ macho->end32 = grub_file_size (file);
+ }
+
+ /* Is it a thin 64-bit file? */
+ if (filestart.thin64.magic == GRUB_MACHO_MAGIC64)
+ {
+ macho->offset64 = 0;
+ macho->end64 = grub_file_size (file);
+ }
+
+ grub_macho_parse32 (macho);
+ /* FIXME: implement 64-bit.*/
+ /* grub_macho_parse64 (macho); */
+
+ return macho;
+
+fail:
+ grub_macho_close (macho);
+ return 0;
+}
+
+grub_macho_t
+grub_macho_open (const char *name)
+{
+ grub_file_t file;
+ grub_macho_t macho;
+
+ file = grub_gzfile_open (name, 1);
+ if (! file)
+ return 0;
+
+ macho = grub_macho_file (file);
+ if (! macho)
+ grub_file_close (file);
+
+ return macho;
+}
diff --git a/loader/xnu.c b/loader/xnu.c
new file mode 100644
index 0000000..12c45db
--- /dev/null
+++ b/loader/xnu.c
@@ -0,0 +1,1377 @@
+/* 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;
+char *grub_xnu_heap_start = 0;
+grub_addr_t grub_xnu_heap_will_be_at = 0;
+grub_size_t grub_xnu_heap_size = 0;
+
+/* Allocate heap by 32MB-blocks. */
+#define GRUB_XNU_HEAP_ALLOC_BLOCK 0x2000000
+
+static grub_err_t
+grub_xnu_register_memory (char *prefix, int *suffix,
+ void *addr, grub_size_t size);
+void *
+grub_xnu_heap_malloc (int size)
+{
+ void *val;
+
+#if 0
+ /* This way booting is faster but less reliable.
+ Once we have advanced mm second way will be as fast as this one. */
+ val = grub_xnu_heap_start = (char *) 0x100000;
+#else
+ int oldblknum, newblknum;
+
+ /* The page after the heap is used for stack. Ensure it's usable. */
+ if (grub_xnu_heap_size)
+ oldblknum = (grub_xnu_heap_size + GRUB_XNU_PAGESIZE
+ + GRUB_XNU_HEAP_ALLOC_BLOCK - 1) / GRUB_XNU_HEAP_ALLOC_BLOCK;
+ else
+ oldblknum = 0;
+ newblknum = (grub_xnu_heap_size + size + GRUB_XNU_PAGESIZE
+ + GRUB_XNU_HEAP_ALLOC_BLOCK - 1) / GRUB_XNU_HEAP_ALLOC_BLOCK;
+ if (oldblknum != newblknum)
+ /* FIXME: instruct realloc to allocate at 1MB if possible once
+ advanced mm is ready. */
+ val = grub_realloc (grub_xnu_heap_start,
+ newblknum * GRUB_XNU_HEAP_ALLOC_BLOCK);
+ else
+ val = grub_xnu_heap_start;
+ if (! val)
+ {
+ grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "not enough space on xnu memory heap");
+ return 0;
+ }
+ grub_xnu_heap_start = val;
+#endif
+
+ val = (char *) grub_xnu_heap_start + grub_xnu_heap_size;
+ grub_xnu_heap_size += size;
+ grub_dprintf ("xnu", "val=%p\n", val);
+ return (char *) val;
+}
+
+/* Make sure next block of the heap will be aligned.
+ Please notice: aligned are pointers AFTER relocation
+ and not the current ones. */
+grub_err_t
+grub_xnu_align_heap (int align)
+{
+ int align_overhead = align - grub_xnu_heap_size % align;
+ if (align_overhead == align)
+ return GRUB_ERR_NONE;
+ if (! grub_xnu_heap_malloc (align_overhead))
+ return grub_errno;
+ return GRUB_ERR_NONE;
+}
+
+/* Free subtree pointed by CUR. */
+void
+grub_xnu_free_devtree (struct grub_xnu_devtree_key *cur)
+{
+ struct grub_xnu_devtree_key *d;
+ while (cur)
+ {
+ grub_free (cur->name);
+ if (cur->datasize == -1)
+ grub_xnu_free_devtree (cur->first_child);
+ else if (cur->data)
+ grub_free (cur->data);
+ d = cur->next;
+ grub_free (cur);
+ cur = d;
+ }
+}
+
+/* Compute the size of device tree in xnu format. */
+static grub_size_t
+grub_xnu_writetree_get_size (struct grub_xnu_devtree_key *start, char *name)
+{
+ grub_size_t ret;
+ struct grub_xnu_devtree_key *cur;
+
+ /* Key header. */
+ ret = 2 * sizeof (grub_uint32_t);
+
+ /* "name" value. */
+ ret += 32 + sizeof (grub_uint32_t)
+ + grub_strlen (name) + 4
+ - (grub_strlen (name) % 4);
+
+ for (cur = start; cur; cur = cur->next)
+ if (cur->datasize != -1)
+ {
+ int align_overhead;
+
+ align_overhead = 4 - (cur->datasize % 4);
+ if (align_overhead == 4)
+ align_overhead = 0;
+ ret += 32 + sizeof (grub_uint32_t) + cur->datasize + align_overhead;
+ }
+ else
+ ret += grub_xnu_writetree_get_size (cur->first_child, cur->name);
+ return ret;
+}
+
+/* Write devtree in XNU format at curptr assuming the head is named NAME.*/
+static void *
+grub_xnu_writetree_toheap_real (void *curptr,
+ struct grub_xnu_devtree_key *start, char *name)
+{
+ struct grub_xnu_devtree_key *cur;
+ int nkeys = 0, nvals = 0;
+ for (cur = start; cur; cur = cur->next)
+ {
+ if (cur->datasize == -1)
+ nkeys++;
+ else
+ nvals++;
+ }
+ /* For the name. */
+ nvals++;
+
+ *((grub_uint32_t *) curptr) = nvals;
+ curptr = ((grub_uint32_t *) curptr) + 1;
+ *((grub_uint32_t *) curptr) = nkeys;
+ curptr = ((grub_uint32_t *) curptr) + 1;
+
+ /* First comes "name" value. */
+ grub_memset (curptr, 0, 32);
+ grub_memcpy (curptr, "name", 4);
+ curptr = ((grub_uint8_t *) curptr) + 32;
+ *((grub_uint32_t *)curptr) = grub_strlen (name) + 1;
+ curptr = ((grub_uint32_t *) curptr) + 1;
+ grub_memcpy (curptr, name, grub_strlen (name));
+ curptr = ((grub_uint8_t *) curptr) + grub_strlen (name);
+ grub_memset (curptr, 0, 4 - (grub_strlen (name) % 4));
+ curptr = ((grub_uint8_t *) curptr) + (4 - (grub_strlen (name) % 4));
+
+ /* Then the other values. */
+ for (cur = start; cur; cur = cur->next)
+ if (cur->datasize != -1)
+ {
+ int align_overhead;
+
+ align_overhead = 4 - (cur->datasize % 4);
+ if (align_overhead == 4)
+ align_overhead = 0;
+ grub_memset (curptr, 0, 32);
+ grub_strncpy (curptr, cur->name, 31);
+ curptr = ((grub_uint8_t *) curptr) + 32;
+ *((grub_uint32_t *) curptr) = cur->datasize;
+ curptr = ((grub_uint32_t *) curptr) + 1;
+ grub_memcpy (curptr, cur->data, cur->datasize);
+ curptr = ((grub_uint8_t *) curptr) + cur->datasize;
+ grub_memset (curptr, 0, align_overhead);
+ curptr = ((grub_uint8_t *) curptr) + align_overhead;
+ }
+
+ /* And then the keys. Recursively use this function. */
+ for (cur = start; cur; cur = cur->next)
+ if (cur->datasize == -1)
+ if (!(curptr = grub_xnu_writetree_toheap_real (curptr,
+ cur->first_child,
+ cur->name)))
+ return 0;
+ return curptr;
+}
+
+grub_err_t
+grub_xnu_writetree_toheap (void **start, grub_size_t *size)
+{
+ struct grub_xnu_devtree_key *chosen, *cur;
+ 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);
+
+ /* Put real data in the dummy. */
+ extdesc->addr = PTR_TO_UINT32 (*start);
+ extdesc->size = (grub_uint32_t) *size;
+
+ /* Relocate memory map. */
+ for (cur = memorymap->first_child; cur; cur = cur->next)
+ {
+ void *payload;
+ extdesc = cur->data;
+ if (cur->datasize != sizeof (*extdesc))
+ continue;
+ /* RAMDisk is announced by its relocated and not original address. */
+ if (grub_strcmp (cur->name, "RAMDisk") == 0)
+ {
+ extdesc->addr = extdesc->addr + grub_xnu_heap_will_be_at;
+ continue;
+ }
+ payload = extdesc->addr + grub_xnu_heap_start;
+ extdesc->addr = PTR_TO_UINT32(payload);
+
+ /* If it's an extension also relocate the header. */
+ if (grub_memcmp (cur->name, "Driver-", sizeof ("Driver-") - 1) == 0)
+ {
+ struct grub_xnu_extheader *exthead
+ = (struct grub_xnu_extheader *) payload;
+ if (exthead->infoplistaddr)
+ exthead->infoplistaddr = PTR_TO_UINT32 (exthead->infoplistaddr
+ + grub_xnu_heap_start);
+ if (exthead->binaryaddr)
+ exthead->binaryaddr = PTR_TO_UINT32 (exthead->binaryaddr
+ + grub_xnu_heap_start);
+ }
+ }
+
+ /* Write the tree to heap. */
+ grub_xnu_writetree_toheap_real (*start, grub_xnu_devtree_root, "/");
+ return GRUB_ERR_NONE;
+}
+
+/* Find a key or value in parent key. */
+struct grub_xnu_devtree_key *
+grub_xnu_find_key (struct grub_xnu_devtree_key *parent, char *name)
+{
+ struct grub_xnu_devtree_key *cur;
+ for (cur = parent; cur; cur = cur->next)
+ if (grub_strcmp (cur->name, name) == 0)
+ return cur;
+ return 0;
+}
+
+struct grub_xnu_devtree_key *
+grub_xnu_create_key (struct grub_xnu_devtree_key **parent, char *name)
+{
+ struct grub_xnu_devtree_key *ret;
+ ret = grub_xnu_find_key (*parent, name);
+ if (ret)
+ return ret;
+ ret = (struct grub_xnu_devtree_key *) grub_malloc (sizeof (*ret));
+ if (! ret)
+ {
+ grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't create key %s", name);
+ return 0;
+ }
+ ret->name = grub_strdup (name);
+ if (! ret->name)
+ {
+ grub_free (ret);
+ grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't create key %s", name);
+ return 0;
+ }
+ ret->datasize = -1;
+ ret->first_child = 0;
+ ret->next = *parent;
+ *parent = ret;
+ return ret;
+}
+
+struct grub_xnu_devtree_key *
+grub_xnu_create_value (struct grub_xnu_devtree_key **parent, char *name)
+{
+ struct grub_xnu_devtree_key *ret;
+ ret = grub_xnu_find_key (*parent, name);
+ if (ret)
+ {
+ if (ret->datasize == -1)
+ grub_xnu_free_devtree (ret->first_child);
+ else if (ret->datasize)
+ grub_free (ret->data);
+ ret->datasize = 0;
+ ret->data = 0;
+ return ret;
+ }
+ ret = (struct grub_xnu_devtree_key *) grub_malloc (sizeof (*ret));
+ if (! ret)
+ {
+ grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't create value %s", name);
+ return 0;
+ }
+ ret->name = grub_strdup (name);
+ if (! ret->name)
+ {
+ grub_free (ret);
+ grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't create value %s", name);
+ return 0;
+ }
+ ret->datasize = 0;
+ ret->data = 0;
+ ret->next = *parent;
+ *parent = ret;
+ return ret;
+}
+
+static grub_err_t
+grub_xnu_unload (void)
+{
+ grub_xnu_free_devtree (grub_xnu_devtree_root);
+ grub_xnu_devtree_root = 0;
+
+ /* Free loaded image. */
+ driversnum = 0;
+ driverspackagenum = 0;
+ grub_free (grub_xnu_heap_start);
+ grub_xnu_heap_start = 0;
+ grub_xnu_heap_size = 0;
+ grub_xnu_unlock ();
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_cmd_xnu_kernel (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *args[])
+{
+ grub_err_t err;
+ grub_macho_t macho;
+ grub_addr_t startcode, endcode;
+ int i;
+ char *ptr, *loadaddr;
+
+ if (argc < 1)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required");
+
+ grub_xnu_unload ();
+
+ macho = grub_macho_open (args[0]);
+ if (! macho)
+ return grub_errno;
+ if (! grub_macho_contains_macho32 (macho))
+ {
+ grub_macho_close (macho);
+ return grub_error (GRUB_ERR_BAD_OS,
+ "Kernel doesn't contain suitable architecture");
+ }
+
+ err = grub_macho32_size (macho, &startcode, &endcode, GRUB_MACHO_NOBSS);
+ if (err)
+ {
+ grub_macho_close (macho);
+ grub_xnu_unload ();
+ return err;
+ }
+
+ grub_dprintf ("xnu", "endcode = %lx, startcode = %lx\n",
+ (unsigned long) endcode, (unsigned long) startcode);
+
+ loadaddr = grub_xnu_heap_malloc (endcode - startcode);
+ grub_xnu_heap_will_be_at = startcode;
+
+ if (! loadaddr)
+ {
+ grub_macho_close (macho);
+ grub_xnu_unload ();
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "not enough memory to load kernel");
+ }
+
+ /* Load kernel. */
+ err = grub_macho32_load (macho, loadaddr - startcode, GRUB_MACHO_NOBSS);
+ if (err)
+ {
+ grub_macho_close (macho);
+ grub_xnu_unload ();
+ return err;
+ }
+
+ grub_xnu_entry_point = grub_macho32_get_entry_point (macho);
+ if (! grub_xnu_entry_point)
+ {
+ grub_macho_close (macho);
+ grub_xnu_unload ();
+ return grub_error (GRUB_ERR_BAD_OS, "couldn't find entry point");
+ }
+ grub_xnu_entry_point -= startcode;
+
+ grub_macho_close (macho);
+
+ err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE);
+ if (err)
+ {
+ grub_xnu_unload ();
+ return err;
+ }
+
+ /* Copy parameters to kernel command line. */
+ ptr = grub_xnu_cmdline;
+ for (i = 1; i < argc; i++)
+ {
+ if (ptr + grub_strlen (args[i]) + 1
+ >= grub_xnu_cmdline + sizeof (grub_xnu_cmdline))
+ break;
+ grub_memcpy (ptr, args[i], grub_strlen (args[i]));
+ ptr += grub_strlen (args[i]);
+ *ptr = ' ';
+ ptr++;
+ }
+
+ /* Replace last space by '\0'. */
+ if (ptr != grub_xnu_cmdline)
+ *(ptr - 1) = 0;
+
+ err = grub_cpu_xnu_fill_devicetree ();
+ if (err)
+ return err;
+
+ grub_loader_set (grub_xnu_boot, grub_xnu_unload, 0);
+
+ grub_xnu_lock ();
+ return 0;
+}
+
+/* Register a memory in a memory map under name PREFIXSUFFIX
+ and increment SUFFIX. */
+static grub_err_t
+grub_xnu_register_memory (char *prefix, int *suffix,
+ void *addr, grub_size_t size)
+{
+ struct grub_xnu_devtree_key *chosen;
+ struct grub_xnu_devtree_key *memorymap;
+ struct grub_xnu_devtree_key *driverkey;
+ struct grub_xnu_extdesc *extdesc;
+
+ if (! grub_xnu_heap_size)
+ return grub_error (GRUB_ERR_BAD_OS, "no xnu kernel loaded");
+
+ chosen = grub_xnu_create_key (&grub_xnu_devtree_root, "chosen");
+ if (! chosen)
+ return grub_errno;
+ memorymap = grub_xnu_create_key (&(chosen->first_child), "memory-map");
+ if (! memorymap)
+ return grub_errno;
+
+ driverkey = (struct grub_xnu_devtree_key *) grub_malloc (sizeof (*driverkey));
+ if (! driverkey)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't register memory");
+ if (suffix)
+ {
+ driverkey->name = grub_malloc (grub_strlen (prefix) + 10);
+ if (!driverkey->name)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't register memory");
+ grub_sprintf (driverkey->name, "%s%d", prefix, (*suffix)++);
+ }
+ else
+ driverkey->name = grub_strdup (prefix);
+ if (! driverkey->name)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't register extension");
+ driverkey->datasize = sizeof (*extdesc);
+ driverkey->next = memorymap->first_child;
+ memorymap->first_child = driverkey;
+ driverkey->data = extdesc
+ = (struct grub_xnu_extdesc *) grub_malloc (sizeof (*extdesc));
+ if (! driverkey->data)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't register extension");
+ extdesc->addr = ((grub_uint8_t *) addr
+ - (grub_uint8_t *) grub_xnu_heap_start);
+ extdesc->size = (grub_uint32_t) size;
+ return GRUB_ERR_NONE;
+}
+
+/* Load .kext. */
+static grub_err_t
+grub_xnu_load_driver (char *infoplistname, grub_file_t binaryfile)
+{
+ grub_macho_t macho;
+ grub_err_t err;
+ grub_file_t infoplist;
+ struct grub_xnu_extheader *exthead;
+ int neededspace = sizeof (*exthead);
+ char *buf;
+ grub_size_t infoplistsize = 0, machosize = 0;
+
+ if (! grub_xnu_heap_size)
+ return grub_error (GRUB_ERR_BAD_OS, "no xnu kernel loaded");
+
+ /* Compute the needed space. */
+ if (binaryfile)
+ {
+ macho = grub_macho_file (binaryfile);
+ if (! macho || ! grub_macho_contains_macho32 (macho))
+ {
+ if (macho)
+ grub_macho_close (macho);
+ return grub_error (GRUB_ERR_BAD_OS,
+ "Extension doesn't contain suitable architecture");
+ }
+ machosize = grub_macho32_filesize (macho);
+ neededspace += machosize;
+ }
+ else
+ macho = 0;
+
+ if (infoplistname)
+ infoplist = grub_gzfile_open (infoplistname, 1);
+ else
+ infoplist = 0;
+ grub_errno = GRUB_ERR_NONE;
+ if (infoplist)
+ {
+ infoplistsize = grub_file_size (infoplist);
+ neededspace += infoplistsize + 1;
+ }
+ else
+ infoplistsize = 0;
+
+ /* Allocate the space. */
+ err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE);
+ if (err)
+ return err;
+ buf = grub_xnu_heap_malloc (neededspace);
+
+ exthead = (struct grub_xnu_extheader *) buf;
+ grub_memset (exthead, 0, sizeof (*exthead));
+ buf += sizeof (*exthead);
+
+ /* Load the binary. */
+ if (macho)
+ {
+ exthead->binaryaddr = (buf - grub_xnu_heap_start);
+ exthead->binarysize = machosize;
+ if ((err = grub_macho32_readfile (macho, buf)))
+ {
+ grub_macho_close (macho);
+ return err;
+ }
+ grub_macho_close (macho);
+ buf += machosize;
+ }
+ grub_errno = GRUB_ERR_NONE;
+
+ /* Load the plist. */
+ if (infoplist)
+ {
+ exthead->infoplistaddr = (buf - grub_xnu_heap_start);
+ exthead->infoplistsize = infoplistsize + 1;
+ if (grub_file_read (infoplist, buf, infoplistsize)
+ != (grub_ssize_t) (infoplistsize))
+ {
+ grub_file_close (infoplist);
+ grub_error_push ();
+ return grub_error (GRUB_ERR_BAD_OS, "Couldn't read file %s: ",
+ infoplistname);
+ }
+ grub_file_close (infoplist);
+ buf[infoplistsize] = 0;
+ }
+ grub_errno = GRUB_ERR_NONE;
+
+ /* Announce to kernel */
+ return grub_xnu_register_memory ("Driver-", &driversnum, exthead,
+ neededspace);
+}
+
+/* Load mkext. */
+static grub_err_t
+grub_cmd_xnu_mkext (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *args[])
+{
+ grub_file_t file;
+ void *loadto;
+ grub_err_t err;
+ grub_off_t readoff = 0;
+ grub_ssize_t readlen = -1;
+ struct grub_macho_fat_header head;
+ struct grub_macho_fat_arch *archs;
+ int narchs, i;
+
+ if (argc != 1)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required");
+
+ if (! grub_xnu_heap_size)
+ return grub_error (GRUB_ERR_BAD_OS, "no xnu kernel loaded");
+
+ file = grub_gzfile_open (args[0], 1);
+ if (! file)
+ return grub_error (GRUB_ERR_FILE_NOT_FOUND,
+ "Couldn't load driver package");
+
+ /* Sometimes caches are fat binary. Errgh. */
+ if (grub_file_read (file, (char *) &head, sizeof (head))
+ != (grub_ssize_t) (sizeof (head)))
+ {
+ /* I don't know the internal structure of package but
+ can hardly imagine a valid package shorter than 20 bytes. */
+ grub_file_close (file);
+ grub_error_push ();
+ return grub_error (GRUB_ERR_BAD_OS, "Couldn't read file %s", args[0]);
+ }
+
+ /* Find the corresponding architecture. */
+ if (grub_be_to_cpu32 (head.magic) == GRUB_MACHO_FAT_MAGIC)
+ {
+ narchs = grub_be_to_cpu32 (head.nfat_arch);
+ archs = grub_malloc (sizeof (struct grub_macho_fat_arch) * narchs);
+ if (! archs)
+ {
+ grub_file_close (file);
+ grub_error_push ();
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "Couldn't read file %s", args[0]);
+
+ }
+ if (grub_file_read (file, (char *) archs,
+ sizeof (struct grub_macho_fat_arch) * narchs)
+ != (grub_ssize_t) sizeof(struct grub_macho_fat_arch) * narchs)
+ {
+ grub_free (archs);
+ grub_error_push ();
+ return grub_error (GRUB_ERR_READ_ERROR, "Cannot read fat header.");
+ }
+ for (i = 0; i < narchs; i++)
+ {
+ if (GRUB_MACHO_CPUTYPE_IS_HOST32
+ (grub_be_to_cpu32 (archs[i].cputype)))
+ {
+ readoff = grub_be_to_cpu32 (archs[i].offset);
+ readlen = grub_be_to_cpu32 (archs[i].size);
+ }
+ }
+ grub_free (archs);
+ }
+ else
+ {
+ /* It's a flat file. Some sane people still exist. */
+ readoff = 0;
+ readlen = grub_file_size (file);
+ }
+
+ if (readlen == -1)
+ {
+ grub_file_close (file);
+ return grub_error (GRUB_ERR_BAD_OS, "no suitable architecture is found");
+ }
+
+ /* Allocate space. */
+ err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE);
+ if (err)
+ {
+ grub_file_close (file);
+ return err;
+ }
+
+ loadto = grub_xnu_heap_malloc (readlen);
+ if (! loadto)
+ {
+ grub_file_close (file);
+ return grub_errno;
+ }
+
+ /* Read the file. */
+ grub_file_seek (file, readoff);
+ if (grub_file_read (file, loadto, readlen) != (grub_ssize_t) (readlen))
+ {
+ grub_file_close (file);
+ grub_error_push ();
+ return grub_error (GRUB_ERR_BAD_OS, "Couldn't read file %s", args[0]);
+ }
+ grub_file_close (file);
+
+ /* Pass it to kernel. */
+ return grub_xnu_register_memory ("DriversPackage-", &driverspackagenum,
+ loadto, readlen);
+}
+
+static grub_err_t
+grub_cmd_xnu_ramdisk (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *args[])
+{
+ grub_file_t file;
+ void *loadto;
+ grub_err_t err;
+ grub_size_t size;
+
+ if (argc != 1)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required");
+
+ if (! grub_xnu_heap_size)
+ return grub_error (GRUB_ERR_BAD_OS, "no xnu kernel loaded");
+
+ file = grub_gzfile_open (args[0], 1);
+ if (! file)
+ return grub_error (GRUB_ERR_FILE_NOT_FOUND,
+ "Couldn't load ramdisk");
+
+ err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE);
+ if (err)
+ return err;
+
+ size = grub_file_size (file);
+
+ loadto = grub_xnu_heap_malloc (size);
+ if (! loadto)
+ return grub_errno;
+ if (grub_file_read (file, loadto, size)
+ != (grub_ssize_t) (size))
+ {
+ grub_file_close (file);
+ grub_error_push ();
+ return grub_error (GRUB_ERR_BAD_OS, "Couldn't read file %s", args[0]);
+ }
+ return grub_xnu_register_memory ("RAMDisk", 0, loadto, size);
+}
+
+/* Parse a devtree file. It uses the following format:
+ valuename:valuedata;
+ keyname{
+ contents
+ }
+ keyname, valuename and valuedata are in hex.
+ */
+static char *
+grub_xnu_parse_devtree (struct grub_xnu_devtree_key **parent,
+ char *start, char *end)
+{
+ char *ptr, *ptr2;
+ char *name, *data;
+ int namelen, datalen, i;
+ for (ptr = start; ptr && ptr < end; )
+ {
+ if (grub_isspace (*ptr))
+ {
+ ptr++;
+ continue;
+ }
+ if (*ptr == '}')
+ return ptr + 1;
+ namelen = 0;
+
+ /* Parse the name. */
+ for (ptr2 = ptr; ptr2 < end && (grub_isspace (*ptr2)
+ || (*ptr2 >= '0' && *ptr2 <= '9')
+ || (*ptr2 >= 'a' && *ptr2 <= 'f')
+ || (*ptr2 >= 'A' && *ptr2 <= 'F'));
+ ptr2++)
+ if (! grub_isspace (*ptr2))
+ namelen++;
+ if (ptr2 == end)
+ return 0;
+ namelen /= 2;
+ name = grub_malloc (namelen + 1);
+ if (!name)
+ return 0;
+ for (i = 0; i < 2 * namelen; i++)
+ {
+ int hex = 0;
+ while (grub_isspace (*ptr))
+ ptr++;
+ if (*ptr >= '0' && *ptr <= '9')
+ hex = *ptr - '0';
+ if (*ptr >= 'a' && *ptr <= 'f')
+ hex = *ptr - 'a' + 10;
+ if (*ptr >= 'A' && *ptr <= 'F')
+ hex = *ptr - 'A' + 10;
+
+ if (i % 2 == 0)
+ name[i / 2] = hex << 4;
+ else
+ name[i / 2] |= hex;
+ ptr++;
+ }
+ name [namelen] = 0;
+ while (grub_isspace (*ptr))
+ ptr++;
+
+ /* If it describes a key recursively invoke the function. */
+ if (*ptr == '{')
+ {
+ struct grub_xnu_devtree_key *newkey
+ = grub_xnu_create_key (parent, name);
+ grub_free (name);
+ if (! newkey)
+ return 0;
+ ptr = grub_xnu_parse_devtree (&(newkey->first_child), ptr + 1, end);
+ continue;
+ }
+
+ /* Parse the data. */
+ if (*ptr != ':')
+ return 0;
+ ptr++;
+ datalen = 0;
+ for (ptr2 = ptr; ptr2 < end && (grub_isspace (*ptr2)
+ || (*ptr2 >= '0' && *ptr2 <= '9')
+ || (*ptr2 >= 'a' && *ptr2 <= 'f')
+ || (*ptr2 >= 'A' && *ptr2 <= 'F'));
+ ptr2++)
+ if (! grub_isspace (*ptr2))
+ datalen++;
+ if (ptr2 == end)
+ return 0;
+ datalen /= 2;
+ data = grub_malloc (datalen);
+ if (! data)
+ return 0;
+ for (i = 0; i < 2 * datalen; i++)
+ {
+ int hex = 0;
+ while (grub_isspace (*ptr))
+ ptr++;
+ if (*ptr >= '0' && *ptr <= '9')
+ hex = *ptr - '0';
+ if (*ptr >= 'a' && *ptr <= 'f')
+ hex = *ptr - 'a' + 10;
+ if (*ptr >= 'A' && *ptr <= 'F')
+ hex = *ptr - 'A' + 10;
+
+ if (i % 2 == 0)
+ data[i / 2] = hex << 4;
+ else
+ data[i / 2] |= hex;
+ ptr++;
+ }
+ while (ptr < end && grub_isspace (*ptr))
+ ptr++;
+ {
+ struct grub_xnu_devtree_key *newkey
+ = grub_xnu_create_value (parent, name);
+ grub_free (name);
+ if (! newkey)
+ return 0;
+ newkey->datasize = datalen;
+ newkey->data = data;
+ }
+ if (*ptr != ';')
+ return 0;
+ ptr++;
+ }
+ if (ptr >= end && *parent != grub_xnu_devtree_root)
+ return 0;
+ return ptr;
+}
+
+/* Returns true if the kext should be loaded according to plist
+ and osbundlereq. Also fill BINNAME. */
+static int
+grub_xnu_check_os_bundle_required (char *plistname, char *osbundlereq,
+ char **binname)
+{
+ grub_file_t file;
+ char *buf = 0, *tagstart = 0, *ptr1 = 0, *keyptr = 0;
+ char *stringptr = 0, *ptr2 = 0;
+ grub_size_t size;
+ int depth = 0;
+ int ret;
+ int osbundlekeyfound = 0, binnamekeyfound = 0;
+ if (binname)
+ *binname = 0;
+
+ file = grub_gzfile_open (plistname, 1);
+ if (! file)
+ {
+ grub_file_close (file);
+ grub_error_push ();
+ grub_error (GRUB_ERR_BAD_OS, "Couldn't read file %s", plistname);
+ return 0;
+ }
+
+ size = grub_file_size (file);
+ buf = grub_malloc (size);
+ if (! buf)
+ {
+ grub_file_close (file);
+ grub_error_push ();
+ grub_error (GRUB_ERR_OUT_OF_MEMORY, "Couldn't read file %s", plistname);
+ return 0;
+ }
+ if (grub_file_read (file, buf, size) != (grub_ssize_t) (size))
+ {
+ grub_file_close (file);
+ grub_error_push ();
+ grub_error (GRUB_ERR_BAD_OS, "Couldn't read file %s", plistname);
+ return 0;
+ }
+ grub_file_close (file);
+
+ /* Set the return value for the case when no OSBundleRequired tag is found. */
+ if (osbundlereq)
+ ret = grub_strword (osbundlereq, "all") || grub_strword (osbundlereq, "-");
+ else
+ ret = 1;
+
+ /* Parse plist. It's quite dirty and inextensible but does its job. */
+ for (ptr1 = buf; ptr1 < buf + size; ptr1++)
+ switch (*ptr1)
+ {
+ case '<':
+ tagstart = ptr1;
+ *ptr1 = 0;
+ if (keyptr && depth == 4
+ && grub_strcmp (keyptr, "OSBundleRequired") == 0)
+ osbundlekeyfound = 1;
+ if (keyptr && depth == 4 &&
+ grub_strcmp (keyptr, "CFBundleExecutable") == 0)
+ binnamekeyfound = 1;
+ if (stringptr && osbundlekeyfound && osbundlereq && depth == 4)
+ {
+ for (ptr2 = stringptr; *ptr2; ptr2++)
+ *ptr2 = grub_tolower (*ptr2);
+ ret = grub_strword (osbundlereq, stringptr)
+ || grub_strword (osbundlereq, "all");
+ }
+ if (stringptr && binnamekeyfound && binname && depth == 4)
+ {
+ if (*binname)
+ grub_free (*binname);
+ *binname = grub_strdup (stringptr);
+ }
+
+ *ptr1 = '<';
+ keyptr = 0;
+ stringptr = 0;
+ break;
+ case '>':
+ if (! tagstart)
+ {
+ grub_free (buf);
+ grub_error (GRUB_ERR_BAD_OS, "can't parse %s", plistname);
+ return 0;
+ }
+ *ptr1 = 0;
+ if (tagstart[1] == '?' || ptr1[-1] == '/')
+ {
+ osbundlekeyfound = 0;
+ *ptr1 = '>';
+ break;
+ }
+ if (depth == 3 && grub_strcmp (tagstart + 1, "key") == 0)
+ keyptr = ptr1 + 1;
+ if (depth == 3 && grub_strcmp (tagstart + 1, "string") == 0)
+ stringptr = ptr1 + 1;
+ else if (grub_strcmp (tagstart + 1, "/key") != 0)
+ {
+ osbundlekeyfound = 0;
+ binnamekeyfound = 0;
+ }
+ *ptr1 = '>';
+
+ if (tagstart[1] == '/')
+ depth--;
+ else
+ depth++;
+ break;
+ }
+ grub_free (buf);
+
+ return ret;
+}
+
+/* Load all loadable kexts placed under DIRNAME and matching OSBUNDLEREQUIRED */
+grub_err_t
+grub_xnu_scan_dir_for_kexts (char *dirname, char *osbundlerequired,
+ int maxrecursion)
+{
+ grub_device_t dev;
+ char *device_name;
+ grub_fs_t fs;
+ const char *path;
+
+ auto int load_hook (const char *filename,
+ const struct grub_dirhook_info *info);
+ int load_hook (const char *filename, const struct grub_dirhook_info *info)
+ {
+ char *newdirname;
+ if (! info->dir)
+ return 0;
+ if (filename[0] == '.')
+ return 0;
+
+ if (grub_strlen (filename) < 5 ||
+ grub_memcmp (filename + grub_strlen (filename) - 5, ".kext", 5) != 0)
+ return 0;
+
+ newdirname
+ = grub_malloc (grub_strlen (dirname) + grub_strlen (filename) + 2);
+
+ /* It's a .kext. Try to load it. */
+ if (newdirname)
+ {
+ grub_strcpy (newdirname, dirname);
+ newdirname[grub_strlen (newdirname) + 1] = 0;
+ newdirname[grub_strlen (newdirname)] = '/';
+ grub_strcpy (newdirname + grub_strlen (newdirname), filename);
+ grub_xnu_load_kext_from_dir (newdirname, osbundlerequired,
+ maxrecursion);
+ if (grub_errno == GRUB_ERR_BAD_OS)
+ grub_errno = GRUB_ERR_NONE;
+ grub_free (newdirname);
+ }
+ return 0;
+ }
+
+ if (! grub_xnu_heap_size)
+ return grub_error (GRUB_ERR_BAD_OS, "no xnu kernel loaded");
+
+ device_name = grub_file_get_device_name (dirname);
+ dev = grub_device_open (device_name);
+ if (dev)
+ {
+ fs = grub_fs_probe (dev);
+ path = grub_strchr (dirname, ')');
+ if (! path)
+ path = dirname;
+ else
+ path++;
+
+ if (fs)
+ (fs->dir) (dev, path, load_hook);
+ grub_device_close (dev);
+ }
+ grub_free (device_name);
+
+ return GRUB_ERR_NONE;
+}
+
+/* Load extension DIRNAME. (extensions are directoris in xnu) */
+grub_err_t
+grub_xnu_load_kext_from_dir (char *dirname, char *osbundlerequired,
+ int maxrecursion)
+{
+ grub_device_t dev;
+ char *plistname = 0;
+ char *newdirname;
+ char *newpath;
+ char *device_name;
+ grub_fs_t fs;
+ const char *path;
+ char *binsuffix;
+ int usemacos = 0;
+ grub_file_t binfile;
+
+ auto int load_hook (const char *filename,
+ const struct grub_dirhook_info *info);
+
+ int load_hook (const char *filename, const struct grub_dirhook_info *info)
+ {
+ if (grub_strlen (filename) > 15)
+ return 0;
+ grub_strcpy (newdirname + grub_strlen (dirname) + 1, filename);
+
+ /* If the kext contains directory "Contents" all real stuff is in
+ this directory. */
+ if (info->dir && grub_strcasecmp (filename, "Contents") == 0)
+ grub_xnu_load_kext_from_dir (newdirname, osbundlerequired,
+ maxrecursion - 1);
+
+ /* Directory "Plugins" contains nested kexts. */
+ if (info->dir && grub_strcasecmp (filename, "Plugins") == 0)
+ grub_xnu_scan_dir_for_kexts (newdirname, osbundlerequired,
+ maxrecursion - 1);
+
+ /* Directory "MacOS" contains executable, otherwise executable is
+ on the top. */
+ if (info->dir && grub_strcasecmp (filename, "MacOS") == 0)
+ usemacos = 1;
+
+ /* Info.plist is the file which governs our future actions. */
+ if (! info->dir && grub_strcasecmp (filename, "Info.plist") == 0
+ && ! plistname)
+ plistname = grub_strdup (newdirname);
+ return 0;
+ }
+
+ newdirname = grub_malloc (grub_strlen (dirname) + 20);
+ if (! newdirname)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't allocate buffer");
+ grub_strcpy (newdirname, dirname);
+ newdirname[grub_strlen (dirname)] = '/';
+ newdirname[grub_strlen (dirname) + 1] = 0;
+ device_name = grub_file_get_device_name (dirname);
+ dev = grub_device_open (device_name);
+ if (dev)
+ {
+ fs = grub_fs_probe (dev);
+ path = grub_strchr (dirname, ')');
+ if (! path)
+ path = dirname;
+ else
+ path++;
+
+ newpath = grub_strchr (newdirname, ')');
+ if (! newpath)
+ newpath = newdirname;
+ else
+ newpath++;
+
+ /* Look at the directory. */
+ if (fs)
+ (fs->dir) (dev, path, load_hook);
+
+ if (plistname && grub_xnu_check_os_bundle_required
+ (plistname, osbundlerequired, &binsuffix))
+ {
+ if (binsuffix)
+ {
+ /* Open the binary. */
+ char *binname = grub_malloc (grub_strlen (dirname)
+ + grub_strlen (binsuffix)
+ + sizeof ("/MacOS/"));
+ grub_strcpy (binname, dirname);
+ if (usemacos)
+ grub_strcpy (binname + grub_strlen (binname), "/MacOS/");
+ else
+ grub_strcpy (binname + grub_strlen (binname), "/");
+ grub_strcpy (binname + grub_strlen (binname), binsuffix);
+ grub_dprintf ("xnu", "%s:%s\n", plistname, binname);
+ binfile = grub_gzfile_open (binname, 1);
+ if (! binfile)
+ grub_errno = GRUB_ERR_NONE;
+
+ /* Load the extension. */
+ grub_xnu_load_driver (plistname, binfile);
+ grub_free (binname);
+ grub_free (binsuffix);
+ }
+ else
+ {
+ grub_dprintf ("xnu", "%s:0\n", plistname);
+ grub_xnu_load_driver (plistname, 0);
+ }
+ }
+ grub_free (plistname);
+ grub_device_close (dev);
+ }
+ grub_free (device_name);
+
+ return GRUB_ERR_NONE;
+}
+
+/* Load devtree file. */
+static grub_err_t
+grub_cmd_xnu_devtree (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *args[])
+{
+ grub_file_t file;
+ char *data, *endret;
+ grub_size_t datalen;
+
+ if (argc != 1)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "Filename required");
+
+ if (! grub_xnu_heap_size)
+ return grub_error (GRUB_ERR_BAD_OS, "no xnu kernel loaded");
+
+ /* Load the file. */
+ file = grub_gzfile_open (args[0], 1);
+ if (! file)
+ return grub_error (GRUB_ERR_FILE_NOT_FOUND, "Couldn't load device tree");
+ datalen = grub_file_size (file);
+ data = grub_malloc (datalen + 1);
+ if (! data)
+ {
+ grub_file_close (file);
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "Could load device tree into memory");
+ }
+ if (grub_file_read (file, data, datalen) != (grub_ssize_t) datalen)
+ {
+ grub_file_close (file);
+ grub_free (data);
+ grub_error_push ();
+ return grub_error (GRUB_ERR_BAD_OS, "Couldn't read file %s", args[0]);
+ }
+ grub_file_close (file);
+ data[datalen] = 0;
+
+ /* Parse the file. */
+ endret = grub_xnu_parse_devtree (&grub_xnu_devtree_root,
+ data, data + datalen);
+ grub_free (data);
+
+ if (! endret)
+ return grub_error (GRUB_ERR_BAD_OS, "Couldn't parse devtree");
+
+ return GRUB_ERR_NONE;
+}
+
+static int locked=0;
+static grub_dl_t my_mod;
+
+/* Load the kext. */
+static grub_err_t
+grub_cmd_xnu_kext (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *args[])
+{
+ grub_file_t binfile = 0;
+ if (argc == 2)
+ {
+ /* User explicitely specified plist and binary. */
+ if (grub_strcmp (args[1], "-") != 0)
+ {
+ binfile = grub_gzfile_open (args[1], 1);
+ if (! binfile)
+ {
+ grub_error (GRUB_ERR_BAD_OS, "can't open file");
+ return GRUB_ERR_NONE;
+ }
+ }
+ return grub_xnu_load_driver (grub_strcmp (args[0], "-") ? args[0] : 0,
+ binfile);
+ }
+
+ /* load kext normally. */
+ if (argc == 1)
+ return grub_xnu_load_kext_from_dir (args[0], 0, 10);
+
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required");
+}
+
+/* Load a directory containing kexts. */
+static grub_err_t
+grub_cmd_xnu_kextdir (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *args[])
+{
+ if (argc != 1 && argc != 2)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "directory name required");
+
+ if (argc == 1)
+ return grub_xnu_scan_dir_for_kexts (args[0],
+ "console,root,local-root,network-root",
+ 10);
+ else
+ {
+ char *osbundlerequired = grub_strdup (args[1]), *ptr;
+ grub_err_t err;
+ if (! osbundlerequired)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "couldn't allocate string temporary space");
+ for (ptr = osbundlerequired; *ptr; ptr++)
+ *ptr = grub_tolower (*ptr);
+ err = grub_xnu_scan_dir_for_kexts (args[0], osbundlerequired, 10);
+ grub_free (osbundlerequired);
+ return err;
+ }
+}
+
+#ifndef GRUB_UTIL
+static grub_err_t
+grub_cmd_xnu_resume (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *args[])
+{
+ if (argc != 1)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required");
+
+ return grub_xnu_resume (args[0]);
+}
+#endif
+
+void
+grub_xnu_lock ()
+{
+#ifndef GRUB_UTIL
+ if (!locked)
+ grub_dl_ref (my_mod);
+#endif
+ locked = 1;
+}
+
+void
+grub_xnu_unlock ()
+{
+#ifndef GRUB_UTIL
+ if (locked)
+ grub_dl_unref (my_mod);
+#endif
+ locked = 0;
+}
+
+static grub_command_t cmd_kernel, cmd_mkext, cmd_kext, cmd_kextdir,
+ cmd_ramdisk, cmd_devtree, cmd_resume;
+
+GRUB_MOD_INIT(xnu)
+{
+ (void) mod; /* To stop warning. */
+ cmd_kernel = grub_register_command ("xnu_kernel", grub_cmd_xnu_kernel, 0,
+ "load a xnu kernel");
+ cmd_mkext = grub_register_command ("xnu_mkext", grub_cmd_xnu_mkext, 0,
+ "Load XNU extension package.");
+ cmd_kext = grub_register_command ("xnu_kext", grub_cmd_xnu_kext, 0,
+ "Load XNU extension.");
+ cmd_kextdir = grub_register_command ("xnu_kextdir", grub_cmd_xnu_kextdir,
+ "xnu_kextdir DIRECTORY [OSBundleRequired]",
+ "Load XNU extension directory");
+ cmd_ramdisk = grub_register_command ("xnu_ramdisk", grub_cmd_xnu_ramdisk, 0,
+ "Load XNU ramdisk. "
+ "It will be seen as md0");
+ cmd_devtree = grub_register_command ("xnu_devtree", grub_cmd_xnu_devtree, 0,
+ "Load XNU devtree");
+#ifndef GRUB_UTIL
+ cmd_resume = grub_register_command ("xnu_resume", grub_cmd_xnu_resume,
+ 0, "Load XNU hibernate image.");
+#endif
+ my_mod=mod;
+}
+
+GRUB_MOD_FINI(xnu)
+{
+#ifndef GRUB_UTIL
+ grub_unregister_command (cmd_resume);
+#endif
+ grub_unregister_command (cmd_mkext);
+ grub_unregister_command (cmd_kext);
+ grub_unregister_command (cmd_kextdir);
+ grub_unregister_command (cmd_devtree);
+ grub_unregister_command (cmd_ramdisk);
+ grub_unregister_command (cmd_kernel);
+}
diff --git a/loader/xnu_resume.c b/loader/xnu_resume.c
new file mode 100644
index 0000000..24dd77b
--- /dev/null
+++ b/loader/xnu_resume.c
@@ -0,0 +1,134 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/normal.h>
+#include <grub/dl.h>
+#include <grub/file.h>
+#include <grub/disk.h>
+#include <grub/misc.h>
+#include <grub/xnu.h>
+#include <grub/cpu/xnu.h>
+#include <grub/mm.h>
+#include <grub/loader.h>
+
+static void *grub_xnu_hibernate_image;
+
+static grub_err_t
+grub_xnu_resume_unload (void)
+{
+ /* Free loaded image */
+ if (grub_xnu_hibernate_image)
+ grub_free (grub_xnu_hibernate_image);
+ grub_xnu_hibernate_image = 0;
+ grub_xnu_unlock ();
+ return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_xnu_resume (char *imagename)
+{
+ grub_file_t file;
+ grub_size_t total_header_size;
+ struct grub_xnu_hibernate_header hibhead;
+ void *buf;
+
+#if GRUB_CPU_SIZEOF_VOID_P == 8
+ grub_uint64_t codedest;
+ grub_uint64_t codesize;
+#else
+ grub_uint32_t codedest;
+ grub_uint32_t codesize;
+#endif
+
+ file = grub_file_open (imagename);
+ if (! file)
+ return 0;
+
+ /* Read the header. */
+ if (grub_file_read (file, (char *) &hibhead, sizeof (hibhead))
+ !=sizeof (hibhead))
+ {
+ grub_file_close (file);
+ return grub_error (GRUB_ERR_READ_ERROR,
+ "cannot read the hibernate header");
+ }
+
+ /* Check the header. */
+ if (hibhead.magic != GRUB_XNU_HIBERNATE_MAGIC)
+ {
+ grub_file_close (file);
+ return grub_error (GRUB_ERR_BAD_OS,
+ "hibernate header has incorrect magic number");
+ }
+ if (hibhead.encoffset)
+ {
+ grub_file_close (file);
+ return grub_error (GRUB_ERR_BAD_OS,
+ "encrypted images aren't supported yet");
+ }
+
+ codedest = hibhead.launchcode_target_page;
+ codedest *= GRUB_XNU_PAGESIZE;
+ codesize = hibhead.launchcode_numpages;
+ codesize *= GRUB_XNU_PAGESIZE;
+
+ /* FIXME: check that codedest..codedest+codesize is available. */
+
+ /* Calculate total size before pages to copy. */
+ total_header_size = hibhead.extmapsize + sizeof (hibhead);
+
+ /* Unload image if any. */
+ if (grub_xnu_hibernate_image)
+ grub_free (grub_xnu_hibernate_image);
+
+ /* Try to allocate necessary space.
+ FIXME: mm isn't good enough yet to handle huge allocations.
+ */
+ grub_xnu_hibernate_image = buf = grub_malloc (hibhead.image_size);
+ if (! buf)
+ {
+ grub_file_close (file);
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "not enough memory to load image");
+ }
+
+ /* Read image. */
+ if (grub_file_seek (file, 0) == (grub_off_t)-1
+ || grub_file_read (file, buf, hibhead.image_size)
+ != (grub_ssize_t) hibhead.image_size)
+ {
+ grub_file_close (file);
+ return grub_error (GRUB_ERR_READ_ERROR, "Cannot read resume image.");
+ }
+ grub_file_close (file);
+
+ /* Move starting pages to appropriate location. */
+ memcpy ((void *) codedest, ((grub_uint8_t *) buf) + total_header_size,
+ codesize);
+
+ /* Setup variables needed by asm helper. */
+ grub_xnu_stack = (codedest + hibhead.stack);
+ grub_xnu_entry_point = (codedest + hibhead.entry_point);
+ grub_xnu_arg1 = (long) buf;
+
+ /* We're ready now. */
+ grub_loader_set (grub_xnu_launch, grub_xnu_resume_unload, 0);
+ /* Prevent module from unloading. */
+ grub_xnu_lock ();
+ return GRUB_ERR_NONE;
+}
^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [PATCH] xnu
2009-04-18 18:52 ` Vladimir Serbinenko
@ 2009-04-24 0:41 ` Joey Korkames
2009-04-24 7:28 ` Vladimir Serbinenko
0 siblings, 1 reply; 13+ messages in thread
From: Joey Korkames @ 2009-04-24 0:41 UTC (permalink / raw)
To: The development of GRUB 2
[-- Attachment #1: Type: text/plain, Size: 1823 bytes --]
This patch did not work as well as the last one posted (on my test machine). The last one got
all the way to trying to run /sbin/launchd off the ramdisk - log post of
this one's boot attempt are attached.
Oh, here is my updated menuentry, works well and gives me serial output
of the darwin kernel (as well as on the VGA monitor):
menuentry "PureDarwin 9 (phcoder efiemu)" {
echo "Switching to VESA and booting Darwin (slow)... "
#bootloader must tickle VESA before xnu can use it
loadfont /osstore/STAGE1a/grub2/unifont.pf2
set gfxmode="800x600x32"
set vbe_mode=0x115
terminal_output gfxterm
echo "VESA enabled. Booting Darwin (slow)... "
#efiemu_loadcore /osstore/STAGE1a/grub2/efiemu64.o
efiemu_loadcore /osstore/STAGE1a/grub2/efiemu32.o
efiemu_pnvram
efiemu_prepare
xnu_kernel /osstore/STAGE2/DARWIN/live/mach_kernel -s -f debug=0x08 serial=2 serialbaud=9600 rd=md0
xnu_mkext /osstore/STAGE2/DARWIN/live/System/Library/Extensions.mkext
xnu_ramdisk /osstore/STAGE2/DARWIN/ramdisk.hfs
# ^^30MB too big? (full /System/Library/Extensions) - yes, box will reboot before kernel turnover
# ^^if ramdisk is invalid, like FBSD, will attempt NFSMOUNT (to nowhere) - else, tries /sbin/launchd
}
Thanks
-joey
Vladimir Serbinenko writes:
> << HTML content follows >>
> New improved version. Numerous bugfixes. Thanks to Florian Idelberger for
> the time he spent on testing and to David Miller for pointing a nasty
> mistake out. Also I added fsb autodetection for intel cpus (thanks to
> Kabyl for the information about Intel speedstep) but I tested fsb
> autodetect only on my own laptop so it may or may not work in general.
> Parameter fsb= is still present and overrides autodetect
>
>
[-- Attachment #2: darwin-crash --]
[-- Type: text/plain, Size: 5228 bytes --]
Switching to VESA and booting Darwin (slow)...
Serial mode specified: 00000002
version_variant = 0
version = Darwin Kernel Version 9.5.0: Thu Sep 18 14:14:00 PDT 2008; root:xnu-1228.7.58.obj/RELEASE_I386
EM64T supported and will be enabled
EFI region: type = 7/7, base = 0x0, top = 0x88
EFI region: type = 8/8, base = 0x89, top = 0x9f
EFI region: type = 8/8, base = 0xca, top = 0xcb
EFI region: type = 8/8, base = 0xe4, top = 0xff
EFI region: type = 7/7, base = 0x100, top = 0x9bfbc
EFI region: type = 5/5, base = 0x9bfbd, top = 0x9bfbd
EFI region: type = 6/6, base = 0x9bfbe, top = 0x9bfc0
EFI region: type = 7/7, base = 0x9bfc1, top = 0xcff4f
EFI region: type = 9/9, base = 0xcff50, top = 0xcff5d
EFI region: type = 10/10, base = 0xcff5e, top = 0xcff5e
EFI region: type = 8/8, base = 0xcff5f, top = 0xcff7f
EFI region: type = 8/8, base = 0xcff80, top = 0xcffff
EFI region: type = 8/8, base = 0xe0000, top = 0xeffff
EFI region: type = 8/8, base = 0xfec00, top = 0xfec0f
EFI region: type = 8/8, base = 0xfee00, top = 0xfee00
EFI region: type = 8/8, base = 0xff000, top = 0xfffff
Physical memory 3328 MB
HIGH_MEM_BASE 0xffe00000 fixed per-cpu begin 0xffe17000
tramp: 0xffe00000, GDT: 0xffe02000, LDT: 0xffe04000, IDT: 0xffe03000, KTSS: 0xffe14000, DFTSS: 0xffe15000
MCTSS: 0xffe15000
gdt/idt reloaded, tr reset to KERNEL_TSS
Kernel virtual space from 0x0 to 0xfe7fffff.
64 bit mode enabled
Available physical space from 0xa58000 to 0xcff4f000
EFI_FSB_frequency: read FSBFrequency value: 100000000
BUS: Frequency = 100.0000MHz, cvtt2n = 0000000A.00000000, cvtn2t = 00000000.19999999, cvtInt = 0098967F.FFFFA920
TSC: Frequency = 700.0000MHz, cvtt2n = 00000001.6DB6DB6D, cvtn2t = 00000000.B3333333, gran = 7
RCBA: vaddr = 00A6E000, paddr = FED1C001
HPET: vaddr = 00A72000, paddr = FED00000
HPET: Frequency = 14.318179MHz, cvtt2n = 00000045.D75E0F7F, cvtn2t = 00000000.03AA5B32
initialize_screen: b=DC000000, w=00000320, h=00000258, r=00000C80, d=00000001
pmap_steal_memory: 00C4B000 - 00C4C000; size=00001000
pmap_steal_memory: 00C4C000 - 010C1000; size=00475000
pmap_steal_memory: 010C1000 - 010CE000; size=0000D000
pmap_steal_memory: 010CE000 - 014CE000; size=00400000
pmap_steal_memory: 014CE000 - 037F8AF0; size=0232AAF0
CPU identification: Intel(R) Xeon(R) CPU L5410 @ 2.33GHz
CPU features: FPU VME DE PSE TSC MSR PAE MCE CX8 APIC SEP MTRR PGE MCA CMOV PAT PSE36 CLFSH DS ACPI MMX FXSR SSE SSE2 SS HTT TM SSE3 MON DSCPL VMX EST TM2 SSSE3 CX16 TPR PDCM SSE4.1
HTT: 4 cores per package; 4 logical cpus per package
CPU extended features: XD EM64T
Initializing EFI runtime services
Boot args version 1 revision 4 mode 32
Processing 32-bit EFI tables at 0x9bfbe060
RuntimeServices table at 0x9bfbe000
MSR_IA32_APIC_BASE 0xfee00000 enabled BSP
Boot cpu local APIC id 0x0
[RTCLOCK] frequency 700000000 (700000000)
maxDec: 21474836470
Kernel boot args: '-s -f debug=0x08 serial=2 serialbaud=9600'
ACPI: RSDP @ 0xf5a50/0x0024 (v002 PTLTD )
ACPI: XSDT @ 0xcff55a96/0x0094 (v001 PTLTD XSDT 0x06040000 LTP 0x00000000)
ACPI: FACP @ 0xcff5dd9c/0x00F4 (v003 INTEL SANCLMNT 0x06040000 PTL 0x00000003)
ACPI: DSDT @ 0xcff575fc/0x671C (v001 Intel SANCLMNT 0x06040000 MSFT 0x03000001)
ACPI: FACS @ 0xcff5efc0/0x0040
ACPI: APIC @ 0xcff5de90/0x00BC (v001 PTLTD APIC 0x06040000 LTP 0x00000000)
ACPI: MCFG @ 0xcff5df4c/0x003C (v001 PTLTD MCFG 0x06040000 LTP 0x00000000)
ACPI: BOOT @ 0xcff5df88/0x0028 (v001 PTLTD $SBFTBL$ 0x06040000 LTP 0x00000001)
ACPI: SPCR @ 0xcff5dfb0/0x0050 (v001 PTLTD $UCRTBL$ 0x06040000 PTL 0x00000001)
ACPI: SSDT @ 0xcff5739d/0x025F (v001 PmRef Cpu0Tst 0x00003000 INTL 0x20061109)
ACPI: SSDT @ 0xcff572f7/0x00A6 (v001 PmRef Cpu7Tst 0x00003000 INTL 0x20061109)
ACPI: SSDT @ 0xcff57251/0x00A6 (v001 PmRef Cpu6Tst 0x00003000 INTL 0x20061109)
ACPI: SSDT @ 0xcff571ab/0x00A6 (v001 PmRef Cpu5Tst 0x00003000 INTL 0x20061109)
ACPI: SSDT @ 0xcff57105/0x00A6 (v001 PmRef Cpu4Tst 0x00003000 INTL 0x20061109)
ACPI: SSDT @ 0xcff5705f/0x00A6 (v001 PmRef Cpu3Tst 0x00003000 INTL 0x20061109)
ACPI: SSDT @ 0xcff56fb9/0x00A6 (v001 PmRef Cpu2Tst 0x00003000 INTL 0x20061109)
ACPI: SSDT @ 0xcff56f13/0x00A6 (v001 PmRef Cpu1Tst 0x00003000 INTL 0x20061109)
ACPI: SSDT @ 0xcff55b2a/0x13E9 (v001 PmRef CpuPm 0x00003000 INTL 0x20061109)
panic(cpu 0 caller 0x001AB328): "commpage no match on last routine"@/Volumes/work/darwin/9F33-roots/BuildRoot/SourceCache/xnu/xnu-1228.7.58/osfmk/i386/commpage/commpage.c:404
Debugger called: <panic>
Backtrace (CPU 0), Frame : Return Address (4 potential args on stack)
0x3fa6fe68 : 0x12b0fa (0x4598d8 0x3fa6fe9c 0x133243 0x0)
0x3fa6feb8 : 0x1ab328 (0x465a28 0x3fa6ff14 0x2000 0x0)
0x3fa6ff38 : 0x1ab531 (0xffff0000 0x524ee8 0x0 0x530f80)
0x3fa6ff88 : 0x18204b (0x4b83010 0x1 0x0 0x54a0c0)
0x3fa6ffa8 : 0x137dac (0x0 0x0 0x137d3d 0x0)
0x3fa6ffc8 : 0x19ecbc (0x0 0xffffffff 0x1a202a 0x0)
Backtrace terminated-invalid frame pointer 0
BSD process name corresponding to current thread: Unknown
Mac OS version:
Not yet set
Kernel version:
Darwin Kernel Version 9.5.0: Thu Sep 18 14:14:00 PDT 2008; root:xnu-1228.7.58.obj/RELEASE_I386
System model name: X7DCL
Attempting to commit panic log to NVRAM
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH] xnu
2009-04-24 0:41 ` Joey Korkames
@ 2009-04-24 7:28 ` Vladimir Serbinenko
2009-04-24 19:25 ` Joey Korkames
0 siblings, 1 reply; 13+ messages in thread
From: Vladimir Serbinenko @ 2009-04-24 7:28 UTC (permalink / raw)
To: The development of GRUB 2
[-- Attachment #1: Type: text/plain, Size: 2612 bytes --]
Your FSB wasn't detected correctly (or rather not detected at all, what's
your CPU?). The actual bug is that if fsb frequency is too low the kernel
will crash when trying to use 64-bit mode (you can use -legacy parameter to
put xnu in 32-bit mode). You need to specify fsb with a variable. Can you
come to irc.freenode.net#grub ?
(e.g. for 200Mhz: set fsb=200).
On Fri, Apr 24, 2009 at 2:41 AM, Joey Korkames
<joey+lists@kidfixit.com<joey%2Blists@kidfixit.com>
> wrote:
> This patch did not work as well as the last one posted (on my test
> machine). The last one got all the way to trying to run /sbin/launchd off
> the ramdisk - log post of this one's boot attempt are attached.
>
> Oh, here is my updated menuentry, works well and gives me serial output of
> the darwin kernel (as well as on the VGA monitor):
>
> menuentry "PureDarwin 9 (phcoder efiemu)" {
> echo "Switching to VESA and booting Darwin (slow)... "
> #bootloader must tickle VESA before xnu can use it
> loadfont /osstore/STAGE1a/grub2/unifont.pf2
> set gfxmode="800x600x32"
> set vbe_mode=0x115
> terminal_output gfxterm
> echo "VESA enabled. Booting Darwin (slow)... "
>
> #efiemu_loadcore /osstore/STAGE1a/grub2/efiemu64.o
> efiemu_loadcore /osstore/STAGE1a/grub2/efiemu32.o
> efiemu_pnvram
> efiemu_prepare
> xnu_kernel /osstore/STAGE2/DARWIN/live/mach_kernel -s -f debug=0x08
> serial=2 serialbaud=9600 rd=md0
>
-f parameter isn't actually used. Use xnu_kextdir if you don't want to use
mkext
>
> xnu_mkext /osstore/STAGE2/DARWIN/live/System/Library/Extensions.mkext
> xnu_ramdisk /osstore/STAGE2/DARWIN/ramdisk.hfs
> # ^^30MB too big? (full /System/Library/Extensions) - yes, box will
> reboot before kernel turnover
Can you give more info about it
>
> # ^^if ramdisk is invalid, like FBSD, will attempt NFSMOUNT (to
> nowhere) - else, tries /sbin/launchd
> }
>
> Thanks
> -joey
>
> Vladimir Serbinenko writes:
>
> << HTML content follows >>
>> New improved version. Numerous bugfixes. Thanks to Florian Idelberger for
>> the time he spent on testing and to David Miller for pointing a nasty
>> mistake out. Also I added fsb autodetection for intel cpus (thanks to Kabyl
>> for the information about Intel speedstep) but I tested fsb autodetect only
>> on my own laptop so it may or may not work in general. Parameter fsb= is
>> still present and overrides autodetect
>>
>>
>>
>
> _______________________________________________
> Grub-devel mailing list
> Grub-devel@gnu.org
> http://lists.gnu.org/mailman/listinfo/grub-devel
>
>
[-- Attachment #2: Type: text/html, Size: 3930 bytes --]
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH] xnu
2009-04-24 7:28 ` Vladimir Serbinenko
@ 2009-04-24 19:25 ` Joey Korkames
0 siblings, 0 replies; 13+ messages in thread
From: Joey Korkames @ 2009-04-24 19:25 UTC (permalink / raw)
To: The development of GRUB 2
Vladimir Serbinenko writes:
> << HTML content follows >>
> Your FSB wasn't detected correctly (or rather not detected at all, what's
> your CPU?). The actual bug is that if fsb frequency is too low the kernel
> will crash when trying to use 64-bit mode (you can use -legacy parameter
> to put xnu in 32-bit mode). You need to specify fsb with a variable. Can
> you come to <URL:http://irc.freenode.net#grub>irc.freenode.net#grub ?
> (e.g. for 200Mhz: set fsb=200).
>
Correct you are, It was a Intel Xeon L5410.
http://en.wikipedia.org/wiki/Xeon tells me it is a fsb=1333 and now the boot
gets to where it was on the first patch. Boot log at bottom.
My ramdisk isn't fully populated yet so the launchd error is not
(necessarily) a bug.
I'm lurking on #grub as joeykork.
>
> On Fri, Apr 24, 2009 at 2:41 AM, Joey Korkames
> <<URL:mailto:joey%2Blists@kidfixit.com>joey+lists@kidfixit.com> wrote:
>
> This patch did not work as well as the last one posted (on my test
> machine). The last one got all the way to trying to run /sbin/launchd
> off the ramdisk - log post of this one's boot attempt are attached.
>
> Oh, here is my updated menuentry, works well and gives me serial output
> of the darwin kernel (as well as on the VGA monitor):
>
>
> menuentry "PureDarwin 9 (phcoder efiemu)" {
> echo "Switching to VESA and booting Darwin (slow)... "
> #bootloader must tickle VESA before xnu can use it
>
> loadfont /osstore/STAGE1a/grub2/unifont.pf2
> set gfxmode="800x600x32"
> set vbe_mode=0x115
> terminal_output gfxterm
> echo "VESA enabled. Booting Darwin (slow)... "
>
> #efiemu_loadcore /osstore/STAGE1a/grub2/efiemu64.o
>
> efiemu_loadcore /osstore/STAGE1a/grub2/efiemu32.o
> efiemu_pnvram
> efiemu_prepare
> xnu_kernel /osstore/STAGE2/DARWIN/live/mach_kernel -s -f
> debug=0x08 serial=2 serialbaud=9600 rd=md0
>
> -f parameter isn't actually used. Use xnu_kextdir if you don't want to
> use mkext
Ok, dropping the -f. I don't think xnu_kextdir will work with (pxefs).
>
>
>
> xnu_mkext
> /osstore/STAGE2/DARWIN/live/System/Library/Extensions.mkext
> xnu_ramdisk /osstore/STAGE2/DARWIN/ramdisk.hfs
> # ^^30MB too big? (full /System/Library/Extensions) - yes, box
> will reboot before kernel turnover
>
> Can you give more info about it
I'll test again, that conclusion was before I had a stable menuentry. But basically,
putting a full /System/Library/Extensions folder from a PureDarwin iso into my ramdisk caused my
machine to reboot itself (triple-fault?) before I saw any kernel printfs.
Boot log:
----------
Switching to VESA and booting Darwin (slow)...
Serial mode specified: 00000002
version_variant = 0
version = Darwin Kernel Version 9.5.0: Thu Sep 18 14:14:00 PDT 2008; root:xnu-1228.7.58.obj/RELEASE_I386
EM64T supported and will be enabled
EFI region: type = 7/7, base = 0x0, top = 0x88
EFI region: type = 8/8, base = 0x89, top = 0x9f
EFI region: type = 8/8, base = 0xca, top = 0xcb
EFI region: type = 8/8, base = 0xe4, top = 0xff
EFI region: type = 7/7, base = 0x100, top = 0x9bfbc
EFI region: type = 5/5, base = 0x9bfbd, top = 0x9bfbd
EFI region: type = 6/6, base = 0x9bfbe, top = 0x9bfc0
EFI region: type = 7/7, base = 0x9bfc1, top = 0xcff4f
EFI region: type = 9/9, base = 0xcff50, top = 0xcff5d
EFI region: type = 10/10, base = 0xcff5e, top = 0xcff5e
EFI region: type = 8/8, base = 0xcff5f, top = 0xcff7f
EFI region: type = 8/8, base = 0xcff80, top = 0xcffff
EFI region: type = 8/8, base = 0xe0000, top = 0xeffff
EFI region: type = 8/8, base = 0xfec00, top = 0xfec0f
EFI region: type = 8/8, base = 0xfee00, top = 0xfee00
EFI region: type = 8/8, base = 0xff000, top = 0xfffff
Physical memory 3328 MB
HIGH_MEM_BASE 0xffe00000 fixed per-cpu begin 0xffe17000
tramp: 0xffe00000, GDT: 0xffe02000, LDT: 0xffe04000, IDT: 0xffe03000, KTSS: 0xffe14000, DFTSS: 0xffe15000
MCTSS: 0xffe15000
gdt/idt reloaded, tr reset to KERNEL_TSS
Kernel virtual space from 0x0 to 0xfe7fffff.
64 bit mode enabled
Available physical space from 0x1058000 to 0xcff4f000
EFI_FSB_frequency: read FSBFrequency value: 1333000000
BUS: Frequency = 1333.0000MHz, cvtt2n = 00000000.C00C4A83, cvtn2t = 00000001.553F7CEE, cvtInt = 000B726B.8BFE44F9
TSC: Frequency = 9331.0019MHz, cvtt2n = 00000000.1B6F785B, cvtn2t = 00000009.54BC6AD2, gran = 7
RCBA: vaddr = 0106E000, paddr = FED1C001
HPET: vaddr = 01072000, paddr = FED00000
HPET: Frequency = 14.318179MHz, cvtt2n = 00000045.D75E0F7F, cvtn2t = 00000000.03AA5B32
initialize_screen: b=DC000000, w=00000320, h=00000258, r=00000C80, d=00000001
pmap_steal_memory: 0124B000 - 0124C000; size=00001000
pmap_steal_memory: 0124C000 - 016BF000; size=00473000
pmap_steal_memory: 016BF000 - 016CC000; size=0000D000
pmap_steal_memory: 016CC000 - 01ACC000; size=00400000
pmap_steal_memory: 01ACC000 - 03DE6608; size=0231A608
CPU identification: Intel(R) Xeon(R) CPU L5410 @ 2.33GHz
CPU features: FPU VME DE PSE TSC MSR PAE MCE CX8 APIC SEP MTRR PGE MCA CMOV PAT PSE36 CLFSH DS ACPI MMX FXSR SSE SSE2 SS HTT TM SSE3 MON DSCPL VMX EST TM2 SSSE3 CX16 TPR PDCM SSE4.1
HTT: 4 cores per package; 4 logical cpus per package
CPU extended features: XD EM64T
Initializing EFI runtime services
Boot args version 1 revision 4 mode 32
Processing 32-bit EFI tables at 0x9bfbe060
RuntimeServices table at 0x9bfbe000
MSR_IA32_APIC_BASE 0xfee00000 enabled BSP
Boot cpu local APIC id 0x0
[RTCLOCK] frequency 9330000000 (9331000019)
maxDec: 1611015488
Kernel boot args: '-s -f debug=0x08 serial=2 serialbaud=9600 rd=md0'
ACPI: RSDP @ 0xf5a50/0x0024 (v002 PTLTD )
ACPI: XSDT @ 0xcff55a96/0x0094 (v001 PTLTD XSDT 0x06040000 LTP 0x00000000)
ACPI: FACP @ 0xcff5dd9c/0x00F4 (v003 INTEL SANCLMNT 0x06040000 PTL 0x00000003)
ACPI: DSDT @ 0xcff575fc/0x671C (v001 Intel SANCLMNT 0x06040000 MSFT 0x03000001)
ACPI: FACS @ 0xcff5efc0/0x0040
ACPI: APIC @ 0xcff5de90/0x00BC (v001 PTLTD APIC 0x06040000 LTP 0x00000000)
ACPI: MCFG @ 0xcff5df4c/0x003C (v001 PTLTD MCFG 0x06040000 LTP 0x00000000)
ACPI: BOOT @ 0xcff5df88/0x0028 (v001 PTLTD $SBFTBL$ 0x06040000 LTP 0x00000001)
ACPI: SPCR @ 0xcff5dfb0/0x0050 (v001 PTLTD $UCRTBL$ 0x06040000 PTL 0x00000001)
ACPI: SSDT @ 0xcff5739d/0x025F (v001 PmRef Cpu0Tst 0x00003000 INTL 0x20061109)
ACPI: SSDT @ 0xcff572f7/0x00A6 (v001 PmRef Cpu7Tst 0x00003000 INTL 0x20061109)
ACPI: SSDT @ 0xcff57251/0x00A6 (v001 PmRef Cpu6Tst 0x00003000 INTL 0x20061109)
ACPI: SSDT @ 0xcff571ab/0x00A6 (v001 PmRef Cpu5Tst 0x00003000 INTL 0x20061109)
ACPI: SSDT @ 0xcff57105/0x00A6 (v001 PmRef Cpu4Tst 0x00003000 INTL 0x20061109)
ACPI: SSDT @ 0xcff5705f/0x00A6 (v001 PmRef Cpu3Tst 0x00003000 INTL 0x20061109)
ACPI: SSDT @ 0xcff56fb9/0x00A6 (v001 PmRef Cpu2Tst 0x00003000 INTL 0x20061109)
ACPI: SSDT @ 0xcff56f13/0x00A6 (v001 PmRef Cpu1Tst 0x00003000 INTL 0x20061109)
ACPI: SSDT @ 0xcff55b2a/0x13E9 (v001 PmRef CpuPm 0x00003000 INTL 0x20061109)
cpu_data_alloc(1) 0x463c6000 desc_table: 0x4ef45000 ldt: 0x4ef48000 int_stack: 0x4ef41000-0x4ef45000
cpu_data_alloc(2) 0x463dd000 desc_table: 0x4ef70000 ldt: 0x4ef73000 int_stack: 0x4ef6c000-0x4ef70000
cpu_data_alloc(3) 0x4ef32000 desc_table: 0x4ef9b000 ldt: 0x4ef9e000 int_stack: 0x4ef97000-0x4ef9b000
cpu_data_alloc(4) 0x4efc4000 desc_table: 0x4efc9000 ldt: 0x4efcc000 int_stack: 0x4efc5000-0x4efc9000
cpu_data_alloc(5) 0x4eff4000 desc_table: 0x4eff9000 ldt: 0x4effc000 int_stack: 0x4eff5000-0x4eff9000
cpu_data_alloc(6) 0x4f024000 desc_table: 0x4f029000 ldt: 0x4f02c000 int_stack: 0x4f025000-0x4f029000
cpu_data_alloc(7) 0x4f054000 desc_table: 0x4f059000 ldt: 0x4f05c000 int_stack: 0x4f055000-0x4f059000
cpu_datap(1):0x463dd000 local apic id 0x1 remapped from 2
cpu_datap(2):0x4efc4000 local apic id 0x2 remapped from 4
cpu_datap(3):0x4f024000 local apic id 0x3 remapped from 6
cpu_datap(4):0x463c6000 local apic id 0x4 remapped from 1
cpu_datap(5):0x4ef32000 local apic id 0x5 remapped from 3
cpu_datap(6):0x4eff4000 local apic id 0x6 remapped from 5
Started cpu 1 (lapic id 00000001)
Started cpu 2 (lapic id 00000002)
Started cpu 3 (lapic id 00000003)
Started cpu 4 (lapic id 00000004)
Started cpu 5 (lapic id 00000005)
Started cpu 6 (lapic id 00000006)
Started cpu 7 (lapic id 00000007)
bsd_autoconf: calling kminit
Serial keyboard started
panic(cpu 3 caller 0x0036FE27): "Process 1 exec of /sbin/launchd failed, errno 2\n"@/Volumes/work/darwin/9F33-roots/BuildRoot/SourceCache/xnu/xnu-1228.7.58/bsd/kern/kern_exec.c:2885
Debugger called: <panic>
Backtrace (CPU 3), Frame : Return Address (4 potential args on stack)
0x4f1abe48 : 0x12b0fa (0x4598d8 0x4f1abe7c 0x133243 0x0)
0x4f1abe98 : 0x36fe27 (0x498bac 0x5196e4 0x2 0x0)
0x4f1abef8 : 0x3518d8 (0x53cac50 0x7e 0x52dcdac 0x80000001)
0x4f1abf38 : 0x37f30a (0x52e8ba0 0x8b0af0b4 0x1a236f 0x52e8c7c)
0x4f1abf68 : 0x1291aa (0x52e8ba0 0x
--------------------
(serial stopped here)
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH] xnu
2009-04-14 22:46 [PATCH] xnu phcoder
2009-04-17 23:22 ` Joey Korkames
2009-04-18 18:52 ` Vladimir Serbinenko
@ 2009-04-27 19:42 ` Vladimir 'phcoder' Serbinenko
2009-05-02 23:20 ` Vladimir 'phcoder' Serbinenko
2 siblings, 1 reply; 13+ messages in thread
From: Vladimir 'phcoder' Serbinenko @ 2009-04-27 19:42 UTC (permalink / raw)
To: The development of GRUB 2
[-- Attachment #1.1: Type: text/plain, Size: 2725 bytes --]
Update. No need to initialize efiemu manually anymore
On Wed, Apr 15, 2009 at 12:46 AM, phcoder <phcoder@gmail.com> wrote:
> Hello. Here is my xnu patch. Tested on i386-pc, i386-efi and x86_64-efi. On
> non-efi it needs efiemu otherwise you need only include/grub/autoefi.h file
> from efiemu patch. To resume xnu from hibernation do:
> xnu_resume <hibernation file>
> Note: you don't need efiemu in this case
> How to boot xnu:
> <efiemu if not on efi platform>
> [on pc only:] vbe_mode=0xYYY # desired vga mode
> fsb=133.3 # your fsb frequency
> xnu_kernel <kernel> <command line>
> <insert modules>
> boot
> Modules can be inserted one of the following ways:
> 1) xnu_mkext <mkext file>
> 2) xnu_kext extension.kext
> 3) xnu_kext extension.kext/Info.plist extension.kext/extension
> 4) xnu_kextdir <directory containing extensions>
> It's also possible to execute these commands multiple times
> The most typical case is
> xnu_kernel /mach_kernel rd=disk0sX
> xnu_mkext /System/Library/Extensions.mkext
>
>
> If you need to add values to device tree the command
> xnu_devtree <devtree file>
> This file uses the following format:
> valuename:valuedata;
> keyname{
> contents
> }
> keyname, valuename and valuedata are in hex.
>
> If you need to adda ramdisk execute
> xnu_ramdisk <ramdisk file>
> ramdisk will be exposed as /dev/md0 which may be used as boot device with
> rd=md0.
>
> The areas which need more work (every help is welcome):
> 1) testing on different platforms
> 2) Detect "device-properties" value in device tree. There are several
> trivial values in device tree present in boot.efi but not in grub2. These
> ones are easy to add and AFAIK don't change anything. But the value
> "device-properties" is difficult. I already know it's format but not where
> the values come from. It contains info about gfx and sound card which may
> not work if this value is missing. The current workaround is xnu_devtree
> command with a dump of device-properties
> 3) autodetect fsb frequency
> 4) Support video splash
> 5) Define and use an unified interface to retrieve video information
> uniformly across platforms
> 6) Boot by UUID. I know how to do it but need md5 for it which is a part of
> pending luks patch
> 7) Scripts for automatic creating of grub.cfg entries
> 8) Support for prelinked kernel. It's compressed and I have yet looked how
> to decompress it (seems it's compressed with something called lzss)
> 9) Use claimmap once available (see my multiboot on efi patch)
> 10) Better collaboration with memory management once advanced mm's
> available
> 11) Resume from encrypted hibernation
>
> --
>
> Regards
> Vladimir 'phcoder' Serbinenko
>
--
Regards
Vladimir 'phcoder' Serbinenko
[-- Attachment #1.2: Type: text/html, Size: 3296 bytes --]
[-- Attachment #2: xnu.diff --]
[-- Type: text/x-patch, Size: 99821 bytes --]
diff --git a/conf/i386-efi.rmk b/conf/i386-efi.rmk
index 3c8c62d..1f91e08 100644
--- a/conf/i386-efi.rmk
+++ b/conf/i386-efi.rmk
@@ -99,7 +99,7 @@ kernel_mod_SOURCES = kern/i386/efi/startup.S kern/main.c kern/device.c \
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 rescue.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)
@@ -201,5 +201,12 @@ fixvideo_mod_SOURCES = commands/efi/fixvideo.c
fixvideo_mod_CFLAGS = $(COMMON_CFLAGS)
fixvideo_mod_LDFLAGS = $(COMMON_LDFLAGS)
+pkglib_MODULES += xnu.mod
+xnu_mod_SOURCES = loader/xnu_resume.c loader/i386/xnu.c loader/i386/efi/xnu.c\
+ loader/macho.c loader/xnu.c loader/i386/xnu_helper.S
+xnu_mod_CFLAGS = $(COMMON_CFLAGS) -Werror -Wall
+xnu_mod_LDFLAGS = $(COMMON_LDFLAGS)
+xnu_mod_ASFLAGS = $(COMMON_ASFLAGS)
+
include $(srcdir)/conf/i386.mk
include $(srcdir)/conf/common.mk
diff --git a/conf/i386-pc.rmk b/conf/i386-pc.rmk
index 2893fb9..0011e9d 100644
--- a/conf/i386-pc.rmk
+++ b/conf/i386-pc.rmk
@@ -61,7 +61,7 @@ kernel_img_HEADERS = boot.h cache.h device.h disk.h dl.h elf.h elfload.h \
partition.h pc_partition.h rescue.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)
@@ -227,6 +227,13 @@ linux_mod_SOURCES = loader/i386/linux.c
linux_mod_CFLAGS = $(COMMON_CFLAGS)
linux_mod_LDFLAGS = $(COMMON_LDFLAGS)
+pkglib_MODULES += xnu.mod
+xnu_mod_SOURCES = loader/xnu_resume.c loader/i386/xnu.c loader/i386/pc/xnu.c\
+ loader/macho.c loader/xnu.c loader/i386/xnu_helper.S
+xnu_mod_CFLAGS = $(COMMON_CFLAGS) -Werror -Wall
+xnu_mod_LDFLAGS = $(COMMON_LDFLAGS)
+xnu_mod_ASFLAGS = $(COMMON_ASFLAGS)
+
#
# Only arch dependant part of normal.mod will be here. Common part for
# all architecures of normal.mod is at start and should be kept at sync
diff --git a/conf/x86_64-efi.rmk b/conf/x86_64-efi.rmk
index d19649b..bc1f511 100644
--- a/conf/x86_64-efi.rmk
+++ b/conf/x86_64-efi.rmk
@@ -100,7 +100,7 @@ kernel_mod_SOURCES = kern/x86_64/efi/startup.S kern/x86_64/efi/callwrap.S \
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 rescue.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 \
+ 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)
@@ -203,5 +203,12 @@ fixvideo_mod_SOURCES = commands/efi/fixvideo.c
fixvideo_mod_CFLAGS = $(COMMON_CFLAGS)
fixvideo_mod_LDFLAGS = $(COMMON_LDFLAGS)
+pkglib_MODULES += xnu.mod
+xnu_mod_SOURCES = loader/xnu_resume.c loader/i386/xnu.c loader/i386/efi/xnu.c\
+ loader/macho.c loader/xnu.c loader/i386/xnu_helper.S
+xnu_mod_CFLAGS = $(COMMON_CFLAGS) -Werror -Wall
+xnu_mod_LDFLAGS = $(COMMON_LDFLAGS)
+xnu_mod_ASFLAGS = $(COMMON_ASFLAGS)
+
include $(srcdir)/conf/i386.mk
include $(srcdir)/conf/common.mk
diff --git a/include/grub/efi/efi.h b/include/grub/efi/efi.h
index 8c277c0..916f9d6 100644
--- a/include/grub/efi/efi.h
+++ b/include/grub/efi/efi.h
@@ -56,6 +56,7 @@ EXPORT_FUNC(grub_efi_get_device_path) (grub_efi_handle_t handle);
int EXPORT_FUNC(grub_efi_exit_boot_services) (grub_efi_uintn_t map_key);
void EXPORT_FUNC (grub_reboot) (void);
void EXPORT_FUNC (grub_halt) (void);
+int EXPORT_FUNC (grub_efi_finish_boot_services) (void);
void grub_efi_mm_init (void);
void grub_efi_mm_fini (void);
diff --git a/include/grub/i386/macho.h b/include/grub/i386/macho.h
new file mode 100644
index 0000000..61e72a7
--- /dev/null
+++ b/include/grub/i386/macho.h
@@ -0,0 +1,11 @@
+#define GRUB_MACHO_CPUTYPE_IS_HOST32(x) ((x)==0x00000007)
+#define GRUB_MACHO_CPUTYPE_IS_HOST64(x) ((x)==0x01000007)
+
+struct grub_macho_thread32
+{
+ grub_uint32_t cmd;
+ grub_uint32_t cmdsize;
+ grub_uint8_t unknown1[48];
+ grub_uint32_t entry_point;
+ grub_uint8_t unknown2[20];
+} __attribute__ ((packed));
diff --git a/include/grub/i386/pit.h b/include/grub/i386/pit.h
index 0da271d..ff9b9a6 100644
--- a/include/grub/i386/pit.h
+++ b/include/grub/i386/pit.h
@@ -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 */
diff --git a/include/grub/i386/xnu.h b/include/grub/i386/xnu.h
new file mode 100644
index 0000000..c4c10b0
--- /dev/null
+++ b/include/grub/i386/xnu.h
@@ -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
diff --git a/include/grub/macho.h b/include/grub/macho.h
new file mode 100644
index 0000000..7dfd54e
--- /dev/null
+++ b/include/grub/macho.h
@@ -0,0 +1,107 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GRUB_MACHO_H
+#define GRUB_MACHO_H 1
+#include <grub/types.h>
+
+/* Multi-architecture header. Always in big-endian. */
+struct grub_macho_fat_header
+{
+ grub_uint32_t magic;
+ grub_uint32_t nfat_arch;
+} __attribute__ ((packed));
+#define GRUB_MACHO_FAT_MAGIC 0xcafebabe
+
+typedef grub_uint32_t grub_macho_cpu_type_t;
+typedef grub_uint32_t grub_macho_cpu_subtype_t;
+
+/* Architecture descriptor. Always in big-endian. */
+struct grub_macho_fat_arch
+{
+ grub_macho_cpu_type_t cputype;
+ grub_macho_cpu_subtype_t cpusubtype;
+ grub_uint32_t offset;
+ grub_uint32_t size;
+ grub_uint32_t align;
+} __attribute__ ((packed));;
+
+/* File header for 32-bit. Always in native-endian. */
+struct grub_macho_header32
+{
+#define GRUB_MACHO_MAGIC32 0xfeedface
+ grub_uint32_t magic;
+ grub_macho_cpu_type_t cputype;
+ grub_macho_cpu_subtype_t cpusubtype;
+ grub_uint32_t filetype;
+ grub_uint32_t ncmds;
+ grub_uint32_t sizeofcmds;
+ grub_uint32_t flags;
+} __attribute__ ((packed));
+
+/* File header for 64-bit. Always in native-endian. */
+struct grub_macho_header64
+{
+#define GRUB_MACHO_MAGIC64 0xfeedfacf
+ grub_uint32_t magic;
+ grub_macho_cpu_type_t cputype;
+ grub_macho_cpu_subtype_t cpusubtype;
+ grub_uint32_t filetype;
+ grub_uint32_t ncmds;
+ grub_uint32_t sizeofcmds;
+ grub_uint32_t flags;
+ grub_uint32_t reserved;
+} __attribute__ ((packed));
+
+/* Convenience union. What do we need to load to identify the file type. */
+union grub_macho_filestart
+{
+ struct grub_macho_fat_header fat;
+ struct grub_macho_header32 thin32;
+ struct grub_macho_header64 thin64;
+} __attribute__ ((packed));
+
+/* Common header of Mach-O commands. */
+struct grub_macho_cmd
+{
+ grub_uint32_t cmd;
+ grub_uint32_t cmdsize;
+} __attribute__ ((packed));
+
+typedef grub_uint32_t grub_macho_vmprot_t;
+
+/* 32-bit segment command. */
+struct grub_macho_segment32
+{
+#define GRUB_MACHO_CMD_SEGMENT32 1
+ grub_uint32_t cmd;
+ grub_uint32_t cmdsize;
+ grub_uint8_t segname[16];
+ grub_uint32_t vmaddr;
+ grub_uint32_t vmsize;
+ grub_uint32_t fileoff;
+ grub_uint32_t filesize;
+ grub_macho_vmprot_t maxprot;
+ grub_macho_vmprot_t initprot;
+ grub_uint32_t nsects;
+ grub_uint32_t flags;
+} __attribute__ ((packed));
+
+#define GRUB_MACHO_CMD_THREAD 5
+
+#endif
diff --git a/include/grub/machoload.h b/include/grub/machoload.h
new file mode 100644
index 0000000..572496f
--- /dev/null
+++ b/include/grub/machoload.h
@@ -0,0 +1,62 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GRUB_MACHOLOAD_HEADER
+#define GRUB_MACHOLOAD_HEADER 1
+
+#include <grub/err.h>
+#include <grub/elf.h>
+#include <grub/file.h>
+#include <grub/symbol.h>
+#include <grub/types.h>
+
+struct grub_macho_file
+{
+ grub_file_t file;
+ grub_ssize_t offset32;
+ grub_ssize_t end32;
+ int ncmds32;
+ grub_size_t cmdsize32;
+ grub_uint8_t *cmds32;
+ grub_ssize_t offset64;
+ grub_ssize_t end64;
+ int ncmds64;
+ grub_size_t cmdsize64;
+ grub_uint8_t *cmds64;
+};
+typedef struct grub_macho_file *grub_macho_t;
+
+grub_macho_t grub_macho_open (const char *);
+grub_macho_t grub_macho_file (grub_file_t);
+grub_err_t grub_macho_close (grub_macho_t);
+
+int grub_macho_contains_macho32 (grub_macho_t);
+grub_err_t grub_macho32_size (grub_macho_t macho, grub_addr_t *segments_start,
+ grub_addr_t *segments_end, int flags);
+grub_uint32_t grub_macho32_get_entry_point (grub_macho_t macho);
+
+/* Ignore BSS segments when loading. */
+#define GRUB_MACHO_NOBSS 0x1
+grub_err_t grub_macho32_load (grub_macho_t macho, char *offset, int flags);
+
+/* Like filesize and file_read but take only 32-bit part
+ for current architecture. */
+grub_size_t grub_macho32_filesize (grub_macho_t macho);
+grub_err_t grub_macho32_readfile (grub_macho_t macho, void *dest);
+
+#endif /* ! GRUB_MACHOLOAD_HEADER */
diff --git a/include/grub/x86_64/macho.h b/include/grub/x86_64/macho.h
new file mode 100644
index 0000000..165b8da
--- /dev/null
+++ b/include/grub/x86_64/macho.h
@@ -0,0 +1 @@
+#include <grub/i386/macho.h>
diff --git a/include/grub/x86_64/xnu.h b/include/grub/x86_64/xnu.h
new file mode 100644
index 0000000..ae61733
--- /dev/null
+++ b/include/grub/x86_64/xnu.h
@@ -0,0 +1 @@
+#include <grub/i386/xnu.h>
diff --git a/include/grub/xnu.h b/include/grub/xnu.h
new file mode 100644
index 0000000..beb9de9
--- /dev/null
+++ b/include/grub/xnu.h
@@ -0,0 +1,104 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GRUB_XNU_H
+#define GRUB_XNU_H 1
+
+/* Header of a hibernation image. */
+struct grub_xnu_hibernate_header
+{
+ /* Size of the image. Notice that file containing image is usually bigger. */
+ grub_uint64_t image_size;
+ grub_uint8_t unknown1[8];
+ /* Where to copy lauchcode?*/
+ grub_uint32_t launchcode_target_page;
+ /* How many pages of launchcode? */
+ grub_uint32_t launchcode_numpages;
+ /* Where to jump? */
+ grub_uint32_t entry_point;
+ /* %esp at start. */
+ grub_uint32_t stack;
+ grub_uint8_t unknown2[44];
+#define GRUB_XNU_HIBERNATE_MAGIC 0x73696d65
+ grub_uint32_t magic;
+ grub_uint8_t unknown3[28];
+ /* This value is non-zero if page is encrypted. Unsupported. */
+ grub_uint64_t encoffset;
+ grub_uint8_t unknown4[360];
+ /* The size of additional header used to locate image without parsing FS.
+ Used only to skip it.
+ */
+ grub_uint32_t extmapsize;
+} __attribute__ ((packed));
+
+/* In-memory structure for temporary keeping device tree. */
+struct grub_xnu_devtree_key
+{
+ char *name;
+ int datasize; /* -1 for not leaves. */
+ union
+ {
+ struct grub_xnu_devtree_key *first_child;
+ void *data;
+ };
+ struct grub_xnu_devtree_key *next;
+};
+
+/* A structure used in memory-map values. */
+struct
+grub_xnu_extdesc
+{
+ grub_uint32_t addr;
+ grub_uint32_t size;
+} __attribute__ ((packed));
+
+/* Header describing extension in the memory. */
+struct grub_xnu_extheader
+{
+ grub_uint32_t infoplistaddr;
+ grub_uint32_t infoplistsize;
+ grub_uint32_t binaryaddr;
+ grub_uint32_t binarysize;
+} __attribute__ ((packed));
+
+struct grub_xnu_devtree_key *grub_xnu_create_key (struct grub_xnu_devtree_key **parent,
+ char *name);
+
+extern struct grub_xnu_devtree_key *grub_xnu_devtree_root;
+
+void grub_xnu_free_devtree (struct grub_xnu_devtree_key *cur);
+
+grub_err_t grub_xnu_writetree_toheap (void **start, grub_size_t *size);
+struct grub_xnu_devtree_key *grub_xnu_create_value (struct grub_xnu_devtree_key **parent,
+ char *name);
+
+void grub_xnu_lock (void);
+void grub_xnu_unlock (void);
+grub_err_t grub_xnu_resume (char *imagename);
+struct grub_xnu_devtree_key *grub_xnu_find_key (struct grub_xnu_devtree_key *parent,
+ char *name);
+grub_err_t grub_xnu_align_heap (int align);
+grub_err_t grub_xnu_scan_dir_for_kexts (char *dirname, char *osbundlerequired,
+ int maxrecursion);
+grub_err_t grub_xnu_load_kext_from_dir (char *dirname, char *osbundlerequired,
+ int maxrecursion);
+void *grub_xnu_heap_malloc (int size);
+extern grub_uint32_t grub_xnu_heap_real_start;
+extern grub_size_t grub_xnu_heap_size;
+extern char *grub_xnu_heap_start;
+#endif
diff --git a/kern/efi/efi.c b/kern/efi/efi.c
index 9c9a400..25007e3 100644
--- a/kern/efi/efi.c
+++ b/kern/efi/efi.c
@@ -734,3 +734,26 @@ grub_efi_print_device_path (grub_efi_device_path_t *dp)
dp = (grub_efi_device_path_t *) ((char *) dp + len);
}
}
+
+int
+grub_efi_finish_boot_services (void)
+{
+ grub_efi_uintn_t mmap_size = 0;
+ grub_efi_uintn_t map_key;
+ grub_efi_uintn_t desc_size;
+ grub_efi_uint32_t desc_version;
+ void *mmap_buf = 0;
+
+ if (grub_efi_get_memory_map (&mmap_size, mmap_buf, &map_key,
+ &desc_size, &desc_version) < 0)
+ return 0;
+
+ mmap_buf = grub_malloc (mmap_size);
+
+ if (grub_efi_get_memory_map (&mmap_size, mmap_buf, &map_key,
+ &desc_size, &desc_version) <= 0)
+ return 0;
+
+ return grub_efi_exit_boot_services (map_key);
+}
+
diff --git a/kern/efi/mm.c b/kern/efi/mm.c
index 35b12ab..4635776 100644
--- a/kern/efi/mm.c
+++ b/kern/efi/mm.c
@@ -47,7 +47,7 @@ static struct allocated_page *allocated_pages = 0;
/* The minimum and maximum heap size for GRUB itself. */
#define MIN_HEAP_SIZE 0x100000
-#define MAX_HEAP_SIZE (16 * 0x100000)
+#define MAX_HEAP_SIZE (1600 * 0x100000)
/* Allocate pages. Return the pointer to the first of allocated pages. */
diff --git a/loader/i386/efi/xnu.c b/loader/i386/efi/xnu.c
new file mode 100644
index 0000000..5085cdb
--- /dev/null
+++ b/loader/i386/efi/xnu.c
@@ -0,0 +1,177 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/env.h>
+#include <grub/xnu.h>
+#include <grub/cpu/xnu.h>
+#include <grub/efi/api.h>
+#include <grub/efi/efi.h>
+#include <grub/efi/uga_draw.h>
+#include <grub/pci.h>
+#include <grub/misc.h>
+
+/* Setup video for xnu. Big parts are copied from linux.c. */
+
+static grub_efi_guid_t uga_draw_guid = GRUB_EFI_UGA_DRAW_GUID;
+
+#define RGB_MASK 0xffffff
+#define RGB_MAGIC 0x121314
+#define LINE_MIN 800
+#define LINE_MAX 4096
+#define FBTEST_STEP (0x10000 >> 2)
+#define FBTEST_COUNT 8
+
+static int
+find_line_len (grub_uint32_t *fb_base, grub_uint32_t *line_len)
+{
+ grub_uint32_t *base = (grub_uint32_t *) (grub_target_addr_t) *fb_base;
+ int i;
+
+ for (i = 0; i < FBTEST_COUNT; i++, base += FBTEST_STEP)
+ {
+ if ((*base & RGB_MASK) == RGB_MAGIC)
+ {
+ int j;
+
+ for (j = LINE_MIN; j <= LINE_MAX; j++)
+ {
+ if ((base[j] & RGB_MASK) == RGB_MAGIC)
+ {
+ *fb_base = (grub_uint32_t) (grub_target_addr_t) base;
+ *line_len = j << 2;
+
+ return 1;
+ }
+ }
+
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int
+find_framebuf (grub_uint32_t *fb_base, grub_uint32_t *line_len)
+{
+ int found = 0;
+
+ auto int NESTED_FUNC_ATTR find_card (int bus, int dev, int func,
+ grub_pci_id_t pciid);
+
+ int NESTED_FUNC_ATTR find_card (int bus, int dev, int func,
+ grub_pci_id_t pciid)
+ {
+ grub_pci_address_t addr;
+
+ addr = grub_pci_make_address (bus, dev, func, 2);
+ if (grub_pci_read (addr) >> 24 == 0x3)
+ {
+ int i;
+
+ grub_printf ("Display controller: %d:%d.%d\nDevice id: %x\n",
+ bus, dev, func, pciid);
+ addr += 8;
+ for (i = 0; i < 6; i++, addr += 4)
+ {
+ grub_uint32_t old_bar1, old_bar2, type;
+ grub_uint64_t base64;
+
+ old_bar1 = grub_pci_read (addr);
+ if ((! old_bar1) || (old_bar1 & GRUB_PCI_ADDR_SPACE_IO))
+ continue;
+
+ type = old_bar1 & GRUB_PCI_ADDR_MEM_TYPE_MASK;
+ if (type == GRUB_PCI_ADDR_MEM_TYPE_64)
+ {
+ if (i == 5)
+ break;
+
+ old_bar2 = grub_pci_read (addr + 4);
+ }
+ else
+ old_bar2 = 0;
+
+ base64 = old_bar2;
+ base64 <<= 32;
+ base64 |= (old_bar1 & GRUB_PCI_ADDR_MEM_MASK);
+
+ grub_printf ("%s(%d): 0x%llx\n",
+ ((old_bar1 & GRUB_PCI_ADDR_MEM_PREFETCH) ?
+ "VMEM" : "MMIO"), i,
+ (unsigned long long) base64);
+
+ if ((old_bar1 & GRUB_PCI_ADDR_MEM_PREFETCH) && (! found))
+ {
+ *fb_base = base64;
+ if (find_line_len (fb_base, line_len))
+ found++;
+ }
+
+ if (type == GRUB_PCI_ADDR_MEM_TYPE_64)
+ {
+ i++;
+ addr += 4;
+ }
+ }
+ }
+
+ return found;
+ }
+
+ grub_pci_iterate (find_card);
+ return found;
+}
+
+grub_err_t
+grub_xnu_set_video (struct grub_xnu_boot_params *params)
+{
+ grub_efi_uga_draw_protocol_t *c;
+ grub_uint32_t width, height, depth, rate, pixel, fb_base, line_len;
+ int ret;
+
+ c = grub_efi_locate_protocol (&uga_draw_guid, 0);
+ if (! c)
+ return grub_error (GRUB_ERR_IO, "Couldn't find UGADraw");
+
+ if (efi_call_5 (c->get_mode, c, &width, &height, &depth, &rate))
+ return grub_error (GRUB_ERR_IO, "Couldn't retrieve video mode");
+
+ grub_printf ("Video mode: %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;
+}
diff --git a/loader/i386/pc/xnu.c b/loader/i386/pc/xnu.c
new file mode 100644
index 0000000..2c78b2b
--- /dev/null
+++ b/loader/i386/pc/xnu.c
@@ -0,0 +1,67 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/env.h>
+#include <grub/misc.h>
+#include <grub/xnu.h>
+#include <grub/cpu/xnu.h>
+#include <grub/machine/vbe.h>
+#include <grub/machine/vga.h>
+
+/* Setup video for xnu. */
+
+grub_err_t
+grub_xnu_set_video (struct grub_xnu_boot_params *bootparams_relloc)
+{
+ grub_uint32_t use_mode = GRUB_VBE_DEFAULT_VIDEO_MODE;
+ char *modevar;
+ grub_err_t err;
+ struct grub_vbe_mode_info_block mode_info;
+
+ /* Check existence of vbe_mode environment variable. */
+ modevar = grub_env_get ("vbe_mode");
+ if (modevar != 0)
+ {
+ unsigned long value;
+
+ value = grub_strtoul (modevar, 0, 0);
+ if (grub_errno == GRUB_ERR_NONE)
+ use_mode = value;
+ else
+ grub_errno = GRUB_ERR_NONE;
+ }
+
+ if (use_mode < 0x100)
+ return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+ "booting xnu in text mode isn't supported yet");
+ err = grub_vbe_set_video_mode (use_mode, &mode_info);
+ if (err != GRUB_ERR_NONE)
+ return err;
+
+ /* FIXME: setting non-32bit color depth results in strange screens. */
+ if (use_mode >= 0x100)
+ {
+ bootparams_relloc->lfb_base = mode_info.phys_base_addr;
+ bootparams_relloc->lfb_mode = GRUB_XNU_VIDEO_TEXT_IN_VIDEO;
+ bootparams_relloc->lfb_line_len = mode_info.bytes_per_scan_line;
+ bootparams_relloc->lfb_width = mode_info.x_resolution;
+ bootparams_relloc->lfb_height = mode_info.y_resolution;
+ bootparams_relloc->lfb_depth = mode_info.bits_per_pixel;
+ }
+ return GRUB_ERR_NONE;
+}
diff --git a/loader/i386/xnu.c b/loader/i386/xnu.c
new file mode 100644
index 0000000..0860160
--- /dev/null
+++ b/loader/i386/xnu.c
@@ -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;
+}
diff --git a/loader/i386/xnu_helper.S b/loader/i386/xnu_helper.S
new file mode 100644
index 0000000..77b0a66
--- /dev/null
+++ b/loader/i386/xnu_helper.S
@@ -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)
diff --git a/loader/macho.c b/loader/macho.c
new file mode 100644
index 0000000..da081a2
--- /dev/null
+++ b/loader/macho.c
@@ -0,0 +1,395 @@
+/* macho.c - load Mach-O files. */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* This Mach-O loader is incomplete and can load only non-relocatable segments.
+ This is however enough to boot xnu (otool -l and Mach-O specs for more info).
+*/
+
+#include <grub/err.h>
+#include <grub/macho.h>
+#include <grub/cpu/macho.h>
+#include <grub/machoload.h>
+#include <grub/file.h>
+#include <grub/gzio.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+
+#define min(a,b) (((a) < (b)) ? (a) : (b))
+
+/* 32-bit. */
+
+int
+grub_macho_contains_macho32 (grub_macho_t macho)
+{
+ return macho->offset32 != -1;
+}
+
+static void
+grub_macho_parse32 (grub_macho_t macho)
+{
+ struct grub_macho_header32 head;
+
+ /* Is there any candidate at all? */
+ if (macho->offset32 == -1)
+ return;
+
+ /* Read header and check magic*/
+ if (grub_file_seek (macho->file, macho->offset32) == (grub_off_t) -1
+ || grub_file_read (macho->file, (char *) &head, sizeof (head))
+ != sizeof(head))
+ {
+ grub_error (GRUB_ERR_READ_ERROR, "Cannot read Mach-O header.");
+ macho->offset32 = -1;
+ return;
+ }
+ if (head.magic != GRUB_MACHO_MAGIC32)
+ {
+ grub_error (GRUB_ERR_BAD_OS, "Invalid Mach-O 32-bit header.");
+ macho->offset32 = -1;
+ return;
+ }
+
+ /* Read commands. */
+ macho->ncmds32 = head.ncmds;
+ macho->cmdsize32 = head.sizeofcmds;
+ macho->cmds32 = grub_malloc(macho->cmdsize32);
+ if (! macho->cmds32)
+ {
+ grub_error (GRUB_ERR_OUT_OF_MEMORY, "not enough memory to read commands");
+ return;
+ }
+ if (grub_file_read (macho->file, (char *) macho->cmds32,
+ (grub_size_t) macho->cmdsize32)
+ != (grub_ssize_t) macho->cmdsize32)
+ {
+ grub_error (GRUB_ERR_READ_ERROR, "Cannot read Mach-O header.");
+ macho->offset32 = -1;
+ }
+}
+
+typedef int NESTED_FUNC_ATTR (*grub_macho_iter_hook_t)
+(grub_macho_t , struct grub_macho_cmd *,
+ void *);
+
+static grub_err_t
+grub_macho32_cmds_iterate (grub_macho_t macho,
+ grub_macho_iter_hook_t hook,
+ void *hook_arg)
+{
+ grub_uint8_t *hdrs = macho->cmds32;
+ int i;
+ if (! macho->cmds32)
+ return grub_error (GRUB_ERR_BAD_OS, "Couldn't find 32-bit Mach-O");
+ for (i = 0; i < macho->ncmds32; i++)
+ {
+ struct grub_macho_cmd *hdr = (struct grub_macho_cmd *) hdrs;
+ if (hook (macho, hdr, hook_arg))
+ break;
+ hdrs += hdr->cmdsize;
+ }
+
+ return grub_errno;
+}
+
+grub_size_t
+grub_macho32_filesize (grub_macho_t macho)
+{
+ if (grub_macho_contains_macho32 (macho))
+ return macho->end32 - macho->offset32;
+ return 0;
+}
+
+grub_err_t
+grub_macho32_readfile (grub_macho_t macho, void *dest)
+{
+ grub_ssize_t read;
+ if (! grub_macho_contains_macho32 (macho))
+ return grub_error (GRUB_ERR_BAD_OS,
+ "Couldn't read arcitecture-specific part");
+
+ if (grub_file_seek (macho->file, macho->offset32) == (grub_off_t) -1)
+ {
+ grub_error_push ();
+ return grub_error (GRUB_ERR_BAD_OS,
+ "Invalid offset in program header.");
+ }
+
+ read = grub_file_read (macho->file, dest,
+ macho->end32 - macho->offset32);
+ if (read != (grub_ssize_t) (macho->end32 - macho->offset32))
+ {
+ grub_error_push ();
+ return grub_error (GRUB_ERR_BAD_OS,
+ "Couldn't read arcitecture-specific part");
+ }
+ return GRUB_ERR_NONE;
+}
+
+/* Calculate the amount of memory spanned by the segments. */
+grub_err_t
+grub_macho32_size (grub_macho_t macho, grub_addr_t *segments_start,
+ grub_addr_t *segments_end, int flags)
+{
+ int nr_phdrs = 0;
+
+ /* Run through the program headers to calculate the total memory size we
+ should claim. */
+ auto int NESTED_FUNC_ATTR calcsize (grub_macho_t _macho,
+ struct grub_macho_cmd *phdr, void *_arg);
+ int NESTED_FUNC_ATTR calcsize (grub_macho_t UNUSED _macho,
+ struct grub_macho_cmd *hdr0, void UNUSED *_arg)
+ {
+ struct grub_macho_segment32 *hdr = (struct grub_macho_segment32 *) hdr0;
+ if (hdr->cmd != GRUB_MACHO_CMD_SEGMENT32)
+ return 0;
+ if (! hdr->filesize && (flags & GRUB_MACHO_NOBSS))
+ return 0;
+
+ nr_phdrs++;
+ if (hdr->vmaddr < *segments_start)
+ *segments_start = hdr->vmaddr;
+ if (hdr->vmaddr + hdr->vmsize > *segments_end)
+ *segments_end = hdr->vmaddr + hdr->vmsize;
+ return 0;
+ }
+
+ *segments_start = (grub_uint32_t) -1;
+ *segments_end = 0;
+
+ grub_macho32_cmds_iterate (macho, calcsize, 0);
+
+ if (nr_phdrs == 0)
+ return grub_error (GRUB_ERR_BAD_OS, "No program headers present");
+
+ if (*segments_end < *segments_start)
+ /* Very bad addresses. */
+ return grub_error (GRUB_ERR_BAD_OS, "Bad program header load addresses");
+
+ return GRUB_ERR_NONE;
+}
+
+/* Load every loadable segment into memory specified by `_load_hook'. */
+grub_err_t
+grub_macho32_load (grub_macho_t macho, char *offset, int flags)
+{
+ grub_err_t err = 0;
+ auto int NESTED_FUNC_ATTR do_load(grub_macho_t _macho,
+ struct grub_macho_cmd *hdr0,
+ void UNUSED *_arg);
+ int NESTED_FUNC_ATTR do_load(grub_macho_t _macho,
+ struct grub_macho_cmd *hdr0,
+ void UNUSED *_arg)
+ {
+ struct grub_macho_segment32 *hdr = (struct grub_macho_segment32 *) hdr0;
+
+ if (hdr->cmd != GRUB_MACHO_CMD_SEGMENT32)
+ return 0;
+
+ if (! hdr->filesize && (flags & GRUB_MACHO_NOBSS))
+ return 0;
+ if (! hdr->vmsize)
+ return 0;
+
+ if (grub_file_seek (_macho->file, hdr->fileoff
+ + _macho->offset32) == (grub_off_t) -1)
+ {
+ grub_error_push ();
+ grub_error (GRUB_ERR_BAD_OS,
+ "Invalid offset in program header.");
+ return 1;
+ }
+
+ if (hdr->filesize)
+ {
+ grub_ssize_t read;
+ read = grub_file_read (_macho->file, offset + hdr->vmaddr,
+ min (hdr->filesize, hdr->vmsize));
+ if (read != (grub_ssize_t) min (hdr->filesize, hdr->vmsize))
+ {
+ /* XXX How can we free memory from `load_hook'? */
+ grub_error_push ();
+ err=grub_error (GRUB_ERR_BAD_OS,
+ "Couldn't read segment from file: "
+ "wanted 0x%lx bytes; read 0x%lx bytes.",
+ hdr->filesize, read);
+ return 1;
+ }
+ }
+
+ if (hdr->filesize < hdr->vmsize)
+ grub_memset (offset + hdr->vmaddr + hdr->filesize,
+ 0, hdr->vmsize - hdr->filesize);
+ return 0;
+ }
+
+ grub_macho32_cmds_iterate (macho, do_load, 0);
+
+ return err;
+}
+
+grub_uint32_t
+grub_macho32_get_entry_point (grub_macho_t macho)
+{
+ grub_uint32_t entry_point = 0;
+ auto int NESTED_FUNC_ATTR hook(grub_macho_t _macho,
+ struct grub_macho_cmd *hdr,
+ void UNUSED *_arg);
+ int NESTED_FUNC_ATTR hook(grub_macho_t UNUSED _macho,
+ struct grub_macho_cmd *hdr,
+ void UNUSED *_arg)
+ {
+ if (hdr->cmd == GRUB_MACHO_CMD_THREAD)
+ entry_point = ((struct grub_macho_thread32 *) hdr)->entry_point;
+ return 0;
+ }
+ grub_macho32_cmds_iterate (macho, hook, 0);
+ return entry_point;
+}
+
+
+grub_err_t
+grub_macho_close (grub_macho_t macho)
+{
+ grub_file_t file = macho->file;
+
+ grub_free (macho->cmds32);
+ grub_free (macho->cmds64);
+
+ grub_free (macho);
+
+ if (file)
+ grub_file_close (file);
+
+ return grub_errno;
+}
+
+grub_macho_t
+grub_macho_file (grub_file_t file)
+{
+ grub_macho_t macho;
+ union grub_macho_filestart filestart;
+
+ macho = grub_malloc (sizeof (*macho));
+ if (! macho)
+ return 0;
+
+ macho->file = file;
+ macho->offset32 = -1;
+ macho->offset64 = -1;
+ macho->end32 = -1;
+ macho->end64 = -1;
+ macho->cmds32 = 0;
+ macho->cmds64 = 0;
+
+ if (grub_file_seek (macho->file, 0) == (grub_off_t) -1)
+ goto fail;
+
+ if (grub_file_read (macho->file, (char *) &filestart, sizeof (filestart))
+ != sizeof (filestart))
+ {
+ grub_error_push ();
+ grub_error (GRUB_ERR_READ_ERROR, "Cannot read Mach-O header.");
+ goto fail;
+ }
+
+ /* Is it a fat file? */
+ if (filestart.fat.magic == grub_be_to_cpu32 (GRUB_MACHO_FAT_MAGIC))
+ {
+ struct grub_macho_fat_arch *archs;
+ int i, narchs;
+
+ /* Load architecture description. */
+ narchs = grub_be_to_cpu32 (filestart.fat.nfat_arch);
+ if (grub_file_seek (macho->file, sizeof (struct grub_macho_fat_header))
+ == (grub_off_t) -1)
+ goto fail;
+ archs = grub_malloc (sizeof (struct grub_macho_fat_arch) * narchs);
+ if (!archs)
+ goto fail;
+ if (grub_file_read (macho->file, (char *) archs,
+ sizeof (struct grub_macho_fat_arch) * narchs)
+ != (grub_ssize_t)sizeof(struct grub_macho_fat_arch) * narchs)
+ {
+ grub_free (archs);
+ grub_error_push ();
+ grub_error (GRUB_ERR_READ_ERROR, "Cannot read Mach-O header.");
+ goto fail;
+ }
+
+ for (i = 0; i < narchs; i++)
+ {
+ if (GRUB_MACHO_CPUTYPE_IS_HOST32
+ (grub_be_to_cpu32 (archs[i].cputype)))
+ {
+ macho->offset32 = grub_be_to_cpu32 (archs[i].offset);
+ macho->end32 = grub_be_to_cpu32 (archs[i].offset)
+ + grub_be_to_cpu32 (archs[i].size);
+ }
+ if (GRUB_MACHO_CPUTYPE_IS_HOST64
+ (grub_be_to_cpu32 (archs[i].cputype)))
+ {
+ macho->offset64 = grub_be_to_cpu32 (archs[i].offset);
+ macho->end64 = grub_be_to_cpu32 (archs[i].offset)
+ + grub_be_to_cpu32 (archs[i].size);
+ }
+ }
+ grub_free (archs);
+ }
+
+ /* Is it a thin 32-bit file? */
+ if (filestart.thin32.magic == GRUB_MACHO_MAGIC32)
+ {
+ macho->offset32 = 0;
+ macho->end32 = grub_file_size (file);
+ }
+
+ /* Is it a thin 64-bit file? */
+ if (filestart.thin64.magic == GRUB_MACHO_MAGIC64)
+ {
+ macho->offset64 = 0;
+ macho->end64 = grub_file_size (file);
+ }
+
+ grub_macho_parse32 (macho);
+ /* FIXME: implement 64-bit.*/
+ /* grub_macho_parse64 (macho); */
+
+ return macho;
+
+fail:
+ grub_macho_close (macho);
+ return 0;
+}
+
+grub_macho_t
+grub_macho_open (const char *name)
+{
+ grub_file_t file;
+ grub_macho_t macho;
+
+ file = grub_gzfile_open (name, 1);
+ if (! file)
+ return 0;
+
+ macho = grub_macho_file (file);
+ if (! macho)
+ grub_file_close (file);
+
+ return macho;
+}
diff --git a/loader/xnu.c b/loader/xnu.c
new file mode 100644
index 0000000..429f1ee
--- /dev/null
+++ b/loader/xnu.c
@@ -0,0 +1,1345 @@
+/* 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);
+ exthead->binarysize = machosize;
+ if ((err = grub_macho32_readfile (macho, buf)))
+ {
+ grub_macho_close (macho);
+ return err;
+ }
+ grub_macho_close (macho);
+ buf += machosize;
+ }
+ grub_errno = GRUB_ERR_NONE;
+
+ /* Load the plist. */
+ if (infoplist)
+ {
+ exthead->infoplistaddr = (buf - grub_xnu_heap_start);
+ exthead->infoplistsize = infoplistsize + 1;
+ if (grub_file_read (infoplist, buf, infoplistsize)
+ != (grub_ssize_t) (infoplistsize))
+ {
+ grub_file_close (infoplist);
+ grub_error_push ();
+ return grub_error (GRUB_ERR_BAD_OS, "Couldn't read file %s: ",
+ infoplistname);
+ }
+ grub_file_close (infoplist);
+ buf[infoplistsize] = 0;
+ }
+ grub_errno = GRUB_ERR_NONE;
+
+ /* Announce to kernel */
+ return grub_xnu_register_memory ("Driver-", &driversnum, exthead,
+ neededspace);
+}
+
+/* Load mkext. */
+static grub_err_t
+grub_cmd_xnu_mkext (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *args[])
+{
+ grub_file_t file;
+ void *loadto;
+ grub_err_t err;
+ grub_off_t readoff = 0;
+ grub_ssize_t readlen = -1;
+ struct grub_macho_fat_header head;
+ struct grub_macho_fat_arch *archs;
+ int narchs, i;
+
+ if (argc != 1)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required");
+
+ if (! grub_xnu_heap_size)
+ return grub_error (GRUB_ERR_BAD_OS, "no xnu kernel loaded");
+
+ file = grub_gzfile_open (args[0], 1);
+ if (! file)
+ return grub_error (GRUB_ERR_FILE_NOT_FOUND,
+ "Couldn't load driver package");
+
+ /* Sometimes caches are fat binary. Errgh. */
+ if (grub_file_read (file, (char *) &head, sizeof (head))
+ != (grub_ssize_t) (sizeof (head)))
+ {
+ /* I don't know the internal structure of package but
+ can hardly imagine a valid package shorter than 20 bytes. */
+ grub_file_close (file);
+ grub_error_push ();
+ return grub_error (GRUB_ERR_BAD_OS, "Couldn't read file %s", args[0]);
+ }
+
+ /* Find the corresponding architecture. */
+ if (grub_be_to_cpu32 (head.magic) == GRUB_MACHO_FAT_MAGIC)
+ {
+ narchs = grub_be_to_cpu32 (head.nfat_arch);
+ archs = grub_malloc (sizeof (struct grub_macho_fat_arch) * narchs);
+ if (! archs)
+ {
+ grub_file_close (file);
+ grub_error_push ();
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "Couldn't read file %s", args[0]);
+
+ }
+ if (grub_file_read (file, (char *) archs,
+ sizeof (struct grub_macho_fat_arch) * narchs)
+ != (grub_ssize_t) sizeof(struct grub_macho_fat_arch) * narchs)
+ {
+ grub_free (archs);
+ grub_error_push ();
+ return grub_error (GRUB_ERR_READ_ERROR, "Cannot read fat header.");
+ }
+ for (i = 0; i < narchs; i++)
+ {
+ if (GRUB_MACHO_CPUTYPE_IS_HOST32
+ (grub_be_to_cpu32 (archs[i].cputype)))
+ {
+ readoff = grub_be_to_cpu32 (archs[i].offset);
+ readlen = grub_be_to_cpu32 (archs[i].size);
+ }
+ }
+ grub_free (archs);
+ }
+ else
+ {
+ /* It's a flat file. Some sane people still exist. */
+ readoff = 0;
+ readlen = grub_file_size (file);
+ }
+
+ if (readlen == -1)
+ {
+ grub_file_close (file);
+ return grub_error (GRUB_ERR_BAD_OS, "no suitable architecture is found");
+ }
+
+ /* Allocate space. */
+ err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE);
+ if (err)
+ {
+ grub_file_close (file);
+ return err;
+ }
+
+ loadto = grub_xnu_heap_malloc (readlen);
+ if (! loadto)
+ {
+ grub_file_close (file);
+ return grub_errno;
+ }
+
+ /* Read the file. */
+ grub_file_seek (file, readoff);
+ if (grub_file_read (file, loadto, readlen) != (grub_ssize_t) (readlen))
+ {
+ grub_file_close (file);
+ grub_error_push ();
+ return grub_error (GRUB_ERR_BAD_OS, "Couldn't read file %s", args[0]);
+ }
+ grub_file_close (file);
+
+ /* Pass it to kernel. */
+ return grub_xnu_register_memory ("DriversPackage-", &driverspackagenum,
+ loadto, readlen);
+}
+
+static grub_err_t
+grub_cmd_xnu_ramdisk (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *args[])
+{
+ grub_file_t file;
+ void *loadto;
+ grub_err_t err;
+ grub_size_t size;
+
+ if (argc != 1)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required");
+
+ if (! grub_xnu_heap_size)
+ return grub_error (GRUB_ERR_BAD_OS, "no xnu kernel loaded");
+
+ file = grub_gzfile_open (args[0], 1);
+ if (! file)
+ return grub_error (GRUB_ERR_FILE_NOT_FOUND,
+ "Couldn't load ramdisk");
+
+ err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE);
+ if (err)
+ return err;
+
+ size = grub_file_size (file);
+
+ loadto = grub_xnu_heap_malloc (size);
+ if (! loadto)
+ return grub_errno;
+ if (grub_file_read (file, loadto, size)
+ != (grub_ssize_t) (size))
+ {
+ grub_file_close (file);
+ grub_error_push ();
+ return grub_error (GRUB_ERR_BAD_OS, "Couldn't read file %s", args[0]);
+ }
+ return grub_xnu_register_memory ("RAMDisk", 0, loadto, size);
+}
+
+/* Parse a devtree file. It uses the following format:
+ valuename:valuedata;
+ keyname{
+ contents
+ }
+ keyname, valuename and valuedata are in hex.
+ */
+static char *
+grub_xnu_parse_devtree (struct grub_xnu_devtree_key **parent,
+ char *start, char *end)
+{
+ char *ptr, *ptr2;
+ char *name, *data;
+ int namelen, datalen, i;
+ for (ptr = start; ptr && ptr < end; )
+ {
+ if (grub_isspace (*ptr))
+ {
+ ptr++;
+ continue;
+ }
+ if (*ptr == '}')
+ return ptr + 1;
+ namelen = 0;
+
+ /* Parse the name. */
+ for (ptr2 = ptr; ptr2 < end && (grub_isspace (*ptr2)
+ || (*ptr2 >= '0' && *ptr2 <= '9')
+ || (*ptr2 >= 'a' && *ptr2 <= 'f')
+ || (*ptr2 >= 'A' && *ptr2 <= 'F'));
+ ptr2++)
+ if (! grub_isspace (*ptr2))
+ namelen++;
+ if (ptr2 == end)
+ return 0;
+ namelen /= 2;
+ name = grub_malloc (namelen + 1);
+ if (!name)
+ return 0;
+ for (i = 0; i < 2 * namelen; i++)
+ {
+ int hex = 0;
+ while (grub_isspace (*ptr))
+ ptr++;
+ if (*ptr >= '0' && *ptr <= '9')
+ hex = *ptr - '0';
+ if (*ptr >= 'a' && *ptr <= 'f')
+ hex = *ptr - 'a' + 10;
+ if (*ptr >= 'A' && *ptr <= 'F')
+ hex = *ptr - 'A' + 10;
+
+ if (i % 2 == 0)
+ name[i / 2] = hex << 4;
+ else
+ name[i / 2] |= hex;
+ ptr++;
+ }
+ name [namelen] = 0;
+ while (grub_isspace (*ptr))
+ ptr++;
+
+ /* If it describes a key recursively invoke the function. */
+ if (*ptr == '{')
+ {
+ struct grub_xnu_devtree_key *newkey
+ = grub_xnu_create_key (parent, name);
+ grub_free (name);
+ if (! newkey)
+ return 0;
+ ptr = grub_xnu_parse_devtree (&(newkey->first_child), ptr + 1, end);
+ continue;
+ }
+
+ /* Parse the data. */
+ if (*ptr != ':')
+ return 0;
+ ptr++;
+ datalen = 0;
+ for (ptr2 = ptr; ptr2 < end && (grub_isspace (*ptr2)
+ || (*ptr2 >= '0' && *ptr2 <= '9')
+ || (*ptr2 >= 'a' && *ptr2 <= 'f')
+ || (*ptr2 >= 'A' && *ptr2 <= 'F'));
+ ptr2++)
+ if (! grub_isspace (*ptr2))
+ datalen++;
+ if (ptr2 == end)
+ return 0;
+ datalen /= 2;
+ data = grub_malloc (datalen);
+ if (! data)
+ return 0;
+ for (i = 0; i < 2 * datalen; i++)
+ {
+ int hex = 0;
+ while (grub_isspace (*ptr))
+ ptr++;
+ if (*ptr >= '0' && *ptr <= '9')
+ hex = *ptr - '0';
+ if (*ptr >= 'a' && *ptr <= 'f')
+ hex = *ptr - 'a' + 10;
+ if (*ptr >= 'A' && *ptr <= 'F')
+ hex = *ptr - 'A' + 10;
+
+ if (i % 2 == 0)
+ data[i / 2] = hex << 4;
+ else
+ data[i / 2] |= hex;
+ ptr++;
+ }
+ while (ptr < end && grub_isspace (*ptr))
+ ptr++;
+ {
+ struct grub_xnu_devtree_key *newkey
+ = grub_xnu_create_value (parent, name);
+ grub_free (name);
+ if (! newkey)
+ return 0;
+ newkey->datasize = datalen;
+ newkey->data = data;
+ }
+ if (*ptr != ';')
+ return 0;
+ ptr++;
+ }
+ if (ptr >= end && *parent != grub_xnu_devtree_root)
+ return 0;
+ return ptr;
+}
+
+/* Returns true if the kext should be loaded according to plist
+ and osbundlereq. Also fill BINNAME. */
+static int
+grub_xnu_check_os_bundle_required (char *plistname, char *osbundlereq,
+ char **binname)
+{
+ grub_file_t file;
+ char *buf = 0, *tagstart = 0, *ptr1 = 0, *keyptr = 0;
+ char *stringptr = 0, *ptr2 = 0;
+ grub_size_t size;
+ int depth = 0;
+ int ret;
+ int osbundlekeyfound = 0, binnamekeyfound = 0;
+ if (binname)
+ *binname = 0;
+
+ file = grub_gzfile_open (plistname, 1);
+ if (! file)
+ {
+ grub_file_close (file);
+ grub_error_push ();
+ grub_error (GRUB_ERR_BAD_OS, "Couldn't read file %s", plistname);
+ return 0;
+ }
+
+ size = grub_file_size (file);
+ buf = grub_malloc (size);
+ if (! buf)
+ {
+ grub_file_close (file);
+ grub_error_push ();
+ grub_error (GRUB_ERR_OUT_OF_MEMORY, "Couldn't read file %s", plistname);
+ return 0;
+ }
+ if (grub_file_read (file, buf, size) != (grub_ssize_t) (size))
+ {
+ grub_file_close (file);
+ grub_error_push ();
+ grub_error (GRUB_ERR_BAD_OS, "Couldn't read file %s", plistname);
+ return 0;
+ }
+ grub_file_close (file);
+
+ /* Set the return value for the case when no OSBundleRequired tag is found. */
+ if (osbundlereq)
+ ret = grub_strword (osbundlereq, "all") || grub_strword (osbundlereq, "-");
+ else
+ ret = 1;
+
+ /* Parse plist. It's quite dirty and inextensible but does its job. */
+ for (ptr1 = buf; ptr1 < buf + size; ptr1++)
+ switch (*ptr1)
+ {
+ case '<':
+ tagstart = ptr1;
+ *ptr1 = 0;
+ if (keyptr && depth == 4
+ && grub_strcmp (keyptr, "OSBundleRequired") == 0)
+ osbundlekeyfound = 1;
+ if (keyptr && depth == 4 &&
+ grub_strcmp (keyptr, "CFBundleExecutable") == 0)
+ binnamekeyfound = 1;
+ if (stringptr && osbundlekeyfound && osbundlereq && depth == 4)
+ {
+ for (ptr2 = stringptr; *ptr2; ptr2++)
+ *ptr2 = grub_tolower (*ptr2);
+ ret = grub_strword (osbundlereq, stringptr)
+ || grub_strword (osbundlereq, "all");
+ }
+ if (stringptr && binnamekeyfound && binname && depth == 4)
+ {
+ if (*binname)
+ grub_free (*binname);
+ *binname = grub_strdup (stringptr);
+ }
+
+ *ptr1 = '<';
+ keyptr = 0;
+ stringptr = 0;
+ break;
+ case '>':
+ if (! tagstart)
+ {
+ grub_free (buf);
+ grub_error (GRUB_ERR_BAD_OS, "can't parse %s", plistname);
+ return 0;
+ }
+ *ptr1 = 0;
+ if (tagstart[1] == '?' || ptr1[-1] == '/')
+ {
+ osbundlekeyfound = 0;
+ *ptr1 = '>';
+ break;
+ }
+ if (depth == 3 && grub_strcmp (tagstart + 1, "key") == 0)
+ keyptr = ptr1 + 1;
+ if (depth == 3 && grub_strcmp (tagstart + 1, "string") == 0)
+ stringptr = ptr1 + 1;
+ else if (grub_strcmp (tagstart + 1, "/key") != 0)
+ {
+ osbundlekeyfound = 0;
+ binnamekeyfound = 0;
+ }
+ *ptr1 = '>';
+
+ if (tagstart[1] == '/')
+ depth--;
+ else
+ depth++;
+ break;
+ }
+ grub_free (buf);
+
+ return ret;
+}
+
+/* Load all loadable kexts placed under DIRNAME and matching OSBUNDLEREQUIRED */
+grub_err_t
+grub_xnu_scan_dir_for_kexts (char *dirname, char *osbundlerequired,
+ int maxrecursion)
+{
+ grub_device_t dev;
+ char *device_name;
+ grub_fs_t fs;
+ const char *path;
+
+ auto int load_hook (const char *filename,
+ const struct grub_dirhook_info *info);
+ int load_hook (const char *filename, const struct grub_dirhook_info *info)
+ {
+ char *newdirname;
+ if (! info->dir)
+ return 0;
+ if (filename[0] == '.')
+ return 0;
+
+ if (grub_strlen (filename) < 5 ||
+ grub_memcmp (filename + grub_strlen (filename) - 5, ".kext", 5) != 0)
+ return 0;
+
+ newdirname
+ = grub_malloc (grub_strlen (dirname) + grub_strlen (filename) + 2);
+
+ /* It's a .kext. Try to load it. */
+ if (newdirname)
+ {
+ grub_strcpy (newdirname, dirname);
+ newdirname[grub_strlen (newdirname) + 1] = 0;
+ newdirname[grub_strlen (newdirname)] = '/';
+ grub_strcpy (newdirname + grub_strlen (newdirname), filename);
+ grub_xnu_load_kext_from_dir (newdirname, osbundlerequired,
+ maxrecursion);
+ if (grub_errno == GRUB_ERR_BAD_OS)
+ grub_errno = GRUB_ERR_NONE;
+ grub_free (newdirname);
+ }
+ return 0;
+ }
+
+ if (! grub_xnu_heap_size)
+ return grub_error (GRUB_ERR_BAD_OS, "no xnu kernel loaded");
+
+ device_name = grub_file_get_device_name (dirname);
+ dev = grub_device_open (device_name);
+ if (dev)
+ {
+ fs = grub_fs_probe (dev);
+ path = grub_strchr (dirname, ')');
+ if (! path)
+ path = dirname;
+ else
+ path++;
+
+ if (fs)
+ (fs->dir) (dev, path, load_hook);
+ grub_device_close (dev);
+ }
+ grub_free (device_name);
+
+ return GRUB_ERR_NONE;
+}
+
+/* Load extension DIRNAME. (extensions are directoris in xnu) */
+grub_err_t
+grub_xnu_load_kext_from_dir (char *dirname, char *osbundlerequired,
+ int maxrecursion)
+{
+ grub_device_t dev;
+ char *plistname = 0;
+ char *newdirname;
+ char *newpath;
+ char *device_name;
+ grub_fs_t fs;
+ const char *path;
+ char *binsuffix;
+ int usemacos = 0;
+ grub_file_t binfile;
+
+ auto int load_hook (const char *filename,
+ const struct grub_dirhook_info *info);
+
+ int load_hook (const char *filename, const struct grub_dirhook_info *info)
+ {
+ if (grub_strlen (filename) > 15)
+ return 0;
+ grub_strcpy (newdirname + grub_strlen (dirname) + 1, filename);
+
+ /* If the kext contains directory "Contents" all real stuff is in
+ this directory. */
+ if (info->dir && grub_strcasecmp (filename, "Contents") == 0)
+ grub_xnu_load_kext_from_dir (newdirname, osbundlerequired,
+ maxrecursion - 1);
+
+ /* Directory "Plugins" contains nested kexts. */
+ if (info->dir && grub_strcasecmp (filename, "Plugins") == 0)
+ grub_xnu_scan_dir_for_kexts (newdirname, osbundlerequired,
+ maxrecursion - 1);
+
+ /* Directory "MacOS" contains executable, otherwise executable is
+ on the top. */
+ if (info->dir && grub_strcasecmp (filename, "MacOS") == 0)
+ usemacos = 1;
+
+ /* Info.plist is the file which governs our future actions. */
+ if (! info->dir && grub_strcasecmp (filename, "Info.plist") == 0
+ && ! plistname)
+ plistname = grub_strdup (newdirname);
+ return 0;
+ }
+
+ newdirname = grub_malloc (grub_strlen (dirname) + 20);
+ if (! newdirname)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't allocate buffer");
+ grub_strcpy (newdirname, dirname);
+ newdirname[grub_strlen (dirname)] = '/';
+ newdirname[grub_strlen (dirname) + 1] = 0;
+ device_name = grub_file_get_device_name (dirname);
+ dev = grub_device_open (device_name);
+ if (dev)
+ {
+ fs = grub_fs_probe (dev);
+ path = grub_strchr (dirname, ')');
+ if (! path)
+ path = dirname;
+ else
+ path++;
+
+ newpath = grub_strchr (newdirname, ')');
+ if (! newpath)
+ newpath = newdirname;
+ else
+ newpath++;
+
+ /* Look at the directory. */
+ if (fs)
+ (fs->dir) (dev, path, load_hook);
+
+ if (plistname && grub_xnu_check_os_bundle_required
+ (plistname, osbundlerequired, &binsuffix))
+ {
+ if (binsuffix)
+ {
+ /* Open the binary. */
+ char *binname = grub_malloc (grub_strlen (dirname)
+ + grub_strlen (binsuffix)
+ + sizeof ("/MacOS/"));
+ grub_strcpy (binname, dirname);
+ if (usemacos)
+ grub_strcpy (binname + grub_strlen (binname), "/MacOS/");
+ else
+ grub_strcpy (binname + grub_strlen (binname), "/");
+ grub_strcpy (binname + grub_strlen (binname), binsuffix);
+ grub_dprintf ("xnu", "%s:%s\n", plistname, binname);
+ binfile = grub_gzfile_open (binname, 1);
+ if (! binfile)
+ grub_errno = GRUB_ERR_NONE;
+
+ /* Load the extension. */
+ grub_xnu_load_driver (plistname, binfile);
+ grub_free (binname);
+ grub_free (binsuffix);
+ }
+ else
+ {
+ grub_dprintf ("xnu", "%s:0\n", plistname);
+ grub_xnu_load_driver (plistname, 0);
+ }
+ }
+ grub_free (plistname);
+ grub_device_close (dev);
+ }
+ grub_free (device_name);
+
+ return GRUB_ERR_NONE;
+}
+
+/* Load devtree file. */
+static grub_err_t
+grub_cmd_xnu_devtree (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *args[])
+{
+ grub_file_t file;
+ char *data, *endret;
+ grub_size_t datalen;
+
+ if (argc != 1)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "Filename required");
+
+ if (! grub_xnu_heap_size)
+ return grub_error (GRUB_ERR_BAD_OS, "no xnu kernel loaded");
+
+ /* Load the file. */
+ file = grub_gzfile_open (args[0], 1);
+ if (! file)
+ return grub_error (GRUB_ERR_FILE_NOT_FOUND, "Couldn't load device tree");
+ datalen = grub_file_size (file);
+ data = grub_malloc (datalen + 1);
+ if (! data)
+ {
+ grub_file_close (file);
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "Could load device tree into memory");
+ }
+ if (grub_file_read (file, data, datalen) != (grub_ssize_t) datalen)
+ {
+ grub_file_close (file);
+ grub_free (data);
+ grub_error_push ();
+ return grub_error (GRUB_ERR_BAD_OS, "Couldn't read file %s", args[0]);
+ }
+ grub_file_close (file);
+ data[datalen] = 0;
+
+ /* Parse the file. */
+ endret = grub_xnu_parse_devtree (&grub_xnu_devtree_root,
+ data, data + datalen);
+ grub_free (data);
+
+ if (! endret)
+ return grub_error (GRUB_ERR_BAD_OS, "Couldn't parse devtree");
+
+ return GRUB_ERR_NONE;
+}
+
+static int locked=0;
+static grub_dl_t my_mod;
+
+/* Load the kext. */
+static grub_err_t
+grub_cmd_xnu_kext (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *args[])
+{
+ grub_file_t binfile = 0;
+ if (argc == 2)
+ {
+ /* User explicitely specified plist and binary. */
+ if (grub_strcmp (args[1], "-") != 0)
+ {
+ binfile = grub_gzfile_open (args[1], 1);
+ if (! binfile)
+ {
+ grub_error (GRUB_ERR_BAD_OS, "can't open file");
+ return GRUB_ERR_NONE;
+ }
+ }
+ return grub_xnu_load_driver (grub_strcmp (args[0], "-") ? args[0] : 0,
+ binfile);
+ }
+
+ /* load kext normally. */
+ if (argc == 1)
+ return grub_xnu_load_kext_from_dir (args[0], 0, 10);
+
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required");
+}
+
+/* Load a directory containing kexts. */
+static grub_err_t
+grub_cmd_xnu_kextdir (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *args[])
+{
+ if (argc != 1 && argc != 2)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "directory name required");
+
+ if (argc == 1)
+ return grub_xnu_scan_dir_for_kexts (args[0],
+ "console,root,local-root,network-root",
+ 10);
+ else
+ {
+ char *osbundlerequired = grub_strdup (args[1]), *ptr;
+ grub_err_t err;
+ if (! osbundlerequired)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "couldn't allocate string temporary space");
+ for (ptr = osbundlerequired; *ptr; ptr++)
+ *ptr = grub_tolower (*ptr);
+ err = grub_xnu_scan_dir_for_kexts (args[0], osbundlerequired, 10);
+ grub_free (osbundlerequired);
+ return err;
+ }
+}
+
+#ifndef GRUB_UTIL
+static grub_err_t
+grub_cmd_xnu_resume (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *args[])
+{
+ if (argc != 1)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required");
+
+ return grub_xnu_resume (args[0]);
+}
+#endif
+
+void
+grub_xnu_lock ()
+{
+#ifndef GRUB_UTIL
+ if (!locked)
+ grub_dl_ref (my_mod);
+#endif
+ locked = 1;
+}
+
+void
+grub_xnu_unlock ()
+{
+#ifndef GRUB_UTIL
+ if (locked)
+ grub_dl_unref (my_mod);
+#endif
+ locked = 0;
+}
+
+static grub_command_t cmd_kernel, cmd_mkext, cmd_kext, cmd_kextdir,
+ cmd_ramdisk, cmd_devtree, cmd_resume;
+
+GRUB_MOD_INIT(xnu)
+{
+ (void) mod; /* To stop warning. */
+ cmd_kernel = grub_register_command ("xnu_kernel", grub_cmd_xnu_kernel, 0,
+ "load a xnu kernel");
+ cmd_mkext = grub_register_command ("xnu_mkext", grub_cmd_xnu_mkext, 0,
+ "Load XNU extension package.");
+ cmd_kext = grub_register_command ("xnu_kext", grub_cmd_xnu_kext, 0,
+ "Load XNU extension.");
+ cmd_kextdir = grub_register_command ("xnu_kextdir", grub_cmd_xnu_kextdir,
+ "xnu_kextdir DIRECTORY [OSBundleRequired]",
+ "Load XNU extension directory");
+ cmd_ramdisk = grub_register_command ("xnu_ramdisk", grub_cmd_xnu_ramdisk, 0,
+ "Load XNU ramdisk. "
+ "It will be seen as md0");
+ cmd_devtree = grub_register_command ("xnu_devtree", grub_cmd_xnu_devtree, 0,
+ "Load XNU devtree");
+#ifndef GRUB_UTIL
+ cmd_resume = grub_register_command ("xnu_resume", grub_cmd_xnu_resume,
+ 0, "Load XNU hibernate image.");
+#endif
+ my_mod=mod;
+}
+
+GRUB_MOD_FINI(xnu)
+{
+#ifndef GRUB_UTIL
+ grub_unregister_command (cmd_resume);
+#endif
+ grub_unregister_command (cmd_mkext);
+ grub_unregister_command (cmd_kext);
+ grub_unregister_command (cmd_kextdir);
+ grub_unregister_command (cmd_devtree);
+ grub_unregister_command (cmd_ramdisk);
+ grub_unregister_command (cmd_kernel);
+}
diff --git a/loader/xnu_resume.c b/loader/xnu_resume.c
new file mode 100644
index 0000000..b501e15
--- /dev/null
+++ b/loader/xnu_resume.c
@@ -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;
+}
diff --git a/term/efi/console.c b/term/efi/console.c
index 3322b8f..7a97d26 100644
--- a/term/efi/console.c
+++ b/term/efi/console.c
@@ -226,9 +226,9 @@ grub_console_getkey (void)
do
{
- status = efi_call_3 (b->wait_for_event, 1, &(i->wait_for_key), &index);
- if (status != GRUB_EFI_SUCCESS)
- return -1;
+ /* Don't use wait_for_event it's known
+ to cause hangs on some mac models */
+ grub_millisleep (10);
grub_console_checkkey ();
}
^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [PATCH] xnu
2009-04-27 19:42 ` Vladimir 'phcoder' Serbinenko
@ 2009-05-02 23:20 ` Vladimir 'phcoder' Serbinenko
2009-05-03 4:32 ` Bean
0 siblings, 1 reply; 13+ messages in thread
From: Vladimir 'phcoder' Serbinenko @ 2009-05-02 23:20 UTC (permalink / raw)
To: The development of GRUB 2
[-- Attachment #1: Type: text/plain, Size: 2953 bytes --]
commited
On Mon, Apr 27, 2009 at 9:42 PM, Vladimir 'phcoder' Serbinenko <
phcoder@gmail.com> wrote:
> Update. No need to initialize efiemu manually anymore
>
> On Wed, Apr 15, 2009 at 12:46 AM, phcoder <phcoder@gmail.com> wrote:
>
>> Hello. Here is my xnu patch. Tested on i386-pc, i386-efi and x86_64-efi.
>> On non-efi it needs efiemu otherwise you need only include/grub/autoefi.h
>> file from efiemu patch. To resume xnu from hibernation do:
>> xnu_resume <hibernation file>
>> Note: you don't need efiemu in this case
>> How to boot xnu:
>> <efiemu if not on efi platform>
>> [on pc only:] vbe_mode=0xYYY # desired vga mode
>> fsb=133.3 # your fsb frequency
>> xnu_kernel <kernel> <command line>
>> <insert modules>
>> boot
>> Modules can be inserted one of the following ways:
>> 1) xnu_mkext <mkext file>
>> 2) xnu_kext extension.kext
>> 3) xnu_kext extension.kext/Info.plist extension.kext/extension
>> 4) xnu_kextdir <directory containing extensions>
>> It's also possible to execute these commands multiple times
>> The most typical case is
>> xnu_kernel /mach_kernel rd=disk0sX
>> xnu_mkext /System/Library/Extensions.mkext
>>
>>
>> If you need to add values to device tree the command
>> xnu_devtree <devtree file>
>> This file uses the following format:
>> valuename:valuedata;
>> keyname{
>> contents
>> }
>> keyname, valuename and valuedata are in hex.
>>
>> If you need to adda ramdisk execute
>> xnu_ramdisk <ramdisk file>
>> ramdisk will be exposed as /dev/md0 which may be used as boot device with
>> rd=md0.
>>
>> The areas which need more work (every help is welcome):
>> 1) testing on different platforms
>> 2) Detect "device-properties" value in device tree. There are several
>> trivial values in device tree present in boot.efi but not in grub2. These
>> ones are easy to add and AFAIK don't change anything. But the value
>> "device-properties" is difficult. I already know it's format but not where
>> the values come from. It contains info about gfx and sound card which may
>> not work if this value is missing. The current workaround is xnu_devtree
>> command with a dump of device-properties
>> 3) autodetect fsb frequency
>> 4) Support video splash
>> 5) Define and use an unified interface to retrieve video information
>> uniformly across platforms
>> 6) Boot by UUID. I know how to do it but need md5 for it which is a part
>> of pending luks patch
>> 7) Scripts for automatic creating of grub.cfg entries
>> 8) Support for prelinked kernel. It's compressed and I have yet looked how
>> to decompress it (seems it's compressed with something called lzss)
>> 9) Use claimmap once available (see my multiboot on efi patch)
>> 10) Better collaboration with memory management once advanced mm's
>> available
>> 11) Resume from encrypted hibernation
>>
>> --
>>
>> Regards
>> Vladimir 'phcoder' Serbinenko
>>
>
>
>
> --
> Regards
> Vladimir 'phcoder' Serbinenko
>
--
Regards
Vladimir 'phcoder' Serbinenko
[-- Attachment #2: Type: text/html, Size: 3858 bytes --]
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH] xnu
2009-05-02 23:20 ` Vladimir 'phcoder' Serbinenko
@ 2009-05-03 4:32 ` Bean
2009-05-03 9:18 ` Vladimir 'phcoder' Serbinenko
0 siblings, 1 reply; 13+ messages in thread
From: Bean @ 2009-05-03 4:32 UTC (permalink / raw)
To: The development of GRUB 2
Hi,
xnu usage is quite complicated, perhaps you could write a wiki page on it.
On Sun, May 3, 2009 at 7:20 AM, Vladimir 'phcoder' Serbinenko
<phcoder@gmail.com> wrote:
> commited
>
> On Mon, Apr 27, 2009 at 9:42 PM, Vladimir 'phcoder' Serbinenko
> <phcoder@gmail.com> wrote:
>>
>> Update. No need to initialize efiemu manually anymore
>>
>> On Wed, Apr 15, 2009 at 12:46 AM, phcoder <phcoder@gmail.com> wrote:
>>>
>>> Hello. Here is my xnu patch. Tested on i386-pc, i386-efi and x86_64-efi.
>>> On non-efi it needs efiemu otherwise you need only include/grub/autoefi.h
>>> file from efiemu patch. To resume xnu from hibernation do:
>>> xnu_resume <hibernation file>
>>> Note: you don't need efiemu in this case
>>> How to boot xnu:
>>> <efiemu if not on efi platform>
>>> [on pc only:] vbe_mode=0xYYY # desired vga mode
>>> fsb=133.3 # your fsb frequency
>>> xnu_kernel <kernel> <command line>
>>> <insert modules>
>>> boot
>>> Modules can be inserted one of the following ways:
>>> 1) xnu_mkext <mkext file>
>>> 2) xnu_kext extension.kext
>>> 3) xnu_kext extension.kext/Info.plist extension.kext/extension
>>> 4) xnu_kextdir <directory containing extensions>
>>> It's also possible to execute these commands multiple times
>>> The most typical case is
>>> xnu_kernel /mach_kernel rd=disk0sX
>>> xnu_mkext /System/Library/Extensions.mkext
>>>
>>>
>>> If you need to add values to device tree the command
>>> xnu_devtree <devtree file>
>>> This file uses the following format:
>>> valuename:valuedata;
>>> keyname{
>>> contents
>>> }
>>> keyname, valuename and valuedata are in hex.
>>>
>>> If you need to adda ramdisk execute
>>> xnu_ramdisk <ramdisk file>
>>> ramdisk will be exposed as /dev/md0 which may be used as boot device with
>>> rd=md0.
>>>
>>> The areas which need more work (every help is welcome):
>>> 1) testing on different platforms
>>> 2) Detect "device-properties" value in device tree. There are several
>>> trivial values in device tree present in boot.efi but not in grub2. These
>>> ones are easy to add and AFAIK don't change anything. But the value
>>> "device-properties" is difficult. I already know it's format but not where
>>> the values come from. It contains info about gfx and sound card which may
>>> not work if this value is missing. The current workaround is xnu_devtree
>>> command with a dump of device-properties
>>> 3) autodetect fsb frequency
>>> 4) Support video splash
>>> 5) Define and use an unified interface to retrieve video information
>>> uniformly across platforms
>>> 6) Boot by UUID. I know how to do it but need md5 for it which is a part
>>> of pending luks patch
>>> 7) Scripts for automatic creating of grub.cfg entries
>>> 8) Support for prelinked kernel. It's compressed and I have yet looked
>>> how to decompress it (seems it's compressed with something called lzss)
>>> 9) Use claimmap once available (see my multiboot on efi patch)
>>> 10) Better collaboration with memory management once advanced mm's
>>> available
>>> 11) Resume from encrypted hibernation
>>>
>>> --
>>>
>>> Regards
>>> Vladimir 'phcoder' Serbinenko
>>
>>
>>
>> --
>> Regards
>> Vladimir 'phcoder' Serbinenko
>
>
>
> --
> Regards
> Vladimir 'phcoder' Serbinenko
>
> _______________________________________________
> Grub-devel mailing list
> Grub-devel@gnu.org
> http://lists.gnu.org/mailman/listinfo/grub-devel
>
>
--
Bean
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH] xnu
2009-05-03 4:32 ` Bean
@ 2009-05-03 9:18 ` Vladimir 'phcoder' Serbinenko
0 siblings, 0 replies; 13+ messages in thread
From: Vladimir 'phcoder' Serbinenko @ 2009-05-03 9:18 UTC (permalink / raw)
To: The development of GRUB 2
[-- Attachment #1: Type: text/plain, Size: 3866 bytes --]
On Sun, May 3, 2009 at 6:32 AM, Bean <bean123ch@gmail.com> wrote:
> Hi,
>
> xnu usage is quite complicated, perhaps you could write a wiki page on it.
>
On it
>
> On Sun, May 3, 2009 at 7:20 AM, Vladimir 'phcoder' Serbinenko
> <phcoder@gmail.com> wrote:
> > commited
> >
> > On Mon, Apr 27, 2009 at 9:42 PM, Vladimir 'phcoder' Serbinenko
> > <phcoder@gmail.com> wrote:
> >>
> >> Update. No need to initialize efiemu manually anymore
> >>
> >> On Wed, Apr 15, 2009 at 12:46 AM, phcoder <phcoder@gmail.com> wrote:
> >>>
> >>> Hello. Here is my xnu patch. Tested on i386-pc, i386-efi and
> x86_64-efi.
> >>> On non-efi it needs efiemu otherwise you need only
> include/grub/autoefi.h
> >>> file from efiemu patch. To resume xnu from hibernation do:
> >>> xnu_resume <hibernation file>
> >>> Note: you don't need efiemu in this case
> >>> How to boot xnu:
> >>> <efiemu if not on efi platform>
> >>> [on pc only:] vbe_mode=0xYYY # desired vga mode
> >>> fsb=133.3 # your fsb frequency
> >>> xnu_kernel <kernel> <command line>
> >>> <insert modules>
> >>> boot
> >>> Modules can be inserted one of the following ways:
> >>> 1) xnu_mkext <mkext file>
> >>> 2) xnu_kext extension.kext
> >>> 3) xnu_kext extension.kext/Info.plist extension.kext/extension
> >>> 4) xnu_kextdir <directory containing extensions>
> >>> It's also possible to execute these commands multiple times
> >>> The most typical case is
> >>> xnu_kernel /mach_kernel rd=disk0sX
> >>> xnu_mkext /System/Library/Extensions.mkext
> >>>
> >>>
> >>> If you need to add values to device tree the command
> >>> xnu_devtree <devtree file>
> >>> This file uses the following format:
> >>> valuename:valuedata;
> >>> keyname{
> >>> contents
> >>> }
> >>> keyname, valuename and valuedata are in hex.
> >>>
> >>> If you need to adda ramdisk execute
> >>> xnu_ramdisk <ramdisk file>
> >>> ramdisk will be exposed as /dev/md0 which may be used as boot device
> with
> >>> rd=md0.
> >>>
> >>> The areas which need more work (every help is welcome):
> >>> 1) testing on different platforms
> >>> 2) Detect "device-properties" value in device tree. There are several
> >>> trivial values in device tree present in boot.efi but not in grub2.
> These
> >>> ones are easy to add and AFAIK don't change anything. But the value
> >>> "device-properties" is difficult. I already know it's format but not
> where
> >>> the values come from. It contains info about gfx and sound card which
> may
> >>> not work if this value is missing. The current workaround is
> xnu_devtree
> >>> command with a dump of device-properties
> >>> 3) autodetect fsb frequency
> >>> 4) Support video splash
> >>> 5) Define and use an unified interface to retrieve video information
> >>> uniformly across platforms
> >>> 6) Boot by UUID. I know how to do it but need md5 for it which is a
> part
> >>> of pending luks patch
> >>> 7) Scripts for automatic creating of grub.cfg entries
> >>> 8) Support for prelinked kernel. It's compressed and I have yet looked
> >>> how to decompress it (seems it's compressed with something called lzss)
> >>> 9) Use claimmap once available (see my multiboot on efi patch)
> >>> 10) Better collaboration with memory management once advanced mm's
> >>> available
> >>> 11) Resume from encrypted hibernation
> >>>
> >>> --
> >>>
> >>> Regards
> >>> Vladimir 'phcoder' Serbinenko
> >>
> >>
> >>
> >> --
> >> Regards
> >> Vladimir 'phcoder' Serbinenko
> >
> >
> >
> > --
> > Regards
> > Vladimir 'phcoder' Serbinenko
> >
> > _______________________________________________
> > Grub-devel mailing list
> > Grub-devel@gnu.org
> > http://lists.gnu.org/mailman/listinfo/grub-devel
> >
> >
>
>
>
> --
> Bean
>
>
> _______________________________________________
> Grub-devel mailing list
> Grub-devel@gnu.org
> http://lists.gnu.org/mailman/listinfo/grub-devel
>
--
Regards
Vladimir 'phcoder' Serbinenko
[-- Attachment #2: Type: text/html, Size: 5981 bytes --]
^ permalink raw reply [flat|nested] 13+ messages in thread
end of thread, other threads:[~2009-05-03 9:19 UTC | newest]
Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-04-14 22:46 [PATCH] xnu phcoder
2009-04-17 23:22 ` Joey Korkames
2009-04-17 23:40 ` Vladimir Serbinenko
2009-04-17 23:55 ` Vladimir Serbinenko
2009-04-18 1:01 ` Joey Korkames
2009-04-18 18:52 ` Vladimir Serbinenko
2009-04-24 0:41 ` Joey Korkames
2009-04-24 7:28 ` Vladimir Serbinenko
2009-04-24 19:25 ` Joey Korkames
2009-04-27 19:42 ` Vladimir 'phcoder' Serbinenko
2009-05-02 23:20 ` Vladimir 'phcoder' Serbinenko
2009-05-03 4:32 ` Bean
2009-05-03 9:18 ` Vladimir 'phcoder' Serbinenko
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.