.vscode/
*.swp
*~
+third_party/
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
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),)
# ---- 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)..."
@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 $<"
--- /dev/null
+#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
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);
#include "procfs.h"
#include "pci.h"
#include "e1000.h"
+#include "net.h"
#include "vbe.h"
#include "uart_console.h"
pci_init();
e1000_init();
+ net_init();
vbe_init(bi);
tty_init();
#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. */
// Infinite loop acting as Idle Task
for(;;) {
+ net_poll();
hal_cpu_idle();
}
}
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) {
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';
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+#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 */
--- /dev/null
+#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 */
--- /dev/null
+/*
+ * 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);
+}