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/sizes.h> 15ca5999fdSMike Rapoport #include <linux/pgtable.h> 16a8e91016SHeiko Stuebner #include <asm/alternative.h> 170cff8bffSVincent Chen #include <asm/sections.h> 18e2c0cdfbSPalmer Dabbelt 198fd6c514SCharlie Jenkins struct used_bucket { 208fd6c514SCharlie Jenkins struct list_head head; 218fd6c514SCharlie Jenkins struct hlist_head *bucket; 228fd6c514SCharlie Jenkins }; 238fd6c514SCharlie Jenkins 248fd6c514SCharlie Jenkins struct relocation_head { 258fd6c514SCharlie Jenkins struct hlist_node node; 26*03f0b548SClément Léger struct list_head rel_entry; 278fd6c514SCharlie Jenkins void *location; 288fd6c514SCharlie Jenkins }; 298fd6c514SCharlie Jenkins 308fd6c514SCharlie Jenkins struct relocation_entry { 318fd6c514SCharlie Jenkins struct list_head head; 328fd6c514SCharlie Jenkins Elf_Addr value; 338fd6c514SCharlie Jenkins unsigned int type; 348fd6c514SCharlie Jenkins }; 358fd6c514SCharlie Jenkins 368fd6c514SCharlie Jenkins struct relocation_handlers { 378fd6c514SCharlie Jenkins int (*reloc_handler)(struct module *me, void *location, Elf_Addr v); 388fd6c514SCharlie Jenkins int (*accumulate_handler)(struct module *me, void *location, 398fd6c514SCharlie Jenkins long buffer); 408fd6c514SCharlie Jenkins }; 418fd6c514SCharlie Jenkins 420966d385SEmil Renner Berthing /* 430966d385SEmil Renner Berthing * The auipc+jalr instruction pair can reach any PC-relative offset 440966d385SEmil Renner Berthing * in the range [-2^31 - 2^11, 2^31 - 2^11) 450966d385SEmil Renner Berthing */ 460966d385SEmil Renner Berthing static bool riscv_insn_valid_32bit_offset(ptrdiff_t val) 470966d385SEmil Renner Berthing { 480966d385SEmil Renner Berthing #ifdef CONFIG_32BIT 490966d385SEmil Renner Berthing return true; 500966d385SEmil Renner Berthing #else 510966d385SEmil Renner Berthing return (-(1L << 31) - (1L << 11)) <= val && val < ((1L << 31) - (1L << 11)); 520966d385SEmil Renner Berthing #endif 530966d385SEmil Renner Berthing } 540966d385SEmil Renner Berthing 558cbe0accSEmil Renner Berthing static int riscv_insn_rmw(void *location, u32 keep, u32 set) 568cbe0accSEmil Renner Berthing { 574a92a879SCharlie Jenkins __le16 *parcel = location; 588cbe0accSEmil Renner Berthing u32 insn = (u32)le16_to_cpu(parcel[0]) | (u32)le16_to_cpu(parcel[1]) << 16; 598cbe0accSEmil Renner Berthing 608cbe0accSEmil Renner Berthing insn &= keep; 618cbe0accSEmil Renner Berthing insn |= set; 628cbe0accSEmil Renner Berthing 638cbe0accSEmil Renner Berthing parcel[0] = cpu_to_le16(insn); 648cbe0accSEmil Renner Berthing parcel[1] = cpu_to_le16(insn >> 16); 658cbe0accSEmil Renner Berthing return 0; 668cbe0accSEmil Renner Berthing } 678cbe0accSEmil Renner Berthing 688cbe0accSEmil Renner Berthing static int riscv_insn_rvc_rmw(void *location, u16 keep, u16 set) 698cbe0accSEmil Renner Berthing { 704a92a879SCharlie Jenkins __le16 *parcel = location; 718cbe0accSEmil Renner Berthing u16 insn = le16_to_cpu(*parcel); 728cbe0accSEmil Renner Berthing 738cbe0accSEmil Renner Berthing insn &= keep; 748cbe0accSEmil Renner Berthing insn |= set; 758cbe0accSEmil Renner Berthing 768cbe0accSEmil Renner Berthing *parcel = cpu_to_le16(insn); 778cbe0accSEmil Renner Berthing return 0; 788cbe0accSEmil Renner Berthing } 798cbe0accSEmil Renner Berthing 808cbe0accSEmil Renner Berthing static int apply_r_riscv_32_rela(struct module *me, void *location, Elf_Addr v) 8177aa85deSAndreas Schwab { 8277aa85deSAndreas Schwab if (v != (u32)v) { 8377aa85deSAndreas Schwab pr_err("%s: value %016llx out of range for 32-bit field\n", 84ef3a6140SOlof Johansson me->name, (long long)v); 8577aa85deSAndreas Schwab return -EINVAL; 8677aa85deSAndreas Schwab } 878cbe0accSEmil Renner Berthing *(u32 *)location = v; 8877aa85deSAndreas Schwab return 0; 8977aa85deSAndreas Schwab } 9077aa85deSAndreas Schwab 918cbe0accSEmil Renner Berthing static int apply_r_riscv_64_rela(struct module *me, void *location, Elf_Addr v) 92e2c0cdfbSPalmer Dabbelt { 93e2c0cdfbSPalmer Dabbelt *(u64 *)location = v; 94e2c0cdfbSPalmer Dabbelt return 0; 95e2c0cdfbSPalmer Dabbelt } 96e2c0cdfbSPalmer Dabbelt 978cbe0accSEmil Renner Berthing static int apply_r_riscv_branch_rela(struct module *me, void *location, 98e2c0cdfbSPalmer Dabbelt Elf_Addr v) 99e2c0cdfbSPalmer Dabbelt { 1008cbe0accSEmil Renner Berthing ptrdiff_t offset = (void *)v - location; 101e2c0cdfbSPalmer Dabbelt u32 imm12 = (offset & 0x1000) << (31 - 12); 102e2c0cdfbSPalmer Dabbelt u32 imm11 = (offset & 0x800) >> (11 - 7); 103e2c0cdfbSPalmer Dabbelt u32 imm10_5 = (offset & 0x7e0) << (30 - 10); 104e2c0cdfbSPalmer Dabbelt u32 imm4_1 = (offset & 0x1e) << (11 - 4); 105e2c0cdfbSPalmer Dabbelt 1068cbe0accSEmil Renner Berthing return riscv_insn_rmw(location, 0x1fff07f, imm12 | imm11 | imm10_5 | imm4_1); 107e2c0cdfbSPalmer Dabbelt } 108e2c0cdfbSPalmer Dabbelt 1098cbe0accSEmil Renner Berthing static int apply_r_riscv_jal_rela(struct module *me, void *location, 110e2c0cdfbSPalmer Dabbelt Elf_Addr v) 111e2c0cdfbSPalmer Dabbelt { 1128cbe0accSEmil Renner Berthing ptrdiff_t offset = (void *)v - location; 113e2c0cdfbSPalmer Dabbelt u32 imm20 = (offset & 0x100000) << (31 - 20); 114e2c0cdfbSPalmer Dabbelt u32 imm19_12 = (offset & 0xff000); 115e2c0cdfbSPalmer Dabbelt u32 imm11 = (offset & 0x800) << (20 - 11); 116e2c0cdfbSPalmer Dabbelt u32 imm10_1 = (offset & 0x7fe) << (30 - 10); 117e2c0cdfbSPalmer Dabbelt 1188cbe0accSEmil Renner Berthing return riscv_insn_rmw(location, 0xfff, imm20 | imm19_12 | imm11 | imm10_1); 119e2c0cdfbSPalmer Dabbelt } 120e2c0cdfbSPalmer Dabbelt 1218cbe0accSEmil Renner Berthing static int apply_r_riscv_rvc_branch_rela(struct module *me, void *location, 12256ea45aeSZong Li Elf_Addr v) 12356ea45aeSZong Li { 1248cbe0accSEmil Renner Berthing ptrdiff_t offset = (void *)v - location; 12556ea45aeSZong Li u16 imm8 = (offset & 0x100) << (12 - 8); 12656ea45aeSZong Li u16 imm7_6 = (offset & 0xc0) >> (6 - 5); 12756ea45aeSZong Li u16 imm5 = (offset & 0x20) >> (5 - 2); 12856ea45aeSZong Li u16 imm4_3 = (offset & 0x18) << (12 - 5); 12956ea45aeSZong Li u16 imm2_1 = (offset & 0x6) << (12 - 10); 13056ea45aeSZong Li 1318cbe0accSEmil Renner Berthing return riscv_insn_rvc_rmw(location, 0xe383, 1328cbe0accSEmil Renner Berthing imm8 | imm7_6 | imm5 | imm4_3 | imm2_1); 13356ea45aeSZong Li } 13456ea45aeSZong Li 1358cbe0accSEmil Renner Berthing static int apply_r_riscv_rvc_jump_rela(struct module *me, void *location, 13656ea45aeSZong Li Elf_Addr v) 13756ea45aeSZong Li { 1388cbe0accSEmil Renner Berthing ptrdiff_t offset = (void *)v - location; 13956ea45aeSZong Li u16 imm11 = (offset & 0x800) << (12 - 11); 14056ea45aeSZong Li u16 imm10 = (offset & 0x400) >> (10 - 8); 14156ea45aeSZong Li u16 imm9_8 = (offset & 0x300) << (12 - 11); 14256ea45aeSZong Li u16 imm7 = (offset & 0x80) >> (7 - 6); 14356ea45aeSZong Li u16 imm6 = (offset & 0x40) << (12 - 11); 14456ea45aeSZong Li u16 imm5 = (offset & 0x20) >> (5 - 2); 14556ea45aeSZong Li u16 imm4 = (offset & 0x10) << (12 - 5); 14656ea45aeSZong Li u16 imm3_1 = (offset & 0xe) << (12 - 10); 14756ea45aeSZong Li 1488cbe0accSEmil Renner Berthing return riscv_insn_rvc_rmw(location, 0xe003, 1498cbe0accSEmil Renner Berthing imm11 | imm10 | imm9_8 | imm7 | imm6 | imm5 | imm4 | imm3_1); 15056ea45aeSZong Li } 15156ea45aeSZong Li 1528cbe0accSEmil Renner Berthing static int apply_r_riscv_pcrel_hi20_rela(struct module *me, void *location, 153e2c0cdfbSPalmer Dabbelt Elf_Addr v) 154e2c0cdfbSPalmer Dabbelt { 1558cbe0accSEmil Renner Berthing ptrdiff_t offset = (void *)v - location; 156e2c0cdfbSPalmer Dabbelt 1570966d385SEmil Renner Berthing if (!riscv_insn_valid_32bit_offset(offset)) { 158e2c0cdfbSPalmer Dabbelt pr_err( 159e2c0cdfbSPalmer Dabbelt "%s: target %016llx can not be addressed by the 32-bit offset from PC = %p\n", 160ef3a6140SOlof Johansson me->name, (long long)v, location); 161e2c0cdfbSPalmer Dabbelt return -EINVAL; 162e2c0cdfbSPalmer Dabbelt } 163e2c0cdfbSPalmer Dabbelt 1648cbe0accSEmil Renner Berthing return riscv_insn_rmw(location, 0xfff, (offset + 0x800) & 0xfffff000); 165e2c0cdfbSPalmer Dabbelt } 166e2c0cdfbSPalmer Dabbelt 1678cbe0accSEmil Renner Berthing static int apply_r_riscv_pcrel_lo12_i_rela(struct module *me, void *location, 168e2c0cdfbSPalmer Dabbelt Elf_Addr v) 169e2c0cdfbSPalmer Dabbelt { 170e2c0cdfbSPalmer Dabbelt /* 171e2c0cdfbSPalmer Dabbelt * v is the lo12 value to fill. It is calculated before calling this 172e2c0cdfbSPalmer Dabbelt * handler. 173e2c0cdfbSPalmer Dabbelt */ 1748cbe0accSEmil Renner Berthing return riscv_insn_rmw(location, 0xfffff, (v & 0xfff) << 20); 175e2c0cdfbSPalmer Dabbelt } 176e2c0cdfbSPalmer Dabbelt 1778cbe0accSEmil Renner Berthing static int apply_r_riscv_pcrel_lo12_s_rela(struct module *me, void *location, 178e2c0cdfbSPalmer Dabbelt Elf_Addr v) 179e2c0cdfbSPalmer Dabbelt { 180e2c0cdfbSPalmer Dabbelt /* 181e2c0cdfbSPalmer Dabbelt * v is the lo12 value to fill. It is calculated before calling this 182e2c0cdfbSPalmer Dabbelt * handler. 183e2c0cdfbSPalmer Dabbelt */ 184e2c0cdfbSPalmer Dabbelt u32 imm11_5 = (v & 0xfe0) << (31 - 11); 185e2c0cdfbSPalmer Dabbelt u32 imm4_0 = (v & 0x1f) << (11 - 4); 186e2c0cdfbSPalmer Dabbelt 1878cbe0accSEmil Renner Berthing return riscv_insn_rmw(location, 0x1fff07f, imm11_5 | imm4_0); 188e2c0cdfbSPalmer Dabbelt } 189e2c0cdfbSPalmer Dabbelt 1908cbe0accSEmil Renner Berthing static int apply_r_riscv_hi20_rela(struct module *me, void *location, 191e7456e69SZong Li Elf_Addr v) 192e7456e69SZong Li { 193da4ed378SJoe Perches if (IS_ENABLED(CONFIG_CMODEL_MEDLOW)) { 194e7456e69SZong Li pr_err( 195e7456e69SZong Li "%s: target %016llx can not be addressed by the 32-bit offset from PC = %p\n", 196ef3a6140SOlof Johansson me->name, (long long)v, location); 197e7456e69SZong Li return -EINVAL; 198e7456e69SZong Li } 199e7456e69SZong Li 2008cbe0accSEmil Renner Berthing return riscv_insn_rmw(location, 0xfff, ((s32)v + 0x800) & 0xfffff000); 201e7456e69SZong Li } 202e7456e69SZong Li 2038cbe0accSEmil Renner Berthing static int apply_r_riscv_lo12_i_rela(struct module *me, void *location, 204e7456e69SZong Li Elf_Addr v) 205e7456e69SZong Li { 206e7456e69SZong Li /* Skip medlow checking because of filtering by HI20 already */ 207e7456e69SZong Li s32 hi20 = ((s32)v + 0x800) & 0xfffff000; 208e7456e69SZong Li s32 lo12 = ((s32)v - hi20); 2098cbe0accSEmil Renner Berthing 2108cbe0accSEmil Renner Berthing return riscv_insn_rmw(location, 0xfffff, (lo12 & 0xfff) << 20); 211e7456e69SZong Li } 212e7456e69SZong Li 2138cbe0accSEmil Renner Berthing static int apply_r_riscv_lo12_s_rela(struct module *me, void *location, 214e7456e69SZong Li Elf_Addr v) 215e7456e69SZong Li { 216e7456e69SZong Li /* Skip medlow checking because of filtering by HI20 already */ 217e7456e69SZong Li s32 hi20 = ((s32)v + 0x800) & 0xfffff000; 218e7456e69SZong Li s32 lo12 = ((s32)v - hi20); 219e7456e69SZong Li u32 imm11_5 = (lo12 & 0xfe0) << (31 - 11); 220e7456e69SZong Li u32 imm4_0 = (lo12 & 0x1f) << (11 - 4); 2218cbe0accSEmil Renner Berthing 2228cbe0accSEmil Renner Berthing return riscv_insn_rmw(location, 0x1fff07f, imm11_5 | imm4_0); 223e7456e69SZong Li } 224e7456e69SZong Li 2258cbe0accSEmil Renner Berthing static int apply_r_riscv_got_hi20_rela(struct module *me, void *location, 226da975dd4SZong Li Elf_Addr v) 227da975dd4SZong Li { 2288cbe0accSEmil Renner Berthing ptrdiff_t offset = (void *)v - location; 229da975dd4SZong Li 230da975dd4SZong Li /* Always emit the got entry */ 231da975dd4SZong Li if (IS_ENABLED(CONFIG_MODULE_SECTIONS)) { 2328cbe0accSEmil Renner Berthing offset = (void *)module_emit_got_entry(me, v) - location; 233da975dd4SZong Li } else { 234da975dd4SZong Li pr_err( 235da975dd4SZong Li "%s: can not generate the GOT entry for symbol = %016llx from PC = %p\n", 236ef3a6140SOlof Johansson me->name, (long long)v, location); 237da975dd4SZong Li return -EINVAL; 238da975dd4SZong Li } 239da975dd4SZong Li 2408cbe0accSEmil Renner Berthing return riscv_insn_rmw(location, 0xfff, (offset + 0x800) & 0xfffff000); 241da975dd4SZong Li } 242da975dd4SZong Li 2438cbe0accSEmil Renner Berthing static int apply_r_riscv_call_plt_rela(struct module *me, void *location, 244e2c0cdfbSPalmer Dabbelt Elf_Addr v) 245e2c0cdfbSPalmer Dabbelt { 2468cbe0accSEmil Renner Berthing ptrdiff_t offset = (void *)v - location; 247e2c0cdfbSPalmer Dabbelt u32 hi20, lo12; 248e2c0cdfbSPalmer Dabbelt 2490966d385SEmil Renner Berthing if (!riscv_insn_valid_32bit_offset(offset)) { 250da975dd4SZong Li /* Only emit the plt entry if offset over 32-bit range */ 251da975dd4SZong Li if (IS_ENABLED(CONFIG_MODULE_SECTIONS)) { 2528cbe0accSEmil Renner Berthing offset = (void *)module_emit_plt_entry(me, v) - location; 253da975dd4SZong Li } else { 254e2c0cdfbSPalmer Dabbelt pr_err( 255e2c0cdfbSPalmer Dabbelt "%s: target %016llx can not be addressed by the 32-bit offset from PC = %p\n", 256ef3a6140SOlof Johansson me->name, (long long)v, location); 257e2c0cdfbSPalmer Dabbelt return -EINVAL; 258e2c0cdfbSPalmer Dabbelt } 259da975dd4SZong Li } 260e2c0cdfbSPalmer Dabbelt 261e2c0cdfbSPalmer Dabbelt hi20 = (offset + 0x800) & 0xfffff000; 262e2c0cdfbSPalmer Dabbelt lo12 = (offset - hi20) & 0xfff; 2638cbe0accSEmil Renner Berthing riscv_insn_rmw(location, 0xfff, hi20); 2648cbe0accSEmil Renner Berthing return riscv_insn_rmw(location + 4, 0xfffff, lo12 << 20); 265e2c0cdfbSPalmer Dabbelt } 266e2c0cdfbSPalmer Dabbelt 2678cbe0accSEmil Renner Berthing static int apply_r_riscv_call_rela(struct module *me, void *location, 268e1910c72SZong Li Elf_Addr v) 269e1910c72SZong Li { 2708cbe0accSEmil Renner Berthing ptrdiff_t offset = (void *)v - location; 271e1910c72SZong Li u32 hi20, lo12; 272e1910c72SZong Li 2730966d385SEmil Renner Berthing if (!riscv_insn_valid_32bit_offset(offset)) { 274e1910c72SZong Li pr_err( 275e1910c72SZong Li "%s: target %016llx can not be addressed by the 32-bit offset from PC = %p\n", 276ef3a6140SOlof Johansson me->name, (long long)v, location); 277e1910c72SZong Li return -EINVAL; 278e1910c72SZong Li } 279e1910c72SZong Li 280e1910c72SZong Li hi20 = (offset + 0x800) & 0xfffff000; 281e1910c72SZong Li lo12 = (offset - hi20) & 0xfff; 2828cbe0accSEmil Renner Berthing riscv_insn_rmw(location, 0xfff, hi20); 2838cbe0accSEmil Renner Berthing return riscv_insn_rmw(location + 4, 0xfffff, lo12 << 20); 284e1910c72SZong Li } 285e1910c72SZong Li 2868cbe0accSEmil Renner Berthing static int apply_r_riscv_relax_rela(struct module *me, void *location, 287e2c0cdfbSPalmer Dabbelt Elf_Addr v) 288e2c0cdfbSPalmer Dabbelt { 289e2c0cdfbSPalmer Dabbelt return 0; 290e2c0cdfbSPalmer Dabbelt } 291e2c0cdfbSPalmer Dabbelt 2928cbe0accSEmil Renner Berthing static int apply_r_riscv_align_rela(struct module *me, void *location, 29329e405cdSZong Li Elf_Addr v) 29429e405cdSZong Li { 29529e405cdSZong Li pr_err( 29629e405cdSZong Li "%s: The unexpected relocation type 'R_RISCV_ALIGN' from PC = %p\n", 29729e405cdSZong Li me->name, location); 29829e405cdSZong Li return -EINVAL; 29929e405cdSZong Li } 30029e405cdSZong Li 3018fd6c514SCharlie Jenkins static int apply_r_riscv_add8_rela(struct module *me, void *location, Elf_Addr v) 3028fd6c514SCharlie Jenkins { 3038fd6c514SCharlie Jenkins *(u8 *)location += (u8)v; 3048fd6c514SCharlie Jenkins return 0; 3058fd6c514SCharlie Jenkins } 3068fd6c514SCharlie Jenkins 3078cbe0accSEmil Renner Berthing static int apply_r_riscv_add16_rela(struct module *me, void *location, 3081bc400ffSAndrew Jones Elf_Addr v) 3091bc400ffSAndrew Jones { 3101bc400ffSAndrew Jones *(u16 *)location += (u16)v; 3111bc400ffSAndrew Jones return 0; 3121bc400ffSAndrew Jones } 3131bc400ffSAndrew Jones 3148cbe0accSEmil Renner Berthing static int apply_r_riscv_add32_rela(struct module *me, void *location, 3158e691b16SZong Li Elf_Addr v) 3168e691b16SZong Li { 317781c8fe2SAndreas Schwab *(u32 *)location += (u32)v; 3188e691b16SZong Li return 0; 3198e691b16SZong Li } 3208e691b16SZong Li 3218cbe0accSEmil Renner Berthing static int apply_r_riscv_add64_rela(struct module *me, void *location, 32211a54f42SEmil Renner Berthing Elf_Addr v) 32311a54f42SEmil Renner Berthing { 32411a54f42SEmil Renner Berthing *(u64 *)location += (u64)v; 32511a54f42SEmil Renner Berthing return 0; 32611a54f42SEmil Renner Berthing } 32711a54f42SEmil Renner Berthing 3288fd6c514SCharlie Jenkins static int apply_r_riscv_sub8_rela(struct module *me, void *location, Elf_Addr v) 3298fd6c514SCharlie Jenkins { 3308fd6c514SCharlie Jenkins *(u8 *)location -= (u8)v; 3318fd6c514SCharlie Jenkins return 0; 3328fd6c514SCharlie Jenkins } 3338fd6c514SCharlie Jenkins 3348cbe0accSEmil Renner Berthing static int apply_r_riscv_sub16_rela(struct module *me, void *location, 3351bc400ffSAndrew Jones Elf_Addr v) 3361bc400ffSAndrew Jones { 3371bc400ffSAndrew Jones *(u16 *)location -= (u16)v; 3381bc400ffSAndrew Jones return 0; 3391bc400ffSAndrew Jones } 3401bc400ffSAndrew Jones 3418cbe0accSEmil Renner Berthing static int apply_r_riscv_sub32_rela(struct module *me, void *location, 3424aad074cSZong Li Elf_Addr v) 3434aad074cSZong Li { 344781c8fe2SAndreas Schwab *(u32 *)location -= (u32)v; 3454aad074cSZong Li return 0; 3464aad074cSZong Li } 3474aad074cSZong Li 3488cbe0accSEmil Renner Berthing static int apply_r_riscv_sub64_rela(struct module *me, void *location, 34911a54f42SEmil Renner Berthing Elf_Addr v) 35011a54f42SEmil Renner Berthing { 35111a54f42SEmil Renner Berthing *(u64 *)location -= (u64)v; 35211a54f42SEmil Renner Berthing return 0; 35311a54f42SEmil Renner Berthing } 35411a54f42SEmil Renner Berthing 3558fd6c514SCharlie Jenkins static int dynamic_linking_not_supported(struct module *me, void *location, 3568fd6c514SCharlie Jenkins Elf_Addr v) 3578fd6c514SCharlie Jenkins { 3588fd6c514SCharlie Jenkins pr_err("%s: Dynamic linking not supported in kernel modules PC = %p\n", 3598fd6c514SCharlie Jenkins me->name, location); 3608fd6c514SCharlie Jenkins return -EINVAL; 3618fd6c514SCharlie Jenkins } 3628fd6c514SCharlie Jenkins 3638fd6c514SCharlie Jenkins static int tls_not_supported(struct module *me, void *location, Elf_Addr v) 3648fd6c514SCharlie Jenkins { 3658fd6c514SCharlie Jenkins pr_err("%s: Thread local storage not supported in kernel modules PC = %p\n", 3668fd6c514SCharlie Jenkins me->name, location); 3678fd6c514SCharlie Jenkins return -EINVAL; 3688fd6c514SCharlie Jenkins } 3698fd6c514SCharlie Jenkins 3708fd6c514SCharlie Jenkins static int apply_r_riscv_sub6_rela(struct module *me, void *location, Elf_Addr v) 3718fd6c514SCharlie Jenkins { 3728fd6c514SCharlie Jenkins u8 *byte = location; 3738fd6c514SCharlie Jenkins u8 value = v; 3748fd6c514SCharlie Jenkins 3758fd6c514SCharlie Jenkins *byte = (*byte - (value & 0x3f)) & 0x3f; 3768fd6c514SCharlie Jenkins return 0; 3778fd6c514SCharlie Jenkins } 3788fd6c514SCharlie Jenkins 3798fd6c514SCharlie Jenkins static int apply_r_riscv_set6_rela(struct module *me, void *location, Elf_Addr v) 3808fd6c514SCharlie Jenkins { 3818fd6c514SCharlie Jenkins u8 *byte = location; 3828fd6c514SCharlie Jenkins u8 value = v; 3838fd6c514SCharlie Jenkins 3848fd6c514SCharlie Jenkins *byte = (*byte & 0xc0) | (value & 0x3f); 3858fd6c514SCharlie Jenkins return 0; 3868fd6c514SCharlie Jenkins } 3878fd6c514SCharlie Jenkins 3888fd6c514SCharlie Jenkins static int apply_r_riscv_set8_rela(struct module *me, void *location, Elf_Addr v) 3898fd6c514SCharlie Jenkins { 3908fd6c514SCharlie Jenkins *(u8 *)location = (u8)v; 3918fd6c514SCharlie Jenkins return 0; 3928fd6c514SCharlie Jenkins } 3938fd6c514SCharlie Jenkins 3948fd6c514SCharlie Jenkins static int apply_r_riscv_set16_rela(struct module *me, void *location, 3958fd6c514SCharlie Jenkins Elf_Addr v) 3968fd6c514SCharlie Jenkins { 3978fd6c514SCharlie Jenkins *(u16 *)location = (u16)v; 3988fd6c514SCharlie Jenkins return 0; 3998fd6c514SCharlie Jenkins } 4008fd6c514SCharlie Jenkins 4018fd6c514SCharlie Jenkins static int apply_r_riscv_set32_rela(struct module *me, void *location, 4028fd6c514SCharlie Jenkins Elf_Addr v) 4038fd6c514SCharlie Jenkins { 4048fd6c514SCharlie Jenkins *(u32 *)location = (u32)v; 4058fd6c514SCharlie Jenkins return 0; 4068fd6c514SCharlie Jenkins } 4078fd6c514SCharlie Jenkins 4088fd6c514SCharlie Jenkins static int apply_r_riscv_32_pcrel_rela(struct module *me, void *location, 4098fd6c514SCharlie Jenkins Elf_Addr v) 4108fd6c514SCharlie Jenkins { 4118fd6c514SCharlie Jenkins *(u32 *)location = v - (uintptr_t)location; 4128fd6c514SCharlie Jenkins return 0; 4138fd6c514SCharlie Jenkins } 4148fd6c514SCharlie Jenkins 4158fd6c514SCharlie Jenkins static int apply_r_riscv_plt32_rela(struct module *me, void *location, 4168fd6c514SCharlie Jenkins Elf_Addr v) 4178fd6c514SCharlie Jenkins { 4188fd6c514SCharlie Jenkins ptrdiff_t offset = (void *)v - location; 4198fd6c514SCharlie Jenkins 4208fd6c514SCharlie Jenkins if (!riscv_insn_valid_32bit_offset(offset)) { 4218fd6c514SCharlie Jenkins /* Only emit the plt entry if offset over 32-bit range */ 4228fd6c514SCharlie Jenkins if (IS_ENABLED(CONFIG_MODULE_SECTIONS)) { 4238fd6c514SCharlie Jenkins offset = (void *)module_emit_plt_entry(me, v) - location; 4248fd6c514SCharlie Jenkins } else { 4258fd6c514SCharlie Jenkins pr_err("%s: target %016llx can not be addressed by the 32-bit offset from PC = %p\n", 4268fd6c514SCharlie Jenkins me->name, (long long)v, location); 4278fd6c514SCharlie Jenkins return -EINVAL; 4288fd6c514SCharlie Jenkins } 4298fd6c514SCharlie Jenkins } 4308fd6c514SCharlie Jenkins 4318fd6c514SCharlie Jenkins *(u32 *)location = (u32)offset; 4328fd6c514SCharlie Jenkins return 0; 4338fd6c514SCharlie Jenkins } 4348fd6c514SCharlie Jenkins 4358fd6c514SCharlie Jenkins static int apply_r_riscv_set_uleb128(struct module *me, void *location, Elf_Addr v) 4368fd6c514SCharlie Jenkins { 4378fd6c514SCharlie Jenkins *(long *)location = v; 4388fd6c514SCharlie Jenkins return 0; 4398fd6c514SCharlie Jenkins } 4408fd6c514SCharlie Jenkins 4418fd6c514SCharlie Jenkins static int apply_r_riscv_sub_uleb128(struct module *me, void *location, Elf_Addr v) 4428fd6c514SCharlie Jenkins { 4438fd6c514SCharlie Jenkins *(long *)location -= v; 4448fd6c514SCharlie Jenkins return 0; 4458fd6c514SCharlie Jenkins } 4468fd6c514SCharlie Jenkins 4478fd6c514SCharlie Jenkins static int apply_6_bit_accumulation(struct module *me, void *location, long buffer) 4488fd6c514SCharlie Jenkins { 4498fd6c514SCharlie Jenkins u8 *byte = location; 4508fd6c514SCharlie Jenkins u8 value = buffer; 4518fd6c514SCharlie Jenkins 4528fd6c514SCharlie Jenkins if (buffer > 0x3f) { 4538fd6c514SCharlie Jenkins pr_err("%s: value %ld out of range for 6-bit relocation.\n", 4548fd6c514SCharlie Jenkins me->name, buffer); 4558fd6c514SCharlie Jenkins return -EINVAL; 4568fd6c514SCharlie Jenkins } 4578fd6c514SCharlie Jenkins 4588fd6c514SCharlie Jenkins *byte = (*byte & 0xc0) | (value & 0x3f); 4598fd6c514SCharlie Jenkins return 0; 4608fd6c514SCharlie Jenkins } 4618fd6c514SCharlie Jenkins 4628fd6c514SCharlie Jenkins static int apply_8_bit_accumulation(struct module *me, void *location, long buffer) 4638fd6c514SCharlie Jenkins { 4648fd6c514SCharlie Jenkins if (buffer > U8_MAX) { 4658fd6c514SCharlie Jenkins pr_err("%s: value %ld out of range for 8-bit relocation.\n", 4668fd6c514SCharlie Jenkins me->name, buffer); 4678fd6c514SCharlie Jenkins return -EINVAL; 4688fd6c514SCharlie Jenkins } 4698fd6c514SCharlie Jenkins *(u8 *)location = (u8)buffer; 4708fd6c514SCharlie Jenkins return 0; 4718fd6c514SCharlie Jenkins } 4728fd6c514SCharlie Jenkins 4738fd6c514SCharlie Jenkins static int apply_16_bit_accumulation(struct module *me, void *location, long buffer) 4748fd6c514SCharlie Jenkins { 4758fd6c514SCharlie Jenkins if (buffer > U16_MAX) { 4768fd6c514SCharlie Jenkins pr_err("%s: value %ld out of range for 16-bit relocation.\n", 4778fd6c514SCharlie Jenkins me->name, buffer); 4788fd6c514SCharlie Jenkins return -EINVAL; 4798fd6c514SCharlie Jenkins } 4808fd6c514SCharlie Jenkins *(u16 *)location = (u16)buffer; 4818fd6c514SCharlie Jenkins return 0; 4828fd6c514SCharlie Jenkins } 4838fd6c514SCharlie Jenkins 4848fd6c514SCharlie Jenkins static int apply_32_bit_accumulation(struct module *me, void *location, long buffer) 4858fd6c514SCharlie Jenkins { 4868fd6c514SCharlie Jenkins if (buffer > U32_MAX) { 4878fd6c514SCharlie Jenkins pr_err("%s: value %ld out of range for 32-bit relocation.\n", 4888fd6c514SCharlie Jenkins me->name, buffer); 4898fd6c514SCharlie Jenkins return -EINVAL; 4908fd6c514SCharlie Jenkins } 4918fd6c514SCharlie Jenkins *(u32 *)location = (u32)buffer; 4928fd6c514SCharlie Jenkins return 0; 4938fd6c514SCharlie Jenkins } 4948fd6c514SCharlie Jenkins 4958fd6c514SCharlie Jenkins static int apply_64_bit_accumulation(struct module *me, void *location, long buffer) 4968fd6c514SCharlie Jenkins { 4978fd6c514SCharlie Jenkins *(u64 *)location = (u64)buffer; 4988fd6c514SCharlie Jenkins return 0; 4998fd6c514SCharlie Jenkins } 5008fd6c514SCharlie Jenkins 5018fd6c514SCharlie Jenkins static int apply_uleb128_accumulation(struct module *me, void *location, long buffer) 5028fd6c514SCharlie Jenkins { 5038fd6c514SCharlie Jenkins /* 5048fd6c514SCharlie Jenkins * ULEB128 is a variable length encoding. Encode the buffer into 5058fd6c514SCharlie Jenkins * the ULEB128 data format. 5068fd6c514SCharlie Jenkins */ 5078fd6c514SCharlie Jenkins u8 *p = location; 5088fd6c514SCharlie Jenkins 5098fd6c514SCharlie Jenkins while (buffer != 0) { 5108fd6c514SCharlie Jenkins u8 value = buffer & 0x7f; 5118fd6c514SCharlie Jenkins 5128fd6c514SCharlie Jenkins buffer >>= 7; 5138fd6c514SCharlie Jenkins value |= (!!buffer) << 7; 5148fd6c514SCharlie Jenkins 5158fd6c514SCharlie Jenkins *p++ = value; 5168fd6c514SCharlie Jenkins } 5178fd6c514SCharlie Jenkins return 0; 5188fd6c514SCharlie Jenkins } 5198fd6c514SCharlie Jenkins 5208fd6c514SCharlie Jenkins /* 5218fd6c514SCharlie Jenkins * Relocations defined in the riscv-elf-psabi-doc. 5228fd6c514SCharlie Jenkins * This handles static linking only. 5238fd6c514SCharlie Jenkins */ 5248fd6c514SCharlie Jenkins static const struct relocation_handlers reloc_handlers[] = { 52528ea54baSPalmer Dabbelt [R_RISCV_32] = { .reloc_handler = apply_r_riscv_32_rela }, 52628ea54baSPalmer Dabbelt [R_RISCV_64] = { .reloc_handler = apply_r_riscv_64_rela }, 52728ea54baSPalmer Dabbelt [R_RISCV_RELATIVE] = { .reloc_handler = dynamic_linking_not_supported }, 52828ea54baSPalmer Dabbelt [R_RISCV_COPY] = { .reloc_handler = dynamic_linking_not_supported }, 52928ea54baSPalmer Dabbelt [R_RISCV_JUMP_SLOT] = { .reloc_handler = dynamic_linking_not_supported }, 53028ea54baSPalmer Dabbelt [R_RISCV_TLS_DTPMOD32] = { .reloc_handler = dynamic_linking_not_supported }, 53128ea54baSPalmer Dabbelt [R_RISCV_TLS_DTPMOD64] = { .reloc_handler = dynamic_linking_not_supported }, 53228ea54baSPalmer Dabbelt [R_RISCV_TLS_DTPREL32] = { .reloc_handler = dynamic_linking_not_supported }, 53328ea54baSPalmer Dabbelt [R_RISCV_TLS_DTPREL64] = { .reloc_handler = dynamic_linking_not_supported }, 53428ea54baSPalmer Dabbelt [R_RISCV_TLS_TPREL32] = { .reloc_handler = dynamic_linking_not_supported }, 53528ea54baSPalmer Dabbelt [R_RISCV_TLS_TPREL64] = { .reloc_handler = dynamic_linking_not_supported }, 5368fd6c514SCharlie Jenkins /* 12-15 undefined */ 53728ea54baSPalmer Dabbelt [R_RISCV_BRANCH] = { .reloc_handler = apply_r_riscv_branch_rela }, 53828ea54baSPalmer Dabbelt [R_RISCV_JAL] = { .reloc_handler = apply_r_riscv_jal_rela }, 53928ea54baSPalmer Dabbelt [R_RISCV_CALL] = { .reloc_handler = apply_r_riscv_call_rela }, 54028ea54baSPalmer Dabbelt [R_RISCV_CALL_PLT] = { .reloc_handler = apply_r_riscv_call_plt_rela }, 54128ea54baSPalmer Dabbelt [R_RISCV_GOT_HI20] = { .reloc_handler = apply_r_riscv_got_hi20_rela }, 54228ea54baSPalmer Dabbelt [R_RISCV_TLS_GOT_HI20] = { .reloc_handler = tls_not_supported }, 54328ea54baSPalmer Dabbelt [R_RISCV_TLS_GD_HI20] = { .reloc_handler = tls_not_supported }, 54428ea54baSPalmer Dabbelt [R_RISCV_PCREL_HI20] = { .reloc_handler = apply_r_riscv_pcrel_hi20_rela }, 54528ea54baSPalmer Dabbelt [R_RISCV_PCREL_LO12_I] = { .reloc_handler = apply_r_riscv_pcrel_lo12_i_rela }, 54628ea54baSPalmer Dabbelt [R_RISCV_PCREL_LO12_S] = { .reloc_handler = apply_r_riscv_pcrel_lo12_s_rela }, 54728ea54baSPalmer Dabbelt [R_RISCV_HI20] = { .reloc_handler = apply_r_riscv_hi20_rela }, 54828ea54baSPalmer Dabbelt [R_RISCV_LO12_I] = { .reloc_handler = apply_r_riscv_lo12_i_rela }, 54928ea54baSPalmer Dabbelt [R_RISCV_LO12_S] = { .reloc_handler = apply_r_riscv_lo12_s_rela }, 55028ea54baSPalmer Dabbelt [R_RISCV_TPREL_HI20] = { .reloc_handler = tls_not_supported }, 55128ea54baSPalmer Dabbelt [R_RISCV_TPREL_LO12_I] = { .reloc_handler = tls_not_supported }, 55228ea54baSPalmer Dabbelt [R_RISCV_TPREL_LO12_S] = { .reloc_handler = tls_not_supported }, 55328ea54baSPalmer Dabbelt [R_RISCV_TPREL_ADD] = { .reloc_handler = tls_not_supported }, 55428ea54baSPalmer Dabbelt [R_RISCV_ADD8] = { .reloc_handler = apply_r_riscv_add8_rela, 55528ea54baSPalmer Dabbelt .accumulate_handler = apply_8_bit_accumulation }, 55628ea54baSPalmer Dabbelt [R_RISCV_ADD16] = { .reloc_handler = apply_r_riscv_add16_rela, 55728ea54baSPalmer Dabbelt .accumulate_handler = apply_16_bit_accumulation }, 55828ea54baSPalmer Dabbelt [R_RISCV_ADD32] = { .reloc_handler = apply_r_riscv_add32_rela, 55928ea54baSPalmer Dabbelt .accumulate_handler = apply_32_bit_accumulation }, 56028ea54baSPalmer Dabbelt [R_RISCV_ADD64] = { .reloc_handler = apply_r_riscv_add64_rela, 56128ea54baSPalmer Dabbelt .accumulate_handler = apply_64_bit_accumulation }, 56228ea54baSPalmer Dabbelt [R_RISCV_SUB8] = { .reloc_handler = apply_r_riscv_sub8_rela, 56328ea54baSPalmer Dabbelt .accumulate_handler = apply_8_bit_accumulation }, 56428ea54baSPalmer Dabbelt [R_RISCV_SUB16] = { .reloc_handler = apply_r_riscv_sub16_rela, 56528ea54baSPalmer Dabbelt .accumulate_handler = apply_16_bit_accumulation }, 56628ea54baSPalmer Dabbelt [R_RISCV_SUB32] = { .reloc_handler = apply_r_riscv_sub32_rela, 56728ea54baSPalmer Dabbelt .accumulate_handler = apply_32_bit_accumulation }, 56828ea54baSPalmer Dabbelt [R_RISCV_SUB64] = { .reloc_handler = apply_r_riscv_sub64_rela, 56928ea54baSPalmer Dabbelt .accumulate_handler = apply_64_bit_accumulation }, 5708fd6c514SCharlie Jenkins /* 41-42 reserved for future standard use */ 57128ea54baSPalmer Dabbelt [R_RISCV_ALIGN] = { .reloc_handler = apply_r_riscv_align_rela }, 57228ea54baSPalmer Dabbelt [R_RISCV_RVC_BRANCH] = { .reloc_handler = apply_r_riscv_rvc_branch_rela }, 57328ea54baSPalmer Dabbelt [R_RISCV_RVC_JUMP] = { .reloc_handler = apply_r_riscv_rvc_jump_rela }, 5748fd6c514SCharlie Jenkins /* 46-50 reserved for future standard use */ 57528ea54baSPalmer Dabbelt [R_RISCV_RELAX] = { .reloc_handler = apply_r_riscv_relax_rela }, 57628ea54baSPalmer Dabbelt [R_RISCV_SUB6] = { .reloc_handler = apply_r_riscv_sub6_rela, 57728ea54baSPalmer Dabbelt .accumulate_handler = apply_6_bit_accumulation }, 57828ea54baSPalmer Dabbelt [R_RISCV_SET6] = { .reloc_handler = apply_r_riscv_set6_rela, 57928ea54baSPalmer Dabbelt .accumulate_handler = apply_6_bit_accumulation }, 58028ea54baSPalmer Dabbelt [R_RISCV_SET8] = { .reloc_handler = apply_r_riscv_set8_rela, 58128ea54baSPalmer Dabbelt .accumulate_handler = apply_8_bit_accumulation }, 58228ea54baSPalmer Dabbelt [R_RISCV_SET16] = { .reloc_handler = apply_r_riscv_set16_rela, 58328ea54baSPalmer Dabbelt .accumulate_handler = apply_16_bit_accumulation }, 58428ea54baSPalmer Dabbelt [R_RISCV_SET32] = { .reloc_handler = apply_r_riscv_set32_rela, 58528ea54baSPalmer Dabbelt .accumulate_handler = apply_32_bit_accumulation }, 58628ea54baSPalmer Dabbelt [R_RISCV_32_PCREL] = { .reloc_handler = apply_r_riscv_32_pcrel_rela }, 58728ea54baSPalmer Dabbelt [R_RISCV_IRELATIVE] = { .reloc_handler = dynamic_linking_not_supported }, 58828ea54baSPalmer Dabbelt [R_RISCV_PLT32] = { .reloc_handler = apply_r_riscv_plt32_rela }, 58928ea54baSPalmer Dabbelt [R_RISCV_SET_ULEB128] = { .reloc_handler = apply_r_riscv_set_uleb128, 59028ea54baSPalmer Dabbelt .accumulate_handler = apply_uleb128_accumulation }, 59128ea54baSPalmer Dabbelt [R_RISCV_SUB_ULEB128] = { .reloc_handler = apply_r_riscv_sub_uleb128, 59228ea54baSPalmer Dabbelt .accumulate_handler = apply_uleb128_accumulation }, 5938fd6c514SCharlie Jenkins /* 62-191 reserved for future standard use */ 5948fd6c514SCharlie Jenkins /* 192-255 nonstandard ABI extensions */ 595e2c0cdfbSPalmer Dabbelt }; 596e2c0cdfbSPalmer Dabbelt 597d8792a57SCharlie Jenkins static void 598d8792a57SCharlie Jenkins process_accumulated_relocations(struct module *me, 599d8792a57SCharlie Jenkins struct hlist_head **relocation_hashtable, 600d8792a57SCharlie Jenkins struct list_head *used_buckets_list) 6018fd6c514SCharlie Jenkins { 6028fd6c514SCharlie Jenkins /* 6038fd6c514SCharlie Jenkins * Only ADD/SUB/SET/ULEB128 should end up here. 6048fd6c514SCharlie Jenkins * 6058fd6c514SCharlie Jenkins * Each bucket may have more than one relocation location. All 6068fd6c514SCharlie Jenkins * relocations for a location are stored in a list in a bucket. 6078fd6c514SCharlie Jenkins * 6088fd6c514SCharlie Jenkins * Relocations are applied to a temp variable before being stored to the 6098fd6c514SCharlie Jenkins * provided location to check for overflow. This also allows ULEB128 to 6108fd6c514SCharlie Jenkins * properly decide how many entries are needed before storing to 6118fd6c514SCharlie Jenkins * location. The final value is stored into location using the handler 6128fd6c514SCharlie Jenkins * for the last relocation to an address. 6138fd6c514SCharlie Jenkins * 6148fd6c514SCharlie Jenkins * Three layers of indexing: 6158fd6c514SCharlie Jenkins * - Each of the buckets in use 6168fd6c514SCharlie Jenkins * - Groups of relocations in each bucket by location address 6178fd6c514SCharlie Jenkins * - Each relocation entry for a location address 6188fd6c514SCharlie Jenkins */ 6198fd6c514SCharlie Jenkins struct used_bucket *bucket_iter; 620d8792a57SCharlie Jenkins struct used_bucket *bucket_iter_tmp; 6218fd6c514SCharlie Jenkins struct relocation_head *rel_head_iter; 622d8792a57SCharlie Jenkins struct hlist_node *rel_head_iter_tmp; 6238fd6c514SCharlie Jenkins struct relocation_entry *rel_entry_iter; 624d8792a57SCharlie Jenkins struct relocation_entry *rel_entry_iter_tmp; 6258fd6c514SCharlie Jenkins int curr_type; 6268fd6c514SCharlie Jenkins void *location; 6278fd6c514SCharlie Jenkins long buffer; 6288fd6c514SCharlie Jenkins 629d8792a57SCharlie Jenkins list_for_each_entry_safe(bucket_iter, bucket_iter_tmp, 630d8792a57SCharlie Jenkins used_buckets_list, head) { 631d8792a57SCharlie Jenkins hlist_for_each_entry_safe(rel_head_iter, rel_head_iter_tmp, 632d8792a57SCharlie Jenkins bucket_iter->bucket, node) { 6338fd6c514SCharlie Jenkins buffer = 0; 6348fd6c514SCharlie Jenkins location = rel_head_iter->location; 635d8792a57SCharlie Jenkins list_for_each_entry_safe(rel_entry_iter, 636d8792a57SCharlie Jenkins rel_entry_iter_tmp, 637*03f0b548SClément Léger &rel_head_iter->rel_entry, 638d8792a57SCharlie Jenkins head) { 6398fd6c514SCharlie Jenkins curr_type = rel_entry_iter->type; 6408fd6c514SCharlie Jenkins reloc_handlers[curr_type].reloc_handler( 6418fd6c514SCharlie Jenkins me, &buffer, rel_entry_iter->value); 6428fd6c514SCharlie Jenkins kfree(rel_entry_iter); 6438fd6c514SCharlie Jenkins } 6448fd6c514SCharlie Jenkins reloc_handlers[curr_type].accumulate_handler( 6458fd6c514SCharlie Jenkins me, location, buffer); 6468fd6c514SCharlie Jenkins kfree(rel_head_iter); 6478fd6c514SCharlie Jenkins } 6488fd6c514SCharlie Jenkins kfree(bucket_iter); 6498fd6c514SCharlie Jenkins } 6508fd6c514SCharlie Jenkins 651d8792a57SCharlie Jenkins kfree(*relocation_hashtable); 6528fd6c514SCharlie Jenkins } 6538fd6c514SCharlie Jenkins 654d8792a57SCharlie Jenkins static int add_relocation_to_accumulate(struct module *me, int type, 655d8792a57SCharlie Jenkins void *location, 656d8792a57SCharlie Jenkins unsigned int hashtable_bits, Elf_Addr v, 657d8792a57SCharlie Jenkins struct hlist_head *relocation_hashtable, 658d8792a57SCharlie Jenkins struct list_head *used_buckets_list) 6598fd6c514SCharlie Jenkins { 6608fd6c514SCharlie Jenkins struct relocation_entry *entry; 6618fd6c514SCharlie Jenkins struct relocation_head *rel_head; 6628fd6c514SCharlie Jenkins struct hlist_head *current_head; 6638fd6c514SCharlie Jenkins struct used_bucket *bucket; 6648fd6c514SCharlie Jenkins unsigned long hash; 6658fd6c514SCharlie Jenkins 6668fd6c514SCharlie Jenkins entry = kmalloc(sizeof(*entry), GFP_KERNEL); 667d8792a57SCharlie Jenkins 668d8792a57SCharlie Jenkins if (!entry) 669d8792a57SCharlie Jenkins return -ENOMEM; 670d8792a57SCharlie Jenkins 6718fd6c514SCharlie Jenkins INIT_LIST_HEAD(&entry->head); 6728fd6c514SCharlie Jenkins entry->type = type; 6738fd6c514SCharlie Jenkins entry->value = v; 6748fd6c514SCharlie Jenkins 6758fd6c514SCharlie Jenkins hash = hash_min((uintptr_t)location, hashtable_bits); 6768fd6c514SCharlie Jenkins 6778fd6c514SCharlie Jenkins current_head = &relocation_hashtable[hash]; 6788fd6c514SCharlie Jenkins 679d8792a57SCharlie Jenkins /* 680d8792a57SCharlie Jenkins * Search for the relocation_head for the relocations that happen at the 681d8792a57SCharlie Jenkins * provided location 682d8792a57SCharlie Jenkins */ 6838fd6c514SCharlie Jenkins bool found = false; 6848fd6c514SCharlie Jenkins struct relocation_head *rel_head_iter; 6858fd6c514SCharlie Jenkins 6868fd6c514SCharlie Jenkins hlist_for_each_entry(rel_head_iter, current_head, node) { 6878fd6c514SCharlie Jenkins if (rel_head_iter->location == location) { 6888fd6c514SCharlie Jenkins found = true; 6898fd6c514SCharlie Jenkins rel_head = rel_head_iter; 6908fd6c514SCharlie Jenkins break; 6918fd6c514SCharlie Jenkins } 6928fd6c514SCharlie Jenkins } 6938fd6c514SCharlie Jenkins 694d8792a57SCharlie Jenkins /* 695d8792a57SCharlie Jenkins * If there has not yet been any relocations at the provided location, 696d8792a57SCharlie Jenkins * create a relocation_head for that location and populate it with this 697d8792a57SCharlie Jenkins * relocation_entry. 698d8792a57SCharlie Jenkins */ 6998fd6c514SCharlie Jenkins if (!found) { 7008fd6c514SCharlie Jenkins rel_head = kmalloc(sizeof(*rel_head), GFP_KERNEL); 701d8792a57SCharlie Jenkins 702d8792a57SCharlie Jenkins if (!rel_head) { 703d8792a57SCharlie Jenkins kfree(entry); 704d8792a57SCharlie Jenkins return -ENOMEM; 705d8792a57SCharlie Jenkins } 706d8792a57SCharlie Jenkins 707*03f0b548SClément Léger INIT_LIST_HEAD(&rel_head->rel_entry); 7088fd6c514SCharlie Jenkins rel_head->location = location; 7098fd6c514SCharlie Jenkins INIT_HLIST_NODE(&rel_head->node); 7108fd6c514SCharlie Jenkins if (!current_head->first) { 7118fd6c514SCharlie Jenkins bucket = 7128fd6c514SCharlie Jenkins kmalloc(sizeof(struct used_bucket), GFP_KERNEL); 713d8792a57SCharlie Jenkins 714d8792a57SCharlie Jenkins if (!bucket) { 715d8792a57SCharlie Jenkins kfree(entry); 71678996eeeSCharlie Jenkins kfree(rel_head); 717d8792a57SCharlie Jenkins return -ENOMEM; 718d8792a57SCharlie Jenkins } 719d8792a57SCharlie Jenkins 7208fd6c514SCharlie Jenkins INIT_LIST_HEAD(&bucket->head); 7218fd6c514SCharlie Jenkins bucket->bucket = current_head; 722d8792a57SCharlie Jenkins list_add(&bucket->head, used_buckets_list); 7238fd6c514SCharlie Jenkins } 7248fd6c514SCharlie Jenkins hlist_add_head(&rel_head->node, current_head); 7258fd6c514SCharlie Jenkins } 7268fd6c514SCharlie Jenkins 7278fd6c514SCharlie Jenkins /* Add relocation to head of discovered rel_head */ 728*03f0b548SClément Léger list_add_tail(&entry->head, &rel_head->rel_entry); 7298fd6c514SCharlie Jenkins 7308fd6c514SCharlie Jenkins return 0; 7318fd6c514SCharlie Jenkins } 7328fd6c514SCharlie Jenkins 733d8792a57SCharlie Jenkins static unsigned int 734d8792a57SCharlie Jenkins initialize_relocation_hashtable(unsigned int num_relocations, 735d8792a57SCharlie Jenkins struct hlist_head **relocation_hashtable) 7368fd6c514SCharlie Jenkins { 7378fd6c514SCharlie Jenkins /* Can safely assume that bits is not greater than sizeof(long) */ 7388fd6c514SCharlie Jenkins unsigned long hashtable_size = roundup_pow_of_two(num_relocations); 7394b38b36bSCharlie Jenkins /* 7404b38b36bSCharlie Jenkins * When hashtable_size == 1, hashtable_bits == 0. 7414b38b36bSCharlie Jenkins * This is valid because the hashing algorithm returns 0 in this case. 7424b38b36bSCharlie Jenkins */ 7438fd6c514SCharlie Jenkins unsigned int hashtable_bits = ilog2(hashtable_size); 7448fd6c514SCharlie Jenkins 7458fd6c514SCharlie Jenkins /* 7468fd6c514SCharlie Jenkins * Double size of hashtable if num_relocations * 1.25 is greater than 7478fd6c514SCharlie Jenkins * hashtable_size. 7488fd6c514SCharlie Jenkins */ 7498fd6c514SCharlie Jenkins int should_double_size = ((num_relocations + (num_relocations >> 2)) > (hashtable_size)); 7508fd6c514SCharlie Jenkins 7518fd6c514SCharlie Jenkins hashtable_bits += should_double_size; 7528fd6c514SCharlie Jenkins 7538fd6c514SCharlie Jenkins hashtable_size <<= should_double_size; 7548fd6c514SCharlie Jenkins 755d8792a57SCharlie Jenkins *relocation_hashtable = kmalloc_array(hashtable_size, 756a35551c7SCharlie Jenkins sizeof(**relocation_hashtable), 7578fd6c514SCharlie Jenkins GFP_KERNEL); 758d8792a57SCharlie Jenkins if (!*relocation_hashtable) 7594b38b36bSCharlie Jenkins return 0; 7608fd6c514SCharlie Jenkins 761d8792a57SCharlie Jenkins __hash_init(*relocation_hashtable, hashtable_size); 7628fd6c514SCharlie Jenkins 7638fd6c514SCharlie Jenkins return hashtable_bits; 7648fd6c514SCharlie Jenkins } 7658fd6c514SCharlie Jenkins 766e2c0cdfbSPalmer Dabbelt int apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab, 767e2c0cdfbSPalmer Dabbelt unsigned int symindex, unsigned int relsec, 768e2c0cdfbSPalmer Dabbelt struct module *me) 769e2c0cdfbSPalmer Dabbelt { 770e2c0cdfbSPalmer Dabbelt Elf_Rela *rel = (void *) sechdrs[relsec].sh_addr; 7718cbe0accSEmil Renner Berthing int (*handler)(struct module *me, void *location, Elf_Addr v); 772e2c0cdfbSPalmer Dabbelt Elf_Sym *sym; 7738cbe0accSEmil Renner Berthing void *location; 774e2c0cdfbSPalmer Dabbelt unsigned int i, type; 775080c4324SMaxim Kochetkov unsigned int j_idx = 0; 776e2c0cdfbSPalmer Dabbelt Elf_Addr v; 777e2c0cdfbSPalmer Dabbelt int res; 7788fd6c514SCharlie Jenkins unsigned int num_relocations = sechdrs[relsec].sh_size / sizeof(*rel); 779d8792a57SCharlie Jenkins struct hlist_head *relocation_hashtable; 780d8792a57SCharlie Jenkins unsigned int hashtable_bits; 7813cc754c2SJinjie Ruan LIST_HEAD(used_buckets_list); 782d8792a57SCharlie Jenkins 783d8792a57SCharlie Jenkins hashtable_bits = initialize_relocation_hashtable(num_relocations, 784d8792a57SCharlie Jenkins &relocation_hashtable); 785d8792a57SCharlie Jenkins 7864b38b36bSCharlie Jenkins if (!relocation_hashtable) 7874b38b36bSCharlie Jenkins return -ENOMEM; 788d8792a57SCharlie Jenkins 789e2c0cdfbSPalmer Dabbelt pr_debug("Applying relocate section %u to %u\n", relsec, 790e2c0cdfbSPalmer Dabbelt sechdrs[relsec].sh_info); 791e2c0cdfbSPalmer Dabbelt 7928fd6c514SCharlie Jenkins for (i = 0; i < num_relocations; i++) { 793e2c0cdfbSPalmer Dabbelt /* This is where to make the change */ 794e2c0cdfbSPalmer Dabbelt location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr 795e2c0cdfbSPalmer Dabbelt + rel[i].r_offset; 796e2c0cdfbSPalmer Dabbelt /* This is the symbol it is referring to */ 797e2c0cdfbSPalmer Dabbelt sym = (Elf_Sym *)sechdrs[symindex].sh_addr 798e2c0cdfbSPalmer Dabbelt + ELF_RISCV_R_SYM(rel[i].r_info); 799e2c0cdfbSPalmer Dabbelt if (IS_ERR_VALUE(sym->st_value)) { 800e2c0cdfbSPalmer Dabbelt /* Ignore unresolved weak symbol */ 801e2c0cdfbSPalmer Dabbelt if (ELF_ST_BIND(sym->st_info) == STB_WEAK) 802e2c0cdfbSPalmer Dabbelt continue; 80304ce8d3fSKefeng Wang pr_warn("%s: Unknown symbol %s\n", 804e2c0cdfbSPalmer Dabbelt me->name, strtab + sym->st_name); 805e2c0cdfbSPalmer Dabbelt return -ENOENT; 806e2c0cdfbSPalmer Dabbelt } 807e2c0cdfbSPalmer Dabbelt 808e2c0cdfbSPalmer Dabbelt type = ELF_RISCV_R_TYPE(rel[i].r_info); 809e2c0cdfbSPalmer Dabbelt 8108fd6c514SCharlie Jenkins if (type < ARRAY_SIZE(reloc_handlers)) 8118fd6c514SCharlie Jenkins handler = reloc_handlers[type].reloc_handler; 812e2c0cdfbSPalmer Dabbelt else 813e2c0cdfbSPalmer Dabbelt handler = NULL; 814e2c0cdfbSPalmer Dabbelt 815e2c0cdfbSPalmer Dabbelt if (!handler) { 816e2c0cdfbSPalmer Dabbelt pr_err("%s: Unknown relocation type %u\n", 817e2c0cdfbSPalmer Dabbelt me->name, type); 818e2c0cdfbSPalmer Dabbelt return -EINVAL; 819e2c0cdfbSPalmer Dabbelt } 820e2c0cdfbSPalmer Dabbelt 821e2c0cdfbSPalmer Dabbelt v = sym->st_value + rel[i].r_addend; 822e2c0cdfbSPalmer Dabbelt 823e2c0cdfbSPalmer Dabbelt if (type == R_RISCV_PCREL_LO12_I || type == R_RISCV_PCREL_LO12_S) { 824080c4324SMaxim Kochetkov unsigned int j = j_idx; 825080c4324SMaxim Kochetkov bool found = false; 826e2c0cdfbSPalmer Dabbelt 827080c4324SMaxim Kochetkov do { 8287df85002SZong Li unsigned long hi20_loc = 829e2c0cdfbSPalmer Dabbelt sechdrs[sechdrs[relsec].sh_info].sh_addr 830e2c0cdfbSPalmer Dabbelt + rel[j].r_offset; 831da975dd4SZong Li u32 hi20_type = ELF_RISCV_R_TYPE(rel[j].r_info); 832da975dd4SZong Li 833da975dd4SZong Li /* Find the corresponding HI20 relocation entry */ 834da975dd4SZong Li if (hi20_loc == sym->st_value 835da975dd4SZong Li && (hi20_type == R_RISCV_PCREL_HI20 836da975dd4SZong Li || hi20_type == R_RISCV_GOT_HI20)) { 837da975dd4SZong Li s32 hi20, lo12; 838e2c0cdfbSPalmer Dabbelt Elf_Sym *hi20_sym = 839e2c0cdfbSPalmer Dabbelt (Elf_Sym *)sechdrs[symindex].sh_addr 840e2c0cdfbSPalmer Dabbelt + ELF_RISCV_R_SYM(rel[j].r_info); 8417df85002SZong Li unsigned long hi20_sym_val = 842e2c0cdfbSPalmer Dabbelt hi20_sym->st_value 843e2c0cdfbSPalmer Dabbelt + rel[j].r_addend; 844da975dd4SZong Li 845e2c0cdfbSPalmer Dabbelt /* Calculate lo12 */ 8467df85002SZong Li size_t offset = hi20_sym_val - hi20_loc; 847da975dd4SZong Li if (IS_ENABLED(CONFIG_MODULE_SECTIONS) 848da975dd4SZong Li && hi20_type == R_RISCV_GOT_HI20) { 849da975dd4SZong Li offset = module_emit_got_entry( 850da975dd4SZong Li me, hi20_sym_val); 851da975dd4SZong Li offset = offset - hi20_loc; 852da975dd4SZong Li } 853da975dd4SZong Li hi20 = (offset + 0x800) & 0xfffff000; 854da975dd4SZong Li lo12 = offset - hi20; 855e2c0cdfbSPalmer Dabbelt v = lo12; 856080c4324SMaxim Kochetkov found = true; 857da975dd4SZong Li 858e2c0cdfbSPalmer Dabbelt break; 859e2c0cdfbSPalmer Dabbelt } 860080c4324SMaxim Kochetkov 861080c4324SMaxim Kochetkov j++; 862080c4324SMaxim Kochetkov if (j > sechdrs[relsec].sh_size / sizeof(*rel)) 863080c4324SMaxim Kochetkov j = 0; 864080c4324SMaxim Kochetkov 865080c4324SMaxim Kochetkov } while (j_idx != j); 866080c4324SMaxim Kochetkov 867080c4324SMaxim Kochetkov if (!found) { 868e2c0cdfbSPalmer Dabbelt pr_err( 869da975dd4SZong Li "%s: Can not find HI20 relocation information\n", 870e2c0cdfbSPalmer Dabbelt me->name); 871e2c0cdfbSPalmer Dabbelt return -EINVAL; 872e2c0cdfbSPalmer Dabbelt } 873080c4324SMaxim Kochetkov 874080c4324SMaxim Kochetkov /* Record the previous j-loop end index */ 875080c4324SMaxim Kochetkov j_idx = j; 876e2c0cdfbSPalmer Dabbelt } 877e2c0cdfbSPalmer Dabbelt 8788fd6c514SCharlie Jenkins if (reloc_handlers[type].accumulate_handler) 879d8792a57SCharlie Jenkins res = add_relocation_to_accumulate(me, type, location, 880d8792a57SCharlie Jenkins hashtable_bits, v, 881d8792a57SCharlie Jenkins relocation_hashtable, 882d8792a57SCharlie Jenkins &used_buckets_list); 8838fd6c514SCharlie Jenkins else 884e2c0cdfbSPalmer Dabbelt res = handler(me, location, v); 885e2c0cdfbSPalmer Dabbelt if (res) 886e2c0cdfbSPalmer Dabbelt return res; 887e2c0cdfbSPalmer Dabbelt } 888e2c0cdfbSPalmer Dabbelt 889d8792a57SCharlie Jenkins process_accumulated_relocations(me, &relocation_hashtable, 890d8792a57SCharlie Jenkins &used_buckets_list); 8918fd6c514SCharlie Jenkins 892e2c0cdfbSPalmer Dabbelt return 0; 893e2c0cdfbSPalmer Dabbelt } 8940cff8bffSVincent Chen 895a8e91016SHeiko Stuebner int module_finalize(const Elf_Ehdr *hdr, 896a8e91016SHeiko Stuebner const Elf_Shdr *sechdrs, 897a8e91016SHeiko Stuebner struct module *me) 898a8e91016SHeiko Stuebner { 899a8e91016SHeiko Stuebner const Elf_Shdr *s; 900a8e91016SHeiko Stuebner 901a8e91016SHeiko Stuebner s = find_section(hdr, sechdrs, ".alternative"); 902a8e91016SHeiko Stuebner if (s) 903a8e91016SHeiko Stuebner apply_module_alternatives((void *)s->sh_addr, s->sh_size); 904a8e91016SHeiko Stuebner 905a8e91016SHeiko Stuebner return 0; 906a8e91016SHeiko Stuebner } 907