mirror of
https://github.com/usatiuk/ficus.git
synced 2025-10-29 00:27:52 +01:00
Seemingly working fork
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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!");
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user