diff options
-rw-r--r-- | arch/x86/Kconfig.debug | 8 | ||||
-rw-r--r-- | arch/x86/mm/fault.c | 56 | ||||
-rw-r--r-- | include/asm-x86/kdebug.h | 9 |
3 files changed, 73 insertions, 0 deletions
diff --git a/arch/x86/Kconfig.debug b/arch/x86/Kconfig.debug index ac1e31ba4795..9431a8399844 100644 --- a/arch/x86/Kconfig.debug +++ b/arch/x86/Kconfig.debug | |||
@@ -168,6 +168,14 @@ config IOMMU_LEAK | |||
168 | Add a simple leak tracer to the IOMMU code. This is useful when you | 168 | Add a simple leak tracer to the IOMMU code. This is useful when you |
169 | are debugging a buggy device driver that leaks IOMMU mappings. | 169 | are debugging a buggy device driver that leaks IOMMU mappings. |
170 | 170 | ||
171 | config PAGE_FAULT_HANDLERS | ||
172 | bool "Custom page fault handlers" | ||
173 | depends on DEBUG_KERNEL | ||
174 | help | ||
175 | Allow the use of custom page fault handlers. A kernel module may | ||
176 | register a function that is called on every page fault. Custom | ||
177 | handlers are used by some debugging and reverse engineering tools. | ||
178 | |||
171 | # | 179 | # |
172 | # IO delay types: | 180 | # IO delay types: |
173 | # | 181 | # |
diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c index fd7e1798c75a..343f5c1aacc8 100644 --- a/arch/x86/mm/fault.c +++ b/arch/x86/mm/fault.c | |||
@@ -49,6 +49,60 @@ | |||
49 | #define PF_RSVD (1<<3) | 49 | #define PF_RSVD (1<<3) |
50 | #define PF_INSTR (1<<4) | 50 | #define PF_INSTR (1<<4) |
51 | 51 | ||
52 | #ifdef CONFIG_PAGE_FAULT_HANDLERS | ||
53 | static HLIST_HEAD(pf_handlers); /* protected by RCU */ | ||
54 | static DEFINE_SPINLOCK(pf_handlers_writer); | ||
55 | |||
56 | void register_page_fault_handler(struct pf_handler *new_pfh) | ||
57 | { | ||
58 | unsigned long flags; | ||
59 | spin_lock_irqsave(&pf_handlers_writer, flags); | ||
60 | hlist_add_head_rcu(&new_pfh->hlist, &pf_handlers); | ||
61 | spin_unlock_irqrestore(&pf_handlers_writer, flags); | ||
62 | } | ||
63 | EXPORT_SYMBOL_GPL(register_page_fault_handler); | ||
64 | |||
65 | /** | ||
66 | * unregister_page_fault_handler: | ||
67 | * The caller must ensure @old_pfh is not in use anymore before freeing it. | ||
68 | * This function does not guarantee it. The list of handlers is protected by | ||
69 | * RCU, so you can do this by e.g. calling synchronize_rcu(). | ||
70 | */ | ||
71 | void unregister_page_fault_handler(struct pf_handler *old_pfh) | ||
72 | { | ||
73 | unsigned long flags; | ||
74 | spin_lock_irqsave(&pf_handlers_writer, flags); | ||
75 | hlist_del_rcu(&old_pfh->hlist); | ||
76 | spin_unlock_irqrestore(&pf_handlers_writer, flags); | ||
77 | } | ||
78 | EXPORT_SYMBOL_GPL(unregister_page_fault_handler); | ||
79 | #endif | ||
80 | |||
81 | /* returns non-zero if do_page_fault() should return */ | ||
82 | static int handle_custom_pf(struct pt_regs *regs, unsigned long error_code, | ||
83 | unsigned long address) | ||
84 | { | ||
85 | #ifdef CONFIG_PAGE_FAULT_HANDLERS | ||
86 | int ret = 0; | ||
87 | struct pf_handler *cur; | ||
88 | struct hlist_node *ncur; | ||
89 | |||
90 | if (hlist_empty(&pf_handlers)) | ||
91 | return 0; | ||
92 | |||
93 | rcu_read_lock(); | ||
94 | hlist_for_each_entry_rcu(cur, ncur, &pf_handlers, hlist) { | ||
95 | ret = cur->handler(regs, error_code, address); | ||
96 | if (ret) | ||
97 | break; | ||
98 | } | ||
99 | rcu_read_unlock(); | ||
100 | return ret; | ||
101 | #else | ||
102 | return 0; | ||
103 | #endif | ||
104 | } | ||
105 | |||
52 | static inline int notify_page_fault(struct pt_regs *regs) | 106 | static inline int notify_page_fault(struct pt_regs *regs) |
53 | { | 107 | { |
54 | #ifdef CONFIG_KPROBES | 108 | #ifdef CONFIG_KPROBES |
@@ -601,6 +655,8 @@ void __kprobes do_page_fault(struct pt_regs *regs, unsigned long error_code) | |||
601 | 655 | ||
602 | if (notify_page_fault(regs)) | 656 | if (notify_page_fault(regs)) |
603 | return; | 657 | return; |
658 | if (handle_custom_pf(regs, error_code, address)) | ||
659 | return; | ||
604 | 660 | ||
605 | /* | 661 | /* |
606 | * We fault-in kernel-space virtual memory on-demand. The | 662 | * We fault-in kernel-space virtual memory on-demand. The |
diff --git a/include/asm-x86/kdebug.h b/include/asm-x86/kdebug.h index 96651bb59ba1..a80f2d6cc737 100644 --- a/include/asm-x86/kdebug.h +++ b/include/asm-x86/kdebug.h | |||
@@ -35,4 +35,13 @@ extern void show_regs(struct pt_regs *regs); | |||
35 | extern unsigned long oops_begin(void); | 35 | extern unsigned long oops_begin(void); |
36 | extern void oops_end(unsigned long, struct pt_regs *, int signr); | 36 | extern void oops_end(unsigned long, struct pt_regs *, int signr); |
37 | 37 | ||
38 | struct pf_handler { | ||
39 | struct hlist_node hlist; | ||
40 | int (*handler)(struct pt_regs *regs, unsigned long error_code, | ||
41 | unsigned long address); | ||
42 | }; | ||
43 | |||
44 | extern void register_page_fault_handler(struct pf_handler *new_pfh); | ||
45 | extern void unregister_page_fault_handler(struct pf_handler *old_pfh); | ||
46 | |||
38 | #endif | 47 | #endif |