RISC-V: fix auipc-jalr addresses in patched alternatives
Alternatives live in a different section, so addresses used by call functions will point to wrong locations after the patch got applied. Similar to arm64, adjust the location to consider that offset. Reviewed-by: Conor Dooley <conor.dooley@microchip.com> Reviewed-by: Andrew Jones <ajones@ventanamicro.com> Reviewed-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com> Tested-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com> Signed-off-by: Heiko Stuebner <heiko.stuebner@vrull.eu> Link: https://lore.kernel.org/r/20221223221332.4127602-13-heiko@sntech.de Signed-off-by: Palmer Dabbelt <palmer@rivosinc.com>
This commit is contained in:
parent
47f05757d3
commit
27c653c065
3 changed files with 63 additions and 1 deletions
|
@ -27,6 +27,9 @@ void __init apply_boot_alternatives(void);
|
|||
void __init apply_early_boot_alternatives(void);
|
||||
void apply_module_alternatives(void *start, size_t length);
|
||||
|
||||
void riscv_alternative_fix_offsets(void *alt_ptr, unsigned int len,
|
||||
int patch_offset);
|
||||
|
||||
struct alt_entry {
|
||||
void *old_ptr; /* address of original instruciton or data */
|
||||
void *alt_ptr; /* address of replacement instruction or data */
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
#include <asm/vendorid_list.h>
|
||||
#include <asm/sbi.h>
|
||||
#include <asm/csr.h>
|
||||
#include <asm/insn.h>
|
||||
#include <asm/patch.h>
|
||||
|
||||
struct cpu_manufacturer_info_t {
|
||||
unsigned long vendor_id;
|
||||
|
@ -53,6 +55,60 @@ static void __init_or_module riscv_fill_cpu_mfr_info(struct cpu_manufacturer_inf
|
|||
}
|
||||
}
|
||||
|
||||
static u32 riscv_instruction_at(void *p)
|
||||
{
|
||||
u16 *parcel = p;
|
||||
|
||||
return (u32)parcel[0] | (u32)parcel[1] << 16;
|
||||
}
|
||||
|
||||
static void riscv_alternative_fix_auipc_jalr(void *ptr, u32 auipc_insn,
|
||||
u32 jalr_insn, int patch_offset)
|
||||
{
|
||||
u32 call[2] = { auipc_insn, jalr_insn };
|
||||
s32 imm;
|
||||
|
||||
/* get and adjust new target address */
|
||||
imm = riscv_insn_extract_utype_itype_imm(auipc_insn, jalr_insn);
|
||||
imm -= patch_offset;
|
||||
|
||||
/* update instructions */
|
||||
riscv_insn_insert_utype_itype_imm(&call[0], &call[1], imm);
|
||||
|
||||
/* patch the call place again */
|
||||
patch_text_nosync(ptr, call, sizeof(u32) * 2);
|
||||
}
|
||||
|
||||
void riscv_alternative_fix_offsets(void *alt_ptr, unsigned int len,
|
||||
int patch_offset)
|
||||
{
|
||||
int num_insn = len / sizeof(u32);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num_insn; i++) {
|
||||
u32 insn = riscv_instruction_at(alt_ptr + i * sizeof(u32));
|
||||
|
||||
/*
|
||||
* May be the start of an auipc + jalr pair
|
||||
* Needs to check that at least one more instruction
|
||||
* is in the list.
|
||||
*/
|
||||
if (riscv_insn_is_auipc(insn) && i < num_insn - 1) {
|
||||
u32 insn2 = riscv_instruction_at(alt_ptr + (i + 1) * sizeof(u32));
|
||||
|
||||
if (!riscv_insn_is_jalr(insn2))
|
||||
continue;
|
||||
|
||||
/* if instruction pair is a call, it will use the ra register */
|
||||
if (RV_EXTRACT_RD_REG(insn) != 1)
|
||||
continue;
|
||||
|
||||
riscv_alternative_fix_auipc_jalr(alt_ptr + i * sizeof(u32),
|
||||
insn, insn2, patch_offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This is called very early in the boot process (directly after we run
|
||||
* a feature detect on the boot CPU). No need to worry about other CPUs
|
||||
|
|
|
@ -339,8 +339,11 @@ void __init_or_module riscv_cpufeature_patch_func(struct alt_entry *begin,
|
|||
}
|
||||
|
||||
tmp = (1U << alt->errata_id);
|
||||
if (cpu_req_feature & tmp)
|
||||
if (cpu_req_feature & tmp) {
|
||||
patch_text_nosync(alt->old_ptr, alt->alt_ptr, alt->alt_len);
|
||||
riscv_alternative_fix_offsets(alt->old_ptr, alt->alt_len,
|
||||
alt->old_ptr - alt->alt_ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
Loading…
Add table
Reference in a new issue