]> Projects (at) Tadryanom (dot) Me - AdrOS.git/commitdiff
feat: lwIP TCP/IP stack integration with E1000 netif
authorTulio A M Mendes <[email protected]>
Wed, 11 Feb 2026 23:43:33 +0000 (20:43 -0300)
committerTulio A M Mendes <[email protected]>
Fri, 13 Feb 2026 02:20:50 +0000 (23:20 -0300)
- third_party/lwip/ added to .gitignore (cloned separately)
- src/net/lwip_port/lwipopts.h: NO_SYS=1 config, IPv4 only, TCP+UDP+ICMP+ARP
- src/net/lwip_port/arch/cc.h: compiler/type defines for lwIP on x86
- src/net/lwip_port/sys_arch.c: sys_now() using kernel tick counter
- src/net/e1000_netif.c: lwIP netif driver bridging E1000 hardware
  net_init() configures IP 10.0.2.15/24, gw 10.0.2.2 (QEMU user-mode)
  net_poll() feeds RX packets to lwIP + processes timeouts
- include/net.h: public API (net_init, net_poll, net_get_netif)
- include/utils.h + src/kernel/utils.c: added memmove, memcmp, strncpy,
  strtol, __memcpy_chk, __ctype_b_loc stubs needed by lwIP
- Makefile: lwIP core+ipv4+ethernet sources compiled with relaxed warnings,
  include paths for lwip_port and lwip/src/include
- src/kernel/main.c: net_poll() in idle loop
- src/kernel/init.c: net_init() after e1000_init()
- 19/19 smoke tests pass, cppcheck clean

.gitignore
Makefile
include/net.h [new file with mode: 0644]
include/utils.h
src/kernel/init.c
src/kernel/main.c
src/kernel/utils.c
src/net/e1000_netif.c [new file with mode: 0644]
src/net/lwip_port/arch/cc.h [new file with mode: 0644]
src/net/lwip_port/lwipopts.h [new file with mode: 0644]
src/net/lwip_port/sys_arch.c [new file with mode: 0644]

index 9a387983a9d3702aee3fd714a7c6e89c2135f7b6..359e9a7cba04574b3a019982cf8f887423022aa5 100644 (file)
@@ -36,3 +36,4 @@ supplementary-material
 .vscode/
 *.swp
 *~
+third_party/
index 4b0a13664642bdf627f8ca70417f3116049f4d47..686d3ce6dc99d989608ac26a4bde61c51a6c06d4 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -28,8 +28,24 @@ ifeq ($(ARCH),x86)
     AS ?= $(TOOLPREFIX)as
     LD ?= $(TOOLPREFIX)ld
     
+    # lwIP sources (NO_SYS=1, IPv4 only, no apps)
+    LWIPDIR := third_party/lwip/src
+    LWIP_CORE := $(LWIPDIR)/core/init.c $(LWIPDIR)/core/def.c $(LWIPDIR)/core/inet_chksum.c \
+        $(LWIPDIR)/core/ip.c $(LWIPDIR)/core/mem.c $(LWIPDIR)/core/memp.c \
+        $(LWIPDIR)/core/netif.c $(LWIPDIR)/core/pbuf.c $(LWIPDIR)/core/raw.c \
+        $(LWIPDIR)/core/stats.c $(LWIPDIR)/core/sys.c $(LWIPDIR)/core/tcp.c \
+        $(LWIPDIR)/core/tcp_in.c $(LWIPDIR)/core/tcp_out.c $(LWIPDIR)/core/timeouts.c \
+        $(LWIPDIR)/core/udp.c
+    LWIP_IPV4 := $(LWIPDIR)/core/ipv4/etharp.c $(LWIPDIR)/core/ipv4/icmp.c \
+        $(LWIPDIR)/core/ipv4/ip4.c $(LWIPDIR)/core/ipv4/ip4_addr.c \
+        $(LWIPDIR)/core/ipv4/ip4_frag.c
+    LWIP_NETIF := $(LWIPDIR)/netif/ethernet.c
+    LWIP_SOURCES := $(LWIP_CORE) $(LWIP_IPV4) $(LWIP_NETIF)
+    NET_SOURCES := $(wildcard $(SRC_DIR)/net/*.c) $(wildcard $(SRC_DIR)/net/lwip_port/*.c)
+    C_SOURCES += $(NET_SOURCES)
+
     # Mandatory Architecture Flags
-    ARCH_CFLAGS := -m32 -ffreestanding -Iinclude
+    ARCH_CFLAGS := -m32 -ffreestanding -fno-builtin -U_FORTIFY_SOURCE -Iinclude -Isrc/net/lwip_port -Ithird_party/lwip/src/include
     ARCH_LDFLAGS := -m elf_i386 -T $(SRC_DIR)/arch/x86/linker.ld
     ARCH_ASFLAGS := --32
 
@@ -91,9 +107,13 @@ ifeq ($(ARCH),mips)
     C_SOURCES += $(wildcard $(SRC_DIR)/arch/mips/*.c)
 endif
 
+# lwIP object files (compiled from third_party, separate pattern)
+LWIP_OBJ := $(patsubst %.c, $(BUILD_DIR)/lwip/%.o, $(LWIP_SOURCES))
+
 # Object generation
 OBJ := $(patsubst $(SRC_DIR)/%.c, $(BUILD_DIR)/%.o, $(C_SOURCES))
 OBJ += $(patsubst $(SRC_DIR)/%.S, $(BUILD_DIR)/%.o, $(ASM_SOURCES))
+OBJ += $(LWIP_OBJ)
 
 QEMU_DFLAGS :=
 ifneq ($(QEMU_DEBUG),)
@@ -203,7 +223,7 @@ check: cppcheck sparse
 # ---- Automated Smoke Test (QEMU + expect) ----
 
 SMOKE_SMP ?= 4
-SMOKE_TIMEOUT ?= 60
+SMOKE_TIMEOUT ?= 90
 
 test: iso
        @echo "[TEST] Running smoke test (SMP=$(SMOKE_SMP), timeout=$(SMOKE_TIMEOUT)s)..."
@@ -257,6 +277,12 @@ $(BUILD_DIR)/%.o: $(SRC_DIR)/%.c
        @echo "  CC      $<"
        @$(CC) $(CFLAGS) -c $< -o $@
 
+# lwIP sources (compiled with relaxed warnings)
+$(BUILD_DIR)/lwip/%.o: %.c
+       @mkdir -p $(dir $@)
+       @echo "  CC      $< (lwIP)"
+       @$(CC) $(CFLAGS) -Wno-address -w -c $< -o $@
+
 $(BUILD_DIR)/%.o: $(SRC_DIR)/%.S
        @mkdir -p $(dir $@)
        @echo "  AS      $<"
diff --git a/include/net.h b/include/net.h
new file mode 100644 (file)
index 0000000..d129611
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef NET_H
+#define NET_H
+
+struct netif;
+
+/* Initialize the network stack (lwIP + E1000 netif). */
+void net_init(void);
+
+/* Poll for received packets and process lwIP timeouts. Call periodically. */
+void net_poll(void);
+
+/* Get the active network interface (or NULL). */
+struct netif* net_get_netif(void);
+
+#endif
index 107e36e8837cca045971c179e7f56bafab1f8004..eab460bde6f8116206e2ba460d07d834089231aa 100644 (file)
@@ -10,7 +10,11 @@ int strncmp(const char* s1, const char* s2, size_t n);
 char* strcpy(char* dest, const char* src);
 void* memset(void* ptr, int value, size_t num);
 void* memcpy(void* dst, const void* src, size_t n);
+void* memmove(void* dst, const void* src, size_t n);
+int   memcmp(const void* a, const void* b, size_t n);
 char* strcpy(char* dest, const char* src);
+char* strncpy(char* dest, const char* src, size_t n);
+long  strtol(const char* nptr, char** endptr, int base);
 
 // Reverse a string (helper for itoa)
 void reverse(char* str, int length);
index 261ae982ecf1dc74cbfea28a89d8834da17ad794..7547bddf4e260505de3058bf616fc9f80f4dfd38 100644 (file)
@@ -14,6 +14,7 @@
 #include "procfs.h"
 #include "pci.h"
 #include "e1000.h"
+#include "net.h"
 #include "vbe.h"
 #include "uart_console.h"
 
@@ -71,6 +72,7 @@ int init_start(const struct boot_info* bi) {
 
     pci_init();
     e1000_init();
+    net_init();
     vbe_init(bi);
 
     tty_init();
index a85847d60d44d88b4d20de27dcffb3afcff47976..bab32ee3e6242f102a1d74aa72db04172d300a82 100644 (file)
@@ -23,6 +23,7 @@
 #include "hal/cpu.h"
 #include "hal/cpu_features.h"
 #include "shm.h"
+#include "net.h"
 
 
 /* Check if the compiler thinks we are targeting the wrong operating system. */
@@ -79,6 +80,7 @@ done:
 
     // Infinite loop acting as Idle Task
     for(;;) {
+        net_poll();
         hal_cpu_idle();
     }
 }
index 63f6822d4672089cd09d57eaee4cff61ba17ea13..f3264018726fb9d3daa14abdcbba6b893fde0aac 100644 (file)
@@ -40,6 +40,27 @@ void* memcpy(void* dst, const void* src, size_t n) {
     return dst;
 }
 
+void* memmove(void* dst, const void* src, size_t n) {
+    unsigned char* d = dst;
+    const unsigned char* s = src;
+    if (d < s) {
+        while (n--) *d++ = *s++;
+    } else {
+        d += n; s += n;
+        while (n--) *--d = *--s;
+    }
+    return dst;
+}
+
+int memcmp(const void* a, const void* b, size_t n) {
+    const unsigned char* pa = a;
+    const unsigned char* pb = b;
+    for (size_t i = 0; i < n; i++) {
+        if (pa[i] != pb[i]) return (int)pa[i] - (int)pb[i];
+    }
+    return 0;
+}
+
 char* strcpy(char* dest, const char* src) {
     char* saved = dest;
     while (*src) {
@@ -107,6 +128,57 @@ int atoi(const char* str) {
     return sign * res;
 }
 
+char* strncpy(char* dest, const char* src, size_t n) {
+    size_t i;
+    for (i = 0; i < n && src[i]; i++) dest[i] = src[i];
+    for (; i < n; i++) dest[i] = '\0';
+    return dest;
+}
+
+long strtol(const char* nptr, char** endptr, int base) {
+    long result = 0;
+    int neg = 0;
+    const char* p = nptr;
+    while (*p == ' ' || *p == '\t') p++;
+    if (*p == '-') { neg = 1; p++; }
+    else if (*p == '+') p++;
+    if (base == 0) {
+        if (*p == '0' && (p[1] == 'x' || p[1] == 'X')) { base = 16; p += 2; }
+        else if (*p == '0') { base = 8; p++; }
+        else base = 10;
+    } else if (base == 16 && *p == '0' && (p[1] == 'x' || p[1] == 'X')) {
+        p += 2;
+    }
+    while (*p) {
+        int digit;
+        if (*p >= '0' && *p <= '9') digit = *p - '0';
+        else if (*p >= 'a' && *p <= 'f') digit = *p - 'a' + 10;
+        else if (*p >= 'A' && *p <= 'F') digit = *p - 'A' + 10;
+        else break;
+        if (digit >= base) break;
+        result = result * base + digit;
+        p++;
+    }
+    if (endptr) *endptr = (char*)p;
+    return neg ? -result : result;
+}
+
+/* GCC fortified memcpy — use builtin to avoid infinite recursion */
+void* __memcpy_chk(void* dst, const void* src, size_t n, size_t dst_len) {
+    (void)dst_len;
+    char* d = dst;
+    const char* s = src;
+    while (n--) *d++ = *s++;
+    return dst;
+}
+
+/* GCC ctype locale stub — lwIP ip4_addr_c uses isdigit/isxdigit */
+static const unsigned short _ctype_table[384] = {0};
+static const unsigned short* _ctype_ptr = &_ctype_table[128];
+const unsigned short** __ctype_b_loc(void) {
+    return (const unsigned short**)&_ctype_ptr;
+}
+
 void itoa_hex(uint32_t num, char* str) {
     const char hex_chars[] = "0123456789ABCDEF";
     str[0] = '0';
diff --git a/src/net/e1000_netif.c b/src/net/e1000_netif.c
new file mode 100644 (file)
index 0000000..c9ab72d
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * lwIP netif driver for the E1000 NIC.
+ * Bridges the E1000 hardware driver to lwIP's network interface abstraction.
+ */
+#include "lwip/opt.h"
+#include "lwip/netif.h"
+#include "lwip/pbuf.h"
+#include "lwip/etharp.h"
+#include "lwip/ip4_addr.h"
+#include "lwip/init.h"
+#include "lwip/timeouts.h"
+#include "netif/ethernet.h"
+
+#include "e1000.h"
+#include "uart_console.h"
+#include "utils.h"
+
+#define E1000_NETIF_MTU  1500
+
+/* Temporary receive buffer (stack-allocated per poll call) */
+static uint8_t rx_tmp[2048];
+
+/* Forward declaration */
+static err_t e1000_netif_output(struct netif* netif, struct pbuf* p);
+
+/*
+ * Low-level output: send a pbuf chain via E1000.
+ */
+static err_t e1000_netif_output(struct netif* netif, struct pbuf* p) {
+    (void)netif;
+    if (!p) return ERR_ARG;
+
+    /* Flatten pbuf chain into a contiguous buffer */
+    uint16_t total = (uint16_t)p->tot_len;
+    if (total > E1000_TX_BUF_SIZE) return ERR_MEM;
+
+    /* If single pbuf, send directly */
+    if (p->next == NULL) {
+        if (e1000_send(p->payload, total) < 0)
+            return ERR_IF;
+        return ERR_OK;
+    }
+
+    /* Multi-segment: copy to temp buffer */
+    static uint8_t tx_tmp[2048];
+    uint16_t off = 0;
+    for (struct pbuf* q = p; q != NULL; q = q->next) {
+        if (off + q->len > sizeof(tx_tmp)) return ERR_MEM;
+        memcpy(tx_tmp + off, q->payload, q->len);
+        off += q->len;
+    }
+
+    if (e1000_send(tx_tmp, total) < 0)
+        return ERR_IF;
+
+    return ERR_OK;
+}
+
+/*
+ * Netif init callback — called by netif_add().
+ */
+err_t e1000_netif_init(struct netif* netif) {
+    netif->name[0] = 'e';
+    netif->name[1] = 'n';
+    netif->output = etharp_output;
+    netif->linkoutput = e1000_netif_output;
+    netif->mtu = E1000_NETIF_MTU;
+    netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;
+    netif->hwaddr_len = 6;
+
+    e1000_get_mac(netif->hwaddr);
+
+    return ERR_OK;
+}
+
+/*
+ * Poll the E1000 for received packets and feed them into lwIP.
+ * Must be called periodically from the main loop.
+ */
+void e1000_netif_poll(struct netif* netif) {
+    for (;;) {
+        int len = e1000_recv(rx_tmp, sizeof(rx_tmp));
+        if (len <= 0) break;
+
+        struct pbuf* p = pbuf_alloc(PBUF_RAW, (u16_t)len, PBUF_POOL);
+        if (!p) break;
+
+        pbuf_take(p, rx_tmp, (u16_t)len);
+
+        if (netif->input(p, netif) != ERR_OK) {
+            pbuf_free(p);
+        }
+    }
+}
+
+/* ---- Global network state ---- */
+
+static struct netif e1000_nif;
+static int net_initialized = 0;
+
+void net_init(void) {
+    if (!e1000_link_up()) {
+        uart_print("[NET] E1000 link down, skipping lwIP init.\n");
+        return;
+    }
+
+    lwip_init();
+
+    ip4_addr_t ipaddr, netmask, gw;
+    IP4_ADDR(&ipaddr,  10, 0, 2, 15);   /* QEMU user-mode default */
+    IP4_ADDR(&netmask, 255, 255, 255, 0);
+    IP4_ADDR(&gw,      10, 0, 2, 2);    /* QEMU user-mode gateway */
+
+    netif_add(&e1000_nif, &ipaddr, &netmask, &gw, NULL,
+              e1000_netif_init, ethernet_input);
+    netif_set_default(&e1000_nif);
+    netif_set_up(&e1000_nif);
+
+    net_initialized = 1;
+
+    uart_print("[NET] lwIP initialized, IP=10.0.2.15\n");
+}
+
+void net_poll(void) {
+    if (!net_initialized) return;
+    e1000_netif_poll(&e1000_nif);
+    sys_check_timeouts();
+}
+
+struct netif* net_get_netif(void) {
+    return net_initialized ? &e1000_nif : NULL;
+}
diff --git a/src/net/lwip_port/arch/cc.h b/src/net/lwip_port/arch/cc.h
new file mode 100644 (file)
index 0000000..3b5070a
--- /dev/null
@@ -0,0 +1,38 @@
+#ifndef LWIP_ARCH_CC_H
+#define LWIP_ARCH_CC_H
+
+#include <stdint.h>
+#include <stddef.h>
+
+/* Define byte order for x86 (little-endian) */
+#define BYTE_ORDER LITTLE_ENDIAN
+
+/* Use GCC-style struct packing */
+#define PACK_STRUCT_FIELD(x) x
+#define PACK_STRUCT_STRUCT __attribute__((packed))
+#define PACK_STRUCT_BEGIN
+#define PACK_STRUCT_END
+
+/* Compiler hints */
+#define LWIP_PLATFORM_DIAG(x)   do { } while(0)
+#define LWIP_PLATFORM_ASSERT(x) do { } while(0)
+
+/* Use our own printf-less approach; lwIP only needs these types */
+typedef uint8_t  u8_t;
+typedef int8_t   s8_t;
+typedef uint16_t u16_t;
+typedef int16_t  s16_t;
+typedef uint32_t u32_t;
+typedef int32_t  s32_t;
+typedef uintptr_t mem_ptr_t;
+
+/* Provide format macros for lwIP debug prints (unused with NO_SYS + no debug) */
+#define U16_F "u"
+#define S16_F "d"
+#define X16_F "x"
+#define U32_F "u"
+#define S32_F "d"
+#define X32_F "x"
+#define SZT_F "u"
+
+#endif /* LWIP_ARCH_CC_H */
diff --git a/src/net/lwip_port/lwipopts.h b/src/net/lwip_port/lwipopts.h
new file mode 100644 (file)
index 0000000..2146ebd
--- /dev/null
@@ -0,0 +1,72 @@
+#ifndef LWIPOPTS_H
+#define LWIPOPTS_H
+
+/* ---- NO_SYS mode (raw API, no threads) ---- */
+#define NO_SYS                  1
+#define LWIP_SOCKET             0
+#define LWIP_NETCONN            0
+#define LWIP_NETIF_API          0
+
+/* ---- Memory settings ---- */
+#define MEM_ALIGNMENT           4
+#define MEM_SIZE                (64 * 1024)     /* 64 KB heap for lwIP */
+#define MEMP_NUM_PBUF           32
+#define MEMP_NUM_UDP_PCB        8
+#define MEMP_NUM_TCP_PCB        8
+#define MEMP_NUM_TCP_PCB_LISTEN 4
+#define MEMP_NUM_TCP_SEG        32
+#define MEMP_NUM_ARP_QUEUE      8
+#define PBUF_POOL_SIZE          32
+#define PBUF_POOL_BUFSIZE       1536
+
+/* ---- IPv4 ---- */
+#define LWIP_IPV4               1
+#define LWIP_IPV6               0
+#define LWIP_ARP                1
+#define LWIP_ICMP               1
+#define LWIP_UDP                1
+#define LWIP_TCP                1
+#define LWIP_DHCP               0
+#define LWIP_AUTOIP             0
+#define LWIP_DNS                0
+#define LWIP_IGMP               0
+
+/* ---- TCP tuning ---- */
+#define TCP_MSS                 1460
+#define TCP_WND                 (4 * TCP_MSS)
+#define TCP_SND_BUF             (4 * TCP_MSS)
+#define TCP_SND_QUEUELEN        (4 * TCP_SND_BUF / TCP_MSS)
+#define TCP_QUEUE_OOSEQ         1
+
+/* ---- Checksum ---- */
+#define CHECKSUM_GEN_IP         1
+#define CHECKSUM_GEN_UDP        1
+#define CHECKSUM_GEN_TCP        1
+#define CHECKSUM_GEN_ICMP       1
+#define CHECKSUM_CHECK_IP       1
+#define CHECKSUM_CHECK_UDP      1
+#define CHECKSUM_CHECK_TCP      1
+#define CHECKSUM_CHECK_ICMP     1
+
+/* ---- ARP ---- */
+#define ARP_TABLE_SIZE          10
+#define ARP_QUEUEING            1
+#define ETHARP_SUPPORT_STATIC_ENTRIES 1
+
+/* ---- Debug (all off) ---- */
+#define LWIP_DEBUG              0
+#define LWIP_DBG_TYPES_ON       0
+
+/* ---- Misc ---- */
+#define LWIP_STATS              0
+#define LWIP_PROVIDE_ERRNO      0
+#define LWIP_RAND()             ((u32_t)0x12345678)  /* TODO: proper RNG */
+#define LWIP_TIMERS             1
+#define SYS_LIGHTWEIGHT_PROT    0
+#define LWIP_DONT_PROVIDE_BYTEORDER_FUNCTIONS 1
+
+/* ---- Raw API callbacks ---- */
+#define LWIP_RAW                1
+#define MEMP_NUM_RAW_PCB        4
+
+#endif /* LWIPOPTS_H */
diff --git a/src/net/lwip_port/sys_arch.c b/src/net/lwip_port/sys_arch.c
new file mode 100644 (file)
index 0000000..d8c395e
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+ * lwIP sys_arch for AdrOS — NO_SYS=1 mode.
+ * Only sys_now() is required (for timeouts).
+ */
+#include "lwip/opt.h"
+#include "lwip/sys.h"
+
+extern uint32_t get_tick_count(void);
+
+/* Return milliseconds since boot. Timer runs at 50 Hz → 20 ms per tick. */
+u32_t sys_now(void) {
+    return (u32_t)(get_tick_count() * 20);
+}