/********************************************************************** * Copyright (c) Digital Equipment Corporation 1984, 1985, 1986. * * All Rights Reserved. * * Reference "/usr/src/COPYRIGHT" for applicable restrictions. * **********************************************************************/ /* * SCCSID: @(#)errlog.c 3.0 5/5/86 */ /* * ULTRIX-11 kernel error log functions * Fred Canter 5/11/83 */ #include <sys/param.h> #include <sys/systm.h> #include <sys/tmscp.h> /* must preceed errlog.h (EL_MAXSZ) */ #include <sys/errlog.h> #include <sys/buf.h> #include <sys/reg.h> #include <sys/seg.h> #include <sys/dir.h> #include <sys/proc.h> #include <sys/user.h> #include <sys/uba.h> /* * Block I/O device error log buffer structure. */ struct elbuf { struct elrhdr bd_hdr; struct el_bdh e_bdh; int e_dreg[]; }; /* * (fmtbde) - Format a block I/O device error log record. * * bp = I/O buffer pointer * ebuf = pointer to driver error log buffer * csr = device CSR address * nreg = number of device registers * dbr = `data buffer' register offset * * The contents of the `data buffer' register are not * logged, because reading this type of register with * no data in the SILO causes a DATA LATE error. */ fmtbde(bp, ebuf, csr, nreg, dbr) register struct buf *bp; physadr csr; { register struct elbuf *ebp; /* register caddr_t dp; OHMS: caused System V C compiler error */ register physadr dp; int i; ebp = ebuf; dp = csr; ebp->bd_hdr.e_time = time; /* time stamp */ ebp->e_bdh.bd_dev = bp->b_dev; /* data from buffer header */ ebp->e_bdh.bd_flags = bp->b_flags; ebp->e_bdh.bd_bcount = bp->b_bcount; if(bp->b_flags&B_MAP) { struct { short ubm_lo; short ubm_hi; } *ubmp = UBMAP; i = (((unsigned)bp->b_un.b_addr >> 13) & 7) | (((unsigned)bp->b_xmem << 3) & 030); ebp->e_bdh.bd_addr = ubmp[i].ubm_lo; /* real physical addr */ ebp->e_bdh.bd_xmem = ubmp[i].ubm_hi; /* from UB map reg */ } else { ebp->e_bdh.bd_addr = bp->b_un.b_addr; ebp->e_bdh.bd_xmem = bp->b_xmem & 077; } ebp->e_bdh.bd_blkno = bp->b_blkno; ebp->e_bdh.bd_errcnt = 0; /* driver loads retry counter later */ ebp->e_bdh.bd_act = el_bdact; /* block device activity */ ebp->e_bdh.bd_csr = csr; /* device CSR address */ ebp->e_bdh.bd_nreg = nreg; /* # of device registers */ ebp->e_bdh.bd_mcact = ra_mcact; /* MSCP cntlr activity */ for(i=0; i<nreg; i++) { /* log device registers */ if(dbr && (i == dbr)) /* except for data buffer */ ebp->e_dreg[i] = 0; else ebp->e_dreg[i] = dp->r[i]; } } /* * (logerr) - Add a header & copy record to error log buffer. * * et = type of error log record * ebuf = pointer to driver error log buffer * sz = size of driver's error log buffer in bytes * */ int el_bact; /* 0 = buffer empty, else # of records in buffer */ char *el_bpi = &el_buf; char *el_bpo = &el_buf; logerr(et, ebuf, sz) struct elbuf *ebuf; { register struct elbuf *debp, *kebp; register int *b; extern int el_buf[]; int hs, pri; int *p; mapinfo map; int nelip; if(!el_on && (et != E_SU)) return(0); /* error logging disabled */ if(el_on && (et == E_SU)) return(0); /* no startup if error logging already on */ kebp = el_bpi; /* kernel error log buffer pointer */ debp = ebuf; /* deivce error log buffer pointer */ hs = sizeof(struct elrhdr); /* size of record header */ if(et == E_BD) { /* block device error, */ hs = 2; /* time stamp already in buffer */ sz -= 2; /* if error on error log device, */ /* don't try to log it, instead */ /* print a message and disable logging */ /* the if statement was using wrong offsets: if((debp->e_bdh.b_dev == el_dev) && (debp->e_bdh.b_blkno >= el_sb) && (debp->e_bdh.b_blkno < (el_sb + el_nb))) { ***/ if((debp->e_bdh.bd_dev == el_dev) && (debp->e_bdh.bd_blkno >= el_sb) && (debp->e_bdh.bd_blkno < (el_sb + el_nb))) { printf("\nlogerr: ERROR LOG DEVICE\n"); el_on = 0; /* disable error logging */ return(0); } } /* * Copy the error log record to the kernel buffer. * If the buffer is full, return zero to cause the * `missed error' message to be printed on the console. * The kernel error log buffer is a circular buffer * and is managed by the el_bpi & el_bpo pointers. * If the input and output pointers are equal, * then the buffer is empty. */ pri = spl7(); /* don't want to be interrupted */ nelip = el_bpi + hs + sz; savemap(map); if(el_bact && (el_bpi == el_bpo)) goto bad; if((el_bpi < el_bpo) && (nelip > el_bpo)) goto bad; if(nelip > &el_buf[elbsiz]) { kebp->bd_hdr.e_type = 0; /* mark end of buffer */ kebp = &el_buf; /* set pointer to start of buffer */ el_bpi = &el_buf; if((el_bpi + hs + sz) > el_bpo) { bad: restormap(map); splx(pri); printf("\nlogerr: MISSED ERROR\n"); return(0); } } el_bact++; /* fill in record header */ kebp->bd_hdr.e_type = et; /* error record type */ kebp->bd_hdr.e_size = (hs + sz); /* total record size in bytes */ if(et != E_BD) /* time stamp, except for block device errors */ kebp->bd_hdr.e_time = time; p = el_bpi+sizeof(struct elrhdr); /* pointer to record body */ b = debp; /* pointer to driver buffer */ if(et == E_BD) { /* block device buffer has dummy header */ b++; /* skip over it (2 bytes) */ p -= 2; /* BD buf has time stamp in it */ } sz /= 2; while(sz--) *p++ = *b++; /* copy record body to kernel buffer */ el_bpi = p; /* now points to start of next record */ if(et == E_SU) el_on = 1; /* if startup record, enable logging */ if(et == E_SD) el_on = 0; /* if shutdown, disable logging */ restormap(map); splx(pri); wakeup((caddr_t)&el_buf); /* wakeup error copy process */ return(1); } /* * errlog() - error logging control and status system call. * * * func = function code (see errlog.h) * bufp = user buffer pointer, for argument return */ char ra_ctid[]; /* RA controller type ID, see ra.c */ errlog() { register struct a { int func; /* function code */ int bufp; /* user buffer pointer */ } *uap; register char *ubp; register int *kbp; int ebuf[5]; int pri, i; uap = (struct a *)u.u_ap; if(!suser()) return; /* only super-user can use errlog() */ switch(uap->func) { case EL_OFFNL: /* disable error logging, don't log shutdown */ el_on = 0; break; case EL_OFF: /* disable error logging & log shutdown */ logerr(E_SD, &ebuf, 0); break; case EL_ON: /* enable error logging & and log a startup */ ebuf[0] = cputype; ebuf[1] = rn_ssr3; /* release # / M/M SSR 3 */ ebuf[2] = el_bdcw; /* block device config word */ ebuf[3] = el_cdcw; /* char device config word */ /* * Save MSCP cntlr types, * see conf/uda.c for ra_ctid[] format. */ for(i=3; i>=0; i--) { ebuf[4] <<= 4; ebuf[4] |= ((ra_ctid[i] >> 4) & 017); } logerr(E_SU, &ebuf, sizeof(ebuf)); /* log a startup */ break; case EL_INIT: /* disable logging & init kernel buffer */ el_on = 0; el_bpi = &el_buf; el_bpo = &el_buf; el_bact = 0; /* say error log buffer empty */ el_init = 1; /* error log buffer reinit flag */ break; case EL_WAIT: /* sleep elc process at hi priority */ el_c_pid = u.u_procp->p_pid; /* save elc process ID */ /* so clock.c will not nice it */ sleep((caddr_t)&el_buf, PZERO - 20); break; case EL_REC: /* move an error log record to caller's buffer */ ubp = uap->bufp; /* user's buffer pointer */ if(el_init) { /* if error log has been init'ed */ el_init = 0; /* tell elc process about it ! */ suword((caddr_t)ubp, E_INIT); break; } if(el_bpi == el_bpo) suword((caddr_t)ubp, E_EOF); /* no more records */ else { (caddr_t)kbp = el_bpo; if((*kbp & 0377) == E_EOF) /* end of kernel buffer */ kbp = &el_buf; i = *kbp & 0377; /* check for bad record */ if(i < 0 || i > E_BD) { /* bad error type */ badrec: suword((caddr_t)ubp, E_BADR); break; } i = (*kbp >> 8) & 0377; /* bad size */ if((i <= 0) || (i & 1) || (i >EL_MAXSZ)) goto badrec; pri = spl7(); for(i=(((*kbp >> 8)&0377)/2); i; i--) { suword((caddr_t)ubp, *kbp++); ubp += 2; } el_bpo = kbp; if(el_bpi == el_bpo) el_bact = 0; splx(pri); } break; case EL_INFO: /* pass system error log information to elc & elp */ /* frees elc & elp form dependence on nlist */ ubp = uap->bufp; /* pointer to user's buffer */ suword((caddr_t)ubp, el_dev); ubp += 2; suword((caddr_t)ubp, (int)(el_sb >> 16)); /* LONG */ ubp += 2; suword((caddr_t)ubp, (int)el_sb); ubp += 2; suword((caddr_t)ubp, el_nb); ubp += 2; suword((caddr_t)ubp, nblkdev); break; default: /* invalid function code or not super-user */ psignal(u.u_procp, SIGSYS); break; } } /* * Log a stray device interrupt. * * A stray interrupt is defined as one that occurs for * a configured device through a valid vector address, * but is unexpected. In the case of big disks, a stray * interrupt is logged when the interrupt service routine * is entered and the device is not active and no attention * summary bits are set. */ logsi(csr) { int si_ebuf[3]; si_ebuf[0] = csr; /* device's CSR address */ si_ebuf[1] = el_bdact; /* other block device activity */ si_ebuf[2] = ra_mcact; /* MSCP controller activity */ logerr(E_SI, &si_ebuf, sizeof(si_ebuf)); printf("\nSI %o\n", si_ebuf[0]); } /* * Interface from unused vectors to the stray * vector logging routine. */ sv0(dev) { logsv(dev, 0); } sv100(dev) { logsv(dev, 0100); } sv200(dev) { logsv(dev, 0200); } sv300(dev) { logsv(dev, 0300); } sv400(dev) { logsv(dev, 0400); } sv500(dev) { logsv(dev, 0500); } sv600(dev) { logsv(dev, 0600); } sv700(dev) { logsv(dev, 0700); } /* * Log a stray vector, i.e., a vector thru * an unused vector location. * The most common of these is thru location zero. * * base - base address of a group of 16 vector locations * dev - offset into that group */ logsv(dev, base) { int sv_ebuf[3]; sv_ebuf[0] = base + ((dev &017) << 2); /* vector address */ sv_ebuf[1] = el_bdact; /* other block device activity */ sv_ebuf[2] = ra_mcact; /* MSCP controller activity */ logerr(E_SV, &sv_ebuf, sizeof(sv_ebuf)); printf("\nSV %o\n", sv_ebuf[0]); }