From d75c851d9342689d031fbdbf2043f56c74807532 Mon Sep 17 00:00:00 2001 From: Keith Bostic Date: Mon, 28 Dec 1987 22:01:57 -0800 Subject: [PATCH] original Harris source code SCCS-vsn: sys/tahoe/vba/hd.c 7.1 --- usr/src/sys/tahoe/vba/hd.c | 1500 ++++++++++++++++++++++++++++++++++++ 1 file changed, 1500 insertions(+) create mode 100644 usr/src/sys/tahoe/vba/hd.c diff --git a/usr/src/sys/tahoe/vba/hd.c b/usr/src/sys/tahoe/vba/hd.c new file mode 100644 index 0000000000..a22ba0957a --- /dev/null +++ b/usr/src/sys/tahoe/vba/hd.c @@ -0,0 +1,1500 @@ +/* + * Driver for HCX Disk Controller (HDC) + * + * @(#)hd.c 7.1 (Berkeley) %G% + */ + +#include +#include +#include "../sys/param.h" +#include "../sys/buf.h" +#include "../sys/conf.h" +#include "../sys/dir.h" +#include "../sys/dk.h" +#include "../ml/mtpr.h" +#include "../sys/systm.h" +#include "../sys/vbavar.h" +#include "../sys/user.h" +#include "../sys/vmmac.h" +#include "../sys/uio.h" +#include "../sys/elog.h" +#include "../sys/iobuf.h" +#include "../sys/kernel.h" +#include "../sys/reboot.h" +#include "../sys/ioctl.h" +#define DSKGENDATA +#include "../sys/dsk.h" +#undef DSKGENDATA +#include "../sys/dskio.h" +#include "../sys/hdc.h" +#include "../sys/proc.h" + +/* + * External data. + */ + +extern unsigned int blkacty; /* for error logging */ +extern hdc_ctlr_type hdc_ctlrs[]; /* hdc controller info */ +extern hdc_unit_type hdc_units[]; /* hdc unit info */ +extern struct vba_ctlr *hdminfo[]; /* vba controller info */ +extern struct vba_device *vddinfo[]; /* vba device info */ +extern struct iotime vdstat[]; /* for disk activity info */ +extern struct iobuf vdtab[]; /* for disk activity info */ +extern int maxfree; /* no. of blocks for dump */ + +/* + * Procedure forward references. + */ + +int hdprobe(); +int hdslave(); +int hdstrategy(); +int hdattach(); + +/* + * Driver structure. + */ + +struct vba_driver hddriver = { + hdprobe, /* handler probe routine */ + hdslave, /* handler slave routine */ + hdattach, /* handler attach routine */ + 0, /* handler go routine */ + 0, /* */ + "dsk", /* name of the device */ + vddinfo, /* table of unit info */ + "HDC Controller #", /* name of the controller */ + hdminfo, /* table of ctlr info */ + HDC_MID, /* controller's module id */ + 0 /* no exclusive use of bdp's */ +}; + +#ifdef HDCLOG +/************************************************************************* +* Procedure: hdlog +* +* Description: logs mcb's, master mcb's, etc. +* +* Returns: +**************************************************************************/ + +#define ENT_SIZE 16 +#define ENT_COUNT 256 +static int hdclog_index = 0; +static unsigned int hdclog[ ENT_SIZE * ENT_COUNT ]; + +hdlog(ptr,id) +register unsigned int *ptr; +register unsigned int id; +{ + int i; + + hdclog[hdclog_index++] = id; + hdclog[hdclog_index++] = time.tv_sec; + hdclog[hdclog_index++] = time.tv_usec; + for (i=3; i= ENT_SIZE * ENT_COUNT) hdclog_index=0; +} +#endif + +/************************************************************************* +* Procedure: hdattach +* +* Description: "hdattach" does device-dependent initialization of +* hdc drives. It is called during the configuration phase +* of a reboot for each disk device on an hdc controller. +* Note that most things get initialized in "hdslave", +* because "slave" initializes what it needs to determine +* whether the drive is ready (which turns out to be a lot). +* +* Returns: +**************************************************************************/ + +hdattach(vba_unit) + +register struct vba_device *vba_unit; /* Pointer to vba drive info + */ +{ + register hdc_unit_type *hu; /* hdc unit info + */ + register int unit; /* drive's unit# (0-31) + */ + unit = vba_unit->ui_unit; + hu = &hdc_units[ unit ]; + + /* + * Initialize the hdc unit information structure. + * A lot of this is done in "hdslave". + */ + + hu->spc = hu->heads * hu->sectors; + + /* + * bytes per second: + * (number of sectors per track) * (bytes per sector) * rpm / 60 + */ + + dk_bps[unit] = hu->sectors * BPS * hu->rpm / 60; +} + +/************************************************************************* +* Procedure: hddump +* +* Description: Dump system memory to disk. The hdc controller is reset. +* After this call, queued operations on this hdc are no +* longer possible until the next reboot. +* +* Returns: ENXIO the dump was truncated for some reason. +* EIO there were controller problems +* 0 normal +**************************************************************************/ + +int +hddump(dev) + +int dev; /* the major/minor device number. + */ +{ + register hdc_unit_type *hu; /* hdc unit info */ + register hdc_ctlr_type *hc; /* hdc controller info */ + register mcb_type *mcb; /* hdc controller info */ + register int current_block; /* next disk block to write */ + register int block_count; /* #blocks to dump total */ + register int blocks; /* #blocks to dump at a time*/ + register int mem_addr; /* memory address to dump */ + int sector; /* sector to write to */ + int par; /* disk partition number */ + int parlen; /* disk partition # blocks */ + int dump_short; /* TRUE= dump was truncated */ + int chn; /* temporary data chain no. */ + int bc; /* temporary byte count */ + + + mem_addr = 0; + dump_short = FALSE; + par = HDC_PARTITION(dev); + hu = &hdc_units[ HDC_UNIT(dev) ]; + hc = &hdc_ctlrs[hu->ctlr]; + mcb = &hu->phio_mcb; + parlen = hu->partition[par].length; + printf("\nhdc: resetting controller #%d.\n", hc->ctlr); + HDC_REGISTER(soft_reset_reg) = 0; + DELAY(1000000); + mtpr(0,PADC); + + /* + * If the drive has not been initialized yet, abort the dump. + * Set dump limits. The dump must fit in the partition. + */ + + if (hu->sectors <= 0 || hu->heads <= 0 || hu->cylinders <= 0 ) { + printf("\nhdc: dump device is not initialized - no dump!\n"); + return EIO; + } + block_count = dumpsize; + if ((dumplo + block_count) > parlen) { + block_count = parlen - dumplo; + dumpsize = block_count; /* let savecore know */ + printf("\nhdc: only dumping first %dmb of memory!\n", + block_count/1024); + dump_short = TRUE; + } + current_block = hu->partition[par].start + dumplo; + + /* + * Dump memory to disk. For each disk transfer, fill in the + * mcb with information describing the transfer, then send + * the mcb to the hdc controller. + */ + + while (block_count > 0) { + blocks = MIN(block_count, HDC_DUMPSIZE); + sector = HDC_SPB * current_block; + mcb->command = HCMD_WRITE; + mcb->cyl = sector/hu->spc; + mcb->head = (sector/hu->sectors) % hu->heads; + mcb->sector = sector % hu->sectors; + chn = 0; + bc = blocks * DEV_BSIZE; + while (bc > 0) { + mcb->chain[chn].ta = mem_addr; + mcb->chain[chn].lwc = (bc > HDC_MAXBC) ? + (LWC_DATA_CHAIN | (HDC_MAXBC/4)) : bc/4; + mem_addr += ((bc > HDC_MAXBC) ? HDC_MAXBC : bc); + chn++; + bc -= HDC_MAXBC; + } + if (!hdimcb(hu,mcb)) + return EIO; + block_count -= blocks; + current_block += blocks; + } + return (dump_short ? ENXIO : 0); +} + +/************************************************************************* +* Procedure: hddumpmcb +* +* Description: Dumps a single mcb to the console - up to the last +* active data chain lword. +* +* Returns: +**************************************************************************/ + +hddumpmcb(mcb) + +register mcb_type *mcb; /* the mcb pointer + */ +{ + unsigned int *ptr,i; + + printf("mcb: "); + ptr = (unsigned int *) &mcb->forw_phaddr; + for (i=0; i<6; i++) + printf(" %x",ptr[i]); + for (i=6; i<72; i+=2) { + printf(" %x %x", ptr[i], ptr[i+1]); + if ( !(ptr[i] & 0x80000000)) break; + } + printf("\n"); +} + +/************************************************************************* +* Procedure: hddumpmmcb +* +* Description: dumps the master mcb on the console up to the +* last non-zero byte of the extended status. +* +* Returns: +**************************************************************************/ + +hddumpmmcb(master) + +register master_mcb_type *master; /* the master mcb pointer + */ +{ + unsigned int *ptr,i,end; + + printf("mmcb: "); + ptr = (unsigned int *) master; + for (i=0;i<8;i++) + printf("%x ",ptr[i]); + for (i=7+HDC_XSTAT_SIZE; i>7; i--) { + end = i; + if (ptr[i] != 0) break; + } + for (i=8;i<=end;i++) + printf(" %x",ptr[i]); + printf("\n"); +}; + +/************************************************************************* +* Procedure: hdimcb +* +* Description: "hdc immediate mcb" sends an mcb to the hdc and returns +* when the hdc has completed the operation (polled io). +* "hdimcb" is called during system configuration or +* when the system is being dumped after a fatal error. +* +* Entry: o There is no active process. +* +* o "hdimcb" cannot be called from interrupt level. +* +* o There can be no queued operations pending; i.e. +* this routine assumes exclusive use of the hdc. +* Note: a soft reset will terminate queued operations. +* +* Returns: Returns FALSE if a controller error occurred. +**************************************************************************/ + +int +hdimcb(hu,mcb) + +register hdc_unit_type *hu; /* unit information + */ +register mcb_type *mcb; /* mcb to send to the hdc + */ +{ + register hdc_ctlr_type *hc; /* controller information */ + register master_mcb_type *master; /* the hdc's master mcb */ + register int timeout; /* used to timeout the mcb */ + register int ctlr; /* controller number */ + int i,ok; + unsigned int *ptr; + + + ok = TRUE; + ctlr = hu->ctlr; + hc = &hdc_ctlrs[ctlr]; + master = &hc->master_mcb; + + /* + * Complete the setup of the mcb and master mcb. + */ + + mcb->priority = 0; + mcb->interrupt = FALSE; + mcb->drive = hu->slave; + mcb->forw_phaddr= 0; + mcb->context = 0; + mcb->reserved[0]= 0; + mcb->reserved[1]= 0; + master->forw_phaddr = (long) vtoph(0,&mcb->forw_phaddr); + master->mcs = 0; + master->reserve1 = 0; + master->reserve2 = 0; + master->context = 0; + master->cmcb_phaddr = 0; + master->mcl = MCL_IMMEDIATE; + bzero( (caddr_t)&master->xstatus[0], HDC_XSTAT_SIZE ); + + /* + * Tell hdc to xqt the mcb; wait for completion. + * If a controller error or timeout occurs, print + * out the mcb and master mcb on the console. + */ + + HDC_REGISTER(master_mcb_reg) = hc->master_phaddr; + timeout = 15000; + while (TRUE) { + DELAY(1000); + mtpr(0,PADC); + if ( (master->mcs & MCS_DONE) && + !(master->mcs & MCS_FATALERROR ) ) break; + timeout--; + if ( timeout > 0 && + !(master->mcs & MCS_FATALERROR) ) continue; + if ( master->mcs & MCS_FATALERROR ) + printf("hdc: controller %d fatal error\n",ctlr); + else + printf("hdc: controller %d timed out\n",ctlr); + hddumpmcb(mcb); + hddumpmmcb(master); + ok = FALSE; + break; + } + master->mcl = MCL_QUEUED; + return(ok); +} + +/************************************************************************* +* Procedure: hdintr +* +* Description: The hdc interrupt routine. +* +* Returns: +**************************************************************************/ + +hdintr(ctlr) + +int ctlr; /* the hdc controller number. + */ +{ + register master_mcb_type *master; /* master mcb for this hdc */ + register mcb_type *mcb; /* the mcb just completed */ + register struct buf *bp; /* buf for the completed mcb*/ + register hdc_ctlr_type *hc; /* info for this controller */ + register struct iobuf *iobp; /* iobuf for this unit */ + register int unit; /* unit# of the hdc drive */ + register int i; /* temporary */ + + + hc = &hdc_ctlrs[ctlr]; + master = &hc->master_mcb; + uncache( &master->mcs ); + uncache( &master->context ); +#ifdef HDCLOG + hdlog(master,1 + 16*hc->ctlr); +#endif + if ( !(master->mcs & MCS_DONE) ) { + printf("\nhdc: spurious interrupt from controller #%d\n",ctlr); + return; + } + mcb = (mcb_type *) master->context; + bp = mcb->buf_ptr; + unit = HDC_UNIT(bp->b_dev); + iobp = &vdtab[unit]; + + /* + * Error log and system activity. + * + * Turn off the activity bit for this device. + * Record the time required to process the buf. + * If there is no more activity on this unit, record the + * amount of time that the unit was active. + * Update dkprf and lastcyl for "sadp". + */ + + blkacty &= ~(1 << major(bp->b_dev)); + if (iobp->b_active) { + vdstat[unit].io_resp += (time.tv_sec - bp->b_start); + if (--iobp->b_active == 0) + vdstat[unit].io_act += (time.tv_sec - iobp->io_start); + } + i = mcb->cyl; + dkprf[unit][i >> 3]++; + i -= lastcyl[unit]; + if (i < 0) i = -i; + skprf[unit][i >> 3]++; + lastcyl[unit] = mcb->cyl; + dk_busy &= ~(1 << unit); + dk_seek[unit]++; + dk_xfer[unit]++; + + /* + * If there are no free mcb's, wake up anyone that might + * be waiting for one. Remove the completed mcb from the + * queue of active mcb's and add it to the free-mcb queue. + */ + + if (hc->forw_free == (mcb_type *)&hc->forw_free) + wakeup(hc); + remque(mcb); + insque(mcb,&hc->forw_free); + + /* + * If there was a fatal error, dump the mcb and master mcb on the + * console, then halt if the system was booted with the debug option. + * + * Record fatal and soft errors in the error log. + */ + + bp->b_resid = 0; + if (master->mcs & (MCS_SOFTERROR | MCS_FATALERROR) ) { + mtpr( (caddr_t) master, P1DC ); + mtpr( (caddr_t) &master->xstatus[HDC_XSTAT_SIZE]-1, P1DC ); + if (master->mcs & MCS_FATALERROR) { + bp->b_flags |= B_ERROR; + bp->b_error = EIO; + harderr(bp,"hdc"); + printf("\nhdc: fatal error on controller #%d\n",ctlr); + hddumpmmcb(master); + hddumpmcb(mcb); + if (boothowto & RB_DEBUG) asm("halt"); + }; + vdstat[unit].ios.io_misc++ ; + iobp->io_erec = 0; + iobp->io_addr = (caddr_t) hc->registers; + iobp->io_stp = &vdstat[unit].ios; + iobp->io_nreg = HDC_XSTAT_SIZE; + for (i=HDC_XSTAT_SIZE-1; i>0; i--) { + if (master->xstatus[i] != 0) break; + iobp->io_nreg--; + } + iobp->b_actf = bp; + iobp->b_dev = bp->b_dev; + fmtberr( iobp, mcb->cyl, &master->xstatus[0] ); + logberr(iobp, master->mcs & MCS_FATALERROR); + bzero( (caddr_t)&master->xstatus[0], HDC_XSTAT_SIZE ); + } + + /* + * If there are any waiting mcb's, move them to the active queue. + * Physically link the new mcb's from the master mcb. + */ + + master->forw_phaddr = 0; +next: mcb = hc->forw_wait; + remque(mcb); + asm(" bvs done"); + insque(mcb,&hc->forw_active); + mcb->forw_phaddr = master->forw_phaddr; +#ifdef HDCLOG + hdlog(mcb,2 + 16*hc->ctlr); +#endif + master->forw_phaddr = mcb->mcb_phaddr; + goto next; +done: asm("done:"); + + /* + * If there are any mcb's active, initialize the master mcb + * and tell the hdc to continue queued operation. + * New mcb's (if any) are linked off of "forw_phaddr". + */ + + if (hc->forw_active != (mcb_type *) &hc->forw_active) { + master->mcs = 0; +#ifdef HDCLOG + hdlog(master,3 + 16*hc->ctlr); +#endif + HDC_REGISTER(master_mcb_reg)= hc->master_phaddr; + } + + /* + * Return the buf for the completed operation. + */ + + iodone(bp); + return; +} + +/************************************************************************* +* Procedure: hdioctl +* +* Description: Character device ioctl routine. +* +* Returns: EACCES formatting is active on the drive +* (or) function is valid only for the format program +* (or) formatting ioctl's must be done on partition 7 +* EIO controller error occurred +* ENXIO invalid parameter value +* 0 normal +**************************************************************************/ + +int +hdioctl(dev, command, arg, flag) + +dev_t dev ; /* Device type. Major/minor dev#. + */ +int command ; /* The ioctl commmand. + */ +int *arg ; /* Data. Format depends on ioctl. + */ +int flag ; /* Not used. + */ +{ + register hdc_unit_type *hu; /* unit information */ + int formatok; /* TRUE= it's ok to format */ + register int i; + + hu = &hdc_units[ HDC_UNIT(dev) ]; + formatok = ( HDC_PARTITION(dev)==7 && hu->format ); + switch (command) { + + case DSKIOCFORMAT: { + + /* + * Format a disk track. The received argument is a pointer + * to a "formatop" structure describing the track to format. + * + * Set up a buffer with each longword corresponding to a + * sector on the track; a 1 means no flaw, a 0 means a flaw. + * Call hdphysio to send the data from the phio_data buffer + * to the hdc to format the track. + */ + + register struct formatop *track; + + if (!formatok) return EACCES; + track = (struct formatop *) arg; + for (i=0; iphys_sectors; i++) + hu->phio_data[i] = 1; + for (i=0; iflaw_count; i++) + hu->phio_data[track->flaw[i]] = 0; + if (!hdphysio( + dev, + HCMD_FORMAT, + track->cylinder, + track->head, + 0, + hu->phio_data, + hu->phys_sectors * 4) ) + return EIO; + break; + } + + case DSKIOCCERTIFY: { + + /* + * Certify a disk track. The received argument is a pointer + * to a "formatop" structure describing the track to certify. + * + * Call hdphysio to read data into the phio_data buffer. + * The controller returns data in which each longword + * corresponds to a sector on the track; a 1 means no flaw, + * a 0 means a flaw. + */ + + register struct formatop *track; + + if (!formatok) return EACCES; + track = (struct formatop *) arg; + if (!hdphysio( + dev, + HCMD_CERTIFY, + track->cylinder, + track->head, + 0, + hu->phio_data, + hu->phys_sectors * 4) ) + return EIO; + track->flaw_count = 0; + for (i=0; iphys_sectors; i++) { + if (track->flaw_count >= MAXVFLAW) break; + if (hu->phio_data[i]==0) { + track->flaw[track->flaw_count] = i; + track->flaw_count++; + } + } + break; + } + + case DSKIOCVERIFY: { + + /* + * Verify a disk track. The received argument is a pointer + * to a "formatop" structure describing the track to verify. + */ + + register struct formatop *track; + + if (!formatok) return EACCES; + track = (struct formatop *) arg; + if (!hdphysio( + dev, + HCMD_VERIFY, + track->cylinder, + track->head, + 0, + 0, + 0) ) + return EIO; + break; + } + + case DSKIOCFORMATCTL: { + + /* + * This ioctl provides special format control. + * + * Currently the valid arguments are: + * arg= 0 disable formatting; + * arg= 1 enable formatting (allow privileged access); + * + * Partition must be the disk definition tracks of + * the raw device. + */ + + if (HDC_PARTITION(dev) != HDC_DEFPART ) + return EACCES; + switch (*arg) { + + case 0: hu->format = FALSE; + break; + + case 1: if (hu->format) + return EACCES; + hu->format = TRUE; + break; + + default: return ENXIO; + } + break; + } + + case DSKIOCGEOMETRY: { + + /* + * Return info about disk geometry (partitions). + * Caller's parameter is a pointer to a geometry + * status structure. + */ + + register geometry_status *geo_status; + + geo_status = (geometry_status *) arg; + for (i=0; ipartition[i].start = hu->partition[i].start; + geo_status->partition[i].length=hu->partition[i].length; + } + break; + } + + case DSKIOCSETGEOMETRY: { + + /* + * Set new geometry - new partition sizes. + * Caller must have formatting privilege. + * Caller's parameter is a pointer to a geometry + * status structure containing the new geometries. + * The disk definition partition cannot be changed. + */ + + register geometry_status *geo_status; + + if (!formatok) return EACCES; + geo_status = (geometry_status *) arg; + for (i=0; ipartition[i].start = geo_status->partition[i].start; + hu->partition[i].length=geo_status->partition[i].length; + } + break; + } + + case DSKIOCSTATUS: { + + /* + * Return info about the disk. Caller's parameter is a + * pointer to a dsk_status structure. + */ + + register dsk_status *status; + + status = (dsk_status *) arg; + status->id = hu->id; + status->rpm = hu->rpm; + status->bytes_per_sec= hu->bytes_per_sec; + status->cylinders = hu->cylinders; + status->heads = hu->heads; + status->sectors = hu->sectors; + status->phys_cylinders= hu->phys_cylinders; + status->phys_heads = hu->phys_heads; + status->phys_sectors = hu->phys_sectors; + status->diag_cyl = hu->diag_cyl; + status->diag_cylinders= hu->diag_cyl_count; + status->def_cyl = hu->def_cyl; + status->def_cylinders = hu->def_cyl_count; + break; + } + + case DSKIOCVENDORFLAW: { + + /* + * Return vendor flaw info. + * + * Read in the vendor data from relative sector 0 of + * the track to the phio_data buffer; then copy the + * vendor flaw data to the caller's buffer. + */ + + register vflaw_type *vflaw; + register struct flaw *vendor; + + if (!formatok) return EACCES; + vflaw = (vflaw_type *) arg; + if (!hdphysio( + dev, + HCMD_VENDOR, + vflaw->cylinder, + vflaw->head, + 0, + hu->phio_buf, + HDC_VDATA_SIZE << 2 )) + return EIO; + vendor = (struct flaw *) &hu->phio_data[0]; + for (i=0; iflaw[i].offset = vendor[i].offset; + vflaw->flaw[i].length = vendor[i].length; + } + break; + } + + default: return ENXIO; + + } + return 0; +} + +/************************************************************************* +* Procedure: hdopen +* +* Description: The character device and block device open routine. +* +* Returns: ENXIO the partition or device isn't defined +* EACCES Formatting is active on this drive +* 0 normal +**************************************************************************/ + +int +hdopen(dev, flag) + +dev_t dev ; /* Device type. Major/minor dev#. + */ +int flag ; /* Not used. + */ +{ + register int unit; /* hdc unit# (0-31)*/ + register int par; /* partition# (0-7) */ + register struct vba_device *vba_unit; /* vba unit info */ + register hdc_unit_type *hu; /* hdc unit info */ + + + unit = HDC_UNIT(dev); + par = HDC_PARTITION(dev); + vba_unit = vddinfo[unit]; + hu = &hdc_units[unit]; + if ( !vba_unit->ui_alive || hu->partition[par].length == 0) + return ENXIO; + if (hu->format) + return EACCES; + vdtab[unit].io_stp = &vdstat[unit].ios; + return 0; +} + +/************************************************************************* +* Procedure: hdphysio +* +* Description: "hdphysio" does the physical i/o initiated by this +* handler. It does the things which "physio" does for +* raw read/writes; i.e. it provides an interface to the +* hdstrategy routine. +* +* hdphysio assumes that it has exclusive access to the +* drive; it uses the drive's phio buf. +* +* Returns: FALSE an i/o error occurred. +* 0 normal; data is in phio_data if read was done +**************************************************************************/ + +int +hdphysio(dev,command,cylinder,head,sector,ta,bc) + +dev_t dev; /* major/minor device number + */ +int command; /* the hdc command to execute + */ +int cylinder; /* disk cylinder address + */ +int head; /* disk head address + */ +int sector; /* disk sector address + */ +int ta; /* memory transfer address + */ +int bc; /* byte count + */ +{ + register struct buf *bp; /* buf structure built here */ + hdc_unit_type *hu; /* hdc device unit info */ + int s; /* processor level save */ + + hu = &hdc_units[ HDC_UNIT(dev) ]; + bp = (struct buf *) &hu->phio_buf; + bp->b_error = 0; + bp->b_proc = u.u_procp; + bp->b_un.b_addr = (caddr_t) ta; + bp->b_flags = B_BUSY | B_PHYS | B_READ | B_LOCALIO; + bp->b_dev = dev; + bp->b_blkno = 0; + bp->b_hdccommand = command; + bp->b_cyl = cylinder; + bp->b_head = head; + bp->b_sector = sector; + bp->b_bcount = bc; + hdstrategy(bp); + s = spl8(); + while ((bp->b_flags & B_DONE) == 0) + slumber((caddr_t)bp, 0, iocomboost); + splx(s); + bp->b_flags &= ~(B_BUSY | B_PHYS | B_WANTED | B_LOCALIO); + if (bp->b_error != 0) + return FALSE; + return TRUE; +} + +/************************************************************************* +* Procedure: hdprobe +* +* Description: "hdprobe" verifies that an hdc controller is really +* there and then initializes the controller. It is called +* during the configuration phase of a reboot for each +* hdc controller in the configuration. +* +* Returns: TRUE means the controller is ready. +**************************************************************************/ + +int +hdprobe(vba_ctlr) + +register struct vba_ctlr *vba_ctlr; /* vba controller information + */ +{ + register hdc_ctlr_type *hc; /* hdc controller info */ + register hdc_mid_type *id; /* returned module id word */ + register int ctlr; /* the controller number */ + register int i; /* temporary */ + mcb_type *mcb; /* temporary mcb pointer */ + extern int Xhdintr0, Xhdintr1, Xhdintr2, Xhdintr3, + Xhdintr4, Xhdintr5, Xhdintr6, Xhdintr7 ; + static int hd_proc[] = { + (int)& Xhdintr0, (int)& Xhdintr1, + (int)& Xhdintr2, (int)& Xhdintr3, + (int)& Xhdintr4, (int)& Xhdintr5, + (int)& Xhdintr6, (int)& Xhdintr7 + } ; + + + ctlr = vba_ctlr->um_ctlr; + hc = &hdc_ctlrs[ctlr]; + /* + * Initialize the hdc controller structure. + * Initially all mcb's are in the free-mcb list. + * The interrupt acknowledge word is the vector offset + * for this controller's interrupts. + */ + + hc->ctlr = ctlr; + hc->registers = (hdc_regs_type *) vba_ctlr->um_addr; + id = &hc->mid; + if (badaddr(&hc->registers->module_id_reg,4,vtoph(0,id))) + return FALSE; + hc->forw_active = (mcb_type *) &hc->forw_active; + hc->back_active = (mcb_type *) &hc->forw_active; + hc->forw_wait = (mcb_type *) &hc->forw_wait; + hc->back_wait = (mcb_type *) &hc->forw_wait; + hc->forw_free = (mcb_type *) &hc->forw_free; + hc->back_free = (mcb_type *) &hc->forw_free; + for (i=HDC_MAXMCBS-1; i>=0; i--) { + mcb = &hc->mcbs[i]; + mcb->mcb_phaddr = vtoph( 0, &mcb->forw_phaddr); + insque( mcb, &hc->forw_free); + } + vba_ctlr -> um_ivct = get_ivct( 0, 1 ) ; + if ( vba_ctlr -> um_ivct == (-1) ) + return FALSE ; + init_ivct( vba_ctlr -> um_ivct, hd_proc[ vba_ctlr -> um_ctlr ] ) ; + hc->master_mcb.interrupt = vba_ctlr -> um_ivct ; + hc->master_phaddr = (u_long) vtoph( 0, &hc->master_mcb) ; + + /* + * Read in the hdc module id word. + */ + + HDC_REGISTER(module_id_reg) = (unsigned long) vtoph(0,id); + DELAY(10000); + mtpr(0,PADC); + + /* + * hdc's are reset and downloaded by the console processor. + * Check the module id; the controller is bad if: + * 1) it is not an hdc; + * 2) the hdc's writeable control store is not loaded; + * 3) the hdc failed the functional integrity test; + */ + + printf("hdc controller %d module id is %x\n", ctlr, *id); + if (id->module_id != (unsigned char) HDC_MID) { + printf("hdc: controller #%d bad module id.\n",ctlr); + return FALSE; + } + if (id->code_rev == (unsigned char) 0xFF ) { + printf("hdc: controller #%d micro-code not loaded.\n",ctlr); + return FALSE; + } + if (id->fit != (unsigned char) 0xFF ) { + printf("hdc: controller #%d FIT test failed.\n",ctlr); + return FALSE; + } + /* + * Reset the hdc in case it still has queued mcb's. + */ + + HDC_REGISTER(soft_reset_reg) = 0; + DELAY(1000000); + return TRUE; +} + +/************************************************************************* +* Procedure: hdread +* +* Description: Character read routine. This procedure is called by the +* inode read/write routine 'ino_rw'. +* +* Returns: Error status returned by 'physio'. +**************************************************************************/ + +int +hdread(dev, uio) + +dev_t dev; /* Device type. Major/minor dev#. + */ +int *uio; /* Pointer to a uio structure describing + * a read request: buffer address; + * sector offset; no. of sectors; etc. + */ +{ + hdc_unit_type *hu; /* hdc unit information */ + + hu = &hdc_units[ HDC_UNIT(dev) ]; + + /* + * 'physio' builds the buf structure, locks the user pages, calls + * 'hdstrategy' to do the read, waits until i/o is complete (iodone), + * then deallocates the buf structure and unlocks the pages. + */ + + return physio( + hdstrategy, /* hdc's strategy routine */ + &hu->raw_buf, /* physio builds a buf struct here */ + dev, /* major/minor device number */ + B_READ, /* read the buffer */ + minphys, /* routine to set max transfer size */ + uio); /* describes the transfer request */ +} + +/************************************************************************* +* Procedure: hdsize +* +* Description: Return the partition size for a specified partition. +* +* Returns: Partition size in blocks. +* -1 means the device isn't there +**************************************************************************/ + +int +hdsize(dev) + +register dev_t dev ; /* Major/minor dev#. + */ +{ + int unit; /* hdc unit# (0-31) */ + int par; /* partition# (0-7) */ + struct vba_device *vba_unit; /* vba unit info */ + hdc_unit_type *hu; /* hdc unit info */ + + unit = HDC_UNIT(dev); + par = HDC_PARTITION(dev); + vba_unit = vddinfo[unit]; + hu = &hdc_units[unit]; + if (vba_unit==0 || !vba_unit->ui_alive) return -1; + return (hu->partition[par].length); +} + +/************************************************************************* +* Procedure: hdslave +* +* Description: "hdslave" verifies that an hdc drive is really there. +* It is called during the configuration phase of a reboot +* for each drive on an hdc. +* +* Note: a lot of device initialization is done here, which +* should normally be done in hdattach; however, it is +* done here since it is info needed to determine whether +* the drive is really there and is functional. +* +* Returns: TRUE means the drive is there. +**************************************************************************/ + +int +hdslave(vba_unit,regs) + +struct vba_device *vba_unit; /* vba drive info + */ +hdc_regs_type *regs; /* hdc io address (not used) + */ +{ + register hdc_ctlr_type *hc; /* hdc ctlr info */ + register hdc_unit_type *hu; /* hdc unit info */ + register mcb_type *mcb; /* mcb to send to the hdc */ + register int unit; /* hdc unit# (0-31) */ + register int ctlr; /* hdc ctlr# (0-15) */ + register int i; /* temp */ + geometry_block *geo; /* ptr to the geometry block*/ + drive_stat_type *drive_status; /* status returned by hdc */ + + ctlr = vba_unit->ui_ctlr; + hc = &hdc_ctlrs[ctlr]; + unit = vba_unit->ui_unit; + hu = &hdc_units[unit]; + mcb = (mcb_type *) &hu->phio_mcb; + + /* + * Initialize things in the hdc unit structure which are used + * by this routine. The rest is initialized by hdattach. + */ + + hu->ctlr = ctlr; + hu->unit = unit; + hu->slave = vba_unit->ui_slave; + + /* + * Read the drive status and keep a permanent copy of the + * info in the hdc unit structure. + */ + + drive_status = (drive_stat_type *) hu->phio_data; + mcb->command = HCMD_STATUS; + mcb->chain[0].lwc = sizeof(drive_stat_type) / 4; + mcb->chain[0].ta = (u_long) vtoph(0,drive_status); + if (!hdimcb(hu,mcb)) + return FALSE; + hu->id = drive_status->id; + hu->cylinders = drive_status->max_cyl+1; + hu->heads = drive_status->max_head+1; + hu->sectors = drive_status->max_sector+1; + hu->phys_cylinders = drive_status->max_phys_cyl+1; + hu->phys_heads = drive_status->max_phys_head+1; + hu->phys_sectors = drive_status->max_phys_sector+1; + hu->def_cyl = drive_status->def_cyl; + hu->def_cyl_count = drive_status->def_cyl_count; + hu->diag_cyl = drive_status->diag_cyl; + hu->diag_cyl_count = drive_status->diag_cyl_count; + hu->bytes_per_sec = drive_status->bytes_per_sec; + hu->rpm = drive_status->rpm; + hu->partition[HDC_DEFPART].start = + hu->def_cyl * hu->sectors * hu->heads / HDC_SPB; + hu->partition[HDC_DEFPART].length = + hu->def_cyl_count * hu->sectors * hu->heads / HDC_SPB; + + /* + * Report the drive down if anything in the drive status + * looks bad. If the drive is offline and it is not on + * cylinder, then the drive is not there. + * If there is a fault condition, the hdc will try to clear + * it when we read the geometry block. + */ + + if (drive_status->drs & DRS_FAULT) + printf("hdc: clearing fault on unit #%d.\n",unit); + if ( !(drive_status->drs & DRS_ONLINE)) { + if ( drive_status->drs & DRS_ON_CYLINDER ) + printf("hdc: unit #%d is not online.\n",unit); + return FALSE; + } + + /* + * Read the geometry block from the start of the drive + * definition cylinder, validate it (must have the correct + * header and checksum), and set partition starts and sizes + * (definition partition has already been set above). + */ + + geo = (geometry_block *) hu->phio_data; + mcb->command = HCMD_READ; + mcb->cyl = hu->def_cyl; + mcb->head = 0; + mcb->sector = 0; + mcb->chain[0].lwc = sizeof(geometry_sector) / 4; + mcb->chain[0].ta = (unsigned long) vtoph(0,geo); + if (!hdimcb(hu,mcb)) + goto badgeo; + if ( geo->version > 64000 || geo->version < 0 ) { + printf("hdc: bad geometry block version# on unit #%d\n",unit); + goto badgeo; + } + if (strcmp(&geo->id[0],GB_ID) != 0) { + printf("hdc: bad geometry block header on unit #%d\n",unit); + goto badgeo; + } + GB_CHECKSUM( geo, i ); + if ( ((geometry_sector *)geo)->checksum != i) { + printf("hdc: bad geometry block checksum on unit #%d\n",unit); + goto badgeo; + } + for (i=0; ipartition[i].start = geo->partition[i].start; + hu->partition[i].length = geo->partition[i].length; + } + return TRUE; + + /* + * If the geometry block is bad, return ok status so that + * the disk can be formatted etc, but zero the partitions + * so that no one except "format" can read/write the disk. + */ + +badgeo: for (i=0; ipartition[i].start = 0; + hu->partition[i].length = 0; + } + return TRUE; +} + +/************************************************************************* +* Procedure: hdstrategy +* +* Description: The hdc strategy routine. It is called by the kernel +* to do a disk operation ('physio' if raw i/o, the block +* i/o routines if block i/o); i.e. this is the point where +* raw i/o and block i/o merge. This routine is also called +* internally by this handler to do misc disk operations. +* +* Returns: +**************************************************************************/ + +hdstrategy(bp) + +register struct buf *bp; /* This buf structure contains info + * describing the requested disk xfer. + */ +{ + register hdc_unit_type *hu; /* hdc device unit info */ + register mcb_type *mcb; /* the mcb built here */ + register int vaddr; /* virtual address of data */ + hdc_ctlr_type *hc; /* hdc controller info */ + int sector; /* absolute sector number */ + int unit; /* minor device unit# */ + int par; /* disk partition number */ + int blocks; /* number of blocks to xfer */ + int priority; /* processor level save */ + int bytes; /* bytecount requested */ + int i; /* temporary */ + + /* + * Initialize pointers and data. + */ + + unit = HDC_UNIT(bp->b_dev); + par = HDC_PARTITION(bp->b_dev); + hu = &hdc_units[unit]; + hc = &hdc_ctlrs[hu->ctlr]; + bytes = bp->b_bcount; + vaddr = (int) bp->b_un.b_addr; + + /* + * Make some preliminary checks of the i/o request. + * Terminate the i/o immediately if: the request is for zero + * bytes or more than 32k bytes; the xfer does not start or + * end on a longword boundary. + * "format" sometimes requires bytes=0; e.g. for verify and + * format ioctls. + */ + + if (bytes==0 || bytes>32*1024) + if (!hu->format) goto enxio; + if ( (bytes&3) || (vaddr&3) ) + goto efault; + + /* + * Round up requested byte count to a multiple of the block size. + * If the transfer would exceed the end of the partition, + * truncate the byte count at the partition boundary (except that + * the format program is allowed to access the entire disk). + * Determine absolute sector number of the start of the transfer + * (requested start plus the start of the partition). + */ + + { + register int par_start; /* partition start blk */ + register int par_length; /* partition blk count */ + + par_start = hu->partition[par].start; + par_length= hu->partition[par].length; + blocks = (bytes + DEV_BSIZE - 1) >> DEV_BSHIFT; + if ( par_length < (bp->b_blkno + blocks) ) + if ( !hu->format) { + blocks = par_length - bp->b_blkno; + if(blocks <= 0) goto enxio; + bytes = blocks * DEV_BSIZE; + } + sector = HDC_SPB * (bp->b_blkno + par_start); + } + + /* + * Insure that nobody except the format program writes to + * the drive definition tracks in partition 7. + * Note: they may access other tracks in partition 7 + * (i.e. diagnostic tracks). + */ + + if (par==HDC_DEFPART) + if (!hu->format && !(bp->b_flags & B_READ)) + { + register int defs; /* definition cyl start */ + register int defe; /* (def cylinder end)+1 */ + + defs = hu->def_cyl * hu->spc; + defe = defs + hu->def_cyl_count * hu->spc; + if (sector < defe && (sector + blocks * HDC_SPB) > defs) + goto eacces; + } + + /* + * Get a free mcb. Wait if no mcb's are available + */ + + priority = spl7(); +get: mcb = hc->forw_free; + remque(mcb); + asm(" bvc got"); + slumber(hc, 0, iocomboost); + goto get; +got: asm("got:"); + splx(priority); + + /* + * Fill in the mcb with information about the xfer. + * + * Currently everything is given equal priority. + * Keep a pointer to the buf associated with the mcb. + * Add virtual address of this mcb to the software context + * word of the mcb; the hdc firmware copies this word to + * the master mcb when the mcb is complete. + * + * If the buf was sent locally by this handler (via 'hdphysio') + * then there may be commands other than just read or write. + * 'hdphysio' also provides a cylinder/head/sector address. + */ + + { + /* + * The following priority calculation is based on the + * real time functional specification. + */ + register struct proc *p = u.u_procp; + mcb->priority = 0; + if ((p->p_ppid) && /* not a system process */ + ((p->p_nice < MIN_NON_RT_NICE_VAL) || + (rt_disk_scheduling))) { + mcb->priority = 32 - p->p_basepri; + } + } + + mcb->interrupt = TRUE; + mcb->drive = hu->slave; + mcb->buf_ptr = bp; + mcb->context = (unsigned long) mcb; + if (bp->b_flags & B_LOCALIO) { + mcb->command = bp->b_hdccommand; + mcb->cyl = bp->b_cyl; + mcb->head = bp->b_head; + mcb->sector = bp->b_sector; + } + else { + mcb->command = (bp->b_flags & B_READ) ? HCMD_READ:HCMD_WRITE; + mcb->cyl = sector/hu->spc; + mcb->head = (sector/hu->sectors) % hu->heads; + mcb->sector = sector % hu->sectors; + } + + /* + * Build the data chain - address/count pairs for each page. + * The first transfer might not start on a page boundary. + * Purge the data cache for pages to be dma'd into. + * + * There is no attempt to combine physically contiguous + * pages into the same data chain, since it is faster + * to just dma the extra data chain into the controller + * than it is to combine the pages; + */ + + { + register struct proc *procp; /* process structure */ + register int bc; /* bytecount this page */ + register int bcremain=bytes; /* bytecount remaining */ + + if ( bp->b_flags & B_DIRTY ) + procp = (struct proc *) &proc[2] ; + else + procp = bp->b_proc; + if (bp->b_flags & B_READ) mtpr(vaddr,P1DC); + bc = min( bcremain, (NBPG-(vaddr&(NBPG-1))) ); + mcb->chain[0].ta = vtoph(procp,vaddr); + mcb->chain[0].lwc = bc/4; + bcremain -= bc; + i = 0; + while (bcremain>0) { + vaddr += bc; + if (bp->b_flags & B_READ) mtpr(vaddr,P1DC); + bc = min(bcremain,NBPG); + mcb->chain[i].lwc |= LWC_DATA_CHAIN; + i++; + mcb->chain[i].ta = vtoph(procp,vaddr); + mcb->chain[i].lwc= bc/4; + bcremain -= bc; + } + } + + /* + * Set up information for error logging and system activity + * for programs such as iostat, sadp, sadc, sar, sag. + * Time-stamp the buf (and the unit if it is just becoming busy). + * Record the total number of transfer operations and the total + * no. of 512-byte blocks xferred. + * Turn on the activity bit for this device - for error logging. + */ + + bp->b_start = time.tv_sec; + if (vdtab[unit].b_active++ == 1) + vdtab[unit].io_start = time.tv_sec; + vdstat[unit].io_cnt++; + vdstat[unit].io_bcnt += blocks * HDC_SPB; + blkacty |= (1 << major(bp->b_dev)); + dk_wds[unit] += bytes/32; + dk_busy |= 1 << unit; + + /* + * If the controller has active mcb's: + * don't send this mcb until the next interrupt occurs. + * + * Otherwise: + * 1) add the mcb to the active queue; + * 2) physically link the mcb from the master mcb; + * 3) fill in the master mcb; + * 4) tell the hdc to scan the new mcb. + */ + + { + register master_mcb_type *master; /* hdc's master mcb */ + + master= &hc->master_mcb; + priority = spl7(); + if ( hc->forw_active != (mcb_type *) &hc->forw_active ) { + insque(mcb, &hc->forw_wait); +#ifdef HDCLOG + hdlog(mcb,4 + 16*hc->ctlr); +#endif + } + else + { + insque(mcb, &hc->forw_active); + master->forw_phaddr = mcb->mcb_phaddr; + mcb->forw_phaddr = 0; + master->mcs = 0; +#ifdef HDCLOG + hdlog(mcb,5 + 16*hc->ctlr); +#endif + HDC_REGISTER(master_mcb_reg) = hc->master_phaddr; + } + splx(priority); + } + + /* + * Returns. + */ + + return; +eacces: bp->b_error = EACCES; + goto errcom; +efault: bp->b_error = EFAULT; + goto errcom; +enxio: bp->b_error = ENXIO; +errcom: bp->b_flags |= B_ERROR; + bp->b_resid = bytes; + iodone(bp); +} + +/************************************************************************* +* Procedure: hdwrite +* +* Description: Character device write routine. It is called by the +* inode read/write routine 'ino_rw'. +* +* Returns: The error status returned by 'physio'. +**************************************************************************/ + +int +hdwrite(dev, uio) + + +dev_t dev; /* Device type. Major/minor dev#. + */ +int *uio; /* Pointer to a uio structure describing + * a write request: buffer address; + * sector offset; no. of sectors; etc. + */ +{ + hdc_unit_type *hu; /* hdc unit information */ + + hu = &hdc_units[ HDC_UNIT(dev) ]; + + /* + * 'physio' builds the buf structure, locks the user pages, calls + * 'hdstrategy' to do the write, waits until i/o is complete + * (iodone), deallocates the buf structure, and unlocks the pages. + */ + + return physio( + hdstrategy, /* hdc's strategy routine */ + &hu->raw_buf, /* physio builds a buf struct here */ + dev, /* major/minor device number */ + B_WRITE, /* write the buffer */ + minphys, /* routine to set max transfer size */ + uio); /* describes the transfer request */ +} -- 2.20.1