📄 sdata.c
字号:
#include "u.h"#include "../port/lib.h"#include "mem.h"#include "dat.h"#include "fns.h"#include "io.h"#include "ureg.h"#include "../port/error.h"#include "../port/sd.h"extern SDifc sdataifc;//BUG?#define PCIWADDR(x) ((ulong)(x))enum { DbgCONFIG = 0x01, /* detected drive config info */ DbgIDENTIFY = 0x02, /* detected drive identify info */ DbgSTATE = 0x04, /* dump state on panic */ DbgPROBE = 0x08, /* trace device probing */ DbgDEBUG = 0x80, /* the current problem... */};#define DEBUG (DbgDEBUG|DbgSTATE|DbgCONFIG)enum { /* I/O ports */ Data = 0, Error = 1, /* (read) */ Features = 1, /* (write) */ Count = 2, /* sector count */ Ir = 2, /* interrupt reason (PACKET) */ Sector = 3, /* sector number, LBA<7-0> */ Cyllo = 4, /* cylinder low, LBA<15-8> */ Bytelo = 4, /* byte count low (PACKET) */ Cylhi = 5, /* cylinder high, LBA<23-16> */ Bytehi = 5, /* byte count hi (PACKET) */ Dh = 6, /* Device/Head, LBA<32-14> */ Status = 7, /* (read) */ Command = 7, /* (write) */ As = 2, /* Alternate Status (read) */ Dc = 2, /* Device Control (write) */};enum { /* Error */ Med = 0x01, /* Media error */ Ili = 0x01, /* command set specific (PACKET) */ Nm = 0x02, /* No Media */ Eom = 0x02, /* command set specific (PACKET) */ Abrt = 0x04, /* Aborted command */ Mcr = 0x08, /* Media Change Request */ Idnf = 0x10, /* no user-accessible address */ Mc = 0x20, /* Media Change */ Unc = 0x40, /* Uncorrectable data error */ Wp = 0x40, /* Write Protect */ Icrc = 0x80, /* Interface CRC error */};enum { /* Features */ Dma = 0x01, /* data transfer via DMA (PACKET) */ Ovl = 0x02, /* command overlapped (PACKET) */};enum { /* Interrupt Reason */ Cd = 0x01, /* Command/Data */ Io = 0x02, /* I/O direction */ Rel = 0x04, /* Bus Release */};enum { /* Device/Head */ Dev0 = 0xA0, /* Master */ Dev1 = 0xB0, /* Slave */ Lba = 0x40, /* LBA mode */};enum { /* Status, Alternate Status */ Err = 0x01, /* Error */ Chk = 0x01, /* Check error (PACKET) */ Drq = 0x08, /* Data Request */ Dsc = 0x10, /* Device Seek Complete */ Serv = 0x10, /* Service */ Df = 0x20, /* Device Fault */ Dmrd = 0x20, /* DMA ready (PACKET) */ Drdy = 0x40, /* Device Ready */ Bsy = 0x80, /* Busy */};enum { /* Command */ Cnop = 0x00, /* NOP */ Cdr = 0x08, /* Device Reset */ Crs = 0x20, /* Read Sectors */ Cws = 0x30, /* Write Sectors */ Cedd = 0x90, /* Execute Device Diagnostics */ Cpkt = 0xA0, /* Packet */ Cidpkt = 0xA1, /* Identify Packet Device */ Crsm = 0xC4, /* Read Multiple */ Cwsm = 0xC5, /* Write Multiple */ Csm = 0xC6, /* Set Multiple */ Crdq = 0xC7, /* Read DMA queued */ Crd = 0xC8, /* Read DMA */ Cwd = 0xCA, /* Write DMA */ Cwdq = 0xCC, /* Write DMA queued */ Cstandby = 0xE2, /* Standby */ Cid = 0xEC, /* Identify Device */ Csf = 0xEF, /* Set Features */};enum { /* Device Control */ Nien = 0x02, /* (not) Interrupt Enable */ Srst = 0x04, /* Software Reset */};enum { /* PCI Configuration Registers */ Bmiba = 0x20, /* Bus Master Interface Base Address */ Idetim = 0x40, /* IE Timing */ Sidetim = 0x44, /* Slave IE Timing */ Udmactl = 0x48, /* Ultra DMA/33 Control */ Udmatim = 0x4A, /* Ultra DMA/33 Timing */};enum { /* Bus Master IDE I/O Ports */ Bmicx = 0, /* Command */ Bmisx = 2, /* Status */ Bmidtpx = 4, /* Descriptor Table Pointer */};enum { /* Bmicx */ Ssbm = 0x01, /* Start/Stop Bus Master */ Rwcon = 0x08, /* Read/Write Control */};enum { /* Bmisx */ Bmidea = 0x01, /* Bus Master IDE Active */ Idedmae = 0x02, /* IDE DMA Error (R/WC) */ Ideints = 0x04, /* IDE Interrupt Status (R/WC) */ Dma0cap = 0x20, /* Drive 0 DMA Capable */ Dma1cap = 0x40, /* Drive 0 DMA Capable */};enum { /* Physical Region Descriptor */ PrdEOT = 0x80000000, /* Bus Master IDE Active */};enum { /* offsets into the identify info. */ Iconfig = 0, /* general configuration */ Ilcyl = 1, /* logical cylinders */ Ilhead = 3, /* logical heads */ Ilsec = 6, /* logical sectors per logical track */ Iserial = 10, /* serial number */ Ifirmware = 23, /* firmware revision */ Imodel = 27, /* model number */ Imaxrwm = 47, /* max. read/write multiple sectors */ Icapabilities = 49, /* capabilities */ Istandby = 50, /* device specific standby timer */ Ipiomode = 51, /* PIO data transfer mode number */ Ivalid = 53, Iccyl = 54, /* cylinders if (valid&0x01) */ Ichead = 55, /* heads if (valid&0x01) */ Icsec = 56, /* sectors if (valid&0x01) */ Iccap = 57, /* capacity if (valid&0x01) */ Irwm = 59, /* read/write multiple */ Ilba0 = 60, /* LBA size */ Ilba1 = 61, /* LBA size */ Imwdma = 63, /* multiword DMA mode */ Iapiomode = 64, /* advanced PIO modes supported */ Iminmwdma = 65, /* min. multiword DMA cycle time */ Irecmwdma = 66, /* rec. multiword DMA cycle time */ Iminpio = 67, /* min. PIO cycle w/o flow control */ Iminiordy = 68, /* min. PIO cycle with IORDY */ Ipcktbr = 71, /* time from PACKET to bus release */ Iserbsy = 72, /* time from SERVICE to !Bsy */ Iqdepth = 75, /* max. queue depth */ Imajor = 80, /* major version number */ Iminor = 81, /* minor version number */ Icsfs = 82, /* command set/feature supported */ Icsfe = 85, /* command set/feature enabled */ Iudma = 88, /* ultra DMA mode */ Ierase = 89, /* time for security erase */ Ieerase = 90, /* time for enhanced security erase */ Ipower = 91, /* current advanced power management */ Irmsn = 127, /* removable status notification */ Istatus = 128, /* security status */};typedef struct Ctlr Ctlr;typedef struct Drive Drive;typedef struct Prd { ulong pa; /* Physical Base Address */ int count;} Prd;enum { Nprd = SDmaxio/(64*1024)+2,};typedef struct Ctlr { int cmdport; int ctlport; int irq; int tbdf; int bmiba; /* bus master interface base address */ void (*ienable)(Ctlr*); SDev* sdev; Drive* drive[2]; Prd* prdt; /* physical region descriptor table */ QLock; /* current command */ Drive* curdrive; int command; /* last command issued (debugging) */ Rendez; int done; Lock; /* register access */} Ctlr;typedef struct Drive { Ctlr* ctlr; int dev; ushort info[256]; int c; /* cylinder */ int h; /* head */ int s; /* sector */ int sectors; /* total */ int secsize; /* sector size */ int dma; /* DMA R/W possible */ int dmactl; int rwm; /* read/write multiple possible */ int rwmctl; int pkt; /* PACKET device, length of pktcmd */ uchar pktcmd[16]; int pktdma; /* this PACKET command using dma */ uchar sense[18]; uchar inquiry[48]; QLock; /* drive access */ int command; /* current command */ int write; uchar* data; int dlen; uchar* limit; int count; /* sectors */ int block; /* R/W bytes per block */ int status; int error;} Drive;static voidatadumpstate(Drive* drive, uchar* cmd, int lba, int count){ Prd *prd; Ctlr *ctlr; int i, bmiba; if(!(DEBUG & DbgSTATE)){ USED(drive, cmd, lba, count); return; } ctlr = drive->ctlr; print("command %2.2uX\n", ctlr->command); print("data %8.8p limit %8.8p dlen %d status %uX error %uX\n", drive->data, drive->limit, drive->dlen, drive->status, drive->error); if(cmd != nil){ print("lba %d -> %d, count %d -> %d (%d)\n", (cmd[2]<<24)|(cmd[3]<<16)|(cmd[4]<<8)|cmd[5], lba, (cmd[7]<<8)|cmd[8], count, drive->count); } if(!(inb(ctlr->ctlport+As) & Bsy)){ for(i = 1; i < 7; i++) print(" 0x%2.2uX", inb(ctlr->cmdport+i)); print(" 0x%2.2uX\n", inb(ctlr->ctlport+As)); } if(drive->command == Cwd || drive->command == Crd){ bmiba = ctlr->bmiba; prd = ctlr->prdt; print("bmicx %2.2uX bmisx %2.2uX prdt %8.8p\n", inb(bmiba+Bmicx), inb(bmiba+Bmisx), prd); for(;;){ print("pa 0x%8.8luX count %8.8uX\n", prd->pa, prd->count); if(prd->count & PrdEOT) break; prd++; } }}static intatadebug(int cmdport, int ctlport, char* fmt, ...){ int i, n; va_list arg; char buf[PRINTSIZE]; if(!(DEBUG & DbgPROBE)){ USED(cmdport, ctlport, fmt); return 0; } va_start(arg, fmt); n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf; va_end(arg); if(cmdport){ if(buf[n-1] == '\n') n--; n += snprint(buf+n, PRINTSIZE-n, " ataregs 0x%uX:", cmdport); for(i = Features; i < Command; i++) n += snprint(buf+n, PRINTSIZE-n, " 0x%2.2uX", inb(cmdport+i)); if(ctlport) n += snprint(buf+n, PRINTSIZE-n, " 0x%2.2uX", inb(ctlport+As)); n += snprint(buf+n, PRINTSIZE-n, "\n"); } putstrn(buf, n); return n;}static intataready(int cmdport, int ctlport, int dev, int reset, int ready, int micro){ int as; atadebug(cmdport, ctlport, "ataready: dev %uX reset %uX ready %uX", dev, reset, ready); for(;;){ /* * Wait for the controller to become not busy and * possibly for a status bit to become true (usually * Drdy). Must change to the appropriate device * register set if necessary before testing for ready. * Always run through the loop at least once so it * can be used as a test for !Bsy. */ as = inb(ctlport+As); if((as & reset) == 0){ if(dev){ outb(cmdport+Dh, dev); dev = 0; } else if(ready == 0 || (as & ready)){ atadebug(0, 0, "ataready: %d 0x%2.2uX\n", micro, as); return as; } } if(micro-- <= 0){ atadebug(0, 0, "ataready: %d 0x%2.2uX\n", micro, as); break; } microdelay(4); } atadebug(cmdport, ctlport, "ataready: timeout"); return -1;}static intatacsf(Drive* drive, vlong csf, int supported){ ushort *info; int cmdset, i, x; if(supported) info = &drive->info[Icsfs]; else info = &drive->info[Icsfe]; for(i = 0; i < 3; i++){ x = (csf>>(16*i)) & 0xFFFF; if(x == 0) continue; cmdset = info[i]; if(cmdset == 0 || cmdset == 0xFFFF) return 0; return cmdset & x; } return 0;}static intatadone(void* arg){ return ((Ctlr*)arg)->done;}static intatarwmmode(Drive* drive, int cmdport, int ctlport, int dev){ int as, maxrwm, rwm; maxrwm = (drive->info[Imaxrwm] & 0xFF); if(maxrwm == 0) return 0; /* * Sometimes drives come up with the current count set * to 0; if so, set a suitable value, otherwise believe * the value in Irwm if the 0x100 bit is set. */ if(drive->info[Irwm] & 0x100) rwm = (drive->info[Irwm] & 0xFF); else rwm = 0; if(rwm == 0) rwm = maxrwm; if(rwm > 16) rwm = 16; if(ataready(cmdport, ctlport, dev, Bsy|Drq, Drdy, 102*1000) < 0) return 0; outb(cmdport+Count, rwm); outb(cmdport+Command, Csm); microdelay(4); as = ataready(cmdport, ctlport, 0, Bsy, Drdy|Df|Err, 1000); inb(cmdport+Status); if(as < 0 || (as & (Df|Err))) return 0; drive->rwm = rwm; return rwm;}static intatadmamode(Drive* drive){ int dma; /* * Check if any DMA mode enabled. * Assumes the BIOS has picked and enabled the best. * This is completely passive at the moment, no attempt is * made to ensure the hardware is correctly set up. */ dma = drive->info[Imwdma] & 0x0707; drive->dma = (dma>>8) & dma; if(drive->dma == 0 && (drive->info[Ivalid] & 0x04)){ dma = drive->info[Iudma] & 0x1F1F; drive->dma = (dma>>8) & dma; if(drive->dma) drive->dma |= 'U'<<16; } return dma;}static intataidentify(int cmdport, int ctlport, int dev, int pkt, void* info){ int as, command, drdy; if(pkt){ command = Cidpkt; drdy = 0; } else{ command = Cid; drdy = Drdy; } as = ataready(cmdport, ctlport, dev, Bsy|Drq, drdy, 103*1000); if(as < 0) return as; outb(cmdport+Command, command); microdelay(4); as = ataready(cmdport, ctlport, 0, Bsy, Drq|Err, 400*1000); if(as < 0) return -1; if(as & Err) return as; memset(info, 0, 512); inss(cmdport+Data, info, 256); inb(cmdport+Status); if(DEBUG & DbgIDENTIFY){ int i; ushort *sp; sp = (ushort*)info; for(i = 0; i < 32; i++){ if(i && (i%16) == 0) print("\n"); print(" %4.4uX", *sp); sp++; } print("\n"); } return 0;}static Drive*atadrive(int cmdport, int ctlport, int dev){ ushort *sp; Drive *drive; int as, i, pkt; uchar buf[512], *p; atadebug(0, 0, "identify: port 0x%uX dev 0x%2.2uX\n", cmdport, dev); pkt = 1;retry: as = ataidentify(cmdport, ctlport, dev, pkt, buf); if(as < 0) return nil; if(as & Err){ if(pkt == 0) return nil; pkt = 0; goto retry; } if((drive = malloc(sizeof(Drive))) == nil) return nil; drive->dev = dev; memmove(drive->info, buf, sizeof(drive->info)); drive->sense[0] = 0x70; drive->sense[7] = sizeof(drive->sense)-7; drive->inquiry[2] = 2; drive->inquiry[3] = 2; drive->inquiry[4] = sizeof(drive->inquiry)-4; p = &drive->inquiry[8]; sp = &drive->info[Imodel]; for(i = 0; i < 20; i++){ *p++ = *sp>>8; *p++ = *sp++; } drive->secsize = 512; if(drive->info[Iconfig] != 0x848A && (drive->info[Iconfig] & 0xC000) == 0x8000){ if(drive->info[Iconfig] & 0x01) drive->pkt = 16; else drive->pkt = 12; } else{ if(drive->info[Ivalid] & 0x0001){ drive->c = drive->info[Ilcyl]; drive->h = drive->info[Ilhead]; drive->s = drive->info[Ilsec]; } else{ drive->c = drive->info[Iccyl]; drive->h = drive->info[Ichead]; drive->s = drive->info[Icsec]; } if(drive->info[Icapabilities] & 0x0200){ drive->sectors = (drive->info[Ilba1]<<16) |drive->info[Ilba0]; drive->dev |= Lba; } else drive->sectors = drive->c*drive->h*drive->s; atarwmmode(drive, cmdport, ctlport, dev); } atadmamode(drive); if(DEBUG & DbgCONFIG){ print("dev %2.2uX port %uX config %4.4uX capabilities %4.4uX", dev, cmdport, drive->info[Iconfig], drive->info[Icapabilities]); print(" mwdma %4.4uX", drive->info[Imwdma]); if(drive->info[Ivalid] & 0x04) print(" udma %4.4uX", drive->info[Iudma]); print(" dma %8.8uX rwm %ud\n", drive->dma, drive->rwm); } return drive;}static voidatasrst(int ctlport){ /* * Srst is a big stick and may cause problems if further * commands are tried before the drives become ready again. * Also, there will be problems here if overlapped commands * are ever supported. */ microdelay(20); outb(ctlport+Dc, Srst); microdelay(20); outb(ctlport+Dc, 0); microdelay(4*1000);}static SDev*ataprobe(int cmdport, int ctlport, int irq){ Ctlr* ctlr; SDev *sdev; Drive *drive; int dev, error, rhi, rlo; /* * Try to detect a floating bus. * Bsy should be cleared. If not, see if the cylinder registers * are read/write capable. * If the master fails, try the slave to catch slave-only * configurations. * There's no need to restore the tested registers as they will * be reset on any detected drives by the Cedd command. * All this indicates is that there is at least one drive on the * controller; when the non-existent drive is selected in a * single-drive configuration the registers of the existing drive * are often seen, only command execution fails. */ dev = Dev0; if(inb(ctlport+As) & Bsy){ outb(cmdport+Dh, dev); microdelay(5);trydev1: atadebug(cmdport, ctlport, "ataprobe bsy"); outb(cmdport+Cyllo, 0xAA); outb(cmdport+Cylhi, 0x55); outb(cmdport+Sector, 0xFF); rlo = inb(cmdport+Cyllo); rhi = inb(cmdport+Cylhi); if(rlo != 0xAA && (rlo == 0xFF || rhi != 0x55)){ if(dev == Dev1){release: return nil; } dev = Dev1; if(ataready(cmdport, ctlport, dev, Bsy, 0, 20*1000) < 0) goto trydev1; } } /* * Disable interrupts on any detected controllers. */ outb(ctlport+Dc, Nien);tryedd1: if(ataready(cmdport, ctlport, dev, Bsy|Drq, 0, 105*1000) < 0){ /* * There's something there, but it didn't come up clean, * so try hitting it with a big stick. The timing here is * wrong but this is a last-ditch effort and it sometimes * gets some marginal hardware back online. */ atasrst(ctlport); if(ataready(cmdport, ctlport, dev, Bsy|Drq, 0, 106*1000) < 0) goto release; } /* * Can only get here if controller is not busy. * If there are drives Bsy will be set within 400nS, * must wait 2mS before testing Status. * Wait for the command to complete (6 seconds max). */ outb(cmdport+Command, Cedd); delay(5); if(ataready(cmdport, ctlport, dev, Bsy|Drq, 0, 6*1000*1000) < 0) goto release; /* * If bit 0 of the error register is set then the selected drive * exists. This is enough to detect single-drive configurations. * However, if the master exists there is no way short of executing * a command to determine if a slave is present. * It appears possible to get here testing Dev0 although it doesn't * exist and the EDD won't take, so try again with Dev1. */ error = inb(cmdport+Error); atadebug(cmdport, ctlport, "ataprobe: dev %uX", dev); if((error & ~0x80) != 0x01){ if(dev == Dev1) goto release; dev = Dev1; goto tryedd1; } /* * At least one drive is known to exist, try to * identify it. If that fails, don't bother checking * any further. * If the one drive found is Dev0 and the EDD command
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -