Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | // Copyright (C) 2005-2017 Andes Technology Corporation |
| 3 | |
| 4 | #include <linux/module.h> |
| 5 | #include <linux/elf.h> |
| 6 | #include <linux/vmalloc.h> |
| 7 | #include <linux/moduleloader.h> |
Olivier Deprez | 157378f | 2022-04-04 15:47:50 +0200 | [diff] [blame^] | 8 | #include <linux/pgtable.h> |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 9 | |
| 10 | void *module_alloc(unsigned long size) |
| 11 | { |
| 12 | return __vmalloc_node_range(size, 1, MODULES_VADDR, MODULES_END, |
| 13 | GFP_KERNEL, PAGE_KERNEL, 0, NUMA_NO_NODE, |
| 14 | __builtin_return_address(0)); |
| 15 | } |
| 16 | |
| 17 | void module_free(struct module *module, void *region) |
| 18 | { |
| 19 | vfree(region); |
| 20 | } |
| 21 | |
| 22 | int module_frob_arch_sections(Elf_Ehdr * hdr, |
| 23 | Elf_Shdr * sechdrs, |
| 24 | char *secstrings, struct module *mod) |
| 25 | { |
| 26 | return 0; |
| 27 | } |
| 28 | |
| 29 | void do_reloc16(unsigned int val, unsigned int *loc, unsigned int val_mask, |
| 30 | unsigned int val_shift, unsigned int loc_mask, |
| 31 | unsigned int partial_in_place, unsigned int swap) |
| 32 | { |
| 33 | unsigned int tmp = 0, tmp2 = 0; |
| 34 | |
| 35 | __asm__ __volatile__("\tlhi.bi\t%0, [%2], 0\n" |
| 36 | "\tbeqz\t%3, 1f\n" |
| 37 | "\twsbh\t%0, %1\n" |
| 38 | "1:\n":"=r"(tmp):"0"(tmp), "r"(loc), "r"(swap) |
| 39 | ); |
| 40 | |
| 41 | tmp2 = tmp & loc_mask; |
| 42 | if (partial_in_place) { |
| 43 | tmp &= (~loc_mask); |
| 44 | tmp = |
| 45 | tmp2 | ((tmp + ((val & val_mask) >> val_shift)) & val_mask); |
| 46 | } else { |
| 47 | tmp = tmp2 | ((val & val_mask) >> val_shift); |
| 48 | } |
| 49 | |
| 50 | __asm__ __volatile__("\tbeqz\t%3, 2f\n" |
| 51 | "\twsbh\t%0, %1\n" |
| 52 | "2:\n" |
| 53 | "\tshi.bi\t%0, [%2], 0\n":"=r"(tmp):"0"(tmp), |
| 54 | "r"(loc), "r"(swap) |
| 55 | ); |
| 56 | } |
| 57 | |
| 58 | void do_reloc32(unsigned int val, unsigned int *loc, unsigned int val_mask, |
| 59 | unsigned int val_shift, unsigned int loc_mask, |
| 60 | unsigned int partial_in_place, unsigned int swap) |
| 61 | { |
| 62 | unsigned int tmp = 0, tmp2 = 0; |
| 63 | |
| 64 | __asm__ __volatile__("\tlmw.bi\t%0, [%2], %0, 0\n" |
| 65 | "\tbeqz\t%3, 1f\n" |
| 66 | "\twsbh\t%0, %1\n" |
| 67 | "\trotri\t%0, %1, 16\n" |
| 68 | "1:\n":"=r"(tmp):"0"(tmp), "r"(loc), "r"(swap) |
| 69 | ); |
| 70 | |
| 71 | tmp2 = tmp & loc_mask; |
| 72 | if (partial_in_place) { |
| 73 | tmp &= (~loc_mask); |
| 74 | tmp = |
| 75 | tmp2 | ((tmp + ((val & val_mask) >> val_shift)) & val_mask); |
| 76 | } else { |
| 77 | tmp = tmp2 | ((val & val_mask) >> val_shift); |
| 78 | } |
| 79 | |
| 80 | __asm__ __volatile__("\tbeqz\t%3, 2f\n" |
| 81 | "\twsbh\t%0, %1\n" |
| 82 | "\trotri\t%0, %1, 16\n" |
| 83 | "2:\n" |
| 84 | "\tsmw.bi\t%0, [%2], %0, 0\n":"=r"(tmp):"0"(tmp), |
| 85 | "r"(loc), "r"(swap) |
| 86 | ); |
| 87 | } |
| 88 | |
| 89 | static inline int exceed_limit(int offset, unsigned int val_mask, |
| 90 | struct module *module, Elf32_Rela * rel, |
| 91 | unsigned int relindex, unsigned int reloc_order) |
| 92 | { |
| 93 | int abs_off = offset < 0 ? ~offset : offset; |
| 94 | |
| 95 | if (abs_off & (~val_mask)) { |
| 96 | pr_err("\n%s: relocation type %d out of range.\n" |
| 97 | "please rebuild the kernel module with gcc option \"-Wa,-mno-small-text\".\n", |
| 98 | module->name, ELF32_R_TYPE(rel->r_info)); |
| 99 | pr_err("section %d reloc %d offset 0x%x relative 0x%x.\n", |
| 100 | relindex, reloc_order, rel->r_offset, offset); |
| 101 | return true; |
| 102 | } |
| 103 | return false; |
| 104 | } |
| 105 | |
| 106 | #ifdef __NDS32_EL__ |
| 107 | #define NEED_SWAP 1 |
| 108 | #else |
| 109 | #define NEED_SWAP 0 |
| 110 | #endif |
| 111 | |
| 112 | int |
| 113 | apply_relocate_add(Elf32_Shdr * sechdrs, const char *strtab, |
| 114 | unsigned int symindex, unsigned int relindex, |
| 115 | struct module *module) |
| 116 | { |
| 117 | Elf32_Shdr *symsec = sechdrs + symindex; |
| 118 | Elf32_Shdr *relsec = sechdrs + relindex; |
| 119 | Elf32_Shdr *dstsec = sechdrs + relsec->sh_info; |
| 120 | Elf32_Rela *rel = (void *)relsec->sh_addr; |
| 121 | unsigned int i; |
| 122 | |
| 123 | for (i = 0; i < relsec->sh_size / sizeof(Elf32_Rela); i++, rel++) { |
| 124 | Elf32_Addr *loc; |
| 125 | Elf32_Sym *sym; |
| 126 | Elf32_Addr v; |
| 127 | s32 offset; |
| 128 | |
| 129 | offset = ELF32_R_SYM(rel->r_info); |
| 130 | if (offset < 0 |
| 131 | || offset > (symsec->sh_size / sizeof(Elf32_Sym))) { |
| 132 | pr_err("%s: bad relocation\n", module->name); |
| 133 | pr_err("section %d reloc %d\n", relindex, i); |
| 134 | return -ENOEXEC; |
| 135 | } |
| 136 | |
| 137 | sym = ((Elf32_Sym *) symsec->sh_addr) + offset; |
| 138 | |
| 139 | if (rel->r_offset < 0 |
| 140 | || rel->r_offset > dstsec->sh_size - sizeof(u16)) { |
| 141 | pr_err("%s: out of bounds relocation\n", module->name); |
| 142 | pr_err("section %d reloc %d offset 0x%0x size %d\n", |
| 143 | relindex, i, rel->r_offset, dstsec->sh_size); |
| 144 | return -ENOEXEC; |
| 145 | } |
| 146 | |
| 147 | loc = (Elf32_Addr *) (dstsec->sh_addr + rel->r_offset); |
| 148 | v = sym->st_value + rel->r_addend; |
| 149 | |
| 150 | switch (ELF32_R_TYPE(rel->r_info)) { |
| 151 | case R_NDS32_NONE: |
| 152 | case R_NDS32_INSN16: |
| 153 | case R_NDS32_LABEL: |
| 154 | case R_NDS32_LONGCALL1: |
| 155 | case R_NDS32_LONGCALL2: |
| 156 | case R_NDS32_LONGCALL3: |
| 157 | case R_NDS32_LONGCALL4: |
| 158 | case R_NDS32_LONGJUMP1: |
| 159 | case R_NDS32_LONGJUMP2: |
| 160 | case R_NDS32_LONGJUMP3: |
| 161 | case R_NDS32_9_FIXED_RELA: |
| 162 | case R_NDS32_15_FIXED_RELA: |
| 163 | case R_NDS32_17_FIXED_RELA: |
| 164 | case R_NDS32_25_FIXED_RELA: |
| 165 | case R_NDS32_LOADSTORE: |
| 166 | case R_NDS32_DWARF2_OP1_RELA: |
| 167 | case R_NDS32_DWARF2_OP2_RELA: |
| 168 | case R_NDS32_DWARF2_LEB_RELA: |
| 169 | case R_NDS32_RELA_NOP_MIX ... R_NDS32_RELA_NOP_MAX: |
| 170 | break; |
| 171 | |
| 172 | case R_NDS32_32_RELA: |
| 173 | do_reloc32(v, loc, 0xffffffff, 0, 0, 0, 0); |
| 174 | break; |
| 175 | |
| 176 | case R_NDS32_HI20_RELA: |
| 177 | do_reloc32(v, loc, 0xfffff000, 12, 0xfff00000, 0, |
| 178 | NEED_SWAP); |
| 179 | break; |
| 180 | |
| 181 | case R_NDS32_LO12S3_RELA: |
| 182 | do_reloc32(v, loc, 0x00000fff, 3, 0xfffff000, 0, |
| 183 | NEED_SWAP); |
| 184 | break; |
| 185 | |
| 186 | case R_NDS32_LO12S2_RELA: |
| 187 | do_reloc32(v, loc, 0x00000fff, 2, 0xfffff000, 0, |
| 188 | NEED_SWAP); |
| 189 | break; |
| 190 | |
| 191 | case R_NDS32_LO12S1_RELA: |
| 192 | do_reloc32(v, loc, 0x00000fff, 1, 0xfffff000, 0, |
| 193 | NEED_SWAP); |
| 194 | break; |
| 195 | |
| 196 | case R_NDS32_LO12S0_RELA: |
| 197 | case R_NDS32_LO12S0_ORI_RELA: |
| 198 | do_reloc32(v, loc, 0x00000fff, 0, 0xfffff000, 0, |
| 199 | NEED_SWAP); |
| 200 | break; |
| 201 | |
| 202 | case R_NDS32_9_PCREL_RELA: |
| 203 | if (exceed_limit |
| 204 | ((v - (Elf32_Addr) loc), 0x000000ff, module, rel, |
| 205 | relindex, i)) |
| 206 | return -ENOEXEC; |
| 207 | do_reloc16(v - (Elf32_Addr) loc, loc, 0x000001ff, 1, |
| 208 | 0xffffff00, 0, NEED_SWAP); |
| 209 | break; |
| 210 | |
| 211 | case R_NDS32_15_PCREL_RELA: |
| 212 | if (exceed_limit |
| 213 | ((v - (Elf32_Addr) loc), 0x00003fff, module, rel, |
| 214 | relindex, i)) |
| 215 | return -ENOEXEC; |
| 216 | do_reloc32(v - (Elf32_Addr) loc, loc, 0x00007fff, 1, |
| 217 | 0xffffc000, 0, NEED_SWAP); |
| 218 | break; |
| 219 | |
| 220 | case R_NDS32_17_PCREL_RELA: |
| 221 | if (exceed_limit |
| 222 | ((v - (Elf32_Addr) loc), 0x0000ffff, module, rel, |
| 223 | relindex, i)) |
| 224 | return -ENOEXEC; |
| 225 | do_reloc32(v - (Elf32_Addr) loc, loc, 0x0001ffff, 1, |
| 226 | 0xffff0000, 0, NEED_SWAP); |
| 227 | break; |
| 228 | |
| 229 | case R_NDS32_25_PCREL_RELA: |
| 230 | if (exceed_limit |
| 231 | ((v - (Elf32_Addr) loc), 0x00ffffff, module, rel, |
| 232 | relindex, i)) |
| 233 | return -ENOEXEC; |
| 234 | do_reloc32(v - (Elf32_Addr) loc, loc, 0x01ffffff, 1, |
| 235 | 0xff000000, 0, NEED_SWAP); |
| 236 | break; |
| 237 | case R_NDS32_WORD_9_PCREL_RELA: |
| 238 | if (exceed_limit |
| 239 | ((v - (Elf32_Addr) loc), 0x000000ff, module, rel, |
| 240 | relindex, i)) |
| 241 | return -ENOEXEC; |
| 242 | do_reloc32(v - (Elf32_Addr) loc, loc, 0x000001ff, 1, |
| 243 | 0xffffff00, 0, NEED_SWAP); |
| 244 | break; |
| 245 | |
| 246 | case R_NDS32_SDA15S3_RELA: |
| 247 | case R_NDS32_SDA15S2_RELA: |
| 248 | case R_NDS32_SDA15S1_RELA: |
| 249 | case R_NDS32_SDA15S0_RELA: |
| 250 | pr_err("%s: unsupported relocation type %d.\n", |
| 251 | module->name, ELF32_R_TYPE(rel->r_info)); |
| 252 | pr_err |
| 253 | ("Small data section access doesn't work in the kernel space; " |
| 254 | "please rebuild the kernel module with gcc option -mcmodel=large.\n"); |
| 255 | pr_err("section %d reloc %d offset 0x%x size %d\n", |
| 256 | relindex, i, rel->r_offset, dstsec->sh_size); |
| 257 | break; |
| 258 | |
| 259 | default: |
| 260 | pr_err("%s: unsupported relocation type %d.\n", |
| 261 | module->name, ELF32_R_TYPE(rel->r_info)); |
| 262 | pr_err("section %d reloc %d offset 0x%x size %d\n", |
| 263 | relindex, i, rel->r_offset, dstsec->sh_size); |
| 264 | } |
| 265 | } |
| 266 | return 0; |
| 267 | } |
| 268 | |
| 269 | int |
| 270 | module_finalize(const Elf32_Ehdr * hdr, const Elf_Shdr * sechdrs, |
| 271 | struct module *module) |
| 272 | { |
| 273 | return 0; |
| 274 | } |
| 275 | |
| 276 | void module_arch_cleanup(struct module *mod) |
| 277 | { |
| 278 | } |