[Fault-injection-developer] [PATCH]Rewrite of fi_core.c using sysfs
Status: Alpha
Brought to you by:
rustyl
From: Rusty L. <ru...@st...> - 2002-12-16 07:04:07
|
As I mentioned in a previous email, I was hacking around with some alternative approaches to the core functionality for fault injection, and ended up making a complete rewrite of fi_core.c. This was actually my second attempt, since the first avenue I went down ended turning more complicated then I believe fault injection warrants. The code is still pretty preliminary, but I wanted to get it out for review before I gave up for the night, and since the second half of tomorrow will be taken up with the opening of the new Star Trek flick... then maybe somebody will have a chance to run with this idea. Why I felt we needed an overhaul of fi_core.c --------------------------------------------- 1. When I read through fi_core.c, I notice that there are a lot of assumptions about what a given interceptor is doing (specifically the mmio interceptor in arch/i386/fi/pf/*). We already have volunteers to implement other interceptors that are described in the high level design documentation, but until we get better model for plugging new interceptors into the core fault injection functionality then life will get very complicated. 2. Anytime I look at a block of kernel code I ask 'is there some way this can be done in user space?'. The high level design papers describe a model where faults (or triggers) are grouped into fault sets. I have no problem with that type of design, but since a given trigger has no need to know about sibling triggers, then this should be information kept in user space. The below implementation implements triggers that expose everything there is to know about themselves to user space via sysfs. With this kind of information user space can keep track of what triggers are associated with what fault set. 3. Ever since I discovered the elegance of the new kobject model for dealing with data in the kernel, I have tried to utilize that model where ever possible. I moved the core functionality to use this model instead of using the fixed size block of data that existed before. I feel this makes the code easier to read, very easy to expose data to user space via sysfs, and allows us to piggy back on one of the most utilized subsystems in the kernel. We no longer have the need for exposing an ioctl interface since all interaction happens via sysfs. Here is an example: when I boot a system with fault injection enabled and mount sysfs on /sysfs/, then I can see ==> # NOTE: All of this is without the aid of a tool. The existing # ficl tool could be tweaked to expose the same interface # to the user (i.e. the manual is still correct), but be # implemented by reading/write to files in sysfs $ tree /sysfs/fault_injection /sysfs/fault_injection/ |-- debug |-- enabled |-- interceptors | `-- pf_interceptor `-- triggers `-- ctl From this I can see that I have one interceptor named pf_interceptor and not triggers inserted. If I want to enable extra debugging information the I ==> $ cat "1" > /sysfs/fault_injection/debug By default fault injection is disabled at startup. I can enable it by ==> $ cat "1" > /sysfs/fault_injection/enabled Adding and removing triggers happens by writing commands to triggers/ctl. To add a trigger ==> # documentation about what each field is for is documented in the code $echo "add test1 MMIO 1 1 1 1 10 1 1 1 1 1 1 1" > \ /sysfs/fault_injection/triggers/ctl I could then see the trigger represented in sysfs by ==> $ tree /sysfs/fault_injection /sysfs/fault_injection/ |-- debug |-- enabled |-- interceptors | `-- pf_interceptor `-- triggers |-- ctl `-- test1 |-- bitmask |-- count |-- hertz |-- max |-- min |-- opcode |-- operand |-- protection |-- registered |-- skip |-- stop |-- type `-- wp And then inspect what ever I wanted, like ==> $ cat /sysfs/fault_injection/triggers/test1/type MMIO To remove the trigger ==> $ echo "del test1" > /sysfs/fault_injection/triggers/ctl Hopefully you get the idea. The possibilities are endless. New model for plugging interceptors into fault injection -------------------------------------------------------- When an interceptor is initialized, it registers with the fi_core.c by: creating an interceptor object which is a kobject, and then passing that object to fi_core.c by calling ==> int fi_register_interceptor(struct interceptor *); One of the fields in a trigger indicates what type of interceptor it is designed for. When a trigger is registered for a INTERCEPTOR_TYPE_MMIO type of interceptor, the trigger will arm itself with all appropriate interceptors by calling the 'arm' callback inside the interceptor object. This is same idea as fi_core.c calling fi_pf_register(), but doesn't require fi_core.c to know about the pf implementation of a mmio interceptor ahead of time. When a trigger is removed, fi_core.c calls the 'disarm' callback also contained in the interceptor object passed to fi_core.c during registration. The code has still got some major holes, but it compiles and fundamental operations can be done via cat and echo on the command line. Since this is a complete rewrite of fi_core.c, I am sending the entire file for browsing, and then also sending a diff against the bk tree. These files can also be found at http//fault-injection.sf.net/rustyl/ --rustyl /****************************************************************************** * 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. * ****************************************************************************** * * This is a rewrite of code originally submitted by * Louis Zhuang <lou...@in...>. I have moved the code from * a ioctl based control using proc to expose information about * fault injection data that was contained in fixed sized tables, * to an extendable kobject based implementation that uses sysfs * for all user space access. * --rustyl * * Contributors: * Louis Zhuang <lou...@in...> * Stanley Wang <sta...@in...> * Rusty Lynch <ru...@li...> */ #include <linux/module.h> #include <linux/init.h> #include <linux/kprobes.h> #include <linux/slab.h> #include <linux/kobject.h> #define PREFIX_NAME "FI" #include <linux/fi/fi.h> #include <linux/fi.h> #ifdef CONFIG_FI_DEBUG #define dbg(format, arg...) \ do { \ if(fi_debug) \ printk (KERN_DEBUG "%s: " format "\n", \ __FUNCTION__, ## arg); \ } while(0) #else #define dbg(format, art...) #endif #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) 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 fault_injection_enabled = 0; static int fi_debug = 1; int fi_register_interceptor(struct interceptor *); struct trigger_attribute { struct attribute attr; ssize_t (*show)(struct trigger *,char *,size_t,loff_t); }; 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_type; 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_type.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_type.attr); } static inline ssize_t add_trigger(struct trigger *t) { struct list_head * entry; struct interceptor * i; struct trigger *n; n = kmalloc(sizeof(struct trigger),GFP_KERNEL); if (!n) return -ENOMEM; memset(n,0,sizeof(struct trigger)); kobject_init(&n->kobj); strncpy(n->kobj.name, t->kobj.name, KOBJ_NAME_LEN); n->kobj.subsys = &trigger_subsys; 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; n->type = t->type; atomic_set(&n->count, 0); if (kobject_register(&n->kobj)) { err("Unable to register kobject"); return -EINVAL; } create_trigger_files(n); list_for_each(entry,&interceptor_subsys.list) { i = to_interceptor(entry); if (i->type == n->type) { (i->arm)(n); } } return 0; } static inline ssize_t del_trigger(struct trigger *t) { struct list_head * entry; struct trigger * n; list_for_each(entry,&trigger_subsys.list) { n = to_trigger(entry); if (!strncmp(n->kobj.name, t->kobj.name, KOBJ_NAME_LEN)) { dbg("comparing %s to %s", n->kobj.name, t->kobj.name); remove_trigger_files(n); kobject_unregister(&n->kobj); return 0; } } err("trigger %s does not exist", t->kobj.name); return -EINVAL; } /* * =========================================================== * <>-- Toplevel Containing Subsystem (fi_subsys) --<> * ----------------------------------------------------------- */ static ssize_t fi_enabled_show(struct subsystem * s, char * page, size_t count, loff_t off) { return off ? 0 : snprintf(page,count,"%i\n",fault_injection_enabled); } static ssize_t fi_enabled_store(struct subsystem * s, const char * page, size_t count, loff_t off) { int tmp = 0; int num; int ret = 0; int error = 0; down(&fi_sem); if (off) goto Done; num = sscanf(page,"%i",&tmp); if (!num) { error = -EINVAL; goto Done; } if (tmp) { fault_injection_enabled = 1; } else { fault_injection_enabled = 0; } ret = error ? error : count; Done: up(&fi_sem); return ret; } static ssize_t fi_debug_show(struct subsystem * s, char * page, size_t count, loff_t off) { return off ? 0 : snprintf(page,count,"%i\n",fi_debug); } static ssize_t fi_debug_store(struct subsystem * s, const char * page, size_t count, loff_t off) { int tmp = 0; int num; int ret = 0; int error = 0; down(&fi_sem); if (off) goto Done; num = sscanf(page,"%i",&tmp); if (!num) { error = -EINVAL; goto Done; } if (tmp) { fi_debug = 1; } else { fi_debug = 0; } ret = error ? error : count; Done: up(&fi_sem); return ret; } static struct subsystem fi_subsys = { .kobj = { .name = "fault_injection" }, }; static struct subsys_attribute fi_subsys_attr_enabled = { .attr = { .name = "enabled", .mode = 0644 }, .show = fi_enabled_show, .store = fi_enabled_store, }; 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 struct subsystem interceptor_subsys = { .kobj = { .name = "interceptors" }, .parent = &fi_subsys, }; /* * =========================================================== * <>-- Trigger Subsystem (trigger_subsys) --<> * ----------------------------------------------------------- */ static ssize_t trigger_attr_show(struct kobject * kobj, struct attribute * attr, char * page, size_t count, loff_t off) { 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,count,off) : 0; } static ssize_t trigger_wp_read(struct trigger * p, char * page, size_t count, loff_t off) { dbg("wp.addr = %#lx, wp.type = %#x", p->wp.addr, p->wp.type); return off ? 0 : snprintf(page,count,"%#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, size_t count, loff_t off) { dbg("bitmask = %#x", p->bitmask); return off ? 0 : snprintf(page,count,"%#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, size_t count, loff_t off) { dbg("min = %i", p->min); return off ? 0 : snprintf(page,count,"%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, size_t count, loff_t off) { dbg("max = %i", p->max); return off ? 0 : snprintf(page,count,"%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, size_t count, loff_t off) { dbg("skip = %i", p->skip); return off ? 0 : snprintf(page,count,"%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, size_t count, loff_t off) { dbg("stop = %i", p->stop); return off ? 0 : snprintf(page,count,"%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, size_t count, loff_t off) { dbg("protection = %i", p->protection); return off ? 0 : snprintf(page,count,"%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, size_t count, loff_t off) { dbg("hertz = %i", p->hertz); return off ? 0 : snprintf(page,count,"%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, size_t count, loff_t off) { dbg("registered = %i", p->registered); return off ? 0 : snprintf(page,count,"%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, size_t count, loff_t off) { dbg("count = %i", atomic_read(&p->count)); return off ? 0 : snprintf(page,count,"%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_type_read(struct trigger * p, char * page, size_t count, loff_t off) { int rv; dbg("type = %i", p->type); if (off) return 0; switch (p->type) { case INTERCEPTOR_TYPE_MMIO: rv = snprintf(page,count,"MMIO\n"); break; default: rv = snprintf(page,count,"UNKNOWN\n"); } return rv; } static struct trigger_attribute trigger_attr_type = { .attr = { .name = "type", .mode = S_IRUGO }, .show = trigger_type_read, }; static ssize_t trigger_opcode_read(struct trigger * p, char * page, size_t count, loff_t off) { dbg("opcode = %i", p->opcode); return off ? 0 : snprintf(page,count,"%i\n",p->opcode); } 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, size_t count, loff_t off) { dbg("operand = %i", p->operand); return off ? 0 : snprintf(page,count,"%i\n",p->operand); } 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, size_t count, loff_t off) { return 0; } static ssize_t trigger_ctl_store(struct subsystem * s, const char * page, size_t count, loff_t off) { char ctl[16]; char type[16]; int num; int error; int ret = 0; struct trigger tmp; if (off) return 0; /* 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 */ /* ctl nm tp 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 */ type, /* 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"); error = -EINVAL; goto Done; } dbg("type = '%s'", type); if (!strncmp(type, "MMIO", 4)) tmp.type = INTERCEPTOR_TYPE_MMIO; else tmp.type = INTERCEPTOR_TYPE_UNKNOWN; if (!strcmp(ctl,"add") && num == 15) error = add_trigger(&tmp); else if (!strcmp(ctl,"del") && num == 2) error = del_trigger(&tmp); else { err("Invalid command format: %s, %i tokens found",ctl,num); error = -EINVAL; } ret = error ? error : count; Done: return ret; } static struct subsys_attribute trigger_subsys_attr_ctl = { .attr = { .name = "ctl", .mode = 0644 }, .show = trigger_ctl_show, .store = trigger_ctl_store, }; static struct sysfs_ops trigger_sysfs_ops = { .show = trigger_attr_show, }; static struct subsystem trigger_subsys = { .kobj = { .name = "triggers" }, .sysfs_ops = &trigger_sysfs_ops, .parent = &fi_subsys, }; static int __init fi_init(void){ down(&fi_sem); if (is_initialized) { dbg("alreading initialized... exiting"); goto Done; } dbg("initializing fault injection core"); subsystem_register(&fi_subsys); subsys_create_file(&fi_subsys,&fi_subsys_attr_debug); subsys_create_file(&fi_subsys,&fi_subsys_attr_enabled); 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; }; int fi_register_interceptor(struct interceptor *i) { /* * 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.subsys = &interceptor_subsys; kobject_init(&i->kobj); return kobject_register(&i->kobj); } /* Is there a better way to get my init code to execute on startup? */ module_init(fi_init); EXPORT_SYMBOL_GPL(fault_injection_enabled); EXPORT_SYMBOL_GPL(fi_register_interceptor); MODULE_PARM(fi_debug, "i"); MODULE_PARM_DESC(fi_debug, "Debugging mode enabled or not"); # 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.879 -> 1.880 # arch/i386/kernel/fi/pf/pf.c 1.6 -> 1.7 # kernel/Makefile 1.25 -> 1.26 # kernel/fi_core.c 1.6 -> 1.7 # (new) -> 1.1 include/linux/fi.h # # The following is the BitKeeper ChangeSet Log # -------------------------------------------- # 02/12/15 ru...@st... 1.880 # This changeset includes a complete rewrite of fi_core.c, a new # header file infavor of the include/linux/fi/ going away, and a new # model for interfacing between interceptors and the core fault injection # routines. # # -------------------------------------------- # diff -Nru a/arch/i386/kernel/fi/pf/pf.c b/arch/i386/kernel/fi/pf/pf.c --- a/arch/i386/kernel/fi/pf/pf.c Sun Dec 15 21:33:50 2002 +++ b/arch/i386/kernel/fi/pf/pf.c Sun Dec 15 21:33:50 2002 @@ -51,6 +51,7 @@ #include <asm/pgalloc.h> #include <asm/io.h> #include <asm/tlbflush.h> +#include <linux/random.h> #include <asm/fi.h> @@ -58,6 +59,7 @@ #include <linux/fi/fi.h> #include <linux/fi/fi_interface.h> #include <linux/fi/fi_internal.h> +#include <linux/fi.h> #include "fi_pf.h" @@ -67,14 +69,105 @@ static struct{ int page_index; struct watchpoint wp; - int id; - struct intercept_hook * hook; + struct trigger * trigger; }wp_tb[MAX_WP] = { [0 ... MAX_WP-1] = { wp: {0,0}, - id: -1, - hook: NULL } + trigger: NULL } }; +/** + * 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 fi_corrupt{ + __u32 corrupted_data; +}; + +static inline void execute_trigger(struct trigger *t, __u32 val, + int len, int type, void *data) +{ + int tmp = 0; + + if (!t) { + PERROR("Unable to execute null trigger\n"); + goto exit; + } + + get_random_bytes(&tmp, sizeof(tmp)); + if (t->hertz==0) {t->hertz=1;};//divid is not 0! + if (tmp >= 0xFFFFFFFF/t->hertz){ + PDEBUG("rnd=%d, HERZ=%d\n", tmp, t->hertz); + goto exit; + } + + tmp = val & (~t->bitmask); + if (t->min>tmp || tmp>=t->max){ + PDEBUG("min=%d, max=%d, val=%d\n", t->min, t->max, val); + if(!(t->min==0 && t->max==0)) + goto exit; + } + + // Increase count only when passing all conditions + atomic_inc(&t->count); + + if (t->skip >= atomic_read(&t->count)){ + PDEBUG("count=%d, skip=%d\n", atomic_read(&t->count), t->skip); + goto exit; + } + + if ( t->stop!=0 && (t->stop < (atomic_read(&t->count)-t->skip)) ) { + PDEBUG("count=%d, stop=%d\n", atomic_read(&t->count), t->stop); + goto exit; + } + + PDEBUG("count=%d, skip=%d, min=%d, max=%d, val=%d\n", + atomic_read(&t->count), t->skip, t->min, t->max, val); + tmp = val; + switch (t->opcode){ + case FI_NOTHING: + goto exit_for; + case FI_SET: + tmp = t->operand; + break; + case FI_AND: + tmp &= t->operand; + break; + case FI_OR: + tmp |= t->operand; + break; + case FI_NOT: + tmp = ~tmp; + break; + case FI_XOR: + tmp ^= t->operand; + break; + case FI_NAND: + tmp = ~(tmp & t->operand); + break; + case FI_NOR: + tmp = ~(tmp | t->operand); + break; + case FI_ADD: + tmp = tmp + t->operand; + break; + case FI_SUB: + tmp = tmp - t->operand; + break; + default: + PERROR("Not recognized opcode%d\n", t->opcode); + break; + } + + exit_for: + val = (val & (t->protection)) | (tmp & (~t->protection)); + + exit: + ((struct fi_corrupt *)data)->corrupted_data = val; + return; +} + static void fi_do_flush_tlb_ipi(void* addr) { __flush_tlb_one(addr); }; @@ -85,7 +178,8 @@ __flush_tlb_one(addr); }; -int fi_pf_register(struct watchpoint wp, int id, struct intercept_hook *hook){ +int fi_pf_register(struct watchpoint wp, struct trigger *trigger) +{ unsigned long flags; int wi=0; //watchpoint index int ri=0; //region index @@ -93,8 +187,8 @@ unsigned long addr; /* Make sure that wp is vaild */ - if ( hook==NULL ) { - PERROR("Reason: %s\n", "/* There are no callback */"); + if ( trigger==NULL ) { + PERROR("Reason: %s\n", "/* There are no trigger */"); return -EFI_INVALID; } if ( wp.type& (~FI_WPTYPE_ALL_MASK) ) { @@ -113,9 +207,10 @@ /* Make sure there are no the same wp or id */ for (wi=0; wi<MAX_WP; wi++) { - if (wp_tb[wi].hook==NULL) continue; - if ((id==wp_tb[wi].id) || (addr==wp_tb[wi].wp.addr)) { - PERROR("Reason: %s\n", "/* there are same wp or id */"); + if (wp_tb[wi].trigger==NULL) continue; + if ((trigger==wp_tb[wi].trigger) || (addr==wp_tb[wi].wp.addr)){ + PERROR("Reason: %s\n", + "/* there are same wp or trigger */"); rv = -EFI_REGISTERED; goto err; } @@ -129,7 +224,7 @@ /* find empty item in wp_tb */ for (wi=0; wi<MAX_WP; wi++){ - if (wp_tb[wi].hook==NULL){ + if (wp_tb[wi].trigger==NULL){ break; } } @@ -142,12 +237,8 @@ /* register this wp into wp_tb*/ wp_tb[wi].wp.addr = addr; wp_tb[wi].wp.type = wp.type; - wp_tb[wi].id = id; - wp_tb[wi].hook = hook; + wp_tb[wi].trigger = trigger; - PDEBUG("register WP: index=%d, addr=0X%#lx, type=0x%x, id=%d, hook=0x%p\n", - wi, wp_tb[wi].wp.addr, wp_tb[wi].wp.type, - wp_tb[wi].id, wp_tb[wi].hook); err: pf_unlock_region_irqrestore(flags); fi_flush_tlb_all((unsigned long)addr); @@ -155,7 +246,7 @@ }; -int fi_pf_unregister(int id) { +int fi_pf_unregister(struct trigger *t) { unsigned long flags; int wi=0; int ri=0; @@ -165,7 +256,7 @@ /* find the id */ for (wi=0; wi<MAX_WP; wi++) { - if (wp_tb[wi].hook!=NULL && wp_tb[wi].id == id) { + if (wp_tb[wi].trigger == t) { break; } } @@ -183,8 +274,7 @@ wp_tb[wi].wp.addr = 0; wp_tb[wi].wp.type = 0; - wp_tb[wi].id = -1; - wp_tb[wi].hook = 0; + wp_tb[wi].trigger = 0; err: pf_unlock_region_irqrestore(flags); @@ -192,16 +282,6 @@ }; -/** - * 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 fi_corrupt{ - __u32 corrupted_data; -}; - int fi_pf_corrupt(int id, __u32 dirty, void *data){ ((struct fi_corrupt *)data)->corrupted_data = dirty; return 0; @@ -212,7 +292,7 @@ int i=0; for (i=0; i<MAX_WP; i++) { - if (wp_tb[i].hook == NULL) continue; + if (wp_tb[i].trigger == NULL) continue; if (addr == (unsigned long)wp_tb[i].wp.addr) { break; } @@ -235,12 +315,16 @@ static unsigned long crpt_mask[] = { 0x000000FF, 0x0000FFFF, 0x00FFFFFF, 0xFFFFFFFF}; +extern int fault_injection_enabled; int fi_page_fault(struct pt_regs * regs, unsigned long address) { unsigned long cpu = smp_processor_id(); struct fi_corrupt crpt; int i; int ins_len, crpt_len; - + + if (!fault_injection_enabled) + return 0; + pf_lock_region(); //lock it! if (pf_find_region(address)<0) { if (pf_is_removed_regions(address)) { @@ -287,9 +371,8 @@ WP_LEN(wp_tb[i].wp):ins_len; crpt.corrupted_data = pf_reason[cpu].val & crpt_mask[crpt_len-1]; - (wp_tb[i].hook->trigger)( wp_tb[i].id, crpt.corrupted_data, - crpt_len, 0, &crpt); - + execute_trigger(wp_tb[i].trigger, crpt.corrupted_data, + crpt_len, 0, &crpt); set_ins_reg_val(pf_reason[cpu].eip, regs, (crpt.corrupted_data&crpt_mask[crpt_len-1]) | (pf_reason[cpu].val&(~crpt_mask[crpt_len-1]))); @@ -305,10 +388,10 @@ crpt_len = (WP_LEN(wp_tb[i].wp)<ins_len)? WP_LEN(wp_tb[i].wp):ins_len; - crpt.corrupted_data = pf_reason[cpu].val & crpt_mask[crpt_len-1]; - (wp_tb[i].hook->trigger)( wp_tb[i].id, crpt.corrupted_data, - crpt_len, 0, &crpt); - + crpt.corrupted_data = pf_reason[cpu].val & crpt_mask[crpt_len-1]; + execute_trigger(wp_tb[i].trigger, crpt.corrupted_data, + crpt_len, 0, &crpt); + set_ins_imm_val(pf_reason[cpu].eip, (crpt.corrupted_data&crpt_mask[crpt_len-1]) | (pf_reason[cpu].val&(~crpt_mask[crpt_len-1]))); @@ -346,6 +429,9 @@ unsigned long cpu = smp_processor_id(); struct fi_corrupt crpt; int ins_len, crpt_len; + + if (!fault_injection_enabled) + return 0; /* * DProbes make do_debug disallow irq. So we are safty from irq now. @@ -383,9 +469,9 @@ crpt_len = (WP_LEN(wp_tb[pf_reason[cpu].index].wp)<ins_len)? WP_LEN(wp_tb[pf_reason[cpu].index].wp):ins_len; crpt.corrupted_data = pf_reason[cpu].val & crpt_mask[crpt_len-1]; - (wp_tb[pf_reason[cpu].index].hook->trigger)( - wp_tb[pf_reason[cpu].index].id, crpt.corrupted_data, - crpt_len, 0, &crpt); + //(wp_tb[pf_reason[cpu].index].hook->trigger)( + //wp_tb[pf_reason[cpu].index].id, crpt.corrupted_data, + //crpt_len, 0, &crpt); set_ins_reg_val(pf_reason[cpu].eip, regs, (crpt.corrupted_data&crpt_mask[crpt_len-1]) @@ -417,11 +503,35 @@ return 0; }; +static void pf_arm(struct trigger *t) +{ + PDEBUG("arming %s trigger\n", t->kobj.name); + fi_pf_register(t->wp, t); +} + +static void pf_disarm(struct trigger *t) +{ + PDEBUG("disarming %s trigger\n", t->kobj.name); +} + +static struct interceptor pf_interceptor = { + .kobj = {.name = "pf_interceptor"}, + .type = INTERCEPTOR_TYPE_MMIO, + .arm = &pf_arm, + .disarm = &pf_disarm +}; + +extern int fi_register_interceptor(struct interceptor *); +static int __init pf_init(void) +{ + return fi_register_interceptor(&pf_interceptor); +} EXPORT_SYMBOL_NOVERS(fi_pf_register); EXPORT_SYMBOL_NOVERS(fi_pf_unregister); EXPORT_SYMBOL_NOVERS(fi_pf_corrupt); +module_init(pf_init); MODULE_AUTHOR("Louis Zhuang"); MODULE_DESCRIPTION("Pagefault interceptor component for FI"); 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 Sun Dec 15 21:33:50 2002 @@ -0,0 +1,56 @@ +#ifndef _FAULT_INJECTION_H +#define _FAULT_INJECTION_H +#if defined(__KERNEL__) + +#include <linux/kobject.h> +#include <linux/fi/fi.h> + +#define to_trigger(entry) container_of(entry,struct trigger,kobj.entry); +#define to_interceptor(entry)container_of(entry,struct interceptor,kobj.entry); + +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; + +typedef enum +{ + INTERCEPTOR_TYPE_UNKNOWN=0, + INTERCEPTOR_TYPE_MMIO=1 +} interceptor_type_t; + +struct trigger { + struct watchpoint wp; + __u32 bitmask; + int min; + int max; + int skip; + int stop; + int protection; + int hertz; + int registered; + trigger_opcode_t opcode; + int operand; + atomic_t count; + interceptor_type_t type; + struct kobject kobj; +}; + +struct interceptor { + interceptor_type_t type; + void (*arm) (struct trigger *); + void (*disarm) (struct trigger *); + struct kobject kobj; +}; + +#endif /* __KERNEL__ */ +#endif /* _FAULT_INJECTION_H */ diff -Nru a/kernel/Makefile b/kernel/Makefile --- a/kernel/Makefile Sun Dec 15 21:33:50 2002 +++ b/kernel/Makefile Sun Dec 15 21:33:50 2002 @@ -4,7 +4,7 @@ export-objs = signal.o sys.o kmod.o workqueue.o ksyms.o pm.o exec_domain.o \ printk.o platform.o suspend.o dma.o module.o cpufreq.o \ - profile.o rcupdate.o intermodule.o kprobes.o + profile.o rcupdate.o intermodule.o kprobes.o fi_core.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 \ diff -Nru a/kernel/fi_core.c b/kernel/fi_core.c --- a/kernel/fi_core.c Sun Dec 15 21:33:50 2002 +++ b/kernel/fi_core.c Sun Dec 15 21:33:50 2002 @@ -1,5 +1,6 @@ + /****************************************************************************** - * Fault Injection Test harness (FI) + * Fault Injection Core Functionality * Copyright (C) Intel Crop. * * This program is free software; you can redistribute it and/or @@ -17,508 +18,577 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ****************************************************************************** + * + * This is a rewrite of code originally submitted by + * Louis Zhuang <lou...@in...>. I have moved the code from + * a ioctl based control using proc to expose information about + * fault injection data that was contained in fixed sized tables, + * to an extendable kobject based implementation that uses sysfs + * for all user space access. + * --rustyl + * + * Contributors: + * Louis Zhuang <lou...@in...> + * Stanley Wang <sta...@in...> + * Rusty Lynch <ru...@li...> */ -/* $Id: dm_core.c,v 1.3 2002/11/14 07:11:37 brlock Exp $ - * Copyright by Intel Crop., 2002 - * Louis Zhuang (lou...@in...) - */ -/** - * @file dm_core.c - */ - -#include <linux/config.h> -#include <linux/version.h> - -#include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> -#include <linux/version.h> -#include <asm/system.h> -#include <asm/uaccess.h> -#include <asm/io.h> -#include <asm/atomic.h> -#include <asm/debugreg.h> -#include <asm/desc.h> -#include <linux/ioport.h> -#include <asm/page.h> -#include <asm/pgtable.h> -#include <asm/pgalloc.h> -#include <linux/sched.h> -#include <asm/system.h> -#include <linux/vmalloc.h> -#include <linux/proc_fs.h> -#include <linux/random.h> -#include <linux/highmem.h> #include <linux/kprobes.h> +#include <linux/slab.h> +#include <linux/kobject.h> #define PREFIX_NAME "FI" #include <linux/fi/fi.h> -#include <linux/fi/fi_internal.h> -#include <linux/fi/fi_interface.h> +#include <linux/fi.h> -#define MAX_MOD 16 +#ifdef CONFIG_FI_DEBUG +#define dbg(format, arg...) \ + do { \ + if(fi_debug) \ + printk (KERN_DEBUG "%s: " format "\n", \ + __FUNCTION__, ## arg); \ + } while(0) +#else +#define dbg(format, art...) +#endif +#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) + +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 fault_injection_enabled = 0; +static int fi_debug = 1; + +int fi_register_interceptor(struct interceptor *); + +struct trigger_attribute { + struct attribute attr; + ssize_t (*show)(struct trigger *,char *,size_t,loff_t); +}; + +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_type; + +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_type.attr); +} -#define MODULE_NAME "fi_dm" -#define MODULE_VERSION "0.1" +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_type.attr); +} -static int insert_faultset(struct fi_faultset *fs); -static int remove_faultset(int index); -static int enable_faultset(int index); -static int disable_faultset(int index); - - -static struct fi_faultset fs_tb[MAX_FAULTSET]; - -extern int fi_pf_register(struct watchpoint wp, int id, struct intercept_hook *hook); -extern int fi_pf_unregister(int id); -extern int fi_pf_corrupt(int id, __u32 dirty, void *data); - - -void dm_trigger(int id, __u32 val, int len, int type, void *data){ - int fs_num= FI_FS_NUM(id); - int tri_num=FI_TRI_NUM(id); - int tmp=0, i=0; - struct fi_trigger *tri = NULL; - __u32 old = val; - - PDEBUG("entry, id=%#x, val=%#x, len=%d, type=%d, data=%p\n", - id, val, len, type, data); - if (fs_num>=MAX_FAULTSET || fs_num<0 - || tri_num>=MAX_TRIGGER_PER_FAULTSET || tri_num<0){ - PERROR(":error id number %d\n", id); - goto exit; - } - - tri = &fs_tb[fs_num].trigger_tb[tri_num]; - - if (!tri->registered){ - PERROR("Should not trigger an unregistered wp %d!?\n", id); - } - - - get_random_bytes(&tmp, sizeof(tmp)); - if (tri->hertz==0) {tri->hertz=1;};//divid is not 0! - if (tmp >= 0xFFFFFFFF/tri->hertz){ - PDEBUG("rnd=%d, HERZ=%d\n", tmp, tri->hertz); - goto exit; - } - - tmp = val & (~tri->bitmask); - if (tri->min>tmp || tmp>=tri->max){ - PDEBUG("min=%d, max=%d, val=%d\n", tri->min, tri->max, val); - if(!(tri->min==0 && tri->max==0)) - goto exit; - } - - // Increase count only when passing all conditions - atomic_inc(&tri->count); - - if (tri->skip >= atomic_read(&tri->count)){ - PDEBUG("count=%d, skip=%d\n", atomic_read(&tri->count), tri->skip); - goto exit; - } - - if ( tri->stop!=0 && (tri->stop < (atomic_read(&tri->count)-tri->skip)) ) { - PDEBUG("count=%d, stop=%d\n", atomic_read(&tri->count), tri->stop); - goto exit; - } - - PDEBUG("count=%d, skip=%d, min=%d, max=%d, val=%d\n", - atomic_read(&tri->count), tri->skip, tri->min, tri->max, val); - tmp = val; - for (i=0; i<MAX_OP_PER_TRIGGER; i++) { - switch (tri->ops[i].opcode){ - case FI_NOTHING: - goto exit_for; - case FI_SET: - tmp = tri->ops[i].operand; - break; - case FI_AND: - tmp &= tri->ops[i].operand; - break; - case FI_OR: - tmp |= tri->ops[i].operand; - break; - case FI_NOT: - tmp = ~tmp; - break; - case FI_XOR: - tmp ^= tri->ops[i].operand; - break; - case FI_NAND: - tmp = ~(tmp & tri->ops[i].operand); - break; - case FI_NOR: - tmp = ~(tmp | tri->ops[i].operand); - break; - case FI_ADD: - tmp = tmp + tri->ops[i].operand; - break; - case FI_SUB: - tmp = tmp - tri->ops[i].operand; - break; - default: - PERROR("Not recognized opcode%d\n", tri->ops[i].opcode); - break; +static inline ssize_t add_trigger(struct trigger *t) +{ + struct list_head * entry; + struct interceptor * i; + struct trigger *n; + + n = kmalloc(sizeof(struct trigger),GFP_KERNEL); + if (!n) + return -ENOMEM; + memset(n,0,sizeof(struct trigger)); + + kobject_init(&n->kobj); + strncpy(n->kobj.name, t->kobj.name, KOBJ_NAME_LEN); + n->kobj.subsys = &trigger_subsys; + 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; + n->type = t->type; + atomic_set(&n->count, 0); + + if (kobject_register(&n->kobj)) { + err("Unable to register kobject"); + return -EINVAL; + } + + create_trigger_files(n); + + list_for_each(entry,&interceptor_subsys.list) { + i = to_interceptor(entry); + if (i->type == n->type) { + (i->arm)(n); } } - exit_for: - val = (val & (tri->protection)) | (tmp & (~tri->protection)); + + return 0; +} - exit: +static inline ssize_t del_trigger(struct trigger *t) +{ + struct list_head * entry; + struct trigger * n; + + list_for_each(entry,&trigger_subsys.list) { + n = to_trigger(entry); + if (!strncmp(n->kobj.name, t->kobj.name, KOBJ_NAME_LEN)) { + dbg("comparing %s to %s", n->kobj.name, t->kobj.name); + remove_trigger_files(n); + kobject_unregister(&n->kobj); + return 0; + } + } - PINFO("id=%#x, val=%#x, old=%#x, len=%d, type=%d, data=%p\n", - id, val, old, len, type, data); - fi_pf_corrupt(id, val, data); - return; + err("trigger %s does not exist", t->kobj.name); + return -EINVAL; } -static struct intercept_hook intcpt_hook[1] = { - { - trigger: dm_trigger - } -}; - +/* + * =========================================================== + * <>-- Toplevel Containing Subsystem (fi_subsys) --<> + * ----------------------------------------------------------- + */ -/*manipulate fault set*/ -static int insert_faultset(struct fi_faultset *fs){ - int rv=0; - int i=0; +static ssize_t fi_enabled_show(struct subsystem * s, char * page, + size_t count, loff_t off) +{ + return off ? 0 : snprintf(page,count,"%i\n",fault_injection_enabled); +} - for (i=0; i<MAX_FAULTSET; i++) { - if (fs_tb[i].stat == NOUSED) break; +static ssize_t fi_enabled_store(struct subsystem * s, const char * page, + size_t count, loff_t off) +{ + int tmp = 0; + int num; + int ret = 0; + int error = 0; + + down(&fi_sem); + if (off) + goto Done; + + num = sscanf(page,"%i",&tmp); + if (!num) { + error = -EINVAL; + goto Done; } - if (i < MAX_FAULTSET) { - int rv1; - copy_from_user(&fs_tb[i], fs, sizeof(fs_tb[i])); - fs_tb[i].stat = DISABLED; - rv = i; - PINFO("insert #%d fs\n", i); - rv1 = enable_faultset(i); - if(rv1<0) { - PERROR("can not enable faultset# %d\n", i); - } - }else{ - rv = -ENOSPC; + + if (tmp) { + fault_injection_enabled = 1; + } else { + fault_injection_enabled = 0; } - return rv; -} -static int remove_faultset(int index){ - int rv=0; + ret = error ? error : count; + Done: + up(&fi_sem); + return ret; +} - if (index<0 || index>=MAX_FAULTSET || fs_tb[index].stat == NOUSED) { - rv = -EPERM; - goto exit1; - } - - if (index>=0 && index<MAX_FAULTSET && fs_tb[index].stat == ENABLED) { - disable_faultset(index); - } - - if (index<0 || index>=MAX_FAULTSET || fs_tb[index].stat == ENABLED) { - PERROR("can not remove #%d fs when it is enabled\n", index); - rv = -EPERM; - }else{ - fs_tb[index].stat = NOUSED; - } - PINFO("remove #%d fs\n", index); - exit1: - return rv; +static ssize_t fi_debug_show(struct subsystem * s, char * page, + size_t count, loff_t off) +{ + return off ? 0 : snprintf(page,count,"%i\n",fi_debug); } -static int enable_faultset(int index){ - int rv=0; - int i=0; - int id_high; - struct fi_faultset *fs; - - PDEBUG("trying to enable faultset# %d\n", index); - - if (index<0 || index>= MAX_FAULTSET || fs_tb[index].stat != DISABLED) { - rv = -EINVAL; - goto exit; - } - - id_high = index<<16; - fs = &fs_tb[index]; - /*register all wp into interceptor component*/ - for (i=0; i<MAX_TRIGGER_PER_FAULTSET && (fs->trigger_tb[i].wp.addr); i++){ - PDEBUG("register WP, id=%d, hook=%p\n", id_high+i, intcpt_hook); - atomic_set(&fs->trigger_tb[i].count, 0); - fs->trigger_tb[i].registered = 1; - rv = fi_pf_register(fs->trigger_tb[i].wp, id_high+i, intcpt_hook); +static ssize_t fi_debug_store(struct subsystem * s, const char * page, + size_t count, loff_t off) +{ + int tmp = 0; + int num; + int ret = 0; + int error = 0; + + down(&fi_sem); + if (off) + goto Done; + + num = sscanf(page,"%i",&tmp); + if (!num) { + error = -EINVAL; + goto Done; + } - if (rv<0) { - fs->trigger_tb[i].registered = 0; - PERROR("can not register WP, id = %d\n", id_high+i); - continue; //goto exit;//XXX: I should remove registered trigger - } - fs->stat = ENABLED; + if (tmp) { + fi_debug = 1; + } else { + fi_debug = 0; } - if (fs->stat == DISABLED) { - PERROR("none of trigger can be active, enable faultset failed!\n"); - goto exit; - } - PINFO("enable #%d fs\n", index); - rv = 0; - exit: - return rv; -} -static int disable_faultset(int index){ - int rv=0; - int i=0; - int id_high=0; - struct fi_faultset *fs; - - PDEBUG("trying to disable faultset# %d\n", index); - if(index<0 || index>MAX_FAULTSET || fs_tb[index].stat!=ENABLED) { - rv=-EINVAL; - goto exit; - } - - id_high=index<<16; - fs = &fs_tb[index]; - - for (i=0; i<MAX_TRIGGER_PER_FAULTSET && (fs->trigger_tb[i].wp.addr); i++){ - rv = fi_pf_unregister(id_high+i); - if (rv>=0) { - fs->trigger_tb[i].registered = 0; - }else{ - PERROR("can not unregister wp, id=%d\n", id_high+i); - } - } - fs->stat = DISABLED; - PINFO("disable #%d fs\n", index); - rv = 0; - exit: - return rv; + ret = error ? error : count; + Done: + up(&fi_sem); + return ret; } -/*END OF fault set functions*/ +static struct subsystem fi_subsys = { + .kobj = { .name = "fault_injection" }, +}; -static int fi_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg){ - int rv=0; - int i=0; - - switch (cmd){ - default: - rv = -ENOTTY; - break; - case FIIOC_INSERT: - rv = insert_faultset((struct fi_faultset *)arg); - break; - case FIIOC_REMOVE: - get_user(i, (int *)arg); - rv = remove_faultset(i); - break; - case FIIOC_ENABLE: - get_user(i, (int *)arg); - rv = enable_faultset(i); - break; - case FIIOC_DISABLE: - get_user(i, (int *)arg); - rv = disable_faultset(i); - break; - }; - return rv; +static struct subsys_attribute fi_subsys_attr_enabled = { + .attr = { .name = "enabled", .mode = 0644 }, + .show = fi_enabled_show, + .store = fi_enabled_store, }; -static spinlock_t counter_lock = SPIN_LOCK_UNLOCKED; -static int counter=0; +static struct subsys_attribute fi_subsys_attr_debug = { + .attr = { .name = "debug", .mode = 0644 }, + .show = fi_debug_show, + .store = fi_debug_store, +}; -static int fi_open(struct inode *inode, struct file *file){ - int rv=0; - - spin_lock(&counter_lock); - if (counter) { - rv = -1; - goto out; - } - counter++; - MOD_INC_USE_COUNT; - out: - spin_unlock(&counter_lock); - return rv; -} +/* + * =============================================================== + * <>-- Interceptor Containing Subsystem (interceptor_subsys) --<> + * --------------------------------------------------------------- + */ + +static struct subsystem interceptor_subsys = { + .kobj = { .name = "interceptors" }, + .parent = &fi_subsys, +}; +/* + * =========================================================== + * <>-- Trigger Subsystem (trigger_subsys) --<> + * ----------------------------------------------------------- + */ -static int fi_release(struct inode *inode, struct file *file){ - spin_lock(&counter_lock); - counter--; - MOD_DEC_USE_COUNT; - spin_unlock(&counter_lock); - return 0; +static ssize_t trigger_attr_show(struct kobject * kobj, + struct attribute * attr, + char * page, size_t count, + loff_t off) +{ + 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,count,off) : 0; } -static int dump_faultset(struct fi_faultset *fs, char *buf){ - int i=0, n=0; - int rv=0; - static char *op_name[]={ - "NOTHING", "SET", "AND", "OR", "NOT", "XOR", "NAND", "NOR", "ADD", "SUB", - }; - - if (fs->stat == NOUSED){ - n += sprintf(buf+n, "This faultset is not used!\n"); - goto exit1; - } - - n += sprintf( buf+n, "This will dump fault set information, stat=%d\n", - fs->stat); - n += sprintf( buf+n, "The faultset is %s\n", - (fs->stat==ENABLED)? "ENABLED" : - ((fs->stat==DISABLED)? "DISABLED": - "UNKNOWN!")); - for(i=0; i<MAX_TRIGGER_PER_FAULTSET; i++){ - int j=0; - struct fi_trigger *tri; - - tri = &fs->trigger_tb[i]; - if (tri->wp.addr==0) break; - n += sprintf(buf+n, "watchpoint #%d phy_addr=%#lx, type=%#x, len=%d\n", - i, tri->wp.addr, tri->wp.type, access_len(tri->wp.type)); - n += sprintf(buf+n, "bitmask=%#x, min=%#x, max=%#x, skip=%d, " - "stop=%d, protection=%#x, Hertz=%#x\n", - tri->bitmask, tri->min, tri->max, tri->skip, - tri->stop, tri->protection, tri->hertz); - - n += sprintf(buf+n, "ops list\n"); - for(j=0; j<MAX_OP_PER_TRIGGER; j++){ - if (tri->ops[j].opcode==FI_NOTHING) break; - n += sprintf(buf+n, "opcode=%s, operand=%#x\n", - op_name[tri->ops[j].opcode], tri->ops[j].operand); - } - - if (is_MMIO(tri->wp.type)) { - n += sprintf(buf+n, "watchpoint status:%s\n", - (tri->registered==1) ? - "ENABLED" : "DISABLED"); - } else { - n += sprintf(buf+n, "watchpoint status:%s\n", - (tri->registered==1) ? "ENABLED" : "DISABLED"); - } +static ssize_t trigger_wp_read(struct trigger * p, char * page, + size_t count, loff_t off) +{ + dbg("wp.addr = %#lx, wp.type = %#x", p->wp.addr, p->wp.type); + return off ? 0 : snprintf(page,count,"%#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, +}; - n += sprintf(buf+n, "-----------------\n"); - } - - exit1: - n += sprintf(buf+n, "\n"); - rv = n; - return rv; +static ssize_t trigger_bitmask_read(struct trigger * p, char * page, + size_t count, loff_t off) +{ + dbg("bitmask = %#x", p->bitmask); + return off ? 0 : snprintf(page,count,"%#x\n",p->bitmask); } +static struct trigger_attribute trigger_attr_bitmask = { + .attr = { .name = "bitmask", .mode = S_IRUGO }, + .show = trigger_bitmask_read, +}; -static int fi_proc_read( char* page, char **start, off_t off, int count, int *eof, void *data){ - return dump_faultset((struct fi_faultset *)data, page); +static ssize_t trigger_min_read(struct trigger * p, char * page, + size_t count, loff_t off) +{ + dbg("min = %i", p->min); + return off ? 0 : snprintf(page,count,"%i\n",p->min); +} +static struct trigger_attribute trigger_attr_min = { + .attr = { .name = "min", .mode = S_IRUGO }, + .show = trigger_min_read, }; -static int fi_proc_write( struct file *file, const char *buffer, unsigned long count, void *data){ - return 0;//dummy now :-) +static ssize_t trigger_max_read(struct trigger * p, char * page, + size_t count, loff_t off) +{ + dbg("max = %i", p->max); + return off ? 0 : snprintf(page,count,"%i\n",p->max); +} +static struct trigger_attribute trigger_attr_max = { + .attr = { .name = "max", .mode = S_IRUGO }, + .show = trigger_max_read, }; -static struct file_operations fi_fops[1]={ - { - owner: THIS_MODULE, - ioctl: fi_ioctl, - open: fi_open, - release: fi_release - } +static ssize_t trigger_skip_read(struct trigger * p, char * page, + size_t count, loff_t off) +{ + dbg("skip = %i", p->skip); + return off ? 0 : snprintf(page,count,"%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, + size_t count, loff_t off) +{ + dbg("stop = %i", p->stop); + return off ? 0 : snprintf(page,count,"%i\n",p->stop); +} +static struct trigger_attribute trigger_attr_stop = { + .attr = { .name = "stop", .mode = S_IRUGO }, + .show = trigger_stop_read, +}; -static struct proc_dir_entry *root_entry; -static struct proc_dir_entry *fs_entry; -static int fi_major = FI_MAJOR; +static ssize_t trigger_protection_read(struct trigger * p, char * page, + size_t count, loff_t off) +{ + dbg("protection = %i", p->protection); + return off ? 0 : snprintf(page,count,"%i\n",p->protection); +} +static struct trigger_attribute trigger_attr_protection = { + .attr = { .name = "protection", .mode = S_IRUGO }, + .show = trigger_protection_read, +}; -static int __init fi_dm_init(void){ - int i=0 ; - int rv=0; - - PDEBUG("FI version: %s\n", FI_VERSION); +static ssize_t trigger_hertz_read(struct trigger * p, char * page, + size_t count, loff_t off) +{ + dbg("hertz = %i", p->hertz); + return off ? 0 : snprintf(page,count,"%i\n",p->hertz); +} +static struct trigger_attribute trigger_attr_hertz = { + .attr = { .name = "hertz", .mode = S_IRUGO }, + .show = trigger_hertz_read, +}; - /*Setup proc file*/ - root_entry = proc_mkdir(MODULE_NAME, NULL); - if (!root_entry){ - PERROR("can not register proc %s\n", MODULE_NAME); - rv = -ENOMEM; - goto exit1; - } - root_entry->owner=THIS_MODULE; - - for (i=0; i<MAX_FAULTSET; i++){ - char name[20]; - - sprintf(name, "fs%d", i); - fs_entry = create_proc_entry(name, 0644, root_entry); - if (!fs_entry){ - PERROR("can not register proc %s\n", name); - rv = -ENOMEM; - goto exit2; - } - fs_entry->owner = THIS_MODULE; - fs_entry->read_proc = fi_proc_read; - fs_entry->write_proc= fi_proc_write; - fs_entry->data = &fs_tb[i]; - } - - rv = register_chrdev(fi_major, MODULE_NAME, fi_fops); - if (rv<0){ - PERROR("can not register device, major=%d\n", fi_major); - goto exit3; - } - if (fi_major==0) fi_major = rv;/*dynamic alloc major number*/ - PDEBUG("Setup IOCTL as char device, major=%d\n", fi_major); - - /*clear fs_tb */ - memset(fs_tb, 0, sizeof(fs_tb)); - EXPORT_NO_SYMBOLS; - return 0; +static ssize_t trigger_registered_read(struct trigger * p, char * page, + size_t count, loff_t off) +{ + dbg("registered = %i", p->registered); + return off ? 0 : snprintf(page,count,"%i\n",p->registered); +} +static struct trigger_attribute trigger_attr_registered = { + .attr = { .name = "registered", .mode = S_IRUGO }, + .show = trigger_registered_read, +}; - exit3: - - i=MAX_FAULTSET;/* error happen above 2, you must clear ALL entries*/ - exit2: - i--; - for (;i>=0;i--){ - char name[20]; +static ssize_t trigger_count_read(struct trigger * p, char * page, + size_t count, loff_t off) +{ + dbg("count = %i", atomic_read(&p->count)); + return off ? 0 : snprintf(page,count,"%i\n",atomic_read(&p->count)); +} +static struct trigger_attribute trigger_attr_count = { + .attr = { .name = "count", .mode = S_IRUGO }, + .show = trigger_count_read, +}; - sprintf(name, "fs%d", i); - remove_proc_entry(name, root_entry); +static ssize_t trigger_type_read(struct trigger * p, char * page, + size_t count, loff_t off) +{ + int rv; + + dbg("type = %i", p->type); + if (off) + return 0; + + switch (p->type) { + case INTERCEPTOR_TYPE_MMIO: + rv = snprintf(page,count,"MMIO\n"); + break; + default: + rv = snprintf(page,count,"UNKNOWN\n"); } - remove_proc_entry(MODULE_NAME, NULL); - exit1: + return rv; +} +static struct trigger_attribute trigger_attr_type = { + .attr = { .name = "type", .mode = S_IRUGO }, + .show = trigger_type_read, }; +static ssize_t trigger_opcode_read(struct trigger * p, char * page, + size_t count, loff_t off) +{ + dbg("opcode = %i", p->opcode); + return off ? 0 : snprintf(page,count,"%i\n",p->opcode); +} +static struct trigger_attribute trigger_attr_opcode = { + .attr = { .name = "opcode", .mode = S_IRUGO }, + .show = trigger_opcode_read, +}; -static void __exit fi_dm_cleanup(void){ - int i=0; +static ssize_t trigger_operand_read(struct trigger * p, char * page, + size_t count, loff_t off) +{ + dbg("operand = %i", p->operand); + return off ? 0 : snprintf(page,count,"%i\n",p->operand); +} +static struct trigger_attribute trigger_attr_operand = { + .attr = { .name = "operand", .mode = S_IRUGO }, + .show = trigger_operand_read, +}; - unregister_chrdev(fi_major, MODULE_NAME); - - /*unregister proc files*/ - remove_proc_entry("globalcounter", root_entry); - for (i=MAX_FAULTSET-1;i>=0;i--){ - char name[20]; - - sprintf(name, "fs%d", i); - remove_proc_entry(name, root_entry); +static ssize_t trigger_ctl_show(struct subsystem * s, char * page, + size_t count, loff_t off) +{ + return 0; +} +static ssize_t trigger_ctl_store(struct subsystem * s, const char * page, + size_t count, loff_t off) +{ + char ctl[16]; + char type[16]; + int num; + int error; + int ret = 0; + struct trigger tmp; + + if (off) + return 0; + + /* 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 */ + /* ctl nm tp 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 */ + type, /* 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"); + error = -EINVAL; + goto Done; } - remove_proc_entry(MODULE_NAME, NULL); + + dbg("type = '%s'", type); + if (!strncmp(type, "MMIO", 4)) + tmp.type = INTERCEPTOR_TYPE_MMIO; + else + tmp.type = INTERCE... [truncated message content] |