xv6

port of xv6 to x86-64
git clone http://frotz.net/git/xv6.git
Log | Files | Refs | README | LICENSE

ide.c (3221B)


      1 // Simple PIO-based (non-DMA) IDE driver code.
      2 
      3 #include "types.h"
      4 #include "defs.h"
      5 #include "param.h"
      6 #include "memlayout.h"
      7 #include "mmu.h"
      8 #include "proc.h"
      9 #include "x86.h"
     10 #include "traps.h"
     11 #include "spinlock.h"
     12 #include "buf.h"
     13 
     14 #define IDE_BSY       0x80
     15 #define IDE_DRDY      0x40
     16 #define IDE_DF        0x20
     17 #define IDE_ERR       0x01
     18 
     19 #define IDE_CMD_READ  0x20
     20 #define IDE_CMD_WRITE 0x30
     21 
     22 // idequeue points to the buf now being read/written to the disk.
     23 // idequeue->qnext points to the next buf to be processed.
     24 // You must hold idelock while manipulating queue.
     25 
     26 static struct spinlock idelock;
     27 static struct buf *idequeue;
     28 
     29 static int havedisk1;
     30 static void idestart(struct buf*);
     31 
     32 // Wait for IDE disk to become ready.
     33 static int
     34 idewait(int checkerr)
     35 {
     36   int r;
     37 
     38   while(((r = inb(0x1f7)) & (IDE_BSY|IDE_DRDY)) != IDE_DRDY) 
     39     ;
     40   if(checkerr && (r & (IDE_DF|IDE_ERR)) != 0)
     41     return -1;
     42   return 0;
     43 }
     44 
     45 void
     46 ideinit(void)
     47 {
     48   int i;
     49 
     50   initlock(&idelock, "ide");
     51   picenable(IRQ_IDE);
     52   ioapicenable(IRQ_IDE, ncpu - 1);
     53   idewait(0);
     54   
     55   // Check if disk 1 is present
     56   outb(0x1f6, 0xe0 | (1<<4));
     57   for(i=0; i<1000; i++){
     58     if(inb(0x1f7) != 0){
     59       havedisk1 = 1;
     60       break;
     61     }
     62   }
     63   
     64   // Switch back to disk 0.
     65   outb(0x1f6, 0xe0 | (0<<4));
     66 }
     67 
     68 // Start the request for b.  Caller must hold idelock.
     69 static void
     70 idestart(struct buf *b)
     71 {
     72   if(b == 0)
     73     panic("idestart");
     74 
     75   idewait(0);
     76   outb(0x3f6, 0);  // generate interrupt
     77   outb(0x1f2, 1);  // number of sectors
     78   outb(0x1f3, b->sector & 0xff);
     79   outb(0x1f4, (b->sector >> 8) & 0xff);
     80   outb(0x1f5, (b->sector >> 16) & 0xff);
     81   outb(0x1f6, 0xe0 | ((b->dev&1)<<4) | ((b->sector>>24)&0x0f));
     82   if(b->flags & B_DIRTY){
     83     outb(0x1f7, IDE_CMD_WRITE);
     84     outsl(0x1f0, b->data, 512/4);
     85   } else {
     86     outb(0x1f7, IDE_CMD_READ);
     87   }
     88 }
     89 
     90 // Interrupt handler.
     91 void
     92 ideintr(void)
     93 {
     94   struct buf *b;
     95 
     96   // First queued buffer is the active request.
     97   acquire(&idelock);
     98   if((b = idequeue) == 0){
     99     release(&idelock);
    100     // cprintf("spurious IDE interrupt\n");
    101     return;
    102   }
    103   idequeue = b->qnext;
    104 
    105   // Read data if needed.
    106   if(!(b->flags & B_DIRTY) && idewait(1) >= 0)
    107     insl(0x1f0, b->data, 512/4);
    108   
    109   // Wake process waiting for this buf.
    110   b->flags |= B_VALID;
    111   b->flags &= ~B_DIRTY;
    112   wakeup(b);
    113   
    114   // Start disk on next buf in queue.
    115   if(idequeue != 0)
    116     idestart(idequeue);
    117 
    118   release(&idelock);
    119 }
    120 
    121 //PAGEBREAK!
    122 // Sync buf with disk. 
    123 // If B_DIRTY is set, write buf to disk, clear B_DIRTY, set B_VALID.
    124 // Else if B_VALID is not set, read buf from disk, set B_VALID.
    125 void
    126 iderw(struct buf *b)
    127 {
    128   struct buf **pp;
    129 
    130   if(!(b->flags & B_BUSY))
    131     panic("iderw: buf not busy");
    132   if((b->flags & (B_VALID|B_DIRTY)) == B_VALID)
    133     panic("iderw: nothing to do");
    134   if(b->dev != 0 && !havedisk1)
    135     panic("iderw: ide disk 1 not present");
    136 
    137   acquire(&idelock);  //DOC:acquire-lock
    138 
    139   // Append b to idequeue.
    140   b->qnext = 0;
    141   for(pp=&idequeue; *pp; pp=&(*pp)->qnext)  //DOC:insert-queue
    142     ;
    143   *pp = b;
    144   
    145   // Start disk if necessary.
    146   if(idequeue == b)
    147     idestart(b);
    148   
    149   // Wait for request to finish.
    150   while((b->flags & (B_VALID|B_DIRTY)) != B_VALID){
    151     sleep(b, &idelock);
    152   }
    153 
    154   release(&idelock);
    155 }