Просмотр исходного кода

Merge pull request #2374 from lioncash/pagetable

core: Reorganize boot order
bunnei 7 лет назад
Родитель
Сommit
40dc893c37

+ 12 - 2
src/core/arm/arm_interface.h

@@ -7,6 +7,10 @@
 #include <array>
 #include <array>
 #include "common/common_types.h"
 #include "common/common_types.h"
 
 
+namespace Common {
+struct PageTable;
+}
+
 namespace Kernel {
 namespace Kernel {
 enum class VMAPermission : u8;
 enum class VMAPermission : u8;
 }
 }
@@ -49,8 +53,14 @@ public:
     /// Clear all instruction cache
     /// Clear all instruction cache
     virtual void ClearInstructionCache() = 0;
     virtual void ClearInstructionCache() = 0;
 
 
-    /// Notify CPU emulation that page tables have changed
-    virtual void PageTableChanged() = 0;
+    /// Notifies CPU emulation that the current page table has changed.
+    ///
+    /// @param new_page_table                 The new page table.
+    /// @param new_address_space_size_in_bits The new usable size of the address space in bits.
+    ///                                       This can be either 32, 36, or 39 on official software.
+    ///
+    virtual void PageTableChanged(Common::PageTable& new_page_table,
+                                  std::size_t new_address_space_size_in_bits) = 0;
 
 
     /**
     /**
      * Set the Program Counter to an address
      * Set the Program Counter to an address

+ 8 - 15
src/core/arm/dynarmic/arm_dynarmic.cpp

@@ -14,7 +14,6 @@
 #include "core/core_timing.h"
 #include "core/core_timing.h"
 #include "core/core_timing_util.h"
 #include "core/core_timing_util.h"
 #include "core/gdbstub/gdbstub.h"
 #include "core/gdbstub/gdbstub.h"
-#include "core/hle/kernel/kernel.h"
 #include "core/hle/kernel/process.h"
 #include "core/hle/kernel/process.h"
 #include "core/hle/kernel/svc.h"
 #include "core/hle/kernel/svc.h"
 #include "core/hle/kernel/vm_manager.h"
 #include "core/hle/kernel/vm_manager.h"
@@ -129,18 +128,16 @@ public:
     u64 tpidr_el0 = 0;
     u64 tpidr_el0 = 0;
 };
 };
 
 
-std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const {
-    auto* current_process = system.Kernel().CurrentProcess();
-    auto** const page_table = current_process->VMManager().page_table.pointers.data();
-
+std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit(Common::PageTable& page_table,
+                                                          std::size_t address_space_bits) const {
     Dynarmic::A64::UserConfig config;
     Dynarmic::A64::UserConfig config;
 
 
     // Callbacks
     // Callbacks
     config.callbacks = cb.get();
     config.callbacks = cb.get();
 
 
     // Memory
     // Memory
-    config.page_table = reinterpret_cast<void**>(page_table);
-    config.page_table_address_space_bits = current_process->VMManager().GetAddressSpaceWidth();
+    config.page_table = reinterpret_cast<void**>(page_table.pointers.data());
+    config.page_table_address_space_bits = address_space_bits;
     config.silently_mirror_page_table = false;
     config.silently_mirror_page_table = false;
 
 
     // Multi-process state
     // Multi-process state
@@ -176,12 +173,7 @@ ARM_Dynarmic::ARM_Dynarmic(System& system, ExclusiveMonitor& exclusive_monitor,
                            std::size_t core_index)
                            std::size_t core_index)
     : cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), inner_unicorn{system},
     : cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), inner_unicorn{system},
       core_index{core_index}, system{system},
       core_index{core_index}, system{system},
-      exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {
-    ThreadContext ctx{};
-    inner_unicorn.SaveContext(ctx);
-    PageTableChanged();
-    LoadContext(ctx);
-}
+      exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {}
 
 
 ARM_Dynarmic::~ARM_Dynarmic() = default;
 ARM_Dynarmic::~ARM_Dynarmic() = default;
 
 
@@ -276,8 +268,9 @@ void ARM_Dynarmic::ClearExclusiveState() {
     jit->ClearExclusiveState();
     jit->ClearExclusiveState();
 }
 }
 
 
-void ARM_Dynarmic::PageTableChanged() {
-    jit = MakeJit();
+void ARM_Dynarmic::PageTableChanged(Common::PageTable& page_table,
+                                    std::size_t new_address_space_size_in_bits) {
+    jit = MakeJit(page_table, new_address_space_size_in_bits);
 }
 }
 
 
 DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(std::size_t core_count) : monitor(core_count) {}
 DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(std::size_t core_count) : monitor(core_count) {}

+ 4 - 2
src/core/arm/dynarmic/arm_dynarmic.h

@@ -48,10 +48,12 @@ public:
     void ClearExclusiveState() override;
     void ClearExclusiveState() override;
 
 
     void ClearInstructionCache() override;
     void ClearInstructionCache() override;
-    void PageTableChanged() override;
+    void PageTableChanged(Common::PageTable& new_page_table,
+                          std::size_t new_address_space_size_in_bits) override;
 
 
 private:
 private:
-    std::unique_ptr<Dynarmic::A64::Jit> MakeJit() const;
+    std::unique_ptr<Dynarmic::A64::Jit> MakeJit(Common::PageTable& page_table,
+                                                std::size_t address_space_bits) const;
 
 
     friend class ARM_Dynarmic_Callbacks;
     friend class ARM_Dynarmic_Callbacks;
     std::unique_ptr<ARM_Dynarmic_Callbacks> cb;
     std::unique_ptr<ARM_Dynarmic_Callbacks> cb;

+ 1 - 1
src/core/arm/unicorn/arm_unicorn.h

@@ -41,7 +41,7 @@ public:
     void Run() override;
     void Run() override;
     void Step() override;
     void Step() override;
     void ClearInstructionCache() override;
     void ClearInstructionCache() override;
-    void PageTableChanged() override{};
+    void PageTableChanged(Common::PageTable&, std::size_t) override {}
     void RecordBreak(GDBStub::BreakpointAddress bkpt);
     void RecordBreak(GDBStub::BreakpointAddress bkpt);
 
 
 private:
 private:

+ 16 - 17
src/core/core.cpp

@@ -3,9 +3,7 @@
 // Refer to the license.txt file included.
 // Refer to the license.txt file included.
 
 
 #include <array>
 #include <array>
-#include <map>
 #include <memory>
 #include <memory>
-#include <thread>
 #include <utility>
 #include <utility>
 
 
 #include "common/file_util.h"
 #include "common/file_util.h"
@@ -38,8 +36,6 @@
 #include "frontend/applets/software_keyboard.h"
 #include "frontend/applets/software_keyboard.h"
 #include "frontend/applets/web_browser.h"
 #include "frontend/applets/web_browser.h"
 #include "video_core/debug_utils/debug_utils.h"
 #include "video_core/debug_utils/debug_utils.h"
-#include "video_core/gpu_asynch.h"
-#include "video_core/gpu_synch.h"
 #include "video_core/renderer_base.h"
 #include "video_core/renderer_base.h"
 #include "video_core/video_core.h"
 #include "video_core/video_core.h"
 
 
@@ -81,7 +77,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
     return vfs->OpenFile(path, FileSys::Mode::Read);
     return vfs->OpenFile(path, FileSys::Mode::Read);
 }
 }
 struct System::Impl {
 struct System::Impl {
-    explicit Impl(System& system) : kernel{system} {}
+    explicit Impl(System& system) : kernel{system}, cpu_core_manager{system} {}
 
 
     Cpu& CurrentCpuCore() {
     Cpu& CurrentCpuCore() {
         return cpu_core_manager.GetCurrentCore();
         return cpu_core_manager.GetCurrentCore();
@@ -99,6 +95,7 @@ struct System::Impl {
         LOG_DEBUG(HW_Memory, "initialized OK");
         LOG_DEBUG(HW_Memory, "initialized OK");
 
 
         core_timing.Initialize();
         core_timing.Initialize();
+        cpu_core_manager.Initialize();
         kernel.Initialize();
         kernel.Initialize();
 
 
         const auto current_time = std::chrono::duration_cast<std::chrono::seconds>(
         const auto current_time = std::chrono::duration_cast<std::chrono::seconds>(
@@ -120,9 +117,6 @@ struct System::Impl {
         if (web_browser == nullptr)
         if (web_browser == nullptr)
             web_browser = std::make_unique<Core::Frontend::DefaultWebBrowserApplet>();
             web_browser = std::make_unique<Core::Frontend::DefaultWebBrowserApplet>();
 
 
-        auto main_process = Kernel::Process::Create(system, "main");
-        kernel.MakeCurrentProcess(main_process.get());
-
         telemetry_session = std::make_unique<Core::TelemetrySession>();
         telemetry_session = std::make_unique<Core::TelemetrySession>();
         service_manager = std::make_shared<Service::SM::ServiceManager>();
         service_manager = std::make_shared<Service::SM::ServiceManager>();
 
 
@@ -134,15 +128,9 @@ struct System::Impl {
             return ResultStatus::ErrorVideoCore;
             return ResultStatus::ErrorVideoCore;
         }
         }
 
 
-        is_powered_on = true;
-
-        if (Settings::values.use_asynchronous_gpu_emulation) {
-            gpu_core = std::make_unique<VideoCommon::GPUAsynch>(system, *renderer);
-        } else {
-            gpu_core = std::make_unique<VideoCommon::GPUSynch>(system, *renderer);
-        }
+        gpu_core = VideoCore::CreateGPU(system);
 
 
-        cpu_core_manager.Initialize(system);
+        is_powered_on = true;
 
 
         LOG_DEBUG(Core, "Initialized OK");
         LOG_DEBUG(Core, "Initialized OK");
 
 
@@ -179,7 +167,8 @@ struct System::Impl {
             return init_result;
             return init_result;
         }
         }
 
 
-        const Loader::ResultStatus load_result{app_loader->Load(*kernel.CurrentProcess())};
+        auto main_process = Kernel::Process::Create(system, "main");
+        const auto [load_result, load_parameters] = app_loader->Load(*main_process);
         if (load_result != Loader::ResultStatus::Success) {
         if (load_result != Loader::ResultStatus::Success) {
             LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<int>(load_result));
             LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<int>(load_result));
             Shutdown();
             Shutdown();
@@ -187,6 +176,16 @@ struct System::Impl {
             return static_cast<ResultStatus>(static_cast<u32>(ResultStatus::ErrorLoader) +
             return static_cast<ResultStatus>(static_cast<u32>(ResultStatus::ErrorLoader) +
                                              static_cast<u32>(load_result));
                                              static_cast<u32>(load_result));
         }
         }
+        kernel.MakeCurrentProcess(main_process.get());
+
+        // Main process has been loaded and been made current.
+        // Begin GPU and CPU execution.
+        gpu_core->Start();
+        cpu_core_manager.StartThreads();
+
+        // All threads are started, begin main process execution, now that we're in the clear.
+        main_process->Run(load_parameters->main_thread_priority,
+                          load_parameters->main_thread_stack_size);
 
 
         status = ResultStatus::Success;
         status = ResultStatus::Success;
         return status;
         return status;

+ 4 - 2
src/core/cpu_core_manager.cpp

@@ -19,17 +19,19 @@ void RunCpuCore(const System& system, Cpu& cpu_state) {
 }
 }
 } // Anonymous namespace
 } // Anonymous namespace
 
 
-CpuCoreManager::CpuCoreManager() = default;
+CpuCoreManager::CpuCoreManager(System& system) : system{system} {}
 CpuCoreManager::~CpuCoreManager() = default;
 CpuCoreManager::~CpuCoreManager() = default;
 
 
-void CpuCoreManager::Initialize(System& system) {
+void CpuCoreManager::Initialize() {
     barrier = std::make_unique<CpuBarrier>();
     barrier = std::make_unique<CpuBarrier>();
     exclusive_monitor = Cpu::MakeExclusiveMonitor(cores.size());
     exclusive_monitor = Cpu::MakeExclusiveMonitor(cores.size());
 
 
     for (std::size_t index = 0; index < cores.size(); ++index) {
     for (std::size_t index = 0; index < cores.size(); ++index) {
         cores[index] = std::make_unique<Cpu>(system, *exclusive_monitor, *barrier, index);
         cores[index] = std::make_unique<Cpu>(system, *exclusive_monitor, *barrier, index);
     }
     }
+}
 
 
+void CpuCoreManager::StartThreads() {
     // Create threads for CPU cores 1-3, and build thread_to_cpu map
     // Create threads for CPU cores 1-3, and build thread_to_cpu map
     // CPU core 0 is run on the main thread
     // CPU core 0 is run on the main thread
     thread_to_cpu[std::this_thread::get_id()] = cores[0].get();
     thread_to_cpu[std::this_thread::get_id()] = cores[0].get();

+ 5 - 2
src/core/cpu_core_manager.h

@@ -18,7 +18,7 @@ class System;
 
 
 class CpuCoreManager {
 class CpuCoreManager {
 public:
 public:
-    CpuCoreManager();
+    explicit CpuCoreManager(System& system);
     CpuCoreManager(const CpuCoreManager&) = delete;
     CpuCoreManager(const CpuCoreManager&) = delete;
     CpuCoreManager(CpuCoreManager&&) = delete;
     CpuCoreManager(CpuCoreManager&&) = delete;
 
 
@@ -27,7 +27,8 @@ public:
     CpuCoreManager& operator=(const CpuCoreManager&) = delete;
     CpuCoreManager& operator=(const CpuCoreManager&) = delete;
     CpuCoreManager& operator=(CpuCoreManager&&) = delete;
     CpuCoreManager& operator=(CpuCoreManager&&) = delete;
 
 
-    void Initialize(System& system);
+    void Initialize();
+    void StartThreads();
     void Shutdown();
     void Shutdown();
 
 
     Cpu& GetCore(std::size_t index);
     Cpu& GetCore(std::size_t index);
@@ -54,6 +55,8 @@ private:
 
 
     /// Map of guest threads to CPU cores
     /// Map of guest threads to CPU cores
     std::map<std::thread::id, Cpu*> thread_to_cpu;
     std::map<std::thread::id, Cpu*> thread_to_cpu;
+
+    System& system;
 };
 };
 
 
 } // namespace Core
 } // namespace Core

+ 6 - 1
src/core/hle/kernel/kernel.cpp

@@ -182,7 +182,12 @@ void KernelCore::AppendNewProcess(SharedPtr<Process> process) {
 
 
 void KernelCore::MakeCurrentProcess(Process* process) {
 void KernelCore::MakeCurrentProcess(Process* process) {
     impl->current_process = process;
     impl->current_process = process;
-    Memory::SetCurrentPageTable(&process->VMManager().page_table);
+
+    if (process == nullptr) {
+        return;
+    }
+
+    Memory::SetCurrentPageTable(*process);
 }
 }
 
 
 Process* KernelCore::CurrentProcess() {
 Process* KernelCore::CurrentProcess() {

+ 6 - 11
src/core/hle/kernel/process.cpp

@@ -28,12 +28,12 @@ namespace {
  *
  *
  * @param owner_process The parent process for the main thread
  * @param owner_process The parent process for the main thread
  * @param kernel The kernel instance to create the main thread under.
  * @param kernel The kernel instance to create the main thread under.
- * @param entry_point The address at which the thread should start execution
  * @param priority The priority to give the main thread
  * @param priority The priority to give the main thread
  */
  */
-void SetupMainThread(Process& owner_process, KernelCore& kernel, VAddr entry_point, u32 priority) {
-    // Initialize new "main" thread
-    const VAddr stack_top = owner_process.VMManager().GetTLSIORegionEndAddress();
+void SetupMainThread(Process& owner_process, KernelCore& kernel, u32 priority) {
+    const auto& vm_manager = owner_process.VMManager();
+    const VAddr entry_point = vm_manager.GetCodeRegionBaseAddress();
+    const VAddr stack_top = vm_manager.GetTLSIORegionEndAddress();
     auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0,
     auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0,
                                      owner_process.GetIdealCore(), stack_top, owner_process);
                                      owner_process.GetIdealCore(), stack_top, owner_process);
 
 
@@ -105,8 +105,6 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) {
     is_64bit_process = metadata.Is64BitProgram();
     is_64bit_process = metadata.Is64BitProgram();
 
 
     vm_manager.Reset(metadata.GetAddressSpaceType());
     vm_manager.Reset(metadata.GetAddressSpaceType());
-    // Ensure that the potentially resized page table is seen by CPU backends.
-    Memory::SetCurrentPageTable(&vm_manager.page_table);
 
 
     const auto& caps = metadata.GetKernelCapabilities();
     const auto& caps = metadata.GetKernelCapabilities();
     const auto capability_init_result =
     const auto capability_init_result =
@@ -118,7 +116,7 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) {
     return handle_table.SetSize(capabilities.GetHandleTableSize());
     return handle_table.SetSize(capabilities.GetHandleTableSize());
 }
 }
 
 
-void Process::Run(VAddr entry_point, s32 main_thread_priority, u64 stack_size) {
+void Process::Run(s32 main_thread_priority, u64 stack_size) {
     // The kernel always ensures that the given stack size is page aligned.
     // The kernel always ensures that the given stack size is page aligned.
     main_thread_stack_size = Common::AlignUp(stack_size, Memory::PAGE_SIZE);
     main_thread_stack_size = Common::AlignUp(stack_size, Memory::PAGE_SIZE);
 
 
@@ -134,7 +132,7 @@ void Process::Run(VAddr entry_point, s32 main_thread_priority, u64 stack_size) {
     vm_manager.LogLayout();
     vm_manager.LogLayout();
     ChangeStatus(ProcessStatus::Running);
     ChangeStatus(ProcessStatus::Running);
 
 
-    SetupMainThread(*this, kernel, entry_point, main_thread_priority);
+    SetupMainThread(*this, kernel, main_thread_priority);
 }
 }
 
 
 void Process::PrepareForTermination() {
 void Process::PrepareForTermination() {
@@ -241,9 +239,6 @@ void Process::LoadModule(CodeSet module_, VAddr base_addr) {
     MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeData);
     MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeData);
 
 
     code_memory_size += module_.memory.size();
     code_memory_size += module_.memory.size();
-
-    // Clear instruction cache in CPU JIT
-    system.InvalidateCpuInstructionCaches();
 }
 }
 
 
 Process::Process(Core::System& system)
 Process::Process(Core::System& system)

+ 5 - 2
src/core/hle/kernel/process.h

@@ -225,9 +225,12 @@ public:
     ResultCode LoadFromMetadata(const FileSys::ProgramMetadata& metadata);
     ResultCode LoadFromMetadata(const FileSys::ProgramMetadata& metadata);
 
 
     /**
     /**
-     * Applies address space changes and launches the process main thread.
+     * 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(VAddr entry_point, s32 main_thread_priority, u64 stack_size);
+    void Run(s32 main_thread_priority, u64 stack_size);
 
 
     /**
     /**
      * Prepares a process for termination by stopping all of its threads
      * Prepares a process for termination by stopping all of its threads

+ 22 - 18
src/core/loader/deconstructed_rom_directory.cpp

@@ -86,25 +86,29 @@ FileType AppLoader_DeconstructedRomDirectory::IdentifyType(const FileSys::Virtua
     return FileType::Error;
     return FileType::Error;
 }
 }
 
 
-ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process) {
+AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirectory::Load(
+    Kernel::Process& process) {
     if (is_loaded) {
     if (is_loaded) {
-        return ResultStatus::ErrorAlreadyLoaded;
+        return {ResultStatus::ErrorAlreadyLoaded, {}};
     }
     }
 
 
     if (dir == nullptr) {
     if (dir == nullptr) {
-        if (file == nullptr)
-            return ResultStatus::ErrorNullFile;
+        if (file == nullptr) {
+            return {ResultStatus::ErrorNullFile, {}};
+        }
+
         dir = file->GetContainingDirectory();
         dir = file->GetContainingDirectory();
     }
     }
 
 
     // Read meta to determine title ID
     // Read meta to determine title ID
     FileSys::VirtualFile npdm = dir->GetFile("main.npdm");
     FileSys::VirtualFile npdm = dir->GetFile("main.npdm");
-    if (npdm == nullptr)
-        return ResultStatus::ErrorMissingNPDM;
+    if (npdm == nullptr) {
+        return {ResultStatus::ErrorMissingNPDM, {}};
+    }
 
 
-    ResultStatus result = metadata.Load(npdm);
+    const ResultStatus result = metadata.Load(npdm);
     if (result != ResultStatus::Success) {
     if (result != ResultStatus::Success) {
-        return result;
+        return {result, {}};
     }
     }
 
 
     if (override_update) {
     if (override_update) {
@@ -114,23 +118,24 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process)
 
 
     // Reread in case PatchExeFS affected the main.npdm
     // Reread in case PatchExeFS affected the main.npdm
     npdm = dir->GetFile("main.npdm");
     npdm = dir->GetFile("main.npdm");
-    if (npdm == nullptr)
-        return ResultStatus::ErrorMissingNPDM;
+    if (npdm == nullptr) {
+        return {ResultStatus::ErrorMissingNPDM, {}};
+    }
 
 
-    ResultStatus result2 = metadata.Load(npdm);
+    const ResultStatus result2 = metadata.Load(npdm);
     if (result2 != ResultStatus::Success) {
     if (result2 != ResultStatus::Success) {
-        return result2;
+        return {result2, {}};
     }
     }
     metadata.Print();
     metadata.Print();
 
 
     const FileSys::ProgramAddressSpaceType arch_bits{metadata.GetAddressSpaceType()};
     const FileSys::ProgramAddressSpaceType arch_bits{metadata.GetAddressSpaceType()};
     if (arch_bits == FileSys::ProgramAddressSpaceType::Is32Bit ||
     if (arch_bits == FileSys::ProgramAddressSpaceType::Is32Bit ||
         arch_bits == FileSys::ProgramAddressSpaceType::Is32BitNoMap) {
         arch_bits == FileSys::ProgramAddressSpaceType::Is32BitNoMap) {
-        return ResultStatus::Error32BitISA;
+        return {ResultStatus::Error32BitISA, {}};
     }
     }
 
 
     if (process.LoadFromMetadata(metadata).IsError()) {
     if (process.LoadFromMetadata(metadata).IsError()) {
-        return ResultStatus::ErrorUnableToParseKernelMetadata;
+        return {ResultStatus::ErrorUnableToParseKernelMetadata, {}};
     }
     }
 
 
     const FileSys::PatchManager pm(metadata.GetTitleID());
     const FileSys::PatchManager pm(metadata.GetTitleID());
@@ -150,7 +155,7 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process)
         const auto tentative_next_load_addr =
         const auto tentative_next_load_addr =
             AppLoader_NSO::LoadModule(process, *module_file, load_addr, should_pass_arguments, pm);
             AppLoader_NSO::LoadModule(process, *module_file, load_addr, should_pass_arguments, pm);
         if (!tentative_next_load_addr) {
         if (!tentative_next_load_addr) {
-            return ResultStatus::ErrorLoadingNSO;
+            return {ResultStatus::ErrorLoadingNSO, {}};
         }
         }
 
 
         next_load_addr = *tentative_next_load_addr;
         next_load_addr = *tentative_next_load_addr;
@@ -159,8 +164,6 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process)
         GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false);
         GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false);
     }
     }
 
 
-    process.Run(base_address, metadata.GetMainThreadPriority(), metadata.GetMainThreadStackSize());
-
     // Find the RomFS by searching for a ".romfs" file in this directory
     // Find the RomFS by searching for a ".romfs" file in this directory
     const auto& files = dir->GetFiles();
     const auto& files = dir->GetFiles();
     const auto romfs_iter =
     const auto romfs_iter =
@@ -175,7 +178,8 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process)
     }
     }
 
 
     is_loaded = true;
     is_loaded = true;
-    return ResultStatus::Success;
+    return {ResultStatus::Success,
+            LoadParameters{metadata.GetMainThreadPriority(), metadata.GetMainThreadStackSize()}};
 }
 }
 
 
 ResultStatus AppLoader_DeconstructedRomDirectory::ReadRomFS(FileSys::VirtualFile& dir) {
 ResultStatus AppLoader_DeconstructedRomDirectory::ReadRomFS(FileSys::VirtualFile& dir) {

+ 1 - 1
src/core/loader/deconstructed_rom_directory.h

@@ -37,7 +37,7 @@ public:
         return IdentifyType(file);
         return IdentifyType(file);
     }
     }
 
 
-    ResultStatus Load(Kernel::Process& process) override;
+    LoadResult Load(Kernel::Process& process) override;
 
 
     ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
     ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
     ResultStatus ReadIcon(std::vector<u8>& buffer) override;
     ResultStatus ReadIcon(std::vector<u8>& buffer) override;

+ 8 - 7
src/core/loader/elf.cpp

@@ -382,13 +382,15 @@ FileType AppLoader_ELF::IdentifyType(const FileSys::VirtualFile& file) {
     return FileType::Error;
     return FileType::Error;
 }
 }
 
 
-ResultStatus AppLoader_ELF::Load(Kernel::Process& process) {
-    if (is_loaded)
-        return ResultStatus::ErrorAlreadyLoaded;
+AppLoader_ELF::LoadResult AppLoader_ELF::Load(Kernel::Process& process) {
+    if (is_loaded) {
+        return {ResultStatus::ErrorAlreadyLoaded, {}};
+    }
 
 
     std::vector<u8> buffer = file->ReadAllBytes();
     std::vector<u8> buffer = file->ReadAllBytes();
-    if (buffer.size() != file->GetSize())
-        return ResultStatus::ErrorIncorrectELFFileSize;
+    if (buffer.size() != file->GetSize()) {
+        return {ResultStatus::ErrorIncorrectELFFileSize, {}};
+    }
 
 
     const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
     const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
     ElfReader elf_reader(&buffer[0]);
     ElfReader elf_reader(&buffer[0]);
@@ -396,10 +398,9 @@ ResultStatus AppLoader_ELF::Load(Kernel::Process& process) {
     const VAddr entry_point = codeset.entrypoint;
     const VAddr entry_point = codeset.entrypoint;
 
 
     process.LoadModule(std::move(codeset), entry_point);
     process.LoadModule(std::move(codeset), entry_point);
-    process.Run(entry_point, 48, Memory::DEFAULT_STACK_SIZE);
 
 
     is_loaded = true;
     is_loaded = true;
-    return ResultStatus::Success;
+    return {ResultStatus::Success, LoadParameters{48, Memory::DEFAULT_STACK_SIZE}};
 }
 }
 
 
 } // namespace Loader
 } // namespace Loader

+ 1 - 1
src/core/loader/elf.h

@@ -26,7 +26,7 @@ public:
         return IdentifyType(file);
         return IdentifyType(file);
     }
     }
 
 
-    ResultStatus Load(Kernel::Process& process) override;
+    LoadResult Load(Kernel::Process& process) override;
 };
 };
 
 
 } // namespace Loader
 } // namespace Loader

+ 7 - 1
src/core/loader/loader.h

@@ -131,6 +131,12 @@ std::ostream& operator<<(std::ostream& os, ResultStatus status);
 /// Interface for loading an application
 /// Interface for loading an application
 class AppLoader : NonCopyable {
 class AppLoader : NonCopyable {
 public:
 public:
+    struct LoadParameters {
+        s32 main_thread_priority;
+        u64 main_thread_stack_size;
+    };
+    using LoadResult = std::pair<ResultStatus, std::optional<LoadParameters>>;
+
     explicit AppLoader(FileSys::VirtualFile file);
     explicit AppLoader(FileSys::VirtualFile file);
     virtual ~AppLoader();
     virtual ~AppLoader();
 
 
@@ -145,7 +151,7 @@ public:
      * @param process The newly created process.
      * @param process The newly created process.
      * @return The status result of the operation.
      * @return The status result of the operation.
      */
      */
-    virtual ResultStatus Load(Kernel::Process& process) = 0;
+    virtual LoadResult Load(Kernel::Process& process) = 0;
 
 
     /**
     /**
      * Loads the system mode that this application needs.
      * Loads the system mode that this application needs.

+ 18 - 12
src/core/loader/nax.cpp

@@ -41,31 +41,37 @@ FileType AppLoader_NAX::GetFileType() const {
     return IdentifyTypeImpl(*nax);
     return IdentifyTypeImpl(*nax);
 }
 }
 
 
-ResultStatus AppLoader_NAX::Load(Kernel::Process& process) {
+AppLoader_NAX::LoadResult AppLoader_NAX::Load(Kernel::Process& process) {
     if (is_loaded) {
     if (is_loaded) {
-        return ResultStatus::ErrorAlreadyLoaded;
+        return {ResultStatus::ErrorAlreadyLoaded, {}};
     }
     }
 
 
-    if (nax->GetStatus() != ResultStatus::Success)
-        return nax->GetStatus();
+    const auto nax_status = nax->GetStatus();
+    if (nax_status != ResultStatus::Success) {
+        return {nax_status, {}};
+    }
 
 
     const auto nca = nax->AsNCA();
     const auto nca = nax->AsNCA();
     if (nca == nullptr) {
     if (nca == nullptr) {
-        if (!Core::Crypto::KeyManager::KeyFileExists(false))
-            return ResultStatus::ErrorMissingProductionKeyFile;
-        return ResultStatus::ErrorNAXInconvertibleToNCA;
+        if (!Core::Crypto::KeyManager::KeyFileExists(false)) {
+            return {ResultStatus::ErrorMissingProductionKeyFile, {}};
+        }
+
+        return {ResultStatus::ErrorNAXInconvertibleToNCA, {}};
     }
     }
 
 
-    if (nca->GetStatus() != ResultStatus::Success)
-        return nca->GetStatus();
+    const auto nca_status = nca->GetStatus();
+    if (nca_status != ResultStatus::Success) {
+        return {nca_status, {}};
+    }
 
 
     const auto result = nca_loader->Load(process);
     const auto result = nca_loader->Load(process);
-    if (result != ResultStatus::Success)
+    if (result.first != ResultStatus::Success) {
         return result;
         return result;
+    }
 
 
     is_loaded = true;
     is_loaded = true;
-
-    return ResultStatus::Success;
+    return result;
 }
 }
 
 
 ResultStatus AppLoader_NAX::ReadRomFS(FileSys::VirtualFile& dir) {
 ResultStatus AppLoader_NAX::ReadRomFS(FileSys::VirtualFile& dir) {

+ 1 - 1
src/core/loader/nax.h

@@ -33,7 +33,7 @@ public:
 
 
     FileType GetFileType() const override;
     FileType GetFileType() const override;
 
 
-    ResultStatus Load(Kernel::Process& process) override;
+    LoadResult Load(Kernel::Process& process) override;
 
 
     ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
     ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
     u64 ReadRomFSIVFCOffset() const override;
     u64 ReadRomFSIVFCOffset() const override;

+ 14 - 12
src/core/loader/nca.cpp

@@ -30,36 +30,38 @@ FileType AppLoader_NCA::IdentifyType(const FileSys::VirtualFile& file) {
     return FileType::Error;
     return FileType::Error;
 }
 }
 
 
-ResultStatus AppLoader_NCA::Load(Kernel::Process& process) {
+AppLoader_NCA::LoadResult AppLoader_NCA::Load(Kernel::Process& process) {
     if (is_loaded) {
     if (is_loaded) {
-        return ResultStatus::ErrorAlreadyLoaded;
+        return {ResultStatus::ErrorAlreadyLoaded, {}};
     }
     }
 
 
     const auto result = nca->GetStatus();
     const auto result = nca->GetStatus();
     if (result != ResultStatus::Success) {
     if (result != ResultStatus::Success) {
-        return result;
+        return {result, {}};
     }
     }
 
 
-    if (nca->GetType() != FileSys::NCAContentType::Program)
-        return ResultStatus::ErrorNCANotProgram;
+    if (nca->GetType() != FileSys::NCAContentType::Program) {
+        return {ResultStatus::ErrorNCANotProgram, {}};
+    }
 
 
     const auto exefs = nca->GetExeFS();
     const auto exefs = nca->GetExeFS();
-
-    if (exefs == nullptr)
-        return ResultStatus::ErrorNoExeFS;
+    if (exefs == nullptr) {
+        return {ResultStatus::ErrorNoExeFS, {}};
+    }
 
 
     directory_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(exefs, true);
     directory_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(exefs, true);
 
 
     const auto load_result = directory_loader->Load(process);
     const auto load_result = directory_loader->Load(process);
-    if (load_result != ResultStatus::Success)
+    if (load_result.first != ResultStatus::Success) {
         return load_result;
         return load_result;
+    }
 
 
-    if (nca->GetRomFS() != nullptr && nca->GetRomFS()->GetSize() > 0)
+    if (nca->GetRomFS() != nullptr && nca->GetRomFS()->GetSize() > 0) {
         Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this));
         Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this));
+    }
 
 
     is_loaded = true;
     is_loaded = true;
-
-    return ResultStatus::Success;
+    return load_result;
 }
 }
 
 
 ResultStatus AppLoader_NCA::ReadRomFS(FileSys::VirtualFile& dir) {
 ResultStatus AppLoader_NCA::ReadRomFS(FileSys::VirtualFile& dir) {

+ 1 - 1
src/core/loader/nca.h

@@ -33,7 +33,7 @@ public:
         return IdentifyType(file);
         return IdentifyType(file);
     }
     }
 
 
-    ResultStatus Load(Kernel::Process& process) override;
+    LoadResult Load(Kernel::Process& process) override;
 
 
     ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
     ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
     u64 ReadRomFSIVFCOffset() const override;
     u64 ReadRomFSIVFCOffset() const override;

+ 7 - 7
src/core/loader/nro.cpp

@@ -201,25 +201,25 @@ bool AppLoader_NRO::LoadNro(Kernel::Process& process, const FileSys::VfsFile& fi
     return LoadNroImpl(process, file.ReadAllBytes(), file.GetName(), load_base);
     return LoadNroImpl(process, file.ReadAllBytes(), file.GetName(), load_base);
 }
 }
 
 
-ResultStatus AppLoader_NRO::Load(Kernel::Process& process) {
+AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::Process& process) {
     if (is_loaded) {
     if (is_loaded) {
-        return ResultStatus::ErrorAlreadyLoaded;
+        return {ResultStatus::ErrorAlreadyLoaded, {}};
     }
     }
 
 
     // Load NRO
     // Load NRO
     const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
     const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
 
 
     if (!LoadNro(process, *file, base_address)) {
     if (!LoadNro(process, *file, base_address)) {
-        return ResultStatus::ErrorLoadingNRO;
+        return {ResultStatus::ErrorLoadingNRO, {}};
     }
     }
 
 
-    if (romfs != nullptr)
+    if (romfs != nullptr) {
         Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this));
         Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this));
-
-    process.Run(base_address, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE);
+    }
 
 
     is_loaded = true;
     is_loaded = true;
-    return ResultStatus::Success;
+    return {ResultStatus::Success,
+            LoadParameters{Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE}};
 }
 }
 
 
 ResultStatus AppLoader_NRO::ReadIcon(std::vector<u8>& buffer) {
 ResultStatus AppLoader_NRO::ReadIcon(std::vector<u8>& buffer) {

+ 1 - 1
src/core/loader/nro.h

@@ -37,7 +37,7 @@ public:
         return IdentifyType(file);
         return IdentifyType(file);
     }
     }
 
 
-    ResultStatus Load(Kernel::Process& process) override;
+    LoadResult Load(Kernel::Process& process) override;
 
 
     ResultStatus ReadIcon(std::vector<u8>& buffer) override;
     ResultStatus ReadIcon(std::vector<u8>& buffer) override;
     ResultStatus ReadProgramId(u64& out_program_id) override;
     ResultStatus ReadProgramId(u64& out_program_id) override;

+ 5 - 6
src/core/loader/nso.cpp

@@ -169,22 +169,21 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
     return load_base + image_size;
     return load_base + image_size;
 }
 }
 
 
-ResultStatus AppLoader_NSO::Load(Kernel::Process& process) {
+AppLoader_NSO::LoadResult AppLoader_NSO::Load(Kernel::Process& process) {
     if (is_loaded) {
     if (is_loaded) {
-        return ResultStatus::ErrorAlreadyLoaded;
+        return {ResultStatus::ErrorAlreadyLoaded, {}};
     }
     }
 
 
     // Load module
     // Load module
     const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
     const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
     if (!LoadModule(process, *file, base_address, true)) {
     if (!LoadModule(process, *file, base_address, true)) {
-        return ResultStatus::ErrorLoadingNSO;
+        return {ResultStatus::ErrorLoadingNSO, {}};
     }
     }
     LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), base_address);
     LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), base_address);
 
 
-    process.Run(base_address, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE);
-
     is_loaded = true;
     is_loaded = true;
-    return ResultStatus::Success;
+    return {ResultStatus::Success,
+            LoadParameters{Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE}};
 }
 }
 
 
 } // namespace Loader
 } // namespace Loader

+ 1 - 1
src/core/loader/nso.h

@@ -84,7 +84,7 @@ public:
                                            VAddr load_base, bool should_pass_arguments,
                                            VAddr load_base, bool should_pass_arguments,
                                            std::optional<FileSys::PatchManager> pm = {});
                                            std::optional<FileSys::PatchManager> pm = {});
 
 
-    ResultStatus Load(Kernel::Process& process) override;
+    LoadResult Load(Kernel::Process& process) override;
 };
 };
 
 
 } // namespace Loader
 } // namespace Loader

+ 23 - 15
src/core/loader/nsp.cpp

@@ -72,37 +72,45 @@ FileType AppLoader_NSP::IdentifyType(const FileSys::VirtualFile& file) {
     return FileType::Error;
     return FileType::Error;
 }
 }
 
 
-ResultStatus AppLoader_NSP::Load(Kernel::Process& process) {
+AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::Process& process) {
     if (is_loaded) {
     if (is_loaded) {
-        return ResultStatus::ErrorAlreadyLoaded;
+        return {ResultStatus::ErrorAlreadyLoaded, {}};
     }
     }
 
 
-    if (title_id == 0)
-        return ResultStatus::ErrorNSPMissingProgramNCA;
+    if (title_id == 0) {
+        return {ResultStatus::ErrorNSPMissingProgramNCA, {}};
+    }
 
 
-    if (nsp->GetStatus() != ResultStatus::Success)
-        return nsp->GetStatus();
+    const auto nsp_status = nsp->GetStatus();
+    if (nsp_status != ResultStatus::Success) {
+        return {nsp_status, {}};
+    }
 
 
-    if (nsp->GetProgramStatus(title_id) != ResultStatus::Success)
-        return nsp->GetProgramStatus(title_id);
+    const auto nsp_program_status = nsp->GetProgramStatus(title_id);
+    if (nsp_program_status != ResultStatus::Success) {
+        return {nsp_program_status, {}};
+    }
 
 
     if (nsp->GetNCA(title_id, FileSys::ContentRecordType::Program) == nullptr) {
     if (nsp->GetNCA(title_id, FileSys::ContentRecordType::Program) == nullptr) {
-        if (!Core::Crypto::KeyManager::KeyFileExists(false))
-            return ResultStatus::ErrorMissingProductionKeyFile;
-        return ResultStatus::ErrorNSPMissingProgramNCA;
+        if (!Core::Crypto::KeyManager::KeyFileExists(false)) {
+            return {ResultStatus::ErrorMissingProductionKeyFile, {}};
+        }
+
+        return {ResultStatus::ErrorNSPMissingProgramNCA, {}};
     }
     }
 
 
     const auto result = secondary_loader->Load(process);
     const auto result = secondary_loader->Load(process);
-    if (result != ResultStatus::Success)
+    if (result.first != ResultStatus::Success) {
         return result;
         return result;
+    }
 
 
     FileSys::VirtualFile update_raw;
     FileSys::VirtualFile update_raw;
-    if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr)
+    if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) {
         Service::FileSystem::SetPackedUpdate(std::move(update_raw));
         Service::FileSystem::SetPackedUpdate(std::move(update_raw));
+    }
 
 
     is_loaded = true;
     is_loaded = true;
-
-    return ResultStatus::Success;
+    return result;
 }
 }
 
 
 ResultStatus AppLoader_NSP::ReadRomFS(FileSys::VirtualFile& file) {
 ResultStatus AppLoader_NSP::ReadRomFS(FileSys::VirtualFile& file) {

+ 1 - 1
src/core/loader/nsp.h

@@ -35,7 +35,7 @@ public:
         return IdentifyType(file);
         return IdentifyType(file);
     }
     }
 
 
-    ResultStatus Load(Kernel::Process& process) override;
+    LoadResult Load(Kernel::Process& process) override;
 
 
     ResultStatus ReadRomFS(FileSys::VirtualFile& file) override;
     ResultStatus ReadRomFS(FileSys::VirtualFile& file) override;
     u64 ReadRomFSIVFCOffset() const override;
     u64 ReadRomFSIVFCOffset() const override;

+ 16 - 12
src/core/loader/xci.cpp

@@ -48,31 +48,35 @@ FileType AppLoader_XCI::IdentifyType(const FileSys::VirtualFile& file) {
     return FileType::Error;
     return FileType::Error;
 }
 }
 
 
-ResultStatus AppLoader_XCI::Load(Kernel::Process& process) {
+AppLoader_XCI::LoadResult AppLoader_XCI::Load(Kernel::Process& process) {
     if (is_loaded) {
     if (is_loaded) {
-        return ResultStatus::ErrorAlreadyLoaded;
+        return {ResultStatus::ErrorAlreadyLoaded, {}};
     }
     }
 
 
-    if (xci->GetStatus() != ResultStatus::Success)
-        return xci->GetStatus();
+    if (xci->GetStatus() != ResultStatus::Success) {
+        return {xci->GetStatus(), {}};
+    }
 
 
-    if (xci->GetProgramNCAStatus() != ResultStatus::Success)
-        return xci->GetProgramNCAStatus();
+    if (xci->GetProgramNCAStatus() != ResultStatus::Success) {
+        return {xci->GetProgramNCAStatus(), {}};
+    }
 
 
-    if (!xci->HasProgramNCA() && !Core::Crypto::KeyManager::KeyFileExists(false))
-        return ResultStatus::ErrorMissingProductionKeyFile;
+    if (!xci->HasProgramNCA() && !Core::Crypto::KeyManager::KeyFileExists(false)) {
+        return {ResultStatus::ErrorMissingProductionKeyFile, {}};
+    }
 
 
     const auto result = nca_loader->Load(process);
     const auto result = nca_loader->Load(process);
-    if (result != ResultStatus::Success)
+    if (result.first != ResultStatus::Success) {
         return result;
         return result;
+    }
 
 
     FileSys::VirtualFile update_raw;
     FileSys::VirtualFile update_raw;
-    if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr)
+    if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) {
         Service::FileSystem::SetPackedUpdate(std::move(update_raw));
         Service::FileSystem::SetPackedUpdate(std::move(update_raw));
+    }
 
 
     is_loaded = true;
     is_loaded = true;
-
-    return ResultStatus::Success;
+    return result;
 }
 }
 
 
 ResultStatus AppLoader_XCI::ReadRomFS(FileSys::VirtualFile& file) {
 ResultStatus AppLoader_XCI::ReadRomFS(FileSys::VirtualFile& file) {

+ 1 - 1
src/core/loader/xci.h

@@ -35,7 +35,7 @@ public:
         return IdentifyType(file);
         return IdentifyType(file);
     }
     }
 
 
-    ResultStatus Load(Kernel::Process& process) override;
+    LoadResult Load(Kernel::Process& process) override;
 
 
     ResultStatus ReadRomFS(FileSys::VirtualFile& file) override;
     ResultStatus ReadRomFS(FileSys::VirtualFile& file) override;
     u64 ReadRomFSIVFCOffset() const override;
     u64 ReadRomFSIVFCOffset() const override;

+ 8 - 8
src/core/memory.cpp

@@ -26,16 +26,16 @@ namespace Memory {
 
 
 static Common::PageTable* current_page_table = nullptr;
 static Common::PageTable* current_page_table = nullptr;
 
 
-void SetCurrentPageTable(Common::PageTable* page_table) {
-    current_page_table = page_table;
+void SetCurrentPageTable(Kernel::Process& process) {
+    current_page_table = &process.VMManager().page_table;
+
+    const std::size_t address_space_width = process.VMManager().GetAddressSpaceWidth();
 
 
     auto& system = Core::System::GetInstance();
     auto& system = Core::System::GetInstance();
-    if (system.IsPoweredOn()) {
-        system.ArmInterface(0).PageTableChanged();
-        system.ArmInterface(1).PageTableChanged();
-        system.ArmInterface(2).PageTableChanged();
-        system.ArmInterface(3).PageTableChanged();
-    }
+    system.ArmInterface(0).PageTableChanged(*current_page_table, address_space_width);
+    system.ArmInterface(1).PageTableChanged(*current_page_table, address_space_width);
+    system.ArmInterface(2).PageTableChanged(*current_page_table, address_space_width);
+    system.ArmInterface(3).PageTableChanged(*current_page_table, address_space_width);
 }
 }
 
 
 static void MapPages(Common::PageTable& page_table, VAddr base, u64 size, u8* memory,
 static void MapPages(Common::PageTable& page_table, VAddr base, u64 size, u8* memory,

+ 3 - 2
src/core/memory.h

@@ -40,8 +40,9 @@ enum : VAddr {
     KERNEL_REGION_END = KERNEL_REGION_VADDR + KERNEL_REGION_SIZE,
     KERNEL_REGION_END = KERNEL_REGION_VADDR + KERNEL_REGION_SIZE,
 };
 };
 
 
-/// Changes the currently active page table.
-void SetCurrentPageTable(Common::PageTable* page_table);
+/// Changes the currently active page table to that of
+/// the given process instance.
+void SetCurrentPageTable(Kernel::Process& process);
 
 
 /// Determines if the given VAddr is valid for the specified process.
 /// Determines if the given VAddr is valid for the specified process.
 bool IsValidVirtualAddress(const Kernel::Process& process, VAddr vaddr);
 bool IsValidVirtualAddress(const Kernel::Process& process, VAddr vaddr);

+ 5 - 0
src/video_core/gpu.h

@@ -207,6 +207,11 @@ public:
         };
         };
     } regs{};
     } regs{};
 
 
+    /// Performs any additional setup necessary in order to begin GPU emulation.
+    /// This can be used to launch any necessary threads and register any necessary
+    /// core timing events.
+    virtual void Start() = 0;
+
     /// Push GPU command entries to be processed
     /// Push GPU command entries to be processed
     virtual void PushGPUEntries(Tegra::CommandList&& entries) = 0;
     virtual void PushGPUEntries(Tegra::CommandList&& entries) = 0;
 
 

+ 5 - 1
src/video_core/gpu_asynch.cpp

@@ -9,10 +9,14 @@
 namespace VideoCommon {
 namespace VideoCommon {
 
 
 GPUAsynch::GPUAsynch(Core::System& system, VideoCore::RendererBase& renderer)
 GPUAsynch::GPUAsynch(Core::System& system, VideoCore::RendererBase& renderer)
-    : Tegra::GPU(system, renderer), gpu_thread{system, renderer, *dma_pusher} {}
+    : GPU(system, renderer), gpu_thread{system} {}
 
 
 GPUAsynch::~GPUAsynch() = default;
 GPUAsynch::~GPUAsynch() = default;
 
 
+void GPUAsynch::Start() {
+    gpu_thread.StartThread(renderer, *dma_pusher);
+}
+
 void GPUAsynch::PushGPUEntries(Tegra::CommandList&& entries) {
 void GPUAsynch::PushGPUEntries(Tegra::CommandList&& entries) {
     gpu_thread.SubmitList(std::move(entries));
     gpu_thread.SubmitList(std::move(entries));
 }
 }

+ 1 - 4
src/video_core/gpu_asynch.h

@@ -13,16 +13,13 @@ class RendererBase;
 
 
 namespace VideoCommon {
 namespace VideoCommon {
 
 
-namespace GPUThread {
-class ThreadManager;
-} // namespace GPUThread
-
 /// Implementation of GPU interface that runs the GPU asynchronously
 /// Implementation of GPU interface that runs the GPU asynchronously
 class GPUAsynch : public Tegra::GPU {
 class GPUAsynch : public Tegra::GPU {
 public:
 public:
     explicit GPUAsynch(Core::System& system, VideoCore::RendererBase& renderer);
     explicit GPUAsynch(Core::System& system, VideoCore::RendererBase& renderer);
     ~GPUAsynch() override;
     ~GPUAsynch() override;
 
 
+    void Start() override;
     void PushGPUEntries(Tegra::CommandList&& entries) override;
     void PushGPUEntries(Tegra::CommandList&& entries) override;
     void SwapBuffers(
     void SwapBuffers(
         std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) override;
         std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) override;

+ 3 - 1
src/video_core/gpu_synch.cpp

@@ -8,10 +8,12 @@
 namespace VideoCommon {
 namespace VideoCommon {
 
 
 GPUSynch::GPUSynch(Core::System& system, VideoCore::RendererBase& renderer)
 GPUSynch::GPUSynch(Core::System& system, VideoCore::RendererBase& renderer)
-    : Tegra::GPU(system, renderer) {}
+    : GPU(system, renderer) {}
 
 
 GPUSynch::~GPUSynch() = default;
 GPUSynch::~GPUSynch() = default;
 
 
+void GPUSynch::Start() {}
+
 void GPUSynch::PushGPUEntries(Tegra::CommandList&& entries) {
 void GPUSynch::PushGPUEntries(Tegra::CommandList&& entries) {
     dma_pusher->Push(std::move(entries));
     dma_pusher->Push(std::move(entries));
     dma_pusher->DispatchCalls();
     dma_pusher->DispatchCalls();

+ 1 - 0
src/video_core/gpu_synch.h

@@ -18,6 +18,7 @@ public:
     explicit GPUSynch(Core::System& system, VideoCore::RendererBase& renderer);
     explicit GPUSynch(Core::System& system, VideoCore::RendererBase& renderer);
     ~GPUSynch() override;
     ~GPUSynch() override;
 
 
+    void Start() override;
     void PushGPUEntries(Tegra::CommandList&& entries) override;
     void PushGPUEntries(Tegra::CommandList&& entries) override;
     void SwapBuffers(
     void SwapBuffers(
         std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) override;
         std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) override;

+ 11 - 6
src/video_core/gpu_thread.cpp

@@ -55,19 +55,24 @@ static void RunThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_p
     }
     }
 }
 }
 
 
-ThreadManager::ThreadManager(Core::System& system, VideoCore::RendererBase& renderer,
-                             Tegra::DmaPusher& dma_pusher)
-    : system{system}, thread{RunThread, std::ref(renderer), std::ref(dma_pusher), std::ref(state)} {
-    synchronization_event = system.CoreTiming().RegisterEvent(
-        "GPUThreadSynch", [this](u64 fence, s64) { state.WaitForSynchronization(fence); });
-}
+ThreadManager::ThreadManager(Core::System& system) : system{system} {}
 
 
 ThreadManager::~ThreadManager() {
 ThreadManager::~ThreadManager() {
+    if (!thread.joinable()) {
+        return;
+    }
+
     // Notify GPU thread that a shutdown is pending
     // Notify GPU thread that a shutdown is pending
     PushCommand(EndProcessingCommand());
     PushCommand(EndProcessingCommand());
     thread.join();
     thread.join();
 }
 }
 
 
+void ThreadManager::StartThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher) {
+    thread = std::thread{RunThread, std::ref(renderer), std::ref(dma_pusher), std::ref(state)};
+    synchronization_event = system.CoreTiming().RegisterEvent(
+        "GPUThreadSynch", [this](u64 fence, s64) { state.WaitForSynchronization(fence); });
+}
+
 void ThreadManager::SubmitList(Tegra::CommandList&& entries) {
 void ThreadManager::SubmitList(Tegra::CommandList&& entries) {
     const u64 fence{PushCommand(SubmitListCommand(std::move(entries)))};
     const u64 fence{PushCommand(SubmitListCommand(std::move(entries)))};
     const s64 synchronization_ticks{Core::Timing::usToCycles(9000)};
     const s64 synchronization_ticks{Core::Timing::usToCycles(9000)};

+ 4 - 2
src/video_core/gpu_thread.h

@@ -138,10 +138,12 @@ struct SynchState final {
 /// Class used to manage the GPU thread
 /// Class used to manage the GPU thread
 class ThreadManager final {
 class ThreadManager final {
 public:
 public:
-    explicit ThreadManager(Core::System& system, VideoCore::RendererBase& renderer,
-                           Tegra::DmaPusher& dma_pusher);
+    explicit ThreadManager(Core::System& system);
     ~ThreadManager();
     ~ThreadManager();
 
 
+    /// Creates and starts the GPU thread.
+    void StartThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher);
+
     /// Push GPU command entries to be processed
     /// Push GPU command entries to be processed
     void SubmitList(Tegra::CommandList&& entries);
     void SubmitList(Tegra::CommandList&& entries);
 
 

+ 10 - 0
src/video_core/video_core.cpp

@@ -5,6 +5,8 @@
 #include <memory>
 #include <memory>
 #include "core/core.h"
 #include "core/core.h"
 #include "core/settings.h"
 #include "core/settings.h"
+#include "video_core/gpu_asynch.h"
+#include "video_core/gpu_synch.h"
 #include "video_core/renderer_base.h"
 #include "video_core/renderer_base.h"
 #include "video_core/renderer_opengl/renderer_opengl.h"
 #include "video_core/renderer_opengl/renderer_opengl.h"
 #include "video_core/video_core.h"
 #include "video_core/video_core.h"
@@ -16,6 +18,14 @@ std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_wind
     return std::make_unique<OpenGL::RendererOpenGL>(emu_window, system);
     return std::make_unique<OpenGL::RendererOpenGL>(emu_window, system);
 }
 }
 
 
+std::unique_ptr<Tegra::GPU> CreateGPU(Core::System& system) {
+    if (Settings::values.use_asynchronous_gpu_emulation) {
+        return std::make_unique<VideoCommon::GPUAsynch>(system, system.Renderer());
+    }
+
+    return std::make_unique<VideoCommon::GPUSynch>(system, system.Renderer());
+}
+
 u16 GetResolutionScaleFactor(const RendererBase& renderer) {
 u16 GetResolutionScaleFactor(const RendererBase& renderer) {
     return static_cast<u16>(
     return static_cast<u16>(
         Settings::values.resolution_factor
         Settings::values.resolution_factor

+ 7 - 0
src/video_core/video_core.h

@@ -14,6 +14,10 @@ namespace Core::Frontend {
 class EmuWindow;
 class EmuWindow;
 }
 }
 
 
+namespace Tegra {
+class GPU;
+}
+
 namespace VideoCore {
 namespace VideoCore {
 
 
 class RendererBase;
 class RendererBase;
@@ -27,6 +31,9 @@ class RendererBase;
 std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window,
 std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window,
                                              Core::System& system);
                                              Core::System& system);
 
 
+/// Creates an emulated GPU instance using the given system context.
+std::unique_ptr<Tegra::GPU> CreateGPU(Core::System& system);
+
 u16 GetResolutionScaleFactor(const RendererBase& renderer);
 u16 GetResolutionScaleFactor(const RendererBase& renderer);
 
 
 } // namespace VideoCore
 } // namespace VideoCore