[RFC 13/15] riscv: probes: sort out endian-ness
Ben Dooks
ben.dooks at codethink.co.uk
Fri Dec 20 07:57:59 PST 2024
Updated {k,u}probe code to deal with big endian mode where
the instruction stream is always in little endian.
Signed-off-by: Ben Dooks <ben.dooks at codethink.co.uk>
---
arch/riscv/kernel/probes/decode-insn.c | 2 +-
arch/riscv/kernel/probes/decode-insn.h | 5 +++++
arch/riscv/kernel/probes/kprobes.c | 30 ++++++++++++++++----------
arch/riscv/kernel/probes/uprobes.c | 10 ++++-----
4 files changed, 30 insertions(+), 17 deletions(-)
diff --git a/arch/riscv/kernel/probes/decode-insn.c b/arch/riscv/kernel/probes/decode-insn.c
index 65d9590bfb9f..5f30c10f7d8d 100644
--- a/arch/riscv/kernel/probes/decode-insn.c
+++ b/arch/riscv/kernel/probes/decode-insn.c
@@ -16,7 +16,7 @@
enum probe_insn __kprobes
riscv_probe_decode_insn(probe_opcode_t *addr, struct arch_probe_insn *api)
{
- probe_opcode_t insn = *addr;
+ probe_opcode_t insn = le32_to_cpu(*addr);
/*
* Reject instructions list:
diff --git a/arch/riscv/kernel/probes/decode-insn.h b/arch/riscv/kernel/probes/decode-insn.h
index 42269a7d676d..0515deb204b5 100644
--- a/arch/riscv/kernel/probes/decode-insn.h
+++ b/arch/riscv/kernel/probes/decode-insn.h
@@ -15,4 +15,9 @@ enum probe_insn {
enum probe_insn __kprobes
riscv_probe_decode_insn(probe_opcode_t *addr, struct arch_probe_insn *asi);
+static inline int read_insn_length(void *ptr)
+{
+ return GET_INSN_LENGTH(le16_to_cpu(*(__le16 *)ptr));
+}
+
#endif /* _RISCV_KERNEL_KPROBES_DECODE_INSN_H */
diff --git a/arch/riscv/kernel/probes/kprobes.c b/arch/riscv/kernel/probes/kprobes.c
index 474a65213657..aba684938284 100644
--- a/arch/riscv/kernel/probes/kprobes.c
+++ b/arch/riscv/kernel/probes/kprobes.c
@@ -24,13 +24,13 @@ post_kprobe_handler(struct kprobe *, struct kprobe_ctlblk *, struct pt_regs *);
static void __kprobes arch_prepare_ss_slot(struct kprobe *p)
{
- size_t len = GET_INSN_LENGTH(p->opcode);
- u32 insn = __BUG_INSN_32;
+ size_t len = read_insn_length(&p->opcode);
+ u32 insn = cpu_to_le32(__BUG_INSN_32);
p->ainsn.api.restore = (unsigned long)p->addr + len;
patch_text_nosync(p->ainsn.api.insn, &p->opcode, len);
- patch_text_nosync(p->ainsn.api.insn + len, &insn, GET_INSN_LENGTH(insn));
+ patch_text_nosync((void *)p->ainsn.api.insn + len, &insn, GET_INSN_LENGTH(__BUG_INSN_32));
}
static void __kprobes arch_prepare_simulate(struct kprobe *p)
@@ -58,7 +58,7 @@ static bool __kprobes arch_check_kprobe(struct kprobe *p)
if (tmp == addr)
return true;
- tmp += GET_INSN_LENGTH(*(u16 *)tmp);
+ tmp += read_insn_length((u16 *)tmp);
}
return false;
@@ -75,9 +75,9 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
return -EILSEQ;
/* copy instruction */
- p->opcode = (kprobe_opcode_t)(*insn++);
- if (GET_INSN_LENGTH(p->opcode) == 4)
- p->opcode |= (kprobe_opcode_t)(*insn) << 16;
+ *((u16 *)&p->opcode) = (*insn++);
+ if (read_insn_length(&p->opcode) == 4)
+ p->opcode = (kprobe_opcode_t)(*(u32 *)p->addr);
/* decode instruction */
switch (riscv_probe_decode_insn(p->addr, &p->ainsn.api)) {
@@ -107,16 +107,24 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
/* install breakpoint in text */
void __kprobes arch_arm_kprobe(struct kprobe *p)
{
- size_t len = GET_INSN_LENGTH(p->opcode);
- u32 insn = len == 4 ? __BUG_INSN_32 : __BUG_INSN_16;
+ size_t len = read_insn_length(&p->opcode);
+ u32 insn;
+
+ if (len == 4)
+ insn = cpu_to_le32(__BUG_INSN_32);
+ else {
+ insn = cpu_to_le16(__BUG_INSN_16);
+ insn |= cpu_to_le16(__BUG_INSN_16) << 16;
+ }
+ pr_info("%s: patching %px (%d bytes)\n", __func__, p->addr, (int)len);
patch_text(p->addr, &insn, len);
}
/* remove breakpoint from text */
void __kprobes arch_disarm_kprobe(struct kprobe *p)
{
- size_t len = GET_INSN_LENGTH(p->opcode);
+ size_t len = read_insn_length(&p->opcode);
patch_text(p->addr, &p->opcode, len);
}
@@ -336,7 +344,7 @@ kprobe_single_step_handler(struct pt_regs *regs)
struct kprobe *cur = kprobe_running();
if (cur && (kcb->kprobe_status & (KPROBE_HIT_SS | KPROBE_REENTER)) &&
- ((unsigned long)&cur->ainsn.api.insn[0] + GET_INSN_LENGTH(cur->opcode) == addr)) {
+ ((unsigned long)&cur->ainsn.api.insn[0] + read_insn_length(&cur->opcode) == addr)) {
kprobes_restore_local_irqflag(kcb, regs);
post_kprobe_handler(cur, kcb, regs);
return true;
diff --git a/arch/riscv/kernel/probes/uprobes.c b/arch/riscv/kernel/probes/uprobes.c
index 4b3dc8beaf77..e31c1dd337d5 100644
--- a/arch/riscv/kernel/probes/uprobes.c
+++ b/arch/riscv/kernel/probes/uprobes.c
@@ -12,9 +12,9 @@
bool is_swbp_insn(uprobe_opcode_t *insn)
{
#ifdef CONFIG_RISCV_ISA_C
- return (*insn & 0xffff) == UPROBE_SWBP_INSN;
+ return (*(u16 *)insn) == cpu_to_le16(UPROBE_SWBP_INSN);
#else
- return *insn == UPROBE_SWBP_INSN;
+ return *insn == cpu_to_le32(UPROBE_SWBP_INSN);
#endif
}
@@ -35,7 +35,7 @@ int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm,
opcode = *(probe_opcode_t *)(&auprobe->insn[0]);
- auprobe->insn_size = GET_INSN_LENGTH(opcode);
+ auprobe->insn_size = GET_INSN_LENGTH(le32_to_cpu(opcode));
switch (riscv_probe_decode_insn(&opcode, &auprobe->api)) {
case INSN_REJECTED:
@@ -172,8 +172,8 @@ void arch_uprobe_copy_ixol(struct page *page, unsigned long vaddr,
/* Add ebreak behind opcode to simulate singlestep */
if (vaddr) {
- dst += GET_INSN_LENGTH(*(probe_opcode_t *)src);
- *(uprobe_opcode_t *)dst = __BUG_INSN_32;
+ dst += read_insn_length(src);
+ *(uprobe_opcode_t *)dst = cpu_to_le32(__BUG_INSN_32);
}
kunmap_atomic(kaddr);
--
2.37.2.352.g3c44437643
More information about the linux-riscv
mailing list