1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-19 16:35:06 +00:00
serenity/Kernel/Bus/PCI/WindowedMMIOAccess.cpp
Andreas Kling 75564b4a5f Kernel: Make kernel region allocators return KResultOr<NOP<Region>>
This expands the reach of error propagation greatly throughout the
kernel. Sadly, it also exposes the fact that we're allocating (and
doing other fallible things) in constructors all over the place.

This patch doesn't attempt to address that of course. That's work for
our future selves.
2021-09-06 01:55:27 +02:00

120 lines
5 KiB
C++

/*
* Copyright (c) 2020-2021, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/ByteReader.h>
#include <AK/Optional.h>
#include <AK/StringView.h>
#include <Kernel/Arch/x86/InterruptDisabler.h>
#include <Kernel/Bus/PCI/WindowedMMIOAccess.h>
#include <Kernel/Debug.h>
#include <Kernel/Memory/MemoryManager.h>
#include <Kernel/Sections.h>
namespace Kernel {
namespace PCI {
UNMAP_AFTER_INIT DeviceConfigurationSpaceMapping::DeviceConfigurationSpaceMapping(Address device_address, const MMIOAccess::MMIOSegment& mmio_segment)
: m_device_address(device_address)
, m_mapped_region(MM.allocate_kernel_region(Memory::page_round_up(PCI_MMIO_CONFIG_SPACE_SIZE), "PCI MMIO Device Access", Memory::Region::Access::ReadWrite).release_value())
{
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.device() + (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) = Memory::PhysicalPage::create(device_physical_mmio_space, Memory::MayReturnToFreeList::No);
m_mapped_region->remap();
}
UNMAP_AFTER_INIT void WindowedMMIOAccess::initialize(PhysicalAddress mcfg)
{
if (!Access::is_initialized()) {
new WindowedMMIOAccess(mcfg);
dbgln_if(PCI_DEBUG, "PCI: MMIO access initialised.");
}
}
UNMAP_AFTER_INIT WindowedMMIOAccess::WindowedMMIOAccess(PhysicalAddress p_mcfg)
: MMIOAccess(p_mcfg)
{
dmesgln("PCI: Using MMIO (mapping per device) for PCI configuration space access");
InterruptDisabler disabler;
enumerate_hardware([&](const Address& address, ID) {
m_mapped_device_regions.append(make<DeviceConfigurationSpaceMapping>(address, m_segments.get(address.seg()).value()));
});
}
Optional<VirtualAddress> WindowedMMIOAccess::get_device_configuration_space(Address address)
{
dbgln_if(PCI_DEBUG, "PCI: Getting device configuration space for {}", address);
for (auto& mapping : m_mapped_device_regions) {
auto checked_address = mapping.address();
dbgln_if(PCI_DEBUG, "PCI Device Configuration Space Mapping: Check if {} was requested", checked_address);
if (address.seg() == checked_address.seg()
&& address.bus() == checked_address.bus()
&& address.device() == checked_address.device()
&& address.function() == checked_address.function()) {
dbgln_if(PCI_DEBUG, "PCI Device Configuration Space Mapping: Found {}", checked_address);
return mapping.vaddr();
}
}
dbgln_if(PCI_DEBUG, "PCI: No device configuration space found for {}", address);
return {};
}
u8 WindowedMMIOAccess::read8_field(Address address, u32 field)
{
InterruptDisabler disabler;
VERIFY(field <= 0xfff);
dbgln_if(PCI_DEBUG, "PCI: MMIO Reading 8-bit field {:#08x} for {}", field, address);
return *((u8*)(get_device_configuration_space(address).value().get() + (field & 0xfff)));
}
u16 WindowedMMIOAccess::read16_field(Address address, u32 field)
{
InterruptDisabler disabler;
VERIFY(field < 0xfff);
dbgln_if(PCI_DEBUG, "PCI: MMIO Reading 16-bit field {:#08x} for {}", field, address);
u16 data = 0;
ByteReader::load<u16>(get_device_configuration_space(address).value().offset(field & 0xfff).as_ptr(), data);
return data;
}
u32 WindowedMMIOAccess::read32_field(Address address, u32 field)
{
InterruptDisabler disabler;
VERIFY(field <= 0xffc);
dbgln_if(PCI_DEBUG, "PCI: MMIO Reading 32-bit field {:#08x} for {}", field, address);
u32 data = 0;
ByteReader::load<u32>(get_device_configuration_space(address).value().offset(field & 0xfff).as_ptr(), data);
return data;
}
void WindowedMMIOAccess::write8_field(Address address, u32 field, u8 value)
{
InterruptDisabler disabler;
VERIFY(field <= 0xfff);
dbgln_if(PCI_DEBUG, "PCI: MMIO Writing 8-bit field {:#08x}, value={:#02x} for {}", field, value, address);
*((u8*)(get_device_configuration_space(address).value().get() + (field & 0xfff))) = value;
}
void WindowedMMIOAccess::write16_field(Address address, u32 field, u16 value)
{
InterruptDisabler disabler;
VERIFY(field < 0xfff);
dbgln_if(PCI_DEBUG, "PCI: MMIO Writing 16-bit field {:#08x}, value={:#02x} for {}", field, value, address);
ByteReader::store<u16>(get_device_configuration_space(address).value().offset(field & 0xfff).as_ptr(), value);
}
void WindowedMMIOAccess::write32_field(Address address, u32 field, u32 value)
{
InterruptDisabler disabler;
VERIFY(field <= 0xffc);
dbgln_if(PCI_DEBUG, "PCI: MMIO Writing 32-bit field {:#08x}, value={:#02x} for {}", field, value, address);
ByteReader::store<u32>(get_device_configuration_space(address).value().offset(field & 0xfff).as_ptr(), value);
}
}
}