/*
 * Author   : Kavita Bala 
 * Email    : kaybee@lcs.mit.edu
 * Comment  : Statistics gathering code.
 */


#include <mach/vm_param.h>
#include <mips/kb_prefetch.h>
#include <mach/error.h>
#include <machine/machspl.h>
#include <strings.h>
#include <mips/mips_cpu.h>


extern int tlb_miss_bug;
extern int tlb_miss_double;	/* double misses */
extern int tlb_miss_double_punt;
extern int tlb_miss_kernel_pt;	/* misses on kernel page table pages */
extern int tlb_miss_user_pt;	/* misses on user page table pages */
extern int tlb_miss_kernel;	/* misses on kernel pages */
extern int tlb_miss_user;	/* misses on user pages */
extern int kb_nested_l2;
extern int kb_nested_l1k;
extern int kb_nested_user_pt;



        

prefetch_stats_t kb_stats;

int kb_prefetch_trace_on, kb_prefetch_index;
int kb_sys_trace_index, kb_server_pc_index;
unsigned int kb_overhead_added;
int prefetch_intercept_enabled;

kern_return_t kb_prefetch_call (command, argument, stats)
  int command, argument;
  prefetch_stats_t *stats;
{
  int s, kr, i, temp;


  s = splhigh();  

  kr = check_command_arguments(command, argument);

  if (kr == KERN_SUCCESS)
    switch (command) {
      
    case KB_PREFETCH_RESET:
      kr = kb_prefetch_reset (stats);
      break;

    case KB_PREFETCH_STATS:
      kr = kb_prefetch_stats (stats);
      break;
      
    case KB_PREFETCH_START:
      kr = kb_prefetch_start ();
      break;

    case KB_PREFETCH_STOP:
      kr = kb_prefetch_stop ();
      break;

    default:
      kr = KERN_INVALID_ARGUMENT;
      break;
    }
  
  splx(s);
  return(kr);
}



/*
 *------------------------------------------------------
 * Function:     check_command_arguments
 * Description:  Makes sure an argument makes sense before proceeding.  
 *                Returns an error code.
 *-------------------------------------------------------
 */

int check_command_arguments(command, argument)
  int command;
  int argument;
{
  if ((command < 0) || (command > MAX_COMMAND))
    return(KERN_INVALID_ARGUMENT);

  return(KERN_SUCCESS);
}



/*
 *-----------------------------------------------------------
 * Function:     kb_prefetch_reset
 * Description:  Reset all data structures
 *-----------------------------------------------------------
 */

int kb_prefetch_reset(stats)
  prefetch_stats_t *stats;
{
  int i, j, k, max;
  int kr;
  int                       old_intercept_state, old_trace_state;

  kr = KERN_SUCCESS;

  /* Make sure that simulator is disabled */
  old_intercept_state        = prefetch_intercept_enabled;
  old_trace_state            = kb_prefetch_trace_on;
  prefetch_intercept_enabled = FALSE;
  kb_prefetch_trace_on       = FALSE;

  kb_prefetch_index          = 0;
  kb_sys_trace_index         = 0;
  kb_server_pc_index         = 0;


  kb_already_there = 0;
  kb_prefetch_l2   = 0;
  kb_prefetch_l1k  = 0;
  kb_prefetch_kpt  = 0;


  kb_overhead_added = 0;



  *((int *) kb_prefetch_ptr + KB_TOTAL_TRAPS_PTR) = 0;
  *((int *) kb_prefetch_ptr + KB_TLB_PENALTIES_PTR) = 0;
  *((int *) kb_prefetch_ptr + KB_OTHER_PENALTIES_PTR) = 0;


  tlb_miss_double = 0;
  kb_nested_l2 = 0;
  kb_nested_l1k = 0;
  kb_nested_user_pt = 0;
  tlb_miss_kernel_pt = 0;
  tlb_miss_user_pt = 0;
  tlb_miss_user = 0;
  tlb_miss_kernel = 0;
  
  max =  (*(volatile unsigned long *)(IOC_CTR));

  copyout (&max,  &stats->tmr_max,
               sizeof(stats->tmr_max)); 

  prefetch_intercept_enabled = old_intercept_state;
  kb_prefetch_trace_on       = old_trace_state;

  return(kr);
}


/*
 *-----------------------------------------------------------
 * Function:     kb_prefetch_start
 * Description:  Start stats gathering
 *-----------------------------------------------------------
 */

int kb_prefetch_start()
{
  int kr;

  kr = KERN_SUCCESS;

  kb_prefetch_reset ();
  /* Make sure that simulator is enabled */
  prefetch_intercept_enabled = TRUE;
  kb_prefetch_trace_on       = TRUE;
  kb_prefetch_index          = 0;
  kb_sys_trace_index         = 0;
  kb_server_pc_index         = 0;

  return(kr);
}

/*
 *-----------------------------------------------------------
 * Function:     kb_prefetch_stop
 * Description:  Stop stats gathering
 *-----------------------------------------------------------
 */

int kb_prefetch_stop()
{
  int kr;

  kr = KERN_SUCCESS;

  prefetch_intercept_enabled = FALSE;
  kb_prefetch_trace_on       = FALSE;

  return(kr);

}


/*--------------------------------------------------------------------
Function:     kb_prefetch_stats
Description:  Extracts and copies out to user space a bunch of useful 
              statistics and internal state.
----------------------------------------------------------------------*/

int kb_prefetch_stats(stats)
  prefetch_stats_t *stats;
{
  int                       kr, old_intercept_state, old_trace_state, min, max;
  int                       i;
  extern prefetch_stats_t   kb_stats;
  unsigned int total, l2_time, other_time;

  kr = KERN_SUCCESS;

  /* Then disable simulator (if it wasn't already) */
  old_intercept_state = prefetch_intercept_enabled;
  old_trace_state     = kb_prefetch_trace_on;
  prefetch_intercept_enabled = FALSE;
  kb_prefetch_trace_on       = FALSE;


  kb_stats.already_there           = kb_already_there;
  kb_stats.prefetch_l2             = kb_prefetch_l2;
  kb_stats.prefetch_l1k            = kb_prefetch_l1k;
  kb_stats.prefetch_kpt            = kb_prefetch_kpt;

  kb_stats.basic_user_pt           = tlb_miss_user_pt;
  kb_stats.basic_l2                = tlb_miss_double;
  kb_stats.basic_kpt               = tlb_miss_kernel_pt;
  kb_stats.basic_l1k               = tlb_miss_kernel;
  kb_stats.other                   = tlb_miss_user;
  kb_stats.tmr_min                 = min;

  kb_stats.nested_l2               = kb_nested_l2;
  kb_stats.nested_l1k              = kb_nested_l1k;
  kb_stats.nested_user_pt          = kb_nested_user_pt;


  copyout (&kb_stats.already_there, &stats->already_there,
           sizeof(kb_stats.already_there));

  copyout (&kb_stats.prefetch_l2, &stats->prefetch_l2,
           sizeof(kb_stats.prefetch_l2));
  copyout (&kb_stats.prefetch_l1k, &stats->prefetch_l1k,
           sizeof(kb_stats.prefetch_l1k));
  copyout (&kb_stats.prefetch_kpt, &stats->prefetch_kpt,
           sizeof(kb_stats.prefetch_kpt));

  copyout (&kb_stats.basic_user_pt, &stats->basic_user_pt,
           sizeof(kb_stats.basic_user_pt));
  copyout (&kb_stats.other, &stats->other,
           sizeof(kb_stats.other));
  copyout (&kb_stats.basic_l2, &stats->basic_l2,
           sizeof(kb_stats.basic_l2));
  copyout (&kb_stats.basic_l1k, &stats->basic_l1k,
           sizeof(kb_stats.basic_l1k));
  copyout (&kb_stats.basic_kpt, &stats->basic_kpt,
           sizeof(kb_stats.basic_kpt));

  copyout (&total, &stats->tlb_traps, sizeof(stats->tlb_traps)); 
  
  kb_already_there = 0;
  kb_prefetch_l2   = 0;
  kb_prefetch_l1k  = 0;
  kb_prefetch_kpt  = 0;

  kb_overhead_added = 0;
  kb_nested_l2 = 0;
  kb_nested_l1k = 0;
  kb_nested_user_pt = 0;

  tlb_miss_double = 0;
  tlb_miss_kernel_pt = 0;
  tlb_miss_user_pt = 0;
  tlb_miss_user = 0;
  tlb_miss_kernel = 0;
  tlb_miss_double = 0;

  /* Put back into old state */
  prefetch_intercept_enabled = old_intercept_state;
  kb_prefetch_trace_on       = old_trace_state;

  return(kr);
}





