/* * Copyright (c) 2020, Liav A. * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include #include #include #include #include namespace Kernel::PCI { enum class HeaderType { Device = 0, Bridge = 1, }; enum class HeaderType0BaseRegister { BAR0 = 0, BAR1, BAR2, BAR3, BAR4, BAR5, }; enum class BARSpaceType { IOSpace, Memory16BitSpace, Memory32BitSpace, Memory64BitSpace, }; enum class RegisterOffset { VENDOR_ID = 0x00, // word DEVICE_ID = 0x02, // word COMMAND = 0x04, // word STATUS = 0x06, // word REVISION_ID = 0x08, // byte PROG_IF = 0x09, // byte SUBCLASS = 0x0a, // byte CLASS = 0x0b, // byte CACHE_LINE_SIZE = 0x0c, // byte LATENCY_TIMER = 0x0d, // byte HEADER_TYPE = 0x0e, // byte BIST = 0x0f, // byte BAR0 = 0x10, // u32 BAR1 = 0x14, // u32 BAR2 = 0x18, // u32 SECONDARY_BUS = 0x19, // byte BAR3 = 0x1C, // u32 BAR4 = 0x20, // u32 BAR5 = 0x24, // u32 SUBSYSTEM_VENDOR_ID = 0x2C, // u16 SUBSYSTEM_ID = 0x2E, // u16 EXPANSION_ROM_POINTER = 0x30, // u32 CAPABILITIES_POINTER = 0x34, // u8 INTERRUPT_LINE = 0x3C, // byte INTERRUPT_PIN = 0x3D, // byte }; enum class Limits { MaxDevicesPerBus = 32, MaxBusesPerDomain = 256, MaxFunctionsPerDevice = 8, }; static constexpr u16 address_port = 0xcf8; static constexpr u16 value_port = 0xcfc; static constexpr size_t mmio_device_space_size = 4096; static constexpr u16 none_value = 0xffff; static constexpr size_t memory_range_per_bus = mmio_device_space_size * to_underlying(Limits::MaxFunctionsPerDevice) * to_underlying(Limits::MaxDevicesPerBus); // Taken from https://pcisig.com/sites/default/files/files/PCI_Code-ID_r_1_11__v24_Jan_2019.pdf enum class ClassID { MassStorage = 0x1, Multimedia = 0x4, Bridge = 0x6, Base = 0x8, }; namespace MassStorage { enum class SubclassID { IDEController = 0x1, SATAController = 0x6, NVMeController = 0x8, }; enum class SATAProgIF { AHCI = 0x1, }; } namespace Multimedia { enum class SubclassID { AudioController = 0x1, HDACompatibleController = 0x3, }; } namespace Bridge { enum class SubclassID { PCI_TO_PCI = 0x4, }; } namespace Base { enum class SubclassID { SDHostController = 0x5, }; } AK_TYPEDEF_DISTINCT_ORDERED_ID(u8, CapabilityID); namespace Capabilities { enum ID { Null = 0x0, MSI = 0x5, VendorSpecific = 0x9, MSIX = 0x11, }; } struct HardwareID { u16 vendor_id { 0 }; u16 device_id { 0 }; bool is_null() const { return !vendor_id && !device_id; } bool operator==(HardwareID const& other) const { return vendor_id == other.vendor_id && device_id == other.device_id; } bool operator!=(HardwareID const& other) const { return vendor_id != other.vendor_id || device_id != other.device_id; } }; class Domain { public: Domain() = delete; Domain(u32 domain_number, u8 start_bus, u8 end_bus) : m_domain_number(domain_number) , m_start_bus(start_bus) , m_end_bus(end_bus) { } u8 start_bus() const { return m_start_bus; } u8 end_bus() const { return m_end_bus; } u32 domain_number() const { return m_domain_number; } private: u32 m_domain_number; u8 m_start_bus; u8 m_end_bus; }; struct Address { public: Address() = default; Address(u32 domain) : m_domain(domain) , m_bus(0) , m_device(0) , m_function(0) { } Address(u32 domain, u8 bus, u8 device, u8 function) : m_domain(domain) , m_bus(bus) , m_device(device) , m_function(function) { } Address(Address const& address) = default; bool is_null() const { return !m_bus && !m_device && !m_function; } operator bool() const { return !is_null(); } // Disable default implementations that would use surprising integer promotion. bool operator<=(Address const&) const = delete; bool operator>=(Address const&) const = delete; bool operator<(Address const&) const = delete; bool operator>(Address const&) const = delete; bool operator==(Address const& other) const { if (this == &other) return true; return m_domain == other.m_domain && m_bus == other.m_bus && m_device == other.m_device && m_function == other.m_function; } bool operator!=(Address const& other) const { return !(*this == other); } u32 domain() const { return m_domain; } u8 bus() const { return m_bus; } u8 device() const { return m_device; } u8 function() const { return m_function; } private: u32 m_domain { 0 }; u8 m_bus { 0 }; u8 m_device { 0 }; u8 m_function { 0 }; }; class Capability { public: Capability(Address address, u8 id, u8 ptr) : m_address(address) , m_id(id) , m_ptr(ptr) { } CapabilityID id() const { return m_id; } u8 read8(size_t offset) const; u16 read16(size_t offset) const; u32 read32(size_t offset) const; private: const Address m_address; const CapabilityID m_id; const u8 m_ptr; }; AK_TYPEDEF_DISTINCT_ORDERED_ID(u8, ClassCode); AK_TYPEDEF_DISTINCT_ORDERED_ID(u8, SubclassCode); AK_TYPEDEF_DISTINCT_ORDERED_ID(u8, ProgrammingInterface); AK_TYPEDEF_DISTINCT_ORDERED_ID(u8, RevisionID); AK_TYPEDEF_DISTINCT_ORDERED_ID(u16, SubsystemID); AK_TYPEDEF_DISTINCT_ORDERED_ID(u16, SubsystemVendorID); AK_TYPEDEF_DISTINCT_ORDERED_ID(u8, InterruptLine); AK_TYPEDEF_DISTINCT_ORDERED_ID(u8, InterruptPin); class Access; class EnumerableDeviceIdentifier { public: EnumerableDeviceIdentifier(Address address, HardwareID hardware_id, RevisionID revision_id, ClassCode class_code, SubclassCode subclass_code, ProgrammingInterface prog_if, SubsystemID subsystem_id, SubsystemVendorID subsystem_vendor_id, InterruptLine interrupt_line, InterruptPin interrupt_pin, Vector const& capabilities) : m_address(address) , m_hardware_id(hardware_id) , m_revision_id(revision_id) , m_class_code(class_code) , m_subclass_code(subclass_code) , m_prog_if(prog_if) , m_subsystem_id(subsystem_id) , m_subsystem_vendor_id(subsystem_vendor_id) , m_interrupt_line(interrupt_line) , m_interrupt_pin(interrupt_pin) , m_capabilities(capabilities) { if constexpr (PCI_DEBUG) { for (auto const& capability : capabilities) dbgln("{} has capability {}", address, capability.id()); } } Vector const& capabilities() const { return m_capabilities; } HardwareID const& hardware_id() const { return m_hardware_id; } Address const& address() const { return m_address; } RevisionID revision_id() const { return m_revision_id; } ClassCode class_code() const { return m_class_code; } SubclassCode subclass_code() const { return m_subclass_code; } ProgrammingInterface prog_if() const { return m_prog_if; } SubsystemID subsystem_id() const { return m_subsystem_id; } SubsystemVendorID subsystem_vendor_id() const { return m_subsystem_vendor_id; } InterruptLine interrupt_line() const { return m_interrupt_line; } InterruptPin interrupt_pin() const { return m_interrupt_pin; } void apply_subclass_code_change(Badge, SubclassCode new_subclass) { m_subclass_code = new_subclass; } void apply_prog_if_change(Badge, ProgrammingInterface new_progif) { m_prog_if = new_progif; } protected: Address m_address; HardwareID m_hardware_id; RevisionID m_revision_id; ClassCode m_class_code; SubclassCode m_subclass_code; ProgrammingInterface m_prog_if; SubsystemID m_subsystem_id; SubsystemVendorID m_subsystem_vendor_id; InterruptLine m_interrupt_line; InterruptPin m_interrupt_pin; Vector m_capabilities; }; class DeviceIdentifier : public RefCounted , public EnumerableDeviceIdentifier { AK_MAKE_NONCOPYABLE(DeviceIdentifier); public: static ErrorOr> from_enumerable_identifier(EnumerableDeviceIdentifier const& other_identifier); Spinlock& operation_lock() { return m_operation_lock; } Spinlock& operation_lock() const { return m_operation_lock; } virtual ~DeviceIdentifier() = default; private: DeviceIdentifier(EnumerableDeviceIdentifier const& other_identifier) : EnumerableDeviceIdentifier(other_identifier.address(), other_identifier.hardware_id(), other_identifier.revision_id(), other_identifier.class_code(), other_identifier.subclass_code(), other_identifier.prog_if(), other_identifier.subsystem_id(), other_identifier.subsystem_vendor_id(), other_identifier.interrupt_line(), other_identifier.interrupt_pin(), other_identifier.capabilities()) { } mutable Spinlock m_operation_lock; }; class Domain; class Device; } template<> struct AK::Formatter : Formatter { ErrorOr format(FormatBuilder& builder, Kernel::PCI::Address value) { return Formatter::format( builder, "PCI [{:04x}:{:02x}:{:02x}:{:02x}]"sv, value.domain(), value.bus(), value.device(), value.function()); } }; template<> struct AK::Formatter : Formatter { ErrorOr format(FormatBuilder& builder, Kernel::PCI::HardwareID value) { return Formatter::format( builder, "PCI::HardwareID [{:04x}:{:04x}]"sv, value.vendor_id, value.device_id); } };