kernel: Add the fork() system call
This commit is contained in:
parent
e664af4c2b
commit
54f2d35416
10
apps/init.c
10
apps/init.c
@ -37,6 +37,12 @@ int main()
|
||||
|
||||
fprintf(stderr, "init is running as PID %d\n", getpid());
|
||||
|
||||
char* argv[] = { "/bin/hello", "--help", NULL };
|
||||
execv("/bin/hello", argv);
|
||||
long ret = syscall(SYS_fork);
|
||||
|
||||
if (ret == 0)
|
||||
{
|
||||
char* argv[] = { "/bin/hello", "--help", NULL };
|
||||
execv("/bin/hello", argv);
|
||||
}
|
||||
else { printf("my child is PID %ld!\n", ret); }
|
||||
}
|
||||
|
@ -42,6 +42,7 @@ namespace MMU
|
||||
|
||||
Result<PageDirectory*> create_page_directory_for_userspace();
|
||||
Result<void> delete_userspace_page_directory(PageDirectory* directory);
|
||||
Result<PageDirectory*> clone_userspace_page_directory(PageDirectory* directory);
|
||||
void setup_initial_page_directory();
|
||||
|
||||
PageDirectory* kernel_page_directory();
|
||||
|
@ -381,6 +381,75 @@ namespace MMU
|
||||
return {};
|
||||
}
|
||||
|
||||
// FIXME: Use the ancient magic of CoW (copy-on-write)
|
||||
Result<PageDirectory*> clone_userspace_page_directory(PageDirectory* directory)
|
||||
{
|
||||
PageDirectory* result = TRY(create_page_directory_for_userspace());
|
||||
|
||||
PageDirectory* const old_table = translate_physical(directory);
|
||||
PageDirectory* const new_table = translate_physical(result);
|
||||
|
||||
// FIXME: Do not leak the WIP new page directory on OOM.
|
||||
|
||||
memcpy(new_table, old_table, sizeof(*old_table));
|
||||
|
||||
// Let's iterate over every top-level entry in the lower half
|
||||
for (u64 i = 0; i < 256; i++)
|
||||
{
|
||||
PageTableEntry& old_l4 = old_table->entries[i];
|
||||
if (!old_l4.present) continue;
|
||||
PageTableEntry& new_l4 = new_table->entries[i];
|
||||
new_l4.set_address(TRY(MemoryManager::alloc_frame()));
|
||||
|
||||
PageDirectory* const old_pdp = &page_table(old_l4);
|
||||
PageDirectory* const new_pdp = &page_table(new_l4);
|
||||
|
||||
memcpy(new_pdp, old_pdp, sizeof(*old_pdp));
|
||||
|
||||
for (u64 j = 0; j < 512; j++)
|
||||
{
|
||||
PageTableEntry& old_l3 = old_pdp->entries[j];
|
||||
if (!old_l3.present) continue;
|
||||
PageTableEntry& new_l3 = new_pdp->entries[j];
|
||||
new_l3.set_address(TRY(MemoryManager::alloc_frame()));
|
||||
|
||||
PageDirectory* const old_pd = &page_table(old_l3);
|
||||
PageDirectory* const new_pd = &page_table(new_l3);
|
||||
|
||||
memcpy(new_pd, old_pd, sizeof(*old_pd));
|
||||
|
||||
if (old_l3.larger_pages) continue;
|
||||
|
||||
for (u64 k = 0; k < 512; k++)
|
||||
{
|
||||
PageTableEntry& old_l2 = old_pd->entries[k];
|
||||
if (!old_l2.present) continue;
|
||||
PageTableEntry& new_l2 = new_pd->entries[k];
|
||||
new_l2.set_address(TRY(MemoryManager::alloc_frame()));
|
||||
|
||||
PageDirectory* const old_pt = &page_table(old_l2);
|
||||
PageDirectory* const new_pt = &page_table(new_l2);
|
||||
|
||||
memcpy(new_pt, old_pt, sizeof(*old_pt));
|
||||
|
||||
if (old_l2.larger_pages) continue;
|
||||
|
||||
for (u64 l = 0; l < 512; l++)
|
||||
{
|
||||
PageTableEntry& old_l1 = old_pt->entries[l];
|
||||
if (!old_l1.present) continue;
|
||||
PageTableEntry& new_l1 = new_pt->entries[l];
|
||||
new_l1.set_address(TRY(MemoryManager::alloc_frame()));
|
||||
|
||||
memcpy(&page_table(new_l1), &page_table(old_l1), ARCH_PAGE_SIZE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
PageDirectory* kernel_page_directory()
|
||||
{
|
||||
return g_kernel_directory;
|
||||
|
@ -26,6 +26,11 @@ u64 Thread::sp()
|
||||
return regs.rsp;
|
||||
}
|
||||
|
||||
void Thread::set_return(u64 ret)
|
||||
{
|
||||
regs.rax = ret;
|
||||
}
|
||||
|
||||
void Thread::init_regs_kernel()
|
||||
{
|
||||
memset(®s, 0, sizeof(Registers));
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include "Log.h"
|
||||
#include "arch/MMU.h"
|
||||
#include "memory/Heap.h"
|
||||
#include <luna/CString.h>
|
||||
#include <luna/ScopeGuard.h>
|
||||
|
||||
static constexpr u64 VM_BASE = 0x10000000;
|
||||
@ -22,6 +23,21 @@ Result<OwnedPtr<UserVM>> UserVM::try_create()
|
||||
return move(ptr);
|
||||
}
|
||||
|
||||
Result<OwnedPtr<UserVM>> UserVM::clone()
|
||||
{
|
||||
void* const base = TRY(kmalloc(m_bitmap.size_in_bytes()));
|
||||
|
||||
auto guard = make_scope_guard([&] { kfree(base); });
|
||||
|
||||
OwnedPtr<UserVM> ptr = TRY(make_owned<UserVM>(base, m_bitmap.size_in_bytes()));
|
||||
|
||||
memcpy(ptr->m_bitmap.location(), m_bitmap.location(), m_bitmap.size_in_bytes());
|
||||
|
||||
guard.deactivate();
|
||||
|
||||
return move(ptr);
|
||||
}
|
||||
|
||||
UserVM::UserVM(void* base, usize size)
|
||||
{
|
||||
kdbgln("user vm created with base=%p, size=%zu", base, size);
|
||||
|
@ -17,6 +17,8 @@ class UserVM
|
||||
|
||||
static Result<OwnedPtr<UserVM>> try_create();
|
||||
|
||||
Result<OwnedPtr<UserVM>> clone();
|
||||
|
||||
private:
|
||||
Result<bool> try_expand(usize size = 160);
|
||||
Bitmap m_bitmap;
|
||||
|
@ -93,3 +93,34 @@ Result<u64> sys_exec(Registers* regs, SyscallArgs args)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result<u64> sys_fork(Registers* regs, SyscallArgs)
|
||||
{
|
||||
auto current = Scheduler::current();
|
||||
|
||||
auto guard = make_scope_guard([current] { MMU::switch_page_directory(current->directory); });
|
||||
|
||||
kinfoln("fork: trying to duplicate process %lu", current->id);
|
||||
|
||||
memcpy(¤t->regs, regs, sizeof(*regs));
|
||||
|
||||
auto image = TRY(ThreadImage::clone_from_thread(current));
|
||||
|
||||
auto thread = TRY(new_thread());
|
||||
|
||||
thread->state = ThreadState::Runnable;
|
||||
thread->is_kernel = false;
|
||||
thread->fp_data.save();
|
||||
|
||||
for (int i = 0; i < FD_MAX; i++) { thread->fd_table[i] = current->fd_table[i]; }
|
||||
|
||||
image->apply(thread);
|
||||
|
||||
memcpy(&thread->regs, regs, sizeof(*regs));
|
||||
|
||||
thread->set_return(0);
|
||||
|
||||
Scheduler::add_thread(thread);
|
||||
|
||||
return thread->id;
|
||||
}
|
||||
|
@ -100,6 +100,7 @@ namespace Scheduler
|
||||
|
||||
return new_kernel_thread_impl(thread);
|
||||
}
|
||||
|
||||
Result<void> new_userspace_thread(SharedPtr<VFS::Inode> inode)
|
||||
{
|
||||
Thread* const thread = TRY(new_thread());
|
||||
@ -122,8 +123,15 @@ namespace Scheduler
|
||||
return {};
|
||||
}
|
||||
|
||||
void add_thread(Thread* thread)
|
||||
{
|
||||
g_threads.append(thread);
|
||||
}
|
||||
|
||||
void reap_thread(Thread* thread)
|
||||
{
|
||||
CPU::disable_interrupts();
|
||||
|
||||
kinfoln("reap: reaping thread with id %zu", thread->id);
|
||||
|
||||
if (thread->is_kernel)
|
||||
@ -144,6 +152,8 @@ namespace Scheduler
|
||||
if (!thread->is_kernel) MMU::delete_userspace_page_directory(thread->directory);
|
||||
|
||||
delete thread;
|
||||
|
||||
CPU::enable_interrupts();
|
||||
}
|
||||
|
||||
Thread* pick_task()
|
||||
|
@ -15,6 +15,8 @@ namespace Scheduler
|
||||
|
||||
Result<void> new_userspace_thread(SharedPtr<VFS::Inode> inode);
|
||||
|
||||
void add_thread(Thread* thread);
|
||||
|
||||
Thread* pick_task();
|
||||
|
||||
void reap_thread(Thread* thread);
|
||||
|
@ -81,6 +81,8 @@ struct Thread : public LinkedListNode<Thread>
|
||||
void set_sp(u64 sp);
|
||||
u64 sp();
|
||||
|
||||
void set_return(u64 ret);
|
||||
|
||||
static void init();
|
||||
};
|
||||
|
||||
|
@ -56,6 +56,29 @@ Result<OwnedPtr<ThreadImage>> ThreadImage::try_load_from_elf(SharedPtr<VFS::Inod
|
||||
return image;
|
||||
}
|
||||
|
||||
Result<OwnedPtr<ThreadImage>> ThreadImage::clone_from_thread(Thread* parent)
|
||||
{
|
||||
auto image = TRY(make_owned<ThreadImage>());
|
||||
|
||||
auto vm_allocator = TRY(parent->vm_allocator->clone());
|
||||
|
||||
auto new_directory = TRY(MMU::clone_userspace_page_directory(parent->directory));
|
||||
|
||||
const ELFData data = { .entry = parent->ip() };
|
||||
|
||||
const u64 kernel_stack_base = TRY(MemoryManager::alloc_for_kernel(4, MMU::ReadWrite | MMU::NoExecute));
|
||||
Stack kernel_stack { kernel_stack_base, 4 * ARCH_PAGE_SIZE };
|
||||
|
||||
image->m_directory = new_directory;
|
||||
image->m_kernel_stack = kernel_stack;
|
||||
image->m_user_stack = parent->stack;
|
||||
image->m_loaded_image_data = data;
|
||||
image->m_vm_allocator = move(vm_allocator);
|
||||
image->m_sp = parent->sp();
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
Result<u64> ThreadImage::push_mem_on_stack(const u8* mem, usize size)
|
||||
{
|
||||
if ((m_sp - size) < m_user_stack.bottom()) return err(E2BIG);
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "arch/MMU.h"
|
||||
#include "fs/VFS.h"
|
||||
#include "memory/UserVM.h"
|
||||
#include "thread/Thread.h"
|
||||
#include <luna/LinkedList.h>
|
||||
#include <luna/OwnedPtr.h>
|
||||
#include <luna/Result.h>
|
||||
@ -17,6 +18,8 @@ class ThreadImage
|
||||
public:
|
||||
static Result<OwnedPtr<ThreadImage>> try_load_from_elf(SharedPtr<VFS::Inode> inode);
|
||||
|
||||
static Result<OwnedPtr<ThreadImage>> clone_from_thread(Thread* parent);
|
||||
|
||||
Result<u64> push_mem_on_stack(const u8* mem, usize size);
|
||||
|
||||
void apply(Thread* thread);
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
#define enumerate_syscalls(_e) \
|
||||
_e(exit) _e(clock_gettime) _e(mmap) _e(munmap) _e(usleep) _e(open) _e(close) _e(read) _e(getpid) _e(write) \
|
||||
_e(lseek) _e(mkdir) _e(exec) _e(mknod)
|
||||
_e(lseek) _e(mkdir) _e(exec) _e(mknod) _e(fork)
|
||||
|
||||
enum Syscalls
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user