diff --git a/Kernel/Bus/PCI/BarMapping.h b/Kernel/Bus/PCI/BarMapping.h new file mode 100644 index 0000000000..fc98f64cc0 --- /dev/null +++ b/Kernel/Bus/PCI/BarMapping.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2024, Leon Albrecht + * Copyright (c) 2022, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include + +namespace Kernel::PCI { + +inline ErrorOr get_bar_address(DeviceIdentifier const& device, HeaderType0BaseRegister bar) +{ + u64 pci_bar_value = get_BAR(device, bar); + auto pci_bar_space_type = get_BAR_space_type(pci_bar_value); + + if (pci_bar_space_type == BARSpaceType::IOSpace) + return EIO; + + if (pci_bar_space_type == BARSpaceType::Memory64BitSpace) { + // FIXME: In theory, BAR5 cannot be assigned to 64 bit as it is the last one... + // however, there might be 64 bit BAR5 for real bare metal hardware, so remove this + // if it makes a problem. + if (bar == HeaderType0BaseRegister::BAR5) { + return Error::from_errno(EINVAL); + } + u64 next_pci_bar_value = get_BAR(device, static_cast(to_underlying(bar) + 1)); + pci_bar_value |= next_pci_bar_value << 32; + + return PhysicalAddress { pci_bar_value & bar_address_mask }; + } + + return PhysicalAddress { pci_bar_value & bar_address_mask }; +} + +template +inline ErrorOr> map_bar(DeviceIdentifier const& device, HeaderType0BaseRegister bar, size_t size = sizeof(T), Memory::Region::Access access = IsConst ? Memory::Region::Access::Read : Memory::Region::Access::ReadWrite) +{ + u64 pci_bar_value = get_BAR(device, bar); + auto pci_bar_space_type = get_BAR_space_type(pci_bar_value); + + if (pci_bar_space_type == PCI::BARSpaceType::IOSpace) + return EIO; + + auto bar_address = TRY(get_bar_address(device, bar)); + + auto pci_bar_space_size = PCI::get_BAR_space_size(device, bar); + if (pci_bar_space_size < size) + return Error::from_errno(EIO); + + if (pci_bar_space_type == PCI::BARSpaceType::Memory32BitSpace && Checked::addition_would_overflow(bar_address.get(), size)) + return Error::from_errno(EOVERFLOW); + if (pci_bar_space_type == PCI::BARSpaceType::Memory16BitSpace && Checked::addition_would_overflow(bar_address.get(), size)) + return Error::from_errno(EOVERFLOW); + if (pci_bar_space_type == PCI::BARSpaceType::Memory64BitSpace && Checked::addition_would_overflow(bar_address.get(), size)) + return Error::from_errno(EOVERFLOW); + + return Memory::map_typed(bar_address, size, access); +} + +template +inline ErrorOr>> adopt_new_nonnull_own_bar_mapping(DeviceIdentifier const& device, HeaderType0BaseRegister bar, size_t size = sizeof(T), Memory::Region::Access access = IsConst ? Memory::Region::Access::Read : Memory::Region::Access::ReadWrite) +{ + return adopt_nonnull_own_or_enomem(new (nothrow) Memory::TypedMapping(TRY(map_bar(device, bar, size, access)))); +} + +}