aboutsummaryrefslogtreecommitdiffstats
path: root/arch/ia64/kernel
diff options
context:
space:
mode:
authorGreg KH <greg@press.(none)>2005-06-28 01:07:56 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2005-06-28 01:07:56 -0400
commit8644d2a42bdba2d513f71c07eaf1b6f9b718b8eb (patch)
treec43b6c2fdf1b68b66906a2de69446dcec0f9af6b /arch/ia64/kernel
parent1cde8a16815bd85c8137d1ea556398983c597c11 (diff)
parent99f95e5286df2f69edab8a04c7080d986ee4233b (diff)
Merge rsync://rsync.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
Diffstat (limited to 'arch/ia64/kernel')
-rw-r--r--arch/ia64/kernel/entry.S4
-rw-r--r--arch/ia64/kernel/kprobes.c128
-rw-r--r--arch/ia64/kernel/process.c16
-rw-r--r--arch/ia64/kernel/vmlinux.lds.S7
4 files changed, 148 insertions, 7 deletions
diff --git a/arch/ia64/kernel/entry.S b/arch/ia64/kernel/entry.S
index b1d5d3d5276c..785a51b0ad8e 100644
--- a/arch/ia64/kernel/entry.S
+++ b/arch/ia64/kernel/entry.S
@@ -1577,8 +1577,8 @@ sys_call_table:
1577 data8 sys_add_key 1577 data8 sys_add_key
1578 data8 sys_request_key 1578 data8 sys_request_key
1579 data8 sys_keyctl 1579 data8 sys_keyctl
1580 data8 sys_ni_syscall 1580 data8 sys_ioprio_set
1581 data8 sys_ni_syscall // 1275 1581 data8 sys_ioprio_get // 1275
1582 data8 sys_set_zone_reclaim 1582 data8 sys_set_zone_reclaim
1583 data8 sys_ni_syscall 1583 data8 sys_ni_syscall
1584 data8 sys_ni_syscall 1584 data8 sys_ni_syscall
diff --git a/arch/ia64/kernel/kprobes.c b/arch/ia64/kernel/kprobes.c
index 5978823d5c63..3aa3167edbec 100644
--- a/arch/ia64/kernel/kprobes.c
+++ b/arch/ia64/kernel/kprobes.c
@@ -34,6 +34,7 @@
34 34
35#include <asm/pgtable.h> 35#include <asm/pgtable.h>
36#include <asm/kdebug.h> 36#include <asm/kdebug.h>
37#include <asm/sections.h>
37 38
38extern void jprobe_inst_return(void); 39extern void jprobe_inst_return(void);
39 40
@@ -263,13 +264,33 @@ static inline void get_kprobe_inst(bundle_t *bundle, uint slot,
263 } 264 }
264} 265}
265 266
267/* Returns non-zero if the addr is in the Interrupt Vector Table */
268static inline int in_ivt_functions(unsigned long addr)
269{
270 return (addr >= (unsigned long)__start_ivt_text
271 && addr < (unsigned long)__end_ivt_text);
272}
273
266static int valid_kprobe_addr(int template, int slot, unsigned long addr) 274static int valid_kprobe_addr(int template, int slot, unsigned long addr)
267{ 275{
268 if ((slot > 2) || ((bundle_encoding[template][1] == L) && slot > 1)) { 276 if ((slot > 2) || ((bundle_encoding[template][1] == L) && slot > 1)) {
269 printk(KERN_WARNING "Attempting to insert unaligned kprobe at 0x%lx\n", 277 printk(KERN_WARNING "Attempting to insert unaligned kprobe "
270 addr); 278 "at 0x%lx\n", addr);
271 return -EINVAL; 279 return -EINVAL;
272 } 280 }
281
282 if (in_ivt_functions(addr)) {
283 printk(KERN_WARNING "Kprobes can't be inserted inside "
284 "IVT functions at 0x%lx\n", addr);
285 return -EINVAL;
286 }
287
288 if (slot == 1 && bundle_encoding[template][1] != L) {
289 printk(KERN_WARNING "Inserting kprobes on slot #1 "
290 "is not supported\n");
291 return -EINVAL;
292 }
293
273 return 0; 294 return 0;
274} 295}
275 296
@@ -290,6 +311,94 @@ static inline void set_current_kprobe(struct kprobe *p)
290 current_kprobe = p; 311 current_kprobe = p;
291} 312}
292 313
314static void kretprobe_trampoline(void)
315{
316}
317
318/*
319 * At this point the target function has been tricked into
320 * returning into our trampoline. Lookup the associated instance
321 * and then:
322 * - call the handler function
323 * - cleanup by marking the instance as unused
324 * - long jump back to the original return address
325 */
326int trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs)
327{
328 struct kretprobe_instance *ri = NULL;
329 struct hlist_head *head;
330 struct hlist_node *node, *tmp;
331 unsigned long orig_ret_address = 0;
332 unsigned long trampoline_address =
333 ((struct fnptr *)kretprobe_trampoline)->ip;
334
335 head = kretprobe_inst_table_head(current);
336
337 /*
338 * It is possible to have multiple instances associated with a given
339 * task either because an multiple functions in the call path
340 * have a return probe installed on them, and/or more then one return
341 * return probe was registered for a target function.
342 *
343 * We can handle this because:
344 * - instances are always inserted at the head of the list
345 * - when multiple return probes are registered for the same
346 * function, the first instance's ret_addr will point to the
347 * real return address, and all the rest will point to
348 * kretprobe_trampoline
349 */
350 hlist_for_each_entry_safe(ri, node, tmp, head, hlist) {
351 if (ri->task != current)
352 /* another task is sharing our hash bucket */
353 continue;
354
355 if (ri->rp && ri->rp->handler)
356 ri->rp->handler(ri, regs);
357
358 orig_ret_address = (unsigned long)ri->ret_addr;
359 recycle_rp_inst(ri);
360
361 if (orig_ret_address != trampoline_address)
362 /*
363 * This is the real return address. Any other
364 * instances associated with this task are for
365 * other calls deeper on the call stack
366 */
367 break;
368 }
369
370 BUG_ON(!orig_ret_address || (orig_ret_address == trampoline_address));
371 regs->cr_iip = orig_ret_address;
372
373 unlock_kprobes();
374 preempt_enable_no_resched();
375
376 /*
377 * By returning a non-zero value, we are telling
378 * kprobe_handler() that we have handled unlocking
379 * and re-enabling preemption.
380 */
381 return 1;
382}
383
384void arch_prepare_kretprobe(struct kretprobe *rp, struct pt_regs *regs)
385{
386 struct kretprobe_instance *ri;
387
388 if ((ri = get_free_rp_inst(rp)) != NULL) {
389 ri->rp = rp;
390 ri->task = current;
391 ri->ret_addr = (kprobe_opcode_t *)regs->b0;
392
393 /* Replace the return addr with trampoline addr */
394 regs->b0 = ((struct fnptr *)kretprobe_trampoline)->ip;
395
396 add_rp_inst(ri);
397 } else {
398 rp->nmissed++;
399 }
400}
401
293int arch_prepare_kprobe(struct kprobe *p) 402int arch_prepare_kprobe(struct kprobe *p)
294{ 403{
295 unsigned long addr = (unsigned long) p->addr; 404 unsigned long addr = (unsigned long) p->addr;
@@ -492,8 +601,8 @@ static int pre_kprobes_handler(struct die_args *args)
492 if (p->pre_handler && p->pre_handler(p, regs)) 601 if (p->pre_handler && p->pre_handler(p, regs))
493 /* 602 /*
494 * Our pre-handler is specifically requesting that we just 603 * Our pre-handler is specifically requesting that we just
495 * do a return. This is handling the case where the 604 * do a return. This is used for both the jprobe pre-handler
496 * pre-handler is really our special jprobe pre-handler. 605 * and the kretprobe trampoline
497 */ 606 */
498 return 1; 607 return 1;
499 608
@@ -599,3 +708,14 @@ int longjmp_break_handler(struct kprobe *p, struct pt_regs *regs)
599 *regs = jprobe_saved_regs; 708 *regs = jprobe_saved_regs;
600 return 1; 709 return 1;
601} 710}
711
712static struct kprobe trampoline_p = {
713 .pre_handler = trampoline_probe_handler
714};
715
716int __init arch_init(void)
717{
718 trampoline_p.addr =
719 (kprobe_opcode_t *)((struct fnptr *)kretprobe_trampoline)->ip;
720 return register_kprobe(&trampoline_p);
721}
diff --git a/arch/ia64/kernel/process.c b/arch/ia64/kernel/process.c
index ebb71f3d6d19..6e35bff05d59 100644
--- a/arch/ia64/kernel/process.c
+++ b/arch/ia64/kernel/process.c
@@ -27,6 +27,7 @@
27#include <linux/efi.h> 27#include <linux/efi.h>
28#include <linux/interrupt.h> 28#include <linux/interrupt.h>
29#include <linux/delay.h> 29#include <linux/delay.h>
30#include <linux/kprobes.h>
30 31
31#include <asm/cpu.h> 32#include <asm/cpu.h>
32#include <asm/delay.h> 33#include <asm/delay.h>
@@ -707,6 +708,13 @@ kernel_thread_helper (int (*fn)(void *), void *arg)
707void 708void
708flush_thread (void) 709flush_thread (void)
709{ 710{
711 /*
712 * Remove function-return probe instances associated with this task
713 * and put them back on the free list. Do not insert an exit probe for
714 * this function, it will be disabled by kprobe_flush_task if you do.
715 */
716 kprobe_flush_task(current);
717
710 /* drop floating-point and debug-register state if it exists: */ 718 /* drop floating-point and debug-register state if it exists: */
711 current->thread.flags &= ~(IA64_THREAD_FPH_VALID | IA64_THREAD_DBG_VALID); 719 current->thread.flags &= ~(IA64_THREAD_FPH_VALID | IA64_THREAD_DBG_VALID);
712 ia64_drop_fpu(current); 720 ia64_drop_fpu(current);
@@ -721,6 +729,14 @@ flush_thread (void)
721void 729void
722exit_thread (void) 730exit_thread (void)
723{ 731{
732
733 /*
734 * Remove function-return probe instances associated with this task
735 * and put them back on the free list. Do not insert an exit probe for
736 * this function, it will be disabled by kprobe_flush_task if you do.
737 */
738 kprobe_flush_task(current);
739
724 ia64_drop_fpu(current); 740 ia64_drop_fpu(current);
725#ifdef CONFIG_PERFMON 741#ifdef CONFIG_PERFMON
726 /* if needed, stop monitoring and flush state to perfmon context */ 742 /* if needed, stop monitoring and flush state to perfmon context */
diff --git a/arch/ia64/kernel/vmlinux.lds.S b/arch/ia64/kernel/vmlinux.lds.S
index b9f0db4c1b04..a676e79e0681 100644
--- a/arch/ia64/kernel/vmlinux.lds.S
+++ b/arch/ia64/kernel/vmlinux.lds.S
@@ -8,6 +8,11 @@
8#define LOAD_OFFSET (KERNEL_START - KERNEL_TR_PAGE_SIZE) 8#define LOAD_OFFSET (KERNEL_START - KERNEL_TR_PAGE_SIZE)
9#include <asm-generic/vmlinux.lds.h> 9#include <asm-generic/vmlinux.lds.h>
10 10
11#define IVT_TEXT \
12 VMLINUX_SYMBOL(__start_ivt_text) = .; \
13 *(.text.ivt) \
14 VMLINUX_SYMBOL(__end_ivt_text) = .;
15
11OUTPUT_FORMAT("elf64-ia64-little") 16OUTPUT_FORMAT("elf64-ia64-little")
12OUTPUT_ARCH(ia64) 17OUTPUT_ARCH(ia64)
13ENTRY(phys_start) 18ENTRY(phys_start)
@@ -39,7 +44,7 @@ SECTIONS
39 44
40 .text : AT(ADDR(.text) - LOAD_OFFSET) 45 .text : AT(ADDR(.text) - LOAD_OFFSET)
41 { 46 {
42 *(.text.ivt) 47 IVT_TEXT
43 *(.text) 48 *(.text)
44 SCHED_TEXT 49 SCHED_TEXT
45 LOCK_TEXT 50 LOCK_TEXT