// Simple PIO-based (non-DMA) IDE driver code. #include "types.h" #include "defs.h" #include "param.h" #include "memlayout.h" #include "mmu.h" #include "proc.h" #include "x86.h" #include "traps.h" #include "spinlock.h" #include "sleeplock.h" #include "fs.h" #include "buf.h" #define SECTOR_SIZE 512 #define IDE_BSY 0x80 #define IDE_DRDY 0x40 #define IDE_DF 0x20 #define IDE_ERR 0x01 #define IDE_CMD_READ 0x20 #define IDE_CMD_WRITE 0x30 #define IDE_CMD_RDMUL 0xc4 #define IDE_CMD_WRMUL 0xc5 // idequeue points to the buf now being read/written to the disk. // idequeue->qnext points to the next buf to be processed. // You must hold idelock while manipulating queue. static struct spinlock idelock; static struct buf *idequeue; static int havedisk1; static void idestart(struct buf*); // Wait for IDE disk to become ready. static int idewait(int checkerr) { int r; while (((r = inb(0x1f7)) & (IDE_BSY | IDE_DRDY)) != IDE_DRDY) { ; } if (checkerr && (r & (IDE_DF | IDE_ERR)) != 0) { return -1; } return 0; } void ideinit(void) { int i; initlock(&idelock, "ide"); ioapicenable(IRQ_IDE, ncpu - 1); idewait(0); // Check if disk 1 is present outb(0x1f6, 0xe0 | (1 << 4)); for (i = 0; i < 1000; i++) { if (inb(0x1f7) != 0) { havedisk1 = 1; break; } } // Switch back to disk 0. outb(0x1f6, 0xe0 | (0 << 4)); } // Start the request for b. Caller must hold idelock. static void idestart(struct buf *b) { if (b == 0) { panic("idestart"); } if (b->blockno >= FSSIZE) { panic("incorrect blockno"); } int sector_per_block = BSIZE / SECTOR_SIZE; int sector = b->blockno * sector_per_block; int read_cmd = (sector_per_block == 1) ? IDE_CMD_READ : IDE_CMD_RDMUL; int write_cmd = (sector_per_block == 1) ? IDE_CMD_WRITE : IDE_CMD_WRMUL; if (sector_per_block > 7) { panic("idestart"); } idewait(0); outb(0x3f6, 0); // generate interrupt outb(0x1f2, sector_per_block); // number of sectors outb(0x1f3, sector & 0xff); outb(0x1f4, (sector >> 8) & 0xff); outb(0x1f5, (sector >> 16) & 0xff); outb(0x1f6, 0xe0 | ((b->dev & 1) << 4) | ((sector >> 24) & 0x0f)); if (b->flags & B_DIRTY) { outb(0x1f7, write_cmd); outsl(0x1f0, b->data, BSIZE / 4); } else { outb(0x1f7, read_cmd); } } // Interrupt handler. void ideintr(void) { struct buf *b; // First queued buffer is the active request. acquire(&idelock); if ((b = idequeue) == 0) { release(&idelock); return; } idequeue = b->qnext; // Read data if needed. if (!(b->flags & B_DIRTY) && idewait(1) >= 0) { insl(0x1f0, b->data, BSIZE / 4); } // Wake process waiting for this buf. b->flags |= B_VALID; b->flags &= ~B_DIRTY; wakeup(b); // Start disk on next buf in queue. if (idequeue != 0) { idestart(idequeue); } release(&idelock); } // Sync buf with disk. // If B_DIRTY is set, write buf to disk, clear B_DIRTY, set B_VALID. // Else if B_VALID is not set, read buf from disk, set B_VALID. void iderw(struct buf *b) { struct buf **pp; if (!holdingsleep(&b->lock)) { panic("iderw: buf not locked"); } if ((b->flags & (B_VALID | B_DIRTY)) == B_VALID) { panic("iderw: nothing to do"); } if (b->dev != 0 && !havedisk1) { panic("iderw: ide disk 1 not present"); } acquire(&idelock); //DOC:acquire-lock // Append b to idequeue. b->qnext = 0; for (pp = &idequeue; *pp; pp = &(*pp)->qnext) { //DOC:insert-queue ; } *pp = b; // Start disk if necessary. if (idequeue == b) { idestart(b); } // Wait for request to finish. while ((b->flags & (B_VALID | B_DIRTY)) != B_VALID) { sleep(b, &idelock); } release(&idelock); }