With the C-extension regular 32bit instructions are not
necessarily aligned on 4-byte boundaries. RISC-V instructions
are in fact an ordered list of 16bit native-endian
"parcels", so access the instruction as such.

This should also make the code work in case someone builds
a big-endian RISC-V machine.

Fix rcv -> rvc typo while we're at it.

Signed-off-by: Emil Renner Berthing <ker...@esmil.dk>
---
 arch/riscv/kernel/module.c | 155 +++++++++++++++++++------------------
 1 file changed, 79 insertions(+), 76 deletions(-)

diff --git a/arch/riscv/kernel/module.c b/arch/riscv/kernel/module.c
index 104fba889cf7..05b2162d96be 100644
--- a/arch/riscv/kernel/module.c
+++ b/arch/riscv/kernel/module.c
@@ -13,68 +13,86 @@
 #include <linux/pgtable.h>
 #include <asm/sections.h>
 
-static int apply_r_riscv_32_rela(struct module *me, u32 *location, Elf_Addr v)
+static int riscv_insn_rmw(u8 *location, u32 keep, u32 set)
+{
+       u16 *parcel = (u16 *)location;
+       u32 insn = (u32)parcel[0] | (u32)parcel[1] << 16;
+
+       insn &= keep;
+       insn |= set;
+
+       parcel[0] = insn;
+       parcel[1] = insn >> 16;
+       return 0;
+}
+
+static int riscv_insn_rvc_rmw(u8 *location, u16 keep, u16 set)
+{
+       u16 *parcel = (u16 *)location;
+
+       *parcel = (*parcel & keep) | set;
+       return 0;
+}
+
+static int apply_r_riscv_32_rela(struct module *me, u8 *location, Elf_Addr v)
 {
        if (v != (u32)v) {
                pr_err("%s: value %016llx out of range for 32-bit field\n",
                       me->name, (long long)v);
                return -EINVAL;
        }
-       *location = v;
+       *(u32 *)location = v;
        return 0;
 }
 
-static int apply_r_riscv_64_rela(struct module *me, u32 *location, Elf_Addr v)
+static int apply_r_riscv_64_rela(struct module *me, u8 *location, Elf_Addr v)
 {
        *(u64 *)location = v;
        return 0;
 }
 
-static int apply_r_riscv_branch_rela(struct module *me, u32 *location,
+static int apply_r_riscv_branch_rela(struct module *me, u8 *location,
                                     Elf_Addr v)
 {
-       ptrdiff_t offset = (void *)v - (void *)location;
+       ptrdiff_t offset = (u8 *)v - location;
        u32 imm12 = (offset & 0x1000) << (31 - 12);
        u32 imm11 = (offset & 0x800) >> (11 - 7);
        u32 imm10_5 = (offset & 0x7e0) << (30 - 10);
        u32 imm4_1 = (offset & 0x1e) << (11 - 4);
 
-       *location = (*location & 0x1fff07f) | imm12 | imm11 | imm10_5 | imm4_1;
-       return 0;
+       return riscv_insn_rmw(location, 0x1fff07f, imm12 | imm11 | imm10_5 | 
imm4_1);
 }
 
-static int apply_r_riscv_jal_rela(struct module *me, u32 *location,
+static int apply_r_riscv_jal_rela(struct module *me, u8 *location,
                                  Elf_Addr v)
 {
-       ptrdiff_t offset = (void *)v - (void *)location;
+       ptrdiff_t offset = (u8 *)v - location;
        u32 imm20 = (offset & 0x100000) << (31 - 20);
        u32 imm19_12 = (offset & 0xff000);
        u32 imm11 = (offset & 0x800) << (20 - 11);
        u32 imm10_1 = (offset & 0x7fe) << (30 - 10);
 
-       *location = (*location & 0xfff) | imm20 | imm19_12 | imm11 | imm10_1;
-       return 0;
+       return riscv_insn_rmw(location, 0xfff, imm20 | imm19_12 | imm11 | 
imm10_1);
 }
 
-static int apply_r_riscv_rcv_branch_rela(struct module *me, u32 *location,
+static int apply_r_riscv_rvc_branch_rela(struct module *me, u8 *location,
                                         Elf_Addr v)
 {
-       ptrdiff_t offset = (void *)v - (void *)location;
+       ptrdiff_t offset = (u8 *)v - location;
        u16 imm8 = (offset & 0x100) << (12 - 8);
        u16 imm7_6 = (offset & 0xc0) >> (6 - 5);
        u16 imm5 = (offset & 0x20) >> (5 - 2);
        u16 imm4_3 = (offset & 0x18) << (12 - 5);
        u16 imm2_1 = (offset & 0x6) << (12 - 10);
 
-       *(u16 *)location = (*(u16 *)location & 0xe383) |
-                   imm8 | imm7_6 | imm5 | imm4_3 | imm2_1;
-       return 0;
+       return riscv_insn_rvc_rmw(location, 0xe383,
+                       imm8 | imm7_6 | imm5 | imm4_3 | imm2_1);
 }
 
-static int apply_r_riscv_rvc_jump_rela(struct module *me, u32 *location,
+static int apply_r_riscv_rvc_jump_rela(struct module *me, u8 *location,
                                       Elf_Addr v)
 {
-       ptrdiff_t offset = (void *)v - (void *)location;
+       ptrdiff_t offset = (u8 *)v - location;
        u16 imm11 = (offset & 0x800) << (12 - 11);
        u16 imm10 = (offset & 0x400) >> (10 - 8);
        u16 imm9_8 = (offset & 0x300) << (12 - 11);
@@ -84,16 +102,14 @@ static int apply_r_riscv_rvc_jump_rela(struct module *me, 
u32 *location,
        u16 imm4 = (offset & 0x10) << (12 - 5);
        u16 imm3_1 = (offset & 0xe) << (12 - 10);
 
-       *(u16 *)location = (*(u16 *)location & 0xe003) |
-                   imm11 | imm10 | imm9_8 | imm7 | imm6 | imm5 | imm4 | imm3_1;
-       return 0;
+       return riscv_insn_rvc_rmw(location, 0xe003,
+                       imm11 | imm10 | imm9_8 | imm7 | imm6 | imm5 | imm4 | 
imm3_1);
 }
 
-static int apply_r_riscv_pcrel_hi20_rela(struct module *me, u32 *location,
+static int apply_r_riscv_pcrel_hi20_rela(struct module *me, u8 *location,
                                         Elf_Addr v)
 {
-       ptrdiff_t offset = (void *)v - (void *)location;
-       s32 hi20;
+       ptrdiff_t offset = (u8 *)v - location;
 
        if (offset != (s32)offset) {
                pr_err(
@@ -102,23 +118,20 @@ static int apply_r_riscv_pcrel_hi20_rela(struct module 
*me, u32 *location,
                return -EINVAL;
        }
 
-       hi20 = (offset + 0x800) & 0xfffff000;
-       *location = (*location & 0xfff) | hi20;
-       return 0;
+       return riscv_insn_rmw(location, 0xfff, (offset + 0x800) & 0xfffff000);
 }
 
-static int apply_r_riscv_pcrel_lo12_i_rela(struct module *me, u32 *location,
+static int apply_r_riscv_pcrel_lo12_i_rela(struct module *me, u8 *location,
                                           Elf_Addr v)
 {
        /*
         * v is the lo12 value to fill. It is calculated before calling this
         * handler.
         */
-       *location = (*location & 0xfffff) | ((v & 0xfff) << 20);
-       return 0;
+       return riscv_insn_rmw(location, 0xfffff, (v & 0xfff) << 20);
 }
 
-static int apply_r_riscv_pcrel_lo12_s_rela(struct module *me, u32 *location,
+static int apply_r_riscv_pcrel_lo12_s_rela(struct module *me, u8 *location,
                                           Elf_Addr v)
 {
        /*
@@ -128,15 +141,12 @@ static int apply_r_riscv_pcrel_lo12_s_rela(struct module 
*me, u32 *location,
        u32 imm11_5 = (v & 0xfe0) << (31 - 11);
        u32 imm4_0 = (v & 0x1f) << (11 - 4);
 
-       *location = (*location & 0x1fff07f) | imm11_5 | imm4_0;
-       return 0;
+       return riscv_insn_rmw(location, 0x1fff07f, imm11_5 | imm4_0);
 }
 
-static int apply_r_riscv_hi20_rela(struct module *me, u32 *location,
+static int apply_r_riscv_hi20_rela(struct module *me, u8 *location,
                                   Elf_Addr v)
 {
-       s32 hi20;
-
        if (IS_ENABLED(CONFIG_CMODEL_MEDLOW)) {
                pr_err(
                  "%s: target %016llx can not be addressed by the 32-bit offset 
from PC = %p\n",
@@ -144,22 +154,20 @@ static int apply_r_riscv_hi20_rela(struct module *me, u32 
*location,
                return -EINVAL;
        }
 
-       hi20 = ((s32)v + 0x800) & 0xfffff000;
-       *location = (*location & 0xfff) | hi20;
-       return 0;
+       return riscv_insn_rmw(location, 0xfff, ((s32)v + 0x800) & 0xfffff000);
 }
 
-static int apply_r_riscv_lo12_i_rela(struct module *me, u32 *location,
+static int apply_r_riscv_lo12_i_rela(struct module *me, u8 *location,
                                     Elf_Addr v)
 {
        /* Skip medlow checking because of filtering by HI20 already */
        s32 hi20 = ((s32)v + 0x800) & 0xfffff000;
        s32 lo12 = ((s32)v - hi20);
-       *location = (*location & 0xfffff) | ((lo12 & 0xfff) << 20);
-       return 0;
+
+       return riscv_insn_rmw(location, 0xfffff, (lo12 & 0xfff) << 20);
 }
 
-static int apply_r_riscv_lo12_s_rela(struct module *me, u32 *location,
+static int apply_r_riscv_lo12_s_rela(struct module *me, u8 *location,
                                     Elf_Addr v)
 {
        /* Skip medlow checking because of filtering by HI20 already */
@@ -167,20 +175,19 @@ static int apply_r_riscv_lo12_s_rela(struct module *me, 
u32 *location,
        s32 lo12 = ((s32)v - hi20);
        u32 imm11_5 = (lo12 & 0xfe0) << (31 - 11);
        u32 imm4_0 = (lo12 & 0x1f) << (11 - 4);
-       *location = (*location & 0x1fff07f) | imm11_5 | imm4_0;
-       return 0;
+
+       return riscv_insn_rmw(location, 0x1fff07f, imm11_5 | imm4_0);
 }
 
-static int apply_r_riscv_got_hi20_rela(struct module *me, u32 *location,
+static int apply_r_riscv_got_hi20_rela(struct module *me, u8 *location,
                                       Elf_Addr v)
 {
-       ptrdiff_t offset = (void *)v - (void *)location;
-       s32 hi20;
+       ptrdiff_t offset = (u8 *)v - location;
 
        /* Always emit the got entry */
        if (IS_ENABLED(CONFIG_MODULE_SECTIONS)) {
-               offset = module_emit_got_entry(me, v);
-               offset = (void *)offset - (void *)location;
+               unsigned long entry = module_emit_got_entry(me, v);
+               offset = (u8 *)entry - location;
        } else {
                pr_err(
                  "%s: can not generate the GOT entry for symbol = %016llx from 
PC = %p\n",
@@ -188,23 +195,21 @@ static int apply_r_riscv_got_hi20_rela(struct module *me, 
u32 *location,
                return -EINVAL;
        }
 
-       hi20 = (offset + 0x800) & 0xfffff000;
-       *location = (*location & 0xfff) | hi20;
-       return 0;
+       return riscv_insn_rmw(location, 0xfff, (offset + 0x800) & 0xfffff000);
 }
 
-static int apply_r_riscv_call_plt_rela(struct module *me, u32 *location,
+static int apply_r_riscv_call_plt_rela(struct module *me, u8 *location,
                                       Elf_Addr v)
 {
-       ptrdiff_t offset = (void *)v - (void *)location;
+       ptrdiff_t offset = (u8 *)v - location;
        s32 fill_v = offset;
        u32 hi20, lo12;
 
        if (offset != fill_v) {
                /* Only emit the plt entry if offset over 32-bit range */
                if (IS_ENABLED(CONFIG_MODULE_SECTIONS)) {
-                       offset = module_emit_plt_entry(me, v);
-                       offset = (void *)offset - (void *)location;
+                       unsigned long entry = module_emit_plt_entry(me, v);
+                       offset = (u8 *)entry - location;
                } else {
                        pr_err(
                          "%s: target %016llx can not be addressed by the 
32-bit offset from PC = %p\n",
@@ -215,15 +220,14 @@ static int apply_r_riscv_call_plt_rela(struct module *me, 
u32 *location,
 
        hi20 = (offset + 0x800) & 0xfffff000;
        lo12 = (offset - hi20) & 0xfff;
-       *location = (*location & 0xfff) | hi20;
-       *(location + 1) = (*(location + 1) & 0xfffff) | (lo12 << 20);
-       return 0;
+       riscv_insn_rmw(location, 0xfff, hi20);
+       return riscv_insn_rmw(location + 4, 0xfffff, lo12 << 20);
 }
 
-static int apply_r_riscv_call_rela(struct module *me, u32 *location,
+static int apply_r_riscv_call_rela(struct module *me, u8 *location,
                                   Elf_Addr v)
 {
-       ptrdiff_t offset = (void *)v - (void *)location;
+       ptrdiff_t offset = (u8 *)v - location;
        s32 fill_v = offset;
        u32 hi20, lo12;
 
@@ -236,18 +240,17 @@ static int apply_r_riscv_call_rela(struct module *me, u32 
*location,
 
        hi20 = (offset + 0x800) & 0xfffff000;
        lo12 = (offset - hi20) & 0xfff;
-       *location = (*location & 0xfff) | hi20;
-       *(location + 1) = (*(location + 1) & 0xfffff) | (lo12 << 20);
-       return 0;
+       riscv_insn_rmw(location, 0xfff, hi20);
+       return riscv_insn_rmw(location + 4, 0xfffff, lo12 << 20);
 }
 
-static int apply_r_riscv_relax_rela(struct module *me, u32 *location,
+static int apply_r_riscv_relax_rela(struct module *me, u8 *location,
                                    Elf_Addr v)
 {
        return 0;
 }
 
-static int apply_r_riscv_align_rela(struct module *me, u32 *location,
+static int apply_r_riscv_align_rela(struct module *me, u8 *location,
                                    Elf_Addr v)
 {
        pr_err(
@@ -256,41 +259,41 @@ static int apply_r_riscv_align_rela(struct module *me, 
u32 *location,
        return -EINVAL;
 }
 
-static int apply_r_riscv_add32_rela(struct module *me, u32 *location,
+static int apply_r_riscv_add32_rela(struct module *me, u8 *location,
                                    Elf_Addr v)
 {
        *(u32 *)location += (u32)v;
        return 0;
 }
 
-static int apply_r_riscv_add64_rela(struct module *me, u32 *location,
+static int apply_r_riscv_add64_rela(struct module *me, u8 *location,
                                    Elf_Addr v)
 {
        *(u64 *)location += (u64)v;
        return 0;
 }
 
-static int apply_r_riscv_sub32_rela(struct module *me, u32 *location,
+static int apply_r_riscv_sub32_rela(struct module *me, u8 *location,
                                    Elf_Addr v)
 {
        *(u32 *)location -= (u32)v;
        return 0;
 }
 
-static int apply_r_riscv_sub64_rela(struct module *me, u32 *location,
+static int apply_r_riscv_sub64_rela(struct module *me, u8 *location,
                                    Elf_Addr v)
 {
        *(u64 *)location -= (u64)v;
        return 0;
 }
 
-static int (*reloc_handlers_rela[]) (struct module *me, u32 *location,
+static int (*reloc_handlers_rela[]) (struct module *me, u8 *location,
                                Elf_Addr v) = {
        [R_RISCV_32]                    = apply_r_riscv_32_rela,
        [R_RISCV_64]                    = apply_r_riscv_64_rela,
        [R_RISCV_BRANCH]                = apply_r_riscv_branch_rela,
        [R_RISCV_JAL]                   = apply_r_riscv_jal_rela,
-       [R_RISCV_RVC_BRANCH]            = apply_r_riscv_rcv_branch_rela,
+       [R_RISCV_RVC_BRANCH]            = apply_r_riscv_rvc_branch_rela,
        [R_RISCV_RVC_JUMP]              = apply_r_riscv_rvc_jump_rela,
        [R_RISCV_PCREL_HI20]            = apply_r_riscv_pcrel_hi20_rela,
        [R_RISCV_PCREL_LO12_I]          = apply_r_riscv_pcrel_lo12_i_rela,
@@ -314,9 +317,9 @@ int apply_relocate_add(Elf_Shdr *sechdrs, const char 
*strtab,
                       struct module *me)
 {
        Elf_Rela *rel = (void *) sechdrs[relsec].sh_addr;
-       int (*handler)(struct module *me, u32 *location, Elf_Addr v);
+       int (*handler)(struct module *me, u8 *location, Elf_Addr v);
        Elf_Sym *sym;
-       u32 *location;
+       u8 *location;
        unsigned int i, type;
        Elf_Addr v;
        int res;
@@ -326,7 +329,7 @@ int apply_relocate_add(Elf_Shdr *sechdrs, const char 
*strtab,
 
        for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
                /* This is where to make the change */
-               location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
+               location = (u8 *)sechdrs[sechdrs[relsec].sh_info].sh_addr
                        + rel[i].r_offset;
                /* This is the symbol it is referring to */
                sym = (Elf_Sym *)sechdrs[symindex].sh_addr
-- 
2.27.0

Reply via email to