/* This file is part of Fennix Kernel. Fennix Kernel is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fennix Kernel is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Fennix Kernel. If not, see . */ #include #include #include #include #include #include #include "../syscalls.h" #include "../kernel.h" #include "../../Userspace/libs/include/libsys/base.h" /* KCtl */ #include "../ipc.h" using InterProcessCommunication::IPC; using InterProcessCommunication::IPCID; using Tasking::Token; using Tasking::TTL; using Tasking::TTL::Trusted; using Tasking::TTL::TrustedByKernel; using Tasking::TTL::UnknownTrustLevel; using Tasking::TTL::Untrusted; static inline bool CheckTrust(int TrustLevel) { // SmartTimeoutLock(SyscallsLock, 10000); - This is already done in the caller Token token = TaskManager->GetCurrentThread()->Security.UniqueToken; if (TaskManager->GetSecurityManager()->IsTokenTrusted(token, TrustLevel)) return true; warn("Thread %s(%lld) tried to access a system call \"%s\" with insufficient trust level", TaskManager->GetCurrentThread()->Name, TaskManager->GetCurrentThread()->ID, KernelSymbolTable->GetSymbolFromAddress((uintptr_t)__builtin_extract_return_addr(__builtin_return_address(0)))); debug("Token: token=%#lx, trust=%d", token, TaskManager->GetSecurityManager()->GetTokenTrustLevel(token)); return false; } static int sys_exit(SyscallsFrame *Frame, int code) { /* Allow everyone to exit */ if (!CheckTrust(TrustedByKernel | Trusted | Untrusted | UnknownTrustLevel)) return SYSCALL_ACCESS_DENIED; trace("Userspace thread %s(%lld) exited with code %#llx", TaskManager->GetCurrentThread()->Name, TaskManager->GetCurrentThread()->ID, code); TaskManager->GetCurrentThread()->ExitCode = code; TaskManager->GetCurrentThread()->Status = Tasking::TaskStatus::Terminated; UNUSED(Frame); return SYSCALL_OK; } static int sys_print(SyscallsFrame *Frame, char Char, int Index) { /* Only trusted threads can write to the kernel console */ if (!CheckTrust(TrustedByKernel | Trusted)) return SYSCALL_ACCESS_DENIED; char ret = Display->Print(Char, Index, true); if (!Config.BootAnimation && Index == 0) #ifdef DEBUG Display->SetBuffer(Index); #else if (Char == '\n') Display->SetBuffer(Index); #endif UNUSED(Frame); return ret; } static uintptr_t sys_request_pages(SyscallsFrame *Frame, size_t Count) { /* Allow everyone to request pages */ if (!CheckTrust(TrustedByKernel | Trusted | Untrusted)) return SYSCALL_ACCESS_DENIED; UNUSED(Frame); return (uintptr_t)TaskManager->GetCurrentThread()->Memory->RequestPages(Count + 1, true); } static int sys_free_pages(SyscallsFrame *Frame, uintptr_t Address, size_t Count) { /* Allow everyone to free pages */ if (!CheckTrust(TrustedByKernel | Trusted | Untrusted)) return SYSCALL_ACCESS_DENIED; TaskManager->GetCurrentThread()->Memory->FreePages((void *)Address, Count + 1); UNUSED(Frame); return SYSCALL_OK; } static int sys_detach_address(SyscallsFrame *Frame, uintptr_t Address) { /* Only trusted threads can detach allocated addresses */ if (!CheckTrust(TrustedByKernel | Trusted)) return SYSCALL_ACCESS_DENIED; TaskManager->GetCurrentThread()->Memory->DetachAddress((void *)Address); UNUSED(Frame); return SYSCALL_OK; } static uintptr_t sys_kernelctl(SyscallsFrame *Frame, enum KCtl Command, uint64_t Arg1, uint64_t Arg2, uint64_t Arg3, uint64_t Arg4) { if (!CheckTrust(TrustedByKernel | Trusted | Untrusted)) return SYSCALL_ACCESS_DENIED; switch (Command) { case KCTL_GET_PID: return TaskManager->GetCurrentThread()->Parent->ID; case KCTL_GET_TID: return TaskManager->GetCurrentThread()->ID; case KCTL_GET_PAGE_SIZE: return PAGE_SIZE; case KCTL_IS_CRITICAL: return TaskManager->GetCurrentThread()->Security.IsCritical; case KCTL_REGISTER_ELF_LIB: { if (!CheckTrust(TrustedByKernel | Trusted)) return SYSCALL_ACCESS_DENIED; char *Identifier = (char *)Arg1; const char *Path = (const char *)Arg2; if (!Identifier || !Path) return SYSCALL_INVALID_ARGUMENT; std::string FullPath = Path; int retries = 0; RetryReadPath: debug("KCTL_REGISTER_ELF_LIB: Trying to open %s", FullPath.c_str()); VirtualFileSystem::File f = vfs->Open(FullPath.c_str()); if (!f.IsOK()) { FullPath.clear(); switch (retries) { case 0: FullPath = "/system/lib/"; break; case 1: FullPath = "/system/lib64/"; break; case 2: FullPath = "/system/"; break; case 3: { // TODO: Check process binary path break; } default: { vfs->Close(f); return SYSCALL_INVALID_ARGUMENT; } } FullPath += Path; vfs->Close(f); retries++; goto RetryReadPath; } vfs->Close(f); if (Execute::AddLibrary(Identifier, (void *)f.node->Address, f.node->Length)) return SYSCALL_OK; else return SYSCALL_INTERNAL_ERROR; } case KCTL_GET_ELF_LIB_FILE: { if (!CheckTrust(TrustedByKernel | Trusted)) return SYSCALL_ACCESS_DENIED; char *Identifier = (char *)Arg1; if (!Identifier) return 0; Execute::SharedLibraries lib = Execute::GetLibrary(Identifier); if (!lib.Address) { debug("Failed to get library address %#lx", (uintptr_t)lib.Address); } debug("Returning library address %#lx (%s)", (uintptr_t)lib.Address, Identifier); return (uintptr_t)lib.Address; } case KCTL_GET_ELF_LIB_MEMORY_IMAGE: { if (!CheckTrust(TrustedByKernel | Trusted)) return SYSCALL_ACCESS_DENIED; char *Identifier = (char *)Arg1; if (!Identifier) return 0; Execute::SharedLibraries lib = Execute::GetLibrary(Identifier); if (!lib.MemoryImage) { debug("Failed to get library memory image %#lx", (uintptr_t)lib.MemoryImage); } debug("Returning memory image %#lx (%s)", (uintptr_t)lib.MemoryImage, Identifier); return (uintptr_t)lib.MemoryImage; } case KCTL_GET_FRAMEBUFFER_BUFFER: return r_cst(uint64_t, Display->GetBuffer(0)->Buffer); case KCTL_GET_FRAMEBUFFER_WIDTH: return Display->GetBuffer(0)->Width; case KCTL_GET_FRAMEBUFFER_HEIGHT: return Display->GetBuffer(0)->Height; case KCTL_GET_FRAMEBUFFER_SIZE: return Display->GetBuffer(0)->Size; default: { warn("KernelCTL: Unknown command: %lld", Command); return SYSCALL_INVALID_ARGUMENT; } } UNUSED(Arg1); UNUSED(Arg2); UNUSED(Arg3); UNUSED(Arg4); UNUSED(Frame); } static int sys_ipc(SyscallsFrame *Frame, enum IPCCommand Command, enum IPCType Type, int ID, int Flags, void *Buffer, size_t Size) { /* Allow everyone to use IPC */ if (!CheckTrust(TrustedByKernel | Trusted | Untrusted)) return SYSCALL_ACCESS_DENIED; UNUSED(Frame); return TaskManager->GetCurrentProcess()->IPC->HandleSyscall(Command, Type, ID, Flags, Buffer, Size); } static uint64_t sys_file_open(SyscallsFrame *Frame, const char *Path, uint64_t Flags) { debug("(Path: %s, Flags: %#lx)", Path, Flags); VirtualFileSystem::Node *cwd = nullptr; if (vfs->PathIsRelative(Path)) cwd = TaskManager->GetCurrentProcess()->CurrentWorkingDirectory; else cwd = vfs->GetRootNode(); VirtualFileSystem::File KPObj = vfs->Open(Path, cwd); if (!KPObj.IsOK()) { debug("Failed to open file %s (%d)", Path, KPObj.Status); vfs->Close(KPObj); return SYSCALL_INTERNAL_ERROR; } VirtualFileSystem::File *KernelPrivate = (VirtualFileSystem::File *)TaskManager->GetCurrentThread()->Memory->RequestPages(TO_PAGES(sizeof(VirtualFileSystem::File))); *KernelPrivate = KPObj; debug("Opened file %s (%d)", KPObj.Name, KPObj.Status); return (uint64_t)KernelPrivate; UNUSED(Frame); UNUSED(Flags); } static int sys_file_close(SyscallsFrame *Frame, void *KernelPrivate) { debug("(KernelPrivate: %#lx)", KernelPrivate); if (KernelPrivate) { VirtualFileSystem::File KPObj = *(VirtualFileSystem::File *)KernelPrivate; debug("Closed file %s (%d)", KPObj.Name, KPObj.Status); vfs->Close(KPObj); TaskManager->GetCurrentThread()->Memory->FreePages(KernelPrivate, TO_PAGES(sizeof(VirtualFileSystem::File))); return SYSCALL_OK; } return SYSCALL_INVALID_ARGUMENT; UNUSED(Frame); } static uint64_t sys_file_read(SyscallsFrame *Frame, void *KernelPrivate, uint64_t Offset, uint8_t *Buffer, uint64_t Size) { if (KernelPrivate == nullptr) return 0; debug("(KernelPrivate: %#lx, Offset: %#lx, Buffer: %#lx, Size: %#lx)", KernelPrivate, Offset, Buffer, Size); return vfs->Read(*(VirtualFileSystem::File *)KernelPrivate, (size_t)Offset, Buffer, (size_t)Size); UNUSED(Frame); } static uint64_t sys_file_write(SyscallsFrame *Frame, void *KernelPrivate, uint64_t Offset, uint8_t *Buffer, uint64_t Size) { if (KernelPrivate == nullptr) return 0; debug("(KernelPrivate: %#lx, Offset: %#lx, Buffer: %#lx, Size: %#lx)", KernelPrivate, Offset, Buffer, Size); return vfs->Write(*(VirtualFileSystem::File *)KernelPrivate, (size_t)Offset, Buffer, (size_t)Size); UNUSED(Frame); } static uint64_t sys_file_seek(SyscallsFrame *Frame, void *KernelPrivate, uint64_t Offset, int Whence) { if (KernelPrivate == nullptr) return 0; debug("(KernelPrivate: %#lx, Offset: %#lx, Whence: %d)", KernelPrivate, Offset, Whence); VirtualFileSystem::File *KPObj = (VirtualFileSystem::File *)KernelPrivate; if (KPObj->node->Operator->Seek == nullptr) return SYSCALL_INTERNAL_ERROR; uint64_t ret = KPObj->node->Operator->Seek(KPObj->node, (size_t)Offset, (uint8_t)Whence); debug("Seek %s %ld", KPObj->Name, ret); return ret; UNUSED(Frame); } static int sys_file_status(SyscallsFrame *Frame) { fixme("sys_file_status: %#lx", Frame); return SYSCALL_NOT_IMPLEMENTED; } static int sys_sleep(SyscallsFrame *Frame, uint64_t Milliseconds) { UNUSED(Frame); if (!CheckTrust(TrustedByKernel | Trusted | Untrusted)) return SYSCALL_ACCESS_DENIED; TaskManager->Sleep(Milliseconds); return 0; } static int _ChildPID = 0; static int sys_fork(SyscallsFrame *Frame) { UNUSED(Frame); if (!CheckTrust(TrustedByKernel | Trusted | Untrusted)) return SYSCALL_ACCESS_DENIED; fixme("sys_fork: %#lx", Frame); return SYSCALL_NOT_IMPLEMENTED; Tasking::PCB *Parent = TaskManager->GetCurrentThread()->Parent; Tasking::TCB *Thread = TaskManager->GetCurrentThread(); Tasking::PCB *NewProcess = TaskManager->CreateProcess(Parent, Parent->Name, Parent->Security.TrustLevel, Parent->ELFSymbolTable ? Parent->ELFSymbolTable->GetImage() : nullptr); if (!NewProcess) { error("Failed to create process for fork"); return SYSCALL_ERROR; } strncpy(NewProcess->Name, Parent->Name, sizeof(NewProcess->Name)); NewProcess->IPC->Fork(Parent->IPC); // FIXME: Do we need to do this? Tasking::TCB *NewThread = TaskManager->CreateThread(NewProcess, 0, nullptr, nullptr, std::vector(), 0, Thread->Info.Architecture, Thread->Info.Compatibility, true); if (!NewThread) { error("Failed to create thread for fork"); return SYSCALL_ERROR; } _ChildPID = (int)NewThread->ID; memcpy(NewThread->FPU, Thread->FPU, sizeof(CPU::x64::FXState)); NewThread->Stack->Fork(Thread->Stack); strncpy(NewThread->Name, Thread->Name, sizeof(Thread->Name)); NewThread->Info = Thread->Info; #ifdef a86 NewThread->ShadowGSBase = Thread->ShadowGSBase; NewThread->GSBase = Thread->GSBase; NewThread->FSBase = Thread->FSBase; #endif CPU::Interrupts(CPU::Disable); static int RetChild = 0; static uint64_t ReturnAddress = 0; static uint64_t ChildStackPointer = 0; #if defined(a86) asmv("int $0x30"); /* This will trigger the IRQ16 instantly so we won't execute the next instruction */ #elif defined(aa64) asmv("svc #0x30"); /* This will trigger the IRQ16 instantly so we won't execute the next instruction */ #endif if (RetChild--) { /* We can't just return 0; because the CPUData->SystemCallStack is no longer valid */ asmv("movq %0, %%rcx\n" : : "r"(ReturnAddress)); asmv("movq $0, %rax\n"); // Return 0 to the child asmv("mov %0, %%rsp\n" : : "r"(ChildStackPointer)); asmv("mov %0, %%rbp\n" : : "r"(ChildStackPointer)); asmv("swapgs\n"); // Swap GS back to the user GS asmv("sti\n"); // Enable interrupts asmv("sysretq\n"); // Return to rcx address in user mode } RetChild = 1; ReturnAddress = Frame->ReturnAddress; ChildStackPointer = Frame->StackPointer; NewThread->Registers = Thread->Registers; debug("Forked thread \"%s\"(%d) to \"%s\"(%d)", Thread->Name, Thread->ID, NewThread->Name, NewThread->ID); NewThread->Status = Tasking::TaskStatus::Ready; if (_ChildPID == (int)TaskManager->GetCurrentThread()->ID) return 0; return (int)NewThread->ID; } static int sys_wait(SyscallsFrame *Frame) { fixme("sys_wait: %#lx", Frame); return SYSCALL_NOT_IMPLEMENTED; } static int sys_kill(SyscallsFrame *Frame) { fixme("sys_kill: %#lx", Frame); return SYSCALL_NOT_IMPLEMENTED; } static int sys_spawn(SyscallsFrame *Frame) { fixme("sys_spawn: %#lx", Frame); return SYSCALL_NOT_IMPLEMENTED; } static int sys_spawn_thread(SyscallsFrame *Frame, uint64_t InstructionPointer) { Tasking::TCB *thread = TaskManager->CreateThread(TaskManager->GetCurrentProcess(), (Tasking::IP)InstructionPointer); if (thread) return (int)thread->ID; return SYSCALL_ERROR; } static int sys_get_thread_list_of_process(SyscallsFrame *Frame) { fixme("sys_get_thread_list_of_process: %#lx", Frame); return SYSCALL_NOT_IMPLEMENTED; } static int sys_get_current_process(SyscallsFrame *Frame) { fixme("sys_get_current_process: %#lx", Frame); return SYSCALL_NOT_IMPLEMENTED; } static int sys_get_current_thread(SyscallsFrame *Frame) { fixme("sys_get_current_thread: %#lx", Frame); return SYSCALL_NOT_IMPLEMENTED; } static int sys_get_current_process_id(SyscallsFrame *Frame) { return (int)TaskManager->GetCurrentProcess()->ID; } static int sys_get_current_thread_id(SyscallsFrame *Frame) { return (int)TaskManager->GetCurrentThread()->ID; } static int sys_get_process_by_pid(SyscallsFrame *Frame) { fixme("sys_get_process_by_pid: %#lx", Frame); return SYSCALL_NOT_IMPLEMENTED; } static int sys_get_thread_by_tid(SyscallsFrame *Frame) { fixme("sys_get_thread_by_tid: %#lx", Frame); return SYSCALL_NOT_IMPLEMENTED; } static int sys_kill_process(SyscallsFrame *Frame) { fixme("sys_kill_process: %#lx", Frame); return SYSCALL_NOT_IMPLEMENTED; } static int sys_kill_thread(SyscallsFrame *Frame) { fixme("sys_kill_thread: %#lx", Frame); return SYSCALL_NOT_IMPLEMENTED; } static int sys_sys_reserved_create_process(SyscallsFrame *Frame) { fixme("sys_sys_reserved_create_process: %#lx", Frame); return SYSCALL_NOT_IMPLEMENTED; } static int sys_sys_reserved_create_thread(SyscallsFrame *Frame) { fixme("sys_sys_reserved_create_thread: %#lx", Frame); return SYSCALL_NOT_IMPLEMENTED; } static void *NativeSyscallsTable[] = { [_Exit] = (void *)sys_exit, [_Print] = (void *)sys_print, [_RequestPages] = (void *)sys_request_pages, [_FreePages] = (void *)sys_free_pages, [_DetachAddress] = (void *)sys_detach_address, [_KernelCTL] = (void *)sys_kernelctl, [_IPC] = (void *)sys_ipc, [_FileOpen] = (void *)sys_file_open, [_FileClose] = (void *)sys_file_close, [_FileRead] = (void *)sys_file_read, [_FileWrite] = (void *)sys_file_write, [_FileSeek] = (void *)sys_file_seek, [_FileStatus] = (void *)sys_file_status, [_Sleep] = (void *)sys_sleep, [_Fork] = (void *)sys_fork, [_Wait] = (void *)sys_wait, [_Kill] = (void *)sys_kill, [_Spawn] = (void *)sys_spawn, [_SpawnThread] = (void *)sys_spawn_thread, [_GetThreadListOfProcess] = (void *)sys_get_thread_list_of_process, [_GetCurrentProcess] = (void *)sys_get_current_process, [_GetCurrentThread] = (void *)sys_get_current_thread, [_GetCurrentProcessID] = (void *)sys_get_current_process_id, [_GetCurrentThreadID] = (void *)sys_get_current_thread_id, [_GetProcessByPID] = (void *)sys_get_process_by_pid, [_GetThreadByTID] = (void *)sys_get_thread_by_tid, [_KillProcess] = (void *)sys_kill_process, [_KillThread] = (void *)sys_kill_thread, [_SysReservedCreateProcess] = (void *)sys_sys_reserved_create_process, [_SysReservedCreateThread] = (void *)sys_sys_reserved_create_thread, }; uintptr_t HandleNativeSyscalls(SyscallsFrame *Frame) { #if defined(a64) if (Frame->rax > sizeof(NativeSyscallsTable)) { fixme("Syscall %ld not implemented", Frame->rax); return SYSCALL_NOT_IMPLEMENTED; } uintptr_t (*call)(uintptr_t, ...) = reinterpret_cast(NativeSyscallsTable[Frame->rax]); if (!call) { error("Syscall %#lx failed.", Frame->rax); return SYSCALL_INTERNAL_ERROR; } // debug("[%#lx]->( %#lx %#lx %#lx %#lx %#lx %#lx )", // Frame->rax, // Frame->rdi, Frame->rsi, Frame->rdx, Frame->rcx, Frame->r8, Frame->r9); uintptr_t ret = call((uintptr_t)Frame, Frame->rdi, Frame->rsi, Frame->rdx, Frame->r10, Frame->r8, Frame->r9); Frame->rax = ret; return ret; #elif defined(a32) if (Frame->eax > sizeof(NativeSyscallsTable)) { fixme("Syscall %ld not implemented", Frame->eax); return SYSCALL_NOT_IMPLEMENTED; } /* ... */ return SYSCALL_INTERNAL_ERROR; #elif defined(aa64) return SYSCALL_INTERNAL_ERROR; #endif }