[PATCH v4 14/16] powerpc64: Add prefixed instructions to instruction data type
Jordan Niethe
jniethe5 at gmail.com
Fri Mar 20 16:18:07 AEDT 2020
For powerpc64, redefine the ppc_inst type so both word and prefixed
instructions can be represented. On powerpc32 the type will remain the
same. Update places which had assumed instructions to be 4 bytes long.
Signed-off-by: Jordan Niethe <jniethe5 at gmail.com>
---
v4: New to series
---
arch/powerpc/include/asm/code-patching.h | 10 +--
arch/powerpc/include/asm/inst.h | 90 ++++++++++++++++++++++++
arch/powerpc/include/asm/kprobes.h | 2 +-
arch/powerpc/include/asm/sstep.h | 4 ++
arch/powerpc/include/asm/uaccess.h | 22 ++++++
arch/powerpc/include/asm/uprobes.h | 2 +-
arch/powerpc/kernel/align.c | 5 +-
arch/powerpc/kernel/hw_breakpoint.c | 2 +-
arch/powerpc/kernel/kprobes.c | 7 +-
arch/powerpc/kernel/optprobes.c | 42 ++++++-----
arch/powerpc/kernel/optprobes_head.S | 3 +
arch/powerpc/kernel/trace/ftrace.c | 19 ++++-
arch/powerpc/kernel/uprobes.c | 2 +-
arch/powerpc/lib/code-patching.c | 22 ++++--
arch/powerpc/lib/sstep.c | 4 +-
arch/powerpc/xmon/xmon.c | 38 +++++++---
16 files changed, 221 insertions(+), 53 deletions(-)
diff --git a/arch/powerpc/include/asm/code-patching.h b/arch/powerpc/include/asm/code-patching.h
index 68bd9db334bd..bd41e1558707 100644
--- a/arch/powerpc/include/asm/code-patching.h
+++ b/arch/powerpc/include/asm/code-patching.h
@@ -25,11 +25,11 @@
bool is_offset_in_branch_range(long offset);
ppc_inst create_branch(const ppc_inst *addr,
unsigned long target, int flags);
-unsigned int create_cond_branch(const ppc_inst *addr,
+ppc_inst create_cond_branch(const void *addr,
unsigned long target, int flags);
-int patch_branch(ppc_inst *addr, unsigned long target, int flags);
-int patch_instruction(ppc_inst *addr, ppc_inst instr);
-int raw_patch_instruction(ppc_inst *addr, ppc_inst instr);
+int patch_branch(void *addr, unsigned long target, int flags);
+int patch_instruction(void *addr, ppc_inst instr);
+int raw_patch_instruction(void *addr, ppc_inst instr);
static inline unsigned long patch_site_addr(s32 *site)
{
@@ -60,7 +60,7 @@ static inline int modify_instruction_site(s32 *site, unsigned int clr, unsigned
int instr_is_relative_branch(ppc_inst instr);
int instr_is_relative_link_branch(ppc_inst instr);
int instr_is_branch_to_addr(const ppc_inst *instr, unsigned long addr);
-unsigned long branch_target(const ppc_inst *instr);
+unsigned long branch_target(const void *instr);
ppc_inst translate_branch(const ppc_inst *dest,
const ppc_inst *src);
extern bool is_conditional_branch(ppc_inst instr);
diff --git a/arch/powerpc/include/asm/inst.h b/arch/powerpc/include/asm/inst.h
index 7c8596ee411e..1a40b0a71128 100644
--- a/arch/powerpc/include/asm/inst.h
+++ b/arch/powerpc/include/asm/inst.h
@@ -6,6 +6,95 @@
* Instruction data type for POWER
*/
+#ifdef __powerpc64__
+
+typedef struct ppc_inst {
+ union {
+ struct {
+ u32 word;
+ u32 pad;
+ } __packed;
+ struct {
+ u32 prefix;
+ u32 suffix;
+ } __packed;
+ };
+} ppc_inst;
+
+#define PPC_INST(x) ((ppc_inst) { .word = (x), .pad = 0 })
+#define PPC_INST_PREFIXED(x, y) ((ppc_inst) { .prefix = (x), .suffix = (y) })
+
+static inline int ppc_inst_opcode(ppc_inst x)
+{
+ return x.word >> 26;
+}
+
+static inline bool ppc_inst_prefixed(ppc_inst x) {
+ return ppc_inst_opcode(x) == 1;
+}
+
+static inline int ppc_inst_len(ppc_inst x)
+{
+ if (ppc_inst_prefixed(x))
+ return 8;
+ else
+ return 4;
+}
+
+static inline u32 ppc_inst_word(ppc_inst x)
+{
+ return x.word;
+}
+
+static inline u32 ppc_inst_prefix(ppc_inst x)
+{
+ return x.prefix;
+}
+
+static inline u32 ppc_inst_suffix(ppc_inst x)
+{
+ return x.suffix;
+}
+
+
+static inline ppc_inst ppc_inst_read(const void *ptr)
+{
+ ppc_inst inst;
+ inst.word = *(u32 *)ptr;
+ if (ppc_inst_prefixed(inst))
+ inst.suffix = *((u32 *)ptr + 1);
+ else
+ inst.pad = 0;
+ return inst;
+}
+
+static inline void ppc_inst_write(ppc_inst *ptr, ppc_inst x)
+{
+ if (ppc_inst_prefixed(x)) {
+ *(u32 *)ptr = x.prefix;
+ *((u32 *)ptr + 1) = x.suffix;
+ } else {
+ *(u32 *)ptr = x.word;
+ }
+}
+
+static inline bool ppc_inst_equal(ppc_inst x, ppc_inst y)
+{
+ return !memcmp(&x, &y, sizeof(struct ppc_inst));
+}
+
+static inline bool ppc_inst_null(ppc_inst x)
+{
+ return x.word == 0 && x.pad == 0;
+}
+
+static inline u32 ppc_inst_mask(ppc_inst x, u32 mask)
+{
+ return ppc_inst_word(x) & mask;
+}
+
+#else /* !__powerpc64__ */
+
typedef u32 ppc_inst;
#define PPC_INST(x) (x)
@@ -50,4 +139,5 @@ static inline u32 ppc_inst_mask(ppc_inst x, u32 mask)
return ppc_inst_word(x) & mask;
}
+#endif /* __powerpc64__ */
#endif /* _ASM_INST_H */
diff --git a/arch/powerpc/include/asm/kprobes.h b/arch/powerpc/include/asm/kprobes.h
index 66b3f2983b22..4fc0e15e23a5 100644
--- a/arch/powerpc/include/asm/kprobes.h
+++ b/arch/powerpc/include/asm/kprobes.h
@@ -43,7 +43,7 @@ extern kprobe_opcode_t optprobe_template_ret[];
extern kprobe_opcode_t optprobe_template_end[];
/* Fixed instruction size for powerpc */
-#define MAX_INSN_SIZE 1
+#define MAX_INSN_SIZE 2
#define MAX_OPTIMIZED_LENGTH sizeof(kprobe_opcode_t) /* 4 bytes */
#define MAX_OPTINSN_SIZE (optprobe_template_end - optprobe_template_entry)
#define RELATIVEJUMP_SIZE sizeof(kprobe_opcode_t) /* 4 bytes */
diff --git a/arch/powerpc/include/asm/sstep.h b/arch/powerpc/include/asm/sstep.h
index ef5483288920..5eb825fb77cd 100644
--- a/arch/powerpc/include/asm/sstep.h
+++ b/arch/powerpc/include/asm/sstep.h
@@ -90,11 +90,15 @@ enum instruction_type {
#define VSX_LDLEFT 4 /* load VSX register from left */
#define VSX_CHECK_VEC 8 /* check MSR_VEC not MSR_VSX for reg >= 32 */
+/* Prefixed flag, ORed in with type */
+#define PREFIXED 0x800
+
/* Size field in type word */
#define SIZE(n) ((n) << 12)
#define GETSIZE(w) ((w) >> 12)
#define GETTYPE(t) ((t) & INSTR_TYPE_MASK)
+#define GETLENGTH(t) (((t) & PREFIXED) ? 8 : 4)
#define MKOP(t, f, s) ((t) | (f) | SIZE(s))
diff --git a/arch/powerpc/include/asm/uaccess.h b/arch/powerpc/include/asm/uaccess.h
index 2f500debae21..dee4fa1cd3ec 100644
--- a/arch/powerpc/include/asm/uaccess.h
+++ b/arch/powerpc/include/asm/uaccess.h
@@ -105,6 +105,28 @@ static inline int __access_ok(unsigned long addr, unsigned long size,
#define __put_user_inatomic(x, ptr) \
__put_user_nosleep((__typeof__(*(ptr)))(x), (ptr), sizeof(*(ptr)))
+/*
+ * When reading an instruction iff it is a prefix, the suffix needs to be also
+ * loaded.
+ */
+#define __get_user_instr(x, ptr) \
+({ \
+ long __gui_ret = 0; \
+ __gui_ret = __get_user(x.prefix, (unsigned int __user *)ptr); \
+ if (!__gui_ret && ppc_inst_prefixed(x)) \
+ __gui_ret = __get_user(x.suffix, (unsigned int __user *)ptr + 1); \
+ __gui_ret; \
+})
+
+#define __get_user_instr_inatomic(x, ptr) \
+({ \
+ long __gui_ret = 0; \
+ __gui_ret = __get_user_inatomic(x.prefix, (unsigned int __user *)ptr); \
+ if (!__gui_ret && ppc_inst_prefixed(x)) \
+ __gui_ret = __get_user_inatomic(x.suffix, (unsigned int __user *)ptr + 1); \
+ __gui_ret; \
+})
+
extern long __put_user_bad(void);
/*
diff --git a/arch/powerpc/include/asm/uprobes.h b/arch/powerpc/include/asm/uprobes.h
index fff3c5fc90b5..7896a7125fa9 100644
--- a/arch/powerpc/include/asm/uprobes.h
+++ b/arch/powerpc/include/asm/uprobes.h
@@ -14,7 +14,7 @@
typedef ppc_opcode_t uprobe_opcode_t;
-#define MAX_UINSN_BYTES 4
+#define MAX_UINSN_BYTES 8
#define UPROBE_XOL_SLOT_BYTES (MAX_UINSN_BYTES)
/* The following alias is needed for reference from arch-agnostic code */
diff --git a/arch/powerpc/kernel/align.c b/arch/powerpc/kernel/align.c
index b246ca124931..110eadd85c58 100644
--- a/arch/powerpc/kernel/align.c
+++ b/arch/powerpc/kernel/align.c
@@ -303,13 +303,14 @@ int fix_alignment(struct pt_regs *regs)
*/
CHECK_FULL_REGS(regs);
- if (unlikely(__get_user(instr, (unsigned int __user *)regs->nip)))
+ if (unlikely(__get_user_instr(instr, (void __user *)regs->nip)))
return -EFAULT;
if ((regs->msr & MSR_LE) != (MSR_KERNEL & MSR_LE)) {
/* We don't handle PPC little-endian any more... */
if (cpu_has_feature(CPU_FTR_PPC_LE))
return -EIO;
- instr = PPC_INST(swab32(ppc_inst_word(instr)));
+ instr = PPC_INST_PREFIXED(swab32(ppc_inst_word(instr)),
+ swab32(ppc_inst_suffix(instr)));
}
#ifdef CONFIG_SPE
diff --git a/arch/powerpc/kernel/hw_breakpoint.c b/arch/powerpc/kernel/hw_breakpoint.c
index f001de471b98..212e0abfda43 100644
--- a/arch/powerpc/kernel/hw_breakpoint.c
+++ b/arch/powerpc/kernel/hw_breakpoint.c
@@ -249,7 +249,7 @@ static bool stepping_handler(struct pt_regs *regs, struct perf_event *bp,
struct instruction_op op;
unsigned long addr = info->address;
- if (__get_user_inatomic(instr, (unsigned int *)regs->nip))
+ if (__get_user_instr_inatomic(instr, (void __user*)regs->nip))
goto fail;
ret = analyse_instr(&op, regs, instr);
diff --git a/arch/powerpc/kernel/kprobes.c b/arch/powerpc/kernel/kprobes.c
index f142d11d7b48..1a5370a3c7c8 100644
--- a/arch/powerpc/kernel/kprobes.c
+++ b/arch/powerpc/kernel/kprobes.c
@@ -153,7 +153,7 @@ NOKPROBE_SYMBOL(arch_arm_kprobe);
void arch_disarm_kprobe(struct kprobe *p)
{
- patch_instruction(p->addr, PPC_INST(p->opcode));
+ patch_instruction(p->addr, ppc_inst_read(p->ainsn.insn));
}
NOKPROBE_SYMBOL(arch_disarm_kprobe);
@@ -487,12 +487,13 @@ int kprobe_post_handler(struct pt_regs *regs)
{
struct kprobe *cur = kprobe_running();
struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+ int len = ppc_inst_len(ppc_inst_read(cur->ainsn.insn));
if (!cur || user_mode(regs))
return 0;
/* make sure we got here for instruction we have a kprobe on */
- if (((unsigned long)cur->ainsn.insn + 4) != regs->nip)
+ if ((unsigned long)cur->ainsn.insn + len != regs->nip)
return 0;
if ((kcb->kprobe_status != KPROBE_REENTER) && cur->post_handler) {
@@ -501,7 +502,7 @@ int kprobe_post_handler(struct pt_regs *regs)
}
/* Adjust nip to after the single-stepped instruction */
- regs->nip = (unsigned long)cur->addr + 4;
+ regs->nip = (unsigned long)cur->addr + len;
regs->msr |= kcb->kprobe_saved_msr;
/*Restore back the original saved kprobes variables and continue. */
diff --git a/arch/powerpc/kernel/optprobes.c b/arch/powerpc/kernel/optprobes.c
index 5b53c373373b..af6761859fba 100644
--- a/arch/powerpc/kernel/optprobes.c
+++ b/arch/powerpc/kernel/optprobes.c
@@ -158,38 +158,38 @@ void patch_imm32_load_insns(unsigned int val, kprobe_opcode_t *addr)
/*
* Generate instructions to load provided immediate 64-bit value
- * to register 'r3' and patch these instructions at 'addr'.
+ * to register 'reg' and patch these instructions at 'addr'.
*/
-void patch_imm64_load_insns(unsigned long val, kprobe_opcode_t *addr)
+void patch_imm64_load_insns(unsigned long val, int reg, kprobe_opcode_t *addr)
{
- /* lis r3,(op)@highest */
- patch_instruction(addr, PPC_INST(PPC_INST_ADDIS | ___PPC_RT(3) |
+ /* lis reg,(op)@highest */
+ patch_instruction(addr, PPC_INST(PPC_INST_ADDIS | ___PPC_RT(reg) |
((val >> 48) & 0xffff)));
addr++;
- /* ori r3,r3,(op)@higher */
- patch_instruction(addr, PPC_INST(PPC_INST_ORI | ___PPC_RA(3) |
- ___PPC_RS(3) | ((val >> 32) & 0xffff)));
+ /* ori reg,reg,(op)@higher */
+ patch_instruction(addr, PPC_INST(PPC_INST_ORI | ___PPC_RA(reg) |
+ ___PPC_RS(reg) | ((val >> 32) & 0xffff)));
addr++;
- /* rldicr r3,r3,32,31 */
- patch_instruction(addr, PPC_INST(PPC_INST_RLDICR | ___PPC_RA(3) |
- ___PPC_RS(3) | __PPC_SH64(32) | __PPC_ME64(31)));
+ /* rldicr reg,reg,32,31 */
+ patch_instruction(addr, PPC_INST(PPC_INST_RLDICR | ___PPC_RA(reg) |
+ ___PPC_RS(reg) | __PPC_SH64(32) | __PPC_ME64(31)));
addr++;
- /* oris r3,r3,(op)@h */
- patch_instruction(addr, PPC_INST(PPC_INST_ORIS | ___PPC_RA(3) |
- ___PPC_RS(3) | ((val >> 16) & 0xffff)));
+ /* oris reg,reg,(op)@h */
+ patch_instruction(addr, PPC_INST(PPC_INST_ORIS | ___PPC_RA(reg) |
+ ___PPC_RS(reg) | ((val >> 16) & 0xffff)));
addr++;
- /* ori r3,r3,(op)@l */
- patch_instruction(addr, PPC_INST(PPC_INST_ORI | ___PPC_RA(3) |
- ___PPC_RS(3) | (val & 0xffff)));
+ /* ori reg,reg,(op)@l */
+ patch_instruction(addr, PPC_INST(PPC_INST_ORI | ___PPC_RA(reg) |
+ ___PPC_RS(reg) | (val & 0xffff)));
}
int arch_prepare_optimized_kprobe(struct optimized_kprobe *op, struct kprobe *p)
{
- ppc_inst branch_op_callback, branch_emulate_step;
+ ppc_inst branch_op_callback, branch_emulate_step, temp;
kprobe_opcode_t *op_callback_addr, *emulate_step_addr, *buff;
long b_offset;
unsigned long nip, size;
@@ -239,7 +239,7 @@ int arch_prepare_optimized_kprobe(struct optimized_kprobe *op, struct kprobe *p)
* Fixup the template with instructions to:
* 1. load the address of the actual probepoint
*/
- patch_imm64_load_insns((unsigned long)op, buff + TMPL_OP_IDX);
+ patch_imm64_load_insns((unsigned long)op, 3, buff + TMPL_OP_IDX);
/*
* 2. branch to optimized_callback() and emulate_step()
@@ -268,7 +268,11 @@ int arch_prepare_optimized_kprobe(struct optimized_kprobe *op, struct kprobe *p)
/*
* 3. load instruction to be emulated into relevant register, and
*/
- patch_imm32_load_insns(*(unsigned int *)p->ainsn.insn, buff + TMPL_INSN_IDX);
+ temp = ppc_inst_read(p->ainsn.insn);
+ patch_imm64_load_insns(ppc_inst_word(temp) |
+ ((u64)ppc_inst_suffix(temp) << 32),
+ 4,
+ buff + TMPL_INSN_IDX);
/*
* 4. branch back from trampoline
diff --git a/arch/powerpc/kernel/optprobes_head.S b/arch/powerpc/kernel/optprobes_head.S
index cf383520843f..ff8ba4d3824d 100644
--- a/arch/powerpc/kernel/optprobes_head.S
+++ b/arch/powerpc/kernel/optprobes_head.S
@@ -94,6 +94,9 @@ optprobe_template_insn:
/* 2, Pass instruction to be emulated in r4 */
nop
nop
+ nop
+ nop
+ nop
.global optprobe_template_call_emulate
optprobe_template_call_emulate:
diff --git a/arch/powerpc/kernel/trace/ftrace.c b/arch/powerpc/kernel/trace/ftrace.c
index ad451205f268..3b8655f57b4a 100644
--- a/arch/powerpc/kernel/trace/ftrace.c
+++ b/arch/powerpc/kernel/trace/ftrace.c
@@ -42,9 +42,24 @@
static unsigned long ftrace_tramps[NUM_FTRACE_TRAMPS];
static long
-read_inst(ppc_inst *inst, const void *src)
+read_inst(ppc_inst *p, const void *src)
{
- return probe_kernel_read((void *)inst, src, MCOUNT_INSN_SIZE);
+ ppc_inst inst;
+ long err;
+
+ err = probe_kernel_read((void *)&inst.prefix,
+ src, MCOUNT_INSN_SIZE);
+ if (err)
+ return err;
+
+ if (ppc_inst_prefixed(inst))
+ err = probe_kernel_read((void *)&inst.suffix,
+ src + 4, MCOUNT_INSN_SIZE);
+ if (err)
+ return err;
+
+ ppc_inst_write(p, inst);
+ return 0;
}
static ppc_inst
diff --git a/arch/powerpc/kernel/uprobes.c b/arch/powerpc/kernel/uprobes.c
index d1dff1dc3a11..3e4fbb5c1b1e 100644
--- a/arch/powerpc/kernel/uprobes.c
+++ b/arch/powerpc/kernel/uprobes.c
@@ -111,7 +111,7 @@ int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
* support doesn't exist and have to fix-up the next instruction
* to be executed.
*/
- regs->nip = utask->vaddr + MAX_UINSN_BYTES;
+ regs->nip = utask->vaddr + ppc_inst_len(ppc_inst_read(auprobe->insn));
user_disable_single_step(current);
return 0;
diff --git a/arch/powerpc/lib/code-patching.c b/arch/powerpc/lib/code-patching.c
index fa7f32adf029..3b8277a64b8f 100644
--- a/arch/powerpc/lib/code-patching.c
+++ b/arch/powerpc/lib/code-patching.c
@@ -24,17 +24,27 @@ static int __patch_instruction(ppc_inst *exec_addr, ppc_inst instr,
{
int err = 0;
- __put_user_asm(instr, patch_addr, err, "stw");
+ __put_user_asm(ppc_inst_word(instr), patch_addr, err, "stw");
if (err)
return err;
asm ("dcbst 0, %0; sync; icbi 0,%1; sync; isync" :: "r" (patch_addr),
"r" (exec_addr));
+ if (!ppc_inst_prefixed(instr))
+ return 0;
+
+ __put_user_asm(ppc_inst_suffix(instr), patch_addr + 4, err, "stw");
+ if (err)
+ return err;
+
+ asm ("dcbst 0, %0; sync; icbi 0,%1; sync; isync" :: "r" (patch_addr + 4),
+ "r" (exec_addr + 4));
+
return 0;
}
-int raw_patch_instruction(ppc_inst *addr, ppc_inst instr)
+int raw_patch_instruction(void *addr, ppc_inst instr)
{
return __patch_instruction(addr, instr, addr);
}
@@ -184,7 +194,7 @@ static int do_patch_instruction(ppc_inst *addr, ppc_inst instr)
#endif /* CONFIG_STRICT_KERNEL_RWX */
-int patch_instruction(unsigned int *addr, unsigned int instr)
+int patch_instruction(void *addr, ppc_inst instr)
{
/* Make sure we aren't patching a freed init section */
if (init_mem_is_free && init_section_contains(addr, 4)) {
@@ -195,7 +205,7 @@ int patch_instruction(unsigned int *addr, unsigned int instr)
}
NOKPROBE_SYMBOL(patch_instruction);
-int patch_branch(ppc_inst *addr, unsigned long target, int flags)
+int patch_branch(void *addr, unsigned long target, int flags)
{
return patch_instruction(addr, create_branch(addr, target, flags));
}
@@ -264,7 +274,7 @@ ppc_inst create_branch(const ppc_inst *addr,
return instruction;
}
-unsigned int create_cond_branch(const unsigned int *addr,
+ppc_inst create_cond_branch(const void *addr,
unsigned long target, int flags)
{
ppc_inst instruction;
@@ -344,7 +354,7 @@ static unsigned long branch_bform_target(const ppc_inst *instr)
return (unsigned long)imm;
}
-unsigned long branch_target(const ppc_inst *instr)
+unsigned long branch_target(const void *instr)
{
if (instr_is_branch_iform(ppc_inst_read(instr)))
return branch_iform_target(instr);
diff --git a/arch/powerpc/lib/sstep.c b/arch/powerpc/lib/sstep.c
index bae878a83fa5..ab4c71c43c8c 100644
--- a/arch/powerpc/lib/sstep.c
+++ b/arch/powerpc/lib/sstep.c
@@ -1169,10 +1169,12 @@ int analyse_instr(struct instruction_op *op, const struct pt_regs *regs,
unsigned long int imm;
unsigned long int val, val2;
unsigned int mb, me, sh;
- unsigned int word;
+ unsigned int word, suffix;
long ival;
word = ppc_inst_word(instr);
+ suffix = ppc_inst_suffix(instr);
+
op->type = COMPUTE;
opcode = word >> 26;
diff --git a/arch/powerpc/xmon/xmon.c b/arch/powerpc/xmon/xmon.c
index ee084411f2f5..c5536e1a3356 100644
--- a/arch/powerpc/xmon/xmon.c
+++ b/arch/powerpc/xmon/xmon.c
@@ -110,7 +110,7 @@ struct bpt {
#define BP_DABR 4
#define NBPTS 256
-#define BPT_WORDS 2
+#define BPT_WORDS 4
static struct bpt bpts[NBPTS];
static struct bpt dabr;
static struct bpt *iabr;
@@ -118,12 +118,13 @@ static unsigned bpinstr = 0x7fe00008; /* trap */
#define BP_NUM(bp) ((bp) - bpts + 1)
-static unsigned int __section(.text.xmon_bpts) bpt_table[NBPTS * BPT_WORDS];
+static unsigned int __section(.text.xmon_bpts) bpt_table[NBPTS * BPT_WORDS] __aligned(64);
/* Prototypes */
static int cmds(struct pt_regs *);
static int mread(unsigned long, void *, int);
static int mwrite(unsigned long, void *, int);
+static int mread_instr(unsigned long, ppc_inst *);
static int handle_fault(struct pt_regs *);
static void byterev(unsigned char *, int);
static void memex(void);
@@ -759,8 +760,8 @@ static int xmon_bpt(struct pt_regs *regs)
/* Are we at the trap at bp->instr[1] for some bp? */
bp = in_breakpoint_table(regs->nip, &offset);
- if (bp != NULL && offset == 4) {
- regs->nip = bp->address + 4;
+ if (bp != NULL && (offset == 4 || offset == 8)) {
+ regs->nip = bp->address + offset;
atomic_dec(&bp->ref_count);
return 1;
}
@@ -862,7 +863,7 @@ static struct bpt *in_breakpoint_table(unsigned long nip, unsigned long *offp)
if (off >= sizeof(bpt_table))
return NULL;
bp_off = off % (sizeof(unsigned int) * BPT_WORDS);
- if (bp_off != 0 && bp_off != 4)
+ if (bp_off != 0 && bp_off != 4 && bp_off != 8)
return NULL;
*offp = bp_off;
return bpts + ((off - bp_off) / (sizeof(unsigned int) * BPT_WORDS));
@@ -881,7 +882,6 @@ static struct bpt *new_breakpoint(unsigned long a)
if (!bp->enabled && atomic_read(&bp->ref_count) == 0) {
bp->address = a;
bp->instr = bpt_table + ((bp - bpts) * BPT_WORDS);
- patch_instruction(bp->instr + 1, PPC_INST(bpinstr));
return bp;
}
}
@@ -900,7 +900,7 @@ static void insert_bpts(void)
for (i = 0; i < NBPTS; ++i, ++bp) {
if ((bp->enabled & (BP_TRAP|BP_CIABR)) == 0)
continue;
- if (mread(bp->address, &instr, 4) != 4) {
+ if (!mread_instr(bp->address, &instr)) {
printf("Couldn't read instruction at %lx, "
"disabling breakpoint there\n", bp->address);
bp->enabled = 0;
@@ -913,9 +913,10 @@ static void insert_bpts(void)
continue;
}
patch_instruction(bp->instr, instr);
+ patch_instruction(bp->instr + ppc_inst_len(instr), PPC_INST(bpinstr));
if (bp->enabled & BP_CIABR)
continue;
- if (patch_instruction((ppc_inst *)bp->address,
+ if (patch_instruction((void *)bp->address,
PPC_INST(bpinstr)) != 0) {
printf("Couldn't write instruction at %lx, "
"disabling breakpoint there\n", bp->address);
@@ -950,7 +951,7 @@ static void remove_bpts(void)
for (i = 0; i < NBPTS; ++i, ++bp) {
if ((bp->enabled & (BP_TRAP|BP_CIABR)) != BP_TRAP)
continue;
- if (mread(bp->address, &instr, 4) == 4
+ if (mread_instr(bp->address, &instr)
&& ppc_inst_equal(instr, PPC_INST(bpinstr))
&& patch_instruction(
(ppc_inst *)bp->address, ppc_inst_read(bp->instr)) != 0)
@@ -1166,7 +1167,7 @@ static int do_step(struct pt_regs *regs)
force_enable_xmon();
/* check we are in 64-bit kernel mode, translation enabled */
if ((regs->msr & (MSR_64BIT|MSR_PR|MSR_IR)) == (MSR_64BIT|MSR_IR)) {
- if (mread(regs->nip, &instr, 4) == 4) {
+ if (mread_instr(regs->nip, &instr)) {
stepped = emulate_step(regs, instr);
if (stepped < 0) {
printf("Couldn't single-step %s instruction\n",
@@ -1333,7 +1334,7 @@ static long check_bp_loc(unsigned long addr)
printf("Breakpoints may only be placed at kernel addresses\n");
return 0;
}
- if (!mread(addr, &instr, sizeof(instr))) {
+ if (!mread_instr(addr, &instr)) {
printf("Can't read instruction at address %lx\n", addr);
return 0;
}
@@ -2126,6 +2127,21 @@ mwrite(unsigned long adrs, void *buf, int size)
return n;
}
+static int
+mread_instr(unsigned long adrs, ppc_inst *instr)
+{
+ if (setjmp(bus_error_jmp) == 0) {
+ catch_memory_errors = 1;
+ sync();
+ *instr = ppc_inst_read((void *)adrs);
+ sync();
+ /* wait a little while to see if we get a machine check */
+ __delay(200);
+ }
+ catch_memory_errors = 0;
+ return ppc_inst_len(*instr);
+}
+
static int fault_type;
static int fault_except;
static char *fault_chars[] = { "--", "**", "##" };
--
2.17.1
More information about the Linuxppc-dev
mailing list