Thread: [Fault-injection-developer] (no subject)
Status: Alpha
Brought to you by:
rustyl
From: Wang, S. <sta...@in...> - 2002-11-12 06:12:57
|
I've splitted the FITH into two parts: fith_core and fith_utility. Please check the updated code. Thanks. Your Sincerely, Stanley Wang SW Engineer, Intel Corporation. Intel China Software Lab. Tel: 021-52574545 ext. 1171 iNet: 8-752-1171 Opinions expressed are those of the author and do not represent Intel Corporation |
From: Louis Z. <lou...@li...> - 2003-01-15 01:35:57
|
Because of Linus' and my :-p long vacation, the patch against 2.5.58 is here. Please take a look at it and use it. -- Yours truly, Louis Zhuang --------------- Fault Injection Test Harness Project BK tree: http://fault-injection.bkbits.net/linux-2.5 Home Page: http://sf.net/projects/fault-injection REPORTING-BUGS | 2 arch/i386/Kconfig | 163 +++++ arch/i386/kernel/Makefile | 5 arch/i386/kernel/entry.S | 22 arch/i386/kernel/kirq.c | 123 ++++ arch/i386/kernel/kmmio.c | 159 +++++ arch/i386/kernel/kprobes.c | 160 +++++ arch/i386/kernel/traps.c | 40 + arch/i386/mm/fault.c | 8 drivers/Makefile | 2 drivers/fi/Makefile | 11 drivers/fi/README | 181 +++++ drivers/fi/codesegments/Makefile | 7 drivers/fi/codesegments/fi_sample_cs.c | 76 ++ drivers/fi/fi_core.c | 941 +++++++++++++++++++++++++++++++ drivers/fi/interceptors/Makefile | 9 drivers/fi/interceptors/dbp/Makefile | 9 drivers/fi/interceptors/dbp/fi_dbp.c | 556 ++++++++++++++++++ drivers/fi/interceptors/fi_irq.c | 239 +++++++ drivers/fi/interceptors/pf/Makefile | 10 drivers/fi/interceptors/pf/fi_pf.h | 43 + drivers/fi/interceptors/pf/pf.c | 269 ++++++++ drivers/fi/interceptors/pf/pf_in.c | 446 ++++++++++++++ drivers/fi/interceptors/pf/pf_utils.c | 63 ++ drivers/fi/testing/Makefile | 9 drivers/fi/testing/fi_mock_cs.c | 41 + drivers/fi/testing/fi_mock_interceptor.c | 121 +++ drivers/fi/testing/fi_test.c | 304 ++++++++++ include/asm-i386/kirq.h | 27 include/asm-i386/kmmio.h | 33 + include/asm-i386/kprobes.h | 34 + include/linux/fi.h | 158 +++++ include/linux/kmmio.h | 67 ++ include/linux/kprobes.h | 60 + kernel/Makefile | 6 kernel/kmmio.c | 149 ++++ kernel/kprobes.c | 89 ++ 37 files changed, 4628 insertions(+), 14 deletions(-) # 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.1024 -> 1.1025 # arch/i386/mm/fault.c 1.20.1.3 -> 1.27 # arch/i386/Kconfig 1.12.2.19 -> 1.36 # arch/i386/kernel/traps.c 1.36.1.6 -> 1.46 # arch/i386/kernel/Makefile 1.29.2.1 -> 1.39 # kernel/Makefile 1.22.1.3 -> 1.31 # arch/i386/kernel/entry.S 1.41.1.11 -> 1.48 # REPORTING-BUGS 1.2 -> 1.3 # kernel/module.c 1.32.1.17 -> 1.43 # include/linux/module.h 1.37.1.2 -> 1.40 # drivers/Makefile 1.29 -> 1.30 # lib/vsprintf.c 1.12.1.1 -> 1.14 # drivers/char/Makefile 1.50.1.2 -> 1.58 # arch/i386/kernel/i386_ksyms.c 1.40.1.2 -> 1.44 # (new) -> 1.1 kernel/kprobes.c # (new) -> 1.3 drivers/fi/interceptors/dbp/Makefile # (new) -> 1.8 drivers/fi/codesegments/fi_sample_cs.c # (new) -> 1.11 drivers/fi/interceptors/fi_irq.c # (new) -> 1.1 arch/i386/kernel/kprobes.c # (new) -> 1.25 drivers/fi/interceptors/pf/pf.c # (new) -> 1.4 drivers/fi/README # (new) -> 1.13 drivers/fi/Makefile # (new) -> 1.4 include/linux/kmmio.h # (new) -> 1.1 drivers/fi/testing/Makefile # (new) -> 1.42 drivers/fi/fi_core.c # (new) -> 1.7 drivers/fi/interceptors/pf/Makefile # (new) -> 1.1 drivers/fi/interceptors/Makefile # (new) -> 1.9 drivers/fi/interceptors/pf/pf_in.c # (new) -> 1.8 arch/i386/kernel/kmmio.c # (new) -> 1.1 include/asm-i386/kprobes.h # (new) -> 1.14 drivers/fi/interceptors/dbp/fi_dbp.c # (new) -> 1.1 include/linux/kprobes.h # (new) -> 1.8 kernel/kmmio.c # (new) -> 1.15 include/linux/fi.h # (new) -> 1.16 drivers/fi/testing/fi_test.c # (new) -> 1.9 drivers/fi/interceptors/pf/pf_utils.c # (new) -> 1.7 arch/i386/kernel/kirq.c # (new) -> 1.6 drivers/fi/testing/fi_mock_cs.c # (new) -> 1.13 drivers/fi/testing/fi_mock_interceptor.c # (new) -> 1.6 include/asm-i386/kirq.h # (new) -> 1.1 drivers/fi/codesegments/Makefile # (new) -> 1.4 include/asm-i386/kmmio.h # (new) -> 1.9 drivers/fi/interceptors/pf/fi_pf.h # # The following is the BitKeeper ChangeSet Log # -------------------------------------------- # 03/01/14 lo...@ha... 1.1017.1.2 # Trivially code clean. Make all but kprobes 80 cloumn width for patch. # -------------------------------------------- # 03/01/15 lo...@ha... 1.1025 # Merge hawk.sh.intel.com:/home/work/linux-2.5 # into hawk.sh.intel.com:/home/louis/2.5-fi # -------------------------------------------- # diff -Nru a/REPORTING-BUGS b/REPORTING-BUGS --- a/REPORTING-BUGS Wed Jan 15 09:25:57 2003 +++ b/REPORTING-BUGS Wed Jan 15 09:25:57 2003 @@ -1,5 +1,7 @@ [Some of this is taken from Frohwalt Egerer's original linux-kernel FAQ] + + What follows is a suggested procedure for reporting Linux bugs. You aren't obliged to use the bug reporting format, it is provided as a guide to the kind of information that can be useful to developers - no more. diff -Nru a/arch/i386/Kconfig b/arch/i386/Kconfig --- a/arch/i386/Kconfig Wed Jan 15 09:25:57 2003 +++ b/arch/i386/Kconfig Wed Jan 15 09:25:57 2003 @@ -1543,6 +1543,169 @@ Say Y here if you are developing drivers or trying to debug and identify kernel problems. +config KPROBES + bool "Kprobes" + depends on DEBUG_KERNEL + help + Kprobes allows you to trap at almost any kernel address, using + 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 KIRQ + bool "Kernel Irq interceptor for X86(experimental)" + depends on DEBUG_KERNEL && EXPERIMENTAL + help + This option enable an IRQ interceptor. You can get the control + before any specified ISR is executing and decide whether it + should be executing through "register_kirq/unregister_kirq". + +config FI + bool "Fault Injection (EXPERIMENTAL)" + depends on DEBUG_KERNEL && EXPERIMENTAL + help + Enabling Fault Injection will add hooks to the kernel to allow + user space tools to insert specific faults into the kernel for + the purpose of testing the kernels ability to handle exceptional + conditions. + + Fault injection would not normally be enabled on a production + system, but would normally be used in a test environment to + validate the suitablity of a kernel level code for an environment + that requires extreme high availability. + + This code is currently _very_ experimental and by definition allows + some very nasty things to happen happen to your system. Only + enable this if you are ok with the possibility of destroying + your operating system. + +config FI_DEBUG + bool "Fault Injection Debugging" + depends on FI + help + Enabling this option will result in more verbose fault injection + debugging information in the system log. + +config FI_SAMPLE_CODESEGMENT + tristate "Fault Injection Sample Code Segment (EXPERIMENTAL)" + depends on FI + help + Enabling this option will cause the sample fault injection + code segment to be built. + + If in doubt say N. + +config FI_PF + tristate "Fault Injection Pagefault Interceptor (EXPERIMENTAL)" + depends on KMMIO + depends on FI + help + This component adds the ability for Fault Injection to intercept + normal pagefault operations. When combined with the core + fault injection infrastructure and a user space tool chain, it + will be possible to create test cases that manipulate data read + during a pagefault. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called fi_pf.o. If you want to compile it as + a module, say M here and read <file:Documentation/modules.txt>. + + If in doubt, say N. + +config FI_DBP + tristate "Fault Injection DBP Interceptor (EXPERIMENTAL)" + depends on FI + help + This component adds the ability for Fault Injection to intercept + int3's handler. When combined with the core fault injection + infrastructure and a user space tool chain, it will be possible + to create test cases that inject fault into normal PIO access. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called fi_dbp.o. If you want to compile it as + a module, say M here and read <file:Documentation/modules.txt>. + + If in doubt, say N. + +config FI_IRQ + tristate "Fault Injection IRQ Interceptor (EXPERIMENTAL)" + depends on KIRQ + depends on FI + help + This component adds the ability for Fault Injection to intercept + specified irq's handler. When combined with the core fault + injection infrastructure and a user space tool chain, it + will be possible to create test cases that inject fault into + driver's isr. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called fi_irq.o. If you want to compile it as + a module, say M here and read <file:Documentation/modules.txt>. + + If in doubt, say N. + + +config FI_INTERNEL_TESTING + bool "Fault Injection Internel Test Components (EXPERIMENTAL)" + depends on FI + help + Enabling this option will build additional components that are + useful when hacking fault injection components, not creating + a fault injection test case. + + If in doubt say N + +config FI_MOCK_INTERCEPTOR + tristate "Fault Injection Mock Interceptor (EXPERIMENTAL)" + depends on FI_INTERNEL_TESTING + help + Enabling this option will build a mock fault injection interceptor + created for the sole purpose of exercising the fault injection + core code. The only reason a person would want to build this + component is to hack the fault injection interceptor interfaces. + + If in doubt say N + +config FI_MOCK_CODESEGMENT + tristate "Fault Injection Mock Codesegment (EXPERIMENTAL)" + depends on FI_INTERNEL_TESTING + help + Enabling this option will build a mock fault injection codesegment + created for the sole purpose of exercising the fault injection + core code. The only reason a person would want to build this + component is to hack the fault injection codesegment interfaces. + + If in doubt say N + +config FI_TEST + tristate "Fault Injection Test Driver (EXPERIMENTAL)" + depends on FI_INTERNEL_TESTING + help + This component is test driver for demonstrating how the fault + injection kernel hooks, associated interceptor modules, and the + ficl user space utility (available from fault-injection.sf.net) + can be coordinated to implement "fault set" for use in creating + test case scenarios. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called fi_test.o. If you want to compile it as + a module, say M here and read <file:Documentation/modules.txt>. + + Unless you are intending to experiment with fault injection testing, + just say N. + config DEBUG_STACKOVERFLOW bool "Check for stack overflows" depends on DEBUG_KERNEL diff -Nru a/arch/i386/kernel/Makefile b/arch/i386/kernel/Makefile --- a/arch/i386/kernel/Makefile Wed Jan 15 09:25:57 2003 +++ b/arch/i386/kernel/Makefile Wed Jan 15 09:25:57 2003 @@ -4,7 +4,7 @@ EXTRA_TARGETS := head.o init_task.o -export-objs := mca.o i386_ksyms.o time.o +export-objs := mca.o i386_ksyms.o time.o kirq.o kmmio.o obj-y := process.o semaphore.o signal.o entry.o traps.o irq.o vm86.o \ ptrace.o i8259.o ioport.o ldt.o setup.o time.o sys_i386.o \ @@ -30,6 +30,9 @@ obj-$(CONFIG_PROFILING) += profile.o obj-$(CONFIG_EDD) += edd.o obj-$(CONFIG_MODULES) += module.o +obj-$(CONFIG_KPROBES) += kprobes.o +obj-$(CONFIG_KMMIO) += kmmio.o +obj-$(CONFIG_KIRQ) += kirq.o obj-y += sysenter.o EXTRA_AFLAGS := -traditional diff -Nru a/arch/i386/kernel/entry.S b/arch/i386/kernel/entry.S --- a/arch/i386/kernel/entry.S Wed Jan 15 09:25:57 2003 +++ b/arch/i386/kernel/entry.S Wed Jan 15 09:25:57 2003 @@ -471,9 +471,16 @@ jmp ret_from_exception ENTRY(debug) + pushl $-1 # mark this as an int + SAVE_ALL + movl %esp,%edx pushl $0 - pushl $do_debug - jmp error_code + pushl %edx + call do_debug + addl $8,%esp + testl %eax,%eax + jnz restore_all + jmp ret_from_exception ENTRY(nmi) pushl %eax @@ -486,9 +493,16 @@ RESTORE_ALL ENTRY(int3) + pushl $-1 # mark this as an int + SAVE_ALL + movl %esp,%edx pushl $0 - pushl $do_int3 - jmp error_code + pushl %edx + call do_int3 + addl $8,%esp + testl %eax,%eax + jnz restore_all + jmp ret_from_exception ENTRY(overflow) pushl $0 diff -Nru a/arch/i386/kernel/kirq.c b/arch/i386/kernel/kirq.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/i386/kernel/kirq.c Wed Jan 15 09:25:57 2003 @@ -0,0 +1,123 @@ +/* Support for kernel irq interceptor. + (C) 2002 Stanley Wang <sta...@in...>. +*/ + +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/spinlock.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <asm/kirq.h> + +struct kirq kirq_list[NR_IRQS] = + { [0 ... NR_IRQS-1] = { NULL, NULL, NULL}}; + +void kirq_handler(int irq, void *dev_id, struct pt_regs *regs) +{ + int i; + struct kirq *p = kirq_list + irq; + if (p->handler != NULL){ + i = (*(p->handler))(p, irq, dev_id, regs); + if ( i == 0 ) + (*(p->isr))(irq, dev_id, regs); + }else{ + printk(KERN_ERR "%s: Dropping unexpected interrupt #%i\n", + __FUNCTION__, irq); + } + return; +} + +int register_kirq(int irq, char *devname, kirq_handler_t handler) +{ + struct irqaction *action; + irq_desc_t *desc = irq_desc + irq; + struct kirq *p = kirq_list + irq; + unsigned long flags; + + if (handler == NULL) { + printk(KERN_ERR "%s: Missing handler!\n", __FUNCTION__); + return -EINVAL; + } + + if (p->handler) { + printk(KERN_ERR "%s: KIRQ was regitsered already!\n", __FUNCTION__); + return -EINVAL; + } + + spin_lock_irqsave(&desc->lock,flags); + + action = desc->action; + while (action) { + if (strcmp(action->name,devname)) { + action = action->next; + }else{ + break; + } + } + + if (!action) { + spin_unlock_irqrestore(&desc->lock,flags); + return -1; + } + + p->isr = action->handler; + p->handler = handler; + p->dev_id = action->dev_id; + + action->handler = kirq_handler; + + spin_unlock_irqrestore(&desc->lock,flags); + + return 0; +} + +int unregister_kirq(int irq) +{ + struct irqaction *action; + irq_desc_t *desc = irq_desc + irq; + struct kirq *p = kirq_list + irq; + unsigned long flags; + + spin_lock_irqsave(&desc->lock,flags); + + action = desc->action; + while ( action && action->dev_id != p->dev_id) { + action = action->next; + } + + if (!action) { + printk(KERN_ERR "%s: Unregister KIRQ failed!\n", __FUNCTION__); + spin_unlock_irqrestore(&desc->lock,flags); + return -1; + } + + action->handler = p->isr; + + p->isr = NULL; + p->handler = NULL; + p->dev_id = NULL; + + spin_unlock_irqrestore(&desc->lock,flags); + + return 0; +} + +void dispatch_kirq(int irq, struct pt_regs *regs) +{ + struct kirq *p = kirq_list + irq; + if (p->isr != NULL){ + (*(p->isr))(irq, p->dev_id, regs); + }else{ + printk(KERN_ERR "%s: Dropping wrong interrupt #%i\n", + __FUNCTION__, irq); + } + return; +} + +EXPORT_SYMBOL_GPL(register_kirq); +EXPORT_SYMBOL_GPL(unregister_kirq); +EXPORT_SYMBOL_GPL(dispatch_kirq); + 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 Jan 15 09:25:57 2003 @@ -0,0 +1,159 @@ +/* + * KMMIO + * Benfit many code from kprobes + * (C) 2002 Louis Zhuang <lou...@in...>. + */ +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/spinlock.h> +#include <linux/mm.h> +#include <linux/slab.h> + +#include <linux/kmmio.h> +#include <linux/ptrace.h> +#include <linux/preempt.h> +#include <asm/io.h> +#include <asm/highmem.h> + +static int cpu=-1; +static struct kmmio_fault_page *cur_page = NULL; +static struct kmmio_probe *cur_probe = NULL; +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) +{ + /* We're in an interrupt, but this is clear and BUG()-safe. */ + preempt_disable(); + + lock_kmmio(); + + cur_page = get_kmmio_fault_page((void *)addr); + if (!cur_page) { + /* this page fault is not caused by kmmio */ + /* XXX some pending fault on other cpu may cause problem! */ + unlock_kmmio(); + goto no_kmmio; + } + cpu = smp_processor_id(); + + cur_probe = get_kmmio_probe((void *)addr); + kmmio_saved_eflags = (regs->eflags & (TF_MASK|IF_MASK)); + + if (cur_probe && cur_probe->pre_handler) { + cur_probe->pre_handler(cur_probe, 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(cur_page->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 (smp_processor_id() != cpu) + return 0; + + if (cur_probe && cur_probe->post_handler) { + cur_probe->post_handler(cur_probe, condition, regs); + } + + arm_kmmio_fault_page(cur_page->page); + __flush_tlb_one(cur_page->page); + + regs->eflags &= ~TF_MASK; + regs->eflags |= kmmio_saved_eflags; + + cpu = -1; + + 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 inline 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 inline 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 inline 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); +} + +/* the function is only used to make virt map to bus */ +void *kmmio_invert_map(void *virt_addr, unsigned long bus_addr) +{ + int offset; + pte_t *pte; + + if((unsigned long)virt_addr & ~PAGE_MASK) + BUG(); + + offset = bus_addr & ~PAGE_MASK; + bus_addr &= PAGE_MASK; + pte = get_pte((unsigned long)virt_addr); + + set_pte( pte, __pte( (pte_val(*pte) & ~PAGE_MASK) | bus_addr) ); + return virt_addr+offset; +} + +EXPORT_SYMBOL_GPL(kmmio_invert_map); diff -Nru a/arch/i386/kernel/kprobes.c b/arch/i386/kernel/kprobes.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/i386/kernel/kprobes.c Wed Jan 15 09:25:57 2003 @@ -0,0 +1,160 @@ +/* + * Support for kernel probes. + * (C) 2002 Vamsi Krishna S <vam...@in...>. + */ + +#include <linux/config.h> +#include <linux/kprobes.h> +#include <linux/ptrace.h> +#include <linux/spinlock.h> +#include <linux/preempt.h> + +/* kprobe_status settings */ +#define KPROBE_HIT_ACTIVE 0x00000001 +#define KPROBE_HIT_SS 0x00000002 + +static struct kprobe *current_kprobe; +static unsigned long kprobe_status, kprobe_old_eflags, kprobe_saved_eflags; + +/* + * returns non-zero if opcode modifies the interrupt flag. + */ +static inline int is_IF_modifier(u8 opcode) +{ + switch(opcode) { + case 0xfa: /* cli */ + case 0xfb: /* sti */ + case 0xcf: /* iret/iretd */ + case 0x9d: /* popf/popfd */ + return 1; + } + return 0; +} + +static inline void disarm_kprobe(struct kprobe *p, struct pt_regs *regs) +{ + *p->addr = p->opcode; + regs->eip = (unsigned long)p->addr; +} + +/* + * Interrupts are disabled on entry as trap3 is an interrupt gate and they + * remain disabled thorough out this function. + */ +int kprobe_handler(struct pt_regs *regs) +{ + struct kprobe *p; + int ret = 0; + u8 *addr = (u8 *)(regs->eip-1); + + /* We're in an interrupt, but this is clear and BUG()-safe. */ + preempt_disable(); + + /* Check we're not actually recursing */ + if (kprobe_running()) { + /* We *are* holding lock here, so this is safe. + Disarm the probe we just hit, and ignore it. */ + p = get_kprobe(addr); + if (p) { + disarm_kprobe(p, regs); + ret = 1; + } + /* If it's not ours, can't be delete race, (we hold lock). */ + goto no_kprobe; + } + + lock_kprobes(); + p = get_kprobe(addr); + if (!p) { + unlock_kprobes(); + /* Unregistered (on another cpu) after this hit? Ignore */ + if (*addr != BREAKPOINT_INSTRUCTION) + ret = 1; + /* Not one of ours: let kernel handle it */ + goto no_kprobe; + } + + kprobe_status = KPROBE_HIT_ACTIVE; + current_kprobe = p; + kprobe_saved_eflags = kprobe_old_eflags + = (regs->eflags & (TF_MASK|IF_MASK)); + if (is_IF_modifier(p->opcode)) + kprobe_saved_eflags &= ~IF_MASK; + + p->pre_handler(p, regs); + + regs->eflags |= TF_MASK; + regs->eflags &= ~IF_MASK; + + /* We hold lock, now we remove breakpoint and single step. */ + disarm_kprobe(p, regs); + kprobe_status = KPROBE_HIT_SS; + return 1; + +no_kprobe: + preempt_enable_no_resched(); + return ret; +} + +static void rearm_kprobe(struct kprobe *p, struct pt_regs *regs) +{ + regs->eflags &= ~TF_MASK; + *p->addr = BREAKPOINT_INSTRUCTION; +} + +/* + * Interrupts are disabled on entry as trap1 is an interrupt gate and they + * remain disabled thorough out this function. And we hold kprobe lock. + */ +int post_kprobe_handler(struct pt_regs *regs) +{ + if (!kprobe_running()) + return 0; + + if (current_kprobe->post_handler) + current_kprobe->post_handler(current_kprobe, regs, 0); + + /* + * We singlestepped with interrupts disabled. So, the result on + * the stack would be incorrect for "pushfl" instruction. + * Note that regs->esp is actually the top of the stack when the + * trap occurs in kernel space. + */ + if (current_kprobe->opcode == 0x9c) { /* pushfl */ + regs->esp &= ~(TF_MASK | IF_MASK); + regs->esp |= kprobe_old_eflags; + } + + rearm_kprobe(current_kprobe, regs); + regs->eflags |= kprobe_saved_eflags; + + unlock_kprobes(); + 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; +} + +/* Interrupts disabled, kprobe_lock held. */ +int kprobe_fault_handler(struct pt_regs *regs, int trapnr) +{ + if (current_kprobe->fault_handler + && current_kprobe->fault_handler(current_kprobe, regs, trapnr)) + return 1; + + if (kprobe_status & KPROBE_HIT_SS) { + rearm_kprobe(current_kprobe, regs); + regs->eflags |= kprobe_old_eflags; + + unlock_kprobes(); + preempt_enable_no_resched(); + } + return 0; +} diff -Nru a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c --- a/arch/i386/kernel/traps.c Wed Jan 15 09:25:57 2003 +++ b/arch/i386/kernel/traps.c Wed Jan 15 09:25:57 2003 @@ -24,6 +24,8 @@ #include <linux/interrupt.h> #include <linux/highmem.h> #include <linux/kallsyms.h> +#include <linux/kprobes.h> +#include <linux/kmmio.h> #ifdef CONFIG_EISA #include <linux/ioport.h> @@ -344,7 +346,6 @@ } DO_VM86_ERROR_INFO( 0, SIGFPE, "divide error", divide_error, FPE_INTDIV, regs->eip) -DO_VM86_ERROR( 3, SIGTRAP, "int3", int3) DO_VM86_ERROR( 4, SIGSEGV, "overflow", overflow) DO_VM86_ERROR( 5, SIGSEGV, "bounds", bounds) DO_ERROR_INFO( 6, SIGILL, "invalid operand", invalid_op, ILL_ILLOPN, regs->eip) @@ -360,6 +361,9 @@ { if (regs->eflags & VM_MASK) goto gp_in_vm86; + + if (kprobe_running() && kprobe_fault_handler(regs, 13)) + return; if (!(regs->xcs & 3)) goto gp_in_kernel; @@ -484,6 +488,17 @@ nmi_callback = dummy_nmi_callback; } +asmlinkage int do_int3(struct pt_regs *regs, long error_code) +{ + if (kprobe_handler(regs)) + return 1; + /* This is an interrupt gate, because kprobes wants interrupts + disabled. Normal trap handlers don't. */ + restore_interrupts(regs); + do_trap(3, SIGTRAP, "int3", 1, regs, error_code, NULL); + return 0; +} + /* * Our handling of the processor debug registers is non-trivial. * We do not clear them on entry and exit from the kernel. Therefore @@ -506,7 +521,7 @@ * find every occurrence of the TF bit that could be saved away even * by user code) */ -asmlinkage void do_debug(struct pt_regs * regs, long error_code) +asmlinkage int do_debug(struct pt_regs * regs, long error_code) { unsigned int condition; struct task_struct *tsk = current; @@ -514,6 +529,15 @@ __asm__ __volatile__("movl %%db6,%0" : "=r" (condition)); + 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); + /* Mask out spurious debug traps due to lazy DR7 setting */ if (condition & (DR_TRAP0|DR_TRAP1|DR_TRAP2|DR_TRAP3)) { if (!tsk->thread.debugreg[7]) @@ -564,17 +588,17 @@ __asm__("movl %0,%%db7" : /* no output */ : "r" (0)); - return; + return 0; debug_vm86: handle_vm86_trap((struct kernel_vm86_regs *) regs, error_code, 1); - return; + return 0; clear_TF_reenable: set_tsk_thread_flag(tsk, TIF_SINGLESTEP); clear_TF: regs->eflags &= ~TF_MASK; - return; + return 0; } /* @@ -738,6 +762,8 @@ struct task_struct *tsk = current; clts(); /* Allow maths ops (or we recurse) */ + if (kprobe_running() && kprobe_fault_handler(®s, 7)) + return; if (!tsk->used_math) init_fpu(tsk); restore_fpu(tsk); @@ -831,9 +857,9 @@ #endif set_trap_gate(0,÷_error); - set_trap_gate(1,&debug); + _set_gate(idt_table+1,14,3,&debug); /* debug trap for kprobes */ set_intr_gate(2,&nmi); - set_system_gate(3,&int3); /* int3-5 can be called from all */ + _set_gate(idt_table+3,14,3,&int3); /* int3-5 can be called from all */ set_system_gate(4,&overflow); set_system_gate(5,&bounds); set_trap_gate(6,&invalid_op); diff -Nru a/arch/i386/mm/fault.c b/arch/i386/mm/fault.c --- a/arch/i386/mm/fault.c Wed Jan 15 09:25:57 2003 +++ b/arch/i386/mm/fault.c Wed Jan 15 09:25:57 2003 @@ -20,6 +20,8 @@ #include <linux/tty.h> #include <linux/vt_kern.h> /* For unblank_screen() */ #include <linux/module.h> +#include <linux/kprobes.h> +#include <linux/kmmio.h> #include <asm/system.h> #include <asm/uaccess.h> @@ -161,6 +163,12 @@ /* get the address */ __asm__("movl %%cr2,%0":"=r" (address)); + 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 -Nru a/drivers/Makefile b/drivers/Makefile --- a/drivers/Makefile Wed Jan 15 09:25:57 2003 +++ b/drivers/Makefile Wed Jan 15 09:25:57 2003 @@ -44,3 +44,5 @@ obj-$(CONFIG_HOTPLUG_PCI) += hotplug/ obj-$(CONFIG_ISDN_BOOL) += isdn/ obj-$(CONFIG_MCA) += mca/ +obj-$(CONFIG_FI) += fi/ + diff -Nru a/drivers/fi/Makefile b/drivers/fi/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/fi/Makefile Wed Jan 15 09:25:57 2003 @@ -0,0 +1,11 @@ +# +# Kernel Fault Injection Features +# + +EXTRA_AFLAGS := -traditional +export-objs := fi_core.o + +obj-$(CONFIG_FI_INTERNEL_TESTING) += testing/ +obj-y += interceptors/ +obj-y += codesegments/ +obj-$(CONFIG_FI) += fi_core.o diff -Nru a/drivers/fi/README b/drivers/fi/README --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/fi/README Wed Jan 15 09:25:57 2003 @@ -0,0 +1,181 @@ +This is the root directory for fault injection. +Fault injection contains the following: + +fi_core.c : Core fault injection code. +EXPORT_SYMBOL_GPL(fi_register_interceptor); +EXPORT_SYMBOL_GPL(fi_unregister_interceptor); +EXPORT_SYMBOL_GPL(fi_register_code_segment); +EXPORT_SYMBOL_GPL(fi_unregister_code_segment); +EXPORT_SYMBOL_GPL(fi_execute_trigger); +EXPORT_SYMBOL_GPL(fi_debug); + +testing/ : Proper place to add any code created for the purpose + of testing the fault injection core code (not to + to actually implement 'fault injection testing') + * Current test code includes + - mock interceptor : + a simple interceptor implementation that + can be configured to represent any + interceptor type, and adds a 'trip'file + to the specific trigger's sysfs directory + to enable the trigger to be tripped via + command line. + - mock code segment : + a simple code segment implementation. + +interceptors/ : Proper place to add any interceptor implementations. + * Current interceptors are + - pf : 'page fault' interceptor for intercepting MMIO + - dbp : ??? + +codesegments/ : Proper place to add any code segment implementations + * Current code segments are + - sample : ??? + + +What is an 'interceptor'? +------------------------- + +An interceptor is a component that knows how to intercept some specific +type of kernel level event or action to enable the fault injection +core to 'hook' into event or action and take some action. + +An interceptor is one level higher then a normal kernel hook. +For example an interceptor could be written to utilize kprobes, +where the kprobe is tripped every time a specific address is executed, +but the interceptor might contain some additional logic to decide if the +specific event or action was happening. + + + Life cycle of an interceptor + ---------------------------- + + ( Startup ) + ---------------- + | Initialization | ---------------------------- + ---------------- || + || | \/ + || | ------------------------------------------ + || | | initialize a 'interceptor' kernel object | + || ------- | with function pointers for the fault | + || | injection core to interface with this | + || | interceptor | + || ------------------------------------------ + || + || + || + \/ + ----------------------------------------------- + | (call into fi_core) fi_register_interceptor() | + ----------------------------------------------- + + (User starts using the interceptor) + -------------------------------- + | Fault injection core creates | + | a trigger that references this | + | interceptor | + -------------------------------- + || + \/ + -------------- + | arm(trigger) | ------------ + -------------- || + | || + | \/ + | --------------------------------------- + |___ | take what ever action is required | + | to hook into the low level event. | + | This could be with kprobes, or kmmio, | + | or whatever | + --------------------------------------- + + (Event happens) + ----------------------------- + | Low level hook is triggered | + ----------------------------- + || + \/ + ---------------------------------------- + | (call into fi_core) fi_execute_trigger | + ---------------------------------------- + + (User stops using the interceptor) + ----------------------------------------------- + | Fault injection core removes the trigger that | + | references this interceptor | + ----------------------------------------------- + || + \/ + ----------------- + | disarm(trigger) | --------- + ----------------- || + | || + | \/ + | --------------------------------------- + |___ | take what ever action is required | + | to remove the level hooks we added | + | when we first armed the interceptor | + --------------------------------------- + + +What is a 'code segment'? +------------------------- +A code segment is a component that knows how to handle a specific +type of trigger event. In the absence of an attached code segment, +the fault injection core will only be able to take some very simple +actions when an interceptor executes a trigger. Developers can +extend these basic cababilities by creating a code segment for a very +specific type of event, loading the code segment, and then attaching +the code segment to the appropriate triggers. + + Life cycle of a code segment + ---------------------------- + + ( Startup ) + + ---------------- + | Initialization | + ---------------- + || + || + || + \/ + ------------------------------------------------ + | (call into fi_core) fi_register_code_segment() | + ------------------------------------------------ + + (User starts using the code segment) + + Completely outside the view of the code segment, the user + via sysfs interfaces provided by the fault injection core + is able to: + 1. create a trigger + 2. 'attach' this code segment to the new trigger + + + (Event happens) + + ------------------------------------------ + | The previously created trigger is | + | executed as described in the trigger | + | life cycle above. Durring the execution | + | of fi_core::fi_execute_trigger the core | + | notices the attached code segment. | + ------------------------------------------ + || + \/ + ---------------------------------------------- + | (using function pointer from cs registration)| + | execute_trigger() | + ---------------------------------------------- + + + (User stops using the code segment) + + Completely outside the view of the code segment, the user + via sysfs interfaces provided by the fault injection core + either: + * removes the associated trigger + - or - + * removes the association between the trigger and the + code segment diff -Nru a/drivers/fi/codesegments/Makefile b/drivers/fi/codesegments/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/fi/codesegments/Makefile Wed Jan 15 09:25:57 2003 @@ -0,0 +1,7 @@ +# +# Kernel Fault Injection Codesegments +# + +EXTRA_AFLAGS := -traditional + +obj-$(CONFIG_FI_SAMPLE_CODESEGMENT) += fi_sample_cs.o diff -Nru a/drivers/fi/codesegments/fi_sample_cs.c b/drivers/fi/codesegments/fi_sample_cs.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/fi/codesegments/fi_sample_cs.c Wed Jan 15 09:25:57 2003 @@ -0,0 +1,76 @@ +/* Copyright (C) 2002 Louis Zhuang <lou...@in...> */ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/kobject.h> +#include <linux/fi.h> + +#include <asm/io.h> +#include <asm/kmmio.h> + +#define DRIVER_AUTHOR "Louis Zhuang <lou...@in...>" +#define DRIVER_DESC "Fault Injection Sample Code Segment" + +static unsigned long bus_addr=0; +static void *iopage; + +static void sample_execute(struct trigger *t, struct interceptor *i, + __u32 val, int len, int type, void *data) +{ + void *virt_ptr; + info("tri=%s, intcpt=%s, val=%i, len=%i, type=%i, data=%p", + t->kobj.name, i->kobj.name, val, len, type, + data); + virt_ptr = kmmio_invert_map(iopage, bus_addr); + info("virt_ptr=%p", virt_ptr); +} + +static struct code_segment sample_code_segment = { + .execute_trigger = sample_execute, + .kobj = {.name="sample"} +}; + +static ssize_t sample_addr_show(struct code_segment *cs, + char *page) +{ + return sprintf(page, "%#lx", bus_addr); +} + +static ssize_t sample_addr_store(struct code_segment *cs, + const char *page, size_t count) +{ + sscanf(page, "%ld", &bus_addr); + return count; +} + +static struct code_segment_attribute sample_attr_trigger = { + .attr = { .name = "bus_addr", .mode = 0644 }, + .show = sample_addr_show, + .store= sample_addr_store +}; + + +static int __init sample_init(void) +{ + iopage = ioremap(0xe0000000, 4096); + if (fi_register_code_segment(&sample_code_segment)) { + err("Failed to register Mock Code Segment\n"); + return -EINVAL; + } + sysfs_create_file(&sample_code_segment.kobj, + &sample_attr_trigger.attr); + return 0; +} + +static void __exit sample_exit(void) +{ + sysfs_remove_file(&sample_code_segment.kobj, + &sample_attr_trigger.attr); + fi_unregister_code_segment(&sample_code_segment); + iounmap(iopage); +} +module_init(sample_init); +module_exit(sample_exit); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); diff -Nru a/drivers/fi/fi_core.c b/drivers/fi/fi_core.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/fi/fi_core.c Wed Jan 15 09:25:57 2003 @@ -0,0 +1,941 @@ +/********************************************************************* + * Fault Injection Core Functionality + * Copyright (C) Intel Crop. + * + ********************************************************************* + * + * interceptor<-------->trigger<-------->code segment + * 1 * 1 * + * + * There are one-multi relationship between interceptor and trigger, + * also between trigger and code segment. + * --lz + ********************************************************************** + * This is a rewrite of code originally submitted by + * Louis Zhuang <lou...@in...>. I have moved the code from + * a ioctl based control using proc to expose information about + * fault injection data that was contained in fixed sized tables, + * to an extendable kobject based implementation that uses sysfs + * for all user space access. + * --rustyl + * + * Contributors: + * Louis Zhuang <lou...@in...> + * Stanley Wang <sta...@in...> + * Rusty Lynch <ru...@li...> + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/kprobes.h> +#include <linux/slab.h> +#include <linux/kobject.h> +#include <linux/random.h> + +#include <linux/fi.h> + +static DECLARE_MUTEX(fi_sem); + +static struct subsystem fi_subsys; +static struct subsystem trigger_subsys; +static struct subsystem interceptor_subsys; + +static int is_initialized = 0; +int fi_debug = 1; + + +/* + * =========================================================== + * <>-- Misc functions --<> + * ----------------------------------------------------------- + */ +static inline struct trigger * +find_trigger_by_name(const char *name) +{ + struct kobject *kobj; + kobj = kset_find_obj(&trigger_subsys.kset, name); + if (kobj) + return container_of(kobj, struct trigger, kobj); + + return NULL; +} + +static inline struct interceptor * +find_interceptor_by_name(const char *name) +{ + struct kobject *kobj; + kobj = kset_find_obj(&interceptor_subsys.kset, name); + if (kobj) + return container_of(kobj, struct interceptor, kobj); + + return NULL; +} + +/* + * =========================================================== + * <>-- Code segment functions --<> + * ----------------------------------------------------------- + */ +static struct code_segment_attribute code_segment_attr_ctl; + +static inline void create_code_segment_files(struct code_segment *cs) +{ + sysfs_create_file(&cs->kobj, &code_segment_attr_ctl.attr); +} + +static inline void remove_code_segment_files(struct code_segment *cs) +{ + sysfs_remove_file(&cs->kobj, &code_segment_attr_ctl.attr); +} + +static inline void add_code_segment(struct code_segment *cs, + struct trigger *t) +{ + kobject_get(&t->kobj); + kobject_get(&cs->kobj); + list_add(&cs->list, &t->cs_list); + cs->tri = t; +} + +static inline void del_code_segment(struct code_segment *cs) +{ + struct trigger *t = cs->tri; + + list_del(&cs->list); + cs->tri = NULL; + kobject_put(&cs->kobj); + kobject_put(&t->kobj); +} + +static inline void del_cs_list(struct list_head *head) { + struct list_head *pos; + struct list_head *tmp; + list_for_each_safe (pos, tmp, head) { + del_code_segment(list_entry(pos, struct code_segment, list)); + } +} + +/* + * =========================================================== + * <>-- Trigger functions --<> + * ----------------------------------------------------------- + */ +static struct trigger_attribute trigger_attr_wp; +static struct trigger_attribute trigger_attr_bitmask; +static struct trigger_attribute trigger_attr_min; +static struct trigger_attribute trigger_attr_max; +static struct trigger_attribute trigger_attr_skip; +static struct trigger_attribute trigger_attr_stop; +static struct trigger_attribute trigger_attr_protection; +static struct trigger_attribute trigger_attr_hertz; +static struct trigger_attribute trigger_attr_registered; +static struct trigger_attribute trigger_attr_opcode; +static struct trigger_attribute trigger_attr_operand; +static struct trigger_attribute trigger_attr_count; +static struct trigger_attribute trigger_attr_intcpt; + +static inline void create_trigger_files(struct trigger *t) +{ + sysfs_create_file(&t->kobj, &trigger_attr_wp.attr); + sysfs_create_file(&t->kobj, &trigger_attr_bitmask.attr); + sysfs_create_file(&t->kobj, &trigger_attr_min.attr); + sysfs_create_file(&t->kobj, &trigger_attr_max.attr); + sysfs_create_file(&t->kobj, &trigger_attr_skip.attr); + sysfs_create_file(&t->kobj, &trigger_attr_stop.attr); + sysfs_create_file(&t->kobj, &trigger_attr_protection.attr); + sysfs_create_file(&t->kobj, &trigger_attr_hertz.attr); + sysfs_create_file(&t->kobj, &trigger_attr_registered.attr); + sysfs_create_file(&t->kobj, &trigger_attr_opcode.attr); + sysfs_create_file(&t->kobj, &trigger_attr_operand.attr); + sysfs_create_file(&t->kobj, &trigger_attr_count.attr); + sysfs_create_file(&t->kobj, &trigger_attr_intcpt.attr); +} + +static inline void remove_trigger_files(struct trigger *t) +{ + sysfs_remove_file(&t->kobj, &trigger_attr_wp.attr); + sysfs_remove_file(&t->kobj, &trigger_attr_bitmask.attr); + sysfs_remove_file(&t->kobj, &trigger_attr_min.attr); + sysfs_remove_file(&t->kobj, &trigger_attr_max.attr); + sysfs_remove_file(&t->kobj, &trigger_attr_skip.attr); + sysfs_remove_file(&t->kobj, &trigger_attr_stop.attr); + sysfs_remove_file(&t->kobj, &trigger_attr_protection.attr); + sysfs_remove_file(&t->kobj, &trigger_attr_hertz.attr); + sysfs_remove_file(&t->kobj, &trigger_attr_registered.attr); + sysfs_remove_file(&t->kobj, &trigger_attr_opcode.attr); + sysfs_remove_file(&t->kobj, &trigger_attr_operand.attr); + sysfs_remove_file(&t->kobj, &trigger_attr_count.attr); + sysfs_remove_file(&t->kobj, &trigger_attr_intcpt.attr); +} + +static inline void unarm_trigger(struct trigger *t) +{ + (t->intcpt->disarm)(t); + list_del(&t->list); +} + +static inline int arm_trigger(struct trigger *t, + const char *intcpt_name) +{ + int rv; + struct interceptor * i; + i = find_interceptor_by_name(intcpt_name); + if (!i) return -1; + t->intcpt = i; + list_add(&t->list, &i->tri_list); + rv = (i->arm)(t); + if (rv) { + list_del(&t->list); + } + return rv; +} + +static inline ssize_t add_trigger(struct trigger *t, + const char *intcpt_name) +{ + struct trigger *n; + + n = kmalloc(sizeof(struct trigger),GFP_KERNEL); + + if (!n) return -ENOMEM; + + memset(n,0,sizeof(struct trigger)); + INIT_LIST_HEAD(&n->cs_list); + INIT_LIST_HEAD(&n->list); + + strncpy(n->kobj.name, t->kobj.name, KOBJ_NAME_LEN); + n->kobj.kset = &trigger_subsys.kset; + n->wp.addr = t->wp.addr; + n->wp.type = t->wp.type; + n->bitmask = t->bitmask; + n->min = t->min; + n->max = t->max; + n->skip = t->skip; + n->stop = t->stop; + n->protection = t->protection; + n->hertz = t->hertz; + n->registered = t->registered; + n->opcode = t->opcode; + n->operand = t->operand; + atomic_set(&n->count, 0); + + if (kobject_register(&n->kobj)) { + err("Unable to register kobject"); + return -EINVAL; + } + + if (arm_trigger(n, intcpt_name)) { + err("Unable to arm the trigger, unregister %s", + n->kobj.name); + kobject_unregister(&n->kobj); + kfree(n); + return -EINVAL; + } + + create_trigger_files(n); + return 0; +} + +static inline void del_trigger(struct trigger *t) +{ + unarm_trigger(t); + remove_trigger_files(t); + del_cs_list(&t->cs_list); + kobject_unregister(&t->kobj); +} + +static inline ssize_t del_trigger_by_name(const char *tri_name) +{ + struct trigger * t; + + t = find_trigger_by_name(tri_name); + if (!t) { + err("trigger %s does not exist", tri_name); + return -EINVAL; + } + + del_trigger(t); + return 0; + +} + +static inline void del_triggers(struct list_head *head) +{ + struct list_head *pos; + struct list_head *tmp; + + list_for_each_safe (pos, tmp, head) { + del_trigger(list_entry(pos, struct trigger, list)); + } +} + +/* + * =========================================================== + * <>-- Toplevel Containing Subsystem (fi_subsys) --<> + * ----------------------------------------------------------- + */ + +static ssize_t fi_debug_show(struct subsystem *s, char *page) +{ + return sprintf(page,"%i\n",fi_debug); +} + +static ssize_t fi_debug_store(struct subsystem *s, const char *page, + size_t count) +{ + int tmp; + int num; + int ret = 0; + + num = sscanf(page,"%i",&tmp); + if (!num) { + ret = -EINVAL; + goto Done; + } + + if (tmp) { + fi_debug = 1; + } else { + fi_debug = 0; + } + +Done: + return ret ? ret : count; +} + +static decl_subsys(fi, NULL); + +static struct subsys_attribute fi_subsys_attr_debug = { + .attr = { .name = "debug", .mode = 0644 }, + .show = fi_debug_show, + .store = fi_debug_store, +}; + +/* + * =============================================================== + * <>-- Interceptor Containing Subsystem (interceptor_subsys) --<> + * --------------------------------------------------------------- + */ + +static ssize_t interceptor_attr_show(struct kobject * kobj, + struct attribute * attr, + char * page) +{ + struct interceptor * i = container_of(kobj,struct interceptor,kobj); + struct interceptor_attribute * a = + container_of(attr,struct interceptor_attribute,attr); + + dbg("about to call show function for %s", a->attr.name); + return a->show ? a->show(i,page) : 0; +} + +static ssize_t interceptor_attr_store(struct kobject * kobj, + struct attribute * attr, + const char * page, + size_t count) +{ + struct interceptor * i = container_of(kobj,struct interceptor,kobj); + struct interceptor_attribute * a = + container_of(attr,struct interceptor_attribute,attr); + + dbg("about to call store function for %s", a->attr.name); + return a->store ? a->store(i,page,count) : 0; +}; + +static struct sysfs_ops interceptor_sysfs_ops = { + .show = interceptor_attr_show, + .store = interceptor_attr_store, +}; + +static struct kobj_type interceptor_ktype = { + .sysfs_ops = &interceptor_sysfs_ops +}; + +static decl_subsys(interceptor, &interceptor_ktype); + +/* + * =========================================================== + * <>-- Trigger Subsystem (trigger_subsys) --<> + * ----------------------------------------------------------- + */ + +static ssize_t trigger_attr_show(struct kobject * kobj, + struct attribute * attr, + char * page) +{ + struct trigger * n = container_of(kobj,struct trigger,kobj); + struct trigger_attribute * trigger_attr = + container_of(attr,struct trigger_attribute,attr); + + dbg("about to call show function for %s", trigger_attr->attr.name); + return trigger_attr->show ? trigger_attr->show(n,page) : 0; +} + +static ssize_t trigger_attr_store(struct kobject * kobj, + struct attribute * attr, + const char * page, + size_t count) +{ + struct trigger * n = container_of(kobj,struct trigger,kobj); + struct trigger_attribute * trigger_attr = + container_of(attr,struct trigger_attribute,attr); + + dbg("about to call store function for %s", trigger_attr->attr.name); + return trigger_attr->store ? trigger_attr->store(n,page,count) : 0; +} + +static struct sysfs_ops trigger_sysfs_ops = { + .show = trigger_attr_show, + .store = trigger_attr_store, +}; + +static struct kobj_type trigger_ktype = { + .sysfs_ops = &trigger_sysfs_ops +}; + +static decl_subsys(trigger, &trigger_ktype); + +static ssize_t trigger_wp_read(struct trigger * p, char * page) +{ + dbg("wp.addr = %#lx, wp.type = %#x", p->wp.addr, p->wp.type); + return sprintf(page,"%#lx %#x\n", + p->wp.addr,p->wp.type); +} +static struct trigger_attribute trigger_attr_wp = { + .attr = { .name = "wp", .mode = S_IRUGO }, + .show = trigger_wp_read, +}; + +static ssize_t trigger_bitmask_read(struct trigger * p, char * page) +{ + dbg("bitmask = %#x", p->bitmask); + return sprintf(page,"%#x\n",p->bitmask); +} +static struct trigger_attribute trigger_attr_bitmask = { + .attr = { .name = "bitmask", .mode = S_IRUGO }, + .show = trigger_bitmask_read, +}; + +static ssize_t trigger_min_read(struct trigger * p, char * page) +{ + dbg("min = %i", p->min); + return sprintf(page,"%i\n",p->min); +} +static struct trigger_attribute trigger_attr_min = { + .attr = { .name = "min", .mode = S_IRUGO }, + .show = trigger_min_read, +}; + +static ssize_t trigger_max_read(struct trigger * p, char * page) +{ + dbg("max = %i", p->max); + return sprintf(page,"%i\n",p->max); +} +static struct trigger_attribute trigger_attr_max = { + .attr = { .name = "max", .mode = S_IRUGO }, + .show = trigger_max_read, +}; + +static ssize_t trigger_skip_read(struct trigger * p, char * page) +{ + dbg("skip = %i", p->skip); + return sprintf(page,"%i\n",p->skip); +} +static struct trigger_attribute trigger_attr_skip = { + .attr = { .name = "skip", .mode = S_IRUGO }, + .show = trigger_skip_read, +}; + +static ssize_t trigger_stop_read(struct trigger * p, char * page) +{ + dbg("stop = %i", p->stop); + return sprintf(page,"%i\n",p->stop); +} +static struct trigger_attribute trigger_attr_stop = { + .attr = { .name = "stop", .mode = S_IRUGO }, + .show = trigger_stop_read, +}; + +static ssize_t trigger_protection_read(struct trigger * p, char * page) +{ + dbg("protection = %i", p->protection); + return sprintf(page,"%i\n",p->protection); +} + +static struct trigger_attribute trigger_attr_protection = { + .attr = { .name = "protection", .mode = S_IRUGO }, + .show = trigger_protection_read, +}; + +static ssize_t trigger_hertz_read(struct trigger * p, char * page) +{ + dbg("hertz = %i", p->hertz); + return sprintf(page,"%i\n",p->hertz); +} + +static struct trigger_attribute trigger_attr_hertz = { + .attr = { .name = "hertz", .mode = S_IRUGO }, + .show = trigger_hertz_read, +}; + +static ssize_t trigger_registered_read(struct trigger * p, char * page) +{ + dbg("registered = %i", p->registered); + return sprintf(page,"%i\n",p->registered); +} + +static struct trigger_attribute trigger_attr_registered = { + .attr = { .name = "registered", .mode = S_IRUGO }, + .show = trigger_registered_read, +}; + +static ssize_t trigger_count_read(struct trigger * p, char * page) +{ + dbg("count = %i", atomic_read(&p->count)); + return sprintf(page,"%i\n",atomic_read(&p->count)); +} + +static struct trigger_attribute trigger_attr_count = { + .attr = { .name = "count", .mode = S_IRUGO }, + .show = trigger_count_read, +}; + +static ssize_t trigger_intcpt_read(struct trigger * p, char * page) +{ + return sprintf(page,"%s\n",p->intcpt->kobj.name); +} + +static struct trigger_attribute trigger_attr_intcpt = { + .attr = { .name = "interceptor", .mode = S_IRUGO }, + .show = trigger_intcpt_read, +}; + +static inline ssize_t print_cs_list(struct list_head *head, + char *page) +{ + int i = 0; + struct code_segment *pos; + list_for_each_entry (pos, head, list) { + i += sprintf(page+i, "code segment name: %s\n", + pos->kobj.name); + } + return 0; +} + +static ssize_t trigger_opcode_read(struct trigger * p, char * page) +{ + dbg("opcode = %i", p->opcode); + if (list_empty(&p->cs_list)) { + return sprintf(page,"op code: %i\n",p->opcode); + } else { + int i; + i = print_cs_list(&p->cs_list, page); + return i; + } +} + +static struct trigger_attribute trigger_attr_opcode = { + .attr = { .name = "opcode", .mode = S_IRUGO }, + .show = trigger_opcode_read, +}; + +static ssize_t trigger_operand_read(struct trigger * p, char * page) +{ + dbg("operand = %i", p->operand); + if (list_empty(&p->cs_list)) { + return sprintf(page,"%i\n",p->operand); + } else { + return sprintf(page,"(no used)\n"); + } +} + +static struct trigger_attribute trigger_attr_operand = { + .attr = { .name = "operand", .mode = S_IRUGO }, + .show = trigger_operand_read, +}; + +static ssize_t trigger_ctl_show(struct subsystem *s, char *page) +{ + return 0; +} + +static ssize_t trigger_ctl_store(struct subsystem *s, const char *page, + size_t count) +{ + char ctl[KOBJ_NAME_LEN]; + char intcpt[KOBJ_NAME_LEN]; + int num; + int ret = 0; + struct trigger tmp; + + /* 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 */ + /* ctl nm ic wa wb bm mn mx sk st pt hz rg oc oa */ + num = sscanf( page, "%15s %s %s %lx %x %x %i %i %i %i %i %i %i %i %i", + ctl, /* 3 */ + tmp.kobj.name, /* 4 */ + intcpt, /* 5 */ + &tmp.wp.addr, /* 6 */ + &tmp.wp.type, /* 7 */ + &tmp.bitmask, /* 8 */ + &tmp.min, /* 9 */ + &tmp.max, /* 10 */ + &tmp.skip, /* 11 */ + &tmp.stop, /* 12 */ + &tmp.protection, /* 13 */ + &tmp.hertz, /* 14 */ + &tmp.registered, /* 15 */ + (int *)&tmp.opcode, /* 16 */ + &tmp.operand); /* 17 */ + if (!num) { + err("Invalid command format, translated no commands"); + ret = -EINVAL; + goto Done; + } + + if (!strcmp(ctl,"add") && num == 15) { + dbg("intcpt = '%s'", intcpt); + ret = add_trigger(&tmp, intcpt); + } else if (!strcmp(ctl,"del") && num == 2) { + ret = del_trigger_by_name(tmp.kobj.name); + } else { + err("Invalid command format: %s, %i tokens found",ctl,num); + ret = -EINVAL; + } + +Done: + return ret ? ret : count; +} + +static struct subsys_attribute trigger_subsys_attr_ctl = { + .attr = { .name = "ctl", .mode = 0644 }, + .show = trigger_ctl_show, + .store = trigger_ctl_store, +}; + + +/* + * =============================================================== + * <>-- Code Segment Containing Subsystem (code_segment_subsys) --<> + * --------------------------------------------------------------- + */ +static ssize_t code_segment_attr_show(struct kobject * kobj, + struct attribute * attr, + char * page) +{ + struct code_segment * cs + = container_of(kobj,struct code_segment,kobj); + struct code_segment_attribute * a + = container_of(attr,struct code_segment_attribute,attr); + + dbg("about to call show function for %s", a->attr.name); + return a->show ? a->show(cs,page) : 0; +} + +static ssize_t code_segment_attr_store(struct kobject * kobj, + struct attribute * attr, + const char * page, + size_t count) +{ + struct code_segment * cs = container_of(kobj,struct code_segment,kobj); + struct code_segment_attribute * a = + container_of(attr,struct code_segment_attribute,attr); + + dbg("about to call store function for %s", a->attr.name); + return a->store ? a->store(cs,page,count) : 0; +} + +static struct sysfs_ops code_segment_sysfs_ops = { + .show = code_segment_attr_show, + .store = code_segment_attr_store, +}; + +static struct kobj_type code_segment_ktype = { + .sysfs_ops = &code_segment_sysfs_ops +}; + +static decl_subsys(code_segment, &code_segment_ktype); + +static ssize_t code_segment_ctl_show(struct code_segment *cs, + char * page) +{ + if (cs->tri) { + return sprintf(page, "%s\n", cs->tri->kobj.name); + } else { + return sprintf(page, "%s\n", + "Usage: attach|detach [trigger_name]"); + } +} + +static ssize_t code_segment_ctl_store(struct code_segment *cs, + const char *page, + size_t count) +{ + char ctl[KOBJ_NAME_LEN]; + char tri_name[KOBJ_NAME_LEN]; + int num; + int ret = 0; + struct trigger *t; + + num = sscanf(page, "%15s %15s", ctl, tri_name); + if (!num) { + err("Invalid command format, translated no commands"); + ret = -EINVAL; + goto Done; + } + + if (!strcmp(ctl, "attach") && num==2) { + if (cs->tri) { + err("Unable to attach more trigger"); + ret = -EINVAL; + goto Done; + } + dbg("trigger = '%s'", tri_name); + t = find_trigger_by_name(tri_name); + if (t) { + add_code_segment(cs, t); + } else { + err("Unable to find trigger %s", tri_name); + } + } else if (!strcmp(ctl, "detach") && num==1) { + if (!cs->tri) { + err("No trigger is attached"); + ret = -EINVAL; + goto Done; + } + del_code_segment(cs); + } else { + err("Invalid command format: %s, %i tokens found",ctl,num); + ret = -EINVAL; + } +Done: + return ret ? ret : count; +} + +static struct code_segment_attribute code_segment_attr_ctl = { + .attr = { .name = "ctl", .mode = 0644 }, + .show = code_segment_ctl_show, + .store= code_segment_ctl_store +}; + +/* + * =============================================================== + * <>-- Others ;) --<> + * --------------------------------------------------------------- + */ + +static inline int is_in_random(int hertz) +{ + int tmp; + if (hertz==0 || hertz==1) return 1; + get_random_bytes(&tmp, sizeof(tmp)); + return (tmp < 0xFFFFFFFF/hertz); +} + +static inline int is_in_range(int data, int min, int max, int bitmask) +{ + int tmp; + if (min==0 && max==0) return 1; + tmp = data & (~bitmask); + if ( min<=tmp && tmp<max) return 1; + ret... [truncated message content] |
From: <ri...@me...> - 2006-07-05 04:37:41
|
Hi. I=A1=AFm Pusan National Univ. student in korea and first time using this = tool. Using the =A1=B0Fault Injection Test Harness tools, I wanted to test it = in Linux, But I was faced with the problem. The problem is that I can=A1=AFt find = a correct instruction of using. It was impossible to download a source = from <http://fault-injection.bkbits.net/> http://fault-injection.bkbits.net/, = so I downloaded fith_utility 1.3 version from Sourceforge.net According to the instruction from =A1=B0README,=A1=B1 first of all,=20 If you execute the patch, the following message will be appeared. =20 ./kprobes-2.5.50-bk2.patch: line 1: Linus,: command not found ./kprobes-2.5.50-bk2.patch: line 3: Here: command not found ./kprobes-2.5.50-bk2.patch: line 4: Rusty: command not found ./kprobes-2.5.50-bk2.patch: line 147: syntax error near unexpected token = `(' ./kprobes-2.5.50-bk2.patch: line 147: `+ /* We're in an = interrupt, but this is clear and BUG()-safe. */' =20 Would you do to send me instruction of using the tools? =20 |