1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 2e2c0cdfbSPalmer Dabbelt /* 3e2c0cdfbSPalmer Dabbelt * 4e2c0cdfbSPalmer Dabbelt * Copyright (C) 2017 Zihao Yu 5e2c0cdfbSPalmer Dabbelt */ 6e2c0cdfbSPalmer Dabbelt 7e2c0cdfbSPalmer Dabbelt #include <linux/elf.h> 8e2c0cdfbSPalmer Dabbelt #include <linux/err.h> 9e2c0cdfbSPalmer Dabbelt #include <linux/errno.h> 108fd6c514SCharlie Jenkins #include <linux/hashtable.h> 118fd6c514SCharlie Jenkins #include <linux/kernel.h> 128fd6c514SCharlie Jenkins #include <linux/log2.h> 13e2c0cdfbSPalmer Dabbelt #include <linux/moduleloader.h> 140cff8bffSVincent Chen #include <linux/vmalloc.h> 150cff8bffSVincent Chen #include <linux/sizes.h> 16ca5999fdSMike Rapoport #include <linux/pgtable.h> 17a8e91016SHeiko Stuebner #include <asm/alternative.h> 180cff8bffSVincent Chen #include <asm/sections.h> 19e2c0cdfbSPalmer Dabbelt 208fd6c514SCharlie Jenkins struct used_bucket { 218fd6c514SCharlie Jenkins struct list_head head; 228fd6c514SCharlie Jenkins struct hlist_head *bucket; 238fd6c514SCharlie Jenkins }; 248fd6c514SCharlie Jenkins 258fd6c514SCharlie Jenkins struct relocation_head { 268fd6c514SCharlie Jenkins struct hlist_node node; 278fd6c514SCharlie Jenkins struct list_head *rel_entry; 288fd6c514SCharlie Jenkins void *location; 298fd6c514SCharlie Jenkins }; 308fd6c514SCharlie Jenkins 318fd6c514SCharlie Jenkins struct relocation_entry { 328fd6c514SCharlie Jenkins struct list_head head; 338fd6c514SCharlie Jenkins Elf_Addr value; 348fd6c514SCharlie Jenkins unsigned int type; 358fd6c514SCharlie Jenkins }; 368fd6c514SCharlie Jenkins 378fd6c514SCharlie Jenkins struct relocation_handlers { 388fd6c514SCharlie Jenkins int (*reloc_handler)(struct module *me, void *location, Elf_Addr v); 398fd6c514SCharlie Jenkins int (*accumulate_handler)(struct module *me, void *location, 408fd6c514SCharlie Jenkins long buffer); 418fd6c514SCharlie Jenkins }; 428fd6c514SCharlie Jenkins 430966d385SEmil Renner Berthing /* 440966d385SEmil Renner Berthing * The auipc+jalr instruction pair can reach any PC-relative offset 450966d385SEmil Renner Berthing * in the range [-2^31 - 2^11, 2^31 - 2^11) 460966d385SEmil Renner Berthing */ 470966d385SEmil Renner Berthing static bool riscv_insn_valid_32bit_offset(ptrdiff_t val) 480966d385SEmil Renner Berthing { 490966d385SEmil Renner Berthing #ifdef CONFIG_32BIT 500966d385SEmil Renner Berthing return true; 510966d385SEmil Renner Berthing #else 520966d385SEmil Renner Berthing return (-(1L << 31) - (1L << 11)) <= val && val < ((1L << 31) - (1L << 11)); 530966d385SEmil Renner Berthing #endif 540966d385SEmil Renner Berthing } 550966d385SEmil Renner Berthing 568cbe0accSEmil Renner Berthing static int riscv_insn_rmw(void *location, u32 keep, u32 set) 578cbe0accSEmil Renner Berthing { 588cbe0accSEmil Renner Berthing u16 *parcel = location; 598cbe0accSEmil Renner Berthing u32 insn = (u32)le16_to_cpu(parcel[0]) | (u32)le16_to_cpu(parcel[1]) << 16; 608cbe0accSEmil Renner Berthing 618cbe0accSEmil Renner Berthing insn &= keep; 628cbe0accSEmil Renner Berthing insn |= set; 638cbe0accSEmil Renner Berthing 648cbe0accSEmil Renner Berthing parcel[0] = cpu_to_le16(insn); 658cbe0accSEmil Renner Berthing parcel[1] = cpu_to_le16(insn >> 16); 668cbe0accSEmil Renner Berthing return 0; 678cbe0accSEmil Renner Berthing } 688cbe0accSEmil Renner Berthing 698cbe0accSEmil Renner Berthing static int riscv_insn_rvc_rmw(void *location, u16 keep, u16 set) 708cbe0accSEmil Renner Berthing { 718cbe0accSEmil Renner Berthing u16 *parcel = location; 728cbe0accSEmil Renner Berthing u16 insn = le16_to_cpu(*parcel); 738cbe0accSEmil Renner Berthing 748cbe0accSEmil Renner Berthing insn &= keep; 758cbe0accSEmil Renner Berthing insn |= set; 768cbe0accSEmil Renner Berthing 778cbe0accSEmil Renner Berthing *parcel = cpu_to_le16(insn); 788cbe0accSEmil Renner Berthing return 0; 798cbe0accSEmil Renner Berthing } 808cbe0accSEmil Renner Berthing 818cbe0accSEmil Renner Berthing static int apply_r_riscv_32_rela(struct module *me, void *location, Elf_Addr v) 8277aa85deSAndreas Schwab { 8377aa85deSAndreas Schwab if (v != (u32)v) { 8477aa85deSAndreas Schwab pr_err("%s: value %016llx out of range for 32-bit field\n", 85ef3a6140SOlof Johansson me->name, (long long)v); 8677aa85deSAndreas Schwab return -EINVAL; 8777aa85deSAndreas Schwab } 888cbe0accSEmil Renner Berthing *(u32 *)location = v; 8977aa85deSAndreas Schwab return 0; 9077aa85deSAndreas Schwab } 9177aa85deSAndreas Schwab 928cbe0accSEmil Renner Berthing static int apply_r_riscv_64_rela(struct module *me, void *location, Elf_Addr v) 93e2c0cdfbSPalmer Dabbelt { 94e2c0cdfbSPalmer Dabbelt *(u64 *)location = v; 95e2c0cdfbSPalmer Dabbelt return 0; 96e2c0cdfbSPalmer Dabbelt } 97e2c0cdfbSPalmer Dabbelt 988cbe0accSEmil Renner Berthing static int apply_r_riscv_branch_rela(struct module *me, void *location, 99e2c0cdfbSPalmer Dabbelt Elf_Addr v) 100e2c0cdfbSPalmer Dabbelt { 1018cbe0accSEmil Renner Berthing ptrdiff_t offset = (void *)v - location; 102e2c0cdfbSPalmer Dabbelt u32 imm12 = (offset & 0x1000) << (31 - 12); 103e2c0cdfbSPalmer Dabbelt u32 imm11 = (offset & 0x800) >> (11 - 7); 104e2c0cdfbSPalmer Dabbelt u32 imm10_5 = (offset & 0x7e0) << (30 - 10); 105e2c0cdfbSPalmer Dabbelt u32 imm4_1 = (offset & 0x1e) << (11 - 4); 106e2c0cdfbSPalmer Dabbelt 1078cbe0accSEmil Renner Berthing return riscv_insn_rmw(location, 0x1fff07f, imm12 | imm11 | imm10_5 | imm4_1); 108e2c0cdfbSPalmer Dabbelt } 109e2c0cdfbSPalmer Dabbelt 1108cbe0accSEmil Renner Berthing static int apply_r_riscv_jal_rela(struct module *me, void *location, 111e2c0cdfbSPalmer Dabbelt Elf_Addr v) 112e2c0cdfbSPalmer Dabbelt { 1138cbe0accSEmil Renner Berthing ptrdiff_t offset = (void *)v - location; 114e2c0cdfbSPalmer Dabbelt u32 imm20 = (offset & 0x100000) << (31 - 20); 115e2c0cdfbSPalmer Dabbelt u32 imm19_12 = (offset & 0xff000); 116e2c0cdfbSPalmer Dabbelt u32 imm11 = (offset & 0x800) << (20 - 11); 117e2c0cdfbSPalmer Dabbelt u32 imm10_1 = (offset & 0x7fe) << (30 - 10); 118e2c0cdfbSPalmer Dabbelt 1198cbe0accSEmil Renner Berthing return riscv_insn_rmw(location, 0xfff, imm20 | imm19_12 | imm11 | imm10_1); 120e2c0cdfbSPalmer Dabbelt } 121e2c0cdfbSPalmer Dabbelt 1228cbe0accSEmil Renner Berthing static int apply_r_riscv_rvc_branch_rela(struct module *me, void *location, 12356ea45aeSZong Li Elf_Addr v) 12456ea45aeSZong Li { 1258cbe0accSEmil Renner Berthing ptrdiff_t offset = (void *)v - location; 12656ea45aeSZong Li u16 imm8 = (offset & 0x100) << (12 - 8); 12756ea45aeSZong Li u16 imm7_6 = (offset & 0xc0) >> (6 - 5); 12856ea45aeSZong Li u16 imm5 = (offset & 0x20) >> (5 - 2); 12956ea45aeSZong Li u16 imm4_3 = (offset & 0x18) << (12 - 5); 13056ea45aeSZong Li u16 imm2_1 = (offset & 0x6) << (12 - 10); 13156ea45aeSZong Li 1328cbe0accSEmil Renner Berthing return riscv_insn_rvc_rmw(location, 0xe383, 1338cbe0accSEmil Renner Berthing imm8 | imm7_6 | imm5 | imm4_3 | imm2_1); 13456ea45aeSZong Li } 13556ea45aeSZong Li 1368cbe0accSEmil Renner Berthing static int apply_r_riscv_rvc_jump_rela(struct module *me, void *location, 13756ea45aeSZong Li Elf_Addr v) 13856ea45aeSZong Li { 1398cbe0accSEmil Renner Berthing ptrdiff_t offset = (void *)v - location; 14056ea45aeSZong Li u16 imm11 = (offset & 0x800) << (12 - 11); 14156ea45aeSZong Li u16 imm10 = (offset & 0x400) >> (10 - 8); 14256ea45aeSZong Li u16 imm9_8 = (offset & 0x300) << (12 - 11); 14356ea45aeSZong Li u16 imm7 = (offset & 0x80) >> (7 - 6); 14456ea45aeSZong Li u16 imm6 = (offset & 0x40) << (12 - 11); 14556ea45aeSZong Li u16 imm5 = (offset & 0x20) >> (5 - 2); 14656ea45aeSZong Li u16 imm4 = (offset & 0x10) << (12 - 5); 14756ea45aeSZong Li u16 imm3_1 = (offset & 0xe) << (12 - 10); 14856ea45aeSZong Li 1498cbe0accSEmil Renner Berthing return riscv_insn_rvc_rmw(location, 0xe003, 1508cbe0accSEmil Renner Berthing imm11 | imm10 | imm9_8 | imm7 | imm6 | imm5 | imm4 | imm3_1); 15156ea45aeSZong Li } 15256ea45aeSZong Li 1538cbe0accSEmil Renner Berthing static int apply_r_riscv_pcrel_hi20_rela(struct module *me, void *location, 154e2c0cdfbSPalmer Dabbelt Elf_Addr v) 155e2c0cdfbSPalmer Dabbelt { 1568cbe0accSEmil Renner Berthing ptrdiff_t offset = (void *)v - location; 157e2c0cdfbSPalmer Dabbelt 1580966d385SEmil Renner Berthing if (!riscv_insn_valid_32bit_offset(offset)) { 159e2c0cdfbSPalmer Dabbelt pr_err( 160e2c0cdfbSPalmer Dabbelt "%s: target %016llx can not be addressed by the 32-bit offset from PC = %p\n", 161ef3a6140SOlof Johansson me->name, (long long)v, location); 162e2c0cdfbSPalmer Dabbelt return -EINVAL; 163e2c0cdfbSPalmer Dabbelt } 164e2c0cdfbSPalmer Dabbelt 1658cbe0accSEmil Renner Berthing return riscv_insn_rmw(location, 0xfff, (offset + 0x800) & 0xfffff000); 166e2c0cdfbSPalmer Dabbelt } 167e2c0cdfbSPalmer Dabbelt 1688cbe0accSEmil Renner Berthing static int apply_r_riscv_pcrel_lo12_i_rela(struct module *me, void *location, 169e2c0cdfbSPalmer Dabbelt Elf_Addr v) 170e2c0cdfbSPalmer Dabbelt { 171e2c0cdfbSPalmer Dabbelt /* 172e2c0cdfbSPalmer Dabbelt * v is the lo12 value to fill. It is calculated before calling this 173e2c0cdfbSPalmer Dabbelt * handler. 174e2c0cdfbSPalmer Dabbelt */ 1758cbe0accSEmil Renner Berthing return riscv_insn_rmw(location, 0xfffff, (v & 0xfff) << 20); 176e2c0cdfbSPalmer Dabbelt } 177e2c0cdfbSPalmer Dabbelt 1788cbe0accSEmil Renner Berthing static int apply_r_riscv_pcrel_lo12_s_rela(struct module *me, void *location, 179e2c0cdfbSPalmer Dabbelt Elf_Addr v) 180e2c0cdfbSPalmer Dabbelt { 181e2c0cdfbSPalmer Dabbelt /* 182e2c0cdfbSPalmer Dabbelt * v is the lo12 value to fill. It is calculated before calling this 183e2c0cdfbSPalmer Dabbelt * handler. 184e2c0cdfbSPalmer Dabbelt */ 185e2c0cdfbSPalmer Dabbelt u32 imm11_5 = (v & 0xfe0) << (31 - 11); 186e2c0cdfbSPalmer Dabbelt u32 imm4_0 = (v & 0x1f) << (11 - 4); 187e2c0cdfbSPalmer Dabbelt 1888cbe0accSEmil Renner Berthing return riscv_insn_rmw(location, 0x1fff07f, imm11_5 | imm4_0); 189e2c0cdfbSPalmer Dabbelt } 190e2c0cdfbSPalmer Dabbelt 1918cbe0accSEmil Renner Berthing static int apply_r_riscv_hi20_rela(struct module *me, void *location, 192e7456e69SZong Li Elf_Addr v) 193e7456e69SZong Li { 194da4ed378SJoe Perches if (IS_ENABLED(CONFIG_CMODEL_MEDLOW)) { 195e7456e69SZong Li pr_err( 196e7456e69SZong Li "%s: target %016llx can not be addressed by the 32-bit offset from PC = %p\n", 197ef3a6140SOlof Johansson me->name, (long long)v, location); 198e7456e69SZong Li return -EINVAL; 199e7456e69SZong Li } 200e7456e69SZong Li 2018cbe0accSEmil Renner Berthing return riscv_insn_rmw(location, 0xfff, ((s32)v + 0x800) & 0xfffff000); 202e7456e69SZong Li } 203e7456e69SZong Li 2048cbe0accSEmil Renner Berthing static int apply_r_riscv_lo12_i_rela(struct module *me, void *location, 205e7456e69SZong Li Elf_Addr v) 206e7456e69SZong Li { 207e7456e69SZong Li /* Skip medlow checking because of filtering by HI20 already */ 208e7456e69SZong Li s32 hi20 = ((s32)v + 0x800) & 0xfffff000; 209e7456e69SZong Li s32 lo12 = ((s32)v - hi20); 2108cbe0accSEmil Renner Berthing 2118cbe0accSEmil Renner Berthing return riscv_insn_rmw(location, 0xfffff, (lo12 & 0xfff) << 20); 212e7456e69SZong Li } 213e7456e69SZong Li 2148cbe0accSEmil Renner Berthing static int apply_r_riscv_lo12_s_rela(struct module *me, void *location, 215e7456e69SZong Li Elf_Addr v) 216e7456e69SZong Li { 217e7456e69SZong Li /* Skip medlow checking because of filtering by HI20 already */ 218e7456e69SZong Li s32 hi20 = ((s32)v + 0x800) & 0xfffff000; 219e7456e69SZong Li s32 lo12 = ((s32)v - hi20); 220e7456e69SZong Li u32 imm11_5 = (lo12 & 0xfe0) << (31 - 11); 221e7456e69SZong Li u32 imm4_0 = (lo12 & 0x1f) << (11 - 4); 2228cbe0accSEmil Renner Berthing 2238cbe0accSEmil Renner Berthing return riscv_insn_rmw(location, 0x1fff07f, imm11_5 | imm4_0); 224e7456e69SZong Li } 225e7456e69SZong Li 2268cbe0accSEmil Renner Berthing static int apply_r_riscv_got_hi20_rela(struct module *me, void *location, 227da975dd4SZong Li Elf_Addr v) 228da975dd4SZong Li { 2298cbe0accSEmil Renner Berthing ptrdiff_t offset = (void *)v - location; 230da975dd4SZong Li 231da975dd4SZong Li /* Always emit the got entry */ 232da975dd4SZong Li if (IS_ENABLED(CONFIG_MODULE_SECTIONS)) { 2338cbe0accSEmil Renner Berthing offset = (void *)module_emit_got_entry(me, v) - location; 234da975dd4SZong Li } else { 235da975dd4SZong Li pr_err( 236da975dd4SZong Li "%s: can not generate the GOT entry for symbol = %016llx from PC = %p\n", 237ef3a6140SOlof Johansson me->name, (long long)v, location); 238da975dd4SZong Li return -EINVAL; 239da975dd4SZong Li } 240da975dd4SZong Li 2418cbe0accSEmil Renner Berthing return riscv_insn_rmw(location, 0xfff, (offset + 0x800) & 0xfffff000); 242da975dd4SZong Li } 243da975dd4SZong Li 2448cbe0accSEmil Renner Berthing static int apply_r_riscv_call_plt_rela(struct module *me, void *location, 245e2c0cdfbSPalmer Dabbelt Elf_Addr v) 246e2c0cdfbSPalmer Dabbelt { 2478cbe0accSEmil Renner Berthing ptrdiff_t offset = (void *)v - location; 248e2c0cdfbSPalmer Dabbelt u32 hi20, lo12; 249e2c0cdfbSPalmer Dabbelt 2500966d385SEmil Renner Berthing if (!riscv_insn_valid_32bit_offset(offset)) { 251da975dd4SZong Li /* Only emit the plt entry if offset over 32-bit range */ 252da975dd4SZong Li if (IS_ENABLED(CONFIG_MODULE_SECTIONS)) { 2538cbe0accSEmil Renner Berthing offset = (void *)module_emit_plt_entry(me, v) - location; 254da975dd4SZong Li } else { 255e2c0cdfbSPalmer Dabbelt pr_err( 256e2c0cdfbSPalmer Dabbelt "%s: target %016llx can not be addressed by the 32-bit offset from PC = %p\n", 257ef3a6140SOlof Johansson me->name, (long long)v, location); 258e2c0cdfbSPalmer Dabbelt return -EINVAL; 259e2c0cdfbSPalmer Dabbelt } 260da975dd4SZong Li } 261e2c0cdfbSPalmer Dabbelt 262e2c0cdfbSPalmer Dabbelt hi20 = (offset + 0x800) & 0xfffff000; 263e2c0cdfbSPalmer Dabbelt lo12 = (offset - hi20) & 0xfff; 2648cbe0accSEmil Renner Berthing riscv_insn_rmw(location, 0xfff, hi20); 2658cbe0accSEmil Renner Berthing return riscv_insn_rmw(location + 4, 0xfffff, lo12 << 20); 266e2c0cdfbSPalmer Dabbelt } 267e2c0cdfbSPalmer Dabbelt 2688cbe0accSEmil Renner Berthing static int apply_r_riscv_call_rela(struct module *me, void *location, 269e1910c72SZong Li Elf_Addr v) 270e1910c72SZong Li { 2718cbe0accSEmil Renner Berthing ptrdiff_t offset = (void *)v - location; 272e1910c72SZong Li u32 hi20, lo12; 273e1910c72SZong Li 2740966d385SEmil Renner Berthing if (!riscv_insn_valid_32bit_offset(offset)) { 275e1910c72SZong Li pr_err( 276e1910c72SZong Li "%s: target %016llx can not be addressed by the 32-bit offset from PC = %p\n", 277ef3a6140SOlof Johansson me->name, (long long)v, location); 278e1910c72SZong Li return -EINVAL; 279e1910c72SZong Li } 280e1910c72SZong Li 281e1910c72SZong Li hi20 = (offset + 0x800) & 0xfffff000; 282e1910c72SZong Li lo12 = (offset - hi20) & 0xfff; 2838cbe0accSEmil Renner Berthing riscv_insn_rmw(location, 0xfff, hi20); 2848cbe0accSEmil Renner Berthing return riscv_insn_rmw(location + 4, 0xfffff, lo12 << 20); 285e1910c72SZong Li } 286e1910c72SZong Li 2878cbe0accSEmil Renner Berthing static int apply_r_riscv_relax_rela(struct module *me, void *location, 288e2c0cdfbSPalmer Dabbelt Elf_Addr v) 289e2c0cdfbSPalmer Dabbelt { 290e2c0cdfbSPalmer Dabbelt return 0; 291e2c0cdfbSPalmer Dabbelt } 292e2c0cdfbSPalmer Dabbelt 2938cbe0accSEmil Renner Berthing static int apply_r_riscv_align_rela(struct module *me, void *location, 29429e405cdSZong Li Elf_Addr v) 29529e405cdSZong Li { 29629e405cdSZong Li pr_err( 29729e405cdSZong Li "%s: The unexpected relocation type 'R_RISCV_ALIGN' from PC = %p\n", 29829e405cdSZong Li me->name, location); 29929e405cdSZong Li return -EINVAL; 30029e405cdSZong Li } 30129e405cdSZong Li 3028fd6c514SCharlie Jenkins static int apply_r_riscv_add8_rela(struct module *me, void *location, Elf_Addr v) 3038fd6c514SCharlie Jenkins { 3048fd6c514SCharlie Jenkins *(u8 *)location += (u8)v; 3058fd6c514SCharlie Jenkins return 0; 3068fd6c514SCharlie Jenkins } 3078fd6c514SCharlie Jenkins 3088cbe0accSEmil Renner Berthing static int apply_r_riscv_add16_rela(struct module *me, void *location, 3091bc400ffSAndrew Jones Elf_Addr v) 3101bc400ffSAndrew Jones { 3111bc400ffSAndrew Jones *(u16 *)location += (u16)v; 3121bc400ffSAndrew Jones return 0; 3131bc400ffSAndrew Jones } 3141bc400ffSAndrew Jones 3158cbe0accSEmil Renner Berthing static int apply_r_riscv_add32_rela(struct module *me, void *location, 3168e691b16SZong Li Elf_Addr v) 3178e691b16SZong Li { 318781c8fe2SAndreas Schwab *(u32 *)location += (u32)v; 3198e691b16SZong Li return 0; 3208e691b16SZong Li } 3218e691b16SZong Li 3228cbe0accSEmil Renner Berthing static int apply_r_riscv_add64_rela(struct module *me, void *location, 32311a54f42SEmil Renner Berthing Elf_Addr v) 32411a54f42SEmil Renner Berthing { 32511a54f42SEmil Renner Berthing *(u64 *)location += (u64)v; 32611a54f42SEmil Renner Berthing return 0; 32711a54f42SEmil Renner Berthing } 32811a54f42SEmil Renner Berthing 3298fd6c514SCharlie Jenkins static int apply_r_riscv_sub8_rela(struct module *me, void *location, Elf_Addr v) 3308fd6c514SCharlie Jenkins { 3318fd6c514SCharlie Jenkins *(u8 *)location -= (u8)v; 3328fd6c514SCharlie Jenkins return 0; 3338fd6c514SCharlie Jenkins } 3348fd6c514SCharlie Jenkins 3358cbe0accSEmil Renner Berthing static int apply_r_riscv_sub16_rela(struct module *me, void *location, 3361bc400ffSAndrew Jones Elf_Addr v) 3371bc400ffSAndrew Jones { 3381bc400ffSAndrew Jones *(u16 *)location -= (u16)v; 3391bc400ffSAndrew Jones return 0; 3401bc400ffSAndrew Jones } 3411bc400ffSAndrew Jones 3428cbe0accSEmil Renner Berthing static int apply_r_riscv_sub32_rela(struct module *me, void *location, 3434aad074cSZong Li Elf_Addr v) 3444aad074cSZong Li { 345781c8fe2SAndreas Schwab *(u32 *)location -= (u32)v; 3464aad074cSZong Li return 0; 3474aad074cSZong Li } 3484aad074cSZong Li 3498cbe0accSEmil Renner Berthing static int apply_r_riscv_sub64_rela(struct module *me, void *location, 35011a54f42SEmil Renner Berthing Elf_Addr v) 35111a54f42SEmil Renner Berthing { 35211a54f42SEmil Renner Berthing *(u64 *)location -= (u64)v; 35311a54f42SEmil Renner Berthing return 0; 35411a54f42SEmil Renner Berthing } 35511a54f42SEmil Renner Berthing 3568fd6c514SCharlie Jenkins static int dynamic_linking_not_supported(struct module *me, void *location, 3578fd6c514SCharlie Jenkins Elf_Addr v) 3588fd6c514SCharlie Jenkins { 3598fd6c514SCharlie Jenkins pr_err("%s: Dynamic linking not supported in kernel modules PC = %p\n", 3608fd6c514SCharlie Jenkins me->name, location); 3618fd6c514SCharlie Jenkins return -EINVAL; 3628fd6c514SCharlie Jenkins } 3638fd6c514SCharlie Jenkins 3648fd6c514SCharlie Jenkins static int tls_not_supported(struct module *me, void *location, Elf_Addr v) 3658fd6c514SCharlie Jenkins { 3668fd6c514SCharlie Jenkins pr_err("%s: Thread local storage not supported in kernel modules PC = %p\n", 3678fd6c514SCharlie Jenkins me->name, location); 3688fd6c514SCharlie Jenkins return -EINVAL; 3698fd6c514SCharlie Jenkins } 3708fd6c514SCharlie Jenkins 3718fd6c514SCharlie Jenkins static int apply_r_riscv_sub6_rela(struct module *me, void *location, Elf_Addr v) 3728fd6c514SCharlie Jenkins { 3738fd6c514SCharlie Jenkins u8 *byte = location; 3748fd6c514SCharlie Jenkins u8 value = v; 3758fd6c514SCharlie Jenkins 3768fd6c514SCharlie Jenkins *byte = (*byte - (value & 0x3f)) & 0x3f; 3778fd6c514SCharlie Jenkins return 0; 3788fd6c514SCharlie Jenkins } 3798fd6c514SCharlie Jenkins 3808fd6c514SCharlie Jenkins static int apply_r_riscv_set6_rela(struct module *me, void *location, Elf_Addr v) 3818fd6c514SCharlie Jenkins { 3828fd6c514SCharlie Jenkins u8 *byte = location; 3838fd6c514SCharlie Jenkins u8 value = v; 3848fd6c514SCharlie Jenkins 3858fd6c514SCharlie Jenkins *byte = (*byte & 0xc0) | (value & 0x3f); 3868fd6c514SCharlie Jenkins return 0; 3878fd6c514SCharlie Jenkins } 3888fd6c514SCharlie Jenkins 3898fd6c514SCharlie Jenkins static int apply_r_riscv_set8_rela(struct module *me, void *location, Elf_Addr v) 3908fd6c514SCharlie Jenkins { 3918fd6c514SCharlie Jenkins *(u8 *)location = (u8)v; 3928fd6c514SCharlie Jenkins return 0; 3938fd6c514SCharlie Jenkins } 3948fd6c514SCharlie Jenkins 3958fd6c514SCharlie Jenkins static int apply_r_riscv_set16_rela(struct module *me, void *location, 3968fd6c514SCharlie Jenkins Elf_Addr v) 3978fd6c514SCharlie Jenkins { 3988fd6c514SCharlie Jenkins *(u16 *)location = (u16)v; 3998fd6c514SCharlie Jenkins return 0; 4008fd6c514SCharlie Jenkins } 4018fd6c514SCharlie Jenkins 4028fd6c514SCharlie Jenkins static int apply_r_riscv_set32_rela(struct module *me, void *location, 4038fd6c514SCharlie Jenkins Elf_Addr v) 4048fd6c514SCharlie Jenkins { 4058fd6c514SCharlie Jenkins *(u32 *)location = (u32)v; 4068fd6c514SCharlie Jenkins return 0; 4078fd6c514SCharlie Jenkins } 4088fd6c514SCharlie Jenkins 4098fd6c514SCharlie Jenkins static int apply_r_riscv_32_pcrel_rela(struct module *me, void *location, 4108fd6c514SCharlie Jenkins Elf_Addr v) 4118fd6c514SCharlie Jenkins { 4128fd6c514SCharlie Jenkins *(u32 *)location = v - (uintptr_t)location; 4138fd6c514SCharlie Jenkins return 0; 4148fd6c514SCharlie Jenkins } 4158fd6c514SCharlie Jenkins 4168fd6c514SCharlie Jenkins static int apply_r_riscv_plt32_rela(struct module *me, void *location, 4178fd6c514SCharlie Jenkins Elf_Addr v) 4188fd6c514SCharlie Jenkins { 4198fd6c514SCharlie Jenkins ptrdiff_t offset = (void *)v - location; 4208fd6c514SCharlie Jenkins 4218fd6c514SCharlie Jenkins if (!riscv_insn_valid_32bit_offset(offset)) { 4228fd6c514SCharlie Jenkins /* Only emit the plt entry if offset over 32-bit range */ 4238fd6c514SCharlie Jenkins if (IS_ENABLED(CONFIG_MODULE_SECTIONS)) { 4248fd6c514SCharlie Jenkins offset = (void *)module_emit_plt_entry(me, v) - location; 4258fd6c514SCharlie Jenkins } else { 4268fd6c514SCharlie Jenkins pr_err("%s: target %016llx can not be addressed by the 32-bit offset from PC = %p\n", 4278fd6c514SCharlie Jenkins me->name, (long long)v, location); 4288fd6c514SCharlie Jenkins return -EINVAL; 4298fd6c514SCharlie Jenkins } 4308fd6c514SCharlie Jenkins } 4318fd6c514SCharlie Jenkins 4328fd6c514SCharlie Jenkins *(u32 *)location = (u32)offset; 4338fd6c514SCharlie Jenkins return 0; 4348fd6c514SCharlie Jenkins } 4358fd6c514SCharlie Jenkins 4368fd6c514SCharlie Jenkins static int apply_r_riscv_set_uleb128(struct module *me, void *location, Elf_Addr v) 4378fd6c514SCharlie Jenkins { 4388fd6c514SCharlie Jenkins *(long *)location = v; 4398fd6c514SCharlie Jenkins return 0; 4408fd6c514SCharlie Jenkins } 4418fd6c514SCharlie Jenkins 4428fd6c514SCharlie Jenkins static int apply_r_riscv_sub_uleb128(struct module *me, void *location, Elf_Addr v) 4438fd6c514SCharlie Jenkins { 4448fd6c514SCharlie Jenkins *(long *)location -= v; 4458fd6c514SCharlie Jenkins return 0; 4468fd6c514SCharlie Jenkins } 4478fd6c514SCharlie Jenkins 4488fd6c514SCharlie Jenkins static int apply_6_bit_accumulation(struct module *me, void *location, long buffer) 4498fd6c514SCharlie Jenkins { 4508fd6c514SCharlie Jenkins u8 *byte = location; 4518fd6c514SCharlie Jenkins u8 value = buffer; 4528fd6c514SCharlie Jenkins 4538fd6c514SCharlie Jenkins if (buffer > 0x3f) { 4548fd6c514SCharlie Jenkins pr_err("%s: value %ld out of range for 6-bit relocation.\n", 4558fd6c514SCharlie Jenkins me->name, buffer); 4568fd6c514SCharlie Jenkins return -EINVAL; 4578fd6c514SCharlie Jenkins } 4588fd6c514SCharlie Jenkins 4598fd6c514SCharlie Jenkins *byte = (*byte & 0xc0) | (value & 0x3f); 4608fd6c514SCharlie Jenkins return 0; 4618fd6c514SCharlie Jenkins } 4628fd6c514SCharlie Jenkins 4638fd6c514SCharlie Jenkins static int apply_8_bit_accumulation(struct module *me, void *location, long buffer) 4648fd6c514SCharlie Jenkins { 4658fd6c514SCharlie Jenkins if (buffer > U8_MAX) { 4668fd6c514SCharlie Jenkins pr_err("%s: value %ld out of range for 8-bit relocation.\n", 4678fd6c514SCharlie Jenkins me->name, buffer); 4688fd6c514SCharlie Jenkins return -EINVAL; 4698fd6c514SCharlie Jenkins } 4708fd6c514SCharlie Jenkins *(u8 *)location = (u8)buffer; 4718fd6c514SCharlie Jenkins return 0; 4728fd6c514SCharlie Jenkins } 4738fd6c514SCharlie Jenkins 4748fd6c514SCharlie Jenkins static int apply_16_bit_accumulation(struct module *me, void *location, long buffer) 4758fd6c514SCharlie Jenkins { 4768fd6c514SCharlie Jenkins if (buffer > U16_MAX) { 4778fd6c514SCharlie Jenkins pr_err("%s: value %ld out of range for 16-bit relocation.\n", 4788fd6c514SCharlie Jenkins me->name, buffer); 4798fd6c514SCharlie Jenkins return -EINVAL; 4808fd6c514SCharlie Jenkins } 4818fd6c514SCharlie Jenkins *(u16 *)location = (u16)buffer; 4828fd6c514SCharlie Jenkins return 0; 4838fd6c514SCharlie Jenkins } 4848fd6c514SCharlie Jenkins 4858fd6c514SCharlie Jenkins static int apply_32_bit_accumulation(struct module *me, void *location, long buffer) 4868fd6c514SCharlie Jenkins { 4878fd6c514SCharlie Jenkins if (buffer > U32_MAX) { 4888fd6c514SCharlie Jenkins pr_err("%s: value %ld out of range for 32-bit relocation.\n", 4898fd6c514SCharlie Jenkins me->name, buffer); 4908fd6c514SCharlie Jenkins return -EINVAL; 4918fd6c514SCharlie Jenkins } 4928fd6c514SCharlie Jenkins *(u32 *)location = (u32)buffer; 4938fd6c514SCharlie Jenkins return 0; 4948fd6c514SCharlie Jenkins } 4958fd6c514SCharlie Jenkins 4968fd6c514SCharlie Jenkins static int apply_64_bit_accumulation(struct module *me, void *location, long buffer) 4978fd6c514SCharlie Jenkins { 4988fd6c514SCharlie Jenkins *(u64 *)location = (u64)buffer; 4998fd6c514SCharlie Jenkins return 0; 5008fd6c514SCharlie Jenkins } 5018fd6c514SCharlie Jenkins 5028fd6c514SCharlie Jenkins static int apply_uleb128_accumulation(struct module *me, void *location, long buffer) 5038fd6c514SCharlie Jenkins { 5048fd6c514SCharlie Jenkins /* 5058fd6c514SCharlie Jenkins * ULEB128 is a variable length encoding. Encode the buffer into 5068fd6c514SCharlie Jenkins * the ULEB128 data format. 5078fd6c514SCharlie Jenkins */ 5088fd6c514SCharlie Jenkins u8 *p = location; 5098fd6c514SCharlie Jenkins 5108fd6c514SCharlie Jenkins while (buffer != 0) { 5118fd6c514SCharlie Jenkins u8 value = buffer & 0x7f; 5128fd6c514SCharlie Jenkins 5138fd6c514SCharlie Jenkins buffer >>= 7; 5148fd6c514SCharlie Jenkins value |= (!!buffer) << 7; 5158fd6c514SCharlie Jenkins 5168fd6c514SCharlie Jenkins *p++ = value; 5178fd6c514SCharlie Jenkins } 5188fd6c514SCharlie Jenkins return 0; 5198fd6c514SCharlie Jenkins } 5208fd6c514SCharlie Jenkins 5218fd6c514SCharlie Jenkins /* 5228fd6c514SCharlie Jenkins * Relocations defined in the riscv-elf-psabi-doc. 5238fd6c514SCharlie Jenkins * This handles static linking only. 5248fd6c514SCharlie Jenkins */ 5258fd6c514SCharlie Jenkins static const struct relocation_handlers reloc_handlers[] = { 52628ea54baSPalmer Dabbelt [R_RISCV_32] = { .reloc_handler = apply_r_riscv_32_rela }, 52728ea54baSPalmer Dabbelt [R_RISCV_64] = { .reloc_handler = apply_r_riscv_64_rela }, 52828ea54baSPalmer Dabbelt [R_RISCV_RELATIVE] = { .reloc_handler = dynamic_linking_not_supported }, 52928ea54baSPalmer Dabbelt [R_RISCV_COPY] = { .reloc_handler = dynamic_linking_not_supported }, 53028ea54baSPalmer Dabbelt [R_RISCV_JUMP_SLOT] = { .reloc_handler = dynamic_linking_not_supported }, 53128ea54baSPalmer Dabbelt [R_RISCV_TLS_DTPMOD32] = { .reloc_handler = dynamic_linking_not_supported }, 53228ea54baSPalmer Dabbelt [R_RISCV_TLS_DTPMOD64] = { .reloc_handler = dynamic_linking_not_supported }, 53328ea54baSPalmer Dabbelt [R_RISCV_TLS_DTPREL32] = { .reloc_handler = dynamic_linking_not_supported }, 53428ea54baSPalmer Dabbelt [R_RISCV_TLS_DTPREL64] = { .reloc_handler = dynamic_linking_not_supported }, 53528ea54baSPalmer Dabbelt [R_RISCV_TLS_TPREL32] = { .reloc_handler = dynamic_linking_not_supported }, 53628ea54baSPalmer Dabbelt [R_RISCV_TLS_TPREL64] = { .reloc_handler = dynamic_linking_not_supported }, 5378fd6c514SCharlie Jenkins /* 12-15 undefined */ 53828ea54baSPalmer Dabbelt [R_RISCV_BRANCH] = { .reloc_handler = apply_r_riscv_branch_rela }, 53928ea54baSPalmer Dabbelt [R_RISCV_JAL] = { .reloc_handler = apply_r_riscv_jal_rela }, 54028ea54baSPalmer Dabbelt [R_RISCV_CALL] = { .reloc_handler = apply_r_riscv_call_rela }, 54128ea54baSPalmer Dabbelt [R_RISCV_CALL_PLT] = { .reloc_handler = apply_r_riscv_call_plt_rela }, 54228ea54baSPalmer Dabbelt [R_RISCV_GOT_HI20] = { .reloc_handler = apply_r_riscv_got_hi20_rela }, 54328ea54baSPalmer Dabbelt [R_RISCV_TLS_GOT_HI20] = { .reloc_handler = tls_not_supported }, 54428ea54baSPalmer Dabbelt [R_RISCV_TLS_GD_HI20] = { .reloc_handler = tls_not_supported }, 54528ea54baSPalmer Dabbelt [R_RISCV_PCREL_HI20] = { .reloc_handler = apply_r_riscv_pcrel_hi20_rela }, 54628ea54baSPalmer Dabbelt [R_RISCV_PCREL_LO12_I] = { .reloc_handler = apply_r_riscv_pcrel_lo12_i_rela }, 54728ea54baSPalmer Dabbelt [R_RISCV_PCREL_LO12_S] = { .reloc_handler = apply_r_riscv_pcrel_lo12_s_rela }, 54828ea54baSPalmer Dabbelt [R_RISCV_HI20] = { .reloc_handler = apply_r_riscv_hi20_rela }, 54928ea54baSPalmer Dabbelt [R_RISCV_LO12_I] = { .reloc_handler = apply_r_riscv_lo12_i_rela }, 55028ea54baSPalmer Dabbelt [R_RISCV_LO12_S] = { .reloc_handler = apply_r_riscv_lo12_s_rela }, 55128ea54baSPalmer Dabbelt [R_RISCV_TPREL_HI20] = { .reloc_handler = tls_not_supported }, 55228ea54baSPalmer Dabbelt [R_RISCV_TPREL_LO12_I] = { .reloc_handler = tls_not_supported }, 55328ea54baSPalmer Dabbelt [R_RISCV_TPREL_LO12_S] = { .reloc_handler = tls_not_supported }, 55428ea54baSPalmer Dabbelt [R_RISCV_TPREL_ADD] = { .reloc_handler = tls_not_supported }, 55528ea54baSPalmer Dabbelt [R_RISCV_ADD8] = { .reloc_handler = apply_r_riscv_add8_rela, 55628ea54baSPalmer Dabbelt .accumulate_handler = apply_8_bit_accumulation }, 55728ea54baSPalmer Dabbelt [R_RISCV_ADD16] = { .reloc_handler = apply_r_riscv_add16_rela, 55828ea54baSPalmer Dabbelt .accumulate_handler = apply_16_bit_accumulation }, 55928ea54baSPalmer Dabbelt [R_RISCV_ADD32] = { .reloc_handler = apply_r_riscv_add32_rela, 56028ea54baSPalmer Dabbelt .accumulate_handler = apply_32_bit_accumulation }, 56128ea54baSPalmer Dabbelt [R_RISCV_ADD64] = { .reloc_handler = apply_r_riscv_add64_rela, 56228ea54baSPalmer Dabbelt .accumulate_handler = apply_64_bit_accumulation }, 56328ea54baSPalmer Dabbelt [R_RISCV_SUB8] = { .reloc_handler = apply_r_riscv_sub8_rela, 56428ea54baSPalmer Dabbelt .accumulate_handler = apply_8_bit_accumulation }, 56528ea54baSPalmer Dabbelt [R_RISCV_SUB16] = { .reloc_handler = apply_r_riscv_sub16_rela, 56628ea54baSPalmer Dabbelt .accumulate_handler = apply_16_bit_accumulation }, 56728ea54baSPalmer Dabbelt [R_RISCV_SUB32] = { .reloc_handler = apply_r_riscv_sub32_rela, 56828ea54baSPalmer Dabbelt .accumulate_handler = apply_32_bit_accumulation }, 56928ea54baSPalmer Dabbelt [R_RISCV_SUB64] = { .reloc_handler = apply_r_riscv_sub64_rela, 57028ea54baSPalmer Dabbelt .accumulate_handler = apply_64_bit_accumulation }, 5718fd6c514SCharlie Jenkins /* 41-42 reserved for future standard use */ 57228ea54baSPalmer Dabbelt [R_RISCV_ALIGN] = { .reloc_handler = apply_r_riscv_align_rela }, 57328ea54baSPalmer Dabbelt [R_RISCV_RVC_BRANCH] = { .reloc_handler = apply_r_riscv_rvc_branch_rela }, 57428ea54baSPalmer Dabbelt [R_RISCV_RVC_JUMP] = { .reloc_handler = apply_r_riscv_rvc_jump_rela }, 5758fd6c514SCharlie Jenkins /* 46-50 reserved for future standard use */ 57628ea54baSPalmer Dabbelt [R_RISCV_RELAX] = { .reloc_handler = apply_r_riscv_relax_rela }, 57728ea54baSPalmer Dabbelt [R_RISCV_SUB6] = { .reloc_handler = apply_r_riscv_sub6_rela, 57828ea54baSPalmer Dabbelt .accumulate_handler = apply_6_bit_accumulation }, 57928ea54baSPalmer Dabbelt [R_RISCV_SET6] = { .reloc_handler = apply_r_riscv_set6_rela, 58028ea54baSPalmer Dabbelt .accumulate_handler = apply_6_bit_accumulation }, 58128ea54baSPalmer Dabbelt [R_RISCV_SET8] = { .reloc_handler = apply_r_riscv_set8_rela, 58228ea54baSPalmer Dabbelt .accumulate_handler = apply_8_bit_accumulation }, 58328ea54baSPalmer Dabbelt [R_RISCV_SET16] = { .reloc_handler = apply_r_riscv_set16_rela, 58428ea54baSPalmer Dabbelt .accumulate_handler = apply_16_bit_accumulation }, 58528ea54baSPalmer Dabbelt [R_RISCV_SET32] = { .reloc_handler = apply_r_riscv_set32_rela, 58628ea54baSPalmer Dabbelt .accumulate_handler = apply_32_bit_accumulation }, 58728ea54baSPalmer Dabbelt [R_RISCV_32_PCREL] = { .reloc_handler = apply_r_riscv_32_pcrel_rela }, 58828ea54baSPalmer Dabbelt [R_RISCV_IRELATIVE] = { .reloc_handler = dynamic_linking_not_supported }, 58928ea54baSPalmer Dabbelt [R_RISCV_PLT32] = { .reloc_handler = apply_r_riscv_plt32_rela }, 59028ea54baSPalmer Dabbelt [R_RISCV_SET_ULEB128] = { .reloc_handler = apply_r_riscv_set_uleb128, 59128ea54baSPalmer Dabbelt .accumulate_handler = apply_uleb128_accumulation }, 59228ea54baSPalmer Dabbelt [R_RISCV_SUB_ULEB128] = { .reloc_handler = apply_r_riscv_sub_uleb128, 59328ea54baSPalmer Dabbelt .accumulate_handler = apply_uleb128_accumulation }, 5948fd6c514SCharlie Jenkins /* 62-191 reserved for future standard use */ 5958fd6c514SCharlie Jenkins /* 192-255 nonstandard ABI extensions */ 596e2c0cdfbSPalmer Dabbelt }; 597e2c0cdfbSPalmer Dabbelt 598*d8792a57SCharlie Jenkins static void 599*d8792a57SCharlie Jenkins process_accumulated_relocations(struct module *me, 600*d8792a57SCharlie Jenkins struct hlist_head **relocation_hashtable, 601*d8792a57SCharlie Jenkins struct list_head *used_buckets_list) 6028fd6c514SCharlie Jenkins { 6038fd6c514SCharlie Jenkins /* 6048fd6c514SCharlie Jenkins * Only ADD/SUB/SET/ULEB128 should end up here. 6058fd6c514SCharlie Jenkins * 6068fd6c514SCharlie Jenkins * Each bucket may have more than one relocation location. All 6078fd6c514SCharlie Jenkins * relocations for a location are stored in a list in a bucket. 6088fd6c514SCharlie Jenkins * 6098fd6c514SCharlie Jenkins * Relocations are applied to a temp variable before being stored to the 6108fd6c514SCharlie Jenkins * provided location to check for overflow. This also allows ULEB128 to 6118fd6c514SCharlie Jenkins * properly decide how many entries are needed before storing to 6128fd6c514SCharlie Jenkins * location. The final value is stored into location using the handler 6138fd6c514SCharlie Jenkins * for the last relocation to an address. 6148fd6c514SCharlie Jenkins * 6158fd6c514SCharlie Jenkins * Three layers of indexing: 6168fd6c514SCharlie Jenkins * - Each of the buckets in use 6178fd6c514SCharlie Jenkins * - Groups of relocations in each bucket by location address 6188fd6c514SCharlie Jenkins * - Each relocation entry for a location address 6198fd6c514SCharlie Jenkins */ 6208fd6c514SCharlie Jenkins struct used_bucket *bucket_iter; 621*d8792a57SCharlie Jenkins struct used_bucket *bucket_iter_tmp; 6228fd6c514SCharlie Jenkins struct relocation_head *rel_head_iter; 623*d8792a57SCharlie Jenkins struct hlist_node *rel_head_iter_tmp; 6248fd6c514SCharlie Jenkins struct relocation_entry *rel_entry_iter; 625*d8792a57SCharlie Jenkins struct relocation_entry *rel_entry_iter_tmp; 6268fd6c514SCharlie Jenkins int curr_type; 6278fd6c514SCharlie Jenkins void *location; 6288fd6c514SCharlie Jenkins long buffer; 6298fd6c514SCharlie Jenkins 630*d8792a57SCharlie Jenkins list_for_each_entry_safe(bucket_iter, bucket_iter_tmp, 631*d8792a57SCharlie Jenkins used_buckets_list, head) { 632*d8792a57SCharlie Jenkins hlist_for_each_entry_safe(rel_head_iter, rel_head_iter_tmp, 633*d8792a57SCharlie Jenkins bucket_iter->bucket, node) { 6348fd6c514SCharlie Jenkins buffer = 0; 6358fd6c514SCharlie Jenkins location = rel_head_iter->location; 636*d8792a57SCharlie Jenkins list_for_each_entry_safe(rel_entry_iter, 637*d8792a57SCharlie Jenkins rel_entry_iter_tmp, 638*d8792a57SCharlie Jenkins rel_head_iter->rel_entry, 639*d8792a57SCharlie Jenkins head) { 6408fd6c514SCharlie Jenkins curr_type = rel_entry_iter->type; 6418fd6c514SCharlie Jenkins reloc_handlers[curr_type].reloc_handler( 6428fd6c514SCharlie Jenkins me, &buffer, rel_entry_iter->value); 6438fd6c514SCharlie Jenkins kfree(rel_entry_iter); 6448fd6c514SCharlie Jenkins } 6458fd6c514SCharlie Jenkins reloc_handlers[curr_type].accumulate_handler( 6468fd6c514SCharlie Jenkins me, location, buffer); 6478fd6c514SCharlie Jenkins kfree(rel_head_iter); 6488fd6c514SCharlie Jenkins } 6498fd6c514SCharlie Jenkins kfree(bucket_iter); 6508fd6c514SCharlie Jenkins } 6518fd6c514SCharlie Jenkins 652*d8792a57SCharlie Jenkins kfree(*relocation_hashtable); 6538fd6c514SCharlie Jenkins } 6548fd6c514SCharlie Jenkins 655*d8792a57SCharlie Jenkins static int add_relocation_to_accumulate(struct module *me, int type, 656*d8792a57SCharlie Jenkins void *location, 657*d8792a57SCharlie Jenkins unsigned int hashtable_bits, Elf_Addr v, 658*d8792a57SCharlie Jenkins struct hlist_head *relocation_hashtable, 659*d8792a57SCharlie Jenkins struct list_head *used_buckets_list) 6608fd6c514SCharlie Jenkins { 6618fd6c514SCharlie Jenkins struct relocation_entry *entry; 6628fd6c514SCharlie Jenkins struct relocation_head *rel_head; 6638fd6c514SCharlie Jenkins struct hlist_head *current_head; 6648fd6c514SCharlie Jenkins struct used_bucket *bucket; 6658fd6c514SCharlie Jenkins unsigned long hash; 6668fd6c514SCharlie Jenkins 6678fd6c514SCharlie Jenkins entry = kmalloc(sizeof(*entry), GFP_KERNEL); 668*d8792a57SCharlie Jenkins 669*d8792a57SCharlie Jenkins if (!entry) 670*d8792a57SCharlie Jenkins return -ENOMEM; 671*d8792a57SCharlie Jenkins 6728fd6c514SCharlie Jenkins INIT_LIST_HEAD(&entry->head); 6738fd6c514SCharlie Jenkins entry->type = type; 6748fd6c514SCharlie Jenkins entry->value = v; 6758fd6c514SCharlie Jenkins 6768fd6c514SCharlie Jenkins hash = hash_min((uintptr_t)location, hashtable_bits); 6778fd6c514SCharlie Jenkins 6788fd6c514SCharlie Jenkins current_head = &relocation_hashtable[hash]; 6798fd6c514SCharlie Jenkins 680*d8792a57SCharlie Jenkins /* 681*d8792a57SCharlie Jenkins * Search for the relocation_head for the relocations that happen at the 682*d8792a57SCharlie Jenkins * provided location 683*d8792a57SCharlie Jenkins */ 6848fd6c514SCharlie Jenkins bool found = false; 6858fd6c514SCharlie Jenkins struct relocation_head *rel_head_iter; 6868fd6c514SCharlie Jenkins 6878fd6c514SCharlie Jenkins hlist_for_each_entry(rel_head_iter, current_head, node) { 6888fd6c514SCharlie Jenkins if (rel_head_iter->location == location) { 6898fd6c514SCharlie Jenkins found = true; 6908fd6c514SCharlie Jenkins rel_head = rel_head_iter; 6918fd6c514SCharlie Jenkins break; 6928fd6c514SCharlie Jenkins } 6938fd6c514SCharlie Jenkins } 6948fd6c514SCharlie Jenkins 695*d8792a57SCharlie Jenkins /* 696*d8792a57SCharlie Jenkins * If there has not yet been any relocations at the provided location, 697*d8792a57SCharlie Jenkins * create a relocation_head for that location and populate it with this 698*d8792a57SCharlie Jenkins * relocation_entry. 699*d8792a57SCharlie Jenkins */ 7008fd6c514SCharlie Jenkins if (!found) { 7018fd6c514SCharlie Jenkins rel_head = kmalloc(sizeof(*rel_head), GFP_KERNEL); 702*d8792a57SCharlie Jenkins 703*d8792a57SCharlie Jenkins if (!rel_head) { 704*d8792a57SCharlie Jenkins kfree(entry); 705*d8792a57SCharlie Jenkins return -ENOMEM; 706*d8792a57SCharlie Jenkins } 707*d8792a57SCharlie Jenkins 7088fd6c514SCharlie Jenkins rel_head->rel_entry = 7098fd6c514SCharlie Jenkins kmalloc(sizeof(struct list_head), GFP_KERNEL); 710*d8792a57SCharlie Jenkins 711*d8792a57SCharlie Jenkins if (!rel_head->rel_entry) { 712*d8792a57SCharlie Jenkins kfree(entry); 713*d8792a57SCharlie Jenkins kfree(rel_head); 714*d8792a57SCharlie Jenkins return -ENOMEM; 715*d8792a57SCharlie Jenkins } 716*d8792a57SCharlie Jenkins 7178fd6c514SCharlie Jenkins INIT_LIST_HEAD(rel_head->rel_entry); 7188fd6c514SCharlie Jenkins rel_head->location = location; 7198fd6c514SCharlie Jenkins INIT_HLIST_NODE(&rel_head->node); 7208fd6c514SCharlie Jenkins if (!current_head->first) { 7218fd6c514SCharlie Jenkins bucket = 7228fd6c514SCharlie Jenkins kmalloc(sizeof(struct used_bucket), GFP_KERNEL); 723*d8792a57SCharlie Jenkins 724*d8792a57SCharlie Jenkins if (!bucket) { 725*d8792a57SCharlie Jenkins kfree(entry); 726*d8792a57SCharlie Jenkins kfree(rel_head); 727*d8792a57SCharlie Jenkins kfree(rel_head->rel_entry); 728*d8792a57SCharlie Jenkins return -ENOMEM; 729*d8792a57SCharlie Jenkins } 730*d8792a57SCharlie Jenkins 7318fd6c514SCharlie Jenkins INIT_LIST_HEAD(&bucket->head); 7328fd6c514SCharlie Jenkins bucket->bucket = current_head; 733*d8792a57SCharlie Jenkins list_add(&bucket->head, used_buckets_list); 7348fd6c514SCharlie Jenkins } 7358fd6c514SCharlie Jenkins hlist_add_head(&rel_head->node, current_head); 7368fd6c514SCharlie Jenkins } 7378fd6c514SCharlie Jenkins 7388fd6c514SCharlie Jenkins /* Add relocation to head of discovered rel_head */ 7398fd6c514SCharlie Jenkins list_add_tail(&entry->head, rel_head->rel_entry); 7408fd6c514SCharlie Jenkins 7418fd6c514SCharlie Jenkins return 0; 7428fd6c514SCharlie Jenkins } 7438fd6c514SCharlie Jenkins 744*d8792a57SCharlie Jenkins static unsigned int 745*d8792a57SCharlie Jenkins initialize_relocation_hashtable(unsigned int num_relocations, 746*d8792a57SCharlie Jenkins struct hlist_head **relocation_hashtable) 7478fd6c514SCharlie Jenkins { 7488fd6c514SCharlie Jenkins /* Can safely assume that bits is not greater than sizeof(long) */ 7498fd6c514SCharlie Jenkins unsigned long hashtable_size = roundup_pow_of_two(num_relocations); 7508fd6c514SCharlie Jenkins unsigned int hashtable_bits = ilog2(hashtable_size); 7518fd6c514SCharlie Jenkins 7528fd6c514SCharlie Jenkins /* 7538fd6c514SCharlie Jenkins * Double size of hashtable if num_relocations * 1.25 is greater than 7548fd6c514SCharlie Jenkins * hashtable_size. 7558fd6c514SCharlie Jenkins */ 7568fd6c514SCharlie Jenkins int should_double_size = ((num_relocations + (num_relocations >> 2)) > (hashtable_size)); 7578fd6c514SCharlie Jenkins 7588fd6c514SCharlie Jenkins hashtable_bits += should_double_size; 7598fd6c514SCharlie Jenkins 7608fd6c514SCharlie Jenkins hashtable_size <<= should_double_size; 7618fd6c514SCharlie Jenkins 762*d8792a57SCharlie Jenkins *relocation_hashtable = kmalloc_array(hashtable_size, 7638fd6c514SCharlie Jenkins sizeof(*relocation_hashtable), 7648fd6c514SCharlie Jenkins GFP_KERNEL); 765*d8792a57SCharlie Jenkins if (!*relocation_hashtable) 766*d8792a57SCharlie Jenkins return -ENOMEM; 7678fd6c514SCharlie Jenkins 768*d8792a57SCharlie Jenkins __hash_init(*relocation_hashtable, hashtable_size); 7698fd6c514SCharlie Jenkins 7708fd6c514SCharlie Jenkins return hashtable_bits; 7718fd6c514SCharlie Jenkins } 7728fd6c514SCharlie Jenkins 773e2c0cdfbSPalmer Dabbelt int apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab, 774e2c0cdfbSPalmer Dabbelt unsigned int symindex, unsigned int relsec, 775e2c0cdfbSPalmer Dabbelt struct module *me) 776e2c0cdfbSPalmer Dabbelt { 777e2c0cdfbSPalmer Dabbelt Elf_Rela *rel = (void *) sechdrs[relsec].sh_addr; 7788cbe0accSEmil Renner Berthing int (*handler)(struct module *me, void *location, Elf_Addr v); 779e2c0cdfbSPalmer Dabbelt Elf_Sym *sym; 7808cbe0accSEmil Renner Berthing void *location; 781e2c0cdfbSPalmer Dabbelt unsigned int i, type; 782e2c0cdfbSPalmer Dabbelt Elf_Addr v; 783e2c0cdfbSPalmer Dabbelt int res; 7848fd6c514SCharlie Jenkins unsigned int num_relocations = sechdrs[relsec].sh_size / sizeof(*rel); 785*d8792a57SCharlie Jenkins struct hlist_head *relocation_hashtable; 786*d8792a57SCharlie Jenkins struct list_head used_buckets_list; 787*d8792a57SCharlie Jenkins unsigned int hashtable_bits; 788*d8792a57SCharlie Jenkins 789*d8792a57SCharlie Jenkins hashtable_bits = initialize_relocation_hashtable(num_relocations, 790*d8792a57SCharlie Jenkins &relocation_hashtable); 791*d8792a57SCharlie Jenkins 792*d8792a57SCharlie Jenkins if (hashtable_bits < 0) 793*d8792a57SCharlie Jenkins return hashtable_bits; 794*d8792a57SCharlie Jenkins 795*d8792a57SCharlie Jenkins INIT_LIST_HEAD(&used_buckets_list); 796e2c0cdfbSPalmer Dabbelt 797e2c0cdfbSPalmer Dabbelt pr_debug("Applying relocate section %u to %u\n", relsec, 798e2c0cdfbSPalmer Dabbelt sechdrs[relsec].sh_info); 799e2c0cdfbSPalmer Dabbelt 8008fd6c514SCharlie Jenkins for (i = 0; i < num_relocations; i++) { 801e2c0cdfbSPalmer Dabbelt /* This is where to make the change */ 802e2c0cdfbSPalmer Dabbelt location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr 803e2c0cdfbSPalmer Dabbelt + rel[i].r_offset; 804e2c0cdfbSPalmer Dabbelt /* This is the symbol it is referring to */ 805e2c0cdfbSPalmer Dabbelt sym = (Elf_Sym *)sechdrs[symindex].sh_addr 806e2c0cdfbSPalmer Dabbelt + ELF_RISCV_R_SYM(rel[i].r_info); 807e2c0cdfbSPalmer Dabbelt if (IS_ERR_VALUE(sym->st_value)) { 808e2c0cdfbSPalmer Dabbelt /* Ignore unresolved weak symbol */ 809e2c0cdfbSPalmer Dabbelt if (ELF_ST_BIND(sym->st_info) == STB_WEAK) 810e2c0cdfbSPalmer Dabbelt continue; 81104ce8d3fSKefeng Wang pr_warn("%s: Unknown symbol %s\n", 812e2c0cdfbSPalmer Dabbelt me->name, strtab + sym->st_name); 813e2c0cdfbSPalmer Dabbelt return -ENOENT; 814e2c0cdfbSPalmer Dabbelt } 815e2c0cdfbSPalmer Dabbelt 816e2c0cdfbSPalmer Dabbelt type = ELF_RISCV_R_TYPE(rel[i].r_info); 817e2c0cdfbSPalmer Dabbelt 8188fd6c514SCharlie Jenkins if (type < ARRAY_SIZE(reloc_handlers)) 8198fd6c514SCharlie Jenkins handler = reloc_handlers[type].reloc_handler; 820e2c0cdfbSPalmer Dabbelt else 821e2c0cdfbSPalmer Dabbelt handler = NULL; 822e2c0cdfbSPalmer Dabbelt 823e2c0cdfbSPalmer Dabbelt if (!handler) { 824e2c0cdfbSPalmer Dabbelt pr_err("%s: Unknown relocation type %u\n", 825e2c0cdfbSPalmer Dabbelt me->name, type); 826e2c0cdfbSPalmer Dabbelt return -EINVAL; 827e2c0cdfbSPalmer Dabbelt } 828e2c0cdfbSPalmer Dabbelt 829e2c0cdfbSPalmer Dabbelt v = sym->st_value + rel[i].r_addend; 830e2c0cdfbSPalmer Dabbelt 831e2c0cdfbSPalmer Dabbelt if (type == R_RISCV_PCREL_LO12_I || type == R_RISCV_PCREL_LO12_S) { 832e2c0cdfbSPalmer Dabbelt unsigned int j; 833e2c0cdfbSPalmer Dabbelt 834e2c0cdfbSPalmer Dabbelt for (j = 0; j < sechdrs[relsec].sh_size / sizeof(*rel); j++) { 8357df85002SZong Li unsigned long hi20_loc = 836e2c0cdfbSPalmer Dabbelt sechdrs[sechdrs[relsec].sh_info].sh_addr 837e2c0cdfbSPalmer Dabbelt + rel[j].r_offset; 838da975dd4SZong Li u32 hi20_type = ELF_RISCV_R_TYPE(rel[j].r_info); 839da975dd4SZong Li 840da975dd4SZong Li /* Find the corresponding HI20 relocation entry */ 841da975dd4SZong Li if (hi20_loc == sym->st_value 842da975dd4SZong Li && (hi20_type == R_RISCV_PCREL_HI20 843da975dd4SZong Li || hi20_type == R_RISCV_GOT_HI20)) { 844da975dd4SZong Li s32 hi20, lo12; 845e2c0cdfbSPalmer Dabbelt Elf_Sym *hi20_sym = 846e2c0cdfbSPalmer Dabbelt (Elf_Sym *)sechdrs[symindex].sh_addr 847e2c0cdfbSPalmer Dabbelt + ELF_RISCV_R_SYM(rel[j].r_info); 8487df85002SZong Li unsigned long hi20_sym_val = 849e2c0cdfbSPalmer Dabbelt hi20_sym->st_value 850e2c0cdfbSPalmer Dabbelt + rel[j].r_addend; 851da975dd4SZong Li 852e2c0cdfbSPalmer Dabbelt /* Calculate lo12 */ 8537df85002SZong Li size_t offset = hi20_sym_val - hi20_loc; 854da975dd4SZong Li if (IS_ENABLED(CONFIG_MODULE_SECTIONS) 855da975dd4SZong Li && hi20_type == R_RISCV_GOT_HI20) { 856da975dd4SZong Li offset = module_emit_got_entry( 857da975dd4SZong Li me, hi20_sym_val); 858da975dd4SZong Li offset = offset - hi20_loc; 859da975dd4SZong Li } 860da975dd4SZong Li hi20 = (offset + 0x800) & 0xfffff000; 861da975dd4SZong Li lo12 = offset - hi20; 862e2c0cdfbSPalmer Dabbelt v = lo12; 863da975dd4SZong Li 864e2c0cdfbSPalmer Dabbelt break; 865e2c0cdfbSPalmer Dabbelt } 866e2c0cdfbSPalmer Dabbelt } 867e2c0cdfbSPalmer Dabbelt if (j == sechdrs[relsec].sh_size / sizeof(*rel)) { 868e2c0cdfbSPalmer Dabbelt pr_err( 869da975dd4SZong Li "%s: Can not find HI20 relocation information\n", 870e2c0cdfbSPalmer Dabbelt me->name); 871e2c0cdfbSPalmer Dabbelt return -EINVAL; 872e2c0cdfbSPalmer Dabbelt } 873e2c0cdfbSPalmer Dabbelt } 874e2c0cdfbSPalmer Dabbelt 8758fd6c514SCharlie Jenkins if (reloc_handlers[type].accumulate_handler) 876*d8792a57SCharlie Jenkins res = add_relocation_to_accumulate(me, type, location, 877*d8792a57SCharlie Jenkins hashtable_bits, v, 878*d8792a57SCharlie Jenkins relocation_hashtable, 879*d8792a57SCharlie Jenkins &used_buckets_list); 8808fd6c514SCharlie Jenkins else 881e2c0cdfbSPalmer Dabbelt res = handler(me, location, v); 882e2c0cdfbSPalmer Dabbelt if (res) 883e2c0cdfbSPalmer Dabbelt return res; 884e2c0cdfbSPalmer Dabbelt } 885e2c0cdfbSPalmer Dabbelt 886*d8792a57SCharlie Jenkins process_accumulated_relocations(me, &relocation_hashtable, 887*d8792a57SCharlie Jenkins &used_buckets_list); 8888fd6c514SCharlie Jenkins 889e2c0cdfbSPalmer Dabbelt return 0; 890e2c0cdfbSPalmer Dabbelt } 8910cff8bffSVincent Chen 8920cff8bffSVincent Chen #if defined(CONFIG_MMU) && defined(CONFIG_64BIT) 8930cff8bffSVincent Chen void *module_alloc(unsigned long size) 8940cff8bffSVincent Chen { 8952bfc6cd8SAlexandre Ghiti return __vmalloc_node_range(size, 1, MODULES_VADDR, 8962bfc6cd8SAlexandre Ghiti MODULES_END, GFP_KERNEL, 8975387054bSJisheng Zhang PAGE_KERNEL, 0, NUMA_NO_NODE, 8980cff8bffSVincent Chen __builtin_return_address(0)); 8990cff8bffSVincent Chen } 9000cff8bffSVincent Chen #endif 901a8e91016SHeiko Stuebner 902a8e91016SHeiko Stuebner int module_finalize(const Elf_Ehdr *hdr, 903a8e91016SHeiko Stuebner const Elf_Shdr *sechdrs, 904a8e91016SHeiko Stuebner struct module *me) 905a8e91016SHeiko Stuebner { 906a8e91016SHeiko Stuebner const Elf_Shdr *s; 907a8e91016SHeiko Stuebner 908a8e91016SHeiko Stuebner s = find_section(hdr, sechdrs, ".alternative"); 909a8e91016SHeiko Stuebner if (s) 910a8e91016SHeiko Stuebner apply_module_alternatives((void *)s->sh_addr, s->sh_size); 911a8e91016SHeiko Stuebner 912a8e91016SHeiko Stuebner return 0; 913a8e91016SHeiko Stuebner } 914