// SPDX-License-Identifier: GPL-2.0-only /* * arch/arm/probes/decode.c * * Copyright (C) 2011 Jon Medhurst <tixy@yxit.co.uk>. * * Some contents moved here from arch/arm/include/asm/kprobes-arm.c which is * Copyright (C) 2006, 2007 Motorola Inc.
*/
/* * For STR and STM instructions, an ARM core may choose to use either * a +8 or a +12 displacement from the current instruction's address. * Whichever value is chosen for a given core, it must be the same for * both instructions and may not change. This function measures it.
*/
int str_pc_offset;
void __init find_str_pc_offset(void)
{ int addr, scratch, ret;
/* * Prepare an instruction slot to receive an instruction for emulating. * This is done by placing a subroutine return after the location where the * instruction will be placed. We also modify ARM instructions to be * unconditional as the condition code will already be checked before any * emulation handler is called.
*/ static probes_opcode_t __kprobes
prepare_emulated_insn(probes_opcode_t insn, struct arch_probes_insn *asi, bool thumb)
{ #ifdef CONFIG_THUMB2_KERNEL if (thumb) {
u16 *thumb_insn = (u16 *)asi->insn; /* Thumb bx lr */
thumb_insn[1] = __opcode_to_mem_thumb16(0x4770);
thumb_insn[2] = __opcode_to_mem_thumb16(0x4770); return insn;
}
asi->insn[1] = __opcode_to_mem_arm(0xe12fff1e); /* ARM bx lr */ #else
asi->insn[1] = __opcode_to_mem_arm(0xe1a0f00e); /* mov pc, lr */ #endif /* Make an ARM instruction unconditional */ if (insn < 0xe0000000)
insn = (insn | 0xe0000000) & ~0x10000000; return insn;
}
/* * Write a (probably modified) instruction into the slot previously prepared by * prepare_emulated_insn
*/ staticvoid __kprobes
set_emulated_insn(probes_opcode_t insn, struct arch_probes_insn *asi, bool thumb)
{ #ifdef CONFIG_THUMB2_KERNEL if (thumb) {
u16 *ip = (u16 *)asi->insn; if (is_wide_instruction(insn))
*ip++ = __opcode_to_mem_thumb16(insn >> 16);
*ip++ = __opcode_to_mem_thumb16(insn); return;
} #endif
asi->insn[0] = __opcode_to_mem_arm(insn);
}
/* * When we modify the register numbers encoded in an instruction to be emulated, * the new values come from this define. For ARM and 32-bit Thumb instructions * this gives... * * bit position 16 12 8 4 0 * ---------------+---+---+---+---+---+ * register r2 r0 r1 -- r3
*/ #define INSN_NEW_BITS 0x00020103
/* Each nibble has same value as that at INSN_NEW_BITS bit 16 */ #define INSN_SAMEAS16_BITS 0x22222222
/* * Validate and modify each of the registers encoded in an instruction. * * Each nibble in regs contains a value from enum decode_reg_type. For each * non-zero value, the corresponding nibble in pinsn is validated and modified * according to the type.
*/ staticbool __kprobes decode_regs(probes_opcode_t *pinsn, u32 regs, bool modify)
{
probes_opcode_t insn = *pinsn;
probes_opcode_t mask = 0xf; /* Start at least significant nibble */
for (; regs != 0; regs >>= 4, mask <<= 4) {
probes_opcode_t new_bits = INSN_NEW_BITS;
switch (regs & 0xf) {
case REG_TYPE_NONE: /* Nibble not a register, skip to next */ continue;
case REG_TYPE_ANY: /* Any register is allowed */ break;
case REG_TYPE_SAMEAS16: /* Replace register with same as at bit position 16 */
new_bits = INSN_SAMEAS16_BITS; break;
case REG_TYPE_SP: /* Only allow SP (R13) */ if ((insn ^ 0xdddddddd) & mask) goto reject; break;
case REG_TYPE_PC: /* Only allow PC (R15) */ if ((insn ^ 0xffffffff) & mask) goto reject; break;
case REG_TYPE_NOSP: /* Reject SP (R13) */ if (((insn ^ 0xdddddddd) & mask) == 0) goto reject; break;
case REG_TYPE_NOSPPC: case REG_TYPE_NOSPPCX: /* Reject SP and PC (R13 and R15) */ if (((insn ^ 0xdddddddd) & 0xdddddddd & mask) == 0) goto reject; break;
case REG_TYPE_NOPCWB: if (!is_writeback(insn)) break; /* No writeback, so any register is OK */
fallthrough; case REG_TYPE_NOPC: case REG_TYPE_NOPCX: /* Reject PC (R15) */ if (((insn ^ 0xffffffff) & mask) == 0) goto reject; break;
}
/* Replace value of nibble with new register number... */
insn &= ~mask;
insn |= new_bits & mask;
}
/* * probes_decode_insn operates on data tables in order to decode an ARM * architecture instruction onto which a kprobe has been placed. * * These instruction decoding tables are a concatenation of entries each * of which consist of one of the following structs: * * decode_table * decode_custom * decode_simulate * decode_emulate * decode_or * decode_reject * * Each of these starts with a struct decode_header which has the following * fields: * * type_regs * mask * value * * The least significant DECODE_TYPE_BITS of type_regs contains a value * from enum decode_type, this indicates which of the decode_* structs * the entry contains. The value DECODE_TYPE_END indicates the end of the * table. * * When the table is parsed, each entry is checked in turn to see if it * matches the instruction to be decoded using the test: * * (insn & mask) == value * * If no match is found before the end of the table is reached then decoding * fails with INSN_REJECTED. * * When a match is found, decode_regs() is called to validate and modify each * of the registers encoded in the instruction; the data it uses to do this * is (type_regs >> DECODE_TYPE_BITS). A validation failure will cause decoding * to fail with INSN_REJECTED. * * Once the instruction has passed the above tests, further processing * depends on the type of the table entry's decode struct. *
*/ int __kprobes
probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, constunion decode_item *table, bool thumb, bool emulate, constunion decode_action *actions, conststruct decode_checker *checkers[])
{ conststruct decode_header *h = (struct decode_header *)table; conststruct decode_header *next; bool matched = false; /* * @insn can be modified by decode_regs. Save its original * value for checkers.
*/
probes_opcode_t origin_insn = insn;
/* * stack_space is initialized to 0 here. Checker functions * should update is value if they find this is a stack store * instruction: positive value means bytes of stack usage, * negitive value means unable to determine stack usage * statically. For instruction doesn't store to stack, checker * do nothing with it.
*/
asi->stack_space = 0;
/* * Similarly to stack_space, register_usage_flags is filled by * checkers. Its default value is set to ~0, which is 'all * registers are used', to prevent any potential optimization.
*/
asi->register_usage_flags = ~0UL;
if (emulate)
insn = prepare_emulated_insn(insn, asi, thumb);
for (;; h = next) { enum decode_type type = h->type_regs.bits & DECODE_TYPE_MASK;
u32 regs = h->type_regs.bits >> DECODE_TYPE_BITS;
if (type == DECODE_TYPE_END) return INSN_REJECTED;
next = (struct decode_header *)
((uintptr_t)h + decode_struct_sizes[type]);
if (!matched && (insn & h->mask.bits) != h->value.bits) continue;
if (!decode_regs(&insn, regs, emulate)) return INSN_REJECTED;
switch (type) {
case DECODE_TYPE_TABLE: { struct decode_table *d = (struct decode_table *)h;
next = (struct decode_header *)d->table.table; break;
}
case DECODE_TYPE_CUSTOM: { int err; struct decode_custom *d = (struct decode_custom *)h; int action = d->decoder.action;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.