// SPDX-License-Identifier: Apache-2.0 // // 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; }