diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/boot/pc/devsd.c |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/boot/pc/devsd.c')
-rwxr-xr-x | sys/src/boot/pc/devsd.c | 632 |
1 files changed, 632 insertions, 0 deletions
diff --git a/sys/src/boot/pc/devsd.c b/sys/src/boot/pc/devsd.c new file mode 100755 index 000000000..17aa35677 --- /dev/null +++ b/sys/src/boot/pc/devsd.c @@ -0,0 +1,632 @@ +/* + * Storage Device. + */ +#include "u.h" +#include "mem.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" +#include "error.h" + +#include "sd.h" +#include "fs.h" + +#define parttrace 0 + +extern SDifc* sdifc[]; + +static SDev* sdlist; +static SDunit** sdunit; +static int sdnunit; +static int _sdmask; +static int cdmask; +static int sdmask; + +enum { + Rawcmd, + Rawdata, + Rawstatus, +}; + +void +sdaddpart(SDunit* unit, char* name, uvlong start, uvlong end) +{ + SDpart *pp; + int i, partno; + + if(parttrace) + print("add %d %s %s %lld %lld\n", unit->npart, unit->name, name, start, end); + /* + * Check name not already used + * and look for a free slot. + */ + if(unit->part != nil){ + partno = -1; + for(i = 0; i < SDnpart; i++){ + pp = &unit->part[i]; + if(!pp->valid){ + if(partno == -1) + partno = i; + break; + } + if(strcmp(name, pp->name) == 0){ + if(pp->start == start && pp->end == end){ + if(parttrace) + print("already present\n"); + return; + } + } + } + }else{ + if((unit->part = malloc(sizeof(SDpart)*SDnpart)) == nil){ + if(parttrace) + print("malloc failed\n"); + return; + } + partno = 0; + } + + /* + * Check there is a free slot and size and extent are valid. + */ + if(partno == -1 || start > end || end > unit->sectors){ + print("cannot add %s!%s [%llud,%llud) to disk [0,%llud): %s\n", + unit->name, name, start, end, unit->sectors, + partno==-1 ? "no free partitions" : "partition boundaries out of range"); + return; + } + pp = &unit->part[partno]; + pp->start = start; + pp->end = end; + strncpy(pp->name, name, NAMELEN); + pp->valid = 1; + unit->npart++; +} + +void +sddelpart(SDunit* unit, char* name) +{ + int i; + SDpart *pp; + + if(parttrace) + print("del %d %s %s\n", unit->npart, unit->name, name); + /* + * Look for the partition to delete. + * Can't delete if someone still has it open. + * If it's the last valid partition zap the + * whole table. + */ + pp = unit->part; + for(i = 0; i < SDnpart; i++){ + if(strncmp(name, pp->name, NAMELEN) == 0) + break; + pp++; + } + if(i >= SDnpart) + return; + pp->valid = 0; + + unit->npart--; + if(unit->npart == 0){ + free(unit->part); + unit->part = nil; + } +} + +static int +sdinitpart(SDunit* unit) +{ + unit->sectors = unit->secsize = 0; + unit->npart = 0; + if(unit->part){ + free(unit->part); + unit->part = nil; + } + + 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 == nil || unit->dev->ifc->online(unit) == 0) + return 0; + sdaddpart(unit, "data", 0, unit->sectors); + return 1; +} + +static SDunit* +sdgetunit(SDev* sdev, int subno) +{ + int index; + SDunit *unit; + + /* + * 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(&sdqlock); + index = sdev->index+subno; + unit = sdunit[index]; + if(unit == nil){ + if((unit = malloc(sizeof(SDunit))) == nil){ + qunlock(&sdqlock); + return nil; + } + + if(sdev->enabled == 0 && sdev->ifc->enable) + sdev->ifc->enable(sdev); + sdev->enabled = 1; + + snprint(unit->name, NAMELEN, "sd%c%d", sdev->idno, subno); + unit->subno = subno; + unit->dev = sdev; + + /* + * 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(&sdqlock); + free(unit); + return nil; + } + sdunit[index] = unit; + } + qunlock(&sdqlock); + + return unit; +} + +static SDunit* +sdindex2unit(int index) +{ + SDev *sdev; + + /* + * Associate a unit with a given index into the top-level + * device directory. + * The device will be probed if it has not already been + * successfully accessed. + */ + for(sdev = sdlist; sdev != nil; sdev = sdev->next){ + if(index >= sdev->index && index < sdev->index+sdev->nunit) + return sdgetunit(sdev, index-sdev->index); + } + + return nil; +} + +static void +_sddetach(void) +{ + SDev *sdev; + + for(sdev = sdlist; sdev != nil; sdev = sdev->next){ + if(sdev->enabled == 0) + continue; + if(sdev->ifc->disable) + sdev->ifc->disable(sdev); + sdev->enabled = 0; + } +} + +static void +sddump(void) +{ + SDev *sdev; + + print("sdevs:\n"); + for(sdev = sdlist; sdev != nil; sdev = sdev->next){ + print("sdev %c index %d nunit %d: ", + sdev->idno, sdev->index, sdev->nunit); + print("\n"); + } +} + +static int +_sdinit(void) +{ + ulong m; + int i; + SDev *sdev, *tail; + SDunit *unit; + + /* + * Probe all configured controllers and make a list + * of devices found, accumulating a possible maximum number + * of units attached and marking each device with an index + * into the linear top-level directory array of units. + */ + tail = nil; + for(i = 0; sdifc[i] != nil; i++){ + if((sdev = sdifc[i]->pnp()) == nil) + continue; + if(sdlist != nil) + tail->next = sdev; + else + sdlist = sdev; + for(tail = sdev; tail->next != nil; tail = tail->next){ + tail->index = sdnunit; + sdnunit += tail->nunit; + } + tail->index = sdnunit; + sdnunit += tail->nunit; + } + /* + * Legacy and option code goes here. This will be hard... + */ + + /* + * The maximum number of possible units is known, allocate + * placeholders for their datastructures; the units will be + * probed and structures allocated when attached. + * Allocate controller names for the different types. + */ + if(sdnunit == 0) + return 0; + if((sdunit = malloc(sdnunit*sizeof(SDunit*))) == nil) + return 0; + sddetach = _sddetach; + + for(i = 0; sdifc[i] != nil; i++){ + if(sdifc[i]->id) + sdifc[i]->id(sdlist); + } + if (0) + sddump(); + + m = 0; + cdmask = sdmask = 0; + for(i=0; i<sdnunit && i < 32; i++) { + unit = sdindex2unit(i); + if(unit == nil) + continue; + sdinitpart(unit); + partition(unit); + if(unit->npart > 0){ /* BUG */ + if((unit->inquiry[0] & 0x1F) == 0x05) + cdmask |= (1<<i); + else + sdmask |= (1<<i); + m |= (1<<i); + } + } + +//notesdinfo(); + _sdmask = m; + return m; +} + +int +cdinit(void) +{ + /* native access to disks seems to interfere with bios loading */ + if(sdnunit == 0 && !biosload) + _sdinit(); + return cdmask; +} + +int +sdinit(void) +{ + if(sdnunit == 0) + _sdinit(); + return sdmask; +} + +void +sdinitdev(int i, char *s) +{ + SDunit *unit; + + unit = sdindex2unit(i); + strcpy(s, unit->name); +} + +void +sdprintdevs(int i) +{ + char *s; + SDunit *unit; + + unit = sdindex2unit(i); + for(i=0; i<unit->npart; i++){ + s = unit->part[i].name; + if(strncmp(s, "dos", 3) == 0 + || strncmp(s, "9fat", 4) == 0 + || strncmp(s, "fs", 2) == 0) + print(" %s!%s", unit->name, s); + } +} + +SDpart* +sdfindpart(SDunit *unit, char *name) +{ + int i; + + if(parttrace) + print("findpart %d %s %s\t\n", unit->npart, unit->name, name); + for(i=0; i<unit->npart; i++) { + if(parttrace) + print("%s...", unit->part[i].name); + if(strcmp(unit->part[i].name, name) == 0){ + if(parttrace) + print("\n"); + return &unit->part[i]; + } + } + if(parttrace) + print("not found\n"); + return nil; +} + +typedef struct Scsicrud Scsicrud; +struct Scsicrud { + Fs fs; + vlong offset; + SDunit *unit; + SDpart *part; +}; + +long +sdread(Fs *vcrud, void *v, long n) +{ + Scsicrud *crud; + long x; + + crud = (Scsicrud*)vcrud; + x = sdbio(crud->unit, crud->part, v, n, crud->offset); + if(x > 0) + crud->offset += x; + return x; +} + +vlong +sdseek(Fs *vcrud, vlong seek) +{ + ((Scsicrud*)vcrud)->offset = seek; + return seek; +} + +void* +sdgetfspart(int i, char *s, int chatty) +{ + SDunit *unit; + SDpart *p; + Scsicrud *crud; + + if(cdmask&(1<<i)){ + if(strcmp(s, "cdboot") != 0) + return nil; + }else if(sdmask&(1<<i)){ + if(strcmp(s, "cdboot") == 0) + return nil; + } + + unit = sdindex2unit(i); + if((p = sdfindpart(unit, s)) == nil){ + if(chatty) + print("unknown partition %s!%s\n", unit->name, s); + return nil; + } + if(p->crud == nil) { + crud = malloc(sizeof(Scsicrud)); + crud->fs.dev = i; + crud->fs.diskread = sdread; + crud->fs.diskseek = sdseek; + // crud->start = 0; + crud->unit = unit; + crud->part = p; + if(dosinit(&crud->fs) < 0 && dosinit(&crud->fs) < 0 && kfsinit(&crud->fs) < 0){ + if(chatty) + print("partition %s!%s does not contain a DOS or KFS file system\n", + unit->name, s); + return nil; + } + p->crud = crud; + } + return p->crud; +} + +/* + * Leave partitions around for devsd to pick up. + * (Needed by boot process; more extensive + * partitioning is done by termrc or cpurc). + */ +void +sdaddconf(int i) +{ + SDunit *unit; + SDpart *pp; + + unit = sdindex2unit(i); + + /* + * If there were no partitions (just data and partition), don't bother. + */ + if(unit->npart<= 1 || (unit->npart==2 && strcmp(unit->part[1].name, "partition")==0)) + return; + + addconf("%spart=", unit->name); + for(i=1, pp=&unit->part[i]; i<unit->npart; i++, pp++) /* skip 0, which is "data" */ + addconf("%s%s %lld %lld", i==1 ? "" : "/", pp->name, + pp->start, pp->end); + addconf("\n"); +} + +int +sdboot(int dev, char *pname, Boot *b) +{ + char *file; + Fs *fs; + + if((file = strchr(pname, '!')) == nil) { + print("syntax is sdC0!partition!file\n"); + return -1; + } + *file++ = '\0'; + + fs = sdgetfspart(dev, pname, 1); + if(fs == nil) + return -1; + + return fsboot(fs, file, b); +} + +long +sdbio(SDunit *unit, SDpart *pp, void* va, long len, vlong off) +{ + long l; + ulong offset; + uvlong bno, max, nb; + char *a; + static ulong bsz; + static uchar *b; + + a = va; +memset(a, 0xDA, len); + qlock(&unit->ctl); + if(unit->changed){ + qunlock(&unit->ctl); + return 0; + } + + /* + * 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-removable media parameters + * (sectors, secsize) can't change once the drive has + * been brought online. + */ + if (unit->secsize == 0) + panic("sdbio: zero sector size"); + 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){ + qunlock(&unit->ctl); + return 0; + } + if(!(unit->inquiry[1] & 0x80)) + qunlock(&unit->ctl); + + if(bsz < nb*unit->secsize){ + b = malloc(nb*unit->secsize); + bsz = nb*unit->secsize; + } +// b = sdmalloc(nb*unit->secsize); +// if(b == nil) +// return 0; + + offset = off % unit->secsize; + if((l = unit->dev->ifc->bio(unit, 0, 0, b, nb, bno)) < 0) { +// sdfree(b); + return 0; + } + + if(l < offset) + len = 0; + else if(len > l - offset) + len = l - offset; + if(len) + memmove(a, b+offset, len); +// sdfree(b); + + if(unit->inquiry[1] & 0x80) + qunlock(&unit->ctl); + + return len; +} + +#ifdef DMA +long +sdrio(SDreq *r, void* a, long n) +{ + if(n >= SDmaxio || n < 0) + return 0; + + r->data = nil; + if(n){ + if((r->data = malloc(n)) == nil) + return 0; + if(r->write) + memmove(r->data, a, n); + } + r->dlen = n; + + if(r->unit->dev->ifc->rio(r) != SDok){ +// cgascreenputs("1", 1); + if(r->data != nil){ + sdfree(r->data); + r->data = nil; + } + return 0; + } +// cgascreenputs("2", 1); + + if(!r->write && r->rlen > 0) + memmove(a, r->data, r->rlen); +// cgascreenputs("3", 1); + if(r->data != nil){ + sdfree(r->data); + r->data = nil; + } + +// cgascreenputs("4", 1); + return r->rlen; +} +#endif /* DMA */ + +void +sleep(void*, int (*fn)(void*), void *v) +{ + int x; + + x = spllo(); + while(!fn(v)) + ; + splx(x); +} + +void +tsleep(void*, int (*fn)(void*), void *v, int msec) +{ + int x; + ulong start; + + x = spllo(); + for(start = m->ticks; TK2MS(m->ticks - start) < msec && !fn(v); ) + ; + splx(x); +} + +void* +sdmalloc(void *p, ulong sz) +{ + if(p != nil) { + memset(p, 0, sz); + return p; + } + return malloc(sz); +} |