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 <[EMAIL PROTECTED]> > > * 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." _______________________________________________ Grub-devel mailing list Grub-devel@gnu.org http://lists.gnu.org/mailman/listinfo/grub-devel