nuclear@78: #include nuclear@78: #include nuclear@78: #include nuclear@83: #include nuclear@78: #include nuclear@78: #include nuclear@78: #include "ata.h" nuclear@78: #include "intr.h" nuclear@78: #include "asmops.h" nuclear@87: #include "sched.h" nuclear@86: #include "mutex.h" nuclear@78: nuclear@78: /* registers */ nuclear@78: #define REG_DATA 0 /* R/W */ nuclear@78: #define REG_ERROR 1 /* R */ nuclear@78: #define REG_FEATURES 1 /* W */ nuclear@78: #define REG_COUNT 2 /* R/W */ nuclear@78: #define REG_LBA0 3 /* R/W */ nuclear@78: #define REG_LBA1 4 /* R/W */ nuclear@78: #define REG_LBA2 5 /* R/W */ nuclear@78: #define REG_DEVICE 6 /* R/W */ nuclear@78: #define REG_CMD 7 /* W */ nuclear@78: #define REG_STATUS 7 /* R */ nuclear@78: nuclear@78: #define REG_CTL 518 nuclear@78: #define REG_ALTSTAT 518 nuclear@78: nuclear@78: /* status bit fields */ nuclear@78: #define ST_ERR (1 << 0) nuclear@78: #define ST_DRQ (1 << 3) nuclear@78: #define ST_DRDY (1 << 6) nuclear@78: #define ST_BSY (1 << 7) nuclear@78: nuclear@78: /* device select bit in control register */ nuclear@78: #define DEV_SEL(x) (((x) & 1) << 4) nuclear@86: #define DEV_LBA (1 << 6) nuclear@78: nuclear@78: /* ATA commands */ nuclear@78: #define CMD_IDENTIFY 0xec nuclear@86: #define CMD_READ 0x20 nuclear@86: #define CMD_READ48 0x24 nuclear@86: #define CMD_WRITE 0x30 nuclear@86: #define CMD_WRITE48 0x34 nuclear@78: nuclear@78: nuclear@78: struct device { nuclear@78: int id; /* id of the device on its ATA interface (0 master, 1 slave) */ nuclear@78: int iface; /* ATA interface for this device (0 or 1) */ nuclear@78: int port_base; /* interface I/O port base */ nuclear@78: nuclear@78: uint32_t nsect_lba; nuclear@78: uint64_t nsect_lba48; nuclear@78: }; nuclear@78: nuclear@78: nuclear@89: static int readwrite_pio(int devno, uint64_t sect, void *buf, void (*rwdata)(struct device*, void*)); nuclear@78: static int identify(struct device *dev, int iface, int id); nuclear@78: static void select_dev(struct device *dev); nuclear@78: static int wait_busy(struct device *dev); nuclear@78: static int wait_drq(struct device *dev); nuclear@78: static void read_data(struct device *dev, void *buf); nuclear@89: static void write_data(struct device *dev, void *buf); nuclear@78: static inline uint8_t read_reg8(struct device *dev, int reg); nuclear@78: static inline uint16_t read_reg16(struct device *dev, int reg); nuclear@78: static inline void write_reg8(struct device *dev, int reg, uint8_t val); nuclear@78: static inline void write_reg16(struct device *dev, int reg, uint16_t val); nuclear@78: static void ata_intr(int inum); nuclear@80: static void *atastr(void *res, void *src, int n); nuclear@78: static char *size_str(uint64_t nsect, char *buf); nuclear@87: static void print_error(int devid, int wr, uint32_t high, uint32_t low, unsigned char err); nuclear@78: nuclear@78: /* last drive selected on each bus */ nuclear@78: static int drvsel[2] = {-1, -1}; nuclear@78: nuclear@78: /* 4 possible devices: 2 ATA interfaces with 2 devices each. nuclear@78: * this will never change unless we start querying the PCI config space nuclear@78: * for additional drives (in which case this whole init code must be nuclear@78: * rewritten anyway), but I like it spelt out like this. nuclear@78: */ nuclear@78: #define MAX_IFACES 2 nuclear@78: #define MAX_DEV (MAX_IFACES * 2) nuclear@86: static struct device devices[MAX_DEV]; nuclear@88: static int ndev; nuclear@86: nuclear@86: /* This serves as a sync point for I/O. While the mutex is held, nuclear@86: * some process is doing I/O and all the others must wait. nuclear@86: */ nuclear@86: static mutex_t pending; nuclear@78: nuclear@78: nuclear@78: void init_ata(void) nuclear@78: { nuclear@78: int i; nuclear@78: nuclear@78: interrupt(IRQ_TO_INTR(15), ata_intr); nuclear@78: nuclear@88: ndev = 0; nuclear@78: for(i=0; insect_lba48) { nuclear@90: return dev->nsect_lba48; nuclear@90: } nuclear@90: return dev->nsect_lba; nuclear@90: } nuclear@90: nuclear@86: int ata_read_pio(int devno, uint64_t sect, void *buf) nuclear@86: { nuclear@89: return readwrite_pio(devno, sect, buf, read_data); nuclear@89: } nuclear@89: nuclear@89: int ata_write_pio(int devno, uint64_t sect, void *buf) nuclear@89: { nuclear@89: return readwrite_pio(devno, sect, buf, write_data); nuclear@89: } nuclear@89: nuclear@89: static int readwrite_pio(int devno, uint64_t sect, void *buf, void (*rwdata)(struct device*, void*)) nuclear@89: { nuclear@88: int use_irq, cmd, st, res = -1; nuclear@87: uint32_t sect_low, sect_high; nuclear@87: struct device *dev = devices + devno; nuclear@86: nuclear@86: if(dev->id == -1) { nuclear@86: return -1; nuclear@86: } nuclear@88: use_irq = get_current_proc() != 0; nuclear@86: nuclear@86: if(use_irq) { nuclear@86: /* wait for the interface to become available */ nuclear@86: mutex_lock(&pending); nuclear@86: } nuclear@86: nuclear@86: select_dev(dev); nuclear@86: nuclear@86: /* LBA48 requires the high-order bits first */ nuclear@86: if(sect >= dev->nsect_lba) { nuclear@87: sect_high = (uint32_t)(sect >> 24); nuclear@86: sect_low = (uint32_t)sect & 0xffffff; nuclear@86: nuclear@86: if(sect >= dev->nsect_lba48) { nuclear@86: goto end; nuclear@86: } nuclear@86: cmd = CMD_READ48; nuclear@86: nuclear@86: write_reg8(dev, REG_COUNT, 0); nuclear@86: write_reg8(dev, REG_LBA0, sect_high & 0xff); nuclear@86: write_reg8(dev, REG_LBA1, (sect_high >> 8) & 0xff); nuclear@86: write_reg8(dev, REG_LBA2, (sect_high >> 16) & 0xff); nuclear@86: } else { nuclear@86: cmd = CMD_READ; nuclear@87: sect_high = 0; nuclear@87: sect_low = (uint32_t)sect & 0xfffffff; nuclear@86: } nuclear@86: nuclear@86: write_reg8(dev, REG_COUNT, 1); nuclear@86: write_reg8(dev, REG_LBA0, sect_low & 0xff); nuclear@86: write_reg8(dev, REG_LBA1, (sect_low >> 8) & 0xff); nuclear@86: write_reg8(dev, REG_LBA2, (sect_low >> 16) & 0xff); nuclear@87: write_reg8(dev, REG_DEVICE, ((sect_low >> 24) & 0xf) | DEV_LBA | DEV_SEL(dev->id)); nuclear@86: /* execute */ nuclear@86: write_reg8(dev, REG_CMD, cmd); nuclear@86: nuclear@86: /* wait for the data to become available */ nuclear@86: do { nuclear@86: if(use_irq) { nuclear@86: /* also sleep on the mutex if we're called from userspace */ nuclear@86: wait(&pending); nuclear@86: } nuclear@87: } while(((st = read_reg8(dev, REG_ALTSTAT)) & (ST_DRQ | ST_ERR)) == 0); nuclear@86: nuclear@86: if(st & ST_ERR) { nuclear@87: //print_error(int devid, int wr, uint32_t high, uint32_t low, unsigned char err); nuclear@87: unsigned char err; nuclear@87: nuclear@87: err = read_reg8(dev, REG_ERROR); nuclear@87: print_error(devno, 0, sect_high, sect_low, err); nuclear@86: goto end; nuclear@86: } nuclear@86: nuclear@89: /* read/write the data and we're done */ nuclear@89: rwdata(dev, buf); nuclear@86: res = 0; nuclear@86: end: nuclear@86: if(use_irq) { nuclear@86: mutex_unlock(&pending); nuclear@86: } nuclear@86: return res; nuclear@86: } nuclear@86: nuclear@78: static int identify(struct device *dev, int iface, int id) nuclear@78: { nuclear@78: /* base address of the two ATA interfaces */ nuclear@78: static const int port_base[] = {0x1f0, 0x170}; nuclear@78: unsigned char st; nuclear@78: uint16_t *info; nuclear@78: char textbuf[42]; /* at most we need 40 chars for ident strings */ nuclear@78: nuclear@78: dev->id = id; nuclear@78: dev->iface = iface; nuclear@78: dev->port_base = port_base[iface]; nuclear@78: nuclear@78: /* a status of 0xff means there's no drive on the interface */ nuclear@78: if((st = read_reg8(dev, REG_ALTSTAT)) == 0xff) { nuclear@78: return -1; nuclear@78: } nuclear@78: nuclear@78: select_dev(dev); nuclear@78: nuclear@78: write_reg8(dev, REG_CMD, CMD_IDENTIFY); nuclear@78: nuclear@78: if(!(st = read_reg8(dev, REG_ALTSTAT)) || (st & ST_ERR)) { nuclear@78: /* does not exist */ nuclear@78: return -1; nuclear@78: } nuclear@78: if(wait_busy(dev) == -1) { nuclear@78: /* got ST_ERR, not ATA */ nuclear@78: return -1; nuclear@78: } nuclear@78: nuclear@78: info = malloc(512); nuclear@78: assert(info); nuclear@78: nuclear@78: /* read the device information */ nuclear@78: read_data(dev, info); nuclear@78: nuclear@78: /* print model and serial */ nuclear@83: printf("ata%d: %s", (dev->iface << 1) | dev->id, atastr(textbuf, info + 27, 40)); nuclear@83: printf(" [s/n: %s]", atastr(textbuf, info + 10, 20)); nuclear@78: nuclear@78: dev->nsect_lba = *(uint32_t*)(info + 60); nuclear@78: dev->nsect_lba48 = *(uint64_t*)(info + 100) & 0xffffffffffffull; nuclear@78: nuclear@78: if(!dev->nsect_lba) { nuclear@78: printf(" drive does not support LBA, ignoring!\n"); nuclear@78: free(info); nuclear@78: return -1; nuclear@78: } nuclear@78: nuclear@78: if(dev->nsect_lba48) { nuclear@78: size_str(dev->nsect_lba48, textbuf); nuclear@78: } else { nuclear@78: size_str(dev->nsect_lba, textbuf); nuclear@78: } nuclear@83: printf(" size: %s\n", textbuf); nuclear@78: nuclear@78: free(info); nuclear@78: return 0; nuclear@78: } nuclear@78: nuclear@78: static void select_dev(struct device *dev) nuclear@78: { nuclear@78: /* if this is the currently selected device, thy work is done */ nuclear@78: if(drvsel[dev->iface] == dev->id) nuclear@78: return; nuclear@78: nuclear@78: /* wait for BSY and DRQ to clear */ nuclear@78: while(read_reg8(dev, REG_ALTSTAT) & (ST_BSY | ST_DRQ)); nuclear@78: nuclear@78: /* set the correct device bit to the device register */ nuclear@78: write_reg8(dev, REG_DEVICE, DEV_SEL(dev->id)); nuclear@86: nuclear@86: /* wait a bit to allow the device time to respond */ nuclear@86: iodelay(); iodelay(); iodelay(); iodelay(); nuclear@78: } nuclear@78: nuclear@78: static int wait_busy(struct device *dev) nuclear@78: { nuclear@78: unsigned char st; nuclear@78: nuclear@78: do { nuclear@78: st = read_reg8(dev, REG_ALTSTAT); nuclear@78: } while((st & ST_BSY) && !(st & ST_ERR)); nuclear@78: nuclear@78: return st & ST_ERR ? -1 : 0; nuclear@78: } nuclear@78: nuclear@78: static int wait_drq(struct device *dev) nuclear@78: { nuclear@78: unsigned char st; nuclear@78: nuclear@78: do { nuclear@78: st = read_reg8(dev, REG_ALTSTAT); nuclear@78: } while(!(st & (ST_DRQ | ST_ERR))); nuclear@78: nuclear@78: return st & ST_ERR ? -1 : 0; nuclear@78: } nuclear@78: nuclear@78: static void read_data(struct device *dev, void *buf) nuclear@78: { nuclear@78: int i; nuclear@78: uint16_t *ptr = buf; nuclear@78: nuclear@78: /* wait for the data request from the drive */ nuclear@78: wait_drq(dev); nuclear@78: nuclear@78: /* ready to transfer */ nuclear@78: for(i=0; i<256; i++) { nuclear@78: *ptr++ = read_reg16(dev, REG_DATA); nuclear@78: } nuclear@78: } nuclear@78: nuclear@89: static void write_data(struct device *dev, void *buf) nuclear@89: { nuclear@89: int i; nuclear@89: uint16_t *ptr = buf; nuclear@89: nuclear@89: /* wait for the data request from the device */ nuclear@89: wait_drq(dev); nuclear@89: nuclear@89: /* ready to transfer */ nuclear@89: for(i=0; i<256; i++) { nuclear@89: write_reg16(dev, REG_DATA, *ptr++); nuclear@89: } nuclear@89: } nuclear@89: nuclear@78: static inline uint8_t read_reg8(struct device *dev, int reg) nuclear@78: { nuclear@78: uint8_t val; nuclear@78: inb(val, dev->port_base + reg); nuclear@78: return val; nuclear@78: } nuclear@78: nuclear@78: static inline uint16_t read_reg16(struct device *dev, int reg) nuclear@78: { nuclear@78: uint16_t val; nuclear@78: inw(val, dev->port_base + reg); nuclear@78: return val; nuclear@78: } nuclear@78: nuclear@78: static inline void write_reg8(struct device *dev, int reg, uint8_t val) nuclear@78: { nuclear@78: outb(val, dev->port_base + reg); nuclear@78: } nuclear@78: nuclear@78: static inline void write_reg16(struct device *dev, int reg, uint16_t val) nuclear@78: { nuclear@78: outw(val, dev->port_base + reg); nuclear@78: } nuclear@78: nuclear@78: static void ata_intr(int inum) nuclear@78: { nuclear@78: printf("ATA interrupt\n"); nuclear@78: } nuclear@78: nuclear@80: static void *atastr(void *res, void *src, int n) nuclear@80: { nuclear@80: int i; nuclear@80: uint16_t *sptr = (uint16_t*)src; nuclear@80: char *dptr = res; nuclear@80: nuclear@80: for(i=0; i> 8; nuclear@80: *dptr++ = *sptr++ & 0xff; nuclear@80: } nuclear@83: nuclear@83: while(isspace(*--dptr)); nuclear@83: *++dptr = 0; nuclear@80: return res; nuclear@80: } nuclear@80: nuclear@78: static char *size_str(uint64_t nsect, char *buf) nuclear@78: { nuclear@78: static const char *suffix[] = {"kb", "mb", "gb", "tb", "pb", 0}; nuclear@78: int i; nuclear@82: unsigned int rem; nuclear@78: nuclear@78: /* start with kilobytes */ nuclear@78: nsect /= 2; nuclear@78: nuclear@78: for(i=0; nsect >= 1024 && suffix[i + 1]; i++) { nuclear@82: rem = nsect % 1024; nuclear@78: nsect /= 1024; nuclear@78: } nuclear@82: sprintf(buf, "%u.%u%s", (unsigned int)nsect, 100 * rem / 1024, suffix[i]); nuclear@78: return buf; nuclear@78: } nuclear@78: nuclear@87: #define ERR_NM (1 << 1) nuclear@87: #define ERR_ABRT (1 << 2) nuclear@87: #define ERR_MCR (1 << 3) nuclear@87: #define ERR_IDNF (1 << 4) nuclear@87: #define ERR_MC (1 << 5) nuclear@87: #define ERR_UNC (1 << 6) nuclear@87: nuclear@87: static void print_error(int devid, int wr, uint32_t high, uint32_t low, unsigned char err) nuclear@87: { nuclear@87: printf("ata%d %s %serror ", devid, wr ? "write" : "read", err & ERR_UNC ? "uncorrectable " : ""); nuclear@87: printf("at sector %x%x: ", high, low); nuclear@87: nuclear@87: if(err & ERR_ABRT) nuclear@87: printf("abort "); nuclear@87: if(err & ERR_IDNF) nuclear@87: printf("invalid address "); nuclear@87: if(err & ERR_NM) nuclear@87: printf("no media "); nuclear@87: nuclear@87: printf("(%x)\n", (unsigned int)err); nuclear@87: }