Ver Fonte

rewrite(e1000e): rewrite e1000e driver with rust

greatbridf há 3 meses atrás
pai
commit
c3b20946d2

+ 0 - 3
CMakeLists.txt

@@ -42,7 +42,6 @@ set(KERNEL_MAIN_SOURCES src/dev/builtin-chardev.cc
                         src/fs/fat.cpp
                         src/fs/tmpfs.cc
                         src/fs/procfs.cc
-                        src/driver/e1000e.cc
                         src/kinit.cpp
                         src/kernel/async/waitlist.cc
                         src/kernel/async/lock.cc
@@ -75,12 +74,10 @@ set(KERNEL_MAIN_SOURCES src/dev/builtin-chardev.cc
                         src/kernel/vfs/vfs.cc
                         src/kernel/signal.cpp
                         src/net/ethernet.cc
-                        src/net/netdev.cc
                         src/types/crc.cc
                         src/types/elf.cpp
                         src/types/libstdcpp.cpp
                         include/defs.hpp
-                        include/driver/e1000e.hpp
                         include/fs/fat.hpp
                         include/kernel/async/waitlist.hpp
                         include/kernel/async/lock.hpp

+ 1 - 0
Cargo.toml

@@ -14,3 +14,4 @@ bindgen = "0.70.1"
 
 [profile.dev]
 panic = "abort"
+opt-level = "s"

+ 1 - 1
build.rs

@@ -2,7 +2,7 @@ fn main() {
     println!("cargo:rustc-link-search=native=./build/gblibstdc++");
     println!("cargo:rustc-link-lib=static=gblibstdc++");
 
-    let headers = ["include/kernel/process.hpp"];
+    let headers = ["include/kernel/process.hpp", "include/kernel/hw/pci.hpp"];
 
     let bindings = bindgen::Builder::default()
         .use_core()

+ 0 - 229
include/driver/e1000e.hpp

@@ -1,229 +0,0 @@
-#include <defs.hpp>
-
-namespace hw::e1000e {
-
-// Register names
-// Control register
-constexpr u32 REG_CTRL = 0x00000;
-// Status register
-constexpr u32 REG_STATUS = 0x00008;
-// Interrupt cause register
-constexpr u32 REG_ICR = 0x000C0;
-// Interrupt mask set/clear register
-constexpr u32 REG_IMS = 0x000D0;
-// Interrupt mask clear register
-constexpr u32 REG_IMC = 0x000D8;
-
-// Receive control
-constexpr u32 REG_RCTL = 0x00100;
-
-// These registers are per-queue
-
-// Receive descriptor base address low
-constexpr u32 REG_RDBAL = 0x02800;
-// Receive descriptor base address high
-constexpr u32 REG_RDBAH = 0x02804;
-// Receive descriptor length
-constexpr u32 REG_RDLEN = 0x02808;
-// Receive descriptor head
-constexpr u32 REG_RDH = 0x02810;
-// Receive descriptor tail
-constexpr u32 REG_RDT = 0x02818;
-// Receive descriptor control
-constexpr u32 REG_RXDCTL = 0x02828;
-
-// Transmit control
-constexpr u32 REG_TCTL = 0x00400;
-
-// These registers are per-queue
-
-// Transmit descriptor base address low
-constexpr u32 REG_TDBAL = 0x03800;
-// Transmit descriptor base address high
-constexpr u32 REG_TDBAH = 0x03804;
-// Transmit descriptor length
-constexpr u32 REG_TDLEN = 0x03808;
-// Transmit descriptor head
-constexpr u32 REG_TDH = 0x03810;
-// Transmit descriptor tail
-constexpr u32 REG_TDT = 0x03818;
-// Transmit descriptor control
-constexpr u32 REG_TXDCTL = 0x03828;
-
-// Collision counter
-constexpr u32 REG_COLC = 0x04028;
-// Good packets received counter
-constexpr u32 REG_GPRC = 0x04074;
-// Multicast packets received counter
-constexpr u32 REG_MPRC = 0x0407C;
-// Good packets transmitted counter
-constexpr u32 REG_GPTC = 0x04080;
-// Good octets received counter low
-constexpr u32 REG_GORCL = 0x04088;
-// Good octets received counter high
-constexpr u32 REG_GORCH = 0x0408C;
-// Good octets transmitted counter low
-constexpr u32 REG_GOTCL = 0x04090;
-// Good octets transmitted counter high
-constexpr u32 REG_GOTCH = 0x04094;
-
-// Full-duplex
-constexpr u32 CTRL_FD = 0x00000001;
-// GIO master disable
-constexpr u32 CTRL_GIOD = 0x00000004;
-// Set link up
-constexpr u32 CTRL_SLU = 0x00000040;
-// Software reset
-constexpr u32 CTRL_RST = 0x04000000;
-// Receive flow control enable
-constexpr u32 CTRL_RFCE = 0x08000000;
-// Transmit flow control enable
-constexpr u32 CTRL_TFCE = 0x10000000;
-
-// Full-duplex
-constexpr u32 STATUS_FD = 0x00000001;
-// Link up
-constexpr u32 STATUS_LU = 0x00000002;
-// Transmit paused
-constexpr u32 STATUS_TXOFF = 0x00000010;
-// Link speed settings
-constexpr u32 STATUS_SPEED_MASK = 0x000000C0;
-constexpr u32 STATUS_SPEED_10M = 0x00000000;
-constexpr u32 STATUS_SPEED_100M = 0x00000040;
-constexpr u32 STATUS_SPEED_1000M = 0x00000080;
-// GIO master enable
-constexpr u32 STATUS_GIOE = 0x00080000;
-
-// Receive control enable
-constexpr u32 RCTL_EN = 0x00000002;
-// Store bad packets
-constexpr u32 RCTL_SBP = 0x00000004;
-// Unicast promiscuous mode
-constexpr u32 RCTL_UPE = 0x00000008;
-// Multicast promiscuous mode
-constexpr u32 RCTL_MPE = 0x00000010;
-// Long packet enable
-constexpr u32 RCTL_LPE = 0x00000020;
-// Loopback mode
-constexpr u32 RCTL_LBM_MASK = 0x000000C0;
-constexpr u32 RCTL_LBM_NO = 0x00000000;
-constexpr u32 RCTL_LBM_MAC = 0x00000040;
-// Receive descriptor minimum threshold size
-constexpr u32 RCTL_RDMTS_MASK = 0x00000300;
-constexpr u32 RCTL_RDMTS_HALF = 0x00000000;
-constexpr u32 RCTL_RDMTS_QUARTER = 0x00000100;
-constexpr u32 RCTL_RDMTS_EIGHTH = 0x00000200;
-// Receive descriptor type
-constexpr u32 RCTL_DTYP_MASK = 0x00000C00;
-constexpr u32 RCTL_DTYP_LEGACY = 0x00000000;
-constexpr u32 RCTL_DTYP_SPLIT = 0x00000400;
-// Multicast offset
-constexpr u32 RCTL_MO_MASK = 0x00003000;
-// Broadcast accept mode
-constexpr u32 RCTL_BAM = 0x00008000;
-// Receive buffer size
-constexpr u32 RCTL_BSIZE_MASK = 0x00030000;
-constexpr u32 RCTL_BSIZE_2048 = 0x00000000;
-constexpr u32 RCTL_BSIZE_1024 = 0x00010000;
-constexpr u32 RCTL_BSIZE_512 = 0x00020000;
-constexpr u32 RCTL_BSIZE_256 = 0x00030000;
-// VLAN filter enable
-constexpr u32 RCTL_VFE = 0x00040000;
-// Canonical form indicator enable
-constexpr u32 RCTL_CFIEN = 0x00080000;
-// Canonical form indicator bit value
-constexpr u32 RCTL_CFI = 0x00100000;
-// Discard pause frames
-constexpr u32 RCTL_DPF = 0x00400000;
-// Pass MAC control frames
-constexpr u32 RCTL_PMCF = 0x00800000;
-// Buffer size extension
-constexpr u32 RCTL_BSEX = 0x02000000;
-// Strip Ethernet CRC
-constexpr u32 RCTL_SECRC = 0x04000000;
-// Flexible buffer size
-constexpr u32 RCTL_FLXBUF_MASK = 0x78000000;
-constexpr u32 RCTL_BSIZE_16384 = (RCTL_BSIZE_1024 | RCTL_BSEX);
-constexpr u32 RCTL_BSIZE_8192 = (RCTL_BSIZE_512 | RCTL_BSEX);
-constexpr u32 RCTL_BSIZE_4096 = (RCTL_BSIZE_256 | RCTL_BSEX);
-
-// Transmit control enable
-constexpr u32 TCTL_EN = 0x00000002;
-// Pad short packets
-constexpr u32 TCTL_PSP = 0x00000008;
-// Collision threshold
-constexpr u32 TCTL_CT_MASK = 0x00000ff0;
-constexpr u32 TCTL_CT_SHIFT = 4;
-// Collision distance
-constexpr u32 TCTL_COLD_MASK = 0x003ff000;
-constexpr u32 TCTL_COLD_SHIFT = 12;
-// Software XOFF transmission
-constexpr u32 TCTL_SWXOFF = 0x00400000;
-// Packet burst enable
-constexpr u32 TCTL_PBE = 0x00800000;
-// Re-transmit on late collision
-constexpr u32 TCTL_RTLC = 0x01000000;
-
-// Transmit descriptor written back
-constexpr u32 ICR_TXDW = 0x00000001;
-// Link status change
-constexpr u32 ICR_LSC = 0x00000004;
-// Receive sequence error
-constexpr u32 ICR_RXSEQ = 0x00000008;
-// Receive descriptor minimum threshold
-constexpr u32 ICR_RXDMT0 = 0x00000010;
-// Receive overrun
-constexpr u32 ICR_RXO = 0x00000040;
-// Receive timer expired
-constexpr u32 ICR_RXT0 = 0x00000080;
-// MDIO access complete
-constexpr u32 ICR_MDAC = 0x00000200;
-// Transmit descriptor low minimum threshold
-constexpr u32 ICR_TXD_LOW = 0x00008000;
-// Small packet received
-constexpr u32 ICR_SRPD = 0x00010000;
-// ACK frame received
-constexpr u32 ICR_ACK = 0x0020000;
-// Management frame received
-constexpr u32 ICR_MNG = 0x0040000;
-// Other interrupt
-constexpr u32 ICR_OTHER = 0x01000000;
-// Interrupt asserted
-constexpr u32 ICR_INT = 0x80000000;
-
-constexpr u32 ICR_NORMAL =
-    (ICR_LSC | ICR_RXO | ICR_MDAC | ICR_SRPD | ICR_ACK | ICR_MNG);
-constexpr u32 ICR_UP = (ICR_TXDW | ICR_RXSEQ | ICR_RXDMT0 | ICR_RXT0);
-
-struct RxDescriptor {
-    u64 bufferAddress;
-    u16 length;
-    u16 volatile csum; // Checksum
-    u8 volatile status;
-    u8 volatile errors;
-    u16 vlan;
-};
-
-// Descriptor done
-constexpr u8 RXD_STAT_DD = 0x01;
-
-struct TxDescriptor {
-    u64 bufferAddress;
-    u16 length;
-    u8 cso; // Checksum offset
-    u8 cmd;
-    u8 volatile status;
-    u8 css; // Checksum start
-    u16 vlan;
-};
-
-// Descriptor done
-constexpr u8 TXD_STAT_DD = 0x01;
-// End of packet
-constexpr u8 TXD_CMD_EOP = 0x01;
-// Insert FCS
-constexpr u8 TXD_CMD_IFCS = 0x02;
-// Report status
-constexpr u8 TXD_CMD_RS = 0x08;
-
-} // namespace hw::e1000e

+ 1 - 0
include/kernel/hw/pci.hpp

@@ -83,5 +83,6 @@ class pci_device {
 using driver_t = std::function<int(pci_device&)>;
 
 int register_driver(uint16_t vendor, uint16_t device, driver_t drv);
+int register_driver_r(uint16_t vendor, uint16_t device, int (*drv)(pci_device*));
 
 } // namespace kernel::hw::pci

+ 1 - 0
src/driver.rs

@@ -0,0 +1 @@
+pub mod e1000e;

+ 0 - 389
src/driver/e1000e.cc

@@ -1,389 +0,0 @@
-#include <defs.hpp>
-
-#include <assert.h>
-#include <errno.h>
-
-#include <driver/e1000e.hpp>
-#include <kernel/hw/pci.hpp>
-#include <kernel/irq.hpp>
-#include <kernel/log.hpp>
-#include <kernel/mem/paging.hpp>
-#include <kernel/mem/phys.hpp>
-#include <kernel/module.hpp>
-#include <net/arp.hpp>
-#include <net/ethernet.hpp>
-#include <net/netdev.hpp>
-
-using namespace kernel::kmod;
-using namespace kernel::hw::pci;
-using namespace kernel::mem;
-using namespace net;
-
-using namespace hw::e1000e;
-
-constexpr int E1000E_RX_DESC_COUNT = 32;
-constexpr int E1000E_TX_DESC_COUNT = 32;
-
-class e1000eDevice : public virtual Netdev {
-    u8* base;
-
-    // rx at 0x0000, tx at 0x0200
-    paging::page* rtDescriptorPage;
-
-    int rxHead;
-    int rxTail;
-
-    int txHead;
-    int txTail;
-
-    void* at(long offset) const;
-    void write(long offset, u32 value) const;
-    u32 read(long offset) const;
-
-    physaddr<RxDescriptor> rxDescriptors() const;
-    physaddr<TxDescriptor> txDescriptors() const;
-
-    virtual int up() override;
-    virtual isize send(const u8* buffer, usize length) override;
-
-    int reset();
-    int getMacAddress();
-    int clearStats();
-    int clearRxTxDescriptorTable();
-
-    int setupRx();
-    int setupTx();
-
-   public:
-    static int probe(pci_device& dev);
-
-    e1000eDevice(pci_device& dev, u8* base);
-    virtual ~e1000eDevice();
-};
-
-e1000eDevice::e1000eDevice(pci_device& dev, u8* base)
-    : Netdev(dev)
-    , base(base)
-    , rtDescriptorPage(paging::alloc_page())
-    , rxHead(-1)
-    , rxTail(-1)
-    , txHead(-1)
-    , txTail(-1) {
-    auto pPage = physaddr<u8>{paging::page_to_pfn(rtDescriptorPage)};
-    memset(pPage, 0, 0x1000);
-
-    auto pRxDescriptors = this->rxDescriptors();
-    for (int i = 0; i < E1000E_RX_DESC_COUNT; i++) {
-        auto& desc = pRxDescriptors[i];
-
-        auto bufPage = paging::alloc_pages(2);
-        desc.bufferAddress = paging::page_to_pfn(bufPage);
-    }
-
-    clearRxTxDescriptorTable();
-
-    getMacAddress();
-}
-
-// TODO: make sure all transactions are complete before freeing resources
-e1000eDevice::~e1000eDevice() {
-    auto pRxDescriptors = this->rxDescriptors();
-    for (int i = 0; i < E1000E_RX_DESC_COUNT; i++) {
-        auto& desc = pRxDescriptors[i];
-
-        auto* bufPage = paging::pfn_to_page(desc.bufferAddress);
-        paging::free_pages(bufPage, 2);
-
-        desc.bufferAddress = 0;
-    }
-
-    paging::free_page(rtDescriptorPage);
-}
-
-void* e1000eDevice::at(long offset) const {
-    return base + offset;
-}
-
-void e1000eDevice::write(long offset, u32 value) const {
-    *reinterpret_cast<u32 volatile*>(at(offset)) = value;
-}
-
-u32 e1000eDevice::read(long offset) const {
-    return *reinterpret_cast<u32 volatile*>(at(offset));
-}
-
-physaddr<RxDescriptor> e1000eDevice::rxDescriptors() const {
-    auto pfn = paging::page_to_pfn(rtDescriptorPage);
-    return physaddr<RxDescriptor>(pfn);
-}
-
-physaddr<TxDescriptor> e1000eDevice::txDescriptors() const {
-    auto pfn = paging::page_to_pfn(rtDescriptorPage);
-    return physaddr<TxDescriptor>(pfn + 0x200);
-}
-
-int e1000eDevice::reset() {
-    // disable interrupts
-    write(REG_IMC, 0xffffffff);
-
-    // pcie master disable
-    u32 ctrl = read(REG_CTRL);
-    ctrl |= CTRL_GIOD;
-    write(REG_CTRL, ctrl);
-
-    while (test(read(REG_STATUS), STATUS_GIOE))
-        ;
-
-    ctrl = read(REG_CTRL);
-    ctrl |= CTRL_RST;
-    write(REG_CTRL, ctrl);
-
-    while (test(read(REG_CTRL), CTRL_RST))
-        ;
-
-    // disable interrupts again
-    write(REG_IMC, 0xffffffff);
-
-    return 0;
-}
-
-int e1000eDevice::getMacAddress() {
-    memcpy(&mac, at(0x5400), 6);
-    return 0;
-}
-
-int e1000eDevice::setupRx() {
-    auto pRxDescriptors = this->rxDescriptors();
-
-    write(REG_RDBAL, pRxDescriptors.phys() & 0xffffffff);
-    write(REG_RDBAH, pRxDescriptors.phys() >> 32);
-
-    write(REG_RDLEN, E1000E_RX_DESC_COUNT * sizeof(RxDescriptor));
-
-    write(REG_RDH, 0);
-    rxHead = 0;
-
-    write(REG_RDT, E1000E_RX_DESC_COUNT - 1);
-    rxTail = E1000E_RX_DESC_COUNT - 1;
-
-    write(REG_RCTL, RCTL_EN | RCTL_UPE | RCTL_MPE | RCTL_LPE | RCTL_LBM_NO |
-                        RCTL_DTYP_LEGACY | RCTL_BAM | RCTL_BSIZE_8192 |
-                        RCTL_SECRC);
-
-    return 0;
-}
-
-int e1000eDevice::setupTx() {
-    auto pTxDescriptors = this->txDescriptors();
-
-    write(REG_TDBAL, pTxDescriptors.phys() & 0xffffffff);
-    write(REG_TDBAH, pTxDescriptors.phys() >> 32);
-
-    write(REG_TDLEN, E1000E_TX_DESC_COUNT * sizeof(TxDescriptor));
-
-    write(REG_TDH, 0);
-    txHead = 0;
-
-    write(REG_TDT, 0);
-    txTail = 0;
-
-    write(REG_TCTL, TCTL_EN | TCTL_PSP | (15 << TCTL_CT_SHIFT) |
-                        (64 << TCTL_COLD_SHIFT) | TCTL_RTLC);
-
-    return 0;
-}
-
-int e1000eDevice::up() {
-    // set link up
-    u32 ctrl = read(REG_CTRL);
-    u32 stat = read(REG_STATUS);
-
-    if (!test(ctrl, CTRL_SLU) || !test(stat, STATUS_LU))
-        return -EIO;
-
-    // auto negotiation speed
-    switch (stat & STATUS_SPEED_MASK) {
-        case STATUS_SPEED_10M:
-            setLinkSpeed(NETDEV_SPEED_10M);
-            break;
-        case STATUS_SPEED_100M:
-            setLinkSpeed(NETDEV_SPEED_100M);
-            break;
-        case STATUS_SPEED_1000M:
-            setLinkSpeed(NETDEV_SPEED_1000M);
-            break;
-        default:
-            return -EINVAL;
-    }
-
-    // clear multicast table
-    for (int i = 0; i < 128; i += 4)
-        write(0x5200 + i, 0);
-
-    clearStats();
-
-    // enable interrupts
-    write(REG_IMS, ICR_NORMAL | ICR_UP);
-
-    // read to clear any pending interrupts
-    read(REG_ICR);
-
-    kernel::irq::register_handler(
-        device.header_type0().interrupt_line, [this]() {
-            auto cause = read(REG_ICR);
-
-            if (!test(cause, ICR_INT))
-                return;
-
-            rxHead = read(REG_RDH);
-
-            auto nextTail = (rxTail + 1) % E1000E_RX_DESC_COUNT;
-            while (nextTail != rxHead) {
-                auto pRxDescriptors = rxDescriptors();
-                auto& desc = pRxDescriptors[nextTail];
-
-                assert(desc.status & RXD_STAT_DD);
-
-                auto pBuf = physaddr<u8>{desc.bufferAddress};
-                kmsg("==== e1000e: received packet ====");
-
-                char buf[256];
-                for (int i = 0; i < desc.length; i++) {
-                    if (i && i % 16 == 0)
-                        kmsg("");
-
-                    snprintf(buf, sizeof(buf), "%x ", pBuf[i]);
-                    kernel::tty::console->print(buf);
-                }
-
-                kmsg("\n==== e1000e: end of packet ====");
-
-                desc.status = 0;
-                rxTail = nextTail;
-                nextTail = (nextTail + 1) % E1000E_RX_DESC_COUNT;
-            }
-
-            write(REG_RDT, rxTail);
-        });
-
-    int ret = setupRx();
-    if (ret != 0)
-        return ret;
-
-    ret = setupTx();
-    if (ret != 0)
-        return ret;
-
-    status &= ~NETDEV_DOWN;
-    status |= NETDEV_UP;
-
-    return 0;
-}
-
-isize e1000eDevice::send(const u8* buffer, usize length) {
-    auto txTailNext = (txTail + 1) % E1000E_TX_DESC_COUNT;
-    if (txTailNext == txHead)
-        return -EAGAIN;
-
-    auto pTxDescriptors = this->txDescriptors();
-    auto& desc = pTxDescriptors[txTail];
-
-    if (!(desc.status & TXD_STAT_DD))
-        return -EIO;
-
-    auto bufPage = paging::alloc_page();
-    auto pPage = physaddr<u8>{paging::page_to_pfn(bufPage)};
-    memcpy(pPage, buffer, length);
-
-    desc.bufferAddress = pPage.phys();
-    desc.length = length;
-    desc.cmd = TXD_CMD_EOP | TXD_CMD_IFCS | TXD_CMD_RS;
-    desc.status = 0;
-
-    txTail = txTailNext;
-    write(REG_TDT, txTailNext);
-
-    while (!test(desc.status, TXD_STAT_DD))
-        ;
-
-    return 0;
-}
-
-int e1000eDevice::clearStats() {
-    write(REG_COLC, 0);
-    write(REG_GPRC, 0);
-    write(REG_MPRC, 0);
-    write(REG_GPTC, 0);
-    write(REG_GORCL, 0);
-    write(REG_GORCH, 0);
-    write(REG_GOTCL, 0);
-    write(REG_GOTCH, 0);
-
-    return 0;
-}
-
-int e1000eDevice::clearRxTxDescriptorTable() {
-    auto pRxDescriptors = this->rxDescriptors();
-    for (int i = 0; i < E1000E_RX_DESC_COUNT; i++) {
-        auto& desc = pRxDescriptors[i];
-        desc.status = 0;
-    }
-
-    auto pTxDescriptors = this->txDescriptors();
-    for (int i = 0; i < E1000E_TX_DESC_COUNT; i++) {
-        auto& desc = pTxDescriptors[i];
-        desc.status = TXD_STAT_DD;
-    }
-
-    return 0;
-}
-
-int e1000eDevice::probe(pci_device& dev) {
-    auto bar0 = dev.header_type0().bars[0];
-    if ((bar0 & 0xf) != 0)
-        return -EINVAL;
-
-    dev.enableBusMastering();
-
-    auto baseAddress = physaddr<uint8_t, false>{bar0 & ~0xf};
-    auto e1000ePointer = std::make_unique<e1000eDevice>(dev, baseAddress);
-
-    int ret = e1000ePointer->reset();
-    if (ret != 0)
-        return ret;
-
-    ret = e1000ePointer->up();
-    if (ret != 0)
-        return ret;
-
-    return registerNetdev(std::move(e1000ePointer));
-}
-
-class e1000e_module : public virtual kmod {
-   public:
-    e1000e_module() : kmod("e1000e") {}
-
-    virtual int init() override {
-        const int device_id[] = {
-            0x100e,
-            0x10d3,
-            0x10ea,
-            0x153a,
-        };
-
-        for (auto id : device_id) {
-            auto ret = kernel::hw::pci::register_driver(0x8086, id,
-                                                        e1000eDevice::probe);
-
-            // TODO: in case any of the devices fail to register,
-            // we should return an error code and cleanup these
-            // device drivers
-            if (ret != 0)
-                return ret;
-        }
-
-        return 0;
-    }
-};
-
-INTERNAL_MODULE(e1000e, e1000e_module);

+ 433 - 0
src/driver/e1000e.rs

@@ -0,0 +1,433 @@
+use crate::bindings::root::kernel::hw::pci;
+use crate::kernel::interrupt::register_irq_handler;
+use crate::kernel::mem::paging::copy_to_page;
+use crate::kernel::mem::{paging, phys};
+use crate::net::netdev;
+use alloc::boxed::Box;
+use alloc::vec::Vec;
+use paging::Page;
+use phys::{NoCachePP, PhysPtr};
+
+use crate::bindings::root::{EAGAIN, EINVAL, EIO};
+
+mod defs;
+
+#[repr(C)]
+struct RxDescriptor {
+    buffer: u64,
+    length: u16,
+    checksum: u16,
+    status: u8,
+    errors: u8,
+    vlan: u16,
+}
+
+#[repr(C)]
+struct TxDescriptor {
+    buffer: u64,
+    length: u16,
+    cso: u8, // Checksum offset
+    cmd: u8,
+    status: u8,
+    css: u8, // Checksum start
+    vlan: u16,
+}
+
+const RX_DESC_SIZE: usize = 32;
+const TX_DESC_SIZE: usize = 32;
+
+struct E1000eDev {
+    mac: netdev::Mac,
+    status: netdev::LinkStatus,
+    speed: netdev::LinkSpeed,
+    id: u32,
+
+    base: NoCachePP,
+    rt_desc_page: Page,
+    rx_head: Option<u32>,
+    rx_tail: Option<u32>,
+    tx_tail: Option<u32>,
+
+    rx_buffers: Option<Box<Vec<Page>>>,
+    tx_buffers: Option<Box<Vec<Page>>>,
+}
+
+fn test(val: u32, bit: u32) -> bool {
+    (val & bit) == bit
+}
+
+impl netdev::Netdev for E1000eDev {
+    fn mac(&self) -> netdev::Mac {
+        self.mac
+    }
+
+    fn link_status(&self) -> netdev::LinkStatus {
+        self.status
+    }
+
+    fn link_speed(&self) -> netdev::LinkSpeed {
+        self.speed
+    }
+
+    fn id(&self) -> u32 {
+        self.id
+    }
+
+    fn up(&mut self) -> Result<(), u32> {
+        let ctrl = self.read(defs::REG_CTRL);
+        let status = self.read(defs::REG_STAT);
+
+        // check link up
+        if !test(ctrl, defs::CTRL_SLU) || !test(status, defs::STAT_LU) {
+            return Err(EIO);
+        }
+
+        // auto negotiation of speed
+        match status & defs::STAT_SPEED_MASK {
+            defs::STAT_SPEED_10M => self.speed = netdev::LinkSpeed::Speed10M,
+            defs::STAT_SPEED_100M => self.speed = netdev::LinkSpeed::Speed100M,
+            defs::STAT_SPEED_1000M => {
+                self.speed = netdev::LinkSpeed::Speed1000M
+            }
+            _ => return Err(EINVAL),
+        }
+
+        // clear multicast table
+        for i in (0..128).step_by(4) {
+            self.write(defs::REG_MTA + i, 0);
+        }
+
+        self.clear_stats()?;
+
+        // setup interrupt handler
+        let device = netdev::get_netdev(self.id).unwrap();
+        let handler = move || {
+            device.lock().fire().unwrap();
+        };
+
+        register_irq_handler(0xb, handler)?;
+
+        // enable interrupts
+        self.write(defs::REG_IMS, defs::ICR_NORMAL | defs::ICR_UP);
+
+        // read to clear any pending interrupts
+        self.read(defs::REG_ICR);
+
+        self.setup_rx()?;
+        self.setup_tx()?;
+
+        self.status = netdev::LinkStatus::Up;
+
+        Ok(())
+    }
+
+    fn fire(&mut self) -> Result<(), u32> {
+        let cause = self.read(defs::REG_ICR);
+        if !test(cause, defs::ICR_INT) {
+            return Ok(());
+        }
+
+        loop {
+            let tail = self.rx_tail.ok_or(EIO)?;
+            let next_tail = (tail + 1) % RX_DESC_SIZE as u32;
+
+            if next_tail == self.read(defs::REG_RDH) {
+                break;
+            }
+
+            let ref mut desc = self.rx_desc_table()[next_tail as usize];
+            if !test(desc.status as u32, defs::RXD_STAT_DD as u32) {
+                Err(EIO)?;
+            }
+
+            desc.status = 0;
+            let len = desc.length as usize;
+
+            let buffers = self.rx_buffers.as_mut().ok_or(EIO)?;
+            let data = unsafe {
+                core::slice::from_raw_parts(
+                    buffers[next_tail as usize].as_cached().as_ptr::<u8>(),
+                    len,
+                )
+            };
+
+            use crate::{dont_check, print, println};
+            dont_check!(println!("==== e1000e: received packet ===="));
+
+            for i in 0..len {
+                if i % 16 == 0 {
+                    dont_check!(println!());
+                }
+                dont_check!(print!("{:02x} ", data[i]));
+            }
+
+            dont_check!(println!("\n\n====  e1000e: end of packet  ===="));
+            self.rx_tail = Some(next_tail);
+        }
+
+        Ok(())
+    }
+
+    fn send(&mut self, buf: &[u8]) -> Result<(), u32> {
+        let tail = self.tx_tail.ok_or(EIO)?;
+        let head = self.read(defs::REG_TDH);
+        let next_tail = (tail + 1) % TX_DESC_SIZE as u32;
+
+        if next_tail == head {
+            return Err(EAGAIN);
+        }
+
+        let ref mut desc = self.tx_desc_table()[tail as usize];
+        if !test(desc.status as u32, defs::TXD_STAT_DD as u32) {
+            return Err(EIO);
+        }
+
+        let buffer_page = Page::alloc_one();
+        copy_to_page(buf, &buffer_page)?;
+
+        desc.buffer = buffer_page.as_phys() as u64;
+        desc.length = buf.len() as u16;
+        desc.cmd = defs::TXD_CMD_EOP | defs::TXD_CMD_IFCS | defs::TXD_CMD_RS;
+        desc.status = 0;
+
+        self.tx_tail = Some(next_tail);
+        self.write(defs::REG_TDT, next_tail);
+
+        // TODO: check if the packets are sent and update self.tx_head state
+
+        Ok(())
+    }
+}
+
+impl E1000eDev {
+    fn setup_rx(&mut self) -> Result<(), u32> {
+        if !self.rx_head.is_none() || !self.rx_tail.is_none() {
+            return Err(EINVAL);
+        }
+
+        let addr = self.rt_desc_page.as_phys();
+
+        self.write(defs::REG_RDBAL, addr as u32);
+        self.write(defs::REG_RDBAH, (addr >> 32) as u32);
+
+        self.write(
+            defs::REG_RDLEN,
+            (RX_DESC_SIZE * size_of::<RxDescriptor>()) as u32,
+        );
+
+        self.write(defs::REG_RDH, 0);
+        self.write(defs::REG_RDT, RX_DESC_SIZE as u32 - 1);
+
+        self.rx_head = Some(0);
+        self.rx_tail = Some(RX_DESC_SIZE as u32 - 1);
+
+        self.write(
+            defs::REG_RCTL,
+            defs::RCTL_EN
+                | defs::RCTL_MPE
+                | defs::RCTL_LPE
+                | defs::RCTL_LBM_NO
+                | defs::RCTL_DTYP_LEGACY
+                | defs::RCTL_BAM
+                | defs::RCTL_BSIZE_8192
+                | defs::RCTL_SECRC,
+        );
+
+        Ok(())
+    }
+
+    fn setup_tx(&mut self) -> Result<(), u32> {
+        if !self.tx_tail.is_none() {
+            return Err(EINVAL);
+        }
+
+        let addr = self.rt_desc_page.as_phys() + 0x200;
+
+        self.write(defs::REG_TDBAL, addr as u32);
+        self.write(defs::REG_TDBAH, (addr >> 32) as u32);
+
+        self.write(
+            defs::REG_TDLEN,
+            (TX_DESC_SIZE * size_of::<TxDescriptor>()) as u32,
+        );
+
+        self.write(defs::REG_TDH, 0);
+        self.write(defs::REG_TDT, 0);
+
+        self.tx_tail = Some(0);
+
+        self.write(
+            defs::REG_TCTL,
+            defs::TCTL_EN
+                | defs::TCTL_PSP
+                | (15 << defs::TCTL_CT_SHIFT)
+                | (64 << defs::TCTL_COLD_SHIFT)
+                | defs::TCTL_RTLC,
+        );
+
+        Ok(())
+    }
+
+    fn reset(&self) -> Result<(), u32> {
+        // disable interrupts so we won't mess things up
+        self.write(defs::REG_IMC, 0xffffffff);
+
+        let ctrl = self.read(defs::REG_CTRL);
+        self.write(defs::REG_CTRL, ctrl | defs::CTRL_GIOD);
+
+        while self.read(defs::REG_STAT) & defs::STAT_GIOE != 0 {
+            // wait for link up
+        }
+
+        let ctrl = self.read(defs::REG_CTRL);
+        self.write(defs::REG_CTRL, ctrl | defs::CTRL_RST);
+
+        while self.read(defs::REG_CTRL) & defs::CTRL_RST != 0 {
+            // wait for reset
+        }
+
+        // disable interrupts again
+        self.write(defs::REG_IMC, 0xffffffff);
+
+        Ok(())
+    }
+
+    fn clear_stats(&self) -> Result<(), u32> {
+        self.write(defs::REG_COLC, 0);
+        self.write(defs::REG_GPRC, 0);
+        self.write(defs::REG_MPRC, 0);
+        self.write(defs::REG_GPTC, 0);
+        self.write(defs::REG_GORCL, 0);
+        self.write(defs::REG_GORCH, 0);
+        self.write(defs::REG_GOTCL, 0);
+        self.write(defs::REG_GOTCH, 0);
+        Ok(())
+    }
+
+    pub fn new(base: NoCachePP) -> Result<Self, u32> {
+        let page = Page::alloc_one();
+
+        page.zero();
+
+        let mut dev = Self {
+            mac: [0; 6],
+            status: netdev::LinkStatus::Down,
+            speed: netdev::LinkSpeed::SpeedUnknown,
+            id: netdev::alloc_id(),
+            base,
+            rt_desc_page: page,
+            rx_head: None,
+            rx_tail: None,
+            tx_tail: None,
+            rx_buffers: None,
+            tx_buffers: None,
+        };
+
+        dev.reset()?;
+
+        dev.mac = unsafe { dev.base.offset(0x5400).as_ptr::<[u8; 6]>().read() };
+        dev.tx_buffers = Some(Box::new(Vec::with_capacity(TX_DESC_SIZE)));
+
+        let mut rx_buffers = Box::new(Vec::with_capacity(RX_DESC_SIZE));
+
+        for index in 0..RX_DESC_SIZE {
+            let page = Page::alloc_many(2);
+
+            let ref mut desc = dev.rx_desc_table()[index];
+            desc.buffer = page.as_phys() as u64;
+            desc.status = 0;
+
+            rx_buffers.push(page);
+        }
+
+        for index in 0..TX_DESC_SIZE {
+            let ref mut desc = dev.tx_desc_table()[index];
+            desc.status = defs::TXD_STAT_DD;
+        }
+
+        dev.rx_buffers = Some(rx_buffers);
+
+        Ok(dev)
+    }
+
+    fn read(&self, offset: u32) -> u32 {
+        unsafe {
+            self.base
+                .offset(offset as isize)
+                .as_ptr::<u32>()
+                .read_volatile()
+        }
+    }
+
+    fn write(&self, offset: u32, value: u32) {
+        unsafe {
+            self.base
+                .offset(offset as isize)
+                .as_ptr::<u32>()
+                .write_volatile(value)
+        }
+    }
+
+    fn rx_desc_table<'lt>(&'lt self) -> &'lt mut [RxDescriptor; RX_DESC_SIZE] {
+        self.rt_desc_page.as_cached().as_mut()
+    }
+
+    fn tx_desc_table<'lt>(&'lt self) -> &'lt mut [TxDescriptor; TX_DESC_SIZE] {
+        self.rt_desc_page.as_cached().offset(0x200).as_mut()
+    }
+}
+
+impl Drop for E1000eDev {
+    fn drop(&mut self) {
+        assert_eq!(self.status, netdev::LinkStatus::Down);
+
+        if let Some(_) = self.rx_buffers.take() {}
+
+        // TODO: we should wait until all packets are sent
+        if let Some(_) = self.tx_buffers.take() {}
+
+        let _ = self.rt_desc_page;
+    }
+}
+
+impl pci::pci_device {
+    fn header0(&self) -> &pci::device_header_type0 {
+        unsafe { self.header_type0().as_ref() }.unwrap()
+    }
+}
+
+fn do_probe_device(dev: &mut pci::pci_device) -> Result<(), u32> {
+    let bar0 = dev.header0().bars[0];
+
+    if bar0 & 0xf != 0 {
+        return Err(EINVAL);
+    }
+
+    unsafe { dev.enableBusMastering() };
+
+    let base = NoCachePP::new((bar0 & !0xf) as usize);
+    let e1000e = E1000eDev::new(base)?;
+
+    netdev::register_netdev(e1000e)?;
+
+    Ok(())
+}
+
+unsafe extern "C" fn probe_device(dev: *mut pci::pci_device) -> i32 {
+    let dev = dev.as_mut().unwrap();
+    match do_probe_device(dev) {
+        Ok(_) => 0,
+        Err(e) => -(e as i32),
+    }
+}
+
+pub fn register_e1000e_driver() {
+    let dev_ids = [0x100e, 0x10d3, 0x10ea, 0x153a];
+
+    for id in dev_ids.into_iter() {
+        let ret =
+            unsafe { pci::register_driver_r(0x8086, id, Some(probe_device)) };
+
+        assert_eq!(ret, 0);
+    }
+}

+ 202 - 0
src/driver/e1000e/defs.rs

@@ -0,0 +1,202 @@
+// Register names
+// Control register
+pub const REG_CTRL: u32 = 0x00000;
+// Status register
+pub const REG_STAT: u32 = 0x00008;
+// Interrupt cause register
+pub const REG_ICR: u32 = 0x000C0;
+// Interrupt mask set/clear register
+pub const REG_IMS: u32 = 0x000D0;
+// Interrupt mask clear register
+pub const REG_IMC: u32 = 0x000D8;
+// Multicast table array start
+pub const REG_MTA: u32 = 0x05200;
+
+// Receive control
+pub const REG_RCTL: u32 = 0x00100;
+
+// These registers are per-queue
+
+// Receive descriptor base address low
+pub const REG_RDBAL: u32 = 0x02800;
+// Receive descriptor base address high
+pub const REG_RDBAH: u32 = 0x02804;
+// Receive descriptor length
+pub const REG_RDLEN: u32 = 0x02808;
+// Receive descriptor head
+pub const REG_RDH: u32 = 0x02810;
+// Receive descriptor tail
+pub const REG_RDT: u32 = 0x02818;
+// Receive descriptor control
+pub const REG_RXDCTL: u32 = 0x02828;
+
+// Transmit control
+pub const REG_TCTL: u32 = 0x00400;
+
+// These registers are per-queue
+
+// Transmit descriptor base address low
+pub const REG_TDBAL: u32 = 0x03800;
+// Transmit descriptor base address high
+pub const REG_TDBAH: u32 = 0x03804;
+// Transmit descriptor length
+pub const REG_TDLEN: u32 = 0x03808;
+// Transmit descriptor head
+pub const REG_TDH: u32 = 0x03810;
+// Transmit descriptor tail
+pub const REG_TDT: u32 = 0x03818;
+// Transmit descriptor control
+pub const REG_TXDCTL: u32 = 0x03828;
+
+// Collision counter
+pub const REG_COLC: u32 = 0x04028;
+// Good packets received counter
+pub const REG_GPRC: u32 = 0x04074;
+// Multicast packets received counter
+pub const REG_MPRC: u32 = 0x0407C;
+// Good packets transmitted counter
+pub const REG_GPTC: u32 = 0x04080;
+// Good octets received counter low
+pub const REG_GORCL: u32 = 0x04088;
+// Good octets received counter high
+pub const REG_GORCH: u32 = 0x0408C;
+// Good octets transmitted counter low
+pub const REG_GOTCL: u32 = 0x04090;
+// Good octets transmitted counter high
+pub const REG_GOTCH: u32 = 0x04094;
+
+// Full-duplex
+pub const CTRL_FD: u32 = 0x00000001;
+// GIO master disable
+pub const CTRL_GIOD: u32 = 0x00000004;
+// Set link up
+pub const CTRL_SLU: u32 = 0x00000040;
+// Software reset
+pub const CTRL_RST: u32 = 0x04000000;
+// Receive flow control enable
+pub const CTRL_RFCE: u32 = 0x08000000;
+// Transmit flow control enable
+pub const CTRL_TFCE: u32 = 0x10000000;
+
+// Full-duplex
+pub const STAT_FD: u32 = 0x00000001;
+// Link up
+pub const STAT_LU: u32 = 0x00000002;
+// Transmit paused
+pub const STAT_TXOFF: u32 = 0x00000010;
+// Link speed settings
+pub const STAT_SPEED_MASK: u32 = 0x000000C0;
+pub const STAT_SPEED_10M: u32 = 0x00000000;
+pub const STAT_SPEED_100M: u32 = 0x00000040;
+pub const STAT_SPEED_1000M: u32 = 0x00000080;
+// GIO master enable
+pub const STAT_GIOE: u32 = 0x00080000;
+
+// Receive control enable
+pub const RCTL_EN: u32 = 0x00000002;
+// Store bad packets
+pub const RCTL_SBP: u32 = 0x00000004;
+// Unicast promiscuous mode
+pub const RCTL_UPE: u32 = 0x00000008;
+// Multicast promiscuous mode
+pub const RCTL_MPE: u32 = 0x00000010;
+// Long packet enable
+pub const RCTL_LPE: u32 = 0x00000020;
+// Loopback mode
+pub const RCTL_LBM_MASK: u32 = 0x000000C0;
+pub const RCTL_LBM_NO: u32 = 0x00000000;
+pub const RCTL_LBM_MAC: u32 = 0x00000040;
+// Receive descriptor minimum threshold size
+pub const RCTL_RDMTS_MASK: u32 = 0x00000300;
+pub const RCTL_RDMTS_HALF: u32 = 0x00000000;
+pub const RCTL_RDMTS_QUARTER: u32 = 0x00000100;
+pub const RCTL_RDMTS_EIGHTH: u32 = 0x00000200;
+// Receive descriptor type
+pub const RCTL_DTYP_MASK: u32 = 0x00000C00;
+pub const RCTL_DTYP_LEGACY: u32 = 0x00000000;
+pub const RCTL_DTYP_SPLIT: u32 = 0x00000400;
+// Multicast offset
+pub const RCTL_MO_MASK: u32 = 0x00003000;
+// Broadcast accept mode
+pub const RCTL_BAM: u32 = 0x00008000;
+// Receive buffer size
+pub const RCTL_BSIZE_MASK: u32 = 0x00030000;
+pub const RCTL_BSIZE_2048: u32 = 0x00000000;
+pub const RCTL_BSIZE_1024: u32 = 0x00010000;
+pub const RCTL_BSIZE_512: u32 = 0x00020000;
+pub const RCTL_BSIZE_256: u32 = 0x00030000;
+// VLAN filter enable
+pub const RCTL_VFE: u32 = 0x00040000;
+// Canonical form indicator enable
+pub const RCTL_CFIEN: u32 = 0x00080000;
+// Canonical form indicator bit value
+pub const RCTL_CFI: u32 = 0x00100000;
+// Discard pause frames
+pub const RCTL_DPF: u32 = 0x00400000;
+// Pass MAC control frames
+pub const RCTL_PMCF: u32 = 0x00800000;
+// Buffer size extension
+pub const RCTL_BSEX: u32 = 0x02000000;
+// Strip Ethernet CRC
+pub const RCTL_SECRC: u32 = 0x04000000;
+// Flexible buffer size
+pub const RCTL_FLXBUF_MASK: u32 = 0x78000000;
+pub const RCTL_BSIZE_16384: u32 = RCTL_BSIZE_1024 | RCTL_BSEX;
+pub const RCTL_BSIZE_8192: u32 = RCTL_BSIZE_512 | RCTL_BSEX;
+pub const RCTL_BSIZE_4096: u32 = RCTL_BSIZE_256 | RCTL_BSEX;
+
+// Transmit control enable
+pub const TCTL_EN: u32 = 0x00000002;
+// Pad short packets
+pub const TCTL_PSP: u32 = 0x00000008;
+// Collision threshold
+pub const TCTL_CT_MASK: u32 = 0x00000ff0;
+pub const TCTL_CT_SHIFT: u32 = 4;
+// Collision distance
+pub const TCTL_COLD_MASK: u32 = 0x003ff000;
+pub const TCTL_COLD_SHIFT: u32 = 12;
+// Software XOFF transmission
+pub const TCTL_SWXOFF: u32 = 0x00400000;
+// Packet burst enable
+pub const TCTL_PBE: u32 = 0x00800000;
+// Re-transmit on late collision
+pub const TCTL_RTLC: u32 = 0x01000000;
+
+// Transmit descriptor written back
+pub const ICR_TXDW: u32 = 0x00000001;
+// Link status change
+pub const ICR_LSC: u32 = 0x00000004;
+// Receive sequence error
+pub const ICR_RXSEQ: u32 = 0x00000008;
+// Receive descriptor minimum threshold
+pub const ICR_RXDMT0: u32 = 0x00000010;
+// Receive overrun
+pub const ICR_RXO: u32 = 0x00000040;
+// Receive timer expired
+pub const ICR_RXT0: u32 = 0x00000080;
+// MDIO access complete
+pub const ICR_MDAC: u32 = 0x00000200;
+// Transmit descriptor low minimum threshold
+pub const ICR_TXD_LOW: u32 = 0x00008000;
+// Small packet received
+pub const ICR_SRPD: u32 = 0x00010000;
+// ACK frame received
+pub const ICR_ACK: u32 = 0x0020000;
+// Management frame received
+pub const ICR_MNG: u32 = 0x0040000;
+// Other interrupt
+pub const ICR_OTHER: u32 = 0x01000000;
+// Interrupt asserted
+pub const ICR_INT: u32 = 0x80000000;
+
+pub const ICR_NORMAL: u32 =
+    ICR_LSC | ICR_RXO | ICR_MDAC | ICR_SRPD | ICR_ACK | ICR_MNG;
+pub const ICR_UP: u32 = ICR_TXDW | ICR_RXSEQ | ICR_RXDMT0 | ICR_RXT0;
+
+pub const RXD_STAT_DD: u8 = 0x01;
+
+pub const TXD_STAT_DD: u8 = 0x01;
+
+pub const TXD_CMD_EOP: u8 = 0x01;
+pub const TXD_CMD_IFCS: u8 = 0x02;
+pub const TXD_CMD_RS: u8 = 0x08;

+ 1 - 0
src/kernel.rs

@@ -1,2 +1,3 @@
 pub mod console;
+pub mod interrupt;
 pub mod mem;

+ 6 - 0
src/kernel/hw/pci.cc

@@ -75,6 +75,12 @@ int register_driver(uint16_t vendor, uint16_t device, driver_t drv) {
     return 0;
 }
 
+int register_driver_r(uint16_t vendor, uint16_t device,
+                      int (*drv)(pci_device*)) {
+    return register_driver(vendor, device,
+                           [=](pci_device& dev) -> int { return drv(&dev); });
+}
+
 } // namespace kernel::hw::pci
 
 namespace kernel::kinit {

+ 7 - 3
src/kernel/interrupt.cpp

@@ -118,17 +118,21 @@ static inline void fault_handler(interrupt_stack* context, mmx_registers*) {
     freeze();
 }
 
+extern "C" void irq_handler_rust(int irqno);
+
 static inline void irq_handler(interrupt_stack* context, mmx_registers*) {
     int irqno = context->int_no - 0x20;
 
     constexpr uint8_t PIC_EOI = 0x20;
 
+    for (const auto& handler : s_irq_handlers[irqno])
+        handler();
+
+    irq_handler_rust(irqno);
+
     port_pic1_command = PIC_EOI;
     if (irqno >= 8)
         port_pic2_command = PIC_EOI;
-
-    for (const auto& handler : s_irq_handlers[irqno])
-        handler();
 }
 
 extern "C" void interrupt_handler(interrupt_stack* context,

+ 44 - 0
src/kernel/interrupt.rs

@@ -0,0 +1,44 @@
+use alloc::boxed::Box;
+use alloc::vec;
+use alloc::vec::Vec;
+
+use crate::bindings::root::EINVAL;
+
+static mut IRQ_HANDLERS: spin::Mutex<[Option<Vec<Box<dyn Fn()>>>; 16]> =
+    spin::Mutex::new([const { None }; 16]);
+
+#[no_mangle]
+pub extern "C" fn irq_handler_rust(irqno: core::ffi::c_int) {
+    assert!(irqno >= 0 && irqno < 16);
+
+    let handlers = unsafe { IRQ_HANDLERS.lock() };
+
+    match handlers[irqno as usize] {
+        Some(ref handlers) => {
+            for handler in handlers {
+                handler();
+            }
+        }
+        None => {}
+    }
+}
+
+pub fn register_irq_handler<F>(irqno: i32, handler: F) -> Result<(), u32>
+where
+    F: Fn() + 'static,
+{
+    if irqno < 0 || irqno >= 16 {
+        return Err(EINVAL);
+    }
+
+    let mut handlers = unsafe { IRQ_HANDLERS.lock() };
+
+    match handlers[irqno as usize] {
+        Some(ref mut handlers) => handlers.push(Box::new(handler)),
+        None => {
+            handlers[irqno as usize].replace(vec![Box::new(handler)]);
+        }
+    }
+
+    Ok(())
+}

+ 1 - 0
src/kernel/mem.rs

@@ -1 +1,2 @@
 pub mod paging;
+pub mod phys;

+ 123 - 0
src/kernel/mem/paging.rs

@@ -1,4 +1,127 @@
+use crate::bindings::root::EFAULT;
 use crate::io::Buffer;
+use crate::kernel::mem::phys;
+use core::fmt;
+
+pub struct Page {
+    page_ptr: *mut crate::bindings::root::kernel::mem::paging::page,
+    order: u32,
+}
+
+impl Page {
+    pub fn alloc_one() -> Self {
+        use crate::bindings::root::kernel::mem::paging::alloc_page;
+        let page_ptr = unsafe { alloc_page() };
+
+        Self { page_ptr, order: 0 }
+    }
+
+    pub fn alloc_many(order: u32) -> Self {
+        use crate::bindings::root::kernel::mem::paging::alloc_pages;
+        let page_ptr = unsafe { alloc_pages(order) };
+
+        Self { page_ptr, order }
+    }
+
+    pub fn len(&self) -> usize {
+        1 << (self.order + 12)
+    }
+
+    pub fn as_phys(&self) -> usize {
+        use crate::bindings::root::kernel::mem::paging::page_to_pfn;
+
+        unsafe { page_to_pfn(self.page_ptr) }
+    }
+
+    pub fn as_cached(&self) -> phys::CachedPP {
+        phys::CachedPP::new(self.as_phys())
+    }
+
+    pub fn as_nocache(&self) -> phys::NoCachePP {
+        phys::NoCachePP::new(self.as_phys())
+    }
+
+    pub fn zero(&self) {
+        use phys::PhysPtr;
+
+        unsafe {
+            core::ptr::write_bytes(
+                self.as_cached().as_ptr::<u8>(),
+                0,
+                self.len(),
+            );
+        }
+    }
+}
+
+impl Clone for Page {
+    fn clone(&self) -> Self {
+        unsafe {
+            crate::bindings::root::kernel::mem::paging::increase_refcount(
+                self.page_ptr,
+            );
+        }
+
+        Self {
+            page_ptr: self.page_ptr,
+            order: self.order,
+        }
+    }
+}
+
+impl Drop for Page {
+    fn drop(&mut self) {
+        unsafe {
+            crate::bindings::root::kernel::mem::paging::free_pages(
+                self.page_ptr,
+                self.order,
+            );
+        }
+    }
+}
+
+impl PartialEq for Page {
+    fn eq(&self, other: &Self) -> bool {
+        assert!(self.page_ptr != other.page_ptr || self.order == other.order);
+
+        self.page_ptr == other.page_ptr
+    }
+}
+
+unsafe impl Sync for Page {}
+unsafe impl Send for Page {}
+
+impl fmt::Debug for Page {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        let pfn = self.as_phys();
+        write!(f, "Page({:#x}, order={})", pfn, self.order)
+    }
+}
+
+/// Copy data from a slice to a `Page`
+///
+/// DONT USE THIS FUNCTION TO COPY DATA TO MMIO ADDRESSES
+///
+/// # Returns
+///
+/// Returns `Err(EFAULT)` if the slice is larger than the page
+/// Returns `Ok(())` otherwise
+pub fn copy_to_page(src: &[u8], dst: &Page) -> Result<(), u32> {
+    use phys::PhysPtr;
+    if src.len() > dst.len() {
+        return Err(EFAULT);
+    }
+
+    unsafe {
+        core::ptr::copy_nonoverlapping(
+            src.as_ptr(),
+            dst.as_cached().as_ptr(),
+            src.len(),
+        );
+    }
+
+    Ok(())
+}
 
 #[repr(C)]
 struct ZoneInfo {

+ 71 - 0
src/kernel/mem/phys.rs

@@ -0,0 +1,71 @@
+use core::fmt;
+
+pub trait PhysPtr {
+    fn as_ptr<T>(&self) -> *mut T;
+
+    fn as_ref<'lifetime, T>(&self) -> &'lifetime T {
+        unsafe { &*(self.as_ptr()) }
+    }
+
+    fn as_mut<'lifetime, T>(&self) -> &'lifetime mut T {
+        unsafe { &mut *(self.as_ptr()) }
+    }
+}
+
+#[derive(Clone, Copy, PartialEq, Eq)]
+pub struct CachedPP {
+    addr: usize,
+}
+
+#[derive(Clone, Copy, PartialEq, Eq)]
+pub struct NoCachePP {
+    addr: usize,
+}
+
+impl CachedPP {
+    pub fn new(addr: usize) -> Self {
+        Self { addr }
+    }
+
+    pub fn offset(&self, offset: usize) -> Self {
+        Self {
+            addr: self.addr + offset,
+        }
+    }
+}
+
+impl PhysPtr for CachedPP {
+    fn as_ptr<T>(&self) -> *mut T {
+        (self.addr + 0xffffff0000000000) as *mut T
+    }
+}
+
+impl fmt::Debug for CachedPP {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "CachedPP({:#x})", self.addr)
+    }
+}
+
+impl NoCachePP {
+    pub fn new(addr: usize) -> Self {
+        Self { addr }
+    }
+
+    pub fn offset(&self, offset: isize) -> Self {
+        Self {
+            addr: self.addr + offset as usize,
+        }
+    }
+}
+
+impl PhysPtr for NoCachePP {
+    fn as_ptr<T>(&self) -> *mut T {
+        (self.addr + 0xffffff4000000000) as *mut T
+    }
+}
+
+impl fmt::Debug for NoCachePP {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "NoCachePP({:#x})", self.addr)
+    }
+}

+ 3 - 0
src/kernel/process.cpp

@@ -205,6 +205,7 @@ static void release_kinit() {
 }
 
 extern "C" void (*const late_init_start[])();
+extern "C" void late_init_rust();
 
 void NORETURN _kernel_init(kernel::mem::paging::pfn_t kernel_stack_pfn) {
     kernel::mem::paging::free_pages(kernel_stack_pfn, 9);
@@ -212,6 +213,8 @@ void NORETURN _kernel_init(kernel::mem::paging::pfn_t kernel_stack_pfn) {
 
     kernel::kmod::load_internal_modules();
 
+    late_init_rust();
+
     asm volatile("sti");
 
     // mount rootfs

+ 27 - 19
src/lib.rs

@@ -7,8 +7,10 @@ extern crate alloc;
 #[allow(warnings)]
 mod bindings;
 
+mod driver;
 mod io;
 mod kernel;
+mod net;
 
 macro_rules! dont_check {
     ($arg:expr) => {
@@ -61,24 +63,30 @@ unsafe impl GlobalAlloc for Allocator {
 #[global_allocator]
 static ALLOCATOR: Allocator = Allocator {};
 
-#[repr(C)]
-#[allow(dead_code)]
-struct Fp {
-    fp: *const core::ffi::c_void,
+#[no_mangle]
+pub extern "C" fn late_init_rust() {
+    driver::e1000e::register_e1000e_driver();
 }
 
-unsafe impl Sync for Fp {}
-
-#[allow(unused_macros)]
-macro_rules! late_init {
-    ($name:ident, $func:ident) => {
-        #[used]
-        #[link_section = ".late_init"]
-        static $name: $crate::Fp = $crate::Fp {
-            fp: $func as *const core::ffi::c_void,
-        };
-    };
-}
-
-#[allow(unused_imports)]
-pub(crate) use late_init;
+//
+// #[repr(C)]
+// #[allow(dead_code)]
+// struct Fp {
+//     fp: *const core::ffi::c_void,
+// }
+//
+// unsafe impl Sync for Fp {}
+//
+// #[allow(unused_macros)]
+// macro_rules! late_init {
+//     ($name:ident, $func:ident) => {
+//         #[used]
+//         #[link_section = ".late_init"]
+//         static $name: $crate::Fp = $crate::Fp {
+//             fp: $func as *const core::ffi::c_void,
+//         };
+//     };
+// }
+//
+// #[allow(unused_imports)]
+// pub(crate) use late_init;

+ 1 - 0
src/net.rs

@@ -0,0 +1 @@
+pub mod netdev;

+ 0 - 26
src/net/netdev.cc

@@ -1,26 +0,0 @@
-#include <map>
-
-#include <errno.h>
-
-#include <net/netdev.hpp>
-
-using namespace net;
-
-static int s_nextNetdevId = 0;
-static std::map<int, std::unique_ptr<Netdev>> s_netdevs;
-
-Netdev::Netdev(kernel::hw::pci::pci_device& device)
-    : status{NETDEV_DOWN | NETDEV_SPEED_UNKNOWN}, mac{}, device{device} {}
-
-int Netdev::setLinkSpeed(unsigned long speedFlag) {
-    status = (status & ~NETDEV_SPEED_MASK) | speedFlag;
-    return 0;
-}
-
-int net::registerNetdev(std::unique_ptr<Netdev> dev) {
-    auto [it, inserted] = s_netdevs.try_emplace(s_nextNetdevId, std::move(dev));
-    if (!inserted)
-        return -EFAULT;
-
-    return s_nextNetdevId++;
-}

+ 88 - 0
src/net/netdev.rs

@@ -0,0 +1,88 @@
+use alloc::{collections::btree_map::BTreeMap, sync::Arc};
+use spin::Mutex;
+
+use crate::bindings::root::EFAULT;
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum LinkStatus {
+    Up,
+    Down,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum LinkSpeed {
+    SpeedUnknown,
+    Speed10M,
+    Speed100M,
+    Speed1000M,
+}
+
+pub type Mac = [u8; 6];
+
+pub trait Netdev {
+    fn up(&mut self) -> Result<(), u32>;
+    fn send(&mut self, data: &[u8]) -> Result<(), u32>;
+    fn fire(&mut self) -> Result<(), u32>;
+
+    fn link_status(&self) -> LinkStatus;
+    fn link_speed(&self) -> LinkSpeed;
+    fn mac(&self) -> Mac;
+
+    fn id(&self) -> u32;
+}
+
+impl PartialEq for dyn Netdev {
+    fn eq(&self, other: &Self) -> bool {
+        self.id() == other.id()
+    }
+}
+
+impl Eq for dyn Netdev {}
+
+impl PartialOrd for dyn Netdev {
+    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
+        Some(self.cmp(other))
+    }
+}
+
+impl Ord for dyn Netdev {
+    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
+        self.id().cmp(&other.id())
+    }
+}
+
+static mut NETDEVS_ID: Mutex<u32> = Mutex::new(0);
+static mut NETDEVS: Mutex<BTreeMap<u32, Arc<Mutex<dyn Netdev>>>> =
+    Mutex::new(BTreeMap::new());
+
+pub fn alloc_id() -> u32 {
+    let mut id = unsafe { NETDEVS_ID.lock() };
+    let retval = *id;
+
+    *id += 1;
+    retval
+}
+
+pub fn register_netdev(
+    netdev: impl Netdev + 'static,
+) -> Result<Arc<Mutex<dyn Netdev>>, u32> {
+    let devid = netdev.id();
+
+    let mut netdevs = unsafe { NETDEVS.lock() };
+
+    use alloc::collections::btree_map::Entry;
+    match netdevs.entry(devid) {
+        Entry::Vacant(entry) => {
+            let netdev = Arc::new(Mutex::new(netdev));
+            entry.insert(netdev.clone());
+            Ok(netdev)
+        }
+        Entry::Occupied(_) => Err(EFAULT),
+    }
+}
+
+pub fn get_netdev(id: u32) -> Option<Arc<Mutex<dyn Netdev>>> {
+    let netdevs = unsafe { NETDEVS.lock() };
+
+    netdevs.get(&id).map(|netdev| netdev.clone())
+}