Patchwork [U-Boot,v5,03/14] Add generic relocation feature

login
register
mail settings
Submitter Simon Glass
Date Feb. 23, 2012, 1:03 p.m.
Message ID <1330002228-29348-4-git-send-email-sjg@chromium.org>
Download mbox | patch
Permalink /patch/142605/
State Deferred
Delegated to: Tom Rini
Headers show

Comments

Simon Glass - Feb. 23, 2012, 1:03 p.m.
Add a relocation implementation as the first thing in the generic board
library. This library is needed by SPL also.

We create a separate header file for link symbols defined by the link
scripts. It is helpful to have these all in one place and try to
make them common across architectures. Since Linux already has a similar
file, we bring this in even though many of the symbols there are not
relevant to us.

The __relocate_code() function is what we expect all architectures which
support relocation will use eventually. For now, they all override this
with their own version.

Note: The conflict with the generic board series is that the file
include/asm-generic/sections.h is created in both. This should be easy
to resolve once we know the order in which these series will be applied.

Signed-off-by: Simon Glass <sjg@chromium.org>
---
Changes in v2:
- Add README file for relocation
- Add function comments
- Import asm-generic/sections.h from Linux and add U-Boot extras
- Move reloc.c into common/
- Squash generic link symbols patch into generic relocation patch
- Use memset, memcpy instead of inline code

Changes in v4:
- Use renamed start_call_board_init_r() function

Changes in v5:
- Rename start_call_board_init_r() to pivot_to_board_init_r()

 common/Makefile                |    4 +
 common/reloc.c                 |  121 ++++++++++++++++++++++++++++++++++++++++
 doc/README.relocation          |   87 ++++++++++++++++++++++++++++
 include/asm-generic/sections.h |   92 ++++++++++++++++++++++++++++++
 include/reloc.h                |   17 +++++-
 5 files changed, 320 insertions(+), 1 deletions(-)
 create mode 100644 common/reloc.c
 create mode 100644 doc/README.relocation
 create mode 100644 include/asm-generic/sections.h

Patch

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