summaryrefslogtreecommitdiff
path: root/arch/mips
diff options
context:
space:
mode:
Diffstat (limited to 'arch/mips')
-rw-r--r--arch/mips/Makefile.postlink23
-rw-r--r--arch/mips/config.mk21
-rw-r--r--arch/mips/cpu/start.S130
-rw-r--r--arch/mips/cpu/u-boot.lds41
-rw-r--r--arch/mips/include/asm/relocs.h24
-rw-r--r--arch/mips/include/asm/sections.h7
-rw-r--r--arch/mips/lib/Makefile1
-rw-r--r--arch/mips/lib/reloc.c164
8 files changed, 233 insertions, 178 deletions
diff --git a/arch/mips/Makefile.postlink b/arch/mips/Makefile.postlink
new file mode 100644
index 0000000000..7da3acdf52
--- /dev/null
+++ b/arch/mips/Makefile.postlink
@@ -0,0 +1,23 @@
+#
+# Copyright (c) 2017 Imagination Technologies Ltd.
+#
+# SPDX-License-Identifier: GPL-2.0+
+#
+
+PHONY := __archpost
+__archpost:
+
+-include include/config/auto.conf
+include scripts/Kbuild.include
+
+CMD_RELOCS = tools/mips-relocs
+quiet_cmd_relocs = RELOCS $@
+ cmd_relocs = $(CMD_RELOCS) $@
+
+u-boot: FORCE
+ @true
+ $(call if_changed,relocs)
+
+.PHONY: FORCE
+
+FORCE:
diff --git a/arch/mips/config.mk b/arch/mips/config.mk
index 2c72c1553d..cefdbe65e1 100644
--- a/arch/mips/config.mk
+++ b/arch/mips/config.mk
@@ -56,25 +56,14 @@ PLATFORM_ELFFLAGS += -B mips $(OBJCOPYFLAGS)
# LDFLAGS_vmlinux += -G 0 -static -n -nostdlib
# MODFLAGS += -mlong-calls
#
-# On the other hand, we want PIC in the U-Boot code to relocate it from ROM
-# to RAM. $28 is always used as gp.
-#
-ifdef CONFIG_SPL_BUILD
-PF_ABICALLS := -mno-abicalls
-PF_PIC := -fno-pic
-PF_PIE :=
-else
-PF_ABICALLS := -mabicalls
-PF_PIC := -fpic
-PF_PIE := -pie
-PF_OBJCOPY := -j .got -j .rel.dyn -j .padding
-PF_OBJCOPY += -j .dtb.init.rodata
+ifndef CONFIG_SPL_BUILD
+OBJCOPYFLAGS += -j .got -j .rel -j .padding -j .dtb.init.rodata
+LDFLAGS_FINAL += --emit-relocs
endif
-PLATFORM_CPPFLAGS += -G 0 $(PF_ABICALLS) $(PF_PIC)
+PLATFORM_CPPFLAGS += -G 0 -mno-abicalls -fno-pic
PLATFORM_CPPFLAGS += -msoft-float
PLATFORM_LDFLAGS += -G 0 -static -n -nostdlib
PLATFORM_RELFLAGS += -ffunction-sections -fdata-sections
-LDFLAGS_FINAL += --gc-sections $(PF_PIE)
+LDFLAGS_FINAL += --gc-sections
OBJCOPYFLAGS += -j .text -j .rodata -j .data -j .u_boot_list
-OBJCOPYFLAGS += $(PF_OBJCOPY)
diff --git a/arch/mips/cpu/start.S b/arch/mips/cpu/start.S
index d01ee9f9bd..952c57afd7 100644
--- a/arch/mips/cpu/start.S
+++ b/arch/mips/cpu/start.S
@@ -221,18 +221,6 @@ wr_done:
ehb
#endif
- /*
- * Initialize $gp, force pointer sized alignment of bal instruction to
- * forbid the compiler to put nop's between bal and _gp. This is
- * required to keep _gp and ra aligned to 8 byte.
- */
- .align PTRLOG
- bal 1f
- nop
- PTR _gp
-1:
- PTR_L gp, 0(ra)
-
#ifdef CONFIG_MIPS_CM
PTR_LA t9, mips_cm_map
jalr t9
@@ -291,121 +279,3 @@ wr_done:
move ra, zero
END(_start)
-
-/*
- * void relocate_code (addr_sp, gd, addr_moni)
- *
- * This "function" does not return, instead it continues in RAM
- * after relocating the monitor code.
- *
- * a0 = addr_sp
- * a1 = gd
- * a2 = destination address
- */
-ENTRY(relocate_code)
- move sp, a0 # set new stack pointer
- move fp, sp
-
- move s0, a1 # save gd in s0
- move s2, a2 # save destination address in s2
-
- PTR_LI t0, CONFIG_SYS_MONITOR_BASE
- PTR_SUB s1, s2, t0 # s1 <-- relocation offset
-
- PTR_LA t2, __image_copy_end
- move t1, a2
-
- /*
- * t0 = source address
- * t1 = target address
- * t2 = source end address
- */
-1:
- PTR_L t3, 0(t0)
- PTR_S t3, 0(t1)
- PTR_ADDU t0, PTRSIZE
- blt t0, t2, 1b
- PTR_ADDU t1, PTRSIZE
-
- /*
- * Now we want to update GOT.
- *
- * GOT[0] is reserved. GOT[1] is also reserved for the dynamic object
- * generated by GNU ld. Skip these reserved entries from relocation.
- */
- PTR_LA t3, num_got_entries
- PTR_LA t8, _GLOBAL_OFFSET_TABLE_
- PTR_ADD t8, s1 # t8 now holds relocated _G_O_T_
- PTR_ADDIU t8, t8, 2 * PTRSIZE # skipping first two entries
- PTR_LI t2, 2
-1:
- PTR_L t1, 0(t8)
- beqz t1, 2f
- PTR_ADD t1, s1
- PTR_S t1, 0(t8)
-2:
- PTR_ADDIU t2, 1
- blt t2, t3, 1b
- PTR_ADDIU t8, PTRSIZE
-
- /* Update dynamic relocations */
- PTR_LA t1, __rel_dyn_start
- PTR_LA t2, __rel_dyn_end
-
- b 2f # skip first reserved entry
- PTR_ADDIU t1, 2 * PTRSIZE
-
-1:
- lw t8, -4(t1) # t8 <-- relocation info
-
- PTR_LI t3, MIPS_RELOC
- bne t8, t3, 2f # skip non-MIPS_RELOC entries
- nop
-
- PTR_L t3, -(2 * PTRSIZE)(t1) # t3 <-- location to fix up in FLASH
-
- PTR_L t8, 0(t3) # t8 <-- original pointer
- PTR_ADD t8, s1 # t8 <-- adjusted pointer
-
- PTR_ADD t3, s1 # t3 <-- location to fix up in RAM
- PTR_S t8, 0(t3)
-
-2:
- blt t1, t2, 1b
- PTR_ADDIU t1, 2 * PTRSIZE # each rel.dyn entry is 2*PTRSIZE bytes
-
- /*
- * Flush caches to ensure our newly modified instructions are visible
- * to the instruction cache. We're still running with the old GOT, so
- * apply the reloc offset to the start address.
- */
- PTR_LA a0, __text_start
- PTR_LA a1, __text_end
- PTR_SUB a1, a1, a0
- PTR_LA t9, flush_cache
- jalr t9
- PTR_ADD a0, s1
-
- PTR_ADD gp, s1 # adjust gp
-
- /*
- * Clear BSS
- *
- * GOT is now relocated. Thus __bss_start and __bss_end can be
- * accessed directly via $gp.
- */
- PTR_LA t1, __bss_start # t1 <-- __bss_start
- PTR_LA t2, __bss_end # t2 <-- __bss_end
-
-1:
- PTR_S zero, 0(t1)
- blt t1, t2, 1b
- PTR_ADDIU t1, PTRSIZE
-
- move a0, s0 # a0 <-- gd
- move a1, s2
- PTR_LA t9, board_init_r
- jr t9
- move ra, zero
-
- END(relocate_code)
diff --git a/arch/mips/cpu/u-boot.lds b/arch/mips/cpu/u-boot.lds
index 0129c99611..bd5536f013 100644
--- a/arch/mips/cpu/u-boot.lds
+++ b/arch/mips/cpu/u-boot.lds
@@ -34,15 +34,6 @@ SECTIONS
*(.data*)
}
- . = .;
- _gp = ALIGN(16) + 0x7ff0;
-
- .got : {
- *(.got)
- }
-
- num_got_entries = SIZEOF(.got) >> PTR_COUNT_SHIFT;
-
. = ALIGN(4);
.sdata : {
*(.sdata*)
@@ -57,33 +48,19 @@ SECTIONS
__image_copy_end = .;
__init_end = .;
- .rel.dyn : {
- __rel_dyn_start = .;
- *(.rel.dyn)
- __rel_dyn_end = .;
- }
-
- .padding : {
- /*
- * Workaround for a binutils feature (or bug?).
- *
- * The GNU ld from binutils puts the dynamic relocation
- * entries into the .rel.dyn section. Sometimes it
- * allocates more dynamic relocation entries than it needs
- * and the unused slots are set to R_MIPS_NONE entries.
- *
- * However the size of the .rel.dyn section in the ELF
- * section header does not cover the unused entries, so
- * objcopy removes those during stripping.
- *
- * Create a small section here to avoid that.
- */
- LONG(0xFFFFFFFF)
+ /*
+ * .rel must come last so that the mips-relocs tool can shrink
+ * the section size & the PT_LOAD program header filesz.
+ */
+ .rel : {
+ __rel_start = .;
+ BYTE(0x0)
+ . += (32 * 1024) - 1;
}
_end = .;
- .bss __rel_dyn_start (OVERLAY) : {
+ .bss __rel_start (OVERLAY) : {
__bss_start = .;
*(.sbss.*)
*(.bss.*)
diff --git a/arch/mips/include/asm/relocs.h b/arch/mips/include/asm/relocs.h
new file mode 100644
index 0000000000..92e9d04f7c
--- /dev/null
+++ b/arch/mips/include/asm/relocs.h
@@ -0,0 +1,24 @@
+/*
+ * MIPS Relocations
+ *
+ * Copyright (c) 2017 Imagination Technologies Ltd.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#ifndef __ASM_MIPS_RELOCS_H__
+#define __ASM_MIPS_RELOCS_H__
+
+#define R_MIPS_NONE 0
+#define R_MIPS_32 2
+#define R_MIPS_26 4
+#define R_MIPS_HI16 5
+#define R_MIPS_LO16 6
+#define R_MIPS_PC16 10
+#define R_MIPS_64 18
+#define R_MIPS_HIGHER 28
+#define R_MIPS_HIGHEST 29
+#define R_MIPS_PC21_S2 60
+#define R_MIPS_PC26_S2 61
+
+#endif /* __ASM_MIPS_RELOCS_H__ */
diff --git a/arch/mips/include/asm/sections.h b/arch/mips/include/asm/sections.h
index fc4640a392..b9d217999e 100644
--- a/arch/mips/include/asm/sections.h
+++ b/arch/mips/include/asm/sections.h
@@ -8,4 +8,11 @@
#include <asm-generic/sections.h>
+/**
+ * __rel_start: Relocation data generated by the mips-relocs tool
+ *
+ * See arch/mips/lib/reloc.c for details on the format & use of this data.
+ */
+extern uint8_t __rel_start[];
+
#endif
diff --git a/arch/mips/lib/Makefile b/arch/mips/lib/Makefile
index 659c6ad187..ef557c6932 100644
--- a/arch/mips/lib/Makefile
+++ b/arch/mips/lib/Makefile
@@ -8,6 +8,7 @@
obj-y += cache.o
obj-y += cache_init.o
obj-y += genex.o
+obj-y += reloc.o
obj-y += stack.o
obj-y += traps.o
diff --git a/arch/mips/lib/reloc.c b/arch/mips/lib/reloc.c
new file mode 100644
index 0000000000..d0c52c9674
--- /dev/null
+++ b/arch/mips/lib/reloc.c
@@ -0,0 +1,164 @@
+/*
+ * MIPS Relocation
+ *
+ * Copyright (c) 2017 Imagination Technologies Ltd.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * Relocation data, found in the .rel section, is generated by the mips-relocs
+ * tool & contains a record of all locations in the U-Boot binary that need to
+ * be fixed up during relocation.
+ *
+ * The data is a sequence of unsigned integers, which are of somewhat arbitrary
+ * size. This is achieved by encoding integers as a sequence of bytes, each of
+ * which contains 7 bits of data with the most significant bit indicating
+ * whether any further bytes need to be read. The least significant bits of the
+ * integer are found in the first byte - ie. it somewhat resembles little
+ * endian.
+ *
+ * Each pair of two integers represents a relocation that must be applied. The
+ * first integer represents the type of relocation as a standard ELF relocation
+ * type (ie. R_MIPS_*). The second integer represents the offset at which to
+ * apply the relocation, relative to the previous relocation or for the first
+ * relocation the start of the relocated .text section.
+ *
+ * The end of the relocation data is indicated when type R_MIPS_NONE (0) is
+ * read, at which point no further integers should be read. That is, the
+ * terminating R_MIPS_NONE reloc includes no offset.
+ */
+
+#include <common.h>
+#include <asm/relocs.h>
+#include <asm/sections.h>
+
+/**
+ * read_uint() - Read an unsigned integer from the buffer
+ * @buf: pointer to a pointer to the reloc buffer
+ *
+ * Read one whole unsigned integer from the relocation data pointed to by @buf,
+ * advancing @buf past the bytes encoding the integer.
+ *
+ * Returns: the integer read from @buf
+ */
+static unsigned long read_uint(uint8_t **buf)
+{
+ unsigned long val = 0;
+ unsigned int shift = 0;
+ uint8_t new;
+
+ do {
+ new = *(*buf)++;
+ val |= (new & 0x7f) << shift;
+ shift += 7;
+ } while (new & 0x80);
+
+ return val;
+}
+
+/**
+ * apply_reloc() - Apply a single relocation
+ * @type: the type of reloc (R_MIPS_*)
+ * @addr: the address that the reloc should be applied to
+ * @off: the relocation offset, ie. number of bytes we're moving U-Boot by
+ *
+ * Apply a single relocation of type @type at @addr. This function is
+ * intentionally simple, and does the bare minimum needed to fixup the
+ * relocated U-Boot - in particular, it does not check for overflows.
+ */
+static void apply_reloc(unsigned int type, void *addr, long off)
+{
+ uint32_t u32;
+
+ switch (type) {
+ case R_MIPS_26:
+ u32 = *(uint32_t *)addr;
+ u32 = (u32 & GENMASK(31, 26)) |
+ ((u32 + (off >> 2)) & GENMASK(25, 0));
+ *(uint32_t *)addr = u32;
+ break;
+
+ case R_MIPS_32:
+ *(uint32_t *)addr += off;
+ break;
+
+ case R_MIPS_64:
+ *(uint64_t *)addr += off;
+ break;
+
+ case R_MIPS_HI16:
+ *(uint32_t *)addr += off >> 16;
+ break;
+
+ default:
+ panic("Unhandled reloc type %u\n", type);
+ }
+}
+
+/**
+ * relocate_code() - Relocate U-Boot, generally from flash to DDR
+ * @start_addr_sp: new stack pointer
+ * @new_gd: pointer to relocated global data
+ * @relocaddr: the address to relocate to
+ *
+ * Relocate U-Boot from its current location (generally in flash) to a new one
+ * (generally in DDR). This function will copy the U-Boot binary & apply
+ * relocations as necessary, then jump to board_init_r in the new build of
+ * U-Boot. As such, this function does not return.
+ */
+void relocate_code(ulong start_addr_sp, gd_t *new_gd, ulong relocaddr)
+{
+ unsigned long addr, length, bss_len;
+ uint8_t *buf, *bss_start;
+ unsigned int type;
+ long off;
+
+ /*
+ * Ensure that we're relocating by an offset which is a multiple of
+ * 64KiB, ie. doesn't change the least significant 16 bits of any
+ * addresses. This allows us to discard R_MIPS_LO16 relocs, saving
+ * space in the U-Boot binary & complexity in handling them.
+ */
+ off = relocaddr - (unsigned long)__text_start;
+ if (off & 0xffff)
+ panic("Mis-aligned relocation\n");
+
+ /* Copy U-Boot to RAM */
+ length = __image_copy_end - __text_start;
+ memcpy((void *)relocaddr, __text_start, length);
+
+ /* Now apply relocations to the copy in RAM */
+ buf = __rel_start;
+ addr = relocaddr;
+ while (true) {
+ type = read_uint(&buf);
+ if (type == R_MIPS_NONE)
+ break;
+
+ addr += read_uint(&buf) << 2;
+ apply_reloc(type, (void *)addr, off);
+ }
+
+ /* Ensure the icache is coherent */
+ flush_cache(relocaddr, length);
+
+ /* Clear the .bss section */
+ bss_start = (uint8_t *)((unsigned long)__bss_start + off);
+ bss_len = (unsigned long)&__bss_end - (unsigned long)__bss_start;
+ memset(bss_start, 0, bss_len);
+
+ /* Jump to the relocated U-Boot */
+ asm volatile(
+ "move $29, %0\n"
+ " move $4, %1\n"
+ " move $5, %2\n"
+ " move $31, $0\n"
+ " jr %3"
+ : /* no outputs */
+ : "r"(start_addr_sp),
+ "r"(new_gd),
+ "r"(relocaddr),
+ "r"((unsigned long)board_init_r + off));
+
+ /* Since we jumped to the new U-Boot above, we won't get here */
+ unreachable();
+}