diff --git a/common/Makefile b/common/Makefile
index 2d9ae8c..3801c28 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -189,6 +189,10 @@ COBJS-y += dlmalloc.o
 COBJS-y += memsize.o
 COBJS-y += stdio.o
 
+ifndef CONFIG_SYS_SKIP_RELOC
+COBJS-y += reloc.o
+endif
+
 
 COBJS	:= $(sort $(COBJS-y))
 XCOBJS	:= $(sort $(XCOBJS-y))
diff --git a/common/reloc.c b/common/reloc.c
new file mode 100644
index 0000000..207e907
--- /dev/null
+++ b/common/reloc.c
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2011 The Chromium OS Authors.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program 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 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <asm-generic/sections.h>
+#include <asm/reloc.h>
+#include <reloc.h>
+#include <nand.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static int reloc_make_copy(void)
+{
+	char *dst_addr = (char *)gd->relocaddr;
+
+	/* TODO: __text_start would be better when we have it */
+	char *src_addr = (char *)_start;
+	/* TODO: switch over to __image_copy_end when we can */
+#ifdef CONFIG_SPL_BUILD
+	char *end_addr = src_addr + _image_copy_end_ofs;
+#else
+	char *end_addr = src_addr + _rel_dyn_start_ofs;
+#endif
+
+	if (dst_addr != src_addr) {
+		size_t size = end_addr - src_addr;
+
+		debug("%s: copy code %p-%p to %p-%p\n", __func__,
+		      src_addr, end_addr, dst_addr, dst_addr + size);
+		memcpy(dst_addr, src_addr, size);
+	}
+	return 0;
+}
+
+static int reloc_elf(void)
+{
+#ifndef CONFIG_SPL_BUILD
+	const Elf32_Rel *ptr, *end;
+	Elf32_Addr *addr;
+	char *src_addr = (char *)_start;
+	Elf32_Sym *dynsym;
+	ulong reloc_ofs = gd->reloc_off;
+
+	/* scan the relocation table for relevant entries */
+	ptr = (Elf32_Rel *)(src_addr + _rel_dyn_start_ofs);
+	end = (Elf32_Rel *)(src_addr + _rel_dyn_end_ofs);
+	dynsym = (Elf32_Sym *)(src_addr + _dynsym_start_ofs);
+	debug("%s: process reloc entries %p-%p, dynsym at %p\n", __func__,
+	      ptr, end, dynsym);
+	for (; ptr < end; ptr++) {
+		addr = (Elf32_Addr *)(ptr->r_offset + reloc_ofs);
+		if (arch_elf_relocate_entry(addr, ptr->r_info, dynsym,
+				reloc_ofs))
+			return -1;
+	}
+#endif
+	return 0;
+}
+
+static int reloc_clear_bss(void)
+{
+	char *dst_addr = (char *)_start + _bss_start_ofs;
+	size_t size = _bss_end_ofs - _bss_start_ofs;
+
+#ifndef CONFIG_SPL_BUILD
+	/* No relocation for SPL (TBD: better to set reloc_off to zero) */
+	dst_addr += gd->reloc_off;
+#endif
+
+	/* TODO: use memset */
+	debug("%s: zero bss %p-%p\n", __func__, dst_addr, dst_addr + size);
+	memset(dst_addr, '\0', size);
+
+	return 0;
+}
+
+void __relocate_code(ulong dest_addr_sp, gd_t *new_gd, ulong dest_addr)
+{
+	ulong new_board_init_r = (uintptr_t)board_init_r + gd->reloc_off;
+
+	/* TODO: It might be better to put the offsets in global data */
+	debug("%s, dest_addr_sp=%lx, new_gd=%p, dest_addr=%lx\n", __func__,
+	      dest_addr_sp, new_gd, dest_addr);
+	reloc_make_copy();
+	reloc_elf();
+	reloc_clear_bss();
+
+	debug("relocation complete: starting from board_init_r() at %lx\n",
+	      new_board_init_r);
+	/* TODO: tidy this up since we don't want a separate nand_boot() */
+#ifdef CONFIG_NAND_SPL
+	nand_boot();
+#else
+	pivot_to_board_init_r(new_gd, dest_addr,
+			      (board_init_r_func)new_board_init_r,
+			      dest_addr_sp);
+#endif
+}
+
+/* Allow architectures to override this function - initially they all will */
+void relocate_code(ulong dest_sp, gd_t *new_gd, ulong dest_add)
+	__attribute__((weak, alias("__relocate_code")));
diff --git a/doc/README.relocation b/doc/README.relocation
new file mode 100644
index 0000000..6dfbe9c
--- /dev/null
+++ b/doc/README.relocation
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2011 The Chromium OS Authors.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program 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 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+Generic Relocation Framework
+============================
+
+Since most architectures perform relocation and mostly share the same
+procedure, a generic relocation framework has been created.
+
+
+What is Relocation?
+-------------------
+The basic purpose of relocation is to move U-Boot from its starting
+address (probably CONFIG_SYS_TEXT_BASE) to the top the RAM. This makes
+it easy to use the rest of available RAM in one chunk for things like
+loading a kernel or ram disk.
+
+The relocation code is in common/reloc.c in a function called
+__relocate_code(). It is called right at the end of board_init_f() and
+performs these steps:
+
+- Copies U-Boot to the top of RAM
+- Adjusts any code/data which needs relocation for the new position
+- Clears our the BSS (so that your global variables start as zero!)
+- Jumps to the new U-Boot, to a function called board_init_r()
+
+
+How do I use the framework?
+---------------------------
+To use the generic framework, you should define a function for your
+architecture in arch/xxx/include/asm/reloc.h like this:
+
+/**
+ * Process a single ELF relocation entry
+ *
+ * @param addr		Pointer to address of intruction/data to relocate
+ * @param info		The ELF information word / flags
+ * @param symtab	The ELF relocation symbol table
+ * @param reloc_off	Offset of relocated U-Boot relative to load address
+ * @return 0 if ok, -1 on error
+ */
+static inline int arch_elf_relocate_entry(Elf32_Addr *addr, Elf32_Word info,
+			    Elf32_Sym *symtab, ulong reloc_off);
+
+
+This function should relocate the code/data at the given relocated address
+based on the relocation information in 'info'. The ELF symbol table and
+relocation offset (new position minus CONFIG_SYS_TEXT_BASE) are provided.
+
+
+How fast is relocation?
+-----------------------
+It's pretty fast, but if you want to speed up relocation, you can define
+these two CONFIGs in your board file:
+
+#define CONFIG_USE_ARCH_MEMSET - speeds up BSS clearing
+#define CONFIG_USE_ARCH_MEMCPY - speeds up copying of code/data
+
+Rough benchmarks on a Tegra2x ARM system showed that using both cut the total
+relocation time by 65% (from 15ms to 5ms).
+
+
+Opting Out
+----------
+If you want to do relocation yourself, you can define your own
+relocate_code() function. See include/reloc.h for the prototype. You
+can also define CONFIG_SYS_SKIP_RELOC to disable the generic relocation
+and remove its code.
diff --git a/include/asm-generic/sections.h b/include/asm-generic/sections.h
new file mode 100644
index 0000000..2935dc1
--- /dev/null
+++ b/include/asm-generic/sections.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2011 The Chromium OS Authors.
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program 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 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+/* Taken from Linux kernel */
+
+#ifndef _ASM_GENERIC_SECTIONS_H_
+#define _ASM_GENERIC_SECTIONS_H_
+
+/* References to section boundaries */
+
+extern char _text[], _stext[], _etext[];
+extern char _data[], _sdata[], _edata[];
+extern char __bss_start[], __bss_stop[];
+extern char __init_begin[], __init_end[];
+extern char _sinittext[], _einittext[];
+extern char _end[];
+extern char __per_cpu_load[], __per_cpu_start[], __per_cpu_end[];
+extern char __kprobes_text_start[], __kprobes_text_end[];
+extern char __entry_text_start[], __entry_text_end[];
+extern char __initdata_begin[], __initdata_end[];
+extern char __start_rodata[], __end_rodata[];
+
+/* Start and end of .ctors section - used for constructor calls. */
+extern char __ctors_start[], __ctors_end[];
+
+/* function descriptor handling (if any).  Override
+ * in asm/sections.h */
+#ifndef dereference_function_descriptor
+#define dereference_function_descriptor(p) (p)
+#endif
+
+/* random extra sections (if any).  Override
+ * in asm/sections.h */
+#ifndef arch_is_kernel_text
+static inline int arch_is_kernel_text(unsigned long addr)
+{
+	return 0;
+}
+#endif
+
+#ifndef arch_is_kernel_data
+static inline int arch_is_kernel_data(unsigned long addr)
+{
+	return 0;
+}
+#endif
+
+#include <elf.h>
+
+/* U-Boot-specific things begin here */
+
+/* Start of U-Boot text region */
+extern char __text_start[];
+
+/* This marks the end of the text region which must be relocated */
+extern char __image_copy_end[];
+
+/*
+ * This is the U-Boot entry point - prior to relocation it should be same
+ * as __text_start
+ */
+extern void _start(void);
+
+/* Start/end of the relocation entries, as an offset from _start */
+extern ulong _rel_dyn_start_ofs;
+extern ulong _rel_dyn_end_ofs;
+
+/* Start/end of the relocation symbol table, as an offset from _start */
+extern ulong _dynsym_start_ofs;
+
+/* End of the region to be relocated, as an offset form _start */
+extern ulong _image_copy_end_ofs;
+
+#endif /* _ASM_GENERIC_SECTIONS_H_ */
diff --git a/include/reloc.h b/include/reloc.h
index 3dc7b85..231892c 100644
--- a/include/reloc.h
+++ b/include/reloc.h
@@ -23,12 +23,27 @@
 #ifndef __RELOC_H
 #define __RELOC_H
 
+/* This is the prototype for the post-relocation init function */
+typedef void (*board_init_r_func)(gd_t *, ulong);
+
+/**
+ * Call the relocated U-Boot. This is the last thing that is done after
+ * relocation. This function does not return.
+ *
+ * @param new_gd		Pointer to the relocated global data
+ * @param dest_addr		Base code address of relocated U-Boot
+ * @param board_init_r_func	Pointer to relocated function to call
+ */
+void pivot_to_board_init_r(gd_t *new_gd, ulong dest_addr,
+		board_init_r_func board_init_r, ulong dest_addr_sp)
+		__attribute__ ((noreturn));
+
 /**
  * Relocate U-Boot and jump to the relocated coded
  *
  * This copies U-Boot to a new location, zeroes the BSS, sets up a new stack
  * and jumps to board_init_r() in the relocated code using the
- * proc_call_board_init_r() function. It does not return.
+ * pivot_to_board_init_r() function. It does not return.
  *
  * @param dest_sp	New stack pointer to use
  * @param new_gd	Pointer to the relocated global data
