From da2ede345012d335ab84bde48593b9870ce28da7 Mon Sep 17 00:00:00 2001 From: apio Date: Mon, 10 Oct 2022 20:21:39 +0200 Subject: [PATCH] Kernel, libc, userspace: Implement file descriptors Kernel: Implement a descriptor struct which stores the opened node and read offset, and give each task 8 of those. Implement three syscalls: sys_read, sys_open and sys_close (sys_write still writes to the console instead of using a fd, for now) Implement three new errors: ENOENT, EBADF and EMFILE. libc: Implement the new errors, and the new syscalls in syscall(). Also fix _RETURN_WITH_ERRNO() to set errno correctly, which was making strerror() return null, thus crashing perror(). userspace: make init demonstrate the new file API. --- apps/src/init.c | 40 ++++++++++-- kernel/include/errno.h | 3 + kernel/include/fs/FileDescriptor.h | 9 ++- kernel/include/sys/Syscall.h | 8 ++- kernel/include/thread/Task.h | 5 ++ kernel/src/fs/FileDescriptor.cpp | 3 +- kernel/src/sys/Syscall.cpp | 3 + kernel/src/sys/stdio.cpp | 99 ++++++++++++++++++++++++++++++ kernel/src/trace/StackTracer.cpp | 7 +-- libs/libc/include/bits/error.h | 2 +- libs/libc/include/errno.h | 3 + libs/libc/include/luna/syscall.h | 3 + libs/libc/src/string.cpp | 6 +- libs/libc/src/unistd.cpp | 3 + 14 files changed, 174 insertions(+), 20 deletions(-) diff --git a/apps/src/init.c b/apps/src/init.c index b03c1d24..1d7c62b2 100644 --- a/apps/src/init.c +++ b/apps/src/init.c @@ -5,6 +5,8 @@ #include #include +typedef long ssize_t; + int main() { if (gettid() == 0) // why are we the idle task? @@ -25,14 +27,42 @@ int main() sleep(2); + const char* filename = "/sys/config"; + + printf("Opening %s for reading...\n", filename); + + int fd = syscall(SYS_open, filename, 1); + if (fd < 0) { - char* variable = malloc(200); - *variable = 3; - printf("Allocated variable at address %p\n", variable); - free(variable); + perror("open"); + // return 1; + } + else { printf("Got fd %d\n", fd); } + + char buf[4096]; + + ssize_t nread = syscall(SYS_read, fd, sizeof(buf), buf); + if (nread < 0) + { + perror("read"); + // return 1; + } + else + { + buf[nread] = 0; + + printf("Read %zd bytes\n\n", nread); + + printf("%s", buf); } - printf("Press any key to restart.\n"); + if (syscall(SYS_close, fd) < 0) + { + perror("close"); + // return 1; + } + + printf("\n\nPress any key to restart.\n"); return 0; } diff --git a/kernel/include/errno.h b/kernel/include/errno.h index 64cb4364..8c9786df 100644 --- a/kernel/include/errno.h +++ b/kernel/include/errno.h @@ -1,6 +1,9 @@ #pragma once #define EPERM 1 +#define ENOENT 2 +#define EBADF 9 #define ENOMEM 12 #define EINVAL 22 +#define EMFILE 24 #define ENOSYS 38 \ No newline at end of file diff --git a/kernel/include/fs/FileDescriptor.h b/kernel/include/fs/FileDescriptor.h index 798fa988..20b1199e 100644 --- a/kernel/include/fs/FileDescriptor.h +++ b/kernel/include/fs/FileDescriptor.h @@ -27,9 +27,8 @@ struct Descriptor Descriptor(); private: - uint64_t m_offset; - VFS::Node* m_node; - bool m_can_read; - bool m_is_open; -} \ No newline at end of file + bool m_can_read; + VFS::Node* m_node; + uint64_t m_offset; +}; \ No newline at end of file diff --git a/kernel/include/sys/Syscall.h b/kernel/include/sys/Syscall.h index e9564a53..9facde47 100644 --- a/kernel/include/sys/Syscall.h +++ b/kernel/include/sys/Syscall.h @@ -12,6 +12,9 @@ #define SYS_gettid 7 #define SYS_mmap 8 #define SYS_munmap 9 +#define SYS_open 10 +#define SYS_read 11 +#define SYS_close 12 namespace Syscall { @@ -27,4 +30,7 @@ void sys_rand(Context* context); void sys_getversion(Context* context, char* buffer, size_t max); void sys_gettid(Context* context); void sys_mmap(Context* context, void* address, size_t size, int flags); -void sys_munmap(Context* context, void* address, size_t size); \ No newline at end of file +void sys_munmap(Context* context, void* address, size_t size); +void sys_open(Context* context, const char* filename, int flags); +void sys_read(Context* context, int fd, size_t size, char* buffer); +void sys_close(Context* context, int fd); \ No newline at end of file diff --git a/kernel/include/thread/Task.h b/kernel/include/thread/Task.h index 4803d275..59239d24 100644 --- a/kernel/include/thread/Task.h +++ b/kernel/include/thread/Task.h @@ -1,7 +1,10 @@ #pragma once +#include "fs/FileDescriptor.h" #include "interrupts/Context.h" #include "sys/elf/Image.h" +#define TASK_MAX_FDS 8 + struct Task { enum TaskState @@ -36,6 +39,8 @@ struct Task bool is_user_task(); ELFImage* image = nullptr; + + Descriptor files[TASK_MAX_FDS]; }; void set_context_from_task(Task& task, Context* ctx); diff --git a/kernel/src/fs/FileDescriptor.cpp b/kernel/src/fs/FileDescriptor.cpp index 84f91692..4af4d25d 100644 --- a/kernel/src/fs/FileDescriptor.cpp +++ b/kernel/src/fs/FileDescriptor.cpp @@ -5,7 +5,7 @@ Descriptor::Descriptor() : m_is_open(false) } Descriptor::Descriptor(const Descriptor& other) - : m_is_open(other.is_open()), m_can_read(other.can_read()), m_node(other.m_node), m_offset(other.m_offset) + : m_is_open(other.m_is_open), m_can_read(other.m_can_read), m_node(other.m_node), m_offset(other.m_offset) { } @@ -14,6 +14,7 @@ void Descriptor::open(VFS::Node* node, bool can_read) m_can_read = can_read; m_node = node; m_offset = 0; + m_is_open = true; } ssize_t Descriptor::read(size_t size, char* buffer) diff --git a/kernel/src/sys/Syscall.cpp b/kernel/src/sys/Syscall.cpp index 70111a09..78c68ae5 100644 --- a/kernel/src/sys/Syscall.cpp +++ b/kernel/src/sys/Syscall.cpp @@ -26,6 +26,9 @@ void Syscall::entry(Context* context) case SYS_gettid: sys_gettid(context); break; case SYS_mmap: sys_mmap(context, (void*)context->rdi, context->rsi, (int)context->rdx); break; case SYS_munmap: sys_munmap(context, (void*)context->rdi, context->rsi); break; + case SYS_open: sys_open(context, (const char*)context->rdi, (int)context->rsi); break; + case SYS_read: sys_read(context, (int)context->rdi, context->rsi, (char*)context->rdx); break; + case SYS_close: sys_close(context, (int)context->rdi); break; default: context->rax = -ENOSYS; break; } } \ No newline at end of file diff --git a/kernel/src/sys/stdio.cpp b/kernel/src/sys/stdio.cpp index 857501bc..91b1a63d 100644 --- a/kernel/src/sys/stdio.cpp +++ b/kernel/src/sys/stdio.cpp @@ -1,9 +1,108 @@ +#define MODULE "stdio" + +#include "errno.h" #include "interrupts/Context.h" #include "io/Serial.h" +#include "log/Log.h" #include "render/TextRenderer.h" +#include "thread/Scheduler.h" +#include "thread/Task.h" + +#define STDIO_FAIL(function, error) kwarnln("%s failed with %s", #function, #error) void sys_write(Context* context, const char* addr, size_t size) { context->rax = size; TextRenderer::write(addr, size); +} + +void sys_open(Context* context, const char* filename, int flags) +{ + Task* current_task = Scheduler::current_task(); + int fd = -1; + for (fd = 0; fd < TASK_MAX_FDS; fd++) + { + if (!current_task->files[fd].is_open()) break; + } + if (fd == -1) + { + STDIO_FAIL(open, EMFILE); + context->rax = -EMFILE; + return; + } + VFS::Node* node = VFS::resolve_path(filename); + if (!node) + { + STDIO_FAIL(open, ENOENT); + context->rax = -ENOENT; + return; + } + current_task->files[fd].open(node, + (bool)flags); // FIXME: Implement more flags. (right now, 1 is can_read, 0 is not) + context->rax = fd; + return; +} + +void sys_read(Context* context, int fd, size_t size, char* buffer) +{ + if (!buffer) + { + STDIO_FAIL(read, EINVAL); + context->rax = -EINVAL; + return; + } + if (fd >= TASK_MAX_FDS) + { + STDIO_FAIL(read, EBADF); + context->rax = -EBADF; + return; + } + if (fd < 0) + { + STDIO_FAIL(read, EBADF); + context->rax = -EBADF; + return; + } + Task* current_task = Scheduler::current_task(); + if (!current_task->files[fd].is_open()) + { + STDIO_FAIL(read, EBADF); + context->rax = -EBADF; + return; + } + if (!current_task->files[fd].can_read()) + { + STDIO_FAIL(read, EBADF); + context->rax = -EBADF; + return; + } + ssize_t result = current_task->files[fd].read(size, buffer); + context->rax = (size_t)result; + return; +} + +void sys_close(Context* context, int fd) +{ + if (fd >= TASK_MAX_FDS) + { + STDIO_FAIL(close, EBADF); + context->rax = -EBADF; + return; + } + if (fd < 0) + { + STDIO_FAIL(close, EBADF); + context->rax = -EBADF; + return; + } + Task* current_task = Scheduler::current_task(); + if (!current_task->files[fd].is_open()) + { + STDIO_FAIL(close, EBADF); + context->rax = -EBADF; + return; + } + current_task->files[fd].close(); + context->rax = 0; + return; } \ No newline at end of file diff --git a/kernel/src/trace/StackTracer.cpp b/kernel/src/trace/StackTracer.cpp index 58cdbeeb..a44670c7 100644 --- a/kernel/src/trace/StackTracer.cpp +++ b/kernel/src/trace/StackTracer.cpp @@ -21,7 +21,7 @@ typedef struct stackframe void StackTracer::trace() { stackframe* frame = (stackframe*)m_base_pointer; - while (Memory::is_kernel_address((uintptr_t)frame)) + while (frame) { char symbol_name[512]; get_symbol_name(frame->instruction, symbol_name); @@ -32,11 +32,6 @@ void StackTracer::trace() void StackTracer::trace_with_ip(uintptr_t ip) { - if (!Memory::is_kernel_address(ip)) - { - printf("(user stack)"); - return; - } char symbol_name[512]; get_symbol_name(ip, symbol_name); printf("%lx: %s\n", ip, symbol_name); diff --git a/libs/libc/include/bits/error.h b/libs/libc/include/bits/error.h index 607695d8..43d4a195 100644 --- a/libs/libc/include/bits/error.h +++ b/libs/libc/include/bits/error.h @@ -7,7 +7,7 @@ do { \ if (rc < 0) \ { \ - errno = (int)rc; \ + errno = (int)(-rc); \ return -1; \ } \ return (type)rc; \ diff --git a/libs/libc/include/errno.h b/libs/libc/include/errno.h index 27deecbf..912dd606 100644 --- a/libs/libc/include/errno.h +++ b/libs/libc/include/errno.h @@ -4,8 +4,11 @@ extern int errno; #define EPERM 1 +#define ENOENT 2 +#define EBADF 9 #define ENOMEM 12 #define EINVAL 22 +#define EMFILE 24 #define ENOSYS 38 #endif \ No newline at end of file diff --git a/libs/libc/include/luna/syscall.h b/libs/libc/include/luna/syscall.h index d11a83a2..8c0715dc 100644 --- a/libs/libc/include/luna/syscall.h +++ b/libs/libc/include/luna/syscall.h @@ -11,6 +11,9 @@ #define SYS_gettid 7 #define SYS_mmap 8 #define SYS_munmap 9 +#define SYS_open 10 +#define SYS_read 11 +#define SYS_close 12 #ifndef __want_syscalls #ifdef __cplusplus diff --git a/libs/libc/src/string.cpp b/libs/libc/src/string.cpp index c99ef0ad..d35c6689 100644 --- a/libs/libc/src/string.cpp +++ b/libs/libc/src/string.cpp @@ -121,7 +121,11 @@ extern "C" case EINVAL: return "Invalid argument"; case ENOMEM: return "Out of memory"; case ENOSYS: return "Function not implemented"; - default: return 0; + case ENOENT: return "No such file or directory"; + case EBADF: return "Bad file descriptor"; + case EMFILE: return "Too many open files"; + case 0: return "Success"; + default: return (char*)(unsigned long int)errnum; } } diff --git a/libs/libc/src/unistd.cpp b/libs/libc/src/unistd.cpp index 783c39b8..bf1ca1e9 100644 --- a/libs/libc/src/unistd.cpp +++ b/libs/libc/src/unistd.cpp @@ -35,15 +35,18 @@ extern "C" case SYS_gettid: case SYS_rand: result = __luna_syscall0(number); break; case SYS_exit: + case SYS_close: case SYS_sleep: result = __luna_syscall1(number, va_arg(ap, arg)); break; case SYS_write: case SYS_munmap: + case SYS_open: case SYS_getversion: { arg arg0 = va_arg(ap, arg); arg arg1 = va_arg(ap, arg); result = __luna_syscall2(number, arg0, arg1); break; } + case SYS_read: case SYS_mmap: { arg arg0 = va_arg(ap, arg); arg arg1 = va_arg(ap, arg);