From a740bf8df4221bc255c5a84cbb1e0f50867b489e Mon Sep 17 00:00:00 2001 From: Bananymous Date: Wed, 13 Sep 2023 19:09:12 +0300 Subject: [PATCH] 1000th COMMIT: Kernel: Add basic E1000 driver This driver is only capable to read mac address and enable and read link status --- kernel/CMakeLists.txt | 1 + kernel/include/kernel/Networking/E1000.h | 58 +++ .../include/kernel/Networking/NetworkDriver.h | 20 + kernel/include/kernel/PCI.h | 13 + kernel/kernel/Networking/E1000.cpp | 381 ++++++++++++++++++ kernel/kernel/PCI.cpp | 45 ++- 6 files changed, 517 insertions(+), 1 deletion(-) create mode 100644 kernel/include/kernel/Networking/E1000.h create mode 100644 kernel/include/kernel/Networking/NetworkDriver.h create mode 100644 kernel/kernel/Networking/E1000.cpp diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 84064de2d4..02706efe5c 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -38,6 +38,7 @@ set(KERNEL_SOURCES kernel/Memory/kmalloc.cpp kernel/Memory/PhysicalRange.cpp kernel/Memory/VirtualRange.cpp + kernel/Networking/E1000.cpp kernel/OpenFileDescriptorSet.cpp kernel/Panic.cpp kernel/PCI.cpp diff --git a/kernel/include/kernel/Networking/E1000.h b/kernel/include/kernel/Networking/E1000.h new file mode 100644 index 0000000000..9ed171dfd4 --- /dev/null +++ b/kernel/include/kernel/Networking/E1000.h @@ -0,0 +1,58 @@ +#pragma once + +#include +#include +#include + +#define E1000_NUM_RX_DESC 32 +#define E1000_NUM_TX_DESC 8 + +namespace Kernel +{ + + class E1000 final : public NetworkDriver + { + public: + static BAN::ErrorOr> create(const PCIDevice&); + ~E1000(); + + virtual uint8_t* get_mac_address() override { return m_mac_address; } + virtual BAN::ErrorOr send_packet(const void* data, uint16_t len) override; + + virtual bool link_up() override { return m_link_up; } + virtual int link_speed() override; + + private: + E1000() = default; + BAN::ErrorOr initialize(const PCIDevice&); + + static void interrupt_handler(); + + void write32(uint16_t reg, uint32_t value); + uint32_t read32(uint16_t reg); + + void detect_eeprom(); + uint32_t eeprom_read(uint8_t addr); + BAN::ErrorOr read_mac_address(); + + void initialize_rx(); + void initialize_tx(); + + void enable_link(); + void enable_interrupts(); + + void handle_receive(); + + private: + PCIDevice::BarType m_bar_type {}; + uint64_t m_bar_addr {}; + bool m_has_eerprom { false }; + uint8_t m_mac_address[6] {}; + uint16_t m_rx_current {}; + uint16_t m_tx_current {}; + struct e1000_rx_desc* m_rx_descs[E1000_NUM_RX_DESC] {}; + struct e1000_tx_desc* m_tx_descs[E1000_NUM_TX_DESC] {}; + bool m_link_up { false }; + }; + +} diff --git a/kernel/include/kernel/Networking/NetworkDriver.h b/kernel/include/kernel/Networking/NetworkDriver.h new file mode 100644 index 0000000000..23a2c92925 --- /dev/null +++ b/kernel/include/kernel/Networking/NetworkDriver.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +namespace Kernel +{ + + class NetworkDriver + { + public: + virtual ~NetworkDriver() {} + + virtual uint8_t* get_mac_address() = 0; + virtual BAN::ErrorOr send_packet(const void* data, uint16_t len) = 0; + + virtual bool link_up() = 0; + virtual int link_speed() = 0; + }; + +} \ No newline at end of file diff --git a/kernel/include/kernel/PCI.h b/kernel/include/kernel/PCI.h index 78cccef436..d5db2960a0 100644 --- a/kernel/include/kernel/PCI.h +++ b/kernel/include/kernel/PCI.h @@ -7,6 +7,14 @@ namespace Kernel class PCIDevice { + public: + enum class BarType + { + INVAL, + MEM, + IO, + }; + public: PCIDevice(uint8_t, uint8_t, uint8_t); @@ -24,6 +32,9 @@ namespace Kernel uint8_t subclass() const { return m_subclass; } uint8_t prog_if() const { return m_prog_if; } + BarType read_bar_type(uint8_t) const; + uint64_t read_bar_address(uint8_t) const; + void enable_bus_mastering() const; void disable_bus_mastering() const; @@ -41,6 +52,8 @@ namespace Kernel uint8_t m_class_code; uint8_t m_subclass; uint8_t m_prog_if; + + uint8_t m_header_type; }; class PCI diff --git a/kernel/kernel/Networking/E1000.cpp b/kernel/kernel/Networking/E1000.cpp new file mode 100644 index 0000000000..5e5b1a9be9 --- /dev/null +++ b/kernel/kernel/Networking/E1000.cpp @@ -0,0 +1,381 @@ +#include +#include +#include +#include +#include +#include + +#define E1000_GENERAL_MEM_SIZE (128 * 1024) + +#define E1000_REG_CTRL 0x0000 +#define E1000_REG_STATUS 0x0008 +#define E1000_REG_EEPROM 0x0014 +#define E1000_REG_INT_CAUSE_READ 0x00C0 +#define E1000_REG_INT_RATE 0x00C4 +#define E1000_REG_INT_MASK_SET 0x00D0 +#define E1000_REG_INT_MASK_CLEAR 0x00D8 +#define E1000_REG_RCTRL 0x0100 +#define E1000_REG_RXDESCLO 0x2800 +#define E1000_REG_RXDESCHI 0x2804 +#define E1000_REG_RXDESCLEN 0x2808 +#define E1000_REG_RXDESCHEAD 0x2810 +#define E1000_REG_RXDESCTAIL 0x2818 +#define E1000_REG_TXDESCLO 0x3800 +#define E1000_REG_TXDESCHI 0x3804 +#define E1000_REG_TXDESCLEN 0x3808 +#define E1000_REG_TXDESCHEAD 0x3810 +#define E1000_REG_TXDESCTAIL 0x3818 +#define E1000_REG_TCTRL 0x0400 +#define E1000_REG_TIPG 0x0410 + +#define E1000_STATUS_LINK_UP 0x02 +#define E1000_STATUS_SPEED_MASK 0xC0 +#define E1000_STATUS_SPEED_10MB 0x00 +#define E1000_STATUS_SPEED_100MB 0x40 +#define E1000_STATUS_SPEED_1000MB1 0x80 +#define E1000_STATUS_SPEED_1000MB2 0xC0 + +#define E1000_CTRL_SET_LINK_UP 0x40 + +#define E1000_INT_TXDW (1 << 0) +#define E1000_INT_TXQE (1 << 1) +#define E1000_INT_LSC (1 << 2) +#define E1000_INT_RXSEQ (1 << 3) +#define E1000_INT_RXDMT0 (1 << 4) +#define E1000_INT_RXO (1 << 6) +#define E1000_INT_RXT0 (1 << 7) +#define E1000_INT_MDAC (1 << 9) +#define E1000_INT_RXCFG (1 << 10) +#define E1000_INT_PHYINT (1 << 12) +#define E1000_INT_TXD_LOW (1 << 15) +#define E1000_INT_SRPD (1 << 16) + + +#define E1000_TCTL_EN (1 << 1) +#define E1000_TCTL_PSP (1 << 3) +#define E1000_TCTL_CT_SHIFT 4 +#define E1000_TCTL_COLD_SHIFT 12 +#define E1000_TCTL_SWXOFF (1 << 22) +#define E1000_TCTL_RTLC (1 << 24) + +#define E1000_RCTL_EN (1 << 1) +#define E1000_RCTL_SBP (1 << 2) +#define E1000_RCTL_UPE (1 << 3) +#define E1000_RCTL_MPE (1 << 4) +#define E1000_RCTL_LPE (1 << 5) +#define E1000_RCTL_LBM_NONE (0 << 6) +#define E1000_RCTL_LBM_PHY (3 << 6) +#define E1000_RTCL_RDMTS_HALF (0 << 8) +#define E1000_RTCL_RDMTS_QUARTER (1 << 8) +#define E1000_RTCL_RDMTS_EIGHTH (2 << 8) +#define E1000_RCTL_MO_36 (0 << 12) +#define E1000_RCTL_MO_35 (1 << 12) +#define E1000_RCTL_MO_34 (2 << 12) +#define E1000_RCTL_MO_32 (3 << 12) +#define E1000_RCTL_BAM (1 << 15) +#define E1000_RCTL_VFE (1 << 18) +#define E1000_RCTL_CFIEN (1 << 19) +#define E1000_RCTL_CFI (1 << 20) +#define E1000_RCTL_DPF (1 << 22) +#define E1000_RCTL_PMCF (1 << 23) +#define E1000_RCTL_SECRC (1 << 26) + +#define E1000_RCTL_BSIZE_256 (3 << 16) +#define E1000_RCTL_BSIZE_512 (2 << 16) +#define E1000_RCTL_BSIZE_1024 (1 << 16) +#define E1000_RCTL_BSIZE_2048 (0 << 16) +#define E1000_RCTL_BSIZE_4096 ((3 << 16) | (1 << 25)) +#define E1000_RCTL_BSIZE_8192 ((2 << 16) | (1 << 25)) +#define E1000_RCTL_BSIZE_16384 ((1 << 16) | (1 << 25)) + +namespace Kernel +{ + + struct e1000_rx_desc + { + 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 e1000_tx_desc + { + 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)); + + BAN::ErrorOr> E1000::create(const PCIDevice& pci_device) + { + E1000* e1000 = new E1000(); + ASSERT(e1000); + if (auto ret = e1000->initialize(pci_device); ret.is_error()) + { + delete e1000; + return ret.release_error(); + } + return BAN::UniqPtr::adopt(e1000); + } + + E1000::~E1000() + { + if (m_bar_type == PCIDevice::BarType::MEM && m_bar_addr) + PageTable::kernel().unmap_range(m_bar_addr & PAGE_ADDR_MASK, E1000_GENERAL_MEM_SIZE); + } + + BAN::ErrorOr E1000::initialize(const PCIDevice& pci_device) + { + m_bar_type = pci_device.read_bar_type(0); + if (m_bar_type == PCIDevice::BarType::INVAL) + { + dwarnln("invalid bar0 type"); + return BAN::Error::from_errno(EINVAL); + } + + if (m_bar_type == PCIDevice::BarType::MEM) + { + uint64_t bar_addr = pci_device.read_bar_address(0); + + vaddr_t page_vaddr = PageTable::kernel().reserve_free_contiguous_pages(E1000_GENERAL_MEM_SIZE / PAGE_SIZE, KERNEL_OFFSET); + paddr_t page_paddr = bar_addr & PAGE_ADDR_MASK; + PageTable::kernel().map_range_at(page_paddr, page_vaddr, E1000_GENERAL_MEM_SIZE, PageTable::Flags::CacheDisable | PageTable::Flags::ReadWrite | PageTable::Flags::Present); + + m_bar_addr = page_vaddr + (bar_addr % PAGE_SIZE); + } + else if (m_bar_type == PCIDevice::BarType::IO) + { + m_bar_addr = pci_device.read_bar_address(0); + } + + pci_device.enable_bus_mastering(); + + detect_eeprom(); + + TRY(read_mac_address()); + + dprintln("E1000 at PCI {}:{}.{}", pci_device.bus(), pci_device.dev(), pci_device.func()); + + dprintln(" MAC: {2H}:{2H}:{2H}:{2H}:{2H}:{2H}", + m_mac_address[0], + m_mac_address[1], + m_mac_address[2], + m_mac_address[3], + m_mac_address[4], + m_mac_address[5] + ); + + initialize_rx(); + initialize_tx(); + + enable_link(); + enable_interrupts(); + + dprintln(" link up: {}", link_up()); + if (link_up()) + dprintln(" link speed: {} Mbps", link_speed()); + + return {}; + } + + void E1000::write32(uint16_t reg, uint32_t value) + { + switch (m_bar_type) + { + case PCIDevice::BarType::MEM: + MMIO::write32(m_bar_addr + reg, value); + break; + case PCIDevice::BarType::IO: + IO::outl(m_bar_addr, reg); + IO::outl(m_bar_addr + 4, value); + break; + default: + ASSERT_NOT_REACHED(); + } + } + + uint32_t E1000::read32(uint16_t reg) + { + uint32_t result = 0; + switch (m_bar_type) + { + case PCIDevice::BarType::MEM: + result = MMIO::read32(m_bar_addr + reg); + break; + case PCIDevice::BarType::IO: + IO::outl(m_bar_addr, reg); + result = IO::inl(m_bar_addr + 4); + break; + default: + ASSERT_NOT_REACHED(); + } + return result; + } + + void E1000::detect_eeprom() + { + m_has_eerprom = false; + write32(E1000_REG_EEPROM, 0x01); + for (int i = 0; i < 1000 && !m_has_eerprom; i++) + if (read32(E1000_REG_EEPROM) & 0x10) + m_has_eerprom = true; + } + + uint32_t E1000::eeprom_read(uint8_t address) + { + uint32_t tmp = 0; + if (m_has_eerprom) + { + write32(E1000_REG_EEPROM, ((uint32_t)address << 8) | 1); + while (!((tmp = read32(E1000_REG_EEPROM)) & (1 << 4))) + continue; + } + else + { + write32(E1000_REG_EEPROM, ((uint32_t)address << 2) | 1); + while (!((tmp = read32(E1000_REG_EEPROM)) & (1 << 1))) + continue; + } + return (tmp >> 16) & 0xFFFF; + } + + BAN::ErrorOr E1000::read_mac_address() + { + if (m_has_eerprom) + { + uint32_t temp = eeprom_read(0); + m_mac_address[0] = temp; + m_mac_address[1] = temp >> 8; + + temp = eeprom_read(1); + m_mac_address[2] = temp; + m_mac_address[3] = temp >> 8; + + temp = eeprom_read(2); + m_mac_address[4] = temp; + m_mac_address[5] = temp >> 8; + + return {}; + } + + if (read32(0x5400) == 0) + { + dwarnln("no mac address"); + return BAN::Error::from_errno(EINVAL); + } + + for (int i = 0; i < 6; i++) + m_mac_address[i] = (uint8_t)read32(0x5400 + i * 8); + + return {}; + } + + void E1000::initialize_rx() + { + uint8_t* ptr = (uint8_t*)kmalloc(sizeof(e1000_rx_desc) * E1000_NUM_RX_DESC + 16, 16, true); + ASSERT(ptr); + + e1000_rx_desc* descs = (e1000_rx_desc*)ptr; + for (int i = 0; i < E1000_NUM_RX_DESC; i++) + { + // FIXME + m_rx_descs[i] = &descs[i]; + m_rx_descs[i]->addr = 0; + m_rx_descs[i]->status = 0; + } + + write32(E1000_REG_RXDESCLO, (uintptr_t)ptr >> 32); + write32(E1000_REG_RXDESCHI, (uintptr_t)ptr & 0xFFFFFFFF); + write32(E1000_REG_RXDESCLEN, E1000_NUM_RX_DESC * sizeof(e1000_rx_desc)); + write32(E1000_REG_RXDESCHEAD, 0); + write32(E1000_REG_RXDESCTAIL, E1000_NUM_RX_DESC - 1); + + m_rx_current = 0; + + uint32_t rctrl = 0; + rctrl |= E1000_RCTL_EN; + rctrl |= E1000_RCTL_SBP; + rctrl |= E1000_RCTL_UPE; + rctrl |= E1000_RCTL_MPE; + rctrl |= E1000_RCTL_LBM_NONE; + rctrl |= E1000_RTCL_RDMTS_HALF; + rctrl |= E1000_RCTL_BAM; + rctrl |= E1000_RCTL_SECRC; + rctrl |= E1000_RCTL_BSIZE_8192; + + write32(E1000_REG_RCTRL, rctrl); + } + + void E1000::initialize_tx() + { + auto* ptr = (uint8_t*)kmalloc(sizeof(e1000_tx_desc) * E1000_NUM_TX_DESC + 16, 16, true); + ASSERT(ptr); + + auto* descs = (e1000_tx_desc*)ptr; + for(int i = 0; i < E1000_NUM_TX_DESC; i++) + { + // FIXME + m_tx_descs[i] = &descs[i]; + m_tx_descs[i]->addr = 0; + m_tx_descs[i]->cmd = 0; + } + + write32(E1000_REG_TXDESCHI, (uintptr_t)ptr >> 32); + write32(E1000_REG_TXDESCLO, (uintptr_t)ptr & 0xFFFFFFFF); + write32(E1000_REG_TXDESCLEN, E1000_NUM_TX_DESC * sizeof(e1000_tx_desc)); + write32(E1000_REG_TXDESCHEAD, 0); + write32(E1000_REG_TXDESCTAIL, 0); + + m_tx_current = 0; + + write32(E1000_REG_TCTRL, read32(E1000_REG_TCTRL) | E1000_TCTL_EN | E1000_TCTL_PSP); + write32(E1000_REG_TIPG, 0x0060200A); + } + + void E1000::enable_link() + { + write32(E1000_REG_CTRL, read32(E1000_REG_CTRL) | E1000_CTRL_SET_LINK_UP); + m_link_up = !!(read32(E1000_REG_STATUS) & E1000_STATUS_LINK_UP); + } + + int E1000::link_speed() + { + if (!link_up()) + return 0; + uint32_t speed = read32(E1000_REG_STATUS) & E1000_STATUS_SPEED_MASK; + if (speed == E1000_STATUS_SPEED_10MB) + return 10; + if (speed == E1000_STATUS_SPEED_100MB) + return 100; + if (speed == E1000_STATUS_SPEED_1000MB1) + return 1000; + if (speed == E1000_STATUS_SPEED_1000MB2) + return 1000; + return 0; + } + + void E1000::enable_interrupts() + { + write32(E1000_REG_INT_RATE, 6000); + write32(E1000_REG_INT_MASK_SET, E1000_INT_LSC | E1000_INT_RXT0 | E1000_INT_RXO); + read32(E1000_REG_INT_CAUSE_READ); + + // FIXME: implement PCI interrupt allocation + //IDT::register_irq_handler(irq, E1000::interrupt_handler); + //InterruptController::enable_irq(irq); + } + + BAN::ErrorOr E1000::send_packet(const void* data, uint16_t len) + { + (void)data; + (void)len; + return BAN::Error::from_errno(ENOTSUP); + } + +} diff --git a/kernel/kernel/PCI.cpp b/kernel/kernel/PCI.cpp index f378abea66..73400f3347 100644 --- a/kernel/kernel/PCI.cpp +++ b/kernel/kernel/PCI.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -105,7 +106,7 @@ namespace Kernel { case 0x01: if (auto res = ATAController::create(pci_device); res.is_error()) - dprintln("{}", res.error()); + dprintln("ATA: {}", res.error()); break; default: dprintln("unsupported storage device (pci {2H}.{2H}.{2H})", pci_device.class_code(), pci_device.subclass(), pci_device.prog_if()); @@ -113,6 +114,20 @@ namespace Kernel } break; } + case 0x02: + { + switch (pci_device.subclass()) + { + case 0x00: + if (auto res = E1000::create(pci_device); res.is_error()) + dprintln("E1000: {}", res.error()); + break; + default: + dprintln("unsupported ethernet device (pci {2H}.{2H}.{2H})", pci_device.class_code(), pci_device.subclass(), pci_device.prog_if()); + break; + } + break; + } default: break; } @@ -126,6 +141,7 @@ namespace Kernel m_class_code = (uint8_t)(type >> 8); m_subclass = (uint8_t)(type); m_prog_if = read_byte(0x09); + m_header_type = read_byte(0x0E); } uint32_t PCIDevice::read_dword(uint8_t offset) const @@ -153,6 +169,33 @@ namespace Kernel write_config_dword(m_bus, m_dev, m_func, offset, value); } + PCIDevice::BarType PCIDevice::read_bar_type(uint8_t bar) const + { + ASSERT(m_header_type == 0x00); + ASSERT(bar <= 5); + + uint32_t type = read_dword(0x10 + bar * 4) & 0b111; + if (type & 1) + return BarType::IO; + type >>= 1; + if (type == 0x0 || type == 0x2) + return BarType::MEM; + return BarType::INVAL; + } + + uint64_t PCIDevice::read_bar_address(uint8_t bar) const + { + ASSERT(m_header_type == 0x00); + ASSERT(bar <= 5); + + uint64_t address = read_dword(0x10 + bar * 4); + if (address & 1) + return address & 0xFFFFFFFC; + if ((address & 0b110) == 0b100) + address |= (uint64_t)read_dword(0x10 + bar * 4 + 4) << 32; + return address & 0xFFFFFFFFFFFFFFF0; + } + void PCIDevice::enable_bus_mastering() const { write_dword(0x04, read_dword(0x04) | 1u << 2);