Kernel: Add basic PCI enumeration

This commit is contained in:
Bananymous 2023-02-26 02:56:53 +02:00
parent dc1a4614fb
commit 04bb08d27f
3 changed files with 161 additions and 0 deletions

View File

@ -45,6 +45,7 @@ kernel/Input.o \
kernel/InterruptController.o \
kernel/kernel.o \
kernel/kmalloc.o \
kernel/PCI.o \
kernel/PIC.o \
kernel/PIT.o \
kernel/RTC.o \

View File

@ -0,0 +1,42 @@
#pragma once
#include <BAN/Vector.h>
namespace Kernel
{
class PCIDevice
{
public:
const uint8_t bus;
const uint8_t dev;
const uint8_t func;
const uint8_t class_code;
const uint8_t subclass;
uint32_t read_dword(uint8_t) const;
uint16_t read_word(uint8_t) const;
uint8_t read_byte(uint8_t) const;
};
class PCI
{
public:
static bool initialize();
static PCI& get();
const BAN::Vector<PCIDevice>& devices() const { return m_devices; }
private:
PCI() = default;
void check_function(uint8_t bus, uint8_t dev, uint8_t func);
void check_device(uint8_t bus, uint8_t dev);
void check_bus(uint8_t bus);
void check_all_buses();
private:
BAN::Vector<PCIDevice> m_devices;
};
}

118
kernel/kernel/PCI.cpp Normal file
View File

@ -0,0 +1,118 @@
#include <kernel/Debug.h>
#include <kernel/IO.h>
#include <kernel/PCI.h>
#define INVALID 0xFFFF
#define MULTI_FUNCTION 0x80
#define CONFIG_ADDRESS 0xCF8
#define CONFIG_DATA 0xCFC
namespace Kernel
{
PCI* s_instance = nullptr;
bool PCI::initialize()
{
ASSERT(s_instance == nullptr);
s_instance = new PCI();
ASSERT(s_instance);
s_instance->check_all_buses();
return !s_instance->m_devices.empty();
}
PCI& PCI::get()
{
ASSERT(s_instance);
return *s_instance;
}
static uint32_t read_config_dword(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset)
{
uint32_t config_addr = 0x80000000 | ((uint32_t)bus << 16) | ((uint32_t)dev << 11) | ((uint32_t)func << 8) | offset;
IO::outl(CONFIG_ADDRESS, config_addr);
return IO::inl(CONFIG_DATA);
}
static uint16_t get_vendor_id(uint8_t bus, uint8_t dev, uint8_t func)
{
uint32_t dword = read_config_dword(bus, dev, func, 0x00);
return dword & 0xFFFF;
}
static uint8_t get_header_type(uint8_t bus, uint8_t dev, uint8_t func)
{
uint32_t dword = read_config_dword(bus, dev, func, 0x0C);
return (dword >> 16) & 0xFF;
}
void PCI::check_function(uint8_t bus, uint8_t dev, uint8_t func)
{
uint32_t type = read_config_dword(bus, dev, func, 0x08);
PCIDevice device {
bus,
dev,
func,
(uint8_t)(type >> 24),
(uint8_t)(type >> 16),
};
MUST(m_devices.push_back(BAN::move(device)));
}
void PCI::check_device(uint8_t bus, uint8_t dev)
{
if (get_vendor_id(bus, dev, 0) == INVALID)
return;
if (get_header_type(bus, dev, 0) & MULTI_FUNCTION)
{
for (uint8_t func = 0; func < 8; func++)
if (get_vendor_id(bus, dev, func) != INVALID)
check_function(bus, dev, func);
}
else
{
check_function(bus, dev, 0);
}
}
void PCI::check_bus(uint8_t bus)
{
for (uint8_t dev = 0; dev < 32; dev++)
check_device(bus, dev);
}
void PCI::check_all_buses()
{
if (get_header_type(0, 0, 0) & MULTI_FUNCTION)
{
for (int func = 0; func < 8; func++)
if (get_vendor_id(0, 0, func) != INVALID)
check_bus(func);
}
else
{
check_bus(0);
}
}
uint32_t PCIDevice::read_dword(uint8_t offset) const
{
ASSERT((offset & 0x03) == 0);
return read_config_dword(bus, dev, func, offset);
}
uint16_t PCIDevice::read_word(uint8_t offset) const
{
ASSERT((offset & 0x01) == 0);
uint32_t dword = read_config_dword(bus, dev, func, offset & 0xFC);
return (uint16_t)(dword >> (offset & 0x03));
}
uint8_t PCIDevice::read_byte(uint8_t offset) const
{
uint32_t dword = read_config_dword(bus, dev, func, offset & 0xFC);
return (uint8_t)(dword >> (offset & 0x03));
}
}