mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 20:07:35 +00:00
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.
This commit is contained in:
parent
1f9d3a3523
commit
a7677f1d9b
6 changed files with 155 additions and 0 deletions
|
@ -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());
|
||||
|
|
|
@ -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&);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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 };
|
||||
};
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue