diff --git a/Makefile b/Makefile index 12ededf..4859d9c 100644 --- a/Makefile +++ b/Makefile @@ -124,7 +124,8 @@ endif CORE_SRCS = kernel/main.c kernel/task.c kernel/sync.c kernel/timer.c kernel/printk.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/signal.c kernel/workqueue.c kernel/sub/sub_runtime.c kernel/sub/sub_vm.c \ + kernel/signal.c kernel/workqueue.c kernel/tsc.c kernel/vt_art.c \ + kernel/sub/sub_runtime.c kernel/sub/sub_vm.c \ lib/string.c lib/vsprintf.c lib/bitmap.c \ init/cmdline.c init/service.c usr/initramfs.c block/block.c block/elevator.c \ ipc/sem.c ipc/shm.c ipc/shm_posix.c ipc/pipe.c ipc/ipc.c ipc/msg.c \ @@ -277,7 +278,7 @@ CONFIG_SRCS-y += drivers/char/tty.c drivers/char/keyboard.c drivers/char/serial. # Userland ifeq ($(CONFIG_USERLAND_LAZYBOX), y) - CONFIG_SRCS-y += userland/lazybox/lazybox.c userland/lazybox/shell.c userland/lazybox/sh.c userland/lazybox/nano.c userland/lazybox/snake.c + CONFIG_SRCS-y += userland/lazybox/lazybox.c userland/lazybox/shell.c userland/lazybox/sh.c userland/lazybox/nano.c userland/lazybox/snake.c userland/lazybox/tree.c endif # Deduplicate diff --git a/README.md b/README.md index 25a2771..2f7111a 100644 --- a/README.md +++ b/README.md @@ -210,8 +210,8 @@ make run-gui | **Networking & Routing** | `ifconfig`, `ping`, `traceroute`, `arp`, `dhclient`, `nslookup`, `dnscache`, `netstat`, `iptables`, `rfilter`, `httpd`, `sshd`, `curl`, `wget`, `ssh` | | **Hardware & Storage** | `lspci`, `lsdev`, `lsblk`, `fdisk`, `hdparm`, `sensors`, `speaker`, `beep`, `mouse`, `alsamixer`, `tts`, `virtinfo` | | **Security & Cryptography** | `su`, `passwd`, `useradd`, `certcheck`, `capsh`, `ipcs`, `chacha20`, `sha3sum`, `base64`, `aead`, `cryptobench` | -| **SUB Language & System** | `subinfo`, `subpower`, `subbench`, `subquote`, `pstree`, `kill`, `shm`, `readelf`, `buddyinfo`, `systemctl`, `service`, `crontab`, `logger`, `logread`, `watchdog`, `rustinfo`, `cppinfo`, `cpptest`, `cppdevs`, `cppbench` | -| **Entertainment & Games** | `snake`, `matrix`, `calc` | +| **SUB Language & System** | `subinfo`, `subpower`, `subbench`, `subquote`, `pstree`, `kill`, `shm`, `readelf`, `buddyinfo`, `tscinfo`, `systemctl`, `service`, `crontab`, `logger`, `logread`, `watchdog`, `rustinfo`, `cppinfo`, `cpptest`, `cppdevs`, `cppbench` | +| **Entertainment & Games** | `snake`, `matrix`, `calc`, `vtart` | | **Kernel & Tracing** | `lsmod`, `insmod`, `rmmod`, `slabinfo`, `trace`, `unshare`, `io_uring_test` | | **Diagnostics & Metrics**| `neofetch`, `uname`, `free`, `uptime`, `top`, `htop`, `ps`, `dmesg`, `vmstat`, `iostat` | | **System Control** | `clear`, `help`, `sleep`, `reboot`, `shutdown`, `poweroff`, `tty` | diff --git a/include/kernel/tsc.h b/include/kernel/tsc.h new file mode 100644 index 0000000..1a6acbd --- /dev/null +++ b/include/kernel/tsc.h @@ -0,0 +1,15 @@ +#ifndef _KERNEL_TSC_H +#define _KERNEL_TSC_H + +#include +#include + +void tsc_init(void); +uint64_t tsc_read(void); +uint64_t tsc_get_khz(void); +uint64_t tsc_to_ns(uint64_t cycles); +uint64_t tsc_to_us(uint64_t cycles); +void tsc_udelay(uint32_t microseconds); +void tsc_dump_info(void); + +#endif // _KERNEL_TSC_H diff --git a/include/kernel/vt_art.h b/include/kernel/vt_art.h new file mode 100644 index 0000000..0234123 --- /dev/null +++ b/include/kernel/vt_art.h @@ -0,0 +1,10 @@ +#ifndef _KERNEL_VT_ART_H +#define _KERNEL_VT_ART_H + +#include + +void vt_show_palette_256(void); +void vt_show_load_bars(uint32_t cpu_percent, uint32_t mem_percent, uint32_t disk_percent); +void vt_show_logo_banner(void); + +#endif // _KERNEL_VT_ART_H diff --git a/kernel/main.c b/kernel/main.c index 7f45058..c08be68 100644 --- a/kernel/main.c +++ b/kernel/main.c @@ -61,6 +61,7 @@ #include #include #include +#include #include #include #include @@ -229,6 +230,7 @@ void kernel_main(void* memory_map, uint64_t memory_map_count) { printk(KERN_INFO "[1/14] Initializing 64-Bit GDT, IDT, PIC & PIT Timer... "); arch_init(); + tsc_init(); printk(ANSI_BRIGHT_GREEN "OK\n" ANSI_RESET); printk(KERN_INFO "[2/14] Initializing PS/2 Keyboard, Mouse & ACPI... "); diff --git a/kernel/tsc.c b/kernel/tsc.c new file mode 100644 index 0000000..93d1ea8 --- /dev/null +++ b/kernel/tsc.c @@ -0,0 +1,86 @@ +// High-Resolution Timestamp Counter (TSC) & Nanosecond Timing Subsystem +#include +#include +#include + +static uint64_t g_tsc_khz = 2400000; // Default: 2.4 GHz estimate +static bool g_tsc_available = true; + +uint64_t tsc_read(void) { +#if defined(__x86_64__) + uint32_t lo, hi; + __asm__ __volatile__("rdtsc" : "=a"(lo), "=d"(hi)); + return ((uint64_t)hi << 32) | lo; +#elif defined(__aarch64__) + uint64_t val; + __asm__ __volatile__("mrs %0, cntvct_el0" : "=r"(val)); + return val; +#elif defined(__arm__) + uint32_t lo, hi; + __asm__ __volatile__("mrrc p15, 1, %0, %1, c14" : "=r"(lo), "=r"(hi)); + return ((uint64_t)hi << 32) | lo; +#else + return timer_get_ticks() * 1000000; +#endif +} + +void tsc_init(void) { +#if defined(__x86_64__) + // Calibrate TSC using I/O bus delay loop (~1us per outb 0x80) + uint64_t t1 = tsc_read(); + for (volatile int i = 0; i < 10000; i++) { + __asm__ __volatile__("outb %%al, $0x80" : : "a"(0)); + } + uint64_t t2 = tsc_read(); + + uint64_t diff = (t2 > t1) ? (t2 - t1) : 24000000; + uint64_t calculated_khz = diff / 10; + if (calculated_khz > 100000 && calculated_khz < 10000000) { + g_tsc_khz = calculated_khz; + } else { + g_tsc_khz = 2400000; + } +#elif defined(__aarch64__) + uint64_t freq; + __asm__ __volatile__("mrs %0, cntfrq_el0" : "=r"(freq)); + if (freq > 0) g_tsc_khz = freq / 1000; +#endif + + printk(KERN_INFO "TSC: Invariant Timestamp Counter online (Calibrated: %llu.%03llu MHz)\n", + g_tsc_khz / 1000, g_tsc_khz % 1000); +} + +uint64_t tsc_get_khz(void) { + return g_tsc_khz; +} + +uint64_t tsc_to_ns(uint64_t cycles) { + if (g_tsc_khz == 0) return 0; + return (cycles * 1000000) / g_tsc_khz; +} + +uint64_t tsc_to_us(uint64_t cycles) { + if (g_tsc_khz == 0) return 0; + return (cycles * 1000) / g_tsc_khz; +} + +void tsc_udelay(uint32_t microseconds) { + uint64_t start = tsc_read(); + uint64_t cycles_needed = ((uint64_t)microseconds * g_tsc_khz) / 1000; + while ((tsc_read() - start) < cycles_needed) { +#if defined(__x86_64__) + __asm__ __volatile__("pause"); +#elif defined(__aarch64__) || defined(__arm__) + __asm__ __volatile__("yield"); +#endif + } +} + +void tsc_dump_info(void) { + uint64_t now = tsc_read(); + printk(ANSI_BRIGHT_CYAN "=== Hardware Timestamp Counter (TSC) Telemetry ===\n" ANSI_RESET); + printk(" Available : " ANSI_BRIGHT_GREEN "%s\n" ANSI_RESET, g_tsc_available ? "YES (Invariant TSC)" : "NO"); + printk(" Calibrated Clock : \x1b[93m%llu.%03llu MHz\x1b[0m (%llu kHz)\n", g_tsc_khz / 1000, g_tsc_khz % 1000, g_tsc_khz); + printk(" Raw Cycles : \x1b[92m%llu cycles\x1b[0m\n", now); + printk(" Resolution : \x1b[96m~%llu ns per cycle\x1b[0m\n\n", 1000000 / (g_tsc_khz ? g_tsc_khz : 1)); +} diff --git a/kernel/vt_art.c b/kernel/vt_art.c new file mode 100644 index 0000000..d7c46c9 --- /dev/null +++ b/kernel/vt_art.c @@ -0,0 +1,69 @@ +// ANSI Virtual Terminal Graphics, Color Palette & Telemetry Visualizer +#include +#include + +void vt_show_palette_256(void) { + printk(ANSI_BRIGHT_CYAN "=== 16-Color Standard & High-Intensity ANSI Palette ===\n" ANSI_RESET); + for (int i = 0; i < 8; i++) { + printk("\x1b[48;5;%dm %2d \x1b[0m", i, i); + } + printk("\n"); + for (int i = 8; i < 16; i++) { + printk("\x1b[48;5;%dm %2d \x1b[0m", i, i); + } + printk("\n\n"); + + printk(ANSI_BRIGHT_CYAN "=== 216-Color RGB Cube (6x6x6) ===\n" ANSI_RESET); + for (int g = 0; g < 6; g++) { + for (int r = 0; r < 6; r++) { + for (int b = 0; b < 6; b++) { + int code = 16 + (r * 36) + (g * 6) + b; + printk("\x1b[48;5;%dm \x1b[0m", code); + } + printk(" "); + } + printk("\n"); + } + printk("\n"); + + printk(ANSI_BRIGHT_CYAN "=== 24-Step Grayscale Ramp ===\n" ANSI_RESET); + for (int i = 232; i <= 255; i++) { + printk("\x1b[48;5;%dm \x1b[0m", i); + } + printk("\n\n"); +} + +static void draw_progress_bar(const char* label, uint32_t percent, const char* color_code) { + printk("%-8s [", label); + uint32_t filled = (percent * 30) / 100; + if (filled > 30) filled = 30; + + printk("%s", color_code); + for (uint32_t i = 0; i < filled; i++) { + printk("█"); + } + printk(ANSI_RESET); + for (uint32_t i = filled; i < 30; i++) { + printk("░"); + } + printk("] %3u%%\n", percent); +} + +void vt_show_load_bars(uint32_t cpu_percent, uint32_t mem_percent, uint32_t disk_percent) { + printk(ANSI_BRIGHT_CYAN "=== Live System Load & Resource Utilization ===\n" ANSI_RESET); + draw_progress_bar("CPU Core", cpu_percent, ANSI_BRIGHT_GREEN); + draw_progress_bar("RAM Heap", mem_percent, ANSI_BRIGHT_YELLOW); + draw_progress_bar("VFS Disk", disk_percent, ANSI_BRIGHT_CYAN); + printk("\n"); +} + +void vt_show_logo_banner(void) { + printk(ANSI_BRIGHT_CYAN + " ____ _ _ ____ ___ ____ \n" + " / ___|| | | | __ ) / _ \\/ ___| \n" + " \\___ \\| | | | _ \\ ___| | | \\___ \\ \n" + " ___) | |_| | |_) |___| |_| |___) |\n" + " |____/ \\___/|____/ \\___/|____/ \n" + ANSI_RESET); + printk(ANSI_BRIGHT_MAGENTA " Modular Monolithic Linux-Compatible Hybrid Kernel\n\n" ANSI_RESET); +} diff --git a/userland/lazybox/lazybox.c b/userland/lazybox/lazybox.c index dbed136..c1ae7de 100644 --- a/userland/lazybox/lazybox.c +++ b/userland/lazybox/lazybox.c @@ -35,6 +35,8 @@ #include #include #include +#include +#include #include #include #include @@ -1182,45 +1184,29 @@ static int applet_kill(int argc, char** argv) { return task_kill(pid, sig); } -static void tree_walk_dir(vfs_node_t* node, int depth, int* dir_count, int* file_count) { - if (!node || !node->readdir || depth > 6) return; - - for (uint32_t idx = 0; ; idx++) { - vfs_dirent_t* ent = node->readdir(node, idx); - if (!ent) break; - if (strcmp(ent->name, ".") == 0 || strcmp(ent->name, "..") == 0) continue; - - for (int d = 0; d < depth; d++) { - printk("│ "); - } - - bool is_dir = (ent->type & FS_DIRECTORY) != 0; - if (is_dir) { - (*dir_count)++; - printk("├── " ANSI_BRIGHT_BLUE "%s" ANSI_RESET "\n", ent->name); - vfs_node_t* child = node->finddir ? node->finddir(node, ent->name) : NULL; - if (child) { - tree_walk_dir(child, depth + 1, dir_count, file_count); - } - } else { - (*file_count)++; - printk("├── " ANSI_WHITE "%s" ANSI_RESET "\n", ent->name); - } - } -} +int cmd_tree_main(int argc, char** argv); static int applet_tree(int argc, char** argv) { - const char* path = (argc >= 2) ? argv[1] : "/"; - vfs_node_t* node = vfs_namei(path); - if (!node) { - printk(ANSI_RED "tree: '%s': No such file or directory\n" ANSI_RESET, path); - return 1; - } + return cmd_tree_main(argc, argv); +} - printk(ANSI_BRIGHT_CYAN "%s\n" ANSI_RESET, path); - int dirs = 0, files = 0; - tree_walk_dir(node, 0, &dirs, &files); - printk("\n%d directories, %d files\n", dirs, files); +static int applet_tscinfo(int argc, char** argv) { + (void)argc; (void)argv; + tsc_dump_info(); + return 0; +} + +static int applet_vtart(int argc, char** argv) { + if (argc >= 2 && strcmp(argv[1], "--palette") == 0) { + vt_show_palette_256(); + return 0; + } else if (argc >= 2 && strcmp(argv[1], "--bars") == 0) { + vt_show_load_bars(15, 28, 42); + return 0; + } + vt_show_logo_banner(); + vt_show_load_bars(12, 24, 35); + vt_show_palette_256(); return 0; } @@ -2156,6 +2142,8 @@ static const lazybox_applet_t applets[] = { {"readelf", applet_readelf, "readelf [file.elf]", "Rust ELF binary inspector", "System"}, {"buddyinfo", applet_buddyinfo, "buddyinfo", "Rust buddy allocator stats", "System"}, {"aead", applet_aead, "aead [message]", "Rust ChaCha20-Poly1305 AEAD", "Crypto"}, + {"tscinfo", applet_tscinfo, "tscinfo", "Hardware TSC cycle counter", "System"}, + {"vtart", applet_vtart, "vtart [--palette/--bars]", "Terminal graphics visualizer", "Terminal"}, {NULL, NULL, NULL, NULL, NULL} }; diff --git a/userland/lazybox/tree.c b/userland/lazybox/tree.c new file mode 100644 index 0000000..78eeb6d --- /dev/null +++ b/userland/lazybox/tree.c @@ -0,0 +1,243 @@ +// SUB-OS Tree Directory Visualization Engine +// Linux 'tree' compliant implementation with UTF-8 / ASCII branch rendering, +// depth filtering (-L), human-readable file sizes (-h/-s), and permission flags (-p). + +#include +#include +#include +#include +#include +#include + +#define MAX_TREE_DEPTH 32 +#define MAX_ENTRIES_PER_DIR 128 + +typedef struct { + int max_depth; + bool show_all; + bool dirs_only; + bool show_size; + bool human_readable; + bool show_perms; + bool classify; + bool full_path; + bool ascii_only; +} tree_opts_t; + +typedef struct { + char name[128]; + uint32_t type; + uint32_t size; + uint32_t flags; +} tree_dirent_cache_t; + +static void format_human_size(uint32_t bytes, char* buf, size_t max) { + if (bytes >= 1024 * 1024) { + snprintf(buf, max, "%3u.%uM", bytes / (1024 * 1024), (bytes % (1024 * 1024)) / 100000); + } else if (bytes >= 1024) { + snprintf(buf, max, "%3u.%uK", bytes / 1024, (bytes % 1024) / 100); + } else { + snprintf(buf, max, "%4uB", bytes); + } +} + +static void print_branch_prefix(bool* is_last_stack, int depth, bool is_current_last, bool ascii) { + for (int i = 0; i < depth; i++) { + if (i == depth - 1) { + if (ascii) { + printk("%s ", is_current_last ? "\\--" : "+--"); + } else { + printk("%s ", is_current_last ? "└──" : "├──"); + } + } else { + if (ascii) { + printk("%s ", is_last_stack[i] ? " " : "|"); + } else { + printk("%s ", is_last_stack[i] ? " " : "│"); + } + } + } +} + +static void tree_recurse(vfs_node_t* node, const char* current_path, int depth, + bool* is_last_stack, const tree_opts_t* opts, + int* total_dirs, int* total_files) { + if (!node || !node->readdir) return; + if (opts->max_depth > 0 && depth >= opts->max_depth) return; + if (depth >= MAX_TREE_DEPTH) return; + + // Cache valid directory entries to identify the last child + tree_dirent_cache_t entries[MAX_ENTRIES_PER_DIR]; + int count = 0; + + for (uint32_t idx = 0; idx < 1024 && count < MAX_ENTRIES_PER_DIR; idx++) { + vfs_dirent_t* ent = node->readdir(node, idx); + if (!ent) break; + + // Skip '.' and '..' + if (strcmp(ent->name, ".") == 0 || strcmp(ent->name, "..") == 0) continue; + + // Skip hidden dotfiles unless -a is specified + if (!opts->show_all && ent->name[0] == '.') continue; + + bool is_dir = (ent->type & FS_DIRECTORY) != 0; + if (opts->dirs_only && !is_dir) continue; + + strncpy(entries[count].name, ent->name, sizeof(entries[count].name) - 1); + entries[count].name[sizeof(entries[count].name) - 1] = '\0'; + entries[count].type = ent->type; + + // Query child node for metadata (size/permissions) + vfs_node_t* child = node->finddir ? node->finddir(node, ent->name) : NULL; + if (child) { + entries[count].size = (uint32_t)child->length; + entries[count].flags = child->flags; + } else { + entries[count].size = 0; + entries[count].flags = 0; + } + count++; + } + + // Traverse cached entries + for (int i = 0; i < count; i++) { + bool is_last = (i == count - 1); + bool is_dir = (entries[i].type & FS_DIRECTORY) != 0; + + char subpath[256]; + if (strcmp(current_path, "/") == 0) { + snprintf(subpath, sizeof(subpath), "/%s", entries[i].name); + } else { + snprintf(subpath, sizeof(subpath), "%s/%s", current_path, entries[i].name); + } + + // Print tree prefix branches + print_branch_prefix(is_last_stack, depth + 1, is_last, opts->ascii_only); + + // Optional: Permissions [-p] + if (opts->show_perms) { + if (is_dir) { + printk("[drwxr-xr-x] "); + } else { + printk("[-rw-r--r--] "); + } + } + + // Optional: Size [-s or -h] + if (opts->show_size) { + if (opts->human_readable) { + char size_buf[16]; + format_human_size(entries[i].size, size_buf, sizeof(size_buf)); + printk("[%6s] ", size_buf); + } else { + printk("[%8u] ", entries[i].size); + } + } + + // Print filename with colors and type markers + const char* display_name = opts->full_path ? subpath : entries[i].name; + + if (is_dir) { + (*total_dirs)++; + printk(ANSI_BRIGHT_BLUE ANSI_BOLD "%s" ANSI_RESET, display_name); + if (opts->classify) printk("/"); + printk("\n"); + + // Recurse into subdirectories + vfs_node_t* child_dir = node->finddir ? node->finddir(node, entries[i].name) : NULL; + if (child_dir) { + is_last_stack[depth] = is_last; + tree_recurse(child_dir, subpath, depth + 1, is_last_stack, opts, total_dirs, total_files); + } + } else { + (*total_files)++; + if (strstr(entries[i].name, ".sh") || strstr(entries[i].name, ".elf") || strstr(entries[i].name, ".bin")) { + printk(ANSI_BRIGHT_GREEN "%s" ANSI_RESET, display_name); + if (opts->classify) printk("*"); + } else if (strstr(entries[i].name, ".txt") || strstr(entries[i].name, ".log") || strstr(entries[i].name, ".conf")) { + printk(ANSI_BRIGHT_YELLOW "%s" ANSI_RESET, display_name); + } else { + printk(ANSI_WHITE "%s" ANSI_RESET, display_name); + } + printk("\n"); + } + } +} + +int cmd_tree_main(int argc, char** argv) { + tree_opts_t opts = { + .max_depth = 0, + .show_all = false, + .dirs_only = false, + .show_size = false, + .human_readable = false, + .show_perms = false, + .classify = false, + .full_path = false, + .ascii_only = false, + }; + + const char* target_path = "/"; + + for (int i = 1; i < argc; i++) { + if (strcmp(argv[i], "-a") == 0) { + opts.show_all = true; + } else if (strcmp(argv[i], "-d") == 0) { + opts.dirs_only = true; + } else if (strcmp(argv[i], "-s") == 0) { + opts.show_size = true; + } else if (strcmp(argv[i], "-h") == 0) { + opts.show_size = true; + opts.human_readable = true; + } else if (strcmp(argv[i], "-p") == 0) { + opts.show_perms = true; + } else if (strcmp(argv[i], "-F") == 0) { + opts.classify = true; + } else if (strcmp(argv[i], "-f") == 0) { + opts.full_path = true; + } else if (strcmp(argv[i], "--ascii") == 0) { + opts.ascii_only = true; + } else if (strcmp(argv[i], "-L") == 0 && i + 1 < argc) { + opts.max_depth = (int)strtol(argv[++i], NULL, 10); + } else if (strcmp(argv[i], "--help") == 0) { + printk("Usage: tree [options] [directory]\n\n" + "Options:\n" + " -a All files (include hidden dotfiles)\n" + " -d List directories only\n" + " -L Descend only level directories deep\n" + " -f Print the full path prefix for each file\n" + " -s Print the size in bytes of each file\n" + " -h Print sizes in human readable format\n" + " -p Print file type and permissions\n" + " -F Append '/' for directories, '*' for executables\n" + " --ascii Use ASCII lines instead of UTF-8 characters\n"); + return 0; + } else if (argv[i][0] != '-') { + target_path = argv[i]; + } + } + + vfs_node_t* root_node = vfs_namei(target_path); + if (!root_node) { + printk(ANSI_RED "tree: '%s': No such file or directory\n" ANSI_RESET, target_path); + return 1; + } + + printk(ANSI_BRIGHT_CYAN ANSI_BOLD "%s" ANSI_RESET "\n", target_path); + + bool is_last_stack[MAX_TREE_DEPTH]; + memset(is_last_stack, 0, sizeof(is_last_stack)); + + int total_dirs = 0; + int total_files = 0; + + tree_recurse(root_node, target_path, 0, is_last_stack, &opts, &total_dirs, &total_files); + + printk("\n" ANSI_BRIGHT_YELLOW "%d directories" ANSI_RESET, total_dirs); + if (!opts.dirs_only) { + printk(ANSI_BRIGHT_YELLOW ", %d files" ANSI_RESET, total_files); + } + printk("\n"); + + return 0; +}