mirror of
https://github.com/RGBCube/serenity
synced 2025-05-19 20:35:06 +00:00
Kernel/PCI: Simplify the entire subsystem
A couple of things were changed: 1. Semantic changes - PCI segments are now called PCI domains, to better match what they are really. It's also the name that Linux gave, and it seems that Wikipedia also uses this name. We also remove PCI::ChangeableAddress, because it was used in the past but now it's no longer being used. 2. There are no WindowedMMIOAccess or MMIOAccess classes anymore, as they made a bunch of unnecessary complexity. Instead, Windowed access is removed entirely (this was tested, but never was benchmarked), so we are left with IO access and memory access options. The memory access option is essentially mapping the PCI bus (from the chosen PCI domain), to virtual memory as-is. This means that unless needed, at any time, there is only one PCI bus being mapped, and this is changed if access to another PCI bus in the same PCI domain is needed. For now, we don't support mapping of different PCI buses from different PCI domains at the same time, because basically it's still a non-issue for most machines out there. 2. OOM-safety is increased, especially when constructing the Access object. It means that we pre-allocating any needed resources, and we try to find PCI domains (if requested to initialize memory access) after we attempt to construct the Access object, so it's possible to fail at this point "gracefully". 3. All PCI API functions are now separated into a different header file, which means only "clients" of the PCI subsystem API will need to include that header file. 4. Functional changes - we only allow now to enumerate the bus after a hardware scan. This means that the old method "enumerate_hardware" is removed, so, when initializing an Access object, the initializing function must call rescan on it to force it to find devices. This makes it possible to fail rescan, and also to defer it after construction from both OOM-safety terms and hotplug capabilities.
This commit is contained in:
parent
d1378339f6
commit
25ea7461a0
39 changed files with 948 additions and 1163 deletions
|
@ -10,7 +10,7 @@
|
||||||
#include <Kernel/ACPI/Parser.h>
|
#include <Kernel/ACPI/Parser.h>
|
||||||
#include <Kernel/Arch/PC/BIOS.h>
|
#include <Kernel/Arch/PC/BIOS.h>
|
||||||
#include <Kernel/Arch/x86/InterruptDisabler.h>
|
#include <Kernel/Arch/x86/InterruptDisabler.h>
|
||||||
#include <Kernel/Bus/PCI/Access.h>
|
#include <Kernel/Bus/PCI/API.h>
|
||||||
#include <Kernel/Debug.h>
|
#include <Kernel/Debug.h>
|
||||||
#include <Kernel/IO.h>
|
#include <Kernel/IO.h>
|
||||||
#include <Kernel/Memory/TypedMapping.h>
|
#include <Kernel/Memory/TypedMapping.h>
|
||||||
|
|
231
Kernel/Bus/PCI/API.cpp
Normal file
231
Kernel/Bus/PCI/API.cpp
Normal file
|
@ -0,0 +1,231 @@
|
||||||
|
/*
|
||||||
|
* 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/Sections.h>
|
||||||
|
|
||||||
|
namespace Kernel::PCI {
|
||||||
|
|
||||||
|
void write8(Address address, u32 field, u8 value) { Access::the().write8_field(address, field, value); }
|
||||||
|
void write16(Address address, u32 field, u16 value) { Access::the().write16_field(address, field, value); }
|
||||||
|
void write32(Address address, u32 field, u32 value) { Access::the().write32_field(address, field, value); }
|
||||||
|
u8 read8(Address address, u32 field) { return Access::the().read8_field(address, field); }
|
||||||
|
u16 read16(Address address, u32 field) { return Access::the().read16_field(address, field); }
|
||||||
|
u32 read32(Address address, u32 field) { return Access::the().read32_field(address, field); }
|
||||||
|
|
||||||
|
void enumerate(Function<void(Address, ID)> callback)
|
||||||
|
{
|
||||||
|
Access::the().fast_enumerate(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
PhysicalID get_physical_id(Address address)
|
||||||
|
{
|
||||||
|
return Access::the().get_physical_id(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
ID get_id(Address address)
|
||||||
|
{
|
||||||
|
return { read16(address, PCI_VENDOR_ID), read16(address, PCI_DEVICE_ID) };
|
||||||
|
}
|
||||||
|
|
||||||
|
void enable_io_space(Address address)
|
||||||
|
{
|
||||||
|
write16(address, PCI_COMMAND, read16(address, PCI_COMMAND) | (1 << 0));
|
||||||
|
}
|
||||||
|
void disable_io_space(Address address)
|
||||||
|
{
|
||||||
|
write16(address, PCI_COMMAND, read16(address, PCI_COMMAND) & ~(1 << 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
void enable_memory_space(Address address)
|
||||||
|
{
|
||||||
|
write16(address, PCI_COMMAND, read16(address, PCI_COMMAND) | (1 << 1));
|
||||||
|
}
|
||||||
|
void disable_memory_space(Address address)
|
||||||
|
{
|
||||||
|
write16(address, PCI_COMMAND, read16(address, PCI_COMMAND) & ~(1 << 1));
|
||||||
|
}
|
||||||
|
bool is_io_space_enabled(Address address)
|
||||||
|
{
|
||||||
|
return (read16(address, PCI_COMMAND) & 1) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void enable_interrupt_line(Address address)
|
||||||
|
{
|
||||||
|
write16(address, PCI_COMMAND, read16(address, PCI_COMMAND) & ~(1 << 10));
|
||||||
|
}
|
||||||
|
|
||||||
|
void disable_interrupt_line(Address address)
|
||||||
|
{
|
||||||
|
write16(address, PCI_COMMAND, read16(address, PCI_COMMAND) | 1 << 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 get_interrupt_line(Address address)
|
||||||
|
{
|
||||||
|
return read8(address, PCI_INTERRUPT_LINE);
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 get_BAR0(Address address)
|
||||||
|
{
|
||||||
|
return read32(address, PCI_BAR0);
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 get_BAR1(Address address)
|
||||||
|
{
|
||||||
|
return read32(address, PCI_BAR1);
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 get_BAR2(Address address)
|
||||||
|
{
|
||||||
|
return read32(address, PCI_BAR2);
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 get_BAR3(Address address)
|
||||||
|
{
|
||||||
|
return read16(address, PCI_BAR3);
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 get_BAR4(Address address)
|
||||||
|
{
|
||||||
|
return read32(address, PCI_BAR4);
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 get_BAR5(Address address)
|
||||||
|
{
|
||||||
|
return read32(address, PCI_BAR5);
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 get_BAR(Address address, u8 bar)
|
||||||
|
{
|
||||||
|
VERIFY(bar <= 5);
|
||||||
|
switch (bar) {
|
||||||
|
case 0:
|
||||||
|
return get_BAR0(address);
|
||||||
|
case 1:
|
||||||
|
return get_BAR1(address);
|
||||||
|
case 2:
|
||||||
|
return get_BAR2(address);
|
||||||
|
case 3:
|
||||||
|
return get_BAR3(address);
|
||||||
|
case 4:
|
||||||
|
return get_BAR4(address);
|
||||||
|
case 5:
|
||||||
|
return get_BAR5(address);
|
||||||
|
default:
|
||||||
|
VERIFY_NOT_REACHED();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 get_revision_id(Address address)
|
||||||
|
{
|
||||||
|
return read8(address, PCI_REVISION_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 get_subclass(Address address)
|
||||||
|
{
|
||||||
|
return read8(address, PCI_SUBCLASS);
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 get_class(Address address)
|
||||||
|
{
|
||||||
|
return read8(address, PCI_CLASS);
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 get_programming_interface(Address address)
|
||||||
|
{
|
||||||
|
return read8(address, PCI_PROG_IF);
|
||||||
|
}
|
||||||
|
|
||||||
|
u16 get_subsystem_id(Address address)
|
||||||
|
{
|
||||||
|
return read16(address, PCI_SUBSYSTEM_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
u16 get_subsystem_vendor_id(Address address)
|
||||||
|
{
|
||||||
|
return read16(address, PCI_SUBSYSTEM_VENDOR_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
void enable_bus_mastering(Address address)
|
||||||
|
{
|
||||||
|
auto value = read16(address, PCI_COMMAND);
|
||||||
|
value |= (1 << 2);
|
||||||
|
value |= (1 << 0);
|
||||||
|
write16(address, PCI_COMMAND, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void disable_bus_mastering(Address address)
|
||||||
|
{
|
||||||
|
auto value = read16(address, PCI_COMMAND);
|
||||||
|
value &= ~(1 << 2);
|
||||||
|
value |= (1 << 0);
|
||||||
|
write16(address, PCI_COMMAND, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t get_BAR_space_size(Address address, u8 bar_number)
|
||||||
|
{
|
||||||
|
// See PCI Spec 2.3, Page 222
|
||||||
|
VERIFY(bar_number < 6);
|
||||||
|
u8 field = (PCI_BAR0 + (bar_number << 2));
|
||||||
|
u32 bar_reserved = read32(address, field);
|
||||||
|
write32(address, field, 0xFFFFFFFF);
|
||||||
|
u32 space_size = read32(address, field);
|
||||||
|
write32(address, field, bar_reserved);
|
||||||
|
space_size &= 0xfffffff0;
|
||||||
|
space_size = (~space_size) + 1;
|
||||||
|
return space_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void raw_access(Address address, u32 field, size_t access_size, u32 value)
|
||||||
|
{
|
||||||
|
VERIFY(access_size != 0);
|
||||||
|
if (access_size == 1) {
|
||||||
|
write8(address, field, value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (access_size == 2) {
|
||||||
|
write16(address, field, value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (access_size == 4) {
|
||||||
|
write32(address, field, value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
VERIFY_NOT_REACHED();
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 Capability::read8(u32 field) const
|
||||||
|
{
|
||||||
|
return PCI::read8(m_address, m_ptr + field);
|
||||||
|
}
|
||||||
|
|
||||||
|
u16 Capability::read16(u32 field) const
|
||||||
|
{
|
||||||
|
return PCI::read16(m_address, m_ptr + field);
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 Capability::read32(u32 field) const
|
||||||
|
{
|
||||||
|
return PCI::read32(m_address, m_ptr + field);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Capability::write8(u32 field, u8 value)
|
||||||
|
{
|
||||||
|
PCI::write8(m_address, m_ptr + field, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Capability::write16(u32 field, u16 value)
|
||||||
|
{
|
||||||
|
PCI::write16(m_address, m_ptr + field, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Capability::write32(u32 field, u32 value)
|
||||||
|
{
|
||||||
|
PCI::write32(m_address, m_ptr + field, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
49
Kernel/Bus/PCI/API.h
Normal file
49
Kernel/Bus/PCI/API.h
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Kernel/Bus/PCI/Definitions.h>
|
||||||
|
|
||||||
|
namespace Kernel::PCI {
|
||||||
|
|
||||||
|
void write8(Address address, u32 field, u8 value);
|
||||||
|
void write16(Address address, u32 field, u16 value);
|
||||||
|
void write32(Address address, u32 field, u32 value);
|
||||||
|
u8 read8(Address address, u32 field);
|
||||||
|
u16 read16(Address address, u32 field);
|
||||||
|
u32 read32(Address address, u32 field);
|
||||||
|
|
||||||
|
ID get_id(PCI::Address);
|
||||||
|
bool is_io_space_enabled(Address);
|
||||||
|
void enumerate(Function<void(Address, ID)> callback);
|
||||||
|
void enable_interrupt_line(Address);
|
||||||
|
void disable_interrupt_line(Address);
|
||||||
|
u8 get_interrupt_line(Address);
|
||||||
|
void raw_access(Address, u32, size_t, u32);
|
||||||
|
u32 get_BAR0(Address);
|
||||||
|
u32 get_BAR1(Address);
|
||||||
|
u32 get_BAR2(Address);
|
||||||
|
u32 get_BAR3(Address);
|
||||||
|
u32 get_BAR4(Address);
|
||||||
|
u32 get_BAR5(Address);
|
||||||
|
u32 get_BAR(Address address, u8 bar);
|
||||||
|
u8 get_revision_id(Address);
|
||||||
|
u8 get_programming_interface(Address);
|
||||||
|
u8 get_subclass(Address);
|
||||||
|
u8 get_class(Address);
|
||||||
|
u16 get_subsystem_id(Address);
|
||||||
|
u16 get_subsystem_vendor_id(Address);
|
||||||
|
size_t get_BAR_space_size(Address, u8);
|
||||||
|
void enable_bus_mastering(Address);
|
||||||
|
void disable_bus_mastering(Address);
|
||||||
|
void enable_io_space(Address);
|
||||||
|
void disable_io_space(Address);
|
||||||
|
void enable_memory_space(Address);
|
||||||
|
void disable_memory_space(Address);
|
||||||
|
PhysicalID get_physical_id(Address address);
|
||||||
|
|
||||||
|
}
|
|
@ -4,24 +4,24 @@
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <AK/ByteReader.h>
|
||||||
|
#include <AK/HashTable.h>
|
||||||
|
#include <Kernel/ACPI/Definitions.h>
|
||||||
|
#include <Kernel/API/KResult.h>
|
||||||
#include <Kernel/Bus/PCI/Access.h>
|
#include <Kernel/Bus/PCI/Access.h>
|
||||||
#include <Kernel/Bus/PCI/IOAccess.h>
|
|
||||||
#include <Kernel/Debug.h>
|
#include <Kernel/Debug.h>
|
||||||
#include <Kernel/IO.h>
|
#include <Kernel/IO.h>
|
||||||
|
#include <Kernel/Memory/MemoryManager.h>
|
||||||
|
#include <Kernel/Memory/Region.h>
|
||||||
#include <Kernel/Sections.h>
|
#include <Kernel/Sections.h>
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel::PCI {
|
||||||
namespace PCI {
|
|
||||||
|
#define PCI_MMIO_CONFIG_SPACE_SIZE 4096
|
||||||
|
#define MEMORY_RANGE_PER_BUS (PCI_MMIO_CONFIG_SPACE_SIZE * PCI_MAX_FUNCTIONS_PER_DEVICE * PCI_MAX_DEVICES_PER_BUS)
|
||||||
|
|
||||||
static Access* s_access;
|
static Access* s_access;
|
||||||
|
|
||||||
inline void write8(Address address, u32 field, u8 value) { Access::the().write8_field(address, field, value); }
|
|
||||||
inline void write16(Address address, u32 field, u16 value) { Access::the().write16_field(address, field, value); }
|
|
||||||
inline void write32(Address address, u32 field, u32 value) { Access::the().write32_field(address, field, value); }
|
|
||||||
inline u8 read8(Address address, u32 field) { return Access::the().read8_field(address, field); }
|
|
||||||
inline u16 read16(Address address, u32 field) { return Access::the().read16_field(address, field); }
|
|
||||||
inline u32 read32(Address address, u32 field) { return Access::the().read32_field(address, field); }
|
|
||||||
|
|
||||||
Access& Access::the()
|
Access& Access::the()
|
||||||
{
|
{
|
||||||
if (s_access == nullptr) {
|
if (s_access == nullptr) {
|
||||||
|
@ -35,16 +35,400 @@ bool Access::is_initialized()
|
||||||
return (s_access != nullptr);
|
return (s_access != nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
UNMAP_AFTER_INIT Access::Access()
|
UNMAP_AFTER_INIT bool Access::initialize_for_memory_access(PhysicalAddress mcfg_table)
|
||||||
: m_enumerated_buses(256, false)
|
|
||||||
{
|
{
|
||||||
|
if (Access::is_initialized())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
InterruptDisabler disabler;
|
||||||
|
auto* access = new Access(Access::AccessType::Memory);
|
||||||
|
if (!access->scan_pci_domains(mcfg_table))
|
||||||
|
return false;
|
||||||
|
access->rescan_hardware();
|
||||||
|
dbgln_if(PCI_DEBUG, "PCI: MMIO access initialised.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
UNMAP_AFTER_INIT bool Access::scan_pci_domains(PhysicalAddress mcfg_table)
|
||||||
|
{
|
||||||
|
auto checkup_region_or_error = MM.allocate_kernel_region(mcfg_table.page_base(), (PAGE_SIZE * 2), "PCI MCFG Checkup", Memory::Region::Access::ReadWrite);
|
||||||
|
if (checkup_region_or_error.is_error())
|
||||||
|
return false;
|
||||||
|
dbgln_if(PCI_DEBUG, "PCI: Checking MCFG Table length to choose the correct mapping size");
|
||||||
|
auto* sdt = (ACPI::Structures::SDTHeader*)checkup_region_or_error.value()->vaddr().offset(mcfg_table.offset_in_page()).as_ptr();
|
||||||
|
u32 length = sdt->length;
|
||||||
|
u8 revision = sdt->revision;
|
||||||
|
|
||||||
|
dbgln("PCI: MCFG, length: {}, revision: {}", length, revision);
|
||||||
|
|
||||||
|
auto mcfg_region_or_error = MM.allocate_kernel_region(mcfg_table.page_base(), Memory::page_round_up(length) + PAGE_SIZE, "PCI Parsing MCFG", Memory::Region::Access::ReadWrite);
|
||||||
|
if (mcfg_region_or_error.is_error())
|
||||||
|
return false;
|
||||||
|
auto& mcfg = *(ACPI::Structures::MCFG*)mcfg_region_or_error.value()->vaddr().offset(mcfg_table.offset_in_page()).as_ptr();
|
||||||
|
dbgln_if(PCI_DEBUG, "PCI: Checking MCFG @ {}, {}", VirtualAddress(&mcfg), mcfg_table);
|
||||||
|
for (u32 index = 0; index < ((mcfg.header.length - sizeof(ACPI::Structures::MCFG)) / sizeof(ACPI::Structures::PCI_MMIO_Descriptor)); index++) {
|
||||||
|
u8 start_bus = mcfg.descriptors[index].start_pci_bus;
|
||||||
|
u8 end_bus = mcfg.descriptors[index].end_pci_bus;
|
||||||
|
u32 lower_addr = mcfg.descriptors[index].base_addr;
|
||||||
|
|
||||||
|
auto result = m_domains.set(index, { PhysicalAddress(lower_addr), start_bus, end_bus });
|
||||||
|
VERIFY(result == AK::HashSetResult::InsertedNewEntry);
|
||||||
|
dmesgln("PCI: New PCI domain @ {}, PCI buses ({}-{})", PhysicalAddress { lower_addr }, start_bus, end_bus);
|
||||||
|
}
|
||||||
|
VERIFY(m_domains.contains(0));
|
||||||
|
dmesgln("PCI: MMIO domain: {}", m_domains.size());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
UNMAP_AFTER_INIT bool Access::initialize_for_io_access()
|
||||||
|
{
|
||||||
|
if (Access::is_initialized()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto* access = new Access(Access::AccessType::IO);
|
||||||
|
access->rescan_hardware();
|
||||||
|
dbgln_if(PCI_DEBUG, "PCI: IO access initialised.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
UNMAP_AFTER_INIT Access::Access(AccessType access_type)
|
||||||
|
: m_enumerated_buses(256, false)
|
||||||
|
, m_access_type(access_type)
|
||||||
|
{
|
||||||
|
if (access_type == AccessType::IO)
|
||||||
|
dmesgln("PCI: Using I/O instructions for PCI configuration space access");
|
||||||
|
else
|
||||||
|
dmesgln("PCI: Using memory access for PCI configuration space accesses");
|
||||||
s_access = this;
|
s_access = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Optional<PhysicalAddress> Access::determine_memory_mapped_bus_base_address(u32 domain, u8 bus) const
|
||||||
|
{
|
||||||
|
auto chosen_domain = m_domains.get(domain);
|
||||||
|
if (!chosen_domain.has_value())
|
||||||
|
return {};
|
||||||
|
if (!(chosen_domain.value().start_bus() <= bus && bus <= chosen_domain.value().end_bus()))
|
||||||
|
return {};
|
||||||
|
return chosen_domain.value().paddr().offset(MEMORY_RANGE_PER_BUS * (bus - chosen_domain.value().start_bus()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Access::map_bus_region(u32 domain, u8 bus)
|
||||||
|
{
|
||||||
|
VERIFY(m_access_lock.is_locked());
|
||||||
|
if (m_mapped_bus == bus && m_mapped_bus_region)
|
||||||
|
return;
|
||||||
|
auto bus_base_address = determine_memory_mapped_bus_base_address(domain, bus);
|
||||||
|
// FIXME: Find a way to propagate error from here.
|
||||||
|
if (!bus_base_address.has_value())
|
||||||
|
VERIFY_NOT_REACHED();
|
||||||
|
auto region_or_error = MM.allocate_kernel_region(bus_base_address.value(), MEMORY_RANGE_PER_BUS, "PCI ECAM", Memory::Region::Access::ReadWrite);
|
||||||
|
// FIXME: Find a way to propagate error from here.
|
||||||
|
if (region_or_error.is_error())
|
||||||
|
VERIFY_NOT_REACHED();
|
||||||
|
m_mapped_bus_region = region_or_error.release_value();
|
||||||
|
m_mapped_bus = bus;
|
||||||
|
dbgln_if(PCI_DEBUG, "PCI: New PCI ECAM Mapped region for bus {} @ {} {}", bus, m_mapped_bus_region->vaddr(), m_mapped_bus_region->physical_page(0)->paddr());
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualAddress Access::get_device_configuration_memory_mapped_space(Address address)
|
||||||
|
{
|
||||||
|
VERIFY(m_access_lock.is_locked());
|
||||||
|
dbgln_if(PCI_DEBUG, "PCI: Getting device configuration space for {}", address);
|
||||||
|
map_bus_region(address.domain(), address.bus());
|
||||||
|
return m_mapped_bus_region->vaddr().offset(PCI_MMIO_CONFIG_SPACE_SIZE * address.function() + (PCI_MMIO_CONFIG_SPACE_SIZE * PCI_MAX_FUNCTIONS_PER_DEVICE) * address.device());
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 Access::io_read8_field(Address address, u32 field)
|
||||||
|
{
|
||||||
|
MutexLocker lock(m_access_lock);
|
||||||
|
dbgln_if(PCI_DEBUG, "PCI: IO Reading 8-bit field {:#08x} for {}", field, address);
|
||||||
|
IO::out32(PCI_ADDRESS_PORT, address.io_address_for_field(field));
|
||||||
|
return IO::in8(PCI_VALUE_PORT + (field & 3));
|
||||||
|
}
|
||||||
|
u16 Access::io_read16_field(Address address, u32 field)
|
||||||
|
{
|
||||||
|
MutexLocker lock(m_access_lock);
|
||||||
|
dbgln_if(PCI_DEBUG, "PCI: IO Reading 16-bit field {:#08x} for {}", field, address);
|
||||||
|
IO::out32(PCI_ADDRESS_PORT, address.io_address_for_field(field));
|
||||||
|
return IO::in16(PCI_VALUE_PORT + (field & 2));
|
||||||
|
}
|
||||||
|
u32 Access::io_read32_field(Address address, u32 field)
|
||||||
|
{
|
||||||
|
MutexLocker lock(m_access_lock);
|
||||||
|
dbgln_if(PCI_DEBUG, "PCI: IO Reading 32-bit field {:#08x} for {}", field, address);
|
||||||
|
IO::out32(PCI_ADDRESS_PORT, address.io_address_for_field(field));
|
||||||
|
return IO::in32(PCI_VALUE_PORT);
|
||||||
|
}
|
||||||
|
void Access::io_write8_field(Address address, u32 field, u8 value)
|
||||||
|
{
|
||||||
|
MutexLocker lock(m_access_lock);
|
||||||
|
dbgln_if(PCI_DEBUG, "PCI: IO Writing to 8-bit field {:#08x}, value={:#02x} for {}", field, value, address);
|
||||||
|
IO::out32(PCI_ADDRESS_PORT, address.io_address_for_field(field));
|
||||||
|
IO::out8(PCI_VALUE_PORT + (field & 3), value);
|
||||||
|
}
|
||||||
|
void Access::io_write16_field(Address address, u32 field, u16 value)
|
||||||
|
{
|
||||||
|
MutexLocker lock(m_access_lock);
|
||||||
|
dbgln_if(PCI_DEBUG, "PCI: IO Writing to 16-bit field {:#08x}, value={:#02x} for {}", field, value, address);
|
||||||
|
IO::out32(PCI_ADDRESS_PORT, address.io_address_for_field(field));
|
||||||
|
IO::out16(PCI_VALUE_PORT + (field & 2), value);
|
||||||
|
}
|
||||||
|
void Access::io_write32_field(Address address, u32 field, u32 value)
|
||||||
|
{
|
||||||
|
MutexLocker lock(m_access_lock);
|
||||||
|
dbgln_if(PCI_DEBUG, "PCI: IO Writing to 32-bit field {:#08x}, value={:#02x} for {}", field, value, address);
|
||||||
|
IO::out32(PCI_ADDRESS_PORT, address.io_address_for_field(field));
|
||||||
|
IO::out32(PCI_VALUE_PORT, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 Access::memory_read8_field(Address address, u32 field)
|
||||||
|
{
|
||||||
|
MutexLocker lock(m_access_lock);
|
||||||
|
VERIFY(field <= 0xfff);
|
||||||
|
dbgln_if(PCI_DEBUG, "PCI: MMIO Reading 8-bit field {:#08x} for {}", field, address);
|
||||||
|
return *((volatile u8*)(get_device_configuration_memory_mapped_space(address).get() + (field & 0xfff)));
|
||||||
|
}
|
||||||
|
u16 Access::memory_read16_field(Address address, u32 field)
|
||||||
|
{
|
||||||
|
MutexLocker lock(m_access_lock);
|
||||||
|
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_memory_mapped_space(address).offset(field & 0xfff).as_ptr(), data);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
u32 Access::memory_read32_field(Address address, u32 field)
|
||||||
|
{
|
||||||
|
MutexLocker lock(m_access_lock);
|
||||||
|
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_memory_mapped_space(address).offset(field & 0xfff).as_ptr(), data);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
void Access::memory_write8_field(Address address, u32 field, u8 value)
|
||||||
|
{
|
||||||
|
MutexLocker lock(m_access_lock);
|
||||||
|
VERIFY(field <= 0xfff);
|
||||||
|
dbgln_if(PCI_DEBUG, "PCI: MMIO Writing 8-bit field {:#08x}, value={:#02x} for {}", field, value, address);
|
||||||
|
*((volatile u8*)(get_device_configuration_memory_mapped_space(address).get() + (field & 0xfff))) = value;
|
||||||
|
}
|
||||||
|
void Access::memory_write16_field(Address address, u32 field, u16 value)
|
||||||
|
{
|
||||||
|
MutexLocker lock(m_access_lock);
|
||||||
|
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_memory_mapped_space(address).offset(field & 0xfff).as_ptr(), value);
|
||||||
|
}
|
||||||
|
void Access::memory_write32_field(Address address, u32 field, u32 value)
|
||||||
|
{
|
||||||
|
MutexLocker lock(m_access_lock);
|
||||||
|
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_memory_mapped_space(address).offset(field & 0xfff).as_ptr(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Access::write8_field(Address address, u32 field, u8 value)
|
||||||
|
{
|
||||||
|
switch (m_access_type) {
|
||||||
|
case AccessType::IO:
|
||||||
|
io_write8_field(address, field, value);
|
||||||
|
return;
|
||||||
|
case AccessType::Memory:
|
||||||
|
memory_write8_field(address, field, value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
VERIFY_NOT_REACHED();
|
||||||
|
}
|
||||||
|
void Access::write16_field(Address address, u32 field, u16 value)
|
||||||
|
{
|
||||||
|
switch (m_access_type) {
|
||||||
|
case AccessType::IO:
|
||||||
|
io_write16_field(address, field, value);
|
||||||
|
return;
|
||||||
|
case AccessType::Memory:
|
||||||
|
memory_write16_field(address, field, value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
VERIFY_NOT_REACHED();
|
||||||
|
}
|
||||||
|
void Access::write32_field(Address address, u32 field, u32 value)
|
||||||
|
{
|
||||||
|
switch (m_access_type) {
|
||||||
|
case AccessType::IO:
|
||||||
|
io_write32_field(address, field, value);
|
||||||
|
return;
|
||||||
|
case AccessType::Memory:
|
||||||
|
memory_write32_field(address, field, value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
VERIFY_NOT_REACHED();
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 Access::read8_field(Address address, u32 field)
|
||||||
|
{
|
||||||
|
switch (m_access_type) {
|
||||||
|
case AccessType::IO:
|
||||||
|
return io_read8_field(address, field);
|
||||||
|
case AccessType::Memory:
|
||||||
|
return memory_read8_field(address, field);
|
||||||
|
}
|
||||||
|
VERIFY_NOT_REACHED();
|
||||||
|
}
|
||||||
|
u16 Access::read16_field(Address address, u32 field)
|
||||||
|
{
|
||||||
|
switch (m_access_type) {
|
||||||
|
case AccessType::IO:
|
||||||
|
return io_read16_field(address, field);
|
||||||
|
case AccessType::Memory:
|
||||||
|
return memory_read16_field(address, field);
|
||||||
|
}
|
||||||
|
VERIFY_NOT_REACHED();
|
||||||
|
}
|
||||||
|
u32 Access::read32_field(Address address, u32 field)
|
||||||
|
{
|
||||||
|
switch (m_access_type) {
|
||||||
|
case AccessType::IO:
|
||||||
|
return io_read32_field(address, field);
|
||||||
|
case AccessType::Memory:
|
||||||
|
return memory_read32_field(address, field);
|
||||||
|
}
|
||||||
|
VERIFY_NOT_REACHED();
|
||||||
|
}
|
||||||
|
|
||||||
|
UNMAP_AFTER_INIT void Access::rescan_hardware()
|
||||||
|
{
|
||||||
|
MutexLocker locker(m_scan_lock);
|
||||||
|
VERIFY(m_physical_ids.is_empty());
|
||||||
|
if (m_access_type == AccessType::IO) {
|
||||||
|
dbgln_if(PCI_DEBUG, "PCI: IO enumerating hardware");
|
||||||
|
|
||||||
|
// First scan bus 0. Find any device on that bus, and if it's a PCI-to-PCI
|
||||||
|
// bridge, recursively scan it too.
|
||||||
|
m_enumerated_buses.set(0, true);
|
||||||
|
enumerate_bus(-1, 0, true);
|
||||||
|
|
||||||
|
// Handle Multiple PCI host bridges on slot 0, device 0.
|
||||||
|
// If we happen to miss some PCI buses because they are not reachable through
|
||||||
|
// recursive PCI-to-PCI bridges starting from bus 0, we might find them here.
|
||||||
|
if ((read8_field(Address(), PCI_HEADER_TYPE) & 0x80) != 0) {
|
||||||
|
for (int bus = 1; bus < 256; ++bus) {
|
||||||
|
if (read16_field(Address(0, 0, 0, bus), PCI_VENDOR_ID) == PCI_NONE)
|
||||||
|
continue;
|
||||||
|
if (read16_field(Address(0, 0, 0, bus), PCI_CLASS) != 0x6)
|
||||||
|
continue;
|
||||||
|
if (m_enumerated_buses.get(bus))
|
||||||
|
continue;
|
||||||
|
enumerate_bus(-1, bus, false);
|
||||||
|
m_enumerated_buses.set(bus, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
VERIFY(m_access_type == AccessType::Memory);
|
||||||
|
|
||||||
|
for (u32 domain = 0; domain < m_domains.size(); domain++) {
|
||||||
|
dbgln_if(PCI_DEBUG, "PCI: Scan memory mapped domain {}", domain);
|
||||||
|
// Single PCI host controller.
|
||||||
|
if ((read8_field(Address(domain), PCI_HEADER_TYPE) & 0x80) == 0) {
|
||||||
|
enumerate_bus(-1, 0, true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Multiple PCI host controllers.
|
||||||
|
for (u8 function = 0; function < 8; ++function) {
|
||||||
|
if (read16_field(Address(domain, 0, 0, function), PCI_VENDOR_ID) == PCI_NONE)
|
||||||
|
break;
|
||||||
|
enumerate_bus(-1, function, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UNMAP_AFTER_INIT Optional<u8> Access::get_capabilities_pointer(Address address)
|
||||||
|
{
|
||||||
|
dbgln_if(PCI_DEBUG, "PCI: Getting capabilities pointer for {}", address);
|
||||||
|
if (read16_field(address, PCI_STATUS) & (1 << 4)) {
|
||||||
|
dbgln_if(PCI_DEBUG, "PCI: Found capabilities pointer for {}", address);
|
||||||
|
return read8_field(address, PCI_CAPABILITIES_POINTER);
|
||||||
|
}
|
||||||
|
dbgln_if(PCI_DEBUG, "PCI: No capabilities pointer for {}", address);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
UNMAP_AFTER_INIT Vector<Capability> Access::get_capabilities(Address address)
|
||||||
|
{
|
||||||
|
dbgln_if(PCI_DEBUG, "PCI: Getting capabilities for {}", address);
|
||||||
|
auto capabilities_pointer = get_capabilities_pointer(address);
|
||||||
|
if (!capabilities_pointer.has_value()) {
|
||||||
|
dbgln_if(PCI_DEBUG, "PCI: No capabilities for {}", address);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
Vector<Capability> capabilities;
|
||||||
|
auto capability_pointer = capabilities_pointer.value();
|
||||||
|
while (capability_pointer != 0) {
|
||||||
|
dbgln_if(PCI_DEBUG, "PCI: Reading in capability at {:#02x} for {}", capability_pointer, address);
|
||||||
|
u16 capability_header = read16_field(address, capability_pointer);
|
||||||
|
u8 capability_id = capability_header & 0xff;
|
||||||
|
capabilities.append({ address, capability_id, capability_pointer });
|
||||||
|
capability_pointer = capability_header >> 8;
|
||||||
|
}
|
||||||
|
return capabilities;
|
||||||
|
}
|
||||||
|
|
||||||
|
UNMAP_AFTER_INIT void Access::enumerate_functions(int type, u8 bus, u8 device, u8 function, bool recursive)
|
||||||
|
{
|
||||||
|
dbgln_if(PCI_DEBUG, "PCI: Enumerating function type={}, bus={}, device={}, function={}", type, bus, device, function);
|
||||||
|
Address address(0, bus, device, function);
|
||||||
|
auto read_type = (read8_field(address, PCI_CLASS) << 8u) | read8_field(address, PCI_SUBCLASS);
|
||||||
|
if (type == -1 || type == read_type) {
|
||||||
|
m_physical_ids.append(PhysicalID { address, { read16_field(address, PCI_VENDOR_ID), read16_field(address, PCI_DEVICE_ID) }, get_capabilities(address) });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (read_type == PCI_TYPE_BRIDGE && recursive && (!m_enumerated_buses.get(read8_field(address, PCI_SECONDARY_BUS)))) {
|
||||||
|
u8 secondary_bus = read8_field(address, PCI_SECONDARY_BUS);
|
||||||
|
dbgln_if(PCI_DEBUG, "PCI: Found secondary bus: {}", secondary_bus);
|
||||||
|
VERIFY(secondary_bus != bus);
|
||||||
|
m_enumerated_buses.set(secondary_bus, true);
|
||||||
|
enumerate_bus(type, secondary_bus, recursive);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UNMAP_AFTER_INIT void Access::enumerate_device(int type, u8 bus, u8 device, bool recursive)
|
||||||
|
{
|
||||||
|
dbgln_if(PCI_DEBUG, "PCI: Enumerating device type={}, bus={}, device={}", type, bus, device);
|
||||||
|
Address address(0, bus, device, 0);
|
||||||
|
if (read16_field(address, PCI_VENDOR_ID) == PCI_NONE)
|
||||||
|
return;
|
||||||
|
enumerate_functions(type, bus, device, 0, recursive);
|
||||||
|
if (!(read8_field(address, PCI_HEADER_TYPE) & 0x80))
|
||||||
|
return;
|
||||||
|
for (u8 function = 1; function < 8; ++function) {
|
||||||
|
Address address(0, bus, device, function);
|
||||||
|
if (read16_field(address, PCI_VENDOR_ID) != PCI_NONE)
|
||||||
|
enumerate_functions(type, bus, device, function, recursive);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UNMAP_AFTER_INIT void Access::enumerate_bus(int type, u8 bus, bool recursive)
|
||||||
|
{
|
||||||
|
dbgln_if(PCI_DEBUG, "PCI: Enumerating bus type={}, bus={}", type, bus);
|
||||||
|
for (u8 device = 0; device < 32; ++device)
|
||||||
|
enumerate_device(type, bus, device, recursive);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Access::fast_enumerate(Function<void(Address, ID)>& callback) const
|
||||||
|
{
|
||||||
|
MutexLocker locker(m_scan_lock);
|
||||||
|
VERIFY(!m_physical_ids.is_empty());
|
||||||
|
for (auto& physical_id : m_physical_ids) {
|
||||||
|
callback(physical_id.address(), physical_id.id());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
PhysicalID Access::get_physical_id(Address address) const
|
PhysicalID Access::get_physical_id(Address address) const
|
||||||
{
|
{
|
||||||
for (auto physical_id : m_physical_ids) {
|
for (auto physical_id : m_physical_ids) {
|
||||||
if (physical_id.address().seg() == address.seg()
|
if (physical_id.address().domain() == address.domain()
|
||||||
&& physical_id.address().bus() == address.bus()
|
&& physical_id.address().bus() == address.bus()
|
||||||
&& physical_id.address().device() == address.device()
|
&& physical_id.address().device() == address.device()
|
||||||
&& physical_id.address().function() == address.function()) {
|
&& physical_id.address().function() == address.function()) {
|
||||||
|
@ -54,400 +438,4 @@ PhysicalID Access::get_physical_id(Address address) const
|
||||||
VERIFY_NOT_REACHED();
|
VERIFY_NOT_REACHED();
|
||||||
}
|
}
|
||||||
|
|
||||||
u8 Access::early_read8_field(Address address, u32 field)
|
|
||||||
{
|
|
||||||
dbgln_if(PCI_DEBUG, "PCI: Early reading 8-bit field {:#08x} for {}", field, address);
|
|
||||||
IO::out32(PCI_ADDRESS_PORT, address.io_address_for_field(field));
|
|
||||||
return IO::in8(PCI_VALUE_PORT + (field & 3));
|
|
||||||
}
|
|
||||||
|
|
||||||
u16 Access::early_read16_field(Address address, u32 field)
|
|
||||||
{
|
|
||||||
dbgln_if(PCI_DEBUG, "PCI: Early reading 16-bit field {:#08x} for {}", field, address);
|
|
||||||
IO::out32(PCI_ADDRESS_PORT, address.io_address_for_field(field));
|
|
||||||
return IO::in16(PCI_VALUE_PORT + (field & 2));
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 Access::early_read32_field(Address address, u32 field)
|
|
||||||
{
|
|
||||||
dbgln_if(PCI_DEBUG, "PCI: Early reading 32-bit field {:#08x} for {}", field, address);
|
|
||||||
IO::out32(PCI_ADDRESS_PORT, address.io_address_for_field(field));
|
|
||||||
return IO::in32(PCI_VALUE_PORT);
|
|
||||||
}
|
|
||||||
|
|
||||||
u16 Access::early_read_type(Address address)
|
|
||||||
{
|
|
||||||
dbgln_if(PCI_DEBUG, "PCI: Early reading type for {}", address);
|
|
||||||
return (early_read8_field(address, PCI_CLASS) << 8u) | early_read8_field(address, PCI_SUBCLASS);
|
|
||||||
}
|
|
||||||
|
|
||||||
UNMAP_AFTER_INIT void Access::enumerate_functions(int type, u8 bus, u8 device, u8 function, Function<void(Address, ID)>& callback, bool recursive)
|
|
||||||
{
|
|
||||||
dbgln_if(PCI_DEBUG, "PCI: Enumerating function type={}, bus={}, device={}, function={}", type, bus, device, function);
|
|
||||||
Address address(0, bus, device, function);
|
|
||||||
if (type == -1 || type == early_read_type(address))
|
|
||||||
callback(address, { early_read16_field(address, PCI_VENDOR_ID), early_read16_field(address, PCI_DEVICE_ID) });
|
|
||||||
if (early_read_type(address) == PCI_TYPE_BRIDGE && recursive && (!m_enumerated_buses.get(early_read8_field(address, PCI_SECONDARY_BUS)))) {
|
|
||||||
u8 secondary_bus = early_read8_field(address, PCI_SECONDARY_BUS);
|
|
||||||
dbgln_if(PCI_DEBUG, "PCI: Found secondary bus: {}", secondary_bus);
|
|
||||||
VERIFY(secondary_bus != bus);
|
|
||||||
m_enumerated_buses.set(secondary_bus, true);
|
|
||||||
enumerate_bus(type, secondary_bus, callback, recursive);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
UNMAP_AFTER_INIT void Access::enumerate_device(int type, u8 bus, u8 device, Function<void(Address, ID)>& callback, bool recursive)
|
|
||||||
{
|
|
||||||
dbgln_if(PCI_DEBUG, "PCI: Enumerating device type={}, bus={}, device={}", type, bus, device);
|
|
||||||
Address address(0, bus, device, 0);
|
|
||||||
if (early_read16_field(address, PCI_VENDOR_ID) == PCI_NONE)
|
|
||||||
return;
|
|
||||||
enumerate_functions(type, bus, device, 0, callback, recursive);
|
|
||||||
if (!(early_read8_field(address, PCI_HEADER_TYPE) & 0x80))
|
|
||||||
return;
|
|
||||||
for (u8 function = 1; function < 8; ++function) {
|
|
||||||
Address address(0, bus, device, function);
|
|
||||||
if (early_read16_field(address, PCI_VENDOR_ID) != PCI_NONE)
|
|
||||||
enumerate_functions(type, bus, device, function, callback, recursive);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
UNMAP_AFTER_INIT void Access::enumerate_bus(int type, u8 bus, Function<void(Address, ID)>& callback, bool recursive)
|
|
||||||
{
|
|
||||||
dbgln_if(PCI_DEBUG, "PCI: Enumerating bus type={}, bus={}", type, bus);
|
|
||||||
for (u8 device = 0; device < 32; ++device)
|
|
||||||
enumerate_device(type, bus, device, callback, recursive);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Access::enumerate(Function<void(Address, ID)>& callback) const
|
|
||||||
{
|
|
||||||
for (auto& physical_id : m_physical_ids) {
|
|
||||||
callback(physical_id.address(), physical_id.id());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void enumerate(Function<void(Address, ID)> callback)
|
|
||||||
{
|
|
||||||
Access::the().enumerate(callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
UNMAP_AFTER_INIT Optional<u8> get_capabilities_pointer(Address address)
|
|
||||||
{
|
|
||||||
dbgln_if(PCI_DEBUG, "PCI: Getting capabilities pointer for {}", address);
|
|
||||||
if (PCI::read16(address, PCI_STATUS) & (1 << 4)) {
|
|
||||||
dbgln_if(PCI_DEBUG, "PCI: Found capabilities pointer for {}", address);
|
|
||||||
return PCI::read8(address, PCI_CAPABILITIES_POINTER);
|
|
||||||
}
|
|
||||||
dbgln_if(PCI_DEBUG, "PCI: No capabilities pointer for {}", address);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
PhysicalID get_physical_id(Address address)
|
|
||||||
{
|
|
||||||
return Access::the().get_physical_id(address);
|
|
||||||
}
|
|
||||||
|
|
||||||
UNMAP_AFTER_INIT Vector<Capability> get_capabilities(Address address)
|
|
||||||
{
|
|
||||||
dbgln_if(PCI_DEBUG, "PCI: Getting capabilities for {}", address);
|
|
||||||
auto capabilities_pointer = PCI::get_capabilities_pointer(address);
|
|
||||||
if (!capabilities_pointer.has_value()) {
|
|
||||||
dbgln_if(PCI_DEBUG, "PCI: No capabilities for {}", address);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
Vector<Capability> capabilities;
|
|
||||||
auto capability_pointer = capabilities_pointer.value();
|
|
||||||
while (capability_pointer != 0) {
|
|
||||||
dbgln_if(PCI_DEBUG, "PCI: Reading in capability at {:#02x} for {}", capability_pointer, address);
|
|
||||||
u16 capability_header = PCI::read16(address, capability_pointer);
|
|
||||||
u8 capability_id = capability_header & 0xff;
|
|
||||||
capabilities.append({ address, capability_id, capability_pointer });
|
|
||||||
capability_pointer = capability_header >> 8;
|
|
||||||
}
|
|
||||||
return capabilities;
|
|
||||||
}
|
|
||||||
|
|
||||||
void raw_access(Address address, u32 field, size_t access_size, u32 value)
|
|
||||||
{
|
|
||||||
VERIFY(access_size != 0);
|
|
||||||
if (access_size == 1) {
|
|
||||||
write8(address, field, value);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (access_size == 2) {
|
|
||||||
write16(address, field, value);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (access_size == 4) {
|
|
||||||
write32(address, field, value);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
VERIFY_NOT_REACHED();
|
|
||||||
}
|
|
||||||
|
|
||||||
ID get_id(Address address)
|
|
||||||
{
|
|
||||||
return { read16(address, PCI_VENDOR_ID), read16(address, PCI_DEVICE_ID) };
|
|
||||||
}
|
|
||||||
|
|
||||||
void enable_io_space(Address address)
|
|
||||||
{
|
|
||||||
write16(address, PCI_COMMAND, read16(address, PCI_COMMAND) | (1 << 0));
|
|
||||||
}
|
|
||||||
void disable_io_space(Address address)
|
|
||||||
{
|
|
||||||
write16(address, PCI_COMMAND, read16(address, PCI_COMMAND) & ~(1 << 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
void enable_memory_space(Address address)
|
|
||||||
{
|
|
||||||
write16(address, PCI_COMMAND, read16(address, PCI_COMMAND) | (1 << 1));
|
|
||||||
}
|
|
||||||
void disable_memory_space(Address address)
|
|
||||||
{
|
|
||||||
write16(address, PCI_COMMAND, read16(address, PCI_COMMAND) & ~(1 << 1));
|
|
||||||
}
|
|
||||||
bool is_io_space_enabled(Address address)
|
|
||||||
{
|
|
||||||
return (read16(address, PCI_COMMAND) & 1) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void enable_interrupt_line(Address address)
|
|
||||||
{
|
|
||||||
write16(address, PCI_COMMAND, read16(address, PCI_COMMAND) & ~(1 << 10));
|
|
||||||
}
|
|
||||||
|
|
||||||
void disable_interrupt_line(Address address)
|
|
||||||
{
|
|
||||||
write16(address, PCI_COMMAND, read16(address, PCI_COMMAND) | 1 << 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
u8 get_interrupt_line(Address address)
|
|
||||||
{
|
|
||||||
return read8(address, PCI_INTERRUPT_LINE);
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 get_BAR0(Address address)
|
|
||||||
{
|
|
||||||
return read32(address, PCI_BAR0);
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 get_BAR1(Address address)
|
|
||||||
{
|
|
||||||
return read32(address, PCI_BAR1);
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 get_BAR2(Address address)
|
|
||||||
{
|
|
||||||
return read32(address, PCI_BAR2);
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 get_BAR3(Address address)
|
|
||||||
{
|
|
||||||
return read16(address, PCI_BAR3);
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 get_BAR4(Address address)
|
|
||||||
{
|
|
||||||
return read32(address, PCI_BAR4);
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 get_BAR5(Address address)
|
|
||||||
{
|
|
||||||
return read32(address, PCI_BAR5);
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 get_BAR(Address address, u8 bar)
|
|
||||||
{
|
|
||||||
VERIFY(bar <= 5);
|
|
||||||
switch (bar) {
|
|
||||||
case 0:
|
|
||||||
return get_BAR0(address);
|
|
||||||
case 1:
|
|
||||||
return get_BAR1(address);
|
|
||||||
case 2:
|
|
||||||
return get_BAR2(address);
|
|
||||||
case 3:
|
|
||||||
return get_BAR3(address);
|
|
||||||
case 4:
|
|
||||||
return get_BAR4(address);
|
|
||||||
case 5:
|
|
||||||
return get_BAR5(address);
|
|
||||||
default:
|
|
||||||
VERIFY_NOT_REACHED();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
u8 get_revision_id(Address address)
|
|
||||||
{
|
|
||||||
return read8(address, PCI_REVISION_ID);
|
|
||||||
}
|
|
||||||
|
|
||||||
u8 get_subclass(Address address)
|
|
||||||
{
|
|
||||||
return read8(address, PCI_SUBCLASS);
|
|
||||||
}
|
|
||||||
|
|
||||||
u8 get_class(Address address)
|
|
||||||
{
|
|
||||||
return read8(address, PCI_CLASS);
|
|
||||||
}
|
|
||||||
|
|
||||||
u8 get_programming_interface(Address address)
|
|
||||||
{
|
|
||||||
return read8(address, PCI_PROG_IF);
|
|
||||||
}
|
|
||||||
|
|
||||||
u16 get_subsystem_id(Address address)
|
|
||||||
{
|
|
||||||
return read16(address, PCI_SUBSYSTEM_ID);
|
|
||||||
}
|
|
||||||
|
|
||||||
u16 get_subsystem_vendor_id(Address address)
|
|
||||||
{
|
|
||||||
return read16(address, PCI_SUBSYSTEM_VENDOR_ID);
|
|
||||||
}
|
|
||||||
|
|
||||||
void enable_bus_mastering(Address address)
|
|
||||||
{
|
|
||||||
auto value = read16(address, PCI_COMMAND);
|
|
||||||
value |= (1 << 2);
|
|
||||||
value |= (1 << 0);
|
|
||||||
write16(address, PCI_COMMAND, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
void disable_bus_mastering(Address address)
|
|
||||||
{
|
|
||||||
auto value = read16(address, PCI_COMMAND);
|
|
||||||
value &= ~(1 << 2);
|
|
||||||
value |= (1 << 0);
|
|
||||||
write16(address, PCI_COMMAND, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t get_BAR_space_size(Address address, u8 bar_number)
|
|
||||||
{
|
|
||||||
// See PCI Spec 2.3, Page 222
|
|
||||||
VERIFY(bar_number < 6);
|
|
||||||
u8 field = (PCI_BAR0 + (bar_number << 2));
|
|
||||||
u32 bar_reserved = read32(address, field);
|
|
||||||
write32(address, field, 0xFFFFFFFF);
|
|
||||||
u32 space_size = read32(address, field);
|
|
||||||
write32(address, field, bar_reserved);
|
|
||||||
space_size &= 0xfffffff0;
|
|
||||||
space_size = (~space_size) + 1;
|
|
||||||
return space_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
u8 Capability::read8(u32 field) const
|
|
||||||
{
|
|
||||||
return PCI::read8(m_address, m_ptr + field);
|
|
||||||
}
|
|
||||||
|
|
||||||
u16 Capability::read16(u32 field) const
|
|
||||||
{
|
|
||||||
return PCI::read16(m_address, m_ptr + field);
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 Capability::read32(u32 field) const
|
|
||||||
{
|
|
||||||
return PCI::read32(m_address, m_ptr + field);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Capability::write8(u32 field, u8 value)
|
|
||||||
{
|
|
||||||
PCI::write8(m_address, m_ptr + field, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Capability::write16(u32 field, u16 value)
|
|
||||||
{
|
|
||||||
PCI::write16(m_address, m_ptr + field, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Capability::write32(u32 field, u32 value)
|
|
||||||
{
|
|
||||||
PCI::write32(m_address, m_ptr + field, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
UNMAP_AFTER_INIT NonnullRefPtr<PCIDeviceSysFSDirectory> PCIDeviceSysFSDirectory::create(const SysFSDirectory& parent_directory, Address address)
|
|
||||||
{
|
|
||||||
return adopt_ref(*new (nothrow) PCIDeviceSysFSDirectory(parent_directory, address));
|
|
||||||
}
|
|
||||||
|
|
||||||
UNMAP_AFTER_INIT PCIDeviceSysFSDirectory::PCIDeviceSysFSDirectory(const SysFSDirectory& parent_directory, Address address)
|
|
||||||
: SysFSDirectory(String::formatted("{:04x}:{:02x}:{:02x}.{}", address.seg(), address.bus(), address.device(), address.function()), parent_directory)
|
|
||||||
, m_address(address)
|
|
||||||
{
|
|
||||||
m_components.append(PCIDeviceAttributeSysFSComponent::create("vendor", *this, PCI_VENDOR_ID, 2));
|
|
||||||
m_components.append(PCIDeviceAttributeSysFSComponent::create("device_id", *this, PCI_DEVICE_ID, 2));
|
|
||||||
m_components.append(PCIDeviceAttributeSysFSComponent::create("class", *this, PCI_CLASS, 1));
|
|
||||||
m_components.append(PCIDeviceAttributeSysFSComponent::create("subclass", *this, PCI_SUBCLASS, 1));
|
|
||||||
m_components.append(PCIDeviceAttributeSysFSComponent::create("revision", *this, PCI_REVISION_ID, 1));
|
|
||||||
m_components.append(PCIDeviceAttributeSysFSComponent::create("progif", *this, PCI_PROG_IF, 1));
|
|
||||||
m_components.append(PCIDeviceAttributeSysFSComponent::create("subsystem_vendor", *this, PCI_SUBSYSTEM_VENDOR_ID, 2));
|
|
||||||
m_components.append(PCIDeviceAttributeSysFSComponent::create("subsystem_id", *this, PCI_SUBSYSTEM_ID, 2));
|
|
||||||
}
|
|
||||||
|
|
||||||
UNMAP_AFTER_INIT void PCIBusSysFSDirectory::initialize()
|
|
||||||
{
|
|
||||||
auto pci_directory = adopt_ref(*new (nothrow) PCIBusSysFSDirectory());
|
|
||||||
SysFSComponentRegistry::the().register_new_component(pci_directory);
|
|
||||||
}
|
|
||||||
|
|
||||||
UNMAP_AFTER_INIT PCIBusSysFSDirectory::PCIBusSysFSDirectory()
|
|
||||||
: SysFSDirectory("pci", SysFSComponentRegistry::the().root_directory())
|
|
||||||
{
|
|
||||||
PCI::enumerate([&](const Address& address, ID) {
|
|
||||||
auto pci_device = PCI::PCIDeviceSysFSDirectory::create(*this, address);
|
|
||||||
m_components.append(pci_device);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
NonnullRefPtr<PCIDeviceAttributeSysFSComponent> PCIDeviceAttributeSysFSComponent::create(String name, const PCIDeviceSysFSDirectory& device, size_t offset, size_t field_bytes_width)
|
|
||||||
{
|
|
||||||
return adopt_ref(*new (nothrow) PCIDeviceAttributeSysFSComponent(name, device, offset, field_bytes_width));
|
|
||||||
}
|
|
||||||
|
|
||||||
PCIDeviceAttributeSysFSComponent::PCIDeviceAttributeSysFSComponent(String name, const PCIDeviceSysFSDirectory& device, size_t offset, size_t field_bytes_width)
|
|
||||||
: SysFSComponent(name)
|
|
||||||
, m_device(device)
|
|
||||||
, m_offset(offset)
|
|
||||||
, m_field_bytes_width(field_bytes_width)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
KResultOr<size_t> PCIDeviceAttributeSysFSComponent::read_bytes(off_t offset, size_t count, UserOrKernelBuffer& buffer, FileDescription*) const
|
|
||||||
{
|
|
||||||
auto blob = try_to_generate_buffer();
|
|
||||||
if (!blob)
|
|
||||||
return KResult(EFAULT);
|
|
||||||
|
|
||||||
if ((size_t)offset >= blob->size())
|
|
||||||
return KSuccess;
|
|
||||||
|
|
||||||
ssize_t nread = min(static_cast<off_t>(blob->size() - offset), static_cast<off_t>(count));
|
|
||||||
if (!buffer.write(blob->data() + offset, nread))
|
|
||||||
return KResult(EFAULT);
|
|
||||||
return nread;
|
|
||||||
}
|
|
||||||
|
|
||||||
OwnPtr<KBuffer> PCIDeviceAttributeSysFSComponent::try_to_generate_buffer() const
|
|
||||||
{
|
|
||||||
String value;
|
|
||||||
switch (m_field_bytes_width) {
|
|
||||||
case 1:
|
|
||||||
value = String::formatted("{:#x}", PCI::read8(m_device->address(), m_offset));
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
value = String::formatted("{:#x}", PCI::read16(m_device->address(), m_offset));
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
value = String::formatted("{:#x}", PCI::read32(m_device->address(), m_offset));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
VERIFY_NOT_REACHED();
|
|
||||||
}
|
|
||||||
|
|
||||||
return KBuffer::try_create_with_bytes(value.substring_view(0).bytes());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,77 +14,72 @@
|
||||||
|
|
||||||
namespace Kernel::PCI {
|
namespace Kernel::PCI {
|
||||||
|
|
||||||
class PCIBusSysFSDirectory final : public SysFSDirectory {
|
|
||||||
public:
|
|
||||||
static void initialize();
|
|
||||||
|
|
||||||
private:
|
|
||||||
PCIBusSysFSDirectory();
|
|
||||||
};
|
|
||||||
|
|
||||||
class PCIDeviceSysFSDirectory final : public SysFSDirectory {
|
|
||||||
public:
|
|
||||||
static NonnullRefPtr<PCIDeviceSysFSDirectory> create(const SysFSDirectory&, Address);
|
|
||||||
const Address& address() const { return m_address; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
PCIDeviceSysFSDirectory(const SysFSDirectory&, Address);
|
|
||||||
|
|
||||||
Address m_address;
|
|
||||||
};
|
|
||||||
|
|
||||||
class PCIDeviceAttributeSysFSComponent : public SysFSComponent {
|
|
||||||
public:
|
|
||||||
static NonnullRefPtr<PCIDeviceAttributeSysFSComponent> create(String name, const PCIDeviceSysFSDirectory& device, size_t offset, size_t field_bytes_width);
|
|
||||||
|
|
||||||
virtual KResultOr<size_t> read_bytes(off_t, size_t, UserOrKernelBuffer&, FileDescription*) const override;
|
|
||||||
virtual ~PCIDeviceAttributeSysFSComponent() {};
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual OwnPtr<KBuffer> try_to_generate_buffer() const;
|
|
||||||
PCIDeviceAttributeSysFSComponent(String name, const PCIDeviceSysFSDirectory& device, size_t offset, size_t field_bytes_width);
|
|
||||||
NonnullRefPtr<PCIDeviceSysFSDirectory> m_device;
|
|
||||||
size_t m_offset;
|
|
||||||
size_t m_field_bytes_width;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Access {
|
class Access {
|
||||||
public:
|
public:
|
||||||
void enumerate(Function<void(Address, ID)>&) const;
|
enum class AccessType {
|
||||||
|
IO,
|
||||||
|
Memory,
|
||||||
|
};
|
||||||
|
|
||||||
void enumerate_bus(int type, u8 bus, Function<void(Address, ID)>&, bool recursive);
|
public:
|
||||||
void enumerate_functions(int type, u8 bus, u8 device, u8 function, Function<void(Address, ID)>& callback, bool recursive);
|
static bool initialize_for_memory_access(PhysicalAddress mcfg_table);
|
||||||
void enumerate_device(int type, u8 bus, u8 device, Function<void(Address, ID)>& callback, bool recursive);
|
static bool initialize_for_io_access();
|
||||||
|
|
||||||
|
void fast_enumerate(Function<void(Address, ID)>&) const;
|
||||||
|
void rescan_hardware();
|
||||||
|
|
||||||
static Access& the();
|
static Access& the();
|
||||||
static bool is_initialized();
|
static bool is_initialized();
|
||||||
virtual uint32_t segment_count() const = 0;
|
|
||||||
virtual uint8_t segment_start_bus(u32 segment) const = 0;
|
|
||||||
virtual uint8_t segment_end_bus(u32 segment) const = 0;
|
|
||||||
|
|
||||||
virtual void write8_field(Address address, u32 field, u8 value) = 0;
|
|
||||||
virtual void write16_field(Address address, u32 field, u16 value) = 0;
|
|
||||||
virtual void write32_field(Address address, u32 field, u32 value) = 0;
|
|
||||||
|
|
||||||
virtual u8 read8_field(Address address, u32 field) = 0;
|
|
||||||
virtual u16 read16_field(Address address, u32 field) = 0;
|
|
||||||
virtual u32 read32_field(Address address, u32 field) = 0;
|
|
||||||
|
|
||||||
|
void write8_field(Address address, u32 field, u8 value);
|
||||||
|
void write16_field(Address address, u32 field, u16 value);
|
||||||
|
void write32_field(Address address, u32 field, u32 value);
|
||||||
|
u8 read8_field(Address address, u32 field);
|
||||||
|
u16 read16_field(Address address, u32 field);
|
||||||
|
u32 read32_field(Address address, u32 field);
|
||||||
PhysicalID get_physical_id(Address address) const;
|
PhysicalID get_physical_id(Address address) const;
|
||||||
|
|
||||||
protected:
|
private:
|
||||||
virtual void enumerate_hardware(Function<void(Address, ID)>) = 0;
|
void enumerate_bus(int type, u8 bus, bool recursive);
|
||||||
|
void enumerate_functions(int type, u8 bus, u8 device, u8 function, bool recursive);
|
||||||
|
void enumerate_device(int type, u8 bus, u8 device, bool recursive);
|
||||||
|
|
||||||
u8 early_read8_field(Address address, u32 field);
|
explicit Access(AccessType);
|
||||||
u16 early_read16_field(Address address, u32 field);
|
bool scan_pci_domains(PhysicalAddress mcfg);
|
||||||
u32 early_read32_field(Address address, u32 field);
|
Vector<Capability> get_capabilities(Address);
|
||||||
u16 early_read_type(Address address);
|
Optional<u8> get_capabilities_pointer(Address address);
|
||||||
|
|
||||||
Access();
|
// IO access (legacy) operations
|
||||||
virtual ~Access() = default;
|
u8 io_read8_field(Address address, u32 field);
|
||||||
|
u16 io_read16_field(Address address, u32 field);
|
||||||
|
u32 io_read32_field(Address address, u32 field);
|
||||||
|
void io_write8_field(Address address, u32, u8);
|
||||||
|
void io_write16_field(Address address, u32, u16);
|
||||||
|
void io_write32_field(Address address, u32, u32);
|
||||||
|
u16 io_read_type(Address address);
|
||||||
|
|
||||||
Vector<PhysicalID> m_physical_ids;
|
// Memory-mapped access operations
|
||||||
|
void map_bus_region(u32 domain, u8 bus);
|
||||||
|
u8 memory_read8_field(Address address, u32 field);
|
||||||
|
u16 memory_read16_field(Address address, u32 field);
|
||||||
|
u32 memory_read32_field(Address address, u32 field);
|
||||||
|
void memory_write8_field(Address address, u32, u8);
|
||||||
|
void memory_write16_field(Address address, u32, u16);
|
||||||
|
void memory_write32_field(Address address, u32, u32);
|
||||||
|
u16 memory_read_type(Address address);
|
||||||
|
VirtualAddress get_device_configuration_memory_mapped_space(Address address);
|
||||||
|
Optional<PhysicalAddress> determine_memory_mapped_bus_base_address(u32 domain, u8 bus) const;
|
||||||
|
|
||||||
|
// Data-members for accessing Memory mapped PCI devices' configuration spaces
|
||||||
|
u8 m_mapped_bus { 0 };
|
||||||
|
OwnPtr<Memory::Region> m_mapped_bus_region;
|
||||||
|
HashMap<u32, PCI::Domain> m_domains;
|
||||||
|
|
||||||
|
// General Data-members
|
||||||
|
mutable Mutex m_access_lock;
|
||||||
|
mutable Mutex m_scan_lock;
|
||||||
Bitmap m_enumerated_buses;
|
Bitmap m_enumerated_buses;
|
||||||
|
AccessType m_access_type;
|
||||||
|
Vector<PhysicalID> m_physical_ids;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,18 +75,37 @@ struct ID {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class Domain {
|
||||||
|
public:
|
||||||
|
Domain() = delete;
|
||||||
|
Domain(PhysicalAddress base_address, u8 start_bus, u8 end_bus)
|
||||||
|
: m_base_addr(base_address)
|
||||||
|
, m_start_bus(start_bus)
|
||||||
|
, m_end_bus(end_bus)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
u8 start_bus() const { return m_start_bus; }
|
||||||
|
u8 end_bus() const { return m_end_bus; }
|
||||||
|
PhysicalAddress paddr() const { return m_base_addr; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
PhysicalAddress m_base_addr;
|
||||||
|
u8 m_start_bus;
|
||||||
|
u8 m_end_bus;
|
||||||
|
};
|
||||||
|
|
||||||
struct Address {
|
struct Address {
|
||||||
public:
|
public:
|
||||||
Address() = default;
|
Address() = default;
|
||||||
Address(u16 seg)
|
Address(u32 domain)
|
||||||
: m_seg(seg)
|
: m_domain(domain)
|
||||||
, m_bus(0)
|
, m_bus(0)
|
||||||
, m_device(0)
|
, m_device(0)
|
||||||
, m_function(0)
|
, m_function(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
Address(u16 seg, u8 bus, u8 device, u8 function)
|
Address(u32 domain, u8 bus, u8 device, u8 function)
|
||||||
: m_seg(seg)
|
: m_domain(domain)
|
||||||
, m_bus(bus)
|
, m_bus(bus)
|
||||||
, m_device(device)
|
, m_device(device)
|
||||||
, m_function(function)
|
, m_function(function)
|
||||||
|
@ -94,7 +113,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
Address(const Address& address)
|
Address(const Address& address)
|
||||||
: m_seg(address.seg())
|
: m_domain(address.domain())
|
||||||
, m_bus(address.bus())
|
, m_bus(address.bus())
|
||||||
, m_device(address.device())
|
, m_device(address.device())
|
||||||
, m_function(address.function())
|
, m_function(address.function())
|
||||||
|
@ -114,14 +133,14 @@ public:
|
||||||
{
|
{
|
||||||
if (this == &other)
|
if (this == &other)
|
||||||
return true;
|
return true;
|
||||||
return m_seg == other.m_seg && m_bus == other.m_bus && m_device == other.m_device && m_function == other.m_function;
|
return m_domain == other.m_domain && m_bus == other.m_bus && m_device == other.m_device && m_function == other.m_function;
|
||||||
}
|
}
|
||||||
bool operator!=(const Address& other) const
|
bool operator!=(const Address& other) const
|
||||||
{
|
{
|
||||||
return !(*this == other);
|
return !(*this == other);
|
||||||
}
|
}
|
||||||
|
|
||||||
u16 seg() const { return m_seg; }
|
u16 domain() const { return m_domain; }
|
||||||
u8 bus() const { return m_bus; }
|
u8 bus() const { return m_bus; }
|
||||||
u8 device() const { return m_device; }
|
u8 device() const { return m_device; }
|
||||||
u8 function() const { return m_function; }
|
u8 function() const { return m_function; }
|
||||||
|
@ -131,47 +150,13 @@ public:
|
||||||
return 0x80000000u | (m_bus << 16u) | (m_device << 11u) | (m_function << 8u) | (field & 0xfc);
|
return 0x80000000u | (m_bus << 16u) | (m_device << 11u) | (m_function << 8u) | (field & 0xfc);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
private:
|
||||||
u32 m_seg { 0 };
|
u32 m_domain { 0 };
|
||||||
u8 m_bus { 0 };
|
u8 m_bus { 0 };
|
||||||
u8 m_device { 0 };
|
u8 m_device { 0 };
|
||||||
u8 m_function { 0 };
|
u8 m_function { 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ChangeableAddress : public Address {
|
|
||||||
ChangeableAddress()
|
|
||||||
: Address(0)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
explicit ChangeableAddress(u16 seg)
|
|
||||||
: Address(seg)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
ChangeableAddress(u16 seg, u8 bus, u8 device, u8 function)
|
|
||||||
: Address(seg, bus, device, function)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
void set_seg(u16 seg) { m_seg = seg; }
|
|
||||||
void set_bus(u8 bus) { m_bus = bus; }
|
|
||||||
void set_device(u8 device) { m_device = device; }
|
|
||||||
void set_function(u8 function) { m_function = function; }
|
|
||||||
bool operator==(const Address& address)
|
|
||||||
{
|
|
||||||
if (m_seg == address.seg() && m_bus == address.bus() && m_device == address.device() && m_function == address.function())
|
|
||||||
return true;
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const ChangeableAddress& operator=(const Address& address)
|
|
||||||
{
|
|
||||||
set_seg(address.seg());
|
|
||||||
set_bus(address.bus());
|
|
||||||
set_device(address.device());
|
|
||||||
set_function(address.function());
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class Capability {
|
class Capability {
|
||||||
public:
|
public:
|
||||||
Capability(const Address& address, u8 id, u8 ptr)
|
Capability(const Address& address, u8 id, u8 ptr)
|
||||||
|
@ -219,42 +204,8 @@ private:
|
||||||
Vector<Capability> m_capabilities;
|
Vector<Capability> m_capabilities;
|
||||||
};
|
};
|
||||||
|
|
||||||
ID get_id(PCI::Address);
|
|
||||||
bool is_io_space_enabled(Address);
|
|
||||||
void enumerate(Function<void(Address, ID)> callback);
|
|
||||||
void enable_interrupt_line(Address);
|
|
||||||
void disable_interrupt_line(Address);
|
|
||||||
u8 get_interrupt_line(Address);
|
|
||||||
void raw_access(Address, u32, size_t, u32);
|
|
||||||
u32 get_BAR0(Address);
|
|
||||||
u32 get_BAR1(Address);
|
|
||||||
u32 get_BAR2(Address);
|
|
||||||
u32 get_BAR3(Address);
|
|
||||||
u32 get_BAR4(Address);
|
|
||||||
u32 get_BAR5(Address);
|
|
||||||
u32 get_BAR(Address address, u8 bar);
|
|
||||||
u8 get_revision_id(Address);
|
|
||||||
u8 get_programming_interface(Address);
|
|
||||||
u8 get_subclass(Address);
|
|
||||||
u8 get_class(Address);
|
|
||||||
u16 get_subsystem_id(Address);
|
|
||||||
u16 get_subsystem_vendor_id(Address);
|
|
||||||
size_t get_BAR_space_size(Address, u8);
|
|
||||||
Optional<u8> get_capabilities_pointer(Address);
|
|
||||||
Vector<Capability> get_capabilities(Address);
|
|
||||||
void enable_bus_mastering(Address);
|
|
||||||
void disable_bus_mastering(Address);
|
|
||||||
void enable_io_space(Address);
|
|
||||||
void disable_io_space(Address);
|
|
||||||
void enable_memory_space(Address);
|
|
||||||
void disable_memory_space(Address);
|
|
||||||
PhysicalID get_physical_id(Address address);
|
|
||||||
|
|
||||||
class Access;
|
class Access;
|
||||||
class MMIOAccess;
|
class Domain;
|
||||||
class WindowedMMIOAccess;
|
|
||||||
class IOAccess;
|
|
||||||
class MMIOSegment;
|
|
||||||
class Device;
|
class Device;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,7 +217,7 @@ struct AK::Formatter<Kernel::PCI::Address> : Formatter<FormatString> {
|
||||||
{
|
{
|
||||||
return Formatter<FormatString>::format(
|
return Formatter<FormatString>::format(
|
||||||
builder,
|
builder,
|
||||||
"PCI [{:04x}:{:02x}:{:02x}:{:02x}]", value.seg(), value.bus(), value.device(), value.function());
|
"PCI [{:04x}:{:02x}:{:02x}:{:02x}]", value.domain(), value.bus(), value.device(), value.function());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <Kernel/Bus/PCI/API.h>
|
||||||
#include <Kernel/Bus/PCI/Device.h>
|
#include <Kernel/Bus/PCI/Device.h>
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
|
@ -1,95 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2020, Liav A. <liavalb@hotmail.co.il>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <Kernel/Bus/PCI/IOAccess.h>
|
|
||||||
#include <Kernel/Debug.h>
|
|
||||||
#include <Kernel/IO.h>
|
|
||||||
#include <Kernel/Sections.h>
|
|
||||||
|
|
||||||
namespace Kernel {
|
|
||||||
namespace PCI {
|
|
||||||
|
|
||||||
UNMAP_AFTER_INIT void IOAccess::initialize()
|
|
||||||
{
|
|
||||||
if (!Access::is_initialized()) {
|
|
||||||
new IOAccess();
|
|
||||||
dbgln_if(PCI_DEBUG, "PCI: IO access initialised.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
UNMAP_AFTER_INIT IOAccess::IOAccess()
|
|
||||||
{
|
|
||||||
dmesgln("PCI: Using I/O instructions for PCI configuration space access");
|
|
||||||
enumerate_hardware([&](const Address& address, ID id) {
|
|
||||||
m_physical_ids.append({ address, id, get_capabilities(address) });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
u8 IOAccess::read8_field(Address address, u32 field)
|
|
||||||
{
|
|
||||||
dbgln_if(PCI_DEBUG, "PCI: IO Reading 8-bit field {:#08x} for {}", field, address);
|
|
||||||
return Access::early_read8_field(address, field);
|
|
||||||
}
|
|
||||||
|
|
||||||
u16 IOAccess::read16_field(Address address, u32 field)
|
|
||||||
{
|
|
||||||
dbgln_if(PCI_DEBUG, "PCI: IO Reading 16-bit field {:#08x} for {}", field, address);
|
|
||||||
return Access::early_read16_field(address, field);
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 IOAccess::read32_field(Address address, u32 field)
|
|
||||||
{
|
|
||||||
dbgln_if(PCI_DEBUG, "PCI: IO Reading 32-bit field {:#08x} for {}", field, address);
|
|
||||||
return Access::early_read32_field(address, field);
|
|
||||||
}
|
|
||||||
|
|
||||||
void IOAccess::write8_field(Address address, u32 field, u8 value)
|
|
||||||
{
|
|
||||||
dbgln_if(PCI_DEBUG, "PCI: IO Writing to 8-bit field {:#08x}, value={:#02x} for {}", field, value, address);
|
|
||||||
IO::out32(PCI_ADDRESS_PORT, address.io_address_for_field(field));
|
|
||||||
IO::out8(PCI_VALUE_PORT + (field & 3), value);
|
|
||||||
}
|
|
||||||
void IOAccess::write16_field(Address address, u32 field, u16 value)
|
|
||||||
{
|
|
||||||
dbgln_if(PCI_DEBUG, "PCI: IO Writing to 16-bit field {:#08x}, value={:#02x} for {}", field, value, address);
|
|
||||||
IO::out32(PCI_ADDRESS_PORT, address.io_address_for_field(field));
|
|
||||||
IO::out16(PCI_VALUE_PORT + (field & 2), value);
|
|
||||||
}
|
|
||||||
void IOAccess::write32_field(Address address, u32 field, u32 value)
|
|
||||||
{
|
|
||||||
dbgln_if(PCI_DEBUG, "PCI: IO Writing to 32-bit field {:#08x}, value={:#02x} for {}", field, value, address);
|
|
||||||
IO::out32(PCI_ADDRESS_PORT, address.io_address_for_field(field));
|
|
||||||
IO::out32(PCI_VALUE_PORT, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
void IOAccess::enumerate_hardware(Function<void(Address, ID)> callback)
|
|
||||||
{
|
|
||||||
dbgln_if(PCI_DEBUG, "PCI: IO enumerating hardware");
|
|
||||||
|
|
||||||
// First scan bus 0. Find any device on that bus, and if it's a PCI-to-PCI
|
|
||||||
// bridge, recursively scan it too.
|
|
||||||
m_enumerated_buses.set(0, true);
|
|
||||||
enumerate_bus(-1, 0, callback, true);
|
|
||||||
|
|
||||||
// Handle Multiple PCI host bridges on slot 0, device 0.
|
|
||||||
// If we happen to miss some PCI buses because they are not reachable through
|
|
||||||
// recursive PCI-to-PCI bridges starting from bus 0, we might find them here.
|
|
||||||
if ((read8_field(Address(), PCI_HEADER_TYPE) & 0x80) != 0) {
|
|
||||||
for (int bus = 1; bus < 256; ++bus) {
|
|
||||||
if (read16_field(Address(0, 0, 0, bus), PCI_VENDOR_ID) == PCI_NONE)
|
|
||||||
continue;
|
|
||||||
if (read16_field(Address(0, 0, 0, bus), PCI_CLASS) != 0x6)
|
|
||||||
continue;
|
|
||||||
if (m_enumerated_buses.get(bus))
|
|
||||||
continue;
|
|
||||||
enumerate_bus(-1, bus, callback, false);
|
|
||||||
m_enumerated_buses.set(bus, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2020, Liav A. <liavalb@hotmail.co.il>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Kernel/Bus/PCI/Access.h>
|
|
||||||
|
|
||||||
namespace Kernel {
|
|
||||||
namespace PCI {
|
|
||||||
|
|
||||||
class IOAccess final : public PCI::Access {
|
|
||||||
public:
|
|
||||||
static void initialize();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
IOAccess();
|
|
||||||
|
|
||||||
private:
|
|
||||||
virtual void enumerate_hardware(Function<void(Address, ID)>) override;
|
|
||||||
virtual uint32_t segment_count() const override { return 1; };
|
|
||||||
virtual void write8_field(Address address, u32, u8) override final;
|
|
||||||
virtual void write16_field(Address address, u32, u16) override final;
|
|
||||||
virtual void write32_field(Address address, u32, u32) override final;
|
|
||||||
virtual u8 read8_field(Address address, u32) override;
|
|
||||||
virtual u16 read16_field(Address address, u32) override;
|
|
||||||
virtual u32 read32_field(Address address, u32) override;
|
|
||||||
|
|
||||||
virtual uint8_t segment_start_bus(u32) const override { return 0x0; }
|
|
||||||
virtual uint8_t segment_end_bus(u32) const override { return 0xFF; }
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -5,10 +5,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <Kernel/ACPI/Parser.h>
|
#include <Kernel/ACPI/Parser.h>
|
||||||
#include <Kernel/Bus/PCI/IOAccess.h>
|
#include <Kernel/Bus/PCI/API.h>
|
||||||
|
#include <Kernel/Bus/PCI/Access.h>
|
||||||
#include <Kernel/Bus/PCI/Initializer.h>
|
#include <Kernel/Bus/PCI/Initializer.h>
|
||||||
#include <Kernel/Bus/PCI/MMIOAccess.h>
|
#include <Kernel/Bus/PCI/SysFSPCI.h>
|
||||||
#include <Kernel/Bus/PCI/WindowedMMIOAccess.h>
|
|
||||||
#include <Kernel/CommandLine.h>
|
#include <Kernel/CommandLine.h>
|
||||||
#include <Kernel/IO.h>
|
#include <Kernel/IO.h>
|
||||||
#include <Kernel/Panic.h>
|
#include <Kernel/Panic.h>
|
||||||
|
@ -38,15 +38,16 @@ UNMAP_AFTER_INIT void initialize()
|
||||||
auto boot_determined = kernel_command_line().pci_access_level();
|
auto boot_determined = kernel_command_line().pci_access_level();
|
||||||
|
|
||||||
switch (detect_optimal_access_type(boot_determined)) {
|
switch (detect_optimal_access_type(boot_determined)) {
|
||||||
case PCIAccessLevel::MappingPerDevice:
|
case PCIAccessLevel::MemoryAddressing: {
|
||||||
WindowedMMIOAccess::initialize(ACPI::Parser::the()->find_table("MCFG"));
|
auto success = Access::initialize_for_memory_access(ACPI::Parser::the()->find_table("MCFG"));
|
||||||
|
VERIFY(success);
|
||||||
break;
|
break;
|
||||||
case PCIAccessLevel::MappingPerBus:
|
}
|
||||||
MMIOAccess::initialize(ACPI::Parser::the()->find_table("MCFG"));
|
case PCIAccessLevel::IOAddressing: {
|
||||||
break;
|
auto success = Access::initialize_for_io_access();
|
||||||
case PCIAccessLevel::IOAddressing:
|
VERIFY(success);
|
||||||
IOAccess::initialize();
|
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
VERIFY_NOT_REACHED();
|
VERIFY_NOT_REACHED();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,215 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <AK/ByteReader.h>
|
|
||||||
#include <AK/Optional.h>
|
|
||||||
#include <Kernel/Arch/x86/InterruptDisabler.h>
|
|
||||||
#include <Kernel/Bus/PCI/MMIOAccess.h>
|
|
||||||
#include <Kernel/Debug.h>
|
|
||||||
#include <Kernel/Memory/MemoryManager.h>
|
|
||||||
#include <Kernel/Sections.h>
|
|
||||||
|
|
||||||
namespace Kernel {
|
|
||||||
namespace PCI {
|
|
||||||
|
|
||||||
#define MEMORY_RANGE_PER_BUS (PCI_MMIO_CONFIG_SPACE_SIZE * PCI_MAX_FUNCTIONS_PER_DEVICE * PCI_MAX_DEVICES_PER_BUS)
|
|
||||||
|
|
||||||
u32 MMIOAccess::segment_count() const
|
|
||||||
{
|
|
||||||
return m_segments.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
u8 MMIOAccess::segment_start_bus(u32 seg) const
|
|
||||||
{
|
|
||||||
auto segment = m_segments.get(seg);
|
|
||||||
VERIFY(segment.has_value());
|
|
||||||
return segment.value().get_start_bus();
|
|
||||||
}
|
|
||||||
|
|
||||||
u8 MMIOAccess::segment_end_bus(u32 seg) const
|
|
||||||
{
|
|
||||||
auto segment = m_segments.get(seg);
|
|
||||||
VERIFY(segment.has_value());
|
|
||||||
return segment.value().get_end_bus();
|
|
||||||
}
|
|
||||||
|
|
||||||
PhysicalAddress MMIOAccess::determine_memory_mapped_bus_region(u32 segment, u8 bus) const
|
|
||||||
{
|
|
||||||
VERIFY(bus >= segment_start_bus(segment) && bus <= segment_end_bus(segment));
|
|
||||||
auto seg = m_segments.get(segment);
|
|
||||||
VERIFY(seg.has_value());
|
|
||||||
return seg.value().get_paddr().offset(MEMORY_RANGE_PER_BUS * (bus - seg.value().get_start_bus()));
|
|
||||||
}
|
|
||||||
|
|
||||||
UNMAP_AFTER_INIT void MMIOAccess::initialize(PhysicalAddress mcfg)
|
|
||||||
{
|
|
||||||
if (!Access::is_initialized()) {
|
|
||||||
new MMIOAccess(mcfg);
|
|
||||||
dbgln_if(PCI_DEBUG, "PCI: MMIO access initialised.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
UNMAP_AFTER_INIT MMIOAccess::MMIOAccess(PhysicalAddress p_mcfg)
|
|
||||||
: m_mcfg(p_mcfg)
|
|
||||||
{
|
|
||||||
dmesgln("PCI: Using MMIO for PCI configuration space access");
|
|
||||||
|
|
||||||
auto checkup_region = MM.allocate_kernel_region(p_mcfg.page_base(), (PAGE_SIZE * 2), "PCI MCFG Checkup", Memory::Region::Access::ReadWrite).release_value();
|
|
||||||
dbgln_if(PCI_DEBUG, "PCI: Checking MCFG Table length to choose the correct mapping size");
|
|
||||||
auto* sdt = (ACPI::Structures::SDTHeader*)checkup_region->vaddr().offset(p_mcfg.offset_in_page()).as_ptr();
|
|
||||||
u32 length = sdt->length;
|
|
||||||
u8 revision = sdt->revision;
|
|
||||||
|
|
||||||
dbgln("PCI: MCFG, length: {}, revision: {}", length, revision);
|
|
||||||
checkup_region->unmap();
|
|
||||||
|
|
||||||
auto mcfg_region = MM.allocate_kernel_region(p_mcfg.page_base(), Memory::page_round_up(length) + PAGE_SIZE, "PCI Parsing MCFG", Memory::Region::Access::ReadWrite).release_value();
|
|
||||||
|
|
||||||
auto& mcfg = *(ACPI::Structures::MCFG*)mcfg_region->vaddr().offset(p_mcfg.offset_in_page()).as_ptr();
|
|
||||||
dbgln_if(PCI_DEBUG, "PCI: Checking MCFG @ {}, {}", VirtualAddress(&mcfg), PhysicalAddress(p_mcfg.get()));
|
|
||||||
|
|
||||||
for (u32 index = 0; index < ((mcfg.header.length - sizeof(ACPI::Structures::MCFG)) / sizeof(ACPI::Structures::PCI_MMIO_Descriptor)); index++) {
|
|
||||||
u8 start_bus = mcfg.descriptors[index].start_pci_bus;
|
|
||||||
u8 end_bus = mcfg.descriptors[index].end_pci_bus;
|
|
||||||
u32 lower_addr = mcfg.descriptors[index].base_addr;
|
|
||||||
|
|
||||||
m_segments.set(index, { PhysicalAddress(lower_addr), start_bus, end_bus });
|
|
||||||
dmesgln("PCI: New PCI segment @ {}, PCI buses ({}-{})", PhysicalAddress { lower_addr }, start_bus, end_bus);
|
|
||||||
}
|
|
||||||
mcfg_region->unmap();
|
|
||||||
dmesgln("PCI: MMIO segments: {}", m_segments.size());
|
|
||||||
|
|
||||||
InterruptDisabler disabler;
|
|
||||||
VERIFY(m_segments.contains(0));
|
|
||||||
|
|
||||||
// Note: we need to map this region before enumerating the hardware and adding
|
|
||||||
// PCI::PhysicalID objects to the vector, because get_capabilities calls
|
|
||||||
// PCI::read16 which will need this region to be mapped.
|
|
||||||
u8 start_bus = m_segments.get(0).value().get_start_bus();
|
|
||||||
m_mapped_region = MM.allocate_kernel_region(determine_memory_mapped_bus_region(0, start_bus), MEMORY_RANGE_PER_BUS, "PCI ECAM", Memory::Region::Access::ReadWrite).release_value();
|
|
||||||
m_mapped_bus = start_bus;
|
|
||||||
dbgln_if(PCI_DEBUG, "PCI: First PCI ECAM Mapped region for starting bus {} @ {} {}", start_bus, m_mapped_region->vaddr(), m_mapped_region->physical_page(0)->paddr());
|
|
||||||
|
|
||||||
enumerate_hardware([&](const Address& address, ID id) {
|
|
||||||
m_physical_ids.append({ address, id, get_capabilities(address) });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
void MMIOAccess::map_bus_region(u32 segment, u8 bus)
|
|
||||||
{
|
|
||||||
VERIFY(m_access_lock.is_locked());
|
|
||||||
if (m_mapped_bus == bus)
|
|
||||||
return;
|
|
||||||
m_mapped_region = MM.allocate_kernel_region(determine_memory_mapped_bus_region(segment, bus), MEMORY_RANGE_PER_BUS, "PCI ECAM", Memory::Region::Access::ReadWrite).release_value();
|
|
||||||
m_mapped_bus = bus;
|
|
||||||
dbgln_if(PCI_DEBUG, "PCI: New PCI ECAM Mapped region for bus {} @ {} {}", bus, m_mapped_region->vaddr(), m_mapped_region->physical_page(0)->paddr());
|
|
||||||
}
|
|
||||||
|
|
||||||
VirtualAddress MMIOAccess::get_device_configuration_space(Address address)
|
|
||||||
{
|
|
||||||
VERIFY(m_access_lock.is_locked());
|
|
||||||
dbgln_if(PCI_DEBUG, "PCI: Getting device configuration space for {}", address);
|
|
||||||
map_bus_region(address.seg(), address.bus());
|
|
||||||
return m_mapped_region->vaddr().offset(PCI_MMIO_CONFIG_SPACE_SIZE * address.function() + (PCI_MMIO_CONFIG_SPACE_SIZE * PCI_MAX_FUNCTIONS_PER_DEVICE) * address.device());
|
|
||||||
}
|
|
||||||
|
|
||||||
u8 MMIOAccess::read8_field(Address address, u32 field)
|
|
||||||
{
|
|
||||||
SpinlockLocker lock(m_access_lock);
|
|
||||||
VERIFY(field <= 0xfff);
|
|
||||||
dbgln_if(PCI_DEBUG, "PCI: MMIO Reading 8-bit field {:#08x} for {}", field, address);
|
|
||||||
return *((volatile u8*)(get_device_configuration_space(address).get() + (field & 0xfff)));
|
|
||||||
}
|
|
||||||
|
|
||||||
u16 MMIOAccess::read16_field(Address address, u32 field)
|
|
||||||
{
|
|
||||||
SpinlockLocker lock(m_access_lock);
|
|
||||||
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).offset(field & 0xfff).as_ptr(), data);
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 MMIOAccess::read32_field(Address address, u32 field)
|
|
||||||
{
|
|
||||||
SpinlockLocker lock(m_access_lock);
|
|
||||||
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).offset(field & 0xfff).as_ptr(), data);
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MMIOAccess::write8_field(Address address, u32 field, u8 value)
|
|
||||||
{
|
|
||||||
SpinlockLocker lock(m_access_lock);
|
|
||||||
VERIFY(field <= 0xfff);
|
|
||||||
dbgln_if(PCI_DEBUG, "PCI: MMIO Writing 8-bit field {:#08x}, value={:#02x} for {}", field, value, address);
|
|
||||||
*((volatile u8*)(get_device_configuration_space(address).get() + (field & 0xfff))) = value;
|
|
||||||
}
|
|
||||||
void MMIOAccess::write16_field(Address address, u32 field, u16 value)
|
|
||||||
{
|
|
||||||
SpinlockLocker lock(m_access_lock);
|
|
||||||
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).offset(field & 0xfff).as_ptr(), value);
|
|
||||||
}
|
|
||||||
void MMIOAccess::write32_field(Address address, u32 field, u32 value)
|
|
||||||
{
|
|
||||||
SpinlockLocker lock(m_access_lock);
|
|
||||||
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).offset(field & 0xfff).as_ptr(), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MMIOAccess::enumerate_hardware(Function<void(Address, ID)> callback)
|
|
||||||
{
|
|
||||||
for (u16 seg = 0; seg < m_segments.size(); seg++) {
|
|
||||||
dbgln_if(PCI_DEBUG, "PCI: Enumerating Memory mapped IO segment {}", seg);
|
|
||||||
// Single PCI host controller.
|
|
||||||
if ((early_read8_field(Address(seg), PCI_HEADER_TYPE) & 0x80) == 0) {
|
|
||||||
enumerate_bus(-1, 0, callback, true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Multiple PCI host controllers.
|
|
||||||
for (u8 function = 0; function < 8; ++function) {
|
|
||||||
if (early_read16_field(Address(seg, 0, 0, function), PCI_VENDOR_ID) == PCI_NONE)
|
|
||||||
break;
|
|
||||||
enumerate_bus(-1, function, callback, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MMIOAccess::MMIOSegment::MMIOSegment(PhysicalAddress segment_base_addr, u8 start_bus, u8 end_bus)
|
|
||||||
: m_base_addr(segment_base_addr)
|
|
||||||
, m_start_bus(start_bus)
|
|
||||||
, m_end_bus(end_bus)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
u8 MMIOAccess::MMIOSegment::get_start_bus() const
|
|
||||||
{
|
|
||||||
return m_start_bus;
|
|
||||||
}
|
|
||||||
|
|
||||||
u8 MMIOAccess::MMIOSegment::get_end_bus() const
|
|
||||||
{
|
|
||||||
return m_end_bus;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t MMIOAccess::MMIOSegment::get_size() const
|
|
||||||
{
|
|
||||||
return (PCI_MMIO_CONFIG_SPACE_SIZE * PCI_MAX_FUNCTIONS_PER_DEVICE * PCI_MAX_DEVICES_PER_BUS * (get_end_bus() - get_start_bus()));
|
|
||||||
}
|
|
||||||
|
|
||||||
PhysicalAddress MMIOAccess::MMIOSegment::get_paddr() const
|
|
||||||
{
|
|
||||||
return m_base_addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,71 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <AK/HashMap.h>
|
|
||||||
#include <AK/NonnullOwnPtrVector.h>
|
|
||||||
#include <AK/OwnPtr.h>
|
|
||||||
#include <AK/Types.h>
|
|
||||||
#include <Kernel/ACPI/Definitions.h>
|
|
||||||
#include <Kernel/Bus/PCI/Access.h>
|
|
||||||
#include <Kernel/Locking/Spinlock.h>
|
|
||||||
#include <Kernel/Memory/AnonymousVMObject.h>
|
|
||||||
#include <Kernel/Memory/PhysicalRegion.h>
|
|
||||||
#include <Kernel/Memory/Region.h>
|
|
||||||
#include <Kernel/Memory/VMObject.h>
|
|
||||||
|
|
||||||
namespace Kernel {
|
|
||||||
namespace PCI {
|
|
||||||
|
|
||||||
#define PCI_MMIO_CONFIG_SPACE_SIZE 4096
|
|
||||||
|
|
||||||
class MMIOAccess : public Access {
|
|
||||||
public:
|
|
||||||
class MMIOSegment {
|
|
||||||
public:
|
|
||||||
MMIOSegment(PhysicalAddress, u8, u8);
|
|
||||||
u8 get_start_bus() const;
|
|
||||||
u8 get_end_bus() const;
|
|
||||||
size_t get_size() const;
|
|
||||||
PhysicalAddress get_paddr() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
PhysicalAddress m_base_addr;
|
|
||||||
u8 m_start_bus;
|
|
||||||
u8 m_end_bus;
|
|
||||||
};
|
|
||||||
static void initialize(PhysicalAddress mcfg);
|
|
||||||
|
|
||||||
private:
|
|
||||||
PhysicalAddress determine_memory_mapped_bus_region(u32 segment, u8 bus) const;
|
|
||||||
void map_bus_region(u32, u8);
|
|
||||||
VirtualAddress get_device_configuration_space(Address address);
|
|
||||||
Spinlock m_access_lock;
|
|
||||||
u8 m_mapped_bus { 0 };
|
|
||||||
OwnPtr<Memory::Region> m_mapped_region;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
explicit MMIOAccess(PhysicalAddress mcfg);
|
|
||||||
|
|
||||||
virtual u32 segment_count() const override;
|
|
||||||
virtual void enumerate_hardware(Function<void(Address, ID)>) override;
|
|
||||||
virtual void write8_field(Address address, u32, u8) override;
|
|
||||||
virtual void write16_field(Address address, u32, u16) override;
|
|
||||||
virtual void write32_field(Address address, u32, u32) override;
|
|
||||||
virtual u8 read8_field(Address address, u32) override;
|
|
||||||
virtual u16 read16_field(Address address, u32) override;
|
|
||||||
virtual u32 read32_field(Address address, u32) override;
|
|
||||||
|
|
||||||
virtual u8 segment_start_bus(u32) const override;
|
|
||||||
virtual u8 segment_end_bus(u32) const override;
|
|
||||||
|
|
||||||
PhysicalAddress m_mcfg;
|
|
||||||
HashMap<u16, MMIOSegment> m_segments;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
97
Kernel/Bus/PCI/SysFSPCI.cpp
Normal file
97
Kernel/Bus/PCI/SysFSPCI.cpp
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
/*
|
||||||
|
* 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/Bus/PCI/SysFSPCI.h>
|
||||||
|
#include <Kernel/Debug.h>
|
||||||
|
#include <Kernel/IO.h>
|
||||||
|
#include <Kernel/Sections.h>
|
||||||
|
|
||||||
|
namespace Kernel::PCI {
|
||||||
|
|
||||||
|
UNMAP_AFTER_INIT NonnullRefPtr<PCIDeviceSysFSDirectory> PCIDeviceSysFSDirectory::create(const SysFSDirectory& parent_directory, Address address)
|
||||||
|
{
|
||||||
|
return adopt_ref(*new (nothrow) PCIDeviceSysFSDirectory(parent_directory, address));
|
||||||
|
}
|
||||||
|
|
||||||
|
UNMAP_AFTER_INIT PCIDeviceSysFSDirectory::PCIDeviceSysFSDirectory(const SysFSDirectory& parent_directory, Address address)
|
||||||
|
: SysFSDirectory(String::formatted("{:04x}:{:02x}:{:02x}.{}", address.domain(), address.bus(), address.device(), address.function()), parent_directory)
|
||||||
|
, m_address(address)
|
||||||
|
{
|
||||||
|
m_components.append(PCIDeviceAttributeSysFSComponent::create("vendor", *this, PCI_VENDOR_ID, 2));
|
||||||
|
m_components.append(PCIDeviceAttributeSysFSComponent::create("device_id", *this, PCI_DEVICE_ID, 2));
|
||||||
|
m_components.append(PCIDeviceAttributeSysFSComponent::create("class", *this, PCI_CLASS, 1));
|
||||||
|
m_components.append(PCIDeviceAttributeSysFSComponent::create("subclass", *this, PCI_SUBCLASS, 1));
|
||||||
|
m_components.append(PCIDeviceAttributeSysFSComponent::create("revision", *this, PCI_REVISION_ID, 1));
|
||||||
|
m_components.append(PCIDeviceAttributeSysFSComponent::create("progif", *this, PCI_PROG_IF, 1));
|
||||||
|
m_components.append(PCIDeviceAttributeSysFSComponent::create("subsystem_vendor", *this, PCI_SUBSYSTEM_VENDOR_ID, 2));
|
||||||
|
m_components.append(PCIDeviceAttributeSysFSComponent::create("subsystem_id", *this, PCI_SUBSYSTEM_ID, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
UNMAP_AFTER_INIT void PCIBusSysFSDirectory::initialize()
|
||||||
|
{
|
||||||
|
auto pci_directory = adopt_ref(*new (nothrow) PCIBusSysFSDirectory());
|
||||||
|
SysFSComponentRegistry::the().register_new_component(pci_directory);
|
||||||
|
}
|
||||||
|
|
||||||
|
UNMAP_AFTER_INIT PCIBusSysFSDirectory::PCIBusSysFSDirectory()
|
||||||
|
: SysFSDirectory("pci", SysFSComponentRegistry::the().root_directory())
|
||||||
|
{
|
||||||
|
PCI::enumerate([&](const Address& address, ID) {
|
||||||
|
auto pci_device = PCI::PCIDeviceSysFSDirectory::create(*this, address);
|
||||||
|
m_components.append(pci_device);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
NonnullRefPtr<PCIDeviceAttributeSysFSComponent> PCIDeviceAttributeSysFSComponent::create(String name, const PCIDeviceSysFSDirectory& device, size_t offset, size_t field_bytes_width)
|
||||||
|
{
|
||||||
|
return adopt_ref(*new (nothrow) PCIDeviceAttributeSysFSComponent(name, device, offset, field_bytes_width));
|
||||||
|
}
|
||||||
|
|
||||||
|
PCIDeviceAttributeSysFSComponent::PCIDeviceAttributeSysFSComponent(String name, const PCIDeviceSysFSDirectory& device, size_t offset, size_t field_bytes_width)
|
||||||
|
: SysFSComponent(name)
|
||||||
|
, m_device(device)
|
||||||
|
, m_offset(offset)
|
||||||
|
, m_field_bytes_width(field_bytes_width)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
KResultOr<size_t> PCIDeviceAttributeSysFSComponent::read_bytes(off_t offset, size_t count, UserOrKernelBuffer& buffer, FileDescription*) const
|
||||||
|
{
|
||||||
|
auto blob = try_to_generate_buffer();
|
||||||
|
if (!blob)
|
||||||
|
return KResult(EFAULT);
|
||||||
|
|
||||||
|
if ((size_t)offset >= blob->size())
|
||||||
|
return KSuccess;
|
||||||
|
|
||||||
|
ssize_t nread = min(static_cast<off_t>(blob->size() - offset), static_cast<off_t>(count));
|
||||||
|
if (!buffer.write(blob->data() + offset, nread))
|
||||||
|
return KResult(EFAULT);
|
||||||
|
return nread;
|
||||||
|
}
|
||||||
|
|
||||||
|
OwnPtr<KBuffer> PCIDeviceAttributeSysFSComponent::try_to_generate_buffer() const
|
||||||
|
{
|
||||||
|
String value;
|
||||||
|
switch (m_field_bytes_width) {
|
||||||
|
case 1:
|
||||||
|
value = String::formatted("{:#x}", PCI::read8(m_device->address(), m_offset));
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
value = String::formatted("{:#x}", PCI::read16(m_device->address(), m_offset));
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
value = String::formatted("{:#x}", PCI::read32(m_device->address(), m_offset));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
VERIFY_NOT_REACHED();
|
||||||
|
}
|
||||||
|
|
||||||
|
return KBuffer::try_create_with_bytes(value.substring_view(0).bytes());
|
||||||
|
}
|
||||||
|
}
|
51
Kernel/Bus/PCI/SysFSPCI.h
Normal file
51
Kernel/Bus/PCI/SysFSPCI.h
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <AK/Bitmap.h>
|
||||||
|
#include <AK/String.h>
|
||||||
|
#include <AK/Vector.h>
|
||||||
|
#include <Kernel/Bus/PCI/Definitions.h>
|
||||||
|
#include <Kernel/FileSystem/SysFS.h>
|
||||||
|
|
||||||
|
namespace Kernel::PCI {
|
||||||
|
|
||||||
|
class PCIBusSysFSDirectory final : public SysFSDirectory {
|
||||||
|
public:
|
||||||
|
static void initialize();
|
||||||
|
|
||||||
|
private:
|
||||||
|
PCIBusSysFSDirectory();
|
||||||
|
};
|
||||||
|
|
||||||
|
class PCIDeviceSysFSDirectory final : public SysFSDirectory {
|
||||||
|
public:
|
||||||
|
static NonnullRefPtr<PCIDeviceSysFSDirectory> create(const SysFSDirectory&, Address);
|
||||||
|
const Address& address() const { return m_address; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
PCIDeviceSysFSDirectory(const SysFSDirectory&, Address);
|
||||||
|
|
||||||
|
Address m_address;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PCIDeviceAttributeSysFSComponent : public SysFSComponent {
|
||||||
|
public:
|
||||||
|
static NonnullRefPtr<PCIDeviceAttributeSysFSComponent> create(String name, const PCIDeviceSysFSDirectory& device, size_t offset, size_t field_bytes_width);
|
||||||
|
|
||||||
|
virtual KResultOr<size_t> read_bytes(off_t, size_t, UserOrKernelBuffer&, FileDescription*) const override;
|
||||||
|
virtual ~PCIDeviceAttributeSysFSComponent() {};
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual OwnPtr<KBuffer> try_to_generate_buffer() const;
|
||||||
|
PCIDeviceAttributeSysFSComponent(String name, const PCIDeviceSysFSDirectory& device, size_t offset, size_t field_bytes_width);
|
||||||
|
NonnullRefPtr<PCIDeviceSysFSDirectory> m_device;
|
||||||
|
size_t m_offset;
|
||||||
|
size_t m_field_bytes_width;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -1,120 +0,0 @@
|
||||||
/*
|
|
||||||
* 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,54 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2020-2021, Liav A. <liavalb@hotmail.co.il>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <AK/HashMap.h>
|
|
||||||
#include <AK/NonnullOwnPtrVector.h>
|
|
||||||
#include <AK/OwnPtr.h>
|
|
||||||
#include <AK/Types.h>
|
|
||||||
#include <Kernel/ACPI/Definitions.h>
|
|
||||||
#include <Kernel/Bus/PCI/Access.h>
|
|
||||||
#include <Kernel/Bus/PCI/MMIOAccess.h>
|
|
||||||
#include <Kernel/Memory/AnonymousVMObject.h>
|
|
||||||
#include <Kernel/Memory/PhysicalRegion.h>
|
|
||||||
#include <Kernel/Memory/Region.h>
|
|
||||||
#include <Kernel/Memory/VMObject.h>
|
|
||||||
|
|
||||||
namespace Kernel {
|
|
||||||
namespace PCI {
|
|
||||||
|
|
||||||
class DeviceConfigurationSpaceMapping {
|
|
||||||
public:
|
|
||||||
DeviceConfigurationSpaceMapping(Address, const MMIOAccess::MMIOSegment&);
|
|
||||||
VirtualAddress vaddr() const { return m_mapped_region->vaddr(); };
|
|
||||||
PhysicalAddress paddr() const { return m_mapped_region->physical_page(0)->paddr(); }
|
|
||||||
const Address& address() const { return m_device_address; };
|
|
||||||
|
|
||||||
private:
|
|
||||||
Address m_device_address;
|
|
||||||
NonnullOwnPtr<Memory::Region> m_mapped_region;
|
|
||||||
};
|
|
||||||
|
|
||||||
class WindowedMMIOAccess final : public MMIOAccess {
|
|
||||||
public:
|
|
||||||
static void initialize(PhysicalAddress mcfg);
|
|
||||||
|
|
||||||
private:
|
|
||||||
explicit WindowedMMIOAccess(PhysicalAddress mcfg);
|
|
||||||
virtual void write8_field(Address address, u32, u8) override;
|
|
||||||
virtual void write16_field(Address address, u32, u16) override;
|
|
||||||
virtual void write32_field(Address address, u32, u32) override;
|
|
||||||
virtual u8 read8_field(Address address, u32) override;
|
|
||||||
virtual u16 read16_field(Address address, u32) override;
|
|
||||||
virtual u32 read32_field(Address address, u32) override;
|
|
||||||
|
|
||||||
Optional<VirtualAddress> get_device_configuration_space(Address address);
|
|
||||||
NonnullOwnPtrVector<DeviceConfigurationSpaceMapping> m_mapped_device_regions;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -6,6 +6,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <AK/Platform.h>
|
#include <AK/Platform.h>
|
||||||
|
#include <Kernel/Bus/PCI/API.h>
|
||||||
#include <Kernel/Bus/USB/UHCI/UHCIController.h>
|
#include <Kernel/Bus/USB/UHCI/UHCIController.h>
|
||||||
#include <Kernel/Bus/USB/USBRequest.h>
|
#include <Kernel/Bus/USB/USBRequest.h>
|
||||||
#include <Kernel/Debug.h>
|
#include <Kernel/Debug.h>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <AK/Singleton.h>
|
#include <AK/Singleton.h>
|
||||||
#include <Kernel/Bus/PCI/Access.h>
|
#include <Kernel/Bus/PCI/API.h>
|
||||||
#include <Kernel/Bus/USB/SysFSUSB.h>
|
#include <Kernel/Bus/USB/SysFSUSB.h>
|
||||||
#include <Kernel/Bus/USB/UHCI/UHCIController.h>
|
#include <Kernel/Bus/USB/UHCI/UHCIController.h>
|
||||||
#include <Kernel/Bus/USB/USBManagement.h>
|
#include <Kernel/Bus/USB/USBManagement.h>
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <Kernel/Bus/PCI/API.h>
|
||||||
#include <Kernel/Bus/PCI/IDs.h>
|
#include <Kernel/Bus/PCI/IDs.h>
|
||||||
#include <Kernel/Bus/VirtIO/Console.h>
|
#include <Kernel/Bus/VirtIO/Console.h>
|
||||||
#include <Kernel/Bus/VirtIO/Device.h>
|
#include <Kernel/Bus/VirtIO/Device.h>
|
||||||
|
|
|
@ -27,11 +27,10 @@ set(KERNEL_SOURCES
|
||||||
Arch/PC/BIOS.cpp
|
Arch/PC/BIOS.cpp
|
||||||
Arch/x86/SmapDisabler.h
|
Arch/x86/SmapDisabler.h
|
||||||
Bus/PCI/Access.cpp
|
Bus/PCI/Access.cpp
|
||||||
|
Bus/PCI/API.cpp
|
||||||
Bus/PCI/Device.cpp
|
Bus/PCI/Device.cpp
|
||||||
Bus/PCI/IOAccess.cpp
|
|
||||||
Bus/PCI/MMIOAccess.cpp
|
|
||||||
Bus/PCI/Initializer.cpp
|
Bus/PCI/Initializer.cpp
|
||||||
Bus/PCI/WindowedMMIOAccess.cpp
|
Bus/PCI/SysFSPCI.cpp
|
||||||
Bus/USB/SysFSUSB.cpp
|
Bus/USB/SysFSUSB.cpp
|
||||||
Bus/USB/UHCI/UHCIController.cpp
|
Bus/USB/UHCI/UHCIController.cpp
|
||||||
Bus/USB/UHCI/UHCIRootHub.cpp
|
Bus/USB/UHCI/UHCIRootHub.cpp
|
||||||
|
|
|
@ -114,9 +114,7 @@ UNMAP_AFTER_INIT PCIAccessLevel CommandLine::pci_access_level() const
|
||||||
{
|
{
|
||||||
auto value = lookup("pci_ecam"sv).value_or("off"sv);
|
auto value = lookup("pci_ecam"sv).value_or("off"sv);
|
||||||
if (value == "on"sv)
|
if (value == "on"sv)
|
||||||
return PCIAccessLevel::MappingPerBus;
|
return PCIAccessLevel::MemoryAddressing;
|
||||||
if (value == "per-device"sv)
|
|
||||||
return PCIAccessLevel::MappingPerDevice;
|
|
||||||
if (value == "off"sv)
|
if (value == "off"sv)
|
||||||
return PCIAccessLevel::IOAddressing;
|
return PCIAccessLevel::IOAddressing;
|
||||||
PANIC("Unknown PCI ECAM setting: {}", value);
|
PANIC("Unknown PCI ECAM setting: {}", value);
|
||||||
|
|
|
@ -33,8 +33,7 @@ enum class AcpiFeatureLevel {
|
||||||
|
|
||||||
enum class PCIAccessLevel {
|
enum class PCIAccessLevel {
|
||||||
IOAddressing,
|
IOAddressing,
|
||||||
MappingPerBus,
|
MemoryAddressing,
|
||||||
MappingPerDevice,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class AHCIResetMode {
|
enum class AHCIResetMode {
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <Kernel/Bus/PCI/API.h>
|
||||||
#include <Kernel/Devices/PCISerialDevice.h>
|
#include <Kernel/Devices/PCISerialDevice.h>
|
||||||
#include <Kernel/Sections.h>
|
#include <Kernel/Sections.h>
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
#include <Kernel/Arch/x86/CPU.h>
|
#include <Kernel/Arch/x86/CPU.h>
|
||||||
#include <Kernel/Arch/x86/InterruptDisabler.h>
|
#include <Kernel/Arch/x86/InterruptDisabler.h>
|
||||||
#include <Kernel/Arch/x86/ProcessorInfo.h>
|
#include <Kernel/Arch/x86/ProcessorInfo.h>
|
||||||
#include <Kernel/Bus/PCI/Access.h>
|
#include <Kernel/Bus/PCI/API.h>
|
||||||
#include <Kernel/CommandLine.h>
|
#include <Kernel/CommandLine.h>
|
||||||
#include <Kernel/ConsoleDevice.h>
|
#include <Kernel/ConsoleDevice.h>
|
||||||
#include <Kernel/Devices/HID/HIDManagement.h>
|
#include <Kernel/Devices/HID/HIDManagement.h>
|
||||||
|
@ -615,7 +615,7 @@ private:
|
||||||
JsonArraySerializer array { builder };
|
JsonArraySerializer array { builder };
|
||||||
PCI::enumerate([&array](PCI::Address address, PCI::ID id) {
|
PCI::enumerate([&array](PCI::Address address, PCI::ID id) {
|
||||||
auto obj = array.add_object();
|
auto obj = array.add_object();
|
||||||
obj.add("seg", address.seg());
|
obj.add("domain", address.domain());
|
||||||
obj.add("bus", address.bus());
|
obj.add("bus", address.bus());
|
||||||
obj.add("device", address.device());
|
obj.add("device", address.device());
|
||||||
obj.add("function", address.function());
|
obj.add("function", address.function());
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
#include <AK/Atomic.h>
|
#include <AK/Atomic.h>
|
||||||
#include <AK/Checked.h>
|
#include <AK/Checked.h>
|
||||||
#include <Kernel/Bus/PCI/Access.h>
|
#include <Kernel/Bus/PCI/API.h>
|
||||||
#include <Kernel/Bus/PCI/IDs.h>
|
#include <Kernel/Bus/PCI/IDs.h>
|
||||||
#include <Kernel/Debug.h>
|
#include <Kernel/Debug.h>
|
||||||
#include <Kernel/Graphics/Bochs/GraphicsAdapter.h>
|
#include <Kernel/Graphics/Bochs/GraphicsAdapter.h>
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <AK/Singleton.h>
|
#include <AK/Singleton.h>
|
||||||
|
#include <Kernel/Bus/PCI/API.h>
|
||||||
#include <Kernel/Bus/PCI/IDs.h>
|
#include <Kernel/Bus/PCI/IDs.h>
|
||||||
#include <Kernel/CommandLine.h>
|
#include <Kernel/CommandLine.h>
|
||||||
#include <Kernel/Graphics/Bochs/GraphicsAdapter.h>
|
#include <Kernel/Graphics/Bochs/GraphicsAdapter.h>
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <Kernel/Bus/PCI/API.h>
|
||||||
#include <Kernel/Graphics/Console/ContiguousFramebufferConsole.h>
|
#include <Kernel/Graphics/Console/ContiguousFramebufferConsole.h>
|
||||||
#include <Kernel/Graphics/Definitions.h>
|
#include <Kernel/Graphics/Definitions.h>
|
||||||
#include <Kernel/Graphics/GraphicsManagement.h>
|
#include <Kernel/Graphics/GraphicsManagement.h>
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <Kernel/Bus/PCI/API.h>
|
||||||
#include <Kernel/Bus/PCI/IDs.h>
|
#include <Kernel/Bus/PCI/IDs.h>
|
||||||
#include <Kernel/Graphics/Console/GenericFramebufferConsole.h>
|
#include <Kernel/Graphics/Console/GenericFramebufferConsole.h>
|
||||||
#include <Kernel/Graphics/GraphicsManagement.h>
|
#include <Kernel/Graphics/GraphicsManagement.h>
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <AK/MACAddress.h>
|
#include <AK/MACAddress.h>
|
||||||
|
#include <Kernel/Bus/PCI/API.h>
|
||||||
#include <Kernel/Bus/PCI/IDs.h>
|
#include <Kernel/Bus/PCI/IDs.h>
|
||||||
#include <Kernel/Net/E1000ENetworkAdapter.h>
|
#include <Kernel/Net/E1000ENetworkAdapter.h>
|
||||||
#include <Kernel/Sections.h>
|
#include <Kernel/Sections.h>
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <AK/MACAddress.h>
|
#include <AK/MACAddress.h>
|
||||||
|
#include <Kernel/Bus/PCI/API.h>
|
||||||
#include <Kernel/Bus/PCI/IDs.h>
|
#include <Kernel/Bus/PCI/IDs.h>
|
||||||
#include <Kernel/Debug.h>
|
#include <Kernel/Debug.h>
|
||||||
#include <Kernel/Net/E1000NetworkAdapter.h>
|
#include <Kernel/Net/E1000NetworkAdapter.h>
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <AK/MACAddress.h>
|
#include <AK/MACAddress.h>
|
||||||
|
#include <Kernel/Bus/PCI/API.h>
|
||||||
#include <Kernel/Debug.h>
|
#include <Kernel/Debug.h>
|
||||||
#include <Kernel/IO.h>
|
#include <Kernel/IO.h>
|
||||||
#include <Kernel/Net/NE2000NetworkAdapter.h>
|
#include <Kernel/Net/NE2000NetworkAdapter.h>
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <AK/Singleton.h>
|
#include <AK/Singleton.h>
|
||||||
|
#include <Kernel/Bus/PCI/API.h>
|
||||||
#include <Kernel/CommandLine.h>
|
#include <Kernel/CommandLine.h>
|
||||||
#include <Kernel/IO.h>
|
#include <Kernel/IO.h>
|
||||||
#include <Kernel/Memory/AnonymousVMObject.h>
|
#include <Kernel/Memory/AnonymousVMObject.h>
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <AK/MACAddress.h>
|
#include <AK/MACAddress.h>
|
||||||
|
#include <Kernel/Bus/PCI/API.h>
|
||||||
#include <Kernel/Debug.h>
|
#include <Kernel/Debug.h>
|
||||||
#include <Kernel/IO.h>
|
#include <Kernel/IO.h>
|
||||||
#include <Kernel/Net/RTL8139NetworkAdapter.h>
|
#include <Kernel/Net/RTL8139NetworkAdapter.h>
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <AK/MACAddress.h>
|
#include <AK/MACAddress.h>
|
||||||
|
#include <Kernel/Bus/PCI/API.h>
|
||||||
#include <Kernel/Bus/PCI/IDs.h>
|
#include <Kernel/Bus/PCI/IDs.h>
|
||||||
#include <Kernel/Debug.h>
|
#include <Kernel/Debug.h>
|
||||||
#include <Kernel/Net/RTL8168NetworkAdapter.h>
|
#include <Kernel/Net/RTL8168NetworkAdapter.h>
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include <AK/OwnPtr.h>
|
#include <AK/OwnPtr.h>
|
||||||
#include <AK/RefPtr.h>
|
#include <AK/RefPtr.h>
|
||||||
#include <AK/Types.h>
|
#include <AK/Types.h>
|
||||||
|
#include <Kernel/Bus/PCI/API.h>
|
||||||
#include <Kernel/Memory/MemoryManager.h>
|
#include <Kernel/Memory/MemoryManager.h>
|
||||||
#include <Kernel/Storage/AHCIController.h>
|
#include <Kernel/Storage/AHCIController.h>
|
||||||
#include <Kernel/Storage/SATADiskDevice.h>
|
#include <Kernel/Storage/SATADiskDevice.h>
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <Kernel/Bus/PCI/API.h>
|
||||||
#include <Kernel/Sections.h>
|
#include <Kernel/Sections.h>
|
||||||
#include <Kernel/Storage/ATA.h>
|
#include <Kernel/Storage/ATA.h>
|
||||||
#include <Kernel/Storage/BMIDEChannel.h>
|
#include <Kernel/Storage/BMIDEChannel.h>
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include <AK/OwnPtr.h>
|
#include <AK/OwnPtr.h>
|
||||||
#include <AK/RefPtr.h>
|
#include <AK/RefPtr.h>
|
||||||
#include <AK/Types.h>
|
#include <AK/Types.h>
|
||||||
|
#include <Kernel/Bus/PCI/API.h>
|
||||||
#include <Kernel/FileSystem/ProcFS.h>
|
#include <Kernel/FileSystem/ProcFS.h>
|
||||||
#include <Kernel/Sections.h>
|
#include <Kernel/Sections.h>
|
||||||
#include <Kernel/Storage/BMIDEChannel.h>
|
#include <Kernel/Storage/BMIDEChannel.h>
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <AK/UUID.h>
|
#include <AK/UUID.h>
|
||||||
|
#include <Kernel/Bus/PCI/API.h>
|
||||||
#include <Kernel/Bus/PCI/Access.h>
|
#include <Kernel/Bus/PCI/Access.h>
|
||||||
#include <Kernel/CommandLine.h>
|
#include <Kernel/CommandLine.h>
|
||||||
#include <Kernel/Devices/BlockDevice.h>
|
#include <Kernel/Devices/BlockDevice.h>
|
||||||
|
|
|
@ -70,7 +70,7 @@ int main(int argc, char** argv)
|
||||||
VERIFY(json.has_value());
|
VERIFY(json.has_value());
|
||||||
json.value().as_array().for_each([db, format](auto& value) {
|
json.value().as_array().for_each([db, format](auto& value) {
|
||||||
auto& dev = value.as_object();
|
auto& dev = value.as_object();
|
||||||
auto seg = dev.get("seg").to_u32();
|
auto domain = dev.get("domain").to_u32();
|
||||||
auto bus = dev.get("bus").to_u32();
|
auto bus = dev.get("bus").to_u32();
|
||||||
auto device = dev.get("device").to_u32();
|
auto device = dev.get("device").to_u32();
|
||||||
auto function = dev.get("function").to_u32();
|
auto function = dev.get("function").to_u32();
|
||||||
|
@ -97,7 +97,7 @@ int main(int argc, char** argv)
|
||||||
if (class_name.is_empty())
|
if (class_name.is_empty())
|
||||||
class_name = String::formatted("{:02x}{:02x}", class_id, subclass_id);
|
class_name = String::formatted("{:02x}{:02x}", class_id, subclass_id);
|
||||||
|
|
||||||
outln(format, seg, bus, device, function, class_name, vendor_name, device_name, revision_id);
|
outln(format, domain, bus, device, function, class_name, vendor_name, device_name, revision_id);
|
||||||
});
|
});
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue