Kernel: Support hardware checksum offload on E1000 receive

This commit is contained in:
2026-06-29 00:26:10 +03:00
parent a6ad9b9b2e
commit ddb7641b05
2 changed files with 65 additions and 9 deletions

View File

@@ -29,6 +29,7 @@ namespace Kernel
REG_TDLEN = 0x3808, REG_TDLEN = 0x3808,
REG_TDH = 0x3810, REG_TDH = 0x3810,
REG_TDT = 0x3818, REG_TDT = 0x3818,
REG_RXCSUM = 0x5000,
REG_MTA = 0x5200, REG_MTA = 0x5200,
}; };
@@ -167,6 +168,15 @@ namespace Kernel
RCTL_BSIZE_16384 = (0b01 << 16) | RCTL_BSEX, RCTL_BSIZE_16384 = (0b01 << 16) | RCTL_BSEX,
}; };
enum E1000_RXCSUM : uint32_t
{
RXSUM_IPOFLD = 1 << 8,
RXSUM_TUOFLD = 1 << 9,
RXSUM_CRCOFLD = 1 << 11,
RXSUM_IPPCE = 1 << 12,
RXSUM_PCSD = 1 << 13,
};
enum E1000_CMD : uint8_t enum E1000_CMD : uint8_t
{ {
CMD_EOP = 1 << 0, CMD_EOP = 1 << 0,
@@ -178,6 +188,26 @@ namespace Kernel
CMD_IDE = 1 << 7, CMD_IDE = 1 << 7,
}; };
enum E1000_RX_STS : uint8_t
{
RX_STS_IPCS = 1 << 6,
RX_STS_TCPCS = 1 << 5,
RX_STS_UDPCS = 1 << 5,
RX_STS_EOP = 1 << 1,
RX_STS_DD = 1 << 0,
};
enum E1000_RX_ERR : uint8_t
{
RX_ERR_RXE = 1 << 7,
RX_ERR_IPE = 1 << 6,
RX_ERR_TCPE = 1 << 5,
RX_ERR_CXE = 1 << 4,
RX_ERR_SEQ = 1 << 2,
RX_ERR_SE = 1 << 1,
RX_ERR_CE = 1 << 0,
};
struct e1000_rx_desc struct e1000_rx_desc
{ {
uint64_t addr; uint64_t addr;

View File

@@ -202,6 +202,8 @@ namespace Kernel
rctrl |= RCTL_BSIZE_8192; rctrl |= RCTL_BSIZE_8192;
write32(REG_RCTL, rctrl); write32(REG_RCTL, rctrl);
write32(REG_RXCSUM, RXSUM_IPOFLD | RXSUM_TUOFLD);
return {}; return {};
} }
@@ -330,20 +332,44 @@ namespace Kernel
for (;;) for (;;)
{ {
auto& descriptor = reinterpret_cast<volatile e1000_rx_desc*>(m_rx_descriptor_region->vaddr())[rx_current]; auto& descriptor = reinterpret_cast<volatile e1000_rx_desc*>(m_rx_descriptor_region->vaddr())[rx_current];
if (!(descriptor.status & 1))
const auto status = descriptor.status;
if (!(status & RX_STS_DD))
break; break;
ASSERT(descriptor.length <= E1000_RX_BUFFER_SIZE);
dprintln_if(DEBUG_E1000, "got {} bytes", (uint16_t)descriptor.length);
const auto errors = descriptor.errors;
if (!(status & RX_STS_EOP))
dwarnln("multi descriptor packet??");
else if (errors & (RX_ERR_CE | RX_ERR_SE | RX_ERR_RXE))
dwarnln("descriptor error {2h}", errors);
else if ((status & RX_STS_IPCS) && (errors & RX_ERR_IPE))
dwarnln("IPv4 checksum error");
else if ((status & RX_STS_TCPCS) && (errors & RX_ERR_TCPE))
dwarnln("TCP checkum error");
else if ((status & RX_STS_UDPCS) && (errors & RX_ERR_TCPE))
dwarnln("UDP checksum error");
else
{
m_rx_lock.unlock(InterruptState::Enabled); m_rx_lock.unlock(InterruptState::Enabled);
NetworkManager::get().on_receive(*this, BAN::ConstByteSpan { const uint32_t packet_length = descriptor.length;
reinterpret_cast<const uint8_t*>(m_rx_buffer_region->vaddr() + rx_current * E1000_RX_BUFFER_SIZE), ASSERT(packet_length <= E1000_RX_BUFFER_SIZE);
descriptor.length
}, 0); dprintln_if(DEBUG_E1000, "got {} bytes", packet_length);
uint32_t validated_cksums = 0;
if ((status & RX_STS_IPCS) && !(errors & RX_ERR_IPE))
validated_cksums |= CKSUM_IPV4;
if ((status & RX_STS_TCPCS) && !(errors & RX_ERR_TCPE))
validated_cksums |= CKSUM_TCP;
if ((status & RX_STS_UDPCS) && !(errors & RX_ERR_TCPE))
validated_cksums |= CKSUM_UDP;
const uint8_t* packet_data = reinterpret_cast<const uint8_t*>(m_rx_buffer_region->vaddr() + rx_current * E1000_RX_BUFFER_SIZE);
NetworkManager::get().on_receive(*this, BAN::ConstByteSpan { packet_data, packet_length }, validated_cksums);
m_rx_lock.lock(); m_rx_lock.lock();
}
descriptor.status = 0; descriptor.status = 0;