mirror of
https://github.com/subhobhai943/SUB-OS.git
synced 2026-10-25 16:52:29 +00:00
feat(rust,gui): Rust checksum engine + Game of Life desktop app
Rust kernel expansion:
- rust/src/checksum/mod.rs (new): memory-safe CRC-32C (Castagnoli), Adler-32,
and FNV-1a-64 with thin C-ABI shims. Registered in lib.rs with a boot-time
self-test (crc32c("SUB-OS")=0x61e96ce9, verified against a reference impl).
- include/kernel/rust.h: FFI declarations; rust/rust_fallback.c: matching C
fallbacks so aarch64/armv8i (no native rustc target) get identical results.
- LazyBox: new `cksum <text>` applet printing all three via the Rust engine.
GUI expansion:
- gui/gui_apps.c: Conway's Game of Life -- an interactive B3/S23 cellular
automaton with Play/Pause/Step/Random/Clear toolbar, click-to-toggle cells,
a live generation/population readout, and a board that scales with the
window. The Random button seeds from the Rust ChaCha20 CSPRNG. Toolbar and
cell input are gated on the MOUSE_DOWN edge so they do not re-fire while held.
- Registered "Life" (flask icon) in the desktop app table and on the desktop.
Verified: clean build on x86_64/aarch64/armv8i; boots through the SUB login to
the desktop (screenshotted) with the checksum self-test and 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
c982d6b538
commit
e93b994469
8 files changed
+341
-1
No files matched your search
+192
@@ -8,6 +8,7 @@
|
||||
#include <mm/kmalloc.h>
|
||||
#include <mm/pmm.h>
|
||||
#include <kernel/printk.h>
|
||||
#include <kernel/rust.h>
|
||||
#include <lib/string.h>
|
||||
#include <lib/printf.h>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
+3
-1
@@ -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;
|
||||
|
||||
@@ -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
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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 <file|text>", "Compute MD5 hash", "Crypto"},
|
||||
{"sha256sum", applet_sha256sum, "sha256sum <file|text>", "Compute SHA-256 hash", "Crypto"},
|
||||
{"crc32", applet_crc32, "crc32 <text>", "Calculate CRC32 checksum", "Crypto"},
|
||||
{"cksum", applet_cksum, "cksum <text>", "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"},
|
||||
|
||||
Reference in new issue
Block a user