diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index ae407f18..b9c27ede 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -171,7 +171,10 @@ SET(KERNEL_SRCS syscall/uname.cpp VMWare.cpp Processor.cpp - StackWalker.cpp) + StackWalker.cpp + net/NetworkAdapter.cpp + net/E1000Adapter.cpp + net/NetworkManager.cpp) add_custom_command( OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/generated/duckos_version.h" diff --git a/kernel/IO.cpp b/kernel/IO.cpp index 9e93c7e6..826c46e3 100644 --- a/kernel/IO.cpp +++ b/kernel/IO.cpp @@ -18,6 +18,8 @@ */ #include "IO.h" +#include "kstd/kstdio.h" +#include "memory/MemoryManager.h" void IO::wait() { asm volatile ( "jmp 1f\n\t" @@ -53,4 +55,29 @@ uint32_t IO::inl(uint16_t port){ uint32_t ret; asm volatile ("inl %1, %0" : "=a" (ret) : "dN" (port)); return ret; -} \ No newline at end of file +} + +IO::Window::Window(PCI::Address addr, uint8_t bar) { + auto bar_val = PCI::read_dword(addr, bar); + if (!(bar_val & 0x1u)) { + // Memory IO + auto type = (bar_val >> 1) & 0x3u; + m_prefetchable = bar_val & 0x8u; + switch (type) { + case 0: + m_type = Mem32; + m_addr = bar_val & ~0xFu; + break; + default: + ASSERT(false); + } + + PCI::write_dword(addr, bar, 0xFFFFFFFF); + m_size = ~(PCI::read_dword(addr, bar) & (~0xfull)) + 1; + PCI::write_dword(addr, bar, bar_val); + m_vm_region = MM.alloc_mapped_region(m_addr, m_size); + } else { + m_type = IOSpace; + m_addr = bar_val & ~0x3u; + } +} diff --git a/kernel/IO.h b/kernel/IO.h index 542033c7..7cb174ae 100644 --- a/kernel/IO.h +++ b/kernel/IO.h @@ -20,6 +20,9 @@ #pragma once #include +#include "pci/PCI.h" +#include "kstd/Arc.h" +#include "memory/VMRegion.h" namespace IO { void wait(); @@ -33,6 +36,63 @@ namespace IO { while(us--) inb(0x80); } + + class Window { + public: + enum Type { + Invalid, Mem16, Mem32, Mem64, IOSpace + }; + + Window() = default; + Window(PCI::Address addr, uint8_t bar); + + template + T in(size_t offset) { + ASSERT(m_type != Invalid); + if (m_type == Type::IOSpace) { + if constexpr(sizeof(T) == 1) + return inb(m_addr + offset); + else if constexpr(sizeof(T) == 2) + return inw(m_addr + offset); + else if constexpr(sizeof(T) == 4) + return inl(m_addr + offset); + static_assert(sizeof(T) <= 4 && sizeof(T) != 3); + } else { + return *((T*) (m_vm_region->start() + offset)); + } + } + + uint8_t in8(size_t offset) { return in(offset); } + uint16_t in16(size_t offset) { return in(offset); } + uint32_t in32(size_t offset) { return in(offset); } + + template + void out(size_t offset, T& data) { + ASSERT(m_type != Invalid); + if (m_type == Type::IOSpace) { + if constexpr(sizeof(T) == 1) + outb(m_addr + offset, data); + else if constexpr(sizeof(T) == 2) + outw(m_addr + offset, data); + else if constexpr(sizeof(T) == 4) + outl(m_addr + offset, data); + static_assert(sizeof(T) <= 4 && sizeof(T) != 3); + } else { + *((T*) (m_vm_region->start() + offset)) = data; + } + } + + void out8(size_t offset, uint8_t val) { return out(offset, val); } + void out16(size_t offset, uint16_t val) { return out(offset, val); } + void out32(size_t offset, uint32_t val) { return out(offset, val); } + + private: + Type m_type = Invalid; + size_t m_size = 0; + size_t m_addr = 0; + kstd::Arc m_vm_region; + bool m_prefetchable = false; + }; }; diff --git a/kernel/api/endian.h b/kernel/api/endian.h new file mode 100644 index 00000000..96102061 --- /dev/null +++ b/kernel/api/endian.h @@ -0,0 +1,84 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* Copyright © 2016-2024 Byteduck */ + +#pragma once + +#include "stdint.h" + +#ifdef __cplusplus + +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +constexpr static bool __little_endian = true; +#else +constexpr static bool __little_endian = false; +#endif + +template +inline constexpr T swap_endianness(T val) { + if constexpr(sizeof(T) == 1) + return val; + else if constexpr (sizeof(T) == 2) + return static_cast(__builtin_bswap16(static_cast(val))); + else if constexpr (sizeof(T) == 4) + return static_cast(__builtin_bswap32(static_cast(val))); + else if constexpr (sizeof(T) == 8) + return static_cast(__builtin_bswap64(static_cast(val))); + else + static_assert("Cannot swap endianness of anything larger than 8 bytes"); +} + +template +inline constexpr T as_little_endian(T val) { + if constexpr(__little_endian) + return val; + else + return swap_endianness(val); +} + +template +inline constexpr T as_big_endian(T val) { + if constexpr(!__little_endian) + return val; + else + return swap_endianness(val); +} + +template +inline constexpr T from_little_endian(T val) { + if constexpr(__little_endian) + return val; + else + return swap_endianness(val); +} + +template +inline constexpr T from_big_endian(T val) { + if constexpr(!__little_endian) + return val; + else + return swap_endianness(val); +} + +template +class LittleEndian { +public: + constexpr LittleEndian() = default; + constexpr LittleEndian(T val): m_val(as_little_endian(val)) {} + constexpr operator T() const { return from_little_endian(m_val); } + constexpr T val() const { return operator T(); } +private: + T m_val; +} __attribute__((packed)); + +template +class BigEndian { +public: + constexpr BigEndian() = default; + constexpr BigEndian(T val): m_val(as_big_endian(val)) {} + constexpr operator T() const { return from_big_endian(m_val); } + constexpr T val() const { return operator T(); } +private: + T m_val; +} __attribute__((packed)); + +#endif \ No newline at end of file diff --git a/kernel/api/ipv4.h b/kernel/api/ipv4.h new file mode 100644 index 00000000..3651853c --- /dev/null +++ b/kernel/api/ipv4.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* Copyright © 2016-2024 Byteduck */ + +#pragma once + +#include "types.h" +#include "endian.h" + +#ifdef __cplusplus +class __attribute__((packed)) IPv4Address { +public: + constexpr IPv4Address() = default; + + constexpr IPv4Address(uint8_t a, uint8_t b, uint8_t c, uint8_t d) { + m_data[0] = a; + m_data[1] = b; + m_data[2] = c; + m_data[3] = d; + } + + inline constexpr uint8_t operator[](int idx) const { + return m_data[idx]; + } + +private: + uint8_t m_data[4]; +}; + +#define IPV4_ARGS(addr) (addr)[0], (addr)[1], (addr)[2], (addr)[3] + +struct __attribute__((packed)) IPv4Packet { + uint8_t version_ihl = 0; + uint8_t dscp_ecn = 0; + BigEndian length; + BigEndian identification; + BigEndian flags_fragment_offset; + uint8_t ttl = 0; + uint8_t proto; + BigEndian checksum; + IPv4Address source_addr; + IPv4Address dest_addr; + uint8_t payload[]; +}; + +static_assert(sizeof(IPv4Packet) == 20); + +enum IPv4Proto { + ICMP = 0x1, + TCP = 0x06, + UDP = 0x11 +}; + +#endif + +__DECL_BEGIN +__DECL_END \ No newline at end of file diff --git a/kernel/api/net.h b/kernel/api/net.h new file mode 100644 index 00000000..324dc51d --- /dev/null +++ b/kernel/api/net.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* Copyright © 2016-2024 Byteduck */ + +#pragma once + +#include "ipv4.h" + +#define IFNAMESIZ 16 + +#ifdef __cplusplus + +class __attribute__((packed)) MACAddress { +public: + MACAddress() = default; + MACAddress(uint8_t a, uint8_t b, uint8_t c, uint8_t d, uint8_t e, uint8_t f) { + m_data[0] = a; + m_data[1] = b; + m_data[2] = c; + m_data[3] = d; + m_data[4] = e; + m_data[5] = f; + } + + uint8_t operator[](size_t index) { + return m_data[index]; + } +private: + uint8_t m_data[6] = {0}; +}; + +#define MAC_ARGS(addr) (addr)[0], (addr)[1], (addr)[2], (addr)[3], (addr)[4], (addr)[5] + +#endif \ No newline at end of file diff --git a/kernel/kmain.cpp b/kernel/kmain.cpp index d3de33b4..509166b7 100644 --- a/kernel/kmain.cpp +++ b/kernel/kmain.cpp @@ -48,6 +48,7 @@ #include #include "bootlogo.h" #include "Processor.h" +#include "net/NetworkAdapter.h" uint8_t boot_disk; @@ -225,6 +226,9 @@ void kmain_late(){ //Try initializing the sound card auto dev = AC97Device::detect(); + //Try initializing network + NetworkAdapter::setup(); + //If we're running tests, do so if(CommandLine::inst().get_option_value("kernel-tests") == "true") { KernelTestRegistry::inst().run_tests(); diff --git a/kernel/net/ARP.h b/kernel/net/ARP.h new file mode 100644 index 00000000..9ec02bf9 --- /dev/null +++ b/kernel/net/ARP.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* Copyright © 2016-2024 Byteduck */ + +#pragma once + +#include "../api/endian.h" +#include "../api/net.h" + +enum ARPHWType { + Ethernet = 1 +}; + +enum ARPOp { + Req = 1, + Resp = 2 +}; + +enum EtherProto { + IPv4 = 0x0800, + ARP = 0x0806, + IPv6 = 0x86DD +}; + +struct ARPPacket { + BigEndian hardware_type {ARPHWType::Ethernet}; + BigEndian protocol_type { EtherProto::IPv4 }; + BigEndian hwaddr_len { sizeof(MACAddress) }; + BigEndian protoaddr_len { sizeof(IPv4Address) }; + BigEndian operation; + MACAddress sender_hwaddr; + IPv4Address sender_protoaddr; + MACAddress target_hwaddr; + IPv4Address target_protoaddr; +} __attribute__((packed)); + +static_assert(sizeof(ARPPacket) == 28); \ No newline at end of file diff --git a/kernel/net/E1000Adapter.cpp b/kernel/net/E1000Adapter.cpp new file mode 100644 index 00000000..2c739293 --- /dev/null +++ b/kernel/net/E1000Adapter.cpp @@ -0,0 +1,298 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* Copyright © 2016-2024 Byteduck */ + +#include "E1000Adapter.h" +#include "../IO.h" +#include "../kstd/KLog.h" +#include "../memory/MemoryManager.h" + +// https://wiki.osdev.org/Intel_Ethernet_i217 + +#define INTEL_VEND 0x8086 // Vendor ID for Intel +#define E1000_DEV 0x100E // Device ID for the e1000 Qemu, Bochs, and VirtualBox emmulated NICs + +#define REG_CTRL 0x0000 +#define REG_STATUS 0x0008 +#define REG_EEPROM 0x0014 +#define REG_CTRL_EXT 0x0018 +#define REG_ICAUSE 0x00C0 +#define REG_ITHROTTLE 0x00C4 +#define REG_ISET 0x00C8 +#define REG_IMASK 0x00D0 +#define REG_ICLEAR 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 0x2828 // 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 +#define STATUS_LINKUP 0x02 + +// Interrupts +#define INT_TXDW 0x01 // Transmit descriptor written back +#define INT_TXQE 0x02 // Transmit queue empty +#define INT_LSC 0x04 // Link status change +#define INT_RXSEQ 0x08 // Receive Sequence Error +#define INT_RXDMT0 0x10 // Receive Descriptor Minimum Threshold Reached +#define INT_RXO 0x40 // Receiver Overrun +#define INT_RXT0 0x80 // Receiver Timer Interrupt + +#define E1000_DBG true + +void E1000Adapter::probe() { + PCI::enumerate_devices([](PCI::Address address, PCI::ID id, uint16_t type, void* dataPtr) { + if(id.vendor == INTEL_VEND && id.device == E1000_DEV) { + new E1000Adapter(address); + } + }, nullptr); + +} + +E1000Adapter::E1000Adapter(PCI::Address addr): NetworkAdapter("en0"), m_pci_address(addr) { + PCI::enable_bus_mastering(addr); + m_window = IO::Window(addr, PCI_BAR0); + detect_eeprom(); + get_mac_addr(); + init_rx(); + init_tx(); + init_link(); + init_irq(); +} + +bool E1000Adapter::detect_eeprom() { + m_window.out32(REG_EEPROM, 0x1); + for (int i = 0; i < 1000; i++) { + auto data = m_window.in32(REG_EEPROM); + if (data & 0x10) { + m_eeprom = true; + return true; + } + } + m_eeprom = false; + return false; +} + +void E1000Adapter::get_mac_addr() { + ASSERT(m_eeprom); + uint32_t a = eeprom_read(0); + uint32_t b = eeprom_read(1); + uint32_t c = eeprom_read(2); + MACAddress addr = {(uint8_t) (a & 0xff), (uint8_t) (a >> 8), + (uint8_t) (b & 0xff), (uint8_t) (b >> 8), + (uint8_t) (c & 0xff), (uint8_t) (c >> 8)}; + KLog::dbg_if("E1000", "%s MAC Address: %x:%x:%x:%x:%x:%x", name().c_str(), (int) addr[0], (int) addr[1], (int) addr[2], (int) addr[3], (int) addr[4], (int) addr[5], (int) addr[6]); + set_mac(addr); +} + +uint32_t E1000Adapter::eeprom_read(uint8_t addr) { + uint32_t data; + if (m_eeprom) { + m_window.out32(REG_EEPROM, 1 | ((uint32_t) addr << 8)); + while(!((data = m_window.in32(REG_EEPROM)) & 0b10000)) + IO::wait(); + } else { + m_window.out32(REG_EEPROM, 1 | ((uint32_t) addr << 2)); + while(!((data = m_window.in32(REG_EEPROM)) & 0b10)) + IO::wait(); + } + return (data >> 16) & 0xffff; +} + +void E1000Adapter::init_rx() { + m_rx_desc_region = MM.alloc_dma_region(sizeof(RxDesc) * num_rx_descriptors); + m_rx_buffer_region = MM.alloc_dma_region(rx_buffer_size * num_rx_descriptors); + auto* descriptors = (RxDesc*) m_rx_desc_region->start(); + auto first_buffer_page = m_rx_buffer_region->object()->physical_page(0).paddr(); + for (int i = 0; i < num_rx_descriptors; i++) { + // Region should be contiguous in physical pages... + descriptors[i].addr = first_buffer_page + (rx_buffer_size * i); + descriptors[i].status = 0; + } + auto desc_paddr = m_rx_desc_region->object()->physical_page(0).paddr(); + m_window.out32(REG_RXDESCLO, desc_paddr); + if constexpr (sizeof(PhysicalAddress) > 4) /* 64-bit proofing :) */ + m_window.out32(REG_RXDESCHI, desc_paddr >> 32); + else + m_window.out32(REG_TXDESCHI, 0); + m_window.out32(REG_RXDESCLEN, num_rx_descriptors * sizeof(RxDesc)); + m_window.out32(REG_RXDESCHEAD, 0); + m_window.out32(REG_RXDESCTAIL, num_rx_descriptors - 1); + m_window.out32(REG_RCTRL, RCTL_EN | RCTL_SBP | RCTL_UPE | RCTL_MPE | RCTL_LBM_NONE | RTCL_RDMTS_HALF | RCTL_BAM | RCTL_SECRC | RCTL_BSIZE_8192); +} + +void E1000Adapter::init_tx() { + m_tx_desc_region = MM.alloc_dma_region(sizeof(TxDesc) * num_tx_descriptors); + m_tx_buffer_region = MM.alloc_dma_region(tx_buffer_size * num_tx_descriptors); + auto* descriptors = (TxDesc*) m_tx_desc_region->start(); + auto first_buffer_page = m_tx_buffer_region->object()->physical_page(0).paddr(); + for (int i = 0; i < num_tx_descriptors; i++) { + // Region should be contiguous in physical pages... + descriptors[i].addr = first_buffer_page + (tx_buffer_size * i); + descriptors[i].cmd = 0; + descriptors[i].status = TSTA_DD; + } + + auto desc_paddr = m_tx_desc_region->object()->physical_page(0).paddr(); + m_window.out32(REG_TXDESCLO, desc_paddr); + if constexpr (sizeof(PhysicalAddress) > 4) /* 64-bit proofing :) */ + m_window.out32(REG_TXDESCHI, desc_paddr >> 32); + else + m_window.out32(REG_TXDESCHI, 0); + m_window.out32(REG_TXDESCLEN, num_tx_descriptors * sizeof(TxDesc)); + m_window.out32(REG_TXDESCHEAD, 0); + m_window.out32(REG_TXDESCTAIL, 0); + m_window.out32(REG_TCTRL, m_window.in32(REG_TCTRL) | TCTL_PSP | TCTL_EN); + m_window.out32(REG_TIPG, 0x0060200A); +} + +void E1000Adapter::handle_irq(IRQRegisters* regs) { + auto cause = m_window.in32(REG_ICAUSE); + if (!cause) + return; + + if (cause & INT_LSC) { + init_link(); + } + + if (cause & INT_RXO) { + KLog::warn_if("E1000", "RX buffer overrun"); + } + + if (cause & INT_RXT0) { + receive(); + } +} + +void E1000Adapter::init_irq() { + int irq = PCI::read_byte(m_pci_address, PCI_INTERRUPT_LINE); + set_irq(irq); + reinstall_irq(); + KLog::dbg_if("E1000", "%s Interrupt line: %d", name().c_str(), irq); + + m_window.out32(REG_ITHROTTLE, 5580); + m_window.out32(REG_IMASK, INT_LSC | INT_RXT0 | INT_RXO); + m_window.in32(REG_ICAUSE); + PCI::enable_interrupt(m_pci_address); +} + +void E1000Adapter::init_link() { + m_window.out32(REG_CTRL, m_window.in32(REG_CTRL) | ECTRL_SLU); + m_link = m_window.in32(REG_STATUS) & STATUS_LINKUP; + KLog::dbg_if("E1000", "Link state: %s", m_link ? "UP" : "DOWN"); +} + +void E1000Adapter::receive() { + auto* descs = (RxDesc*) m_rx_desc_region->start(); + while (true) { + auto cur_desc = (m_window.in32(REG_RXDESCTAIL) + 1) % num_rx_descriptors; + auto& desc = descs[cur_desc]; + if (!(desc.status & 1)) + break; // No more packets! + ASSERT(desc.length <= rx_buffer_size); + KLog::dbg_if("E1000", "Received packet (%d bytes)", desc.length); + desc.status = 0; + { + TaskManager::ScopedCritical crit; + receive_bytes(KernelPointer((uint8_t*) (m_rx_buffer_region->start() + (rx_buffer_size * cur_desc))), desc.length); + } + m_window.out32(REG_RXDESCTAIL, cur_desc); + } +} + +void E1000Adapter::send_bytes(SafePointer bytes, size_t count) { + ASSERT(count <= tx_buffer_size); + PCI::disable_interrupt(m_pci_address); + auto cur_tx_desc = m_window.in32(REG_TXDESCTAIL) % num_tx_descriptors; + auto& desc = ((TxDesc*) m_tx_desc_region->start())[cur_tx_desc]; + auto* buf = (uint8_t*) (m_tx_buffer_region->start() + (tx_buffer_size * cur_tx_desc)); + bytes.read(buf, count); + desc.status = 0; + desc.length = count; + desc.cmd = CMD_EOP | CMD_IFCS | CMD_RS; + KLog::dbg_if("E1000", "Sending packet (%d bytes)", desc.length); + TaskManager::enter_critical(); + PCI::enable_interrupt(m_pci_address); + m_window.out32(REG_TXDESCTAIL, (cur_tx_desc + 1) % num_tx_descriptors); + while (true) { + if (desc.status) { + TaskManager::leave_critical(); + break; + } + // Do we need to spin...? + } +} diff --git a/kernel/net/E1000Adapter.h b/kernel/net/E1000Adapter.h new file mode 100644 index 00000000..efecea01 --- /dev/null +++ b/kernel/net/E1000Adapter.h @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* Copyright © 2016-2024 Byteduck */ + +#pragma once + +#include "NetworkAdapter.h" +#include "../pci/PCI.h" +#include "../memory/VMRegion.h" +#include "../IO.h" +#include "../interrupt/IRQHandler.h" + +class E1000Adapter: public NetworkAdapter, IRQHandler { +public: + static void probe(); + +protected: + void handle_irq(IRQRegisters *regs) override; + void send_bytes(SafePointer bytes, size_t count) override; + +private: + explicit E1000Adapter(PCI::Address addr); + + struct RxDesc { + volatile uint64_t addr; + volatile uint16_t length; + volatile uint16_t checksum; + volatile uint8_t status; + volatile uint8_t errors; + volatile uint16_t special; + } __attribute__((packed)); + + struct TxDesc { + volatile uint64_t addr; + volatile uint16_t length; + volatile uint8_t cso; + volatile uint8_t cmd; + volatile uint8_t status; + volatile uint8_t css; + volatile uint16_t special; + } __attribute__((packed)); + + bool detect_eeprom(); + void get_mac_addr(); + void init_rx(); + void init_tx(); + void init_irq(); + void init_link(); + void receive(); + + uint32_t eeprom_read(uint8_t addr); + + PCI::Address m_pci_address; + bool m_eeprom = false; + IO::Window m_window; + kstd::Arc m_rx_desc_region; + kstd::Arc m_rx_buffer_region; + kstd::Arc m_tx_desc_region; + kstd::Arc m_tx_buffer_region; + bool m_link = false; + + static constexpr size_t num_rx_descriptors = 32; + static constexpr size_t num_tx_descriptors = 16; + static constexpr size_t rx_buffer_size = 8192; + static constexpr size_t tx_buffer_size = 8192; +}; diff --git a/kernel/net/ICMP.h b/kernel/net/ICMP.h new file mode 100644 index 00000000..5d4ca554 --- /dev/null +++ b/kernel/net/ICMP.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* Copyright © 2016-2024 Byteduck */ + +#pragma once + +enum ICMPType { + EchoReply = 0, + DestinationUnreachable = 3, + SourceQuench = 4, + RedirectMessage = 5, + EchoRequest = 8, + RouterAdvertisement = 9, + RouterSolicitation = 10, + TimeExceeded = 11, + BadIP = 12, + Timestamp = 13, + TimestampReply = 14, + InforationRequest = 15, + InformationReply = 16, + AddressMaskRequest = 17, + AddressMaskReply = 18, + Traceroute = 30, + ExtendedEchoRequest = 42, + ExtendedEchoReply = 43 +}; + +struct ICMPHeader { + uint8_t type; + uint8_t code; + BigEndian checksum; +} __attribute__((packed)); + +struct ICMPEchoPacket { + ICMPHeader header; + BigEndian id; + BigEndian sequence_num; +} __attribute__((packed)); \ No newline at end of file diff --git a/kernel/net/NetworkAdapter.cpp b/kernel/net/NetworkAdapter.cpp new file mode 100644 index 00000000..949f721e --- /dev/null +++ b/kernel/net/NetworkAdapter.cpp @@ -0,0 +1,98 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* Copyright © 2016-2024 Byteduck */ + +#include "NetworkAdapter.h" +#include "../api/errno.h" +#include "../kstd/KLog.h" +#include "E1000Adapter.h" +#include "NetworkManager.h" + +kstd::vector NetworkAdapter::s_interfaces; + +NetworkAdapter::NetworkAdapter(kstd::string name): + m_name(kstd::move(name)) +{ + KLog::dbg("NetworkAdapter", "Registered network adapter %s", m_name.c_str()); + s_interfaces.push_back(this); +} + +const kstd::string& NetworkAdapter::name() const { + return m_name; +} + +void NetworkAdapter::set_mac(MACAddress addr) { + m_mac_addr = addr; +} + +void NetworkAdapter::set_ipv4(IPv4Address addr) { + m_ipv4_addr = addr; +} + +void NetworkAdapter::setup() { + E1000Adapter::probe(); +} + +ResultRet NetworkAdapter::get_interface(const kstd::string& name) { + for(auto interface : s_interfaces) { + if (interface->name() == name) + return interface; + } + return Result(ENOENT); +} + +const kstd::vector& NetworkAdapter::interfaces() { + return s_interfaces; +} + +void NetworkAdapter::receive_bytes(SafePointer bytes, size_t count) { + ASSERT(count <= sizeof(Packet::buffer)); + + int i; + for (i = 0; i < 32; i++) { + if (!m_packets[i].used) + break; + } + if (i == 32) { + KLog::warn("NetworkAdapter", "%s had to drop packet, no more space in buffer!", name().c_str()); + return; + } + + if (!m_packet_queue) { + m_packet_queue = &m_packets[i]; + } else { + auto* prev_packet = m_packet_queue; + while (prev_packet->next) + prev_packet = prev_packet->next; + prev_packet->next = &m_packets[i]; + } + + bytes.read(m_packets[i].buffer, count); + m_packets[i].size = count; + m_packets[i].next = nullptr; + + NetworkManager::inst().wakeup(); +} + +void NetworkAdapter::send_arp_packet(MACAddress dest, const ARPPacket& packet) { + // TODO: We probably don't wanna use the stack here... + uint8_t buf[sizeof(FrameHeader) + sizeof(ARPPacket)]; + auto* hdr = (FrameHeader*) buf; + hdr->source = m_mac_addr; + hdr->destination = dest; + hdr->type = {EtherProto::ARP}; + memcpy(hdr->payload, &packet, sizeof(ARPPacket)); + send_raw_packet(KernelPointer(buf), sizeof(FrameHeader) + sizeof(ARPPacket)); +} + +void NetworkAdapter::send_raw_packet(SafePointer bytes, size_t count) { + send_bytes(bytes, count); +} + +NetworkAdapter::Packet* NetworkAdapter::dequeue_packet() { + TaskManager::ScopedCritical crit; + if (!m_packet_queue) + return nullptr; + auto* pkt = m_packet_queue; + m_packet_queue = m_packet_queue->next; + return pkt; +} diff --git a/kernel/net/NetworkAdapter.h b/kernel/net/NetworkAdapter.h new file mode 100644 index 00000000..b5c203e6 --- /dev/null +++ b/kernel/net/NetworkAdapter.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* Copyright © 2016-2024 Byteduck */ + +#pragma once + +#include "../api/net.h" +#include "../kstd/string.h" +#include "../kstd/vector.hpp" +#include "../Result.hpp" +#include "../memory/SafePointer.h" +#include "ARP.h" +class NetworkAdapter { +public: + virtual ~NetworkAdapter() = default; + static void setup(); + + struct Packet { + uint8_t buffer[8192]; /* TODO: We need non-constant packet sizes... */ + union { + size_t size; + bool used = false; + }; + Packet* next = nullptr; + }; + + struct FrameHeader { + MACAddress destination; + MACAddress source; + BigEndian type; + uint32_t payload[]; + } __attribute__((packed)); + + void send_arp_packet(MACAddress dest, const ARPPacket& packet); + void send_raw_packet(SafePointer bytes, size_t count); + Packet* dequeue_packet(); + + [[nodiscard]] IPv4Address ipv4_address() const { return m_ipv4_addr; } + [[nodiscard]] MACAddress mac_address() const { return m_mac_addr; } + + static ResultRet get_interface(const kstd::string& name); + static const kstd::vector& interfaces(); + +protected: + explicit NetworkAdapter(kstd::string name); + + const kstd::string& name() const; + + void set_mac(MACAddress addr); + void set_ipv4(IPv4Address addr); + + virtual void send_bytes(SafePointer bytes, size_t count) = 0; + void receive_bytes(SafePointer bytes, size_t count); + +private: + static kstd::vector s_interfaces; + + kstd::string m_name; + IPv4Address m_ipv4_addr {10, 0, 2, 15}; + MACAddress m_mac_addr; + Packet* m_packet_queue = nullptr; + Packet m_packets[32]; +}; diff --git a/kernel/net/NetworkManager.cpp b/kernel/net/NetworkManager.cpp new file mode 100644 index 00000000..807c48a6 --- /dev/null +++ b/kernel/net/NetworkManager.cpp @@ -0,0 +1,118 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* Copyright © 2016-2024 Byteduck */ + +#include "NetworkManager.h" +#include "../kstd/KLog.h" +#include "ICMP.h" + +#define ARP_DEBUG 1 + +NetworkManager* NetworkManager::s_inst = nullptr; + +NetworkManager& NetworkManager::inst() { + if (__builtin_expect(!s_inst, false)) + s_inst = new NetworkManager(); + return *s_inst; +} + +void NetworkManager::task_entry() { + NetworkManager::inst().do_task(); +} + +void NetworkManager::do_task() { + while (true) { + /* Block until we get a packet */ + TaskManager::current_thread()->block(m_blocker); + m_blocker.set_ready(false); + + NetworkAdapter::Packet* packet; + for (auto& iface : NetworkAdapter::interfaces()) { + while ((packet = iface->dequeue_packet())) { + handle_packet(iface, packet); + packet->used = false; + } + } + } +} + +void NetworkManager::wakeup() { + m_blocker.set_ready(true); +} + +void NetworkManager::handle_packet(NetworkAdapter* adapter, NetworkAdapter::Packet* packet) { + ASSERT(packet->size >= sizeof(NetworkAdapter::FrameHeader)); + auto* hdr = (NetworkAdapter::FrameHeader*) packet->buffer; + switch (hdr->type) { + case EtherProto::ARP: + handle_arp(adapter, packet); + break; + case EtherProto::IPv4: + handle_ipv4(adapter, packet); + break; + case EtherProto::IPv6: + KLog::warn("NetworkManager", "Got IPv6 packet, can't handle this!"); + break; + default: + KLog::warn("NetworkManager", "Unknown packet of type %d!", hdr->type); + break; + } +} + +void NetworkManager::handle_arp(NetworkAdapter* adapter, const NetworkAdapter::Packet* raw_packet) { + if (raw_packet->size < (sizeof(NetworkAdapter::FrameHeader) + sizeof(ARPPacket))) { + KLog::warn("NetworkManager", "Got IPv4 packet with invalid frame size!"); + } + + auto& packet = *((ARPPacket*) ((NetworkAdapter::FrameHeader* ) raw_packet->buffer)->payload); + + switch (packet.operation) { + case ARPOp::Req: { + KLog::dbg_if("NetworkManager", "Got ARP request from %d.%d.%d.%d (%x:%x:%x:%x:%x:%x)", IPV4_ARGS(packet.sender_protoaddr), MAC_ARGS(packet.sender_hwaddr)); + + ARPPacket resp; + resp.operation = ARPOp::Resp; + resp.sender_hwaddr = adapter->mac_address(); + resp.sender_protoaddr = adapter->ipv4_address(); + resp.target_protoaddr = packet.sender_protoaddr; + resp.target_hwaddr = packet.sender_hwaddr; + adapter->send_arp_packet(packet.sender_hwaddr, resp); + + break; + } + case ARPOp::Resp: + break; + default: + KLog::warn("NetworkManager", "Got ARP packet with unknown operation %d!", packet.operation.val()); + } +} + +void NetworkManager::handle_ipv4(NetworkAdapter* adapter, const NetworkAdapter::Packet* raw_packet) { + if (raw_packet->size < (sizeof(NetworkAdapter::FrameHeader) + sizeof(IPv4Packet))) { + KLog::warn("NetworkManager", "Got IPv4 packet with invalid frame size!"); + } + + auto& packet = *((IPv4Packet*) ((NetworkAdapter::FrameHeader* ) raw_packet->buffer)->payload); + + if (packet.length < sizeof(IPv4Packet)) { + KLog::warn("NetworkManager", "Got IPv4 packet with invalid size!"); + return; + } + + switch (packet.proto) { + case IPv4Proto::ICMP: + handle_icmp(adapter, packet); + break; + case IPv4Proto::TCP: + case IPv4Proto::UDP: + default: + KLog::warn("NetworkManager", "Received IPv4 packet with unknown protocol %d!", packet.proto); + } +} + +void NetworkManager::handle_icmp(NetworkAdapter* adapter, const IPv4Packet& packet) { + if (packet.length < (sizeof(IPv4Packet) + sizeof(ICMPHeader))) { + KLog::warn("NetworkManager", "Received ICMP packet of invalid size!"); + return; + } + const auto& header= *((ICMPHeader*) packet.payload); +} diff --git a/kernel/net/NetworkManager.h b/kernel/net/NetworkManager.h new file mode 100644 index 00000000..a6f56031 --- /dev/null +++ b/kernel/net/NetworkManager.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* Copyright © 2016-2024 Byteduck */ + +#pragma once + +#include "NetworkAdapter.h" + +class NetworkManager { +public: + static NetworkManager& inst(); + static void task_entry(); + +protected: + friend class NetworkAdapter; + void wakeup(); + +private: + NetworkManager() = default; + + [[noreturn]] void do_task(); + void handle_packet(NetworkAdapter* adapter, NetworkAdapter::Packet* packet); + void handle_arp(NetworkAdapter* adapter, const NetworkAdapter::Packet* packet); + void handle_ipv4(NetworkAdapter* adapter, const NetworkAdapter::Packet* packet); + void handle_icmp(NetworkAdapter* adapter, const IPv4Packet& packet); + + static NetworkManager* s_inst; + + BooleanBlocker m_blocker; +}; diff --git a/kernel/tasking/TaskManager.cpp b/kernel/tasking/TaskManager.cpp index 3dc414e0..b638a75b 100644 --- a/kernel/tasking/TaskManager.cpp +++ b/kernel/tasking/TaskManager.cpp @@ -27,6 +27,7 @@ #include "Reaper.h" #include #include +#include TSS TaskManager::tss; Mutex TaskManager::g_tasking_lock {"Tasking"}; @@ -156,6 +157,7 @@ void TaskManager::init(){ //Create kernel threads kernel_process->spawn_kernel_thread(kreaper_entry); + kernel_process->spawn_kernel_thread(NetworkManager::task_entry); //Preempt cur_thread = kernel_process->get_thread(kernel_process->pid());