Kernel: Dynamically allocate PCI devices
There was no need to prealloce almost a 5 MiB buffer for PCI all possible pci devies :D
This commit is contained in:
@@ -66,11 +66,13 @@ namespace Kernel::PCI
|
|||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Device() = default;
|
Device(uint8_t bus, uint8_t dev, uint8_t func)
|
||||||
|
: m_bus(bus)
|
||||||
|
, m_dev(dev)
|
||||||
|
, m_func(func)
|
||||||
|
{ }
|
||||||
|
|
||||||
void set_location(uint8_t bus, uint8_t dev, uint8_t func);
|
|
||||||
void initialize(paddr_t pcie_paddr);
|
void initialize(paddr_t pcie_paddr);
|
||||||
bool is_valid() const { return m_is_valid; }
|
|
||||||
|
|
||||||
uint32_t read_dword(uint8_t) const;
|
uint32_t read_dword(uint8_t) const;
|
||||||
uint16_t read_word(uint8_t) const;
|
uint16_t read_word(uint8_t) const;
|
||||||
@@ -124,10 +126,9 @@ namespace Kernel::PCI
|
|||||||
BAN::ErrorOr<uint8_t> find_intx_interrupt();
|
BAN::ErrorOr<uint8_t> find_intx_interrupt();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool m_is_valid { false };
|
const uint8_t m_bus { 0 };
|
||||||
uint8_t m_bus { 0 };
|
const uint8_t m_dev { 0 };
|
||||||
uint8_t m_dev { 0 };
|
const uint8_t m_func { 0 };
|
||||||
uint8_t m_func { 0 };
|
|
||||||
|
|
||||||
vaddr_t m_mmio_config { 0 };
|
vaddr_t m_mmio_config { 0 };
|
||||||
|
|
||||||
@@ -161,11 +162,8 @@ namespace Kernel::PCI
|
|||||||
template<typename F>
|
template<typename F>
|
||||||
void for_each_device(F callback)
|
void for_each_device(F callback)
|
||||||
{
|
{
|
||||||
for (auto& bus : m_buses)
|
for (auto& dev : m_devices)
|
||||||
for (auto& dev : bus)
|
callback(dev);
|
||||||
for (auto& func : dev)
|
|
||||||
if (func.is_valid())
|
|
||||||
callback(func);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
uint32_t read_config_dword(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset);
|
uint32_t read_config_dword(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset);
|
||||||
@@ -179,19 +177,22 @@ namespace Kernel::PCI
|
|||||||
BAN::Optional<uint8_t> reserve_msi();
|
BAN::Optional<uint8_t> reserve_msi();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PCIManager() : m_bus_pcie_paddr(0) {}
|
struct PCIeInfo
|
||||||
void check_function(uint8_t bus, uint8_t dev, uint8_t func);
|
{
|
||||||
void check_device(uint8_t bus, uint8_t dev);
|
paddr_t bus_paddr[256];
|
||||||
void check_bus(uint8_t bus);
|
};
|
||||||
void check_all_buses();
|
|
||||||
|
PCIManager() = default;
|
||||||
|
void check_function(const PCIeInfo&, uint8_t bus, uint8_t dev, uint8_t func);
|
||||||
|
void check_device(const PCIeInfo&, uint8_t bus, uint8_t dev);
|
||||||
|
void check_bus(const PCIeInfo&, uint8_t bus);
|
||||||
|
void check_all_buses(const PCIeInfo&);
|
||||||
void initialize_impl();
|
void initialize_impl();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr uint8_t m_msi_count = IRQ_MSI_END - IRQ_MSI_BASE;
|
static constexpr uint8_t m_msi_count = IRQ_MSI_END - IRQ_MSI_BASE;
|
||||||
using PCIBus = BAN::Array<BAN::Array<Device, 8>, 32>;
|
|
||||||
BAN::Array<PCIBus, 256> m_buses;
|
BAN::Vector<Device> m_devices;
|
||||||
BAN::Array<paddr_t, 256> m_bus_pcie_paddr;
|
|
||||||
bool m_is_pcie { false };
|
|
||||||
|
|
||||||
SpinLock m_reserved_msi_lock;
|
SpinLock m_reserved_msi_lock;
|
||||||
BAN::Array<uint8_t, m_msi_count / 8> m_reserved_msi_bitmap;
|
BAN::Array<uint8_t, m_msi_count / 8> m_reserved_msi_bitmap;
|
||||||
|
|||||||
@@ -45,34 +45,58 @@ namespace Kernel::PCI
|
|||||||
};
|
};
|
||||||
static_assert(sizeof(MSIXEntry) == 16);
|
static_assert(sizeof(MSIXEntry) == 16);
|
||||||
|
|
||||||
|
static uint32_t get_device_io_address(uint8_t bus, uint8_t dev, uint8_t func)
|
||||||
|
{
|
||||||
|
return 0x80000000
|
||||||
|
| (static_cast<uint32_t>(bus) << 16)
|
||||||
|
| (static_cast<uint32_t>(dev) << 11)
|
||||||
|
| (static_cast<uint32_t>(func) << 8);
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t PCIManager::read_config_dword(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset)
|
uint32_t PCIManager::read_config_dword(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset)
|
||||||
{
|
{
|
||||||
return m_buses[bus][dev][func].read_dword(offset);
|
ASSERT(offset % 4 == 0);
|
||||||
|
IO::outl(CONFIG_ADDRESS, get_device_io_address(bus, dev, func) | offset);
|
||||||
|
return IO::inl(CONFIG_DATA);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t PCIManager::read_config_word(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset)
|
uint16_t PCIManager::read_config_word(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset)
|
||||||
{
|
{
|
||||||
return m_buses[bus][dev][func].read_word(offset);
|
ASSERT(offset % 2 == 0);
|
||||||
|
const uint32_t dword = read_config_dword(bus, dev, func, offset & ~3);
|
||||||
|
return (dword >> ((offset & 3) * 8)) & 0xFFFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t PCIManager::read_config_byte(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset)
|
uint8_t PCIManager::read_config_byte(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset)
|
||||||
{
|
{
|
||||||
return m_buses[bus][dev][func].read_byte(offset);
|
const uint32_t dword = read_config_dword(bus, dev, func, offset & ~3);
|
||||||
|
return (dword >> ((offset & 3) * 8)) & 0xFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PCIManager::write_config_dword(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset, uint32_t value)
|
void PCIManager::write_config_dword(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset, uint32_t value)
|
||||||
{
|
{
|
||||||
m_buses[bus][dev][func].write_dword(offset, value);
|
ASSERT(offset % 4 == 0);
|
||||||
|
IO::outl(CONFIG_ADDRESS, get_device_io_address(bus, dev, func) | offset);
|
||||||
|
IO::outl(CONFIG_DATA, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PCIManager::write_config_word(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset, uint16_t value)
|
void PCIManager::write_config_word(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset, uint16_t value)
|
||||||
{
|
{
|
||||||
m_buses[bus][dev][func].write_word(offset, value);
|
ASSERT(offset % 2 == 0);
|
||||||
|
const uint32_t byte = (offset & 3) * 8;
|
||||||
|
uint32_t temp = read_config_dword(bus, dev, func, offset & ~3);
|
||||||
|
temp &= ~(0xFFFF << byte);
|
||||||
|
temp |= (uint32_t)value << byte;
|
||||||
|
write_config_dword(bus, dev, func, offset & ~3, temp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PCIManager::write_config_byte(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset, uint8_t value)
|
void PCIManager::write_config_byte(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset, uint8_t value)
|
||||||
{
|
{
|
||||||
m_buses[bus][dev][func].write_byte(offset, value);
|
const uint32_t byte = (offset & 3) * 8;
|
||||||
|
uint32_t temp = read_config_dword(bus, dev, func, offset & ~3);
|
||||||
|
temp &= ~(0xFF << byte);
|
||||||
|
temp |= (uint32_t)value << byte;
|
||||||
|
write_config_dword(bus, dev, func, offset & ~3, temp);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint16_t get_vendor_id(uint8_t bus, uint8_t dev, uint8_t func)
|
static uint16_t get_vendor_id(uint8_t bus, uint8_t dev, uint8_t func)
|
||||||
@@ -107,6 +131,9 @@ namespace Kernel::PCI
|
|||||||
};
|
};
|
||||||
static_assert(sizeof(BAAS) == 16);
|
static_assert(sizeof(BAAS) == 16);
|
||||||
|
|
||||||
|
auto* pcie_info = new PCIeInfo;
|
||||||
|
ASSERT(pcie_info);
|
||||||
|
|
||||||
if (auto* mcfg = ACPI::ACPI::get().get_header("MCFG", 0))
|
if (auto* mcfg = ACPI::ACPI::get().get_header("MCFG", 0))
|
||||||
{
|
{
|
||||||
const size_t count = (mcfg->length - 44) / 16;
|
const size_t count = (mcfg->length - 44) / 16;
|
||||||
@@ -118,19 +145,15 @@ namespace Kernel::PCI
|
|||||||
continue;
|
continue;
|
||||||
for (uint64_t bus = baas->bus_start; bus <= baas->bus_end; bus++)
|
for (uint64_t bus = baas->bus_start; bus <= baas->bus_end; bus++)
|
||||||
{
|
{
|
||||||
ASSERT(m_bus_pcie_paddr[bus] == 0);
|
ASSERT(pcie_info->bus_paddr[bus] == 0);
|
||||||
m_bus_pcie_paddr[bus] = baas->addr + (bus << 20);
|
pcie_info->bus_paddr[bus] = baas->addr + (bus << 20);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_is_pcie = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t bus = 0; bus < m_buses.size(); bus++)
|
check_all_buses(*pcie_info);
|
||||||
for (size_t dev = 0; dev < m_buses[bus].size(); dev++)
|
|
||||||
for (size_t func = 0; func < m_buses[bus][dev].size(); func++)
|
delete pcie_info;
|
||||||
m_buses[bus][dev][func].set_location(bus, dev, func);
|
|
||||||
s_instance->check_all_buses();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PCIManager& PCIManager::get()
|
PCIManager& PCIManager::get()
|
||||||
@@ -139,43 +162,47 @@ namespace Kernel::PCI
|
|||||||
return *s_instance;
|
return *s_instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PCIManager::check_function(uint8_t bus, uint8_t dev, uint8_t func)
|
void PCIManager::check_function(const PCIeInfo& pcie_info, uint8_t bus, uint8_t dev, uint8_t func)
|
||||||
{
|
{
|
||||||
auto& device = m_buses[bus][dev][func];
|
const paddr_t pcie_paddr = pcie_info.bus_paddr[bus] ? pcie_info.bus_paddr[bus] + (((paddr_t)dev << 15) | ((paddr_t)func << 12)) : 0;
|
||||||
const paddr_t pcie_paddr = m_is_pcie ? m_bus_pcie_paddr[bus] + (((paddr_t)dev << 15) | ((paddr_t)func << 12)) : 0;
|
|
||||||
|
MUST(m_devices.emplace_back(bus, dev, func));
|
||||||
|
|
||||||
|
auto& device = m_devices.back();
|
||||||
device.initialize(pcie_paddr);
|
device.initialize(pcie_paddr);
|
||||||
|
|
||||||
if (device.class_code() == 0x06 && device.subclass() == 0x04)
|
if (device.class_code() == 0x06 && device.subclass() == 0x04)
|
||||||
check_bus(device.read_byte(0x19));
|
check_bus(pcie_info, device.read_byte(0x19));
|
||||||
}
|
}
|
||||||
|
|
||||||
void PCIManager::check_device(uint8_t bus, uint8_t dev)
|
void PCIManager::check_device(const PCIeInfo& pcie_info, uint8_t bus, uint8_t dev)
|
||||||
{
|
{
|
||||||
if (get_vendor_id(bus, dev, 0) == INVALID_VENDOR)
|
if (get_vendor_id(bus, dev, 0) == INVALID_VENDOR)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
check_function(bus, dev, 0);
|
check_function(pcie_info, bus, dev, 0);
|
||||||
if (get_header_type(bus, dev, 0) & MULTI_FUNCTION)
|
if (get_header_type(bus, dev, 0) & MULTI_FUNCTION)
|
||||||
for (uint8_t func = 1; func < 8; func++)
|
for (uint8_t func = 1; func < 8; func++)
|
||||||
if (get_vendor_id(bus, dev, func) != INVALID_VENDOR)
|
if (get_vendor_id(bus, dev, func) != INVALID_VENDOR)
|
||||||
check_function(bus, dev, func);
|
check_function(pcie_info, bus, dev, func);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PCIManager::check_bus(uint8_t bus)
|
void PCIManager::check_bus(const PCIeInfo& pcie_info, uint8_t bus)
|
||||||
{
|
{
|
||||||
for (uint8_t dev = 0; dev < 32; dev++)
|
for (uint8_t dev = 0; dev < 32; dev++)
|
||||||
check_device(bus, dev);
|
check_device(pcie_info, bus, dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PCIManager::check_all_buses()
|
void PCIManager::check_all_buses(const PCIeInfo& pcie_info)
|
||||||
{
|
{
|
||||||
if (get_header_type(0, 0, 0) & MULTI_FUNCTION)
|
if (get_header_type(0, 0, 0) & MULTI_FUNCTION)
|
||||||
{
|
{
|
||||||
for (int func = 0; func < 8 && get_vendor_id(0, 0, func) != INVALID_VENDOR; func++)
|
for (int func = 0; func < 8 && get_vendor_id(0, 0, func) != INVALID_VENDOR; func++)
|
||||||
check_bus(func);
|
check_bus(pcie_info, func);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
check_bus(0);
|
check_bus(pcie_info, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,32 +291,23 @@ namespace Kernel::PCI
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PCI::Device::set_location(uint8_t bus, uint8_t dev, uint8_t func)
|
|
||||||
{
|
|
||||||
m_bus = bus;
|
|
||||||
m_dev = dev;
|
|
||||||
m_func = func;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PCI::Device::initialize(paddr_t pcie_paddr)
|
void PCI::Device::initialize(paddr_t pcie_paddr)
|
||||||
{
|
{
|
||||||
m_is_valid = true;
|
|
||||||
|
|
||||||
if (pcie_paddr)
|
if (pcie_paddr)
|
||||||
{
|
{
|
||||||
vaddr_t vaddr = PageTable::kernel().reserve_free_page(KERNEL_OFFSET);
|
const vaddr_t vaddr = PageTable::kernel().reserve_free_page(KERNEL_OFFSET);
|
||||||
ASSERT(vaddr);
|
ASSERT(vaddr);
|
||||||
PageTable::kernel().map_page_at(pcie_paddr, vaddr, PageTable::Flags::ReadWrite | PageTable::Flags::Present, PageTable::MemoryType::Uncached);
|
PageTable::kernel().map_page_at(pcie_paddr, vaddr, PageTable::Flags::ReadWrite | PageTable::Flags::Present, PageTable::MemoryType::Uncached);
|
||||||
m_mmio_config = vaddr;
|
m_mmio_config = vaddr;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t type = read_word(0x0A);
|
const uint32_t type = read_word(0x0A);
|
||||||
m_class_code = (uint8_t)(type >> 8);
|
m_class_code = (uint8_t)(type >> 8);
|
||||||
m_subclass = (uint8_t)(type);
|
m_subclass = (uint8_t)(type);
|
||||||
m_prog_if = read_byte(0x09);
|
m_prog_if = read_byte(0x09);
|
||||||
m_header_type = read_byte(0x0E);
|
m_header_type = read_byte(0x0E);
|
||||||
|
|
||||||
uint32_t device = read_dword(0x00);
|
const uint32_t device = read_dword(0x00);
|
||||||
m_vendor_id = device & 0xFFFF;
|
m_vendor_id = device & 0xFFFF;
|
||||||
m_device_id = device >> 16;
|
m_device_id = device >> 16;
|
||||||
|
|
||||||
@@ -303,62 +321,44 @@ namespace Kernel::PCI
|
|||||||
|
|
||||||
uint32_t PCI::Device::read_dword(uint8_t offset) const
|
uint32_t PCI::Device::read_dword(uint8_t offset) const
|
||||||
{
|
{
|
||||||
ASSERT(offset % 4 == 0);
|
|
||||||
if (m_mmio_config)
|
if (m_mmio_config)
|
||||||
return MMIO::read32(m_mmio_config + offset);
|
return MMIO::read32(m_mmio_config + offset);
|
||||||
uint32_t config_addr = 0x80000000 | ((uint32_t)m_bus << 16) | ((uint32_t)m_dev << 11) | ((uint32_t)m_func << 8) | offset;
|
return s_instance->read_config_dword(m_bus, m_dev, m_func, offset);
|
||||||
IO::outl(CONFIG_ADDRESS, config_addr);
|
|
||||||
return IO::inl(CONFIG_DATA);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t PCI::Device::read_word(uint8_t offset) const
|
uint16_t PCI::Device::read_word(uint8_t offset) const
|
||||||
{
|
{
|
||||||
ASSERT(offset % 2 == 0);
|
|
||||||
if (m_mmio_config)
|
if (m_mmio_config)
|
||||||
return MMIO::read16(m_mmio_config + offset);
|
return MMIO::read16(m_mmio_config + offset);
|
||||||
uint32_t dword = read_dword(offset & ~3);
|
return s_instance->read_config_word(m_bus, m_dev, m_func, offset);
|
||||||
return (dword >> ((offset & 3) * 8)) & 0xFFFF;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t PCI::Device::read_byte(uint8_t offset) const
|
uint8_t PCI::Device::read_byte(uint8_t offset) const
|
||||||
{
|
{
|
||||||
if (m_mmio_config)
|
if (m_mmio_config)
|
||||||
return MMIO::read8(m_mmio_config + offset);
|
return MMIO::read8(m_mmio_config + offset);
|
||||||
uint32_t dword = read_dword(offset & ~3);
|
return s_instance->read_config_byte(m_bus, m_dev, m_func, offset);
|
||||||
return (dword >> ((offset & 3) * 8)) & 0xFF;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PCI::Device::write_dword(uint8_t offset, uint32_t value)
|
void PCI::Device::write_dword(uint8_t offset, uint32_t value)
|
||||||
{
|
{
|
||||||
ASSERT(offset % 4 == 0);
|
|
||||||
if (m_mmio_config)
|
if (m_mmio_config)
|
||||||
return MMIO::write32(m_mmio_config + offset, value);
|
return MMIO::write32(m_mmio_config + offset, value);
|
||||||
uint32_t config_addr = 0x80000000 | ((uint32_t)m_bus << 16) | ((uint32_t)m_dev << 11) | ((uint32_t)m_func << 8) | offset;
|
s_instance->write_config_dword(m_bus, m_dev, m_func, offset, value);
|
||||||
IO::outl(CONFIG_ADDRESS, config_addr);
|
|
||||||
IO::outl(CONFIG_DATA, value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PCI::Device::write_word(uint8_t offset, uint16_t value)
|
void PCI::Device::write_word(uint8_t offset, uint16_t value)
|
||||||
{
|
{
|
||||||
ASSERT(offset % 2 == 0);
|
|
||||||
if (m_mmio_config)
|
if (m_mmio_config)
|
||||||
return MMIO::write16(m_mmio_config + offset, value);
|
return MMIO::write16(m_mmio_config + offset, value);
|
||||||
uint32_t byte = (offset & 3) * 8;
|
s_instance->write_config_word(m_bus, m_dev, m_func, offset, value);
|
||||||
uint32_t temp = read_dword(offset & ~3);
|
|
||||||
temp &= ~(0xFFFF << byte);
|
|
||||||
temp |= (uint32_t)value << byte;
|
|
||||||
write_dword(offset & ~3, temp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PCI::Device::write_byte(uint8_t offset, uint8_t value)
|
void PCI::Device::write_byte(uint8_t offset, uint8_t value)
|
||||||
{
|
{
|
||||||
if (m_mmio_config)
|
if (m_mmio_config)
|
||||||
return MMIO::write8(m_mmio_config + offset, value);
|
return MMIO::write8(m_mmio_config + offset, value);
|
||||||
uint32_t byte = (offset & 3) * 8;
|
s_instance->write_config_byte(m_bus, m_dev, m_func, offset, value);
|
||||||
uint32_t temp = read_dword(offset & ~3);
|
|
||||||
temp &= ~(0xFF << byte);
|
|
||||||
temp |= (uint32_t)value << byte;
|
|
||||||
write_dword(offset & ~3, temp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BAN::ErrorOr<BAN::UniqPtr<BarRegion>> PCI::Device::allocate_bar_region(uint8_t bar_num)
|
BAN::ErrorOr<BAN::UniqPtr<BarRegion>> PCI::Device::allocate_bar_region(uint8_t bar_num)
|
||||||
|
|||||||
Reference in New Issue
Block a user