mirror of
https://github.com/RGBCube/serenity
synced 2025-05-18 13:55:08 +00:00

This class is intended to replace all IOAddress usages in the Kernel codebase altogether. The idea is to ensure IO can be done in arch-specific manner that is determined mostly in compile-time, but to still be able to use most of the Kernel code in non-x86 builds. Specific devices that rely on x86-specific IO instructions are already placed in the Arch/x86 directory and are omitted for non-x86 builds. The reason this works so well is the fact that x86 IO space acts in a similar fashion to the traditional memory space being available in most CPU architectures - the x86 IO space is essentially just an array of bytes like the physical memory address space, but requires x86 IO instructions to load and store data. Therefore, many devices allow host software to interact with the hardware registers in both ways, with a noticeable trend even in the modern x86 hardware to move away from the old x86 IO space to exclusively using memory-mapped IO. Therefore, the IOWindow class encapsulates both methods for x86 builds. The idea is to allow PCI devices to be used in either way in x86 builds, so when trying to map an IOWindow on a PCI BAR, the Kernel will try to find the proper method being declared with the PCI BAR flags. For old PCI hardware on non-x86 builds this might turn into a problem as we can't use port mapped IO, so the Kernel will gracefully fail with ENOTSUP error code if that's the case, as there's really nothing we can do within such case. For general IO, the read{8,16,32} and write{8,16,32} methods are available as a convenient API for other places in the Kernel. There are simply no direct 64-bit IO API methods yet, as it's not needed right now and is not considered to be Arch-agnostic too - the x86 IO space doesn't support generating 64 bit cycle on IO bus and instead requires two 2 32-bit accesses. If for whatever reason it appears to be necessary to do IO in such manner, it could probably be added with some neat tricks to do so. It is recommended to use Memory::TypedMapping struct if direct 64 bit IO is actually needed.
256 lines
8.4 KiB
C++
256 lines
8.4 KiB
C++
/*
|
|
* Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#include <AK/MACAddress.h>
|
|
#include <Kernel/Bus/PCI/API.h>
|
|
#include <Kernel/Bus/PCI/IDs.h>
|
|
#include <Kernel/Net/Intel/E1000ENetworkAdapter.h>
|
|
#include <Kernel/Net/NetworkingManagement.h>
|
|
#include <Kernel/Sections.h>
|
|
|
|
namespace Kernel {
|
|
|
|
#define REG_EEPROM 0x0014
|
|
|
|
static bool is_valid_device_id(u16 device_id)
|
|
{
|
|
// Note: All ids listed here are valid, but only the ones that are verified working are set to 'true'
|
|
switch (device_id) {
|
|
case 0x10D3: // 82574L
|
|
return true;
|
|
case 0x1000: // 82542
|
|
case 0x0438: // DH89XXCC_SGMII
|
|
case 0x043A: // DH89XXCC_SERDES
|
|
case 0x043C: // DH89XXCC_BACKPLANE
|
|
case 0x0440: // DH89XXCC_SFP
|
|
case 0x1001: // 82543GC_FIBER
|
|
case 0x1004: // 82543GC_COPPER
|
|
case 0x1008: // 82544EI_COPPER
|
|
case 0x1009: // 82544EI_FIBER
|
|
case 0x100C: // 82544GC_COPPER
|
|
case 0x100D: // 82544GC_LOM
|
|
case 0x100E: // 82540EM
|
|
case 0x100F: // 82545EM_COPPER
|
|
case 0x1010: // 82546EB_COPPER
|
|
case 0x1011: // 82545EM_FIBER
|
|
case 0x1012: // 82546EB_FIBER
|
|
case 0x1013: // 82541EI
|
|
case 0x1014: // 82541ER_LOM
|
|
case 0x1015: // 82540EM_LOM
|
|
case 0x1016: // 82540EP_LOM
|
|
case 0x1017: // 82540EP
|
|
case 0x1018: // 82541EI_MOBILE
|
|
case 0x1019: // 82547EI
|
|
case 0x101A: // 82547EI_MOBILE
|
|
case 0x101D: // 82546EB_QUAD_COPPER
|
|
case 0x101E: // 82540EP_LP
|
|
case 0x1026: // 82545GM_COPPER
|
|
case 0x1027: // 82545GM_FIBER
|
|
case 0x1028: // 82545GM_SERDES
|
|
case 0x1049: // ICH8_IGP_M_AMT
|
|
case 0x104A: // ICH8_IGP_AMT
|
|
case 0x104B: // ICH8_IGP_C
|
|
case 0x104C: // ICH8_IFE
|
|
case 0x104D: // ICH8_IGP_M
|
|
case 0x105E: // 82571EB_COPPER
|
|
case 0x105F: // 82571EB_FIBER
|
|
case 0x1060: // 82571EB_SERDES
|
|
case 0x1075: // 82547GI
|
|
case 0x1076: // 82541GI
|
|
case 0x1077: // 82541GI_MOBILE
|
|
case 0x1078: // 82541ER
|
|
case 0x1079: // 82546GB_COPPER
|
|
case 0x107A: // 82546GB_FIBER
|
|
case 0x107B: // 82546GB_SERDES
|
|
case 0x107C: // 82541GI_LF
|
|
case 0x107D: // 82572EI_COPPER
|
|
case 0x107E: // 82572EI_FIBER
|
|
case 0x107F: // 82572EI_SERDES
|
|
case 0x108A: // 82546GB_PCIE
|
|
case 0x108B: // 82573E
|
|
case 0x108C: // 82573E_IAMT
|
|
case 0x1096: // 80003ES2LAN_COPPER_DPT
|
|
case 0x1098: // 80003ES2LAN_SERDES_DPT
|
|
case 0x1099: // 82546GB_QUAD_COPPER
|
|
case 0x109A: // 82573L
|
|
case 0x10A4: // 82571EB_QUAD_COPPER
|
|
case 0x10A5: // 82571EB_QUAD_FIBER
|
|
case 0x10A7: // 82575EB_COPPER
|
|
case 0x10A9: // 82575EB_FIBER_SERDES
|
|
case 0x10B5: // 82546GB_QUAD_COPPER_KSP3
|
|
case 0x10B9: // 82572EI
|
|
case 0x10BA: // 80003ES2LAN_COPPER_SPT
|
|
case 0x10BB: // 80003ES2LAN_SERDES_SPT
|
|
case 0x10BC: // 82571EB_QUAD_COPPER_LP
|
|
case 0x10BD: // ICH9_IGP_AMT
|
|
case 0x10BF: // ICH9_IGP_M
|
|
case 0x10C0: // ICH9_IFE
|
|
case 0x10C2: // ICH9_IFE_G
|
|
case 0x10C3: // ICH9_IFE_GT
|
|
case 0x10C4: // ICH8_IFE_GT
|
|
case 0x10C5: // ICH8_IFE_G
|
|
case 0x10C9: // 82576
|
|
case 0x10CA: // 82576_VF
|
|
case 0x10CB: // ICH9_IGP_M_V
|
|
case 0x10CC: // ICH10_R_BM_LM
|
|
case 0x10CD: // ICH10_R_BM_LF
|
|
case 0x10CE: // ICH10_R_BM_V
|
|
case 0x10D5: // 82571PT_QUAD_COPPER
|
|
case 0x10D6: // 82575GB_QUAD_COPPER
|
|
case 0x10D9: // 82571EB_SERDES_DUAL
|
|
case 0x10DA: // 82571EB_SERDES_QUAD
|
|
case 0x10DE: // ICH10_D_BM_LM
|
|
case 0x10DF: // ICH10_D_BM_LF
|
|
case 0x10E5: // ICH9_BM
|
|
case 0x10E6: // 82576_FIBER
|
|
case 0x10E7: // 82576_SERDES
|
|
case 0x10E8: // 82576_QUAD_COPPER
|
|
case 0x10EA: // PCH_M_HV_LM
|
|
case 0x10EB: // PCH_M_HV_LC
|
|
case 0x10EF: // PCH_D_HV_DM
|
|
case 0x10F0: // PCH_D_HV_DC
|
|
case 0x10F5: // ICH9_IGP_M_AMT
|
|
case 0x10F6: // 82574LA
|
|
case 0x1501: // ICH8_82567V_3
|
|
case 0x1502: // PCH2_LV_LM
|
|
case 0x1503: // PCH2_LV_V
|
|
case 0x150A: // 82576_NS
|
|
case 0x150C: // 82583V
|
|
case 0x150D: // 82576_SERDES_QUAD
|
|
case 0x150E: // 82580_COPPER
|
|
case 0x150F: // 82580_FIBER
|
|
case 0x1510: // 82580_SERDES
|
|
case 0x1511: // 82580_SGMII
|
|
case 0x1516: // 82580_COPPER_DUAL
|
|
case 0x1518: // 82576_NS_SERDES
|
|
case 0x1520: // I350_VF
|
|
case 0x1521: // I350_COPPER
|
|
case 0x1522: // I350_FIBER
|
|
case 0x1523: // I350_SERDES
|
|
case 0x1524: // I350_SGMII
|
|
case 0x1525: // ICH10_D_BM_V
|
|
case 0x1526: // 82576_QUAD_COPPER_ET2
|
|
case 0x1527: // 82580_QUAD_FIBER
|
|
case 0x152D: // 82576_VF_HV
|
|
case 0x152F: // I350_VF_HV
|
|
case 0x1533: // I210_COPPER
|
|
case 0x1534: // I210_COPPER_OEM1
|
|
case 0x1535: // I210_COPPER_IT
|
|
case 0x1536: // I210_FIBER
|
|
case 0x1537: // I210_SERDES
|
|
case 0x1538: // I210_SGMII
|
|
case 0x1539: // I211_COPPER
|
|
case 0x153A: // PCH_LPT_I217_LM
|
|
case 0x153B: // PCH_LPT_I217_V
|
|
case 0x1546: // I350_DA4
|
|
case 0x1559: // PCH_LPTLP_I218_V
|
|
case 0x155A: // PCH_LPTLP_I218_LM
|
|
case 0x156F: // PCH_SPT_I219_LM
|
|
case 0x1570: // PCH_SPT_I219_V
|
|
case 0x157B: // I210_COPPER_FLASHLESS
|
|
case 0x157C: // I210_SERDES_FLASHLESS
|
|
case 0x15A0: // PCH_I218_LM2
|
|
case 0x15A1: // PCH_I218_V2
|
|
case 0x15A2: // PCH_I218_LM3
|
|
case 0x15A3: // PCH_I218_V3
|
|
case 0x15B7: // PCH_SPT_I219_LM2
|
|
case 0x15B8: // PCH_SPT_I219_V2
|
|
case 0x15B9: // PCH_LBG_I219_LM3
|
|
case 0x15BB: // PCH_CNP_I219_LM7
|
|
case 0x15BC: // PCH_CNP_I219_V7
|
|
case 0x15BD: // PCH_CNP_I219_LM6
|
|
case 0x15BE: // PCH_CNP_I219_V6
|
|
case 0x15D6: // PCH_SPT_I219_V5
|
|
case 0x15D7: // PCH_SPT_I219_LM4
|
|
case 0x15D8: // PCH_SPT_I219_V4
|
|
case 0x15DF: // PCH_ICP_I219_LM8
|
|
case 0x15E0: // PCH_ICP_I219_V8
|
|
case 0x15E1: // PCH_ICP_I219_LM9
|
|
case 0x15E2: // PCH_ICP_I219_V9
|
|
case 0x15E3: // PCH_SPT_I219_LM5
|
|
case 0x1F40: // I354_BACKPLANE_1GBPS
|
|
case 0x1F41: // I354_SGMII
|
|
case 0x1F45: // I354_BACKPLANE_2_5GBPS
|
|
case 0x294C: // ICH9_IGP_C
|
|
return false;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
UNMAP_AFTER_INIT LockRefPtr<E1000ENetworkAdapter> E1000ENetworkAdapter::try_to_initialize(PCI::DeviceIdentifier const& pci_device_identifier)
|
|
{
|
|
if (pci_device_identifier.hardware_id().vendor_id != PCI::VendorID::Intel)
|
|
return {};
|
|
if (!is_valid_device_id(pci_device_identifier.hardware_id().device_id))
|
|
return {};
|
|
u8 irq = pci_device_identifier.interrupt_line().value();
|
|
// FIXME: Better propagate errors here
|
|
auto interface_name_or_error = NetworkingManagement::generate_interface_name_from_pci_address(pci_device_identifier);
|
|
if (interface_name_or_error.is_error())
|
|
return {};
|
|
auto registers_io_window = IOWindow::create_for_pci_device_bar(pci_device_identifier, PCI::HeaderType0BaseRegister::BAR0).release_value_but_fixme_should_propagate_errors();
|
|
auto adapter = adopt_lock_ref_if_nonnull(new (nothrow) E1000ENetworkAdapter(pci_device_identifier.address(), irq, move(registers_io_window), interface_name_or_error.release_value()));
|
|
if (!adapter)
|
|
return {};
|
|
if (adapter->initialize())
|
|
return adapter;
|
|
return {};
|
|
}
|
|
|
|
UNMAP_AFTER_INIT bool E1000ENetworkAdapter::initialize()
|
|
{
|
|
dmesgln("E1000e: Found @ {}", pci_address());
|
|
enable_bus_mastering(pci_address());
|
|
|
|
dmesgln("E1000e: IO base: {}", m_registers_io_window);
|
|
dmesgln("E1000e: Interrupt line: {}", interrupt_number());
|
|
detect_eeprom();
|
|
dmesgln("E1000e: Has EEPROM? {}", m_has_eeprom);
|
|
read_mac_address();
|
|
auto const& 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, NonnullOwnPtr<IOWindow> registers_io_window, NonnullOwnPtr<KString> interface_name)
|
|
: E1000NetworkAdapter(address, irq, move(registers_io_window), move(interface_name))
|
|
{
|
|
}
|
|
|
|
UNMAP_AFTER_INIT E1000ENetworkAdapter::~E1000ENetworkAdapter() = default;
|
|
|
|
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;
|
|
}
|
|
|
|
}
|