From a898e01d4dc93e281196971000b110d264351919 Mon Sep 17 00:00:00 2001 From: Idan Horowitz Date: Fri, 2 Apr 2021 17:53:31 +0300 Subject: [PATCH] Kernel: Add driver for RTL8168 & RTL8111 NICs These are pretty common on older LGA1366 & LGA1150 motherboards. NOTE: Since the registers datasheets for all versions of the chip besides versions 1 - 3 are still under NDAs i had to collect several "magical vendor constants" from the *BSD driver and the linux driver that i was not able to name verbosely, and as such these are labeled with the comment "vendor magic values". --- Documentation/HardwareCompatibility.md | 1 + Kernel/CMakeLists.txt | 1 + Kernel/Debug.h.in | 4 + Kernel/Net/NetworkingManagement.cpp | 3 + Kernel/Net/RTL8168NetworkAdapter.cpp | 1473 ++++++++++++++++++++++++ Kernel/Net/RTL8168NetworkAdapter.h | 198 ++++ Kernel/PCI/IDs.h | 3 +- Meta/CMake/all_the_debug_macros.cmake | 1 + 8 files changed, 1683 insertions(+), 1 deletion(-) create mode 100644 Kernel/Net/RTL8168NetworkAdapter.cpp create mode 100644 Kernel/Net/RTL8168NetworkAdapter.h diff --git a/Documentation/HardwareCompatibility.md b/Documentation/HardwareCompatibility.md index f97ec724b5..caf9fcbd6c 100644 --- a/Documentation/HardwareCompatibility.md +++ b/Documentation/HardwareCompatibility.md @@ -12,6 +12,7 @@ Serenity boots to a graphical desktop on all machines unless otherwise noted. | Intel 82545XX | Also known as e1000 | | NE2000 compatible cards | | | Intel 82574L | Also known as e1000e | +| RTL8168/8111 (Variants B & E-VL) | Other variants are WIP | ### Desktop machines diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt index e1cd0e4dac..cf8e73f4be 100644 --- a/Kernel/CMakeLists.txt +++ b/Kernel/CMakeLists.txt @@ -125,6 +125,7 @@ set(KERNEL_SOURCES Net/NetworkingManagement.cpp Net/NetworkTask.cpp Net/RTL8139NetworkAdapter.cpp + Net/RTL8168NetworkAdapter.cpp Net/Routing.cpp Net/Socket.cpp Net/TCPSocket.cpp diff --git a/Kernel/Debug.h.in b/Kernel/Debug.h.in index e30f1c331c..23b57a1f7c 100644 --- a/Kernel/Debug.h.in +++ b/Kernel/Debug.h.in @@ -238,6 +238,10 @@ #cmakedefine01 RTL8139_DEBUG #endif +#ifndef RTL8168_DEBUG +#cmakedefine01 RTL8168_DEBUG +#endif + #ifndef SB16_DEBUG #cmakedefine01 SB16_DEBUG #endif diff --git a/Kernel/Net/NetworkingManagement.cpp b/Kernel/Net/NetworkingManagement.cpp index dfd4792469..36e08853e1 100644 --- a/Kernel/Net/NetworkingManagement.cpp +++ b/Kernel/Net/NetworkingManagement.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -81,6 +82,8 @@ UNMAP_AFTER_INIT RefPtr NetworkingManagement::determine_network_ return candidate; if (auto candidate = RTL8139NetworkAdapter::try_to_initialize(address); !candidate.is_null()) return candidate; + if (auto candidate = RTL8168NetworkAdapter::try_to_initialize(address); !candidate.is_null()) + return candidate; if (auto candidate = NE2000NetworkAdapter::try_to_initialize(address); !candidate.is_null()) return candidate; return {}; diff --git a/Kernel/Net/RTL8168NetworkAdapter.cpp b/Kernel/Net/RTL8168NetworkAdapter.cpp new file mode 100644 index 0000000000..ee1b13e9fe --- /dev/null +++ b/Kernel/Net/RTL8168NetworkAdapter.cpp @@ -0,0 +1,1473 @@ +/* + * Copyright (c) 2021, Idan Horowitz + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include + +namespace Kernel { + +#define REG_MAC 0x00 +#define REG_MAR4 0x0B +#define REG_MAR0 0x0F +#define REG_EEE_LED 0x1B +#define REG_TXADDR 0x20 +#define REG_COMMAND 0x37 +#define REG_TXSTART 0x38 +#define REG_IMR 0x3C +#define REG_ISR 0x3E +#define REG_TXCFG 0x40 +#define REG_RXCFG 0x44 +#define REG_MPC 0x4C +#define REG_CFG9346 0x50 +#define REG_CONFIG1 0x52 +#define REG_CONFIG2 0x53 +#define REG_CONFIG3 0x54 +#define REG_CONFIG4 0x55 +#define REG_CONFIG5 0x56 +#define REG_MULTIINTR 0x5C +#define REG_PHYACCESS 0x60 +#define REG_CSI_DATA 0x64 +#define REG_CSI_ADDR 0x68 +#define REG_PHYSTATUS 0x6C +#define REG_PMCH 0x6F +#define REG_ERI_DATA 0x70 +#define REG_ERI_ADDR 0x74 +#define REG_EPHYACCESS 0x80 +#define REG_OCP_DATA 0xB0 +#define REG_OCP_ADDR 0xB4 +#define REG_GPHY_OCP 0xB8 +#define REG_DLLPR 0xD0 +#define REG_MCU 0xD3 +#define REG_RMS 0xDA +#define REG_CPLUS_COMMAND 0xE0 +#define REG_INT_MOD 0xE2 +#define REG_RXADDR 0xE4 +#define REG_MTPS 0xEC +#define REG_MISC 0xF0 +#define REG_MISC2 0xF2 +#define REG_IBCR0 0xF8 +#define REG_IBCR2 0xF9 +#define REG_IBISR0 0xFB + +#define COMMAND_TX_ENABLE 0x4 +#define COMMAND_RX_ENABLE 0x8 +#define COMMAND_RESET 0x10 + +#define CPLUS_COMMAND_VERIFY_CHECKSUM 0x20 +#define CPLUS_COMMAND_VLAN_STRIP 0x40 +#define CPLUS_COMMAND_MAC_DBGO_SEL 0x1C +#define CPLUS_COMMAND_PACKET_CONTROL_DISABLE 0x80 +#define CPLUS_COMMAND_ASF 0x100 +#define CPLUS_COMMAND_CXPL_DBG_SEL 0x200 +#define CPLUS_COMMAND_FORCE_TXFLOW_ENABLE 0x400 +#define CPLUS_COMMAND_FORCE_RXFLOW_ENABLE 0x800 +#define CPLUS_COMMAND_FORCE_HALF_DUP 0x1000 +#define CPLUS_COMMAND_MAC_DBGO_OE 0x4000 +#define CPLUS_COMMAND_ENABLE_BIST 0x8000 + +#define INT_RXOK 0x1 +#define INT_RXERR 0x2 +#define INT_TXOK 0x4 +#define INT_TXERR 0x8 +#define INT_RX_OVERFLOW 0x10 +#define INT_LINK_CHANGE 0x20 +#define INT_RX_FIFO_OVERFLOW 0x40 +#define INT_SYS_ERR 0x8000 + +#define CFG9346_NONE 0x00 +#define CFG9346_EEM0 0x40 +#define CFG9346_EEM1 0x80 +#define CFG9346_UNLOCK (CFG9346_EEM0 | CFG9346_EEM1) + +#define TXCFG_AUTO_FIFO 0x80 +#define TXCFG_MAX_DMA_UNLIMITED 0x700 +#define TXCFG_EMPTY 0x800 +#define TXCFG_IFG011 0x3000000 + +#define RXCFG_READ_MASK 0x3F + +#define RXCFG_APM 0x2 +#define RXCFG_AM 0x4 +#define RXCFG_AB 0x8 +#define RXCFG_MAX_DMA_UNLIMITED 0x700 +#define RXCFG_EARLY_OFF_V2 0x800 +#define RXCFG_FTH_NONE 0xE000 +#define RXCFG_MULTI_ENABLE 0x4000 +#define RXCFG_128INT_ENABLE 0x8000 + +#define CFG2_CLOCK_REQUEST_ENABLE 0x80 + +#define CFG3_BEACON_ENABLE 0x1 +#define CFG3_READY_TO_L23 0x2 + +#define CFG5_ASPM_ENABLE 0x1 + +#define PHY_LINK_STATUS 0x2 + +#define PHY_FLAG 0x80000000 +#define PHY_REG_BMCR 0x00 +#define PHY_REG_ANAR 0x4 +#define PHY_REG_GBCR 0x9 + +#define CSI_FLAG 0x80000000 +#define CSI_BYTE_ENABLE 0xF000 +#define CSI_FUNC_NIC 0x20000 +#define CSI_FUNC_NIC2 0x10000 + +#define CSI_ACCESS_1 0x17000000 +#define CSI_ACCESS_2 0x27000000 + +#define EPHY_FLAG 0x80000000 + +#define ERI_FLAG 0x80000000 +#define ERI_MASK_0001 0x1000 +#define ERI_MASK_0011 0x3000 +#define ERI_MASK_0100 0x4000 +#define ERI_MASK_0101 0x5000 +#define ERI_MASK_1111 0xF000 + +#define ERI_EXGMAC 0x0 + +#define OCP_FLAG 0x80000000 +#define OCP_STANDARD_PHY_BASE 0xa400 + +#define TXSTART_START 0x40 + +#define BMCR_RESET 0x8000 +#define BMCR_SPEED_0 0x2000 +#define BMCR_AUTO_NEGOTIATE 0x1000 +#define BMCR_RESTART_AUTO_NEGOTIATE 0x200 +#define BMCR_DUPLEX 0x100 +#define BMCR_SPEED_1 0x40 + +#define ADVERTISE_10_HALF 0x20 +#define ADVERTISE_10_FULL 0x40 +#define ADVERTISE_100_HALF 0x80 +#define ADVERTISE_100_FULL 0x100 +#define ADVERTISE_PAUSE_CAP 0x400 +#define ADVERTISE_PAUSE_ASYM 0x800 + +#define ADVERTISE_1000_HALF 0x100 +#define ADVERTISE_1000_FULL 0x200 + +#define DLLPR_PFM_ENABLE 0x40 +#define DLLPR_TX_10M_PS_ENABLE 0x80 + +#define MCU_LINK_LIST_READY 0x2 +#define MCU_RX_EMPTY 0x10 +#define MCU_TX_EMPTY 0x20 +#define MCU_NOW_IS_OOB 0x80 + +#define MTPS_JUMBO 0x3F + +#define MISC_RXDV_GATE_ENABLE 0x80000 + +#define MISC2_PFM_D3COLD_ENABLE 0x40 + +#define TX_BUFFER_SIZE 0x1FF8 +#define RX_BUFFER_SIZE 0x1FF8 // FIXME: this should be increased (0x3FFF) + +UNMAP_AFTER_INIT RefPtr RTL8168NetworkAdapter::try_to_initialize(PCI::Address address) +{ + auto id = PCI::get_id(address); + if (id.vendor_id != (u16)PCIVendorID::Realtek) + return {}; + if (id.device_id != 0x8168) + return {}; + u8 irq = PCI::get_interrupt_line(address); + return adopt_ref_if_nonnull(new RTL8168NetworkAdapter(address, irq)); +} + +UNMAP_AFTER_INIT RTL8168NetworkAdapter::RTL8168NetworkAdapter(PCI::Address address, u8 irq) + : PCI::Device(address, irq) + , m_io_base(PCI::get_BAR0(pci_address()) & ~1) + , m_rx_descriptors_region(MM.allocate_contiguous_kernel_region(page_round_up(sizeof(TXDescriptor) * (number_of_rx_descriptors + 1)), "RTL8168 RX", Region::Access::Read | Region::Access::Write)) + , m_tx_descriptors_region(MM.allocate_contiguous_kernel_region(page_round_up(sizeof(RXDescriptor) * (number_of_tx_descriptors + 1)), "RTL8168 TX", Region::Access::Read | Region::Access::Write)) +{ + set_interface_name(address); + + dmesgln("RTL8168: Found @ {}", pci_address()); + dmesgln("RTL8168: I/O port base: {}", m_io_base); + + identify_chip_version(); + dmesgln("RTL8168: Version detected - {} ({}{})", possible_device_name(), (u8)m_version, m_version_uncertain ? "?" : ""); + if (m_version == ChipVersion::Unknown || m_version >= ChipVersion::Version4) { + dmesgln("RTL8168: Aborting initialization! Support for your chip version ({}) is not implemented yet, please open a GH issue and include this message.", (u8)m_version); + return; // Each ChipVersion requires a specific implementation of configure_phy and hardware_quirks + } + + initialize(); + startup(); +} + +void RTL8168NetworkAdapter::initialize() +{ + // set initial REG_RXCFG + auto rx_config = RXCFG_MAX_DMA_UNLIMITED; + if (m_version <= ChipVersion::Version3) { + rx_config |= RXCFG_FTH_NONE; + } else if (m_version <= ChipVersion::Version8 || (m_version >= ChipVersion::Version16 && m_version <= ChipVersion::Version19)) { + rx_config |= RXCFG_128INT_ENABLE | RXCFG_MULTI_ENABLE; + } else if (m_version >= ChipVersion::Version21) { + rx_config |= RXCFG_128INT_ENABLE | RXCFG_MULTI_ENABLE | RXCFG_EARLY_OFF_V2; + } else { + rx_config |= RXCFG_128INT_ENABLE; + } + out32(REG_RXCFG, rx_config); + + // disable interrupts + out16(REG_IMR, 0); + + // initialize hardware + if (m_version == ChipVersion::Version23 || m_version == ChipVersion::Version27 || m_version == ChipVersion::Version28) { + // disable CMAC + out8(REG_IBCR2, in8(REG_IBCR2) & ~1); + + while ((in32(REG_IBISR0) & 0x2) != 0) + ; + + out8(REG_IBISR0, in8(REG_IBISR0) | 0x20); + out8(REG_IBCR0, in8(REG_IBCR0) & ~1); + } + if (m_version >= ChipVersion::Version21) { + m_ocp_base_address = OCP_STANDARD_PHY_BASE; + + // enable RXDV gate + out32(REG_MISC, in32(REG_MISC) | MISC_RXDV_GATE_ENABLE); + + while ((in32(REG_TXCFG) & TXCFG_EMPTY) == 0) + ; + + while ((in32(REG_MCU) & (MCU_RX_EMPTY | MCU_TX_EMPTY)) == 0) + ; + + out8(REG_COMMAND, in8(REG_COMMAND) & ~(COMMAND_RX_ENABLE | COMMAND_TX_ENABLE)); + out8(REG_MCU, in8(REG_MCU) & ~MCU_NOW_IS_OOB); + + // vendor magic values ??? + auto data = ocp_in(0xe8de); + data &= ~(1 << 14); + ocp_out(0xe8de, data); + + while ((in32(REG_MCU) & MCU_LINK_LIST_READY) == 0) + ; + + // vendor magic values ??? + data = ocp_in(0xe8de); + data |= (1 << 15); + ocp_out(0xe8de, data); + + while ((in32(REG_MCU) & MCU_LINK_LIST_READY) == 0) + ; + } + + // software reset + reset(); + + // clear interrupts + out16(REG_ISR, 0xffff); + + enable_bus_mastering(pci_address()); + + read_mac_address(); + dmesgln("RTL8168: MAC address: {}", mac_address().to_string()); + + // notify about driver start + if (m_version >= ChipVersion::Version11 && m_version <= ChipVersion::Version13) { + // if check_dash + // notify + TODO(); + } else if (m_version == ChipVersion::Version23 || m_version == ChipVersion::Version27 || m_version == ChipVersion::Version28) { + // if check_dash + // notify + TODO(); + } +} + +void RTL8168NetworkAdapter::startup() +{ + // initialize descriptors + initialize_rx_descriptors(); + initialize_tx_descriptors(); + + // register irq + enable_irq(); + + // version specific phy configuration + configure_phy(); + + // software reset phy + phy_out(PHY_REG_BMCR, phy_in(PHY_REG_BMCR) | BMCR_RESET); + while ((phy_in(PHY_REG_BMCR) & BMCR_RESET) != 0) + ; + + set_phy_speed(); + + // set C+ command + auto cplus_command = in16(REG_CPLUS_COMMAND) | CPLUS_COMMAND_VERIFY_CHECKSUM | CPLUS_COMMAND_VLAN_STRIP; + out16(REG_CPLUS_COMMAND, cplus_command); + in16(REG_CPLUS_COMMAND); // C+ Command barrier + + // power up phy + if (m_version >= ChipVersion::Version9 && m_version <= ChipVersion::Version15) { + out8(REG_PMCH, in8(REG_PMCH) | 0x80); + } else if (m_version >= ChipVersion::Version26) { + out8(REG_PMCH, in8(REG_PMCH) | 0xC0); + } else if (m_version >= ChipVersion::Version21 && m_version <= ChipVersion::Version23) { + out8(REG_PMCH, in8(REG_PMCH) | 0xC0); + // vendor magic values ??? + eri_update(0x1a8, ERI_MASK_1111, 0xfc000000, 0, ERI_EXGMAC); + } + + // wakeup phy (more vendor magic values) + phy_out(0x1F, 0); + if (m_version <= ChipVersion::Version13) { + phy_out(0x0E, 0); + } + phy_out(PHY_REG_BMCR, BMCR_AUTO_NEGOTIATE); // send known good phy write (acts as a phy barrier) + start_hardware(); + + // re-enable interrupts + auto enabled_interrupts = INT_RXOK | INT_RXERR | INT_TXOK | INT_TXERR | INT_RX_OVERFLOW | INT_LINK_CHANGE | INT_SYS_ERR; + if (m_version == ChipVersion::Version1) { + enabled_interrupts |= INT_RX_FIFO_OVERFLOW; + enabled_interrupts &= ~INT_RX_OVERFLOW; + } + out16(REG_IMR, enabled_interrupts); + + // update link status + m_link_up = (in8(REG_PHYSTATUS) & PHY_LINK_STATUS) != 0; +} + +void RTL8168NetworkAdapter::configure_phy() +{ + // this method sets a bunch of magic vendor values to the phy configuration registers based on the hardware version + switch (m_version) { + case ChipVersion::Version1: { + configure_phy_b_1(); + return; + } + case ChipVersion::Version2: + case ChipVersion::Version3: { + configure_phy_b_2(); + return; + } + case ChipVersion::Version4: + TODO(); + case ChipVersion::Version5: + TODO(); + case ChipVersion::Version6: + TODO(); + case ChipVersion::Version7: + TODO(); + case ChipVersion::Version8: + TODO(); + case ChipVersion::Version9: + TODO(); + case ChipVersion::Version10: + TODO(); + case ChipVersion::Version11: + TODO(); + case ChipVersion::Version12: + TODO(); + case ChipVersion::Version13: + TODO(); + case ChipVersion::Version14: + TODO(); + case ChipVersion::Version15: + TODO(); + case ChipVersion::Version16: + TODO(); + case ChipVersion::Version17: + TODO(); + case ChipVersion::Version18: + TODO(); + case ChipVersion::Version19: + TODO(); + case ChipVersion::Version20: + TODO(); + case ChipVersion::Version21: + TODO(); + case ChipVersion::Version22: + TODO(); + case ChipVersion::Version23: + TODO(); + case ChipVersion::Version24: + TODO(); + case ChipVersion::Version25: + TODO(); + case ChipVersion::Version26: + TODO(); + case ChipVersion::Version27: + TODO(); + case ChipVersion::Version28: + TODO(); + case ChipVersion::Version29: { + configure_phy_h_1(); + return; + } + case ChipVersion::Version30: { + configure_phy_h_2(); + return; + } + default: + VERIFY_NOT_REACHED(); + } +} + +void RTL8168NetworkAdapter::configure_phy_b_1() +{ + constexpr PhyRegister phy_registers[] = { + { 0x10, 0xf41b }, + { 0x1f, 0 } + }; + + phy_out(0x1f, 0x1); + phy_out(0x16, 1 << 0); + + phy_out_batch(phy_registers, 2); +} + +void RTL8168NetworkAdapter::configure_phy_b_2() +{ + constexpr PhyRegister phy_registers[] = { + { 0x1f, 0x1 }, + { 0x10, 0xf41b }, + { 0x1f, 0 } + }; + + phy_out_batch(phy_registers, 3); +} + +void RTL8168NetworkAdapter::configure_phy_h_1() +{ + // FIXME: linux's driver writes a firmware blob to the device at this point, is this needed? + + // CHN EST parameters adjust - giga master + phy_out(0x1f, 0x0a43); + phy_out(0x13, 0x809b); + phy_update(0x14, 0x8000, 0xf800); + phy_out(0x13, 0x80a2); + phy_update(0x14, 0x8000, 0xff00); + phy_out(0x13, 0x80a4); + phy_update(0x14, 0x8500, 0xff00); + phy_out(0x13, 0x809c); + phy_update(0x14, 0xbd00, 0xff00); + phy_out(0x1f, 0); + + // CHN EST parameters adjust - giga slave + phy_out(0x1f, 0x0a43); + phy_out(0x13, 0x80ad); + phy_update(0x14, 0x7000, 0xf800); + phy_out(0x13, 0x80b4); + phy_update(0x14, 0x5000, 0xff00); + phy_out(0x13, 0x80ac); + phy_update(0x14, 0x4000, 0xff00); + phy_out(0x1f, 0); + + // CHN EST parameters adjust - fnet + phy_out(0x1f, 0x0a43); + phy_out(0x13, 0x808e); + phy_update(0x14, 0x1200, 0xff00); + phy_out(0x13, 0x8090); + phy_update(0x14, 0xe500, 0xff00); + phy_out(0x13, 0x8092); + phy_update(0x14, 0x9f00, 0xff00); + phy_out(0x1f, 0); + + // enable R-tune & PGA-retune function + u16 dout_tapbin = 0; + phy_out(0x1f, 0x0a46); + auto data = phy_in(0x13); + data &= 3; + data <<= 2; + dout_tapbin |= data; + data = phy_in(0x12); + data &= 0xc000; + data >>= 14; + dout_tapbin |= data; + dout_tapbin = ~(dout_tapbin ^ 0x8); + dout_tapbin <<= 12; + dout_tapbin &= 0xf000; + phy_out(0x1f, 0x0a43); + phy_out(0x13, 0x827a); + phy_update(0x14, dout_tapbin, 0xf000); + phy_out(0x13, 0x827b); + phy_update(0x14, dout_tapbin, 0xf000); + phy_out(0x13, 0x827c); + phy_update(0x14, dout_tapbin, 0xf000); + phy_out(0x13, 0x827d); + phy_update(0x14, dout_tapbin, 0xf000); + + phy_out(0x1f, 0x0a43); + phy_out(0x13, 0x811); + phy_update(0x14, 0x800, 0); + phy_out(0x1f, 0x0a42); + phy_update(0x16, 0x2, 0); + phy_out(0x1f, 0); + + // enable GPHY 10M + phy_out(0x1f, 0x0a44); + phy_update(0x11, 0x800, 0); + phy_out(0x1f, 0); + + // SAR ADC performance + phy_out(0x1f, 0x0bca); + phy_update(0x17, 0x4000, 0x3000); + phy_out(0x1f, 0); + + phy_out(0x1f, 0x0a43); + phy_out(0x13, 0x803f); + phy_update(0x14, 0, 0x3000); + phy_out(0x13, 0x8047); + phy_update(0x14, 0, 0x3000); + phy_out(0x13, 0x804f); + phy_update(0x14, 0, 0x3000); + phy_out(0x13, 0x8057); + phy_update(0x14, 0, 0x3000); + phy_out(0x13, 0x805f); + phy_update(0x14, 0, 0x3000); + phy_out(0x13, 0x8067); + phy_update(0x14, 0, 0x3000); + phy_out(0x13, 0x806f); + phy_update(0x14, 0, 0x3000); + phy_out(0x1f, 0); + + // disable phy pfm mode + phy_out(0x1f, 0x0a44); + phy_update(0x11, 0, 0x80); + phy_out(0x1f, 0); + + // Check ALDPS bit, disable it if enabled + phy_out(0x1f, 0x0a43); + if (phy_in(0x10) & 0x4) + phy_update(0x10, 0, 0x4); + + phy_out(0x1f, 0); +} + +void RTL8168NetworkAdapter::configure_phy_h_2() +{ + // FIXME: linux's driver writes a firmware blob to the device at this point, is this needed? + + // CHIN EST parameter update + phy_out(0x1f, 0x0a43); + phy_out(0x13, 0x808a); + phy_update(0x14, 0x000a, 0x3f); + phy_out(0x1f, 0); + + // enable R-tune & PGA-retune function + phy_out(0x1f, 0x0a43); + phy_out(0x13, 0x811); + phy_update(0x14, 0x800, 0); + phy_out(0x1f, 0x0a42); + phy_update(0x16, 0x2, 0); + phy_out(0x1f, 0); + + // enable GPHY 10M + phy_out(0x1f, 0x0a44); + phy_update(0x11, 0x800, 0); + phy_out(0x1f, 0); + + ocp_out(0xdd02, 0x807d); + auto data = ocp_in(0xdd02); + u16 ioffset_p3 = ((data & 0x80) >> 7); + ioffset_p3 <<= 3; + + data = ocp_in(0xdd00); + ioffset_p3 |= ((data & (0xe000)) >> 13); + u16 ioffset_p2 = ((data & (0x1e00)) >> 9); + u16 ioffset_p1 = ((data & (0x1e0)) >> 5); + u16 ioffset_p0 = ((data & 0x10) >> 4); + ioffset_p0 <<= 3; + ioffset_p0 |= (data & (0x7)); + data = (ioffset_p3 << 12) | (ioffset_p2 << 8) | (ioffset_p1 << 4) | (ioffset_p0); + + if ((ioffset_p3 != 0x0f) || (ioffset_p2 != 0x0f) || (ioffset_p1 != 0x0f) || (ioffset_p0 != 0x0f)) { + phy_out(0x1f, 0x0bcf); + phy_out(0x16, data); + phy_out(0x1f, 0); + } + + // Modify rlen (TX LPF corner frequency) level + phy_out(0x1f, 0x0bcd); + data = phy_in(0x16); + data &= 0x000f; + u16 rlen = 0; + if (data > 3) + rlen = data - 3; + data = rlen | (rlen << 4) | (rlen << 8) | (rlen << 12); + phy_out(0x17, data); + phy_out(0x1f, 0x0bcd); + phy_out(0x1f, 0); + + // disable phy pfm mode + phy_out(0x1f, 0x0a44); + phy_update(0x11, 0, 0x80); + phy_out(0x1f, 0); + + // Check ALDPS bit, disable it if enabled + phy_out(0x1f, 0x0a43); + if (phy_in(0x10) & 0x4) + phy_update(0x10, 0, 0x4); + + phy_out(0x1f, 0); +} + +void RTL8168NetworkAdapter::start_hardware() +{ + + // unlock config registers + out8(REG_CFG9346, CFG9346_UNLOCK); + + // configure the maximum transmit packet size + out16(REG_MTPS, MTPS_JUMBO); + + // configure the maximum receive packet size + out16(REG_RMS, RX_BUFFER_SIZE); + + auto cplus_command = in16(REG_CPLUS_COMMAND); + cplus_command |= CPLUS_COMMAND_PACKET_CONTROL_DISABLE; + // undocumented magic value??? + cplus_command |= 0x1; + out16(REG_CPLUS_COMMAND, cplus_command); + + // setup interrupt moderation, magic from vendor (Linux Driver uses 0x5151, *BSD Driver uses 0x5100, RTL Driver use 0x5f51???) + out16(REG_INT_MOD, 0x5151); + + // point to tx descriptors + out64(REG_TXADDR, m_tx_descriptors_region->physical_page(0)->paddr().get()); + + // point to rx descriptors + out64(REG_RXADDR, m_rx_descriptors_region->physical_page(0)->paddr().get()); + + // configure tx: use the maximum dma transfer size, default interframe gap time. + out32(REG_TXCFG, TXCFG_IFG011 | TXCFG_MAX_DMA_UNLIMITED); + + // version specific quirks and tweaks + hardware_quirks(); + + in8(REG_IMR); // known good read (acts as a barrier) + + // relock config registers + out8(REG_CFG9346, CFG9346_NONE); + + // enable rx/tx + out8(REG_COMMAND, COMMAND_RX_ENABLE | COMMAND_TX_ENABLE); + + // turn on all multicast + out32(REG_MAR0, 0xFFFFFFFF); + out32(REG_MAR4, 0xFFFFFFFF); + + // configure rx mode: accept physical (MAC) match, multicast, and broadcast + out32(REG_RXCFG, (in32(REG_RXCFG) & ~RXCFG_READ_MASK) | RXCFG_APM | RXCFG_AM | RXCFG_AB); + + // disable early-rx interrupts + out16(REG_MULTIINTR, in16(REG_MULTIINTR) & 0xF000); +} + +void RTL8168NetworkAdapter::hardware_quirks() +{ + switch (m_version) { + case ChipVersion::Version1: + hardware_quirks_b_1(); + return; + case ChipVersion::Version2: + case ChipVersion::Version3: + hardware_quirks_b_2(); + return; + case ChipVersion::Version4: + TODO(); + case ChipVersion::Version5: + TODO(); + case ChipVersion::Version6: + TODO(); + case ChipVersion::Version7: + TODO(); + case ChipVersion::Version8: + TODO(); + case ChipVersion::Version9: + TODO(); + case ChipVersion::Version10: + TODO(); + case ChipVersion::Version11: + TODO(); + case ChipVersion::Version12: + TODO(); + case ChipVersion::Version13: + TODO(); + case ChipVersion::Version14: + TODO(); + case ChipVersion::Version15: + TODO(); + case ChipVersion::Version16: + TODO(); + case ChipVersion::Version17: + TODO(); + case ChipVersion::Version18: + TODO(); + case ChipVersion::Version19: + TODO(); + case ChipVersion::Version20: + TODO(); + case ChipVersion::Version21: + TODO(); + case ChipVersion::Version22: + TODO(); + case ChipVersion::Version23: + TODO(); + case ChipVersion::Version24: + TODO(); + case ChipVersion::Version25: + TODO(); + case ChipVersion::Version26: + TODO(); + case ChipVersion::Version27: + TODO(); + case ChipVersion::Version28: + TODO(); + case ChipVersion::Version29: + case ChipVersion::Version30: + hardware_quirks_h(); + return; + default: + VERIFY_NOT_REACHED(); + } +} + +void RTL8168NetworkAdapter::hardware_quirks_b_1() +{ + // disable checked reserved bits + out8(REG_CONFIG3, in8(REG_CONFIG3) & ~CFG3_BEACON_ENABLE); + constexpr u16 version1_cplus_quirks = CPLUS_COMMAND_ENABLE_BIST | CPLUS_COMMAND_MAC_DBGO_OE | CPLUS_COMMAND_FORCE_HALF_DUP | CPLUS_COMMAND_FORCE_RXFLOW_ENABLE | CPLUS_COMMAND_FORCE_TXFLOW_ENABLE | CPLUS_COMMAND_CXPL_DBG_SEL | CPLUS_COMMAND_ASF | CPLUS_COMMAND_PACKET_CONTROL_DISABLE | CPLUS_COMMAND_MAC_DBGO_SEL; + out16(REG_CPLUS_COMMAND, in16(REG_CPLUS_COMMAND) & ~version1_cplus_quirks); +} + +void RTL8168NetworkAdapter::hardware_quirks_b_2() +{ + hardware_quirks_b_1(); + + // configure the maximum transmit packet size (again) + out16(REG_MTPS, MTPS_JUMBO); + + // disable checked reserved bits + out8(REG_CONFIG4, in8(REG_CONFIG4) & ~1); +} + +void RTL8168NetworkAdapter::hardware_quirks_h() +{ + // disable aspm and clock request before accessing extended phy + out8(REG_CONFIG2, in8(REG_CONFIG2) & ~CFG2_CLOCK_REQUEST_ENABLE); + out8(REG_CONFIG5, in8(REG_CONFIG5) & ~CFG5_ASPM_ENABLE); + + // initialize extended phy + constexpr EPhyUpdate ephy_info[] = { + { 0x1e, 0x800, 0x1 }, + { 0x1d, 0, 0x800 }, + { 0x5, 0xffff, 0x2089 }, + { 0x6, 0xffff, 0x5881 }, + { 0x4, 0xffff, 0x154a }, + { 0x1, 0xffff, 0x68b } + }; + extended_phy_initialize(ephy_info, 6); + + // enable tx auto fifo + out32(REG_TXCFG, in32(REG_TXCFG) | TXCFG_AUTO_FIFO); + + // vendor magic values ??? + eri_out(0xC8, ERI_MASK_0101, 0x80002, ERI_EXGMAC); + eri_out(0xCC, ERI_MASK_0001, 0x38, ERI_EXGMAC); + eri_out(0xD0, ERI_MASK_0001, 0x48, ERI_EXGMAC); + eri_out(0xE8, ERI_MASK_1111, 0x100006, ERI_EXGMAC); + + csi_enable(CSI_ACCESS_1); + + // vendor magic values ??? + eri_update(0xDC, ERI_MASK_0001, 0x0, 0x1, ERI_EXGMAC); + eri_update(0xDC, ERI_MASK_0001, 0x1, 0x0, ERI_EXGMAC); + eri_update(0xDC, ERI_MASK_1111, 0x10, 0x0, ERI_EXGMAC); + eri_update(0xD4, ERI_MASK_1111, 0x1F00, 0x0, ERI_EXGMAC); + eri_out(0x5F0, ERI_MASK_0011, 0x4F87, ERI_EXGMAC); + + // disable rxdv gate + out32(REG_MISC, in32(REG_MISC) & ~MISC_RXDV_GATE_ENABLE); + + // set early TX + out8(REG_MTPS, 0x27); + + // vendor magic values ??? + eri_out(0xC0, ERI_MASK_0011, 0, ERI_EXGMAC); + eri_out(0xB8, ERI_MASK_0011, 0, ERI_EXGMAC); + + // Set EEE LED frequency + out8(REG_EEE_LED, in8(REG_EEE_LED) & ~0x7); + + out8(REG_DLLPR, in8(REG_DLLPR) & ~DLLPR_PFM_ENABLE); + out8(REG_MISC2, in8(REG_MISC2) & ~MISC2_PFM_D3COLD_ENABLE); + out8(REG_DLLPR, in8(REG_DLLPR) & ~DLLPR_TX_10M_PS_ENABLE); + + // vendor magic values ??? + eri_update(0x1B0, ERI_MASK_0011, 0, 0x1000, ERI_EXGMAC); + + // disable l2l3 state + out8(REG_CONFIG3, in8(REG_CONFIG3) & ~CFG3_READY_TO_L23); + + // blackmagic code taken from linux's r8169 + phy_out(0x1F, 0x0C42); + auto rg_saw_count = (phy_in(0x13) & 0x3FFF); + phy_out(0x1F, 0); + if (rg_saw_count > 0) { + u16 sw_count_1ms_ini = 16000000 / rg_saw_count; + sw_count_1ms_ini &= 0x0fff; + u32 data = ocp_in(0xd412); + data &= ~0x0fff; + data |= sw_count_1ms_ini; + ocp_out(0xd412, data); + } + + u32 data = ocp_in(0xe056); + data &= ~0xf0; + data |= 0x70; + ocp_out(0xe056, data); + + data = ocp_in(0xe052); + data &= ~0x6000; + data |= 0x8008; + ocp_out(0xe052, data); + + data = ocp_in(0xe0d6); + data &= ~0x1ff; + data |= 0x17f; + ocp_out(0xe0d6, data); + + data = ocp_in(0xd420); + data &= ~0x0fff; + data |= 0x47f; + ocp_out(0xd420, data); + + ocp_out(0xe63e, 0x1); + ocp_out(0xe63e, 0); + ocp_out(0xc094, 0); + ocp_out(0xc09e, 0); +} + +void RTL8168NetworkAdapter::set_phy_speed() +{ + // wakeup phy + phy_out(0x1F, 0); + + // advertise all available features to get best connection possible + auto auto_negotiation_advertisement = phy_in(PHY_REG_ANAR); + auto_negotiation_advertisement |= ADVERTISE_10_HALF; // 10 mbit half duplex + auto_negotiation_advertisement |= ADVERTISE_10_FULL; // 10 mbit full duplex + auto_negotiation_advertisement |= ADVERTISE_100_HALF; // 100 mbit half duplex + auto_negotiation_advertisement |= ADVERTISE_100_FULL; // 100 mbit full duplex + auto_negotiation_advertisement |= ADVERTISE_PAUSE_CAP; // capable of pause flow control + auto_negotiation_advertisement |= ADVERTISE_PAUSE_ASYM; // capable of asymmetric pause flow control + phy_out(PHY_REG_ANAR, auto_negotiation_advertisement); + + auto gigabyte_control = phy_in(PHY_REG_GBCR); + gigabyte_control |= ADVERTISE_1000_HALF; // 1000 mbit half dulpex + gigabyte_control |= ADVERTISE_1000_FULL; // 1000 mbit full duplex + phy_out(PHY_REG_GBCR, gigabyte_control); + + // restart auto-negotation with set advertisements + phy_out(PHY_REG_BMCR, BMCR_AUTO_NEGOTIATE | BMCR_RESTART_AUTO_NEGOTIATE); +} + +UNMAP_AFTER_INIT void RTL8168NetworkAdapter::initialize_rx_descriptors() +{ + auto* rx_descriptors = (RXDescriptor*)m_rx_descriptors_region->vaddr().as_ptr(); + for (size_t i = 0; i < number_of_rx_descriptors; ++i) { + auto& descriptor = rx_descriptors[i]; + auto region = MM.allocate_contiguous_kernel_region(page_round_up(RX_BUFFER_SIZE), "RTL8168 RX buffer", Region::Access::Read | Region::Access::Write); + VERIFY(region); + memset(region->vaddr().as_ptr(), 0, region->size()); // MM already zeros out newly allocated pages, but we do it again in case that ever changes + m_rx_buffers_regions.append(region.release_nonnull()); + + descriptor.buffer_size = RX_BUFFER_SIZE; + descriptor.flags = RXDescriptor::Ownership; // let the NIC know it can use this descriptor + auto physical_address = m_rx_buffers_regions[i].physical_page(0)->paddr().get(); + descriptor.buffer_address_low = physical_address & 0xFFFFFFFF; + descriptor.buffer_address_high = (u64)physical_address >> 32; // cast to prevent shift count >= with of type warnings in 32 bit systems + } + rx_descriptors[number_of_rx_descriptors - 1].flags = rx_descriptors[number_of_rx_descriptors - 1].flags | RXDescriptor::EndOfRing; +} + +UNMAP_AFTER_INIT void RTL8168NetworkAdapter::initialize_tx_descriptors() +{ + auto* tx_descriptors = (TXDescriptor*)m_tx_descriptors_region->vaddr().as_ptr(); + for (size_t i = 0; i < number_of_tx_descriptors; ++i) { + auto& descriptor = tx_descriptors[i]; + auto region = MM.allocate_contiguous_kernel_region(page_round_up(TX_BUFFER_SIZE), "RTL8168 TX buffer", Region::Access::Read | Region::Access::Write); + VERIFY(region); + memset(region->vaddr().as_ptr(), 0, region->size()); // MM already zeros out newly allocated pages, but we do it again in case that ever changes + m_tx_buffers_regions.append(region.release_nonnull()); + + descriptor.flags = TXDescriptor::FirstSegment | TXDescriptor::LastSegment; + auto physical_address = m_tx_buffers_regions[i].physical_page(0)->paddr().get(); + descriptor.buffer_address_low = physical_address & 0xFFFFFFFF; + descriptor.buffer_address_high = (u64)physical_address >> 32; + } + tx_descriptors[number_of_tx_descriptors - 1].flags = tx_descriptors[number_of_tx_descriptors - 1].flags | TXDescriptor::EndOfRing; +} + +UNMAP_AFTER_INIT RTL8168NetworkAdapter::~RTL8168NetworkAdapter() +{ +} + +void RTL8168NetworkAdapter::handle_irq(const RegisterState&) +{ + for (;;) { + int status = in16(REG_ISR); + out16(REG_ISR, status); + + m_entropy_source.add_random_event(status); + + dbgln_if(RTL8168_DEBUG, "RTL8168: handle_irq status={:#04x}", status); + + if ((status & (INT_RXOK | INT_RXERR | INT_TXOK | INT_TXERR | INT_RX_OVERFLOW | INT_LINK_CHANGE | INT_RX_FIFO_OVERFLOW | INT_SYS_ERR)) == 0) + break; + + if (status & INT_RXOK) { + dbgln_if(RTL8168_DEBUG, "RTL8168: RX ready"); + receive(); + } + if (status & INT_RXERR) { + dbgln_if(RTL8168_DEBUG, "RTL8168: RX error - invalid packet"); + } + if (status & INT_TXOK) { + dbgln_if(RTL8168_DEBUG, "RTL8168: TX complete"); + m_wait_queue.wake_one(); + } + if (status & INT_TXERR) { + dbgln_if(RTL8168_DEBUG, "RTL8168: TX error - invalid packet"); + } + if (status & INT_RX_OVERFLOW) { + dmesgln("RTL8168: RX descriptor unavailable (packet lost)"); + receive(); + } + if (status & INT_LINK_CHANGE) { + m_link_up = (in8(REG_PHYSTATUS) & PHY_LINK_STATUS) != 0; + dmesgln("RTL8168: Link status changed up={}", m_link_up); + } + if (status & INT_RX_FIFO_OVERFLOW) { + dmesgln("RTL8168: RX FIFO overflow"); + receive(); + } + if (status & INT_SYS_ERR) { + dmesgln("RTL8168: Fatal system error"); + } + } +} + +void RTL8168NetworkAdapter::reset() +{ + out8(REG_COMMAND, COMMAND_RESET); + while ((in8(REG_COMMAND) & COMMAND_RESET) != 0) + ; +} + +UNMAP_AFTER_INIT void RTL8168NetworkAdapter::read_mac_address() +{ + MACAddress mac {}; + for (int i = 0; i < 6; i++) + mac[i] = in8(REG_MAC + i); + set_mac_address(mac); +} + +void RTL8168NetworkAdapter::send_raw(ReadonlyBytes payload) +{ + dbgln_if(RTL8168_DEBUG, "RTL8168: send_raw length={}", payload.size()); + + if (payload.size() > TX_BUFFER_SIZE) { + dmesgln("RTL8168: Packet was too big; discarding"); + return; + } + + auto* tx_descriptors = (TXDescriptor*)m_tx_descriptors_region->vaddr().as_ptr(); + auto& free_descriptor = tx_descriptors[m_tx_free_index]; + + if ((free_descriptor.flags & TXDescriptor::Ownership) != 0) { + dbgln_if(RTL8168_DEBUG, "RTL8168: No free TX buffers, sleeping until one is available"); + m_wait_queue.wait_forever("RTL8168NetworkAdapter"); + return send_raw(payload); + // if we woke up a TX descriptor is guaranteed to be available, so this should never recurse more than once + // but this can probably be done more cleanly + } + + dbgln_if(RTL8168_DEBUG, "RTL8168: Chose descriptor {}", m_tx_free_index); + memcpy(m_tx_buffers_regions[m_tx_free_index].vaddr().as_ptr(), payload.data(), payload.size()); + + m_tx_free_index = (m_tx_free_index + 1) % number_of_tx_descriptors; + + free_descriptor.frame_length = payload.size() & 0x3FFF; + free_descriptor.flags = free_descriptor.flags | TXDescriptor::Ownership; + + out8(REG_TXSTART, TXSTART_START); // FIXME: this shouldnt be done so often, we should look into doing this using the watchdog timer +} + +void RTL8168NetworkAdapter::receive() +{ + auto* rx_descriptors = (RXDescriptor*)m_rx_descriptors_region->vaddr().as_ptr(); + for (u16 i = 0; i < number_of_rx_descriptors; ++i) { + auto descriptor_index = (m_rx_free_index + i) % number_of_rx_descriptors; + auto& descriptor = rx_descriptors[descriptor_index]; + + if ((descriptor.flags & RXDescriptor::Ownership) != 0) { + m_rx_free_index = descriptor_index; + break; + } + + u16 flags = descriptor.flags; + u16 length = descriptor.buffer_size & 0x3FFF; + + dbgln_if(RTL8168_DEBUG, "RTL8168: receive, flags={:#04x}, length={}, descriptor={}", flags, length, descriptor_index); + + if (length > RX_BUFFER_SIZE || (flags & RXDescriptor::ErrorSummary) != 0) { + dmesgln("RTL8168: receive got bad packet, flags={:#04x}, length={}", flags, length); + } else if ((flags & RXDescriptor::FirstSegment) != 0 && (flags & RXDescriptor::LastSegment) == 0) { + VERIFY_NOT_REACHED(); + // Our maximum received packet size is smaller than the descriptor buffer size, so packets should never be segmented + // if this happens on a real NIC it might not respect that, and we will have to support packet segmentation + } else { + did_receive({ m_rx_buffers_regions[descriptor_index].vaddr().as_ptr(), length }); + } + + descriptor.buffer_size = RX_BUFFER_SIZE; + flags = RXDescriptor::Ownership; + if (descriptor_index == number_of_rx_descriptors - 1) + flags |= RXDescriptor::EndOfRing; + descriptor.flags = flags; // let the NIC know it can use this descriptor again + } +} + +void RTL8168NetworkAdapter::out8(u16 address, u8 data) +{ + m_io_base.offset(address).out(data); +} + +void RTL8168NetworkAdapter::out16(u16 address, u16 data) +{ + m_io_base.offset(address).out(data); +} + +void RTL8168NetworkAdapter::out32(u16 address, u32 data) +{ + m_io_base.offset(address).out(data); +} + +void RTL8168NetworkAdapter::out64(u16 address, u64 data) +{ + // ORDER MATTERS: Some NICs require the high part of the address to be written first + m_io_base.offset(address + 4).out((u32)(data >> 32)); + m_io_base.offset(address).out((u32)(data & 0xFFFFFFFF)); +} + +u8 RTL8168NetworkAdapter::in8(u16 address) +{ + return m_io_base.offset(address).in(); +} + +u16 RTL8168NetworkAdapter::in16(u16 address) +{ + return m_io_base.offset(address).in(); +} + +u32 RTL8168NetworkAdapter::in32(u16 address) +{ + return m_io_base.offset(address).in(); +} + +void RTL8168NetworkAdapter::phy_out(u8 address, u16 data) +{ + if (m_version == ChipVersion::Version11) { + TODO(); + } else if (m_version == ChipVersion::Version12 || m_version == ChipVersion::Version13) { + TODO(); + } else if (m_version >= ChipVersion::Version21) { + if (address == 0x1F) { + m_ocp_base_address = data ? data << 4 : OCP_STANDARD_PHY_BASE; + return; + } + + if (m_ocp_base_address != OCP_STANDARD_PHY_BASE) + address -= 0x10; + + ocp_phy_out(m_ocp_base_address + address * 2, data); + } else { + VERIFY((address & 0xE0) == 0); // register address is only 5 bit + out32(REG_PHYACCESS, PHY_FLAG | (address & 0x1F) << 16 | (data & 0xFFFF)); + while ((in32(REG_PHYACCESS) & PHY_FLAG) != 0) + ; + } +} + +u16 RTL8168NetworkAdapter::phy_in(u8 address) +{ + if (m_version == ChipVersion::Version11) { + TODO(); + } else if (m_version == ChipVersion::Version12 || m_version == ChipVersion::Version13) { + TODO(); + } else if (m_version >= ChipVersion::Version21) { + if (m_ocp_base_address != OCP_STANDARD_PHY_BASE) + address -= 0x10; + + return ocp_phy_in(m_ocp_base_address + address * 2); + } else { + VERIFY((address & 0xE0) == 0); // register address is only 5 bit + out32(REG_PHYACCESS, (address & 0x1F) << 16); + while ((in32(REG_PHYACCESS) & PHY_FLAG) == 0) + ; + return in32(REG_PHYACCESS) & 0xFFFF; + } +} + +void RTL8168NetworkAdapter::phy_update(u32 address, u32 set, u32 clear) +{ + auto value = phy_in(address); + phy_out(address, (value & ~clear) | set); +} + +void RTL8168NetworkAdapter::phy_out_batch(const PhyRegister phy_registers[], size_t length) +{ + for (size_t i = 0; i < length; i++) { + phy_out(phy_registers[i].address, phy_registers[i].data); + } +} + +void RTL8168NetworkAdapter::extended_phy_out(u8 address, u16 data) +{ + VERIFY((address & 0xE0) == 0); // register address is only 5 bit + out32(REG_EPHYACCESS, EPHY_FLAG | (address & 0x1F) << 16 | (data & 0xFFFF)); + while ((in32(REG_EPHYACCESS) & EPHY_FLAG) != 0) + ; +} + +u16 RTL8168NetworkAdapter::extended_phy_in(u8 address) +{ + VERIFY((address & 0xE0) == 0); // register address is only 5 bit + out32(REG_EPHYACCESS, (address & 0x1F) << 16); + while ((in32(REG_EPHYACCESS) & EPHY_FLAG) == 0) + ; + return in32(REG_EPHYACCESS) & 0xFFFF; +} + +void RTL8168NetworkAdapter::extended_phy_initialize(const EPhyUpdate ephy_info[], size_t length) +{ + for (size_t i = 0; i < length; i++) { + auto updated_value = (extended_phy_in(ephy_info[i].offset) & ~ephy_info[i].clear) | ephy_info[i].set; + extended_phy_out(ephy_info[i].offset, updated_value); + } +} + +void RTL8168NetworkAdapter::eri_out(u32 address, u32 mask, u32 data, u32 type) +{ + out32(REG_ERI_DATA, data); + out32(REG_ERI_ADDR, ERI_FLAG | type | mask | address); + while ((in32(REG_ERI_ADDR) & ERI_FLAG) != 0) + ; +} + +u32 RTL8168NetworkAdapter::eri_in(u32 address, u32 type) +{ + out32(REG_ERI_ADDR, type | ERI_MASK_1111 | address); + while ((in32(REG_ERI_ADDR) & ERI_FLAG) == 0) + ; + return in32(REG_ERI_DATA); +} + +void RTL8168NetworkAdapter::eri_update(u32 address, u32 mask, u32 set, u32 clear, u32 type) +{ + auto value = eri_in(address, type); + eri_out(address, mask, (value & ~clear) | set, type); +} + +void RTL8168NetworkAdapter::csi_out(u32 address, u32 data) +{ + VERIFY(m_version >= ChipVersion::Version4); + out32(REG_CSI_DATA, data); + auto modifier = CSI_BYTE_ENABLE; + if (m_version == ChipVersion::Version20) { + modifier |= CSI_FUNC_NIC; + } else if (m_version == ChipVersion::Version26) { + modifier |= CSI_FUNC_NIC2; + } + out32(REG_CSI_ADDR, CSI_FLAG | (address & 0xFFF) | modifier); + while ((in32(REG_CSI_ADDR) & CSI_FLAG) != 0) + ; +} + +u32 RTL8168NetworkAdapter::csi_in(u32 address) +{ + VERIFY(m_version >= ChipVersion::Version4); + auto modifier = CSI_BYTE_ENABLE; + if (m_version == ChipVersion::Version20) { + modifier |= CSI_FUNC_NIC; + } else if (m_version == ChipVersion::Version26) { + modifier |= CSI_FUNC_NIC2; + } + out32(REG_CSI_ADDR, (address & 0xFFF) | modifier); + while ((in32(REG_CSI_ADDR) & CSI_FLAG) == 0) + ; + return in32(REG_CSI_DATA) & 0xFFFF; +} + +void RTL8168NetworkAdapter::csi_enable(u32 bits) +{ + auto csi = csi_in(0x70c) & 0x00ffffff; + csi_out(0x70c, csi | bits); +} + +void RTL8168NetworkAdapter::ocp_out(u32 address, u32 data) +{ + VERIFY((address & 0xFFFF0001) == 0); + out32(REG_OCP_DATA, OCP_FLAG | address << 15 | data); +} + +u32 RTL8168NetworkAdapter::ocp_in(u32 address) +{ + VERIFY((address & 0xFFFF0001) == 0); + out32(REG_OCP_DATA, address << 15); + return in32(REG_OCP_DATA); +} + +void RTL8168NetworkAdapter::ocp_phy_out(u32 address, u32 data) +{ + VERIFY((address & 0xFFFF0001) == 0); + out32(REG_GPHY_OCP, OCP_FLAG | (address << 15) | data); + while ((in32(REG_GPHY_OCP) & OCP_FLAG) != 0) + ; +} + +u16 RTL8168NetworkAdapter::ocp_phy_in(u32 address) +{ + VERIFY((address & 0xFFFF0001) == 0); + out32(REG_GPHY_OCP, address << 15); + while ((in32(REG_GPHY_OCP) & OCP_FLAG) == 0) + ; + return in32(REG_GPHY_OCP) & 0xFFFF; +} + +void RTL8168NetworkAdapter::identify_chip_version() +{ + auto transmit_config = in32(REG_TXCFG); + auto registers = transmit_config & 0x7c800000; + auto hw_version_id = transmit_config & 0x700000; + + m_version_uncertain = false; + + switch (registers) { + case 0x30000000: + m_version = ChipVersion::Version1; + break; + case 0x38000000: + if (hw_version_id == 00000) { + m_version = ChipVersion::Version2; + } else if (hw_version_id == 0x500000) { + m_version = ChipVersion::Version3; + } else { + m_version = ChipVersion::Version3; + m_version_uncertain = true; + } + break; + case 0x3C000000: + if (hw_version_id == 00000) { + m_version = ChipVersion::Version4; + } else if (hw_version_id == 0x200000) { + m_version = ChipVersion::Version5; + } else if (hw_version_id == 0x400000) { + m_version = ChipVersion::Version6; + } else { + m_version = ChipVersion::Version6; + m_version_uncertain = true; + } + break; + case 0x3C800000: + if (hw_version_id == 0x100000) { + m_version = ChipVersion::Version7; + } else if (hw_version_id == 0x300000) { + m_version = ChipVersion::Version8; + } else { + m_version = ChipVersion::Version8; + m_version_uncertain = true; + } + break; + case 0x28000000: + if (hw_version_id == 0x100000) { + m_version = ChipVersion::Version9; + } else if (hw_version_id == 0x300000) { + m_version = ChipVersion::Version10; + } else { + m_version = ChipVersion::Version10; + m_version_uncertain = true; + } + break; + case 0x28800000: + if (hw_version_id == 00000) { + m_version = ChipVersion::Version11; + } else if (hw_version_id == 0x200000) { + m_version = ChipVersion::Version12; + } else if (hw_version_id == 0x300000) { + m_version = ChipVersion::Version13; + } else { + m_version = ChipVersion::Version13; + m_version_uncertain = true; + } + break; + case 0x2C000000: + if (hw_version_id == 0x100000) { + m_version = ChipVersion::Version14; + } else if (hw_version_id == 0x200000) { + m_version = ChipVersion::Version15; + } else { + m_version = ChipVersion::Version15; + m_version_uncertain = true; + } + break; + case 0x2C800000: + if (hw_version_id == 00000) { + m_version = ChipVersion::Version16; + } else if (hw_version_id == 0x100000) { + m_version = ChipVersion::Version17; + } else { + m_version = ChipVersion::Version17; + m_version_uncertain = true; + } + break; + case 0x48000000: + if (hw_version_id == 00000) { + m_version = ChipVersion::Version18; + } else if (hw_version_id == 0x100000) { + m_version = ChipVersion::Version19; + } else { + m_version = ChipVersion::Version19; + m_version_uncertain = true; + } + break; + case 0x48800000: + if (hw_version_id == 00000) { + m_version = ChipVersion::Version20; + } else { + m_version = ChipVersion::Version20; + m_version_uncertain = true; + } + + break; + case 0x4C000000: + if (hw_version_id == 00000) { + m_version = ChipVersion::Version21; + } else if (hw_version_id == 0x100000) { + m_version = ChipVersion::Version22; + } else { + m_version = ChipVersion::Version22; + m_version_uncertain = true; + } + break; + case 0x50000000: + if (hw_version_id == 00000) { + m_version = ChipVersion::Version23; + } else if (hw_version_id == 0x100000) { + m_version = ChipVersion::Version27; + } else if (hw_version_id == 0x200000) { + m_version = ChipVersion::Version28; + } else { + m_version = ChipVersion::Version28; + m_version_uncertain = true; + } + break; + case 0x50800000: + if (hw_version_id == 00000) { + m_version = ChipVersion::Version24; + } else if (hw_version_id == 0x100000) { + m_version = ChipVersion::Version25; + } else { + m_version = ChipVersion::Version25; + m_version_uncertain = true; + } + break; + case 0x5C800000: + if (hw_version_id == 00000) { + m_version = ChipVersion::Version26; + } else { + m_version = ChipVersion::Version26; + m_version_uncertain = true; + } + break; + case 0x54000000: + if (hw_version_id == 00000) { + m_version = ChipVersion::Version29; + } else if (hw_version_id == 0x100000) { + m_version = ChipVersion::Version30; + } else { + m_version = ChipVersion::Version30; + m_version_uncertain = true; + } + break; + default: + dbgln_if(RTL8168_DEBUG, "Unable to determine device version: {#04x}", registers); + m_version = ChipVersion::Unknown; + m_version_uncertain = true; + break; + } +} + +String RTL8168NetworkAdapter::possible_device_name() +{ + switch (m_version) { // We are following *BSD's versioning scheme, the comments note linux's versioning scheme, but they dont match up exactly + case ChipVersion::Version1: + case ChipVersion::Version2: + case ChipVersion::Version3: + return "RTL8168B/8111B"; // 11, 12, 17 + case ChipVersion::Version4: + case ChipVersion::Version5: + case ChipVersion::Version6: + return "RTL8168C/8111C"; // 19, 20, 21, 22 + case ChipVersion::Version7: + case ChipVersion::Version8: + return "RTL8168CP/8111CP"; // 18, 23, 24 + case ChipVersion::Version9: + case ChipVersion::Version10: + return "RTL8168D/8111D"; // 25, 26 + case ChipVersion::Version11: + case ChipVersion::Version12: + case ChipVersion::Version13: + return "RTL8168DP/8111DP"; // 27, 28, 31 + case ChipVersion::Version14: + case ChipVersion::Version15: + return "RTL8168E/8111E"; // 32, 33 + case ChipVersion::Version16: + case ChipVersion::Version17: + return "RTL8168E-VL/8111E-VL"; // 34 + case ChipVersion::Version18: + case ChipVersion::Version19: + return "RTL8168F/8111F"; // 35, 36 + case ChipVersion::Version20: + return "RTL8411"; // 38 + case ChipVersion::Version21: + case ChipVersion::Version22: + return "RTL8168G/8111G"; // 40, 41, 42 + case ChipVersion::Version23: + case ChipVersion::Version27: + case ChipVersion::Version28: + return "RTL8168EP/8111EP"; // 49, 50, 51 + case ChipVersion::Version24: + case ChipVersion::Version25: + return "RTL8168GU/8111GU"; // ??? + case ChipVersion::Version26: + return "RTL8411B"; // 44 + case ChipVersion::Version29: + case ChipVersion::Version30: + return "RTL8168H/8111H"; // 45, 46 + case ChipVersion::Unknown: + return "Unknown"; + } + VERIFY_NOT_REACHED(); +} +} diff --git a/Kernel/Net/RTL8168NetworkAdapter.h b/Kernel/Net/RTL8168NetworkAdapter.h new file mode 100644 index 0000000000..9a6fccb6ab --- /dev/null +++ b/Kernel/Net/RTL8168NetworkAdapter.h @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2021, Idan Horowitz + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace Kernel { + +// RTL8618 / RTL8111 Driver based on https://people.freebsd.org/~wpaul/RealTek/RTL8111B_8168B_Registers_DataSheet_1.0.pdf +class RTL8168NetworkAdapter final : public NetworkAdapter + , public PCI::Device { +public: + static RefPtr try_to_initialize(PCI::Address); + + virtual ~RTL8168NetworkAdapter() override; + + virtual void send_raw(ReadonlyBytes) override; + virtual bool link_up() override { return m_link_up; } + + virtual const char* purpose() const override { return class_name(); } + +private: + // FIXME: should this be increased? (maximum allowed here is 1024) - memory usage vs packet loss chance tradeoff + static const size_t number_of_rx_descriptors = 64; + static const size_t number_of_tx_descriptors = 16; + + RTL8168NetworkAdapter(PCI::Address, u8 irq); + + virtual void handle_irq(const RegisterState&) override; + virtual const char* class_name() const override { return "RTL8168NetworkAdapter"; } + + struct [[gnu::packed]] TXDescriptor { + volatile u16 frame_length; // top 2 bits are reserved + volatile u16 flags; + volatile u16 vlan_tag; + volatile u16 vlan_flags; + volatile u32 buffer_address_low; + volatile u32 buffer_address_high; + + // flags bit field + static constexpr u16 Ownership = 0x8000u; + static constexpr u16 EndOfRing = 0x4000u; + static constexpr u16 FirstSegment = 0x2000u; + static constexpr u16 LastSegment = 0x1000u; + static constexpr u16 LargeSend = 0x800u; + }; + + static_assert(sizeof(TXDescriptor) == 16u); + + struct [[gnu::packed]] RXDescriptor { + volatile u16 buffer_size; // top 2 bits are reserved + volatile u16 flags; + volatile u16 vlan_tag; + volatile u16 vlan_flags; + volatile u32 buffer_address_low; + volatile u32 buffer_address_high; + + // flags bit field + static constexpr u16 Ownership = 0x8000u; + static constexpr u16 EndOfRing = 0x4000u; + static constexpr u16 FirstSegment = 0x2000u; + static constexpr u16 LastSegment = 0x1000u; + static constexpr u16 MulticastPacket = 0x800u; + static constexpr u16 PhysicalPacket = 0x400u; + static constexpr u16 BroadcastPacket = 0x200u; + static constexpr u16 WatchdogTimerExpired = 0x40; + static constexpr u16 ErrorSummary = 0x20; + static constexpr u16 RuntPacket = 0x10; + static constexpr u16 CRCError = 0x8; + }; + + static_assert(sizeof(RXDescriptor) == 16u); + + enum class ChipVersion : u8 { + Unknown = 0, + Version1 = 1, + Version2 = 2, + Version3 = 3, + Version4 = 4, + Version5 = 5, + Version6 = 6, + Version7 = 7, + Version8 = 8, + Version9 = 9, + Version10 = 10, + Version11 = 11, + Version12 = 12, + Version13 = 13, + Version14 = 14, + Version15 = 15, + Version16 = 16, + Version17 = 17, + Version18 = 18, + Version19 = 19, + Version20 = 20, + Version21 = 21, + Version22 = 22, + Version23 = 23, + Version24 = 24, + Version25 = 25, + Version26 = 26, + Version27 = 27, + Version28 = 28, + Version29 = 29, + Version30 = 30 + }; + + void identify_chip_version(); + String possible_device_name(); + + void reset(); + void read_mac_address(); + void set_phy_speed(); + void start_hardware(); + void initialize(); + void startup(); + + void configure_phy(); + void configure_phy_b_1(); + void configure_phy_b_2(); + void configure_phy_h_1(); + void configure_phy_h_2(); + + void hardware_quirks(); + void hardware_quirks_b_1(); + void hardware_quirks_b_2(); + void hardware_quirks_h(); + + void initialize_rx_descriptors(); + void initialize_tx_descriptors(); + + void receive(); + + void out8(u16 address, u8 data); + void out16(u16 address, u16 data); + void out32(u16 address, u32 data); + void out64(u16 address, u64 data); + u8 in8(u16 address); + u16 in16(u16 address); + u32 in32(u16 address); + + void phy_out(u8 address, u16 data); + u16 phy_in(u8 address); + void phy_update(u32 address, u32 set, u32 clear); + struct PhyRegister { + u16 address; + u16 data; + }; + void phy_out_batch(const PhyRegister[], size_t length); + + void extended_phy_out(u8 address, u16 data); + u16 extended_phy_in(u8 address); + struct EPhyUpdate { + u32 offset; + u16 clear; + u16 set; + }; + void extended_phy_initialize(const EPhyUpdate[], size_t length); + + void eri_out(u32 address, u32 mask, u32 data, u32 type); + u32 eri_in(u32 address, u32 type); + void eri_update(u32 address, u32 mask, u32 set, u32 clear, u32 type); + + void csi_out(u32 address, u32 data); + u32 csi_in(u32 address); + void csi_enable(u32 bits); + + void ocp_out(u32 address, u32 data); + u32 ocp_in(u32 address); + + void ocp_phy_out(u32 address, u32 data); + u16 ocp_phy_in(u32 address); + + ChipVersion m_version { ChipVersion::Unknown }; + bool m_version_uncertain { true }; + IOAddress m_io_base; + u32 m_ocp_base_address { 0 }; + OwnPtr m_rx_descriptors_region; + NonnullOwnPtrVector m_rx_buffers_regions; + u16 m_rx_free_index { 0 }; + OwnPtr m_tx_descriptors_region; + NonnullOwnPtrVector m_tx_buffers_regions; + u16 m_tx_free_index { 0 }; + bool m_link_up { false }; + EntropySource m_entropy_source; + WaitQueue m_wait_queue; +}; +} diff --git a/Kernel/PCI/IDs.h b/Kernel/PCI/IDs.h index 52a7d80b8b..7c40e79a3e 100644 --- a/Kernel/PCI/IDs.h +++ b/Kernel/PCI/IDs.h @@ -12,7 +12,8 @@ enum class PCIVendorID { VirtIO = 0x1af4, Intel = 0x8086, WCH = 0x1c00, - RedHat = 0x1b36 + RedHat = 0x1b36, + Realtek = 0x10ec }; enum class PCIDeviceID { diff --git a/Meta/CMake/all_the_debug_macros.cmake b/Meta/CMake/all_the_debug_macros.cmake index d90a5d892c..342808ca9e 100644 --- a/Meta/CMake/all_the_debug_macros.cmake +++ b/Meta/CMake/all_the_debug_macros.cmake @@ -142,6 +142,7 @@ set(RESOURCE_DEBUG ON) set(ROUTING_DEBUG ON) set(RSA_PARSE_DEBUG ON) set(RTL8139_DEBUG ON) +set(RTL8168_DEBUG ON) set(SAFE_SYSCALL_DEBUG ON) set(SB16_DEBUG ON) set(SCHEDULER_DEBUG ON)