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;