612 lines
14 KiB
C
612 lines
14 KiB
C
/*
|
|
* @(#)sd.c 1.1 94/10/31 Copyright (c) 1985 by Sun Microsystems, Inc.
|
|
*
|
|
* Driver for SCSI disk drives.
|
|
*/
|
|
|
|
/*#define SDDEBUG /* Allow compiling of debug code */
|
|
#define REL4 /* Enable release 4 mods */
|
|
|
|
#ifdef REL4
|
|
#include <stand/saio.h>
|
|
#include <stand/param.h>
|
|
#include <sun/dklabel.h>
|
|
#include <sun/dkio.h>
|
|
#include <sys/buf.h>
|
|
#include <mon/sunromvec.h>
|
|
#include <mon/idprom.h>
|
|
#include <mon/cpu.addrs.h>
|
|
#include <stand/scsi.h>
|
|
#else REL4
|
|
#include "saio.h"
|
|
#include "param.h"
|
|
#include "../sun/dklabel.h"
|
|
#include "../sun/dkio.h"
|
|
#include "../h/buf.h"
|
|
#include "../mon/sunromvec.h"
|
|
#include "../mon/idprom.h"
|
|
#include "../mon/cpu.addrs.h"
|
|
#include "scsi.h"
|
|
#endif REL4
|
|
|
|
|
|
extern int sc_reset;
|
|
extern int xxprobe(), xxboot(), nullsys();
|
|
int sdopen(), sdclose(), sdstrategy();
|
|
|
|
#ifdef SDDEBUG
|
|
int sd_debug = 1; /*
|
|
* 0 = normal operation
|
|
* 1 = extended error info only
|
|
* 2 = debug and error info
|
|
* 3 = all status info
|
|
*/
|
|
/* Handy debugging 0, 1, and 2 argument printfs */
|
|
#define DPRINTF(str) \
|
|
if (sd_debug > 1) printf(str)
|
|
#define DPRINTF1(str, arg1) \
|
|
if (sd_debug > 1) printf(str,arg1)
|
|
#define DPRINTF2(str, arg1, arg2) \
|
|
if (sd_debug > 1) printf(str,arg1,arg2)
|
|
|
|
/* Handy extended error reporting 0, 1, and 2 argument printfs */
|
|
#define EPRINTF(str) \
|
|
if (sd_debug) printf(str)
|
|
#define EPRINTF1(str, arg1) \
|
|
if (sd_debug) printf(str,arg1)
|
|
#define EPRINTF2(str, arg1, arg2) \
|
|
if (sd_debug) printf(str,arg1,arg2)
|
|
|
|
#else SDDEBUG
|
|
#define DPRINTF(str)
|
|
#define DPRINTF1(str, arg2)
|
|
#define DPRINTF2(str, arg1, arg2)
|
|
#define EPRINTF(str)
|
|
#define EPRINTF1(str, arg2)
|
|
#define EPRINTF2(str, arg1, arg2)
|
|
#endif SDDEBUG
|
|
|
|
|
|
#ifdef BOOTBLOCK
|
|
#define SD_MAXSIZE MAXBSIZE
|
|
#define SC_ERROR(str)
|
|
#define SC_ERROR1(str, arg1)
|
|
#else BOOTBLOCK
|
|
#define SD_MAXSIZE (4 * MAXBSIZE)
|
|
#define SC_ERROR printf
|
|
#define SC_ERROR1 printf
|
|
#endif BOOTBLOCK
|
|
|
|
/*
|
|
* This table defines the auto-probe disk list. Currently only st0 and st1
|
|
* are probed automatically. Others can be added by simply adding them
|
|
* to the following table.
|
|
*/
|
|
u_char sd_index[] = { 0x00, 0x18 };
|
|
|
|
struct sdparam {
|
|
int sd_target;
|
|
int sd_unit;
|
|
unsigned sd_boff; /* real boff... */
|
|
struct host_saioreq h_sip; /* sip for host adapter */
|
|
};
|
|
|
|
/*
|
|
* Our DMA space
|
|
*/
|
|
struct sddma {
|
|
char buffer[SD_MAXSIZE];
|
|
char sbuffer[SC_SENSE_LENGTH];
|
|
};
|
|
#define BUF (((struct sddma *)sip->si_dmaaddr)->buffer)
|
|
#define STSBUF (((struct sddma *)sip->si_dmaaddr)->sbuffer)
|
|
#define ROUNDUP(x) ((x + SECSIZE - 1) & ~(SECSIZE - 1))
|
|
#define MSGS_OFF 1
|
|
#define MSGS_ON 0
|
|
|
|
/*
|
|
* What resources we need to run
|
|
*/
|
|
struct devinfo sdinfo = {
|
|
0, /* No device to map in */
|
|
sizeof (struct sddma),
|
|
sizeof (struct sdparam),
|
|
0, /* Dummy devices */
|
|
0, /* Dummy devices */
|
|
MAP_MAINMEM,
|
|
SD_MAXSIZE, /* transfer size */
|
|
};
|
|
|
|
/*
|
|
* The interfaces we export
|
|
*/
|
|
struct boottab sddriver = {
|
|
"sd", xxprobe, xxboot, sdopen, sdclose, sdstrategy,
|
|
"sd: SCSI disk", &sdinfo
|
|
};
|
|
|
|
|
|
#ifdef SDDEBUG
|
|
sd_print_buffer(y, count)
|
|
register u_char *y;
|
|
register int count;
|
|
{
|
|
register int x;
|
|
|
|
for (x = 0; x < count; x++)
|
|
printf("%x ", *y++);
|
|
printf("\n");
|
|
}
|
|
#endif SDDEBUG
|
|
|
|
|
|
#ifndef BOOTBLOCK
|
|
/*
|
|
* Test routine for isspinning() to see if SCSI disk is running.
|
|
*/
|
|
/*ARGSUSED*/
|
|
int
|
|
sdspin(sip, dummy)
|
|
struct saioreq *sip;
|
|
int dummy;
|
|
{
|
|
DPRINTF("sdspin:\n");
|
|
return (sdcmd(SC_TEST_UNIT_READY, sip, MSGS_OFF));
|
|
}
|
|
#endif BOOTBLOCK
|
|
|
|
|
|
/*
|
|
* Open the SCSI Disk controller
|
|
*/
|
|
sdopen(sip)
|
|
register struct saioreq *sip;
|
|
{
|
|
register struct sdparam *sdp;
|
|
register struct host_saioreq *h_sip; /* Sip for host adapter */
|
|
register struct dk_label *label;
|
|
int index, ctlr;
|
|
int do_reset = 1;
|
|
DPRINTF("sdopen:\n");
|
|
|
|
sdp = (struct sdparam *)sip->si_devdata;
|
|
label = (struct dk_label *)BUF;
|
|
bzero( (char *)sdp, (sizeof (struct sdparam)));
|
|
ctlr = sip->si_ctlr; /* Ctlr. number */
|
|
|
|
/* Set up host adapter and target info. */
|
|
SD_OPEN_PROBE:
|
|
if (sip->si_unit == 0) {
|
|
index = 0;
|
|
sdp->sd_unit = sd_index[index] & 0x07; /* Logical unit */
|
|
sdp->sd_target = sd_index[index++] >> 3; /* Target number */
|
|
} else {
|
|
sdp->sd_unit = sip->si_unit & 0x07; /* Logical unit */
|
|
sdp->sd_target = sip->si_unit >> 3; /* Target number */
|
|
}
|
|
|
|
|
|
SD_RETRY_OPEN:
|
|
DPRINTF2("sdopen: ctlr= 0x%x unit= 0x%x", ctlr, sdp->sd_unit);
|
|
DPRINTF1(" target= 0x%x\n", sdp->sd_target);
|
|
|
|
h_sip = &sdp->h_sip;
|
|
h_sip->ctlr = ctlr; /* Ctlr. number */
|
|
h_sip->unit = sdp->sd_target; /* Host adapter LU number */
|
|
|
|
/* Probe for host adapter */
|
|
if (scsi_probe(h_sip) == 0) {
|
|
|
|
/* If an error occurred previously, reset the SCSI bus. */
|
|
if (sc_reset && do_reset) {
|
|
EPRINTF("sdopen: reset\n");
|
|
(void) (*h_sip->reset)(h_sip, 1);
|
|
}
|
|
|
|
/*
|
|
* Test for the controller being ready. First test will fail
|
|
* if the SCSI bus is permanently busy or if a previous op
|
|
* was interrupted in mid-transfer. Second one should work.
|
|
*/
|
|
if (sdcmd(SC_TEST_UNIT_READY, sip, MSGS_OFF) > 0)
|
|
goto SD_OPEN;
|
|
|
|
#ifndef BOOTBLOCK
|
|
/* If current disk device not ready, try the next one. */
|
|
if (sip->si_unit == 0 && index < sizeof(sd_index)) {
|
|
sdp->sd_unit = sd_index[index] & 0x07;
|
|
sdp->sd_target = sd_index[index++] >> 3;
|
|
do_reset = 0;
|
|
goto SD_RETRY_OPEN;
|
|
}
|
|
|
|
/* If current host adapter not ready, try the next one. */
|
|
if (sip->si_ctlr == 0 && ctlr < SC_NSC) {
|
|
EPRINTF("sdopen: trying next ha\n");
|
|
do_reset++;
|
|
ctlr++;
|
|
h_sip->devaddr = 0;
|
|
goto SD_OPEN_PROBE;
|
|
}
|
|
#endif BOOTBLOCK
|
|
}
|
|
EPRINTF("sdopen: device offline\n");
|
|
return (-1);
|
|
|
|
SD_OPEN:
|
|
sc_reset = 1;
|
|
sip->si_unit = (sdp->sd_target <<3) + sdp->sd_unit;
|
|
(void) sdcmd(SC_START, sip, MSGS_OFF); /* Spin-up disk */
|
|
|
|
#ifndef BOOTBLOCK
|
|
switch (isspinning(sdspin, (char *)sip, 0)) {
|
|
|
|
default: /* Error from sdspin */
|
|
case 0: /* Disk still not ready */
|
|
EPRINTF("sdopen: device not ready\n");
|
|
return (-1);
|
|
case 1: /* Spinning */
|
|
DPRINTF("sdopen: device ready\n");
|
|
break;
|
|
case 2: /* Just started spinup */
|
|
EPRINTF("sdopen: spinning up\n");
|
|
DELAY(2000000); /* Wait 2 Sec. */
|
|
break;
|
|
}
|
|
#endif BOOTBLOCK
|
|
|
|
/*
|
|
* Check if it's a disk. Note, if inquiry fails, we presume
|
|
* the user knows what he's doing.
|
|
*/
|
|
sip->si_ma = BUF;
|
|
if (sdcmd(SC_INQUIRY, sip, MSGS_OFF) > 0 &&
|
|
((struct scsi_inquiry_data *)sip->si_ma)->dtype == 1) {
|
|
EPRINTF("sdopen: not a disk\n");
|
|
return (-1);
|
|
}
|
|
/*
|
|
* Read the label
|
|
*/
|
|
label->dkl_magic = 0;
|
|
sip->si_bn = 0; /* Read block #0 */
|
|
sip->si_cc = SECSIZE;
|
|
/*sip->si_ma = BUF;*/
|
|
if (sdcmd(SC_READ, sip, MSGS_ON) <= 0) {
|
|
EPRINTF("sdopen: label read failed\n");
|
|
return (-1);
|
|
}
|
|
|
|
EPRINTF1("sd: <%s>\n", (char *)sip->si_ma);
|
|
if (chklabel(label)) {
|
|
EPRINTF("sdopen: no label\n");
|
|
return (-1);
|
|
}
|
|
|
|
/*
|
|
* Compute starting (abs.) starting block number and partition
|
|
* size (in bytes). Note:
|
|
* si_cyloff = starting block number of partition
|
|
* si_boff = size of partition in bytes
|
|
* FIXME: sd_boff really should be in sip structure. Note
|
|
* being there mans that ONLY one connection to sd can be
|
|
* used at a time (which is all we really do at the present).
|
|
*/
|
|
sip->si_cyloff = (u_short)(label->dkl_map[sip->si_boff].dkl_cylno)
|
|
* (u_short)(label->dkl_nhead * label->dkl_nsect);
|
|
sdp->sd_boff =
|
|
(u_short) label->dkl_map[sip->si_boff].dkl_nblk * SECSIZE;
|
|
if (sdp->sd_boff == 0) {
|
|
EPRINTF("sdopen: zero size partition\n");
|
|
return (-1);
|
|
}
|
|
sc_reset = 0;
|
|
return (0);
|
|
}
|
|
|
|
|
|
/*
|
|
* Close the SCSI Disk controller.
|
|
*/
|
|
sdclose(sip)
|
|
register struct saioreq *sip;
|
|
{
|
|
EPRINTF("sdclose:\n");
|
|
#ifdef lint
|
|
sip = sip;
|
|
#endif lint
|
|
|
|
sc_reset = 0;
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
* Execute reads or writes for the outside world.
|
|
*/
|
|
sdstrategy(sip, rw)
|
|
struct saioreq *sip;
|
|
register int rw;
|
|
{
|
|
register struct sdparam *sdp;
|
|
DPRINTF2("sdstrategy: addr= 0x%x size= 0x%x\n",
|
|
(int)(sip->si_ma), sip->si_cc);
|
|
|
|
sc_reset = 1;
|
|
sdp = (struct sdparam *)sip->si_devdata;
|
|
if (sdp) {
|
|
if (sip->si_cc > sdp->sd_boff) {
|
|
EPRINTF("sdstrategy: request too big\n");
|
|
sip->si_cc = sdp->sd_boff;
|
|
}
|
|
}
|
|
if (sip->si_cc <= 0) {
|
|
EPRINTF("sdstrategy: request too small\n");
|
|
return(0);
|
|
}
|
|
rw = sdcmd((rw == WRITE ? SC_WRITE : SC_READ), sip, MSGS_ON);
|
|
return (rw < 0 ? 0 : rw);
|
|
}
|
|
|
|
/*
|
|
* Internal interface to the disk command set
|
|
*
|
|
* Returns the character count read (or 1 if count==0) for success,
|
|
* returns 0 for failure, or -1 for severe unretryable failure.
|
|
*/
|
|
static int
|
|
sdcmd(cmd, sip, errprint)
|
|
short cmd;
|
|
register struct saioreq *sip;
|
|
int errprint;
|
|
{
|
|
register struct sdparam *sdp;
|
|
register struct host_saioreq *h_sip; /* Sip for host adapter */
|
|
register char *buf;
|
|
struct scsi_cdb cdb, scdb;
|
|
struct scsi_scb scb, sscb;
|
|
int retry = 0;
|
|
int trycount = 16;
|
|
int r, i, cc, dir;
|
|
int sense_key;
|
|
|
|
DPRINTF("sdcmd:\n");
|
|
sdp = (struct sdparam *)sip->si_devdata;
|
|
h_sip = &sdp->h_sip; /* Host adapter block */
|
|
h_sip->unit = sdp->sd_target;
|
|
cc = ROUNDUP(sip->si_cc);
|
|
buf = BUF;
|
|
|
|
/* Set up cdb */
|
|
bzero((char *) &cdb, CDB_GROUP1);
|
|
bzero((char *) &scb, sizeof scb);
|
|
cdb.cmd = cmd;
|
|
cdb.lun = sdp->sd_unit;
|
|
sip->si_bn += sip->si_cyloff;
|
|
|
|
/* Some fields in the cdb are command specific */
|
|
switch (cmd) {
|
|
|
|
case SC_READ:
|
|
DPRINTF("sdcmd: read\n");
|
|
h_sip->dma_dir = SC_RECV_DATA;
|
|
goto SDCMD_RD;
|
|
|
|
case SC_WRITE:
|
|
DPRINTF("sdcmd: write\n");
|
|
h_sip->dma_dir = SC_SEND_DATA;
|
|
if (sip->si_ma < DVMA_BASE) {
|
|
DPRINTF("sdcmd: write bcopy\n");
|
|
bcopy(sip->si_ma, BUF, sip->si_cc);
|
|
}
|
|
|
|
SDCMD_RD:
|
|
if (sip->si_ma >= DVMA_BASE) {
|
|
DPRINTF("sdcmd: DVMA write\n");
|
|
buf = sip->si_ma;
|
|
}
|
|
FORMG0ADDR(&cdb, sip->si_bn);
|
|
FORMG0COUNT(&cdb, (cc / SECSIZE));
|
|
break;
|
|
|
|
case SC_INQUIRY:
|
|
EPRINTF("sdcmd: inquiry\n");
|
|
h_sip->dma_dir = SC_RECV_DATA;
|
|
sip->si_cc = cc = sizeof(struct scsi_inquiry_data);
|
|
FORMG0COUNT(&cdb, cc);
|
|
bzero((char *)h_sip->ma, cc);
|
|
break;
|
|
|
|
case SC_TEST_UNIT_READY:
|
|
DPRINTF("sdcmd: test unit ready\n");
|
|
trycount = 4;
|
|
h_sip->dma_dir = SC_NO_DATA;
|
|
cc = 0;
|
|
break;
|
|
|
|
case SC_START:
|
|
DPRINTF("sdcmd: start\n");
|
|
trycount = 2;
|
|
h_sip->dma_dir = SC_NO_DATA;
|
|
FORMG0COUNT(&cdb, 1);
|
|
break;
|
|
|
|
default:
|
|
if (!errprint)
|
|
SC_ERROR1("sd: unknown command (0x%x)\n", cdb.cmd);
|
|
return (0);
|
|
}
|
|
|
|
/* Run command and process errors. */
|
|
while (retry++ < trycount) {
|
|
h_sip->cc = cc;
|
|
h_sip->ma = buf;
|
|
r = (*h_sip->doit)(&cdb, &scb, h_sip);
|
|
DPRINTF2("sdcmd: r= 0x%x (0x%x)\n", cc, r);
|
|
|
|
/* Major SCSI bus error */
|
|
if (r < 0) {
|
|
EPRINTF("sdcmd: SCSI bus error\n");
|
|
return (r);
|
|
}
|
|
|
|
/* Disk busy - delay 2 Sec. */
|
|
if (scb.busy) {
|
|
EPRINTF("sdcmd: busy\n");
|
|
DELAY(2000000);
|
|
continue;
|
|
}
|
|
|
|
/* Error, get sense data. */
|
|
if (scb.chk) {
|
|
EPRINTF("sdcmd: check condition\n");
|
|
bzero((char *) &scdb, CDB_GROUP0);
|
|
bzero((char *) &sscb, sizeof scb);
|
|
|
|
scdb.cmd = SC_REQUEST_SENSE;
|
|
scdb.lun = sdp->sd_unit;
|
|
h_sip->cc = sizeof(struct scsi_sense);
|
|
FORMG0COUNT(&scdb, h_sip->cc);
|
|
h_sip->ma = STSBUF;
|
|
dir = h_sip->dma_dir;
|
|
h_sip->dma_dir = SC_RECV_DATA;
|
|
|
|
i = (*h_sip->doit)(&scdb, &sscb, h_sip);
|
|
h_sip->dma_dir = dir;
|
|
|
|
sense_key = sd_pr_sense((u_char *)h_sip->ma, i, errprint);
|
|
if (sense_key == SC_RECOVERABLE_ERROR) {
|
|
goto SDCMD_EXIT;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
/* Return count should match */
|
|
if (r < cc) {
|
|
EPRINTF("sd: short transfer");
|
|
if (!errprint)
|
|
SC_ERROR1("sd: short transfer (0x%x)\n", r);
|
|
continue;
|
|
}
|
|
|
|
/* Copy from buffer */
|
|
SDCMD_EXIT:
|
|
if (cmd == SC_READ && sip->si_ma < DVMA_BASE) {
|
|
DPRINTF("sdcmd: read bcopy\n");
|
|
bcopy(BUF, sip->si_ma, sip->si_cc);
|
|
}
|
|
return (r ? r : 1); /* Returns count */
|
|
}
|
|
|
|
if (!errprint)
|
|
SC_ERROR1("sd: %d retries\n", trycount); /* Unlikely */
|
|
return (0);
|
|
}
|
|
|
|
|
|
#ifndef BOOTBLOCK
|
|
static u_char sd_adaptec_keys[] = {
|
|
0, 4, 4, 4, 2, 4, 4, 4, /* 0x00-0x07 */
|
|
4, 4, 4, 4, 4, 4, 4, 4, /* 0x08-0x0f */
|
|
3, 3, 3, 3, 3, 3, 3, 1, /* 0x10-0x17 */
|
|
1, 1, 5, 5, 1, 1, 1, 1, /* 0x18-0x1f */
|
|
5, 5, 5, 5, 5, 5, 5, 5, /* 0x20-0x27 */
|
|
6, 6, 6, 6, 6, 6, 6, 6, /* 0x28-0x30 */
|
|
};
|
|
#define MAX_ADAPTEC_KEYS \
|
|
(sizeof(sd_adaptec_keys))
|
|
#endif BOOTBLOCK
|
|
|
|
|
|
#ifdef SDDEBUG
|
|
static char *sd_key_error_str[] = SENSE_KEY_INFO;
|
|
#define MAX_KEY_ERROR_STR \
|
|
(sizeof(sd_key_error_str)/sizeof(sd_key_error_str[0]))
|
|
#endif SDDEBUG
|
|
|
|
|
|
/*
|
|
* Print out sense info.
|
|
*/
|
|
static int
|
|
sd_pr_sense(cp, len, errprint)
|
|
u_char *cp;
|
|
int len;
|
|
int errprint;
|
|
{
|
|
register struct scsi_sense *ss;
|
|
register struct scsi_ext_sense *ses;
|
|
int sense_key;
|
|
|
|
DPRINTF("sd_pr_sense:\n");
|
|
ses = (struct scsi_ext_sense *) cp;
|
|
ss = (struct scsi_sense *) cp;
|
|
#ifdef lint
|
|
len = len;
|
|
#endif lint
|
|
|
|
#ifdef SDDEBUG
|
|
if (sd_debug > 2) {
|
|
printf("sd: error:");
|
|
while (--len >= 0) {
|
|
printf(" %x", *cp++);
|
|
}
|
|
printf("\n");
|
|
}
|
|
#endif SDDEBUG
|
|
|
|
if (ss->code <= 6) {
|
|
/*
|
|
* Synthesize sense key for extended sense. Note,
|
|
* Without the decode table, we don't know what's
|
|
* so we'll say we don't know.
|
|
*/
|
|
#ifdef BOOTBLOCK
|
|
sense_key = SC_NO_SENSE;
|
|
#else BOOTBLOCK
|
|
if (ss->code < MAX_ADAPTEC_KEYS) {
|
|
sense_key = sd_adaptec_keys[ss->code];
|
|
}
|
|
#endif BOOTBLOCK
|
|
|
|
if (errprint)
|
|
return (sense_key);
|
|
|
|
SC_ERROR1("sd: sense key = %x", sense_key);
|
|
#ifdef SDDEBUG
|
|
SC_ERROR1(" (%s)", sd_key_error_str[ses->key]);
|
|
#endif SDDEBUG
|
|
SC_ERROR1(" error = %x ", ss->code);
|
|
|
|
if (ss->adr_val) {
|
|
SC_ERROR1("- block %d", (ss->high_addr << 16) |
|
|
(ss->mid_addr << 8) | ss->low_addr);
|
|
}
|
|
|
|
|
|
/* Sense class 7: the standardized one */
|
|
} else {
|
|
/*
|
|
* For CCS disks, it tells us the problem.
|
|
*/
|
|
sense_key = ses->key;
|
|
|
|
if (errprint)
|
|
return (sense_key);
|
|
|
|
SC_ERROR1("sd: sense key = %x", ses->key);
|
|
#ifdef SDDEBUG
|
|
SC_ERROR1(" (%s)", sd_key_error_str[ses->key]);
|
|
#endif SDDEBUG
|
|
SC_ERROR1(" error = %x ", ses->error_code);
|
|
|
|
if (ses->adr_val) {
|
|
SC_ERROR1("- block %d", (ses->info_1 << 24) |
|
|
(ses->info_2 << 16) | (ses->info_3 << 8) |
|
|
ses->info_4);
|
|
}
|
|
}
|
|
SC_ERROR("\n");
|
|
return (sense_key);
|
|
}
|