mirror of
https://github.com/subhobhai943/SUB-OS.git
synced 2026-10-25 16:52:29 +00:00
Merge branch 'main' into feat/gui-desktop
This commit is contained in:
commit
29d7726a91
22 files changed
+3631
-5
No files matched your search
@@ -0,0 +1,62 @@
|
||||
#ifndef _KERNEL_FUTEX_H
|
||||
#define _KERNEL_FUTEX_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <kernel/wait.h>
|
||||
|
||||
// Fast Userspace muTEX. A futex is an ordinary 32-bit word that stays entirely
|
||||
// in user memory while uncontended; the kernel is only consulted when a thread
|
||||
// actually has to block, at which point the word's address is hashed onto one
|
||||
// of FUTEX_BUCKETS wait queues.
|
||||
|
||||
#define FUTEX_BUCKETS 32
|
||||
|
||||
typedef enum {
|
||||
FUTEX_OP_WAIT = 0,
|
||||
FUTEX_OP_WAKE = 1,
|
||||
FUTEX_OP_REQUEUE = 2,
|
||||
FUTEX_OP_WAKE_ALL = 3
|
||||
} futex_op_t;
|
||||
|
||||
typedef struct {
|
||||
uint64_t waits;
|
||||
uint64_t wakes;
|
||||
uint64_t requeues;
|
||||
uint64_t spurious;
|
||||
uint64_t timeouts;
|
||||
uint32_t active_waiters;
|
||||
} futex_stats_t;
|
||||
|
||||
void futex_init(void);
|
||||
|
||||
// Sleep on `uaddr` only while *uaddr still equals `expected`. Returns 0 when
|
||||
// woken, 1 on timeout, -1 if the value changed before parking (the caller
|
||||
// should retry its fast path).
|
||||
int futex_wait(volatile uint32_t* uaddr, uint32_t expected, uint64_t timeout_ticks);
|
||||
|
||||
// Wake up to `count` waiters parked on `uaddr`. Returns how many were woken.
|
||||
int futex_wake(volatile uint32_t* uaddr, int count);
|
||||
|
||||
// Move waiters from `uaddr1` onto `uaddr2` without waking them (used to avoid
|
||||
// the thundering herd when a condvar hands off to a mutex).
|
||||
int futex_requeue(volatile uint32_t* uaddr1, volatile uint32_t* uaddr2, int count);
|
||||
|
||||
futex_stats_t futex_get_stats(void);
|
||||
void futex_dump(void);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// A futex-backed mutex, for kernel code that wants sleeping mutual exclusion.
|
||||
// ---------------------------------------------------------------------------
|
||||
typedef struct {
|
||||
volatile uint32_t state; // 0 = unlocked, 1 = locked, 2 = locked + waiters
|
||||
char name[24];
|
||||
} fmutex_t;
|
||||
|
||||
void fmutex_init(fmutex_t* m, const char* name);
|
||||
void fmutex_lock(fmutex_t* m);
|
||||
bool fmutex_trylock(fmutex_t* m);
|
||||
void fmutex_unlock(fmutex_t* m);
|
||||
|
||||
#endif // _KERNEL_FUTEX_H
|
||||
@@ -0,0 +1,81 @@
|
||||
#ifndef _KERNEL_KTEST_H
|
||||
#define _KERNEL_KTEST_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// In-kernel self-test harness (KUnit-style). Suites register a list of cases;
|
||||
// `ktest` from the LazyBox shell runs them and reports pass/fail per assertion.
|
||||
|
||||
#define KTEST_MAX_SUITES 16
|
||||
#define KTEST_MAX_CASES 24
|
||||
|
||||
typedef struct ktest_context {
|
||||
const char* suite;
|
||||
const char* test;
|
||||
uint32_t assertions;
|
||||
uint32_t failures;
|
||||
bool aborted;
|
||||
} ktest_context_t;
|
||||
|
||||
typedef void (*ktest_fn_t)(ktest_context_t* ctx);
|
||||
|
||||
typedef struct {
|
||||
const char* name;
|
||||
ktest_fn_t func;
|
||||
} ktest_case_t;
|
||||
|
||||
typedef struct {
|
||||
const char* name;
|
||||
const ktest_case_t* cases;
|
||||
size_t case_count;
|
||||
} ktest_suite_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t suites_run;
|
||||
uint32_t tests_run;
|
||||
uint32_t tests_passed;
|
||||
uint32_t tests_failed;
|
||||
uint32_t assertions;
|
||||
uint32_t assertion_failures;
|
||||
uint64_t elapsed_ticks;
|
||||
} ktest_result_t;
|
||||
|
||||
void ktest_init(void);
|
||||
int ktest_register_suite(const ktest_suite_t* suite);
|
||||
|
||||
// Run every suite, or one by name. Returns the number of failed tests.
|
||||
int ktest_run_all(ktest_result_t* result_out);
|
||||
int ktest_run_suite(const char* name, ktest_result_t* result_out);
|
||||
void ktest_list_suites(void);
|
||||
|
||||
// Assertion primitives used by the macros below.
|
||||
void ktest_report(ktest_context_t* ctx, bool passed, const char* expr,
|
||||
const char* file, int line, const char* detail);
|
||||
|
||||
#define KTEST_ASSERT(ctx, cond) \
|
||||
ktest_report((ctx), (cond), #cond, __FILE__, __LINE__, NULL)
|
||||
|
||||
#define KTEST_ASSERT_EQ(ctx, a, b) \
|
||||
do { \
|
||||
long long _a = (long long)(a); \
|
||||
long long _b = (long long)(b); \
|
||||
ktest_expect_eq((ctx), _a, _b, #a " == " #b, __FILE__, __LINE__); \
|
||||
} while (0)
|
||||
|
||||
#define KTEST_ASSERT_STREQ(ctx, a, b) \
|
||||
ktest_expect_streq((ctx), (a), (b), #a " == " #b, __FILE__, __LINE__)
|
||||
|
||||
#define KTEST_ASSERT_NOT_NULL(ctx, p) \
|
||||
ktest_report((ctx), (p) != NULL, #p " != NULL", __FILE__, __LINE__, NULL)
|
||||
|
||||
void ktest_expect_eq(ktest_context_t* ctx, long long a, long long b,
|
||||
const char* expr, const char* file, int line);
|
||||
void ktest_expect_streq(ktest_context_t* ctx, const char* a, const char* b,
|
||||
const char* expr, const char* file, int line);
|
||||
|
||||
// Built-in suites covering the core data structures and subsystems.
|
||||
void ktest_register_builtin_suites(void);
|
||||
|
||||
#endif // _KERNEL_KTEST_H
|
||||
@@ -0,0 +1,58 @@
|
||||
#ifndef _KERNEL_RCU_H
|
||||
#define _KERNEL_RCU_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// Tiny RCU (Read-Copy-Update).
|
||||
//
|
||||
// Readers are free: rcu_read_lock() only bumps a per-CPU nesting depth, so a
|
||||
// lookup never contends with an update. Writers publish a new version with
|
||||
// rcu_assign_pointer() and defer reclamation of the old one until every reader
|
||||
// that could still hold a reference has finished — a "grace period".
|
||||
|
||||
#define RCU_MAX_CALLBACKS 64
|
||||
|
||||
typedef void (*rcu_callback_t)(void* arg);
|
||||
|
||||
typedef struct {
|
||||
uint64_t grace_periods;
|
||||
uint64_t callbacks_queued;
|
||||
uint64_t callbacks_invoked;
|
||||
uint64_t read_locks;
|
||||
uint32_t nesting_depth;
|
||||
uint32_t pending;
|
||||
} rcu_stats_t;
|
||||
|
||||
void rcu_init(void);
|
||||
|
||||
void rcu_read_lock(void);
|
||||
void rcu_read_unlock(void);
|
||||
bool rcu_read_lock_held(void);
|
||||
|
||||
// Block until every reader active at call time has left its critical section.
|
||||
void rcu_synchronize(void);
|
||||
|
||||
// Queue `func(arg)` to run once the current grace period elapses.
|
||||
int call_rcu(rcu_callback_t func, void* arg);
|
||||
|
||||
// Free `ptr` after a grace period (the common case for call_rcu).
|
||||
int kfree_rcu(void* ptr);
|
||||
|
||||
// Advance the state machine; called from the timer tick and from rcu_synchronize.
|
||||
void rcu_process_callbacks(void);
|
||||
|
||||
uint64_t rcu_get_grace_period(void);
|
||||
rcu_stats_t rcu_get_stats(void);
|
||||
void rcu_dump(void);
|
||||
|
||||
// Publish/consume helpers. The barriers keep the compiler from reordering the
|
||||
// initialisation of an object past the store that makes it visible.
|
||||
#define rcu_assign_pointer(p, v) \
|
||||
do { __atomic_store_n(&(p), (v), __ATOMIC_RELEASE); } while (0)
|
||||
|
||||
#define rcu_dereference(p) \
|
||||
__atomic_load_n(&(p), __ATOMIC_ACQUIRE)
|
||||
|
||||
#endif // _KERNEL_RCU_H
|
||||
@@ -0,0 +1,65 @@
|
||||
#ifndef _KERNEL_WAIT_H
|
||||
#define _KERNEL_WAIT_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <lib/list.h>
|
||||
#include <kernel/sync.h>
|
||||
#include <kernel/task.h>
|
||||
|
||||
// Wait queues and completions.
|
||||
//
|
||||
// SUB-OS runs a cooperative round-robin scheduler, so a sleeper parks itself
|
||||
// on the queue, marks its task BLOCKED and yields until a waker flips the
|
||||
// entry's `woken` flag. Timed waits fall back to the PIT tick counter.
|
||||
|
||||
typedef struct wait_entry {
|
||||
task_t* task;
|
||||
volatile bool woken;
|
||||
struct list_head node;
|
||||
} wait_entry_t;
|
||||
|
||||
typedef struct wait_queue {
|
||||
char name[24];
|
||||
struct list_head waiters;
|
||||
spinlock_t lock;
|
||||
uint64_t wakeups;
|
||||
uint64_t sleeps;
|
||||
uint32_t nr_waiting;
|
||||
} wait_queue_t;
|
||||
|
||||
void wait_queue_init(wait_queue_t* wq, const char* name);
|
||||
|
||||
// Park the caller until woken. Returns 0 on wake, -1 if the queue is full.
|
||||
int wait_event(wait_queue_t* wq);
|
||||
|
||||
// Park the caller for at most `timeout_ticks` PIT ticks (100 Hz).
|
||||
// Returns 0 if woken, 1 if the wait timed out.
|
||||
int wait_event_timeout(wait_queue_t* wq, uint64_t timeout_ticks);
|
||||
|
||||
// Wake the longest-waiting sleeper, or every sleeper. Returns the count woken.
|
||||
int wake_up(wait_queue_t* wq);
|
||||
int wake_up_all(wait_queue_t* wq);
|
||||
|
||||
uint32_t wait_queue_waiters(const wait_queue_t* wq);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Completions: one-shot "this is done" barriers built on a wait queue.
|
||||
// ---------------------------------------------------------------------------
|
||||
typedef struct completion {
|
||||
wait_queue_t wq;
|
||||
volatile bool done;
|
||||
} completion_t;
|
||||
|
||||
void completion_init(completion_t* c, const char* name);
|
||||
void wait_for_completion(completion_t* c);
|
||||
int wait_for_completion_timeout(completion_t* c, uint64_t timeout_ticks);
|
||||
void complete(completion_t* c);
|
||||
void complete_all(completion_t* c);
|
||||
bool completion_done(const completion_t* c);
|
||||
|
||||
void wait_subsystem_init(void);
|
||||
void wait_dump_stats(void);
|
||||
|
||||
#endif // _KERNEL_WAIT_H
|
||||
@@ -0,0 +1,50 @@
|
||||
#ifndef _LIB_HASHTABLE_H
|
||||
#define _LIB_HASHTABLE_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// Separately-chained hash table with FNV-1a hashing and automatic growth.
|
||||
// Keys are either NUL-terminated strings (owned copies) or raw 64-bit ids.
|
||||
|
||||
typedef struct hnode {
|
||||
uint64_t hash;
|
||||
char* skey; // NULL for integer-keyed entries
|
||||
uint64_t ikey;
|
||||
void* value;
|
||||
struct hnode* next;
|
||||
} hnode_t;
|
||||
|
||||
typedef struct hashtable {
|
||||
hnode_t** buckets;
|
||||
size_t bucket_count;
|
||||
size_t entries;
|
||||
size_t collisions;
|
||||
bool string_keys;
|
||||
} hashtable_t;
|
||||
|
||||
uint64_t hash_fnv1a(const void* data, size_t len);
|
||||
uint64_t hash_string(const char* s);
|
||||
uint64_t hash_u64(uint64_t v);
|
||||
|
||||
hashtable_t* hashtable_create(size_t initial_buckets, bool string_keys);
|
||||
void hashtable_destroy(hashtable_t* ht);
|
||||
|
||||
int hashtable_put(hashtable_t* ht, const char* key, void* value);
|
||||
void* hashtable_get(const hashtable_t* ht, const char* key);
|
||||
bool hashtable_remove(hashtable_t* ht, const char* key);
|
||||
|
||||
int hashtable_put_u64(hashtable_t* ht, uint64_t key, void* value);
|
||||
void* hashtable_get_u64(const hashtable_t* ht, uint64_t key);
|
||||
bool hashtable_remove_u64(hashtable_t* ht, uint64_t key);
|
||||
|
||||
size_t hashtable_size(const hashtable_t* ht);
|
||||
size_t hashtable_buckets(const hashtable_t* ht);
|
||||
size_t hashtable_collisions(const hashtable_t* ht);
|
||||
size_t hashtable_longest_chain(const hashtable_t* ht);
|
||||
|
||||
typedef void (*hashtable_iter_fn)(const hnode_t* node, void* ctx);
|
||||
void hashtable_foreach(const hashtable_t* ht, hashtable_iter_fn fn, void* ctx);
|
||||
|
||||
#endif // _LIB_HASHTABLE_H
|
||||
@@ -0,0 +1,40 @@
|
||||
#ifndef _LIB_KFIFO_H
|
||||
#define _LIB_KFIFO_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <kernel/sync.h>
|
||||
|
||||
// Power-of-two byte ring buffer with an unsigned index-wrap discipline,
|
||||
// mirroring the classic Linux kfifo. Producer and consumer indices are
|
||||
// free-running; masking happens only at access time.
|
||||
|
||||
typedef struct kfifo {
|
||||
uint8_t* buffer;
|
||||
size_t size; // Always a power of two
|
||||
size_t mask; // size - 1
|
||||
size_t in; // Free-running producer index
|
||||
size_t out; // Free-running consumer index
|
||||
bool owns_buffer;
|
||||
spinlock_t lock;
|
||||
} kfifo_t;
|
||||
|
||||
int kfifo_alloc(kfifo_t* fifo, size_t size);
|
||||
int kfifo_init_static(kfifo_t* fifo, void* buffer, size_t size);
|
||||
void kfifo_free(kfifo_t* fifo);
|
||||
void kfifo_reset(kfifo_t* fifo);
|
||||
|
||||
size_t kfifo_in(kfifo_t* fifo, const void* src, size_t len);
|
||||
size_t kfifo_out(kfifo_t* fifo, void* dst, size_t len);
|
||||
size_t kfifo_peek(const kfifo_t* fifo, void* dst, size_t len);
|
||||
|
||||
size_t kfifo_len(const kfifo_t* fifo);
|
||||
size_t kfifo_avail(const kfifo_t* fifo);
|
||||
bool kfifo_is_empty(const kfifo_t* fifo);
|
||||
bool kfifo_is_full(const kfifo_t* fifo);
|
||||
|
||||
int kfifo_put_byte(kfifo_t* fifo, uint8_t byte);
|
||||
int kfifo_get_byte(kfifo_t* fifo, uint8_t* byte_out);
|
||||
|
||||
#endif // _LIB_KFIFO_H
|
||||
@@ -0,0 +1,63 @@
|
||||
#ifndef _LIB_RBTREE_H
|
||||
#define _LIB_RBTREE_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// Linux-style Red-Black Tree: O(log n) ordered container used by the
|
||||
// CFS-style scheduler index, VMA lookup and the page cache radix index.
|
||||
|
||||
#define RB_RED 0
|
||||
#define RB_BLACK 1
|
||||
|
||||
struct rb_node {
|
||||
struct rb_node* rb_parent;
|
||||
struct rb_node* rb_left;
|
||||
struct rb_node* rb_right;
|
||||
int rb_color;
|
||||
};
|
||||
|
||||
struct rb_root {
|
||||
struct rb_node* rb_node;
|
||||
};
|
||||
|
||||
#define RB_ROOT_INIT { NULL }
|
||||
|
||||
#define rb_entry(ptr, type, member) \
|
||||
((type*)((char*)(ptr) - offsetof(type, member)))
|
||||
|
||||
static inline void rb_root_init(struct rb_root* root) {
|
||||
root->rb_node = NULL;
|
||||
}
|
||||
|
||||
static inline bool rb_empty(const struct rb_root* root) {
|
||||
return root->rb_node == NULL;
|
||||
}
|
||||
|
||||
// Attach a freshly allocated node into the tree, then recolour.
|
||||
static inline void rb_link_node(struct rb_node* node, struct rb_node* parent,
|
||||
struct rb_node** rb_link) {
|
||||
node->rb_parent = parent;
|
||||
node->rb_color = RB_RED;
|
||||
node->rb_left = NULL;
|
||||
node->rb_right = NULL;
|
||||
*rb_link = node;
|
||||
}
|
||||
|
||||
void rb_insert_color(struct rb_node* node, struct rb_root* root);
|
||||
void rb_erase(struct rb_node* node, struct rb_root* root);
|
||||
|
||||
struct rb_node* rb_first(const struct rb_root* root);
|
||||
struct rb_node* rb_last(const struct rb_root* root);
|
||||
struct rb_node* rb_next(const struct rb_node* node);
|
||||
struct rb_node* rb_prev(const struct rb_node* node);
|
||||
|
||||
size_t rb_count(const struct rb_root* root);
|
||||
int rb_black_height(const struct rb_root* root);
|
||||
bool rb_validate(const struct rb_root* root);
|
||||
|
||||
#define rb_for_each(pos, root) \
|
||||
for (pos = rb_first(root); pos != NULL; pos = rb_next(pos))
|
||||
|
||||
#endif // _LIB_RBTREE_H
|
||||
@@ -0,0 +1,63 @@
|
||||
#ifndef _MM_PAGE_CACHE_H
|
||||
#define _MM_PAGE_CACHE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// Write-back page cache sitting between the VFS and the block layer.
|
||||
//
|
||||
// Every entry caches PAGE_CACHE_BLOCK_SIZE bytes of one block device, keyed by
|
||||
// (device, page index). Lookups go through a hash table; eviction follows a
|
||||
// CLOCK approximation of LRU, and dirty pages are written back before reuse.
|
||||
|
||||
#define PAGE_CACHE_BLOCK_SIZE 4096
|
||||
#define PAGE_CACHE_SECTORS (PAGE_CACHE_BLOCK_SIZE / 512)
|
||||
#define PAGE_CACHE_MAX_PAGES 64
|
||||
|
||||
typedef struct page_cache_entry {
|
||||
char dev_name[32];
|
||||
uint64_t page_index; // Offset in PAGE_CACHE_BLOCK_SIZE units
|
||||
uint8_t* data;
|
||||
bool valid;
|
||||
bool dirty;
|
||||
bool referenced; // CLOCK second-chance bit
|
||||
uint32_t pin_count;
|
||||
uint64_t last_access;
|
||||
uint64_t hits;
|
||||
} page_cache_entry_t;
|
||||
|
||||
typedef struct {
|
||||
uint64_t hits;
|
||||
uint64_t misses;
|
||||
uint64_t evictions;
|
||||
uint64_t writebacks;
|
||||
uint64_t read_ios;
|
||||
uint64_t write_ios;
|
||||
uint32_t resident_pages;
|
||||
uint32_t dirty_pages;
|
||||
size_t memory_bytes;
|
||||
} page_cache_stats_t;
|
||||
|
||||
void page_cache_init(void);
|
||||
|
||||
// Read/write through the cache. Offsets and lengths are arbitrary; the cache
|
||||
// splits them across pages internally. Both return bytes transferred.
|
||||
size_t page_cache_read(const char* dev_name, uint64_t offset, void* buf, size_t len);
|
||||
size_t page_cache_write(const char* dev_name, uint64_t offset, const void* buf, size_t len);
|
||||
|
||||
// Borrow a page directly (pinned until page_cache_release).
|
||||
page_cache_entry_t* page_cache_get(const char* dev_name, uint64_t page_index);
|
||||
void page_cache_release(page_cache_entry_t* entry);
|
||||
void page_cache_mark_dirty(page_cache_entry_t* entry);
|
||||
|
||||
int page_cache_sync(void); // Flush every dirty page
|
||||
int page_cache_sync_dev(const char* dev_name); // Flush one device
|
||||
int page_cache_invalidate(const char* dev_name); // Drop clean pages
|
||||
void page_cache_drop_all(void);
|
||||
|
||||
page_cache_stats_t page_cache_get_stats(void);
|
||||
uint32_t page_cache_hit_percent(void);
|
||||
void page_cache_dump(void);
|
||||
|
||||
#endif // _MM_PAGE_CACHE_H
|
||||
@@ -0,0 +1,22 @@
|
||||
#ifndef _USERLAND_COREUTILS_H
|
||||
#define _USERLAND_COREUTILS_H
|
||||
|
||||
// GNU-style text-processing applets for LazyBox. Each entry point matches
|
||||
// applet_fn_t so it can be placed directly in the LazyBox dispatch table.
|
||||
|
||||
int coreutils_sort(int argc, char** argv);
|
||||
int coreutils_uniq(int argc, char** argv);
|
||||
int coreutils_cut(int argc, char** argv);
|
||||
int coreutils_tr(int argc, char** argv);
|
||||
int coreutils_rev(int argc, char** argv);
|
||||
int coreutils_tac(int argc, char** argv);
|
||||
int coreutils_nl(int argc, char** argv);
|
||||
int coreutils_seq(int argc, char** argv);
|
||||
int coreutils_diff(int argc, char** argv);
|
||||
int coreutils_xxd(int argc, char** argv);
|
||||
int coreutils_du(int argc, char** argv);
|
||||
int coreutils_factor(int argc, char** argv);
|
||||
int coreutils_sum(int argc, char** argv);
|
||||
int coreutils_truncate(int argc, char** argv);
|
||||
|
||||
#endif // _USERLAND_COREUTILS_H
|
||||
Reference in new issue
Block a user