diff --git a/grub-core/loader/i386/linux.c b/grub-core/loader/i386/linux.c index fddcc46..d3870ae 100644 --- a/grub-core/loader/i386/linux.c +++ b/grub-core/loader/i386/linux.c @@ -35,6 +35,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -673,11 +674,11 @@ grub_linux_unload (void) return GRUB_ERR_NONE; } + static grub_err_t -grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), - int argc, char *argv[]) +linux_load (grub_file_t file, grub_off_t fileoffset, grub_off_t filesize, + const char *extra_cmdline, int argc, char *argv[]) { - grub_file_t file = 0; struct linux_kernel_header lh; grub_uint8_t setup_sects; grub_size_t real_size, prot_size, prot_file_size; @@ -689,15 +690,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), grub_dl_ref (my_mod); - if (argc == 0) - { - grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); - goto fail; - } - - file = grub_file_open (argv[0]); - if (! file) - goto fail; + grub_file_seek (file, fileoffset); if (grub_file_read (file, &lh, sizeof (lh)) != sizeof (lh)) { @@ -757,7 +750,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), setup_sects = GRUB_LINUX_DEFAULT_SETUP_SECTS; real_size = setup_sects << GRUB_DISK_SECTOR_BITS; - prot_file_size = grub_file_size (file) - real_size - GRUB_DISK_SECTOR_SIZE; + prot_file_size = filesize - real_size - GRUB_DISK_SECTOR_SIZE; if (grub_le_to_cpu16 (lh.version) >= 0x205 && lh.kernel_alignment != 0 @@ -871,7 +864,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), /* The other parameters are filled when booting. */ - grub_file_seek (file, real_size + GRUB_DISK_SECTOR_SIZE); + grub_file_seek (file, fileoffset + real_size + GRUB_DISK_SECTOR_SIZE); grub_dprintf ("linux", "bzImage, setup=0x%x, size=0x%x\n", (unsigned) real_size, (unsigned) prot_size); @@ -1008,12 +1001,32 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), linux_cmdline = grub_zalloc (maximal_cmdline_size + 1); if (!linux_cmdline) goto fail; - grub_memcpy (linux_cmdline, LINUX_IMAGE, sizeof (LINUX_IMAGE)); - grub_create_loader_cmdline (argc, argv, - linux_cmdline - + sizeof (LINUX_IMAGE) - 1, - maximal_cmdline_size - - (sizeof (LINUX_IMAGE) - 1)); + char *cmdline_ptr = linux_cmdline; + char *cmdline_ptr_end = linux_cmdline + maximal_cmdline_size; + /* Construct BOOT_IMAGE=/boot/... */ + cmdline_ptr = grub_stpcpy (cmdline_ptr, LINUX_IMAGE); + grub_create_loader_cmdline (1, argv, cmdline_ptr, + cmdline_ptr_end - cmdline_ptr); + cmdline_ptr += grub_strlen (cmdline_ptr); + /* Extra */ + if (cmdline_ptr < cmdline_ptr_end - 1 && *extra_cmdline) + { + *cmdline_ptr++ = ' '; + cmdline_ptr = grub_stpncpy (cmdline_ptr, extra_cmdline, + cmdline_ptr_end - cmdline_ptr); + } + + /* Rest of command line. */ + if (cmdline_ptr < cmdline_ptr_end - 1 && argc > 1) + { + *cmdline_ptr++ = ' '; + grub_create_loader_cmdline (argc - 1, argv + 1, cmdline_ptr, + cmdline_ptr_end - cmdline_ptr); + } + else + { + *cmdline_ptr = '\0'; + } len = prot_file_size; if (grub_file_read (file, prot_mode_mem, len) != len && !grub_errno) @@ -1029,9 +1042,6 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), fail: - if (file) - grub_file_close (file); - if (grub_errno != GRUB_ERR_NONE) { grub_dl_unref (my_mod); @@ -1042,31 +1052,32 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), } static grub_err_t -grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), - int argc, char *argv[]) +grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), + int argc, char *argv[]) { - grub_size_t size = 0, aligned_size = 0; - grub_addr_t addr_min, addr_max; - grub_addr_t addr; + grub_file_t file = 0; grub_err_t err; - struct grub_linux_initrd_context initrd_ctx = { 0, 0, 0 }; - if (argc == 0) - { - grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); - goto fail; - } + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); - if (! loaded) - { - grub_error (GRUB_ERR_BAD_ARGUMENT, N_("you need to load the kernel first")); - goto fail; - } + file = grub_file_open (argv[0]); + if (! file) + return grub_errno; - if (grub_initrd_init (argc, argv, &initrd_ctx)) - goto fail; + err = linux_load (file, 0, grub_file_size (file), "", argc, argv); + grub_file_close (file); + return err; +} + +static grub_err_t +load_initrd (struct grub_linux_initrd_context *initrd_ctx, char *filenames[]) +{ + grub_size_t size = 0, aligned_size = 0; + grub_addr_t addr_min, addr_max; + grub_addr_t addr; + grub_err_t err; - size = grub_get_initrd_size (&initrd_ctx); + size = grub_get_initrd_size (initrd_ctx); aligned_size = ALIGN_UP (size, 4096); /* Get the highest address available for the initrd. */ @@ -1098,10 +1109,7 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), addr = (addr_max - aligned_size) & ~0xFFF; if (addr < addr_min) - { - grub_error (GRUB_ERR_OUT_OF_RANGE, "the initrd is too big"); - goto fail; - } + return grub_error (GRUB_ERR_OUT_OF_RANGE, "the initrd is too big"); { grub_relocator_chunk_t ch; @@ -1116,8 +1124,9 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), initrd_mem_target = get_physical_target_address (ch); } - if (grub_initrd_load (&initrd_ctx, argv, initrd_mem)) - goto fail; + err = grub_initrd_load (initrd_ctx, filenames, initrd_mem); + if (err) + return err; grub_dprintf ("linux", "Initrd, addr=0x%x, size=0x%x\n", (unsigned) addr, (unsigned) size); @@ -1126,18 +1135,123 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), linux_params.ramdisk_size = size; linux_params.root_dev = 0x0100; /* XXX */ - fail: + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), + int argc, char *argv[]) +{ + struct grub_linux_initrd_context initrd_ctx = { 0, 0, 0 }; + grub_err_t err; + + if (argc == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); + + if (! loaded) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("you need to load the kernel first")); + + err = grub_initrd_init (argc, argv, &initrd_ctx); + + if (!err) + err = load_initrd (&initrd_ctx, argv); + grub_initrd_close (&initrd_ctx); + return err; +} + +static grub_err_t +grub_cmd_android_bootimg (grub_command_t cmd __attribute__ ((unused)), + int argc, char *argv[]) +{ + grub_file_t file = 0; + grub_err_t err = GRUB_ERR_NONE; + int do_load_initrd = 1, do_load_cmdline = 1; + struct grub_android_boot_img_hdr boot_img_header; + char *cmdline = NULL; + + for (; argc > 0; argc--, argv++) + { + if (grub_strcmp (argv[0], "--no-cmdline") == 0) + { + do_load_cmdline = 0; + continue; + } + if (grub_strcmp (argv[0], "--no-initrd") == 0) + { + do_load_initrd = 0; + continue; + } + break; + } + + if (argc == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); + + file = grub_file_open (argv[0]); + if (! file) + return grub_errno; + + if (grub_file_read (file, &boot_img_header, sizeof (boot_img_header)) != sizeof (boot_img_header)) + { + if (!grub_errno) + grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), + argv[0]); + err = grub_errno; + goto fail; + } + + if (grub_memcmp (boot_img_header.magic, GRUB_ANDROID_BOOT_MAGIC, GRUB_ANDROID_BOOT_MAGIC_SIZE) != 0) + { + err = grub_error (GRUB_ERR_BAD_OS, "invalid android magic"); + goto fail; + } + + if (do_load_cmdline) + { + cmdline = grub_malloc (GRUB_ANDROID_BOOT_EXTRA_ARGS_SIZE + + GRUB_ANDROID_BOOT_ARGS_SIZE + 1); + if (!cmdline) + goto fail; + grub_memcpy (cmdline, boot_img_header.cmdline, GRUB_ANDROID_BOOT_ARGS_SIZE); + grub_memcpy (cmdline + GRUB_ANDROID_BOOT_ARGS_SIZE, boot_img_header.extra_cmdline, GRUB_ANDROID_BOOT_EXTRA_ARGS_SIZE); + cmdline[GRUB_ANDROID_BOOT_EXTRA_ARGS_SIZE + GRUB_ANDROID_BOOT_ARGS_SIZE] = '\0'; + } + + err = linux_load (file, boot_img_header.page_size, boot_img_header.kernel_size, cmdline ? : "", argc, argv); + if (err) + goto fail; + + if (do_load_initrd && boot_img_header.ramdisk_size) + { + struct grub_linux_initrd_component component = { + .size = boot_img_header.ramdisk_size, + .newc_name = 0, + .file = file, + .fileoffset = ALIGN_UP (boot_img_header.kernel_size, boot_img_header.page_size) + }; + struct grub_linux_initrd_context initrd_ctx = { + .nfiles = 1, + .size = boot_img_header.ramdisk_size, + .components = &component + }; + err = load_initrd (&initrd_ctx, argv); + } + fail: + grub_file_close (file); return grub_errno; } -static grub_command_t cmd_linux, cmd_initrd; + +static grub_command_t cmd_linux, cmd_initrd, cmd_android_bootimg; GRUB_MOD_INIT(linux) { cmd_linux = grub_register_command ("linux", grub_cmd_linux, 0, N_("Load Linux.")); + cmd_android_bootimg = grub_register_command ("android_bootimg", grub_cmd_android_bootimg, + "[--no-initrd] [--no-cmdline] IMAGE [OPTIONS]", N_("Load Android bootimg.")); cmd_initrd = grub_register_command ("initrd", grub_cmd_initrd, 0, N_("Load initrd.")); my_mod = mod; @@ -1147,4 +1261,5 @@ GRUB_MOD_FINI(linux) { grub_unregister_command (cmd_linux); grub_unregister_command (cmd_initrd); + grub_unregister_command (cmd_android_bootimg); } diff --git a/grub-core/loader/linux.c b/grub-core/loader/linux.c index be6fa0f..dea0299 100644 --- a/grub-core/loader/linux.c +++ b/grub-core/loader/linux.c @@ -23,13 +23,6 @@ struct newc_head char check[8]; } GRUB_PACKED; -struct grub_linux_initrd_component -{ - grub_file_t file; - char *newc_name; - grub_off_t size; -}; - struct dir { char *name; @@ -208,6 +201,7 @@ grub_initrd_init (int argc, char *argv[], initrd_ctx->nfiles++; initrd_ctx->components[i].size = grub_file_size (initrd_ctx->components[i].file); + initrd_ctx->components[i].fileoffset = 0; initrd_ctx->size += initrd_ctx->components[i].size; } @@ -279,6 +273,8 @@ grub_initrd_load (struct grub_linux_initrd_context *initrd_ctx, } cursize = initrd_ctx->components[i].size; + grub_file_seek (initrd_ctx->components[i].file, + initrd_ctx->components[i].fileoffset); if (grub_file_read (initrd_ctx->components[i].file, ptr, cursize) != cursize) { diff --git a/include/grub/android.h b/include/grub/android.h new file mode 100644 index 0000000..7cd25f4 --- /dev/null +++ b/include/grub/android.h @@ -0,0 +1,73 @@ +/* tools/mkbootimg/bootimg.h +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +#ifndef _GRUB_ANDROID_BOOT_IMAGE_H_ +#define _GRUB_ANDROID_BOOT_IMAGE_H_ + +#include + +#define GRUB_ANDROID_BOOT_MAGIC "ANDROID!" +#define GRUB_ANDROID_BOOT_MAGIC_SIZE 8 +#define GRUB_ANDROID_BOOT_NAME_SIZE 16 +#define GRUB_ANDROID_BOOT_ARGS_SIZE 512 +#define GRUB_ANDROID_BOOT_EXTRA_ARGS_SIZE 1024 +struct grub_android_boot_img_hdr +{ + grub_uint8_t magic[GRUB_ANDROID_BOOT_MAGIC_SIZE]; + grub_uint32_t kernel_size; /* size in bytes */ + grub_uint32_t kernel_addr; /* physical load addr */ + grub_uint32_t ramdisk_size; /* size in bytes */ + grub_uint32_t ramdisk_addr; /* physical load addr */ + grub_uint32_t second_size; /* size in bytes */ + grub_uint32_t second_addr; /* physical load addr */ + grub_uint32_t tags_addr; /* physical addr for kernel tags */ + grub_uint32_t page_size; /* flash page size we assume */ + grub_uint32_t unused[2]; /* future expansion: should be 0 */ + grub_uint8_t name[GRUB_ANDROID_BOOT_NAME_SIZE]; /* asciiz product name */ + grub_uint8_t cmdline[GRUB_ANDROID_BOOT_ARGS_SIZE]; + grub_uint32_t id[8]; /* timestamp / checksum / sha1 / etc */ + /* Supplemental command line data; kept here to maintain + * binary compatibility with older versions of mkbootimg */ + grub_uint8_t extra_cmdline[GRUB_ANDROID_BOOT_EXTRA_ARGS_SIZE]; +} __attribute__((packed)); +/* +** +-----------------+ +** | boot header | 1 page +** +-----------------+ +** | kernel | n pages +** +-----------------+ +** | ramdisk | m pages +** +-----------------+ +** | second stage | o pages +** +-----------------+ +** +** n = (kernel_size + page_size - 1) / page_size +** m = (ramdisk_size + page_size - 1) / page_size +** o = (second_size + page_size - 1) / page_size +** +** 0. all entities are page_size aligned in flash +** 1. kernel and ramdisk are required (size != 0) +** 2. second is optional (second_size == 0 -> no second) +** 3. load each element (kernel, ramdisk, second) at +** the specified physical address (kernel_addr, etc) +** 4. prepare tags at tag_addr. kernel_args[] is +** appended to the kernel commandline in the tags. +** 5. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr +** 6. if second_size != 0: jump to second_addr +** else: jump to kernel_addr +*/ + +#endif diff --git a/include/grub/linux.h b/include/grub/linux.h index 594a3f3..4a99d78 100644 --- a/include/grub/linux.h +++ b/include/grub/linux.h @@ -9,6 +9,14 @@ struct grub_linux_initrd_context grub_size_t size; }; +struct grub_linux_initrd_component +{ + grub_file_t file; + char *newc_name; + grub_off_t fileoffset; + grub_off_t size; +}; + grub_err_t grub_initrd_init (int argc, char *argv[], struct grub_linux_initrd_context *ctx); diff --git a/include/grub/misc.h b/include/grub/misc.h index 2a9f87c..ef9c9b6 100644 --- a/include/grub/misc.h +++ b/include/grub/misc.h @@ -64,6 +64,17 @@ grub_stpcpy (char *dest, const char *src) return d - 1; } +static inline char * +grub_stpncpy (char *dest, const char *src, int c) +{ + char *p = dest; + + while ((*p++ = *src++) != '\0' && --c) + ; + + return p - 1; +} + /* XXX: If grub_memmove is too slow, we must implement grub_memcpy. */ static inline void * grub_memcpy (void *dest, const void *src, grub_size_t n)