[Fault-injection-developer] [PATCH] kmmio patch (based on kprobes)
Status: Alpha
Brought to you by:
rustyl
|
From: Zhuang, L. <lou...@in...> - 2002-12-21 15:08:30
|
Hi all
This is updated kmmio patch. Please comment ;->
- Louis
diff -Nur a/arch/i386/Kconfig b/arch/i386/Kconfig
--- a/arch/i386/Kconfig 2002-12-21 22:51:54.000000000 +0800
+++ b/arch/i386/Kconfig 2002-12-21 22:46:34.000000000 +0800
@@ -1506,6 +1506,14 @@
register_kprobe(), and providing a callback function. This is
useful
for kernel debugging, non-intrusive instrumentation and testing.
If
in doubt, say "N".
+config KMMIO
+ bool "KMMIO (EXPERIMENTAL)"
+ depends on EXPERIMENTAL
+ depends on KPROBES
+ help
+ KMMIO is a Kprobes add-ons for placing a probe on MMIO access,
using
+ register_kmmio(), and providing a callback function. This is
useful
+ for monitoring driver access specific MMIO address.
config DEBUG_SLAB
bool "Debug memory allocations"
diff -Nur a/arch/i386/kernel/Makefile b/arch/i386/kernel/Makefile
--- a/arch/i386/kernel/Makefile 2002-12-21 22:53:56.000000000 +0800
+++ b/arch/i386/kernel/Makefile 2002-12-21 22:48:11.000000000 +0800
@@ -30,6 +30,7 @@
obj-$(CONFIG_EDD) += edd.o
obj-$(CONFIG_MODULES) += module.o
obj-$(CONFIG_KPROBES) += kprobes.o
+obj-$(CONFIG_KMMIO) += kmmio.o
obj-y += sysenter.o
EXTRA_AFLAGS := -traditional
diff -Nur a/arch/i386/kernel/kmmio.c b/arch/i386/kernel/kmmio.c
--- a/arch/i386/kernel/kmmio.c 1970-01-01 08:00:00.000000000 +0800
+++ b/arch/i386/kernel/kmmio.c 2002-12-21 22:21:25.000000000 +0800
@@ -0,0 +1,140 @@
+/*
+ * KMMIO
+ * Benfit many code from kprobes
+ * (C) 2002 Louis Zhuang <lou...@in...>.
+ */
+
+#include <linux/config.h>
+#include <linux/kmmio.h>
+#include <linux/ptrace.h>
+#include <linux/spinlock.h>
+#include <linux/preempt.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <asm/io.h>
+#include <asm/highmem.h>
+
+static struct kmmio_probe *current_kmmio = NULL;
+static int is_trigger;
+static unsigned long kmmio_saved_eflags;
+/*
+ * Interrupts are disabled on entry as trap3 is an interrupt gate and they
+ * remain disabled thorough out this function.
+ */
+int kmmio_handler(struct pt_regs *regs, unsigned long addr)
+{
+ struct kmmio_probe *p;
+ struct kmmio_fault_page *f;
+
+ /* We're in an interrupt, but this is clear and BUG()-safe. */
+ preempt_disable();
+
+ lock_kmmio();
+
+ is_trigger = 1;
+ f = get_kmmio_fault_page((void *)addr);
+ if (!f) {
+ /* this page fault is not caused by kmmio */
+ /* XXX some pending fault on other cpu may cause problem! */
+ unlock_kmmio();
+ goto no_kmmio;
+ }
+
+ p = get_kmmio_probe((void *)addr);
+ if (!p) {
+ /* The fault is caused by kmmio, but no trigger care the
adress */
+ is_trigger = 0;
+ }
+
+ current_kmmio = p;
+ kmmio_saved_eflags = (regs->eflags & (TF_MASK|IF_MASK));
+
+ if (is_trigger) p->pre_handler(p, regs, addr);
+
+ regs->eflags |= TF_MASK;
+ regs->eflags &= ~IF_MASK;
+
+ /* We hold lock, now we set present bit in PTE and single step. */
+ disarm_kmmio_fault_page(f->page);
+
+
+ return 1;
+
+no_kmmio:
+ preempt_enable_no_resched();
+ return 0;
+}
+
+/*
+ * Interrupts are disabled on entry as trap1 is an interrupt gate and they
+ * remain disabled thorough out this function. And we hold kmmio lock.
+ */
+int post_kmmio_handler(unsigned long condition, struct pt_regs *regs)
+{
+ if (!is_kmmio_active())
+ return 0;
+ if (!current_kmmio)
+ return 0;
+
+ if (is_trigger && current_kmmio->post_handler)
+ current_kmmio->post_handler(current_kmmio, condition, regs);
+
+ arm_kmmio_fault_page(get_kmmio_fault_page(current_kmmio->addr)->page
);
+ __flush_tlb_one(get_kmmio_fault_page(current_kmmio->addr)->page);
+
+ regs->eflags &= ~TF_MASK;
+ regs->eflags |= kmmio_saved_eflags;
+
+ current_kmmio = NULL;
+
+ unlock_kmmio();
+ preempt_enable_no_resched();
+
+ /*
+ * if somebody else is singlestepping across a probe point, eflags
+ * will have TF set, in which case, continue the remaining
processing
+ * of do_debug, as if this is not a probe hit.
+ */
+ if (regs->eflags & TF_MASK)
+ return 0;
+
+ return 1;
+}
+
+static pte_t *get_pte(unsigned long address)
+{
+ pgd_t *pgd = pgd_offset_k(address);
+ pmd_t *pmd = pmd_offset(pgd, address);
+ if (pmd_large(*pmd))
+ return (pte_t *)pmd;
+ return pte_offset_kernel(pmd, address);
+};
+
+/**
+ * Set/Clear pte bits
+ */
+static void clr_pte_bits(unsigned long addr, unsigned long bitmask)
+{
+ pte_t *pte;
+ pte = get_pte(addr);
+ set_pte( pte, __pte( pte_val(*pte) & ~bitmask) );
+};
+
+static void set_pte_bits(unsigned long addr, unsigned long bitmask)
+{
+ pte_t *pte;
+ pte = get_pte(addr);
+ set_pte( pte, __pte( pte_val(*pte) | bitmask) );
+};
+
+void arm_kmmio_fault_page(kmmio_addr_t page)
+{
+ (unsigned long)page &= PAGE_MASK;
+ clr_pte_bits((unsigned long)page, _PAGE_PRESENT);
+}
+
+void disarm_kmmio_fault_page(kmmio_addr_t page)
+{
+ (unsigned long)page &= PAGE_MASK;
+ set_pte_bits((unsigned long)page, _PAGE_PRESENT);
+}
diff -Nur a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c
--- a/arch/i386/kernel/traps.c 2002-12-21 22:52:41.000000000 +0800
+++ b/arch/i386/kernel/traps.c 2002-12-21 22:20:35.000000000 +0800
@@ -25,6 +25,7 @@
#include <linux/highmem.h>
#include <linux/kallsyms.h>
#include <linux/kprobes.h>
+#include <linux/kmmio.h>
#ifdef CONFIG_EISA
#include <linux/ioport.h>
@@ -598,6 +599,9 @@
if (post_kprobe_handler(regs))
return 1;
+ if (post_kmmio_handler(condition, regs))
+ return 1;
+
/* Interrupts not disabled for normal trap handling. */
restore_interrupts(regs);
diff -Nur a/arch/i386/mm/fault.c b/arch/i386/mm/fault.c
--- a/arch/i386/mm/fault.c 2002-12-21 22:54:30.000000000 +0800
+++ b/arch/i386/mm/fault.c 2002-12-21 22:20:11.000000000 +0800
@@ -20,6 +20,7 @@
#include <linux/tty.h>
#include <linux/vt_kern.h> /* For unblank_screen() */
#include <linux/kprobes.h>
+#include <linux/kmmio.h>
#include <asm/system.h>
#include <asm/uaccess.h>
@@ -165,6 +166,9 @@
if (kprobe_running() && kprobe_fault_handler(regs, 14))
return;
+ if (is_kmmio_active() && kmmio_handler(regs, address))
+ return;
+
/* It's safe to allow irq's after cr2 has been saved */
if (regs->eflags & X86_EFLAGS_IF)
local_irq_enable();
diff -Nur a/include/asm-i386/kmmio.h b/include/asm-i386/kmmio.h
--- a/include/asm-i386/kmmio.h 1970-01-01 08:00:00.000000000 +0800
+++ b/include/asm-i386/kmmio.h 2002-12-21 22:22:19.000000000 +0800
@@ -0,0 +1,24 @@
+/*
+ * Benfit many code from kprobes
+ * (C) 2002 Louis Zhuang <lou...@in...>.
+ */
+
+#ifndef _ASM_KMMIO_H
+#define _ASM_KMMIO_H
+#include <linux/types.h>
+#include <linux/ptrace.h>
+
+struct pt_regs;
+
+typedef void* kmmio_addr_t;
+
+#ifdef CONFIG_KMMIO
+extern void arm_kmmio_fault_page(kmmio_addr_t page);
+extern void disarm_kmmio_fault_page(kmmio_addr_t page);
+extern int post_kmmio_handler(unsigned long condition, struct pt_regs
*regs);
+extern int kmmio_handler(struct pt_regs *regs, unsigned long addr);
+#else /* !CONFIG_KMMIO */
+static inline int post_kmmio_handler(unsigned long condition, struct
pt_regs *regs) { return 0; }
+static inline int kmmio_handler(struct pt_regs *regs, unsigned long addr) {
return 0; }
+#endif
+#endif /* _ASM_KMMIO_H */
diff -Nur a/include/linux/kmmio.h b/include/linux/kmmio.h
--- a/include/linux/kmmio.h 1970-01-01 08:00:00.000000000 +0800
+++ b/include/linux/kmmio.h 2002-12-21 22:20:52.000000000 +0800
@@ -0,0 +1,62 @@
+#ifndef _LINUX_KMMIO_H
+#define _LINUX_KMMIO_H
+#include <linux/config.h>
+#include <linux/list.h>
+#include <linux/notifier.h>
+#include <linux/smp.h>
+#include <asm/kmmio.h>
+
+struct kmmio_probe;
+struct kmmio_fault_page;
+struct pt_regs;
+
+typedef void (*kmmio_pre_handler_t)(struct kmmio_probe *, struct pt_regs *,
unsigned long addr);
+typedef void (*kmmio_post_handler_t)(struct kmmio_probe *, unsigned long
condition, struct pt_regs *);
+struct kmmio_probe {
+ struct list_head list;
+
+ /* location of the probe point */
+ kmmio_addr_t addr;
+
+ /* Called before addr is executed. */
+ kmmio_pre_handler_t pre_handler;
+
+ /* Called after addr is executed, unless... */
+ kmmio_post_handler_t post_handler;
+};
+
+struct kmmio_fault_page {
+ struct list_head list;
+
+ /* location of the fault page */
+ kmmio_addr_t page;
+
+ int count;
+};
+
+#ifdef CONFIG_KMMIO
+/* Locks kmmio: irq must be disabled */
+void lock_kmmio(void);
+void unlock_kmmio(void);
+
+/* kmmio is active by some kmmio_probes? */
+static inline int is_kmmio_active(void)
+{
+ extern unsigned int kmmio_count;
+ return kmmio_count;
+}
+
+/* Get the kmmio at this addr (if any). Must have called lock_kmmio */
+struct kmmio_probe *get_kmmio_probe(kmmio_addr_t addr);
+struct kmmio_fault_page *get_kmmio_fault_page(kmmio_addr_t page);
+int add_kmmio_fault_page(kmmio_addr_t page);
+void release_kmmio_fault_page(kmmio_addr_t page);
+
+int register_kmmio_probe(struct kmmio_probe *p);
+void unregister_kmmio_probe(struct kmmio_probe *p);
+#else
+static inline int is_kmmio_active(void) { return 0; }
+static inline int register_kmmio_probe(struct kmmio_probe *p) { return
-ENOSYS; }
+static inline void unregister_kmmio_probe(struct kmmio_probe *p) { }
+#endif
+#endif /* _LINUX_KMMIO_H */
diff -Nur a/kernel/Makefile b/kernel/Makefile
--- a/kernel/Makefile 2002-12-21 22:54:51.000000000 +0800
+++ b/kernel/Makefile 2002-12-21 22:20:36.000000000 +0800
@@ -23,6 +23,7 @@
obj-$(CONFIG_SOFTWARE_SUSPEND) += suspend.o
obj-$(CONFIG_COMPAT) += compat.o
obj-$(CONFIG_KPROBES) += kprobes.o
+obj-$(CONFIG_KMMIO) += kmmio.o
ifneq ($(CONFIG_IA64),y)
# According to Alan Modra <al...@li...>, the
-fno-omit-frame-pointer is
diff -Nur a/kernel/kmmio.c b/kernel/kmmio.c
--- a/kernel/kmmio.c 1970-01-01 08:00:00.000000000 +0800
+++ b/kernel/kmmio.c 2002-12-21 22:21:57.000000000 +0800
@@ -0,0 +1,144 @@
+/* Support for MMIO probes.
+ * Benfit many code from kprobes
+ * (C) 2002 Louis Zhuang <lou...@in...>.
+*/
+
+#include <linux/kmmio.h>
+#include <linux/spinlock.h>
+#include <linux/hash.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <asm/cacheflush.h>
+#include <asm/errno.h>
+#include <asm/highmem.h>
+
+#define KMMIO_HASH_BITS 6
+#define KMMIO_TABLE_SIZE (1 << KMMIO_HASH_BITS)
+
+static struct list_head kmmio_table[KMMIO_TABLE_SIZE];
+
+#define KMMIO_PAGE_HASH_BITS 4
+#define KMMIO_PAGE_TABLE_SIZE (1 << KMMIO_PAGE_HASH_BITS)
+static struct list_head kmmio_page_table[KMMIO_PAGE_TABLE_SIZE];
+
+unsigned int kmmio_count = 0;
+static spinlock_t kmmio_lock = SPIN_LOCK_UNLOCKED;
+
+/* Locks kmmio: irqs must be disabled */
+void lock_kmmio(void)
+{
+ spin_lock(&kmmio_lock);
+}
+
+void unlock_kmmio(void)
+{
+ spin_unlock(&kmmio_lock);
+}
+
+/* You have to be holding the kmmio_lock */
+struct kmmio_probe *get_kmmio_probe(void *addr)
+{
+ struct list_head *head, *tmp;
+
+ head = &kmmio_table[hash_ptr(addr, KMMIO_HASH_BITS)];
+ list_for_each(tmp, head) {
+ struct kmmio_probe *p = list_entry(tmp, struct kmmio_probe,
list);
+ if (p->addr == addr)
+ return p;
+ }
+ return NULL;
+}
+
+int register_kmmio_probe(struct kmmio_probe *p)
+{
+ int ret = 0;
+
+ spin_lock_irq(&kmmio_lock);
+ kmmio_count++;
+ if (get_kmmio_probe(p->addr)) {
+ ret = -EEXIST;
+ goto out;
+ }
+ list_add(&p->list, &kmmio_table[hash_ptr(p->addr,
KMMIO_HASH_BITS)]);
+
+ add_kmmio_fault_page(p->addr);
+
+ out:
+ spin_unlock_irq(&kmmio_lock);
+ flush_tlb_all();
+ return ret;
+}
+
+void unregister_kmmio_probe(struct kmmio_probe *p)
+{
+ spin_lock_irq(&kmmio_lock);
+ release_kmmio_fault_page(p->addr);
+ list_del(&p->list);
+ kmmio_count--;
+ spin_unlock_irq(&kmmio_lock);
+}
+
+struct kmmio_fault_page *get_kmmio_fault_page(kmmio_addr_t page)
+{
+ struct list_head *head, *tmp;
+
+ (unsigned long)page &= PAGE_MASK;
+ head = &kmmio_page_table[hash_ptr(page, KMMIO_PAGE_HASH_BITS)];
+ list_for_each(tmp, head) {
+ struct kmmio_fault_page *p = list_entry(tmp, struct
kmmio_fault_page, list);
+ if (p->page == page)
+ return p;
+ }
+ return NULL;
+}
+
+int add_kmmio_fault_page(kmmio_addr_t page)
+{
+ struct kmmio_fault_page *f;
+
+ (unsigned long)page &= PAGE_MASK;
+ f = get_kmmio_fault_page(page);
+ if (f) {
+ f->count++;
+ return 0;
+ }
+ f = (struct kmmio_fault_page *)kmalloc(sizeof(*f), GFP_KERNEL);
+ f->count = 1;
+ f->page = page;
+ list_add(&f->list, &kmmio_page_table[hash_ptr(f->page,
KMMIO_PAGE_HASH_BITS)]);
+
+ arm_kmmio_fault_page(f->page);
+ return 0;
+}
+
+void release_kmmio_fault_page(kmmio_addr_t page)
+{
+ struct kmmio_fault_page *f;
+
+ (unsigned long)page &= PAGE_MASK;
+ f = get_kmmio_fault_page(page);
+ if (!f) return;
+ f->count--;
+ if(!f->count) {
+ disarm_kmmio_fault_page(f->page);
+ list_del(&f->list);
+ }
+}
+
+static int __init init_kmmio(void)
+{
+ int i;
+
+ /* FIXME allocate the probe table, currently defined statically */
+ /* initialize all list heads */
+ for (i = 0; i < KMMIO_TABLE_SIZE; i++)
+ INIT_LIST_HEAD(&kmmio_table[i]);
+ for (i = 0; i < KMMIO_PAGE_TABLE_SIZE; i++)
+ INIT_LIST_HEAD(&kmmio_page_table[i]);
+ return 0;
+}
+__initcall(init_kmmio);
+
+EXPORT_SYMBOL_GPL(register_kmmio_probe);
+EXPORT_SYMBOL_GPL(unregister_kmmio_probe);
|