diff options
author | Liav A <liavalb@gmail.com> | 2022-01-15 09:17:07 +0200 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2022-01-23 01:12:55 +0100 |
commit | eb9c8f389503cad155b4b025baf7ace7389c7b15 (patch) | |
tree | f8e2843bc38cf2369cd571e728b3898b043ff78b | |
parent | df73e8b46b3b5634320f2f2075954d5d2dad682c (diff) | |
download | serenity-eb9c8f389503cad155b4b025baf7ace7389c7b15.zip |
Kernel/PCI: Add basic support for the VMD PCI bridge device
-rw-r--r-- | Kernel/Bus/PCI/Access.cpp | 16 | ||||
-rw-r--r-- | Kernel/Bus/PCI/Access.h | 2 | ||||
-rw-r--r-- | Kernel/Bus/PCI/Controller/MemoryBackedHostBridge.h | 4 | ||||
-rw-r--r-- | Kernel/Bus/PCI/Controller/VolumeManagementDevice.cpp | 86 | ||||
-rw-r--r-- | Kernel/Bus/PCI/Controller/VolumeManagementDevice.h | 35 | ||||
-rw-r--r-- | Kernel/CMakeLists.txt | 1 | ||||
-rw-r--r-- | Kernel/Storage/StorageManagement.cpp | 19 |
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)); |