diff options
author | Liav A <liavalb@gmail.com> | 2020-10-31 22:24:01 +0200 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-11-01 10:19:17 +0100 |
commit | 6131048a5f28439c2f1bba4773b4f5ba0f92ad45 (patch) | |
tree | 31d456d38574c4a15f72a0b50cb2ada3adba98ed | |
parent | 0aeef47abd823c603ed78d8ec2b6d9947209dcb0 (diff) | |
download | serenity-6131048a5f28439c2f1bba4773b4f5ba0f92ad45.zip |
Kernel: Map PCI devices only once during boot
Instead of mapping a 4KB region to access device configuration space
each time we call one of the PCI helpers, just map them once during
the boot process.
Then, if we request to access one of those devices, we can ask the
PCI subsystem to give us the virtual address where the device's
configuration space is mapped.
-rw-r--r-- | Kernel/PCI/Access.cpp | 37 | ||||
-rw-r--r-- | Kernel/PCI/Access.h | 5 | ||||
-rw-r--r-- | Kernel/PCI/MMIOAccess.cpp | 95 | ||||
-rw-r--r-- | Kernel/PCI/MMIOAccess.h | 18 |
4 files changed, 95 insertions, 60 deletions
diff --git a/Kernel/PCI/Access.cpp b/Kernel/PCI/Access.cpp index a480cb8f2e..74efa8fbd4 100644 --- a/Kernel/PCI/Access.cpp +++ b/Kernel/PCI/Access.cpp @@ -24,6 +24,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include <Kernel/IO.h> #include <Kernel/PCI/Access.h> #include <Kernel/PCI/IOAccess.h> @@ -57,18 +58,36 @@ Access::Access() s_access = this; } -static u16 read_type(Address address) +u8 Access::early_read8_field(Address address, u32 field) { - return (read8(address, PCI_CLASS) << 8u) | read8(address, PCI_SUBCLASS); + IO::out32(PCI_ADDRESS_PORT, address.io_address_for_field(field)); + return IO::in8(PCI_VALUE_PORT + (field & 3)); +} + +u16 Access::early_read16_field(Address address, u32 field) +{ + IO::out32(PCI_ADDRESS_PORT, address.io_address_for_field(field)); + return IO::in16(PCI_VALUE_PORT + (field & 2)); +} + +u32 Access::early_read32_field(Address address, u32 field) +{ + IO::out32(PCI_ADDRESS_PORT, address.io_address_for_field(field)); + return IO::in32(PCI_VALUE_PORT); +} + +u16 Access::early_read_type(Address address) +{ + return (early_read8_field(address, PCI_CLASS) << 8u) | early_read8_field(address, PCI_SUBCLASS); } void Access::enumerate_functions(int type, u8 bus, u8 slot, u8 function, Function<void(Address, ID)>& callback) { Address address(0, bus, slot, function); - if (type == -1 || type == read_type(address)) - callback(address, { read16_field(address, PCI_VENDOR_ID), read16_field(address, PCI_DEVICE_ID) }); - if (read_type(address) == PCI_TYPE_BRIDGE) { - u8 secondary_bus = read8_field(address, PCI_SECONDARY_BUS); + if (type == -1 || type == early_read_type(address)) + callback(address, { early_read16_field(address, PCI_VENDOR_ID), early_read16_field(address, PCI_DEVICE_ID) }); + if (early_read_type(address) == PCI_TYPE_BRIDGE) { + u8 secondary_bus = early_read8_field(address, PCI_SECONDARY_BUS); #ifdef PCI_DEBUG klog() << "PCI: Found secondary bus: " << secondary_bus; #endif @@ -80,14 +99,14 @@ void Access::enumerate_functions(int type, u8 bus, u8 slot, u8 function, Functio void Access::enumerate_slot(int type, u8 bus, u8 slot, Function<void(Address, ID)>& callback) { Address address(0, bus, slot, 0); - if (read16_field(address, PCI_VENDOR_ID) == PCI_NONE) + if (early_read16_field(address, PCI_VENDOR_ID) == PCI_NONE) return; enumerate_functions(type, bus, slot, 0, callback); - if (!(read8_field(address, PCI_HEADER_TYPE) & 0x80)) + if (!(early_read8_field(address, PCI_HEADER_TYPE) & 0x80)) return; for (u8 function = 1; function < 8; ++function) { Address address(0, bus, slot, function); - if (read16_field(address, PCI_VENDOR_ID) != PCI_NONE) + if (early_read16_field(address, PCI_VENDOR_ID) != PCI_NONE) enumerate_functions(type, bus, slot, function, callback); } } diff --git a/Kernel/PCI/Access.h b/Kernel/PCI/Access.h index 47375f8f4a..4b6ade98d2 100644 --- a/Kernel/PCI/Access.h +++ b/Kernel/PCI/Access.h @@ -63,6 +63,11 @@ public: protected: virtual void enumerate_hardware(Function<void(Address, ID)>) = 0; + u8 early_read8_field(Address address, u32 field); + u16 early_read16_field(Address address, u32 field); + u32 early_read32_field(Address address, u32 field); + u16 early_read_type(Address address); + Access(); Vector<PhysicalID> m_physical_ids; }; diff --git a/Kernel/PCI/MMIOAccess.cpp b/Kernel/PCI/MMIOAccess.cpp index 9ade94ba4e..8fb27e6ae5 100644 --- a/Kernel/PCI/MMIOAccess.cpp +++ b/Kernel/PCI/MMIOAccess.cpp @@ -35,10 +35,10 @@ namespace PCI { class MMIOSegment { public: MMIOSegment(PhysicalAddress, u8, u8); - u8 get_start_bus(); - u8 get_end_bus(); - size_t get_size(); - PhysicalAddress get_paddr(); + u8 get_start_bus() const; + u8 get_end_bus() const; + size_t get_size() const; + PhysicalAddress get_paddr() const; private: PhysicalAddress m_base_addr; @@ -48,6 +48,17 @@ private: #define PCI_MMIO_CONFIG_SPACE_SIZE 4096 +DeviceConfigurationSpaceMapping::DeviceConfigurationSpaceMapping(Address device_address, const MMIOSegment& mmio_segment) + : m_device_address(device_address) + , m_mapped_region(MM.allocate_kernel_region(PAGE_ROUND_UP(PCI_MMIO_CONFIG_SPACE_SIZE), "PCI MMIO Device Access", Region::Access::Read | Region::Access::Write).release_nonnull()) +{ + PhysicalAddress segment_lower_addr = mmio_segment.get_paddr(); + PhysicalAddress device_physical_mmio_space = segment_lower_addr.offset( + PCI_MMIO_CONFIG_SPACE_SIZE * m_device_address.function() + (PCI_MMIO_CONFIG_SPACE_SIZE * PCI_MAX_FUNCTIONS_PER_DEVICE) * m_device_address.slot() + (PCI_MMIO_CONFIG_SPACE_SIZE * PCI_MAX_FUNCTIONS_PER_DEVICE * PCI_MAX_DEVICES_PER_BUS) * (m_device_address.bus() - mmio_segment.get_start_bus())); + m_mapped_region->physical_page_slot(0) = PhysicalPage::create(device_physical_mmio_space, false, false); + m_mapped_region->remap(); +} + uint32_t MMIOAccess::segment_count() const { return m_segments.size(); @@ -75,10 +86,8 @@ void MMIOAccess::initialize(PhysicalAddress mcfg) MMIOAccess::MMIOAccess(PhysicalAddress p_mcfg) : m_mcfg(p_mcfg) - , m_mapped_address(ChangeableAddress(0xFFFF, 0xFF, 0xFF, 0xFF)) { klog() << "PCI: Using MMIO for PCI configuration space access"; - m_mmio_window_region = MM.allocate_kernel_region(PAGE_ROUND_UP(PCI_MMIO_CONFIG_SPACE_SIZE), "PCI MMIO", Region::Access::Read | Region::Access::Write); auto checkup_region = MM.allocate_kernel_region(p_mcfg.page_base(), (PAGE_SIZE * 2), "PCI MCFG Checkup", Region::Access::Read | Region::Access::Write); #ifdef PCI_DEBUG @@ -112,38 +121,34 @@ MMIOAccess::MMIOAccess(PhysicalAddress p_mcfg) InterruptDisabler disabler; -#ifdef PCI_DEBUG - dbg() << "PCI: mapped address (" << String::format("%w", m_mapped_address.seg()) << ":" << String::format("%b", m_mapped_address.bus()) << ":" << String::format("%b", m_mapped_address.slot()) << "." << String::format("%b", m_mapped_address.function()) << ")"; -#endif - map_device(Address(0, 0, 0, 0)); -#ifdef PCI_DEBUG - dbg() << "PCI: Default mapped address (" << String::format("%w", m_mapped_address.seg()) << ":" << String::format("%b", m_mapped_address.bus()) << ":" << String::format("%b", m_mapped_address.slot()) << "." << String::format("%b", m_mapped_address.function()) << ")"; -#endif - enumerate_hardware([&](const Address& address, ID id) { m_physical_ids.append({ address, id }); + m_mapped_device_regions.append(make<DeviceConfigurationSpaceMapping>(address, m_segments.get(address.seg()).value())); +#ifdef PCI_DEBUG + dbg() << "PCI: Mapping device @ pci (" << String::format("%w", address.seg()) << ":" << String::format("%b", address.bus()) << ":" << String::format("%b", address.slot()) << "." << String::format("%b", address.function()) << ")" + << " " << m_mapped_device_regions.last().vaddr() << " " << m_mapped_device_regions.last().paddr(); +#endif }); } -void MMIOAccess::map_device(Address address) +Optional<VirtualAddress> MMIOAccess::get_device_configuration_space(Address address) { - if (m_mapped_address == address) - return; - // FIXME: Map and put some lock! - ASSERT_INTERRUPTS_DISABLED(); - auto segment = m_segments.get(address.seg()); - ASSERT(segment.has_value()); - PhysicalAddress segment_lower_addr = segment.value().get_paddr(); - PhysicalAddress device_physical_mmio_space = segment_lower_addr.offset( - PCI_MMIO_CONFIG_SPACE_SIZE * address.function() + (PCI_MMIO_CONFIG_SPACE_SIZE * PCI_MAX_FUNCTIONS_PER_DEVICE) * address.slot() + (PCI_MMIO_CONFIG_SPACE_SIZE * PCI_MAX_FUNCTIONS_PER_DEVICE * PCI_MAX_DEVICES_PER_BUS) * (address.bus() - segment.value().get_start_bus())); - + for (auto& mapping : m_mapped_device_regions) { + auto checked_address = mapping.address(); #ifdef PCI_DEBUG - dbg() << "PCI: Mapping device @ pci (" << String::format("%w", address.seg()) << ":" << String::format("%b", address.bus()) << ":" << String::format("%b", address.slot()) << "." << String::format("%b", address.function()) << ")" - << " V 0x" << String::format("%x", m_mmio_window_region->vaddr().get()) << " P 0x" << String::format("%x", device_physical_mmio_space.get()); + dbg() << "PCI Device Configuration Space Mapping: Check if " << checked_address << " was requested"; #endif - m_mmio_window_region->physical_page_slot(0) = PhysicalPage::create(device_physical_mmio_space, false, false); - m_mmio_window_region->remap(); - m_mapped_address = address; + if (address.seg() == checked_address.seg() + && address.bus() == checked_address.bus() + && address.slot() == checked_address.slot() + && address.function() == checked_address.function()) { +#ifdef PCI_DEBUG + dbg() << "PCI Device Configuration Space Mapping: Found " << checked_address; +#endif + return mapping.vaddr(); + } + } + return {}; } u8 MMIOAccess::read8_field(Address address, u32 field) @@ -153,8 +158,7 @@ u8 MMIOAccess::read8_field(Address address, u32 field) #ifdef PCI_DEBUG dbg() << "PCI: Reading field " << field << ", Address(" << String::format("%w", address.seg()) << ":" << String::format("%b", address.bus()) << ":" << String::format("%b", address.slot()) << "." << String::format("%b", address.function()) << ")"; #endif - map_device(address); - return *((u8*)(m_mmio_window_region->vaddr().get() + (field & 0xfff))); + return *((u8*)(get_device_configuration_space(address).value().get() + (field & 0xfff))); } u16 MMIOAccess::read16_field(Address address, u32 field) @@ -164,8 +168,7 @@ u16 MMIOAccess::read16_field(Address address, u32 field) #ifdef PCI_DEBUG dbg() << "PCI: Reading field " << field << ", Address(" << String::format("%w", address.seg()) << ":" << String::format("%b", address.bus()) << ":" << String::format("%b", address.slot()) << "." << String::format("%b", address.function()) << ")"; #endif - map_device(address); - return *((u16*)(m_mmio_window_region->vaddr().get() + (field & 0xfff))); + return *((u16*)(get_device_configuration_space(address).value().get() + (field & 0xfff))); } u32 MMIOAccess::read32_field(Address address, u32 field) @@ -175,8 +178,7 @@ u32 MMIOAccess::read32_field(Address address, u32 field) #ifdef PCI_DEBUG dbg() << "PCI: Reading field " << field << ", Address(" << String::format("%w", address.seg()) << ":" << String::format("%b", address.bus()) << ":" << String::format("%b", address.slot()) << "." << String::format("%b", address.function()) << ")"; #endif - map_device(address); - return *((u32*)(m_mmio_window_region->vaddr().get() + (field & 0xfff))); + return *((u32*)(get_device_configuration_space(address).value().get() + (field & 0xfff))); } void MMIOAccess::write8_field(Address address, u32 field, u8 value) @@ -186,8 +188,7 @@ void MMIOAccess::write8_field(Address address, u32 field, u8 value) #ifdef PCI_DEBUG dbg() << "PCI: Writing to field " << field << ", Address(" << String::format("%w", address.seg()) << ":" << String::format("%b", address.bus()) << ":" << String::format("%b", address.slot()) << "." << String::format("%b", address.function()) << ") value 0x" << String::format("%x", value); #endif - map_device(address); - *((u8*)(m_mmio_window_region->vaddr().get() + (field & 0xfff))) = value; + *((u8*)(get_device_configuration_space(address).value().get() + (field & 0xfff))) = value; } void MMIOAccess::write16_field(Address address, u32 field, u16 value) { @@ -196,8 +197,7 @@ void MMIOAccess::write16_field(Address address, u32 field, u16 value) #ifdef PCI_DEBUG dbg() << "PCI: Writing to field " << field << ", Address(" << String::format("%w", address.seg()) << ":" << String::format("%b", address.bus()) << ":" << String::format("%b", address.slot()) << "." << String::format("%b", address.function()) << ") value 0x" << String::format("%x", value); #endif - map_device(address); - *((u16*)(m_mmio_window_region->vaddr().get() + (field & 0xfff))) = value; + *((u16*)(get_device_configuration_space(address).value().get() + (field & 0xfff))) = value; } void MMIOAccess::write32_field(Address address, u32 field, u32 value) { @@ -206,8 +206,7 @@ void MMIOAccess::write32_field(Address address, u32 field, u32 value) #ifdef PCI_DEBUG dbg() << "PCI: Writing to field " << field << ", Address(" << String::format("%w", address.seg()) << ":" << String::format("%b", address.bus()) << ":" << String::format("%b", address.slot()) << "." << String::format("%b", address.function()) << ") value 0x" << String::format("%x", value); #endif - map_device(address); - *((u32*)(m_mmio_window_region->vaddr().get() + (field & 0xfff))) = value; + *((u32*)(get_device_configuration_space(address).value().get() + (field & 0xfff))) = value; } void MMIOAccess::enumerate_hardware(Function<void(Address, ID)> callback) @@ -217,14 +216,14 @@ void MMIOAccess::enumerate_hardware(Function<void(Address, ID)> callback) dbg() << "PCI: Enumerating Memory mapped IO segment " << seg; #endif // Single PCI host controller. - if ((read8_field(Address(seg), PCI_HEADER_TYPE) & 0x80) == 0) { + if ((early_read8_field(Address(seg), PCI_HEADER_TYPE) & 0x80) == 0) { enumerate_bus(-1, 0, callback); return; } // Multiple PCI host controllers. for (u8 function = 0; function < 8; ++function) { - if (read16_field(Address(seg, 0, 0, function), PCI_VENDOR_ID) == PCI_NONE) + if (early_read16_field(Address(seg, 0, 0, function), PCI_VENDOR_ID) == PCI_NONE) break; enumerate_bus(-1, function, callback); } @@ -238,22 +237,22 @@ MMIOSegment::MMIOSegment(PhysicalAddress segment_base_addr, u8 start_bus, u8 end { } -u8 MMIOSegment::get_start_bus() +u8 MMIOSegment::get_start_bus() const { return m_start_bus; } -u8 MMIOSegment::get_end_bus() +u8 MMIOSegment::get_end_bus() const { return m_end_bus; } -size_t MMIOSegment::get_size() +size_t MMIOSegment::get_size() const { return (PCI_MMIO_CONFIG_SPACE_SIZE * PCI_MAX_FUNCTIONS_PER_DEVICE * PCI_MAX_DEVICES_PER_BUS * (get_end_bus() - get_start_bus())); } -PhysicalAddress MMIOSegment::get_paddr() +PhysicalAddress MMIOSegment::get_paddr() const { return m_base_addr; } diff --git a/Kernel/PCI/MMIOAccess.h b/Kernel/PCI/MMIOAccess.h index d763a9bcfb..97f6fcd8a3 100644 --- a/Kernel/PCI/MMIOAccess.h +++ b/Kernel/PCI/MMIOAccess.h @@ -27,6 +27,7 @@ #pragma once #include <AK/HashMap.h> +#include <AK/NonnullOwnPtrVector.h> #include <AK/OwnPtr.h> #include <AK/Types.h> #include <Kernel/ACPI/Definitions.h> @@ -39,6 +40,18 @@ namespace Kernel { namespace PCI { +class DeviceConfigurationSpaceMapping { +public: + DeviceConfigurationSpaceMapping(Address, const MMIOSegment&); + VirtualAddress vaddr() const { return m_mapped_region->vaddr(); }; + PhysicalAddress paddr() const { return m_mapped_region->physical_page(0)->paddr(); } + const Address& address() const { return m_device_address; }; + +private: + Address m_device_address; + NonnullOwnPtr<Region> m_mapped_region; +}; + class MMIOAccess final : public Access { public: static void initialize(PhysicalAddress mcfg); @@ -57,14 +70,13 @@ private: virtual u16 read16_field(Address address, u32) override; virtual u32 read32_field(Address address, u32) override; - void map_device(Address address); + Optional<VirtualAddress> get_device_configuration_space(Address address); virtual u8 segment_start_bus(u32) const override; virtual u8 segment_end_bus(u32) const override; PhysicalAddress m_mcfg; HashMap<u16, MMIOSegment> m_segments; - OwnPtr<Region> m_mmio_window_region; - ChangeableAddress m_mapped_address; + NonnullOwnPtrVector<DeviceConfigurationSpaceMapping> m_mapped_device_regions; }; } |