From: Jordan Niethe <jniethe5@gmail.com>
To: linuxppc-dev@lists.ozlabs.org
Cc: alistair@popple.id.au, Jordan Niethe <jniethe5@gmail.com>
Subject: [PATCH 14/18] powerpc/kprobes: Support kprobes on prefixed instructions
Date: Tue, 26 Nov 2019 16:21:37 +1100 [thread overview]
Message-ID: <20191126052141.28009-15-jniethe5@gmail.com> (raw)
In-Reply-To: <20191126052141.28009-1-jniethe5@gmail.com>
A prefixed instruction is composed of a word prefix followed by a word
suffix. It does not make sense to be able to have a kprobe on the suffix
of a prefixed instruction, so make this impossible.
Kprobes work by replacing an instruction with a trap and saving that
instruction to be single stepped out of place later. Currently there is
not enough space allocated to keep a prefixed instruction for single
stepping. Increase the amount of space allocated for holding the
instruction copy.
kprobe_post_handler() expects all instructions to be 4 bytes long which
means that it does not function correctly for prefixed instructions.
Add checks for prefixed instructions which will use a length of 8 bytes
instead.
For optprobes we normally patch in loading the instruction we put a
probe on into r4 before calling emulate_step(). We now make space and
patch in loading the suffix into r5 as well.
Signed-off-by: Jordan Niethe <jniethe5@gmail.com>
---
arch/powerpc/include/asm/kprobes.h | 5 +--
arch/powerpc/kernel/kprobes.c | 46 +++++++++++++++++++++-------
arch/powerpc/kernel/optprobes.c | 31 +++++++++++--------
arch/powerpc/kernel/optprobes_head.S | 6 ++++
4 files changed, 62 insertions(+), 26 deletions(-)
diff --git a/arch/powerpc/include/asm/kprobes.h b/arch/powerpc/include/asm/kprobes.h
index 66b3f2983b22..1f03a1cacb1e 100644
--- a/arch/powerpc/include/asm/kprobes.h
+++ b/arch/powerpc/include/asm/kprobes.h
@@ -38,12 +38,13 @@ extern kprobe_opcode_t optprobe_template_entry[];
extern kprobe_opcode_t optprobe_template_op_address[];
extern kprobe_opcode_t optprobe_template_call_handler[];
extern kprobe_opcode_t optprobe_template_insn[];
+extern kprobe_opcode_t optprobe_template_sufx[];
extern kprobe_opcode_t optprobe_template_call_emulate[];
extern kprobe_opcode_t optprobe_template_ret[];
extern kprobe_opcode_t optprobe_template_end[];
-/* Fixed instruction size for powerpc */
-#define MAX_INSN_SIZE 1
+/* Prefixed instructions are two words */
+#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/kernel/kprobes.c b/arch/powerpc/kernel/kprobes.c
index 7303fe3856cc..aa15b3480385 100644
--- a/arch/powerpc/kernel/kprobes.c
+++ b/arch/powerpc/kernel/kprobes.c
@@ -104,17 +104,30 @@ kprobe_opcode_t *kprobe_lookup_name(const char *name, unsigned int offset)
int arch_prepare_kprobe(struct kprobe *p)
{
+ int len;
int ret = 0;
+ struct kprobe *prev;
kprobe_opcode_t insn = *p->addr;
+ kprobe_opcode_t prfx = *(p->addr - 1);
+ preempt_disable();
if ((unsigned long)p->addr & 0x03) {
printk("Attempt to register kprobe at an unaligned address\n");
ret = -EINVAL;
} else if (IS_MTMSRD(insn) || IS_RFID(insn) || IS_RFI(insn)) {
printk("Cannot register a kprobe on rfi/rfid or mtmsr[d]\n");
ret = -EINVAL;
+ } else if (IS_PREFIX(prfx)) {
+ printk("Cannot register a kprobe on the second word of prefixed instruction\n");
+ ret = -EINVAL;
+ }
+ prev = get_kprobe(p->addr - 1);
+ if (prev && IS_PREFIX(*prev->ainsn.insn)) {
+ printk("Cannot register a kprobe on the second word of prefixed instruction\n");
+ ret = -EINVAL;
}
+
/* insn must be on a special executable page on ppc64. This is
* not explicitly required on ppc32 (right now), but it doesn't hurt */
if (!ret) {
@@ -124,14 +137,18 @@ int arch_prepare_kprobe(struct kprobe *p)
}
if (!ret) {
- memcpy(p->ainsn.insn, p->addr,
- MAX_INSN_SIZE * sizeof(kprobe_opcode_t));
+ if (IS_PREFIX(insn))
+ len = MAX_INSN_SIZE * sizeof(kprobe_opcode_t);
+ else
+ len = sizeof(kprobe_opcode_t);
+ memcpy(p->ainsn.insn, p->addr, len);
p->opcode = *p->addr;
flush_icache_range((unsigned long)p->ainsn.insn,
(unsigned long)p->ainsn.insn + sizeof(kprobe_opcode_t));
}
p->ainsn.boostable = 0;
+ preempt_enable_no_resched();
return ret;
}
NOKPROBE_SYMBOL(arch_prepare_kprobe);
@@ -216,10 +233,11 @@ NOKPROBE_SYMBOL(arch_prepare_kretprobe);
static int try_to_emulate(struct kprobe *p, struct pt_regs *regs)
{
int ret;
- unsigned int insn = *p->ainsn.insn;
+ unsigned int insn = p->ainsn.insn[0];
+ unsigned int sufx = p->ainsn.insn[1];
/* regs->nip is also adjusted if emulate_step returns 1 */
- ret = emulate_step(regs, insn, 0);
+ ret = emulate_step(regs, insn, sufx);
if (ret > 0) {
/*
* Once this instruction has been boosted
@@ -233,7 +251,10 @@ static int try_to_emulate(struct kprobe *p, struct pt_regs *regs)
* So, we should never get here... but, its still
* good to catch them, just in case...
*/
- printk("Can't step on instruction %x\n", insn);
+ if (!IS_PREFIX(insn))
+ printk("Can't step on instruction %x\n", insn);
+ else
+ printk("Can't step on instruction %x %x\n", insn, sufx);
BUG();
} else {
/*
@@ -275,7 +296,7 @@ int kprobe_handler(struct pt_regs *regs)
if (kprobe_running()) {
p = get_kprobe(addr);
if (p) {
- kprobe_opcode_t insn = *p->ainsn.insn;
+ kprobe_opcode_t insn = p->ainsn.insn[0];
if (kcb->kprobe_status == KPROBE_HIT_SS &&
is_trap(insn)) {
/* Turn off 'trace' bits */
@@ -448,9 +469,10 @@ static int trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs)
* the link register properly so that the subsequent 'blr' in
* kretprobe_trampoline jumps back to the right instruction.
*
- * For nip, we should set the address to the previous instruction since
- * we end up emulating it in kprobe_handler(), which increments the nip
- * again.
+ * To keep the nip at the correct address we need to counter the
+ * increment that happens when we emulate the kretprobe_trampoline noop
+ * in kprobe_handler(). We do this by decrementing the address by the
+ * length of the noop which is always 4 bytes.
*/
regs->nip = orig_ret_address - 4;
regs->link = orig_ret_address;
@@ -478,12 +500,14 @@ int kprobe_post_handler(struct pt_regs *regs)
{
struct kprobe *cur = kprobe_running();
struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+ kprobe_opcode_t insn;
if (!cur || user_mode(regs))
return 0;
+ insn = *cur->ainsn.insn;
/* 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 + (IS_PREFIX(insn) ? 8 : 4)) != regs->nip)
return 0;
if ((kcb->kprobe_status != KPROBE_REENTER) && cur->post_handler) {
@@ -492,7 +516,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 + (IS_PREFIX(insn) ? 8 : 4);
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 82dc8a589c87..b2aef27bac27 100644
--- a/arch/powerpc/kernel/optprobes.c
+++ b/arch/powerpc/kernel/optprobes.c
@@ -27,6 +27,8 @@
(optprobe_template_op_address - optprobe_template_entry)
#define TMPL_INSN_IDX \
(optprobe_template_insn - optprobe_template_entry)
+#define TMPL_SUFX_IDX \
+ (optprobe_template_sufx - optprobe_template_entry)
#define TMPL_END_IDX \
(optprobe_template_end - optprobe_template_entry)
@@ -100,7 +102,8 @@ static unsigned long can_optimize(struct kprobe *p)
* and that can be emulated.
*/
if (!is_conditional_branch(*p->ainsn.insn) &&
- analyse_instr(&op, ®s, *p->ainsn.insn, 0) == 1) {
+ analyse_instr(&op, ®s, p->ainsn.insn[0],
+ p->ainsn.insn[1]) == 1) {
emulate_update_regs(®s, &op);
nip = regs.nip;
}
@@ -140,27 +143,27 @@ void arch_remove_optimized_kprobe(struct optimized_kprobe *op)
}
/*
- * emulate_step() requires insn to be emulated as
- * second parameter. Load register 'r4' with the
- * instruction.
+ * emulate_step() requires insn to be emulated as second parameter, and the
+ * suffix as the third parameter. Load these into registers.
*/
-void patch_imm32_load_insns(unsigned int val, kprobe_opcode_t *addr)
+static void patch_imm32_load_insns(int reg, unsigned int val,
+ kprobe_opcode_t *addr)
{
- /* addis r4,0,(insn)@h */
- patch_instruction(addr, PPC_INST_ADDIS | ___PPC_RT(4) |
+ /* addis reg,0,(insn)@h */
+ patch_instruction(addr, PPC_INST_ADDIS | ___PPC_RT(reg) |
((val >> 16) & 0xffff));
addr++;
- /* ori r4,r4,(insn)@l */
- patch_instruction(addr, PPC_INST_ORI | ___PPC_RA(4) |
- ___PPC_RS(4) | (val & 0xffff));
+ /* ori reg,reg,(insn)@l */
+ patch_instruction(addr, PPC_INST_ORI | ___PPC_RA(reg) |
+ ___PPC_RS(reg) | (val & 0xffff));
}
/*
* Generate instructions to load provided immediate 64-bit value
* to register 'r3' and patch these instructions at 'addr'.
*/
-void patch_imm64_load_insns(unsigned long val, kprobe_opcode_t *addr)
+static void patch_imm64_load_insns(unsigned long val, kprobe_opcode_t *addr)
{
/* lis r3,(op)@highest */
patch_instruction(addr, PPC_INST_ADDIS | ___PPC_RT(3) |
@@ -266,9 +269,11 @@ int arch_prepare_optimized_kprobe(struct optimized_kprobe *op, struct kprobe *p)
patch_instruction(buff + TMPL_EMULATE_IDX, branch_emulate_step);
/*
- * 3. load instruction to be emulated into relevant register, and
+ * 3. load instruction and suffix to be emulated into the relevant
+ * registers, and
*/
- patch_imm32_load_insns(*p->ainsn.insn, buff + TMPL_INSN_IDX);
+ patch_imm32_load_insns(4, p->ainsn.insn[0], buff + TMPL_INSN_IDX);
+ patch_imm32_load_insns(5, p->ainsn.insn[1], buff + TMPL_SUFX_IDX);
/*
* 4. branch back from trampoline
diff --git a/arch/powerpc/kernel/optprobes_head.S b/arch/powerpc/kernel/optprobes_head.S
index cf383520843f..998359ae44ec 100644
--- a/arch/powerpc/kernel/optprobes_head.S
+++ b/arch/powerpc/kernel/optprobes_head.S
@@ -95,6 +95,12 @@ optprobe_template_insn:
nop
nop
+ .global optprobe_template_sufx
+optprobe_template_sufx:
+ /* Pass suffix to be emulated in r5 */
+ nop
+ nop
+
.global optprobe_template_call_emulate
optprobe_template_call_emulate:
/* Branch to emulate_step() */
--
2.20.1
next prev parent reply other threads:[~2019-11-26 5:52 UTC|newest]
Thread overview: 42+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-11-26 5:21 [PATCH 00/18] Initial Prefixed Instruction support Jordan Niethe
2019-11-26 5:21 ` [PATCH 01/18] powerpc: Enable Prefixed Instructions Jordan Niethe
2019-11-26 5:21 ` [PATCH 02/18] powerpc: Add BOUNDARY SRR1 bit for future ISA version Jordan Niethe
2019-11-26 5:21 ` [PATCH 03/18] powerpc: Add PREFIXED " Jordan Niethe
2019-12-18 8:23 ` Daniel Axtens
2019-12-20 5:09 ` Jordan Niethe
2019-11-26 5:21 ` [PATCH 04/18] powerpc: Rename Bit 35 of SRR1 to indicate new purpose Jordan Niethe
2019-11-26 5:21 ` [PATCH 05/18] powerpc sstep: Prepare to support prefixed instructions Jordan Niethe
2019-12-18 8:35 ` Daniel Axtens
2019-12-20 5:11 ` Jordan Niethe
2019-12-20 5:40 ` Christophe Leroy
2019-12-18 14:15 ` Daniel Axtens
2019-12-20 5:17 ` Jordan Niethe
2020-01-07 3:01 ` Jordan Niethe
2020-01-13 6:18 ` Balamuruhan S
2020-02-06 23:12 ` Jordan Niethe
2019-11-26 5:21 ` [PATCH 06/18] powerpc sstep: Add support for prefixed integer load/stores Jordan Niethe
2020-01-10 10:38 ` Balamuruhan S
2020-02-07 0:18 ` Jordan Niethe
2020-01-10 15:13 ` Balamuruhan S
2020-02-07 0:20 ` Jordan Niethe
2019-11-26 5:21 ` [PATCH 07/18] powerpc sstep: Add support for prefixed floating-point load/stores Jordan Niethe
2019-11-26 5:21 ` [PATCH 08/18] powerpc sstep: Add support for prefixed VSX load/stores Jordan Niethe
2019-12-18 14:05 ` Daniel Axtens
2019-11-26 5:21 ` [PATCH 09/18] powerpc sstep: Add support for prefixed fixed-point arithmetic Jordan Niethe
2019-11-26 5:21 ` [PATCH 10/18] powerpc: Support prefixed instructions in alignment handler Jordan Niethe
2019-11-26 5:21 ` [PATCH 11/18] powerpc/traps: Check for prefixed instructions in facility_unavailable_exception() Jordan Niethe
2019-11-26 5:21 ` [PATCH 12/18] powerpc/xmon: Add initial support for prefixed instructions Jordan Niethe
2019-11-26 5:21 ` [PATCH 13/18] powerpc/xmon: Dump " Jordan Niethe
2019-11-26 5:21 ` Jordan Niethe [this message]
2020-01-14 7:19 ` [PATCH 14/18] powerpc/kprobes: Support kprobes on " Balamuruhan S
2020-01-16 6:09 ` Michael Ellerman
2019-11-26 5:21 ` [PATCH 15/18] powerpc/uprobes: Add support for " Jordan Niethe
2020-01-13 11:30 ` Balamuruhan S
2020-02-06 23:09 ` Jordan Niethe
2019-11-26 5:21 ` [PATCH 16/18] powerpc/hw_breakpoints: Initial " Jordan Niethe
2019-11-26 5:21 ` [PATCH 17/18] powerpc: Add prefix support to mce_find_instr_ea_and_pfn() Jordan Niethe
2019-11-26 5:21 ` [PATCH 18/18] powerpc/fault: Use analyse_instr() to check for store with updates to sp Jordan Niethe
2019-12-18 14:11 ` Daniel Axtens
2020-02-07 8:15 ` Greg Kurz
2020-02-08 0:28 ` Jordan Niethe
2019-12-03 4:31 ` [PATCH 00/18] Initial Prefixed Instruction support Andrew Donnellan
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20191126052141.28009-15-jniethe5@gmail.com \
--to=jniethe5@gmail.com \
--cc=alistair@popple.id.au \
--cc=linuxppc-dev@lists.ozlabs.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).