RE: [Fault-injection-developer] [RFC] kmmio mechanism
Status: Alpha
Brought to you by:
rustyl
From: Lynch, R. <rus...@in...> - 2002-12-18 17:08:58
|
I think this is looking very promising. Go ahead and add it to the tree. The only thing I ask is that if your code does not currently compile, then remove it from the Makefile until it does. When you have it working (even if it just limping along) then let the list know. I only casually looked at the code, but even with a casual glance it was obvious what you were trying to do. This is the way it should be. One problem I did notice was the way you marked a couple of your new files as coded by Vamsi Krishna. It is correct to give credit to the author of code you utilized, but you need to make it clear that you own the current file. Otherwise random people will stumble across this file and will submit patches/feedback/questions to Vamsi who will have no idea what they are talking about. -rusty > -----Original Message----- > From: Zhuang, Louis [mailto:lou...@in...] > Sent: Wednesday, December 18, 2002 4:09 AM > To: fau...@so... > Cc: Lynch, Rusty; Gao, Kevin; Wang, Frank; Wang, Stanley; > Zhuang, Louis > Subject: [Fault-injection-developer] [RFC] kmmio mechanism > > > > We'd like split instruction analysis code and general mmio > > probe code in pf > > module. That is, we'd like support a general mmio probe > > mechanism interface > > as the infrastructure of pf module. the interface is like > > > > struct kmmio_probe { > > kmmio_addr_t *addr; > > kmmio_pre_handler_t pre_handler; > > kmmio_post_handler_t post_handler; > > void *data; //opaque data structure > > } > > > > kmmio_probe_register(struct kmmio_probe *); > > kmmio_probe_unregister(struct kmmio_probe *); > > > > Then, pf module place probe on the address and analysis > instruction in > > pre/post_handler and feedback to fi_core. > > > I've developed a *very* primarily patch against our tip BK > tree. This is > only a compileable code, *NOT TEST AT ALL*. All comments? > - Louis > > -------------------------------------------------------------- > -------------- > ---------------- > # This is a BitKeeper generated patch for the following project: > # Project Name: Linux kernel tree > # This patch format is intended for GNU patch command version > 2.5 or higher. > # This patch includes the following deltas: > # ChangeSet 1.887 -> 1.888 > # arch/i386/mm/fault.c 1.23 -> 1.24 > # arch/i386/Kconfig 1.18 -> 1.19 > # arch/i386/kernel/traps.c 1.38 -> 1.39 > # arch/i386/kernel/Makefile 1.33 -> 1.34 > # kernel/Makefile 1.26 -> 1.27 > # (new) -> 1.2 include/linux/kmmio.h > # (new) -> 1.1 > arch/i386/kernel/kmmio.c > # (new) -> 1.2 kernel/kmmio.c > # (new) -> 1.1 > include/asm-i386/kmmio.h > # > # The following is the BitKeeper ChangeSet Log > # -------------------------------------------- > # 02/12/18 lo...@ha... 1.888 > # add kmmio mechanism. > # *NO TEST AT ALL* > # -------------------------------------------- > # > diff -Nru a/arch/i386/Kconfig b/arch/i386/Kconfig > --- a/arch/i386/Kconfig Wed Dec 18 20:02:43 2002 > +++ b/arch/i386/Kconfig Wed Dec 18 20:02:43 2002 > @@ -1506,6 +1506,13 @@ > 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" > + 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 FI > bool "Fault Injection (EXPERIMENTAL)" > diff -Nru a/arch/i386/kernel/Makefile b/arch/i386/kernel/Makefile > --- a/arch/i386/kernel/Makefile Wed Dec 18 20:02:43 2002 > +++ b/arch/i386/kernel/Makefile Wed Dec 18 20:02:43 2002 > @@ -31,6 +31,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 -Nru a/arch/i386/kernel/kmmio.c b/arch/i386/kernel/kmmio.c > --- /dev/null Wed Dec 31 16:00:00 1969 > +++ b/arch/i386/kernel/kmmio.c Wed Dec 18 20:02:43 2002 > @@ -0,0 +1,134 @@ > +/* > + * Support for kernel probes. > + * (C) 2002 Vamsi Krishna S <vam...@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> > + > +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; > + goto no_trigger; > + } > + > + current_kmmio = p; > + kmmio_saved_eflags = (regs->eflags & (TF_MASK|IF_MASK)); > + > + p->pre_handler(p, regs, addr); > + > +no_trigger: > + 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 > ); > + regs->eflags &= ~TF_MASK; > + regs->eflags |= kmmio_saved_eflags; > + > + 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(page, _PAGE_PRESENT); > +} > + > +void disarm_kmmio_fault_page(kmmio_addr_t page) > +{ > + (unsigned long)page &= PAGE_MASK; > + set_pte_bits(page, _PAGE_PRESENT); > +} > diff -Nru a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c > --- a/arch/i386/kernel/traps.c Wed Dec 18 20:02:43 2002 > +++ b/arch/i386/kernel/traps.c Wed Dec 18 20:02:43 2002 > @@ -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> > @@ -47,7 +48,6 @@ > #include <asm/smp.h> > #include <asm/pgalloc.h> > #include <asm/arch_hooks.h> > -#include <asm/fi.h> > > #include <linux/irq.h> > #include <linux/module.h> > @@ -599,7 +599,7 @@ > if (post_kprobe_handler(regs)) > return 1; > > - if (fi_post_page_fault(condition, regs)) > + if (post_kmmio_handler(condition, regs)) > return 1; > > /* Interrupts not disabled for normal trap handling. */ > diff -Nru a/arch/i386/mm/fault.c b/arch/i386/mm/fault.c > --- a/arch/i386/mm/fault.c Wed Dec 18 20:02:43 2002 > +++ b/arch/i386/mm/fault.c Wed Dec 18 20:02:43 2002 > @@ -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> > @@ -166,7 +167,7 @@ > if (kprobe_running() && kprobe_fault_handler(regs, 14)) > return; > > - if (fi_page_fault(regs, address)) > + if (is_kmmio_active() && kmmio_handler(regs, address)) > return; > > /* It's safe to allow irq's after cr2 has been saved */ > diff -Nru a/include/asm-i386/kmmio.h b/include/asm-i386/kmmio.h > --- /dev/null Wed Dec 31 16:00:00 1969 > +++ b/include/asm-i386/kmmio.h Wed Dec 18 20:02:43 2002 > @@ -0,0 +1,24 @@ > +#ifndef _ASM_KMMIO_H > +#define _ASM_KMMIO_H > +/* > + * Dynamic Probes (kprobes) support > + * Vamsi Krishna S <vam...@in...>, July, 2002 > + * Mailing list: dp...@ww... > + */ > +#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 -Nru a/include/linux/kmmio.h b/include/linux/kmmio.h > --- /dev/null Wed Dec 31 16:00:00 1969 > +++ b/include/linux/kmmio.h Wed Dec 18 20:02:43 2002 > @@ -0,0 +1,65 @@ > +#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; > + > + /* opaque data structure used by register */ > + void *data; > +}; > + > +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 -Nru a/kernel/Makefile b/kernel/Makefile > --- a/kernel/Makefile Wed Dec 18 20:02:43 2002 > +++ b/kernel/Makefile Wed Dec 18 20:02:43 2002 > @@ -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 > obj-$(CONFIG_FI) += fi_core.o > > ifneq ($(CONFIG_IA64),y) > diff -Nru a/kernel/kmmio.c b/kernel/kmmio.c > --- /dev/null Wed Dec 31 16:00:00 1969 > +++ b/kernel/kmmio.c Wed Dec 18 20:02:43 2002 > @@ -0,0 +1,141 @@ > +/* Support for MMIO probes. > + * Stolen 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> > + > +#define KMMIO_HASH_BITS 6 > +#define KMMIO_TABLE_SIZE (1 << KMMIO_HASH_BITS) > + > +static struct list_head kmmio_table[KMMIO_TABLE_SIZE]; > + > +#define KMMIO_FAULT_HASH_BITS 4 > +#define KMMIO_FAULT_HASH_SIZE (1 << KMMIO_FAULT_HASH_BITS) > +static struct list_head kmmio_fault_table[KMMIO_FAULT_HASH_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; > + > + kmmio_count++; > + spin_lock_irq(&kmmio_lock); > + 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); > + 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); > + spin_unlock_irq(&kmmio_lock); > + kmmio_count--; > +} > + > +struct kmmio_fault_page *get_kmmio_fault_page(kmmio_addr_t page) > +{ > + struct list_head *head, *tmp; > + > + (unsigned long)page &= PAGE_MASK; > + head = &kmmio_fault_table[hash_ptr(page, KMMIO_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_fault_table[hash_ptr(f->page, > KMMIO_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]); > + > + return 0; > +} > +__initcall(init_kmmio); > + > +EXPORT_SYMBOL_GPL(register_kmmio_probe); > +EXPORT_SYMBOL_GPL(unregister_kmmio_probe); > > |