Zong Li | ab1ef68 | 2018-03-15 16:50:41 +0800 | [diff] [blame] | 1 | /* SPDX-License-Identifier: GPL-2.0 */ |
| 2 | /* Copyright (C) 2017 Andes Technology Corporation */ |
| 3 | |
| 4 | #ifndef _ASM_RISCV_MODULE_H |
| 5 | #define _ASM_RISCV_MODULE_H |
| 6 | |
| 7 | #include <asm-generic/module.h> |
| 8 | |
| 9 | #define MODULE_ARCH_VERMAGIC "riscv" |
| 10 | |
| 11 | u64 module_emit_got_entry(struct module *mod, u64 val); |
| 12 | u64 module_emit_plt_entry(struct module *mod, u64 val); |
| 13 | |
| 14 | #ifdef CONFIG_MODULE_SECTIONS |
| 15 | struct mod_section { |
| 16 | struct elf64_shdr *shdr; |
| 17 | int num_entries; |
| 18 | int max_entries; |
| 19 | }; |
| 20 | |
| 21 | struct mod_arch_specific { |
| 22 | struct mod_section got; |
| 23 | struct mod_section plt; |
Zong Li | b8bde0e | 2018-03-15 16:50:42 +0800 | [diff] [blame] | 24 | struct mod_section got_plt; |
Zong Li | ab1ef68 | 2018-03-15 16:50:41 +0800 | [diff] [blame] | 25 | }; |
| 26 | |
| 27 | struct got_entry { |
| 28 | u64 symbol_addr; /* the real variable address */ |
| 29 | }; |
| 30 | |
| 31 | static inline struct got_entry emit_got_entry(u64 val) |
| 32 | { |
| 33 | return (struct got_entry) {val}; |
| 34 | } |
| 35 | |
| 36 | static inline struct got_entry *get_got_entry(u64 val, |
| 37 | const struct mod_section *sec) |
| 38 | { |
| 39 | struct got_entry *got = (struct got_entry *)sec->shdr->sh_addr; |
| 40 | int i; |
| 41 | for (i = 0; i < sec->num_entries; i++) { |
| 42 | if (got[i].symbol_addr == val) |
| 43 | return &got[i]; |
| 44 | } |
| 45 | return NULL; |
| 46 | } |
| 47 | |
| 48 | struct plt_entry { |
| 49 | /* |
| 50 | * Trampoline code to real target address. The return address |
| 51 | * should be the original (pc+4) before entring plt entry. |
Zong Li | ab1ef68 | 2018-03-15 16:50:41 +0800 | [diff] [blame] | 52 | */ |
| 53 | u32 insn_auipc; /* auipc t0, 0x0 */ |
| 54 | u32 insn_ld; /* ld t1, 0x10(t0) */ |
| 55 | u32 insn_jr; /* jr t1 */ |
Zong Li | ab1ef68 | 2018-03-15 16:50:41 +0800 | [diff] [blame] | 56 | }; |
| 57 | |
| 58 | #define OPC_AUIPC 0x0017 |
| 59 | #define OPC_LD 0x3003 |
| 60 | #define OPC_JALR 0x0067 |
| 61 | #define REG_T0 0x5 |
| 62 | #define REG_T1 0x6 |
Zong Li | ab1ef68 | 2018-03-15 16:50:41 +0800 | [diff] [blame] | 63 | |
Zong Li | b8bde0e | 2018-03-15 16:50:42 +0800 | [diff] [blame] | 64 | static inline struct plt_entry emit_plt_entry(u64 val, u64 plt, u64 got_plt) |
Zong Li | ab1ef68 | 2018-03-15 16:50:41 +0800 | [diff] [blame] | 65 | { |
| 66 | /* |
| 67 | * U-Type encoding: |
| 68 | * +------------+----------+----------+ |
| 69 | * | imm[31:12] | rd[11:7] | opc[6:0] | |
| 70 | * +------------+----------+----------+ |
| 71 | * |
| 72 | * I-Type encoding: |
| 73 | * +------------+------------+--------+----------+----------+ |
| 74 | * | imm[31:20] | rs1[19:15] | funct3 | rd[11:7] | opc[6:0] | |
| 75 | * +------------+------------+--------+----------+----------+ |
| 76 | * |
| 77 | */ |
Zong Li | b8bde0e | 2018-03-15 16:50:42 +0800 | [diff] [blame] | 78 | u64 offset = got_plt - plt; |
| 79 | u32 hi20 = (offset + 0x800) & 0xfffff000; |
| 80 | u32 lo12 = (offset - hi20); |
Zong Li | ab1ef68 | 2018-03-15 16:50:41 +0800 | [diff] [blame] | 81 | return (struct plt_entry) { |
Zong Li | b8bde0e | 2018-03-15 16:50:42 +0800 | [diff] [blame] | 82 | OPC_AUIPC | (REG_T0 << 7) | hi20, |
| 83 | OPC_LD | (lo12 << 20) | (REG_T0 << 15) | (REG_T1 << 7), |
| 84 | OPC_JALR | (REG_T1 << 15) |
Zong Li | ab1ef68 | 2018-03-15 16:50:41 +0800 | [diff] [blame] | 85 | }; |
| 86 | } |
| 87 | |
Zong Li | b8bde0e | 2018-03-15 16:50:42 +0800 | [diff] [blame] | 88 | static inline int get_got_plt_idx(u64 val, const struct mod_section *sec) |
Zong Li | ab1ef68 | 2018-03-15 16:50:41 +0800 | [diff] [blame] | 89 | { |
Zong Li | b8bde0e | 2018-03-15 16:50:42 +0800 | [diff] [blame] | 90 | struct got_entry *got_plt = (struct got_entry *)sec->shdr->sh_addr; |
Zong Li | ab1ef68 | 2018-03-15 16:50:41 +0800 | [diff] [blame] | 91 | int i; |
| 92 | for (i = 0; i < sec->num_entries; i++) { |
Zong Li | b8bde0e | 2018-03-15 16:50:42 +0800 | [diff] [blame] | 93 | if (got_plt[i].symbol_addr == val) |
| 94 | return i; |
Zong Li | ab1ef68 | 2018-03-15 16:50:41 +0800 | [diff] [blame] | 95 | } |
Zong Li | b8bde0e | 2018-03-15 16:50:42 +0800 | [diff] [blame] | 96 | return -1; |
| 97 | } |
| 98 | |
| 99 | static inline struct plt_entry *get_plt_entry(u64 val, |
| 100 | const struct mod_section *sec_plt, |
| 101 | const struct mod_section *sec_got_plt) |
| 102 | { |
| 103 | struct plt_entry *plt = (struct plt_entry *)sec_plt->shdr->sh_addr; |
| 104 | int got_plt_idx = get_got_plt_idx(val, sec_got_plt); |
| 105 | if (got_plt_idx >= 0) |
| 106 | return plt + got_plt_idx; |
| 107 | else |
| 108 | return NULL; |
Zong Li | ab1ef68 | 2018-03-15 16:50:41 +0800 | [diff] [blame] | 109 | } |
| 110 | |
| 111 | #endif /* CONFIG_MODULE_SECTIONS */ |
| 112 | |
| 113 | #endif /* _ASM_RISCV_MODULE_H */ |