From af99d75fc627c379e653ca7dd2e98db6b6b92576 Mon Sep 17 00:00:00 2001 From: Hendiadyoin1 Date: Sun, 1 Dec 2024 15:59:26 +0100 Subject: [PATCH] Kernel: Modernize the E1000(E) driver using the new IORegister API This also adds some quirk handling, and 64 bit DMA support, which where previously missing. --- Kernel/Net/Intel/E1000ENetworkAdapter.cpp | 17 +- Kernel/Net/Intel/E1000ENetworkAdapter.h | 1 - Kernel/Net/Intel/E1000NetworkAdapter.cpp | 404 ++++++++++------------ Kernel/Net/Intel/E1000NetworkAdapter.h | 19 +- Kernel/Net/Intel/E1000Registers.h | 329 ++++++++++++++++++ 5 files changed, 518 insertions(+), 252 deletions(-) create mode 100644 Kernel/Net/Intel/E1000Registers.h diff --git a/Kernel/Net/Intel/E1000ENetworkAdapter.cpp b/Kernel/Net/Intel/E1000ENetworkAdapter.cpp index 222c8b0d77bc85b..0736b2375ec0f37 100644 --- a/Kernel/Net/Intel/E1000ENetworkAdapter.cpp +++ b/Kernel/Net/Intel/E1000ENetworkAdapter.cpp @@ -218,8 +218,9 @@ UNMAP_AFTER_INIT ErrorOr E1000ENetworkAdapter::initialize(Badge().eeprom_present) m_has_eeprom.set(); } -UNMAP_AFTER_INIT u32 E1000ENetworkAdapter::read_eeprom(u8 address) -{ - VERIFY(m_has_eeprom.was_set()); - u16 data = 0; - u32 tmp = 0; - out32(REG_EEPROM, ((u32)address << 2) | 1); - while (!((tmp = in32(REG_EEPROM)) & (1 << 1))) - Processor::wait_check(); - data = (tmp >> 16) & 0xffff; - return data; -} - } diff --git a/Kernel/Net/Intel/E1000ENetworkAdapter.h b/Kernel/Net/Intel/E1000ENetworkAdapter.h index 59252ef26ac65d0..f3d68161b32983b 100644 --- a/Kernel/Net/Intel/E1000ENetworkAdapter.h +++ b/Kernel/Net/Intel/E1000ENetworkAdapter.h @@ -37,6 +37,5 @@ class E1000ENetworkAdapter final virtual StringView class_name() const override { return "E1000ENetworkAdapter"sv; } virtual void detect_eeprom() override; - virtual u32 read_eeprom(u8 address) override; }; } diff --git a/Kernel/Net/Intel/E1000NetworkAdapter.cpp b/Kernel/Net/Intel/E1000NetworkAdapter.cpp index 2f685c37f2d73bb..56a131c661c6c5c 100644 --- a/Kernel/Net/Intel/E1000NetworkAdapter.cpp +++ b/Kernel/Net/Intel/E1000NetworkAdapter.cpp @@ -14,113 +14,6 @@ namespace Kernel { -#define REG_CTRL 0x0000 -#define REG_STATUS 0x0008 -#define REG_EEPROM 0x0014 -#define REG_CTRL_EXT 0x0018 -#define REG_INTERRUPT_CAUSE_READ 0x00C0 -#define REG_INTERRUPT_RATE 0x00C4 -#define REG_INTERRUPT_MASK_SET 0x00D0 -#define REG_INTERRUPT_MASK_CLEAR 0x00D8 -#define REG_RCTRL 0x0100 -#define REG_RXDESCLO 0x2800 -#define REG_RXDESCHI 0x2804 -#define REG_RXDESCLEN 0x2808 -#define REG_RXDESCHEAD 0x2810 -#define REG_RXDESCTAIL 0x2818 -#define REG_TCTRL 0x0400 -#define REG_TXDESCLO 0x3800 -#define REG_TXDESCHI 0x3804 -#define REG_TXDESCLEN 0x3808 -#define REG_TXDESCHEAD 0x3810 -#define REG_TXDESCTAIL 0x3818 -#define REG_RDTR 0x2820 // RX Delay Timer Register -#define REG_RXDCTL 0x3828 // RX Descriptor Control -#define REG_RADV 0x282C // RX Int. Absolute Delay Timer -#define REG_RSRPD 0x2C00 // RX Small Packet Detect Interrupt -#define REG_TIPG 0x0410 // Transmit Inter Packet Gap -#define ECTRL_SLU 0x40 // set link up -#define RCTL_EN (1 << 1) // Receiver Enable -#define RCTL_SBP (1 << 2) // Store Bad Packets -#define RCTL_UPE (1 << 3) // Unicast Promiscuous Enabled -#define RCTL_MPE (1 << 4) // Multicast Promiscuous Enabled -#define RCTL_LPE (1 << 5) // Long Packet Reception Enable -#define RCTL_LBM_NONE (0 << 6) // No Loopback -#define RCTL_LBM_PHY (3 << 6) // PHY or external SerDesc loopback -#define RTCL_RDMTS_HALF (0 << 8) // Free Buffer Threshold is 1/2 of RDLEN -#define RTCL_RDMTS_QUARTER (1 << 8) // Free Buffer Threshold is 1/4 of RDLEN -#define RTCL_RDMTS_EIGHTH (2 << 8) // Free Buffer Threshold is 1/8 of RDLEN -#define RCTL_MO_36 (0 << 12) // Multicast Offset - bits 47:36 -#define RCTL_MO_35 (1 << 12) // Multicast Offset - bits 46:35 -#define RCTL_MO_34 (2 << 12) // Multicast Offset - bits 45:34 -#define RCTL_MO_32 (3 << 12) // Multicast Offset - bits 43:32 -#define RCTL_BAM (1 << 15) // Broadcast Accept Mode -#define RCTL_VFE (1 << 18) // VLAN Filter Enable -#define RCTL_CFIEN (1 << 19) // Canonical Form Indicator Enable -#define RCTL_CFI (1 << 20) // Canonical Form Indicator Bit Value -#define RCTL_DPF (1 << 22) // Discard Pause Frames -#define RCTL_PMCF (1 << 23) // Pass MAC Control Frames -#define RCTL_SECRC (1 << 26) // Strip Ethernet CRC - -// Buffer Sizes -#define RCTL_BSIZE_256 (3 << 16) -#define RCTL_BSIZE_512 (2 << 16) -#define RCTL_BSIZE_1024 (1 << 16) -#define RCTL_BSIZE_2048 (0 << 16) -#define RCTL_BSIZE_4096 ((3 << 16) | (1 << 25)) -#define RCTL_BSIZE_8192 ((2 << 16) | (1 << 25)) -#define RCTL_BSIZE_16384 ((1 << 16) | (1 << 25)) - -// Transmit Command - -#define CMD_EOP (1 << 0) // End of Packet -#define CMD_IFCS (1 << 1) // Insert FCS -#define CMD_IC (1 << 2) // Insert Checksum -#define CMD_RS (1 << 3) // Report Status -#define CMD_RPS (1 << 4) // Report Packet Sent -#define CMD_VLE (1 << 6) // VLAN Packet Enable -#define CMD_IDE (1 << 7) // Interrupt Delay Enable - -// TCTL Register - -#define TCTL_EN (1 << 1) // Transmit Enable -#define TCTL_PSP (1 << 3) // Pad Short Packets -#define TCTL_CT_SHIFT 4 // Collision Threshold -#define TCTL_COLD_SHIFT 12 // Collision Distance -#define TCTL_SWXOFF (1 << 22) // Software XOFF Transmission -#define TCTL_RTLC (1 << 24) // Re-transmit on Late Collision - -#define TSTA_DD (1 << 0) // Descriptor Done -#define TSTA_EC (1 << 1) // Excess Collisions -#define TSTA_LC (1 << 2) // Late Collision -#define LSTA_TU (1 << 3) // Transmit Underrun - -// STATUS Register - -#define STATUS_FD 0x01 -#define STATUS_LU 0x02 -#define STATUS_TXOFF 0x08 -#define STATUS_SPEED 0xC0 -#define STATUS_SPEED_10MB 0x00 -#define STATUS_SPEED_100MB 0x40 -#define STATUS_SPEED_1000MB1 0x80 -#define STATUS_SPEED_1000MB2 0xC0 - -// Interrupt Masks - -#define INTERRUPT_TXDW (1 << 0) -#define INTERRUPT_TXQE (1 << 1) -#define INTERRUPT_LSC (1 << 2) -#define INTERRUPT_RXSEQ (1 << 3) -#define INTERRUPT_RXDMT0 (1 << 4) -#define INTERRUPT_RXO (1 << 6) -#define INTERRUPT_RXT0 (1 << 7) -#define INTERRUPT_MDAC (1 << 9) -#define INTERRUPT_RXCFG (1 << 10) -#define INTERRUPT_PHYINT (1 << 12) -#define INTERRUPT_TXD_LOW (1 << 15) -#define INTERRUPT_SRPD (1 << 16) - // https://www.intel.com/content/dam/doc/manual/pci-pci-x-family-gbe-controllers-software-dev-manual.pdf Section 5.2 UNMAP_AFTER_INIT static bool is_valid_device_id(u16 device_id) { @@ -192,8 +85,9 @@ UNMAP_AFTER_INIT ErrorOr E1000NetworkAdapter::initialize(Badge E1000NetworkAdapter::initialize(Badge().link_up != 0; autoconfigure_link_local_ipv6(); return {}; } +UNMAP_AFTER_INIT void E1000NetworkAdapter::check_quirks() +{ + + u16 device_id = device_identifier().hardware_id().device_id; + // The 82541xx and 82547GI/EI have a different EEPROM access method + switch (device_id) { + case 0x1019: // 82547EI-A0, 82547EI-A1, 82547EI-B0, 82547GI-B0 + case 0x101A: // 82547EI-B0 + case 0x1013: // 82541EI-A0, 82541EI-B0 + case 0x1018: // 82541EI-B0 + case 0x1076: // 82541GI-B1, 82541PI-C0 + case 0x1077: // 82541GI-B1 + case 0x1078: // 82541ER-C0 + m_is_82541xx_82547GI_EI.set(); + break; + default: + break; + } +} + UNMAP_AFTER_INIT void E1000NetworkAdapter::setup_link() { - u32 flags = in32(REG_CTRL); - out32(REG_CTRL, flags | ECTRL_SLU); + auto ctrl = m_registers.read(); + ctrl.set_link_up = 1; + m_registers.write(ctrl); } UNMAP_AFTER_INIT void E1000NetworkAdapter::setup_interrupts() { - out32(REG_INTERRUPT_RATE, 6000); // Interrupt rate of 1.536 milliseconds - out32(REG_INTERRUPT_MASK_SET, INTERRUPT_LSC | INTERRUPT_RXT0 | INTERRUPT_RXO); - in32(REG_INTERRUPT_CAUSE_READ); + // FIXME: Do this properly, + // There's probably a way to find an ideal interrupt rate etc + m_registers.write(6000); // Interrupt rate of 1.536 milliseconds + // We want: Link status change, RX timer, RX overrun + using InterruptMask = E1000::InterruptMask; + m_registers.write(InterruptMask::LSC | InterruptMask::RXT0 | InterruptMask::RXO); + (void)m_registers.read(); enable_irq(); } @@ -234,7 +153,7 @@ UNMAP_AFTER_INIT E1000NetworkAdapter::E1000NetworkAdapter(StringView interface_n : NetworkAdapter(interface_name) , PCI::Device(device_identifier) , IRQHandler(irq) - , m_registers_io_window(move(registers_io_window)) + , m_registers(move(registers_io_window)) , m_rx_descriptors(move(rx_descriptors)) , m_tx_descriptors(move(tx_descriptors)) , m_rx_buffer_region(move(rx_buffer_region)) @@ -246,82 +165,108 @@ UNMAP_AFTER_INIT E1000NetworkAdapter::~E1000NetworkAdapter() = default; bool E1000NetworkAdapter::handle_irq() { - u32 status = in32(REG_INTERRUPT_CAUSE_READ); + auto irq_cause = m_registers.read(); - m_entropy_source.add_random_event(status); + m_entropy_source.add_random_event(irq_cause); - if (status == 0) + if (irq_cause.raw == 0) return false; - if (status & INTERRUPT_LSC) { - u32 flags = in32(REG_CTRL); - out32(REG_CTRL, flags | ECTRL_SLU); + if (irq_cause.link_status_change) { + auto ctrl = m_registers.read(); + ctrl.set_link_up = 1; + m_registers.write(ctrl); + + m_link_up = m_registers.read().link_up != 0; - m_link_up = ((in32(REG_STATUS) & STATUS_LU) != 0); autoconfigure_link_local_ipv6(); } - if (status & INTERRUPT_RXDMT0) { + if (irq_cause.receive_descriptor_minimum_threshold_reached) { // Threshold OK? } - if (status & INTERRUPT_RXO) { + if (irq_cause.receiver_overrun) { dbgln_if(E1000_DEBUG, "E1000: RX buffer overrun"); } - if (status & INTERRUPT_RXT0) { + if (irq_cause.receive_timer_interrupt) { receive(); } m_wait_queue.wake_all(); - out32(REG_INTERRUPT_CAUSE_READ, 0xffffffff); + m_registers.write({ .raw = 0xffffffff }); return true; } UNMAP_AFTER_INIT void E1000NetworkAdapter::detect_eeprom() { - out32(REG_EEPROM, 0x1); + // FIXME: On most devices we can check the EECD.EE_PRES register (this is what the E1000E driver does) + // But this is not supported on the 82544GC/EI + + m_registers.write({ .start = 1 }); for (int i = 0; i < 999; ++i) { - u32 data = in32(REG_EEPROM); - if (data & 0x10) { - m_has_eeprom.set(); - return; + auto data = m_registers.read(); + if (m_is_82541xx_82547GI_EI.was_set()) { + if (data.variant_82541xx_or_82547GI_EI.done) { + m_has_eeprom.set(); + return; + } + } else { + if (data.done) { + m_has_eeprom.set(); + return; + } } + Processor::wait_check(); } + + dmesgln_pci(*this, "E1000: EEPROM failed to initialize"); } -UNMAP_AFTER_INIT u32 E1000NetworkAdapter::read_eeprom(u8 address) +UNMAP_AFTER_INIT u16 E1000NetworkAdapter::read_eeprom(u16 address) { - u16 data = 0; - u32 tmp = 0; - if (m_has_eeprom.was_set()) { - out32(REG_EEPROM, ((u32)address << 8) | 1); - while (!((tmp = in32(REG_EEPROM)) & (1 << 4))) - Processor::wait_check(); - } else { - out32(REG_EEPROM, ((u32)address << 2) | 1); - while (!((tmp = in32(REG_EEPROM)) & (1 << 1))) + // FIXME: Should this just return 0 then? + VERIFY(m_has_eeprom.was_set()); + + // FIXME: Maybe add a timeout to the loops? + + if (m_is_82541xx_82547GI_EI.was_set()) { + E1000::EEPROMRead eerd = { .variant_82541xx_or_82547GI_EI { .start = 1, .address = address } }; + m_registers.write(eerd); + while (eerd = m_registers.read(), + !eerd.variant_82541xx_or_82547GI_EI.done) Processor::wait_check(); + return eerd.variant_82541xx_or_82547GI_EI.data; } - data = (tmp >> 16) & 0xffff; - return data; + + // The normal EEPROM only has an 8 bit address field + VERIFY(address < 0xFF); + E1000::EEPROMRead eerd = { .start = 1, .address = address }; + m_registers.write(eerd); + while (eerd = m_registers.read(), !eerd.done) + Processor::wait_check(); + return eerd.data; } UNMAP_AFTER_INIT void E1000NetworkAdapter::read_mac_address() { - if (m_has_eeprom.was_set()) { - MACAddress mac {}; - u32 tmp = read_eeprom(0); - mac[0] = tmp & 0xff; - mac[1] = tmp >> 8; - tmp = read_eeprom(1); - mac[2] = tmp & 0xff; - mac[3] = tmp >> 8; - tmp = read_eeprom(2); - mac[4] = tmp & 0xff; - mac[5] = tmp >> 8; - set_mac_address(mac); - } else { - VERIFY_NOT_REACHED(); - } + VERIFY(m_has_eeprom.was_set()); + + // 5.6.1 Ethernet Address (00h-02h) + MACAddress mac {}; + + u16 tmp = read_eeprom(0); + mac[0] = tmp & 0xff; + mac[1] = tmp >> 8; + + tmp = read_eeprom(1); + mac[2] = tmp & 0xff; + mac[3] = tmp >> 8; + + tmp = read_eeprom(2); + mac[4] = tmp & 0xff; + mac[5] = tmp >> 8; + + set_mac_address(mac); } UNMAP_AFTER_INIT void E1000NetworkAdapter::initialize_rx_descriptors() @@ -334,13 +279,24 @@ UNMAP_AFTER_INIT void E1000NetworkAdapter::initialize_rx_descriptors() descriptor.status = 0; } - out32(REG_RXDESCLO, m_rx_descriptors.paddr.get()); - out32(REG_RXDESCHI, 0); - out32(REG_RXDESCLEN, number_of_rx_descriptors * sizeof(RxDescriptor)); - out32(REG_RXDESCHEAD, 0); - out32(REG_RXDESCTAIL, number_of_rx_descriptors - 1); - - out32(REG_RCTRL, RCTL_EN | RCTL_SBP | RCTL_UPE | RCTL_MPE | RCTL_LBM_NONE | RTCL_RDMTS_HALF | RCTL_BAM | RCTL_SECRC | RCTL_BSIZE_8192); + m_registers.write(m_rx_descriptors.paddr.get() & 0xffffffff); + m_registers.write(m_rx_descriptors.paddr.get() >> 32); + m_registers.write(number_of_rx_descriptors * sizeof(RxDescriptor)); + m_registers.write(0); + m_registers.write(number_of_rx_descriptors - 1); + + E1000::ReceiveControl rctl; + rctl.enable = 1, + rctl.store_bad_frames = 1, + rctl.unicast_promiscuous_enable = 1, + rctl.multicast_promiscuous_enable = 1, + rctl.loopback_mode = E1000::LoopbackMode::None, + rctl.read_descriptor_minimum_threshold_size = E1000::ReceiveControl::FreeBufferThreshold::Half, + rctl.broadcast_accept_mode = 1, + rctl.buffer_size_extension = 1, + rctl.buffer_size = E1000::ReceiveControl::BufferSize::Size8192, + rctl.strip_ethernet_crc = 1, + m_registers.write(rctl); } UNMAP_AFTER_INIT void E1000NetworkAdapter::initialize_tx_descriptors() @@ -351,72 +307,60 @@ UNMAP_AFTER_INIT void E1000NetworkAdapter::initialize_tx_descriptors() auto& descriptor = m_tx_descriptors[i]; m_tx_buffers[i] = m_tx_buffer_region->vaddr().as_ptr() + tx_buffer_size * i; descriptor.addr = m_tx_buffer_region->physical_page(tx_buffer_page_count * i)->paddr().get(); - descriptor.cmd = 0; + descriptor.cmd = {}; } - out32(REG_TXDESCLO, m_tx_descriptors.paddr.get()); - out32(REG_TXDESCHI, 0); - out32(REG_TXDESCLEN, number_of_tx_descriptors * sizeof(TxDescriptor)); - out32(REG_TXDESCHEAD, 0); - out32(REG_TXDESCTAIL, 0); - - out32(REG_TCTRL, in32(REG_TCTRL) | TCTL_EN | TCTL_PSP); - out32(REG_TIPG, 0x0060200A); -} - -void E1000NetworkAdapter::out8(u16 address, u8 data) -{ - dbgln_if(E1000_DEBUG, "E1000: OUT8 {:#02x} @ {:#04x}", data, address); - m_registers_io_window->write8(address, data); -} - -void E1000NetworkAdapter::out16(u16 address, u16 data) -{ - dbgln_if(E1000_DEBUG, "E1000: OUT16 {:#04x} @ {:#04x}", data, address); - m_registers_io_window->write16(address, data); -} - -void E1000NetworkAdapter::out32(u16 address, u32 data) -{ - dbgln_if(E1000_DEBUG, "E1000: OUT32 {:#08x} @ {:#04x}", data, address); - m_registers_io_window->write32(address, data); + m_registers.write(m_tx_descriptors.paddr.get() & 0xffffffff); + m_registers.write(m_tx_descriptors.paddr.get() >> 32); + m_registers.write(number_of_tx_descriptors * sizeof(TxDescriptor)); + m_registers.write(0); + m_registers.write(0); + + E1000::TransmitControl tctl = m_registers.read(); + tctl.enable = 1; + tctl.pad_short_packets = 1; + m_registers.write(tctl); + + // FIXME: Do this properly + // IPGT:= 10 (8/6 for the 82544GC/EI) - IPG Transmit Time (10 bit) + // The 82544GC/EI has a different value for IPGT depending if it is in IEEE 802.3 mode or 10/100/1000BASE-T mode + // IPGR1:= 8 - IPG Receive Time 1 (half duplex only) ~= 2/3 of IPGR2 (10 bit) + // IPGR2:= 6 (+6) - IPG Receive Time 2 (half duplex only) (10 bit) + // Recommendation are for "IEEE 802.3 minimum IPG value of 96-bit time" + // The magic value here has: + // 10, 8, 6 so it should be fine for now + m_registers.write(0x0060200A); } -u8 E1000NetworkAdapter::in8(u16 address) +void E1000NetworkAdapter::send_raw(ReadonlyBytes payload) { - dbgln_if(E1000_DEBUG, "E1000: IN8 @ {:#04x}", address); - return m_registers_io_window->read8(address); -} + VERIFY(payload.size() <= 8192); -u16 E1000NetworkAdapter::in16(u16 address) -{ - dbgln_if(E1000_DEBUG, "E1000: IN16 @ {:#04x}", address); - return m_registers_io_window->read16(address); -} + disable_irq(); -u32 E1000NetworkAdapter::in32(u16 address) -{ - dbgln_if(E1000_DEBUG, "E1000: IN32 @ {:#04x}", address); - return m_registers_io_window->read32(address); -} + size_t tx_current = m_registers.read() % number_of_tx_descriptors; + [[maybe_unused]] auto tx_head = m_registers.read(); -void E1000NetworkAdapter::send_raw(ReadonlyBytes payload) -{ - disable_irq(); - size_t tx_current = in32(REG_TXDESCTAIL) % number_of_tx_descriptors; dbgln_if(E1000_DEBUG, "E1000: Sending packet ({} bytes)", payload.size()); + dbgln_if(E1000_DEBUG, "E1000: Using tx descriptor {} (head is at {})", tx_current, tx_head); auto& descriptor = m_tx_descriptors[tx_current]; - VERIFY(payload.size() <= 8192); - auto* vptr = (void*)m_tx_buffers[tx_current]; - memcpy(vptr, payload.data(), payload.size()); + + void* buffer = m_tx_buffers[tx_current]; + memcpy(buffer, payload.data(), payload.size()); + descriptor.length = payload.size(); descriptor.status = 0; - descriptor.cmd = CMD_EOP | CMD_IFCS | CMD_RS; - dbgln_if(E1000_DEBUG, "E1000: Using tx descriptor {} (head is at {})", tx_current, in32(REG_TXDESCHEAD)); + using enum E1000::TXCommand; + descriptor.cmd = EOP | IFCS | RS; // Single packet, insert FCS, report status + tx_current = (tx_current + 1) % number_of_tx_descriptors; + + // FIXME: This seems odd to me: + // We disable interrupts and then wait for and irq to happen... Processor::disable_interrupts(); enable_irq(); - out32(REG_TXDESCTAIL, tx_current); + + m_registers.write(tx_current); for (;;) { if (descriptor.status) { Processor::enable_interrupts(); @@ -424,6 +368,7 @@ void E1000NetworkAdapter::send_raw(ReadonlyBytes payload) } m_wait_queue.wait_forever("E1000NetworkAdapter"sv); } + // FIXME: This should probably do some error checking dbgln_if(E1000_DEBUG, "E1000: Sent packet, status is now {:#02x}!", (u8)descriptor.status); } @@ -431,17 +376,22 @@ void E1000NetworkAdapter::receive() { u32 rx_current; for (;;) { - rx_current = in32(REG_RXDESCTAIL) % number_of_rx_descriptors; + rx_current = m_registers.read() % number_of_rx_descriptors; rx_current = (rx_current + 1) % number_of_rx_descriptors; + if (!(m_rx_descriptors[rx_current].status & 1)) break; + auto* buffer = m_rx_buffers[rx_current]; u16 length = m_rx_descriptors[rx_current].length; + VERIFY(length <= 8192); + dbgln_if(E1000_DEBUG, "E1000: Received 1 packet @ {:p} ({} bytes)", buffer, length); did_receive({ buffer, length }); + m_rx_descriptors[rx_current].status = 0; - out32(REG_RXDESCTAIL, rx_current); + m_registers.write(rx_current); } } @@ -450,14 +400,15 @@ i32 E1000NetworkAdapter::link_speed() if (!link_up()) return NetworkAdapter::LINKSPEED_INVALID; - u32 speed = in32(REG_STATUS) & STATUS_SPEED; - switch (speed) { - case STATUS_SPEED_10MB: + auto status = m_registers.read(); + using enum E1000::LinkSpeed; + switch (status.speed) { + case Speed10M: return 10; - case STATUS_SPEED_100MB: + case Speed100M: return 100; - case STATUS_SPEED_1000MB1: - case STATUS_SPEED_1000MB2: + case Speed1000M_1: + case Speed1000M_2: return 1000; default: return NetworkAdapter::LINKSPEED_INVALID; @@ -466,8 +417,7 @@ i32 E1000NetworkAdapter::link_speed() bool E1000NetworkAdapter::link_full_duplex() { - u32 status = in32(REG_STATUS); - return !!(status & STATUS_FD); + auto status = m_registers.read(); + return status.full_duplex != 0; } - } diff --git a/Kernel/Net/Intel/E1000NetworkAdapter.h b/Kernel/Net/Intel/E1000NetworkAdapter.h index 3b3eeff8635575b..b3dcb576f495bb6 100644 --- a/Kernel/Net/Intel/E1000NetworkAdapter.h +++ b/Kernel/Net/Intel/E1000NetworkAdapter.h @@ -6,6 +6,8 @@ #pragma once +#include "E1000Registers.h" +#include #include #include #include @@ -54,7 +56,7 @@ class E1000NetworkAdapter : public NetworkAdapter uint64_t addr { 0 }; uint16_t length { 0 }; uint8_t cso { 0 }; - uint8_t cmd { 0 }; + E1000::TXCommand cmd { 0 }; uint8_t status { 0 }; uint8_t css { 0 }; uint16_t special { 0 }; @@ -70,29 +72,25 @@ class E1000NetworkAdapter : public NetworkAdapter Memory::TypedMapping rx_descriptors, Memory::TypedMapping tx_descriptors); + void check_quirks(); + virtual bool handle_irq() override; virtual StringView class_name() const override { return "E1000NetworkAdapter"sv; } virtual void detect_eeprom(); - virtual u32 read_eeprom(u8 address); + virtual u16 read_eeprom(u16 address); void read_mac_address(); void initialize_rx_descriptors(); void initialize_tx_descriptors(); - void out8(u16 address, u8); - void out16(u16 address, u16); - void out32(u16 address, u32); - u8 in8(u16 address); - u16 in16(u16 address); - u32 in32(u16 address); - void receive(); static constexpr size_t number_of_rx_descriptors = 256; static constexpr size_t number_of_tx_descriptors = 256; - NonnullOwnPtr m_registers_io_window; + using Register = E1000::Register; + E1000::RegisterMap m_registers; Memory::TypedMapping m_rx_descriptors; Memory::TypedMapping m_tx_descriptors; @@ -102,6 +100,7 @@ class E1000NetworkAdapter : public NetworkAdapter Array m_rx_buffers; Array m_tx_buffers; SetOnce m_has_eeprom; + SetOnce m_is_82541xx_82547GI_EI; // EEPROM logic for those is different bool m_link_up { false }; EntropySource m_entropy_source; diff --git a/Kernel/Net/Intel/E1000Registers.h b/Kernel/Net/Intel/E1000Registers.h new file mode 100644 index 000000000000000..976fa57ed09346a --- /dev/null +++ b/Kernel/Net/Intel/E1000Registers.h @@ -0,0 +1,329 @@ +/* + * Copyright (c) 2024, Leon Albrecht + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include + +namespace Kernel::E1000 { + +// 3.3.13 +enum class TXCommand : u8 { + EOP = 1 << 0, // End of Packet + IFCS = 1 << 1, // Insert FCS + IC = 1 << 2, // Insert Checksum + RS = 1 << 3, // Report Status + RPS = 1 << 4, // Report Packet Sent (82544GC/EI only) + DEXT = 1 << 5, // Descriptor Extension (Do not use) + VLE = 1 << 6, // VLAN Packet Enable + ID = 1 << 7, // Interrupt Delay Enable +}; +AK_ENUM_BITWISE_OPERATORS(TXCommand); + +// https://www.intel.com/content/dam/doc/manual/pci-pci-x-family-gbe-controllers-software-dev-manual.pdf +// Table 13-2 +enum class Register { + // FIXME: There are more registers, allowing some more HW acceleration, + // statistics and more. Support these! + Ctrl = 0x0000, + Status = 0x0008, + EEPROMControl = 0x0010, + EEPROMRead = 0x0014, + CtrlExt = 0x0018, + InterruptCauseR = 0x00C0, + InterruptThrottling = 0x00C4, + InterruptMask = 0x00D0, + InterruptMaskClear = 0x00D8, + RCtrl = 0x0100, + TCtrl = 0x0400, + TIPG = 0x0410, // Transmit Inter Packet Gap + RXDescLow = 0x2800, // Receive Descriptor Base Low (RDBAL) + RXDescHigh = 0x2804, // Receive Descriptor Base High (RDBAH) + RXDescLength = 0x2808, + RXDescHead = 0x2810, + RXDescTail = 0x2818, + RDTR = 0x2820, // RX Delay Timer Register + RADV = 0x282C, // RX Int. Absolute Delay Timer + RSRPD = 0x2C00, // RX Small Packet Detect Interrupt + TXDescLow = 0x3800, + TXDescHigh = 0x3804, + TXDescLength = 0x3808, + TXDescHead = 0x3810, + TXDescTail = 0x3818, + RXDCTL = 0x3828, // RX Descriptor Control +}; + +enum class LinkSpeed : u32 { + Speed10M = 0b00, + Speed100M = 0b01, + Speed1000M_1 = 0b10, + Speed1000M_2 = 0b11, +}; + +enum class LoopbackMode : u32 { + None = 0b00, + PHY = 0b11, +}; + +// 13.4.1 +// Table 13-3 +struct Ctrl { + + enum SDPIODirection : u32 { + Input = 0, + Output = 1, + }; + + u32 full_duplex : 1; + u32 : 2; + u32 link_reset : 1; + u32 : 1; + u32 auto_speed_detection : 1; + u32 set_link_up : 1; + u32 invert_loss_of_signal : 1; + LinkSpeed speed : 2; + u32 : 1; + u32 force_speed : 1; + u32 force_duplex : 1; + u32 : 5; + u32 sdp0_data_value : 1; + u32 sdp1_data_value : 1; + u32 d3_cold_wakeup_advertisement_enable : 1; + u32 enable_phy_power_management : 1; + SDPIODirection sdp0_direction : 1; + SDPIODirection sdp1_direction : 1; + u32 : 2; + u32 reset : 1; + u32 receive_flow_control_enable : 1; + u32 transmit_flow_control_enable : 1; + u32 : 1; + u32 vlan_mode : 1; + u32 phy_reset : 1; +}; +static_assert(AssertSize()); + +// 13.4.2 +// Table 13-5 +struct Status { + enum class PCIXSpeed : u32 { + Speed66MHz = 0b00, + Speed100MHz = 0b01, + Speed133MHz = 0b10, + Reserved = 0b11, + }; + + u32 full_duplex : 1; + u32 link_up : 1; + u32 function_id : 2; // 82546GB/EB only + u32 txoff : 1; + u32 tbi_mode : 1; + LinkSpeed speed : 2; + LinkSpeed auto_speed_detection : 2; + u32 : 1; + u32 pci_66mhz : 1; + u32 pci_64bit_bus : 1; + u32 pcix_mode : 1; + PCIXSpeed pcix_speed : 2; + u32 : 16; +}; +static_assert(AssertSize()); + +// 13.4.3 +// Table 13-6 +struct EEPROMControl { + enum class FlashWriteEnable : u32 { + Disabled = 0b01, + Enabled = 0b10, + }; + + u32 clock_input : 1; // SK + u32 chip_select : 1; // CS + u32 data_in : 1; // DI + u32 data_out : 1; // DO + FlashWriteEnable flash_write_enable : 2; // FW + // Not on 82544GC/EI: + u32 direct_eeprom_access_request : 1; // EE_REQ + u32 direct_eeprom_access_grant : 1; // EE_GNT + u32 eeprom_present : 1; // EE_PRES // always 0 on 82541xx and 82547GI/EI + // 82541xx and 82547GI/EI: + u32 eeprom_size : 2; // EE_SIZE + u32 : 2; + u32 eeprom_type : 1; // EE_TYPE + u32 : 18; +}; +static_assert(AssertSize()); + +// 13.4.4 +// Table 13-7 +union EEPROMRead { + struct { + u32 start : 1; + u32 : 3; + u32 done : 1; + u32 : 3; + u32 address : 8; + u32 data : 16; + }; + struct { + u32 start : 1 = 0; + u32 done : 1 = 0; + u32 address : 14 = 0; + u32 data : 16 = 0; + } variant_82541xx_or_82547GI_EI; + u32 raw; +}; + +// 13.4.17 +// Table 13-63 +union InterruptCauseRead { + struct { + u32 transmit_descriptor_written_back : 1; + u32 transmit_queue_empty : 1; + u32 link_status_change : 1; + u32 receive_sequence_error : 1; + u32 receive_descriptor_minimum_threshold_reached : 1; // RXDMT0 + u32 : 1; + u32 receiver_overrun : 1; // RXO + u32 receive_timer_interrupt : 1; // RXT0 + u32 : 1; + u32 mdio_access_complete : 1; + u32 receiving_c_ordered_sets : 1; + // The following may also be GP interrupts + u32 : 1; + u32 phy_interrupt : 1; + u32 general_purpose_interrupt_62 : 1; + u32 general_purpose_interrupt_73 : 1; + + u32 transmit_descriptor_low_threshold_hit : 1; + u32 small_receive_packet_detected : 1; + u32 : 15; + }; + u32 raw; +}; +static_assert(AssertSize()); + +// 13.4.20 +// Table 13-65 +enum class InterruptMask { + TXDW = 1 << 0, // Transmit Descriptor Written Back + TXQE = 1 << 1, // Transmit Queue Empty + LSC = 1 << 2, // Link Status Change + RXSEQ = 1 << 3, // Receive Sequence Error (not on 82541xx, 82547GI/EI) + RXDMT0 = 1 << 4, // Receive Descriptor Minimum Threshold hit + // 5 Reserved + RXO = 1 << 6, // Receiver FIFO Overrun + RXT0 = 1 << 7, // Receive Timer Interrupt + // 8 Reserved + MDAC = 1 << 9, // MDIO Access Complete + RXCFG = 1 << 10, // Receiving /C/ Ordered Sets + // + PHYINT = 1 << 12, // PHY Interrupt (not on 82544GI/EI) + // 11-12 General Purpose Interrupts (82544GI/EI only) + // otherwise Reserved + GPI1 = 1 << 13, + GPI2 = 1 << 14, + TXD_LOW = 1 << 15, // Transmit Descriptor Low Threshold Hit (not on 82544GC/EI) + SRPD = 1 << 16, // Small Receive Packet Detection (not on 82544GC/EI) + // 17-31 Reserved +}; +AK_ENUM_BITWISE_OPERATORS(InterruptMask); +// FIXME: This should be the same as the interrupt cause, which is nicer? + +// 13.4.22 +// Table 13-67 +struct ReceiveControl { + enum class FreeBufferThreshold : u32 { + Half = 0b00, + Quarter = 0b01, + Eighth = 0b10, + Reserved = 0b11, + }; + enum class BufferSize : u32 { + Size2048 = 0b00, + Size1024 = 0b01, + Size512 = 0b10, + Size256 = 0b11, + // With RCTL.BSEX set to 1 + // Do not use 0b00 + Size16384 = 0b01, + Size8192 = 0b10, + Size4096 = 0b11, + }; + + u32 : 1; + u32 enable : 1; + u32 store_bad_frames : 1; + u32 unicast_promiscuous_enable : 1; + u32 multicast_promiscuous_enable : 1; + u32 long_packet_enable : 1; + LoopbackMode loopback_mode : 2; + FreeBufferThreshold read_descriptor_minimum_threshold_size : 2; + u32 : 2; + u32 multicast_offset : 2; + u32 : 1; + u32 broadcast_accept_mode : 1; + BufferSize buffer_size : 2; + u32 vlan_filter_enable : 1; + u32 canonical_form_indicator_enable : 1; + u32 canonical_form_indicator_value : 1; + u32 : 1; + u32 discard_pause_frames : 1; + u32 pass_mac_control_frames : 1; + u32 : 1; + u32 buffer_size_extension : 1; // BSEX + u32 strip_ethernet_crc : 1; + u32 : 5; +}; +static_assert(AssertSize()); + +// 13.4.33 +// Table 13-76 +struct TransmitControl { + u32 : 1; + u32 enable : 1; + u32 : 1; + u32 pad_short_packets : 1; + u32 collision_threshold : 8; + u32 collision_distance : 10; + u32 software_xoff_transmit : 1; + u32 : 1; + u32 retransmit_on_late_collision : 1; + u32 no_retransmit_on_underrun : 1; // 82544GC/EI only + u32 : 6; +}; +static_assert(AssertSize()); + +using RegisterMap = IORegister, + IOReg, + IOReg, + IOReg, + + IOReg, + IOReg, + IOReg, + + IOReg, + + IOReg, + IOReg, + + IOReg, + IOReg, + IOReg, + IOReg, + IOReg, + + IOReg, + IOReg, + IOReg, + IOReg, + IOReg>; + +}