1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * (C) Copyright 2022 Advanced Micro Devices, Inc
4  * Michal Simek <michal.simek@amd.com>
5  */
6 
7 #include <elf.h>
8 #include <log.h>
9 #include <linux/types.h>
10 
11 #define R_MICROBLAZE_NONE	0
12 #define R_MICROBLAZE_32		1
13 #define R_MICROBLAZE_REL	16
14 #define R_MICROBLAZE_GLOB_DAT	18
15 
16 /**
17  * mb_fix_rela - update relocation to new address
18  * @reloc_addr: new relocation address
19  * @verbose: enable version messages
20  * @rela_start: rela section start
21  * @rela_end: rela section end
22  * @dyn_start: dynamic section start
23  * @origin_addr: address where u-boot starts(doesn't need to be CONFIG_TEXT_BASE)
24  */
mb_fix_rela(u32 reloc_addr,u32 verbose,u32 rela_start,u32 rela_end,u32 dyn_start,u32 origin_addr)25 void mb_fix_rela(u32 reloc_addr, u32 verbose, u32 rela_start,
26 		 u32 rela_end, u32 dyn_start, u32 origin_addr)
27 {
28 	u32 num, type, mask, i, reloc_off;
29 
30 	/*
31 	 * Return in case u-boot.elf is used directly.
32 	 * Skip it when u-boot.bin is loaded to different address than
33 	 * CONFIG_TEXT_BASE. In this case relocation is necessary to run.
34 	 */
35 	if (reloc_addr == CONFIG_TEXT_BASE) {
36 		debug_cond(verbose,
37 			   "Relocation address is the same - skip relocation\n");
38 		return;
39 	}
40 
41 	reloc_off = reloc_addr - origin_addr;
42 
43 	debug_cond(verbose, "Relocation address:\t0x%08x\n", reloc_addr);
44 	debug_cond(verbose, "Relocation offset:\t0x%08x\n", reloc_off);
45 	debug_cond(verbose, "Origin address:\t0x%08x\n", origin_addr);
46 	debug_cond(verbose, "Rela start:\t0x%08x\n", rela_start);
47 	debug_cond(verbose, "Rela end:\t0x%08x\n", rela_end);
48 	debug_cond(verbose, "Dynsym start:\t0x%08x\n", dyn_start);
49 
50 	num = (rela_end - rela_start) / sizeof(Elf32_Rela);
51 
52 	debug_cond(verbose, "Number of entries:\t%u\n", num);
53 
54 	for (i = 0; i < num; i++) {
55 		Elf32_Rela *rela;
56 		u32 temp;
57 
58 		rela = (Elf32_Rela *)(rela_start + sizeof(Elf32_Rela) * i);
59 
60 		mask = 0xffULL; /* would be different on 32-bit */
61 		type = rela->r_info & mask;
62 
63 		debug_cond(verbose, "\nRela possition:\t%d/0x%x\n",
64 			   i, (u32)rela);
65 
66 		switch (type) {
67 		case R_MICROBLAZE_REL:
68 			temp = *(u32 *)rela->r_offset;
69 
70 			debug_cond(verbose, "Type:\tREL\n");
71 			debug_cond(verbose, "Rela r_offset:\t\t0x%x\n", rela->r_offset);
72 			debug_cond(verbose, "Rela r_info:\t\t0x%x\n", rela->r_info);
73 			debug_cond(verbose, "Rela r_addend:\t\t0x%x\n", rela->r_addend);
74 			debug_cond(verbose, "Value at r_offset:\t0x%x\n", temp);
75 
76 			rela->r_offset += reloc_off;
77 			rela->r_addend += reloc_off;
78 
79 			temp = *(u32 *)rela->r_offset;
80 			temp += reloc_off;
81 			*(u32 *)rela->r_offset = temp;
82 
83 			debug_cond(verbose, "New:Rela r_offset:\t0x%x\n", rela->r_offset);
84 			debug_cond(verbose, "New:Rela r_addend:\t0x%x\n", rela->r_addend);
85 			debug_cond(verbose, "New:Value at r_offset:\t0x%x\n", temp);
86 			break;
87 		case R_MICROBLAZE_32:
88 		case R_MICROBLAZE_GLOB_DAT:
89 			debug_cond(verbose, "Type:\t(32/GLOB) %u\n", type);
90 			debug_cond(verbose, "Rela r_offset:\t\t0x%x\n", rela->r_offset);
91 			debug_cond(verbose, "Rela r_info:\t\t0x%x\n", rela->r_info);
92 			debug_cond(verbose, "Rela r_addend:\t\t0x%x\n", rela->r_addend);
93 			debug_cond(verbose, "Value at r_offset:\t0x%x\n", temp);
94 
95 			rela->r_offset += reloc_off;
96 
97 			temp = *(u32 *)rela->r_offset;
98 			temp += reloc_off;
99 			*(u32 *)rela->r_offset = temp;
100 
101 			debug_cond(verbose, "New:Rela r_offset:\t0x%x\n", rela->r_offset);
102 			debug_cond(verbose, "New:Value at r_offset:\t0x%x\n", temp);
103 			break;
104 		case R_MICROBLAZE_NONE:
105 			debug_cond(verbose, "R_MICROBLAZE_NONE - skip\n");
106 			break;
107 		default:
108 			debug_cond(verbose, "warning: unsupported relocation type %d at %x\n",
109 				   type, rela->r_offset);
110 		}
111 	}
112 }
113