+2012-02-29 Vladimir Serbinenko <phcoder@gmail.com>
+
+ Add LZSS Mach-O support (needed for new xnu kernelcache).
+
+ * grub-core/Makefile.core.def (xnu): Add file lzss.c
+ * grub-core/loader/lzss.c: New file.
+ * grub-core/loader/xnu.c (grub_xnu_load_driver): Close binaryfile
+ on Mach-O open failure.
+ * grub-core/loader/macho.c (grub_macho_close): Free uncompressedXX.
+ Don't free cmdsXX in uncompressedXX is set.
+ (grub_macho_file): Init new fields.
+ New argument is_64bit. All users updated.
+ Handle compressed. Error out if no suitable architecture is found.
+ Don't close file.
+ (grub_macho_open): New argument is_64bit. All users updated.
+ * grub-core/loader/macho32.c: Add defines for new fields.
+ * grub-core/loader/macho64.c: Likewise.
+ * grub-core/loader/machoXX.c (grub_macho_contains_macho): Make static.
+ (grub_macho_parse): Handle compressed.
+ Defer actual processing if compressed.
+ (grub_macho_cmds_iterate): Decompress if compressed. New argument
+ "filename". All users updated.
+ (grub_macho_size): New argument "filename". All users updated.
+ (grub_macho_get_entry_point): Likewise.
+ (grub_macho_load): Handle compressed.
+ * include/grub/macho.h (grub_macho_lzss_header): New struct.
+ (GRUB_MACHO_LZSS_OFFSET): New define.
+ (grub_decompress_lzss): New proto.
+ * include/grub/machoload.h (grub_macho_file): New fields to handle
+ compressed.
+ (grub_macho_contains_macho64): Remove proto.
+ (grub_macho_contains_macho32): Likewise.
+ * util/grub.d/30_os-prober.in: Use kernel cache if available.
+
2012-02-29 Vladimir Serbinenko <phcoder@gmail.com>
* grub-core/disk/pata.c (grub_pata_readwrite): Fix ATAPI protocol error.
x86 = loader/macho64.c;
x86 = loader/macho.c;
x86 = loader/xnu.c;
+ x86 = loader/lzss.c;
extra_dist = loader/machoXX.c;
enable = x86;
--- /dev/null
+/**************************************************************
+ LZSS.C -- A Data Compression Program
+ (tab = 4 spaces)
+***************************************************************
+ 4/6/1989 Haruhiko Okumura
+ Use, distribute, and modify this program freely.
+ Please send me your improved versions.
+ PC-VAN SCIENCE
+ NIFTY-Serve PAF01022
+ CompuServe 74050,1022
+**************************************************************/
+
+#include <grub/types.h>
+#include <grub/macho.h>
+
+#define N 4096 /* size of ring buffer */
+#define F 18 /* upper limit for match_length */
+#define THRESHOLD 2 /* encode string into position and length
+ if match_length is greater than this */
+#define NIL N /* index for root of binary search trees */
+
+#define EOF -1
+#define getc(file) ((src < srcend) ? *src++ : EOF)
+#define putc(c, file) (dst < dstend) ? (*dst++ = (c)) : 0;
+
+grub_size_t
+grub_decompress_lzss (grub_uint8_t *dst, grub_uint8_t *dstend,
+ grub_uint8_t *src, grub_uint8_t *srcend)
+{
+ int i, j, k, r, c;
+ unsigned int flags;
+ static unsigned char text_buf[N + F - 1];
+ grub_uint8_t *dst0 = dst;
+
+ for (i = 0; i < N - F; i++) text_buf[i] = ' ';
+ r = N - F; flags = 0;
+ for ( ; ; ) {
+ if (((flags >>= 1) & 256) == 0) {
+ if ((c = getc(infile)) == EOF) break;
+ flags = c | 0xff00; /* uses higher byte cleverly */
+ } /* to count eight */
+ if (flags & 1) {
+ if ((c = getc(infile)) == EOF) break;
+ putc(c, outfile); text_buf[r++] = c; r &= (N - 1);
+ } else {
+ if ((i = getc(infile)) == EOF) break;
+ if ((j = getc(infile)) == EOF) break;
+ i |= ((j & 0xf0) << 4); j = (j & 0x0f) + THRESHOLD;
+ for (k = 0; k <= j; k++) {
+ c = text_buf[(i + k) & (N - 1)];
+ putc(c, outfile); text_buf[r++] = c; r &= (N - 1);
+ }
+ }
+ }
+ return dst - dst0;
+}
{
grub_file_t file = macho->file;
- grub_free (macho->cmds32);
- grub_free (macho->cmds64);
+ if (!macho->uncompressed32)
+ grub_free (macho->cmds32);
+ grub_free (macho->uncompressed32);
+ if (!macho->uncompressed64)
+ grub_free (macho->cmds64);
+ grub_free (macho->uncompressed64);
grub_free (macho);
}
grub_macho_t
-grub_macho_file (grub_file_t file, const char *filename)
+grub_macho_file (grub_file_t file, const char *filename, int is_64bit)
{
grub_macho_t macho;
union grub_macho_filestart filestart;
macho->end64 = -1;
macho->cmds32 = 0;
macho->cmds64 = 0;
+ macho->uncompressed32 = 0;
+ macho->uncompressed64 = 0;
+ macho->compressed32 = 0;
+ macho->compressed64 = 0;
if (grub_file_seek (macho->file, 0) == (grub_off_t) -1)
goto fail;
for (i = 0; i < narchs; i++)
{
if (GRUB_MACHO_CPUTYPE_IS_HOST32
- (grub_be_to_cpu32 (archs[i].cputype)))
+ (grub_be_to_cpu32 (archs[i].cputype)) && !is_64bit)
{
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)))
+ (grub_be_to_cpu32 (archs[i].cputype)) && is_64bit)
{
macho->offset64 = grub_be_to_cpu32 (archs[i].offset);
macho->end64 = grub_be_to_cpu32 (archs[i].offset)
}
/* Is it a thin 32-bit file? */
- if (filestart.thin32.magic == GRUB_MACHO_MAGIC32)
+ if (filestart.thin32.magic == GRUB_MACHO_MAGIC32 && !is_64bit)
{
macho->offset32 = 0;
macho->end32 = grub_file_size (file);
}
/* Is it a thin 64-bit file? */
- if (filestart.thin64.magic == GRUB_MACHO_MAGIC64)
+ if (filestart.thin64.magic == GRUB_MACHO_MAGIC64 && is_64bit)
+ {
+ macho->offset64 = 0;
+ macho->end64 = grub_file_size (file);
+ }
+
+ if (grub_memcmp (filestart.lzss.magic, GRUB_MACHO_LZSS_MAGIC,
+ sizeof (filestart.lzss.magic)) == 0 && !is_64bit)
+ {
+ macho->offset32 = 0;
+ macho->end32 = grub_file_size (file);
+ }
+
+ /* Is it a thin 64-bit file? */
+ if (grub_memcmp (filestart.lzss.magic, GRUB_MACHO_LZSS_MAGIC,
+ sizeof (filestart.lzss.magic)) == 0 && is_64bit)
{
macho->offset64 = 0;
macho->end64 = grub_file_size (file);
grub_macho_parse32 (macho, filename);
grub_macho_parse64 (macho, filename);
+ if (macho->offset32 == -1 && !is_64bit)
+ {
+ grub_error (GRUB_ERR_BAD_OS,
+ "Mach-O doesn't contain suitable 32-bit architecture");
+ goto fail;
+ }
+
+ if (macho->offset64 == -1 && is_64bit)
+ {
+ grub_error (GRUB_ERR_BAD_OS,
+ "Mach-O doesn't contain suitable 64-bit architecture");
+ goto fail;
+ }
+
return macho;
fail:
+ macho->file = 0;
grub_macho_close (macho);
return 0;
}
grub_macho_t
-grub_macho_open (const char *name)
+grub_macho_open (const char *name, int is_64bit)
{
grub_file_t file;
grub_macho_t macho;
if (! file)
return 0;
- macho = grub_macho_file (file, name);
+ macho = grub_macho_file (file, name, is_64bit);
if (! macho)
grub_file_close (file);
#define cmdsizeXX cmdsize32
#define cmdsXX cmds32
#define endXX end32
+#define uncompressedXX uncompressed32
+#define compressedXX compressed32
+#define uncompressed_sizeXX uncompressed_size32
+#define compressed_sizeXX compressed_size32
#define XX "32"
#define GRUB_MACHO_MAGIC GRUB_MACHO_MAGIC32
#define GRUB_MACHO_CMD_SEGMENT GRUB_MACHO_CMD_SEGMENT32
#define cmdsizeXX cmdsize64
#define cmdsXX cmds64
#define endXX end64
+#define uncompressedXX uncompressed64
+#define compressedXX compressed64
+#define uncompressed_sizeXX uncompressed_size64
+#define compressed_sizeXX compressed_size64
#define XX "64"
#define GRUB_MACHO_MAGIC GRUB_MACHO_MAGIC64
#define GRUB_MACHO_CMD_SEGMENT GRUB_MACHO_CMD_SEGMENT64
#define min(a,b) (((a) < (b)) ? (a) : (b))
-int
+static int
SUFFIX (grub_macho_contains_macho) (grub_macho_t macho)
{
return macho->offsetXX != -1;
void
SUFFIX (grub_macho_parse) (grub_macho_t macho, const char *filename)
{
- grub_macho_header_t head;
+ union {
+ struct grub_macho_lzss_header lzss;
+ grub_macho_header_t macho;
+ } head;
/* Is there any candidate at all? */
if (macho->offsetXX == -1)
return;
- /* Read header and check magic*/
+ /* Read header and check magic. */
if (grub_file_seek (macho->file, macho->offsetXX) == (grub_off_t) -1
|| grub_file_read (macho->file, &head, sizeof (head))
- != sizeof(head))
+ != sizeof (head))
{
if (!grub_errno)
grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
macho->offsetXX = -1;
return;
}
- if (head.magic != GRUB_MACHO_MAGIC)
+ if (grub_memcmp (head.lzss.magic, GRUB_MACHO_LZSS_MAGIC,
+ sizeof (head.lzss.magic)) == 0)
+ {
+ macho->compressed_sizeXX = grub_be_to_cpu32 (head.lzss.compressed_size);
+ macho->uncompressed_sizeXX
+ = grub_be_to_cpu32 (head.lzss.uncompressed_size);
+ if (macho->uncompressed_sizeXX < sizeof (head.macho))
+ {
+ grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+ filename);
+ macho->offsetXX = -1;
+ return;
+ }
+ /* Skip header check. */
+ macho->compressedXX = 1;
+ return;
+ }
+
+ if (head.macho.magic != GRUB_MACHO_MAGIC)
{
grub_error (GRUB_ERR_BAD_OS, "invalid Mach-O " XX "-bit header");
macho->offsetXX = -1;
}
/* Read commands. */
- macho->ncmdsXX = head.ncmds;
- macho->cmdsizeXX = head.sizeofcmds;
- macho->cmdsXX = grub_malloc(macho->cmdsizeXX);
+ macho->ncmdsXX = head.macho.ncmds;
+ macho->cmdsizeXX = head.macho.sizeofcmds;
+ macho->cmdsXX = grub_malloc (macho->cmdsizeXX);
if (! macho->cmdsXX)
return;
- if (grub_file_read (macho->file, macho->cmdsXX,
+ if (grub_file_seek (macho->file, macho->offsetXX
+ + sizeof (grub_macho_header_t)) == (grub_off_t) -1
+ || grub_file_read (macho->file, macho->cmdsXX,
(grub_size_t) macho->cmdsizeXX)
!= (grub_ssize_t) macho->cmdsizeXX)
{
static grub_err_t
grub_macho_cmds_iterate (grub_macho_t macho,
grub_macho_iter_hook_t hook,
- void *hook_arg)
+ void *hook_arg,
+ const char *filename)
{
- grub_uint8_t *hdrs = macho->cmdsXX;
+ grub_uint8_t *hdrs;
int i;
+
+ if (macho->compressedXX && !macho->uncompressedXX)
+ {
+ grub_uint8_t *tmp;
+ grub_macho_header_t *head;
+ macho->uncompressedXX = grub_malloc (macho->uncompressed_sizeXX);
+ if (!macho->uncompressedXX)
+ return grub_errno;
+ tmp = grub_malloc (macho->compressed_sizeXX);
+ if (!tmp)
+ {
+ grub_free (macho->uncompressedXX);
+ macho->uncompressedXX = 0;
+ return grub_errno;
+ }
+ if (grub_file_seek (macho->file, macho->offsetXX
+ + GRUB_MACHO_LZSS_OFFSET) == (grub_off_t) -1
+ || grub_file_read (macho->file, tmp,
+ (grub_size_t) macho->compressed_sizeXX)
+ != (grub_ssize_t) macho->compressed_sizeXX)
+ {
+ if (!grub_errno)
+ grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+ filename);
+ grub_free (tmp);
+ grub_free (macho->uncompressedXX);
+ macho->uncompressedXX = 0;
+ macho->offsetXX = -1;
+ return grub_errno;
+ }
+ if (grub_decompress_lzss (macho->uncompressedXX,
+ macho->uncompressedXX
+ + macho->uncompressed_sizeXX,
+ tmp, tmp + macho->compressed_sizeXX)
+ != macho->uncompressed_sizeXX)
+ {
+ if (!grub_errno)
+ grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+ filename);
+ grub_free (tmp);
+ grub_free (macho->uncompressedXX);
+ macho->uncompressedXX = 0;
+ macho->offsetXX = -1;
+ return grub_errno;
+ }
+ grub_free (tmp);
+ head = (grub_macho_header_t *) macho->uncompressedXX;
+ macho->ncmdsXX = head->ncmds;
+ macho->cmdsizeXX = head->sizeofcmds;
+ macho->cmdsXX = macho->uncompressedXX + sizeof (grub_macho_header_t);
+ if (sizeof (grub_macho_header_t) + macho->cmdsizeXX
+ >= macho->uncompressed_sizeXX)
+ {
+ grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+ filename);
+ grub_free (macho->uncompressedXX);
+ macho->uncompressedXX = 0;
+ macho->offsetXX = -1;
+ return grub_errno;
+ }
+ }
+
if (! macho->cmdsXX)
return grub_error (GRUB_ERR_BAD_OS, "couldn't find " XX "-bit Mach-O");
+ hdrs = macho->cmdsXX;
for (i = 0; i < macho->ncmdsXX; i++)
{
struct grub_macho_cmd *hdr = (struct grub_macho_cmd *) hdrs;
/* Calculate the amount of memory spanned by the segments. */
grub_err_t
SUFFIX (grub_macho_size) (grub_macho_t macho, grub_macho_addr_t *segments_start,
- grub_macho_addr_t *segments_end, int flags)
+ grub_macho_addr_t *segments_end, int flags,
+ const char *filename)
{
int nr_phdrs = 0;
*segments_start = (grub_macho_addr_t) -1;
*segments_end = 0;
- grub_macho_cmds_iterate (macho, calcsize, 0);
+ grub_macho_cmds_iterate (macho, calcsize, 0, filename);
if (nr_phdrs == 0)
return grub_error (GRUB_ERR_BAD_OS, "no program headers present");
if (! hdr->vmsize)
return 0;
- if (grub_file_seek (_macho->file, hdr->fileoff
- + _macho->offsetXX) == (grub_off_t) -1)
- 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))
+ grub_ssize_t read, toread = min (hdr->filesize, hdr->vmsize);
+ if (macho->uncompressedXX)
+ {
+ if (hdr->fileoff + toread
+ > _macho->uncompressed_sizeXX)
+ read = -1;
+ else
+ {
+ read = toread;
+ grub_memcpy (offset + hdr->vmaddr,
+ _macho->uncompressedXX + hdr->fileoff, read);
+ }
+ }
+ else
+ {
+ if (grub_file_seek (_macho->file, hdr->fileoff
+ + _macho->offsetXX) == (grub_off_t) -1)
+ return 1;
+ read = grub_file_read (_macho->file, offset + hdr->vmaddr,
+ toread);
+ }
+
+ if (read != toread)
{
/* XXX How can we free memory from `load_hook'? */
if (!grub_errno)
if (darwin_version)
*darwin_version = 0;
- grub_macho_cmds_iterate (macho, do_load, 0);
+ grub_macho_cmds_iterate (macho, do_load, 0, filename);
return grub_errno;
}
grub_macho_addr_t
-SUFFIX (grub_macho_get_entry_point) (grub_macho_t macho)
+SUFFIX (grub_macho_get_entry_point) (grub_macho_t macho, const char *filename)
{
grub_macho_addr_t entry_point = 0;
auto int NESTED_FUNC_ATTR hook(grub_macho_t _macho,
entry_point = ((grub_macho_thread_t *) hdr)->entry_point;
return 0;
}
- grub_macho_cmds_iterate (macho, hook, 0);
+ grub_macho_cmds_iterate (macho, hook, 0, filename);
return entry_point;
}
grub_xnu_unload ();
- macho = grub_macho_open (args[0]);
+ macho = grub_macho_open (args[0], 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 32-bit architecture");
- }
- err = grub_macho_size32 (macho, &startcode, &endcode, GRUB_MACHO_NOBSS);
+ err = grub_macho_size32 (macho, &startcode, &endcode, GRUB_MACHO_NOBSS,
+ args[0]);
if (err)
{
grub_macho_close (macho);
return err;
}
- grub_xnu_entry_point = grub_macho_get_entry_point32 (macho);
+ grub_xnu_entry_point = grub_macho_get_entry_point32 (macho, args[0]);
if (! grub_xnu_entry_point)
{
grub_macho_close (macho);
grub_xnu_unload ();
- macho = grub_macho_open (args[0]);
+ macho = grub_macho_open (args[0], 1);
if (! macho)
return grub_errno;
- if (! grub_macho_contains_macho64 (macho))
- {
- grub_macho_close (macho);
- return grub_error (GRUB_ERR_BAD_OS,
- "kernel doesn't contain suitable 64-bit architecture");
- }
- err = grub_macho_size64 (macho, &startcode, &endcode, GRUB_MACHO_NOBSS);
+ err = grub_macho_size64 (macho, &startcode, &endcode, GRUB_MACHO_NOBSS,
+ args[0]);
if (err)
{
grub_macho_close (macho);
return err;
}
- grub_xnu_entry_point = grub_macho_get_entry_point64 (macho) & 0x0fffffff;
+ grub_xnu_entry_point = grub_macho_get_entry_point64 (macho, args[0])
+ & 0x0fffffff;
if (! grub_xnu_entry_point)
{
grub_macho_close (macho);
/* Compute the needed space. */
if (binaryfile)
{
- macho = grub_macho_file (binaryfile, filename);
- if (! macho || ! grub_macho_contains_macho32 (macho))
+ macho = grub_macho_file (binaryfile, filename, grub_xnu_is_64bit);
+ if (!macho)
+ grub_file_close (binaryfile);
+ else
{
- if (macho)
- grub_macho_close (macho);
- return grub_error (GRUB_ERR_BAD_OS,
- "extension doesn't contain suitable architecture");
+ if (grub_xnu_is_64bit)
+ machosize = grub_macho_filesize64 (macho);
+ else
+ machosize = grub_macho_filesize32 (macho);
}
- if (grub_xnu_is_64bit)
- machosize = grub_macho_filesize64 (macho);
- else
- machosize = grub_macho_filesize32 (macho);
neededspace += machosize;
}
else
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
{
#define GRUB_MACHO_CMD_THREAD 5
+struct grub_macho_lzss_header
+{
+ char magic[8];
+#define GRUB_MACHO_LZSS_MAGIC "complzss"
+ grub_uint32_t unused;
+ grub_uint32_t uncompressed_size;
+ grub_uint32_t compressed_size;
+};
+
+/* 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;
+ struct grub_macho_lzss_header lzss;
+} __attribute__ ((packed));
+
+#define GRUB_MACHO_LZSS_OFFSET 0x180
+
+grub_size_t
+grub_decompress_lzss (grub_uint8_t *dst, grub_uint8_t *dstend,
+ grub_uint8_t *src, grub_uint8_t *srcend);
+
#endif
int ncmds32;
grub_size_t cmdsize32;
grub_uint8_t *cmds32;
+ grub_uint8_t *uncompressed32;
+ int compressed32;
+ grub_size_t compressed_size32;
+ grub_size_t uncompressed_size32;
grub_ssize_t offset64;
grub_ssize_t end64;
int ncmds64;
grub_size_t cmdsize64;
grub_uint8_t *cmds64;
+ grub_uint8_t *uncompressed64;
+ int compressed64;
+ grub_size_t compressed_size64;
+ grub_size_t uncompressed_size64;
};
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 file, const char *filename);
+grub_macho_t grub_macho_open (const char *, int is_64bit);
+grub_macho_t grub_macho_file (grub_file_t file, const char *filename,
+ int is_64bit);
grub_err_t grub_macho_close (grub_macho_t);
-int grub_macho_contains_macho32 (grub_macho_t);
grub_err_t grub_macho_size32 (grub_macho_t macho, grub_uint32_t *segments_start,
- grub_uint32_t *segments_end, int flags);
-grub_uint32_t grub_macho_get_entry_point32 (grub_macho_t macho);
+ grub_uint32_t *segments_end, int flags,
+ const char *filename);
+grub_uint32_t grub_macho_get_entry_point32 (grub_macho_t macho,
+ const char *filename);
-int grub_macho_contains_macho64 (grub_macho_t);
grub_err_t grub_macho_size64 (grub_macho_t macho, grub_uint64_t *segments_start,
- grub_uint64_t *segments_end, int flags);
-grub_uint64_t grub_macho_get_entry_point64 (grub_macho_t macho);
+ grub_uint64_t *segments_end, int flags,
+ const char *filename);
+grub_uint64_t grub_macho_get_entry_point64 (grub_macho_t macho,
+ const char *filename);
/* Ignore BSS segments when loading. */
#define GRUB_MACHO_NOBSS 0x1
if [ -f /Extra/DSDT.aml ]; then
acpi -e /Extra/DSDT.aml
fi
- $1 /mach_kernel boot-uuid=\${uuid} rd=*uuid
- if [ /System/Library/Extensions.mkext -nt /System/Library/Extensions ]; then
+ if [ /kernelcache -nt /System/Library/Extensions ]; then
+ $1 /kernelcache boot-uuid=\${uuid} rd=*uuid
+ else
+ $1 /mach_kernel boot-uuid=\${uuid} rd=*uuid
+ elif [ /System/Library/Extensions.mkext -nt /System/Library/Extensions ]; then
xnu_mkext /System/Library/Extensions.mkext
else
xnu_kextdir /System/Library/Extensions