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