mirror of
https://github.com/subhobhai943/SUB-OS.git
synced 2026-10-25 16:52:29 +00:00
feat(kernel,gui): NT I/O Manager (IRP-based drivers) + Registry Editor
Kernel (NT I/O subsystem): - kernel/nt/io.c + include/kernel/nt/io.h (new): DRIVER_OBJECTs with a MajorFunction dispatch table, DEVICE_OBJECTs that can be stacked (attached), and IRPs pushed down the stack via io_call_driver / completed with io_complete_request. Ships three demo drivers: \Driver\Null (write sink, EOF reads), \Driver\Zero (zero-filled reads), and \Driver\Monitor -- a filter attached above \Device\Null that counts and forwards each IRP down the stack. Wired into the [12/14] boot step. - LazyBox `iodev` lists drivers/devices with the stack and IRP counters, then runs a live demo: a WRITE IRP through the filtered Null stack and a READ from the Zero device. GUI: - gui/gui_apps.c: "Registry Editor" -- navigate the NT \Registry hive tree, click subkeys to descend and ".." to go up, with a values pane showing each value's name/type/data. Registered in the app table (start menu). Verified live in the GUI terminal (screenshotted): 3 drivers / 3 devices, the NullMonitor->Null device stack, a 16-byte WRITE sunk through the filter (status 0x0), and an 8-byte zeroed READ from \Device\Zero. Clean build on x86_64/aarch64/armv8i; 0 panics / 0 OOM. Co-Authored-By: Claude Opus 4.8 <[email protected]> Claude-Session: https://claude.ai/code/session_01Fh5z6V7nGPbHvqVG5p4CXX
This commit is contained in:
1 parent
526ad8c741
commit
ea937a699c
8 files changed
+513
-3
No files matched your search
@@ -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/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/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/signal.c kernel/workqueue.c kernel/tsc.c kernel/vt_art.c \
|
||||||
kernel/sub/sub_runtime.c kernel/sub/sub_vm.c kernel/nt/ob.c kernel/nt/reg.c \
|
kernel/sub/sub_runtime.c kernel/sub/sub_vm.c kernel/nt/ob.c kernel/nt/reg.c kernel/nt/io.c \
|
||||||
kernel/wait.c kernel/futex.c kernel/rcu.c kernel/ktest.c \
|
kernel/wait.c kernel/futex.c kernel/rcu.c kernel/ktest.c \
|
||||||
kernel/elf.c kernel/exec.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 \
|
lib/string.c lib/vsprintf.c lib/bitmap.c lib/rbtree.c lib/kfifo.c lib/hashtable.c lib/font8x8.c \
|
||||||
|
|||||||
+120
@@ -10,6 +10,7 @@
|
|||||||
#include <kernel/printk.h>
|
#include <kernel/printk.h>
|
||||||
#include <kernel/rust.h>
|
#include <kernel/rust.h>
|
||||||
#include <kernel/nt/ob.h>
|
#include <kernel/nt/ob.h>
|
||||||
|
#include <kernel/nt/reg.h>
|
||||||
#include <lib/string.h>
|
#include <lib/string.h>
|
||||||
#include <lib/printf.h>
|
#include <lib/printf.h>
|
||||||
|
|
||||||
@@ -768,3 +769,122 @@ void gui_app_objbrowse_launch(int x, int y, int w, int h) {
|
|||||||
win->handle_event = objbrowse_event;
|
win->handle_event = objbrowse_event;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// =================================================================
|
||||||
|
// 10. Registry Editor -- navigate the NT \Registry hive tree
|
||||||
|
// =================================================================
|
||||||
|
typedef struct {
|
||||||
|
reg_key_t* current;
|
||||||
|
int row_h;
|
||||||
|
int list_top_rel; // list start, relative to window content top
|
||||||
|
int sub_count; // subkeys shown this frame (for click mapping)
|
||||||
|
bool has_up; // a ".." row is present
|
||||||
|
} regedit_data_t;
|
||||||
|
|
||||||
|
static void regedit_build_path(reg_key_t* key, char* out, size_t len) {
|
||||||
|
// Walk parents into a reversed segment stack, then join with backslashes.
|
||||||
|
const char* segs[16];
|
||||||
|
int n = 0;
|
||||||
|
reg_key_t* k = key;
|
||||||
|
while (k && n < 16) { segs[n++] = k->name; k = k->parent; }
|
||||||
|
size_t pos = 0;
|
||||||
|
out[0] = '\0';
|
||||||
|
for (int i = n - 1; i >= 0; i--) {
|
||||||
|
int wrote = snprintf(out + pos, (pos < len) ? len - pos : 0, "\\%s", segs[i]);
|
||||||
|
if (wrote > 0) pos += (size_t)wrote;
|
||||||
|
if (pos >= len) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void regedit_paint(gui_window_t* win) {
|
||||||
|
regedit_data_t* rd = (regedit_data_t*)win->user_data;
|
||||||
|
if (!rd) return;
|
||||||
|
if (!rd->current) rd->current = reg_root();
|
||||||
|
|
||||||
|
int cx = win->x + 12;
|
||||||
|
int top = win->y + GUI_TITLEBAR_HEIGHT;
|
||||||
|
|
||||||
|
char path[192];
|
||||||
|
regedit_build_path(rd->current, path, sizeof(path));
|
||||||
|
gui_gfx_draw_string_16_shadow(cx, top + 8, "Registry Editor", GUI_THEME_PRIMARY, GUI_COLOR_BLACK);
|
||||||
|
gui_gfx_draw_string(cx, top + 28, path, GUI_THEME_TEXT_MUTED);
|
||||||
|
|
||||||
|
rd->row_h = 13;
|
||||||
|
rd->list_top_rel = GUI_TITLEBAR_HEIGHT + 46;
|
||||||
|
int y = top + 46;
|
||||||
|
|
||||||
|
rd->has_up = (rd->current->parent != NULL);
|
||||||
|
if (rd->has_up) {
|
||||||
|
gui_gfx_draw_string(cx, y, "..", GUI_THEME_ACCENT);
|
||||||
|
y += rd->row_h;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subkeys (navigable)
|
||||||
|
int kc = reg_enum_key_count(rd->current);
|
||||||
|
rd->sub_count = kc;
|
||||||
|
for (int i = 0; i < kc; i++) {
|
||||||
|
reg_key_t* sub = reg_enum_key(rd->current, i);
|
||||||
|
char line[64];
|
||||||
|
snprintf(line, sizeof(line), "[%s]", sub->name);
|
||||||
|
gui_gfx_draw_string(cx, y, line, GUI_THEME_SUCCESS);
|
||||||
|
y += rd->row_h;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Values pane
|
||||||
|
y += 4;
|
||||||
|
gui_gfx_draw_line(cx, y, win->x + win->width - 12, y, GUI_THEME_BORDER);
|
||||||
|
y += 6;
|
||||||
|
int vc = reg_enum_value_count(rd->current);
|
||||||
|
if (vc == 0) {
|
||||||
|
gui_gfx_draw_string(cx, y, "(no values)", GUI_THEME_TEXT_DIM);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < vc; i++) {
|
||||||
|
reg_value_t* v = reg_enum_value(rd->current, i);
|
||||||
|
char rendered[REG_STR_MAX + 8];
|
||||||
|
reg_value_to_string(v, rendered, sizeof(rendered));
|
||||||
|
char line[160];
|
||||||
|
snprintf(line, sizeof(line), "%-18s %-10s %s", v->name, reg_type_name(v->type), rendered);
|
||||||
|
gui_gfx_draw_string(cx, y, line, GUI_THEME_TEXT_MAIN);
|
||||||
|
y += rd->row_h;
|
||||||
|
if (y > win->y + win->height - 12) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void regedit_event(gui_window_t* win, const gui_event_t* ev) {
|
||||||
|
regedit_data_t* rd = (regedit_data_t*)win->user_data;
|
||||||
|
if (!rd) return;
|
||||||
|
|
||||||
|
if (ev->type == GUI_EVENT_CLOSE) {
|
||||||
|
if (win->user_data) { kfree(win->user_data); win->user_data = NULL; }
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ev->type != GUI_EVENT_MOUSE_DOWN) return;
|
||||||
|
if (!rd->current) return;
|
||||||
|
|
||||||
|
int rel = ev->rel_y - rd->list_top_rel;
|
||||||
|
if (rel < 0) return;
|
||||||
|
int row = rel / (rd->row_h > 0 ? rd->row_h : 13);
|
||||||
|
|
||||||
|
int idx = 0;
|
||||||
|
if (rd->has_up) {
|
||||||
|
if (row == 0) { rd->current = rd->current->parent; return; }
|
||||||
|
idx = row - 1;
|
||||||
|
} else {
|
||||||
|
idx = row;
|
||||||
|
}
|
||||||
|
if (idx >= 0 && idx < rd->sub_count) {
|
||||||
|
reg_key_t* sub = reg_enum_key(rd->current, idx);
|
||||||
|
if (sub) rd->current = sub;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void gui_app_regedit_launch(int x, int y, int w, int h) {
|
||||||
|
gui_window_t* win = gui_wm_create_window("Registry Editor", x, y, (w > 0) ? w : 480, (h > 0) ? h : 360);
|
||||||
|
if (win) {
|
||||||
|
regedit_data_t* rd = (regedit_data_t*)kzalloc(sizeof(regedit_data_t));
|
||||||
|
if (rd) rd->current = reg_root();
|
||||||
|
win->user_data = rd;
|
||||||
|
win->paint = regedit_paint;
|
||||||
|
win->handle_event = regedit_event;
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
-2
@@ -59,6 +59,7 @@ static const desktop_app_t g_apps[] = {
|
|||||||
{ "Life", GUI_ICON_FLASK, gui_app_life_launch, 240, 70, 480, 380 },
|
{ "Life", GUI_ICON_FLASK, gui_app_life_launch, 240, 70, 480, 380 },
|
||||||
{ "Rust Lab", GUI_ICON_INFO, gui_app_rustlab_launch, 300, 80, 420, 300 },
|
{ "Rust Lab", GUI_ICON_INFO, gui_app_rustlab_launch, 300, 80, 420, 300 },
|
||||||
{ "Objects", GUI_ICON_TASKS, gui_app_objbrowse_launch,280, 70, 460, 340 },
|
{ "Objects", GUI_ICON_TASKS, gui_app_objbrowse_launch,280, 70, 460, 340 },
|
||||||
|
{ "RegEdit", GUI_ICON_SETTINGS, gui_app_regedit_launch, 300, 90, 480, 360 },
|
||||||
{ "Clock", GUI_ICON_CLOCK, gui_app_clock_launch, 460, 120, 280, 260 },
|
{ "Clock", GUI_ICON_CLOCK, gui_app_clock_launch, 460, 120, 280, 260 },
|
||||||
{ "Settings", GUI_ICON_SETTINGS, gui_app_settings_launch, 160, 60, 400, 320 },
|
{ "Settings", GUI_ICON_SETTINGS, gui_app_settings_launch, 160, 60, 400, 320 },
|
||||||
{ "About", GUI_ICON_INFO, gui_app_about_launch, 240, 140, 330, 170 },
|
{ "About", GUI_ICON_INFO, gui_app_about_launch, 240, 140, 330, 170 },
|
||||||
@@ -67,8 +68,8 @@ static const desktop_app_t g_apps[] = {
|
|||||||
|
|
||||||
// Which of the above appear on the desktop itself.
|
// Which of the above appear on the desktop itself.
|
||||||
// Indices into g_apps[]: Terminal, Files, Monitor, Kernel Log, KTest, Life,
|
// Indices into g_apps[]: Terminal, Files, Monitor, Kernel Log, KTest, Life,
|
||||||
// Rust Lab, Settings. (Objects lives in the start menu.)
|
// Rust Lab, Settings. (Objects/RegEdit live in the start menu.)
|
||||||
static const int g_desktop_icons[] = { 0, 1, 2, 4, 5, 9, 10, 13 };
|
static const int g_desktop_icons[] = { 0, 1, 2, 4, 5, 9, 10, 14 };
|
||||||
#define DESKTOP_ICON_COUNT ((int)(sizeof(g_desktop_icons) / sizeof(g_desktop_icons[0])))
|
#define DESKTOP_ICON_COUNT ((int)(sizeof(g_desktop_icons) / sizeof(g_desktop_icons[0])))
|
||||||
|
|
||||||
static bool g_start_menu_open = false;
|
static bool g_start_menu_open = false;
|
||||||
|
|||||||
@@ -12,5 +12,6 @@ void gui_app_about_launch(int x, int y, int w, int h);
|
|||||||
void gui_app_life_launch(int x, int y, int w, int h);
|
void gui_app_life_launch(int x, int y, int w, int h);
|
||||||
void gui_app_rustlab_launch(int x, int y, int w, int h);
|
void gui_app_rustlab_launch(int x, int y, int w, int h);
|
||||||
void gui_app_objbrowse_launch(int x, int y, int w, int h);
|
void gui_app_objbrowse_launch(int x, int y, int w, int h);
|
||||||
|
void gui_app_regedit_launch(int x, int y, int w, int h);
|
||||||
|
|
||||||
#endif // _GUI_APPS_H
|
#endif // _GUI_APPS_H
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
#ifndef _KERNEL_NT_IO_H
|
||||||
|
#define _KERNEL_NT_IO_H
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// SUB-OS NT-Inspired I/O Manager
|
||||||
|
//
|
||||||
|
// Models the Windows NT I/O subsystem: DRIVER_OBJECTs export a MajorFunction
|
||||||
|
// dispatch table, DEVICE_OBJECTs are created by drivers and can be stacked
|
||||||
|
// (attached) into a device stack, and I/O flows as IRPs (I/O Request Packets)
|
||||||
|
// pushed down the stack with IoCallDriver / completed with IoCompleteRequest.
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <kernel/nt/ob.h> // NTSTATUS, STATUS_* codes
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
IRP_MJ_CREATE = 0,
|
||||||
|
IRP_MJ_CLOSE,
|
||||||
|
IRP_MJ_READ,
|
||||||
|
IRP_MJ_WRITE,
|
||||||
|
IRP_MJ_DEVICE_CONTROL,
|
||||||
|
IRP_MJ_MAXIMUM
|
||||||
|
} irp_major_t;
|
||||||
|
|
||||||
|
#define IO_NAME_MAX 48
|
||||||
|
#define IO_MAX_DRIVERS 16
|
||||||
|
#define IO_MAX_DEVICES 32
|
||||||
|
|
||||||
|
typedef struct _DEVICE_OBJECT DEVICE_OBJECT;
|
||||||
|
typedef struct _DRIVER_OBJECT DRIVER_OBJECT;
|
||||||
|
typedef struct _IRP IRP;
|
||||||
|
|
||||||
|
typedef NTSTATUS (*driver_dispatch_t)(DEVICE_OBJECT* dev, IRP* irp);
|
||||||
|
typedef NTSTATUS (*driver_entry_t)(DRIVER_OBJECT* drv);
|
||||||
|
|
||||||
|
struct _DRIVER_OBJECT {
|
||||||
|
char name[IO_NAME_MAX]; // e.g. "\Driver\Null"
|
||||||
|
driver_dispatch_t major_function[IRP_MJ_MAXIMUM];
|
||||||
|
DEVICE_OBJECT* device_list; // devices this driver owns
|
||||||
|
uint64_t irp_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _DEVICE_OBJECT {
|
||||||
|
char name[IO_NAME_MAX]; // e.g. "\Device\Null"
|
||||||
|
DRIVER_OBJECT* driver;
|
||||||
|
DEVICE_OBJECT* next_on_driver; // next in the driver's device_list
|
||||||
|
DEVICE_OBJECT* attached_to; // lower device in the stack (or NULL)
|
||||||
|
void* device_extension;
|
||||||
|
uint64_t read_count;
|
||||||
|
uint64_t write_count;
|
||||||
|
uint64_t ioctl_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _IRP {
|
||||||
|
irp_major_t major_function;
|
||||||
|
void* buffer;
|
||||||
|
uint32_t length;
|
||||||
|
uint32_t io_control_code;
|
||||||
|
NTSTATUS status;
|
||||||
|
uint32_t information; // bytes transferred on completion
|
||||||
|
DEVICE_OBJECT* current_device;
|
||||||
|
};
|
||||||
|
|
||||||
|
void io_init(void);
|
||||||
|
const char* io_major_name(irp_major_t mj);
|
||||||
|
|
||||||
|
DRIVER_OBJECT* io_create_driver(const char* name, driver_entry_t entry);
|
||||||
|
NTSTATUS io_create_device(DRIVER_OBJECT* drv, const char* name,
|
||||||
|
size_t ext_size, DEVICE_OBJECT** out);
|
||||||
|
DEVICE_OBJECT* io_attach_device(DEVICE_OBJECT* source, DEVICE_OBJECT* target);
|
||||||
|
DEVICE_OBJECT* io_lookup_device(const char* name);
|
||||||
|
|
||||||
|
IRP* io_build_irp(irp_major_t mj, void* buffer, uint32_t length);
|
||||||
|
NTSTATUS io_call_driver(DEVICE_OBJECT* dev, IRP* irp);
|
||||||
|
void io_complete_request(IRP* irp, NTSTATUS status);
|
||||||
|
void io_free_irp(IRP* irp);
|
||||||
|
|
||||||
|
// Enumeration / stats.
|
||||||
|
int io_driver_count(void);
|
||||||
|
DRIVER_OBJECT* io_driver_at(int index);
|
||||||
|
int io_device_count(void);
|
||||||
|
DEVICE_OBJECT* io_device_at(int index);
|
||||||
|
void io_get_stats(uint32_t* out_drivers, uint32_t* out_devices, uint64_t* out_irps);
|
||||||
|
|
||||||
|
#endif // _KERNEL_NT_IO_H
|
||||||
@@ -9,6 +9,7 @@
|
|||||||
#include <kernel/module.h>
|
#include <kernel/module.h>
|
||||||
#include <kernel/nt/ob.h>
|
#include <kernel/nt/ob.h>
|
||||||
#include <kernel/nt/reg.h>
|
#include <kernel/nt/reg.h>
|
||||||
|
#include <kernel/nt/io.h>
|
||||||
#include <kernel/trace.h>
|
#include <kernel/trace.h>
|
||||||
#include <kernel/namespace.h>
|
#include <kernel/namespace.h>
|
||||||
#include <kernel/syslog.h>
|
#include <kernel/syslog.h>
|
||||||
@@ -243,6 +244,7 @@ static void subos_modular_core_boot(void) {
|
|||||||
cron_init();
|
cron_init();
|
||||||
ob_init();
|
ob_init();
|
||||||
reg_init();
|
reg_init();
|
||||||
|
io_init();
|
||||||
service_manager_init();
|
service_manager_init();
|
||||||
printk(ANSI_BRIGHT_GREEN "OK\n" ANSI_RESET);
|
printk(ANSI_BRIGHT_GREEN "OK\n" ANSI_RESET);
|
||||||
|
|
||||||
|
|||||||
+242
@@ -0,0 +1,242 @@
|
|||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// SUB-OS NT-Inspired I/O Manager (Io)
|
||||||
|
//
|
||||||
|
// Drivers register a MajorFunction dispatch table and create named device
|
||||||
|
// objects. Devices can be stacked (a filter attached on top of a lower device);
|
||||||
|
// io_call_driver invokes the top device's dispatch, which may forward the IRP
|
||||||
|
// down to dev->attached_to. Ships three demo drivers: Null, Zero, and a Monitor
|
||||||
|
// filter stacked above Null that counts and forwards every IRP.
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#include <kernel/nt/io.h>
|
||||||
|
#include <mm/kmalloc.h>
|
||||||
|
#include <lib/string.h>
|
||||||
|
#include <kernel/printk.h>
|
||||||
|
#include <kernel/sync.h>
|
||||||
|
|
||||||
|
static DRIVER_OBJECT* g_drivers[IO_MAX_DRIVERS];
|
||||||
|
static int g_driver_count = 0;
|
||||||
|
static DEVICE_OBJECT* g_devices[IO_MAX_DEVICES];
|
||||||
|
static int g_device_count = 0;
|
||||||
|
static uint64_t g_total_irps = 0;
|
||||||
|
static spinlock_t g_io_lock = SPINLOCK_INIT;
|
||||||
|
|
||||||
|
const char* io_major_name(irp_major_t mj) {
|
||||||
|
switch (mj) {
|
||||||
|
case IRP_MJ_CREATE: return "CREATE";
|
||||||
|
case IRP_MJ_CLOSE: return "CLOSE";
|
||||||
|
case IRP_MJ_READ: return "READ";
|
||||||
|
case IRP_MJ_WRITE: return "WRITE";
|
||||||
|
case IRP_MJ_DEVICE_CONTROL: return "DEVICE_CONTROL";
|
||||||
|
default: return "UNKNOWN";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DRIVER_OBJECT* io_create_driver(const char* name, driver_entry_t entry) {
|
||||||
|
spin_lock(&g_io_lock);
|
||||||
|
if (g_driver_count >= IO_MAX_DRIVERS) {
|
||||||
|
spin_unlock(&g_io_lock);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
DRIVER_OBJECT* drv = (DRIVER_OBJECT*)kzalloc(sizeof(DRIVER_OBJECT));
|
||||||
|
if (!drv) {
|
||||||
|
spin_unlock(&g_io_lock);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
strncpy(drv->name, name ? name : "", IO_NAME_MAX - 1);
|
||||||
|
g_drivers[g_driver_count++] = drv;
|
||||||
|
spin_unlock(&g_io_lock);
|
||||||
|
|
||||||
|
if (entry) {
|
||||||
|
entry(drv); // DriverEntry fills the MajorFunction table and creates devices
|
||||||
|
}
|
||||||
|
return drv;
|
||||||
|
}
|
||||||
|
|
||||||
|
NTSTATUS io_create_device(DRIVER_OBJECT* drv, const char* name,
|
||||||
|
size_t ext_size, DEVICE_OBJECT** out) {
|
||||||
|
if (!drv) return STATUS_UNSUCCESSFUL;
|
||||||
|
spin_lock(&g_io_lock);
|
||||||
|
if (g_device_count >= IO_MAX_DEVICES) {
|
||||||
|
spin_unlock(&g_io_lock);
|
||||||
|
return STATUS_INSUFFICIENT_RES;
|
||||||
|
}
|
||||||
|
DEVICE_OBJECT* dev = (DEVICE_OBJECT*)kzalloc(sizeof(DEVICE_OBJECT));
|
||||||
|
if (!dev) {
|
||||||
|
spin_unlock(&g_io_lock);
|
||||||
|
return STATUS_INSUFFICIENT_RES;
|
||||||
|
}
|
||||||
|
strncpy(dev->name, name ? name : "", IO_NAME_MAX - 1);
|
||||||
|
dev->driver = drv;
|
||||||
|
if (ext_size > 0) {
|
||||||
|
dev->device_extension = kzalloc(ext_size);
|
||||||
|
}
|
||||||
|
// Link into the driver's device list and the global table.
|
||||||
|
dev->next_on_driver = drv->device_list;
|
||||||
|
drv->device_list = dev;
|
||||||
|
g_devices[g_device_count++] = dev;
|
||||||
|
spin_unlock(&g_io_lock);
|
||||||
|
|
||||||
|
if (out) *out = dev;
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEVICE_OBJECT* io_attach_device(DEVICE_OBJECT* source, DEVICE_OBJECT* target) {
|
||||||
|
if (!source || !target) return NULL;
|
||||||
|
spin_lock(&g_io_lock);
|
||||||
|
source->attached_to = target; // source now sits above target in the stack
|
||||||
|
spin_unlock(&g_io_lock);
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEVICE_OBJECT* io_lookup_device(const char* name) {
|
||||||
|
if (!name) return NULL;
|
||||||
|
spin_lock(&g_io_lock);
|
||||||
|
DEVICE_OBJECT* found = NULL;
|
||||||
|
for (int i = 0; i < g_device_count; i++) {
|
||||||
|
if (strcmp(g_devices[i]->name, name) == 0) { found = g_devices[i]; break; }
|
||||||
|
}
|
||||||
|
spin_unlock(&g_io_lock);
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
IRP* io_build_irp(irp_major_t mj, void* buffer, uint32_t length) {
|
||||||
|
IRP* irp = (IRP*)kzalloc(sizeof(IRP));
|
||||||
|
if (!irp) return NULL;
|
||||||
|
irp->major_function = mj;
|
||||||
|
irp->buffer = buffer;
|
||||||
|
irp->length = length;
|
||||||
|
irp->status = STATUS_UNSUCCESSFUL;
|
||||||
|
irp->information = 0;
|
||||||
|
return irp;
|
||||||
|
}
|
||||||
|
|
||||||
|
NTSTATUS io_call_driver(DEVICE_OBJECT* dev, IRP* irp) {
|
||||||
|
if (!dev || !irp) return STATUS_UNSUCCESSFUL;
|
||||||
|
irp->current_device = dev;
|
||||||
|
|
||||||
|
spin_lock(&g_io_lock);
|
||||||
|
g_total_irps++;
|
||||||
|
if (dev->driver) dev->driver->irp_count++;
|
||||||
|
switch (irp->major_function) {
|
||||||
|
case IRP_MJ_READ: dev->read_count++; break;
|
||||||
|
case IRP_MJ_WRITE: dev->write_count++; break;
|
||||||
|
case IRP_MJ_DEVICE_CONTROL: dev->ioctl_count++; break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
driver_dispatch_t fn = NULL;
|
||||||
|
if (dev->driver && irp->major_function < IRP_MJ_MAXIMUM) {
|
||||||
|
fn = dev->driver->major_function[irp->major_function];
|
||||||
|
}
|
||||||
|
spin_unlock(&g_io_lock);
|
||||||
|
|
||||||
|
if (!fn) {
|
||||||
|
irp->status = STATUS_UNSUCCESSFUL;
|
||||||
|
return irp->status;
|
||||||
|
}
|
||||||
|
return fn(dev, irp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void io_complete_request(IRP* irp, NTSTATUS status) {
|
||||||
|
if (!irp) return;
|
||||||
|
irp->status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
void io_free_irp(IRP* irp) {
|
||||||
|
if (irp) kfree(irp);
|
||||||
|
}
|
||||||
|
|
||||||
|
int io_driver_count(void) { return g_driver_count; }
|
||||||
|
DRIVER_OBJECT* io_driver_at(int index) {
|
||||||
|
return (index >= 0 && index < g_driver_count) ? g_drivers[index] : NULL;
|
||||||
|
}
|
||||||
|
int io_device_count(void) { return g_device_count; }
|
||||||
|
DEVICE_OBJECT* io_device_at(int index) {
|
||||||
|
return (index >= 0 && index < g_device_count) ? g_devices[index] : NULL;
|
||||||
|
}
|
||||||
|
void io_get_stats(uint32_t* out_drivers, uint32_t* out_devices, uint64_t* out_irps) {
|
||||||
|
if (out_drivers) *out_drivers = (uint32_t)g_driver_count;
|
||||||
|
if (out_devices) *out_devices = (uint32_t)g_device_count;
|
||||||
|
if (out_irps) *out_irps = g_total_irps;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Demo drivers
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// \Driver\Null : sink writes, return EOF on reads (like NT's \Device\Null).
|
||||||
|
static NTSTATUS null_read(DEVICE_OBJECT* dev, IRP* irp) {
|
||||||
|
(void)dev;
|
||||||
|
irp->information = 0; // 0 bytes == end of file
|
||||||
|
io_complete_request(irp, STATUS_SUCCESS);
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
static NTSTATUS null_write(DEVICE_OBJECT* dev, IRP* irp) {
|
||||||
|
(void)dev;
|
||||||
|
irp->information = irp->length; // swallow everything
|
||||||
|
io_complete_request(irp, STATUS_SUCCESS);
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
static NTSTATUS null_entry(DRIVER_OBJECT* drv) {
|
||||||
|
drv->major_function[IRP_MJ_READ] = null_read;
|
||||||
|
drv->major_function[IRP_MJ_WRITE] = null_write;
|
||||||
|
io_create_device(drv, "\\Device\\Null", 0, NULL);
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// \Driver\Zero : reads yield a buffer full of zero bytes.
|
||||||
|
static NTSTATUS zero_read(DEVICE_OBJECT* dev, IRP* irp) {
|
||||||
|
(void)dev;
|
||||||
|
if (irp->buffer && irp->length) {
|
||||||
|
memset(irp->buffer, 0, irp->length);
|
||||||
|
}
|
||||||
|
irp->information = irp->length;
|
||||||
|
io_complete_request(irp, STATUS_SUCCESS);
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
static NTSTATUS zero_entry(DRIVER_OBJECT* drv) {
|
||||||
|
drv->major_function[IRP_MJ_READ] = zero_read;
|
||||||
|
io_create_device(drv, "\\Device\\Zero", 0, NULL);
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// \Driver\Monitor : a filter stacked above \Device\Null. It counts each IRP in
|
||||||
|
// its device extension and forwards to the lower device, demonstrating a stack.
|
||||||
|
typedef struct { uint64_t forwarded; } monitor_ext_t;
|
||||||
|
|
||||||
|
static NTSTATUS monitor_passthrough(DEVICE_OBJECT* dev, IRP* irp) {
|
||||||
|
monitor_ext_t* ext = (monitor_ext_t*)dev->device_extension;
|
||||||
|
if (ext) ext->forwarded++;
|
||||||
|
if (dev->attached_to) {
|
||||||
|
return io_call_driver(dev->attached_to, irp); // pass down the stack
|
||||||
|
}
|
||||||
|
io_complete_request(irp, STATUS_SUCCESS);
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
static NTSTATUS monitor_entry(DRIVER_OBJECT* drv) {
|
||||||
|
drv->major_function[IRP_MJ_READ] = monitor_passthrough;
|
||||||
|
drv->major_function[IRP_MJ_WRITE] = monitor_passthrough;
|
||||||
|
drv->major_function[IRP_MJ_DEVICE_CONTROL] = monitor_passthrough;
|
||||||
|
|
||||||
|
DEVICE_OBJECT* filter = NULL;
|
||||||
|
io_create_device(drv, "\\Device\\NullMonitor", sizeof(monitor_ext_t), &filter);
|
||||||
|
DEVICE_OBJECT* lower = io_lookup_device("\\Device\\Null");
|
||||||
|
if (filter && lower) {
|
||||||
|
io_attach_device(filter, lower);
|
||||||
|
}
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void io_init(void) {
|
||||||
|
g_driver_count = 0;
|
||||||
|
g_device_count = 0;
|
||||||
|
g_total_irps = 0;
|
||||||
|
|
||||||
|
io_create_driver("\\Driver\\Null", null_entry);
|
||||||
|
io_create_driver("\\Driver\\Zero", zero_entry);
|
||||||
|
io_create_driver("\\Driver\\Monitor", monitor_entry);
|
||||||
|
|
||||||
|
printk(ANSI_BRIGHT_GREEN "IO: " ANSI_RESET
|
||||||
|
"NT I/O Manager online (%d drivers, %d devices, IRP-based dispatch)\n",
|
||||||
|
g_driver_count, g_device_count);
|
||||||
|
}
|
||||||
@@ -31,6 +31,7 @@
|
|||||||
#include <kernel/rust.h>
|
#include <kernel/rust.h>
|
||||||
#include <kernel/nt/ob.h>
|
#include <kernel/nt/ob.h>
|
||||||
#include <kernel/nt/reg.h>
|
#include <kernel/nt/reg.h>
|
||||||
|
#include <kernel/nt/io.h>
|
||||||
#include <kernel/sub_lang.h>
|
#include <kernel/sub_lang.h>
|
||||||
#include <kernel/cpp_kernel.h>
|
#include <kernel/cpp_kernel.h>
|
||||||
#include <userland/snake.h>
|
#include <userland/snake.h>
|
||||||
@@ -659,6 +660,61 @@ static int applet_reg(int argc, char** argv) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int applet_iodev(int argc, char** argv) {
|
||||||
|
(void)argc; (void)argv;
|
||||||
|
uint32_t drivers = 0, devices = 0;
|
||||||
|
uint64_t irps = 0;
|
||||||
|
io_get_stats(&drivers, &devices, &irps);
|
||||||
|
|
||||||
|
printk(ANSI_BRIGHT_CYAN "NT I/O Manager:\n" ANSI_RESET);
|
||||||
|
printk(" Drivers: %u Devices: %u IRPs dispatched: %llu\n",
|
||||||
|
drivers, devices, (unsigned long long)irps);
|
||||||
|
|
||||||
|
int dc = io_device_count();
|
||||||
|
for (int i = 0; i < dc; i++) {
|
||||||
|
DEVICE_OBJECT* dev = io_device_at(i);
|
||||||
|
if (!dev) continue;
|
||||||
|
printk(" " ANSI_YELLOW "%-22s" ANSI_RESET " drv=%-16s r=%llu w=%llu ioctl=%llu",
|
||||||
|
dev->name, dev->driver ? dev->driver->name : "?",
|
||||||
|
(unsigned long long)dev->read_count,
|
||||||
|
(unsigned long long)dev->write_count,
|
||||||
|
(unsigned long long)dev->ioctl_count);
|
||||||
|
if (dev->attached_to) {
|
||||||
|
printk(" -> %s", dev->attached_to->name);
|
||||||
|
}
|
||||||
|
printk("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Live demo: push a WRITE IRP through the filtered Null stack and a READ to
|
||||||
|
// the Zero device, then report what came back.
|
||||||
|
printk(ANSI_BRIGHT_CYAN "IRP demo:\n" ANSI_RESET);
|
||||||
|
DEVICE_OBJECT* mon = io_lookup_device("\\Device\\NullMonitor");
|
||||||
|
if (mon) {
|
||||||
|
char payload[16];
|
||||||
|
memset(payload, 'X', sizeof(payload));
|
||||||
|
IRP* irp = io_build_irp(IRP_MJ_WRITE, payload, sizeof(payload));
|
||||||
|
if (irp) {
|
||||||
|
NTSTATUS st = io_call_driver(mon, irp);
|
||||||
|
printk(" WRITE %u bytes to %s -> status=0x%X, %u bytes sunk (via filter)\n",
|
||||||
|
(unsigned)sizeof(payload), mon->name, st, irp->information);
|
||||||
|
io_free_irp(irp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DEVICE_OBJECT* zero = io_lookup_device("\\Device\\Zero");
|
||||||
|
if (zero) {
|
||||||
|
uint8_t buf[8];
|
||||||
|
memset(buf, 0xEE, sizeof(buf));
|
||||||
|
IRP* irp = io_build_irp(IRP_MJ_READ, buf, sizeof(buf));
|
||||||
|
if (irp) {
|
||||||
|
io_call_driver(zero, irp);
|
||||||
|
printk(" READ %u bytes from %s -> first bytes %02X %02X %02X (zeroed)\n",
|
||||||
|
irp->information, zero->name, buf[0], buf[1], buf[2]);
|
||||||
|
io_free_irp(irp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int applet_rand(int argc, char** argv) {
|
static int applet_rand(int argc, char** argv) {
|
||||||
int count = (argc >= 2) ? atoi(argv[1]) : 4;
|
int count = (argc >= 2) ? atoi(argv[1]) : 4;
|
||||||
printk("Random Integers: ");
|
printk("Random Integers: ");
|
||||||
@@ -2371,6 +2427,7 @@ static const lazybox_applet_t applets[] = {
|
|||||||
{"objdir", applet_objdir, "objdir [\\path]", "List the NT object namespace","Kernel"},
|
{"objdir", applet_objdir, "objdir [\\path]", "List the NT object namespace","Kernel"},
|
||||||
{"ntobj", applet_ntobj, "ntobj", "NT Object Manager stats/demo","Kernel"},
|
{"ntobj", applet_ntobj, "ntobj", "NT Object Manager stats/demo","Kernel"},
|
||||||
{"reg", applet_reg, "reg query|add|set|stats", "NT configuration registry", "Kernel"},
|
{"reg", applet_reg, "reg query|add|set|stats", "NT configuration registry", "Kernel"},
|
||||||
|
{"iodev", applet_iodev, "iodev", "NT I/O Manager devices & IRP","Kernel"},
|
||||||
{"rand", applet_rand, "rand [count]", "Generate random numbers", "Crypto"},
|
{"rand", applet_rand, "rand [count]", "Generate random numbers", "Crypto"},
|
||||||
{"certcheck", applet_certcheck, "certcheck", "View X.509 kernel keyring", "Security"},
|
{"certcheck", applet_certcheck, "certcheck", "View X.509 kernel keyring", "Security"},
|
||||||
{"capsh", applet_capsh, "capsh", "View process capabilities", "Security"},
|
{"capsh", applet_capsh, "capsh", "View process capabilities", "Security"},
|
||||||
|
|||||||
Reference in new issue
Block a user