diff --git a/apps/Makefile b/apps/Makefile index 97187f7e..dba694f6 100644 --- a/apps/Makefile +++ b/apps/Makefile @@ -11,7 +11,6 @@ CFLAGS := -Wall -Wextra -Werror -Os $(APPS_BIN)/%: $(APPS_SRC)/%.c @mkdir -p $(@D) $(CC) $(CFLAGS) -o $@ $^ - $(STRIP) $@ build: $(REAL_APPS) diff --git a/apps/src/init.c b/apps/src/init.c index f83fd1f7..29f51333 100644 --- a/apps/src/init.c +++ b/apps/src/init.c @@ -141,9 +141,13 @@ int main() fclose(new_stderr); - execv("/bin/sym", NULL); + pid_t child = fork(); + if (child == 0) + { + msleep(500); + printf("I am the child, who is my parent?\n"); + } + else { printf("My child is %ld!", child); } - perror("execv"); // If we're here, execv failed - - return 1; + return 0; } diff --git a/kernel/include/memory/AddressSpace.h b/kernel/include/memory/AddressSpace.h index ef40b774..151e35bd 100644 --- a/kernel/include/memory/AddressSpace.h +++ b/kernel/include/memory/AddressSpace.h @@ -7,8 +7,6 @@ struct AddressSpace void destroy(); - void detach(); - AddressSpace clone(); PageTable* get_pml4() @@ -16,13 +14,6 @@ struct AddressSpace return m_pml4; } - bool is_cloned() - { - return *m_refs > 1; - } - private: PageTable* m_pml4; - - int* m_refs; }; \ No newline at end of file diff --git a/kernel/include/std/string.h b/kernel/include/std/string.h index ac1a2459..7c0db986 100644 --- a/kernel/include/std/string.h +++ b/kernel/include/std/string.h @@ -13,6 +13,7 @@ int strncmp(const char* a, const char* b, size_t n); char* strncat(char* dest, const char* src, size_t n); char* strstr(char* haystack, const char* needle); +void* memmem(void* haystack, size_t haystacklen, const void* needle, size_t needlelen); void* memcpy(void* dest, const void* src, size_t n); void* memset(void* dest, int c, size_t n); diff --git a/kernel/include/sys/Syscall.h b/kernel/include/sys/Syscall.h index e92a795f..5653c67a 100644 --- a/kernel/include/sys/Syscall.h +++ b/kernel/include/sys/Syscall.h @@ -18,6 +18,7 @@ #define SYS_fcntl 13 #define SYS_mprotect 14 #define SYS_clock 15 +#define SYS_fork 16 namespace Syscall { @@ -41,4 +42,5 @@ void sys_seek(Context* context, int fd, long offset, int whence); void sys_exec(Context* context, const char* pathname); void sys_fcntl(Context* context, int fd, int command, uintptr_t arg); void sys_mprotect(Context* context, void* address, size_t size, int prot); -void sys_clock(Context* context); \ No newline at end of file +void sys_clock(Context* context); +void sys_fork(Context* context); \ No newline at end of file diff --git a/kernel/src/interrupts/Entry.cpp b/kernel/src/interrupts/Entry.cpp index f6562ae4..cd9ee88d 100644 --- a/kernel/src/interrupts/Entry.cpp +++ b/kernel/src/interrupts/Entry.cpp @@ -46,13 +46,18 @@ extern "C" void common_handler(Context* context) { kerrorln("Page fault in ring 3 (RIP %lx), while trying to access %lx, error code %ld", context->rip, context->cr2, context->error_code); + + if (Scheduler::current_task()) + { + kinfoln("Page fault ocurred while in task: %ld", Scheduler::current_task()->id); + } + + hang(); // FIXME: Remove this when multiple address spaces are working. kinfoln("Stack trace:"); StackTracer tracer(context->rbp); tracer.trace_with_ip(context->rip); - hang(); // FIXME: Remove this when multiple address spaces are working. - Scheduler::task_misbehave(context, -3); } } diff --git a/kernel/src/memory/AddressSpace.cpp b/kernel/src/memory/AddressSpace.cpp index bef29b71..95ecee94 100644 --- a/kernel/src/memory/AddressSpace.cpp +++ b/kernel/src/memory/AddressSpace.cpp @@ -4,27 +4,21 @@ #include "log/Log.h" #include "memory/PMM.h" #include "memory/VMM.h" +#include "misc/hang.h" #include "std/stdlib.h" +#include "std/string.h" #include "utils/move.h" AddressSpace AddressSpace::create() { AddressSpace result; result.m_pml4 = (PageTable*)PMM::request_page(); - result.m_refs = (int*)kmalloc(sizeof(int)); - *result.m_refs = 1; VMM::install_kernel_page_directory_into_address_space(result); return move(result); } void AddressSpace::destroy() { - if (is_cloned()) - { - kdbgln("Will not destroy a cloned address space, I don't own it"); - (*m_refs)--; - return; - } uint64_t pages_freed = 0; for (int i = 0; i < 512; i++) { @@ -78,26 +72,75 @@ void AddressSpace::destroy() pages_freed++; PMM::free_page(m_pml4); - kfree(m_refs); - kdbgln("Reclaimed %ld pages from address space!", pages_freed); } -void AddressSpace::detach() -{ - if (!is_cloned()) return; - (*m_refs)--; - m_refs = (int*)kmalloc(sizeof(int)); - *m_refs = 1; - m_pml4 = (PageTable*)PMM::request_page(); - VMM::install_kernel_page_directory_into_address_space(*this); -} - -AddressSpace AddressSpace::clone() +AddressSpace AddressSpace::clone() // FIXME: Add out-of-memory checks to this function. { AddressSpace result; - result.m_pml4 = m_pml4; - result.m_refs = m_refs; - *m_refs = *m_refs + 1; + result.m_pml4 = (PageTable*)PMM::request_page(); + if (!result.m_pml4) return result; + memcpy(result.m_pml4, m_pml4, PAGE_SIZE); + for (int i = 0; i < 512; i++) + { + PageDirectoryEntry& pdp_pde = m_pml4->entries[i]; + PageDirectoryEntry& cloned_pdp_pde = result.m_pml4->entries[i]; + if (!pdp_pde.present) continue; + if (pdp_pde.larger_pages) + { + void* cloned = PMM::request_page(); + memcpy(cloned, (void*)pdp_pde.get_address(), PAGE_SIZE); + cloned_pdp_pde.set_address((uint64_t)cloned); + continue; + } + PageTable* pdp = (PageTable*)pdp_pde.get_address(); + PageTable* cloned_pdp = (PageTable*)PMM::request_page(); + memcpy(cloned_pdp, pdp, PAGE_SIZE); + cloned_pdp_pde.set_address((uint64_t)cloned_pdp); + for (int j = 0; j < 511; j++) // skip the last page directory, it's the kernel one + { + PageDirectoryEntry& pd_pde = pdp->entries[j]; + PageDirectoryEntry& cloned_pd_pde = cloned_pdp->entries[j]; + if (!pd_pde.present) continue; + if (pd_pde.larger_pages) + { + void* cloned = PMM::request_page(); + memcpy(cloned, (void*)pd_pde.get_address(), PAGE_SIZE); + cloned_pd_pde.set_address((uint64_t)cloned); + continue; + } + PageTable* pd = (PageTable*)pd_pde.get_address(); + PageTable* cloned_pd = (PageTable*)PMM::request_page(); + memcpy(cloned_pd, pd, PAGE_SIZE); + cloned_pd_pde.set_address((uint64_t)cloned_pd); + for (int k = 0; k < 512; k++) + { + PageDirectoryEntry& pt_pde = pd->entries[k]; + PageDirectoryEntry& cloned_pt_pde = cloned_pd->entries[k]; + if (!pt_pde.present) continue; + if (pt_pde.larger_pages) + { + void* cloned = PMM::request_page(); + memcpy(cloned, (void*)pt_pde.get_address(), PAGE_SIZE); + cloned_pt_pde.set_address((uint64_t)cloned); + continue; + } + PageTable* pt = (PageTable*)pt_pde.get_address(); + PageTable* cloned_pt = (PageTable*)PMM::request_page(); + memcpy(cloned_pt, pt, PAGE_SIZE); + cloned_pt_pde.set_address((uint64_t)cloned_pt); + for (int l = 0; l < 512; l++) + { + PageDirectoryEntry& pde = pt->entries[l]; + PageDirectoryEntry& cloned_pde = cloned_pt->entries[l]; + if (!pde.present) continue; + void* cloned = PMM::request_page(); + memcpy(cloned, (void*)pde.get_address(), PAGE_SIZE); + cloned_pde.set_address((uint64_t)cloned); + continue; + } + } + } + } return result; } \ No newline at end of file diff --git a/kernel/src/memory/VMM.cpp b/kernel/src/memory/VMM.cpp index 3932a742..90c00f2c 100644 --- a/kernel/src/memory/VMM.cpp +++ b/kernel/src/memory/VMM.cpp @@ -275,13 +275,19 @@ void VMM::install_kernel_page_directory_into_address_space(AddressSpace& space) 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); + PageTable* space_last_pdp; + + if (!space_last_pdp_pde.present) + { + space_last_pdp = (PageTable*)PMM::request_page(); + + space_last_pdp_pde.present = true; + space_last_pdp_pde.read_write = true; + space_last_pdp_pde.set_address((uint64_t)space_last_pdp); + } + else { space_last_pdp = (PageTable*)space_last_pdp_pde.get_address(); } PageDirectoryEntry& space_last_pd_pde = space_last_pdp->entries[511]; diff --git a/kernel/src/std/string.cpp b/kernel/src/std/string.cpp index ef5481fd..d6540ca2 100644 --- a/kernel/src/std/string.cpp +++ b/kernel/src/std/string.cpp @@ -105,6 +105,26 @@ char* strstr(char* haystack, const char* needle) return NULL; } +void* memmem(void* haystack, size_t haystacklen, const void* needle, size_t needlelen) +{ + const char* hs = (const char*)haystack; + const char* nd = (const char*)needle; + while (haystacklen) + { + if (*hs == *nd) + { + if (needlelen <= haystacklen) + { + if (!memcmp(hs, nd, needlelen)) return (void*)(const_cast(hs)); + } + else { return NULL; } + } + hs++; + haystacklen--; + } + return NULL; +} + void* memcpy(void* dest, const void* src, size_t n) { for (size_t i = 0; i < n; ++i) { *((char*)dest + i) = *((const char*)src + i); } diff --git a/kernel/src/sys/Syscall.cpp b/kernel/src/sys/Syscall.cpp index 243756ed..a8e10126 100644 --- a/kernel/src/sys/Syscall.cpp +++ b/kernel/src/sys/Syscall.cpp @@ -27,6 +27,7 @@ void Syscall::entry(Context* context) case SYS_fcntl: sys_fcntl(context, (int)context->rdi, (int)context->rsi, context->rdx); break; case SYS_mprotect: sys_mprotect(context, (void*)context->rdi, context->rsi, (int)context->rdx); break; case SYS_clock: sys_clock(context); break; + case SYS_fork: sys_fork(context); break; default: context->rax = -ENOSYS; break; } VMM::exit_syscall_context(); diff --git a/kernel/src/sys/exec.cpp b/kernel/src/sys/exec.cpp index 9e5df9f7..231edd40 100644 --- a/kernel/src/sys/exec.cpp +++ b/kernel/src/sys/exec.cpp @@ -6,17 +6,50 @@ #include "memory/MemoryManager.h" #include "memory/PMM.h" #include "memory/VMM.h" +#include "misc/hang.h" #include "std/stdlib.h" #include "std/string.h" #include "sys/Syscall.h" #include "sys/elf/ELFLoader.h" #include "thread/Scheduler.h" +#include "utils/Addresses.h" + +void sys_fork(Context* context) +{ + kinfoln("fork(): attempting fork"); + + Task* parent = Scheduler::current_task(); + + Task* child = Scheduler::create_user_task(); + + memcpy(&child->regs, &parent->regs, sizeof(Context)); + if (parent->floating_saved) + { + memcpy(child->floating_region, parent->floating_region, sizeof(parent->floating_region)); + child->floating_saved = true; + } + for (int i = 0; i < TASK_MAX_FDS; i++) { child->files[i] = parent->files[i]; } + + child->address_space = parent->address_space.clone(); + + child->regs.rsp += sizeof(uintptr_t) * 2; + + child->regs.rax = 0; + context->rax = child->id; + + child->state = child->Sleeping; + child->task_sleep = 1000; + + kinfoln("fork(): parent RIP %lx, child RIP %lx, parent RSP %lx, child RSP %lx", parent->regs.rip, child->regs.rip, + parent->regs.rsp, child->regs.rsp); + + kinfoln("fork(): forked parent %ld into child %ld", parent->id, child->id); + + return; +} 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) { @@ -49,20 +82,9 @@ void sys_exec(Context* context, const char* pathname) 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; } @@ -73,29 +95,12 @@ void sys_exec(Context* context, const char* pathname) Task* task = Scheduler::current_task(); ASSERT(task); - if (task->address_space.is_cloned()) - { - kdbgln("Detaching cloned address space, %p, %s", (void*)task->address_space.get_pml4(), - task->address_space.is_cloned() ? "is cloned" : "is not cloned"); - task->address_space.detach(); - VMM::switch_to_user_address_space(task->address_space); - kdbgln("Detached cloned address space, %p, %s", (void*)task->address_space.get_pml4(), - task->address_space.is_cloned() ? "is cloned" : "is not cloned"); - } - // 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); diff --git a/kernel/src/thread/Scheduler.cpp b/kernel/src/thread/Scheduler.cpp index 3544bb9d..4a9fa0bb 100644 --- a/kernel/src/thread/Scheduler.cpp +++ b/kernel/src/thread/Scheduler.cpp @@ -132,8 +132,8 @@ void Scheduler::load_user_task(const char* filename) new_task->user_task = true; new_task->regs.rip = image->entry; new_task->image = image; - 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? + new_task->allocated_stack = (uint64_t)MemoryManager::get_pages_at( + 0x100000, TASK_PAGES_IN_STACK, MAP_READ_WRITE | MAP_USER); // 16 KB is enough for everyone, right? 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; @@ -185,8 +185,6 @@ void Scheduler::reap_task(Task* task) 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); - if (exiting_task->allocated_stack) - MemoryManager::release_pages((void*)exiting_task->allocated_stack, TASK_PAGES_IN_STACK); if (exiting_task->image) // FIXME: Also free pages the task has mmap-ed but not munmap-ed. { // ELFLoader::release_elf_image(exiting_task->image); diff --git a/kernel/src/trace/StackTracer.cpp b/kernel/src/trace/StackTracer.cpp index ea305c52..39a38e7f 100644 --- a/kernel/src/trace/StackTracer.cpp +++ b/kernel/src/trace/StackTracer.cpp @@ -1,5 +1,6 @@ #include "trace/StackTracer.h" #include "memory/Memory.h" +#include "memory/VMM.h" #include "std/stdio.h" #include "trace/Resolve.h" @@ -20,6 +21,11 @@ typedef struct stackframe void StackTracer::trace() { + if (!VMM::is_using_kernel_address_space()) + { + VMM::switch_back_to_kernel_address_space(); + VMM::apply_address_space(); + } stackframe* frame = (stackframe*)m_base_pointer; while (frame && frame->instruction) { @@ -27,6 +33,7 @@ void StackTracer::trace() get_symbol_name(frame->instruction, symbol_name, sizeof(symbol_name)); printf("%lx: %s\n", frame->instruction, symbol_name); frame = frame->next; + if (VMM::get_physical((uint64_t)frame) == (uint64_t)-1) return; } } diff --git a/libs/libc/include/luna/syscall.h b/libs/libc/include/luna/syscall.h index 6d92d320..b706db8d 100644 --- a/libs/libc/include/luna/syscall.h +++ b/libs/libc/include/luna/syscall.h @@ -17,6 +17,7 @@ #define SYS_fcntl 13 #define SYS_mprotect 14 #define SYS_clock 15 +#define SYS_fork 16 #ifndef __want_syscalls #ifdef __cplusplus diff --git a/libs/libc/include/unistd.h b/libs/libc/include/unistd.h index 80add4cb..3e5a00a0 100644 --- a/libs/libc/include/unistd.h +++ b/libs/libc/include/unistd.h @@ -22,7 +22,8 @@ extern "C" int execve(const char*, char* const[], char* const[]); /* Not implemented. */ int execvp(const char*, char* const[]); - /* Not implemented. */ + + /* Creates an identical copy of the current process and returns its process ID. */ pid_t fork(void); /* Terminates the program with the status code status. */ diff --git a/libs/libc/src/unistd.cpp b/libs/libc/src/unistd.cpp index 86ac5df3..1c4577f0 100644 --- a/libs/libc/src/unistd.cpp +++ b/libs/libc/src/unistd.cpp @@ -19,9 +19,10 @@ extern "C" { NOT_IMPLEMENTED("execvp"); } + pid_t fork(void) { - NOT_IMPLEMENTED("fork"); + return syscall(SYS_fork); } long syscall(long number, ...) @@ -34,6 +35,7 @@ extern "C" { case SYS_clock: case SYS_yield: + case SYS_fork: case SYS_gettid: result = __luna_syscall0(number); break; case SYS_exit: case SYS_close: