2026-01-21 18:59:54 +08:00

100 lines
2.6 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Based on arch/arm64/kernel/probes/decode-insn.c
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*/
#include <linux/kprobes.h>
#include "common.h"
static bool __kprobes sw64_insn_is_steppable(u32 insn)
{
/*
* Branch instructions will write a new value into the PC which is
* likely to be relative to the XOL address and therefore invalid.
* Deliberate generation of an exception during stepping is also not
* currently safe. Lastly, MSR instructions can do any number of nasty
* things we can't handle during single-stepping.
*/
if (sw64_insn_is_sys_call_b(insn) ||
sw64_insn_is_sys_call(insn) ||
sw64_insn_is_call(insn) ||
sw64_insn_is_ret(insn) ||
sw64_insn_is_jmp(insn) ||
sw64_insn_is_br(insn) ||
sw64_insn_is_bsr(insn) ||
sw64_insn_is_memb(insn) ||
sw64_insn_is_imemb(insn) ||
sw64_insn_is_rtc(insn) ||
sw64_insn_is_lldl(insn) ||
sw64_insn_is_lldw(insn) ||
sw64_insn_is_beq(insn) ||
sw64_insn_is_bne(insn) ||
sw64_insn_is_blt(insn) ||
sw64_insn_is_ble(insn) ||
sw64_insn_is_bgt(insn) ||
sw64_insn_is_bge(insn) ||
sw64_insn_is_blbc(insn) ||
sw64_insn_is_blbs(insn) ||
sw64_insn_is_fbeq(insn) ||
sw64_insn_is_fbne(insn) ||
sw64_insn_is_fblt(insn) ||
sw64_insn_is_fble(insn) ||
sw64_insn_is_fbgt(insn) ||
sw64_insn_is_fbge(insn))
return false;
return true;
}
#ifdef CONFIG_KPROBES
// lldl rd_f
static bool __kprobes is_probed_between_atomic(kprobe_opcode_t *addr)
{
int count = 0;
unsigned long size = 0, offset = 0;
kprobe_opcode_t *scan_start = NULL;
if (kallsyms_lookup_size_offset((unsigned long)addr, &size, &offset))
scan_start = addr - (offset / sizeof(kprobe_opcode_t));
while (scan_start < addr) {
if (sw64_insn_is_lldl(le32_to_cpu(*scan_start)) ||
sw64_insn_is_lldw(le32_to_cpu(*scan_start)))
count++;
if (sw64_insn_is_rd_f(le32_to_cpu(*scan_start)))
count--;
scan_start++;
}
if (count)
return false;
return true;
}
bool __kprobes sw64_insn_can_kprobe(kprobe_opcode_t *addr)
{
u32 insn = le32_to_cpu(*addr);
if (!sw64_insn_is_steppable(insn)) {
printk("addr can't steppable\n");
return false;
}
if (!is_probed_between_atomic(addr)) {
printk("addr between atomic cant probe\n");
return false;
}
return true;
}
#endif