Compare commits
86 Commits
97461c7c1f
...
3c1146f2c5
Author | SHA1 | Date | |
---|---|---|---|
3c1146f2c5 | |||
511ad67a9a | |||
e17a21dbad | |||
494b48bbe3 | |||
4f41b9ed37 | |||
8b17065718 | |||
87ef210759 | |||
55808d5cc4 | |||
1e96a45f33 | |||
a2c05de604 | |||
c2ecc4fe95 | |||
593daba651 | |||
ea8a42b8c0 | |||
e7522c21ca | |||
34fc6996b0 | |||
92634048fc | |||
64f5078494 | |||
966fdc76d7 | |||
b334e1cd50 | |||
682be58d97 | |||
3bf4f854c7 | |||
8b70635e79 | |||
250db2c90f | |||
c2fa4f380d | |||
ce10fb5743 | |||
891651f2d6 | |||
e34045a78c | |||
9b39d618de | |||
a1146a5ce2 | |||
6d821d2638 | |||
f35bbe52b7 | |||
e1e86ab889 | |||
d2e2883a79 | |||
18fbccafb7 | |||
0a46feb162 | |||
d62eb6c791 | |||
f8154ce230 | |||
8c0a57f0c2 | |||
1624f0360d | |||
d30010d524 | |||
68403dc029 | |||
eca7227fda | |||
aca1367158 | |||
42b6b927c9 | |||
48f38bdcef | |||
45afd3e243 | |||
18f1f8b7ca | |||
dd358eca29 | |||
116e7326a4 | |||
5256166e7a | |||
6953a28ce8 | |||
f13c48b562 | |||
7139b4403f | |||
e3e33bacbc | |||
8d552b1522 | |||
46f60b192a | |||
9aa96de61d | |||
6507146c60 | |||
4bad782aad | |||
f50017912d | |||
2395c7a871 | |||
b1739f7f0d | |||
62a2bcf2ff | |||
3a9dddaa57 | |||
225284a6ca | |||
613f8170b6 | |||
62d631f1b4 | |||
a002e75725 | |||
3fde7e46f5 | |||
3e2a4276e9 | |||
94a6336e4d | |||
523e88e5a9 | |||
b0e071e964 | |||
3e2bebf0aa | |||
1e86acd4c0 | |||
36bb1cab5c | |||
5c61252061 | |||
3eb1bff2e9 | |||
c77e752a82 | |||
d0d6557e99 | |||
8398b2e2e4 | |||
1043b0772d | |||
d5bc87099f | |||
eb67ab113e | |||
91d76a2ee4 | |||
faaf930a14 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -7,6 +7,7 @@ kernel/bin/moon.elf
|
||||
initrd/sys/moon.sym
|
||||
initrd/bin/**
|
||||
apps/bin/**
|
||||
tests/**/bin/**
|
||||
base/usr/include/**
|
||||
base/usr/lib/**
|
||||
**/*.a
|
@ -6,11 +6,12 @@ Not so much at the moment.
|
||||
|
||||
- x86_64-compatible [kernel](kernel/).
|
||||
- Keeps track of which [memory](kernel/src/memory/) is used and which memory is free, and can allocate memory for itself and [user programs](kernel/src/sys/mem.cpp).
|
||||
- Can [load files](kernel/src/fs/) from an [initial ramdisk](initrd/), no disk support yet.
|
||||
- Can load files from a [virtual file system](kernel/src/fs/) supporting an initial ramdisk, device pseudo-filesystems... but no hard disks yet.
|
||||
- Basic preemptive multitasking, round-robin [scheduler](kernel/src/thread/) that can switch between tasks.
|
||||
- Can [load userspace ELF programs](kernel/src/sys/elf/) from the initial ramdisk as user tasks.
|
||||
- [System call](kernel/src/sys/) interface and bare-bones [C Library](libs/libc/).
|
||||
- Some very simple [example programs](apps/), written in C, that the kernel then loads from the initial ramdisk.
|
||||
- Can [load userspace ELF programs](kernel/src/sys/elf/) from the file system as user tasks.
|
||||
- [System call](kernel/src/sys/) interface and simple [C Library](libs/libc/), aiming to be POSIX-compatible.
|
||||
- Some very simple [example programs](apps/), written in C, that then can get loaded and executed by the kernel.
|
||||
- UNIX-like [multitasking primitives](kernel/src/sys/exec.cpp), which allow user tasks to spawn other tasks.
|
||||
|
||||
## Setup
|
||||
To build and run Luna, you will need to build a [GCC Cross-Compiler](https://wiki.osdev.org/Why_do_I_need_a_Cross_Compiler%3F) and cross-binutils for `x86_64-luna`. (Yes, Luna is advanced enough that it can use its own [OS-Specific Toolchain](https://wiki.osdev.org/OS_Specific_Toolchain), instead of a bare metal target like `x86_64-elf`. It is the first of my OS projects to be able to do so. The patches for Binutils and GCC are [binutils.patch](tools/binutils.patch) and [gcc.patch](tools/gcc.patch)).
|
||||
|
@ -11,7 +11,6 @@ CFLAGS := -Wall -Wextra -Werror -Os
|
||||
$(APPS_BIN)/%: $(APPS_SRC)/%.c
|
||||
@mkdir -p $(@D)
|
||||
$(CC) $(CFLAGS) -o $@ $^
|
||||
$(STRIP) $@
|
||||
|
||||
build: $(REAL_APPS)
|
||||
|
||||
|
@ -1,26 +1,24 @@
|
||||
#include <errno.h>
|
||||
#include <luna.h>
|
||||
#include <setjmp.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
typedef long ssize_t;
|
||||
|
||||
int print_version()
|
||||
{
|
||||
char version[4096];
|
||||
|
||||
FILE* verfile = fopen("/dev/version", "r");
|
||||
if (!verfile)
|
||||
FILE* fp = fopen("/dev/version", "r");
|
||||
if (!fp)
|
||||
{
|
||||
perror("fopen");
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t nread = fread(version, 4096, 1, verfile);
|
||||
if (ferror(verfile))
|
||||
size_t nread = fread(version, 4096, 1, fp);
|
||||
if (ferror(fp))
|
||||
{
|
||||
perror("fread");
|
||||
return 1;
|
||||
@ -28,7 +26,7 @@ int print_version()
|
||||
|
||||
version[nread] = 0;
|
||||
|
||||
if (fclose(verfile) < 0)
|
||||
if (fclose(fp) < 0)
|
||||
{
|
||||
perror("fclose");
|
||||
return 1;
|
||||
@ -41,9 +39,9 @@ int print_version()
|
||||
|
||||
int main()
|
||||
{
|
||||
if (gettid() == 0) // why are we the idle task?
|
||||
if (getpid() != 1)
|
||||
{
|
||||
fprintf(stderr, "SHENANIGANS! init is tid 0 (which is reserved for the idle task)\n");
|
||||
fprintf(stderr, "init should be started as PID 1\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -66,7 +64,7 @@ int main()
|
||||
|
||||
printf("Welcome to Luna!\n");
|
||||
|
||||
printf("Running as tid %ld\n\n", gettid());
|
||||
printf("Running as PID %ld\n\n", getpid());
|
||||
|
||||
sleep(1);
|
||||
|
||||
@ -117,9 +115,69 @@ int main()
|
||||
|
||||
if (fclose(config) < 0) { perror("fclose"); }
|
||||
|
||||
printf("\n\nGot random number %d\n\n", rand());
|
||||
|
||||
sleep(2);
|
||||
|
||||
printf("\n\nPress any key to restart.\n\n");
|
||||
printf("Press any key to restart.\n\n");
|
||||
|
||||
int stderr_fd = fileno(stderr);
|
||||
int new_stderr_fd = dup(stderr_fd);
|
||||
if (new_stderr_fd < 0)
|
||||
{
|
||||
perror("dup");
|
||||
return 1;
|
||||
}
|
||||
FILE* new_stderr = fdopen(new_stderr_fd, "rw");
|
||||
if (!new_stderr)
|
||||
{
|
||||
perror("fdopen");
|
||||
return 1;
|
||||
}
|
||||
|
||||
fprintf(new_stderr, "Bye!\n\n");
|
||||
|
||||
fclose(new_stderr);
|
||||
|
||||
const char* pathname = "/etc";
|
||||
|
||||
printf("Creating directory %s\n", pathname);
|
||||
|
||||
if (mkdir(pathname, 0) < 0)
|
||||
{
|
||||
perror("mkdir");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("Success!!\n");
|
||||
|
||||
printf("Forking...\n");
|
||||
|
||||
pid_t child = fork();
|
||||
|
||||
if (child < 0)
|
||||
{
|
||||
perror("fork");
|
||||
return 1;
|
||||
}
|
||||
if (child == 0)
|
||||
{
|
||||
msleep(500);
|
||||
printf("I am the child, who is my parent?\n");
|
||||
execv("/bin/sym", NULL);
|
||||
perror("execv");
|
||||
return 1;
|
||||
}
|
||||
else { printf("Success!! Got PID %ld\n", child); }
|
||||
|
||||
jmp_buf env;
|
||||
int val = setjmp(env);
|
||||
if (val == 0) { printf("Returning from setjmp!\n"); }
|
||||
else
|
||||
{
|
||||
printf("Returning from longjmp! val=%d\n", val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
longjmp(env, 3);
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
int main()
|
||||
{
|
||||
sleep(6);
|
||||
sleep(2);
|
||||
|
||||
FILE* syms = fopen("/sys/moon.sym", "r");
|
||||
if (!syms)
|
||||
@ -13,15 +13,15 @@ int main()
|
||||
return 1;
|
||||
}
|
||||
|
||||
char buf[1025];
|
||||
char buf[1200];
|
||||
|
||||
if (fseek(syms, 8000, SEEK_SET) < 0)
|
||||
if (fseek(syms, -1199, SEEK_END) < 0)
|
||||
{
|
||||
perror("fseek");
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t nread = fread(buf, 1024, 1, syms);
|
||||
size_t nread = fread(buf, 1199, 1, syms);
|
||||
if (ferror(syms))
|
||||
{
|
||||
perror("fread");
|
||||
|
@ -1,39 +0,0 @@
|
||||
#pragma once
|
||||
#include "log/Log.h"
|
||||
#include "misc/hang.h"
|
||||
#include "thread/Scheduler.h"
|
||||
#include "trace/StackTracer.h"
|
||||
|
||||
#define __call_assert_fail(...) \
|
||||
kerrorln(__VA_ARGS__); \
|
||||
StackTracer tracer; \
|
||||
tracer.trace(); \
|
||||
hang();
|
||||
|
||||
#define ASSERT(expr) \
|
||||
do { \
|
||||
if (!(expr)) \
|
||||
{ \
|
||||
Task* cur_task = Scheduler::current_task(); \
|
||||
if (cur_task) \
|
||||
{ \
|
||||
__call_assert_fail("Assertion failed in task %ld at %s, line %d: %s", cur_task->id, __FILE__, \
|
||||
__LINE__, #expr); \
|
||||
} \
|
||||
else { __call_assert_fail("Assertion failed at %s, line %d: %s", __FILE__, __LINE__, #expr); } \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define TODO(message) \
|
||||
do { \
|
||||
Task* cur_task = Scheduler::current_task(); \
|
||||
if (cur_task) \
|
||||
{ \
|
||||
__call_assert_fail("TODO in task %ld at %s, line %d: %s", cur_task->id, __FILE__, __LINE__, message); \
|
||||
} \
|
||||
else { __call_assert_fail("TODO at %s, line %d: %s", __FILE__, __LINE__, message); } \
|
||||
} while (0)
|
||||
|
||||
#ifdef ___weird_hack_to_put_something_at_end_of_file
|
||||
#undef ___weird_hack_to_put_something_at_end_of_file
|
||||
#endif
|
@ -6,7 +6,11 @@
|
||||
#define EBADF 9
|
||||
#define ENOMEM 12
|
||||
#define EFAULT 14
|
||||
#define EEXIST 17
|
||||
#define ENOTDIR 20
|
||||
#define EISDIR 21
|
||||
#define EINVAL 22
|
||||
#define EMFILE 24
|
||||
#define ENOSPC 28
|
||||
#define ENOSYS 38
|
||||
#define ENOTSUP 95
|
@ -43,6 +43,8 @@ struct Descriptor
|
||||
Descriptor(const Descriptor& other);
|
||||
Descriptor();
|
||||
|
||||
const Descriptor& operator=(const Descriptor& other);
|
||||
|
||||
private:
|
||||
bool m_is_open;
|
||||
bool m_can_read;
|
||||
|
@ -35,12 +35,15 @@ namespace VFS
|
||||
|
||||
ssize_t read(Node* node, size_t offset, size_t length, char* buffer);
|
||||
ssize_t write(Node* node, size_t offset, size_t length, const char* buffer);
|
||||
int mkdir(const char* path, const char* name); // FIXME: Support deducing this via a single path.
|
||||
int mkdir(const char* path, const char* name);
|
||||
int mkdir(const char* pathname);
|
||||
|
||||
void mount_root(Node* root);
|
||||
|
||||
Node* resolve_path(const char* filename, Node* root = nullptr);
|
||||
|
||||
bool exists(const char* pathname);
|
||||
|
||||
void mount(Node* mountpoint, Node* mounted);
|
||||
void mount(const char* pathname, Node* mounted);
|
||||
|
||||
|
9
kernel/include/fs/devices/Random.h
Normal file
9
kernel/include/fs/devices/Random.h
Normal file
@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
#include "fs/VFS.h"
|
||||
|
||||
namespace RandomDevice
|
||||
{
|
||||
VFS::Node* create_new(const char* devname);
|
||||
|
||||
ssize_t read(VFS::Node* node, size_t offset, size_t size, char* buffer);
|
||||
}
|
6
kernel/include/kassert.h
Normal file
6
kernel/include/kassert.h
Normal file
@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
#include "panic/Panic.h"
|
||||
|
||||
#define ASSERT(expr) (bool)(expr) || panic("Assertion failed: " #expr)
|
||||
|
||||
#define TODO(message) panic("TODO: " message)
|
@ -22,6 +22,7 @@ namespace KernelLog
|
||||
void logln(const char* function, LogLevel level, const char* message, ...) PRINTF_LIKE(3, 4);
|
||||
void toggle_log_level(LogLevel level);
|
||||
void toggle_log_backend(Backend backend);
|
||||
void enable_log_backend(Backend backend);
|
||||
}
|
||||
|
||||
#ifndef MODULE
|
||||
|
@ -7,7 +7,7 @@ struct AddressSpace
|
||||
|
||||
void destroy();
|
||||
|
||||
void detach();
|
||||
void clear();
|
||||
|
||||
AddressSpace clone();
|
||||
|
||||
@ -16,12 +16,6 @@ struct AddressSpace
|
||||
return m_pml4;
|
||||
}
|
||||
|
||||
bool is_cloned()
|
||||
{
|
||||
return m_cloned;
|
||||
}
|
||||
|
||||
private:
|
||||
PageTable* m_pml4;
|
||||
bool m_cloned;
|
||||
};
|
27
kernel/include/memory/UserHeap.h
Normal file
27
kernel/include/memory/UserHeap.h
Normal file
@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
struct UserHeap
|
||||
{
|
||||
bool init();
|
||||
|
||||
uint64_t request_virtual_page();
|
||||
uint64_t request_virtual_pages(uint64_t count);
|
||||
|
||||
void free_virtual_page(uint64_t address);
|
||||
void free_virtual_pages(uint64_t address, uint64_t count);
|
||||
|
||||
void free();
|
||||
|
||||
bool inherit(UserHeap& other);
|
||||
|
||||
private:
|
||||
uint8_t* bitmap = nullptr;
|
||||
uint64_t bitmap_size = 0;
|
||||
uint64_t start_index = 0;
|
||||
bool bitmap_read(uint64_t index);
|
||||
void bitmap_set(uint64_t index, bool value);
|
||||
|
||||
bool try_expand();
|
||||
bool try_expand_size(uint64_t size);
|
||||
};
|
@ -5,13 +5,11 @@
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
[[noreturn]] void __do_int_panic(Context* context, const char* file, int line, const char* message);
|
||||
[[noreturn]] void __do_panic(Context* context, const char* message);
|
||||
|
||||
[[noreturn]] void __panic(const char* message);
|
||||
[[noreturn]] bool __do_int_panic(Context* context, const char* file, int line, const char* message);
|
||||
[[noreturn]] bool __do_panic(const char* file, int line, const char* message);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#define panic(message) asm volatile("cli\npush $16\npushq %%rsp\npushfq\npush $8\ncall __panic" : : "D"(message))
|
||||
#define panic(message) __do_panic(__FILE__, __LINE__, message)
|
||||
#define int_panic(context, message) __do_int_panic(context, __FILE__, __LINE__, message)
|
4
kernel/include/std/libgen.h
Normal file
4
kernel/include/std/libgen.h
Normal file
@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
char* basename(char* path);
|
||||
char* dirname(char* path);
|
@ -8,6 +8,7 @@ __attribute__((deprecated)) int strcmp(const char* a, const char* b);
|
||||
__attribute__((deprecated)) char* strcat(char* dest, const char* src);
|
||||
|
||||
char* strncpy(char* dest, const char* src, size_t n);
|
||||
size_t strlcpy(char* dest, const char* src, size_t size);
|
||||
int strncmp(const char* a, const char* b, size_t n);
|
||||
char* strncat(char* dest, const char* src, size_t n);
|
||||
|
||||
@ -19,3 +20,4 @@ int memcmp(const void* a, const void* b, size_t n);
|
||||
void* memmove(void* dest, void* src, size_t n);
|
||||
|
||||
char* strdup(const char* src);
|
||||
char* strrchr(const char* str, int c);
|
@ -7,15 +7,19 @@
|
||||
#define SYS_sleep 2
|
||||
#define SYS_write 3
|
||||
#define SYS_paint 4
|
||||
#define SYS_rand 5
|
||||
#define SYS_gettid 6
|
||||
#define SYS_mmap 7
|
||||
#define SYS_munmap 8
|
||||
#define SYS_open 9
|
||||
#define SYS_read 10
|
||||
#define SYS_close 11
|
||||
#define SYS_seek 12
|
||||
#define SYS_exec 13
|
||||
#define SYS_getpid 5
|
||||
#define SYS_mmap 6
|
||||
#define SYS_munmap 7
|
||||
#define SYS_open 8
|
||||
#define SYS_read 9
|
||||
#define SYS_close 10
|
||||
#define SYS_seek 11
|
||||
#define SYS_exec 12
|
||||
#define SYS_fcntl 13
|
||||
#define SYS_mprotect 14
|
||||
#define SYS_clock 15
|
||||
#define SYS_mkdir 16
|
||||
#define SYS_fork 17
|
||||
|
||||
namespace Syscall
|
||||
{
|
||||
@ -29,12 +33,16 @@ void sys_yield(Context* context);
|
||||
void sys_sleep(Context* context, uint64_t ms);
|
||||
void sys_write(Context* context, int fd, size_t size, const char* addr);
|
||||
void sys_paint(Context* context, uint64_t x, uint64_t y, uint64_t w, uint64_t h, uint64_t col);
|
||||
void sys_rand(Context* context);
|
||||
void sys_gettid(Context* context);
|
||||
void sys_mmap(Context* context, void* address, size_t size, int flags);
|
||||
void sys_getpid(Context* context);
|
||||
void sys_mmap(Context* context, void* address, size_t size, int prot);
|
||||
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);
|
||||
void sys_seek(Context* context, int fd, long offset, int whence);
|
||||
void sys_exec(Context* context, const char* pathname);
|
||||
void sys_fcntl(Context* context, int fd, int command, uintptr_t arg);
|
||||
void sys_mprotect(Context* context, void* address, size_t size, int prot);
|
||||
void sys_clock(Context* context);
|
||||
void sys_mkdir(Context* context, const char* filename);
|
||||
void sys_fork(Context* context);
|
@ -9,11 +9,11 @@ namespace Scheduler
|
||||
void yield();
|
||||
void exit(int status);
|
||||
void sleep(unsigned long ms);
|
||||
void add_kernel_task(void (*task)(void));
|
||||
void add_kernel_task(const char* taskname, void (*task)(void));
|
||||
|
||||
Task* create_user_task();
|
||||
|
||||
void load_user_task(const char* filename);
|
||||
long load_user_task(const char* filename);
|
||||
|
||||
void task_exit(Context* context, int64_t status);
|
||||
void task_misbehave(Context* context, int64_t status);
|
||||
@ -27,4 +27,8 @@ namespace Scheduler
|
||||
void reap_tasks();
|
||||
|
||||
void reset_task(Task* task, ELFImage* new_image);
|
||||
|
||||
void append_task(Task* task);
|
||||
|
||||
Task* find_by_pid(uint64_t pid);
|
||||
}
|
@ -2,9 +2,10 @@
|
||||
#include "fs/FileDescriptor.h"
|
||||
#include "interrupts/Context.h"
|
||||
#include "memory/AddressSpace.h"
|
||||
#include "memory/UserHeap.h"
|
||||
#include "sys/elf/Image.h"
|
||||
|
||||
#define TASK_MAX_FDS 8
|
||||
#define TASK_MAX_FDS 32
|
||||
|
||||
struct Task
|
||||
{
|
||||
@ -46,10 +47,22 @@ struct Task
|
||||
Descriptor files[TASK_MAX_FDS];
|
||||
|
||||
AddressSpace address_space;
|
||||
|
||||
UserHeap allocator;
|
||||
|
||||
int alloc_fd();
|
||||
|
||||
int alloc_fd_greater_than_or_equal(int base_fd);
|
||||
|
||||
void save_context(Context* context);
|
||||
void restore_context(Context* context);
|
||||
|
||||
void save_floating();
|
||||
void restore_floating();
|
||||
|
||||
void switch_to_address_space();
|
||||
|
||||
bool has_died();
|
||||
|
||||
char name[128];
|
||||
};
|
||||
|
||||
void set_context_from_task(Task& task, Context* ctx);
|
||||
void get_context_to_task(Task& task, Context* ctx);
|
||||
|
||||
void task_save_floating(Task& task);
|
||||
void task_restore_floating(Task& task);
|
@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
void get_symbol_name(uintptr_t address, char* buffer);
|
||||
void get_symbol_name(uintptr_t address, char* buffer, size_t size);
|
@ -41,3 +41,13 @@ int Descriptor::seek(long offset)
|
||||
m_offset = (uint64_t)offset;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const Descriptor& Descriptor::operator=(const Descriptor& other)
|
||||
{
|
||||
m_is_open = other.m_is_open;
|
||||
m_can_read = other.m_can_read;
|
||||
m_can_write = other.m_can_write;
|
||||
m_offset = other.m_offset;
|
||||
m_node = other.m_node;
|
||||
return other;
|
||||
}
|
@ -3,6 +3,7 @@
|
||||
#include "fs/VFS.h"
|
||||
#include "errno.h"
|
||||
#include "log/Log.h"
|
||||
#include "std/libgen.h"
|
||||
#include "std/stdlib.h"
|
||||
#include "std/string.h"
|
||||
|
||||
@ -131,16 +132,54 @@ int VFS::mkdir(const char* path, const char* name)
|
||||
if (!node)
|
||||
{
|
||||
kwarnln("Attempting to mkdir in %s, which does not exist", path);
|
||||
return -1;
|
||||
return -ENOENT;
|
||||
}
|
||||
if (node->type != VFS_DIRECTORY)
|
||||
{
|
||||
kwarnln("Attempting to mkdir in %s, which is not a directory!!", path);
|
||||
return -ENOTDIR;
|
||||
}
|
||||
if (!node->mkdir_func)
|
||||
{
|
||||
kwarnln("Chosen node does not support mkdir()");
|
||||
return -1;
|
||||
return -ENOTSUP;
|
||||
}
|
||||
if (!node->find_func)
|
||||
{
|
||||
kwarnln("Chosen node does not support finddir()");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
if (node->find_func(node, name) != nullptr)
|
||||
{
|
||||
kwarnln("Already exists");
|
||||
return -EEXIST;
|
||||
}
|
||||
return node->mkdir_func(node, name);
|
||||
}
|
||||
|
||||
int VFS::mkdir(const char* pathname)
|
||||
{
|
||||
char* bstr = strdup(pathname);
|
||||
char* dstr = strdup(pathname);
|
||||
|
||||
char* base = basename(bstr);
|
||||
char* dir = dirname(dstr);
|
||||
|
||||
kdbgln("mkdir(): creating %s in directory %s", base, dir);
|
||||
|
||||
int result = mkdir(dir, base);
|
||||
|
||||
kfree(bstr);
|
||||
kfree(dstr);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool VFS::exists(const char* pathname)
|
||||
{
|
||||
return resolve_path(pathname) != nullptr;
|
||||
}
|
||||
|
||||
void VFS::mount(Node* mountpoint, Node* mounted)
|
||||
{
|
||||
if (!mountpoint || !mounted) return;
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include "fs/devices/DeviceFS.h"
|
||||
#include "fs/devices/Console.h"
|
||||
#include "fs/devices/Random.h"
|
||||
#include "fs/devices/Serial.h"
|
||||
#include "fs/devices/Version.h"
|
||||
#include "std/stdlib.h"
|
||||
@ -25,6 +26,7 @@ VFS::Node* DeviceFS::get()
|
||||
devfs_files[devfs_file_count++] = VersionDevice::create_new("version");
|
||||
devfs_files[devfs_file_count++] = ConsoleDevice::create_new("console");
|
||||
devfs_files[devfs_file_count++] = SerialDevice::create_new("serial");
|
||||
devfs_files[devfs_file_count++] = RandomDevice::create_new("random");
|
||||
return devfs_root;
|
||||
}
|
||||
|
||||
|
38
kernel/src/fs/devices/Random.cpp
Normal file
38
kernel/src/fs/devices/Random.cpp
Normal file
@ -0,0 +1,38 @@
|
||||
#include "fs/devices/Random.h"
|
||||
#include "config.h"
|
||||
#include "rand/Mersenne.h"
|
||||
#include "render/TextRenderer.h"
|
||||
#include "std/stdio.h"
|
||||
#include "std/stdlib.h"
|
||||
#include "std/string.h"
|
||||
|
||||
VFS::Node* RandomDevice::create_new(const char* devname)
|
||||
{
|
||||
VFS::Node* dev = new VFS::Node;
|
||||
dev->read_func = RandomDevice::read;
|
||||
dev->inode = 0;
|
||||
dev->length = 0;
|
||||
dev->type = VFS_DEVICE;
|
||||
dev->flags = 0;
|
||||
strncpy(dev->name, devname, sizeof(dev->name));
|
||||
return dev;
|
||||
}
|
||||
|
||||
ssize_t RandomDevice::read(VFS::Node* node, size_t, size_t size, char* buffer)
|
||||
{
|
||||
if (!node) return -1;
|
||||
size_t nread = size;
|
||||
while (size >= sizeof(uint64_t))
|
||||
{
|
||||
*(uint64_t*)buffer = Mersenne::get();
|
||||
size -= sizeof(uint64_t);
|
||||
buffer += sizeof(uint64_t);
|
||||
}
|
||||
while (size)
|
||||
{
|
||||
*buffer = (char)(Mersenne::get() & 0xFF);
|
||||
size--;
|
||||
buffer++;
|
||||
}
|
||||
return (ssize_t)nread;
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
#define MODULE "gdt"
|
||||
|
||||
#include "gdt/GDT.h"
|
||||
#include "assert.h"
|
||||
#include "kassert.h"
|
||||
#include "log/Log.h"
|
||||
#include "memory/MemoryManager.h"
|
||||
#include "std/string.h"
|
||||
|
@ -1,12 +1,12 @@
|
||||
#define MODULE "init"
|
||||
|
||||
#include "init/Init.h"
|
||||
#include "assert.h"
|
||||
#include "bootboot.h"
|
||||
#include "cpu/CPU.h"
|
||||
#include "init/InitRD.h"
|
||||
#include "interrupts/Interrupts.h"
|
||||
#include "io/Serial.h"
|
||||
#include "kassert.h"
|
||||
#include "log/Log.h"
|
||||
#include "memory/MemoryManager.h"
|
||||
#include "memory/PMM.h"
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include "init/InitRD.h"
|
||||
#include "bootboot.h"
|
||||
#include "errno.h"
|
||||
#include "fs/VFS.h"
|
||||
#include "io/Serial.h"
|
||||
#include "log/Log.h"
|
||||
@ -190,23 +191,23 @@ int initrd_mkdir(VFS::Node* node, const char* name) // FIXME: Return proper erro
|
||||
if (total_dirs >= 32)
|
||||
{
|
||||
kwarnln("mkdir() failed: too many directories");
|
||||
return -1;
|
||||
return -ENOSPC;
|
||||
}
|
||||
if (node->inode > total_dirs)
|
||||
{
|
||||
kwarnln("mkdir() failed: invalid node");
|
||||
return -1;
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!(node->type & VFS_DIRECTORY))
|
||||
{
|
||||
kwarnln("mkdir() failed: not a directory");
|
||||
return -1;
|
||||
return -ENOTDIR;
|
||||
}
|
||||
InitRD::Directory& parent = dirs[node->inode];
|
||||
if (parent.entries == INITRD_MAX_FILES_IN_DIR)
|
||||
{
|
||||
kwarnln("mkdir() failed: parent is null");
|
||||
return -1;
|
||||
kwarnln("mkdir() failed: parent is full");
|
||||
return -ENOSPC;
|
||||
}
|
||||
uint64_t inode = total_dirs;
|
||||
VFS::Node& new_node = nodes[total_nodes++];
|
||||
|
@ -1,10 +1,10 @@
|
||||
#define MODULE "isr"
|
||||
|
||||
#include "assert.h"
|
||||
#include "interrupts/Context.h"
|
||||
#include "interrupts/IRQ.h"
|
||||
#include "interrupts/Interrupts.h"
|
||||
#include "io/Serial.h"
|
||||
#include "kassert.h"
|
||||
#include "log/Log.h"
|
||||
#include "misc/hang.h"
|
||||
#include "panic/Panic.h"
|
||||
|
@ -1,7 +1,7 @@
|
||||
#define MODULE "idt"
|
||||
|
||||
#include "interrupts/IDT.h"
|
||||
#include "assert.h"
|
||||
#include "kassert.h"
|
||||
#include "log/Log.h"
|
||||
|
||||
struct IDTEntry
|
||||
|
@ -81,3 +81,8 @@ void KernelLog::toggle_log_backend(Backend backend)
|
||||
{
|
||||
backend_mask ^= (1 << (int)backend);
|
||||
}
|
||||
|
||||
void KernelLog::enable_log_backend(Backend backend)
|
||||
{
|
||||
backend_mask |= (1 << (int)backend);
|
||||
}
|
@ -1,40 +1,26 @@
|
||||
#define MODULE "main"
|
||||
|
||||
#include "acpi/RSDT.h"
|
||||
#include "assert.h"
|
||||
#include "config.h"
|
||||
#include "cpu/CPU.h"
|
||||
#include "fs/VFS.h"
|
||||
#include "fs/devices/DeviceFS.h"
|
||||
#include "gdt/GDT.h"
|
||||
#include "init/Init.h"
|
||||
#include "init/InitRD.h"
|
||||
#include "interrupts/IDT.h"
|
||||
#include "interrupts/Install.h"
|
||||
#include "interrupts/Interrupts.h"
|
||||
#include "io/PCI.h"
|
||||
#include "io/PIC.h"
|
||||
#include "io/Serial.h"
|
||||
#include "kassert.h"
|
||||
#include "log/Log.h"
|
||||
#include "memory/AddressSpace.h"
|
||||
#include "memory/Memory.h"
|
||||
#include "memory/MemoryManager.h"
|
||||
#include "memory/MemoryMap.h"
|
||||
#include "memory/PMM.h"
|
||||
#include "memory/VMM.h"
|
||||
#include "misc/PCITypes.h"
|
||||
#include "misc/reboot.h"
|
||||
#include "panic/Panic.h"
|
||||
#include "rand/Mersenne.h"
|
||||
#include "render/Framebuffer.h"
|
||||
#include "render/TextRenderer.h"
|
||||
#include "std/stdio.h"
|
||||
#include "misc/hang.h"
|
||||
#include "std/stdlib.h"
|
||||
#include "std/string.h"
|
||||
#include "sys/elf/ELFLoader.h"
|
||||
#include "thread/PIT.h"
|
||||
#include "thread/Scheduler.h"
|
||||
|
||||
#define STRINGIZE(x) #x
|
||||
#define STRINGIZE_VALUE_OF(x) STRINGIZE(x)
|
||||
|
||||
extern "C" void _start()
|
||||
{
|
||||
Init::check_magic();
|
||||
@ -65,7 +51,13 @@ extern "C" void _start()
|
||||
|
||||
kinfoln("Prepared scheduler");
|
||||
|
||||
Scheduler::add_kernel_task([]() {
|
||||
#ifdef RUN_TEST_AS_INIT
|
||||
ASSERT(Scheduler::load_user_task(STRINGIZE_VALUE_OF(RUN_TEST_AS_INIT)) > 0);
|
||||
#else
|
||||
ASSERT(Scheduler::load_user_task("/bin/init") > 0);
|
||||
#endif
|
||||
|
||||
Scheduler::add_kernel_task("[moon-reaper]", []() {
|
||||
while (1)
|
||||
{
|
||||
sleep(400);
|
||||
@ -73,24 +65,18 @@ extern "C" void _start()
|
||||
}
|
||||
});
|
||||
|
||||
Scheduler::load_user_task("/bin/init");
|
||||
|
||||
kinfoln("Prepared scheduler tasks");
|
||||
|
||||
ASSERT(VFS::mkdir("/", "dev") == 0);
|
||||
ASSERT(VFS::mkdir("/dev") == 0);
|
||||
VFS::mount("/dev", DeviceFS::get());
|
||||
|
||||
Init::finish_kernel_boot();
|
||||
|
||||
PIC::remap();
|
||||
PIC::enable_master(0b11111100); // enable keyboard and PIT
|
||||
PIC::enable_master(0b11111100);
|
||||
PIC::enable_slave(0b11111111);
|
||||
|
||||
kinfoln("Interrupts enabled");
|
||||
Interrupts::enable();
|
||||
|
||||
PCI::scan([](PCI::Device& dev) {
|
||||
kinfoln("Found PCI device %x:%x, %s", dev.id().vendor, dev.id().device, pci_type_name(dev.type()));
|
||||
});
|
||||
|
||||
Scheduler::exit(0);
|
||||
while (1) halt();
|
||||
}
|
@ -4,24 +4,20 @@
|
||||
#include "log/Log.h"
|
||||
#include "memory/PMM.h"
|
||||
#include "memory/VMM.h"
|
||||
#include "std/stdlib.h"
|
||||
#include "std/string.h"
|
||||
#include "utils/move.h"
|
||||
|
||||
AddressSpace AddressSpace::create()
|
||||
{
|
||||
AddressSpace result;
|
||||
result.m_pml4 = (PageTable*)PMM::request_page();
|
||||
result.m_cloned = false;
|
||||
VMM::install_kernel_page_directory_into_address_space(result);
|
||||
return move(result);
|
||||
}
|
||||
|
||||
void AddressSpace::destroy()
|
||||
{
|
||||
if (m_cloned)
|
||||
{
|
||||
kdbgln("Will not destroy a cloned address space, I don't own it");
|
||||
return;
|
||||
}
|
||||
uint64_t pages_freed = 0;
|
||||
for (int i = 0; i < 512; i++)
|
||||
{
|
||||
@ -78,18 +74,128 @@ void AddressSpace::destroy()
|
||||
kdbgln("Reclaimed %ld pages from address space!", pages_freed);
|
||||
}
|
||||
|
||||
void AddressSpace::detach()
|
||||
void AddressSpace::clear()
|
||||
{
|
||||
if (!m_cloned) return;
|
||||
m_pml4 = (PageTable*)PMM::request_page();
|
||||
VMM::install_kernel_page_directory_into_address_space(*this);
|
||||
m_cloned = false;
|
||||
uint64_t pages_freed = 0;
|
||||
for (int i = 0; i < 512; i++)
|
||||
{
|
||||
PageDirectoryEntry& pdp_pde = m_pml4->entries[i];
|
||||
if (!pdp_pde.present) continue;
|
||||
if (pdp_pde.larger_pages)
|
||||
{
|
||||
pages_freed++;
|
||||
PMM::free_page((void*)pdp_pde.get_address());
|
||||
continue;
|
||||
}
|
||||
PageTable* pdp = (PageTable*)pdp_pde.get_address();
|
||||
for (int j = 0; j < 511; j++) // skip the last page directory, it's the kernel one
|
||||
{
|
||||
PageDirectoryEntry& pd_pde = pdp->entries[j];
|
||||
if (!pd_pde.present) continue;
|
||||
if (pd_pde.larger_pages)
|
||||
{
|
||||
pages_freed++;
|
||||
PMM::free_page((void*)pd_pde.get_address());
|
||||
continue;
|
||||
}
|
||||
PageTable* pd = (PageTable*)pd_pde.get_address();
|
||||
for (int k = 0; k < 512; k++)
|
||||
{
|
||||
PageDirectoryEntry& pt_pde = pd->entries[k];
|
||||
if (!pt_pde.present) continue;
|
||||
if (pt_pde.larger_pages)
|
||||
{
|
||||
pages_freed++;
|
||||
PMM::free_page((void*)pt_pde.get_address());
|
||||
continue;
|
||||
}
|
||||
PageTable* pt = (PageTable*)pt_pde.get_address();
|
||||
for (int l = 0; l < 512; l++)
|
||||
{
|
||||
PageDirectoryEntry& pde = pt->entries[l];
|
||||
if (!pde.present) continue;
|
||||
pages_freed++;
|
||||
PMM::free_page((void*)pde.get_address());
|
||||
}
|
||||
pages_freed++;
|
||||
PMM::free_page(pt);
|
||||
}
|
||||
pages_freed++;
|
||||
PMM::free_page(pd);
|
||||
}
|
||||
pages_freed++;
|
||||
PMM::free_page(pdp);
|
||||
}
|
||||
|
||||
kdbgln("Reclaimed %ld pages from address space!", pages_freed);
|
||||
}
|
||||
|
||||
AddressSpace AddressSpace::clone()
|
||||
AddressSpace AddressSpace::clone() // FIXME: Add out-of-memory checks to this function.
|
||||
{
|
||||
AddressSpace result;
|
||||
result.m_pml4 = m_pml4;
|
||||
result.m_cloned = true;
|
||||
result.m_pml4 = (PageTable*)PMM::request_page();
|
||||
if (!result.m_pml4) return result;
|
||||
memcpy(result.m_pml4, m_pml4, PAGE_SIZE);
|
||||
for (int i = 0; i < 512; i++)
|
||||
{
|
||||
PageDirectoryEntry& pdp_pde = m_pml4->entries[i];
|
||||
PageDirectoryEntry& cloned_pdp_pde = result.m_pml4->entries[i];
|
||||
if (!pdp_pde.present) continue;
|
||||
if (pdp_pde.larger_pages)
|
||||
{
|
||||
void* cloned = PMM::request_page();
|
||||
memcpy(cloned, (void*)pdp_pde.get_address(), PAGE_SIZE);
|
||||
cloned_pdp_pde.set_address((uint64_t)cloned);
|
||||
continue;
|
||||
}
|
||||
PageTable* pdp = (PageTable*)pdp_pde.get_address();
|
||||
PageTable* cloned_pdp = (PageTable*)PMM::request_page();
|
||||
memcpy(cloned_pdp, pdp, PAGE_SIZE);
|
||||
cloned_pdp_pde.set_address((uint64_t)cloned_pdp);
|
||||
for (int j = 0; j < 511; j++) // skip the last page directory, it's the kernel one
|
||||
{
|
||||
PageDirectoryEntry& pd_pde = pdp->entries[j];
|
||||
PageDirectoryEntry& cloned_pd_pde = cloned_pdp->entries[j];
|
||||
if (!pd_pde.present) continue;
|
||||
if (pd_pde.larger_pages)
|
||||
{
|
||||
void* cloned = PMM::request_page();
|
||||
memcpy(cloned, (void*)pd_pde.get_address(), PAGE_SIZE);
|
||||
cloned_pd_pde.set_address((uint64_t)cloned);
|
||||
continue;
|
||||
}
|
||||
PageTable* pd = (PageTable*)pd_pde.get_address();
|
||||
PageTable* cloned_pd = (PageTable*)PMM::request_page();
|
||||
memcpy(cloned_pd, pd, PAGE_SIZE);
|
||||
cloned_pd_pde.set_address((uint64_t)cloned_pd);
|
||||
for (int k = 0; k < 512; k++)
|
||||
{
|
||||
PageDirectoryEntry& pt_pde = pd->entries[k];
|
||||
PageDirectoryEntry& cloned_pt_pde = cloned_pd->entries[k];
|
||||
if (!pt_pde.present) continue;
|
||||
if (pt_pde.larger_pages)
|
||||
{
|
||||
void* cloned = PMM::request_page();
|
||||
memcpy(cloned, (void*)pt_pde.get_address(), PAGE_SIZE);
|
||||
cloned_pt_pde.set_address((uint64_t)cloned);
|
||||
continue;
|
||||
}
|
||||
PageTable* pt = (PageTable*)pt_pde.get_address();
|
||||
PageTable* cloned_pt = (PageTable*)PMM::request_page();
|
||||
memcpy(cloned_pt, pt, PAGE_SIZE);
|
||||
cloned_pt_pde.set_address((uint64_t)cloned_pt);
|
||||
for (int l = 0; l < 512; l++)
|
||||
{
|
||||
PageDirectoryEntry& pde = pt->entries[l];
|
||||
PageDirectoryEntry& cloned_pde = cloned_pt->entries[l];
|
||||
if (!pde.present) continue;
|
||||
void* cloned = PMM::request_page();
|
||||
memcpy(cloned, (void*)pde.get_address(), PAGE_SIZE);
|
||||
cloned_pde.set_address((uint64_t)cloned);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
@ -3,7 +3,7 @@
|
||||
#include "log/Log.h"
|
||||
#endif
|
||||
|
||||
#include "assert.h"
|
||||
#include "kassert.h"
|
||||
#include "memory/KernelHeap.h"
|
||||
#include "memory/MemoryManager.h"
|
||||
#include "memory/PMM.h"
|
||||
|
@ -1,8 +1,8 @@
|
||||
#define MODULE "mem"
|
||||
|
||||
#include "memory/PMM.h"
|
||||
#include "assert.h"
|
||||
#include "bootboot.h"
|
||||
#include "kassert.h"
|
||||
#include "memory/Memory.h"
|
||||
#include "memory/MemoryManager.h"
|
||||
#include "misc/utils.h"
|
||||
|
164
kernel/src/memory/UserHeap.cpp
Normal file
164
kernel/src/memory/UserHeap.cpp
Normal file
@ -0,0 +1,164 @@
|
||||
#define MODULE "mem"
|
||||
|
||||
#include "memory/UserHeap.h"
|
||||
#include "log/Log.h"
|
||||
#include "misc/utils.h"
|
||||
#include "std/stdlib.h"
|
||||
#include "std/string.h"
|
||||
|
||||
#ifndef USER_HEAP_DEBUG
|
||||
#undef kdbgln
|
||||
#define kdbgln(...)
|
||||
#endif
|
||||
|
||||
#ifndef PAGE_SIZE
|
||||
#define PAGE_SIZE 4096
|
||||
#endif
|
||||
|
||||
#define ALLOC_BASE 0xa00000
|
||||
|
||||
#define INITIAL_SIZE 0x2000
|
||||
#define EXPAND_SIZE 0x1000
|
||||
|
||||
bool UserHeap::init()
|
||||
{
|
||||
bitmap = (uint8_t*)kmalloc(INITIAL_SIZE);
|
||||
if (!bitmap) return false;
|
||||
bitmap_size = INITIAL_SIZE;
|
||||
memset(bitmap, 0, bitmap_size);
|
||||
kdbgln("new user heap, bitmap at %p, size %ld", (void*)bitmap, bitmap_size);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UserHeap::inherit(UserHeap& other)
|
||||
{
|
||||
bitmap = (uint8_t*)kmalloc(other.bitmap_size);
|
||||
if (!bitmap) return false;
|
||||
bitmap_size = other.bitmap_size;
|
||||
memcpy(bitmap, other.bitmap, bitmap_size);
|
||||
kdbgln("child user heap, bitmap at %p, size %ld", (void*)bitmap, bitmap_size);
|
||||
return true;
|
||||
}
|
||||
|
||||
void UserHeap::free()
|
||||
{
|
||||
kdbgln("freeing user heap, bitmap at %p, size %ld", (void*)bitmap, bitmap_size);
|
||||
kfree(bitmap);
|
||||
}
|
||||
|
||||
bool UserHeap::try_expand()
|
||||
{
|
||||
kdbgln("attempting to expand user heap");
|
||||
void* new_bitmap = krealloc(bitmap, bitmap_size + EXPAND_SIZE);
|
||||
if (!new_bitmap)
|
||||
{
|
||||
kdbgln("expansion failed");
|
||||
return false;
|
||||
}
|
||||
bitmap = (uint8_t*)new_bitmap;
|
||||
memset(bitmap + bitmap_size, 0, EXPAND_SIZE);
|
||||
bitmap_size += EXPAND_SIZE;
|
||||
kdbgln("expanded user heap, bitmap at %p, size %ld", (void*)bitmap, bitmap_size);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UserHeap::try_expand_size(uint64_t size)
|
||||
{
|
||||
void* new_bitmap = krealloc(bitmap, bitmap_size + size);
|
||||
if (!new_bitmap)
|
||||
{
|
||||
kdbgln("expansion failed");
|
||||
return false;
|
||||
}
|
||||
bitmap = (uint8_t*)new_bitmap;
|
||||
memset(bitmap + bitmap_size, 0, size);
|
||||
bitmap_size += size;
|
||||
kdbgln("expanded user heap, bitmap at %p, size %ld", (void*)bitmap, bitmap_size);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UserHeap::bitmap_read(uint64_t index)
|
||||
{
|
||||
return (bitmap[index / 8] & (0b10000000 >> (index % 8))) > 0;
|
||||
}
|
||||
|
||||
void UserHeap::bitmap_set(uint64_t index, bool value)
|
||||
{
|
||||
uint64_t byteIndex = index / 8;
|
||||
uint8_t bitIndexer = 0b10000000 >> (index % 8);
|
||||
bitmap[byteIndex] &= ~bitIndexer;
|
||||
if (value) { bitmap[byteIndex] |= bitIndexer; }
|
||||
}
|
||||
|
||||
uint64_t UserHeap::request_virtual_page()
|
||||
{
|
||||
uint64_t attempts = 0;
|
||||
allocate:
|
||||
for (uint64_t index = start_index; index < bitmap_size * 8; index++)
|
||||
{
|
||||
if (bitmap_read(index)) continue;
|
||||
bitmap_set(index, true);
|
||||
start_index = index + 1;
|
||||
return ALLOC_BASE + (index * PAGE_SIZE);
|
||||
}
|
||||
|
||||
if (attempts == 0 && try_expand()) // We are allocating ONE PAGE, only one attempt should be necessary.
|
||||
{
|
||||
attempts++;
|
||||
goto allocate;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t UserHeap::request_virtual_pages(uint64_t count)
|
||||
{
|
||||
uint64_t attempts = 0;
|
||||
allocate:
|
||||
uint64_t contiguous = 0;
|
||||
uint64_t contiguous_start = 0;
|
||||
for (uint64_t index = start_index; index < bitmap_size * 8; index++)
|
||||
{
|
||||
if (bitmap_read(index))
|
||||
{
|
||||
contiguous = 0;
|
||||
continue;
|
||||
}
|
||||
if (contiguous == 0)
|
||||
{
|
||||
contiguous_start = index;
|
||||
contiguous++;
|
||||
}
|
||||
else
|
||||
contiguous++;
|
||||
if (contiguous == count)
|
||||
{
|
||||
for (uint64_t i = 0; i < count; i++) bitmap_set(contiguous_start + i, true);
|
||||
return ALLOC_BASE + (contiguous_start * PAGE_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
if (attempts == 0 && try_expand_size(Utilities::get_blocks_from_size(8, count) + 256))
|
||||
{
|
||||
attempts++;
|
||||
goto allocate;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void UserHeap::free_virtual_page(uint64_t address)
|
||||
{
|
||||
if (address < ALLOC_BASE || address >= (ALLOC_BASE + bitmap_size * 8 * PAGE_SIZE)) return;
|
||||
uint64_t index = (address - ALLOC_BASE) / PAGE_SIZE;
|
||||
bitmap_set(index, false);
|
||||
if (start_index > index) start_index = index;
|
||||
}
|
||||
|
||||
void UserHeap::free_virtual_pages(uint64_t address, uint64_t count)
|
||||
{
|
||||
if (address < ALLOC_BASE || address >= (ALLOC_BASE + bitmap_size * 8 * PAGE_SIZE)) return;
|
||||
uint64_t index = (address - ALLOC_BASE) / PAGE_SIZE;
|
||||
for (uint64_t i = 0; i < count; i++) { bitmap_set(index + i, false); }
|
||||
if (start_index > index) start_index = index;
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
#define MODULE "vmm"
|
||||
|
||||
#include "memory/VMM.h"
|
||||
#include "assert.h"
|
||||
#include "kassert.h"
|
||||
#include "log/Log.h"
|
||||
#include "memory/PMM.h"
|
||||
#include "misc/utils.h"
|
||||
@ -275,13 +275,19 @@ void VMM::install_kernel_page_directory_into_address_space(AddressSpace& space)
|
||||
PageTable* kernel_last_pdp = (PageTable*)kernel_pml4->entries[511].get_address();
|
||||
PageTable* kernel_last_pd = (PageTable*)kernel_last_pdp->entries[511].get_address();
|
||||
|
||||
PageTable* space_last_pdp = (PageTable*)PMM::request_page();
|
||||
|
||||
PageDirectoryEntry& space_last_pdp_pde = space_pml4->entries[511];
|
||||
|
||||
PageTable* space_last_pdp;
|
||||
|
||||
if (!space_last_pdp_pde.present)
|
||||
{
|
||||
space_last_pdp = (PageTable*)PMM::request_page();
|
||||
|
||||
space_last_pdp_pde.present = true;
|
||||
space_last_pdp_pde.read_write = true;
|
||||
space_last_pdp_pde.set_address((uint64_t)space_last_pdp);
|
||||
}
|
||||
else { space_last_pdp = (PageTable*)space_last_pdp_pde.get_address(); }
|
||||
|
||||
PageDirectoryEntry& space_last_pd_pde = space_last_pdp->entries[511];
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
#endif
|
||||
|
||||
#ifndef MOON_MINOR
|
||||
#define MOON_MINOR 11
|
||||
#define MOON_MINOR 12
|
||||
#endif
|
||||
|
||||
#ifndef _MOON_SUFFIX
|
||||
|
@ -1,9 +1,14 @@
|
||||
#define MODULE "stack"
|
||||
|
||||
#include "panic/Panic.h"
|
||||
#include "log/Log.h"
|
||||
#include "misc/hang.h"
|
||||
#include "render/TextRenderer.h"
|
||||
#include <stdint.h>
|
||||
|
||||
extern "C" void __stack_chk_fail()
|
||||
{
|
||||
panic("Stack smashing detected");
|
||||
KernelLog::enable_log_backend(Backend::Console);
|
||||
TextRenderer::reset();
|
||||
kerrorln("stack smashing detected");
|
||||
hang();
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
extern __do_panic
|
||||
global __panic
|
||||
|
||||
__panic:
|
||||
push BYTE 0 ; interrupt number
|
||||
push BYTE 0 ; error code
|
||||
push rax
|
||||
push rbx
|
||||
push rcx
|
||||
push rdx
|
||||
push rbp
|
||||
push rdi
|
||||
push rsi
|
||||
push r8
|
||||
push r9
|
||||
push r10
|
||||
push r11
|
||||
push r12
|
||||
push r13
|
||||
push r14
|
||||
push r15
|
||||
|
||||
mov r8, cr2
|
||||
push r8
|
||||
|
||||
mov rsi, rdi
|
||||
mov rdi, rsp
|
||||
|
||||
call __do_panic
|
||||
|
||||
cli
|
||||
loop:
|
||||
hlt
|
||||
jmp loop
|
@ -6,33 +6,45 @@
|
||||
#include "io/PIC.h"
|
||||
#include "log/Log.h"
|
||||
#include "misc/MSR.h"
|
||||
#include "render/TextRenderer.h"
|
||||
#include "std/stdio.h"
|
||||
#include "thread/Scheduler.h"
|
||||
#include "trace/StackTracer.h"
|
||||
|
||||
static void dump_registers(Context* context)
|
||||
void dump_registers(Context* context)
|
||||
{
|
||||
printf("-- Registers: \n");
|
||||
printf("rax: %lx, rbx: %lx, rcx: %lx, rdx: %lx\n", context->rax, context->rbx, context->rcx, context->rdx);
|
||||
printf("rsi: %lx, rdi: %lx, rsp: %lx, rbp: %lx\n", context->rsi, context->rdi, context->rsp, context->rbp);
|
||||
printf("r8: %lx, r9: %lx, r10: %lx, r11: %lx\n", context->r8, context->r9, context->r10, context->r11);
|
||||
printf("r12: %lx, r13: %lx, r14: %lx, r15: %lx\n", context->r12, context->r13, context->r14, context->r15);
|
||||
printf("rip: %lx, cs: %lx, ss: %lx\n", context->rip, context->cs, context->ss);
|
||||
printf("rflags: %lx, cr2: %lx\n", context->rflags, context->cr2);
|
||||
printf("ia32_efer: %lx\n", MSR::read_from(IA32_EFER_MSR));
|
||||
kinfoln("-- Registers:");
|
||||
kinfoln("rax: %lx, rbx: %lx, rcx: %lx, rdx: %lx", context->rax, context->rbx, context->rcx, context->rdx);
|
||||
kinfoln("rsi: %lx, rdi: %lx, rsp: %lx, rbp: %lx", context->rsi, context->rdi, context->rsp, context->rbp);
|
||||
kinfoln("r8: %lx, r9: %lx, r10: %lx, r11: %lx", context->r8, context->r9, context->r10, context->r11);
|
||||
kinfoln("r12: %lx, r13: %lx, r14: %lx, r15: %lx", context->r12, context->r13, context->r14, context->r15);
|
||||
kinfoln("rip: %lx, cs: %lx, ss: %lx", context->rip, context->cs, context->ss);
|
||||
kinfoln("rflags: %lx, cr2: %lx", context->rflags, context->cr2);
|
||||
kinfoln("ia32_efer: %lx", MSR::read_from(IA32_EFER_MSR));
|
||||
}
|
||||
|
||||
[[noreturn]] static void __panic_stub(Context* context)
|
||||
[[noreturn]] void __panic_stub(Context* context)
|
||||
{
|
||||
dump_registers(context);
|
||||
if (context) dump_registers(context);
|
||||
|
||||
if (InitRD::is_initialized())
|
||||
{
|
||||
printf("-- Stack trace:\n");
|
||||
kinfoln("-- Stack trace:");
|
||||
if (context)
|
||||
{
|
||||
StackTracer tracer(context->rbp);
|
||||
tracer.trace_with_ip(context->rip);
|
||||
}
|
||||
else { printf("-- No stack trace available\n"); }
|
||||
else
|
||||
{
|
||||
uintptr_t rbp;
|
||||
|
||||
asm volatile("mov %%rbp, %0" : "=r"(rbp));
|
||||
StackTracer tracer(rbp);
|
||||
tracer.trace();
|
||||
}
|
||||
}
|
||||
else { kinfoln("-- No stack trace available"); }
|
||||
|
||||
PIC::enable_master(0b11111101); // enable keyboard only
|
||||
PIC::enable_slave(0b11111111);
|
||||
@ -53,10 +65,14 @@ static void dump_registers(Context* context)
|
||||
while (1) asm volatile("hlt");
|
||||
}
|
||||
|
||||
extern "C" [[noreturn]] void __do_int_panic(Context* context, const char* file, int line, const char* message)
|
||||
extern "C" [[noreturn]] bool __do_int_panic(Context* context, const char* file, int line, const char* message)
|
||||
{
|
||||
asm volatile("cli");
|
||||
|
||||
KernelLog::enable_log_backend(Backend::Console);
|
||||
|
||||
TextRenderer::reset();
|
||||
|
||||
if (context->number >= 0x20 && context->number < 0x30) { PIC::send_eoi((uint8_t)(context->irq_number & 0xFF)); }
|
||||
|
||||
Task* task;
|
||||
@ -69,11 +85,20 @@ extern "C" [[noreturn]] void __do_int_panic(Context* context, const char* file,
|
||||
__panic_stub(context);
|
||||
}
|
||||
|
||||
extern "C" [[noreturn]] void __do_panic(Context* context, const char* message)
|
||||
extern "C" [[noreturn]] bool __do_panic(const char* file, int line, const char* message)
|
||||
{
|
||||
Task* task;
|
||||
if ((task = Scheduler::current_task())) { kerrorln("Kernel panic in task %ld: %s", task->id, message); }
|
||||
else { kerrorln("Kernel panic: %s", message); }
|
||||
asm volatile("cli");
|
||||
|
||||
__panic_stub(context);
|
||||
KernelLog::enable_log_backend(Backend::Console);
|
||||
|
||||
TextRenderer::reset();
|
||||
|
||||
Task* task;
|
||||
if ((task = Scheduler::current_task()))
|
||||
{
|
||||
kerrorln("Kernel panic in task %ld, at %s, line %d: %s", task->id, file, line, message);
|
||||
}
|
||||
else { kerrorln("Kernel panic at %s, line %d: %s", file, line, message); }
|
||||
|
||||
__panic_stub(nullptr);
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
#define MODULE "rand"
|
||||
|
||||
#include "rand/Mersenne.h"
|
||||
#include "assert.h"
|
||||
#include "kassert.h"
|
||||
#include <stddef.h>
|
||||
|
||||
typedef uint64_t word_t;
|
||||
|
47
kernel/src/std/libgen.cpp
Normal file
47
kernel/src/std/libgen.cpp
Normal file
@ -0,0 +1,47 @@
|
||||
#include "std/libgen.h"
|
||||
#include "std/string.h"
|
||||
|
||||
static char dot[] = ".";
|
||||
|
||||
char* basename(char* path)
|
||||
{
|
||||
// If path is NULL, or the string's length is 0, return .
|
||||
if (!path) return dot;
|
||||
size_t len = strlen(path);
|
||||
if (!len) return dot;
|
||||
|
||||
// Strip trailing slashes.
|
||||
char* it = path + len - 1;
|
||||
while (*it == '/' && it != path) { it--; }
|
||||
*(it + 1) = 0;
|
||||
if (it == path) return path;
|
||||
|
||||
// Return path from the first character if there are no more slashes, or from the first character after the last
|
||||
// slash.
|
||||
char* beg = strrchr(path, '/');
|
||||
if (!beg) return path;
|
||||
return beg + 1;
|
||||
}
|
||||
|
||||
char* dirname(char* path)
|
||||
{
|
||||
// If path is NULL, or the string's length is 0, return .
|
||||
if (!path) return dot;
|
||||
size_t len = strlen(path);
|
||||
if (!len) return dot;
|
||||
|
||||
// Strip trailing slashes.
|
||||
char* it = path + len - 1;
|
||||
while (*it == '/' && it != path) { it--; }
|
||||
*(char*)(it + 1) = 0;
|
||||
if (it == path) return path;
|
||||
|
||||
// Search for the last slash. If there is none, return .
|
||||
// Otherwise, we end the string there and return.
|
||||
char* end = strrchr(path, '/');
|
||||
if (!end) return dot;
|
||||
if (end != path) *end = 0;
|
||||
else
|
||||
*(end + 1) = 0;
|
||||
return path;
|
||||
}
|
@ -17,11 +17,29 @@ char* strcpy(char* dest, const char* src)
|
||||
|
||||
char* strncpy(char* dest, const char* src, size_t n)
|
||||
{
|
||||
size_t src_len = strlen(src) + 1; // NULL byte
|
||||
memcpy(dest, src, src_len > n ? n : src_len);
|
||||
size_t i;
|
||||
for (i = 0; i < n && src[i] != 0; i++) dest[i] = src[i];
|
||||
for (; i < n; i++) dest[i] = 0;
|
||||
return dest;
|
||||
}
|
||||
|
||||
size_t strlcpy(char* dest, const char* src, size_t size)
|
||||
{
|
||||
size_t len = strlen(src);
|
||||
if (size == 0) return len;
|
||||
if (len < (size - 1))
|
||||
{
|
||||
memcpy(dest, src, len);
|
||||
dest[len] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(dest, src, size - 1);
|
||||
dest[size - 1] = 0;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
int strcmp(const char* a, const char* b)
|
||||
{
|
||||
while (*a && (*a == *b))
|
||||
@ -131,3 +149,11 @@ char* strdup(const char* src)
|
||||
memcpy(duplicated, src, length + 1);
|
||||
return duplicated;
|
||||
}
|
||||
|
||||
char* strrchr(const char* str, int c)
|
||||
{
|
||||
const char* s = str + strlen(str);
|
||||
while (s != str && *s != (char)c) s--;
|
||||
if (*s == (char)c) return const_cast<char*>(s);
|
||||
return NULL;
|
||||
}
|
@ -16,8 +16,7 @@ void Syscall::entry(Context* context)
|
||||
case SYS_sleep: sys_sleep(context, context->rdi); break;
|
||||
case SYS_write: sys_write(context, (int)context->rdi, context->rsi, (const char*)context->rdx); break;
|
||||
case SYS_paint: sys_paint(context, context->rdi, context->rsi, context->rdx, context->r10, context->r8); break;
|
||||
case SYS_rand: sys_rand(context); break;
|
||||
case SYS_gettid: sys_gettid(context); break;
|
||||
case SYS_getpid: sys_getpid(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;
|
||||
@ -25,12 +24,17 @@ void Syscall::entry(Context* context)
|
||||
case SYS_close: sys_close(context, (int)context->rdi); break;
|
||||
case SYS_seek: sys_seek(context, (int)context->rdi, (long)context->rsi, (int)context->rdx); break;
|
||||
case SYS_exec: sys_exec(context, (const char*)context->rdi); break;
|
||||
case SYS_fcntl: sys_fcntl(context, (int)context->rdi, (int)context->rsi, context->rdx); break;
|
||||
case SYS_mprotect: sys_mprotect(context, (void*)context->rdi, context->rsi, (int)context->rdx); break;
|
||||
case SYS_clock: sys_clock(context); break;
|
||||
case SYS_mkdir: sys_mkdir(context, (const char*)context->rdi); break;
|
||||
case SYS_fork: sys_fork(context); break;
|
||||
default: context->rax = -ENOSYS; break;
|
||||
}
|
||||
VMM::exit_syscall_context();
|
||||
}
|
||||
|
||||
char* Syscall::strdup_from_user(const char* user_string)
|
||||
char* Syscall::strdup_from_user(const char* user_string) // FIXME: This function is a little hacky.
|
||||
{
|
||||
uint64_t phys = VMM::get_physical((uint64_t)user_string);
|
||||
if (phys == (uint64_t)-1) { return nullptr; }
|
||||
|
9
kernel/src/sys/clock.cpp
Normal file
9
kernel/src/sys/clock.cpp
Normal file
@ -0,0 +1,9 @@
|
||||
#include "interrupts/Context.h"
|
||||
#include "thread/Scheduler.h"
|
||||
|
||||
void sys_clock(Context* context)
|
||||
{
|
||||
Task* current_task = Scheduler::current_task();
|
||||
context->rax = (long)current_task->cpu_time;
|
||||
return;
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
#define MODULE "elf"
|
||||
|
||||
#include "sys/elf/ELFLoader.h"
|
||||
#include "assert.h"
|
||||
#include "errno.h"
|
||||
#include "fs/VFS.h"
|
||||
#include "init/InitRD.h"
|
||||
#include "kassert.h"
|
||||
#include "log/Log.h"
|
||||
#include "memory/Memory.h"
|
||||
#include "memory/MemoryManager.h"
|
||||
@ -123,40 +123,40 @@ ELFImage* ELFLoader::load_elf_from_vfs(VFS::Node* node)
|
||||
long ELFLoader::check_elf_image(VFS::Node* node)
|
||||
{
|
||||
Elf64_Ehdr elf_ehdr;
|
||||
if (VFS::read(node, 0, sizeof(elf_ehdr), (char*)&elf_ehdr) < 0)
|
||||
if (VFS::read(node, 0, sizeof(elf_ehdr), (char*)&elf_ehdr) < (long)sizeof(elf_ehdr))
|
||||
{
|
||||
kwarnln("Unable to read ELF header");
|
||||
return -1;
|
||||
return -ENOEXEC;
|
||||
}
|
||||
if (strncmp((const char*)elf_ehdr.e_ident, ELFMAG, SELFMAG) != 0)
|
||||
{
|
||||
kwarnln("ELF file has invalid magic, skipping");
|
||||
return -1;
|
||||
return -ENOEXEC;
|
||||
}
|
||||
if (elf_ehdr.e_ident[EI_CLASS] != ELFCLASS64)
|
||||
{
|
||||
kwarnln("ELF file is not ELF64, skipping");
|
||||
return -1;
|
||||
return -ENOEXEC;
|
||||
}
|
||||
if (elf_ehdr.e_ident[EI_DATA] != ELFDATA2LSB)
|
||||
{
|
||||
kwarnln("ELF file is not little-endian, skipping");
|
||||
return -1;
|
||||
return -ENOEXEC;
|
||||
}
|
||||
if (elf_ehdr.e_type != ET_EXEC)
|
||||
{
|
||||
kwarnln("not supported: ELF file is not an executable");
|
||||
return -1;
|
||||
return -ENOEXEC;
|
||||
}
|
||||
if (elf_ehdr.e_machine != EM_MACH)
|
||||
{
|
||||
kwarnln("Unsupported target machine");
|
||||
return -1;
|
||||
return -ENOEXEC;
|
||||
}
|
||||
if (elf_ehdr.e_phnum == 0)
|
||||
{
|
||||
kwarnln("ELF file has no PHDRS");
|
||||
return -1;
|
||||
return -ENOEXEC;
|
||||
}
|
||||
int i;
|
||||
int loadable_sections = 0;
|
||||
@ -170,12 +170,12 @@ long ELFLoader::check_elf_image(VFS::Node* node)
|
||||
if (!phdr.p_vaddr)
|
||||
{
|
||||
kerrorln("segment address is NULL, this is invalid :(");
|
||||
return -1;
|
||||
return -ENOEXEC;
|
||||
}
|
||||
if (Memory::is_kernel_address(phdr.p_vaddr) || Memory::is_kernel_address(phdr.p_vaddr + phdr.p_memsz))
|
||||
{
|
||||
kerrorln("trying to load ELF into kernel memory");
|
||||
return -1;
|
||||
return -ENOEXEC;
|
||||
}
|
||||
loadable_sections++;
|
||||
memusage += Utilities::get_blocks_from_size(PAGE_SIZE, phdr.p_memsz) * PAGE_SIZE;
|
||||
@ -184,7 +184,7 @@ long ELFLoader::check_elf_image(VFS::Node* node)
|
||||
if (!loadable_sections)
|
||||
{
|
||||
kwarnln("No loadable sections");
|
||||
return -1;
|
||||
return -ENOEXEC;
|
||||
}
|
||||
return memusage;
|
||||
}
|
||||
|
@ -1,8 +1,9 @@
|
||||
#define MODULE "exec"
|
||||
|
||||
#include "assert.h"
|
||||
#include "errno.h"
|
||||
#include "interrupts/Interrupts.h"
|
||||
#include "kassert.h"
|
||||
#include "log/Log.h"
|
||||
#include "memory/MemoryManager.h"
|
||||
#include "memory/PMM.h"
|
||||
#include "memory/VMM.h"
|
||||
@ -12,11 +13,48 @@
|
||||
#include "sys/elf/ELFLoader.h"
|
||||
#include "thread/Scheduler.h"
|
||||
|
||||
void sys_fork(Context* context)
|
||||
{
|
||||
kinfoln("fork(): attempting fork");
|
||||
|
||||
Task* parent = Scheduler::current_task();
|
||||
|
||||
Task* child = Scheduler::create_user_task();
|
||||
if (!child)
|
||||
{
|
||||
context->rax = -ENOMEM;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!child->allocator.inherit(parent->allocator))
|
||||
{
|
||||
child->state = child->Exited;
|
||||
child->exit_status = -127; // so the reaper reaps it on next reaping
|
||||
context->rax = -ENOMEM;
|
||||
return;
|
||||
}
|
||||
|
||||
child->save_context(context);
|
||||
child->save_floating();
|
||||
|
||||
for (int i = 0; i < TASK_MAX_FDS; i++) { child->files[i] = parent->files[i]; }
|
||||
|
||||
child->address_space = parent->address_space.clone();
|
||||
|
||||
child->regs.rax = 0;
|
||||
context->rax = child->id;
|
||||
|
||||
strlcpy(child->name, parent->name, sizeof(child->name));
|
||||
|
||||
child->state = child->Running;
|
||||
|
||||
kinfoln("fork(): forked parent %ld into child %ld", parent->id, child->id);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void sys_exec(Context* context, const char* pathname)
|
||||
{
|
||||
/*context->rax = -ENOSYS; // FIXME: Make exec() work under separate address spaces.
|
||||
return;*/
|
||||
|
||||
char* kpathname = Syscall::strdup_from_user(pathname);
|
||||
if (!kpathname)
|
||||
{
|
||||
@ -49,20 +87,9 @@ void sys_exec(Context* context, const char* pathname)
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t allocated_stack = (uint64_t)MemoryManager::get_pages(TASK_PAGES_IN_STACK, MAP_READ_WRITE | MAP_USER);
|
||||
if (!allocated_stack)
|
||||
{
|
||||
kfree(kpathname);
|
||||
context->rax = -ENOMEM;
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t allocated_stack_phys = VMM::get_physical(allocated_stack);
|
||||
|
||||
if ((uint64_t)memusage > PMM::get_free())
|
||||
{
|
||||
kfree(kpathname);
|
||||
MemoryManager::release_pages((void*)allocated_stack, TASK_PAGES_IN_STACK);
|
||||
context->rax = -ENOMEM;
|
||||
return;
|
||||
}
|
||||
@ -75,20 +102,25 @@ void sys_exec(Context* context, const char* pathname)
|
||||
|
||||
// At this point, pretty much nothing can fail.
|
||||
|
||||
task->allocator.free();
|
||||
task->allocator
|
||||
.init(); // If we had enough space for the old bitmap, we should have enough space for the new bitmap.
|
||||
|
||||
task->address_space.clear();
|
||||
task->allocated_stack = (uint64_t)MemoryManager::get_pages_at(
|
||||
0x100000, TASK_PAGES_IN_STACK,
|
||||
MAP_USER | MAP_READ_WRITE); // If we had enough space for the old stack, there should be enough space for the
|
||||
// new stack.
|
||||
|
||||
ELFImage* image = ELFLoader::load_elf_from_vfs(program);
|
||||
ASSERT(image); // If check_elf_image succeeded, load_elf_from_vfs MUST succeed, unless something has gone terribly
|
||||
// wrong.
|
||||
|
||||
task->allocated_stack = allocated_stack;
|
||||
|
||||
for (uint64_t i = 0; i < TASK_PAGES_IN_STACK; i++)
|
||||
{
|
||||
VMM::map(allocated_stack + (i * PAGE_SIZE), allocated_stack_phys + (i * PAGE_SIZE), MAP_READ_WRITE | MAP_USER);
|
||||
}
|
||||
strlcpy(task->name, kpathname, sizeof(task->name));
|
||||
|
||||
Scheduler::reset_task(task, image);
|
||||
|
||||
set_context_from_task(*task, context);
|
||||
task->restore_context(context);
|
||||
|
||||
kfree(kpathname);
|
||||
|
||||
|
@ -3,14 +3,37 @@
|
||||
#include "errno.h"
|
||||
#include "interrupts/Context.h"
|
||||
#include "log/Log.h"
|
||||
#include "memory/Memory.h"
|
||||
#include "memory/MemoryManager.h"
|
||||
#include "memory/VMM.h"
|
||||
#include "misc/utils.h"
|
||||
#include "thread/Scheduler.h"
|
||||
#include <stddef.h>
|
||||
|
||||
#define MAP_READ 1
|
||||
#define MAP_WRITE 2
|
||||
#define MAP_NONE 0
|
||||
|
||||
#define MAP_FAIL(errno) 0xffffffffffffff00 | (unsigned char)(errno)
|
||||
|
||||
void sys_mmap(Context* context, void* address, size_t size, int flags)
|
||||
static const char* format_prot(int prot)
|
||||
{
|
||||
static char prot_string[3];
|
||||
prot_string[2] = 0;
|
||||
prot_string[0] = ((prot & MAP_READ) > 0) ? 'r' : '-';
|
||||
prot_string[1] = ((prot & MAP_WRITE) > 0) ? 'w' : '-';
|
||||
return prot_string;
|
||||
}
|
||||
|
||||
static int mman_flags_from_prot(int prot)
|
||||
{
|
||||
prot &= 0b11;
|
||||
if (prot == MAP_NONE) return 0;
|
||||
if ((prot & MAP_WRITE) > 0) return MAP_USER | MAP_READ_WRITE;
|
||||
return MAP_USER;
|
||||
}
|
||||
|
||||
void sys_mmap(Context* context, void* address, size_t size, int prot)
|
||||
{
|
||||
if (size < PAGE_SIZE)
|
||||
{
|
||||
@ -24,12 +47,16 @@ void sys_mmap(Context* context, void* address, size_t size, int flags)
|
||||
context->rax = MAP_FAIL(EINVAL);
|
||||
return;
|
||||
}
|
||||
int real_flags = MAP_USER;
|
||||
if (flags & MAP_READ_WRITE) real_flags |= MAP_READ_WRITE;
|
||||
int real_flags = mman_flags_from_prot(prot);
|
||||
if (address)
|
||||
{
|
||||
kdbgln("mmap(): %ld pages at address %p, %s", size / PAGE_SIZE, address,
|
||||
real_flags & MAP_READ_WRITE ? "rw" : "ro");
|
||||
kdbgln("mmap(): %ld pages at address %p, %s", size / PAGE_SIZE, address, format_prot(prot));
|
||||
if (Memory::is_kernel_address((uintptr_t)address))
|
||||
{
|
||||
kwarnln("munmap() failed: attempted to unmap a kernel page");
|
||||
context->rax = MAP_FAIL(ENOMEM);
|
||||
return;
|
||||
}
|
||||
if (VMM::get_physical((uint64_t)address) != (uint64_t)-1) // Address is already used.
|
||||
{
|
||||
kwarnln("attempt to map an already mapped address");
|
||||
@ -47,14 +74,21 @@ void sys_mmap(Context* context, void* address, size_t size, int flags)
|
||||
}
|
||||
else
|
||||
{
|
||||
kwarnln("mmap() failed");
|
||||
kwarnln("mmap() failed: failed to allocate physical memory");
|
||||
context->rax = MAP_FAIL(ENOMEM);
|
||||
return;
|
||||
}
|
||||
}
|
||||
kdbgln("mmap(): %ld pages at any address, %s", Utilities::get_blocks_from_size(PAGE_SIZE, size),
|
||||
real_flags & MAP_READ_WRITE ? "rw" : "ro");
|
||||
void* result = MemoryManager::get_pages(Utilities::get_blocks_from_size(PAGE_SIZE, size), real_flags);
|
||||
kdbgln("mmap(): %ld pages at any address, %s", Utilities::get_blocks_from_size(PAGE_SIZE, size), format_prot(prot));
|
||||
uint64_t ptr =
|
||||
Scheduler::current_task()->allocator.request_virtual_pages(Utilities::get_blocks_from_size(PAGE_SIZE, size));
|
||||
if (!ptr)
|
||||
{
|
||||
kwarnln("mmap() failed: failed to allocate virtual address");
|
||||
context->rax = MAP_FAIL(ENOMEM);
|
||||
return;
|
||||
}
|
||||
void* result = MemoryManager::get_pages_at(ptr, Utilities::get_blocks_from_size(PAGE_SIZE, size), real_flags);
|
||||
if (result)
|
||||
{
|
||||
kdbgln("mmap() succeeded: %p", result);
|
||||
@ -63,7 +97,7 @@ void sys_mmap(Context* context, void* address, size_t size, int flags)
|
||||
}
|
||||
else
|
||||
{
|
||||
kwarnln("mmap() failed");
|
||||
kwarnln("mmap() failed: failed to allocate physical memory");
|
||||
context->rax = MAP_FAIL(ENOMEM);
|
||||
return;
|
||||
}
|
||||
@ -90,16 +124,68 @@ void sys_munmap(Context* context, void* address, size_t size)
|
||||
context->rax = -EINVAL;
|
||||
return;
|
||||
}
|
||||
uint64_t flags = VMM::get_flags((uint64_t)address);
|
||||
if (!(flags & MAP_USER))
|
||||
if (Memory::is_kernel_address((uintptr_t)address))
|
||||
{
|
||||
kwarnln("munmap() failed: attempted to unmap a non-existent or kernel page");
|
||||
kwarnln("munmap() failed: attempted to unmap a kernel page");
|
||||
context->rax = -EINVAL;
|
||||
return;
|
||||
}
|
||||
uint64_t phys = VMM::get_physical((uint64_t)address);
|
||||
if (phys == (uint64_t)-1)
|
||||
{
|
||||
kwarnln("munmap() failed: attempted to unmap a non-existent page");
|
||||
context->rax = -EINVAL;
|
||||
return;
|
||||
}
|
||||
uint64_t offset = (uint64_t)address % PAGE_SIZE;
|
||||
Scheduler::current_task()->allocator.free_virtual_pages(((uint64_t)address - offset),
|
||||
Utilities::get_blocks_from_size(PAGE_SIZE, size));
|
||||
MemoryManager::release_pages((void*)((uint64_t)address - offset), Utilities::get_blocks_from_size(PAGE_SIZE, size));
|
||||
kdbgln("munmap() succeeded");
|
||||
context->rax = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
void sys_mprotect(Context* context, void* address, size_t size, int prot)
|
||||
{
|
||||
kdbgln("mprotect(): attempting to protect %p with %s", address, format_prot(prot));
|
||||
|
||||
if (size < PAGE_SIZE)
|
||||
{
|
||||
kwarnln("mprotect() failed: size is too small");
|
||||
context->rax = -EINVAL;
|
||||
return;
|
||||
}
|
||||
if (size % PAGE_SIZE)
|
||||
{
|
||||
kwarnln("mprotect() failed: size is not a multiple of PAGE_SIZE");
|
||||
context->rax = -EINVAL;
|
||||
return;
|
||||
}
|
||||
if (!address)
|
||||
{
|
||||
kwarnln("mprotect() failed: attempted to protect page 0");
|
||||
context->rax = -EINVAL;
|
||||
return;
|
||||
}
|
||||
if (Memory::is_kernel_address((uintptr_t)address))
|
||||
{
|
||||
kwarnln("mprotect() failed: attempted to protect a kernel page");
|
||||
context->rax = -EINVAL;
|
||||
return;
|
||||
}
|
||||
uint64_t phys = VMM::get_physical((uint64_t)address);
|
||||
if (phys == (uint64_t)-1)
|
||||
{
|
||||
kwarnln("mprotect() failed: attempted to protect a non-existent page");
|
||||
context->rax = -EINVAL;
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t offset = (uint64_t)address % PAGE_SIZE;
|
||||
MemoryManager::protect((void*)((uint64_t)address - offset), Utilities::get_blocks_from_size(PAGE_SIZE, size),
|
||||
mman_flags_from_prot(prot));
|
||||
kdbgln("mprotect() succeeded");
|
||||
context->rax = 0;
|
||||
return;
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
#include "interrupts/Context.h"
|
||||
#include "rand/Mersenne.h"
|
||||
|
||||
void sys_rand(Context* context)
|
||||
{
|
||||
context->rax = Mersenne::get();
|
||||
}
|
@ -20,7 +20,7 @@ void sys_sleep(Context* context, uint64_t ms)
|
||||
Scheduler::task_yield(context);
|
||||
}
|
||||
|
||||
void sys_gettid(Context* context)
|
||||
void sys_getpid(Context* context)
|
||||
{
|
||||
context->rax = Scheduler::current_task()->id;
|
||||
}
|
@ -18,6 +18,47 @@
|
||||
#define SEEK_CUR 1
|
||||
#define SEEK_END 2
|
||||
|
||||
#define FNCTL_DUPFD 0
|
||||
|
||||
void sys_fcntl(Context* context, int fd, int command, uintptr_t arg)
|
||||
{
|
||||
if (fd >= TASK_MAX_FDS || fd < 0)
|
||||
{
|
||||
context->rax = -EBADF;
|
||||
return;
|
||||
}
|
||||
Task* current_task = Scheduler::current_task();
|
||||
if (!current_task->files[fd].is_open())
|
||||
{
|
||||
context->rax = -EBADF;
|
||||
return;
|
||||
}
|
||||
Descriptor& file = current_task->files[fd];
|
||||
if (command == FNCTL_DUPFD)
|
||||
{
|
||||
if ((int)arg < 0 || (int)arg >= TASK_MAX_FDS)
|
||||
{
|
||||
context->rax = -EINVAL;
|
||||
return;
|
||||
}
|
||||
int dupfd = current_task->alloc_fd_greater_than_or_equal((int)arg);
|
||||
if (dupfd < 0)
|
||||
{
|
||||
context->rax = -EMFILE;
|
||||
return;
|
||||
}
|
||||
current_task->files[dupfd] = file;
|
||||
context->rax = dupfd;
|
||||
kdbgln("fcntl(F_DUPFD): duplicated fd %d, result is %d", fd, dupfd);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
context->rax = -EINVAL;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void sys_seek(Context* context, int fd, long offset, int whence)
|
||||
{
|
||||
if (whence < SEEK_SET || whence > SEEK_END)
|
||||
@ -36,12 +77,13 @@ void sys_seek(Context* context, int fd, long offset, int whence)
|
||||
context->rax = -EBADF;
|
||||
return;
|
||||
}
|
||||
Descriptor& file = current_task->files[fd];
|
||||
long new_offset;
|
||||
if (whence == SEEK_SET) new_offset = offset;
|
||||
else if (whence == SEEK_CUR)
|
||||
new_offset = offset + current_task->files[fd].offset();
|
||||
new_offset = offset + file.offset();
|
||||
else if (whence == SEEK_END)
|
||||
new_offset = current_task->files[fd].length() + offset;
|
||||
new_offset = file.length() + offset;
|
||||
else
|
||||
__builtin_unreachable();
|
||||
if (new_offset < 0)
|
||||
@ -49,7 +91,12 @@ void sys_seek(Context* context, int fd, long offset, int whence)
|
||||
context->rax = -EINVAL; // FIXME: Is this the right error?
|
||||
return;
|
||||
}
|
||||
int result = current_task->files[fd].seek(new_offset);
|
||||
if (new_offset == file.offset())
|
||||
{
|
||||
context->rax = new_offset;
|
||||
return;
|
||||
}
|
||||
int result = file.seek(new_offset);
|
||||
if (result < 0)
|
||||
{
|
||||
context->rax = result;
|
||||
@ -77,12 +124,13 @@ void sys_write(Context* context, int fd, size_t size, const char* addr)
|
||||
context->rax = -EBADF;
|
||||
return;
|
||||
}
|
||||
if (!current_task->files[fd].can_write())
|
||||
Descriptor& file = current_task->files[fd];
|
||||
if (!file.can_write())
|
||||
{
|
||||
context->rax = -EBADF;
|
||||
return;
|
||||
}
|
||||
ssize_t result = current_task->files[fd].write(size, (const char*)VMM::get_physical((uint64_t)addr));
|
||||
ssize_t result = file.write(size, (const char*)VMM::get_physical((uint64_t)addr));
|
||||
context->rax = (size_t)result;
|
||||
return;
|
||||
}
|
||||
@ -90,13 +138,8 @@ void sys_write(Context* context, int fd, size_t size, const char* addr)
|
||||
void sys_open(Context* context, const char* filename, int flags)
|
||||
{
|
||||
Task* current_task = Scheduler::current_task();
|
||||
int fd;
|
||||
for (fd = 0; fd < TASK_MAX_FDS; fd++)
|
||||
{
|
||||
if (!current_task->files[fd].is_open()) break;
|
||||
}
|
||||
|
||||
if (fd == TASK_MAX_FDS)
|
||||
int fd = current_task->alloc_fd();
|
||||
if (fd < 0)
|
||||
{
|
||||
context->rax = -EMFILE;
|
||||
return;
|
||||
@ -179,3 +222,19 @@ void sys_close(Context* context, int fd)
|
||||
context->rax = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
void sys_mkdir(Context* context, const char* filename)
|
||||
{
|
||||
char* kfilename = Syscall::strdup_from_user(filename);
|
||||
if (!kfilename)
|
||||
{
|
||||
context->rax = -EFAULT;
|
||||
return;
|
||||
}
|
||||
|
||||
int rc = VFS::mkdir(kfilename);
|
||||
|
||||
kfree(kfilename);
|
||||
|
||||
context->rax = rc;
|
||||
}
|
@ -1,10 +1,12 @@
|
||||
#define MODULE "sched"
|
||||
|
||||
#include "thread/Scheduler.h"
|
||||
#include "assert.h"
|
||||
#include "errno.h"
|
||||
#include "interrupts/Interrupts.h"
|
||||
#include "kassert.h"
|
||||
#include "log/Log.h"
|
||||
#include "memory/MemoryManager.h"
|
||||
#include "memory/PMM.h"
|
||||
#include "memory/VMM.h"
|
||||
#include "misc/hang.h"
|
||||
#include "misc/utils.h"
|
||||
@ -31,6 +33,51 @@ extern "C" void idle_task_function();
|
||||
|
||||
static uint64_t frequency;
|
||||
|
||||
template <typename Callback> void sched_for_each_task(Callback callback)
|
||||
{
|
||||
Task* task = base_task;
|
||||
if (!task) return;
|
||||
do {
|
||||
bool will_continue = callback(task);
|
||||
if (!will_continue) break;
|
||||
task = task->next_task;
|
||||
} while (task != base_task);
|
||||
}
|
||||
|
||||
Task* Scheduler::find_by_pid(uint64_t pid)
|
||||
{
|
||||
Task* result = nullptr;
|
||||
sched_for_each_task([&](Task* task) {
|
||||
if (task->id == pid)
|
||||
{
|
||||
result = task;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
void Scheduler::append_task(Task* task)
|
||||
{
|
||||
if (!base_task)
|
||||
{
|
||||
ASSERT(!end_task);
|
||||
base_task = task;
|
||||
end_task = base_task;
|
||||
task->next_task = task;
|
||||
task->prev_task = task;
|
||||
}
|
||||
else
|
||||
{
|
||||
end_task->next_task = task;
|
||||
task->prev_task = end_task;
|
||||
base_task->prev_task = task;
|
||||
task->next_task = base_task;
|
||||
end_task = task;
|
||||
}
|
||||
}
|
||||
|
||||
void Scheduler::init()
|
||||
{
|
||||
memset(&idle_task, 0, sizeof(Task));
|
||||
@ -44,22 +91,12 @@ void Scheduler::init()
|
||||
idle_task.user_task = false;
|
||||
idle_task.state = idle_task.Idle;
|
||||
|
||||
base_task = new Task;
|
||||
end_task = base_task;
|
||||
sched_current_task = base_task;
|
||||
sched_current_task->id = free_tid++;
|
||||
sched_current_task->task_time = 20; // gets 20 ms of cpu time before next switch
|
||||
sched_current_task->next_task = sched_current_task;
|
||||
sched_current_task->prev_task = sched_current_task;
|
||||
sched_current_task->state = sched_current_task->Running;
|
||||
sched_current_task->user_task = false;
|
||||
task_num++;
|
||||
// the other registers will be saved next task switch
|
||||
sched_current_task = &idle_task;
|
||||
|
||||
frequency = 1000 / PIT::frequency();
|
||||
}
|
||||
|
||||
void Scheduler::add_kernel_task(void (*task)(void))
|
||||
void Scheduler::add_kernel_task(const char* taskname, void (*task)(void))
|
||||
{
|
||||
Task* new_task = new Task;
|
||||
ASSERT(new_task);
|
||||
@ -76,53 +113,51 @@ void Scheduler::add_kernel_task(void (*task)(void))
|
||||
new_task->task_sleep = 0;
|
||||
new_task->task_time = 0;
|
||||
new_task->cpu_time = 0;
|
||||
end_task->next_task = new_task;
|
||||
new_task->prev_task = end_task;
|
||||
base_task->prev_task = new_task;
|
||||
new_task->next_task = base_task;
|
||||
end_task = new_task;
|
||||
strlcpy(new_task->name, taskname, sizeof(new_task->name));
|
||||
append_task(new_task);
|
||||
new_task->state = new_task->Running;
|
||||
task_num++;
|
||||
kinfoln("Adding kernel task: starts at %lx, tid %ld, stack at %lx, total tasks: %ld", new_task->regs.rip,
|
||||
new_task->id, new_task->regs.rsp, task_num);
|
||||
kinfoln("Adding kernel task: %s, starts at %lx, PID %ld, stack at %lx, total tasks: %ld", new_task->name,
|
||||
new_task->regs.rip, new_task->id, new_task->regs.rsp, task_num);
|
||||
}
|
||||
|
||||
Task* Scheduler::create_user_task()
|
||||
{
|
||||
Task* new_task = new Task;
|
||||
ASSERT(new_task);
|
||||
if (!new_task) return nullptr;
|
||||
memset(&new_task->regs, 0, sizeof(Context));
|
||||
new_task->user_task = true;
|
||||
new_task->id = free_tid++;
|
||||
new_task->allocated_stack = (uint64_t)MemoryManager::get_pages(
|
||||
TASK_PAGES_IN_STACK, MAP_READ_WRITE | MAP_USER); // 16 KB is enough for everyone, right?
|
||||
new_task->regs.rsp = get_top_of_stack(new_task->allocated_stack, TASK_PAGES_IN_STACK);
|
||||
new_task->task_sleep = 0;
|
||||
new_task->task_time = 0;
|
||||
new_task->cpu_time = 0;
|
||||
end_task->next_task = new_task;
|
||||
new_task->prev_task = end_task;
|
||||
base_task->prev_task = new_task;
|
||||
new_task->next_task = base_task;
|
||||
end_task = new_task;
|
||||
append_task(new_task);
|
||||
task_num++;
|
||||
return new_task;
|
||||
}
|
||||
|
||||
void Scheduler::load_user_task(const char* filename)
|
||||
long Scheduler::load_user_task(const char* filename)
|
||||
{
|
||||
kinfoln("Loading user task: %s", filename);
|
||||
Interrupts::push_and_disable();
|
||||
if (ELFLoader::check_elf_image_from_filesystem(filename) < 0)
|
||||
long result;
|
||||
if ((result = ELFLoader::check_elf_image_from_filesystem(filename)) < 0)
|
||||
{
|
||||
kerrorln("Failed to load %s from initrd", filename);
|
||||
Interrupts::pop();
|
||||
return;
|
||||
return result;
|
||||
}
|
||||
if ((uint64_t)result > PMM::get_free()) { return -ENOMEM; }
|
||||
Task* new_task = new Task;
|
||||
ASSERT(new_task);
|
||||
memset(&new_task->regs, 0, sizeof(Context));
|
||||
new_task->id = free_tid++;
|
||||
if (!new_task->allocator.init())
|
||||
{
|
||||
delete new_task;
|
||||
Interrupts::pop();
|
||||
return -ENOMEM;
|
||||
}
|
||||
new_task->address_space = AddressSpace::create();
|
||||
VMM::switch_to_user_address_space(new_task->address_space);
|
||||
ELFImage* image = ELFLoader::load_elf_from_filesystem(
|
||||
@ -132,8 +167,17 @@ void Scheduler::load_user_task(const char* filename)
|
||||
new_task->user_task = true;
|
||||
new_task->regs.rip = image->entry;
|
||||
new_task->image = image;
|
||||
new_task->allocated_stack = (uint64_t)MemoryManager::get_pages(
|
||||
TASK_PAGES_IN_STACK, MAP_READ_WRITE | MAP_USER); // 16 KB is enough for everyone, right?
|
||||
new_task->allocated_stack = (uint64_t)MemoryManager::get_pages_at(
|
||||
0x100000, TASK_PAGES_IN_STACK, MAP_READ_WRITE | MAP_USER); // 16 KB is enough for everyone, right?
|
||||
if (!new_task->allocated_stack)
|
||||
{
|
||||
new_task->address_space.destroy();
|
||||
delete new_task;
|
||||
ELFLoader::release_elf_image(image);
|
||||
VMM::switch_back_to_kernel_address_space();
|
||||
Interrupts::pop();
|
||||
return -ENOMEM;
|
||||
}
|
||||
new_task->regs.rsp = get_top_of_stack(new_task->allocated_stack, TASK_PAGES_IN_STACK);
|
||||
new_task->regs.cs = 0x18 | 0x03;
|
||||
new_task->regs.ss = 0x20 | 0x03;
|
||||
@ -142,17 +186,15 @@ void Scheduler::load_user_task(const char* filename)
|
||||
new_task->task_sleep = 0;
|
||||
new_task->task_time = 0;
|
||||
new_task->cpu_time = 0;
|
||||
end_task->next_task = new_task;
|
||||
new_task->prev_task = end_task;
|
||||
base_task->prev_task = new_task;
|
||||
new_task->next_task = base_task;
|
||||
end_task = new_task;
|
||||
strlcpy(new_task->name, filename, sizeof(new_task->name));
|
||||
append_task(new_task);
|
||||
new_task->state = new_task->Running;
|
||||
task_num++;
|
||||
kinfoln("Adding user task: loaded at %lx, tid %ld, stack at %lx, total tasks: %ld", new_task->regs.rip,
|
||||
new_task->id, new_task->regs.rsp, task_num);
|
||||
kinfoln("Adding user task: %s, loaded at %lx, PID %ld, stack at %lx, total tasks: %ld", new_task->name,
|
||||
new_task->regs.rip, new_task->id, new_task->regs.rsp, task_num);
|
||||
VMM::switch_back_to_kernel_address_space();
|
||||
Interrupts::pop();
|
||||
return (long)new_task->id;
|
||||
}
|
||||
|
||||
void Scheduler::reset_task(Task* task, ELFImage* new_image)
|
||||
@ -168,8 +210,8 @@ void Scheduler::reset_task(Task* task, ELFImage* new_image)
|
||||
task->regs.rflags = (1 << 21) | (1 << 9); // enable interrupts
|
||||
task->task_sleep = 0;
|
||||
task->cpu_time = 0;
|
||||
kinfoln("Resetting task: loaded at %lx, tid %ld, stack at %lx, total tasks: %ld", task->regs.rip, task->id,
|
||||
task->regs.rsp, task_num);
|
||||
kinfoln("Resetting task: %s, loaded at %lx, PID %ld, stack at %lx, total tasks: %ld", task->name, task->regs.rip,
|
||||
task->id, task->regs.rsp, task_num);
|
||||
}
|
||||
|
||||
void Scheduler::reap_task(Task* task)
|
||||
@ -184,8 +226,9 @@ void Scheduler::reap_task(Task* task)
|
||||
VMM::apply_address_space();
|
||||
VMM::switch_to_user_address_space(exiting_task->address_space);
|
||||
}
|
||||
kinfoln("reaping task %ld, exited with code %ld", exiting_task->id, exiting_task->exit_status);
|
||||
if (exiting_task->allocated_stack)
|
||||
kinfoln("reaping task %s, PID %ld, exited with code %ld", exiting_task->name, exiting_task->id,
|
||||
exiting_task->exit_status);
|
||||
if (exiting_task->allocated_stack && !exiting_task->is_user_task())
|
||||
MemoryManager::release_pages((void*)exiting_task->allocated_stack, TASK_PAGES_IN_STACK);
|
||||
if (exiting_task->image) // FIXME: Also free pages the task has mmap-ed but not munmap-ed.
|
||||
{
|
||||
@ -194,6 +237,7 @@ void Scheduler::reap_task(Task* task)
|
||||
}
|
||||
if (exiting_task->is_user_task())
|
||||
{
|
||||
exiting_task->allocator.free();
|
||||
VMM::switch_back_to_kernel_address_space();
|
||||
VMM::apply_address_space();
|
||||
Interrupts::push_and_enable();
|
||||
@ -276,17 +320,15 @@ void Scheduler::reap_tasks()
|
||||
|
||||
static void sched_decrement_sleep_times()
|
||||
{
|
||||
Task* task = base_task;
|
||||
if (!task) return;
|
||||
do {
|
||||
sched_for_each_task([](Task* task) {
|
||||
if (task->task_sleep > 0)
|
||||
{
|
||||
task->task_sleep -= frequency;
|
||||
if (task->task_sleep < 0) task->task_sleep = 0;
|
||||
}
|
||||
if (task->task_sleep == 0 && task->state == task->Sleeping) task->state = task->Running;
|
||||
task = task->next_task;
|
||||
} while (task != base_task);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
void Scheduler::task_tick(Context* context)
|
||||
@ -309,7 +351,7 @@ void Scheduler::task_yield(Context* context)
|
||||
{
|
||||
ASSERT(Interrupts::is_in_handler());
|
||||
Interrupts::disable();
|
||||
get_context_to_task(*sched_current_task, context);
|
||||
sched_current_task->save_context(context);
|
||||
bool was_idle = false;
|
||||
if (sched_current_task->state == sched_current_task->Idle)
|
||||
{
|
||||
@ -323,15 +365,14 @@ void Scheduler::task_yield(Context* context)
|
||||
{
|
||||
if (sched_current_task->id != original_task->id || was_idle)
|
||||
{
|
||||
if (!was_idle && original_task->is_user_task() && original_task->state != original_task->Exited)
|
||||
if (!was_idle && original_task->is_user_task() && !original_task->has_died())
|
||||
{
|
||||
task_save_floating(*original_task);
|
||||
original_task->save_floating();
|
||||
}
|
||||
if (sched_current_task->is_user_task())
|
||||
{
|
||||
VMM::switch_to_user_address_space(sched_current_task->address_space);
|
||||
VMM::apply_address_space();
|
||||
task_restore_floating(*sched_current_task);
|
||||
sched_current_task->switch_to_address_space();
|
||||
sched_current_task->restore_floating();
|
||||
}
|
||||
else if (!was_idle && original_task->is_user_task() && !sched_current_task->is_user_task())
|
||||
{
|
||||
@ -340,17 +381,17 @@ void Scheduler::task_yield(Context* context)
|
||||
}
|
||||
}
|
||||
sched_current_task->task_time = 20;
|
||||
set_context_from_task(*sched_current_task, context);
|
||||
sched_current_task->restore_context(context);
|
||||
return;
|
||||
}
|
||||
} while (sched_current_task != original_task);
|
||||
if (!was_idle && original_task->is_user_task() && original_task->state != original_task->Exited)
|
||||
{
|
||||
task_save_floating(*original_task);
|
||||
original_task->save_floating();
|
||||
}
|
||||
sched_current_task = &idle_task;
|
||||
sched_current_task->task_time = frequency;
|
||||
if (!was_idle) { set_context_from_task(*sched_current_task, context); }
|
||||
if (!was_idle) { sched_current_task->restore_context(context); }
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1,29 +1,68 @@
|
||||
#include "thread/Task.h"
|
||||
#include "memory/VMM.h"
|
||||
#include "std/string.h"
|
||||
|
||||
void set_context_from_task(Task& task, Context* ctx)
|
||||
void Task::restore_context(Context* context)
|
||||
{
|
||||
memcpy(ctx, &task.regs, sizeof(Context));
|
||||
memcpy(context, ®s, sizeof(Context));
|
||||
}
|
||||
|
||||
void get_context_to_task(Task& task, Context* ctx)
|
||||
void Task::save_context(Context* context)
|
||||
{
|
||||
memcpy(&task.regs, ctx, sizeof(Context));
|
||||
memcpy(®s, context, sizeof(Context));
|
||||
}
|
||||
|
||||
void task_save_floating(Task& task)
|
||||
void Task::save_floating()
|
||||
{
|
||||
task.floating_saved = true;
|
||||
asm volatile("fxsave (%0)" : : "r"(&task.floating_region));
|
||||
floating_saved = true;
|
||||
asm volatile("fxsave (%0)" : : "r"((char*)floating_region));
|
||||
}
|
||||
|
||||
void task_restore_floating(Task& task)
|
||||
void Task::restore_floating()
|
||||
{
|
||||
if (!task.floating_saved) return;
|
||||
asm volatile("fxrstor (%0)" : : "r"(&task.floating_region));
|
||||
if (!floating_saved) return;
|
||||
asm volatile("fxrstor (%0)" : : "r"((char*)floating_region));
|
||||
}
|
||||
|
||||
bool Task::is_user_task()
|
||||
{
|
||||
return user_task;
|
||||
}
|
||||
|
||||
int Task::alloc_fd()
|
||||
{
|
||||
int fd;
|
||||
for (fd = 0; fd < TASK_MAX_FDS; fd++)
|
||||
{
|
||||
if (!files[fd].is_open()) break;
|
||||
}
|
||||
|
||||
if (fd == TASK_MAX_FDS) { return -1; }
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
int Task::alloc_fd_greater_than_or_equal(int base_fd)
|
||||
{
|
||||
int fd;
|
||||
if (base_fd >= TASK_MAX_FDS) return -1;
|
||||
for (fd = base_fd; fd < TASK_MAX_FDS; fd++)
|
||||
{
|
||||
if (!files[fd].is_open()) break;
|
||||
}
|
||||
|
||||
if (fd == TASK_MAX_FDS) { return -1; }
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
void Task::switch_to_address_space()
|
||||
{
|
||||
VMM::switch_to_user_address_space(address_space);
|
||||
VMM::apply_address_space();
|
||||
}
|
||||
|
||||
bool Task::has_died()
|
||||
{
|
||||
return state == Exited;
|
||||
}
|
@ -17,10 +17,10 @@ static size_t symbol_strlen(const char* symbol)
|
||||
return (i - symbol);
|
||||
}
|
||||
|
||||
void get_symbol_name(uintptr_t address, char* buffer)
|
||||
void get_symbol_name(uintptr_t address, char* buffer, size_t max)
|
||||
{
|
||||
if (symbol_map.addr == (void*)-1) { symbol_map = InitRD::open("sys/moon.sym"); }
|
||||
if (!symbol_map.addr) { strncpy(buffer, "(no symbols loaded)", 20); }
|
||||
if (!symbol_map.addr) { strlcpy(buffer, "(no symbols loaded)", max); }
|
||||
while (address >= (uintptr_t)&kernel_start && address <= (uintptr_t)&kernel_end)
|
||||
{
|
||||
char addr_as_str[60];
|
||||
@ -30,11 +30,12 @@ void get_symbol_name(uintptr_t address, char* buffer)
|
||||
{
|
||||
symbol += 19;
|
||||
size_t symlen = symbol_strlen(symbol);
|
||||
memcpy(buffer, symbol, symlen);
|
||||
buffer[symlen] = 0;
|
||||
size_t copylen = (max - 1) < symlen ? (max - 1) : symlen;
|
||||
memcpy(buffer, symbol, copylen);
|
||||
buffer[copylen] = 0;
|
||||
return;
|
||||
}
|
||||
address--;
|
||||
}
|
||||
strncpy(buffer, "(no symbol)", 12);
|
||||
strlcpy(buffer, "(no symbol)", max);
|
||||
}
|
@ -1,4 +1,7 @@
|
||||
#define MODULE "trace"
|
||||
|
||||
#include "trace/StackTracer.h"
|
||||
#include "log/Log.h"
|
||||
#include "memory/Memory.h"
|
||||
#include "std/stdio.h"
|
||||
#include "trace/Resolve.h"
|
||||
@ -21,11 +24,11 @@ typedef struct stackframe
|
||||
void StackTracer::trace()
|
||||
{
|
||||
stackframe* frame = (stackframe*)m_base_pointer;
|
||||
while (frame)
|
||||
while (frame && frame->instruction)
|
||||
{
|
||||
char symbol_name[512];
|
||||
get_symbol_name(frame->instruction, symbol_name);
|
||||
printf("%lx: %s\n", frame->instruction, symbol_name);
|
||||
get_symbol_name(frame->instruction - sizeof(uintptr_t), symbol_name, sizeof(symbol_name));
|
||||
kinfoln("%lx: %s", frame->instruction - sizeof(uintptr_t), symbol_name);
|
||||
frame = frame->next;
|
||||
}
|
||||
}
|
||||
@ -33,8 +36,8 @@ void StackTracer::trace()
|
||||
void StackTracer::trace_with_ip(uintptr_t ip)
|
||||
{
|
||||
char symbol_name[512];
|
||||
get_symbol_name(ip, symbol_name);
|
||||
printf("%lx: %s\n", ip, symbol_name);
|
||||
get_symbol_name(ip, symbol_name, sizeof(symbol_name));
|
||||
kinfoln("%lx: %s", ip, symbol_name);
|
||||
trace();
|
||||
}
|
||||
|
||||
|
24
libs/libc/include/assert.h
Normal file
24
libs/libc/include/assert.h
Normal file
@ -0,0 +1,24 @@
|
||||
#ifndef _ASSERT_H
|
||||
#define _ASSERT_H
|
||||
|
||||
#include <bits/macros.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
__lc_noreturn bool __assertion_failed(const char* file, int line, const char* function, const char* expr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef NDEBUG
|
||||
#define assert(expr) (void)0
|
||||
#else
|
||||
#define assert(expr) (bool)(expr) || __assertion_failed(__FILE__, __LINE__, __FUNCTION__, #expr)
|
||||
#endif
|
||||
|
||||
#endif
|
@ -1,8 +1,9 @@
|
||||
#ifndef _BITS_MACROS_H
|
||||
#define _BITS_MACROS_H
|
||||
|
||||
#define noreturn __attribute__((noreturn))
|
||||
#define align(n) __attribute__((aligned(n)))
|
||||
#define deprecated(msg) __attribute__((deprecated(msg)))
|
||||
#define __lc_noreturn __attribute__((noreturn))
|
||||
#define __lc_align(n) __attribute__((aligned(n)))
|
||||
#define __lc_deprecated(msg) __attribute__((deprecated(msg)))
|
||||
#define __lc_unreachable __builtin_unreachable
|
||||
|
||||
#endif
|
58
libs/libc/include/ctype.h
Normal file
58
libs/libc/include/ctype.h
Normal file
@ -0,0 +1,58 @@
|
||||
#ifndef _CTYPE_H
|
||||
#define _CTYPE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/* Is this character alphanumeric? */
|
||||
int isalnum(int c);
|
||||
|
||||
/* Is this character a letter? */
|
||||
int isalpha(int c);
|
||||
|
||||
/* Is this character part of ASCII? */
|
||||
int isascii(int c);
|
||||
|
||||
/* Is this character a blank character (space or tab)? */
|
||||
int isblank(int c);
|
||||
|
||||
/* Is this character a control character? */
|
||||
int iscntrl(int c);
|
||||
|
||||
/* Is this character a digit? */
|
||||
int isdigit(int c);
|
||||
|
||||
/* Is this character any printable character except space? */
|
||||
int isgraph(int c);
|
||||
|
||||
/* Is this character a lowercase letter? */
|
||||
int islower(int c);
|
||||
|
||||
/* Is this character any printable character (including space)? */
|
||||
int isprint(int c);
|
||||
|
||||
/* Is this character any printable character which is not a space or an alphanumeric character? */
|
||||
int ispunct(int c);
|
||||
|
||||
/* Is this character any space character (space, form feed, newline, carriage return, tab, vertical tab)? */
|
||||
int isspace(int c);
|
||||
|
||||
/* Is this character an uppercase letter? */
|
||||
int isupper(int c);
|
||||
|
||||
/* Is this character a hexadecimal digit (0-9, a-f, A-F)? */
|
||||
int isxdigit(int c);
|
||||
|
||||
/* Returns the lowercase form of the specified character. */
|
||||
int tolower(int c);
|
||||
|
||||
/* Returns the uppercase form of the specified character. */
|
||||
int toupper(int c);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
@ -10,9 +10,14 @@ extern int errno;
|
||||
#define EBADF 9 // Bad file descriptor
|
||||
#define ENOMEM 12 // Cannot allocate memory
|
||||
#define EFAULT 14 // Bad address
|
||||
#define EEXIST 17 // File exists
|
||||
#define ENOTDIR 20 // Not a directory
|
||||
#define EISDIR 21 // Is a directory
|
||||
#define EINVAL 22 // Invalid argument
|
||||
#define EMFILE 24 // Too many open files
|
||||
#define ENOSPC 28 // No space left on device
|
||||
#define EPIPE 32 // Broken pipe. Not implemented.
|
||||
#define ENOSYS 38 // Function not implemented
|
||||
#define ENOTSUP 95 // Operation not supported.
|
||||
|
||||
#endif
|
@ -8,6 +8,9 @@
|
||||
/* Open for reading and writing. */
|
||||
#define O_RDWR 3
|
||||
|
||||
/* Duplicate a file descriptor. */
|
||||
#define F_DUPFD 0
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
@ -16,6 +19,9 @@ extern "C"
|
||||
/* Opens the file specified by pathname. Returns a file descriptor on success, or -1 on error. */
|
||||
int open(const char* pathname, int flags);
|
||||
|
||||
/* Performs an operation on the file descriptor fd determined by cmd. */
|
||||
int fcntl(int fd, int cmd, ...);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
63
libs/libc/include/inttypes.h
Normal file
63
libs/libc/include/inttypes.h
Normal file
@ -0,0 +1,63 @@
|
||||
#ifndef _INTTYPES_H
|
||||
#define _INTTYPES_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define PRId8 "%d"
|
||||
#define PRId16 "%d"
|
||||
#define PRId32 "%d"
|
||||
#define PRId64 "%ld"
|
||||
#define PRIdLEAST8 "%d"
|
||||
#define PRIdLEAST16 "%d"
|
||||
#define PRIdLEAST32 "%d"
|
||||
#define PRIdLEAST64 "%ld"
|
||||
#define PRIdFAST8 "%d"
|
||||
#define PRIdFAST16 "%d"
|
||||
#define PRIdFAST32 "%d"
|
||||
#define PRIdFAST64 "%ld"
|
||||
#define PRIdMAX "%ld"
|
||||
#define PRIdPTR "%ld"
|
||||
#define PRIi8 "%d"
|
||||
#define PRIi16 "%d"
|
||||
#define PRIi32 "%d"
|
||||
#define PRIi64 "%ld"
|
||||
#define PRIiLEAST8 "%d"
|
||||
#define PRIiLEAST16 "%d"
|
||||
#define PRIiLEAST32 "%d"
|
||||
#define PRIiLEAST64 "%ld"
|
||||
#define PRIiFAST8 "%d"
|
||||
#define PRIiFAST16 "%d"
|
||||
#define PRIiFAST32 "%d"
|
||||
#define PRIiFAST64 "%ld"
|
||||
#define PRIiMAX "%ld"
|
||||
#define PRIiPTR "%ld"
|
||||
#define PRIu8 "%u"
|
||||
#define PRIu16 "%u"
|
||||
#define PRIu32 "%u"
|
||||
#define PRIu64 "%lu"
|
||||
#define PRIuLEAST8 "%u"
|
||||
#define PRIuLEAST16 "%u"
|
||||
#define PRIuLEAST32 "%u"
|
||||
#define PRIuLEAST64 "%lu"
|
||||
#define PRIuFAST8 "%u"
|
||||
#define PRIuFAST16 "%u"
|
||||
#define PRIuFAST32 "%u"
|
||||
#define PRIuFAST64 "%lu"
|
||||
#define PRIuMAX "%lu"
|
||||
#define PRIuPTR "%lu"
|
||||
#define PRIx8 "%x"
|
||||
#define PRIx16 "%x"
|
||||
#define PRIx32 "%x"
|
||||
#define PRIx64 "%lx"
|
||||
#define PRIxLEAST8 "%x"
|
||||
#define PRIxLEAST16 "%x"
|
||||
#define PRIxLEAST32 "%x"
|
||||
#define PRIxLEAST64 "%lx"
|
||||
#define PRIxFAST8 "%x"
|
||||
#define PRIxFAST16 "%x"
|
||||
#define PRIxFAST32 "%x"
|
||||
#define PRIxFAST64 "%lx"
|
||||
#define PRIxMAX "%lx"
|
||||
#define PRIxPTR "%lx"
|
||||
|
||||
#endif
|
21
libs/libc/include/libgen.h
Normal file
21
libs/libc/include/libgen.h
Normal file
@ -0,0 +1,21 @@
|
||||
#ifndef _LIBGEN_H
|
||||
#define _LIBGEN_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/* Returns the last component of a path. This function is allowed to modify the string passed to it, so it should
|
||||
* probably be a copy of another string. */
|
||||
char* basename(char* path);
|
||||
|
||||
/* Returns the parent directory of a path. This function is allowed to modify the string passed to it, so it should
|
||||
* probably be a copy of another string. */
|
||||
char* dirname(char* path);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
@ -9,14 +9,11 @@ extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/* Returns the current program's thread identifier. */
|
||||
pid_t gettid();
|
||||
|
||||
/* Sleeps for ms milliseconds. */
|
||||
unsigned int msleep(unsigned int ms);
|
||||
|
||||
/* Prints a message to standard error and aborts the program. */
|
||||
noreturn void __luna_abort(const char* message);
|
||||
__lc_noreturn void __luna_abort(const char* message);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
10
libs/libc/include/luna/os-limits.h
Normal file
10
libs/libc/include/luna/os-limits.h
Normal file
@ -0,0 +1,10 @@
|
||||
#ifndef _LUNA_OS_LIMITS_H
|
||||
#define _LUNA_OS_LIMITS_H
|
||||
|
||||
#define OPEN_MAX 32
|
||||
#define ATEXIT_MAX 32
|
||||
|
||||
#define PAGESIZE 4096
|
||||
#define PAGE_SIZE 4096
|
||||
|
||||
#endif
|
@ -6,15 +6,19 @@
|
||||
#define SYS_sleep 2
|
||||
#define SYS_write 3
|
||||
#define SYS_paint 4
|
||||
#define SYS_rand 5
|
||||
#define SYS_gettid 6
|
||||
#define SYS_mmap 7
|
||||
#define SYS_munmap 8
|
||||
#define SYS_open 9
|
||||
#define SYS_read 10
|
||||
#define SYS_close 11
|
||||
#define SYS_seek 12
|
||||
#define SYS_exec 13
|
||||
#define SYS_getpid 5
|
||||
#define SYS_mmap 6
|
||||
#define SYS_munmap 7
|
||||
#define SYS_open 8
|
||||
#define SYS_read 9
|
||||
#define SYS_close 10
|
||||
#define SYS_seek 11
|
||||
#define SYS_exec 12
|
||||
#define SYS_fcntl 13
|
||||
#define SYS_mprotect 14
|
||||
#define SYS_clock 15
|
||||
#define SYS_mkdir 16
|
||||
#define SYS_fork 17
|
||||
|
||||
#ifndef __want_syscalls
|
||||
#ifdef __cplusplus
|
||||
|
16
libs/libc/include/sched.h
Normal file
16
libs/libc/include/sched.h
Normal file
@ -0,0 +1,16 @@
|
||||
#ifndef _SCHED_H
|
||||
#define _SCHED_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/* Yield the processor. */
|
||||
int sched_yield(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
32
libs/libc/include/setjmp.h
Normal file
32
libs/libc/include/setjmp.h
Normal file
@ -0,0 +1,32 @@
|
||||
#ifndef _SETJMP_H
|
||||
#define _SETJMP_H
|
||||
|
||||
#include <bits/macros.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef uintptr_t jmp_buf[8];
|
||||
typedef uintptr_t sigjmp_buf[8];
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/* Saves the current execution state in env. Returns 0 when called from the first time, or nonzero when returning
|
||||
* from longjmp. */
|
||||
int setjmp(jmp_buf env);
|
||||
|
||||
/* Right now, does the exact same as setjmp() (savesigs is ignored), since signals are not implemented. */
|
||||
int sigsetjmp(sigjmp_buf env, int savesigs);
|
||||
|
||||
/* Restores the execution state saved in env by a setjmp() call. */
|
||||
__lc_noreturn void longjmp(jmp_buf env, int val);
|
||||
|
||||
/* Right now, does the exact same as longjmp(), since signals are not implemented. */
|
||||
__lc_noreturn void siglongjmp(sigjmp_buf env, int val);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
7
libs/libc/include/signal.h
Normal file
7
libs/libc/include/signal.h
Normal file
@ -0,0 +1,7 @@
|
||||
#ifndef _SIGNAL_H
|
||||
#define _SIGNAL_H
|
||||
|
||||
typedef int sig_atomic_t; // FIXME: Implement signals (I'm trying to build bc, this header is only to satisfy it) and
|
||||
// use a proper atomic integer type.
|
||||
|
||||
#endif
|
@ -6,6 +6,8 @@
|
||||
|
||||
#include <bits/seek.h>
|
||||
|
||||
#define FOPEN_MAX 32
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int f_fd;
|
||||
@ -40,6 +42,9 @@ extern "C"
|
||||
/* Returns a new file associated with the file descriptor fd. */
|
||||
FILE* fdopen(int fd, const char* mode);
|
||||
|
||||
/* Returns the file descriptor associated with the file stream. */
|
||||
int fileno(FILE* stream);
|
||||
|
||||
/* Writes formatted output according to the string format to the file stream. */
|
||||
int fprintf(FILE* stream, const char* format, ...);
|
||||
|
||||
|
@ -10,16 +10,25 @@ extern "C"
|
||||
#endif
|
||||
|
||||
/* Aborts the program. */
|
||||
noreturn void abort();
|
||||
__lc_noreturn void abort(void);
|
||||
|
||||
/* Normally exits the program with the specified status code. */
|
||||
noreturn void exit(int status);
|
||||
__lc_noreturn void exit(int status);
|
||||
|
||||
/* Abnormally exits the program with the specified status code. */
|
||||
__lc_noreturn void _Exit(int status);
|
||||
|
||||
/* Registers a handler function to be run at normal program termination. */
|
||||
int atexit(void (*handler)(void));
|
||||
|
||||
/* Not implemented.*/
|
||||
int atoi(const char*);
|
||||
/* Returns an integer (of type int) parsed from the string str. */
|
||||
int atoi(const char* str);
|
||||
|
||||
/* Returns an integer (of type long) parsed from the string str. */
|
||||
long atol(const char* str);
|
||||
|
||||
/* Returns an integer (of type long long) parsed from the string str. */
|
||||
long long atoll(const char* str);
|
||||
|
||||
/* Not implemented. */
|
||||
char* getenv(const char*);
|
||||
@ -41,6 +50,12 @@ extern "C"
|
||||
* is undefined behavior. */
|
||||
void free(void* ptr);
|
||||
|
||||
/* Returns a random number. */
|
||||
int rand(void);
|
||||
|
||||
/* Seeds the random number generator with the specified seed. */
|
||||
void srand(unsigned int seed);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -15,15 +15,26 @@ extern "C"
|
||||
/* Sets n bytes of buf to c, cast to a character. */
|
||||
void* memset(void* buf, int c, size_t n);
|
||||
|
||||
/* Clears n bytes of buf. */
|
||||
void* memclr(void* buf, size_t n);
|
||||
/* Searches for the character c in n bytes of buf. */
|
||||
void* memchr(const void* buf, int c, size_t n);
|
||||
|
||||
/* Compares n bytes of memory at a and b. */
|
||||
int memcmp(const void* a, const void* b, size_t n);
|
||||
|
||||
/* Copies n bytes from src to dst. Can be used if src and dst overlap. */
|
||||
void* memmove(void* dest, const void* src, size_t n);
|
||||
|
||||
/* Returns a heap-allocated copy of the string str. Should be freed when it is not used anymore. */
|
||||
char* strdup(const char* str);
|
||||
|
||||
/* Returns the length of the string str. */
|
||||
size_t strlen(const char* str);
|
||||
|
||||
/* Copies the string src into dest. This function is unsafe, use strncpy instead. */
|
||||
deprecated("strcpy is unsafe and should not be used; use strncpy instead") char* strcpy(char* dest,
|
||||
const char* src);
|
||||
/* Returns the length of the string str, while examining at most max bytes of str. */
|
||||
size_t strnlen(const char* str, size_t max);
|
||||
|
||||
/* Copies at most size-1 bytes from the string src into dest, null-terminating the result. */
|
||||
size_t strlcpy(char* dst, const char* src, size_t size);
|
||||
|
||||
/* Copies at most max bytes from the string src into dest. */
|
||||
char* strncpy(char* dest, const char* src, size_t max);
|
||||
@ -31,16 +42,35 @@ extern "C"
|
||||
/* Returns a pointer to the first occurrence of the character c in str, or NULL if it is not found. */
|
||||
char* strchr(const char* str, int c);
|
||||
|
||||
/* Concatenates the string src into dest. This function is unsafe, use strncat instead. */
|
||||
deprecated("strcat is unsafe and should not be used; use strncat instead") char* strcat(char* dest,
|
||||
const char* src);
|
||||
/* Returns a pointer to the last occurrence of the character c in str, or NULL if it is not found. */
|
||||
char* strrchr(const char* str, int c);
|
||||
|
||||
/* Concatenates at most max bytes of the string src into dest. */
|
||||
char* strncat(char* dest, const char* src, size_t max);
|
||||
|
||||
/* Compares strings a and b. You might prefer to use the safer strncmp function. */
|
||||
int strcmp(const char* a, const char* b);
|
||||
|
||||
/* Compares at most max bytes of the strings a and b. */
|
||||
int strncmp(const char* a, const char* b, size_t max);
|
||||
|
||||
/* Searches for the needle string in the haystack string. */
|
||||
char* strstr(const char* haystack, const char* needle);
|
||||
|
||||
/* Returns the error string associated with the error number err. */
|
||||
char* strerror(int err);
|
||||
|
||||
/* Clears n bytes of buf. */
|
||||
void* bzero(void* buf, size_t n);
|
||||
|
||||
/* Copies the string src into dest. This function is unsafe, use strlcpy instead. */
|
||||
__lc_deprecated("strcpy is unsafe and should not be used; use strlcpy instead") char* strcpy(char* dest,
|
||||
const char* src);
|
||||
|
||||
/* Concatenates the string src into dest. This function is unsafe, use strncat instead. */
|
||||
__lc_deprecated("strcat is unsafe and should not be used; use strncat instead") char* strcat(char* dest,
|
||||
const char* src);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -7,7 +7,9 @@
|
||||
/* Address returned by mmap when it fails. */
|
||||
#define MAP_FAILED (void*)-1
|
||||
|
||||
#define PROT_READ_WRITE 1
|
||||
#define PROT_NONE 0
|
||||
#define PROT_READ 1
|
||||
#define PROT_WRITE 2
|
||||
|
||||
#define PAGE_SIZE 4096
|
||||
|
||||
@ -24,6 +26,9 @@ extern "C"
|
||||
* address space. */
|
||||
int munmap(void* addr, size_t size);
|
||||
|
||||
/* Protects size bytes of memory according to the prot argument. */
|
||||
int mprotect(void* addr, size_t size, int prot);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
18
libs/libc/include/sys/stat.h
Normal file
18
libs/libc/include/sys/stat.h
Normal file
@ -0,0 +1,18 @@
|
||||
#ifndef _SYS_STAT_H
|
||||
#define _SYS_STAT_H
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/* Creates a new directory at the path pathname. FIXME: For now, mode is ignored. */
|
||||
int mkdir(const char* pathname, mode_t mode);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
@ -13,4 +13,7 @@ typedef long int ssize_t;
|
||||
/* The type of an offset into a file. */
|
||||
typedef long int off_t;
|
||||
|
||||
/* The type of a file's mode. */
|
||||
typedef unsigned short mode_t;
|
||||
|
||||
#endif
|
@ -0,0 +1,22 @@
|
||||
#ifndef _TIME_H
|
||||
#define _TIME_H
|
||||
|
||||
typedef long int clock_t;
|
||||
typedef long int time_t;
|
||||
|
||||
#define CLOCKS_PER_SEC 1000
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/* Returns a number representing how much CPU time has been used by this process. Divide this by CLOCKS_PER_SEC to
|
||||
* get the value in seconds. */
|
||||
clock_t clock(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
@ -3,8 +3,12 @@
|
||||
|
||||
#include <bits/macros.h>
|
||||
#include <bits/seek.h>
|
||||
#include <luna/os-limits.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#define STDOUT_FILENO 0
|
||||
#define STDERR_FILENO 1
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
@ -18,11 +22,16 @@ extern "C"
|
||||
int execve(const char*, char* const[], char* const[]);
|
||||
/* Not implemented. */
|
||||
int execvp(const char*, char* const[]);
|
||||
/* Not implemented. */
|
||||
|
||||
/* Creates an identical copy (child) of the current process (parent). Returns 0 to the child, and the child's PID to
|
||||
* the parent. */
|
||||
pid_t fork(void);
|
||||
|
||||
/* Returns the current process's process ID. */
|
||||
pid_t getpid(void);
|
||||
|
||||
/* Terminates the program with the status code status. */
|
||||
noreturn void _exit(int status);
|
||||
__lc_noreturn void _exit(int status);
|
||||
|
||||
/* Calls the kernel for a specific service, determined by number. */
|
||||
long syscall(long number, ...);
|
||||
@ -42,6 +51,9 @@ extern "C"
|
||||
/* Moves the read/write file offset for fd to offset, depending on whence. */
|
||||
off_t lseek(int fd, off_t offset, int whence);
|
||||
|
||||
/* Returns a copy of the file descriptor fd. */
|
||||
int dup(int fd);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
12
libs/libc/src/assert.cpp
Normal file
12
libs/libc/src/assert.cpp
Normal file
@ -0,0 +1,12 @@
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
__lc_noreturn bool __assertion_failed(const char* file, int line, const char* function, const char* expr)
|
||||
{
|
||||
fprintf(stderr, "%s:%d: %s: Assertion '%s' failed.", file, line, function, expr);
|
||||
abort();
|
||||
}
|
||||
}
|
@ -17,7 +17,7 @@ extern "C"
|
||||
return 0;
|
||||
}
|
||||
|
||||
noreturn void exit(int status)
|
||||
__lc_noreturn void exit(int status)
|
||||
{
|
||||
for (int i = 0; i < atexit_function_count; i++) { atexit_functions[i](); }
|
||||
_exit(status);
|
||||
|
@ -13,7 +13,7 @@ int liballoc_unlock()
|
||||
|
||||
void* liballoc_alloc(size_t size)
|
||||
{
|
||||
void* result = mmap(NULL, size * PAGE_SIZE, PROT_READ_WRITE, 0, 0, 0);
|
||||
void* result = mmap(NULL, size * PAGE_SIZE, PROT_READ | PROT_WRITE, 0, 0, 0);
|
||||
if (result == MAP_FAILED) return 0;
|
||||
return (void*)result;
|
||||
}
|
||||
|
81
libs/libc/src/ctype.cpp
Normal file
81
libs/libc/src/ctype.cpp
Normal file
@ -0,0 +1,81 @@
|
||||
#include <ctype.h>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
int isalnum(int c)
|
||||
{
|
||||
return isalpha(c) || isdigit(c);
|
||||
}
|
||||
|
||||
int isalpha(int c)
|
||||
{
|
||||
return islower(c) || isupper(c);
|
||||
}
|
||||
|
||||
int isascii(int c)
|
||||
{
|
||||
return !(c & ~0x7f);
|
||||
}
|
||||
|
||||
int isblank(int c)
|
||||
{
|
||||
return c == ' ' || c == '\t';
|
||||
}
|
||||
|
||||
int iscntrl(int c)
|
||||
{
|
||||
return (unsigned int)c < 0x20 || c == 0x7f;
|
||||
}
|
||||
|
||||
int isdigit(int c)
|
||||
{
|
||||
return c >= '0' && c < ':';
|
||||
}
|
||||
|
||||
int isgraph(int c)
|
||||
{
|
||||
return (unsigned int)c - 0x21 < 0x5e;
|
||||
}
|
||||
|
||||
int islower(int c)
|
||||
{
|
||||
return c >= 'a' && c < '{';
|
||||
}
|
||||
|
||||
int isprint(int c)
|
||||
{
|
||||
return (unsigned int)c - 0x20 < 0x5f;
|
||||
}
|
||||
|
||||
int ispunct(int c)
|
||||
{
|
||||
return isgraph(c) && !isalnum(c);
|
||||
}
|
||||
|
||||
int isspace(int c)
|
||||
{
|
||||
return c == ' ' || (unsigned int)c - '\t' < 5;
|
||||
}
|
||||
|
||||
int isupper(int c)
|
||||
{
|
||||
return c >= 'A' && c < '[';
|
||||
}
|
||||
|
||||
int isxdigit(int c)
|
||||
{
|
||||
return isdigit(c) || ((unsigned int)c | 32) - 'a' < 6;
|
||||
}
|
||||
|
||||
int tolower(int c)
|
||||
{
|
||||
if (isupper(c)) return c | 32;
|
||||
return c;
|
||||
}
|
||||
|
||||
int toupper(int c)
|
||||
{
|
||||
if (islower(c)) return c & 0x5f;
|
||||
return c;
|
||||
}
|
||||
}
|
@ -1,4 +1,6 @@
|
||||
#include <fcntl.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
|
||||
@ -8,4 +10,13 @@ extern "C"
|
||||
{
|
||||
return (int)syscall(SYS_open, pathname, flags);
|
||||
}
|
||||
|
||||
int fcntl(int fd, int cmd, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, cmd);
|
||||
long result = syscall(SYS_fcntl, fd, cmd, va_arg(ap, uintptr_t));
|
||||
va_end(ap);
|
||||
return (int)result;
|
||||
}
|
||||
}
|
@ -30,14 +30,11 @@ extern "C"
|
||||
return 0; // FIXME: Implement buffered IO.
|
||||
}
|
||||
|
||||
FILE* fopen(const char* pathname, const char*)
|
||||
FILE* fopen(const char* pathname, const char* mode)
|
||||
{
|
||||
int fd = open(pathname, O_RDWR); // FIXME: Use the mode string.
|
||||
if (fd < 0) { return 0; }
|
||||
FILE* stream = (FILE*)malloc(sizeof(FILE));
|
||||
stream->f_fd = fd;
|
||||
clearerr(stream);
|
||||
return stream;
|
||||
return fdopen(fd, mode);
|
||||
}
|
||||
|
||||
FILE* fdopen(int fd, const char*)
|
||||
@ -48,11 +45,17 @@ extern "C"
|
||||
return 0;
|
||||
}
|
||||
FILE* stream = (FILE*)malloc(sizeof(FILE));
|
||||
if (!stream) { return 0; }
|
||||
stream->f_fd = fd;
|
||||
clearerr(stream);
|
||||
return stream;
|
||||
}
|
||||
|
||||
int fileno(FILE* stream)
|
||||
{
|
||||
return stream->f_fd;
|
||||
}
|
||||
|
||||
size_t fread(void* buf, size_t size, size_t nmemb, FILE* stream)
|
||||
{
|
||||
ssize_t status = read(stream->f_fd, buf, size * nmemb);
|
||||
|
@ -10,14 +10,41 @@ static void terminate_libc()
|
||||
fclose(stderr);
|
||||
}
|
||||
|
||||
static void initialize_random()
|
||||
{
|
||||
unsigned int seed = 3735928559U;
|
||||
|
||||
FILE* fp = fopen("/dev/random", "rw");
|
||||
if (!fp)
|
||||
{
|
||||
errno = 0;
|
||||
goto failed_to_read_dev_random;
|
||||
}
|
||||
|
||||
fread(&seed, sizeof(seed), 1, fp);
|
||||
if (ferror(fp))
|
||||
{
|
||||
errno = 0;
|
||||
fclose(fp);
|
||||
goto failed_to_read_dev_random;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
failed_to_read_dev_random:
|
||||
srand(seed); // If we failed, this will be seeded with a known value. Else, it will be seeded with a random value
|
||||
// from the kernel.
|
||||
return;
|
||||
}
|
||||
|
||||
static void check_for_file(int fd, FILE** target_stream, const char* path, const char* mode)
|
||||
{
|
||||
if (lseek(fd, 0, SEEK_CUR) < 0)
|
||||
{
|
||||
if (errno == EBADF) *target_stream = fopen(path, mode);
|
||||
else
|
||||
exit(errno);
|
||||
if (!*target_stream) exit(errno);
|
||||
exit(-127);
|
||||
if (!*target_stream) exit(-127);
|
||||
errno = 0;
|
||||
}
|
||||
else { *target_stream = fdopen(fd, mode); }
|
||||
@ -25,7 +52,9 @@ static void check_for_file(int fd, FILE** target_stream, const char* path, const
|
||||
|
||||
extern "C" void initialize_libc()
|
||||
{
|
||||
check_for_file(0, &stdout, "/dev/console", "rw");
|
||||
check_for_file(1, &stderr, "/dev/console", "rw");
|
||||
check_for_file(STDOUT_FILENO, &stdout, "/dev/console", "rw");
|
||||
check_for_file(STDERR_FILENO, &stderr, "/dev/console", "rw");
|
||||
|
||||
initialize_random();
|
||||
atexit(terminate_libc);
|
||||
}
|
50
libs/libc/src/libgen.cpp
Normal file
50
libs/libc/src/libgen.cpp
Normal file
@ -0,0 +1,50 @@
|
||||
#include <libgen.h>
|
||||
#include <string.h>
|
||||
|
||||
static char dot[] = ".";
|
||||
|
||||
extern "C"
|
||||
{
|
||||
char* basename(char* path)
|
||||
{
|
||||
// If path is NULL, or the string's length is 0, return .
|
||||
if (!path) return dot;
|
||||
size_t len = strlen(path);
|
||||
if (!len) return dot;
|
||||
|
||||
// Strip trailing slashes.
|
||||
char* it = path + len - 1;
|
||||
while (*it == '/' && it != path) { it--; }
|
||||
*(it + 1) = 0;
|
||||
if (it == path) return path;
|
||||
|
||||
// Return path from the first character if there are no more slashes, or from the first character after the last
|
||||
// slash.
|
||||
char* beg = strrchr(path, '/');
|
||||
if (!beg) return path;
|
||||
return beg + 1;
|
||||
}
|
||||
|
||||
char* dirname(char* path)
|
||||
{
|
||||
// If path is NULL, or the string's length is 0, return .
|
||||
if (!path) return dot;
|
||||
size_t len = strlen(path);
|
||||
if (!len) return dot;
|
||||
|
||||
// Strip trailing slashes.
|
||||
char* it = path + len - 1;
|
||||
while (*it == '/' && it != path) { it--; }
|
||||
*(char*)(it + 1) = 0;
|
||||
if (it == path) return path;
|
||||
|
||||
// Search for the last slash. If there is none, return .
|
||||
// Otherwise, we end the string there and return.
|
||||
char* end = strrchr(path, '/');
|
||||
if (!end) return dot;
|
||||
if (end != path) *end = 0;
|
||||
else
|
||||
*(end + 1) = 0;
|
||||
return path;
|
||||
}
|
||||
}
|
@ -7,17 +7,12 @@
|
||||
|
||||
extern "C"
|
||||
{
|
||||
pid_t gettid()
|
||||
{
|
||||
return syscall(SYS_gettid);
|
||||
}
|
||||
|
||||
unsigned int msleep(unsigned int ms)
|
||||
{
|
||||
return (unsigned int)syscall(SYS_sleep, ms);
|
||||
}
|
||||
|
||||
noreturn void __luna_abort(const char* message)
|
||||
__lc_noreturn void __luna_abort(const char* message)
|
||||
{
|
||||
fputs(message, stderr);
|
||||
abort();
|
||||
|
64
libs/libc/src/rand.cpp
Normal file
64
libs/libc/src/rand.cpp
Normal file
@ -0,0 +1,64 @@
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef uint32_t word_t;
|
||||
#define STATE_SIZE 624
|
||||
#define MIDDLE 397
|
||||
#define INIT_SHIFT 30
|
||||
#define INIT_FACT 1812433253
|
||||
#define TWIST_MASK 0x9908b0df
|
||||
#define SHIFT1 11
|
||||
#define MASK1 0xffffffff
|
||||
#define SHIFT2 7
|
||||
#define MASK2 0x9d2c5680
|
||||
#define SHIFT3 15
|
||||
#define MASK3 0xefc60000
|
||||
#define SHIFT4 18
|
||||
|
||||
#define LOWER_MASK 0x7fffffff
|
||||
#define UPPER_MASK (~(word_t)LOWER_MASK)
|
||||
|
||||
static word_t state[STATE_SIZE];
|
||||
static size_t index = STATE_SIZE + 1;
|
||||
|
||||
static void twist()
|
||||
{
|
||||
for (size_t i = 0; i < STATE_SIZE; i++)
|
||||
{
|
||||
word_t x = (state[i] & UPPER_MASK) | (state[(i + 1) % STATE_SIZE] & LOWER_MASK);
|
||||
x = (x >> 1) ^ (x & 1 ? TWIST_MASK : 0);
|
||||
state[i] = state[(i + MIDDLE) % STATE_SIZE] ^ x;
|
||||
}
|
||||
index = 0;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
{
|
||||
int rand()
|
||||
{
|
||||
if (index >= STATE_SIZE)
|
||||
{
|
||||
assert(index == STATE_SIZE && "Mersenne generator was never seeded");
|
||||
twist();
|
||||
}
|
||||
|
||||
word_t y = state[index];
|
||||
y ^= (y >> SHIFT1) & MASK1;
|
||||
y ^= (y << SHIFT2) & MASK2;
|
||||
y ^= (y << SHIFT3) & MASK3;
|
||||
y ^= y >> SHIFT4;
|
||||
|
||||
index++;
|
||||
return y;
|
||||
}
|
||||
|
||||
void srand(unsigned int seed)
|
||||
{
|
||||
index = STATE_SIZE;
|
||||
state[0] = seed;
|
||||
for (size_t i = 1; i < STATE_SIZE; i++)
|
||||
state[i] = (word_t)((INIT_FACT * (state[i - 1] ^ (state[i - 1] >> INIT_SHIFT))) + i);
|
||||
}
|
||||
}
|
11
libs/libc/src/sched.cpp
Normal file
11
libs/libc/src/sched.cpp
Normal file
@ -0,0 +1,11 @@
|
||||
#include <sched.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
int sched_yield()
|
||||
{
|
||||
return (int)syscall(SYS_yield);
|
||||
}
|
||||
}
|
36
libs/libc/src/setjmp.asm
Normal file
36
libs/libc/src/setjmp.asm
Normal file
@ -0,0 +1,36 @@
|
||||
global _setjmp
|
||||
global setjmp
|
||||
_setjmp:
|
||||
setjmp:
|
||||
mov rsi, 0
|
||||
mov [rdi], rbx
|
||||
mov [rdi+8], r12
|
||||
mov [rdi+16], r13
|
||||
mov [rdi+24], r14
|
||||
mov [rdi+32], r15
|
||||
mov [rdi+40], rbp
|
||||
mov [rdi+48], rsp
|
||||
mov rax, [rsp]
|
||||
mov [rdi+56], rax
|
||||
xor rax, rax
|
||||
ret
|
||||
|
||||
global _longjmp
|
||||
global longjmp
|
||||
_longjmp:
|
||||
longjmp:
|
||||
mov rax, rsi
|
||||
cmp rax, 0
|
||||
jne .nonzero
|
||||
mov rax, 1
|
||||
.nonzero:
|
||||
mov rbx, [rdi]
|
||||
mov r12, [rdi+8]
|
||||
mov r13, [rdi+16]
|
||||
mov r14, [rdi+24]
|
||||
mov r15, [rdi+32]
|
||||
mov rbp, [rdi+40]
|
||||
mov rsp, [rdi+48]
|
||||
mov rcx, [rdi+56]
|
||||
mov [rsp], rcx
|
||||
ret
|
15
libs/libc/src/setjmp.cpp
Normal file
15
libs/libc/src/setjmp.cpp
Normal file
@ -0,0 +1,15 @@
|
||||
#include <luna.h>
|
||||
#include <setjmp.h>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
int sigsetjmp(sigjmp_buf env, int)
|
||||
{
|
||||
return setjmp(env);
|
||||
}
|
||||
|
||||
__lc_noreturn void siglongjmp(sigjmp_buf env, int val)
|
||||
{
|
||||
longjmp(env, val);
|
||||
}
|
||||
}
|
@ -1,22 +1,59 @@
|
||||
#include <ctype.h>
|
||||
#include <luna.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
|
||||
extern "C"
|
||||
template <typename T> T string_to_integer_type(const char* str)
|
||||
{
|
||||
noreturn void abort()
|
||||
bool neg = false;
|
||||
T val = 0;
|
||||
|
||||
switch (*str)
|
||||
{
|
||||
_exit(-1);
|
||||
case '-':
|
||||
neg = true;
|
||||
str++;
|
||||
break;
|
||||
case '+': str++; break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
int atoi(const char*)
|
||||
while (isdigit(*str)) { val = (10 * val) + (*str++ - '0'); }
|
||||
|
||||
return (neg ? -val : val);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
{
|
||||
__lc_noreturn void abort()
|
||||
{
|
||||
NOT_IMPLEMENTED("atoi");
|
||||
_Exit(-1);
|
||||
}
|
||||
|
||||
int atoi(const char* str)
|
||||
{
|
||||
return string_to_integer_type<int>(str);
|
||||
}
|
||||
|
||||
long atol(const char* str)
|
||||
{
|
||||
return string_to_integer_type<long>(str);
|
||||
}
|
||||
|
||||
long long atoll(const char* str)
|
||||
{
|
||||
return string_to_integer_type<long long>(str);
|
||||
}
|
||||
|
||||
char* getenv(const char*)
|
||||
{
|
||||
NOT_IMPLEMENTED("getenv");
|
||||
}
|
||||
|
||||
__lc_noreturn void _Exit(int status)
|
||||
{
|
||||
syscall(SYS_exit, status);
|
||||
__lc_unreachable();
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
#include <errno.h>
|
||||
#include <luna.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
extern "C"
|
||||
@ -16,6 +16,44 @@ extern "C"
|
||||
return buf;
|
||||
}
|
||||
|
||||
void* memchr(const void* buf, int c, size_t n)
|
||||
{
|
||||
const char* s = (const char*)buf;
|
||||
for (; n && *s != (char)c; s++, n--)
|
||||
;
|
||||
if (n) return (void*)(const_cast<char*>(s));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int memcmp(const void* a, const void* b, size_t n)
|
||||
{
|
||||
const char* _a = (const char*)a;
|
||||
const char* _b = (const char*)b;
|
||||
for (; n && _a == _b; n--, _a++, _b++)
|
||||
;
|
||||
if (!n) return 0;
|
||||
if (*_a > *_b) return 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
void* memmove(void* dest, const void* src, size_t n)
|
||||
{
|
||||
if (dest == src) return dest;
|
||||
if (dest > src)
|
||||
for (long i = n - 1; i >= 0; i++) { *((char*)dest + i) = *((const char*)src + i); }
|
||||
else
|
||||
for (long i = 0; i < (long)n; i++) { *((char*)dest + i) = *((const char*)src + i); }
|
||||
return dest;
|
||||
}
|
||||
|
||||
char* strdup(const char* str)
|
||||
{
|
||||
size_t len = strlen(str);
|
||||
char* dest = (char*)malloc(len + 1);
|
||||
if (!dest) return dest;
|
||||
return (char*)memcpy(dest, str, len + 1);
|
||||
}
|
||||
|
||||
size_t strlen(const char* str)
|
||||
{
|
||||
const char* i = str;
|
||||
@ -24,17 +62,61 @@ extern "C"
|
||||
return (i - str);
|
||||
}
|
||||
|
||||
size_t strnlen(const char* str, size_t max)
|
||||
{
|
||||
char* p = (char*)memchr(str, 0, max);
|
||||
return p ? p - str : max;
|
||||
}
|
||||
|
||||
char* strcpy(char* dest, const char* src)
|
||||
{
|
||||
memcpy(dest, src, strlen(src) + 1);
|
||||
return (char*)memcpy(dest, src, strlen(src) + 1);
|
||||
}
|
||||
|
||||
char* strncpy(char* dest, const char* src, size_t max)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < max && src[i] != 0; i++) dest[i] = src[i];
|
||||
for (; i < max; i++) dest[i] = 0;
|
||||
return dest;
|
||||
}
|
||||
|
||||
char* strncpy(char* dest, const char* src, size_t max) // FIXME: Implement strncpy according to the specification.
|
||||
size_t strlcpy(char* dest, const char* src, size_t size)
|
||||
{
|
||||
size_t src_len = strlen(src) + 1; // NULL byte
|
||||
memcpy(dest, src, src_len > max ? max : src_len);
|
||||
return dest;
|
||||
size_t len = strlen(src);
|
||||
if (size == 0) return len;
|
||||
if (len < (size - 1))
|
||||
{
|
||||
memcpy(dest, src, len);
|
||||
dest[len] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(dest, src, size - 1);
|
||||
dest[size - 1] = 0;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
int strcmp(const char* a, const char* b)
|
||||
{
|
||||
while (*a && (*a == *b))
|
||||
{
|
||||
a++;
|
||||
b++;
|
||||
}
|
||||
return *(const unsigned char*)a - *(const unsigned char*)b;
|
||||
}
|
||||
|
||||
int strncmp(const char* a, const char* b, size_t max)
|
||||
{
|
||||
const char* base = a;
|
||||
while (*a && (*a == *b) && (size_t)(a - base) < (max - 1))
|
||||
{
|
||||
a++;
|
||||
b++;
|
||||
}
|
||||
return *(const unsigned char*)a - *(const unsigned char*)b;
|
||||
}
|
||||
|
||||
char* strcat(char* dest, const char* src)
|
||||
@ -68,48 +150,38 @@ extern "C"
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void* memclr(void* buf, size_t n)
|
||||
char* strrchr(const char* str, int c)
|
||||
{
|
||||
// "i" is our counter of how many bytes we've cleared
|
||||
size_t i;
|
||||
|
||||
// find out if "m_start" is aligned on a SSE_XMM_SIZE boundary
|
||||
if ((size_t)buf & (15))
|
||||
{
|
||||
i = 0;
|
||||
|
||||
// we need to clear byte-by-byte until "m_start" is aligned on an SSE_XMM_SIZE boundary
|
||||
// ... and lets make sure we don't copy 'too' many bytes (i < m_count)
|
||||
while (((size_t)buf + i) & (15) && i < n)
|
||||
{
|
||||
asm("stosb;" ::"D"((size_t)buf + i), "a"(0));
|
||||
i++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// if "m_start" was aligned, set our count to 0
|
||||
i = 0;
|
||||
const char* s = str + strlen(str);
|
||||
while (s != str && *s != (char)c) s--;
|
||||
if (s != str) return const_cast<char*>(s);
|
||||
if (*s == (char)c) return const_cast<char*>(s);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// clear 64-byte chunks of memory (4 16-byte operations)
|
||||
for (; i + 64 <= n; i += 64)
|
||||
void* bzero(void* buf, size_t n)
|
||||
{
|
||||
asm volatile(" pxor %%xmm0, %%xmm0; " // set XMM0 to 0
|
||||
" movdqa %%xmm0, 0(%0); " // move 16 bytes from XMM0 to %0 + 0
|
||||
" movdqa %%xmm0, 16(%0); "
|
||||
" movdqa %%xmm0, 32(%0); "
|
||||
" movdqa %%xmm0, 48(%0); " ::"r"((size_t)buf + i));
|
||||
return memset(buf, 0, n);
|
||||
}
|
||||
|
||||
// copy the remaining bytes (if any)
|
||||
asm(" rep stosb; " ::"a"((size_t)(0)), "D"(((size_t)buf) + i), "c"(n - i));
|
||||
|
||||
// "i" will contain the total amount of bytes that were actually transfered
|
||||
i += n - i;
|
||||
|
||||
// we return "m_start" + the amount of bytes that were transfered
|
||||
return (void*)(((size_t)buf) + i);
|
||||
char* strstr(const char* haystack, const char* needle)
|
||||
{
|
||||
size_t needle_size = strlen(needle);
|
||||
size_t haystack_size = strlen(haystack);
|
||||
while (*haystack)
|
||||
{
|
||||
if (*haystack == *needle)
|
||||
{
|
||||
if (needle_size <= haystack_size)
|
||||
{
|
||||
if (!strncmp(haystack, needle, needle_size)) return const_cast<char*>(haystack);
|
||||
}
|
||||
else { return NULL; }
|
||||
}
|
||||
haystack++;
|
||||
haystack_size--;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#pragma GCC push_options
|
||||
@ -129,8 +201,13 @@ extern "C"
|
||||
case EISDIR: return "Is a directory";
|
||||
case ENOEXEC: return "Exec format error";
|
||||
case EFAULT: return "Bad address";
|
||||
case EEXIST: return "File exists";
|
||||
case ENOTDIR: return "Not a directory";
|
||||
case ENOSPC: return "No space left on device";
|
||||
case ENOTSUP: return "Operation not supported";
|
||||
case EPIPE: return "Broken pipe";
|
||||
case 0: return "Success";
|
||||
default: return (char*)(unsigned long int)err;
|
||||
default: return "Unknown error";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,4 +14,9 @@ extern "C"
|
||||
{
|
||||
return (int)syscall(SYS_munmap, addr, len);
|
||||
}
|
||||
|
||||
int mprotect(void* addr, size_t size, int prot)
|
||||
{
|
||||
return (int)syscall(SYS_mprotect, addr, size, prot);
|
||||
}
|
||||
}
|
11
libs/libc/src/sys/stat.cpp
Normal file
11
libs/libc/src/sys/stat.cpp
Normal file
@ -0,0 +1,11 @@
|
||||
#include <sys/stat.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
int mkdir(const char* pathname, mode_t)
|
||||
{
|
||||
return (int)syscall(SYS_mkdir, pathname);
|
||||
}
|
||||
}
|
11
libs/libc/src/time.cpp
Normal file
11
libs/libc/src/time.cpp
Normal file
@ -0,0 +1,11 @@
|
||||
#include <sys/syscall.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
clock_t clock()
|
||||
{
|
||||
return syscall(SYS_clock);
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
#include <bits/error.h>
|
||||
#include <fcntl.h>
|
||||
#include <luna.h>
|
||||
#include <luna/syscall.h>
|
||||
#include <stdarg.h>
|
||||
@ -18,9 +19,15 @@ extern "C"
|
||||
{
|
||||
NOT_IMPLEMENTED("execvp");
|
||||
}
|
||||
|
||||
pid_t fork(void)
|
||||
{
|
||||
NOT_IMPLEMENTED("fork");
|
||||
return syscall(SYS_fork);
|
||||
}
|
||||
|
||||
pid_t getpid(void)
|
||||
{
|
||||
return syscall(SYS_getpid);
|
||||
}
|
||||
|
||||
long syscall(long number, ...)
|
||||
@ -31,12 +38,14 @@ extern "C"
|
||||
va_start(ap, number);
|
||||
switch (number)
|
||||
{
|
||||
case SYS_clock:
|
||||
case SYS_yield:
|
||||
case SYS_gettid:
|
||||
case SYS_rand: result = __luna_syscall0(number); break;
|
||||
case SYS_fork:
|
||||
case SYS_getpid: result = __luna_syscall0(number); break;
|
||||
case SYS_exit:
|
||||
case SYS_close:
|
||||
case SYS_exec:
|
||||
case SYS_mkdir:
|
||||
case SYS_sleep: result = __luna_syscall1(number, va_arg(ap, arg)); break;
|
||||
case SYS_munmap:
|
||||
case SYS_open: {
|
||||
@ -45,9 +54,11 @@ extern "C"
|
||||
result = __luna_syscall2(number, arg0, arg1);
|
||||
break;
|
||||
}
|
||||
case SYS_fcntl:
|
||||
case SYS_seek:
|
||||
case SYS_write:
|
||||
case SYS_read:
|
||||
case SYS_mprotect:
|
||||
case SYS_mmap: {
|
||||
arg arg0 = va_arg(ap, arg);
|
||||
arg arg1 = va_arg(ap, arg);
|
||||
@ -96,9 +107,14 @@ extern "C"
|
||||
return syscall(SYS_seek, fd, offset, whence);
|
||||
}
|
||||
|
||||
noreturn void _exit(int status)
|
||||
__lc_noreturn void _exit(int status)
|
||||
{
|
||||
syscall(SYS_exit, status);
|
||||
__builtin_unreachable();
|
||||
__lc_unreachable();
|
||||
}
|
||||
|
||||
int dup(int fd)
|
||||
{
|
||||
return fcntl(fd, F_DUPFD, 0);
|
||||
}
|
||||
}
|
9
tests/Makefile
Normal file
9
tests/Makefile
Normal file
@ -0,0 +1,9 @@
|
||||
build:
|
||||
make -C libc build
|
||||
|
||||
install:
|
||||
make -C libc install
|
||||
|
||||
test:
|
||||
make -C libc test
|
||||
|
39
tests/Test.h
Normal file
39
tests/Test.h
Normal file
@ -0,0 +1,39 @@
|
||||
#ifndef __TEST_H_
|
||||
#define __TEST_H_
|
||||
#include <stdbool.h>
|
||||
|
||||
int printf(const char*, ...);
|
||||
|
||||
#define DEFINE_TEST(name) bool test_##name()
|
||||
#define START_TEST(name) printf("testing whether %s works... ", #name)
|
||||
#define START_TEST_CASE(name) printf("testing %s...\n", #name)
|
||||
|
||||
#define TEST_SUCCESS() \
|
||||
do { \
|
||||
printf("yes!\n"); \
|
||||
return true; \
|
||||
} while (0)
|
||||
|
||||
#define TEST_FAIL(expr) \
|
||||
do { \
|
||||
printf("no (%s)\n", #expr); \
|
||||
return false; \
|
||||
} while (0)
|
||||
|
||||
#define EXPECT(expr) \
|
||||
do { \
|
||||
if (!(expr)) { TEST_FAIL(expr); } \
|
||||
} while (0)
|
||||
|
||||
#define EXPECT_EQ(a, b) EXPECT((a) == (b))
|
||||
|
||||
#define EXPECT_NOT_EQ(a, b) EXPECT((a) != (b))
|
||||
|
||||
#define EXPECT_NOT_CRASHED() TEST_SUCCESS()
|
||||
|
||||
#define RUN_TEST(name) \
|
||||
do { \
|
||||
if (!test_##name()) return 1; \
|
||||
} while (0)
|
||||
|
||||
#endif
|
15
tests/libc/Makefile
Normal file
15
tests/libc/Makefile
Normal file
@ -0,0 +1,15 @@
|
||||
TESTDIR := $(LUNA_ROOT)/tests/libc
|
||||
DESTDIR := $(LUNA_ROOT)/initrd/bin
|
||||
|
||||
build:
|
||||
@mkdir -p $(TESTDIR)/bin
|
||||
$(LUNA_ROOT)/tools/sync-libc.sh
|
||||
$(CC) $(TESTDIR)/string.c $(TESTDIR)/Test.c -I$(LUNA_ROOT)/tests -o $(TESTDIR)/bin/test-libc -Wall -Wextra -Wno-stringop-overread -Werror
|
||||
|
||||
install:
|
||||
$(LUNA_ROOT)/tools/clean.sh
|
||||
@mkdir -p $(DESTDIR)
|
||||
cp $(TESTDIR)/bin/test-libc $(DESTDIR)/test-libc
|
||||
|
||||
test:
|
||||
CFLAGS="-DRUN_TEST_AS_INIT=/bin/test-libc" $(LUNA_ROOT)/tools/run.sh
|
11
tests/libc/Test.c
Normal file
11
tests/libc/Test.c
Normal file
@ -0,0 +1,11 @@
|
||||
#include "Test.h"
|
||||
|
||||
DEFINE_TEST(strlen);
|
||||
DEFINE_TEST(strnlen);
|
||||
|
||||
int main()
|
||||
{
|
||||
START_TEST_CASE(string.h);
|
||||
RUN_TEST(strlen);
|
||||
RUN_TEST(strnlen);
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user