Commit 257cb251925f854da435cbf79b140984413871ac

Authored by Will Deacon
Committed by Catalin Marinas
1 parent f27bb139c3

arm64: Loadable modules

This patch adds support for loadable modules. Loadable modules are
loaded 64MB below the kernel image due to branch relocation restrictions
(see Documentation/arm64/memory.txt).

Signed-off-by: Will Deacon <will.deacon@arm.com>
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
Acked-by: Tony Lindgren <tony@atomide.com>
Acked-by: Nicolas Pitre <nico@linaro.org>
Acked-by: Olof Johansson <olof@lixom.net>
Acked-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
Acked-by: Arnd Bergmann <arnd@arndb.de>

Showing 2 changed files with 479 additions and 0 deletions Side-by-side Diff

arch/arm64/include/asm/module.h
  1 +/*
  2 + * Copyright (C) 2012 ARM Ltd.
  3 + *
  4 + * This program is free software; you can redistribute it and/or modify
  5 + * it under the terms of the GNU General Public License version 2 as
  6 + * published by the Free Software Foundation.
  7 + *
  8 + * This program is distributed in the hope that it will be useful,
  9 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11 + * GNU General Public License for more details.
  12 + *
  13 + * You should have received a copy of the GNU General Public License
  14 + * along with this program. If not, see <http://www.gnu.org/licenses/>.
  15 + */
  16 +#ifndef __ASM_MODULE_H
  17 +#define __ASM_MODULE_H
  18 +
  19 +#include <asm-generic/module.h>
  20 +
  21 +#define MODULE_ARCH_VERMAGIC "aarch64"
  22 +
  23 +#endif /* __ASM_MODULE_H */
arch/arm64/kernel/module.c
  1 +/*
  2 + * AArch64 loadable module support.
  3 + *
  4 + * Copyright (C) 2012 ARM Limited
  5 + *
  6 + * This program is free software; you can redistribute it and/or modify
  7 + * it under the terms of the GNU General Public License version 2 as
  8 + * published by the Free Software Foundation.
  9 + *
  10 + * This program is distributed in the hope that it will be useful,
  11 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13 + * GNU General Public License for more details.
  14 + *
  15 + * You should have received a copy of the GNU General Public License
  16 + * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17 + *
  18 + * Author: Will Deacon <will.deacon@arm.com>
  19 + */
  20 +
  21 +#include <linux/bitops.h>
  22 +#include <linux/elf.h>
  23 +#include <linux/gfp.h>
  24 +#include <linux/kernel.h>
  25 +#include <linux/mm.h>
  26 +#include <linux/moduleloader.h>
  27 +#include <linux/vmalloc.h>
  28 +
  29 +void *module_alloc(unsigned long size)
  30 +{
  31 + return __vmalloc_node_range(size, 1, MODULES_VADDR, MODULES_END,
  32 + GFP_KERNEL, PAGE_KERNEL_EXEC, -1,
  33 + __builtin_return_address(0));
  34 +}
  35 +
  36 +enum aarch64_reloc_op {
  37 + RELOC_OP_NONE,
  38 + RELOC_OP_ABS,
  39 + RELOC_OP_PREL,
  40 + RELOC_OP_PAGE,
  41 +};
  42 +
  43 +static u64 do_reloc(enum aarch64_reloc_op reloc_op, void *place, u64 val)
  44 +{
  45 + switch (reloc_op) {
  46 + case RELOC_OP_ABS:
  47 + return val;
  48 + case RELOC_OP_PREL:
  49 + return val - (u64)place;
  50 + case RELOC_OP_PAGE:
  51 + return (val & ~0xfff) - ((u64)place & ~0xfff);
  52 + case RELOC_OP_NONE:
  53 + return 0;
  54 + }
  55 +
  56 + pr_err("do_reloc: unknown relocation operation %d\n", reloc_op);
  57 + return 0;
  58 +}
  59 +
  60 +static int reloc_data(enum aarch64_reloc_op op, void *place, u64 val, int len)
  61 +{
  62 + u64 imm_mask = (1 << len) - 1;
  63 + s64 sval = do_reloc(op, place, val);
  64 +
  65 + switch (len) {
  66 + case 16:
  67 + *(s16 *)place = sval;
  68 + break;
  69 + case 32:
  70 + *(s32 *)place = sval;
  71 + break;
  72 + case 64:
  73 + *(s64 *)place = sval;
  74 + break;
  75 + default:
  76 + pr_err("Invalid length (%d) for data relocation\n", len);
  77 + return 0;
  78 + }
  79 +
  80 + /*
  81 + * Extract the upper value bits (including the sign bit) and
  82 + * shift them to bit 0.
  83 + */
  84 + sval = (s64)(sval & ~(imm_mask >> 1)) >> (len - 1);
  85 +
  86 + /*
  87 + * Overflow has occurred if the value is not representable in
  88 + * len bits (i.e the bottom len bits are not sign-extended and
  89 + * the top bits are not all zero).
  90 + */
  91 + if ((u64)(sval + 1) > 2)
  92 + return -ERANGE;
  93 +
  94 + return 0;
  95 +}
  96 +
  97 +enum aarch64_imm_type {
  98 + INSN_IMM_MOVNZ,
  99 + INSN_IMM_MOVK,
  100 + INSN_IMM_ADR,
  101 + INSN_IMM_26,
  102 + INSN_IMM_19,
  103 + INSN_IMM_16,
  104 + INSN_IMM_14,
  105 + INSN_IMM_12,
  106 + INSN_IMM_9,
  107 +};
  108 +
  109 +static u32 encode_insn_immediate(enum aarch64_imm_type type, u32 insn, u64 imm)
  110 +{
  111 + u32 immlo, immhi, lomask, himask, mask;
  112 + int shift;
  113 +
  114 + switch (type) {
  115 + case INSN_IMM_MOVNZ:
  116 + /*
  117 + * For signed MOVW relocations, we have to manipulate the
  118 + * instruction encoding depending on whether or not the
  119 + * immediate is less than zero.
  120 + */
  121 + insn &= ~(3 << 29);
  122 + if ((s64)imm >= 0) {
  123 + /* >=0: Set the instruction to MOVZ (opcode 10b). */
  124 + insn |= 2 << 29;
  125 + } else {
  126 + /*
  127 + * <0: Set the instruction to MOVN (opcode 00b).
  128 + * Since we've masked the opcode already, we
  129 + * don't need to do anything other than
  130 + * inverting the new immediate field.
  131 + */
  132 + imm = ~imm;
  133 + }
  134 + case INSN_IMM_MOVK:
  135 + mask = BIT(16) - 1;
  136 + shift = 5;
  137 + break;
  138 + case INSN_IMM_ADR:
  139 + lomask = 0x3;
  140 + himask = 0x7ffff;
  141 + immlo = imm & lomask;
  142 + imm >>= 2;
  143 + immhi = imm & himask;
  144 + imm = (immlo << 24) | (immhi);
  145 + mask = (lomask << 24) | (himask);
  146 + shift = 5;
  147 + break;
  148 + case INSN_IMM_26:
  149 + mask = BIT(26) - 1;
  150 + shift = 0;
  151 + break;
  152 + case INSN_IMM_19:
  153 + mask = BIT(19) - 1;
  154 + shift = 5;
  155 + break;
  156 + case INSN_IMM_16:
  157 + mask = BIT(16) - 1;
  158 + shift = 5;
  159 + break;
  160 + case INSN_IMM_14:
  161 + mask = BIT(14) - 1;
  162 + shift = 5;
  163 + break;
  164 + case INSN_IMM_12:
  165 + mask = BIT(12) - 1;
  166 + shift = 10;
  167 + break;
  168 + case INSN_IMM_9:
  169 + mask = BIT(9) - 1;
  170 + shift = 12;
  171 + break;
  172 + default:
  173 + pr_err("encode_insn_immediate: unknown immediate encoding %d\n",
  174 + type);
  175 + return 0;
  176 + }
  177 +
  178 + /* Update the immediate field. */
  179 + insn &= ~(mask << shift);
  180 + insn |= (imm & mask) << shift;
  181 +
  182 + return insn;
  183 +}
  184 +
  185 +static int reloc_insn_movw(enum aarch64_reloc_op op, void *place, u64 val,
  186 + int lsb, enum aarch64_imm_type imm_type)
  187 +{
  188 + u64 imm, limit = 0;
  189 + s64 sval;
  190 + u32 insn = *(u32 *)place;
  191 +
  192 + sval = do_reloc(op, place, val);
  193 + sval >>= lsb;
  194 + imm = sval & 0xffff;
  195 +
  196 + /* Update the instruction with the new encoding. */
  197 + *(u32 *)place = encode_insn_immediate(imm_type, insn, imm);
  198 +
  199 + /* Shift out the immediate field. */
  200 + sval >>= 16;
  201 +
  202 + /*
  203 + * For unsigned immediates, the overflow check is straightforward.
  204 + * For signed immediates, the sign bit is actually the bit past the
  205 + * most significant bit of the field.
  206 + * The INSN_IMM_16 immediate type is unsigned.
  207 + */
  208 + if (imm_type != INSN_IMM_16) {
  209 + sval++;
  210 + limit++;
  211 + }
  212 +
  213 + /* Check the upper bits depending on the sign of the immediate. */
  214 + if ((u64)sval > limit)
  215 + return -ERANGE;
  216 +
  217 + return 0;
  218 +}
  219 +
  220 +static int reloc_insn_imm(enum aarch64_reloc_op op, void *place, u64 val,
  221 + int lsb, int len, enum aarch64_imm_type imm_type)
  222 +{
  223 + u64 imm, imm_mask;
  224 + s64 sval;
  225 + u32 insn = *(u32 *)place;
  226 +
  227 + /* Calculate the relocation value. */
  228 + sval = do_reloc(op, place, val);
  229 + sval >>= lsb;
  230 +
  231 + /* Extract the value bits and shift them to bit 0. */
  232 + imm_mask = (BIT(lsb + len) - 1) >> lsb;
  233 + imm = sval & imm_mask;
  234 +
  235 + /* Update the instruction's immediate field. */
  236 + *(u32 *)place = encode_insn_immediate(imm_type, insn, imm);
  237 +
  238 + /*
  239 + * Extract the upper value bits (including the sign bit) and
  240 + * shift them to bit 0.
  241 + */
  242 + sval = (s64)(sval & ~(imm_mask >> 1)) >> (len - 1);
  243 +
  244 + /*
  245 + * Overflow has occurred if the upper bits are not all equal to
  246 + * the sign bit of the value.
  247 + */
  248 + if ((u64)(sval + 1) >= 2)
  249 + return -ERANGE;
  250 +
  251 + return 0;
  252 +}
  253 +
  254 +int apply_relocate_add(Elf64_Shdr *sechdrs,
  255 + const char *strtab,
  256 + unsigned int symindex,
  257 + unsigned int relsec,
  258 + struct module *me)
  259 +{
  260 + unsigned int i;
  261 + int ovf;
  262 + bool overflow_check;
  263 + Elf64_Sym *sym;
  264 + void *loc;
  265 + u64 val;
  266 + Elf64_Rela *rel = (void *)sechdrs[relsec].sh_addr;
  267 +
  268 + for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
  269 + /* loc corresponds to P in the AArch64 ELF document. */
  270 + loc = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
  271 + + rel[i].r_offset;
  272 +
  273 + /* sym is the ELF symbol we're referring to. */
  274 + sym = (Elf64_Sym *)sechdrs[symindex].sh_addr
  275 + + ELF64_R_SYM(rel[i].r_info);
  276 +
  277 + /* val corresponds to (S + A) in the AArch64 ELF document. */
  278 + val = sym->st_value + rel[i].r_addend;
  279 +
  280 + /* Check for overflow by default. */
  281 + overflow_check = true;
  282 +
  283 + /* Perform the static relocation. */
  284 + switch (ELF64_R_TYPE(rel[i].r_info)) {
  285 + /* Null relocations. */
  286 + case R_ARM_NONE:
  287 + case R_AARCH64_NONE:
  288 + ovf = 0;
  289 + break;
  290 +
  291 + /* Data relocations. */
  292 + case R_AARCH64_ABS64:
  293 + overflow_check = false;
  294 + ovf = reloc_data(RELOC_OP_ABS, loc, val, 64);
  295 + break;
  296 + case R_AARCH64_ABS32:
  297 + ovf = reloc_data(RELOC_OP_ABS, loc, val, 32);
  298 + break;
  299 + case R_AARCH64_ABS16:
  300 + ovf = reloc_data(RELOC_OP_ABS, loc, val, 16);
  301 + break;
  302 + case R_AARCH64_PREL64:
  303 + overflow_check = false;
  304 + ovf = reloc_data(RELOC_OP_PREL, loc, val, 64);
  305 + break;
  306 + case R_AARCH64_PREL32:
  307 + ovf = reloc_data(RELOC_OP_PREL, loc, val, 32);
  308 + break;
  309 + case R_AARCH64_PREL16:
  310 + ovf = reloc_data(RELOC_OP_PREL, loc, val, 16);
  311 + break;
  312 +
  313 + /* MOVW instruction relocations. */
  314 + case R_AARCH64_MOVW_UABS_G0_NC:
  315 + overflow_check = false;
  316 + case R_AARCH64_MOVW_UABS_G0:
  317 + ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 0,
  318 + INSN_IMM_16);
  319 + break;
  320 + case R_AARCH64_MOVW_UABS_G1_NC:
  321 + overflow_check = false;
  322 + case R_AARCH64_MOVW_UABS_G1:
  323 + ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 16,
  324 + INSN_IMM_16);
  325 + break;
  326 + case R_AARCH64_MOVW_UABS_G2_NC:
  327 + overflow_check = false;
  328 + case R_AARCH64_MOVW_UABS_G2:
  329 + ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 32,
  330 + INSN_IMM_16);
  331 + break;
  332 + case R_AARCH64_MOVW_UABS_G3:
  333 + /* We're using the top bits so we can't overflow. */
  334 + overflow_check = false;
  335 + ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 48,
  336 + INSN_IMM_16);
  337 + break;
  338 + case R_AARCH64_MOVW_SABS_G0:
  339 + ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 0,
  340 + INSN_IMM_MOVNZ);
  341 + break;
  342 + case R_AARCH64_MOVW_SABS_G1:
  343 + ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 16,
  344 + INSN_IMM_MOVNZ);
  345 + break;
  346 + case R_AARCH64_MOVW_SABS_G2:
  347 + ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 32,
  348 + INSN_IMM_MOVNZ);
  349 + break;
  350 + case R_AARCH64_MOVW_PREL_G0_NC:
  351 + overflow_check = false;
  352 + ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 0,
  353 + INSN_IMM_MOVK);
  354 + break;
  355 + case R_AARCH64_MOVW_PREL_G0:
  356 + ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 0,
  357 + INSN_IMM_MOVNZ);
  358 + break;
  359 + case R_AARCH64_MOVW_PREL_G1_NC:
  360 + overflow_check = false;
  361 + ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 16,
  362 + INSN_IMM_MOVK);
  363 + break;
  364 + case R_AARCH64_MOVW_PREL_G1:
  365 + ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 16,
  366 + INSN_IMM_MOVNZ);
  367 + break;
  368 + case R_AARCH64_MOVW_PREL_G2_NC:
  369 + overflow_check = false;
  370 + ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 32,
  371 + INSN_IMM_MOVK);
  372 + break;
  373 + case R_AARCH64_MOVW_PREL_G2:
  374 + ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 32,
  375 + INSN_IMM_MOVNZ);
  376 + break;
  377 + case R_AARCH64_MOVW_PREL_G3:
  378 + /* We're using the top bits so we can't overflow. */
  379 + overflow_check = false;
  380 + ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 48,
  381 + INSN_IMM_MOVNZ);
  382 + break;
  383 +
  384 + /* Immediate instruction relocations. */
  385 + case R_AARCH64_LD_PREL_LO19:
  386 + ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 19,
  387 + INSN_IMM_19);
  388 + break;
  389 + case R_AARCH64_ADR_PREL_LO21:
  390 + ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 0, 21,
  391 + INSN_IMM_ADR);
  392 + break;
  393 + case R_AARCH64_ADR_PREL_PG_HI21_NC:
  394 + overflow_check = false;
  395 + case R_AARCH64_ADR_PREL_PG_HI21:
  396 + ovf = reloc_insn_imm(RELOC_OP_PAGE, loc, val, 12, 21,
  397 + INSN_IMM_ADR);
  398 + break;
  399 + case R_AARCH64_ADD_ABS_LO12_NC:
  400 + case R_AARCH64_LDST8_ABS_LO12_NC:
  401 + overflow_check = false;
  402 + ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 0, 12,
  403 + INSN_IMM_12);
  404 + break;
  405 + case R_AARCH64_LDST16_ABS_LO12_NC:
  406 + overflow_check = false;
  407 + ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 1, 11,
  408 + INSN_IMM_12);
  409 + break;
  410 + case R_AARCH64_LDST32_ABS_LO12_NC:
  411 + overflow_check = false;
  412 + ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 2, 10,
  413 + INSN_IMM_12);
  414 + break;
  415 + case R_AARCH64_LDST64_ABS_LO12_NC:
  416 + overflow_check = false;
  417 + ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 3, 9,
  418 + INSN_IMM_12);
  419 + break;
  420 + case R_AARCH64_LDST128_ABS_LO12_NC:
  421 + overflow_check = false;
  422 + ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 4, 8,
  423 + INSN_IMM_12);
  424 + break;
  425 + case R_AARCH64_TSTBR14:
  426 + ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 14,
  427 + INSN_IMM_14);
  428 + break;
  429 + case R_AARCH64_CONDBR19:
  430 + ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 19,
  431 + INSN_IMM_19);
  432 + break;
  433 + case R_AARCH64_JUMP26:
  434 + case R_AARCH64_CALL26:
  435 + ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 26,
  436 + INSN_IMM_26);
  437 + break;
  438 +
  439 + default:
  440 + pr_err("module %s: unsupported RELA relocation: %llu\n",
  441 + me->name, ELF64_R_TYPE(rel[i].r_info));
  442 + return -ENOEXEC;
  443 + }
  444 +
  445 + if (overflow_check && ovf == -ERANGE)
  446 + goto overflow;
  447 +
  448 + }
  449 +
  450 + return 0;
  451 +
  452 +overflow:
  453 + pr_err("module %s: overflow in relocation type %d val %Lx\n",
  454 + me->name, (int)ELF64_R_TYPE(rel[i].r_info), val);
  455 + return -ENOEXEC;
  456 +}