Linux Audio

Check our new training course

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