Kernel/Net: Use the new MDIO abstraction to initialize E1000E properly

This makes the virtual e1000e network adapter from QEMU work with
serenity again. This was possibly broken by a QEMU update?
This commit is contained in:
Sönke Holz authored and Sönke Holz committed 2026-07-09 13:49:51 +02:00
1 parent 6fa1f85c74
commit 84d1758de3
2 files changed
+80 -2

No files matched your search

+69 -1
View File
@@ -10,16 +10,35 @@
#include <Kernel/Net/Intel/E1000ENetworkAdapter.h>
#include <Kernel/Net/NetworkingManagement.h>
#include <Kernel/Sections.h>
#include <Kernel/Tasks/Process.h>
namespace Kernel {
#define REG_CTRL 0x0000
#define REG_EECD 0x0010
#define REG_EEPROM 0x0014
#define REG_MDIC 0x0020
// CTRL Register
#define REG_CTRL_SLU (1 << 6)
// EECD Register
#define EECD_PRES 0x100
// MIDC Register
#define REG_MDIC_DATA_MASK (0xffff << 0)
#define REG_MDIC_DATA_OFFSET 0
#define REG_MDIC_REGADD_OFFSET 16
#define REG_MDIC_R (1 << 28)
#define REG_MDIC_OP_WRITE (0b01 << 26)
#define REG_MDIC_OP_READ (0b10 << 26)
#define REG_MDIC_PHYADD_OFFSET 21
#define GIGABIT_PHY_ID 1
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'
@@ -226,6 +245,8 @@ UNMAP_AFTER_INIT ErrorOr<void> E1000ENetworkAdapter::initialize(Badge<Networking
auto const& mac = mac_address();
dmesgln("E1000e: MAC address: {}", mac.to_string());
m_mdio_handling_process = TRY(spawn_mdio_handling_task(GIGABIT_PHY_ID));
initialize_rx_descriptors();
initialize_tx_descriptors();
@@ -248,7 +269,54 @@ UNMAP_AFTER_INIT E1000ENetworkAdapter::E1000ENetworkAdapter(StringView interface
{
}
UNMAP_AFTER_INIT E1000ENetworkAdapter::~E1000ENetworkAdapter() = default;
UNMAP_AFTER_INIT E1000ENetworkAdapter::~E1000ENetworkAdapter()
{
if (m_mdio_handling_process) {
m_mdio_handling_process->die();
// Block until all threads exited to prevent UAF
ErrorOr<siginfo_t> result = siginfo_t {};
(void)Thread::current()->block<Thread::WaitBlocker>({}, WEXITED, m_mdio_handling_process.release_nonnull(), result);
}
}
void E1000ENetworkAdapter::on_phy_link_status_change(MDIO::LinkStatus link_status)
{
auto ctrl = in32(REG_CTRL);
if (link_status == MDIO::LinkStatus::Up)
ctrl |= REG_CTRL_SLU;
else
ctrl &= ~REG_CTRL_SLU;
out32(REG_CTRL, ctrl);
}
u16 E1000ENetworkAdapter::read_phy_register(u8 phy_id, MDIO::Clause22::RegisterAddress address)
{
// FIXME: Newer controllers (like the I211) don't have a PHYADD field in the MDIC register.
// They instead have it in a separate register.
out32(REG_MDIC, REG_MDIC_OP_READ | (to_underlying(address) << REG_MDIC_REGADD_OFFSET) | (phy_id << REG_MDIC_PHYADD_OFFSET));
for (;;) {
u32 mdic = in32(REG_MDIC);
if ((mdic & REG_MDIC_R) == 0) {
Processor::wait_check();
continue;
}
return (mdic & REG_MDIC_DATA_MASK) >> REG_MDIC_DATA_OFFSET;
}
}
void E1000ENetworkAdapter::write_phy_register(u8 phy_id, MDIO::Clause22::RegisterAddress address, u16 value)
{
// FIXME: Newer controllers (like the I211) don't have a PHYADD field in the MDIC register.
// They instead have it in a separate register.
out32(REG_MDIC, REG_MDIC_OP_WRITE | (to_underlying(address) << REG_MDIC_REGADD_OFFSET) | (value << REG_MDIC_DATA_OFFSET) | (phy_id << REG_MDIC_PHYADD_OFFSET));
while ((in32(REG_MDIC) & REG_MDIC_R) == 0)
Processor::wait_check();
}
UNMAP_AFTER_INIT void E1000ENetworkAdapter::detect_eeprom()
{
+11 -1
View File
@@ -12,13 +12,15 @@
#include <Kernel/Interrupts/IRQHandler.h>
#include <Kernel/Library/IOWindow.h>
#include <Kernel/Net/Intel/E1000NetworkAdapter.h>
#include <Kernel/Net/MDIO.h>
#include <Kernel/Net/NetworkAdapter.h>
#include <Kernel/Security/Random.h>
namespace Kernel {
class E1000ENetworkAdapter final
: public E1000NetworkAdapter {
: public E1000NetworkAdapter
, public MDIO::Clause22::Interface {
public:
static ErrorOr<bool> probe(PCI::DeviceIdentifier const&);
static ErrorOr<NonnullRefPtr<NetworkAdapter>> create(PCI::DeviceIdentifier const&);
@@ -28,6 +30,12 @@ public:
virtual StringView purpose() const override { return class_name(); }
protected:
// ^MDIO::Clause22::Interface
virtual void on_phy_link_status_change(MDIO::LinkStatus) override;
virtual u16 read_phy_register(u8 phy_id, MDIO::Clause22::RegisterAddress address) override;
virtual void write_phy_register(u8 phy_id, MDIO::Clause22::RegisterAddress address, u16 value) override;
private:
E1000ENetworkAdapter(StringView interface_name, PCI::DeviceIdentifier const&, InterruptNumber irq,
NonnullOwnPtr<IOWindow> registers_io_window, NonnullOwnPtr<Memory::Region> rx_buffer_region,
@@ -38,5 +46,7 @@ private:
virtual void detect_eeprom() override;
virtual u32 read_eeprom(u8 address) override;
RefPtr<Process> m_mdio_handling_process;
};
}