From 7d8c9ec63df5396df0bdc9843675ed56876525fd Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sat, 23 Jan 2010 13:30:24 +0100 Subject: [PATCH] newreloc on yeeloong --- conf/mips.rmk | 4 +- include/grub/mips/relocator.h | 9 ++- include/grub/relocator_private.h | 2 +- lib/i386/relocator.c | 6 +- lib/mips/relocator.c | 111 ++++++++++++++++++++----------- lib/mips/relocator_asm.S | 8 +-- lib/relocator.c | 8 ++- loader/mips/linux.c | 60 +++++++++-------- 8 files changed, 126 insertions(+), 82 deletions(-) diff --git a/conf/mips.rmk b/conf/mips.rmk index d0b1c484c..0e96caeb4 100644 --- a/conf/mips.rmk +++ b/conf/mips.rmk @@ -17,7 +17,7 @@ kernel_img_HEADERS = boot.h cache.h device.h disk.h dl.h elf.h elfload.h \ env.h err.h file.h fs.h kernel.h misc.h mm.h net.h parser.h reader.h \ symbol.h term.h time.h types.h loader.h partition.h \ msdos_partition.h machine/kernel.h handler.h list.h \ - command.h machine/memory.h cpu/libgcc.h cpu/cache.h i18n.h + command.h machine/memory.h cpu/libgcc.h cpu/cache.h i18n.h mm_private.h ifeq ($(platform), yeeloong) kernel_img_HEADERS += pci.h @@ -68,7 +68,7 @@ serial_mod_LDFLAGS = $(COMMON_LDFLAGS) # For relocator.mod. pkglib_MODULES += relocator.mod -relocator_mod_SOURCES = lib/$(target_cpu)/relocator.c lib/$(target_cpu)/relocator_asm.S +relocator_mod_SOURCES = lib/$(target_cpu)/relocator.c lib/relocator.c lib/$(target_cpu)/relocator_asm.S relocator_mod_CFLAGS = $(COMMON_CFLAGS) relocator_mod_ASFLAGS = $(COMMON_ASFLAGS) relocator_mod_LDFLAGS = $(COMMON_LDFLAGS) diff --git a/include/grub/mips/relocator.h b/include/grub/mips/relocator.h index 838ef832f..67b0a4c43 100644 --- a/include/grub/mips/relocator.h +++ b/include/grub/mips/relocator.h @@ -21,6 +21,7 @@ #include #include +#include struct grub_relocator32_state { @@ -30,10 +31,8 @@ struct grub_relocator32_state int jumpreg; }; -void *grub_relocator32_alloc (grub_size_t size); -grub_err_t grub_relocator32_boot (void *relocator, grub_uint32_t dest, - struct grub_relocator32_state state); -void *grub_relocator32_realloc (void *relocator, grub_size_t size); -void grub_relocator32_free (void *relocator); +grub_err_t +grub_relocator32_boot (struct grub_relocator *rel, + struct grub_relocator32_state state); #endif /* ! GRUB_RELOCATOR_CPU_HEADER */ diff --git a/include/grub/relocator_private.h b/include/grub/relocator_private.h index 7a3ef2bd7..f9e76468e 100644 --- a/include/grub/relocator_private.h +++ b/include/grub/relocator_private.h @@ -31,7 +31,7 @@ void grub_cpu_relocator_init (void); grub_err_t grub_relocator_prepare_relocs (struct grub_relocator *rel, grub_addr_t addr, - grub_addr_t *relstart); + grub_addr_t *relstart, grub_size_t *relsize); void grub_cpu_relocator_forward (void *rels, void *src, void *tgt, grub_size_t size); void grub_cpu_relocator_backward (void *rels, void *src, void *tgt, diff --git a/lib/i386/relocator.c b/lib/i386/relocator.c index d040c80ab..4eaa66890 100644 --- a/lib/i386/relocator.c +++ b/lib/i386/relocator.c @@ -169,7 +169,7 @@ grub_relocator32_boot (struct grub_relocator *rel, grub_memmove (src, &grub_relocator32_start, RELOCATOR_SIZEOF (32)); - err = grub_relocator_prepare_relocs (rel, target, &relst); + err = grub_relocator_prepare_relocs (rel, target, &relst, NULL); if (err) return err; @@ -209,7 +209,7 @@ grub_relocator16_boot (struct grub_relocator *rel, grub_memmove (src, &grub_relocator16_start, RELOCATOR_SIZEOF (16)); - err = grub_relocator_prepare_relocs (rel, target, &relst); + err = grub_relocator_prepare_relocs (rel, target, &relst, NULL); if (err) return err; @@ -248,7 +248,7 @@ grub_relocator64_boot (struct grub_relocator *rel, grub_memmove (src, &grub_relocator64_start, RELOCATOR_SIZEOF (64)); - err = grub_relocator_prepare_relocs (rel, target, &relst); + err = grub_relocator_prepare_relocs (rel, target, &relst, NULL); if (err) return err; diff --git a/lib/mips/relocator.c b/lib/mips/relocator.c index 118ddbd6f..410b68b8b 100644 --- a/lib/mips/relocator.c +++ b/lib/mips/relocator.c @@ -25,26 +25,33 @@ #include #include +#include -/* Remark: doesn't work with source outside of 4G. - Use relocator64 in this case. - */ +/* Do we need mips64? */ -extern grub_uint8_t grub_relocator32_forward_start; -extern grub_uint8_t grub_relocator32_forward_end; -extern grub_uint8_t grub_relocator32_backward_start; -extern grub_uint8_t grub_relocator32_backward_end; +extern grub_uint8_t grub_relocator_forward_start; +extern grub_uint8_t grub_relocator_forward_end; +extern grub_uint8_t grub_relocator_backward_start; +extern grub_uint8_t grub_relocator_backward_end; #define REGW_SIZEOF (2 * sizeof (grub_uint32_t)) #define JUMP_SIZEOF (2 * sizeof (grub_uint32_t)) -#define RELOCATOR_SRC_SIZEOF(x) (&grub_relocator32_##x##_end \ - - &grub_relocator32_##x##_start) +#define RELOCATOR_SRC_SIZEOF(x) (&grub_relocator_##x##_end \ + - &grub_relocator_##x##_start) #define RELOCATOR_SIZEOF(x) (RELOCATOR_SRC_SIZEOF(x) \ - + REGW_SIZEOF * (31 + 3) + JUMP_SIZEOF) -#define RELOCATOR_ALIGN 16 + + REGW_SIZEOF * 3) +grub_size_t grub_relocator_align = sizeof (grub_uint32_t); +grub_size_t grub_relocator_forward_size; +grub_size_t grub_relocator_backward_size; +grub_size_t grub_relocator_jumper_size = JUMP_SIZEOF + REGW_SIZEOF; -#define PREFIX(x) grub_relocator32_ ## x +void +grub_cpu_relocator_init (void) +{ + grub_relocator_forward_size = RELOCATOR_SIZEOF(forward); + grub_relocator_backward_size = RELOCATOR_SIZEOF(backward); +} static void write_reg (int regn, grub_uint32_t val, void **target) @@ -69,44 +76,70 @@ write_jump (int regn, void **target) *target = ((grub_uint32_t *) *target) + 1; } -static void -write_call_relocator_bw (void *ptr0, void *src, grub_uint32_t dest, - grub_size_t size, struct grub_relocator32_state state) +void +grub_cpu_relocator_jumper (void *rels, grub_addr_t addr) +{ + write_reg (1, addr, &rels); + write_jump (1, &rels); +} + +void +grub_cpu_relocator_backward (void *ptr0, void *src, void *dest, + grub_size_t size) { void *ptr = ptr0; - int i; write_reg (8, (grub_uint32_t) src, &ptr); - write_reg (9, dest, &ptr); - write_reg (10, size, &ptr); - grub_memcpy (ptr, &grub_relocator32_backward_start, + write_reg (9, (grub_uint32_t) dest, &ptr); + write_reg (10, (grub_uint32_t) size, &ptr); + grub_memcpy (ptr, &grub_relocator_backward_start, RELOCATOR_SRC_SIZEOF (backward)); - ptr = (grub_uint8_t *) ptr + RELOCATOR_SRC_SIZEOF (backward); - for (i = 1; i < 32; i++) - write_reg (i, state.gpr[i], &ptr); - write_jump (state.jumpreg, &ptr); - grub_arch_sync_caches (ptr0, (grub_uint8_t *) ptr - (grub_uint8_t *) ptr0); - grub_dprintf ("relocator", "Backward relocator: about to jump to %p\n", ptr0); - ((void (*) (void)) ptr0) (); } -static void -write_call_relocator_fw (void *ptr0, void *src, grub_uint32_t dest, - grub_size_t size, struct grub_relocator32_state state) +void +grub_cpu_relocator_forward (void *ptr0, void *src, void *dest, + grub_size_t size) { void *ptr = ptr0; - int i; write_reg (8, (grub_uint32_t) src, &ptr); - write_reg (9, dest, &ptr); - write_reg (10, size, &ptr); - grub_memcpy (ptr, &grub_relocator32_forward_start, + write_reg (9, (grub_uint32_t) dest, &ptr); + write_reg (10, (grub_uint32_t) size, &ptr); + grub_memcpy (ptr, &grub_relocator_forward_start, RELOCATOR_SRC_SIZEOF (forward)); - ptr = (grub_uint8_t *) ptr + RELOCATOR_SRC_SIZEOF (forward); +} + +grub_err_t +grub_relocator32_boot (struct grub_relocator *rel, + struct grub_relocator32_state state) +{ + grub_addr_t target; + void *src, *ptr; + grub_err_t err; + grub_addr_t relst; + grub_size_t relsize; + grub_size_t stateset_size = 31 * REGW_SIZEOF + JUMP_SIZEOF; + unsigned i; + + err = grub_relocator_alloc_chunk_align (rel, &src, &target, 0, + (0xffffffff - stateset_size) + + 1, stateset_size, + sizeof (grub_uint32_t), + GRUB_RELOCATOR_PREFERENCE_NONE); + if (err) + return err; + + ptr = src; for (i = 1; i < 32; i++) write_reg (i, state.gpr[i], &ptr); write_jump (state.jumpreg, &ptr); - grub_arch_sync_caches (ptr0, (grub_uint8_t *) ptr - (grub_uint8_t *) ptr0); - grub_dprintf ("relocator", "Forward relocator: about to jump to %p\n", ptr0); - ((void (*) (void)) ptr0) (); -} -#include "../relocator.c" + err = grub_relocator_prepare_relocs (rel, target, &relst, &relsize); + if (err) + return err; + + grub_arch_sync_caches ((void *) relst, relsize); + + ((void (*) (void)) relst) (); + + /* Not reached. */ + return GRUB_ERR_NONE; +} diff --git a/lib/mips/relocator_asm.S b/lib/mips/relocator_asm.S index ff4fa31e0..8ffab99b7 100644 --- a/lib/mips/relocator_asm.S +++ b/lib/mips/relocator_asm.S @@ -20,7 +20,7 @@ .p2align 4 /* force 16-byte alignment */ -VARIABLE (grub_relocator32_forward_start) +VARIABLE (grub_relocator_forward_start) move $a0, $9 move $a1, $10 @@ -34,9 +34,9 @@ copycont1: #include "../../kern/mips/cache_flush.S" -VARIABLE (grub_relocator32_forward_end) +VARIABLE (grub_relocator_forward_end) -VARIABLE (grub_relocator32_backward_start) +VARIABLE (grub_relocator_backward_start) move $a0, $9 move $a1, $10 @@ -55,4 +55,4 @@ copycont2: #include "../../kern/mips/cache_flush.S" -VARIABLE (grub_relocator32_backward_end) +VARIABLE (grub_relocator_backward_end) diff --git a/lib/relocator.c b/lib/relocator.c index 0644691cd..dad0ec70c 100644 --- a/lib/relocator.c +++ b/lib/relocator.c @@ -20,6 +20,7 @@ #include #include #include +#include /* TODO: use more efficient data structures if necessary. */ /* FIXME: implement unload. */ @@ -605,7 +606,7 @@ grub_relocator_unload (struct grub_relocator *rel) grub_err_t grub_relocator_prepare_relocs (struct grub_relocator *rel, grub_addr_t addr, - grub_addr_t *relstart) + grub_addr_t *relstart, grub_size_t *relsize) { grub_addr_t rels; grub_addr_t rels0; @@ -622,6 +623,9 @@ grub_relocator_prepare_relocs (struct grub_relocator *rel, grub_addr_t addr, return grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory"); rels = rels0; + if (relsize) + *relsize = rel->relocators_size; + grub_dprintf ("relocator", "Relocs allocated\n"); { @@ -698,6 +702,8 @@ grub_relocator_prepare_relocs (struct grub_relocator *rel, grub_addr_t addr, sorted[j].size); rels += grub_relocator_forward_size; } + if (sorted[j].src == sorted[j].target) + grub_arch_sync_caches ((void *) sorted[j].src, sorted[j].size); } grub_cpu_relocator_jumper ((void *) rels, addr); *relstart = rels0; diff --git a/loader/mips/linux.c b/loader/mips/linux.c index 51060c4fb..09c7f1275 100644 --- a/loader/mips/linux.c +++ b/loader/mips/linux.c @@ -42,6 +42,7 @@ static int loaded; static grub_size_t linux_size; +static struct grub_relocator *relocator; static grub_uint8_t *playground; static grub_addr_t target_addr, entry_addr; static int linux_argc; @@ -60,15 +61,7 @@ grub_linux_boot (void) state.gpr[5] = target_addr + argv_off; state.gpr[6] = target_addr + envp_off; state.jumpreg = 1; - grub_relocator32_boot (playground, target_addr, state); - - return GRUB_ERR_NONE; -} - -static grub_err_t -grub_linux_release_mem (void) -{ - grub_relocator32_free (playground); + grub_relocator32_boot (relocator, state); return GRUB_ERR_NONE; } @@ -76,14 +69,12 @@ grub_linux_release_mem (void) static grub_err_t grub_linux_unload (void) { - grub_err_t err; - - err = grub_linux_release_mem (); + grub_relocator_unload (relocator); grub_dl_unref (my_mod); loaded = 0; - return err; + return GRUB_ERR_NONE; } static grub_err_t @@ -91,6 +82,7 @@ grub_linux_load32 (grub_elf_t elf, void **extra_mem, grub_size_t extra_size) { Elf32_Addr base; int extraoff; + grub_err_t err; /* Linux's entry point incorrectly contains a virtual address. */ entry_addr = elf->ehdr.ehdr32.e_entry & ~ELF32_LOADMASK; @@ -105,10 +97,15 @@ grub_linux_load32 (grub_elf_t elf, void **extra_mem, grub_size_t extra_size) extraoff = linux_size; linux_size += extra_size; - playground = grub_relocator32_alloc (linux_size); - if (!playground) + relocator = grub_relocator_new (); + if (!relocator) return grub_errno; + err = grub_relocator_alloc_chunk_addr (relocator, (void **) &playground, + target_addr, linux_size); + if (err) + return err; + *extra_mem = playground + extraoff; /* Now load the segments into the area we claimed. */ @@ -135,6 +132,7 @@ grub_linux_load64 (grub_elf_t elf, void **extra_mem, grub_size_t extra_size) { Elf64_Addr base; int extraoff; + grub_err_t err; /* Linux's entry point incorrectly contains a virtual address. */ entry_addr = elf->ehdr.ehdr64.e_entry & ~ELF64_LOADMASK; @@ -149,10 +147,15 @@ grub_linux_load64 (grub_elf_t elf, void **extra_mem, grub_size_t extra_size) extraoff = linux_size; linux_size += extra_size; - playground = grub_relocator32_alloc (linux_size); - if (!playground) + relocator = grub_relocator_new (); + if (!relocator) return grub_errno; + err = grub_relocator_alloc_chunk_addr (relocator, (void **) &playground, + target_addr, linux_size); + if (err) + return err; + *extra_mem = playground + extraoff; /* Now load the segments into the area we claimed. */ @@ -322,7 +325,9 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), { grub_file_t file = 0; grub_ssize_t size; - grub_size_t overhead; + void *initrd_src; + grub_addr_t initrd_dest; + grub_err_t err; if (argc == 0) return grub_error (GRUB_ERR_BAD_ARGUMENT, "No initrd specified"); @@ -339,19 +344,20 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), size = grub_file_size (file); - overhead = ALIGN_UP (target_addr + linux_size + 0x10000, 0x10000) - - (target_addr + linux_size); + err = grub_relocator_alloc_chunk_align (relocator, &initrd_src, + &initrd_dest, + target_addr + linux_size + 0x10000, + (0xffffffff - size) + 1, + size, 0x10000, + GRUB_RELOCATOR_PREFERENCE_NONE); - playground = grub_relocator32_realloc (playground, - linux_size + overhead + size); - - if (!playground) + if (err) { grub_file_close (file); - return grub_errno; + return err; } - if (grub_file_read (file, playground + linux_size + overhead, size) != size) + if (grub_file_read (file, initrd_src, size) != size) { grub_error (GRUB_ERR_FILE_READ_ERROR, "Couldn't read file"); grub_file_close (file); @@ -361,7 +367,7 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), grub_snprintf ((char *) playground + rd_addr_arg_off, sizeof ("rd_start=0xXXXXXXXXXXXXXXXX"), "rd_start=0x%llx", - (unsigned long long) target_addr + linux_size + overhead); + (unsigned long long) initrd_dest); ((grub_uint32_t *) (playground + argv_off))[linux_argc] = target_addr + rd_addr_arg_off; linux_argc++;