1
0
Files
irix-657m-src/eoe/cmd/fx/io.c
2022-09-29 17:59:04 +03:00

533 lines
12 KiB
C

#include "fx.h"
#include <sys/stat.h>
#include <sys/major.h>
#include <sys/sysmacros.h>
/*
* i/o routines.
* these implement software controlled retries,
* and device addressing by driveno instead of fd.
* THIS ASSUMES THAT FX DEALS WITH AT MOST ONE CTLR AT A TIME!
*/
#ifdef _STANDALONE
#include <saioctl.h>
#endif
int ioretries = 3; /* error retries */
int fds[MAX_DRIVE]; /* fd's, per drive */
/* controller & drive info: set by fx_find_ctlr() which looks at
* system hardware. */
#ifdef SCSI_NAME
char scsi_name[] = SCSI_NAME;
#endif
#ifdef SMFD_NAME
char smfd_name[] = SMFD_NAME;
#endif
/* no prototypes in headers */
#ifndef ARCS_SA
extern int open(char *, int modes);
extern int readb(int, void *, daddr_t, int);
extern int writeb(int, void *, daddr_t, int);
#endif
extern int nomenus; /* see fx.c, -c option */
extern char *script; /* see fx.c, -s option */
/* fx_find_ctlr(). */
/* In standalone mode, this makes probe calls to the various drivers */
/* to try to find out what type of disk controller is actually here. */
/* In runtime mode, we will default to the kind of controller being */
/* used for root. We do a 'devnm' on root, and look at the resulting */
/* string. */
void
fx_find_ctlr(void)
{
#ifdef _STANDALONE
/* coded this way so it doesn't matter which controllers are actually
included. */
int found = 0;
scsilun = 0;
if(!found) { /* assume it must be SCSI; NOTE: this should deal
with the second scsi chip some day... */
driver = scsi_name;
drivernum = SCSI_NUMBER;
ctlrno = 0;
driveno = 1;
}
#else /* runtime. If search for root fails, default to dkip, drive 1 */
/* (Shouldn't happen of course, still, this is only a default prompt */
if (rt_cfind() != 0){
driver = scsi_name;
drivernum = SCSI_NUMBER;
ctlrno = 0;
driveno = 1;
printf("Unable to determine drive type, defaulting to SCSI, ctrlr %d, drive %d\n",
ctlrno, driveno);
}
#endif
}
/*
* quick controller check
* NOTE: we can't start diagnostics at runtime, so in fact all the
* ioctl does now is is look at the 'board ok' bit. The diag_info
* is not significant.... Dave H 3/28/88.
* NOTE: none of the drivers use the bcnt fields at this time;
* if any of them ever take it to be the actual block size, this
* may need to be modified, since the actual block size may not
* be known at this point.
* do not use scerrwarn for failures, as we do not want to exit on this
* error when -c INITIALIZE or similar calls are used.
*/
void
ctlrcheck(void)
{
printf("...drive selftest...");
flushoutp();
if(gioctl(DIOCTEST, 0) < 0 )
err_fmt("FAILED, may have further problems");
else
printf("OK\n");
}
/*
* get volume header from driver
* do not use scerrwarn for failures, as we do not want to exit on this
* error when -c INITIALIZE or similar calls are used.
*/
void
get_vh(struct volume_header *vp)
{
if( gioctl(DIOCGETVH, vp) < 0 )
err_fmt("no volume header available from driver");
}
/*
* set volume header in driver
*/
int
set_vh(struct volume_header *vp)
{
int error;
vp->vh_magic = VHMAGIC;
vp->vh_csum = 0;
vp->vh_csum = -vhchksum((int *)vp, sizeof *vp);
if ( gioctl(DIOCSETVH, vp) < 0 ) {
error = oserror(); /* Keep a copy for later */
if (error == EBUSY) {
/*
* An EBUSY can only happen on non-miniroot kernels.
* We're either interactive, command, or script driven.
* If we're not miniroot, and are command or script
* driven, we should terminate.
*/
scerrwarn(
"\n\tBy re-partitioning you are attempting to delete a\n"
"\tpartition in use by a mounted file system!\n\n");
if (nomenus || script)
exit(1);
if ( yesno(-1,
"Continue only if you are very certain about this action.\n"
"A kernel panic could occur if you proceed, and then attempt\n"
"to unmount the affected file system.\n"
"Continue by forcing the driver update" )) {
(void)gioctl(DIOCSETVH | DIOCFORCE, vp);
return 0;
}
setoserror(error); /* Put it back */
return error;
} else {
scerrwarn("Driver update failed!\n");
return error;
}
}
return 0;
}
/* get drive type */
void
get_drivetype(int *_n)
{
int n;
if( gioctl(DIOCDRIVETYPE, &n) < 0 )
{
scerrwarn("can't get drivetype");
*_n = -1;
return;
}
*_n = n;
}
#ifndef _STANDALONE
/*
* runtime version diskfinder. We attempt to default to the controller
* running the root disk, but for security the unit number will be set
* to the back drive.
*/
int
rt_cfind(void)
{
FILE *popf;
char pbuf[200];
int length;
struct stat sbuf;
int xlv = 0;
int ret;
if ((ret = stat("/", &sbuf)) != -1) {
if (major(sbuf.st_dev) == XLV_MAJOR) {
xlv = 1;
}
else {
if (dev_to_drivername(sbuf.st_dev, pbuf, &length)) {
if (strncmp(pbuf, "xlv", 3) == 0)
xlv = 1;
}
}
}
else {
return -1;
}
if (!xlv) {
dev_to_alias(sbuf.st_dev, pbuf, &length);
if (strncmp(pbuf, "/dev/dsk/", 9))
return -1; /* unexpected format? */
else
strcpy(pbuf, (pbuf + 9)); /* get the distinguishing part of */
}
/* the device name: ips or dks */
#ifdef SCSI_NAME
if (!strncmp(pbuf, "dks", 3) || xlv){
driver = scsi_name;
drivernum = SCSI_NUMBER;
ctlrno = 0;
driveno = 1;
return 0;
}
#endif
#ifdef SMFD_NAME
if (!strncmp(pbuf, "fd", 3)){
driver = smfd_name;
drivernum = SMFD_NUMBER;
ctlrno = 0;
driveno = 1;
return 0;
}
#endif
return -1;
}
#endif
/*
* open the device file associated with the given
* driveno and parition. driver and ctlrno are globals.
*/
int
gopen(uint d, uint p)
{
STRBUF s;
int fd;
*s.c = 0;
#ifdef ARCS_SA
if (!strcmp(driver, SCSI_NAME))
sprintf(s.c, "scsi(%d)disk(%d)part(%d)", ctlrno, d, p);
#elif _STANDALONE /* 'old' standalone */
sprintf(s.c, "%s(%d,%d,%d)", driver, ctlrno, d, p);
#else
#ifdef SCSI_NAME
if (device_name_specified)
strcpy(s.c, device_name);
else if(!strcmp(driver, SCSI_NAME)) {
if (scsilun > 0)
{
if (p == PTNUM_VOLUME)
sprintf(s.c, "/dev/rdsk/dks%ud%ul%dvol",
ctlrno, d, scsilun);
else
sprintf(s.c, "/dev/rdsk/dks%ud%ul%ds%u",
ctlrno, d, scsilun, p);
}
else
{
if (p == PTNUM_VOLUME)
sprintf(s.c, "/dev/rdsk/dks%ud%uvol", ctlrno, d);
else
sprintf(s.c, "/dev/rdsk/dks%ud%us%u", ctlrno, d, p);
}
}
#endif
#ifdef SMFD_NAME
if(!strcmp(driver, SMFD_NAME)) {
char *type = smfd_partname(p);
if(!type)
return -1;
sprintf(s.c, "/dev/rdsk/fds%ud%u.%s", ctlrno, d, type);
}
#endif /* SMFD_NAME */
#endif /* _STANDALONE */
if(!s.c) {
printf("unknown driver type \"%s\"\n", driver);
return -1;
}
printf("...opening %s\n", dksub());
fds[d] = fd = (int)open(s.c, 2);
if(fd == -1 && errno == EACCES && (fds[d] = fd = (int)open(s.c, 0)) != -1) {
printf("Notice: read only device\n");
}
if((int)fd != -1) {
fds[d] = (int)fd;
#ifndef _STANDALONE
if(chkmounts(s.c)) {
static char msg[] = "this disk appears to have mounted filesystems.\n"
"\t Don't do anything destructive, unless you are sure that nothing\n"
"\t you are changing will affect the parts of the disk in use.\n\n";
if(nomenus && !script) { /* make it fatal */
errno = 0;
scerrwarn(msg);
}
else
errwarn(msg);
}
if(nomenus)
sleep(10); /* give them a bit of a chance to abort */
#endif
}
else { /* different than what we told them we were opening, so
* give them a hint */
static char msg[] = "Failed to open %s";
if(nomenus) /* make it fatal */
scerrwarn(msg, s.c);
else
errwarn(msg, s.c);
}
return fd;
}
#ifndef ARCS_SA /* arcs versions are different; in arcs.c */
/*
* read from disk at block bn, with retries
*
* For SCSI, don't retry on multiple block reads or report errors
* or add them to the error log if we are exercising the
* disk AND this is a multiple block read. The exercisor
* will retry each block separately when it gets errors
* on multiple block reads. Otherwise we confuse the
* user by reporting errors as the first block of the read, with
* some drives and firmware revs.
*/
int
gread(daddr_t bn, void *buf, uint count)
{
int i, cnt, syscnt, doreadb;
daddr_t blk;
int secsz = DP(&vh)->dp_secbytes;
/* have to avoid overflow on count, so handle sector
* sizes that are not a multiple of BBSIZE by doing
* old style reads, but only if within range.
* This is primarily to handle things like floppies, and
* perhaps some raid disks, which might have (individual) disks
* with a logical block size < BBSIZE.
*/
if(secsz%BBSIZE) {
if((0x80000000u/secsz) <= bn) {
errno = 0; /* prevent bogus error message */
scerrwarn("sector size not a multiple of %d and block #%d too"
" large for read system call\n",
BBSIZE, bn);
return -1;
}
syscnt = count * secsz;
blk = bn * secsz;
doreadb = 0;
}
else {
blk = bn * (secsz/BBSIZE);
syscnt = count * (secsz/BBSIZE);
doreadb = 1;
}
for(i = 0; i <= ioretries; i++) {
if(i)
printf("retry #%d\n", i+1);
if(doreadb)
cnt = readb(fds[driveno], buf, blk, syscnt);
else {
if(lseek(fds[driveno], (long)blk, 0) == -1) {
/* should have some way to suppress trying to add
* a bad block for this one, but with the check above
* for out of range, this should be quite rare... */
scerrwarn("Seek to block #%d fails\n", blk);
return -1;
}
cnt = read(fds[driveno], buf, syscnt);
}
if(cnt == syscnt)
return 0;
if(cnt == -1) {
if(count == 1)
scerrwarn("on read at block %u for 1 block", bn);
else
scerrwarn("on read starting at block %u for %d blocks", bn, count);
adderr(0, bn, "sr");
}
else {
const char *s =
"Tried to read %d blocks, only read %d at %u\n";
if(!doreadb)
cnt /= secsz;
printf(s, count, cnt, blk);
logmsg((char *)s, count, cnt, blk);
flushoutp();
}
}
adderr(0, bn, "hr");
return -1;
}
/*
* write to disk at block bn, with retries. See comments at gread() above
*/
int
gwrite(daddr_t bn, void *buf, uint count)
{
int i, cnt, syscnt, dowriteb;
daddr_t blk;
int secsz = DP(&vh)->dp_secbytes;
/* have to avoid overflow on count, so handle sector
* sizes that are not a multiple of BBSIZE by doing
* old style writes, but only if within range.
* This is primarily to handle things like floppies, and
* perhaps some raid disks, which might have (individual) disks
* with a logical block size < BBSIZE.
*/
if(secsz%BBSIZE) {
if((0x80000000u/secsz) <= bn) {
errno = 0; /* prevent bogus error message */
scerrwarn("sector size not a multiple of %d and block #%d too"
" large for write system call\n",
BBSIZE, bn);
return -1;
}
syscnt = count * secsz;
blk = bn * secsz;
dowriteb = 0;
}
else {
blk = bn * (secsz/BBSIZE);
syscnt = count * (secsz/BBSIZE);
dowriteb = 1;
}
for(i = 0; i <= ioretries; i++) {
if(i)
printf("retry #%d\n", i+1);
if(dowriteb)
cnt = writeb(fds[driveno], buf, blk, syscnt);
else {
if(lseek(fds[driveno], (long)blk, 0) == -1) {
/* should have some way to suppress trying to add
* a bad block for this one, but with the check above
* for out of range, this should be quite rare... */
scerrwarn("Seek to block #%d fails\n", blk);
return -1;
}
cnt = write(fds[driveno], buf, syscnt);
}
if(cnt == syscnt)
return 0;
if(cnt == -1) {
if(errno == EROFS) { /* no point in retrying! */
/* print message once here, instead of each caller */
printf("drive appears to be write protected\n");
break;
}
if(count == 1)
scerrwarn("on write at block %u for 1 block", bn);
else
scerrwarn("on write starting at block %u for %d blocks", bn, count);
adderr(0, bn, "sr");
}
else {
const char *s =
"Tried to write %d blocks, only wrote %d at %u\n";
if(!dowriteb)
cnt /= secsz;
printf(s, count, cnt, blk);
logmsg((char *)s, count, cnt, blk);
flushoutp();
}
}
adderr(0, bn, "hr");
return -1;
}
/*
* ioctl on the disk, with error reporting
*/
int
gioctl(int cmd, void *ptr)
{
errno = 0; /* for scerrwarn and err_fmt calls from caller */
return ioctl(fds[driveno], cmd, ptr);
}
/*
* close all open disks
*/
void
gclose(void)
{
register int i;
for( i = 0; i < MAX_DRIVE; i++ )
if( fds[i] > 0 )
close(fds[i]);
}
#endif /* ARCS_SA arcs versions are different; in arcs.c */