grub-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

xnu support


From: phcoder
Subject: xnu support
Date: Wed, 28 Jan 2009 06:53:02 +0100
User-agent: Thunderbird 2.0.0.19 (X11/20090105)

Hello, I'm working on xnu (darwin kernel, under OSI approved APSL) support and would like to discuss some interfaces about it. Issue 1) First in xnu world booter hands the loading of compressed hibernation image. Resuming doesn't need to load the kernel conventional way. And trying to resume from hibernate should be done before trying to load kernel. So I propose the following configuration entries:
menuentry "my XNU"
{
   if ! xnu_resume <hibernate image>; then
        xnu_kernel <kernel> <options>
        xnu_extensions --cache=<cache file> --dir=<cache directory>
        xnu_loadenv <environment description file>
   fi
}

Issue 2) Also normally when hibernation succeeds kernel should invalidate the image. Unfortunately in some scenarios it doesn't. A reliable way to check the image is to compare modification timestamp of FS and image. I know where to find them on FS but unfortunately now grub has no way of interfacing this with FS code. I propose to modify
  /* Call HOOK with each file under DIR.  */
  grub_err_t (*dir) (grub_device_t device, const char *path,
                     int (*hook) (const char *filename, int dir));
to
  /* Call HOOK with each file under DIR.  */
  grub_err_t (*dir) (grub_device_t device, const char *path,
int (*hook) (const char *filename, int dir, grub_time_t modtime));
Or add
grub_err_t (*file_modtime) (const char *name, grub_time_t *modtime);
and add a function for retrieving FS modification type
grub_err_t (*fs_modtime) (grub_device_t device, grub_time_t *modtime);
to grub_fs_t
This has an additional benefit of invalidating image if FS was modified by external tool. This may be overriden by --force option to override it. File modification timestamp are also needed to check if the extension cache is up to date. Issue 3) Kernel may theoretically request loading parts to any address. In practice it does it only in 1Mb-64Mb range. Is it possible to somehow secure this range from grub_malloc after xnu_kernel command? (I would like to avoid "just before boot" relocations if possible)

I send an implementation of xnu_resume. Not for inclusion of course but for illustration
Thanks
Any opinion is appreciated
Vladimir 'phcoder' Serbinenko
Index: conf/i386.rmk
===================================================================
--- conf/i386.rmk       (revision 1962)
+++ conf/i386.rmk       (working copy)
@@ -14,3 +14,8 @@
 vga_text_mod_SOURCES = term/i386/pc/vga_text.c term/i386/vga_common.c
 vga_text_mod_CFLAGS = $(COMMON_CFLAGS)
 vga_text_mod_LDFLAGS = $(COMMON_LDFLAGS)
+
+pkglib_MODULES += xnu.mod
+xnu_mod_SOURCES = loader/xnu_resume.c loader/i386/xnu.c
+xnu_mod_CFLAGS = $(COMMON_CFLAGS)
+xnu_mod_LDFLAGS = $(COMMON_LDFLAGS)
Index: include/grub/i386/xnu.h
===================================================================
--- include/grub/i386/xnu.h     (revision 0)
+++ include/grub/i386/xnu.h     (revision 0)
@@ -0,0 +1,9 @@
+#define XNU_MEMORY_START  0x100000
+#define XNU_MEMORY_END 0x4000000
+#define XNU_PAGESIZE 4096
+
+grub_err_t grub_xnu_boot (void);
+extern void *grub_xnu_entry_point;
+extern void *grub_xnu_stack;
+extern void *grub_xnu_arg1;
+
Index: include/grub/xnu.h
===================================================================
--- include/grub/xnu.h  (revision 0)
+++ include/grub/xnu.h  (revision 0)
@@ -0,0 +1,18 @@
+#define XNU_HIBERNATE_MAGIC 0x73696d65
+struct xnu_hibernate_header
+{
+  grub_uint64_t image_size;
+  grub_uint8_t unknown1[8];
+  grub_uint32_t restorepage;
+  grub_uint32_t numofpages;
+  grub_uint32_t entry_point;
+  grub_uint32_t stack;
+  grub_uint8_t unknown2[20];
+  grub_uint32_t restored_checksum;
+  grub_uint8_t unknown3[20];
+  grub_uint32_t magic;
+  grub_uint8_t unknown4[28];
+  grub_uint64_t encoffset;
+  grub_uint8_t unknown5[360];
+  grub_uint32_t extmapsize;
+} __attribute__ ((packed));
Index: loader/i386/xnu.c
===================================================================
--- loader/i386/xnu.c   (revision 0)
+++ loader/i386/xnu.c   (revision 0)
@@ -0,0 +1,27 @@
+#include <grub/normal.h>
+#include <grub/dl.h>
+#include <grub/arg.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/cpu/loader.h>
+
+/* Registers on XNU boot: eip,esp and eax*/
+void *grub_xnu_entry_point=0;
+void *grub_xnu_stack=0;
+void *grub_xnu_arg1=0;
+
+grub_err_t 
+grub_xnu_boot (void)
+{
+  /*Setup regs and launch xnu*/
+  asm volatile ("movl %0, %%eax" : : "m" (grub_xnu_arg1));
+  asm volatile ("movl %0, %%ebx" : : "m" (grub_xnu_stack));
+  asm volatile ("movl %0, %%ecx" : : "m" (grub_xnu_entry_point));
+  asm volatile ("movl %%ebx, %%esp" : : );
+  asm volatile ("jmp *%%ecx" : : );
+}
Index: loader/xnu_resume.c
===================================================================
--- loader/xnu_resume.c (revision 0)
+++ loader/xnu_resume.c (revision 0)
@@ -0,0 +1,141 @@
+#include <grub/normal.h>
+#include <grub/dl.h>
+#include <grub/arg.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/cpu/loader.h>
+
+static grub_dl_t my_mod;
+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;
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_cmd_xnu_resume (struct grub_arg_list *state __attribute__ ((unused)),
+                       int argc, char **args)
+
+{
+  grub_file_t file;
+  grub_size_t total_header_size;
+  struct xnu_hibernate_header hibhead;
+  void *buf;
+  grub_uint32_t csum1, csum2;
+  grub_uint8_t *ptr;
+
+  if (argc != 1)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required");
+
+  file = grub_file_open (args[0]);
+  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 != XNU_HIBERNATE_MAGIC)
+    {
+      grub_file_close (file);
+      return grub_error (GRUB_ERR_BAD_OS, 
+                        "hibernate header has incorrect magick number");
+    }
+  if (hibhead.encoffset)
+    {
+      grub_file_close (file);
+      return grub_error (GRUB_ERR_BAD_OS, 
+                        "encrypted images aren't supported yet");
+    }
+  if (hibhead.restorepage*XNU_PAGESIZE<XNU_MEMORY_START 
+      || (hibhead.restorepage+hibhead.numofpages)*XNU_PAGESIZE>XNU_MEMORY_END)
+    {
+      grub_file_close (file);
+      return grub_error (GRUB_ERR_BAD_OS, 
+                        "loading address isn't within allowable space");
+    }
+
+  /*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*/
+  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*/
+  grub_file_seek (file, 0);
+  grub_file_read (file, buf, hibhead.image_size);
+  grub_file_close (file);
+
+  /* Checksum the pages to copy */
+  csum1=1;
+  csum2=0;
+  for (ptr=(grub_uint8_t*)buf+total_header_size;
+       ptr<(grub_uint8_t*)buf+total_header_size
+        +hibhead.numofpages*XNU_PAGESIZE;
+       ptr++)
+    {
+      csum1 += *ptr;
+      csum2 += csum1;
+    }
+  ((struct xnu_hibernate_header *)buf)->restored_checksum=(csum2<<16)|csum1;
+
+  /* Move starting pages to appropriate locations */
+  memcpy ((void *)(hibhead.restorepage*XNU_PAGESIZE), 
+         (grub_uint8_t*)buf+total_header_size, 
+         hibhead.restorepage*XNU_PAGESIZE);
+
+  /* Setup variables needed by booter*/
+  grub_xnu_stack=(void*)(hibhead.restorepage*XNU_PAGESIZE+hibhead.stack);
+  grub_xnu_entry_point 
+    = (void*)(hibhead.restorepage*XNU_PAGESIZE+hibhead.entry_point);
+  grub_xnu_arg1 = buf;
+
+  /* We're ready now */
+  grub_loader_set (grub_xnu_boot, 
+                  grub_xnu_resume_unload, 1);    
+  grub_dl_ref (my_mod);
+
+  return GRUB_ERR_NONE;
+}
+
+
+
+GRUB_MOD_INIT(xnu)
+{
+  (void) mod;                  /* To stop warning. */
+  grub_register_command ("xnu_resume", grub_cmd_xnu_resume, 
+                        GRUB_COMMAND_FLAG_BOTH,
+                        "xnu_resume FILE", "Load XNU hibernate image.", 0);
+  my_mod=mod;
+}
+
+GRUB_MOD_FINI(xnu)
+{
+  grub_unregister_command ("xnu_resume");
+}

reply via email to

[Prev in Thread] Current Thread [Next in Thread]