401 lines
7.8 KiB
C
401 lines
7.8 KiB
C
#ifndef lint
|
|
static char sccsid[] = "@(#)simdisk.c 1.1 92/07/30";
|
|
#endif
|
|
|
|
/*
|
|
* Copyright (c) 1987 by Sun Microsystems, Inc.
|
|
*/
|
|
|
|
/*
|
|
* Dummy driver for sparc simulated disks.
|
|
* uses systems calls via simulator to accomplish
|
|
* base level open, seek, read, and write operations
|
|
*/
|
|
|
|
int errno;
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/dk.h>
|
|
#include <sys/buf.h>
|
|
#include <sys/conf.h>
|
|
#include <sys/dir.h>
|
|
#include <sys/user.h>
|
|
#include <sys/map.h>
|
|
#include <sys/vmmac.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/uio.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/dkbad.h>
|
|
#include <sys/file.h>
|
|
|
|
#include <machine/pte.h>
|
|
#include <machine/psl.h>
|
|
#include <machine/mmu.h>
|
|
#include <machine/cpu.h>
|
|
#include <vm/page.h>
|
|
|
|
#include <sun/dklabel.h>
|
|
#include <sun/dkio.h>
|
|
|
|
#include <sundev/mbvar.h>
|
|
|
|
#include <sas_simdef.h> /* Defintions for real devices used by simulator */
|
|
|
|
#ifdef IOMMU
|
|
#include <machine/iommu.h>
|
|
#endif IOMMU
|
|
|
|
int nsimd = 0;
|
|
|
|
int simdidentify();
|
|
int simdattach();
|
|
int simdintr();
|
|
int simdstart();
|
|
|
|
#define SIMDISK_OK 0
|
|
#define SIMDISK_RD 1
|
|
#define SIMDISK_WR 2
|
|
#define SIMDISK_ERR 3
|
|
|
|
struct dev_ops simd_ops = {
|
|
1,
|
|
simdidentify,
|
|
simdattach
|
|
};
|
|
|
|
int
|
|
simdidentify(name)
|
|
char *name;
|
|
{
|
|
#if defined(SIMD_DEBUG)
|
|
printf("simdidentify: <%s> ", name);
|
|
#endif
|
|
if (strcmp(name, "simd")) {
|
|
#if defined(SIMD_DEBUG)
|
|
printf("rejected\n\r", name);
|
|
#endif
|
|
return 0;
|
|
}
|
|
#if defined(SIMD_DEBUG)
|
|
printf("recognized\n\r");
|
|
#endif
|
|
nsimd ++;
|
|
return 1;
|
|
}
|
|
|
|
union simcsr {
|
|
struct csr {
|
|
u_int cnt : 28; /* number of bytes to xfer/xferd */
|
|
u_int cmd : 2; /* read or write */
|
|
u_int ie : 1; /* interrupts enabled */
|
|
u_int go : 1; /* do the transfer */
|
|
} s;
|
|
unsigned w;
|
|
};
|
|
|
|
struct simdregs {
|
|
union simcsr csr;
|
|
unsigned saddr;
|
|
unsigned part;
|
|
unsigned block;
|
|
};
|
|
|
|
#define NPARTS 8
|
|
#define UNIT(dev) (((dev)&0x38) >> 3)
|
|
#define PART(dev) ((dev)&7)
|
|
#define DEV(u,p) (((u)<<3)|(p))
|
|
|
|
struct simdisk {
|
|
struct simdregs *regs; /* mapped controller registers */
|
|
int sizes[NPARTS]; /* partition sizes */
|
|
|
|
struct buf *foq; /* first on queue */
|
|
struct buf *loq; /* last on queue */
|
|
struct buf *act; /* current buf active */
|
|
|
|
unsigned addr; /* base of dvma region */
|
|
unsigned offset; /* total transferred so far */
|
|
} *simdunits = 0;
|
|
|
|
int
|
|
simdattach(dev)
|
|
struct dev_info *dev;
|
|
{
|
|
struct simdisk *sd;
|
|
static int unit = -1;
|
|
register int part;
|
|
int nreg, nint;
|
|
|
|
unit ++;
|
|
|
|
#if defined(SIMD_DEBUG)
|
|
printf("simdidentify: attaching simd%d\n", unit);
|
|
#endif
|
|
|
|
if (!simdunits) {
|
|
simdunits = (struct simdisk *)
|
|
new_kmem_zalloc((u_int)(nsimd * sizeof (struct simdisk)),
|
|
KMEM_SLEEP);
|
|
if (!simdunits) {
|
|
printf ("simd: no space for structures\n");
|
|
return -1;
|
|
}
|
|
}
|
|
dev->devi_unit = unit;
|
|
sd = simdunits + unit;
|
|
nreg = dev->devi_nreg;
|
|
if (nreg != 1) {
|
|
printf("simd: regs not specified correctly\n");
|
|
return -1;
|
|
}
|
|
/*
|
|
* assumption: if we have different "simd" nodes, then
|
|
* they talk via different registers to the simulator,
|
|
* and hence get different "spindles".
|
|
*/
|
|
sd->regs = (struct simdregs *)
|
|
map_regs(dev->devi_reg->reg_addr,
|
|
dev->devi_reg->reg_size,
|
|
dev->devi_reg->reg_bustype);
|
|
if (!sd->regs) {
|
|
printf("simd: unable to map registers\n");
|
|
return -1;
|
|
}
|
|
for (part=0; part<NPARTS; ++part) {
|
|
sd->regs->part = part;
|
|
sd->sizes[part] = sd->regs->block;
|
|
#if defined(SIMD_DEBUG)
|
|
printf("simd%d: partition %d has %d blocks\n",
|
|
unit, part, sd->sizes[part]);
|
|
#endif
|
|
}
|
|
nint = dev->devi_nintr;
|
|
if (nint != 1) {
|
|
printf("simd: ints not specified correctly for unit %d\n",
|
|
unit);
|
|
return -1;
|
|
}
|
|
addintr(dev->devi_intr->int_pri, simdintr, dev->devi_name, unit);
|
|
report_dev(dev);
|
|
return 0;
|
|
}
|
|
|
|
/*ARGSUSED*/
|
|
simdopen(dev, flag)
|
|
register dev_t dev;
|
|
register int flag;
|
|
{
|
|
register int unit = UNIT(dev);
|
|
register int part = PART(dev);
|
|
|
|
if (unit >= nsimd) {
|
|
printf("Illegal simulated disk unit (%d of %d).\n",
|
|
unit, nsimd);
|
|
return (ENXIO);
|
|
}
|
|
|
|
if (part >= NPARTS) {
|
|
printf("Illegal simulated disk partition (%d of %d).\n",
|
|
part, NPARTS);
|
|
return (ENXIO);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
simdsize(dev)
|
|
register dev_t dev;
|
|
{
|
|
int unit = UNIT(dev);
|
|
int part = PART(dev);
|
|
int rv;
|
|
|
|
if (!simdunits) {
|
|
#if defined(SIMD_DEBUG)
|
|
printf("simdsize: simdunits not allocated\n");
|
|
#endif
|
|
return 0;
|
|
}
|
|
if (unit >= nsimd) {
|
|
#if defined(SIMD_DEBUG)
|
|
printf("simdsize: bad unit (%d of %d)\n",
|
|
unit, nsimd);
|
|
#endif
|
|
return 0;
|
|
}
|
|
if (part >= NPARTS) {
|
|
#if defined(SIMD_DEBUG)
|
|
printf("simdsize: bad part (%d of %d)\n",
|
|
part, NPARTS);
|
|
#endif
|
|
return 0;
|
|
}
|
|
rv = simdunits[unit].sizes[part];
|
|
#if defined(SIMD_DEBUG)
|
|
printf("simdsize: simd%d%c has %d blocks\n",
|
|
unit, part + 'a', rv);
|
|
#endif
|
|
return rv;
|
|
}
|
|
|
|
simdstrategy(bp)
|
|
register struct buf *bp;
|
|
{
|
|
register int unit; /* unit number */
|
|
register int part; /* partition number */
|
|
struct simdisk *sd;
|
|
int s; /* psr bits for splx */
|
|
|
|
unit = UNIT(bp->b_dev);
|
|
part = PART(bp->b_dev);
|
|
sd = simdunits + unit;
|
|
|
|
if (bp->b_bcount & (DEV_BSIZE - 1)) {
|
|
printf("simd%d%c: non-block size i/o request\n", unit, part+'a');
|
|
goto bad;
|
|
}
|
|
|
|
s = splhigh(); /* protect from simdintr */
|
|
if (!sd->foq) {
|
|
sd->foq = bp;
|
|
} else {
|
|
sd->loq->av_forw = bp;
|
|
}
|
|
bp->av_forw = NULL;
|
|
sd->loq = bp;
|
|
simdstart(sd);
|
|
splx(s); /* end of critical section */
|
|
|
|
return;
|
|
bad:
|
|
bp->b_flags |= B_ERROR;
|
|
iodone(bp);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* simdstart: start a transfer.
|
|
* must be at a high enough priority
|
|
* that the sd->foq/sd->eoq structure
|
|
* is protected from simdintr().
|
|
* called from simdintr() and
|
|
* from simdstrategy().
|
|
*/
|
|
simdstart(sd)
|
|
register struct simdisk *sd;
|
|
{
|
|
register struct buf *bp; /* buf we want to transfer */
|
|
register u_int addr; /* dvma cookie */
|
|
|
|
for (bp = sd->foq; bp && !sd->act; bp = bp->av_forw) {
|
|
addr = mb_mapalloc(dvmamap, bp, SBUS_MAP,
|
|
simdstart, (caddr_t)sd);
|
|
if (!addr)
|
|
break;
|
|
bp->b_resid = bp->b_bcount;
|
|
sd->act = bp;
|
|
sd->addr = addr;
|
|
sd->offset = 0;
|
|
simdgo(sd);
|
|
}
|
|
sd->foq = bp;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* simdgo: sd->{act,addr,offset} refer to a transfer that needs
|
|
* to be started. inform the simulator.
|
|
*/
|
|
simdgo(sd)
|
|
struct simdisk *sd;
|
|
{
|
|
int unit;
|
|
int part;
|
|
struct buf *bp;
|
|
unsigned addr;
|
|
union simcsr t;
|
|
unsigned block;
|
|
unsigned count;
|
|
|
|
bp = sd->act;
|
|
if (!bp)
|
|
return;
|
|
addr = sd->addr + sd->offset + (u_int) DVMA;
|
|
unit = minor(bp->b_dev);
|
|
block = dkblock(bp) + (sd->offset >> DEV_BSHIFT);
|
|
count = bp->b_resid - sd->offset;
|
|
|
|
/* don't transfer across pages, thanks. */
|
|
if (((addr & MMU_PAGEOFFSET) + count) > MMU_PAGESIZE)
|
|
count = MMU_PAGESIZE - (addr & MMU_PAGEOFFSET);
|
|
|
|
t.w = 0;
|
|
t.s.ie = 1;
|
|
t.s.cnt = count;
|
|
if (bp->b_flags & B_READ)
|
|
t.s.cmd = SIMDISK_RD;
|
|
else
|
|
t.s.cmd = SIMDISK_WR;
|
|
t.s.go = 1;
|
|
|
|
sd->regs->part = unit;
|
|
sd->regs->block = block;
|
|
sd->regs->saddr = addr;
|
|
sd->regs->csr = t;
|
|
}
|
|
|
|
/*
|
|
* simdintr: a transfer completed.
|
|
* update the sd->* variables; if
|
|
* there is more to do, poke simdgo();
|
|
* otherwise, tell the kernel the transfer
|
|
* has completed, and poke simdstart() to
|
|
* get the next buffer on the list.
|
|
*/
|
|
simdintr()
|
|
{
|
|
int unit;
|
|
struct simdisk *sd;
|
|
struct buf *bp;
|
|
union simcsr csr;
|
|
int count;
|
|
int rv;
|
|
|
|
rv = 0;
|
|
for (unit=0; unit<nsimd; ++unit) {
|
|
sd = simdunits + unit;
|
|
bp = sd->act;
|
|
if (!bp)
|
|
continue; /* nobody active */
|
|
csr = sd->regs->csr;
|
|
if (csr.s.go)
|
|
continue; /* still busy */
|
|
|
|
if (csr.s.cmd == SIMDISK_ERR)
|
|
bp->b_flags |= B_ERROR;
|
|
|
|
count = csr.s.cnt;
|
|
sd->offset += count;
|
|
|
|
if ((sd->offset >= bp->b_bcount) ||
|
|
(bp->b_flags & B_ERROR)) {
|
|
bp->b_resid = bp->b_bcount - sd->offset;
|
|
mb_mapfree(dvmamap, &sd->addr);
|
|
sd->act = 0;
|
|
iodone(bp);
|
|
simdstart(sd); /* do another */
|
|
} else
|
|
simdgo(sd); /* continue */
|
|
rv = 1;
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
simdcheck()
|
|
{
|
|
if (nsimd > 0)
|
|
return;
|
|
panic("simd: missing devinfo node");
|
|
}
|