FEATURE: add Bsh + userspace terminal, remove legacy cmd/cli utils

This commit is contained in:
boreddevnl
2026-04-15 22:47:24 +02:00
parent a8866da3cb
commit bdd43f43cd
34 changed files with 3099 additions and 2912 deletions

30
src/sys/cmd.h Normal file
View File

@@ -0,0 +1,30 @@
// Copyright (c) 2023-2026 Chris (boreddevnl)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#ifndef CMD_H
#define CMD_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
void cmd_init(void);
void cmd_reset(void);
void cmd_write(const char *str);
void cmd_write_len(const char *str, size_t len);
void cmd_putchar(char c);
void cmd_write_int(int n);
void cmd_write_hex(uint64_t n);
int cmd_get_cursor_col(void);
void cmd_screen_clear(void);
void cmd_increment_msg_count(void);
void cmd_reset_msg_count(void);
uint32_t cmd_get_config_value(const char *key);
void cmd_set_current_color(uint32_t color);
void cmd_set_raw_mode(bool enabled);
void cmd_process_finished(void);
#endif

76
src/sys/cmd_stub.c Normal file
View File

@@ -0,0 +1,76 @@
// Copyright (c) 2023-2026 Chris (boreddevnl)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#include "cmd.h"
#include "core/kutils.h"
extern void serial_write(const char *str);
static void serial_write_char(char c) {
char buf[2] = { c, 0 };
serial_write(buf);
}
void cmd_init(void) {
}
void cmd_reset(void) {
}
void cmd_write(const char *str) {
if (!str) return;
serial_write(str);
}
void cmd_write_len(const char *str, size_t len) {
if (!str || len == 0) return;
for (size_t i = 0; i < len; i++) {
serial_write_char(str[i]);
}
}
void cmd_putchar(char c) {
serial_write_char(c);
}
void cmd_write_int(int n) {
char buf[32];
k_itoa(n, buf);
cmd_write(buf);
}
void cmd_write_hex(uint64_t n) {
char buf[17];
k_itoa_hex(n, buf);
cmd_write("0x");
cmd_write(buf);
}
int cmd_get_cursor_col(void) {
return 0;
}
void cmd_screen_clear(void) {
}
void cmd_increment_msg_count(void) {
}
void cmd_reset_msg_count(void) {
}
uint32_t cmd_get_config_value(const char *key) {
(void)key;
return 0;
}
void cmd_set_current_color(uint32_t color) {
(void)color;
}
void cmd_set_raw_mode(bool enabled) {
(void)enabled;
}
void cmd_process_finished(void) {
}

View File

@@ -27,23 +27,24 @@ static uint64_t free_pml4_later[MAX_CPUS_SCHED] = {0};
static spinlock_t runqueue_lock = SPINLOCK_INIT;
static uint32_t next_cpu_assign = 1;
static void process_cleanup_inner(process_t *proc);
void process_init(void) {
for (int i = 0; i < MAX_PROCESSES; i++) {
processes[i].pid = 0xFFFFFFFF;
}
// Current kernel execution is PID 0
process_t *kernel_proc = &processes[0];
kernel_proc->pid = next_pid++;
kernel_proc->is_user = false;
kernel_proc->is_idle = true;
kernel_proc->tty_id = -1;
kernel_proc->kill_pending = false;
// We don't have its RSP or PML4 yet, but it's already running.
// The timer interrupt will naturally capture its context on the first tick!
kernel_proc->pml4_phys = paging_get_pml4_phys();
kernel_proc->kernel_stack = 0;
// Initialize FPU/SSE state for kernel (first interrupt will capture it on stack)
kernel_proc->fpu_initialized = true;
for (int i = 0; i < MAX_PROCESS_FDS; i++) kernel_proc->fds[i] = NULL;
@@ -78,11 +79,14 @@ process_t* process_create(void (*entry_point)(void), bool is_user) {
new_proc->pid = next_pid++;
new_proc->is_user = is_user;
new_proc->tty_id = -1;
new_proc->kill_pending = false;
process_t *parent = process_get_current();
if (parent) {
extern void mem_memcpy(void *dest, const void *src, size_t len);
mem_memcpy(new_proc->cwd, parent->cwd, 1024);
new_proc->tty_id = parent->tty_id;
} else {
mem_memset(new_proc->cwd, 0, 1024);
new_proc->cwd[0] = '/';
@@ -207,6 +211,18 @@ process_t* process_create_elf(const char* filepath, const char* args_str) {
new_proc->heap_start = 0x20000000; // 512MB mark
new_proc->heap_end = 0x20000000;
new_proc->is_terminal_proc = false;
new_proc->tty_id = -1;
new_proc->kill_pending = false;
process_t *parent = process_get_current();
if (parent) {
extern void mem_memcpy(void *dest, const void *src, size_t len);
mem_memcpy(new_proc->cwd, parent->cwd, 1024);
} else {
extern void mem_memset(void *dest, int val, size_t len);
mem_memset(new_proc->cwd, 0, 1024);
new_proc->cwd[0] = '/';
}
// 2. Load ELF executable
size_t elf_load_size = 0;
@@ -426,6 +442,69 @@ uint64_t process_schedule(uint64_t current_rsp) {
// Save context
cur->rsp = current_rsp;
if (cur->kill_pending && cur->pid != 0xFFFFFFFF && cur->pid != 0) {
process_cleanup_inner(cur);
process_t *prev = cur;
while (prev->next != cur) {
prev = prev->next;
}
if (prev != cur) {
prev->next = cur->next;
process_t *next_proc = cur->next;
while (next_proc != cur) {
if (next_proc->cpu_affinity == my_cpu && next_proc->pid != 0xFFFFFFFF && !next_proc->kill_pending) break;
next_proc = next_proc->next;
}
if (next_proc == cur || next_proc->cpu_affinity != my_cpu) {
for (int i = 0; i < MAX_PROCESSES; i++) {
if (processes[i].pid == 0 || (processes[i].cpu_affinity == my_cpu && processes[i].is_user == false)) {
next_proc = &processes[i];
break;
}
}
}
current_process[my_cpu] = next_proc;
cur->pid = 0xFFFFFFFF;
cur->cpu_affinity = 0xFFFFFFFF;
cur->ui_window = NULL;
cur->is_terminal_proc = false;
cur->kill_pending = false;
free_kernel_stack_later[my_cpu] = cur->kernel_stack_alloc;
cur->kernel_stack_alloc = NULL;
free_pml4_later[my_cpu] = cur->pml4_phys;
cur->pml4_phys = 0;
if (current_process[my_cpu]->is_user && current_process[my_cpu]->kernel_stack) {
tss_set_stack_cpu(my_cpu, current_process[my_cpu]->kernel_stack);
cpu_state_t *cpu_state = smp_get_cpu(my_cpu);
if (cpu_state) {
cpu_state->kernel_syscall_stack = current_process[my_cpu]->kernel_stack;
}
}
paging_switch_directory(current_process[my_cpu]->pml4_phys);
current_process[my_cpu]->ticks++;
uint64_t next_rsp = current_process[my_cpu]->rsp;
spinlock_release_irqrestore(&runqueue_lock, rflags);
if (cleanup_stack) kfree(cleanup_stack);
if (cleanup_pml4) {
extern void paging_destroy_user_pml4_phys(uint64_t pml4_phys);
paging_destroy_user_pml4_phys(cleanup_pml4);
}
return next_rsp;
}
}
// Switch to next ready process assigned to this CPU
extern uint32_t wm_get_ticks(void);
uint32_t now = wm_get_ticks();
@@ -435,7 +514,7 @@ uint64_t process_schedule(uint64_t current_rsp) {
while (next_proc != start) {
// Only consider processes assigned to our CPU and not terminated
if (next_proc->cpu_affinity == my_cpu && next_proc->pid != 0xFFFFFFFF) {
if (next_proc->cpu_affinity == my_cpu && next_proc->pid != 0xFFFFFFFF && !next_proc->kill_pending) {
if (next_proc->pid == 0 || next_proc->sleep_until == 0 || next_proc->sleep_until <= now) {
break;
}
@@ -497,12 +576,20 @@ process_t* process_get_by_pid(uint32_t pid) {
return NULL;
}
void process_kill_by_tty(int tty_id) {
if (tty_id < 0) return;
for (int i = 0; i < MAX_PROCESSES; i++) {
if (processes[i].pid != 0xFFFFFFFF && processes[i].pid != 0 && processes[i].tty_id == tty_id) {
process_terminate(&processes[i]);
}
}
}
static void process_cleanup_inner(process_t *proc) {
if (!proc || proc->pid == 0xFFFFFFFF) return;
// 1. Cleanup side effects
extern Window win_cmd;
if (proc->ui_window && (proc->ui_window != &win_cmd)) {
if (proc->ui_window) {
wm_remove_window((Window *)proc->ui_window);
proc->ui_window = NULL;
}
@@ -529,6 +616,14 @@ static void process_cleanup_inner(process_t *proc) {
void process_terminate(process_t *to_delete) {
if (!to_delete || to_delete->pid == 0xFFFFFFFF || to_delete->pid == 0) return;
uint32_t cpu_count = smp_cpu_count();
for (uint32_t c = 0; c < cpu_count && c < MAX_CPUS_SCHED; c++) {
if (current_process[c] == to_delete) {
to_delete->kill_pending = true;
return;
}
}
uint64_t rflags = spinlock_acquire_irqsave(&runqueue_lock);
process_cleanup_inner(to_delete);
@@ -549,7 +644,6 @@ void process_terminate(process_t *to_delete) {
prev->next = to_delete->next;
// Update per-CPU current_process if this was the current on any CPU
uint32_t cpu_count = smp_cpu_count();
for (uint32_t c = 0; c < cpu_count && c < MAX_CPUS_SCHED; c++) {
if (current_process[c] == to_delete) {
process_t *np = to_delete->next;
@@ -571,6 +665,7 @@ void process_terminate(process_t *to_delete) {
// Mark slot as free
to_delete->pid = 0xFFFFFFFF;
to_delete->cpu_affinity = 0xFFFFFFFF;
to_delete->kill_pending = false;
if (to_delete->user_stack_alloc) kfree(to_delete->user_stack_alloc);
// Defer kernel stack until we switch away from it
@@ -639,6 +734,7 @@ uint64_t process_terminate_current(void) {
to_delete->cpu_affinity = 0xFFFFFFFF;
to_delete->ui_window = NULL;
to_delete->is_terminal_proc = false;
to_delete->kill_pending = false;
// 4. Load context for the NEXT process
if (current_process[my_cpu]->is_user && current_process[my_cpu]->kernel_stack) {

View File

@@ -43,6 +43,8 @@ typedef struct process {
void *user_stack_alloc;
bool is_terminal_proc;
int tty_id;
bool kill_pending;
struct process *next;
@@ -75,6 +77,7 @@ uint64_t process_schedule(uint64_t current_rsp);
uint64_t process_terminate_current(void);
void process_terminate(process_t *proc);
process_t* process_get_by_pid(uint32_t pid);
void process_kill_by_tty(int tty_id);
// SMP: IPI handler for AP scheduling
uint64_t sched_ipi_handler(registers_t *regs);

View File

@@ -19,11 +19,16 @@
#include "network.h"
#include "icmp.h"
#include "cmd.h"
#include "tty.h"
#include "font_manager.h"
#include "graphics.h"
extern bool ps2_ctrl_pressed;
#define SPAWN_FLAG_TERMINAL 0x1
#define SPAWN_FLAG_INHERIT_TTY 0x2
#define SPAWN_FLAG_TTY_ID 0x4
// Read MSR
static inline uint64_t rdmsr(uint32_t msr) {
uint32_t low, high;
@@ -180,6 +185,12 @@ static void user_window_resize(Window *win, int w, int h) {
extern void mem_memset(void *dest, int val, size_t len);
mem_memset(win->pixels, 0, w * h * sizeof(uint32_t));
}
process_t *proc = process_get_by_ui_window(win);
if (proc) {
gui_event_t ev = { .type = GUI_EVENT_RESIZE, .arg1 = w, .arg2 = h };
process_push_gui_event(proc, &ev);
}
}
@@ -197,9 +208,21 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
if (syscall_num == 1) { // SYS_WRITE
extern void cmd_write_len(const char *str, size_t len);
process_t *proc = process_get_current();
if (!proc || !proc->is_user || proc->is_terminal_proc) {
cmd_write_len((const char*)arg2, (size_t)arg3);
const char *buf = (const char*)arg2;
size_t len = (size_t)arg3;
if (!proc || !proc->is_user) {
cmd_write_len(buf, len);
return len;
}
if (proc->is_terminal_proc) {
if (proc->tty_id >= 0) {
tty_write_output(proc->tty_id, buf, len);
return len;
}
cmd_write_len(buf, len);
return len;
}
return len;
} else if (syscall_num == 3) { // SYS_GUI
int cmd = (int)arg1;
process_t *proc = process_get_current();
@@ -1254,6 +1277,35 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
return cmd_get_config_value(key);
} else if (cmd == 29) { // SYSTEM_CMD_SET_TEXT_COLOR
uint32_t color = (uint32_t)arg2;
if (proc->is_terminal_proc && proc->tty_id >= 0) {
char seq[32];
int pos = 0;
int r = (color >> 16) & 0xFF;
int g = (color >> 8) & 0xFF;
int b = color & 0xFF;
seq[pos++] = 0x1B;
seq[pos++] = '[';
seq[pos++] = '3';
seq[pos++] = '8';
seq[pos++] = ';';
seq[pos++] = '2';
seq[pos++] = ';';
char num[8];
k_itoa(r, num);
for (int i = 0; num[i] && pos < (int)sizeof(seq) - 1; i++) seq[pos++] = num[i];
seq[pos++] = ';';
k_itoa(g, num);
for (int i = 0; num[i] && pos < (int)sizeof(seq) - 1; i++) seq[pos++] = num[i];
seq[pos++] = ';';
k_itoa(b, num);
for (int i = 0; num[i] && pos < (int)sizeof(seq) - 1; i++) seq[pos++] = num[i];
seq[pos++] = 'm';
tty_write_output(proc->tty_id, seq, (size_t)pos);
return 0;
}
cmd_set_current_color(color);
return 0;
} else if (cmd == 31) { // SYSTEM_CMD_SET_WALLPAPER_PATH
@@ -1374,6 +1426,85 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
}
return -1;
return -1;
} else if (cmd == 60) { // SYSTEM_CMD_TTY_CREATE
return tty_create();
} else if (cmd == 61) { // SYSTEM_CMD_TTY_READ_OUT
int tty_id = (int)arg2;
char *buf = (char *)arg3;
size_t len = (size_t)arg4;
if (!buf || len == 0) return 0;
return tty_read_output(tty_id, buf, len);
} else if (cmd == 62) { // SYSTEM_CMD_TTY_WRITE_IN
int tty_id = (int)arg2;
const char *buf = (const char *)arg3;
size_t len = (size_t)arg4;
if (!buf || len == 0) return 0;
return tty_write_input(tty_id, buf, len);
} else if (cmd == 63) { // SYSTEM_CMD_TTY_READ_IN
char *buf = (char *)arg2;
size_t len = (size_t)arg3;
if (!buf || len == 0) return 0;
if (proc->tty_id < 0) return 0;
return tty_read_input(proc->tty_id, buf, len);
} else if (cmd == 65) { // SYSTEM_CMD_TTY_SET_FG
int tty_id = (int)arg2;
int pid = (int)arg3;
return tty_set_foreground(tty_id, pid);
} else if (cmd == 66) { // SYSTEM_CMD_TTY_GET_FG
int tty_id = (int)arg2;
return tty_get_foreground(tty_id);
} else if (cmd == 67) { // SYSTEM_CMD_TTY_KILL_FG
int tty_id = (int)arg2;
int pid = tty_get_foreground(tty_id);
if (pid <= 0) return 0;
process_t *target = process_get_by_pid((uint32_t)pid);
if (target) process_terminate(target);
tty_set_foreground(tty_id, 0);
return 0;
} else if (cmd == 68) {
int tty_id = (int)arg2;
process_kill_by_tty(tty_id);
tty_set_foreground(tty_id, 0);
return 0;
} else if (cmd == 69) {
int tty_id = (int)arg2;
return tty_destroy(tty_id);
} else if (cmd == 64) {
const char *user_path = (const char *)arg2;
const char *user_args = (const char *)arg3;
uint64_t flags = arg4;
int tty_id = (int)arg5;
if (!user_path) return -1;
char path_buf[256];
int pi = 0;
while (pi < 255 && user_path[pi]) {
path_buf[pi] = user_path[pi];
pi++;
}
path_buf[pi] = 0;
char args_buf[512];
const char *args_ptr = NULL;
if (user_args) {
int ai = 0;
while (ai < 511 && user_args[ai]) {
args_buf[ai] = user_args[ai];
ai++;
}
args_buf[ai] = 0;
args_ptr = args_buf;
}
process_t *child = process_create_elf(path_buf, args_ptr);
if (!child) return -1;
if (flags & SPAWN_FLAG_TERMINAL) child->is_terminal_proc = true;
if (flags & SPAWN_FLAG_TTY_ID) child->tty_id = tty_id;
else if (flags & SPAWN_FLAG_INHERIT_TTY) child->tty_id = proc->tty_id;
return (uint64_t)child->pid;
} else if (cmd == SYSTEM_CMD_PARALLEL_RUN) {
void (*user_fn)(void*) = (void (*)(void*))arg2;
void **args = (void **)arg3;

158
src/sys/tty.c Normal file
View File

@@ -0,0 +1,158 @@
// Copyright (c) 2023-2026 Chris (boreddevnl)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#include "tty.h"
#include "spinlock.h"
#include <stdbool.h>
#include <stdint.h>
#define TTY_MAX 8
#define TTY_OUT_SIZE 16384
#define TTY_IN_SIZE 4096
typedef struct {
bool used;
int id;
char out_buf[TTY_OUT_SIZE];
uint32_t out_head;
uint32_t out_tail;
char in_buf[TTY_IN_SIZE];
uint32_t in_head;
uint32_t in_tail;
int fg_pid;
spinlock_t lock;
} tty_t;
static tty_t ttys[TTY_MAX] = {0};
extern void mem_memset(void *dest, int val, size_t len);
static tty_t *tty_get(int tty_id) {
if (tty_id < 0 || tty_id >= TTY_MAX) return NULL;
if (!ttys[tty_id].used) return NULL;
return &ttys[tty_id];
}
int tty_create(void) {
for (int i = 0; i < TTY_MAX; i++) {
if (!ttys[i].used) {
ttys[i].used = true;
ttys[i].id = i;
ttys[i].out_head = 0;
ttys[i].out_tail = 0;
ttys[i].in_head = 0;
ttys[i].in_tail = 0;
ttys[i].fg_pid = -1;
ttys[i].lock = SPINLOCK_INIT;
mem_memset(ttys[i].out_buf, 0, sizeof(ttys[i].out_buf));
mem_memset(ttys[i].in_buf, 0, sizeof(ttys[i].in_buf));
return i;
}
}
return -1;
}
int tty_destroy(int tty_id) {
if (tty_id < 0 || tty_id >= TTY_MAX) return -1;
tty_t *tty = &ttys[tty_id];
uint64_t rflags = spinlock_acquire_irqsave(&tty->lock);
if (!tty->used) {
spinlock_release_irqrestore(&tty->lock, rflags);
return -1;
}
tty->used = false;
tty->id = -1;
tty->out_head = 0;
tty->out_tail = 0;
tty->in_head = 0;
tty->in_tail = 0;
tty->fg_pid = -1;
mem_memset(tty->out_buf, 0, sizeof(tty->out_buf));
mem_memset(tty->in_buf, 0, sizeof(tty->in_buf));
spinlock_release_irqrestore(&tty->lock, rflags);
return 0;
}
static int tty_write_ring(char *buf, uint32_t size, uint32_t *head, uint32_t *tail, const char *data, size_t len) {
int written = 0;
for (size_t i = 0; i < len; i++) {
uint32_t next = (*head + 1) % size;
if (next == *tail) break;
buf[*head] = data[i];
*head = next;
written++;
}
return written;
}
static int tty_read_ring(char *buf, uint32_t size, uint32_t *head, uint32_t *tail, char *out, size_t max_len) {
int read = 0;
while (*tail != *head && (size_t)read < max_len) {
out[read++] = buf[*tail];
*tail = (*tail + 1) % size;
}
return read;
}
int tty_write_output(int tty_id, const char *data, size_t len) {
tty_t *tty = tty_get(tty_id);
if (!tty || !data || len == 0) return 0;
uint64_t rflags = spinlock_acquire_irqsave(&tty->lock);
int written = tty_write_ring(tty->out_buf, TTY_OUT_SIZE, &tty->out_head, &tty->out_tail, data, len);
spinlock_release_irqrestore(&tty->lock, rflags);
return written;
}
int tty_read_output(int tty_id, char *buf, size_t max_len) {
tty_t *tty = tty_get(tty_id);
if (!tty || !buf || max_len == 0) return 0;
uint64_t rflags = spinlock_acquire_irqsave(&tty->lock);
int read = tty_read_ring(tty->out_buf, TTY_OUT_SIZE, &tty->out_head, &tty->out_tail, buf, max_len);
spinlock_release_irqrestore(&tty->lock, rflags);
return read;
}
int tty_write_input(int tty_id, const char *data, size_t len) {
tty_t *tty = tty_get(tty_id);
if (!tty || !data || len == 0) return 0;
uint64_t rflags = spinlock_acquire_irqsave(&tty->lock);
int written = tty_write_ring(tty->in_buf, TTY_IN_SIZE, &tty->in_head, &tty->in_tail, data, len);
spinlock_release_irqrestore(&tty->lock, rflags);
return written;
}
int tty_read_input(int tty_id, char *buf, size_t max_len) {
tty_t *tty = tty_get(tty_id);
if (!tty || !buf || max_len == 0) return 0;
uint64_t rflags = spinlock_acquire_irqsave(&tty->lock);
int read = tty_read_ring(tty->in_buf, TTY_IN_SIZE, &tty->in_head, &tty->in_tail, buf, max_len);
spinlock_release_irqrestore(&tty->lock, rflags);
return read;
}
int tty_set_foreground(int tty_id, int pid) {
tty_t *tty = tty_get(tty_id);
if (!tty) return -1;
uint64_t rflags = spinlock_acquire_irqsave(&tty->lock);
tty->fg_pid = pid;
spinlock_release_irqrestore(&tty->lock, rflags);
return 0;
}
int tty_get_foreground(int tty_id) {
tty_t *tty = tty_get(tty_id);
if (!tty) return -1;
uint64_t rflags = spinlock_acquire_irqsave(&tty->lock);
int pid = tty->fg_pid;
spinlock_release_irqrestore(&tty->lock, rflags);
return pid;
}

18
src/sys/tty.h Normal file
View File

@@ -0,0 +1,18 @@
// Copyright (c) 2023-2026 Chris (boreddevnl)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#ifndef TTY_H
#define TTY_H
#include <stddef.h>
int tty_create(void);
int tty_destroy(int tty_id);
int tty_write_output(int tty_id, const char *data, size_t len);
int tty_read_output(int tty_id, char *buf, size_t max_len);
int tty_write_input(int tty_id, const char *data, size_t len);
int tty_read_input(int tty_id, char *buf, size_t max_len);
int tty_set_foreground(int tty_id, int pid);
int tty_get_foreground(int tty_id);
#endif