xref: /openbmc/linux/arch/riscv/kernel/module.c (revision 8dde5715)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  *
4  *  Copyright (C) 2017 Zihao Yu
5  */
6 
7 #include <linux/elf.h>
8 #include <linux/err.h>
9 #include <linux/errno.h>
10 #include <linux/moduleloader.h>
11 
12 static int apply_r_riscv_32_rela(struct module *me, u32 *location, Elf_Addr v)
13 {
14 	if (v != (u32)v) {
15 		pr_err("%s: value %016llx out of range for 32-bit field\n",
16 		       me->name, (long long)v);
17 		return -EINVAL;
18 	}
19 	*location = v;
20 	return 0;
21 }
22 
23 static int apply_r_riscv_64_rela(struct module *me, u32 *location, Elf_Addr v)
24 {
25 	*(u64 *)location = v;
26 	return 0;
27 }
28 
29 static int apply_r_riscv_branch_rela(struct module *me, u32 *location,
30 				     Elf_Addr v)
31 {
32 	ptrdiff_t offset = (void *)v - (void *)location;
33 	u32 imm12 = (offset & 0x1000) << (31 - 12);
34 	u32 imm11 = (offset & 0x800) >> (11 - 7);
35 	u32 imm10_5 = (offset & 0x7e0) << (30 - 10);
36 	u32 imm4_1 = (offset & 0x1e) << (11 - 4);
37 
38 	*location = (*location & 0x1fff07f) | imm12 | imm11 | imm10_5 | imm4_1;
39 	return 0;
40 }
41 
42 static int apply_r_riscv_jal_rela(struct module *me, u32 *location,
43 				  Elf_Addr v)
44 {
45 	ptrdiff_t offset = (void *)v - (void *)location;
46 	u32 imm20 = (offset & 0x100000) << (31 - 20);
47 	u32 imm19_12 = (offset & 0xff000);
48 	u32 imm11 = (offset & 0x800) << (20 - 11);
49 	u32 imm10_1 = (offset & 0x7fe) << (30 - 10);
50 
51 	*location = (*location & 0xfff) | imm20 | imm19_12 | imm11 | imm10_1;
52 	return 0;
53 }
54 
55 static int apply_r_riscv_rcv_branch_rela(struct module *me, u32 *location,
56 					 Elf_Addr v)
57 {
58 	ptrdiff_t offset = (void *)v - (void *)location;
59 	u16 imm8 = (offset & 0x100) << (12 - 8);
60 	u16 imm7_6 = (offset & 0xc0) >> (6 - 5);
61 	u16 imm5 = (offset & 0x20) >> (5 - 2);
62 	u16 imm4_3 = (offset & 0x18) << (12 - 5);
63 	u16 imm2_1 = (offset & 0x6) << (12 - 10);
64 
65 	*(u16 *)location = (*(u16 *)location & 0xe383) |
66 		    imm8 | imm7_6 | imm5 | imm4_3 | imm2_1;
67 	return 0;
68 }
69 
70 static int apply_r_riscv_rvc_jump_rela(struct module *me, u32 *location,
71 				       Elf_Addr v)
72 {
73 	ptrdiff_t offset = (void *)v - (void *)location;
74 	u16 imm11 = (offset & 0x800) << (12 - 11);
75 	u16 imm10 = (offset & 0x400) >> (10 - 8);
76 	u16 imm9_8 = (offset & 0x300) << (12 - 11);
77 	u16 imm7 = (offset & 0x80) >> (7 - 6);
78 	u16 imm6 = (offset & 0x40) << (12 - 11);
79 	u16 imm5 = (offset & 0x20) >> (5 - 2);
80 	u16 imm4 = (offset & 0x10) << (12 - 5);
81 	u16 imm3_1 = (offset & 0xe) << (12 - 10);
82 
83 	*(u16 *)location = (*(u16 *)location & 0xe003) |
84 		    imm11 | imm10 | imm9_8 | imm7 | imm6 | imm5 | imm4 | imm3_1;
85 	return 0;
86 }
87 
88 static int apply_r_riscv_pcrel_hi20_rela(struct module *me, u32 *location,
89 					 Elf_Addr v)
90 {
91 	ptrdiff_t offset = (void *)v - (void *)location;
92 	s32 hi20;
93 
94 	if (offset != (s32)offset) {
95 		pr_err(
96 		  "%s: target %016llx can not be addressed by the 32-bit offset from PC = %p\n",
97 		  me->name, (long long)v, location);
98 		return -EINVAL;
99 	}
100 
101 	hi20 = (offset + 0x800) & 0xfffff000;
102 	*location = (*location & 0xfff) | hi20;
103 	return 0;
104 }
105 
106 static int apply_r_riscv_pcrel_lo12_i_rela(struct module *me, u32 *location,
107 					   Elf_Addr v)
108 {
109 	/*
110 	 * v is the lo12 value to fill. It is calculated before calling this
111 	 * handler.
112 	 */
113 	*location = (*location & 0xfffff) | ((v & 0xfff) << 20);
114 	return 0;
115 }
116 
117 static int apply_r_riscv_pcrel_lo12_s_rela(struct module *me, u32 *location,
118 					   Elf_Addr v)
119 {
120 	/*
121 	 * v is the lo12 value to fill. It is calculated before calling this
122 	 * handler.
123 	 */
124 	u32 imm11_5 = (v & 0xfe0) << (31 - 11);
125 	u32 imm4_0 = (v & 0x1f) << (11 - 4);
126 
127 	*location = (*location & 0x1fff07f) | imm11_5 | imm4_0;
128 	return 0;
129 }
130 
131 static int apply_r_riscv_hi20_rela(struct module *me, u32 *location,
132 				   Elf_Addr v)
133 {
134 	s32 hi20;
135 
136 	if (IS_ENABLED(CONFIG_CMODEL_MEDLOW)) {
137 		pr_err(
138 		  "%s: target %016llx can not be addressed by the 32-bit offset from PC = %p\n",
139 		  me->name, (long long)v, location);
140 		return -EINVAL;
141 	}
142 
143 	hi20 = ((s32)v + 0x800) & 0xfffff000;
144 	*location = (*location & 0xfff) | hi20;
145 	return 0;
146 }
147 
148 static int apply_r_riscv_lo12_i_rela(struct module *me, u32 *location,
149 				     Elf_Addr v)
150 {
151 	/* Skip medlow checking because of filtering by HI20 already */
152 	s32 hi20 = ((s32)v + 0x800) & 0xfffff000;
153 	s32 lo12 = ((s32)v - hi20);
154 	*location = (*location & 0xfffff) | ((lo12 & 0xfff) << 20);
155 	return 0;
156 }
157 
158 static int apply_r_riscv_lo12_s_rela(struct module *me, u32 *location,
159 				     Elf_Addr v)
160 {
161 	/* Skip medlow checking because of filtering by HI20 already */
162 	s32 hi20 = ((s32)v + 0x800) & 0xfffff000;
163 	s32 lo12 = ((s32)v - hi20);
164 	u32 imm11_5 = (lo12 & 0xfe0) << (31 - 11);
165 	u32 imm4_0 = (lo12 & 0x1f) << (11 - 4);
166 	*location = (*location & 0x1fff07f) | imm11_5 | imm4_0;
167 	return 0;
168 }
169 
170 static int apply_r_riscv_got_hi20_rela(struct module *me, u32 *location,
171 				       Elf_Addr v)
172 {
173 	ptrdiff_t offset = (void *)v - (void *)location;
174 	s32 hi20;
175 
176 	/* Always emit the got entry */
177 	if (IS_ENABLED(CONFIG_MODULE_SECTIONS)) {
178 		offset = module_emit_got_entry(me, v);
179 		offset = (void *)offset - (void *)location;
180 	} else {
181 		pr_err(
182 		  "%s: can not generate the GOT entry for symbol = %016llx from PC = %p\n",
183 		  me->name, (long long)v, location);
184 		return -EINVAL;
185 	}
186 
187 	hi20 = (offset + 0x800) & 0xfffff000;
188 	*location = (*location & 0xfff) | hi20;
189 	return 0;
190 }
191 
192 static int apply_r_riscv_call_plt_rela(struct module *me, u32 *location,
193 				       Elf_Addr v)
194 {
195 	ptrdiff_t offset = (void *)v - (void *)location;
196 	s32 fill_v = offset;
197 	u32 hi20, lo12;
198 
199 	if (offset != fill_v) {
200 		/* Only emit the plt entry if offset over 32-bit range */
201 		if (IS_ENABLED(CONFIG_MODULE_SECTIONS)) {
202 			offset = module_emit_plt_entry(me, v);
203 			offset = (void *)offset - (void *)location;
204 		} else {
205 			pr_err(
206 			  "%s: target %016llx can not be addressed by the 32-bit offset from PC = %p\n",
207 			  me->name, (long long)v, location);
208 			return -EINVAL;
209 		}
210 	}
211 
212 	hi20 = (offset + 0x800) & 0xfffff000;
213 	lo12 = (offset - hi20) & 0xfff;
214 	*location = (*location & 0xfff) | hi20;
215 	*(location + 1) = (*(location + 1) & 0xfffff) | (lo12 << 20);
216 	return 0;
217 }
218 
219 static int apply_r_riscv_call_rela(struct module *me, u32 *location,
220 				   Elf_Addr v)
221 {
222 	ptrdiff_t offset = (void *)v - (void *)location;
223 	s32 fill_v = offset;
224 	u32 hi20, lo12;
225 
226 	if (offset != fill_v) {
227 		pr_err(
228 		  "%s: target %016llx can not be addressed by the 32-bit offset from PC = %p\n",
229 		  me->name, (long long)v, location);
230 		return -EINVAL;
231 	}
232 
233 	hi20 = (offset + 0x800) & 0xfffff000;
234 	lo12 = (offset - hi20) & 0xfff;
235 	*location = (*location & 0xfff) | hi20;
236 	*(location + 1) = (*(location + 1) & 0xfffff) | (lo12 << 20);
237 	return 0;
238 }
239 
240 static int apply_r_riscv_relax_rela(struct module *me, u32 *location,
241 				    Elf_Addr v)
242 {
243 	return 0;
244 }
245 
246 static int apply_r_riscv_align_rela(struct module *me, u32 *location,
247 				    Elf_Addr v)
248 {
249 	pr_err(
250 	  "%s: The unexpected relocation type 'R_RISCV_ALIGN' from PC = %p\n",
251 	  me->name, location);
252 	return -EINVAL;
253 }
254 
255 static int apply_r_riscv_add32_rela(struct module *me, u32 *location,
256 				    Elf_Addr v)
257 {
258 	*(u32 *)location += (u32)v;
259 	return 0;
260 }
261 
262 static int apply_r_riscv_sub32_rela(struct module *me, u32 *location,
263 				    Elf_Addr v)
264 {
265 	*(u32 *)location -= (u32)v;
266 	return 0;
267 }
268 
269 static int (*reloc_handlers_rela[]) (struct module *me, u32 *location,
270 				Elf_Addr v) = {
271 	[R_RISCV_32]			= apply_r_riscv_32_rela,
272 	[R_RISCV_64]			= apply_r_riscv_64_rela,
273 	[R_RISCV_BRANCH]		= apply_r_riscv_branch_rela,
274 	[R_RISCV_JAL]			= apply_r_riscv_jal_rela,
275 	[R_RISCV_RVC_BRANCH]		= apply_r_riscv_rcv_branch_rela,
276 	[R_RISCV_RVC_JUMP]		= apply_r_riscv_rvc_jump_rela,
277 	[R_RISCV_PCREL_HI20]		= apply_r_riscv_pcrel_hi20_rela,
278 	[R_RISCV_PCREL_LO12_I]		= apply_r_riscv_pcrel_lo12_i_rela,
279 	[R_RISCV_PCREL_LO12_S]		= apply_r_riscv_pcrel_lo12_s_rela,
280 	[R_RISCV_HI20]			= apply_r_riscv_hi20_rela,
281 	[R_RISCV_LO12_I]		= apply_r_riscv_lo12_i_rela,
282 	[R_RISCV_LO12_S]		= apply_r_riscv_lo12_s_rela,
283 	[R_RISCV_GOT_HI20]		= apply_r_riscv_got_hi20_rela,
284 	[R_RISCV_CALL_PLT]		= apply_r_riscv_call_plt_rela,
285 	[R_RISCV_CALL]			= apply_r_riscv_call_rela,
286 	[R_RISCV_RELAX]			= apply_r_riscv_relax_rela,
287 	[R_RISCV_ALIGN]			= apply_r_riscv_align_rela,
288 	[R_RISCV_ADD32]			= apply_r_riscv_add32_rela,
289 	[R_RISCV_SUB32]			= apply_r_riscv_sub32_rela,
290 };
291 
292 int apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab,
293 		       unsigned int symindex, unsigned int relsec,
294 		       struct module *me)
295 {
296 	Elf_Rela *rel = (void *) sechdrs[relsec].sh_addr;
297 	int (*handler)(struct module *me, u32 *location, Elf_Addr v);
298 	Elf_Sym *sym;
299 	u32 *location;
300 	unsigned int i, type;
301 	Elf_Addr v;
302 	int res;
303 
304 	pr_debug("Applying relocate section %u to %u\n", relsec,
305 	       sechdrs[relsec].sh_info);
306 
307 	for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
308 		/* This is where to make the change */
309 		location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
310 			+ rel[i].r_offset;
311 		/* This is the symbol it is referring to */
312 		sym = (Elf_Sym *)sechdrs[symindex].sh_addr
313 			+ ELF_RISCV_R_SYM(rel[i].r_info);
314 		if (IS_ERR_VALUE(sym->st_value)) {
315 			/* Ignore unresolved weak symbol */
316 			if (ELF_ST_BIND(sym->st_info) == STB_WEAK)
317 				continue;
318 			pr_warning("%s: Unknown symbol %s\n",
319 				   me->name, strtab + sym->st_name);
320 			return -ENOENT;
321 		}
322 
323 		type = ELF_RISCV_R_TYPE(rel[i].r_info);
324 
325 		if (type < ARRAY_SIZE(reloc_handlers_rela))
326 			handler = reloc_handlers_rela[type];
327 		else
328 			handler = NULL;
329 
330 		if (!handler) {
331 			pr_err("%s: Unknown relocation type %u\n",
332 			       me->name, type);
333 			return -EINVAL;
334 		}
335 
336 		v = sym->st_value + rel[i].r_addend;
337 
338 		if (type == R_RISCV_PCREL_LO12_I || type == R_RISCV_PCREL_LO12_S) {
339 			unsigned int j;
340 
341 			for (j = 0; j < sechdrs[relsec].sh_size / sizeof(*rel); j++) {
342 				unsigned long hi20_loc =
343 					sechdrs[sechdrs[relsec].sh_info].sh_addr
344 					+ rel[j].r_offset;
345 				u32 hi20_type = ELF_RISCV_R_TYPE(rel[j].r_info);
346 
347 				/* Find the corresponding HI20 relocation entry */
348 				if (hi20_loc == sym->st_value
349 				    && (hi20_type == R_RISCV_PCREL_HI20
350 					|| hi20_type == R_RISCV_GOT_HI20)) {
351 					s32 hi20, lo12;
352 					Elf_Sym *hi20_sym =
353 						(Elf_Sym *)sechdrs[symindex].sh_addr
354 						+ ELF_RISCV_R_SYM(rel[j].r_info);
355 					unsigned long hi20_sym_val =
356 						hi20_sym->st_value
357 						+ rel[j].r_addend;
358 
359 					/* Calculate lo12 */
360 					size_t offset = hi20_sym_val - hi20_loc;
361 					if (IS_ENABLED(CONFIG_MODULE_SECTIONS)
362 					    && hi20_type == R_RISCV_GOT_HI20) {
363 						offset = module_emit_got_entry(
364 							 me, hi20_sym_val);
365 						offset = offset - hi20_loc;
366 					}
367 					hi20 = (offset + 0x800) & 0xfffff000;
368 					lo12 = offset - hi20;
369 					v = lo12;
370 
371 					break;
372 				}
373 			}
374 			if (j == sechdrs[relsec].sh_size / sizeof(*rel)) {
375 				pr_err(
376 				  "%s: Can not find HI20 relocation information\n",
377 				  me->name);
378 				return -EINVAL;
379 			}
380 		}
381 
382 		res = handler(me, location, v);
383 		if (res)
384 			return res;
385 	}
386 
387 	return 0;
388 }
389