From: Tulio A M Mendes Date: Wed, 11 Feb 2026 23:43:33 +0000 (-0300) Subject: feat: lwIP TCP/IP stack integration with E1000 netif X-Git-Url: https://projects.tadryanom.me/docs/POSIX_ROADMAP.md?a=commitdiff_plain;h=704a45a934eed65363e47528127b97f8054a9db1;p=AdrOS.git feat: lwIP TCP/IP stack integration with E1000 netif - 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 --- diff --git a/.gitignore b/.gitignore index 9a38798..359e9a7 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,4 @@ supplementary-material .vscode/ *.swp *~ +third_party/ diff --git a/Makefile b/Makefile index 4b0a136..686d3ce 100644 --- 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 index 0000000..d129611 --- /dev/null +++ b/include/net.h @@ -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 diff --git a/include/utils.h b/include/utils.h index 107e36e..eab460b 100644 --- a/include/utils.h +++ b/include/utils.h @@ -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); diff --git a/src/kernel/init.c b/src/kernel/init.c index 261ae98..7547bdd 100644 --- a/src/kernel/init.c +++ b/src/kernel/init.c @@ -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(); diff --git a/src/kernel/main.c b/src/kernel/main.c index a85847d..bab32ee 100644 --- a/src/kernel/main.c +++ b/src/kernel/main.c @@ -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(); } } diff --git a/src/kernel/utils.c b/src/kernel/utils.c index 63f6822..f326401 100644 --- a/src/kernel/utils.c +++ b/src/kernel/utils.c @@ -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 index 0000000..c9ab72d --- /dev/null +++ b/src/net/e1000_netif.c @@ -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 index 0000000..3b5070a --- /dev/null +++ b/src/net/lwip_port/arch/cc.h @@ -0,0 +1,38 @@ +#ifndef LWIP_ARCH_CC_H +#define LWIP_ARCH_CC_H + +#include +#include + +/* 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 index 0000000..2146ebd --- /dev/null +++ b/src/net/lwip_port/lwipopts.h @@ -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 index 0000000..d8c395e --- /dev/null +++ b/src/net/lwip_port/sys_arch.c @@ -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); +}