Attempt 2 #2

Open
seeseemelk wants to merge 56 commits from attempt-2 into master
10 changed files with 745 additions and 16 deletions
Showing only changes of commit c25ba1fccd - Show all commits

21
apps/libc/crt0.S Normal file
View File

@@ -0,0 +1,21 @@
/**
* @file crt0.S
* @brief C runtime startup for user-mode applications.
*
* Calls the C main() function and then exits with the return value.
*/
.section .text
.global _start
.extern main
_start:
/* Call main() */
call main
/* Exit with main's return value (in EAX) */
movl %eax, %ebx /* exit code = return value */
movl $0, %eax /* SYS_EXIT = 0 */
int $0x80
/* Should never reach here */
1: jmp 1b

147
apps/libc/syscalls.h Normal file
View File

@@ -0,0 +1,147 @@
/**
* @file syscalls.h
* @brief User-space system call wrappers for ClaudeOS.
*
* Provides inline functions that invoke INT 0x80 with the appropriate
* system call numbers and arguments.
*/
#ifndef USERSPACE_SYSCALLS_H
#define USERSPACE_SYSCALLS_H
typedef unsigned int uint32_t;
typedef int int32_t;
/* System call numbers (must match kernel's syscall.h) */
#define SYS_EXIT 0
#define SYS_WRITE 1
#define SYS_READ 2
#define SYS_FORK 3
#define SYS_GETPID 4
#define SYS_YIELD 5
#define SYS_WAITPID 6
#define SYS_EXEC 7
#define SYS_GETENV 8
#define SYS_SETENV 9
static inline int32_t syscall0(int num) {
int32_t ret;
__asm__ volatile("int $0x80" : "=a"(ret) : "a"(num));
return ret;
}
static inline int32_t syscall1(int num, uint32_t arg1) {
int32_t ret;
__asm__ volatile("int $0x80" : "=a"(ret) : "a"(num), "b"(arg1));
return ret;
}
static inline int32_t syscall2(int num, uint32_t arg1, uint32_t arg2) {
int32_t ret;
__asm__ volatile("int $0x80" : "=a"(ret)
: "a"(num), "b"(arg1), "c"(arg2));
return ret;
}
static inline int32_t syscall3(int num, uint32_t arg1, uint32_t arg2, uint32_t arg3) {
int32_t ret;
__asm__ volatile("int $0x80" : "=a"(ret)
: "a"(num), "b"(arg1), "c"(arg2), "d"(arg3));
return ret;
}
static inline void exit(int code) {
syscall1(SYS_EXIT, (uint32_t)code);
__builtin_unreachable();
}
static inline int32_t write(int fd, const void *buf, uint32_t len) {
return syscall3(SYS_WRITE, (uint32_t)fd, (uint32_t)buf, len);
}
static inline int32_t read(int fd, void *buf, uint32_t len) {
return syscall3(SYS_READ, (uint32_t)fd, (uint32_t)buf, len);
}
static inline int32_t fork(void) {
return syscall0(SYS_FORK);
}
static inline int32_t getpid(void) {
return syscall0(SYS_GETPID);
}
static inline void yield(void) {
syscall0(SYS_YIELD);
}
static inline int32_t waitpid(int32_t pid) {
return syscall1(SYS_WAITPID, (uint32_t)pid);
}
static inline int32_t exec(const char *path) {
return syscall1(SYS_EXEC, (uint32_t)path);
}
static inline int32_t getenv(const char *key, char *buf, uint32_t bufsize) {
return syscall3(SYS_GETENV, (uint32_t)key, (uint32_t)buf, bufsize);
}
static inline int32_t setenv(const char *key, const char *value) {
return syscall2(SYS_SETENV, (uint32_t)key, (uint32_t)value);
}
/* Basic string operations for user-space */
static inline uint32_t strlen(const char *s) {
uint32_t len = 0;
while (s[len]) len++;
return len;
}
static inline int strcmp(const char *a, const char *b) {
while (*a && *a == *b) { a++; b++; }
return (unsigned char)*a - (unsigned char)*b;
}
static inline int strncmp(const char *a, const char *b, uint32_t n) {
while (n && *a && *a == *b) { a++; b++; n--; }
return n ? ((unsigned char)*a - (unsigned char)*b) : 0;
}
static inline char *strcpy(char *dst, const char *src) {
char *d = dst;
while ((*d++ = *src++));
return dst;
}
static inline char *strncpy(char *dst, const char *src, uint32_t n) {
char *d = dst;
while (n && (*d++ = *src++)) n--;
while (n--) *d++ = '\0';
return dst;
}
static inline void *memset(void *s, int c, uint32_t n) {
unsigned char *p = (unsigned char *)s;
while (n--) *p++ = (unsigned char)c;
return s;
}
static inline void *memcpy(void *dst, const void *src, uint32_t n) {
unsigned char *d = (unsigned char *)dst;
const unsigned char *s = (const unsigned char *)src;
while (n--) *d++ = *s++;
return dst;
}
/** Print a string to stdout. */
static inline void puts(const char *s) {
write(1, s, strlen(s));
}
/** Print a single character to stdout. */
static inline void putchar(char c) {
write(1, &c, 1);
}
#endif /* USERSPACE_SYSCALLS_H */

180
apps/sh/sh.c Normal file
View File

@@ -0,0 +1,180 @@
/**
* @file sh.c
* @brief ClaudeOS shell.
*
* A simple interactive shell that reads commands from stdin,
* supports built-in commands (cd, exit, help, env), and
* executes external programs via fork+exec.
*/
#include "syscalls.h"
/** Maximum command line length. */
#define CMD_MAX 256
/** Read a line from stdin with echo and basic line editing.
* Returns length of the line (excluding newline). */
static int readline(char *buf, int maxlen) {
int pos = 0;
while (pos < maxlen - 1) {
char c;
int n = read(0, &c, 1);
if (n <= 0) {
/* No data yet: yield CPU and retry */
yield();
continue;
}
if (c == '\n' || c == '\r') {
putchar('\n');
break;
} else if (c == '\b' || c == 127) {
/* Backspace */
if (pos > 0) {
pos--;
puts("\b \b"); /* Move back, overwrite with space, move back */
}
} else if (c >= 32) {
/* Printable character */
buf[pos++] = c;
putchar(c);
}
}
buf[pos] = '\0';
return pos;
}
/** Skip leading whitespace, return pointer to first non-space. */
static char *skip_spaces(char *s) {
while (*s == ' ' || *s == '\t') s++;
return s;
}
/** Find the next space or end of string. */
static char *find_space(char *s) {
while (*s && *s != ' ' && *s != '\t') s++;
return s;
}
/** Built-in: cd <path> */
static void builtin_cd(char *arg) {
if (!arg || !*arg) {
arg = "/";
}
/* Update CWD environment variable */
setenv("CWD", arg);
/* Verify it was set */
char cwd[128];
if (getenv("CWD", cwd, sizeof(cwd)) >= 0) {
puts("cd: ");
puts(cwd);
putchar('\n');
}
}
/** Built-in: env - print all known env vars. */
static void builtin_env(void) {
char buf[128];
if (getenv("CWD", buf, sizeof(buf)) >= 0) {
puts("CWD=");
puts(buf);
putchar('\n');
}
if (getenv("PATH", buf, sizeof(buf)) >= 0) {
puts("PATH=");
puts(buf);
putchar('\n');
}
if (getenv("TEST", buf, sizeof(buf)) >= 0) {
puts("TEST=");
puts(buf);
putchar('\n');
}
}
/** Built-in: help */
static void builtin_help(void) {
puts("ClaudeOS Shell\n");
puts("Built-in commands:\n");
puts(" cd <path> - change working directory\n");
puts(" env - show environment variables\n");
puts(" help - show this message\n");
puts(" exit - exit the shell\n");
puts("External commands are loaded from initrd.\n");
}
/** Execute an external command via fork+exec. */
static void run_command(const char *cmd) {
int32_t pid = fork();
if (pid < 0) {
puts("sh: fork failed\n");
return;
}
if (pid == 0) {
/* Child: exec the command */
int32_t ret = exec(cmd);
if (ret < 0) {
puts("sh: ");
puts(cmd);
puts(": not found\n");
exit(127);
}
/* exec doesn't return on success */
exit(1);
}
/* Parent: wait for child */
waitpid(pid);
}
int main(void) {
puts("ClaudeOS Shell v0.1\n");
puts("Type 'help' for available commands.\n\n");
char cmd[CMD_MAX];
for (;;) {
/* Print prompt with CWD */
char cwd[128];
if (getenv("CWD", cwd, sizeof(cwd)) < 0) {
strcpy(cwd, "/");
}
puts(cwd);
puts("$ ");
/* Read command */
int len = readline(cmd, CMD_MAX);
if (len == 0) continue;
/* Parse command and arguments */
char *line = skip_spaces(cmd);
if (*line == '\0') continue;
char *arg_start = find_space(line);
char *arg = (char *)0;
if (*arg_start) {
*arg_start = '\0';
arg = skip_spaces(arg_start + 1);
if (*arg == '\0') arg = (char *)0;
}
/* Check built-in commands */
if (strcmp(line, "exit") == 0) {
puts("Goodbye!\n");
exit(0);
} else if (strcmp(line, "cd") == 0) {
builtin_cd(arg);
} else if (strcmp(line, "env") == 0) {
builtin_env();
} else if (strcmp(line, "help") == 0) {
builtin_help();
} else {
/* External command */
run_command(line);
}
}
return 0;
}

View File

@@ -11,15 +11,25 @@ LINKER_SCRIPT="$APPS_DIR/user.ld"
CC="${CC:-clang}"
OBJCOPY="${OBJCOPY:-objcopy}"
CFLAGS="-ffreestanding -m32 -fno-pie -fno-pic -fno-builtin -fno-stack-protector -mno-sse -mno-mmx -O2 -Wall"
CFLAGS="-ffreestanding -m32 -fno-pie -fno-pic -fno-builtin -fno-stack-protector -mno-sse -mno-mmx -O2 -Wall -I$APPS_DIR/libc"
LDFLAGS="-m32 -nostdlib -no-pie -Wl,--no-dynamic-linker"
mkdir -p "$OUTPUT_DIR"
# Build crt0 if it exists
CRT0_OBJ=""
if [ -f "$APPS_DIR/libc/crt0.S" ]; then
CRT0_OBJ="$OUTPUT_DIR/_crt0.o"
$CC $CFLAGS -c "$APPS_DIR/libc/crt0.S" -o "$CRT0_OBJ"
fi
for app_dir in "$APPS_DIR"/*/; do
[ -d "$app_dir" ] || continue
app_name=$(basename "$app_dir")
# Skip the libc directory (shared library, not an app)
[ "$app_name" = "libc" ] && continue
echo "Building app: $app_name"
# Collect source files
@@ -36,13 +46,22 @@ for app_dir in "$APPS_DIR"/*/; do
continue
fi
# Link into ELF
# Link into ELF (include crt0 if app has .c files and doesn't have its own _start)
elf="$OUTPUT_DIR/$app_name.elf"
$CC $LDFLAGS -T "$LINKER_SCRIPT" $OBJ_FILES -o "$elf"
HAS_C_FILES=""
for src in "$app_dir"*.c; do
[ -f "$src" ] && HAS_C_FILES="yes"
done
# Convert to flat binary (strip non-code sections)
if [ -n "$HAS_C_FILES" ] && [ -n "$CRT0_OBJ" ]; then
$CC $LDFLAGS -T "$LINKER_SCRIPT" "$CRT0_OBJ" $OBJ_FILES -o "$elf"
else
$CC $LDFLAGS -T "$LINKER_SCRIPT" $OBJ_FILES -o "$elf"
fi
# Convert to flat binary (include .bss for zero-initialized data)
bin="$OUTPUT_DIR/$app_name"
$OBJCOPY -O binary --only-section=.text --only-section=.rodata --only-section=.data "$elf" "$bin"
$OBJCOPY -O binary --only-section=.text --only-section=.rodata --only-section=.data --only-section=.bss "$elf" "$bin"
size=$(wc -c < "$bin")
echo " Built: $bin ($size bytes)"

View File

@@ -20,6 +20,7 @@ add_executable(kernel
vfs.c
initrd_fs.c
env.c
keyboard.c
interrupts.S
kernel.c
)

View File

@@ -2,6 +2,7 @@
#include "pic.h"
#include "process.h"
#include "syscall.h"
#include "keyboard.h"
#include <stdint.h>
/* Forward declaration for kernel panic or similar */
@@ -61,8 +62,8 @@ void isr_handler(registers_t *regs)
/* Timer tick - invoke scheduler */
schedule_tick(regs);
} else if (regs->int_no == 33) {
/* Keyboard */
offset_print("Keyboard IRQ!\n");
/* Keyboard IRQ */
keyboard_irq(regs);
}
return;
}

View File

@@ -16,6 +16,7 @@
#include "cpio.h"
#include "vfs.h"
#include "initrd_fs.h"
#include "keyboard.h"
void offset_print(const char *str)
{
@@ -52,6 +53,8 @@ void kernel_main(uint32_t magic, uint32_t addr) {
offset_print("IDT initialized\n");
init_pic();
/* Unmask timer IRQ (IRQ0) explicitly */
pic_clear_mask(0);
offset_print("PIC initialized\n");
init_pmm(addr);
@@ -130,6 +133,9 @@ void kernel_main(uint32_t magic, uint32_t addr) {
init_syscalls();
offset_print("Syscalls initialized\n");
keyboard_init();
offset_print("Keyboard initialized\n");
init_process();
offset_print("Process subsystem initialized\n");

188
src/keyboard.c Normal file
View File

@@ -0,0 +1,188 @@
/**
* @file keyboard.c
* @brief PS/2 keyboard driver implementation.
*
* Reads scancodes from I/O port 0x60 on IRQ1, translates scancode set 1
* to ASCII using a simple lookup table, stores characters in a ring buffer,
* and wakes any process blocked waiting for keyboard input.
*/
#include "keyboard.h"
#include "port_io.h"
#include "pic.h"
#include "process.h"
#include <stddef.h>
#include <string.h>
/* Debug print helpers defined in kernel.c */
extern void offset_print(const char *str);
extern void print_hex(uint32_t val);
/** PS/2 keyboard data port. */
#define KB_DATA_PORT 0x60
/** Ring buffer for keyboard input. */
static char kb_buffer[KB_BUFFER_SIZE];
static volatile uint32_t kb_head = 0;
static volatile uint32_t kb_tail = 0;
/** Process waiting for keyboard input (PID, or 0 if none). */
static volatile uint32_t kb_waiting_pid = 0;
/** Shift key state. */
static int shift_pressed = 0;
/**
* Scancode set 1 to ASCII lookup table (unshifted).
* Index = scancode, value = ASCII character (0 = unmapped).
*/
static const char scancode_ascii[128] = {
0, 27, '1', '2', '3', '4', '5', '6', /* 0x00 - 0x07 */
'7', '8', '9', '0', '-', '=', '\b', '\t', /* 0x08 - 0x0F */
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', /* 0x10 - 0x17 */
'o', 'p', '[', ']', '\n', 0, 'a', 's', /* 0x18 - 0x1F */
'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', /* 0x20 - 0x27 */
'\'', '`', 0, '\\', 'z', 'x', 'c', 'v', /* 0x28 - 0x2F */
'b', 'n', 'm', ',', '.', '/', 0, '*', /* 0x30 - 0x37 */
0, ' ', 0, 0, 0, 0, 0, 0, /* 0x38 - 0x3F */
0, 0, 0, 0, 0, 0, 0, 0, /* 0x40 - 0x47 */
0, 0, 0, 0, 0, 0, 0, 0, /* 0x48 - 0x4F */
0, 0, 0, 0, 0, 0, 0, 0, /* 0x50 - 0x57 */
0, 0, 0, 0, 0, 0, 0, 0, /* 0x58 - 0x5F */
0, 0, 0, 0, 0, 0, 0, 0, /* 0x60 - 0x67 */
0, 0, 0, 0, 0, 0, 0, 0, /* 0x68 - 0x6F */
0, 0, 0, 0, 0, 0, 0, 0, /* 0x70 - 0x77 */
0, 0, 0, 0, 0, 0, 0, 0, /* 0x78 - 0x7F */
};
/**
* Scancode set 1 to ASCII lookup table (shifted).
*/
static const char scancode_ascii_shift[128] = {
0, 27, '!', '@', '#', '$', '%', '^', /* 0x00 - 0x07 */
'&', '*', '(', ')', '_', '+', '\b', '\t', /* 0x08 - 0x0F */
'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', /* 0x10 - 0x17 */
'O', 'P', '{', '}', '\n', 0, 'A', 'S', /* 0x18 - 0x1F */
'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', /* 0x20 - 0x27 */
'"', '~', 0, '|', 'Z', 'X', 'C', 'V', /* 0x28 - 0x2F */
'B', 'N', 'M', '<', '>', '?', 0, '*', /* 0x30 - 0x37 */
0, ' ', 0, 0, 0, 0, 0, 0, /* 0x38 - 0x3F */
0, 0, 0, 0, 0, 0, 0, 0, /* 0x40 - 0x47 */
0, 0, 0, 0, 0, 0, 0, 0, /* 0x48 - 0x4F */
0, 0, 0, 0, 0, 0, 0, 0, /* 0x50 - 0x57 */
0, 0, 0, 0, 0, 0, 0, 0, /* 0x58 - 0x5F */
0, 0, 0, 0, 0, 0, 0, 0, /* 0x60 - 0x67 */
0, 0, 0, 0, 0, 0, 0, 0, /* 0x68 - 0x6F */
0, 0, 0, 0, 0, 0, 0, 0, /* 0x70 - 0x77 */
0, 0, 0, 0, 0, 0, 0, 0, /* 0x78 - 0x7F */
};
/** Left/right shift scancodes. */
#define SC_LSHIFT_PRESS 0x2A
#define SC_LSHIFT_RELEASE 0xAA
#define SC_RSHIFT_PRESS 0x36
#define SC_RSHIFT_RELEASE 0xB6
/**
* Put a character into the ring buffer.
*/
static void kb_buffer_put(char c) {
uint32_t next = (kb_head + 1) % KB_BUFFER_SIZE;
if (next == kb_tail) {
return; /* Buffer full, drop character */
}
kb_buffer[kb_head] = c;
kb_head = next;
}
void keyboard_init(void) {
kb_head = 0;
kb_tail = 0;
kb_waiting_pid = 0;
shift_pressed = 0;
/* Flush any pending data from the keyboard controller */
while (inb(0x64) & 0x01) {
inb(KB_DATA_PORT);
}
/* Unmask IRQ1 (keyboard) in the PIC */
pic_clear_mask(1);
offset_print(" KEYBOARD: initialized\n");
}
void keyboard_irq(registers_t *regs) {
(void)regs;
uint8_t scancode = inb(KB_DATA_PORT);
/* Handle shift keys */
if (scancode == SC_LSHIFT_PRESS || scancode == SC_RSHIFT_PRESS) {
shift_pressed = 1;
return;
}
if (scancode == SC_LSHIFT_RELEASE || scancode == SC_RSHIFT_RELEASE) {
shift_pressed = 0;
return;
}
/* Ignore key releases (bit 7 set) */
if (scancode & 0x80) {
return;
}
/* Translate scancode to ASCII */
char c;
if (shift_pressed) {
c = scancode_ascii_shift[scancode];
} else {
c = scancode_ascii[scancode];
}
if (c == 0) {
return; /* Unmapped key */
}
/* Put character in buffer */
kb_buffer_put(c);
/* Wake any process waiting for keyboard input */
if (kb_waiting_pid != 0) {
process_t *waiter = process_get(kb_waiting_pid);
if (waiter && waiter->state == PROCESS_BLOCKED) {
waiter->state = PROCESS_READY;
}
kb_waiting_pid = 0;
}
}
uint32_t keyboard_read(char *buf, uint32_t count) {
uint32_t n = 0;
while (n < count && kb_tail != kb_head) {
buf[n++] = kb_buffer[kb_tail];
kb_tail = (kb_tail + 1) % KB_BUFFER_SIZE;
}
return n;
}
int keyboard_has_data(void) {
return kb_head != kb_tail;
}
void keyboard_block_for_input(registers_t *regs) {
process_t *cur = process_current();
if (!cur) return;
cur->state = PROCESS_BLOCKED;
cur->saved_regs = *regs;
/* Rewind EIP by 2 bytes so that when the process is unblocked and
* scheduled, the CPU re-executes the INT 0x80 instruction. At that
* point keyboard_has_data() will return true and the read succeeds. */
cur->saved_regs.eip -= 2;
kb_waiting_pid = cur->pid;
/* Schedule next process */
schedule_tick(regs);
}

56
src/keyboard.h Normal file
View File

@@ -0,0 +1,56 @@
/**
* @file keyboard.h
* @brief PS/2 keyboard driver.
*
* Handles IRQ1 keyboard interrupts, translates scancodes to ASCII,
* and provides a ring buffer for user-space reading via SYS_READ.
*/
#ifndef KEYBOARD_H
#define KEYBOARD_H
#include <stdint.h>
#include "isr.h"
/** Keyboard input buffer size. */
#define KB_BUFFER_SIZE 256
/**
* Initialize the keyboard driver.
*/
void keyboard_init(void);
/**
* Handle a keyboard IRQ (called from isr_handler for IRQ1).
*
* @param regs Register state (may be used to wake blocked processes).
*/
void keyboard_irq(registers_t *regs);
/**
* Read characters from the keyboard buffer.
* Non-blocking: returns whatever is available, 0 if empty.
*
* @param buf Destination buffer.
* @param count Maximum bytes to read.
* @return Number of bytes read.
*/
uint32_t keyboard_read(char *buf, uint32_t count);
/**
* Check if there is data available in the keyboard buffer.
*
* @return Non-zero if data is available.
*/
int keyboard_has_data(void);
/**
* Block the given process until keyboard data is available.
* Sets the process to BLOCKED state and records it as waiting for keyboard.
* When data arrives, the process will be unblocked.
*
* @param regs Current interrupt frame (for saving process state).
*/
void keyboard_block_for_input(registers_t *regs);
#endif /* KEYBOARD_H */

View File

@@ -12,7 +12,12 @@
#include "env.h"
#include "port_io.h"
#include "vga.h"
#include "keyboard.h"
#include "cpio.h"
#include "paging.h"
#include "pmm.h"
#include <stddef.h>
#include <string.h>
/** Magic return value indicating the syscall blocked and switched processes.
* syscall_handler must NOT overwrite regs->eax in this case. */
@@ -60,11 +65,24 @@ static int32_t sys_write(registers_t *regs) {
/**
* Handle SYS_READ: read bytes from a file descriptor.
* Stub for now.
* fd=0 (stdin) reads from the keyboard buffer (non-blocking).
* Returns 0 if no data available; caller should yield and retry.
*/
static int32_t sys_read(registers_t *regs) {
(void)regs;
return -1; /* Not implemented */
int fd = (int)regs->ebx;
char *buf = (char *)regs->ecx;
uint32_t len = regs->edx;
if (fd == 0) {
/* stdin: non-blocking read from keyboard */
if (keyboard_has_data()) {
uint32_t n = keyboard_read(buf, len);
return (int32_t)n;
}
return 0; /* No data available */
}
return -1; /* Invalid fd */
}
/**
@@ -85,11 +103,11 @@ static int32_t sys_getpid(registers_t *regs) {
/**
* Handle SYS_YIELD: voluntarily yield the CPU.
* Calls schedule_tick directly to potentially switch to another process.
*/
static int32_t sys_yield(registers_t *regs) {
(void)regs;
schedule();
return 0;
schedule_tick(regs);
return SYSCALL_SWITCHED;
}
/**
@@ -133,11 +151,103 @@ static int32_t sys_waitpid(registers_t *regs) {
}
/**
* Handle SYS_EXEC: placeholder.
* Handle SYS_EXEC: replace the current process image with a new program.
* EBX = path to binary (C string), e.g. "hello-world".
* Loads the binary from the initrd and replaces the current process's
* code and stack. Does not return on success.
*/
static int32_t sys_exec(registers_t *regs) {
(void)regs;
return -1; /* Not implemented yet */
const char *path = (const char *)regs->ebx;
if (!path) return -1;
process_t *cur = process_current();
if (!cur) return -1;
/* Look up the binary in the initrd */
cpio_entry_t entry;
if (cpio_find(path, &entry) != 0) {
return -1; /* Not found */
}
uint32_t *pd = (uint32_t *)cur->page_directory;
/* Unmap and free old user code pages (0x08048000 region).
* We don't know exactly how many pages were mapped, so scan a
* reasonable range. */
for (uint32_t vaddr = USER_CODE_START;
vaddr < USER_CODE_START + 0x100000; /* up to 1 MiB of code */
vaddr += 4096) {
uint32_t pd_idx = vaddr >> 22;
uint32_t pt_idx = (vaddr >> 12) & 0x3FF;
if (!(pd[pd_idx] & 0x001)) break; /* No page table */
uint32_t *pt = (uint32_t *)(pd[pd_idx] & 0xFFFFF000);
if (!(pt[pt_idx] & 0x001)) break; /* No page */
phys_addr_t old_phys = pt[pt_idx] & 0xFFFFF000;
pt[pt_idx] = 0;
pmm_free_page(old_phys);
}
/* Map new code pages */
uint32_t code_pages = (entry.datasize + 4095) / 4096;
for (uint32_t i = 0; i < code_pages; i++) {
phys_addr_t phys = pmm_alloc_page(PMM_ZONE_NORMAL);
if (phys == 0) return -1;
uint32_t vaddr = USER_CODE_START + i * 4096;
paging_map_page_in(pd, vaddr, phys,
PAGE_PRESENT | PAGE_WRITE | PAGE_USER);
uint32_t offset = i * 4096;
uint32_t bytes = entry.datasize - offset;
if (bytes > 4096) bytes = 4096;
memcpy((void *)phys, (const uint8_t *)entry.data + offset, bytes);
if (bytes < 4096) {
memset((void *)(phys + bytes), 0, 4096 - bytes);
}
}
/* Zero the user stack pages (reuse existing stack mappings) */
uint32_t stack_base = USER_STACK_TOP - USER_STACK_PAGES * 4096;
for (uint32_t i = 0; i < USER_STACK_PAGES; i++) {
uint32_t vaddr = stack_base + i * 4096;
uint32_t pd_idx = vaddr >> 22;
uint32_t pt_idx = (vaddr >> 12) & 0x3FF;
if ((pd[pd_idx] & 0x001)) {
uint32_t *pt = (uint32_t *)(pd[pd_idx] & 0xFFFFF000);
if ((pt[pt_idx] & 0x001)) {
phys_addr_t phys = pt[pt_idx] & 0xFFFFF000;
memset((void *)phys, 0, 4096);
}
}
}
/* Flush TLB */
paging_switch_directory(cur->page_directory);
/* Update process name */
uint32_t nlen = strlen(path);
if (nlen > 31) nlen = 31;
memcpy(cur->name, path, nlen);
cur->name[nlen] = '\0';
/* Set up registers for the new program */
regs->eip = USER_CODE_START;
regs->useresp = USER_STACK_TOP;
regs->esp = USER_STACK_TOP;
regs->eax = 0;
regs->ebx = 0;
regs->ecx = 0;
regs->edx = 0;
regs->esi = 0;
regs->edi = 0;
regs->ebp = 0;
regs->cs = 0x1B;
regs->ds = 0x23;
regs->ss = 0x23;
regs->eflags = 0x202; /* IF=1 */
/* Return SYSCALL_SWITCHED so syscall_handler doesn't overwrite regs */
return SYSCALL_SWITCHED;
}
/**