#include "IDEDiskDevice.h" #include #include "Process.h" #include "StdLib.h" #include "IO.h" #include "Scheduler.h" #include "PIC.h" #include //#define DISK_DEBUG #define IRQ_FIXED_DISK 14 #define IDE0_DATA 0x1F0 #define IDE0_STATUS 0x1F7 #define IDE0_COMMAND 0x1F7 enum IDECommand : byte { IDENTIFY_DRIVE = 0xEC, READ_SECTORS = 0x21, WRITE_SECTORS = 0x30, FLUSH_CACHE = 0xe7, }; enum IDEStatus : byte { BUSY = (1 << 7), DRDY = (1 << 6), DF = (1 << 5), SRV = (1 << 4), DRQ = (1 << 3), CORR = (1 << 2), IDX = (1 << 1), ERR = (1 << 0), }; Retained IDEDiskDevice::create() { return adopt(*new IDEDiskDevice); } IDEDiskDevice::IDEDiskDevice() : IRQHandler(IRQ_FIXED_DISK) , m_lock("IDEDiskDevice") { initialize(); } IDEDiskDevice::~IDEDiskDevice() { } const char* IDEDiskDevice::class_name() const { return "IDEDiskDevice"; } unsigned IDEDiskDevice::block_size() const { return 512; } bool IDEDiskDevice::read_block(unsigned index, byte* out) const { return const_cast(*this).read_sectors(index, 1, out); } bool IDEDiskDevice::write_block(unsigned index, const byte* data) { return write_sectors(index, 1, data); } static void print_ide_status(byte status) { kprintf("DRQ=%u BUSY=%u DRDY=%u SRV=%u DF=%u CORR=%u IDX=%u ERR=%u\n", (status & DRQ) != 0, (status & BUSY) != 0, (status & DRDY) != 0, (status & SRV) != 0, (status & DF) != 0, (status & CORR) != 0, (status & IDX) != 0, (status & ERR) != 0); } bool IDEDiskDevice::wait_for_irq() { #ifdef DISK_DEBUG kprintf("disk: waiting for interrupt...\n"); #endif // FIXME: Add timeout. while (!m_interrupted) { // FIXME: Put this process into a Blocked state instead, it's stupid to wake up just to check a flag. Scheduler::yield(); } #ifdef DISK_DEBUG kprintf("disk: got interrupt!\n"); #endif memory_barrier(); return true; } void IDEDiskDevice::handle_irq() { byte status = IO::in8(0x1f7); if (status & ERR) { print_ide_status(status); m_device_error = IO::in8(0x1f1); kprintf("IDEDiskDevice: Error %b!\n", m_device_error); } else { m_device_error = 0; } #ifdef DISK_DEBUG kprintf("disk:interrupt: DRQ=%u BUSY=%u DRDY=%u\n", (status & DRQ) != 0, (status & BUSY) != 0, (status & DRDY) != 0); #endif m_interrupted = true; } void IDEDiskDevice::initialize() { #ifdef DISK_DEBUG byte status = IO::in8(IDE0_STATUS); kprintf("initial status: "); print_ide_status(status); #endif m_interrupted = false; while (IO::in8(IDE0_STATUS) & BUSY); enable_irq(); IO::out8(0x1F6, 0xA0); // 0xB0 for 2nd device IO::out8(0x3F6, 0xA0); // 0xB0 for 2nd device IO::out8(IDE0_COMMAND, IDENTIFY_DRIVE); enable_irq(); wait_for_irq(); ByteBuffer wbuf = ByteBuffer::create_uninitialized(512); ByteBuffer bbuf = ByteBuffer::create_uninitialized(512); byte* b = bbuf.pointer(); word* w = (word*)wbuf.pointer(); const word* wbufbase = (word*)wbuf.pointer(); for (dword i = 0; i < 256; ++i) { word data = IO::in16(IDE0_DATA); *(w++) = data; *(b++) = MSB(data); *(b++) = LSB(data); } // "Unpad" the device name string. for (dword i = 93; i > 54 && bbuf[i] == ' '; --i) bbuf[i] = 0; m_cylinders = wbufbase[1]; m_heads = wbufbase[3]; m_sectors_per_track = wbufbase[6]; kprintf( "IDEDiskDevice: Master=\"%s\", C/H/Spt=%u/%u/%u\n", bbuf.pointer() + 54, m_cylinders, m_heads, m_sectors_per_track ); } bool IDEDiskDevice::read_sectors(dword start_sector, word count, byte* outbuf) { ASSERT(count <= 256); LOCKER(m_lock); #ifdef DISK_DEBUG dbgprintf("%s: Disk::read_sectors request (%u sector(s) @ %u)\n", current->process().name().characters(), count, start_sector); #endif disable_irq(); while (IO::in8(IDE0_STATUS) & BUSY); #ifdef DISK_DEBUG kprintf("IDEDiskDevice: Reading %u sector(s) @ LBA %u\n", count, start_sector); #endif IO::out8(0x1f2, count == 256 ? 0 : LSB(count)); IO::out8(0x1f3, start_sector & 0xff); IO::out8(0x1f4, (start_sector >> 8) & 0xff); IO::out8(0x1f5, (start_sector >> 16) & 0xff); IO::out8(0x1f6, 0xe0 | ((start_sector >> 24) & 0xf)); // 0xf0 for 2nd device IO::out8(0x3F6, 0x08); while (!(IO::in8(IDE0_STATUS) & DRDY)); IO::out8(IDE0_COMMAND, READ_SECTORS); m_interrupted = false; enable_irq(); wait_for_irq(); if (m_device_error) return false; byte status = IO::in8(0x1f7); ASSERT(status & DRQ); #ifdef DISK_DEBUG kprintf("Retrieving %u bytes (status=%b), outbuf=%p...\n", count * 512, status, outbuf); #endif IO::repeated_in16(IDE0_DATA, outbuf, count * 256); return true; } bool IDEDiskDevice::write_sectors(dword start_sector, word count, const byte* data) { ASSERT(count <= 256); LOCKER(m_lock); #ifdef DISK_DEBUG dbgprintf("%s(%u): IDEDiskDevice::write_sectors request (%u sector(s) @ %u)\n", current->process().name().characters(), current->pid(), count, start_sector); #endif disable_irq(); while (IO::in8(IDE0_STATUS) & BUSY); //dbgprintf("IDEDiskDevice: Writing %u sector(s) @ LBA %u\n", count, start_sector); IO::out8(0x1f2, count == 256 ? 0 : LSB(count)); IO::out8(0x1f3, start_sector & 0xff); IO::out8(0x1f4, (start_sector >> 8) & 0xff); IO::out8(0x1f5, (start_sector >> 16) & 0xff); IO::out8(0x1f6, 0xe0 | ((start_sector >> 24) & 0xf)); // 0xf0 for 2nd device IO::out8(0x3F6, 0x08); IO::out8(IDE0_COMMAND, WRITE_SECTORS); while (!(IO::in8(IDE0_STATUS) & DRQ)); byte status = IO::in8(0x1f7); ASSERT(status & DRQ); IO::repeated_out16(IDE0_DATA, data, count * 256); m_interrupted = false; enable_irq(); wait_for_irq(); disable_irq(); IO::out8(IDE0_COMMAND, FLUSH_CACHE); while (IO::in8(IDE0_STATUS) & BUSY); m_interrupted = false; enable_irq(); wait_for_irq(); return !m_device_error; }