From: Peter Dinda Date: Fri, 8 Feb 2008 22:27:57 +0000 (+0000) Subject: Added IDE driver stuff (not working for anything >plain old ide X-Git-Tag: JACK-WORKING-STATIC-GEEKOS~10 X-Git-Url: http://v3vee.org/palacios/gitweb/gitweb.cgi?p=palacios.git;a=commitdiff_plain;h=d83e2708c74101e906f251b872223c6545afe3a1 Added IDE driver stuff (not working for anything >plain old ide --- diff --git a/palacios/include/geekos/blockdev.h b/palacios/include/geekos/blockdev.h new file mode 100644 index 0000000..a099da8 --- /dev/null +++ b/palacios/include/geekos/blockdev.h @@ -0,0 +1,137 @@ +/* + * Block devices + * Copyright (c) 2003 David H. Hovemeyer + * Copyright (c) 2003, Jeffrey K. Hollingsworth + * $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 +#include +#include +#include + +#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 index 0000000..5c33ecc --- /dev/null +++ b/palacios/include/geekos/ide.h @@ -0,0 +1,19 @@ +/* + * ATA (aka IDE) driver. + * Copyright (c) 2003, Jeffrey K. Hollingsworth + * $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 index 0000000..5a4bf07 --- /dev/null +++ b/palacios/src/geekos/blockdev.c @@ -0,0 +1,265 @@ +/* + * Block devices + * Copyright (c) 2003 David H. Hovemeyer + * $Revision: 1.1 $ + * + * This is free software. You are permitted to use, + * redistribute, and modify it as specified in the file "COPYING". + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/*#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 index 0000000..22b02c2 --- /dev/null +++ b/palacios/src/geekos/ide.c @@ -0,0 +1,442 @@ +/* + * ATA (aka IDE) driver. + * Copyright (c) 2003, Jeffrey K. Hollingsworth + * Copyright (c) 2003,2004 David H. Hovemeyer + * $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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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); +}