/* * Copyright (c) 2018-2020 Atmosphère-NX * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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 this program. If not, see . */ #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace ams::kern { class KProcess final : public KAutoObjectWithSlabHeapAndContainer, public KWorkerTask { MESOSPHERE_AUTOOBJECT_TRAITS(KProcess, KSynchronizationObject); public: enum State { State_Created = ams::svc::ProcessState_Created, State_CreatedAttached = ams::svc::ProcessState_CreatedAttached, State_Running = ams::svc::ProcessState_Running, State_Crashed = ams::svc::ProcessState_Crashed, State_RunningAttached = ams::svc::ProcessState_RunningAttached, State_Terminating = ams::svc::ProcessState_Terminating, State_Terminated = ams::svc::ProcessState_Terminated, State_DebugBreak = ams::svc::ProcessState_DebugBreak, }; using ThreadList = util::IntrusiveListMemberTraits<&KThread::process_list_node>::ListType; private: using SharedMemoryInfoList = util::IntrusiveListBaseTraits::ListType; using TLPTree = util::IntrusiveRedBlackTreeBaseTraits::TreeType; using TLPIterator = TLPTree::iterator; private: KProcessPageTable page_table{}; std::atomic used_kernel_memory_size{}; TLPTree fully_used_tlp_tree{}; TLPTree partially_used_tlp_tree{}; s32 ideal_core_id{}; void *attached_object{}; KResourceLimit *resource_limit{}; KVirtualAddress system_resource_address{}; size_t system_resource_num_pages{}; size_t memory_release_hint{}; State state{}; KLightLock state_lock{}; KLightLock list_lock{}; KConditionVariable cond_var{}; KAddressArbiter address_arbiter{}; u64 entropy[4]{}; bool is_signaled{}; bool is_initialized{}; bool is_application{}; char name[13]{}; std::atomic num_threads{}; u16 peak_num_threads{}; u32 flags{}; KMemoryManager::Pool memory_pool{}; s64 schedule_count{}; KCapabilities capabilities{}; ams::svc::ProgramId program_id{}; u64 process_id{}; s64 creation_time{}; KProcessAddress code_address{}; size_t code_size{}; size_t main_thread_stack_size{}; size_t max_process_memory{}; u32 version{}; KHandleTable handle_table{}; KProcessAddress plr_address{}; void *plr_heap_address{}; KThread *exception_thread{}; ThreadList thread_list{}; SharedMemoryInfoList shared_memory_list{}; bool is_suspended{}; bool is_jit_debug{}; ams::svc::DebugEvent jit_debug_event_type{}; ams::svc::DebugException jit_debug_exception_type{}; uintptr_t jit_debug_params[4]{}; u64 jit_debug_thread_id{}; KWaitObject wait_object{}; KThread *running_threads[cpu::NumCores]{}; u64 running_thread_idle_counts[cpu::NumCores]{}; KThread *pinned_threads[cpu::NumCores]{}; std::atomic num_created_threads{}; std::atomic cpu_time{}; std::atomic num_process_switches{}; std::atomic num_thread_switches{}; std::atomic num_fpu_switches{}; std::atomic num_supervisor_calls{}; std::atomic num_ipc_messages{}; std::atomic num_ipc_replies{}; std::atomic num_ipc_receives{}; KDynamicPageManager dynamic_page_manager{}; KMemoryBlockSlabManager memory_block_slab_manager{}; KBlockInfoManager block_info_manager{}; KPageTableManager page_table_manager{}; private: Result Initialize(const ams::svc::CreateProcessParameter ¶ms); public: KProcess() { /* ... */ } virtual ~KProcess() { /* ... */ } Result Initialize(const ams::svc::CreateProcessParameter ¶ms, const KPageGroup &pg, const u32 *caps, s32 num_caps, KResourceLimit *res_limit, KMemoryManager::Pool pool); void Exit(); constexpr const char *GetName() const { return this->name; } constexpr ams::svc::ProgramId GetProgramId() const { return this->program_id; } constexpr u64 GetProcessId() const { return this->process_id; } constexpr u64 GetCoreMask() const { return this->capabilities.GetCoreMask(); } constexpr u64 GetPriorityMask() const { return this->capabilities.GetPriorityMask(); } constexpr s32 GetIdealCoreId() const { return this->ideal_core_id; } constexpr void SetIdealCoreId(s32 core_id) { this->ideal_core_id = core_id; } constexpr bool CheckThreadPriority(s32 prio) const { return ((1ul << prio) & this->GetPriorityMask()) != 0; } constexpr bool Is64Bit() const { return this->flags & ams::svc::CreateProcessFlag_Is64Bit; } constexpr KProcessAddress GetEntryPoint() const { return this->code_address; } constexpr bool IsSuspended() const { return this->is_suspended; } bool EnterUserException(); bool LeaveUserException(); bool ReleaseUserException(KThread *thread); KThread *GetPreemptionStatePinnedThread(s32 core_id) const { MESOSPHERE_ASSERT(0 <= core_id && core_id < static_cast(cpu::NumCores)); return this->pinned_threads[core_id]; } void CopySvcPermissionsTo(KThread::StackParameters &sp) { this->capabilities.CopySvcPermissionsTo(sp); } constexpr KResourceLimit *GetResourceLimit() const { return this->resource_limit; } bool ReserveResource(ams::svc::LimitableResource which, s64 value); bool ReserveResource(ams::svc::LimitableResource which, s64 value, s64 timeout); void ReleaseResource(ams::svc::LimitableResource which, s64 value); void ReleaseResource(ams::svc::LimitableResource which, s64 value, s64 hint); constexpr KLightLock &GetStateLock() { return this->state_lock; } constexpr KLightLock &GetListLock() { return this->list_lock; } constexpr KProcessPageTable &GetPageTable() { return this->page_table; } constexpr const KProcessPageTable &GetPageTable() const { return this->page_table; } constexpr KHandleTable &GetHandleTable() { return this->handle_table; } constexpr const KHandleTable &GetHandleTable() const { return this->handle_table; } size_t GetUsedUserPhysicalMemorySize() const; size_t GetTotalUserPhysicalMemorySize() const; size_t GetUsedNonSystemUserPhysicalMemorySize() const; size_t GetTotalNonSystemUserPhysicalMemorySize() const; Result CreateThreadLocalRegion(KProcessAddress *out); void *GetThreadLocalRegionPointer(KProcessAddress addr); constexpr KProcessAddress GetProcessLocalRegionAddress() const { return this->plr_address; } void AddCpuTime(s64 diff) { this->cpu_time += diff; } constexpr s64 GetScheduledCount() const { return this->schedule_count; } void IncrementScheduledCount() { ++this->schedule_count; } void IncrementThreadCount(); void DecrementThreadCount(); void ClearRunningThread(KThread *thread) { for (size_t i = 0; i < util::size(this->running_threads); ++i) { if (this->running_threads[i] == thread) { this->running_threads[i] = nullptr; } } } void RegisterThread(KThread *thread); void UnregisterThread(KThread *thread); Result Run(s32 priority, size_t stack_size); Result Reset(); void SetPreemptionState(); Result SignalToAddress(KProcessAddress address) { return this->cond_var.SignalToAddress(address); } Result WaitForAddress(ams::svc::Handle handle, KProcessAddress address, u32 tag) { return this->cond_var.WaitForAddress(handle, address, tag); } void SignalConditionVariable(uintptr_t cv_key, int32_t count) { return this->cond_var.Signal(cv_key, count); } Result WaitConditionVariable(KProcessAddress address, uintptr_t cv_key, u32 tag, s64 ns) { return this->cond_var.Wait(address, cv_key, tag, ns); } static void Switch(KProcess *cur_process, KProcess *next_process) { /* Set the current process pointer. */ SetCurrentProcess(next_process); /* Update the current page table. */ if (next_process) { next_process->GetPageTable().Activate(next_process->GetProcessId()); } else { Kernel::GetKernelPageTable().Activate(); } } public: /* Overridden parent functions. */ virtual bool IsInitialized() const override { return this->is_initialized; } static void PostDestroy(uintptr_t arg) { /* ... */ } virtual void Finalize() override; virtual u64 GetId() const override { return this->GetProcessId(); } virtual bool IsSignaled() const override { MESOSPHERE_ASSERT_THIS(); MESOSPHERE_ASSERT(KScheduler::IsSchedulerLockedByCurrentThread()); return this->is_signaled; } virtual void DoWorkerTask() override; private: void ChangeState(State new_state) { if (this->state != new_state) { this->state = new_state; this->is_signaled = true; this->NotifyAvailable(); } } }; }