summaryrefslogtreecommitdiffstats
path: root/src/core/hle/kernel/k_process.h
blob: e562a79b8c0b5ceee4cc81c3ad80c2f169266129 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
// 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 "common/common_types.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_handle_table.h"
#include "core/hle/kernel/k_synchronization_object.h"
#include "core/hle/kernel/k_thread_local_page.h"
#include "core/hle/kernel/k_worker_task.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 KSharedMemoryInfo;
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,
};

enum class ProcessActivity : u32 {
    Runnable,
    Paused,
};

class KProcess final : public KAutoObjectWithSlabHeapAndContainer<KProcess, KWorkerTask> {
    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 process_name,
                                 ProcessType type, KResourceLimit* res_limit);

    /// 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.
    KHandleTable& GetHandleTable() {
        return handle_table;
    }

    /// Gets a const reference to the process' handle table.
    const KHandleTable& 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 program ID corresponding to this process.
    u64 GetProgramID() 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 IncrementRunningThreadCount();
    void DecrementRunningThreadCount();

    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.
    std::list<KThread*>& GetThreadList() {
        return thread_list;
    }

    /// Registers a thread as being created under this process,
    /// adding it to this process' thread list.
    void RegisterThread(KThread* thread);

    /// Unregisters a thread from this process, removing it
    /// from this process' thread list.
    void UnregisterThread(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 ResultSuccess 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);

    bool IsInitialized() const override {
        return is_initialized;
    }

    static void PostDestroy([[maybe_unused]] uintptr_t arg) {}

    void Finalize() override;

    u64 GetId() const override {
        return GetProcessID();
    }

    bool IsSignaled() const override;

    void DoWorkerTaskImpl();

    ResultCode SetActivity(ProcessActivity activity);

    void PinCurrentThread(s32 core_id);
    void UnpinCurrentThread(s32 core_id);
    void UnpinThread(KThread* thread);

    KLightLock& GetStateLock() {
        return state_lock;
    }

    ResultCode AddSharedMemory(KSharedMemory* shmem, VAddr address, size_t size);
    void RemoveSharedMemory(KSharedMemory* shmem, VAddr address, size_t size);

    ///////////////////////////////////////////////////////////////////////////////////////////////
    // Thread-local storage management

    // Marks the next available region as used and returns the address of the slot.
    [[nodiscard]] ResultCode CreateThreadLocalRegion(VAddr* out);

    // Frees a used TLS slot identified by the given address
    ResultCode DeleteThreadLocalRegion(VAddr addr);

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;

    /// 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.
    std::atomic<u64> total_process_running_time_ticks = 0;

    /// Per-process handle table for storing created object handles in.
    KHandleTable 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<KThread*> thread_list;

    /// List of shared memory that are running with this process as their owner.
    std::list<KSharedMemoryInfo*> shared_memory_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<u16> num_running_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;
    KLightLock list_lock;

    using TLPTree =
        Common::IntrusiveRedBlackTreeBaseTraits<KThreadLocalPage>::TreeType<KThreadLocalPage>;
    using TLPIterator = TLPTree::iterator;
    TLPTree fully_used_tlp_tree;
    TLPTree partially_used_tlp_tree;
};

} // namespace Kernel