On Wed, Jul 30, 2008 at 09:15:10PM +0200, Robert Millan wrote:
>
> Let's try to revive this discussion. Here's a patch I made a while ago that
> implements support for loading at any address. It works by having a "special"
> version of malloc() that is told to allocate a chunk of memory that does
> _not_ overlap with a specific region. It does so by iteratively reserving
> memory (and keeping track of what was reserved, of course).
>
> Then we use this function to allocate the asm relocator code in heap, asking
> it to garantee that it won't overlap with our final destination. Finally,
> our loadee can be put anywhere (e.g. in heap), and we just jump to the
> relocator which will jump to the loadee.
>
> But I really find the approach really ugly. What do you think? If we agree
> that this is the way to go, I can do some cleanup & update it to current svn
> for a merge.
Here's a new patch, with the following approach. We put this in a single
heap area:
<forward_relocator> <payload> <backward_relocator>
we pick the relocator we want depending on wether we want to copy to a lower
or higher address. This garantees that the relocator itself isn't
overwritten. Then we set cpu context appropiately, and jump.
I like this much better. Also, it doesn't depend on the static OS load
area.
Some doubts:
- What to do about physical_entry_addr now? My patch currently discards
it, which I suppose is not what we want.
- Should we do the same for the elf64 loader? And how? I don't know of
any example ELF64 images I could test with.
--
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."
Index: kern/i386/loader.S
===================================================================
--- kern/i386/loader.S (revision 1755)
+++ 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/kernel.h
===================================================================
--- include/grub/i386/pc/kernel.h (revision 1755)
+++ include/grub/i386/pc/kernel.h (working copy)
@@ -86,6 +86,10 @@
extern grub_addr_t EXPORT_FUNC(grub_arch_memdisk_addr) (void);
extern grub_off_t EXPORT_FUNC(grub_arch_memdisk_size) (void);
+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 /* ! ASM_FILE */
#endif /* ! KERNEL_MACHINE_HEADER */
Index: loader/i386/pc/multiboot.c
===================================================================
--- loader/i386/pc/multiboot.c (revision 1756)
+++ loader/i386/pc/multiboot.c (working copy)
@@ -50,6 +50,28 @@
static struct grub_multiboot_info *mbi;
static grub_addr_t entry;
+static char *playground = NULL;
+extern char *grub_multiboot_payload_orig, *grub_multiboot_payload_dest;
+extern grub_size_t grub_multiboot_payload_size;
+
+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)
{
@@ -118,35 +140,50 @@
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) &&
@@ -159,6 +196,13 @@
if (physical_entry_addr)
entry = physical_entry_addr;
+ /* FIXME: how do we handle physical_entry_addr now? */
+
+ 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;
+
return grub_errno;
}
_______________________________________________
Grub-devel mailing list
[email protected]
http://lists.gnu.org/mailman/listinfo/grub-devel