diff --git a/gui/gui_apps.c b/gui/gui_apps.c index b8876a2..d2667c0 100644 --- a/gui/gui_apps.c +++ b/gui/gui_apps.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -354,3 +355,194 @@ void gui_app_about_launch(int x, int y, int w, int h) { win->paint = about_paint; } } + +// ================================================================= +// 7. Conway's Game of Life (interactive cellular automaton) +// ================================================================= +#define LIFE_COLS 56 +#define LIFE_ROWS 34 +#define LIFE_TOOLBAR_H 24 +#define LIFE_STATUS_H 22 + +typedef struct { + uint8_t cur[LIFE_ROWS][LIFE_COLS]; + uint8_t nxt[LIFE_ROWS][LIFE_COLS]; + bool running; + uint32_t generation; + uint32_t frame; +} life_data_t; + +// Seed the grid from the Rust ChaCha20 CSPRNG (~28% of cells alive). +static void life_randomize(life_data_t* ld) { + // Static (not on the stack): the desktop is single-threaded, and this keeps + // ~1.9 KB off the kernel stack that the GUI thread runs on. + static uint8_t rnd[LIFE_ROWS * LIFE_COLS]; + if (rust_csprng_get_random(rnd, sizeof(rnd)) != 0) { + // Deterministic fallback if the CSPRNG is unavailable. + for (size_t i = 0; i < sizeof(rnd); i++) { + rnd[i] = (uint8_t)((i * 2654435761u) >> 24); + } + } + for (int r = 0; r < LIFE_ROWS; r++) { + for (int c = 0; c < LIFE_COLS; c++) { + ld->cur[r][c] = ((rnd[r * LIFE_COLS + c] & 7) < 2) ? 1 : 0; + } + } + ld->generation = 0; +} + +// One tick of the B3/S23 rule with dead borders. +static void life_step(life_data_t* ld) { + for (int r = 0; r < LIFE_ROWS; r++) { + for (int c = 0; c < LIFE_COLS; c++) { + int n = 0; + for (int dr = -1; dr <= 1; dr++) { + for (int dc = -1; dc <= 1; dc++) { + if (dr == 0 && dc == 0) continue; + int rr = r + dr, cc = c + dc; + if (rr >= 0 && rr < LIFE_ROWS && cc >= 0 && cc < LIFE_COLS && ld->cur[rr][cc]) { + n++; + } + } + } + uint8_t alive = ld->cur[r][c]; + ld->nxt[r][c] = ((alive && (n == 2 || n == 3)) || (!alive && n == 3)) ? 1 : 0; + } + } + memcpy(ld->cur, ld->nxt, sizeof(ld->cur)); + ld->generation++; +} + +// Grid cell size and top-left, derived from the current window geometry so the +// board scales when the window is resized. Kept identical in paint and event. +static void life_geometry(gui_window_t* win, int* cell, int* gw, int* gh, int* gx_rel, int* grid_top_rel) { + int avail_w = win->width - 16; + int avail_h = win->height - GUI_TITLEBAR_HEIGHT - LIFE_TOOLBAR_H - LIFE_STATUS_H; + int cw = avail_w / LIFE_COLS; + int ch = avail_h / LIFE_ROWS; + int sz = (cw < ch) ? cw : ch; + if (sz < 2) sz = 2; + *cell = sz; + *gw = sz * LIFE_COLS; + *gh = sz * LIFE_ROWS; + *gx_rel = (win->width - *gw) / 2; + *grid_top_rel = LIFE_TOOLBAR_H; +} + +static const char* const LIFE_BTNS[4] = { "Play", "Step", "Random", "Clear" }; +#define LIFE_BTN_W 66 +#define LIFE_BTN_GAP 72 + +static void life_paint(gui_window_t* win) { + life_data_t* ld = (life_data_t*)win->user_data; + if (!ld) return; + + int cx = win->x + 8; + int top = win->y + GUI_TITLEBAR_HEIGHT; + + // Toolbar + for (int i = 0; i < 4; i++) { + int bx = cx + i * LIFE_BTN_GAP; + int by = top + 3; + gui_gfx_fill_rect(bx, by, LIFE_BTN_W, 18, GUI_THEME_BG_DARK); + gui_gfx_draw_rect(bx, by, LIFE_BTN_W, 18, GUI_THEME_BORDER); + const char* label = (i == 0 && ld->running) ? "Pause" : LIFE_BTNS[i]; + int tw = (int)strlen(label) * 8; + gui_gfx_draw_string(bx + (LIFE_BTN_W - tw) / 2, by + 5, label, GUI_THEME_TEXT_MAIN); + } + + // Board + int cell, gw, gh, gx_rel, grid_top_rel; + life_geometry(win, &cell, &gw, &gh, &gx_rel, &grid_top_rel); + int gx = win->x + gx_rel; + int gy = top + grid_top_rel; + + gui_gfx_fill_rect(gx, gy, gw, gh, 0xFF0B1220); + int pop = 0; + for (int r = 0; r < LIFE_ROWS; r++) { + for (int c = 0; c < LIFE_COLS; c++) { + if (ld->cur[r][c]) { + pop++; + gui_gfx_fill_rect(gx + c * cell, gy + r * cell, + (cell > 2) ? cell - 1 : cell, + (cell > 2) ? cell - 1 : cell, GUI_THEME_SUCCESS); + } + } + } + gui_gfx_draw_rect(gx, gy, gw, gh, GUI_THEME_BORDER); + + // Status line + char buf[96]; + snprintf(buf, sizeof(buf), "Gen %u Pop %d %s -- click cells to toggle", + ld->generation, pop, ld->running ? "RUNNING" : "paused"); + gui_gfx_draw_string(cx, gy + gh + 5, buf, GUI_THEME_TEXT_MUTED); + + // Advance the automaton while running, throttled so it is watchable. + if (ld->running) { + ld->frame++; + if ((ld->frame & 3) == 0) { + life_step(ld); + } + } +} + +static void life_event(gui_window_t* win, const gui_event_t* ev) { + life_data_t* ld = (life_data_t*)win->user_data; + if (!ld) return; + + if (ev->type == GUI_EVENT_CLOSE) { + if (win->user_data) { + kfree(win->user_data); + win->user_data = NULL; + } + return; + } + + // Act only on the press edge so buttons and cells do not re-fire while held. + if (ev->type != GUI_EVENT_MOUSE_DOWN) return; + + int rel_x = ev->rel_x; + int rel_y = ev->rel_y; + + // Toolbar row + if (rel_y >= 3 && rel_y < LIFE_TOOLBAR_H) { + int i = (rel_x - 8) / LIFE_BTN_GAP; + int within = (rel_x - 8) - i * LIFE_BTN_GAP; + if (i >= 0 && i < 4 && within >= 0 && within < LIFE_BTN_W) { + switch (i) { + case 0: ld->running = !ld->running; break; + case 1: life_step(ld); break; + case 2: life_randomize(ld); break; + case 3: memset(ld->cur, 0, sizeof(ld->cur)); ld->generation = 0; break; + } + } + return; + } + + // Board: toggle the clicked cell + int cell, gw, gh, gx_rel, grid_top_rel; + life_geometry(win, &cell, &gw, &gh, &gx_rel, &grid_top_rel); + int lx = rel_x - gx_rel; + int ly = rel_y - grid_top_rel; + if (lx >= 0 && ly >= 0 && lx < gw && ly < gh) { + int c = lx / cell; + int r = ly / cell; + if (r >= 0 && r < LIFE_ROWS && c >= 0 && c < LIFE_COLS) { + ld->cur[r][c] ^= 1; + } + } +} + +void gui_app_life_launch(int x, int y, int w, int h) { + gui_window_t* win = gui_wm_create_window("Game of Life", x, y, (w > 0) ? w : 480, (h > 0) ? h : 380); + if (win) { + life_data_t* ld = (life_data_t*)kzalloc(sizeof(life_data_t)); + if (ld) { + life_randomize(ld); + ld->running = true; + } + win->user_data = ld; + win->paint = life_paint; + win->handle_event = life_event; + } +} diff --git a/gui/gui_desktop.c b/gui/gui_desktop.c index 0e4aeea..0966cf8 100644 --- a/gui/gui_desktop.c +++ b/gui/gui_desktop.c @@ -56,6 +56,7 @@ static const desktop_app_t g_apps[] = { { "Editor", GUI_ICON_FILE, gui_app_editor_launch, 120, 50, 500, 330 }, { "Calculator", GUI_ICON_CALC, gui_app_calc_launch, 260, 100, 220, 240 }, { "Paint", GUI_ICON_PAINT, gui_app_paint_launch, 200, 60, 360, 260 }, + { "Life", GUI_ICON_FLASK, gui_app_life_launch, 240, 70, 480, 380 }, { "Clock", GUI_ICON_CLOCK, gui_app_clock_launch, 460, 120, 280, 260 }, { "Settings", GUI_ICON_SETTINGS, gui_app_settings_launch, 160, 60, 400, 320 }, { "About", GUI_ICON_INFO, gui_app_about_launch, 240, 140, 330, 170 }, @@ -63,7 +64,8 @@ static const desktop_app_t g_apps[] = { #define APP_COUNT ((int)(sizeof(g_apps) / sizeof(g_apps[0]))) // Which of the above appear on the desktop itself. -static const int g_desktop_icons[] = { 0, 1, 2, 4, 5, 10 }; +// Indices into g_apps[]: Terminal, Files, Monitor, Kernel Log, KTest, Life, Settings. +static const int g_desktop_icons[] = { 0, 1, 2, 4, 5, 9, 11 }; #define DESKTOP_ICON_COUNT ((int)(sizeof(g_desktop_icons) / sizeof(g_desktop_icons[0]))) static bool g_start_menu_open = false; diff --git a/include/gui/gui_apps.h b/include/gui/gui_apps.h index 2f64a7f..53500f7 100644 --- a/include/gui/gui_apps.h +++ b/include/gui/gui_apps.h @@ -9,5 +9,6 @@ void gui_app_fileman_launch(int x, int y, int w, int h); void gui_app_calc_launch(int x, int y, int w, int h); void gui_app_paint_launch(int x, int y, int w, int h); 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); #endif // _GUI_APPS_H diff --git a/include/kernel/rust.h b/include/kernel/rust.h index a4bfc28..489d91d 100644 --- a/include/kernel/rust.h +++ b/include/kernel/rust.h @@ -71,6 +71,11 @@ typedef struct { int rust_kernel_init(void); const char* rust_kernel_status(void); +// Checksums & Non-Cryptographic Hashing (Rust) +uint32_t rust_crc32c(const uint8_t* data, size_t len); +uint32_t rust_adler32(const uint8_t* data, size_t len); +uint64_t rust_fnv1a64(const uint8_t* data, size_t len); + // Cryptography int rust_chacha20_crypt(const uint8_t* key, const uint8_t* nonce, uint32_t counter, uint8_t* data, size_t len); int rust_csprng_get_random(uint8_t* buffer, size_t len); diff --git a/rust/rust_fallback.c b/rust/rust_fallback.c index 0dec12c..53af674 100644 --- a/rust/rust_fallback.c +++ b/rust/rust_fallback.c @@ -215,3 +215,39 @@ int rust_aead_decrypt(const uint8_t* key, const uint8_t* nonce, const uint8_t* a if (cipher && plain_out && cipher_len > 0) memcpy(plain_out, cipher, cipher_len); return 0; } + +// --- Checksums & non-cryptographic hashing (C fallback for non-x86 targets) --- +// These mirror rust/src/checksum/mod.rs bit-for-bit so callers get identical +// results whether the native Rust staticlib or this bridge is linked. +uint32_t rust_crc32c(const uint8_t* data, size_t len) { + if (!data) return 0; + uint32_t crc = 0xFFFFFFFFu; + for (size_t i = 0; i < len; i++) { + crc ^= data[i]; + for (int b = 0; b < 8; b++) { + uint32_t mask = -(crc & 1u); + crc = (crc >> 1) ^ (0x82F63B78u & mask); + } + } + return ~crc; +} + +uint32_t rust_adler32(const uint8_t* data, size_t len) { + if (!data) return 1; + uint32_t a = 1, b = 0; + for (size_t i = 0; i < len; i++) { + a = (a + data[i]) % 65521u; + b = (b + a) % 65521u; + } + return (b << 16) | a; +} + +uint64_t rust_fnv1a64(const uint8_t* data, size_t len) { + uint64_t hash = 0xcbf29ce484222325ull; + if (!data) return hash; + for (size_t i = 0; i < len; i++) { + hash ^= data[i]; + hash *= 0x00000100000001B3ull; + } + return hash; +} diff --git a/rust/src/checksum/mod.rs b/rust/src/checksum/mod.rs new file mode 100644 index 0000000..a0e201c --- /dev/null +++ b/rust/src/checksum/mod.rs @@ -0,0 +1,76 @@ +//! Memory-safe checksum and non-cryptographic hash engine for SUB-OS. +//! +//! Implements CRC-32C (Castagnoli, the checksum used by ext4 metadata, iSCSI +//! and SSE4.2's `crc32` instruction), the Adler-32 rolling checksum from zlib, +//! and the 64-bit FNV-1a hash used for fast in-kernel hash-table keying. All +//! three are pure `no_std` computations over borrowed slices -- no allocation, +//! no unsafe outside the thin FFI shims. + +/// CRC-32C (Castagnoli) using the reflected polynomial 0x82F63B78. Computed +/// bit-by-bit so no lookup table has to live in the kernel image. +pub fn crc32c(data: &[u8]) -> u32 { + let mut crc: u32 = 0xFFFF_FFFF; + for &byte in data { + crc ^= byte as u32; + for _ in 0..8 { + let mask = (crc & 1).wrapping_neg(); // 0xFFFFFFFF if LSB set, else 0 + crc = (crc >> 1) ^ (0x82F6_3B78 & mask); + } + } + !crc +} + +/// Adler-32 (zlib): two running 16-bit sums modulo 65521. +pub fn adler32(data: &[u8]) -> u32 { + const MOD: u32 = 65521; + let mut a: u32 = 1; + let mut b: u32 = 0; + for &byte in data { + a = (a + byte as u32) % MOD; + b = (b + a) % MOD; + } + (b << 16) | a +} + +/// 64-bit FNV-1a hash. Good spread and speed for hash-table keys. +pub fn fnv1a64(data: &[u8]) -> u64 { + const OFFSET: u64 = 0xcbf2_9ce4_8422_2325; + const PRIME: u64 = 0x0000_0100_0000_01B3; + let mut hash = OFFSET; + for &byte in data { + hash ^= byte as u64; + hash = hash.wrapping_mul(PRIME); + } + hash +} + +// --------------------------------------------------------------------------- +// C ABI shims +// --------------------------------------------------------------------------- + +#[no_mangle] +pub extern "C" fn rust_crc32c(data: *const u8, len: usize) -> u32 { + if data.is_null() { + return 0; + } + let bytes = unsafe { core::slice::from_raw_parts(data, len) }; + crc32c(bytes) +} + +#[no_mangle] +pub extern "C" fn rust_adler32(data: *const u8, len: usize) -> u32 { + if data.is_null() { + return 1; // Adler-32 of the empty string + } + let bytes = unsafe { core::slice::from_raw_parts(data, len) }; + adler32(bytes) +} + +#[no_mangle] +pub extern "C" fn rust_fnv1a64(data: *const u8, len: usize) -> u64 { + if data.is_null() { + return 0xcbf2_9ce4_8422_2325; + } + let bytes = unsafe { core::slice::from_raw_parts(data, len) }; + fnv1a64(bytes) +} diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 98edf95..3c9ca1a 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -8,6 +8,7 @@ pub mod printk; pub mod drivers; pub mod crypto; +pub mod checksum; pub mod fs; pub mod kernel; pub mod storage; @@ -42,6 +43,13 @@ pub extern "C" fn rust_kernel_init() -> i32 { // Ping Rust watchdog kernel::watchdog::rust_watchdog_ping(1); + // Self-test the checksum engine against a known vector ("SUB-OS"). + let crc = checksum::crc32c(b"SUB-OS"); + kprintln!( + "\x1b[32mRUST:\x1b[0m Checksum engine online (CRC-32C, Adler-32, FNV-1a) [crc32c(\"SUB-OS\")=0x{:08x}]", + crc + ); + 0 } diff --git a/userland/lazybox/lazybox.c b/userland/lazybox/lazybox.c index 9c22462..60b563d 100644 --- a/userland/lazybox/lazybox.c +++ b/userland/lazybox/lazybox.c @@ -463,6 +463,25 @@ static int applet_crc32(int argc, char** argv) { return 0; } +static int applet_cksum(int argc, char** argv) { + const char* str = (argc >= 2) ? argv[1] : ""; + size_t len = strlen(str); + const uint8_t* buf = (const uint8_t*)str; + + // All three come from the memory-safe Rust checksum engine + // (rust/src/checksum/mod.rs), with a C bridge on non-x86 targets. + uint32_t crc = rust_crc32c(buf, len); + uint32_t adl = rust_adler32(buf, len); + uint64_t fnv = rust_fnv1a64(buf, len); + + printk(ANSI_BRIGHT_CYAN "Rust checksum engine" ANSI_RESET " for \"%s\" (%llu bytes):\n", + str, (unsigned long long)len); + printk(" CRC-32C : 0x%08X\n", crc); + printk(" Adler-32 : 0x%08X\n", adl); + printk(" FNV-1a-64 : 0x%016llX\n", (unsigned long long)fnv); + return 0; +} + static int applet_rand(int argc, char** argv) { int count = (argc >= 2) ? atoi(argv[1]) : 4; printk("Random Integers: "); @@ -2170,6 +2189,7 @@ static const lazybox_applet_t applets[] = { {"md5sum", applet_md5sum, "md5sum ", "Compute MD5 hash", "Crypto"}, {"sha256sum", applet_sha256sum, "sha256sum ", "Compute SHA-256 hash", "Crypto"}, {"crc32", applet_crc32, "crc32 ", "Calculate CRC32 checksum", "Crypto"}, + {"cksum", applet_cksum, "cksum ", "Rust CRC-32C/Adler-32/FNV", "Crypto"}, {"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"},