Quellcode durchsuchen

Kernel: Introduce the new Storage subsystem

This new subsystem is somewhat replacing the IDE disk code we had with a
new flexible design.

StorageDevice is a generic class that represent a generic storage
device. It is meant that specific storage hardware will override the
interface. StorageController is a generic class that represent
a storage controller that can be found in a machine.

The IDEController class governs two IDEChannels. An IDEChannel is
responsible to manage the master & slave devices of the channel,
therefore an IDEChannel is an IRQHandler.
Liav A vor 4 Jahren
Ursprung
Commit
0a2b00a1bf

+ 1 - 1
CMakeLists.txt

@@ -68,7 +68,7 @@ if (ALL_THE_DEBUG_MACROS)
     set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DOBJECT_DEBUG -DOCCLUSIONS_DEBUG -DOFFD_DEBUG")
     set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DPAGE_FAULT_DEBUG -DPARSER_DEBUG -DPATA_DEBUG -DPATA_DEVICE_DEBUG -DPATH_DEBUG -DPCI_DEBUG -DPNG_DEBUG -DPPM_DEBUG -DPROCESS_DEBUG -DPROCFS_DEBUG -DPS2MOUSE_DEBUG -DPTHREAD_DEBUG -DPTMX_DEBUG")
     set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DREACHABLE_DEBUG -DREGEX_DEBUG -DRESIZE_DEBUG -DRESOURCE_DEBUG -DROUTING_DEBUG -DRTL8139_DEBUG")
-    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSAFE_SYSCALL_DEBUG -DSB16_DEBUG -DSCHEDULER_DEBUG -DSCHEDULER_RUNNABLE_DEBUG -DSELECTION_DEBUG -DSERVICE_DEBUG -DSHARED_BUFFER_DEBUG -DSH_DEBUG -DSIGNAL_DEBUG -DSLAVEPTY_DEBUG -DSMP_DEBUG -DSOCKET_DEBUG -DSYSTEM_MENU_DEBUG -DSYSTEMSERVER_DEBUG")
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSAFE_SYSCALL_DEBUG -DSB16_DEBUG -DSCHEDULER_DEBUG -DSCHEDULER_RUNNABLE_DEBUG -DSELECTION_DEBUG -DSERVICE_DEBUG -DSHARED_BUFFER_DEBUG -DSH_DEBUG -DSIGNAL_DEBUG -DSLAVEPTY_DEBUG -DSMP_DEBUG -DSOCKET_DEBUG -DSYSTEM_MENU_DEBUG -DSYSTEMSERVER_DEBUG -DSTORAGE_DEVICE_DEBUG")
     set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DTCP_DEBUG -DTCP_SOCKET_DEBUG -DTERMCAP_DEBUG -DTERMINAL_DEBUG -DTHREAD_DEBUG -DTLS_DEBUG -DTTY_DEBUG")
     set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DUCI_DEBUG -DUDP_DEBUG -DUPDATE_COALESCING_DEBUG")
     set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DVERY_DEBUG -DVFS_DEBUG -DVMWAREBACKDOOR_DEBUG -DVRA_DEBUG")

+ 4 - 2
Kernel/CMakeLists.txt

@@ -31,8 +31,6 @@ set(KERNEL_SOURCES
     Devices/MBRPartitionTable.cpp
     Devices/MBVGADevice.cpp
     Devices/NullDevice.cpp
-    Devices/PATAChannel.cpp
-    Devices/PATADiskDevice.cpp
     Devices/PCSpeaker.cpp
     Devices/PS2MouseDevice.cpp
     Devices/RandomDevice.cpp
@@ -41,6 +39,10 @@ set(KERNEL_SOURCES
     Devices/UHCIController.cpp
     Devices/VMWareBackdoor.cpp
     Devices/ZeroDevice.cpp
+    Storage/StorageDevice.cpp
+    Storage/IDEController.cpp
+    Storage/IDEChannel.cpp
+    Storage/PATADiskDevice.cpp
     DoubleBuffer.cpp
     FileSystem/BlockBasedFileSystem.cpp
     FileSystem/Custody.cpp

+ 134 - 133
Kernel/Devices/PATAChannel.cpp → Kernel/Storage/IDEChannel.cpp

@@ -27,11 +27,12 @@
 #include <AK/ByteBuffer.h>
 #include <AK/Singleton.h>
 #include <AK/StringView.h>
-#include <Kernel/Devices/PATAChannel.h>
-#include <Kernel/Devices/PATADiskDevice.h>
 #include <Kernel/FileSystem/ProcFS.h>
 #include <Kernel/IO.h>
 #include <Kernel/Process.h>
+#include <Kernel/Storage/IDEChannel.h>
+#include <Kernel/Storage/IDEController.h>
+#include <Kernel/Storage/PATADiskDevice.h>
 #include <Kernel/VM/MemoryManager.h>
 
 namespace Kernel {
@@ -108,24 +109,25 @@ namespace Kernel {
 #define PCI_Mass_Storage_Class 0x1
 #define PCI_IDE_Controller_Subclass 0x1
 
-OwnPtr<PATAChannel> PATAChannel::create(ChannelType type, bool force_pio)
+NonnullOwnPtr<IDEChannel> IDEChannel::create(const IDEController& controller, IOAddressGroup io_group, ChannelType type, bool force_pio)
 {
-    PCI::Address pci_address;
-    PCI::enumerate([&](const PCI::Address& address, PCI::ID id) {
-        if (PCI::get_class(address) == PCI_Mass_Storage_Class && PCI::get_subclass(address) == PCI_IDE_Controller_Subclass) {
-            pci_address = address;
-            klog() << "PATAChannel: PATA Controller found, ID " << id;
-        }
-    });
-    return make<PATAChannel>(pci_address, type, force_pio);
+    return make<IDEChannel>(controller, io_group, type, force_pio);
 }
 
-PATAChannel::PATAChannel(PCI::Address address, ChannelType type, bool force_pio)
-    : PCI::Device(address, (type == ChannelType::Primary ? PATA_PRIMARY_IRQ : PATA_SECONDARY_IRQ))
+RefPtr<StorageDevice> IDEChannel::master_device()
+{
+    return m_master;
+}
+RefPtr<StorageDevice> IDEChannel::slave_device()
+{
+    return m_slave;
+}
+
+IDEChannel::IDEChannel(const IDEController& controller, IOAddressGroup io_group, ChannelType type, bool force_pio)
+    : 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))
-    , m_control_base((type == ChannelType::Primary ? 0x3f6 : 0x376))
-    , m_bus_master_base(PCI::get_BAR4(pci_address()) & 0xfffc)
+    , m_io_group(io_group)
+    , m_parent_controller(controller)
 {
     disable_irq();
 
@@ -137,15 +139,15 @@ PATAChannel::PATAChannel(PCI::Address address, ChannelType type, bool force_pio)
     enable_irq();
 }
 
-PATAChannel::~PATAChannel()
+IDEChannel::~IDEChannel()
 {
 }
 
-void PATAChannel::start_request(AsyncBlockDeviceRequest& request, bool use_dma, bool is_slave)
+void IDEChannel::start_request(AsyncBlockDeviceRequest& request, bool use_dma, bool is_slave)
 {
     ScopedSpinLock lock(m_request_lock);
 #ifdef PATA_DEBUG
-    dbg() << "PATAChannel::start_request";
+    dbg() << "IDEChannel::start_request";
 #endif
     m_current_request = &request;
     m_current_request_block_index = 0;
@@ -165,7 +167,7 @@ void PATAChannel::start_request(AsyncBlockDeviceRequest& request, bool use_dma,
     }
 }
 
-void PATAChannel::complete_current_request(AsyncDeviceRequest::RequestResult result)
+void IDEChannel::complete_current_request(AsyncDeviceRequest::RequestResult result)
 {
     // NOTE: this may be called from the interrupt handler!
     ASSERT(m_current_request);
@@ -177,7 +179,7 @@ void PATAChannel::complete_current_request(AsyncDeviceRequest::RequestResult res
     // before Processor::deferred_call_queue returns!
     Processor::deferred_call_queue([this, result]() {
 #ifdef PATA_DEBUG
-        dbg() << "PATAChannel::complete_current_request result: " << result;
+        dbg() << "IDEChannel::complete_current_request result: " << result;
 #endif
         ASSERT(m_current_request);
         auto& request = *m_current_request;
@@ -193,7 +195,7 @@ void PATAChannel::complete_current_request(AsyncDeviceRequest::RequestResult res
                 }
 
                 // I read somewhere that this may trigger a cache flush so let's do it.
-                m_bus_master_base.offset(2).out<u8>(m_bus_master_base.offset(2).in<u8>() | 0x6);
+                m_io_group.bus_master_base().offset(2).out<u8>(m_io_group.bus_master_base().offset(2).in<u8>() | 0x6);
             }
         }
 
@@ -201,50 +203,50 @@ void PATAChannel::complete_current_request(AsyncDeviceRequest::RequestResult res
     });
 }
 
-void PATAChannel::initialize(bool force_pio)
+void IDEChannel::initialize(bool force_pio)
 {
-    PCI::enable_interrupt_line(pci_address());
+    m_parent_controller->enable_pin_based_interrupts();
     if (force_pio) {
-        klog() << "PATAChannel: Requested to force PIO mode; not setting up DMA";
+        klog() << "IDEChannel: Requested to force PIO mode; not setting up DMA";
         return;
     }
 
     // Let's try to set up DMA transfers.
-    PCI::enable_bus_mastering(pci_address());
+    PCI::enable_bus_mastering(m_parent_controller->pci_address());
     m_prdt_page = MM.allocate_supervisor_physical_page();
     prdt().end_of_table = 0x8000;
     m_dma_buffer_page = MM.allocate_supervisor_physical_page();
-    klog() << "PATAChannel: Bus master IDE: " << m_bus_master_base;
+    klog() << "IDEChannel: Bus master IDE: " << m_io_group.bus_master_base();
 }
 
 static void print_ide_status(u8 status)
 {
-    klog() << "PATAChannel: print_ide_status: DRQ=" << ((status & ATA_SR_DRQ) != 0) << " BSY=" << ((status & ATA_SR_BSY) != 0) << " DRDY=" << ((status & ATA_SR_DRDY) != 0) << " DSC=" << ((status & ATA_SR_DSC) != 0) << " DF=" << ((status & ATA_SR_DF) != 0) << " CORR=" << ((status & ATA_SR_CORR) != 0) << " IDX=" << ((status & ATA_SR_IDX) != 0) << " ERR=" << ((status & ATA_SR_ERR) != 0);
+    klog() << "IDEChannel: print_ide_status: DRQ=" << ((status & ATA_SR_DRQ) != 0) << " BSY=" << ((status & ATA_SR_BSY) != 0) << " DRDY=" << ((status & ATA_SR_DRDY) != 0) << " DSC=" << ((status & ATA_SR_DSC) != 0) << " DF=" << ((status & ATA_SR_DF) != 0) << " CORR=" << ((status & ATA_SR_CORR) != 0) << " IDX=" << ((status & ATA_SR_IDX) != 0) << " ERR=" << ((status & ATA_SR_ERR) != 0);
 }
 
-void PATAChannel::handle_irq(const RegisterState&)
+void IDEChannel::handle_irq(const RegisterState&)
 {
-    u8 status = m_io_base.offset(ATA_REG_STATUS).in<u8>();
+    u8 status = m_io_group.io_base().offset(ATA_REG_STATUS).in<u8>();
 
     m_entropy_source.add_random_event(status);
 
-    u8 bstatus = m_bus_master_base.offset(2).in<u8>();
+    u8 bstatus = m_io_group.bus_master_base().offset(2).in<u8>();
     if (!(bstatus & 0x4)) {
         // interrupt not from this device, ignore
 #ifdef PATA_DEBUG
-        klog() << "PATAChannel: ignore interrupt";
+        klog() << "IDEChannel: ignore interrupt";
 #endif
         return;
     }
 
     ScopedSpinLock lock(m_request_lock);
 #ifdef PATA_DEBUG
-    klog() << "PATAChannel: interrupt: DRQ=" << ((status & ATA_SR_DRQ) != 0) << " BSY=" << ((status & ATA_SR_BSY) != 0) << " DRDY=" << ((status & ATA_SR_DRDY) != 0);
+    klog() << "IDEChannel: interrupt: DRQ=" << ((status & ATA_SR_DRQ) != 0) << " BSY=" << ((status & ATA_SR_BSY) != 0) << " DRDY=" << ((status & ATA_SR_DRDY) != 0);
 #endif
 
     if (!m_current_request) {
 #ifdef PATA_DEBUG
-        dbg() << "PATAChannel: IRQ but no pending request!";
+        dbg() << "IDEChannel: IRQ but no pending request!";
 #endif
         return;
     }
@@ -253,8 +255,8 @@ void PATAChannel::handle_irq(const RegisterState&)
 
     if (status & ATA_SR_ERR) {
         print_ide_status(status);
-        m_device_error = m_io_base.offset(ATA_REG_ERROR).in<u8>();
-        klog() << "PATAChannel: Error " << String::format("%b", m_device_error) << "!";
+        m_device_error = m_io_group.io_base().offset(ATA_REG_ERROR).in<u8>();
+        klog() << "IDEChannel: Error " << String::format("%b", m_device_error) << "!";
         complete_current_request(AsyncDeviceRequest::Failure);
         return;
     }
@@ -270,7 +272,7 @@ void PATAChannel::handle_irq(const RegisterState&)
         // trigger page faults
         Processor::deferred_call_queue([this]() {
             if (m_current_request->request_type() == AsyncBlockDeviceRequest::Read) {
-                dbg() << "PATAChannel: Read block " << m_current_request_block_index << "/" << m_current_request->block_count();
+                dbg() << "IDEChannel: Read block " << m_current_request_block_index << "/" << m_current_request->block_count();
                 if (ata_do_read_sector()) {
                     if (++m_current_request_block_index >= m_current_request->block_count()) {
                         complete_current_request(AsyncDeviceRequest::Success);
@@ -281,12 +283,12 @@ void PATAChannel::handle_irq(const RegisterState&)
                 }
             } else {
                 if (!m_current_request_flushing_cache) {
-                    dbg() << "PATAChannel: Wrote block " << m_current_request_block_index << "/" << m_current_request->block_count();
+                    dbg() << "IDEChannel: Wrote block " << m_current_request_block_index << "/" << m_current_request->block_count();
                     if (++m_current_request_block_index >= m_current_request->block_count()) {
                         // We read the last block, flush cache
                         ASSERT(!m_current_request_flushing_cache);
                         m_current_request_flushing_cache = true;
-                        m_io_base.offset(ATA_REG_COMMAND).out<u8>(ATA_CMD_CACHE_FLUSH);
+                        m_io_group.io_base().offset(ATA_REG_COMMAND).out<u8>(ATA_CMD_CACHE_FLUSH);
                     } else {
                         // Read next block
                         ata_do_write_sector();
@@ -305,27 +307,27 @@ static void io_delay()
         IO::in8(0x3f6);
 }
 
-void PATAChannel::detect_disks()
+void IDEChannel::detect_disks()
 {
     // There are only two possible disks connected to a channel
     for (auto i = 0; i < 2; i++) {
-        m_io_base.offset(ATA_REG_HDDEVSEL).out<u8>(0xA0 | (i << 4)); // First, we need to select the drive itself
+        m_io_group.io_base().offset(ATA_REG_HDDEVSEL).out<u8>(0xA0 | (i << 4)); // First, we need to select the drive itself
 
         // Apparently these need to be 0 before sending IDENTIFY?!
-        m_io_base.offset(ATA_REG_SECCOUNT0).out<u8>(0x00);
-        m_io_base.offset(ATA_REG_LBA0).out<u8>(0x00);
-        m_io_base.offset(ATA_REG_LBA1).out<u8>(0x00);
-        m_io_base.offset(ATA_REG_LBA2).out<u8>(0x00);
+        m_io_group.io_base().offset(ATA_REG_SECCOUNT0).out<u8>(0x00);
+        m_io_group.io_base().offset(ATA_REG_LBA0).out<u8>(0x00);
+        m_io_group.io_base().offset(ATA_REG_LBA1).out<u8>(0x00);
+        m_io_group.io_base().offset(ATA_REG_LBA2).out<u8>(0x00);
 
-        m_io_base.offset(ATA_REG_COMMAND).out<u8>(ATA_CMD_IDENTIFY); // Send the ATA_IDENTIFY command
+        m_io_group.io_base().offset(ATA_REG_COMMAND).out<u8>(ATA_CMD_IDENTIFY); // Send the ATA_IDENTIFY command
 
         // Wait for the BSY flag to be reset
-        while (m_io_base.offset(ATA_REG_STATUS).in<u8>() & ATA_SR_BSY)
+        while (m_io_group.io_base().offset(ATA_REG_STATUS).in<u8>() & ATA_SR_BSY)
             ;
 
-        if (m_io_base.offset(ATA_REG_STATUS).in<u8>() == 0x00) {
+        if (m_io_group.io_base().offset(ATA_REG_STATUS).in<u8>() == 0x00) {
 #ifdef PATA_DEBUG
-            klog() << "PATAChannel: No " << (i == 0 ? "master" : "slave") << " disk detected!";
+            klog() << "IDEChannel: No " << (i == 0 ? "master" : "slave") << " disk detected!";
 #endif
             continue;
         }
@@ -337,7 +339,7 @@ void PATAChannel::detect_disks()
         const u16* wbufbase = (u16*)wbuf.data();
 
         for (u32 i = 0; i < 256; ++i) {
-            u16 data = m_io_base.offset(ATA_REG_DATA).in<u16>();
+            u16 data = m_io_group.io_base().offset(ATA_REG_DATA).in<u16>();
             *(w++) = data;
             *(b++) = MSB(data);
             *(b++) = LSB(data);
@@ -350,26 +352,25 @@ void PATAChannel::detect_disks()
         u8 cyls = wbufbase[1];
         u8 heads = wbufbase[3];
         u8 spt = wbufbase[6];
-
-        klog() << "PATAChannel: Name=" << ((char*)bbuf.data() + 54) << ", C/H/Spt=" << cyls << "/" << heads << "/" << spt;
+        if (cyls == 0 || heads == 0 || spt == 0)
+            continue;
+        klog() << "IDEChannel: Name=" << ((char*)bbuf.data() + 54) << ", C/H/Spt=" << cyls << "/" << heads << "/" << spt;
 
         int major = (m_channel_number == 0) ? 3 : 4;
         if (i == 0) {
-            m_master = PATADiskDevice::create(*this, PATADiskDevice::DriveType::Master, major, 0);
-            m_master->set_drive_geometry(cyls, heads, spt);
+            m_master = PATADiskDevice::create(m_parent_controller, *this, PATADiskDevice::DriveType::Master, cyls, heads, spt, major, 0);
         } else {
-            m_slave = PATADiskDevice::create(*this, PATADiskDevice::DriveType::Slave, major, 1);
-            m_slave->set_drive_geometry(cyls, heads, spt);
+            m_slave = PATADiskDevice::create(m_parent_controller, *this, PATADiskDevice::DriveType::Slave, cyls, heads, spt, major, 1);
         }
     }
 }
 
-void PATAChannel::ata_read_sectors_with_dma(bool slave_request)
+void IDEChannel::ata_read_sectors_with_dma(bool slave_request)
 {
     auto& request = *m_current_request;
     u32 lba = request.block_index();
 #ifdef PATA_DEBUG
-    dbg() << "PATAChannel::ata_read_sectors_with_dma (" << lba << " x" << request.block_count() << ")";
+    dbg() << "IDEChannel::ata_read_sectors_with_dma (" << lba << " x" << request.block_count() << ")";
 #endif
 
     prdt().offset = m_dma_buffer_page->paddr();
@@ -378,57 +379,57 @@ void PATAChannel::ata_read_sectors_with_dma(bool slave_request)
     ASSERT(prdt().size <= PAGE_SIZE);
 
     // Stop bus master
-    m_bus_master_base.out<u8>(0);
+    m_io_group.bus_master_base().out<u8>(0);
 
     // Write the PRDT location
-    m_bus_master_base.offset(4).out(m_prdt_page->paddr().get());
+    m_io_group.bus_master_base().offset(4).out(m_prdt_page->paddr().get());
 
     // Turn on "Interrupt" and "Error" flag. The error flag should be cleared by hardware.
-    m_bus_master_base.offset(2).out<u8>(m_bus_master_base.offset(2).in<u8>() | 0x6);
+    m_io_group.bus_master_base().offset(2).out<u8>(m_io_group.bus_master_base().offset(2).in<u8>() | 0x6);
 
     // Set transfer direction
-    m_bus_master_base.out<u8>(0x8);
+    m_io_group.bus_master_base().out<u8>(0x8);
 
-    while (m_io_base.offset(ATA_REG_STATUS).in<u8>() & ATA_SR_BSY)
+    while (m_io_group.io_base().offset(ATA_REG_STATUS).in<u8>() & ATA_SR_BSY)
         ;
 
-    m_control_base.offset(ATA_CTL_CONTROL).out<u8>(0);
-    m_io_base.offset(ATA_REG_HDDEVSEL).out<u8>(0x40 | (static_cast<u8>(slave_request) << 4));
+    m_io_group.control_base().offset(ATA_CTL_CONTROL).out<u8>(0);
+    m_io_group.io_base().offset(ATA_REG_HDDEVSEL).out<u8>(0x40 | (static_cast<u8>(slave_request) << 4));
     io_delay();
 
-    m_io_base.offset(ATA_REG_FEATURES).out<u16>(0);
+    m_io_group.io_base().offset(ATA_REG_FEATURES).out<u16>(0);
 
-    m_io_base.offset(ATA_REG_SECCOUNT0).out<u8>(0);
-    m_io_base.offset(ATA_REG_LBA0).out<u8>(0);
-    m_io_base.offset(ATA_REG_LBA1).out<u8>(0);
-    m_io_base.offset(ATA_REG_LBA2).out<u8>(0);
+    m_io_group.io_base().offset(ATA_REG_SECCOUNT0).out<u8>(0);
+    m_io_group.io_base().offset(ATA_REG_LBA0).out<u8>(0);
+    m_io_group.io_base().offset(ATA_REG_LBA1).out<u8>(0);
+    m_io_group.io_base().offset(ATA_REG_LBA2).out<u8>(0);
 
-    m_io_base.offset(ATA_REG_SECCOUNT0).out<u8>(request.block_count());
-    m_io_base.offset(ATA_REG_LBA0).out<u8>((lba & 0x000000ff) >> 0);
-    m_io_base.offset(ATA_REG_LBA1).out<u8>((lba & 0x0000ff00) >> 8);
-    m_io_base.offset(ATA_REG_LBA2).out<u8>((lba & 0x00ff0000) >> 16);
+    m_io_group.io_base().offset(ATA_REG_SECCOUNT0).out<u8>(request.block_count());
+    m_io_group.io_base().offset(ATA_REG_LBA0).out<u8>((lba & 0x000000ff) >> 0);
+    m_io_group.io_base().offset(ATA_REG_LBA1).out<u8>((lba & 0x0000ff00) >> 8);
+    m_io_group.io_base().offset(ATA_REG_LBA2).out<u8>((lba & 0x00ff0000) >> 16);
 
     for (;;) {
-        auto status = m_io_base.offset(ATA_REG_STATUS).in<u8>();
+        auto status = m_io_group.io_base().offset(ATA_REG_STATUS).in<u8>();
         if (!(status & ATA_SR_BSY) && (status & ATA_SR_DRDY))
             break;
     }
 
-    m_io_base.offset(ATA_REG_COMMAND).out<u8>(ATA_CMD_READ_DMA_EXT);
+    m_io_group.io_base().offset(ATA_REG_COMMAND).out<u8>(ATA_CMD_READ_DMA_EXT);
     io_delay();
 
     enable_irq();
     // Start bus master
-    m_bus_master_base.out<u8>(0x9);
+    m_io_group.bus_master_base().out<u8>(0x9);
 }
 
-bool PATAChannel::ata_do_read_sector()
+bool IDEChannel::ata_do_read_sector()
 {
     auto& request = *m_current_request;
     auto out_buffer = request.buffer().offset(m_current_request_block_index * 512);
     ssize_t nwritten = request.write_to_buffer_buffered<512>(out_buffer, 512, [&](u8* buffer, size_t buffer_bytes) {
         for (size_t i = 0; i < buffer_bytes; i += sizeof(u16))
-            *(u16*)&buffer[i] = IO::in16(m_io_base.offset(ATA_REG_DATA).get());
+            *(u16*)&buffer[i] = IO::in16(m_io_group.io_base().offset(ATA_REG_DATA).get());
         return (ssize_t)buffer_bytes;
     });
     if (nwritten < 0) {
@@ -439,58 +440,58 @@ bool PATAChannel::ata_do_read_sector()
     return true;
 }
 
-void PATAChannel::ata_read_sectors(bool slave_request)
+void IDEChannel::ata_read_sectors(bool slave_request)
 {
     auto& request = *m_current_request;
     ASSERT(request.block_count() <= 256);
 #ifdef PATA_DEBUG
-    dbg() << "PATAChannel::ata_read_sectors";
+    dbg() << "IDEChannel::ata_read_sectors";
 #endif
 
-    while (m_io_base.offset(ATA_REG_STATUS).in<u8>() & ATA_SR_BSY)
+    while (m_io_group.io_base().offset(ATA_REG_STATUS).in<u8>() & ATA_SR_BSY)
         ;
 
     auto lba = request.block_index();
 #ifdef PATA_DEBUG
-    klog() << "PATAChannel: Reading " << request.block_count() << " sector(s) @ LBA " << lba;
+    klog() << "IDEChannel: Reading " << request.block_count() << " sector(s) @ LBA " << lba;
 #endif
 
     u8 devsel = 0xe0;
     if (slave_request)
         devsel |= 0x10;
 
-    m_control_base.offset(ATA_CTL_CONTROL).out<u8>(0);
-    m_io_base.offset(ATA_REG_HDDEVSEL).out<u8>(devsel | (static_cast<u8>(slave_request) << 4) | 0x40);
+    m_io_group.control_base().offset(ATA_CTL_CONTROL).out<u8>(0);
+    m_io_group.io_base().offset(ATA_REG_HDDEVSEL).out<u8>(devsel | (static_cast<u8>(slave_request) << 4) | 0x40);
     io_delay();
 
-    m_io_base.offset(ATA_REG_FEATURES).out<u8>(0);
+    m_io_group.io_base().offset(ATA_REG_FEATURES).out<u8>(0);
 
-    m_io_base.offset(ATA_REG_SECCOUNT0).out<u8>(0);
-    m_io_base.offset(ATA_REG_LBA0).out<u8>(0);
-    m_io_base.offset(ATA_REG_LBA1).out<u8>(0);
-    m_io_base.offset(ATA_REG_LBA2).out<u8>(0);
+    m_io_group.io_base().offset(ATA_REG_SECCOUNT0).out<u8>(0);
+    m_io_group.io_base().offset(ATA_REG_LBA0).out<u8>(0);
+    m_io_group.io_base().offset(ATA_REG_LBA1).out<u8>(0);
+    m_io_group.io_base().offset(ATA_REG_LBA2).out<u8>(0);
 
-    m_io_base.offset(ATA_REG_SECCOUNT0).out<u8>(request.block_count());
-    m_io_base.offset(ATA_REG_LBA0).out<u8>((lba & 0x000000ff) >> 0);
-    m_io_base.offset(ATA_REG_LBA1).out<u8>((lba & 0x0000ff00) >> 8);
-    m_io_base.offset(ATA_REG_LBA2).out<u8>((lba & 0x00ff0000) >> 16);
+    m_io_group.io_base().offset(ATA_REG_SECCOUNT0).out<u8>(request.block_count());
+    m_io_group.io_base().offset(ATA_REG_LBA0).out<u8>((lba & 0x000000ff) >> 0);
+    m_io_group.io_base().offset(ATA_REG_LBA1).out<u8>((lba & 0x0000ff00) >> 8);
+    m_io_group.io_base().offset(ATA_REG_LBA2).out<u8>((lba & 0x00ff0000) >> 16);
 
     for (;;) {
-        auto status = m_io_base.offset(ATA_REG_STATUS).in<u8>();
+        auto status = m_io_group.io_base().offset(ATA_REG_STATUS).in<u8>();
         if (!(status & ATA_SR_BSY) && (status & ATA_SR_DRDY))
             break;
     }
 
     enable_irq();
-    m_io_base.offset(ATA_REG_COMMAND).out<u8>(ATA_CMD_READ_PIO);
+    m_io_group.io_base().offset(ATA_REG_COMMAND).out<u8>(ATA_CMD_READ_PIO);
 }
 
-void PATAChannel::ata_write_sectors_with_dma(bool slave_request)
+void IDEChannel::ata_write_sectors_with_dma(bool slave_request)
 {
     auto& request = *m_current_request;
     u32 lba = request.block_index();
 #ifdef PATA_DEBUG
-    dbg() << "PATAChannel::ata_write_sectors_with_dma (" << lba << " x" << request.block_count() << ")";
+    dbg() << "IDEChannel::ata_write_sectors_with_dma (" << lba << " x" << request.block_count() << ")";
 #endif
 
     prdt().offset = m_dma_buffer_page->paddr();
@@ -504,72 +505,72 @@ void PATAChannel::ata_write_sectors_with_dma(bool slave_request)
     ASSERT(prdt().size <= PAGE_SIZE);
 
     // Stop bus master
-    m_bus_master_base.out<u8>(0);
+    m_io_group.bus_master_base().out<u8>(0);
 
     // Write the PRDT location
-    m_bus_master_base.offset(4).out<u32>(m_prdt_page->paddr().get());
+    m_io_group.bus_master_base().offset(4).out<u32>(m_prdt_page->paddr().get());
 
     // Turn on "Interrupt" and "Error" flag. The error flag should be cleared by hardware.
-    m_bus_master_base.offset(2).out<u8>(m_bus_master_base.offset(2).in<u8>() | 0x6);
+    m_io_group.bus_master_base().offset(2).out<u8>(m_io_group.bus_master_base().offset(2).in<u8>() | 0x6);
 
-    while (m_io_base.offset(ATA_REG_STATUS).in<u8>() & ATA_SR_BSY)
+    while (m_io_group.io_base().offset(ATA_REG_STATUS).in<u8>() & ATA_SR_BSY)
         ;
 
-    m_control_base.offset(ATA_CTL_CONTROL).out<u8>(0);
-    m_io_base.offset(ATA_REG_HDDEVSEL).out<u8>(0x40 | (static_cast<u8>(slave_request) << 4));
+    m_io_group.control_base().offset(ATA_CTL_CONTROL).out<u8>(0);
+    m_io_group.io_base().offset(ATA_REG_HDDEVSEL).out<u8>(0x40 | (static_cast<u8>(slave_request) << 4));
     io_delay();
 
-    m_io_base.offset(ATA_REG_FEATURES).out<u16>(0);
+    m_io_group.io_base().offset(ATA_REG_FEATURES).out<u16>(0);
 
-    m_io_base.offset(ATA_REG_SECCOUNT0).out<u8>(0);
-    m_io_base.offset(ATA_REG_LBA0).out<u8>(0);
-    m_io_base.offset(ATA_REG_LBA1).out<u8>(0);
-    m_io_base.offset(ATA_REG_LBA2).out<u8>(0);
+    m_io_group.io_base().offset(ATA_REG_SECCOUNT0).out<u8>(0);
+    m_io_group.io_base().offset(ATA_REG_LBA0).out<u8>(0);
+    m_io_group.io_base().offset(ATA_REG_LBA1).out<u8>(0);
+    m_io_group.io_base().offset(ATA_REG_LBA2).out<u8>(0);
 
-    m_io_base.offset(ATA_REG_SECCOUNT0).out<u8>(request.block_count());
-    m_io_base.offset(ATA_REG_LBA0).out<u8>((lba & 0x000000ff) >> 0);
-    m_io_base.offset(ATA_REG_LBA1).out<u8>((lba & 0x0000ff00) >> 8);
-    m_io_base.offset(ATA_REG_LBA2).out<u8>((lba & 0x00ff0000) >> 16);
+    m_io_group.io_base().offset(ATA_REG_SECCOUNT0).out<u8>(request.block_count());
+    m_io_group.io_base().offset(ATA_REG_LBA0).out<u8>((lba & 0x000000ff) >> 0);
+    m_io_group.io_base().offset(ATA_REG_LBA1).out<u8>((lba & 0x0000ff00) >> 8);
+    m_io_group.io_base().offset(ATA_REG_LBA2).out<u8>((lba & 0x00ff0000) >> 16);
 
     for (;;) {
-        auto status = m_io_base.offset(ATA_REG_STATUS).in<u8>();
+        auto status = m_io_group.io_base().offset(ATA_REG_STATUS).in<u8>();
         if (!(status & ATA_SR_BSY) && (status & ATA_SR_DRDY))
             break;
     }
 
-    m_io_base.offset(ATA_REG_COMMAND).out<u8>(ATA_CMD_WRITE_DMA_EXT);
+    m_io_group.io_base().offset(ATA_REG_COMMAND).out<u8>(ATA_CMD_WRITE_DMA_EXT);
     io_delay();
 
     enable_irq();
     // Start bus master
-    m_bus_master_base.out<u8>(0x1);
+    m_io_group.bus_master_base().out<u8>(0x1);
 }
 
-void PATAChannel::ata_do_write_sector()
+void IDEChannel::ata_do_write_sector()
 {
     auto& request = *m_current_request;
 
     io_delay();
-    while ((m_io_base.offset(ATA_REG_STATUS).in<u8>() & ATA_SR_BSY) || !(m_io_base.offset(ATA_REG_STATUS).in<u8>() & ATA_SR_DRQ))
+    while ((m_io_group.io_base().offset(ATA_REG_STATUS).in<u8>() & ATA_SR_BSY) || !(m_io_group.io_base().offset(ATA_REG_STATUS).in<u8>() & ATA_SR_DRQ))
         ;
 
-    u8 status = m_io_base.offset(ATA_REG_STATUS).in<u8>();
+    u8 status = m_io_group.io_base().offset(ATA_REG_STATUS).in<u8>();
     ASSERT(status & ATA_SR_DRQ);
 
     auto in_buffer = request.buffer().offset(m_current_request_block_index * 512);
 #ifdef PATA_DEBUG
-    dbg() << "PATAChannel: Writing 512 bytes (part " << m_current_request_block_index << ") (status=" << String::format("%b", status) << ")...";
+    dbg() << "IDEChannel: Writing 512 bytes (part " << m_current_request_block_index << ") (status=" << String::format("%b", status) << ")...";
 #endif
     ssize_t nread = request.read_from_buffer_buffered<512>(in_buffer, 512, [&](const u8* buffer, size_t buffer_bytes) {
         for (size_t i = 0; i < buffer_bytes; i += sizeof(u16))
-            IO::out16(m_io_base.offset(ATA_REG_DATA).get(), *(const u16*)&buffer[i]);
+            IO::out16(m_io_group.io_base().offset(ATA_REG_DATA).get(), *(const u16*)&buffer[i]);
         return (ssize_t)buffer_bytes;
     });
     if (nread < 0)
         complete_current_request(AsyncDeviceRequest::MemoryFault);
 }
 
-void PATAChannel::ata_write_sectors(bool slave_request)
+void IDEChannel::ata_write_sectors(bool slave_request)
 {
     auto& request = *m_current_request;
 
@@ -577,34 +578,34 @@ void PATAChannel::ata_write_sectors(bool slave_request)
     u32 start_sector = request.block_index();
     u32 count = request.block_count();
 #ifdef PATA_DEBUG
-    klog() << "PATAChannel::ata_write_sectors request (" << count << " sector(s) @ " << start_sector << ")";
+    klog() << "IDEChannel::ata_write_sectors request (" << count << " sector(s) @ " << start_sector << ")";
 #endif
 
-    while (m_io_base.offset(ATA_REG_STATUS).in<u8>() & ATA_SR_BSY)
+    while (m_io_group.io_base().offset(ATA_REG_STATUS).in<u8>() & ATA_SR_BSY)
         ;
 
 #ifdef PATA_DEBUG
-    klog() << "PATAChannel: Writing " << count << " sector(s) @ LBA " << start_sector;
+    klog() << "IDEChannel: Writing " << count << " sector(s) @ LBA " << start_sector;
 #endif
 
     u8 devsel = 0xe0;
     if (slave_request)
         devsel |= 0x10;
 
-    m_io_base.offset(ATA_REG_SECCOUNT0).out<u8>(count == 256 ? 0 : LSB(count));
-    m_io_base.offset(ATA_REG_LBA0).out<u8>(start_sector & 0xff);
-    m_io_base.offset(ATA_REG_LBA1).out<u8>((start_sector >> 8) & 0xff);
-    m_io_base.offset(ATA_REG_LBA2).out<u8>((start_sector >> 16) & 0xff);
-    m_io_base.offset(ATA_REG_HDDEVSEL).out<u8>(devsel | ((start_sector >> 24) & 0xf));
+    m_io_group.io_base().offset(ATA_REG_SECCOUNT0).out<u8>(count == 256 ? 0 : LSB(count));
+    m_io_group.io_base().offset(ATA_REG_LBA0).out<u8>(start_sector & 0xff);
+    m_io_group.io_base().offset(ATA_REG_LBA1).out<u8>((start_sector >> 8) & 0xff);
+    m_io_group.io_base().offset(ATA_REG_LBA2).out<u8>((start_sector >> 16) & 0xff);
+    m_io_group.io_base().offset(ATA_REG_HDDEVSEL).out<u8>(devsel | ((start_sector >> 24) & 0xf));
 
     IO::out8(0x3F6, 0x08);
-    while (!(m_io_base.offset(ATA_REG_STATUS).in<u8>() & ATA_SR_DRDY))
+    while (!(m_io_group.io_base().offset(ATA_REG_STATUS).in<u8>() & ATA_SR_DRDY))
         ;
 
-    m_io_base.offset(ATA_REG_COMMAND).out<u8>(ATA_CMD_WRITE_PIO);
+    m_io_group.io_base().offset(ATA_REG_COMMAND).out<u8>(ATA_CMD_WRITE_PIO);
 
     io_delay();
-    while ((m_io_base.offset(ATA_REG_STATUS).in<u8>() & ATA_SR_BSY) || !(m_io_base.offset(ATA_REG_STATUS).in<u8>() & ATA_SR_DRQ))
+    while ((m_io_group.io_base().offset(ATA_REG_STATUS).in<u8>() & ATA_SR_BSY) || !(m_io_group.io_base().offset(ATA_REG_STATUS).in<u8>() & ATA_SR_DRQ))
         ;
 
     enable_irq();

+ 49 - 14
Kernel/Devices/PATAChannel.h → Kernel/Storage/IDEChannel.h

@@ -41,11 +41,11 @@
 #include <AK/RefPtr.h>
 #include <Kernel/Devices/Device.h>
 #include <Kernel/IO.h>
+#include <Kernel/Interrupts/IRQHandler.h>
 #include <Kernel/Lock.h>
-#include <Kernel/PCI/Access.h>
-#include <Kernel/PCI/Device.h>
 #include <Kernel/PhysicalAddress.h>
 #include <Kernel/Random.h>
+#include <Kernel/Storage/StorageDevice.h>
 #include <Kernel/VM/PhysicalPage.h>
 #include <Kernel/WaitQueue.h>
 
@@ -59,8 +59,9 @@ struct PhysicalRegionDescriptor {
     u16 end_of_table { 0 };
 };
 
-class PATADiskDevice;
-class PATAChannel final : public PCI::Device {
+class IDEController;
+class IDEChannel final : public IRQHandler {
+    friend class IDEController;
     friend class PATADiskDevice;
     AK_MAKE_ETERNAL
 public:
@@ -69,13 +70,46 @@ public:
         Secondary
     };
 
+    struct IOAddressGroup {
+        IOAddressGroup(IOAddress io_base, IOAddress control_base, IOAddress bus_master_base)
+            : m_io_base(io_base)
+            , m_control_base(control_base)
+            , m_bus_master_base(bus_master_base)
+        {
+        }
+
+        // Disable default implementations that would use surprising integer promotion.
+        bool operator==(const IOAddressGroup&) const = delete;
+        bool operator<=(const IOAddressGroup&) const = delete;
+        bool operator>=(const IOAddressGroup&) const = delete;
+        bool operator<(const IOAddressGroup&) const = delete;
+        bool operator>(const IOAddressGroup&) const = delete;
+
+        IOAddress io_base() const { return m_io_base; };
+        IOAddress control_base() const { return m_control_base; }
+        IOAddress bus_master_base() const { return m_bus_master_base; }
+
+        const IOAddressGroup& operator=(const IOAddressGroup& group)
+        {
+            m_io_base = group.io_base();
+            m_control_base = group.control_base();
+            m_bus_master_base = group.bus_master_base();
+            return *this;
+        }
+
+    private:
+        IOAddress m_io_base;
+        IOAddress m_control_base;
+        IOAddress m_bus_master_base;
+    };
+
 public:
-    static OwnPtr<PATAChannel> create(ChannelType type, bool force_pio);
-    PATAChannel(PCI::Address address, ChannelType type, bool force_pio);
-    virtual ~PATAChannel() override;
+    static NonnullOwnPtr<IDEChannel> create(const IDEController&, IOAddressGroup, ChannelType type, bool force_pio);
+    IDEChannel(const IDEController&, IOAddressGroup, ChannelType type, bool force_pio);
+    virtual ~IDEChannel() override;
 
-    RefPtr<PATADiskDevice> master_device() { return m_master; };
-    RefPtr<PATADiskDevice> slave_device() { return m_slave; };
+    RefPtr<StorageDevice> master_device();
+    RefPtr<StorageDevice> slave_device();
 
     virtual const char* purpose() const override { return "PATA Channel"; }
 
@@ -98,24 +132,25 @@ private:
 
     // Data members
     u8 m_channel_number { 0 }; // Channel number. 0 = master, 1 = slave
-    IOAddress m_io_base;
-    IOAddress m_control_base;
+
     volatile u8 m_device_error { 0 };
 
     PhysicalRegionDescriptor& prdt() { return *reinterpret_cast<PhysicalRegionDescriptor*>(m_prdt_page->paddr().offset(0xc0000000).as_ptr()); }
     RefPtr<PhysicalPage> m_prdt_page;
     RefPtr<PhysicalPage> m_dma_buffer_page;
-    IOAddress m_bus_master_base;
     Lockable<bool> m_dma_enabled;
     EntropySource m_entropy_source;
 
-    RefPtr<PATADiskDevice> m_master;
-    RefPtr<PATADiskDevice> m_slave;
+    RefPtr<StorageDevice> m_master;
+    RefPtr<StorageDevice> m_slave;
 
     AsyncBlockDeviceRequest* m_current_request { nullptr };
     u32 m_current_request_block_index { 0 };
     bool m_current_request_uses_dma { false };
     bool m_current_request_flushing_cache { false };
     SpinLock<u8> m_request_lock;
+
+    IOAddressGroup m_io_group;
+    NonnullRefPtr<IDEController> m_parent_controller;
 };
 }

+ 103 - 0
Kernel/Storage/IDEController.cpp

@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2020, Liav A. <liavalb@hotmail.co.il>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <AK/OwnPtr.h>
+#include <AK/RefPtr.h>
+#include <AK/Types.h>
+#include <Kernel/Storage/IDEController.h>
+#include <Kernel/Storage/PATADiskDevice.h>
+
+namespace Kernel {
+
+NonnullRefPtr<IDEController> IDEController::initialize(PCI::Address address, bool force_pio)
+{
+    return adopt(*new IDEController(address, force_pio));
+}
+
+bool IDEController::reset()
+{
+    TODO();
+}
+
+bool IDEController::shutdown()
+{
+    TODO();
+}
+
+void IDEController::start_request(const StorageDevice&, AsyncBlockDeviceRequest&)
+{
+    ASSERT_NOT_REACHED();
+}
+
+void IDEController::complete_current_request(AsyncDeviceRequest::RequestResult)
+{
+    ASSERT_NOT_REACHED();
+}
+
+IDEController::IDEController(PCI::Address address, bool force_pio)
+    : StorageController(address)
+{
+    initialize(force_pio);
+}
+
+IDEController::~IDEController()
+{
+}
+
+void IDEController::initialize(bool force_pio)
+{
+    auto bus_master_base = IOAddress(PCI::get_BAR4(pci_address()) & (~1));
+
+    auto bar0 = PCI::get_BAR0(pci_address());
+    auto base_io = (bar0 == 0x1 || bar0 == 0) ? IOAddress(0x1F0) : IOAddress(bar0);
+    auto bar1 = PCI::get_BAR1(pci_address());
+    auto control_io = (bar1 == 0x1 || bar1 == 0) ? IOAddress(0x3F6) : IOAddress(bar1);
+
+    m_channels.append(IDEChannel::create(*this, { base_io, control_io, bus_master_base }, IDEChannel::ChannelType::Primary, force_pio));
+
+    auto bar2 = PCI::get_BAR2(pci_address());
+    base_io = (bar2 == 0x1 || bar2 == 0) ? IOAddress(0x170) : IOAddress(bar2);
+    auto bar3 = PCI::get_BAR3(pci_address());
+    control_io = (bar3 == 0x1 || bar3 == 0) ? IOAddress(0x376) : IOAddress(bar3);
+    m_channels.append(IDEChannel::create(*this, { base_io, control_io, bus_master_base.offset(8) }, IDEChannel::ChannelType::Secondary, force_pio));
+}
+
+RefPtr<StorageDevice> IDEController::device(u32 index)
+{
+    switch (index) {
+    case 0:
+        return m_channels[0].master_device();
+    case 1:
+        return m_channels[0].slave_device();
+    case 2:
+        return m_channels[1].master_device();
+    case 3:
+        return m_channels[1].slave_device();
+    }
+    return nullptr;
+}
+
+}

+ 61 - 0
Kernel/Storage/IDEController.h

@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2020, Liav A. <liavalb@hotmail.co.il>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/OwnPtr.h>
+#include <AK/RefPtr.h>
+#include <AK/Types.h>
+#include <Kernel/Storage/IDEChannel.h>
+#include <Kernel/Storage/StorageController.h>
+#include <Kernel/Storage/StorageDevice.h>
+
+namespace Kernel {
+
+class AsyncBlockDeviceRequest;
+
+class IDEController final : public StorageController {
+    AK_MAKE_ETERNAL
+public:
+public:
+    static NonnullRefPtr<IDEController> initialize(PCI::Address address, bool force_pio);
+    virtual ~IDEController() override;
+
+    virtual RefPtr<StorageDevice> device(u32 index) override;
+    virtual bool reset() override;
+    virtual bool shutdown() override;
+    virtual void start_request(const StorageDevice&, AsyncBlockDeviceRequest&) override;
+    virtual void complete_current_request(AsyncDeviceRequest::RequestResult) override;
+
+private:
+    IDEController(PCI::Address address, bool force_pio);
+
+    void initialize(bool force_pio);
+    void detect_disks();
+
+    NonnullOwnPtrVector<IDEChannel> m_channels;
+};
+}

+ 78 - 0
Kernel/Storage/PATADiskDevice.cpp

@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+//#define PATA_DEVICE_DEBUG
+
+#include <AK/Memory.h>
+#include <AK/StringView.h>
+#include <Kernel/FileSystem/FileDescription.h>
+#include <Kernel/Storage/IDEChannel.h>
+#include <Kernel/Storage/IDEController.h>
+#include <Kernel/Storage/PATADiskDevice.h>
+
+namespace Kernel {
+
+NonnullRefPtr<PATADiskDevice> PATADiskDevice::create(const IDEController& controller, IDEChannel& channel, DriveType type, u8 cylinders, u8 heads, u8 spt, int major, int minor)
+{
+    return adopt(*new PATADiskDevice(controller, channel, type, cylinders, heads, spt, major, minor));
+}
+
+PATADiskDevice::PATADiskDevice(const IDEController& controller, IDEChannel& channel, DriveType type, u8 cylinders, u8 heads, u8 spt, int major, int minor)
+    : StorageDevice(controller, major, minor, 512, 0)
+    , m_cylinders(cylinders)
+    , m_heads(heads)
+    , m_sectors_per_track(spt)
+    , m_channel(channel)
+    , m_drive_type(type)
+{
+}
+
+PATADiskDevice::~PATADiskDevice()
+{
+}
+
+const char* PATADiskDevice::class_name() const
+{
+    return "PATADiskDevice";
+}
+
+void PATADiskDevice::start_request(AsyncBlockDeviceRequest& request)
+{
+    bool use_dma = !m_channel.m_io_group.bus_master_base().is_null() && m_channel.m_dma_enabled.resource();
+    m_channel.start_request(request, use_dma, is_slave());
+}
+
+size_t PATADiskDevice::max_addressable_block() const
+{
+    return m_cylinders * m_heads * m_sectors_per_track;
+}
+
+bool PATADiskDevice::is_slave() const
+{
+    return m_drive_type == DriveType::Slave;
+}
+
+}

+ 11 - 14
Kernel/Devices/PATADiskDevice.h → Kernel/Storage/PATADiskDevice.h

@@ -30,15 +30,16 @@
 
 #pragma once
 
-#include <Kernel/Devices/BlockDevice.h>
 #include <Kernel/Interrupts/IRQHandler.h>
 #include <Kernel/Lock.h>
+#include <Kernel/Storage/StorageDevice.h>
 
 namespace Kernel {
 
-class PATAChannel;
+class IDEController;
 
-class PATADiskDevice final : public BlockDevice {
+class PATADiskDevice final : public StorageDevice {
+    friend class IDEController;
     AK_MAKE_ETERNAL
 public:
     // Type of drive this IDEDiskDevice is on the ATA channel.
@@ -51,22 +52,19 @@ public:
     };
 
 public:
-    static NonnullRefPtr<PATADiskDevice> create(PATAChannel&, DriveType, int major, int minor);
+    static NonnullRefPtr<PATADiskDevice> create(const IDEController&, IDEChannel&, DriveType, u8, u8, u8, int major, int minor);
     virtual ~PATADiskDevice() override;
 
-    void set_drive_geometry(u16, u16, u16);
+    // ^StorageDevice
+    virtual Type type() const override { return StorageDevice::Type::IDE; }
+    virtual size_t max_addressable_block() const override;
 
     // ^BlockDevice
     virtual void start_request(AsyncBlockDeviceRequest&) override;
-    virtual KResultOr<size_t> read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) override;
-    virtual bool can_read(const FileDescription&, size_t) const override;
-    virtual KResultOr<size_t> write(FileDescription&, size_t, const UserOrKernelBuffer&, size_t) override;
-    virtual bool can_write(const FileDescription&, size_t) const override;
-
-protected:
-    explicit PATADiskDevice(PATAChannel&, DriveType, int, int);
 
 private:
+    PATADiskDevice(const IDEController&, IDEChannel&, DriveType, u8, u8, u8, int major, int minor);
+
     // ^DiskDevice
     virtual const char* class_name() const override;
 
@@ -76,9 +74,8 @@ private:
     u16 m_cylinders { 0 };
     u16 m_heads { 0 };
     u16 m_sectors_per_track { 0 };
+    IDEChannel& m_channel;
     DriveType m_drive_type { DriveType::Master };
-
-    PATAChannel& m_channel;
 };
 
 }

+ 64 - 0
Kernel/Storage/StorageController.h

@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2020, Liav A. <liavalb@hotmail.co.il>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/OwnPtr.h>
+#include <AK/RefPtr.h>
+#include <Kernel/Devices/Device.h>
+#include <Kernel/IO.h>
+#include <Kernel/Lock.h>
+#include <Kernel/PCI/Access.h>
+#include <Kernel/PCI/DeviceController.h>
+#include <Kernel/PhysicalAddress.h>
+#include <Kernel/Random.h>
+#include <Kernel/VM/PhysicalPage.h>
+#include <Kernel/WaitQueue.h>
+
+namespace Kernel {
+
+class AsyncBlockDeviceRequest;
+class StorageDevice;
+class StorageController : public RefCounted<StorageController>
+    , public PCI::DeviceController {
+    AK_MAKE_ETERNAL
+public:
+protected:
+    explicit StorageController(PCI::Address address)
+        : PCI::DeviceController(address)
+    {
+    }
+
+    virtual RefPtr<StorageDevice> device(u32 index) = 0;
+    virtual void start_request(const StorageDevice&, AsyncBlockDeviceRequest&) = 0;
+
+protected:
+    virtual bool reset() = 0;
+    virtual bool shutdown() = 0;
+
+    virtual void complete_current_request(AsyncDeviceRequest::RequestResult) = 0;
+};
+}

+ 21 - 44
Kernel/Devices/PATADiskDevice.cpp → Kernel/Storage/StorageDevice.cpp

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * Copyright (c) 2020, Liav A. <liavalb@hotmail.co.il>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -24,51 +24,33 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-//#define PATA_DEVICE_DEBUG
+//#define STORAGE_DEVICE_DEBUG
 
 #include <AK/Memory.h>
 #include <AK/StringView.h>
-#include <Kernel/Devices/PATAChannel.h>
-#include <Kernel/Devices/PATADiskDevice.h>
 #include <Kernel/FileSystem/FileDescription.h>
+#include <Kernel/Storage/StorageDevice.h>
 
 namespace Kernel {
 
-NonnullRefPtr<PATADiskDevice> PATADiskDevice::create(PATAChannel& channel, DriveType type, int major, int minor)
+StorageDevice::StorageDevice(const StorageController& controller, int major, int minor, size_t sector_size, size_t max_addressable_block)
+    : BlockDevice(major, minor, sector_size)
+    , m_storage_controller(controller)
+    , m_max_addressable_block(max_addressable_block)
 {
-    return adopt(*new PATADiskDevice(channel, type, major, minor));
 }
 
-PATADiskDevice::PATADiskDevice(PATAChannel& channel, DriveType type, int major, int minor)
-    : BlockDevice(major, minor, 512)
-    , m_drive_type(type)
-    , m_channel(channel)
+const char* StorageDevice::class_name() const
 {
+    return "StorageDevice";
 }
 
-PATADiskDevice::~PATADiskDevice()
+NonnullRefPtr<StorageController> StorageDevice::controller() const
 {
+    return m_storage_controller;
 }
 
-const char* PATADiskDevice::class_name() const
-{
-    return "PATADiskDevice";
-}
-
-void PATADiskDevice::start_request(AsyncBlockDeviceRequest& request)
-{
-    bool use_dma = !m_channel.m_bus_master_base.is_null() && m_channel.m_dma_enabled.resource();
-    m_channel.start_request(request, use_dma, is_slave());
-}
-
-void PATADiskDevice::set_drive_geometry(u16 cyls, u16 heads, u16 spt)
-{
-    m_cylinders = cyls;
-    m_heads = heads;
-    m_sectors_per_track = spt;
-}
-
-KResultOr<size_t> PATADiskDevice::read(FileDescription&, size_t offset, UserOrKernelBuffer& outbuf, size_t len)
+KResultOr<size_t> StorageDevice::read(FileDescription&, size_t offset, UserOrKernelBuffer& outbuf, size_t len)
 {
     unsigned index = offset / block_size();
     u16 whole_blocks = len / block_size();
@@ -83,8 +65,8 @@ KResultOr<size_t> PATADiskDevice::read(FileDescription&, size_t offset, UserOrKe
         remaining = 0;
     }
 
-#ifdef PATA_DEVICE_DEBUG
-    klog() << "PATADiskDevice::read() index=" << index << " whole_blocks=" << whole_blocks << " remaining=" << remaining;
+#ifdef STORAGE_DEVICE_DEBUG
+    klog() << "StorageDevice::read() index=" << index << " whole_blocks=" << whole_blocks << " remaining=" << remaining;
 #endif
 
     if (whole_blocks > 0) {
@@ -130,12 +112,12 @@ KResultOr<size_t> PATADiskDevice::read(FileDescription&, size_t offset, UserOrKe
     return pos + remaining;
 }
 
-bool PATADiskDevice::can_read(const FileDescription&, size_t offset) const
+bool StorageDevice::can_read(const FileDescription&, size_t offset) const
 {
-    return offset < (m_cylinders * m_heads * m_sectors_per_track * block_size());
+    return offset < (max_addressable_block() * block_size());
 }
 
-KResultOr<size_t> PATADiskDevice::write(FileDescription&, size_t offset, const UserOrKernelBuffer& inbuf, size_t len)
+KResultOr<size_t> StorageDevice::write(FileDescription&, size_t offset, const UserOrKernelBuffer& inbuf, size_t len)
 {
     unsigned index = offset / block_size();
     u16 whole_blocks = len / block_size();
@@ -150,8 +132,8 @@ KResultOr<size_t> PATADiskDevice::write(FileDescription&, size_t offset, const U
         remaining = 0;
     }
 
-#ifdef PATA_DEVICE_DEBUG
-    klog() << "PATADiskDevice::write() index=" << index << " whole_blocks=" << whole_blocks << " remaining=" << remaining;
+#ifdef STORAGE_DEVICE_DEBUG
+    klog() << "StorageDevice::write() index=" << index << " whole_blocks=" << whole_blocks << " remaining=" << remaining;
 #endif
 
     if (whole_blocks > 0) {
@@ -222,14 +204,9 @@ KResultOr<size_t> PATADiskDevice::write(FileDescription&, size_t offset, const U
     return pos + remaining;
 }
 
-bool PATADiskDevice::can_write(const FileDescription&, size_t offset) const
-{
-    return offset < (m_cylinders * m_heads * m_sectors_per_track * block_size());
-}
-
-bool PATADiskDevice::is_slave() const
+bool StorageDevice::can_write(const FileDescription&, size_t offset) const
 {
-    return m_drive_type == DriveType::Slave;
+    return offset < (max_addressable_block() * block_size());
 }
 
 }

+ 66 - 0
Kernel/Storage/StorageDevice.h

@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2020, Liav A. <liavalb@hotmail.co.il>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <Kernel/Devices/BlockDevice.h>
+#include <Kernel/Interrupts/IRQHandler.h>
+#include <Kernel/Lock.h>
+#include <Kernel/Storage/StorageController.h>
+
+namespace Kernel {
+
+class StorageDevice : public BlockDevice {
+    AK_MAKE_ETERNAL
+public:
+    enum class Type : u8 {
+        IDE,
+        NVMe,
+    };
+
+public:
+    virtual Type type() const = 0;
+    virtual size_t max_addressable_block() const { return m_max_addressable_block; }
+
+    NonnullRefPtr<StorageController> controller() const;
+
+    // ^BlockDevice
+    virtual KResultOr<size_t> read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) override;
+    virtual bool can_read(const FileDescription&, size_t) const override;
+    virtual KResultOr<size_t> write(FileDescription&, size_t, const UserOrKernelBuffer&, size_t) override;
+    virtual bool can_write(const FileDescription&, size_t) const override;
+
+protected:
+    StorageDevice(const StorageController&, int, int, size_t, size_t);
+    // ^DiskDevice
+    virtual const char* class_name() const override;
+
+private:
+    NonnullRefPtr<StorageController> m_storage_controller;
+    size_t m_max_addressable_block;
+};
+
+}

+ 11 - 4
Kernel/init.cpp

@@ -40,8 +40,6 @@
 #include <Kernel/Devices/MBRPartitionTable.h>
 #include <Kernel/Devices/MBVGADevice.h>
 #include <Kernel/Devices/NullDevice.h>
-#include <Kernel/Devices/PATAChannel.h>
-#include <Kernel/Devices/PATADiskDevice.h>
 #include <Kernel/Devices/RandomDevice.h>
 #include <Kernel/Devices/SB16.h>
 #include <Kernel/Devices/SerialDevice.h>
@@ -67,6 +65,8 @@
 #include <Kernel/RTC.h>
 #include <Kernel/Random.h>
 #include <Kernel/Scheduler.h>
+#include <Kernel/Storage/IDEController.h>
+#include <Kernel/Storage/PATADiskDevice.h>
 #include <Kernel/TTY/PTYMultiplexer.h>
 #include <Kernel/TTY/VirtualConsole.h>
 #include <Kernel/Tasks/FinalizerTask.h>
@@ -276,8 +276,15 @@ void init_stage2(void*)
         Processor::halt();
     }
 
-    auto pata0 = PATAChannel::create(PATAChannel::ChannelType::Primary, force_pio);
-    NonnullRefPtr<BlockDevice> root_dev = *pata0->master_device();
+    RefPtr<BlockDevice> checked_root_dev;
+    PCI::enumerate([&](const PCI::Address& address, PCI::ID) {
+        if (PCI::get_class(address) == 0x1 && PCI::get_subclass(address) == 0x1) {
+            auto pata0 = IDEController::initialize(address, force_pio);
+            checked_root_dev = *pata0->device(0);
+        }
+    });
+    ASSERT(!checked_root_dev.is_null());
+    NonnullRefPtr<BlockDevice> root_dev = checked_root_dev.release_nonnull();
 
     root = root.substring(strlen("/dev/hda"), root.length() - strlen("/dev/hda"));