#include "svc.h" #include namespace skyline::kernel::svc { void SetHeapSize(DeviceState &state) { auto heap = state.thisProcess->MapPrivateRegion(0, state.nce->GetRegister(Wreg::W1), {true, true, false}, memory::Type::Heap, memory::Region::Heap); state.nce->SetRegister(Wreg::W0, constant::status::Success); state.nce->SetRegister(Xreg::X1, heap.item->address); state.logger->Write(Logger::Debug, "Heap size was set to 0x{:X}", state.nce->GetRegister(Wreg::W1)); } void QueryMemory(DeviceState &state) { memory::MemoryInfo memInf; u64 addr = state.nce->GetRegister(Xreg::X2); bool memFree = true; for(const auto& [address, region] : state.thisProcess->memoryMap) { if (addr >= address && addr < (address + region->size)) { memInf = region->GetInfo(); memFree = false; break; } } if (memFree) { memInf = { .baseAddress = static_cast(static_cast(addr / PAGE_SIZE) * PAGE_SIZE), .size = static_cast(-constant::BaseSize + 1), .type = static_cast(memory::Type::Unmapped), }; } state.thisProcess->WriteMemory(memInf, state.nce->GetRegister(Xreg::X0)); state.nce->SetRegister(Wreg::W0, constant::status::Success); } void CreateThread(DeviceState &state) { // TODO: Support Core Mask potentially auto thread = state.thisProcess->CreateThread(state.nce->GetRegister(Xreg::X1), state.nce->GetRegister(Xreg::X2), state.nce->GetRegister(Xreg::X3), static_cast(state.nce->GetRegister(Wreg::W4))); state.nce->SetRegister(Wreg::W0, constant::status::Success); state.nce->SetRegister(Wreg::W1, thread->handle); state.logger->Write(Logger::Info, "Creating a thread: {}", thread->handle); } void StartThread(DeviceState &state) { auto &object = state.thisProcess->handleTable.at(static_cast(state.nce->GetRegister(Wreg::W0))); if (object->objectType == type::KType::KThread) std::static_pointer_cast(object)->Start(); else throw exception("StartThread was called on a non-KThread object"); } void ExitThread(DeviceState &state) { state.os->KillThread(state.thisThread->pid); } void SleepThread(DeviceState &state) { auto in = state.nce->GetRegister(Xreg::X0); switch(in) { case 0: case 1: case 2: state.thisThread->status = type::KThread::ThreadStatus::Runnable; // Will cause the application to awaken on the next iteration of the main loop default: state.thisThread->timeout = GetCurrTimeNs() + in; state.thisThread->status = type::KThread::ThreadStatus::Sleeping; } } void GetThreadPriority(DeviceState &state) { state.nce->SetRegister(Wreg::W1, state.thisProcess->GetHandle(static_cast(state.nce->GetRegister(Wreg::W0)))->priority); state.nce->SetRegister(Wreg::W0, constant::status::Success); } void SetThreadPriority(DeviceState &state) { state.thisProcess->GetHandle(static_cast(state.nce->GetRegister(Wreg::W0)))->Start(); state.nce->SetRegister(Wreg::W0, constant::status::Success); } void MapSharedMemory(DeviceState &state) { auto object = state.thisProcess->GetHandle(static_cast(state.nce->GetRegister(Wreg::W0))); object->Map(state.nce->GetRegister(Xreg::X1), state.nce->GetRegister(Xreg::X2), state.thisProcess->mainThread); state.nce->SetRegister(Wreg::W0, constant::status::Success); } void CloseHandle(DeviceState &state) { auto handle = static_cast(state.nce->GetRegister(Wreg::W0)); state.logger->Write(Logger::Debug, "Closing handle: 0x{:X}", handle); auto &object = state.thisProcess->handleTable.at(handle); switch (object->objectType) { case (type::KType::KThread): state.os->KillThread(std::static_pointer_cast(object)->pid); break; case (type::KType::KProcess): state.os->KillThread(std::static_pointer_cast(object)->mainThread); break; default: state.thisProcess->handleTable.erase(handle); } state.nce->SetRegister(Wreg::W0, constant::status::Success); } void WaitSynchronization(DeviceState &state) { auto numHandles = state.nce->GetRegister(Wreg::W2); if (numHandles > constant::MaxSyncHandles) { state.nce->SetRegister(Wreg::W0, constant::status::MaxHandles); return; } std::vector waitHandles(numHandles); state.thisProcess->ReadMemory(waitHandles.data(), state.nce->GetRegister(Xreg::X1), numHandles * sizeof(handle_t)); for (const auto& handle : waitHandles) { auto object = state.thisProcess->handleTable.at(handle); switch(object->objectType) { case type::KType::KProcess: case type::KType::KThread: case type::KType::KEvent: case type::KType::KSession: break; default: state.nce->SetRegister(Wreg::W0, constant::status::InvHandle); return; } auto syncObject = std::static_pointer_cast(object); if(syncObject->signalled) { state.nce->SetRegister(Wreg::W0, constant::status::Success); return; } state.thisThread->waitObjects.push_back(syncObject); syncObject->waitThreads.push_back(state.thisThread->pid); } state.thisThread->timeout = GetCurrTimeNs() + state.nce->GetRegister(Xreg::X3); state.thisThread->status = type::KThread::ThreadStatus::WaitSync; } void ArbitrateLock(DeviceState &state) { if (state.nce->GetRegister(Wreg::W2) != state.thisThread->handle) throw exception("A process requested locking a thread on behalf of another process"); state.thisProcess->MutexLock(state.nce->GetRegister(Xreg::X1)); state.nce->SetRegister(Wreg::W0, constant::status::Success); } void ArbitrateUnlock(DeviceState &state) { state.thisProcess->MutexUnlock(state.nce->GetRegister(Xreg::X0)); state.nce->SetRegister(Wreg::W0, constant::status::Success); } void WaitProcessWideKeyAtomic(DeviceState &state) { auto mtxAddr = state.nce->GetRegister(Xreg::X0); if (state.nce->GetRegister(Wreg::W2) != state.thisThread->handle) throw exception("svcWaitProcessWideKeyAtomic was called on behalf of another thread"); state.thisProcess->MutexUnlock(mtxAddr); auto &cvarVec = state.thisProcess->condVarMap[state.nce->GetRegister(Xreg::X1)]; for (auto thread = cvarVec.begin();; thread++) { if ((*thread)->priority < state.thisThread->priority) { cvarVec.insert(thread, state.thisThread); break; } else if (thread + 1 == cvarVec.end()) { cvarVec.push_back(state.thisThread); break; } } state.thisThread->status = type::KThread::ThreadStatus::WaitCondVar; state.thisThread->timeout = GetCurrTimeNs() + state.nce->GetRegister(Xreg::X3); state.nce->SetRegister(Wreg::W0, constant::status::Success); } void SignalProcessWideKey(DeviceState &state) { auto address = state.nce->GetRegister(Xreg::X0); auto count = state.nce->GetRegister(Wreg::W1); state.nce->SetRegister(Wreg::W0, constant::status::Success); if (!state.thisProcess->condVarMap.count(address)) return; // No threads to awaken auto &cvarVec = state.thisProcess->condVarMap[address]; count = std::min(count, static_cast(cvarVec.size())); for (uint index = 0; index < count; index++) cvarVec[index]->status = type::KThread::ThreadStatus::Runnable; cvarVec.erase(cvarVec.begin(), cvarVec.begin() + count); if (cvarVec.empty()) state.thisProcess->condVarMap.erase(address); } void ConnectToNamedPort(DeviceState &state) { char port[constant::PortSize + 1]{0}; // +1 so string will always be null terminated state.os->thisProcess->ReadMemory(port, state.nce->GetRegister(Xreg::X1), constant::PortSize); if (std::strcmp(port, "sm:") == 0) state.nce->SetRegister(Wreg::W1, state.os->serviceManager.NewSession(service::Service::sm)); else throw exception(fmt::format("svcConnectToNamedPort tried connecting to invalid port: \"{}\"", port)); state.nce->SetRegister(Wreg::W0, constant::status::Success); } void SendSyncRequest(DeviceState &state) { state.os->serviceManager.SyncRequestHandler(static_cast(state.nce->GetRegister(Xreg::X0))); state.nce->SetRegister(Wreg::W0, constant::status::Success); } void OutputDebugString(DeviceState &state) { std::string debug(state.nce->GetRegister(Xreg::X1), '\0'); state.os->thisProcess->ReadMemory((void *) debug.data(), state.nce->GetRegister(Xreg::X0), state.nce->GetRegister(Xreg::X1)); std::string::size_type pos = 0; while ((pos = debug.find("\r\n", pos)) != std::string::npos) debug.erase(pos, 2); state.logger->Write(Logger::Info, "Debug Output: {}", debug); state.nce->SetRegister(Wreg::W0, 0); } void GetInfo(DeviceState &state) { state.logger->Write(Logger::Debug, "svcGetInfo called with ID0: {}, ID1: {}", state.nce->GetRegister(Wreg::W1), state.nce->GetRegister(Xreg::X3)); switch (state.nce->GetRegister(Wreg::W1)) { case constant::infoState::AllowedCpuIdBitmask: case constant::infoState::AllowedThreadPriorityMask: case constant::infoState::IsCurrentProcessBeingDebugged: case constant::infoState::TitleId: case constant::infoState::PrivilegedProcessId: state.nce->SetRegister(Xreg::X1, 0); break; case constant::infoState::AliasRegionBaseAddr: state.nce->SetRegister(Xreg::X1, constant::MapAddr); break; case constant::infoState::AliasRegionSize: state.nce->SetRegister(Xreg::X1, constant::MapSize); break; case constant::infoState::HeapRegionBaseAddr: state.nce->SetRegister(Xreg::X1, state.os->thisProcess->memoryRegionMap.at(memory::Region::Heap)->address); break; case constant::infoState::HeapRegionSize: state.nce->SetRegister(Xreg::X1, state.os->thisProcess->memoryRegionMap.at(memory::Region::Heap)->size); break; case constant::infoState::TotalMemoryAvailable: state.nce->SetRegister(Xreg::X1, constant::TotalPhyMem); break; case constant::infoState::TotalMemoryUsage: state.nce->SetRegister(Xreg::X1, state.os->thisProcess->memoryRegionMap.at(memory::Region::Heap)->address + state.thisProcess->mainThreadStackSz + state.thisProcess->GetProgramSize()); break; case constant::infoState::AddressSpaceBaseAddr: state.nce->SetRegister(Xreg::X1, constant::BaseAddr); break; case constant::infoState::AddressSpaceSize: state.nce->SetRegister(Xreg::X1, constant::BaseSize); break; case constant::infoState::StackRegionBaseAddr: state.nce->SetRegister(Xreg::X1, state.thisThread->stackTop); break; case constant::infoState::StackRegionSize: state.nce->SetRegister(Xreg::X1, state.thisProcess->mainThreadStackSz); break; case constant::infoState::PersonalMmHeapSize: state.nce->SetRegister(Xreg::X1, constant::TotalPhyMem); break; case constant::infoState::PersonalMmHeapUsage: state.nce->SetRegister(Xreg::X1, state.os->thisProcess->memoryRegionMap.at(memory::Region::Heap)->address + state.thisProcess->mainThreadStackSz); break; case constant::infoState::TotalMemoryAvailableWithoutMmHeap: state.nce->SetRegister(Xreg::X1, constant::TotalPhyMem); // TODO: NPDM specifies SystemResourceSize, subtract that from this break; case constant::infoState::TotalMemoryUsedWithoutMmHeap: state.nce->SetRegister(Xreg::X1, state.os->thisProcess->memoryRegionMap.at(memory::Region::Heap)->address + state.thisProcess->mainThreadStackSz); // TODO: Same as above break; case constant::infoState::UserExceptionContextAddr: state.nce->SetRegister(Xreg::X1, state.thisProcess->tlsPages[0]->Get(0)); break; default: state.logger->Write(Logger::Warn, "Unimplemented svcGetInfo with ID0: {}, ID1: {}", state.nce->GetRegister(Wreg::W1), state.nce->GetRegister(Xreg::X3)); state.nce->SetRegister(Wreg::W0, constant::status::Unimpl); return; } state.nce->SetRegister(Wreg::W0, constant::status::Success); } void ExitProcess(DeviceState &state) { state.os->KillThread(state.thisProcess->mainThread); } }