]> Projects (at) Tadryanom (dot) Me - AdrOS.git/commitdiff
feat: E1000 NIC driver (Intel 82540EM)
authorTulio A M Mendes <[email protected]>
Wed, 11 Feb 2026 23:33:57 +0000 (20:33 -0300)
committerTulio A M Mendes <[email protected]>
Fri, 13 Feb 2026 02:20:50 +0000 (23:20 -0300)
- include/e1000.h: register defines, TX/RX descriptor structs, public API
- src/drivers/e1000.c: full E1000 driver implementation
  PCI BAR0 MMIO mapping (128KB at 0xC0230000), bus mastering enabled
  EEPROM MAC address read, TX/RX descriptor ring setup (32 entries each)
  DMA buffer allocation, legacy TX descriptors, interrupt handler
  e1000_send/e1000_recv/e1000_get_mac/e1000_link_up API
- src/arch/x86/arch_platform.c: IOAPIC route IRQ 11 -> vector 43
- src/kernel/init.c: call e1000_init() after pci_init()
- Tested: MAC 52:54:00:12:34:56, IRQ=11, 19/19 smoke tests pass

include/e1000.h [new file with mode: 0644]
src/arch/x86/arch_platform.c
src/drivers/e1000.c [new file with mode: 0644]
src/kernel/init.c

diff --git a/include/e1000.h b/include/e1000.h
new file mode 100644 (file)
index 0000000..0aaaee6
--- /dev/null
@@ -0,0 +1,124 @@
+#ifndef E1000_H
+#define E1000_H
+
+#include <stdint.h>
+
+/* Intel 82540EM (E1000) PCI IDs */
+#define E1000_VENDOR_ID  0x8086
+#define E1000_DEVICE_ID  0x100E
+
+/* E1000 Register Offsets */
+#define E1000_CTRL       0x0000  /* Device Control */
+#define E1000_STATUS     0x0008  /* Device Status */
+#define E1000_EERD       0x0014  /* EEPROM Read */
+#define E1000_ICR        0x00C0  /* Interrupt Cause Read */
+#define E1000_ICS        0x00C4  /* Interrupt Cause Set */
+#define E1000_IMS        0x00C8  /* Interrupt Mask Set */
+#define E1000_IMC        0x00D0  /* Interrupt Mask Clear */
+#define E1000_RCTL       0x0100  /* Receive Control */
+#define E1000_TCTL       0x0400  /* Transmit Control */
+#define E1000_TIPG       0x0410  /* TX Inter-Packet Gap */
+#define E1000_RDBAL      0x2800  /* RX Descriptor Base Low */
+#define E1000_RDBAH      0x2804  /* RX Descriptor Base High */
+#define E1000_RDLEN      0x2808  /* RX Descriptor Length */
+#define E1000_RDH        0x2810  /* RX Descriptor Head */
+#define E1000_RDT        0x2818  /* RX Descriptor Tail */
+#define E1000_TDBAL      0x3800  /* TX Descriptor Base Low */
+#define E1000_TDBAH      0x3804  /* TX Descriptor Base High */
+#define E1000_TDLEN      0x3808  /* TX Descriptor Length */
+#define E1000_TDH        0x3810  /* TX Descriptor Head */
+#define E1000_TDT        0x3818  /* TX Descriptor Tail */
+#define E1000_MTA        0x5200  /* Multicast Table Array (128 entries) */
+#define E1000_RAL0       0x5400  /* Receive Address Low */
+#define E1000_RAH0       0x5404  /* Receive Address High */
+
+/* CTRL bits */
+#define E1000_CTRL_FD    (1U << 0)   /* Full Duplex */
+#define E1000_CTRL_ASDE  (1U << 5)   /* Auto-Speed Detection Enable */
+#define E1000_CTRL_SLU   (1U << 6)   /* Set Link Up */
+#define E1000_CTRL_RST   (1U << 26)  /* Device Reset */
+
+/* RCTL bits */
+#define E1000_RCTL_EN    (1U << 1)   /* Receiver Enable */
+#define E1000_RCTL_SBP   (1U << 2)   /* Store Bad Packets */
+#define E1000_RCTL_UPE   (1U << 3)   /* Unicast Promiscuous */
+#define E1000_RCTL_MPE   (1U << 4)   /* Multicast Promiscuous */
+#define E1000_RCTL_LBM   (3U << 6)   /* Loopback Mode */
+#define E1000_RCTL_BAM   (1U << 15)  /* Broadcast Accept Mode */
+#define E1000_RCTL_BSIZE_2048 (0U << 16) /* Buffer Size 2048 */
+#define E1000_RCTL_BSIZE_4096 (3U << 16 | 1U << 25) /* Buffer Size 4096 (BSEX) */
+#define E1000_RCTL_SECRC (1U << 26)  /* Strip Ethernet CRC */
+
+/* TCTL bits */
+#define E1000_TCTL_EN    (1U << 1)   /* Transmitter Enable */
+#define E1000_TCTL_PSP   (1U << 3)   /* Pad Short Packets */
+#define E1000_TCTL_CT_SHIFT  4       /* Collision Threshold */
+#define E1000_TCTL_COLD_SHIFT 12     /* Collision Distance */
+
+/* ICR / IMS bits */
+#define E1000_ICR_TXDW   (1U << 0)   /* TX Descriptor Written Back */
+#define E1000_ICR_TXQE   (1U << 1)   /* TX Queue Empty */
+#define E1000_ICR_LSC    (1U << 2)   /* Link Status Change */
+#define E1000_ICR_RXDMT0 (1U << 4)   /* RX Descriptor Minimum Threshold */
+#define E1000_ICR_RXO    (1U << 6)   /* Receiver Overrun */
+#define E1000_ICR_RXT0   (1U << 7)   /* Receiver Timer Interrupt */
+
+/* EERD bits */
+#define E1000_EERD_START (1U << 0)
+#define E1000_EERD_DONE  (1U << 4)
+
+/* TX command bits */
+#define E1000_TXD_CMD_EOP  (1U << 0) /* End of Packet */
+#define E1000_TXD_CMD_IFCS (1U << 1) /* Insert FCS/CRC */
+#define E1000_TXD_CMD_RS   (1U << 3) /* Report Status */
+
+/* TX status bits */
+#define E1000_TXD_STAT_DD  (1U << 0) /* Descriptor Done */
+
+/* RX status bits */
+#define E1000_RXD_STAT_DD  (1U << 0) /* Descriptor Done */
+#define E1000_RXD_STAT_EOP (1U << 1) /* End of Packet */
+
+/* Ring sizes */
+#define E1000_NUM_TX_DESC  32
+#define E1000_NUM_RX_DESC  32
+#define E1000_RX_BUF_SIZE  2048
+#define E1000_TX_BUF_SIZE  2048
+
+/* TX Descriptor (Legacy) */
+struct e1000_tx_desc {
+    uint64_t buffer_addr;
+    uint16_t length;
+    uint8_t  cso;
+    uint8_t  cmd;
+    uint8_t  status;
+    uint8_t  css;
+    uint16_t special;
+} __attribute__((packed));
+
+/* RX Descriptor */
+struct e1000_rx_desc {
+    uint64_t buffer_addr;
+    uint16_t length;
+    uint16_t checksum;
+    uint8_t  status;
+    uint8_t  errors;
+    uint16_t special;
+} __attribute__((packed));
+
+/* Initialize the E1000 NIC. Returns 0 on success, -1 on failure. */
+int e1000_init(void);
+
+/* Send a packet. Returns 0 on success. */
+int e1000_send(const void* data, uint16_t len);
+
+/* Receive a packet into buf (max buf_len bytes). Returns bytes received, or 0. */
+int e1000_recv(void* buf, uint16_t buf_len);
+
+/* Get the MAC address (6 bytes). */
+void e1000_get_mac(uint8_t mac[6]);
+
+/* Check if the NIC is initialized and link is up. */
+int e1000_link_up(void);
+
+#endif
index 27f0a3fab4ed7771d1e708bdc8cdfe5e9d79db8b..44b3ceca792892fc779911fa72148819963a5463 100644 (file)
@@ -103,6 +103,7 @@ int arch_platform_setup(const struct boot_info* bi) {
              * IRQ 14 (ATA primary) -> IDT vector 46 */
             ioapic_route_irq(0,  32, (uint8_t)bsp_id);
             ioapic_route_irq(1,  33, (uint8_t)bsp_id);
+            ioapic_route_irq(11, 43, (uint8_t)bsp_id); /* E1000 NIC */
             ioapic_route_irq(14, 46, (uint8_t)bsp_id);
 
             /* Now that IOAPIC routes are live, disable the legacy PIC.
diff --git a/src/drivers/e1000.c b/src/drivers/e1000.c
new file mode 100644 (file)
index 0000000..de38725
--- /dev/null
@@ -0,0 +1,373 @@
+#include "e1000.h"
+#include "pci.h"
+#include "vmm.h"
+#include "pmm.h"
+#include "interrupts.h"
+#include "uart_console.h"
+#include "utils.h"
+#include "io.h"
+
+#include <stddef.h>
+
+/* ------------------------------------------------------------------ */
+/* Kernel VA layout for E1000 DMA buffers                             */
+/*   0xC0230000 .. 0xC024FFFF  E1000 MMIO (128 KB = 32 pages)        */
+/*   0xC0250000                TX descriptor ring (1 page)            */
+/*   0xC0251000                RX descriptor ring (1 page)            */
+/*   0xC0252000 .. 0xC0261FFF  TX buffers (32 x 2 KB = 16 pages)     */
+/*   0xC0262000 .. 0xC0271FFF  RX buffers (32 x 2 KB = 16 pages)     */
+/* ------------------------------------------------------------------ */
+#define E1000_MMIO_VA      0xC0230000U
+#define E1000_MMIO_PAGES   32
+#define E1000_TX_DESC_VA   0xC0250000U
+#define E1000_RX_DESC_VA   0xC0251000U
+#define E1000_TX_BUF_VA    0xC0252000U
+#define E1000_RX_BUF_VA    0xC0262000U
+
+static volatile uint32_t* e1000_mmio = 0;
+static uint8_t e1000_mac[6];
+static int e1000_ready = 0;
+
+/* Physical addresses for DMA */
+static uint32_t tx_desc_phys;
+static uint32_t rx_desc_phys;
+static uint32_t tx_buf_phys[E1000_NUM_TX_DESC];
+static uint32_t rx_buf_phys[E1000_NUM_RX_DESC];
+
+/* Ring indices */
+static volatile uint32_t tx_tail = 0;
+static volatile uint32_t rx_tail = 0;
+
+/* Cached PCI device info */
+static uint8_t e1000_bus, e1000_slot, e1000_func;
+
+/* ------------------------------------------------------------------ */
+/* MMIO helpers                                                       */
+/* ------------------------------------------------------------------ */
+
+static inline uint32_t e1000_read(uint32_t reg) {
+    return e1000_mmio[reg / 4];
+}
+
+static inline void e1000_write(uint32_t reg, uint32_t val) {
+    e1000_mmio[reg / 4] = val;
+}
+
+/* ------------------------------------------------------------------ */
+/* EEPROM                                                             */
+/* ------------------------------------------------------------------ */
+
+static uint16_t e1000_eeprom_read(uint8_t addr) {
+    e1000_write(E1000_EERD, ((uint32_t)addr << 8) | E1000_EERD_START);
+    uint32_t val;
+    for (int i = 0; i < 1000; i++) {
+        val = e1000_read(E1000_EERD);
+        if (val & E1000_EERD_DONE)
+            return (uint16_t)(val >> 16);
+    }
+    return 0;
+}
+
+static void e1000_read_mac(void) {
+    uint16_t w0 = e1000_eeprom_read(0);
+    uint16_t w1 = e1000_eeprom_read(1);
+    uint16_t w2 = e1000_eeprom_read(2);
+    e1000_mac[0] = (uint8_t)(w0 & 0xFF);
+    e1000_mac[1] = (uint8_t)(w0 >> 8);
+    e1000_mac[2] = (uint8_t)(w1 & 0xFF);
+    e1000_mac[3] = (uint8_t)(w1 >> 8);
+    e1000_mac[4] = (uint8_t)(w2 & 0xFF);
+    e1000_mac[5] = (uint8_t)(w2 >> 8);
+}
+
+/* ------------------------------------------------------------------ */
+/* DMA memory allocation                                              */
+/* ------------------------------------------------------------------ */
+
+static uint32_t alloc_dma_page(uintptr_t va) {
+    void* phys = pmm_alloc_page();
+    if (!phys) return 0;
+    vmm_map_page((uint64_t)(uintptr_t)phys, (uint64_t)va,
+                 VMM_FLAG_PRESENT | VMM_FLAG_RW | VMM_FLAG_NOCACHE);
+    memset((void*)va, 0, 4096);
+    return (uint32_t)(uintptr_t)phys;
+}
+
+/* ------------------------------------------------------------------ */
+/* TX ring setup                                                      */
+/* ------------------------------------------------------------------ */
+
+static int e1000_init_tx(void) {
+    tx_desc_phys = alloc_dma_page(E1000_TX_DESC_VA);
+    if (!tx_desc_phys) return -1;
+
+    struct e1000_tx_desc* txd = (struct e1000_tx_desc*)E1000_TX_DESC_VA;
+
+    /* Allocate TX buffers: 2 buffers per page (2048 each) */
+    for (int i = 0; i < E1000_NUM_TX_DESC; i++) {
+        int page_idx = i / 2;
+        int buf_off  = (i % 2) * E1000_TX_BUF_SIZE;
+        uintptr_t va = E1000_TX_BUF_VA + (uintptr_t)page_idx * 4096;
+
+        if (buf_off == 0) {
+            /* First buffer on this page — allocate it */
+            uint32_t phys = alloc_dma_page(va);
+            if (!phys) return -1;
+            tx_buf_phys[i] = phys;
+        } else {
+            tx_buf_phys[i] = tx_buf_phys[i - 1] + E1000_TX_BUF_SIZE;
+        }
+
+        txd[i].buffer_addr = (uint64_t)tx_buf_phys[i];
+        txd[i].cmd = 0;
+        txd[i].status = E1000_TXD_STAT_DD; /* Mark as done so first send works */
+    }
+
+    e1000_write(E1000_TDBAL, tx_desc_phys);
+    e1000_write(E1000_TDBAH, 0);
+    e1000_write(E1000_TDLEN, E1000_NUM_TX_DESC * (uint32_t)sizeof(struct e1000_tx_desc));
+    e1000_write(E1000_TDH, 0);
+    e1000_write(E1000_TDT, 0);
+    tx_tail = 0;
+
+    /* Enable transmitter */
+    e1000_write(E1000_TCTL, E1000_TCTL_EN | E1000_TCTL_PSP |
+                (15U << E1000_TCTL_CT_SHIFT) |
+                (64U << E1000_TCTL_COLD_SHIFT));
+
+    /* Inter-packet gap: recommended 10, 8, 6 for copper */
+    e1000_write(E1000_TIPG, 10 | (8 << 10) | (6 << 20));
+
+    return 0;
+}
+
+/* ------------------------------------------------------------------ */
+/* RX ring setup                                                      */
+/* ------------------------------------------------------------------ */
+
+static int e1000_init_rx(void) {
+    rx_desc_phys = alloc_dma_page(E1000_RX_DESC_VA);
+    if (!rx_desc_phys) return -1;
+
+    struct e1000_rx_desc* rxd = (struct e1000_rx_desc*)E1000_RX_DESC_VA;
+
+    for (int i = 0; i < E1000_NUM_RX_DESC; i++) {
+        int page_idx = i / 2;
+        int buf_off  = (i % 2) * E1000_RX_BUF_SIZE;
+        uintptr_t va = E1000_RX_BUF_VA + (uintptr_t)page_idx * 4096;
+
+        if (buf_off == 0) {
+            uint32_t phys = alloc_dma_page(va);
+            if (!phys) return -1;
+            rx_buf_phys[i] = phys;
+        } else {
+            rx_buf_phys[i] = rx_buf_phys[i - 1] + E1000_RX_BUF_SIZE;
+        }
+
+        rxd[i].buffer_addr = (uint64_t)rx_buf_phys[i];
+        rxd[i].status = 0;
+    }
+
+    /* Set receive address (MAC filter) */
+    uint32_t ral = (uint32_t)e1000_mac[0] | ((uint32_t)e1000_mac[1] << 8) |
+                   ((uint32_t)e1000_mac[2] << 16) | ((uint32_t)e1000_mac[3] << 24);
+    uint32_t rah = (uint32_t)e1000_mac[4] | ((uint32_t)e1000_mac[5] << 8) |
+                   (1U << 31); /* Address Valid */
+    e1000_write(E1000_RAL0, ral);
+    e1000_write(E1000_RAH0, rah);
+
+    /* Clear multicast table */
+    for (int i = 0; i < 128; i++) {
+        e1000_write(E1000_MTA + (uint32_t)i * 4, 0);
+    }
+
+    e1000_write(E1000_RDBAL, rx_desc_phys);
+    e1000_write(E1000_RDBAH, 0);
+    e1000_write(E1000_RDLEN, E1000_NUM_RX_DESC * (uint32_t)sizeof(struct e1000_rx_desc));
+    e1000_write(E1000_RDH, 0);
+    e1000_write(E1000_RDT, E1000_NUM_RX_DESC - 1);
+    rx_tail = 0;
+
+    /* Enable receiver: accept broadcast, 2048-byte buffers, strip CRC */
+    e1000_write(E1000_RCTL, E1000_RCTL_EN | E1000_RCTL_BAM |
+                E1000_RCTL_BSIZE_2048 | E1000_RCTL_SECRC);
+
+    return 0;
+}
+
+/* ------------------------------------------------------------------ */
+/* Interrupt handler                                                  */
+/* ------------------------------------------------------------------ */
+
+static void e1000_irq_handler(struct registers* regs) {
+    (void)regs;
+    uint32_t icr = e1000_read(E1000_ICR);
+    (void)icr;
+    /* Reading ICR clears the pending interrupt bits.
+     * RX/TX processing is done via polling in e1000_recv/e1000_send
+     * for simplicity. The interrupt just wakes the system. */
+}
+
+/* ------------------------------------------------------------------ */
+/* Public API                                                         */
+/* ------------------------------------------------------------------ */
+
+int e1000_init(void) {
+    const struct pci_device* dev = pci_find_device(E1000_VENDOR_ID, E1000_DEVICE_ID);
+    if (!dev) {
+        uart_print("[E1000] Device not found.\n");
+        return -1;
+    }
+
+    e1000_bus  = dev->bus;
+    e1000_slot = dev->slot;
+    e1000_func = dev->func;
+
+    /* Read BAR0 (MMIO base) */
+    uint32_t bar0 = dev->bar[0];
+    if (bar0 & 1) {
+        uart_print("[E1000] BAR0 is I/O (unsupported).\n");
+        return -1;
+    }
+    uint32_t mmio_phys = bar0 & 0xFFFFFFF0U;
+
+    /* Map E1000 MMIO region (128KB) */
+    for (int i = 0; i < E1000_MMIO_PAGES; i++) {
+        vmm_map_page((uint64_t)(mmio_phys + (uint32_t)i * 4096),
+                     (uint64_t)(E1000_MMIO_VA + (uint32_t)i * 4096),
+                     VMM_FLAG_PRESENT | VMM_FLAG_RW | VMM_FLAG_NOCACHE);
+    }
+    e1000_mmio = (volatile uint32_t*)E1000_MMIO_VA;
+
+    /* Enable PCI bus mastering + memory space */
+    uint32_t cmd = pci_config_read(e1000_bus, e1000_slot, e1000_func, 0x04);
+    cmd |= (1U << 2) | (1U << 1); /* Bus Master | Memory Space */
+    pci_config_write(e1000_bus, e1000_slot, e1000_func, 0x04, cmd);
+
+    /* Reset the device */
+    uint32_t ctrl = e1000_read(E1000_CTRL);
+    e1000_write(E1000_CTRL, ctrl | E1000_CTRL_RST);
+    /* Wait for reset to complete (spec says ~1us, be generous) */
+    for (volatile int i = 0; i < 100000; i++) { }
+
+    /* Disable interrupts during setup */
+    e1000_write(E1000_IMC, 0xFFFFFFFF);
+    e1000_read(E1000_ICR);  /* Clear pending */
+
+    /* Set link up */
+    ctrl = e1000_read(E1000_CTRL);
+    e1000_write(E1000_CTRL, ctrl | E1000_CTRL_SLU | E1000_CTRL_ASDE);
+
+    /* Read MAC address from EEPROM */
+    e1000_read_mac();
+
+    uart_print("[E1000] MAC: ");
+    char hex[4];
+    for (int i = 0; i < 6; i++) {
+        if (i) uart_print(":");
+        hex[0] = "0123456789ABCDEF"[(e1000_mac[i] >> 4) & 0xF];
+        hex[1] = "0123456789ABCDEF"[e1000_mac[i] & 0xF];
+        hex[2] = '\0';
+        uart_print(hex);
+    }
+    uart_print("\n");
+
+    /* Init TX and RX rings */
+    if (e1000_init_tx() < 0) {
+        uart_print("[E1000] Failed to init TX ring.\n");
+        return -1;
+    }
+    if (e1000_init_rx() < 0) {
+        uart_print("[E1000] Failed to init RX ring.\n");
+        return -1;
+    }
+
+    /* Register interrupt handler */
+    uint8_t irq = dev->irq_line;
+    if (irq < 16) {
+        register_interrupt_handler((uint8_t)(32 + irq), e1000_irq_handler);
+    }
+
+    /* Enable RX interrupts */
+    e1000_write(E1000_IMS, E1000_ICR_RXT0 | E1000_ICR_LSC |
+                E1000_ICR_RXDMT0 | E1000_ICR_RXO);
+
+    e1000_ready = 1;
+
+    char buf[12];
+    uart_print("[E1000] Initialized, IRQ=");
+    itoa(irq, buf, 10);
+    uart_print(buf);
+    uart_print(", MMIO=");
+    itoa_hex(mmio_phys, buf);
+    uart_print(buf);
+    uart_print("\n");
+
+    return 0;
+}
+
+int e1000_send(const void* data, uint16_t len) {
+    if (!e1000_ready || !data || len == 0 || len > E1000_TX_BUF_SIZE)
+        return -1;
+
+    struct e1000_tx_desc* txd = (struct e1000_tx_desc*)E1000_TX_DESC_VA;
+    uint32_t idx = tx_tail;
+
+    /* Wait for descriptor to be available */
+    int timeout = 100000;
+    while (!(txd[idx].status & E1000_TXD_STAT_DD) && --timeout > 0) { }
+    if (timeout <= 0) return -1;
+
+    /* Copy data to TX buffer */
+    uintptr_t buf_va = E1000_TX_BUF_VA + (uintptr_t)(idx / 2) * 4096 +
+                       (uintptr_t)(idx % 2) * E1000_TX_BUF_SIZE;
+    memcpy((void*)buf_va, data, len);
+
+    /* Set up descriptor */
+    txd[idx].buffer_addr = (uint64_t)tx_buf_phys[idx];
+    txd[idx].length = len;
+    txd[idx].cmd = E1000_TXD_CMD_EOP | E1000_TXD_CMD_IFCS | E1000_TXD_CMD_RS;
+    txd[idx].status = 0;
+
+    /* Advance tail */
+    tx_tail = (idx + 1) % E1000_NUM_TX_DESC;
+    e1000_write(E1000_TDT, tx_tail);
+
+    return 0;
+}
+
+int e1000_recv(void* buf, uint16_t buf_len) {
+    if (!e1000_ready || !buf || buf_len == 0)
+        return 0;
+
+    struct e1000_rx_desc* rxd = (struct e1000_rx_desc*)E1000_RX_DESC_VA;
+    uint32_t idx = rx_tail;
+
+    if (!(rxd[idx].status & E1000_RXD_STAT_DD))
+        return 0;  /* No packet available */
+
+    uint16_t pkt_len = rxd[idx].length;
+    if (pkt_len > buf_len) pkt_len = buf_len;
+
+    /* Copy data from RX buffer */
+    uintptr_t buf_va = E1000_RX_BUF_VA + (uintptr_t)(idx / 2) * 4096 +
+                       (uintptr_t)(idx % 2) * E1000_RX_BUF_SIZE;
+    memcpy(buf, (const void*)buf_va, pkt_len);
+
+    /* Reset descriptor and advance tail */
+    rxd[idx].status = 0;
+    uint32_t old_tail = rx_tail;
+    rx_tail = (idx + 1) % E1000_NUM_RX_DESC;
+    e1000_write(E1000_RDT, old_tail);
+
+    return (int)pkt_len;
+}
+
+void e1000_get_mac(uint8_t mac[6]) {
+    for (int i = 0; i < 6; i++) mac[i] = e1000_mac[i];
+}
+
+int e1000_link_up(void) {
+    if (!e1000_ready) return 0;
+    return (e1000_read(E1000_STATUS) & (1U << 1)) ? 1 : 0;
+}
index 97ef6594482ac21dfec20baaa3f77fc64fb76084..261ae982ecf1dc74cbfea28a89d8834da17ad794 100644 (file)
@@ -13,6 +13,7 @@
 #include "diskfs.h"
 #include "procfs.h"
 #include "pci.h"
+#include "e1000.h"
 #include "vbe.h"
 #include "uart_console.h"
 
@@ -69,6 +70,7 @@ int init_start(const struct boot_info* bi) {
     }
 
     pci_init();
+    e1000_init();
     vbe_init(bi);
 
     tty_init();