#include #include #include #include "atazz.h" #include "tabs.h" #pragma varargck argpos eprint 1 #pragma varargck type "π" char** enum { Dontread = -2, }; int interrupted; int rflag; int squelch; int scttrace; uchar issuetr[0x100]; Atatab *idcmd; Atatab *idpktcmd; Atatab *sigcmd; Atatab *sctread; Atatab *sctissue; int πfmt(Fmt *f) { char **p; p = va_arg(f->args, char**); if(p == nil) return fmtstrcpy(f, ""); for(; *p; p++){ fmtstrcpy(f, *p); if(p[1] != nil) fmtstrcpy(f, " "); } return 0; } int eprint(char *fmt, ...) { int n; va_list args; if(squelch) return 0; // Bflush(&out); va_start(args, fmt); n = vfprint(2, fmt, args); va_end(args); return n; } void fisset(Req *r, uint i, uint v) { if(r->fisbits & 1<fisbits |= 1<cmd.fis[i] = v; } void prreq(Req *r) { uchar *u; print("%.2ux %.2ux\n", r->cmd.sdcmd, r->cmd.ataproto); u = r->cmd.fis; print("%.2ux%.2ux%.2ux%.2ux%.2ux%.2ux%.2ux%.2ux ", u[0], u[1], u[2], u[3], u[4], u[5], u[6], u[7]); u += 8; print("%.2ux%.2ux%.2ux%.2ux%.2ux%.2ux%.2ux%.2ux\n", u[0], u[1], u[2], u[3], u[4], u[5], u[6], u[7]); } char* protostr(char *p, char *e, int pr) { char *s; *p = 0; if(pr & P28) p = seprint(p, e, "28:"); else p = seprint(p, e, "28:"); switch(pr & Pprotom){ default: s = "unk"; break; case Ppkt: s = "pkt"; break; case Pdiag: s = "dig"; break; case Preset: s = "rst"; break; case Pdmq: s = "dmq"; break; case Pdma: s = "dma"; break; case Ppio: s = "pio"; break; } p = seprint(p, e, "%s:", s); switch(pr & Pdatam){ default: s = "nd"; break; case Pin: s = "in"; break; case Pout: s = "out"; break; } p = seprint(p, e, "%s", s); return p; } void displaycmd(Req *r, Atatab *a) { char buf[32]; int i; if(a->cc > nelem(issuetr) || !issuetr[a->cc]) return; protostr(buf, buf + sizeof buf, a->protocol); fprint(2, "cmd %s:%2ux ", buf, a->cc); for(i = 0; i < 16; i++) fprint(2, "%.2ux", r->cmd.fis[i]); fprint(2, "\n"); } int issueata(Req *r, Atatab *a, Dev *d) { uchar u; int n, ok, pr, rv; r->haverfis = 0; pr = a->protocol & Pdatam; r->data = realloc(r->data, r->count); if(r->data == nil && r->count > 0) sysfatal("realloc: %r"); if(r->data == nil && pr != Pnd) sysfatal("no data for cmd %.2ux", a->cc); if(0 && r->fisbits) print("fisbits %.16b\n", r->fisbits); r->cmd.sdcmd = 0xff; r->cmd.ataproto = a->protocol; if(a->cc & 0xff00) fisset(r, 0, a->cc >> 8); else fisset(r, 0, H2dev); fisset(r, 1, Fiscmd); fisset(r, 2, a->cc); switch(pr){ case Pout: if(r->rfd != Dontread){ n = readn(r->rfd, r->data, r->count); if(n != r->count){ if(n == -1) eprint("!short src read %r\n"); else eprint("!short src read %d wanted %lld\n", n, r->count); return -1; } } case Pnd: case Pin: if(0 && (d->feat & Dlba) == 0 && (d->c | d->h | d->s)){ int c, h, s; c = r->lba / (d->s * d->h); h = (r->lba / d->s) % d->h; s = (r->lba % d->s) + 1; fprint(2, "chs: %d %d %d\n", c, h, s); fisset(r, 4, s); fisset(r, 5, c); fisset(r, 6, c >> 8); fisset(r, 7, Ataobs | h); }else{ fisset(r, 4, r->lba); fisset(r, 5, r->lba >> 8); fisset(r, 6, r->lba >> 16); u = Ataobs; if(pr == Pin || pr == Pout) u |= Atalba; if((d->feat & Dllba) == 0) u |= (r->lba >> 24) & 7; fisset(r, 7, u); fisset(r, 8, r->lba >> 24); fisset(r, 9, r->lba >> 32); fisset(r, 10, r->lba >> 48); } fisset(r, 12, r->nsect); fisset(r, 13, r->nsect >> 8); break; } fisset(r, 7, Ataobs); displaycmd(r, a); if(write(d->fd, &r->cmd, Cmdsz) != Cmdsz){ eprint("fis write error: %r\n"); return -1; } werrstr(""); switch(pr){ default: ok = read(d->fd, "", 0) == 0; break; case Pin: ok = read(d->fd, r->data, r->count) == r->count; r->lba += r->nsect; break; case Pout: ok = write(d->fd, r->data, r->count) == r->count; r->lba += r->nsect; break; } rv = 0; if(ok == 0){ eprint("xfer error: %.2ux %r\n", a->cc); rv = -1; } switch(n = read(d->fd, &r->reply, Replysz)){ case Replysz: r->haverfis = 1; return rv; case -1: eprint("status fis read error: %r\n"); return -1; default: eprint("status fis read error: short read: %d of %d\n", n, Replysz); return -1; } } /* * cheezy code; just issue a inquiry. use scuzz * for real work with atapi devices */ int issuepkt(Req *r, Atatab *a, Dev *d) { char *p; uchar *u; int n, rv; r->haverfis = 0; r->count = 128; r->data = realloc(r->data, r->count); if(r->data == nil && r->count > 0) sysfatal("realloc: %r"); r->cmd.sdcmd = 0xff; r->cmd.ataproto = a->protocol; memset(r->cmd.fis, 0, Fissize); u = r->cmd.fis; u[0] = 0x12; u[4] = 128-1; displaycmd(r, a); if(write(d->fd, &r->cmd, 6 + 2) != 6 + 2){ eprint("fis write error: %r\n"); return -1; } n = read(d->fd, r->data, r->count); rv = 0; if(n == -1){ eprint("xfer error: %.2ux %r\n", a->cc); rv = -1; } print("n is %d (%lld)\n", n, r->count); if(n > 32){ p = (char*)r->data; print("%.8s %.16s\n", p + 8, p + 16); } u = (uchar*)&r->reply; n = read(d->fd, u, Replysz); if(n < 0){ eprint("status fis read error (%d): %r\n", n); return -1; } if(n < Replysz) memset(u + n, 0, Replysz - n); r->haverfis = 1; return rv; } /* * silly protocol: * 1. use write log ext 0xe0 to fill out the command * 2. use write log ext 0xe1 to write or data (if any) * 3. use read log ext 0xe0 to nab status. polled */ void sctreq(Req *r) { memset(r, 0, sizeof *r); r->rfd = Dontread; } char* sctrsp(Req *r) { uint i; static char buf[32]; if(!r->haverfis) return "no rfis"; if((r->reply.fis[Frerror] & (Eidnf | Eabrt)) == 0) return nil; i = r->reply.fis[Fsc] | r->reply.fis[Flba0]<<8; if(i == 0xffff) return "in progress"; else if(i == 0){ snprint(buf, sizeof buf, "unknown %.2ux", r->reply.fis[Frerror]); return buf; }else if(i < nelem(sctetab)) return sctetab[i]; else return ""; } char* sctready(Dev *d, int sec) { char *s; int i; Req r; static char e[ERRMAX]; for(;;){ if(interrupted){ s = "interrupted"; break; } sctreq(&r); fisset(&r, Fsc, 1); fisset(&r, Flba0, 0xe0); r.count = 512; i = issueata(&r, sctread, d); free(r.data); if(i == -1){ rerrstr(e, ERRMAX); s = e; break; } if((r.cmd.fis[Fsc] | r.cmd.fis[Fsc8]<<8) != 0xffff){ s = sctrsp(&r); break; } if(sec == 0){ s = "timeout"; break; } sleep(1000); sec--; } return s; } typedef struct Sttab Sttab; struct Sttab { int o; int sz; char *name; }; Sttab sctt[] = { 0, 2, "version", 2, 2, "period", 4, 2, "intval", 6, 1, "max op", 7, 1, "max", 8, 1, "min op", 9, 1, "min", }; void sctttab(Req *r) { char c, buf[10]; int i, n, l, d; uchar *u; u = r->data; for(i = 0; i < nelem(sctt); i++){ switch(sctt[i].sz){ case 1: c = u[sctt[i].o]; print("%s\t%d\n", sctt[i].name, c); break; case 2: d = w(u + sctt[i].o); print("%s\t%ud\n", sctt[i].name, d); break; } } n = w(u + 30); l = w(u + 32); for(i = 0; i < n; i++){ c = u[34 + (l + i) % n]; if((uchar)c == 0x80) snprint(buf, sizeof buf, "xx"); else snprint(buf, sizeof buf, "%d", c); d = i%10; if(d == 0) print("\nt%d\t%d", i, c); else print("% .2d", c); } if(i%10) print("\n"); } static struct { uint code; char *s; char *ms; } fxtab[] = { 0x00010001, "set features", 0, 0x00010002, "enabled", 0, 0x00010003, "disabled", 0, 0x00020001, "enabled", 0, 0x00020002, "disabled", 0, 0x0003ffff, "minute", "minutes", }; void sctfcout(ushort *u, Req *r) { uchar *f; ushort v; uint c, m, i; f = r->reply.fis; switch(u[1]){ case 1: case 2: v = f[Fsc] | f[Flba0]<<8; c = u[2]<<16 | v; m = u[2]<<16 | 0xffff; for(i = 0; i < nelem(fxtab); i++) if(fxtab[i].code == c) print("%s\n", fxtab[i].s); else if(fxtab[i].code == m) print("%d %s\n", v, v>1? fxtab[i].ms: fxtab[i].s); break; case 3: v = f[Fsc] | f[Flba0]<<8; if(v & 1) print("preserve\n"); else print("volatile\n"); break; } } void scterout(ushort *u, Req *r) { uchar *f; uint v; f = r->reply.fis; switch(u[1]){ case 2: v = f[Fsc] | f[Flba0]<<8; v *= 100; print("%dms\n", v); } } void sctout(ushort *u, Req *r) { switch(u[0]){ case 5: sctttab(r); break; case 4: sctfcout(u, r); break; case 3: scterout(u, r); break; } } int issuesct0(Req *r0, Atatab *a, Dev *d) { char *s; uchar proto; Atatab *txa; Req r; if((d->feat & Dsct) == 0){ eprint("sct not supported\n"); return -1; } /* 1. issue command */ sctreq(&r); r.data = malloc(r0->count); memcpy(r.data, r0->data, r0->count); r.count = r0->count; fisset(&r, Fsc, 1); fisset(&r, Flba0, 0xe0); if(issueata(&r, sctissue, d) == -1) return -1; if(s = sctrsp(&r)){ eprint("sct error: %s\n", s); return -1; } /* 1a. check response */ if((s = sctready(d, 1)) != nil){ eprint("sct cmd: %s\n", s); return -1; } /* 2. transfer data */ proto = a->protocol; if(r0->fisbits & 1 << 16){ proto &= ~Pdatam; proto |= r0->cmd.ataproto; } switch(proto & Pdatam){ default: txa = nil; break; case Pin: txa = sctread; break; /* case Pout: txa = sctout; break; */ } if(txa != nil){ sctreq(&r); r.count = 512; fisset(&r, Fsc, 1); fisset(&r, Flba0, 0xe1); if(issueata(&r, txa, d) == -1) return -1; /* 2a. check response */ if((s = sctready(d, 1)) != nil){ eprint("sct cmd: %s\n", s); return -1; } } sctout((ushort*)r0->data, &r); free(r.data); return 0; } static void* pushtrace(int i) { void *tr0; tr0 = malloc(sizeof issuetr); if(tr0 == 0) return 0; memcpy(tr0, issuetr, sizeof issuetr); memset(issuetr, i, sizeof issuetr); return tr0; } static void poptrace(void *tr0) { if(tr0 == nil) return; memcpy(issuetr, tr0, sizeof issuetr); free(tr0); } int issuesct(Req *r0, Atatab *a, Dev *d) { int r; void *t; t = nil; if(scttrace) t = pushtrace(1); r = issuesct0(r0, a, d); if(scttrace) poptrace(t); return r; } int issue(Req *r, Atatab *a, Dev *d) { int rv; int (*f)(Req*, Atatab*, Dev*); if(a->protocol & Psct) f = issuesct; else if((a->protocol & Pprotom) == Ppkt) f = issuepkt; else f = issueata; rv = f(r, a, d); if(r->haverfis) if(r->reply.fis[Fstatus] & ASerr){ werrstr("ata error"); rv = -1; } return rv; } void sigfmt(Req *r) { print("%.8ux\n", fistosig(r->reply.fis)); } int opendev(char *dev, Dev *d) { char buf[128]; int rv; ushort *u; Req r; if(d->fd != -1) close(d->fd); memset(d, 0, sizeof *d); snprint(buf, sizeof buf, "%s/raw", dev); d->fd = open(buf, ORDWR); if(d->fd == -1) return -1; memset(&r, 0, sizeof r); if(issue(&r, sigcmd, d) == -1){ lose: close(d->fd); return -1; } setfissig(d, fistosig(r.reply.fis)); memset(&r, 0, sizeof r); r.count = 512; r.nsect = 1; if(d->sig>>16 == 0xeb14) rv = issue(&r, idpktcmd, d); else rv = issue(&r, idcmd, d); if(rv == -1) goto lose; u = (ushort*)r.data; d->nsect = idfeat(d, u); d->secsize = idss(d, u); d->wwn = idwwn(d, u); return 0; } void rawout(Req *r) { int n; n = write(r->wfd, r->data, r->count); if(n != r->count) eprint("!short write %ud wanted %lld\n", n, r->count); } static ushort gbit16(void *a) { ushort j; uchar *i; i = a; j = i[1] << 8; j |= i[0]; return j; } static Btab extra[] = { 12, "ncqpri", 11, "ncqunload", 10, "phyevent", 9, "hpwrctl", 3, "6.0gbit", 2, "3.0gbit", 1, "1.5gbit", }; static Btab suptab[] = { 8, "wwn", 5, "mediaserial", 1, "smartst", 0, "smartlog" }; char* pextraid(char *p, char *e, ushort *id, uint *medserial) { char *p0; ushort u; *p = 0; *medserial = 0; p0 = p; p = sebtab(p, e, extra, nelem(extra), gbit16(id + 76)); if(p != p0) p = seprint(p, e, " "); u = gbit16(id + 83); if(u & 1<<5) p = seprint(p, e, "gpl "); p0 = p; p = sebtab(p, e, suptab, nelem(suptab), gbit16(id + 84)); if(p != p0) p = seprint(p, e, " "); u = gbit16(id + 120); if(u & 1<<2) p = seprint(p, e, "wunc "); return p; } static char *patatab[] = { "ata8-apt", "ata/atapi-7", }; static char *satatab[] = { "ata8-ast", "sata1.0a", "sataiiext", "sata2.5", "sata2.6", "sata3.0", }; char* ptransport(char *p, char *e, ushort *id) { char *s; ushort u, i; u = gbit16(id + 222); if(u == 0 || u == 0xffff) return seprint(p, e, "unreported "); i = (u>>5) & 0x7f; switch(u & 7<<12){ default: s = "unktransport"; break; case 0: s = "unkparallel"; if(i < nelem(patatab)) s = patatab[i]; break; case 1<<12: s = "unkserial"; if(i < nelem(satatab)) s = satatab[i]; break; } return seprint(p, e, "%s ", s); } Btab entab[] = { 10, "hpa", 9, "reset", 8, "service", 7, "release", 6, "rdlookahd", 5, "vwc", 4, "packet", 3, "pm", 2, "security", 1, "smart", }; Btab addlen[] = { 15, "cfast", // 14, "trim", /* check 169 */ 13, "lpsalignerr", 12, "iddma", 11, "rbufdma", 10, "wbufdma", 9, "pwddma", 8, "dlmcdma", }; char* penabled(char *p, char *e, ushort *id) { char *p0; ushort u; p0 = p; p = sebtab(p, e, addlen, nelem(addlen), gbit16(id + 69)); u = gbit16(id + 87); if(u>>14 == 1){ if(p != p0) p = seprint(p, e, " "); p = sebtab(p, e, entab, nelem(entab), gbit16(id + 85)); } return p; } static char *fftab[] = { nil, "5¼", "3½", "2½", "1.8", "<1.8", }; char* pff(char *p, char *e, ushort *id) { char *p0; ushort u; p0 = p; u = gbit16(id + 168); if(u < nelem(fftab) && fftab[u] != nil) p = seprint(p, e, "%s ", fftab[u]); u = gbit16(id + 217); if(u == 1) p = seprint(p, e, "solid-state "); else if(u != 0 && u != 0xfffe) p = seprint(p, e, "%udrpm ", u); if(p != p0) p--; *p = 0; return p; } Btab scttab[] = { 5, "tables", 4, "feactl", 3, "errctl", 2, "wsame", 1, "rwlong", 0, "sct", }; char* psct(char *p, char *e, ushort *id) { return sebtab(p, e, scttab, nelem(scttab), gbit16(id + 206)); } void idfmt(Req *r) { char buf[100]; uint ss, i; ushort *id; uvlong nsect; Sfis f; if(r->fmtrw == 0){ rawout(r); return; } id = (ushort*)r->data; nsect = idfeat(&f, id); ss = idss(&f, id); idmove(buf, id+10, 20); print("serial\t%s\n", buf); idmove(buf, id+23, 8); print("firm\t%s\n", buf); idmove(buf, id+27, 40); print("model\t%s\n", buf); print("wwn\t%ullx\n", idwwn(&f, id)); pflag(buf, buf + sizeof buf, &f); print("flags\t%s", buf); print("geometry %llud %ud", nsect, ss); if(f.c | f.h | f.s) print(" %ud %ud %ud", f.c, f.h, f.s); print("\n"); penabled(buf, buf + sizeof buf, id); print("enabled\t%s\n", buf); pextraid(buf, buf + sizeof buf, id, &i); print("extra\t%s\n", buf); if(i){ idmove(buf, id + 176, 60); if(buf[0] != 0) print("medias\t%s\n", buf); } psct(buf, buf + sizeof buf, id); if(buf[0]) print("sct\t%s\n", buf); ptransport(buf, buf + sizeof buf, id); print("trans\t%s\n", buf); pff(buf, buf + sizeof buf, id); if(buf[0]) print("ff\t%s\n", buf); } void smfmt(Req *r) { uchar *fis; if(r->cmd.fis[Ffeat] == 0xda){ fis = r->reply.fis; if(fis[5] == 0x4f && fis[6] == 0xc2) eprint("normal\n"); else eprint("threshold exceeded\n"); return; } } void iofmt(Req *r) { uchar *u; int i; if(r->fmtrw == 0){ rawout(r); return; } u = r->data; for(i = 0; i < r->count; i += 16) fprint(2, "%.2ux%.2ux%.2ux%.2ux%.2ux%.2ux%.2ux%.2ux" "%.2ux%.2ux%.2ux%.2ux%.2ux%.2ux%.2ux%.2ux\n", u[i + 0], u[i + 1], u[i + 2], u[i + 3], u[i + 4], u[i + 5], u[i + 6], u[i + 7], u[i + 8], u[i + 9], u[i +10], u[i +11], u[i +12], u[i +13], u[i +14], u[i +15]); } static char *csbyte[] = { "never started", nil, "competed without error", "in progress", "suspended by cmd from host", "aborted by cmd from host", "aborted by device with fatal error", }; static char *exe[] = { "no error or never run", "aborted by host", "interrupted by host", "fatal error; unable to complete", "failed", "failed: electricial", "failed: servo", "failed: read", "failed: shipping damage", [0xf] "in progress", }; char* tabtr(uint u, char **tab, int ntab) { char *s; if(u >= ntab || (s = tab[u]) == nil) s = "reserved"; return s; } void sdfmt(Req *r) { char *s; uchar *b; ushort u; if(r->fmtrw == 0){ rawout(r); return; } b = r->data; u = b[362]; if((u & 0xf0) == 0x80 && u != 0x81 && u != 0x83) u &= 0xf; s = tabtr(u, csbyte, nelem(csbyte)); print("col status: %.2ux %s\n", b[362], s); u = b[363]; s = tabtr(u>>4, exe, nelem(exe)); if(u & 0xf) print("exe status: %.2ux %s, %d0%% left\n", u, s, u & 0xf); else print("exe status: %.2ux %s\n", u, s); u = b[364] | b[365]<<8; print("time left: %uds\n", u); print("shrt poll: %udm\n", b[373]); u = b[374]; if(u == 0xff) u = b[375] | b[376]<<8; print("ext poll: %udm\n", u); } void pagemapfmt(Req *r) { int i; ushort *u; u = (ushort*)r->data; if(u[0] != 1){ print("unsupported\n"); return; } for(i = 1; i < 128; i++) if(u[i] > 0) print("page %d: %d\n", i, u[i]); } void slfmt(Req *r) { switch(r->cmd.fis[Flba0]){ default: iofmt(r); break; case 0: pagemapfmt(r); break; } } enum{ Physz = 7<<12, }; static char *phyec[] = { "no event", "icrc", "err data", "err d2h data", "err h2d data", [0x05] "err nd", "err d2h nd", "err h2d nd", "retry d2h nd", "nready", [0x0a] "comreset", "h2d crc", nil, "bad h2d", nil, "err h2d data crc", [0x10] "err h2d data", nil, "err h2d nd crc", "err h2d nd", }; void phyfmt(Req *r) { char *ec; uchar *p; ushort *u, *e, id, sz; u = (ushort*)r->data; e = u + 510/sizeof *u; for(u += 2; u < e; u += sz){ id = w((uchar*)u); sz = (id & Physz) >> 12; id &= ~Physz; if(sz == 0) break; ec = "unk"; if(id < nelem(phyec) && phyec[id] != nil) ec = phyec[id]; print("%.4ux\t%-15s\t", id, ec); p = (uchar*)u + 2; switch(sz<<1){ default: print("\n"); break; case 2: print("%.4ux\n", w(p)); break; case 4: print("%.8ux\n", dw(p)); break; case 8: print("%.16llux\n", qw(p)); break; } sz += 1; } } typedef struct Gltab Gltab; struct Gltab{ int offset; char *name; }; Gltab page3[] = { 8, "power-on hrs", 16, "head flying hrs", 24, "head loads", 32, "realloc'd sec", 40, "read recovery att", 48, "start failures" }; void qpfmt(Req *r, Gltab *t, int ntab) { uchar *u; int i; uvlong v; u = r->data; for(i = 0; i < ntab; i++){ v = qw(u + t[i].offset); if((v & 3ll<<63) != 3ll<<63) continue; print("%lud\t%s\n", (ulong)v, t[i].name); } } static char *sctsttab[] = { "active waiting", "standby", "sleep", "dst bgnd", "smart bgnd", "sct bgnd", }; void sctstatfmt(Req *r) { char *s; uchar *id, c; id = r->data; print("version\t%d\n", gbit16(id + 0)); print("vnd ver\t%2ux\n", gbit16(id + 2)); print("flags\t%.8ux\n", dw(id + 6)); c = id[10]; s = "unk"; if(c < nelem(sctsttab)) s = sctsttab[c]; print("state\t%s\n", s); print("ext stat\t%.4ux\n", gbit16(id + 14)); print("act code\t%.4ux\n", gbit16(id + 16)); print("fn code\t%.4ux\n", gbit16(id + 18)); print("lba\t%llud\n", qw(id + 40)); print("temp\t%d\n", id[200]); print("min t\t%d %d\n", id[201], id[203]); print("max t\t%d %d\n", id[202], id[204]); print("ot\t%d\n", dw(id + 206)); print("ut\t%d\n", dw(id + 210)); } void glfmt(Req *r) { switch(r->cmd.fis[Flba0]){ case 0: pagemapfmt(r); break; case 3: qpfmt(r, page3, nelem(page3)); break; case 17: phyfmt(r); break; case 0xe0: sctstatfmt(r); break; default: iofmt(r); break; } } char* readline(char *prompt, char *line, int len) { char *p, *e, *q; int n, dump; e = line + len; retry: dump = 0; if(interrupted) eprint("\n%s", prompt); else eprint("%s", prompt); interrupted = 0; for(p = line;; p += n){ if(p == e){ dump = 1; p = line; } n = read(0, p, e - p); if(n < 0){ if(interrupted) goto retry; return nil; } if(n == 0) return nil; if(q = memchr(p, '\n', n)){ if(dump){ eprint("!line too long\n"); goto retry; } p = q; break; } } *p = 0; return line; } void suggesttab(char *cmd, Atatab *a, int n) { int i, l; l = strlen(cmd); for(i = 0; i < n; i++) if(cistrncmp(cmd, a[i].name, l) == 0) eprint("%s\n", a[i].name); } Atatab* findtab(char **cmd, Atatab *a, int n) { char *p, *c; int i, cc, max, l; cc = strtoul(*cmd, &p, 0); if(p != *cmd && (*p == 0 || *p == ' ')){ for(i = 0; i < n; i++) if(a[i].cc == cc){ *cmd = p + 1; return a + cc; } return 0; } max = 0; cc = 0; c = *cmd; for(i = 0; i < n; i++){ l = strlen(a[i].name); if(l > max && cistrncmp(*cmd, a[i].name, l) == 0) if(c[l] == ' ' || c[l] == 0){ max = l + (c[l] == ' '); cc = i; } } if(max > 0){ *cmd = *cmd + max; return a + cc; } return 0; } int catch(void*, char *note) { if(strstr(note, "interrupt") != nil) return interrupted = 1; return 0; } char** ndargs(Atatab*, Req *, char **p) { return p; } char** ioargs(Atatab *, Req *r, char **p) { if(r->nsect == 0) r->nsect = 1; if(p[0] == 0) return p; r->lba = strtoull(p[0], 0, 0); p++; if(p[0] == 0) return p; r->nsect = strtoul(p[0], 0, 0); return p + 1; } char** stdargs(Atatab *, Req *r, char **p) { char *s; Rune x; for(; p[0] && p[0][0] == '-' && p[0][1]; p++){ s = p[0] + 1; if(*s == '-'){ p++; break; } while(*s && (s += chartorune(&x, s))) switch(x){ case 'r': r->raw = 1; break; default: return p; } } return p; } static void chopoff(char *s, char *extra) { char *p; int l, ls; l = strlen(extra); ls = strlen(s); if(l >= ls) return; p = s + ls - l; if(strcmp(p, extra) == 0) *p = 0; } char* trim(char *s) { char *p; while(*s && (*s == ' ' || *s == '\t')) s++; if(*s == 0) return nil; p = s + strlen(s) - 1; while(*p == ' ' || *p == '\t') p--; p[1] = 0; return s; } int doredir(Req *r, char **f, int nf, int mode, int *fd1, int *fd2) { int fd; if(nf != 1 && nf != 2){ eprint("!args\n"); return -1; } fd = -1; if(nf == 2){ fd = open(f[1], mode); if(mode != OREAD){ if(fd == -1) fd = create(f[1], mode, 0660); else seek(fd, 0, 2); } } if(fd1){ close(*fd1); *fd1 = fd; } if(fd2){ r->fmtrw = fd == -1; close(*fd2); *fd2 = fd; } return fd; } int special(char *s, Dev *d, Req *r) { char buf[512], path[128], *f[20], sbuf[512], s2[512], *p, *e, *t; uchar *u; int i, j, nf; Atatab *a; p = buf; e = buf + sizeof buf; if(!strcmp(s, "close")){ r->haverfis = 0; close(d->fd); d->fd = -1; return 0; } if(!strcmp(s, "scttrace")){ scttrace = 1; return 0; } if(!strcmp(s, "dev")){ if(d->fd == -1){ eprint("!bad cmd (device closed)\n"); return 0; } if(fd2path(d->fd, path, sizeof path) == -1) sysfatal("fd2path: %r"); chopoff(path, "/raw"); p = seprint(p, e, "dev\t%s\n", path); p = seprint(p, e, "flags\t"); p = pflag(p, e, d); p = seprint(p, e, "lsectsz\t" "%ud ptol %ud\n", d->lsectsz, 1<physshift); p = seprint(p, e, "geometry %llud %ud\n", d->nsect, d->secsize); if(d->c | d->h | d->s) seprint(p, e, "chs\t%d %d %d\n", d->c, d->h, d->s); print("%s", buf); return 0; } if(!strcmp(s, "help")){ suggesttab(buf, atatab, nelem(atatab)); return 0; } if(!strcmp(s, "probe")){ probe(); return 0; } if(!strcmp(s, "rfis")){ if(r->haverfis == 0){ eprint("!no rfis\n"); return 0; } p = seprint(p, e, "%.2x\n", r->reply.sdcmd); u = r->reply.fis; for(i = 0; i < 16; i++) p = seprint(p, e, "%.2ux", u[i]); seprint(p, e, "\n"); print("%s", buf); return 0; } for(t = s; *t == '<' || *t == '>'; t++) ; if(t != s) snprint(sbuf, sizeof buf, "%.*s %s", (int)(t - s), s, t); else snprint(sbuf, sizeof sbuf, "%s", s); nf = tokenize(sbuf, f, nelem(f)); if(!strcmp(f[0], "issuetr")){ if(nf == 1) for(i = 0; i < nelem(issuetr); i++) issuetr[i] ^= 1; else{ p = s2; e = s2 + sizeof s2; for(i = 1; i < nf - 1; i++) p = seprint(p, e, "%s ", f[i]); p = seprint(p, e, "%s", f[i]); e = s2; for(i = 1; i < nf; i++){ j = strtoul(f[i], &p, 0); if(*p == 0 && j < nelem(issuetr)) issuetr[i] ^= 1; else if(a = findtab(&e, atatab, nelem(atatab))) issuetr[a->cc & 0xff] ^= 1; } } return 0; } if(!strcmp(f[0], "open")){ r->lba = 0; if(nf == 2) opendev(f[1], d); else eprint("!bad args to open\n"); return 0; } if(!strcmp(f[0], ">")){ doredir(r, f, nf, OWRITE, 0, &r->wfd); return 0; } if(!strcmp(f[0], "<")){ doredir(r, f, nf, OREAD, &r->rfd, 0); return 0; } if(!strcmp(f[0], "<>")){ doredir(r, f, nf, OWRITE, &r->rfd, &r->wfd); return 0; } return -1; } static char *regtab[] = { "Ftype", "Fflags", "Fcmd", "Ffeat", "Flba0", "Flba8", "Flba16", "Fdev", "Flba24", "Flba32", "Flba40", "Ffeat8", "Fsc", "Fsc8", "Fr", "Fcontrol", }; void setreg(Req *r, uint reg, uvlong v) { uchar *o; int x; switch(reg & (Sbase | Pbase)){ case 0: r->fisbits |= 1 << reg; r->cmd.fis[reg] = v; // print("%s: %.2ux\n", regtab[reg], (uchar)v); break; case Sbase: case Sbase | Pbase: x = reg & ~(Sbase | Ssz); o = r->data + x*2; assert(x < r->count); switch(reg & Ssz){ default: print("reg & Ssz %ux\n", reg & Ssz); _assert("bad table"); case Sw: pw(o, v); break; case Sdw: pdw(o, v); break; case Sqw: pqw(o, v); break; } break; case Pbase: /* fix me please: this is teh suck */ r->fisbits |= 1 << 16; r->cmd.ataproto = v; break; } } int setfis0(Req *r, Txtab *t, char *p) { char *e; uvlong v; v = strtoull(p, &e, 0); setreg(r, t->val, v); return *e != 0; } char** setfis(Atatab*, Req *r, char **p) { char *s; int i; loop: if((s = p[0]) == 0) return p; for(i = 0; i < nelem(regtx); i++) if(strcmp(s, regtx[i].name) == 0 && p[1] != nil){ // print("setfis0 %s %s\n", p[0], p[1]); setfis0(r, regtx + i, p[1]); p += 2; goto loop; } return p; } char* rname(char *buf, int n, int r) { int i; for(i = 0; i < nelem(regtx); i++) if(regtx[i].val == r){ snprint(buf, n, "%s", regtx[i].name); return buf; } snprint(buf, n, "%.2ux", r); return buf; } int mwcmp(char *a, char ***l) { char buf[128], *f[20], **p; int nf, i; if(*a == 0) return 0; p = *l; if(p[0] == 0) return -1; snprint(buf, sizeof buf, "%s", a); nf = tokenize(buf, f, nelem(f)); for(i = 0; i < nf; i++) if(p[i] == nil || cistrcmp(p[i], f[i]) != 0) return -1; *l = p + i - 1; return 0; } char **dofetab(Fetab*, Req*, char**); static char hexdig[] = "ABCDEFabcdef0123456789"; static char hexonly[] = "ABCDEFabcdef"; static char Enum[] = "expecting number"; int fenum(Fetab *, int v, char ***p) { char *e, *s, *r; int base; if(v >= 0) return v; s = *(*p + 1); e = nil; if(s == nil || *s == 0) e = Enum; else{ base = 0; if(strspn(s, hexdig) == strlen(s) && strpbrk(s, hexonly) != nil) base = 0x10; v = strtoul(s, &r, base); if(*r) e = Enum; } if(e == nil) (*p)++; else print("error: %s [%s]\n", e, s); return v; } char** dofetab0(Fetab *t, Req *r, char **p) { int i, v; Txtab *tab; if(t == nil) return p; tab = t->tab; loop: for(i = 0; i < t->ntab; i++) if(mwcmp(tab[i].name, &p) == 0){ v = fenum(t, tab[i].val, &p); setreg(r, t->reg, v); if(tab[i].name[0] != 0){ p = dofetab(tab[i].fe, r, p + 1); goto loop; } } return p; } char** dofetab(Fetab *t, Req *r, char **p) { for(; t != nil && t->ntab > 0; t++) p = dofetab0(t, r, p); return p; } char** dotab(Atatab *a, Req *r, char **p) { if(a->tab == nil) return p; return dofetab(a->tab, r, p); } void initreq(Req *r) { memset(r, 0, sizeof *r); // r->wfd = open("/dev/null", OWRITE); r->wfd = dup(1, -1); if(rflag == 0) r->fmtrw = 1; r->rfd = open("/dev/zero", OREAD); } static Atatab* lookata(uint cc) { int i; for(i = 0; i < nelem(atatab); i++) if(atatab[i].cc == cc) return atatab + i; assert(0); return nil; } void setup(void) { sctread = lookata(0x2f); sctissue = lookata(0x3f); idpktcmd = lookata(0xa1); idcmd = lookata(0xec); sigcmd = lookata(0xf000); } typedef struct Htab Htab; struct Htab { ulong bit; char *name; }; Htab ertab[] = { Eicrc, "icrc", Ewp, "wp", Emc, "mc", Eidnf, "idnf", Emcr, "mcr", Eabrt, "abrt", Enm, "nm", Emed, "med", Eunc, "unc", }; Htab sttab[] = { ASbsy, "bsy", ASdrdy, "drdy", ASdf, "df", ASdrq, "drq", ASerr, "err", }; static char* htabfmt(char *p, char *e, Htab *t, int n, ulong u) { char *p0; uint i; p0 = p; for(i = 0; i < n; i++) if(u & t[i].bit) p = seprint(p, e, "%s | ", t[i].name); if(p - 3 >= p0) p -= 3; if(p < e) p[0] = 0; return p; } void prerror(Req *r) { char st[64], er[64]; uchar *u; u = r->reply.fis; if(r->haverfis == 0 || (u[Fstatus] & ASerr) == 0) return; htabfmt(er, er + sizeof er, ertab, nelem(ertab), u[Frerror]); htabfmt(st, st + sizeof st, sttab, nelem(sttab), u[Fstatus] & ~ASobs); fprint(2, "err %.2ux %.2ux (%s, %s)\n", u[Frerror], u[Fstatus], er, st); } void usage(void) { eprint("usage: atazz dev\n"); eprint(" or -c cmd\n"); exits("usage"); } void main(int argc, char **argv) { char buf[1024], *p, *f[20], **fp; int nf, cflag, i; Atatab *a; Req r; Dev d; cflag = 0; ARGBEGIN{ case 'c': cflag = atoi(EARGF(usage())); break; case 'r': rflag = 1; break; default: usage(); }ARGEND if(cflag){ for(i = 0; i < nelem(atatab); i++) if(atatab[i].cc == cflag) print("%s\n", atatab[i].name); exits(""); } setup(); fmtinstall(L'π', πfmt); if(argc > 1) usage(); initreq(&r); d.fd = -1; if(argc == 1 && opendev(*argv, &d) == -1) sysfatal("opendev: %r"); atnotify(catch, 1); for(;;){ memset(&r.cmd, 0, sizeof r.cmd); r.fisbits = 0; if(readline("az> ", buf, sizeof buf-1) == nil) break; if((p = trim(buf)) == nil) continue; if(special(buf, &d, &r) == 0) continue; if(d.fd == -1){ eprint("!bad cmd (device closed)\n"); continue; } a = findtab(&p, atatab, nelem(atatab)); if(!a){ suggesttab(buf, atatab, nelem(atatab)); eprint("!unknown cmd\n"); continue; } nf = tokenize(p, f, nelem(f) - 1); f[nf] = 0; fp = stdargs(a, &r, f); fp = setfis(a, &r, fp); if(a->protocol & Psct){ r.count = 1 * 512; r.data = realloc(r.data, r.count); memset(r.data, 0, r.count); } fp = dotab(a, &r, fp); switch(a->protocol & Pprotom){ default: eprint("!bad proto1 %.2ux\n", a->protocol & Pprotom); continue; case Pnd: fp = ndargs(a, &r, fp); case Preset: case Pdiag: r.count = 0; r.lba = 0; r.nsect = 0; break; case Ppio: case Pdma: case Pdmq: case Ppkt: if(a->flags & Cmd5sc){ r.nsect = r.cmd.fis[Fsc]; if(r.nsect == 0) r.nsect = 1; r.cmd.fis[Fsc] = r.nsect; r.count = r.nsect * 0x200; }else if((a->protocol & Pssm) == P512){ r.lba = 0; r.nsect = 0; r.count = 512; }else{ fp = ioargs(a, &r, fp); r.count = d.secsize * r.nsect; } break; } if(fp[0]){ eprint("!extra args %π\n", fp); continue; } if(issue(&r, a, &d) == -1){ prerror(&r); continue; } if(a->fmt) a->fmt(&r); } exits(""); }