18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci* Copyright (C) 2020 Loongson Technology Corporation Limited
48c2ecf20Sopenharmony_ci*
58c2ecf20Sopenharmony_ci* Author: Hanlu Li <lihanlu@loongson.cn>
68c2ecf20Sopenharmony_ci* Author: Huacai Chen <chenhuacai@loongson.cn>
78c2ecf20Sopenharmony_ci*/
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "kmod: " fmt
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/moduleloader.h>
128c2ecf20Sopenharmony_ci#include <linux/elf.h>
138c2ecf20Sopenharmony_ci#include <linux/ftrace.h>
148c2ecf20Sopenharmony_ci#include <linux/mm.h>
158c2ecf20Sopenharmony_ci#include <linux/numa.h>
168c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
178c2ecf20Sopenharmony_ci#include <linux/slab.h>
188c2ecf20Sopenharmony_ci#include <linux/fs.h>
198c2ecf20Sopenharmony_ci#include <linux/string.h>
208c2ecf20Sopenharmony_ci#include <linux/kernel.h>
218c2ecf20Sopenharmony_ci#include <asm/alternative.h>
228c2ecf20Sopenharmony_ci#include <asm/inst.h>
238c2ecf20Sopenharmony_ci#include <asm/unwind.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistatic inline bool signed_imm_check(long val, unsigned int bit)
268c2ecf20Sopenharmony_ci{
278c2ecf20Sopenharmony_ci	return -(1L << (bit - 1)) <= val && val < (1L << (bit - 1));
288c2ecf20Sopenharmony_ci}
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic inline bool unsigned_imm_check(unsigned long val, unsigned int bit)
318c2ecf20Sopenharmony_ci{
328c2ecf20Sopenharmony_ci	return val < (1UL << bit);
338c2ecf20Sopenharmony_ci}
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic int rela_stack_push(s64 stack_value, s64 *rela_stack, size_t *rela_stack_top)
368c2ecf20Sopenharmony_ci{
378c2ecf20Sopenharmony_ci	if (RELA_STACK_DEPTH <= *rela_stack_top)
388c2ecf20Sopenharmony_ci		return -ENOEXEC;
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	rela_stack[(*rela_stack_top)++] = stack_value;
418c2ecf20Sopenharmony_ci	pr_debug("%s stack_value = 0x%llx\n", __func__, stack_value);
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	return 0;
448c2ecf20Sopenharmony_ci}
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistatic int rela_stack_pop(s64 *stack_value, s64 *rela_stack, size_t *rela_stack_top)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	if (*rela_stack_top == 0)
498c2ecf20Sopenharmony_ci		return -ENOEXEC;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	*stack_value = rela_stack[--(*rela_stack_top)];
528c2ecf20Sopenharmony_ci	pr_debug("%s stack_value = 0x%llx\n", __func__, *stack_value);
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	return 0;
558c2ecf20Sopenharmony_ci}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic int apply_r_larch_none(struct module *mod, u32 *location, Elf_Addr v,
588c2ecf20Sopenharmony_ci			s64 *rela_stack, size_t *rela_stack_top, unsigned int type)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	return 0;
618c2ecf20Sopenharmony_ci}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_cistatic int apply_r_larch_error(struct module *me, u32 *location, Elf_Addr v,
648c2ecf20Sopenharmony_ci			s64 *rela_stack, size_t *rela_stack_top, unsigned int type)
658c2ecf20Sopenharmony_ci{
668c2ecf20Sopenharmony_ci	pr_err("%s: Unsupport relocation type %u, please add its support.\n", me->name, type);
678c2ecf20Sopenharmony_ci	return -EINVAL;
688c2ecf20Sopenharmony_ci}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_cistatic int apply_r_larch_32(struct module *mod, u32 *location, Elf_Addr v,
718c2ecf20Sopenharmony_ci			s64 *rela_stack, size_t *rela_stack_top, unsigned int type)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	*location = v;
748c2ecf20Sopenharmony_ci	return 0;
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistatic int apply_r_larch_64(struct module *mod, u32 *location, Elf_Addr v,
788c2ecf20Sopenharmony_ci			s64 *rela_stack, size_t *rela_stack_top, unsigned int type)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	*(Elf_Addr *)location = v;
818c2ecf20Sopenharmony_ci	return 0;
828c2ecf20Sopenharmony_ci}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_cistatic int apply_r_larch_sop_push_pcrel(struct module *mod, u32 *location, Elf_Addr v,
858c2ecf20Sopenharmony_ci			s64 *rela_stack, size_t *rela_stack_top, unsigned int type)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	return rela_stack_push(v - (u64)location, rela_stack, rela_stack_top);
888c2ecf20Sopenharmony_ci}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistatic int apply_r_larch_sop_push_absolute(struct module *mod, u32 *location, Elf_Addr v,
918c2ecf20Sopenharmony_ci			s64 *rela_stack, size_t *rela_stack_top, unsigned int type)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	return rela_stack_push(v, rela_stack, rela_stack_top);
948c2ecf20Sopenharmony_ci}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cistatic int apply_r_larch_sop_push_dup(struct module *mod, u32 *location, Elf_Addr v,
978c2ecf20Sopenharmony_ci			s64 *rela_stack, size_t *rela_stack_top, unsigned int type)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	int err = 0;
1008c2ecf20Sopenharmony_ci	s64 opr1;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	err = rela_stack_pop(&opr1, rela_stack, rela_stack_top);
1038c2ecf20Sopenharmony_ci	if (err)
1048c2ecf20Sopenharmony_ci		return err;
1058c2ecf20Sopenharmony_ci	err = rela_stack_push(opr1, rela_stack, rela_stack_top);
1068c2ecf20Sopenharmony_ci	if (err)
1078c2ecf20Sopenharmony_ci		return err;
1088c2ecf20Sopenharmony_ci	err = rela_stack_push(opr1, rela_stack, rela_stack_top);
1098c2ecf20Sopenharmony_ci	if (err)
1108c2ecf20Sopenharmony_ci		return err;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	return 0;
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cistatic int apply_r_larch_sop_push_plt_pcrel(struct module *mod,
1168c2ecf20Sopenharmony_ci			Elf_Shdr *sechdrs, u32 *location, Elf_Addr v,
1178c2ecf20Sopenharmony_ci			s64 *rela_stack, size_t *rela_stack_top, unsigned int type)
1188c2ecf20Sopenharmony_ci{
1198c2ecf20Sopenharmony_ci	ptrdiff_t offset = (void *)v - (void *)location;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	if (offset >= SZ_128M)
1228c2ecf20Sopenharmony_ci		v = module_emit_plt_entry(mod, sechdrs, v);
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	if (offset < -SZ_128M)
1258c2ecf20Sopenharmony_ci		v = module_emit_plt_entry(mod, sechdrs, v);
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	return apply_r_larch_sop_push_pcrel(mod, location, v, rela_stack, rela_stack_top, type);
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cistatic int apply_r_larch_sop(struct module *mod, u32 *location, Elf_Addr v,
1318c2ecf20Sopenharmony_ci			s64 *rela_stack, size_t *rela_stack_top, unsigned int type)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	int err = 0;
1348c2ecf20Sopenharmony_ci	s64 opr1, opr2, opr3;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	if (type == R_LARCH_SOP_IF_ELSE) {
1378c2ecf20Sopenharmony_ci		err = rela_stack_pop(&opr3, rela_stack, rela_stack_top);
1388c2ecf20Sopenharmony_ci		if (err)
1398c2ecf20Sopenharmony_ci			return err;
1408c2ecf20Sopenharmony_ci	}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	err = rela_stack_pop(&opr2, rela_stack, rela_stack_top);
1438c2ecf20Sopenharmony_ci	if (err)
1448c2ecf20Sopenharmony_ci		return err;
1458c2ecf20Sopenharmony_ci	err = rela_stack_pop(&opr1, rela_stack, rela_stack_top);
1468c2ecf20Sopenharmony_ci	if (err)
1478c2ecf20Sopenharmony_ci		return err;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	switch (type) {
1508c2ecf20Sopenharmony_ci	case R_LARCH_SOP_AND:
1518c2ecf20Sopenharmony_ci		err = rela_stack_push(opr1 & opr2, rela_stack, rela_stack_top);
1528c2ecf20Sopenharmony_ci		break;
1538c2ecf20Sopenharmony_ci	case R_LARCH_SOP_ADD:
1548c2ecf20Sopenharmony_ci		err = rela_stack_push(opr1 + opr2, rela_stack, rela_stack_top);
1558c2ecf20Sopenharmony_ci		break;
1568c2ecf20Sopenharmony_ci	case R_LARCH_SOP_SUB:
1578c2ecf20Sopenharmony_ci		err = rela_stack_push(opr1 - opr2, rela_stack, rela_stack_top);
1588c2ecf20Sopenharmony_ci		break;
1598c2ecf20Sopenharmony_ci	case R_LARCH_SOP_SL:
1608c2ecf20Sopenharmony_ci		err = rela_stack_push(opr1 << opr2, rela_stack, rela_stack_top);
1618c2ecf20Sopenharmony_ci		break;
1628c2ecf20Sopenharmony_ci	case R_LARCH_SOP_SR:
1638c2ecf20Sopenharmony_ci		err = rela_stack_push(opr1 >> opr2, rela_stack, rela_stack_top);
1648c2ecf20Sopenharmony_ci		break;
1658c2ecf20Sopenharmony_ci	case R_LARCH_SOP_IF_ELSE:
1668c2ecf20Sopenharmony_ci		err = rela_stack_push(opr1 ? opr2 : opr3, rela_stack, rela_stack_top);
1678c2ecf20Sopenharmony_ci		break;
1688c2ecf20Sopenharmony_ci	default:
1698c2ecf20Sopenharmony_ci		pr_err("%s: Unsupport relocation type %u\n", mod->name, type);
1708c2ecf20Sopenharmony_ci		return -EINVAL;
1718c2ecf20Sopenharmony_ci	}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	return err;
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_cistatic int apply_r_larch_sop_imm_field(struct module *mod, u32 *location, Elf_Addr v,
1778c2ecf20Sopenharmony_ci			s64 *rela_stack, size_t *rela_stack_top, unsigned int type)
1788c2ecf20Sopenharmony_ci{
1798c2ecf20Sopenharmony_ci	int err = 0;
1808c2ecf20Sopenharmony_ci	s64 opr1;
1818c2ecf20Sopenharmony_ci	union loongarch_instruction *insn = (union loongarch_instruction *)location;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	err = rela_stack_pop(&opr1, rela_stack, rela_stack_top);
1848c2ecf20Sopenharmony_ci	if (err)
1858c2ecf20Sopenharmony_ci		return err;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	switch (type) {
1888c2ecf20Sopenharmony_ci	case R_LARCH_SOP_POP_32_U_10_12:
1898c2ecf20Sopenharmony_ci		if (!unsigned_imm_check(opr1, 12))
1908c2ecf20Sopenharmony_ci			goto overflow;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci		/* (*(uint32_t *) PC) [21 ... 10] = opr [11 ... 0] */
1938c2ecf20Sopenharmony_ci		insn->reg2ui12_format.simmediate = opr1 & 0xfff;
1948c2ecf20Sopenharmony_ci		return 0;
1958c2ecf20Sopenharmony_ci	case R_LARCH_SOP_POP_32_S_10_12:
1968c2ecf20Sopenharmony_ci		if (!signed_imm_check(opr1, 12))
1978c2ecf20Sopenharmony_ci			goto overflow;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci		insn->reg2i12_format.simmediate = opr1 & 0xfff;
2008c2ecf20Sopenharmony_ci		return 0;
2018c2ecf20Sopenharmony_ci	case R_LARCH_SOP_POP_32_S_10_16:
2028c2ecf20Sopenharmony_ci		if (!signed_imm_check(opr1, 16))
2038c2ecf20Sopenharmony_ci			goto overflow;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci		insn->reg2i16_format.simmediate = opr1 & 0xffff;
2068c2ecf20Sopenharmony_ci		return 0;
2078c2ecf20Sopenharmony_ci	case R_LARCH_SOP_POP_32_S_10_16_S2:
2088c2ecf20Sopenharmony_ci		if (opr1 % 4)
2098c2ecf20Sopenharmony_ci			goto unaligned;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci		if (!signed_imm_check(opr1, 18))
2128c2ecf20Sopenharmony_ci			goto overflow;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci		insn->reg2i16_format.simmediate = (opr1 >> 2) & 0xffff;
2158c2ecf20Sopenharmony_ci		return 0;
2168c2ecf20Sopenharmony_ci	case R_LARCH_SOP_POP_32_S_5_20:
2178c2ecf20Sopenharmony_ci		if (!signed_imm_check(opr1, 20))
2188c2ecf20Sopenharmony_ci			goto overflow;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci		insn->reg1i20_format.simmediate = (opr1) & 0xfffff;
2218c2ecf20Sopenharmony_ci		return 0;
2228c2ecf20Sopenharmony_ci	case R_LARCH_SOP_POP_32_S_0_5_10_16_S2:
2238c2ecf20Sopenharmony_ci		if (opr1 % 4)
2248c2ecf20Sopenharmony_ci			goto unaligned;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci		if (!signed_imm_check(opr1, 23))
2278c2ecf20Sopenharmony_ci			goto overflow;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci		opr1 >>= 2;
2308c2ecf20Sopenharmony_ci		insn->reg1i21_format.simmediate_l = opr1 & 0xffff;
2318c2ecf20Sopenharmony_ci		insn->reg1i21_format.simmediate_h = (opr1 >> 16) & 0x1f;
2328c2ecf20Sopenharmony_ci		return 0;
2338c2ecf20Sopenharmony_ci	case R_LARCH_SOP_POP_32_S_0_10_10_16_S2:
2348c2ecf20Sopenharmony_ci		if (opr1 % 4)
2358c2ecf20Sopenharmony_ci			goto unaligned;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci		if (!signed_imm_check(opr1, 28))
2388c2ecf20Sopenharmony_ci			goto overflow;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci		opr1 >>= 2;
2418c2ecf20Sopenharmony_ci		insn->reg0i26_format.simmediate_l = opr1 & 0xffff;
2428c2ecf20Sopenharmony_ci		insn->reg0i26_format.simmediate_h = (opr1 >> 16) & 0x3ff;
2438c2ecf20Sopenharmony_ci		return 0;
2448c2ecf20Sopenharmony_ci	case R_LARCH_SOP_POP_32_U:
2458c2ecf20Sopenharmony_ci		if (!unsigned_imm_check(opr1, 32))
2468c2ecf20Sopenharmony_ci			goto overflow;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci		/* (*(uint32_t *) PC) = opr */
2498c2ecf20Sopenharmony_ci		*location = (u32)opr1;
2508c2ecf20Sopenharmony_ci		return 0;
2518c2ecf20Sopenharmony_ci	default:
2528c2ecf20Sopenharmony_ci		pr_err("%s: Unsupport relocation type %u\n", mod->name, type);
2538c2ecf20Sopenharmony_ci		return -EINVAL;
2548c2ecf20Sopenharmony_ci	}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_cioverflow:
2578c2ecf20Sopenharmony_ci	pr_err("module %s: opr1 = 0x%llx overflow! dangerous %s (%u) relocation\n",
2588c2ecf20Sopenharmony_ci		mod->name, opr1, __func__, type);
2598c2ecf20Sopenharmony_ci	return -ENOEXEC;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ciunaligned:
2628c2ecf20Sopenharmony_ci	pr_err("module %s: opr1 = 0x%llx unaligned! dangerous %s (%u) relocation\n",
2638c2ecf20Sopenharmony_ci		mod->name, opr1, __func__, type);
2648c2ecf20Sopenharmony_ci	return -ENOEXEC;
2658c2ecf20Sopenharmony_ci}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_cistatic int apply_r_larch_add_sub(struct module *mod, u32 *location, Elf_Addr v,
2688c2ecf20Sopenharmony_ci			s64 *rela_stack, size_t *rela_stack_top, unsigned int type)
2698c2ecf20Sopenharmony_ci{
2708c2ecf20Sopenharmony_ci	switch (type) {
2718c2ecf20Sopenharmony_ci	case R_LARCH_ADD32:
2728c2ecf20Sopenharmony_ci		*(s32 *)location += v;
2738c2ecf20Sopenharmony_ci		return 0;
2748c2ecf20Sopenharmony_ci	case R_LARCH_ADD64:
2758c2ecf20Sopenharmony_ci		*(s64 *)location += v;
2768c2ecf20Sopenharmony_ci		return 0;
2778c2ecf20Sopenharmony_ci	case R_LARCH_SUB32:
2788c2ecf20Sopenharmony_ci		*(s32 *)location -= v;
2798c2ecf20Sopenharmony_ci		return 0;
2808c2ecf20Sopenharmony_ci	case R_LARCH_SUB64:
2818c2ecf20Sopenharmony_ci		*(s64 *)location -= v;
2828c2ecf20Sopenharmony_ci		return 0;
2838c2ecf20Sopenharmony_ci	default:
2848c2ecf20Sopenharmony_ci		pr_err("%s: Unsupport relocation type %u\n", mod->name, type);
2858c2ecf20Sopenharmony_ci		return -EINVAL;
2868c2ecf20Sopenharmony_ci	}
2878c2ecf20Sopenharmony_ci}
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_cistatic int apply_r_larch_b26(struct module *mod,
2908c2ecf20Sopenharmony_ci			Elf_Shdr *sechdrs, u32 *location, Elf_Addr v,
2918c2ecf20Sopenharmony_ci			s64 *rela_stack, size_t *rela_stack_top, unsigned int type)
2928c2ecf20Sopenharmony_ci{
2938c2ecf20Sopenharmony_ci	ptrdiff_t offset = (void *)v - (void *)location;
2948c2ecf20Sopenharmony_ci	union loongarch_instruction *insn = (union loongarch_instruction *)location;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	if (offset >= SZ_128M)
2978c2ecf20Sopenharmony_ci		v = module_emit_plt_entry(mod, sechdrs, v);
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	if (offset < -SZ_128M)
3008c2ecf20Sopenharmony_ci		v = module_emit_plt_entry(mod, sechdrs, v);
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	offset = (void *)v - (void *)location;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	if (offset & 3) {
3058c2ecf20Sopenharmony_ci		pr_err("module %s: jump offset = 0x%llx unaligned! dangerous R_LARCH_B26 (%u) relocation\n",
3068c2ecf20Sopenharmony_ci				mod->name, (long long)offset, type);
3078c2ecf20Sopenharmony_ci		return -ENOEXEC;
3088c2ecf20Sopenharmony_ci	}
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	if (!signed_imm_check(offset, 28)) {
3118c2ecf20Sopenharmony_ci		pr_err("module %s: jump offset = 0x%llx overflow! dangerous R_LARCH_B26 (%u) relocation\n",
3128c2ecf20Sopenharmony_ci				mod->name, (long long)offset, type);
3138c2ecf20Sopenharmony_ci		return -ENOEXEC;
3148c2ecf20Sopenharmony_ci	}
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	offset >>= 2;
3178c2ecf20Sopenharmony_ci	insn->reg0i26_format.simmediate_l = offset & 0xffff;
3188c2ecf20Sopenharmony_ci	insn->reg0i26_format.simmediate_h = (offset >> 16) & 0x3ff;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	return 0;
3218c2ecf20Sopenharmony_ci}
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_cistatic int apply_r_larch_pcala(struct module *mod, u32 *location, Elf_Addr v,
3248c2ecf20Sopenharmony_ci			s64 *rela_stack, size_t *rela_stack_top, unsigned int type)
3258c2ecf20Sopenharmony_ci{
3268c2ecf20Sopenharmony_ci	union loongarch_instruction *insn = (union loongarch_instruction *)location;
3278c2ecf20Sopenharmony_ci	/* Use s32 for a sign-extension deliberately. */
3288c2ecf20Sopenharmony_ci	s32 offset_hi20 = (void *)((v + 0x800) & ~0xfff) -
3298c2ecf20Sopenharmony_ci			  (void *)((Elf_Addr)location & ~0xfff);
3308c2ecf20Sopenharmony_ci	Elf_Addr anchor = (((Elf_Addr)location) & ~0xfff) + offset_hi20;
3318c2ecf20Sopenharmony_ci	ptrdiff_t offset_rem = (void *)v - (void *)anchor;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	switch (type) {
3348c2ecf20Sopenharmony_ci	case R_LARCH_PCALA_LO12:
3358c2ecf20Sopenharmony_ci		insn->reg2i12_format.simmediate = v & 0xfff;
3368c2ecf20Sopenharmony_ci		break;
3378c2ecf20Sopenharmony_ci	case R_LARCH_PCALA_HI20:
3388c2ecf20Sopenharmony_ci		v = offset_hi20 >> 12;
3398c2ecf20Sopenharmony_ci		insn->reg1i20_format.simmediate = v & 0xfffff;
3408c2ecf20Sopenharmony_ci		break;
3418c2ecf20Sopenharmony_ci	case R_LARCH_PCALA64_LO20:
3428c2ecf20Sopenharmony_ci		v = offset_rem >> 32;
3438c2ecf20Sopenharmony_ci		insn->reg1i20_format.simmediate = v & 0xfffff;
3448c2ecf20Sopenharmony_ci		break;
3458c2ecf20Sopenharmony_ci	case R_LARCH_PCALA64_HI12:
3468c2ecf20Sopenharmony_ci		v = offset_rem >> 52;
3478c2ecf20Sopenharmony_ci		insn->reg2i12_format.simmediate = v & 0xfff;
3488c2ecf20Sopenharmony_ci		break;
3498c2ecf20Sopenharmony_ci	default:
3508c2ecf20Sopenharmony_ci		pr_err("%s: Unsupport relocation type %u\n", mod->name, type);
3518c2ecf20Sopenharmony_ci		return -EINVAL;
3528c2ecf20Sopenharmony_ci	}
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	return 0;
3558c2ecf20Sopenharmony_ci}
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_cistatic int apply_r_larch_got_pc(struct module *mod,
3588c2ecf20Sopenharmony_ci			Elf_Shdr *sechdrs, u32 *location, Elf_Addr v,
3598c2ecf20Sopenharmony_ci			s64 *rela_stack, size_t *rela_stack_top, unsigned int type)
3608c2ecf20Sopenharmony_ci{
3618c2ecf20Sopenharmony_ci	Elf_Addr got = module_emit_got_entry(mod, sechdrs, v);
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	if (!got)
3648c2ecf20Sopenharmony_ci		return -EINVAL;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	switch (type) {
3678c2ecf20Sopenharmony_ci	case R_LARCH_GOT_PC_LO12:
3688c2ecf20Sopenharmony_ci		type = R_LARCH_PCALA_LO12;
3698c2ecf20Sopenharmony_ci		break;
3708c2ecf20Sopenharmony_ci	case R_LARCH_GOT_PC_HI20:
3718c2ecf20Sopenharmony_ci		type = R_LARCH_PCALA_HI20;
3728c2ecf20Sopenharmony_ci		break;
3738c2ecf20Sopenharmony_ci	default:
3748c2ecf20Sopenharmony_ci		pr_err("%s: Unsupport relocation type %u\n", mod->name, type);
3758c2ecf20Sopenharmony_ci		return -EINVAL;
3768c2ecf20Sopenharmony_ci	}
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	return apply_r_larch_pcala(mod, location, got, rela_stack, rela_stack_top, type);
3798c2ecf20Sopenharmony_ci}
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_cistatic int apply_r_larch_32_pcrel(struct module *mod, u32 *location, Elf_Addr v,
3828c2ecf20Sopenharmony_ci				  s64 *rela_stack, size_t *rela_stack_top, unsigned int type)
3838c2ecf20Sopenharmony_ci{
3848c2ecf20Sopenharmony_ci	ptrdiff_t offset = (void *)v - (void *)location;
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	*(u32 *)location = offset;
3878c2ecf20Sopenharmony_ci	return 0;
3888c2ecf20Sopenharmony_ci}
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_cistatic int apply_r_larch_64_pcrel(struct module *mod, u32 *location, Elf_Addr v,
3918c2ecf20Sopenharmony_ci				  s64 *rela_stack, size_t *rela_stack_top, unsigned int type)
3928c2ecf20Sopenharmony_ci{
3938c2ecf20Sopenharmony_ci	ptrdiff_t offset = (void *)v - (void *)location;
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	*(u64 *)location = offset;
3968c2ecf20Sopenharmony_ci	return 0;
3978c2ecf20Sopenharmony_ci}
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci/*
4008c2ecf20Sopenharmony_ci * reloc_handlers_rela() - Apply a particular relocation to a module
4018c2ecf20Sopenharmony_ci * @mod: the module to apply the reloc to
4028c2ecf20Sopenharmony_ci * @location: the address at which the reloc is to be applied
4038c2ecf20Sopenharmony_ci * @v: the value of the reloc, with addend for RELA-style
4048c2ecf20Sopenharmony_ci * @rela_stack: the stack used for store relocation info, LOCAL to THIS module
4058c2ecf20Sopenharmony_ci * @rela_stac_top: where the stack operation(pop/push) applies to
4068c2ecf20Sopenharmony_ci *
4078c2ecf20Sopenharmony_ci * Return: 0 upon success, else -ERRNO
4088c2ecf20Sopenharmony_ci */
4098c2ecf20Sopenharmony_citypedef int (*reloc_rela_handler)(struct module *mod, u32 *location, Elf_Addr v,
4108c2ecf20Sopenharmony_ci			s64 *rela_stack, size_t *rela_stack_top, unsigned int type);
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci/* The handlers for known reloc types */
4138c2ecf20Sopenharmony_cistatic reloc_rela_handler reloc_rela_handlers[] = {
4148c2ecf20Sopenharmony_ci	[R_LARCH_NONE ... R_LARCH_64_PCREL]		     = apply_r_larch_error,
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	[R_LARCH_NONE]					     = apply_r_larch_none,
4178c2ecf20Sopenharmony_ci	[R_LARCH_32]					     = apply_r_larch_32,
4188c2ecf20Sopenharmony_ci	[R_LARCH_64]					     = apply_r_larch_64,
4198c2ecf20Sopenharmony_ci	[R_LARCH_MARK_LA]				     = apply_r_larch_none,
4208c2ecf20Sopenharmony_ci	[R_LARCH_MARK_PCREL]				     = apply_r_larch_none,
4218c2ecf20Sopenharmony_ci	[R_LARCH_SOP_PUSH_PCREL]			     = apply_r_larch_sop_push_pcrel,
4228c2ecf20Sopenharmony_ci	[R_LARCH_SOP_PUSH_ABSOLUTE]			     = apply_r_larch_sop_push_absolute,
4238c2ecf20Sopenharmony_ci	[R_LARCH_SOP_PUSH_DUP]				     = apply_r_larch_sop_push_dup,
4248c2ecf20Sopenharmony_ci	[R_LARCH_SOP_SUB ... R_LARCH_SOP_IF_ELSE] 	     = apply_r_larch_sop,
4258c2ecf20Sopenharmony_ci	[R_LARCH_SOP_POP_32_S_10_5 ... R_LARCH_SOP_POP_32_U] = apply_r_larch_sop_imm_field,
4268c2ecf20Sopenharmony_ci	[R_LARCH_ADD32 ... R_LARCH_SUB64]		     = apply_r_larch_add_sub,
4278c2ecf20Sopenharmony_ci	[R_LARCH_PCALA_HI20...R_LARCH_PCALA64_HI12]	     = apply_r_larch_pcala,
4288c2ecf20Sopenharmony_ci	[R_LARCH_32_PCREL]				     = apply_r_larch_32_pcrel,
4298c2ecf20Sopenharmony_ci	[R_LARCH_64_PCREL]				     = apply_r_larch_64_pcrel,
4308c2ecf20Sopenharmony_ci};
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ciint apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab,
4338c2ecf20Sopenharmony_ci		       unsigned int symindex, unsigned int relsec,
4348c2ecf20Sopenharmony_ci		       struct module *mod)
4358c2ecf20Sopenharmony_ci{
4368c2ecf20Sopenharmony_ci	int i, err;
4378c2ecf20Sopenharmony_ci	unsigned int type;
4388c2ecf20Sopenharmony_ci	s64 rela_stack[RELA_STACK_DEPTH];
4398c2ecf20Sopenharmony_ci	size_t rela_stack_top = 0;
4408c2ecf20Sopenharmony_ci	reloc_rela_handler handler;
4418c2ecf20Sopenharmony_ci	void *location;
4428c2ecf20Sopenharmony_ci	Elf_Addr v;
4438c2ecf20Sopenharmony_ci	Elf_Sym *sym;
4448c2ecf20Sopenharmony_ci	Elf_Rela *rel = (void *) sechdrs[relsec].sh_addr;
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	pr_debug("%s: Applying relocate section %u to %u\n", __func__, relsec,
4478c2ecf20Sopenharmony_ci	       sechdrs[relsec].sh_info);
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	rela_stack_top = 0;
4508c2ecf20Sopenharmony_ci	for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
4518c2ecf20Sopenharmony_ci		/* This is where to make the change */
4528c2ecf20Sopenharmony_ci		location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr + rel[i].r_offset;
4538c2ecf20Sopenharmony_ci		/* This is the symbol it is referring to */
4548c2ecf20Sopenharmony_ci		sym = (Elf_Sym *)sechdrs[symindex].sh_addr + ELF_R_SYM(rel[i].r_info);
4558c2ecf20Sopenharmony_ci		if (IS_ERR_VALUE(sym->st_value)) {
4568c2ecf20Sopenharmony_ci			/* Ignore unresolved weak symbol */
4578c2ecf20Sopenharmony_ci			if (ELF_ST_BIND(sym->st_info) == STB_WEAK)
4588c2ecf20Sopenharmony_ci				continue;
4598c2ecf20Sopenharmony_ci			pr_warn("%s: Unknown symbol %s\n", mod->name, strtab + sym->st_name);
4608c2ecf20Sopenharmony_ci			return -ENOENT;
4618c2ecf20Sopenharmony_ci		}
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci		type = ELF_R_TYPE(rel[i].r_info);
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci		if (type < ARRAY_SIZE(reloc_rela_handlers))
4668c2ecf20Sopenharmony_ci			handler = reloc_rela_handlers[type];
4678c2ecf20Sopenharmony_ci		else
4688c2ecf20Sopenharmony_ci			handler = NULL;
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci		if (!handler) {
4718c2ecf20Sopenharmony_ci			pr_err("%s: Unknown relocation type %u\n", mod->name, type);
4728c2ecf20Sopenharmony_ci			return -EINVAL;
4738c2ecf20Sopenharmony_ci		}
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci		pr_debug("type %d st_value %llx r_addend %llx loc %llx\n",
4768c2ecf20Sopenharmony_ci		       (int)ELF_R_TYPE(rel[i].r_info),
4778c2ecf20Sopenharmony_ci		       sym->st_value, rel[i].r_addend, (u64)location);
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci		v = sym->st_value + rel[i].r_addend;
4808c2ecf20Sopenharmony_ci		switch (type) {
4818c2ecf20Sopenharmony_ci		case R_LARCH_B26:
4828c2ecf20Sopenharmony_ci			err = apply_r_larch_b26(mod, sechdrs, location,
4838c2ecf20Sopenharmony_ci						     v, rela_stack, &rela_stack_top, type);
4848c2ecf20Sopenharmony_ci			break;
4858c2ecf20Sopenharmony_ci		case R_LARCH_GOT_PC_HI20...R_LARCH_GOT_PC_LO12:
4868c2ecf20Sopenharmony_ci			err = apply_r_larch_got_pc(mod, sechdrs, location,
4878c2ecf20Sopenharmony_ci						     v, rela_stack, &rela_stack_top, type);
4888c2ecf20Sopenharmony_ci			break;
4898c2ecf20Sopenharmony_ci		case R_LARCH_SOP_PUSH_PLT_PCREL:
4908c2ecf20Sopenharmony_ci			err = apply_r_larch_sop_push_plt_pcrel(mod, sechdrs, location,
4918c2ecf20Sopenharmony_ci						     v, rela_stack, &rela_stack_top, type);
4928c2ecf20Sopenharmony_ci			break;
4938c2ecf20Sopenharmony_ci		default:
4948c2ecf20Sopenharmony_ci			err = handler(mod, location, v, rela_stack, &rela_stack_top, type);
4958c2ecf20Sopenharmony_ci		}
4968c2ecf20Sopenharmony_ci		if (err)
4978c2ecf20Sopenharmony_ci			return err;
4988c2ecf20Sopenharmony_ci	}
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	return 0;
5018c2ecf20Sopenharmony_ci}
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_civoid *module_alloc(unsigned long size)
5048c2ecf20Sopenharmony_ci{
5058c2ecf20Sopenharmony_ci	return __vmalloc_node_range(size, 1, MODULES_VADDR, MODULES_END,
5068c2ecf20Sopenharmony_ci			GFP_KERNEL, PAGE_KERNEL, 0, NUMA_NO_NODE, __builtin_return_address(0));
5078c2ecf20Sopenharmony_ci}
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_cistatic int module_init_ftrace_plt(const Elf_Ehdr *hdr,
5108c2ecf20Sopenharmony_ci				  const Elf_Shdr *sechdrs, struct module *mod)
5118c2ecf20Sopenharmony_ci{
5128c2ecf20Sopenharmony_ci#ifdef CONFIG_DYNAMIC_FTRACE
5138c2ecf20Sopenharmony_ci	struct plt_entry *ftrace_plts;
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	ftrace_plts = (void *)sechdrs->sh_addr;
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	ftrace_plts[FTRACE_PLT_IDX] = emit_plt_entry(FTRACE_ADDR);
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci	if (IS_ENABLED(CONFIG_DYNAMIC_FTRACE_WITH_REGS))
5208c2ecf20Sopenharmony_ci		ftrace_plts[FTRACE_REGS_PLT_IDX] = emit_plt_entry(FTRACE_REGS_ADDR);
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	mod->arch.ftrace_trampolines = ftrace_plts;
5238c2ecf20Sopenharmony_ci#endif
5248c2ecf20Sopenharmony_ci	return 0;
5258c2ecf20Sopenharmony_ci}
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ciint module_finalize(const Elf_Ehdr *hdr,
5288c2ecf20Sopenharmony_ci		    const Elf_Shdr *sechdrs, struct module *mod)
5298c2ecf20Sopenharmony_ci{
5308c2ecf20Sopenharmony_ci	char *secstrings;
5318c2ecf20Sopenharmony_ci	const Elf_Shdr *s, *orc = NULL, *orc_ip = NULL, *alt = NULL, *ftrace = NULL;
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	for (s = sechdrs; s < sechdrs + hdr->e_shnum; s++) {
5368c2ecf20Sopenharmony_ci		if (!strcmp(".altinstructions", secstrings + s->sh_name))
5378c2ecf20Sopenharmony_ci			alt = s;
5388c2ecf20Sopenharmony_ci		if (!strcmp(".orc_unwind", secstrings + s->sh_name))
5398c2ecf20Sopenharmony_ci			orc = s;
5408c2ecf20Sopenharmony_ci		if (!strcmp(".orc_unwind_ip", secstrings + s->sh_name))
5418c2ecf20Sopenharmony_ci			orc_ip = s;
5428c2ecf20Sopenharmony_ci		if (!strcmp(".ftrace_trampoline", secstrings + s->sh_name))
5438c2ecf20Sopenharmony_ci			ftrace = s;
5448c2ecf20Sopenharmony_ci	}
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	if (alt)
5478c2ecf20Sopenharmony_ci		apply_alternatives((void *)alt->sh_addr, (void *)alt->sh_addr + alt->sh_size);
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	if (orc && orc_ip)
5508c2ecf20Sopenharmony_ci		unwind_module_init(mod, (void *)orc_ip->sh_addr, orc_ip->sh_size, (void *)orc->sh_addr, orc->sh_size);
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	return module_init_ftrace_plt(hdr, ftrace, mod);
5538c2ecf20Sopenharmony_ci}
554