diff --git a/conf/i386-pc.rmk b/conf/i386-pc.rmk index 1978c7caa..75e1ffdaa 100644 --- a/conf/i386-pc.rmk +++ b/conf/i386-pc.rmk @@ -202,14 +202,14 @@ serial_mod_CFLAGS = $(COMMON_CFLAGS) serial_mod_LDFLAGS = $(COMMON_LDFLAGS) # For multiboot.mod. -#pkglib_MODULES += multiboot.mod +pkglib_MODULES += multiboot.mod multiboot_mod_SOURCES = loader/i386/multiboot.c \ loader/multiboot_loader.c multiboot_mod_CFLAGS = $(COMMON_CFLAGS) multiboot_mod_LDFLAGS = $(COMMON_LDFLAGS) multiboot_mod_ASFLAGS = $(COMMON_ASFLAGS) -#pkglib_MODULES += multiboot2.mod +pkglib_MODULES += multiboot2.mod multiboot2_mod_SOURCES = loader/i386/multiboot.c \ loader/multiboot_loader.c multiboot2_mod_CFLAGS = $(COMMON_CFLAGS) -DGRUB_USE_MULTIBOOT2 diff --git a/include/grub/i386/multiboot.h b/include/grub/i386/multiboot.h index 584955449..a3ff3f4da 100644 --- a/include/grub/i386/multiboot.h +++ b/include/grub/i386/multiboot.h @@ -27,9 +27,7 @@ void grub_multiboot2_real_boot (grub_addr_t entry, struct multiboot_info *mbi) __attribute__ ((noreturn)); +extern struct grub_relocator *grub_multiboot_relocator; extern grub_uint32_t grub_multiboot_payload_eip; -extern char *grub_multiboot_payload_orig; -extern grub_addr_t grub_multiboot_payload_dest; -extern grub_size_t grub_multiboot_payload_size; #endif /* ! GRUB_MULTIBOOT_CPU_HEADER */ diff --git a/kern/i386/pc/init.c b/kern/i386/pc/init.c index 0f2374c6d..3885bba7a 100644 --- a/kern/i386/pc/init.c +++ b/kern/i386/pc/init.c @@ -190,7 +190,7 @@ grub_machine_init (void) from 1MB. This region is partially used for loading OS images. For now, 1/4 of this is added to free memory. */ for (i = 0; i < num_regions; i++) - if (mem_regions[i].addr == 0x100000) + /* if (mem_regions[i].addr == 0x100000) { grub_size_t quarter = mem_regions[i].size >> 2; @@ -199,11 +199,11 @@ grub_machine_init (void) grub_mm_init_region ((void *) (grub_os_area_addr + grub_os_area_size), quarter); } - else + else*/ grub_mm_init_region ((void *) mem_regions[i].addr, mem_regions[i].size); - if (! grub_os_area_addr) - grub_fatal ("no upper memory"); + // if (! grub_os_area_addr) + //grub_fatal ("no upper memory"); grub_tsc_init (); } diff --git a/lib/i386/relocator.c b/lib/i386/relocator.c index d4555e5f3..1129ee8ef 100644 --- a/lib/i386/relocator.c +++ b/lib/i386/relocator.c @@ -19,9 +19,9 @@ #include #include -#include #include #include +#include #include #include @@ -67,15 +67,16 @@ grub_cpu_relocator_jumper (void *rels, grub_addr_t addr) { grub_uint8_t *ptr; ptr = rels; + /* movl $addr, %eax (for relocator) */ + *(grub_uint8_t *) ptr = 0xb8; + ptr++; + *(grub_uint32_t *) ptr = addr; + ptr += 4; /* jmp $addr */ *(grub_uint8_t *) ptr = 0xe9; ptr++; *(grub_uint32_t *) ptr = addr - (grub_uint32_t) (ptr + 4); ptr += 4; - /* movl $addr, %eax (for relocator) */ - *(grub_uint8_t *) ptr = 0xb8; - ptr++; - *(grub_uint32_t *) ptr = addr; } void @@ -112,6 +113,7 @@ grub_relocator32_boot (struct grub_relocator *rel, void *src; grub_err_t err; grub_addr_t relst; + err = grub_relocator_alloc_chunk_align (rel, &src, &target, 0, (0xffffffff - RELOCATOR_SIZEOF (32)) + 1, RELOCATOR_SIZEOF (32), 16); @@ -130,6 +132,7 @@ grub_relocator32_boot (struct grub_relocator *rel, err = grub_relocator_prepare_relocs (rel, target, &relst); if (err) return err; + asm volatile ("cli"); ((void (*) (void)) relst) (); diff --git a/lib/relocator.c b/lib/relocator.c index 25ebd27bb..a2ebf99a7 100644 --- a/lib/relocator.c +++ b/lib/relocator.c @@ -68,12 +68,18 @@ get_best_header (struct grub_relocator *rel, for (chunk = rel->chunks; chunk; chunk = chunk->next) if ((chunk->target <= addr && addr < chunk->target + chunk->size) - || (chunk->target <= addr + size + || (chunk->target < addr + size && addr + size < chunk->target + chunk->size) || (addr <= chunk->target && chunk->target < addr + size) - || (addr <= chunk->target + chunk->size + || (addr < chunk->target + chunk->size && chunk->target + chunk->size < addr + size)) { + grub_dprintf ("relocator", + "collision 0x%llx+0x%llx, 0x%llx+0x%llx\n", + (unsigned long long) chunk->target, + (unsigned long long) chunk->size, + (unsigned long long) addr, + (unsigned long long) size); addr = ALIGN_UP (chunk->target + chunk->size, align); break; } @@ -110,10 +116,10 @@ get_best_header (struct grub_relocator *rel, for (chunk = rel->chunks; chunk; chunk = chunk->next) if ((chunk->target <= addr && addr < chunk->target + chunk->size) - || (chunk->target <= addr + size + || (chunk->target < addr + size && addr + size < chunk->target + chunk->size) || (addr <= chunk->target && chunk->target < addr + size) - || (addr <= chunk->target + chunk->size + || (addr < chunk->target + chunk->size && chunk->target + chunk->size < addr + size)) { addr = ALIGN_DOWN (chunk->target - size, align); @@ -138,18 +144,29 @@ get_best_header (struct grub_relocator *rel, } } - for (hp = NULL, h = rb->first; h; hp = h, h = h->next) + hp = rb->first; + h = hp->next; + grub_dprintf ("relocator", "alive\n"); + do { grub_addr_t allowable_start, allowable_end; allowable_start = (grub_addr_t) h; allowable_end = (grub_addr_t) (h + 1 + h->size); - + try_addr (allowable_start, allowable_end); - + if ((grub_addr_t) h == (grub_addr_t) (rb + 1)) - try_addr (allowable_start - sizeof (*rb) - rb->pre_size, - allowable_end - sizeof (*rb)); + { + grub_dprintf ("relocator", "Trying region start 0x%llx\n", + (unsigned long long) (allowable_start + - sizeof (*rb) - rb->pre_size)); + try_addr (allowable_start - sizeof (*rb) - rb->pre_size, + allowable_end - sizeof (*rb)); + } + hp = h; + h = hp->next; } + while (hp && hp != rb->first); *prev = hbp; return hb; } @@ -172,7 +189,7 @@ malloc_in_range (struct grub_relocator *rel, { if ((grub_addr_t) r + r->size + sizeof (*r) > start && (grub_addr_t) r <= end && r->size + sizeof (*r) >= size - && (rb == NULL || from_low_priv ? rb > r : rb < r)) + && (rb == NULL || (from_low_priv ? rb > r : rb < r))) { rb = r; rbp = rp; @@ -183,8 +200,13 @@ malloc_in_range (struct grub_relocator *rel, if (!rb) return 0; + grub_dprintf ("relocator", "trying region %p - %p\n", rb, rb + rb->size + 1); + hb = get_best_header (rel, start, end, align, size, rb, &hbp, &best_addr, from_low_priv, collisioncheck); + + grub_dprintf ("relocator", "best header %p\n", hb); + if (!hb) { if (from_low_priv) @@ -263,9 +285,8 @@ malloc_in_range (struct grub_relocator *rel, if (foll) { foll->next = hb; - if (hbp) - hbp->next = foll; - else + hbp->next = foll; + if (rb->first == hbp) rb->first = foll; } } @@ -275,9 +296,7 @@ malloc_in_range (struct grub_relocator *rel, foll->next = hb->next; else foll = hb->next; - if (hbp) - hbp->next = foll; - else + if (rb->first == hbp) rb->first = foll; } *res = best_addr; @@ -315,6 +334,9 @@ grub_relocator_alloc_chunk_addr (struct grub_relocator *rel, void **src, if (!chunk) return grub_errno; + grub_dprintf ("relocator", "min_addr = 0x%llx, max_addr = 0x%llx\n", + (unsigned long long) min_addr, (unsigned long long) max_addr); + do { /* A trick to improve Linux allocation. */ @@ -339,6 +361,9 @@ grub_relocator_alloc_chunk_addr (struct grub_relocator *rel, void **src, } while (0); + grub_dprintf ("relocator", "allocated 0x%llx/0x%llx\n", + (unsigned long long) start, (unsigned long long) target); + if (rel->highestaddr < target + size) rel->highestaddr = target + size; @@ -415,7 +440,8 @@ grub_relocator_alloc_chunk_align (struct grub_relocator *rel, void **src, grub_free (chunk); return grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory"); } - + + /* FIXME: check memory map. */ chunk->target = ALIGN_UP (min_addr, align); while (1) { @@ -455,6 +481,8 @@ void grub_relocator_unload (struct grub_relocator *rel) { struct grub_relocator_chunk *chunk, *next; + if (!rel) + return; for (chunk = rel->chunks; chunk; chunk = next) { grub_fatal ("Relocator unloading isn't implemented yet"); diff --git a/loader/i386/multiboot.c b/loader/i386/multiboot.c index f4869594e..5dbfc3bfd 100644 --- a/loader/i386/multiboot.c +++ b/loader/i386/multiboot.c @@ -28,7 +28,7 @@ */ /* The bits in the required part of flags field we don't support. */ -#define UNSUPPORTED_FLAGS 0x0000fffc +#define UNSUPPORTED_FLAGS 0x0000fff0 #include #include @@ -55,12 +55,10 @@ extern grub_dl_t my_mod; static struct multiboot_info *mbi, *mbi_dest; +struct grub_relocator *grub_multiboot_relocator = NULL; static grub_size_t code_size; -char *grub_multiboot_payload_orig; -grub_addr_t grub_multiboot_payload_dest; -grub_size_t grub_multiboot_payload_size; grub_uint32_t grub_multiboot_payload_eip; static grub_err_t @@ -78,9 +76,7 @@ grub_multiboot_boot (void) .esp = 0x7ff00 }; - grub_relocator32_boot (grub_multiboot_payload_orig, - grub_multiboot_payload_dest, - state); + grub_relocator32_boot (grub_multiboot_relocator, state); /* Not reached. */ return GRUB_ERR_NONE; @@ -101,10 +97,10 @@ grub_multiboot_unload (void) } grub_free ((void *) mbi->mods_addr); } - grub_relocator32_free (grub_multiboot_payload_orig); + grub_relocator_unload (grub_multiboot_relocator); + grub_multiboot_relocator = NULL; mbi = NULL; - grub_multiboot_payload_orig = NULL; grub_dl_unref (my_mod); return GRUB_ERR_NONE; @@ -224,6 +220,10 @@ grub_multiboot (int argc, char *argv[]) int i; int cmdline_argc; char **cmdline_argv; + int mbichunk_size; + void *mbichunk; + grub_addr_t mbichunk_dest; + grub_err_t err; grub_loader_unset (); @@ -271,8 +271,8 @@ grub_multiboot (int argc, char *argv[]) goto fail; } - grub_relocator32_free (grub_multiboot_payload_orig); - grub_multiboot_payload_orig = NULL; + grub_relocator_unload (grub_multiboot_relocator); + grub_multiboot_relocator = NULL; mmap_length = grub_get_multiboot_mmap_len (); @@ -289,68 +289,81 @@ grub_multiboot (int argc, char *argv[]) boot_loader_name_length = sizeof(PACKAGE_STRING); -#define cmdline_addr(x) ((void *) ((x) + code_size)) +#define cmdline_addr(x) ((void *) (((grub_uint8_t *) x))) #define boot_loader_name_addr(x) \ - ((void *) ((x) + code_size + cmdline_length)) -#define mbi_addr(x) ((void *) ((x) + code_size + cmdline_length + boot_loader_name_length)) -#define mmap_addr(x) ((void *) ((x) + code_size + cmdline_length + boot_loader_name_length + sizeof (struct multiboot_info))) + ((void *) (((grub_uint8_t *) x) + cmdline_length)) +#define mbi_addr(x) ((void *) (((grub_uint8_t *) x) + cmdline_length + boot_loader_name_length)) +#define mmap_addr(x) ((void *) (((grub_uint8_t *) x) + cmdline_length + boot_loader_name_length + sizeof (struct multiboot_info))) - grub_multiboot_payload_size = cmdline_length + mbichunk_size = cmdline_length /* boot_loader_name_length might need to grow for mbi,etc to be aligned (see below) */ + boot_loader_name_length + 3 + sizeof (struct multiboot_info) + mmap_length; + grub_multiboot_relocator = grub_relocator_new (); + + if (!grub_multiboot_relocator) + goto fail; + if (header->flags & MULTIBOOT_AOUT_KLUDGE) { int offset = ((char *) header - buffer - (header->header_addr - header->load_addr)); int load_size = ((header->load_end_addr == 0) ? file->size - offset : header->load_end_addr - header->load_addr); + void *source; if (header->bss_end_addr) code_size = (header->bss_end_addr - header->load_addr); else code_size = load_size; - grub_multiboot_payload_dest = header->load_addr; - grub_multiboot_payload_size += code_size; - - grub_multiboot_payload_orig - = grub_relocator32_alloc (grub_multiboot_payload_size); - - if (! grub_multiboot_payload_orig) - goto fail; + err = grub_relocator_alloc_chunk_addr (grub_multiboot_relocator, + &source, header->load_addr, + code_size); + if (err) + { + grub_dprintf ("multiboot_loader", "Error loading aout kludge\n"); + goto fail; + } if ((grub_file_seek (file, offset)) == (grub_off_t) -1) goto fail; - grub_file_read (file, (void *) grub_multiboot_payload_orig, load_size); + grub_file_read (file, source, load_size); if (grub_errno) goto fail; if (header->bss_end_addr) - grub_memset ((void *) (grub_multiboot_payload_orig + load_size), 0, + grub_memset ((grub_uint32_t *) source + load_size, 0, header->bss_end_addr - header->load_addr - load_size); grub_multiboot_payload_eip = header->entry_addr; - } else if (grub_multiboot_load_elf (file, buffer) != GRUB_ERR_NONE) goto fail; /* This provides alignment for the MBI, the memory map and the backward relocator. */ - boot_loader_name_length += (0x04 - ((unsigned long) mbi_addr (grub_multiboot_payload_dest) & 0x03)); + boot_loader_name_length += (0x04 - ((unsigned long) mbi_addr (0) & 0x03)); + + err = grub_relocator_alloc_chunk_align (grub_multiboot_relocator, &mbichunk, + &mbichunk_dest, + 0, (0xffffffff - mbichunk_size) + 1, + mbichunk_size, 4); + if (err) + { + grub_dprintf ("multiboot_loader", "Error allocating mbi chunk\n"); + goto fail; + } + mbi = mbi_addr (mbichunk); + mbi_dest = mbi_addr (mbichunk_dest); - mbi = mbi_addr (grub_multiboot_payload_orig); - mbi_dest = mbi_addr (grub_multiboot_payload_dest); grub_memset (mbi, 0, sizeof (struct multiboot_info)); mbi->mmap_length = mmap_length; - grub_fill_multiboot_mmap (mmap_addr (grub_multiboot_payload_orig)); + grub_fill_multiboot_mmap (mmap_addr (mbichunk)); - /* FIXME: grub_uint32_t will break for addresses above 4 GiB, but is mandated - by the spec. Is there something we can do about it? */ - mbi->mmap_addr = (grub_uint32_t) mmap_addr (grub_multiboot_payload_dest); + mbi->mmap_addr = (grub_uint32_t) mmap_addr (mbichunk_dest); mbi->flags |= MULTIBOOT_INFO_MEM_MAP; /* Convert from bytes to kilobytes. */ @@ -358,7 +371,7 @@ grub_multiboot (int argc, char *argv[]) mbi->mem_upper = grub_mmap_get_upper () / 1024; mbi->flags |= MULTIBOOT_INFO_MEMORY; - cmdline = p = cmdline_addr (grub_multiboot_payload_orig); + cmdline = p = cmdline_addr (mbichunk); if (! cmdline) goto fail; @@ -374,17 +387,17 @@ grub_multiboot (int argc, char *argv[]) *p = 0; mbi->flags |= MULTIBOOT_INFO_CMDLINE; - mbi->cmdline = (grub_uint32_t) cmdline_addr (grub_multiboot_payload_dest); + mbi->cmdline = (grub_uint32_t) cmdline_addr (mbichunk_dest); - grub_strcpy (boot_loader_name_addr (grub_multiboot_payload_orig), PACKAGE_STRING); + grub_strcpy (boot_loader_name_addr (mbichunk), PACKAGE_STRING); mbi->flags |= MULTIBOOT_INFO_BOOT_LOADER_NAME; - mbi->boot_loader_name = (grub_uint32_t) boot_loader_name_addr (grub_multiboot_payload_dest); + mbi->boot_loader_name = (grub_uint32_t) boot_loader_name_addr (mbichunk_dest); if (grub_multiboot_get_bootdev (&mbi->boot_device)) mbi->flags |= MULTIBOOT_INFO_BOOTDEV; - grub_loader_set (grub_multiboot_boot, grub_multiboot_unload, 1); + grub_loader_set (grub_multiboot_boot, grub_multiboot_unload, 0); fail: if (file) diff --git a/loader/i386/multiboot_elfxx.c b/loader/i386/multiboot_elfxx.c index 80db25144..155de5801 100644 --- a/loader/i386/multiboot_elfxx.c +++ b/loader/i386/multiboot_elfxx.c @@ -51,7 +51,6 @@ CONCAT(grub_multiboot_load_elf, XX) (grub_file_t file, void *buffer) { Elf_Ehdr *ehdr = (Elf_Ehdr *) buffer; char *phdr_base; - int lowest_segment = -1, highest_segment = -1; int i; if (ehdr->e_ident[EI_CLASS] != ELFCLASSXX) @@ -82,54 +81,38 @@ CONCAT(grub_multiboot_load_elf, XX) (grub_file_t file, void *buffer) phdr_base = (char *) buffer + ehdr->e_phoff; #define phdr(i) ((Elf_Phdr *) (phdr_base + (i) * ehdr->e_phentsize)) - for (i = 0; i < ehdr->e_phnum; i++) - if (phdr(i)->p_type == PT_LOAD && phdr(i)->p_filesz != 0) - { - /* Beware that segment 0 isn't necessarily loadable */ - if (lowest_segment == -1 - || phdr(i)->p_paddr < phdr(lowest_segment)->p_paddr) - lowest_segment = i; - if (highest_segment == -1 - || phdr(i)->p_paddr > phdr(highest_segment)->p_paddr) - highest_segment = i; - } - - if (lowest_segment == -1) - return grub_error (GRUB_ERR_BAD_OS, "ELF contains no loadable segments"); - - code_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; - - grub_multiboot_payload_size += code_size; - - grub_multiboot_payload_orig - = grub_relocator32_alloc (grub_multiboot_payload_size); - - if (!grub_multiboot_payload_orig) - return grub_errno; - /* Load every loadable segment in memory. */ for (i = 0; i < ehdr->e_phnum; i++) { if (phdr(i)->p_type == PT_LOAD && phdr(i)->p_filesz != 0) { - char *load_this_module_at = (char *) (grub_multiboot_payload_orig + (long) (phdr(i)->p_paddr - phdr(lowest_segment)->p_paddr)); + grub_err_t err; + void *source; grub_dprintf ("multiboot_loader", "segment %d: paddr=0x%lx, memsz=0x%lx, vaddr=0x%lx\n", i, (long) phdr(i)->p_paddr, (long) phdr(i)->p_memsz, (long) phdr(i)->p_vaddr); + err = grub_relocator_alloc_chunk_addr (grub_multiboot_relocator, + &source, phdr(i)->p_paddr, + phdr(i)->p_memsz); + if (err) + { + grub_dprintf ("multiboot_loader", "Error loading phdr %d\n", i); + return err; + } + 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, load_this_module_at, phdr(i)->p_filesz) + if (grub_file_read (file, source, 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 (load_this_module_at + phdr(i)->p_filesz, 0, + grub_memset ((grub_uint8_t *) source + phdr(i)->p_filesz, 0, phdr(i)->p_memsz - phdr(i)->p_filesz); } } @@ -138,8 +121,8 @@ CONCAT(grub_multiboot_load_elf, XX) (grub_file_t file, void *buffer) if (phdr(i)->p_vaddr <= ehdr->e_entry && phdr(i)->p_vaddr + phdr(i)->p_memsz > ehdr->e_entry) { - grub_multiboot_payload_eip = grub_multiboot_payload_dest - + (ehdr->e_entry - phdr(i)->p_vaddr) + (phdr(i)->p_paddr - phdr(lowest_segment)->p_paddr); + grub_multiboot_payload_eip = (ehdr->e_entry - phdr(i)->p_vaddr) + + phdr(i)->p_paddr; break; }