Commit fd7b231ff98578308d8f5fb76a25a369ce1074ae

Authored by Anil S Keshavamurthy
Committed by Linus Torvalds
1 parent 7213b25218

[PATCH] Kprobes/IA64: arch specific handling

This is an IA64 arch specific handling of Kprobes

Signed-off-by: Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>
Signed-off-by: Rusty Lynch <Rusty.lynch@intel.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>

Showing 4 changed files with 416 additions and 0 deletions Side-by-side Diff

arch/ia64/Kconfig.debug
... ... @@ -2,6 +2,17 @@
2 2  
3 3 source "lib/Kconfig.debug"
4 4  
  5 +config KPROBES
  6 + bool "Kprobes"
  7 + depends on DEBUG_KERNEL
  8 + help
  9 + Kprobes allows you to trap at almost any kernel address and
  10 + execute a callback function. register_kprobe() establishes
  11 + a probepoint and specifies the callback. Kprobes is useful
  12 + for kernel debugging, non-intrusive instrumentation and testing.
  13 + If in doubt, say "N".
  14 +
  15 +
5 16 choice
6 17 prompt "Physical memory granularity"
7 18 default IA64_GRANULE_64MB
arch/ia64/kernel/Makefile
... ... @@ -20,6 +20,7 @@
20 20 obj-$(CONFIG_PERFMON) += perfmon_default_smpl.o
21 21 obj-$(CONFIG_IA64_CYCLONE) += cyclone.o
22 22 obj-$(CONFIG_IA64_MCA_RECOVERY) += mca_recovery.o
  23 +obj-$(CONFIG_KPROBES) += kprobes.o
23 24 obj-$(CONFIG_IA64_UNCACHED_ALLOCATOR) += uncached.o
24 25 mca_recovery-y += mca_drv.o mca_drv_asm.o
25 26  
arch/ia64/kernel/kprobes.c
  1 +/*
  2 + * Kernel Probes (KProbes)
  3 + * arch/ia64/kernel/kprobes.c
  4 + *
  5 + * This program is free software; you can redistribute it and/or modify
  6 + * it under the terms of the GNU General Public License as published by
  7 + * the Free Software Foundation; either version 2 of the License, or
  8 + * (at your option) any later version.
  9 + *
  10 + * This program is distributed in the hope that it will be useful,
  11 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13 + * GNU General Public License for more details.
  14 + *
  15 + * You should have received a copy of the GNU General Public License
  16 + * along with this program; if not, write to the Free Software
  17 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  18 + *
  19 + * Copyright (C) IBM Corporation, 2002, 2004
  20 + * Copyright (C) Intel Corporation, 2005
  21 + *
  22 + * 2005-Apr Rusty Lynch <rusty.lynch@intel.com> and Anil S Keshavamurthy
  23 + * <anil.s.keshavamurthy@intel.com> adapted from i386
  24 + */
  25 +
  26 +#include <linux/config.h>
  27 +#include <linux/kprobes.h>
  28 +#include <linux/ptrace.h>
  29 +#include <linux/spinlock.h>
  30 +#include <linux/string.h>
  31 +#include <linux/slab.h>
  32 +#include <linux/preempt.h>
  33 +#include <linux/moduleloader.h>
  34 +
  35 +#include <asm/pgtable.h>
  36 +#include <asm/kdebug.h>
  37 +
  38 +/* kprobe_status settings */
  39 +#define KPROBE_HIT_ACTIVE 0x00000001
  40 +#define KPROBE_HIT_SS 0x00000002
  41 +
  42 +static struct kprobe *current_kprobe;
  43 +static unsigned long kprobe_status;
  44 +
  45 +enum instruction_type {A, I, M, F, B, L, X, u};
  46 +static enum instruction_type bundle_encoding[32][3] = {
  47 + { M, I, I }, /* 00 */
  48 + { M, I, I }, /* 01 */
  49 + { M, I, I }, /* 02 */
  50 + { M, I, I }, /* 03 */
  51 + { M, L, X }, /* 04 */
  52 + { M, L, X }, /* 05 */
  53 + { u, u, u }, /* 06 */
  54 + { u, u, u }, /* 07 */
  55 + { M, M, I }, /* 08 */
  56 + { M, M, I }, /* 09 */
  57 + { M, M, I }, /* 0A */
  58 + { M, M, I }, /* 0B */
  59 + { M, F, I }, /* 0C */
  60 + { M, F, I }, /* 0D */
  61 + { M, M, F }, /* 0E */
  62 + { M, M, F }, /* 0F */
  63 + { M, I, B }, /* 10 */
  64 + { M, I, B }, /* 11 */
  65 + { M, B, B }, /* 12 */
  66 + { M, B, B }, /* 13 */
  67 + { u, u, u }, /* 14 */
  68 + { u, u, u }, /* 15 */
  69 + { B, B, B }, /* 16 */
  70 + { B, B, B }, /* 17 */
  71 + { M, M, B }, /* 18 */
  72 + { M, M, B }, /* 19 */
  73 + { u, u, u }, /* 1A */
  74 + { u, u, u }, /* 1B */
  75 + { M, F, B }, /* 1C */
  76 + { M, F, B }, /* 1D */
  77 + { u, u, u }, /* 1E */
  78 + { u, u, u }, /* 1F */
  79 +};
  80 +
  81 +int arch_prepare_kprobe(struct kprobe *p)
  82 +{
  83 + unsigned long addr = (unsigned long) p->addr;
  84 + unsigned long bundle_addr = addr & ~0xFULL;
  85 + unsigned long slot = addr & 0xf;
  86 + bundle_t bundle;
  87 + unsigned long template;
  88 +
  89 + /*
  90 + * TODO: Verify that a probe is not being inserted
  91 + * in sensitive regions of code
  92 + * TODO: Verify that the memory holding the probe is rwx
  93 + * TODO: verify this is a kernel address
  94 + */
  95 + memcpy(&bundle, (unsigned long *)bundle_addr, sizeof(bundle_t));
  96 + template = bundle.quad0.template;
  97 + if (((bundle_encoding[template][1] == L) && slot > 1) || (slot > 2)) {
  98 + printk(KERN_WARNING "Attempting to insert unaligned kprobe at 0x%lx\n", addr);
  99 + return -EINVAL;
  100 + }
  101 + return 0;
  102 +}
  103 +
  104 +void arch_copy_kprobe(struct kprobe *p)
  105 +{
  106 + unsigned long addr = (unsigned long)p->addr;
  107 + unsigned long bundle_addr = addr & ~0xFULL;
  108 +
  109 + memcpy(&p->ainsn.insn.bundle, (unsigned long *)bundle_addr,
  110 + sizeof(bundle_t));
  111 + memcpy(&p->opcode.bundle, &p->ainsn.insn.bundle, sizeof(bundle_t));
  112 +}
  113 +
  114 +void arch_arm_kprobe(struct kprobe *p)
  115 +{
  116 + unsigned long addr = (unsigned long)p->addr;
  117 + unsigned long arm_addr = addr & ~0xFULL;
  118 + unsigned long slot = addr & 0xf;
  119 + unsigned long template;
  120 + bundle_t bundle;
  121 +
  122 + memcpy(&bundle, &p->ainsn.insn.bundle, sizeof(bundle_t));
  123 +
  124 + template = bundle.quad0.template;
  125 + if (slot == 1 && bundle_encoding[template][1] == L)
  126 + slot = 2;
  127 + switch (slot) {
  128 + case 0:
  129 + bundle.quad0.slot0 = BREAK_INST;
  130 + break;
  131 + case 1:
  132 + bundle.quad0.slot1_p0 = BREAK_INST;
  133 + bundle.quad1.slot1_p1 = (BREAK_INST >> (64-46));
  134 + break;
  135 + case 2:
  136 + bundle.quad1.slot2 = BREAK_INST;
  137 + break;
  138 + }
  139 +
  140 + /* Flush icache for the instruction at the emulated address */
  141 + flush_icache_range((unsigned long)&p->ainsn.insn.bundle,
  142 + (unsigned long)&p->ainsn.insn.bundle +
  143 + sizeof(bundle_t));
  144 + /*
  145 + * Patch the original instruction with the probe instruction
  146 + * and flush the instruction cache
  147 + */
  148 + memcpy((char *) arm_addr, (char *) &bundle, sizeof(bundle_t));
  149 + flush_icache_range(arm_addr, arm_addr + sizeof(bundle_t));
  150 +}
  151 +
  152 +void arch_disarm_kprobe(struct kprobe *p)
  153 +{
  154 + unsigned long addr = (unsigned long)p->addr;
  155 + unsigned long arm_addr = addr & ~0xFULL;
  156 +
  157 + /* p->opcode contains the original unaltered bundle */
  158 + memcpy((char *) arm_addr, (char *) &p->opcode.bundle, sizeof(bundle_t));
  159 + flush_icache_range(arm_addr, arm_addr + sizeof(bundle_t));
  160 +}
  161 +
  162 +void arch_remove_kprobe(struct kprobe *p)
  163 +{
  164 +}
  165 +
  166 +/*
  167 + * We are resuming execution after a single step fault, so the pt_regs
  168 + * structure reflects the register state after we executed the instruction
  169 + * located in the kprobe (p->ainsn.insn.bundle). We still need to adjust
  170 + * the ip to point back to the original stack address, and if we see that
  171 + * the slot has incremented back to zero, then we need to point to the next
  172 + * slot location.
  173 + */
  174 +static void resume_execution(struct kprobe *p, struct pt_regs *regs)
  175 +{
  176 + unsigned long bundle = (unsigned long)p->addr & ~0xFULL;
  177 +
  178 + /*
  179 + * TODO: Handle cases where kprobe was inserted on a branch instruction
  180 + */
  181 +
  182 + if (!ia64_psr(regs)->ri)
  183 + regs->cr_iip = bundle + 0x10;
  184 + else
  185 + regs->cr_iip = bundle;
  186 +
  187 + ia64_psr(regs)->ss = 0;
  188 +}
  189 +
  190 +static void prepare_ss(struct kprobe *p, struct pt_regs *regs)
  191 +{
  192 + unsigned long bundle_addr = (unsigned long) &p->ainsn.insn.bundle;
  193 + unsigned long slot = (unsigned long)p->addr & 0xf;
  194 +
  195 + /* Update instruction pointer (IIP) and slot number (IPSR.ri) */
  196 + regs->cr_iip = bundle_addr & ~0xFULL;
  197 +
  198 + if (slot > 2)
  199 + slot = 0;
  200 +
  201 + ia64_psr(regs)->ri = slot;
  202 +
  203 + /* turn on single stepping */
  204 + ia64_psr(regs)->ss = 1;
  205 +}
  206 +
  207 +static int pre_kprobes_handler(struct pt_regs *regs)
  208 +{
  209 + struct kprobe *p;
  210 + int ret = 0;
  211 + kprobe_opcode_t *addr = (kprobe_opcode_t *)instruction_pointer(regs);
  212 +
  213 + preempt_disable();
  214 +
  215 + /* Handle recursion cases */
  216 + if (kprobe_running()) {
  217 + p = get_kprobe(addr);
  218 + if (p) {
  219 + if (kprobe_status == KPROBE_HIT_SS) {
  220 + unlock_kprobes();
  221 + goto no_kprobe;
  222 + }
  223 + arch_disarm_kprobe(p);
  224 + ret = 1;
  225 + } else {
  226 + /*
  227 + * jprobe instrumented function just completed
  228 + */
  229 + p = current_kprobe;
  230 + if (p->break_handler && p->break_handler(p, regs)) {
  231 + goto ss_probe;
  232 + }
  233 + }
  234 + }
  235 +
  236 + lock_kprobes();
  237 + p = get_kprobe(addr);
  238 + if (!p) {
  239 + unlock_kprobes();
  240 + goto no_kprobe;
  241 + }
  242 +
  243 + kprobe_status = KPROBE_HIT_ACTIVE;
  244 + current_kprobe = p;
  245 +
  246 + if (p->pre_handler && p->pre_handler(p, regs))
  247 + /*
  248 + * Our pre-handler is specifically requesting that we just
  249 + * do a return. This is handling the case where the
  250 + * pre-handler is really our special jprobe pre-handler.
  251 + */
  252 + return 1;
  253 +
  254 +ss_probe:
  255 + prepare_ss(p, regs);
  256 + kprobe_status = KPROBE_HIT_SS;
  257 + return 1;
  258 +
  259 +no_kprobe:
  260 + preempt_enable_no_resched();
  261 + return ret;
  262 +}
  263 +
  264 +static int post_kprobes_handler(struct pt_regs *regs)
  265 +{
  266 + if (!kprobe_running())
  267 + return 0;
  268 +
  269 + if (current_kprobe->post_handler)
  270 + current_kprobe->post_handler(current_kprobe, regs, 0);
  271 +
  272 + resume_execution(current_kprobe, regs);
  273 +
  274 + unlock_kprobes();
  275 + preempt_enable_no_resched();
  276 + return 1;
  277 +}
  278 +
  279 +static int kprobes_fault_handler(struct pt_regs *regs, int trapnr)
  280 +{
  281 + if (!kprobe_running())
  282 + return 0;
  283 +
  284 + if (current_kprobe->fault_handler &&
  285 + current_kprobe->fault_handler(current_kprobe, regs, trapnr))
  286 + return 1;
  287 +
  288 + if (kprobe_status & KPROBE_HIT_SS) {
  289 + resume_execution(current_kprobe, regs);
  290 + unlock_kprobes();
  291 + preempt_enable_no_resched();
  292 + }
  293 +
  294 + return 0;
  295 +}
  296 +
  297 +int kprobe_exceptions_notify(struct notifier_block *self, unsigned long val,
  298 + void *data)
  299 +{
  300 + struct die_args *args = (struct die_args *)data;
  301 + switch(val) {
  302 + case DIE_BREAK:
  303 + if (pre_kprobes_handler(args->regs))
  304 + return NOTIFY_STOP;
  305 + break;
  306 + case DIE_SS:
  307 + if (post_kprobes_handler(args->regs))
  308 + return NOTIFY_STOP;
  309 + break;
  310 + case DIE_PAGE_FAULT:
  311 + if (kprobes_fault_handler(args->regs, args->trapnr))
  312 + return NOTIFY_STOP;
  313 + default:
  314 + break;
  315 + }
  316 + return NOTIFY_DONE;
  317 +}
  318 +
  319 +int setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs)
  320 +{
  321 + printk(KERN_WARNING "Jprobes is not supported\n");
  322 + return 0;
  323 +}
  324 +
  325 +void jprobe_return(void)
  326 +{
  327 +}
  328 +
  329 +int longjmp_break_handler(struct kprobe *p, struct pt_regs *regs)
  330 +{
  331 + return 0;
  332 +}
include/asm-ia64/kprobes.h
  1 +#ifndef _ASM_KPROBES_H
  2 +#define _ASM_KPROBES_H
  3 +/*
  4 + * Kernel Probes (KProbes)
  5 + * include/asm-ia64/kprobes.h
  6 + *
  7 + * This program is free software; you can redistribute it and/or modify
  8 + * it under the terms of the GNU General Public License as published by
  9 + * the Free Software Foundation; either version 2 of the License, or
  10 + * (at your option) any later version.
  11 + *
  12 + * This program is distributed in the hope that it will be useful,
  13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15 + * GNU General Public License for more details.
  16 + *
  17 + * You should have received a copy of the GNU General Public License
  18 + * along with this program; if not, write to the Free Software
  19 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  20 + *
  21 + * Copyright (C) IBM Corporation, 2002, 2004
  22 + * Copyright (C) Intel Corporation, 2005
  23 + *
  24 + * 2005-Apr Rusty Lynch <rusty.lynch@intel.com> and Anil S Keshavamurthy
  25 + * <anil.s.keshavamurthy@intel.com> adapted from i386
  26 + */
  27 +#include <linux/types.h>
  28 +#include <linux/ptrace.h>
  29 +#include <asm/break.h>
  30 +
  31 +#define BREAK_INST (long)(__IA64_BREAK_KPROBE << 6)
  32 +
  33 +typedef struct _bundle {
  34 + struct {
  35 + unsigned long long template : 5;
  36 + unsigned long long slot0 : 41;
  37 + unsigned long long slot1_p0 : 64-46;
  38 + } quad0;
  39 + struct {
  40 + unsigned long long slot1_p1 : 41 - (64-46);
  41 + unsigned long long slot2 : 41;
  42 + } quad1;
  43 +} __attribute__((__aligned__(16))) bundle_t;
  44 +
  45 +#define JPROBE_ENTRY(pentry) (kprobe_opcode_t *)pentry
  46 +
  47 +typedef struct kprobe_opcode {
  48 + bundle_t bundle;
  49 +} kprobe_opcode_t;
  50 +
  51 +struct fnptr {
  52 + unsigned long ip;
  53 + unsigned long gp;
  54 +};
  55 +
  56 +/* Architecture specific copy of original instruction*/
  57 +struct arch_specific_insn {
  58 + /* copy of the original instruction */
  59 + kprobe_opcode_t insn;
  60 +};
  61 +
  62 +#ifdef CONFIG_KPROBES
  63 +extern int kprobe_exceptions_notify(struct notifier_block *self,
  64 + unsigned long val, void *data);
  65 +#else /* !CONFIG_KPROBES */
  66 +static inline int kprobe_exceptions_notify(struct notifier_block *self,
  67 + unsigned long val, void *data)
  68 +{
  69 + return 0;
  70 +}
  71 +#endif
  72 +#endif /* _ASM_KPROBES_H */