diff options
author | Jesse <jesse.buhagiar@student.rmit.edu.au> | 2019-07-28 23:44:01 +1000 |
---|---|---|
committer | Andreas Kling <awesomekling@gmail.com> | 2019-07-28 15:44:01 +0200 |
commit | 59e122f8ba745178f1796406a49bf90d4fcaad8d (patch) | |
tree | 4090124ae2e1d6a7d032e04983db4f61a5d707be | |
parent | cf57d644818d6a217d4cf055d896bc7261e6f972 (diff) | |
download | serenity-59e122f8ba745178f1796406a49bf90d4fcaad8d.zip |
Kernel: Expand PATA driver to support multiple hard drives (#365)
The previous implementation of the PIIX3/4 PATA/IDE channel driver only
supported a single drive, as the object model was wrong (the channel
inherits the IRQ, not the disk drive itself). This fixes it by 'attaching'
two `PATADiskDevices` to a `PATAChannel`, which makes more sense.
The reading/writing code is presented as is, which violates the spec
outlined by Seagate in the linked datasheet. That spec is rather old,
so it might not be 100% up to date, though may cause issues on real
hardware, so until we can actually test it, this will suffice.
-rw-r--r-- | Kernel/Devices/PATAChannel.cpp (renamed from Kernel/Devices/IDEDiskDevice.cpp) | 375 | ||||
-rw-r--r-- | Kernel/Devices/PATAChannel.h | 72 | ||||
-rw-r--r-- | Kernel/Devices/PATADiskDevice.cpp | 164 | ||||
-rw-r--r-- | Kernel/Devices/PATADiskDevice.h (renamed from Kernel/Devices/IDEDiskDevice.h) | 43 | ||||
-rw-r--r-- | Kernel/Makefile | 3 | ||||
-rw-r--r-- | Kernel/init.cpp | 10 |
6 files changed, 435 insertions, 232 deletions
diff --git a/Kernel/Devices/IDEDiskDevice.cpp b/Kernel/Devices/PATAChannel.cpp index f9ce5daa72..8847505636 100644 --- a/Kernel/Devices/IDEDiskDevice.cpp +++ b/Kernel/Devices/PATAChannel.cpp @@ -1,14 +1,15 @@ -#include <Kernel/Devices/IDEDiskDevice.h> +#include "PATADiskDevice.h" +#include <AK/ByteBuffer.h> +#include <Kernel/Devices/PATAChannel.h> #include <Kernel/FileSystem/ProcFS.h> #include <Kernel/IO.h> -#include <Kernel/Arch/i386/PIC.h> #include <Kernel/Process.h> -#include <Kernel/StdLib.h> #include <Kernel/VM/MemoryManager.h> -//#define DISK_DEBUG +#define PATA_PRIMARY_IRQ 14 +#define PATA_SECONDARY_IRQ 15 -#define IRQ_FIXED_DISK 14 +//#define PATA_DEBUG #define ATA_SR_BSY 0x80 #define ATA_SR_DRDY 0x40 @@ -78,61 +79,55 @@ #define ATA_REG_ALTSTATUS 0x0C #define ATA_REG_DEVADDRESS 0x0D -NonnullRefPtr<IDEDiskDevice> IDEDiskDevice::create(DriveType type) +static Lock& s_lock() { - return adopt(*new IDEDiskDevice(type)); -} + static Lock* lock; + if (!lock) + lock = new Lock; -IDEDiskDevice::IDEDiskDevice(DriveType type) - : IRQHandler(IRQ_FIXED_DISK) - , m_io_base(0x1f0) - , m_drive_type(type) -{ - m_dma_enabled.resource() = true; - ProcFS::the().add_sys_bool("ide_dma", m_dma_enabled); - initialize(); -} + return *lock; +}; -IDEDiskDevice::~IDEDiskDevice() +OwnPtr<PATAChannel> PATAChannel::create(ChannelType type) { + return make<PATAChannel>(type); } -const char* IDEDiskDevice::class_name() const +PATAChannel::PATAChannel(ChannelType type) + : IRQHandler((type == ChannelType::Primary ? PATA_PRIMARY_IRQ : PATA_SECONDARY_IRQ)) + , m_channel_number((type == ChannelType::Primary ? 0 : 1)) + , m_io_base((type == ChannelType::Primary ? 0x1F0 : 0x170)) { - return "IDEDiskDevice"; -} + m_dma_enabled.resource() = true; + ProcFS::the().add_sys_bool("ide_dma", m_dma_enabled); -unsigned IDEDiskDevice::block_size() const -{ - return 512; + initialize(); + detect_disks(); } -bool IDEDiskDevice::read_blocks(unsigned index, u16 count, u8* out) +PATAChannel::~PATAChannel() { - if (m_bus_master_base && m_dma_enabled.resource()) - return read_sectors_with_dma(index, count, out); - return read_sectors(index, count, out); } -bool IDEDiskDevice::read_block(unsigned index, u8* out) const +void PATAChannel::initialize() { - return const_cast<IDEDiskDevice*>(this)->read_blocks(index, 1, out); -} + static const PCI::ID piix3_ide_id = { 0x8086, 0x7010 }; + static const PCI::ID piix4_ide_id = { 0x8086, 0x7111 }; + PCI::enumerate_all([this](const PCI::Address& address, PCI::ID id) { + if (id == piix3_ide_id || id == piix4_ide_id) { + m_pci_address = address; + kprintf("PIIX%u PATA Controller found!\n", id == piix3_ide_id ? 3 : 4); + } + }); -bool IDEDiskDevice::write_blocks(unsigned index, u16 count, const u8* 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; + // Let's try to set up DMA transfers. + if (!m_pci_address.is_null()) { + m_prdt.end_of_table = 0x8000; + PCI::enable_bus_mastering(m_pci_address); + m_bus_master_base = PCI::get_BAR4(m_pci_address) & 0xfffc; + m_dma_buffer_page = MM.allocate_supervisor_physical_page(); + dbgprintf("PIIX Bus master IDE: I/O @ %x\n", m_bus_master_base); } - return true; -} - -bool IDEDiskDevice::write_block(unsigned index, const u8* data) -{ - return write_blocks(index, 1, data); } static void print_ide_status(u8 status) @@ -148,125 +143,114 @@ static void print_ide_status(u8 status) (status & ATA_SR_ERR) != 0); } -bool IDEDiskDevice::wait_for_irq() +bool PATAChannel::wait_for_irq() { -#ifdef DISK_DEBUG - kprintf("disk: waiting for interrupt...\n"); +#ifdef PATA_DEBUG + kprintf("PATA: waiting for IRQ %d...\n", irq_number()); #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"); +#ifdef PATA_DEBUG + kprintf("PATA: received IRQ %d!\n", irq_number()); #endif + return true; } -void IDEDiskDevice::handle_irq() +void PATAChannel::handle_irq() { u8 status = IO::in8(m_io_base + ATA_REG_STATUS); if (status & ATA_SR_ERR) { print_ide_status(status); m_device_error = IO::in8(m_io_base + ATA_REG_ERROR); - kprintf("IDEDiskDevice: Error %b!\n", m_device_error); + kprintf("PATADiskDevice: Error %b!\n", m_device_error); } else { m_device_error = 0; } -#ifdef DISK_DEBUG +#ifdef PATA_DEBUG kprintf("disk:interrupt: DRQ=%u BSY=%u DRDY=%u\n", (status & ATA_SR_DRQ) != 0, (status & ATA_SR_BSY) != 0, (status & ATA_SR_DRDY) != 0); #endif m_interrupted = true; } -void IDEDiskDevice::initialize() +static void wait_400ns(u16 io_base) { - static const PCI::ID piix3_ide_id = { 0x8086, 0x7010 }; - static const PCI::ID piix4_ide_id = { 0x8086, 0x7111 }; - PCI::enumerate_all([this](const PCI::Address& address, PCI::ID id) { - if (id == piix3_ide_id || id == piix4_ide_id) { - m_pci_address = address; - kprintf("PIIX%u IDE device found!\n", id == piix3_ide_id ? 3 : 4); - } - }); - -#ifdef DISK_DEBUG - u8 status = IO::in8(m_io_base + ATA_REG_STATUS); - kprintf("initial status: "); - print_ide_status(status); - - if (is_slave()) - kprintf("This IDE device is the SECONDARY device on the channel!\n"); -#endif - - m_interrupted = false; - - while (IO::in8(m_io_base + ATA_REG_STATUS) & ATA_SR_BSY) - ; - - enable_irq(); + for (int i = 0; i < 4; ++i) + IO::in8(io_base + ATA_REG_ALTSTATUS); +} - u8 devsel = 0xA0; - if (is_slave()) - devsel |= 0x10; +void PATAChannel::detect_disks() +{ + // There are only two possible disks connected to a channel + for (auto i = 0; i < 2; i++) { + enable_irq(); - IO::out8(0x1F6, devsel); - IO::out8(0x3F6, devsel); - IO::out8(m_io_base + ATA_REG_COMMAND, ATA_CMD_IDENTIFY); + IO::out8(m_io_base + ATA_REG_HDDEVSEL, 0xA0 | (i << 4)); // First, we need to select the drive itself - enable_irq(); - wait_for_irq(); + // Apparently these need to be 0 before sending IDENTIFY?! + IO::out8(m_io_base + ATA_REG_SECCOUNT0, 0x00); + IO::out8(m_io_base + ATA_REG_LBA0, 0x00); + IO::out8(m_io_base + ATA_REG_LBA1, 0x00); + IO::out8(m_io_base + ATA_REG_LBA2, 0x00); - ByteBuffer wbuf = ByteBuffer::create_uninitialized(512); - ByteBuffer bbuf = ByteBuffer::create_uninitialized(512); - u8* b = bbuf.pointer(); - u16* w = (u16*)wbuf.pointer(); - const u16* wbufbase = (u16*)wbuf.pointer(); - - for (u32 i = 0; i < 256; ++i) { - u16 data = IO::in16(m_io_base + ATA_REG_DATA); - *(w++) = data; - *(b++) = MSB(data); - *(b++) = LSB(data); - } + IO::out8(m_io_base + ATA_REG_COMMAND, ATA_CMD_IDENTIFY); // Send the ATA_IDENTIFY command - // "Unpad" the device name string. - for (u32 i = 93; i > 54 && bbuf[i] == ' '; --i) - bbuf[i] = 0; + // Wait for the BSY flag to be reset + while (IO::in8(m_io_base + ATA_REG_STATUS) & ATA_SR_BSY) + ; - m_cylinders = wbufbase[1]; - m_heads = wbufbase[3]; - m_sectors_per_track = wbufbase[6]; + if (IO::in8(m_io_base + ATA_REG_STATUS) == 0x00) { +#ifdef PATA_DEBUG + kprintf("PATA: No %s disk detected!\n", (i == 0 ? "master" : "slave")); +#endif + continue; + } - kprintf( - "IDEDiskDevice: Master=\"%s\", C/H/Spt=%u/%u/%u\n", - bbuf.pointer() + 54, - m_cylinders, - m_heads, - m_sectors_per_track); + ByteBuffer wbuf = ByteBuffer::create_uninitialized(512); + ByteBuffer bbuf = ByteBuffer::create_uninitialized(512); + u8* b = bbuf.pointer(); + u16* w = (u16*)wbuf.pointer(); + const u16* wbufbase = (u16*)wbuf.pointer(); + + for (u32 i = 0; i < 256; ++i) { + u16 data = IO::in16(m_io_base + ATA_REG_DATA); + *(w++) = data; + *(b++) = MSB(data); + *(b++) = LSB(data); + } - // Let's try to set up DMA transfers. - if (!m_pci_address.is_null()) { - m_prdt.end_of_table = 0x8000; - PCI::enable_bus_mastering(m_pci_address); - m_bus_master_base = PCI::get_BAR4(m_pci_address) & 0xfffc; - m_dma_buffer_page = MM.allocate_supervisor_physical_page(); - dbgprintf("PIIX Bus master IDE: I/O @ %x\n", m_bus_master_base); + // "Unpad" the device name string. + for (u32 i = 93; i > 54 && bbuf[i] == ' '; --i) + bbuf[i] = 0; + + u8 cyls = wbufbase[1]; + u8 heads = wbufbase[3]; + u8 spt = wbufbase[6]; + + kprintf( + "PATADiskDevice: Name=\"%s\", C/H/Spt=%u/%u/%u\n", + bbuf.pointer() + 54, + cyls, + heads, + spt); + + if (i == 0) { + m_master = PATADiskDevice::create(*this, PATADiskDevice::DriveType::Master); + m_master->set_drive_geometry(cyls, heads, spt); + } else { + m_slave = PATADiskDevice::create(*this, PATADiskDevice::DriveType::Slave); + m_slave->set_drive_geometry(cyls, heads, spt); + } } } -static void wait_400ns(u16 io_base) +bool PATAChannel::ata_read_sectors_with_dma(u32 lba, u16 count, u8* outbuf, bool slave_request) { - for (int i = 0; i < 4; ++i) - IO::in8(io_base + ATA_REG_ALTSTATUS); -} - -bool IDEDiskDevice::read_sectors_with_dma(u32 lba, u16 count, u8* outbuf) -{ - LOCKER(m_lock); -#ifdef DISK_DEBUG - dbgprintf("%s(%u): IDEDiskDevice::read_sectors_with_dma (%u x%u) -> %p\n", + LOCKER(s_lock()); +#ifdef PATA_DEBUG + dbgprintf("%s(%u): PATADiskDevice::read_sectors_with_dma (%u x%u) -> %p\n", current->process().name().characters(), current->pid(), lba, count, outbuf); #endif @@ -297,11 +281,11 @@ bool IDEDiskDevice::read_sectors_with_dma(u32 lba, u16 count, u8* outbuf) ; u8 devsel = 0xe0; - if (is_slave()) + if (slave_request) devsel |= 0x10; IO::out8(m_io_base + ATA_REG_CONTROL, 0); - IO::out8(m_io_base + ATA_REG_HDDEVSEL, devsel | (is_slave() << 4)); + IO::out8(m_io_base + ATA_REG_HDDEVSEL, devsel | (static_cast<u8>(slave_request) << 4)); wait_400ns(m_io_base); IO::out8(m_io_base + ATA_REG_FEATURES, 0); @@ -341,62 +325,11 @@ bool IDEDiskDevice::read_sectors_with_dma(u32 lba, u16 count, u8* outbuf) return true; } -bool IDEDiskDevice::read_sectors(u32 start_sector, u16 count, u8* outbuf) +bool PATAChannel::ata_write_sectors_with_dma(u32 lba, u16 count, const u8* inbuf, bool slave_request) { - 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(m_io_base + ATA_REG_STATUS) & ATA_SR_BSY) - ; - -#ifdef DISK_DEBUG - kprintf("IDEDiskDevice: Reading %u sector(s) @ LBA %u\n", count, start_sector); -#endif - - u8 devsel = 0xe0; - if (is_slave()) - devsel |= 0x10; - - IO::out8(m_io_base + ATA_REG_SECCOUNT0, count == 256 ? 0 : LSB(count)); - IO::out8(m_io_base + ATA_REG_LBA0, start_sector & 0xff); - IO::out8(m_io_base + ATA_REG_LBA1, (start_sector >> 8) & 0xff); - IO::out8(m_io_base + ATA_REG_LBA2, (start_sector >> 16) & 0xff); - IO::out8(m_io_base + ATA_REG_HDDEVSEL, devsel | ((start_sector >> 24) & 0xf)); - - IO::out8(0x3F6, 0x08); - while (!(IO::in8(m_io_base + ATA_REG_STATUS) & ATA_SR_DRDY)) - ; - - IO::out8(m_io_base + ATA_REG_COMMAND, ATA_CMD_READ_PIO); - m_interrupted = false; - enable_irq(); - wait_for_irq(); - - if (m_device_error) - return false; - - u8 status = IO::in8(m_io_base + ATA_REG_STATUS); - ASSERT(status & ATA_SR_DRQ); -#ifdef DISK_DEBUG - kprintf("Retrieving %u bytes (status=%b), outbuf=%p...\n", count * 512, status, outbuf); -#endif - - IO::repeated_in16(m_io_base + ATA_REG_DATA, outbuf, count * 256); - return true; -} - -bool IDEDiskDevice::write_sectors_with_dma(u32 lba, u16 count, const u8* inbuf) -{ - LOCKER(m_lock); -#ifdef DISK_DEBUG - dbgprintf("%s(%u): IDEDiskDevice::write_sectors_with_dma (%u x%u) <- %p\n", + LOCKER(s_lock()); +#ifdef PATA_DEBUG + dbgprintf("%s(%u): PATADiskDevice::write_sectors_with_dma (%u x%u) <- %p\n", current->process().name().characters(), current->pid(), lba, count, inbuf); #endif @@ -426,11 +359,11 @@ bool IDEDiskDevice::write_sectors_with_dma(u32 lba, u16 count, const u8* inbuf) ; u8 devsel = 0xe0; - if (is_slave()) + if (slave_request) devsel |= 0x10; IO::out8(m_io_base + ATA_REG_CONTROL, 0); - IO::out8(m_io_base + ATA_REG_HDDEVSEL, devsel | (is_slave() << 4)); + IO::out8(m_io_base + ATA_REG_HDDEVSEL, devsel | (static_cast<u8>(slave_request) << 4)); wait_400ns(m_io_base); IO::out8(m_io_base + ATA_REG_FEATURES, 0); @@ -468,12 +401,63 @@ bool IDEDiskDevice::write_sectors_with_dma(u32 lba, u16 count, const u8* inbuf) return true; } -bool IDEDiskDevice::write_sectors(u32 start_sector, u16 count, const u8* data) +bool PATAChannel::ata_read_sectors(u32 start_sector, u16 count, u8* outbuf, bool slave_request) { ASSERT(count <= 256); - LOCKER(m_lock); -#ifdef DISK_DEBUG - dbgprintf("%s(%u): IDEDiskDevice::write_sectors request (%u sector(s) @ %u)\n", + LOCKER(s_lock()); +#ifdef PATA_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(m_io_base + ATA_REG_STATUS) & ATA_SR_BSY) + ; + +#ifdef PATA_DEBUG + kprintf("PATADiskDevice: Reading %u sector(s) @ LBA %u\n", count, start_sector); +#endif + + u8 devsel = 0xe0; + if (slave_request) + devsel |= 0x10; + + IO::out8(m_io_base + ATA_REG_SECCOUNT0, count == 256 ? 0 : LSB(count)); + IO::out8(m_io_base + ATA_REG_LBA0, start_sector & 0xff); + IO::out8(m_io_base + ATA_REG_LBA1, (start_sector >> 8) & 0xff); + IO::out8(m_io_base + ATA_REG_LBA2, (start_sector >> 16) & 0xff); + IO::out8(m_io_base + ATA_REG_HDDEVSEL, devsel | ((start_sector >> 24) & 0xf)); + + IO::out8(0x3F6, 0x08); + while (!(IO::in8(m_io_base + ATA_REG_STATUS) & ATA_SR_DRDY)) + ; + + IO::out8(m_io_base + ATA_REG_COMMAND, ATA_CMD_READ_PIO); + m_interrupted = false; + enable_irq(); + wait_for_irq(); + + if (m_device_error) + return false; + + u8 status = IO::in8(m_io_base + ATA_REG_STATUS); + ASSERT(status & ATA_SR_DRQ); +#ifdef PATA_DEBUG + kprintf("Retrieving %u bytes (status=%b), outbuf=%p...\n", count * 512, status, outbuf); +#endif + + IO::repeated_in16(m_io_base + ATA_REG_DATA, outbuf, count * 256); + return true; +} + +bool PATAChannel::ata_write_sectors(u32 start_sector, u16 count, const u8* inbuf, bool slave_request) +{ + ASSERT(count <= 256); + LOCKER(s_lock()); +#ifdef PATA_DEBUG + dbgprintf("%s(%u): PATADiskDevice::write_sectors request (%u sector(s) @ %u)\n", current->process().name().characters(), current->pid(), count, @@ -484,10 +468,10 @@ bool IDEDiskDevice::write_sectors(u32 start_sector, u16 count, const u8* data) while (IO::in8(m_io_base + ATA_REG_STATUS) & ATA_SR_BSY) ; - //dbgprintf("IDEDiskDevice: Writing %u sector(s) @ LBA %u\n", count, start_sector); + //dbgprintf("PATADiskDevice: Writing %u sector(s) @ LBA %u\n", count, start_sector); u8 devsel = 0xe0; - if (is_slave()) + if (slave_request) devsel |= 0x10; IO::out8(m_io_base + ATA_REG_SECCOUNT0, count == 256 ? 0 : LSB(count)); @@ -505,7 +489,7 @@ bool IDEDiskDevice::write_sectors(u32 start_sector, u16 count, const u8* data) u8 status = IO::in8(m_io_base + ATA_REG_STATUS); ASSERT(status & ATA_SR_DRQ); - IO::repeated_out16(m_io_base + ATA_REG_DATA, data, count * 256); + IO::repeated_out16(m_io_base + ATA_REG_DATA, inbuf, count * 256); m_interrupted = false; enable_irq(); @@ -521,8 +505,3 @@ bool IDEDiskDevice::write_sectors(u32 start_sector, u16 count, const u8* data) return !m_device_error; } - -bool IDEDiskDevice::is_slave() const -{ - return m_drive_type == DriveType::SLAVE; -} diff --git a/Kernel/Devices/PATAChannel.h b/Kernel/Devices/PATAChannel.h new file mode 100644 index 0000000000..8bf28adf1a --- /dev/null +++ b/Kernel/Devices/PATAChannel.h @@ -0,0 +1,72 @@ +// +// Parallel ATA (PATA) controller driver +// +// This driver describes a logical PATA Channel. Each channel can connect up to 2 +// IDE Hard Disk Drives. The drives themselves can be either the master drive (hd0) +// or the slave drive (hd1). +// +// More information about the ATA spec for PATA can be found here: +// ftp://ftp.seagate.com/acrobat/reference/111-1c.pdf +// +#pragma once + +#include <AK/OwnPtr.h> +#include <AK/RefPtr.h> +#include <Kernel/IRQHandler.h> +#include <Kernel/Lock.h> +#include <Kernel/PCI.h> +#include <Kernel/VM/PhysicalAddress.h> +#include <Kernel/VM/PhysicalPage.h> + +struct PhysicalRegionDescriptor { + PhysicalAddress offset; + u16 size { 0 }; + u16 end_of_table { 0 }; +}; + +class PATADiskDevice; +class PATAChannel final : public IRQHandler { + friend class PATADiskDevice; + AK_MAKE_ETERNAL +public: + enum class ChannelType : u8 { + Primary, + Secondary + }; + +public: + static OwnPtr<PATAChannel> create(ChannelType type); + explicit PATAChannel(ChannelType); + virtual ~PATAChannel() override; + + RefPtr<PATADiskDevice> master_device() { return m_master; }; + RefPtr<PATADiskDevice> slave_device() { return m_slave; }; + +private: + //^ IRQHandler + virtual void handle_irq() override; + + void initialize(); + void detect_disks(); + + bool wait_for_irq(); + bool ata_read_sectors_with_dma(u32, u16, u8*, bool); + bool ata_write_sectors_with_dma(u32, u16, const u8*, bool); + bool ata_read_sectors(u32, u16, u8*, bool); + bool ata_write_sectors(u32, u16, const u8*, bool); + + // Data members + u8 m_channel_number { 0 }; // Channel number. 0 = master, 1 = slave + u16 m_io_base { 0x1F0 }; + volatile u8 m_device_error { 0 }; + volatile bool m_interrupted { false }; + + PCI::Address m_pci_address; + PhysicalRegionDescriptor m_prdt; + RefPtr<PhysicalPage> m_dma_buffer_page; + u16 m_bus_master_base { 0 }; + Lockable<bool> m_dma_enabled; + + RefPtr<PATADiskDevice> m_master; + RefPtr<PATADiskDevice> m_slave; +}; diff --git a/Kernel/Devices/PATADiskDevice.cpp b/Kernel/Devices/PATADiskDevice.cpp new file mode 100644 index 0000000000..69b50e08c2 --- /dev/null +++ b/Kernel/Devices/PATADiskDevice.cpp @@ -0,0 +1,164 @@ +#include "PATADiskDevice.h" +#include <Kernel/Arch/i386/PIC.h> +#include <Kernel/FileSystem/ProcFS.h> +#include <Kernel/IO.h> +#include <Kernel/Process.h> +#include <Kernel/StdLib.h> +#include <Kernel/VM/MemoryManager.h> + +//#define DISK_DEBUG + +#define IRQ_FIXED_DISK 14 + +#define ATA_SR_BSY 0x80 +#define ATA_SR_DRDY 0x40 +#define ATA_SR_DF 0x20 +#define ATA_SR_DSC 0x10 +#define ATA_SR_DRQ 0x08 +#define ATA_SR_CORR 0x04 +#define ATA_SR_IDX 0x02 +#define ATA_SR_ERR 0x01 + +#define ATA_ER_BBK 0x80 +#define ATA_ER_UNC 0x40 +#define ATA_ER_MC 0x20 +#define ATA_ER_IDNF 0x10 +#define ATA_ER_MCR 0x08 +#define ATA_ER_ABRT 0x04 +#define ATA_ER_TK0NF 0x02 +#define ATA_ER_AMNF 0x01 + +#define ATA_CMD_READ_PIO 0x20 +#define ATA_CMD_READ_PIO_EXT 0x24 +#define ATA_CMD_READ_DMA 0xC8 +#define ATA_CMD_READ_DMA_EXT 0x25 +#define ATA_CMD_WRITE_PIO 0x30 +#define ATA_CMD_WRITE_PIO_EXT 0x34 +#define ATA_CMD_WRITE_DMA 0xCA +#define ATA_CMD_WRITE_DMA_EXT 0x35 +#define ATA_CMD_CACHE_FLUSH 0xE7 +#define ATA_CMD_CACHE_FLUSH_EXT 0xEA +#define ATA_CMD_PACKET 0xA0 +#define ATA_CMD_IDENTIFY_PACKET 0xA1 +#define ATA_CMD_IDENTIFY 0xEC + +#define ATAPI_CMD_READ 0xA8 +#define ATAPI_CMD_EJECT 0x1B + +#define ATA_IDENT_DEVICETYPE 0 +#define ATA_IDENT_CYLINDERS 2 +#define ATA_IDENT_HEADS 6 +#define ATA_IDENT_SECTORS 12 +#define ATA_IDENT_SERIAL 20 +#define ATA_IDENT_MODEL 54 +#define ATA_IDENT_CAPABILITIES 98 +#define ATA_IDENT_FIELDVALID 106 +#define ATA_IDENT_MAX_LBA 120 +#define ATA_IDENT_COMMANDSETS 164 +#define ATA_IDENT_MAX_LBA_EXT 200 + +#define IDE_ATA 0x00 +#define IDE_ATAPI 0x01 + +#define ATA_REG_DATA 0x00 +#define ATA_REG_ERROR 0x01 +#define ATA_REG_FEATURES 0x01 +#define ATA_REG_SECCOUNT0 0x02 +#define ATA_REG_LBA0 0x03 +#define ATA_REG_LBA1 0x04 +#define ATA_REG_LBA2 0x05 +#define ATA_REG_HDDEVSEL 0x06 +#define ATA_REG_COMMAND 0x07 +#define ATA_REG_STATUS 0x07 +#define ATA_REG_SECCOUNT1 0x08 +#define ATA_REG_LBA3 0x09 +#define ATA_REG_LBA4 0x0A +#define ATA_REG_LBA5 0x0B +#define ATA_REG_CONTROL 0x0C +#define ATA_REG_ALTSTATUS 0x0C +#define ATA_REG_DEVADDRESS 0x0D + +NonnullRefPtr<PATADiskDevice> PATADiskDevice::create(PATAChannel& channel, DriveType type) +{ + return adopt(*new PATADiskDevice(channel, type)); +} + +PATADiskDevice::PATADiskDevice(PATAChannel& channel, DriveType type) + : m_drive_type(type) + , m_channel(channel) +{ +} + +PATADiskDevice::~PATADiskDevice() +{ +} + +const char* PATADiskDevice::class_name() const +{ + return "PATADiskDevice"; +} + +unsigned PATADiskDevice::block_size() const +{ + return 512; +} + +bool PATADiskDevice::read_blocks(unsigned index, u16 count, u8* out) +{ + if (m_channel.m_bus_master_base && m_channel.m_dma_enabled.resource()) + return read_sectors_with_dma(index, count, out); + return read_sectors(index, count, out); +} + +bool PATADiskDevice::read_block(unsigned index, u8* out) const +{ + return const_cast<PATADiskDevice*>(this)->read_blocks(index, 1, out); +} + +bool PATADiskDevice::write_blocks(unsigned index, u16 count, const u8* data) +{ + if (m_channel.m_bus_master_base && m_channel.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; + } + return true; +} + +bool PATADiskDevice::write_block(unsigned index, const u8* data) +{ + return write_blocks(index, 1, data); +} + +void PATADiskDevice::set_drive_geometry(u16 cyls, u16 heads, u16 spt) +{ + m_cylinders = cyls; + m_heads = heads; + m_sectors_per_track = spt; +} + +bool PATADiskDevice::read_sectors_with_dma(u32 lba, u16 count, u8* outbuf) +{ + return m_channel.ata_read_sectors_with_dma(lba, count, outbuf, is_slave()); +} + +bool PATADiskDevice::read_sectors(u32 start_sector, u16 count, u8* outbuf) +{ + return m_channel.ata_read_sectors(start_sector, count, outbuf, is_slave()); +} + +bool PATADiskDevice::write_sectors_with_dma(u32 lba, u16 count, const u8* inbuf) +{ + return m_channel.ata_write_sectors_with_dma(lba, count, inbuf, is_slave()); +} + +bool PATADiskDevice::write_sectors(u32 start_sector, u16 count, const u8* inbuf) +{ + return m_channel.ata_write_sectors(start_sector, count, inbuf, is_slave()); +} + +bool PATADiskDevice::is_slave() const +{ + return m_drive_type == DriveType::Slave; +} diff --git a/Kernel/Devices/IDEDiskDevice.h b/Kernel/Devices/PATADiskDevice.h index 5c55cee5bc..3d3c4928d4 100644 --- a/Kernel/Devices/IDEDiskDevice.h +++ b/Kernel/Devices/PATADiskDevice.h @@ -1,36 +1,33 @@ +// +// A Disk Device Connected to a PATA Channel +// +// #pragma once #include <AK/RefPtr.h> #include <Kernel/Devices/DiskDevice.h> +#include <Kernel/Devices/PATAChannel.h> #include <Kernel/IRQHandler.h> #include <Kernel/Lock.h> #include <Kernel/PCI.h> #include <Kernel/VM/PhysicalAddress.h> #include <Kernel/VM/PhysicalPage.h> -struct PhysicalRegionDescriptor { - PhysicalAddress offset; - u16 size { 0 }; - u16 end_of_table { 0 }; -}; - -class IDEDiskDevice final : public IRQHandler - , public DiskDevice { +class PATADiskDevice final : public DiskDevice { AK_MAKE_ETERNAL public: - // Type of drive this IDEDiskDevice is on the ATA channel. // // Each PATA channel can contain only two devices, which (I think) are // jumper selectable on the drive itself by shorting two pins. enum class DriveType : u8 { - MASTER, - SLAVE + Master, + Slave }; public: - static NonnullRefPtr<IDEDiskDevice> create(DriveType type); - virtual ~IDEDiskDevice() override; + static NonnullRefPtr<PATADiskDevice> create(PATAChannel&, DriveType); + virtual ~PATADiskDevice() override; // ^DiskDevice virtual unsigned block_size() const override; @@ -39,37 +36,27 @@ public: virtual bool read_blocks(unsigned index, u16 count, u8*) override; virtual bool write_blocks(unsigned index, u16 count, const u8*) override; + void set_drive_geometry(u16, u16, u16); + protected: - explicit IDEDiskDevice(DriveType); + explicit PATADiskDevice(PATAChannel&, DriveType); private: - // ^IRQHandler - virtual void handle_irq() override; - // ^DiskDevice virtual const char* class_name() const override; - void initialize(); bool wait_for_irq(); bool read_sectors_with_dma(u32 lba, u16 count, u8*); bool write_sectors_with_dma(u32 lba, u16 count, const u8*); bool read_sectors(u32 lba, u16 count, u8* buffer); bool write_sectors(u32 lba, u16 count, const u8* data); - bool is_slave() const; Lock m_lock { "IDEDiskDevice" }; u16 m_cylinders { 0 }; u16 m_heads { 0 }; u16 m_sectors_per_track { 0 }; - u16 m_io_base { 0 }; - volatile bool m_interrupted { false }; - volatile u8 m_device_error { 0 }; + DriveType m_drive_type { DriveType::Master }; - DriveType m_drive_type { DriveType::MASTER }; - PCI::Address m_pci_address; - PhysicalRegionDescriptor m_prdt; - RefPtr<PhysicalPage> m_dma_buffer_page; - u16 m_bus_master_base { 0 }; - Lockable<bool> m_dma_enabled; + PATAChannel& m_channel; }; diff --git a/Kernel/Makefile b/Kernel/Makefile index c302147db7..65325a1ad0 100644 --- a/Kernel/Makefile +++ b/Kernel/Makefile @@ -13,7 +13,8 @@ KERNEL_OBJS = \ CMOS.o \ Arch/i386/PIC.o \ Syscall.o \ - Devices/IDEDiskDevice.o \ + Devices/PATAChannel.o \ + Devices/PATADiskDevice.o \ Devices/FloppyDiskDevice.o \ VM/MemoryManager.o \ VM/Region.o \ diff --git a/Kernel/init.cpp b/Kernel/init.cpp index 1b7c243311..2b22bfe29f 100644 --- a/Kernel/init.cpp +++ b/Kernel/init.cpp @@ -1,3 +1,4 @@ +#include "Devices/PATADiskDevice.h" #include "KSyms.h" #include "Process.h" #include "RTC.h" @@ -13,13 +14,13 @@ #include <Kernel/Devices/DiskPartition.h> #include <Kernel/Devices/FloppyDiskDevice.h> #include <Kernel/Devices/FullDevice.h> -#include <Kernel/Devices/IDEDiskDevice.h> #include <Kernel/Devices/KeyboardDevice.h> #include <Kernel/Devices/MBRPartitionTable.h> #include <Kernel/Devices/NullDevice.h> +#include <Kernel/Devices/PATAChannel.h> #include <Kernel/Devices/PS2MouseDevice.h> -#include <Kernel/Devices/SB16.h> #include <Kernel/Devices/RandomDevice.h> +#include <Kernel/Devices/SB16.h> #include <Kernel/Devices/SerialDevice.h> #include <Kernel/Devices/ZeroDevice.h> #include <Kernel/FileSystem/DevPtsFS.h> @@ -68,9 +69,8 @@ VFS* vfs; hang(); } - auto dev_hd0 = IDEDiskDevice::create(IDEDiskDevice::DriveType::MASTER); - - NonnullRefPtr<DiskDevice> root_dev = dev_hd0; + auto pata0 = PATAChannel::create(PATAChannel::ChannelType::Primary); + NonnullRefPtr<DiskDevice> root_dev = *pata0->master_device(); root = root.substring(strlen("/dev/hda"), root.length() - strlen("/dev/hda")); |