/*
 * fault-model.c -- fault injection code for drivers
 *
 * Copyright (C) 2003 Mike Swift
 * Copyright (c) 1999 Wee Teck Ng
 *
 * The source code in this file can be freely used, adapted,
 * and redistributed in source or binary form, so long as an
 * acknowledgment appears in derived source files.  No warranty 
 * is attached; * we cannot take responsibility for errors or 
 * fitness for use.
 *
 */


/*
 * Fault injector for testing the usefulness of NOOKS
 * 
 * Adapted from the SWIFI tools used by Wee Teck Ng to evaluate the RIO
 * file cache at the University of Michigan
 * 
 */

/* 
 * This tool can inject faults into modules, whether they are loaded into a 
 * nook or loaded into the kernel (for comparison testing).
 * 
 * There are several classes of faults emulated:
 * - Corruption of text
 *    - corruption
 *    - simulated programming faults
 *         - skip initialization (immediate write to EBP-x)
 *         - remove instruction (replace with NOP)
 *	   - incorrect source/destination (corrupted)
 *         - remove jmp or rep instruction
 *         - change address computation for memory access (not stack)
 *	   - change termination condition for loop (change repeat to repeat 
 *           -while equal, change condition to !condition
	   - remove instructions loading registers from arguments (ebp+x)
 *        
 * - Corruption of stack
 * - Corruption of heap
 * - copy overruns
 * - use after free
 */

#if 0
#include <linux/kernel.h>
#include <linux/kallsyms.h>
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/smp_lock.h>
#include <asm/uaccess.h>
#include <asm/delay.h>
#include <asm/page.h>
#endif
#include "ddb.h"
#include "db_sym.h"
#include "swifi.h"

#include "extra.h"
#include <assert.h>

#define CRASH_INTERVAL	8192
#define FI_MASK			0xfff
#define P50     0x3fffffff      /* 50% of max rand */
#define P94     0x7851eb84      /* 94% of max rand */
#define NOP		0x90

unsigned long randomSeed=0;		/* random number */
unsigned long injectFault=1;		/* inject fault ? */
unsigned long diskTest=0;	        /* run disk test instead of rio */
unsigned long faultInjected=0;	        /* has fault been injected? */
unsigned long crashInterval=0;	        /* interval between injecting fault */
unsigned long crashCount=0;	        /* number of times fault is injected */
unsigned long faultType;			 
unsigned long numFaults;
char *crashAddr=0;		        /* track current malloc */
int crashToggle=1;
int text_fault(char *mod_name, pswifi_result_t res);
int stack_fault(pswifi_result_t res);
int heap_fault(pswifi_result_t res);
int direct_fault(int fault_address, int fault_content, pswifi_result_t res);
int direct_fault1(int fault_address, int fault_content, pswifi_result_t res);
int while1(void);

int *testVA;

#if 0
#define PDEBUG(fmt, args...) \
do { \
      printk( KERN_ALERT "SWIFI: " fmt, ## args); \
} while (0)
#else
#include <stdio.h>
#define PDEBUG(args) /* (printf args) */
#endif

#define inline

#ifdef CONFIG_SWIFI

#if 0
static inline long
get_mod_name(const char *user_name, char **buf)
{
	unsigned long page;
	long retval;

	page = __get_free_page(GFP_KERNEL);
	if (!page)
		return -ENOMEM;

	retval = strncpy_from_user((char *)page, user_name, PAGE_SIZE);
	if (retval > 0) {
		if (retval < PAGE_SIZE) {
			*buf = (char *)page;
			return retval;
		}
		retval = -ENAMETOOLONG;
	} else if (!retval)
		retval = -EINVAL;

	free_page(page);
	return retval;
}

static inline void
put_mod_name(char *buf)
{
	free_page((unsigned long)buf);
}
#endif

long 
sys_inject_fault(char * module_name,
		 unsigned long argFaultType,
		 unsigned long argRandomSeed,
		 unsigned long argNumFaults,
		 pswifi_result_t result_record,
		 unsigned long argInjectFault)
{   
  int result = 0;
  unsigned long fault_address = 0; 
  unsigned long fault_data = 0 ; 
  char * kern_name = NULL;
#if 0
  struct module * mod = NULL;
#endif
  int found = 0;
  pswifi_result_t res = NULL;

  if (argNumFaults > SWIFI_MAX_FAULTS) {
    result = -E2BIG;
    goto Cleanup;
  }
  res = (pswifi_result_t) malloc((1+argNumFaults) * sizeof(swifi_result_t));
  if (res == NULL) {
    result = -ENOMEM;
    goto Cleanup;
  }
  memset(res, 0, (1 + argNumFaults) * sizeof(swifi_result_t));
  
  /*
  // Capture the name of the module from usermode
  */

#if 0
  result = get_mod_name(module_name, &kern_name);
  if (result < 0) {
    goto Cleanup;
  }
#endif

  kern_name= module_name;




#if 0
    lock_kernel();

    for (mod = module_list; mod ; mod = mod->next) {
      if (strcmp(kern_name, mod->name) == 0) {
	found = 1;
	break;
      }
    }
    unlock_kernel(); 
    if (!found) {
      result = -ENOENT;
      goto Cleanup;
    }
#endif
		
  numFaults = argNumFaults;
  faultType = argFaultType;
  randomSeed = argRandomSeed;
  injectFault = argInjectFault;


  if(faultType>=DISK_TEST) {
    faultType=faultType-DISK_TEST;
    diskTest=1;
  }
  if(faultType==STATS) {
#if 0    
    extern long time_vmp, n_vmp;
    extern long time_pmp, n_pmp;

    PDEBUG("# vm_map_protect=%ld, total cycle=%ld\n", n_vmp, time_vmp);
    PDEBUG("# pmap_protect=%ld, total cycle=%ld\n", n_pmp, time_pmp);
    n_vmp=0; time_vmp=0;
    n_pmp=0; time_pmp=0;
#endif
  } else if (faultType == DIRECT_FAULT) {
    fault_address = numFaults;
    fault_data = randomSeed;
    PDEBUG(("sys inject fault, type %ld, addr=%lx, flip bit%lx\n", 
	   faultType, fault_address, fault_data));
  } else if (faultType == DIRECT_FAULT1) {
    fault_address = numFaults;
    fault_data = randomSeed;
    PDEBUG(("sys inject fault, type %ld, addr=%lx, zero bytes %lx\n", 
	   faultType, fault_address, fault_data));
  } else {
    PDEBUG(("sys inject fault, type %ld, seed=%ld, fault=%ld\n", 
	   faultType, randomSeed, numFaults));
  }
  faultInjected=1;
  
  srandom(randomSeed);
  /* set warm reboot, leave RAM unchanged  
   * 0 : don't inject fault
   * 1 : run POST, wipe out memory
   * 2 : don't test memory
   * 3 : don't change memory (doesn't work)
   * 4 : don't sync registry
   */
  
  /* default number of faults is 5 */
  if(numFaults<=0 || numFaults>100) numFaults=5;
  
  switch(faultType)
    {
    case TEXT_FAULT: 
      result = text_fault(module_name, res); 
      break;
    case STACK_FAULT: 
      result = stack_fault(res); 
      break;
    case HEAP_FAULT: 
      result = heap_fault(res); 
      break;
    case INIT_FAULT:
    case NOP_FAULT: 
    case DST_FAULT: 
    case SRC_FAULT: 
    case BRANCH_FAULT: 
    case PTR_FAULT: 
    case LOOP_FAULT: 
    case INTERFACE_FAULT: 
    case IRQ_FAULT:
      result = text_fault(module_name, res); 
      break;
    case FREE_FAULT: 
    case BCOPY_FAULT: 
    case SYNC_FAULT:
    case ALLOC_FAULT:
      crashInterval=CRASH_INTERVAL; 	/* interval between crash */
      break;
    case MEM_LEAK_FAULT: 
      crashToggle=0;
      crashInterval=CRASH_INTERVAL; 	/* interval between crash */
      break;
    case PANIC_FAULT: 
      panic("testing panic"); 
      result = 0;
      break;
      /*        case WP_FAULT: page_reg_fault(random()); break; */
    case DIRECT_FAULT:
      {
	direct_fault(fault_address, fault_data, res);
	break;
      }
    case DIRECT_FAULT1: 
      {
	result = direct_fault1(fault_address, fault_data, res);
	
	break;
      }
      /*    	case PAGE_REG_DUMP: rio_dump(); break; */
    case WHILE1_FAULT: 
      {
	
	result = while1();
	
	break;
      }
      /* case CPU_RESET_FAULT: cpu_reset(); break; */;
    case COW_FAULT: 
      {
				/* test writing to kernel text. freebsd currently do a COW on a
				 * write to kernel text.
				 */
	unsigned long *addr1, *addr2;
	
	addr1 = (unsigned long *) 0xf0212000;
	addr2 = (unsigned long *) 0xf0212010;
	PDEBUG(("%p=%lx, %p=%lx\n", addr1, *addr1, addr2, *addr2));
	/*
	__asm__ ("movl $0xf0212000, %eax\n\t" \
		 "movl $6, 0(%eax)\n\t" \
		 "movl $6, 4(%eax)\n\t");
	*/
	/* Not implemented on MINIX */
	assert(0);
	addr1 = (unsigned long *) 0xf0212000;
	addr2 = (unsigned long *) 0xf0212010;
	PDEBUG(("after injecting fault\n"));
	PDEBUG(("%p=%lx, %p=%lx\n", addr1, *addr1, addr2, *addr2));
	result = 0;
	break;
      }
    
    case DEBUGGER_FAULT: 
      PDEBUG(("Debugger fault"));
      /*
      __asm__ ("movl %cr4, %ecx\n\t" \
	       "movl $42, %ecx; .byte 0x0f, 0x32\n\t" \
	       "movl $377, %ecx; .byte 0x0f, 0x32\n\t");
      */
      /* Not implemented on MINIX */
      assert(0);
      result = 0;
      break;
    default: PDEBUG(("unknown fault type %ld\n", faultType)); break;
    }
  if (copy_to_user(result_record, res, argNumFaults * sizeof(swifi_result_t))) {
    result = -EFAULT;
  }
 Cleanup:
#if 0
  if (kern_name != NULL) {
    put_mod_name(kern_name);
  }
#endif
  if (res != NULL) {
    free(res);
  }

  return (result);
}

int while1(void)
{
  int i=0;

  PDEBUG(("entering into while 1 loop\n"));
  while(1) { 
    udelay(20000); 
    PDEBUG(("delay %4d secs, cpl=0x%x, ipend=0x%x\n", i+=5, 20, 30)); 
    if(i>(100 * 2500)) 
      break;
  }
  return(0);
}


int direct_fault(int fault_address, int fault_content, pswifi_result_t res)
{   
  unsigned long *addr;
  int flip_bit=0;


  addr = (unsigned long *) (PAGE_OFFSET + fault_address);

  PDEBUG(("%p:0x%lx => ", addr, *addr));
  
  flip_bit = 1 << fault_content;

  res[0].address = (unsigned long) addr;
  res[0].old = *addr;
  res[0].new = (*addr) ^ flip_bit;

  if (injectFault) {
    *addr = (*addr) ^ flip_bit; 
  }
  PDEBUG(("%lx\n", *addr));
  return(0);
}

int direct_fault1(int fault_address, int fault_content, pswifi_result_t res)
{   
  unsigned long *addr, data;


  addr = (unsigned long *) (PAGE_OFFSET + fault_address);
  
  PDEBUG(("%p:%lx => ", addr, *addr));
  
  
  data = *addr;
  if(fault_content==1) {
    data = data & 0xffffff00;
    data = data | 0x00000090;
  } else if(fault_content==2) {
    data = data & 0xffff0000;
    data = data | 0x00009090;
  } else if(fault_content==3) {
    data = data & 0xff000000;
    data = data | 0x00909090;
  } else if(fault_content==4) {
    data = 0x90909090;
  } 
  res[0].address = (unsigned long) addr;
  res[0].old = *addr;
  res[0].new = data;
  if (injectFault) {
    *addr = data;
  }

  PDEBUG(("%lx\n", *addr));
  
    
  return(0);
}




/*
#include <linux/sched.h>
*/

#define MAX_NUM_TASKS 20

struct task_struct *
find_task(void)
{
  struct task_struct * task = NULL, *result = NULL ;
  int i,j;
  i = 1 + (random() % MAX_NUM_TASKS);
  j = i;

  
  do {
#if 0
    read_lock(&tasklist_lock);
#endif
    for_each_task(task) { 
      if (--i == 0) {
	result = task;
	break;
      }
    }
#if 0
    read_unlock(&tasklist_lock);
#endif
  } while ((i > 0) && (i != j));

  return(result);
}

int
stack_fault(pswifi_result_t res)
{   
  unsigned long *addr, size, taddr;
  int flip_bit=0;
  int count=0;
  struct task_struct *task = NULL;

  while(count < numFaults) {
    task = find_task();
    if (task == NULL) {
      return(-1);
    }

    size = (unsigned long) task + TASK_SIZE - task->thread.esp; 

    PDEBUG(("stack range=%lx-%lx\n", 
	   (unsigned long) task->thread.esp, 
	   (unsigned long) task + TASK_SIZE));

    addr = (unsigned long *) ((long) task->thread.esp + 
			      (random()&~0x3)%size);  
    taddr=(unsigned long) addr;
    flip_bit = random() & 0x1f;
    PDEBUG(("%lx:%lx flip bit %d => ", taddr, *addr, flip_bit));
    flip_bit = 1 << flip_bit;
    res[count].address = taddr;
    res[count].old = *addr;
    res[count].new = (*addr) ^ flip_bit;
    if (injectFault) {
      *addr = ((*addr)^flip_bit); 
    }
    PDEBUG(("%lx\n", *addr));
    count++;
  }
  return(0);
}



/*
// Instead of dealing with heaps directly, we look at the area cache of pages 
// and vm pages and find an address there.
*/


int heap_fault(pswifi_result_t res)
{   
#ifdef notdef
  unsigned long *addr, taddr;
  int flip_bit=0;
  int count=0;
  unsigned long flags;
  struct list_head *next;

   addr = (unsigned long *) (map->address + (random()&~0xf)%map->size); 
   
   taddr=(unsigned long) addr;
   flip_bit = random() & 0x1f;
   PDEBUG("heap range=%lx-%lx ", map->address, map->address + map->size);
   PDEBUG("%lx:%lx flip bit %d => ", taddr, *addr, flip_bit);
   flip_bit = 1 << flip_bit;
   res[count].address = taddr;
   res[count].old = *addr;
   res[count].new = (*addr) ^ flip_bit;

   if (injectFault) {
     *addr = ((*addr)^flip_bit); 
   }
   PDEBUG("%lx\n", *addr);
   count++;   
 } while (count < numFaults);
#endif
  return(-1);
  
}


unsigned long	
do_fault_copy_from_user (void *kaddr, const void *udaddr, unsigned long len,
		      unsigned long (* copy_fn) (void *, const void *, unsigned long))
{   
  unsigned int prob, i=0;

  if ( faultInjected && (faultType==BCOPY_FAULT) ) {

    if (++crashCount == crashInterval) {   
      
      crashCount=0;
      prob = random();
      crashInterval = CRASH_INTERVAL + (random() & FI_MASK);
      
      if (prob < P50) {                    /* corrupt 1 QW         */
	i=1; 
      } else if (prob < P94) {               /* corrupt 2 - 1024 QW  */
	i = prob & 0x3fe;
	while(!i) {
	  i = random() & 0x3fe; 
	}
      } else {                            /* corrupt 2-4 pages    */
	i= prob & 0xc00;
	while(!i) {
	  i = random() & 0xc00; 
	}
      }
      PDEBUG(("copyin: %p to %p, len=%ld overrun=%d, Intvl=%ld, inj=%ld\n", 
	     udaddr, kaddr, len, i, crashInterval, faultInjected));
      if (faultInjected++ <numFaults) {
	len += i;
      } else {
	faultInjected = 0;
      }
      i = 1;
    }
    return(copy_fn(kaddr, udaddr, len));
  } else {
    return(copy_fn(kaddr, udaddr, len));
  }
}

unsigned long
do_fault_copy_to_user(void *udaddr, const void *kaddr, unsigned long len,
		   unsigned long (* copy_fn) (void *, 
					      const void *, 
					      unsigned long))
{   
  unsigned int prob, i=0;

  if( faultInjected && (faultType==BCOPY_FAULT) ){
    crashCount++;
    if (crashCount == crashInterval) {
      crashCount=0;
      prob = random();
      crashInterval = CRASH_INTERVAL + (random() & FI_MASK);

      if ( prob < P50) {                    /* corrupt 1 QW         */
	i=1; 
      } else if(prob < P94) {               /* corrupt 2 - 1024 QW  */
	i = prob & 0x3fe;
	while (!i) {
	  i = random() & 0x3fe; 
	}
      } else {
	i = prob & 0xc00;
	while(!i) {
	  i = random() & 0xc00; 
	}
      }
      PDEBUG(("copyout: %p to %p, len=%ld overrun=%d, Intvl=%ld, inj=%ld\n",
	     kaddr, udaddr, len, i, crashInterval, faultInjected));
      if (faultInjected++ <numFaults) {
	len+=i;
      } else  {
	faultInjected = 0;
      }
      i=1;
    }
    return(copy_fn(udaddr, kaddr, len));
  } else 
    return(copy_fn(udaddr, kaddr, len));
}


unsigned long
swifi___generic_copy_from_user (void *kaddr, void *udaddr, unsigned long len)
{
  return(do_fault_copy_from_user(kaddr, 
				 udaddr, 
				 len, 
				 __generic_copy_from_user));
}

unsigned long	
swifi___generic_copy_to_user(void *udaddr, void *kaddr, unsigned long len)
{
  return(do_fault_copy_to_user(udaddr, 
			       kaddr, 
			       len, 
			       __generic_copy_to_user));
}



void *
swifi_memcpy_fn (void *to, void *from, size_t len)
{   
  unsigned int prob, i=0;

  if( faultInjected && (faultType==BCOPY_FAULT) ) {
    crashCount++;
    if (crashCount == crashInterval) {
      crashCount=0;
      prob = random();
      crashInterval = CRASH_INTERVAL + (random() & FI_MASK);

      if (prob < P50) {                    /* corrupt 1 QW         */
               i=1; 
      } else if (prob < P94) {               /* corrupt 2 - 1024 QW  */
	i= prob & 0x3fe;
	while(!i) {
	  i = random() & 0x3fe; 
	}
      } else {                            /* corrupt 2-4 pages    */
	i=prob&0xc00;
	while(!i) {
	  i = random() & 0xc00; 
	}
      }
    
      PDEBUG(("memcpy: %p to %p, len=%d overrun=%d, Intvl=%ld, inj=%ld\n", 
	     from, to, len, i, crashInterval, faultInjected));
      if(faultInjected++ <numFaults) len+=i;
      else faultInjected=0;
      i=1;
    }
    return(memcpy(to, from, len));
  } else 
    return(memcpy(to, from, len));
}


void *
swifi_memmove_fn (void *to, void *from, size_t len)
{   
  unsigned int prob, i=0;

  if( faultInjected && (faultType==BCOPY_FAULT) ) {
    crashCount++;
    if (crashCount == crashInterval) {
      crashCount=0;
      prob = random();
      crashInterval = CRASH_INTERVAL + (random() & FI_MASK);

      if (prob < P50) {                    /* corrupt 1 QW         */
               i=1; 
      } else if (prob < P94) {               /* corrupt 2 - 1024 QW  */
	i= prob & 0x3fe;
	while(!i) {
	  i = random() & 0x3fe; 
	}
      } else {                            /* corrupt 2-4 pages    */
	i=prob&0xc00;
	while(!i) {
	  i = random() & 0xc00; 
	}
      }
    
      PDEBUG(("memmove: %p to %p, len=%d overrun=%d, Intvl=%ld, inj=%ld\n", 
	     from, to, len, i, crashInterval, faultInjected));
      if(faultInjected++ <numFaults) len+=i;
      else faultInjected=0;
      i=1;
    }
    return(memmove(to, from, len));
  } else 
    return(memmove(to, from, len));
}


void *
memmove_fn(void *to, void *from, size_t len)
{
  return(memmove(to, from, len));
}



void *
memcpy_fn(void *to, void *from, size_t len)
{
  return(memcpy(to, from, len));
}




void
do_fault_kfree(void *addr, void (* kfree_fn)(const void *))
{   
  if(addr == crashAddr) {
    crashAddr=0;
  }
  if (faultInjected && (faultType==FREE_FAULT || 
			faultType==MEM_LEAK_FAULT)) {
    crashCount++;
    if(crashCount>=crashInterval) {   
      
      /* alternate between premature freeing and non-free */
      if(crashToggle) {
	if(crashAddr) { 
	  PDEBUG(("malloc : freeing %p prematurely\n", 
		 crashAddr));
	  kfree_fn(crashAddr);
	  kfree_fn(addr);
	  crashAddr=0;
	  crashToggle=0;
	  crashCount=0;
	  crashInterval = CRASH_INTERVAL + (random()&FI_MASK);
	  if (faultInjected++ > numFaults) {
	    faultInjected=0;
	  }
	} 
      } else {
	PDEBUG(("free: don't free %p\n", addr));
	if(faultInjected++ > numFaults) {
	  faultInjected=0;
	}
	if(faultType==FREE_FAULT) {
	  crashToggle=1;
	}
	crashCount=0;
	crashInterval = CRASH_INTERVAL + (random()&FI_MASK);
      }
    }
  } else {
    kfree_fn(addr);
  }
}

#if 0
void
swifi_kfree(const void *addr)
{
  do_fault_kfree((void *) addr, kfree);
}
#endif


void do_vfree(const void * addr)
{
  vfree((void *) addr);
}


void
swifi_vfree(void *addr)
{
  do_fault_kfree(addr, do_vfree);
}




void *
do_fault_kmalloc(size_t size, 
		 int flags,
		 void * (* kmalloc_fn)(size_t size, int flags))
{
  if (faultInjected && (faultType==ALLOC_FAULT)) {
    crashCount++;
    if(crashCount>=crashInterval) {   
      PDEBUG(("kmalloc : returning null\n"));
      crashCount=0;
      crashInterval = CRASH_INTERVAL + (random()&FI_MASK);
      if (faultInjected++ > numFaults) {
	faultInjected=0;
	return(NULL);
      }

    }
  }

  return(kmalloc_fn(size, flags));
}


#if 0
void *
swifi_kmalloc(size_t size, int flags)
{
  return(do_fault_kmalloc(size, flags, kmalloc));
}
#endif



void * do_fault_vmalloc(unsigned long size, 
			int gfp_mask, 
			pgprot_t prot,
			void * (*vmalloc_fn)(unsigned long size, 
					     int gfp_mask, 
					     pgprot_t prot))
{
  if (faultInjected && (faultType==ALLOC_FAULT)) {
    crashCount++;
    if(crashCount>=crashInterval) {   
      PDEBUG(("vmalloc : returning null\n"));
      crashCount=0;
      crashInterval = CRASH_INTERVAL + (random()&FI_MASK);
      if (faultInjected++ > numFaults) {
	faultInjected=0;
	return(NULL);
      }

    }
  }
  return(vmalloc_fn(size, gfp_mask, prot));
}

void *
swifi___vmalloc(unsigned long size, int gfp_mask, pgprot_t prot)
{
  return(do_fault_vmalloc(size, gfp_mask, prot, __vmalloc));
}
 


#if 0
typedef struct section_callback {
  const char * module_name;
  const char * section_name;
  unsigned long sec_start;
  unsigned long sec_end;
} section_callback_t;

static int
text_section_callback(void *token, 
		      const char *modname, 
		      const char *secname,
		      ElfW(Addr) secstart, 
		      ElfW(Addr) secend, 
		      ElfW(Word) secflags)
{
  section_callback_t * info = (section_callback_t *) token;
  
  if ((strcmp(modname, info->module_name) == 0) &&
      (strcmp(secname, info->section_name) == 0)) {
    info->sec_start = secstart;
    info->sec_end = secend;
    return(1);
  }
  return(0);
}
#endif


int text_fault(char *mod_name, pswifi_result_t res)
{   
  unsigned long *addr, text_size, offset, page, taddr;
  unsigned long btext, etext;

  int count, flip_bit=0, len, rc;
  unsigned char *c;
#if 0
  struct module * module;
  section_callback_t info;
#endif

#define MAX_NUM_MODULES 10

  /* inject faults into text space */

  for(count=0; count<numFaults; count++) {
    int i = 1 + (random() % MAX_NUM_MODULES);
    int j = i;
#if 0
    module = mod;
#endif

#if 0
    info.module_name = module->name;
    info.module_name = "<module-name>";
    info.section_name = ".text";

    kallsyms_sections(&info, text_section_callback);
    if (info.sec_start == 0 ) {
      return(-1);
    }
#endif

    load_nlist(mod_name, &btext, &etext);

#if 0
    btext = info.sec_start;
    etext = info.sec_end;
#endif
    text_size = etext - btext;
    
    PDEBUG(("text=%lx-%lx, size=%lx\n", btext, etext, text_size));
    
    addr = (unsigned long *) 
      (btext + ((unsigned long) (random()&~0xf) % text_size)); 
    
    /* now the tricky part */

    taddr=(unsigned long) addr;
    if( faultType==INIT_FAULT || 
	faultType==NOP_FAULT || 
	faultType==DST_FAULT || 
	faultType==SRC_FAULT ||
	faultType==BRANCH_FAULT || 
	faultType==PTR_FAULT || 
	faultType==LOOP_FAULT || 
	faultType==INTERFACE_FAULT ||
	faultType==IRQ_FAULT ) {
      addr = (unsigned long *) find_faulty_instr(taddr, faultType, &len);
      /* do it over again if we can't find the right instruction */
      if(!addr || !len ) {
	i--;
	continue;
      }
    }

printf("len = %d\n", len);

    PDEBUG(("target addr=%lx, instr addr=%p, %lx=>", taddr, addr,
	text_read_ul(addr))); 
      
    offset = (unsigned long) addr&PAGE_MASK;
    page = (unsigned long) addr&~PAGE_MASK;
    
    /* it doesn't matter what we used here to unprotect page,
     * as this routine will not be in production code.
     */
      
    res[count].address = taddr;
    res[count].old = text_read_ul(addr);
    res[count].new = text_read_ul(addr);

    if (faultType==TEXT_FAULT) {

      flip_bit = random() & 0x1f;
      PDEBUG(("flip bit %d => ", flip_bit));
      flip_bit = 1 << flip_bit;

      res[count].new = text_read_ul(addr) ^ flip_bit;
      
      if (injectFault) {
	text_write_ul(addr, text_read_ul(addr)^flip_bit); 
      }

    } else if (faultType==NOP_FAULT || 
	       faultType==INIT_FAULT ||
	       faultType==BRANCH_FAULT || 
	       faultType==INTERFACE_FAULT ||
	       faultType==IRQ_FAULT) {
      c = (unsigned char *) addr;

      for (j = 0; j < len; j++) {
	/* replace these bytes with NOP (*c=NOP) */
	if (j < sizeof(unsigned long)) {
	  ((unsigned char *) &res[count].new)[j] = NOP;
	}
	if (injectFault) {
	  text_write_ub(c, NOP);
	}	

	c++;
      }
    } else if (faultType==DST_FAULT || faultType==SRC_FAULT) {
      /* skip thru the prefix and opcode, and flip bits in following bytes */
      int prefix;
      c=(unsigned char *) addr;
      do {
	switch (text_read_ub(c)) {
	case 0x66: case 0x67: case 0x26: case 0x36:
	case 0x2e: case 0x3e: case 0x64: case 0x65:
	case 0xf0: case 0xf2: case 0xf3:
	  prefix = 1;
	  break;
	default:
	  prefix = 0;
	  break;
	}
	if (prefix) {
	  c++;
	}
      } while (prefix);
      if(text_read_ub(c)>=0xd8 && text_read_ub(c)<=0xdf) {
	/* don't mess with fp instruction, yet.
	 * but there shouldn't be any fp instr in kernel.
	 */
	PDEBUG(("floating point instruction, bailing out\n"));
	i--;
	continue;
      } else if(text_read_ub(c)==0x0f) {
	c++;
      }
      if(text_read_ub(c)==0x0f) {
	c++;
      }
      c++;
      len = len-((long) c - (long) addr);
      if (len == 0)
      {
	printf("tex_fault: len = %d\n", len);
	count--;
	continue;
      }
if (len == 0)
{
	int i;

	printf(
	"text_fault: bad length at address 0x%x, c = 0x%x, fault type %d\n",
		addr, c, faultType);
	printf("bytes:");
	for (i= 0; i<16; i++)
		printf(" 0x%02x", text_read_ub((char *)addr+i));
	printf("\n");
	abort();
	*(int *)-4 = 0;
}
      flip_bit = random() % (len*8);
      PDEBUG(("flip bit %d (len=%d) => ", flip_bit, len));
      for(j=0; j<len; j++) {
	/* go to the right byte */
	if(flip_bit<8) {
	  flip_bit = 1 << flip_bit;

	  if (j < sizeof(unsigned long)) {
	    ((unsigned char *) &res[count].new)[j] =
		(text_read_ub(c) ^ flip_bit);
	  }


	  if (injectFault) {
	    text_write_ub(c, (text_read_ub(c)^flip_bit));
	  }

	  j=len;
	}
	c++;
	flip_bit = flip_bit-8;
      }
    } else if(faultType==PTR_FAULT) {
      /* 5f) ptr: if instruction has regmodrm byte (i_has_modrm),
       *     flip 1 bit in lower byte (0x0f) or any bit in following
       *     bytes (sib, imm or disp).
       */
      int prefix;
      c=(unsigned char *) addr;
      do {
	switch (text_read_ub(c)) {
	case 0x66: case 0x67: case 0x26: case 0x36:
	case 0x2e: case 0x3e: case 0x64: case 0x65:
	case 0xf0: case 0xf2: case 0xf3:
	  prefix = 1;
	  break;
	default:
	  prefix = 0;
	  break;
	}
	if (prefix) {
	  c++;
	}
      } while (prefix);
      if(text_read_ub(c)>=0xd8 && text_read_ub(c)<=0xdf) {
	/* don't mess with fp instruction, yet */
	PDEBUG(("floating point instruction, bailing out\n"));
	i--;
	continue;
      } else if(text_read_ub(c)==0x0f) {
	c++;
      }
      if(text_read_ub(c)==0x0f) {
	c++;
      }
      c++;
      len = len-((long) c - (long) addr);
      flip_bit = random() % (len*8-4);
      PDEBUG(("flip bit %d (len=%d) => ", flip_bit, len));

      /* mod/rm byte is special */

      if (flip_bit < 4) {
	flip_bit = 1 << flip_bit;

	rc = c - (unsigned char *) addr;
	if (rc < sizeof(unsigned long)) {
	  ((unsigned char *) &res[count].new)[rc] = text_read_ub(c) ^ flip_bit;
	  
	}
	if (injectFault) {
	  text_write_ub(c, text_read_ub(c)^flip_bit);
	}

      }
      c++; 
      flip_bit=flip_bit-4;

      for(j=1; j<len; j++) {
	/* go to the right byte */
	if (flip_bit<8) {
	  flip_bit = 1 << flip_bit;

	  rc = (c - (unsigned char *) addr);
	  if (rc < sizeof(unsigned long)) {
	    ((unsigned char *) &res[count].new)[rc] =
		text_read_ub(c) ^ flip_bit;
	    
	  }
	  if (injectFault) {
	    text_write_ub(c, text_read_ub(c)^flip_bit);
	  }

	  j=len;
	}
	c++;
	flip_bit = flip_bit-8;
      }
    } else if(faultType==LOOP_FAULT) {
      c=(unsigned char *) addr;
      /* replace rep with repe, and vice versa */
	if(text_read_ub(c)==0xf3) {
	  if (j < sizeof(unsigned long)) {
	    ((unsigned char *) &res[count].new)[j] = NOP;
	  }

	  rc = (c - (unsigned char *) addr);
	  if (rc < sizeof(unsigned long)) {
	    ((unsigned char *) &res[count].new)[rc] = 0xf2;
	    
	  }
	  if (injectFault) {
	    text_write_ub(c, 0xf2);
	  }
	} else if(text_read_ub(c)==0xf2) {
	  rc = (c - (unsigned char *) addr);
	  if (rc < sizeof(unsigned long)) {
	    ((unsigned char *) &res[count].new)[rc] = 0xf3;
	    
	  }
	  if (injectFault) {
	    text_write_ub(c, 0xf3);
	  }
	} else if( (text_read_ub(c)&0xf0)==0x70 ) {
	  /* if we've jxx imm8 instruction, 
	   * incl even byte instruction, eg jo (70) to jno (71)
	   * decl odd byte instruction,  eg jnle (7f) to jle (7e)
	   */ 
	  if(text_read_ub(c)%2 == 0) { 
	    rc = (c - (unsigned char *) addr);
	    if (rc < sizeof(unsigned long)) {
	      ((unsigned char *) &res[count].new)[rc] = text_read_ub(c) + 1;
	    
	    }

	    if (injectFault) {
	      text_write_ub(c, text_read_ub(c)+1);
	    }
	  }  else {

	    rc = (c - (unsigned char *) addr);
	    if (rc < sizeof(unsigned long)) {
	      ((unsigned char *) &res[count].new)[rc] = text_read_ub(c) - 1;
	      
	    }

	    if (injectFault) {
	      text_write_ub(c, text_read_ub(c)-1);
	    }
	  }
	} else if(text_read_ub(c)==0x66 || text_read_ub(c)==0x67)	{ 
		/* override prefix */
	  c++;
	} else if(text_read_ub(c++)==0xf && (text_read_ub(c)&0xf0)==0x80 ) {
	  /* if we've jxx imm16/32 instruction, 
	   * incl even byte instruction, eg jo (80) to jno (81)
	   * decl odd byte instruction,  eg jnle (8f) to jle (8e)
	   */ 
	  if(text_read_ub(c)%2 == 0) {
	    rc = (c - (unsigned char *) addr);
	    if (rc < sizeof(unsigned long)) {
	      ((unsigned char *) &res[count].new)[rc] = text_read_ub(c) + 1;
	      
	    }
	    if (injectFault) {
	      text_write_ub(c, text_read_ub(c)+1);
	    }
	  } else {
	    rc = (c - (unsigned char *) addr);
	    if (rc < sizeof(unsigned long)) {
	      ((unsigned char *) &res[count].new)[rc] = text_read_ub(c) -1;
	      
	    }

	    if (injectFault) {
	      text_write_ub(c, text_read_ub(c)-1);
	    }
	  }
	}
      
    }
    PDEBUG(("%lx\n", text_read_ul(addr)));
  }
  return(0);
}


#else /* CONFIG_SWIFI */

long
sys_inject_fault(char * module_name,
		 unsigned long argFaultType,
		 unsigned long argRandomSeed,
		 unsigned long argNumFaults,
		 pswifi_result_t result_record,
		 unsigned long do_inject)
{
  return(0);
}

#endif /* CONFIG_SWIFI */
