summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar bunnei2019-04-19 19:09:20 -0400
committerGravatar GitHub2019-04-19 19:09:20 -0400
commit40dc893c372c81c687eca2d0b964220a8f8aeab4 (patch)
tree1c05675d446978752a749f2cb18a797c6b737998 /src
parentMerge pull request #2397 from lioncash/thread-unused (diff)
parentcore/core: Move process execution start to System's Load() (diff)
downloadyuzu-40dc893c372c81c687eca2d0b964220a8f8aeab4.tar.gz
yuzu-40dc893c372c81c687eca2d0b964220a8f8aeab4.tar.xz
yuzu-40dc893c372c81c687eca2d0b964220a8f8aeab4.zip
Merge pull request #2374 from lioncash/pagetable
core: Reorganize boot order
Diffstat (limited to 'src')
-rw-r--r--src/core/arm/arm_interface.h14
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.cpp23
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.h6
-rw-r--r--src/core/arm/unicorn/arm_unicorn.h2
-rw-r--r--src/core/core.cpp33
-rw-r--r--src/core/cpu_core_manager.cpp6
-rw-r--r--src/core/cpu_core_manager.h7
-rw-r--r--src/core/hle/kernel/kernel.cpp7
-rw-r--r--src/core/hle/kernel/process.cpp17
-rw-r--r--src/core/hle/kernel/process.h7
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp40
-rw-r--r--src/core/loader/deconstructed_rom_directory.h2
-rw-r--r--src/core/loader/elf.cpp15
-rw-r--r--src/core/loader/elf.h2
-rw-r--r--src/core/loader/loader.h8
-rw-r--r--src/core/loader/nax.cpp30
-rw-r--r--src/core/loader/nax.h2
-rw-r--r--src/core/loader/nca.cpp26
-rw-r--r--src/core/loader/nca.h2
-rw-r--r--src/core/loader/nro.cpp14
-rw-r--r--src/core/loader/nro.h2
-rw-r--r--src/core/loader/nso.cpp11
-rw-r--r--src/core/loader/nso.h2
-rw-r--r--src/core/loader/nsp.cpp38
-rw-r--r--src/core/loader/nsp.h2
-rw-r--r--src/core/loader/xci.cpp28
-rw-r--r--src/core/loader/xci.h2
-rw-r--r--src/core/memory.cpp16
-rw-r--r--src/core/memory.h5
-rw-r--r--src/video_core/gpu.h5
-rw-r--r--src/video_core/gpu_asynch.cpp6
-rw-r--r--src/video_core/gpu_asynch.h5
-rw-r--r--src/video_core/gpu_synch.cpp4
-rw-r--r--src/video_core/gpu_synch.h1
-rw-r--r--src/video_core/gpu_thread.cpp17
-rw-r--r--src/video_core/gpu_thread.h6
-rw-r--r--src/video_core/video_core.cpp10
-rw-r--r--src/video_core/video_core.h7
38 files changed, 253 insertions, 177 deletions
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index 4dfd41b43..978b1518f 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -7,6 +7,10 @@
7#include <array> 7#include <array>
8#include "common/common_types.h" 8#include "common/common_types.h"
9 9
10namespace Common {
11struct PageTable;
12}
13
10namespace Kernel { 14namespace Kernel {
11enum class VMAPermission : u8; 15enum class VMAPermission : u8;
12} 16}
@@ -49,8 +53,14 @@ public:
49 /// Clear all instruction cache 53 /// Clear all instruction cache
50 virtual void ClearInstructionCache() = 0; 54 virtual void ClearInstructionCache() = 0;
51 55
52 /// Notify CPU emulation that page tables have changed 56 /// Notifies CPU emulation that the current page table has changed.
53 virtual void PageTableChanged() = 0; 57 ///
58 /// @param new_page_table The new page table.
59 /// @param new_address_space_size_in_bits The new usable size of the address space in bits.
60 /// This can be either 32, 36, or 39 on official software.
61 ///
62 virtual void PageTableChanged(Common::PageTable& new_page_table,
63 std::size_t new_address_space_size_in_bits) = 0;
54 64
55 /** 65 /**
56 * Set the Program Counter to an address 66 * Set the Program Counter to an address
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp
index dc96e35d5..44307fa19 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic.cpp
@@ -14,7 +14,6 @@
14#include "core/core_timing.h" 14#include "core/core_timing.h"
15#include "core/core_timing_util.h" 15#include "core/core_timing_util.h"
16#include "core/gdbstub/gdbstub.h" 16#include "core/gdbstub/gdbstub.h"
17#include "core/hle/kernel/kernel.h"
18#include "core/hle/kernel/process.h" 17#include "core/hle/kernel/process.h"
19#include "core/hle/kernel/svc.h" 18#include "core/hle/kernel/svc.h"
20#include "core/hle/kernel/vm_manager.h" 19#include "core/hle/kernel/vm_manager.h"
@@ -129,18 +128,16 @@ public:
129 u64 tpidr_el0 = 0; 128 u64 tpidr_el0 = 0;
130}; 129};
131 130
132std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const { 131std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit(Common::PageTable& page_table,
133 auto* current_process = system.Kernel().CurrentProcess(); 132 std::size_t address_space_bits) const {
134 auto** const page_table = current_process->VMManager().page_table.pointers.data();
135
136 Dynarmic::A64::UserConfig config; 133 Dynarmic::A64::UserConfig config;
137 134
138 // Callbacks 135 // Callbacks
139 config.callbacks = cb.get(); 136 config.callbacks = cb.get();
140 137
141 // Memory 138 // Memory
142 config.page_table = reinterpret_cast<void**>(page_table); 139 config.page_table = reinterpret_cast<void**>(page_table.pointers.data());
143 config.page_table_address_space_bits = current_process->VMManager().GetAddressSpaceWidth(); 140 config.page_table_address_space_bits = address_space_bits;
144 config.silently_mirror_page_table = false; 141 config.silently_mirror_page_table = false;
145 142
146 // Multi-process state 143 // Multi-process state
@@ -176,12 +173,7 @@ ARM_Dynarmic::ARM_Dynarmic(System& system, ExclusiveMonitor& exclusive_monitor,
176 std::size_t core_index) 173 std::size_t core_index)
177 : cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), inner_unicorn{system}, 174 : cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), inner_unicorn{system},
178 core_index{core_index}, system{system}, 175 core_index{core_index}, system{system},
179 exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} { 176 exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {}
180 ThreadContext ctx{};
181 inner_unicorn.SaveContext(ctx);
182 PageTableChanged();
183 LoadContext(ctx);
184}
185 177
186ARM_Dynarmic::~ARM_Dynarmic() = default; 178ARM_Dynarmic::~ARM_Dynarmic() = default;
187 179
@@ -276,8 +268,9 @@ void ARM_Dynarmic::ClearExclusiveState() {
276 jit->ClearExclusiveState(); 268 jit->ClearExclusiveState();
277} 269}
278 270
279void ARM_Dynarmic::PageTableChanged() { 271void ARM_Dynarmic::PageTableChanged(Common::PageTable& page_table,
280 jit = MakeJit(); 272 std::size_t new_address_space_size_in_bits) {
273 jit = MakeJit(page_table, new_address_space_size_in_bits);
281} 274}
282 275
283DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(std::size_t core_count) : monitor(core_count) {} 276DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(std::size_t core_count) : monitor(core_count) {}
diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h
index c1db254e8..b701e97a3 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.h
+++ b/src/core/arm/dynarmic/arm_dynarmic.h
@@ -48,10 +48,12 @@ public:
48 void ClearExclusiveState() override; 48 void ClearExclusiveState() override;
49 49
50 void ClearInstructionCache() override; 50 void ClearInstructionCache() override;
51 void PageTableChanged() override; 51 void PageTableChanged(Common::PageTable& new_page_table,
52 std::size_t new_address_space_size_in_bits) override;
52 53
53private: 54private:
54 std::unique_ptr<Dynarmic::A64::Jit> MakeJit() const; 55 std::unique_ptr<Dynarmic::A64::Jit> MakeJit(Common::PageTable& page_table,
56 std::size_t address_space_bits) const;
55 57
56 friend class ARM_Dynarmic_Callbacks; 58 friend class ARM_Dynarmic_Callbacks;
57 std::unique_ptr<ARM_Dynarmic_Callbacks> cb; 59 std::unique_ptr<ARM_Dynarmic_Callbacks> cb;
diff --git a/src/core/arm/unicorn/arm_unicorn.h b/src/core/arm/unicorn/arm_unicorn.h
index 209fc16ad..34e974b4d 100644
--- a/src/core/arm/unicorn/arm_unicorn.h
+++ b/src/core/arm/unicorn/arm_unicorn.h
@@ -41,7 +41,7 @@ public:
41 void Run() override; 41 void Run() override;
42 void Step() override; 42 void Step() override;
43 void ClearInstructionCache() override; 43 void ClearInstructionCache() override;
44 void PageTableChanged() override{}; 44 void PageTableChanged(Common::PageTable&, std::size_t) override {}
45 void RecordBreak(GDBStub::BreakpointAddress bkpt); 45 void RecordBreak(GDBStub::BreakpointAddress bkpt);
46 46
47private: 47private:
diff --git a/src/core/core.cpp b/src/core/core.cpp
index bc9e887b6..175a5f2ea 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -3,9 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <array> 5#include <array>
6#include <map>
7#include <memory> 6#include <memory>
8#include <thread>
9#include <utility> 7#include <utility>
10 8
11#include "common/file_util.h" 9#include "common/file_util.h"
@@ -38,8 +36,6 @@
38#include "frontend/applets/software_keyboard.h" 36#include "frontend/applets/software_keyboard.h"
39#include "frontend/applets/web_browser.h" 37#include "frontend/applets/web_browser.h"
40#include "video_core/debug_utils/debug_utils.h" 38#include "video_core/debug_utils/debug_utils.h"
41#include "video_core/gpu_asynch.h"
42#include "video_core/gpu_synch.h"
43#include "video_core/renderer_base.h" 39#include "video_core/renderer_base.h"
44#include "video_core/video_core.h" 40#include "video_core/video_core.h"
45 41
@@ -81,7 +77,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
81 return vfs->OpenFile(path, FileSys::Mode::Read); 77 return vfs->OpenFile(path, FileSys::Mode::Read);
82} 78}
83struct System::Impl { 79struct System::Impl {
84 explicit Impl(System& system) : kernel{system} {} 80 explicit Impl(System& system) : kernel{system}, cpu_core_manager{system} {}
85 81
86 Cpu& CurrentCpuCore() { 82 Cpu& CurrentCpuCore() {
87 return cpu_core_manager.GetCurrentCore(); 83 return cpu_core_manager.GetCurrentCore();
@@ -99,6 +95,7 @@ struct System::Impl {
99 LOG_DEBUG(HW_Memory, "initialized OK"); 95 LOG_DEBUG(HW_Memory, "initialized OK");
100 96
101 core_timing.Initialize(); 97 core_timing.Initialize();
98 cpu_core_manager.Initialize();
102 kernel.Initialize(); 99 kernel.Initialize();
103 100
104 const auto current_time = std::chrono::duration_cast<std::chrono::seconds>( 101 const auto current_time = std::chrono::duration_cast<std::chrono::seconds>(
@@ -120,9 +117,6 @@ struct System::Impl {
120 if (web_browser == nullptr) 117 if (web_browser == nullptr)
121 web_browser = std::make_unique<Core::Frontend::DefaultWebBrowserApplet>(); 118 web_browser = std::make_unique<Core::Frontend::DefaultWebBrowserApplet>();
122 119
123 auto main_process = Kernel::Process::Create(system, "main");
124 kernel.MakeCurrentProcess(main_process.get());
125
126 telemetry_session = std::make_unique<Core::TelemetrySession>(); 120 telemetry_session = std::make_unique<Core::TelemetrySession>();
127 service_manager = std::make_shared<Service::SM::ServiceManager>(); 121 service_manager = std::make_shared<Service::SM::ServiceManager>();
128 122
@@ -134,15 +128,9 @@ struct System::Impl {
134 return ResultStatus::ErrorVideoCore; 128 return ResultStatus::ErrorVideoCore;
135 } 129 }
136 130
137 is_powered_on = true; 131 gpu_core = VideoCore::CreateGPU(system);
138
139 if (Settings::values.use_asynchronous_gpu_emulation) {
140 gpu_core = std::make_unique<VideoCommon::GPUAsynch>(system, *renderer);
141 } else {
142 gpu_core = std::make_unique<VideoCommon::GPUSynch>(system, *renderer);
143 }
144 132
145 cpu_core_manager.Initialize(system); 133 is_powered_on = true;
146 134
147 LOG_DEBUG(Core, "Initialized OK"); 135 LOG_DEBUG(Core, "Initialized OK");
148 136
@@ -179,7 +167,8 @@ struct System::Impl {
179 return init_result; 167 return init_result;
180 } 168 }
181 169
182 const Loader::ResultStatus load_result{app_loader->Load(*kernel.CurrentProcess())}; 170 auto main_process = Kernel::Process::Create(system, "main");
171 const auto [load_result, load_parameters] = app_loader->Load(*main_process);
183 if (load_result != Loader::ResultStatus::Success) { 172 if (load_result != Loader::ResultStatus::Success) {
184 LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<int>(load_result)); 173 LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<int>(load_result));
185 Shutdown(); 174 Shutdown();
@@ -187,6 +176,16 @@ struct System::Impl {
187 return static_cast<ResultStatus>(static_cast<u32>(ResultStatus::ErrorLoader) + 176 return static_cast<ResultStatus>(static_cast<u32>(ResultStatus::ErrorLoader) +
188 static_cast<u32>(load_result)); 177 static_cast<u32>(load_result));
189 } 178 }
179 kernel.MakeCurrentProcess(main_process.get());
180
181 // Main process has been loaded and been made current.
182 // Begin GPU and CPU execution.
183 gpu_core->Start();
184 cpu_core_manager.StartThreads();
185
186 // All threads are started, begin main process execution, now that we're in the clear.
187 main_process->Run(load_parameters->main_thread_priority,
188 load_parameters->main_thread_stack_size);
190 189
191 status = ResultStatus::Success; 190 status = ResultStatus::Success;
192 return status; 191 return status;
diff --git a/src/core/cpu_core_manager.cpp b/src/core/cpu_core_manager.cpp
index 93bc5619c..8fcb4eeb1 100644
--- a/src/core/cpu_core_manager.cpp
+++ b/src/core/cpu_core_manager.cpp
@@ -19,17 +19,19 @@ void RunCpuCore(const System& system, Cpu& cpu_state) {
19} 19}
20} // Anonymous namespace 20} // Anonymous namespace
21 21
22CpuCoreManager::CpuCoreManager() = default; 22CpuCoreManager::CpuCoreManager(System& system) : system{system} {}
23CpuCoreManager::~CpuCoreManager() = default; 23CpuCoreManager::~CpuCoreManager() = default;
24 24
25void CpuCoreManager::Initialize(System& system) { 25void CpuCoreManager::Initialize() {
26 barrier = std::make_unique<CpuBarrier>(); 26 barrier = std::make_unique<CpuBarrier>();
27 exclusive_monitor = Cpu::MakeExclusiveMonitor(cores.size()); 27 exclusive_monitor = Cpu::MakeExclusiveMonitor(cores.size());
28 28
29 for (std::size_t index = 0; index < cores.size(); ++index) { 29 for (std::size_t index = 0; index < cores.size(); ++index) {
30 cores[index] = std::make_unique<Cpu>(system, *exclusive_monitor, *barrier, index); 30 cores[index] = std::make_unique<Cpu>(system, *exclusive_monitor, *barrier, index);
31 } 31 }
32}
32 33
34void CpuCoreManager::StartThreads() {
33 // Create threads for CPU cores 1-3, and build thread_to_cpu map 35 // Create threads for CPU cores 1-3, and build thread_to_cpu map
34 // CPU core 0 is run on the main thread 36 // CPU core 0 is run on the main thread
35 thread_to_cpu[std::this_thread::get_id()] = cores[0].get(); 37 thread_to_cpu[std::this_thread::get_id()] = cores[0].get();
diff --git a/src/core/cpu_core_manager.h b/src/core/cpu_core_manager.h
index a4d70ec56..2cbbf8216 100644
--- a/src/core/cpu_core_manager.h
+++ b/src/core/cpu_core_manager.h
@@ -18,7 +18,7 @@ class System;
18 18
19class CpuCoreManager { 19class CpuCoreManager {
20public: 20public:
21 CpuCoreManager(); 21 explicit CpuCoreManager(System& system);
22 CpuCoreManager(const CpuCoreManager&) = delete; 22 CpuCoreManager(const CpuCoreManager&) = delete;
23 CpuCoreManager(CpuCoreManager&&) = delete; 23 CpuCoreManager(CpuCoreManager&&) = delete;
24 24
@@ -27,7 +27,8 @@ public:
27 CpuCoreManager& operator=(const CpuCoreManager&) = delete; 27 CpuCoreManager& operator=(const CpuCoreManager&) = delete;
28 CpuCoreManager& operator=(CpuCoreManager&&) = delete; 28 CpuCoreManager& operator=(CpuCoreManager&&) = delete;
29 29
30 void Initialize(System& system); 30 void Initialize();
31 void StartThreads();
31 void Shutdown(); 32 void Shutdown();
32 33
33 Cpu& GetCore(std::size_t index); 34 Cpu& GetCore(std::size_t index);
@@ -54,6 +55,8 @@ private:
54 55
55 /// Map of guest threads to CPU cores 56 /// Map of guest threads to CPU cores
56 std::map<std::thread::id, Cpu*> thread_to_cpu; 57 std::map<std::thread::id, Cpu*> thread_to_cpu;
58
59 System& system;
57}; 60};
58 61
59} // namespace Core 62} // namespace Core
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 4d58e7c69..8539fabe4 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -182,7 +182,12 @@ void KernelCore::AppendNewProcess(SharedPtr<Process> process) {
182 182
183void KernelCore::MakeCurrentProcess(Process* process) { 183void KernelCore::MakeCurrentProcess(Process* process) {
184 impl->current_process = process; 184 impl->current_process = process;
185 Memory::SetCurrentPageTable(&process->VMManager().page_table); 185
186 if (process == nullptr) {
187 return;
188 }
189
190 Memory::SetCurrentPageTable(*process);
186} 191}
187 192
188Process* KernelCore::CurrentProcess() { 193Process* KernelCore::CurrentProcess() {
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index 8b2b3877d..6d7a7e754 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -28,12 +28,12 @@ namespace {
28 * 28 *
29 * @param owner_process The parent process for the main thread 29 * @param owner_process The parent process for the main thread
30 * @param kernel The kernel instance to create the main thread under. 30 * @param kernel The kernel instance to create the main thread under.
31 * @param entry_point The address at which the thread should start execution
32 * @param priority The priority to give the main thread 31 * @param priority The priority to give the main thread
33 */ 32 */
34void SetupMainThread(Process& owner_process, KernelCore& kernel, VAddr entry_point, u32 priority) { 33void SetupMainThread(Process& owner_process, KernelCore& kernel, u32 priority) {
35 // Initialize new "main" thread 34 const auto& vm_manager = owner_process.VMManager();
36 const VAddr stack_top = owner_process.VMManager().GetTLSIORegionEndAddress(); 35 const VAddr entry_point = vm_manager.GetCodeRegionBaseAddress();
36 const VAddr stack_top = vm_manager.GetTLSIORegionEndAddress();
37 auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0, 37 auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0,
38 owner_process.GetIdealCore(), stack_top, owner_process); 38 owner_process.GetIdealCore(), stack_top, owner_process);
39 39
@@ -105,8 +105,6 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) {
105 is_64bit_process = metadata.Is64BitProgram(); 105 is_64bit_process = metadata.Is64BitProgram();
106 106
107 vm_manager.Reset(metadata.GetAddressSpaceType()); 107 vm_manager.Reset(metadata.GetAddressSpaceType());
108 // Ensure that the potentially resized page table is seen by CPU backends.
109 Memory::SetCurrentPageTable(&vm_manager.page_table);
110 108
111 const auto& caps = metadata.GetKernelCapabilities(); 109 const auto& caps = metadata.GetKernelCapabilities();
112 const auto capability_init_result = 110 const auto capability_init_result =
@@ -118,7 +116,7 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) {
118 return handle_table.SetSize(capabilities.GetHandleTableSize()); 116 return handle_table.SetSize(capabilities.GetHandleTableSize());
119} 117}
120 118
121void Process::Run(VAddr entry_point, s32 main_thread_priority, u64 stack_size) { 119void Process::Run(s32 main_thread_priority, u64 stack_size) {
122 // The kernel always ensures that the given stack size is page aligned. 120 // The kernel always ensures that the given stack size is page aligned.
123 main_thread_stack_size = Common::AlignUp(stack_size, Memory::PAGE_SIZE); 121 main_thread_stack_size = Common::AlignUp(stack_size, Memory::PAGE_SIZE);
124 122
@@ -134,7 +132,7 @@ void Process::Run(VAddr entry_point, s32 main_thread_priority, u64 stack_size) {
134 vm_manager.LogLayout(); 132 vm_manager.LogLayout();
135 ChangeStatus(ProcessStatus::Running); 133 ChangeStatus(ProcessStatus::Running);
136 134
137 SetupMainThread(*this, kernel, entry_point, main_thread_priority); 135 SetupMainThread(*this, kernel, main_thread_priority);
138} 136}
139 137
140void Process::PrepareForTermination() { 138void Process::PrepareForTermination() {
@@ -241,9 +239,6 @@ void Process::LoadModule(CodeSet module_, VAddr base_addr) {
241 MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeData); 239 MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeData);
242 240
243 code_memory_size += module_.memory.size(); 241 code_memory_size += module_.memory.size();
244
245 // Clear instruction cache in CPU JIT
246 system.InvalidateCpuInstructionCaches();
247} 242}
248 243
249Process::Process(Core::System& system) 244Process::Process(Core::System& system)
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h
index dda52f4c0..bf3b7eef3 100644
--- a/src/core/hle/kernel/process.h
+++ b/src/core/hle/kernel/process.h
@@ -225,9 +225,12 @@ public:
225 ResultCode LoadFromMetadata(const FileSys::ProgramMetadata& metadata); 225 ResultCode LoadFromMetadata(const FileSys::ProgramMetadata& metadata);
226 226
227 /** 227 /**
228 * Applies address space changes and launches the process main thread. 228 * Starts the main application thread for this process.
229 *
230 * @param main_thread_priority The priority for the main thread.
231 * @param stack_size The stack size for the main thread in bytes.
229 */ 232 */
230 void Run(VAddr entry_point, s32 main_thread_priority, u64 stack_size); 233 void Run(s32 main_thread_priority, u64 stack_size);
231 234
232 /** 235 /**
233 * Prepares a process for termination by stopping all of its threads 236 * Prepares a process for termination by stopping all of its threads
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index 07aa7a1cd..10b13fb1d 100644
--- a/src/core/loader/deconstructed_rom_directory.cpp
+++ b/src/core/loader/deconstructed_rom_directory.cpp
@@ -86,25 +86,29 @@ FileType AppLoader_DeconstructedRomDirectory::IdentifyType(const FileSys::Virtua
86 return FileType::Error; 86 return FileType::Error;
87} 87}
88 88
89ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process) { 89AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirectory::Load(
90 Kernel::Process& process) {
90 if (is_loaded) { 91 if (is_loaded) {
91 return ResultStatus::ErrorAlreadyLoaded; 92 return {ResultStatus::ErrorAlreadyLoaded, {}};
92 } 93 }
93 94
94 if (dir == nullptr) { 95 if (dir == nullptr) {
95 if (file == nullptr) 96 if (file == nullptr) {
96 return ResultStatus::ErrorNullFile; 97 return {ResultStatus::ErrorNullFile, {}};
98 }
99
97 dir = file->GetContainingDirectory(); 100 dir = file->GetContainingDirectory();
98 } 101 }
99 102
100 // Read meta to determine title ID 103 // Read meta to determine title ID
101 FileSys::VirtualFile npdm = dir->GetFile("main.npdm"); 104 FileSys::VirtualFile npdm = dir->GetFile("main.npdm");
102 if (npdm == nullptr) 105 if (npdm == nullptr) {
103 return ResultStatus::ErrorMissingNPDM; 106 return {ResultStatus::ErrorMissingNPDM, {}};
107 }
104 108
105 ResultStatus result = metadata.Load(npdm); 109 const ResultStatus result = metadata.Load(npdm);
106 if (result != ResultStatus::Success) { 110 if (result != ResultStatus::Success) {
107 return result; 111 return {result, {}};
108 } 112 }
109 113
110 if (override_update) { 114 if (override_update) {
@@ -114,23 +118,24 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process)
114 118
115 // Reread in case PatchExeFS affected the main.npdm 119 // Reread in case PatchExeFS affected the main.npdm
116 npdm = dir->GetFile("main.npdm"); 120 npdm = dir->GetFile("main.npdm");
117 if (npdm == nullptr) 121 if (npdm == nullptr) {
118 return ResultStatus::ErrorMissingNPDM; 122 return {ResultStatus::ErrorMissingNPDM, {}};
123 }
119 124
120 ResultStatus result2 = metadata.Load(npdm); 125 const ResultStatus result2 = metadata.Load(npdm);
121 if (result2 != ResultStatus::Success) { 126 if (result2 != ResultStatus::Success) {
122 return result2; 127 return {result2, {}};
123 } 128 }
124 metadata.Print(); 129 metadata.Print();
125 130
126 const FileSys::ProgramAddressSpaceType arch_bits{metadata.GetAddressSpaceType()}; 131 const FileSys::ProgramAddressSpaceType arch_bits{metadata.GetAddressSpaceType()};
127 if (arch_bits == FileSys::ProgramAddressSpaceType::Is32Bit || 132 if (arch_bits == FileSys::ProgramAddressSpaceType::Is32Bit ||
128 arch_bits == FileSys::ProgramAddressSpaceType::Is32BitNoMap) { 133 arch_bits == FileSys::ProgramAddressSpaceType::Is32BitNoMap) {
129 return ResultStatus::Error32BitISA; 134 return {ResultStatus::Error32BitISA, {}};
130 } 135 }
131 136
132 if (process.LoadFromMetadata(metadata).IsError()) { 137 if (process.LoadFromMetadata(metadata).IsError()) {
133 return ResultStatus::ErrorUnableToParseKernelMetadata; 138 return {ResultStatus::ErrorUnableToParseKernelMetadata, {}};
134 } 139 }
135 140
136 const FileSys::PatchManager pm(metadata.GetTitleID()); 141 const FileSys::PatchManager pm(metadata.GetTitleID());
@@ -150,7 +155,7 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process)
150 const auto tentative_next_load_addr = 155 const auto tentative_next_load_addr =
151 AppLoader_NSO::LoadModule(process, *module_file, load_addr, should_pass_arguments, pm); 156 AppLoader_NSO::LoadModule(process, *module_file, load_addr, should_pass_arguments, pm);
152 if (!tentative_next_load_addr) { 157 if (!tentative_next_load_addr) {
153 return ResultStatus::ErrorLoadingNSO; 158 return {ResultStatus::ErrorLoadingNSO, {}};
154 } 159 }
155 160
156 next_load_addr = *tentative_next_load_addr; 161 next_load_addr = *tentative_next_load_addr;
@@ -159,8 +164,6 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process)
159 GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false); 164 GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false);
160 } 165 }
161 166
162 process.Run(base_address, metadata.GetMainThreadPriority(), metadata.GetMainThreadStackSize());
163
164 // Find the RomFS by searching for a ".romfs" file in this directory 167 // Find the RomFS by searching for a ".romfs" file in this directory
165 const auto& files = dir->GetFiles(); 168 const auto& files = dir->GetFiles();
166 const auto romfs_iter = 169 const auto romfs_iter =
@@ -175,7 +178,8 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process)
175 } 178 }
176 179
177 is_loaded = true; 180 is_loaded = true;
178 return ResultStatus::Success; 181 return {ResultStatus::Success,
182 LoadParameters{metadata.GetMainThreadPriority(), metadata.GetMainThreadStackSize()}};
179} 183}
180 184
181ResultStatus AppLoader_DeconstructedRomDirectory::ReadRomFS(FileSys::VirtualFile& dir) { 185ResultStatus AppLoader_DeconstructedRomDirectory::ReadRomFS(FileSys::VirtualFile& dir) {
diff --git a/src/core/loader/deconstructed_rom_directory.h b/src/core/loader/deconstructed_rom_directory.h
index 1615cb5a8..1a65c16a4 100644
--- a/src/core/loader/deconstructed_rom_directory.h
+++ b/src/core/loader/deconstructed_rom_directory.h
@@ -37,7 +37,7 @@ public:
37 return IdentifyType(file); 37 return IdentifyType(file);
38 } 38 }
39 39
40 ResultStatus Load(Kernel::Process& process) override; 40 LoadResult Load(Kernel::Process& process) override;
41 41
42 ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; 42 ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
43 ResultStatus ReadIcon(std::vector<u8>& buffer) override; 43 ResultStatus ReadIcon(std::vector<u8>& buffer) override;
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp
index 46ac372f6..6d4b02375 100644
--- a/src/core/loader/elf.cpp
+++ b/src/core/loader/elf.cpp
@@ -382,13 +382,15 @@ FileType AppLoader_ELF::IdentifyType(const FileSys::VirtualFile& file) {
382 return FileType::Error; 382 return FileType::Error;
383} 383}
384 384
385ResultStatus AppLoader_ELF::Load(Kernel::Process& process) { 385AppLoader_ELF::LoadResult AppLoader_ELF::Load(Kernel::Process& process) {
386 if (is_loaded) 386 if (is_loaded) {
387 return ResultStatus::ErrorAlreadyLoaded; 387 return {ResultStatus::ErrorAlreadyLoaded, {}};
388 }
388 389
389 std::vector<u8> buffer = file->ReadAllBytes(); 390 std::vector<u8> buffer = file->ReadAllBytes();
390 if (buffer.size() != file->GetSize()) 391 if (buffer.size() != file->GetSize()) {
391 return ResultStatus::ErrorIncorrectELFFileSize; 392 return {ResultStatus::ErrorIncorrectELFFileSize, {}};
393 }
392 394
393 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); 395 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
394 ElfReader elf_reader(&buffer[0]); 396 ElfReader elf_reader(&buffer[0]);
@@ -396,10 +398,9 @@ ResultStatus AppLoader_ELF::Load(Kernel::Process& process) {
396 const VAddr entry_point = codeset.entrypoint; 398 const VAddr entry_point = codeset.entrypoint;
397 399
398 process.LoadModule(std::move(codeset), entry_point); 400 process.LoadModule(std::move(codeset), entry_point);
399 process.Run(entry_point, 48, Memory::DEFAULT_STACK_SIZE);
400 401
401 is_loaded = true; 402 is_loaded = true;
402 return ResultStatus::Success; 403 return {ResultStatus::Success, LoadParameters{48, Memory::DEFAULT_STACK_SIZE}};
403} 404}
404 405
405} // namespace Loader 406} // namespace Loader
diff --git a/src/core/loader/elf.h b/src/core/loader/elf.h
index a2d33021c..7ef7770a6 100644
--- a/src/core/loader/elf.h
+++ b/src/core/loader/elf.h
@@ -26,7 +26,7 @@ public:
26 return IdentifyType(file); 26 return IdentifyType(file);
27 } 27 }
28 28
29 ResultStatus Load(Kernel::Process& process) override; 29 LoadResult Load(Kernel::Process& process) override;
30}; 30};
31 31
32} // namespace Loader 32} // namespace Loader
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index bb925f4a6..f7846db52 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -131,6 +131,12 @@ std::ostream& operator<<(std::ostream& os, ResultStatus status);
131/// Interface for loading an application 131/// Interface for loading an application
132class AppLoader : NonCopyable { 132class AppLoader : NonCopyable {
133public: 133public:
134 struct LoadParameters {
135 s32 main_thread_priority;
136 u64 main_thread_stack_size;
137 };
138 using LoadResult = std::pair<ResultStatus, std::optional<LoadParameters>>;
139
134 explicit AppLoader(FileSys::VirtualFile file); 140 explicit AppLoader(FileSys::VirtualFile file);
135 virtual ~AppLoader(); 141 virtual ~AppLoader();
136 142
@@ -145,7 +151,7 @@ public:
145 * @param process The newly created process. 151 * @param process The newly created process.
146 * @return The status result of the operation. 152 * @return The status result of the operation.
147 */ 153 */
148 virtual ResultStatus Load(Kernel::Process& process) = 0; 154 virtual LoadResult Load(Kernel::Process& process) = 0;
149 155
150 /** 156 /**
151 * Loads the system mode that this application needs. 157 * Loads the system mode that this application needs.
diff --git a/src/core/loader/nax.cpp b/src/core/loader/nax.cpp
index 93a970d10..34efef09a 100644
--- a/src/core/loader/nax.cpp
+++ b/src/core/loader/nax.cpp
@@ -41,31 +41,37 @@ FileType AppLoader_NAX::GetFileType() const {
41 return IdentifyTypeImpl(*nax); 41 return IdentifyTypeImpl(*nax);
42} 42}
43 43
44ResultStatus AppLoader_NAX::Load(Kernel::Process& process) { 44AppLoader_NAX::LoadResult AppLoader_NAX::Load(Kernel::Process& process) {
45 if (is_loaded) { 45 if (is_loaded) {
46 return ResultStatus::ErrorAlreadyLoaded; 46 return {ResultStatus::ErrorAlreadyLoaded, {}};
47 } 47 }
48 48
49 if (nax->GetStatus() != ResultStatus::Success) 49 const auto nax_status = nax->GetStatus();
50 return nax->GetStatus(); 50 if (nax_status != ResultStatus::Success) {
51 return {nax_status, {}};
52 }
51 53
52 const auto nca = nax->AsNCA(); 54 const auto nca = nax->AsNCA();
53 if (nca == nullptr) { 55 if (nca == nullptr) {
54 if (!Core::Crypto::KeyManager::KeyFileExists(false)) 56 if (!Core::Crypto::KeyManager::KeyFileExists(false)) {
55 return ResultStatus::ErrorMissingProductionKeyFile; 57 return {ResultStatus::ErrorMissingProductionKeyFile, {}};
56 return ResultStatus::ErrorNAXInconvertibleToNCA; 58 }
59
60 return {ResultStatus::ErrorNAXInconvertibleToNCA, {}};
57 } 61 }
58 62
59 if (nca->GetStatus() != ResultStatus::Success) 63 const auto nca_status = nca->GetStatus();
60 return nca->GetStatus(); 64 if (nca_status != ResultStatus::Success) {
65 return {nca_status, {}};
66 }
61 67
62 const auto result = nca_loader->Load(process); 68 const auto result = nca_loader->Load(process);
63 if (result != ResultStatus::Success) 69 if (result.first != ResultStatus::Success) {
64 return result; 70 return result;
71 }
65 72
66 is_loaded = true; 73 is_loaded = true;
67 74 return result;
68 return ResultStatus::Success;
69} 75}
70 76
71ResultStatus AppLoader_NAX::ReadRomFS(FileSys::VirtualFile& dir) { 77ResultStatus AppLoader_NAX::ReadRomFS(FileSys::VirtualFile& dir) {
diff --git a/src/core/loader/nax.h b/src/core/loader/nax.h
index f40079574..00f1659c1 100644
--- a/src/core/loader/nax.h
+++ b/src/core/loader/nax.h
@@ -33,7 +33,7 @@ public:
33 33
34 FileType GetFileType() const override; 34 FileType GetFileType() const override;
35 35
36 ResultStatus Load(Kernel::Process& process) override; 36 LoadResult Load(Kernel::Process& process) override;
37 37
38 ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; 38 ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
39 u64 ReadRomFSIVFCOffset() const override; 39 u64 ReadRomFSIVFCOffset() const override;
diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp
index ce8196fcf..b3f8f1083 100644
--- a/src/core/loader/nca.cpp
+++ b/src/core/loader/nca.cpp
@@ -30,36 +30,38 @@ FileType AppLoader_NCA::IdentifyType(const FileSys::VirtualFile& file) {
30 return FileType::Error; 30 return FileType::Error;
31} 31}
32 32
33ResultStatus AppLoader_NCA::Load(Kernel::Process& process) { 33AppLoader_NCA::LoadResult AppLoader_NCA::Load(Kernel::Process& process) {
34 if (is_loaded) { 34 if (is_loaded) {
35 return ResultStatus::ErrorAlreadyLoaded; 35 return {ResultStatus::ErrorAlreadyLoaded, {}};
36 } 36 }
37 37
38 const auto result = nca->GetStatus(); 38 const auto result = nca->GetStatus();
39 if (result != ResultStatus::Success) { 39 if (result != ResultStatus::Success) {
40 return result; 40 return {result, {}};
41 } 41 }
42 42
43 if (nca->GetType() != FileSys::NCAContentType::Program) 43 if (nca->GetType() != FileSys::NCAContentType::Program) {
44 return ResultStatus::ErrorNCANotProgram; 44 return {ResultStatus::ErrorNCANotProgram, {}};
45 }
45 46
46 const auto exefs = nca->GetExeFS(); 47 const auto exefs = nca->GetExeFS();
47 48 if (exefs == nullptr) {
48 if (exefs == nullptr) 49 return {ResultStatus::ErrorNoExeFS, {}};
49 return ResultStatus::ErrorNoExeFS; 50 }
50 51
51 directory_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(exefs, true); 52 directory_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(exefs, true);
52 53
53 const auto load_result = directory_loader->Load(process); 54 const auto load_result = directory_loader->Load(process);
54 if (load_result != ResultStatus::Success) 55 if (load_result.first != ResultStatus::Success) {
55 return load_result; 56 return load_result;
57 }
56 58
57 if (nca->GetRomFS() != nullptr && nca->GetRomFS()->GetSize() > 0) 59 if (nca->GetRomFS() != nullptr && nca->GetRomFS()->GetSize() > 0) {
58 Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this)); 60 Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this));
61 }
59 62
60 is_loaded = true; 63 is_loaded = true;
61 64 return load_result;
62 return ResultStatus::Success;
63} 65}
64 66
65ResultStatus AppLoader_NCA::ReadRomFS(FileSys::VirtualFile& dir) { 67ResultStatus AppLoader_NCA::ReadRomFS(FileSys::VirtualFile& dir) {
diff --git a/src/core/loader/nca.h b/src/core/loader/nca.h
index b9f077468..94f0ed677 100644
--- a/src/core/loader/nca.h
+++ b/src/core/loader/nca.h
@@ -33,7 +33,7 @@ public:
33 return IdentifyType(file); 33 return IdentifyType(file);
34 } 34 }
35 35
36 ResultStatus Load(Kernel::Process& process) override; 36 LoadResult Load(Kernel::Process& process) override;
37 37
38 ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; 38 ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
39 u64 ReadRomFSIVFCOffset() const override; 39 u64 ReadRomFSIVFCOffset() const override;
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
index 31e4a0c84..6a0ca389b 100644
--- a/src/core/loader/nro.cpp
+++ b/src/core/loader/nro.cpp
@@ -201,25 +201,25 @@ bool AppLoader_NRO::LoadNro(Kernel::Process& process, const FileSys::VfsFile& fi
201 return LoadNroImpl(process, file.ReadAllBytes(), file.GetName(), load_base); 201 return LoadNroImpl(process, file.ReadAllBytes(), file.GetName(), load_base);
202} 202}
203 203
204ResultStatus AppLoader_NRO::Load(Kernel::Process& process) { 204AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::Process& process) {
205 if (is_loaded) { 205 if (is_loaded) {
206 return ResultStatus::ErrorAlreadyLoaded; 206 return {ResultStatus::ErrorAlreadyLoaded, {}};
207 } 207 }
208 208
209 // Load NRO 209 // Load NRO
210 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); 210 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
211 211
212 if (!LoadNro(process, *file, base_address)) { 212 if (!LoadNro(process, *file, base_address)) {
213 return ResultStatus::ErrorLoadingNRO; 213 return {ResultStatus::ErrorLoadingNRO, {}};
214 } 214 }
215 215
216 if (romfs != nullptr) 216 if (romfs != nullptr) {
217 Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this)); 217 Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this));
218 218 }
219 process.Run(base_address, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE);
220 219
221 is_loaded = true; 220 is_loaded = true;
222 return ResultStatus::Success; 221 return {ResultStatus::Success,
222 LoadParameters{Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE}};
223} 223}
224 224
225ResultStatus AppLoader_NRO::ReadIcon(std::vector<u8>& buffer) { 225ResultStatus AppLoader_NRO::ReadIcon(std::vector<u8>& buffer) {
diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h
index 85b0ed644..1ffdae805 100644
--- a/src/core/loader/nro.h
+++ b/src/core/loader/nro.h
@@ -37,7 +37,7 @@ public:
37 return IdentifyType(file); 37 return IdentifyType(file);
38 } 38 }
39 39
40 ResultStatus Load(Kernel::Process& process) override; 40 LoadResult Load(Kernel::Process& process) override;
41 41
42 ResultStatus ReadIcon(std::vector<u8>& buffer) override; 42 ResultStatus ReadIcon(std::vector<u8>& buffer) override;
43 ResultStatus ReadProgramId(u64& out_program_id) override; 43 ResultStatus ReadProgramId(u64& out_program_id) override;
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index d7c47c197..a86653204 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -169,22 +169,21 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
169 return load_base + image_size; 169 return load_base + image_size;
170} 170}
171 171
172ResultStatus AppLoader_NSO::Load(Kernel::Process& process) { 172AppLoader_NSO::LoadResult AppLoader_NSO::Load(Kernel::Process& process) {
173 if (is_loaded) { 173 if (is_loaded) {
174 return ResultStatus::ErrorAlreadyLoaded; 174 return {ResultStatus::ErrorAlreadyLoaded, {}};
175 } 175 }
176 176
177 // Load module 177 // Load module
178 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); 178 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
179 if (!LoadModule(process, *file, base_address, true)) { 179 if (!LoadModule(process, *file, base_address, true)) {
180 return ResultStatus::ErrorLoadingNSO; 180 return {ResultStatus::ErrorLoadingNSO, {}};
181 } 181 }
182 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), base_address); 182 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), base_address);
183 183
184 process.Run(base_address, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE);
185
186 is_loaded = true; 184 is_loaded = true;
187 return ResultStatus::Success; 185 return {ResultStatus::Success,
186 LoadParameters{Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE}};
188} 187}
189 188
190} // namespace Loader 189} // namespace Loader
diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h
index 4674c3724..fdce9191c 100644
--- a/src/core/loader/nso.h
+++ b/src/core/loader/nso.h
@@ -84,7 +84,7 @@ public:
84 VAddr load_base, bool should_pass_arguments, 84 VAddr load_base, bool should_pass_arguments,
85 std::optional<FileSys::PatchManager> pm = {}); 85 std::optional<FileSys::PatchManager> pm = {});
86 86
87 ResultStatus Load(Kernel::Process& process) override; 87 LoadResult Load(Kernel::Process& process) override;
88}; 88};
89 89
90} // namespace Loader 90} // namespace Loader
diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp
index 7da1f8960..ad56bbb38 100644
--- a/src/core/loader/nsp.cpp
+++ b/src/core/loader/nsp.cpp
@@ -72,37 +72,45 @@ FileType AppLoader_NSP::IdentifyType(const FileSys::VirtualFile& file) {
72 return FileType::Error; 72 return FileType::Error;
73} 73}
74 74
75ResultStatus AppLoader_NSP::Load(Kernel::Process& process) { 75AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::Process& process) {
76 if (is_loaded) { 76 if (is_loaded) {
77 return ResultStatus::ErrorAlreadyLoaded; 77 return {ResultStatus::ErrorAlreadyLoaded, {}};
78 } 78 }
79 79
80 if (title_id == 0) 80 if (title_id == 0) {
81 return ResultStatus::ErrorNSPMissingProgramNCA; 81 return {ResultStatus::ErrorNSPMissingProgramNCA, {}};
82 }
82 83
83 if (nsp->GetStatus() != ResultStatus::Success) 84 const auto nsp_status = nsp->GetStatus();
84 return nsp->GetStatus(); 85 if (nsp_status != ResultStatus::Success) {
86 return {nsp_status, {}};
87 }
85 88
86 if (nsp->GetProgramStatus(title_id) != ResultStatus::Success) 89 const auto nsp_program_status = nsp->GetProgramStatus(title_id);
87 return nsp->GetProgramStatus(title_id); 90 if (nsp_program_status != ResultStatus::Success) {
91 return {nsp_program_status, {}};
92 }
88 93
89 if (nsp->GetNCA(title_id, FileSys::ContentRecordType::Program) == nullptr) { 94 if (nsp->GetNCA(title_id, FileSys::ContentRecordType::Program) == nullptr) {
90 if (!Core::Crypto::KeyManager::KeyFileExists(false)) 95 if (!Core::Crypto::KeyManager::KeyFileExists(false)) {
91 return ResultStatus::ErrorMissingProductionKeyFile; 96 return {ResultStatus::ErrorMissingProductionKeyFile, {}};
92 return ResultStatus::ErrorNSPMissingProgramNCA; 97 }
98
99 return {ResultStatus::ErrorNSPMissingProgramNCA, {}};
93 } 100 }
94 101
95 const auto result = secondary_loader->Load(process); 102 const auto result = secondary_loader->Load(process);
96 if (result != ResultStatus::Success) 103 if (result.first != ResultStatus::Success) {
97 return result; 104 return result;
105 }
98 106
99 FileSys::VirtualFile update_raw; 107 FileSys::VirtualFile update_raw;
100 if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) 108 if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) {
101 Service::FileSystem::SetPackedUpdate(std::move(update_raw)); 109 Service::FileSystem::SetPackedUpdate(std::move(update_raw));
110 }
102 111
103 is_loaded = true; 112 is_loaded = true;
104 113 return result;
105 return ResultStatus::Success;
106} 114}
107 115
108ResultStatus AppLoader_NSP::ReadRomFS(FileSys::VirtualFile& file) { 116ResultStatus AppLoader_NSP::ReadRomFS(FileSys::VirtualFile& file) {
diff --git a/src/core/loader/nsp.h b/src/core/loader/nsp.h
index 953a1b508..85e870bdf 100644
--- a/src/core/loader/nsp.h
+++ b/src/core/loader/nsp.h
@@ -35,7 +35,7 @@ public:
35 return IdentifyType(file); 35 return IdentifyType(file);
36 } 36 }
37 37
38 ResultStatus Load(Kernel::Process& process) override; 38 LoadResult Load(Kernel::Process& process) override;
39 39
40 ResultStatus ReadRomFS(FileSys::VirtualFile& file) override; 40 ResultStatus ReadRomFS(FileSys::VirtualFile& file) override;
41 u64 ReadRomFSIVFCOffset() const override; 41 u64 ReadRomFSIVFCOffset() const override;
diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp
index 89f7bbf77..1e285a053 100644
--- a/src/core/loader/xci.cpp
+++ b/src/core/loader/xci.cpp
@@ -48,31 +48,35 @@ FileType AppLoader_XCI::IdentifyType(const FileSys::VirtualFile& file) {
48 return FileType::Error; 48 return FileType::Error;
49} 49}
50 50
51ResultStatus AppLoader_XCI::Load(Kernel::Process& process) { 51AppLoader_XCI::LoadResult AppLoader_XCI::Load(Kernel::Process& process) {
52 if (is_loaded) { 52 if (is_loaded) {
53 return ResultStatus::ErrorAlreadyLoaded; 53 return {ResultStatus::ErrorAlreadyLoaded, {}};
54 } 54 }
55 55
56 if (xci->GetStatus() != ResultStatus::Success) 56 if (xci->GetStatus() != ResultStatus::Success) {
57 return xci->GetStatus(); 57 return {xci->GetStatus(), {}};
58 }
58 59
59 if (xci->GetProgramNCAStatus() != ResultStatus::Success) 60 if (xci->GetProgramNCAStatus() != ResultStatus::Success) {
60 return xci->GetProgramNCAStatus(); 61 return {xci->GetProgramNCAStatus(), {}};
62 }
61 63
62 if (!xci->HasProgramNCA() && !Core::Crypto::KeyManager::KeyFileExists(false)) 64 if (!xci->HasProgramNCA() && !Core::Crypto::KeyManager::KeyFileExists(false)) {
63 return ResultStatus::ErrorMissingProductionKeyFile; 65 return {ResultStatus::ErrorMissingProductionKeyFile, {}};
66 }
64 67
65 const auto result = nca_loader->Load(process); 68 const auto result = nca_loader->Load(process);
66 if (result != ResultStatus::Success) 69 if (result.first != ResultStatus::Success) {
67 return result; 70 return result;
71 }
68 72
69 FileSys::VirtualFile update_raw; 73 FileSys::VirtualFile update_raw;
70 if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) 74 if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) {
71 Service::FileSystem::SetPackedUpdate(std::move(update_raw)); 75 Service::FileSystem::SetPackedUpdate(std::move(update_raw));
76 }
72 77
73 is_loaded = true; 78 is_loaded = true;
74 79 return result;
75 return ResultStatus::Success;
76} 80}
77 81
78ResultStatus AppLoader_XCI::ReadRomFS(FileSys::VirtualFile& file) { 82ResultStatus AppLoader_XCI::ReadRomFS(FileSys::VirtualFile& file) {
diff --git a/src/core/loader/xci.h b/src/core/loader/xci.h
index 436f7387c..ae7145b14 100644
--- a/src/core/loader/xci.h
+++ b/src/core/loader/xci.h
@@ -35,7 +35,7 @@ public:
35 return IdentifyType(file); 35 return IdentifyType(file);
36 } 36 }
37 37
38 ResultStatus Load(Kernel::Process& process) override; 38 LoadResult Load(Kernel::Process& process) override;
39 39
40 ResultStatus ReadRomFS(FileSys::VirtualFile& file) override; 40 ResultStatus ReadRomFS(FileSys::VirtualFile& file) override;
41 u64 ReadRomFSIVFCOffset() const override; 41 u64 ReadRomFSIVFCOffset() const override;
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 4e0538bc2..f18f6226b 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -26,16 +26,16 @@ namespace Memory {
26 26
27static Common::PageTable* current_page_table = nullptr; 27static Common::PageTable* current_page_table = nullptr;
28 28
29void SetCurrentPageTable(Common::PageTable* page_table) { 29void SetCurrentPageTable(Kernel::Process& process) {
30 current_page_table = page_table; 30 current_page_table = &process.VMManager().page_table;
31
32 const std::size_t address_space_width = process.VMManager().GetAddressSpaceWidth();
31 33
32 auto& system = Core::System::GetInstance(); 34 auto& system = Core::System::GetInstance();
33 if (system.IsPoweredOn()) { 35 system.ArmInterface(0).PageTableChanged(*current_page_table, address_space_width);
34 system.ArmInterface(0).PageTableChanged(); 36 system.ArmInterface(1).PageTableChanged(*current_page_table, address_space_width);
35 system.ArmInterface(1).PageTableChanged(); 37 system.ArmInterface(2).PageTableChanged(*current_page_table, address_space_width);
36 system.ArmInterface(2).PageTableChanged(); 38 system.ArmInterface(3).PageTableChanged(*current_page_table, address_space_width);
37 system.ArmInterface(3).PageTableChanged();
38 }
39} 39}
40 40
41static void MapPages(Common::PageTable& page_table, VAddr base, u64 size, u8* memory, 41static void MapPages(Common::PageTable& page_table, VAddr base, u64 size, u8* memory,
diff --git a/src/core/memory.h b/src/core/memory.h
index 6845f5fe1..b9fa18b1d 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -40,8 +40,9 @@ enum : VAddr {
40 KERNEL_REGION_END = KERNEL_REGION_VADDR + KERNEL_REGION_SIZE, 40 KERNEL_REGION_END = KERNEL_REGION_VADDR + KERNEL_REGION_SIZE,
41}; 41};
42 42
43/// Changes the currently active page table. 43/// Changes the currently active page table to that of
44void SetCurrentPageTable(Common::PageTable* page_table); 44/// the given process instance.
45void SetCurrentPageTable(Kernel::Process& process);
45 46
46/// Determines if the given VAddr is valid for the specified process. 47/// Determines if the given VAddr is valid for the specified process.
47bool IsValidVirtualAddress(const Kernel::Process& process, VAddr vaddr); 48bool IsValidVirtualAddress(const Kernel::Process& process, VAddr vaddr);
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index de30ea354..fe6628923 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -207,6 +207,11 @@ public:
207 }; 207 };
208 } regs{}; 208 } regs{};
209 209
210 /// Performs any additional setup necessary in order to begin GPU emulation.
211 /// This can be used to launch any necessary threads and register any necessary
212 /// core timing events.
213 virtual void Start() = 0;
214
210 /// Push GPU command entries to be processed 215 /// Push GPU command entries to be processed
211 virtual void PushGPUEntries(Tegra::CommandList&& entries) = 0; 216 virtual void PushGPUEntries(Tegra::CommandList&& entries) = 0;
212 217
diff --git a/src/video_core/gpu_asynch.cpp b/src/video_core/gpu_asynch.cpp
index db507cf04..d4e2553a9 100644
--- a/src/video_core/gpu_asynch.cpp
+++ b/src/video_core/gpu_asynch.cpp
@@ -9,10 +9,14 @@
9namespace VideoCommon { 9namespace VideoCommon {
10 10
11GPUAsynch::GPUAsynch(Core::System& system, VideoCore::RendererBase& renderer) 11GPUAsynch::GPUAsynch(Core::System& system, VideoCore::RendererBase& renderer)
12 : Tegra::GPU(system, renderer), gpu_thread{system, renderer, *dma_pusher} {} 12 : GPU(system, renderer), gpu_thread{system} {}
13 13
14GPUAsynch::~GPUAsynch() = default; 14GPUAsynch::~GPUAsynch() = default;
15 15
16void GPUAsynch::Start() {
17 gpu_thread.StartThread(renderer, *dma_pusher);
18}
19
16void GPUAsynch::PushGPUEntries(Tegra::CommandList&& entries) { 20void GPUAsynch::PushGPUEntries(Tegra::CommandList&& entries) {
17 gpu_thread.SubmitList(std::move(entries)); 21 gpu_thread.SubmitList(std::move(entries));
18} 22}
diff --git a/src/video_core/gpu_asynch.h b/src/video_core/gpu_asynch.h
index 1dcc61a6c..30be74cba 100644
--- a/src/video_core/gpu_asynch.h
+++ b/src/video_core/gpu_asynch.h
@@ -13,16 +13,13 @@ class RendererBase;
13 13
14namespace VideoCommon { 14namespace VideoCommon {
15 15
16namespace GPUThread {
17class ThreadManager;
18} // namespace GPUThread
19
20/// Implementation of GPU interface that runs the GPU asynchronously 16/// Implementation of GPU interface that runs the GPU asynchronously
21class GPUAsynch : public Tegra::GPU { 17class GPUAsynch : public Tegra::GPU {
22public: 18public:
23 explicit GPUAsynch(Core::System& system, VideoCore::RendererBase& renderer); 19 explicit GPUAsynch(Core::System& system, VideoCore::RendererBase& renderer);
24 ~GPUAsynch() override; 20 ~GPUAsynch() override;
25 21
22 void Start() override;
26 void PushGPUEntries(Tegra::CommandList&& entries) override; 23 void PushGPUEntries(Tegra::CommandList&& entries) override;
27 void SwapBuffers( 24 void SwapBuffers(
28 std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) override; 25 std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) override;
diff --git a/src/video_core/gpu_synch.cpp b/src/video_core/gpu_synch.cpp
index 2cfc900ed..45e43b1dc 100644
--- a/src/video_core/gpu_synch.cpp
+++ b/src/video_core/gpu_synch.cpp
@@ -8,10 +8,12 @@
8namespace VideoCommon { 8namespace VideoCommon {
9 9
10GPUSynch::GPUSynch(Core::System& system, VideoCore::RendererBase& renderer) 10GPUSynch::GPUSynch(Core::System& system, VideoCore::RendererBase& renderer)
11 : Tegra::GPU(system, renderer) {} 11 : GPU(system, renderer) {}
12 12
13GPUSynch::~GPUSynch() = default; 13GPUSynch::~GPUSynch() = default;
14 14
15void GPUSynch::Start() {}
16
15void GPUSynch::PushGPUEntries(Tegra::CommandList&& entries) { 17void GPUSynch::PushGPUEntries(Tegra::CommandList&& entries) {
16 dma_pusher->Push(std::move(entries)); 18 dma_pusher->Push(std::move(entries));
17 dma_pusher->DispatchCalls(); 19 dma_pusher->DispatchCalls();
diff --git a/src/video_core/gpu_synch.h b/src/video_core/gpu_synch.h
index 766b5631c..3031fcf72 100644
--- a/src/video_core/gpu_synch.h
+++ b/src/video_core/gpu_synch.h
@@ -18,6 +18,7 @@ public:
18 explicit GPUSynch(Core::System& system, VideoCore::RendererBase& renderer); 18 explicit GPUSynch(Core::System& system, VideoCore::RendererBase& renderer);
19 ~GPUSynch() override; 19 ~GPUSynch() override;
20 20
21 void Start() override;
21 void PushGPUEntries(Tegra::CommandList&& entries) override; 22 void PushGPUEntries(Tegra::CommandList&& entries) override;
22 void SwapBuffers( 23 void SwapBuffers(
23 std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) override; 24 std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) override;
diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp
index cc56cf467..c9a2077de 100644
--- a/src/video_core/gpu_thread.cpp
+++ b/src/video_core/gpu_thread.cpp
@@ -55,19 +55,24 @@ static void RunThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_p
55 } 55 }
56} 56}
57 57
58ThreadManager::ThreadManager(Core::System& system, VideoCore::RendererBase& renderer, 58ThreadManager::ThreadManager(Core::System& system) : system{system} {}
59 Tegra::DmaPusher& dma_pusher)
60 : system{system}, thread{RunThread, std::ref(renderer), std::ref(dma_pusher), std::ref(state)} {
61 synchronization_event = system.CoreTiming().RegisterEvent(
62 "GPUThreadSynch", [this](u64 fence, s64) { state.WaitForSynchronization(fence); });
63}
64 59
65ThreadManager::~ThreadManager() { 60ThreadManager::~ThreadManager() {
61 if (!thread.joinable()) {
62 return;
63 }
64
66 // Notify GPU thread that a shutdown is pending 65 // Notify GPU thread that a shutdown is pending
67 PushCommand(EndProcessingCommand()); 66 PushCommand(EndProcessingCommand());
68 thread.join(); 67 thread.join();
69} 68}
70 69
70void ThreadManager::StartThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher) {
71 thread = std::thread{RunThread, std::ref(renderer), std::ref(dma_pusher), std::ref(state)};
72 synchronization_event = system.CoreTiming().RegisterEvent(
73 "GPUThreadSynch", [this](u64 fence, s64) { state.WaitForSynchronization(fence); });
74}
75
71void ThreadManager::SubmitList(Tegra::CommandList&& entries) { 76void ThreadManager::SubmitList(Tegra::CommandList&& entries) {
72 const u64 fence{PushCommand(SubmitListCommand(std::move(entries)))}; 77 const u64 fence{PushCommand(SubmitListCommand(std::move(entries)))};
73 const s64 synchronization_ticks{Core::Timing::usToCycles(9000)}; 78 const s64 synchronization_ticks{Core::Timing::usToCycles(9000)};
diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h
index 62bcea5bb..cc14527c7 100644
--- a/src/video_core/gpu_thread.h
+++ b/src/video_core/gpu_thread.h
@@ -138,10 +138,12 @@ struct SynchState final {
138/// Class used to manage the GPU thread 138/// Class used to manage the GPU thread
139class ThreadManager final { 139class ThreadManager final {
140public: 140public:
141 explicit ThreadManager(Core::System& system, VideoCore::RendererBase& renderer, 141 explicit ThreadManager(Core::System& system);
142 Tegra::DmaPusher& dma_pusher);
143 ~ThreadManager(); 142 ~ThreadManager();
144 143
144 /// Creates and starts the GPU thread.
145 void StartThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher);
146
145 /// Push GPU command entries to be processed 147 /// Push GPU command entries to be processed
146 void SubmitList(Tegra::CommandList&& entries); 148 void SubmitList(Tegra::CommandList&& entries);
147 149
diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp
index cb82ecf3f..60cda0ca3 100644
--- a/src/video_core/video_core.cpp
+++ b/src/video_core/video_core.cpp
@@ -5,6 +5,8 @@
5#include <memory> 5#include <memory>
6#include "core/core.h" 6#include "core/core.h"
7#include "core/settings.h" 7#include "core/settings.h"
8#include "video_core/gpu_asynch.h"
9#include "video_core/gpu_synch.h"
8#include "video_core/renderer_base.h" 10#include "video_core/renderer_base.h"
9#include "video_core/renderer_opengl/renderer_opengl.h" 11#include "video_core/renderer_opengl/renderer_opengl.h"
10#include "video_core/video_core.h" 12#include "video_core/video_core.h"
@@ -16,6 +18,14 @@ std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_wind
16 return std::make_unique<OpenGL::RendererOpenGL>(emu_window, system); 18 return std::make_unique<OpenGL::RendererOpenGL>(emu_window, system);
17} 19}
18 20
21std::unique_ptr<Tegra::GPU> CreateGPU(Core::System& system) {
22 if (Settings::values.use_asynchronous_gpu_emulation) {
23 return std::make_unique<VideoCommon::GPUAsynch>(system, system.Renderer());
24 }
25
26 return std::make_unique<VideoCommon::GPUSynch>(system, system.Renderer());
27}
28
19u16 GetResolutionScaleFactor(const RendererBase& renderer) { 29u16 GetResolutionScaleFactor(const RendererBase& renderer) {
20 return static_cast<u16>( 30 return static_cast<u16>(
21 Settings::values.resolution_factor 31 Settings::values.resolution_factor
diff --git a/src/video_core/video_core.h b/src/video_core/video_core.h
index 3c583f195..b8e0ac372 100644
--- a/src/video_core/video_core.h
+++ b/src/video_core/video_core.h
@@ -14,6 +14,10 @@ namespace Core::Frontend {
14class EmuWindow; 14class EmuWindow;
15} 15}
16 16
17namespace Tegra {
18class GPU;
19}
20
17namespace VideoCore { 21namespace VideoCore {
18 22
19class RendererBase; 23class RendererBase;
@@ -27,6 +31,9 @@ class RendererBase;
27std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window, 31std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window,
28 Core::System& system); 32 Core::System& system);
29 33
34/// Creates an emulated GPU instance using the given system context.
35std::unique_ptr<Tegra::GPU> CreateGPU(Core::System& system);
36
30u16 GetResolutionScaleFactor(const RendererBase& renderer); 37u16 GetResolutionScaleFactor(const RendererBase& renderer);
31 38
32} // namespace VideoCore 39} // namespace VideoCore