mirror of
https://github.com/SerenityOS/serenity
synced 2024-10-15 04:13:11 +00:00
Kernel: Support automatic configuration of PCI bridges based on the FDT
This let's us actually boot with RISC-V.
This commit is contained in:
parent
08d4b231e1
commit
543fc4d0fc
|
@ -52,6 +52,10 @@ void initialize()
|
||||||
[[maybe_unused]] auto soc_size_cells = soc.get_property("#size-cells"sv).value().as<u32>();
|
[[maybe_unused]] auto soc_size_cells = soc.get_property("#size-cells"sv).value().as<u32>();
|
||||||
|
|
||||||
Optional<u32> domain_counter;
|
Optional<u32> domain_counter;
|
||||||
|
Optional<FlatPtr> pci_32bit_mmio_base;
|
||||||
|
u32 pci_32bit_mmio_size = 0;
|
||||||
|
Optional<FlatPtr> pci_64bit_mmio_base;
|
||||||
|
u64 pci_64bit_mmio_size = 0;
|
||||||
for (auto const& entry : soc.children()) {
|
for (auto const& entry : soc.children()) {
|
||||||
if (!entry.key.starts_with("pci"sv))
|
if (!entry.key.starts_with("pci"sv))
|
||||||
continue;
|
continue;
|
||||||
|
@ -147,8 +151,53 @@ void initialize()
|
||||||
case ControllerCompatible::Unknown:
|
case ControllerCompatible::Unknown:
|
||||||
VERIFY_NOT_REACHED(); // This should have been rejected earlier
|
VERIFY_NOT_REACHED(); // This should have been rejected earlier
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto maybe_ranges = node.get_property("ranges"sv);
|
||||||
|
if (maybe_ranges.has_value()) {
|
||||||
|
auto address_cells = node.get_property("#address-cells"sv).value().as<u32>();
|
||||||
|
VERIFY(address_cells == 3); // Additional cell for OpenFirmware PCI address metadata
|
||||||
|
auto size_cells = node.get_property("#size-cells"sv).value().as<u32>();
|
||||||
|
auto stream = maybe_ranges.value().as_stream();
|
||||||
|
while (!stream.is_eof()) {
|
||||||
|
u32 pci_address_metadata = MUST(stream.read_value<BigEndian<u32>>());
|
||||||
|
FlatPtr pci_address = MUST(stream.read_value<BigEndian<u64>>());
|
||||||
|
FlatPtr mmio_address;
|
||||||
|
if (soc_address_cells == 1)
|
||||||
|
mmio_address = MUST(stream.read_value<BigEndian<u32>>());
|
||||||
|
else
|
||||||
|
mmio_address = MUST(stream.read_value<BigEndian<u64>>());
|
||||||
|
u64 mmio_size;
|
||||||
|
if (size_cells == 1)
|
||||||
|
mmio_size = MUST(stream.read_value<BigEndian<u32>>());
|
||||||
|
else
|
||||||
|
mmio_size = MUST(stream.read_value<BigEndian<u64>>());
|
||||||
|
auto space_type = (pci_address_metadata >> OpenFirmwareAddress::space_type_offset) & OpenFirmwareAddress::space_type_mask;
|
||||||
|
if (space_type != OpenFirmwareAddress::SpaceType::Memory32BitSpace && space_type != OpenFirmwareAddress::SpaceType::Memory64BitSpace)
|
||||||
|
continue; // We currently only support memory-mapped PCI on RISC-V
|
||||||
|
// TODO: Support mapped PCI addresses
|
||||||
|
VERIFY(pci_address == mmio_address);
|
||||||
|
if (space_type == OpenFirmwareAddress::SpaceType::Memory32BitSpace) {
|
||||||
|
auto prefetchable = (pci_address_metadata >> OpenFirmwareAddress::prefetchable_offset) & OpenFirmwareAddress::prefetchable_mask;
|
||||||
|
if (prefetchable)
|
||||||
|
continue; // We currently only use non-prefetchable 32-bit regions, since 64-bit regions are always prefetchable - TODO: Use 32-bit prefetchable regions if only they are available
|
||||||
|
if (pci_32bit_mmio_base.has_value() && pci_32bit_mmio_size >= mmio_size)
|
||||||
|
continue; // We currently only use the single largest region - TODO: Use all available regions if needed
|
||||||
|
pci_32bit_mmio_base = mmio_address;
|
||||||
|
pci_32bit_mmio_size = mmio_size;
|
||||||
|
} else {
|
||||||
|
if (pci_64bit_mmio_base.has_value() && pci_64bit_mmio_size >= mmio_size)
|
||||||
|
continue; // We currently only use the single largest region - TODO: Use all available regions if needed
|
||||||
|
pci_64bit_mmio_base = mmio_address;
|
||||||
|
pci_64bit_mmio_size = mmio_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pci_32bit_mmio_base.has_value() || pci_64bit_mmio_base.has_value())
|
||||||
|
Access::the().configure_pci_space(pci_32bit_mmio_base.value_or(0), pci_32bit_mmio_size, pci_64bit_mmio_base.value_or(0), pci_64bit_mmio_size);
|
||||||
|
else
|
||||||
|
dmesgln("PCI: No MMIO ranges found - assuming pre-configured by bootloader");
|
||||||
Access::the().rescan_hardware();
|
Access::the().rescan_hardware();
|
||||||
|
|
||||||
PCIBusSysFSDirectory::initialize();
|
PCIBusSysFSDirectory::initialize();
|
||||||
|
|
|
@ -154,6 +154,16 @@ UNMAP_AFTER_INIT Access::Access()
|
||||||
s_access = this;
|
s_access = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UNMAP_AFTER_INIT void Access::configure_pci_space(FlatPtr mmio_32bit_base, u32 mmio_32bit_size, FlatPtr mmio_64bit_base, u64 mmio_64bit_size)
|
||||||
|
{
|
||||||
|
SpinlockLocker locker(m_access_lock);
|
||||||
|
SpinlockLocker scan_locker(m_scan_lock);
|
||||||
|
FlatPtr mmio_32bit_end = mmio_32bit_base + mmio_32bit_size;
|
||||||
|
FlatPtr mmio_64bit_end = mmio_64bit_base + mmio_64bit_size;
|
||||||
|
for (auto& [_, host_controller] : m_host_controllers)
|
||||||
|
host_controller->configure_attached_devices(mmio_32bit_base, mmio_32bit_end, mmio_64bit_base, mmio_64bit_end);
|
||||||
|
}
|
||||||
|
|
||||||
UNMAP_AFTER_INIT void Access::rescan_hardware()
|
UNMAP_AFTER_INIT void Access::rescan_hardware()
|
||||||
{
|
{
|
||||||
SpinlockLocker locker(m_access_lock);
|
SpinlockLocker locker(m_access_lock);
|
||||||
|
|
|
@ -27,6 +27,7 @@ public:
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ErrorOr<void> fast_enumerate(Function<void(DeviceIdentifier const&)>&) const;
|
ErrorOr<void> fast_enumerate(Function<void(DeviceIdentifier const&)>&) const;
|
||||||
|
void configure_pci_space(FlatPtr mmio_32bit_base, u32 mmio_32bit_size, FlatPtr mmio_64bit_base, u64 mmio_64bit_size);
|
||||||
void rescan_hardware();
|
void rescan_hardware();
|
||||||
|
|
||||||
static Access& the();
|
static Access& the();
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <AK/Format.h>
|
#include <AK/Format.h>
|
||||||
|
#include <Kernel/Bus/PCI/API.h>
|
||||||
#include <Kernel/Bus/PCI/Access.h>
|
#include <Kernel/Bus/PCI/Access.h>
|
||||||
#include <Kernel/Bus/PCI/Controller/HostController.h>
|
#include <Kernel/Bus/PCI/Controller/HostController.h>
|
||||||
#include <Kernel/Bus/PCI/Definitions.h>
|
#include <Kernel/Bus/PCI/Definitions.h>
|
||||||
|
@ -45,6 +46,21 @@ UNMAP_AFTER_INIT Vector<Capability> HostController::get_capabilities_for_functio
|
||||||
return capabilities;
|
return capabilities;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HostController::write8_field(BusNumber bus, DeviceNumber device, FunctionNumber function, PCI::RegisterOffset field, u8 value)
|
||||||
|
{
|
||||||
|
write8_field(bus, device, function, to_underlying(field), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HostController::write16_field(BusNumber bus, DeviceNumber device, FunctionNumber function, RegisterOffset field, u16 value)
|
||||||
|
{
|
||||||
|
write16_field(bus, device, function, to_underlying(field), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HostController::write32_field(BusNumber bus, DeviceNumber device, FunctionNumber function, RegisterOffset field, u32 value)
|
||||||
|
{
|
||||||
|
write32_field(bus, device, function, to_underlying(field), value);
|
||||||
|
}
|
||||||
|
|
||||||
u8 HostController::read8_field(BusNumber bus, DeviceNumber device, FunctionNumber function, PCI::RegisterOffset field)
|
u8 HostController::read8_field(BusNumber bus, DeviceNumber device, FunctionNumber function, PCI::RegisterOffset field)
|
||||||
{
|
{
|
||||||
return read8_field(bus, device, function, to_underlying(field));
|
return read8_field(bus, device, function, to_underlying(field));
|
||||||
|
@ -54,7 +70,7 @@ u16 HostController::read16_field(BusNumber bus, DeviceNumber device, FunctionNum
|
||||||
return read16_field(bus, device, function, to_underlying(field));
|
return read16_field(bus, device, function, to_underlying(field));
|
||||||
}
|
}
|
||||||
|
|
||||||
UNMAP_AFTER_INIT void HostController::enumerate_functions(Function<void(EnumerableDeviceIdentifier const&)> const& callback, BusNumber bus, DeviceNumber device, FunctionNumber function, bool recursive_search_into_bridges)
|
UNMAP_AFTER_INIT void HostController::enumerate_functions(Function<void(EnumerableDeviceIdentifier const&)> const& callback, Function<void(EnumerableDeviceIdentifier const&)>& post_bridge_callback, BusNumber bus, DeviceNumber device, FunctionNumber function, bool recursive_search_into_bridges)
|
||||||
{
|
{
|
||||||
dbgln_if(PCI_DEBUG, "PCI: Enumerating function, bus={}, device={}, function={}", bus, device, function);
|
dbgln_if(PCI_DEBUG, "PCI: Enumerating function, bus={}, device={}, function={}", bus, device, function);
|
||||||
Address address(domain_number(), bus.value(), device.value(), function.value());
|
Address address(domain_number(), bus.value(), device.value(), function.value());
|
||||||
|
@ -70,7 +86,8 @@ UNMAP_AFTER_INIT void HostController::enumerate_functions(Function<void(Enumerab
|
||||||
InterruptLine interrupt_line = read8_field(bus, device, function, PCI::RegisterOffset::INTERRUPT_LINE);
|
InterruptLine interrupt_line = read8_field(bus, device, function, PCI::RegisterOffset::INTERRUPT_LINE);
|
||||||
InterruptPin interrupt_pin = read8_field(bus, device, function, PCI::RegisterOffset::INTERRUPT_PIN);
|
InterruptPin interrupt_pin = read8_field(bus, device, function, PCI::RegisterOffset::INTERRUPT_PIN);
|
||||||
auto capabilities = get_capabilities_for_function(bus, device, function);
|
auto capabilities = get_capabilities_for_function(bus, device, function);
|
||||||
callback(EnumerableDeviceIdentifier { address, id, revision_id, class_code, subclass_code, prog_if, subsystem_id, subsystem_vendor_id, interrupt_line, interrupt_pin, capabilities });
|
auto device_ientifier = EnumerableDeviceIdentifier { address, id, revision_id, class_code, subclass_code, prog_if, subsystem_id, subsystem_vendor_id, interrupt_line, interrupt_pin, capabilities };
|
||||||
|
callback(device_ientifier);
|
||||||
|
|
||||||
if (pci_class == (to_underlying(PCI::ClassID::Bridge) << 8 | to_underlying(PCI::Bridge::SubclassID::PCI_TO_PCI))
|
if (pci_class == (to_underlying(PCI::ClassID::Bridge) << 8 | to_underlying(PCI::Bridge::SubclassID::PCI_TO_PCI))
|
||||||
&& recursive_search_into_bridges
|
&& recursive_search_into_bridges
|
||||||
|
@ -79,32 +96,35 @@ UNMAP_AFTER_INIT void HostController::enumerate_functions(Function<void(Enumerab
|
||||||
dbgln_if(PCI_DEBUG, "PCI: Found secondary bus: {}", secondary_bus);
|
dbgln_if(PCI_DEBUG, "PCI: Found secondary bus: {}", secondary_bus);
|
||||||
VERIFY(secondary_bus != bus);
|
VERIFY(secondary_bus != bus);
|
||||||
m_enumerated_buses.set(secondary_bus, true);
|
m_enumerated_buses.set(secondary_bus, true);
|
||||||
enumerate_bus(callback, secondary_bus, recursive_search_into_bridges);
|
enumerate_bus(callback, post_bridge_callback, secondary_bus, recursive_search_into_bridges);
|
||||||
|
|
||||||
|
if (post_bridge_callback)
|
||||||
|
post_bridge_callback(device_ientifier);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UNMAP_AFTER_INIT void HostController::enumerate_device(Function<void(EnumerableDeviceIdentifier const&)> const& callback, BusNumber bus, DeviceNumber device, bool recursive_search_into_bridges)
|
UNMAP_AFTER_INIT void HostController::enumerate_device(Function<void(EnumerableDeviceIdentifier const&)> const& callback, Function<void(EnumerableDeviceIdentifier const&)>& post_bridge_callback, BusNumber bus, DeviceNumber device, bool recursive_search_into_bridges)
|
||||||
{
|
{
|
||||||
dbgln_if(PCI_DEBUG, "PCI: Enumerating device in bus={}, device={}", bus, device);
|
dbgln_if(PCI_DEBUG, "PCI: Enumerating device in bus={}, device={}", bus, device);
|
||||||
if (read16_field(bus, device, 0, PCI::RegisterOffset::VENDOR_ID) == PCI::none_value)
|
if (read16_field(bus, device, 0, PCI::RegisterOffset::VENDOR_ID) == PCI::none_value)
|
||||||
return;
|
return;
|
||||||
enumerate_functions(callback, bus, device, 0, recursive_search_into_bridges);
|
enumerate_functions(callback, post_bridge_callback, bus, device, 0, recursive_search_into_bridges);
|
||||||
if (!(read8_field(bus, device, 0, PCI::RegisterOffset::HEADER_TYPE) & 0x80))
|
if (!(read8_field(bus, device, 0, PCI::RegisterOffset::HEADER_TYPE) & 0x80))
|
||||||
return;
|
return;
|
||||||
for (u8 function = 1; function < 8; ++function) {
|
for (u8 function = 1; function < 8; ++function) {
|
||||||
if (read16_field(bus, device, function, PCI::RegisterOffset::VENDOR_ID) != PCI::none_value)
|
if (read16_field(bus, device, function, PCI::RegisterOffset::VENDOR_ID) != PCI::none_value)
|
||||||
enumerate_functions(callback, bus, device, function, recursive_search_into_bridges);
|
enumerate_functions(callback, post_bridge_callback, bus, device, function, recursive_search_into_bridges);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UNMAP_AFTER_INIT void HostController::enumerate_bus(Function<void(EnumerableDeviceIdentifier const&)> const& callback, BusNumber bus, bool recursive_search_into_bridges)
|
UNMAP_AFTER_INIT void HostController::enumerate_bus(Function<void(EnumerableDeviceIdentifier const&)> const& callback, Function<void(EnumerableDeviceIdentifier const&)>& post_bridge_callback, BusNumber bus, bool recursive_search_into_bridges)
|
||||||
{
|
{
|
||||||
dbgln_if(PCI_DEBUG, "PCI: Enumerating bus {}", bus);
|
dbgln_if(PCI_DEBUG, "PCI: Enumerating bus {}", bus);
|
||||||
for (u8 device = 0; device < 32; ++device)
|
for (u8 device = 0; device < 32; ++device)
|
||||||
enumerate_device(callback, bus, device, recursive_search_into_bridges);
|
enumerate_device(callback, post_bridge_callback, bus, device, recursive_search_into_bridges);
|
||||||
}
|
}
|
||||||
|
|
||||||
UNMAP_AFTER_INIT void HostController::enumerate_attached_devices(Function<void(EnumerableDeviceIdentifier const&)> callback)
|
UNMAP_AFTER_INIT void HostController::enumerate_attached_devices(Function<void(EnumerableDeviceIdentifier const&)> callback, Function<void(EnumerableDeviceIdentifier const&)> post_bridge_callback)
|
||||||
{
|
{
|
||||||
VERIFY(Access::the().access_lock().is_locked());
|
VERIFY(Access::the().access_lock().is_locked());
|
||||||
VERIFY(Access::the().scan_lock().is_locked());
|
VERIFY(Access::the().scan_lock().is_locked());
|
||||||
|
@ -112,7 +132,7 @@ UNMAP_AFTER_INIT void HostController::enumerate_attached_devices(Function<void(E
|
||||||
// First scan bus 0. Find any device on that bus, and if it's a PCI-to-PCI
|
// First scan bus 0. Find any device on that bus, and if it's a PCI-to-PCI
|
||||||
// bridge, recursively scan it too.
|
// bridge, recursively scan it too.
|
||||||
m_enumerated_buses.set(m_domain.start_bus(), true);
|
m_enumerated_buses.set(m_domain.start_bus(), true);
|
||||||
enumerate_bus(callback, m_domain.start_bus(), true);
|
enumerate_bus(callback, post_bridge_callback, m_domain.start_bus(), true);
|
||||||
|
|
||||||
// Handle Multiple PCI host bridges on bus 0, device 0, functions 1-7 (function 0
|
// Handle Multiple PCI host bridges on bus 0, device 0, functions 1-7 (function 0
|
||||||
// is the main host bridge).
|
// is the main host bridge).
|
||||||
|
@ -128,10 +148,124 @@ UNMAP_AFTER_INIT void HostController::enumerate_attached_devices(Function<void(E
|
||||||
break;
|
break;
|
||||||
if (m_enumerated_buses.get(m_domain.start_bus() + bus_as_function_number))
|
if (m_enumerated_buses.get(m_domain.start_bus() + bus_as_function_number))
|
||||||
continue;
|
continue;
|
||||||
enumerate_bus(callback, m_domain.start_bus() + bus_as_function_number, false);
|
enumerate_bus(callback, post_bridge_callback, m_domain.start_bus() + bus_as_function_number, false);
|
||||||
m_enumerated_buses.set(m_domain.start_bus() + bus_as_function_number, true);
|
m_enumerated_buses.set(m_domain.start_bus() + bus_as_function_number, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HostController::configure_attached_devices(FlatPtr& mmio_32bit_base, FlatPtr mmio_32bit_end, FlatPtr& mmio_64bit_base, FlatPtr mmio_64bit_end)
|
||||||
|
{
|
||||||
|
// First, Assign PCI-to-PCI bridge bus numbering
|
||||||
|
u8 bus_id = 0;
|
||||||
|
enumerate_attached_devices([this, &bus_id](EnumerableDeviceIdentifier const& device_identifier) {
|
||||||
|
// called for each PCI device encountered (including bridges)
|
||||||
|
if (read8_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), PCI::RegisterOffset::CLASS) != PCI::ClassID::Bridge)
|
||||||
|
return;
|
||||||
|
if (read8_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), PCI::RegisterOffset::SUBCLASS) != PCI::Bridge::SubclassID::PCI_TO_PCI)
|
||||||
|
return;
|
||||||
|
write8_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), PCI::RegisterOffset::SECONDARY_BUS, ++bus_id);
|
||||||
|
write8_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), PCI::RegisterOffset::SUBORDINATE_BUS, 0xFF); },
|
||||||
|
[this, &bus_id](EnumerableDeviceIdentifier const& device_identifier) {
|
||||||
|
// called after a bridge was recursively enumerated
|
||||||
|
write8_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), PCI::RegisterOffset::SUBORDINATE_BUS, bus_id);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Second, Assign BAR addresses
|
||||||
|
// TODO: We currently naively assign addresses bump-allocator style - Switch to a proper allocator if this is not good enough
|
||||||
|
enumerate_attached_devices([this, &mmio_32bit_base, mmio_32bit_end, &mmio_64bit_base, mmio_64bit_end](EnumerableDeviceIdentifier const& device_identifier) {
|
||||||
|
// device-generic handling
|
||||||
|
auto header_type = read8_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), PCI::RegisterOffset::HEADER_TYPE);
|
||||||
|
auto const max_bar = (header_type == 0) ? RegisterOffset::BAR5 : RegisterOffset::BAR1;
|
||||||
|
for (u32 bar_offset = to_underlying(RegisterOffset::BAR0); bar_offset <= to_underlying(max_bar); bar_offset += 4) {
|
||||||
|
auto bar_value = read32_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), bar_offset);
|
||||||
|
auto bar_space_type = get_BAR_space_type(bar_value);
|
||||||
|
auto bar_prefetchable = (bar_value >> 3) & 1;
|
||||||
|
if (bar_space_type != BARSpaceType::Memory32BitSpace && bar_space_type != BARSpaceType::Memory64BitSpace)
|
||||||
|
continue; // We only support memory-mapped BAR configuration at the moment
|
||||||
|
if (bar_space_type == BARSpaceType::Memory32BitSpace) {
|
||||||
|
write32_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), bar_offset, 0xFFFFFFFF);
|
||||||
|
auto bar_size = read32_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), bar_offset);
|
||||||
|
write32_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), bar_offset, bar_value);
|
||||||
|
bar_size &= bar_address_mask;
|
||||||
|
bar_size = (~bar_size) + 1;
|
||||||
|
if (bar_size == 0)
|
||||||
|
continue;
|
||||||
|
auto mmio_32bit_address = align_up_to(mmio_32bit_base, bar_size);
|
||||||
|
if (mmio_32bit_address + bar_size <= mmio_32bit_end) {
|
||||||
|
write32_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), bar_offset, mmio_32bit_address);
|
||||||
|
mmio_32bit_base = mmio_32bit_address + bar_size;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto mmio_64bit_address = align_up_to(mmio_64bit_base, bar_size);
|
||||||
|
if (bar_prefetchable && mmio_64bit_address + bar_size <= mmio_64bit_end && mmio_64bit_address + bar_size <= NumericLimits<u32>::max()) {
|
||||||
|
write32_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), bar_offset, mmio_64bit_address);
|
||||||
|
mmio_64bit_base = mmio_64bit_address + bar_size;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
dmesgln("PCI: Ran out of 32-bit MMIO address space");
|
||||||
|
VERIFY_NOT_REACHED();
|
||||||
|
}
|
||||||
|
// 64-bit space
|
||||||
|
auto next_bar_value = read32_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), bar_offset + 4);
|
||||||
|
write32_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), bar_offset, 0xFFFFFFFF);
|
||||||
|
write32_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), bar_offset + 4, 0xFFFFFFFF);
|
||||||
|
u64 bar_size = read32_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), bar_offset);
|
||||||
|
bar_size |= (u64)read32_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), bar_offset + 4) << 32;
|
||||||
|
write32_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), bar_offset, bar_value);
|
||||||
|
write32_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), bar_offset + 4, next_bar_value);
|
||||||
|
bar_size &= bar_address_mask;
|
||||||
|
bar_size = (~bar_size) + 1;
|
||||||
|
if (bar_size == 0) {
|
||||||
|
bar_offset += 4;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto mmio_64bit_address = align_up_to(mmio_64bit_base, bar_size);
|
||||||
|
if (bar_prefetchable && mmio_64bit_address + bar_size <= mmio_64bit_end) {
|
||||||
|
write32_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), bar_offset, mmio_64bit_address & 0xFFFFFFFF);
|
||||||
|
write32_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), bar_offset + 4, mmio_64bit_address >> 32);
|
||||||
|
mmio_64bit_base = mmio_64bit_address + bar_size;
|
||||||
|
bar_offset += 4;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto mmio_32bit_address = align_up_to(mmio_32bit_base, bar_size);
|
||||||
|
if (mmio_32bit_address + bar_size <= mmio_32bit_end) {
|
||||||
|
write32_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), bar_offset, mmio_32bit_address & 0xFFFFFFFF);
|
||||||
|
write32_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), bar_offset + 4, mmio_32bit_address >> 32);
|
||||||
|
mmio_32bit_base = mmio_32bit_address + bar_size;
|
||||||
|
bar_offset += 4;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
dmesgln("PCI: Ran out of 64-bit MMIO address space");
|
||||||
|
VERIFY_NOT_REACHED();
|
||||||
|
}
|
||||||
|
// enable memory space
|
||||||
|
auto command_value = read16_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), PCI::RegisterOffset::COMMAND);
|
||||||
|
command_value |= 1 << 1; // memory space enable
|
||||||
|
write16_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), PCI::RegisterOffset::COMMAND, command_value);
|
||||||
|
|
||||||
|
if (read8_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), PCI::RegisterOffset::CLASS) != PCI::ClassID::Bridge)
|
||||||
|
return;
|
||||||
|
if (read8_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), PCI::RegisterOffset::SUBCLASS) != PCI::Bridge::SubclassID::PCI_TO_PCI)
|
||||||
|
return;
|
||||||
|
// bridge-specific handling
|
||||||
|
mmio_32bit_base = align_up_to(mmio_32bit_base, MiB);
|
||||||
|
mmio_64bit_base = align_up_to(mmio_64bit_base, MiB);
|
||||||
|
write32_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), PCI::RegisterOffset::MEMORY_BASE, mmio_32bit_base >> 16);
|
||||||
|
write32_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), PCI::RegisterOffset::PREFETCHABLE_MEMORY_BASE, mmio_64bit_base >> 16);
|
||||||
|
write32_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), PCI::RegisterOffset::PREFETCHABLE_MEMORY_BASE_UPPER_32_BITS, mmio_64bit_base >> 32); },
|
||||||
|
[this, &mmio_32bit_base, &mmio_64bit_base](EnumerableDeviceIdentifier const& device_identifier) {
|
||||||
|
// called after a bridge was recursively enumerated
|
||||||
|
mmio_32bit_base = align_up_to(mmio_32bit_base, MiB);
|
||||||
|
mmio_64bit_base = align_up_to(mmio_64bit_base, MiB);
|
||||||
|
write32_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), PCI::RegisterOffset::MEMORY_LIMIT, mmio_32bit_base >> 16);
|
||||||
|
write32_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), PCI::RegisterOffset::PREFETCHABLE_MEMORY_LIMIT, mmio_64bit_base >> 16);
|
||||||
|
write32_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), PCI::RegisterOffset::PREFETCHABLE_MEMORY_LIMIT_UPPER_32_BITS, mmio_64bit_base >> 32);
|
||||||
|
// enable bridging
|
||||||
|
auto command_value = read16_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), PCI::RegisterOffset::COMMAND);
|
||||||
|
command_value |= 1 << 2; // enable forwarding of requests by the bridge
|
||||||
|
write16_field(device_identifier.address().bus(), device_identifier.address().device(), device_identifier.address().function(), PCI::RegisterOffset::COMMAND, command_value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,12 +31,17 @@ public:
|
||||||
|
|
||||||
u32 domain_number() const { return m_domain.domain_number(); }
|
u32 domain_number() const { return m_domain.domain_number(); }
|
||||||
|
|
||||||
void enumerate_attached_devices(Function<void(EnumerableDeviceIdentifier const&)> callback);
|
void enumerate_attached_devices(Function<void(EnumerableDeviceIdentifier const&)> callback, Function<void(EnumerableDeviceIdentifier const&)> post_bridge_callback = nullptr);
|
||||||
|
void configure_attached_devices(FlatPtr& mmio_32bit_base, FlatPtr mmio_32bit_end, FlatPtr& mmio_64bit_base, FlatPtr mmio_64bit_end);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void enumerate_bus(Function<void(EnumerableDeviceIdentifier const&)> const& callback, BusNumber, bool recursive_search_into_bridges);
|
void enumerate_bus(Function<void(EnumerableDeviceIdentifier const&)> const& callback, Function<void(EnumerableDeviceIdentifier const&)>& post_bridge_callback, BusNumber, bool recursive_search_into_bridges);
|
||||||
void enumerate_functions(Function<void(EnumerableDeviceIdentifier const&)> const& callback, BusNumber, DeviceNumber, FunctionNumber, bool recursive_search_into_bridges);
|
void enumerate_functions(Function<void(EnumerableDeviceIdentifier const&)> const& callback, Function<void(EnumerableDeviceIdentifier const&)>& post_bridge_callback, BusNumber, DeviceNumber, FunctionNumber, bool recursive_search_into_bridges);
|
||||||
void enumerate_device(Function<void(EnumerableDeviceIdentifier const&)> const& callback, BusNumber bus, DeviceNumber device, bool recursive_search_into_bridges);
|
void enumerate_device(Function<void(EnumerableDeviceIdentifier const&)> const& callback, Function<void(EnumerableDeviceIdentifier const&)>& post_bridge_callback, BusNumber bus, DeviceNumber device, bool recursive_search_into_bridges);
|
||||||
|
|
||||||
|
void write8_field(BusNumber, DeviceNumber, FunctionNumber, RegisterOffset field, u8 value);
|
||||||
|
void write16_field(BusNumber, DeviceNumber, FunctionNumber, RegisterOffset field, u16 value);
|
||||||
|
void write32_field(BusNumber, DeviceNumber, FunctionNumber, RegisterOffset field, u32 value);
|
||||||
|
|
||||||
u8 read8_field(BusNumber, DeviceNumber, FunctionNumber, RegisterOffset field);
|
u8 read8_field(BusNumber, DeviceNumber, FunctionNumber, RegisterOffset field);
|
||||||
u16 read16_field(BusNumber, DeviceNumber, FunctionNumber, RegisterOffset field);
|
u16 read16_field(BusNumber, DeviceNumber, FunctionNumber, RegisterOffset field);
|
||||||
|
|
|
@ -39,32 +39,39 @@ enum class BARSpaceType {
|
||||||
Memory64BitSpace,
|
Memory64BitSpace,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class RegisterOffset {
|
enum class RegisterOffset : u32 {
|
||||||
VENDOR_ID = 0x00, // word
|
VENDOR_ID = 0x00, // word
|
||||||
DEVICE_ID = 0x02, // word
|
DEVICE_ID = 0x02, // word
|
||||||
COMMAND = 0x04, // word
|
COMMAND = 0x04, // word
|
||||||
STATUS = 0x06, // word
|
STATUS = 0x06, // word
|
||||||
REVISION_ID = 0x08, // byte
|
REVISION_ID = 0x08, // byte
|
||||||
PROG_IF = 0x09, // byte
|
PROG_IF = 0x09, // byte
|
||||||
SUBCLASS = 0x0a, // byte
|
SUBCLASS = 0x0a, // byte
|
||||||
CLASS = 0x0b, // byte
|
CLASS = 0x0b, // byte
|
||||||
CACHE_LINE_SIZE = 0x0c, // byte
|
CACHE_LINE_SIZE = 0x0c, // byte
|
||||||
LATENCY_TIMER = 0x0d, // byte
|
LATENCY_TIMER = 0x0d, // byte
|
||||||
HEADER_TYPE = 0x0e, // byte
|
HEADER_TYPE = 0x0e, // byte
|
||||||
BIST = 0x0f, // byte
|
BIST = 0x0f, // byte
|
||||||
BAR0 = 0x10, // u32
|
BAR0 = 0x10, // u32
|
||||||
BAR1 = 0x14, // u32
|
BAR1 = 0x14, // u32
|
||||||
BAR2 = 0x18, // u32
|
BAR2 = 0x18, // u32
|
||||||
SECONDARY_BUS = 0x19, // byte
|
SECONDARY_BUS = 0x19, // byte
|
||||||
BAR3 = 0x1C, // u32
|
SUBORDINATE_BUS = 0x1A, // byte
|
||||||
BAR4 = 0x20, // u32
|
BAR3 = 0x1C, // u32
|
||||||
BAR5 = 0x24, // u32
|
BAR4 = 0x20, // u32
|
||||||
SUBSYSTEM_VENDOR_ID = 0x2C, // u16
|
MEMORY_BASE = 0x20, // u16
|
||||||
SUBSYSTEM_ID = 0x2E, // u16
|
MEMORY_LIMIT = 0x22, // u16
|
||||||
EXPANSION_ROM_POINTER = 0x30, // u32
|
BAR5 = 0x24, // u32
|
||||||
CAPABILITIES_POINTER = 0x34, // u8
|
PREFETCHABLE_MEMORY_BASE = 0x24, // u16
|
||||||
INTERRUPT_LINE = 0x3C, // byte
|
PREFETCHABLE_MEMORY_LIMIT = 0x26, // u16
|
||||||
INTERRUPT_PIN = 0x3D, // byte
|
PREFETCHABLE_MEMORY_BASE_UPPER_32_BITS = 0x28, // u32
|
||||||
|
PREFETCHABLE_MEMORY_LIMIT_UPPER_32_BITS = 0x2C, // u32
|
||||||
|
SUBSYSTEM_VENDOR_ID = 0x2C, // u16
|
||||||
|
SUBSYSTEM_ID = 0x2E, // u16
|
||||||
|
EXPANSION_ROM_POINTER = 0x30, // u32
|
||||||
|
CAPABILITIES_POINTER = 0x34, // u8
|
||||||
|
INTERRUPT_LINE = 0x3C, // byte
|
||||||
|
INTERRUPT_PIN = 0x3D, // byte
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class Limits {
|
enum class Limits {
|
||||||
|
@ -92,6 +99,22 @@ static constexpr u8 msix_table_bir_mask = 0x7;
|
||||||
static constexpr u16 msix_table_offset_mask = 0xfff8;
|
static constexpr u16 msix_table_offset_mask = 0xfff8;
|
||||||
static constexpr u16 msix_control_enable = 0x8000;
|
static constexpr u16 msix_control_enable = 0x8000;
|
||||||
|
|
||||||
|
namespace OpenFirmwareAddress {
|
||||||
|
|
||||||
|
static constexpr u8 space_type_offset = 24;
|
||||||
|
static constexpr u8 space_type_mask = 0x3;
|
||||||
|
static constexpr u8 prefetchable_offset = 30;
|
||||||
|
static constexpr u8 prefetchable_mask = 0x1;
|
||||||
|
|
||||||
|
enum SpaceType {
|
||||||
|
ConfigurationSpace = 0,
|
||||||
|
IOSpace = 1,
|
||||||
|
Memory32BitSpace = 2,
|
||||||
|
Memory64BitSpace = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// Taken from https://pcisig.com/sites/default/files/files/PCI_Code-ID_r_1_11__v24_Jan_2019.pdf
|
// Taken from https://pcisig.com/sites/default/files/files/PCI_Code-ID_r_1_11__v24_Jan_2019.pdf
|
||||||
enum class ClassID {
|
enum class ClassID {
|
||||||
Legacy = 0x00,
|
Legacy = 0x00,
|
||||||
|
|
Loading…
Reference in a new issue