瀏覽代碼

Kernel/PCI: Add basic support for the VMD PCI bridge device

Liav A 3 年之前
父節點
當前提交
eb9c8f3895

+ 16 - 0
Kernel/Bus/PCI/Access.cpp

@@ -108,6 +108,22 @@ UNMAP_AFTER_INIT bool Access::initialize_for_one_pci_domain()
     return true;
 }
 
+void Access::add_host_controller_and_enumerate_attached_devices(NonnullOwnPtr<HostController> controller, Function<void(DeviceIdentifier const&)> callback)
+{
+    MutexLocker locker(m_access_lock);
+    SpinlockLocker scan_locker(m_scan_lock);
+    auto domain_number = controller->domain_number();
+
+    VERIFY(!m_host_controllers.contains(domain_number));
+    // Note: We need to register the new controller as soon as possible, and
+    // definitely before enumerating devices behing that.
+    m_host_controllers.set(domain_number, move(controller));
+    m_host_controllers.get(domain_number).value()->enumerate_attached_devices([&](DeviceIdentifier const& device_identifier) -> void {
+        m_device_identifiers.append(device_identifier);
+        callback(device_identifier);
+    });
+}
+
 UNMAP_AFTER_INIT void Access::add_host_controller(NonnullOwnPtr<HostController> controller)
 {
     auto domain_number = controller->domain_number();

+ 2 - 0
Kernel/Bus/PCI/Access.h

@@ -38,6 +38,8 @@ public:
     Spinlock const& scan_lock() const { return m_scan_lock; }
     Mutex const& access_lock() const { return m_access_lock; }
 
+    void add_host_controller_and_enumerate_attached_devices(NonnullOwnPtr<HostController>, Function<void(DeviceIdentifier const&)> callback);
+
 private:
     u8 read8_field(Address address, RegisterOffset field);
     u16 read16_field(Address address, RegisterOffset field);

+ 2 - 2
Kernel/Bus/PCI/Controller/MemoryBackedHostBridge.h

@@ -13,7 +13,7 @@
 
 namespace Kernel::PCI {
 
-class MemoryBackedHostBridge final : public HostBridge {
+class MemoryBackedHostBridge : public HostBridge {
 public:
     static NonnullOwnPtr<MemoryBackedHostBridge> must_create(Domain const&, PhysicalAddress);
 
@@ -25,7 +25,7 @@ public:
     virtual u16 read16_field(BusNumber, DeviceNumber, FunctionNumber, u32 field) override;
     virtual u32 read32_field(BusNumber, DeviceNumber, FunctionNumber, u32 field) override;
 
-private:
+protected:
     MemoryBackedHostBridge(PCI::Domain const&, PhysicalAddress);
 
     // Memory-mapped access operations

+ 86 - 0
Kernel/Bus/PCI/Controller/VolumeManagementDevice.cpp

@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <AK/ByteReader.h>
+#include <Kernel/Arch/x86/IO.h>
+#include <Kernel/Bus/PCI/API.h>
+#include <Kernel/Bus/PCI/Access.h>
+#include <Kernel/Bus/PCI/Controller/VolumeManagementDevice.h>
+
+namespace Kernel::PCI {
+
+static Atomic<u32> s_vmd_pci_domain_number = 0x10000;
+
+NonnullOwnPtr<VolumeManagementDevice> VolumeManagementDevice::must_create(PCI::DeviceIdentifier const& device_identifier)
+{
+    u8 start_bus = 0;
+    switch ((PCI::read16(device_identifier.address(), static_cast<PCI::RegisterOffset>(0x44)) >> 8) & 0x3) {
+    case 0:
+        break;
+    case 1:
+        start_bus = 128;
+        break;
+    case 2:
+        start_bus = 224;
+        break;
+    default:
+        dbgln("VMD @ {}: Unknown bus offset option was set to {}", device_identifier.address(),
+            ((PCI::read16(device_identifier.address(), static_cast<PCI::RegisterOffset>(0x44)) >> 8) & 0x3));
+        VERIFY_NOT_REACHED();
+    }
+
+    // FIXME: The end bus might not be 255, so we actually need to check it with the
+    // resource size of BAR0.
+    dbgln("VMD Host bridge @ {}: Start bus at {}, end bus {}", device_identifier.address(), start_bus, 0xff);
+    PCI::Domain domain { s_vmd_pci_domain_number++, start_bus, 0xff };
+    auto start_address = PhysicalAddress(PCI::get_BAR0(device_identifier.address())).page_base();
+    return adopt_own_if_nonnull(new (nothrow) VolumeManagementDevice(domain, start_address)).release_nonnull();
+}
+
+void VolumeManagementDevice::write8_field(BusNumber bus, DeviceNumber device, FunctionNumber function, u32 field, u8 value)
+{
+    SpinlockLocker locker(m_config_lock);
+    // Note: We must write then read to ensure completion before returning.
+    MemoryBackedHostBridge::write8_field(bus, device, function, field, value);
+    MemoryBackedHostBridge::read8_field(bus, device, function, field);
+}
+void VolumeManagementDevice::write16_field(BusNumber bus, DeviceNumber device, FunctionNumber function, u32 field, u16 value)
+{
+    SpinlockLocker locker(m_config_lock);
+    // Note: We must write then read to ensure completion before returning.
+    MemoryBackedHostBridge::write16_field(bus, device, function, field, value);
+    MemoryBackedHostBridge::read16_field(bus, device, function, field);
+}
+void VolumeManagementDevice::write32_field(BusNumber bus, DeviceNumber device, FunctionNumber function, u32 field, u32 value)
+{
+    SpinlockLocker locker(m_config_lock);
+    // Note: We must write then read to ensure completion before returning.
+    MemoryBackedHostBridge::write32_field(bus, device, function, field, value);
+    MemoryBackedHostBridge::read32_field(bus, device, function, field);
+}
+
+u8 VolumeManagementDevice::read8_field(BusNumber bus, DeviceNumber device, FunctionNumber function, u32 field)
+{
+    SpinlockLocker locker(m_config_lock);
+    return MemoryBackedHostBridge::read8_field(bus, device, function, field);
+}
+u16 VolumeManagementDevice::read16_field(BusNumber bus, DeviceNumber device, FunctionNumber function, u32 field)
+{
+    SpinlockLocker locker(m_config_lock);
+    return MemoryBackedHostBridge::read16_field(bus, device, function, field);
+}
+u32 VolumeManagementDevice::read32_field(BusNumber bus, DeviceNumber device, FunctionNumber function, u32 field)
+{
+    SpinlockLocker locker(m_config_lock);
+    return MemoryBackedHostBridge::read32_field(bus, device, function, field);
+}
+
+VolumeManagementDevice::VolumeManagementDevice(PCI::Domain const& domain, PhysicalAddress start_address)
+    : MemoryBackedHostBridge(domain, start_address)
+{
+}
+
+}

+ 35 - 0
Kernel/Bus/PCI/Controller/VolumeManagementDevice.h

@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/Bitmap.h>
+#include <AK/Vector.h>
+#include <Kernel/Bus/PCI/Controller/MemoryBackedHostBridge.h>
+#include <Kernel/Locking/Spinlock.h>
+
+namespace Kernel::PCI {
+
+class VolumeManagementDevice final : public MemoryBackedHostBridge {
+public:
+    static NonnullOwnPtr<VolumeManagementDevice> must_create(PCI::DeviceIdentifier const& device_identifier);
+
+private:
+    VolumeManagementDevice(PCI::Domain const&, PhysicalAddress);
+
+    virtual void write8_field(BusNumber, DeviceNumber, FunctionNumber, u32 field, u8 value) override;
+    virtual void write16_field(BusNumber, DeviceNumber, FunctionNumber, u32 field, u16 value) override;
+    virtual void write32_field(BusNumber, DeviceNumber, FunctionNumber, u32 field, u32 value) override;
+    virtual u8 read8_field(BusNumber, DeviceNumber, FunctionNumber, u32 field) override;
+    virtual u16 read16_field(BusNumber, DeviceNumber, FunctionNumber, u32 field) override;
+    virtual u32 read32_field(BusNumber, DeviceNumber, FunctionNumber, u32 field) override;
+
+    // Note: All read and writes must be done with a spinlock because
+    // Linux says that CPU might deadlock otherwise if access is not serialized.
+    Spinlock m_config_lock;
+};
+
+}

+ 1 - 0
Kernel/CMakeLists.txt

@@ -21,6 +21,7 @@ set(KERNEL_SOURCES
     AddressSanitizer.cpp
     Bus/PCI/Controller/HostBridge.cpp
     Bus/PCI/Controller/MemoryBackedHostBridge.cpp
+    Bus/PCI/Controller/VolumeManagementDevice.cpp
     Bus/PCI/Access.cpp
     Bus/PCI/API.cpp
     Bus/PCI/Device.cpp

+ 19 - 0
Kernel/Storage/StorageManagement.cpp

@@ -9,6 +9,7 @@
 #include <AK/UUID.h>
 #include <Kernel/Bus/PCI/API.h>
 #include <Kernel/Bus/PCI/Access.h>
+#include <Kernel/Bus/PCI/Controller/VolumeManagementDevice.h>
 #include <Kernel/CommandLine.h>
 #include <Kernel/Devices/BlockDevice.h>
 #include <Kernel/FileSystem/Ext2FileSystem.h>
@@ -55,6 +56,24 @@ UNMAP_AFTER_INIT void StorageManagement::enumerate_controllers(bool force_pio)
                 return;
             }
 
+            {
+                static constexpr PCI::HardwareID vmd_device = { 0x8086, 0x9a0b };
+                if (device_identifier.hardware_id() == vmd_device) {
+                    auto controller = PCI::VolumeManagementDevice::must_create(device_identifier);
+                    PCI::Access::the().add_host_controller_and_enumerate_attached_devices(move(controller), [this](PCI::DeviceIdentifier const& device_identifier) -> void {
+                        auto subclass_code = static_cast<SubclassID>(device_identifier.subclass_code().value());
+                        if (subclass_code == SubclassID::NVMeController) {
+                            auto controller = NVMeController::try_initialize(device_identifier);
+                            if (controller.is_error()) {
+                                dmesgln("Unable to initialize NVMe controller: {}", controller.error());
+                            } else {
+                                m_controllers.append(controller.release_value());
+                            }
+                        }
+                    });
+                }
+            }
+
             auto subclass_code = static_cast<SubclassID>(device_identifier.subclass_code().value());
             if (subclass_code == SubclassID::IDEController && kernel_command_line().is_ide_enabled()) {
                 m_controllers.append(IDEController::initialize(device_identifier, force_pio));