From b8622f2b4bfc8b7d6055db1d05bfa59cdb788aaa Mon Sep 17 00:00:00 2001 From: Bananymous Date: Thu, 19 Sep 2024 22:58:03 +0300 Subject: [PATCH] Kernel: Implement simple RTL8169 driver This allows me to use internet when running banan-os on my hardware! --- kernel/CMakeLists.txt | 1 + .../kernel/Networking/RTL8169/Definitions.h | 151 +++++++++ .../kernel/Networking/RTL8169/RTL8169.h | 75 +++++ kernel/kernel/Networking/NetworkManager.cpp | 6 + kernel/kernel/Networking/RTL8169/RTL8169.cpp | 295 ++++++++++++++++++ 5 files changed, 528 insertions(+) create mode 100644 kernel/include/kernel/Networking/RTL8169/Definitions.h create mode 100644 kernel/include/kernel/Networking/RTL8169/RTL8169.h create mode 100644 kernel/kernel/Networking/RTL8169/RTL8169.cpp diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index d25bdd890b..c3509e79f4 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -65,6 +65,7 @@ set(KERNEL_SOURCES kernel/Networking/NetworkLayer.cpp kernel/Networking/NetworkManager.cpp kernel/Networking/NetworkSocket.cpp + kernel/Networking/RTL8169/RTL8169.cpp kernel/Networking/TCPSocket.cpp kernel/Networking/UDPSocket.cpp kernel/Networking/UNIX/Socket.cpp diff --git a/kernel/include/kernel/Networking/RTL8169/Definitions.h b/kernel/include/kernel/Networking/RTL8169/Definitions.h new file mode 100644 index 0000000000..c54485b8c3 --- /dev/null +++ b/kernel/include/kernel/Networking/RTL8169/Definitions.h @@ -0,0 +1,151 @@ +#pragma once + +#include + +namespace Kernel +{ + + enum RTL8169_IO_REGS : uint16_t + { + RTL8169_IO_IDR0 = 0x00, + RTL8169_IO_IDR1 = 0x01, + RTL8169_IO_IDR2 = 0x02, + RTL8169_IO_IDR3 = 0x03, + RTL8169_IO_IDR4 = 0x04, + RTL8169_IO_IDR5 = 0x05, + + RTL8169_IO_TNPDS = 0x20, + RTL8169_IO_CR = 0x37, + RTL8169_IO_TPPoll = 0x38, + RTL8169_IO_IMR = 0x3C, + RTL8169_IO_ISR = 0x3E, + RTL8169_IO_TCR = 0x40, + RTL8169_IO_RCR = 0x44, + RTL8169_IO_9346CR = 0x50, + RTL8169_IO_PHYSts = 0x6C, + RTL8169_IO_RMS = 0xDA, + RTL8169_IO_RDSAR = 0xE4, + RTL8169_IO_MTPS = 0xEC, + }; + + enum RTL8169_CR : uint8_t + { + RTL8169_CR_TE = 1u << 2, + RTL8169_CR_RE = 1u << 3, + RTL8169_CR_RST = 1u << 4, + }; + + enum RTL8169_TPPoll : uint8_t + { + RTL8169_TPPoll_FSWInt = 1u << 0, + RTL8169_TPPoll_NPQ = 1u << 6, + RTL8169_TPPoll_HPQ = 1u << 7, + }; + + enum RTL8169_IR : uint16_t + { + RTL8169_IR_ROK = 1u << 0, + RTL8169_IR_RER = 1u << 1, + RTL8169_IR_TOK = 1u << 2, + RTL8169_IR_TER = 1u << 3, + RTL8169_IR_RDU = 1u << 4, + RTL8169_IR_LinkChg = 1u << 5, + RTL8169_IR_FVOW = 1u << 6, + RTL8169_IR_TDU = 1u << 7, + RTL8169_IR_SWInt = 1u << 8, + RTL8169_IR_FEmp = 1u << 9, + RTL8169_IR_TimeOut = 1u << 14, + }; + + enum RTL8169_TCR : uint32_t + { + RTL8169_TCR_MXDMA_16 = 0b000u << 8, + RTL8169_TCR_MXDMA_32 = 0b001u << 8, + RTL8169_TCR_MXDMA_64 = 0b010u << 8, + RTL8169_TCR_MXDMA_128 = 0b011u << 8, + RTL8169_TCR_MXDMA_256 = 0b100u << 8, + RTL8169_TCR_MXDMA_512 = 0b101u << 8, + RTL8169_TCR_MXDMA_1024 = 0b110u << 8, + RTL8169_TCR_MXDMA_UNLIMITED = 0b111u << 8, + + RTL8169_TCR_TX_NOCRC = 1u << 16, + + RTL8169_TCR_LOOPBACK = 0b01u << 17, + + RTL8169_TCR_IFG_0 = (0u << 19) | (0b11 << 24), + RTL8169_TCR_IFG_8 = (1u << 19) | (0b01 << 24), + RTL8169_TCR_IFG_16 = (1u << 19) | (0b11 << 24), + RTL8169_TCR_IFG_24 = (0u << 19) | (0b01 << 24), + RTL8169_TCR_IFG_48 = (0u << 19) | (0b10 << 24), + }; + + enum RTL8169_RCR : uint32_t + { + RTL8169_RCR_AAP = 1u << 0, // accept all packets with destination + RTL8169_RCR_APM = 1u << 1, // accept physical matches + RTL8169_RCR_AM = 1u << 2, // accept multicast + RTL8169_RCR_AB = 1u << 3, // accept broadcast + RTL8169_RCR_AR = 1u << 4, // accept runt + RTL8169_RCR_AER = 1u << 5, // accept error + + RTL8169_RCR_MXDMA_64 = 0b010u << 8, + RTL8169_RCR_MXDMA_128 = 0b011u << 8, + RTL8169_RCR_MXDMA_256 = 0b100u << 8, + RTL8169_RCR_MXDMA_512 = 0b101u << 8, + RTL8169_RCR_MXDMA_1024 = 0b110u << 8, + RTL8169_RCR_MXDMA_UNLIMITED = 0b111u << 8, + + RTL8169_RCR_RXFTH_64 = 0b010u << 13, + RTL8169_RCR_RXFTH_128 = 0b011u << 13, + RTL8169_RCR_RXFTH_256 = 0b100u << 13, + RTL8169_RCR_RXFTH_512 = 0b101u << 13, + RTL8169_RCR_RXFTH_1024 = 0b110u << 13, + RTL8169_RCR_RXFTH_NO = 0b111u << 13, + }; + + enum RTL8169_9346CR : uint8_t + { + RTL8169_9346CR_MODE_NORMAL = 0b00 << 6, + RTL8169_9346CR_MODE_CONFIG = 0b11 << 6, + }; + + enum RTL8169_PHYSts : uint8_t + { + RTL8169_PHYSts_FullDup = 1u << 0, + RTL8169_PHYSts_LinkSts = 1u << 1, + RTL8169_PHYSts_10M = 1u << 2, + RTL8169_PHYSts_100M = 1u << 3, + RTL8169_PHYSts_1000MF = 1u << 4, + RTL8169_PHYSts_RxFlow = 1u << 5, + RTL8169_PHYSts_TxFlow = 1u << 6, + }; + + enum RTL8169_RMS : uint16_t + { + // 8192 - 1 + RTL8169_RMS_MAX = 0x1FFF, + }; + + enum RTL8169_MTPS : uint8_t + { + RTL8169_MTPS_MAX = 0x3B, + }; + + struct RTL8169Descriptor + { + uint32_t command; + uint32_t vlan; + uint32_t buffer_low; + uint32_t buffer_high; + }; + + enum RTL8169DescriptorCommand : uint32_t + { + RTL8169_DESC_CMD_LGSEN = 1u << 27, + RTL8169_DESC_CMD_LS = 1u << 28, + RTL8169_DESC_CMD_FS = 1u << 29, + RTL8169_DESC_CMD_EOR = 1u << 30, + RTL8169_DESC_CMD_OWN = 1u << 31, + }; + +} diff --git a/kernel/include/kernel/Networking/RTL8169/RTL8169.h b/kernel/include/kernel/Networking/RTL8169/RTL8169.h new file mode 100644 index 0000000000..def4033d93 --- /dev/null +++ b/kernel/include/kernel/Networking/RTL8169/RTL8169.h @@ -0,0 +1,75 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace Kernel +{ + + class RTL8169 final : public NetworkInterface, public Interruptable + { + public: + static bool probe(PCI::Device&); + static BAN::ErrorOr> create(PCI::Device&); + + virtual BAN::MACAddress get_mac_address() const override { return m_mac_address; } + + virtual bool link_up() override { return m_link_up; } + virtual int link_speed() override; + + virtual size_t payload_mtu() const override { return 7436 - sizeof(EthernetHeader); } + + virtual void handle_irq() override; + + protected: + RTL8169(PCI::Device& pci_device) + : m_pci_device(pci_device) + { } + BAN::ErrorOr initialize(); + + virtual BAN::ErrorOr send_bytes(BAN::MACAddress destination, EtherType protocol, BAN::ConstByteSpan) override; + + virtual bool can_read_impl() const override { return false; } + virtual bool can_write_impl() const override { return false; } + virtual bool has_error_impl() const override { return false; } + + private: + BAN::ErrorOr reset(); + + BAN::ErrorOr initialize_rx(); + BAN::ErrorOr initialize_tx(); + + void enable_link(); + BAN::ErrorOr enable_interrupt(); + + void handle_receive(); + + protected: + PCI::Device& m_pci_device; + BAN::UniqPtr m_io_bar_region; + + private: + static constexpr size_t m_rx_descriptor_count = 256; + static constexpr size_t m_tx_descriptor_count = 256; + + BAN::UniqPtr m_rx_buffer_region; + BAN::UniqPtr m_tx_buffer_region; + BAN::UniqPtr m_rx_descriptor_region; + BAN::UniqPtr m_tx_descriptor_region; + + SpinLock m_lock; + ThreadBlocker m_thread_blocker; + + uint32_t m_rx_current { 0 }; + size_t m_tx_current { 0 }; + + BAN::MACAddress m_mac_address {}; + BAN::Atomic m_link_up { false }; + + friend class BAN::RefPtr; + }; + +} diff --git a/kernel/kernel/Networking/NetworkManager.cpp b/kernel/kernel/Networking/NetworkManager.cpp index 1f5f876fe1..a80636cc5d 100644 --- a/kernel/kernel/Networking/NetworkManager.cpp +++ b/kernel/kernel/Networking/NetworkManager.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -48,6 +49,11 @@ namespace Kernel interface = TRY(E1000E::create(pci_device)); break; } + if (RTL8169::probe(pci_device)) + { + interface = TRY(RTL8169::create(pci_device)); + break; + } // fall through default: dprintln("unsupported network controller (pci {2H}.{2H}.{2H})", pci_device.class_code(), pci_device.subclass(), pci_device.prog_if()); diff --git a/kernel/kernel/Networking/RTL8169/RTL8169.cpp b/kernel/kernel/Networking/RTL8169/RTL8169.cpp new file mode 100644 index 0000000000..0c165481ba --- /dev/null +++ b/kernel/kernel/Networking/RTL8169/RTL8169.cpp @@ -0,0 +1,295 @@ +#include +#include +#include +#include + +namespace Kernel +{ + + bool RTL8169::probe(PCI::Device& pci_device) + { + if (pci_device.vendor_id() != 0x10ec) + return false; + switch (pci_device.device_id()) + { + case 0x8161: + case 0x8168: + case 0x8169: + return true; + default: + return false; + } + } + + BAN::ErrorOr> RTL8169::create(PCI::Device& pci_device) + { + auto rtl8169 = TRY(BAN::RefPtr::create(pci_device)); + TRY(rtl8169->initialize()); + return rtl8169; + } + + BAN::ErrorOr RTL8169::initialize() + { + m_pci_device.enable_bus_mastering(); + + m_io_bar_region = TRY(m_pci_device.allocate_bar_region(0)); + if (m_io_bar_region->type() != PCI::BarType::IO) + { + dwarnln("RTL8169 BAR0 is not IO space"); + return BAN::Error::from_errno(EINVAL); + } + + dprintln("Initializing RTL8169"); + + TRY(reset()); + + dprintln(" reset done"); + + for (size_t i = 0; i < 6; i++) + m_mac_address.address[i] = m_io_bar_region->read8(RTL8169_IO_IDR0 + i); + dprintln(" MAC {}", m_mac_address); + + // unlock config registers + m_io_bar_region->write8(RTL8169_IO_9346CR, RTL8169_9346CR_MODE_CONFIG); + + TRY(initialize_rx()); + TRY(initialize_tx()); + m_io_bar_region->write8(RTL8169_IO_CR, RTL8169_CR_RE | RTL8169_CR_TE); + dprintln(" descriptors initialized"); + + m_link_up = m_io_bar_region->read8(RTL8169_IO_PHYSts) & RTL8169_PHYSts_LinkSts; + if (m_link_up) + dprintln(" link speed {}", link_speed()); + + TRY(enable_interrupt()); + dprintln(" interrupts enabled"); + + // lock config registers + m_io_bar_region->write8(RTL8169_IO_9346CR, RTL8169_9346CR_MODE_NORMAL); + + return {}; + } + + BAN::ErrorOr RTL8169::reset() + { + m_io_bar_region->write8(RTL8169_IO_CR, RTL8169_CR_RST); + + const uint64_t timeout_ms = SystemTimer::get().ms_since_boot() + 100; + while (m_io_bar_region->read8(RTL8169_IO_CR) & RTL8169_CR_RST) + if (SystemTimer::get().ms_since_boot() >= timeout_ms) + return BAN::Error::from_errno(ETIMEDOUT); + + return {}; + } + + BAN::ErrorOr RTL8169::initialize_rx() + { + // each buffer is 7440 bytes + padding = 8192 + constexpr size_t buffer_size = 2 * PAGE_SIZE; + + m_rx_buffer_region = TRY(DMARegion::create(m_rx_descriptor_count * buffer_size)); + m_rx_descriptor_region = TRY(DMARegion::create(m_rx_descriptor_count * sizeof(RTL8169Descriptor))); + + for (size_t i = 0; i < m_rx_descriptor_count; i++) + { + const paddr_t rx_buffer_paddr = m_rx_buffer_region->paddr() + i * buffer_size; + + uint32_t command = 0x1FF8 | RTL8169_DESC_CMD_OWN; + if (i == m_rx_descriptor_count - 1) + command |= RTL8169_DESC_CMD_EOR; + + auto& rx_descriptor = reinterpret_cast(m_rx_descriptor_region->vaddr())[i]; + rx_descriptor.command = command; + rx_descriptor.vlan = 0; + rx_descriptor.buffer_low = rx_buffer_paddr & 0xFFFFFFFF; + rx_descriptor.buffer_high = rx_buffer_paddr >> 32; + } + + // configure rx descriptor addresses + m_io_bar_region->write32(RTL8169_IO_RDSAR + 0, m_rx_descriptor_region->paddr() & 0xFFFFFFFF); + m_io_bar_region->write32(RTL8169_IO_RDSAR + 4, m_rx_descriptor_region->paddr() >> 32); + + // configure receibe control (no fifo threshold, max dma burst 1024, accept physical match, broadcast, multicast) + m_io_bar_region->write32(RTL8169_IO_RCR, + RTL8169_RCR_RXFTH_NO | RTL8169_RCR_MXDMA_1024 | RTL8169_RCR_AB | RTL8169_RCR_AM | RTL8169_RCR_APM + ); + + m_io_bar_region->write32(0x44, 0b111111 | (0b111 << 8) | (0b111 << 13)); + + // configure max rx packet size + m_io_bar_region->write16(RTL8169_IO_RMS, RTL8169_RMS_MAX); + + + return {}; + } + + BAN::ErrorOr RTL8169::initialize_tx() + { + // each buffer is 7440 bytes + padding = 8192 + constexpr size_t buffer_size = 2 * PAGE_SIZE; + + m_tx_buffer_region = TRY(DMARegion::create(m_tx_descriptor_count * buffer_size)); + m_tx_descriptor_region = TRY(DMARegion::create(m_tx_descriptor_count * sizeof(RTL8169Descriptor))); + + for (size_t i = 0; i < m_tx_descriptor_count; i++) + { + const paddr_t tx_buffer_paddr = m_tx_buffer_region->paddr() + i * buffer_size; + + uint32_t command = 0; + if (i == m_tx_descriptor_count - 1) + command |= RTL8169_DESC_CMD_EOR; + + auto& tx_descriptor = reinterpret_cast(m_tx_descriptor_region->vaddr())[i]; + tx_descriptor.command = command; + tx_descriptor.vlan = 0; + tx_descriptor.buffer_low = tx_buffer_paddr & 0xFFFFFFFF; + tx_descriptor.buffer_high = tx_buffer_paddr >> 32; + } + + // configure tx descriptor addresses + m_io_bar_region->write32(RTL8169_IO_TNPDS + 0, m_tx_descriptor_region->paddr() & 0xFFFFFFFF); + m_io_bar_region->write32(RTL8169_IO_TNPDS + 4, m_tx_descriptor_region->paddr() >> 32); + + // configure transmit control (standard ifg, max dma burst 1024) + m_io_bar_region->write32(RTL8169_IO_TCR, RTL8169_TCR_IFG_0 | RTL8169_TCR_MXDMA_1024); + + // configure max tx packet size + m_io_bar_region->write8(RTL8169_IO_MTPS, RTL8169_MTPS_MAX); + + return {}; + } + + BAN::ErrorOr RTL8169::enable_interrupt() + { + TRY(m_pci_device.reserve_irqs(1)); + set_irq(m_pci_device.get_irq(0)); + Interruptable::enable_interrupt(); + + m_io_bar_region->write16(RTL8169_IO_IMR, + RTL8169_IR_ROK + | RTL8169_IR_RER + | RTL8169_IR_TOK + | RTL8169_IR_TER + | RTL8169_IR_RDU + | RTL8169_IR_LinkChg + | RTL8169_IR_FVOW + | RTL8169_IR_TDU + ); + m_io_bar_region->write16(RTL8169_IO_ISR, 0xFFFF); + + return {}; + } + + int RTL8169::link_speed() + { + if (!link_up()) + return 0; + const uint8_t phy_status = m_io_bar_region->read8(RTL8169_IO_PHYSts); + if (phy_status & RTL8169_PHYSts_1000MF) + return 1000; + if (phy_status & RTL8169_PHYSts_100M) + return 100; + if (phy_status & RTL8169_PHYSts_10M) + return 10; + return 0; + } + + BAN::ErrorOr RTL8169::send_bytes(BAN::MACAddress destination, EtherType protocol, BAN::ConstByteSpan buffer) + { + constexpr size_t buffer_size = 8192; + + const uint16_t packet_size = sizeof(EthernetHeader) + buffer.size(); + if (packet_size > buffer_size) + return BAN::Error::from_errno(EINVAL); + + if (!link_up()) + return BAN::Error::from_errno(EADDRNOTAVAIL); + + auto state = m_lock.lock(); + const uint32_t tx_current = m_tx_current; + m_tx_current = (m_tx_current + 1) % m_tx_descriptor_count; + m_lock.unlock(state); + + auto& descriptor = reinterpret_cast(m_tx_descriptor_region->vaddr())[tx_current]; + while (descriptor.command & RTL8169_DESC_CMD_OWN) + m_thread_blocker.block_with_timeout_ms(100); + + auto* tx_buffer = reinterpret_cast(m_tx_buffer_region->vaddr() + tx_current * buffer_size); + + // write packet + auto& ethernet_header = *reinterpret_cast(tx_buffer); + ethernet_header.dst_mac = destination; + ethernet_header.src_mac = get_mac_address(); + ethernet_header.ether_type = protocol; + memcpy(tx_buffer + sizeof(EthernetHeader), buffer.data(), buffer.size()); + + // give packet ownership to NIC + uint32_t command = packet_size | RTL8169_DESC_CMD_OWN | RTL8169_DESC_CMD_LS | RTL8169_DESC_CMD_FS; + if (tx_current >= m_tx_descriptor_count - 1) + command |= RTL8169_DESC_CMD_EOR; + descriptor.command = command; + + // notify NIC about new packet + m_io_bar_region->write8(RTL8169_IO_TPPoll, RTL8169_TPPoll_NPQ); + + return {}; + } + + void RTL8169::handle_irq() + { + const uint16_t interrupt_status = m_io_bar_region->read16(RTL8169_IO_ISR); + m_io_bar_region->write16(RTL8169_IO_ISR, interrupt_status); + + if (interrupt_status & RTL8169_IR_LinkChg) + { + m_link_up = m_io_bar_region->read8(RTL8169_IO_PHYSts) & RTL8169_PHYSts_LinkSts; + dprintln("link status -> {}", m_link_up.load()); + } + + if (interrupt_status & RTL8169_IR_TOK) + m_thread_blocker.unblock(); + + if (interrupt_status & RTL8169_IR_RER) + dwarnln("Rx error"); + if (interrupt_status & RTL8169_IR_TER) + dwarnln("Tx error"); + if (interrupt_status & RTL8169_IR_RDU) + dwarnln("Rx descriptor not available"); + if (interrupt_status & RTL8169_IR_FVOW) + dwarnln("Rx FIFO overflow"); + // dont log TDU is sent after each sent packet + + if (!(interrupt_status & RTL8169_IR_ROK)) + return; + + constexpr size_t buffer_size = 8192; + + for (;;) + { + auto& descriptor = reinterpret_cast(m_rx_descriptor_region->vaddr())[m_rx_current]; + if (descriptor.command & RTL8169_DESC_CMD_OWN) + break; + + // packet buffer can only hold single packet, so we should not receive any multi-descriptor packets + ASSERT((descriptor.command & RTL8169_DESC_CMD_LS) && (descriptor.command & RTL8169_DESC_CMD_FS)); + + const uint16_t packet_length = descriptor.command & 0x3FFF; + if (packet_length > buffer_size) + dwarnln("Got {} bytes to {} byte buffer", packet_length, buffer_size); + else if (descriptor.command & (1u << 21)) + ; // descriptor has an error + else + { + NetworkManager::get().on_receive(*this, BAN::ConstByteSpan { + reinterpret_cast(m_rx_buffer_region->vaddr() + m_rx_current * buffer_size), + packet_length + }); + } + + m_rx_current = (m_rx_current + 1) % m_rx_descriptor_count; + + descriptor.command = descriptor.command | RTL8169_DESC_CMD_OWN; + } + } + +}