mirror of
https://git.suyu.dev/suyu/suyu.git
synced 2024-11-29 23:06:23 -05:00
Merge pull request #2374 from lioncash/pagetable
core: Reorganize boot order
This commit is contained in:
commit
40dc893c37
38 changed files with 253 additions and 177 deletions
|
@ -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
|
/// Notifies CPU emulation that the current page table has changed.
|
||||||
virtual void PageTableChanged() = 0;
|
///
|
||||||
|
/// @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
|
||||||
|
|
|
@ -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 {
|
std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit(Common::PageTable& page_table,
|
||||||
auto* current_process = system.Kernel().CurrentProcess();
|
std::size_t address_space_bits) const {
|
||||||
auto** const page_table = current_process->VMManager().page_table.pointers.data();
|
|
||||||
|
|
||||||
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 = reinterpret_cast<void**>(page_table.pointers.data());
|
||||||
config.page_table_address_space_bits = current_process->VMManager().GetAddressSpaceWidth();
|
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)} {
|
exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {}
|
||||||
ThreadContext ctx{};
|
|
||||||
inner_unicorn.SaveContext(ctx);
|
|
||||||
PageTableChanged();
|
|
||||||
LoadContext(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
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() {
|
void ARM_Dynarmic::PageTableChanged(Common::PageTable& page_table,
|
||||||
jit = MakeJit();
|
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) {}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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,16 +128,10 @@ struct System::Impl {
|
||||||
return ResultStatus::ErrorVideoCore;
|
return ResultStatus::ErrorVideoCore;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gpu_core = VideoCore::CreateGPU(system);
|
||||||
|
|
||||||
is_powered_on = true;
|
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
cpu_core_manager.Initialize(system);
|
|
||||||
|
|
||||||
LOG_DEBUG(Core, "Initialized OK");
|
LOG_DEBUG(Core, "Initialized OK");
|
||||||
|
|
||||||
// Reset counters and set time origin to current frame
|
// Reset counters and set time origin to current frame
|
||||||
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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) {
|
void SetupMainThread(Process& owner_process, KernelCore& kernel, u32 priority) {
|
||||||
// Initialize new "main" thread
|
const auto& vm_manager = owner_process.VMManager();
|
||||||
const VAddr stack_top = owner_process.VMManager().GetTLSIORegionEndAddress();
|
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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
if (file == nullptr) {
|
||||||
return ResultStatus::ErrorNullFile;
|
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)
|
if (npdm == nullptr) {
|
||||||
return ResultStatus::ErrorMissingNPDM;
|
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)
|
if (npdm == nullptr) {
|
||||||
return ResultStatus::ErrorMissingNPDM;
|
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) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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) {
|
AppLoader_ELF::LoadResult AppLoader_ELF::Load(Kernel::Process& process) {
|
||||||
if (is_loaded)
|
if (is_loaded) {
|
||||||
return ResultStatus::ErrorAlreadyLoaded;
|
return {ResultStatus::ErrorAlreadyLoaded, {}};
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<u8> buffer = file->ReadAllBytes();
|
std::vector<u8> buffer = file->ReadAllBytes();
|
||||||
if (buffer.size() != file->GetSize())
|
if (buffer.size() != file->GetSize()) {
|
||||||
return ResultStatus::ErrorIncorrectELFFileSize;
|
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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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)
|
const auto nax_status = nax->GetStatus();
|
||||||
return 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))
|
if (!Core::Crypto::KeyManager::KeyFileExists(false)) {
|
||||||
return ResultStatus::ErrorMissingProductionKeyFile;
|
return {ResultStatus::ErrorMissingProductionKeyFile, {}};
|
||||||
return ResultStatus::ErrorNAXInconvertibleToNCA;
|
}
|
||||||
|
|
||||||
|
return {ResultStatus::ErrorNAXInconvertibleToNCA, {}};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nca->GetStatus() != ResultStatus::Success)
|
const auto nca_status = nca->GetStatus();
|
||||||
return 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 result;
|
||||||
return ResultStatus::Success;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultStatus AppLoader_NAX::ReadRomFS(FileSys::VirtualFile& dir) {
|
ResultStatus AppLoader_NAX::ReadRomFS(FileSys::VirtualFile& dir) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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)
|
if (nca->GetType() != FileSys::NCAContentType::Program) {
|
||||||
return ResultStatus::ErrorNCANotProgram;
|
return {ResultStatus::ErrorNCANotProgram, {}};
|
||||||
|
}
|
||||||
|
|
||||||
const auto exefs = nca->GetExeFS();
|
const auto exefs = nca->GetExeFS();
|
||||||
|
if (exefs == nullptr) {
|
||||||
if (exefs == nullptr)
|
return {ResultStatus::ErrorNoExeFS, {}};
|
||||||
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 load_result;
|
||||||
return ResultStatus::Success;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultStatus AppLoader_NCA::ReadRomFS(FileSys::VirtualFile& dir) {
|
ResultStatus AppLoader_NCA::ReadRomFS(FileSys::VirtualFile& dir) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
if (title_id == 0) {
|
||||||
return ResultStatus::ErrorNSPMissingProgramNCA;
|
return {ResultStatus::ErrorNSPMissingProgramNCA, {}};
|
||||||
|
}
|
||||||
|
|
||||||
if (nsp->GetStatus() != ResultStatus::Success)
|
const auto nsp_status = nsp->GetStatus();
|
||||||
return nsp->GetStatus();
|
if (nsp_status != ResultStatus::Success) {
|
||||||
|
return {nsp_status, {}};
|
||||||
|
}
|
||||||
|
|
||||||
if (nsp->GetProgramStatus(title_id) != ResultStatus::Success)
|
const auto nsp_program_status = nsp->GetProgramStatus(title_id);
|
||||||
return 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))
|
if (!Core::Crypto::KeyManager::KeyFileExists(false)) {
|
||||||
return ResultStatus::ErrorMissingProductionKeyFile;
|
return {ResultStatus::ErrorMissingProductionKeyFile, {}};
|
||||||
return ResultStatus::ErrorNSPMissingProgramNCA;
|
}
|
||||||
|
|
||||||
|
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 result;
|
||||||
return ResultStatus::Success;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultStatus AppLoader_NSP::ReadRomFS(FileSys::VirtualFile& file) {
|
ResultStatus AppLoader_NSP::ReadRomFS(FileSys::VirtualFile& file) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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)
|
if (xci->GetStatus() != ResultStatus::Success) {
|
||||||
return xci->GetStatus();
|
return {xci->GetStatus(), {}};
|
||||||
|
}
|
||||||
|
|
||||||
if (xci->GetProgramNCAStatus() != ResultStatus::Success)
|
if (xci->GetProgramNCAStatus() != ResultStatus::Success) {
|
||||||
return xci->GetProgramNCAStatus();
|
return {xci->GetProgramNCAStatus(), {}};
|
||||||
|
}
|
||||||
|
|
||||||
if (!xci->HasProgramNCA() && !Core::Crypto::KeyManager::KeyFileExists(false))
|
if (!xci->HasProgramNCA() && !Core::Crypto::KeyManager::KeyFileExists(false)) {
|
||||||
return ResultStatus::ErrorMissingProductionKeyFile;
|
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 result;
|
||||||
return ResultStatus::Success;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultStatus AppLoader_XCI::ReadRomFS(FileSys::VirtualFile& file) {
|
ResultStatus AppLoader_XCI::ReadRomFS(FileSys::VirtualFile& file) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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) {
|
void SetCurrentPageTable(Kernel::Process& process) {
|
||||||
current_page_table = page_table;
|
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(*current_page_table, address_space_width);
|
||||||
system.ArmInterface(0).PageTableChanged();
|
system.ArmInterface(1).PageTableChanged(*current_page_table, address_space_width);
|
||||||
system.ArmInterface(1).PageTableChanged();
|
system.ArmInterface(2).PageTableChanged(*current_page_table, address_space_width);
|
||||||
system.ArmInterface(2).PageTableChanged();
|
system.ArmInterface(3).PageTableChanged(*current_page_table, address_space_width);
|
||||||
system.ArmInterface(3).PageTableChanged();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
||||||
|
|
|
@ -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.
|
/// Changes the currently active page table to that of
|
||||||
void SetCurrentPageTable(Common::PageTable* page_table);
|
/// 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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -55,19 +55,24 @@ static void RunThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_p
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ThreadManager::ThreadManager(Core::System& system, VideoCore::RendererBase& renderer,
|
ThreadManager::ThreadManager(Core::System& system) : system{system} {}
|
||||||
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() {
|
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)};
|
||||||
|
|
|
@ -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,
|
explicit ThreadManager(Core::System& system);
|
||||||
Tegra::DmaPusher& dma_pusher);
|
|
||||||
~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);
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue