As discussed on two other threads, this patch is a proof of concept of
relocation in the loader.
Some comments:
- grub_protected_malloc() is like malloc, but with the garantee that
it won't overlap with a particular region. Do you think this is the
right approach? Should this be kept as a layer on top of the memory
manager, like now, or rather be integrated with it to take advantage of
its internal knowledge? I find that the current implementation is an
ugly hack...
- This works for multiboot only. How far can we go at making it more
generic? At least the malloc part should be; not sure about the rest.
- The phdr(i) hack can be committed separately in case we decide that it
is necessary for this feature (or just convenient for readability). It's
described at:
http://www.mail-archive.com/[email protected]/msg03517.html
--
Robert Millan
<GPLv2> I know my rights; I want my phone call!
<DRM> What use is a phone call… if you are unable to speak?
(as seen on /.)
diff -ur grub2/include/grub/i386/loader.h grub2.self/include/grub/i386/loader.h
--- grub2/include/grub/i386/loader.h 2007-10-17 11:38:55.000000000 +0200
+++ grub2.self/include/grub/i386/loader.h 2007-10-17 12:36:16.000000000 +0200
@@ -29,13 +29,23 @@
extern grub_addr_t EXPORT_VAR(grub_os_area_addr);
extern grub_size_t EXPORT_VAR(grub_os_area_size);
+extern grub_addr_t EXPORT_VAR(playground);
+extern grub_addr_t EXPORT_VAR(requested_addr);
+extern int EXPORT_VAR(playground_size);
+
void EXPORT_FUNC(grub_linux_boot_zimage) (void) __attribute__ ((noreturn));
void EXPORT_FUNC(grub_linux_boot_bzimage) (void) __attribute__ ((noreturn));
/* The asm part of the multiboot loader. */
+void EXPORT_FUNC(grub_multiboot_real_boot_1st) (grub_addr_t entry,
+ struct grub_multiboot_info *mbi,
+ void *next)
+ __attribute__ ((noreturn));
void EXPORT_FUNC(grub_multiboot_real_boot) (grub_addr_t entry,
struct grub_multiboot_info *mbi)
__attribute__ ((noreturn));
+extern int EXPORT_VAR(end_of_grub_multiboot_real_boot);
+
void EXPORT_FUNC(grub_multiboot2_real_boot) (grub_addr_t entry,
struct grub_multiboot_info *mbi)
__attribute__ ((noreturn));
diff -ur grub2/kern/i386/pc/startup.S grub2.self/kern/i386/pc/startup.S
--- grub2/kern/i386/pc/startup.S 2007-07-25 21:29:24.000000000 +0200
+++ grub2.self/kern/i386/pc/startup.S 2007-10-17 12:36:16.000000000 +0200
@@ -800,23 +800,59 @@
* This starts the multiboot kernel.
*/
-FUNCTION(grub_multiboot_real_boot)
+VARIABLE(playground)
+ .long 0
+VARIABLE(playground_size)
+ .long 0
+VARIABLE(requested_addr)
+ .long 0
+
+/* entry, mbi, next */
+FUNCTION(grub_multiboot_real_boot_1st)
/* Push the entry address on the stack. */
pushl %eax
/* Move the address of the multiboot information structure to ebx. */
movl %edx,%ebx
+ /* Push the grub_multiboot_real_boot address on the stack. */
+ pushl %ecx
/* Unload all modules and stop the floppy driver. */
call EXT_C(grub_dl_unload_all)
call EXT_C(grub_stop_floppy)
+ movl EXT_C(playground_size), %ecx
+ movl EXT_C(playground), %esi
+ movl EXT_C(requested_addr), %edi
+
/* Interrupts should be disabled. */
cli
-
- /* Move the magic value into eax and jump to the kernel. */
+
+ /* Jump to relocated grub_multiboot_real_boot */
+ popl %eax
+ jmp *%eax
+
+ /* External references stop working at this point */
+
+FUNCTION(grub_multiboot_real_boot)
+ /* Get this from stack now that we still can */
+ popl %edx
+
+ /* Relocate the payload. Overlaps are garanteed NOT to happen, so
+ we don't need to check for them (like in grub_memmove) */
+
+ /* %ecx, %esi and %edi were set in the previous function */
+ cld
+ rep
+ movsb
+
+ /* Stack may have stopped working at this point */
+
+ /* Move the magic value into eax, and jump to the kernel */
movl $MULTIBOOT_MAGIC2,%eax
- popl %ecx
- jmp *%ecx
+ jmp *%edx
+
+/* Nothing here, just used to relocate the above function */
+VARIABLE(end_of_grub_multiboot_real_boot)
/*
* This starts the multiboot 2 kernel.
diff -ur grub2/loader/i386/pc/multiboot.c grub2.self/loader/i386/pc/multiboot.c
--- grub2/loader/i386/pc/multiboot.c 2007-07-25 21:29:24.000000000 +0200
+++ grub2.self/loader/i386/pc/multiboot.c 2007-10-17 12:36:16.000000000 +0200
@@ -47,10 +47,48 @@
static struct grub_multiboot_info *mbi;
static grub_addr_t entry;
+extern grub_addr_t playground;
+extern int playground_size;
+extern grub_addr_t requested_addr;
+
+void *
+grub_protected_malloc (grub_size_t size, grub_addr_t protected_region_addr, grub_size_t protected_region_size)
+{
+ void **old, **tmp, **ret;
+ tmp = grub_malloc (size);
+ *tmp = NULL;
+
+ while ((tmp + size >= protected_region_addr) && (tmp <= protected_region_addr + protected_region_size))
+ {
+ /* Get a new region */
+ old = tmp;
+ tmp = grub_malloc (size);
+ /* Store the address of our previous region */
+ *tmp = old;
+ }
+
+ /* Save result */
+ ret = tmp;
+
+ /* Cleanup */
+ tmp = *tmp;
+ while (tmp)
+ {
+ old = *tmp;
+ grub_free (tmp);
+ tmp = old;
+ }
+
+ return ret;
+}
+
static grub_err_t
grub_multiboot_boot (void)
{
- grub_multiboot_real_boot (entry, mbi);
+ void *tmp = grub_protected_malloc ((long) &end_of_grub_multiboot_real_boot - (long) grub_multiboot_real_boot + 32,
+ requested_addr, playground_size);
+ grub_memcpy (tmp, grub_multiboot_real_boot, (long) &end_of_grub_multiboot_real_boot - (long) grub_multiboot_real_boot + 32);
+ grub_multiboot_real_boot_1st (entry, mbi, tmp);
/* Not reached. */
return GRUB_ERR_NONE;
@@ -94,7 +132,7 @@
grub_multiboot_load_elf32 (grub_file_t file, void *buffer)
{
Elf32_Ehdr *ehdr = (Elf32_Ehdr *) buffer;
- Elf32_Phdr *phdr;
+ void *phdr_base;
int i;
if (ehdr->e_ident[EI_CLASS] != ELFCLASS32)
@@ -112,35 +150,37 @@
entry = ehdr->e_entry;
+ phdr_base = (void *) buffer + ehdr->e_phoff;
+#define phdr(i) ((Elf32_Phdr *) (phdr_base + (i) * ehdr->e_phentsize))
+
+ requested_addr = phdr(0)->p_paddr;
+ playground_size = (phdr(ehdr->e_phnum - 1)->p_paddr + phdr(ehdr->e_phnum - 1)->p_memsz) - phdr(0)->p_paddr;
+ playground = grub_protected_malloc (playground_size, requested_addr, playground_size); /* FIXME: memleak */
+
/* Load every loadable segment in memory. */
for (i = 0; i < ehdr->e_phnum; i++)
{
- phdr = (Elf32_Phdr *) ((char *) buffer + ehdr->e_phoff
- + i * ehdr->e_phentsize);
- if (phdr->p_type == PT_LOAD)
+ if (phdr(i)->p_type == PT_LOAD)
{
- /* The segment should fit in the area reserved for the OS. */
- if ((phdr->p_paddr < grub_os_area_addr)
- || (phdr->p_paddr + phdr->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");
+ void *playground_addr = playground + (phdr(i)->p_paddr - phdr(0)->p_paddr);
- if (grub_file_seek (file, (grub_off_t) phdr->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->p_paddr, phdr->p_filesz)
- != (grub_ssize_t) phdr->p_filesz)
+ if (grub_file_read (file, playground_addr, 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->p_filesz < phdr->p_memsz)
- grub_memset ((char *) phdr->p_paddr + phdr->p_filesz, 0,
- phdr->p_memsz - phdr->p_filesz);
+ if (phdr(i)->p_filesz < phdr(i)->p_memsz)
+ grub_memset ((char *) playground_addr + phdr(i)->p_filesz, 0,
+ phdr(i)->p_memsz - phdr(i)->p_filesz);
}
}
+
+#undef phdr
return grub_errno;
}
@@ -293,7 +333,7 @@
if (grub_multiboot_load_elf (file, buffer) != GRUB_ERR_NONE)
goto fail;
- mbi = grub_malloc (sizeof (struct grub_multiboot_info));
+ mbi = grub_protected_malloc (sizeof (struct grub_multiboot_info), requested_addr, playground_size);
if (! mbi)
goto fail;
@@ -306,7 +346,7 @@
for (i = 0, len = 0; i < argc; i++)
len += grub_strlen (argv[i]) + 1;
- cmdline = p = grub_malloc (len);
+ cmdline = p = grub_protected_malloc (len, requested_addr, playground_size);
if (! cmdline)
goto fail;
@@ -379,7 +419,7 @@
for (i = 0; i < argc; i++)
len += grub_strlen (argv[i]) + 1;
- cmdline = p = grub_malloc (len);
+ cmdline = p = grub_protected_malloc (len, requested_addr, playground_size);
if (! cmdline)
goto fail;
@@ -410,7 +450,7 @@
}
else
{
- struct grub_mod_list *modlist = grub_malloc (sizeof (struct grub_mod_list));
+ struct grub_mod_list *modlist = grub_protected_malloc (sizeof (struct grub_mod_list), requested_addr, playground_size);
if (! modlist)
goto fail;
modlist->mod_start = (grub_uint32_t) module;
_______________________________________________
Grub-devel mailing list
[email protected]
http://lists.gnu.org/mailman/listinfo/grub-devel