diff --git a/include/config.h b/include/config.h index 4b187da..0544725 100644 --- a/include/config.h +++ b/include/config.h @@ -63,4 +63,7 @@ /* ssp */ #define STACK_CHK_GUARD 0x7ABA5C007ABA5C00 +/* fs */ +#define FDT_MAX 8 // Maximum amount of file descriptors per process + #endif diff --git a/include/fs/initfs.h b/include/fs/initfs.h index c47d517..266ce33 100644 --- a/include/fs/initfs.h +++ b/include/fs/initfs.h @@ -11,5 +11,7 @@ int initfs_init(struct limine_file* tar_file); int tar_lookup(unsigned char* archive, char* filename, char** out); +int tar_read(char* filename, char** buf); +int tar_exists(const char* filename); #endif \ No newline at end of file diff --git a/include/kernel.h b/include/kernel.h index 01cfb42..cb66dc0 100644 --- a/include/kernel.h +++ b/include/kernel.h @@ -8,10 +8,13 @@ #define KERNEL_H #include "limine.h" + enum ErrorCodes { - ENOMEM, - EIO, - ENOENT + ENOMEM, // No memory + EIO, // Input/output error + ENOENT, // No entry + EBADFD, // Bad file descriptor + EMFILE // Too many open files }; #define CLEAR_INTERRUPTS __asm__ volatile("cli") diff --git a/include/sched/process.h b/include/sched/process.h index af36cb6..b822f4a 100644 --- a/include/sched/process.h +++ b/include/sched/process.h @@ -11,6 +11,7 @@ #include #include #include +#include typedef enum { READY, @@ -18,6 +19,13 @@ typedef enum { DEAD } status_t; +struct fd { + int fd; + char filename[PROCESS_NAME_MAX]; // File opened + uint64_t cursor; // Cursor position in file + bool open; +}; + struct process { size_t pid; char name[PROCESS_NAME_MAX]; @@ -26,6 +34,10 @@ struct process { struct cpu_status* context; void* root_page_table; // Process PML4 (should contain kernel PML4 in higher half [256-511] void* kernel_stack; // Used for interrupts (syscall: int 0x80), defines the TSS RSP0 + + struct fd fdt[FDT_MAX]; // File Descriptor Table + size_t next_free_fd; + struct process* next; }; @@ -38,7 +50,6 @@ void process_exit(void); void process_display_list(struct process* processes_list); -void process_create_user(struct limine_file* file, char* name); void process_create_user_raw(char* file, int size, char* name); #endif diff --git a/src/arch/x86/syscall.c b/src/arch/x86/syscall.c index 839969f..75bb001 100644 --- a/src/arch/x86/syscall.c +++ b/src/arch/x86/syscall.c @@ -4,15 +4,84 @@ * @license GPL-3.0-only */ +#include "config.h" #include "sched/scheduler.h" #include #include #include #include #include +#include +#include +#include extern struct process* current_process; +// Return fd on success, -errno on error +int sys_open(const char* filename, int flags) +{ + if (tar_exists(filename) < 0) { + return -ENOENT; // file doesn't exist.. + } + // file exists here! + if (current_process->next_free_fd >= FDT_MAX) { + return -EMFILE; + } + int fd = current_process->next_free_fd++; + current_process->fdt[fd].fd = fd; + current_process->fdt[fd].open = true; + current_process->fdt[fd].cursor = 0; + strncpy(current_process->fdt[fd].filename, filename, PROCESS_NAME_MAX - 1); + return fd; +} + +// Return 0 on success, -EBADFD if invalid FD +int sys_close(int fd) +{ + if (fd < 0 || fd >= FDT_MAX) { + return -EBADFD; + } + + if (!current_process->fdt[fd].open) { + return -EBADFD; // FD not opened in the first place + } + + current_process->fdt[fd].open = false; + current_process->fdt[fd].filename[0] = '\0'; + current_process->fdt[fd].cursor = 0; + return 0; +} + +// Should return the number of bytes read +int sys_read(unsigned int fd, char* buf, size_t count) +{ + size_t i; + switch (fd) { + case 0: //read from stdin (keyboard) + for (i=0; ifdt[fd].open == false) { + return -EBADFD; // File descriptor wasn't open + } + // Here fd refers to a valid opened file.. + int sz = tar_read(current_process->fdt[fd].filename,&buf); + if (sz == 0) { + return -ENOENT; + } else { + return sz; + } + } + + return -EBADFD; +} + +// TODO: Should have a return value: number of bytes written on success, -1 on error (errno set) void sys_write(unsigned int fd, const char* buf, size_t count) { switch (fd) { @@ -61,17 +130,23 @@ struct cpu_status* syscall_handler(struct cpu_status* regs) switch (regs->rax) { case 0: //sys_read + regs->rax = sys_read(regs->rdi, (char*)regs->rsi, regs->rdx); break; case 1: //sys_write sys_write(regs->rdi, (char*)regs->rsi, regs->rdx); break; + case 2: + regs->rax = sys_open((const char*)regs->rdi, regs->rsi); + break; + case 3: + regs->rax = sys_close(regs->rdi); + break; case 60: //sys_exit sys_exit(regs->rdi); break; - default: - regs->rax = 0xbad515ca11; + default: // bad syscall + regs->rax = 0xbad515ca11; break; } - return regs; } \ No newline at end of file diff --git a/src/fs/initfs.c b/src/fs/initfs.c index ddeff2b..7a8675a 100644 --- a/src/fs/initfs.c +++ b/src/fs/initfs.c @@ -59,6 +59,42 @@ int tar_lookup(unsigned char* archive, char* filename, char** out) return 0; } +/* + * tar_read - read a file in the TAR file + * @filename: file to read (absolute path) + * @out: out buffer (if file is found) + * + * Return: + * $filesize - size of the file, if found + * $0 - file not found + */ +int tar_read(char* filename, char** buf) +{ + return tar_lookup(archive_start_addr, filename, buf); +} + +/* + * tar_exists - check if a file exists in the TAR archive + * @filename: file to check (absolute path) + * + * Return: + * $filesize - size of the file, if found + * $-ENOENT - file not found + */ +int tar_exists(const char* filename) +{ + unsigned char* ptr = archive_start_addr; + + while (!memcmp(ptr + 257, "ustar", 5)) { + int filesize = tar_oct2bin(ptr + 0x7c, 11); + if (!memcmp(ptr, filename, strlen(filename) + 1)) { + return filesize; + } + ptr += (((filesize + 511) / 512) + 1) * 512; + } + return -ENOENT; +} + /* * initfs_init - initialize the TAR initial filesystem * @tar_file: pointer to the Limine-loaded archive diff --git a/src/kmain.c b/src/kmain.c index c0ec88b..33ee671 100644 --- a/src/kmain.c +++ b/src/kmain.c @@ -78,14 +78,6 @@ void idle_main(void* arg) } } -void thing_main(void* arg) -{ - printf("What's your name, pal? "); - char name[10]; - keyboard_getline(name, 10); - printf("\r\n{%s} is such a nice name!\r\n", name); -} - extern uintptr_t kheap_start; /* diff --git a/src/sched/process.c b/src/sched/process.c index e7d72d6..b8bc7af 100644 --- a/src/sched/process.c +++ b/src/sched/process.c @@ -225,69 +225,28 @@ void process_jump_to_user(uintptr_t stack_top, uintptr_t user_code) extern struct tss tss; /* - * process_create_user - Create a new user process - * @file: pointer to Limine file structure + * process_create_user_raw - Create a new user process from raw binary + * @file: pointer to beginning of binary + * @size: size of the binary * @name: name for the new process * - * This function takes a loaded Limine executable - * module, and maps its code, a user stack, sets the - * TSS RSP0 for interrupts, and finally jumps to the - * user code. + * This function takes an executable loaded in memory + * and maps its code, a user stack, sets the TSS RSP0 + * for interrupts, and finally jumps to the user code. */ -void process_create_user(struct limine_file* file, char* name) -{ - CLEAR_INTERRUPTS; - struct process* proc = (struct process*)kmalloc(sizeof(struct process)); - struct cpu_status* ctx = (struct cpu_status*)kmalloc(sizeof(struct cpu_status)); - - if (!proc || !ctx) panic(NULL, "out of memory while creating user process"); - - strncpy(proc->name, name, PROCESS_NAME_MAX); - memset(ctx, 0, sizeof(struct cpu_status)); // set GP registers to zero - proc->pid = next_free_pid++; - proc->status = READY; - proc->next = 0; - proc->context = ctx; - proc->context->iret_ss = USER_DATA_SEGMENT | 3; - proc->context->iret_cs = USER_CODE_SEGMENT | 3; - proc->context->iret_flags = 0x202; // Interrupt Flag set - - void* exec_addr = file->address; - uint64_t exec_size = file->size; - - uint64_t* user_pml4 = vmm_create_address_space(); - if (!user_pml4) panic(NULL, "failed to create user address space"); - proc->root_page_table = user_pml4; - - uintptr_t stack_top = vmm_alloc_user_stack(user_pml4); - uint64_t code = vmm_alloc_user_code(user_pml4, exec_addr, exec_size); - - proc->context->iret_rsp = stack_top; - proc->context->iret_rip = code; - proc->kernel_stack = kalloc_stack(); - if (!proc->kernel_stack) panic(NULL, "failed to allocate kernel stack"); - - // Copy code into user pages; for that we need to temporarily switch to the user pml4 - load_cr3(VIRT_TO_PHYS((uint64_t)user_pml4)); - memcpy((uint64_t*)code, exec_addr, exec_size); - load_cr3(VIRT_TO_PHYS((uint64_t)kernel_pml4)); - - process_add(&processes_list, proc); - DEBUG("user process '%s' (pid=%u) enqueued for scheduling", name, proc->pid); - SET_INTERRUPTS; -} - -// Same as above but for a raw data pointer (pointing to raw binary, no ELF) void process_create_user_raw(char* file, int size, char* name) { + // Need to refactor this mess CLEAR_INTERRUPTS; struct process* proc = (struct process*)kmalloc(sizeof(struct process)); struct cpu_status* ctx = (struct cpu_status*)kmalloc(sizeof(struct cpu_status)); if (!proc || !ctx) panic(NULL, "out of memory while creating user process"); + memset(proc, 0, sizeof(struct process)); + memset(ctx, 0, sizeof(struct cpu_status)); + strncpy(proc->name, name, PROCESS_NAME_MAX); - memset(ctx, 0, sizeof(struct cpu_status)); // set GP registers to zero proc->pid = next_free_pid++; proc->status = READY; proc->next = 0; @@ -296,6 +255,24 @@ void process_create_user_raw(char* file, int size, char* name) proc->context->iret_cs = USER_CODE_SEGMENT | 3; proc->context->iret_flags = 0x202; // Interrupt Flag set + /* Set basic entries for the process's File Descriptor Table */ + proc->fdt[0].fd = 0; + proc->fdt[0].open = true; + proc->fdt[0].cursor = 0; + strncpy(proc->fdt[0].filename, "stdin", PROCESS_NAME_MAX - 1); + + proc->fdt[1].fd = 1; + proc->fdt[1].open = true; + proc->fdt[1].cursor = 0; + strncpy(proc->fdt[1].filename, "stdout", PROCESS_NAME_MAX - 1); + + proc->fdt[2].fd = 2; + proc->fdt[2].open = true; + proc->fdt[2].cursor = 0; + strncpy(proc->fdt[2].filename, "stderr", PROCESS_NAME_MAX - 1); + + proc->next_free_fd = 3; // file descriptors are also bump-allocated + void* exec_addr = (void*)file; uint64_t exec_size = size;