2008-08-02 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/loader.h (grub_multiboot_payload_size) (grub_multiboot_payload_orig, grub_multiboot_payload_dest) (grub_multiboot_payload_entry_offset): Export.
This commit is contained in:
parent
b15d8a0cc6
commit
a927cc7383
4 changed files with 104 additions and 26 deletions
20
ChangeLog
20
ChangeLog
|
@ -1,3 +1,23 @@
|
||||||
|
2008-08-02 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/loader.h (grub_multiboot_payload_size)
|
||||||
|
(grub_multiboot_payload_orig, grub_multiboot_payload_dest)
|
||||||
|
(grub_multiboot_payload_entry_offset): Export.
|
||||||
|
|
||||||
2008-08-01 Bean <bean123ch@gmail.com>
|
2008-08-01 Bean <bean123ch@gmail.com>
|
||||||
|
|
||||||
* normal/menu_entry.c (editor_getline): Don't return the original
|
* normal/menu_entry.c (editor_getline): Don't return the original
|
||||||
|
|
|
@ -43,6 +43,10 @@ void EXPORT_FUNC(grub_multiboot2_real_boot) (grub_addr_t entry,
|
||||||
void EXPORT_FUNC(grub_unix_real_boot) (grub_addr_t entry, ...)
|
void EXPORT_FUNC(grub_unix_real_boot) (grub_addr_t entry, ...)
|
||||||
__attribute__ ((cdecl,noreturn));
|
__attribute__ ((cdecl,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);
|
||||||
|
|
||||||
/* It is necessary to export these functions, because normal mode commands
|
/* It is necessary to export these functions, because normal mode commands
|
||||||
reuse rescue mode commands. */
|
reuse rescue mode commands. */
|
||||||
|
|
|
@ -123,6 +123,15 @@ linux_setup_seg:
|
||||||
* This starts the multiboot kernel.
|
* 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)
|
FUNCTION(grub_multiboot_real_boot)
|
||||||
/* Push the entry address on the stack. */
|
/* Push the entry address on the stack. */
|
||||||
pushl %eax
|
pushl %eax
|
||||||
|
@ -136,11 +145,16 @@ FUNCTION(grub_multiboot_real_boot)
|
||||||
/* Interrupts should be disabled. */
|
/* Interrupts should be disabled. */
|
||||||
cli
|
cli
|
||||||
|
|
||||||
/* Move the magic value into eax and jump to the kernel. */
|
/* Where do we copy what from. */
|
||||||
movl $MULTIBOOT_MAGIC2,%eax
|
movl EXT_C(grub_multiboot_payload_size), %ecx
|
||||||
popl %ecx
|
movl EXT_C(grub_multiboot_payload_orig), %esi
|
||||||
jmp *%ecx
|
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.
|
* This starts the multiboot 2 kernel.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -50,6 +50,33 @@ extern grub_dl_t my_mod;
|
||||||
static struct grub_multiboot_info *mbi;
|
static struct grub_multiboot_info *mbi;
|
||||||
static grub_addr_t entry;
|
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
|
static grub_err_t
|
||||||
grub_multiboot_boot (void)
|
grub_multiboot_boot (void)
|
||||||
{
|
{
|
||||||
|
@ -99,6 +126,7 @@ grub_multiboot_load_elf32 (grub_file_t file, void *buffer)
|
||||||
Elf32_Ehdr *ehdr = (Elf32_Ehdr *) buffer;
|
Elf32_Ehdr *ehdr = (Elf32_Ehdr *) buffer;
|
||||||
char *phdr_base;
|
char *phdr_base;
|
||||||
grub_addr_t physical_entry_addr = 0;
|
grub_addr_t physical_entry_addr = 0;
|
||||||
|
int lowest_segment = 0, highest_segment = 0;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (ehdr->e_ident[EI_CLASS] != ELFCLASS32)
|
if (ehdr->e_ident[EI_CLASS] != ELFCLASS32)
|
||||||
|
@ -114,50 +142,62 @@ grub_multiboot_load_elf32 (grub_file_t file, void *buffer)
|
||||||
if (ehdr->e_phoff + ehdr->e_phnum * ehdr->e_phentsize > MULTIBOOT_SEARCH)
|
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");
|
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;
|
phdr_base = (char *) buffer + ehdr->e_phoff;
|
||||||
#define phdr(i) ((Elf32_Phdr *) (phdr_base + (i) * ehdr->e_phentsize))
|
#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. */
|
/* Load every loadable segment in memory. */
|
||||||
for (i = 0; i < ehdr->e_phnum; i++)
|
for (i = 0; i < ehdr->e_phnum; i++)
|
||||||
{
|
{
|
||||||
if (phdr(i)->p_type == PT_LOAD)
|
if (phdr(i)->p_type == PT_LOAD)
|
||||||
{
|
{
|
||||||
/* The segment should fit in the area reserved for the OS. */
|
char *load_this_module_at = grub_multiboot_payload_orig + (phdr(i)->p_paddr - phdr(0)->p_paddr);
|
||||||
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);
|
|
||||||
|
|
||||||
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)
|
== (grub_off_t) -1)
|
||||||
return grub_error (GRUB_ERR_BAD_OS,
|
return grub_error (GRUB_ERR_BAD_OS,
|
||||||
"invalid offset in program header");
|
"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)
|
!= (grub_ssize_t) phdr(i)->p_filesz)
|
||||||
return grub_error (GRUB_ERR_BAD_OS,
|
return grub_error (GRUB_ERR_BAD_OS,
|
||||||
"couldn't read segment from file");
|
"couldn't read segment from file");
|
||||||
|
|
||||||
if (phdr(i)->p_filesz < phdr(i)->p_memsz)
|
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);
|
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
|
#undef phdr
|
||||||
|
|
||||||
if (physical_entry_addr)
|
if (grub_multiboot_payload_dest >= grub_multiboot_payload_orig)
|
||||||
entry = physical_entry_addr;
|
entry = (grub_addr_t) playground;
|
||||||
|
else
|
||||||
|
entry = (grub_addr_t) grub_multiboot_payload_orig + grub_multiboot_payload_size;
|
||||||
|
|
||||||
return grub_errno;
|
return grub_errno;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue