Files
KatauOS/src/tasking/syscalls.c
T

667 lines
18 KiB
C

#include "../include/syscalls.h"
#include <stddef.h>
#include "../include/stdio.h"
#include "../include/liballoc.h"
#include "../include/paging.h"
#include "../include/string.h"
#include "../include/screen.h"
#include "../include/fat.h"
#include "../include/vfs.h"
#include "../include/isr.h"
#include "../include/task.h"
extern void switchProcess(Process* next);
#define STDIN_FILENO 0
#define STDOUT_FILENO 1
#define STDERR_FILENO 2
#define EBADF 9
#define EXECVE_MAX_ARGUMENT_SIZE 255
const char* flags_to_mode_str(int flags) {
static char mode[4] = {0};
int accmode = flags & 3;
if (accmode == 0) { // O_RDONLY
mode[0] = 'r';
mode[1] = '\0';
} else if (accmode == 1) { // O_WRONLY
if (flags & 0x400) { // O_APPEND
mode[0] = 'a';
mode[1] = '\0';
} else {
mode[0] = 'w';
mode[1] = '\0';
}
} else if (accmode == 2) { // O_RDWR
if (flags & 0x400) { // O_APPEND
mode[0] = 'a';
mode[1] = '+';
mode[2] = '\0';
} else if (flags & 0x200) { // O_TRUNC
mode[0] = 'w';
mode[1] = '+';
mode[2] = '\0';
} else {
mode[0] = 'r';
mode[1] = '+';
mode[2] = '\0';
}
} else {
mode[0] = 'r';
mode[1] = '\0';
}
return mode;
}
int sys_exit(TrapFrame *tf)
{
int error_code = tf->ebx;
debug_log("killing task\n");
task_kill(current);
asm("sti");
while(1){}
return error_code;
}
extern void trapret(void);
int sys_fork(TrapFrame *tf)
{
debug_log("FORKFORKFORKFORK\n");
scheduler_lock();
uint32_t* new_pd = copy_page_dir(current->pagedir);
if (!new_pd) {
debug_log("Failed to copy page directory\n");
return -1;
}
debug_log("new_pd: %X\n", new_pd);
void *p_physical = alloc_page();
if (!p_physical) {
destroy_page_dir(new_pd);
return -1;
}
Process* child = (Process*)((uint32_t)p_physical + 0xC0000000);
map_page(p_physical, (void*)child, PAGE_PRESENT | PAGE_RW);
memset(child, 0, sizeof(Process));
child->pid = ++pid_counter;
child->state = Ready;
child->ring = current->ring;
child->pagedir = new_pd;
void *kstack_physical = alloc_page();
if (!kstack_physical) {
destroy_page_dir(new_pd);
free_page(p_physical);
debug_log("Failed to allocate kernel stack\n");
return -1;
}
child->kstack = (char*)((uint32_t)kstack_physical + 0xC0000000);
map_page(kstack_physical, child->kstack, PAGE_PRESENT | PAGE_RW);
debug_log("child->kstack: %X\n", child->kstack);
uint8_t* sp = (uint8_t*)(child->kstack + KSTACKSIZE);
// 1. Place the TrapFrame on the child's stack.
sp -= sizeof(TrapFrame);
child->tf = (TrapFrame*)sp;
memcpy(&child->tf->gs, &tf->gs, 12 * sizeof(uint32_t));
uint32_t* parent_cpu_state_ptr = (uint32_t*)&tf->interrupt;
child->tf->eip = parent_cpu_state_ptr[0];
child->tf->cs = parent_cpu_state_ptr[1];
child->tf->eflags = parent_cpu_state_ptr[2];
child->tf->usermode_esp = parent_cpu_state_ptr[3];
child->tf->usermode_ss = parent_cpu_state_ptr[4];
child->tf->eax = 0; // Child returns 0
debug_log("Parent returns PID: %X, Child returns 0\n", child->pid);
sp -= sizeof(Context);
child->context = (Context*)sp;
memset(child->context, 0, sizeof(Context));
// 5. Set the child's starting instruction pointer to trapret.
// When the child is scheduled, it will execute trapret, which will
// restore the registers from the TrapFrame and iret to user mode.
child->context->eip = (uint32_t)trapret;
// 6. IMPORTANT: Set the child's kernel stack pointer for the scheduler.
child->kesp = (uint32_t)sp;
for (int i = 0; i < MAX_OPEN_FILES; i++) {
child->file_descriptors[i] = current->file_descriptors[i];
}
debug_log("File descriptors copied\n");
Process* curr = queue;
while (curr->next) {
curr = curr->next;
}
curr->next = child;
debug_log("Child added to queue\n");
scheduler_unlock(); // Release the lock
return child->pid;
}
#include "../../include/keyboard.h"
int sys_read(TrapFrame *tf)
{
int fd = tf->ebx;
char *user_buf = (char *)tf->ecx;
size_t count = tf->edx;
if (fd == STDIN_FILENO) {
scheduler_lock();
while (!stdin_has_line) {//while enter has not been pressed
current->state = Waiting;
current->waiting_reason = STDIN;
schedule();
}
scheduler_unlock();
//find the pos of the first \n
int line_len = 0;
while (line_len < stdin_pos && stdin_buffer[line_len] != '\n') line_len++;
if (line_len < stdin_pos) line_len++;
//copy the input to the user buffer
int to_copy = (line_len < count) ? line_len : count;
memcpy(user_buf, stdin_buffer, to_copy);
memmove(stdin_buffer, stdin_buffer + to_copy, stdin_pos - to_copy);
stdin_pos -= to_copy;
stdin_has_line = false;
for (int i = 0; i < stdin_pos; i++) {
if (stdin_buffer[i] == '\n') {
stdin_has_line = true;
break;
}
}
return to_copy;
}
else if(fd < 3)
{
return -EBADF;
}
else
{
if(current->file_descriptors[fd] == NULL)
{
return -EBADF;
}
return vfs_read_file_length(current->file_descriptors[fd], (uint8_t*)user_buf, count);
//return 0;//fat_fread(current->file_descriptors[tf->ebx], (void*)tf->ecx, tf->edx);
}
}
int sys_write(TrapFrame *tf)
{
if(tf->ebx < 3)
{
//stdin, stdout or stderr
if(tf->ebx == STDOUT_FILENO)
{
//debug_log("\n=================SYS_WRITE output to stdout=====================\n");
print((char*)tf->ecx, tf->edx);
//debug_log("\n=================END OF THAT SHIT=====================\n");
return tf->edx;
}
if(tf->ebx == STDERR_FILENO)
{
uint8_t prev_color = terminal_getcolor();
terminal_setcolor(VGA_COLOR_RED);
print((char*)tf->ecx, tf->edx);
terminal_setcolor(prev_color);
return tf->edx;
}
}
else
{
if(current->file_descriptors[tf->ebx] == NULL)
{
return -EBADF;
}
return 0;//fat_fwrite(current->file_descriptors[tf->ebx], (void*)tf->ecx, tf->edx);
}
}
int sys_open(TrapFrame *tf)
{
const char* filename = (const char*)tf->ebx;
int flags = tf->ecx;
int fd = -1;
const char* mode_str = flags_to_mode_str(flags);
//debug_log("\n====filename: %s\n", filename);
//debug_log("\n====mode_str: %s\n", mode_str);
for(int i = 3; i < MAX_OPEN_FILES; i++)
{
//debug_log("\n====i: %X\n", i);
if(current->file_descriptors[i] == NULL)
{
current->file_descriptors[i] = (l9660_file*)malloc(sizeof(l9660_file)); // Kernel malloc
l9660_openat(current->file_descriptors[i], root_dir, filename);
//debug_log("\n====FILE_DESCRIPTOR: %X\n", i);
int result = 0;//fat_fopen(current->file_descriptors[i], filename, mode_str);
if (result < 0) {
free(current->file_descriptors[i]); // Free on failure
current->file_descriptors[i] = NULL;
return result;
}
fd = i;
break;
}
}
return fd;
}
int sys_close(TrapFrame *tf)
{
int fd = tf->ebx;
//debug_log("SYS_CLOSE: fd = 0x%X", fd);
if(current->file_descriptors[fd] != NULL)
{
//debug_log("SYS_CLOSE: current->file_descriptors[fd] != NULL");
int result = 0;//fat_fclose(current->file_descriptors[fd]);
//debug_log("SYS_CLOSE: fat_fclose result: 0x%X", result);
free(current->file_descriptors[fd]);
current->file_descriptors[fd] = NULL;
return result;
}
return -1;
}
#include "../include/exec_from_file.h"
extern void execve_return(TrapFrame *tf);
/**
* this might be a not really good implementation as it only supports
* up to EXECVE_MAX_ARGUMENT_SIZE characters in each argument or environment
* string and for some reason I was unable to use malloc for kernel_argv and
* kernel_envp
*/
int sys_execve(TrapFrame *tf) {
const char* filename = (const char*)tf->ebx;
char **argv = (char**)tf->ecx;
char **envp = (char**)tf->edx;
int argc = 0;
int envc = 0;
while(argv[argc] != NULL)
{
argc++;
}
while(envp[envc] != NULL)
{
envc++;
}
//TODO: check why was there a triple fault when I tried using malloc
char kernel_argv[argc][EXECVE_MAX_ARGUMENT_SIZE];
char kernel_envp[envc][EXECVE_MAX_ARGUMENT_SIZE];
for(int i = 0; i < argc; i++)
{
strcpy(kernel_argv[i], argv[i]);
}
for(int i = 0; i < envc; i++)
{
strcpy(kernel_envp[i], envp[i]);
}
uint8_t* file_buffer;
int err = vfs_read_file_by_path(filename, &file_buffer);
if (err != 0) {
debug_log("execve: failed to read file '%s'\n", filename);
return -1;
}
Elf32_Ehdr *elf_header = (Elf32_Ehdr*)file_buffer;
if (memcmp(elf_header->e_ident, "\x7F" "ELF", 4) != 0) {
debug_log("execve: '%s' is not a valid ELF file\n", filename);
free(file_buffer);
return -1;
}
uint32_t* new_page_dir = create_page_dir();
if (!new_page_dir) {
free(file_buffer);
return -1;
}
uint32_t new_page_dir_phys = (uint32_t)get_physaddr((void*)new_page_dir);
uint32_t* old_page_dir_to_free = current->pagedir;
set_page_dir(new_page_dir_phys);
current->pagedir = new_page_dir;
void* kstack_phys = get_physaddr(current->kstack);
map_page(kstack_phys, current->kstack, PAGE_PRESENT | PAGE_RW);
uint32_t highest_vaddr = 0;
for (int i = 0; i < elf_header->e_phnum; i++) {
Elf32_Phdr *p_header = (Elf32_Phdr *)(file_buffer + elf_header->e_phoff + i * elf_header->e_phentsize);
if (p_header->p_type == PT_LOAD) {
uint32_t start_addr = p_header->p_vaddr;
uint32_t end_addr = start_addr + p_header->p_memsz;
uint32_t start_page = start_addr & ~0xFFF;
uint32_t end_page = (end_addr - 1) & ~0xFFF;
for (uint32_t vaddr = start_page; vaddr <= end_page; vaddr += PAGE_SIZE) {
void* phys_addr = alloc_page();
map_page(phys_addr, (void*)vaddr, PAGE_PRESENT | PAGE_RW | PAGE_USER);
}
memcpy((void*)p_header->p_vaddr, file_buffer + p_header->p_offset, p_header->p_filesz);
if (p_header->p_memsz > p_header->p_filesz) {
memset((void*)(p_header->p_vaddr + p_header->p_filesz), 0, p_header->p_memsz - p_header->p_filesz);
}
if (p_header->p_vaddr + p_header->p_memsz > highest_vaddr) {
highest_vaddr = p_header->p_vaddr + p_header->p_memsz;
}
}
}
for (uint32_t vaddr = USER_STACK_BOTTOM; vaddr <= USER_STACK_TOP; vaddr += PAGE_SIZE) {
void* phys_addr = alloc_page();
map_page(phys_addr, (void*)(vaddr & ~0xFFF), PAGE_PRESENT | PAGE_RW | PAGE_USER);
}
uint32_t user_esp = USER_STACK_TOP;
//push environment strings
uint32_t envp_pointers[envc + 1];
for (int i = envc - 1; i >= 0; i--) {
size_t len = strlen(kernel_envp[i]) + 1;
user_esp -= len;
memcpy((void*)user_esp, kernel_envp[i], len);
envp_pointers[i] = user_esp;
}
envp_pointers[envc] = 0;//null terminator
//push argument strings
uint32_t argv_pointers[argc + 1];
for (int i = argc - 1; i >= 0; i--) {
size_t len = strlen(kernel_argv[i]) + 1;
user_esp -= len;
memcpy((void*)user_esp, kernel_argv[i], len);
argv_pointers[i] = user_esp;
}
argv_pointers[argc] = 0;//null terminator
//push envp pointers
user_esp -= (envc + 1) * sizeof(uint32_t);
memcpy((void*)user_esp, envp_pointers, (envc + 1) * sizeof(uint32_t));
//push argv pointers
user_esp -= (argc + 1) * sizeof(uint32_t);
memcpy((void*)user_esp, argv_pointers, (argc + 1) * sizeof(uint32_t));
//push argc
user_esp -= sizeof(uint32_t);
*((uint32_t*)user_esp) = argc;
current->brk = (void*)(DivRoundUp(highest_vaddr, PAGE_SIZE) * PAGE_SIZE);
free(file_buffer);
destroy_page_dir(old_page_dir_to_free);
tf->gs = SEG_UDATA | DPL_USER;
tf->fs = SEG_UDATA | DPL_USER;
tf->es = SEG_UDATA | DPL_USER;
tf->ds = SEG_UDATA | DPL_USER;
tf->eax = 0; tf->ecx = 0; tf->edx = 0; tf->ebx = 0;
tf->ebp = 0; tf->esi = 0; tf->edi = 0;
tf->eip = elf_header->e_entry;
tf->cs = SEG_UCODE | DPL_USER;
tf->eflags = FL_IF;
tf->usermode_esp = user_esp;
tf->usermode_ss = SEG_UDATA | DPL_USER;
execve_return(tf);
return 0;
}
int sys_chdir(TrapFrame *tf)
{
l9660_dir new_cwd;
l9660_file placeholder_file;
int status = follow_path((char*)tf->ebx, &placeholder_file, &new_cwd);
if(status == L9660_OK)
{
*(current->cwd) = new_cwd;
}
switch(status)
{
case L9660_EIO:
return -5;//-EIO
break;
case L9660_EBADFS:
return -5;//EIO as well
break;
case L9660_ENOENT:
return -2;
break;
case L9660_ENOTDIR:
return -20;//ENOTDIR
break;
default:
return -5;//EIO
break;
}
//strcat(current->cwd->fat->path, "/");
//strcat(current->cwd->fat->path, (char*)tf->ebx);
int result = 0;//fat_opendir(current->cwd, (const char*)tf->ebx);
if(result == 0)
return 0;
return -1;
}
#include "../include/vfs.h"
/**
* I'm very sorry before myself for this but these directory traversing things are so hard, follow_path in vfs was pretty okay,
* this is when shit got real though
*/
uint32_t sys_getcwd(TrapFrame *tf)
{
l9660_dir current_dir = *(current->cwd);
char path_reversed[256] = {0};
char component_name[64];
while (!is_root_dir(&current_dir)) {
uint32_t child_sector = current_dir.file.first_sector;
// Open the parent directory
l9660_dir parent_dir;
l9660_opendirat(&parent_dir, &current_dir, "..");
// Find our previous directory's name within the parent
find_name_for_sector(component_name, sizeof(component_name), &parent_dir, child_sector);
// Prepend the name to our reversed path string (e.g., "dash/" + "katau/" -> "katau/dash/")
strcat(path_reversed, component_name);
strcat(path_reversed, "/");
// Move up one level
current_dir = parent_dir;
}
// Now, reverse the string "dash/katau/" to create the final path "/katau/dash"
char final_path[256] = "/";
char* token = strtok(path_reversed, "/");
while (token != NULL) {
// A bit of a trick to prepend tokens
char temp[256];
strcpy(temp, "/");
strcat(temp, token);
strcat(temp, final_path);
strcpy(final_path, temp);
token = strtok(NULL, "/");
}
// Handle the root case where the loop doesn't run
if (strlen(final_path) > 1) {
final_path[strlen(final_path) - 1] = '\0'; // Remove trailing slash
}
strcpy((char*)tf->ebx, final_path);
//strcpy((char*)tf->ebx, current->cwd->fat->path);
return strlen(final_path);//tf->ebx;
}
int sys_getpid(TrapFrame *tf)
{
return current->pid;
}
#define DivRoundUp(number, divisor) ((number + divisor - 1) / divisor)
uint32_t sys_brk(TrapFrame *tf)
{
if(!tf->ebx)
{
//debug_log("current brk is %X\n", (uint32_t)current->brk);
return (uint32_t)current->brk;
}
if(tf->ebx < (uint32_t)current->brk)
{
debug_log("PIZDETS!!!! new brk less than current brk\n");
while(1){}
}
uint32_t old_brk_page = DivRoundUp((uint32_t)current->brk, 0x1000);
uint32_t new_brk_page = DivRoundUp(tf->ebx, 0x1000);
uint32_t pages_needed = new_brk_page - old_brk_page;
for(uint32_t i = 0; i < pages_needed; i++)
{
void* phys_addr = alloc_page();
void* virt_addr = (void*)(old_brk_page * 0x1000 + i * 0x1000);
map_page(phys_addr, virt_addr, 0x7);
//debug_log("mapping page %X to %X\n", phys_addr, virt_addr);
memset(virt_addr, 0, 0x1000);
current->brk = (void*)tf->ebx;
//debug_log("new brk is %X\n", (uint32_t)current->brk);
}
return (uint32_t)current->brk;
}
typedef struct timespec {
int32_t tv_sec; // seconds
int32_t tv_nsec; // nanoseconds
} timespec;
uint32_t sys_nanosleep(TrapFrame *tf)
{
timespec* duration = (timespec*)tf->ebx;
size_t ms = duration->tv_sec * 1000 + duration->tv_nsec / 1000000;
//debug_log("waiting for 0x%X ms\n", ms);
scheduler_lock();
current->wake_up_time = timer_ticks + ms;
current->state = Waiting;
current->waiting_reason = TIMER;
schedule();
scheduler_unlock();
return 0;
}
void handle_syscall(TrapFrame *tf)
{
/*
debug_log("EAX: %X ", tf->eax);
debug_log("EBX: %X ", tf->ebx);
debug_log("ECX: %s ", tf->ecx);
debug_log("EDX: %X\n", tf->edx);
*/
//debug_log("calling 0x%X\n", tf->eax);
switch(tf->eax)
{
case 1://exit
tf->eax = sys_exit(tf);
break;
case 2://fork
tf->eax = sys_fork(tf);
break;
case 3://read
tf->eax = sys_read(tf);
break;
case 4://write
tf->eax = sys_write(tf);
break;
case 5://open
tf->eax = sys_open(tf);
break;
case 6://close
tf->eax = sys_close(tf);
break;
case 11://execve
tf->eax = sys_execve(tf);
break;
case 12://chdir
tf->eax = sys_chdir(tf);
break;
case 20://getpid
tf->eax = sys_getpid(tf);
break;
case 183://getcwd
tf->eax = sys_getcwd(tf);
break;
case 0x2d://brk
tf->eax = sys_brk(tf);
break;
case 0x36://ioctl
debug_log("IOCTL WAS CALLED, FUCKFUCK!!!!\n");
tf->eax = -38;//-ENOSYS
break;
case 0xa2://nanosleep
tf->eax = sys_nanosleep(tf);
break;
default:
debug_log("unknown syscall: %X\n", tf->eax);
tf->eax = -38;//-ENOSYS
break;
}
}