freebsd-src/usr.sbin/ncrcontrol/ncrcontrol.c
Stefan Eßer 619e03fa69 Reading NCR registers is not secure at all times, so prevent
an ordinary user from doing this. (Problem pointed out by
Guido van Rooij).
1995-03-16 15:10:11 +00:00

1631 lines
38 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**************************************************************************
**
** $Id: ncrcontrol.c,v 1.6 1995/03/14 21:12:26 se Exp $
**
** Utility for NCR 53C810 device driver.
**
** 386bsd / FreeBSD / NetBSD
**
**-------------------------------------------------------------------------
**
** Written for 386bsd and FreeBSD by
** wolf@dentaro.gun.de Wolfgang Stanglmeier
** se@mi.Uni-Koeln.de Stefan Esser
**
** Ported to NetBSD by
** mycroft@gnu.ai.mit.edu
**
**-------------------------------------------------------------------------
**
** Copyright (c) 1994 Wolfgang Stanglmeier. All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
**
***************************************************************************
*/
#include <sys/file.h>
#include <sys/types.h>
#ifdef __NetBSD__
#include <sys/device.h>
#endif
#include <nlist.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <paths.h>
#include <limits.h>
#include <kvm.h>
#include <pci/ncr.c>
/*
** used external functions
*/
#if defined(__NetBSD__) || (__FreeBSD__ >= 2)
kvm_t *kvm;
#define KVM_NLIST(n) (kvm_nlist(kvm, (n)) >= 0)
#define KVM_READ(o, p, l) (kvm_read(kvm, (o), (void*)(p), (l)) == (l))
#else
#define KVM_NLIST(n) (kvm_nlist((n)) >= 0)
#define KVM_READ(o, p, l) (kvm_read((void*)(o), (p), (l)) == (l))
#endif
extern void exit();
extern char* strerror (int num);
/*===========================================================
**
** Global variables.
**
**===========================================================
*/
char *prog;
u_long verbose;
u_long wizard;
struct nlist nl[] = {
#define N_NCR_VERSION 0
{ "_ncr_version" },
#ifdef __NetBSD__
#define N_NCRCD 1
{ "_ncrcd" },
#else
#define N_NCRP 1
{ "_ncrp" },
#define N_NNCR 2
{ "_nncr" },
#endif
{ 0 }
};
const char *vmunix = NULL;
char *kmemf = NULL;
int kvm_isopen;
u_long ncr_base;
u_long lcb_base;
u_long ccb_base;
u_long ncr_unit;
#ifdef __NetBSD__
struct cfdriver ncrcd;
#else
u_long ncr_units;
#endif
struct ncb ncr;
struct lcb lcb;
struct ccb ccb;
u_long target_mask;
u_long global_lun_mask;
u_long lun_mask;
u_long interval;
/*===========================================================
**
** Accessing kernel memory via kvm library.
**
**===========================================================
*/
read_ccb(u_long base)
{
ccb_base = base;
if (!KVM_READ (
base,
&ccb,
sizeof (struct ccb))) {
fprintf (stderr, "%s: bad kvm read at %x.\n", prog, base);
exit (1);
};
}
read_lcb(u_long base)
{
lcb_base = base;
if (!KVM_READ (
base,
&lcb,
sizeof (struct lcb))) {
fprintf (stderr, "%s: bad kvm read at %x.\n", prog, base);
exit (1);
};
}
read_ncr()
{
if (!KVM_READ (
ncr_base,
&ncr,
sizeof (ncr))) {
fprintf (stderr, "%s: bad kvm read at %x.\n", prog, ncr_base);
exit (1);
};
}
void open_kvm(int flags)
{
int i;
u_long kernel_version;
#if defined(__NetBSD__) || (__FreeBSD__ >= 2)
char errbuf[_POSIX2_LINE_MAX];
#endif
if (kvm_isopen) return;
#if (__FreeBSD__ >= 2)
vmunix = getbootfile();
#endif
if (vmunix == NULL) {
vmunix = _PATH_UNIX;
}
#if defined(__NetBSD__) || (__FreeBSD__ >= 2)
kvm = kvm_openfiles(vmunix, kmemf, NULL, flags, errbuf);
if (kvm == NULL) {
fprintf(stderr, "%s: kvm_openfiles: %s\n", prog, errbuf);
exit(1);
}
#else
if (kvm_openfiles(vmunix, kmemf, NULL) == -1) {
fprintf(stderr, "%s: kvm_openfiles: %s\n", prog, kvm_geterr());
exit(1);
}
#endif
if (!KVM_NLIST(nl)) {
fprintf(stderr, "%s: no symbols in \"%s\".\n",
prog, vmunix);
exit (2);
};
for (i=0; nl[i].n_name; i++)
if (nl[i].n_type == 0) {
fprintf(stderr, "%s: no symbol \"%s\" in \"%s\".\n",
prog, nl[i].n_name, vmunix);
exit(1);
}
if (!KVM_READ (
nl[N_NCR_VERSION].n_value,
&kernel_version,
sizeof (kernel_version))) {
fprintf (stderr, "%s: bad kvm read.\n", prog);
exit (1);
};
if (kernel_version != ncr_version){
fprintf (stderr, "%s: incompatible with kernel. Rebuild!\n",
prog);
exit (1);
};
#ifdef __NetBSD__
if (!KVM_READ (
nl[N_NCRCD].n_value,
&ncrcd,
sizeof (ncrcd))) {
fprintf (stderr, "%s: bad kvm read.\n", prog);
exit (1);
};
if (ncr_unit >= ncrcd.cd_ndevs){
fprintf (stderr, "%s: bad unit number (valid range: 0-%d).\n",
prog, ncrcd.cd_ndevs-1);
exit (1);
};
if (!KVM_READ (
ncrcd.cd_devs+4*ncr_unit,
&ncr_base,
sizeof (ncr_base))) {
fprintf (stderr, "%s: bad kvm read.\n", prog);
exit (1);
};
if (!ncr_base) {
fprintf (stderr,
"%s: control structure not allocated (not found in autoconfig?)\n", prog);
exit (1);
};
#else /* !__NetBSD__ */
if (!KVM_READ (
nl[N_NNCR].n_value,
&ncr_units,
sizeof (ncr_units))) {
fprintf (stderr, "%s: bad kvm read.\n", prog);
exit (1);
};
if (ncr_unit >= ncr_units){
fprintf (stderr, "%s: bad unit number (valid range: 0-%d).\n",
prog, ncr_units-1);
exit (1);
};
if (!KVM_READ (
nl[N_NCRP].n_value+4*ncr_unit,
&ncr_base,
sizeof (ncr_base))) {
fprintf (stderr, "%s: bad kvm read.\n", prog);
exit (1);
};
if (!ncr_base) {
fprintf (stderr,
"%s: control structure not allocated (not found in autoconfig?)\n", prog);
exit (1);
};
#endif /* !__NetBSD__ */
read_ncr();
if (!ncr.vaddr) {
fprintf (stderr,
"%s: 53c810 not mapped (not found in autoconfig?)\n", prog);
exit (1);
};
kvm_isopen = 1;
}
void set_target_mask(void)
{
int t;
if (target_mask) return;
for (t=0; t<MAX_TARGET; t++)
if (ncr.target[t].jump_tcb.l_cmd) target_mask |= (1<<t);
}
void set_lun_mask(struct tcb * tp)
{
int l;
lun_mask = global_lun_mask;
if (lun_mask) return;
for (l=0; l<MAX_LUN; l++)
if (tp->lp[l]) lun_mask |= (1<<l);
}
void printc (u_char*p, int l)
{
for (;l>0;l--) {
char c=*p++;
printf ("%c", c?c:'_');
}
}
/*================================================================
**
**
** system info
**
**
**================================================================
*/
do_info(void)
{
int t,l,i,d,f,fl;
struct tcb * tip;
open_kvm(O_RDONLY);
if (verbose>=3)
printf ("ncr unit=%d data@%x register@%x (pci@%x)\n\n",
ncr_unit, ncr_base, ncr.vaddr, ncr.paddr);
set_target_mask();
printf ("T:L Vendor Device Rev Speed Max Wide Tags\n");
for (t=0; t<MAX_TARGET;t++) {
if (!((target_mask>>t)&1)) continue;
tip = &ncr.target[t];
set_lun_mask(tip);
if (!lun_mask) lun_mask=1;
fl=1;
for (l=0; l<MAX_LUN; l++) {
if (!((lun_mask>>l)&1)) continue;
printf ("%d:%d ", t, l);
if (!tip->jump_tcb.l_cmd) break;
if (fl) {
fl=0;
printc (&tip->inqdata[ 8], 8);printf(" ");
printc (&tip->inqdata[16],16);printf(" ");
printc (&tip->inqdata[32], 4);printf(" ");
if (tip->period==0xffff) {
printf ("asyn");
} else if (tip->period) {
printf ("%4.1f", 1000.0 / tip->period);
} else {
printf (" ?");
}
printf (" ");
if (tip->minsync==255) {
printf ("asyn");
} else if (tip->minsync) {
printf ("%4.1f", 250.0 / tip->minsync);
} else {
printf (" ?");
}
} else printf (" ");
if (!tip->lp[l]) {
printf (" no\n");
continue;
};
read_lcb ((u_long) tip->lp[l]);
switch (tip->widedone) {
case 1:
printf (" 8");
break;
case 2:
printf (" 16");
break;
case 3:
printf (" 32");
break;
default:
printf (" ?");
};
if (lcb.usetags)
printf ("%5d", lcb.actlink);
else
printf (" -");
printf ("\n");
};
if (!tip->jump_tcb.l_cmd) {
printf (" --- no target.\n");
continue;
};
if (verbose<1) continue;
for (i=0; i<8; i++) {
char* (class[10])={
"disk","tape","printer","processor",
"worm", "cdrom", "scanner", "optical disk",
"media changer", "communication device"};
d = tip->inqdata[i];
printf ("[%02x]: ",d);
switch (i) {
case 0:
f = d & 0x1f;
if (f<10) printf (class[f]);
else printf ("unknown (%x)", f);
break;
case 1:
f = (d>>7) & 1;
if (f) printf ("removable media");
else printf ("fixed media");
break;
case 2:
f = d & 7;
switch (f) {
case 0: printf ("SCSI-1");
break;
case 1: printf ("SCSI-1 with CCS");
break;
case 2: printf ("SCSI-2");
break;
default:
printf ("unknown ansi version (%d)",
f);
}
break;
case 3:
if (d&0xc0) printf ("capabilities:");
if (d&0x80) printf (" AEN");
if (d&0x40) printf (" TERMINATE-I/O");
break;
case 7:
if (d&0xfb) printf ("capabilities:");
if (d&0x80) printf (" relative");
if (d&0x40) printf (" wide32");
if (d&0x20) printf (" wide");
if (d&0x10) printf (" synch");
if (d&0x08) printf (" link");
if (d&0x02) printf (" tags");
if (d&0x01) printf (" soft-reset");
};
printf ("\n");
};
printf ("\n");
};
printf ("\n");
}
/*================================================================
**
**
** profiling
**
**
**================================================================
*/
do_profile(void)
{
#define old backup.profile
#define new ncr.profile
struct ncb backup;
struct profile diff;
int tra,line,t;
open_kvm(O_RDONLY);
set_target_mask();
if (interval<1) interval=1;
for (;;) {
/*
** Header Line 1
*/
printf (" total ");
for (t=0; t<MAX_TARGET; t++) {
if (!((target_mask>>t)&1)) continue;
printf (" ");
printc (&ncr.target[t].inqdata[16],8);
};
printf (" transf. disconn interru");
if (verbose>=1) printf (" ---- ms/transfer ----");
printf ("\n");
/*
** Header Line 2
*/
printf ("t/s kb/s ");
for (t=0; t<MAX_TARGET; t++) {
if (!((target_mask>>t)&1)) continue;
printf (" t/s kb/s");
};
printf (" length exp une fly brk");
if (verbose>=1) printf (" total pre post disc");
printf ("\n");
/*
** Data
*/
for(line=0;line<20;line++) {
backup = ncr;
read_ncr();
diff.num_trans = new.num_trans - old.num_trans;
diff.num_bytes = new.num_bytes - old.num_bytes;
diff.num_fly = new.num_fly - old.num_fly ;
diff.num_int = new.num_int - old.num_int ;
diff.ms_setup = new.ms_setup - old.ms_setup;
diff.ms_data = new.ms_data - old.ms_data;
diff.ms_disc = new.ms_disc - old.ms_disc;
diff.ms_post = new.ms_post - old.ms_post;
diff.num_disc = new.num_disc - old.num_disc;
diff.num_break = new.num_break - old.num_break;
tra = diff.num_trans;
if (!tra) tra=1;
printf ("%3.0f %4.0f ",
(1.0 * diff.num_trans) / interval,
(1.0 * diff.num_bytes) / (1024*interval));
for (t=0; t<MAX_TARGET; t++) {
if (!((target_mask>>t)&1)) continue;
printf (" %3.0f %4.0f",
((ncr.target[t].transfers-
backup.target[t].transfers)*1.0)
/interval,
((ncr.target[t].bytes-
backup.target[t].bytes)*1.0)
/(1024*interval));
};
printf ("%7.0f ", (diff.num_bytes*1.0) / tra);
printf (" %4.0f", (1.0*(diff.num_disc-diff.num_break))
/interval);
printf ("%4.0f", (1.0*diff.num_break)/interval);
printf ("%4.0f", (1.0*diff.num_fly) / interval);
printf ("%4.0f", (1.0*diff.num_int) / interval);
if (verbose >= 1) {
printf ("%7.1f",
(diff.ms_disc+diff.ms_data+diff.ms_setup+diff.ms_post)
* 1.0 / tra);
printf ("%5.1f%5.1f%6.1f",
1.0 * diff.ms_setup / tra,
1.0 * diff.ms_post / tra,
1.0 * diff.ms_disc / tra);
};
printf ("\n");
fflush (stdout);
sleep (interval);
};
};
}
/*================================================================
**
**
** Port access
**
**
**================================================================
*/
static int kernelwritefile;
static char* kernelwritefilename = _PATH_KMEM;
void openkernelwritefile(void)
{
if (kernelwritefile) return;
kernelwritefile = open (kernelwritefilename, O_WRONLY);
if (kernelwritefile<3) {
fprintf (stderr, "%s: %s: %s\n",
prog, kernelwritefilename, strerror(errno));
exit (1);
};
}
void out (u_char reg, u_char val)
{
u_long addr = ncr.vaddr + reg;
openkernelwritefile();
if (lseek (kernelwritefile, addr, 0) != addr) {
fprintf (stderr, "%s: %s: %s\n",
prog, kernelwritefilename, strerror(errno));
exit (1);
}
if (write (kernelwritefile, &val, 1) < 0) {
fprintf (stderr, "%s: %s: %s\n",
prog, kernelwritefilename, strerror(errno));
exit (1);
};
}
u_char in (u_char reg)
{
u_char res;
if (!KVM_READ (
(ncr.vaddr + reg),
&res,
1)) {
fprintf (stderr, "%s: bad kvm read.\n", prog);
exit (1);
}
return (res);
}
/*================================================================
**
**
** Setting of driver parameters
**
**
**================================================================
*/
void do_set (char * arg)
{
struct usrcmd user;
u_long addr;
int i;
open_kvm(O_RDWR);
addr = ncr_base + offsetof (struct ncb, user);
for (i=3; i; i--) {
if (!KVM_READ (
(addr),
&user,
sizeof (user))) {
fprintf (stderr, "%s: bad kvm read.\n", prog);
exit (1);
}
if (!user.cmd) break;
sleep (1);
}
if (user.cmd) {
fprintf (stderr, "%s: ncb.user busy.\n", prog);
exit (1);
};
set_target_mask();
user.target = target_mask;
user.lun = lun_mask;
user.data = 0;
user.cmd = 0;
if (!strcmp(arg, "?")) { printf (
"async: disable synchronous transfers.\n"
"sync=value: set the maximal synchronous transfer rate (MHz).\n"
"fast: set FAST SCSI-2.\n"
"\n"
"wide=value: set the bus width (0=8bit 1=16bit).\n"
"\n"
"tags=value: use this number of tags.\n"
"orderedtag: use ordered tags only.\n"
"simpletag: use simple tags only.\n"
"orderedwrite: use simple tags for read, else ordered tags.\n"
"\n"
"debug=value: set debug mode.\n"
"\n");
return;
};
if (!strcmp(arg, "async")) {
user.data = 255;
user.cmd = UC_SETSYNC;
};
if (!strcmp(arg, "fast")) {
user.data = 25;
user.cmd = UC_SETSYNC;
};
if (!strncmp(arg, "sync=", 5)) {
double f = strtod (arg+5, NULL);
if (f>=4.0 && f<=10.0) {
user.data = 250.0 / f;
user.cmd = UC_SETSYNC;
};
};
if (!strncmp(arg, "wide=", 5)) {
u_char t = strtoul (arg+5, (char**)0, 0);
if (t<=1) {
user.data = t;
user.cmd = UC_SETWIDE;
};
};
if (!strncmp(arg, "tags=", 5)) {
u_char t = strtoul (arg+5, (char**)0, 0);
if (t<=MAX_TAGS) {
user.data = t;
user.cmd = UC_SETTAGS;
};
};
if (!strncmp(arg, "flags=", 6)) {
u_char t = strtoul (arg+6, (char**)0, 0);
if (t<=0xff) {
user.data = t;
user.cmd = UC_SETFLAG;
};
};
if (!strncmp(arg, "debug=", 6)) {
user.data = strtoul (arg+6, (char**)0, 0);
user.cmd = UC_SETDEBUG;
};
if (!strcmp(arg, "orderedtag")) {
user.data = M_ORDERED_TAG;
user.cmd = UC_SETORDER;
};
if (!strcmp(arg, "simpletag")) {
user.data = M_SIMPLE_TAG;
user.cmd = UC_SETORDER;
};
if (!strcmp(arg, "orderedwrite")) {
user.data = 0;
user.cmd = UC_SETORDER;
};
if (user.cmd) {
openkernelwritefile();
if (lseek (kernelwritefile, addr, 0) != addr) {
fprintf (stderr, "%s: %s: %s\n",
prog, kernelwritefilename, strerror(errno));
exit (1);
}
if (write (kernelwritefile, &user, sizeof (user)) < 0) {
fprintf (stderr, "%s: %s: %s\n",
prog, kernelwritefilename, strerror(errno));
exit (1);
}
return;
};
fprintf (stderr, "%s: do_set \"%s\" not (yet) implemented.\n",
prog, arg);
}
/*================================================================
**
**
** D O _ K I L L
**
**
**================================================================
*/
do_kill(char * arg)
{
open_kvm(O_RDWR);
if (!strcmp(arg, "?")) { printf (
"scsireset: force SCSI bus reset.\n"
"scriptabort: send an abort cmd to the script processor.\n"
"scriptstart: start script processind (set SIGP bit).\n"
"evenparity: force even parity.\n"
"oddparity: force odd parity.\n"
"noreselect: disable reselect (force timeouts).\n"
"doreselect: enable reselect.\n"
"\n");
return;
};
if (!wizard) {
fprintf (stderr, "%s: You are NOT a wizard!\n", prog);
exit (2);
};
if (!strcmp(arg, "scsireset")) {
out (0x01, 0x08);
out (0x01, 0x00);
return;
};
if (!strcmp(arg, "scriptabort")) {
out (0x14, 0x80);
out (0x14, 0x20);
return;
};
if (!strcmp(arg, "scriptstart")) {
out (0x14, 0x20);
return;
};
if (!strcmp(arg, "evenparity")) {
out (0x01, 0x04);
return;
};
if (!strcmp(arg, "oddparity")) {
out (0x01, 0x00);
return;
};
if (!strcmp(arg, "noreselect")) {
out (0x04, in (0x04) & ~RRE);
return;
};
if (!strcmp(arg, "doreselect")) {
out (0x04, in (0x04) | RRE);
return;
};
fprintf (stderr, "%s: do_kill \"%s\" not (yet) implemented.\n",
prog, arg);
}
/*================================================================
**
**
** Write debug info: utilities: write symbolname.
**
**
**================================================================
*/
static const char * sn (u_long a)
{
static char buffer[100];
const char * s="";
u_long d,m;
a -= ncr.p_script;
m = sizeof (struct script);
if ((d=a-offsetof(struct script, start))<m) m=d, s="<start>";
if ((d=a-offsetof(struct script, start1))<m) m=d, s="<start1>";
if ((d=a-offsetof(struct script, startpos))<m) m=d, s="<startpos>";
if ((d=a-offsetof(struct script, tryloop))<m) m=d, s="<tryloop>";
if ((d=a-offsetof(struct script, trysel))<m) m=d, s="<trysel>";
if ((d=a-offsetof(struct script, skip))<m) m=d, s="<skip>";
if ((d=a-offsetof(struct script, skip2))<m) m=d, s="<skip2>";
if ((d=a-offsetof(struct script, idle))<m) m=d, s="<idle>";
if ((d=a-offsetof(struct script, select))<m) m=d, s="<select>";
if ((d=a-offsetof(struct script, prepare))<m) m=d, s="<prepare>";
if ((d=a-offsetof(struct script, loadpos))<m) m=d, s="<loadpos>";
if ((d=a-offsetof(struct script, prepare2))<m) m=d, s="<prepare2>";
if ((d=a-offsetof(struct script, setmsg))<m) m=d, s="<setmsg>";
if ((d=a-offsetof(struct script, clrack))<m) m=d, s="<clrack>";
if ((d=a-offsetof(struct script, dispatch))<m) m=d, s="<dispatch>";
if ((d=a-offsetof(struct script, checkatn))<m) m=d, s="<checkatn>";
if ((d=a-offsetof(struct script, command))<m) m=d, s="<command>";
if ((d=a-offsetof(struct script, status))<m) m=d, s="<status>";
if ((d=a-offsetof(struct script, msg_in))<m) m=d, s="<msg_in>";
if ((d=a-offsetof(struct script, msg_bad))<m) m=d, s="<msg_bad>";
if ((d=a-offsetof(struct script, msg_parity))<m) m=d, s="<msg_parity>";
if ((d=a-offsetof(struct script, msg_reject))<m) m=d, s="<msg_reject>";
if ((d=a-offsetof(struct script, msg_extended))<m) m=d, s="<msg_extended>";
if ((d=a-offsetof(struct script, msg_sdtr))<m) m=d, s="<msg_sdtr>";
if ((d=a-offsetof(struct script, complete))<m) m=d, s="<complete>";
if ((d=a-offsetof(struct script, cleanup))<m) m=d, s="<cleanup>";
if ((d=a-offsetof(struct script, cleanup0))<m) m=d, s="<cleanup>";
if ((d=a-offsetof(struct script, signal))<m) m=d, s="<signal>";
if ((d=a-offsetof(struct script, save_dp))<m) m=d, s="<save_dp>";
if ((d=a-offsetof(struct script, restore_dp))<m) m=d, s="<restore_dp>";
if ((d=a-offsetof(struct script, disconnect))<m) m=d, s="<disconnect>";
if ((d=a-offsetof(struct script, msg_out))<m) m=d, s="<msg_out>";
if ((d=a-offsetof(struct script, msg_out_done))<m) m=d, s="<msg_out_done>";
if ((d=a-offsetof(struct script, msg_out_abort))<m) m=d, s="<msg_out_abort>";
if ((d=a-offsetof(struct script, getcc))<m) m=d, s="<getcc>";
if ((d=a-offsetof(struct script, getcc1))<m) m=d, s="<getcc1>";
if ((d=a-offsetof(struct script, getcc2))<m) m=d, s="<getcc2>";
if ((d=a-offsetof(struct script, badgetcc))<m) m=d, s="<badgetcc>";
if ((d=a-offsetof(struct script, reselect))<m) m=d, s="<reselect>";
if ((d=a-offsetof(struct script, reselect2))<m) m=d, s="<reselect2>";
if ((d=a-offsetof(struct script, resel_tmp))<m) m=d, s="<resel_tmp>";
if ((d=a-offsetof(struct script, resel_lun))<m) m=d, s="<resel_lun>";
if ((d=a-offsetof(struct script, resel_tag))<m) m=d, s="<resel_tag>";
if ((d=a-offsetof(struct script, data_in))<m) m=d, s="<data_in>";
if ((d=a-offsetof(struct script, data_out))<m) m=d, s="<data_out>";
if ((d=a-offsetof(struct script, no_data))<m) m=d, s="<no_data>";
if ((d=a-offsetof(struct script, aborttag))<m) m=d, s="<aborttag>";
if ((d=a-offsetof(struct script, abort))<m) m=d, s="<abort>";
if (!*s) return s;
sprintf (buffer, "%s:%d%c", s, m/4, 0);
return (buffer);
}
/*================================================================
**
**
** Write debug info: utilities: write misc. fields.
**
**
**================================================================
*/
static void printm (u_char * msg, int len)
{
u_char l;
do {
if (*msg==M_EXTENDED)
l=msg[1]+2;
else if ((*msg & 0xf0)==0x20)
l=2;
else l=1;
len-=l;
printf (" %x",*msg++);
while (--l>0) printf ("-%x",*msg++);
} while (len>0);
}
void dump_table (const char * str, struct scr_tblmove * p, int l)
{
int i;
for (i=0;l>0;i++,p++,l--) if (p->size) {
printf (" %s[%d]: %5d @ 0x%08x\n",
str, i, p->size, p->addr);
};
}
void dump_link (const char* name, struct link * link)
{
printf ("%s: cmd=%08x pa=%08x %s\n",
name, link->l_cmd, link->l_paddr, sn(link->l_paddr));
}
/*================================================================
**
**
** Write debug info: utilities: write time fields.
**
**
**================================================================
*/
void dump_tstamp (const char* name, struct tstamp * p)
#define P(id,fld)\
if (p->fld.tv_sec) \
printf ("%s: "id" at %s.%06d",\
name,ctime(&p->fld.tv_sec),p->fld.tv_usec);
{
P ("started ", start);
P ("ended ", end );
P ("selected ", select);
P ("command ", command);
P ("data ", data);
P ("status ", status);
P ("disconnected", disconnect);
P ("reselected ", reselect);
printf ("\n");
}
void dump_profile (const char* name, struct profile * p)
{
printf ("%s: %10d transfers.\n" ,name,p->num_trans);
printf ("%s: %10d bytes transferred.\n",name,p->num_bytes);
printf ("%s: %10d disconnects.\n" ,name,p->num_disc);
printf ("%s: %10d short transfers.\n" ,name,p->num_break);
printf ("%s: %10d interrupts.\n" ,name,p->num_int);
printf ("%s: %10d on the fly ints.\n" ,name,p->num_fly);
printf ("%s: %10d ms setup time.\n" ,name,p->ms_setup);
printf ("%s: %10d ms data transfer.\n" ,name,p->ms_data);
printf ("%s: %10d ms disconnected.\n" ,name,p->ms_disc);
printf ("%s: %10d ms postprocessing.\n",name,p->ms_post);
printf ("\n");
}
/*================================================================
**
**
** Write debug info: utilities: write script registers.
**
**
**================================================================
*/
static void dump_reg(struct ncr_reg * rp)
{
u_char *reg = (u_char*) rp;
#define l(i) (reg[i]+(reg[i+1]<<8ul)+(reg[i+2]<<16ul)+(reg[i+3]<<24ul))
int ad;
char*(phasename[8])={"DATA-OUT","DATA-IN","COMMAND","STATUS",
"ILG-OUT","ILG-IN","MESSAGE-OUT","MESSAGE-IN"};
for (ad=0x00;ad<0x80;ad++) {
switch (ad % 16) {
case 0:
printf (" %02x:\t",ad);
break;
case 8:
printf (" : ");
break;
default:
printf (" ");
};
printf ("%02x", reg[ad]);
if (ad % 16 == 15) printf ("\n");
};
printf ("\n");
printf (" DSP %08x %-20s CMD %08x DSPS %08x %s\n",
l(0x2c),sn(l(0x2c)),l(0x24),l(0x30), sn(l(0x30)));
printf (" TEMP %08x %-20s DSA %08x\n",
l(0x1c),sn(l(0x1c)),l(0x10));
printf ("\n");
printf (" Busstatus: ");
if ((reg[0x0b]>>7)&1) printf (" Req");
if ((reg[0x0b]>>6)&1) printf (" Ack");
if ((reg[0x0b]>>5)&1) printf (" Bsy");
if ((reg[0x0b]>>4)&1) printf (" Sel");
if ((reg[0x0b]>>3)&1) printf (" Atn");
printf (" %s\n", phasename[reg[0x0b]&7]);
printf (" Dmastatus: ");
if ((reg[0x0c]>>7)&1) printf (" FifoEmpty");
if ((reg[0x0c]>>6)&1) printf (" MasterParityError");
if ((reg[0x0c]>>5)&1) printf (" BusFault");
if ((reg[0x0c]>>4)&1) printf (" Aborted");
if ((reg[0x0c]>>3)&1) printf (" SingleStep");
if ((reg[0x0c]>>2)&1) printf (" Interrupt");
if ((reg[0x0c]>>0)&1) printf (" IllegalInstruction");
printf ("\n");
printf (" Intstatus: ");
if ((reg[0x14]>>7)&1) printf (" Abort");
if ((reg[0x14]>>6)&1) printf (" SoftwareReset");
if ((reg[0x14]>>5)&1) printf (" SignalProcess");
if ((reg[0x14]>>4)&1) printf (" Semaphore");
if ((reg[0x14]>>3)&1) printf (" Connected");
if ((reg[0x14]>>2)&1) printf (" IntOnTheFly");
if ((reg[0x14]>>1)&1) printf (" SCSI-Interrupt");
if ((reg[0x14]>>0)&1) printf (" DMA-Interrupt");
printf ("\n");
printf (" ScsiIstat: ");
if ((reg[0x42]>>7)&1) printf (" PhaseMismatch");
if ((reg[0x42]>>6)&1) printf (" Complete");
if ((reg[0x42]>>5)&1) printf (" Selected");
if ((reg[0x42]>>4)&1) printf (" Reselected");
if ((reg[0x42]>>3)&1) printf (" GrossError");
if ((reg[0x42]>>2)&1) printf (" UnexpectedDisconnect");
if ((reg[0x42]>>1)&1) printf (" ScsiReset");
if ((reg[0x42]>>0)&1) printf (" ParityError");
if ((reg[0x43]>>2)&1) printf (" SelectionTimeout");
if ((reg[0x43]>>1)&1) printf (" TimerExpired");
if ((reg[0x43]>>0)&1) printf (" HandshakeTimeout");
printf ("\n");
printf (" ID=%d DEST-ID=%d RESEL-ID=%d\n", reg[4]&7, reg[6]&7, reg[0xa]&7);
printf ("\n");
}
/*================================================================
**
**
** Write debug info: utilities: write header.
**
**
**================================================================
*/
char * debug_opt;
dump_head (struct head * hp)
{
dump_link (" launch", & hp->launch);
printf (" savep: %08x %s\n",
hp->savep, sn((u_long) hp->savep));
printf (" cp: %08x %s\n",
hp->cp, sn((u_long)hp->cp));
if (strchr (debug_opt, 'y')) {
printf ("\n");
dump_tstamp (" timestamp", &hp->stamp);
};
printf (" status: %x %x %x %x %x %x %x %x\n",
hp->status[0], hp->status[1], hp->status[2], hp->status[3],
hp->status[4], hp->status[5], hp->status[6], hp->status[7]);
printf ("\n");
}
/*================================================================
**
**
** Write debug info: utilities: write ccb.
**
**
**================================================================
*/
void dump_ccb (struct ccb * cp, u_long base)
{
printf ("----------------------\n");
printf ("struct ccb @ %08x:\n", base);
printf ("----------------------\n");
dump_link (" next", &cp->jump_ccb);
dump_link (" call", &cp->call_tmp);
dump_head (&cp->phys.header);
if (strchr (debug_opt, 's')) {
dump_table(" smsg", &cp->phys.smsg, 1);
dump_table("smsg2", &cp->phys.smsg2, 1);
dump_table(" cmd", &cp->phys.cmd, 1);
dump_table(" data", &cp->phys.data[0],MAX_SCATTER);
dump_table("sense", &cp->phys.sense, 1);
};
if (strchr (debug_opt, 'a')) {
int i;
for (i=0; i<8; i++)
printf (" patch[%d]: %08x\n", i, cp->patch[i]);
};
if (strchr (debug_opt, 'x')) {
printf (" xfer: -- dump not yet implemented.\n");
};
if (strchr (debug_opt, 'm')) {
printf (" smsg:");
printm (cp->scsi_smsg, cp->phys.smsg.size);
printf ("\n");
printf (" smsg2:");
printm (cp->scsi_smsg2, cp->phys.smsg2.size);
printf ("\n");
};
printf (" magic: %x\n", cp->magic);
if (cp->tlimit)
printf (" timeout at: %s", ctime((time_t*)&cp->tlimit));
printf (" link_ccb: %08x\n", (u_long) cp->link_ccb);
printf (" next_ccb: %08x\n", (u_long) cp->next_ccb);
printf (" tag: %d\n", cp->tag);
printf ("\n");
}
/*================================================================
**
**
** Write debug info: struct lcb
**
**
**================================================================
*/
static void dump_lcb (u_long base)
{
struct lcb l;
struct ccb c;
u_long cp,cn;
printf ("----------------------\n");
printf ("struct lcb @ %08x:\n", base);
printf ("----------------------\n");
if (!KVM_READ (
base,
&l,
sizeof (struct lcb))) {
fprintf (stderr, "%s: bad kvm read.\n", prog);
exit (1);
};
printf (" reqccbs: %d\n", l.reqccbs);
printf (" actccbs: %d\n", l.actccbs);
printf (" reqlink: %d\n", l.reqlink);
printf (" actlink: %d\n", l.actlink);
printf (" usetags: %d\n", l.usetags);
dump_link (" jump_lcb", &l.jump_lcb);
dump_link (" call_tag", &l.call_tag);
dump_link (" jump_ccb", &l.jump_ccb);
printf ("\n");
cp = (u_long) l.next_ccb;
cn = 0;
while (cp) {
cn++;
printf ("ccb #%d:\n", cn);
if (!KVM_READ (
cp,
&c,
sizeof (struct ccb))) {
fprintf (stderr, "%s: bad kvm read.\n", prog);
exit (1);
};
dump_ccb (&c, cp);
cp= (u_long) c.next_ccb;
};
}
/*================================================================
**
**
** Write debug info: struct tcb
**
**
**================================================================
*/
static void dump_tip (struct tcb * tip)
{
int i;
u_long lp;
printf ("----------------------\n");
printf ("struct tcb:\n");
printf ("----------------------\n");
printf (" transfers:%10d.\n", tip->transfers);
printf (" bytes:%10d.\n", tip->bytes );
printf (" user limits: usrsync=%d usrwide=%d usrtags=%d.\n",
tip->usrsync, tip->usrwide, tip->usrtags);
printf (" sync: minsync=%d, maxoffs=%d, period=%d ns, sval=%x.\n",
tip->minsync, tip->maxoffs, tip->period, tip->sval);
printf (" wide: widedone=%d, wval=%x.\n",
tip->widedone, tip->wval);
printf (" hold_cp: %x\n", tip->hold_cp);
dump_link (" jump_tcb", &tip->jump_tcb);
dump_link (" call_lun", &tip->call_lun);
dump_link (" jump_lcb", &tip->jump_lcb);
if (tip->hold_cp) printf (" hold_cp: @ %x\n", tip->hold_cp);
printf ("\n");
if (strchr (debug_opt, 'l')) {
for (i=0;i<MAX_LUN;i++) {
lp= (u_long) tip->lp[i];
printf ("logic unit #%d:\n", i);
if (lp) dump_lcb (lp);
};
}
}
/*================================================================
**
**
** Write debug info: struct ncb
**
**
**================================================================
*/
static void dump_ncr (void)
{
u_long tp;
int i;
printf ("----------------------\n");
printf ("struct ncb @ %x:\n", ncr_base);
printf ("----------------------\n");
dump_link (" jump_tcb", &ncr.jump_tcb);
printf (" register: @ %x (p=%x)\n", ncr.vaddr, ncr.paddr);
if (wizard && strchr (debug_opt, 'r')) {
struct ncr_reg reg;
if (!KVM_READ (
ncr.vaddr,
&reg,
sizeof (reg))) {
fprintf (stderr, "%s: bad kvm read.\n", prog);
exit (1);
};
printf ("\n");
dump_reg (&reg);
};
printf (" script: @ %x (p=%x)\n", ncr.script, ncr.p_script);
printf ("hostscsiaddr: %d\n", ncr.myaddr);
printf (" ns_async: %d ns\n", ncr.ns_async);
printf (" ns_sync : %d ns\n", ncr.ns_sync);
printf (" scntl3: 0x%02x\n", ncr.rv_scntl3);
printf ("\n");
/* sc_link not dumped */
if (strchr (debug_opt, 'u')) {
printf (" usercmd: cmd=%x data=%x target=%x lun=%x\n",
ncr.user.cmd,
ncr.user.data,
ncr.user.target,
ncr.user.lun);
};
printf (" actccbs: %d\n", ncr.actccbs);
if (strchr (debug_opt, 'q')) {
u_long startpos;
if (!KVM_READ (
((u_long)ncr.script
+offsetof(struct script, startpos)),
&startpos,
sizeof (startpos))) {
fprintf (stderr, "%s: bad kvm read.\n", prog);
exit (1);
};
printf (" startpos: %x\n", startpos);
printf (" slot: %d\n", (startpos-
(ncr.p_script+offsetof(struct script, tryloop)))/20);
printf (" squeuput: %d\n", ncr.squeueput);
for (i=0; i<MAX_START; i++)
printf ("%12d: %08x %s\n", i,
ncr.squeue[i], sn(ncr.squeue[i]));
printf ("\n");
};
printf (" ticks: %d ms\n", ncr.ticks * 10);
printf (" heartbeat: %s", ctime ((time_t*)&ncr.heartbeat));
printf (" lasttime: %s", ctime ((time_t*)&ncr.lasttime));
printf ("\n");
if (wizard && strchr (debug_opt, 'd') && ncr.regtime.tv_sec) {
printf (" regdump: %s", ctime (&ncr.regtime.tv_sec));
dump_reg (&ncr.regdump);
};
if (strchr (debug_opt, 'p')) {
printf ("\n");
dump_profile (" profile", &ncr.profile);
};
if (strchr (debug_opt, 'h')) {
printf ("\n");
dump_head ( &ncr.header);
};
if (strchr (debug_opt, 'c')) {
dump_ccb (&ncr.ccb, ncr_base + offsetof (struct ncb, ccb));
};
if (strchr (debug_opt, 'm')) {
printf (" msgout:"); printm (ncr.msgout,0); printf ("\n");
printf (" msg in:"); printm (ncr.msgin,0); printf ("\n");
printf ("\n");
};
if (strchr (debug_opt, 't')) {
struct tcb * tip;
for (i=0;i<MAX_TARGET;i++) {
tip = &ncr.target[i];
if (!tip->jump_tcb.l_cmd) continue;
printf ("target #%d:\n", i);
dump_tip (tip);
}
}
}
/*================================================================
**
**
** D O _ D E B U G
**
**
**================================================================
*/
do_debug(char * arg)
{
open_kvm(O_RDONLY);
debug_opt = arg;
if (strchr (debug_opt, '?')) printf (
"'?': list debug options [sic].\n"
"'a': show patchfields in ccbs (requires c).\n"
"'c': show ccbs.\n"
"'d': show register dump.\n"
"'h': show header information.\n"
"'m': show message buffers.\n"
"'n': show ncr main control block.\n"
"'p': show profiling information.\n"
"'q': show start queue.\n"
"'r': show registers (*DANGEROUS*).\n"
"'s': show scatter/gather info.\n"
"'t': show target control blocks.\n"
"'u': show user cmd field.\n"
"'x': show generic xfer structure.\n"
"'y': show timestamps.\n"
"\n"
);
if (strchr (debug_opt, 'n')) dump_ncr ();
if (!wizard) {
fprintf (stderr, "%s: You are NOT a wizard!\n", prog);
exit (2);
};
if (strchr (debug_opt, 'r')) {
struct ncr_reg reg;
if (!KVM_READ (
ncr.vaddr,
&reg,
sizeof (reg))) {
fprintf (stderr, "%s: bad kvm read.\n", prog);
exit (1);
};
dump_reg (&reg);
};
}
/*================================================================
**
**
** Main function
**
**
**================================================================
*/
void main(argc, argv)
int argc;
char **argv;
{
extern char *optarg;
extern int optind;
int usage=0;
char * charp;
int ch, getopt(),atoi();
int i,step;
prog = *argv;
while ((ch = getopt(argc, argv, "M:N:u:f:t:l:p:s:k:d:vwhin:?")) != -1)
switch((char)ch) {
case 'M':
if (kvm_isopen) {
fprintf (stderr,
"%s: -M: kernel file already open.\n",
prog);
exit (1);
};
kmemf = optarg;
break;
case 'N':
if (kvm_isopen) {
fprintf (stderr,
"%s: -N: symbol table already open.\n",
prog);
exit (1);
};
vmunix = optarg;
break;
case 'f':
fprintf (stderr,
"%s: -f: option not yet implemented.\n",
prog);
exit (1);
case 'u':
i = strtoul (optarg, &charp, 0);
if (!*optarg || *charp || (i<0)) {
fprintf (stderr,
"%s: bad unit number \"%s\".\n",
prog, optarg);
exit (1);
}
ncr_unit = i;
break;
case 't':
i = strtoul (optarg, &charp, 0);
if (!*optarg || *charp || (i<0) || (i>=MAX_TARGET)) {
fprintf (stderr,
"%s: bad target number \"%s\" (valid range: 0-%d).\n",
prog, optarg, MAX_TARGET-1);
exit (1);
}
target_mask |= 1ul << i;
break;
case 'n':
open_kvm(O_RDONLY);
i = strtoul (optarg, &charp, 0);
printf ("addr %d (0x%x) has label %s.\n",
i,i,sn(i));
break;
case 'l':
i = strtoul (optarg, &charp, 0);
if (!*optarg || *charp || (i<0) || (i>=MAX_LUN)) {
fprintf (stderr,
"%s: bad logic unit number \"%s\" (valid range: 0-%d).\n",
prog, optarg, MAX_LUN);
exit (1);
}
global_lun_mask |= 1ul << i;
break;
case 'p':
i = strtoul (optarg, &charp, 0);
if (!*optarg || *charp || (i<1) || (i>60)) {
fprintf (stderr,
"%s: bad interval \"%s\".\n",
prog, optarg);
exit (1);
}
interval = i;
do_profile();
break;
case 'w':
if(geteuid()==0)
wizard=1;
break;
case 'v':
verbose++;
break;
case 'i':
do_info();
break;
case 's':
do_set(optarg);
break;
case 'd':
do_debug(optarg);
break;
case 'k':
do_kill(optarg);
break;
case 'h':
case '?':
usage++;
break;
default:(void)fprintf(stderr,
"%s: illegal option \"%c\".\n", prog, ch);
usage++;
}
argv += optind;
argc -= optind;
if (argc) printf ("%s: rest of line starting with \"%s\" ignored.\n",
prog, *argv);
if (verbose&&!kvm_isopen) usage++;
if (usage) {
fprintf (stderr,
"Usage:\n"
"\n"
"%s [-M$] [-N$] {-f$} {-t#} {-l#} [-hivw?] [-d$] [-s$] [-k] [[-p] <time>]\n"
"\n"
"-t <#> select target number\n"
"-l <#> select lun number\n"
"-i get info\n"
"-v verbose\n"
"-p <seconds> performance data\n"
"\n"
"Wizards only (proceed on your own risk):\n"
"-n <#> get the name for address #\n"
"-w wizard mode\n"
"-d <options> debug info\n"
"-d? list debug options\n"
"-s <param=value> set parameter\n"
"-s? list parameters\n"
"-k <torture> torture driver by simulating errors\n"
"-k? list tortures\n"
"-M <kernelimage> (default: %s)\n"
"-N <symboltable> (default: %s)\n"
, prog, _PATH_KMEM, _PATH_UNIX);
if (verbose) fprintf (stderr, ident);
exit (1);
}
if (!kvm_isopen) {
do_info();
do_profile();
};
exit (0);
}