diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt index 93f0fd58ee..e1cd0e4dac 100644 --- a/Kernel/CMakeLists.txt +++ b/Kernel/CMakeLists.txt @@ -115,6 +115,7 @@ set(KERNEL_SOURCES KString.cpp KSyms.cpp Lock.cpp + Net/E1000ENetworkAdapter.cpp Net/E1000NetworkAdapter.cpp Net/IPv4Socket.cpp Net/LocalSocket.cpp diff --git a/Kernel/Debug.h.in b/Kernel/Debug.h.in index e12d176dc1..e30f1c331c 100644 --- a/Kernel/Debug.h.in +++ b/Kernel/Debug.h.in @@ -46,6 +46,10 @@ #cmakedefine01 E1000_DEBUG #endif +#ifndef E1000E_DEBUG +#cmakedefine01 E1000E_DEBUG +#endif + #ifndef ETHERNET_DEBUG #cmakedefine01 ETHERNET_DEBUG #endif diff --git a/Kernel/Net/E1000ENetworkAdapter.cpp b/Kernel/Net/E1000ENetworkAdapter.cpp new file mode 100644 index 0000000000..c829dc3113 --- /dev/null +++ b/Kernel/Net/E1000ENetworkAdapter.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2018-2021, Andreas Kling + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include + +namespace Kernel { + +#define REG_EEPROM 0x0014 + +static bool is_valid_device_id(u16 device_id) +{ + switch (device_id) { + case 0x10D3: // 82574 + return true; + default: + return false; + } +} + +UNMAP_AFTER_INIT RefPtr E1000ENetworkAdapter::try_to_initialize(PCI::Address address) +{ + auto id = PCI::get_id(address); + if (id.vendor_id != (u16)PCIVendorID::Intel) + return {}; + if (!is_valid_device_id(id.device_id)) + return {}; + u8 irq = PCI::get_interrupt_line(address); + auto adapter = adopt_ref_if_nonnull(new E1000ENetworkAdapter(address, irq)); + if (!adapter) + return {}; + if (adapter->initialize()) + return adapter; + return {}; +} + +UNMAP_AFTER_INIT bool E1000ENetworkAdapter::initialize() +{ + dmesgln("E1000e: Found @ {}", pci_address()); + + m_io_base = IOAddress(PCI::get_BAR2(pci_address()) & ~1); + + enable_bus_mastering(pci_address()); + + size_t mmio_base_size = PCI::get_BAR_space_size(pci_address(), 0); + m_mmio_region = MM.allocate_kernel_region(PhysicalAddress(page_base_of(PCI::get_BAR0(pci_address()))), page_round_up(mmio_base_size), "E1000e MMIO", Region::Access::Read | Region::Access::Write, Region::Cacheable::No); + if (!m_mmio_region) + return false; + m_mmio_base = m_mmio_region->vaddr(); + m_use_mmio = true; + m_interrupt_line = PCI::get_interrupt_line(pci_address()); + dmesgln("E1000e: port base: {}", m_io_base); + dmesgln("E1000e: MMIO base: {}", PhysicalAddress(PCI::get_BAR0(pci_address()) & 0xfffffffc)); + dmesgln("E1000e: MMIO base size: {} bytes", mmio_base_size); + dmesgln("E1000e: Interrupt line: {}", m_interrupt_line); + detect_eeprom(); + dmesgln("E1000e: Has EEPROM? {}", m_has_eeprom); + read_mac_address(); + const auto& mac = mac_address(); + dmesgln("E1000e: MAC address: {}", mac.to_string()); + + initialize_rx_descriptors(); + initialize_tx_descriptors(); + + setup_link(); + setup_interrupts(); + return true; +} + +UNMAP_AFTER_INIT E1000ENetworkAdapter::E1000ENetworkAdapter(PCI::Address address, u8 irq) + : E1000NetworkAdapter(address, irq) +{ +} + +UNMAP_AFTER_INIT E1000ENetworkAdapter::~E1000ENetworkAdapter() +{ +} + +UNMAP_AFTER_INIT void E1000ENetworkAdapter::detect_eeprom() +{ + // FIXME: Try to find a way to detect if EEPROM exists instead of assuming it is + m_has_eeprom = true; +} + +UNMAP_AFTER_INIT u32 E1000ENetworkAdapter::read_eeprom(u8 address) +{ + VERIFY(m_has_eeprom); + u16 data = 0; + u32 tmp = 0; + if (m_has_eeprom) { + out32(REG_EEPROM, ((u32)address << 2) | 1); + while (!((tmp = in32(REG_EEPROM)) & (1 << 1))) + ; + } else { + out32(REG_EEPROM, ((u32)address << 2) | 1); + while (!((tmp = in32(REG_EEPROM)) & (1 << 1))) + ; + } + data = (tmp >> 16) & 0xffff; + return data; +} + +} diff --git a/Kernel/Net/E1000ENetworkAdapter.h b/Kernel/Net/E1000ENetworkAdapter.h new file mode 100644 index 0000000000..a6e84f37fc --- /dev/null +++ b/Kernel/Net/E1000ENetworkAdapter.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Kernel { + +class E1000ENetworkAdapter final + : public E1000NetworkAdapter { +public: + static RefPtr try_to_initialize(PCI::Address); + + virtual bool initialize() override; + + virtual ~E1000ENetworkAdapter() override; + + virtual const char* purpose() const override { return class_name(); } + +private: + E1000ENetworkAdapter(PCI::Address, u8 irq); + + virtual const char* class_name() const override { return "E1000ENetworkAdapter"; } + + virtual void detect_eeprom() override; + virtual u32 read_eeprom(u8 address) override; +}; +} diff --git a/Kernel/Net/E1000NetworkAdapter.cpp b/Kernel/Net/E1000NetworkAdapter.cpp index 03d1fce700..ce2a72d568 100644 --- a/Kernel/Net/E1000NetworkAdapter.cpp +++ b/Kernel/Net/E1000NetworkAdapter.cpp @@ -164,23 +164,39 @@ UNMAP_AFTER_INIT RefPtr E1000NetworkAdapter::try_to_initial if (!is_valid_device_id(id.device_id)) return {}; u8 irq = PCI::get_interrupt_line(address); - return adopt_ref_if_nonnull(new E1000NetworkAdapter(address, irq)); + auto adapter = adopt_ref_if_nonnull(new E1000NetworkAdapter(address, irq)); + if (!adapter) + return {}; + if (adapter->initialize()) + return adapter; + return {}; } -UNMAP_AFTER_INIT E1000NetworkAdapter::E1000NetworkAdapter(PCI::Address address, u8 irq) - : PCI::Device(address, irq) - , m_io_base(PCI::get_BAR1(pci_address()) & ~1) - , m_rx_descriptors_region(MM.allocate_contiguous_kernel_region(page_round_up(sizeof(e1000_rx_desc) * number_of_rx_descriptors + 16), "E1000 RX", Region::Access::Read | Region::Access::Write)) - , m_tx_descriptors_region(MM.allocate_contiguous_kernel_region(page_round_up(sizeof(e1000_tx_desc) * number_of_tx_descriptors + 16), "E1000 TX", Region::Access::Read | Region::Access::Write)) +UNMAP_AFTER_INIT void E1000NetworkAdapter::setup_link() { - set_interface_name(address); + u32 flags = in32(REG_CTRL); + out32(REG_CTRL, flags | ECTRL_SLU); +} +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); + enable_irq(); +} + +UNMAP_AFTER_INIT bool E1000NetworkAdapter::initialize() +{ dmesgln("E1000: Found @ {}", pci_address()); - enable_bus_mastering(pci_address()); + m_io_base = IOAddress(PCI::get_BAR1(pci_address()) & ~1); + size_t mmio_base_size = PCI::get_BAR_space_size(pci_address(), 0); m_mmio_region = MM.allocate_kernel_region(PhysicalAddress(page_base_of(PCI::get_BAR0(pci_address()))), page_round_up(mmio_base_size), "E1000 MMIO", Region::Access::Read | Region::Access::Write, Region::Cacheable::No); + if (!m_mmio_region) + return false; m_mmio_base = m_mmio_region->vaddr(); m_use_mmio = true; m_interrupt_line = PCI::get_interrupt_line(pci_address()); @@ -194,18 +210,20 @@ UNMAP_AFTER_INIT E1000NetworkAdapter::E1000NetworkAdapter(PCI::Address address, const auto& mac = mac_address(); dmesgln("E1000: MAC address: {}", mac.to_string()); - u32 flags = in32(REG_CTRL); - out32(REG_CTRL, flags | ECTRL_SLU); - - out32(REG_INTERRUPT_RATE, 6000); // Interrupt rate of 1.536 milliseconds - initialize_rx_descriptors(); initialize_tx_descriptors(); - out32(REG_INTERRUPT_MASK_SET, INTERRUPT_LSC | INTERRUPT_RXT0 | INTERRUPT_RXO); - in32(REG_INTERRUPT_CAUSE_READ); + setup_link(); + setup_interrupts(); + return true; +} - enable_irq(); +UNMAP_AFTER_INIT E1000NetworkAdapter::E1000NetworkAdapter(PCI::Address address, u8 irq) + : PCI::Device(address, irq) + , m_rx_descriptors_region(MM.allocate_contiguous_kernel_region(page_round_up(sizeof(e1000_rx_desc) * number_of_rx_descriptors + 16), "E1000 RX", Region::Access::Read | Region::Access::Write)) + , m_tx_descriptors_region(MM.allocate_contiguous_kernel_region(page_round_up(sizeof(e1000_tx_desc) * number_of_tx_descriptors + 16), "E1000 TX", Region::Access::Read | Region::Access::Write)) +{ + set_interface_name(pci_address()); } UNMAP_AFTER_INIT E1000NetworkAdapter::~E1000NetworkAdapter() diff --git a/Kernel/Net/E1000NetworkAdapter.h b/Kernel/Net/E1000NetworkAdapter.h index 6458ae9d45..21389fc412 100644 --- a/Kernel/Net/E1000NetworkAdapter.h +++ b/Kernel/Net/E1000NetworkAdapter.h @@ -17,11 +17,13 @@ namespace Kernel { -class E1000NetworkAdapter final : public NetworkAdapter +class E1000NetworkAdapter : public NetworkAdapter , public PCI::Device { public: static RefPtr try_to_initialize(PCI::Address); + virtual bool initialize(); + virtual ~E1000NetworkAdapter() override; virtual void send_raw(ReadonlyBytes) override; @@ -29,7 +31,10 @@ public: virtual const char* purpose() const override { return class_name(); } -private: +protected: + void setup_interrupts(); + void setup_link(); + E1000NetworkAdapter(PCI::Address, u8 irq); virtual void handle_irq(const RegisterState&) override; virtual const char* class_name() const override { return "E1000NetworkAdapter"; } @@ -53,8 +58,8 @@ private: volatile uint16_t special { 0 }; }; - void detect_eeprom(); - u32 read_eeprom(u8 address); + virtual void detect_eeprom(); + virtual u32 read_eeprom(u8 address); void read_mac_address(); void write_command(u16 address, u32); diff --git a/Kernel/Net/NetworkingManagement.cpp b/Kernel/Net/NetworkingManagement.cpp index af961ec069..dfd4792469 100644 --- a/Kernel/Net/NetworkingManagement.cpp +++ b/Kernel/Net/NetworkingManagement.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -76,6 +77,8 @@ UNMAP_AFTER_INIT RefPtr NetworkingManagement::determine_network_ { if (auto candidate = E1000NetworkAdapter::try_to_initialize(address); !candidate.is_null()) return candidate; + if (auto candidate = E1000ENetworkAdapter::try_to_initialize(address); !candidate.is_null()) + return candidate; if (auto candidate = RTL8139NetworkAdapter::try_to_initialize(address); !candidate.is_null()) return candidate; if (auto candidate = NE2000NetworkAdapter::try_to_initialize(address); !candidate.is_null()) diff --git a/Meta/CMake/all_the_debug_macros.cmake b/Meta/CMake/all_the_debug_macros.cmake index 3c7acc1960..d90a5d892c 100644 --- a/Meta/CMake/all_the_debug_macros.cmake +++ b/Meta/CMake/all_the_debug_macros.cmake @@ -35,6 +35,7 @@ set(DOUBLECLICK_DEBUG ON) set(DWARF_DEBUG ON) set(DYNAMIC_LOAD_DEBUG ON) set(E1000_DEBUG ON) +set(E1000E_DEBUG ON) set(EDITOR_DEBUG ON) set(ELF_IMAGE_DEBUG ON) set(EMOJI_DEBUG ON)