diff options
author | Liav A <liavalb@gmail.com> | 2022-02-10 20:35:30 +0200 |
---|---|---|
committer | Jelle Raaijmakers <jelle@gmta.nl> | 2023-01-26 23:04:26 +0100 |
commit | a7677f1d9bb2cf1265006dbd8382057dffef0050 (patch) | |
tree | bca34c4329fe6a98d9f76e244c1aa1904b947bf5 | |
parent | 1f9d3a3523d066a2bc80dd60e472f191492df2dd (diff) | |
download | serenity-a7677f1d9bb2cf1265006dbd8382057dffef0050.zip |
Kernel/PCI: Expose PCI option ROM data from the sysfs interface
For each exposed PCI device in sysfs, there's a new node called "rom"
and by reading it, it exposes the raw data of a PCI option ROM blob to
a user for examining the blob.
-rw-r--r-- | Kernel/Bus/PCI/API.cpp | 13 | ||||
-rw-r--r-- | Kernel/Bus/PCI/API.h | 2 | ||||
-rw-r--r-- | Kernel/CMakeLists.txt | 1 | ||||
-rw-r--r-- | Kernel/FileSystem/SysFS/Subsystems/Bus/PCI/DeviceDirectory.cpp | 2 | ||||
-rw-r--r-- | Kernel/FileSystem/SysFS/Subsystems/Bus/PCI/DeviceExpansionROM.cpp | 105 | ||||
-rw-r--r-- | Kernel/FileSystem/SysFS/Subsystems/Bus/PCI/DeviceExpansionROM.h | 32 |
6 files changed, 155 insertions, 0 deletions
diff --git a/Kernel/Bus/PCI/API.cpp b/Kernel/Bus/PCI/API.cpp index 5ea9b5245a..ff4602acd4 100644 --- a/Kernel/Bus/PCI/API.cpp +++ b/Kernel/Bus/PCI/API.cpp @@ -232,6 +232,19 @@ size_t get_BAR_space_size(DeviceIdentifier const& identifier, HeaderType0BaseReg return space_size; } +size_t get_expansion_rom_space_size(DeviceIdentifier const& identifier) +{ + SpinlockLocker locker(identifier.operation_lock()); + u8 field = to_underlying(PCI::RegisterOffset::EXPANSION_ROM_POINTER); + u32 bar_reserved = read32_offsetted(identifier, field); + write32_offsetted(identifier, field, 0xFFFFFFFF); + u32 space_size = read32_offsetted(identifier, field); + write32_offsetted(identifier, field, bar_reserved); + space_size &= 0xfffffff0; + space_size = (~space_size) + 1; + return space_size; +} + void raw_access(DeviceIdentifier const& identifier, u32 field, size_t access_size, u32 value) { SpinlockLocker locker(identifier.operation_lock()); diff --git a/Kernel/Bus/PCI/API.h b/Kernel/Bus/PCI/API.h index 1b7c641c2e..5f9a62ade3 100644 --- a/Kernel/Bus/PCI/API.h +++ b/Kernel/Bus/PCI/API.h @@ -35,6 +35,8 @@ u32 get_BAR5(DeviceIdentifier const&); u32 get_BAR(DeviceIdentifier const&, HeaderType0BaseRegister); size_t get_BAR_space_size(DeviceIdentifier const&, HeaderType0BaseRegister); BARSpaceType get_BAR_space_type(u32 pci_bar_value); +size_t get_expansion_rom_space_size(DeviceIdentifier const&); + void enable_bus_mastering(DeviceIdentifier const&); void disable_bus_mastering(DeviceIdentifier const&); void enable_io_space(DeviceIdentifier const&); diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt index b52696809d..5db487d2c5 100644 --- a/Kernel/CMakeLists.txt +++ b/Kernel/CMakeLists.txt @@ -150,6 +150,7 @@ set(KERNEL_SOURCES FileSystem/SysFS/Subsystems/Bus/PCI/BusDirectory.cpp FileSystem/SysFS/Subsystems/Bus/PCI/DeviceAttribute.cpp FileSystem/SysFS/Subsystems/Bus/PCI/DeviceDirectory.cpp + FileSystem/SysFS/Subsystems/Bus/PCI/DeviceExpansionROM.cpp FileSystem/SysFS/Subsystems/Bus/USB/BusDirectory.cpp FileSystem/SysFS/Subsystems/Bus/USB/DeviceInformation.cpp FileSystem/SysFS/Subsystems/Bus/Directory.cpp diff --git a/Kernel/FileSystem/SysFS/Subsystems/Bus/PCI/DeviceDirectory.cpp b/Kernel/FileSystem/SysFS/Subsystems/Bus/PCI/DeviceDirectory.cpp index 111825a6fa..e62f50dd48 100644 --- a/Kernel/FileSystem/SysFS/Subsystems/Bus/PCI/DeviceDirectory.cpp +++ b/Kernel/FileSystem/SysFS/Subsystems/Bus/PCI/DeviceDirectory.cpp @@ -8,6 +8,7 @@ #include <Kernel/Bus/PCI/Access.h> #include <Kernel/FileSystem/SysFS/Subsystems/Bus/PCI/DeviceAttribute.h> #include <Kernel/FileSystem/SysFS/Subsystems/Bus/PCI/DeviceDirectory.h> +#include <Kernel/FileSystem/SysFS/Subsystems/Bus/PCI/DeviceExpansionROM.h> #include <Kernel/Sections.h> namespace Kernel { @@ -34,6 +35,7 @@ UNMAP_AFTER_INIT NonnullLockRefPtr<PCIDeviceSysFSDirectory> PCIDeviceSysFSDirect list.append(PCIDeviceAttributeSysFSComponent::create(*directory, PCI::RegisterOffset::BAR3, 4)); list.append(PCIDeviceAttributeSysFSComponent::create(*directory, PCI::RegisterOffset::BAR4, 4)); list.append(PCIDeviceAttributeSysFSComponent::create(*directory, PCI::RegisterOffset::BAR5, 4)); + list.append(PCIDeviceExpansionROMSysFSComponent::create(*directory)); return {}; })); return directory; diff --git a/Kernel/FileSystem/SysFS/Subsystems/Bus/PCI/DeviceExpansionROM.cpp b/Kernel/FileSystem/SysFS/Subsystems/Bus/PCI/DeviceExpansionROM.cpp new file mode 100644 index 0000000000..dca21f5192 --- /dev/null +++ b/Kernel/FileSystem/SysFS/Subsystems/Bus/PCI/DeviceExpansionROM.cpp @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <Kernel/Bus/PCI/API.h> +#include <Kernel/Bus/PCI/Access.h> +#include <Kernel/FileSystem/SysFS/Subsystems/Bus/PCI/DeviceExpansionROM.h> +#include <Kernel/Memory/TypedMapping.h> +#include <Kernel/Sections.h> + +namespace Kernel { + +NonnullLockRefPtr<PCIDeviceExpansionROMSysFSComponent> PCIDeviceExpansionROMSysFSComponent::create(PCIDeviceSysFSDirectory const& device) +{ + auto option_rom_size = PCI::get_expansion_rom_space_size(device.device_identifier()); + return adopt_lock_ref(*new (nothrow) PCIDeviceExpansionROMSysFSComponent(device, option_rom_size)); +} + +PCIDeviceExpansionROMSysFSComponent::PCIDeviceExpansionROMSysFSComponent(PCIDeviceSysFSDirectory const& device, size_t option_rom_size) + : SysFSComponent() + , m_device(device) + , m_option_rom_size(option_rom_size) +{ +} + +ErrorOr<size_t> PCIDeviceExpansionROMSysFSComponent::read_bytes(off_t offset, size_t count, UserOrKernelBuffer& buffer, OpenFileDescription*) const +{ + // NOTE: It might be that the PCI option ROM size is zero, indicating non-existing ROM. + if (m_option_rom_size == 0) + return Error::from_errno(EIO); + // NOTE: This takes into account that `off_t offset` might be a negative number and + // there's no meaningful way to handle negative values, so just return with an error. + if (offset < 0) + return Error::from_errno(EINVAL); + auto unsigned_offset = static_cast<size_t>(offset); + // NOTE: If the offset is beyond the PCI option ROM size, return EOF. + if (unsigned_offset >= m_option_rom_size) + return 0; + + auto blob = TRY(try_to_generate_buffer(unsigned_offset, count)); + if (static_cast<size_t>(offset) >= blob->size()) + return 0; + + ssize_t nread = min(static_cast<off_t>(blob->size() - offset), static_cast<off_t>(count)); + TRY(buffer.write(blob->data() + offset, nread)); + return nread; +} + +ErrorOr<NonnullOwnPtr<KBuffer>> PCIDeviceExpansionROMSysFSComponent::try_to_generate_buffer(size_t offset_in_rom, size_t count) const +{ + // NOTE: If the offset is beyond the PCI option ROM size, panic!. + VERIFY(offset_in_rom < m_option_rom_size); + auto temporary_buffer_size = TRY(Memory::page_round_up(count)); + auto temporary_buffer = TRY(KBuffer::try_create_with_size("SysFS DeviceExpansionROM Device"sv, temporary_buffer_size, Memory::Region::Access::ReadWrite)); + + SpinlockLocker locker(m_device->device_identifier().operation_lock()); + // NOTE: These checks takes into account a couple of cases: + // 1. Option ROM doesn't exist so the value of the ROM physical pointer is 0, and in that case + // we should not allow mapping. + // 2. Option ROM exists but for some (odd) reason, it is found in non-reserved (usable) physical memory + // region, so access to it should be forbidden from this sysfs node. + auto pci_option_rom_physical_pointer = PCI::read32_locked(m_device->device_identifier(), PCI::RegisterOffset::EXPANSION_ROM_POINTER); + if (pci_option_rom_physical_pointer == 0) + return Error::from_errno(EIO); + + if (pci_option_rom_physical_pointer & 1) + dbgln("SysFS DeviceExpansionROM: Possible firmware bug! PCI option ROM was found already to be enabled."); + + auto offested_option_rom_memory_mapped_start_address = PhysicalAddress(pci_option_rom_physical_pointer + offset_in_rom); + auto mapping_size = min(static_cast<off_t>(m_option_rom_size - offset_in_rom), static_cast<off_t>(count)); + if (!MM.is_allowed_to_read_physical_memory_for_userspace(offested_option_rom_memory_mapped_start_address, mapping_size)) + return Error::from_errno(EPERM); + + ScopeGuard unmap_option_rom_on_return([&] { + // NOTE: In general, there's probably nothing wrong in leaving Option ROM being + // mapped into physical memory. + // For the sake of completeness, let's ensure we don't leave the Option ROM being + // mapped into physical memory. + // NOTE: It might be that in the future some driver will need to have the expansion ROM + // being present in the physical address space. If that's the case then we should add a flag + // to the PCI::DeviceIdentifier to indicate this condition! + PCI::write32_locked(m_device->device_identifier(), PCI::RegisterOffset::EXPANSION_ROM_POINTER, pci_option_rom_physical_pointer); + }); + // Note: Write the original value ORed with 1 to enable mapping into physical memory + PCI::write32_locked(m_device->device_identifier(), PCI::RegisterOffset::EXPANSION_ROM_POINTER, pci_option_rom_physical_pointer | 1); + + auto do_io_transaction = [](Bytes bytes_buffer, Memory::TypedMapping<u8> const& mapping, size_t length_to_map) { + VERIFY(length_to_map <= PAGE_SIZE); + memcpy(bytes_buffer.data(), mapping.region->vaddr().offset(mapping.offset).as_ptr(), length_to_map); + }; + + size_t remaining_length = count; + size_t nprocessed = 0; + while (remaining_length > 0) { + size_t length_to_map = min<size_t>(PAGE_SIZE, remaining_length); + auto mapping = TRY(Memory::map_typed<u8>(offested_option_rom_memory_mapped_start_address.offset(nprocessed), length_to_map, Memory::Region::Access::Read)); + do_io_transaction(temporary_buffer->bytes().slice(nprocessed, length_to_map), mapping, length_to_map); + nprocessed += length_to_map; + remaining_length -= length_to_map; + } + return temporary_buffer; +} +} diff --git a/Kernel/FileSystem/SysFS/Subsystems/Bus/PCI/DeviceExpansionROM.h b/Kernel/FileSystem/SysFS/Subsystems/Bus/PCI/DeviceExpansionROM.h new file mode 100644 index 0000000000..817839ff3b --- /dev/null +++ b/Kernel/FileSystem/SysFS/Subsystems/Bus/PCI/DeviceExpansionROM.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include <AK/Vector.h> +#include <Kernel/Bus/PCI/Definitions.h> +#include <Kernel/FileSystem/SysFS/Component.h> +#include <Kernel/FileSystem/SysFS/Subsystems/Bus/PCI/DeviceDirectory.h> + +namespace Kernel { + +class PCIDeviceExpansionROMSysFSComponent : public SysFSComponent { +public: + static NonnullLockRefPtr<PCIDeviceExpansionROMSysFSComponent> create(PCIDeviceSysFSDirectory const& device); + + virtual ErrorOr<size_t> read_bytes(off_t, size_t, UserOrKernelBuffer&, OpenFileDescription*) const override; + virtual ~PCIDeviceExpansionROMSysFSComponent() {}; + + virtual StringView name() const override { return "rom"sv; } + +protected: + ErrorOr<NonnullOwnPtr<KBuffer>> try_to_generate_buffer(size_t offset_in_rom, size_t count) const; + PCIDeviceExpansionROMSysFSComponent(PCIDeviceSysFSDirectory const& device, size_t option_rom_size); + NonnullLockRefPtr<PCIDeviceSysFSDirectory> m_device; + size_t const m_option_rom_size { 0 }; +}; + +} |