// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Kernel Probes (KProbes)
* arch/ia64/kernel/kprobes.c
*
* Copyright (C) IBM Corporation, 2002, 2004
* Copyright (C) Intel Corporation, 2005
*
* 2005-Apr Rusty Lynch <rusty.lynch@intel.com> and Anil S Keshavamurthy
* <anil.s.keshavamurthy@intel.com> adapted from i386
*/
#include <linux/kprobes.h>
#include <linux/ptrace.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/preempt.h>
#include <linux/extable.h>
#include <linux/kdebug.h>
#include <linux/pgtable.h>
#include <asm/sections.h>
#include <asm/exception.h>
DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL;
DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk);
struct kretprobe_blackpoint kretprobe_blacklist[] = {{NULL, NULL}};
enum instruction_type {A, I, M, F, B, L, X, u};
static enum instruction_type bundle_encoding[32][3] = {
[0x00] = { M, I, I },
[0x01] = { M, I, I },
[0x02] = { M, I, I },
[0x03] = { M, I, I },
[0x04] = { M, L, X },
[0x05] = { M, L, X },
[0x06] = { u, u, u },
[0x07] = { u, u, u },
[0x08] = { M, M, I },
[0x09] = { M, M, I },
[0x0A] = { M, M, I },
[0x0B] = { M, M, I },
[0x0C] = { M, F, I },
[0x0D] = { M, F, I },
[0x0E] = { M, M, F },
[0x0F] = { M, M, F },
[0x10] = { M, I, B },
[0x11] = { M, I, B },
[0x12] = { M, B, B },
[0x13] = { M, B, B },
[0x14] = { u, u, u },
[0x15] = { u, u, u },
[0x16] = { B, B, B },
[0x17] = { B, B, B },
[0x18] = { M, M, B },
[0x19] = { M, M, B },
[0x1A] = { u, u, u },
[0x1B] = { u, u, u },
[0x1C] = { M, F, B },
[0x1D] = { M, F, B },
[0x1E] = { u, u, u },
[0x1F] = { u, u, u },
};
/* Insert a long branch code */
static void __kprobes set_brl_inst(void *from, void *to)
{
s64 rel = ((s64) to - (s64) from) >> 4;
bundle_t *brl;
brl = (bundle_t *) ((u64) from & ~0xf);
brl->quad0.template = 0x05; /* [MLX](stop) */
brl->quad0.slot0 = NOP_M_INST; /* nop.m 0x0 */
brl->quad0.slot1_p0 = ((rel >> 20) & 0x7fffffffff) << 2;
brl->quad1.slot1_p1 = (((rel >> 20) & 0x7fffffffff) << 2) >> (64 - 46);
/* brl.cond.sptk.many.clr rel<<4 (qp=0) */
brl->quad1.slot2 = BRL_INST(rel >> 59, rel & 0xfffff);
}
/*
* In this function we check to see if the instruction
* is IP relative instruction and update the kprobe
* inst flag accordingly
*/
static void __kprobes update_kprobe_inst_flag(uint template, uint slot,
uint major_opcode,
unsigned long kprobe_inst,
struct kprobe *p)
{
p->ainsn.inst_flag = 0;
p->ainsn.target_br_reg = 0;
p->ainsn.slot = slot;
/* Check for Break instruction
* Bits 37:40 Major opcode to be zero
* Bits 27:32 X6 to be zero
* Bits 32:35 X3 to be zero
*/
if ((!major_opcode) && (!((kprobe_inst >> 27) & 0x1FF)) ) {
/* is a break instruction */
p->ainsn.inst_flag |= INST_FLAG_BREAK_INST;
return;
}
if (bundle_encoding[template][slot] == B) {
switch (major_opcode) {
case INDIRECT_CALL_OPCODE:
p->ainsn.inst_flag |= INST_FLAG_FIX_BRANCH_REG;
p->ainsn.target_br_reg = ((kprobe_inst >> 6) & 0x7);
break;
case IP_RELATIVE_PREDICT_OPCODE:
case IP_RELATIVE_BRANCH_OPCODE:
p->ainsn.inst_flag |= INST_FLAG_FIX_RELATIVE_IP_ADDR;
break;
case IP_RELATIVE_CALL_OPCODE:
p->ainsn.inst_flag |= INST_FLAG_FIX_RELATIVE_IP_ADDR;
p->ainsn.inst_flag |= INST_FLAG_FIX_BRANCH_REG;
p->ainsn.target_br_reg = ((kprobe_inst >> 6) & 0x7);
break;
}
} else if (bundle_encoding[template][slot] == X) {
switch (major_opcode) {
case LONG_CALL_OPCODE:
p->ainsn.inst_flag |= INST_FLAG_FIX_BRANCH_REG;
p->ainsn.target_br_reg = ((kprobe_inst >> 6) & 0x7);
break;
}
}
return;
}
/*
* In this function we check to see if the instruction
* (qp) cmpx.crel.ctype p1,p2=r2,r3
* on which we are inserting kprobe is cmp instruction
* with ctype as unc.
*/
static uint __kprobes is_cmp_ctype_unc_inst(uint template, uint slot,
uint major_opcode,
unsigned long kprobe_inst)
{
cmp_inst_t cmp_inst;
uint ctype_unc = 0;
if (!((bundle_encoding[template][slot] == I) ||
(bundle_encoding[template][slot] == M)))
goto out;
if (!((major_opcode == 0xC) || (major_opcode == 0xD) ||
(major_opcode == 0xE)))
goto out;
cmp_inst.l = kprobe_inst;
if ((cmp_inst.f.x2 == 0) || (cmp_inst.f.