hw-breakpoints: Rewrite the hw-breakpoints layer on top of perf events
[linux-3.10.git] / kernel / hw_breakpoint.c
1 /*
2  * This program is free software; you can redistribute it and/or modify
3  * it under the terms of the GNU General Public License as published by
4  * the Free Software Foundation; either version 2 of the License, or
5  * (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software
14  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15  *
16  * Copyright (C) 2007 Alan Stern
17  * Copyright (C) IBM Corporation, 2009
18  * Copyright (C) 2009, Frederic Weisbecker <fweisbec@gmail.com>
19  */
20
21 /*
22  * HW_breakpoint: a unified kernel/user-space hardware breakpoint facility,
23  * using the CPU's debug registers.
24  * This file contains the arch-independent routines.
25  */
26
27 #include <linux/irqflags.h>
28 #include <linux/kallsyms.h>
29 #include <linux/notifier.h>
30 #include <linux/kprobes.h>
31 #include <linux/kdebug.h>
32 #include <linux/kernel.h>
33 #include <linux/module.h>
34 #include <linux/percpu.h>
35 #include <linux/sched.h>
36 #include <linux/init.h>
37 #include <linux/smp.h>
38
39 #include <linux/hw_breakpoint.h>
40
41 #include <asm/processor.h>
42
43 #ifdef CONFIG_X86
44 #include <asm/debugreg.h>
45 #endif
46
47 static atomic_t bp_slot;
48
49 int reserve_bp_slot(struct perf_event *bp)
50 {
51         if (atomic_inc_return(&bp_slot) == HBP_NUM) {
52                 atomic_dec(&bp_slot);
53
54                 return -ENOSPC;
55         }
56
57         return 0;
58 }
59
60 void release_bp_slot(struct perf_event *bp)
61 {
62         atomic_dec(&bp_slot);
63 }
64
65 int __register_perf_hw_breakpoint(struct perf_event *bp)
66 {
67         int ret;
68
69         ret = reserve_bp_slot(bp);
70         if (ret)
71                 return ret;
72
73         if (!bp->attr.disabled)
74                 ret = arch_validate_hwbkpt_settings(bp, bp->ctx->task);
75
76         return ret;
77 }
78
79 int register_perf_hw_breakpoint(struct perf_event *bp)
80 {
81         bp->callback = perf_bp_event;
82
83         return __register_perf_hw_breakpoint(bp);
84 }
85
86 /*
87  * Register a breakpoint bound to a task and a given cpu.
88  * If cpu is -1, the breakpoint is active for the task in every cpu
89  * If the task is -1, the breakpoint is active for every tasks in the given
90  * cpu.
91  */
92 static struct perf_event *
93 register_user_hw_breakpoint_cpu(unsigned long addr,
94                                 int len,
95                                 int type,
96                                 perf_callback_t triggered,
97                                 pid_t pid,
98                                 int cpu,
99                                 bool active)
100 {
101         struct perf_event_attr *attr;
102         struct perf_event *bp;
103
104         attr = kzalloc(sizeof(*attr), GFP_KERNEL);
105         if (!attr)
106                 return ERR_PTR(-ENOMEM);
107
108         attr->type = PERF_TYPE_BREAKPOINT;
109         attr->size = sizeof(*attr);
110         attr->bp_addr = addr;
111         attr->bp_len = len;
112         attr->bp_type = type;
113         /*
114          * Such breakpoints are used by debuggers to trigger signals when
115          * we hit the excepted memory op. We can't miss such events, they
116          * must be pinned.
117          */
118         attr->pinned = 1;
119
120         if (!active)
121                 attr->disabled = 1;
122
123         bp = perf_event_create_kernel_counter(attr, cpu, pid, triggered);
124         kfree(attr);
125
126         return bp;
127 }
128
129 /**
130  * register_user_hw_breakpoint - register a hardware breakpoint for user space
131  * @addr: is the memory address that triggers the breakpoint
132  * @len: the length of the access to the memory (1 byte, 2 bytes etc...)
133  * @type: the type of the access to the memory (read/write/exec)
134  * @triggered: callback to trigger when we hit the breakpoint
135  * @tsk: pointer to 'task_struct' of the process to which the address belongs
136  * @active: should we activate it while registering it
137  *
138  */
139 struct perf_event *
140 register_user_hw_breakpoint(unsigned long addr,
141                             int len,
142                             int type,
143                             perf_callback_t triggered,
144                             struct task_struct *tsk,
145                             bool active)
146 {
147         return register_user_hw_breakpoint_cpu(addr, len, type, triggered,
148                                                tsk->pid, -1, active);
149 }
150 EXPORT_SYMBOL_GPL(register_user_hw_breakpoint);
151
152 /**
153  * modify_user_hw_breakpoint - modify a user-space hardware breakpoint
154  * @bp: the breakpoint structure to modify
155  * @addr: is the memory address that triggers the breakpoint
156  * @len: the length of the access to the memory (1 byte, 2 bytes etc...)
157  * @type: the type of the access to the memory (read/write/exec)
158  * @triggered: callback to trigger when we hit the breakpoint
159  * @tsk: pointer to 'task_struct' of the process to which the address belongs
160  * @active: should we activate it while registering it
161  */
162 struct perf_event *
163 modify_user_hw_breakpoint(struct perf_event *bp,
164                           unsigned long addr,
165                           int len,
166                           int type,
167                           perf_callback_t triggered,
168                           struct task_struct *tsk,
169                           bool active)
170 {
171         /*
172          * FIXME: do it without unregistering
173          * - We don't want to lose our slot
174          * - If the new bp is incorrect, don't lose the older one
175          */
176         unregister_hw_breakpoint(bp);
177
178         return register_user_hw_breakpoint(addr, len, type, triggered,
179                                            tsk, active);
180 }
181 EXPORT_SYMBOL_GPL(modify_user_hw_breakpoint);
182
183 /**
184  * unregister_hw_breakpoint - unregister a user-space hardware breakpoint
185  * @bp: the breakpoint structure to unregister
186  */
187 void unregister_hw_breakpoint(struct perf_event *bp)
188 {
189         if (!bp)
190                 return;
191         perf_event_release_kernel(bp);
192 }
193 EXPORT_SYMBOL_GPL(unregister_hw_breakpoint);
194
195 static struct perf_event *
196 register_kernel_hw_breakpoint_cpu(unsigned long addr,
197                                   int len,
198                                   int type,
199                                   perf_callback_t triggered,
200                                   int cpu,
201                                   bool active)
202 {
203         return register_user_hw_breakpoint_cpu(addr, len, type, triggered,
204                                                -1, cpu, active);
205 }
206
207 /**
208  * register_wide_hw_breakpoint - register a wide breakpoint in the kernel
209  * @addr: is the memory address that triggers the breakpoint
210  * @len: the length of the access to the memory (1 byte, 2 bytes etc...)
211  * @type: the type of the access to the memory (read/write/exec)
212  * @triggered: callback to trigger when we hit the breakpoint
213  * @active: should we activate it while registering it
214  *
215  * @return a set of per_cpu pointers to perf events
216  */
217 struct perf_event **
218 register_wide_hw_breakpoint(unsigned long addr,
219                             int len,
220                             int type,
221                             perf_callback_t triggered,
222                             bool active)
223 {
224         struct perf_event **cpu_events, **pevent, *bp;
225         long err;
226         int cpu;
227
228         cpu_events = alloc_percpu(typeof(*cpu_events));
229         if (!cpu_events)
230                 return ERR_PTR(-ENOMEM);
231
232         for_each_possible_cpu(cpu) {
233                 pevent = per_cpu_ptr(cpu_events, cpu);
234                 bp = register_kernel_hw_breakpoint_cpu(addr, len, type,
235                                         triggered, cpu, active);
236
237                 *pevent = bp;
238
239                 if (IS_ERR(bp) || !bp) {
240                         err = PTR_ERR(bp);
241                         goto fail;
242                 }
243         }
244
245         return cpu_events;
246
247 fail:
248         for_each_possible_cpu(cpu) {
249                 pevent = per_cpu_ptr(cpu_events, cpu);
250                 if (IS_ERR(*pevent) || !*pevent)
251                         break;
252                 unregister_hw_breakpoint(*pevent);
253         }
254         free_percpu(cpu_events);
255         /* return the error if any */
256         return ERR_PTR(err);
257 }
258
259 /**
260  * unregister_wide_hw_breakpoint - unregister a wide breakpoint in the kernel
261  * @cpu_events: the per cpu set of events to unregister
262  */
263 void unregister_wide_hw_breakpoint(struct perf_event **cpu_events)
264 {
265         int cpu;
266         struct perf_event **pevent;
267
268         for_each_possible_cpu(cpu) {
269                 pevent = per_cpu_ptr(cpu_events, cpu);
270                 unregister_hw_breakpoint(*pevent);
271         }
272         free_percpu(cpu_events);
273 }
274
275
276 static struct notifier_block hw_breakpoint_exceptions_nb = {
277         .notifier_call = hw_breakpoint_exceptions_notify,
278         /* we need to be notified first */
279         .priority = 0x7fffffff
280 };
281
282 static int __init init_hw_breakpoint(void)
283 {
284         return register_die_notifier(&hw_breakpoint_exceptions_nb);
285 }
286 core_initcall(init_hw_breakpoint);
287
288
289 struct pmu perf_ops_bp = {
290         .enable         = arch_install_hw_breakpoint,
291         .disable        = arch_uninstall_hw_breakpoint,
292         .read           = hw_breakpoint_pmu_read,
293         .unthrottle     = hw_breakpoint_pmu_unthrottle
294 };