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.
This commit is contained in:
apio 2022-10-10 20:21:39 +02:00
parent 63b2de4e3c
commit da2ede3450
14 changed files with 174 additions and 20 deletions

View File

@ -5,6 +5,8 @@
#include <sys/syscall.h> #include <sys/syscall.h>
#include <unistd.h> #include <unistd.h>
typedef long ssize_t;
int main() int main()
{ {
if (gettid() == 0) // why are we the idle task? if (gettid() == 0) // why are we the idle task?
@ -25,14 +27,42 @@ int main()
sleep(2); 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); perror("open");
*variable = 3; // return 1;
printf("Allocated variable at address %p\n", variable); }
free(variable); 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; return 0;
} }

View File

@ -1,6 +1,9 @@
#pragma once #pragma once
#define EPERM 1 #define EPERM 1
#define ENOENT 2
#define EBADF 9
#define ENOMEM 12 #define ENOMEM 12
#define EINVAL 22 #define EINVAL 22
#define EMFILE 24
#define ENOSYS 38 #define ENOSYS 38

View File

@ -27,9 +27,8 @@ struct Descriptor
Descriptor(); Descriptor();
private: private:
uint64_t m_offset;
VFS::Node* m_node;
bool m_can_read;
bool m_is_open; bool m_is_open;
} bool m_can_read;
VFS::Node* m_node;
uint64_t m_offset;
};

View File

@ -12,6 +12,9 @@
#define SYS_gettid 7 #define SYS_gettid 7
#define SYS_mmap 8 #define SYS_mmap 8
#define SYS_munmap 9 #define SYS_munmap 9
#define SYS_open 10
#define SYS_read 11
#define SYS_close 12
namespace Syscall namespace Syscall
{ {
@ -27,4 +30,7 @@ void sys_rand(Context* context);
void sys_getversion(Context* context, char* buffer, size_t max); void sys_getversion(Context* context, char* buffer, size_t max);
void sys_gettid(Context* context); void sys_gettid(Context* context);
void sys_mmap(Context* context, void* address, size_t size, int flags); void sys_mmap(Context* context, void* address, size_t size, int flags);
void sys_munmap(Context* context, void* address, size_t size); 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);

View File

@ -1,7 +1,10 @@
#pragma once #pragma once
#include "fs/FileDescriptor.h"
#include "interrupts/Context.h" #include "interrupts/Context.h"
#include "sys/elf/Image.h" #include "sys/elf/Image.h"
#define TASK_MAX_FDS 8
struct Task struct Task
{ {
enum TaskState enum TaskState
@ -36,6 +39,8 @@ struct Task
bool is_user_task(); bool is_user_task();
ELFImage* image = nullptr; ELFImage* image = nullptr;
Descriptor files[TASK_MAX_FDS];
}; };
void set_context_from_task(Task& task, Context* ctx); void set_context_from_task(Task& task, Context* ctx);

View File

@ -5,7 +5,7 @@ Descriptor::Descriptor() : m_is_open(false)
} }
Descriptor::Descriptor(const Descriptor& other) 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_can_read = can_read;
m_node = node; m_node = node;
m_offset = 0; m_offset = 0;
m_is_open = true;
} }
ssize_t Descriptor::read(size_t size, char* buffer) ssize_t Descriptor::read(size_t size, char* buffer)

View File

@ -26,6 +26,9 @@ void Syscall::entry(Context* context)
case SYS_gettid: sys_gettid(context); break; case SYS_gettid: sys_gettid(context); break;
case SYS_mmap: sys_mmap(context, (void*)context->rdi, context->rsi, (int)context->rdx); 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_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; default: context->rax = -ENOSYS; break;
} }
} }

View File

@ -1,9 +1,108 @@
#define MODULE "stdio"
#include "errno.h"
#include "interrupts/Context.h" #include "interrupts/Context.h"
#include "io/Serial.h" #include "io/Serial.h"
#include "log/Log.h"
#include "render/TextRenderer.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) void sys_write(Context* context, const char* addr, size_t size)
{ {
context->rax = size; context->rax = size;
TextRenderer::write(addr, 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;
} }

View File

@ -21,7 +21,7 @@ typedef struct stackframe
void StackTracer::trace() void StackTracer::trace()
{ {
stackframe* frame = (stackframe*)m_base_pointer; stackframe* frame = (stackframe*)m_base_pointer;
while (Memory::is_kernel_address((uintptr_t)frame)) while (frame)
{ {
char symbol_name[512]; char symbol_name[512];
get_symbol_name(frame->instruction, symbol_name); get_symbol_name(frame->instruction, symbol_name);
@ -32,11 +32,6 @@ void StackTracer::trace()
void StackTracer::trace_with_ip(uintptr_t ip) void StackTracer::trace_with_ip(uintptr_t ip)
{ {
if (!Memory::is_kernel_address(ip))
{
printf("(user stack)");
return;
}
char symbol_name[512]; char symbol_name[512];
get_symbol_name(ip, symbol_name); get_symbol_name(ip, symbol_name);
printf("%lx: %s\n", ip, symbol_name); printf("%lx: %s\n", ip, symbol_name);

View File

@ -7,7 +7,7 @@
do { \ do { \
if (rc < 0) \ if (rc < 0) \
{ \ { \
errno = (int)rc; \ errno = (int)(-rc); \
return -1; \ return -1; \
} \ } \
return (type)rc; \ return (type)rc; \

View File

@ -4,8 +4,11 @@
extern int errno; extern int errno;
#define EPERM 1 #define EPERM 1
#define ENOENT 2
#define EBADF 9
#define ENOMEM 12 #define ENOMEM 12
#define EINVAL 22 #define EINVAL 22
#define EMFILE 24
#define ENOSYS 38 #define ENOSYS 38
#endif #endif

View File

@ -11,6 +11,9 @@
#define SYS_gettid 7 #define SYS_gettid 7
#define SYS_mmap 8 #define SYS_mmap 8
#define SYS_munmap 9 #define SYS_munmap 9
#define SYS_open 10
#define SYS_read 11
#define SYS_close 12
#ifndef __want_syscalls #ifndef __want_syscalls
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -121,7 +121,11 @@ extern "C"
case EINVAL: return "Invalid argument"; case EINVAL: return "Invalid argument";
case ENOMEM: return "Out of memory"; case ENOMEM: return "Out of memory";
case ENOSYS: return "Function not implemented"; 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;
} }
} }

View File

@ -35,15 +35,18 @@ extern "C"
case SYS_gettid: case SYS_gettid:
case SYS_rand: result = __luna_syscall0(number); break; case SYS_rand: result = __luna_syscall0(number); break;
case SYS_exit: case SYS_exit:
case SYS_close:
case SYS_sleep: result = __luna_syscall1(number, va_arg(ap, arg)); break; case SYS_sleep: result = __luna_syscall1(number, va_arg(ap, arg)); break;
case SYS_write: case SYS_write:
case SYS_munmap: case SYS_munmap:
case SYS_open:
case SYS_getversion: { case SYS_getversion: {
arg arg0 = va_arg(ap, arg); arg arg0 = va_arg(ap, arg);
arg arg1 = va_arg(ap, arg); arg arg1 = va_arg(ap, arg);
result = __luna_syscall2(number, arg0, arg1); result = __luna_syscall2(number, arg0, arg1);
break; break;
} }
case SYS_read:
case SYS_mmap: { case SYS_mmap: {
arg arg0 = va_arg(ap, arg); arg arg0 = va_arg(ap, arg);
arg arg1 = va_arg(ap, arg); arg arg1 = va_arg(ap, arg);