Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


Added IDE driver stuff (not working for anything >plain old ide
Peter Dinda [Fri, 8 Feb 2008 22:27:57 +0000 (22:27 +0000)]
palacios/include/geekos/blockdev.h [new file with mode: 0644]
palacios/include/geekos/ide.h [new file with mode: 0644]
palacios/src/geekos/blockdev.c [new file with mode: 0644]
palacios/src/geekos/ide.c [new file with mode: 0644]

diff --git a/palacios/include/geekos/blockdev.h b/palacios/include/geekos/blockdev.h
new file mode 100644 (file)
index 0000000..a099da8
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * Block devices
+ * Copyright (c) 2003 David H. Hovemeyer <daveho@cs.umd.edu>
+ * Copyright (c) 2003, Jeffrey K. Hollingsworth <hollings@cs.umd.edu>
+ * $Revision: 1.1 $
+ * 
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef GEEKOS_BLOCKDEV_H
+#define GEEKOS_BLOCKDEV_H
+
+#include <geekos/ktypes.h>
+#include <geekos/kthread.h>
+#include <geekos/list.h>
+#include <geekos/fileio.h>
+
+#ifdef GEEKOS
+
+/*
+ * Type of block device request.
+ */
+enum Request_Type {
+    BLOCK_READ, BLOCK_WRITE
+};
+
+/*
+ * State of a block I/O request.
+ */
+enum Request_State {
+    PENDING, COMPLETED, ERROR
+};
+
+struct Block_Request;
+
+/*
+ * List of block I/O requests.
+ */
+DEFINE_LIST(Block_Request_List, Block_Request);
+
+/*
+ * An I/O request for a block device.
+ */
+struct Block_Request {
+    struct Block_Device *dev;
+    enum Request_Type type;
+    int blockNum;
+    void *buf;
+    volatile enum Request_State state;
+    volatile int errorCode;
+    struct Thread_Queue waitQueue;
+
+    DEFINE_LINK(Block_Request_List, Block_Request);
+};
+
+IMPLEMENT_LIST(Block_Request_List, Block_Request);
+
+struct Block_Device;
+struct Block_Device_Ops;
+
+/*
+ * A block device.
+ */
+struct Block_Device {
+    char name[BLOCKDEV_MAX_NAME_LEN];
+    struct Block_Device_Ops *ops;
+    int unit;
+    bool inUse;
+    void *driverData;
+    struct Thread_Queue *waitQueue;
+    struct Block_Request_List *requestQueue;
+
+    DEFINE_LINK(Block_Device_List, Block_Device);
+};
+
+/*
+ * Operations that may be requested on block devices.
+ */
+struct Block_Device_Ops {
+    int (*Open)(struct Block_Device *dev);
+    int (*Close)(struct Block_Device *dev);
+    int (*Get_Num_Blocks)(struct Block_Device *dev);
+};
+
+/*
+ * Low level block device API.
+ * Only block device drivers need to use these functions.
+ */
+int Register_Block_Device(const char *name, struct Block_Device_Ops *ops,
+    int unit, void *driverData, struct Thread_Queue *waitQueue,
+    struct Block_Request_List *requestQueue);
+int Open_Block_Device(const char *name, struct Block_Device **pDev);
+int Close_Block_Device(struct Block_Device *dev);
+struct Block_Request *Create_Request(struct Block_Device *dev, enum Request_Type type,
+    int blockNum, void *buf);
+void Post_Request_And_Wait(struct Block_Request *request);
+struct Block_Request *Dequeue_Request(struct Block_Request_List *requestQueue,
+    struct Thread_Queue *waitQueue);
+void Notify_Request_Completion(struct Block_Request *request, enum Request_State state, int errorCode);
+
+/*
+ * High level block device API.
+ * For use by filesystem and disk paging code.
+ */
+int Block_Read(struct Block_Device *dev, int blockNum, void *buf);
+int Block_Write(struct Block_Device *dev, int blockNum, void *buf);
+int Get_Num_Blocks(struct Block_Device *dev);
+
+/*
+ * Misc. routines
+ */
+
+/*
+ * Round offset up to nearest sector.
+ */
+static __inline__ ulong_t Round_Up_To_Block(ulong_t offset)
+{
+    return (offset % SECTOR_SIZE) == 0
+       ? offset
+       : offset + (SECTOR_SIZE - (offset % SECTOR_SIZE));
+}
+
+/*
+ * Round offset down to nearest sector.
+ */
+static __inline__ ulong_t Round_Down_To_Block(ulong_t offset)
+{
+    return (offset % SECTOR_SIZE) == 0
+       ? offset
+       : offset - (offset % SECTOR_SIZE);
+}
+
+#endif /* GEEKOS */
+
+#endif
+
diff --git a/palacios/include/geekos/ide.h b/palacios/include/geekos/ide.h
new file mode 100644 (file)
index 0000000..5c33ecc
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * ATA (aka IDE) driver.
+ * Copyright (c) 2003, Jeffrey K. Hollingsworth <hollings@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef GEEKOS_IDE_H
+#define GEEKOS_IDE_H
+
+#ifdef GEEKOS
+
+void Init_IDE(void);
+
+#endif  /* GEEKOS */
+
+#endif  /* GEEKOS_IDE_H */
diff --git a/palacios/src/geekos/blockdev.c b/palacios/src/geekos/blockdev.c
new file mode 100644 (file)
index 0000000..5a4bf07
--- /dev/null
@@ -0,0 +1,265 @@
+/*
+ * Block devices
+ * Copyright (c) 2003 David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ * 
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#include <geekos/errno.h>
+#include <geekos/screen.h>
+#include <geekos/string.h>
+#include <geekos/malloc.h>
+#include <geekos/int.h>
+#include <geekos/kthread.h>
+#include <geekos/synch.h>
+#include <geekos/blockdev.h>
+
+/*#define BLOCKDEV_DEBUG */
+#ifdef BLOCKDEV_DEBUG
+#  define Debug(args...) Print(args)
+#else
+#  define Debug(args...)
+#endif
+
+/* ----------------------------------------------------------------------
+ * Private data and functions
+ * ---------------------------------------------------------------------- */
+
+/*
+ * Lock protecting access/modification of block device list.
+ */
+static struct Mutex s_blockdevLock;
+
+/*
+ * List datatype for list of block devices.
+ */
+DEFINE_LIST(Block_Device_List, Block_Device);
+IMPLEMENT_LIST(Block_Device_List, Block_Device);
+
+/*
+ * The list in which all block devices in the system
+ * are registered.
+ */
+static struct Block_Device_List s_deviceList;
+
+/*
+ * Perform a block IO request.
+ * Returns 0 if successful, error code on failure.
+ */
+static int Do_Request(struct Block_Device *dev, enum Request_Type type, int blockNum, void *buf)
+{
+    struct Block_Request *request;
+    int rc;
+
+    request = Create_Request(dev, type, blockNum, buf);
+    if (request == 0)
+       return ENOMEM;
+    Post_Request_And_Wait(request);
+    rc = request->errorCode;
+    Free(request);
+    return rc;
+}
+
+/* ----------------------------------------------------------------------
+ * Public functions
+ * ---------------------------------------------------------------------- */
+
+/*
+ * Register a block device.
+ * This should be called by device drivers in their Init
+ * functions to register all detected devices.
+ * Returns 0 if successful, error code otherwise.
+ */
+int Register_Block_Device(const char *name, struct Block_Device_Ops *ops,
+    int unit, void *driverData, struct Thread_Queue *waitQueue,
+    struct Block_Request_List *requestQueue)
+{
+    struct Block_Device *dev;
+
+    KASSERT(ops != 0);
+    KASSERT(waitQueue != 0);
+    KASSERT(requestQueue != 0);
+
+    dev = (struct Block_Device*) Malloc(sizeof(*dev));
+    if (dev == 0)
+       return ENOMEM;
+
+    strcpy(dev->name, name);
+    dev->ops = ops;
+    dev->unit = unit;
+    dev->inUse = false;
+    dev->driverData = driverData;
+    dev->waitQueue = waitQueue;
+    dev->requestQueue = requestQueue;
+
+    Mutex_Lock(&s_blockdevLock);
+    /* FIXME: handle name conflict with existing device */
+    Debug("Registering block device %s\n", dev->name);
+    Add_To_Back_Of_Block_Device_List(&s_deviceList, dev);
+    Mutex_Unlock(&s_blockdevLock);
+
+    return 0;
+}
+
+/*
+ * Open a named block device.
+ * Return 0 if successful, error code on error.
+ */
+int Open_Block_Device(const char *name, struct Block_Device **pDev)
+{
+    struct Block_Device *dev;
+    int rc = 0;
+
+    Mutex_Lock(&s_blockdevLock);
+
+    dev = Get_Front_Of_Block_Device_List(&s_deviceList);
+    while (dev != 0) {
+       if (strcmp(dev->name, name) == 0)
+           break;
+       dev = Get_Next_In_Block_Device_List(dev);
+    }
+
+    if (dev == 0)
+       rc = ENODEV;
+    else if (dev->inUse)
+       rc = EBUSY;
+    else {
+       rc = dev->ops->Open(dev);
+       if (rc == 0) {
+           *pDev = dev;
+           dev->inUse = true;
+       }
+    }
+
+    Mutex_Unlock(&s_blockdevLock);
+
+    return rc;
+}
+
+/*
+ * Close given block device.
+ * Return 0 if successful, error code on error.
+ */
+int Close_Block_Device(struct Block_Device *dev)
+{
+    int rc;
+
+    Mutex_Lock(&s_blockdevLock);
+
+    KASSERT(dev->inUse);
+    rc = dev->ops->Close(dev);
+    if (rc == 0)
+       dev->inUse = false;
+
+    Mutex_Unlock(&s_blockdevLock);
+
+    return rc;
+}
+
+/*
+ * Create a block device request to transfer a single block.
+ */
+struct Block_Request *Create_Request(struct Block_Device *dev, enum Request_Type type,
+    int blockNum, void *buf)
+{
+    struct Block_Request *request = Malloc(sizeof(*request));
+    if (request != 0) {
+       request->dev = dev;
+       request->type = type;
+       request->blockNum = blockNum;
+       request->buf = buf;
+       request->state = PENDING;
+       Clear_Thread_Queue(&request->waitQueue);
+    }
+    return request;
+}
+
+/*
+ * Send a block IO request to a device and wait for it to be handled.
+ * Returns when the driver completes the requests or signals
+ * an error.
+ */
+void Post_Request_And_Wait(struct Block_Request *request)
+{
+    struct Block_Device *dev;
+
+    KASSERT(request != 0);
+
+    dev = request->dev;
+    KASSERT(dev != 0);
+
+    /* Send request to the driver */
+    Debug("Posting block device request [@%x]...\n", request);
+    Disable_Interrupts();
+    Add_To_Back_Of_Block_Request_List(dev->requestQueue, request);
+    Wake_Up(dev->waitQueue);
+    Enable_Interrupts();
+
+    /* Wait for request to be processed */
+    Disable_Interrupts();
+    while (request->state == PENDING) {
+       Debug("Waiting, state=%d\n", request->state);
+       Wait(&request->waitQueue);
+    }
+    Debug("Wait completed!\n");
+    Enable_Interrupts();
+}
+
+/*
+ * Wait for a block request to arrive.
+ */
+struct Block_Request *Dequeue_Request(struct Block_Request_List *requestQueue,
+    struct Thread_Queue *waitQueue)
+{
+    struct Block_Request *request;
+
+    Disable_Interrupts();
+    while (Is_Block_Request_List_Empty(requestQueue))
+       Wait(waitQueue);
+    request = Get_Front_Of_Block_Request_List(requestQueue);
+    Remove_From_Front_Of_Block_Request_List(requestQueue);
+    Enable_Interrupts();
+
+    return request;
+}
+
+/*
+ * Signal the completion of a block request.
+ */
+void Notify_Request_Completion(struct Block_Request *request, enum Request_State state, int errorCode)
+{
+    Disable_Interrupts();
+    request->state = state;
+    request->errorCode = errorCode;
+    Wake_Up(&request->waitQueue);
+    Enable_Interrupts();
+}
+
+/*
+ * Read a block from given device.
+ * Return 0 if successful, error code on error.
+ */
+int Block_Read(struct Block_Device *dev, int blockNum, void *buf)
+{
+    return Do_Request(dev, BLOCK_READ, blockNum, buf);
+}
+
+/*
+ * Write a block to given device.
+ * Return 0 if successful, error code on error.
+ */
+int Block_Write(struct Block_Device *dev, int blockNum, void *buf)
+{
+    return Do_Request(dev, BLOCK_WRITE, blockNum, buf);
+}
+
+/*
+ * Get number of blocks in given device.
+ */
+int Get_Num_Blocks(struct Block_Device *dev)
+{
+    return dev->ops->Get_Num_Blocks(dev);
+}
+
diff --git a/palacios/src/geekos/ide.c b/palacios/src/geekos/ide.c
new file mode 100644 (file)
index 0000000..22b02c2
--- /dev/null
@@ -0,0 +1,442 @@
+/*
+ * ATA (aka IDE) driver.
+ * Copyright (c) 2003, Jeffrey K. Hollingsworth <hollings@cs.umd.edu>
+ * Copyright (c) 2003,2004 David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+/*
+ * NOTES:
+ * 12/22/03 - Converted to use new block device layer with queued requests
+ *  1/20/04 - Changed probing of drives to work on Bochs 2.0 with 2 drives
+ */
+
+#include <geekos/serial.h>
+
+#include <geekos/ktypes.h>
+#include <geekos/kassert.h>
+#include <geekos/errno.h>
+#include <geekos/malloc.h>
+#include <geekos/string.h>
+#include <geekos/io.h>
+#include <geekos/int.h>
+#include <geekos/screen.h>
+#include <geekos/timer.h>
+#include <geekos/kthread.h>
+#include <geekos/blockdev.h>
+#include <geekos/ide.h>
+
+/* Registers */
+#define IDE_DATA_REGISTER              0x1f0
+#define IDE_ERROR_REGISTER             0x1f1
+#define IDE_FEATURE_REG                        IDE_ERROR_REGISTER
+#define IDE_SECTOR_COUNT_REGISTER      0x1f2
+#define IDE_SECTOR_NUMBER_REGISTER     0x1f3
+#define IDE_CYLINDER_LOW_REGISTER      0x1f4
+#define IDE_CYLINDER_HIGH_REGISTER     0x1f5
+#define IDE_DRIVE_HEAD_REGISTER                0x1f6
+#define IDE_STATUS_REGISTER            0x1f7
+#define IDE_COMMAND_REGISTER           0x1f7
+#define IDE_DEVICE_CONTROL_REGISTER    0x3F6
+
+/* Drives */
+#define IDE_DRIVE_0                    0xa0
+#define IDE_DRIVE_1                    0xb0
+
+/* Commands */
+#define IDE_COMMAND_IDENTIFY_DRIVE     0xEC
+#define IDE_COMMAND_SEEK               0x70
+#define IDE_COMMAND_READ_SECTORS       0x21
+#define IDE_COMMAND_READ_BUFFER                0xE4
+#define IDE_COMMAND_WRITE_SECTORS      0x30
+#define IDE_COMMAND_WRITE_BUFFER       0xE8
+#define IDE_COMMAND_DIAGNOSTIC         0x90
+#define IDE_COMMAND_ATAPI_IDENT_DRIVE  0xA1
+
+/* Results words from Identify Drive Request */
+#define        IDE_INDENTIFY_NUM_CYLINDERS     0x01
+#define        IDE_INDENTIFY_NUM_HEADS         0x03
+#define        IDE_INDENTIFY_NUM_BYTES_TRACK   0x04
+#define        IDE_INDENTIFY_NUM_BYTES_SECTOR  0x05
+#define        IDE_INDENTIFY_NUM_SECTORS_TRACK 0x06
+
+/* bits of Status Register */
+#define IDE_STATUS_DRIVE_BUSY          0x80
+#define IDE_STATUS_DRIVE_READY         0x40
+#define IDE_STATUS_DRIVE_WRITE_FAULT   0x20
+#define IDE_STATUS_DRIVE_SEEK_COMPLETE 0x10
+#define IDE_STATUS_DRIVE_DATA_REQUEST  0x08
+#define IDE_STATUS_DRIVE_CORRECTED_DATA        0x04
+#define IDE_STATUS_DRIVE_INDEX         0x02
+#define IDE_STATUS_DRIVE_ERROR         0x01
+
+/* Bits of Device Control Register */
+#define IDE_DCR_NOINTERRUPT            0x02
+#define IDE_DCR_RESET                  0x04
+
+/* Return codes from various IDE_* functions */
+#define        IDE_ERROR_NO_ERROR      0
+#define        IDE_ERROR_BAD_DRIVE     -1
+#define        IDE_ERROR_INVALID_BLOCK -2
+#define        IDE_ERROR_DRIVE_ERROR   -3
+
+/* Control register bits */
+#define IDE_CONTROL_REGISTER           0x3F6
+#define IDE_CONTROL_SOFTWARE_RESET     0x04
+#define IDE_CONTROL_INT_DISABLE                0x02
+
+#define LOW_BYTE(x)    (x & 0xff)
+#define HIGH_BYTE(x)   ((x >> 8) & 0xff)
+
+#define IDE_MAX_DRIVES                 2
+
+typedef struct {
+    short num_Cylinders;
+    short num_Heads;
+    short num_SectorsPerTrack;
+    short num_BytesPerSector;
+} ideDisk;
+
+int ideDebug = 99;
+static int numDrives;
+static ideDisk drives[IDE_MAX_DRIVES];
+
+struct Thread_Queue s_ideWaitQueue;
+struct Block_Request_List s_ideRequestQueue;
+
+/*
+ * return the number of logical blocks for a particular drive.
+ *
+ */
+static int IDE_getNumBlocks(int driveNum)
+{
+    if (driveNum < 0 || driveNum > IDE_MAX_DRIVES) {
+        return IDE_ERROR_BAD_DRIVE;
+    }
+
+    return (drives[driveNum].num_Heads * 
+            drives[driveNum].num_SectorsPerTrack *
+           drives[driveNum].num_Cylinders);
+}
+
+/*
+ * Read a block at the logical block number indicated.
+ */
+static int IDE_Read(int driveNum, int blockNum, char *buffer)
+{
+    int i;
+    int head;
+    int sector;
+    int cylinder;
+    short *bufferW;
+    int reEnable = 0;
+
+    if (driveNum < 0 || driveNum > (numDrives-1)) {
+       if (ideDebug) Print("ide: invalid drive %d\n", driveNum);
+        return IDE_ERROR_BAD_DRIVE;
+    }
+
+    if (blockNum < 0 || blockNum >= IDE_getNumBlocks(driveNum)) {
+       if (ideDebug) Print("ide: invalid block %d\n", blockNum);
+        return IDE_ERROR_INVALID_BLOCK;
+    }
+
+    if (Interrupts_Enabled()) {
+       Disable_Interrupts();
+       reEnable = 1;
+    }
+
+    /* now compute the head, cylinder, and sector */
+    sector = blockNum % drives[driveNum].num_SectorsPerTrack + 1;
+    cylinder = blockNum / (drives[driveNum].num_Heads * 
+       drives[driveNum].num_SectorsPerTrack);
+    head = (blockNum / drives[driveNum].num_SectorsPerTrack) % 
+        drives[driveNum].num_Heads;
+
+    if (ideDebug >= 2) {
+       Print ("request to read block %d\n", blockNum);
+       Print ("    head %d\n", head);
+       Print ("    cylinder %d\n", cylinder);
+       Print ("    sector %d\n", sector);
+    }
+
+    Out_Byte(IDE_SECTOR_COUNT_REGISTER, 1);
+    Out_Byte(IDE_SECTOR_NUMBER_REGISTER, sector);
+    Out_Byte(IDE_CYLINDER_LOW_REGISTER, LOW_BYTE(cylinder));
+    Out_Byte(IDE_CYLINDER_HIGH_REGISTER, HIGH_BYTE(cylinder));
+    if (driveNum == 0) {
+       Out_Byte(IDE_DRIVE_HEAD_REGISTER, IDE_DRIVE_0 | head);
+    } else if (driveNum == 1) {
+       Out_Byte(IDE_DRIVE_HEAD_REGISTER, IDE_DRIVE_1 | head);
+    }
+
+    Out_Byte(IDE_COMMAND_REGISTER, IDE_COMMAND_READ_SECTORS);
+
+    if (ideDebug > 2) Print("About to wait for Read \n");
+
+    /* wait for the drive */
+    while (In_Byte(IDE_STATUS_REGISTER) & IDE_STATUS_DRIVE_BUSY);
+
+    if (In_Byte(IDE_STATUS_REGISTER) & IDE_STATUS_DRIVE_ERROR) {
+       Print("ERROR: Got Read %d\n", In_Byte(IDE_STATUS_REGISTER));
+       return IDE_ERROR_DRIVE_ERROR;
+    }
+
+    if (ideDebug > 2) Print("got buffer \n");
+
+    bufferW = (short *) buffer;
+    for (i=0; i < 256; i++) {
+        bufferW[i] = In_Word(IDE_DATA_REGISTER);
+    }
+
+    if (reEnable) Enable_Interrupts();
+
+    return IDE_ERROR_NO_ERROR;
+}
+
+/*
+ * Write a block at the logical block number indicated.
+ */
+static int IDE_Write(int driveNum, int blockNum, char *buffer)
+{
+    int i;
+    int head;
+    int sector;
+    int cylinder;
+    short *bufferW;
+    int reEnable = 0;
+
+    if (driveNum < 0 || driveNum > (numDrives-1)) {
+        return IDE_ERROR_BAD_DRIVE;
+    }
+
+    if (blockNum < 0 || blockNum >= IDE_getNumBlocks(driveNum)) {
+        return IDE_ERROR_INVALID_BLOCK;
+    }
+
+    if (Interrupts_Enabled()) {
+       Disable_Interrupts();
+       reEnable = 1;
+    }
+
+    /* now compute the head, cylinder, and sector */
+    sector = blockNum % drives[driveNum].num_SectorsPerTrack + 1;
+    cylinder = blockNum / (drives[driveNum].num_Heads * 
+       drives[driveNum].num_SectorsPerTrack);
+    head = (blockNum / drives[driveNum].num_SectorsPerTrack) % 
+        drives[driveNum].num_Heads;
+
+    if (ideDebug) {
+       Print ("request to write block %d\n", blockNum);
+       Print ("    head %d\n", head);
+       Print ("    cylinder %d\n", cylinder);
+       Print ("    sector %d\n", sector);
+    }
+
+    Out_Byte(IDE_SECTOR_COUNT_REGISTER, 1);
+    Out_Byte(IDE_SECTOR_NUMBER_REGISTER, sector);
+    Out_Byte(IDE_CYLINDER_LOW_REGISTER, LOW_BYTE(cylinder));
+    Out_Byte(IDE_CYLINDER_HIGH_REGISTER, HIGH_BYTE(cylinder));
+    if (driveNum == 0) {
+       Out_Byte(IDE_DRIVE_HEAD_REGISTER, IDE_DRIVE_0 | head);
+    } else if (driveNum == 1) {
+       Out_Byte(IDE_DRIVE_HEAD_REGISTER, IDE_DRIVE_1 | head);
+    }
+
+    Out_Byte(IDE_COMMAND_REGISTER, IDE_COMMAND_WRITE_SECTORS);
+
+
+    /* wait for the drive */
+    while (In_Byte(IDE_STATUS_REGISTER) & IDE_STATUS_DRIVE_BUSY);
+
+    bufferW = (short *) buffer;
+    for (i=0; i < 256; i++) {
+        Out_Word(IDE_DATA_REGISTER, bufferW[i]);
+    }
+
+    if (ideDebug) Print("About to wait for Write \n");
+
+    /* wait for the drive */
+    while (In_Byte(IDE_STATUS_REGISTER) & IDE_STATUS_DRIVE_BUSY);
+
+    if (In_Byte(IDE_STATUS_REGISTER) & IDE_STATUS_DRIVE_ERROR) {
+       Print("ERROR: Got Read %d\n", In_Byte(IDE_STATUS_REGISTER));
+       return IDE_ERROR_DRIVE_ERROR;
+    }
+
+    if (reEnable) Enable_Interrupts();
+
+    return IDE_ERROR_NO_ERROR;
+}
+
+static int IDE_Open(struct Block_Device *dev)
+{
+    KASSERT(!dev->inUse);
+    return 0;
+}
+
+static int IDE_Close(struct Block_Device *dev)
+{
+    KASSERT(dev->inUse);
+    return 0;
+}
+
+static int IDE_Get_Num_Blocks(struct Block_Device *dev)
+{
+    return IDE_getNumBlocks(dev->unit);
+}
+
+static struct Block_Device_Ops s_ideDeviceOps = {
+    IDE_Open,
+    IDE_Close,
+    IDE_Get_Num_Blocks,
+};
+
+static void IDE_Request_Thread(ulong_t arg)
+{
+    for (;;) {
+       struct Block_Request *request;
+       int rc;
+
+       /* Wait for a request to arrive */
+       request = Dequeue_Request(&s_ideRequestQueue, &s_ideWaitQueue);
+
+       /* Do the I/O */
+       if (request->type == BLOCK_READ)
+           rc = IDE_Read(request->dev->unit, request->blockNum, request->buf);
+       else
+           rc = IDE_Write(request->dev->unit, request->blockNum, request->buf);
+
+       /* Notify requesting thread of final status */
+       Notify_Request_Completion(request, rc == 0 ? COMPLETED : ERROR, rc);
+    }
+}
+
+static int readDriveConfig(int drive)
+{
+    int i;
+    int status;
+    short info[256];
+    char devname[BLOCKDEV_MAX_NAME_LEN];
+    int rc;
+
+    if (ideDebug) Print("ide: about to read drive config for drive #%d\n", drive);
+
+    Out_Byte(IDE_DRIVE_HEAD_REGISTER, (drive == 0) ? IDE_DRIVE_0 : IDE_DRIVE_1);
+    Print("Set head register\n");
+
+    Out_Byte(IDE_COMMAND_REGISTER, IDE_COMMAND_IDENTIFY_DRIVE);
+
+    Print ("identify drive\n");
+
+    while (In_Byte(IDE_STATUS_REGISTER) & IDE_STATUS_DRIVE_BUSY);
+
+    Print ("Status no longer busy\n");
+
+    status = In_Byte(IDE_STATUS_REGISTER);
+
+    Print ("Status is %x\n",status);
+    /*
+     * simulate failure
+     * status = 0x50;
+     */
+    if ((status & IDE_STATUS_DRIVE_DATA_REQUEST)) {
+      Print("ide: probe found ATA drive\n");
+      /* drive responded to ATA probe */
+      for (i=0; i < 256; i++) {
+       info[i] = In_Word(IDE_DATA_REGISTER);
+      }
+      
+      drives[drive].num_Cylinders = info[IDE_INDENTIFY_NUM_CYLINDERS];
+      drives[drive].num_Heads = info[IDE_INDENTIFY_NUM_HEADS];
+      drives[drive].num_SectorsPerTrack = info[IDE_INDENTIFY_NUM_SECTORS_TRACK];
+      drives[drive].num_BytesPerSector = info[IDE_INDENTIFY_NUM_BYTES_SECTOR];
+    } else {
+       /* try for ATAPI */
+      Print("Trying for ATAPI\n");
+      Out_Byte(IDE_FEATURE_REG, 0);             /* disable dma & overlap */
+      Print("Out FEATURE REG\n");
+      
+      Out_Byte(IDE_DRIVE_HEAD_REGISTER, (drive == 0) ? IDE_DRIVE_0 : IDE_DRIVE_1);
+      Print("Out Head Select\n");
+      Out_Byte(IDE_COMMAND_REGISTER, IDE_COMMAND_ATAPI_IDENT_DRIVE);
+      Print("Out Ident drive\n");
+
+      while (In_Byte(IDE_STATUS_REGISTER) & IDE_STATUS_DRIVE_BUSY);
+      Print("No longer busy\n");
+
+      status = In_Byte(IDE_STATUS_REGISTER);
+      
+      Print("status is %x\n",status);
+      Print("ide: found atapi drive\n");
+      return -1;
+    }
+    
+    Print("    ide%d: cyl=%d, heads=%d, sectors=%d\n", drive, drives[drive].num_Cylinders,
+         drives[drive].num_Heads, drives[drive].num_SectorsPerTrack);
+    
+    /* Register the drive as a block device */
+    snprintf(devname, sizeof(devname), "ide%d", drive);
+    rc = Register_Block_Device(devname, &s_ideDeviceOps, drive, 0, &s_ideWaitQueue, &s_ideRequestQueue);
+    if (rc != 0)
+       Print("  Error: could not create block device for %s\n", devname);
+
+    return 0;
+}
+
+
+void Init_IDE(void)
+{
+    int errorCode;
+
+    // Check to see if controller 0 is present
+    Out_Byte(0x1f3,0x88);
+    if (In_Byte(0x1f3)==0x88) { 
+      Print("IDE Controller 0 is present\n");
+    }
+
+    // Check to see if controller 1 is present
+    Out_Byte(0x173,0x88);
+    if (In_Byte(0x173)==0x88) { 
+      Print("IDE Controller 1 is present\n");
+    }
+
+    Print("Initializing IDE controller (0x1f0)...\n");
+
+    /* Reset the controller and drives */
+    Out_Byte(IDE_DEVICE_CONTROL_REGISTER, IDE_DCR_NOINTERRUPT | IDE_DCR_RESET);
+    Micro_Delay(100);
+    Out_Byte(IDE_DEVICE_CONTROL_REGISTER, IDE_DCR_NOINTERRUPT);
+
+/*
+ * FIXME: This code doesn't work on Bochs 2.0.
+ *    while ((In_Byte(IDE_STATUS_REGISTER) & IDE_STATUS_DRIVE_READY) == 0)
+ *     ;
+ */
+
+    /* This code does work on Bochs 2.0. */
+    while (In_Byte(IDE_STATUS_REGISTER) & IDE_STATUS_DRIVE_BUSY)
+       ;
+
+    if (ideDebug) Print("About to run drive Diagnosis\n");
+
+    Out_Byte(IDE_COMMAND_REGISTER, IDE_COMMAND_DIAGNOSTIC);
+    while (In_Byte(IDE_STATUS_REGISTER) & IDE_STATUS_DRIVE_BUSY);
+    errorCode = In_Byte(IDE_ERROR_REGISTER);
+    if (ideDebug) Print("ide: ide error register = %x\n", errorCode);
+
+    /* Probe and register drives */
+    if (readDriveConfig(0) == 0)
+       ++numDrives;
+    if (readDriveConfig(1) == 0)
+       ++numDrives;
+    if (ideDebug) Print("Found %d IDE drives\n", numDrives);
+
+    /* Start request thread */
+    if (numDrives > 0)
+       Start_Kernel_Thread(IDE_Request_Thread, 0, PRIORITY_NORMAL, true);
+}