summaryrefslogtreecommitdiffstats
path: root/src/core/hle/kernel/k_process.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/hle/kernel/k_process.h')
-rw-r--r--src/core/hle/kernel/k_process.h480
1 files changed, 480 insertions, 0 deletions
diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h
new file mode 100644
index 000000000..961c0d9ba
--- /dev/null
+++ b/src/core/hle/kernel/k_process.h
@@ -0,0 +1,480 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include <cstddef>
+#include <list>
+#include <string>
+#include <unordered_map>
+#include <vector>
+#include "common/common_types.h"
+#include "core/hle/kernel/handle_table.h"
+#include "core/hle/kernel/k_address_arbiter.h"
+#include "core/hle/kernel/k_auto_object.h"
+#include "core/hle/kernel/k_condition_variable.h"
+#include "core/hle/kernel/k_synchronization_object.h"
+#include "core/hle/kernel/process_capability.h"
+#include "core/hle/kernel/slab_helpers.h"
+#include "core/hle/result.h"
+
+namespace Core {
+class System;
+}
+
+namespace FileSys {
+class ProgramMetadata;
+}
+
+namespace Kernel {
+
+class KernelCore;
+class KPageTable;
+class KResourceLimit;
+class KThread;
+class TLSPage;
+
+struct CodeSet;
+
+enum class MemoryRegion : u16 {
+ APPLICATION = 1,
+ SYSTEM = 2,
+ BASE = 3,
+};
+
+/**
+ * Indicates the status of a Process instance.
+ *
+ * @note These match the values as used by kernel,
+ * so new entries should only be added if RE
+ * shows that a new value has been introduced.
+ */
+enum class ProcessStatus {
+ Created,
+ CreatedWithDebuggerAttached,
+ Running,
+ WaitingForDebuggerToAttach,
+ DebuggerAttached,
+ Exiting,
+ Exited,
+ DebugBreak,
+};
+
+class KProcess final
+ : public KAutoObjectWithSlabHeapAndContainer<KProcess, KSynchronizationObject> {
+ KERNEL_AUTOOBJECT_TRAITS(KProcess, KSynchronizationObject);
+
+public:
+ explicit KProcess(KernelCore& kernel);
+ ~KProcess() override;
+
+ enum : u64 {
+ /// Lowest allowed process ID for a kernel initial process.
+ InitialKIPIDMin = 1,
+ /// Highest allowed process ID for a kernel initial process.
+ InitialKIPIDMax = 80,
+
+ /// Lowest allowed process ID for a userland process.
+ ProcessIDMin = 81,
+ /// Highest allowed process ID for a userland process.
+ ProcessIDMax = 0xFFFFFFFFFFFFFFFF,
+ };
+
+ // Used to determine how process IDs are assigned.
+ enum class ProcessType {
+ KernelInternal,
+ Userland,
+ };
+
+ static constexpr std::size_t RANDOM_ENTROPY_SIZE = 4;
+
+ static ResultCode Initialize(KProcess* process, Core::System& system, std::string name,
+ ProcessType type);
+
+ /// Gets a reference to the process' page table.
+ KPageTable& PageTable() {
+ return *page_table;
+ }
+
+ /// Gets const a reference to the process' page table.
+ const KPageTable& PageTable() const {
+ return *page_table;
+ }
+
+ /// Gets a reference to the process' handle table.
+ HandleTable& GetHandleTable() {
+ return handle_table;
+ }
+
+ /// Gets a const reference to the process' handle table.
+ const HandleTable& GetHandleTable() const {
+ return handle_table;
+ }
+
+ ResultCode SignalToAddress(VAddr address) {
+ return condition_var.SignalToAddress(address);
+ }
+
+ ResultCode WaitForAddress(Handle handle, VAddr address, u32 tag) {
+ return condition_var.WaitForAddress(handle, address, tag);
+ }
+
+ void SignalConditionVariable(u64 cv_key, int32_t count) {
+ return condition_var.Signal(cv_key, count);
+ }
+
+ ResultCode WaitConditionVariable(VAddr address, u64 cv_key, u32 tag, s64 ns) {
+ return condition_var.Wait(address, cv_key, tag, ns);
+ }
+
+ ResultCode SignalAddressArbiter(VAddr address, Svc::SignalType signal_type, s32 value,
+ s32 count) {
+ return address_arbiter.SignalToAddress(address, signal_type, value, count);
+ }
+
+ ResultCode WaitAddressArbiter(VAddr address, Svc::ArbitrationType arb_type, s32 value,
+ s64 timeout) {
+ return address_arbiter.WaitForAddress(address, arb_type, value, timeout);
+ }
+
+ /// Gets the address to the process' dedicated TLS region.
+ VAddr GetTLSRegionAddress() const {
+ return tls_region_address;
+ }
+
+ /// Gets the current status of the process
+ ProcessStatus GetStatus() const {
+ return status;
+ }
+
+ /// Gets the unique ID that identifies this particular process.
+ u64 GetProcessID() const {
+ return process_id;
+ }
+
+ /// Gets the title ID corresponding to this process.
+ u64 GetTitleID() const {
+ return program_id;
+ }
+
+ /// Gets the resource limit descriptor for this process
+ KResourceLimit* GetResourceLimit() const;
+
+ /// Gets the ideal CPU core ID for this process
+ u8 GetIdealCoreId() const {
+ return ideal_core;
+ }
+
+ /// Checks if the specified thread priority is valid.
+ bool CheckThreadPriority(s32 prio) const {
+ return ((1ULL << prio) & GetPriorityMask()) != 0;
+ }
+
+ /// Gets the bitmask of allowed cores that this process' threads can run on.
+ u64 GetCoreMask() const {
+ return capabilities.GetCoreMask();
+ }
+
+ /// Gets the bitmask of allowed thread priorities.
+ u64 GetPriorityMask() const {
+ return capabilities.GetPriorityMask();
+ }
+
+ /// Gets the amount of secure memory to allocate for memory management.
+ u32 GetSystemResourceSize() const {
+ return system_resource_size;
+ }
+
+ /// Gets the amount of secure memory currently in use for memory management.
+ u32 GetSystemResourceUsage() const {
+ // On hardware, this returns the amount of system resource memory that has
+ // been used by the kernel. This is problematic for Yuzu to emulate, because
+ // system resource memory is used for page tables -- and yuzu doesn't really
+ // have a way to calculate how much memory is required for page tables for
+ // the current process at any given time.
+ // TODO: Is this even worth implementing? Games may retrieve this value via
+ // an SDK function that gets used + available system resource size for debug
+ // or diagnostic purposes. However, it seems unlikely that a game would make
+ // decisions based on how much system memory is dedicated to its page tables.
+ // Is returning a value other than zero wise?
+ return 0;
+ }
+
+ /// Whether this process is an AArch64 or AArch32 process.
+ bool Is64BitProcess() const {
+ return is_64bit_process;
+ }
+
+ [[nodiscard]] bool IsSuspended() const {
+ return is_suspended;
+ }
+
+ void SetSuspended(bool suspended) {
+ is_suspended = suspended;
+ }
+
+ /// Gets the total running time of the process instance in ticks.
+ u64 GetCPUTimeTicks() const {
+ return total_process_running_time_ticks;
+ }
+
+ /// Updates the total running time, adding the given ticks to it.
+ void UpdateCPUTimeTicks(u64 ticks) {
+ total_process_running_time_ticks += ticks;
+ }
+
+ /// Gets the process schedule count, used for thread yelding
+ s64 GetScheduledCount() const {
+ return schedule_count;
+ }
+
+ /// Increments the process schedule count, used for thread yielding.
+ void IncrementScheduledCount() {
+ ++schedule_count;
+ }
+
+ void IncrementThreadCount();
+ void DecrementThreadCount();
+
+ void SetRunningThread(s32 core, KThread* thread, u64 idle_count) {
+ running_threads[core] = thread;
+ running_thread_idle_counts[core] = idle_count;
+ }
+
+ void ClearRunningThread(KThread* thread) {
+ for (size_t i = 0; i < running_threads.size(); ++i) {
+ if (running_threads[i] == thread) {
+ running_threads[i] = nullptr;
+ }
+ }
+ }
+
+ [[nodiscard]] KThread* GetRunningThread(s32 core) const {
+ return running_threads[core];
+ }
+
+ bool ReleaseUserException(KThread* thread);
+
+ [[nodiscard]] KThread* GetPinnedThread(s32 core_id) const {
+ ASSERT(0 <= core_id && core_id < static_cast<s32>(Core::Hardware::NUM_CPU_CORES));
+ return pinned_threads[core_id];
+ }
+
+ /// Gets 8 bytes of random data for svcGetInfo RandomEntropy
+ u64 GetRandomEntropy(std::size_t index) const {
+ return random_entropy.at(index);
+ }
+
+ /// Retrieves the total physical memory available to this process in bytes.
+ u64 GetTotalPhysicalMemoryAvailable() const;
+
+ /// Retrieves the total physical memory available to this process in bytes,
+ /// without the size of the personal system resource heap added to it.
+ u64 GetTotalPhysicalMemoryAvailableWithoutSystemResource() const;
+
+ /// Retrieves the total physical memory used by this process in bytes.
+ u64 GetTotalPhysicalMemoryUsed() const;
+
+ /// Retrieves the total physical memory used by this process in bytes,
+ /// without the size of the personal system resource heap added to it.
+ u64 GetTotalPhysicalMemoryUsedWithoutSystemResource() const;
+
+ /// Gets the list of all threads created with this process as their owner.
+ const std::list<const KThread*>& GetThreadList() const {
+ return thread_list;
+ }
+
+ /// Registers a thread as being created under this process,
+ /// adding it to this process' thread list.
+ void RegisterThread(const KThread* thread);
+
+ /// Unregisters a thread from this process, removing it
+ /// from this process' thread list.
+ void UnregisterThread(const KThread* thread);
+
+ /// Clears the signaled state of the process if and only if it's signaled.
+ ///
+ /// @pre The process must not be already terminated. If this is called on a
+ /// terminated process, then ERR_INVALID_STATE will be returned.
+ ///
+ /// @pre The process must be in a signaled state. If this is called on a
+ /// process instance that is not signaled, ERR_INVALID_STATE will be
+ /// returned.
+ ResultCode Reset();
+
+ /**
+ * Loads process-specifics configuration info with metadata provided
+ * by an executable.
+ *
+ * @param metadata The provided metadata to load process specific info from.
+ *
+ * @returns RESULT_SUCCESS if all relevant metadata was able to be
+ * loaded and parsed. Otherwise, an error code is returned.
+ */
+ ResultCode LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size);
+
+ /**
+ * Starts the main application thread for this process.
+ *
+ * @param main_thread_priority The priority for the main thread.
+ * @param stack_size The stack size for the main thread in bytes.
+ */
+ void Run(s32 main_thread_priority, u64 stack_size);
+
+ /**
+ * Prepares a process for termination by stopping all of its threads
+ * and clearing any other resources.
+ */
+ void PrepareForTermination();
+
+ void LoadModule(CodeSet code_set, VAddr base_addr);
+
+ virtual bool IsInitialized() const override {
+ return is_initialized;
+ }
+
+ static void PostDestroy([[maybe_unused]] uintptr_t arg) {}
+
+ virtual void Finalize();
+
+ virtual u64 GetId() const override final {
+ return GetProcessID();
+ }
+
+ virtual bool IsSignaled() const override;
+
+ void PinCurrentThread();
+ void UnpinCurrentThread();
+
+ KLightLock& GetStateLock() {
+ return state_lock;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ // Thread-local storage management
+
+ // Marks the next available region as used and returns the address of the slot.
+ [[nodiscard]] VAddr CreateTLSRegion();
+
+ // Frees a used TLS slot identified by the given address
+ void FreeTLSRegion(VAddr tls_address);
+
+private:
+ void PinThread(s32 core_id, KThread* thread) {
+ ASSERT(0 <= core_id && core_id < static_cast<s32>(Core::Hardware::NUM_CPU_CORES));
+ ASSERT(thread != nullptr);
+ ASSERT(pinned_threads[core_id] == nullptr);
+ pinned_threads[core_id] = thread;
+ }
+
+ void UnpinThread(s32 core_id, KThread* thread) {
+ ASSERT(0 <= core_id && core_id < static_cast<s32>(Core::Hardware::NUM_CPU_CORES));
+ ASSERT(thread != nullptr);
+ ASSERT(pinned_threads[core_id] == thread);
+ pinned_threads[core_id] = nullptr;
+ }
+
+ /// Changes the process status. If the status is different
+ /// from the current process status, then this will trigger
+ /// a process signal.
+ void ChangeStatus(ProcessStatus new_status);
+
+ /// Allocates the main thread stack for the process, given the stack size in bytes.
+ ResultCode AllocateMainThreadStack(std::size_t stack_size);
+
+ /// Memory manager for this process
+ std::unique_ptr<KPageTable> page_table;
+
+ /// Current status of the process
+ ProcessStatus status{};
+
+ /// The ID of this process
+ u64 process_id = 0;
+
+ /// Title ID corresponding to the process
+ u64 program_id = 0;
+
+ /// Specifies additional memory to be reserved for the process's memory management by the
+ /// system. When this is non-zero, secure memory is allocated and used for page table allocation
+ /// instead of using the normal global page tables/memory block management.
+ u32 system_resource_size = 0;
+
+ /// Resource limit descriptor for this process
+ KResourceLimit* resource_limit{};
+
+ /// The ideal CPU core for this process, threads are scheduled on this core by default.
+ u8 ideal_core = 0;
+
+ /// The Thread Local Storage area is allocated as processes create threads,
+ /// each TLS area is 0x200 bytes, so one page (0x1000) is split up in 8 parts, and each part
+ /// holds the TLS for a specific thread. This vector contains which parts are in use for each
+ /// page as a bitmask.
+ /// This vector will grow as more pages are allocated for new threads.
+ std::vector<TLSPage> tls_pages;
+
+ /// Contains the parsed process capability descriptors.
+ ProcessCapabilities capabilities;
+
+ /// Whether or not this process is AArch64, or AArch32.
+ /// By default, we currently assume this is true, unless otherwise
+ /// specified by metadata provided to the process during loading.
+ bool is_64bit_process = true;
+
+ /// Total running time for the process in ticks.
+ u64 total_process_running_time_ticks = 0;
+
+ /// Per-process handle table for storing created object handles in.
+ HandleTable handle_table;
+
+ /// Per-process address arbiter.
+ KAddressArbiter address_arbiter;
+
+ /// The per-process mutex lock instance used for handling various
+ /// forms of services, such as lock arbitration, and condition
+ /// variable related facilities.
+ KConditionVariable condition_var;
+
+ /// Address indicating the location of the process' dedicated TLS region.
+ VAddr tls_region_address = 0;
+
+ /// Random values for svcGetInfo RandomEntropy
+ std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy{};
+
+ /// List of threads that are running with this process as their owner.
+ std::list<const KThread*> thread_list;
+
+ /// Address of the top of the main thread's stack
+ VAddr main_thread_stack_top{};
+
+ /// Size of the main thread's stack
+ std::size_t main_thread_stack_size{};
+
+ /// Memory usage capacity for the process
+ std::size_t memory_usage_capacity{};
+
+ /// Process total image size
+ std::size_t image_size{};
+
+ /// Schedule count of this process
+ s64 schedule_count{};
+
+ bool is_signaled{};
+ bool is_suspended{};
+ bool is_initialized{};
+
+ std::atomic<s32> num_created_threads{};
+ std::atomic<u16> num_threads{};
+ u16 peak_num_threads{};
+
+ std::array<KThread*, Core::Hardware::NUM_CPU_CORES> running_threads{};
+ std::array<u64, Core::Hardware::NUM_CPU_CORES> running_thread_idle_counts{};
+ std::array<KThread*, Core::Hardware::NUM_CPU_CORES> pinned_threads{};
+
+ KThread* exception_thread{};
+
+ KLightLock state_lock;
+};
+
+} // namespace Kernel