From 202bdb553c2dc073f06105b12fbf0b2a8045069b Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sat, 3 Nov 2018 01:49:40 +0100 Subject: Implemented sys$execve(). It's really crufty, but it basically works! --- Kernel/MemoryManager.cpp | 16 ++--- Kernel/MemoryManager.h | 4 +- Kernel/Process.cpp | 149 ++++++++++++++++++++++++++++++++++++++++++++++- Kernel/Process.h | 1 + Kernel/Syscall.cpp | 2 + Kernel/Syscall.h | 1 + LibC/unistd.cpp | 27 ++++++--- LibC/unistd.h | 1 + Userland/sh.cpp | 35 ++++++++++- 9 files changed, 215 insertions(+), 21 deletions(-) diff --git a/Kernel/MemoryManager.cpp b/Kernel/MemoryManager.cpp index 97d5b32831..500f57bfb1 100644 --- a/Kernel/MemoryManager.cpp +++ b/Kernel/MemoryManager.cpp @@ -31,21 +31,21 @@ MemoryManager::~MemoryManager() { } -void MemoryManager::populate_page_directory(Process& process) +void MemoryManager::populate_page_directory(PageDirectory& page_directory) { - memset(process.m_page_directory, 0, sizeof(PageDirectory)); - process.m_page_directory->entries[0] = m_kernel_page_directory->entries[0]; - process.m_page_directory->entries[1] = m_kernel_page_directory->entries[1]; + memset(&page_directory, 0, sizeof(PageDirectory)); + page_directory.entries[0] = m_kernel_page_directory->entries[0]; + page_directory.entries[1] = m_kernel_page_directory->entries[1]; } -void MemoryManager::release_page_directory(Process& process) +void MemoryManager::release_page_directory(PageDirectory& page_directory) { ASSERT_INTERRUPTS_DISABLED(); #ifdef MM_DEBUG - dbgprintf("MM: release_page_directory for pid %d, PD K%x\n", process.pid(), process.m_page_directory); + dbgprintf("MM: release_page_directory for PD K%x\n", &page_directory); #endif for (size_t i = 0; i < 1024; ++i) { - auto page_table = process.m_page_directory->physical_addresses[i]; + auto page_table = page_directory.physical_addresses[i]; if (!page_table.is_null()) { #ifdef MM_DEBUG dbgprintf("MM: deallocating process page table [%u] P%x @ %p\n", i, page_table.get(), &process.m_page_directory->physical_addresses[i]); @@ -54,7 +54,7 @@ void MemoryManager::release_page_directory(Process& process) } } #ifdef SCRUB_DEALLOCATED_PAGE_TABLES - memset(process.m_page_directory, 0xc9, sizeof(PageDirectory)); + memset(&page_directory, 0xc9, sizeof(PageDirectory)); #endif } diff --git a/Kernel/MemoryManager.h b/Kernel/MemoryManager.h index 2a72841318..4a1c77b140 100644 --- a/Kernel/MemoryManager.h +++ b/Kernel/MemoryManager.h @@ -84,8 +84,8 @@ public: void registerZone(Zone&); void unregisterZone(Zone&); - void populate_page_directory(Process&); - void release_page_directory(Process&); + void populate_page_directory(PageDirectory&); + void release_page_directory(PageDirectory&); byte* create_kernel_alias_for_region(Region&); void remove_kernel_alias_for_region(Region&, byte*); diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp index 1c634f3820..c4ef53777f 100644 --- a/Kernel/Process.cpp +++ b/Kernel/Process.cpp @@ -282,6 +282,149 @@ pid_t Process::sys$fork(RegisterDump& regs) return child->pid(); } +int Process::sys$execve(const char* filename, const char** argv, const char** envp) +{ + VALIDATE_USER_READ(filename, strlen(filename)); + if (argv) { + for (size_t i = 0; argv[i]; ++i) { + VALIDATE_USER_READ(argv[i], strlen(argv[i])); + } + } + if (envp) { + for (size_t i = 0; envp[i]; ++i) { + VALIDATE_USER_READ(envp[i], strlen(envp[i])); + } + } + + String path(filename); + auto parts = path.split('/'); + if (parts.isEmpty()) + return -ENOENT; + + int error; + auto handle = VirtualFileSystem::the().open(path, error, 0, m_cwd ? m_cwd->inode : InodeIdentifier()); + if (!handle) { + ASSERT(error != 0); + return error; + } + + if (!handle->metadata().mayExecute(m_uid, m_gid)) + return -EACCES; + + auto elfData = handle->readEntireFile(); + if (!elfData) + return -EIO; // FIXME: Get a more detailed error from VFS. + + Vector processArguments; + if (argv) { + for (size_t i = 0; argv[i]; ++i) { + processArguments.append(argv[i]); + } + } else { + processArguments.append(parts.last()); + } + + Vector processEnvironment; + if (envp) { + for (size_t i = 0; envp[i]; ++i) { + processEnvironment.append(envp[i]); + } + } + + dword entry_eip = 0; + PageDirectory* old_page_directory; + PageDirectory* new_page_directory; + { + ExecSpace space; + Region* region = nullptr; + + InterruptDisabler disabler; + // Okay, here comes the sleight of hand, pay close attention.. + auto old_regions = move(m_regions); + auto old_subregions = move(m_subregions); + old_page_directory = m_page_directory; + new_page_directory = reinterpret_cast(kmalloc_page_aligned(sizeof(PageDirectory))); + MM.populate_page_directory(*new_page_directory); + m_page_directory = new_page_directory; + + ProcessPagingScope pagingScope(*this); + space.hookableAlloc = [&] (const String& name, size_t size) { + if (!size) + return (void*)nullptr; + size = ((size / 4096) + 1) * 4096; // FIXME: Use ceil_div? + region = allocateRegion(size, String(name)); + return (void*)region->linearAddress.get(); + }; + bool success = space.loadELF(move(elfData)); + if (!success) { + MM.release_page_directory(*new_page_directory); + m_page_directory = old_page_directory; + m_regions = move(old_regions); + m_subregions = move(old_subregions); + kprintf("Failure loading ELF %s\n", path.characters()); + return -ENOEXEC; + } + + space.forEachArea([&] (const String& name, dword offset, size_t size, LinearAddress laddr) { + if (laddr.isNull()) + return; + dword roundedOffset = offset & 0xfffff000; + size_t roundedSize = 4096 * ceilDiv((offset - roundedOffset) + size, 4096u); + LinearAddress roundedLaddr = laddr; + roundedLaddr.mask(0xfffff000); + m_subregions.append(make(*region, roundedOffset, roundedSize, roundedLaddr, String(name))); + MM.mapSubregion(*this, *m_subregions.last()); + }); + + entry_eip = (dword)space.symbolPtr("_start"); + if (!entry_eip) { + MM.release_page_directory(*new_page_directory); + m_page_directory = old_page_directory; + m_regions = move(old_regions); + m_subregions = move(old_subregions); + return -ENOEXEC; + } + } + + InterruptDisabler disabler; + loadTaskRegister(s_kernelProcess->selector()); + + m_name = parts.takeLast(); + + dword old_esp0 = m_tss.esp0; + + memset(&m_tss, 0, sizeof(m_tss)); + m_tss.eflags = 0x0202; + m_tss.eip = entry_eip; + m_tss.cs = 0x1b; + m_tss.ds = 0x23; + m_tss.es = 0x23; + m_tss.fs = 0x23; + m_tss.ss = 0x23; + m_tss.cr3 = (dword)m_page_directory; + auto* stack_region = allocateRegion(defaultStackSize, "stack"); + ASSERT(stack_region); + m_stackTop3 = stack_region->linearAddress.offset(defaultStackSize).get() & 0xfffffff8; + m_tss.esp = m_stackTop3; + m_tss.ss0 = 0x10; + m_tss.esp0 = old_esp0; + m_tss.ss2 = m_pid; + + MM.release_page_directory(*old_page_directory); + + m_executable = handle->vnode(); + m_arguments = move(processArguments); + m_initialEnvironment = move(processEnvironment); + +#ifdef TASK_DEBUG + kprintf("Process %u (%s) execve'd %s @ %p\n", pid(), name().characters(), filename, m_tss.eip); +#endif + + yield(); + ASSERT(false); + return 0; +} + int Process::sys$spawn(const char* path, const char** args) { if (args) { @@ -485,7 +628,7 @@ Process::Process(String&& name, uid_t uid, gid_t gid, pid_t parentPID, RingLevel } m_page_directory = (PageDirectory*)kmalloc_page_aligned(sizeof(PageDirectory)); - MM.populate_page_directory(*this); + MM.populate_page_directory(*m_page_directory); if (fork_parent) { m_file_descriptors.resize(fork_parent->m_file_descriptors.size()); @@ -583,7 +726,7 @@ Process::~Process() m_kernelStack = nullptr; } - MM.release_page_directory(*this); + MM.release_page_directory(*m_page_directory); } void Process::dumpRegions() @@ -771,7 +914,7 @@ bool scheduleNewProcess() for (auto* process = s_processes->head(); process; process = process->next()) { //if (process->state() == Process::BlockedWait || process->state() == Process::BlockedSleep) // continue; - dbgprintf("%w %s(%u)\n", process->state(), process->name().characters(), process->pid()); + dbgprintf("%w %s(%u) @ %w:%x\n", process->state(), process->name().characters(), process->pid(), process->tss().cs, process->tss().eip); } #endif diff --git a/Kernel/Process.h b/Kernel/Process.h index 2ee01dd07a..3f51132d58 100644 --- a/Kernel/Process.h +++ b/Kernel/Process.h @@ -120,6 +120,7 @@ public: int sys$readlink(const char*, char*, size_t); int sys$ttyname_r(int fd, char*, size_t); pid_t sys$fork(RegisterDump&); + int sys$execve(const char* filename, const char** argv, const char** envp); static void initialize(); diff --git a/Kernel/Syscall.cpp b/Kernel/Syscall.cpp index 353dcbf1a9..dd2ca9aeee 100644 --- a/Kernel/Syscall.cpp +++ b/Kernel/Syscall.cpp @@ -130,6 +130,8 @@ static DWORD handle(RegisterDump& regs, DWORD function, DWORD arg1, DWORD arg2, return current->sys$tcsetpgrp((int)arg1, (pid_t)arg2); case Syscall::PosixFork: return current->sys$fork(regs); + case Syscall::PosixExecve: + return current->sys$execve((const char*)arg1, (const char**)arg2, (const char**)arg3); default: kprintf("<%u> int0x80: Unknown function %x requested {%x, %x, %x}\n", current->pid(), function, arg1, arg2, arg3); break; diff --git a/Kernel/Syscall.h b/Kernel/Syscall.h index 8b4208f81e..3576c637cf 100644 --- a/Kernel/Syscall.h +++ b/Kernel/Syscall.h @@ -48,6 +48,7 @@ enum Function { PosixTcsetpgrp = 0x2016, PosixTcgetpgrp = 0x2017, PosixFork = 0x2018, + PosixExecve = 0x2019, }; void initialize(); diff --git a/LibC/unistd.cpp b/LibC/unistd.cpp index 9a3b2459b8..86c9ce237c 100644 --- a/LibC/unistd.cpp +++ b/LibC/unistd.cpp @@ -7,7 +7,14 @@ extern "C" { pid_t fork() { - return Syscall::invoke(Syscall::PosixFork); + int rc = Syscall::invoke(Syscall::PosixFork); + __RETURN_WITH_ERRNO(rc, rc, -1); +} + +int execve(const char* filename, const char** argv, const char** envp) +{ + int rc = Syscall::invoke(Syscall::PosixExecve, (dword)filename, (dword)argv, (dword)envp); + __RETURN_WITH_ERRNO(rc, rc, -1); } uid_t getuid() @@ -27,32 +34,38 @@ pid_t getpid() pid_t setsid() { - return Syscall::invoke(Syscall::PosixSetsid); + int rc = Syscall::invoke(Syscall::PosixSetsid); + __RETURN_WITH_ERRNO(rc, rc, -1); } pid_t tcgetpgrp(int fd) { - return Syscall::invoke(Syscall::PosixTcgetpgrp, (dword)fd); + int rc = Syscall::invoke(Syscall::PosixTcgetpgrp, (dword)fd); + __RETURN_WITH_ERRNO(rc, rc, -1); } int tcsetpgrp(int fd, pid_t pgid) { - return Syscall::invoke(Syscall::PosixTcsetpgrp, (dword)fd, (dword)pgid); + int rc = Syscall::invoke(Syscall::PosixTcsetpgrp, (dword)fd, (dword)pgid); + __RETURN_WITH_ERRNO(rc, rc, -1); } int setpgid(pid_t pid, pid_t pgid) { - return Syscall::invoke(Syscall::PosixSetpgid, (dword)pid, (dword)pgid); + int rc = Syscall::invoke(Syscall::PosixSetpgid, (dword)pid, (dword)pgid); + __RETURN_WITH_ERRNO(rc, rc, -1); } pid_t getpgid(pid_t pid) { - return Syscall::invoke(Syscall::PosixGetpgid, (dword)pid); + int rc = Syscall::invoke(Syscall::PosixGetpgid, (dword)pid); + __RETURN_WITH_ERRNO(rc, rc, -1); } pid_t getpgrp() { - return Syscall::invoke(Syscall::PosixGetpgrp); + int rc = Syscall::invoke(Syscall::PosixGetpgrp); + __RETURN_WITH_ERRNO(rc, rc, -1); } int open(const char* path, int options) diff --git a/LibC/unistd.h b/LibC/unistd.h index 205002dff8..abcf410aba 100644 --- a/LibC/unistd.h +++ b/LibC/unistd.h @@ -9,6 +9,7 @@ extern char** environ; inline int getpagesize() { return 4096; } pid_t fork(); +int execve(const char* filename, const char** argv, const char** envp); pid_t getsid(pid_t); pid_t setsid(); int setpgid(pid_t pid, pid_t pgid); diff --git a/Userland/sh.cpp b/Userland/sh.cpp index 6efa9ce4de..6c83c806e4 100644 --- a/Userland/sh.cpp +++ b/Userland/sh.cpp @@ -39,6 +39,32 @@ static int sh_fork(int, const char**) return 0; } +static int sh_fe(int, const char**) +{ + pid_t pid = fork(); + if (!pid) { + int rc = execve("/bin/ps", nullptr, nullptr); + if (rc < 0) { + perror("execve"); + exit(1); + } + } + return 0; +} + +static int sh_fef(int, const char**) +{ + pid_t pid = fork(); + if (!pid) { + int rc = execve("/bin/psx", nullptr, nullptr); + if (rc < 0) { + perror("execve"); + exit(1); + } + } + return 0; +} + static int sh_exit(int, const char**) { printf("Good-bye!\n"); @@ -101,7 +127,14 @@ static bool handle_builtin(int argc, const char** argv, int& retval) retval = sh_exit(argc, argv); return true; } - + if (!strcmp(argv[0], "fe")) { + retval = sh_fe(argc, argv); + return true; + } + if (!strcmp(argv[0], "fef")) { + retval = sh_fef(argc, argv); + return true; + } if (!strcmp(argv[0], "fork")) { retval = sh_fork(argc, argv); return true; -- cgit v1.2.3