* loader modules jumping back to kernel
@ 2008-07-19 23:06 Robert Millan
2008-07-20 3:26 ` Bean
2008-07-20 9:09 ` Yoshinori K. Okuji
0 siblings, 2 replies; 9+ messages in thread
From: Robert Millan @ 2008-07-19 23:06 UTC (permalink / raw)
To: grub-devel
Anyone recalls the reason our loaders had to jump back to kernel (startup.S) to
do the final part of the load?
IIRC this causes trouble when the loadee chose an address that precisely
overwrites the loader, which is garanteed to happen when GRUB is loading
itself, AFAICT.
--
Robert Millan
<GPLv2> I know my rights; I want my phone call!
<DRM> What good is a phone call… if you are unable to speak?
(as seen on /.)
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: loader modules jumping back to kernel
2008-07-19 23:06 loader modules jumping back to kernel Robert Millan
@ 2008-07-20 3:26 ` Bean
2008-07-20 9:09 ` Yoshinori K. Okuji
1 sibling, 0 replies; 9+ messages in thread
From: Bean @ 2008-07-20 3:26 UTC (permalink / raw)
To: The development of GRUB 2
On Sun, Jul 20, 2008 at 7:06 AM, Robert Millan <rmh@aybabtu.com> wrote:
>
> Anyone recalls the reason our loaders had to jump back to kernel (startup.S) to
> do the final part of the load?
>
> IIRC this causes trouble when the loadee chose an address that precisely
> overwrites the loader, which is garanteed to happen when GRUB is loading
> itself, AFAICT.
Hi,
I think there is no appear reason for this other than to keep assembly
code in a single place. IMO, a lot of stuff should be moved out of
startup.S. For example, vbe functions are only used by vbe module,
they should be placed alongside other vbe files.
--
Bean
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: loader modules jumping back to kernel
2008-07-19 23:06 loader modules jumping back to kernel Robert Millan
2008-07-20 3:26 ` Bean
@ 2008-07-20 9:09 ` Yoshinori K. Okuji
2008-07-27 21:39 ` Robert Millan
1 sibling, 1 reply; 9+ messages in thread
From: Yoshinori K. Okuji @ 2008-07-20 9:09 UTC (permalink / raw)
To: The development of GRUB 2
On Sunday 20 July 2008 01:06:22 Robert Millan wrote:
> Anyone recalls the reason our loaders had to jump back to kernel
> (startup.S) to do the final part of the load?
Not all of them should do that, but it might be more convenient. I look at one
by one:
- The chainloader needs to get back the original state (e.g. A20 disabled), so
the final code must be located at below 1MB. Since the address of the startup
code is well known, it is easier to use.
- The linux loader does not have to overwrite the startup code, but other
regions can be. So it is easier to use.
- The multiboot loader had, historically speaking, a limitation that it may
not load an OS image below 1MB. So it was easier to use. But I don't remember
if this limitation is still present in the current implementation.
> IIRC this causes trouble when the loadee chose an address that precisely
> overwrites the loader, which is garanteed to happen when GRUB is loading
> itself, AFAICT.
Sure. My recommendation is, in case where you might overwrite that part, that
you should write relocatable code (which is rather easy for simple code on
i386) at anywhere (it could be in the startup), find out a safe region when
loading an OS image, copy the code to the safe region, and finalize the
bootstrap in that code (e.g. relocating the OS image, initializing registers,
and jumping to it). On i386, we have a reserved region to temporarily load an
OS image for the very reason, so this is not difficult.
Regards,
Okuji
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: loader modules jumping back to kernel
2008-07-20 9:09 ` Yoshinori K. Okuji
@ 2008-07-27 21:39 ` Robert Millan
2008-07-30 19:15 ` loadee relocation (Re: loader modules jumping back to kernel) Robert Millan
0 siblings, 1 reply; 9+ messages in thread
From: Robert Millan @ 2008-07-27 21:39 UTC (permalink / raw)
To: The development of GRUB 2; +Cc: Yoshinori K. Okuji
On Sun, Jul 20, 2008 at 11:09:02AM +0200, Yoshinori K. Okuji wrote:
> > IIRC this causes trouble when the loadee chose an address that precisely
> > overwrites the loader, which is garanteed to happen when GRUB is loading
> > itself, AFAICT.
>
> Sure. My recommendation is, in case where you might overwrite that part, that
> you should write relocatable code (which is rather easy for simple code on
> i386) at anywhere (it could be in the startup), find out a safe region when
> loading an OS image, copy the code to the safe region, and finalize the
> bootstrap in that code (e.g. relocating the OS image, initializing registers,
> and jumping to it). On i386, we have a reserved region to temporarily load an
> OS image for the very reason, so this is not difficult.
Ok. I've been looking at grub_multiboot_load_elf32() which contains the
bound checks that make loading abort in first place; It seems that bounds
are checked for every segment in the ELF image, in:
/* Load every loadable segment in memory. */
for (i = 0; i < ehdr->e_phnum; i++)
so I'm wondering if it is safe to assume the segments are going to occupy
a single block of memory (which can be relocated in one run) or it is allowed
for them to be scattered.
As for the safe region, AFAICT the OS load area is our only choice, or maybe
the heap, but in both cases overlaps are a problem, as we don't want the
relocator code to overwrite itself. In case of the OS load area, we could
abort on situations where payload requested region overlaps with our area,
and in case of the heap, we could play some ugly tricks in order to obtain
a non-overlapped region from malloc.
TBH I don't like either of the options. Do you have any other suggestions?
--
Robert Millan
<GPLv2> I know my rights; I want my phone call!
<DRM> What good is a phone call… if you are unable to speak?
(as seen on /.)
^ permalink raw reply [flat|nested] 9+ messages in thread
* loadee relocation (Re: loader modules jumping back to kernel)
2008-07-27 21:39 ` Robert Millan
@ 2008-07-30 19:15 ` Robert Millan
2008-07-31 23:45 ` Robert Millan
0 siblings, 1 reply; 9+ messages in thread
From: Robert Millan @ 2008-07-30 19:15 UTC (permalink / raw)
To: The development of GRUB 2; +Cc: Yoshinori K. Okuji
[-- Attachment #1: Type: text/plain, Size: 2899 bytes --]
On Sun, Jul 27, 2008 at 11:39:49PM +0200, Robert Millan wrote:
> On Sun, Jul 20, 2008 at 11:09:02AM +0200, Yoshinori K. Okuji wrote:
> > > IIRC this causes trouble when the loadee chose an address that precisely
> > > overwrites the loader, which is garanteed to happen when GRUB is loading
> > > itself, AFAICT.
> >
> > Sure. My recommendation is, in case where you might overwrite that part, that
> > you should write relocatable code (which is rather easy for simple code on
> > i386) at anywhere (it could be in the startup), find out a safe region when
> > loading an OS image, copy the code to the safe region, and finalize the
> > bootstrap in that code (e.g. relocating the OS image, initializing registers,
> > and jumping to it). On i386, we have a reserved region to temporarily load an
> > OS image for the very reason, so this is not difficult.
>
> Ok. I've been looking at grub_multiboot_load_elf32() which contains the
> bound checks that make loading abort in first place; It seems that bounds
> are checked for every segment in the ELF image, in:
>
> /* Load every loadable segment in memory. */
> for (i = 0; i < ehdr->e_phnum; i++)
>
> so I'm wondering if it is safe to assume the segments are going to occupy
> a single block of memory (which can be relocated in one run) or it is allowed
> for them to be scattered.
>
> As for the safe region, AFAICT the OS load area is our only choice, or maybe
> the heap, but in both cases overlaps are a problem, as we don't want the
> relocator code to overwrite itself. In case of the OS load area, we could
> abort on situations where payload requested region overlaps with our area,
> and in case of the heap, we could play some ugly tricks in order to obtain
> a non-overlapped region from malloc.
>
> TBH I don't like either of the options. Do you have any other suggestions?
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.
--
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."
[-- Attachment #2: loader_relocation.diff --]
[-- Type: text/x-diff, Size: 8131 bytes --]
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;
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: loadee relocation (Re: loader modules jumping back to kernel)
2008-07-30 19:15 ` loadee relocation (Re: loader modules jumping back to kernel) Robert Millan
@ 2008-07-31 23:45 ` Robert Millan
2008-08-01 16:16 ` Robert Millan
0 siblings, 1 reply; 9+ messages in thread
From: Robert Millan @ 2008-07-31 23:45 UTC (permalink / raw)
To: The development of GRUB 2; +Cc: Yoshinori K. Okuji
[-- Attachment #1: Type: text/plain, Size: 1813 bytes --]
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."
[-- Attachment #2: relocate_payload.diff --]
[-- Type: text/x-diff, Size: 5529 bytes --]
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;
}
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: loadee relocation (Re: loader modules jumping back to kernel)
2008-07-31 23:45 ` Robert Millan
@ 2008-08-01 16:16 ` Robert Millan
2008-08-01 22:45 ` Robert Millan
0 siblings, 1 reply; 9+ messages in thread
From: Robert Millan @ 2008-08-01 16:16 UTC (permalink / raw)
To: The development of GRUB 2; +Cc: Yoshinori K. Okuji
[-- Attachment #1: Type: text/plain, Size: 541 bytes --]
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."
[-- Attachment #2: relocate_payload.diff --]
[-- Type: text/x-diff, Size: 6654 bytes --]
2008-08-01 Robert Millan <rmh@aybabtu.com>
* 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;
}
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: loadee relocation (Re: loader modules jumping back to kernel)
2008-08-01 16:16 ` Robert Millan
@ 2008-08-01 22:45 ` Robert Millan
2008-08-02 12:11 ` Robert Millan
0 siblings, 1 reply; 9+ messages in thread
From: Robert Millan @ 2008-08-01 22:45 UTC (permalink / raw)
To: The development of GRUB 2; +Cc: Yoshinori K. Okuji
[-- Attachment #1: Type: text/plain, Size: 1326 bytes --]
On Fri, Aug 01, 2008 at 06:16:06PM +0200, Robert Millan wrote:
> 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.
Then again, I still got spurious crashes when trying my code with:
ftp://ftp.netbsd.org/pub/NetBSD-daily/netbsd-4/200807310002Z/i386/binary/kernel/netbsd-GENERIC.gz
In case someone is curious, the problems that made me spend all day debugging
are:
grub_multiboot_payload_entry_offset was defined with a 64-bit type but
allocated with ".long 0" in loader.S, resulting in the first 4 bytes of
grub_multiboot_real_boot being fucked up occasionally.
%edi was off-by-one in the backward relocator, which was not usually a
problem for invaders (what harm can one byte do?) but broke netbsd.
Lessons learned: gdb is your friend, and is definitely worth the hassle
of setting up for use in QEMU/GRUB.
--
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."
[-- Attachment #2: relocate_payload.diff --]
[-- Type: text/x-diff, Size: 7555 bytes --]
2008-08-01 Robert Millan <rmh@aybabtu.com>
* 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)
(grub_multiboot_payload_entry_offset): 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)
(grub_multiboot_payload_entry_offset): Export.
Index: kern/i386/loader.S
===================================================================
--- kern/i386/loader.S (revision 1758)
+++ kern/i386/loader.S (working copy)
@@ -123,6 +123,15 @@
* 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
+VARIABLE(grub_multiboot_payload_entry_offset)
+ .long 0
+
FUNCTION(grub_multiboot_real_boot)
/* Push the entry address on the stack. */
pushl %eax
@@ -136,11 +145,16 @@
/* Interrupts should be disabled. */
cli
- /* 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
+ movl EXT_C(grub_multiboot_payload_entry_offset), %eax
+
+ /* 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,9 @@
/* 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);
+extern grub_uint32_t EXPORT_VAR(grub_multiboot_payload_entry_offset);
+
#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,33 @@
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 */
+ 0x01, 0xc2, /* addl %eax, %edx */
+ 0xb8, 0x02, 0xb0, 0xad, 0x2b, /* movl $MULTIBOOT_MAGIC2, %eax */
+ 0xff, 0xe2, /* jmp *%edx */
+};
+
+static grub_uint8_t backward_relocator[] =
+{
+ 0xfd, /* std */
+ 0x01, 0xce, /* addl %ecx, %esi */
+ 0x01, 0xcf, /* addl %ecx, %edi */
+ /* backward movsb is implicitly off-by-one. compensate that. */
+ 0x41, /* incl %ecx */
+ 0xf3, 0xa4, /* rep movsb */
+ /* same problem again. */
+ 0x47, /* incl %edi */
+ 0x01, 0xc7, /* addl %eax, %edi */
+ 0xb8, 0x02, 0xb0, 0xad, 0x2b, /* movl $MULTIBOOT_MAGIC2, %eax */
+ 0xff, 0xe7, /* jmp *%edi */
+};
+
static grub_err_t
grub_multiboot_boot (void)
{
@@ -99,6 +126,7 @@
Elf32_Ehdr *ehdr = (Elf32_Ehdr *) buffer;
char *phdr_base;
grub_addr_t physical_entry_addr = 0;
+ int lowest_segment = 0, highest_segment = 0;
int i;
if (ehdr->e_ident[EI_CLASS] != ELFCLASS32)
@@ -114,50 +142,62 @@
if (ehdr->e_phoff + ehdr->e_phnum * ehdr->e_phentsize > MULTIBOOT_SEARCH)
return grub_error (GRUB_ERR_BAD_OS, "program header at a too high offset");
- entry = ehdr->e_entry;
-
phdr_base = (char *) buffer + ehdr->e_phoff;
#define phdr(i) ((Elf32_Phdr *) (phdr_base + (i) * ehdr->e_phentsize))
+ for (i = 0; i < ehdr->e_phnum; i++)
+ if (phdr(i)->p_type == PT_LOAD)
+ {
+ if (phdr(i)->p_paddr < phdr(lowest_segment)->p_paddr)
+ lowest_segment = i;
+ if (phdr(i)->p_paddr > phdr(highest_segment)->p_paddr)
+ highest_segment = i;
+ }
+ grub_multiboot_payload_size = (phdr(highest_segment)->p_paddr + phdr(highest_segment)->p_memsz) - phdr(lowest_segment)->p_paddr;
+ grub_multiboot_payload_dest = phdr(lowest_segment)->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_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;
}
}
+
+ grub_multiboot_payload_entry_offset = ehdr->e_entry - phdr(lowest_segment)->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;
return grub_errno;
}
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: loadee relocation (Re: loader modules jumping back to kernel)
2008-08-01 22:45 ` Robert Millan
@ 2008-08-02 12:11 ` Robert Millan
0 siblings, 0 replies; 9+ messages in thread
From: Robert Millan @ 2008-08-02 12:11 UTC (permalink / raw)
To: The development of GRUB 2; +Cc: Yoshinori K. Okuji
Committed.
On Sat, Aug 02, 2008 at 12:45:20AM +0200, Robert Millan wrote:
> On Fri, Aug 01, 2008 at 06:16:06PM +0200, Robert Millan wrote:
> > 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.
>
> Then again, I still got spurious crashes when trying my code with:
> ftp://ftp.netbsd.org/pub/NetBSD-daily/netbsd-4/200807310002Z/i386/binary/kernel/netbsd-GENERIC.gz
>
> In case someone is curious, the problems that made me spend all day debugging
> are:
>
> grub_multiboot_payload_entry_offset was defined with a 64-bit type but
> allocated with ".long 0" in loader.S, resulting in the first 4 bytes of
> grub_multiboot_real_boot being fucked up occasionally.
>
> %edi was off-by-one in the backward relocator, which was not usually a
> problem for invaders (what harm can one byte do?) but broke netbsd.
>
> Lessons learned: gdb is your friend, and is definitely worth the hassle
> of setting up for use in QEMU/GRUB.
>
> --
> 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 <rmh@aybabtu.com>
>
> * 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)
> (grub_multiboot_payload_entry_offset): 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)
> (grub_multiboot_payload_entry_offset): Export.
>
> Index: kern/i386/loader.S
> ===================================================================
> --- kern/i386/loader.S (revision 1758)
> +++ kern/i386/loader.S (working copy)
> @@ -123,6 +123,15 @@
> * 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
> +VARIABLE(grub_multiboot_payload_entry_offset)
> + .long 0
> +
> FUNCTION(grub_multiboot_real_boot)
> /* Push the entry address on the stack. */
> pushl %eax
> @@ -136,11 +145,16 @@
> /* Interrupts should be disabled. */
> cli
>
> - /* 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
> + movl EXT_C(grub_multiboot_payload_entry_offset), %eax
> +
> + /* 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,9 @@
> /* 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);
> +extern grub_uint32_t EXPORT_VAR(grub_multiboot_payload_entry_offset);
> +
> #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,33 @@
> 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 */
> + 0x01, 0xc2, /* addl %eax, %edx */
> + 0xb8, 0x02, 0xb0, 0xad, 0x2b, /* movl $MULTIBOOT_MAGIC2, %eax */
> + 0xff, 0xe2, /* jmp *%edx */
> +};
> +
> +static grub_uint8_t backward_relocator[] =
> +{
> + 0xfd, /* std */
> + 0x01, 0xce, /* addl %ecx, %esi */
> + 0x01, 0xcf, /* addl %ecx, %edi */
> + /* backward movsb is implicitly off-by-one. compensate that. */
> + 0x41, /* incl %ecx */
> + 0xf3, 0xa4, /* rep movsb */
> + /* same problem again. */
> + 0x47, /* incl %edi */
> + 0x01, 0xc7, /* addl %eax, %edi */
> + 0xb8, 0x02, 0xb0, 0xad, 0x2b, /* movl $MULTIBOOT_MAGIC2, %eax */
> + 0xff, 0xe7, /* jmp *%edi */
> +};
> +
> static grub_err_t
> grub_multiboot_boot (void)
> {
> @@ -99,6 +126,7 @@
> Elf32_Ehdr *ehdr = (Elf32_Ehdr *) buffer;
> char *phdr_base;
> grub_addr_t physical_entry_addr = 0;
> + int lowest_segment = 0, highest_segment = 0;
> int i;
>
> if (ehdr->e_ident[EI_CLASS] != ELFCLASS32)
> @@ -114,50 +142,62 @@
> if (ehdr->e_phoff + ehdr->e_phnum * ehdr->e_phentsize > MULTIBOOT_SEARCH)
> return grub_error (GRUB_ERR_BAD_OS, "program header at a too high offset");
>
> - entry = ehdr->e_entry;
> -
> phdr_base = (char *) buffer + ehdr->e_phoff;
> #define phdr(i) ((Elf32_Phdr *) (phdr_base + (i) * ehdr->e_phentsize))
>
> + for (i = 0; i < ehdr->e_phnum; i++)
> + if (phdr(i)->p_type == PT_LOAD)
> + {
> + if (phdr(i)->p_paddr < phdr(lowest_segment)->p_paddr)
> + lowest_segment = i;
> + if (phdr(i)->p_paddr > phdr(highest_segment)->p_paddr)
> + highest_segment = i;
> + }
> + grub_multiboot_payload_size = (phdr(highest_segment)->p_paddr + phdr(highest_segment)->p_memsz) - phdr(lowest_segment)->p_paddr;
> + grub_multiboot_payload_dest = phdr(lowest_segment)->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_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;
> }
> }
> +
> + grub_multiboot_payload_entry_offset = ehdr->e_entry - phdr(lowest_segment)->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;
>
> return grub_errno;
> }
> _______________________________________________
> Grub-devel mailing list
> Grub-devel@gnu.org
> http://lists.gnu.org/mailman/listinfo/grub-devel
--
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."
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2008-08-02 12:12 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-07-19 23:06 loader modules jumping back to kernel Robert Millan
2008-07-20 3:26 ` Bean
2008-07-20 9:09 ` Yoshinori K. Okuji
2008-07-27 21:39 ` Robert Millan
2008-07-30 19:15 ` loadee relocation (Re: loader modules jumping back to kernel) Robert Millan
2008-07-31 23:45 ` Robert Millan
2008-08-01 16:16 ` Robert Millan
2008-08-01 22:45 ` Robert Millan
2008-08-02 12:11 ` Robert Millan
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.