From 11717f90c12cfaf74a2a42130ff8de37fc27240b Mon Sep 17 00:00:00 2001 From: Bananymous Date: Mon, 18 Sep 2023 13:53:10 +0300 Subject: [PATCH] Kernel: PCI devices can now create region for BAR This creates either MEM or IO region for read/write access to PCI device. --- kernel/include/kernel/Networking/E1000.h | 9 +- kernel/include/kernel/PCI.h | 94 +++++-- kernel/include/kernel/Storage/ATAController.h | 4 +- kernel/kernel/Networking/E1000.cpp | 76 +---- kernel/kernel/PCI.cpp | 262 ++++++++++++++---- kernel/kernel/Storage/ATAController.cpp | 4 +- kernel/kernel/kernel.cpp | 2 +- 7 files changed, 296 insertions(+), 155 deletions(-) diff --git a/kernel/include/kernel/Networking/E1000.h b/kernel/include/kernel/Networking/E1000.h index 9ed171dfd4..dad2961326 100644 --- a/kernel/include/kernel/Networking/E1000.h +++ b/kernel/include/kernel/Networking/E1000.h @@ -13,7 +13,7 @@ namespace Kernel class E1000 final : public NetworkDriver { public: - static BAN::ErrorOr> create(const PCIDevice&); + static BAN::ErrorOr> create(PCI::Device&); ~E1000(); virtual uint8_t* get_mac_address() override { return m_mac_address; } @@ -24,12 +24,12 @@ namespace Kernel private: E1000() = default; - BAN::ErrorOr initialize(const PCIDevice&); + BAN::ErrorOr initialize(PCI::Device&); static void interrupt_handler(); - void write32(uint16_t reg, uint32_t value); uint32_t read32(uint16_t reg); + void write32(uint16_t reg, uint32_t value); void detect_eeprom(); uint32_t eeprom_read(uint8_t addr); @@ -44,8 +44,7 @@ namespace Kernel void handle_receive(); private: - PCIDevice::BarType m_bar_type {}; - uint64_t m_bar_addr {}; + BAN::UniqPtr m_bar_region; bool m_has_eerprom { false }; uint8_t m_mac_address[6] {}; uint16_t m_rx_current {}; diff --git a/kernel/include/kernel/PCI.h b/kernel/include/kernel/PCI.h index d5db2960a0..1041c07ea4 100644 --- a/kernel/include/kernel/PCI.h +++ b/kernel/include/kernel/PCI.h @@ -1,28 +1,63 @@ #pragma once +#include #include +#include -namespace Kernel +namespace Kernel::PCI { - class PCIDevice + enum class BarType { - public: - enum class BarType - { - INVAL, - MEM, - IO, - }; + INVALID, + MEM, + IO, + }; + + class Device; + + class BarRegion + { + BAN_NON_COPYABLE(BarRegion); public: - PCIDevice(uint8_t, uint8_t, uint8_t); + static BAN::ErrorOr> create(PCI::Device&, uint8_t bar_num); + ~BarRegion(); + + BarType type() const { return m_type; } + vaddr_t vaddr() const { return m_vaddr; } + paddr_t paddr() const { return m_paddr; } + size_t size() const { return m_size; } + + void write8(off_t, uint8_t); + void write16(off_t, uint16_t); + void write32(off_t, uint32_t); + + uint8_t read8(off_t); + uint16_t read16(off_t); + uint32_t read32(off_t); + + private: + BarRegion(BarType, paddr_t, size_t); + BAN::ErrorOr initialize(); + + private: + const BarType m_type {}; + const paddr_t m_paddr {}; + const size_t m_size {}; + vaddr_t m_vaddr {}; + }; + + class Device + { + public: + Device(uint8_t, uint8_t, uint8_t); uint32_t read_dword(uint8_t) const; uint16_t read_word(uint8_t) const; uint8_t read_byte(uint8_t) const; - void write_dword(uint8_t, uint32_t) const; + void write_dword(uint8_t, uint32_t); uint8_t bus() const { return m_bus; } uint8_t dev() const { return m_dev; } @@ -32,17 +67,24 @@ namespace Kernel uint8_t subclass() const { return m_subclass; } uint8_t prog_if() const { return m_prog_if; } - BarType read_bar_type(uint8_t) const; - uint64_t read_bar_address(uint8_t) const; + uint8_t header_type() const { return m_header_type; } - void enable_bus_mastering() const; - void disable_bus_mastering() const; + BAN::ErrorOr> allocate_bar_region(uint8_t bar_num); - void enable_memory_space() const; - void disable_memory_space() const; + void enable_bus_mastering(); + void disable_bus_mastering(); - void enable_pin_interrupts() const; - void disable_pin_interrupts() const; + void enable_memory_space(); + void disable_memory_space(); + + void enable_io_space(); + void disable_io_space(); + + void enable_pin_interrupts(); + void disable_pin_interrupts(); + + private: + void enumerate_capabilites(); private: uint8_t m_bus; @@ -56,19 +98,19 @@ namespace Kernel uint8_t m_header_type; }; - class PCI + class PCIManager { - BAN_NON_COPYABLE(PCI); - BAN_NON_MOVABLE(PCI); + BAN_NON_COPYABLE(PCIManager); + BAN_NON_MOVABLE(PCIManager); public: static void initialize(); - static PCI& get(); + static PCIManager& get(); - const BAN::Vector& devices() const { return m_devices; } + const BAN::Vector& devices() const { return m_devices; } private: - PCI() = default; + PCIManager() = 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); @@ -76,7 +118,7 @@ namespace Kernel void initialize_devices(); private: - BAN::Vector m_devices; + BAN::Vector m_devices; }; } \ No newline at end of file diff --git a/kernel/include/kernel/Storage/ATAController.h b/kernel/include/kernel/Storage/ATAController.h index 433d98f292..c8de79e979 100644 --- a/kernel/include/kernel/Storage/ATAController.h +++ b/kernel/include/kernel/Storage/ATAController.h @@ -11,13 +11,13 @@ namespace Kernel class ATAController final : public StorageController { public: - static BAN::ErrorOr> create(const PCIDevice&); + static BAN::ErrorOr> create(const PCI::Device&); virtual BAN::Vector> devices() override; private: ATAController(); - BAN::ErrorOr initialize(const PCIDevice& device); + BAN::ErrorOr initialize(const PCI::Device& device); private: ATABus* m_buses[2] { nullptr, nullptr }; diff --git a/kernel/kernel/Networking/E1000.cpp b/kernel/kernel/Networking/E1000.cpp index 5e5b1a9be9..be45183ec0 100644 --- a/kernel/kernel/Networking/E1000.cpp +++ b/kernel/kernel/Networking/E1000.cpp @@ -5,7 +5,7 @@ #include #include -#define E1000_GENERAL_MEM_SIZE (128 * 1024) +#define DEBUG_E1000 1 #define E1000_REG_CTRL 0x0000 #define E1000_REG_STATUS 0x0008 @@ -112,7 +112,7 @@ namespace Kernel volatile uint16_t special; } __attribute__((packed)); - BAN::ErrorOr> E1000::create(const PCIDevice& pci_device) + BAN::ErrorOr> E1000::create(PCI::Device& pci_device) { E1000* e1000 = new E1000(); ASSERT(e1000); @@ -126,42 +126,26 @@ namespace Kernel E1000::~E1000() { - if (m_bar_type == PCIDevice::BarType::MEM && m_bar_addr) - PageTable::kernel().unmap_range(m_bar_addr & PAGE_ADDR_MASK, E1000_GENERAL_MEM_SIZE); } - BAN::ErrorOr E1000::initialize(const PCIDevice& pci_device) + BAN::ErrorOr E1000::initialize(PCI::Device& pci_device) { - m_bar_type = pci_device.read_bar_type(0); - if (m_bar_type == PCIDevice::BarType::INVAL) - { - dwarnln("invalid bar0 type"); - return BAN::Error::from_errno(EINVAL); - } - - if (m_bar_type == PCIDevice::BarType::MEM) - { - uint64_t bar_addr = pci_device.read_bar_address(0); - - vaddr_t page_vaddr = PageTable::kernel().reserve_free_contiguous_pages(E1000_GENERAL_MEM_SIZE / PAGE_SIZE, KERNEL_OFFSET); - paddr_t page_paddr = bar_addr & PAGE_ADDR_MASK; - PageTable::kernel().map_range_at(page_paddr, page_vaddr, E1000_GENERAL_MEM_SIZE, PageTable::Flags::CacheDisable | PageTable::Flags::ReadWrite | PageTable::Flags::Present); - - m_bar_addr = page_vaddr + (bar_addr % PAGE_SIZE); - } - else if (m_bar_type == PCIDevice::BarType::IO) - { - m_bar_addr = pci_device.read_bar_address(0); - } - + m_bar_region = TRY(pci_device.allocate_bar_region(0)); pci_device.enable_bus_mastering(); detect_eeprom(); TRY(read_mac_address()); - dprintln("E1000 at PCI {}:{}.{}", pci_device.bus(), pci_device.dev(), pci_device.func()); + initialize_rx(); + initialize_tx(); + + enable_link(); + enable_interrupts(); + +#if DEBUG_E1000 + dprintln("E1000 at PCI {}:{}.{}", pci_device.bus(), pci_device.dev(), pci_device.func()); dprintln(" MAC: {2H}:{2H}:{2H}:{2H}:{2H}:{2H}", m_mac_address[0], m_mac_address[1], @@ -170,52 +154,22 @@ namespace Kernel m_mac_address[4], m_mac_address[5] ); - - initialize_rx(); - initialize_tx(); - - enable_link(); - enable_interrupts(); - dprintln(" link up: {}", link_up()); if (link_up()) dprintln(" link speed: {} Mbps", link_speed()); +#endif return {}; } void E1000::write32(uint16_t reg, uint32_t value) { - switch (m_bar_type) - { - case PCIDevice::BarType::MEM: - MMIO::write32(m_bar_addr + reg, value); - break; - case PCIDevice::BarType::IO: - IO::outl(m_bar_addr, reg); - IO::outl(m_bar_addr + 4, value); - break; - default: - ASSERT_NOT_REACHED(); - } + m_bar_region->write32(reg, value); } uint32_t E1000::read32(uint16_t reg) { - uint32_t result = 0; - switch (m_bar_type) - { - case PCIDevice::BarType::MEM: - result = MMIO::read32(m_bar_addr + reg); - break; - case PCIDevice::BarType::IO: - IO::outl(m_bar_addr, reg); - result = IO::inl(m_bar_addr + 4); - break; - default: - ASSERT_NOT_REACHED(); - } - return result; + return m_bar_region->read32(reg); } void E1000::detect_eeprom() diff --git a/kernel/kernel/PCI.cpp b/kernel/kernel/PCI.cpp index 73400f3347..9dd8b012f3 100644 --- a/kernel/kernel/PCI.cpp +++ b/kernel/kernel/PCI.cpp @@ -1,33 +1,22 @@ #include +#include +#include #include #include #include -#define INVALID 0xFFFF +#define INVALID_VENDOR 0xFFFF #define MULTI_FUNCTION 0x80 #define CONFIG_ADDRESS 0xCF8 #define CONFIG_DATA 0xCFC -namespace Kernel +#define DEBUG_PCI 1 + +namespace Kernel::PCI { - static PCI* s_instance = nullptr; - - void PCI::initialize() - { - ASSERT(s_instance == nullptr); - s_instance = new PCI(); - ASSERT(s_instance); - s_instance->check_all_buses(); - s_instance->initialize_devices(); - } - - PCI& PCI::get() - { - ASSERT(s_instance); - return *s_instance; - } + static PCIManager* s_instance = nullptr; static uint32_t read_config_dword(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset) { @@ -55,7 +44,22 @@ namespace Kernel return (dword >> 16) & 0xFF; } - void PCI::check_function(uint8_t bus, uint8_t dev, uint8_t func) + void PCIManager::initialize() + { + ASSERT(s_instance == nullptr); + s_instance = new PCIManager(); + ASSERT(s_instance); + s_instance->check_all_buses(); + s_instance->initialize_devices(); + } + + PCIManager& PCIManager::get() + { + ASSERT(s_instance); + return *s_instance; + } + + 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(); @@ -63,29 +67,29 @@ namespace Kernel check_bus(device.read_byte(0x19)); } - void PCI::check_device(uint8_t bus, uint8_t dev) + void PCIManager::check_device(uint8_t bus, uint8_t dev) { - if (get_vendor_id(bus, dev, 0) == INVALID) + if (get_vendor_id(bus, dev, 0) == INVALID_VENDOR) return; check_function(bus, dev, 0); if (get_header_type(bus, dev, 0) & MULTI_FUNCTION) for (uint8_t func = 1; func < 8; func++) - if (get_vendor_id(bus, dev, func) != INVALID) + if (get_vendor_id(bus, dev, func) != INVALID_VENDOR) check_function(bus, dev, func); } - void PCI::check_bus(uint8_t bus) + void PCIManager::check_bus(uint8_t bus) { for (uint8_t dev = 0; dev < 32; dev++) check_device(bus, dev); } - void PCI::check_all_buses() + void PCIManager::check_all_buses() { if (get_header_type(0, 0, 0) & MULTI_FUNCTION) { - for (int func = 0; func < 8 && get_vendor_id(0, 0, func) != INVALID; func++) + for (int func = 0; func < 8 && get_vendor_id(0, 0, func) != INVALID_VENDOR; func++) check_bus(func); } else @@ -94,9 +98,9 @@ namespace Kernel } } - void PCI::initialize_devices() + void PCIManager::initialize_devices() { - for (const auto& pci_device : PCI::get().devices()) + for (auto& pci_device : m_devices) { switch (pci_device.class_code()) { @@ -134,7 +138,144 @@ namespace Kernel } } - PCIDevice::PCIDevice(uint8_t bus, uint8_t dev, uint8_t func) + BAN::ErrorOr> BarRegion::create(PCI::Device& device, uint8_t bar_num) + { + ASSERT(device.header_type() == 0x00); + + uint32_t command_status = device.read_dword(0x04); + + // disable io/mem space while reading bar + device.write_dword(0x04, command_status & ~3); + + uint8_t offset = 0x10 + bar_num * 8; + + uint64_t addr = device.read_dword(offset); + + device.write_dword(offset, 0xFFFFFFFF); + uint32_t size = device.read_dword(0x10 + bar_num * 8); + 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 + 8) << 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::adopt(region_ptr); + TRY(region->initialize()); + + // restore old command register and enable correct IO/MEM + command_status |= (type == BarType::IO) ? 1 : 2; + device.write_dword(0x04, command_status); + +#if DEBUG_PCI + dprintln("created BAR region for PCI {}:{}.{}", + device.bus(), + device.dev(), + device.func() + ); + dprintln(" type: {}", region->type() == BarType::IO ? "IO" : "MEM"); + 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 BarRegion::initialize() + { + if (m_type == BarType::IO) + return {}; + + size_t needed_pages = BAN::Math::div_round_up(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_vaddr + 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_vaddr + 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_vaddr + reg, val); + MMIO::write32(m_vaddr + reg, val); + } + + uint8_t BarRegion::read8(off_t reg) + { + if (m_type == BarType::IO) + return IO::inb(m_vaddr + reg); + return MMIO::read8(m_vaddr + reg); + } + + uint16_t BarRegion::read16(off_t reg) + { + if (m_type == BarType::IO) + return IO::inw(m_vaddr + reg); + return MMIO::read16(m_vaddr + reg); + } + + uint32_t BarRegion::read32(off_t reg) + { + if (m_type == BarType::IO) + return IO::inl(m_vaddr + 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); @@ -142,87 +283,92 @@ namespace Kernel m_subclass = (uint8_t)(type); m_prog_if = read_byte(0x09); m_header_type = read_byte(0x0E); + + enumerate_capabilites(); } - uint32_t PCIDevice::read_dword(uint8_t offset) const + uint32_t PCI::Device::read_dword(uint8_t offset) const { ASSERT((offset & 0x03) == 0); return read_config_dword(m_bus, m_dev, m_func, offset); } - uint16_t PCIDevice::read_word(uint8_t offset) const + uint16_t PCI::Device::read_word(uint8_t offset) const { ASSERT((offset & 0x01) == 0); uint32_t dword = read_config_dword(m_bus, m_dev, m_func, offset & 0xFC); return (uint16_t)(dword >> (8 * (offset & 0x03))); } - uint8_t PCIDevice::read_byte(uint8_t offset) const + uint8_t PCI::Device::read_byte(uint8_t offset) const { uint32_t dword = read_config_dword(m_bus, m_dev, m_func, offset & 0xFC); return (uint8_t)(dword >> (8 * (offset & 0x03))); } - void PCIDevice::write_dword(uint8_t offset, uint32_t value) const + void PCI::Device::write_dword(uint8_t offset, uint32_t value) { ASSERT((offset & 0x03) == 0); write_config_dword(m_bus, m_dev, m_func, offset, value); } - PCIDevice::BarType PCIDevice::read_bar_type(uint8_t bar) const + BAN::ErrorOr> PCI::Device::allocate_bar_region(uint8_t bar_num) { - ASSERT(m_header_type == 0x00); - ASSERT(bar <= 5); - - uint32_t type = read_dword(0x10 + bar * 4) & 0b111; - if (type & 1) - return BarType::IO; - type >>= 1; - if (type == 0x0 || type == 0x2) - return BarType::MEM; - return BarType::INVAL; + return BarRegion::create(*this, bar_num); } - uint64_t PCIDevice::read_bar_address(uint8_t bar) const + void PCI::Device::enumerate_capabilites() { - ASSERT(m_header_type == 0x00); - ASSERT(bar <= 5); + uint16_t status = read_word(0x06); + if (!(status & (1 << 4))) + return; - uint64_t address = read_dword(0x10 + bar * 4); - if (address & 1) - return address & 0xFFFFFFFC; - if ((address & 0b110) == 0b100) - address |= (uint64_t)read_dword(0x10 + bar * 4 + 4) << 32; - return address & 0xFFFFFFFFFFFFFFF0; + uint8_t capabilities = read_byte(0x34) & 0xFC; + while (capabilities) + { + uint16_t next = read_word(capabilities); + dprintln(" cap {2H}", next & 0xFF); + capabilities = (next >> 8) & 0xFC; + } } - void PCIDevice::enable_bus_mastering() const + void PCI::Device::enable_bus_mastering() { write_dword(0x04, read_dword(0x04) | 1u << 2); } - void PCIDevice::disable_bus_mastering() const + void PCI::Device::disable_bus_mastering() { write_dword(0x04, read_dword(0x04) & ~(1u << 2)); } - void PCIDevice::enable_memory_space() const + void PCI::Device::enable_memory_space() { write_dword(0x04, read_dword(0x04) | 1u << 1); } - void PCIDevice::disable_memory_space() const + void PCI::Device::disable_memory_space() { write_dword(0x04, read_dword(0x04) & ~(1u << 1)); } - void PCIDevice::enable_pin_interrupts() const + void PCI::Device::enable_io_space() + { + write_dword(0x04, read_dword(0x04) | 1u << 0); + } + + void PCI::Device::disable_io_space() + { + write_dword(0x04, read_dword(0x04) & ~(1u << 0)); + } + + void PCI::Device::enable_pin_interrupts() { write_dword(0x04, read_dword(0x04) | 1u << 10); } - void PCIDevice::disable_pin_interrupts() const + void PCI::Device::disable_pin_interrupts() { write_dword(0x04, read_dword(0x04) & ~(1u << 10)); } diff --git a/kernel/kernel/Storage/ATAController.cpp b/kernel/kernel/Storage/ATAController.cpp index 2b7d4ed27d..a4a481f346 100644 --- a/kernel/kernel/Storage/ATAController.cpp +++ b/kernel/kernel/Storage/ATAController.cpp @@ -11,7 +11,7 @@ namespace Kernel { - BAN::ErrorOr> ATAController::create(const PCIDevice& device) + BAN::ErrorOr> ATAController::create(const PCI::Device& device) { ATAController* controller = new ATAController(); if (controller == nullptr) @@ -50,7 +50,7 @@ namespace Kernel : m_rdev(makedev(DevFileSystem::get().get_next_dev(), 0)) { } - BAN::ErrorOr ATAController::initialize(const PCIDevice& pci_device) + BAN::ErrorOr ATAController::initialize(const PCI::Device& pci_device) { struct Bus { diff --git a/kernel/kernel/kernel.cpp b/kernel/kernel/kernel.cpp index 8e6c50e13f..da9e39650b 100644 --- a/kernel/kernel/kernel.cpp +++ b/kernel/kernel/kernel.cpp @@ -167,7 +167,7 @@ static void init2(void*) DevFileSystem::get().initialize_device_updater(); - PCI::initialize(); + PCI::PCIManager::initialize(); dprintln("PCI initialized"); VirtualFileSystem::initialize(cmdline.root);