diff --git a/conf/mips.rmk b/conf/mips.rmk
index 6ebd7d665..46b74d68f 100644
--- a/conf/mips.rmk
+++ b/conf/mips.rmk
@@ -140,4 +140,11 @@ pci_mod_SOURCES = bus/pci.c
pci_mod_CFLAGS = $(COMMON_CFLAGS)
pci_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_CFLAGS = $(COMMON_CFLAGS)
+relocator_mod_ASFLAGS = $(COMMON_ASFLAGS)
+relocator_mod_LDFLAGS = $(COMMON_LDFLAGS)
+
include $(srcdir)/conf/common.mk
diff --git a/include/grub/mips/relocator.h b/include/grub/mips/relocator.h
new file mode 100644
index 000000000..838ef832f
--- /dev/null
+++ b/include/grub/mips/relocator.h
@@ -0,0 +1,39 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see .
+ */
+
+#ifndef GRUB_RELOCATOR_CPU_HEADER
+#define GRUB_RELOCATOR_CPU_HEADER 1
+
+#include
+#include
+
+struct grub_relocator32_state
+{
+ /* gpr[0] is ignored since it's hardwired to 0. */
+ grub_uint32_t gpr[32];
+ /* Register holding target $pc. */
+ 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);
+
+#endif /* ! GRUB_RELOCATOR_CPU_HEADER */
diff --git a/kern/mips/qemu-mipssim/startup.S b/kern/mips/qemu-mipssim/startup.S
index 19de4779d..36c5fabd7 100644
--- a/kern/mips/qemu-mipssim/startup.S
+++ b/kern/mips/qemu-mipssim/startup.S
@@ -49,7 +49,7 @@ bsscont:
sb $0,0($t1)
addiu $t1,$t1,1
sltu $t3,$t1,$t2
- bne $3, $0, bsscont
+ bne $t3, $t0, bsscont
li $sp, GRUB_MACHINE_MEMORY_STACK_HIGH
b grub_main
\ No newline at end of file
diff --git a/lib/mips/relocator.c b/lib/mips/relocator.c
new file mode 100644
index 000000000..4ec7f6c08
--- /dev/null
+++ b/lib/mips/relocator.c
@@ -0,0 +1,102 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see .
+ */
+
+#include
+#include
+
+#include
+#include
+#include
+
+#include
+
+/* Remark: doesn't work with source outside of 4G.
+ Use relocator64 in this case.
+ */
+
+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;
+
+#define REGW_SIZEOF (2 * sizeof (grub_uint32_t))
+#define JUMP_SIZEOF (sizeof (grub_uint32_t))
+
+#define RELOCATOR_SRC_SIZEOF(x) (&grub_relocator32_##x##_end \
+ - &grub_relocator32_##x##_start)
+#define RELOCATOR_SIZEOF(x) (RELOCATOR_SRC_SIZEOF(x) \
+ + REGW_SIZEOF * (31 + 3) + JUMP_SIZEOF)
+#define RELOCATOR_ALIGN 16
+
+#define PREFIX(x) grub_relocator32_ ## x
+
+static void
+write_reg (int regn, grub_uint32_t val, void **target)
+{
+ /* lui $r, (val+0x8000). */
+ *(grub_uint32_t *) *target = ((0x3c00 | regn) << 16) | ((val + 0x8000) >> 16);
+ *target = ((grub_uint32_t *) *target) + 1;
+ /* addiu $r, $r, val. */
+ *(grub_uint32_t *) *target = (((0x2400 | regn << 5 | regn) << 16)
+ | ((val + 0x8000) >> 16));
+ *target = ((grub_uint32_t *) *target) + 1;
+}
+
+static void
+write_jump (int regn, void **target)
+{
+ /* j $r. */
+ *(grub_uint32_t *) *target = (regn<<21) | 0x8;
+ *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 *ptr = ptr0;
+ int i;
+ write_reg (1, (grub_uint32_t) src, &ptr);
+ write_reg (2, dest, &ptr);
+ write_reg (3, size, &ptr);
+ grub_memcpy (ptr, &grub_relocator32_backward_start,
+ RELOCATOR_SRC_SIZEOF (backward));
+ for (i = 1; i < 32; i++)
+ write_reg (i, state.gpr[i], &ptr);
+ write_jump (state.jumpreg, &ptr);
+ ((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 *ptr = ptr0;
+ int i;
+ write_reg (1, (grub_uint32_t) src, &ptr);
+ write_reg (2, dest, &ptr);
+ write_reg (3, size, &ptr);
+ grub_memcpy (ptr, &grub_relocator32_forward_start,
+ RELOCATOR_SRC_SIZEOF (forward));
+ for (i = 1; i < 32; i++)
+ write_reg (i, state.gpr[i], &ptr);
+ write_jump (state.jumpreg, &ptr);
+ ((void (*) ())ptr0) ();
+}
+
+#include "../relocator.c"
diff --git a/lib/mips/relocator_asm.S b/lib/mips/relocator_asm.S
new file mode 100644
index 000000000..607a46303
--- /dev/null
+++ b/lib/mips/relocator_asm.S
@@ -0,0 +1,50 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see .
+ */
+
+#include
+
+#ifdef BACKWARD
+#define RELOCATOR_VARIABLE(x) VARIABLE(grub_relocator32_backward_ ## x)
+#else
+#define RELOCATOR_VARIABLE(x) VARIABLE(grub_relocator32_forward_ ## x)
+#endif
+
+ .p2align 4 /* force 16-byte alignment */
+
+VARIABLE (grub_relocator32_forward_start)
+copycont1:
+ lb $4,0($1)
+ sb $4,0($2)
+ addiu $3, $3, 0xffff
+ subu $4,$3,$0
+ bne $4, $0, copycont1
+VARIABLE (grub_relocator32_forward_end)
+
+VARIABLE (grub_relocator32_backward_start)
+ addu $2, $2, $3
+ addu $1, $1, $3
+ /* Backward movsl is implicitly off-by-one. compensate that. */
+ addiu $2, $2, 0xffff
+ addiu $1, $1, 0xffff
+copycont2:
+ lb $4,0($1)
+ sb $4,0($2)
+ addiu $3, 0xffff
+ subu $4,$3,$0
+ bne $4, $0, copycont2
+VARIABLE (grub_relocator32_backward_end)