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

This commit is contained in:
Liav A 2022-01-15 09:17:07 +02:00 committed by Andreas Kling
parent df73e8b46b
commit eb9c8f3895
Notes: sideshowbarker 2024-07-17 20:26:51 +09:00
7 changed files with 161 additions and 2 deletions

View File

@ -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();

View File

@ -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);

View File

@ -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

View File

@ -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)
{
}
}

View File

@ -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;
};
}

View File

@ -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

View File

@ -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));