diff --git a/include/slab.h b/include/slab.h new file mode 100644 index 0000000..21d6087 --- /dev/null +++ b/include/slab.h @@ -0,0 +1,55 @@ +#ifndef FROSTBYTE_SLAB_H +#define FROSTBYTE_SLAB_H + +#include "types.h" + +/* + * Slab: a fixed-size object cache backed directly by physical frames from + * pmm, bypassing the general-purpose heap. Each slab is exactly one frame; + * the Slab descriptor lives at the start of that same frame (so freeing a + * pointer is O(1): mask it down to the enclosing frame to recover the + * descriptor, no list walk needed), and the rest of the frame is carved + * into objSize-aligned objects threaded onto a singly-linked free list + * through their own (currently-unused) storage. + * + * Intended for kernel-internal fixed-size allocations (list/tree nodes, + * PCB-sized structs, ...) where slab's O(1) alloc/free and per-slab + * locality beat the heap's general-purpose free-list search. + */ + +typedef struct KmemCache { + const char* name; + usize objSize; /* rounded up to at least sizeof(void*), 8-aligned */ + usize objsPerSlab; + struct Slab* slabs; /* every slab owned by this cache, newest first */ + usize slabCount; + usize usedObjs; +} KmemCache; + +/* Returns false if objSize (after rounding) cannot fit at least one object + per frame alongside the slab header -- the cache is left zeroed either + way, so kmemCacheDestroy is always safe to call. */ +bool kmemCacheInit(KmemCache* cache, const char* name, usize objSize); + +/* Releases every frame the cache ever grew into back to pmm. The cache is + left in a freshly-initialized (empty, growable-again) state. */ +void kmemCacheDestroy(KmemCache* cache); + +/* Grows the cache (allocates a new frame from pmm) on demand. nullptr only + if pmm is out of frames. */ +void* kmemCacheAlloc(KmemCache* cache); + +/* obj must have come from kmemCacheAlloc on this exact cache and not + already be free. */ +void kmemCacheFree(KmemCache* cache, void* obj); + +usize kmemCacheUsed(const KmemCache* cache); +/* Objects across every slab the cache has grown into so far -- 0 until the + first kmemCacheAlloc, since slabs are only ever grown lazily on demand. */ +usize kmemCacheCapacity(const KmemCache* cache); +usize kmemCacheSlabCount(const KmemCache* cache); +/* How many objects a single slab (frame) holds for this cache -- fixed at + kmemCacheInit time, independent of how many slabs have been grown. */ +usize kmemCacheObjsPerSlab(const KmemCache* cache); + +#endif diff --git a/include/spinlock.h b/include/spinlock.h new file mode 100644 index 0000000..3527550 --- /dev/null +++ b/include/spinlock.h @@ -0,0 +1,44 @@ +#ifndef FROSTBYTE_SPINLOCK_H +#define FROSTBYTE_SPINLOCK_H + +#include "types.h" + +/* + * SpinLock: an interrupt-safe mutual-exclusion lock for state shared between + * thread context and interrupt handlers (irq0/irq1 currently run with + * interrupts re-enabled per pic.c, so a naive "just disable interrupts" + * critical section is not enough once two handlers can nest). + * + * Acquire disables interrupts and spins on an atomic test-and-set before + * returning; release restores whatever the interrupt flag was at the + * matching acquire. This is correct and SMP-ready even though frostbyte is + * currently single-core: on one core, acquiring an already-held lock is a + * bug (it would spin forever with interrupts off), so spinLockTryAcquire is + * provided for call sites -- and tests -- that need to observe contention + * without deadlocking. + */ + +typedef struct SpinLock { + volatile uint32 locked; +} SpinLock; + +void spinLockInit(SpinLock* lock); + +/* Disables interrupts, then spins until the lock is acquired. Returns the + interrupt flag as it was immediately before this call, to be passed back + to spinLockRelease unchanged (callers must not interleave two different + locks' flags). */ +uint64 spinLockAcquire(SpinLock* lock); + +/* Non-blocking. On success (true), interrupts are disabled and *savedFlags + holds the pre-call flag word, exactly as spinLockAcquire would leave + things. On failure (false) nothing is touched -- interrupts are left + exactly as the caller had them. */ +bool spinLockTryAcquire(SpinLock* lock, uint64* savedFlags); + +/* flags must be the value spinLockAcquire/spinLockTryAcquire handed back. */ +void spinLockRelease(SpinLock* lock, uint64 flags); + +bool spinLockIsLocked(const SpinLock* lock); + +#endif diff --git a/include/syscall.h b/include/syscall.h index 5942c65..1e4ac2d 100644 --- a/include/syscall.h +++ b/include/syscall.h @@ -15,5 +15,59 @@ #define sysGetdents 11 #define sysMkdir 12 #define sysUnlink 13 +#define sysPipe 14 +#define sysSymlink 15 +#define sysReadlink 16 +#define sysLink 17 +#define sysExec 18 +#define sysSpawn 19 +#define sysWait 20 +#define sysPipeCreate 21 +#define sysPipeAttach 22 +#define sysKill 23 +#define sysMkfifo 24 +#define sysSigSend 25 +#define sysSigAction 26 +#define sysSigProcMask 27 +#define sysSigReturn 28 +#define sysAlarm 29 +#define sysSetpgid 30 +#define sysGetpgid 31 +#define sysSetFgPgrp 32 +#define sysGetFgPgrp 33 +#define sysPoll 34 +#define sysChdir 35 +#define sysGetcwd 36 +#define sysGetEnv 37 +#define sysSetEnv 38 +#define sysNice 39 +#define sysGetPriority 40 +#define sysTime 41 +#define sysUnsetEnv 42 +#define sysRmdir 43 +#define sysChmod 44 +#define sysGetuid 45 +#define sysSetuid 46 +#define sysChown 47 +#define sysGetgid 48 +#define sysSetgid 49 +#define sysChgrp 50 +#define sysPidfdOpen 51 +#define sysPidfdSend 52 +#define sysPidfdWait 53 +#define sysMmap 54 +#define sysMunmap 55 + +/* Sentinel pid for sysWait (syscall.c)/userWaitAny (ulib.c): real + waitpid(-1)-style "reap whichever child happens to already be dead, + without caring which specific pid" instead of the ordinary "wait for + exactly this one" contract every other sysWait id value has. + (unsigned int)-1 can never collide with a real pid, since pid 0 (idle) + and every id returned by threadCreate/threadCreateUser are always < + maxThreads (sched.c). Deliberately spelled with the plain C type, not + sched.h's uint32, so this header stays usable from userland (ulib.h/ + ulib.c) exactly like fs.h/signalnum.h's own shared definitions already + are -- see fs.h's FbStat for the same convention. */ +#define waitAnyPid ((unsigned int)-1) #endif diff --git a/include/vec.h b/include/vec.h new file mode 100644 index 0000000..dc05a82 --- /dev/null +++ b/include/vec.h @@ -0,0 +1,46 @@ +#ifndef FROSTBYTE_VEC_H +#define FROSTBYTE_VEC_H + +#include "types.h" + +/* + * Vec: a growable, type-erased array backed by the kernel heap. Elements + * are stored by value (memcpy'd in and out), sized by elemSize so a single + * implementation serves every element type without void* indirection or + * per-type code generation. + */ + +typedef struct Vec { + uint8* data; + usize elemSize; + usize length; + usize capacity; +} Vec; + +void vecInit(Vec* v, usize elemSize); +void vecFree(Vec* v); + +/* Ensures capacity for at least newCapacity elements. Returns false only + if the underlying allocation failed (existing contents are untouched). */ +bool vecReserve(Vec* v, usize newCapacity); + +bool vecPush(Vec* v, const void* elem); +/* Removes and copies out the last element. False (and *outElem untouched) + when the vector is empty; outElem may be nullptr to just drop it. */ +bool vecPop(Vec* v, void* outElem); + +/* Pointer into the backing storage; nullptr if index is out of range. + Invalidated by any push/insert/remove/reserve that reallocates. */ +void* vecAt(Vec* v, usize index); +bool vecSet(Vec* v, usize index, const void* elem); + +bool vecInsert(Vec* v, usize index, const void* elem); +bool vecRemove(Vec* v, usize index, void* outElem); + +void vecClear(Vec* v); + +usize vecLen(const Vec* v); +usize vecCap(const Vec* v); +bool vecIsEmpty(const Vec* v); + +#endif