diff options
Diffstat (limited to 'grub-core/loader/xnu_resume.c')
-rw-r--r-- | grub-core/loader/xnu_resume.c | 188 |
1 files changed, 188 insertions, 0 deletions
diff --git a/grub-core/loader/xnu_resume.c b/grub-core/loader/xnu_resume.c new file mode 100644 index 0000000..d648ef0 --- /dev/null +++ b/grub-core/loader/xnu_resume.c @@ -0,0 +1,188 @@ +/* + * 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> +#include <grub/i18n.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 *code; + void *image; + grub_uint32_t codedest; + grub_uint32_t codesize; + grub_addr_t target_image; + grub_err_t err; + + file = grub_file_open (imagename, GRUB_FILE_TYPE_XNU_HIBERNATE_IMAGE + | GRUB_FILE_TYPE_NO_DECOMPRESS); + if (! file) + return 0; + + /* Read the header. */ + if (grub_file_read (file, &hibhead, sizeof (hibhead)) + != sizeof (hibhead)) + { + grub_file_close (file); + if (!grub_errno) + grub_error (GRUB_ERR_READ_ERROR, + N_("premature end of file %s"), imagename); + return grub_errno; + } + + /* 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"); + } + + if (hibhead.image_size == 0) + { + grub_file_close (file); + return grub_error (GRUB_ERR_BAD_OS, + "hibernate image is empty"); + } + + 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_relocator = grub_relocator_new (); + if (!grub_xnu_relocator) + { + grub_file_close (file); + return grub_errno; + } + + { + grub_relocator_chunk_t ch; + err = grub_relocator_alloc_chunk_addr (grub_xnu_relocator, &ch, codedest, + codesize + GRUB_XNU_PAGESIZE); + if (err) + { + grub_file_close (file); + return err; + } + code = get_virtual_current_address (ch); + } + + { + grub_relocator_chunk_t ch; + err = grub_relocator_alloc_chunk_align (grub_xnu_relocator, &ch, 0, + UP_TO_TOP32 (hibhead.image_size), + hibhead.image_size, + GRUB_XNU_PAGESIZE, + GRUB_RELOCATOR_PREFERENCE_NONE, 0); + if (err) + { + grub_file_close (file); + return err; + } + image = get_virtual_current_address (ch); + target_image = get_physical_target_address (ch); + } + + /* Read code part. */ + if (grub_file_seek (file, total_header_size) == (grub_off_t) -1 + || grub_file_read (file, code, codesize) + != (grub_ssize_t) codesize) + { + grub_file_close (file); + if (!grub_errno) + grub_error (GRUB_ERR_READ_ERROR, + N_("premature end of file %s"), imagename); + return grub_errno; + } + + /* Read image. */ + if (grub_file_seek (file, 0) == (grub_off_t) -1 + || grub_file_read (file, image, hibhead.image_size) + != (grub_ssize_t) hibhead.image_size) + { + grub_file_close (file); + if (!grub_errno) + grub_error (GRUB_ERR_READ_ERROR, + N_("premature end of file %s"), imagename); + return grub_errno; + } + grub_file_close (file); + + /* Setup variables needed by asm helper. */ + grub_xnu_heap_target_start = codedest; + grub_xnu_heap_size = target_image + hibhead.image_size - codedest; + grub_xnu_stack = (codedest + hibhead.stack); + grub_xnu_entry_point = (codedest + hibhead.entry_point); + grub_xnu_arg1 = target_image; + + grub_dprintf ("xnu", "entry point 0x%x\n", codedest + hibhead.entry_point); + grub_dprintf ("xnu", "image at 0x%x\n", + codedest + codesize + GRUB_XNU_PAGESIZE); + + /* We're ready now. */ + grub_loader_set (grub_xnu_boot_resume, + grub_xnu_resume_unload, 0); + + /* Prevent module from unloading. */ + grub_xnu_lock (); + return GRUB_ERR_NONE; +} |