add primative support for enumerating sas expanders and enclosures Reference: /n/atom/patch/applied2013/scuzzenc Date: Fri Oct 18 19:28:45 CES 2013 Signed-off-by: quanstro@quanstro.net --- /sys/src/cmd/scuzz/scsireq.c Fri Oct 18 19:27:50 2013 +++ /sys/src/cmd/scuzz/scsireq.c Fri Oct 18 19:27:50 2013 @@ -443,6 +443,26 @@ } long +SRrxdiag(ScsiReq *rp, uchar page, uchar *list, long nbytes) +{ + uchar cmd[6]; + + memset(cmd, 0, sizeof cmd); + cmd[0] = ScmdRxdiag; + cmd[1] = 1; + cmd[2] = page; + cmd[3] = nbytes>>8; + cmd[4] = nbytes; + cmd[5] = 0; /* ctl */ + rp->cmd.p = cmd; + rp->cmd.count = sizeof cmd; + rp->data.p = list; + rp->data.count = nbytes; + rp->data.write = 0; + return SRrequest(rp); +} + +long SRreadattr(ScsiReq *rp, uchar sa, uchar *list, long nbytes) { uchar cmd[16]; @@ -853,6 +873,8 @@ case 0x08: /* medium-changer */ rp->flags |= Fchanger; + return 0; + case 0x0d: /* ses enclosure */ return 0; } } --- /sys/src/cmd/scuzz/scsireq.h Fri Oct 18 19:27:50 2013 +++ /sys/src/cmd/scuzz/scsireq.h Fri Oct 18 19:27:50 2013 @@ -85,6 +85,8 @@ ScmdMsense6 = 0x1A, /* mode sense */ ScmdMsense10 = 0x5A, /* mode sense */ ScmdStart = 0x1B, /* start/stop unit */ + ScmdSenddiag = 0x1d, /* rx diagnostics */ + ScmdRxdiag = 0x1c, /* rx diagnostics */ ScmdRcapacity = 0x25, /* read capacity */ ScmdRcapacity16 = 0x9e, /* long read capacity */ ScmdReadattr = 0x8c, /* read attribute */ @@ -188,6 +190,9 @@ long SRrcapacity16(ScsiReq*, uchar*); long SRmediaserial(ScsiReq*, uchar*, ulong); long SRvpd(ScsiReq*, int, uchar*, long); + +long SRsenddiag(ScsiReq*, uchar, uchar*, long); +long SRrxdiag(ScsiReq*, uchar, uchar*, long); long SRblank(ScsiReq*, uchar, uchar); /* MMC CD-R/CD-RW commands */ long SRsynccache(ScsiReq*); --- /sys/src/cmd/scuzz/scuzz.c Fri Oct 18 19:27:50 2013 +++ /sys/src/cmd/scuzz/scuzz.c Fri Oct 18 19:27:50 2013 @@ -796,6 +796,288 @@ return status; } +struct { + int page; + char *desc; +} diagpage[] = { +0x00, "list of supported diagnostic pages", +0x01, "configuration", +0x02, "enclosure control / enclosure status", +0x03, "help text", +0x04, "string out / string in", +0x05, "threshold out / threshold in", +0x06, "obsolete", +0x07, "element descriptor", +0x08, "short enclosure status", +0x09, "enclosure busy", +0x0a, "additional element", +0x0b, "subenclosure help text", +0x0c, "subenclosure string out / subenclosure string in", +0x0d, "supported diagnostic pages", +0x0e, "download microcode control / download microcode status", +0x0f, "subenclosure nickname control / subenclosure nickname status", +}; + +static long +cmdsenddiag(ScsiReq *rp, int argc, char *argv[]) +{ + USED(argv); + switch(argc){ + default: + rp->status = Status_BADARG; + return -1; + } +} + +static uchar* +parsesub(uchar *p, uchar *e, int *ndesc) +{ + int len; + + len = p[3]+4; + if(p+len >= e){ + fprint(2, "short response\n"); + return e; + } + Bprint(&bout, "flags %.2ux; len %d\n", p[0], len); + Bprint(&bout, " sub %d %.16llux id '%.15s'\n", p[1], getbe(p+4, 8), (char*)p+20); + Bprint(&bout, " desc %.2ux\n", p[2]); + *ndesc += p[2]; +// Bprint(&bout, " vendor\n%.*s\n", len-40, (char*)p+40); /* not interesting for lsi */ + return p+len; +} + +static uchar* +parsecfg(ScsiReq *, uchar *p, uchar *e) +{ + char *t; + int i, sub, ndesc; + + ndesc = 0; + sub = p[1]; + Bprint(&bout, "subenc %d+1; gen %d\n", sub, (int)getbe(p+4, 4)); + p = p+8; + for(i = 0; i < sub+1; i++) + p = parsesub(p, e, &ndesc); + Bprint(&bout, "desc:\n"); + Bprint(&bout, " typ count subenc desc\n"); + t = (char*)p + 4*ndesc; + for(i = 0; i < ndesc; i++){ + if((uchar*)t + p[3] >= e){ + Bprint(&bout, " (corrupt descriptors %d left)\n", ndesc-i); + break; + } + Bprint(&bout, " %.2ux %.2ux %.2ux %.*s\n", p[0], p[1], p[2], p[3], t); + t += p[3]; + p += 4; + } + return e; +} + +static char *estab[] = { +[0] "unsupported", +[1] "ok", +[2] "critical", +[3] "noncritical", +[4] "unrecoverable", +[5] "not installed", +[6] "unknown", +[7] "not available", +[8] "no access", +}; + +static uchar* +parseencstatus(ScsiReq*, uchar *p, uchar *e) +{ + char *s; + int i; + + i = 0; + for(p += 8; p < e; p += 4){ + s = "(bad info)"; + if(p[0] < nelem(estab)) + s = estab[p[0]]; + Bprint(&bout, "%d %s", i++, s); + switch(p[0]){ + case 1: + case 2: + case 3: + case 4: + /* + * think 1<<5 (enclosure bypass a) + * 1<<4 (enclosure bypass a) + * 1<<2 rmb (removable) + */ + Bprint(&bout, " %.2ux %.2ux %.2ux", + p[1], p[2], p[3]); + break; + } + Bprint(&bout, "\n"); + } + return p; +} + +static uchar* +parseelementdesc(ScsiReq*, uchar *p, uchar *e) +{ + int len, m, i; + + len = getbe(p+2, 2) + 4; + if(p+len >= e){ + fprint(2, "(bad response)\n"); + return e; + } + + i = 0; + for(p = p+8; p < e; p += m+4){ + m = getbe(p+2, 2); + print("%d %.*s\n", i++, m, (char*)p+4); + } + return e; +} + +static uchar* +parsephy(uchar *p) +{ + uint type; + + type = p[0]>>4 & 7; + if(type == 0 && (p[3]&1) == 0) + return p+28; /* sata with no connected device */ + + Bprint(&bout, " %d %llux → %.16llux id %d\n", type, getbe(p+4, 8), getbe(p+12, 8), p[20]); + return p+28; +} + +static uchar* +parseexpphy(uchar *p) +{ + Bprint(&bout, " %d → %d\n", p[0], p[1]); + return p+2; +} + +static uchar* +parseed(uchar *p) +{ + uchar *s; + uint i, w, l, nphy; + + w = p[0]; + l = p[1]; + + if(w & 0x80) /* invalid */ + return p+l; + if((w & 0x10) == 0) /* no eip bit */ + return p+l; + if((w & 0xf) != 6) /* not sas */ + return p+l; + + s = p+4; + nphy = s[0]; + switch(s[1]>>6){ + case 0: /* device */ + Bprint(&bout, "device bay %d nphy %d idx %d\n", s[4], nphy, s[3]); + s += 4; + for(i = 0; i < nphy; i++) + parsephy(s+28*i); + break; + case 1: /* expander and whatnot */ + Bprint(&bout, "expander nphy %d idx %d wwn %.16llux\n", nphy, s[3], getbe(s+4, 8)); + s += 12; + for(i = 0; i < nphy; i++) + parseexpphy(s+2*i); + break; + } + + return p+l+2; +} + +static void +parseelement(ScsiReq *, uchar *p, uchar *e) +{ + Bprint(&bout, "%d bytes; generation %d\n", (int)getbe(p+2, 2), (int)getbe(p+4, 2)); + + for(p = p+8; p < e;) + p = parseed(p); +} + +static long +cmdrxdiag(ScsiReq *rp, int argc, char *argv[]) +{ + char *p; + uchar *list; + int i, status, page, nbytes; + + nbytes = 4096; + switch(argc){ + default: + rp->status = Status_BADARG; + return -1; + case 2: + if((nbytes = strtoul(argv[1], &p, 0)) == 0 && p == argv[1]){ + rp->status = Status_BADARG; + return -1; + } + /*FALLTHROUGH*/ + case 1: + if((page = strtoul(argv[0], &p, 0)) == 0 && p == argv[0]){ + rp->status = Status_BADARG; + return -1; + } + break; + case 0: + page = 0; + break; + } + + Bprint(&bout, "page %d\n", page); + + list = malloc(nbytes); + if(list == 0){ + rp->status = STnomem; + return -1; + } + if((status = SRrxdiag(rp, page, list, nbytes)) == -1) + return -1; + switch(page){ + case 0: + Bprint(&bout, "supported pages:\n\t"); + for(i = 4; i < status; i++) + Bprint(&bout, "%d ", list[i]); + Bprint(&bout, "\n"); + break; + case 1: + Bprint(&bout, "%s\n", diagpage[page].desc); + parsecfg(rp, list, list + status + 1); + break; + case 2: + /* in theory can be used for control, too */ + parseencstatus(rp, list, list + status + 1); + break; + case 7: + /* in theory can be used for control, too */ + parseelementdesc(rp, list, list + status + 1); + break; + case 0xa: + parseelement(rp, list, list+status); + break; + default: + Bprint(&bout, " Header\n "); + for(i = 0; i < 8; i++){ /* header */ + Bprint(&bout, " %2.2ux", list[i]); + } + for(i = 8; i < status; i++){ + if(i%8 == 0) + Bprint(&bout, "\n"); + Bprint(&bout, " %2.2ux", list[i]); + } + Bprint(&bout, "\n"); + break; + } + free(list); + return status; +} + static long cmdmediaserial(ScsiReq *r, int, char**) { @@ -943,10 +1225,11 @@ "vendorid", "eui-64", "name_id", - "relport4", - "relport5", - "relport6", + "relport", + "targgrp", + "lugrp", "md5", + "namestr", }; static char* @@ -961,8 +1244,8 @@ static int vpd83(uchar *u, uchar *e) { - char *proto, *assoc, *idtype, buf0[12], buf1[12], buf2[12]; - int l; + char *proto, buf0[12], buf1[12], buf2[12]; + int l, typeidx, associdx; fmtinstall('H', encodefmt); if((u[1] & 0x80) != 0) @@ -971,21 +1254,32 @@ proto = buf0; snprint(buf0, sizeof buf0, "invalid/%d", u[0]>>4); } - assoc = tabtr(u[1]>>4 & 3, associd, nelem(associd), buf1); - idtype = tabtr(u[1] & 0xf, idtypeid, nelem(idtypeid), buf2); - Bprint(&bout, " proto %s type %s assoc %s len %d\n", proto, idtype, assoc, l = u[3]); + associdx = u[1]>>4 & 3; + typeidx = u[1] & 0xf; + l = u[3]; + + Bprint(&bout, " proto %s type %s assoc %s len %d\n", + proto, tabtr(typeidx, idtypeid, nelem(idtypeid), buf2), + tabtr(associdx, associd, nelem(associd), buf1), l); + if(u + l > e) l = e - u; - switch(u[0] & 0xf){ + switch(typeidx){ case 2: + case 8: Bprint(&bout, " %.*s", l, (char*)u + 4); break; + case 4: + case 5: + case 6: + Bprint(&bout, " %.4ux", (int)getbe(u + 4 + 2, 2)); + break; default: Bprint(&bout, " %.*lH", l, u + 4); break; } - return u[3]; + return u[3]+4; } static long @@ -2160,7 +2454,7 @@ "inquiry", }, { "logsense", cmdlogsense, 1, /*[0x4d]*/ - "modesense [page [nbytes]]", + "logsense [page [nbytes]]", }, { "modeselect6",cmdmodeselect6, 1, /*[0x15] */ "modeselect6 bytes...", @@ -2173,6 +2467,12 @@ }, { "modesense", cmdmodesense10, 1, /*[0x5A]*/ "modesense [page [nbytes]]", + }, + { "senddiag", cmdsenddiag, 1, /*[0x1d] */ + "senddiag page ...", + }, + { "rxdiag", cmdrxdiag, 1, /*[0x1c] */ + "rxdiag page ...", }, { "mediaserial", cmdmediaserial, 1, /*[0xab]*/ "mediaserial",