/*-=-=-=-=-=-=-=-=-=-=-=- RCCL/RCI Notice of Copyright -=-=-=-=-=-=-=-=-=-=-*
 *									    *
 *	Copyright (C) 1987, 1991 by Mike Parker.			    *
 *									    *
 *	Information on copyright permissions, as well as a complete	    *
 *	disclaimer of warranty, is given in the file COPYRIGHT.		    *
 *									    *
 *-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/

#include <vax/psl.h>
#include <vax/pte.h>
#include <vax/mtpr.h>

#include "../sys/rciext/kacrash.h"
#include "../sys/rciext/kamsgs.h"

#include "regs.h"

/* This routine is EXTREMELY paranoid.  It reads a longword from the	 */
/*  given addr and stores it through the given pointer.  If the address	 */
/*  is not a valid virtual address, or the longword does not reside in	 */
/*  physical memory, return value is nonzero (and *place may be		 */
/*  modified).  If no error occurs, return value is zero and *place	 */
/*  will contain the value read.					 */

static int readlong(addr,place)
int addr;
int *place;
{
 int pws;
 int bwp;
 int pa;
 int br;
 int lr;
 int pte;
 int ptea;
 int n;
 char *vp;
 extern int cpu;
 extern int memsize;

 bwp = addr &~ 0xc0000000;
 pws = bwp >> 9;
 bwp &= 511;
 switch (addr & 0xc0000000)
  { case 0x00000000:
       br = mfpr(P0BR);
       lr = mfpr(P0LR);
       if (bwp >= 509)
	{ if (pws+1 >= lr) return(1);
	  ptea = (br + (pws*4)) | 0x80000000;
	  if (ptea > (memsize+0x80000000-4)) return(1);
	  pte = * (int *) ptea;
	  if (! goodpte(pte)) return(1);
	  n = 512 - bwp;
	  vp = (char *) place;
	  bcopy((char *)(0x80000000|((pte&PG_PFNUM)<<9)|bwp),vp,n);
	  vp += n;
	  n = 4 - n;
	  ptea = (br + (pws*4) + 4) | 0x80000000;
	  if (ptea > (memsize+0x80000000-4)) return(1);
	  pte = * (int *) ptea;
	  if (! goodpte(pte)) return(1);
	  bcopy((char *)(0x80000000|((pte&PG_PFNUM)<<9)),vp,n);
	}
       else
	{ if (pws >= lr) return(1);
	  ptea = (br + (pws*4)) | 0x80000000;
	  if (ptea > (memsize+0x80000000-4)) return(1);
	  pte = * (int *) ptea;
	  if (! goodpte(pte)) return(1);
	  *place = * (int *) (0x80000000|((pte&PG_PFNUM)<<9)|bwp);
	}
       break;
    case 0x40000000:
       br = mfpr(P1BR);
       lr = mfpr(P1LR);
       if (pws < lr) return(1);
       if (bwp >= 509)
	{ if (addr > 0x7ffffffc ) return(1);
	  ptea = (br + (pws*4)) | 0x80000000;
	  if (ptea > (memsize+0x80000000-4)) return(1);
	  pte = * (int *) ptea;
	  if (! goodpte(pte)) return(1);
	  n = 512 - bwp;
	  vp = (char *) place;
	  bcopy((char *)(0x80000000|((pte&PG_PFNUM)<<9)|bwp),vp,n);
	  vp += n;
	  n = 4 - n;
	  ptea = (br + (pws*4) + 4) | 0x80000000;
	  if (ptea > (memsize+0x80000000-4)) return(1);
	  pte = * (int *) ptea;
	  if (! goodpte(pte)) return(1);
	  bcopy((char *)(0x80000000|((pte&PG_PFNUM)<<9)),vp,n);
	}
       else
	{ ptea = (br + (pws*4)) | 0x80000000;
	  if (ptea > (memsize+0x80000000-4)) return(1);
	  pte = * (int *) ptea;
	  if (! goodpte(pte)) return(1);
	  *place = * (int *) (0x80000000|((pte&PG_PFNUM)<<9)|bwp);
	}
       break;
    case 0x80000000:
       br = mfpr(SBR);
       lr = mfpr(SLR);
       if (bwp >= 509)
	{ if (pws+1 >= lr) return(1);
	  ptea = (br + (pws*4)) | 0x80000000;
	  if (ptea > (memsize+0x80000000-4)) return(1);
	  pte = * (int *) ptea;
	  if (! goodpte(pte)) return(1);
	  n = 512 - bwp;
	  vp = (char *) place;
	  bcopy((char *)(0x80000000|((pte&PG_PFNUM)<<9)|bwp),vp,n);
	  vp += n;
	  n = 4 - n;
	  ptea = (br + (pws*4) + 4) | 0x80000000;
	  if (ptea > (memsize+0x80000000-4)) return(1);
	  pte = * (int *) ptea;
	  if (! goodpte(pte)) return(1);
	  bcopy((char *)(0x80000000|((pte&PG_PFNUM)<<9)),vp,n);
	}
       else
	{ if (pws >= lr) return(1);
	  ptea = (br + (pws*4)) | 0x80000000;
	  if (ptea > (memsize+0x80000000-4)) return(1);
	  pte = * (int *) ptea;
	  if (! goodpte(pte)) return(1);
	  *place = * (int *) (0x80000000|((pte&PG_PFNUM)<<9)|bwp);
	}
       break;
    default:
       return(1);
       break;
  }
 return(0);
}

/* Helper for the above */
static int goodpte(pte)
register int pte;
{
 extern int memsize;

 if (! (pte & PG_V)) return(0);
 switch (pte & PG_PROT)
  { default:
       return(0);
       break;
    case PG_KW:
    case PG_KR:
    case PG_URKW:
    case PG_URKR:
    case PG_UW:
       break;
  }
 if ((pte&PG_PFNUM) >= (memsize>>9)) return(0);
 return(1);
}

crashstack(regs,ci)
int regs[];
CRASHINFO *ci;
{
 int fp;
 int ap;
 int na;
 int i;
 int newfp;
 int newap;
 int newpc;
 int argi;
 CRASHFRAME *cf;

 newfp = regs[FP];
 newap = regs[AP];
 newpc = ci->pc;
 i = 0;
 argi = 0;
 while (i < KAC_MAXCALLS)
  { cf = &ci->callstack[i];
    cf->pc = newpc;
    fp = newfp;
    ap = newap;
    cf->flags = 0;
    if (readlong(ap,&na))
     { cf->nargs = ~0;
     }
    else
     { na &= 0xff;
       if (na > KAC_MAXARGS-argi)
	{ cf->flags |= CI_CF_ARGLOST;
	  na = KAC_MAXARGS - argi;
	}
       cf->nargs = na;
       cf->argindex = argi;
       for (;na>0;na--)
	{ if (readlong(ap+=4,&ci->callargs[argi]))
	   { ci->callargs[argi] = -1;
	   }
	  argi ++;
	}
     }
    cf->flags |= CI_CF_VALID;
    i ++;
    if ( readlong(fp+ 8,&newap) ||
	 readlong(fp+12,&newfp) ||
	 readlong(fp+16,&newpc) )
     { break;
     }
  }
 for (;i<KAC_MAXCALLS;i++)
  { ci->callstack[i].flags = 0;
  }
}

usercrash(regs)
int regs[];
{
 int s;
 extern CRASHINFO crashinfo;
 char *cp;
 int i;
 extern char _EMbegin;
 extern char _EMend;

 s = splhigh();
 if ( ((crashinfo.psl & PSL_CURMOD) != PSL_CURMOD) ||
      ( (crashinfo.pc & 0x80000000) &&
	( (crashinfo.pc < (int)&_EMbegin) ||
	  (crashinfo.pc > (int)&_EMend) ) ) )
  { crashpanic();
  }
 crashstack(regs,&crashinfo);
 for (i=0;i<14;i++) crashinfo.registers[i] = regs[i];
 crashinfo.registers[14] = mfpr(USP); /* know is a user-mode psl; we'd	 */
				      /*  call crashpanic() otherwise	 */
 crashinfo.registers[15] = crashinfo.pc;
 beginmsg(sizeof(CRASHINFO)+5);
 putmsgbyte(MSG_CRASH);
 putmsgint(sizeof(CRASHINFO)+5);
 cp = (char *) &crashinfo;
 for (i=0;i<sizeof(CRASHINFO);i++)
  { putmsgbyte(cp[i]);
  }
 endmsg();
 ringdoorbell(0);
 init_ast();
 init_timers();
 force_exit();
}

crashpanic()
{
 printf("Crash, pc=%x, psl=",crashinfo.pc);
 printpsl(crashinfo.psl);
 printf("\n");
 switch (crashinfo.type)
  { default:
       printf("type = %d?\n",crashinfo.type);
       break;
    case CI_NULL:
       printf("Null crashinfo\n");
       break;
    case CI_EXIT:
       printf("exit() called\n");
       printf("code = %d\n",crashinfo.u.subtype);
       break;
    case CI_ARITHTRAP:
       printf("arithmetic trap\n");
       switch (crashinfo.u.subtype)
	{ default:
	     printf("subtype = %d?\n",crashinfo.u.subtype);
	     break;
	  case CI_AT_IOVERF:
	     printf("subtype = integer overflow trap\n");
	     break;
	  case CI_AT_IDIVZ:
	     printf("subtype = integer divide by zero trap\n");
	     break;
	  case CI_AT_FOVERF:
	     printf("subtype = floating overflow trap\n");
	     break;
	  case CI_AT_FDIVZ:
	     printf("subtype = floating divide by zero trap\n");
	     break;
	  case CI_AT_FUNDERF:
	     printf("subtype = floating underflow trap\n");
	     break;
	  case CI_AT_DOVERF:
	     printf("subtype = decimal overflow trap\n");
	     break;
	  case CI_AT_SUBRANGE:
	     printf("subtype = subscript range trap\n");
	     break;
	  case CI_AF_FOVERF:
	     printf("subtype = floating overflow fault\n");
	     break;
	  case CI_AF_FDIVZ:
	     printf("subtype = floating divide by zero fault\n");
	     break;
	  case CI_AF_FUNDERF:
	     printf("subtype = floating underflow fault\n");
	     break;
	}
       break;
    case CI_ILLINST:
       printf("illegal instruction\n");
       switch (crashinfo.u.subtype)
	{ default:
	     printf("subtype = %d?\n",crashinfo.u.subtype);
	     break;
	  case CI_II_RADRMOD:
	     printf("subtype = reserved addressing mode\n");
	     break;
	  case CI_II_PRIVINST:
	     printf("subtype = privileged instruction\n");
	     break;
	  case CI_II_RESOPRND:
	     printf("subtype = reserved operand\n");
	     break;
	  case CI_II_BPT:
	     printf("subtype = breakpoint\n");
	     break;
	}
       break;
    case CI_TRACE:
       printf("trace trap\n");
       break;
    case CI_MEMFAULT:
       printf("memory fault\n");
       printf("%s access to 0x%08x, %s fault, %s violation, to %s\n",
((crashinfo.u.memfault.bits & CI_MF_ACCESS) == CI_MF_READ) ? "read" : "write",
crashinfo.u.memfault.vaddr,
((crashinfo.u.memfault.bits & CI_MF_WHAT) == CI_MF_PTE) ? "PTE" : "page",
((crashinfo.u.memfault.bits & CI_MF_TYPE) == CI_MF_LENGTH) ? "length" : "protection",
((crashinfo.u.memfault.bits & CI_MF_WHERE) == CI_MF_QBUS) ? "Q-bus" : "memory");
       break;
  }
 panic("crash");
}
