1fcdfe9d2SHuacai Chen // SPDX-License-Identifier: GPL-2.0+ 2fcdfe9d2SHuacai Chen /* 3fcdfe9d2SHuacai Chen * Author: Hanlu Li <lihanlu@loongson.cn> 4fcdfe9d2SHuacai Chen * Huacai Chen <chenhuacai@loongson.cn> 5fcdfe9d2SHuacai Chen * 6fcdfe9d2SHuacai Chen * Copyright (C) 2020-2022 Loongson Technology Corporation Limited 7fcdfe9d2SHuacai Chen */ 8fcdfe9d2SHuacai Chen 9fcdfe9d2SHuacai Chen #define pr_fmt(fmt) "kmod: " fmt 10fcdfe9d2SHuacai Chen 11fcdfe9d2SHuacai Chen #include <linux/moduleloader.h> 12fcdfe9d2SHuacai Chen #include <linux/elf.h> 13fcdfe9d2SHuacai Chen #include <linux/mm.h> 14d4b6f156SHuacai Chen #include <linux/numa.h> 15fcdfe9d2SHuacai Chen #include <linux/vmalloc.h> 16fcdfe9d2SHuacai Chen #include <linux/slab.h> 17fcdfe9d2SHuacai Chen #include <linux/fs.h> 18fcdfe9d2SHuacai Chen #include <linux/string.h> 19fcdfe9d2SHuacai Chen #include <linux/kernel.h> 2019e5eb15SHuacai Chen #include <asm/alternative.h> 21fcdfe9d2SHuacai Chen 22fcdfe9d2SHuacai Chen static int rela_stack_push(s64 stack_value, s64 *rela_stack, size_t *rela_stack_top) 23fcdfe9d2SHuacai Chen { 24fcdfe9d2SHuacai Chen if (*rela_stack_top >= RELA_STACK_DEPTH) 25fcdfe9d2SHuacai Chen return -ENOEXEC; 26fcdfe9d2SHuacai Chen 27fcdfe9d2SHuacai Chen rela_stack[(*rela_stack_top)++] = stack_value; 28fcdfe9d2SHuacai Chen pr_debug("%s stack_value = 0x%llx\n", __func__, stack_value); 29fcdfe9d2SHuacai Chen 30fcdfe9d2SHuacai Chen return 0; 31fcdfe9d2SHuacai Chen } 32fcdfe9d2SHuacai Chen 33fcdfe9d2SHuacai Chen static int rela_stack_pop(s64 *stack_value, s64 *rela_stack, size_t *rela_stack_top) 34fcdfe9d2SHuacai Chen { 35fcdfe9d2SHuacai Chen if (*rela_stack_top == 0) 36fcdfe9d2SHuacai Chen return -ENOEXEC; 37fcdfe9d2SHuacai Chen 38fcdfe9d2SHuacai Chen *stack_value = rela_stack[--(*rela_stack_top)]; 39fcdfe9d2SHuacai Chen pr_debug("%s stack_value = 0x%llx\n", __func__, *stack_value); 40fcdfe9d2SHuacai Chen 41fcdfe9d2SHuacai Chen return 0; 42fcdfe9d2SHuacai Chen } 43fcdfe9d2SHuacai Chen 44fcdfe9d2SHuacai Chen static int apply_r_larch_none(struct module *mod, u32 *location, Elf_Addr v, 45fcdfe9d2SHuacai Chen s64 *rela_stack, size_t *rela_stack_top, unsigned int type) 46fcdfe9d2SHuacai Chen { 47fcdfe9d2SHuacai Chen return 0; 48fcdfe9d2SHuacai Chen } 49fcdfe9d2SHuacai Chen 50fcdfe9d2SHuacai Chen static int apply_r_larch_error(struct module *me, u32 *location, Elf_Addr v, 51fcdfe9d2SHuacai Chen s64 *rela_stack, size_t *rela_stack_top, unsigned int type) 52fcdfe9d2SHuacai Chen { 53fcdfe9d2SHuacai Chen pr_err("%s: Unsupport relocation type %u, please add its support.\n", me->name, type); 54fcdfe9d2SHuacai Chen return -EINVAL; 55fcdfe9d2SHuacai Chen } 56fcdfe9d2SHuacai Chen 57fcdfe9d2SHuacai Chen static int apply_r_larch_32(struct module *mod, u32 *location, Elf_Addr v, 58fcdfe9d2SHuacai Chen s64 *rela_stack, size_t *rela_stack_top, unsigned int type) 59fcdfe9d2SHuacai Chen { 60fcdfe9d2SHuacai Chen *location = v; 61fcdfe9d2SHuacai Chen return 0; 62fcdfe9d2SHuacai Chen } 63fcdfe9d2SHuacai Chen 64fcdfe9d2SHuacai Chen static int apply_r_larch_64(struct module *mod, u32 *location, Elf_Addr v, 65fcdfe9d2SHuacai Chen s64 *rela_stack, size_t *rela_stack_top, unsigned int type) 66fcdfe9d2SHuacai Chen { 67fcdfe9d2SHuacai Chen *(Elf_Addr *)location = v; 68fcdfe9d2SHuacai Chen return 0; 69fcdfe9d2SHuacai Chen } 70fcdfe9d2SHuacai Chen 71fcdfe9d2SHuacai Chen static int apply_r_larch_sop_push_pcrel(struct module *mod, u32 *location, Elf_Addr v, 72fcdfe9d2SHuacai Chen s64 *rela_stack, size_t *rela_stack_top, unsigned int type) 73fcdfe9d2SHuacai Chen { 74fcdfe9d2SHuacai Chen return rela_stack_push(v - (u64)location, rela_stack, rela_stack_top); 75fcdfe9d2SHuacai Chen } 76fcdfe9d2SHuacai Chen 77fcdfe9d2SHuacai Chen static int apply_r_larch_sop_push_absolute(struct module *mod, u32 *location, Elf_Addr v, 78fcdfe9d2SHuacai Chen s64 *rela_stack, size_t *rela_stack_top, unsigned int type) 79fcdfe9d2SHuacai Chen { 80fcdfe9d2SHuacai Chen return rela_stack_push(v, rela_stack, rela_stack_top); 81fcdfe9d2SHuacai Chen } 82fcdfe9d2SHuacai Chen 83fcdfe9d2SHuacai Chen static int apply_r_larch_sop_push_dup(struct module *mod, u32 *location, Elf_Addr v, 84fcdfe9d2SHuacai Chen s64 *rela_stack, size_t *rela_stack_top, unsigned int type) 85fcdfe9d2SHuacai Chen { 86fcdfe9d2SHuacai Chen int err = 0; 87fcdfe9d2SHuacai Chen s64 opr1; 88fcdfe9d2SHuacai Chen 89fcdfe9d2SHuacai Chen err = rela_stack_pop(&opr1, rela_stack, rela_stack_top); 90fcdfe9d2SHuacai Chen if (err) 91fcdfe9d2SHuacai Chen return err; 92fcdfe9d2SHuacai Chen err = rela_stack_push(opr1, rela_stack, rela_stack_top); 93fcdfe9d2SHuacai Chen if (err) 94fcdfe9d2SHuacai Chen return err; 95fcdfe9d2SHuacai Chen err = rela_stack_push(opr1, rela_stack, rela_stack_top); 96fcdfe9d2SHuacai Chen if (err) 97fcdfe9d2SHuacai Chen return err; 98fcdfe9d2SHuacai Chen 99fcdfe9d2SHuacai Chen return 0; 100fcdfe9d2SHuacai Chen } 101fcdfe9d2SHuacai Chen 102*9151dde4SHuacai Chen static int apply_r_larch_sop_push_plt_pcrel(struct module *mod, 103*9151dde4SHuacai Chen Elf_Shdr *sechdrs, u32 *location, Elf_Addr v, 104fcdfe9d2SHuacai Chen s64 *rela_stack, size_t *rela_stack_top, unsigned int type) 105fcdfe9d2SHuacai Chen { 106fcdfe9d2SHuacai Chen ptrdiff_t offset = (void *)v - (void *)location; 107fcdfe9d2SHuacai Chen 108fcdfe9d2SHuacai Chen if (offset >= SZ_128M) 109*9151dde4SHuacai Chen v = module_emit_plt_entry(mod, sechdrs, v); 110fcdfe9d2SHuacai Chen 111fcdfe9d2SHuacai Chen if (offset < -SZ_128M) 112*9151dde4SHuacai Chen v = module_emit_plt_entry(mod, sechdrs, v); 113fcdfe9d2SHuacai Chen 114fcdfe9d2SHuacai Chen return apply_r_larch_sop_push_pcrel(mod, location, v, rela_stack, rela_stack_top, type); 115fcdfe9d2SHuacai Chen } 116fcdfe9d2SHuacai Chen 117fcdfe9d2SHuacai Chen static int apply_r_larch_sop(struct module *mod, u32 *location, Elf_Addr v, 118fcdfe9d2SHuacai Chen s64 *rela_stack, size_t *rela_stack_top, unsigned int type) 119fcdfe9d2SHuacai Chen { 120fcdfe9d2SHuacai Chen int err = 0; 121fcdfe9d2SHuacai Chen s64 opr1, opr2, opr3; 122fcdfe9d2SHuacai Chen 123fcdfe9d2SHuacai Chen if (type == R_LARCH_SOP_IF_ELSE) { 124fcdfe9d2SHuacai Chen err = rela_stack_pop(&opr3, rela_stack, rela_stack_top); 125fcdfe9d2SHuacai Chen if (err) 126fcdfe9d2SHuacai Chen return err; 127fcdfe9d2SHuacai Chen } 128fcdfe9d2SHuacai Chen 129fcdfe9d2SHuacai Chen err = rela_stack_pop(&opr2, rela_stack, rela_stack_top); 130fcdfe9d2SHuacai Chen if (err) 131fcdfe9d2SHuacai Chen return err; 132fcdfe9d2SHuacai Chen err = rela_stack_pop(&opr1, rela_stack, rela_stack_top); 133fcdfe9d2SHuacai Chen if (err) 134fcdfe9d2SHuacai Chen return err; 135fcdfe9d2SHuacai Chen 136fcdfe9d2SHuacai Chen switch (type) { 137fcdfe9d2SHuacai Chen case R_LARCH_SOP_AND: 138fcdfe9d2SHuacai Chen err = rela_stack_push(opr1 & opr2, rela_stack, rela_stack_top); 139fcdfe9d2SHuacai Chen break; 140fcdfe9d2SHuacai Chen case R_LARCH_SOP_ADD: 141fcdfe9d2SHuacai Chen err = rela_stack_push(opr1 + opr2, rela_stack, rela_stack_top); 142fcdfe9d2SHuacai Chen break; 143fcdfe9d2SHuacai Chen case R_LARCH_SOP_SUB: 144fcdfe9d2SHuacai Chen err = rela_stack_push(opr1 - opr2, rela_stack, rela_stack_top); 145fcdfe9d2SHuacai Chen break; 146fcdfe9d2SHuacai Chen case R_LARCH_SOP_SL: 147fcdfe9d2SHuacai Chen err = rela_stack_push(opr1 << opr2, rela_stack, rela_stack_top); 148fcdfe9d2SHuacai Chen break; 149fcdfe9d2SHuacai Chen case R_LARCH_SOP_SR: 150fcdfe9d2SHuacai Chen err = rela_stack_push(opr1 >> opr2, rela_stack, rela_stack_top); 151fcdfe9d2SHuacai Chen break; 152fcdfe9d2SHuacai Chen case R_LARCH_SOP_IF_ELSE: 153fcdfe9d2SHuacai Chen err = rela_stack_push(opr1 ? opr2 : opr3, rela_stack, rela_stack_top); 154fcdfe9d2SHuacai Chen break; 155fcdfe9d2SHuacai Chen default: 156fcdfe9d2SHuacai Chen pr_err("%s: Unsupport relocation type %u\n", mod->name, type); 157fcdfe9d2SHuacai Chen return -EINVAL; 158fcdfe9d2SHuacai Chen } 159fcdfe9d2SHuacai Chen 160fcdfe9d2SHuacai Chen return err; 161fcdfe9d2SHuacai Chen } 162fcdfe9d2SHuacai Chen 163fcdfe9d2SHuacai Chen static int apply_r_larch_sop_imm_field(struct module *mod, u32 *location, Elf_Addr v, 164fcdfe9d2SHuacai Chen s64 *rela_stack, size_t *rela_stack_top, unsigned int type) 165fcdfe9d2SHuacai Chen { 166fcdfe9d2SHuacai Chen int err = 0; 167fcdfe9d2SHuacai Chen s64 opr1; 168fcdfe9d2SHuacai Chen union loongarch_instruction *insn = (union loongarch_instruction *)location; 169fcdfe9d2SHuacai Chen 170fcdfe9d2SHuacai Chen err = rela_stack_pop(&opr1, rela_stack, rela_stack_top); 171fcdfe9d2SHuacai Chen if (err) 172fcdfe9d2SHuacai Chen return err; 173fcdfe9d2SHuacai Chen 174fcdfe9d2SHuacai Chen switch (type) { 175fcdfe9d2SHuacai Chen case R_LARCH_SOP_POP_32_U_10_12: 176fcdfe9d2SHuacai Chen if (!unsigned_imm_check(opr1, 12)) 177fcdfe9d2SHuacai Chen goto overflow; 178fcdfe9d2SHuacai Chen 179fcdfe9d2SHuacai Chen /* (*(uint32_t *) PC) [21 ... 10] = opr [11 ... 0] */ 180fcdfe9d2SHuacai Chen insn->reg2i12_format.immediate = opr1 & 0xfff; 181fcdfe9d2SHuacai Chen return 0; 182fcdfe9d2SHuacai Chen case R_LARCH_SOP_POP_32_S_10_12: 183fcdfe9d2SHuacai Chen if (!signed_imm_check(opr1, 12)) 184fcdfe9d2SHuacai Chen goto overflow; 185fcdfe9d2SHuacai Chen 186fcdfe9d2SHuacai Chen insn->reg2i12_format.immediate = opr1 & 0xfff; 187fcdfe9d2SHuacai Chen return 0; 188fcdfe9d2SHuacai Chen case R_LARCH_SOP_POP_32_S_10_16: 189fcdfe9d2SHuacai Chen if (!signed_imm_check(opr1, 16)) 190fcdfe9d2SHuacai Chen goto overflow; 191fcdfe9d2SHuacai Chen 192fcdfe9d2SHuacai Chen insn->reg2i16_format.immediate = opr1 & 0xffff; 193fcdfe9d2SHuacai Chen return 0; 194fcdfe9d2SHuacai Chen case R_LARCH_SOP_POP_32_S_10_16_S2: 195fcdfe9d2SHuacai Chen if (opr1 % 4) 196fcdfe9d2SHuacai Chen goto unaligned; 197fcdfe9d2SHuacai Chen 198fcdfe9d2SHuacai Chen if (!signed_imm_check(opr1, 18)) 199fcdfe9d2SHuacai Chen goto overflow; 200fcdfe9d2SHuacai Chen 201fcdfe9d2SHuacai Chen insn->reg2i16_format.immediate = (opr1 >> 2) & 0xffff; 202fcdfe9d2SHuacai Chen return 0; 203fcdfe9d2SHuacai Chen case R_LARCH_SOP_POP_32_S_5_20: 204fcdfe9d2SHuacai Chen if (!signed_imm_check(opr1, 20)) 205fcdfe9d2SHuacai Chen goto overflow; 206fcdfe9d2SHuacai Chen 207fcdfe9d2SHuacai Chen insn->reg1i20_format.immediate = (opr1) & 0xfffff; 208fcdfe9d2SHuacai Chen return 0; 209fcdfe9d2SHuacai Chen case R_LARCH_SOP_POP_32_S_0_5_10_16_S2: 210fcdfe9d2SHuacai Chen if (opr1 % 4) 211fcdfe9d2SHuacai Chen goto unaligned; 212fcdfe9d2SHuacai Chen 213fcdfe9d2SHuacai Chen if (!signed_imm_check(opr1, 23)) 214fcdfe9d2SHuacai Chen goto overflow; 215fcdfe9d2SHuacai Chen 216fcdfe9d2SHuacai Chen opr1 >>= 2; 217fcdfe9d2SHuacai Chen insn->reg1i21_format.immediate_l = opr1 & 0xffff; 218fcdfe9d2SHuacai Chen insn->reg1i21_format.immediate_h = (opr1 >> 16) & 0x1f; 219fcdfe9d2SHuacai Chen return 0; 220fcdfe9d2SHuacai Chen case R_LARCH_SOP_POP_32_S_0_10_10_16_S2: 221fcdfe9d2SHuacai Chen if (opr1 % 4) 222fcdfe9d2SHuacai Chen goto unaligned; 223fcdfe9d2SHuacai Chen 224fcdfe9d2SHuacai Chen if (!signed_imm_check(opr1, 28)) 225fcdfe9d2SHuacai Chen goto overflow; 226fcdfe9d2SHuacai Chen 227fcdfe9d2SHuacai Chen opr1 >>= 2; 228fcdfe9d2SHuacai Chen insn->reg0i26_format.immediate_l = opr1 & 0xffff; 229fcdfe9d2SHuacai Chen insn->reg0i26_format.immediate_h = (opr1 >> 16) & 0x3ff; 230fcdfe9d2SHuacai Chen return 0; 231fcdfe9d2SHuacai Chen case R_LARCH_SOP_POP_32_U: 232fcdfe9d2SHuacai Chen if (!unsigned_imm_check(opr1, 32)) 233fcdfe9d2SHuacai Chen goto overflow; 234fcdfe9d2SHuacai Chen 235fcdfe9d2SHuacai Chen /* (*(uint32_t *) PC) = opr */ 236fcdfe9d2SHuacai Chen *location = (u32)opr1; 237fcdfe9d2SHuacai Chen return 0; 238fcdfe9d2SHuacai Chen default: 239fcdfe9d2SHuacai Chen pr_err("%s: Unsupport relocation type %u\n", mod->name, type); 240fcdfe9d2SHuacai Chen return -EINVAL; 241fcdfe9d2SHuacai Chen } 242fcdfe9d2SHuacai Chen 243fcdfe9d2SHuacai Chen overflow: 244fcdfe9d2SHuacai Chen pr_err("module %s: opr1 = 0x%llx overflow! dangerous %s (%u) relocation\n", 245fcdfe9d2SHuacai Chen mod->name, opr1, __func__, type); 246fcdfe9d2SHuacai Chen return -ENOEXEC; 247fcdfe9d2SHuacai Chen 248fcdfe9d2SHuacai Chen unaligned: 249fcdfe9d2SHuacai Chen pr_err("module %s: opr1 = 0x%llx unaligned! dangerous %s (%u) relocation\n", 250fcdfe9d2SHuacai Chen mod->name, opr1, __func__, type); 251fcdfe9d2SHuacai Chen return -ENOEXEC; 252fcdfe9d2SHuacai Chen } 253fcdfe9d2SHuacai Chen 254fcdfe9d2SHuacai Chen static int apply_r_larch_add_sub(struct module *mod, u32 *location, Elf_Addr v, 255fcdfe9d2SHuacai Chen s64 *rela_stack, size_t *rela_stack_top, unsigned int type) 256fcdfe9d2SHuacai Chen { 257fcdfe9d2SHuacai Chen switch (type) { 258fcdfe9d2SHuacai Chen case R_LARCH_ADD32: 259fcdfe9d2SHuacai Chen *(s32 *)location += v; 260fcdfe9d2SHuacai Chen return 0; 261fcdfe9d2SHuacai Chen case R_LARCH_ADD64: 262fcdfe9d2SHuacai Chen *(s64 *)location += v; 263fcdfe9d2SHuacai Chen return 0; 264fcdfe9d2SHuacai Chen case R_LARCH_SUB32: 265fcdfe9d2SHuacai Chen *(s32 *)location -= v; 266fcdfe9d2SHuacai Chen return 0; 267fcdfe9d2SHuacai Chen case R_LARCH_SUB64: 268fcdfe9d2SHuacai Chen *(s64 *)location -= v; 269fcdfe9d2SHuacai Chen return 0; 270fcdfe9d2SHuacai Chen default: 271fcdfe9d2SHuacai Chen pr_err("%s: Unsupport relocation type %u\n", mod->name, type); 272fcdfe9d2SHuacai Chen return -EINVAL; 273fcdfe9d2SHuacai Chen } 274fcdfe9d2SHuacai Chen } 275fcdfe9d2SHuacai Chen 276*9151dde4SHuacai Chen static int apply_r_larch_b26(struct module *mod, 277*9151dde4SHuacai Chen Elf_Shdr *sechdrs, u32 *location, Elf_Addr v, 2789bd1e380SXi Ruoyao s64 *rela_stack, size_t *rela_stack_top, unsigned int type) 2799bd1e380SXi Ruoyao { 2809bd1e380SXi Ruoyao ptrdiff_t offset = (void *)v - (void *)location; 2819bd1e380SXi Ruoyao union loongarch_instruction *insn = (union loongarch_instruction *)location; 2829bd1e380SXi Ruoyao 2839bd1e380SXi Ruoyao if (offset >= SZ_128M) 284*9151dde4SHuacai Chen v = module_emit_plt_entry(mod, sechdrs, v); 2859bd1e380SXi Ruoyao 2869bd1e380SXi Ruoyao if (offset < -SZ_128M) 287*9151dde4SHuacai Chen v = module_emit_plt_entry(mod, sechdrs, v); 2889bd1e380SXi Ruoyao 2899bd1e380SXi Ruoyao offset = (void *)v - (void *)location; 2909bd1e380SXi Ruoyao 2919bd1e380SXi Ruoyao if (offset & 3) { 2929bd1e380SXi Ruoyao pr_err("module %s: jump offset = 0x%llx unaligned! dangerous R_LARCH_B26 (%u) relocation\n", 2939bd1e380SXi Ruoyao mod->name, (long long)offset, type); 2949bd1e380SXi Ruoyao return -ENOEXEC; 2959bd1e380SXi Ruoyao } 2969bd1e380SXi Ruoyao 2979bd1e380SXi Ruoyao if (!signed_imm_check(offset, 28)) { 2989bd1e380SXi Ruoyao pr_err("module %s: jump offset = 0x%llx overflow! dangerous R_LARCH_B26 (%u) relocation\n", 2999bd1e380SXi Ruoyao mod->name, (long long)offset, type); 3009bd1e380SXi Ruoyao return -ENOEXEC; 3019bd1e380SXi Ruoyao } 3029bd1e380SXi Ruoyao 3039bd1e380SXi Ruoyao offset >>= 2; 3049bd1e380SXi Ruoyao insn->reg0i26_format.immediate_l = offset & 0xffff; 3059bd1e380SXi Ruoyao insn->reg0i26_format.immediate_h = (offset >> 16) & 0x3ff; 3069bd1e380SXi Ruoyao 3079bd1e380SXi Ruoyao return 0; 3089bd1e380SXi Ruoyao } 3099bd1e380SXi Ruoyao 3109bd1e380SXi Ruoyao static int apply_r_larch_pcala(struct module *mod, u32 *location, Elf_Addr v, 3119bd1e380SXi Ruoyao s64 *rela_stack, size_t *rela_stack_top, unsigned int type) 3129bd1e380SXi Ruoyao { 3139bd1e380SXi Ruoyao union loongarch_instruction *insn = (union loongarch_instruction *)location; 3149bd1e380SXi Ruoyao /* Use s32 for a sign-extension deliberately. */ 3159bd1e380SXi Ruoyao s32 offset_hi20 = (void *)((v + 0x800) & ~0xfff) - 3169bd1e380SXi Ruoyao (void *)((Elf_Addr)location & ~0xfff); 3179bd1e380SXi Ruoyao Elf_Addr anchor = (((Elf_Addr)location) & ~0xfff) + offset_hi20; 3189bd1e380SXi Ruoyao ptrdiff_t offset_rem = (void *)v - (void *)anchor; 3199bd1e380SXi Ruoyao 3209bd1e380SXi Ruoyao switch (type) { 3219bd1e380SXi Ruoyao case R_LARCH_PCALA_LO12: 3229bd1e380SXi Ruoyao insn->reg2i12_format.immediate = v & 0xfff; 3239bd1e380SXi Ruoyao break; 3249bd1e380SXi Ruoyao case R_LARCH_PCALA_HI20: 3259bd1e380SXi Ruoyao v = offset_hi20 >> 12; 3269bd1e380SXi Ruoyao insn->reg1i20_format.immediate = v & 0xfffff; 3279bd1e380SXi Ruoyao break; 3289bd1e380SXi Ruoyao case R_LARCH_PCALA64_LO20: 3299bd1e380SXi Ruoyao v = offset_rem >> 32; 3309bd1e380SXi Ruoyao insn->reg1i20_format.immediate = v & 0xfffff; 3319bd1e380SXi Ruoyao break; 3329bd1e380SXi Ruoyao case R_LARCH_PCALA64_HI12: 3339bd1e380SXi Ruoyao v = offset_rem >> 52; 3349bd1e380SXi Ruoyao insn->reg2i12_format.immediate = v & 0xfff; 3359bd1e380SXi Ruoyao break; 3369bd1e380SXi Ruoyao default: 3379bd1e380SXi Ruoyao pr_err("%s: Unsupport relocation type %u\n", mod->name, type); 3389bd1e380SXi Ruoyao return -EINVAL; 3399bd1e380SXi Ruoyao } 3409bd1e380SXi Ruoyao 3419bd1e380SXi Ruoyao return 0; 3429bd1e380SXi Ruoyao } 3439bd1e380SXi Ruoyao 344*9151dde4SHuacai Chen static int apply_r_larch_got_pc(struct module *mod, 345*9151dde4SHuacai Chen Elf_Shdr *sechdrs, u32 *location, Elf_Addr v, 34659b3d4a9SXi Ruoyao s64 *rela_stack, size_t *rela_stack_top, unsigned int type) 34759b3d4a9SXi Ruoyao { 348*9151dde4SHuacai Chen Elf_Addr got = module_emit_got_entry(mod, sechdrs, v); 34959b3d4a9SXi Ruoyao 35059b3d4a9SXi Ruoyao if (!got) 35159b3d4a9SXi Ruoyao return -EINVAL; 35259b3d4a9SXi Ruoyao 35359b3d4a9SXi Ruoyao switch (type) { 35459b3d4a9SXi Ruoyao case R_LARCH_GOT_PC_LO12: 35559b3d4a9SXi Ruoyao type = R_LARCH_PCALA_LO12; 35659b3d4a9SXi Ruoyao break; 35759b3d4a9SXi Ruoyao case R_LARCH_GOT_PC_HI20: 35859b3d4a9SXi Ruoyao type = R_LARCH_PCALA_HI20; 35959b3d4a9SXi Ruoyao break; 36059b3d4a9SXi Ruoyao default: 36159b3d4a9SXi Ruoyao pr_err("%s: Unsupport relocation type %u\n", mod->name, type); 36259b3d4a9SXi Ruoyao return -EINVAL; 36359b3d4a9SXi Ruoyao } 36459b3d4a9SXi Ruoyao 36559b3d4a9SXi Ruoyao return apply_r_larch_pcala(mod, location, got, rela_stack, rela_stack_top, type); 36659b3d4a9SXi Ruoyao } 36759b3d4a9SXi Ruoyao 368fcdfe9d2SHuacai Chen /* 369fcdfe9d2SHuacai Chen * reloc_handlers_rela() - Apply a particular relocation to a module 370fcdfe9d2SHuacai Chen * @mod: the module to apply the reloc to 371fcdfe9d2SHuacai Chen * @location: the address at which the reloc is to be applied 372fcdfe9d2SHuacai Chen * @v: the value of the reloc, with addend for RELA-style 373fcdfe9d2SHuacai Chen * @rela_stack: the stack used for store relocation info, LOCAL to THIS module 374fcdfe9d2SHuacai Chen * @rela_stac_top: where the stack operation(pop/push) applies to 375fcdfe9d2SHuacai Chen * 376fcdfe9d2SHuacai Chen * Return: 0 upon success, else -ERRNO 377fcdfe9d2SHuacai Chen */ 378fcdfe9d2SHuacai Chen typedef int (*reloc_rela_handler)(struct module *mod, u32 *location, Elf_Addr v, 379fcdfe9d2SHuacai Chen s64 *rela_stack, size_t *rela_stack_top, unsigned int type); 380fcdfe9d2SHuacai Chen 381fcdfe9d2SHuacai Chen /* The handlers for known reloc types */ 382fcdfe9d2SHuacai Chen static reloc_rela_handler reloc_rela_handlers[] = { 3830a75e5d1SXi Ruoyao [R_LARCH_NONE ... R_LARCH_RELAX] = apply_r_larch_error, 384fcdfe9d2SHuacai Chen 385fcdfe9d2SHuacai Chen [R_LARCH_NONE] = apply_r_larch_none, 386fcdfe9d2SHuacai Chen [R_LARCH_32] = apply_r_larch_32, 387fcdfe9d2SHuacai Chen [R_LARCH_64] = apply_r_larch_64, 388fcdfe9d2SHuacai Chen [R_LARCH_MARK_LA] = apply_r_larch_none, 389fcdfe9d2SHuacai Chen [R_LARCH_MARK_PCREL] = apply_r_larch_none, 390fcdfe9d2SHuacai Chen [R_LARCH_SOP_PUSH_PCREL] = apply_r_larch_sop_push_pcrel, 391fcdfe9d2SHuacai Chen [R_LARCH_SOP_PUSH_ABSOLUTE] = apply_r_larch_sop_push_absolute, 392fcdfe9d2SHuacai Chen [R_LARCH_SOP_PUSH_DUP] = apply_r_larch_sop_push_dup, 393fcdfe9d2SHuacai Chen [R_LARCH_SOP_SUB ... R_LARCH_SOP_IF_ELSE] = apply_r_larch_sop, 394fcdfe9d2SHuacai Chen [R_LARCH_SOP_POP_32_S_10_5 ... R_LARCH_SOP_POP_32_U] = apply_r_larch_sop_imm_field, 395fcdfe9d2SHuacai Chen [R_LARCH_ADD32 ... R_LARCH_SUB64] = apply_r_larch_add_sub, 3969bd1e380SXi Ruoyao [R_LARCH_PCALA_HI20...R_LARCH_PCALA64_HI12] = apply_r_larch_pcala, 397fcdfe9d2SHuacai Chen }; 398fcdfe9d2SHuacai Chen 399fcdfe9d2SHuacai Chen int apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab, 400fcdfe9d2SHuacai Chen unsigned int symindex, unsigned int relsec, 401fcdfe9d2SHuacai Chen struct module *mod) 402fcdfe9d2SHuacai Chen { 403fcdfe9d2SHuacai Chen int i, err; 404fcdfe9d2SHuacai Chen unsigned int type; 405fcdfe9d2SHuacai Chen s64 rela_stack[RELA_STACK_DEPTH]; 406fcdfe9d2SHuacai Chen size_t rela_stack_top = 0; 407fcdfe9d2SHuacai Chen reloc_rela_handler handler; 408fcdfe9d2SHuacai Chen void *location; 409fcdfe9d2SHuacai Chen Elf_Addr v; 410fcdfe9d2SHuacai Chen Elf_Sym *sym; 411fcdfe9d2SHuacai Chen Elf_Rela *rel = (void *) sechdrs[relsec].sh_addr; 412fcdfe9d2SHuacai Chen 413fcdfe9d2SHuacai Chen pr_debug("%s: Applying relocate section %u to %u\n", __func__, relsec, 414fcdfe9d2SHuacai Chen sechdrs[relsec].sh_info); 415fcdfe9d2SHuacai Chen 416fcdfe9d2SHuacai Chen rela_stack_top = 0; 417fcdfe9d2SHuacai Chen for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { 418fcdfe9d2SHuacai Chen /* This is where to make the change */ 419fcdfe9d2SHuacai Chen location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr + rel[i].r_offset; 420fcdfe9d2SHuacai Chen /* This is the symbol it is referring to */ 421fcdfe9d2SHuacai Chen sym = (Elf_Sym *)sechdrs[symindex].sh_addr + ELF_R_SYM(rel[i].r_info); 422fcdfe9d2SHuacai Chen if (IS_ERR_VALUE(sym->st_value)) { 423fcdfe9d2SHuacai Chen /* Ignore unresolved weak symbol */ 424fcdfe9d2SHuacai Chen if (ELF_ST_BIND(sym->st_info) == STB_WEAK) 425fcdfe9d2SHuacai Chen continue; 426fcdfe9d2SHuacai Chen pr_warn("%s: Unknown symbol %s\n", mod->name, strtab + sym->st_name); 427fcdfe9d2SHuacai Chen return -ENOENT; 428fcdfe9d2SHuacai Chen } 429fcdfe9d2SHuacai Chen 430fcdfe9d2SHuacai Chen type = ELF_R_TYPE(rel[i].r_info); 431fcdfe9d2SHuacai Chen 432fcdfe9d2SHuacai Chen if (type < ARRAY_SIZE(reloc_rela_handlers)) 433fcdfe9d2SHuacai Chen handler = reloc_rela_handlers[type]; 434fcdfe9d2SHuacai Chen else 435fcdfe9d2SHuacai Chen handler = NULL; 436fcdfe9d2SHuacai Chen 437fcdfe9d2SHuacai Chen if (!handler) { 438fcdfe9d2SHuacai Chen pr_err("%s: Unknown relocation type %u\n", mod->name, type); 439fcdfe9d2SHuacai Chen return -EINVAL; 440fcdfe9d2SHuacai Chen } 441fcdfe9d2SHuacai Chen 442fcdfe9d2SHuacai Chen pr_debug("type %d st_value %llx r_addend %llx loc %llx\n", 443fcdfe9d2SHuacai Chen (int)ELF_R_TYPE(rel[i].r_info), 444fcdfe9d2SHuacai Chen sym->st_value, rel[i].r_addend, (u64)location); 445fcdfe9d2SHuacai Chen 446fcdfe9d2SHuacai Chen v = sym->st_value + rel[i].r_addend; 447*9151dde4SHuacai Chen switch (type) { 448*9151dde4SHuacai Chen case R_LARCH_B26: 449*9151dde4SHuacai Chen err = apply_r_larch_b26(mod, sechdrs, location, 450*9151dde4SHuacai Chen v, rela_stack, &rela_stack_top, type); 451*9151dde4SHuacai Chen break; 452*9151dde4SHuacai Chen case R_LARCH_GOT_PC_HI20...R_LARCH_GOT_PC_LO12: 453*9151dde4SHuacai Chen err = apply_r_larch_got_pc(mod, sechdrs, location, 454*9151dde4SHuacai Chen v, rela_stack, &rela_stack_top, type); 455*9151dde4SHuacai Chen break; 456*9151dde4SHuacai Chen case R_LARCH_SOP_PUSH_PLT_PCREL: 457*9151dde4SHuacai Chen err = apply_r_larch_sop_push_plt_pcrel(mod, sechdrs, location, 458*9151dde4SHuacai Chen v, rela_stack, &rela_stack_top, type); 459*9151dde4SHuacai Chen break; 460*9151dde4SHuacai Chen default: 461fcdfe9d2SHuacai Chen err = handler(mod, location, v, rela_stack, &rela_stack_top, type); 462*9151dde4SHuacai Chen } 463fcdfe9d2SHuacai Chen if (err) 464fcdfe9d2SHuacai Chen return err; 465fcdfe9d2SHuacai Chen } 466fcdfe9d2SHuacai Chen 467fcdfe9d2SHuacai Chen return 0; 468fcdfe9d2SHuacai Chen } 469fcdfe9d2SHuacai Chen 470fcdfe9d2SHuacai Chen void *module_alloc(unsigned long size) 471fcdfe9d2SHuacai Chen { 472fcdfe9d2SHuacai Chen return __vmalloc_node_range(size, 1, MODULES_VADDR, MODULES_END, 473fcdfe9d2SHuacai Chen GFP_KERNEL, PAGE_KERNEL, 0, NUMA_NO_NODE, __builtin_return_address(0)); 474fcdfe9d2SHuacai Chen } 47519e5eb15SHuacai Chen 47619e5eb15SHuacai Chen int module_finalize(const Elf_Ehdr *hdr, 47719e5eb15SHuacai Chen const Elf_Shdr *sechdrs, struct module *mod) 47819e5eb15SHuacai Chen { 47919e5eb15SHuacai Chen const Elf_Shdr *s, *se; 48019e5eb15SHuacai Chen const char *secstrs = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; 48119e5eb15SHuacai Chen 48219e5eb15SHuacai Chen for (s = sechdrs, se = sechdrs + hdr->e_shnum; s < se; s++) { 48319e5eb15SHuacai Chen if (!strcmp(".altinstructions", secstrs + s->sh_name)) 48419e5eb15SHuacai Chen apply_alternatives((void *)s->sh_addr, (void *)s->sh_addr + s->sh_size); 48519e5eb15SHuacai Chen } 48619e5eb15SHuacai Chen 48719e5eb15SHuacai Chen return 0; 48819e5eb15SHuacai Chen } 489