feat: shell (sh) with keyboard driver, SYS_READ, SYS_EXEC
- PS/2 keyboard driver: IRQ1 handler, scancode set 1 translation, ring buffer, shift key support - SYS_READ (fd=0): non-blocking keyboard read for stdin - SYS_YIELD: calls schedule_tick directly (no nested INT 0x80) - SYS_EXEC: loads binary from CPIO initrd, replaces process image - User-space libc: crt0.S (C runtime startup), syscalls.h (inline syscall wrappers, basic string functions) - Shell app (sh): readline with echo/backspace, builtins (cd, env, help, exit), fork+exec for external commands - Updated build_apps.sh: C app support with crt0 linking, libc include path, .bss section in objcopy Tested: shell boots, keyboard input works, hello-world runs via fork+exec, env shows CWD, exit cleanly terminates.
This commit is contained in:
128
src/syscall.c
128
src/syscall.c
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user