add lwIP TCP/IP stack as a runtime driver, native socket syscalls, SysGetMemoryStatus replacing CatchAllDebugBucket, fix timeGetTime to return milliseconds, fix SysSleep to interpret argument as milliseconds, SysGetTick now returns microseconds, harden all syscalls against bad user pointers with CopyFromUser/CopyToUser/MmProbeUser throughout, add networking test apps, port-forward 8080 in runa.bat, split LWIP_DRIVER_CFLAGS, add settings.ini to deploy, remove ethernet.c stub

This commit is contained in:
maksw committed 2026-05-22 18:54:53 +02:00
1 parent 18b4dd0f61
commit df13b8056c
365 files changed
+143263 -1362

No files matched your search

+363
View File
@@ -0,0 +1,363 @@
// lwip_sys_arch.c - lwIP OS abstraction layer for the kernel.
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
#include <intrin.h>
#include <lwip/opt.h>
#include <lwip/sys.h>
#include <lwip/err.h>
#include <mm_virt.h>
#include <ob_object.h>
#include <ke_debug.h>
#include <ke_sched.h>
#include <ke_thread.h>
#include <ke_semaphore.h>
#include <ke_spinlock.h>
#include <ke_irql.h>
#define LWIP_PORT_MBOX_DEFAULT_SIZE 128u
struct sys_sem {
SEMAPHORE* Sem;
};
struct sys_mutex {
SEMAPHORE* Sem;
};
struct sys_mbox {
SPINLOCK Lock;
SEMAPHORE* Items;
void** Ring;
uint32_t Size;
uint32_t Head;
uint32_t Tail;
uint32_t Count;
uint32_t Valid;
};
static uint64_t (*gLwipQueryTicksUs)(void) = 0;
static uint32_t gRandState = 0x4b4e4554u; /* 'KNET' */
void LwipPortSetTimerQuery(uint64_t (*query_ticks_us)(void)) {
gLwipQueryTicksUs = query_ticks_us;
}
unsigned int lwip_port_rand(void) {
/* Tiny deterministic LCG. Good enough for ephemeral port randomisation. */
gRandState = gRandState * 1664525u + 1013904223u;
return gRandState;
}
void sys_init(void) {
}
u32_t sys_now(void) {
if (!gLwipQueryTicksUs)
return 0;
return (u32_t)(gLwipQueryTicksUs() / 1000u);
}
u32_t sys_jiffies(void) {
return sys_now();
}
/*
* lwIP's core/sys.c provides sys_msleep() when it is not a macro. Do not
* define it here as well, or MSVC links both sys.obj and lwip_sys_arch.obj
* with a duplicate sys_msleep symbol.
*/
/*
* First bring-up errno storage for lwIP's socket/netdb compatibility layer.
* This is global, not per-thread; the kernel socket-object wrapper should
* eventually translate lwIP errors to KSTATUS instead of exposing this.
*/
// fuckass microsoft
#ifdef errno
#undef errno
#endif
int errno;
int* __cdecl _errno(void) { return &errno; }
err_t sys_sem_new(sys_sem_t* sem, u8_t count) {
if (!sem)
return ERR_ARG;
struct sys_sem* s = (struct sys_sem*)MmAllocatePool(sizeof(*s));
if (!s)
return ERR_MEM;
memset(s, 0, sizeof(*s));
SEMAPHORE* ksem = 0;
if (!KSUCCESS(KeSemaphoreCreate(count ? 1 : 0, 0x7fffffff, &ksem))) {
MmFreePool(s);
return ERR_MEM;
}
s->Sem = ksem;
*sem = s;
return ERR_OK;
}
void sys_sem_signal(sys_sem_t* sem) {
if (sem && *sem && (*sem)->Sem)
KeSignalSemaphore((*sem)->Sem);
}
u32_t sys_arch_sem_wait(sys_sem_t* sem, u32_t timeout) {
if (!sem || !*sem || !(*sem)->Sem)
return SYS_ARCH_TIMEOUT;
u32_t start = sys_now();
if (timeout == 0) {
KSTATUS st = KeWaitSemaphore((*sem)->Sem);
return KSUCCESS(st) ? (u32_t)(sys_now() - start) : SYS_ARCH_TIMEOUT;
}
for (;;) {
if (KSUCCESS(KeTrySemaphore((*sem)->Sem)))
return (u32_t)(sys_now() - start);
if ((u32_t)(sys_now() - start) >= timeout)
return SYS_ARCH_TIMEOUT;
KeYield();
}
}
void sys_sem_free(sys_sem_t* sem) {
if (!sem || !*sem)
return;
if ((*sem)->Sem)
ObDereferenceObject(&(*sem)->Sem->Header);
MmFreePool(*sem);
*sem = 0;
}
err_t sys_mutex_new(sys_mutex_t* mutex) {
if (!mutex)
return ERR_ARG;
struct sys_mutex* m = (struct sys_mutex*)MmAllocatePool(sizeof(*m));
if (!m)
return ERR_MEM;
memset(m, 0, sizeof(*m));
SEMAPHORE* sem = 0;
if (!KSUCCESS(KeSemaphoreCreate(1, 1, &sem))) {
MmFreePool(m);
return ERR_MEM;
}
m->Sem = sem;
*mutex = m;
return ERR_OK;
}
void sys_mutex_lock(sys_mutex_t* mutex) {
if (mutex && *mutex && (*mutex)->Sem)
(void)KeWaitSemaphore((*mutex)->Sem);
}
void sys_mutex_unlock(sys_mutex_t* mutex) {
if (mutex && *mutex && (*mutex)->Sem)
KeSignalSemaphore((*mutex)->Sem);
}
void sys_mutex_free(sys_mutex_t* mutex) {
if (!mutex || !*mutex)
return;
if ((*mutex)->Sem)
ObDereferenceObject(&(*mutex)->Sem->Header);
MmFreePool(*mutex);
*mutex = 0;
}
static err_t LwipMboxTryPost(sys_mbox_t* mbox, void* msg) {
if (!mbox || !*mbox || !(*mbox)->Valid)
return ERR_ARG;
struct sys_mbox* m = *mbox;
uintptr_t flags = KeAcquireSpinlock(&m->Lock);
if (m->Count >= m->Size) {
KeReleaseSpinlock(&m->Lock, flags);
return ERR_MEM;
}
m->Ring[m->Tail] = msg;
m->Tail = (m->Tail + 1u) % m->Size;
m->Count++;
KeReleaseSpinlock(&m->Lock, flags);
KeSignalSemaphore(m->Items);
return ERR_OK;
}
err_t sys_mbox_new(sys_mbox_t* mbox, int size) {
if (!mbox)
return ERR_ARG;
uint32_t n = (size > 0) ? (uint32_t)size : LWIP_PORT_MBOX_DEFAULT_SIZE;
struct sys_mbox* m = (struct sys_mbox*)MmAllocatePool(sizeof(*m));
if (!m)
return ERR_MEM;
memset(m, 0, sizeof(*m));
m->Ring = (void**)MmAllocatePool(sizeof(void*) * n);
if (!m->Ring) {
MmFreePool(m);
return ERR_MEM;
}
memset(m->Ring, 0, sizeof(void*) * n);
SEMAPHORE* items = 0;
if (!KSUCCESS(KeSemaphoreCreate(0, (int32_t)n, &items))) {
MmFreePool(m->Ring);
MmFreePool(m);
return ERR_MEM;
}
m->Lock = (SPINLOCK)SPINLOCK_INIT;
m->Items = items;
m->Size = n;
m->Valid = 1;
*mbox = m;
return ERR_OK;
}
void sys_mbox_post(sys_mbox_t* mbox, void* msg) {
/* lwIP promises this is not called from ISR context. */
while (LwipMboxTryPost(mbox, msg) != ERR_OK)
KeYield();
}
err_t sys_mbox_trypost(sys_mbox_t* mbox, void* msg) {
return LwipMboxTryPost(mbox, msg);
}
err_t sys_mbox_trypost_fromisr(sys_mbox_t* mbox, void* msg) {
return LwipMboxTryPost(mbox, msg);
}
static u32_t LwipMboxPopAfterItem(sys_mbox_t* mbox, void** msg, u32_t waited) {
struct sys_mbox* m = *mbox;
uintptr_t flags = KeAcquireSpinlock(&m->Lock);
if (m->Count == 0) {
KeReleaseSpinlock(&m->Lock, flags);
return SYS_ARCH_TIMEOUT;
}
void* value = m->Ring[m->Head];
m->Ring[m->Head] = 0;
m->Head = (m->Head + 1u) % m->Size;
m->Count--;
KeReleaseSpinlock(&m->Lock, flags);
if (msg)
*msg = value;
return waited;
}
u32_t sys_arch_mbox_fetch(sys_mbox_t* mbox, void** msg, u32_t timeout) {
if (!mbox || !*mbox || !(*mbox)->Valid)
return SYS_ARCH_TIMEOUT;
u32_t start = sys_now();
if (timeout == 0) {
KSTATUS st = KeWaitSemaphore((*mbox)->Items);
if (!KSUCCESS(st))
return SYS_ARCH_TIMEOUT;
return LwipMboxPopAfterItem(mbox, msg, (u32_t)(sys_now() - start));
}
for (;;) {
if (KSUCCESS(KeTrySemaphore((*mbox)->Items)))
return LwipMboxPopAfterItem(mbox, msg, (u32_t)(sys_now() - start));
if ((u32_t)(sys_now() - start) >= timeout)
return SYS_ARCH_TIMEOUT;
KeYield();
}
}
u32_t sys_arch_mbox_tryfetch(sys_mbox_t* mbox, void** msg) {
if (!mbox || !*mbox || !(*mbox)->Valid)
return SYS_MBOX_EMPTY;
if (!KSUCCESS(KeTrySemaphore((*mbox)->Items)))
return SYS_MBOX_EMPTY;
return LwipMboxPopAfterItem(mbox, msg, 0);
}
void sys_mbox_free(sys_mbox_t* mbox) {
if (!mbox || !*mbox)
return;
struct sys_mbox* m = *mbox;
m->Valid = 0;
if (m->Items)
ObDereferenceObject(&m->Items->Header);
if (m->Ring)
MmFreePool(m->Ring);
MmFreePool(m);
*mbox = 0;
}
sys_thread_t sys_thread_new(const char* name, lwip_thread_fn thread, void* arg,
int stacksize, int prio) {
(void)name;
(void)stacksize;
if (!thread)
KePanic("lwIP: sys_thread_new called with null entry\n");
THREAD_PRIORITY kp = PriorityNormal;
if (prio >= 2)
kp = PriorityHigh;
else if (prio <= 0)
kp = PriorityLow;
/*
* lwIP thread entries already use the same ABI as kernel worker threads:
*
* void entry(void* arg)
*
* Do not bounce through a heap-allocated thunk. The first scaffold used a
* tiny { function, argument } heap block and a wrapper, but an early boot
* crash showed the wrapper sometimes loaded a bogus function pointer and
* jumped into lwIP's .rdata strings. Passing the lwIP entry directly keeps
* the initial thread stack simple: KeKernelThreadEntry calls exactly the
* function pointer that lwIP supplied, with exactly the argument it supplied.
*/
THREAD* t = 0;
KSTATUS st = KeCreateKernelThread((uintptr_t)thread,
(uintptr_t)arg,
kp,
&t);
if (!KSUCCESS(st))
KePanic("lwIP: failed to create kernel thread: 0x%x\n", st);
return (sys_thread_t)t;
}
sys_prot_t sys_arch_protect(void) {
uintptr_t flags = __readeflags();
_disable();
return (sys_prot_t)flags;
}
void sys_arch_unprotect(sys_prot_t pval) {
if (((uintptr_t)pval) & 0x200u)
_enable();
else
_disable();
}