summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLiav A <liavalb@gmail.com>2022-01-15 09:17:07 +0200
committerAndreas Kling <kling@serenityos.org>2022-01-23 01:12:55 +0100
commiteb9c8f389503cad155b4b025baf7ace7389c7b15 (patch)
treef8e2843bc38cf2369cd571e728b3898b043ff78b
parentdf73e8b46b3b5634320f2f2075954d5d2dad682c (diff)
downloadserenity-eb9c8f389503cad155b4b025baf7ace7389c7b15.zip
Kernel/PCI: Add basic support for the VMD PCI bridge device
-rw-r--r--Kernel/Bus/PCI/Access.cpp16
-rw-r--r--Kernel/Bus/PCI/Access.h2
-rw-r--r--Kernel/Bus/PCI/Controller/MemoryBackedHostBridge.h4
-rw-r--r--Kernel/Bus/PCI/Controller/VolumeManagementDevice.cpp86
-rw-r--r--Kernel/Bus/PCI/Controller/VolumeManagementDevice.h35
-rw-r--r--Kernel/CMakeLists.txt1
-rw-r--r--Kernel/Storage/StorageManagement.cpp19
7 files changed, 161 insertions, 2 deletions
diff --git a/Kernel/Bus/PCI/Access.cpp b/Kernel/Bus/PCI/Access.cpp
index c9fb04e81f..01f60168a0 100644
--- a/Kernel/Bus/PCI/Access.cpp
+++ b/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();
diff --git a/Kernel/Bus/PCI/Access.h b/Kernel/Bus/PCI/Access.h
index a247572a05..0c9be8c064 100644
--- a/Kernel/Bus/PCI/Access.h
+++ b/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);
diff --git a/Kernel/Bus/PCI/Controller/MemoryBackedHostBridge.h b/Kernel/Bus/PCI/Controller/MemoryBackedHostBridge.h
index 68968554ea..1d0edebd7e 100644
--- a/Kernel/Bus/PCI/Controller/MemoryBackedHostBridge.h
+++ b/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
diff --git a/Kernel/Bus/PCI/Controller/VolumeManagementDevice.cpp b/Kernel/Bus/PCI/Controller/VolumeManagementDevice.cpp
new file mode 100644
index 0000000000..cc42df3148
--- /dev/null
+++ b/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)
+{
+}
+
+}
diff --git a/Kernel/Bus/PCI/Controller/VolumeManagementDevice.h b/Kernel/Bus/PCI/Controller/VolumeManagementDevice.h
new file mode 100644
index 0000000000..aa5a1e2849
--- /dev/null
+++ b/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;
+};
+
+}
diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt
index 8b27969e0d..b887683496 100644
--- a/Kernel/CMakeLists.txt
+++ b/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
diff --git a/Kernel/Storage/StorageManagement.cpp b/Kernel/Storage/StorageManagement.cpp
index d7a1692756..6bc2eb9d84 100644
--- a/Kernel/Storage/StorageManagement.cpp
+++ b/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));