From 6fdbe6f9c232ab6098dff830f4bc092d72404417 Mon Sep 17 00:00:00 2001 From: Bananymous Date: Sun, 28 May 2023 18:08:26 +0300 Subject: [PATCH] Kernel: Add bareboness fork() function --- kernel/CMakeLists.txt | 1 + kernel/include/kernel/Arch.h | 6 ++++- kernel/include/kernel/Process.h | 2 ++ kernel/include/kernel/Thread.h | 2 ++ kernel/kernel/Memory/VirtualRange.cpp | 2 ++ kernel/kernel/Process.cpp | 38 +++++++++++++++++++++++++-- kernel/kernel/Scheduler.cpp | 1 - kernel/kernel/Syscall.S | 22 ++++++++++++++++ kernel/kernel/Syscall.cpp | 13 +++++++++ kernel/kernel/Thread.cpp | 27 ++++++++++++++++++- kernel/kernel/kernel.cpp | 4 +-- libc/include/sys/syscall.h | 1 + libc/unistd.cpp | 27 +++++-------------- userspace/test/test.cpp | 23 +++++----------- 14 files changed, 125 insertions(+), 44 deletions(-) create mode 100644 kernel/kernel/Syscall.S diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index ccb7416d..ec88a26b 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -48,6 +48,7 @@ set(KERNEL_SOURCES kernel/Storage/ATADevice.cpp kernel/Storage/StorageDevice.cpp kernel/Syscall.cpp + kernel/Syscall.S kernel/Thread.cpp kernel/Terminal/TTY.cpp kernel/Terminal/VesaTerminalDriver.cpp diff --git a/kernel/include/kernel/Arch.h b/kernel/include/kernel/Arch.h index 1fb3295d..0a31be74 100644 --- a/kernel/include/kernel/Arch.h +++ b/kernel/include/kernel/Arch.h @@ -17,4 +17,8 @@ #define read_rsp(rsp) asm volatile("movl %%esp, %0" : "=r"(rsp)) #define push_callee_saved() asm volatile("pushal") #define pop_callee_saved() asm volatile("popal") -#endif \ No newline at end of file +#endif + +#include + +extern "C" uintptr_t read_rip(); diff --git a/kernel/include/kernel/Process.h b/kernel/include/kernel/Process.h index 38664d80..49fd8708 100644 --- a/kernel/include/kernel/Process.h +++ b/kernel/include/kernel/Process.h @@ -41,6 +41,8 @@ namespace Kernel pid_t pid() const { return m_pid; } + BAN::ErrorOr fork(uintptr_t rsp, uintptr_t rip); + BAN::ErrorOr open(BAN::StringView, int); BAN::ErrorOr close(int fd); BAN::ErrorOr read(int fd, void* buffer, size_t count); diff --git a/kernel/include/kernel/Thread.h b/kernel/include/kernel/Thread.h index 3ab0ebba..7643ed3b 100644 --- a/kernel/include/kernel/Thread.h +++ b/kernel/include/kernel/Thread.h @@ -31,6 +31,8 @@ namespace Kernel static BAN::ErrorOr create_userspace(uintptr_t, Process*, int, char**); ~Thread(); + BAN::ErrorOr clone(Process*, uintptr_t rsp, uintptr_t rip); + pid_t tid() const { return m_tid; } void set_rsp(uintptr_t rsp) { m_rsp = rsp; validate_stack(); } diff --git a/kernel/kernel/Memory/VirtualRange.cpp b/kernel/kernel/Memory/VirtualRange.cpp index ba1d4023..68f087c8 100644 --- a/kernel/kernel/Memory/VirtualRange.cpp +++ b/kernel/kernel/Memory/VirtualRange.cpp @@ -83,9 +83,11 @@ namespace Kernel for (size_t i = 0; i < result->m_physical_pages.size(); i++) { m_mmu.map_page_at(result->m_physical_pages[i], 0, MMU::Flags::ReadWrite | MMU::Flags::Present); + m_mmu.invalidate(0); memcpy((void*)0, (void*)(vaddr() + i * PAGE_SIZE), PAGE_SIZE); } m_mmu.unmap_page(0); + m_mmu.invalidate(0); return result; } diff --git a/kernel/kernel/Process.cpp b/kernel/kernel/Process.cpp index a7830c52..425aa650 100644 --- a/kernel/kernel/Process.cpp +++ b/kernel/kernel/Process.cpp @@ -129,6 +129,8 @@ namespace Kernel MMU::kernel().load(); delete m_mmu; } + + dprintln("process {} exit", pid()); } void Process::add_thread(Thread* thread) @@ -167,8 +169,6 @@ namespace Kernel m_general_allocator = nullptr; } - dprintln("process {} exit", pid()); - s_process_lock.lock(); for (size_t i = 0; i < s_processes.size(); i++) if (s_processes[i] == this) @@ -197,6 +197,40 @@ namespace Kernel return {}; } + BAN::ErrorOr Process::fork(uintptr_t rsp, uintptr_t rip) + { + LockGuard _(m_lock); + + Process* forked = create_process(); + + forked->m_tty = m_tty; + forked->m_working_directory = m_working_directory; + + forked->m_open_files = m_open_files; + + forked->m_mmu = new MMU(); + ASSERT(forked->m_mmu); + + for (auto* mapped_range : m_mapped_ranges) + MUST(forked->m_mapped_ranges.push_back(mapped_range->clone(forked->mmu()))); + + ASSERT(m_threads.size() == 1); + ASSERT(m_threads.front() == &Thread::current()); + + //for (auto& allocator : m_fixed_width_allocators) + // MUST(forked->m_fixed_width_allocators.push_back(allocator.clone())); + + //if (m_general_allocator) + // forked->m_general_allocator = m_general_allocator->clone(); + + Thread* thread = MUST(m_threads.front()->clone(forked, rsp, rip)); + forked->add_thread(thread); + + register_process(forked); + + return forked; + } + BAN::ErrorOr Process::open(BAN::StringView path, int flags) { if (flags & ~O_RDWR) diff --git a/kernel/kernel/Scheduler.cpp b/kernel/kernel/Scheduler.cpp index a246713e..d678c0ed 100644 --- a/kernel/kernel/Scheduler.cpp +++ b/kernel/kernel/Scheduler.cpp @@ -19,7 +19,6 @@ namespace Kernel extern "C" [[noreturn]] void start_thread(uintptr_t rsp, uintptr_t rip); extern "C" [[noreturn]] void continue_thread(uintptr_t rsp, uintptr_t rip); - extern "C" uintptr_t read_rip(); static Scheduler* s_instance = nullptr; diff --git a/kernel/kernel/Syscall.S b/kernel/kernel/Syscall.S new file mode 100644 index 00000000..4dc203c0 --- /dev/null +++ b/kernel/kernel/Syscall.S @@ -0,0 +1,22 @@ +.global sys_fork_trampoline +sys_fork_trampoline: + pushq %rbx + pushq %rbp + pushq %r12 + pushq %r13 + pushq %r14 + pushq %r15 + call read_rip + testq %rax, %rax + je .done + movq %rax, %rsi + movq %rsp, %rdi + call sys_fork +.done: + popq %r15 + popq %r14 + popq %r13 + popq %r12 + popq %rbp + popq %rbx + ret diff --git a/kernel/kernel/Syscall.cpp b/kernel/kernel/Syscall.cpp index 483bf33e..b7157903 100644 --- a/kernel/kernel/Syscall.cpp +++ b/kernel/kernel/Syscall.cpp @@ -98,6 +98,16 @@ namespace Kernel return 0; } + extern "C" long sys_fork(uintptr_t rsp, uintptr_t rip) + { + auto ret = Process::current().fork(rsp, rip); + if (ret.is_error()) + return -ret.error().get_error_code(); + return ret.value()->pid(); + } + + extern "C" long sys_fork_trampoline(); + extern "C" long cpp_syscall_handler(int syscall, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, uintptr_t arg4, uintptr_t arg5) { Thread::current().set_in_syscall(true); @@ -149,6 +159,9 @@ namespace Kernel case SYS_SET_TERMIOS: ret = sys_set_termios((const ::termios*)arg1); break; + case SYS_FORK: + ret = sys_fork_trampoline(); + break; default: Kernel::panic("Unknown syscall {}", syscall); } diff --git a/kernel/kernel/Thread.cpp b/kernel/kernel/Thread.cpp index 55af6ea4..056d010e 100644 --- a/kernel/kernel/Thread.cpp +++ b/kernel/kernel/Thread.cpp @@ -111,13 +111,38 @@ namespace Kernel Thread::~Thread() { - dprintln("thread {} ({}) exit", tid(), m_process->pid()); if (m_stack) delete m_stack; m_stack = nullptr; if (m_interrupt_stack) delete m_interrupt_stack; m_interrupt_stack = nullptr; + + dprintln("thread {} ({}) exit", tid(), m_process->pid()); + } + + BAN::ErrorOr Thread::clone(Process* new_process, uintptr_t rsp, uintptr_t rip) + { + ASSERT(m_is_userspace); + ASSERT(m_state == State::Executing); + + Thread* thread = new Thread(s_next_tid++, new_process); + if (thread == nullptr) + return BAN::Error::from_errno(ENOMEM); + thread->m_is_userspace = true; + + thread->m_interrupt_stack = m_interrupt_stack->clone(new_process->mmu()); + thread->m_stack = m_stack->clone(new_process->mmu()); + + thread->m_state = State::Executing; + thread->m_in_syscall = true; + + thread->m_rip = rip; + thread->m_rsp = rsp; + + thread->m_userspace_entry = {}; + + return thread; } void Thread::validate_stack() const diff --git a/kernel/kernel/kernel.cpp b/kernel/kernel/kernel.cpp index b1df11d8..906c3671 100644 --- a/kernel/kernel/kernel.cpp +++ b/kernel/kernel/kernel.cpp @@ -126,7 +126,7 @@ extern "C" void kernel_main() dprintln("GDT initialized"); IDT::initialize(); - dprintln("IDT initialized"); + dprintln("IDT initialized"); MMU::initialize(); dprintln("MMU initialized"); @@ -180,7 +180,7 @@ static void init2(void* tty1) ((TTY*)tty1)->initialize_device(); - MUST(Process::create_userspace("/usr/bin/Shell"sv)); + MUST(Process::create_userspace("/usr/bin/test"sv)); return; Process::create_kernel( diff --git a/libc/include/sys/syscall.h b/libc/include/sys/syscall.h index 43917960..4fd4e125 100644 --- a/libc/include/sys/syscall.h +++ b/libc/include/sys/syscall.h @@ -17,6 +17,7 @@ __BEGIN_DECLS #define SYS_TELL 10 #define SYS_GET_TERMIOS 11 #define SYS_SET_TERMIOS 12 +#define SYS_FORK 13 __END_DECLS diff --git a/libc/unistd.cpp b/libc/unistd.cpp index 0c697a8c..898bb1c1 100644 --- a/libc/unistd.cpp +++ b/libc/unistd.cpp @@ -103,6 +103,11 @@ long syscall(long syscall, ...) ret = Kernel::syscall(SYS_SET_TERMIOS, (uintptr_t)termios); break; } + case SYS_FORK: + { + ret = Kernel::syscall(SYS_FORK); + break; + } default: puts("LibC: Unhandeled syscall"); ret = -ENOSYS; @@ -122,25 +127,5 @@ long syscall(long syscall, ...) pid_t fork(void) { - return -1; -} - -int execv(const char*, char* const[]) -{ - return -1; -} - -int execve(const char*, char* const[], char* const[]) -{ - return -1; -} - -int execvp(const char*, char* const[]) -{ - return -1; -} - -pid_t getpid(void) -{ - return -1; + return syscall(SYS_FORK); } diff --git a/userspace/test/test.cpp b/userspace/test/test.cpp index 9da8479b..8ba9b1e6 100644 --- a/userspace/test/test.cpp +++ b/userspace/test/test.cpp @@ -1,31 +1,22 @@ #include #include #include +#include #define ERROR(msg) { perror(msg); return 1; } #define BUF_SIZE 1024 int main() { - FILE* fp = fopen("/usr/include/stdio.h", "r"); - if (fp == NULL) - ERROR("fopen"); + printf("forking\n"); - char* buffer = (char*)malloc(BUF_SIZE); - if (buffer == NULL) - ERROR("malloc"); - - for (;;) + pid_t pid = fork(); + if (pid == 0) { - size_t n_read = fread(buffer, 1, BUF_SIZE - 1, fp); - if (n_read == 0) - break; - buffer[n_read] = '\0'; - printf("%s", buffer); + printf("child\n"); + return 0; } - - free(buffer); - fclose(fp); + printf("parent\n"); return 0; }