Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * arch/arm/probes/kprobes/actions-common.c
0004  *
0005  * Copyright (C) 2011 Jon Medhurst <tixy@yxit.co.uk>.
0006  *
0007  * Some contents moved here from arch/arm/include/asm/kprobes-arm.c which is
0008  * Copyright (C) 2006, 2007 Motorola Inc.
0009  */
0010 
0011 #include <linux/kernel.h>
0012 #include <linux/kprobes.h>
0013 #include <asm/opcodes.h>
0014 
0015 #include "core.h"
0016 
0017 
0018 static void __kprobes simulate_ldm1stm1(probes_opcode_t insn,
0019         struct arch_probes_insn *asi,
0020         struct pt_regs *regs)
0021 {
0022     int rn = (insn >> 16) & 0xf;
0023     int lbit = insn & (1 << 20);
0024     int wbit = insn & (1 << 21);
0025     int ubit = insn & (1 << 23);
0026     int pbit = insn & (1 << 24);
0027     long *addr = (long *)regs->uregs[rn];
0028     int reg_bit_vector;
0029     int reg_count;
0030 
0031     reg_count = 0;
0032     reg_bit_vector = insn & 0xffff;
0033     while (reg_bit_vector) {
0034         reg_bit_vector &= (reg_bit_vector - 1);
0035         ++reg_count;
0036     }
0037 
0038     if (!ubit)
0039         addr -= reg_count;
0040     addr += (!pbit == !ubit);
0041 
0042     reg_bit_vector = insn & 0xffff;
0043     while (reg_bit_vector) {
0044         int reg = __ffs(reg_bit_vector);
0045         reg_bit_vector &= (reg_bit_vector - 1);
0046         if (lbit)
0047             regs->uregs[reg] = *addr++;
0048         else
0049             *addr++ = regs->uregs[reg];
0050     }
0051 
0052     if (wbit) {
0053         if (!ubit)
0054             addr -= reg_count;
0055         addr -= (!pbit == !ubit);
0056         regs->uregs[rn] = (long)addr;
0057     }
0058 }
0059 
0060 static void __kprobes simulate_stm1_pc(probes_opcode_t insn,
0061     struct arch_probes_insn *asi,
0062     struct pt_regs *regs)
0063 {
0064     unsigned long addr = regs->ARM_pc - 4;
0065 
0066     regs->ARM_pc = (long)addr + str_pc_offset;
0067     simulate_ldm1stm1(insn, asi, regs);
0068     regs->ARM_pc = (long)addr + 4;
0069 }
0070 
0071 static void __kprobes simulate_ldm1_pc(probes_opcode_t insn,
0072     struct arch_probes_insn *asi,
0073     struct pt_regs *regs)
0074 {
0075     simulate_ldm1stm1(insn, asi, regs);
0076     load_write_pc(regs->ARM_pc, regs);
0077 }
0078 
0079 static void __kprobes
0080 emulate_generic_r0_12_noflags(probes_opcode_t insn,
0081     struct arch_probes_insn *asi, struct pt_regs *regs)
0082 {
0083     register void *rregs asm("r1") = regs;
0084     register void *rfn asm("lr") = asi->insn_fn;
0085 
0086     __asm__ __volatile__ (
0087 ARM(        "stmdb  sp!, {%[regs], r11} \n\t"   )
0088 THUMB(      "stmdb  sp!, {%[regs], r7}  \n\t"   )
0089         "ldmia  %[regs], {r0-r12}   \n\t"
0090 #if __LINUX_ARM_ARCH__ >= 6
0091         "blx    %[fn]           \n\t"
0092 #else
0093         "str    %[fn], [sp, #-4]!   \n\t"
0094         "adr    lr, 1f          \n\t"
0095         "ldr    pc, [sp], #4        \n\t"
0096         "1:             \n\t"
0097 #endif
0098         "ldr    lr, [sp], #4        \n\t" /* lr = regs */
0099         "stmia  lr, {r0-r12}        \n\t"
0100 ARM(        "ldr    r11, [sp], #4       \n\t"   )
0101 THUMB(      "ldr    r7, [sp], #4        \n\t"   )
0102         : [regs] "=r" (rregs), [fn] "=r" (rfn)
0103         : "0" (rregs), "1" (rfn)
0104         : "r0", "r2", "r3", "r4", "r5", "r6", ARM("r7") THUMB("r11"),
0105           "r8", "r9", "r10", "r12", "memory", "cc"
0106         );
0107 }
0108 
0109 static void __kprobes
0110 emulate_generic_r2_14_noflags(probes_opcode_t insn,
0111     struct arch_probes_insn *asi, struct pt_regs *regs)
0112 {
0113     emulate_generic_r0_12_noflags(insn, asi,
0114         (struct pt_regs *)(regs->uregs+2));
0115 }
0116 
0117 static void __kprobes
0118 emulate_ldm_r3_15(probes_opcode_t insn,
0119     struct arch_probes_insn *asi, struct pt_regs *regs)
0120 {
0121     emulate_generic_r0_12_noflags(insn, asi,
0122         (struct pt_regs *)(regs->uregs+3));
0123     load_write_pc(regs->ARM_pc, regs);
0124 }
0125 
0126 enum probes_insn __kprobes
0127 kprobe_decode_ldmstm(probes_opcode_t insn, struct arch_probes_insn *asi,
0128         const struct decode_header *h)
0129 {
0130     probes_insn_handler_t *handler = 0;
0131     unsigned reglist = insn & 0xffff;
0132     int is_ldm = insn & 0x100000;
0133     int rn = (insn >> 16) & 0xf;
0134 
0135     if (rn <= 12 && (reglist & 0xe000) == 0) {
0136         /* Instruction only uses registers in the range R0..R12 */
0137         handler = emulate_generic_r0_12_noflags;
0138 
0139     } else if (rn >= 2 && (reglist & 0x8003) == 0) {
0140         /* Instruction only uses registers in the range R2..R14 */
0141         rn -= 2;
0142         reglist >>= 2;
0143         handler = emulate_generic_r2_14_noflags;
0144 
0145     } else if (rn >= 3 && (reglist & 0x0007) == 0) {
0146         /* Instruction only uses registers in the range R3..R15 */
0147         if (is_ldm && (reglist & 0x8000)) {
0148             rn -= 3;
0149             reglist >>= 3;
0150             handler = emulate_ldm_r3_15;
0151         }
0152     }
0153 
0154     if (handler) {
0155         /* We can emulate the instruction in (possibly) modified form */
0156         asi->insn[0] = __opcode_to_mem_arm((insn & 0xfff00000) |
0157                            (rn << 16) | reglist);
0158         asi->insn_handler = handler;
0159         return INSN_GOOD;
0160     }
0161 
0162     /* Fallback to slower simulation... */
0163     if (reglist & 0x8000)
0164         handler = is_ldm ? simulate_ldm1_pc : simulate_stm1_pc;
0165     else
0166         handler = simulate_ldm1stm1;
0167     asi->insn_handler = handler;
0168     return INSN_GOOD_NO_SLOT;
0169 }
0170