diff --git a/Kernel/Devices/IDEDiskDevice.cpp b/Kernel/Devices/IDEDiskDevice.cpp index a3dbbc309b4..436891da61f 100644 --- a/Kernel/Devices/IDEDiskDevice.cpp +++ b/Kernel/Devices/IDEDiskDevice.cpp @@ -120,6 +120,8 @@ bool IDEDiskDevice::read_block(unsigned index, byte* out) const bool IDEDiskDevice::write_blocks(unsigned index, word count, const byte* data) { + if (m_bus_master_base && m_dma_enabled.resource()) + return write_sectors_with_dma(index, count, data); for (unsigned i = 0; i < count; ++i) { if (!write_sectors(index + i, 1, data + i * 512)) return false; @@ -374,6 +376,79 @@ bool IDEDiskDevice::read_sectors(dword start_sector, word count, byte* outbuf) return true; } +bool IDEDiskDevice::write_sectors_with_dma(dword lba, word count, const byte* inbuf) +{ + LOCKER(m_lock); +#ifdef DISK_DEBUG + dbgprintf("%s(%u): IDEDiskDevice::write_sectors_with_dma (%u x%u) <- %p\n", + current->process().name().characters(), + current->pid(), lba, count, inbuf); +#endif + + disable_irq(); + + m_prdt.offset = m_dma_buffer_page->paddr(); + m_prdt.size = 512 * count; + + memcpy(m_dma_buffer_page->paddr().as_ptr(), inbuf, 512 * count); + + ASSERT(m_prdt.size <= PAGE_SIZE); + + // Stop bus master + IO::out8(m_bus_master_base, 0); + + // Write the PRDT location + IO::out32(m_bus_master_base + 4, (dword)&m_prdt); + + // Turn on "Interrupt" and "Error" flag. The error flag should be cleared by hardware. + IO::out8(m_bus_master_base + 2, IO::in8(m_bus_master_base + 2) | 0x6); + + m_interrupted = false; + enable_irq(); + + while (IO::in8(m_io_base + ATA_REG_STATUS) & ATA_SR_BSY); + + bool is_slave = false; + + IO::out8(m_io_base + ATA_REG_CONTROL, 0); + IO::out8(m_io_base + ATA_REG_HDDEVSEL, 0xe0 | (is_slave << 4)); + wait_400ns(m_io_base); + + IO::out8(m_io_base + ATA_REG_FEATURES, 0); + + IO::out8(m_io_base + ATA_REG_SECCOUNT0, 0); + IO::out8(m_io_base + ATA_REG_LBA0, 0); + IO::out8(m_io_base + ATA_REG_LBA1, 0); + IO::out8(m_io_base + ATA_REG_LBA2, 0); + + IO::out8(m_io_base + ATA_REG_SECCOUNT0, count); + IO::out8(m_io_base + ATA_REG_LBA0, (lba & 0x000000ff) >> 0); + IO::out8(m_io_base + ATA_REG_LBA1, (lba & 0x0000ff00) >> 8); + IO::out8(m_io_base + ATA_REG_LBA2, (lba & 0x00ff0000) >> 16); + + for (;;) { + auto status = IO::in8(m_io_base + ATA_REG_STATUS); + if (!(status & ATA_SR_BSY) && (status & ATA_SR_DRDY)) + break; + } + + IO::out8(m_io_base + ATA_REG_COMMAND, ATA_CMD_WRITE_DMA_EXT); + wait_400ns(m_io_base); + + // Start bus master + IO::out8(m_bus_master_base, 0x1); + + wait_for_irq(); + disable_irq(); + + if (m_device_error) + return false; + + // I read somewhere that this may trigger a cache flush so let's do it. + IO::out8(m_bus_master_base + 2, IO::in8(m_bus_master_base + 2) | 0x6); + return true; +} + bool IDEDiskDevice::write_sectors(dword start_sector, word count, const byte* data) { ASSERT(count <= 256); diff --git a/Kernel/Devices/IDEDiskDevice.h b/Kernel/Devices/IDEDiskDevice.h index 10387842a9a..d87ab9e690d 100644 --- a/Kernel/Devices/IDEDiskDevice.h +++ b/Kernel/Devices/IDEDiskDevice.h @@ -39,9 +39,10 @@ private: void initialize(); bool wait_for_irq(); - bool read_sectors_with_dma(dword sector, word count, byte*); - bool read_sectors(dword start_sector, word count, byte* buffer); - bool write_sectors(dword start_sector, word count, const byte* data); + bool read_sectors_with_dma(dword lba, word count, byte*); + bool write_sectors_with_dma(dword lba, word count, const byte*); + bool read_sectors(dword lba, word count, byte* buffer); + bool write_sectors(dword lba, word count, const byte* data); Lock m_lock { "IDEDiskDevice" }; word m_cylinders { 0 };