diff options
| author | Jisheng Zhang <jszhang@kernel.org> | 2023-01-13 22:22:05 +0100 |
|---|---|---|
| committer | Palmer Dabbelt <palmer@rivosinc.com> | 2023-01-24 18:43:56 -0800 |
| commit | 9d5567ccf96fb2f1eb83d531eee23ead4aa8f2a3 (patch) | |
| tree | ddec5fe948a1e29c2fa309ad03b046dbb164323c /arch/riscv/kernel/alternative.c | |
| parent | 451fb217cdca0ead3f396f00f8da331401344950 (diff) | |
riscv: fix jal offsets in patched alternatives
Alternatives live in a different section, so offsets used by jal
instruction will point to wrong locations after the patch got applied.
Similar to arm64, adjust the location to consider that offset.
Co-developed-by: Heiko Stuebner <heiko.stuebner@vrull.eu>
Signed-off-by: Jisheng Zhang <jszhang@kernel.org>
Reviewed-by: Conor Dooley <conor.dooley@microchip.com>
Reviewed-by: Andrew Jones <ajones@ventanamicro.com>
Signed-off-by: Heiko Stuebner <heiko.stuebner@vrull.eu>
Link: https://lore.kernel.org/r/20230113212205.3534622-1-heiko@sntech.de
Fixes: 27c653c06505 ("RISC-V: fix auipc-jalr addresses in patched alternatives")
Signed-off-by: Palmer Dabbelt <palmer@rivosinc.com>
Diffstat (limited to 'arch/riscv/kernel/alternative.c')
| -rw-r--r-- | arch/riscv/kernel/alternative.c | 27 |
1 files changed, 27 insertions, 0 deletions
diff --git a/arch/riscv/kernel/alternative.c b/arch/riscv/kernel/alternative.c index 6212ea0eed72..3d4f1f32c7f6 100644 --- a/arch/riscv/kernel/alternative.c +++ b/arch/riscv/kernel/alternative.c @@ -79,6 +79,21 @@ static void riscv_alternative_fix_auipc_jalr(void *ptr, u32 auipc_insn, patch_text_nosync(ptr, call, sizeof(u32) * 2); } +static void riscv_alternative_fix_jal(void *ptr, u32 jal_insn, int patch_offset) +{ + s32 imm; + + /* get and adjust new target address */ + imm = riscv_insn_extract_jtype_imm(jal_insn); + imm -= patch_offset; + + /* update instruction */ + riscv_insn_insert_jtype_imm(&jal_insn, imm); + + /* patch the call place again */ + patch_text_nosync(ptr, &jal_insn, sizeof(u32)); +} + void riscv_alternative_fix_offsets(void *alt_ptr, unsigned int len, int patch_offset) { @@ -106,6 +121,18 @@ void riscv_alternative_fix_offsets(void *alt_ptr, unsigned int len, riscv_alternative_fix_auipc_jalr(alt_ptr + i * sizeof(u32), insn, insn2, patch_offset); } + + if (riscv_insn_is_jal(insn)) { + s32 imm = riscv_insn_extract_jtype_imm(insn); + + /* Don't modify jumps inside the alternative block */ + if ((alt_ptr + i * sizeof(u32) + imm) >= alt_ptr && + (alt_ptr + i * sizeof(u32) + imm) < (alt_ptr + len)) + continue; + + riscv_alternative_fix_jal(alt_ptr + i * sizeof(u32), + insn, patch_offset); + } } } |
