microblaze: Add support for run time relocation

Microblaze is using NEEDS_MANUAL_RELOC from the beginnging. This is causing
issues with function pointer arrays which need to be updated manually after
relocation. Building code with -fPIC and linking with -pic will remove this
limitation and there is no longer need to run manual update.

By default still old option is enabled but by disabling NEEDS_MANUAL_RELOC
code will be compiled for full relocation.

The patch does couple of things which are connected to each other.
- Define STATIC_RELA dependency to call relocate-rela to fill sections.
- REMAKE_ELF was already enabled but u-boot file can't be used because
  sections are empty. relocate-rela will fill them and output file is
  u-boot.elf which should be used.
- Add support for full relocation (u-boot.elf)
- Add support for early relocation when u-boot.bin is loaded to different
  address then CONFIG_SYS_TEXT_BASE
- Add rela.dyn and dynsym sections

Disabling NEEDS_MANUAL_RELOC U-Boot size increased by 10% of it's original
size (550kB to 608kB).

Signed-off-by: Michal Simek <michal.simek@amd.com>
Link: https://lore.kernel.org/r/a845670b34925859b2e321875f7588a29f6655f9.1655299267.git.michal.simek@amd.com
diff --git a/arch/microblaze/cpu/relocate.c b/arch/microblaze/cpu/relocate.c
new file mode 100644
index 0000000..b00d02b
--- /dev/null
+++ b/arch/microblaze/cpu/relocate.c
@@ -0,0 +1,111 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * (C) Copyright 2022 Advanced Micro Devices, Inc
+ * Michal Simek <michal.simek@amd.com>
+ */
+
+#include <common.h>
+#include <elf.h>
+
+#define R_MICROBLAZE_NONE	0
+#define R_MICROBLAZE_32		1
+#define R_MICROBLAZE_REL	16
+#define R_MICROBLAZE_GLOB_DAT	18
+
+/**
+ * mb_fix_rela - update relocation to new address
+ * @reloc_addr: new relocation address
+ * @verbose: enable version messages
+ * @rela_start: rela section start
+ * @rela_end: rela section end
+ * @dyn_start: dynamic section start
+ * @origin_addr: address where u-boot starts(doesn't need to be CONFIG_SYS_TEXT_BASE)
+ */
+void mb_fix_rela(u32 reloc_addr, u32 verbose, u32 rela_start,
+		 u32 rela_end, u32 dyn_start, u32 origin_addr)
+{
+	u32 num, type, mask, i, reloc_off;
+
+	/*
+	 * Return in case u-boot.elf is used directly.
+	 * Skip it when u-boot.bin is loaded to different address than
+	 * CONFIG_SYS_TEXT_BASE. In this case relocation is necessary to run.
+	 */
+	if (reloc_addr == CONFIG_SYS_TEXT_BASE) {
+		debug_cond(verbose,
+			   "Relocation address is the same - skip relocation\n");
+		return;
+	}
+
+	reloc_off = reloc_addr - origin_addr;
+
+	debug_cond(verbose, "Relocation address:\t0x%08x\n", reloc_addr);
+	debug_cond(verbose, "Relocation offset:\t0x%08x\n", reloc_off);
+	debug_cond(verbose, "Origin address:\t0x%08x\n", origin_addr);
+	debug_cond(verbose, "Rela start:\t0x%08x\n", rela_start);
+	debug_cond(verbose, "Rela end:\t0x%08x\n", rela_end);
+	debug_cond(verbose, "Dynsym start:\t0x%08x\n", dyn_start);
+
+	num = (rela_end - rela_start) / sizeof(Elf32_Rela);
+
+	debug_cond(verbose, "Number of entries:\t%u\n", num);
+
+	for (i = 0; i < num; i++) {
+		Elf32_Rela *rela;
+		u32 temp;
+
+		rela = (Elf32_Rela *)(rela_start + sizeof(Elf32_Rela) * i);
+
+		mask = 0xffULL; /* would be different on 32-bit */
+		type = rela->r_info & mask;
+
+		debug_cond(verbose, "\nRela possition:\t%d/0x%x\n",
+			   i, (u32)rela);
+
+		switch (type) {
+		case R_MICROBLAZE_REL:
+			temp = *(u32 *)rela->r_offset;
+
+			debug_cond(verbose, "Type:\tREL\n");
+			debug_cond(verbose, "Rela r_offset:\t\t0x%x\n", rela->r_offset);
+			debug_cond(verbose, "Rela r_info:\t\t0x%x\n", rela->r_info);
+			debug_cond(verbose, "Rela r_addend:\t\t0x%x\n", rela->r_addend);
+			debug_cond(verbose, "Value at r_offset:\t0x%x\n", temp);
+
+			rela->r_offset += reloc_off;
+			rela->r_addend += reloc_off;
+
+			temp = *(u32 *)rela->r_offset;
+			temp += reloc_off;
+			*(u32 *)rela->r_offset = temp;
+
+			debug_cond(verbose, "New:Rela r_offset:\t0x%x\n", rela->r_offset);
+			debug_cond(verbose, "New:Rela r_addend:\t0x%x\n", rela->r_addend);
+			debug_cond(verbose, "New:Value at r_offset:\t0x%x\n", temp);
+			break;
+		case R_MICROBLAZE_32:
+		case R_MICROBLAZE_GLOB_DAT:
+			debug_cond(verbose, "Type:\t(32/GLOB) %u\n", type);
+			debug_cond(verbose, "Rela r_offset:\t\t0x%x\n", rela->r_offset);
+			debug_cond(verbose, "Rela r_info:\t\t0x%x\n", rela->r_info);
+			debug_cond(verbose, "Rela r_addend:\t\t0x%x\n", rela->r_addend);
+			debug_cond(verbose, "Value at r_offset:\t0x%x\n", temp);
+
+			rela->r_offset += reloc_off;
+
+			temp = *(u32 *)rela->r_offset;
+			temp += reloc_off;
+			*(u32 *)rela->r_offset = temp;
+
+			debug_cond(verbose, "New:Rela r_offset:\t0x%x\n", rela->r_offset);
+			debug_cond(verbose, "New:Value at r_offset:\t0x%x\n", temp);
+			break;
+		case R_MICROBLAZE_NONE:
+			debug_cond(verbose, "R_MICROBLAZE_NONE - skip\n");
+			break;
+		default:
+			debug_cond(verbose, "warning: unsupported relocation type %d at %x\n",
+				   type, rela->r_offset);
+		}
+	}
+}