Nexoniarz is the sole author and SchOS was never published under Apache-2.0, so this is a straight relicence. LICENSE is the FSF GPLv2 text, every first-party SPDX header moves to GPL-2.0-or-later, and NOTICE, README.md, docs/licensing.md and the site say so. projects/schfs-linux follows the rest of the tree. The GPL gives copyleft: a fork has to ship its source. It also lifts the old "Linux drivers, never" rule, since GPL-2.0-only code combines with GPL-2.0-or-later. "Or later" leaves room for GPLv3, but one binary still has to pick one version, so docs/licensing.md now keeps Apache-2.0 and GPLv3 code out of the kernel to keep Linux code possible there. BSD 4-clause code goes from "avoid" to "no", because the advertising clause is GPL-incompatible. LDLS still ships no .ko. That boundary is now project policy rather than a legal requirement, and ldls.md, devices.md and licensing.md section 6 say so. refmod declares license=GPL, and ldls-check passes (27 checks). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
121 lines
3.9 KiB
C
121 lines
3.9 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
//
|
|
// The lwIP port layer: everything lwIP needs from the system it runs on.
|
|
//
|
|
// With NO_SYS=1 that is a short list -- a clock, a source of randomness, and a
|
|
// lock -- because lwIP is not creating threads or mailboxes of its own. The
|
|
// lock is the interesting one.
|
|
//
|
|
// ---------------------------------------------------------------------------
|
|
// The concurrency model, in full
|
|
//
|
|
// lwIP with NO_SYS=1 is not re-entrant. It is entered from exactly two kinds of
|
|
// place here: the network thread (kernel/net/net.c), which feeds it received
|
|
// frames and runs its timers, and the socket syscalls, which call into it on
|
|
// behalf of a process. Those can run on different CPUs at the same time, so
|
|
// something has to serialise them, and lwIP's own SYS_ARCH_PROTECT hooks are
|
|
// the sanctioned place to put it.
|
|
//
|
|
// A sleeplock rather than a spinlock, because a call into lwIP can reach the
|
|
// driver and the driver can sleep. That makes this lock unusable from an
|
|
// interrupt handler -- which is why the e1000 interrupt does not feed lwIP
|
|
// directly and the network thread polls instead.
|
|
//
|
|
// It is recursive by necessity: lwIP brackets small critical sections inside
|
|
// larger ones and expects nesting to be free.
|
|
|
|
#include "types.h"
|
|
#include "defs.h"
|
|
#include "param.h"
|
|
#include "spinlock.h"
|
|
#include "sleeplock.h"
|
|
#include "proc.h"
|
|
|
|
#include "lwip/opt.h"
|
|
#include "lwip/sys.h"
|
|
|
|
static struct sleeplock lwip_lock;
|
|
static struct proc *lwip_owner;
|
|
static int lwip_depth;
|
|
static int lwip_lock_ready;
|
|
|
|
void
|
|
lwip_sys_init(void)
|
|
{
|
|
initsleeplock(&lwip_lock, "lwip");
|
|
lwip_owner = 0;
|
|
lwip_depth = 0;
|
|
lwip_lock_ready = 1;
|
|
}
|
|
|
|
// Reading `lwip_owner` without holding anything is safe in the one direction it
|
|
// is asked: only the process inside the lock ever writes its own proc pointer
|
|
// there, and it holds the lock the whole time. No other process can see its own
|
|
// pointer here, so a stale read cannot produce a false "I hold it". The null
|
|
// check matters because myproc() is 0 in early boot, and 0 == 0 would otherwise
|
|
// claim the lock for a process that does not exist.
|
|
static int
|
|
lwip_lock_mine(void)
|
|
{
|
|
return lwip_owner != 0 && lwip_owner == myproc();
|
|
}
|
|
|
|
void
|
|
lwip_sys_lock(void)
|
|
{
|
|
// Before lwip_sys_init runs there is exactly one context in existence, so
|
|
// there is nothing to serialise against and the sleeplock is not yet valid.
|
|
if (!lwip_lock_ready)
|
|
return;
|
|
if (lwip_lock_mine()) {
|
|
lwip_depth++;
|
|
return;
|
|
}
|
|
acquiresleep(&lwip_lock);
|
|
lwip_owner = myproc();
|
|
lwip_depth = 1;
|
|
}
|
|
|
|
void
|
|
lwip_sys_unlock(void)
|
|
{
|
|
if (!lwip_lock_ready)
|
|
return;
|
|
if (!lwip_lock_mine())
|
|
return; // not ours; releasing would free someone else's
|
|
if (--lwip_depth > 0)
|
|
return;
|
|
lwip_owner = 0;
|
|
releasesleep(&lwip_lock);
|
|
}
|
|
|
|
// Milliseconds since boot. lwIP drives every timer off this: retransmission,
|
|
// TIME_WAIT, ARP expiry, DHCP renewal. It need not match wall time, but it must
|
|
// advance monotonically -- a clock that goes backwards makes lwIP believe every
|
|
// timer is due at once.
|
|
//
|
|
// `ticks` is the timer interrupt count at 100 Hz, so one tick is 10 ms. Taken
|
|
// without the lock: it is a single aligned word, and a torn read would cost
|
|
// lwIP one early or late timer rather than anything worse.
|
|
u32_t
|
|
sys_now(void)
|
|
{
|
|
return (u32_t)ticks * 10u;
|
|
}
|
|
|
|
// lwIP wants randomness for initial TCP sequence numbers and for the DHCP
|
|
// transaction id. Sequence numbers that are predictable are a real weakness on
|
|
// a network with anyone hostile on it; this is not a CSPRNG and does not claim
|
|
// to be, but it is seeded from a clock that an outsider cannot observe.
|
|
u32_t
|
|
lwip_rand_seed(void)
|
|
{
|
|
static u32_t s;
|
|
if (s == 0)
|
|
s = (u32_t)ticks ^ 0x9E3779B9u;
|
|
// xorshift32
|
|
s ^= s << 13;
|
|
s ^= s >> 17;
|
|
s ^= s << 5;
|
|
return s;
|
|
}
|