From 612e1388df3bed64081488f2a99cce522c80c76d Mon Sep 17 00:00:00 2001
From: Lioncash <mathew1800@gmail.com>
Date: Tue, 9 Apr 2019 17:03:04 -0400
Subject: [PATCH] core/core: Move process execution start to System's Load()

This gives us significantly more control over where in the
initialization process we start execution of the main process.

Previously we were running the main process before the CPU or GPU
threads were initialized (not good). This amends execution to start
after all of our threads are properly set up.
---
 src/core/core.cpp                             |  6 ++-
 src/core/hle/kernel/process.cpp               | 12 +++---
 src/core/hle/kernel/process.h                 |  7 +++-
 .../loader/deconstructed_rom_directory.cpp    | 40 ++++++++++---------
 src/core/loader/deconstructed_rom_directory.h |  2 +-
 src/core/loader/elf.cpp                       | 15 +++----
 src/core/loader/elf.h                         |  2 +-
 src/core/loader/loader.h                      |  8 +++-
 src/core/loader/nax.cpp                       | 30 ++++++++------
 src/core/loader/nax.h                         |  2 +-
 src/core/loader/nca.cpp                       | 26 ++++++------
 src/core/loader/nca.h                         |  2 +-
 src/core/loader/nro.cpp                       | 14 +++----
 src/core/loader/nro.h                         |  2 +-
 src/core/loader/nso.cpp                       | 11 +++--
 src/core/loader/nso.h                         |  2 +-
 src/core/loader/nsp.cpp                       | 38 +++++++++++-------
 src/core/loader/nsp.h                         |  2 +-
 src/core/loader/xci.cpp                       | 28 +++++++------
 src/core/loader/xci.h                         |  2 +-
 20 files changed, 144 insertions(+), 107 deletions(-)

diff --git a/src/core/core.cpp b/src/core/core.cpp
index 265ac2835c..175a5f2ea5 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -168,7 +168,7 @@ struct System::Impl {
         }
 
         auto main_process = Kernel::Process::Create(system, "main");
-        const Loader::ResultStatus load_result{app_loader->Load(*main_process)};
+        const auto [load_result, load_parameters] = app_loader->Load(*main_process);
         if (load_result != Loader::ResultStatus::Success) {
             LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<int>(load_result));
             Shutdown();
@@ -183,6 +183,10 @@ struct System::Impl {
         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;
         return status;
     }
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index bf0d479af2..9825274b4d 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -28,12 +28,12 @@ namespace {
  *
  * @param owner_process The parent process for the main thread
  * @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
  */
-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,
                                      owner_process.GetIdealCore(), stack_top, owner_process);
 
@@ -117,7 +117,7 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) {
     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.
     main_thread_stack_size = Common::AlignUp(stack_size, Memory::PAGE_SIZE);
 
@@ -133,7 +133,7 @@ void Process::Run(VAddr entry_point, s32 main_thread_priority, u64 stack_size) {
     vm_manager.LogLayout();
     ChangeStatus(ProcessStatus::Running);
 
-    SetupMainThread(*this, kernel, entry_point, main_thread_priority);
+    SetupMainThread(*this, kernel, main_thread_priority);
 }
 
 void Process::PrepareForTermination() {
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h
index dda52f4c01..bf3b7eef32 100644
--- a/src/core/hle/kernel/process.h
+++ b/src/core/hle/kernel/process.h
@@ -225,9 +225,12 @@ public:
     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
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index 07aa7a1cd6..10b13fb1df 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
     return FileType::Error;
 }
 
-ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process) {
+AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirectory::Load(
+    Kernel::Process& process) {
     if (is_loaded) {
-        return ResultStatus::ErrorAlreadyLoaded;
+        return {ResultStatus::ErrorAlreadyLoaded, {}};
     }
 
     if (dir == nullptr) {
-        if (file == nullptr)
-            return ResultStatus::ErrorNullFile;
+        if (file == nullptr) {
+            return {ResultStatus::ErrorNullFile, {}};
+        }
+
         dir = file->GetContainingDirectory();
     }
 
     // Read meta to determine title ID
     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) {
-        return result;
+        return {result, {}};
     }
 
     if (override_update) {
@@ -114,23 +118,24 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process)
 
     // Reread in case PatchExeFS affected the 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) {
-        return result2;
+        return {result2, {}};
     }
     metadata.Print();
 
     const FileSys::ProgramAddressSpaceType arch_bits{metadata.GetAddressSpaceType()};
     if (arch_bits == FileSys::ProgramAddressSpaceType::Is32Bit ||
         arch_bits == FileSys::ProgramAddressSpaceType::Is32BitNoMap) {
-        return ResultStatus::Error32BitISA;
+        return {ResultStatus::Error32BitISA, {}};
     }
 
     if (process.LoadFromMetadata(metadata).IsError()) {
-        return ResultStatus::ErrorUnableToParseKernelMetadata;
+        return {ResultStatus::ErrorUnableToParseKernelMetadata, {}};
     }
 
     const FileSys::PatchManager pm(metadata.GetTitleID());
@@ -150,7 +155,7 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process)
         const auto tentative_next_load_addr =
             AppLoader_NSO::LoadModule(process, *module_file, load_addr, should_pass_arguments, pm);
         if (!tentative_next_load_addr) {
-            return ResultStatus::ErrorLoadingNSO;
+            return {ResultStatus::ErrorLoadingNSO, {}};
         }
 
         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);
     }
 
-    process.Run(base_address, metadata.GetMainThreadPriority(), metadata.GetMainThreadStackSize());
-
     // Find the RomFS by searching for a ".romfs" file in this directory
     const auto& files = dir->GetFiles();
     const auto romfs_iter =
@@ -175,7 +178,8 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process)
     }
 
     is_loaded = true;
-    return ResultStatus::Success;
+    return {ResultStatus::Success,
+            LoadParameters{metadata.GetMainThreadPriority(), metadata.GetMainThreadStackSize()}};
 }
 
 ResultStatus 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 1615cb5a87..1a65c16a41 100644
--- a/src/core/loader/deconstructed_rom_directory.h
+++ b/src/core/loader/deconstructed_rom_directory.h
@@ -37,7 +37,7 @@ public:
         return IdentifyType(file);
     }
 
-    ResultStatus Load(Kernel::Process& process) override;
+    LoadResult Load(Kernel::Process& process) override;
 
     ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
     ResultStatus ReadIcon(std::vector<u8>& buffer) override;
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp
index 46ac372f66..6d4b023758 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) {
     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();
-    if (buffer.size() != file->GetSize())
-        return ResultStatus::ErrorIncorrectELFFileSize;
+    if (buffer.size() != file->GetSize()) {
+        return {ResultStatus::ErrorIncorrectELFFileSize, {}};
+    }
 
     const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
     ElfReader elf_reader(&buffer[0]);
@@ -396,10 +398,9 @@ ResultStatus AppLoader_ELF::Load(Kernel::Process& process) {
     const VAddr entry_point = codeset.entrypoint;
 
     process.LoadModule(std::move(codeset), entry_point);
-    process.Run(entry_point, 48, Memory::DEFAULT_STACK_SIZE);
 
     is_loaded = true;
-    return ResultStatus::Success;
+    return {ResultStatus::Success, LoadParameters{48, Memory::DEFAULT_STACK_SIZE}};
 }
 
 } // namespace Loader
diff --git a/src/core/loader/elf.h b/src/core/loader/elf.h
index a2d33021ce..7ef7770a6c 100644
--- a/src/core/loader/elf.h
+++ b/src/core/loader/elf.h
@@ -26,7 +26,7 @@ public:
         return IdentifyType(file);
     }
 
-    ResultStatus Load(Kernel::Process& process) override;
+    LoadResult Load(Kernel::Process& process) override;
 };
 
 } // namespace Loader
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index bb925f4a64..f7846db52c 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);
 /// Interface for loading an application
 class AppLoader : NonCopyable {
 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);
     virtual ~AppLoader();
 
@@ -145,7 +151,7 @@ public:
      * @param process The newly created process.
      * @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.
diff --git a/src/core/loader/nax.cpp b/src/core/loader/nax.cpp
index 93a970d100..34efef09a7 100644
--- a/src/core/loader/nax.cpp
+++ b/src/core/loader/nax.cpp
@@ -41,31 +41,37 @@ FileType AppLoader_NAX::GetFileType() const {
     return IdentifyTypeImpl(*nax);
 }
 
-ResultStatus AppLoader_NAX::Load(Kernel::Process& process) {
+AppLoader_NAX::LoadResult AppLoader_NAX::Load(Kernel::Process& process) {
     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();
     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);
-    if (result != ResultStatus::Success)
+    if (result.first != ResultStatus::Success) {
         return result;
+    }
 
     is_loaded = true;
-
-    return ResultStatus::Success;
+    return result;
 }
 
 ResultStatus AppLoader_NAX::ReadRomFS(FileSys::VirtualFile& dir) {
diff --git a/src/core/loader/nax.h b/src/core/loader/nax.h
index f400795743..00f1659c14 100644
--- a/src/core/loader/nax.h
+++ b/src/core/loader/nax.h
@@ -33,7 +33,7 @@ public:
 
     FileType GetFileType() const override;
 
-    ResultStatus Load(Kernel::Process& process) override;
+    LoadResult Load(Kernel::Process& process) override;
 
     ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
     u64 ReadRomFSIVFCOffset() const override;
diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp
index ce8196fcfd..b3f8f1083b 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) {
     return FileType::Error;
 }
 
-ResultStatus AppLoader_NCA::Load(Kernel::Process& process) {
+AppLoader_NCA::LoadResult AppLoader_NCA::Load(Kernel::Process& process) {
     if (is_loaded) {
-        return ResultStatus::ErrorAlreadyLoaded;
+        return {ResultStatus::ErrorAlreadyLoaded, {}};
     }
 
     const auto result = nca->GetStatus();
     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();
-
-    if (exefs == nullptr)
-        return ResultStatus::ErrorNoExeFS;
+    if (exefs == nullptr) {
+        return {ResultStatus::ErrorNoExeFS, {}};
+    }
 
     directory_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(exefs, true);
 
     const auto load_result = directory_loader->Load(process);
-    if (load_result != ResultStatus::Success)
+    if (load_result.first != ResultStatus::Success) {
         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));
+    }
 
     is_loaded = true;
-
-    return ResultStatus::Success;
+    return load_result;
 }
 
 ResultStatus AppLoader_NCA::ReadRomFS(FileSys::VirtualFile& dir) {
diff --git a/src/core/loader/nca.h b/src/core/loader/nca.h
index b9f0774683..94f0ed6779 100644
--- a/src/core/loader/nca.h
+++ b/src/core/loader/nca.h
@@ -33,7 +33,7 @@ public:
         return IdentifyType(file);
     }
 
-    ResultStatus Load(Kernel::Process& process) override;
+    LoadResult Load(Kernel::Process& process) override;
 
     ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
     u64 ReadRomFSIVFCOffset() const override;
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
index 31e4a0c840..6a0ca389b0 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
     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) {
-        return ResultStatus::ErrorAlreadyLoaded;
+        return {ResultStatus::ErrorAlreadyLoaded, {}};
     }
 
     // Load NRO
     const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
 
     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));
-
-    process.Run(base_address, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE);
+    }
 
     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) {
diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h
index 85b0ed6441..1ffdae8058 100644
--- a/src/core/loader/nro.h
+++ b/src/core/loader/nro.h
@@ -37,7 +37,7 @@ public:
         return IdentifyType(file);
     }
 
-    ResultStatus Load(Kernel::Process& process) override;
+    LoadResult Load(Kernel::Process& process) override;
 
     ResultStatus ReadIcon(std::vector<u8>& buffer) override;
     ResultStatus ReadProgramId(u64& out_program_id) override;
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index d7c47c1971..a866532044 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,
     return load_base + image_size;
 }
 
-ResultStatus AppLoader_NSO::Load(Kernel::Process& process) {
+AppLoader_NSO::LoadResult AppLoader_NSO::Load(Kernel::Process& process) {
     if (is_loaded) {
-        return ResultStatus::ErrorAlreadyLoaded;
+        return {ResultStatus::ErrorAlreadyLoaded, {}};
     }
 
     // Load module
     const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
     if (!LoadModule(process, *file, base_address, true)) {
-        return ResultStatus::ErrorLoadingNSO;
+        return {ResultStatus::ErrorLoadingNSO, {}};
     }
     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;
-    return ResultStatus::Success;
+    return {ResultStatus::Success,
+            LoadParameters{Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE}};
 }
 
 } // namespace Loader
diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h
index 4674c37242..fdce9191cd 100644
--- a/src/core/loader/nso.h
+++ b/src/core/loader/nso.h
@@ -84,7 +84,7 @@ public:
                                            VAddr load_base, bool should_pass_arguments,
                                            std::optional<FileSys::PatchManager> pm = {});
 
-    ResultStatus Load(Kernel::Process& process) override;
+    LoadResult Load(Kernel::Process& process) override;
 };
 
 } // namespace Loader
diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp
index 7da1f8960d..ad56bbb385 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) {
     return FileType::Error;
 }
 
-ResultStatus AppLoader_NSP::Load(Kernel::Process& process) {
+AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::Process& process) {
     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 (!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);
-    if (result != ResultStatus::Success)
+    if (result.first != ResultStatus::Success) {
         return result;
+    }
 
     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));
+    }
 
     is_loaded = true;
-
-    return ResultStatus::Success;
+    return result;
 }
 
 ResultStatus AppLoader_NSP::ReadRomFS(FileSys::VirtualFile& file) {
diff --git a/src/core/loader/nsp.h b/src/core/loader/nsp.h
index 953a1b5087..85e870bdf0 100644
--- a/src/core/loader/nsp.h
+++ b/src/core/loader/nsp.h
@@ -35,7 +35,7 @@ public:
         return IdentifyType(file);
     }
 
-    ResultStatus Load(Kernel::Process& process) override;
+    LoadResult Load(Kernel::Process& process) override;
 
     ResultStatus ReadRomFS(FileSys::VirtualFile& file) override;
     u64 ReadRomFSIVFCOffset() const override;
diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp
index 89f7bbf774..1e285a0531 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) {
     return FileType::Error;
 }
 
-ResultStatus AppLoader_XCI::Load(Kernel::Process& process) {
+AppLoader_XCI::LoadResult AppLoader_XCI::Load(Kernel::Process& process) {
     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);
-    if (result != ResultStatus::Success)
+    if (result.first != ResultStatus::Success) {
         return result;
+    }
 
     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));
+    }
 
     is_loaded = true;
-
-    return ResultStatus::Success;
+    return result;
 }
 
 ResultStatus AppLoader_XCI::ReadRomFS(FileSys::VirtualFile& file) {
diff --git a/src/core/loader/xci.h b/src/core/loader/xci.h
index 436f7387cb..ae7145b14a 100644
--- a/src/core/loader/xci.h
+++ b/src/core/loader/xci.h
@@ -35,7 +35,7 @@ public:
         return IdentifyType(file);
     }
 
-    ResultStatus Load(Kernel::Process& process) override;
+    LoadResult Load(Kernel::Process& process) override;
 
     ResultStatus ReadRomFS(FileSys::VirtualFile& file) override;
     u64 ReadRomFSIVFCOffset() const override;