Seemingly working fork

This commit is contained in:
2024-04-13 23:02:51 +02:00
parent 03777624fb
commit a6c551e443
10 changed files with 129 additions and 25 deletions

View File

@@ -4,18 +4,32 @@
#include <sys/errno.h>
#include <sys/fcntl.h>
#include <sys/stat.h>
#include <sys/syscalls.h>
#include <sys/time.h>
#include <sys/times.h>
#include <sys/types.h>
#include <sys/syscalls.h>
uint64_t _do_syscall(uint64_t id_rdi, uint64_t a1_rsi, uint64_t a2_rdx, uint64_t a3_rcx) {
uint64_t res;
asm volatile("syscall; mov (0x10016), %%rsp" // TASK_POINTER->ret_sp_val
: "=r"(res)
: "D"(id_rdi), "S"(a1_rsi), "d"(a2_rdx), "a"(a3_rcx)
: "cc", "rcx", "r8",
"r9", "r10", "r11", "r15", "memory");
register uint64_t res asm("rax");
if (id_rdi != SYSCALL_FORK_ID)
asm volatile("syscall; mov (0x10016), %%rsp;" // TASK_POINTER->ret_sp_val
: "=ra"(res)
: "D"(id_rdi), "S"(a1_rsi), "d"(a2_rdx), "a"(a3_rcx)
: "cc", "rcx", "r8",
"r9", "r10", "r11", "r15", "memory");
else
asm volatile("syscall; mov (0x10016), %%rsp;" // TASK_POINTER->ret_sp_val
"pop %%r15;"
"pop %%r14;"
"pop %%r13;"
"pop %%r12;"
"pop %%rbp;"
"pop %%rbx;"
: "=ra"(res)
: "D"(id_rdi), "S"(a1_rsi), "d"(a2_rdx), "a"(a3_rcx)
: "cc", "rcx", "r8",
"r9", "r10", "r11", "r15", "memory");
return res;
}
@@ -106,4 +120,3 @@ void print_mem() {
void print_tasks() {
_do_syscall(SYSCALL_PRINT_TASKS, 0, 0, 0);
}

View File

@@ -3,21 +3,36 @@
extern syscall_impl
section .text
global _syscall_entrypoint:function (_syscall_entrypoint.end - _syscall_entrypoint)
global _syscall_ret
global _syscall_entrypoint:function (_syscall_ret.end - _syscall_entrypoint)
_syscall_entrypoint:
; TODO: make it synced somehow
mov r15, 51
cmp rdi, r15
jne .not_fork
push rbx
push rbp
push r12
push r13
push r14
push r15
.not_fork:
mov [0x10016], rsp ; TASK_POINTER->ret_sp_val
mov [0x10024], r11 ; TASK_POINTER->ret_flags
mov [0x10032], rcx ; TASK_POINTER->ret_ip
mov rsp, [0x10008] ; TASK_POINTER->entry_ksp_val
mov r15, rcx ; We need to save rcx
mov rcx, rax
mov rcx, rax ; FIXME: Not needed anymore
sti
; Do very complicated stuff here
call syscall_impl
_syscall_ret:
cli
mov rcx, r15
mov r11, [0x10024] ; TASK_POINTER->ret_flags
mov rcx, [0x10032] ; TASK_POINTER->ret_ip
o64 sysret
.end:

View File

@@ -6,8 +6,8 @@
#include "VFSApi.hpp"
#include "VMA.hpp"
#include <sys/syscalls.h>
#include <sys/dirent.h>
#include <sys/syscalls.h>
#include <algorithm>
#include <cstdint>
@@ -24,6 +24,7 @@
#include "Vector.hpp"
#include "memman.hpp"
#include "paging.hpp"
#include "sys/types.h"
#include "task.hpp"
#include "timer.hpp"
@@ -245,6 +246,15 @@ int64_t syscall_getdents(int fd, struct dirent *dp, int count) {
return count * sizeof(dirent);
}
pid_t syscall_fork() {
Task *utask = Scheduler::cur_task()->clone();
utask->_frame.rax = 0;
utask->_frame.ip = (uint64_t) &_syscall_ret;
utask->start();
return utask->pid();
}
extern "C" uint64_t syscall_impl(uint64_t id_rdi, uint64_t a1_rsi, uint64_t a2_rdx, uint64_t a3_rcx) {
assert2(are_interrupts_enabled(), "why wouldn't they be?");
switch (id_rdi) {
@@ -268,6 +278,8 @@ extern "C" uint64_t syscall_impl(uint64_t id_rdi, uint64_t a1_rsi, uint64_t a2_r
return syscall_write(a1_rsi, reinterpret_cast<const char *>(a2_rdx), a3_rcx);
case SYSCALL_LSEEK_ID:
return syscall_lseek(a1_rsi, a2_rdx, a3_rcx);
case SYSCALL_FORK_ID:
return syscall_fork();
case SYSCALL_EXECVE_ID:
return syscall_execve(reinterpret_cast<const char *>(a1_rsi), reinterpret_cast<char *const *>(a2_rdx), reinterpret_cast<char *const *>(a3_rcx));
case SYSCALL_SBRK_ID:

View File

@@ -10,6 +10,9 @@
void setup_syscalls();
extern "C" void _syscall_entrypoint();
extern "C" void _syscall_ret();
extern "C" uint64_t syscall_impl(uint64_t id_rdi, uint64_t a1_rsi, uint64_t a2_rdx, uint64_t a3_rcx);
#endif //FICUS_SYSCALLS_HPP

View File

@@ -25,6 +25,8 @@
char temp_fxsave[512] __attribute__((aligned(16)));
void sanity_check_frame(Arch::TaskFrame *cur_frame) {
//FIXME: not quite useful with clone and exceptions
return;
// TODO: This makes sense to check when entering, but not when switching
// assert((((uintptr_t) cur_frame) & 0xFULL) == 0);
assert2((void *) cur_frame->ip != NULL, "Sanity check");
@@ -234,7 +236,7 @@ static void task_waker() {
WaitingTasks_mlock.lock();
while (WaitingTasks.begin() != WaitingTasks.end() && WaitingTasks.begin()->first <= micros && WaitingTasks.begin()->second->val->state() != Task::TaskState::TS_RUNNING) {
auto node = WaitingTasks.begin();
auto node = WaitingTasks.begin();
// FIXME:
auto node2 = node;
++node2;
@@ -265,6 +267,25 @@ static void task_waker() {
}
}
Task *Task::clone() {
Task *ret = new Task(TaskMode::TASKMODE_USER, nullptr, Scheduler::cur_task()->name().c_str());
ret->_vma->clone_from(*Scheduler::cur_task()->_vma);
task_pointer *taskptr_real = reinterpret_cast<task_pointer *>(HHDM_P2V(ret->_addressSpace->virt2real((void *) TASK_POINTER)));
_entry_ksp_val = ((((uintptr_t) _kstack->_ptr) + (TASK_SS - 9) - 1) & (~0xFULL)); // Ensure 16byte alignment
// It should be aligned before call, therefore it actually should be aligned here
assert((_entry_ksp_val & 0xFULL) == 0);
taskptr_real->taskptr = ret;
taskptr_real->entry_ksp_val = ret->_entry_ksp_val;
ret->_frame.ss = Arch::GDT::gdt_data.selector();
ret->_frame.cs = Arch::GDT::gdt_code.selector();
ret->_frame.sp = ret->_entry_ksp_val;
return ret;
}
void Scheduler::init_tasks() {
// FIXME: not actually thread-safe, but it probably doesn't matter
assert2(!atomic_load(&initialized), "Tasks should be initialized once!");

View File

@@ -33,10 +33,10 @@ public:
TS_BLOCKED
};
Task(TaskMode mode, void (*entrypoint)(), const char *name);
Task(TaskMode mode, void (*entrypoint)(), const char *name);
Task(const Task &) = delete;
Task(Task &&) = delete;
Task(const Task &) = delete;
Task(Task &&) = delete;
Task &operator=(const Task &) = delete;
Task &operator=(Task &&) = delete;
@@ -47,7 +47,9 @@ public:
[[nodiscard]] uint64_t used_time() const { return _used_time; }
[[nodiscard]] TaskState state() const { return _state; }
~Task();
~ Task();
Task *clone();
//private:
struct KernStack {
@@ -59,7 +61,7 @@ public:
} __attribute__((aligned(16)));
uint64_t _entry_ksp_val;
Arch::TaskFrame _frame;
Arch::TaskFrame _frame;
TaskPID _pid;
std::atomic<uint64_t> _used_time;
@@ -83,6 +85,7 @@ struct task_pointer {
uint64_t entry_ksp_val;
uint64_t ret_sp;
uint64_t ret_flags;
uint64_t ret_ip;
} __attribute__((packed));
namespace Scheduler {
@@ -108,7 +111,7 @@ namespace Scheduler {
// TODO: that's quite inefficient!
SkipListMap<uint64_t, std::pair<String, Task::TaskPID>> getTaskTimePerPid();
void yield_self();
void yield_self();
} // namespace Scheduler
// Expects the caller to save interrupt state

View File

@@ -14,7 +14,7 @@
VMA::VMA(AddressSpace *space) : space(space) {
LockGuard l(regions_lock);
regions.emplace(std::make_pair<uintptr_t, ListEntry>(0x1000, {0x1000, 0xFFF8000000000000ULL - 0x20000, EntryType::FREE}));
regions.emplace(std::make_pair<uintptr_t, ListEntry>(0x1000, {0x1000, 0xFFF8000000000000ULL - 0x20000, EntryType::FREE, PAGE_RW}));
}
VMA::ListEntry *VMA::get_entry(uintptr_t v_addr, size_t length) {
@@ -71,7 +71,8 @@ void *VMA::mmap_phys(void *v_addr, void *real_addr, size_t length, int flags) {
ListEntry *entry = get_entry(reinterpret_cast<uintptr_t>(v_addr), length);
if (!entry) return nullptr;
entry->type = EntryType::PHYS;
entry->type = EntryType::PHYS;
entry->flags = flags;
for (size_t i = 0; i < length; i += PAGE_SIZE) {
space->map((char *) v_addr + i, (char *) real_addr + i, flags);
@@ -93,6 +94,8 @@ void *VMA::mmap_mem(void *v_addr, size_t length, int prot, int flags) {
//
ListEntry *found = get_entry(reinterpret_cast<uintptr_t>(v_addr), length);
if (!found) return nullptr;
found->flags = flags;
found->type = EntryType::ANON;
for (int i = 0; i < page_len; i++) {
void *p = get4k();
@@ -117,3 +120,28 @@ VMA::~VMA() {
}
}
}
void VMA::clone_from(const VMA &vma) {
for (const auto &e: vma.regions) {
if (e.second.type == EntryType::ANON) {
assert((e.second.length & (PAGE_SIZE - 1)) == 0);
//FIXME:
if (auto p = regions.find(e.first); p != regions.end() && p->second.type != EntryType::FREE) {
assert(p->second.length == e.second.length);
} else
assert(mmap_mem((void *) e.first, e.second.length, 0, e.second.flags) == (void *) e.first);
uint64_t page_len = e.second.length / PAGE_SIZE;
for (int i = 0; i < page_len; i++) {
memcpy((char *) HHDM_P2V(space->virt2real(reinterpret_cast<void *>(e.second.begin + i * PAGE_SIZE))),
(char *) HHDM_P2V(vma.space->virt2real(reinterpret_cast<void *>(e.second.begin + i * PAGE_SIZE))),
PAGE_SIZE);
}
} else {
if (e.second.type != EntryType::FREE)
assert(false);
}
}
brk_end_fake = vma.brk_end_fake;
brk_end_real = vma.brk_end_real;
brk_start = vma.brk_start;
}

View File

@@ -37,6 +37,8 @@ public:
std::optional<char*> brk_end_fake;
std::optional<char*> brk_end_real;
void clone_from(const VMA& vma);
private:
AddressSpace *space = nullptr;
Mutex space_lock;
@@ -51,6 +53,7 @@ private:
uintptr_t begin;
uint64_t length;
EntryType type = EntryType::FREE;
int flags;
};
ListEntry *get_entry(uintptr_t v_addr, size_t length);

View File

@@ -1,6 +1,12 @@
#include <stdio.h>
#include <sys/unistd.h>
int main() {
printf("Hi!\n");
if (fork() == 0) {
printf("I'm a fork!\n");
} else {
printf("Forked!\n");
}
}

View File

@@ -6,8 +6,8 @@
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/wait.h>
#include <unistd.h>
void ls() {
DIR *rfd = opendir("/");
@@ -45,7 +45,7 @@ int main() {
print_tasks();
} else {
execve(line, 0, 0);
sleep(10000);
// sleep(10000);
}
}
}