Compare commits

...

111 Commits

Author SHA1 Message Date
97461c7c1f Prepare for moon 0.11 2022-10-14 19:49:52 +02:00
ccf8f404a8 libc: Make the stdio initialization code cleaner 2022-10-14 19:36:20 +02:00
4e08c9d8ce libc: Pass the last argument in __luna_syscall5 correctly 2022-10-14 19:24:58 +02:00
0e64c57e10 libc: Change __{stderr, stdout} to {stderr, stdout} 2022-10-14 19:23:14 +02:00
3b83d7ccaf libc: Implement fsetpos() and fgetpos()
Not much to do, since these are kind of equivalent to fseek() and ftell().
2022-10-14 19:12:40 +02:00
e0aa552fae Kernel: Add a move() function
The standard C++ move() function.
2022-10-14 19:04:56 +02:00
f82fbbe60c Make address spaces not cloned by default 2022-10-14 18:23:04 +02:00
c8a92af4d2 Whoops! bugfix 2022-10-14 18:21:09 +02:00
e11280ad3f Merge branch 'address-spaces' 2022-10-14 18:20:42 +02:00
1c3377fc98 Prepare for cloning address spaces, not there yet 2022-10-14 18:17:57 +02:00
e43777bd31 Apparently, it just works now. 2022-10-14 18:00:33 +02:00
97a8a4a4a2 Solve rebase 2022-10-14 17:39:24 +02:00
81f56083c5 Almost there! 2022-10-14 17:38:29 +02:00
cdb73836b0 Some more multiple address space stuff
This page-faults. This is because the memory where the ELF should be is all zeroes, which the CPU tries to interpret.
2022-10-14 17:37:51 +02:00
bb7887a29d Add basic address space infrastructure 2022-10-14 17:34:29 +02:00
177282d79c Use the more appropriate size_t 2022-10-14 17:33:06 +02:00
5abd8814e3 Kernel: Continue moving utilities to a separate subdirectory 2022-10-14 17:33:06 +02:00
e21b608af4 Utilities: Start moving utilities into specific headers in a utils/ subdirectory 2022-10-14 17:31:47 +02:00
4f13be7741 Use the more appropriate size_t 2022-10-14 17:27:35 +02:00
1707739477 Kernel: Continue moving utilities to a separate subdirectory 2022-10-14 17:26:47 +02:00
13a5c0e445 Utilities: Start moving utilities into specific headers in a utils/ subdirectory 2022-10-14 17:21:16 +02:00
9b3c7816a3 Scheduler: pop the interrupt state when returning early from load_user_task() 2022-10-14 16:54:52 +02:00
26211bd49f It (almost) works now
The only thing doing weird stuff is exec(), so that's commented out and throws ENOSYS right now.

But we have two user tasks running in parallel, isolated from each other!
2022-10-14 16:46:00 +02:00
0c7c249935 exec is still doing some weird stuff, totally corrupting the page tables 2022-10-13 22:20:24 +02:00
5d41b4b113 Almost there...
exec() is not working yet. But the rest are!!
2022-10-13 22:13:04 +02:00
24272c57ef Almost there! 2022-10-13 21:55:51 +02:00
50d52c9a6b Scheduler: pop the interrupt state when returning early from load_user_task() 2022-10-13 21:23:51 +02:00
83982a24e2 add a comment 2022-10-13 21:21:02 +02:00
ee712432bd Some more multiple address space stuff
This page-faults. This is because the memory where the ELF should be is all zeroes, which the CPU tries to interpret.
2022-10-13 21:14:39 +02:00
229b06c63b Add basic address space infrastructure 2022-10-13 19:19:51 +02:00
522aa2f812 mmap, munmap: Add more checks 2022-10-13 18:50:12 +02:00
83e6b8cd21 VMM: Fix naming convention 2022-10-13 18:42:53 +02:00
57482e4e93 VMM: Make it even nicer 2022-10-13 18:15:52 +02:00
b360307f41 VMM: Make it so much gooder
There are still some fixes to be made, but I think this is already way cleaner than before.
2022-10-13 17:58:13 +02:00
9f2c9fb190 Kernel: Make Utilities be inline 2022-10-13 17:17:28 +02:00
531b2848ac init: Execute /bin/sym instead of /sys/config
We have now proven that exec() does fail and return to userspace when a file is not a valid executable.
We can now go back to executing a normal program.
2022-10-12 20:54:32 +02:00
b0e1b8a2b2 Missed some empty lines 2022-10-12 20:51:24 +02:00
2dd3a23092 Kernel: remove warnings when a standard IO syscall returns an error
That will probably happen a lot. We want userspace to tell us IF THE ERROR IS RELEVANT.
So, these unnecessary warnings are just noise.
Userspace may also use these functions to check for file descriptors.
For example, libc does this at program initialization, it checks whether fd 0 and 1 exist (by calling lseek() and seeing if it fails with errno=EBADF).
2022-10-12 20:50:21 +02:00
743aedcd49 libc: Implement atexit() and _exit()
exit() now calls registered handlers before calling _exit().

And initialize_libc() can now register a handler to close stdout and stderr on program termination!! :)
2022-10-12 20:41:55 +02:00
be9026442e libc: Check for file descriptors 0 and 1, and if they exist do not close and reopen them 2022-10-12 20:19:45 +02:00
de6041fede libc: Add fdopen() 2022-10-12 20:19:13 +02:00
52944ba5d8 Kernel/VMM: Add support for larger pages to getFlags() 2022-10-12 20:05:27 +02:00
69a9f7f06a Kernel: Move VMM from a class to a namespace
Also, rename the ugly Paging::VirtualMemoryManager name to just 'VMM'. Which is now used instead of kernelVMM.
2022-10-12 20:02:25 +02:00
5f8376409d Kernel, libc: Implement EFAULT 2022-10-12 19:25:35 +02:00
e37ff67da2 Make exec return an error if the loaded executable would use more memory than is currently available 2022-10-12 19:22:08 +02:00
9cddf9485d ELFLoader: Make check_elf_image return how much memory the executable will use on success 2022-10-12 19:20:14 +02:00
4091799701 Kernel, libc: Add ENOEXEC (Exec format error) 2022-10-12 19:15:44 +02:00
ea886f58a0 Update README.md 2022-10-12 19:07:28 +02:00
8a7ddfca80 exec: Use check_elf_image()
This allows exec to recover if an error should occur when loading the executable.

Thus, the calling process will be notified instead of killed.
2022-10-12 18:43:48 +02:00
1a54342454 Sanity check 2022-10-12 18:38:18 +02:00
d4c4c0177d compilation fix :) 2022-10-12 18:37:32 +02:00
3ac9fed23a ELFLoader: Add check_elf_image() and check_elf_image_from_filesystem()
These two functions validate an image, without actually loading it. Very useful for exec!
2022-10-12 18:37:00 +02:00
261fc73146 ELFLoader: Read the ELF file header by header using the VFS
Instead of just allocating one big redundant blob of memory and reading into it, then having to free it...
2022-10-12 18:23:52 +02:00
7a2e313a20 init: demo execv() 2022-10-12 18:04:27 +02:00
e9df5fd663 exec: Copy pathname into kernel memory, since the user memory where it resides is going to be freed 2022-10-12 18:04:20 +02:00
bcbf43e55c Kernel/std: Add strdup() 2022-10-12 18:03:54 +02:00
531afc3d6f libc: Add support for the new exec() system call
execv() is a temporary wrapper that ignores the second parameter, while execve() and execvp() still error out.
2022-10-12 17:45:58 +02:00
f8b3567042 Kernel: Add an exec() syscall
Very bare-bones for now. Doesn't support arguments or environment (we don't have that stuff right now), and the executable is not a valid ELF, it terminates the task.

But it's a start!
2022-10-12 17:42:01 +02:00
34c35163e4 libc: use the normal names that everybody uses for stdout and stderr 2022-10-12 17:15:57 +02:00
dfdc3b2d11 libc: close fds 0 and 1 before opening stdout and stderr 2022-10-12 17:14:49 +02:00
25a460e3c6 Scheduler: clear user tasks' registers 2022-10-12 17:12:06 +02:00
136c0b3ae9 Scheduler: add a reset_task function
This can be used later to implement execve()
2022-10-12 17:08:45 +02:00
4e3ef9593d Scheduler: Move ELF image freeing to ELFLoader 2022-10-12 17:08:17 +02:00
a6f0a7056f Scheduler: Set the user_task field in a Task at creation time
We were previously looking at its segment registers to see if they were user-like, but this method is bad.
What is the task was executing a system call?

So now, we store that value at creation time.
2022-10-12 17:07:39 +02:00
edda41a7bb libc: Implement fseek(), ftell() and rewind()
All three use the new syscall seek() (with its lseek() wrapper in unistd.h)!!
2022-10-12 15:56:03 +02:00
4a5db1dca7 libc: Add lseek() 2022-10-12 15:37:29 +02:00
928ade123c libc: Add support for the new seek() system call 2022-10-12 15:32:09 +02:00
e40304f2f1 libc: Add off_t to sys/types.h 2022-10-12 15:30:41 +02:00
854f585e1a Kernel: Add a seek() system call
Now, time for libc support!!
2022-10-12 15:28:52 +02:00
97b7572933 VFS: Implement a new type of Node, VFS_DEVICE
This is used to differentiate normal files from devices.
2022-10-12 15:22:34 +02:00
0f5910add7 Kernel/Utilities: Add new round_{up,down}_to_nearest_page functions 2022-10-12 14:51:04 +02:00
baa71b09cc Kernel: Build with -fstack-protector-strong instead of -fstack-protector-all
We lose a LITTLE bit of security, while allowing the compiler to optimize MUCH more.

Very simple functions, like most functions in misc/utils.cpp, were being made very big when some of them can just be "jmp thingy" or "and rax, something" and waste much less space.

This change makes more sense, I think.
2022-10-12 14:50:31 +02:00
4768d5fc12 ELFLoader: Consider the offset when calculating how many pages to map
If a section needs to be mapped at 0x50f50 and its size is 0x200, then that address space exceeds one page.
But since 0x200 is less than one page, we only map one page.

If we count the offset, 0xf50 + 0x200 need two pages. So we can map the right amount of memory.
2022-10-12 14:40:06 +02:00
bbd9f1d187 VMM: Add FIXME 2022-10-12 14:35:34 +02:00
15f340dbbe VMM: Do not map recursively 2022-10-12 14:34:12 +02:00
8daffa876c Make some shell scripts executable 2022-10-12 14:31:50 +02:00
28469497e9 Change build system to accept arbitrary CFLAGS during the build process
For the kernel, at least.
2022-10-12 14:31:41 +02:00
d3cb642e5f Scheduler: add FIXME 2022-10-12 14:30:57 +02:00
0ee9bd7290 Scheduler: free a task's ELF image.
Now that we have support for larger pages, this works!!
2022-10-12 14:30:46 +02:00
eaea4603c6 MemoryManager: Fix logging when built with debug logging
That is to say, -DMM_DEBUG
2022-10-12 14:29:30 +02:00
4021cb3ac0 KernelHeap: do not crash the entire system
Previously, calling free_virtual_page(s) would cause an assertion fail if the address was not in the kernel heap range.
Now, we just return.
2022-10-12 14:28:48 +02:00
ad9c7af0bf VMM: add FIXME 2022-10-12 14:27:47 +02:00
950f4ef608 VMM: Add support for larger pages
getPhysical() now stops at a larger page, unmap() can unmap a larger page, but map() just transforms it into a normal page.
getFlags() larger pages support is still pending.

At least now we don't page fault because we're trying to free a larger page.
2022-10-12 14:27:26 +02:00
525d567af6 VMM: When unmapping a page, invalidate the TLB for that page 2022-10-12 14:24:34 +02:00
c9ebe89899 Kernel/KernelHeap: Add MODULE #define 2022-10-12 14:04:41 +02:00
cf160d1260 Scheduler: Use misc/utils.h instead of doing everything manually.
That way the code is cleaner + we have one single point of failure.
2022-10-12 13:18:35 +02:00
66add380cf Kernel/Utilities: add a new get_top_of_stack convenience function 2022-10-12 13:17:58 +02:00
cf3f61e373 Kernel: More GDT refactoring 2022-10-12 13:12:46 +02:00
c1f9d3323f Kernel: Refactor TSS loading to make it cleaner >.< 2022-10-12 13:07:28 +02:00
97eacc027e Kernel: Use PAGE_SIZE in more places 2022-10-12 13:05:57 +02:00
f5deb1048a Kernel: Add functions to push and pop the interrupt state
This can be useful when you want to disable interrupts, but then only enable them back if they were previously enabled.
2022-10-12 12:58:56 +02:00
b3e16068ef Kernel: Add Utilities::get_rflags(), and thus Interrupts::are_enabled() 2022-10-12 12:56:55 +02:00
e90b90c556 Kernel, libc: Round up to nearest page-aligned size instead of down 2022-10-12 12:15:12 +02:00
a8eb7a6b66 Merge branch descriptive_headers into main
Reviewed-on: #11
2022-10-12 10:11:45 +00:00
d89685bb36 libc: Document sys/types.h 2022-10-12 12:07:42 +02:00
1e16a78106 libc: Document functions in sys/mman.h 2022-10-12 12:06:45 +02:00
a3362429d3 libc: Update strerror() 2022-10-12 12:01:18 +02:00
0d3e7d4463 libc: Document errno.h 2022-10-12 12:01:07 +02:00
b42c866db8 Correct description for fopen() 2022-10-12 11:58:07 +02:00
9f5b3b76d2 libc: Document the function in fcntl.h 2022-10-12 11:57:49 +02:00
f44411aa46 libc: Document the functions in luna.h 2022-10-12 11:54:29 +02:00
6aabe19fb4 libc: Document the functions in stdio.h 2022-10-12 11:51:32 +02:00
ffc223c2cf libc: Document functions in stdlib.h
Also, add prototypes for calloc() and realloc(), which were already implemented but not in the header.
2022-10-12 11:30:21 +02:00
c8c4d31cca Add a FIXME to string.cpp 2022-10-12 11:20:48 +02:00
19ee20b6f5 libc: Document the functions in string.h 2022-10-12 11:19:14 +02:00
38e87d8f8f libc: Document the functions in unistd.h 2022-10-12 11:02:18 +02:00
96b1d1c2f2 Devices: Tell a device which name you want it to be instantiated with
This allows us to create a device in any path, with any filename.
2022-10-12 10:54:46 +02:00
eb03ae91e0 Devices: Add a new Serial device
This device permits userspace to interface with the serial port.
2022-10-12 10:44:30 +02:00
89cbe5957b Update .gdbconf to point to the non-stripped kernel executable 2022-10-12 10:17:49 +02:00
44834b8a0f libc: Implement fputs, fputc, putc and putchar 2022-10-12 10:05:14 +02:00
eaf7a1620b Next version! 2022-10-11 21:42:23 +02:00
75 changed files with 1658 additions and 576 deletions

View File

@ -1,4 +1,4 @@
file initrd/boot/moon.elf file kernel/bin/moon.elf
break _start break _start
target remote :1234 target remote :1234
continue continue

View File

@ -6,7 +6,7 @@ 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/init/InitRD.cpp) from an [initial ramdisk](initrd/), no disk support yet. - Can [load files](kernel/src/fs/) from an [initial ramdisk](initrd/), no disk support 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 initial ramdisk as user tasks.
- [System call](kernel/src/sys/) interface and bare-bones [C Library](libs/libc/). - [System call](kernel/src/sys/) interface and bare-bones [C Library](libs/libc/).

View File

@ -1,4 +1,4 @@
APPS := init APPS := init sym
APPS_DIR := $(LUNA_ROOT)/apps APPS_DIR := $(LUNA_ROOT)/apps
APPS_SRC := $(APPS_DIR)/src APPS_SRC := $(APPS_DIR)/src

View File

@ -43,7 +43,25 @@ int main()
{ {
if (gettid() == 0) // why are we the idle task? if (gettid() == 0) // why are we the idle task?
{ {
__luna_abort("SHENANIGANS! init is tid 0 (which is reserved for the idle task)\n"); fprintf(stderr, "SHENANIGANS! init is tid 0 (which is reserved for the idle task)\n");
return 1;
}
FILE* serial = fopen("/dev/serial", "r");
if (!serial)
{
perror("fopen");
return 1;
}
if (fputs("Hello from init!\n", serial) < 0)
{
perror("fputs");
return 1;
}
if (fclose(serial) < 0)
{
perror("fclose");
return 1;
} }
printf("Welcome to Luna!\n"); printf("Welcome to Luna!\n");
@ -67,6 +85,23 @@ int main()
return 1; return 1;
} }
if (fseek(config, 0, SEEK_END) < 0)
{
perror("fseek");
return 1;
}
long offset = ftell(config);
if (offset < 0)
{
perror("ftell");
return 1;
}
printf("%s is %ld bytes long\n", filename, offset);
rewind(config);
char buf[4096]; char buf[4096];
size_t nread = fread(buf, sizeof(buf), 1, config); size_t nread = fread(buf, sizeof(buf), 1, config);
@ -82,7 +117,9 @@ int main()
if (fclose(config) < 0) { perror("fclose"); } if (fclose(config) < 0) { perror("fclose"); }
printf("\n\nPress any key to restart.\n"); sleep(2);
printf("\n\nPress any key to restart.\n\n");
return 0; return 0;
} }

38
apps/src/sym.c Normal file
View File

@ -0,0 +1,38 @@
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main()
{
sleep(6);
FILE* syms = fopen("/sys/moon.sym", "r");
if (!syms)
{
perror("fopen");
return 1;
}
char buf[1025];
if (fseek(syms, 8000, SEEK_SET) < 0)
{
perror("fseek");
return 1;
}
size_t nread = fread(buf, 1024, 1, syms);
if (ferror(syms))
{
perror("fread");
return 1;
}
buf[nread] = 0;
printf("%s\n", strchr(buf, '\n') + 1);
fclose(syms);
return 0;
}

View File

@ -3,7 +3,8 @@ MOON_SRC := $(MOON_DIR)/src
MOON_OBJ := $(MOON_DIR)/lib MOON_OBJ := $(MOON_DIR)/lib
MOON_BIN := $(MOON_DIR)/bin MOON_BIN := $(MOON_DIR)/bin
CFLAGS := -pedantic -Wall -Wextra -Werror -Wfloat-equal -Wdisabled-optimization -Wformat=2 -Winit-self -Wmissing-include-dirs -Wswitch-default -Wcast-qual -Wundef -Wcast-align -Wwrite-strings -Wlogical-op -Wredundant-decls -Wshadow -Wconversion -Os -ffreestanding -fstack-protector-all -fno-omit-frame-pointer -mno-red-zone -mno-mmx -mno-sse -mno-sse2 -fshort-wchar -mcmodel=kernel -I$(MOON_DIR)/include -isystem $(MOON_DIR)/include/std CFLAGS ?= -Os
CFLAGS := ${CFLAGS} -pedantic -Wall -Wextra -Werror -Wfloat-equal -Wdisabled-optimization -Wformat=2 -Winit-self -Wmissing-include-dirs -Wswitch-default -Wcast-qual -Wundef -Wcast-align -Wwrite-strings -Wlogical-op -Wredundant-decls -Wshadow -Wconversion -ffreestanding -fstack-protector-strong -fno-omit-frame-pointer -mno-red-zone -mno-mmx -mno-sse -mno-sse2 -fshort-wchar -mcmodel=kernel -I$(MOON_DIR)/include -isystem $(MOON_DIR)/include/std
CXXFLAGS := -fno-rtti -fno-exceptions -Wsign-promo -Wstrict-null-sentinel -Wctor-dtor-privacy CXXFLAGS := -fno-rtti -fno-exceptions -Wsign-promo -Wstrict-null-sentinel -Wctor-dtor-privacy
ASMFLAGS := -felf64 ASMFLAGS := -felf64
LDFLAGS := -T$(MOON_DIR)/moon.ld -nostdlib -lgcc -Wl,--build-id=none -z max-page-size=0x1000 -mno-red-zone -mcmodel=kernel LDFLAGS := -T$(MOON_DIR)/moon.ld -nostdlib -lgcc -Wl,--build-id=none -z max-page-size=0x1000 -mno-red-zone -mcmodel=kernel

View File

@ -2,8 +2,10 @@
#define EPERM 1 #define EPERM 1
#define ENOENT 2 #define ENOENT 2
#define ENOEXEC 8
#define EBADF 9 #define EBADF 9
#define ENOMEM 12 #define ENOMEM 12
#define EFAULT 14
#define EISDIR 21 #define EISDIR 21
#define EINVAL 22 #define EINVAL 22
#define EMFILE 24 #define EMFILE 24

View File

@ -29,6 +29,17 @@ struct Descriptor
void open(VFS::Node* node, bool can_read, bool can_write); void open(VFS::Node* node, bool can_read, bool can_write);
int seek(long offset);
long offset()
{
return (long)m_offset;
}
unsigned long length()
{
return m_node->length;
}
Descriptor(const Descriptor& other); Descriptor(const Descriptor& other);
Descriptor(); Descriptor();

View File

@ -6,6 +6,7 @@ typedef long ssize_t;
#define VFS_FILE 0x0 #define VFS_FILE 0x0
#define VFS_DIRECTORY 0x1 #define VFS_DIRECTORY 0x1
#define VFS_DEVICE 0x2
#define VFS_MOUNTPOINT 0x1 #define VFS_MOUNTPOINT 0x1

View File

@ -3,7 +3,7 @@
namespace ConsoleDevice namespace ConsoleDevice
{ {
VFS::Node* create_new(); VFS::Node* create_new(const char* devname);
ssize_t write(VFS::Node* node, size_t offset, size_t size, const char* buffer); ssize_t write(VFS::Node* node, size_t offset, size_t size, const char* buffer);
} }

View File

@ -0,0 +1,9 @@
#pragma once
#include "fs/VFS.h"
namespace SerialDevice
{
VFS::Node* create_new(const char* devname);
ssize_t write(VFS::Node* node, size_t offset, size_t size, const char* buffer);
}

View File

@ -3,7 +3,7 @@
namespace VersionDevice namespace VersionDevice
{ {
VFS::Node* create_new(); VFS::Node* create_new(const char* devname);
ssize_t read(VFS::Node* node, size_t offset, size_t size, char* buffer); ssize_t read(VFS::Node* node, size_t offset, size_t size, char* buffer);
} }

View File

@ -8,4 +8,10 @@ namespace Interrupts
bool is_in_handler(); bool is_in_handler();
void return_from_handler(Context* context); void return_from_handler(Context* context);
bool are_enabled();
bool were_enabled();
void push_and_disable();
void push_and_enable();
void pop();
} }

View File

@ -0,0 +1,27 @@
#pragma once
#include "memory/Paging.h"
struct AddressSpace
{
static AddressSpace create();
void destroy();
void detach();
AddressSpace clone();
PageTable* get_pml4()
{
return m_pml4;
}
bool is_cloned()
{
return m_cloned;
}
private:
PageTable* m_pml4;
bool m_cloned;
};

View File

@ -29,4 +29,6 @@ namespace MemoryManager
void release_page(void* page); void release_page(void* page);
void release_pages(void* pages, uint64_t count); void release_pages(void* pages, uint64_t count);
void protect(void* page, uint64_t count, int flags);
} }

View File

@ -5,27 +5,25 @@
#define PAGE_SIZE 4096 #define PAGE_SIZE 4096
#endif #endif
namespace Paging struct PageDirectoryEntry
{ {
struct PageDirectoryEntry bool present : 1;
{ bool read_write : 1;
bool Present : 1; bool user : 1;
bool ReadWrite : 1; bool write_through : 1;
bool UserSuper : 1; bool cache_disabled : 1;
bool WriteThrough : 1; bool accessed : 1;
bool CacheDisabled : 1; bool ignore0 : 1;
bool Accessed : 1; bool larger_pages : 1;
bool ignore0 : 1; bool ignore1 : 1;
bool LargerPages : 1; uint8_t available : 3;
bool ignore1 : 1; uint64_t address : 52;
uint8_t Available : 3;
uint64_t Address : 52;
void set_address(uint64_t addr); void set_address(uint64_t addr);
}; uint64_t get_address();
};
struct PageTable struct PageTable
{ {
PageDirectoryEntry entries[512]; PageDirectoryEntry entries[512];
} __attribute__((aligned(0x1000))); } __attribute__((aligned(PAGE_SIZE)));
}

View File

@ -1,29 +1,45 @@
#pragma once #pragma once
#include "memory/AddressSpace.h"
#include "memory/Paging.h" #include "memory/Paging.h"
namespace Paging enum Flags
{ {
enum Flags ReadWrite = 1 << 0,
{ User = 1 << 1,
ReadWrite = 1 << 0, Execute = 1 << 2
User = 1 << 1, };
Execute = 1 << 2 namespace VMM
}; {
class VirtualMemoryManager void init(); // Fetch page table from cr3
{
public:
void init(); // fetch page table from cr3
void init(PageTable* cr3);
void map(uint64_t virtualAddress, uint64_t physicalAddress, int flags); void switch_to_user_address_space(AddressSpace& space);
void remap(uint64_t virtualAddress, int flags); void switch_to_previous_user_address_space();
void unmap(uint64_t virtualAddress); void switch_back_to_kernel_address_space();
uint64_t getPhysical(uint64_t virtualAddress);
uint64_t getFlags(uint64_t virtualAddress);
private: void enter_syscall_context();
PageTable* PML4; void exit_syscall_context();
};
}
extern Paging::VirtualMemoryManager kernelVMM; void apply_address_space();
bool is_using_kernel_address_space();
void map(uint64_t vaddr, uint64_t paddr, int flags);
void remap(uint64_t vaddr, int flags);
void unmap(uint64_t vaddr);
uint64_t get_physical(uint64_t vaddr);
uint64_t get_flags(uint64_t vaddr);
PageDirectoryEntry* find_pde(PageTable* root, uint64_t vaddr);
PageDirectoryEntry* create_pde_if_not_exists(PageTable* root, uint64_t vaddr);
void propagate_read_write(PageTable* root, uint64_t vaddr);
void propagate_user(PageTable* root, uint64_t vaddr);
void flush_tlb(uint64_t addr);
void decompose_vaddr(uint64_t vaddr, uint64_t& page_index, uint64_t& pt_index, uint64_t& pd_index,
uint64_t& pdp_index);
uint64_t recompose_vaddr(uint64_t page_index, uint64_t pt_index, uint64_t pd_index, uint64_t pdp_index);
void install_kernel_page_directory_into_address_space(AddressSpace& space);
};

View File

@ -3,6 +3,8 @@
namespace Utilities namespace Utilities
{ {
uint64_t get_blocks_from_size(uint64_t blocksize, inline uint64_t get_blocks_from_size(uint64_t blocksize, uint64_t size)
uint64_t size); // Returns how many blocks of size blocksize does size occupy. {
return (size + (blocksize - 1)) / blocksize;
}
} }

View File

@ -17,3 +17,5 @@ void* memcpy(void* dest, const void* src, size_t n);
void* memset(void* dest, int c, size_t n); 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);

View File

@ -14,10 +14,14 @@
#define SYS_open 9 #define SYS_open 9
#define SYS_read 10 #define SYS_read 10
#define SYS_close 11 #define SYS_close 11
#define SYS_seek 12
#define SYS_exec 13
namespace Syscall namespace Syscall
{ {
void entry(Context* context); void entry(Context* context);
char* strdup_from_user(const char* user_string);
} }
void sys_exit(Context* context, int status); void sys_exit(Context* context, int status);
@ -32,3 +36,5 @@ 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_exec(Context* context, const char* pathname);

View File

@ -1,9 +1,13 @@
#pragma once #pragma once
#include "fs/VFS.h"
#include "sys/elf/Image.h" #include "sys/elf/Image.h"
#include <stdint.h> #include <stdint.h>
namespace ELFLoader namespace ELFLoader
{ {
ELFImage* load_elf_from_address(uintptr_t addr); ELFImage* load_elf_from_vfs(VFS::Node* node); // This function assumes check_elf_image has been called first.
ELFImage* load_elf_from_filesystem(const char* filename); ELFImage* load_elf_from_filesystem(const char* filename);
void release_elf_image(ELFImage* image);
long check_elf_image(VFS::Node* node);
long check_elf_image_from_filesystem(const char* filename);
} }

View File

@ -1,6 +1,8 @@
#pragma once #pragma once
#include "thread/Task.h" #include "thread/Task.h"
#define TASK_PAGES_IN_STACK 4
namespace Scheduler namespace Scheduler
{ {
void init(); void init();
@ -8,7 +10,8 @@ namespace Scheduler
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(void (*task)(void));
void add_user_task(void* task);
Task* create_user_task();
void load_user_task(const char* filename); void load_user_task(const char* filename);
@ -22,4 +25,6 @@ namespace Scheduler
void reap_task(Task* task); void reap_task(Task* task);
void reap_tasks(); void reap_tasks();
void reset_task(Task* task, ELFImage* new_image);
} }

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "fs/FileDescriptor.h" #include "fs/FileDescriptor.h"
#include "interrupts/Context.h" #include "interrupts/Context.h"
#include "memory/AddressSpace.h"
#include "sys/elf/Image.h" #include "sys/elf/Image.h"
#define TASK_MAX_FDS 8 #define TASK_MAX_FDS 8
@ -36,11 +37,15 @@ struct Task
char floating_region[512] __attribute__((aligned(16))); char floating_region[512] __attribute__((aligned(16)));
bool floating_saved = false; bool floating_saved = false;
bool user_task = true;
bool is_user_task(); bool is_user_task();
ELFImage* image = nullptr; ELFImage* image = nullptr;
Descriptor files[TASK_MAX_FDS]; Descriptor files[TASK_MAX_FDS];
AddressSpace address_space;
}; };
void set_context_from_task(Task& task, Context* ctx); void set_context_from_task(Task& task, Context* ctx);

View File

@ -0,0 +1,24 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
#ifndef PAGE_SIZE
#define PAGE_SIZE 4096
#endif
inline uintptr_t get_top_of_stack(uintptr_t bottom, size_t stack_pages)
{
return bottom + (stack_pages * PAGE_SIZE) - sizeof(uintptr_t);
}
inline uintptr_t round_down_to_nearest_page(uintptr_t addr)
{
return addr - (addr % PAGE_SIZE);
}
inline uintptr_t round_up_to_nearest_page(uintptr_t addr)
{
if (addr % PAGE_SIZE) return addr + (PAGE_SIZE - (addr % PAGE_SIZE));
return addr;
}

View File

@ -0,0 +1,52 @@
#pragma once
#include <stdint.h>
extern "C" uintptr_t asm_get_rflags();
extern "C" void asm_set_rflags(uintptr_t);
inline uintptr_t read_rflags()
{
return asm_get_rflags();
}
inline void write_rflags(uintptr_t value)
{
asm_set_rflags(value);
}
inline uintptr_t read_cr0()
{
uintptr_t value;
asm volatile("mov %%cr0, %0" : "=r"(value));
return value;
}
inline uintptr_t read_cr3()
{
uintptr_t value;
asm volatile("mov %%cr3, %0" : "=r"(value));
return value;
}
inline uintptr_t read_cr4()
{
uintptr_t value;
asm volatile("mov %%cr4, %0" : "=r"(value));
return value;
}
template <typename T> inline void write_cr0(T value)
{
asm volatile("mov %0, %%cr0" : : "r"(value));
}
template <typename T> inline void write_cr3(T value)
{
asm volatile("mov %0, %%cr3" : : "r"(value));
}
template <typename T> inline void write_cr4(T value)
{
asm volatile("mov %0, %%cr4" : : "r"(value));
}

View File

@ -0,0 +1,6 @@
#pragma once
template <typename T> inline T&& move(T& lvalue)
{
return (T &&) lvalue;
}

View File

@ -1,4 +1,5 @@
#include "fs/FileDescriptor.h" #include "fs/FileDescriptor.h"
#include "errno.h"
Descriptor::Descriptor() : m_is_open(false) Descriptor::Descriptor() : m_is_open(false)
{ {
@ -32,3 +33,11 @@ ssize_t Descriptor::write(size_t size, const char* buffer)
m_offset += result; m_offset += result;
return result; return result;
} }
int Descriptor::seek(long offset)
{
if (m_node->type != VFS_DEVICE && (uint64_t)offset > m_node->length)
return -EINVAL; // FIXME: Support seeking beyond the current file's length.
m_offset = (uint64_t)offset;
return 0;
}

View File

@ -5,15 +5,15 @@
#include "std/stdlib.h" #include "std/stdlib.h"
#include "std/string.h" #include "std/string.h"
VFS::Node* ConsoleDevice::create_new() VFS::Node* ConsoleDevice::create_new(const char* devname)
{ {
VFS::Node* dev = new VFS::Node; VFS::Node* dev = new VFS::Node;
dev->write_func = ConsoleDevice::write; dev->write_func = ConsoleDevice::write;
dev->inode = 0; dev->inode = 0;
dev->length = 0; dev->length = 0;
dev->type = VFS_FILE; dev->type = VFS_DEVICE;
dev->flags = 0; dev->flags = 0;
strncpy(dev->name, "console", sizeof(dev->name)); strncpy(dev->name, devname, sizeof(dev->name));
return dev; return dev;
} }

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/Serial.h"
#include "fs/devices/Version.h" #include "fs/devices/Version.h"
#include "std/stdlib.h" #include "std/stdlib.h"
#include "std/string.h" #include "std/string.h"
@ -21,8 +22,9 @@ VFS::Node* DeviceFS::get()
devfs_root->find_func = DeviceFS::finddir; devfs_root->find_func = DeviceFS::finddir;
strncpy(devfs_root->name, "dev", sizeof(devfs_root->name)); strncpy(devfs_root->name, "dev", sizeof(devfs_root->name));
devfs_files[devfs_file_count++] = VersionDevice::create_new(); devfs_files[devfs_file_count++] = VersionDevice::create_new("version");
devfs_files[devfs_file_count++] = ConsoleDevice::create_new(); devfs_files[devfs_file_count++] = ConsoleDevice::create_new("console");
devfs_files[devfs_file_count++] = SerialDevice::create_new("serial");
return devfs_root; return devfs_root;
} }

View File

@ -0,0 +1,25 @@
#include "io/Serial.h"
#include "config.h"
#include "fs/devices/Serial.h"
#include "std/stdio.h"
#include "std/stdlib.h"
#include "std/string.h"
VFS::Node* SerialDevice::create_new(const char* devname)
{
VFS::Node* dev = new VFS::Node;
dev->write_func = SerialDevice::write;
dev->inode = 0;
dev->length = 0;
dev->type = VFS_DEVICE;
dev->flags = 0;
strncpy(dev->name, devname, sizeof(dev->name));
return dev;
}
ssize_t SerialDevice::write(VFS::Node* node, size_t, size_t size, const char* buffer)
{
if (!node) return -1;
Serial::write(buffer, size);
return (ssize_t)size;
}

View File

@ -4,15 +4,15 @@
#include "std/stdlib.h" #include "std/stdlib.h"
#include "std/string.h" #include "std/string.h"
VFS::Node* VersionDevice::create_new() VFS::Node* VersionDevice::create_new(const char* devname)
{ {
VFS::Node* dev = new VFS::Node; VFS::Node* dev = new VFS::Node;
dev->read_func = VersionDevice::read; dev->read_func = VersionDevice::read;
dev->inode = 0; dev->inode = 0;
dev->length = strlen(moon_version()) + 5; dev->length = strlen(moon_version()) + 5;
dev->type = VFS_FILE; dev->type = VFS_DEVICE;
dev->flags = 0; dev->flags = 0;
strncpy(dev->name, "version", sizeof(dev->name)); strncpy(dev->name, devname, sizeof(dev->name));
return dev; return dev;
} }

View File

@ -49,15 +49,15 @@ struct InternalGDT
GDTEntry user_data; GDTEntry user_data;
GDTEntry tss; GDTEntry tss;
HighGDTEntry tss2; HighGDTEntry tss2;
} __attribute__((packed)) __attribute((aligned(0x1000))); } __attribute__((packed)) __attribute((aligned(PAGE_SIZE)));
__attribute__((aligned(0x1000))) static InternalGDT internal_gdt = {{0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00}, __attribute__((aligned(PAGE_SIZE))) static InternalGDT internal_gdt = {{0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00},
{0xffff, 0x0000, 0x00, 0x9a, 0xaf, 0x00}, {0xffff, 0x0000, 0x00, 0x9a, 0xaf, 0x00},
{0xffff, 0x0000, 0x00, 0x92, 0xcf, 0x00}, {0xffff, 0x0000, 0x00, 0x92, 0xcf, 0x00},
{0xffff, 0x0000, 0x00, 0xfa, 0xaf, 0x00}, {0xffff, 0x0000, 0x00, 0xfa, 0xaf, 0x00},
{0xffff, 0x0000, 0x00, 0xf2, 0xcf, 0x00}, {0xffff, 0x0000, 0x00, 0xf2, 0xcf, 0x00},
{0x0000, 0x0000, 0x00, 0xe9, 0x0f, 0x00}, {0x0000, 0x0000, 0x00, 0xe9, 0x0f, 0x00},
{0x00000000, 0x00000000}}; {0x00000000, 0x00000000}};
static TSS main_tss; static TSS main_tss;
@ -78,20 +78,35 @@ static void set_limit(GDTEntry* entry, uint32_t limit)
entry->limit1_flags = (entry->limit1_flags & 0xF0) | ((limit >> 16) & 0xF); entry->limit1_flags = (entry->limit1_flags & 0xF0) | ((limit >> 16) & 0xF);
} }
static void set_tss_base(GDTEntry* tss1, HighGDTEntry* tss2, uint64_t addr)
{
set_base(tss1, addr & 0xffffffff);
tss2->base_high = (uint32_t)(addr >> 32);
}
static void setup_tss()
{
memset(&main_tss, 0, sizeof(TSS));
main_tss.rsp[0] =
(uint64_t)MemoryManager::get_pages(4) + (PAGE_SIZE * 4) - 8; // allocate 16KB for the syscall stack
main_tss.iomap_base = sizeof(TSS);
set_tss_base(&internal_gdt.tss, &internal_gdt.tss2, (uint64_t)&main_tss);
set_limit(&internal_gdt.tss, sizeof(TSS) - 1);
}
static void load_tss()
{
kdbgln("Loading TR (GDT entry 0x2b)");
load_tr(0x2b);
}
void GDT::load() void GDT::load()
{ {
static GDTR gdtr; static GDTR gdtr;
gdtr.offset = (uint64_t)&internal_gdt; gdtr.offset = (uint64_t)&internal_gdt;
gdtr.size = sizeof(InternalGDT); gdtr.size = sizeof(InternalGDT);
memset(&main_tss, 0, sizeof(TSS)); setup_tss();
main_tss.rsp[0] =
(uint64_t)MemoryManager::get_pages(4) + (PAGE_SIZE * 4) - 8; // allocate 16KB for the syscall stack
main_tss.iomap_base = sizeof(TSS);
set_base(&internal_gdt.tss, (uint64_t)&main_tss & 0xffffffff);
internal_gdt.tss2.base_high = (uint32_t)(((uint64_t)&main_tss >> 32) & 0xffffffff);
set_limit(&internal_gdt.tss, sizeof(TSS) - 1);
kdbgln("Loading GDT at offset %lx, size %d", gdtr.offset, gdtr.size); kdbgln("Loading GDT at offset %lx, size %d", gdtr.offset, gdtr.size);
load_gdt(&gdtr); load_gdt(&gdtr);
kdbgln("Loading TR (GDT entry 0x2b)"); load_tss();
load_tr(0x2b);
} }

View File

@ -51,6 +51,8 @@ extern "C" void common_handler(Context* context)
StackTracer tracer(context->rbp); StackTracer tracer(context->rbp);
tracer.trace_with_ip(context->rip); tracer.trace_with_ip(context->rip);
hang(); // FIXME: Remove this when multiple address spaces are working.
Scheduler::task_misbehave(context, -3); Scheduler::task_misbehave(context, -3);
} }
} }

View File

@ -1,5 +1,6 @@
#include "interrupts/Interrupts.h" #include "interrupts/Interrupts.h"
#include "trace/StackTracer.h" #include "trace/StackTracer.h"
#include "utils/Registers.h"
void Interrupts::disable() void Interrupts::disable()
{ {
@ -25,3 +26,34 @@ void Interrupts::return_from_handler(Context* context)
: :
: "r"(context)); : "r"(context));
} }
bool Interrupts::are_enabled()
{
return (read_rflags() & 0x200) > 0;
}
static bool saved_interrupt_state;
void Interrupts::push_and_disable()
{
saved_interrupt_state = are_enabled();
disable();
}
void Interrupts::push_and_enable()
{
saved_interrupt_state = are_enabled();
enable();
}
void Interrupts::pop()
{
if (saved_interrupt_state && !are_enabled()) enable();
else if (!saved_interrupt_state && are_enabled())
disable();
}
bool Interrupts::were_enabled()
{
return saved_interrupt_state;
}

View File

@ -26,3 +26,15 @@ asm_enable_sse:
or ax, 3 << 9 or ax, 3 << 9
mov cr4, rax mov cr4, rax
ret ret
global asm_get_rflags
asm_get_rflags:
pushfq
pop rax
ret
global asm_set_rflags
asm_set_rflags:
push rdi
popfq
ret

View File

@ -16,6 +16,7 @@
#include "io/PIC.h" #include "io/PIC.h"
#include "io/Serial.h" #include "io/Serial.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/MemoryManager.h"
#include "memory/MemoryMap.h" #include "memory/MemoryMap.h"
@ -56,12 +57,6 @@ extern "C" void _start()
kinfoln("Loaded IDT"); kinfoln("Loaded IDT");
PIC::remap();
PIC::enable_master(0b11111100); // enable keyboard and PIT
PIC::enable_slave(0b11111111);
kinfoln("Prepared PIC");
PIT::initialize(1000); // 1000 times per second PIT::initialize(1000); // 1000 times per second
kinfoln("Prepared PIT"); kinfoln("Prepared PIT");
@ -87,7 +82,9 @@ extern "C" void _start()
Init::finish_kernel_boot(); Init::finish_kernel_boot();
Interrupts::enable(); // Task switching commences here PIC::remap();
PIC::enable_master(0b11111100); // enable keyboard and PIT
PIC::enable_slave(0b11111111);
kinfoln("Interrupts enabled"); kinfoln("Interrupts enabled");

View File

@ -0,0 +1,95 @@
#define MODULE "vmm"
#include "memory/AddressSpace.h"
#include "log/Log.h"
#include "memory/PMM.h"
#include "memory/VMM.h"
#include "utils/move.h"
AddressSpace AddressSpace::create()
{
AddressSpace result;
result.m_pml4 = (PageTable*)PMM::request_page();
result.m_cloned = false;
VMM::install_kernel_page_directory_into_address_space(result);
return move(result);
}
void AddressSpace::destroy()
{
if (m_cloned)
{
kdbgln("Will not destroy a cloned address space, I don't own it");
return;
}
uint64_t pages_freed = 0;
for (int i = 0; i < 512; i++)
{
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);
}
pages_freed++;
PMM::free_page(m_pml4);
kdbgln("Reclaimed %ld pages from address space!", pages_freed);
}
void AddressSpace::detach()
{
if (!m_cloned) return;
m_pml4 = (PageTable*)PMM::request_page();
VMM::install_kernel_page_directory_into_address_space(*this);
m_cloned = false;
}
AddressSpace AddressSpace::clone()
{
AddressSpace result;
result.m_pml4 = m_pml4;
result.m_cloned = true;
return result;
}

View File

@ -1,3 +1,5 @@
#define MODULE "kheap"
#include "memory/KernelHeap.h" #include "memory/KernelHeap.h"
#include "assert.h" #include "assert.h"
@ -68,7 +70,7 @@ uint64_t KernelHeap::request_virtual_pages(uint64_t count)
void KernelHeap::free_virtual_page(uint64_t address) void KernelHeap::free_virtual_page(uint64_t address)
{ {
ASSERT(address >= ALLOC_BASE && address < ALLOC_END); if (address < ALLOC_BASE || address >= ALLOC_END) return;
uint64_t index = (address - ALLOC_BASE) / PAGE_SIZE; uint64_t index = (address - ALLOC_BASE) / PAGE_SIZE;
bitmap_set(index, false); bitmap_set(index, false);
if (start_index > index) start_index = index; if (start_index > index) start_index = index;
@ -76,7 +78,7 @@ void KernelHeap::free_virtual_page(uint64_t address)
void KernelHeap::free_virtual_pages(uint64_t address, uint64_t count) void KernelHeap::free_virtual_pages(uint64_t address, uint64_t count)
{ {
ASSERT(address >= ALLOC_BASE && address < ALLOC_END); if (address < ALLOC_BASE || address >= ALLOC_END) return;
uint64_t index = (address - ALLOC_BASE) / PAGE_SIZE; uint64_t index = (address - ALLOC_BASE) / PAGE_SIZE;
for (uint64_t i = 0; i < count; i++) { bitmap_set(index + i, false); } for (uint64_t i = 0; i < count; i++) { bitmap_set(index + i, false); }
if (start_index > index) start_index = index; if (start_index > index) start_index = index;

View File

@ -45,7 +45,7 @@ uint64_t Memory::get_usable()
bool Memory::is_kernel_address(uintptr_t address) bool Memory::is_kernel_address(uintptr_t address)
{ {
return address > 0xfffffffff8000000; return address >= 0xfffffffff8000000;
} }
bool Memory::is_user_address(uintptr_t address) bool Memory::is_user_address(uintptr_t address)

View File

@ -12,7 +12,7 @@
void MemoryManager::init() void MemoryManager::init()
{ {
PMM::init(); PMM::init();
kernelVMM.init(); VMM::init();
PMM::map_bitmap_to_virtual(); PMM::map_bitmap_to_virtual();
} }
@ -26,7 +26,7 @@ void* MemoryManager::get_mapping(void* physicalAddress, int flags)
#endif #endif
return 0; return 0;
} }
kernelVMM.map(virtualAddress, (uint64_t)physicalAddress, flags); VMM::map(virtualAddress, (uint64_t)physicalAddress, flags);
return (void*)virtualAddress; return (void*)virtualAddress;
} }
@ -41,7 +41,7 @@ void* MemoryManager::get_unaligned_mapping(void* physicalAddress, int flags)
#endif #endif
return 0; return 0;
} }
kernelVMM.map(virtualAddress, (uint64_t)physicalAddress - offset, flags); VMM::map(virtualAddress, (uint64_t)physicalAddress - offset, flags);
return (void*)(virtualAddress + offset); return (void*)(virtualAddress + offset);
} }
@ -62,7 +62,7 @@ void* MemoryManager::get_unaligned_mappings(void* physicalAddress, uint64_t coun
} }
for (uint64_t i = 0; i < count; i++) for (uint64_t i = 0; i < count; i++)
{ {
kernelVMM.map(virtualAddress + (i * PAGE_SIZE), ((uint64_t)physicalAddress - offset) + (i * PAGE_SIZE), flags); VMM::map(virtualAddress + (i * PAGE_SIZE), ((uint64_t)physicalAddress - offset) + (i * PAGE_SIZE), flags);
} }
return (void*)(virtualAddress + offset); return (void*)(virtualAddress + offset);
} }
@ -70,7 +70,7 @@ void* MemoryManager::get_unaligned_mappings(void* physicalAddress, uint64_t coun
void MemoryManager::release_unaligned_mapping(void* mapping) void MemoryManager::release_unaligned_mapping(void* mapping)
{ {
uint64_t offset = (uint64_t)mapping % PAGE_SIZE; uint64_t offset = (uint64_t)mapping % PAGE_SIZE;
kernelVMM.unmap((uint64_t)mapping - offset); VMM::unmap((uint64_t)mapping - offset);
KernelHeap::free_virtual_page((uint64_t)mapping - offset); KernelHeap::free_virtual_page((uint64_t)mapping - offset);
} }
@ -80,12 +80,12 @@ void MemoryManager::release_unaligned_mappings(void* mapping, uint64_t count)
if (count == 1) return release_unaligned_mapping(mapping); if (count == 1) return release_unaligned_mapping(mapping);
uint64_t offset = (uint64_t)mapping % PAGE_SIZE; uint64_t offset = (uint64_t)mapping % PAGE_SIZE;
KernelHeap::free_virtual_pages((uint64_t)mapping - offset, count); KernelHeap::free_virtual_pages((uint64_t)mapping - offset, count);
for (uint64_t i = 0; i < count; i++) { kernelVMM.unmap(((uint64_t)mapping - offset) + (i * PAGE_SIZE)); } for (uint64_t i = 0; i < count; i++) { VMM::unmap(((uint64_t)mapping - offset) + (i * PAGE_SIZE)); }
} }
void MemoryManager::release_mapping(void* mapping) void MemoryManager::release_mapping(void* mapping)
{ {
kernelVMM.unmap((uint64_t)mapping); VMM::unmap((uint64_t)mapping);
KernelHeap::free_virtual_page((uint64_t)mapping); KernelHeap::free_virtual_page((uint64_t)mapping);
} }
@ -112,15 +112,15 @@ void* MemoryManager::get_page_at(uint64_t addr, int flags)
#endif #endif
return 0; return 0;
} }
kernelVMM.map(addr, (uint64_t)physicalAddress, flags); VMM::map(addr, (uint64_t)physicalAddress, flags);
return (void*)addr; return (void*)addr;
} }
void MemoryManager::release_page(void* page) void MemoryManager::release_page(void* page)
{ {
uint64_t physicalAddress = kernelVMM.getPhysical((uint64_t)page); uint64_t physicalAddress = VMM::get_physical((uint64_t)page);
ASSERT(physicalAddress != UINT64_MAX); // this address is not mapped in the virtual address space... ASSERT(physicalAddress != UINT64_MAX); // this address is not mapped in the virtual address space...
kernelVMM.unmap((uint64_t)page); VMM::unmap((uint64_t)page);
PMM::free_page((void*)physicalAddress); PMM::free_page((void*)physicalAddress);
} }
@ -158,13 +158,13 @@ void* MemoryManager::get_pages_at(uint64_t addr, uint64_t count, int flags)
// smaller range, so this might not be fatal. // smaller range, so this might not be fatal.
{ {
#ifdef MM_DEBUG #ifdef MM_DEBUG
kwarnln("OOM while allocating page %ld of memory. this might be recoverable..."); kwarnln("OOM while allocating page %ld of memory. this might be recoverable...", i);
#endif #endif
return 0; return 0;
} }
kernelVMM.map(addr + (i * PAGE_SIZE), (uint64_t)physicalAddress, flags); VMM::map(addr + (i * PAGE_SIZE), (uint64_t)physicalAddress, flags);
#ifdef MM_DEBUG #ifdef MM_DEBUG
kdbgln("allocating virtual %lx, physical %p", virtualAddress + (i * PAGE_SIZE), physicalAddress); kdbgln("allocating virtual %lx, physical %p", addr + (i * PAGE_SIZE), physicalAddress);
#endif #endif
} }
return (void*)addr; return (void*)addr;
@ -180,9 +180,9 @@ void MemoryManager::release_pages(void* pages, uint64_t count)
for (uint64_t i = 0; i < count; i++) for (uint64_t i = 0; i < count; i++)
{ {
void* page = (void*)((uint64_t)pages + (i * PAGE_SIZE)); void* page = (void*)((uint64_t)pages + (i * PAGE_SIZE));
uint64_t physicalAddress = kernelVMM.getPhysical((uint64_t)page); uint64_t physicalAddress = VMM::get_physical((uint64_t)page);
ASSERT(physicalAddress != UINT64_MAX); ASSERT(physicalAddress != UINT64_MAX);
kernelVMM.unmap((uint64_t)page); VMM::unmap((uint64_t)page);
#ifdef MM_DEBUG #ifdef MM_DEBUG
kdbgln("releasing virtual %p, physical %lx", page, physicalAddress); kdbgln("releasing virtual %p, physical %lx", page, physicalAddress);
#endif #endif
@ -190,3 +190,8 @@ void MemoryManager::release_pages(void* pages, uint64_t count)
} }
KernelHeap::free_virtual_pages((uint64_t)pages, count); KernelHeap::free_virtual_pages((uint64_t)pages, count);
} }
void MemoryManager::protect(void* page, uint64_t count, int flags)
{
for (uint64_t i = 0; i < count; i++) { VMM::remap((uint64_t)page + (i * PAGE_SIZE), flags); }
}

View File

@ -3,9 +3,14 @@
#pragma GCC push_options #pragma GCC push_options
#pragma GCC diagnostic ignored "-Wconversion" #pragma GCC diagnostic ignored "-Wconversion"
void Paging::PageDirectoryEntry::set_address(uint64_t addr) void PageDirectoryEntry::set_address(uint64_t addr)
{ {
this->Address = (addr >> 12); this->address = (addr >> 12);
}
uint64_t PageDirectoryEntry::get_address()
{
return (uint64_t)this->address << 12;
} }
#pragma GCC pop_options #pragma GCC pop_options

View File

@ -1,228 +1,291 @@
#define MODULE "vmm"
#include "memory/VMM.h" #include "memory/VMM.h"
#include "assert.h" #include "assert.h"
#include "log/Log.h"
#include "memory/PMM.h" #include "memory/PMM.h"
#include "misc/utils.h"
#include "std/string.h" #include "std/string.h"
#include "utils/Addresses.h"
#include "utils/Registers.h"
Paging::VirtualMemoryManager kernelVMM; static PageTable* kernel_pml4;
static PageTable* current_pml4;
static AddressSpace* user_address_space;
namespace Paging void VMM::switch_back_to_kernel_address_space()
{ {
void VirtualMemoryManager::init() if (current_pml4 != kernel_pml4) { current_pml4 = kernel_pml4; }
}
void VMM::switch_to_user_address_space(AddressSpace& space)
{
user_address_space = &space;
current_pml4 = user_address_space->get_pml4();
}
void VMM::switch_to_previous_user_address_space()
{
current_pml4 = user_address_space->get_pml4();
}
void VMM::enter_syscall_context()
{
if (current_pml4 != kernel_pml4)
{ {
asm volatile("mov %%cr3, %0" : "=r"(PML4)); current_pml4 = kernel_pml4;
} apply_address_space();
switch_to_previous_user_address_space();
void VirtualMemoryManager::init(PageTable* cr3)
{
PML4 = cr3;
}
void VirtualMemoryManager::unmap(uint64_t virtualAddress)
{
virtualAddress >>= 12;
uint64_t P_i = virtualAddress & 0x1ff;
virtualAddress >>= 9;
uint64_t PT_i = virtualAddress & 0x1ff;
virtualAddress >>= 9;
uint64_t PD_i = virtualAddress & 0x1ff;
virtualAddress >>= 9;
uint64_t PDP_i = virtualAddress & 0x1ff;
PageDirectoryEntry PDE;
PDE = PML4->entries[PDP_i];
PageTable* PDP;
if (!PDE.Present)
{
return; // Already unmapped
}
else { PDP = (PageTable*)((uint64_t)PDE.Address << 12); }
PDE = PDP->entries[PD_i];
PageTable* PD;
if (!PDE.Present)
{
return; // Already unmapped
}
else { PD = (PageTable*)((uint64_t)PDE.Address << 12); }
PDE = PD->entries[PT_i];
PageTable* PT;
if (!PDE.Present)
{
return; // Already unmapped
}
else { PT = (PageTable*)((uint64_t)PDE.Address << 12); }
PDE = PT->entries[P_i];
PDE.Present = false;
PT->entries[P_i] = PDE;
}
uint64_t VirtualMemoryManager::getPhysical(uint64_t virtualAddress)
{
virtualAddress >>= 12;
uint64_t P_i = virtualAddress & 0x1ff;
virtualAddress >>= 9;
uint64_t PT_i = virtualAddress & 0x1ff;
virtualAddress >>= 9;
uint64_t PD_i = virtualAddress & 0x1ff;
virtualAddress >>= 9;
uint64_t PDP_i = virtualAddress & 0x1ff;
PageDirectoryEntry PDE;
PDE = PML4->entries[PDP_i];
PageTable* PDP;
if (!PDE.Present)
{
return UINT64_MAX; // Not mapped
}
else { PDP = (PageTable*)((uint64_t)PDE.Address << 12); }
PDE = PDP->entries[PD_i];
PageTable* PD;
if (!PDE.Present)
{
return UINT64_MAX; // Not mapped
}
else { PD = (PageTable*)((uint64_t)PDE.Address << 12); }
PDE = PD->entries[PT_i];
PageTable* PT;
if (!PDE.Present)
{
return UINT64_MAX; // Not mapped
}
else { PT = (PageTable*)((uint64_t)PDE.Address << 12); }
PDE = PT->entries[P_i];
return PDE.Address << 12;
}
uint64_t VirtualMemoryManager::getFlags(uint64_t virtualAddress)
{
virtualAddress >>= 12;
uint64_t P_i = virtualAddress & 0x1ff;
virtualAddress >>= 9;
uint64_t PT_i = virtualAddress & 0x1ff;
virtualAddress >>= 9;
uint64_t PD_i = virtualAddress & 0x1ff;
virtualAddress >>= 9;
uint64_t PDP_i = virtualAddress & 0x1ff;
PageDirectoryEntry PDE;
PDE = PML4->entries[PDP_i];
PageTable* PDP;
if (!PDE.Present)
{
return 0; // Not mapped
}
else { PDP = (PageTable*)((uint64_t)PDE.Address << 12); }
PDE = PDP->entries[PD_i];
PageTable* PD;
if (!PDE.Present)
{
return 0; // Not mapped
}
else { PD = (PageTable*)((uint64_t)PDE.Address << 12); }
PDE = PD->entries[PT_i];
PageTable* PT;
if (!PDE.Present)
{
return 0; // Not mapped
}
else { PT = (PageTable*)((uint64_t)PDE.Address << 12); }
uint64_t flags = 0;
PDE = PT->entries[P_i];
if (PDE.UserSuper) flags |= User;
if (PDE.ReadWrite) flags |= ReadWrite;
return flags;
}
void VirtualMemoryManager::map(uint64_t virtualAddress, uint64_t physicalAddress, int flags)
{
virtualAddress >>= 12;
uint64_t P_i = virtualAddress & 0x1ff;
virtualAddress >>= 9;
uint64_t PT_i = virtualAddress & 0x1ff;
virtualAddress >>= 9;
uint64_t PD_i = virtualAddress & 0x1ff;
virtualAddress >>= 9;
uint64_t PDP_i = virtualAddress & 0x1ff;
PageDirectoryEntry PDE;
PDE = PML4->entries[PDP_i];
PageTable* PDP;
if (!PDE.Present)
{
PDP = (PageTable*)PMM::request_page();
ASSERT(!(PMM_DID_FAIL(PDP)));
memset(PDP, 0, 0x1000);
PDE.set_address((uint64_t)PDP);
PDE.Present = true;
PDE.ReadWrite = true;
if (flags & User) PDE.UserSuper = true;
PML4->entries[PDP_i] = PDE;
}
else { PDP = (PageTable*)((uint64_t)PDE.Address << 12); }
if ((flags & User) && !PDE.UserSuper)
{
PDE.UserSuper = true;
PML4->entries[PDP_i] = PDE;
}
PDE = PDP->entries[PD_i];
PageTable* PD;
if (!PDE.Present)
{
PD = (PageTable*)PMM::request_page();
ASSERT(!(PMM_DID_FAIL(PD)));
memset(PD, 0, 0x1000);
PDE.set_address((uint64_t)PD);
PDE.Present = true;
PDE.ReadWrite = true;
if (flags & User) PDE.UserSuper = true;
PDP->entries[PD_i] = PDE;
}
else { PD = (PageTable*)((uint64_t)PDE.Address << 12); }
if ((flags & User) && !PDE.UserSuper)
{
PDE.UserSuper = true;
PDP->entries[PD_i] = PDE;
}
PDE = PD->entries[PT_i];
PageTable* PT;
if (!PDE.Present)
{
PT = (PageTable*)PMM::request_page();
ASSERT(!(PMM_DID_FAIL(PT)));
memset(PT, 0, 0x1000);
PDE.set_address((uint64_t)PT);
PDE.Present = true;
PDE.ReadWrite = true;
if (flags & User) PDE.UserSuper = true;
PD->entries[PT_i] = PDE;
}
else { PT = (PageTable*)((uint64_t)PDE.Address << 12); }
if ((flags & User) && !PDE.UserSuper)
{
PDE.UserSuper = true;
PD->entries[PT_i] = PDE;
}
PDE = PT->entries[P_i];
PDE.Present = true;
PDE.ReadWrite = flags & ReadWrite;
PDE.UserSuper = flags & User;
PDE.set_address(physicalAddress);
PT->entries[P_i] = PDE;
} }
} }
void VMM::exit_syscall_context()
{
if (current_pml4 != user_address_space->get_pml4()) { switch_to_previous_user_address_space(); }
apply_address_space();
}
void VMM::apply_address_space()
{
write_cr3(current_pml4);
}
bool VMM::is_using_kernel_address_space()
{
return current_pml4 == kernel_pml4;
}
void VMM::init()
{
kernel_pml4 = (PageTable*)read_cr3();
current_pml4 = kernel_pml4;
}
void VMM::unmap(uint64_t vaddr)
{
vaddr = round_down_to_nearest_page(vaddr);
PageDirectoryEntry* pde = find_pde(current_pml4, vaddr);
if (!pde) return; // Already unmapped
memset(pde, 0, sizeof(PageDirectoryEntry));
flush_tlb(vaddr);
}
void VMM::remap(uint64_t vaddr, int flags)
{
vaddr = round_down_to_nearest_page(vaddr);
PageDirectoryEntry* pde = find_pde(current_pml4, vaddr);
if (!pde) return; // Not mapped
if (flags & User) propagate_user(current_pml4, vaddr);
else
pde->user = false;
if (flags & ReadWrite) propagate_read_write(current_pml4, vaddr);
else
pde->read_write = false;
flush_tlb(vaddr);
}
uint64_t VMM::get_physical(uint64_t vaddr)
{
PageDirectoryEntry* pde = find_pde(current_pml4, round_down_to_nearest_page(vaddr));
if (!pde) return UINT64_MAX; // Not mapped
return pde->get_address() | (vaddr % PAGE_SIZE);
}
uint64_t VMM::get_flags(uint64_t vaddr)
{
PageDirectoryEntry* pde = find_pde(current_pml4, round_down_to_nearest_page(vaddr));
if (!pde) return 0; // Not mapped
uint64_t flags = 0;
if (pde->user) flags |= User;
if (pde->read_write) flags |= ReadWrite;
return flags;
}
void VMM::map(uint64_t vaddr, uint64_t paddr, int flags)
{
vaddr = round_down_to_nearest_page(vaddr);
PageDirectoryEntry* pde = find_pde(current_pml4, vaddr);
bool will_flush_tlb = true;
if (!pde)
{
pde = create_pde_if_not_exists(current_pml4, vaddr);
will_flush_tlb = false;
}
else if (pde->larger_pages)
{
unmap(vaddr);
pde = create_pde_if_not_exists(current_pml4, vaddr);
}
pde->set_address(round_down_to_nearest_page(paddr));
if (flags & User) propagate_user(current_pml4, vaddr);
else
pde->user = false;
if (flags & ReadWrite) propagate_read_write(current_pml4, vaddr);
else
pde->read_write = false;
if (will_flush_tlb) flush_tlb(vaddr);
}
PageDirectoryEntry* VMM::find_pde(PageTable* root, uint64_t vaddr)
{
uint64_t page_index;
PageDirectoryEntry* pde;
PageTable* pt = root;
uint64_t indexes[3];
decompose_vaddr(vaddr, page_index, indexes[2], indexes[1], indexes[0]);
for (int i = 0; i < 3;
i++) // Walk through the page map level 4, page directory pointer, and page directory to find the page table.
{
pde = &pt->entries[indexes[i]];
if (!pde->present) return nullptr;
else if (pde->larger_pages)
return pde;
else { pt = (PageTable*)pde->get_address(); }
}
pde = &pt->entries[page_index]; // PT
if (!pde->present) return nullptr;
return pde;
}
PageDirectoryEntry* VMM::create_pde_if_not_exists(PageTable* root, uint64_t vaddr)
{
uint64_t page_index;
PageDirectoryEntry* pde;
PageTable* pt = root;
uint64_t indexes[3];
decompose_vaddr(vaddr, page_index, indexes[2], indexes[1], indexes[0]);
auto pde_create_if_not_present = [&]() {
pt = (PageTable*)PMM::request_page();
ASSERT(!(PMM_DID_FAIL(pt)));
memset(pt, 0, PAGE_SIZE);
pde->set_address((uint64_t)pt);
pde->present = true;
};
for (int i = 0; i < 3; i++)
{
pde = &pt->entries[indexes[i]];
if (!pde->present) { pde_create_if_not_present(); }
else if (pde->larger_pages)
return pde;
else { pt = (PageTable*)pde->get_address(); }
}
pde = &pt->entries[page_index];
if (!pde->present) { pde->present = true; }
return pde;
}
void VMM::propagate_read_write(PageTable* root, uint64_t vaddr)
{
uint64_t page_index;
PageDirectoryEntry* pde;
PageTable* pt = root;
uint64_t indexes[3];
decompose_vaddr(vaddr, page_index, indexes[2], indexes[1], indexes[0]);
for (int i = 0; i < 3; i++)
{
pde = &pt->entries[indexes[i]];
if (!pde->present) return;
else
{
pde->read_write = true;
if (pde->larger_pages) return;
pt = (PageTable*)pde->get_address();
}
}
pde = &pt->entries[page_index];
if (!pde->present) return;
else
pde->read_write = true;
}
void VMM::propagate_user(PageTable* root, uint64_t vaddr)
{
uint64_t page_index;
PageDirectoryEntry* pde;
PageTable* pt = root;
uint64_t indexes[3];
decompose_vaddr(vaddr, page_index, indexes[2], indexes[1], indexes[0]);
for (int i = 0; i < 3; i++)
{
pde = &pt->entries[indexes[i]];
if (!pde->present) return;
else
{
pde->user = true;
if (pde->larger_pages) return;
pt = (PageTable*)pde->get_address();
}
}
pde = &pt->entries[page_index];
if (!pde->present) return;
else
pde->user = true;
}
void VMM::flush_tlb(uint64_t addr)
{
asm volatile("invlpg (%0)" : : "r"(addr) : "memory");
}
void VMM::decompose_vaddr(uint64_t vaddr, uint64_t& page_index, uint64_t& pt_index, uint64_t& pd_index,
uint64_t& pdp_index)
{
vaddr >>= 12;
page_index = vaddr & 0x1ff;
vaddr >>= 9;
pt_index = vaddr & 0x1ff;
vaddr >>= 9;
pd_index = vaddr & 0x1ff;
vaddr >>= 9;
pdp_index = vaddr & 0x1ff;
}
uint64_t VMM::recompose_vaddr(uint64_t page_index, uint64_t pt_index, uint64_t pd_index, uint64_t pdp_index)
{
return pdp_index << 39 | pd_index << 30 | pt_index << 21 | page_index << 12;
}
void VMM::install_kernel_page_directory_into_address_space(AddressSpace& space)
{
PageTable* space_pml4 = space.get_pml4();
PageTable* kernel_last_pdp = (PageTable*)kernel_pml4->entries[511].get_address();
PageTable* kernel_last_pd = (PageTable*)kernel_last_pdp->entries[511].get_address();
PageTable* space_last_pdp = (PageTable*)PMM::request_page();
PageDirectoryEntry& space_last_pdp_pde = space_pml4->entries[511];
space_last_pdp_pde.present = true;
space_last_pdp_pde.read_write = true;
space_last_pdp_pde.set_address((uint64_t)space_last_pdp);
PageDirectoryEntry& space_last_pd_pde = space_last_pdp->entries[511];
space_last_pd_pde.present = true;
space_last_pd_pde.read_write = true;
space_last_pd_pde.set_address((uint64_t)kernel_last_pd);
}

View File

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

View File

@ -8,6 +8,7 @@
#include "interrupts/Interrupts.h" #include "interrupts/Interrupts.h"
#include "io/IO.h" #include "io/IO.h"
#include "log/Log.h" #include "log/Log.h"
#include "memory/VMM.h"
#include "misc/hang.h" #include "misc/hang.h"
#include "std/string.h" #include "std/string.h"
@ -85,6 +86,11 @@ static void try_idt_triple_fault()
[[noreturn]] void reboot() [[noreturn]] void reboot()
{ {
Interrupts::disable(); Interrupts::disable();
if (!VMM::is_using_kernel_address_space())
{
VMM::switch_back_to_kernel_address_space();
VMM::apply_address_space();
}
kinfoln("Attempting reboot using ACPI"); kinfoln("Attempting reboot using ACPI");
try_acpi_reboot(); try_acpi_reboot();
kinfoln("Attempting reboot using keyboard RESET pulsing"); kinfoln("Attempting reboot using keyboard RESET pulsing");

View File

@ -1,6 +0,0 @@
#include "misc/utils.h"
uint64_t Utilities::get_blocks_from_size(uint64_t blocksize, uint64_t size)
{
return (size + (blocksize - 1)) / blocksize;
}

View File

@ -1,4 +1,5 @@
#include <string.h> #include "std/string.h"
#include "std/stdlib.h"
size_t strlen(const char* __s) size_t strlen(const char* __s)
{ {
@ -122,3 +123,11 @@ void* memmove(void* dest, void* src, size_t n)
} }
return dest; return dest;
} }
char* strdup(const char* src)
{
size_t length = strlen(src);
char* duplicated = (char*)kmalloc(length + 1);
memcpy(duplicated, src, length + 1);
return duplicated;
}

View File

@ -1,11 +1,14 @@
#include "sys/Syscall.h" #include "sys/Syscall.h"
#include "errno.h" #include "errno.h"
#include "io/Serial.h" #include "io/Serial.h"
#include "memory/VMM.h"
#include "std/string.h"
#include "thread/Scheduler.h" #include "thread/Scheduler.h"
void Syscall::entry(Context* context) void Syscall::entry(Context* context)
{ {
asm volatile("cli"); asm volatile("cli");
VMM::enter_syscall_context();
switch (context->rax) switch (context->rax)
{ {
case SYS_exit: sys_exit(context, (int)context->rdi); break; case SYS_exit: sys_exit(context, (int)context->rdi); break;
@ -20,6 +23,16 @@ void Syscall::entry(Context* context)
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;
case SYS_read: sys_read(context, (int)context->rdi, context->rsi, (char*)context->rdx); break; case SYS_read: sys_read(context, (int)context->rdi, context->rsi, (char*)context->rdx); break;
case SYS_close: sys_close(context, (int)context->rdi); break; case SYS_close: sys_close(context, (int)context->rdi); break;
case SYS_seek: sys_seek(context, (int)context->rdi, (long)context->rsi, (int)context->rdx); break;
case SYS_exec: sys_exec(context, (const char*)context->rdi); break;
default: context->rax = -ENOSYS; break; default: context->rax = -ENOSYS; break;
} }
VMM::exit_syscall_context();
}
char* Syscall::strdup_from_user(const char* user_string)
{
uint64_t phys = VMM::get_physical((uint64_t)user_string);
if (phys == (uint64_t)-1) { return nullptr; }
return strdup((const char*)phys);
} }

View File

@ -1,14 +1,19 @@
#define MODULE "elf" #define MODULE "elf"
#include "sys/elf/ELFLoader.h" #include "sys/elf/ELFLoader.h"
#include "assert.h"
#include "errno.h"
#include "fs/VFS.h" #include "fs/VFS.h"
#include "init/InitRD.h" #include "init/InitRD.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 "misc/utils.h" #include "misc/utils.h"
#include "std/stdlib.h" #include "std/stdlib.h"
#include "std/string.h" #include "std/string.h"
#include "sys/elf/ELF.h" #include "sys/elf/ELF.h"
#include "utils/Addresses.h"
static const char* format_permissions(uint32_t flags) static const char* format_permissions(uint32_t flags)
{ {
@ -36,78 +41,72 @@ ELFImage* ELFLoader::load_elf_from_filesystem(const char* filename)
return 0; return 0;
} }
void* file = kmalloc(node->length); ELFImage* result = load_elf_from_vfs(node);
if (VFS::read(node, 0, node->length, (char*)file) < 0)
{
kwarnln("Failed to read ELF image from file");
kfree(file);
return 0;
}
ELFImage* result =
load_elf_from_address((uintptr_t)file); // FIXME: Read headers and sections as we go along the file, to avoid
// loading the entire file at once into memory.
kfree(file);
return result; return result;
} }
ELFImage* ELFLoader::load_elf_from_address(uintptr_t addr) long ELFLoader::check_elf_image_from_filesystem(const char* filename)
{ {
Elf64_Ehdr* elf_ehdr = (Elf64_Ehdr*)addr; VFS::Node* node = VFS::resolve_path(filename);
if (strncmp((const char*)elf_ehdr->e_ident, ELFMAG, SELFMAG) != 0)
if (!node)
{ {
kwarnln("ELF file has invalid magic, skipping"); kwarnln("Failed to open file %s for checking", filename);
return 0; return -ENOENT;
} }
if (elf_ehdr->e_ident[EI_CLASS] != ELFCLASS64)
if (node->type == VFS_DIRECTORY)
{ {
kwarnln("ELF file is not ELF64, skipping"); kwarnln("Failed to check %s: is a directory", filename);
return 0; return -EISDIR;
}
if (elf_ehdr->e_ident[EI_DATA] != ELFDATA2LSB)
{
kwarnln("ELF file is not little-endian, skipping");
return 0;
}
if (elf_ehdr->e_type != ET_EXEC)
{
kwarnln("not supported: ELF file is not an executable");
return 0;
}
if (elf_ehdr->e_machine != EM_MACH)
{
kwarnln("Unsupported target machine");
return 0;
}
if (elf_ehdr->e_phnum == 0)
{
kwarnln("ELF file has no PHDRS");
return 0;
} }
return check_elf_image(node);
}
ELFImage* ELFLoader::load_elf_from_vfs(VFS::Node* node)
{
Elf64_Ehdr elf_ehdr;
ASSERT(VFS::read(node, 0, sizeof(elf_ehdr), (char*)&elf_ehdr) >= 0);
ASSERT(strncmp((const char*)elf_ehdr.e_ident, ELFMAG, SELFMAG) ==
0); // If you haven't checked the ELF executable with check_elf_image() first, then an assertion fail is your
// fault =D
ASSERT(elf_ehdr.e_ident[EI_CLASS] == ELFCLASS64);
ASSERT(elf_ehdr.e_ident[EI_DATA] == ELFDATA2LSB);
ASSERT(elf_ehdr.e_type == ET_EXEC);
ASSERT(elf_ehdr.e_machine == EM_MACH);
ASSERT(elf_ehdr.e_phnum != 0);
ELFImage* image = (ELFImage*)kmalloc(sizeof(ELFImage) - sizeof(ELFSection)); ELFImage* image = (ELFImage*)kmalloc(sizeof(ELFImage) - sizeof(ELFSection));
memset(image, 0, sizeof(ELFImage) - sizeof(ELFSection)); memset(image, 0, sizeof(ELFImage) - sizeof(ELFSection));
image->entry = elf_ehdr->e_entry; image->entry = elf_ehdr.e_entry;
int i; int i;
Elf64_Phdr* phdr; Elf64_Phdr phdr;
for (phdr = (Elf64_Phdr*)((uint64_t)addr + elf_ehdr->e_phoff), i = 0; i < elf_ehdr->e_phnum; for (VFS::read(node, elf_ehdr.e_phoff, sizeof(Elf64_Phdr), (char*)&phdr), i = 0; i < elf_ehdr.e_phnum;
i++, phdr = (Elf64_Phdr*)((uint8_t*)phdr + elf_ehdr->e_phentsize)) i++, VFS::read(node, elf_ehdr.e_phoff + (i * elf_ehdr.e_phentsize), sizeof(Elf64_Phdr), (char*)&phdr))
{ {
if (phdr->p_type == PT_LOAD) if (phdr.p_type == PT_LOAD)
{ {
kdbgln("Loading loadable segment at address %lx, file size %ld, mem size %ld, permissions %s", kdbgln("Loading loadable segment at address %lx, file size %ld, mem size %ld, permissions %s", phdr.p_vaddr,
phdr->p_vaddr, phdr->p_filesz, phdr->p_memsz, format_permissions(phdr->p_flags)); phdr.p_filesz, phdr.p_memsz, format_permissions(phdr.p_flags));
if (!phdr->p_vaddr) ASSERT(phdr.p_vaddr);
{
kerrorln("Address is NULL, this is invalid :("); uint64_t pages = Utilities::get_blocks_from_size(PAGE_SIZE, (phdr.p_vaddr % PAGE_SIZE) + phdr.p_memsz);
return 0; void* buffer = (void*)((uint64_t)MemoryManager::get_pages_at(round_down_to_nearest_page(phdr.p_vaddr),
} pages, MAP_READ_WRITE) +
uint64_t pages = Utilities::get_blocks_from_size(0x1000, phdr->p_memsz); (phdr.p_vaddr % PAGE_SIZE));
void* buffer = MemoryManager::get_pages_at(phdr->p_vaddr, pages,
phdr->p_flags & 2 ? MAP_READ_WRITE | MAP_USER : MAP_USER); if (VMM::is_using_kernel_address_space()) { VMM::switch_to_previous_user_address_space(); }
memcpy(buffer, (void*)(addr + phdr->p_offset), phdr->p_filesz); VMM::apply_address_space();
memset((void*)((uint64_t)buffer + phdr->p_filesz), 0, phdr->p_memsz - phdr->p_filesz);
VFS::read(node, phdr.p_offset, phdr.p_filesz, (char*)buffer);
memset((void*)((uint64_t)buffer + phdr.p_filesz), 0, phdr.p_memsz - phdr.p_filesz);
VMM::switch_back_to_kernel_address_space();
VMM::apply_address_space();
VMM::switch_to_previous_user_address_space();
MemoryManager::protect(buffer, pages, phdr.p_flags & 2 ? MAP_READ_WRITE | MAP_USER : MAP_USER);
image = (ELFImage*)krealloc(image, (sizeof(ELFImage) - sizeof(ELFSection)) + image = (ELFImage*)krealloc(image, (sizeof(ELFImage) - sizeof(ELFSection)) +
(image->section_count + 1) * sizeof(ELFSection)); (image->section_count + 1) * sizeof(ELFSection));
ELFSection& section = image->sections[image->section_count]; ELFSection& section = image->sections[image->section_count];
@ -117,10 +116,86 @@ ELFImage* ELFLoader::load_elf_from_address(uintptr_t addr)
} }
else { kdbgln("skipping non-loadable segment"); } else { kdbgln("skipping non-loadable segment"); }
} }
if (!image->section_count) ASSERT(image->section_count);
{
kfree(image);
return 0;
}
return image; return image;
} }
long ELFLoader::check_elf_image(VFS::Node* node)
{
Elf64_Ehdr elf_ehdr;
if (VFS::read(node, 0, sizeof(elf_ehdr), (char*)&elf_ehdr) < 0)
{
kwarnln("Unable to read ELF header");
return -1;
}
if (strncmp((const char*)elf_ehdr.e_ident, ELFMAG, SELFMAG) != 0)
{
kwarnln("ELF file has invalid magic, skipping");
return -1;
}
if (elf_ehdr.e_ident[EI_CLASS] != ELFCLASS64)
{
kwarnln("ELF file is not ELF64, skipping");
return -1;
}
if (elf_ehdr.e_ident[EI_DATA] != ELFDATA2LSB)
{
kwarnln("ELF file is not little-endian, skipping");
return -1;
}
if (elf_ehdr.e_type != ET_EXEC)
{
kwarnln("not supported: ELF file is not an executable");
return -1;
}
if (elf_ehdr.e_machine != EM_MACH)
{
kwarnln("Unsupported target machine");
return -1;
}
if (elf_ehdr.e_phnum == 0)
{
kwarnln("ELF file has no PHDRS");
return -1;
}
int i;
int loadable_sections = 0;
long memusage = 0;
Elf64_Phdr phdr;
for (VFS::read(node, elf_ehdr.e_phoff, sizeof(Elf64_Phdr), (char*)&phdr), i = 0; i < elf_ehdr.e_phnum;
i++, VFS::read(node, elf_ehdr.e_phoff + (i * elf_ehdr.e_phentsize), sizeof(Elf64_Phdr), (char*)&phdr))
{
if (phdr.p_type == PT_LOAD)
{
if (!phdr.p_vaddr)
{
kerrorln("segment address is NULL, this is invalid :(");
return -1;
}
if (Memory::is_kernel_address(phdr.p_vaddr) || Memory::is_kernel_address(phdr.p_vaddr + phdr.p_memsz))
{
kerrorln("trying to load ELF into kernel memory");
return -1;
}
loadable_sections++;
memusage += Utilities::get_blocks_from_size(PAGE_SIZE, phdr.p_memsz) * PAGE_SIZE;
}
}
if (!loadable_sections)
{
kwarnln("No loadable sections");
return -1;
}
return memusage;
}
void ELFLoader::release_elf_image(ELFImage* image)
{
for (uint64_t i = 0; i < image->section_count; i++)
{
ELFSection& section = image->sections[i];
kdbgln("Freeing up section %lx, was using %ld pages", section.base, section.pages);
MemoryManager::release_pages((void*)round_down_to_nearest_page(section.base), section.pages);
}
kfree(image);
}

96
kernel/src/sys/exec.cpp Normal file
View File

@ -0,0 +1,96 @@
#define MODULE "exec"
#include "assert.h"
#include "errno.h"
#include "interrupts/Interrupts.h"
#include "memory/MemoryManager.h"
#include "memory/PMM.h"
#include "memory/VMM.h"
#include "std/stdlib.h"
#include "std/string.h"
#include "sys/Syscall.h"
#include "sys/elf/ELFLoader.h"
#include "thread/Scheduler.h"
void sys_exec(Context* context, const char* pathname)
{
/*context->rax = -ENOSYS; // FIXME: Make exec() work under separate address spaces.
return;*/
char* kpathname = Syscall::strdup_from_user(pathname);
if (!kpathname)
{
context->rax = -EFAULT;
return;
}
kinfoln("exec(): executing %s", kpathname);
VFS::Node* program = VFS::resolve_path(kpathname);
if (!program)
{
kfree(kpathname);
context->rax = -ENOENT;
return;
}
if (program->type == VFS_DIRECTORY)
{
kfree(kpathname);
context->rax = -EISDIR;
return;
}
long memusage;
if ((memusage = ELFLoader::check_elf_image(program)) < 0)
{
kfree(kpathname);
context->rax = -ENOEXEC;
return;
}
uint64_t allocated_stack = (uint64_t)MemoryManager::get_pages(TASK_PAGES_IN_STACK, MAP_READ_WRITE | MAP_USER);
if (!allocated_stack)
{
kfree(kpathname);
context->rax = -ENOMEM;
return;
}
uint64_t allocated_stack_phys = VMM::get_physical(allocated_stack);
if ((uint64_t)memusage > PMM::get_free())
{
kfree(kpathname);
MemoryManager::release_pages((void*)allocated_stack, TASK_PAGES_IN_STACK);
context->rax = -ENOMEM;
return;
}
Interrupts::disable();
ASSERT(!Interrupts::are_enabled()); // This part is pretty sensitive.
Task* task = Scheduler::current_task();
ASSERT(task);
// At this point, pretty much nothing can fail.
ELFImage* image = ELFLoader::load_elf_from_vfs(program);
ASSERT(image); // If check_elf_image succeeded, load_elf_from_vfs MUST succeed, unless something has gone terribly
// wrong.
task->allocated_stack = allocated_stack;
for (uint64_t i = 0; i < TASK_PAGES_IN_STACK; i++)
{
VMM::map(allocated_stack + (i * PAGE_SIZE), allocated_stack_phys + (i * PAGE_SIZE), MAP_READ_WRITE | MAP_USER);
}
Scheduler::reset_task(task, image);
set_context_from_task(*task, context);
kfree(kpathname);
return;
}

View File

@ -5,6 +5,7 @@
#include "log/Log.h" #include "log/Log.h"
#include "memory/MemoryManager.h" #include "memory/MemoryManager.h"
#include "memory/VMM.h" #include "memory/VMM.h"
#include "misc/utils.h"
#include <stddef.h> #include <stddef.h>
#define MAP_FAIL(errno) 0xffffffffffffff00 | (unsigned char)(errno) #define MAP_FAIL(errno) 0xffffffffffffff00 | (unsigned char)(errno)
@ -13,7 +14,13 @@ void sys_mmap(Context* context, void* address, size_t size, int flags)
{ {
if (size < PAGE_SIZE) if (size < PAGE_SIZE)
{ {
kdbgln("sys_mmap: size too small"); kwarnln("mmap(): size too small");
context->rax = MAP_FAIL(EINVAL);
return;
}
if (size % PAGE_SIZE)
{
kwarnln("mmap(): size not a multiple of PAGE_SIZE");
context->rax = MAP_FAIL(EINVAL); context->rax = MAP_FAIL(EINVAL);
return; return;
} }
@ -21,40 +28,42 @@ void sys_mmap(Context* context, void* address, size_t size, int flags)
if (flags & MAP_READ_WRITE) real_flags |= MAP_READ_WRITE; if (flags & MAP_READ_WRITE) real_flags |= MAP_READ_WRITE;
if (address) if (address)
{ {
kdbgln("sys_mmap: %ld pages at address %p, %s", size / PAGE_SIZE, address, kdbgln("mmap(): %ld pages at address %p, %s", size / PAGE_SIZE, address,
real_flags & MAP_READ_WRITE ? "rw" : "ro"); real_flags & MAP_READ_WRITE ? "rw" : "ro");
if (kernelVMM.getPhysical((uint64_t)address) != (uint64_t)-1) // Address is already used. if (VMM::get_physical((uint64_t)address) != (uint64_t)-1) // Address is already used.
{ {
kdbgln("attempt to mmap an already mapped address"); kwarnln("attempt to map an already mapped address");
context->rax = MAP_FAIL(ENOMEM); context->rax = MAP_FAIL(ENOMEM);
return; return;
} }
uint64_t offset = (uint64_t)address % PAGE_SIZE; uint64_t offset = (uint64_t)address % PAGE_SIZE;
void* result = MemoryManager::get_pages_at((uint64_t)address - offset, size / PAGE_SIZE, real_flags); void* result = MemoryManager::get_pages_at((uint64_t)address - offset,
Utilities::get_blocks_from_size(PAGE_SIZE, size), real_flags);
if (result) if (result)
{ {
kdbgln("mmap succeeded: %p", result); kdbgln("mmap() succeeded: %p", result);
context->rax = (uint64_t)result; context->rax = (uint64_t)result;
return; return;
} }
else else
{ {
kdbgln("mmap failed"); kwarnln("mmap() failed");
context->rax = MAP_FAIL(ENOMEM); context->rax = MAP_FAIL(ENOMEM);
return; return;
} }
} }
kdbgln("sys_mmap: %ld pages at any address, %s", size / PAGE_SIZE, real_flags & MAP_READ_WRITE ? "rw" : "ro"); kdbgln("mmap(): %ld pages at any address, %s", Utilities::get_blocks_from_size(PAGE_SIZE, size),
void* result = MemoryManager::get_pages(size / PAGE_SIZE, real_flags); real_flags & MAP_READ_WRITE ? "rw" : "ro");
void* result = MemoryManager::get_pages(Utilities::get_blocks_from_size(PAGE_SIZE, size), real_flags);
if (result) if (result)
{ {
kdbgln("mmap succeeded: %p", result); kdbgln("mmap() succeeded: %p", result);
context->rax = (uint64_t)result; context->rax = (uint64_t)result;
return; return;
} }
else else
{ {
kdbgln("mmap failed"); kwarnln("mmap() failed");
context->rax = MAP_FAIL(ENOMEM); context->rax = MAP_FAIL(ENOMEM);
return; return;
} }
@ -62,29 +71,35 @@ 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)
{ {
kdbgln("sys_munmap: attempting to unmap %p", address); kdbgln("munmap(): attempting to unmap %p", address);
if (size < PAGE_SIZE) if (size < PAGE_SIZE)
{ {
kdbgln("munmap failed: size is too small"); kwarnln("munmap() failed: size is too small");
context->rax = -EINVAL;
return;
}
if (size % PAGE_SIZE)
{
kwarnln("munmap() failed: size is not a multiple of PAGE_SIZE");
context->rax = -EINVAL; context->rax = -EINVAL;
return; return;
} }
if (!address) if (!address)
{ {
kdbgln("munmap failed: attempted to unmap page 0"); kwarnln("munmap() failed: attempted to unmap page 0");
context->rax = -EINVAL; context->rax = -EINVAL;
return; return;
} }
uint64_t flags = kernelVMM.getFlags((uint64_t)address); uint64_t flags = VMM::get_flags((uint64_t)address);
if (!(flags & MAP_USER)) if (!(flags & MAP_USER))
{ {
kdbgln("munmap failed: attempted to unmap a kernel page"); kwarnln("munmap() failed: attempted to unmap a non-existent or kernel 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;
MemoryManager::release_pages((void*)((uint64_t)address - offset), size / PAGE_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;
} }

View File

@ -4,46 +4,85 @@
#include "interrupts/Context.h" #include "interrupts/Context.h"
#include "io/Serial.h" #include "io/Serial.h"
#include "log/Log.h" #include "log/Log.h"
#include "memory/VMM.h"
#include "render/TextRenderer.h" #include "render/TextRenderer.h"
#include "std/stdlib.h"
#include "sys/Syscall.h"
#include "thread/Scheduler.h" #include "thread/Scheduler.h"
#include "thread/Task.h" #include "thread/Task.h"
#define OPEN_READ 1 #define OPEN_READ 1
#define OPEN_WRITE 2 #define OPEN_WRITE 2
#define STDIO_FAIL(function, error) kwarnln("%s failed with %s", #function, #error) #define SEEK_SET 0
#define SEEK_CUR 1
#define SEEK_END 2
void sys_write(Context* context, int fd, size_t size, const char* addr) void sys_seek(Context* context, int fd, long offset, int whence)
{ {
if (!addr) if (whence < SEEK_SET || whence > SEEK_END)
{ {
STDIO_FAIL(write, EINVAL); context->rax = -EINVAL;
context->rax = -EINVAL; // FIXME: This should probably return EFAULT. return;
}
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;
}
long new_offset;
if (whence == SEEK_SET) new_offset = offset;
else if (whence == SEEK_CUR)
new_offset = offset + current_task->files[fd].offset();
else if (whence == SEEK_END)
new_offset = current_task->files[fd].length() + offset;
else
__builtin_unreachable();
if (new_offset < 0)
{
context->rax = -EINVAL; // FIXME: Is this the right error?
return;
}
int result = current_task->files[fd].seek(new_offset);
if (result < 0)
{
context->rax = result;
return;
}
context->rax = new_offset;
return;
}
void sys_write(Context* context, int fd, size_t size, const char* addr)
{
if (!addr)
{
context->rax = -EFAULT;
return; return;
} }
if (fd >= TASK_MAX_FDS || fd < 0) if (fd >= TASK_MAX_FDS || fd < 0)
{ {
kwarnln("file descriptor %d is outside the valid range", fd);
STDIO_FAIL(write, EBADF);
context->rax = -EBADF; context->rax = -EBADF;
return; return;
} }
Task* current_task = Scheduler::current_task(); Task* current_task = Scheduler::current_task();
if (!current_task->files[fd].is_open()) if (!current_task->files[fd].is_open())
{ {
kwarnln("file descriptor %d is not open", fd);
STDIO_FAIL(write, EBADF);
context->rax = -EBADF; context->rax = -EBADF;
return; return;
} }
if (!current_task->files[fd].can_write()) if (!current_task->files[fd].can_write())
{ {
kwarnln("file descriptor %d is not open for writing", fd);
STDIO_FAIL(write, EBADF);
context->rax = -EBADF; context->rax = -EBADF;
return; return;
} }
ssize_t result = current_task->files[fd].write(size, addr); ssize_t result = current_task->files[fd].write(size, (const char*)VMM::get_physical((uint64_t)addr));
context->rax = (size_t)result; context->rax = (size_t)result;
return; return;
} }
@ -59,15 +98,21 @@ void sys_open(Context* context, const char* filename, int flags)
if (fd == TASK_MAX_FDS) if (fd == TASK_MAX_FDS)
{ {
STDIO_FAIL(open, EMFILE);
context->rax = -EMFILE; context->rax = -EMFILE;
return; return;
} }
VFS::Node* node = VFS::resolve_path(filename); char* kernel_filename = Syscall::strdup_from_user(filename);
if (!kernel_filename)
{
context->rax = -EFAULT;
return;
}
VFS::Node* node = VFS::resolve_path(kernel_filename);
if (!node) if (!node)
{ {
STDIO_FAIL(open, ENOENT); kfree(kernel_filename);
context->rax = -ENOENT; context->rax = -ENOENT;
return; return;
} }
@ -76,17 +121,18 @@ void sys_open(Context* context, const char* filename, int flags)
bool can_write = (flags & OPEN_WRITE) > 0; bool can_write = (flags & OPEN_WRITE) > 0;
if (!can_read && !can_write) if (!can_read && !can_write)
{ {
STDIO_FAIL(open, EINVAL); kfree(kernel_filename);
context->rax = -EINVAL; context->rax = -EINVAL;
return; return;
} }
kdbgln("open(): opening %s %s, allocated file descriptor %d", filename, kdbgln("open(): opening %s %s, allocated file descriptor %d", kernel_filename,
(can_read && can_write) ? "rw" (can_read && can_write) ? "rw"
: can_read ? "r-" : can_read ? "r-"
: "-w", : "-w",
fd); fd);
kfree(kernel_filename);
current_task->files[fd].open(node, can_read, can_write); current_task->files[fd].open(node, can_read, can_write);
context->rax = fd; context->rax = fd;
return; return;
@ -96,24 +142,21 @@ void sys_read(Context* context, int fd, size_t size, char* buffer)
{ {
if (!buffer) if (!buffer)
{ {
STDIO_FAIL(read, EINVAL); context->rax = -EFAULT;
context->rax = -EINVAL; // FIXME: This should probably return EFAULT.
return; return;
} }
if (fd >= TASK_MAX_FDS || fd < 0) if (fd >= TASK_MAX_FDS || fd < 0)
{ {
STDIO_FAIL(read, EBADF);
context->rax = -EBADF; context->rax = -EBADF;
return; return;
} }
Task* current_task = Scheduler::current_task(); Task* current_task = Scheduler::current_task();
if (!current_task->files[fd].is_open() || !current_task->files[fd].can_read()) if (!current_task->files[fd].is_open() || !current_task->files[fd].can_read())
{ {
STDIO_FAIL(read, EBADF);
context->rax = -EBADF; context->rax = -EBADF;
return; return;
} }
ssize_t result = current_task->files[fd].read(size, buffer); ssize_t result = current_task->files[fd].read(size, (char*)VMM::get_physical((uint64_t)buffer));
context->rax = (size_t)result; context->rax = (size_t)result;
return; return;
} }
@ -122,14 +165,12 @@ void sys_close(Context* context, int fd)
{ {
if (fd >= TASK_MAX_FDS || fd < 0) if (fd >= TASK_MAX_FDS || fd < 0)
{ {
STDIO_FAIL(close, EBADF);
context->rax = -EBADF; context->rax = -EBADF;
return; return;
} }
Task* current_task = Scheduler::current_task(); Task* current_task = Scheduler::current_task();
if (!current_task->files[fd].is_open()) if (!current_task->files[fd].is_open())
{ {
STDIO_FAIL(close, EBADF);
context->rax = -EBADF; context->rax = -EBADF;
return; return;
} }

View File

@ -7,14 +7,15 @@
#include "memory/MemoryManager.h" #include "memory/MemoryManager.h"
#include "memory/VMM.h" #include "memory/VMM.h"
#include "misc/hang.h" #include "misc/hang.h"
#include "misc/utils.h"
#include "panic/Panic.h" #include "panic/Panic.h"
#include "std/stdlib.h" #include "std/stdlib.h"
#include "std/string.h" #include "std/string.h"
#include "sys/elf/ELFLoader.h" #include "sys/elf/ELFLoader.h"
#include "thread/PIT.h" #include "thread/PIT.h"
#include "thread/Task.h" #include "thread/Task.h"
#include "utils/Addresses.h"
#define TASK_PAGES_IN_STACK 4 #include "utils/Registers.h"
static uint64_t task_num = 0; static uint64_t task_num = 0;
@ -35,11 +36,12 @@ void Scheduler::init()
memset(&idle_task, 0, sizeof(Task)); memset(&idle_task, 0, sizeof(Task));
idle_task.id = free_tid++; idle_task.id = free_tid++;
idle_task.regs.rip = (uint64_t)idle_task_function; idle_task.regs.rip = (uint64_t)idle_task_function;
idle_task.regs.rsp = (uint64_t)MemoryManager::get_page(); idle_task.regs.rsp = get_top_of_stack((uint64_t)MemoryManager::get_page(), 1);
idle_task.regs.cs = 0x08; idle_task.regs.cs = 0x08;
idle_task.regs.ss = 0x10; idle_task.regs.ss = 0x10;
idle_task.regs.rflags = (1 << 21) | (1 << 9); idle_task.regs.rflags = (1 << 21) | (1 << 9);
idle_task.task_sleep = 1000; idle_task.task_sleep = 1000;
idle_task.user_task = false;
idle_task.state = idle_task.Idle; idle_task.state = idle_task.Idle;
base_task = new Task; base_task = new Task;
@ -50,6 +52,7 @@ void Scheduler::init()
sched_current_task->next_task = sched_current_task; sched_current_task->next_task = sched_current_task;
sched_current_task->prev_task = sched_current_task; sched_current_task->prev_task = sched_current_task;
sched_current_task->state = sched_current_task->Running; sched_current_task->state = sched_current_task->Running;
sched_current_task->user_task = false;
task_num++; task_num++;
// the other registers will be saved next task switch // the other registers will be saved next task switch
@ -60,16 +63,16 @@ void Scheduler::add_kernel_task(void (*task)(void))
{ {
Task* new_task = new Task; Task* new_task = new Task;
ASSERT(new_task); ASSERT(new_task);
new_task->user_task = false;
new_task->id = free_tid++; new_task->id = free_tid++;
new_task->regs.rip = (uint64_t)task; new_task->regs.rip = (uint64_t)task;
new_task->allocated_stack = new_task->allocated_stack =
(uint64_t)MemoryManager::get_pages(TASK_PAGES_IN_STACK); // 16 KB is enough for everyone, right? (uint64_t)MemoryManager::get_pages(TASK_PAGES_IN_STACK); // 16 KB is enough for everyone, right?
new_task->regs.rsp = new_task->allocated_stack + (PAGE_SIZE * TASK_PAGES_IN_STACK) - sizeof(uintptr_t); new_task->regs.rsp = get_top_of_stack(new_task->allocated_stack, TASK_PAGES_IN_STACK);
new_task->regs.cs = 0x08; new_task->regs.cs = 0x08;
new_task->regs.ss = 0x10; new_task->regs.ss = 0x10;
new_task->regs.ds = 0x10; new_task->regs.ds = 0x10;
asm volatile("pushfq; movq (%%rsp), %%rax; movq %%rax, %0; popfq;" : "=m"(new_task->regs.rflags)::"%rax"); new_task->regs.rflags = read_rflags() | 0x200; // enable interrupts
new_task->regs.rflags |= 0x200; // enable interrupts
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;
@ -84,19 +87,16 @@ void Scheduler::add_kernel_task(void (*task)(void))
new_task->id, new_task->regs.rsp, task_num); new_task->id, new_task->regs.rsp, task_num);
} }
void Scheduler::add_user_task(void* task) Task* Scheduler::create_user_task()
{ {
Task* new_task = new Task; Task* new_task = new Task;
ASSERT(new_task); ASSERT(new_task);
memset(&new_task->regs, 0, sizeof(Context));
new_task->user_task = true;
new_task->id = free_tid++; new_task->id = free_tid++;
new_task->regs.rip = (uint64_t)task;
new_task->allocated_stack = (uint64_t)MemoryManager::get_pages( 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? TASK_PAGES_IN_STACK, MAP_READ_WRITE | MAP_USER); // 16 KB is enough for everyone, right?
new_task->regs.rsp = new_task->allocated_stack + (PAGE_SIZE * TASK_PAGES_IN_STACK) - sizeof(uintptr_t); new_task->regs.rsp = get_top_of_stack(new_task->allocated_stack, TASK_PAGES_IN_STACK);
new_task->regs.cs = 0x18 | 0x03;
new_task->regs.ss = 0x20 | 0x03;
new_task->regs.ds = 0x20 | 0x03;
new_task->regs.rflags = (1 << 21) | (1 << 9); // enable interrupts
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;
@ -105,30 +105,36 @@ void Scheduler::add_user_task(void* task)
base_task->prev_task = new_task; base_task->prev_task = new_task;
new_task->next_task = base_task; new_task->next_task = base_task;
end_task = new_task; end_task = new_task;
new_task->state = new_task->Running;
task_num++; task_num++;
kinfoln("Adding user task: starts at %lx, tid %ld, stack at %lx, total tasks: %ld", new_task->regs.rip, return new_task;
new_task->id, new_task->regs.rsp, task_num);
} }
void Scheduler::load_user_task(const char* filename) void Scheduler::load_user_task(const char* filename)
{ {
kinfoln("Loading user task: %s", filename); kinfoln("Loading user task: %s", filename);
Task* new_task = new Task; Interrupts::push_and_disable();
ASSERT(new_task); if (ELFLoader::check_elf_image_from_filesystem(filename) < 0)
new_task->id = free_tid++;
ELFImage* image = ELFLoader::load_elf_from_filesystem(filename);
if (!image)
{ {
kerrorln("Failed to load %s from initrd", filename); kerrorln("Failed to load %s from initrd", filename);
delete new_task; Interrupts::pop();
return; return;
} }
Task* new_task = new Task;
ASSERT(new_task);
memset(&new_task->regs, 0, sizeof(Context));
new_task->id = free_tid++;
new_task->address_space = AddressSpace::create();
VMM::switch_to_user_address_space(new_task->address_space);
ELFImage* image = ELFLoader::load_elf_from_filesystem(
filename); // FIXME: TOCTOU? Right now, impossible, since interrupts are disabled and SMP is not a thing. But in
// the future, it might be possible.
ASSERT(image);
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(
TASK_PAGES_IN_STACK, MAP_READ_WRITE | MAP_USER); // 16 KB is enough for everyone, right? TASK_PAGES_IN_STACK, MAP_READ_WRITE | MAP_USER); // 16 KB is enough for everyone, right?
new_task->regs.rsp = new_task->allocated_stack + (PAGE_SIZE * TASK_PAGES_IN_STACK) - sizeof(uintptr_t); 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;
new_task->regs.ds = 0x20 | 0x03; new_task->regs.ds = 0x20 | 0x03;
@ -145,6 +151,25 @@ void Scheduler::load_user_task(const char* filename)
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: loaded at %lx, tid %ld, stack at %lx, total tasks: %ld", new_task->regs.rip,
new_task->id, new_task->regs.rsp, task_num); new_task->id, new_task->regs.rsp, task_num);
VMM::switch_back_to_kernel_address_space();
Interrupts::pop();
}
void Scheduler::reset_task(Task* task, ELFImage* new_image)
{
memset(&task->regs, 0, sizeof(Context));
task->state = task->Running;
task->regs.rip = new_image->entry;
task->image = new_image;
task->regs.rsp = get_top_of_stack(task->allocated_stack, TASK_PAGES_IN_STACK);
task->regs.cs = 0x18 | 0x03;
task->regs.ss = 0x20 | 0x03;
task->regs.ds = 0x20 | 0x03;
task->regs.rflags = (1 << 21) | (1 << 9); // enable interrupts
task->task_sleep = 0;
task->cpu_time = 0;
kinfoln("Resetting task: loaded at %lx, tid %ld, stack at %lx, total tasks: %ld", task->regs.rip, task->id,
task->regs.rsp, task_num);
} }
void Scheduler::reap_task(Task* task) void Scheduler::reap_task(Task* task)
@ -153,18 +178,29 @@ void Scheduler::reap_task(Task* task)
task_num--; task_num--;
Task* exiting_task = task; Task* exiting_task = task;
ASSERT(task->id != 0); // WHY IN THE WORLD WOULD WE BE REAPING THE IDLE TASK? ASSERT(task->id != 0); // WHY IN THE WORLD WOULD WE BE REAPING THE IDLE TASK?
if (exiting_task->is_user_task())
{
VMM::switch_back_to_kernel_address_space();
VMM::apply_address_space();
VMM::switch_to_user_address_space(exiting_task->address_space);
}
kinfoln("reaping task %ld, exited with code %ld", exiting_task->id, exiting_task->exit_status); kinfoln("reaping task %ld, exited with code %ld", exiting_task->id, exiting_task->exit_status);
if (exiting_task->allocated_stack) if (exiting_task->allocated_stack)
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) if (exiting_task->image) // FIXME: Also free pages the task has mmap-ed but not munmap-ed.
{ {
for (uint64_t i = 0; i < exiting_task->image->section_count; i++) // ELFLoader::release_elf_image(exiting_task->image);
{
ELFSection& section = exiting_task->image->sections[i];
kdbgln("Task was using region %lx, which used %ld pages", section.base, section.pages);
}
kfree(exiting_task->image); kfree(exiting_task->image);
} }
if (exiting_task->is_user_task())
{
VMM::switch_back_to_kernel_address_space();
VMM::apply_address_space();
Interrupts::push_and_enable();
exiting_task->address_space.destroy();
Interrupts::pop();
}
for (int i = 0; i < TASK_MAX_FDS; i++) { exiting_task->files[i].close(); }
delete exiting_task; delete exiting_task;
} }
@ -291,7 +327,17 @@ void Scheduler::task_yield(Context* context)
{ {
task_save_floating(*original_task); task_save_floating(*original_task);
} }
if (sched_current_task->is_user_task()) { task_restore_floating(*sched_current_task); } if (sched_current_task->is_user_task())
{
VMM::switch_to_user_address_space(sched_current_task->address_space);
VMM::apply_address_space();
task_restore_floating(*sched_current_task);
}
else if (!was_idle && original_task->is_user_task() && !sched_current_task->is_user_task())
{
VMM::switch_back_to_kernel_address_space();
VMM::apply_address_space();
}
} }
sched_current_task->task_time = 20; sched_current_task->task_time = 20;
set_context_from_task(*sched_current_task, context); set_context_from_task(*sched_current_task, context);

View File

@ -25,5 +25,5 @@ void task_restore_floating(Task& task)
bool Task::is_user_task() bool Task::is_user_task()
{ {
return (regs.cs & 3) > 0; return user_task;
} }

View File

@ -0,0 +1,8 @@
#ifndef _BITS_SEEK_H
#define _BITS_SEEK_H
#define SEEK_SET 0 // Seek from beginning of file.
#define SEEK_CUR 1 // Seek from current position.
#define SEEK_END 2 // Seek from end of file.
#endif

View File

@ -1,15 +1,18 @@
#ifndef _ERRNO_H #ifndef _ERRNO_H
#define _ERRNO_H #define _ERRNO_H
/* The last error encountered during a call to a library or system function. */
extern int errno; extern int errno;
#define EPERM 1 #define EPERM 1 // Operation not permitted
#define ENOENT 2 #define ENOENT 2 // No such file or directory
#define EBADF 9 #define ENOEXEC 8 // Exec format error
#define ENOMEM 12 #define EBADF 9 // Bad file descriptor
#define EISDIR 21 #define ENOMEM 12 // Cannot allocate memory
#define EINVAL 22 #define EFAULT 14 // Bad address
#define EMFILE 24 #define EISDIR 21 // Is a directory
#define ENOSYS 38 #define EINVAL 22 // Invalid argument
#define EMFILE 24 // Too many open files
#define ENOSYS 38 // Function not implemented
#endif #endif

View File

@ -1,8 +1,11 @@
#ifndef _FCNTL_H #ifndef _FCNTL_H
#define _FCNTL_H #define _FCNTL_H
/* Open for reading only. */
#define O_RDONLY 1 #define O_RDONLY 1
/* Open for writing only. */
#define O_WRONLY 2 #define O_WRONLY 2
/* Open for reading and writing. */
#define O_RDWR 3 #define O_RDWR 3
#ifdef __cplusplus #ifdef __cplusplus
@ -10,7 +13,8 @@ extern "C"
{ {
#endif #endif
int open(const char*, int); /* Opens the file specified by pathname. Returns a file descriptor on success, or -1 on error. */
int open(const char* pathname, int flags);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -9,8 +9,13 @@ extern "C"
{ {
#endif #endif
/* Returns the current program's thread identifier. */
pid_t gettid(); pid_t gettid();
unsigned int msleep(unsigned int);
/* Sleeps for ms milliseconds. */
unsigned int msleep(unsigned int ms);
/* Prints a message to standard error and aborts the program. */
noreturn void __luna_abort(const char* message); noreturn void __luna_abort(const char* message);
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -13,6 +13,8 @@
#define SYS_open 9 #define SYS_open 9
#define SYS_read 10 #define SYS_read 10
#define SYS_close 11 #define SYS_close 11
#define SYS_seek 12
#define SYS_exec 13
#ifndef __want_syscalls #ifndef __want_syscalls
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -4,7 +4,7 @@
#include <stdarg.h> #include <stdarg.h>
#include <stddef.h> #include <stddef.h>
#define SEEK_SET 0 #include <bits/seek.h>
typedef struct typedef struct
{ {
@ -13,36 +13,109 @@ typedef struct
int f_err; int f_err;
} FILE; } FILE;
extern FILE* __stderr; extern FILE* stderr;
extern FILE* __stdout; extern FILE* stdout;
#define stderr __stderr #define stdout stdout
#define stdout __stdout #define stderr stderr
typedef struct
{
long f_offset;
} fpos_t;
#ifdef __cplusplus #ifdef __cplusplus
extern "C" extern "C"
{ {
#endif #endif
int fclose(FILE*);
int fflush(FILE*); /* Closes the file handle stream. */
FILE* fopen(const char*, const char*); int fclose(FILE* stream);
int fprintf(FILE*, const char*, ...);
size_t fread(void*, size_t, size_t, FILE*); /* Does not do anything for now, since buffered IO is not implemented yet. */
int fseek(FILE*, long, int); int fflush(FILE* stream);
long ftell(FILE*);
size_t fwrite(const void*, size_t, size_t, FILE*); /* Opens the file specified by pathname. Returns the file handle on success, or NULL on error. */
int ferror(FILE*); FILE* fopen(const char* pathname, const char* mode);
int feof(FILE*);
void clearerr(FILE*); /* Returns a new file associated with the file descriptor fd. */
void setbuf(FILE*, char*); FILE* fdopen(int fd, const char* mode);
int vfprintf(FILE*, const char*, va_list);
int printf(const char*, ...); /* Writes formatted output according to the string format to the file stream. */
int vprintf(const char*, va_list); int fprintf(FILE* stream, const char* format, ...);
int sprintf(char*, const char*, ...);
int snprintf(char*, size_t, const char*, ...); /* Reads nmemb items of size size from the file stream into buf. */
int vsprintf(char*, const char*, va_list); size_t fread(void* buf, size_t size, size_t nmemb, FILE* stream);
int vsnprintf(char*, size_t, const char*, va_list);
int puts(const char*); /* Moves stream's read/write offset by offset, depending on whence. */
void perror(const char*); int fseek(FILE* stream, long offset, int whence);
/* Moves stream's read/write offset to the offset stored in the pos structure. */
int fsetpos(FILE* stream, const fpos_t* pos);
/* Returns the current offset for stream. */
long ftell(FILE* stream);
/* Stores the current offset for stream in the pos structure. */
int fgetpos(FILE* stream, fpos_t* pos);
/* Rewinds stream's offset to start of file. */
void rewind(FILE* stream);
/* Writes nmemb items of size size from buf into the file stream. */
size_t fwrite(const void* buf, size_t size, size_t nmemb, FILE* stream);
/* Returns nonzero if the error flag in stream was set. */
int ferror(FILE* stream);
/* Returns nonzero if the end-of-file flag in stream was set. */
int feof(FILE* stream);
/* Clears the error and end-of-file flags from stream. */
void clearerr(FILE* stream);
void setbuf(FILE*, char*); // Not implemented.
/* Writes formatted output according to the string format to the file stream. */
int vfprintf(FILE* stream, const char* format, va_list ap);
/* Writes formatted output according to the string format to standard output. */
int printf(const char* format, ...);
/* Writes formatted output according to the string format to standard output. */
int vprintf(const char* format, va_list ap);
/* Writes formatted output according to the string format to the string str. This function is unsafe, use snprintf
* instead. */
int sprintf(char* str, const char* format, ...);
/* Writes at most max bytes of formatted output according to the string format to the string str.*/
int snprintf(char* str, size_t max, const char* format, ...);
/* Writes formatted output according to the string format to the string str. This function is unsafe, use vsnprintf
* instead. */
int vsprintf(char* str, const char* format, va_list ap);
/* Writes at most max bytes of formatted output according to the string format to the string str. */
int vsnprintf(char* str, size_t max, const char* format, va_list ap);
/* Writes the string str followed by a trailing newline to stdout. */
int puts(const char* str);
/* Writes the string str to the file stream. */
int fputs(const char* str, FILE* stream);
/* Writes the character c to the file stream. */
int fputc(int c, FILE* stream);
/* Writes the character c to the file stream. */
int putc(int c, FILE* stream);
/* Writes the character c to standard output. */
int putchar(int c);
/* Prints a message to standard error consisting of the string str followed by a colon and the string representation
* of the last error encountered during a call to a system or library function. */
void perror(const char* str);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -9,13 +9,37 @@ extern "C"
{ {
#endif #endif
/* Aborts the program. */
noreturn void abort(); noreturn void abort();
noreturn void exit(int);
int atexit(void (*)(void)); /* Normally exits the program with the specified status code. */
noreturn void exit(int status);
/* Registers a handler function to be run at normal program termination. */
int atexit(void (*handler)(void));
/* Not implemented.*/
int atoi(const char*); int atoi(const char*);
void free(void*);
/* Not implemented. */
char* getenv(const char*); char* getenv(const char*);
void* malloc(size_t);
/* Allocates n bytes of memory and returns a pointer to it. This memory should be freed by calling free() when it is
* not in use anymore. */
void* malloc(size_t n);
/* Allocates enough bytes of memory for an array containing nmemb items of size n and returns a pointer to it. This
* memory should be freed by calling free() when it is not in use anymore. */
void* calloc(size_t nmemb, size_t n);
/* Resizes memory allocated by malloc() or calloc() to n bytes. Returns a pointer to the new resized region of
* memory, which should be used instead of the old one. This memory should be freed by calling free() when it is not
* in use anymore. */
void* realloc(void* ptr, size_t n);
/* Frees a pointer to memory allocated by malloc(), calloc() or realloc(). Accessing the contents of ptr afterwards
* is undefined behavior. */
void free(void* ptr);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -9,21 +9,37 @@ extern "C"
{ {
#endif #endif
void* memcpy(void*, const void*, size_t); /* Copies n bytes from src to dst. */
void* memset(void*, int, size_t); void* memcpy(void* dest, const void* src, size_t n);
void* memclr(void*, size_t);
size_t strlen(const char*); /* Sets n bytes of buf to c, cast to a character. */
void* memset(void* buf, int c, size_t n);
deprecated("strcpy is unsafe and should not be used; use strncpy instead") char* strcpy(char*, const char*); /* Clears n bytes of buf. */
char* strncpy(char*, const char*, size_t); void* memclr(void* buf, size_t n);
char* strchr(const char*, int); /* Returns the length of the string str. */
size_t strlen(const char* str);
deprecated("strcat is unsafe and should not be used; use strncat instead") char* strcat(char*, const char*); /* Copies the string src into dest. This function is unsafe, use strncpy instead. */
char* strncat(char*, const char*, size_t); deprecated("strcpy is unsafe and should not be used; use strncpy instead") char* strcpy(char* dest,
const char* src);
char* strerror(int); /* Copies at most max bytes from the string src into dest. */
char* strncpy(char* dest, const char* src, size_t max);
/* Returns a pointer to the first occurrence of the character c in str, or NULL if it is not found. */
char* strchr(const char* str, int c);
/* Concatenates the string src into dest. This function is unsafe, use strncat instead. */
deprecated("strcat is unsafe and should not be used; use strncat instead") char* strcat(char* dest,
const char* src);
/* Concatenates at most max bytes of the string src into dest. */
char* strncat(char* dest, const char* src, size_t max);
/* Returns the error string associated with the error number err. */
char* strerror(int err);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -2,9 +2,9 @@
#define _SYS_MMAN_H #define _SYS_MMAN_H
#include <stddef.h> #include <stddef.h>
#include <sys/types.h> // for off_t
typedef unsigned long off_t; /* Address returned by mmap when it fails. */
#define MAP_FAILED (void*)-1 #define MAP_FAILED (void*)-1
#define PROT_READ_WRITE 1 #define PROT_READ_WRITE 1
@ -16,8 +16,13 @@ extern "C"
{ {
#endif #endif
void* mmap(void*, size_t, int, int, int, off_t); /* Maps size bytes of memory (rounded up to the nearest page-aligned size) into the current process's address
int munmap(void* addr, size_t len); * space at addr. If addr is null, the kernel will choose an address. */
void* mmap(void* addr, size_t size, int prot, int flags, int fd, off_t offset);
/* Unmaps size bytes of memory (rounded up to the nearest page-aligned size) at addr from the current process's
* address space. */
int munmap(void* addr, size_t size);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -1,8 +1,16 @@
#ifndef _SYS_TYPES_H #ifndef _SYS_TYPES_H
#define _SYS_TYPES_H #define _SYS_TYPES_H
/* The type of a process identifier. */
typedef long int pid_t; typedef long int pid_t;
/* The type returned by sizeof(). */
typedef unsigned long int size_t; typedef unsigned long int size_t;
/* Signed version of size_t. */
typedef long int ssize_t; typedef long int ssize_t;
/* The type of an offset into a file. */
typedef long int off_t;
#endif #endif

View File

@ -1,6 +1,8 @@
#ifndef _UNISTD_H #ifndef _UNISTD_H
#define _UNISTD_H #define _UNISTD_H
#include <bits/macros.h>
#include <bits/seek.h>
#include <sys/types.h> #include <sys/types.h>
#ifdef __cplusplus #ifdef __cplusplus
@ -8,16 +10,37 @@ extern "C"
{ {
#endif #endif
int execv(const char*, char* const[]); /* Executes the program program. On success, does not return. The second parameter (arguments) is not implemented
int execve(const char*, char* const[], char* const[]); * for now. You can pass NULL to it. */
int execvp(const char*, char* const[]); int execv(const char* program, char* const[]);
pid_t fork(void);
long syscall(long, ...);
unsigned int sleep(unsigned int);
ssize_t read(int, void*, size_t); /* Not implemented. */
ssize_t write(int, const void*, size_t); int execve(const char*, char* const[], char* const[]);
int close(int); /* Not implemented. */
int execvp(const char*, char* const[]);
/* Not implemented. */
pid_t fork(void);
/* Terminates the program with the status code status. */
noreturn void _exit(int status);
/* Calls the kernel for a specific service, determined by number. */
long syscall(long number, ...);
/* Suspends execution for a chosen amount of seconds. */
unsigned int sleep(unsigned int seconds);
/* Reads count bytes from the file descriptor fd into the memory at buf. */
ssize_t read(int fd, void* buf, size_t count);
/* Writes count bytes of the memory at buf to the file descriptor fd. */
ssize_t write(int fd, const void* buf, size_t count);
/* Closes the file descriptor fd. */
int close(int fd);
/* Moves the read/write file offset for fd to offset, depending on whence. */
off_t lseek(int fd, off_t offset, int whence);
#ifdef __cplusplus #ifdef __cplusplus
} }

25
libs/libc/src/atexit.cpp Normal file
View File

@ -0,0 +1,25 @@
#include <stdlib.h>
#include <unistd.h>
#define ATEXIT_MAX_FUNCS 32
typedef void (*atexit_func_t)(void);
atexit_func_t atexit_functions[ATEXIT_MAX_FUNCS];
int atexit_function_count = 0;
extern "C"
{
int atexit(atexit_func_t handler)
{
if (atexit_function_count >= ATEXIT_MAX_FUNCS) return -1;
atexit_functions[atexit_function_count++] = handler;
return 0;
}
noreturn void exit(int status)
{
for (int i = 0; i < atexit_function_count; i++) { atexit_functions[i](); }
_exit(status);
}
}

View File

@ -5,8 +5,8 @@
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
FILE* __stderr; FILE* stderr;
FILE* __stdout; FILE* stdout;
extern "C" extern "C"
{ {
@ -40,6 +40,19 @@ extern "C"
return stream; return stream;
} }
FILE* fdopen(int fd, const char*)
{
if (fd < 0) // FIXME: Also check if the mode string is compatible with how fd was opened.
{
errno = EBADF;
return 0;
}
FILE* stream = (FILE*)malloc(sizeof(FILE));
stream->f_fd = fd;
clearerr(stream);
return stream;
}
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);
@ -67,14 +80,37 @@ extern "C"
stream->f_err = stream->f_eof = 0; stream->f_err = stream->f_eof = 0;
} }
int fseek(FILE*, long, int) int fseek(FILE* stream, long offset, int whence)
{ {
NOT_IMPLEMENTED("fseek"); long result = lseek(stream->f_fd, offset, whence);
if (result < 0) { return -1; }
return 0;
} }
long ftell(FILE*) int fsetpos(FILE* stream, const fpos_t* pos)
{ {
NOT_IMPLEMENTED("ftell"); return fseek(stream, pos->f_offset, SEEK_SET);
}
long ftell(FILE* stream)
{
return lseek(stream->f_fd, 0,
SEEK_CUR); // FIXME: Store the last seeked position in the file struct to avoid redundant syscalls
// maybe? We'd have to update this value in fread() and fwrite() as well...
}
int fgetpos(FILE* stream, fpos_t* pos)
{
long result = ftell(stream);
if (result < 0) { return -1; }
pos->f_offset = result;
return 0;
}
void rewind(FILE* stream)
{
lseek(stream->f_fd, 0, SEEK_SET);
clearerr(stream);
} }
size_t fwrite(const void* buf, size_t size, size_t nmemb, FILE* stream) size_t fwrite(const void* buf, size_t size, size_t nmemb, FILE* stream)

View File

@ -2,13 +2,30 @@
#include <fcntl.h> #include <fcntl.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h>
static void terminate_libc()
{
fclose(stdout);
fclose(stderr);
}
static void check_for_file(int fd, FILE** target_stream, const char* path, const char* mode)
{
if (lseek(fd, 0, SEEK_CUR) < 0)
{
if (errno == EBADF) *target_stream = fopen(path, mode);
else
exit(errno);
if (!*target_stream) exit(errno);
errno = 0;
}
else { *target_stream = fdopen(fd, mode); }
}
extern "C" void initialize_libc() extern "C" void initialize_libc()
{ {
__stderr = fopen("/dev/console", "rw"); check_for_file(0, &stdout, "/dev/console", "rw");
if (!stderr) exit(errno); check_for_file(1, &stderr, "/dev/console", "rw");
__stdout = fopen("/dev/console", "rw"); atexit(terminate_libc);
if (!stdout) exit(errno);
clearerr(__stderr);
clearerr(__stdout);
} }

View File

@ -5,8 +5,6 @@
#include <sys/syscall.h> #include <sys/syscall.h>
#include <unistd.h> #include <unistd.h>
#define noreturn __attribute__((noreturn))
extern "C" extern "C"
{ {
pid_t gettid() pid_t gettid()
@ -21,7 +19,7 @@ extern "C"
noreturn void __luna_abort(const char* message) noreturn void __luna_abort(const char* message)
{ {
fwrite(message, strlen(message), 1, stdout); fputs(message, stderr);
abort(); abort();
} }
} }

View File

@ -42,7 +42,7 @@ long int __luna_syscall5(long int sys_num, unsigned long int arg0, unsigned long
{ {
long int result; long int result;
register unsigned long int value0 asm("r10") = arg3; register unsigned long int value0 asm("r10") = arg3;
register unsigned long int value1 asm("r10") = arg4; register unsigned long int value1 asm("r8") = arg4;
asm volatile("int $0x42" : "=a"(result) : "a"(sys_num), "D"(arg0), "S"(arg1), "d"(arg2), "r"(value0), "r"(value1)); asm volatile("int $0x42" : "=a"(result) : "a"(sys_num), "D"(arg0), "S"(arg1), "d"(arg2), "r"(value0), "r"(value1));
return result; return result;
} }

View File

@ -10,10 +10,31 @@ extern "C"
{ {
int puts(const char* s) int puts(const char* s)
{ {
long nwritten = fwrite(s, strlen(s), 1, stdout); int nwritten = fputs(s, stdout);
if (nwritten < 0) return -1; if (nwritten < 0) return -1;
nwritten += fwrite("\n", 1, 1, stdout); if (putchar('\n') < 0) return -1;
return (int)nwritten; return nwritten + 1;
}
int fputs(const char* s, FILE* stream)
{
int result = (int)fwrite(s, strlen(s), 1, stream);
if (ferror(stream)) return -1;
return result;
}
int fputc(int c, FILE* stream)
{
char chr = (char)c;
fwrite(&chr, 1, 1, stream);
if (ferror(stream)) { return -1; }
return c;
}
int putc(int c, FILE* stream)
{
return fputc(c, stream);
}
int putchar(int c)
{
return fputc(c, stdout);
} }
void perror(const char* s) void perror(const char* s)
{ {

View File

@ -3,32 +3,18 @@
#include <sys/syscall.h> #include <sys/syscall.h>
#include <unistd.h> #include <unistd.h>
#define noreturn __attribute__((noreturn))
#define maybe_unused __attribute__((maybe_unused))
#define unused __attribute__((unused))
extern "C" extern "C"
{ {
noreturn void abort() noreturn void abort()
{ {
exit(-1); _exit(-1);
} }
noreturn void exit(int status)
{
syscall(SYS_exit, status);
__builtin_unreachable();
}
int atexit(void (*)(void))
{
NOT_IMPLEMENTED("atexit");
}
int atoi(const char*) int atoi(const char*)
{ {
NOT_IMPLEMENTED("atoi"); NOT_IMPLEMENTED("atoi");
} }
char* getenv(const char*) char* getenv(const char*)
{ {
NOT_IMPLEMENTED("getenv"); NOT_IMPLEMENTED("getenv");

View File

@ -10,18 +10,18 @@ extern "C"
return dest; return dest;
} }
void* memset(void* dest, int c, size_t n) void* memset(void* buf, int c, size_t n)
{ {
for (size_t i = 0; i < n; ++i) { *((char*)dest + i) = (char)c; } for (size_t i = 0; i < n; ++i) { *((char*)buf + i) = (char)c; }
return dest; return buf;
} }
size_t strlen(const char* s) size_t strlen(const char* str)
{ {
const char* i = s; const char* i = str;
for (; *i; ++i) for (; *i; ++i)
; ;
return (i - s); return (i - str);
} }
char* strcpy(char* dest, const char* src) char* strcpy(char* dest, const char* src)
@ -30,10 +30,10 @@ extern "C"
return dest; return dest;
} }
char* strncpy(char* dest, const char* src, size_t n) char* strncpy(char* dest, const char* src, size_t max) // FIXME: Implement strncpy according to the specification.
{ {
size_t src_len = strlen(src) + 1; // NULL byte size_t src_len = strlen(src) + 1; // NULL byte
memcpy(dest, src, src_len > n ? n : src_len); memcpy(dest, src, src_len > max ? max : src_len);
return dest; return dest;
} }
@ -49,40 +49,40 @@ extern "C"
return dest; return dest;
} }
char* strncat(char* dest, const char* src, size_t n) char* strncat(char* dest, const char* src, size_t max)
{ {
size_t dest_len = strlen(dest); size_t dest_len = strlen(dest);
size_t i; size_t i;
for (i = 0; i < n && *(src + i); i++) *(char*)(dest + dest_len + i) = *(const char*)(src + i); for (i = 0; i < max && *(src + i); i++) *(char*)(dest + dest_len + i) = *(const char*)(src + i);
*(char*)(dest + dest_len + i) = '\0'; *(char*)(dest + dest_len + i) = '\0';
return dest; return dest;
} }
char* strchr(const char* str, int chr) char* strchr(const char* str, int c)
{ {
while (*str && *str != (char)chr) str++; while (*str && *str != (char)c) str++;
if (*str) return const_cast<char*>(str); if (*str) return const_cast<char*>(str);
return NULL; return NULL;
} }
void* memclr(void* start, size_t count) void* memclr(void* buf, size_t n)
{ {
// "i" is our counter of how many bytes we've cleared // "i" is our counter of how many bytes we've cleared
size_t i; size_t i;
// find out if "m_start" is aligned on a SSE_XMM_SIZE boundary // find out if "m_start" is aligned on a SSE_XMM_SIZE boundary
if ((size_t)start & (15)) if ((size_t)buf & (15))
{ {
i = 0; i = 0;
// we need to clear byte-by-byte until "m_start" is aligned on an SSE_XMM_SIZE boundary // 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) // ... and lets make sure we don't copy 'too' many bytes (i < m_count)
while (((size_t)start + i) & (15) && i < count) while (((size_t)buf + i) & (15) && i < n)
{ {
asm("stosb;" ::"D"((size_t)start + i), "a"(0)); asm("stosb;" ::"D"((size_t)buf + i), "a"(0));
i++; i++;
} }
} }
@ -93,42 +93,44 @@ extern "C"
} }
// clear 64-byte chunks of memory (4 16-byte operations) // clear 64-byte chunks of memory (4 16-byte operations)
for (; i + 64 <= count; i += 64) for (; i + 64 <= n; i += 64)
{ {
asm volatile(" pxor %%xmm0, %%xmm0; " // set XMM0 to 0 asm volatile(" pxor %%xmm0, %%xmm0; " // set XMM0 to 0
" movdqa %%xmm0, 0(%0); " // move 16 bytes from XMM0 to %0 + 0 " movdqa %%xmm0, 0(%0); " // move 16 bytes from XMM0 to %0 + 0
" movdqa %%xmm0, 16(%0); " " movdqa %%xmm0, 16(%0); "
" movdqa %%xmm0, 32(%0); " " movdqa %%xmm0, 32(%0); "
" movdqa %%xmm0, 48(%0); " ::"r"((size_t)start + i)); " movdqa %%xmm0, 48(%0); " ::"r"((size_t)buf + i));
} }
// copy the remaining bytes (if any) // copy the remaining bytes (if any)
asm(" rep stosb; " ::"a"((size_t)(0)), "D"(((size_t)start) + i), "c"(count - i)); 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" will contain the total amount of bytes that were actually transfered
i += count - i; i += n - i;
// we return "m_start" + the amount of bytes that were transfered // we return "m_start" + the amount of bytes that were transfered
return (void*)(((size_t)start) + i); return (void*)(((size_t)buf) + i);
} }
#pragma GCC push_options #pragma GCC push_options
#pragma GCC diagnostic ignored "-Wwrite-strings" #pragma GCC diagnostic ignored "-Wwrite-strings"
char* strerror(int errnum) char* strerror(int err)
{ {
switch (errnum) switch (err)
{ {
case EPERM: return "Operation not permitted"; case EPERM: return "Operation not permitted";
case EINVAL: return "Invalid argument"; case EINVAL: return "Invalid argument";
case ENOMEM: return "Out of memory"; case ENOMEM: return "Cannot allocate memory";
case ENOSYS: return "Function not implemented"; case ENOSYS: return "Function not implemented";
case ENOENT: return "No such file or directory"; case ENOENT: return "No such file or directory";
case EBADF: return "Bad file descriptor"; case EBADF: return "Bad file descriptor";
case EMFILE: return "Too many open files"; case EMFILE: return "Too many open files";
case EISDIR: return "Is a directory"; case EISDIR: return "Is a directory";
case ENOEXEC: return "Exec format error";
case EFAULT: return "Bad address";
case 0: return "Success"; case 0: return "Success";
default: return (char*)(unsigned long int)errnum; default: return (char*)(unsigned long int)err;
} }
} }

View File

@ -6,9 +6,9 @@
extern "C" extern "C"
{ {
int execv(const char*, char* const[]) int execv(const char* program, char* const[])
{ {
NOT_IMPLEMENTED("execv"); return (int)syscall(SYS_exec, program);
} }
int execve(const char*, char* const[], char* const[]) int execve(const char*, char* const[], char* const[])
{ {
@ -36,6 +36,7 @@ extern "C"
case SYS_rand: result = __luna_syscall0(number); break; case SYS_rand: result = __luna_syscall0(number); break;
case SYS_exit: case SYS_exit:
case SYS_close: case SYS_close:
case SYS_exec:
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: {
@ -44,6 +45,7 @@ extern "C"
result = __luna_syscall2(number, arg0, arg1); result = __luna_syscall2(number, arg0, arg1);
break; break;
} }
case SYS_seek:
case SYS_write: case SYS_write:
case SYS_read: case SYS_read:
case SYS_mmap: { case SYS_mmap: {
@ -88,4 +90,15 @@ extern "C"
{ {
return (int)syscall(SYS_close, fd); return (int)syscall(SYS_close, fd);
} }
off_t lseek(int fd, off_t offset, int whence)
{
return syscall(SYS_seek, fd, offset, whence);
}
noreturn void _exit(int status)
{
syscall(SYS_exit, status);
__builtin_unreachable();
}
} }

0
tools/build-debug.sh Normal file → Executable file
View File

0
tools/gdb.sh Normal file → Executable file
View File