mirror of
https://github.com/Fennix-Project/Kernel.git
synced 2025-05-25 22:14:37 +00:00
193 lines
4.9 KiB
C++
193 lines
4.9 KiB
C++
/*
|
|
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 <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <exec.hpp>
|
|
|
|
#include <memory.hpp>
|
|
#include <lock.hpp>
|
|
#include <msexec.h>
|
|
#include <cwalk.h>
|
|
#include <elf.h>
|
|
#include <abi.h>
|
|
|
|
#include "../kernel.h"
|
|
|
|
using namespace Tasking;
|
|
|
|
namespace Execute
|
|
{
|
|
int Spawn(char *Path, const char **argv, const char **envp,
|
|
Tasking::PCB *Parent, bool Fork,
|
|
Tasking::TaskCompatibility Compatibility,
|
|
bool Critical)
|
|
{
|
|
FileNode *fd = fs->GetByPath(Path, nullptr);
|
|
if (fd == nullptr)
|
|
return -ENOENT;
|
|
|
|
if (!fd->IsRegularFile())
|
|
return -ENOEXEC;
|
|
|
|
switch (GetBinaryType(Path))
|
|
{
|
|
case BinaryType::BinTypeELF:
|
|
{
|
|
TaskArchitecture Arch = TaskArchitecture::UnknownArchitecture;
|
|
const char *BaseName;
|
|
cwk_path_get_basename(Path, &BaseName, nullptr);
|
|
Elf32_Ehdr ELFHeader;
|
|
fd->Read(&ELFHeader, sizeof(Elf32_Ehdr), 0);
|
|
|
|
switch (ELFHeader.e_machine)
|
|
{
|
|
case EM_386:
|
|
Arch = TaskArchitecture::x32;
|
|
break;
|
|
case EM_X86_64:
|
|
Arch = TaskArchitecture::x64;
|
|
break;
|
|
case EM_ARM:
|
|
Arch = TaskArchitecture::ARM32;
|
|
break;
|
|
case EM_AARCH64:
|
|
Arch = TaskArchitecture::ARM64;
|
|
break;
|
|
default:
|
|
error("Unknown ELF architecture %d",
|
|
ELFHeader.e_machine);
|
|
break;
|
|
}
|
|
|
|
// TODO: This shouldn't be ignored
|
|
if (ELFHeader.e_ident[EI_CLASS] == ELFCLASS32)
|
|
fixme("32-bit ELF");
|
|
else if (ELFHeader.e_ident[EI_CLASS] == ELFCLASS64)
|
|
fixme("64-bit ELF");
|
|
else
|
|
fixme("Unknown class %d", ELFHeader.e_ident[EI_CLASS]);
|
|
|
|
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
|
if (ELFHeader.e_ident[EI_DATA] != ELFDATA2LSB)
|
|
{
|
|
fixme("ELF32 LSB expected, got %d", ELFHeader.e_ident[EI_DATA]);
|
|
}
|
|
#else
|
|
if (ELFHeader.e_ident[EI_DATA] != ELFDATA2MSB)
|
|
{
|
|
fixme("ELF32 MSB expected, got %d", ELFHeader.e_ident[EI_DATA]);
|
|
}
|
|
#endif
|
|
|
|
/* ------------------------------------------------------------------------------------------------------------------------------ */
|
|
|
|
PCB *Process;
|
|
if (Fork)
|
|
{
|
|
assert(Parent != nullptr);
|
|
CriticalSection cs;
|
|
|
|
Process = Parent;
|
|
foreach (auto tcb in Process->Threads)
|
|
{
|
|
debug("Deleting thread %d", tcb->ID);
|
|
// delete tcb;
|
|
tcb->SetState(Tasking::Terminated);
|
|
}
|
|
|
|
fixme("free allocated memory");
|
|
// Process->vma->FreeAllPages();
|
|
}
|
|
else
|
|
{
|
|
if (Parent == nullptr)
|
|
Parent = thisProcess;
|
|
|
|
Process = TaskManager->CreateProcess(Parent, BaseName,
|
|
TaskExecutionMode::User,
|
|
false, 0, 0);
|
|
Process->Info.Compatibility = Compatibility;
|
|
Process->Info.Architecture = Arch;
|
|
}
|
|
|
|
Process->SetWorkingDirectory(fs->GetByPath(Path, nullptr)->Parent);
|
|
Process->SetExe(Path);
|
|
|
|
ELFObject *obj = new ELFObject(Path, Process, argv, envp);
|
|
if (!obj->IsValid)
|
|
{
|
|
error("Failed to load ELF object");
|
|
delete Process;
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
vfs::FileDescriptorTable *pfdt = Parent->FileDescriptors;
|
|
vfs::FileDescriptorTable *fdt = Process->FileDescriptors;
|
|
|
|
auto ForkStdio = [pfdt, fdt](FileNode *SearchNode)
|
|
{
|
|
if (unlikely(SearchNode == nullptr))
|
|
return false;
|
|
|
|
foreach (const auto &ffd in pfdt->FileMap)
|
|
{
|
|
if (ffd.second.Flags & O_CLOEXEC)
|
|
continue;
|
|
|
|
if (ffd.second.Node == SearchNode)
|
|
{
|
|
fdt->usr_open(ffd.second.Node->Path.c_str(),
|
|
ffd.second.Flags, ffd.second.Mode);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
|
|
fixme("remove workarounds for stdio and tty");
|
|
if (!Parent->tty)
|
|
Process->tty = KernelConsole::CurrentTerminal.load();
|
|
|
|
if (!ForkStdio(Parent->stdin))
|
|
fdt->usr_open("/dev/kcon", O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
|
|
|
|
if (!ForkStdio(Parent->stdout))
|
|
fdt->usr_open("/dev/kcon", O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
|
|
|
|
if (!ForkStdio(Parent->stderr))
|
|
fdt->usr_open("/dev/kcon", O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
|
|
|
|
TCB *Thread = nullptr;
|
|
{
|
|
CriticalSection cs;
|
|
Thread = TaskManager->CreateThread(Process, obj->InstructionPointer,
|
|
obj->argv, obj->envp, obj->auxv,
|
|
Arch, Compatibility);
|
|
Thread->SetCritical(Critical);
|
|
}
|
|
return Thread->ID;
|
|
}
|
|
default:
|
|
{
|
|
debug("Unknown binary type: %d", GetBinaryType(Path));
|
|
return -ENOEXEC;
|
|
}
|
|
}
|
|
|
|
return -ENOEXEC;
|
|
}
|
|
}
|