/**     errhand.c
 **
 ** Author: Ted Schmitz
 ** Date of original creation December 8, 2008
 **
 **     This code contains the error handler for the common diagnostic menu.
 **
 **/

#include "errhand.h"            /* error handler & test definitions */
#include "diag_mon.h"

#if !defined(__U_BOOT__)
#include <stdio.h>    /* standard library headers */
#include <string.h>
#else
#include "common.h"  /* common library headers for Uboot environment */
#endif

#include "../ll_bist.h"
#include "../ll_post.h"
#include "../ll_post_utils.h"
#include "ascii_functions.h"
#include "errcodes.h"

unsigned char error_mode;	/* action to be taken when error detected */

int error_flag;                 /* flag if test failed */

struct save_fail first_fail;    /* keep a record of first failure.  NOTE: If
                                   first_fail.test_no=0, no failure
                                   has occured yet. */
struct save_fail last_fail;     /* keep a record of the last failure. */

/* All internal prototypes are defined here */
void disp_failure(struct save_fail * sf);
void save_error_information(const struct errhand_info * sehi, 
                            struct save_fail * sf);
char * get_fru_name(enum frus fru);

/* All external prototypes are define here */


/****************************************************************************
 **                        Common Error Handler
 **
 **     This handler is called when a failure is detected by any test as well
 ** as other test handling procedures.
 **
 **	The error handler has only one parameter which is a pointer to a
 ** structure containing all the necessary information required for the
 ** type of call to the error handler.
 **
 **	The inputs below describe all the elements of the "errhand_info"
 ** structure.  The "call_type" element defines what information in the
 ** structure is required.  Below is a list of all the required elements
 ** for each "call_type".
 **
 ** INPUT - call_type = This element defines why the error handler was
 **         called and what other elements are used for each call as defined
 **         in the table above.  All the "call_type's" are defined in the
 **         "errhand.h" file.
 **       - error_stop = The upper 16 bits of the error code.
 **       - supl_error_stop = The lower 16 bits of the error code.
 **	  - severity = The severity of the detected malfunction.  Some 
 **         problems may not be severe enough to bring the hardware down
 **	    while others do.  All the severity codes are defined in the
 **         "errhand.h" file.
 **	  - data_exp = an array of up to "MAX_EXP_REC" 32-bit values 
 **	    representing the data expected at the failure address.
 **	  - data_rec = an array of up to "MAX_EXP_REC" 32-bit values
 **	    representing the bad data read back from the failure address.
 **	  - fail_adrs = four byte location of where the bad data is 
 **         located or comming from.
 **       - disp_size = Size of date expected and data received elements
 **         as described in "errhand.h".
 **       - void_ptr = Pointer to various information depending on 
 **         "call_type". For instance if "call_type" is set to 
 **         "EH_FAIL_USR_MSG", this variable will contain a pointer to a
 **         custom user defined error message, etc.
 **	  - fru_list = This is an array of integers representing which slots
 **	    or other FRU's are most likely causing the malfunction or are
 **	    bad.  A complete list of all the items that can be placed in this
 **	    array can be found in the "errhand.h" file.  This array must be
 **	    terminated with an 0xff.
 **
 ** OUTPUT - Various information from the "errhand_info" structure is saved
 **	     in the maintenance log.
 **
 **/
void error_handler(struct errhand_info * ehi)
{
  int i;
  char * byt_ptr;                       /* byte reference pointer */

#if 0 /* debug */
  printf("\nIn 'error_handler()'...\n");
  printf("call_type = %0X\n",ehi->call_type);
  printf("error_code = %04X %04X\n",ehi->error_stop, ehi->sup_error_stop);
  printf("severity = %0d\n",ehi->severity);
  printf("data_exp ->%08lX %08lX<- data_rec\n",ehi->data_exp[0],
         ehi->data_rec[0]);
  printf("fail_adrs ->%08lX %08lX<- disp_size\n",ehi->fail_adrs,
         ehi->disp_size);
  printf("void_ptr = %08lx\n",ehi->void_ptr);
  printf("fru_list: %02X %02X %02X %02X\n",ehi->fru_list[0],
         ehi->fru_list[1], ehi->fru_list[2], ehi->fru_list[3]);
#endif

  /* set global error flag if call is severe enough */
  if (ehi->severity == SLEVEL_FATAL)
    {
    error_flag = FAIL;
    printf("Failed!\n");
    }
  else if (ehi->severity == SLEVEL_WARN)
    {
    printf("Warning!\n");
    }
  else if (ehi->severity == SLEVEL_INFO)
    {
    printf("\n");			/* just start a new line */
    }

  /**
   ** Save all the error information even if this type of call does not
   ** use every field.
   **/
   
  /* Save a copy of fail data if this is the first failure */
#if 0
  printf("A - first_fail.eh.call_type = %0X\n",
         first_fail.eh.error_stop);
#endif
  if (first_fail.eh.error_stop == 0)
    {
    save_error_information(ehi, &first_fail);
    /* copy user message if available */
    if ((ehi->call_type & EI_USR_MSG_AVAIL) == EI_USR_MSG_AVAIL)
      {
      byt_ptr = (char *) ehi->void_ptr; /* set up pointer to user message */
      for (i=0;i<MAX_USR_MSG;i++)	/* save a copy of user message */
        first_fail.err_msg[i] = byt_ptr[i];
      }
    }

#if 0
  printf("B - first_fail.eh.call_type = %0X\n",
         first_fail.eh.error_stop);
#endif
  /* Save every error in last fail structure */
  save_error_information(ehi, &last_fail);
  save_error_info_in_nv_storage(&last_fail);/* save what we can in nv storage */

  /* copy user message if this error has one */
  if ((ehi->call_type & EI_USR_MSG_AVAIL) == EI_USR_MSG_AVAIL)
    {
    byt_ptr = (char *) ehi->void_ptr;	/* set up pointer to user message */
    for (i=0;i<MAX_USR_MSG;i++)	      /* save a copy of user message */
      last_fail.err_msg[i] = byt_ptr[i];
    }
    
  disp_failure(&last_fail);		/* display all failure information */
   
}
/****************************************************************************
 **                         Save Error Information Routine
 **
 **     This routine copies the data from the error handler information 
 ** structure to the save fail structure and also saves the current test
 ** number and loop count in the save fail structure as well.
 **
 ** INPUT - "sehi" pointer to source error handler information structure.
 **       - "sf" pointer to destination "save_fail" structure.
 **
 ** OUTPUT - destination "save_fail" structure will be filled in with the
 **	     current failure information.
 **/
void save_error_information(const struct errhand_info * sehi, 
                            struct save_fail * sf)
{
  int i;

  sf->eh.call_type = sehi->call_type;
  sf->eh.error_stop = sehi->error_stop;
  sf->eh.sup_error_stop = sehi->sup_error_stop;
  sf->eh.severity = sehi->severity;
  for (i = 0; i < 4; i++)
    {
    sf->eh.data_exp[i] = sehi->data_exp[i];
    sf->eh.data_rec[i] = sehi->data_rec[i];
    }
  sf->eh.fail_adrs = sehi->fail_adrs;
  sf->eh.disp_size = sehi->disp_size;
  sf->eh.void_ptr = sehi->void_ptr;
  for (i = 0; i < MAX_FRUS; i++)
    sf->eh.fru_list[i] = sehi->fru_list[i];

  sf->test_no = cur_test;
  sf->exec_count = cur_loop;

}

/****************************************************************************
 **             Display Failure Information
 **
 **     This routine displays the standard error information which the error
 ** handler builds and stores in a format defined by structure "save_fail"
 ** located in "errhand.h".
 **
 ** INPUT - "sf" must be pointing to the failure structure that is
 **         to be displayed.
 **
 ** OUTPUT - All pertinent error information is displayed on the screen.
 **
 ** RETURN - Nothing.
 **/
void disp_failure(struct save_fail * sf)
{
  int i;
  char * byt_ptr;                       /* byte reference pointer */
  char * byt_ptr_2;                     /* byte reference pointer # 2 */
  short * short_ptr;                    /* 16-bit refrence pointer */
  short * short_ptr_2;                  /* 16-bit refrence pointer # 2 */
  unsigned long * lptr;                 /* 32-bit pointer */
  unsigned long * lptr_2;               /* 32-bit pointer # 2*/

  
  printf("Test Number = %3d\n",sf->test_no);
  printf("Execution Count = %d\n",sf->exec_count);
  /* display complete error code if available */
  if ((sf->eh.call_type & EI_ERROR_CODE_AVAIL) == EI_ERROR_CODE_AVAIL)
    {
    printf("Error code = %04x-%04x\n",sf->eh.error_stop,sf->eh.sup_error_stop);
    }

  /* display Severity level */
  printf("Severity = ");
  if (sf->eh.severity == SLEVEL_WARN)
    printf("Warning\n");
  else if (sf->eh.severity == SLEVEL_FATAL)
    printf("Fatal\n");
  else if (sf->eh.severity == SLEVEL_INFO)
    printf("Information only\n");

  /* display any user defined message here if available */
  if ((sf->eh.call_type & EI_USR_MSG_AVAIL) == EI_USR_MSG_AVAIL)
    {
    printf("%s\n",sf->err_msg);
    }

  /* display FRU replacement list if available */
  if ((sf->eh.call_type & EI_FRU_LIST_AVAIL) == EI_FRU_LIST_AVAIL)
    {
    printf("FRU Replacement List = ");
    for (i = 0; i < MAX_FRUS; i++)
      {
      if (sf->eh.fru_list[i] >= 0xff)
        {
        printf("END.\n");
        break;				/* exit when reached end of list */
	}

      /* print next ASCII FRU name followed by a comma */
      printf("%s, ",get_fru_name(sf->eh.fru_list[i]));
      if (((i+1) % 3) == 0)
        printf("\n                       "); /* new line every 3 FRU's disp */
      }
    }

  /* display failure address if used by the current "call_type" */
  if ((sf->eh.call_type & EI_ERR_ADRS_AVAIL) == EI_ERR_ADRS_AVAIL)
    //printf("Failure address = %08X\n",sf->eh.fail_adrs);
    printf("Failure address = %016qx\n",sf->eh.fail_adrs);

  /* display proper amount of expected & received data if available */
  if ((sf->eh.call_type & EI_EXP_REC_DATA_AVAIL) == EI_EXP_REC_DATA_AVAIL)
    {
    switch (sf->eh.disp_size)
      {
      case DISP_SIZE_1B: /* display 1 byte data */
        byt_ptr = (char *) &sf->eh.data_exp[0];  /* get adrs of exp data */
        byt_ptr_2 = (char *) &sf->eh.data_rec[0]; /* get adrs of rec data */
        /* display one byte */
        printf("Expected -> %02X  %02X <- Received\n",(byt_ptr[7] & 0xFF),
               (byt_ptr_2[7] & 0xFF));
        break;
      case DISP_SIZE_2B: /* display 2 byte data */
        short_ptr = (short *) &sf->eh.data_exp[0];  /* adrs of exp data */
        short_ptr_2 = (short *) &sf->eh.data_rec[0];  /* adrs of rec data */
        printf("Expected -> %04X  %04X <- Received\n",
	       (short_ptr[3] & 0xFFFF),(short_ptr_2[3] & 0xFFFF));
        break;
      case DISP_SIZE_4B: /* display 4 byte data */
        lptr = (unsigned long *) &sf->eh.data_exp[0];  /* adrs of exp data */
        lptr_2 = (unsigned long *) &sf->eh.data_rec[0];  /* adrs of rec data */
        printf("Expected -> %08X  %08X <- Received\n",
               (lptr[1] & 0xFFFFFFFF), (lptr_2[1] & 0xFFFFFFFF));
        break;
      case DISP_SIZE_8B: /* display one 8-byte double long word of data */
        printf("Expected -> %016qx  %016qx <- Received",
               sf->eh.data_exp[0],
               sf->eh.data_rec[0]);
        break;
#if 0 /* 12 bytes not used on MIPS 64 processor */
      case DISP_SIZE_12B: /* display 12 bytes (three 4-byte words) of data */
        for (i=0;i<3;i++)
          {
          printf("Data expected[%01d] -> %08X  %08X <- Data received[%01d]",
	         i,
                 sf->eh.data_exp[i],
                 sf->eh.data_rec[i],
	         i);
	  if (sf->eh.data_exp[i] == sf->eh.data_rec[i])
	    printf("\n");
	  else
	    printf(" *\n"); /* mark data that does not compare */
	  }
        break;
#endif
      case DISP_SIZE_16B: /* display 16 bytes (two 8-byte words) of data */
        for (i=0;i<2;i++)
          {
          printf("Expected[%01d] -> %016qx  %016qx <- Received[%01d]",
	         i,
                 sf->eh.data_exp[i],
                 sf->eh.data_rec[i],
	         i);
	  if (sf->eh.data_exp[i] == sf->eh.data_rec[i])
	    printf("\n");
	  else
	    printf(" *\n"); 		/* mark data that does not compare */
	  }
        break;
      } /* end switch */

    } /* end 'if (...' */

}

/****************************************************************************
 **           Display Failure Information From Non-Volatile Storage
 **
 **     This routine displays any error information that was written to 
 ** non-volatile storage.  Since the space allocated for diagnotics in 
 ** non-volatile storage is not very big, it cannot store all the information
 ** tha a standard erro stop may generate.  For this reason, error
 ** information stored in non-volatile requires a separate display function
 ** to accomodate the differences.  If no error information is available
 ** in non-volatile storage, noting will be displayed by this function.
 **
 ** INPUT - Nothing
 **
 ** OUTPUT - All pertinent error information is displayed on the screen.
 **
 ** RETURN - Nothing
 **/
void disp_non_volatile_failure_info(void)
{
  unsigned char test_num;               /* test number that failed */
  unsigned long error_code;
  uint64_t error_adrs;                  /* adrs that failed */
  uint64_t exp_data;                    /* good data exp from fail adrs */
  uint64_t rec_data;                    /* bad data read from fail adrs */

 /* old way.  not every failure has an associated test number */
  if (read_select_data_from_seeprom_timed(&test_num, FF_TEST_NO_OS, 
      TOT_TEST_NUM_BYTES) == PROBLEM_DETECTED)
    {
    report_seeprom_problem(ES1210);     /* log the error & exit */
    return;
    }

  /* new way.  Use error code.  that is always non-zero */
  if (read_select_data_from_seeprom_timed((unsigned char *)&error_code, 
      FF_ERROR_CODE_OS, TOT_ERR_CODE_BYTES) == PROBLEM_DETECTED)
    {
    report_seeprom_problem(ES1210);     /* log the error & exit */
    return;
    }


  //if (test_num !=0) /* old way see comments above */
  if (error_code !=0) /* new way */
    {
    printf("\n<<< First Failure Information From Non-Volatile Storage >>>\n");
    printf("Test number that failed = %02d\n",test_num);

#if 0 /* new way makes this redundent */
    if (read_select_data_from_seeprom_timed((unsigned char *) &error_code, 
        FF_ERROR_CODE_OS, TOT_ERR_CODE_BYTES) == PROBLEM_DETECTED)
      {
      report_seeprom_problem(ES1210);     /* log the error & exit */
      return;
      }
#endif

    printf("Error code = %08lX\n",error_code);

    if (read_select_data_from_seeprom_timed((unsigned char *) &error_adrs, 
        FF_ERR_ADRS_OS, TOT_FAIL_ADRS_BYTES) == PROBLEM_DETECTED)
      {
      report_seeprom_problem(ES1210);     /* log the error & exit */
      return;
      }

    //printf("Address that failed = %08lX\n",error_adrs);
    printf("Address that failed = %016qx\n",error_adrs);

    if (read_select_data_from_seeprom_timed((unsigned char *) &exp_data, 
        FF_EXP_DATA_OS, TOT_EXP_DATA_BYTES) == PROBLEM_DETECTED)
      {
      report_seeprom_problem(ES1210);     /* log the error & exit */
      return;
      }

    printf("Data expected = %016qx\n",exp_data);

    if (read_select_data_from_seeprom_timed((unsigned char *) &rec_data, 
        FF_REC_DATA_OS, TOT_REC_DATA_BYTES) == PROBLEM_DETECTED)
      {
      report_seeprom_problem(ES1210);     /* log the error & exit */
      return;
      }

    printf("Data received = %016qx\n",rec_data);

    printf("\n<<< Last Failure Information From Non-Volatile Storage >>>\n");
    if (read_select_data_from_seeprom_timed((unsigned char *) &error_code, 
        LF_ERROR_CODE_OS, TOT_ERR_CODE_BYTES) == PROBLEM_DETECTED)
      {
      report_seeprom_problem(ES1210);     /* log the error & exit */
      return;
      }

    printf("Error code = %08lX\n",error_code);

    printf("...End of non-volatile error information...\n");
    }

}


/****************************************************************************
 **                    Look Up FRU ASCII Name
 **
 **     This routine returns a pointer to the proper ASCII name for the FRU 
 ** value supplied by the calling code.  All the FRU values are defined in
 ** an enumerated list in the file "errhand.h".
 **
 ** INPUT - "fru" is one of the values from the enumerated list 'frus'
 **	    defined in the "errhand.h" file.
 **
 ** OUTPUT - None.
 **
 ** RETURN - Returns a pointer to the ASCII FRU name.
 **
 **/
char * get_fru_name(enum frus fru)
{
  static char fru_name[MAX_FRU_ASCII_CHARS]; /* storage for ASCII name of FRU */

  /* All slot names can be created as shown below */
  if ((fru >= slot1) && (fru <= slot8))
    {
    sprintf(fru_name, "Slot %02d",(fru+1));
    }
  else
    { /* all other FRU ASCII names must be handled separately */
    switch (fru)
      {
#if 1 /* get rid of error using -O0 option */
      case ps1:
        strcpy(fru_name, "Power Supply 1");
        break;
      case ps2:
        strcpy(fru_name, "Power Supply 2");
        break;
      case ps3:
        strcpy(fru_name, "Power Supply 3");
        break;
      case f1:
        strcpy(fru_name, "Fan 1");
        break;
      case f2:
        strcpy(fru_name, "Fan 2");
        break;
      case f3:
        strcpy(fru_name, "Fan 3");
        break;
      case dimm1:
        strcpy(fru_name, "DIMM 1");
        break;
      case dimm2:
        strcpy(fru_name, "DIMM 2");
        break;
      case unit:
        strcpy(fru_name, "Entire Unit");
        break;
      case mb:
        strcpy(fru_name, "Mother Board");
        break;
      case ha_bd:
        strcpy(fru_name, "High Availability Board");
        break;
#endif
      case slot1: /* to eliminate warning for enum array */
      case slot2:
      case slot3:
      case slot4:
      case slot5:
      case slot6:
      case slot7:
      case slot8:
        break;
      } /* end 'switch' */

    }/* end 'if ((fru >=...' */

  return fru_name;		/* return pointer to the FRU ASCII name */
}
