Compare commits
2 Commits
df260fe0e8
...
2a4d986da5
Author | SHA1 | Date |
---|---|---|
Bananymous | 2a4d986da5 | |
Bananymous | 3b18730af6 |
|
@ -56,7 +56,11 @@ namespace Kernel::PCI
|
|||
class Device
|
||||
{
|
||||
public:
|
||||
Device(uint8_t, uint8_t, uint8_t);
|
||||
Device() = default;
|
||||
|
||||
void set_location(uint8_t bus, uint8_t dev, uint8_t func);
|
||||
void initialize(paddr_t pcie_paddr);
|
||||
bool is_valid() const { return m_is_valid; }
|
||||
|
||||
uint32_t read_dword(uint8_t) const;
|
||||
uint16_t read_word(uint8_t) const;
|
||||
|
@ -103,17 +107,20 @@ namespace Kernel::PCI
|
|||
void disable_pin_interrupts();
|
||||
|
||||
private:
|
||||
const uint8_t m_bus;
|
||||
const uint8_t m_dev;
|
||||
const uint8_t m_func;
|
||||
bool m_is_valid { false };
|
||||
uint8_t m_bus { 0 };
|
||||
uint8_t m_dev { 0 };
|
||||
uint8_t m_func { 0 };
|
||||
|
||||
uint8_t m_class_code;
|
||||
uint8_t m_subclass;
|
||||
uint8_t m_prog_if;
|
||||
vaddr_t m_mmio_config { 0 };
|
||||
|
||||
uint8_t m_header_type;
|
||||
uint16_t m_vendor_id;
|
||||
uint16_t m_device_id;
|
||||
uint8_t m_class_code { 0 };
|
||||
uint8_t m_subclass { 0 };
|
||||
uint8_t m_prog_if { 0 };
|
||||
|
||||
uint8_t m_header_type { 0 };
|
||||
uint16_t m_vendor_id { 0 };
|
||||
uint16_t m_device_id { 0 };
|
||||
|
||||
uint32_t m_reserved_irqs { 0 };
|
||||
uint8_t m_reserved_irq_count { 0 };
|
||||
|
@ -131,26 +138,39 @@ namespace Kernel::PCI
|
|||
static void initialize();
|
||||
static PCIManager& get();
|
||||
|
||||
const BAN::Vector<PCI::Device>& devices() const { return m_devices; }
|
||||
void initialize_devices();
|
||||
|
||||
static uint32_t read_config_dword(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset);
|
||||
static uint16_t read_config_word(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset);
|
||||
static uint8_t read_config_byte(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset);
|
||||
template<typename F>
|
||||
void for_each_device(F callback)
|
||||
{
|
||||
for (auto& bus : m_buses)
|
||||
for (auto& dev : bus)
|
||||
for (auto& func : dev)
|
||||
if (func.is_valid())
|
||||
callback(func);
|
||||
};
|
||||
|
||||
static void write_config_dword(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset, uint32_t value);
|
||||
static void write_config_word(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset, uint16_t value);
|
||||
static void write_config_byte(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset, uint8_t value);
|
||||
uint32_t read_config_dword(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset);
|
||||
uint16_t read_config_word(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset);
|
||||
uint8_t read_config_byte(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset);
|
||||
|
||||
void write_config_dword(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset, uint32_t value);
|
||||
void write_config_word(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset, uint16_t value);
|
||||
void write_config_byte(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset, uint8_t value);
|
||||
|
||||
private:
|
||||
PCIManager() = default;
|
||||
PCIManager() : m_bus_pcie_paddr(0) {}
|
||||
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();
|
||||
void initialize_devices();
|
||||
void initialize_impl();
|
||||
|
||||
private:
|
||||
BAN::Vector<PCI::Device> m_devices;
|
||||
using PCIBus = BAN::Array<BAN::Array<Device, 8>, 32>;
|
||||
BAN::Array<PCIBus, 256> m_buses;
|
||||
BAN::Array<paddr_t, 256> m_bus_pcie_paddr;
|
||||
bool m_is_pcie { false };
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -211,9 +211,9 @@ namespace Kernel::ACPI
|
|||
uint64_t result = 0;
|
||||
switch (access_size)
|
||||
{
|
||||
case 1: result = PCI::PCIManager::read_config_byte(0, device, function, offset); break;
|
||||
case 2: result = PCI::PCIManager::read_config_word(0, device, function, offset); break;
|
||||
case 4: result = PCI::PCIManager::read_config_dword(0, device, function, offset); break;
|
||||
case 1: result = PCI::PCIManager::get().read_config_byte(0, device, function, offset); break;
|
||||
case 2: result = PCI::PCIManager::get().read_config_word(0, device, function, offset); break;
|
||||
case 4: result = PCI::PCIManager::get().read_config_dword(0, device, function, offset); break;
|
||||
default:
|
||||
AML_ERROR("FieldElement read_field (PCIConfig) with access size {}", access_size);
|
||||
return {};
|
||||
|
@ -268,9 +268,9 @@ namespace Kernel::ACPI
|
|||
|
||||
switch (access_size)
|
||||
{
|
||||
case 1: PCI::PCIManager::write_config_byte(0, device, function, offset, value); break;
|
||||
case 2: PCI::PCIManager::write_config_word(0, device, function, offset, value); break;
|
||||
case 4: PCI::PCIManager::write_config_dword(0, device, function, offset, value); break;
|
||||
case 1: PCI::PCIManager::get().write_config_byte(0, device, function, offset, value); break;
|
||||
case 2: PCI::PCIManager::get().write_config_word(0, device, function, offset, value); break;
|
||||
case 4: PCI::PCIManager::get().write_config_dword(0, device, function, offset, value); break;
|
||||
default:
|
||||
AML_ERROR("FieldElement write_field (PCIConfig) with access size {}", access_size);
|
||||
return false;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#include <kernel/ACPI/ACPI.h>
|
||||
#include <kernel/IDT.h>
|
||||
#include <kernel/IO.h>
|
||||
#include <kernel/Memory/PageTable.h>
|
||||
|
@ -43,61 +44,43 @@ namespace Kernel::PCI
|
|||
|
||||
uint32_t PCIManager::read_config_dword(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset)
|
||||
{
|
||||
ASSERT(offset % 4 == 0);
|
||||
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);
|
||||
return m_buses[bus][dev][func].read_dword(offset);
|
||||
}
|
||||
|
||||
uint16_t PCIManager::read_config_word(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset)
|
||||
{
|
||||
ASSERT(offset % 2 == 0);
|
||||
uint32_t dword = read_config_dword(bus, dev, func, offset & ~3);
|
||||
return (dword >> ((offset & 3) * 8)) & 0xFFFF;
|
||||
return m_buses[bus][dev][func].read_word(offset);
|
||||
}
|
||||
|
||||
uint8_t PCIManager::read_config_byte(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset)
|
||||
{
|
||||
uint32_t dword = read_config_dword(bus, dev, func, offset & ~3);
|
||||
return (dword >> ((offset & 3) * 8)) & 0xFF;
|
||||
return m_buses[bus][dev][func].read_byte(offset);
|
||||
}
|
||||
|
||||
void PCIManager::write_config_dword(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset, uint32_t value)
|
||||
{
|
||||
ASSERT(offset % 4 == 0);
|
||||
uint32_t config_addr = 0x80000000 | ((uint32_t)bus << 16) | ((uint32_t)dev << 11) | ((uint32_t)func << 8) | offset;
|
||||
IO::outl(CONFIG_ADDRESS, config_addr);
|
||||
IO::outl(CONFIG_DATA, value);
|
||||
m_buses[bus][dev][func].write_dword(offset, value);
|
||||
}
|
||||
|
||||
void PCIManager::write_config_word(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset, uint16_t value)
|
||||
{
|
||||
ASSERT(offset % 2 == 0);
|
||||
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);
|
||||
m_buses[bus][dev][func].write_word(offset, value);
|
||||
}
|
||||
|
||||
void PCIManager::write_config_byte(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset, uint8_t value)
|
||||
{
|
||||
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);
|
||||
m_buses[bus][dev][func].write_byte(offset, value);
|
||||
}
|
||||
|
||||
static uint16_t get_vendor_id(uint8_t bus, uint8_t dev, uint8_t func)
|
||||
{
|
||||
uint32_t dword = PCIManager::read_config_dword(bus, dev, func, 0x00);
|
||||
uint32_t dword = PCIManager::get().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 = PCIManager::read_config_dword(bus, dev, func, 0x0C);
|
||||
uint32_t dword = PCIManager::get().read_config_dword(bus, dev, func, 0x0C);
|
||||
return (dword >> 16) & 0xFF;
|
||||
}
|
||||
|
||||
|
@ -106,8 +89,45 @@ namespace Kernel::PCI
|
|||
ASSERT(s_instance == nullptr);
|
||||
s_instance = new PCIManager();
|
||||
ASSERT(s_instance);
|
||||
s_instance->initialize_impl();
|
||||
}
|
||||
|
||||
void PCIManager::initialize_impl()
|
||||
{
|
||||
struct BAAS
|
||||
{
|
||||
uint64_t addr;
|
||||
uint16_t segment;
|
||||
uint8_t bus_start;
|
||||
uint8_t bus_end;
|
||||
uint32_t __reserved;
|
||||
};
|
||||
static_assert(sizeof(BAAS) == 16);
|
||||
|
||||
if (auto* mcfg = ACPI::ACPI::get().get_header("MCFG", 0))
|
||||
{
|
||||
const size_t count = (mcfg->length - 44) / 16;
|
||||
const BAAS* baas = reinterpret_cast<BAAS*>(reinterpret_cast<vaddr_t>(mcfg) + 44);
|
||||
for (size_t i = 0; i < count; i++, baas++)
|
||||
{
|
||||
// FIXME: support all segments
|
||||
if (baas->segment != 0)
|
||||
continue;
|
||||
for (uint64_t bus = baas->bus_start; bus <= baas->bus_end; bus++)
|
||||
{
|
||||
ASSERT(m_bus_pcie_paddr[bus] == 0);
|
||||
m_bus_pcie_paddr[bus] = baas->addr + (bus << 20);
|
||||
}
|
||||
}
|
||||
|
||||
m_is_pcie = true;
|
||||
}
|
||||
|
||||
for (size_t bus = 0; bus < m_buses.size(); bus++)
|
||||
for (size_t dev = 0; dev < m_buses[bus].size(); dev++)
|
||||
for (size_t func = 0; func < m_buses[bus][dev].size(); func++)
|
||||
m_buses[bus][dev][func].set_location(bus, dev, func);
|
||||
s_instance->check_all_buses();
|
||||
s_instance->initialize_devices();
|
||||
}
|
||||
|
||||
PCIManager& PCIManager::get()
|
||||
|
@ -118,8 +138,9 @@ namespace Kernel::PCI
|
|||
|
||||
void PCIManager::check_function(uint8_t bus, uint8_t dev, uint8_t func)
|
||||
{
|
||||
MUST(m_devices.emplace_back(bus, dev, func));
|
||||
auto& device = m_devices.back();
|
||||
auto& device = m_buses[bus][dev][func];
|
||||
const paddr_t pcie_paddr = m_is_pcie ? m_bus_pcie_paddr[bus] + (((paddr_t)dev << 15) | ((paddr_t)func << 12)) : 0;
|
||||
device.initialize(pcie_paddr);
|
||||
if (device.class_code() == 0x06 && device.subclass() == 0x04)
|
||||
check_bus(device.read_byte(0x19));
|
||||
}
|
||||
|
@ -157,190 +178,63 @@ namespace Kernel::PCI
|
|||
|
||||
void PCIManager::initialize_devices()
|
||||
{
|
||||
for (auto& pci_device : m_devices)
|
||||
{
|
||||
switch (pci_device.class_code())
|
||||
for_each_device(
|
||||
[&](PCI::Device& pci_device)
|
||||
{
|
||||
case 0x01:
|
||||
switch (pci_device.class_code())
|
||||
{
|
||||
switch (pci_device.subclass())
|
||||
case 0x01:
|
||||
{
|
||||
case 0x01:
|
||||
case 0x05:
|
||||
case 0x06:
|
||||
if (auto res = ATAController::create(pci_device); res.is_error())
|
||||
dprintln("ATA: {}", res.error());
|
||||
break;
|
||||
case 0x08:
|
||||
if (auto res = NVMeController::create(pci_device); res.is_error())
|
||||
dprintln("NVMe: {}", res.error());
|
||||
break;
|
||||
default:
|
||||
dprintln("unsupported storage device (pci {2H}.{2H}.{2H})", pci_device.class_code(), pci_device.subclass(), pci_device.prog_if());
|
||||
break;
|
||||
switch (pci_device.subclass())
|
||||
{
|
||||
case 0x01:
|
||||
case 0x05:
|
||||
case 0x06:
|
||||
if (auto res = ATAController::create(pci_device); res.is_error())
|
||||
dprintln("ATA: {}", res.error());
|
||||
break;
|
||||
case 0x08:
|
||||
if (auto res = NVMeController::create(pci_device); res.is_error())
|
||||
dprintln("NVMe: {}", res.error());
|
||||
break;
|
||||
default:
|
||||
dprintln("unsupported storage device (pci {2H}.{2H}.{2H})", pci_device.class_code(), pci_device.subclass(), pci_device.prog_if());
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0x02:
|
||||
{
|
||||
if (auto res = NetworkManager::get().add_interface(pci_device); res.is_error())
|
||||
dprintln("{}", res.error());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
case 0x02:
|
||||
{
|
||||
if (auto res = NetworkManager::get().add_interface(pci_device); res.is_error())
|
||||
dprintln("{}", res.error());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BAN::ErrorOr<BAN::UniqPtr<BarRegion>> BarRegion::create(PCI::Device& device, uint8_t bar_num)
|
||||
{
|
||||
if (device.header_type() != 0x00)
|
||||
{
|
||||
dprintln("BAR regions for non general devices are not supported");
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
}
|
||||
|
||||
// disable io/mem space while reading bar
|
||||
uint16_t command = device.read_word(PCI_REG_COMMAND);
|
||||
device.write_word(PCI_REG_COMMAND, command & ~(PCI_CMD_IO_SPACE | PCI_CMD_MEM_SPACE));
|
||||
|
||||
uint8_t offset = 0x10 + bar_num * 4;
|
||||
|
||||
uint64_t addr = device.read_dword(offset);
|
||||
|
||||
device.write_dword(offset, 0xFFFFFFFF);
|
||||
uint32_t size = device.read_dword(offset);
|
||||
size = ~size + 1;
|
||||
device.write_dword(offset, addr);
|
||||
|
||||
// determine bar type
|
||||
BarType type = BarType::INVALID;
|
||||
if (addr & 1)
|
||||
{
|
||||
type = BarType::IO;
|
||||
addr &= 0xFFFFFFFC;
|
||||
}
|
||||
else if ((addr & 0b110) == 0b000)
|
||||
{
|
||||
type = BarType::MEM;
|
||||
addr &= 0xFFFFFFF0;
|
||||
}
|
||||
else if ((addr & 0b110) == 0b100)
|
||||
{
|
||||
type = BarType::MEM;
|
||||
addr &= 0xFFFFFFF0;
|
||||
addr |= (uint64_t)device.read_dword(offset + 4) << 32;
|
||||
}
|
||||
|
||||
if (type == BarType::INVALID)
|
||||
{
|
||||
dwarnln("invalid pci device bar");
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
|
||||
auto* region_ptr = new BarRegion(type, addr, size);
|
||||
ASSERT(region_ptr);
|
||||
|
||||
auto region = BAN::UniqPtr<BarRegion>::adopt(region_ptr);
|
||||
TRY(region->initialize());
|
||||
|
||||
// restore old command register and enable correct IO/MEM space
|
||||
command |= (type == BarType::IO) ? PCI_CMD_IO_SPACE : PCI_CMD_MEM_SPACE;
|
||||
device.write_word(PCI_REG_COMMAND, command);
|
||||
|
||||
#if DEBUG_PCI
|
||||
dprintln("created BAR region for PCI {2H}:{2H}.{2H}",
|
||||
device.bus(),
|
||||
device.dev(),
|
||||
device.func()
|
||||
);
|
||||
dprintln(" type: {}", region->type() == BarType::IO ? "IO" : "MEM");
|
||||
if (region->type() == BarType::IO)
|
||||
dprintln(" iobase {8H}", region->iobase());
|
||||
else
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
m_is_valid = true;
|
||||
|
||||
if (pcie_paddr)
|
||||
{
|
||||
dprintln(" paddr {}", (void*)region->paddr());
|
||||
dprintln(" vaddr {}", (void*)region->vaddr());
|
||||
vaddr_t vaddr = PageTable::kernel().reserve_free_page(KERNEL_OFFSET);
|
||||
ASSERT(vaddr);
|
||||
PageTable::kernel().map_page_at(pcie_paddr, vaddr, PageTable::Flags::ReadWrite | PageTable::Flags::Present);
|
||||
m_mmio_config = vaddr;
|
||||
}
|
||||
dprintln(" size {}", region->size());
|
||||
#endif
|
||||
|
||||
return region;
|
||||
}
|
||||
|
||||
BarRegion::BarRegion(BarType type, paddr_t paddr, size_t size)
|
||||
: m_type(type)
|
||||
, m_paddr(paddr)
|
||||
, m_size(size)
|
||||
{ }
|
||||
|
||||
BarRegion::~BarRegion()
|
||||
{
|
||||
if (m_type == BarType::MEM && m_vaddr)
|
||||
PageTable::kernel().unmap_range(m_vaddr, m_size);
|
||||
m_vaddr = 0;
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> BarRegion::initialize()
|
||||
{
|
||||
if (m_type == BarType::IO)
|
||||
return {};
|
||||
|
||||
size_t needed_pages = BAN::Math::div_round_up<size_t>(m_size, PAGE_SIZE);
|
||||
m_vaddr = PageTable::kernel().reserve_free_contiguous_pages(needed_pages, KERNEL_OFFSET);
|
||||
if (m_vaddr == 0)
|
||||
return BAN::Error::from_errno(ENOMEM);
|
||||
PageTable::kernel().map_range_at(m_paddr, m_vaddr, m_size, PageTable::Flags::CacheDisable | PageTable::Flags::ReadWrite | PageTable::Flags::Present);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void BarRegion::write8(off_t reg, uint8_t val)
|
||||
{
|
||||
if (m_type == BarType::IO)
|
||||
return IO::outb(m_paddr + reg, val);
|
||||
MMIO::write8(m_vaddr + reg, val);
|
||||
}
|
||||
|
||||
void BarRegion::write16(off_t reg, uint16_t val)
|
||||
{
|
||||
if (m_type == BarType::IO)
|
||||
return IO::outw(m_paddr + reg, val);
|
||||
MMIO::write16(m_vaddr + reg, val);
|
||||
}
|
||||
|
||||
void BarRegion::write32(off_t reg, uint32_t val)
|
||||
{
|
||||
if (m_type == BarType::IO)
|
||||
return IO::outl(m_paddr + reg, val);
|
||||
MMIO::write32(m_vaddr + reg, val);
|
||||
}
|
||||
|
||||
uint8_t BarRegion::read8(off_t reg)
|
||||
{
|
||||
if (m_type == BarType::IO)
|
||||
return IO::inb(m_paddr + reg);
|
||||
return MMIO::read8(m_vaddr + reg);
|
||||
}
|
||||
|
||||
uint16_t BarRegion::read16(off_t reg)
|
||||
{
|
||||
if (m_type == BarType::IO)
|
||||
return IO::inw(m_paddr + reg);
|
||||
return MMIO::read16(m_vaddr + reg);
|
||||
}
|
||||
|
||||
uint32_t BarRegion::read32(off_t reg)
|
||||
{
|
||||
if (m_type == BarType::IO)
|
||||
return IO::inl(m_paddr + reg);
|
||||
return MMIO::read32(m_vaddr + reg);
|
||||
}
|
||||
|
||||
PCI::Device::Device(uint8_t bus, uint8_t dev, uint8_t func)
|
||||
: m_bus(bus), m_dev(dev), m_func(func)
|
||||
{
|
||||
uint32_t type = read_word(0x0A);
|
||||
m_class_code = (uint8_t)(type >> 8);
|
||||
m_subclass = (uint8_t)(type);
|
||||
|
@ -362,35 +256,61 @@ namespace Kernel::PCI
|
|||
uint32_t PCI::Device::read_dword(uint8_t offset) const
|
||||
{
|
||||
ASSERT(offset % 4 == 0);
|
||||
return PCIManager::read_config_dword(m_bus, m_dev, m_func, offset);
|
||||
if (m_mmio_config)
|
||||
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;
|
||||
IO::outl(CONFIG_ADDRESS, config_addr);
|
||||
return IO::inl(CONFIG_DATA);
|
||||
}
|
||||
|
||||
uint16_t PCI::Device::read_word(uint8_t offset) const
|
||||
{
|
||||
ASSERT(offset % 2 == 0);
|
||||
return PCIManager::read_config_word(m_bus, m_dev, m_func, offset);
|
||||
if (m_mmio_config)
|
||||
return MMIO::read16(m_mmio_config + offset);
|
||||
uint32_t dword = read_dword(offset & ~3);
|
||||
return (dword >> ((offset & 3) * 8)) & 0xFFFF;
|
||||
}
|
||||
|
||||
uint8_t PCI::Device::read_byte(uint8_t offset) const
|
||||
{
|
||||
return PCIManager::read_config_byte(m_bus, m_dev, m_func, offset);
|
||||
if (m_mmio_config)
|
||||
return MMIO::read8(m_mmio_config + offset);
|
||||
uint32_t dword = read_dword(offset & ~3);
|
||||
return (dword >> ((offset & 3) * 8)) & 0xFF;
|
||||
}
|
||||
|
||||
void PCI::Device::write_dword(uint8_t offset, uint32_t value)
|
||||
{
|
||||
ASSERT(offset % 4 == 0);
|
||||
PCIManager::write_config_dword(m_bus, m_dev, m_func, offset, value);
|
||||
if (m_mmio_config)
|
||||
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;
|
||||
IO::outl(CONFIG_ADDRESS, config_addr);
|
||||
IO::outl(CONFIG_DATA, value);
|
||||
}
|
||||
|
||||
void PCI::Device::write_word(uint8_t offset, uint16_t value)
|
||||
{
|
||||
ASSERT(offset % 2 == 0);
|
||||
PCIManager::write_config_word(m_bus, m_dev, m_func, offset, value);
|
||||
if (m_mmio_config)
|
||||
return MMIO::write16(m_mmio_config + offset, value);
|
||||
uint32_t byte = (offset & 3) * 8;
|
||||
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)
|
||||
{
|
||||
PCIManager::write_config_byte(m_bus, m_dev, m_func, offset, value);
|
||||
if (m_mmio_config)
|
||||
return MMIO::write8(m_mmio_config + offset, value);
|
||||
uint32_t byte = (offset & 3) * 8;
|
||||
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)
|
||||
|
@ -619,4 +539,149 @@ namespace Kernel::PCI
|
|||
set_command_bits(PCI_CMD_INTERRUPT_DISABLE);
|
||||
}
|
||||
|
||||
BAN::ErrorOr<BAN::UniqPtr<BarRegion>> BarRegion::create(PCI::Device& device, uint8_t bar_num)
|
||||
{
|
||||
if (device.header_type() != 0x00)
|
||||
{
|
||||
dprintln("BAR regions for non general devices are not supported");
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
}
|
||||
|
||||
// disable io/mem space while reading bar
|
||||
uint16_t command = device.read_word(PCI_REG_COMMAND);
|
||||
device.write_word(PCI_REG_COMMAND, command & ~(PCI_CMD_IO_SPACE | PCI_CMD_MEM_SPACE));
|
||||
|
||||
uint8_t offset = 0x10 + bar_num * 4;
|
||||
|
||||
uint64_t addr = device.read_dword(offset);
|
||||
|
||||
device.write_dword(offset, 0xFFFFFFFF);
|
||||
uint32_t size = device.read_dword(offset);
|
||||
size = ~size + 1;
|
||||
device.write_dword(offset, addr);
|
||||
|
||||
// determine bar type
|
||||
BarType type = BarType::INVALID;
|
||||
if (addr & 1)
|
||||
{
|
||||
type = BarType::IO;
|
||||
addr &= 0xFFFFFFFC;
|
||||
}
|
||||
else if ((addr & 0b110) == 0b000)
|
||||
{
|
||||
type = BarType::MEM;
|
||||
addr &= 0xFFFFFFF0;
|
||||
}
|
||||
else if ((addr & 0b110) == 0b100)
|
||||
{
|
||||
type = BarType::MEM;
|
||||
addr &= 0xFFFFFFF0;
|
||||
addr |= (uint64_t)device.read_dword(offset + 4) << 32;
|
||||
}
|
||||
|
||||
if (type == BarType::INVALID)
|
||||
{
|
||||
dwarnln("invalid pci device bar");
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
|
||||
auto* region_ptr = new BarRegion(type, addr, size);
|
||||
ASSERT(region_ptr);
|
||||
|
||||
auto region = BAN::UniqPtr<BarRegion>::adopt(region_ptr);
|
||||
TRY(region->initialize());
|
||||
|
||||
// restore old command register and enable correct IO/MEM space
|
||||
command |= (type == BarType::IO) ? PCI_CMD_IO_SPACE : PCI_CMD_MEM_SPACE;
|
||||
device.write_word(PCI_REG_COMMAND, command);
|
||||
|
||||
#if DEBUG_PCI
|
||||
dprintln("created BAR region for PCI {2H}:{2H}.{2H}",
|
||||
device.bus(),
|
||||
device.dev(),
|
||||
device.func()
|
||||
);
|
||||
dprintln(" type: {}", region->type() == BarType::IO ? "IO" : "MEM");
|
||||
if (region->type() == BarType::IO)
|
||||
dprintln(" iobase {8H}", region->iobase());
|
||||
else
|
||||
{
|
||||
dprintln(" paddr {}", (void*)region->paddr());
|
||||
dprintln(" vaddr {}", (void*)region->vaddr());
|
||||
}
|
||||
dprintln(" size {}", region->size());
|
||||
#endif
|
||||
|
||||
return region;
|
||||
}
|
||||
|
||||
BarRegion::BarRegion(BarType type, paddr_t paddr, size_t size)
|
||||
: m_type(type)
|
||||
, m_paddr(paddr)
|
||||
, m_size(size)
|
||||
{ }
|
||||
|
||||
BarRegion::~BarRegion()
|
||||
{
|
||||
if (m_type == BarType::MEM && m_vaddr)
|
||||
PageTable::kernel().unmap_range(m_vaddr, m_size);
|
||||
m_vaddr = 0;
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> BarRegion::initialize()
|
||||
{
|
||||
if (m_type == BarType::IO)
|
||||
return {};
|
||||
|
||||
size_t needed_pages = BAN::Math::div_round_up<size_t>(m_size, PAGE_SIZE);
|
||||
m_vaddr = PageTable::kernel().reserve_free_contiguous_pages(needed_pages, KERNEL_OFFSET);
|
||||
if (m_vaddr == 0)
|
||||
return BAN::Error::from_errno(ENOMEM);
|
||||
PageTable::kernel().map_range_at(m_paddr, m_vaddr, m_size, PageTable::Flags::CacheDisable | PageTable::Flags::ReadWrite | PageTable::Flags::Present);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void BarRegion::write8(off_t reg, uint8_t val)
|
||||
{
|
||||
if (m_type == BarType::IO)
|
||||
return IO::outb(m_paddr + reg, val);
|
||||
MMIO::write8(m_vaddr + reg, val);
|
||||
}
|
||||
|
||||
void BarRegion::write16(off_t reg, uint16_t val)
|
||||
{
|
||||
if (m_type == BarType::IO)
|
||||
return IO::outw(m_paddr + reg, val);
|
||||
MMIO::write16(m_vaddr + reg, val);
|
||||
}
|
||||
|
||||
void BarRegion::write32(off_t reg, uint32_t val)
|
||||
{
|
||||
if (m_type == BarType::IO)
|
||||
return IO::outl(m_paddr + reg, val);
|
||||
MMIO::write32(m_vaddr + reg, val);
|
||||
}
|
||||
|
||||
uint8_t BarRegion::read8(off_t reg)
|
||||
{
|
||||
if (m_type == BarType::IO)
|
||||
return IO::inb(m_paddr + reg);
|
||||
return MMIO::read8(m_vaddr + reg);
|
||||
}
|
||||
|
||||
uint16_t BarRegion::read16(off_t reg)
|
||||
{
|
||||
if (m_type == BarType::IO)
|
||||
return IO::inw(m_paddr + reg);
|
||||
return MMIO::read16(m_vaddr + reg);
|
||||
}
|
||||
|
||||
uint32_t BarRegion::read32(off_t reg)
|
||||
{
|
||||
if (m_type == BarType::IO)
|
||||
return IO::inl(m_paddr + reg);
|
||||
return MMIO::read32(m_vaddr + reg);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -176,7 +176,12 @@ static void init2(void*)
|
|||
|
||||
auto console = MUST(DevFileSystem::get().root_inode()->find_inode(cmdline.console));
|
||||
ASSERT(console->is_tty());
|
||||
((TTY*)console.ptr())->set_as_current();
|
||||
static_cast<Kernel::TTY*>(console.ptr())->set_as_current();
|
||||
|
||||
// This only initializes PCIManager by enumerating available devices and choosing PCIe/legacy
|
||||
// ACPI might require PCI access during its namespace initialization
|
||||
PCI::PCIManager::initialize();
|
||||
dprintln("PCI initialized");
|
||||
|
||||
if (ACPI::ACPI::get().enter_acpi_mode(InterruptController::get().is_using_apic()).is_error())
|
||||
dprintln("Failed to enter ACPI mode");
|
||||
|
@ -197,8 +202,8 @@ static void init2(void*)
|
|||
|
||||
// NOTE: PCI devices are the last ones to be initialized
|
||||
// so other devices can reserve predefined interrupts
|
||||
PCI::PCIManager::initialize();
|
||||
dprintln("PCI initialized");
|
||||
PCI::PCIManager::get().initialize_devices();
|
||||
dprintln("PCI devices initialized");
|
||||
|
||||
VirtualFileSystem::initialize(cmdline.root);
|
||||
dprintln("VFS initialized");
|
||||
|
|
|
@ -206,6 +206,15 @@ if [[ -f $BANAN_TOOLCHAIN_PREFIX/bin/$BANAN_TOOLCHAIN_TRIPLE_PREFIX-gcc ]]; then
|
|||
fi
|
||||
fi
|
||||
|
||||
BUILD_LIBSTDCPP=0
|
||||
#BUILD_LIBSTDCPP=$BUILD_GCC
|
||||
#if ! (($BUILD_LIBSTDCPP)); then
|
||||
# read -e -p "Do you want to rebuild libstdc++ [y/N]? " choice
|
||||
# if [[ "$choice" == [Yy]* ]]; then
|
||||
# BUILD_LIBSTDCPP=1
|
||||
# fi
|
||||
#fi
|
||||
|
||||
BUILD_GRUB=1
|
||||
if [[ -f $BANAN_TOOLCHAIN_PREFIX/bin/grub-mkstandalone ]]; then
|
||||
echo "You already seem to have a grub installed."
|
||||
|
@ -251,7 +260,7 @@ if (($BUILD_CMAKE)); then
|
|||
build_cmake
|
||||
fi
|
||||
|
||||
if (($BUILD_GCC)); then
|
||||
if (($BUILD_LIBSTDCPP)); then
|
||||
# delete sysroot and install libc
|
||||
rm -r $BANAN_SYSROOT
|
||||
$BANAN_SCRIPT_DIR/build.sh libc-install
|
||||
|
|
Loading…
Reference in New Issue