#include "KProcess.h" #include #include #include namespace skyline::kernel::type { KProcess::TlsPage::TlsPage(u64 address) : address(address) {} u64 KProcess::TlsPage::ReserveSlot() { if (Full()) throw exception("Trying to get TLS slot from full page"); slot[index] = true; return Get(index++); // ++ on right will cause increment after evaluation of expression } u64 KProcess::TlsPage::Get(u8 slotNo) { if (slotNo >= constant::TlsSlots) throw exception("TLS slot is out of range"); return address + (constant::TlsSlotSize * slotNo); } bool KProcess::TlsPage::Full() { return slot[constant::TlsSlots - 1]; } u64 KProcess::GetTlsSlot() { for (auto &tlsPage: tlsPages) { if (!tlsPage->Full()) return tlsPage->ReserveSlot(); } auto tlsMem = NewHandle(mainThread, 0, 0, PAGE_SIZE, memory::Permission(true, true, false), memory::Type::ThreadLocal).item; memoryMap[tlsMem->address] = tlsMem; tlsPages.push_back(std::make_shared(tlsMem->address)); auto &tlsPage = tlsPages.back(); if (tlsPages.empty()) tlsPage->ReserveSlot(); // User-mode exception handling return tlsPage->ReserveSlot(); } KProcess::KProcess(const DeviceState &state, pid_t pid, u64 entryPoint, u64 stackBase, u64 stackSize) : mainThread(pid), mainThreadStackSz(stackSize), KSyncObject(state, KType::KProcess) { state.nce->WaitRdy(pid); threadMap[pid] = NewHandle(pid, entryPoint, 0, stackBase + stackSize, GetTlsSlot(), constant::DefaultPriority, this).item; MapPrivateRegion(0, constant::DefHeapSize, {true, true, true}, memory::Type::Heap, memory::Region::Heap); memFd = open(fmt::format("/proc/{}/mem", pid).c_str(), O_RDWR | O_CLOEXEC); // NOLINT(hicpp-signed-bitwise) if (memFd == -1) throw exception(fmt::format("Cannot open file descriptor to /proc/{}/mem, \"{}\"", pid, strerror(errno))); } KProcess::~KProcess() { close(memFd); } /** * @brief Function executed by all child threads after cloning */ int ExecuteChild(void *) { asm volatile("BRK #0xFF"); // BRK #constant::brkRdy (So we know when the thread/process is ready) return 0; } u64 CreateThreadFunc(u64 stackTop) { pid_t pid = clone(&ExecuteChild, reinterpret_cast(stackTop), CLONE_THREAD | CLONE_SIGHAND | CLONE_PTRACE | CLONE_FS | CLONE_VM | CLONE_FILES | CLONE_IO, nullptr); // NOLINT(hicpp-signed-bitwise) return static_cast(pid); } std::shared_ptr KProcess::CreateThread(u64 entryPoint, u64 entryArg, u64 stackTop, u8 priority) { user_pt_regs fregs = {0}; fregs.regs[0] = entryPoint; fregs.regs[1] = stackTop; state.nce->ExecuteFunction((void *) CreateThreadFunc, fregs, mainThread); auto pid = static_cast(fregs.regs[0]); if (pid == -1) throw exception(fmt::format("Cannot create thread: Address: 0x{:X}, Stack Top: 0x{:X}", entryPoint, stackTop)); threadMap[pid] = NewHandle(pid, entryPoint, entryArg, stackTop, GetTlsSlot(), priority, this).item; state.logger->Write(Logger::Info, "EP: 0x{:X}, EA: 0x{:X}, STP: 0x{:X}, PR: 0x{:X}, TLS: {}", entryPoint, entryArg, stackTop, priority, threadMap[pid]->tls); return threadMap[pid]; } void KProcess::ReadMemory(void *destination, u64 offset, size_t size) const { pread64(memFd, destination, size, offset); } void KProcess::WriteMemory(void *source, u64 offset, size_t size) const { pwrite64(memFd, source, size, offset); } int KProcess::GetMemoryFd() const { return memFd; } KProcess::HandleOut KProcess::MapPrivateRegion(u64 address, size_t size, const memory::Permission perms, const memory::Type type, const memory::Region region) { auto mem = NewHandle(mainThread, address, 0, size, perms, type); memoryMap[mem.item->address] = mem.item; memoryRegionMap[region] = mem.item; return mem; } size_t KProcess::GetProgramSize() { size_t sharedSize = 0; for (auto ®ion : memoryRegionMap) sharedSize += region.second->size; return sharedSize; } void KProcess::MutexLock(u64 address) { auto mtxVec = state.thisProcess->mutexMap[address]; u32 mtxVal = state.thisProcess->ReadMemory(address); if (mtxVec.empty()) { mtxVal = (mtxVal & ~constant::mtxOwnerMask) | state.thisThread->handle; state.thisProcess->WriteMemory(mtxVal, address); } else { for (auto thread = mtxVec.begin();; thread++) { if ((*thread)->priority < state.thisThread->priority) { mtxVec.insert(thread, state.thisThread); break; } else if (thread + 1 == mtxVec.end()) { mtxVec.push_back(state.thisThread); break; } } state.thisThread->status = KThread::ThreadStatus::WaitMutex; } } void KProcess::MutexUnlock(u64 address) { auto mtxVec = state.thisProcess->mutexMap[address]; u32 mtxVal = state.thisProcess->ReadMemory(address); if ((mtxVal & constant::mtxOwnerMask) != state.thisThread->pid) throw exception("A non-owner thread tried to release a mutex"); if (mtxVec.empty()) { mtxVal = 0; } else { auto &thread = mtxVec.front(); mtxVal = (mtxVal & ~constant::mtxOwnerMask) | thread->handle; thread->status = KThread::ThreadStatus::Runnable; mtxVec.erase(mtxVec.begin()); if (!mtxVec.empty()) mtxVal |= (~constant::mtxOwnerMask); } state.thisProcess->WriteMemory(mtxVal, address); } }