From cb7506f02771ee1136662f11584a0dd0dde7d3a0 Mon Sep 17 00:00:00 2001 From: subhobhai943 Date: Tue, 25 Aug 2026 20:23:07 +0530 Subject: [PATCH] feat(kernel): NT-inspired Object Manager, dispatcher and IRQL Take inspiration from the Windows NT Executive and add its most iconic subsystem to SUB-OS: a typed, reference-counted object namespace. - kernel/nt/ob.c + include/kernel/nt/ob.h (new): * Object Manager (Ob): typed objects (Directory/Event/Semaphore/Mutant) in a hierarchical named namespace rooted at "\", with NT-style deletion once handle_count and ref_count both reach zero (unless permanent). * Handle table (Io/Ob): NT-shaped non-zero 4-aligned HANDLEs into a 256-slot system table; open/reference/close with proper refcount transfer. * Dispatcher (Ke): set/reset/test events (auto- and manual-reset) and release/wait semaphores, poll-style. * Software IRQL: PASSIVE/APC/DISPATCH/HIGH with raise/lower semantics. * NTSTATUS result codes and NT_SUCCESS(). * ob_init() seeds the standard namespace (\BaseNamedObjects, \Device, \ObjectTypes, \KernelObjects) plus a boot event and an I/O semaphore. - Wired ob_init() into the [12/14] boot step; added kernel/nt/ob.c to CORE_SRCS. - LazyBox: `objdir [\path]` walks the object namespace tree (WinObj-style); `ntobj` prints Ob statistics and runs a live dispatcher/handle/IRQL demo. Verified live in the GUI terminal (screenshotted): 7 seeded objects (5 directories, 1 event, 1 semaphore), auto-reset event signalling, handle close reclaiming the object, and PASSIVE->DISPATCH->PASSIVE IRQL transitions. Clean build on x86_64/aarch64/armv8i; 0 panics / 0 OOM. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01Fh5z6V7nGPbHvqVG5p4CXX --- Makefile | 2 +- include/kernel/nt/ob.h | 105 ++++++++++ kernel/main.c | 2 + kernel/nt/ob.c | 386 +++++++++++++++++++++++++++++++++++++ userland/lazybox/lazybox.c | 82 ++++++++ 5 files changed, 576 insertions(+), 1 deletion(-) create mode 100644 include/kernel/nt/ob.h create mode 100644 kernel/nt/ob.c diff --git a/Makefile b/Makefile index 51ea46c..528e06a 100644 --- a/Makefile +++ b/Makefile @@ -125,7 +125,7 @@ CORE_SRCS = kernel/main.c kernel/task.c kernel/sync.c kernel/timer.c kernel/prin kernel/panic.c kernel/trace.c kernel/sched.c kernel/cron.c kernel/syscall.c \ kernel/syslog.c kernel/module.c kernel/kobject.c kernel/metrics.c kernel/namespace.c \ kernel/signal.c kernel/workqueue.c kernel/tsc.c kernel/vt_art.c \ - kernel/sub/sub_runtime.c kernel/sub/sub_vm.c \ + kernel/sub/sub_runtime.c kernel/sub/sub_vm.c kernel/nt/ob.c \ kernel/wait.c kernel/futex.c kernel/rcu.c kernel/ktest.c \ kernel/elf.c kernel/exec.c \ lib/string.c lib/vsprintf.c lib/bitmap.c lib/rbtree.c lib/kfifo.c lib/hashtable.c lib/font8x8.c \ diff --git a/include/kernel/nt/ob.h b/include/kernel/nt/ob.h new file mode 100644 index 0000000..9a39d07 --- /dev/null +++ b/include/kernel/nt/ob.h @@ -0,0 +1,105 @@ +#ifndef _KERNEL_NT_OB_H +#define _KERNEL_NT_OB_H + +// ----------------------------------------------------------------------------- +// SUB-OS NT-Inspired Object Manager +// +// A faithful-in-spirit port of the Windows NT Executive Object Manager (Ob): +// every kernel resource is a typed, reference-counted object living in a +// hierarchical named namespace ("\BaseNamedObjects\MyEvent"), reached through a +// per-process handle table. Also carries NT dispatcher primitives (events, +// semaphores, mutants) and a software IRQL model. +// ----------------------------------------------------------------------------- + +#include +#include +#include + +// NTSTATUS-style result codes (high bit set == error, as in NT). +typedef int32_t NTSTATUS; +#define STATUS_SUCCESS ((NTSTATUS)0x00000000) +#define STATUS_UNSUCCESSFUL ((NTSTATUS)0xC0000001) +#define STATUS_INVALID_HANDLE ((NTSTATUS)0xC0000008) +#define STATUS_INSUFFICIENT_RES ((NTSTATUS)0xC000009A) +#define STATUS_OBJECT_NAME_EXISTS ((NTSTATUS)0xC0000035) +#define STATUS_OBJECT_NOT_FOUND ((NTSTATUS)0xC0000225) +#define NT_SUCCESS(s) (((NTSTATUS)(s)) >= 0) + +typedef enum { + OB_TYPE_DIRECTORY = 0, + OB_TYPE_EVENT, + OB_TYPE_SEMAPHORE, + OB_TYPE_MUTANT, + OB_TYPE_MAX +} ob_type_t; + +#define OB_NAME_MAX 48 +#define OB_DIR_MAX 32 // children per directory +#define OB_HANDLE_MAX 256 // system handle table size + +typedef struct ob_object ob_object_t; + +struct ob_object { + ob_type_t type; + char name[OB_NAME_MAX]; + int32_t ref_count; // outstanding pointer references + int32_t handle_count; // outstanding open handles + bool permanent; // survives handle_count/ref_count reaching 0 + ob_object_t* parent; // containing directory (NULL for the root) + + union { + struct { ob_object_t* children[OB_DIR_MAX]; int count; } dir; + struct { bool signaled; bool manual_reset; } event; + struct { int32_t count; int32_t limit; } sem; + struct { bool held; uint32_t owner; int32_t recursion; } mutant; + } body; +}; + +// A HANDLE indexes the system handle table; -1 is the invalid handle. +typedef int32_t HANDLE; +#define OB_INVALID_HANDLE ((HANDLE)-1) + +// Lifecycle ------------------------------------------------------------------- +void ob_init(void); +const char* ob_type_name(ob_type_t t); +ob_object_t* ob_root_directory(void); + +NTSTATUS ob_create_object(ob_type_t type, const char* name, + ob_object_t* dir, ob_object_t** out); +ob_object_t* ob_lookup_path(const char* path); // "\BaseNamedObjects\Name" +void ob_reference_object(ob_object_t* obj); +void ob_dereference_object(ob_object_t* obj); +void ob_make_permanent(ob_object_t* obj); + +// Handle table ---------------------------------------------------------------- +HANDLE ob_open_handle(ob_object_t* obj); +ob_object_t* ob_reference_by_handle(HANDLE h); +NTSTATUS ob_close_handle(HANDLE h); +int ob_open_handle_count(void); + +// Dispatcher primitives (non-blocking, poll-style) ---------------------------- +NTSTATUS ke_set_event(ob_object_t* evt); +NTSTATUS ke_reset_event(ob_object_t* evt); +bool ke_test_event(ob_object_t* evt); // true if signaled (auto-reset consumes) +NTSTATUS ke_release_semaphore(ob_object_t* sem, int32_t count); +bool ke_wait_semaphore(ob_object_t* sem); // true if a unit was acquired + +// Enumeration (for the objdir tool) ------------------------------------------- +int ob_dir_child_count(ob_object_t* dir); +ob_object_t* ob_dir_child(ob_object_t* dir, int index); +void ob_get_stats(uint32_t* out_objects, uint32_t* out_handles, + uint32_t per_type[OB_TYPE_MAX]); + +// Software IRQL model --------------------------------------------------------- +typedef enum { + PASSIVE_LEVEL = 0, + APC_LEVEL = 1, + DISPATCH_LEVEL = 2, + HIGH_LEVEL = 15 +} KIRQL; + +KIRQL ke_get_current_irql(void); +KIRQL ke_raise_irql(KIRQL new_irql); // returns previous IRQL +void ke_lower_irql(KIRQL old_irql); + +#endif // _KERNEL_NT_OB_H diff --git a/kernel/main.c b/kernel/main.c index bdd363c..a97bef6 100644 --- a/kernel/main.c +++ b/kernel/main.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -239,6 +240,7 @@ static void subos_modular_core_boot(void) { syslog_init(); metrics_init(); cron_init(); + ob_init(); service_manager_init(); printk(ANSI_BRIGHT_GREEN "OK\n" ANSI_RESET); diff --git a/kernel/nt/ob.c b/kernel/nt/ob.c new file mode 100644 index 0000000..0de0dc6 --- /dev/null +++ b/kernel/nt/ob.c @@ -0,0 +1,386 @@ +// ----------------------------------------------------------------------------- +// SUB-OS NT-Inspired Object Manager (Ob) + Dispatcher (Ke) + IRQL +// +// Objects are typed, reference-counted, and named in a directory hierarchy +// rooted at "\". A global handle table stands in for the NT System process's +// handle table. Deletion follows NT rules: an object is torn down once its +// handle_count and ref_count both reach zero and it is not permanent. +// ----------------------------------------------------------------------------- + +#include +#include +#include +#include +#include + +static ob_object_t* g_root = NULL; +static ob_object_t* g_handle_table[OB_HANDLE_MAX]; +static uint32_t g_object_count = 0; +static KIRQL g_current_irql = PASSIVE_LEVEL; +static spinlock_t g_ob_lock = SPINLOCK_INIT; + +static const char* const OB_TYPE_NAMES[OB_TYPE_MAX] = { + "Directory", "Event", "Semaphore", "Mutant" +}; + +const char* ob_type_name(ob_type_t t) { + return (t < OB_TYPE_MAX) ? OB_TYPE_NAMES[t] : "Unknown"; +} + +ob_object_t* ob_root_directory(void) { return g_root; } + +// --- Directory child list (caller holds g_ob_lock) --------------------------- +static bool dir_add_child(ob_object_t* dir, ob_object_t* child) { + if (!dir || dir->type != OB_TYPE_DIRECTORY) return false; + if (dir->body.dir.count >= OB_DIR_MAX) return false; + dir->body.dir.children[dir->body.dir.count++] = child; + child->parent = dir; + return true; +} + +static void dir_remove_child(ob_object_t* dir, ob_object_t* child) { + if (!dir || dir->type != OB_TYPE_DIRECTORY) return; + int n = dir->body.dir.count; + for (int i = 0; i < n; i++) { + if (dir->body.dir.children[i] == child) { + for (int j = i; j < n - 1; j++) { + dir->body.dir.children[j] = dir->body.dir.children[j + 1]; + } + dir->body.dir.count--; + return; + } + } +} + +static ob_object_t* dir_find_child(ob_object_t* dir, const char* name) { + if (!dir || dir->type != OB_TYPE_DIRECTORY) return NULL; + for (int i = 0; i < dir->body.dir.count; i++) { + if (strcmp(dir->body.dir.children[i]->name, name) == 0) { + return dir->body.dir.children[i]; + } + } + return NULL; +} + +// --- Allocation (caller holds g_ob_lock) ------------------------------------- +static ob_object_t* ob_alloc(ob_type_t type, const char* name) { + ob_object_t* obj = (ob_object_t*)kzalloc(sizeof(ob_object_t)); + if (!obj) return NULL; + obj->type = type; + strncpy(obj->name, name ? name : "", OB_NAME_MAX - 1); + obj->ref_count = 1; // the reference handed back to the creator + obj->handle_count = 0; + obj->permanent = false; + obj->parent = NULL; + if (type == OB_TYPE_SEMAPHORE) { + obj->body.sem.count = 0; + obj->body.sem.limit = 1; + } + g_object_count++; + return obj; +} + +// Tear an object down once nothing references it. Caller holds g_ob_lock. +static void ob_maybe_delete(ob_object_t* obj) { + if (!obj || obj->permanent) return; + if (obj->ref_count > 0 || obj->handle_count > 0) return; + if (obj->type == OB_TYPE_DIRECTORY && obj->body.dir.count > 0) return; + if (obj->parent) { + dir_remove_child(obj->parent, obj); + } + if (g_object_count > 0) g_object_count--; + kfree(obj); +} + +NTSTATUS ob_create_object(ob_type_t type, const char* name, + ob_object_t* dir, ob_object_t** out) { + if (type >= OB_TYPE_MAX) return STATUS_UNSUCCESSFUL; + + spin_lock(&g_ob_lock); + + ob_object_t* parent = dir ? dir : g_root; + if (name && name[0] && parent && dir_find_child(parent, name)) { + spin_unlock(&g_ob_lock); + return STATUS_OBJECT_NAME_EXISTS; + } + + ob_object_t* obj = ob_alloc(type, name); + if (!obj) { + spin_unlock(&g_ob_lock); + return STATUS_INSUFFICIENT_RES; + } + + if (name && name[0] && parent) { + if (!dir_add_child(parent, obj)) { + g_object_count--; + kfree(obj); + spin_unlock(&g_ob_lock); + return STATUS_INSUFFICIENT_RES; + } + } + + spin_unlock(&g_ob_lock); + if (out) *out = obj; + return STATUS_SUCCESS; +} + +void ob_reference_object(ob_object_t* obj) { + if (!obj) return; + spin_lock(&g_ob_lock); + obj->ref_count++; + spin_unlock(&g_ob_lock); +} + +void ob_dereference_object(ob_object_t* obj) { + if (!obj) return; + spin_lock(&g_ob_lock); + if (obj->ref_count > 0) obj->ref_count--; + ob_maybe_delete(obj); + spin_unlock(&g_ob_lock); +} + +void ob_make_permanent(ob_object_t* obj) { + if (!obj) return; + spin_lock(&g_ob_lock); + obj->permanent = true; + spin_unlock(&g_ob_lock); +} + +ob_object_t* ob_lookup_path(const char* path) { + if (!path) return NULL; + // A bare "\" (or empty) resolves to the root directory. + if (path[0] == '\0' || (path[0] == '\\' && path[1] == '\0')) { + return g_root; + } + + char buf[192]; + strncpy(buf, path, sizeof(buf) - 1); + buf[sizeof(buf) - 1] = '\0'; + + spin_lock(&g_ob_lock); + ob_object_t* cur = g_root; + char* p = buf; + if (*p == '\\') p++; // skip leading backslash + + while (*p && cur) { + char* seg = p; + while (*p && *p != '\\') p++; + if (*p == '\\') { *p = '\0'; p++; } + if (*seg) { + cur = dir_find_child(cur, seg); + } + } + spin_unlock(&g_ob_lock); + return cur; +} + +// --- Handle table ------------------------------------------------------------ +HANDLE ob_open_handle(ob_object_t* obj) { + if (!obj) return OB_INVALID_HANDLE; + spin_lock(&g_ob_lock); + for (int i = 0; i < OB_HANDLE_MAX; i++) { + if (g_handle_table[i] == NULL) { + g_handle_table[i] = obj; + obj->handle_count++; + obj->ref_count++; // the handle holds a reference + spin_unlock(&g_ob_lock); + // NT hands out non-zero, 4-aligned handle values; mirror that shape. + return (HANDLE)((i + 1) * 4); + } + } + spin_unlock(&g_ob_lock); + return OB_INVALID_HANDLE; +} + +static int handle_to_index(HANDLE h) { + if (h <= 0 || (h & 3)) return -1; + int idx = (h / 4) - 1; + return (idx >= 0 && idx < OB_HANDLE_MAX) ? idx : -1; +} + +ob_object_t* ob_reference_by_handle(HANDLE h) { + int idx = handle_to_index(h); + if (idx < 0) return NULL; + spin_lock(&g_ob_lock); + ob_object_t* obj = g_handle_table[idx]; + if (obj) obj->ref_count++; + spin_unlock(&g_ob_lock); + return obj; +} + +NTSTATUS ob_close_handle(HANDLE h) { + int idx = handle_to_index(h); + if (idx < 0) return STATUS_INVALID_HANDLE; + spin_lock(&g_ob_lock); + ob_object_t* obj = g_handle_table[idx]; + if (!obj) { + spin_unlock(&g_ob_lock); + return STATUS_INVALID_HANDLE; + } + g_handle_table[idx] = NULL; + if (obj->handle_count > 0) obj->handle_count--; + if (obj->ref_count > 0) obj->ref_count--; // drop the handle's reference + ob_maybe_delete(obj); + spin_unlock(&g_ob_lock); + return STATUS_SUCCESS; +} + +int ob_open_handle_count(void) { + int n = 0; + spin_lock(&g_ob_lock); + for (int i = 0; i < OB_HANDLE_MAX; i++) { + if (g_handle_table[i]) n++; + } + spin_unlock(&g_ob_lock); + return n; +} + +// --- Dispatcher primitives --------------------------------------------------- +NTSTATUS ke_set_event(ob_object_t* evt) { + if (!evt || evt->type != OB_TYPE_EVENT) return STATUS_UNSUCCESSFUL; + spin_lock(&g_ob_lock); + evt->body.event.signaled = true; + spin_unlock(&g_ob_lock); + return STATUS_SUCCESS; +} + +NTSTATUS ke_reset_event(ob_object_t* evt) { + if (!evt || evt->type != OB_TYPE_EVENT) return STATUS_UNSUCCESSFUL; + spin_lock(&g_ob_lock); + evt->body.event.signaled = false; + spin_unlock(&g_ob_lock); + return STATUS_SUCCESS; +} + +bool ke_test_event(ob_object_t* evt) { + if (!evt || evt->type != OB_TYPE_EVENT) return false; + spin_lock(&g_ob_lock); + bool sig = evt->body.event.signaled; + if (sig && !evt->body.event.manual_reset) { + evt->body.event.signaled = false; // auto-reset consumes the signal + } + spin_unlock(&g_ob_lock); + return sig; +} + +NTSTATUS ke_release_semaphore(ob_object_t* sem, int32_t count) { + if (!sem || sem->type != OB_TYPE_SEMAPHORE || count <= 0) return STATUS_UNSUCCESSFUL; + spin_lock(&g_ob_lock); + sem->body.sem.count += count; + if (sem->body.sem.count > sem->body.sem.limit) { + sem->body.sem.count = sem->body.sem.limit; + } + spin_unlock(&g_ob_lock); + return STATUS_SUCCESS; +} + +bool ke_wait_semaphore(ob_object_t* sem) { + if (!sem || sem->type != OB_TYPE_SEMAPHORE) return false; + spin_lock(&g_ob_lock); + bool ok = false; + if (sem->body.sem.count > 0) { + sem->body.sem.count--; + ok = true; + } + spin_unlock(&g_ob_lock); + return ok; +} + +// --- Enumeration / stats ----------------------------------------------------- +int ob_dir_child_count(ob_object_t* dir) { + if (!dir || dir->type != OB_TYPE_DIRECTORY) return 0; + return dir->body.dir.count; +} + +ob_object_t* ob_dir_child(ob_object_t* dir, int index) { + if (!dir || dir->type != OB_TYPE_DIRECTORY) return NULL; + if (index < 0 || index >= dir->body.dir.count) return NULL; + return dir->body.dir.children[index]; +} + +static void count_recursive(ob_object_t* obj, uint32_t per_type[OB_TYPE_MAX]) { + if (!obj) return; + if (obj->type < OB_TYPE_MAX) per_type[obj->type]++; + if (obj->type == OB_TYPE_DIRECTORY) { + for (int i = 0; i < obj->body.dir.count; i++) { + count_recursive(obj->body.dir.children[i], per_type); + } + } +} + +void ob_get_stats(uint32_t* out_objects, uint32_t* out_handles, + uint32_t per_type[OB_TYPE_MAX]) { + for (int i = 0; i < OB_TYPE_MAX; i++) per_type[i] = 0; + count_recursive(g_root, per_type); + if (out_objects) *out_objects = g_object_count; + if (out_handles) *out_handles = (uint32_t)ob_open_handle_count(); +} + +// --- Software IRQL ----------------------------------------------------------- +KIRQL ke_get_current_irql(void) { return g_current_irql; } + +KIRQL ke_raise_irql(KIRQL new_irql) { + KIRQL old = g_current_irql; + if (new_irql >= g_current_irql) { // raising only; NT bugchecks otherwise + g_current_irql = new_irql; + } + return old; +} + +void ke_lower_irql(KIRQL old_irql) { + if (old_irql <= g_current_irql) { + g_current_irql = old_irql; + } +} + +// --- Initialization ---------------------------------------------------------- +void ob_init(void) { + for (int i = 0; i < OB_HANDLE_MAX; i++) g_handle_table[i] = NULL; + g_object_count = 0; + g_current_irql = PASSIVE_LEVEL; + + // Root directory "\" is permanent and self-parented. + g_root = (ob_object_t*)kzalloc(sizeof(ob_object_t)); + if (!g_root) { + printk(KERN_ERR "OB: failed to allocate the root directory\n"); + return; + } + g_root->type = OB_TYPE_DIRECTORY; + strncpy(g_root->name, "\\", OB_NAME_MAX - 1); + g_root->permanent = true; + g_root->ref_count = 1; + g_object_count = 1; + + // Standard NT namespace directories. + const char* std_dirs[] = { "BaseNamedObjects", "Device", "ObjectTypes", "KernelObjects" }; + for (size_t i = 0; i < sizeof(std_dirs) / sizeof(std_dirs[0]); i++) { + ob_object_t* d = NULL; + if (NT_SUCCESS(ob_create_object(OB_TYPE_DIRECTORY, std_dirs[i], g_root, &d)) && d) { + ob_make_permanent(d); + ob_dereference_object(d); // drop the creation reference; name keeps it alive + } + } + + // Seed a couple of named dispatcher objects so the namespace is not empty. + ob_object_t* bno = ob_lookup_path("\\BaseNamedObjects"); + ob_object_t* evt = NULL; + if (NT_SUCCESS(ob_create_object(OB_TYPE_EVENT, "SubOsBootEvent", bno, &evt)) && evt) { + evt->body.event.manual_reset = true; + ke_set_event(evt); + ob_make_permanent(evt); + ob_dereference_object(evt); + } + ob_object_t* sem = NULL; + if (NT_SUCCESS(ob_create_object(OB_TYPE_SEMAPHORE, "SubOsIoSemaphore", bno, &sem)) && sem) { + sem->body.sem.limit = 4; + sem->body.sem.count = 4; + ob_make_permanent(sem); + ob_dereference_object(sem); + } + + uint32_t objs = 0, handles = 0, per[OB_TYPE_MAX]; + ob_get_stats(&objs, &handles, per); + printk(ANSI_BRIGHT_GREEN "OB: " ANSI_RESET + "NT Object Manager online (namespace root \\, %u objects, handle table %d slots)\n", + objs, OB_HANDLE_MAX); +} diff --git a/userland/lazybox/lazybox.c b/userland/lazybox/lazybox.c index b8fc53f..2556da5 100644 --- a/userland/lazybox/lazybox.c +++ b/userland/lazybox/lazybox.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -507,6 +508,85 @@ static int applet_rle(int argc, char** argv) { return ok ? 0 : 1; } +static void objdir_print_tree(ob_object_t* obj, int depth) { + if (!obj) return; + char indent[40]; + int n = depth * 2; + if (n > (int)sizeof(indent) - 1) n = sizeof(indent) - 1; + for (int i = 0; i < n; i++) indent[i] = ' '; + indent[n] = '\0'; + + char detail[72]; + detail[0] = '\0'; + if (obj->type == OB_TYPE_EVENT) { + snprintf(detail, sizeof(detail), " [%s, %s]", + obj->body.event.manual_reset ? "manual-reset" : "auto-reset", + obj->body.event.signaled ? "signaled" : "nonsignaled"); + } else if (obj->type == OB_TYPE_SEMAPHORE) { + snprintf(detail, sizeof(detail), " [%d/%d]", + obj->body.sem.count, obj->body.sem.limit); + } + + printk("%s%s%s " ANSI_YELLOW "(%s" ANSI_RESET " ref=%d hnd=%d%s)%s\n", + indent, obj->name, + (obj->type == OB_TYPE_DIRECTORY) ? "\\" : "", + ob_type_name(obj->type), obj->ref_count, obj->handle_count, + obj->permanent ? " perm" : "", detail); + + if (obj->type == OB_TYPE_DIRECTORY) { + int c = ob_dir_child_count(obj); + for (int i = 0; i < c; i++) { + objdir_print_tree(ob_dir_child(obj, i), depth + 1); + } + } +} + +static int applet_objdir(int argc, char** argv) { + const char* path = (argc >= 2) ? argv[1] : "\\"; + ob_object_t* obj = ob_lookup_path(path); + if (!obj) { + printk(KERN_ERR "objdir: object '%s' not found in the namespace\n", path); + return 1; + } + printk(ANSI_BRIGHT_CYAN "NT Object Namespace" ANSI_RESET " at '%s':\n", path); + objdir_print_tree(obj, 0); + return 0; +} + +static int applet_ntobj(int argc, char** argv) { + (void)argc; (void)argv; + uint32_t objs = 0, handles = 0, per[OB_TYPE_MAX]; + ob_get_stats(&objs, &handles, per); + + printk(ANSI_BRIGHT_CYAN "NT Object Manager statistics:\n" ANSI_RESET); + printk(" Total objects : %u\n", objs); + printk(" Open handles : %u / %d\n", handles, OB_HANDLE_MAX); + for (int i = 0; i < OB_TYPE_MAX; i++) { + printk(" %-10s: %u\n", ob_type_name((ob_type_t)i), per[i]); + } + + printk(ANSI_BRIGHT_CYAN "Dispatcher / handle demo:\n" ANSI_RESET); + ob_object_t* evt = NULL; + if (NT_SUCCESS(ob_create_object(OB_TYPE_EVENT, "DemoEvent", + ob_lookup_path("\\KernelObjects"), &evt)) && evt) { + HANDLE h = ob_open_handle(evt); + ob_dereference_object(evt); // the handle now keeps it alive + bool before = ke_test_event(evt); + ke_set_event(evt); + bool after = ke_test_event(evt); // auto-reset consumes the signal + printk(" \\KernelObjects\\DemoEvent handle=0x%X test:%d set->test:%d\n", + h, before, after); + ob_close_handle(h); + printk(" Closed handle -> object reclaimed (handle_count and ref_count hit 0)\n"); + } + + KIRQL old = ke_raise_irql(DISPATCH_LEVEL); + printk(" IRQL: raised %d(PASSIVE) -> %d(DISPATCH); ", old, ke_get_current_irql()); + ke_lower_irql(old); + printk("lowered back to %d\n", ke_get_current_irql()); + return 0; +} + static int applet_rand(int argc, char** argv) { int count = (argc >= 2) ? atoi(argv[1]) : 4; printk("Random Integers: "); @@ -2216,6 +2296,8 @@ static const lazybox_applet_t applets[] = { {"crc32", applet_crc32, "crc32 ", "Calculate CRC32 checksum", "Crypto"}, {"cksum", applet_cksum, "cksum ", "Rust CRC-32C/Adler-32/FNV", "Crypto"}, {"rle", applet_rle, "rle ", "Rust RLE compress round-trip","Crypto"}, + {"objdir", applet_objdir, "objdir [\\path]", "List the NT object namespace","Kernel"}, + {"ntobj", applet_ntobj, "ntobj", "NT Object Manager stats/demo","Kernel"}, {"rand", applet_rand, "rand [count]", "Generate random numbers", "Crypto"}, {"certcheck", applet_certcheck, "certcheck", "View X.509 kernel keyring", "Security"}, {"capsh", applet_capsh, "capsh", "View process capabilities", "Security"},