BSD 4_3_Net_2 release
[unix-history] / usr / src / sys / vax / bi / kdb.c
/*
* Copyright (c) 1988 Regents of the University of California.
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Chris Torek.
*
* 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. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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.
*
* @(#)kdb.c 7.10 (Berkeley) 12/16/90
*/
/*
* KDB50/MSCP device driver
*/
/*
* TODO
* rethink BI software interface
* write bad block forwarding code
*/
#include "kra.h" /* XXX */
#define DRIVENAMES "kra" /* XXX */
#if NKDB > 0
/*
* CONFIGURATION OPTIONS. The next three defines are tunable -- tune away!
*
* NRSPL2 and NCMDL2 control the number of response and command
* packets respectively. They may be any value from 0 to 7, though
* setting them higher than 5 is unlikely to be of any value.
* If you get warnings about your command ring being too small,
* try increasing the values by one.
*
* MAXUNIT controls the maximum slave number (and hence number of drives
* per controller) we are prepared to handle.
*/
#define NRSPL2 5 /* log2 number of response packets */
#define NCMDL2 5 /* log2 number of command packets */
#define MAXUNIT 8 /* maximum allowed unit number */
#include "sys/param.h"
#include "sys/systm.h"
#include "sys/malloc.h"
#include "sys/map.h"
#include "sys/buf.h"
#include "sys/conf.h"
#include "sys/user.h"
#include "sys/proc.h"
#include "sys/vm.h"
#include "sys/dkstat.h"
#include "sys/cmap.h"
#include "sys/syslog.h"
#include "sys/kernel.h"
#define NRSP (1 << NRSPL2)
#define NCMD (1 << NCMDL2)
#include "../include/pte.h"
#include "../include/cpu.h"
#include "../vax/mscp.h"
#include "../vax/mscpvar.h"
#include "../include/mtpr.h"
#include "bireg.h"
#include "kdbreg.h"
#include "../uba/ubavar.h"
/*
* Conversions from kernel virtual to physical and page table addresses.
* PHYS works only for kernel text and primary (compile time) data addresses.
*/
#define PHYS(cast, addr) \
((cast) ((int)(addr) & 0x7fffffff))
/*
* KDB variables, per controller.
*/
struct kdbinfo {
/* software info, per KDB */
struct kdb_regs *ki_kdb; /* KDB registers */
struct kdb_regs *ki_physkdb; /* phys address of KDB registers */
short ki_state; /* KDB50 state; see below */
short ki_flags; /* flags; see below */
int ki_micro; /* microcode revision */
short ki_vec; /* scb vector offset */
short ki_wticks; /* watchdog timer ticks */
/*
* KDB PTEs must be contiguous. Some I/O is done on addresses
* for which this is true (PTEs in Sysmap and Usrptmap), but
* other transfers may have PTEs that are scattered in physical
* space. Ki_map maps a physically contiguous PTE space used
* for these transfers.
*/
#define KI_MAPSIZ (NCMD + 2)
struct map *ki_map; /* resource map */
#define KI_PTES 256
struct pte ki_pte[KI_PTES]; /* contiguous PTE space */
long ki_ptephys; /* phys address of &ki_pte[0] */
struct mscp_info ki_mi; /* MSCP info (per mscpvar.h) */
struct buf ki_tab; /* controller queue */
/* stuff read and written by hardware */
struct kdbca ki_ca; /* communications area */
struct mscp ki_rsp[NRSP]; /* response packets */
struct mscp ki_cmd[NCMD]; /* command packets */
} kdbinfo[NKDB];
#define ki_ctlr ki_mi.mi_ctlr
/*
* Controller states
*/
#define ST_IDLE 0 /* uninitialised */
#define ST_STEP1 1 /* in `STEP 1' */
#define ST_STEP2 2 /* in `STEP 2' */
#define ST_STEP3 3 /* in `STEP 3' */
#define ST_SETCHAR 4 /* in `Set Controller Characteristics' */
#define ST_RUN 5 /* up and running */
/*
* Flags
*/
#define KDB_ALIVE 0x01 /* this KDB50 exists */
#define KDB_GRIPED 0x04 /* griped about cmd ring too small */
#define KDB_INSLAVE 0x08 /* inside kdbslave() */
#define KDB_DOWAKE 0x10 /* wakeup when ctlr init done */
struct kdbstats kdbstats; /* statistics */
/*
* Device to unit number and partition:
*/
#define UNITSHIFT 3
#define UNITMASK 7
#define kdbunit(dev) (minor(dev) >> UNITSHIFT)
#define kdbpart(dev) (minor(dev) & UNITMASK)
/* THIS SHOULD BE READ OFF THE PACK, PER DRIVE */
/* THESE SHOULD BE SHARED WITH uda.c (but not yet) */
struct size {
daddr_t nblocks;
daddr_t blkoff;
} kra81_sizes[8] = {
#ifdef MARYLAND
67832, 0, /* A=cyl 0 thru 94 + 2 sectors */
67828, 67832, /* B=cyl 95 thru 189 - 2 sectors */
-1, 0, /* C=cyl 0 thru 1247 */
-1, 135660, /* D=cyl 190 thru 1247 */
449466, 49324, /* E xxx */
64260, 498790, /* F xxx */
328022, 563050, /* G xxx */
0, 0,
#else
15884, 0, /* a */
33440, 15884, /* b */
-1, 0, /* c */
-1, 49324, /* d */
449466, 49324, /* e */
64260, 498790, /* f */
328022, 563050, /* g */
0, 0,
#endif
}, kra80_sizes[8] = {
15884, 0, /* A=blk 0 thru 15883 */
33440, 15884, /* B=blk 15884 thru 49323 */
-1, 0, /* C=blk 0 thru end */
0, 0,
0, 0,
0, 0,
82080, 49324, /* G=blk 49324 thru 131403 */
-1, 131404, /* H=blk 131404 thru end */
}, kra60_sizes[8] = {
15884, 0, /* A=blk 0 thru 15883 */
33440, 15884, /* B=blk 15884 thru 49323 */
-1, 0, /* C=blk 0 thru end */
-1, 49324, /* D=blk 49324 thru end */
0, 0,
0, 0,
82080, 49324, /* G=blk 49324 thru 131403 */
-1, 131404, /* H=blk 131404 thru end */
};
/* END OF STUFF WHICH SHOULD BE READ IN PER DISK */
/*
* Drive type index decoding table. `ut_name' is null iff the
* type is not known.
*/
struct kdbtypes {
char *ut_name; /* drive type name */
struct size *ut_sizes; /* partition tables */
} kdbtypes[] = {
NULL, NULL,
"ra80", kra80_sizes, /* 1 = ra80 */
NULL, NULL,
NULL, NULL,
"ra60", kra60_sizes, /* 4 = ra60 */
"ra81", kra81_sizes, /* 5 = ra81 */
};
#define NTYPES 6
/*
* Definition of the driver for autoconf and generic MSCP code.
* SOME OF THIS IS BOGUS (must fix config)
*/
#ifdef notdef /* not when driver is for kra disks */
/*
* Some of these variables (per-drive stuff) are shared
* with the UDA50 code (why not, they are the same drives).
* N.B.: kdbdinfo must not be shared.
*/
#define kdbutab udautab /* shared */
#define kdbslavereply udaslavereply /* shared */
#endif
int kdbprobe(); /* XXX */
int kdbslave(), kdbattach();
int kdbdgram(), kdbctlrdone(), kdbunconf(), kdbiodone();
int kdbonline(), kdbgotstatus(), kdbioerror();
struct uba_device *kdbdinfo[NKRA]; /* uba_device indeed! */
struct buf kdbutab[NKRA]; /* per drive transfer queue */
u_short kdbstd[] = { 0 }; /* XXX */
struct uba_driver kdbdriver = /* XXX */
{ kdbprobe, kdbslave, kdbattach, 0, kdbstd, DRIVENAMES, kdbdinfo, "kdb" };
struct mscp_driver kdbmscpdriver =
{ MAXUNIT, NKRA, UNITSHIFT, kdbutab, (struct disklabel *)0, kdbdinfo,
kdbdgram, kdbctlrdone, kdbunconf, kdbiodone,
kdbonline, kdbgotstatus, NULL, kdbioerror, NULL,
"kdb", DRIVENAMES };
/*
* Miscellaneous private variables.
*/
char kdbsr_bits[] = KDBSR_BITS;
struct uba_device *kdbip[NKDB][MAXUNIT];
/* inverting pointers: ctlr & unit => `Unibus'
device pointer */
daddr_t ra_dsize[NKRA]; /* drive sizes, from on line end packets */
struct mscp kdbslavereply; /* get unit status response packet, set
for kdbslave by kdbunconf, via kdbintr */
int kdbwstart, kdbwatch(); /* watchdog timer */
int wakeup();
/*
* If kdbprobe is called, return 0 to keep Unibus code from attempting
* to use this device. XXX rethink
*/
/* ARGSUSED */
kdbprobe(reg, ctlr)
caddr_t reg;
int ctlr;
{
return (0);
}
/*
* Configure in a KDB50 controller.
*/
kdbconfig(kdbnum, va, pa, vec)
int kdbnum;
struct biiregs *va, *pa;
int vec;
{
register struct kdbinfo *ki;
#define mi (&ki->ki_mi)
#ifdef lint
extern int (*kdbint0[])();
(*kdbint0[0])(0); /* this is a config botch */
kdbintr(0);
#endif
/*
* Set up local KDB status.
*/
ki = &kdbinfo[kdbnum];
ki->ki_kdb = (struct kdb_regs *)va;
ki->ki_physkdb = (struct kdb_regs *)pa;
ki->ki_vec = vec;
ki->ki_map =
(struct map *)malloc((u_long)(KI_MAPSIZ * sizeof(struct map)),
M_DEVBUF, M_NOWAIT);
if (ki->ki_map == NULL) {
printf("kdb%d: cannot get memory for ptes\n", kdbnum);
return;
}
ki->ki_ptephys = PHYS(long, ki->ki_pte); /* kvtophys(ki->ki_pte) */
ki->ki_flags = KDB_ALIVE;
/* THE FOLLOWING IS ONLY NEEDED TO CIRCUMVENT A BUG IN rminit */
bzero((caddr_t)ki->ki_map, KI_MAPSIZ * sizeof(struct map));
rminit(ki->ki_map, (long)KI_PTES, (long)1, "kdb", KI_MAPSIZ);
/*
* Set up the generic MSCP structures.
*/
mi->mi_md = &kdbmscpdriver;
mi->mi_ctlr = kdbnum; /* also sets ki->ki_ctlr */
mi->mi_tab = &ki->ki_tab;
mi->mi_ip = kdbip[kdbnum];
mi->mi_cmd.mri_size = NCMD;
mi->mi_cmd.mri_desc = ki->ki_ca.ca_cmddsc;
mi->mi_cmd.mri_ring = ki->ki_cmd;
mi->mi_rsp.mri_size = NRSP;
mi->mi_rsp.mri_desc = ki->ki_ca.ca_rspdsc;
mi->mi_rsp.mri_ring = ki->ki_rsp;
mi->mi_wtab.av_forw = mi->mi_wtab.av_back = &mi->mi_wtab;
#undef mi
}
/*
* Find a slave.
* Note that by the time kdbslave is called, the interrupt vector
* for the KDB50 has been set up (so that kdbunconf() will be called).
*/
kdbslave(ui)
register struct uba_device *ui;
{
register struct kdbinfo *ki;
register struct mscp *mp;
int next = 0, type, timeout, tries, i;
#ifdef lint
i = 0; i = i;
#endif
/*
* Make sure the controller is fully initialised, by waiting
* for it if necessary.
*/
ki = &kdbinfo[ui->ui_ctlr];
if (ki->ki_state == ST_RUN)
goto findunit;
tries = 0;
again:
if (kdbinit(ki))
return (0);
timeout = todr() + 1000; /* 10 seconds */
while (todr() < timeout)
if (ki->ki_state == ST_RUN) /* made it */
goto findunit;
if (++tries < 2)
goto again;
printf("kdb%d: controller hung\n", ki->ki_ctlr);
return (0);
/*
* The controller is all set; go find the unit. Grab an
* MSCP packet and send out a Get Unit Status command, with
* the `next unit' modifier if we are looking for a generic
* unit. We set the `in slave' flag so that kdbunconf()
* knows to copy the response to `kdbslavereply'.
*/
findunit:
kdbslavereply.mscp_opcode = 0;
ki->ki_flags |= KDB_INSLAVE;
if ((mp = mscp_getcp(&ki->ki_mi, MSCP_DONTWAIT)) == NULL)
panic("kdbslave"); /* `cannot happen' */
mp->mscp_opcode = M_OP_GETUNITST;
if (ui->ui_slave == '?') {
mp->mscp_unit = next;
mp->mscp_modifier = M_GUM_NEXTUNIT;
} else {
mp->mscp_unit = ui->ui_slave;
mp->mscp_modifier = 0;
}
*mp->mscp_addr |= MSCP_OWN | MSCP_INT;
i = ki->ki_kdb->kdb_ip; /* initiate polling */
mp = &kdbslavereply;
timeout = todr() + 1000;
while (todr() < timeout)
if (mp->mscp_opcode)
goto gotit;
printf("kdb%d: no response to Get Unit Status request\n",
ki->ki_ctlr);
ki->ki_flags &= ~KDB_INSLAVE;
return (0);
gotit:
ki->ki_flags &= ~KDB_INSLAVE;
/*
* Got a slave response. If the unit is there, use it.
*/
switch (mp->mscp_status & M_ST_MASK) {
case M_ST_SUCCESS: /* worked */
case M_ST_AVAILABLE: /* found another drive */
break; /* use it */
case M_ST_OFFLINE:
/*
* Figure out why it is off line. It may be because
* it is nonexistent, or because it is spun down, or
* for some other reason.
*/
switch (mp->mscp_status & ~M_ST_MASK) {
case M_OFFLINE_UNKNOWN:
/*
* No such drive, and there are none with
* higher unit numbers either, if we are
* using M_GUM_NEXTUNIT.
*/
return (0);
case M_OFFLINE_UNMOUNTED:
/*
* The drive is not spun up. Use it anyway.
*
* N.B.: this seems to be a common occurrance
* after a power failure. The first attempt
* to bring it on line seems to spin it up
* (and thus takes several minutes). Perhaps
* we should note here that the on-line may
* take longer than usual.
*/
break;
default:
/*
* In service, or something else equally unusable.
*/
printf("kdb%d: unit %d off line:", ki->ki_ctlr,
mp->mscp_unit);
mscp_printevent(mp);
goto try_another;
}
break;
default:
printf("kdb%d: unable to get unit status:", ki->ki_ctlr);
mscp_printevent(mp);
return (0);
}
/*
* Does this ever happen? What (if anything) does it mean?
*/
if (mp->mscp_unit < next) {
printf("kdb%d: unit %d, next %d\n",
ki->ki_ctlr, mp->mscp_unit, next);
return (0);
}
if (mp->mscp_unit >= MAXUNIT) {
printf("kdb%d: cannot handle unit number %d (max is %d)\n",
ki->ki_ctlr, mp->mscp_unit, MAXUNIT - 1);
return (0);
}
/*
* See if we already handle this drive.
* (Only likely if ui->ui_slave=='?'.)
*/
if (kdbip[ki->ki_ctlr][mp->mscp_unit] != NULL)
goto try_another;
/*
* Make sure we know about this kind of drive.
* Others say we should treat unknowns as RA81s; I am
* not sure this is safe.
*/
type = mp->mscp_guse.guse_drivetype;
if (type >= NTYPES || kdbtypes[type].ut_name == 0) {
register long id = mp->mscp_guse.guse_mediaid;
printf("kdb%d: unit %d: media ID `", ki->ki_ctlr,
mp->mscp_unit);
printf("%c%c %c%c%c%d",
MSCP_MID_CHAR(4, id), MSCP_MID_CHAR(3, id),
MSCP_MID_CHAR(2, id), MSCP_MID_CHAR(1, id),
MSCP_MID_CHAR(0, id), MSCP_MID_NUM(id));
printf("' is of unknown type %d; ignored\n", type);
try_another:
if (ui->ui_slave != '?')
return (0);
next = mp->mscp_unit + 1;
goto findunit;
}
/*
* Voila!
*/
ui->ui_type = type;
ui->ui_flags = 0; /* not on line, nor anything else */
ui->ui_slave = mp->mscp_unit;
return (1);
}
/*
* Attach a found slave. Make sure the watchdog timer is running.
* If this disk is being profiled, fill in the `wpms' value (used by
* what?). Set up the inverting pointer, and attempt to bring the
* drive on line.
*/
kdbattach(ui)
register struct uba_device *ui;
{
if (kdbwstart == 0) {
timeout(kdbwatch, (caddr_t)0, hz);
kdbwstart++;
}
if (ui->ui_dk >= 0)
dk_wpms[ui->ui_dk] = (60 * 31 * 256); /* approx */
kdbip[ui->ui_ctlr][ui->ui_slave] = ui;
(void) kdb_bringonline(ui, 1);
/* should we get its status too? */
}
/*
* Initialise a KDB50. Return true iff something goes wrong.
*/
kdbinit(ki)
register struct kdbinfo *ki;
{
register struct kdb_regs *ka = ki->ki_kdb;
int timo;
/*
* While we are thinking about it, reset the next command
* and response indicies.
*/
ki->ki_mi.mi_cmd.mri_next = 0;
ki->ki_mi.mi_rsp.mri_next = 0;
/*
* Start up the hardware initialisation sequence.
*/
#define STEP0MASK (KDB_ERR | KDB_STEP4 | KDB_STEP3 | KDB_STEP2 | KDB_STEP1)
ki->ki_state = ST_IDLE; /* in case init fails */
bi_reset(&ka->kdb_bi); /* reset bi node (but not the BI itself) */
timo = todr() + 1000;
while ((ka->kdb_sa & STEP0MASK) == 0) {
if (todr() > timo) {
printf("kdb%d: timeout during init\n", ki->ki_ctlr);
return (-1);
}
}
if ((ka->kdb_sa & STEP0MASK) != KDB_STEP1) {
printf("kdb%d: init failed, sa=%b\n", ki->ki_ctlr,
ka->kdb_sa, kdbsr_bits);
return (-1);
}
/*
* Success! Record new state, and start step 1 initialisation.
* The rest is done in the interrupt handler.
*/
ki->ki_state = ST_STEP1;
ka->kdb_bi.bi_intrdes = 1 << mastercpu;
#ifdef unneeded /* is it? */
ka->kdb_bi.bi_csr = (ka->kdb_bi.bi_csr&~BICSR_ARB_MASK)|BICSR_ARB_???;
#endif
ka->kdb_bi.bi_bcicsr |= BCI_STOPEN | BCI_IDENTEN | BCI_UINTEN |
BCI_INTEN;
/* I THINK THIS IS WRONG */
/* Mach uses 0x601d0, which includes IPL16, but 1d0 is IPL17, nexzvec...? */
ka->kdb_bi.bi_eintrcsr = BIEIC_IPL15 | ki->ki_vec; /* ??? */
/* END I THINK WRONG */
ka->kdb_bi.bi_uintrcsr = ki->ki_vec;
ka->kdb_sw = KDB_ERR | (NCMDL2 << 11) | (NRSPL2 << 8) | KDB_IE |
(ki->ki_vec >> 2);
return (0);
}
/*
* Open a drive.
*/
/*ARGSUSED*/
kdbopen(dev, flag)
dev_t dev;
int flag;
{
register int unit;
register struct uba_device *ui;
register struct kdbinfo *ki;
int s;
/*
* Make sure this is a reasonable open request.
*/
unit = kdbunit(dev);
if (unit >= NKRA || (ui = kdbdinfo[unit]) == 0 || ui->ui_alive == 0)
return (ENXIO);
/*
* Make sure the controller is running, by (re)initialising it if
* necessary.
*/
ki = &kdbinfo[ui->ui_ctlr];
s = spl5();
if (ki->ki_state != ST_RUN) {
if (ki->ki_state == ST_IDLE && kdbinit(ki)) {
splx(s);
return (EIO);
}
/*
* In case it does not come up, make sure we will be
* restarted in 10 seconds. This corresponds to the
* 10 second timeouts in kdbprobe() and kdbslave().
*/
ki->ki_flags |= KDB_DOWAKE;
timeout(wakeup, (caddr_t)&ki->ki_flags, 10 * hz);
sleep((caddr_t)&ki->ki_flags, PRIBIO);
if (ki->ki_state != ST_RUN) {
splx(s);
printf("kdb%d: controller hung\n", ui->ui_ctlr);
return (EIO);
}
untimeout(wakeup, (caddr_t)&ki->ki_flags);
}
if ((ui->ui_flags & UNIT_ONLINE) == 0) {
/*
* Bring the drive on line so we can find out how
* big it is. If it is not spun up, it will not
* come on line; this cannot really be considered
* an `error condition'.
*/
if (kdb_bringonline(ui, 0)) {
splx(s);
printf("%s%d: drive will not come on line\n",
kdbdriver.ud_dname, unit);
return (EIO);
}
}
splx(s);
return (0);
}
/*
* Bring a drive on line. In case it fails to respond, we set
* a timeout on it. The `nosleep' parameter should be set if
* we are to spin-wait; otherwise this must be called at spl5().
*/
kdb_bringonline(ui, nosleep)
register struct uba_device *ui;
int nosleep;
{
register struct kdbinfo *ki = &kdbinfo[ui->ui_ctlr];
register struct mscp *mp;
int i;
if (nosleep) {
mp = mscp_getcp(&ki->ki_mi, MSCP_DONTWAIT);
if (mp == NULL)
return (-1);
} else
mp = mscp_getcp(&ki->ki_mi, MSCP_WAIT);
mp->mscp_opcode = M_OP_ONLINE;
mp->mscp_unit = ui->ui_slave;
mp->mscp_cmdref = (long)&ui->ui_flags;
*mp->mscp_addr |= MSCP_OWN | MSCP_INT;
i = ki->ki_kdb->kdb_ip;
if (nosleep) {
i = todr() + 1000;
while ((ui->ui_flags & UNIT_ONLINE) == 0)
if (todr() > i)
return (-1);
} else {
timeout(wakeup, (caddr_t)&ui->ui_flags, 10 * hz);
sleep((caddr_t)&ui->ui_flags, PRIBIO);
if ((ui->ui_flags & UNIT_ONLINE) == 0)
return (-1);
untimeout(wakeup, (caddr_t)&ui->ui_flags);
}
return (0); /* made it */
}
/*
* Queue a transfer request, and if possible, hand it to the controller.
*
* This routine is broken into two so that the internal version
* kdbstrat1() can be called by the (nonexistent, as yet) bad block
* revectoring routine.
*/
kdbstrategy(bp)
register struct buf *bp;
{
register int unit;
register struct uba_device *ui;
register struct size *st;
daddr_t sz, maxsz;
/*
* Make sure this is a reasonable drive to use.
*/
if ((unit = kdbunit(bp->b_dev)) >= NKRA ||
(ui = kdbdinfo[unit]) == NULL || ui->ui_alive == 0) {
bp->b_error = ENXIO;
bp->b_flags |= B_ERROR;
biodone(bp);
return;
}
/*
* Determine the size of the transfer, and make sure it is
* within the boundaries of the drive.
*/
sz = (bp->b_bcount + 511) >> 9;
st = &kdbtypes[ui->ui_type].ut_sizes[kdbpart(bp->b_dev)];
if ((maxsz = st->nblocks) < 0)
maxsz = ra_dsize[unit] - st->blkoff;
if (bp->b_blkno < 0 || bp->b_blkno + sz > maxsz ||
st->blkoff >= ra_dsize[unit]) {
/* if exactly at end of disk, return an EOF */
if (bp->b_blkno == maxsz)
bp->b_resid = bp->b_bcount;
else {
bp->b_error = EINVAL;
bp->b_flags |= B_ERROR;
}
biodone(bp);
return;
}
kdbstrat1(bp);
}
/*
* Work routine for kdbstrategy.
*/
kdbstrat1(bp)
register struct buf *bp;
{
register int unit = kdbunit(bp->b_dev);
register struct buf *dp;
register struct kdbinfo *ki;
struct uba_device *ui;
int s;
/*
* Append the buffer to the drive queue, and if it is not
* already there, the drive to the controller queue. (However,
* if the drive queue is marked to be requeued, we must be
* awaiting an on line or get unit status command; in this
* case, leave it off the controller queue.)
*/
ui = kdbdinfo[unit];
ki = &kdbinfo[ui->ui_ctlr];
dp = &kdbutab[unit];
s = spl5();
APPEND(bp, dp, av_forw);
if (dp->b_active == 0 && (ui->ui_flags & UNIT_REQUEUE) == 0) {
APPEND(dp, &ki->ki_tab, b_forw);
dp->b_active++;
}
/*
* Start activity on the controller.
*/
kdbstart(ki);
splx(s);
}
/*
* Find the physical address of some contiguous PTEs that map the
* transfer described in `bp', creating them (by copying) if
* necessary. Store the physical base address of the map through
* mapbase, and the page offset through offset, and any resource
* information in *info (or 0 if none).
*
* If we cannot allocate space, return a nonzero status.
*/
int
kdbmap(ki, bp, mapbase, offset, info)
struct kdbinfo *ki;
register struct buf *bp;
long *mapbase, *offset;
int *info;
{
register struct pte *spte, *dpte;
register struct proc *rp;
register int i, a, o;
u_int v;
int npf;
o = (int)bp->b_un.b_addr & PGOFSET;
/* handle contiguous cases */
if ((bp->b_flags & B_PHYS) == 0) {
spte = kvtopte(bp->b_un.b_addr);
kdbstats.ks_sys++;
*mapbase = PHYS(long, spte);
*offset = o;
*info = 0;
return (0);
}
if (bp->b_flags & B_PAGET) {
spte = &Usrptmap[btokmx((struct pte *)bp->b_un.b_addr)];
if (spte->pg_v == 0) panic("kdbmap");
kdbstats.ks_paget++;
*mapbase = PHYS(long, spte);
*offset = o;
*info = 0;
return (0);
}
/* potentially discontiguous or invalid ptes */
v = btop(bp->b_un.b_addr);
rp = bp->b_flags & B_DIRTY ? &proc[2] : bp->b_proc;
if (bp->b_flags & B_UAREA)
spte = &rp->p_addr[v];
else
spte = vtopte(rp, v);
npf = btoc(bp->b_bcount + o);
#ifdef notdef
/*
* The current implementation of the VM system requires
* that all of these be done with a copy. Even if the
* PTEs could be used now, they may be snatched out from
* under us later. It would be nice if we could stop that....
*/
/* check for invalid */
/* CONSIDER CHANGING VM TO VALIDATE PAGES EARLIER */
for (dpte = spte, i = npf; --i >= 0; dpte++)
if (dpte->pg_v == 0)
goto copy1;
/*
* Check for discontiguous physical pte addresses. It is
* not necessary to check each pte, since they come in clumps
* of pages.
*/
i = howmany(npf + (((int)spte & PGOFSET) / sizeof (*spte)), NPTEPG);
/* often i==1, and we can avoid work */
if (--i > 0) {
dpte = kvtopte(spte);
a = dpte->pg_pfnum;
while (--i >= 0)
if ((++dpte)->pg_pfnum != ++a)
goto copy2;
}
/* made it */
kdbstats.ks_contig++;
*mapbase = kvtophys(spte);
*offset = o;
*info = 0;
return (0);
copy1:
kdbstats.ks_inval++; /* temp */
copy2:
#endif /* notdef */
kdbstats.ks_copies++;
i = npf + 1;
if ((a = rmalloc(ki->ki_map, (long)i)) == 0) {
kdbstats.ks_mapwait++;
return (-1);
}
*info = (i << 16) | a;
a--;
/* if offset > PGOFSET, btop(offset) indexes mapbase */
*mapbase = ki->ki_ptephys;
*offset = (a << PGSHIFT) | o;
dpte = &ki->ki_pte[a];
while (--i > 0)
*(int *)dpte++ = PG_V | *(int *)spte++;
*(int *)dpte = 0;
return (0);
}
#define KDBFREE(ki, info) if (info) \
rmfree((ki)->ki_map, (long)((info) >> 16), (long)((info) & 0xffff))
/*
* Start up whatever transfers we can find.
* Note that kdbstart() must be called at spl5().
*/
kdbstart(ki)
register struct kdbinfo *ki;
{
register struct buf *bp, *dp;
register struct mscp *mp;
register struct uba_device *ui;
long mapbase, offset;
int info, ncmd = 0;
/*
* If it is not running, try (again and again...) to initialise
* it. If it is currently initialising just ignore it for now.
*/
if (ki->ki_state != ST_RUN) {
if (ki->ki_state == ST_IDLE && kdbinit(ki))
printf("kdb%d: still hung\n", ki->ki_ctlr);
return;
}
loop:
/* if insufficient credit, avoid overhead */
if (ki->ki_mi.mi_credits <= MSCP_MINCREDITS)
goto out;
/*
* Service the drive at the head of the queue. It may not
* need anything; eventually this will finish up the close
* protocol, but that is yet to be implemented here.
*/
if ((dp = ki->ki_tab.b_actf) == NULL)
goto out;
if ((bp = dp->b_actf) == NULL) {
dp->b_active = 0;
ki->ki_tab.b_actf = dp->b_forw;
goto loop;
}
if (ki->ki_kdb->kdb_sa & KDB_ERR) { /* ctlr fatal error */
kdbsaerror(ki);
goto out;
}
/* find or create maps for this transfer */
if (kdbmap(ki, bp, &mapbase, &offset, &info))
goto out; /* effectively, resource wait */
/*
* Get an MSCP packet, then figure out what to do. If
* we cannot get a command packet, the command ring may
* be too small: We should have at least as many command
* packets as credits, for best performance.
*/
if ((mp = mscp_getcp(&ki->ki_mi, MSCP_DONTWAIT)) == NULL) {
if (ki->ki_mi.mi_credits > MSCP_MINCREDITS &&
(ki->ki_flags & KDB_GRIPED) == 0) {
log(LOG_NOTICE, "kdb%d: command ring too small\n",
ki->ki_ctlr);
ki->ki_flags |= KDB_GRIPED;/* complain only once */
}
KDBFREE(ki, info);
goto out;
}
/*
* Bring the drive on line if it is not already. Get its status
* if we do not already have it. Otherwise just start the transfer.
*/
ui = kdbdinfo[kdbunit(bp->b_dev)];
if ((ui->ui_flags & UNIT_ONLINE) == 0) {
mp->mscp_opcode = M_OP_ONLINE;
goto common;
}
if ((ui->ui_flags & UNIT_HAVESTATUS) == 0) {
mp->mscp_opcode = M_OP_GETUNITST;
common:
if (ui->ui_flags & UNIT_REQUEUE) panic("kdbstart");
/*
* Take the drive off the controller queue. When the
* command finishes, make sure the drive is requeued.
* Give up any mapping (not needed now). This last is
* not efficient, but is rare.
*/
KDBFREE(ki, info);
ki->ki_tab.b_actf = dp->b_forw;
dp->b_active = 0;
ui->ui_flags |= UNIT_REQUEUE;
mp->mscp_unit = ui->ui_slave;
*mp->mscp_addr |= MSCP_OWN | MSCP_INT;
ncmd++;
goto loop;
}
mp->mscp_opcode = (bp->b_flags & B_READ) ? M_OP_READ : M_OP_WRITE;
mp->mscp_unit = ui->ui_slave;
mp->mscp_seq.seq_lbn = bp->b_blkno +
kdbtypes[ui->ui_type].ut_sizes[kdbpart(bp->b_dev)].blkoff;
mp->mscp_seq.seq_bytecount = bp->b_bcount;
mp->mscp_seq.seq_buffer = offset | KDB_MAP;
mp->mscp_seq.seq_mapbase = mapbase;
/* profile the drive */
if (ui->ui_dk >= 0) {
dk_busy |= 1 << ui->ui_dk;
dk_xfer[ui->ui_dk]++;
dk_wds[ui->ui_dk] += bp->b_bcount >> 6;
}
/*
* Fill in the rest of the MSCP packet and move the buffer to the
* I/O wait queue.
*/
mscp_go(&ki->ki_mi, mp, info);
ncmd++; /* note the transfer */
ki->ki_tab.b_active++; /* another one going */
goto loop;
out:
if (ncmd >= KS_MAXC)
ncmd = KS_MAXC - 1;
kdbstats.ks_cmd[ncmd]++;
if (ncmd) /* start some transfers */
ncmd = ki->ki_kdb->kdb_ip;
}
/* ARGSUSED */
kdbiodone(mi, bp, info)
struct mscp_info *mi;
struct buf *bp;
int info;
{
register struct kdbinfo *ki = &kdbinfo[mi->mi_ctlr];
KDBFREE(ki, info);
biodone(bp);
ki->ki_tab.b_active--; /* another one done */
}
/*
* The error bit was set in the controller status register. Gripe,
* reset the controller, requeue pending transfers.
*/
kdbsaerror(ki)
register struct kdbinfo *ki;
{
printf("kdb%d: controller error, sa=%b\n", ki->ki_ctlr,
ki->ki_kdb->kdb_sa, kdbsr_bits);
mscp_requeue(&ki->ki_mi);
(void) kdbinit(ki);
}
/*
* Interrupt routine. Depending on the state of the controller,
* continue initialisation, or acknowledge command and response
* interrupts, and process responses.
*/
kdbintr(ctlr)
int ctlr;
{
register struct kdbinfo *ki = &kdbinfo[ctlr];
register struct kdb_regs *kdbaddr = ki->ki_kdb;
register struct mscp *mp;
register int i;
ki->ki_wticks = 0; /* reset interrupt watchdog */
/*
* Combinations during steps 1, 2, and 3: STEPnMASK
* corresponds to which bits should be tested;
* STEPnGOOD corresponds to the pattern that should
* appear after the interrupt from STEPn initialisation.
* All steps test the bits in ALLSTEPS.
*/
#define ALLSTEPS (KDB_ERR|KDB_STEP4|KDB_STEP3|KDB_STEP2|KDB_STEP1)
#define STEP1MASK (ALLSTEPS | KDB_IE | KDB_NCNRMASK)
#define STEP1GOOD (KDB_STEP2 | KDB_IE | (NCMDL2 << 3) | NRSPL2)
#define STEP2MASK (ALLSTEPS | KDB_IE | KDB_IVECMASK)
#define STEP2GOOD (KDB_STEP3 | KDB_IE | (ki->ki_vec >> 2))
#define STEP3MASK ALLSTEPS
#define STEP3GOOD KDB_STEP4
switch (ki->ki_state) {
case ST_IDLE:
/*
* Ignore unsolicited interrupts.
*/
log(LOG_WARNING, "kdb%d: stray intr\n", ctlr);
return;
case ST_STEP1:
/*
* Begin step two initialisation.
*/
if ((kdbaddr->kdb_sa & STEP1MASK) != STEP1GOOD) {
i = 1;
initfailed:
printf("kdb%d: init step %d failed, sa=%b\n",
ctlr, i, kdbaddr->kdb_sa, kdbsr_bits);
ki->ki_state = ST_IDLE;
if (ki->ki_flags & KDB_DOWAKE) {
ki->ki_flags &= ~KDB_DOWAKE;
wakeup((caddr_t)&ki->ki_flags);
}
return;
}
kdbaddr->kdb_sw = PHYS(int, &ki->ki_ca.ca_rspdsc[0]);
ki->ki_state = ST_STEP2;
return;
case ST_STEP2:
/*
* Begin step 3 initialisation.
*/
if ((kdbaddr->kdb_sa & STEP2MASK) != STEP2GOOD) {
i = 2;
goto initfailed;
}
kdbaddr->kdb_sw = PHYS(int, &ki->ki_ca.ca_rspdsc[0]) >> 16;
ki->ki_state = ST_STEP3;
return;
case ST_STEP3:
/*
* Set controller characteristics (finish initialisation).
*/
if ((kdbaddr->kdb_sa & STEP3MASK) != STEP3GOOD) {
i = 3;
goto initfailed;
}
i = kdbaddr->kdb_sa & 0xff;
if (i != ki->ki_micro) {
ki->ki_micro = i;
printf("kdb%d: version %d model %d\n",
ctlr, i & 0xf, i >> 4);
}
kdbaddr->kdb_sw = KDB_GO;
/* initialise hardware data structures */
for (i = 0, mp = ki->ki_rsp; i < NRSP; i++, mp++) {
ki->ki_ca.ca_rspdsc[i] = MSCP_OWN | MSCP_INT |
PHYS(long, &ki->ki_rsp[i].mscp_cmdref);
mp->mscp_addr = &ki->ki_ca.ca_rspdsc[i];
mp->mscp_msglen = MSCP_MSGLEN;
}
for (i = 0, mp = ki->ki_cmd; i < NCMD; i++, mp++) {
ki->ki_ca.ca_cmddsc[i] = MSCP_INT |
PHYS(long, &ki->ki_cmd[i].mscp_cmdref);
mp->mscp_addr = &ki->ki_ca.ca_cmddsc[i];
mp->mscp_msglen = MSCP_MSGLEN;
}
/*
* Before we can get a command packet, we need some
* credits. Fake some up to keep mscp_getcp() happy,
* get a packet, and cancel all credits (the right
* number should come back in the response to the
* SCC packet).
*/
ki->ki_mi.mi_credits = MSCP_MINCREDITS + 1;
mp = mscp_getcp(&ki->ki_mi, MSCP_DONTWAIT);
if (mp == NULL) /* `cannot happen' */
panic("kdbintr");
ki->ki_mi.mi_credits = 0;
mp->mscp_opcode = M_OP_SETCTLRC;
mp->mscp_unit = 0;
mp->mscp_sccc.sccc_ctlrflags = M_CF_ATTN | M_CF_MISC |
M_CF_THIS;
*mp->mscp_addr |= MSCP_OWN | MSCP_INT;
i = kdbaddr->kdb_ip;
ki->ki_state = ST_SETCHAR;
return;
case ST_SETCHAR:
case ST_RUN:
/*
* Handle Set Ctlr Characteristics responses and operational
* responses (via mscp_dorsp).
*/
break;
default:
log(LOG_ERR, "kdb%d: driver bug, state %d\n", ctlr,
ki->ki_state);
return;
}
if (kdbaddr->kdb_sa & KDB_ERR) {/* ctlr fatal error */
kdbsaerror(ki);
return;
}
/*
* Handle buffer purge requests.
* KDB DOES NOT HAVE BDPs
*/
if (ki->ki_ca.ca_bdp) {
printf("kdb%d: purge bdp %d\n", ctlr, ki->ki_ca.ca_bdp);
panic("kdb purge");
}
/*
* Check for response and command ring transitions.
*/
if (ki->ki_ca.ca_rspint) {
ki->ki_ca.ca_rspint = 0;
mscp_dorsp(&ki->ki_mi);
}
if (ki->ki_ca.ca_cmdint) {
ki->ki_ca.ca_cmdint = 0;
MSCP_DOCMD(&ki->ki_mi);
}
if (ki->ki_tab.b_actf != NULL)
kdbstart(ki);
}
/*
* Handle an error datagram. All we do now is decode it.
*/
kdbdgram(mi, mp)
struct mscp_info *mi;
struct mscp *mp;
{
mscp_decodeerror(mi->mi_md->md_mname, mi->mi_ctlr, mp);
}
/*
* The Set Controller Characteristics command finished.
* Record the new state of the controller.
*/
kdbctlrdone(mi, mp)
struct mscp_info *mi;
struct mscp *mp;
{
register struct kdbinfo *ki = &kdbinfo[mi->mi_ctlr];
if ((mp->mscp_status & M_ST_MASK) == M_ST_SUCCESS)
ki->ki_state = ST_RUN;
else {
printf("kdb%d: SETCTLRC failed, status 0x%x\n",
ki->ki_ctlr, mp->mscp_status);
ki->ki_state = ST_IDLE;
}
if (ki->ki_flags & KDB_DOWAKE) {
ki->ki_flags &= ~KDB_DOWAKE;
wakeup((caddr_t)&ki->ki_flags);
}
}
/*
* Received a response from an as-yet unconfigured drive. Configure it
* in, if possible.
*/
kdbunconf(mi, mp)
struct mscp_info *mi;
register struct mscp *mp;
{
/*
* If it is a slave response, copy it to kdbslavereply for
* kdbslave() to look at.
*/
if (mp->mscp_opcode == (M_OP_GETUNITST | M_OP_END) &&
(kdbinfo[mi->mi_ctlr].ki_flags & KDB_INSLAVE) != 0) {
kdbslavereply = *mp;
return (MSCP_DONE);
}
/*
* Otherwise, it had better be an available attention response.
*/
if (mp->mscp_opcode != M_OP_AVAILATTN)
return (MSCP_FAILED);
/* do what autoconf does */
return (MSCP_FAILED); /* not yet */
}
/*
* A drive came on line. Check its type and size. Return DONE if
* we think the drive is truly on line. In any case, awaken anyone
* sleeping on the drive on-line-ness.
*/
kdbonline(ui, mp)
register struct uba_device *ui;
struct mscp *mp;
{
register int type;
wakeup((caddr_t)&ui->ui_flags);
if ((mp->mscp_status & M_ST_MASK) != M_ST_SUCCESS) {
printf("kdb%d: attempt to bring %s%d on line failed:",
ui->ui_ctlr, kdbdriver.ud_dname, ui->ui_unit);
mscp_printevent(mp);
return (MSCP_FAILED);
}
type = mp->mscp_onle.onle_drivetype;
if (type >= NTYPES || kdbtypes[type].ut_name == 0) {
printf("kdb%d: %s%d: unknown type %d\n",
ui->ui_ctlr, kdbdriver.ud_dname, ui->ui_unit, type);
return (MSCP_FAILED);
}
/*
* Note any change of types. Not sure if we should do
* something special about them, or if so, what....
*/
if (type != ui->ui_type) {
printf("%s%d: changed types! was %s\n",
kdbdriver.ud_dname, ui->ui_unit,
kdbtypes[ui->ui_type].ut_name);
ui->ui_type = type;
}
ra_dsize[ui->ui_unit] = (daddr_t) mp->mscp_onle.onle_unitsize;
printf("%s%d: %s, size = %d sectors\n",
kdbdriver.ud_dname, ui->ui_unit,
kdbtypes[type].ut_name, ra_dsize[ui->ui_unit]);
return (MSCP_DONE);
}
/*
* We got some (configured) unit's status. Return DONE if it succeeded.
*/
kdbgotstatus(ui, mp)
register struct uba_device *ui;
register struct mscp *mp;
{
if ((mp->mscp_status & M_ST_MASK) != M_ST_SUCCESS) {
printf("kdb%d: attempt to get status for %s%d failed:",
ui->ui_ctlr, kdbdriver.ud_dname, ui->ui_unit);
mscp_printevent(mp);
return (MSCP_FAILED);
}
/* need to record later for bad block forwarding - for now, print */
printf("\
%s%d: unit %d, nspt %d, group %d, ngpc %d, rctsize %d, nrpt %d, nrct %d\n",
kdbdriver.ud_dname, ui->ui_unit, mp->mscp_unit,
mp->mscp_guse.guse_nspt, mp->mscp_guse.guse_group,
mp->mscp_guse.guse_ngpc, mp->mscp_guse.guse_rctsize,
mp->mscp_guse.guse_nrpt, mp->mscp_guse.guse_nrct);
return (MSCP_DONE);
}
/*
* A transfer failed. We get a chance to fix or restart it.
* Need to write the bad block forwaring code first....
*/
/*ARGSUSED*/
kdbioerror(ui, mp, bp)
register struct uba_device *ui;
register struct mscp *mp;
struct buf *bp;
{
if (mp->mscp_flags & M_EF_BBLKR) {
/*
* A bad block report. Eventually we will
* restart this transfer, but for now, just
* log it and give up.
*/
log(LOG_ERR, "%s%d: bad block report: %d%s\n",
kdbdriver.ud_dname, ui->ui_unit, mp->mscp_seq.seq_lbn,
mp->mscp_flags & M_EF_BBLKU ? " + others" : "");
} else {
/*
* What the heck IS a `serious exception' anyway?
*/
if (mp->mscp_flags & M_EF_SEREX)
log(LOG_ERR, "%s%d: serious exception reported\n",
kdbdriver.ud_dname, ui->ui_unit);
}
return (MSCP_FAILED);
}
#ifdef notyet
/*
* I/O controls. Not yet!
*/
kdbioctl(dev, cmd, flag, data)
dev_t dev;
int cmd, flag;
caddr_t data;
{
int error = 0;
register int unit = kdbunit(dev);
if (unit >= NKRA || uddinfo[unit] == NULL)
return (ENXIO);
switch (cmd) {
case KDBIOCREPLACE:
/*
* Initiate bad block replacement for the given LBN.
* (Should we allow modifiers?)
*/
error = EOPNOTSUPP;
break;
case KDBIOCGMICRO:
/*
* Return the microcode revision for the KDB50 running
* this drive.
*/
*(int *)data = kdbinfo[kdbdinfo[unit]->ui_ctlr].ki_micro;
break;
case KDBIOCGSIZE:
/*
* Return the size (in 512 byte blocks) of this
* disk drive.
*/
*(daddr_t *)data = ra_dsize[unit];
break;
default:
error = EINVAL;
break;
}
return (error);
}
#endif
#ifdef notyet
/*
* Reset a KDB50 (self test and all).
* What if it fails?
*/
kdbreset(ki)
register struct kdbinfo *ki;
{
printf("reset kdb%d", ki->ki_ctlr);
bi_selftest(&ki->ki_kdb.kdb_bi);
ki->ki_state = ST_IDLE;
rminit(ki->ki_map, (long)KI_PTES, (long)1, "kdb", KI_MAPSIZ);
mscp_requeue(&ki->ki_mi);
if (kdbinit(ctlr))
printf(" (hung)");
printf("\n");
}
#endif
/*
* Watchdog timer: If the controller is active, and no interrupts
* have occurred for 30 seconds, assume it has gone away.
*/
kdbwatch()
{
register struct kdbinfo *ki;
register int i;
timeout(kdbwatch, (caddr_t)0, hz); /* every second */
for (i = 0, ki = kdbinfo; i < NKDB; i++, ki++) {
if ((ki->ki_flags & KDB_ALIVE) == 0)
continue;
if (ki->ki_state == ST_IDLE)
continue;
if (ki->ki_state == ST_RUN && !ki->ki_tab.b_active)
ki->ki_wticks = 0;
else if (++ki->ki_wticks >= 30) {
ki->ki_wticks = 0;
printf("kdb%d: lost interrupt\n", i);
/* kdbreset(ki); */
panic("kdb lost interrupt");
}
}
}
/*
* Do a panic dump.
*/
#define DBSIZE 32 /* dump 16K at a time */
struct kdbdumpspace {
struct kdb1ca kd_ca;
struct mscp kd_rsp;
struct mscp kd_cmd;
} kdbdumpspace;
kdbdump(dev)
dev_t dev;
{
register struct kdbdumpspace *kd;
register struct kdb_regs *k;
register int i;
struct uba_device *ui;
char *start;
int num, blk, unit, maxsz, blkoff;
/*
* Make sure the device is a reasonable place on which to dump.
*/
unit = kdbunit(dev);
if (unit >= NKRA)
return (ENXIO);
ui = PHYS(struct uba_device *, kdbdinfo[unit]);
if (ui == NULL || ui->ui_alive == 0)
return (ENXIO);
/*
* Find and initialise the KDB; get the physical address of the
* device registers, and of communications area and command and
* response packet.
*/
k = PHYS(struct kdbinfo *, &kdbinfo[ui->ui_ctlr])->ki_physkdb;
kd = PHYS(struct kdbdumpspace *, &kdbdumpspace);
/*
* Initialise the controller, with one command and one response
* packet.
*/
bi_reset(&k->kdb_bi);
if (kdbdumpwait(k, KDB_STEP1))
return (EFAULT);
k->kdb_sw = KDB_ERR;
if (kdbdumpwait(k, KDB_STEP2))
return (EFAULT);
k->kdb_sw = (int)&kd->kd_ca.ca_rspdsc;
if (kdbdumpwait(k, KDB_STEP3))
return (EFAULT);
k->kdb_sw = ((int)&kd->kd_ca.ca_rspdsc) >> 16;
if (kdbdumpwait(k, KDB_STEP4))
return (EFAULT);
k->kdb_sw = KDB_GO;
/*
* Set up the command and response descriptor, then set the
* controller characteristics and bring the drive on line.
* Note that all uninitialised locations in kd_cmd are zero.
*/
kd->kd_ca.ca_rspdsc = (long)&kd->kd_rsp.mscp_cmdref;
kd->kd_ca.ca_cmddsc = (long)&kd->kd_cmd.mscp_cmdref;
/* kd->kd_cmd.mscp_sccc.sccc_ctlrflags = 0; */
/* kd->kd_cmd.mscp_sccc.sccc_version = 0; */
if (kdbdumpcmd(M_OP_SETCTLRC, k, kd, ui->ui_ctlr))
return (EFAULT);
kd->kd_cmd.mscp_unit = ui->ui_slave;
if (kdbdumpcmd(M_OP_ONLINE, k, kd, ui->ui_ctlr))
return (EFAULT);
/*
* Pick up the drive type from the on line end packet;
* convert that to a dump area size and a disk offset.
* Note that the assembler uses pc-relative addressing
* to get at kdbtypes[], no need for PHYS().
*/
i = kd->kd_rsp.mscp_onle.onle_drivetype;
if (i >= NTYPES || kdbtypes[i].ut_name == 0) {
printf("disk type %d unknown\ndump ");
return (EINVAL);
}
printf("on %s ", kdbtypes[i].ut_name);
maxsz = kdbtypes[i].ut_sizes[kdbpart(dev)].nblocks;
blkoff = kdbtypes[i].ut_sizes[kdbpart(dev)].blkoff;
/*
* Dump all of physical memory, or as much as will fit in the
* space provided.
*/
start = 0;
num = maxfree;
if (dumplo < 0)
return (EINVAL);
if (dumplo + num >= maxsz)
num = maxsz - dumplo;
blkoff += dumplo;
/*
* Write out memory, DBSIZE pages at a time.
* N.B.: this code depends on the fact that the sector
* size == the page size.
*/
while (num > 0) {
blk = num > DBSIZE ? DBSIZE : num;
kd->kd_cmd.mscp_unit = ui->ui_slave;
kd->kd_cmd.mscp_seq.seq_lbn = btop(start) + blkoff;
kd->kd_cmd.mscp_seq.seq_bytecount = blk << PGSHIFT;
kd->kd_cmd.mscp_seq.seq_buffer = (long)start | KDB_PHYS;
if (kdbdumpcmd(M_OP_WRITE, k, kd, ui->ui_ctlr))
return (EIO);
start += blk << PGSHIFT;
num -= blk;
}
return (0); /* made it! */
}
/*
* Wait for some of the bits in `bits' to come on. If the error bit
* comes on, or ten seconds pass without response, return true (error).
*/
kdbdumpwait(k, bits)
register struct kdb_regs *k;
register int bits;
{
register int timo = todr() + 1000;
while ((k->kdb_sa & bits) == 0) {
if (k->kdb_sa & KDB_ERR) {
printf("kdb_sa=%b\ndump ", k->kdb_sa, kdbsr_bits);
return (1);
}
if (todr() >= timo) {
printf("timeout\ndump ");
return (1);
}
}
return (0);
}
/*
* Feed a command to the KDB50, wait for its response, and return
* true iff something went wrong.
*/
kdbdumpcmd(op, k, kd, ctlr)
int op;
register struct kdb_regs *k;
register struct kdbdumpspace *kd;
int ctlr;
{
register int n;
#define mp (&kd->kd_rsp)
kd->kd_cmd.mscp_opcode = op;
kd->kd_cmd.mscp_msglen = MSCP_MSGLEN;
kd->kd_rsp.mscp_msglen = MSCP_MSGLEN;
kd->kd_ca.ca_rspdsc |= MSCP_OWN | MSCP_INT;
kd->kd_ca.ca_cmddsc |= MSCP_OWN | MSCP_INT;
if (k->kdb_sa & KDB_ERR) {
printf("kdb_sa=%b\ndump ", k->kdb_sa, kdbsr_bits);
return (1);
}
n = k->kdb_ip;
n = todr() + 1000;
for (;;) {
if (todr() > n) {
printf("timeout\ndump ");
return (1);
}
if (kd->kd_ca.ca_cmdint)
kd->kd_ca.ca_cmdint = 0;
if (kd->kd_ca.ca_rspint == 0)
continue;
kd->kd_ca.ca_rspint = 0;
if (mp->mscp_opcode == (op | M_OP_END))
break;
printf("\n");
switch (MSCP_MSGTYPE(mp->mscp_msgtc)) {
case MSCPT_SEQ:
printf("sequential");
break;
case MSCPT_DATAGRAM:
mscp_decodeerror("kdb", ctlr, mp);
printf("datagram");
break;
case MSCPT_CREDITS:
printf("credits");
break;
case MSCPT_MAINTENANCE:
printf("maintenance");
break;
default:
printf("unknown (type 0x%x)",
MSCP_MSGTYPE(mp->mscp_msgtc));
break;
}
printf(" ignored\ndump ");
kd->kd_ca.ca_rspdsc |= MSCP_OWN | MSCP_INT;
}
if ((mp->mscp_status & M_ST_MASK) != M_ST_SUCCESS) {
printf("error: op 0x%x => 0x%x status 0x%x\ndump ", op,
mp->mscp_opcode, mp->mscp_status);
return (1);
}
return (0);
#undef mp
}
/*
* Return the size of a partition, if known, or -1 if not.
*/
kdbsize(dev)
dev_t dev;
{
register int unit = kdbunit(dev);
register struct uba_device *ui;
register struct size *st;
if (unit >= NKRA || (ui = kdbdinfo[unit]) == NULL || ui->ui_alive == 0)
return (-1);
st = &kdbtypes[ui->ui_type].ut_sizes[kdbpart(dev)];
if (st->nblocks == -1) {
int s = spl5();
/*
* We need to have the drive on line to find the size
* of this particular partition.
* IS IT OKAY TO GO TO SLEEP IN THIS ROUTINE?
* (If not, better not page on one of these...)
*/
if ((ui->ui_flags & UNIT_ONLINE) == 0) {
if (kdb_bringonline(ui, 0)) {
splx(s);
return (-1);
}
}
splx(s);
if (st->blkoff > ra_dsize[unit])
return (-1);
return (ra_dsize[unit] - st->blkoff);
}
return (st->nblocks);
}
#endif NKDB > 0