[prev in list] [next in list] [prev in thread] [next in thread]
List: fault-injection-developer
Subject: [Fault-injection-developer] FITH ALL-IN-ONE patch
From: Louis Zhuang <louis.zhuang () linux ! co ! intel ! com>
Date: 2003-01-14 5:55:31
[Download RAW message or body]
Pls take a look ;-)
--
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
# 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.1017 -> 1.1018
# 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.7
drivers/fi/codesegments/fi_sample_cs.c
# (new) -> 1.10
drivers/fi/interceptors/fi_irq.c
# (new) -> 1.1 arch/i386/kernel/kprobes.c
# (new) -> 1.24
drivers/fi/interceptors/pf/pf.c
# (new) -> 1.3 drivers/fi/README
# (new) -> 1.13 drivers/fi/Makefile
# (new) -> 1.3 include/linux/kmmio.h
# (new) -> 1.1 drivers/fi/testing/Makefile
# (new) -> 1.41 drivers/fi/fi_core.c
# (new) -> 1.7
drivers/fi/interceptors/pf/Makefile
# (new) -> 1.1
drivers/fi/interceptors/Makefile
# (new) -> 1.8
drivers/fi/interceptors/pf/pf_in.c
# (new) -> 1.7 arch/i386/kernel/kmmio.c
# (new) -> 1.1 include/asm-i386/kprobes.h
# (new) -> 1.13
drivers/fi/interceptors/dbp/fi_dbp.c
# (new) -> 1.1 include/linux/kprobes.h
# (new) -> 1.7 kernel/kmmio.c
# (new) -> 1.14 include/linux/fi.h
# (new) -> 1.15 drivers/fi/testing/fi_test.c
# (new) -> 1.8
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.12
drivers/fi/testing/fi_mock_interceptor.c
# (new) -> 1.5 include/asm-i386/kirq.h
# (new) -> 1.1
drivers/fi/codesegments/Makefile
# (new) -> 1.3 include/asm-i386/kmmio.h
# (new) -> 1.8
drivers/fi/interceptors/pf/fi_pf.h
#
# The following is the BitKeeper ChangeSet Log
# --------------------------------------------
# 03/01/14 stanley@manticore.sh.intel.com 1.1018
# Merge from vanilla kernel tree by hand.
# --------------------------------------------
#
diff -Nru a/REPORTING-BUGS b/REPORTING-BUGS
--- a/REPORTING-BUGS Tue Jan 14 13:43:34 2003
+++ b/REPORTING-BUGS Tue Jan 14 13:43:34 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 Tue Jan 14 13:43:34 2003
+++ b/arch/i386/Kconfig Tue Jan 14 13:43:34 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 Tue Jan 14 13:43:34 2003
+++ b/arch/i386/kernel/Makefile Tue Jan 14 13:43:34 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 Tue Jan 14 13:43:34 2003
+++ b/arch/i386/kernel/entry.S Tue Jan 14 13:43:34 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 Tue Jan 14 13:43:34 2003
@@ -0,0 +1,123 @@
+/* Support for kernel irq interceptor.
+ (C) 2002 Stanley Wang <stanley.wang@intel.com>.
+*/
+
+#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 Tue Jan 14 13:43:34 2003
@@ -0,0 +1,156 @@
+/*
+ * KMMIO
+ * Benfit many code from kprobes
+ * (C) 2002 Louis Zhuang <louis.zhuang@intel.com>.
+ */
+#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 Tue Jan 14 13:43:34 2003
@@ -0,0 +1,160 @@
+/*
+ * Support for kernel probes.
+ * (C) 2002 Vamsi Krishna S <vamsi_krishna@in.ibm.com>.
+ */
+
+#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 Tue Jan 14 13:43:34 2003
+++ b/arch/i386/kernel/traps.c Tue Jan 14 13:43:34 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 Tue Jan 14 13:43:34 2003
+++ b/arch/i386/mm/fault.c Tue Jan 14 13:43:34 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 Tue Jan 14 13:43:34 2003
+++ b/drivers/Makefile Tue Jan 14 13:43:34 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 Tue Jan 14 13:43:34 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 Tue Jan 14 13:43:34 2003
@@ -0,0 +1,182 @@
+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
+ that ???
+
+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 Tue Jan 14 13:43:34 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 Tue Jan 14 13:43:34 2003
@@ -0,0 +1,72 @@
+/* Copyright (C) 2002 Louis Zhuang <louis.zhuang@intel.com> */
+#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 <louis.zhuang@intel.com>"
+#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 Tue Jan 14 13:43:34 2003
@@ -0,0 +1,949 @@
+/******************************************************************************
+ * Fault Injection Core Functionality
+ * Copyright (C) Intel Crop.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.
+ *
+
******************************************************************************
+ *
+ * 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 <louis.zhuang@intel.com>. 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 <louis.zhuang@intel.com>
+ * Stanley Wang <stanley.wang@intel.com>
+ * Rusty Lynch <rusty@linux.intel.com>
+ */
+
+#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;
+ return 0;
+}
+
+static inline int is_ready(struct trigger *t, unsigned int val)
+{
+ if ( is_in_random(t->hertz)
+ && is_in_range(val, t->min, t->max, t->bitmask)) {
+ return 1;
+ }
+ return 0;
+}
+
+static inline int is_in_count(int count, int skip, int stop)
+{
+ if (skip >= count) return 0;
+ if (stop!=0 && (stop+skip) <count) return 0;
+ return 1;
+}
+
+static inline int protect_bits(int new, int orig, int bitmask)
+{
+ return (new&(~bitmask)) | (orig&bitmask);
+}
+
+void fi_execute_trigger(struct trigger *t, struct interceptor *i,
+ __u32 val, int len, int type, void *data)
+{
+ int tmp = 0;
+
+ if (!t) {
+ err("Unable to execute null trigger");
+ return;
+ }
+
+ if(!is_ready(t,val)) return;
+
+ /* increase count only when all conditions have passed */
+ atomic_inc(&t->count);
+
+ if(!is_in_count(atomic_read(&t->count), t->skip, t->stop)) return;
+
+ dbg("count=%d, skip=%d, min=%d, max=%d, val=%d",
+ atomic_read(&t->count), t->skip, t->min, t->max, val);
+
+ if (!list_empty(&t->cs_list)) {
+ struct code_segment *pos;
+ list_for_each_entry (pos, &t->cs_list, list) {
+ pos->execute_trigger(t, i, val, len, type, data);
+ }
+ return;
+ }
+
+ //legacy trigger logic code
+ tmp = val;
+ switch (t->opcode) {
+ case TRIGGER_OPCODE_NOTHING:
+ goto exit_for;
+ case TRIGGER_OPCODE_SET:
+ tmp = t->operand;
+ break;
+ case TRIGGER_OPCODE_AND:
+ tmp &= t->operand;
+ break;
+ case TRIGGER_OPCODE_OR:
+ tmp |= t->operand;
+ break;
+ case TRIGGER_OPCODE_NOT:
+ tmp = ~tmp;
+ break;
+ case TRIGGER_OPCODE_XOR:
+ tmp ^= t->operand;
+ break;
+ case TRIGGER_OPCODE_NAND:
+ tmp = ~(tmp & t->operand);
+ break;
+ case TRIGGER_OPCODE_NOR:
+ tmp = ~(tmp | t->operand);
+ break;
+ case TRIGGER_OPCODE_ADD:
+ tmp = tmp + t->operand;
+ break;
+ case TRIGGER_OPCODE_SUB:
+ tmp = tmp - t->operand;
+ break;
+ default:
+ err("Not recognized opcode%d", t->opcode);
+ break;
+ }
+
+ exit_for:
+ val = protect_bits(tmp, val, t->protection);
+
+ i->corrupt(val, data);
+ return;
+}
+
+static int __init fi_init(void);
+int fi_register_interceptor(struct interceptor *i)
+{
+ int rv ;
+
+ /*
+ * If an interceptor is able to initialize
+ * before we are then do our own initialization
+ * before going on
+ */
+ if (!is_initialized)
+ fi_init();
+
+ dbg("registering interceptor %s", i->kobj.name);
+ i->kobj.kset = &interceptor_subsys.kset;
+ INIT_LIST_HEAD(&i->tri_list);
+ rv = kobject_register(&i->kobj);
+ if (rv) {
+ err("Unable to register kobject");
+ }
+ return rv;
+}
+
+void fi_unregister_interceptor(struct interceptor *i)
+{
+ del_triggers(&i->tri_list);
+ kobject_unregister(&i->kobj);
+}
+
+int fi_register_code_segment(struct code_segment *cs)
+{
+ int rv;
+
+ if (!is_initialized)
+ fi_init();
+
+ dbg("registering code segment %s", cs->kobj.name);
+ cs->kobj.kset = &code_segment_subsys.kset;
+ rv = kobject_register(&cs->kobj);
+ if (rv) {
+ err("Unable to register kobject");
+ }
+ create_code_segment_files(cs);
+ return 0;
+}
+
+void fi_unregister_code_segment(struct code_segment *cs)
+{
+ if (cs->tri) del_code_segment(cs);
+ remove_code_segment_files(cs);
+ kobject_unregister(&cs->kobj);
+}
+
+static int __init fi_init(void)
+{
+ down(&fi_sem);
+ if (is_initialized) {
+ dbg("alreading initialized... exiting");
+ goto Done;
+ }
+
+ dbg("initializing fault injection core");
+
+
+ interceptor_subsys.kset.subsys = &fi_subsys;
+ code_segment_subsys.kset.subsys = &fi_subsys;
+ trigger_subsys.kset.subsys = &fi_subsys;
+
+ subsystem_register(&fi_subsys);
+ subsys_create_file(&fi_subsys,&fi_subsys_attr_debug);
+ subsystem_register(&code_segment_subsys);
+ subsystem_register(&interceptor_subsys);
+ subsystem_register(&trigger_subsys);
+ subsys_create_file(&trigger_subsys,&trigger_subsys_attr_ctl);
+ is_initialized++;
+Done:
+ up(&fi_sem);
+ return 0;
+}
+
+static void __exit fi_exit(void)
+{
+ dbg("exit fault injection core");
+ subsys_remove_file(&trigger_subsys,&trigger_subsys_attr_ctl);
+ subsystem_unregister(&trigger_subsys);
+ subsystem_unregister(&interceptor_subsys);
+ subsystem_unregister(&code_segment_subsys);
+ subsys_remove_file(&fi_subsys,&fi_subsys_attr_debug);
+ subsystem_unregister(&fi_subsys);
+}
+
+/* Is there a better way to get my init code to execute on startup? */
+module_init(fi_init);
+module_exit(fi_exit);
+
+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);
+
+MODULE_PARM(fi_debug, "i");
+MODULE_PARM_DESC(fi_debug, "Debugging mode enabled or not");
+
+MODULE_LICENSE("GPL");
diff -Nru a/drivers/fi/interceptors/Makefile
b/drivers/fi/interceptors/Makefile
--- /dev/null Wed Dec 31 16:00:00 1969
+++ b/drivers/fi/interceptors/Makefile Tue Jan 14 13:43:34 2003
@@ -0,0 +1,9 @@
+#
+# Kernel Fault Injection Interceptors
+#
+
+EXTRA_AFLAGS := -traditional
+
+obj-$(CONFIG_FI_IRQ) += fi_irq.o
+obj-$(CONFIG_FI_DBP) += dbp/
+obj-$(CONFIG_FI_PF) += pf/
diff -Nru a/drivers/fi/interceptors/dbp/Makefile
b/drivers/fi/interceptors/dbp/Makefile
--- /dev/null Wed Dec 31 16:00:00 1969
+++ b/drivers/fi/interceptors/dbp/Makefile Tue Jan 14 13:43:34 2003
@@ -0,0 +1,9 @@
+#
+# Makefile for the linux kernel.
+#
+export-objs = fi_dbp.o
+
+obj-$(CONFIG_FI_DBP) += fi_dbp.o
+
+
+EXTRA_AFLAGS := -traditional
diff -Nru a/drivers/fi/interceptors/dbp/fi_dbp.c
b/drivers/fi/interceptors/dbp/fi_dbp.c
--- /dev/null Wed Dec 31 16:00:00 1969
+++ b/drivers/fi/interceptors/dbp/fi_dbp.c Tue Jan 14 13:43:34 2003
@@ -0,0 +1,563 @@
+/******************************************************************************
+ * Fault Injection Test harness (FI)
+ * Copyright (C) Intel Crop.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.
+ *
+
******************************************************************************
+ */
+
+/* $Id: fi_dbp.c,v 1.9 2002/11/19 08:03:04 brlock Exp $
+ * Copyright by Intel Crop., 2002
+ * Stanley Wang (stanley.wang@intel.com)
+ *
+ */
+
+/**
+ * @file dbp.c
+ */
+
+#include <linux/config.h>
+#include <linux/version.h>
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <asm/atomic.h>
+#include <asm/debugreg.h>
+#include <asm/uaccess.h>
+#include <linux/smp.h>
+#include <asm/processor.h>
+#include <linux/list.h>
+#include <linux/kprobes.h>
+
+#include <linux/fi.h>
+
+#define MAX_WP 32
+
+/*define reason of entering do_debug*/
+#define REASON_NOTME 0
+#define REASON_READ 1
+#define REASON_WRITE 2
+
+#define PREFIX_16 0x66
+
+struct opcode {
+ unsigned int port;
+ unsigned int write;
+ unsigned int len; /*0:byte 1:word 2:double word*/
+ unsigned int inst_len;
+};
+
+struct corrupt {
+ unsigned long *preg;
+ unsigned long mask;
+};
+
+struct inst_rec {
+ struct list_head list;
+ struct kprobe kprobe;
+};
+
+struct {
+ struct watchpoint wp;
+ struct trigger *trigger;
+ spinlock_t lock;
+}wp_tb[MAX_WP] = { [0 ... MAX_WP-1] = {{0,0}, NULL,
SPIN_LOCK_UNLOCKED}};
+
+struct {
+ int reason;
+ int wp_tab_id;
+ unsigned long value;
+ unsigned char cancel_opcode;
+ struct inst_rec *inst;
+ struct opcode op;
+}which_inst[NR_CPUS] = {[0 ... NR_CPUS-1] = {-1, -1, 0, 0, NULL, {0, 0,
0, 0}}};
+
+static DECLARE_MUTEX(dbp_sem);
+static LIST_HEAD(inst_rec_head);
+static struct interceptor dbp_interceptor;
+
+static int dbp_arm(struct trigger *t);
+void dbp_disarm(struct trigger *t);
+void dbp_corrupt(__u32 dirty, void *data);
+int get_IO_opcode(struct opcode *op, struct pt_regs *regs);
+
+int add_ins(const char *page);
+void clear_ins(void);
+
+void post_probe_handler(struct kprobe *, struct pt_regs *, unsigned
long);
+void pre_probe_handler(struct kprobe *, struct pt_regs *);
+void rmmod_exit(struct module *);
+
+
+static inline int find_wp(unsigned long addr){
+ int i=0;
+
+ for (i=0; i<MAX_WP; i++) {
+ if (addr == wp_tb[i].wp.addr) {
+ break;
+ }
+ }
+ return i;
+};
+
+static int dbp_arm(struct trigger *t)
+{
+ unsigned long flags;
+ int i=0;
+
+ if ( t->wp.type& (~FI_WPTYPE_ALL_MASK) ) {
+ err("Reason: %s\n", "/* There are reserved bits in use*/");
+ return -EINVAL;
+ }
+ if (!is_IO(t->wp.type)) {
+ err("Reason: %s\n", "/* Isn't I/O access*/");
+ return -EINVAL;
+ }
+ if ( (unsigned int)t->wp.addr>0xffff ) {
+ err("Reason: %s\n", "/* beyond 64k IO space*/");
+ return -EINVAL;
+ }
+
+
+ /* Make sure there are no the same wp or id */
+ for (i=0; i<MAX_WP; i++){
+ if (t->wp.addr==wp_tb[i].wp.addr) {
+ err("Reason: %s\n", "/*there are same wp*/");
+ return -EINVAL;
+ }
+ }
+
+find_empty_wp:
+ /* find empty item in wp_tb */
+ for (i=0; i<MAX_WP; i++){
+ if (wp_tb[i].wp.addr==0){
+ break;
+ }
+ }
+ if (i>=MAX_WP) return -EINVAL;
+
+ /*LOCK! NOTICE!!!*/
+ spin_lock_irqsave(&wp_tb[i].lock, flags);
+ if (wp_tb[i].wp.addr!=0) {
+ //if there is some one who changed the wp before we lock it
+ spin_unlock_irqrestore(&wp_tb[i].lock, flags);
+ goto find_empty_wp;
+ }
+
+ /* register this wp into wp_tb*/
+ wp_tb[i].wp.addr = t->wp.addr;
+ wp_tb[i].wp.type = t->wp.type;
+ wp_tb[i].trigger = t;
+
+ /* UNLOCK! NOTICE!!! */
+ spin_unlock_irqrestore(&wp_tb[i].lock, flags);
+ return 0;
+}
+
+void dbp_disarm(struct trigger *t)
+{
+ unsigned long flags;
+ int i = 0;
+
+find_wp_by_addr:
+ for (i=0; i<MAX_WP; i++) {
+ if (wp_tb[i].wp.addr == t->wp.addr) {
+ break;
+ }
+ }
+ if (i>=MAX_WP) return ;
+
+ /* LOCK */
+ spin_lock_irqsave(&wp_tb[i].lock, flags);
+ if (wp_tb[i].wp.addr!=t->wp.addr) {
+ spin_unlock_irqrestore(&wp_tb[i].lock, flags);
+ goto find_wp_by_addr;
+ }
+
+ wp_tb[i].wp.addr = 0;
+ wp_tb[i].wp.type = 0;
+ wp_tb[i].trigger = NULL;
+ /* UNLOCK */
+ spin_unlock_irqrestore(&wp_tb[i].lock, flags);
+ return;
+}
+
+void dbp_corrupt(__u32 dirty, void *data)
+{
+ unsigned long mask = ((struct corrupt*)data)->mask;
+ *(((struct corrupt*)data)->preg) &= ~mask;
+ *(((struct corrupt*)data)->preg) |= (dirty&mask);
+ dbg("corrupt data to:%#lX\n", dirty&mask);
+ return;
+}
+
+int get_IO_opcode(struct opcode *op, struct pt_regs *regs)
+{
+ unsigned char *p;
+ int flag_16=0;
+
+ p = (unsigned char*)(regs->eip);
+ if (*p == PREFIX_16) {
+ flag_16 = 1; /*word*/
+ p++;
+ }
+ switch(*p) {
+ case 0xec:
+ op->port = regs->edx;
+ op->write = 0;
+ op->len = 1; /*byte*/
+ op->inst_len = 1;
+ dbg("OPCODE: %lX\n",*(unsigned long*)p);
+ return 0;
+ case 0xed:
+ op->port = regs->edx;
+ op->write = 0;
+ if (flag_16) {
+ op->len = 2; /*word*/
+ op->inst_len = 2;
+ } else {
+ op->len = 4; /*double word*/
+ op->inst_len = 1;
+ }
+ dbg("OPCODE: %lX\n",*(unsigned long*)p);
+ return 0;
+ case 0xee:
+ op->port = regs->edx;
+ op->write = 1;
+ op->len = 1; /*byte*/
+ op->inst_len = 1;
+ dbg("OPCODE: %lX\n",*(unsigned long*)p);
+ return 0;
+ case 0xef:
+ op->port = regs->edx;
+ op->write = 1;
+ if (flag_16) {
+ op->len = 2; /*word*/
+ op->inst_len = 2;
+ } else {
+ op->len = 4; /*double word*/
+ op->inst_len = 1;
+ }
+ dbg("OPCODE: %lX\n",*(unsigned long*)p);
+ return 0;
+ case 0xe4:
+ op->port = *(++p);
+ op->write = 0;
+ op->len = 1; /*byte*/
+ op->inst_len = 2;
+ dbg("OPCODE: %lX\n",*(unsigned long*)p);
+ return 0;
+ case 0xe5:
+ op->port = *(++p);
+ op->write = 0;
+ if (flag_16) {
+ op->len = 2; /*word*/
+ op->inst_len = 3;
+ } else {
+ op->len = 4; /*double word*/
+ op->inst_len = 2;
+ }
+ dbg("OPCODE: %lX\n",*(unsigned long*)p);
+ return 0;
+ case 0xe6:
+ op->port = *(++p);
+ op->write = 1;
+ op->len = 1; /*byte*/
+ op->inst_len = 2;
+ dbg("OPCODE: %lX\n",*(unsigned long*)p);
+ return 0;
+ case 0xe7:
+ op->port = *(++p);
+ op->write = 1;
+ if (flag_16) {
+ op->len = 2; /*word*/
+ op->inst_len = 3;
+ } else {
+ op->len = 4; /*double word*/
+ op->inst_len = 2;
+ }
+ dbg("OPCODE: %lX\n",*(unsigned long*)p);
+ return 0;
+ default:
+ err("Can't analyze IO opcode! @%s\n", __FILE__);
+ return -EINVAL;
+ }
+
+ dbg("OPCODE: %lX\n",*(unsigned long*)p);
+ return 0;
+}
+
+void pre_probe_handler(struct kprobe *kprobe, struct pt_regs * regs)
+{
+ struct opcode op;
+ struct corrupt crpt;
+ int i;
+ unsigned long origin;
+ int cpu = smp_processor_id();
+
+ dbg("Enter pre_probe_handler, Instruction @ %lX\n",(unsigned
long)(regs->eip - 1));
+
+ *kprobe->addr = kprobe->opcode; /*restore the original opcode*/
+ regs->eip = (unsigned long)kprobe->addr; /*back step*/
+
+ i = get_IO_opcode(&op, regs);
+ if ( i < 0 ) {
+ err("Can't analyze opcode! @%s\n", __FILE__);
+ goto exit;
+ }
+
+ regs->eip += 1;
+
+ which_inst[cpu].op.port = op.port;
+ which_inst[cpu].op.write = op.write;
+ which_inst[cpu].op.inst_len = op.inst_len;
+ which_inst[cpu].value = regs->eax;
+
+ i=0;
+
+find_wp_by_addr:
+ i=find_wp((unsigned long)(op.port));/*XXX*/
+ if (i>=MAX_WP) {
+ /*not watched by me*/
+ which_inst[cpu].reason = REASON_NOTME;
+ goto exit;
+ }
+ /*LOCK!*/
+ spin_lock(&wp_tb[i].lock);
+ if (wp_tb[i].wp.addr!=(unsigned long)(op.port)) {
+ //there are others who modify this wp
+ spin_unlock(&wp_tb[i].lock);
+ goto find_wp_by_addr;
+ }
+ if (op.write){
+ if (!is_write(wp_tb[i].wp.type)) {
+ spin_unlock(&wp_tb[i].lock);
+ which_inst[cpu].reason = REASON_NOTME;
+ goto exit;
+ }
+ }else if (!is_read(wp_tb[i].wp.type)) {
+ spin_unlock(&wp_tb[i].lock);
+ which_inst[cpu].reason = REASON_NOTME;
+ goto exit;
+ }
+
+ which_inst[cpu].op.len = access_len(wp_tb[i].wp.type);
+ which_inst[cpu].wp_tab_id = i;
+
+ if (op.write) {
+ /*inject write fault*/
+ crpt.preg = &(regs->eax);
+ switch (which_inst[cpu].op.len) {
+ case 1:
+ crpt.mask =0xFF;
+ break;
+ case 2:
+ crpt.mask =0xFFFF;
+ break;
+ case 4:
+ crpt.mask =0xFFFFFFFF;
+ break;
+ default:
+ err("Wrong length!@%s\n", __FILE__);
+ break;
+ }
+ origin = (regs->eax)&crpt.mask;
+ fi_execute_trigger(wp_tb[i].trigger, &dbp_interceptor, origin, \
+ op.len, op.write, &crpt);
+ /*UNLOCK*/
+ spin_unlock(&wp_tb[i].lock);
+ which_inst[cpu].reason = REASON_WRITE;
+ }else {
+ /*inject read fault*/
+ which_inst[cpu].reason = REASON_READ;
+ }
+
+exit:
+ return;
+}
+
+void post_probe_handler(struct kprobe *kprobe, struct pt_regs *regs,
unsigned long flags)
+{
+ unsigned long origin;
+ struct corrupt crpt;
+ int cpu;
+
+ cpu = smp_processor_id();
+
+ dbg("Enter post_probe_handler @CPU%d\n", smp_processor_id());
+
+ switch (which_inst[cpu].reason) {
+ case REASON_WRITE:
+ regs->eax = which_inst[cpu].value;
+ case REASON_NOTME:
+ which_inst[cpu].reason = -1;
+ break;
+ case REASON_READ:
+ which_inst[cpu].reason = -1;
+
+ crpt.preg = &(regs->eax);
+ if(which_inst[cpu].op.len == 1) {
+ crpt.mask =0xFF;
+ }
+ if(which_inst[cpu].op.len == 2) {
+ crpt.mask =0xFFFF;
+ }
+ if(which_inst[cpu].op.len == 4) {
+ crpt.mask =0xFFFFFFFF;
+ }
+ if(which_inst[cpu].op.len > 4 || which_inst[cpu].op.len < 1) {
+ err("Wrong length!@%s\n", __FILE__);
+ }
+ origin = (regs->eax)&crpt.mask;
+ fi_execute_trigger(wp_tb[which_inst[cpu].wp_tab_id].trigger, \
+ &dbp_interceptor,origin, which_inst[cpu].op.len, \
+ which_inst[cpu].op.write, &crpt);
+ /*UNLOCK*/
+ spin_unlock(&wp_tb[which_inst[cpu].wp_tab_id].lock);
+ break;
+ default:
+ return;
+ }
+ return ;
+}
+
+int add_ins(const char *page)
+{
+ struct inst_rec *inst;
+ char tmp[16];
+ int num=0;
+ unsigned long addr=0;
+
+ num = sscanf(page, "%15s %lu", tmp, &addr);
+ if (num != 2) {
+ err("Invalid command format, translated no commands\n");
+ return -EINVAL;
+ }
+
+ inst=(struct inst_rec *)kmalloc(sizeof(struct inst_rec), GFP_KERNEL);
+ if (!inst) {
+ err("Allocate memory failed.\n");
+ return -ENOMEM;
+ }
+
+ inst->kprobe.addr = (kprobe_opcode_t *)addr;
+ inst->kprobe.pre_handler = pre_probe_handler;
+ inst->kprobe.post_handler = post_probe_handler;
+ inst->kprobe.fault_handler = NULL;
+ if ( register_kprobe(&(inst->kprobe)) < 0 )
+ {
+ kfree(inst);
+ err("register_kprobe failed @ %s\n", __FUNCTION__);
+ return -EINVAL;
+ }
+
+ list_add(&inst->list, &inst_rec_head);
+ return 0;
+}
+
+void clear_ins(void)
+{
+ struct inst_rec *inst;
+
+ while (!list_empty(&inst_rec_head)) {
+ inst=list_entry(inst_rec_head.next, struct inst_rec, list);
+ list_del(&inst->list);
+ unregister_kprobe(&inst->kprobe);
+ kfree(inst);
+ }
+}
+static ssize_t ctl_show(struct interceptor * p, char * page)
+{
+ return 0;
+}
+
+static ssize_t ctl_store(struct interceptor * p, const char * page,
+ size_t count)
+{
+ char ctl[16];
+ int num;
+ int error=0;
+
+ num = sscanf(page, "%15s", ctl);
+ if (!num) {
+ err("Invalid command format, translated no commands\n");
+ error = -EINVAL;
+ goto done;
+ }
+
+ down(&dbp_sem);
+ if (!strncmp(ctl, "add", 3)) {
+ error = add_ins(page);
+ } else if (!strncmp(ctl, "clear", 5)) {
+ clear_ins();
+ } else {
+ err("Unknow command %s\n", page);
+ error = -EINVAL;
+ }
+ up(&dbp_sem);
+
+done:
+ return error ? error : count;
+}
+
+static struct interceptor_attribute attr_ctl = {
+ .attr = { .name = "ctl", .mode = 0644 },
+ .show = ctl_show,
+ .store = ctl_store,
+};
+
+static struct interceptor dbp_interceptor = {
+ .kobj = {.name = "dbp_interceptor"},
+ .arm = dbp_arm,
+ .disarm = dbp_disarm,
+ .corrupt = dbp_corrupt
+};
+
+static int __init dbp_start(void)
+{
+ if (fi_register_interceptor(&dbp_interceptor)) {
+ dbg("Failed to register DBP Interceptor");
+ return -EINVAL;
+ }
+
+ sysfs_create_file(&dbp_interceptor.kobj, &attr_ctl.attr);
+
+ EXPORT_NO_SYMBOLS;
+ return 0;
+}
+
+static void __exit dbp_stop(void)
+{
+ clear_ins();
+ sysfs_remove_file(&dbp_interceptor.kobj, &attr_ctl.attr);
+ fi_unregister_interceptor(&dbp_interceptor);
+ return;
+}
+
+module_init(dbp_start);
+module_exit(dbp_stop);
+
+MODULE_AUTHOR("Stanley Wang");
+MODULE_DESCRIPTION("DBP component for FI.");
+MODULE_LICENSE("GPL");
diff -Nru a/drivers/fi/interceptors/fi_irq.c
b/drivers/fi/interceptors/fi_irq.c
--- /dev/null Wed Dec 31 16:00:00 1969
+++ b/drivers/fi/interceptors/fi_irq.c Tue Jan 14 13:43:34 2003
@@ -0,0 +1,243 @@
+/******************************************************************************
+ * Fault Injection Test harness Irq Interceptor(FI)
+ * Copyright (C) Intel Crop.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.
+ *
+
******************************************************************************
+ * Contributors:
+ * Louis Zhuang <louis.zhuang@intel.com>
+ * Stanley Wang <stanley.wang@intel.com>
+ * Rusty Lynch <rusty.lynch@intel.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+
+#include <linux/fi.h>
+#include <asm/kirq.h>
+
+struct fi_irq_faultset{
+ int irq_spurious_hertz;//0 means never do this
+ int irq_delay_hertz;
+ int irq_delay_time;//0 means lost
+ int num;
+ char devname[80];
+};
+
+static DECLARE_MUTEX(irq_sem);
+static int in_used=0;
+static struct fi_irq_faultset irq_fs[1];
+
+static int irq_spurious_count=0;
+static int irq_delay_count=0;
+static int irq_delay_time_count=0;
+static atomic_t irq_delay_num=ATOMIC_INIT(0);
+int fi_irq_handler(struct kirq *k, int num, void *id, struct pt_regs
*regs)
+{
+ if (num == 0) { //it is timer interrupt
+ dispatch_kirq(num, regs);//dispatch timer first
+
+ if (irq_fs->irq_spurious_hertz){
+ irq_spurious_count++;
+ if (irq_spurious_count>=irq_fs->irq_spurious_hertz){
+ irq_spurious_count=0;
+ dispatch_kirq(irq_fs->num, regs);
+ info("dispatch a spurious %s interrupt, num# %d\n",
irq_fs->devname, irq_fs->num);
+ }
+ }
+
+ if (atomic_read(&irq_delay_num)) {
+ irq_delay_time_count++;
+ if (irq_delay_time_count>=irq_fs->irq_delay_time) {
+ irq_delay_time_count=0;
+ atomic_dec(&irq_delay_num);
+ dispatch_kirq(irq_fs->num, regs);
+ info("dispatch a delayed %s interrupt, num# %d\n", irq_fs->devname,
irq_fs->num);
+ }
+ }
+ }else if (num == irq_fs->num) { //it is fake_irq
+ if (irq_fs->irq_delay_hertz) {
+ irq_delay_count++;
+ if (irq_delay_count>=irq_fs->irq_delay_hertz) {
+ if (irq_fs->irq_delay_time){
+ //FIXME:add spin lock
+ atomic_inc(&irq_delay_num);
+ info("announce a delayed %s interrupt, num# %d, jiffies: %ld\n",
+ irq_fs->devname, num, jiffies);
+ }else{
+ info("announce a lost %s interrupt, num# %d\n", irq_fs->devname,
num);
+ }
+ irq_delay_count=0;
+
+ } else {
+ dispatch_kirq(num, regs);//dispatch timer first
+ }
+ } else {
+ dispatch_kirq(num, regs);//dispatch timer first
+ }
+ }else{
+ err("error interrupt! num# %d\n", num);
+ }
+ return 1;
+}
+
+
+static int set_irq_faultset(struct fi_irq_faultset *f) {
+ int rv=0;
+
+ strncpy(irq_fs->devname, f->devname, 80);
+ irq_fs->num = f->num;
+ irq_fs->irq_spurious_hertz = f->irq_spurious_hertz;
+ irq_fs->irq_delay_hertz = f->irq_delay_hertz;
+ irq_fs->irq_delay_time = f->irq_delay_time;
+
+ if ( register_kirq(irq_fs->num, irq_fs->devname, fi_irq_handler) != 0)
{
+ dbg("set irq faultset fail@%s, irq=%d, devname=%s, handler=%p\n",
+ __FUNCTION__, irq_fs->num, irq_fs->devname, fi_irq_handler);
+ rv = -EINVAL;
+ goto exit;
+ }
+ if ( register_kirq(0, "timer", fi_irq_handler) != 0) {
+ dbg("set irq faultset fail@%s, irq=0, devname=timer, handler=%p\n",
+ __FUNCTION__, fi_irq_handler);
+ rv = -EINVAL;
+ }
+
+exit:
+ return rv;
+};
+static void clear_irq_faultset(void)
+{
+ unregister_kirq(0);
+ unregister_kirq(irq_fs->num);
+
+ irq_fs->num =0;
+ irq_fs->irq_spurious_hertz =0;
+ irq_fs->irq_delay_hertz =0;
+ irq_fs->irq_delay_time =0;
+
+ /* clear vars used by fi_irq_handler */
+ irq_spurious_count=0;
+ irq_delay_count=0;
+ irq_delay_time_count=0;
+ atomic_set(&irq_delay_num, 0);
+};
+
+static ssize_t ctl_show(struct interceptor * p, char * page)
+{
+ return sprintf( page,
+ "IRQ Faultset:\n"
+ "Device Name = %s\n"
+ "IRQ number = %d\n"
+ "Spurious Hertz = %d\n"
+ "Delay Hertz = %d\n"
+ "Delay Time = %d\n\n",
+ irq_fs->num ? irq_fs->devname : "(NULL)",
+ irq_fs->num, irq_fs->irq_spurious_hertz,
+ irq_fs->irq_delay_hertz,
+ irq_fs->irq_delay_time);
+}
+
+static ssize_t ctl_store(struct interceptor * p, const char * page,
size_t count)
+{
+ char ctl[16];
+ int num;
+ int error = 0;
+ struct fi_irq_faultset tmp;
+
+ /* 3 4 5 6 7 8 */
+ /* ctl irq dn sh dh dt */
+ num = sscanf(page,"%15s %i %s %i %i %i",
+ ctl, /* 3 */
+ &tmp.num, /* 4 */
+ tmp.devname, /* 5 */
+ &tmp.irq_spurious_hertz, /* 6 */
+ &tmp.irq_delay_hertz, /* 7 */
+ &tmp.irq_delay_time); /* 8 */
+ if (!num) {
+ err("Invalid command format, translated no commands");
+ error = -EINVAL;
+ goto Done;
+ }
+
+ down(&irq_sem);
+ if (!strcmp(ctl,"add") && num == 6){
+ if (tmp.num == 0) {
+ err("Intercept Timer IRQ is not permitted!\n");
+ error = -EACCES;
+ } else if ((in_used == 0) && ((error = set_irq_faultset(&tmp)) == 0))
{
+ in_used++;
+ } else {
+ err("Wrong IRQ faultset OR IRQ_INTERCEPTOR is in used!\n");
+ error = -EINVAL;
+ }
+ } else if (!strcmp(ctl,"del") && num == 1) {
+ if (in_used == 1) {
+ clear_irq_faultset();
+ in_used--;
+ } else {
+ err("IRQ_INTERCEPTOR is not in used!\nNO irq faultset to
remove.\n");
+ error = -EINVAL;
+ }
+ } else {
+ err("Invalid command format: %s, %i tokens found",ctl,num);
+ error = -EINVAL;
+ }
+ up(&irq_sem);
+
+Done:
+ return error ? error : count;
+}
+
+static struct interceptor_attribute attr_ctl = {
+ .attr = { .name = "ctl", .mode = 0644 },
+ .show = ctl_show,
+ .store = ctl_store
+};
+
+static struct interceptor irq_interceptor = {
+ .kobj = {.name = "irq_interceptor"},
+ .arm = NULL,
+ .disarm = NULL,
+ .corrupt = NULL
+};
+
+static int __init fi_irq_init(void)
+{
+ if (fi_register_interceptor(&irq_interceptor)) {
+ dbg("Failed to register irq Interceptor");
+ return -EINVAL;
+ }
+
+ sysfs_create_file(&irq_interceptor.kobj, &attr_ctl.attr);
+ return 0;
+}
+
+static void __exit fi_irq_exit (void)
+{
+ sysfs_remove_file(&irq_interceptor.kobj, &attr_ctl.attr);
+ fi_unregister_interceptor(&irq_interceptor);
+}
+
+module_init(fi_irq_init);
+module_exit(fi_irq_exit);
+
+
+MODULE_DESCRIPTION("Implementation for inject fault into IRQ");
+MODULE_LICENSE("GPL");
+
diff -Nru a/drivers/fi/interceptors/pf/Makefile
b/drivers/fi/interceptors/pf/Makefile
--- /dev/null Wed Dec 31 16:00:00 1969
+++ b/drivers/fi/interceptors/pf/Makefile Tue Jan 14 13:43:34 2003
@@ -0,0 +1,10 @@
+#
+# Makefile for the linux kernel.
+#
+export-objs = fi_pf.o
+
+obj-$(CONFIG_FI_PF) += fi_pf.o
+fi_pf-objs := pf.o pf_in.o pf_utils.o
+
+
+EXTRA_AFLAGS := -traditional
diff -Nru a/drivers/fi/interceptors/pf/fi_pf.h
b/drivers/fi/interceptors/pf/fi_pf.h
--- /dev/null Wed Dec 31 16:00:00 1969
+++ b/drivers/fi/interceptors/pf/fi_pf.h Tue Jan 14 13:43:34 2003
@@ -0,0 +1,50 @@
+/******************************************************************************
+ * Fault Injection Test harness (FI)
+ * Copyright (C) Intel Crop.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.
+ *
+
******************************************************************************
+ */
+
+#ifndef __PF_H_
+#define __PF_H_
+
+/*
+ * Delcare typedefs
+ */
+enum reason_type{
+ REG_READ = 0, /* read from addr to reg */
+ REG_WRITE = 1, /* write from reg to addr */
+ IMM_WRITE = 2, /* write from imm to addr */
+ OTHERS = 3 /* Other instructions can not intercept */
+};
+
+/*
+ * Declare functions in pf_in.c
+ */
+enum reason_type get_ins_type(unsigned long ins_addr);
+unsigned int get_ins_width(unsigned long ins_addr);
+unsigned long get_ins_reg_val(unsigned long ins_addr, struct pt_regs
*regs);
+void set_ins_reg_val(unsigned long addr, struct pt_regs *regs, unsigned
long val);
+unsigned long get_ins_imm_val(unsigned long ins_addr);
+void set_ins_imm_val(unsigned long ins_addr, unsigned long val);
+
+/*
+ * Declare functions in pf_utils.c
+ */
+unsigned long fi_phy_to_virt(unsigned long phy_addr);
+
+#endif//__PF_H_
diff -Nru a/drivers/fi/interceptors/pf/pf.c
b/drivers/fi/interceptors/pf/pf.c
--- /dev/null Wed Dec 31 16:00:00 1969
+++ b/drivers/fi/interceptors/pf/pf.c Tue Jan 14 13:43:34 2003
@@ -0,0 +1,284 @@
+/******************************************************************************
+ * Fault Injection Test harness (FI)
+ * Copyright (C) Intel Crop.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.
+ *
+
******************************************************************************
+ */
+
+/* $Id: pf.c,v 1.2 2002/11/14 06:19:49 yzhuang Exp $
+ * Copyright by Intel Crop., 2002
+ * Louis Zhuang (louis.zhuang@intel.com)
+ */
+
+/**
+ * @file pf.c
+ */
+
+#include <linux/config.h>
+#include <linux/version.h>
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/kmmio.h>
+#include <asm/pgalloc.h>
+#include <asm/io.h>
+#include <asm/highmem.h>
+
+#include <linux/fi.h>
+
+#include "fi_pf.h"
+
+static struct interceptor pf_interceptor;
+
+/* Wrapping trigger within the structure */
+struct pf_trigger {
+ struct kmmio_probe probe;
+ struct trigger *trigger;
+ unsigned long eip;
+ enum reason_type type;
+ unsigned long val;
+};
+
+/**
+ * This is a KEY data structure. When interceptor triggers DM
+ * and then DM call iAction method. iAction remembers this corrupted
data.
+ * Then DM returns to Interceptor and Interceptor returns corrupted
data
+ * to intercepted access.
+ */
+struct pf_corrupt{
+ __u32 corrupted_data;
+};
+
+static unsigned long crpt_mask[] = { 0x000000FF, 0x0000FFFF,
0x00FFFFFF, 0xFFFFFFFF};
+
+extern void fi_execute_trigger(struct trigger *,struct interceptor *,
+ __u32,int,int,void *);
+void pf_page_fault(struct kmmio_probe *p, struct pt_regs *regs,
unsigned long address)
+{
+ struct pf_trigger *pft;
+ struct pf_corrupt crpt;
+ int ins_len, crpt_len;
+
+ pft = container_of(p, struct pf_trigger, probe);;
+
+ if (pft->probe.addr != (void*)address)
+ BUG();
+
+ pft->eip = regs->eip;
+ pft->type = get_ins_type(pft->eip);
+
+ dbg("watching %p, type=%d\n", pft->probe.addr, pft->type);
+
+ switch (pft->type) {
+ case OTHERS:
+ dbg("Hmm? Can not analyze the instruction, *eip=0x%08lx\n",
+ *(unsigned long *)pft->eip);
+ break;
+ case REG_WRITE:
+ dbg("reg_write instruction eip=0x%08lx\n",
+ pft->eip);
+ if (is_write(pft->trigger->wp.type)) {
+
+ ins_len = get_ins_width(pft->eip);
+ pft->val = get_ins_reg_val(pft->eip, regs);
+
+ crpt_len = (WP_LEN(pft->trigger->wp)<ins_len)?
+ WP_LEN(pft->trigger->wp):ins_len;
+ crpt.corrupted_data =
+ pft->val & crpt_mask[crpt_len-1];
+ fi_execute_trigger(pft->trigger,
+ &pf_interceptor,
+ crpt.corrupted_data,
+ crpt_len, 1, &crpt);
+ set_ins_reg_val(pft->eip, regs,
+ (crpt.corrupted_data&crpt_mask[crpt_len-1])
+ | (pft->val&(~crpt_mask[crpt_len-1])));
+ }
+ break;
+ case IMM_WRITE:
+ dbg("imm_write instruction eip=0x%08lx\n",
+ pft->eip);
+ if (is_write(pft->trigger->wp.type)) {
+
+ ins_len = get_ins_width(pft->eip);
+ pft->val = get_ins_imm_val(pft->eip);
+
+ crpt_len = (WP_LEN(pft->trigger->wp)<ins_len)?
+ WP_LEN(pft->trigger->wp):ins_len;
+ crpt.corrupted_data = pft->val & crpt_mask[crpt_len-1];
+ fi_execute_trigger(pft->trigger,
+ &pf_interceptor,
+ crpt.corrupted_data,
+ crpt_len, 1, &crpt);
+
+ set_ins_imm_val(pft->eip,
+ (crpt.corrupted_data&crpt_mask[crpt_len-1])
+ | (pft->val&(~crpt_mask[crpt_len-1])));
+ }
+ break;
+ case REG_READ:
+ dbg("reg_read instruction eip=0x%08lx\n",
+ pft->eip);
+ /* we should trigger it in do_debug*/
+ break;
+ default:
+ err("error when getting instruction eip=0x%08lx\n",
+ pft->eip);
+ break;
+ }
+}
+
+void pf_post_page_fault(struct kmmio_probe *p, unsigned long condition,
struct pt_regs * regs)
+{
+ struct pf_trigger *pft;
+ struct pf_corrupt crpt;
+ int ins_len, crpt_len;
+
+ pft = container_of(p, struct pf_trigger, probe);
+ switch (pft->type) {
+ case OTHERS:
+ dbg("can not analyze the instruction cond=0x%08lx\n", condition);
+ break;
+ case REG_WRITE:
+ dbg( "restore regval after reg_write corrupting, val=0x%08lx\n",
+ pft->val);
+ if (is_write(pft->trigger->wp.type)) {
+ set_ins_reg_val(pft->eip, regs, pft->val);
+ }
+ break;
+ case IMM_WRITE:
+ dbg("restore immval after imm_write corrputing, val=0x%08lx\n",
+ pft->val);
+ if (is_write(pft->trigger->wp.type)) {
+ set_ins_imm_val(pft->eip, pft->val);
+ }
+ break;
+ case REG_READ:
+ dbg("corrupt reg after reg_read! cond=0x%08lx\n", condition);
+
+ if (is_read(pft->trigger->wp.type)) {
+
+ ins_len = get_ins_width(pft->eip);
+ pft->val = get_ins_reg_val(pft->eip, regs);
+
+ crpt_len = (WP_LEN(pft->trigger->wp)<ins_len)?
+ WP_LEN(pft->trigger->wp):ins_len;
+ crpt.corrupted_data = pft->val & crpt_mask[crpt_len-1];
+ fi_execute_trigger(pft->trigger,
+ &pf_interceptor,
+ crpt.corrupted_data,
+ crpt_len, 0, &crpt);
+
+ set_ins_reg_val(pft->eip, regs,
+ (crpt.corrupted_data&crpt_mask[crpt_len-1])
+ | (pft->val&(~crpt_mask[crpt_len-1])));
+ }
+ break;
+ default:
+ err("should never reach here! cond=0x%08lx\n", condition);
+ break;
+ }
+};
+
+static int pf_arm(struct trigger *t)
+{
+ unsigned long addr;
+ struct pf_trigger *pft;
+
+ /* Make sure that wp is vaild */
+ if ( t==NULL ) {
+ err("Reason: %s\n", "/* There are no trigger */");
+ return -1;
+ }
+ if ( t->wp.type& (~FI_WPTYPE_ALL_MASK) ) {
+ err("Reason: %s\n", "/* There are reserved bits in use*/");
+ return -1;
+ }
+
+ addr = fi_phy_to_virt(t->wp.addr);
+
+ if ( !(is_MMIO(t->wp.type) && addr) ) {
+ err("Reason: %s\n", "Address is not present.");
+ return -1;
+ }
+
+ pft = kmalloc(sizeof(*pft), GFP_KERNEL);
+
+ /* date trigger points to pft, so we can find it in unregister */
+ t->data = pft;
+
+ /* register this trigger */
+ pft->probe.addr = (void *)addr;
+ pft->probe.pre_handler = pf_page_fault;
+ pft->probe.post_handler = pf_post_page_fault;
+ pft->trigger = t;
+
+ return register_kmmio_probe(&pft->probe);
+};
+
+
+static void pf_disarm(struct trigger *t)
+{
+ struct pf_trigger *pft;
+
+ pft = t->data;
+ unregister_kmmio_probe(&pft->probe);
+ kfree(pft);
+};
+
+static void corrupt(__u32 dirty, void *data)
+{
+ ((struct pf_corrupt *)data)->corrupted_data = dirty;
+};
+
+static struct interceptor pf_interceptor = {
+ .kobj = {.name = "pf"},
+ .arm = &pf_arm,
+ .disarm = &pf_disarm,
+ .corrupt = &corrupt
+};
+
+static int __init pf_init(void)
+{
+ return fi_register_interceptor(&pf_interceptor);
+}
+
+static void __exit pf_exit(void)
+{
+ fi_unregister_interceptor(&pf_interceptor);
+}
+
+module_init(pf_init);
+module_exit(pf_exit);
+
+MODULE_DESCRIPTION("Pagefault interceptor component for FI");
+MODULE_LICENSE("GPL");
diff -Nru a/drivers/fi/interceptors/pf/pf_in.c
b/drivers/fi/interceptors/pf/pf_in.c
--- /dev/null Wed Dec 31 16:00:00 1969
+++ b/drivers/fi/interceptors/pf/pf_in.c Tue Jan 14 13:43:34 2003
@@ -0,0 +1,453 @@
+/******************************************************************************
+ * Fault Injection Test harness (FI)
+ * Copyright (C) Intel Crop.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.
+ *
+
******************************************************************************
+ */
+
+/* $Id: pf_in.c,v 1.1.1.1 2002/11/12 05:56:32 brlock Exp $
+ * Copyright by Intel Crop., 2002
+ * Louis Zhuang (louis.zhuang@intel.com)
+ */
+
+/**
+ * @file pf_hooks.c
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <asm/io.h>
+
+#include <linux/fi.h>
+
+#include "fi_pf.h"
+
+#define NR_ELEMENTS(array) (sizeof(array)/sizeof(array[0]))
+
+/* IA32 Manual 3, 2-1 */
+static unsigned char prefix_codes[] = {
+ 0xF0, 0xF2, 0xF3, 0x2E, 0x36, 0x3E, 0x26, 0x64,
+ 0x65, 0x2E, 0x3E, 0x66, 0x67
+};
+
+int skip_prefix(unsigned char *addr, int *shorted)
+{
+ int i=0;
+ unsigned char *p=addr;
+ *shorted=0;
+
+ restart:
+ for (i=0; i<NR_ELEMENTS(prefix_codes); i++) {
+ if (*p == prefix_codes[i]){
+ if (*p == 0x66) *shorted = 1;
+ p++;
+ goto restart;
+ }
+ }
+ return p-addr;
+};
+
+int get_opcode(unsigned char *addr, unsigned int *opcode)
+{
+ int rv;
+ if (*addr == 0x0F){/*0x0F is extension instruction*/
+ *opcode = *(unsigned short*)addr;
+ rv=2;
+ }else{
+ *opcode = *addr;
+ rv=1;
+ }
+ return rv;
+};
+
+/* IA32 Manual 3, 3-432*/
+static unsigned int reg_rop[] = {0x8A, 0x8B, 0xB60F, 0xB70F, 0xBE0F,
0xBF0F};
+static unsigned int reg_wop[] = {0x88, 0x89};
+static unsigned int imm_wop[] = {0xC6, 0xC7};
+
+enum reason_type get_ins_type(unsigned long ins_addr)
+{
+ unsigned int opcode;
+ unsigned char *p;
+ int shorted=0;
+ int i=0;
+ enum reason_type rv=OTHERS;
+
+ p = (unsigned char *)ins_addr;
+ p += skip_prefix(p, &shorted);
+ p += get_opcode(p, &opcode);
+
+ for (i=0; i<NR_ELEMENTS(reg_rop); i++) {
+ if (reg_rop[i]==opcode) {
+ rv = REG_READ;
+ goto exit;
+ }
+ }
+
+ for (i=0; i<NR_ELEMENTS(reg_wop); i++) {
+ if (reg_wop[i]==opcode) {
+ rv = REG_WRITE;
+ goto exit;
+ }
+ }
+
+ for (i=0; i<NR_ELEMENTS(imm_wop); i++) {
+ if (imm_wop[i]==opcode) {
+ rv = IMM_WRITE;
+ goto exit;
+ }
+ }
+
+ exit:
+ return rv;
+};
+
+/* IA32 Manual 3, 3-432*/
+static unsigned int w8[] = {0x88, 0x8A, 0xC6};
+static unsigned int w32[]= {0x89, 0x8B, 0xC7, 0xB60F, 0xB70F, 0xBE0F,
0xBF0F};
+unsigned int get_ins_width(unsigned long ins_addr)
+{
+ unsigned int opcode;
+ unsigned char *p;
+ int shorted=0;
+ int i=0;
+
+ p = (unsigned char *)ins_addr;
+ p += skip_prefix(p, &shorted);
+ p += get_opcode(p, &opcode);
+
+ for (i=0; i<NR_ELEMENTS(w8); i++) {
+ if (w8[i]==opcode) {
+ return 1;
+ }
+ }
+ for (i=0; i<NR_ELEMENTS(w32); i++) {
+ if (w32[i]==opcode) {
+ if (shorted) {
+ return 2;
+ } else {
+ return 4;
+ }
+ }
+ }
+
+ err("Unknow opcode=0x%02x\n", opcode);
+ return 0;
+};
+
+/* define register ident in mod/rm byte after undefine it in ptrace*/
+#undef EAX
+#define EAX 0
+#define ECX 1
+#define EDX 2
+#undef EBX
+#define EBX 3
+#define ESP 4
+#define EBP 5
+#undef ESI
+#define ESI 6
+#undef EDI
+#define EDI 7
+
+#define AL 0
+#define CL 1
+#define DL 2
+#define BL 3
+#define AH 4
+#define CH 5
+#define DH 6
+#define BH 7
+
+unsigned char *get_reg_w8(int no, struct pt_regs *regs)
+{
+ unsigned char *rv=NULL;
+
+ switch (no) {
+ case AL:
+ rv = (unsigned char *)®s->eax;
+ break;
+ case BL:
+ rv = (unsigned char *)®s->ebx;
+ break;
+ case CL:
+ rv = (unsigned char *)®s->ecx;
+ break;
+ case DL:
+ rv = (unsigned char *)®s->edx;
+ break;
+ case AH:
+ rv = 1+(unsigned char *)®s->eax;
+ break;
+ case BH:
+ rv = 1+(unsigned char *)®s->ebx;
+ break;
+ case CH:
+ rv = 1+(unsigned char *)®s->ecx;
+ break;
+ case DH:
+ rv = 1+(unsigned char *)®s->edx;
+ break;
+ default:
+ err("Error reg no# %d\n", no);
+ break;
+ }
+ return rv;
+};
+
+unsigned long *get_reg_w32(int no, struct pt_regs *regs)
+{
+ unsigned long *rv=NULL;
+
+ switch (no) {
+ case EAX:
+ rv = ®s->eax;
+ break;
+ case EBX:
+ rv = ®s->ebx;
+ break;
+ case ECX:
+ rv = ®s->ecx;
+ break;
+ case EDX:
+ rv = ®s->edx;
+ break;
+ case ESP:
+ rv = ®s->esp;
+ break;
+ case EBP:
+ rv = ®s->ebp;
+ break;
+ case ESI:
+ rv = ®s->esi;
+ break;
+ case EDI:
+ rv = ®s->edi;
+ break;
+ default:
+ err("Error reg no# %d\n", no);
+ }
+ return rv;
+};
+
+unsigned long get_ins_reg_val(unsigned long ins_addr, struct pt_regs
*regs)
+{
+ unsigned int opcode;
+ unsigned char mod_rm;
+ int reg;
+ unsigned char *p;
+ int shorted=0;
+ int i=0;
+ unsigned long rv;
+
+ p = (unsigned char *)ins_addr;
+ p += skip_prefix(p, &shorted);
+ p += get_opcode(p, &opcode);
+ for (i=0; i<NR_ELEMENTS(reg_rop); i++) {
+ if (reg_rop[i]==opcode) {
+ rv = REG_READ;
+ goto do_work;
+ }
+ }
+
+ for (i=0; i<NR_ELEMENTS(reg_wop); i++) {
+ if (reg_wop[i]==opcode) {
+ rv = REG_WRITE;
+ goto do_work;
+ }
+ }
+ err("Not a register instruction, opcode=0x%02x\n", opcode);
+ goto err;
+ do_work:
+ mod_rm = *p;
+ reg = (mod_rm>>3)&0x7;
+ if (get_ins_width(ins_addr)==1) {
+ return *get_reg_w8(reg, regs);
+ }else if (get_ins_width(ins_addr)==2) {
+ return *(unsigned short*)get_reg_w32(reg, regs);
+ }else if (get_ins_width(ins_addr)==4) {
+ return *get_reg_w32(reg, regs);
+ }else{
+ err("Error width# %d\n", reg);
+ goto err;
+ }
+ err:
+ return 0;
+};
+
+
+void set_ins_reg_val(unsigned long ins_addr, struct pt_regs *regs,
unsigned long val)
+{
+ unsigned int opcode;
+ unsigned char mod_rm;
+ int reg;
+ unsigned char *p;
+ int shorted=0;
+ int i=0;
+ unsigned long rv;
+
+ p= (unsigned char *)ins_addr;
+ p += skip_prefix(p, &shorted);
+ p += get_opcode(p, &opcode);
+ for (i=0; i<NR_ELEMENTS(reg_rop); i++) {
+ if (reg_rop[i]==opcode) {
+ rv = REG_READ;
+ goto do_work;
+ }
+ }
+
+ for (i=0; i<NR_ELEMENTS(reg_wop); i++) {
+ if (reg_wop[i]==opcode) {
+ rv = REG_WRITE;
+ goto do_work;
+ }
+ }
+ err("Not a register instruction, opcode=0x%02x\n", opcode);
+ goto err;
+ do_work:
+ mod_rm = *p;
+ reg = (mod_rm>>3)&0x7;
+ if (get_ins_width(ins_addr)==1) {
+ *get_reg_w8(reg, regs) = val;
+ }else if (get_ins_width(ins_addr)==2) {
+ *(unsigned short*)get_reg_w32(reg, regs) = val;
+ }else if (get_ins_width(ins_addr)==4) {
+ *get_reg_w32(reg, regs) = val;
+ }else{
+ err("Error width, reg=%d\n", reg);
+ goto err;
+ }
+ err:
+ return;
+
+};
+
+unsigned long get_ins_imm_val(unsigned long ins_addr)
+{
+ unsigned int opcode;
+ unsigned char mod_rm;
+ unsigned char mod;
+ unsigned char *p;
+ int shorted=0;
+ int i=0;
+ unsigned long rv;
+
+ p= (unsigned char *)ins_addr;
+ p += skip_prefix(p, &shorted);
+ p += get_opcode(p, &opcode);
+ for (i=0; i<NR_ELEMENTS(imm_wop); i++) {
+ if (imm_wop[i]==opcode) {
+ rv = IMM_WRITE;
+ goto do_work;
+ }
+ }
+ err("Not a imm instruction, opcode=0x%02x\n", opcode);
+ goto err;
+ do_work:
+ mod_rm = *p;
+ mod = mod_rm>>6;
+ p++;
+ switch (mod) {
+ case 0:
+ break;
+ case 1:
+ p += 1;
+ break;
+ case 2:
+ p += 4;
+ case 3:
+ default:
+ err("it is not a memory access instruction, rm_mod=0x%02x\n",
mod_rm);
+ }
+ if (get_ins_width(ins_addr)==1) {
+ return *(unsigned char *)p;
+ }else if (get_ins_width(ins_addr)==2) {
+ return *(unsigned short*)p;
+ }else if (get_ins_width(ins_addr)==4) {
+ return *(unsigned long*)p;
+ }else{
+ err("Error width%s\n", ".");
+ goto err;
+ }
+ err:
+ return 0;
+};
+
+void set_ins_imm_val(unsigned long ins_addr, unsigned long val)
+{
+ unsigned int opcode;
+ unsigned char mod_rm;
+ unsigned char mod;
+ unsigned char *p;
+ int shorted=0;
+ int i=0;
+ unsigned long rv;
+
+ p= (unsigned char *)ins_addr;
+ p += skip_prefix(p, &shorted);
+ p += get_opcode(p, &opcode);
+ for (i=0; i<NR_ELEMENTS(imm_wop); i++) {
+ if (imm_wop[i]==opcode) {
+ rv = IMM_WRITE;
+ goto do_work;
+ }
+ }
+ err("Not a imm instruction, opcode=0x%02x\n", opcode);
+ goto err;
+ do_work:
+ mod_rm = *p;
+ mod = mod_rm>>6;
+ p++;
+ switch (mod) {
+ case 0:
+ break;
+ case 1:
+ p += 1;
+ break;
+ case 2:
+ p += 4;
+ break;
+ case 3:
+ default:
+ err("it is not a memory access instruction, rm_mod=0x%02x\n",
mod_rm);
+ }
+ if (get_ins_width(ins_addr)==1) {
+ *(unsigned char *)p = val;
+ }else if (get_ins_width(ins_addr)==2) {
+ *(unsigned short*)p = val;
+ }else if (get_ins_width(ins_addr)==4) {
+ *(unsigned long*)p = val;
+ }else{
+ err("Error width%s\n", ".");
+ goto err;
+ }
+ err:
+ return;
+};
diff -Nru a/drivers/fi/interceptors/pf/pf_utils.c
b/drivers/fi/interceptors/pf/pf_utils.c
--- /dev/null Wed Dec 31 16:00:00 1969
+++ b/drivers/fi/interceptors/pf/pf_utils.c Tue Jan 14 13:43:34 2003
@@ -0,0 +1,78 @@
+/******************************************************************************
+ * Fault Injection Test harness (FI)
+ * Copyright (C) Intel Crop.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.
+ *
+
******************************************************************************
+ */
+
+/* $Id: pf_utils.c,v 1.3 2002/11/14 07:11:40 brlock Exp $
+ * Copyright by Intel Crop., 2002
+ * Stanley Wang (stanley.wang@intel.com)
+ */
+
+/**
+ * @file pf_utils.c
+ */
+
+#include <linux/config.h>
+#include <linux/version.h>
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <asm/io.h>
+#include <linux/highmem.h>
+#include <asm/pgtable.h>
+
+
+#include <linux/fi.h>
+
+#include "fi_pf.h"
+
+unsigned long fi_phy_to_virt(unsigned long phy_addr)
+{
+ pgd_t *pgd;
+ pmd_t *pmd;
+ pte_t *pte;
+ unsigned long virt_addr;
+
+ for (virt_addr=VMALLOC_START; virt_addr<VMALLOC_END;
+ virt_addr+=PAGE_SIZE ) {
+ pgd = pgd_offset_k(virt_addr);
+ if ( !(pgd_val(*pgd)&_PAGE_PRESENT) ) {
+ continue;
+ };
+ pmd = pmd_offset(pgd, virt_addr);
+ if (pmd_large(*pmd)) {
+ pte = (pte_t *)pmd;
+ } else {
+ pte = pte_offset_kernel(pmd, virt_addr);
+ }
+ if ( (pte_val(*pte)&PAGE_MASK) == (phy_addr&PAGE_MASK) ) {
+ return virt_addr + (phy_addr&(~PAGE_MASK));
+ };
+ }
+ return 0;
+};
diff -Nru a/drivers/fi/testing/Makefile b/drivers/fi/testing/Makefile
--- /dev/null Wed Dec 31 16:00:00 1969
+++ b/drivers/fi/testing/Makefile Tue Jan 14 13:43:34 2003
@@ -0,0 +1,9 @@
+#
+# Kernel Fault Injection Interceptors
+#
+
+EXTRA_AFLAGS := -traditional
+
+obj-$(CONFIG_FI_MOCK_INTERCEPTOR) += fi_mock_interceptor.o
+obj-$(CONFIG_FI_MOCK_CODESEGMENT) += fi_mock_cs.o
+obj-$(CONFIG_FI_TEST) += fi_test.o
diff -Nru a/drivers/fi/testing/fi_mock_cs.c
b/drivers/fi/testing/fi_mock_cs.c
--- /dev/null Wed Dec 31 16:00:00 1969
+++ b/drivers/fi/testing/fi_mock_cs.c Tue Jan 14 13:43:34 2003
@@ -0,0 +1,41 @@
+/* Copyright (C) 2002 Louis Zhuang <louis.zhuang@intel.com> */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/kobject.h>
+#include <linux/fi.h>
+
+#define DRIVER_AUTHOR "Louis Zhuang <louis.zhuang@intel.com>"
+#define DRIVER_DESC "Fault Injection Mock Code Segment"
+
+void mock_execute(struct trigger *t, struct interceptor *i,
+ __u32 val, int len, int type, void *data)
+{
+ info("tri=%s, intcpt=%s, val=%i, len=%i, type=%i, data=%p",
+ t->kobj.name, i->kobj.name, val, len, type,
+ data);
+}
+
+static struct code_segment mock_code_segment = {
+ .execute_trigger = mock_execute,
+ .kobj = {.name="mock"}
+};
+
+static int __init mock_init(void)
+{
+ if (fi_register_code_segment(&mock_code_segment)) {
+ err("Failed to register Mock Code Segment\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void __exit mock_exit(void)
+{
+ fi_unregister_code_segment(&mock_code_segment);
+}
+module_init(mock_init);
+module_exit(mock_exit);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
diff -Nru a/drivers/fi/testing/fi_mock_interceptor.c
b/drivers/fi/testing/fi_mock_interceptor.c
--- /dev/null Wed Dec 31 16:00:00 1969
+++ b/drivers/fi/testing/fi_mock_interceptor.c Tue Jan 14 13:43:34 2003
@@ -0,0 +1,120 @@
+/*
+ * Fault Injection Mock Interceptor Driver
+ * ---------------------------------------
+ *
+ * This is a fault injection interceptor interceptor created
+ * for the sole purpose of testing the core fault injection code.
+ * By loading this driver a new interceptor named 'mock_interceptor'
+ * will appear in the list of available interceptors.
+ *
+ * When a trigger is loaded that utilizes this interceptor:
+ * - you should be able to trace of the interceptor <==> core
+ * by looking at dmesg
+ * - a new file called 'trip' will created in the trigger directory
+ * that when written to will trip the trigger
+ *
+ * In addition to this, the type of intercepor (interceptor_type_t)
+ * that this moc implementation claims to be when it registers
+ * with the fault injection core can be controled with the
+ * 'mock_type' parameter.
+ *
+ * For example, to pretend to be a a INTERCEPTOR_TYPE_PIO
+ * interceptor (which is really just the number '2' as defined
+ * in the fi.h enum declaration), you would load:
+ * $ insmod fi_mock_interceptor mod_type=2
+ *
+ * You can find out what the current type is by reading the
+ * 'type' file in the mock_interceptor directory.
+ * (i.e. 'cat fault_injection/interceptors/mock_interceptor/type')
+ *
+ * Copyright (C) 2002 Rusty Lynch <rusty@linux.intel.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/kobject.h>
+#include <linux/fi.h>
+
+#define DRIVER_AUTHOR "Rusty Lynch <rusty@linux.intel.com>"
+#define DRIVER_DESC "Fault Injection Mock Interceptor"
+
+static struct interceptor mock_interceptor;
+
+static ssize_t trip_store(struct trigger * p, const char * page,
+ size_t count)
+{
+ trace("%s, %p, %i", p ? p->kobj.name:"NULL",page,count);
+ fi_execute_trigger(p, &mock_interceptor, 0, 0, 0, 0);
+ return count;
+}
+static struct trigger_attribute attr_trip = {
+ .attr = { .name = "trip", .mode = 0644 },
+ .store = &trip_store,
+};
+
+static int arm(struct trigger *t)
+{
+ trace("%s", t ? t->kobj.name : "NULL");
+ if (!t) {
+ dbg("Null trigger passed into arming function!");
+ return -EINVAL;
+ }
+
+ sysfs_create_file(&t->kobj, &attr_trip.attr);
+ return 0;
+};
+
+static void disarm(struct trigger *t)
+{
+ trace("%s", t ? t->kobj.name : "NULL");
+ if (!t) {
+ dbg("Null trigger passed into disarming function!");
+ return;
+ }
+
+ sysfs_remove_file(&t->kobj, &attr_trip.attr);
+ return;
+};
+
+static void corrupt(__u32 dirty, void *data)
+{
+ trace("%ul, %p", dirty, data);
+ return;
+};
+
+static struct interceptor mock_interceptor = {
+ .kobj = {.name = "mock"},
+ .arm = &arm,
+ .disarm = &disarm,
+ .corrupt = &corrupt
+};
+
+static int __init mock_init(void)
+{
+ trace();
+
+ if (fi_register_interceptor(&mock_interceptor)) {
+ dbg("Failed to register Mock Interceptor");
+
+ /* FIXME!! For some reason kobject_register */
+ /* is returning an error when none occured */
+ /*return -EINVAL;*/
+ }
+ return 0;
+}
+
+static void __exit mock_exit (void)
+{
+ trace();
+ fi_unregister_interceptor(&mock_interceptor);
+}
+
+module_init(mock_init);
+module_exit(mock_exit);
+
+MODULE_PARM_DESC(mock_type, "(interceptor_type_t) Type of
interceptor");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
diff -Nru a/drivers/fi/testing/fi_test.c b/drivers/fi/testing/fi_test.c
--- /dev/null Wed Dec 31 16:00:00 1969
+++ b/drivers/fi/testing/fi_test.c Tue Jan 14 13:43:34 2003
@@ -0,0 +1,304 @@
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <asm/io.h>
+#include <linux/vmalloc.h>
+#include <linux/kobject.h>
+#include <linux/sysfs.h>
+
+
+#define LOG_TIME printk(KERN_INFO "FI_TEST: %ld jiffies = %ld
\n",global_counter++,jiffies)
+
+#if !defined(CONFIG_FI_TEST_MODULE)
+#define MY_NAME "fi_test"
+#else
+#define MY_NAME THIS_MODULE->name
+#endif
+
+#define dbg(format, arg...) \
+ do { \
+ if(debug) \
+ printk (KERN_DEBUG "%s: " format "\n", \
+ MY_NAME , ## arg); \
+ } while(0)
+
+
+#define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME
, ## arg)
+#define info(format, arg...) printk(KERN_INFO "#%ld %s : " format "\n",
global_counter++, MY_NAME , ## arg)
+#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n",
MY_NAME , ## arg)
+
+
+#define PP_BASE 0x378
+#define MM_BASE 0x00001000
+
+static int debug;
+
+void *mmio_address;
+volatile unsigned long global_counter = 0;
+ssize_t io_show(struct subsystem *s, char *page);
+ssize_t io_store(struct subsystem *s, const char *page, size_t count);
+
+ssize_t mmio_show(struct subsystem *s, char *page);
+ssize_t mmio_store(struct subsystem *s, const char *page, size_t
count);
+
+struct subsys_attribute io =
+{
+ .attr = { .name = "io", .mode = 0644 },
+ .show = io_show,
+ .store = io_store,
+};
+
+struct subsys_attribute mmio =
+{
+ .attr = { .name = "mmio", .mode = 0644 },
+ .show = mmio_show,
+ .store = mmio_store,
+};
+
+ssize_t fith_test_show(struct kobject *k, struct attribute *attr,
+ char *page)
+{
+ return 0;
+}
+
+ssize_t fith_test_store(struct kobject *kobj, struct attribute *attr,
+ const char *page, size_t count)
+{
+ return count;
+}
+
+struct sysfs_ops fith_test_ops =
+{
+ .show = fith_test_show,
+ .store = fith_test_store,
+};
+
+struct kobj_type ktype_fith_test = {
+ .sysfs_ops = &fith_test_ops
+};
+
+decl_subsys(fith_test, &ktype_fith_test);
+
+int print_global_counter(char *page, char **start, off_t off, int
count,
+ int *eof, void *data)
+{
+ sprintf(page,"%ld\n",global_counter);
+ return strlen(page);
+}
+
+void do_io_access(int write, int type)
+{
+ unsigned char value=0;
+ if (write)
+ info("Write 0X5A* to parellel port register!\n");
+ else
+ info("Expect read 0X5A* from parellel port register!\n");
+
+ switch (type){
+ case 0:
+ outb(0x5a, PP_BASE);
+ value = inb(PP_BASE);
+ break;
+ case 1:
+ outw(0x5a, PP_BASE);
+ value = inw(PP_BASE);
+ break;
+ case 2:
+ outl(0x5a, PP_BASE);
+ value = inl(PP_BASE);
+ break;
+ case 4:
+ outw_p(0x5a, PP_BASE);
+ value = inw_p(PP_BASE);
+ break;
+ case 5:
+ outl_p(0x5a, PP_BASE);
+ value = inl_p(PP_BASE);
+ break;
+ default:
+ break;
+ }
+ if (write)
+ info("%#X was write to parellel port register!\n",value);
+ else
+ info("%#X was read from parellel port register!\n",value);
+}
+
+void do_mm_io_access(int write, int type)
+{
+ unsigned long value=0;
+ if (write)
+ info("Write 0X5A* to MMI/O address");
+ else
+ info("Expect read 0X5A* from MMI/O address");
+
+ switch(type){
+ case 0:
+ writeb(0x5a,mmio_address);
+ value = readb(mmio_address);
+ break;
+ case 1:
+ writew(0x5a5a,mmio_address);
+ value = readw(mmio_address);
+ break;
+ case 2:
+ writel(0x5a5a5a5a,mmio_address);
+ value = readl(mmio_address);
+ break;
+ default:
+ break;
+ }
+ if (write)
+ info("%#lX was write to MMI/O address", value);
+ else
+ info("%#lX was read from MMI/O address", value);
+
+}
+
+ssize_t io_show(struct subsystem *s, char *page)
+{
+ return 0;
+}
+
+ssize_t io_store(struct subsystem *s, const char *page, size_t count)
+{
+ char cmd[16];
+ char len[16];
+ int num = 0;
+ int error = 0;
+
+ num = sscanf(page, "%15s %15s", cmd, len);
+
+ if (num < 0) {
+ err("Unknow command format!");
+ error = -EINVAL;
+ goto done;
+ }
+
+ LOG_TIME;
+ if (!strncmp(cmd, "read", 4)) {
+ if (!strncmp(len, "byte", 4)) {
+ info("COMMAND_IO_READ");
+ do_io_access(0,0);
+ } else if (!strncmp(len, "word", 4)) {
+ info("COMMAND_IO_READW");
+ do_io_access(0,1);
+ } else if (!strncmp(len, "long", 4)) {
+ info("COMMAND_IO_READL");
+ do_io_access(0,2);
+ } else {
+ err("Invalid length!");
+ error = -EINVAL;
+ }
+ } else if (!strncmp(cmd, "write", 5)) {
+ if (!strncmp(len, "byte", 4)) {
+ info("COMMAND_IO_WRITE");
+ do_io_access(1,0);
+ } else if (!strncmp(len, "word", 4)) {
+ info("COMMAND_IO_WRITEW");
+ do_io_access(1,1);
+ } else if (!strncmp(len, "long", 4)) {
+ info("COMMAND_IO_WRITEL");
+ do_io_access(1,2);
+ } else {
+ err("Invalid length!");
+ error = -EINVAL;
+ }
+ } else {
+ err("Unknow command!");
+ error = -EINVAL;
+ }
+done:
+ return error ? error : count;
+}
+
+ssize_t mmio_show(struct subsystem *s, char *page)
+{
+ return 0;
+}
+
+ssize_t mmio_store(struct subsystem *s, const char *page,
+ size_t count)
+{
+ char cmd[16];
+ char len[16];
+ int num = 0;
+ int error = 0;
+
+ num = sscanf(page, "%15s %15s", cmd, len);
+
+ if (num < 0) {
+ err("Unknow command format!");
+ error = -EINVAL;
+ goto done;
+ }
+
+ LOG_TIME;
+ if (!strncmp(cmd, "read", 4)) {
+ if (!strncmp(len, "byte", 4)) {
+ info("COMMAND_MMIO_READ");
+ do_mm_io_access(0,0);
+ } else if (!strncmp(len, "word", 4)) {
+ info("COMMAND_MMIO_READW");
+ do_mm_io_access(0,1);
+ } else if (!strncmp(len, "long", 4)) {
+ info("COMMAND_MMIO_READL");
+ do_mm_io_access(0,2);
+ } else {
+ err("Invalid length!");
+ error = -EINVAL;
+ }
+ } else if (!strncmp(cmd, "write", 5)) {
+ if (!strncmp(len, "byte", 4)) {
+ info("COMMAND_MMIO_WRITE");
+ do_mm_io_access(1,0);
+ } else if (!strncmp(len, "word", 4)) {
+ info("COMMAND_MMIO_WRITEW");
+ do_mm_io_access(1,1);
+ } else if (!strncmp(len, "long", 4)) {
+ info("COMMAND_MMIO_WRITEL");
+ do_mm_io_access(1,2);
+ } else {
+ err("Invalid length!");
+ error = -EINVAL;
+ }
+ } else {
+ err("Unknow command!");
+ error = -EINVAL;
+ }
+done:
+ return error ? error : count;
+}
+
+static int __init fi_test_init(void)
+{
+ dbg("Initialize fith_test.");
+ subsystem_register(&fith_test_subsys);
+ subsys_create_file(&fith_test_subsys, &mmio);
+ subsys_create_file(&fith_test_subsys, &io);
+
+ mmio_address = ioremap(MM_BASE, 0x8);
+ info("mmio_address = %p", mmio_address);
+ *(unsigned long *)mmio_address = 0xf0f0;
+ mb();
+
+ return 0;
+}
+
+static void __exit fi_test_exit (void)
+{
+ dbg("Cleanup fith_test.");
+ subsys_remove_file(&fith_test_subsys, &io);
+ subsys_remove_file(&fith_test_subsys, &mmio);
+ subsystem_unregister(&fith_test_subsys);
+ iounmap(mmio_address);
+}
+
+module_init(fi_test_init);
+module_exit(fi_test_exit);
+
+MODULE_PARM(debug, "i");
+MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
+MODULE_AUTHOR("Stanley Wang");
+MODULE_DESCRIPTION("Fault Injection test driver.");
+MODULE_LICENSE("GPL");
diff -Nru a/include/asm-i386/kirq.h b/include/asm-i386/kirq.h
--- /dev/null Wed Dec 31 16:00:00 1969
+++ b/include/asm-i386/kirq.h Tue Jan 14 13:43:34 2003
@@ -0,0 +1,27 @@
+#ifndef _ASM_KIRQ_H
+#define _ASM_KIRQ_H
+
+#include <linux/errno.h>
+
+/* Define return value for kirq handler. */
+#define KIRQ_CONTINUE 0
+#define KIRQ_SKIP 1
+
+struct kirq;
+typedef int (*kirq_handler_t)(struct kirq *, int, void *, struct
pt_regs *);
+struct kirq {
+ void *dev_id;
+ void (*isr)(int, void *, struct pt_regs *);
+ kirq_handler_t handler;
+};
+
+#ifdef CONFIG_KIRQ
+extern int register_kirq(int irq, char *devname, kirq_handler_t
handler);
+extern int unregister_kirq(int irq);
+extern void dispatch_kirq(int irq, struct pt_regs* regs);
+#else
+int register_kirq(int irq, char *devname, kirq_handler_t handler) {
return -ENOSYS; }
+int unregister_kirq(int irq) { return -ENOSYS; }
+void dispatch_kirq(int irq, struct pt_regs* regs) {}
+#endif /*CONFIG_KIRQ*/
+#endif /*_ASM_KIRQ_H*/
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 Tue Jan 14 13:43:34 2003
@@ -0,0 +1,25 @@
+/*
+ * Benfit many code from kprobes
+ * (C) 2002 Louis Zhuang <louis.zhuang@intel.com>.
+ */
+
+#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);
+extern void *kmmio_invert_map(void *virt_addr, unsigned long bus_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/asm-i386/kprobes.h b/include/asm-i386/kprobes.h
--- /dev/null Wed Dec 31 16:00:00 1969
+++ b/include/asm-i386/kprobes.h Tue Jan 14 13:43:34 2003
@@ -0,0 +1,34 @@
+#ifndef _ASM_KPROBES_H
+#define _ASM_KPROBES_H
+/*
+ * Dynamic Probes (kprobes) support
+ * Vamsi Krishna S <vamsi_krishna@in.ibm.com>, July, 2002
+ * Mailing list: dprobes@www-124.ibm.com
+ */
+#include <linux/types.h>
+#include <linux/ptrace.h>
+
+struct pt_regs;
+
+typedef u8 kprobe_opcode_t;
+#define BREAKPOINT_INSTRUCTION 0xcc
+
+/* trap3/1 are intr gates for kprobes. So, restore the status of IF,
+ * if necessary, before executing the original int3/1 (trap) handler.
+ */
+static inline void restore_interrupts(struct pt_regs *regs)
+{
+ if (regs->eflags & IF_MASK)
+ __asm__ __volatile__ ("sti");
+}
+
+#ifdef CONFIG_KPROBES
+extern int kprobe_fault_handler(struct pt_regs *regs, int trapnr);
+extern int post_kprobe_handler(struct pt_regs *regs);
+extern int kprobe_handler(struct pt_regs *regs);
+#else /* !CONFIG_KPROBES */
+static inline int kprobe_fault_handler(struct pt_regs *regs, int
trapnr) { return 0; }
+static inline int post_kprobe_handler(struct pt_regs *regs) { return 0;
}
+static inline int kprobe_handler(struct pt_regs *regs) { return 0; }
+#endif
+#endif /* _ASM_KPROBES_H */
diff -Nru a/include/linux/fi.h b/include/linux/fi.h
--- /dev/null Wed Dec 31 16:00:00 1969
+++ b/include/linux/fi.h Tue Jan 14 13:43:34 2003
@@ -0,0 +1,155 @@
+#ifndef _FAULT_INJECTION_H
+#define _FAULT_INJECTION_H
+#if defined(__KERNEL__)
+
+#include <linux/kobject.h>
+
+/**
+ * define uniform watchpoint structure.
+ */
+#define FI_WPTYPE_MMIO 4
+#define FI_WPTYPE_READ 8
+#define FI_WPTYPE_WRITE 16
+#define FI_WPTYPE_EXEC 32
+
+#define FI_WPTYPE_LEN_MASK 3
+#define FI_WPTYPE_ALL_MASK ( FI_WPTYPE_MMIO \
+ | FI_WPTYPE_READ \
+ | FI_WPTYPE_WRITE \
+ | FI_WPTYPE_EXEC \
+ | FI_WPTYPE_LEN_MASK \
+ )
+
+struct watchpoint{
+ /*address intercepted.*/
+ unsigned long addr;
+ /**
+ * intercepted type.
+ * bit 0-1 2^N bytes leng.
+ * bit 2 IO-space IO/MMIO
+ * bit 3 Read
+ * bit 4 Write
+ * bit 5 Execute(not implementation yet)
+ * XXX: you can not register the same address with IO and MMIO,
+ * but MMIO seldom uses low 64k space, so it is not a problem in
fact.*/
+ unsigned int type;
+};
+
+#define WP_LEN(wp) (1<<((unsigned long)(wp.type)&3))
+typedef enum
+{
+ TRIGGER_OPCODE_NOTHING=0,
+ TRIGGER_OPCODE_SET=1,
+ TRIGGER_OPCODE_AND=2,
+ TRIGGER_OPCODE_OR=3,
+ TRIGGER_OPCODE_NOT=4,
+ TRIGGER_OPCODE_XOR=5,
+ TRIGGER_OPCODE_NAND=6,
+ TRIGGER_OPCODE_NOR=7,
+ TRIGGER_OPCODE_ADD=8,
+ TRIGGER_OPCODE_SUB=9,
+} trigger_opcode_t;
+
+struct trigger;
+struct interceptor;
+struct code_segment {
+ struct list_head list;
+ void (*execute_trigger) (struct trigger *t,
+ struct interceptor *i,
+ __u32 val,
+ int len,
+ int type,
+ void *data);
+ struct trigger *tri;
+ struct kobject kobj;
+};
+
+struct trigger {
+ struct list_head list;
+ struct watchpoint wp;
+ __u32 bitmask;
+ int min;
+ int max;
+ int skip;
+ int stop;
+ int protection;
+ int hertz;
+ int registered;
+ struct list_head cs_list;
+ struct interceptor *intcpt;
+ atomic_t count;
+ /* opaque data struct for register */
+ void *data;
+ struct kobject kobj;
+
+ /* these two fields is legacy */
+ trigger_opcode_t opcode;
+ int operand;
+};
+
+struct interceptor {
+ int (*arm) (struct trigger *);
+ void (*disarm) (struct trigger *);
+ void (*corrupt)(__u32, void *);
+ struct list_head tri_list;
+ struct kobject kobj;
+};
+
+struct code_segment_attribute {
+ struct attribute attr;
+ ssize_t (*show) (struct code_segment *,char *);
+ ssize_t (*store) (struct code_segment *,const char *, size_t);
+};
+
+struct trigger_attribute {
+ struct attribute attr;
+ ssize_t (*show) (struct trigger *,char *);
+ ssize_t (*store)(struct trigger *,const char *, size_t);
+};
+
+struct interceptor_attribute {
+ struct attribute attr;
+ ssize_t (*show) (struct interceptor *,char *);
+ ssize_t (*store)(struct interceptor *,const char *, size_t);
+};
+
+
+extern int fi_register_interceptor(struct interceptor *);
+extern void fi_unregister_interceptor(struct interceptor *i);
+extern int fi_register_code_segment(struct code_segment *);
+extern void fi_unregister_code_segment(struct code_segment *);
+extern void fi_execute_trigger(struct trigger *t, struct interceptor
*i,
+ __u32 val, int len, int type, void *data);
+
+#endif /* __KERNEL__ */
+
+#ifdef CONFIG_FI_DEBUG
+extern int fi_debug;
+#define dbg(format, arg...) \
+ do { \
+ if(fi_debug) \
+ printk (KERN_DEBUG "%s: " format "\n", \
+ __FUNCTION__, ## arg); \
+ } while(0)
+#else
+#define dbg(format, arg...)
+#endif /* CONFIG_FI_DEBUG */
+
+#define err(format, arg...) \
+ printk(KERN_ERR "%s: " format "\n", __FUNCTION__ , ##
arg)
+#define info(format, arg...) \
+ printk(KERN_INFO "%s: " format "\n", __FUNCTION__ , ##
arg)
+#define warn(format, arg...) \
+ printk(KERN_WARNING "%s: " format "\n", __FUNCTION__ ,
## arg)
+#define trace(format, arg...) \
+ printk(KERN_INFO "%s(" format ")\n", __FUNCTION__ , ##
arg)
+
+/*define macros for WP type*/
+inline static int is_MMIO(int type) { return (type&0x04)?1:0; };
+inline static int is_IO(int type) { return (type&0x04)?0:1; };
+inline static int is_read(int type) { return (type&0x08)?1:0; };
+inline static int is_write(int type) { return (type&0x10)?1:0; };
+inline static int is_execute(int type) { return (type&0x20)?1:0; };
+inline static int access_len(int type) { return 1UL<<(type&0x03);};
+
+#endif /* _FAULT_INJECTION_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 Tue Jan 14 13:43:34 2003
@@ -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 -Nru a/include/linux/kprobes.h b/include/linux/kprobes.h
--- /dev/null Wed Dec 31 16:00:00 1969
+++ b/include/linux/kprobes.h Tue Jan 14 13:43:34 2003
@@ -0,0 +1,60 @@
+#ifndef _LINUX_KPROBES_H
+#define _LINUX_KPROBES_H
+#include <linux/config.h>
+#include <linux/list.h>
+#include <linux/notifier.h>
+#include <linux/smp.h>
+#include <asm/kprobes.h>
+
+struct kprobe;
+struct pt_regs;
+
+typedef void (*kprobe_pre_handler_t)(struct kprobe *, struct pt_regs
*);
+typedef void (*kprobe_post_handler_t)(struct kprobe *, struct pt_regs
*,
+ unsigned long flags);
+typedef int (*kprobe_fault_handler_t)(struct kprobe *, struct pt_regs
*,
+ int trapnr);
+
+struct kprobe {
+ struct list_head list;
+
+ /* location of the probe point */
+ kprobe_opcode_t *addr;
+
+ /* Called before addr is executed. */
+ kprobe_pre_handler_t pre_handler;
+
+ /* Called after addr is executed, unless... */
+ kprobe_post_handler_t post_handler;
+
+ /* ... called if executing addr causes a fault (eg. page fault).
+ * Return 1 if it handled fault, otherwise kernel will see it. */
+ kprobe_fault_handler_t fault_handler;
+
+ /* Saved opcode (which has been replaced with breakpoint) */
+ kprobe_opcode_t opcode;
+};
+
+#ifdef CONFIG_KPROBES
+/* Locks kprobe: irq must be disabled */
+void lock_kprobes(void);
+void unlock_kprobes(void);
+
+/* kprobe running now on this CPU? */
+static inline int kprobe_running(void)
+{
+ extern unsigned int kprobe_cpu;
+ return kprobe_cpu == smp_processor_id();
+}
+
+/* Get the kprobe at this addr (if any). Must have called lock_kprobes
*/
+struct kprobe *get_kprobe(void *addr);
+
+int register_kprobe(struct kprobe *p);
+void unregister_kprobe(struct kprobe *p);
+#else
+static inline int kprobe_running(void) { return 0; }
+static inline int register_kprobe(struct kprobe *p) { return -ENOSYS; }
+static inline void unregister_kprobe(struct kprobe *p) { }
+#endif
+#endif /* _LINUX_KPROBES_H */
diff -Nru a/kernel/Makefile b/kernel/Makefile
--- a/kernel/Makefile Tue Jan 14 13:43:34 2003
+++ b/kernel/Makefile Tue Jan 14 13:43:34 2003
@@ -1,10 +1,10 @@
-#
+#}{
# Makefile for the linux kernel.
#
export-objs = signal.o sys.o kmod.o workqueue.o ksyms.o pm.o
exec_domain.o \
printk.o suspend.o dma.o module.o cpufreq.o \
- profile.o rcupdate.o intermodule.o params.o
+ profile.o rcupdate.o intermodule.o params.o kprobes.o kmmio.o
obj-y = sched.o fork.o exec_domain.o panic.o printk.o profile.o \
exit.o itimer.o time.o softirq.o resource.o \
@@ -22,6 +22,8 @@
obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o
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 <alan@linuxcare.com.au>, the
-fno-omit-frame-pointer is
diff -Nru a/kernel/kmmio.c b/kernel/kmmio.c
--- /dev/null Wed Dec 31 16:00:00 1969
+++ b/kernel/kmmio.c Tue Jan 14 13:43:34 2003
@@ -0,0 +1,147 @@
+/* Support for MMIO probes.
+ * Benfit many code from kprobes
+ * (C) 2002 Louis Zhuang <louis.zhuang@intel.com>.
+*/
+
+#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)]);
+
+ if (add_kmmio_fault_page(p->addr)) {
+ printk(KERN_ERR "Unable to set page fault\n");
+ }
+
+ 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_ATOMIC);
+ if (!f) return -1;
+ 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);
diff -Nru a/kernel/kprobes.c b/kernel/kprobes.c
--- /dev/null Wed Dec 31 16:00:00 1969
+++ b/kernel/kprobes.c Tue Jan 14 13:43:34 2003
@@ -0,0 +1,89 @@
+/* Support for kernel probes.
+ (C) 2002 Vamsi Krishna S <vamsi_krishna@in.ibm.com>.
+*/
+#include <linux/kprobes.h>
+#include <linux/spinlock.h>
+#include <linux/hash.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <asm/cacheflush.h>
+#include <asm/errno.h>
+
+#define KPROBE_HASH_BITS 6
+#define KPROBE_TABLE_SIZE (1 << KPROBE_HASH_BITS)
+
+static struct list_head kprobe_table[KPROBE_TABLE_SIZE];
+
+unsigned int kprobe_cpu = NR_CPUS;
+static spinlock_t kprobe_lock = SPIN_LOCK_UNLOCKED;
+
+/* Locks kprobe: irqs must be disabled */
+void lock_kprobes(void)
+{
+ spin_lock(&kprobe_lock);
+ kprobe_cpu = smp_processor_id();
+}
+
+void unlock_kprobes(void)
+{
+ kprobe_cpu = NR_CPUS;
+ spin_unlock(&kprobe_lock);
+}
+
+/* You have to be holding the kprobe_lock */
+struct kprobe *get_kprobe(void *addr)
+{
+ struct list_head *head, *tmp;
+
+ head = &kprobe_table[hash_ptr(addr, KPROBE_HASH_BITS)];
+ list_for_each(tmp, head) {
+ struct kprobe *p = list_entry(tmp, struct kprobe, list);
+ if (p->addr == addr)
+ return p;
+ }
+ return NULL;
+}
+
+int register_kprobe(struct kprobe *p)
+{
+ int ret = 0;
+
+ spin_lock_irq(&kprobe_lock);
+ if (get_kprobe(p->addr)) {
+ ret = -EEXIST;
+ goto out;
+ }
+ list_add(&p->list, &kprobe_table[hash_ptr(p->addr,
KPROBE_HASH_BITS)]);
+
+ p->opcode = *p->addr;
+ *p->addr = BREAKPOINT_INSTRUCTION;
+ flush_icache_range(p->addr, p->addr + sizeof(kprobe_opcode_t));
+ out:
+ spin_unlock_irq(&kprobe_lock);
+ return ret;
+}
+
+void unregister_kprobe(struct kprobe *p)
+{
+ spin_lock_irq(&kprobe_lock);
+ *p->addr = p->opcode;
+ list_del(&p->list);
+ flush_icache_range(p->addr, p->addr + sizeof(kprobe_opcode_t));
+ spin_unlock_irq(&kprobe_lock);
+}
+
+static int __init init_kprobes(void)
+{
+ int i;
+
+ /* FIXME allocate the probe table, currently defined statically */
+ /* initialize all list heads */
+ for (i = 0; i < KPROBE_TABLE_SIZE; i++)
+ INIT_LIST_HEAD(&kprobe_table[i]);
+
+ return 0;
+}
+__initcall(init_kprobes);
+
+EXPORT_SYMBOL_GPL(register_kprobe);
+EXPORT_SYMBOL_GPL(unregister_kprobe);
-------------------------------------------------------
This SF.NET email is sponsored by: FREE SSL Guide from Thawte
are you planning your Web Server Security? Click here to get a FREE
Thawte SSL guide and find the answers to all your SSL security issues.
http://ads.sourceforge.net/cgi-bin/redirect.pl?thaw0026en
_______________________________________________
Fault-injection-developer mailing list
Fault-injection-developer@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/fault-injection-developer
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic