On Fri, Aug 01, 2008 at 01:45:30AM +0200, Robert Millan wrote:
> 
>   - What to do about physical_entry_addr now?  My patch currently discards
>     it, which I suppose is not what we want.

Fixed after some discussion with Bean on IRC.  This version of the patch
should handle physical_entry_addr fine.

-- 
Robert Millan

  The DRM opt-in fallacy: "Your data belongs to us. We will decide when (and
  how) you may access your data; but nobody's threatening your freedom: we
  still allow you to remove your data and not access it at all."
2008-08-01  Robert Millan  <[EMAIL PROTECTED]>

	* loader/i386/pc/multiboot.c (playground, forward_relocator)
	(backward_relocator): New variables.  Used to allocate and relocate
	the payload, respectively.
	(grub_multiboot_load_elf32): Load into heap instead of requested
	address, install the appropiate relocator code in each bound of
	the payload, and set the entry point such that
	grub_multiboot_real_boot() will jump to one of them.

	* kern/i386/loader.S (grub_multiboot_payload_size)
	(grub_multiboot_payload_orig, grub_multiboot_payload_dest): New
	variables.
	(grub_multiboot_real_boot): Set cpu context to what the relocator
	expects, and jump to the relocator instead of the payload.

	* include/grub/i386/pc/loader.h (grub_multiboot_payload_size)
	(grub_multiboot_payload_orig, grub_multiboot_payload_dest): Export.

Index: kern/i386/loader.S
===================================================================
--- kern/i386/loader.S	(revision 1758)
+++ kern/i386/loader.S	(working copy)
@@ -123,6 +123,13 @@
  * This starts the multiboot kernel.
  */
 
+VARIABLE(grub_multiboot_payload_size)
+	.long	0
+VARIABLE(grub_multiboot_payload_orig)
+	.long	0
+VARIABLE(grub_multiboot_payload_dest)
+	.long	0
+
 FUNCTION(grub_multiboot_real_boot)
 	/* Push the entry address on the stack.  */
 	pushl	%eax
@@ -138,9 +145,16 @@
 
 	/* Move the magic value into eax and jump to the kernel.  */
 	movl	$MULTIBOOT_MAGIC2,%eax
-	popl	%ecx
-	jmp 	*%ecx
 
+	/* Where do we copy what from.  */
+	movl	EXT_C(grub_multiboot_payload_size), %ecx
+	movl	EXT_C(grub_multiboot_payload_orig), %esi
+	movl	EXT_C(grub_multiboot_payload_dest), %edi
+			
+	/* Jump to the relocator.  */
+	popl	%edx
+	jmp 	*%edx
+	
 /*
  * This starts the multiboot 2 kernel.
  */
Index: include/grub/i386/pc/loader.h
===================================================================
--- include/grub/i386/pc/loader.h	(revision 1758)
+++ include/grub/i386/pc/loader.h	(working copy)
@@ -25,4 +25,8 @@
 /* This is an asm part of the chainloader.  */
 void EXPORT_FUNC(grub_chainloader_real_boot) (int drive, void *part_addr) __attribute__ ((noreturn));
 
+extern grub_addr_t EXPORT_VAR(grub_multiboot_payload_orig);
+extern grub_addr_t EXPORT_VAR(grub_multiboot_payload_dest);
+extern grub_size_t EXPORT_VAR(grub_multiboot_payload_size);
+
 #endif /* ! GRUB_LOADER_MACHINE_HEADER */
Index: loader/i386/pc/multiboot.c
===================================================================
--- loader/i386/pc/multiboot.c	(revision 1758)
+++ loader/i386/pc/multiboot.c	(working copy)
@@ -50,6 +50,26 @@
 static struct grub_multiboot_info *mbi;
 static grub_addr_t entry;
 
+static char *playground = NULL;
+
+static grub_uint8_t forward_relocator[] =
+{
+  0xfc,		/* cld */
+  0x89, 0xf2,	/* movl %esi, %edx */
+  0xf3, 0xa4,	/* rep movsb */
+  0xff, 0xe2,	/* jmp *%edx */
+};
+
+static grub_uint8_t backward_relocator[] =
+{
+  0xfd,		/* std */
+  0x01, 0xce,	/* addl %ecx, %esi */
+  0x01, 0xcf,	/* addl %ecx, %edi */
+  0x41,		/* incl %ecx */
+  0xf3, 0xa4,	/* rep movsb */
+  0xff, 0xe7,	/* jmp *%edi */
+};
+
 static grub_err_t
 grub_multiboot_boot (void)
 {
@@ -98,7 +118,7 @@
 {
   Elf32_Ehdr *ehdr = (Elf32_Ehdr *) buffer;
   char *phdr_base;
-  grub_addr_t physical_entry_addr = 0;
+  grub_off_t physical_entry_offset = 0;
   int i;
 
   if (ehdr->e_ident[EI_CLASS] != ELFCLASS32)
@@ -118,47 +138,66 @@
 
   phdr_base = (char *) buffer + ehdr->e_phoff;
 #define phdr(i)			((Elf32_Phdr *) (phdr_base + (i) * ehdr->e_phentsize))
+  
+  {
+    /* Find the highest address claimed by our payload.  */
+    char *end_addr = NULL;
+    for (i = 0; i < ehdr->e_phnum; i++)
+      {
+	grub_addr_t addr = (phdr(i)->p_paddr + phdr(i)->p_memsz);
+	if (addr > end_addr)
+	  end_addr = addr;
+      }
+    grub_multiboot_payload_size = end_addr - phdr(0)->p_paddr;
+  }
 
+  if (playground)
+    grub_free (playground);
+  playground = grub_malloc (sizeof (forward_relocator) + grub_multiboot_payload_size + sizeof (backward_relocator));
+  if (! playground)
+    return grub_errno;
+
+  grub_multiboot_payload_orig = playground + sizeof (forward_relocator);
+  grub_multiboot_payload_dest = (char *) phdr(0)->p_paddr;
+
+  grub_memmove (playground, forward_relocator, sizeof (forward_relocator));
+  grub_memmove (grub_multiboot_payload_orig + grub_multiboot_payload_size, backward_relocator, sizeof (backward_relocator));
+
   /* Load every loadable segment in memory.  */
   for (i = 0; i < ehdr->e_phnum; i++)
     {
       if (phdr(i)->p_type == PT_LOAD)
         {
-          /* The segment should fit in the area reserved for the OS.  */
-          if (phdr(i)->p_paddr < grub_os_area_addr)
-	    return grub_error (GRUB_ERR_BAD_OS,
-			       "segment doesn't fit in memory reserved for the OS (0x%lx < 0x%lx)",
-			       phdr(i)->p_paddr, grub_os_area_addr);
-          if (phdr(i)->p_paddr + phdr(i)->p_memsz > grub_os_area_addr + grub_os_area_size)
-	    return grub_error (GRUB_ERR_BAD_OS,
-			       "segment doesn't fit in memory reserved for the OS (0x%lx > 0x%lx)",
-			       phdr(i)->p_paddr + phdr(i)->p_memsz,
-			       grub_os_area_addr + grub_os_area_size);
+	  char *load_this_module_at = grub_multiboot_payload_orig + (phdr(i)->p_paddr - phdr(0)->p_paddr);
 
-          if (grub_file_seek (file, (grub_off_t) phdr(i)->p_offset)
+	  if (grub_file_seek (file, (grub_off_t) phdr(i)->p_offset)
 	      == (grub_off_t) -1)
 	    return grub_error (GRUB_ERR_BAD_OS,
 			       "invalid offset in program header");
 
-          if (grub_file_read (file, (void *) phdr(i)->p_paddr, phdr(i)->p_filesz)
+          if (grub_file_read (file, load_this_module_at, phdr(i)->p_filesz)
               != (grub_ssize_t) phdr(i)->p_filesz)
 	    return grub_error (GRUB_ERR_BAD_OS,
 			       "couldn't read segment from file");
 
           if (phdr(i)->p_filesz < phdr(i)->p_memsz)
-            grub_memset ((char *) phdr(i)->p_paddr + phdr(i)->p_filesz, 0,
+            grub_memset (load_this_module_at + phdr(i)->p_filesz, 0,
 			 phdr(i)->p_memsz - phdr(i)->p_filesz);
 
           if ((entry >= phdr(i)->p_vaddr) &&
 	      (entry < phdr(i)->p_vaddr + phdr(i)->p_memsz))
-	    physical_entry_addr = entry + phdr(i)->p_paddr - phdr(i)->p_vaddr;
+	    physical_entry_offset = phdr(i)->p_paddr - phdr(i)->p_vaddr;
         }
     }
 #undef phdr
-
-  if (physical_entry_addr)
-    entry = physical_entry_addr;
-
+  
+  if (grub_multiboot_payload_dest >= grub_multiboot_payload_orig)
+    entry = (grub_addr_t) playground;
+  else
+    entry = (grub_addr_t) grub_multiboot_payload_orig + grub_multiboot_payload_size;
+  
+  entry += physical_entry_offset;
+  
   return grub_errno;
 }
 
_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/grub-devel

Reply via email to