diff --git a/grub-core/loader/i386/xen_file.c b/grub-core/loader/i386/xen_file.c index 5836218..90e0a6e 100644 --- a/grub-core/loader/i386/xen_file.c +++ b/grub-core/loader/i386/xen_file.c @@ -20,12 +20,99 @@ #include #include +#define LZOP_MAGIC "\x89\x4c\x5a\x4f\x00\x0d\x0a\x1a\x0a" +#define LZOP_MAGIC_SIZE 9 +#define XZ_MAGIC "\3757zXZ\0" +#define XZ_MAGIC_SIZE 6 +#define ELF_MAGIC "\177ELF" +#define ELF_MAGIC_SIZE 4 +#define GZ_MAGIC "\x1F\x8B" +#define GZ_MAGIC_SIZE 2 +#define GZ_OLD_MAGIC "\x1F\x9E" +#define GZ_OLD_MAGIC_SIZE 2 + +#ifndef GRUB_UTIL + +#include + +static grub_uint8_t +mod_31 (grub_uint16_t v) +{ + /* At most 2 iterations for any number that + we can get here. + In any case faster than real division. */ + while (v > 0x1f) + v = (v & 0x1f) + (v >> 5); + if (v == 0x1f) + return 0; + return v; +} + +static int +is_zlib (grub_uint8_t *head) +{ + grub_uint8_t cmf, flg; + + cmf = head[0]; + flg = head[1]; + + if ((cmf & 0xf) != 8) + return 0; + + if (mod_31 (cmf + flg * 4) != 0) + return 0; + + if (flg & 0x20) + return 0; + + return 1; +} + +static void +autoload_filters (grub_file_t file, grub_uint32_t payload_offset) +{ + grub_uint8_t payload_header[LZOP_MAGIC_SIZE]; + + grub_file_seek (file, payload_offset); + + if (grub_file_read (file, &payload_header, sizeof (payload_header)) != sizeof (payload_header)) + { + grub_print_error (); + return; + } + + if (grub_memcmp (payload_header, ELF_MAGIC, ELF_MAGIC_SIZE) == 0) + { + /* Uncompressed. */ + } + else if (grub_memcmp (payload_header, XZ_MAGIC, XZ_MAGIC_SIZE) == 0) + { + grub_dl_load("xzio"); + grub_print_error (); + } + else if (grub_memcmp (payload_header, LZOP_MAGIC, LZOP_MAGIC_SIZE) == 0) + { + grub_dl_load("lzopio"); + grub_print_error (); + } + else if (grub_memcmp (payload_header, GZ_MAGIC, GZ_MAGIC_SIZE) == 0 + || grub_memcmp (payload_header, GZ_OLD_MAGIC, GZ_OLD_MAGIC_SIZE) == 0 + || is_zlib (payload_header)) + { + grub_dl_load("gzio"); + grub_print_error (); + } +} + +#endif + grub_elf_t grub_xen_file (grub_file_t file) { grub_elf_t elf; struct linux_kernel_header lh; grub_file_t off_file; + grub_uint32_t payload_offset; elf = grub_elf_file (file, file->name); if (elf) @@ -46,19 +133,24 @@ grub_xen_file (grub_file_t file) return NULL; } - if (lh.payload_length < 4) + if (lh.payload_length < LZOP_MAGIC_SIZE) { grub_error (GRUB_ERR_BAD_OS, "payload too short"); return NULL; } + payload_offset = (lh.setup_sects + 1) * 512 + lh.payload_offset; + +#ifndef GRUB_UTIL + autoload_filters (file, payload_offset); +#endif + grub_dprintf ("xen", "found bzimage payload 0x%llx-0x%llx\n", (unsigned long long) (lh.setup_sects + 1) * 512 + lh.payload_offset, (unsigned long long) lh.payload_length - 4); - off_file = grub_file_offset_open (file, (lh.setup_sects + 1) * 512 - + lh.payload_offset, + off_file = grub_file_offset_open (file, payload_offset, lh.payload_length - 4); if (!off_file) goto fail;