Compare commits

...

86 Commits

Author SHA1 Message Date
3c1146f2c5 libc: Implement setjmp() and longjmp() 2022-10-17 21:22:18 +02:00
511ad67a9a fdopen: Check for malloc errors 2022-10-17 20:54:32 +02:00
e17a21dbad libc: Use fdopen() after calling open() in fopen() 2022-10-17 20:54:09 +02:00
494b48bbe3 init: Adjust init 2022-10-17 20:53:09 +02:00
4f41b9ed37 Scheduler: Implement a find_by_pid function 2022-10-17 20:40:38 +02:00
8b17065718 Kernel, libc: Rename gettid() to getpid() and move it to unistd.h 2022-10-17 20:08:44 +02:00
87ef210759 Kernel, libc: Remove spawn()
Now, fork() and exec() are both implemented. More POSIX-y, thus spawn can be removed.
2022-10-17 19:55:01 +02:00
55808d5cc4 Update README.md with new features 2022-10-17 19:39:34 +02:00
1e96a45f33 Change 'tid' to 'PID' in init.c
I think tasks can be considered programs now. So, gettid() is equal to getpid().

In fact, it should be renamed. And moved to unistd.h. Soon.
2022-10-17 19:39:26 +02:00
a2c05de604 mmap(), mprotect(), munmap(): Check more stuff 2022-10-17 19:32:24 +02:00
c2ecc4fe95 Update README.md 2022-10-17 19:32:02 +02:00
593daba651 Kernel: Remove unused headers in main.cpp 2022-10-17 19:23:53 +02:00
ea8a42b8c0 Kernel: Add a name field to the Task structure 2022-10-17 19:12:47 +02:00
e7522c21ca libc: Document fork() 2022-10-17 18:56:04 +02:00
34fc6996b0 UserHeap: allocate needed memory in request_virtual_pages 2022-10-17 18:52:11 +02:00
92634048fc UserHeap: some nice improvements 2022-10-17 18:49:19 +02:00
64f5078494 Kernel, libc: Implement fork()
This time for real.

Also, add a new per-user-task virtual address allocator (UserHeap), so that mmap'ed pages are in user range and can be copied.
2022-10-17 18:43:35 +02:00
966fdc76d7 Move userspace task to userspace memory 2022-10-17 17:30:05 +02:00
b334e1cd50 VMM: check if the entry is already present 2022-10-17 17:26:16 +02:00
682be58d97 AddressSpace: copy instead of linking 2022-10-17 17:24:33 +02:00
3bf4f854c7 init: return 0 at the end of main 2022-10-17 17:19:31 +02:00
8b70635e79 Do not strip apps 2022-10-17 17:19:23 +02:00
250db2c90f Scheduler: add an append_task() function 2022-10-17 17:14:22 +02:00
c2fa4f380d Kernel: Use the new Task member functions 2022-10-17 17:07:25 +02:00
ce10fb5743 fcntl(F_DUPFD): Allocate any file descriptor greater than or equal to arg 2022-10-17 17:01:22 +02:00
891651f2d6 Task: Move functions operating on Task to member functions
Also, add an alloc_fd_greater_than_or_equal() function, for use in fcntl(F_DUPFD)
2022-10-17 17:00:07 +02:00
e34045a78c Kernel: on stack smashing detection, do not trigger a normal panic
That triggers stack traces, which we do not want in a stack smash fail situation
2022-10-16 19:04:24 +02:00
9b39d618de Kernel, libc: Implement spawn()
This function is a Luna alternative to fork() and exec().

Why? Simply because I can't figure out for the life of me how to implement a working fork().

So meanwhile, we have spawn() as a replacement. exec() still exists, though.
2022-10-16 18:48:35 +02:00
a1146a5ce2 Panic: show panic message on screen 2022-10-16 18:23:33 +02:00
6d821d2638 libc: Add support for mkdir() 2022-10-16 17:37:29 +02:00
f35bbe52b7 fnctl: Do not switch on cmd 2022-10-16 17:27:15 +02:00
e1e86ab889 libc: Add support for EEXIST, ENOTDIR, ENOSPC, ENOTSUP and EPIPE to strerror() 2022-10-16 17:24:58 +02:00
d2e2883a79 Kernel: Make mkdir() accessible to userspace 2022-10-16 17:22:12 +02:00
18fbccafb7 VFS: add an exists() function 2022-10-16 16:58:18 +02:00
0a46feb162 Add strnlen test 2022-10-16 16:00:14 +02:00
d62eb6c791 Tests: Add test framework 2022-10-16 15:31:58 +02:00
f8154ce230 Kernel: Implement mkdir() from a single path
This is done using dirname() and basename() :)
2022-10-16 14:45:25 +02:00
8c0a57f0c2 Kernel: Copy strrchr, dirname and basename over from libc 2022-10-16 14:36:25 +02:00
1624f0360d exec: More logging 2022-10-15 17:47:57 +02:00
d30010d524 apps/sym: Read from the end of the file 2022-10-15 17:45:53 +02:00
68403dc029 Kernel: Make AddressSpaces reference-counted 2022-10-15 17:40:33 +02:00
eca7227fda StackTracer: Stop when instruction is at nullptr 2022-10-15 17:31:57 +02:00
aca1367158 Kernel: Switch to strlcpy() as well
Surprisingly, most uses of strncpy() are in places where strncpy() is actually a better choice.
For example, copying to a fixed-length char array in a structure.
2022-10-15 17:30:34 +02:00
42b6b927c9 libc: Implement strlcpy(), and make strncpy() standard-compliant 2022-10-15 17:24:22 +02:00
48f38bdcef libc: More inttypes.h 2022-10-15 16:53:26 +02:00
45afd3e243 Replace the rand() which reads from /dev/random with a Mersenne PRNG.
Much better now.

Also, it is seeded by reading from /dev/random at startup. Not sure if that's the best idea, but we'll see.
2022-10-15 16:46:54 +02:00
18f1f8b7ca Add VERY HACKY rand() implementation. 2022-10-15 16:31:08 +02:00
dd358eca29 libc/libgen.cpp: Remove #include <luna.h>
This header was included for the NOT_IMPLEMENTED macro.

Now it is implemented.
2022-10-15 16:09:54 +02:00
116e7326a4 libc: Implement dirname() and basename() 2022-10-15 16:08:27 +02:00
5256166e7a libc: Fix strrchr 2022-10-15 16:06:41 +02:00
6953a28ce8 libc: Implement strrchr() 2022-10-15 15:16:19 +02:00
f13c48b562 libc: make strerror() return "Unknown error" instead of an invalid pointer 2022-10-15 15:14:30 +02:00
7139b4403f libc: Add EPIPE (not returned by anything yet) 2022-10-15 15:14:02 +02:00
e3e33bacbc libc: Add stubbed signal.h header 2022-10-15 15:13:38 +02:00
8d552b1522 libc: Add setjmp.h stub functions (not implemented) 2022-10-15 15:13:25 +02:00
46f60b192a libc: Add libgen.h stub functions (not implemented) 2022-10-15 15:13:10 +02:00
9aa96de61d libc: Start implementing inttypes.h 2022-10-15 15:12:53 +02:00
6507146c60 libc: Add Luna-specific limits 2022-10-15 14:31:55 +02:00
4bad782aad Kernel: Increment the maximum number of file descriptors a task can have
Doesn't use up more space in the Task structure, and now we are above the Minimum Acceptable Value as defined by POSIX (20), I think.
2022-10-15 14:20:29 +02:00
f50017912d libc: Implement atoi(), atol() and atoll() 2022-10-15 14:02:24 +02:00
2395c7a871 libc: Implement _Exit
Exactly the same as _exit, but in stdlib.h instead of unistd.h
2022-10-15 14:02:10 +02:00
b1739f7f0d libc: Add support for the new clock() system call 2022-10-15 13:21:22 +02:00
62a2bcf2ff Kernel: Add a clock() system call 2022-10-15 13:17:26 +02:00
3a9dddaa57 Kernel, libc: Remove the rand() system call
That's why we now have a VFS and a /dev pseudo-filesystem. To provide that kind of things.

Remember, everything is a file!!

The new way to ask the kernel for random numbers is to read from /dev/random.
2022-10-15 13:04:48 +02:00
225284a6ca libc: Add support for mprotect() 2022-10-15 12:59:13 +02:00
613f8170b6 Kernel, libc: Implement mprotect() and use proper PROT_* values 2022-10-15 12:57:14 +02:00
62d631f1b4 Kernel: Rename assert.h to kassert.h so IDEs pickup the kernel header instead of the userspace one 2022-10-15 12:56:48 +02:00
a002e75725 libc: Add strstr() 2022-10-15 12:33:36 +02:00
3fde7e46f5 libc: Add strcmp() and strncmp() 2022-10-15 12:30:49 +02:00
3e2a4276e9 libc: Add memcmp() and memmove()
Those were there since forever in the kernel, but they haven't been added to libc until now.
2022-10-15 12:23:37 +02:00
94a6336e4d libc: add memchr(), strnlen(), strdup(), and rename memclr() to bzero() 2022-10-15 12:18:37 +02:00
523e88e5a9 libc: Add sched_yield() 2022-10-15 11:43:13 +02:00
b0e071e964 libc: Make functions with no arguments be defined as type function(void)
Instead of type function().
2022-10-15 11:39:13 +02:00
3e2bebf0aa libc: Alias __lc_unreachable to __builtin_unreachable 2022-10-15 11:20:20 +02:00
1e86acd4c0 libc: Implement fcntl(F_DUPFD) and dup() 2022-10-15 11:16:34 +02:00
36bb1cab5c FileDescriptor: add operator=() so that the fcntl(F_DUPFD syscall compiles :) 2022-10-15 11:16:18 +02:00
5c61252061 Kernel: Add a new fcntl() system call 2022-10-15 10:56:06 +02:00
3eb1bff2e9 Task: add an alloc_fd() function 2022-10-15 10:45:12 +02:00
c77e752a82 libc: Implement fileno() 2022-10-15 10:28:52 +02:00
d0d6557e99 libc: Add assert() 2022-10-15 10:05:48 +02:00
8398b2e2e4 libc: Change bits/macros.h to use a __lc_ prefix for internal macros
That way, we don't pollute user programs with our own 'noreturn' and 'deprecated' macros
2022-10-15 09:52:37 +02:00
1043b0772d Make libc exit with a specific code if program initialization fails
Now you know, if a program exits with code -127 it's libc's fault :)
2022-10-14 21:26:46 +02:00
d5bc87099f libc: #define STDOUT_FILENO and STDERR_FILENO
Still missing stdin >.<
2022-10-14 21:24:18 +02:00
eb67ab113e libc: Add ctype.h 2022-10-14 21:12:26 +02:00
91d76a2ee4 Devices: Add a new RandomDevice :)
This new device uses the seeded Mersenne PRNG we use in the kernel.
This device is not meant for regular userspace use, but more for userspace to seed their own PRNGs from.

If the DeviceFS is mounted at /dev, this device can be found at /dev/random.
2022-10-14 20:14:49 +02:00
faaf930a14 Moon 0.12-dev 2022-10-14 19:55:29 +02:00
104 changed files with 2222 additions and 483 deletions

1
.gitignore vendored
View File

@ -7,6 +7,7 @@ kernel/bin/moon.elf
initrd/sys/moon.sym initrd/sys/moon.sym
initrd/bin/** initrd/bin/**
apps/bin/** apps/bin/**
tests/**/bin/**
base/usr/include/** base/usr/include/**
base/usr/lib/** base/usr/lib/**
**/*.a **/*.a

View File

@ -6,11 +6,12 @@ Not so much at the moment.
- x86_64-compatible [kernel](kernel/). - 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). - 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. - 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. - Can [load userspace ELF programs](kernel/src/sys/elf/) from the file system as user tasks.
- [System call](kernel/src/sys/) interface and bare-bones [C Library](libs/libc/). - [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 the kernel then loads from the initial ramdisk. - 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 ## 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)). 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)).

View File

@ -11,7 +11,6 @@ CFLAGS := -Wall -Wextra -Werror -Os
$(APPS_BIN)/%: $(APPS_SRC)/%.c $(APPS_BIN)/%: $(APPS_SRC)/%.c
@mkdir -p $(@D) @mkdir -p $(@D)
$(CC) $(CFLAGS) -o $@ $^ $(CC) $(CFLAGS) -o $@ $^
$(STRIP) $@
build: $(REAL_APPS) build: $(REAL_APPS)

View File

@ -1,26 +1,24 @@
#include <errno.h>
#include <luna.h> #include <luna.h>
#include <setjmp.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/syscall.h> #include <sys/stat.h>
#include <unistd.h> #include <unistd.h>
typedef long ssize_t;
int print_version() int print_version()
{ {
char version[4096]; char version[4096];
FILE* verfile = fopen("/dev/version", "r"); FILE* fp = fopen("/dev/version", "r");
if (!verfile) if (!fp)
{ {
perror("fopen"); perror("fopen");
return 1; return 1;
} }
size_t nread = fread(version, 4096, 1, verfile); size_t nread = fread(version, 4096, 1, fp);
if (ferror(verfile)) if (ferror(fp))
{ {
perror("fread"); perror("fread");
return 1; return 1;
@ -28,7 +26,7 @@ int print_version()
version[nread] = 0; version[nread] = 0;
if (fclose(verfile) < 0) if (fclose(fp) < 0)
{ {
perror("fclose"); perror("fclose");
return 1; return 1;
@ -41,9 +39,9 @@ int print_version()
int main() 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; return 1;
} }
@ -66,7 +64,7 @@ int main()
printf("Welcome to Luna!\n"); printf("Welcome to Luna!\n");
printf("Running as tid %ld\n\n", gettid()); printf("Running as PID %ld\n\n", getpid());
sleep(1); sleep(1);
@ -117,9 +115,69 @@ int main()
if (fclose(config) < 0) { perror("fclose"); } if (fclose(config) < 0) { perror("fclose"); }
printf("\n\nGot random number %d\n\n", rand());
sleep(2); sleep(2);
printf("\n\nPress any key to restart.\n\n"); printf("Press any key to restart.\n\n");
return 0; 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);
} }

View File

@ -4,7 +4,7 @@
int main() int main()
{ {
sleep(6); sleep(2);
FILE* syms = fopen("/sys/moon.sym", "r"); FILE* syms = fopen("/sys/moon.sym", "r");
if (!syms) if (!syms)
@ -13,15 +13,15 @@ int main()
return 1; return 1;
} }
char buf[1025]; char buf[1200];
if (fseek(syms, 8000, SEEK_SET) < 0) if (fseek(syms, -1199, SEEK_END) < 0)
{ {
perror("fseek"); perror("fseek");
return 1; return 1;
} }
size_t nread = fread(buf, 1024, 1, syms); size_t nread = fread(buf, 1199, 1, syms);
if (ferror(syms)) if (ferror(syms))
{ {
perror("fread"); perror("fread");

View File

@ -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

View File

@ -6,7 +6,11 @@
#define EBADF 9 #define EBADF 9
#define ENOMEM 12 #define ENOMEM 12
#define EFAULT 14 #define EFAULT 14
#define EEXIST 17
#define ENOTDIR 20
#define EISDIR 21 #define EISDIR 21
#define EINVAL 22 #define EINVAL 22
#define EMFILE 24 #define EMFILE 24
#define ENOSYS 38 #define ENOSPC 28
#define ENOSYS 38
#define ENOTSUP 95

View File

@ -43,6 +43,8 @@ struct Descriptor
Descriptor(const Descriptor& other); Descriptor(const Descriptor& other);
Descriptor(); Descriptor();
const Descriptor& operator=(const Descriptor& other);
private: private:
bool m_is_open; bool m_is_open;
bool m_can_read; bool m_can_read;

View File

@ -35,12 +35,15 @@ namespace VFS
ssize_t read(Node* node, size_t offset, size_t length, char* buffer); 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); 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); void mount_root(Node* root);
Node* resolve_path(const char* filename, Node* root = nullptr); Node* resolve_path(const char* filename, Node* root = nullptr);
bool exists(const char* pathname);
void mount(Node* mountpoint, Node* mounted); void mount(Node* mountpoint, Node* mounted);
void mount(const char* pathname, Node* mounted); void mount(const char* pathname, Node* mounted);

View 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
View 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)

View File

@ -22,6 +22,7 @@ namespace KernelLog
void logln(const char* function, LogLevel level, const char* message, ...) PRINTF_LIKE(3, 4); void logln(const char* function, LogLevel level, const char* message, ...) PRINTF_LIKE(3, 4);
void toggle_log_level(LogLevel level); void toggle_log_level(LogLevel level);
void toggle_log_backend(Backend backend); void toggle_log_backend(Backend backend);
void enable_log_backend(Backend backend);
} }
#ifndef MODULE #ifndef MODULE

View File

@ -7,7 +7,7 @@ struct AddressSpace
void destroy(); void destroy();
void detach(); void clear();
AddressSpace clone(); AddressSpace clone();
@ -16,12 +16,6 @@ struct AddressSpace
return m_pml4; return m_pml4;
} }
bool is_cloned()
{
return m_cloned;
}
private: private:
PageTable* m_pml4; PageTable* m_pml4;
bool m_cloned;
}; };

View 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);
};

View File

@ -5,13 +5,11 @@
extern "C" extern "C"
{ {
#endif #endif
[[noreturn]] void __do_int_panic(Context* context, const char* file, int line, const char* message); [[noreturn]] bool __do_int_panic(Context* context, const char* file, int line, const char* message);
[[noreturn]] void __do_panic(Context* context, const char* message); [[noreturn]] bool __do_panic(const char* file, int line, const char* message);
[[noreturn]] void __panic(const char* message);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #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) #define int_panic(context, message) __do_int_panic(context, __FILE__, __LINE__, message)

View File

@ -0,0 +1,4 @@
#pragma once
char* basename(char* path);
char* dirname(char* path);

View File

@ -8,6 +8,7 @@ __attribute__((deprecated)) int strcmp(const char* a, const char* b);
__attribute__((deprecated)) char* strcat(char* dest, const char* src); __attribute__((deprecated)) char* strcat(char* dest, const char* src);
char* strncpy(char* dest, const char* src, size_t n); 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); int strncmp(const char* a, const char* b, size_t n);
char* strncat(char* dest, const char* src, size_t n); char* strncat(char* dest, const char* src, size_t n);
@ -18,4 +19,5 @@ void* memset(void* dest, int c, size_t n);
int memcmp(const void* a, const void* b, size_t n); int memcmp(const void* a, const void* b, size_t n);
void* memmove(void* dest, void* src, size_t n); void* memmove(void* dest, void* src, size_t n);
char* strdup(const char* src); char* strdup(const char* src);
char* strrchr(const char* str, int c);

View File

@ -7,15 +7,19 @@
#define SYS_sleep 2 #define SYS_sleep 2
#define SYS_write 3 #define SYS_write 3
#define SYS_paint 4 #define SYS_paint 4
#define SYS_rand 5 #define SYS_getpid 5
#define SYS_gettid 6 #define SYS_mmap 6
#define SYS_mmap 7 #define SYS_munmap 7
#define SYS_munmap 8 #define SYS_open 8
#define SYS_open 9 #define SYS_read 9
#define SYS_read 10 #define SYS_close 10
#define SYS_close 11 #define SYS_seek 11
#define SYS_seek 12 #define SYS_exec 12
#define SYS_exec 13 #define SYS_fcntl 13
#define SYS_mprotect 14
#define SYS_clock 15
#define SYS_mkdir 16
#define SYS_fork 17
namespace Syscall namespace Syscall
{ {
@ -29,12 +33,16 @@ void sys_yield(Context* context);
void sys_sleep(Context* context, uint64_t ms); void sys_sleep(Context* context, uint64_t ms);
void sys_write(Context* context, int fd, size_t size, const char* addr); 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_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_getpid(Context* context);
void sys_gettid(Context* context); void sys_mmap(Context* context, void* address, size_t size, int prot);
void sys_mmap(Context* context, void* address, size_t size, int flags);
void sys_munmap(Context* context, void* address, size_t size); void sys_munmap(Context* context, void* address, size_t size);
void sys_open(Context* context, const char* filename, int flags); void sys_open(Context* context, const char* filename, int flags);
void sys_read(Context* context, int fd, size_t size, char* buffer); void sys_read(Context* context, int fd, size_t size, char* buffer);
void sys_close(Context* context, int fd); void sys_close(Context* context, int fd);
void sys_seek(Context* context, int fd, long offset, int whence); void sys_seek(Context* context, int fd, long offset, int whence);
void sys_exec(Context* context, const char* pathname); 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);

View File

@ -9,11 +9,11 @@ namespace Scheduler
void yield(); void yield();
void exit(int status); void exit(int status);
void sleep(unsigned long ms); 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(); 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_exit(Context* context, int64_t status);
void task_misbehave(Context* context, int64_t status); void task_misbehave(Context* context, int64_t status);
@ -27,4 +27,8 @@ namespace Scheduler
void reap_tasks(); void reap_tasks();
void reset_task(Task* task, ELFImage* new_image); void reset_task(Task* task, ELFImage* new_image);
void append_task(Task* task);
Task* find_by_pid(uint64_t pid);
} }

View File

@ -2,9 +2,10 @@
#include "fs/FileDescriptor.h" #include "fs/FileDescriptor.h"
#include "interrupts/Context.h" #include "interrupts/Context.h"
#include "memory/AddressSpace.h" #include "memory/AddressSpace.h"
#include "memory/UserHeap.h"
#include "sys/elf/Image.h" #include "sys/elf/Image.h"
#define TASK_MAX_FDS 8 #define TASK_MAX_FDS 32
struct Task struct Task
{ {
@ -46,10 +47,22 @@ struct Task
Descriptor files[TASK_MAX_FDS]; Descriptor files[TASK_MAX_FDS];
AddressSpace address_space; AddressSpace address_space;
};
void set_context_from_task(Task& task, Context* ctx); UserHeap allocator;
void get_context_to_task(Task& task, Context* ctx);
void task_save_floating(Task& task); int alloc_fd();
void task_restore_floating(Task& task);
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];
};

View File

@ -1,4 +1,5 @@
#pragma once #pragma once
#include <stddef.h>
#include <stdint.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);

View File

@ -40,4 +40,14 @@ int Descriptor::seek(long offset)
return -EINVAL; // FIXME: Support seeking beyond the current file's length. return -EINVAL; // FIXME: Support seeking beyond the current file's length.
m_offset = (uint64_t)offset; m_offset = (uint64_t)offset;
return 0; 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;
} }

View File

@ -3,6 +3,7 @@
#include "fs/VFS.h" #include "fs/VFS.h"
#include "errno.h" #include "errno.h"
#include "log/Log.h" #include "log/Log.h"
#include "std/libgen.h"
#include "std/stdlib.h" #include "std/stdlib.h"
#include "std/string.h" #include "std/string.h"
@ -131,16 +132,54 @@ int VFS::mkdir(const char* path, const char* name)
if (!node) if (!node)
{ {
kwarnln("Attempting to mkdir in %s, which does not exist", path); 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) if (!node->mkdir_func)
{ {
kwarnln("Chosen node does not support mkdir()"); 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); 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) void VFS::mount(Node* mountpoint, Node* mounted)
{ {
if (!mountpoint || !mounted) return; if (!mountpoint || !mounted) return;

View File

@ -1,5 +1,6 @@
#include "fs/devices/DeviceFS.h" #include "fs/devices/DeviceFS.h"
#include "fs/devices/Console.h" #include "fs/devices/Console.h"
#include "fs/devices/Random.h"
#include "fs/devices/Serial.h" #include "fs/devices/Serial.h"
#include "fs/devices/Version.h" #include "fs/devices/Version.h"
#include "std/stdlib.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++] = VersionDevice::create_new("version");
devfs_files[devfs_file_count++] = ConsoleDevice::create_new("console"); devfs_files[devfs_file_count++] = ConsoleDevice::create_new("console");
devfs_files[devfs_file_count++] = SerialDevice::create_new("serial"); devfs_files[devfs_file_count++] = SerialDevice::create_new("serial");
devfs_files[devfs_file_count++] = RandomDevice::create_new("random");
return devfs_root; return devfs_root;
} }

View 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;
}

View File

@ -1,7 +1,7 @@
#define MODULE "gdt" #define MODULE "gdt"
#include "gdt/GDT.h" #include "gdt/GDT.h"
#include "assert.h" #include "kassert.h"
#include "log/Log.h" #include "log/Log.h"
#include "memory/MemoryManager.h" #include "memory/MemoryManager.h"
#include "std/string.h" #include "std/string.h"

View File

@ -1,12 +1,12 @@
#define MODULE "init" #define MODULE "init"
#include "init/Init.h" #include "init/Init.h"
#include "assert.h"
#include "bootboot.h" #include "bootboot.h"
#include "cpu/CPU.h" #include "cpu/CPU.h"
#include "init/InitRD.h" #include "init/InitRD.h"
#include "interrupts/Interrupts.h" #include "interrupts/Interrupts.h"
#include "io/Serial.h" #include "io/Serial.h"
#include "kassert.h"
#include "log/Log.h" #include "log/Log.h"
#include "memory/MemoryManager.h" #include "memory/MemoryManager.h"
#include "memory/PMM.h" #include "memory/PMM.h"

View File

@ -2,6 +2,7 @@
#include "init/InitRD.h" #include "init/InitRD.h"
#include "bootboot.h" #include "bootboot.h"
#include "errno.h"
#include "fs/VFS.h" #include "fs/VFS.h"
#include "io/Serial.h" #include "io/Serial.h"
#include "log/Log.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) if (total_dirs >= 32)
{ {
kwarnln("mkdir() failed: too many directories"); kwarnln("mkdir() failed: too many directories");
return -1; return -ENOSPC;
} }
if (node->inode > total_dirs) if (node->inode > total_dirs)
{ {
kwarnln("mkdir() failed: invalid node"); kwarnln("mkdir() failed: invalid node");
return -1; return -EINVAL;
} }
if (!(node->type & VFS_DIRECTORY)) if (!(node->type & VFS_DIRECTORY))
{ {
kwarnln("mkdir() failed: not a directory"); kwarnln("mkdir() failed: not a directory");
return -1; return -ENOTDIR;
} }
InitRD::Directory& parent = dirs[node->inode]; InitRD::Directory& parent = dirs[node->inode];
if (parent.entries == INITRD_MAX_FILES_IN_DIR) if (parent.entries == INITRD_MAX_FILES_IN_DIR)
{ {
kwarnln("mkdir() failed: parent is null"); kwarnln("mkdir() failed: parent is full");
return -1; return -ENOSPC;
} }
uint64_t inode = total_dirs; uint64_t inode = total_dirs;
VFS::Node& new_node = nodes[total_nodes++]; VFS::Node& new_node = nodes[total_nodes++];

View File

@ -1,10 +1,10 @@
#define MODULE "isr" #define MODULE "isr"
#include "assert.h"
#include "interrupts/Context.h" #include "interrupts/Context.h"
#include "interrupts/IRQ.h" #include "interrupts/IRQ.h"
#include "interrupts/Interrupts.h" #include "interrupts/Interrupts.h"
#include "io/Serial.h" #include "io/Serial.h"
#include "kassert.h"
#include "log/Log.h" #include "log/Log.h"
#include "misc/hang.h" #include "misc/hang.h"
#include "panic/Panic.h" #include "panic/Panic.h"

View File

@ -1,7 +1,7 @@
#define MODULE "idt" #define MODULE "idt"
#include "interrupts/IDT.h" #include "interrupts/IDT.h"
#include "assert.h" #include "kassert.h"
#include "log/Log.h" #include "log/Log.h"
struct IDTEntry struct IDTEntry

View File

@ -80,4 +80,9 @@ void KernelLog::toggle_log_level(LogLevel level)
void KernelLog::toggle_log_backend(Backend backend) void KernelLog::toggle_log_backend(Backend backend)
{ {
backend_mask ^= (1 << (int)backend); backend_mask ^= (1 << (int)backend);
}
void KernelLog::enable_log_backend(Backend backend)
{
backend_mask |= (1 << (int)backend);
} }

View File

@ -1,40 +1,26 @@
#define MODULE "main" #define MODULE "main"
#include "acpi/RSDT.h"
#include "assert.h"
#include "config.h" #include "config.h"
#include "cpu/CPU.h" #include "cpu/CPU.h"
#include "fs/VFS.h"
#include "fs/devices/DeviceFS.h" #include "fs/devices/DeviceFS.h"
#include "gdt/GDT.h" #include "gdt/GDT.h"
#include "init/Init.h" #include "init/Init.h"
#include "init/InitRD.h"
#include "interrupts/IDT.h" #include "interrupts/IDT.h"
#include "interrupts/Install.h" #include "interrupts/Install.h"
#include "interrupts/Interrupts.h" #include "interrupts/Interrupts.h"
#include "io/PCI.h"
#include "io/PIC.h" #include "io/PIC.h"
#include "io/Serial.h" #include "kassert.h"
#include "log/Log.h" #include "log/Log.h"
#include "memory/AddressSpace.h"
#include "memory/Memory.h" #include "memory/Memory.h"
#include "memory/MemoryManager.h"
#include "memory/MemoryMap.h" #include "memory/MemoryMap.h"
#include "memory/PMM.h" #include "misc/hang.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 "std/stdlib.h" #include "std/stdlib.h"
#include "std/string.h"
#include "sys/elf/ELFLoader.h"
#include "thread/PIT.h" #include "thread/PIT.h"
#include "thread/Scheduler.h" #include "thread/Scheduler.h"
#define STRINGIZE(x) #x
#define STRINGIZE_VALUE_OF(x) STRINGIZE(x)
extern "C" void _start() extern "C" void _start()
{ {
Init::check_magic(); Init::check_magic();
@ -65,7 +51,13 @@ extern "C" void _start()
kinfoln("Prepared scheduler"); 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) while (1)
{ {
sleep(400); sleep(400);
@ -73,24 +65,18 @@ extern "C" void _start()
} }
}); });
Scheduler::load_user_task("/bin/init");
kinfoln("Prepared scheduler tasks"); kinfoln("Prepared scheduler tasks");
ASSERT(VFS::mkdir("/", "dev") == 0); ASSERT(VFS::mkdir("/dev") == 0);
VFS::mount("/dev", DeviceFS::get()); VFS::mount("/dev", DeviceFS::get());
Init::finish_kernel_boot(); Init::finish_kernel_boot();
PIC::remap(); PIC::remap();
PIC::enable_master(0b11111100); // enable keyboard and PIT PIC::enable_master(0b11111100);
PIC::enable_slave(0b11111111); PIC::enable_slave(0b11111111);
kinfoln("Interrupts enabled"); Interrupts::enable();
PCI::scan([](PCI::Device& dev) { while (1) halt();
kinfoln("Found PCI device %x:%x, %s", dev.id().vendor, dev.id().device, pci_type_name(dev.type()));
});
Scheduler::exit(0);
} }

View File

@ -4,24 +4,20 @@
#include "log/Log.h" #include "log/Log.h"
#include "memory/PMM.h" #include "memory/PMM.h"
#include "memory/VMM.h" #include "memory/VMM.h"
#include "std/stdlib.h"
#include "std/string.h"
#include "utils/move.h" #include "utils/move.h"
AddressSpace AddressSpace::create() AddressSpace AddressSpace::create()
{ {
AddressSpace result; AddressSpace result;
result.m_pml4 = (PageTable*)PMM::request_page(); result.m_pml4 = (PageTable*)PMM::request_page();
result.m_cloned = false;
VMM::install_kernel_page_directory_into_address_space(result); VMM::install_kernel_page_directory_into_address_space(result);
return move(result); return move(result);
} }
void AddressSpace::destroy() 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; uint64_t pages_freed = 0;
for (int i = 0; i < 512; i++) for (int i = 0; i < 512; i++)
{ {
@ -78,18 +74,128 @@ void AddressSpace::destroy()
kdbgln("Reclaimed %ld pages from address space!", pages_freed); kdbgln("Reclaimed %ld pages from address space!", pages_freed);
} }
void AddressSpace::detach() void AddressSpace::clear()
{ {
if (!m_cloned) return; uint64_t pages_freed = 0;
m_pml4 = (PageTable*)PMM::request_page(); for (int i = 0; i < 512; i++)
VMM::install_kernel_page_directory_into_address_space(*this); {
m_cloned = false; 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; AddressSpace result;
result.m_pml4 = m_pml4; result.m_pml4 = (PageTable*)PMM::request_page();
result.m_cloned = true; 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; return result;
} }

View File

@ -3,7 +3,7 @@
#include "log/Log.h" #include "log/Log.h"
#endif #endif
#include "assert.h" #include "kassert.h"
#include "memory/KernelHeap.h" #include "memory/KernelHeap.h"
#include "memory/MemoryManager.h" #include "memory/MemoryManager.h"
#include "memory/PMM.h" #include "memory/PMM.h"

View File

@ -1,8 +1,8 @@
#define MODULE "mem" #define MODULE "mem"
#include "memory/PMM.h" #include "memory/PMM.h"
#include "assert.h"
#include "bootboot.h" #include "bootboot.h"
#include "kassert.h"
#include "memory/Memory.h" #include "memory/Memory.h"
#include "memory/MemoryManager.h" #include "memory/MemoryManager.h"
#include "misc/utils.h" #include "misc/utils.h"

View 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;
}

View File

@ -1,7 +1,7 @@
#define MODULE "vmm" #define MODULE "vmm"
#include "memory/VMM.h" #include "memory/VMM.h"
#include "assert.h" #include "kassert.h"
#include "log/Log.h" #include "log/Log.h"
#include "memory/PMM.h" #include "memory/PMM.h"
#include "misc/utils.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_pdp = (PageTable*)kernel_pml4->entries[511].get_address();
PageTable* kernel_last_pd = (PageTable*)kernel_last_pdp->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]; PageDirectoryEntry& space_last_pdp_pde = space_pml4->entries[511];
space_last_pdp_pde.present = true; PageTable* space_last_pdp;
space_last_pdp_pde.read_write = true;
space_last_pdp_pde.set_address((uint64_t)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]; PageDirectoryEntry& space_last_pd_pde = space_last_pdp->entries[511];

View File

@ -8,7 +8,7 @@
#endif #endif
#ifndef MOON_MINOR #ifndef MOON_MINOR
#define MOON_MINOR 11 #define MOON_MINOR 12
#endif #endif
#ifndef _MOON_SUFFIX #ifndef _MOON_SUFFIX

View File

@ -1,9 +1,14 @@
#define MODULE "stack" #define MODULE "stack"
#include "panic/Panic.h" #include "log/Log.h"
#include "misc/hang.h"
#include "render/TextRenderer.h"
#include <stdint.h> #include <stdint.h>
extern "C" void __stack_chk_fail() extern "C" void __stack_chk_fail()
{ {
panic("Stack smashing detected"); KernelLog::enable_log_backend(Backend::Console);
TextRenderer::reset();
kerrorln("stack smashing detected");
hang();
} }

View File

@ -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

View File

@ -6,33 +6,45 @@
#include "io/PIC.h" #include "io/PIC.h"
#include "log/Log.h" #include "log/Log.h"
#include "misc/MSR.h" #include "misc/MSR.h"
#include "render/TextRenderer.h"
#include "std/stdio.h" #include "std/stdio.h"
#include "thread/Scheduler.h" #include "thread/Scheduler.h"
#include "trace/StackTracer.h" #include "trace/StackTracer.h"
static void dump_registers(Context* context) void dump_registers(Context* context)
{ {
printf("-- Registers: \n"); kinfoln("-- Registers:");
printf("rax: %lx, rbx: %lx, rcx: %lx, rdx: %lx\n", context->rax, context->rbx, context->rcx, context->rdx); kinfoln("rax: %lx, rbx: %lx, rcx: %lx, rdx: %lx", 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); kinfoln("rsi: %lx, rdi: %lx, rsp: %lx, rbp: %lx", 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); kinfoln("r8: %lx, r9: %lx, r10: %lx, r11: %lx", 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); kinfoln("r12: %lx, r13: %lx, r14: %lx, r15: %lx", context->r12, context->r13, context->r14, context->r15);
printf("rip: %lx, cs: %lx, ss: %lx\n", context->rip, context->cs, context->ss); kinfoln("rip: %lx, cs: %lx, ss: %lx", context->rip, context->cs, context->ss);
printf("rflags: %lx, cr2: %lx\n", context->rflags, context->cr2); kinfoln("rflags: %lx, cr2: %lx", context->rflags, context->cr2);
printf("ia32_efer: %lx\n", MSR::read_from(IA32_EFER_MSR)); 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()) if (InitRD::is_initialized())
{ {
printf("-- Stack trace:\n"); kinfoln("-- Stack trace:");
StackTracer tracer(context->rbp); if (context)
tracer.trace_with_ip(context->rip); {
StackTracer tracer(context->rbp);
tracer.trace_with_ip(context->rip);
}
else
{
uintptr_t rbp;
asm volatile("mov %%rbp, %0" : "=r"(rbp));
StackTracer tracer(rbp);
tracer.trace();
}
} }
else { printf("-- No stack trace available\n"); } else { kinfoln("-- No stack trace available"); }
PIC::enable_master(0b11111101); // enable keyboard only PIC::enable_master(0b11111101); // enable keyboard only
PIC::enable_slave(0b11111111); PIC::enable_slave(0b11111111);
@ -53,10 +65,14 @@ static void dump_registers(Context* context)
while (1) asm volatile("hlt"); 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"); 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)); } if (context->number >= 0x20 && context->number < 0x30) { PIC::send_eoi((uint8_t)(context->irq_number & 0xFF)); }
Task* task; Task* task;
@ -69,11 +85,20 @@ extern "C" [[noreturn]] void __do_int_panic(Context* context, const char* file,
__panic_stub(context); __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; asm volatile("cli");
if ((task = Scheduler::current_task())) { kerrorln("Kernel panic in task %ld: %s", task->id, message); }
else { kerrorln("Kernel panic: %s", message); }
__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);
} }

View File

@ -1,7 +1,7 @@
#define MODULE "rand" #define MODULE "rand"
#include "rand/Mersenne.h" #include "rand/Mersenne.h"
#include "assert.h" #include "kassert.h"
#include <stddef.h> #include <stddef.h>
typedef uint64_t word_t; typedef uint64_t word_t;

47
kernel/src/std/libgen.cpp Normal file
View 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;
}

View File

@ -17,11 +17,29 @@ char* strcpy(char* dest, const char* src)
char* strncpy(char* dest, const char* src, size_t n) char* strncpy(char* dest, const char* src, size_t n)
{ {
size_t src_len = strlen(src) + 1; // NULL byte size_t i;
memcpy(dest, src, src_len > n ? n : src_len); for (i = 0; i < n && src[i] != 0; i++) dest[i] = src[i];
for (; i < n; i++) dest[i] = 0;
return dest; 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) int strcmp(const char* a, const char* b)
{ {
while (*a && (*a == *b)) while (*a && (*a == *b))
@ -130,4 +148,12 @@ char* strdup(const char* src)
char* duplicated = (char*)kmalloc(length + 1); char* duplicated = (char*)kmalloc(length + 1);
memcpy(duplicated, src, length + 1); memcpy(duplicated, src, length + 1);
return duplicated; 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;
} }

View File

@ -16,8 +16,7 @@ void Syscall::entry(Context* context)
case SYS_sleep: sys_sleep(context, context->rdi); break; 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_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_paint: sys_paint(context, context->rdi, context->rsi, context->rdx, context->r10, context->r8); break;
case SYS_rand: sys_rand(context); break; case SYS_getpid: sys_getpid(context); break;
case SYS_gettid: sys_gettid(context); break;
case SYS_mmap: sys_mmap(context, (void*)context->rdi, context->rsi, (int)context->rdx); break; case SYS_mmap: sys_mmap(context, (void*)context->rdi, context->rsi, (int)context->rdx); break;
case SYS_munmap: sys_munmap(context, (void*)context->rdi, context->rsi); break; case SYS_munmap: sys_munmap(context, (void*)context->rdi, context->rsi); break;
case SYS_open: sys_open(context, (const char*)context->rdi, (int)context->rsi); break; case SYS_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_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_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_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; default: context->rax = -ENOSYS; break;
} }
VMM::exit_syscall_context(); 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); uint64_t phys = VMM::get_physical((uint64_t)user_string);
if (phys == (uint64_t)-1) { return nullptr; } if (phys == (uint64_t)-1) { return nullptr; }

9
kernel/src/sys/clock.cpp Normal file
View 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;
}

View File

@ -1,10 +1,10 @@
#define MODULE "elf" #define MODULE "elf"
#include "sys/elf/ELFLoader.h" #include "sys/elf/ELFLoader.h"
#include "assert.h"
#include "errno.h" #include "errno.h"
#include "fs/VFS.h" #include "fs/VFS.h"
#include "init/InitRD.h" #include "init/InitRD.h"
#include "kassert.h"
#include "log/Log.h" #include "log/Log.h"
#include "memory/Memory.h" #include "memory/Memory.h"
#include "memory/MemoryManager.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) long ELFLoader::check_elf_image(VFS::Node* node)
{ {
Elf64_Ehdr elf_ehdr; 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"); kwarnln("Unable to read ELF header");
return -1; return -ENOEXEC;
} }
if (strncmp((const char*)elf_ehdr.e_ident, ELFMAG, SELFMAG) != 0) if (strncmp((const char*)elf_ehdr.e_ident, ELFMAG, SELFMAG) != 0)
{ {
kwarnln("ELF file has invalid magic, skipping"); kwarnln("ELF file has invalid magic, skipping");
return -1; return -ENOEXEC;
} }
if (elf_ehdr.e_ident[EI_CLASS] != ELFCLASS64) if (elf_ehdr.e_ident[EI_CLASS] != ELFCLASS64)
{ {
kwarnln("ELF file is not ELF64, skipping"); kwarnln("ELF file is not ELF64, skipping");
return -1; return -ENOEXEC;
} }
if (elf_ehdr.e_ident[EI_DATA] != ELFDATA2LSB) if (elf_ehdr.e_ident[EI_DATA] != ELFDATA2LSB)
{ {
kwarnln("ELF file is not little-endian, skipping"); kwarnln("ELF file is not little-endian, skipping");
return -1; return -ENOEXEC;
} }
if (elf_ehdr.e_type != ET_EXEC) if (elf_ehdr.e_type != ET_EXEC)
{ {
kwarnln("not supported: ELF file is not an executable"); kwarnln("not supported: ELF file is not an executable");
return -1; return -ENOEXEC;
} }
if (elf_ehdr.e_machine != EM_MACH) if (elf_ehdr.e_machine != EM_MACH)
{ {
kwarnln("Unsupported target machine"); kwarnln("Unsupported target machine");
return -1; return -ENOEXEC;
} }
if (elf_ehdr.e_phnum == 0) if (elf_ehdr.e_phnum == 0)
{ {
kwarnln("ELF file has no PHDRS"); kwarnln("ELF file has no PHDRS");
return -1; return -ENOEXEC;
} }
int i; int i;
int loadable_sections = 0; int loadable_sections = 0;
@ -170,12 +170,12 @@ long ELFLoader::check_elf_image(VFS::Node* node)
if (!phdr.p_vaddr) if (!phdr.p_vaddr)
{ {
kerrorln("segment address is NULL, this is invalid :("); 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)) 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"); kerrorln("trying to load ELF into kernel memory");
return -1; return -ENOEXEC;
} }
loadable_sections++; loadable_sections++;
memusage += Utilities::get_blocks_from_size(PAGE_SIZE, phdr.p_memsz) * PAGE_SIZE; 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) if (!loadable_sections)
{ {
kwarnln("No loadable sections"); kwarnln("No loadable sections");
return -1; return -ENOEXEC;
} }
return memusage; return memusage;
} }

View File

@ -1,8 +1,9 @@
#define MODULE "exec" #define MODULE "exec"
#include "assert.h"
#include "errno.h" #include "errno.h"
#include "interrupts/Interrupts.h" #include "interrupts/Interrupts.h"
#include "kassert.h"
#include "log/Log.h"
#include "memory/MemoryManager.h" #include "memory/MemoryManager.h"
#include "memory/PMM.h" #include "memory/PMM.h"
#include "memory/VMM.h" #include "memory/VMM.h"
@ -12,11 +13,48 @@
#include "sys/elf/ELFLoader.h" #include "sys/elf/ELFLoader.h"
#include "thread/Scheduler.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) 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); char* kpathname = Syscall::strdup_from_user(pathname);
if (!kpathname) if (!kpathname)
{ {
@ -49,20 +87,9 @@ void sys_exec(Context* context, const char* pathname)
return; 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()) if ((uint64_t)memusage > PMM::get_free())
{ {
kfree(kpathname); kfree(kpathname);
MemoryManager::release_pages((void*)allocated_stack, TASK_PAGES_IN_STACK);
context->rax = -ENOMEM; context->rax = -ENOMEM;
return; return;
} }
@ -75,20 +102,25 @@ void sys_exec(Context* context, const char* pathname)
// At this point, pretty much nothing can fail. // 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); 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 ASSERT(image); // If check_elf_image succeeded, load_elf_from_vfs MUST succeed, unless something has gone terribly
// wrong. // wrong.
task->allocated_stack = allocated_stack; strlcpy(task->name, kpathname, sizeof(task->name));
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);
}
Scheduler::reset_task(task, image); Scheduler::reset_task(task, image);
set_context_from_task(*task, context); task->restore_context(context);
kfree(kpathname); kfree(kpathname);

View File

@ -3,14 +3,37 @@
#include "errno.h" #include "errno.h"
#include "interrupts/Context.h" #include "interrupts/Context.h"
#include "log/Log.h" #include "log/Log.h"
#include "memory/Memory.h"
#include "memory/MemoryManager.h" #include "memory/MemoryManager.h"
#include "memory/VMM.h" #include "memory/VMM.h"
#include "misc/utils.h" #include "misc/utils.h"
#include "thread/Scheduler.h"
#include <stddef.h> #include <stddef.h>
#define MAP_READ 1
#define MAP_WRITE 2
#define MAP_NONE 0
#define MAP_FAIL(errno) 0xffffffffffffff00 | (unsigned char)(errno) #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) 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); context->rax = MAP_FAIL(EINVAL);
return; return;
} }
int real_flags = MAP_USER; int real_flags = mman_flags_from_prot(prot);
if (flags & MAP_READ_WRITE) real_flags |= MAP_READ_WRITE;
if (address) if (address)
{ {
kdbgln("mmap(): %ld pages at address %p, %s", size / PAGE_SIZE, address, kdbgln("mmap(): %ld pages at address %p, %s", size / PAGE_SIZE, address, format_prot(prot));
real_flags & MAP_READ_WRITE ? "rw" : "ro"); 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. if (VMM::get_physical((uint64_t)address) != (uint64_t)-1) // Address is already used.
{ {
kwarnln("attempt to map an already mapped address"); 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 else
{ {
kwarnln("mmap() failed"); kwarnln("mmap() failed: failed to allocate physical memory");
context->rax = MAP_FAIL(ENOMEM); context->rax = MAP_FAIL(ENOMEM);
return; return;
} }
} }
kdbgln("mmap(): %ld pages at any address, %s", Utilities::get_blocks_from_size(PAGE_SIZE, size), kdbgln("mmap(): %ld pages at any address, %s", Utilities::get_blocks_from_size(PAGE_SIZE, size), format_prot(prot));
real_flags & MAP_READ_WRITE ? "rw" : "ro"); uint64_t ptr =
void* result = MemoryManager::get_pages(Utilities::get_blocks_from_size(PAGE_SIZE, size), real_flags); 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) if (result)
{ {
kdbgln("mmap() succeeded: %p", result); kdbgln("mmap() succeeded: %p", result);
@ -63,7 +97,7 @@ void sys_mmap(Context* context, void* address, size_t size, int flags)
} }
else else
{ {
kwarnln("mmap() failed"); kwarnln("mmap() failed: failed to allocate physical memory");
context->rax = MAP_FAIL(ENOMEM); context->rax = MAP_FAIL(ENOMEM);
return; return;
} }
@ -90,16 +124,68 @@ void sys_munmap(Context* context, void* address, size_t size)
context->rax = -EINVAL; context->rax = -EINVAL;
return; return;
} }
uint64_t flags = VMM::get_flags((uint64_t)address); if (Memory::is_kernel_address((uintptr_t)address))
if (!(flags & MAP_USER))
{ {
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; context->rax = -EINVAL;
return; return;
} }
uint64_t offset = (uint64_t)address % PAGE_SIZE; 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)); MemoryManager::release_pages((void*)((uint64_t)address - offset), Utilities::get_blocks_from_size(PAGE_SIZE, size));
kdbgln("munmap() succeeded"); kdbgln("munmap() succeeded");
context->rax = 0; context->rax = 0;
return; 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;
} }

View File

@ -1,7 +0,0 @@
#include "interrupts/Context.h"
#include "rand/Mersenne.h"
void sys_rand(Context* context)
{
context->rax = Mersenne::get();
}

View File

@ -20,7 +20,7 @@ void sys_sleep(Context* context, uint64_t ms)
Scheduler::task_yield(context); Scheduler::task_yield(context);
} }
void sys_gettid(Context* context) void sys_getpid(Context* context)
{ {
context->rax = Scheduler::current_task()->id; context->rax = Scheduler::current_task()->id;
} }

View File

@ -18,6 +18,47 @@
#define SEEK_CUR 1 #define SEEK_CUR 1
#define SEEK_END 2 #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) void sys_seek(Context* context, int fd, long offset, int whence)
{ {
if (whence < SEEK_SET || whence > SEEK_END) 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; context->rax = -EBADF;
return; return;
} }
Descriptor& file = current_task->files[fd];
long new_offset; long new_offset;
if (whence == SEEK_SET) new_offset = offset; if (whence == SEEK_SET) new_offset = offset;
else if (whence == SEEK_CUR) else if (whence == SEEK_CUR)
new_offset = offset + current_task->files[fd].offset(); new_offset = offset + file.offset();
else if (whence == SEEK_END) else if (whence == SEEK_END)
new_offset = current_task->files[fd].length() + offset; new_offset = file.length() + offset;
else else
__builtin_unreachable(); __builtin_unreachable();
if (new_offset < 0) 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? context->rax = -EINVAL; // FIXME: Is this the right error?
return; 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) if (result < 0)
{ {
context->rax = result; context->rax = result;
@ -77,12 +124,13 @@ void sys_write(Context* context, int fd, size_t size, const char* addr)
context->rax = -EBADF; context->rax = -EBADF;
return; return;
} }
if (!current_task->files[fd].can_write()) Descriptor& file = current_task->files[fd];
if (!file.can_write())
{ {
context->rax = -EBADF; context->rax = -EBADF;
return; 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; context->rax = (size_t)result;
return; 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) void sys_open(Context* context, const char* filename, int flags)
{ {
Task* current_task = Scheduler::current_task(); Task* current_task = Scheduler::current_task();
int fd; int fd = current_task->alloc_fd();
for (fd = 0; fd < TASK_MAX_FDS; fd++) if (fd < 0)
{
if (!current_task->files[fd].is_open()) break;
}
if (fd == TASK_MAX_FDS)
{ {
context->rax = -EMFILE; context->rax = -EMFILE;
return; return;
@ -178,4 +221,20 @@ void sys_close(Context* context, int fd)
current_task->files[fd].close(); current_task->files[fd].close();
context->rax = 0; context->rax = 0;
return; 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;
} }

View File

@ -1,10 +1,12 @@
#define MODULE "sched" #define MODULE "sched"
#include "thread/Scheduler.h" #include "thread/Scheduler.h"
#include "assert.h" #include "errno.h"
#include "interrupts/Interrupts.h" #include "interrupts/Interrupts.h"
#include "kassert.h"
#include "log/Log.h" #include "log/Log.h"
#include "memory/MemoryManager.h" #include "memory/MemoryManager.h"
#include "memory/PMM.h"
#include "memory/VMM.h" #include "memory/VMM.h"
#include "misc/hang.h" #include "misc/hang.h"
#include "misc/utils.h" #include "misc/utils.h"
@ -31,6 +33,51 @@ extern "C" void idle_task_function();
static uint64_t frequency; 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() void Scheduler::init()
{ {
memset(&idle_task, 0, sizeof(Task)); memset(&idle_task, 0, sizeof(Task));
@ -44,22 +91,12 @@ void Scheduler::init()
idle_task.user_task = false; idle_task.user_task = false;
idle_task.state = idle_task.Idle; idle_task.state = idle_task.Idle;
base_task = new Task; sched_current_task = &idle_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
frequency = 1000 / PIT::frequency(); 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; Task* new_task = new Task;
ASSERT(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_sleep = 0;
new_task->task_time = 0; new_task->task_time = 0;
new_task->cpu_time = 0; new_task->cpu_time = 0;
end_task->next_task = new_task; strlcpy(new_task->name, taskname, sizeof(new_task->name));
new_task->prev_task = end_task; append_task(new_task);
base_task->prev_task = new_task;
new_task->next_task = base_task;
end_task = new_task;
new_task->state = new_task->Running; new_task->state = new_task->Running;
task_num++; task_num++;
kinfoln("Adding kernel task: starts at %lx, tid %ld, stack at %lx, total tasks: %ld", new_task->regs.rip, kinfoln("Adding kernel task: %s, starts at %lx, PID %ld, stack at %lx, total tasks: %ld", new_task->name,
new_task->id, new_task->regs.rsp, task_num); new_task->regs.rip, new_task->id, new_task->regs.rsp, task_num);
} }
Task* Scheduler::create_user_task() Task* Scheduler::create_user_task()
{ {
Task* new_task = new Task; Task* new_task = new Task;
ASSERT(new_task); if (!new_task) return nullptr;
memset(&new_task->regs, 0, sizeof(Context)); memset(&new_task->regs, 0, sizeof(Context));
new_task->user_task = true; new_task->user_task = true;
new_task->id = free_tid++; 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_sleep = 0;
new_task->task_time = 0; new_task->task_time = 0;
new_task->cpu_time = 0; new_task->cpu_time = 0;
end_task->next_task = new_task; append_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;
task_num++; task_num++;
return new_task; 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); kinfoln("Loading user task: %s", filename);
Interrupts::push_and_disable(); 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); kerrorln("Failed to load %s from initrd", filename);
Interrupts::pop(); Interrupts::pop();
return; return result;
} }
if ((uint64_t)result > PMM::get_free()) { return -ENOMEM; }
Task* new_task = new Task; Task* new_task = new Task;
ASSERT(new_task); ASSERT(new_task);
memset(&new_task->regs, 0, sizeof(Context)); memset(&new_task->regs, 0, sizeof(Context));
new_task->id = free_tid++; new_task->id = free_tid++;
if (!new_task->allocator.init())
{
delete new_task;
Interrupts::pop();
return -ENOMEM;
}
new_task->address_space = AddressSpace::create(); new_task->address_space = AddressSpace::create();
VMM::switch_to_user_address_space(new_task->address_space); VMM::switch_to_user_address_space(new_task->address_space);
ELFImage* image = ELFLoader::load_elf_from_filesystem( 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->user_task = true;
new_task->regs.rip = image->entry; new_task->regs.rip = image->entry;
new_task->image = image; new_task->image = image;
new_task->allocated_stack = (uint64_t)MemoryManager::get_pages( new_task->allocated_stack = (uint64_t)MemoryManager::get_pages_at(
TASK_PAGES_IN_STACK, MAP_READ_WRITE | MAP_USER); // 16 KB is enough for everyone, right? 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.rsp = get_top_of_stack(new_task->allocated_stack, TASK_PAGES_IN_STACK);
new_task->regs.cs = 0x18 | 0x03; new_task->regs.cs = 0x18 | 0x03;
new_task->regs.ss = 0x20 | 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_sleep = 0;
new_task->task_time = 0; new_task->task_time = 0;
new_task->cpu_time = 0; new_task->cpu_time = 0;
end_task->next_task = new_task; strlcpy(new_task->name, filename, sizeof(new_task->name));
new_task->prev_task = end_task; append_task(new_task);
base_task->prev_task = new_task;
new_task->next_task = base_task;
end_task = new_task;
new_task->state = new_task->Running; new_task->state = new_task->Running;
task_num++; task_num++;
kinfoln("Adding user task: loaded at %lx, tid %ld, stack at %lx, total tasks: %ld", new_task->regs.rip, kinfoln("Adding user task: %s, loaded at %lx, PID %ld, stack at %lx, total tasks: %ld", new_task->name,
new_task->id, new_task->regs.rsp, task_num); new_task->regs.rip, new_task->id, new_task->regs.rsp, task_num);
VMM::switch_back_to_kernel_address_space(); VMM::switch_back_to_kernel_address_space();
Interrupts::pop(); Interrupts::pop();
return (long)new_task->id;
} }
void Scheduler::reset_task(Task* task, ELFImage* new_image) 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->regs.rflags = (1 << 21) | (1 << 9); // enable interrupts
task->task_sleep = 0; task->task_sleep = 0;
task->cpu_time = 0; task->cpu_time = 0;
kinfoln("Resetting task: loaded at %lx, tid %ld, stack at %lx, total tasks: %ld", task->regs.rip, task->id, kinfoln("Resetting task: %s, loaded at %lx, PID %ld, stack at %lx, total tasks: %ld", task->name, task->regs.rip,
task->regs.rsp, task_num); task->id, task->regs.rsp, task_num);
} }
void Scheduler::reap_task(Task* task) void Scheduler::reap_task(Task* task)
@ -184,8 +226,9 @@ void Scheduler::reap_task(Task* task)
VMM::apply_address_space(); VMM::apply_address_space();
VMM::switch_to_user_address_space(exiting_task->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); kinfoln("reaping task %s, PID %ld, exited with code %ld", exiting_task->name, exiting_task->id,
if (exiting_task->allocated_stack) 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); 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. 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()) if (exiting_task->is_user_task())
{ {
exiting_task->allocator.free();
VMM::switch_back_to_kernel_address_space(); VMM::switch_back_to_kernel_address_space();
VMM::apply_address_space(); VMM::apply_address_space();
Interrupts::push_and_enable(); Interrupts::push_and_enable();
@ -276,17 +320,15 @@ void Scheduler::reap_tasks()
static void sched_decrement_sleep_times() static void sched_decrement_sleep_times()
{ {
Task* task = base_task; sched_for_each_task([](Task* task) {
if (!task) return;
do {
if (task->task_sleep > 0) if (task->task_sleep > 0)
{ {
task->task_sleep -= frequency; task->task_sleep -= frequency;
if (task->task_sleep < 0) task->task_sleep = 0; if (task->task_sleep < 0) task->task_sleep = 0;
} }
if (task->task_sleep == 0 && task->state == task->Sleeping) task->state = task->Running; if (task->task_sleep == 0 && task->state == task->Sleeping) task->state = task->Running;
task = task->next_task; return true;
} while (task != base_task); });
} }
void Scheduler::task_tick(Context* context) void Scheduler::task_tick(Context* context)
@ -309,7 +351,7 @@ void Scheduler::task_yield(Context* context)
{ {
ASSERT(Interrupts::is_in_handler()); ASSERT(Interrupts::is_in_handler());
Interrupts::disable(); Interrupts::disable();
get_context_to_task(*sched_current_task, context); sched_current_task->save_context(context);
bool was_idle = false; bool was_idle = false;
if (sched_current_task->state == sched_current_task->Idle) 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 (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()) if (sched_current_task->is_user_task())
{ {
VMM::switch_to_user_address_space(sched_current_task->address_space); sched_current_task->switch_to_address_space();
VMM::apply_address_space(); sched_current_task->restore_floating();
task_restore_floating(*sched_current_task);
} }
else if (!was_idle && original_task->is_user_task() && !sched_current_task->is_user_task()) 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; sched_current_task->task_time = 20;
set_context_from_task(*sched_current_task, context); sched_current_task->restore_context(context);
return; return;
} }
} while (sched_current_task != original_task); } while (sched_current_task != original_task);
if (!was_idle && original_task->is_user_task() && original_task->state != original_task->Exited) 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 = &idle_task;
sched_current_task->task_time = frequency; 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; return;
} }

View File

@ -1,29 +1,68 @@
#include "thread/Task.h" #include "thread/Task.h"
#include "memory/VMM.h"
#include "std/string.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, &regs, sizeof(Context));
} }
void get_context_to_task(Task& task, Context* ctx) void Task::save_context(Context* context)
{ {
memcpy(&task.regs, ctx, sizeof(Context)); memcpy(&regs, context, sizeof(Context));
} }
void task_save_floating(Task& task) void Task::save_floating()
{ {
task.floating_saved = true; floating_saved = true;
asm volatile("fxsave (%0)" : : "r"(&task.floating_region)); asm volatile("fxsave (%0)" : : "r"((char*)floating_region));
} }
void task_restore_floating(Task& task) void Task::restore_floating()
{ {
if (!task.floating_saved) return; if (!floating_saved) return;
asm volatile("fxrstor (%0)" : : "r"(&task.floating_region)); asm volatile("fxrstor (%0)" : : "r"((char*)floating_region));
} }
bool Task::is_user_task() bool Task::is_user_task()
{ {
return 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;
} }

View File

@ -17,10 +17,10 @@ static size_t symbol_strlen(const char* symbol)
return (i - 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 == (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) while (address >= (uintptr_t)&kernel_start && address <= (uintptr_t)&kernel_end)
{ {
char addr_as_str[60]; char addr_as_str[60];
@ -30,11 +30,12 @@ void get_symbol_name(uintptr_t address, char* buffer)
{ {
symbol += 19; symbol += 19;
size_t symlen = symbol_strlen(symbol); size_t symlen = symbol_strlen(symbol);
memcpy(buffer, symbol, symlen); size_t copylen = (max - 1) < symlen ? (max - 1) : symlen;
buffer[symlen] = 0; memcpy(buffer, symbol, copylen);
buffer[copylen] = 0;
return; return;
} }
address--; address--;
} }
strncpy(buffer, "(no symbol)", 12); strlcpy(buffer, "(no symbol)", max);
} }

View File

@ -1,4 +1,7 @@
#define MODULE "trace"
#include "trace/StackTracer.h" #include "trace/StackTracer.h"
#include "log/Log.h"
#include "memory/Memory.h" #include "memory/Memory.h"
#include "std/stdio.h" #include "std/stdio.h"
#include "trace/Resolve.h" #include "trace/Resolve.h"
@ -21,11 +24,11 @@ typedef struct stackframe
void StackTracer::trace() void StackTracer::trace()
{ {
stackframe* frame = (stackframe*)m_base_pointer; stackframe* frame = (stackframe*)m_base_pointer;
while (frame) while (frame && frame->instruction)
{ {
char symbol_name[512]; char symbol_name[512];
get_symbol_name(frame->instruction, symbol_name); get_symbol_name(frame->instruction - sizeof(uintptr_t), symbol_name, sizeof(symbol_name));
printf("%lx: %s\n", frame->instruction, symbol_name); kinfoln("%lx: %s", frame->instruction - sizeof(uintptr_t), symbol_name);
frame = frame->next; frame = frame->next;
} }
} }
@ -33,8 +36,8 @@ void StackTracer::trace()
void StackTracer::trace_with_ip(uintptr_t ip) void StackTracer::trace_with_ip(uintptr_t ip)
{ {
char symbol_name[512]; char symbol_name[512];
get_symbol_name(ip, symbol_name); get_symbol_name(ip, symbol_name, sizeof(symbol_name));
printf("%lx: %s\n", ip, symbol_name); kinfoln("%lx: %s", ip, symbol_name);
trace(); trace();
} }

View 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

View File

@ -1,8 +1,9 @@
#ifndef _BITS_MACROS_H #ifndef _BITS_MACROS_H
#define _BITS_MACROS_H #define _BITS_MACROS_H
#define noreturn __attribute__((noreturn)) #define __lc_noreturn __attribute__((noreturn))
#define align(n) __attribute__((aligned(n))) #define __lc_align(n) __attribute__((aligned(n)))
#define deprecated(msg) __attribute__((deprecated(msg))) #define __lc_deprecated(msg) __attribute__((deprecated(msg)))
#define __lc_unreachable __builtin_unreachable
#endif #endif

58
libs/libc/include/ctype.h Normal file
View 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

View File

@ -4,15 +4,20 @@
/* The last error encountered during a call to a library or system function. */ /* The last error encountered during a call to a library or system function. */
extern int errno; extern int errno;
#define EPERM 1 // Operation not permitted #define EPERM 1 // Operation not permitted
#define ENOENT 2 // No such file or directory #define ENOENT 2 // No such file or directory
#define ENOEXEC 8 // Exec format error #define ENOEXEC 8 // Exec format error
#define EBADF 9 // Bad file descriptor #define EBADF 9 // Bad file descriptor
#define ENOMEM 12 // Cannot allocate memory #define ENOMEM 12 // Cannot allocate memory
#define EFAULT 14 // Bad address #define EFAULT 14 // Bad address
#define EISDIR 21 // Is a directory #define EEXIST 17 // File exists
#define EINVAL 22 // Invalid argument #define ENOTDIR 20 // Not a directory
#define EMFILE 24 // Too many open files #define EISDIR 21 // Is a directory
#define ENOSYS 38 // Function not implemented #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 #endif

View File

@ -8,6 +8,9 @@
/* Open for reading and writing. */ /* Open for reading and writing. */
#define O_RDWR 3 #define O_RDWR 3
/* Duplicate a file descriptor. */
#define F_DUPFD 0
#ifdef __cplusplus #ifdef __cplusplus
extern "C" extern "C"
{ {
@ -16,6 +19,9 @@ extern "C"
/* Opens the file specified by pathname. Returns a file descriptor on success, or -1 on error. */ /* Opens the file specified by pathname. Returns a file descriptor on success, or -1 on error. */
int open(const char* pathname, int flags); 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 #ifdef __cplusplus
} }
#endif #endif

View 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

View 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

View File

@ -9,14 +9,11 @@ extern "C"
{ {
#endif #endif
/* Returns the current program's thread identifier. */
pid_t gettid();
/* Sleeps for ms milliseconds. */ /* Sleeps for ms milliseconds. */
unsigned int msleep(unsigned int ms); unsigned int msleep(unsigned int ms);
/* Prints a message to standard error and aborts the program. */ /* 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 #ifdef __cplusplus
} }

View 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

View File

@ -6,15 +6,19 @@
#define SYS_sleep 2 #define SYS_sleep 2
#define SYS_write 3 #define SYS_write 3
#define SYS_paint 4 #define SYS_paint 4
#define SYS_rand 5 #define SYS_getpid 5
#define SYS_gettid 6 #define SYS_mmap 6
#define SYS_mmap 7 #define SYS_munmap 7
#define SYS_munmap 8 #define SYS_open 8
#define SYS_open 9 #define SYS_read 9
#define SYS_read 10 #define SYS_close 10
#define SYS_close 11 #define SYS_seek 11
#define SYS_seek 12 #define SYS_exec 12
#define SYS_exec 13 #define SYS_fcntl 13
#define SYS_mprotect 14
#define SYS_clock 15
#define SYS_mkdir 16
#define SYS_fork 17
#ifndef __want_syscalls #ifndef __want_syscalls
#ifdef __cplusplus #ifdef __cplusplus

16
libs/libc/include/sched.h Normal file
View 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

View 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

View 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

View File

@ -6,6 +6,8 @@
#include <bits/seek.h> #include <bits/seek.h>
#define FOPEN_MAX 32
typedef struct typedef struct
{ {
int f_fd; int f_fd;
@ -40,6 +42,9 @@ extern "C"
/* Returns a new file associated with the file descriptor fd. */ /* Returns a new file associated with the file descriptor fd. */
FILE* fdopen(int fd, const char* mode); 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. */ /* Writes formatted output according to the string format to the file stream. */
int fprintf(FILE* stream, const char* format, ...); int fprintf(FILE* stream, const char* format, ...);

View File

@ -10,16 +10,25 @@ extern "C"
#endif #endif
/* Aborts the program. */ /* Aborts the program. */
noreturn void abort(); __lc_noreturn void abort(void);
/* Normally exits the program with the specified status code. */ /* 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. */ /* Registers a handler function to be run at normal program termination. */
int atexit(void (*handler)(void)); int atexit(void (*handler)(void));
/* Not implemented.*/ /* Returns an integer (of type int) parsed from the string str. */
int atoi(const char*); 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. */ /* Not implemented. */
char* getenv(const char*); char* getenv(const char*);
@ -41,6 +50,12 @@ extern "C"
* is undefined behavior. */ * is undefined behavior. */
void free(void* ptr); 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 #ifdef __cplusplus
} }
#endif #endif

View File

@ -15,15 +15,26 @@ extern "C"
/* Sets n bytes of buf to c, cast to a character. */ /* Sets n bytes of buf to c, cast to a character. */
void* memset(void* buf, int c, size_t n); void* memset(void* buf, int c, size_t n);
/* Clears n bytes of buf. */ /* Searches for the character c in n bytes of buf. */
void* memclr(void* buf, size_t n); 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. */ /* Returns the length of the string str. */
size_t strlen(const char* str); size_t strlen(const char* str);
/* Copies the string src into dest. This function is unsafe, use strncpy instead. */ /* Returns the length of the string str, while examining at most max bytes of str. */
deprecated("strcpy is unsafe and should not be used; use strncpy instead") char* strcpy(char* dest, size_t strnlen(const char* str, size_t max);
const char* src);
/* 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. */ /* Copies at most max bytes from the string src into dest. */
char* strncpy(char* dest, const char* src, size_t max); 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. */ /* 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); char* strchr(const char* str, int c);
/* Concatenates the string src into dest. This function is unsafe, use strncat instead. */ /* Returns a pointer to the last occurrence of the character c in str, or NULL if it is not found. */
deprecated("strcat is unsafe and should not be used; use strncat instead") char* strcat(char* dest, char* strrchr(const char* str, int c);
const char* src);
/* Concatenates at most max bytes of the string src into dest. */ /* Concatenates at most max bytes of the string src into dest. */
char* strncat(char* dest, const char* src, size_t max); 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. */ /* Returns the error string associated with the error number err. */
char* strerror(int 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 #ifdef __cplusplus
} }
#endif #endif

View File

@ -7,7 +7,9 @@
/* Address returned by mmap when it fails. */ /* Address returned by mmap when it fails. */
#define MAP_FAILED (void*)-1 #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 #define PAGE_SIZE 4096
@ -24,6 +26,9 @@ extern "C"
* address space. */ * address space. */
int munmap(void* addr, size_t size); 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 #ifdef __cplusplus
} }
#endif #endif

View 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

View File

@ -13,4 +13,7 @@ typedef long int ssize_t;
/* The type of an offset into a file. */ /* The type of an offset into a file. */
typedef long int off_t; typedef long int off_t;
/* The type of a file's mode. */
typedef unsigned short mode_t;
#endif #endif

View File

@ -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

View File

@ -3,8 +3,12 @@
#include <bits/macros.h> #include <bits/macros.h>
#include <bits/seek.h> #include <bits/seek.h>
#include <luna/os-limits.h>
#include <sys/types.h> #include <sys/types.h>
#define STDOUT_FILENO 0
#define STDERR_FILENO 1
#ifdef __cplusplus #ifdef __cplusplus
extern "C" extern "C"
{ {
@ -18,11 +22,16 @@ extern "C"
int execve(const char*, char* const[], char* const[]); int execve(const char*, char* const[], char* const[]);
/* Not implemented. */ /* Not implemented. */
int execvp(const char*, char* const[]); 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); pid_t fork(void);
/* Returns the current process's process ID. */
pid_t getpid(void);
/* Terminates the program with the status code status. */ /* 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. */ /* Calls the kernel for a specific service, determined by number. */
long syscall(long number, ...); long syscall(long number, ...);
@ -42,6 +51,9 @@ extern "C"
/* Moves the read/write file offset for fd to offset, depending on whence. */ /* Moves the read/write file offset for fd to offset, depending on whence. */
off_t lseek(int fd, off_t offset, int 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 #ifdef __cplusplus
} }
#endif #endif

12
libs/libc/src/assert.cpp Normal file
View 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();
}
}

View File

@ -17,7 +17,7 @@ extern "C"
return 0; 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](); } for (int i = 0; i < atexit_function_count; i++) { atexit_functions[i](); }
_exit(status); _exit(status);

View File

@ -13,7 +13,7 @@ int liballoc_unlock()
void* liballoc_alloc(size_t size) 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; if (result == MAP_FAILED) return 0;
return (void*)result; return (void*)result;
} }

81
libs/libc/src/ctype.cpp Normal file
View 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;
}
}

View File

@ -1,4 +1,6 @@
#include <fcntl.h> #include <fcntl.h>
#include <stdarg.h>
#include <stdint.h>
#include <sys/syscall.h> #include <sys/syscall.h>
#include <unistd.h> #include <unistd.h>
@ -8,4 +10,13 @@ extern "C"
{ {
return (int)syscall(SYS_open, pathname, flags); 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;
}
} }

View File

@ -30,14 +30,11 @@ extern "C"
return 0; // FIXME: Implement buffered IO. 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. int fd = open(pathname, O_RDWR); // FIXME: Use the mode string.
if (fd < 0) { return 0; } if (fd < 0) { return 0; }
FILE* stream = (FILE*)malloc(sizeof(FILE)); return fdopen(fd, mode);
stream->f_fd = fd;
clearerr(stream);
return stream;
} }
FILE* fdopen(int fd, const char*) FILE* fdopen(int fd, const char*)
@ -48,11 +45,17 @@ extern "C"
return 0; return 0;
} }
FILE* stream = (FILE*)malloc(sizeof(FILE)); FILE* stream = (FILE*)malloc(sizeof(FILE));
if (!stream) { return 0; }
stream->f_fd = fd; stream->f_fd = fd;
clearerr(stream); clearerr(stream);
return stream; return stream;
} }
int fileno(FILE* stream)
{
return stream->f_fd;
}
size_t fread(void* buf, size_t size, size_t nmemb, FILE* stream) size_t fread(void* buf, size_t size, size_t nmemb, FILE* stream)
{ {
ssize_t status = read(stream->f_fd, buf, size * nmemb); ssize_t status = read(stream->f_fd, buf, size * nmemb);

View File

@ -10,14 +10,41 @@ static void terminate_libc()
fclose(stderr); 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) static void check_for_file(int fd, FILE** target_stream, const char* path, const char* mode)
{ {
if (lseek(fd, 0, SEEK_CUR) < 0) if (lseek(fd, 0, SEEK_CUR) < 0)
{ {
if (errno == EBADF) *target_stream = fopen(path, mode); if (errno == EBADF) *target_stream = fopen(path, mode);
else else
exit(errno); exit(-127);
if (!*target_stream) exit(errno); if (!*target_stream) exit(-127);
errno = 0; errno = 0;
} }
else { *target_stream = fdopen(fd, mode); } 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() extern "C" void initialize_libc()
{ {
check_for_file(0, &stdout, "/dev/console", "rw"); check_for_file(STDOUT_FILENO, &stdout, "/dev/console", "rw");
check_for_file(1, &stderr, "/dev/console", "rw"); check_for_file(STDERR_FILENO, &stderr, "/dev/console", "rw");
initialize_random();
atexit(terminate_libc); atexit(terminate_libc);
} }

50
libs/libc/src/libgen.cpp Normal file
View 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;
}
}

View File

@ -7,17 +7,12 @@
extern "C" extern "C"
{ {
pid_t gettid()
{
return syscall(SYS_gettid);
}
unsigned int msleep(unsigned int ms) unsigned int msleep(unsigned int ms)
{ {
return (unsigned int)syscall(SYS_sleep, 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); fputs(message, stderr);
abort(); abort();

64
libs/libc/src/rand.cpp Normal file
View 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
View 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
View 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
View 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);
}
}

View File

@ -1,22 +1,59 @@
#include <ctype.h>
#include <luna.h> #include <luna.h>
#include <stdlib.h> #include <stdlib.h>
#include <sys/syscall.h> #include <sys/syscall.h>
#include <unistd.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*) char* getenv(const char*)
{ {
NOT_IMPLEMENTED("getenv"); NOT_IMPLEMENTED("getenv");
} }
__lc_noreturn void _Exit(int status)
{
syscall(SYS_exit, status);
__lc_unreachable();
}
} }

View File

@ -1,5 +1,5 @@
#include <errno.h> #include <errno.h>
#include <luna.h> #include <stdlib.h>
#include <string.h> #include <string.h>
extern "C" extern "C"
@ -16,6 +16,44 @@ extern "C"
return buf; 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) size_t strlen(const char* str)
{ {
const char* i = str; const char* i = str;
@ -24,17 +62,61 @@ extern "C"
return (i - str); 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) 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; 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 size_t len = strlen(src);
memcpy(dest, src, src_len > max ? max : src_len); if (size == 0) return len;
return dest; 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) char* strcat(char* dest, const char* src)
@ -68,48 +150,38 @@ extern "C"
return NULL; 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 const char* s = str + strlen(str);
size_t i; 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;
}
// find out if "m_start" is aligned on a SSE_XMM_SIZE boundary void* bzero(void* buf, size_t n)
if ((size_t)buf & (15)) {
return memset(buf, 0, n);
}
char* strstr(const char* haystack, const char* needle)
{
size_t needle_size = strlen(needle);
size_t haystack_size = strlen(haystack);
while (*haystack)
{ {
i = 0; if (*haystack == *needle)
// 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)); if (needle_size <= haystack_size)
i++; {
if (!strncmp(haystack, needle, needle_size)) return const_cast<char*>(haystack);
}
else { return NULL; }
} }
haystack++;
haystack_size--;
} }
else return NULL;
{
// if "m_start" was aligned, set our count to 0
i = 0;
}
// clear 64-byte chunks of memory (4 16-byte operations)
for (; i + 64 <= n; i += 64)
{
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));
}
// 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);
} }
#pragma GCC push_options #pragma GCC push_options
@ -129,8 +201,13 @@ extern "C"
case EISDIR: return "Is a directory"; case EISDIR: return "Is a directory";
case ENOEXEC: return "Exec format error"; case ENOEXEC: return "Exec format error";
case EFAULT: return "Bad address"; 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"; case 0: return "Success";
default: return (char*)(unsigned long int)err; default: return "Unknown error";
} }
} }

View File

@ -14,4 +14,9 @@ extern "C"
{ {
return (int)syscall(SYS_munmap, addr, len); return (int)syscall(SYS_munmap, addr, len);
} }
int mprotect(void* addr, size_t size, int prot)
{
return (int)syscall(SYS_mprotect, addr, size, prot);
}
} }

View 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
View File

@ -0,0 +1,11 @@
#include <sys/syscall.h>
#include <time.h>
#include <unistd.h>
extern "C"
{
clock_t clock()
{
return syscall(SYS_clock);
}
}

View File

@ -1,4 +1,5 @@
#include <bits/error.h> #include <bits/error.h>
#include <fcntl.h>
#include <luna.h> #include <luna.h>
#include <luna/syscall.h> #include <luna/syscall.h>
#include <stdarg.h> #include <stdarg.h>
@ -18,9 +19,15 @@ extern "C"
{ {
NOT_IMPLEMENTED("execvp"); NOT_IMPLEMENTED("execvp");
} }
pid_t fork(void) pid_t fork(void)
{ {
NOT_IMPLEMENTED("fork"); return syscall(SYS_fork);
}
pid_t getpid(void)
{
return syscall(SYS_getpid);
} }
long syscall(long number, ...) long syscall(long number, ...)
@ -31,12 +38,14 @@ extern "C"
va_start(ap, number); va_start(ap, number);
switch (number) switch (number)
{ {
case SYS_clock:
case SYS_yield: case SYS_yield:
case SYS_gettid: case SYS_fork:
case SYS_rand: result = __luna_syscall0(number); break; case SYS_getpid: result = __luna_syscall0(number); break;
case SYS_exit: case SYS_exit:
case SYS_close: case SYS_close:
case SYS_exec: case SYS_exec:
case SYS_mkdir:
case SYS_sleep: result = __luna_syscall1(number, va_arg(ap, arg)); break; case SYS_sleep: result = __luna_syscall1(number, va_arg(ap, arg)); break;
case SYS_munmap: case SYS_munmap:
case SYS_open: { case SYS_open: {
@ -45,9 +54,11 @@ extern "C"
result = __luna_syscall2(number, arg0, arg1); result = __luna_syscall2(number, arg0, arg1);
break; break;
} }
case SYS_fcntl:
case SYS_seek: case SYS_seek:
case SYS_write: case SYS_write:
case SYS_read: case SYS_read:
case SYS_mprotect:
case SYS_mmap: { case SYS_mmap: {
arg arg0 = va_arg(ap, arg); arg arg0 = va_arg(ap, arg);
arg arg1 = va_arg(ap, arg); arg arg1 = va_arg(ap, arg);
@ -96,9 +107,14 @@ extern "C"
return syscall(SYS_seek, fd, offset, whence); return syscall(SYS_seek, fd, offset, whence);
} }
noreturn void _exit(int status) __lc_noreturn void _exit(int status)
{ {
syscall(SYS_exit, status); syscall(SYS_exit, status);
__builtin_unreachable(); __lc_unreachable();
}
int dup(int fd)
{
return fcntl(fd, F_DUPFD, 0);
} }
} }

9
tests/Makefile Normal file
View 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
View 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
View 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
View 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