vx32

Local 9vx git repository for patches.
git clone git://r-36.net/vx32
Log | Files | Refs

commit 7414e9996dc38431669b1da77013891e3f8940d6
parent b73f901985b9aaada2c11d977276a2d064555048
Author: Russ Cox <rsc@swtch.com>
Date:   Sat, 28 Jun 2008 11:40:18 -0400

9vx/OSX: add devsd, add device size code to devfs-posix

Diffstat:
src/9vx/Makefrag | 3+++
src/9vx/a/devsd.c | 1645+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/9vx/a/sd.h | 137+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/9vx/a/sdscsi.c | 424+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/9vx/devfs-posix.c | 62++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/9vx/devtab.c | 2++
src/9vx/sdloop.c | 274+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
7 files changed, 2547 insertions(+), 0 deletions(-)

diff --git a/src/9vx/Makefrag b/src/9vx/Makefrag @@ -45,6 +45,7 @@ PLAN9_OBJS = \ main.o \ mmu.o \ sched.o \ + sdloop.o \ stub.o \ term.o \ time.o \ @@ -74,6 +75,7 @@ PLAN9_A_OBJS = \ devproc.o \ devpipe.o \ devroot.o \ + devsd.o \ devsrv.o \ devssl.o \ devtls.o \ @@ -93,6 +95,7 @@ PLAN9_A_OBJS = \ proc.o \ qio.o \ qlock.o \ + sdscsi.o \ segment.o \ strecpy.o \ sysfile.o \ diff --git a/src/9vx/a/devsd.c b/src/9vx/a/devsd.c @@ -0,0 +1,1645 @@ +/* + * Storage Device. + */ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" +#include "error.h" + +#include "sd.h" + +extern Dev sddevtab; +extern SDifc* sdifc[]; + +static char Echange[] = "media or partition has changed"; + +static char devletters[] = "0123456789" + "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + +static SDev *devs[sizeof devletters-1]; +static QLock devslock; + +enum { + Rawcmd, + Rawdata, + Rawstatus, +}; + +enum { + Qtopdir = 1, /* top level directory */ + Qtopbase, + Qtopctl = Qtopbase, + + Qunitdir, /* directory per unit */ + Qunitbase, + Qctl = Qunitbase, + Qraw, + Qpart, + + TypeLOG = 4, + NType = (1<<TypeLOG), + TypeMASK = (NType-1), + TypeSHIFT = 0, + + PartLOG = 8, + NPart = (1<<PartLOG), + PartMASK = (NPart-1), + PartSHIFT = TypeLOG, + + UnitLOG = 8, + NUnit = (1<<UnitLOG), + UnitMASK = (NUnit-1), + UnitSHIFT = (PartLOG+TypeLOG), + + DevLOG = 8, + NDev = (1 << DevLOG), + DevMASK = (NDev-1), + DevSHIFT = (UnitLOG+PartLOG+TypeLOG), + + Ncmd = 20, +}; + +#define TYPE(q) ((((ulong)(q).path)>>TypeSHIFT) & TypeMASK) +#define PART(q) ((((ulong)(q).path)>>PartSHIFT) & PartMASK) +#define UNIT(q) ((((ulong)(q).path)>>UnitSHIFT) & UnitMASK) +#define DEV(q) ((((ulong)(q).path)>>DevSHIFT) & DevMASK) +#define QID(d,u, p, t) (((d)<<DevSHIFT)|((u)<<UnitSHIFT)|\ + ((p)<<PartSHIFT)|((t)<<TypeSHIFT)) + + +static void +sdaddpart(SDunit* unit, char* name, uvlong start, uvlong end) +{ + SDpart *pp; + int i, partno; + + /* + * Check name not already used + * and look for a free slot. + */ + if(unit->part != nil){ + partno = -1; + for(i = 0; i < unit->npart; i++){ + pp = &unit->part[i]; + if(!pp->valid){ + if(partno == -1) + partno = i; + break; + } + if(strcmp(name, pp->perm.name) == 0){ + if(pp->start == start && pp->end == end) + return; + error(Ebadctl); + } + } + } + else{ + if((unit->part = malloc(sizeof(SDpart)*SDnpart)) == nil) + error(Enomem); + unit->npart = SDnpart; + partno = 0; + } + + /* + * If no free slot found then increase the + * array size (can't get here with unit->part == nil). + */ + if(partno == -1){ + if(unit->npart >= NPart) + error(Enomem); + if((pp = malloc(sizeof(SDpart)*(unit->npart+SDnpart))) == nil) + error(Enomem); + memmove(pp, unit->part, sizeof(SDpart)*unit->npart); + free(unit->part); + unit->part = pp; + partno = unit->npart; + unit->npart += SDnpart; + } + + /* + * Check size and extent are valid. + */ + if(start > end || end > unit->sectors) + error(Eio); + pp = &unit->part[partno]; + pp->start = start; + pp->end = end; + kstrdup(&pp->perm.name, name); + kstrdup(&pp->perm.user, eve); + pp->perm.perm = 0640; + pp->valid = 1; +} + +static void +sddelpart(SDunit* unit, char* name) +{ + int i; + SDpart *pp; + + /* + * Look for the partition to delete. + * Can't delete if someone still has it open. + */ + pp = unit->part; + for(i = 0; i < unit->npart; i++){ + if(strcmp(name, pp->perm.name) == 0) + break; + pp++; + } + if(i >= unit->npart) + error(Ebadctl); + if(strcmp(up->user, pp->perm.user) && !iseve()) + error(Eperm); + pp->valid = 0; + pp->vers++; +} + +static void +sdincvers(SDunit *unit) +{ + int i; + + unit->vers++; + if(unit->part){ + for(i = 0; i < unit->npart; i++){ + unit->part[i].valid = 0; + unit->part[i].vers++; + } + } +} + +static int +sdinitpart(SDunit* unit) +{ + int nf; + uvlong start, end; + char *f[4], *p, *q, buf[10]; + + if(unit->sectors > 0){ + unit->sectors = unit->secsize = 0; + sdincvers(unit); + } + + if(unit->inquiry[0] & 0xC0) + return 0; + switch(unit->inquiry[0] & 0x1F){ + case 0x00: /* DA */ + case 0x04: /* WORM */ + case 0x05: /* CD-ROM */ + case 0x07: /* MO */ + break; + default: + return 0; + } + + if(unit->dev->ifc->online) + unit->dev->ifc->online(unit); + if(unit->sectors){ + sdincvers(unit); + sdaddpart(unit, "data", 0, unit->sectors); +#if 0 + /* + * Use partitions passed from boot program, + * e.g. + * sdC0part=dos 63 123123/plan9 123123 456456 + * This happens before /boot sets hostname so the + * partitions will have the null-string for user. + * The gen functions patch it up. + */ + snprint(buf, sizeof buf, "%spart", unit->perm.name); + for(p = getconf(buf); p != nil; p = q){ + if(q = strchr(p, '/')) + *q++ = '\0'; + nf = tokenize(p, f, nelem(f)); + if(nf < 3) + continue; + + start = strtoull(f[1], 0, 0); + end = strtoull(f[2], 0, 0); + if(!waserror()){ + sdaddpart(unit, f[0], start, end); + poperror(); + } + } +#endif + } + + return 1; +} + +static int +sdindex(int idno) +{ + char *p; + + p = strchr(devletters, idno); + if(p == nil) + return -1; + return p-devletters; +} + +static SDev* +sdgetdev(int idno) +{ + SDev *sdev; + int i; + + if((i = sdindex(idno)) < 0) + return nil; + + qlock(&devslock); + if(sdev = devs[i]) + incref(&sdev->r); + qunlock(&devslock); + return sdev; +} + +static SDunit* +sdgetunit(SDev* sdev, int subno) +{ + SDunit *unit; + char buf[32]; + + /* + * Associate a unit with a given device and sub-unit + * number on that device. + * The device will be probed if it has not already been + * successfully accessed. + */ + qlock(&sdev->unitlock); + if(subno > sdev->nunit){ + qunlock(&sdev->unitlock); + return nil; + } + + unit = sdev->unit[subno]; + if(unit == nil){ + /* + * Probe the unit only once. This decision + * may be a little severe and reviewed later. + */ + if(sdev->unitflg[subno]){ + qunlock(&sdev->unitlock); + return nil; + } + if((unit = malloc(sizeof(SDunit))) == nil){ + qunlock(&sdev->unitlock); + return nil; + } + sdev->unitflg[subno] = 1; + + snprint(buf, sizeof(buf), "%s%d", sdev->name, subno); + kstrdup(&unit->perm.name, buf); + kstrdup(&unit->perm.user, eve); + unit->perm.perm = 0555; + unit->subno = subno; + unit->dev = sdev; + + if(sdev->enabled == 0 && sdev->ifc->enable) + sdev->ifc->enable(sdev); + sdev->enabled = 1; + + /* + * No need to lock anything here as this is only + * called before the unit is made available in the + * sdunit[] array. + */ + if(unit->dev->ifc->verify(unit) == 0){ + qunlock(&sdev->unitlock); + free(unit); + return nil; + } + sdev->unit[subno] = unit; + } + qunlock(&sdev->unitlock); + return unit; +} + +static void +sdreset(void) +{ + int i; + SDev *sdev; + + /* + * Probe all known controller types and register any devices found. + */ + for(i = 0; sdifc[i] != nil; i++){ + if(sdifc[i]->pnp == nil || (sdev = sdifc[i]->pnp()) == nil) + continue; + sdadddevs(sdev); + } +} + +void +sdadddevs(SDev *sdev) +{ + int i, j, id; + SDev *next; + + for(; sdev; sdev=next){ + next = sdev->next; + + sdev->unit = (SDunit**)malloc(sdev->nunit * sizeof(SDunit*)); + sdev->unitflg = (int*)malloc(sdev->nunit * sizeof(int)); + if(sdev->unit == nil || sdev->unitflg == nil){ + print("sdadddevs: out of memory\n"); + giveup: + free(sdev->unit); + free(sdev->unitflg); + if(sdev->ifc->clear) + sdev->ifc->clear(sdev); + free(sdev); + continue; + } + id = sdindex(sdev->idno); + if(id == -1){ + print("sdadddevs: bad id number %d (%C)\n", id, id); + goto giveup; + } + qlock(&devslock); + for(i=0; i<nelem(devs); i++){ + if(devs[j = (id+i)%nelem(devs)] == nil){ + sdev->idno = devletters[j]; + devs[j] = sdev; + snprint(sdev->name, sizeof sdev->name, "sd%c", devletters[j]); + break; + } + } + qunlock(&devslock); + if(i == nelem(devs)){ + print("sdadddevs: out of device letters\n"); + goto giveup; + } + } +} + +// void +// sdrmdevs(SDev *sdev) +// { +// char buf[2]; +// +// snprint(buf, sizeof buf, "%c", sdev->idno); +// unconfigure(buf); +// } + +static int +sd2gen(Chan* c, int i, Dir* dp) +{ + Qid q; + uvlong l; + SDpart *pp; + SDperm *perm; + SDunit *unit; + SDev *sdev; + int rv; + + sdev = sdgetdev(DEV(c->qid)); + assert(sdev); + unit = sdev->unit[UNIT(c->qid)]; + + rv = -1; + switch(i){ + case Qctl: + mkqid(&q, QID(DEV(c->qid), UNIT(c->qid), PART(c->qid), Qctl), + unit->vers, QTFILE); + perm = &unit->ctlperm; + if(emptystr(perm->user)){ + kstrdup(&perm->user, eve); + perm->perm = 0640; + } + devdir(c, q, "ctl", 0, perm->user, perm->perm, dp); + rv = 1; + break; + + case Qraw: + mkqid(&q, QID(DEV(c->qid), UNIT(c->qid), PART(c->qid), Qraw), + unit->vers, QTFILE); + perm = &unit->rawperm; + if(emptystr(perm->user)){ + kstrdup(&perm->user, eve); + perm->perm = DMEXCL|0600; + } + devdir(c, q, "raw", 0, perm->user, perm->perm, dp); + rv = 1; + break; + + case Qpart: + pp = &unit->part[PART(c->qid)]; + l = (pp->end - pp->start) * unit->secsize; + mkqid(&q, QID(DEV(c->qid), UNIT(c->qid), PART(c->qid), Qpart), + unit->vers+pp->vers, QTFILE); + if(emptystr(pp->perm.user)) + kstrdup(&pp->perm.user, eve); + devdir(c, q, pp->perm.name, l, pp->perm.user, pp->perm.perm, dp); + rv = 1; + break; + } + + decref(&sdev->r); + return rv; +} + +static int +sd1gen(Chan* c, int i, Dir* dp) +{ + Qid q; + + switch(i){ + case Qtopctl: + mkqid(&q, QID(0, 0, 0, Qtopctl), 0, QTFILE); + devdir(c, q, "sdctl", 0, eve, 0640, dp); + return 1; + } + return -1; +} + +static int +sdgen(Chan* c, char *name, Dirtab *dt, int j, int s, Dir* dp) +{ + Qid q; + uvlong l; + int i, r; + SDpart *pp; + SDunit *unit; + SDev *sdev; + + switch(TYPE(c->qid)){ + case Qtopdir: + if(s == DEVDOTDOT){ + mkqid(&q, QID(0, 0, 0, Qtopdir), 0, QTDIR); + sprint(up->genbuf, "#%C", sddevtab.dc); + devdir(c, q, up->genbuf, 0, eve, 0555, dp); + return 1; + } + + if(s+Qtopbase < Qunitdir) + return sd1gen(c, s+Qtopbase, dp); + s -= (Qunitdir-Qtopbase); + + qlock(&devslock); + for(i=0; i<nelem(devs); i++){ + if(devs[i]){ + if(s < devs[i]->nunit) + break; + s -= devs[i]->nunit; + } + } + + if(i == nelem(devs)){ + /* Run off the end of the list */ + qunlock(&devslock); + return -1; + } + + if((sdev = devs[i]) == nil){ + qunlock(&devslock); + return 0; + } + + incref(&sdev->r); + qunlock(&devslock); + + if((unit = sdev->unit[s]) == nil) + if((unit = sdgetunit(sdev, s)) == nil){ + decref(&sdev->r); + return 0; + } + + mkqid(&q, QID(sdev->idno, s, 0, Qunitdir), 0, QTDIR); + if(emptystr(unit->perm.user)) + kstrdup(&unit->perm.user, eve); + devdir(c, q, unit->perm.name, 0, unit->perm.user, unit->perm.perm, dp); + decref(&sdev->r); + return 1; + + case Qunitdir: + if(s == DEVDOTDOT){ + mkqid(&q, QID(0, 0, 0, Qtopdir), 0, QTDIR); + sprint(up->genbuf, "#%C", sddevtab.dc); + devdir(c, q, up->genbuf, 0, eve, 0555, dp); + return 1; + } + + if((sdev = sdgetdev(DEV(c->qid))) == nil){ + devdir(c, c->qid, "unavailable", 0, eve, 0, dp); + return 1; + } + + unit = sdev->unit[UNIT(c->qid)]; + qlock(&unit->ctl); + + /* + * Check for media change. + * If one has already been detected, sectors will be zero. + * If there is one waiting to be detected, online + * will return > 1. + * Online is a bit of a large hammer but does the job. + */ + if(unit->sectors == 0 + || (unit->dev->ifc->online && unit->dev->ifc->online(unit) > 1)) + sdinitpart(unit); + + i = s+Qunitbase; + if(i < Qpart){ + r = sd2gen(c, i, dp); + qunlock(&unit->ctl); + decref(&sdev->r); + return r; + } + i -= Qpart; + if(unit->part == nil || i >= unit->npart){ + qunlock(&unit->ctl); + decref(&sdev->r); + break; + } + pp = &unit->part[i]; + if(!pp->valid){ + qunlock(&unit->ctl); + decref(&sdev->r); + return 0; + } + l = (pp->end - pp->start) * unit->secsize; + mkqid(&q, QID(DEV(c->qid), UNIT(c->qid), i, Qpart), + unit->vers+pp->vers, QTFILE); + if(emptystr(pp->perm.user)) + kstrdup(&pp->perm.user, eve); + devdir(c, q, pp->perm.name, l, pp->perm.user, pp->perm.perm, dp); + qunlock(&unit->ctl); + decref(&sdev->r); + return 1; + case Qraw: + case Qctl: + case Qpart: + if((sdev = sdgetdev(DEV(c->qid))) == nil){ + devdir(c, q, "unavailable", 0, eve, 0, dp); + return 1; + } + unit = sdev->unit[UNIT(c->qid)]; + qlock(&unit->ctl); + r = sd2gen(c, TYPE(c->qid), dp); + qunlock(&unit->ctl); + decref(&sdev->r); + return r; + case Qtopctl: + return sd1gen(c, TYPE(c->qid), dp); + default: + break; + } + + return -1; +} + +static Chan* +sdattach(char* spec) +{ + Chan *c; + char *p; + SDev *sdev; + int idno, subno; + + if(*spec == '\0'){ + c = devattach(sddevtab.dc, spec); + mkqid(&c->qid, QID(0, 0, 0, Qtopdir), 0, QTDIR); + return c; + } + + if(spec[0] != 's' || spec[1] != 'd') + error(Ebadspec); + idno = spec[2]; + subno = strtol(&spec[3], &p, 0); + if(p == &spec[3]) + error(Ebadspec); + + if((sdev=sdgetdev(idno)) == nil) + error(Enonexist); + if(sdgetunit(sdev, subno) == nil){ + decref(&sdev->r); + error(Enonexist); + } + + c = devattach(sddevtab.dc, spec); + mkqid(&c->qid, QID(sdev->idno, subno, 0, Qunitdir), 0, QTDIR); + c->dev = (sdev->idno << UnitLOG) + subno; + decref(&sdev->r); + return c; +} + +static Walkqid* +sdwalk(Chan* c, Chan* nc, char** name, int nname) +{ + return devwalk(c, nc, name, nname, nil, 0, sdgen); +} + +static int +sdstat(Chan* c, uchar* db, int n) +{ + return devstat(c, db, n, nil, 0, sdgen); +} + +static Chan* +sdopen(Chan* c, int omode) +{ + SDpart *pp; + SDunit *unit; + SDev *sdev; + uchar tp; + + c = devopen(c, omode, 0, 0, sdgen); + if((tp = TYPE(c->qid)) != Qctl && tp != Qraw && tp != Qpart) + return c; + + sdev = sdgetdev(DEV(c->qid)); + if(sdev == nil) + error(Enonexist); + + unit = sdev->unit[UNIT(c->qid)]; + + switch(TYPE(c->qid)){ + case Qctl: + c->qid.vers = unit->vers; + break; + case Qraw: + c->qid.vers = unit->vers; + if(tas(&unit->rawinuse) != 0){ + c->flag &= ~COPEN; + decref(&sdev->r); + error(Einuse); + } + unit->state = Rawcmd; + break; + case Qpart: + qlock(&unit->ctl); + if(waserror()){ + qunlock(&unit->ctl); + c->flag &= ~COPEN; + decref(&sdev->r); + nexterror(); + } + pp = &unit->part[PART(c->qid)]; + c->qid.vers = unit->vers+pp->vers; + qunlock(&unit->ctl); + poperror(); + break; + } + decref(&sdev->r); + return c; +} + +static void +sdclose(Chan* c) +{ + SDunit *unit; + SDev *sdev; + + if(c->qid.type & QTDIR) + return; + if(!(c->flag & COPEN)) + return; + + switch(TYPE(c->qid)){ + default: + break; + case Qraw: + sdev = sdgetdev(DEV(c->qid)); + if(sdev){ + unit = sdev->unit[UNIT(c->qid)]; + unit->rawinuse = 0; + decref(&sdev->r); + } + break; + } +} + +static long +sdbio(Chan* c, int write, char* a, long len, uvlong off) +{ + int nchange; + long l; + uchar *b; + SDpart *pp; + SDunit *unit; + SDev *sdev; + ulong max, nb, offset; + uvlong bno; + + sdev = sdgetdev(DEV(c->qid)); + if(sdev == nil){ + decref(&sdev->r); + error(Enonexist); + } + unit = sdev->unit[UNIT(c->qid)]; + if(unit == nil) + error(Enonexist); + + nchange = 0; + qlock(&unit->ctl); + while(waserror()){ + /* notification of media change; go around again */ + if(strcmp(up->errstr, Eio) == 0 && unit->sectors == 0 && nchange++ == 0){ + sdinitpart(unit); + continue; + } + + /* other errors; give up */ + qunlock(&unit->ctl); + decref(&sdev->r); + nexterror(); + } + pp = &unit->part[PART(c->qid)]; + if(unit->vers+pp->vers != c->qid.vers) + error(Echange); + + /* + * Check the request is within bounds. + * Removeable drives are locked throughout the I/O + * in case the media changes unexpectedly. + * Non-removeable drives are not locked during the I/O + * to allow the hardware to optimise if it can; this is + * a little fast and loose. + * It's assumed that non-removeable media parameters + * (sectors, secsize) can't change once the drive has + * been brought online. + */ + bno = (off/unit->secsize) + pp->start; + nb = ((off+len+unit->secsize-1)/unit->secsize) + pp->start - bno; + max = SDmaxio/unit->secsize; + if(nb > max) + nb = max; + if(bno+nb > pp->end) + nb = pp->end - bno; + if(bno >= pp->end || nb == 0){ + if(write) + error(Eio); + qunlock(&unit->ctl); + decref(&sdev->r); + poperror(); + return 0; + } + if(!(unit->inquiry[1] & 0x80)){ + qunlock(&unit->ctl); + poperror(); + } + + b = sdmalloc(nb*unit->secsize); + if(b == nil) + error(Enomem); + if(waserror()){ + sdfree(b); + if(!(unit->inquiry[1] & 0x80)) + decref(&sdev->r); /* gadverdamme! */ + nexterror(); + } + + offset = off%unit->secsize; + if(offset+len > nb*unit->secsize) + len = nb*unit->secsize - offset; + if(write){ + if(offset || (len%unit->secsize)){ + l = unit->dev->ifc->bio(unit, 0, 0, b, nb, bno); + if(l < 0) + error(Eio); + if(l < (nb*unit->secsize)){ + nb = l/unit->secsize; + l = nb*unit->secsize - offset; + if(len > l) + len = l; + } + } + memmove(b+offset, a, len); + l = unit->dev->ifc->bio(unit, 0, 1, b, nb, bno); + if(l < 0) + error(Eio); + if(l < offset) + len = 0; + else if(len > l - offset) + len = l - offset; + } + else{ + l = unit->dev->ifc->bio(unit, 0, 0, b, nb, bno); + if(l < 0) + error(Eio); + if(l < offset) + len = 0; + else if(len > l - offset) + len = l - offset; + memmove(a, b+offset, len); + } + sdfree(b); + poperror(); + + if(unit->inquiry[1] & 0x80){ + qunlock(&unit->ctl); + poperror(); + } + + decref(&sdev->r); + return len; +} + +static long +sdrio(SDreq* r, void* a, long n) +{ + void *data; + + if(n >= SDmaxio || n < 0) + error(Etoobig); + + data = nil; + if(n){ + if((data = sdmalloc(n)) == nil) + error(Enomem); + if(r->write) + memmove(data, a, n); + } + r->data = data; + r->dlen = n; + + if(waserror()){ + sdfree(data); + r->data = nil; + nexterror(); + } + + if(r->unit->dev->ifc->rio(r) != SDok) + error(Eio); + + if(!r->write && r->rlen > 0) + memmove(a, data, r->rlen); + sdfree(data); + r->data = nil; + poperror(); + + return r->rlen; +} + +/* + * SCSI simulation for non-SCSI devices + */ +int +sdsetsense(SDreq *r, int status, int key, int asc, int ascq) +{ + int len; + SDunit *unit; + + unit = r->unit; + unit->sense[2] = key; + unit->sense[12] = asc; + unit->sense[13] = ascq; + + r->status = status; + if(status == SDcheck && !(r->flags & SDnosense)){ + /* request sense case from sdfakescsi */ + len = sizeof unit->sense; + if(len > sizeof r->sense-1) + len = sizeof r->sense-1; + memmove(r->sense, unit->sense, len); + unit->sense[2] = 0; + unit->sense[12] = 0; + unit->sense[13] = 0; + r->flags |= SDvalidsense; + return SDok; + } + return status; +} + +int +sdmodesense(SDreq *r, uchar *cmd, void *info, int ilen) +{ + int len; + uchar *data; + + /* + * Fake a vendor-specific request with page code 0, + * return the drive info. + */ + if((cmd[2] & 0x3F) != 0 && (cmd[2] & 0x3F) != 0x3F) + return sdsetsense(r, SDcheck, 0x05, 0x24, 0); + len = (cmd[7]<<8)|cmd[8]; + if(len == 0) + return SDok; + if(len < 8+ilen) + return sdsetsense(r, SDcheck, 0x05, 0x1A, 0); + if(r->data == nil || r->dlen < len) + return sdsetsense(r, SDcheck, 0x05, 0x20, 1); + data = r->data; + memset(data, 0, 8); + data[0] = ilen>>8; + data[1] = ilen; + if(ilen) + memmove(data+8, info, ilen); + r->rlen = 8+ilen; + return sdsetsense(r, SDok, 0, 0, 0); +} + +int +sdfakescsi(SDreq *r, void *info, int ilen) +{ + uchar *cmd, *p; + uvlong len; + SDunit *unit; + + cmd = r->cmd; + r->rlen = 0; + unit = r->unit; + + /* + * Rewrite read(6)/write(6) into read(10)/write(10). + */ + switch(cmd[0]){ + case 0x08: /* read */ + case 0x0A: /* write */ + cmd[9] = 0; + cmd[8] = cmd[4]; + cmd[7] = 0; + cmd[6] = 0; + cmd[5] = cmd[3]; + cmd[4] = cmd[2]; + cmd[3] = cmd[1] & 0x0F; + cmd[2] = 0; + cmd[1] &= 0xE0; + cmd[0] |= 0x20; + break; + } + + /* + * Map SCSI commands into ATA commands for discs. + * Fail any command with a LUN except INQUIRY which + * will return 'logical unit not supported'. + */ + if((cmd[1]>>5) && cmd[0] != 0x12) + return sdsetsense(r, SDcheck, 0x05, 0x25, 0); + + switch(cmd[0]){ + default: + return sdsetsense(r, SDcheck, 0x05, 0x20, 0); + + case 0x00: /* test unit ready */ + return sdsetsense(r, SDok, 0, 0, 0); + + case 0x03: /* request sense */ + if(cmd[4] < sizeof unit->sense) + len = cmd[4]; + else + len = sizeof unit->sense; + if(r->data && r->dlen >= len){ + memmove(r->data, unit->sense, len); + r->rlen = len; + } + return sdsetsense(r, SDok, 0, 0, 0); + + case 0x12: /* inquiry */ + if(cmd[4] < sizeof unit->inquiry) + len = cmd[4]; + else + len = sizeof unit->inquiry; + if(r->data && r->dlen >= len){ + memmove(r->data, unit->inquiry, len); + r->rlen = len; + } + return sdsetsense(r, SDok, 0, 0, 0); + + case 0x1B: /* start/stop unit */ + /* + * nop for now, can use power management later. + */ + return sdsetsense(r, SDok, 0, 0, 0); + + case 0x25: /* read capacity */ + if((cmd[1] & 0x01) || cmd[2] || cmd[3]) + return sdsetsense(r, SDcheck, 0x05, 0x24, 0); + if(r->data == nil || r->dlen < 8) + return sdsetsense(r, SDcheck, 0x05, 0x20, 1); + + /* + * Read capacity returns the LBA of the last sector. + */ + len = unit->sectors - 1; + p = r->data; + *p++ = len>>24; + *p++ = len>>16; + *p++ = len>>8; + *p++ = len; + len = 512; + *p++ = len>>24; + *p++ = len>>16; + *p++ = len>>8; + *p++ = len; + r->rlen = p - (uchar*)r->data; + return sdsetsense(r, SDok, 0, 0, 0); + + case 0x9E: /* long read capacity */ + if((cmd[1] & 0x01) || cmd[2] || cmd[3]) + return sdsetsense(r, SDcheck, 0x05, 0x24, 0); + if(r->data == nil || r->dlen < 8) + return sdsetsense(r, SDcheck, 0x05, 0x20, 1); + /* + * Read capcity returns the LBA of the last sector. + */ + len = unit->sectors - 1; + p = r->data; + *p++ = len>>56; + *p++ = len>>48; + *p++ = len>>40; + *p++ = len>>32; + *p++ = len>>24; + *p++ = len>>16; + *p++ = len>>8; + *p++ = len; + len = 512; + *p++ = len>>24; + *p++ = len>>16; + *p++ = len>>8; + *p++ = len; + r->rlen = p - (uchar*)r->data; + return sdsetsense(r, SDok, 0, 0, 0); + + case 0x5A: /* mode sense */ + return sdmodesense(r, cmd, info, ilen); + + case 0x28: /* read */ + case 0x2A: /* write */ + case 0x88: /* read16 */ + case 0x8a: /* write16 */ + return SDnostatus; + } +} + +static long +sdread(Chan *c, void *a, long n, vlong off) +{ + char *p, *e, *buf; + SDpart *pp; + SDunit *unit; + SDev *sdev; + ulong offset; + int i, l, m, status; + + offset = off; + switch(TYPE(c->qid)){ + default: + error(Eperm); + case Qtopctl: + m = 64*1024; /* room for register dumps */ + p = buf = malloc(m); + assert(p); + e = p + m; + qlock(&devslock); + for(i = 0; i < nelem(devs); i++){ + sdev = devs[i]; + if(sdev && sdev->ifc->rtopctl) + p = sdev->ifc->rtopctl(sdev, p, e); + } + qunlock(&devslock); + n = readstr(off, a, n, buf); + free(buf); + return n; + + case Qtopdir: + case Qunitdir: + return devdirread(c, a, n, 0, 0, sdgen); + + case Qctl: + sdev = sdgetdev(DEV(c->qid)); + if(sdev == nil) + error(Enonexist); + + unit = sdev->unit[UNIT(c->qid)]; + m = 16*1024; /* room for register dumps */ + p = malloc(m); + l = snprint(p, m, "inquiry %.48s\n", + (char*)unit->inquiry+8); + qlock(&unit->ctl); + /* + * If there's a device specific routine it must + * provide all information pertaining to night geometry + * and the garscadden trains. + */ + if(unit->dev->ifc->rctl) + l += unit->dev->ifc->rctl(unit, p+l, m-l); + if(unit->sectors == 0) + sdinitpart(unit); + if(unit->sectors){ + if(unit->dev->ifc->rctl == nil) + l += snprint(p+l, m-l, + "geometry %llud %lud\n", + unit->sectors, unit->secsize); + pp = unit->part; + for(i = 0; i < unit->npart; i++){ + if(pp->valid) + l += snprint(p+l, m-l, + "part %s %llud %llud\n", + pp->perm.name, pp->start, pp->end); + pp++; + } + } + qunlock(&unit->ctl); + decref(&sdev->r); + l = readstr(offset, a, n, p); + free(p); + return l; + + case Qraw: + sdev = sdgetdev(DEV(c->qid)); + if(sdev == nil) + error(Enonexist); + + unit = sdev->unit[UNIT(c->qid)]; + qlock(&unit->raw); + if(waserror()){ + qunlock(&unit->raw); + decref(&sdev->r); + nexterror(); + } + if(unit->state == Rawdata){ + unit->state = Rawstatus; + i = sdrio(unit->req, a, n); + } + else if(unit->state == Rawstatus){ + status = unit->req->status; + unit->state = Rawcmd; + free(unit->req); + unit->req = nil; + i = readnum(0, a, n, status, NUMSIZE); + } else + i = 0; + qunlock(&unit->raw); + decref(&sdev->r); + poperror(); + return i; + + case Qpart: + return sdbio(c, 0, a, n, off); + } +} + +static void legacytopctl(Cmdbuf*); + +static long +sdwrite(Chan* c, void* a, long n, vlong off) +{ + char *f0; + int i; + uvlong end, start; + Cmdbuf *cb; + SDifc *ifc; + SDreq *req; + SDunit *unit; + SDev *sdev; + + switch(TYPE(c->qid)){ + default: + error(Eperm); + case Qtopctl: + cb = parsecmd(a, n); + if(waserror()){ + free(cb); + nexterror(); + } + if(cb->nf == 0) + error("empty control message"); + f0 = cb->f[0]; + cb->f++; + cb->nf--; + if(strcmp(f0, "config") == 0){ + /* wormhole into ugly legacy interface */ + legacytopctl(cb); + poperror(); + free(cb); + break; + } + /* + * "ata arg..." invokes sdifc[i]->wtopctl(nil, cb), + * where sdifc[i]->name=="ata" and cb contains the args. + */ + ifc = nil; + sdev = nil; + for(i=0; sdifc[i]; i++){ + if(strcmp(sdifc[i]->name, f0) == 0){ + ifc = sdifc[i]; + sdev = nil; + goto subtopctl; + } + } + /* + * "sd1 arg..." invokes sdifc[i]->wtopctl(sdev, cb), + * where sdifc[i] and sdev match controller letter "1", + * and cb contains the args. + */ + if(f0[0]=='s' && f0[1]=='d' && f0[2] && f0[3] == 0){ + if((sdev = sdgetdev(f0[2])) != nil){ + ifc = sdev->ifc; + goto subtopctl; + } + } + error("unknown interface"); + + subtopctl: + if(waserror()){ + if(sdev) + decref(&sdev->r); + nexterror(); + } + if(ifc->wtopctl) + ifc->wtopctl(sdev, cb); + else + error(Ebadctl); + poperror(); + poperror(); + if (sdev) + decref(&sdev->r); + free(cb); + break; + + case Qctl: + cb = parsecmd(a, n); + sdev = sdgetdev(DEV(c->qid)); + if(sdev == nil) + error(Enonexist); + unit = sdev->unit[UNIT(c->qid)]; + + qlock(&unit->ctl); + if(waserror()){ + qunlock(&unit->ctl); + decref(&sdev->r); + free(cb); + nexterror(); + } + if(unit->vers != c->qid.vers) + error(Echange); + + if(cb->nf < 1) + error(Ebadctl); + if(strcmp(cb->f[0], "part") == 0){ + if(cb->nf != 4) + error(Ebadctl); + if(unit->sectors == 0 && !sdinitpart(unit)) + error(Eio); + start = strtoull(cb->f[2], 0, 0); + end = strtoull(cb->f[3], 0, 0); + sdaddpart(unit, cb->f[1], start, end); + } + else if(strcmp(cb->f[0], "delpart") == 0){ + if(cb->nf != 2 || unit->part == nil) + error(Ebadctl); + sddelpart(unit, cb->f[1]); + } + else if(unit->dev->ifc->wctl) + unit->dev->ifc->wctl(unit, cb); + else + error(Ebadctl); + qunlock(&unit->ctl); + decref(&sdev->r); + poperror(); + free(cb); + break; + + case Qraw: + sdev = sdgetdev(DEV(c->qid)); + if(sdev == nil) + error(Enonexist); + unit = sdev->unit[UNIT(c->qid)]; + qlock(&unit->raw); + if(waserror()){ + qunlock(&unit->raw); + decref(&sdev->r); + nexterror(); + } + switch(unit->state){ + case Rawcmd: + if(n < 6 || n > sizeof(req->cmd)) + error(Ebadarg); + if((req = malloc(sizeof(SDreq))) == nil) + error(Enomem); + req->unit = unit; + memmove(req->cmd, a, n); + req->clen = n; + req->flags = SDnosense; + req->status = ~0; + + unit->req = req; + unit->state = Rawdata; + break; + + case Rawstatus: + unit->state = Rawcmd; + free(unit->req); + unit->req = nil; + error(Ebadusefd); + + case Rawdata: + unit->state = Rawstatus; + unit->req->write = 1; + n = sdrio(unit->req, a, n); + } + qunlock(&unit->raw); + decref(&sdev->r); + poperror(); + break; + case Qpart: + return sdbio(c, 1, a, n, off); + } + + return n; +} + +static int +sdwstat(Chan* c, uchar* dp, int n) +{ + Dir *d; + SDpart *pp; + SDperm *perm; + SDunit *unit; + SDev *sdev; + + if(c->qid.type & QTDIR) + error(Eperm); + + sdev = sdgetdev(DEV(c->qid)); + if(sdev == nil) + error(Enonexist); + unit = sdev->unit[UNIT(c->qid)]; + qlock(&unit->ctl); + d = nil; + if(waserror()){ + free(d); + qunlock(&unit->ctl); + decref(&sdev->r); + nexterror(); + } + + switch(TYPE(c->qid)){ + default: + error(Eperm); + case Qctl: + perm = &unit->ctlperm; + break; + case Qraw: + perm = &unit->rawperm; + break; + case Qpart: + pp = &unit->part[PART(c->qid)]; + if(unit->vers+pp->vers != c->qid.vers) + error(Enonexist); + perm = &pp->perm; + break; + } + + if(strcmp(up->user, perm->user) && !iseve()) + error(Eperm); + + d = smalloc(sizeof(Dir)+n); + n = convM2D(dp, n, &d[0], (char*)&d[1]); + if(n == 0) + error(Eshortstat); + if(!emptystr(d[0].uid)) + kstrdup(&perm->user, d[0].uid); + if(d[0].mode != ~0UL) + perm->perm = (perm->perm & ~0777) | (d[0].mode & 0777); + + free(d); + qunlock(&unit->ctl); + decref(&sdev->r); + poperror(); + return n; +} + +static int +configure(char* spec, DevConf* cf) +{ + SDev *s, *sdev; + char *p; + int i; + + if(sdindex(*spec) < 0) + error("bad sd spec"); + + if((p = strchr(cf->type, '/')) != nil) + *p++ = '\0'; + + for(i = 0; sdifc[i] != nil; i++) + if(strcmp(sdifc[i]->name, cf->type) == 0) + break; + if(sdifc[i] == nil) + error("sd type not found"); + if(p) + *(p-1) = '/'; + + if(sdifc[i]->probe == nil) + error("sd type cannot probe"); + + sdev = sdifc[i]->probe(cf); + for(s=sdev; s; s=s->next) + s->idno = *spec; + sdadddevs(sdev); + return 0; +} + +static int +unconfigure(char* spec) +{ + int i; + SDev *sdev; + SDunit *unit; + + if((i = sdindex(*spec)) < 0) + error(Enonexist); + + qlock(&devslock); + if((sdev = devs[i]) == nil){ + qunlock(&devslock); + error(Enonexist); + } + if(sdev->r.ref){ + qunlock(&devslock); + error(Einuse); + } + devs[i] = nil; + qunlock(&devslock); + + /* make sure no interrupts arrive anymore before removing resources */ + if(sdev->enabled && sdev->ifc->disable) + sdev->ifc->disable(sdev); + + for(i = 0; i != sdev->nunit; i++){ + if(unit = sdev->unit[i]){ + free(unit->perm.name); + free(unit->perm.user); + free(unit); + } + } + + if(sdev->ifc->clear) + sdev->ifc->clear(sdev); + free(sdev); + return 0; +} + +static int +sdconfig(int on, char* spec, DevConf* cf) +{ + if(on) + return configure(spec, cf); + return unconfigure(spec); +} + +Dev sddevtab = { + 'S', + "sd", + + sdreset, + devinit, + devshutdown, + sdattach, + sdwalk, + sdstat, + sdopen, + devcreate, + sdclose, + sdread, + devbread, + sdwrite, + devbwrite, + devremove, + sdwstat, + devpower, + sdconfig, +}; + +/* + * This is wrong for so many reasons. This code must go. + */ +typedef struct Confdata Confdata; +struct Confdata { + int on; + char* spec; + DevConf cf; +}; + +static void +parseswitch(Confdata* cd, char* option) +{ + if(!strcmp("on", option)) + cd->on = 1; + else if(!strcmp("off", option)) + cd->on = 0; + else + error(Ebadarg); +} + +static void +parsespec(Confdata* cd, char* option) +{ + if(strlen(option) > 1) + error(Ebadarg); + cd->spec = option; +} + +static Devport* +getnewport(DevConf* dc) +{ + Devport *p; + + p = (Devport *)malloc((dc->nports + 1) * sizeof(Devport)); + if(dc->nports > 0){ + memmove(p, dc->ports, dc->nports * sizeof(Devport)); + free(dc->ports); + } + dc->ports = p; + p = &dc->ports[dc->nports++]; + p->size = -1; + p->port = (ulong)-1; + return p; +} + +static void +parseport(Confdata* cd, char* option) +{ + char *e; + Devport *p; + + if(cd->cf.nports == 0 || cd->cf.ports[cd->cf.nports-1].port != (ulong)-1) + p = getnewport(&cd->cf); + else + p = &cd->cf.ports[cd->cf.nports-1]; + p->port = strtol(option, &e, 0); + if(e == nil || *e != '\0') + error(Ebadarg); +} + +static void +parsesize(Confdata* cd, char* option) +{ + char *e; + Devport *p; + + if(cd->cf.nports == 0 || cd->cf.ports[cd->cf.nports-1].size != -1) + p = getnewport(&cd->cf); + else + p = &cd->cf.ports[cd->cf.nports-1]; + p->size = (int)strtol(option, &e, 0); + if(e == nil || *e != '\0') + error(Ebadarg); +} + +static void +parseirq(Confdata* cd, char* option) +{ + char *e; + + cd->cf.intnum = strtoul(option, &e, 0); + if(e == nil || *e != '\0') + error(Ebadarg); +} + +static void +parsetype(Confdata* cd, char* option) +{ + cd->cf.type = option; +} + +static struct { + char *name; + void (*parse)(Confdata*, char*); +} options[] = { + "switch", parseswitch, + "spec", parsespec, + "port", parseport, + "size", parsesize, + "irq", parseirq, + "type", parsetype, +}; + +static void +legacytopctl(Cmdbuf *cb) +{ + char *opt; + int i, j; + Confdata cd; + + memset(&cd, 0, sizeof cd); + cd.on = -1; + for(i=0; i<cb->nf; i+=2){ + if(i+2 > cb->nf) + error(Ebadarg); + opt = cb->f[i]; + for(j=0; j<nelem(options); j++) + if(strcmp(opt, options[j].name) == 0){ + options[j].parse(&cd, cb->f[i+1]); + break; + } + if(j == nelem(options)) + error(Ebadarg); + } + /* this has been rewritten to accomodate sdaoe */ + if(cd.on < 0 || cd.spec == 0) + error(Ebadarg); + if(cd.on && cd.cf.type == nil) + error(Ebadarg); + sdconfig(cd.on, cd.spec, &cd.cf); +} diff --git a/src/9vx/a/sd.h b/src/9vx/a/sd.h @@ -0,0 +1,137 @@ +/* + * Storage Device. + */ +typedef struct SDev SDev; +typedef struct SDifc SDifc; +typedef struct SDpart SDpart; +typedef struct SDperm SDperm; +typedef struct SDreq SDreq; +typedef struct SDunit SDunit; + +struct SDperm { + char* name; + char* user; + ulong perm; +}; + +struct SDpart { + uvlong start; + uvlong end; + SDperm perm; + int valid; + ulong vers; +}; + +struct SDunit { + SDev* dev; + int subno; + uchar inquiry[255]; /* format follows SCSI spec */ + uchar sense[18]; /* format follows SCSI spec */ + SDperm perm; + + QLock ctl; + uvlong sectors; + ulong secsize; + SDpart* part; /* nil or array of size npart */ + int npart; + ulong vers; + SDperm ctlperm; + + QLock raw; /* raw read or write in progress */ + ulong rawinuse; /* really just a test-and-set */ + int state; + SDreq* req; + SDperm rawperm; +}; + +/* + * Each controller is represented by a SDev. + */ +struct SDev { + Ref r; /* Number of callers using device */ + SDifc* ifc; /* pnp/legacy */ + void* ctlr; + int idno; + char name[8]; + SDev* next; + + QLock lk; /* enable/disable */ + int enabled; + int nunit; /* Number of units */ + QLock unitlock; /* `Loading' of units */ + int* unitflg; /* Unit flags */ + SDunit**unit; +}; + +struct SDifc { + char* name; + + SDev* (*pnp)(void); + SDev* (*legacy)(int, int); + int (*enable)(SDev*); + int (*disable)(SDev*); + + int (*verify)(SDunit*); + int (*online)(SDunit*); + int (*rio)(SDreq*); + int (*rctl)(SDunit*, char*, int); + int (*wctl)(SDunit*, Cmdbuf*); + + long (*bio)(SDunit*, int, int, void*, long, uvlong); + SDev* (*probe)(DevConf*); + void (*clear)(SDev*); + char* (*rtopctl)(SDev*, char*, char*); + int (*wtopctl)(SDev*, Cmdbuf*); +}; + +struct SDreq { + SDunit* unit; + int lun; + int write; + uchar cmd[16]; + int clen; + void* data; + int dlen; + + int flags; + + int status; + long rlen; + uchar sense[256]; +}; + +enum { + SDnosense = 0x00000001, + SDvalidsense = 0x00010000, +}; + +enum { + SDretry = -5, /* internal to controllers */ + SDmalloc = -4, + SDeio = -3, + SDtimeout = -2, + SDnostatus = -1, + + SDok = 0, + + SDcheck = 0x02, /* check condition */ + SDbusy = 0x08, /* busy */ + + SDmaxio = 2048*1024, + SDnpart = 16, +}; + +#define sdmalloc(n) malloc(n) +#define sdfree(p) free(p) + +/* devsd.c */ +extern void sdadddevs(SDev*); +extern int sdsetsense(SDreq*, int, int, int, int); +extern int sdmodesense(SDreq*, uchar*, void*, int); +extern int sdfakescsi(SDreq*, void*, int); + +/* sdscsi.c */ +extern int scsiverify(SDunit*); +extern int scsionline(SDunit*); +extern long scsibio(SDunit*, int, int, void*, long, uvlong); +extern SDev* scsiid(SDev*, SDifc*); diff --git a/src/9vx/a/sdscsi.c b/src/9vx/a/sdscsi.c @@ -0,0 +1,424 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" +#include "error.h" + +#include "sd.h" + +static int +scsitest(SDreq* r) +{ + r->write = 0; + memset(r->cmd, 0, sizeof(r->cmd)); + r->cmd[1] = r->lun<<5; + r->clen = 6; + r->data = nil; + r->dlen = 0; + r->flags = 0; + + r->status = ~0; + + return r->unit->dev->ifc->rio(r); +} + +int +scsiverify(SDunit* unit) +{ + SDreq *r; + int i, status; + uchar *inquiry; + + if((r = malloc(sizeof(SDreq))) == nil) + return 0; + if((inquiry = sdmalloc(sizeof(unit->inquiry))) == nil){ + free(r); + return 0; + } + r->unit = unit; + r->lun = 0; /* ??? */ + + memset(unit->inquiry, 0, sizeof(unit->inquiry)); + r->write = 0; + r->cmd[0] = 0x12; + r->cmd[1] = r->lun<<5; + r->cmd[4] = sizeof(unit->inquiry)-1; + r->clen = 6; + r->data = inquiry; + r->dlen = sizeof(unit->inquiry)-1; + r->flags = 0; + + r->status = ~0; + if(unit->dev->ifc->rio(r) != SDok){ + free(r); + return 0; + } + memmove(unit->inquiry, inquiry, r->dlen); + free(inquiry); + + status = 0; + for(i = 0; i < 3; i++){ + while((status = scsitest(r)) == SDbusy) + ; + if(status == SDok || status != SDcheck) + break; + if(!(r->flags & SDvalidsense)) + break; + if((r->sense[2] & 0x0F) != 0x02) + continue; + + /* + * Unit is 'not ready'. + * If it is in the process of becoming ready or needs + * an initialising command, set status so it will be spun-up + * below. + * If there's no medium, that's OK too, but don't + * try to spin it up. + */ + if(r->sense[12] == 0x04){ + if(r->sense[13] == 0x02 || r->sense[13] == 0x01){ + status = SDok; + break; + } + } + if(r->sense[12] == 0x3A) + break; + } + + if(status == SDok){ + /* + * Try to ensure a direct-access device is spinning. + * Don't wait for completion, ignore the result. + */ + if((unit->inquiry[0] & 0x1F) == 0){ + memset(r->cmd, 0, sizeof(r->cmd)); + r->write = 0; + r->cmd[0] = 0x1B; + r->cmd[1] = (r->lun<<5)|0x01; + r->cmd[4] = 1; + r->clen = 6; + r->data = nil; + r->dlen = 0; + r->flags = 0; + + r->status = ~0; + unit->dev->ifc->rio(r); + } + } + free(r); + + if(status == SDok || status == SDcheck) + return 1; + return 0; +} + +static int +scsirio(SDreq* r) +{ + /* + * Perform an I/O request, returning + * -1 failure + * 0 ok + * 1 no medium present + * 2 retry + * The contents of r may be altered so the + * caller should re-initialise if necesary. + */ + r->status = ~0; + switch(r->unit->dev->ifc->rio(r)){ + default: + break; + case SDcheck: + if(!(r->flags & SDvalidsense)) + break; + switch(r->sense[2] & 0x0F){ + case 0x00: /* no sense */ + case 0x01: /* recovered error */ + return 2; + case 0x06: /* check condition */ + /* + * 0x28 - not ready to ready transition, + * medium may have changed. + * 0x29 - power on or some type of reset. + */ + if(r->sense[12] == 0x28 && r->sense[13] == 0) + return 2; + if(r->sense[12] == 0x29) + return 2; + break; + case 0x02: /* not ready */ + /* + * If no medium present, bail out. + * If unit is becoming ready, rather than not + * not ready, wait a little then poke it again. */ + if(r->sense[12] == 0x3A) + break; + if(r->sense[12] != 0x04 || r->sense[13] != 0x01) + break; + + while(waserror()) + ; + tsleep(&up->sleep, return0, 0, 500); + poperror(); + scsitest(r); + return 2; + default: + break; + } + break; + case SDok: + return 0; + } + return -1; +} + +int +scsionline(SDunit* unit) +{ + SDreq *r; + uchar *p; + int ok, retries; + + if((r = malloc(sizeof(SDreq))) == nil) + return 0; + if((p = sdmalloc(8)) == nil){ + free(r); + return 0; + } + + ok = 0; + + r->unit = unit; + r->lun = 0; /* ??? */ + for(retries = 0; retries < 10; retries++){ + /* + * Read-capacity is mandatory for DA, WORM, CD-ROM and + * MO. It may return 'not ready' if type DA is not + * spun up, type MO or type CD-ROM are not loaded or just + * plain slow getting their act together after a reset. + */ + r->write = 0; + memset(r->cmd, 0, sizeof(r->cmd)); + r->cmd[0] = 0x25; + r->cmd[1] = r->lun<<5; + r->clen = 10; + r->data = p; + r->dlen = 8; + r->flags = 0; + + r->status = ~0; + switch(scsirio(r)){ + default: + break; + case 0: + unit->sectors = (p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3]; + unit->secsize = (p[4]<<24)|(p[5]<<16)|(p[6]<<8)|p[7]; + + /* + * Some ATAPI CD readers lie about the block size. + * Since we don't read audio via this interface + * it's okay to always fudge this. + */ + if(unit->secsize == 2352) + unit->secsize = 2048; + /* + * Devices with removable media may return 0 sectors + * when they have empty media (e.g. sata dvd writers); + * if so, keep the count zero. + * + * Read-capacity returns the LBA of the last sector, + * therefore the number of sectors must be incremented. + */ + if(unit->sectors != 0) + unit->sectors++; + ok = 1; + break; + case 1: + ok = 1; + break; + case 2: + continue; + } + break; + } + free(p); + free(r); + + if(ok) + return ok+retries; + else + return 0; +} + +int +scsiexec(SDunit* unit, int write, uchar* cmd, int clen, void* data, int* dlen) +{ + SDreq *r; + int status; + + if((r = malloc(sizeof(SDreq))) == nil) + return SDmalloc; + r->unit = unit; + r->lun = cmd[1]>>5; /* ??? */ + r->write = write; + memmove(r->cmd, cmd, clen); + r->clen = clen; + r->data = data; + if(dlen) + r->dlen = *dlen; + r->flags = 0; + + r->status = ~0; + + /* + * Call the device-specific I/O routine. + * There should be no calls to 'error()' below this + * which percolate back up. + */ + switch(status = unit->dev->ifc->rio(r)){ + case SDok: + if(dlen) + *dlen = r->rlen; + /*FALLTHROUGH*/ + case SDcheck: + /*FALLTHROUGH*/ + default: + /* + * It's more complicated than this. There are conditions + * which are 'ok' but for which the returned status code + * is not 'SDok'. + * Also, not all conditions require a reqsense, might + * need to do a reqsense here and make it available to the + * caller somehow. + * + * MaƱana. + */ + break; + } + sdfree(r); + + return status; +} + +static void +scsifmt10(SDreq *r, int write, int lun, ulong nb, uvlong bno) +{ + uchar *c; + + c = r->cmd; + if(write == 0) + c[0] = 0x28; + else + c[0] = 0x2A; + c[1] = lun<<5; + c[2] = bno>>24; + c[3] = bno>>16; + c[4] = bno>>8; + c[5] = bno; + c[6] = 0; + c[7] = nb>>8; + c[8] = nb; + c[9] = 0; + + r->clen = 10; +} + +static void +scsifmt16(SDreq *r, int write, int lun, ulong nb, uvlong bno) +{ + uchar *c; + + c = r->cmd; + if(write == 0) + c[0] = 0x88; + else + c[0] = 0x8A; + c[1] = lun<<5; /* so wrong */ + c[2] = bno>>56; + c[3] = bno>>48; + c[4] = bno>>40; + c[5] = bno>>32; + c[6] = bno>>24; + c[7] = bno>>16; + c[8] = bno>>8; + c[9] = bno; + c[10] = nb>>24; + c[11] = nb>>16; + c[12] = nb>>8; + c[13] = nb; + c[14] = 0; + c[15] = 0; + + r->clen = 16; +} + +long +scsibio(SDunit* unit, int lun, int write, void* data, long nb, uvlong bno) +{ + SDreq *r; + long rlen; + + if((r = malloc(sizeof(SDreq))) == nil) + error(Enomem); + r->unit = unit; + r->lun = lun; +again: + r->write = write; + if(bno >= (1ULL<<32)) + scsifmt16(r, write, lun, nb, bno); + else + scsifmt10(r, write, lun, nb, bno); + r->data = data; + r->dlen = nb*unit->secsize; + r->flags = 0; + + r->status = ~0; + switch(scsirio(r)){ + default: + rlen = -1; + break; + case 0: + rlen = r->rlen; + break; + case 2: + rlen = -1; + if(!(r->flags & SDvalidsense)) + break; + switch(r->sense[2] & 0x0F){ + default: + break; + case 0x01: /* recovered error */ + print("%s: recovered error at sector %llud\n", + unit->perm.name, bno); + rlen = r->rlen; + break; + case 0x06: /* check condition */ + /* + * Check for a removeable media change. + * If so, mark it by zapping the geometry info + * to force an online request. + */ + if(r->sense[12] != 0x28 || r->sense[13] != 0) + break; + if(unit->inquiry[1] & 0x80) + unit->sectors = 0; + break; + case 0x02: /* not ready */ + /* + * If unit is becoming ready, + * rather than not not ready, try again. + */ + if(r->sense[12] == 0x04 && r->sense[13] == 0x01) + goto again; + break; + } + break; + } + free(r); + + return rlen; +} + diff --git a/src/9vx/devfs-posix.c b/src/9vx/devfs-posix.c @@ -4,6 +4,16 @@ #include <grp.h> /* going to regret this - getgrgid is a stack smasher */ #include <sys/socket.h> #include <sys/un.h> +#if defined(__FreeBSD__) +#include <sys/disk.h> +#include <sys/disklabel.h> +#include <sys/ioctl.h> +#endif +#if defined(__linux__) +#include <linux/hdreg.h> +#include <linux/fs.h> +#include <sys/ioctl.h> +#endif #include "lib.h" #include "mem.h" #include "dat.h" @@ -24,6 +34,8 @@ static char *gidtoname(int); static int nametouid(char*); static int nametogid(char*); +static vlong disksize(int, struct stat*); + typedef struct UnixFd UnixFd; struct UnixFd { @@ -247,6 +259,7 @@ fswalk(Chan *c, Chan *nc, char **name, int nname) static int fsdirstat(char *path, int dev, Dir *d) { + int fd; struct stat st; if(stat(path, &st) < 0 && lstat(path, &st) < 0) @@ -261,6 +274,10 @@ fsdirstat(char *path, int dev, Dir *d) d->atime = st.st_atime; d->mtime = st.st_mtime; d->length = st.st_size; + if(S_ISBLK(st.st_mode) && (fd = open(path, O_RDONLY)) >= 0){ + d->length = disksize(fd, &st); + close(fd); + } d->type = FsChar; d->dev = dev; return 0; @@ -844,3 +861,48 @@ nametogid(char *name) return -1; return u->id; } + +#if defined(__linux__) + +static vlong +disksize(int fd, struct stat *st) +{ + uvlong u64; + long l; + struct hd_geometry geo; + + memset(&geo, 0, sizeof geo); + l = 0; + u64 = 0; +#ifdef BLKGETSIZE64 + if(ioctl(fd, BLKGETSIZE64, &u64) >= 0) + return u64; +#endif + if(ioctl(fd, BLKGETSIZE, &l) >= 0) + return l*512; + if(ioctl(fd, HDIO_GETGEO, &geo) >= 0) + return (vlong)geo.heads*geo.sectors*geo.cylinders*512; + return 0; +} + +#elif defined(__FreeBSD__) && defined(DIOCGMEDIASIZE) + +static vlong +disksize(int fd, struct stat *st) +{ + off_t mediasize; + + if(ioctl(fd, DIOCGMEDIASIZE, &mediasize) >= 0) + return mediasize; + return 0; +} + +#else + +static vlong +disksize(int fd, struct stat *st) +{ + return 0; +} + +#endif diff --git a/src/9vx/devtab.c b/src/9vx/devtab.c @@ -21,6 +21,7 @@ extern Dev srvdevtab; extern Dev procdevtab; extern Dev mntloopdevtab; extern Dev dupdevtab; +extern Dev sddevtab; Dev *devtab[] = { &rootdevtab, /* must be first */ @@ -39,6 +40,7 @@ Dev *devtab[] = { &srvdevtab, &ssldevtab, &tlsdevtab, + &sddevtab, 0 }; diff --git a/src/9vx/sdloop.c b/src/9vx/sdloop.c @@ -0,0 +1,274 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" +#include "error.h" + +#include "sd.h" + +void loopdev(char*, int); + +typedef struct Ctlr Ctlr; +struct Ctlr{ + Ctlr *next; + Ctlr *prev; + + QLock lk; + SDev *sdev; + + Chan *c; + int mode; + uvlong qidpath; +}; + +static Lock ctlrlock; +static Ctlr *ctlrhead; +static Ctlr *ctlrtail; + +SDifc sdloopifc; + +static SDev* +looppnp(void) +{ + return nil; +} + +/* + * Cannot error. + * Check that unit is available. + * Return 1 if so, 0 if not. + */ +static int +loopverify(SDunit *u) +{ + return 1; +} + +/* + * Cannot error. + * Check that unit is online. + * If media changed, return 2. + * If ready, return 1. + * If not ready, return 0. + */ +static int +looponline(SDunit *unit) +{ + uchar buf[sizeof(Dir)+100]; + Chan *c; + SDev *sdev; + Ctlr *ctlr; + Dir dir; + long n; + + if(waserror()) + return 0; + + sdev = unit->dev; + ctlr = sdev->ctlr; + c = ctlr->c; + n = devtab[c->type]->stat(c, buf, sizeof buf); + if(convM2D(buf, n, &dir, nil) == 0) + error("internal error: stat error in looponline"); + if(ctlr->qidpath != dir.qid.path){ + unit->sectors = dir.length/512; + unit->secsize = 512; + ctlr->qidpath = dir.qid.path; + poperror(); + return 2; + } + poperror(); + return 1; +} + +static int +looprio(SDreq *r) +{ + SDev *sdev; + SDunit *unit; + Ctlr *ctlr; + uchar *cmd; + uvlong lba; + long count, n; + Chan *c; + int status; + + unit = r->unit; + sdev = unit->dev; + ctlr = sdev->ctlr; + cmd = r->cmd; + +#if 0 + if((status = sdfakescsi(r, ctlr->info, sizeof ctlr->info)) != SDnostatus){ + /* XXX check for SDcheck here */ + r->status = status; + return status; + } +#endif + + switch(cmd[0]){ + case 0x28: /* read */ + case 0x2A: /* write */ + break; + default: + print("%s: bad cmd 0x%.2ux\n", unit->perm.name, cmd[0]); + r->status = SDcheck; + return SDcheck; + } + + lba = (cmd[2]<<24)|(cmd[3]<<16)|(cmd[4]<<8)|cmd[5]; + count = (cmd[7]<<8)|cmd[8]; + if(r->data == nil) + return SDok; + if(r->dlen < count*512) + count = r->dlen/512; + + c = ctlr->c; + if(cmd[0] == 0x28) + n = devtab[c->type]->read(c, r->data, count*512, lba*512); + else + n = devtab[c->type]->write(c, r->data, count*512, lba*512); + r->rlen = n; + return SDok; +} + +static int +looprctl(SDunit *unit, char *p, int l) +{ + Ctlr *ctlr; + char *e, *op; + + ctlr = unit->dev->ctlr; + e = p+l; + op = p; + + p = seprint(p, e, "loop %s %s\n", ctlr->mode == ORDWR ? "rw" : "ro", chanpath(ctlr->c)); + return p - op; +} + +static int +loopwctl(SDunit *u, Cmdbuf *cmd) +{ + cmderror(cmd, Ebadarg); + return 0; +} + +static void +loopclear1(Ctlr *ctlr) +{ + lock(&ctlrlock); + if(ctlr->prev) + ctlr->prev->next = ctlr->next; + else + ctlrhead = ctlr; + if(ctlr->next) + ctlr->next->prev = ctlr->prev; + else + ctlrtail = ctlr->prev; + unlock(&ctlrlock); + + cclose(ctlr->c); + free(ctlr); +} + +static void +loopclear(SDev *sdev) +{ + loopclear1(sdev->ctlr); +} + +static int +loopwtopctl(SDev *sdev, Cmdbuf *cb) +{ + int mode; + + mode = 0; + if(cb->nf != 2) + cmderror(cb, Ebadarg); + if(strcmp(cb->f[0], "rw") == 0) + mode = ORDWR; + else if(strcmp(cb->f[0], "ro") == 0) + mode = OREAD; + else + cmderror(cb, Ebadarg); + + loopdev(cb->f[1], mode); + return 0; +} + +void +loopdev(char *name, int mode) +{ + Chan *c; + Ctlr *volatile ctlr; + SDev *volatile sdev; + + c = namec(name, Aopen, mode, 0); + ctlr = nil; + sdev = nil; + if(waserror()){ + cclose(c); + if(ctlr) + free(ctlr); + if(sdev) + free(sdev); + nexterror(); + } + + ctlr = smalloc(sizeof *ctlr); + sdev = smalloc(sizeof *sdev); + sdev->ifc = &sdloopifc; + sdev->ctlr = ctlr; + sdev->nunit = 1; + sdev->idno = '0'; + ctlr->sdev = sdev; + ctlr->c = c; + ctlr->mode = mode; + poperror(); + + lock(&ctlrlock); + ctlr->next = nil; + ctlr->prev = ctlrtail; + ctlrtail = ctlr; + if(ctlr->prev) + ctlr->prev->next = ctlr; + else + ctlrhead = ctlr; + unlock(&ctlrlock); + + sdadddevs(sdev); +} + + +SDifc sdloopifc = { + "loop", + + looppnp, + nil, /* legacy */ + nil, /* enable */ + nil, /* disable */ + + loopverify, + looponline, + looprio, + looprctl, + loopwctl, + + scsibio, + nil, /* probe */ + loopclear, /* clear */ + nil, + loopwtopctl, +}; + +SDifc *sdifc[] = +{ + &sdloopifc, + nil +}; + + +