// ata.c: ATA simulator for vblade #include "config.h" #include #include #include #include "dat.h" #include "fns.h" enum { // err bits UNC = 1<<6, MC = 1<<5, IDNF = 1<<4, MCR = 1<<3, ABRT = 1<<2, NM = 1<<1, // status bits BSY = 1<<7, DRDY = 1<<6, DF = 1<<5, DRQ = 1<<3, ERR = 1<<0, }; static ushort ident[256] = { [47] 0x8000, [49] 0x0200, [50] 0x4000, [83] 0x5400, [84] 0x4000, [86] 0x1400, [87] 0x4000, [93] 0x400b, }; static void setfld(ushort *a, int idx, int len, char *str) // set field in ident { uchar *p; p = (uchar *)(a+idx); while (len > 0) { if (*str == 0) p[1] = ' '; else p[1] = *str++; if (*str == 0) p[0] = ' '; else p[0] = *str++; p += 2; len -= 2; } } static void setlba28(ushort *ident, vlong lba) { uchar *cp; cp = (uchar *) &ident[60]; *cp++ = lba; *cp++ = lba >>= 8; *cp++ = lba >>= 8; *cp++ = (lba >>= 8) & 0xf; } static void setlba48(ushort *ident, vlong lba) { uchar *cp; cp = (uchar *) &ident[100]; *cp++ = lba; *cp++ = lba >>= 8; *cp++ = lba >>= 8; *cp++ = lba >>= 8; *cp++ = lba >>= 8; *cp++ = lba >>= 8; } void atainit(void) { char buf[64]; setfld(ident, 27, 40, "Coraid EtherDrive vblade"); sprintf(buf, "V%d.%d\n", 4, 0); setfld(ident, 23, 8, buf); setfld(ident, 10, 20, "SSN HERE"); } /* The ATA spec is weird in that you specify the device size as number * of sectors and then address the sectors with an offset. That means * with LBA 28 you shouldn't see an LBA of all ones. Still, we don't * check for that. */ void atacmd(Ataregs *p, uchar *dp) // do the ata cmd { vlong lba; ushort *ip; int n; enum { MAXLBA28SIZE = 0x0fffffff }; switch (p->cmd) { case 0x20: // read sectors case 0x30: // write sectors lba = p->lba & MAXLBA28SIZE; break; case 0x24: // read sectors ext case 0x34: // write sectors ext lba = p->lba & 0x0000ffffffffffffLL; // full 48 break; case 0xe7: // flush cache return; case 0xec: // identify device memmove(dp, ident, 512); ip = (ushort *)dp; if (size & ~MAXLBA28SIZE) setlba28(ip, MAXLBA28SIZE); else setlba28(ip, size); setlba48(ip, size); p->err = 0; p->status = DRDY; return; default: p->status = DRDY | ERR; p->err = ABRT; return; } if (lba + p->sectors > size) { p->err = IDNF; p->status = DRDY | ERR; p->lba = lba; return; } if (p->cmd == 0x20 || p->cmd == 0x24) { n = getsec(bfd, dp, lba, p->sectors); if (n == -1) { perror("read block device"); p->status |= ERR; } } else { n = putsec(bfd, dp, lba, p->sectors); if (n == -1) { perror("write block device"); p->status |= ERR; } } p->lba += p->sectors; p->sectors = 0; p->status = DRDY; p->err = 0; }