general: properly support multiple memory instances
This commit is contained in:
parent
cae675343c
commit
31bf57a310
|
@ -166,6 +166,10 @@ u32 ProgramMetadata::GetSystemResourceSize() const {
|
||||||
return npdm_header.system_resource_size;
|
return npdm_header.system_resource_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PoolPartition ProgramMetadata::GetPoolPartition() const {
|
||||||
|
return acid_header.pool_partition;
|
||||||
|
}
|
||||||
|
|
||||||
const ProgramMetadata::KernelCapabilityDescriptors& ProgramMetadata::GetKernelCapabilities() const {
|
const ProgramMetadata::KernelCapabilityDescriptors& ProgramMetadata::GetKernelCapabilities() const {
|
||||||
return aci_kernel_capabilities;
|
return aci_kernel_capabilities;
|
||||||
}
|
}
|
||||||
|
@ -201,7 +205,7 @@ void ProgramMetadata::Print() const {
|
||||||
// Begin ACID printing (potential perms, signed)
|
// Begin ACID printing (potential perms, signed)
|
||||||
LOG_DEBUG(Service_FS, "Magic: {:.4}", acid_header.magic.data());
|
LOG_DEBUG(Service_FS, "Magic: {:.4}", acid_header.magic.data());
|
||||||
LOG_DEBUG(Service_FS, "Flags: 0x{:02X}", acid_header.flags);
|
LOG_DEBUG(Service_FS, "Flags: 0x{:02X}", acid_header.flags);
|
||||||
LOG_DEBUG(Service_FS, " > Is Retail: {}", acid_header.is_retail ? "YES" : "NO");
|
LOG_DEBUG(Service_FS, " > Is Retail: {}", acid_header.production_flag ? "YES" : "NO");
|
||||||
LOG_DEBUG(Service_FS, "Title ID Min: 0x{:016X}", acid_header.title_id_min);
|
LOG_DEBUG(Service_FS, "Title ID Min: 0x{:016X}", acid_header.title_id_min);
|
||||||
LOG_DEBUG(Service_FS, "Title ID Max: 0x{:016X}", acid_header.title_id_max);
|
LOG_DEBUG(Service_FS, "Title ID Max: 0x{:016X}", acid_header.title_id_max);
|
||||||
LOG_DEBUG(Service_FS, "Filesystem Access: 0x{:016X}\n", acid_file_access.permissions);
|
LOG_DEBUG(Service_FS, "Filesystem Access: 0x{:016X}\n", acid_file_access.permissions);
|
||||||
|
|
|
@ -34,6 +34,13 @@ enum class ProgramFilePermission : u64 {
|
||||||
Everything = 1ULL << 63,
|
Everything = 1ULL << 63,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class PoolPartition : u32 {
|
||||||
|
Application = 0,
|
||||||
|
Applet = 1,
|
||||||
|
System = 2,
|
||||||
|
SystemNonSecure = 3,
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper which implements an interface to parse Program Description Metadata (NPDM)
|
* Helper which implements an interface to parse Program Description Metadata (NPDM)
|
||||||
* Data can either be loaded from a file path or with data and an offset into it.
|
* Data can either be loaded from a file path or with data and an offset into it.
|
||||||
|
@ -72,6 +79,7 @@ public:
|
||||||
u64 GetTitleID() const;
|
u64 GetTitleID() const;
|
||||||
u64 GetFilesystemPermissions() const;
|
u64 GetFilesystemPermissions() const;
|
||||||
u32 GetSystemResourceSize() const;
|
u32 GetSystemResourceSize() const;
|
||||||
|
PoolPartition GetPoolPartition() const;
|
||||||
const KernelCapabilityDescriptors& GetKernelCapabilities() const;
|
const KernelCapabilityDescriptors& GetKernelCapabilities() const;
|
||||||
const std::array<u8, 0x10>& GetName() const {
|
const std::array<u8, 0x10>& GetName() const {
|
||||||
return npdm_header.application_name;
|
return npdm_header.application_name;
|
||||||
|
@ -116,8 +124,9 @@ private:
|
||||||
union {
|
union {
|
||||||
u32 flags;
|
u32 flags;
|
||||||
|
|
||||||
BitField<0, 1, u32> is_retail;
|
BitField<0, 1, u32> production_flag;
|
||||||
BitField<1, 31, u32> flags_unk;
|
BitField<1, 1, u32> unqualified_approval;
|
||||||
|
BitField<2, 4, PoolPartition> pool_partition;
|
||||||
};
|
};
|
||||||
u64_le title_id_min;
|
u64_le title_id_min;
|
||||||
u64_le title_id_max;
|
u64_le title_id_max;
|
||||||
|
|
|
@ -58,9 +58,8 @@ Result KClientPort::CreateSession(KClientSession** out) {
|
||||||
KSession* session{};
|
KSession* session{};
|
||||||
|
|
||||||
// Reserve a new session from the resource limit.
|
// Reserve a new session from the resource limit.
|
||||||
//! FIXME: we are reserving this from the wrong resource limit!
|
|
||||||
KScopedResourceReservation session_reservation(
|
KScopedResourceReservation session_reservation(
|
||||||
m_kernel.ApplicationProcess()->GetResourceLimit(), LimitableResource::SessionCountMax);
|
GetCurrentProcessPointer(m_kernel)->GetResourceLimit(), LimitableResource::SessionCountMax);
|
||||||
R_UNLESS(session_reservation.Succeeded(), ResultLimitReached);
|
R_UNLESS(session_reservation.Succeeded(), ResultLimitReached);
|
||||||
|
|
||||||
// Allocate a session normally.
|
// Allocate a session normally.
|
||||||
|
|
|
@ -30,7 +30,7 @@ public:
|
||||||
public:
|
public:
|
||||||
explicit KHandleTable(KernelCore& kernel) : m_kernel(kernel) {}
|
explicit KHandleTable(KernelCore& kernel) : m_kernel(kernel) {}
|
||||||
|
|
||||||
Result Initialize(s32 size) {
|
Result Initialize(KProcess* owner, s32 size) {
|
||||||
// Check that the table size is valid.
|
// Check that the table size is valid.
|
||||||
R_UNLESS(size <= static_cast<s32>(MaxTableSize), ResultOutOfMemory);
|
R_UNLESS(size <= static_cast<s32>(MaxTableSize), ResultOutOfMemory);
|
||||||
|
|
||||||
|
@ -44,6 +44,7 @@ public:
|
||||||
m_next_linear_id = MinLinearId;
|
m_next_linear_id = MinLinearId;
|
||||||
m_count = 0;
|
m_count = 0;
|
||||||
m_free_head_index = -1;
|
m_free_head_index = -1;
|
||||||
|
m_owner = owner;
|
||||||
|
|
||||||
// Free all entries.
|
// Free all entries.
|
||||||
for (s32 i = 0; i < static_cast<s32>(m_table_size); ++i) {
|
for (s32 i = 0; i < static_cast<s32>(m_table_size); ++i) {
|
||||||
|
@ -90,8 +91,8 @@ public:
|
||||||
// Handle pseudo-handles.
|
// Handle pseudo-handles.
|
||||||
if constexpr (std::derived_from<KProcess, T>) {
|
if constexpr (std::derived_from<KProcess, T>) {
|
||||||
if (handle == Svc::PseudoHandle::CurrentProcess) {
|
if (handle == Svc::PseudoHandle::CurrentProcess) {
|
||||||
//! FIXME: this is the wrong process!
|
// TODO: this should be the current process
|
||||||
auto* const cur_process = m_kernel.ApplicationProcess();
|
auto* const cur_process = m_owner;
|
||||||
ASSERT(cur_process != nullptr);
|
ASSERT(cur_process != nullptr);
|
||||||
return cur_process;
|
return cur_process;
|
||||||
}
|
}
|
||||||
|
@ -301,6 +302,7 @@ private:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
KernelCore& m_kernel;
|
KernelCore& m_kernel;
|
||||||
|
KProcess* m_owner{};
|
||||||
std::array<EntryInfo, MaxTableSize> m_entry_infos{};
|
std::array<EntryInfo, MaxTableSize> m_entry_infos{};
|
||||||
std::array<KAutoObject*, MaxTableSize> m_objects{};
|
std::array<KAutoObject*, MaxTableSize> m_objects{};
|
||||||
mutable KSpinLock m_lock;
|
mutable KSpinLock m_lock;
|
||||||
|
|
|
@ -306,12 +306,16 @@ Result KProcess::Initialize(const Svc::CreateProcessParameter& params, const KPa
|
||||||
False(params.flags & Svc::CreateProcessFlag::DisableDeviceAddressSpaceMerge);
|
False(params.flags & Svc::CreateProcessFlag::DisableDeviceAddressSpaceMerge);
|
||||||
R_TRY(m_page_table.Initialize(as_type, enable_aslr, enable_das_merge, !enable_aslr, pool,
|
R_TRY(m_page_table.Initialize(as_type, enable_aslr, enable_das_merge, !enable_aslr, pool,
|
||||||
params.code_address, params.code_num_pages * PageSize,
|
params.code_address, params.code_num_pages * PageSize,
|
||||||
m_system_resource, res_limit, this->GetMemory(), 0));
|
m_system_resource, res_limit, m_memory, 0));
|
||||||
}
|
}
|
||||||
ON_RESULT_FAILURE_2 {
|
ON_RESULT_FAILURE_2 {
|
||||||
m_page_table.Finalize();
|
m_page_table.Finalize();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Ensure our memory is initialized.
|
||||||
|
m_memory.SetCurrentPageTable(*this);
|
||||||
|
m_memory.SetGPUDirtyManagers(m_dirty_memory_managers);
|
||||||
|
|
||||||
// Ensure we can insert the code region.
|
// Ensure we can insert the code region.
|
||||||
R_UNLESS(m_page_table.CanContain(params.code_address, params.code_num_pages * PageSize,
|
R_UNLESS(m_page_table.CanContain(params.code_address, params.code_num_pages * PageSize,
|
||||||
KMemoryState::Code),
|
KMemoryState::Code),
|
||||||
|
@ -399,12 +403,16 @@ Result KProcess::Initialize(const Svc::CreateProcessParameter& params,
|
||||||
False(params.flags & Svc::CreateProcessFlag::DisableDeviceAddressSpaceMerge);
|
False(params.flags & Svc::CreateProcessFlag::DisableDeviceAddressSpaceMerge);
|
||||||
R_TRY(m_page_table.Initialize(as_type, enable_aslr, enable_das_merge, !enable_aslr, pool,
|
R_TRY(m_page_table.Initialize(as_type, enable_aslr, enable_das_merge, !enable_aslr, pool,
|
||||||
params.code_address, code_size, m_system_resource, res_limit,
|
params.code_address, code_size, m_system_resource, res_limit,
|
||||||
this->GetMemory(), aslr_space_start));
|
m_memory, aslr_space_start));
|
||||||
}
|
}
|
||||||
ON_RESULT_FAILURE_2 {
|
ON_RESULT_FAILURE_2 {
|
||||||
m_page_table.Finalize();
|
m_page_table.Finalize();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Ensure our memory is initialized.
|
||||||
|
m_memory.SetCurrentPageTable(*this);
|
||||||
|
m_memory.SetGPUDirtyManagers(m_dirty_memory_managers);
|
||||||
|
|
||||||
// Ensure we can insert the code region.
|
// Ensure we can insert the code region.
|
||||||
R_UNLESS(m_page_table.CanContain(params.code_address, code_size, KMemoryState::Code),
|
R_UNLESS(m_page_table.CanContain(params.code_address, code_size, KMemoryState::Code),
|
||||||
ResultInvalidMemoryRegion);
|
ResultInvalidMemoryRegion);
|
||||||
|
@ -1094,8 +1102,7 @@ void KProcess::UnpinThread(KThread* thread) {
|
||||||
|
|
||||||
Result KProcess::GetThreadList(s32* out_num_threads, KProcessAddress out_thread_ids,
|
Result KProcess::GetThreadList(s32* out_num_threads, KProcessAddress out_thread_ids,
|
||||||
s32 max_out_count) {
|
s32 max_out_count) {
|
||||||
// TODO: use current memory reference
|
auto& memory = this->GetMemory();
|
||||||
auto& memory = m_kernel.System().ApplicationMemory();
|
|
||||||
|
|
||||||
// Lock the list.
|
// Lock the list.
|
||||||
KScopedLightLock lk(m_list_lock);
|
KScopedLightLock lk(m_list_lock);
|
||||||
|
@ -1128,15 +1135,15 @@ void KProcess::Switch(KProcess* cur_process, KProcess* next_process) {}
|
||||||
KProcess::KProcess(KernelCore& kernel)
|
KProcess::KProcess(KernelCore& kernel)
|
||||||
: KAutoObjectWithSlabHeapAndContainer(kernel), m_page_table{kernel}, m_state_lock{kernel},
|
: KAutoObjectWithSlabHeapAndContainer(kernel), m_page_table{kernel}, m_state_lock{kernel},
|
||||||
m_list_lock{kernel}, m_cond_var{kernel.System()}, m_address_arbiter{kernel.System()},
|
m_list_lock{kernel}, m_cond_var{kernel.System()}, m_address_arbiter{kernel.System()},
|
||||||
m_handle_table{kernel}, m_dirty_memory_managers{}, m_exclusive_monitor{},
|
m_handle_table{kernel}, m_dirty_memory_managers{},
|
||||||
m_memory{kernel.System()} {}
|
m_exclusive_monitor{}, m_memory{kernel.System()} {}
|
||||||
KProcess::~KProcess() = default;
|
KProcess::~KProcess() = default;
|
||||||
|
|
||||||
Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size,
|
Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size,
|
||||||
KProcessAddress aslr_space_start, bool is_hbl) {
|
KProcessAddress aslr_space_start, bool is_hbl) {
|
||||||
// Create a resource limit for the process.
|
// Create a resource limit for the process.
|
||||||
const auto physical_memory_size =
|
const auto pool = static_cast<KMemoryManager::Pool>(metadata.GetPoolPartition());
|
||||||
m_kernel.MemoryManager().GetSize(Kernel::KMemoryManager::Pool::Application);
|
const auto physical_memory_size = m_kernel.MemoryManager().GetSize(pool);
|
||||||
auto* res_limit =
|
auto* res_limit =
|
||||||
Kernel::CreateResourceLimitForProcess(m_kernel.System(), physical_memory_size);
|
Kernel::CreateResourceLimitForProcess(m_kernel.System(), physical_memory_size);
|
||||||
|
|
||||||
|
@ -1147,8 +1154,10 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
|
||||||
Svc::CreateProcessFlag flag{};
|
Svc::CreateProcessFlag flag{};
|
||||||
u64 code_address{};
|
u64 code_address{};
|
||||||
|
|
||||||
// We are an application.
|
// Determine if we are an application.
|
||||||
|
if (pool == KMemoryManager::Pool::Application) {
|
||||||
flag |= Svc::CreateProcessFlag::IsApplication;
|
flag |= Svc::CreateProcessFlag::IsApplication;
|
||||||
|
}
|
||||||
|
|
||||||
// If we are 64-bit, create as such.
|
// If we are 64-bit, create as such.
|
||||||
if (metadata.Is64BitProgram()) {
|
if (metadata.Is64BitProgram()) {
|
||||||
|
@ -1197,8 +1206,8 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
|
||||||
std::memcpy(params.name.data(), name.data(), sizeof(params.name));
|
std::memcpy(params.name.data(), name.data(), sizeof(params.name));
|
||||||
|
|
||||||
// Initialize for application process.
|
// Initialize for application process.
|
||||||
R_TRY(this->Initialize(params, metadata.GetKernelCapabilities(), res_limit,
|
R_TRY(this->Initialize(params, metadata.GetKernelCapabilities(), res_limit, pool,
|
||||||
KMemoryManager::Pool::Application, aslr_space_start));
|
aslr_space_start));
|
||||||
|
|
||||||
// Assign remaining properties.
|
// Assign remaining properties.
|
||||||
m_is_hbl = is_hbl;
|
m_is_hbl = is_hbl;
|
||||||
|
@ -1239,9 +1248,6 @@ void KProcess::InitializeInterfaces() {
|
||||||
m_exclusive_monitor =
|
m_exclusive_monitor =
|
||||||
Core::MakeExclusiveMonitor(this->GetMemory(), Core::Hardware::NUM_CPU_CORES);
|
Core::MakeExclusiveMonitor(this->GetMemory(), Core::Hardware::NUM_CPU_CORES);
|
||||||
|
|
||||||
this->GetMemory().SetCurrentPageTable(*this);
|
|
||||||
this->GetMemory().SetGPUDirtyManagers(m_dirty_memory_managers);
|
|
||||||
|
|
||||||
#ifdef HAS_NCE
|
#ifdef HAS_NCE
|
||||||
if (this->Is64Bit() && Settings::IsNceEnabled()) {
|
if (this->Is64Bit() && Settings::IsNceEnabled()) {
|
||||||
for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
|
for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
|
||||||
|
|
|
@ -552,7 +552,7 @@ private:
|
||||||
|
|
||||||
Result InitializeHandleTable(s32 size) {
|
Result InitializeHandleTable(s32 size) {
|
||||||
// Try to initialize the handle table.
|
// Try to initialize the handle table.
|
||||||
R_TRY(m_handle_table.Initialize(size));
|
R_TRY(m_handle_table.Initialize(this, size));
|
||||||
|
|
||||||
// We succeeded, so note that we did.
|
// We succeeded, so note that we did.
|
||||||
m_is_handle_table_initialized = true;
|
m_is_handle_table_initialized = true;
|
||||||
|
|
|
@ -33,8 +33,7 @@ void KSession::Initialize(KClientPort* client_port, uintptr_t name) {
|
||||||
m_name = name;
|
m_name = name;
|
||||||
|
|
||||||
// Set our owner process.
|
// Set our owner process.
|
||||||
//! FIXME: this is the wrong process!
|
m_process = GetCurrentProcessPointer(m_kernel);
|
||||||
m_process = m_kernel.ApplicationProcess();
|
|
||||||
m_process->Open();
|
m_process->Open();
|
||||||
|
|
||||||
// Set our port.
|
// Set our port.
|
||||||
|
|
|
@ -1422,8 +1422,7 @@ s32 GetCurrentCoreId(KernelCore& kernel) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Core::Memory::Memory& GetCurrentMemory(KernelCore& kernel) {
|
Core::Memory::Memory& GetCurrentMemory(KernelCore& kernel) {
|
||||||
// TODO: per-process memory
|
return GetCurrentProcess(kernel).GetMemory();
|
||||||
return kernel.System().ApplicationMemory();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
KScopedDisableDispatch::~KScopedDisableDispatch() {
|
KScopedDisableDispatch::~KScopedDisableDispatch() {
|
||||||
|
|
|
@ -68,8 +68,6 @@ struct KernelCore::Impl {
|
||||||
|
|
||||||
global_object_list_container = std::make_unique<KAutoObjectWithListContainer>(kernel);
|
global_object_list_container = std::make_unique<KAutoObjectWithListContainer>(kernel);
|
||||||
global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel);
|
global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel);
|
||||||
global_handle_table = std::make_unique<Kernel::KHandleTable>(kernel);
|
|
||||||
global_handle_table->Initialize(KHandleTable::MaxTableSize);
|
|
||||||
|
|
||||||
is_phantom_mode_for_singlecore = false;
|
is_phantom_mode_for_singlecore = false;
|
||||||
|
|
||||||
|
@ -121,9 +119,6 @@ struct KernelCore::Impl {
|
||||||
next_user_process_id = KProcess::ProcessIdMin;
|
next_user_process_id = KProcess::ProcessIdMin;
|
||||||
next_thread_id = 1;
|
next_thread_id = 1;
|
||||||
|
|
||||||
global_handle_table->Finalize();
|
|
||||||
global_handle_table.reset();
|
|
||||||
|
|
||||||
preemption_event = nullptr;
|
preemption_event = nullptr;
|
||||||
|
|
||||||
// Cleanup persistent kernel objects
|
// Cleanup persistent kernel objects
|
||||||
|
@ -787,10 +782,6 @@ struct KernelCore::Impl {
|
||||||
|
|
||||||
std::shared_ptr<Core::Timing::EventType> preemption_event;
|
std::shared_ptr<Core::Timing::EventType> preemption_event;
|
||||||
|
|
||||||
// This is the kernel's handle table or supervisor handle table which
|
|
||||||
// stores all the objects in place.
|
|
||||||
std::unique_ptr<KHandleTable> global_handle_table;
|
|
||||||
|
|
||||||
std::unique_ptr<KAutoObjectWithListContainer> global_object_list_container;
|
std::unique_ptr<KAutoObjectWithListContainer> global_object_list_container;
|
||||||
|
|
||||||
std::unique_ptr<KObjectNameGlobalData> object_name_global_data;
|
std::unique_ptr<KObjectNameGlobalData> object_name_global_data;
|
||||||
|
@ -877,10 +868,6 @@ KResourceLimit* KernelCore::GetSystemResourceLimit() {
|
||||||
return impl->system_resource_limit;
|
return impl->system_resource_limit;
|
||||||
}
|
}
|
||||||
|
|
||||||
KScopedAutoObject<KThread> KernelCore::RetrieveThreadFromGlobalHandleTable(Handle handle) const {
|
|
||||||
return impl->global_handle_table->GetObject<KThread>(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
void KernelCore::AppendNewProcess(KProcess* process) {
|
void KernelCore::AppendNewProcess(KProcess* process) {
|
||||||
impl->process_list.push_back(process);
|
impl->process_list.push_back(process);
|
||||||
}
|
}
|
||||||
|
@ -1017,14 +1004,6 @@ u64 KernelCore::CreateNewUserProcessID() {
|
||||||
return impl->next_user_process_id++;
|
return impl->next_user_process_id++;
|
||||||
}
|
}
|
||||||
|
|
||||||
KHandleTable& KernelCore::GlobalHandleTable() {
|
|
||||||
return *impl->global_handle_table;
|
|
||||||
}
|
|
||||||
|
|
||||||
const KHandleTable& KernelCore::GlobalHandleTable() const {
|
|
||||||
return *impl->global_handle_table;
|
|
||||||
}
|
|
||||||
|
|
||||||
void KernelCore::RegisterCoreThread(std::size_t core_id) {
|
void KernelCore::RegisterCoreThread(std::size_t core_id) {
|
||||||
impl->RegisterCoreThread(core_id);
|
impl->RegisterCoreThread(core_id);
|
||||||
}
|
}
|
||||||
|
|
|
@ -116,9 +116,6 @@ public:
|
||||||
/// Retrieves a shared pointer to the system resource limit instance.
|
/// Retrieves a shared pointer to the system resource limit instance.
|
||||||
KResourceLimit* GetSystemResourceLimit();
|
KResourceLimit* GetSystemResourceLimit();
|
||||||
|
|
||||||
/// Retrieves a shared pointer to a Thread instance within the thread wakeup handle table.
|
|
||||||
KScopedAutoObject<KThread> RetrieveThreadFromGlobalHandleTable(Handle handle) const;
|
|
||||||
|
|
||||||
/// Adds the given shared pointer to an internal list of active processes.
|
/// Adds the given shared pointer to an internal list of active processes.
|
||||||
void AppendNewProcess(KProcess* process);
|
void AppendNewProcess(KProcess* process);
|
||||||
|
|
||||||
|
|
|
@ -89,7 +89,7 @@ static void GenerateErrorReport(Core::System& system, Result error_code, const F
|
||||||
crash_report += fmt::format(" ESR: {:016x}\n", info.esr);
|
crash_report += fmt::format(" ESR: {:016x}\n", info.esr);
|
||||||
crash_report += fmt::format(" FAR: {:016x}\n", info.far);
|
crash_report += fmt::format(" FAR: {:016x}\n", info.far);
|
||||||
crash_report += "\nBacktrace:\n";
|
crash_report += "\nBacktrace:\n";
|
||||||
for (size_t i = 0; i < info.backtrace_size; i++) {
|
for (u32 i = 0; i < std::min<u32>(info.backtrace_size, 32); i++) {
|
||||||
crash_report +=
|
crash_report +=
|
||||||
fmt::format(" Backtrace[{:02d}]: {:016x}\n", i, info.backtrace[i]);
|
fmt::format(" Backtrace[{:02d}]: {:016x}\n", i, info.backtrace[i]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@ ServerManager::~ServerManager() {
|
||||||
m_stopped.Wait();
|
m_stopped.Wait();
|
||||||
m_threads.clear();
|
m_threads.clear();
|
||||||
|
|
||||||
// Clean up ports.
|
// Clean up server ports.
|
||||||
for (const auto& [port, handler] : m_ports) {
|
for (const auto& [port, handler] : m_ports) {
|
||||||
port->Close();
|
port->Close();
|
||||||
}
|
}
|
||||||
|
@ -97,22 +97,15 @@ Result ServerManager::RegisterNamedService(const std::string& service_name,
|
||||||
u32 max_sessions) {
|
u32 max_sessions) {
|
||||||
ASSERT(m_sessions.size() + m_ports.size() < MaximumWaitObjects);
|
ASSERT(m_sessions.size() + m_ports.size() < MaximumWaitObjects);
|
||||||
|
|
||||||
// Add the new server to sm:.
|
// Add the new server to sm: and get the moved server port.
|
||||||
ASSERT(R_SUCCEEDED(
|
Kernel::KServerPort* server_port{};
|
||||||
m_system.ServiceManager().RegisterService(service_name, max_sessions, handler_factory)));
|
R_ASSERT(m_system.ServiceManager().RegisterService(std::addressof(server_port), service_name,
|
||||||
|
max_sessions, handler_factory));
|
||||||
// Get the registered port.
|
|
||||||
Kernel::KPort* port{};
|
|
||||||
ASSERT(
|
|
||||||
R_SUCCEEDED(m_system.ServiceManager().GetServicePort(std::addressof(port), service_name)));
|
|
||||||
|
|
||||||
// Open a new reference to the server port.
|
|
||||||
port->GetServerPort().Open();
|
|
||||||
|
|
||||||
// Begin tracking the server port.
|
// Begin tracking the server port.
|
||||||
{
|
{
|
||||||
std::scoped_lock ll{m_list_mutex};
|
std::scoped_lock ll{m_list_mutex};
|
||||||
m_ports.emplace(std::addressof(port->GetServerPort()), std::move(handler_factory));
|
m_ports.emplace(server_port, std::move(handler_factory));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Signal the wakeup event.
|
// Signal the wakeup event.
|
||||||
|
|
|
@ -507,6 +507,14 @@ void SET_SYS::SetTvSettings(HLERequestContext& ctx) {
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SET_SYS::GetDebugModeFlag(HLERequestContext& ctx) {
|
||||||
|
LOG_DEBUG(Service_SET, "called");
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 3};
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
|
rb.Push<u32>(0);
|
||||||
|
}
|
||||||
|
|
||||||
void SET_SYS::GetQuestFlag(HLERequestContext& ctx) {
|
void SET_SYS::GetQuestFlag(HLERequestContext& ctx) {
|
||||||
LOG_WARNING(Service_SET, "(STUBBED) called");
|
LOG_WARNING(Service_SET, "(STUBBED) called");
|
||||||
|
|
||||||
|
@ -926,7 +934,7 @@ SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"},
|
||||||
{59, &SET_SYS::SetNetworkSystemClockContext, "SetNetworkSystemClockContext"},
|
{59, &SET_SYS::SetNetworkSystemClockContext, "SetNetworkSystemClockContext"},
|
||||||
{60, &SET_SYS::IsUserSystemClockAutomaticCorrectionEnabled, "IsUserSystemClockAutomaticCorrectionEnabled"},
|
{60, &SET_SYS::IsUserSystemClockAutomaticCorrectionEnabled, "IsUserSystemClockAutomaticCorrectionEnabled"},
|
||||||
{61, &SET_SYS::SetUserSystemClockAutomaticCorrectionEnabled, "SetUserSystemClockAutomaticCorrectionEnabled"},
|
{61, &SET_SYS::SetUserSystemClockAutomaticCorrectionEnabled, "SetUserSystemClockAutomaticCorrectionEnabled"},
|
||||||
{62, nullptr, "GetDebugModeFlag"},
|
{62, &SET_SYS::GetDebugModeFlag, "GetDebugModeFlag"},
|
||||||
{63, &SET_SYS::GetPrimaryAlbumStorage, "GetPrimaryAlbumStorage"},
|
{63, &SET_SYS::GetPrimaryAlbumStorage, "GetPrimaryAlbumStorage"},
|
||||||
{64, nullptr, "SetPrimaryAlbumStorage"},
|
{64, nullptr, "SetPrimaryAlbumStorage"},
|
||||||
{65, nullptr, "GetUsb30EnableFlag"},
|
{65, nullptr, "GetUsb30EnableFlag"},
|
||||||
|
@ -1143,6 +1151,8 @@ void SET_SYS::StoreSettings() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SET_SYS::StoreSettingsThreadFunc(std::stop_token stop_token) {
|
void SET_SYS::StoreSettingsThreadFunc(std::stop_token stop_token) {
|
||||||
|
Common::SetCurrentThreadName("SettingsStore");
|
||||||
|
|
||||||
while (Common::StoppableTimedWait(stop_token, std::chrono::minutes(1))) {
|
while (Common::StoppableTimedWait(stop_token, std::chrono::minutes(1))) {
|
||||||
std::scoped_lock l{m_save_needed_mutex};
|
std::scoped_lock l{m_save_needed_mutex};
|
||||||
if (!std::exchange(m_save_needed, false)) {
|
if (!std::exchange(m_save_needed, false)) {
|
||||||
|
|
|
@ -98,6 +98,7 @@ private:
|
||||||
void GetSettingsItemValue(HLERequestContext& ctx);
|
void GetSettingsItemValue(HLERequestContext& ctx);
|
||||||
void GetTvSettings(HLERequestContext& ctx);
|
void GetTvSettings(HLERequestContext& ctx);
|
||||||
void SetTvSettings(HLERequestContext& ctx);
|
void SetTvSettings(HLERequestContext& ctx);
|
||||||
|
void GetDebugModeFlag(HLERequestContext& ctx);
|
||||||
void GetQuestFlag(HLERequestContext& ctx);
|
void GetQuestFlag(HLERequestContext& ctx);
|
||||||
void GetDeviceTimeZoneLocationName(HLERequestContext& ctx);
|
void GetDeviceTimeZoneLocationName(HLERequestContext& ctx);
|
||||||
void SetDeviceTimeZoneLocationName(HLERequestContext& ctx);
|
void SetDeviceTimeZoneLocationName(HLERequestContext& ctx);
|
||||||
|
|
|
@ -29,8 +29,7 @@ ServiceManager::ServiceManager(Kernel::KernelCore& kernel_) : kernel{kernel_} {
|
||||||
|
|
||||||
ServiceManager::~ServiceManager() {
|
ServiceManager::~ServiceManager() {
|
||||||
for (auto& [name, port] : service_ports) {
|
for (auto& [name, port] : service_ports) {
|
||||||
port->GetClientPort().Close();
|
port->Close();
|
||||||
port->GetServerPort().Close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (deferral_event) {
|
if (deferral_event) {
|
||||||
|
@ -50,8 +49,8 @@ static Result ValidateServiceName(const std::string& name) {
|
||||||
return ResultSuccess;
|
return ResultSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result ServiceManager::RegisterService(std::string name, u32 max_sessions,
|
Result ServiceManager::RegisterService(Kernel::KServerPort** out_server_port, std::string name,
|
||||||
SessionRequestHandlerFactory handler) {
|
u32 max_sessions, SessionRequestHandlerFactory handler) {
|
||||||
R_TRY(ValidateServiceName(name));
|
R_TRY(ValidateServiceName(name));
|
||||||
|
|
||||||
std::scoped_lock lk{lock};
|
std::scoped_lock lk{lock};
|
||||||
|
@ -66,13 +65,17 @@ Result ServiceManager::RegisterService(std::string name, u32 max_sessions,
|
||||||
// Register the port.
|
// Register the port.
|
||||||
Kernel::KPort::Register(kernel, port);
|
Kernel::KPort::Register(kernel, port);
|
||||||
|
|
||||||
service_ports.emplace(name, port);
|
service_ports.emplace(name, std::addressof(port->GetClientPort()));
|
||||||
registered_services.emplace(name, handler);
|
registered_services.emplace(name, handler);
|
||||||
if (deferral_event) {
|
if (deferral_event) {
|
||||||
deferral_event->Signal();
|
deferral_event->Signal();
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResultSuccess;
|
// Set our output.
|
||||||
|
*out_server_port = std::addressof(port->GetServerPort());
|
||||||
|
|
||||||
|
// We succeeded.
|
||||||
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result ServiceManager::UnregisterService(const std::string& name) {
|
Result ServiceManager::UnregisterService(const std::string& name) {
|
||||||
|
@ -91,7 +94,8 @@ Result ServiceManager::UnregisterService(const std::string& name) {
|
||||||
return ResultSuccess;
|
return ResultSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result ServiceManager::GetServicePort(Kernel::KPort** out_port, const std::string& name) {
|
Result ServiceManager::GetServicePort(Kernel::KClientPort** out_client_port,
|
||||||
|
const std::string& name) {
|
||||||
R_TRY(ValidateServiceName(name));
|
R_TRY(ValidateServiceName(name));
|
||||||
|
|
||||||
std::scoped_lock lk{lock};
|
std::scoped_lock lk{lock};
|
||||||
|
@ -101,7 +105,7 @@ Result ServiceManager::GetServicePort(Kernel::KPort** out_port, const std::strin
|
||||||
return Service::SM::ResultNotRegistered;
|
return Service::SM::ResultNotRegistered;
|
||||||
}
|
}
|
||||||
|
|
||||||
*out_port = it->second;
|
*out_client_port = it->second;
|
||||||
return ResultSuccess;
|
return ResultSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,8 +176,8 @@ Result SM::GetServiceImpl(Kernel::KClientSession** out_client_session, HLEReques
|
||||||
std::string name(PopServiceName(rp));
|
std::string name(PopServiceName(rp));
|
||||||
|
|
||||||
// Find the named port.
|
// Find the named port.
|
||||||
Kernel::KPort* port{};
|
Kernel::KClientPort* client_port{};
|
||||||
auto port_result = service_manager.GetServicePort(&port, name);
|
auto port_result = service_manager.GetServicePort(&client_port, name);
|
||||||
if (port_result == Service::SM::ResultInvalidServiceName) {
|
if (port_result == Service::SM::ResultInvalidServiceName) {
|
||||||
LOG_ERROR(Service_SM, "Invalid service name '{}'", name);
|
LOG_ERROR(Service_SM, "Invalid service name '{}'", name);
|
||||||
return Service::SM::ResultInvalidServiceName;
|
return Service::SM::ResultInvalidServiceName;
|
||||||
|
@ -187,7 +191,7 @@ Result SM::GetServiceImpl(Kernel::KClientSession** out_client_session, HLEReques
|
||||||
|
|
||||||
// Create a new session.
|
// Create a new session.
|
||||||
Kernel::KClientSession* session{};
|
Kernel::KClientSession* session{};
|
||||||
if (const auto result = port->GetClientPort().CreateSession(&session); result.IsError()) {
|
if (const auto result = client_port->CreateSession(&session); result.IsError()) {
|
||||||
LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, result.raw);
|
LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, result.raw);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -221,7 +225,9 @@ void SM::RegisterServiceImpl(HLERequestContext& ctx, std::string name, u32 max_s
|
||||||
LOG_DEBUG(Service_SM, "called with name={}, max_session_count={}, is_light={}", name,
|
LOG_DEBUG(Service_SM, "called with name={}, max_session_count={}, is_light={}", name,
|
||||||
max_session_count, is_light);
|
max_session_count, is_light);
|
||||||
|
|
||||||
if (const auto result = service_manager.RegisterService(name, max_session_count, nullptr);
|
Kernel::KServerPort* server_port{};
|
||||||
|
if (const auto result = service_manager.RegisterService(std::addressof(server_port), name,
|
||||||
|
max_session_count, nullptr);
|
||||||
result.IsError()) {
|
result.IsError()) {
|
||||||
LOG_ERROR(Service_SM, "failed to register service with error_code={:08X}", result.raw);
|
LOG_ERROR(Service_SM, "failed to register service with error_code={:08X}", result.raw);
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
@ -229,13 +235,9 @@ void SM::RegisterServiceImpl(HLERequestContext& ctx, std::string name, u32 max_s
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* port = Kernel::KPort::Create(kernel);
|
|
||||||
port->Initialize(ServerSessionCountMax, is_light, 0);
|
|
||||||
SCOPE_EXIT({ port->GetClientPort().Close(); });
|
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
|
IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
rb.PushMoveObjects(port->GetServerPort());
|
rb.PushMoveObjects(server_port);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SM::UnregisterService(HLERequestContext& ctx) {
|
void SM::UnregisterService(HLERequestContext& ctx) {
|
||||||
|
|
|
@ -56,10 +56,10 @@ public:
|
||||||
explicit ServiceManager(Kernel::KernelCore& kernel_);
|
explicit ServiceManager(Kernel::KernelCore& kernel_);
|
||||||
~ServiceManager();
|
~ServiceManager();
|
||||||
|
|
||||||
Result RegisterService(std::string name, u32 max_sessions,
|
Result RegisterService(Kernel::KServerPort** out_server_port, std::string name,
|
||||||
SessionRequestHandlerFactory handler_factory);
|
u32 max_sessions, SessionRequestHandlerFactory handler_factory);
|
||||||
Result UnregisterService(const std::string& name);
|
Result UnregisterService(const std::string& name);
|
||||||
Result GetServicePort(Kernel::KPort** out_port, const std::string& name);
|
Result GetServicePort(Kernel::KClientPort** out_client_port, const std::string& name);
|
||||||
|
|
||||||
template <Common::DerivedFrom<SessionRequestHandler> T>
|
template <Common::DerivedFrom<SessionRequestHandler> T>
|
||||||
std::shared_ptr<T> GetService(const std::string& service_name) const {
|
std::shared_ptr<T> GetService(const std::string& service_name) const {
|
||||||
|
@ -84,7 +84,7 @@ private:
|
||||||
/// Map of registered services, retrieved using GetServicePort.
|
/// Map of registered services, retrieved using GetServicePort.
|
||||||
std::mutex lock;
|
std::mutex lock;
|
||||||
std::unordered_map<std::string, SessionRequestHandlerFactory> registered_services;
|
std::unordered_map<std::string, SessionRequestHandlerFactory> registered_services;
|
||||||
std::unordered_map<std::string, Kernel::KPort*> service_ports;
|
std::unordered_map<std::string, Kernel::KClientPort*> service_ports;
|
||||||
|
|
||||||
/// Kernel context
|
/// Kernel context
|
||||||
Kernel::KernelCore& kernel;
|
Kernel::KernelCore& kernel;
|
||||||
|
|
|
@ -45,7 +45,13 @@ struct Memory::Impl {
|
||||||
|
|
||||||
void SetCurrentPageTable(Kernel::KProcess& process) {
|
void SetCurrentPageTable(Kernel::KProcess& process) {
|
||||||
current_page_table = &process.GetPageTable().GetImpl();
|
current_page_table = &process.GetPageTable().GetImpl();
|
||||||
|
|
||||||
|
if (std::addressof(process) == system.ApplicationProcess() &&
|
||||||
|
Settings::IsFastmemEnabled()) {
|
||||||
current_page_table->fastmem_arena = system.DeviceMemory().buffer.VirtualBasePointer();
|
current_page_table->fastmem_arena = system.DeviceMemory().buffer.VirtualBasePointer();
|
||||||
|
} else {
|
||||||
|
current_page_table->fastmem_arena = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MapMemoryRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size,
|
void MapMemoryRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size,
|
||||||
|
@ -57,7 +63,7 @@ struct Memory::Impl {
|
||||||
MapPages(page_table, base / YUZU_PAGESIZE, size / YUZU_PAGESIZE, target,
|
MapPages(page_table, base / YUZU_PAGESIZE, size / YUZU_PAGESIZE, target,
|
||||||
Common::PageType::Memory);
|
Common::PageType::Memory);
|
||||||
|
|
||||||
if (Settings::IsFastmemEnabled()) {
|
if (current_page_table->fastmem_arena) {
|
||||||
system.DeviceMemory().buffer.Map(GetInteger(base),
|
system.DeviceMemory().buffer.Map(GetInteger(base),
|
||||||
GetInteger(target) - DramMemoryMap::Base, size, perms);
|
GetInteger(target) - DramMemoryMap::Base, size, perms);
|
||||||
}
|
}
|
||||||
|
@ -69,7 +75,7 @@ struct Memory::Impl {
|
||||||
MapPages(page_table, base / YUZU_PAGESIZE, size / YUZU_PAGESIZE, 0,
|
MapPages(page_table, base / YUZU_PAGESIZE, size / YUZU_PAGESIZE, 0,
|
||||||
Common::PageType::Unmapped);
|
Common::PageType::Unmapped);
|
||||||
|
|
||||||
if (Settings::IsFastmemEnabled()) {
|
if (current_page_table->fastmem_arena) {
|
||||||
system.DeviceMemory().buffer.Unmap(GetInteger(base), size);
|
system.DeviceMemory().buffer.Unmap(GetInteger(base), size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,7 +85,7 @@ struct Memory::Impl {
|
||||||
ASSERT_MSG((size & YUZU_PAGEMASK) == 0, "non-page aligned size: {:016X}", size);
|
ASSERT_MSG((size & YUZU_PAGEMASK) == 0, "non-page aligned size: {:016X}", size);
|
||||||
ASSERT_MSG((vaddr & YUZU_PAGEMASK) == 0, "non-page aligned base: {:016X}", vaddr);
|
ASSERT_MSG((vaddr & YUZU_PAGEMASK) == 0, "non-page aligned base: {:016X}", vaddr);
|
||||||
|
|
||||||
if (!Settings::IsFastmemEnabled()) {
|
if (!current_page_table->fastmem_arena) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,11 +94,6 @@ struct Memory::Impl {
|
||||||
const bool is_x =
|
const bool is_x =
|
||||||
True(perms & Common::MemoryPermission::Execute) && Settings::IsNceEnabled();
|
True(perms & Common::MemoryPermission::Execute) && Settings::IsNceEnabled();
|
||||||
|
|
||||||
if (!current_page_table) {
|
|
||||||
system.DeviceMemory().buffer.Protect(vaddr, size, is_r, is_w, is_x);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 protect_bytes{};
|
u64 protect_bytes{};
|
||||||
u64 protect_begin{};
|
u64 protect_begin{};
|
||||||
for (u64 addr = vaddr; addr < vaddr + size; addr += YUZU_PAGESIZE) {
|
for (u64 addr = vaddr; addr < vaddr + size; addr += YUZU_PAGESIZE) {
|
||||||
|
@ -239,7 +240,7 @@ struct Memory::Impl {
|
||||||
|
|
||||||
bool WalkBlock(const Common::ProcessAddress addr, const std::size_t size, auto on_unmapped,
|
bool WalkBlock(const Common::ProcessAddress addr, const std::size_t size, auto on_unmapped,
|
||||||
auto on_memory, auto on_rasterizer, auto increment) {
|
auto on_memory, auto on_rasterizer, auto increment) {
|
||||||
const auto& page_table = system.ApplicationProcess()->GetPageTable().GetImpl();
|
const auto& page_table = *current_page_table;
|
||||||
std::size_t remaining_size = size;
|
std::size_t remaining_size = size;
|
||||||
std::size_t page_index = addr >> YUZU_PAGEBITS;
|
std::size_t page_index = addr >> YUZU_PAGEBITS;
|
||||||
std::size_t page_offset = addr & YUZU_PAGEMASK;
|
std::size_t page_offset = addr & YUZU_PAGEMASK;
|
||||||
|
@ -484,7 +485,7 @@ struct Memory::Impl {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Settings::IsFastmemEnabled()) {
|
if (current_page_table->fastmem_arena) {
|
||||||
system.DeviceMemory().buffer.Protect(vaddr, size, !debug, !debug);
|
system.DeviceMemory().buffer.Protect(vaddr, size, !debug, !debug);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -541,7 +542,7 @@ struct Memory::Impl {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Settings::IsFastmemEnabled()) {
|
if (current_page_table->fastmem_arena) {
|
||||||
const bool is_read_enable =
|
const bool is_read_enable =
|
||||||
!Settings::values.use_reactive_flushing.GetValue() || !cached;
|
!Settings::values.use_reactive_flushing.GetValue() || !cached;
|
||||||
system.DeviceMemory().buffer.Protect(vaddr, size, is_read_enable, !cached);
|
system.DeviceMemory().buffer.Protect(vaddr, size, is_read_enable, !cached);
|
||||||
|
@ -886,8 +887,7 @@ void Memory::ProtectRegion(Common::PageTable& page_table, Common::ProcessAddress
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Memory::IsValidVirtualAddress(const Common::ProcessAddress vaddr) const {
|
bool Memory::IsValidVirtualAddress(const Common::ProcessAddress vaddr) const {
|
||||||
const Kernel::KProcess& process = *system.ApplicationProcess();
|
const auto& page_table = *impl->current_page_table;
|
||||||
const auto& page_table = process.GetPageTable().GetImpl();
|
|
||||||
const size_t page = vaddr >> YUZU_PAGEBITS;
|
const size_t page = vaddr >> YUZU_PAGEBITS;
|
||||||
if (page >= page_table.pointers.size()) {
|
if (page >= page_table.pointers.size()) {
|
||||||
return false;
|
return false;
|
||||||
|
|
Reference in New Issue