[Fault-injection-developer] RFC: fi_dbp
Status: Alpha
Brought to you by:
rustyl
From: Wang, S. <sta...@in...> - 2002-12-31 10:10:33
|
Hi, folks Here is the fi_dbp: # 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.961 -> 1.977 # drivers/char/fi/Makefile 1.9 -> 1.10 # (new) -> 1.1 drivers/char/fi/dbp/Makefile # (new) -> 1.1 drivers/char/fi/dbp/fi_dbp.h # (new) -> 1.5 drivers/char/fi/dbp/fi_dbp.c # # The following is the BitKeeper ChangeSet Log # -------------------------------------------- # 02/12/27 st...@ma... 1.964 # Add FI_DBP # -------------------------------------------- # 02/12/30 st...@ma... 1.966 # Change name : "attach" -> "ctl" # -------------------------------------------- # 02/12/30 st...@ma... 1.967 # Bug fix for fi_dbp # -------------------------------------------- # 02/12/30 st...@ma... 1.968 # Merge bk://fau...@fa.../linux-2.5 # into manticore.sh.intel.com:/home/stanley/fi-bkbits # -------------------------------------------- # 02/12/31 st...@ma... 1.968.1.1 # Bug fix for fi_dbp # -------------------------------------------- # 02/12/31 st...@ma... 1.976 # Merge bk://fau...@fa.../linux-2.5 # into manticore.sh.intel.com:/home/stanley/fi-bkbits # -------------------------------------------- # 02/12/31 st...@ma... 1.977 # Remove interceptor type # -------------------------------------------- # diff -Nru a/drivers/char/fi/Makefile b/drivers/char/fi/Makefile --- a/drivers/char/fi/Makefile Tue Dec 31 17:43:41 2002 +++ b/drivers/char/fi/Makefile Tue Dec 31 17:43:41 2002 @@ -3,6 +3,7 @@ # obj-y += pf/ +obj-y += dbp/ EXTRA_AFLAGS := -traditional diff -Nru a/drivers/char/fi/dbp/Makefile b/drivers/char/fi/dbp/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/char/fi/dbp/Makefile Tue Dec 31 17:43:41 2002 @@ -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/char/fi/dbp/fi_dbp.c b/drivers/char/fi/dbp/fi_dbp.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/char/fi/dbp/fi_dbp.c Tue Dec 31 17:43:41 2002 @@ -0,0 +1,630 @@ +/************************************************************************** **** + * 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 (sta...@in...) + * + */ + +/** + * @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 <linux/kprobes.h> + +#include "fi_dbp.h" + +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 attach_module(const char *page) +{ + int num=0; + int i=0; + char tmp[16]; + + for(i=0;i<MAX_MOD;i++){ + if(inst_tab[i].counter <= 0) { + break; + } + } + if(i > MAX_MOD) { + return -EINVAL; + } + + num = sscanf(page, "%15s %s %d", tmp, inst_tab[i].modname, &inst_tab[i].counter); + if (num != 3) { + err("Invalid command format, translated no commands\n"); + goto err; + } + + if (inst_tab[i].counter == 0) { + err("Needn't to attach %s\n", inst_tab[i].modname); + goto err; + } + + inst_tab[i].kprobes = (struct kprobe*)kmalloc(inst_tab[i].counter*(sizeof(struct kprobe)), GFP_KERNEL); + if(inst_tab[i].kprobes == NULL) { + err("No memory @ %s\n", __FUNCTION__); + goto err; + } + + mod_index = i; + ins_index = 0; + return 0; +err: + inst_tab[i].modname[0] = '\0'; + inst_tab[i].counter = 0; + return -EINVAL; +} + +void add_ins(const char *page) +{ + char tmp[16]; + int num=0; + unsigned long addr=0; + struct module *mod; + + num = sscanf(page, "%15s %lu", tmp, &addr); + if (num != 2) { + err("Invalid command format, translated no commands\n"); + return; + } + if (ins_index > inst_tab[mod_index].counter - 1) { + err("Exceed address table!\n"); + return; + } + mod = fi_get_module((const char *)(inst_tab[mod_index].modname)); + if (!mod) { + err("Can not get module, not be loaded?\n"); + return; + } + dbg("module_core:%#lx\toff_set:%ld\n",(unsigned long)mod->module_core, addr); + inst_tab[mod_index].kprobes[ins_index].addr = (kprobe_opcode_t *)(addr + mod->module_core); + inst_tab[mod_index].kprobes[ins_index].pre_handler = pre_probe_handler; + inst_tab[mod_index].kprobes[ins_index].post_handler = post_probe_handler; + inst_tab[mod_index].kprobes[ins_index].fault_handler = NULL; + if ( register_kprobe(&(inst_tab[mod_index].kprobes[ins_index])) < 0 ) + { + err("register_kprobe failed @ %s\n", __FUNCTION__); + return; + } + fi_put_module(); + ins_index++; + return; +} +int detach_module(const char *page) +{ + int num=0; + int i=0; + int j=0; + char tmp[16]; + char modname[256]; + + num = sscanf(page, "%15s %s", tmp, modname); + if (num != 2) { + err("Invalid command format, translated no commands\n"); + return 0; + } + + for(i=0;i<MAX_MOD;i++){ + if(!strcmp(inst_tab[i].modname, modname)) { + break; + } + } + if(i > MAX_MOD) { + return -EINVAL; + } + + for(j=0;j<inst_tab[i].counter;j++) { + unregister_kprobe(&(inst_tab[i].kprobes[j])); + } + inst_tab[i].counter = 0; + inst_tab[i].modname[0] = '\0'; + if (inst_tab[i].kprobes) + { + kfree(inst_tab[i].kprobes); + } + return 0; +} + +static ssize_t ctl_show(struct interceptor * p, char * page, + size_t count, loff_t off) +{ + int i=0; + int n=0; + int j; + if (off) + return 0; + n += sprintf (page, "Module\tInstructions\n"); + while (inst_tab[i].modname[0]!='\0' && i < MAX_MOD) { + n += sprintf (page+n, "%s\t%d\n", inst_tab[i].modname, inst_tab[i].counter); + for(j=0;j<inst_tab[i].counter;j++){ + n += sprintf (page+n, "addr:\t%#lx\n", (unsigned long)inst_tab[i].kprobes[j].addr); + } + i++; + } + return n; +} + +static ssize_t ctl_store(struct interceptor * p, const char * page, + size_t count, loff_t off) +{ + char ctl[16]; + int num; + + if (off) + return 0; + + num = sscanf(page, "%15s", ctl); + if (!num) { + err("Invalid command format, translated no commands\n"); + return count; + } + + if (!strncmp(ctl, "add", 3)) { + down(&dbp_sem); + if (!in_used) { + up(&dbp_sem); + err("Use attach first.\n"); + return count; + } + add_ins(page); + up(&dbp_sem); + return count; + } + + if (!strncmp(ctl, "attach", 6)) { + down(&dbp_sem); + if (in_used) { + up(&dbp_sem); + err("FI_DBP is busy.(Attaching %s)\n", inst_tab[mod_index].modname); + return count; + } + if(attach_module(page)==0) + in_used++; + up(&dbp_sem); + return count; + } + + if (!strncmp(ctl, "end", 3)) { + down(&dbp_sem); + if (!in_used) { + up(&dbp_sem); + err("No attaching action to be ended.\n"); + return count; + } + in_used--; + up(&dbp_sem); + return count; + } + + if (!strncmp(ctl, "detach", 6)) { + down(&dbp_sem); + if (in_used) { + up(&dbp_sem); + err("FI_DBP is busy.(Attaching %s)\n", inst_tab[mod_index].modname); + return count; + } + detach_module(page); + up(&dbp_sem); + return count; + } + err("Unknow command %s\n", page); + return 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) +{ + int i; + + if (fi_register_interceptor(&dbp_interceptor)) { + dbg("Failed to register DBP Interceptor"); + return -EINVAL; + } + + sysfs_create_file(&dbp_interceptor.kobj, &attr_ctl.attr); + + for (i=0;i<MAX_MOD;i++) { + inst_tab[i].modname[0]='\0'; + } + + EXPORT_NO_SYMBOLS; + return 0; +} + +static void __exit dbp_stop(void) +{ + int i; + int j; + + for(i=0;i<MAX_MOD;i++) + { + for(j=0;j<inst_tab[i].counter;j++) { + unregister_kprobe(&(inst_tab[i].kprobes[j])); + } + inst_tab[i].counter = 0; + inst_tab[i].modname[0] = '\0'; + if (inst_tab[i].kprobes) + { + kfree(inst_tab[i].kprobes); + } + } + 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/char/fi/dbp/fi_dbp.h b/drivers/char/fi/dbp/fi_dbp.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/char/fi/dbp/fi_dbp.h Tue Dec 31 17:43:41 2002 @@ -0,0 +1,88 @@ +#ifndef __FI_DBP_H +#define __FI_DBP_H + +#include <linux/fi_internal.h> +#include <linux/fi.h> +#include <asm/processor.h> + +#define MAX_WP 32 +#define MAX_MOD 16 + +/*define reason of entering do_debug*/ +#define REASON_NOTME 0 +#define REASON_READ 1 +#define REASON_WRITE 2 + +#define PREFIX_16 0x66 + +static DECLARE_MUTEX(dbp_sem); +static int in_used; +static int mod_index; +static int ins_index; +static struct interceptor dbp_interceptor; + + +struct inst_rec_header { + char modname[256]; + int counter; + struct kprobe *kprobes; +}inst_tab[MAX_MOD] = {[0 ... MAX_MOD-1] = {"",0,NULL}}; + +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 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 { + 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 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 attach_module(const char *page); +int detach_module(const char *page); +void add_ins(const char *page); + + + + +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; +}; + +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 *); + +extern struct module *fi_get_module(const char *name); +extern void fi_put_module(void); +#endif /*__FI_DBP_H*/ Your Sincerely, Stanley Wang SW Engineer, Intel Corporation. Intel China Software Lab. Tel: 021-52574545 ext. 1171 iNet: 8-752-1171 Opinions expressed are those of the author and do not represent Intel Corporation |