/*
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
#include "../kernel.h"
using Tasking::PCB;
using Tasking::TCB;
__no_stack_protector void __ForkReturn(void *tableAddr)
{
#if defined(__amd64__)
asmv("movq %0, %%cr3" ::"r"(tableAddr)); /* Load process page table */
asmv("movq $0, %rax\n"); /* Return 0 */
asmv("movq %r8, %rsp\n"); /* Restore stack pointer */
asmv("movq %r8, %rbp\n"); /* Restore base pointer */
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 */
#elif defined(__i386__)
#warning "__ForkReturn not implemented for i386"
#endif
__builtin_unreachable();
}
__noreturn void sys_exit(SysFrm *Frame, int status)
{
TCB *t = thisThread;
{
CriticalSection cs;
trace("Userspace thread %s(%d) exited with code %d (%#x)",
t->Name,
t->ID, status,
status < 0 ? -status : status);
t->SetState(Tasking::Zombie);
t->SetExitCode(status);
}
while (true)
t->GetContext()->Yield();
__builtin_unreachable();
}
pid_t sys_fork(SysFrm *Frame)
{
TCB *Thread = thisThread;
PCB *Parent = Thread->Parent;
PCB *NewProcess =
TaskManager->CreateProcess(Parent, Parent->Name,
Parent->Security.ExecutionMode,
true);
if (unlikely(!NewProcess))
{
error("Failed to create process for fork");
return -EAGAIN;
}
NewProcess->Security.ProcessGroupID = Parent->Security.ProcessGroupID;
NewProcess->Security.SessionID = Parent->Security.SessionID;
NewProcess->PageTable = Parent->PageTable->Fork();
NewProcess->vma->Table = NewProcess->PageTable;
NewProcess->vma->Fork(Parent->vma);
NewProcess->ProgramBreak->SetTable(NewProcess->PageTable);
NewProcess->FileDescriptors->Fork(Parent->FileDescriptors);
NewProcess->Executable = Parent->Executable;
NewProcess->CWD = Parent->CWD;
NewProcess->FileCreationMask = Parent->FileCreationMask;
TCB *NewThread =
TaskManager->CreateThread(NewProcess,
0,
nullptr,
nullptr,
std::vector(),
Thread->Info.Architecture,
Thread->Info.Compatibility,
true);
if (!NewThread)
{
error("Failed to create thread for fork");
delete NewProcess;
return -EAGAIN;
}
NewThread->Rename(Thread->Name);
TaskManager->UpdateFrame();
#if defined(__amd64__) || defined(__i386__)
NewThread->FPU = Thread->FPU;
#endif
NewThread->Stack->Fork(Thread->Stack);
NewThread->Info.Architecture = Thread->Info.Architecture;
NewThread->Info.Compatibility = Thread->Info.Compatibility;
NewThread->Security.IsCritical = Thread->Security.IsCritical;
NewThread->Registers = Thread->Registers;
#if defined(__amd64__)
NewThread->Registers.rip = (uintptr_t)__ForkReturn;
/* For sysretq */
NewThread->Registers.rdi = (uintptr_t)NewProcess->PageTable;
NewThread->Registers.rcx = Frame->ReturnAddress;
NewThread->Registers.r8 = Frame->StackPointer;
#else
#warning "sys_fork not implemented for other platforms"
#endif
#if defined(__amd64__) || defined(__i386__)
NewThread->GSBase = NewThread->ShadowGSBase;
NewThread->ShadowGSBase = Thread->ShadowGSBase;
NewThread->FSBase = Thread->FSBase;
#endif
debug("ret addr: %#lx, stack: %#lx ip: %#lx", Frame->ReturnAddress,
Frame->StackPointer, (uintptr_t)__ForkReturn);
debug("Forked thread \"%s\"(%d) to \"%s\"(%d)",
Thread->Name, Thread->ID,
NewThread->Name, NewThread->ID);
NewThread->SetState(Tasking::Ready);
// Parent->GetContext()->Yield();
return (int)NewProcess->ID;
}
int sys_execve(SysFrm *Frame, const char *pathname, char *const argv[], char *const envp[])
{
return -ENOSYS;
}
pid_t sys_getpid(SysFrm *Frame)
{
return -ENOSYS;
}
pid_t sys_getppid(SysFrm *Frame)
{
return -ENOSYS;
}
pid_t sys_waitpid(pid_t pid, int *wstatus, int options)
{
return -ENOSYS;
}
int sys_kill(SysFrm *Frame, pid_t pid, int sig)
{
PCB *pcb = thisProcess->GetContext()->GetProcessByID(pid);
if (!pcb)
return -ESRCH;
/* TODO: Check permissions */
if (sig == 0)
return 0;
if (pid == 0)
{
bool found = false;
for (auto proc : pcb->GetContext()->GetProcessList())
{
if (proc->Security.ProcessGroupID == thisProcess->Security.ProcessGroupID)
{
debug("Sending signal %d to %s(%d)", sig, proc->Name, proc->ID);
proc->SendSignal(sig);
found = true;
}
}
if (!found)
return -ESRCH;
return 0;
}
if (pid == -1)
{
fixme("Sending signal %d to all processes except init", sig);
return -ENOSYS;
}
if (pid < -1)
{
fixme("Sending signal %d to process group %d", sig, pid);
return -ENOSYS;
}
return pcb->SendSignal(sig);
}
int sys_prctl(SysFrm *Frame, prctl_options_t option, unsigned long arg1, unsigned long arg2, unsigned long arg3, unsigned long arg4)
{
PCB *pcb = thisProcess;
Memory::VirtualMemoryArea *vma = pcb->vma;
switch (option)
{
case __SYS_GET_GS:
{
auto arg = vma->UserCheckAndGetAddress((void *)arg1);
if (arg == nullptr)
return -EFAULT;
#if defined(__amd64__) || defined(__i386__)
*r_cst(uintptr_t *, arg) = CPU::x86::rdmsr(CPU::x86::MSRID::MSR_GS_BASE);
#endif
return 0;
}
case __SYS_SET_GS:
{
#if defined(__amd64__) || defined(__i386__)
CPU::x86::wrmsr(CPU::x86::MSRID::MSR_GS_BASE, arg1);
#endif
return 0;
}
case __SYS_GET_FS:
{
auto arg = vma->UserCheckAndGetAddress((void *)arg1);
if (arg == nullptr)
return -EFAULT;
#if defined(__amd64__) || defined(__i386__)
*r_cst(uintptr_t *, arg) = CPU::x86::rdmsr(CPU::x86::MSRID::MSR_FS_BASE);
#endif
return 0;
}
case __SYS_SET_FS:
{
#if defined(__amd64__) || defined(__i386__)
CPU::x86::wrmsr(CPU::x86::MSRID::MSR_FS_BASE, arg1);
#endif
return 0;
}
default:
return -EINVAL;
}
}