diff --git a/src/kernel/kernel.c b/src/kernel/kernel.c index 1563af3..1210087 100644 --- a/src/kernel/kernel.c +++ b/src/kernel/kernel.c @@ -218,6 +218,408 @@ duk_ret_t native_irq_register(duk_context *ctx) return 1; } +// Proxy getter for __oskrnl that forwards to root context's globalThis.__oskrnl +duk_ret_t oskrnl_proxy_getter(duk_context *isolated_ctx) +{ + // Get the property name being accessed + const char *prop_name = duk_require_string(isolated_ctx, 1); + + // Get the root context from the hidden symbol in the isolated context + duk_push_global_stash(isolated_ctx); + duk_get_prop_string(isolated_ctx, -1, "\xFF" + "root_ctx_ptr"); + duk_context *root_ctx = (duk_context *)duk_get_pointer(isolated_ctx, -1); + duk_pop_2(isolated_ctx); + + if (root_ctx == NULL) + { + duk_push_undefined(isolated_ctx); + return 1; + } + + // Access globalThis.__oskrnl[prop_name] in the root context + duk_push_global_object(root_ctx); + duk_get_prop_string(root_ctx, -1, "__oskrnl"); + + if (!duk_is_object(root_ctx, -1)) + { + duk_pop_2(root_ctx); + duk_push_undefined(isolated_ctx); + return 1; + } + + duk_get_prop_string(root_ctx, -1, prop_name); + + // If it's a function, we need to create a wrapper that calls it in root context + if (duk_is_function(root_ctx, -1)) + { + // Store the function reference in root context's stash + char stash_key[128]; + snprintf(stash_key, sizeof(stash_key), "\xFF" + "oskrnl_fn_%s", + prop_name); + + duk_push_global_stash(root_ctx); + duk_dup(root_ctx, -2); // Duplicate the function + duk_put_prop_string(root_ctx, -2, stash_key); + duk_pop(root_ctx); // Pop stash + + duk_pop_3(root_ctx); // Pop function, __oskrnl, globalThis + + // Create a wrapper function in the isolated context + // Use ES5 syntax compatible with Duktape + const char *wrapper_code = + "(function(propName) {" + " return function() {" + " var args = Array.prototype.slice.call(arguments);" + " return __oskrnl_call_native(propName, args);" + " };" + "})"; + + duk_push_string(isolated_ctx, wrapper_code); + if (duk_peval(isolated_ctx) != 0) + { + duk_pop(isolated_ctx); + duk_push_undefined(isolated_ctx); + return 1; + } + + duk_push_string(isolated_ctx, prop_name); + if (duk_pcall(isolated_ctx, 1) != 0) + { + duk_pop(isolated_ctx); + duk_push_undefined(isolated_ctx); + return 1; + } + + return 1; + } + + // For non-function properties, just get the value (primitives) + if (duk_is_number(root_ctx, -1)) + { + double val = duk_get_number(root_ctx, -1); + duk_pop_3(root_ctx); + duk_push_number(isolated_ctx, val); + return 1; + } + else if (duk_is_string(root_ctx, -1)) + { + const char *val = duk_get_string(root_ctx, -1); + duk_pop_3(root_ctx); + duk_push_string(isolated_ctx, val); + return 1; + } + else if (duk_is_boolean(root_ctx, -1)) + { + int val = duk_get_boolean(root_ctx, -1); + duk_pop_3(root_ctx); + duk_push_boolean(isolated_ctx, val); + return 1; + } + + duk_pop_3(root_ctx); + duk_push_undefined(isolated_ctx); + return 1; +} + +// Callback bridge to call from root context back to isolated context +duk_ret_t native_bridge_callback(duk_context *root_ctx) +{ + // Get metadata from the current function (the bridge) + duk_push_current_function(root_ctx); + + duk_get_prop_string(root_ctx, -1, "\xFF" + "isolated_ctx_ptr"); + duk_context *isolated_ctx = (duk_context *)duk_get_pointer(root_ctx, -1); + duk_pop(root_ctx); + + duk_get_prop_string(root_ctx, -1, "\xFF" + "fn_id"); + const char *fn_id = duk_get_string(root_ctx, -1); + duk_pop(root_ctx); + + duk_pop(root_ctx); // Pop current function + + if (isolated_ctx == NULL || fn_id == NULL) + { + return 0; + } + + // Get the target function from isolated context stash + duk_push_global_stash(isolated_ctx); + duk_get_prop_string(isolated_ctx, -1, fn_id); + duk_remove(isolated_ctx, -2); // Remove stash + + if (!duk_is_function(isolated_ctx, -1)) + { + duk_pop(isolated_ctx); + return 0; + } + + // Marshal arguments from root to isolated + duk_idx_t nargs = duk_get_top(root_ctx); + for (duk_idx_t i = 0; i < nargs; i++) + { + if (duk_is_number(root_ctx, i)) + { + duk_push_number(isolated_ctx, duk_get_number(root_ctx, i)); + } + else if (duk_is_string(root_ctx, i)) + { + duk_push_string(isolated_ctx, duk_get_string(root_ctx, i)); + } + else if (duk_is_boolean(root_ctx, i)) + { + duk_push_boolean(isolated_ctx, duk_get_boolean(root_ctx, i)); + } + else + { + duk_push_undefined(isolated_ctx); + } + } + + // Call the isolated function + if (duk_pcall(isolated_ctx, nargs) != 0) + { + // Error occurred + const char *err = duk_safe_to_string(isolated_ctx, -1); + // k_printf((char *)"[Bridge] Error: ", 24); + // k_printf((char *)err, 25); + duk_pop(isolated_ctx); // Pop error + return 0; + } + + // Marshal return value back to root + if (duk_is_number(isolated_ctx, -1)) + { + duk_push_number(root_ctx, duk_get_number(isolated_ctx, -1)); + } + else if (duk_is_string(isolated_ctx, -1)) + { + duk_push_string(root_ctx, duk_get_string(isolated_ctx, -1)); + } + else if (duk_is_boolean(isolated_ctx, -1)) + { + duk_push_boolean(root_ctx, duk_get_boolean(isolated_ctx, -1)); + } + else + { + duk_push_undefined(root_ctx); + } + + duk_pop(isolated_ctx); // Pop result + + return 1; +} + +// Native helper to call root context oskrnl functions +duk_ret_t oskrnl_call_native(duk_context *isolated_ctx) +{ + // Get function name and arguments + const char *fn_name = duk_require_string(isolated_ctx, 0); + + if (!duk_is_array(isolated_ctx, 1)) + { + duk_push_undefined(isolated_ctx); + return 1; + } + + // Get the root context + duk_push_global_stash(isolated_ctx); + duk_get_prop_string(isolated_ctx, -1, "\xFF" + "root_ctx_ptr"); + duk_context *root_ctx = (duk_context *)duk_get_pointer(isolated_ctx, -1); + duk_pop_2(isolated_ctx); + + if (root_ctx == NULL) + { + duk_push_undefined(isolated_ctx); + return 1; + } + + // Get the function from root context stash + char stash_key[128]; + snprintf(stash_key, sizeof(stash_key), "\xFF" + "oskrnl_fn_%s", + fn_name); + + duk_push_global_stash(root_ctx); + duk_get_prop_string(root_ctx, -1, stash_key); + + if (!duk_is_function(root_ctx, -1)) + { + duk_pop_2(root_ctx); + duk_push_undefined(isolated_ctx); + return 1; + } + + // Transfer arguments from isolated context to root context + duk_size_t arg_count = duk_get_length(isolated_ctx, 1); + + for (duk_size_t i = 0; i < arg_count; i++) + { + duk_get_prop_index(isolated_ctx, 1, i); + + if (duk_is_number(isolated_ctx, -1)) + { + duk_push_number(root_ctx, duk_get_number(isolated_ctx, -1)); + } + else if (duk_is_string(isolated_ctx, -1)) + { + duk_push_string(root_ctx, duk_get_string(isolated_ctx, -1)); + } + else if (duk_is_boolean(isolated_ctx, -1)) + { + duk_push_boolean(root_ctx, duk_get_boolean(isolated_ctx, -1)); + } + else if (duk_is_function(isolated_ctx, -1)) + { + // Create a bridge function in root context + static int bridge_cnt = 0; + char bridge_id[32]; + snprintf(bridge_id, sizeof(bridge_id), "b_%d", bridge_cnt++); + + // Stash isolated function + duk_push_global_stash(isolated_ctx); + duk_dup(isolated_ctx, -2); + duk_put_prop_string(isolated_ctx, -2, bridge_id); + duk_pop(isolated_ctx); + + // Create bridge in root + duk_push_c_function(root_ctx, native_bridge_callback, DUK_VARARGS); + + // Attach metadata + duk_push_pointer(root_ctx, isolated_ctx); + duk_put_prop_string(root_ctx, -2, "\xFF" + "isolated_ctx_ptr"); + + duk_push_string(root_ctx, bridge_id); + duk_put_prop_string(root_ctx, -2, "\xFF" + "fn_id"); + } + else + { + duk_push_undefined(root_ctx); + } + + duk_pop(isolated_ctx); + } + + // Call the function in root context + duk_int_t rc = duk_pcall(root_ctx, arg_count); + + // Transfer return value back to isolated context + if (rc == 0) + { + if (duk_is_number(root_ctx, -1)) + { + duk_push_number(isolated_ctx, duk_get_number(root_ctx, -1)); + } + else if (duk_is_string(root_ctx, -1)) + { + duk_push_string(isolated_ctx, duk_get_string(root_ctx, -1)); + } + else if (duk_is_boolean(root_ctx, -1)) + { + duk_push_boolean(isolated_ctx, duk_get_boolean(root_ctx, -1)); + } + else + { + duk_push_undefined(isolated_ctx); + } + duk_pop(root_ctx); // Pop result + } + else + { + duk_pop(root_ctx); // Pop error + duk_push_undefined(isolated_ctx); + } + + duk_pop(root_ctx); // Pop stash + + return 1; +} + +// Native function to execute JS code in isolated context with __oskrnl access +duk_ret_t native_isolated_exec(duk_context *ctx) +{ + // Get the JS code to execute + const char *js_code = duk_require_string(ctx, 0); + + // Create a new isolated Duktape heap + duk_context *isolated_ctx = duk_create_heap_default(); + + if (isolated_ctx == NULL) + { + duk_push_boolean(ctx, 0); + return 1; + } + + // Store reference to root context in isolated context's stash + duk_push_global_stash(isolated_ctx); + duk_push_pointer(isolated_ctx, ctx); + duk_put_prop_string(isolated_ctx, -2, "\xFF" + "root_ctx_ptr"); + duk_pop(isolated_ctx); + + // Register the native call helper in isolated context + duk_push_c_function(isolated_ctx, oskrnl_call_native, 2); + duk_put_global_string(isolated_ctx, "__oskrnl_call_native"); + + // Register the getter function + duk_push_c_function(isolated_ctx, oskrnl_proxy_getter, 2); + duk_put_global_string(isolated_ctx, "__oskrnl_get_prop"); + + // Create __oskrnl proxy object using ES6 Proxy + const char *proxy_code = + "(function() {" + " var handler = {" + " get: function(target, prop) {" + " if (typeof prop === 'symbol') return undefined;" + " return __oskrnl_get_prop(null, String(prop));" + " }" + " };" + " return new Proxy({}, handler);" + "})()"; + + duk_push_string(isolated_ctx, proxy_code); + + if (duk_peval(isolated_ctx) != 0) + { + duk_pop(isolated_ctx); + duk_destroy_heap(isolated_ctx); + duk_push_boolean(ctx, 0); + return 1; + } + + duk_put_global_string(isolated_ctx, "__oskrnl"); + + // Execute the provided JS code + duk_push_string(isolated_ctx, js_code); + duk_int_t rc = duk_peval(isolated_ctx); + + // Check for errors + int success = (rc == 0); + + if (!success) + { + // Transfer error message back to root context for display + const char *err = duk_safe_to_string(isolated_ctx, -1); + k_printf((char *)"[Isolated] Execution error: ", 12); + k_printf((char *)err, 13); + } + + duk_pop(isolated_ctx); // Pop result or error + + // Clean up the isolated heap + // duk_destroy_heap(isolated_ctx); + + // Return success/failure + duk_push_boolean(ctx, success); + return 1; +} + void kmain() { // Initialize IDT and ISRs @@ -268,6 +670,10 @@ void kmain() duk_push_c_function(ctx, native_irq_register, 2); duk_put_global_string(ctx, "$irqregister"); + // Register native isolated execution function + duk_push_c_function(ctx, native_isolated_exec, 1); + duk_put_global_string(ctx, "$isolatedExec"); + // Enable interrupts before JavaScript execution __asm__ volatile("sti"); diff --git a/src/os/src/kernel/index.ts b/src/os/src/kernel/index.ts index 0ef9260..ab2537c 100644 --- a/src/os/src/kernel/index.ts +++ b/src/os/src/kernel/index.ts @@ -1,6 +1,7 @@ import { Logger } from "../lib/libstd/logger/logger.kmod"; import { charc } from "../lib/libts/byte"; import { padStart } from "../lib/libts/string"; +import { uiarrtostr } from "../lib/libts/uint_arr"; import { getDate } from "../lib/sys/date"; import { kdriver_dev_ata_detectDisks } from "./drivers/dev/ata"; import { kdriver_dev_pci_detectDevices } from "./drivers/dev/pci"; @@ -11,6 +12,7 @@ import { import { devfs_driver, devfs_getDevice } from "./filesystem/devfs"; import { fat32_driver } from "./filesystem/fat32"; import { sysfs_driver, sysfs_readFile } from "./filesystem/sysfs"; +import { kmod_app_init, kmod_app_run } from "./modules/app/app.kmod"; import { kmod_disks_detectDisks } from "./modules/disks/disks.kmod"; import { kmod_drivers_init, @@ -56,42 +58,19 @@ export function kmain() { Logger.log("[Kernel] Initializing drives..."); kmod_disks_detectDisks(); - kmod_graphics_vga_pushLine("[Kernel] Kernel initialized successfully."); - const dev = devfs_getDevice("/hda1"); - if (!dev) throw new Error("Drive not found"); - - kmod_filesystem_mount("/home", function () { + if (!dev) throw new Error("System drive not found"); + + kmod_filesystem_mount("/disk", function () { return fat32_driver(dev.driver, dev.data); }); - let input = ""; - let running = false; + Logger.log("[Kernel] Initializing app module..."); + kmod_app_init(); - kmod_graphics_vga_setLastLine("> "); + Logger.log("[Kernel] Kernel initialized successfully."); - kmod_terminal_input_onKeyboardInput(function (code) { - if (running) return; + const shell = uiarrtostr(kmod_filesystem_readFile("/disk/ushellc")!); - if (code == "Enter") { - running = true; - kmod_graphics_vga_pushLine("Starting " + input); - const f = kmod_filesystem_readFile(input); - if (!f) return; - - let app = ""; - - for (let i = 0; i < f.length; i++) { - app += String.fromCharCode(f[i]!); - } - - // EXTREMELY DANGEROUS -- REPLACE WITH SANDBOXING - eval("const console = { log: Logger.log } ;" + app); - Logger.log("DONE"); - running = false; - } else { - input += code.toLowerCase(); - kmod_graphics_vga_setLastLine("> " + input); - } - }); + kmod_app_run(shell); } diff --git a/src/os/src/kernel/modules/app/app.kmod.ts b/src/os/src/kernel/modules/app/app.kmod.ts new file mode 100644 index 0000000..1c36fd4 --- /dev/null +++ b/src/os/src/kernel/modules/app/app.kmod.ts @@ -0,0 +1,26 @@ +import { Logger } from "../../../lib/libstd/logger/logger.kmod"; +import { Path } from "../../../lib/libstd/path"; +import { iexec } from "../../../lib/libts/exec"; +import { uiarrtostr } from "../../../lib/libts/uint_arr"; +import { + kmod_filesystem_listDir, + kmod_filesystem_readFile, +} from "../filesystem/filesystem.kmod"; +import { oskrnl_register } from "../../../oskrnl"; + +export function kmod_app_init() { + oskrnl_register(); +} + +export function kmod_app_run(path: string) { + const meta = JSON.parse( + uiarrtostr(kmod_filesystem_readFile(Path.join(path, "meta.lam"))!) + ); + + if (!meta) throw new Error("App metadata not found"); + + const entrypoint = Path.join(path, meta["app:entrypoint"]); + const code = uiarrtostr(kmod_filesystem_readFile(entrypoint)!); + + iexec(code); +} diff --git a/src/os/src/lib/libstd/logger/logger.kmod.ts b/src/os/src/lib/libstd/logger/logger.kmod.ts index 2eba19e..4148671 100644 --- a/src/os/src/lib/libstd/logger/logger.kmod.ts +++ b/src/os/src/lib/libstd/logger/logger.kmod.ts @@ -1,6 +1,9 @@ import { charc } from "../../libts/byte"; import { kdriver_etc_serial_transmit } from "../../../kernel/drivers/etc/serial"; -import { kmod_graphics_vga_pushLine } from "../../../kernel/modules/graphics/vga"; +import { + kmod_graphics_vga_pushLine, + kmod_graphics_vga_setLastLine, +} from "../../../kernel/modules/graphics/vga"; export const Logger = { log(message: string) { @@ -12,4 +15,13 @@ export const Logger = { kdriver_etc_serial_transmit(charc("\n")); }, + update(message: string) { + kmod_graphics_vga_setLastLine(message); + + kdriver_etc_serial_transmit(charc("\x1b[2K")); + kdriver_etc_serial_transmit(charc("\r")); + for (let i = 0; i < message.length; i++) { + kdriver_etc_serial_transmit(charc(message[i]!)); + } + }, }; diff --git a/src/os/src/lib/libts/exec.ts b/src/os/src/lib/libts/exec.ts new file mode 100644 index 0000000..a26b97c --- /dev/null +++ b/src/os/src/lib/libts/exec.ts @@ -0,0 +1,3 @@ +export function iexec(code: string) { + $isolatedExec(code); +} \ No newline at end of file diff --git a/src/os/src/lib/libts/uint_arr.ts b/src/os/src/lib/libts/uint_arr.ts new file mode 100644 index 0000000..7db3c36 --- /dev/null +++ b/src/os/src/lib/libts/uint_arr.ts @@ -0,0 +1,9 @@ +export function uiarrtostr(a: number[]) { + let s = ""; + + for (let i = 0; i < a.length; i++) { + s += String.fromCharCode(a[i]!); + } + + return s; +} \ No newline at end of file diff --git a/src/os/src/oskrnl/console/console.ts b/src/os/src/oskrnl/console/console.ts new file mode 100644 index 0000000..8013b90 --- /dev/null +++ b/src/os/src/oskrnl/console/console.ts @@ -0,0 +1,9 @@ +import { Logger } from "../../lib/libstd/logger/logger.kmod"; + +export function oskrnl_console_log(data: any) { + Logger.log(String(data)); +} + +export function oskrnl_console_update(data: any) { + Logger.update(String(data)); +} diff --git a/src/os/src/oskrnl/index.ts b/src/os/src/oskrnl/index.ts new file mode 100644 index 0000000..0542e69 --- /dev/null +++ b/src/os/src/oskrnl/index.ts @@ -0,0 +1,10 @@ +import { oskrnl_console_log, oskrnl_console_update } from "./console/console"; +import { oskrnl_input_onKeyPress } from "./input/input"; + +export function oskrnl_register() { + (globalThis as any).__oskrnl = { + console_log: oskrnl_console_log, + console_update: oskrnl_console_update, + input_onKeyPress: oskrnl_input_onKeyPress, + }; +} diff --git a/src/os/src/oskrnl/input/input.ts b/src/os/src/oskrnl/input/input.ts new file mode 100644 index 0000000..94cc0c3 --- /dev/null +++ b/src/os/src/oskrnl/input/input.ts @@ -0,0 +1,5 @@ +import { kmod_terminal_input_onKeyboardInput } from "../../kernel/modules/terminal/input"; + +export function oskrnl_input_onKeyPress(handler: (key: string) => void) { + kmod_terminal_input_onKeyboardInput(handler); +} diff --git a/src/os/src/programs/tsdos/index.ts b/src/os/src/programs/tsdos/index.ts deleted file mode 100644 index 608a092..0000000 --- a/src/os/src/programs/tsdos/index.ts +++ /dev/null @@ -1 +0,0 @@ -console.log("TS-DOS Program Started"); diff --git a/src/os/src/types/c/bindings.d.ts b/src/os/src/types/c/bindings.d.ts index a6482f2..809d847 100644 --- a/src/os/src/types/c/bindings.d.ts +++ b/src/os/src/types/c/bindings.d.ts @@ -11,3 +11,5 @@ declare const $irqregister: ( irq: number, handler: (irqNum: number) => void ) => boolean; +declare function $isolatedExec(code: string): void; +