Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 39a67d49 authored by Sandeepa Prabhu's avatar Sandeepa Prabhu Committed by Catalin Marinas
Browse files

arm64: kprobes instruction simulation support



Kprobes needs simulation of instructions that cannot be stepped
from a different memory location, e.g.: those instructions
that uses PC-relative addressing. In simulation, the behaviour
of the instruction is implemented using a copy of pt_regs.

The following instruction categories are simulated:
 - All branching instructions(conditional, register, and immediate)
 - Literal access instructions(load-literal, adr/adrp)

Conditional execution is limited to branching instructions in
ARM v8. If conditions at PSTATE do not match the condition fields
of opcode, the instruction is effectively NOP.

Thanks to Will Cohen for assorted suggested changes.

Signed-off-by: default avatarSandeepa Prabhu <sandeepa.s.prabhu@gmail.com>
Signed-off-by: default avatarWilliam Cohen <wcohen@redhat.com>
Signed-off-by: default avatarDavid A. Long <dave.long@linaro.org>
Acked-by: default avatarMasami Hiramatsu <mhiramat@kernel.org>
[catalin.marinas@arm.com: removed linux/module.h include]
Signed-off-by: default avatarCatalin Marinas <catalin.marinas@arm.com>
parent 888b3c87
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -15,17 +15,18 @@
#ifndef _ARM_PROBES_H
#define _ARM_PROBES_H

#include <asm/opcodes.h>

struct kprobe;
struct arch_specific_insn;

typedef u32 kprobe_opcode_t;
typedef unsigned long (kprobes_pstate_check_t)(unsigned long);
typedef void (kprobes_handler_t) (u32 opcode, long addr, struct pt_regs *);

/* architecture specific copy of original instruction */
struct arch_specific_insn {
	kprobe_opcode_t *insn;
	kprobes_pstate_check_t *pstate_cc;
	pstate_check_t *pstate_cc;
	kprobes_handler_t *handler;
	/* restore address after step xol */
	unsigned long restore;
+1 −0
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@
#include <asm/cacheflush.h>
#include <asm/debug-monitors.h>
#include <asm/fixmap.h>
#include <asm/opcodes.h>
#include <asm/insn.h>

#define AARCH64_INSN_SF_BIT	BIT(31)
+2 −1
Original line number Diff line number Diff line
obj-$(CONFIG_KPROBES)		+= kprobes.o decode-insn.o
obj-$(CONFIG_KPROBES)		+= kprobes.o decode-insn.o	\
				   simulate-insn.o
+32 −1
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@
#include <asm/sections.h>

#include "decode-insn.h"
#include "simulate-insn.h"

static bool __kprobes aarch64_insn_is_steppable(u32 insn)
{
@@ -74,6 +75,7 @@ static bool __kprobes aarch64_insn_is_steppable(u32 insn)
/* Return:
 *   INSN_REJECTED     If instruction is one not allowed to kprobe,
 *   INSN_GOOD         If instruction is supported and uses instruction slot,
 *   INSN_GOOD_NO_SLOT If instruction is supported but doesn't use its slot.
 */
static enum kprobe_insn __kprobes
arm_probe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi)
@@ -84,10 +86,39 @@ arm_probe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi)
	 */
	if (aarch64_insn_is_steppable(insn))
		return INSN_GOOD;
	else

	if (aarch64_insn_is_bcond(insn)) {
		asi->handler = simulate_b_cond;
	} else if (aarch64_insn_is_cbz(insn) ||
	    aarch64_insn_is_cbnz(insn)) {
		asi->handler = simulate_cbz_cbnz;
	} else if (aarch64_insn_is_tbz(insn) ||
	    aarch64_insn_is_tbnz(insn)) {
		asi->handler = simulate_tbz_tbnz;
	} else if (aarch64_insn_is_adr_adrp(insn)) {
		asi->handler = simulate_adr_adrp;
	} else if (aarch64_insn_is_b(insn) ||
	    aarch64_insn_is_bl(insn)) {
		asi->handler = simulate_b_bl;
	} else if (aarch64_insn_is_br(insn) ||
	    aarch64_insn_is_blr(insn) ||
	    aarch64_insn_is_ret(insn)) {
		asi->handler = simulate_br_blr_ret;
	} else if (aarch64_insn_is_ldr_lit(insn)) {
		asi->handler = simulate_ldr_literal;
	} else if (aarch64_insn_is_ldrsw_lit(insn)) {
		asi->handler = simulate_ldrsw_literal;
	} else {
		/*
		 * Instruction cannot be stepped out-of-line and we don't
		 * (yet) simulate it.
		 */
		return INSN_REJECTED;
	}

	return INSN_GOOD_NO_SLOT;
}

static bool __kprobes
is_probed_address_atomic(kprobe_opcode_t *scan_start, kprobe_opcode_t *scan_end)
{
+1 −0
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@

enum kprobe_insn {
	INSN_REJECTED,
	INSN_GOOD_NO_SLOT,
	INSN_GOOD,
};

Loading