diff --git a/README.md b/README.md index 63fe20fed..114ee7e51 100755 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ yuzu emulator early access ============= -This is the source code for early-access 1419. +This is the source code for early-access 1420. ## Legal Notice diff --git a/src/common/ring_buffer.h b/src/common/ring_buffer.h index 138fa0131..4a8d09806 100755 --- a/src/common/ring_buffer.h +++ b/src/common/ring_buffer.h @@ -19,15 +19,14 @@ namespace Common { /// SPSC ring buffer /// @tparam T Element type /// @tparam capacity Number of slots in ring buffer -/// @tparam granularity Slot size in terms of number of elements -template +template class RingBuffer { - /// A "slot" is made of `granularity` elements of `T`. - static constexpr std::size_t slot_size = granularity * sizeof(T); + /// A "slot" is made of a single `T`. + static constexpr std::size_t slot_size = sizeof(T); // T must be safely memcpy-able and have a trivial default constructor. static_assert(std::is_trivial_v); // Ensure capacity is sensible. - static_assert(capacity < std::numeric_limits::max() / 2 / granularity); + static_assert(capacity < std::numeric_limits::max() / 2); static_assert((capacity & (capacity - 1)) == 0, "capacity must be a power of two"); // Ensure lock-free. static_assert(std::atomic_size_t::is_always_lock_free); @@ -47,7 +46,7 @@ public: const std::size_t second_copy = push_count - first_copy; const char* in = static_cast(new_slots); - std::memcpy(m_data.data() + pos * granularity, in, first_copy * slot_size); + std::memcpy(m_data.data() + pos, in, first_copy * slot_size); in += first_copy * slot_size; std::memcpy(m_data.data(), in, second_copy * slot_size); @@ -74,7 +73,7 @@ public: const std::size_t second_copy = pop_count - first_copy; char* out = static_cast(output); - std::memcpy(out, m_data.data() + pos * granularity, first_copy * slot_size); + std::memcpy(out, m_data.data() + pos, first_copy * slot_size); out += first_copy * slot_size; std::memcpy(out, m_data.data(), second_copy * slot_size); @@ -84,9 +83,9 @@ public: } std::vector Pop(std::size_t max_slots = ~std::size_t(0)) { - std::vector out(std::min(max_slots, capacity) * granularity); - const std::size_t count = Pop(out.data(), out.size() / granularity); - out.resize(count * granularity); + std::vector out(std::min(max_slots, capacity)); + const std::size_t count = Pop(out.data(), out.size()); + out.resize(count); return out; } @@ -113,7 +112,7 @@ private: alignas(128) std::atomic_size_t m_write_index{0}; #endif - std::array m_data; + std::array m_data; }; } // namespace Common diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 1ec75f915..3b5c79f9a 100755 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -173,6 +173,7 @@ add_library(core STATIC hle/kernel/k_scheduler.h hle/kernel/k_scheduler_lock.h hle/kernel/k_scoped_lock.h + hle/kernel/k_scoped_resource_reservation.h hle/kernel/k_scoped_scheduler_lock_and_sleep.h hle/kernel/k_synchronization_object.cpp hle/kernel/k_synchronization_object.h diff --git a/src/core/hle/kernel/k_address_arbiter.cpp b/src/core/hle/kernel/k_address_arbiter.cpp index f2f497dc4..d0e90fd60 100755 --- a/src/core/hle/kernel/k_address_arbiter.cpp +++ b/src/core/hle/kernel/k_address_arbiter.cpp @@ -118,9 +118,10 @@ ResultCode KAddressArbiter::SignalAndIncrementIfEqual(VAddr addr, s32 value, s32 // Check the userspace value. s32 user_value{}; - R_UNLESS(UpdateIfEqual(system, std::addressof(user_value), addr, value, value + 1), - Svc::ResultInvalidCurrentMemory); - + if (!UpdateIfEqual(system, &user_value, addr, value, value + 1)) { + LOG_ERROR(Kernel, "Invalid current memory!"); + return Svc::ResultInvalidCurrentMemory; + } if (user_value != value) { return Svc::ResultInvalidState; } @@ -146,61 +147,34 @@ ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32 // Perform signaling. s32 num_waiters{}; { - KScopedSchedulerLock sl(kernel); + [[maybe_unused]] const KScopedSchedulerLock sl(kernel); auto it = thread_tree.nfind_light({addr, -1}); // Determine the updated value. s32 new_value{}; - if (/*GetTargetFirmware() >= TargetFirmware_7_0_0*/ true) { - if (count <= 0) { - if ((it != thread_tree.end()) && (it->GetAddressArbiterKey() == addr)) { - new_value = value - 2; - } else { - new_value = value + 1; - } + if (count <= 0) { + if (it != thread_tree.end() && it->GetAddressArbiterKey() == addr) { + new_value = value - 2; } else { - if ((it != thread_tree.end()) && (it->GetAddressArbiterKey() == addr)) { - auto tmp_it = it; - s32 tmp_num_waiters{}; - while ((++tmp_it != thread_tree.end()) && - (tmp_it->GetAddressArbiterKey() == addr)) { - if ((tmp_num_waiters++) >= count) { - break; - } - } - - if (tmp_num_waiters < count) { - new_value = value - 1; - } else { - new_value = value; - } - } else { - new_value = value + 1; - } + new_value = value + 1; } } else { - if (count <= 0) { - if ((it != thread_tree.end()) && (it->GetAddressArbiterKey() == addr)) { - new_value = value - 1; - } else { - new_value = value + 1; - } - } else { + if (it != thread_tree.end() && it->GetAddressArbiterKey() == addr) { auto tmp_it = it; s32 tmp_num_waiters{}; - while ((tmp_it != thread_tree.end()) && (tmp_it->GetAddressArbiterKey() == addr) && - (tmp_num_waiters < count + 1)) { - ++tmp_num_waiters; - ++tmp_it; + while (++tmp_it != thread_tree.end() && tmp_it->GetAddressArbiterKey() == addr) { + if (tmp_num_waiters++ >= count) { + break; + } } - if (tmp_num_waiters == 0) { - new_value = value + 1; - } else if (tmp_num_waiters <= count) { + if (tmp_num_waiters < count) { new_value = value - 1; } else { new_value = value; } + } else { + new_value = value + 1; } } @@ -208,13 +182,15 @@ ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32 s32 user_value{}; bool succeeded{}; if (value != new_value) { - succeeded = UpdateIfEqual(system, std::addressof(user_value), addr, value, new_value); + succeeded = UpdateIfEqual(system, &user_value, addr, value, new_value); } else { - succeeded = ReadFromUser(system, std::addressof(user_value), addr); + succeeded = ReadFromUser(system, &user_value, addr); } - R_UNLESS(succeeded, Svc::ResultInvalidCurrentMemory); - + if (!succeeded) { + LOG_ERROR(Kernel, "Invalid current memory!"); + return Svc::ResultInvalidCurrentMemory; + } if (user_value != value) { return Svc::ResultInvalidState; } @@ -255,9 +231,9 @@ ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement s32 user_value{}; bool succeeded{}; if (decrement) { - succeeded = DecrementIfLessThan(system, std::addressof(user_value), addr, value); + succeeded = DecrementIfLessThan(system, &user_value, addr, value); } else { - succeeded = ReadFromUser(system, std::addressof(user_value), addr); + succeeded = ReadFromUser(system, &user_value, addr); } if (!succeeded) { @@ -278,7 +254,7 @@ ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement } // Set the arbiter. - cur_thread->SetAddressArbiter(std::addressof(thread_tree), addr); + cur_thread->SetAddressArbiter(&thread_tree, addr); thread_tree.insert(*cur_thread); cur_thread->SetState(ThreadState::Waiting); cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Arbitration); @@ -299,7 +275,7 @@ ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement // Get the result. KSynchronizationObject* dummy{}; - return cur_thread->GetWaitResult(std::addressof(dummy)); + return cur_thread->GetWaitResult(&dummy); } ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) { @@ -320,7 +296,7 @@ ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) { // Read the value from userspace. s32 user_value{}; - if (!ReadFromUser(system, std::addressof(user_value), addr)) { + if (!ReadFromUser(system, &user_value, addr)) { slp.CancelSleep(); return Svc::ResultInvalidCurrentMemory; } @@ -338,7 +314,7 @@ ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) { } // Set the arbiter. - cur_thread->SetAddressArbiter(std::addressof(thread_tree), addr); + cur_thread->SetAddressArbiter(&thread_tree, addr); thread_tree.insert(*cur_thread); cur_thread->SetState(ThreadState::Waiting); cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Arbitration); @@ -359,7 +335,7 @@ ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) { // Get the result. KSynchronizationObject* dummy{}; - return cur_thread->GetWaitResult(std::addressof(dummy)); + return cur_thread->GetWaitResult(&dummy); } } // namespace Kernel diff --git a/src/core/hle/kernel/k_scoped_resource_reservation.h b/src/core/hle/kernel/k_scoped_resource_reservation.h new file mode 100755 index 000000000..c5deca00b --- /dev/null +++ b/src/core/hle/kernel/k_scoped_resource_reservation.h @@ -0,0 +1,67 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +// This file references various implementation details from Atmosphere, an open-source firmware for +// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX. + +#pragma once + +#include "common/common_types.h" +#include "core/hle/kernel/k_resource_limit.h" +#include "core/hle/kernel/process.h" + +namespace Kernel { + +class KScopedResourceReservation { +public: + explicit KScopedResourceReservation(std::shared_ptr l, LimitableResource r, + s64 v, s64 timeout) + : resource_limit(std::move(l)), value(v), resource(r) { + if (resource_limit && value) { + success = resource_limit->Reserve(resource, value, timeout); + } else { + success = true; + } + } + + explicit KScopedResourceReservation(std::shared_ptr l, LimitableResource r, + s64 v = 1) + : resource_limit(std::move(l)), value(v), resource(r) { + if (resource_limit && value) { + success = resource_limit->Reserve(resource, value); + } else { + success = true; + } + } + + explicit KScopedResourceReservation(const Process* p, LimitableResource r, s64 v, s64 t) + : KScopedResourceReservation(p->GetResourceLimit(), r, v, t) {} + + explicit KScopedResourceReservation(const Process* p, LimitableResource r, s64 v = 1) + : KScopedResourceReservation(p->GetResourceLimit(), r, v) {} + + ~KScopedResourceReservation() noexcept { + if (resource_limit && value && success) { + // resource was not committed, release the reservation. + resource_limit->Release(resource, value); + } + } + + /// Commit the resource reservation, destruction of this object does not release the resource + void Commit() { + resource_limit = nullptr; + } + + [[nodiscard]] bool Succeeded() const { + return success; + } + +private: + std::shared_ptr resource_limit; + s64 value; + LimitableResource resource; + bool success; +}; + +} // namespace Kernel diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index b20c2d13a..ba65bb022 100755 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -141,11 +141,17 @@ struct KernelCore::Impl { ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Events, 700).IsSuccess()); ASSERT(system_resource_limit->SetLimitValue(LimitableResource::TransferMemory, 200) .IsSuccess()); - ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Sessions, 900).IsSuccess()); + ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Sessions, 933).IsSuccess()); - if (!system_resource_limit->Reserve(LimitableResource::PhysicalMemory, 0x60000)) { + // Derived from recent software updates. The kernel reserves 27MB + constexpr u64 kernel_size{0x1b00000}; + if (!system_resource_limit->Reserve(LimitableResource::PhysicalMemory, kernel_size)) { UNREACHABLE(); } + // Reserve secure applet memory, introduced in firmware 5.0.0 + constexpr u64 secure_applet_memory_size{0x400000}; + ASSERT(system_resource_limit->Reserve(LimitableResource::PhysicalMemory, + secure_applet_memory_size)); } void InitializePreemption(KernelCore& kernel) { @@ -302,8 +308,11 @@ struct KernelCore::Impl { // Allocate slab heaps user_slab_heap_pages = std::make_unique>(); + constexpr u64 user_slab_heap_size{0x1ef000}; + // Reserve slab heaps + ASSERT( + system_resource_limit->Reserve(LimitableResource::PhysicalMemory, user_slab_heap_size)); // Initialize slab heaps - constexpr u64 user_slab_heap_size{0x3de000}; user_slab_heap_pages->Initialize( system.DeviceMemory().GetPointer(Core::DramMemoryMap::SlabHeapBase), user_slab_heap_size); diff --git a/src/core/hle/kernel/memory/page_table.cpp b/src/core/hle/kernel/memory/page_table.cpp index 7de91c768..769748047 100755 --- a/src/core/hle/kernel/memory/page_table.cpp +++ b/src/core/hle/kernel/memory/page_table.cpp @@ -7,7 +7,7 @@ #include "common/scope_exit.h" #include "core/core.h" #include "core/hle/kernel/errors.h" -#include "core/hle/kernel/k_resource_limit.h" +#include "core/hle/kernel/k_scoped_resource_reservation.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/memory/address_space_info.h" #include "core/hle/kernel/memory/memory_block.h" @@ -409,27 +409,25 @@ ResultCode PageTable::MapPhysicalMemory(VAddr addr, std::size_t size) { return RESULT_SUCCESS; } - auto process{system.Kernel().CurrentProcess()}; const std::size_t remaining_size{size - mapped_size}; const std::size_t remaining_pages{remaining_size / PageSize}; - if (process->GetResourceLimit() && - !process->GetResourceLimit()->Reserve(LimitableResource::PhysicalMemory, remaining_size)) { + // Reserve the memory from the process resource limit. + KScopedResourceReservation memory_reservation( + system.Kernel().CurrentProcess()->GetResourceLimit(), LimitableResource::PhysicalMemory, + remaining_size); + if (!memory_reservation.Succeeded()) { + LOG_ERROR(Kernel, "Could not reserve remaining {:X} bytes", remaining_size); return ERR_RESOURCE_LIMIT_EXCEEDED; } PageLinkedList page_linked_list; - { - auto block_guard = detail::ScopeExit([&] { - system.Kernel().MemoryManager().Free(page_linked_list, remaining_pages, memory_pool); - process->GetResourceLimit()->Release(LimitableResource::PhysicalMemory, remaining_size); - }); - CASCADE_CODE(system.Kernel().MemoryManager().Allocate(page_linked_list, remaining_pages, - memory_pool)); + CASCADE_CODE( + system.Kernel().MemoryManager().Allocate(page_linked_list, remaining_pages, memory_pool)); - block_guard.Cancel(); - } + // We succeeded, so commit the memory reservation. + memory_reservation.Commit(); MapPhysicalMemory(page_linked_list, addr, end_addr); @@ -781,9 +779,13 @@ ResultVal PageTable::SetHeapSize(std::size_t size) { const u64 delta{size - previous_heap_size}; - auto process{system.Kernel().CurrentProcess()}; - if (process->GetResourceLimit() && delta != 0 && - !process->GetResourceLimit()->Reserve(LimitableResource::PhysicalMemory, delta)) { + // Reserve memory for the heap extension. + KScopedResourceReservation memory_reservation( + system.Kernel().CurrentProcess()->GetResourceLimit(), LimitableResource::PhysicalMemory, + delta); + + if (!memory_reservation.Succeeded()) { + LOG_ERROR(Kernel, "Could not reserve heap extension of size {:X} bytes", delta); return ERR_RESOURCE_LIMIT_EXCEEDED; } @@ -800,6 +802,9 @@ ResultVal PageTable::SetHeapSize(std::size_t size) { CASCADE_CODE( Operate(current_heap_addr, num_pages, page_linked_list, OperationType::MapGroup)); + // Succeeded in allocation, commit the resource reservation + memory_reservation.Commit(); + block_manager->Update(current_heap_addr, num_pages, MemoryState::Normal, MemoryPermission::ReadAndWrite); diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 2286b292d..0de46cf06 100755 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -17,6 +17,7 @@ #include "core/hle/kernel/errors.h" #include "core/hle/kernel/k_resource_limit.h" #include "core/hle/kernel/k_scheduler.h" +#include "core/hle/kernel/k_scoped_resource_reservation.h" #include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/memory/memory_block_manager.h" @@ -39,6 +40,7 @@ namespace { */ void SetupMainThread(Core::System& system, Process& owner_process, u32 priority, VAddr stack_top) { const VAddr entry_point = owner_process.PageTable().GetCodeRegionStart(); + ASSERT(owner_process.GetResourceLimit()->Reserve(LimitableResource::Threads, 1)); auto thread_res = KThread::Create(system, ThreadType::User, "main", entry_point, priority, 0, owner_process.GetIdealCoreId(), stack_top, &owner_process); @@ -117,6 +119,9 @@ std::shared_ptr Process::Create(Core::System& system, std::string name, std::shared_ptr process = std::make_shared(system); process->name = std::move(name); + + // TODO: This is inaccurate + // The process should hold a reference to the kernel-wide resource limit. process->resource_limit = std::make_shared(kernel, system); process->status = ProcessStatus::Created; process->program_id = 0; @@ -155,6 +160,9 @@ void Process::DecrementThreadCount() { } u64 Process::GetTotalPhysicalMemoryAvailable() const { + // TODO: This is expected to always return the application memory pool size after accurately + // reserving kernel resources. The current workaround uses a process-local resource limit of + // application memory pool size, which is inaccurate. const u64 capacity{resource_limit->GetFreeValue(LimitableResource::PhysicalMemory) + page_table->GetTotalHeapSize() + GetSystemResourceSize() + image_size + main_thread_stack_size}; @@ -264,6 +272,17 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, system_resource_size = metadata.GetSystemResourceSize(); image_size = code_size; + // Set initial resource limits + resource_limit->SetLimitValue( + LimitableResource::PhysicalMemory, + kernel.MemoryManager().GetSize(Memory::MemoryManager::Pool::Application)); + KScopedResourceReservation memory_reservation(resource_limit, LimitableResource::PhysicalMemory, + code_size + system_resource_size); + if (!memory_reservation.Succeeded()) { + LOG_ERROR(Kernel, "Could not reserve process memory requirements of size {:X} bytes", + code_size + system_resource_size); + return ERR_RESOURCE_LIMIT_EXCEEDED; + } // Initialize proces address space if (const ResultCode result{ page_table->InitializeForProcess(metadata.GetAddressSpaceType(), false, 0x8000000, @@ -305,24 +324,22 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, UNREACHABLE(); } - // Set initial resource limits - resource_limit->SetLimitValue( - LimitableResource::PhysicalMemory, - kernel.MemoryManager().GetSize(Memory::MemoryManager::Pool::Application)); resource_limit->SetLimitValue(LimitableResource::Threads, 608); resource_limit->SetLimitValue(LimitableResource::Events, 700); resource_limit->SetLimitValue(LimitableResource::TransferMemory, 128); resource_limit->SetLimitValue(LimitableResource::Sessions, 894); - ASSERT(resource_limit->Reserve(LimitableResource::PhysicalMemory, code_size)); // Create TLS region tls_region_address = CreateTLSRegion(); + memory_reservation.Commit(); return handle_table.SetSize(capabilities.GetHandleTableSize()); } void Process::Run(s32 main_thread_priority, u64 stack_size) { AllocateMainThreadStack(stack_size); + resource_limit->Reserve(LimitableResource::Threads, 1); + resource_limit->Reserve(LimitableResource::PhysicalMemory, main_thread_stack_size); const std::size_t heap_capacity{memory_usage_capacity - main_thread_stack_size - image_size}; ASSERT(!page_table->SetHeapCapacity(heap_capacity).IsError()); @@ -330,8 +347,6 @@ void Process::Run(s32 main_thread_priority, u64 stack_size) { ChangeStatus(ProcessStatus::Running); SetupMainThread(system, *this, main_thread_priority, main_thread_stack_top); - resource_limit->Reserve(LimitableResource::Threads, 1); - resource_limit->Reserve(LimitableResource::PhysicalMemory, main_thread_stack_size); } void Process::PrepareForTermination() { @@ -358,6 +373,11 @@ void Process::PrepareForTermination() { FreeTLSRegion(tls_region_address); tls_region_address = 0; + if (resource_limit) { + resource_limit->Release(LimitableResource::PhysicalMemory, + main_thread_stack_size + image_size); + } + ChangeStatus(ProcessStatus::Exited); } diff --git a/src/core/hle/kernel/session.cpp b/src/core/hle/kernel/session.cpp index 75304b961..8830d4e91 100755 --- a/src/core/hle/kernel/session.cpp +++ b/src/core/hle/kernel/session.cpp @@ -4,15 +4,23 @@ #include "common/assert.h" #include "core/hle/kernel/client_session.h" +#include "core/hle/kernel/k_scoped_resource_reservation.h" #include "core/hle/kernel/server_session.h" #include "core/hle/kernel/session.h" namespace Kernel { Session::Session(KernelCore& kernel) : KSynchronizationObject{kernel} {} -Session::~Session() = default; +Session::~Session() { + // Release reserved resource when the Session pair was created. + kernel.GetSystemResourceLimit()->Release(LimitableResource::Sessions, 1); +} Session::SessionPair Session::Create(KernelCore& kernel, std::string name) { + // Reserve a new session from the resource limit. + KScopedResourceReservation session_reservation(kernel.GetSystemResourceLimit(), + LimitableResource::Sessions); + ASSERT(session_reservation.Succeeded()); auto session{std::make_shared(kernel)}; auto client_session{Kernel::ClientSession::Create(kernel, session, name + "_Client").Unwrap()}; auto server_session{Kernel::ServerSession::Create(kernel, session, name + "_Server").Unwrap()}; @@ -21,6 +29,7 @@ Session::SessionPair Session::Create(KernelCore& kernel, std::string name) { session->client = client_session; session->server = server_session; + session_reservation.Commit(); return std::make_pair(std::move(client_session), std::move(server_session)); } diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp index 0cd467110..2eadd51d7 100755 --- a/src/core/hle/kernel/shared_memory.cpp +++ b/src/core/hle/kernel/shared_memory.cpp @@ -4,6 +4,7 @@ #include "common/assert.h" #include "core/core.h" +#include "core/hle/kernel/k_scoped_resource_reservation.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/memory/page_table.h" #include "core/hle/kernel/shared_memory.h" @@ -13,7 +14,9 @@ namespace Kernel { SharedMemory::SharedMemory(KernelCore& kernel, Core::DeviceMemory& device_memory) : Object{kernel}, device_memory{device_memory} {} -SharedMemory::~SharedMemory() = default; +SharedMemory::~SharedMemory() { + kernel.GetSystemResourceLimit()->Release(LimitableResource::PhysicalMemory, size); +} std::shared_ptr SharedMemory::Create( KernelCore& kernel, Core::DeviceMemory& device_memory, Process* owner_process, @@ -21,6 +24,11 @@ std::shared_ptr SharedMemory::Create( Memory::MemoryPermission user_permission, PAddr physical_address, std::size_t size, std::string name) { + const auto resource_limit = kernel.GetSystemResourceLimit(); + KScopedResourceReservation memory_reservation(resource_limit, LimitableResource::PhysicalMemory, + size); + ASSERT(memory_reservation.Succeeded()); + std::shared_ptr shared_memory{ std::make_shared(kernel, device_memory)}; @@ -32,6 +40,7 @@ std::shared_ptr SharedMemory::Create( shared_memory->size = size; shared_memory->name = name; + memory_reservation.Commit(); return shared_memory; } diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index edf208eff..f47349285 100755 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -31,6 +31,7 @@ #include "core/hle/kernel/k_readable_event.h" #include "core/hle/kernel/k_resource_limit.h" #include "core/hle/kernel/k_scheduler.h" +#include "core/hle/kernel/k_scoped_resource_reservation.h" #include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" #include "core/hle/kernel/k_synchronization_object.h" #include "core/hle/kernel/k_thread.h" @@ -138,6 +139,7 @@ ResultCode MapUnmapMemorySanityChecks(const Memory::PageTable& manager, VAddr ds enum class ResourceLimitValueType { CurrentValue, LimitValue, + PeakValue, }; ResultVal RetrieveResourceLimitValue(Core::System& system, Handle resource_limit, @@ -160,11 +162,17 @@ ResultVal RetrieveResourceLimitValue(Core::System& system, Handle resource_ return ERR_INVALID_HANDLE; } - if (value_type == ResourceLimitValueType::CurrentValue) { + switch (value_type) { + case ResourceLimitValueType::CurrentValue: return MakeResult(resource_limit_object->GetCurrentValue(type)); + case ResourceLimitValueType::LimitValue: + return MakeResult(resource_limit_object->GetLimitValue(type)); + case ResourceLimitValueType::PeakValue: + return MakeResult(resource_limit_object->GetPeakValue(type)); + default: + LOG_ERROR(Kernel_SVC, "Invalid resource value_type: '{}'", value_type); + return ERR_INVALID_ENUM_VALUE; } - - return MakeResult(resource_limit_object->GetLimitValue(type)); } } // Anonymous namespace @@ -314,8 +322,6 @@ static ResultCode ConnectToNamedPort(Core::System& system, Handle* out_handle, return ERR_NOT_FOUND; } - ASSERT(kernel.CurrentProcess()->GetResourceLimit()->Reserve(LimitableResource::Sessions, 1)); - auto client_port = it->second; std::shared_ptr client_session; @@ -1452,8 +1458,13 @@ static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr e Svc::ResultInvalidPriority); R_UNLESS(process.CheckThreadPriority(priority), Svc::ResultInvalidPriority); - ASSERT(process.GetResourceLimit()->Reserve( - LimitableResource::Threads, 1, system.CoreTiming().GetGlobalTimeNs().count() + 100000000)); + KScopedResourceReservation thread_reservation( + kernel.CurrentProcess(), LimitableResource::Threads, 1, + system.CoreTiming().GetGlobalTimeNs().count() + 100000000); + if (!thread_reservation.Succeeded()) { + LOG_ERROR(Kernel_SVC, "Could not reserve a new thread"); + return ERR_RESOURCE_LIMIT_EXCEEDED; + } std::shared_ptr thread; { @@ -1473,6 +1484,7 @@ static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr e // Set the thread name for debugging purposes. thread->SetName( fmt::format("thread[entry_point={:X}, handle={:X}]", entry_point, *new_thread_handle)); + thread_reservation.Commit(); return RESULT_SUCCESS; } @@ -1787,6 +1799,13 @@ static ResultCode CreateTransferMemory(Core::System& system, Handle* handle, VAd } auto& kernel = system.Kernel(); + // Reserve a new transfer memory from the process resource limit. + KScopedResourceReservation trmem_reservation(kernel.CurrentProcess(), + LimitableResource::TransferMemory); + if (!trmem_reservation.Succeeded()) { + LOG_ERROR(Kernel_SVC, "Could not reserve a new transfer memory"); + return ERR_RESOURCE_LIMIT_EXCEEDED; + } auto transfer_mem_handle = TransferMemory::Create(kernel, system.Memory(), addr, size, perms); if (const auto reserve_result{transfer_mem_handle->Reserve()}; reserve_result.IsError()) { @@ -1798,6 +1817,7 @@ static ResultCode CreateTransferMemory(Core::System& system, Handle* handle, VAd if (result.Failed()) { return result.Code(); } + trmem_reservation.Commit(); *handle = *result; return RESULT_SUCCESS; @@ -1879,13 +1899,25 @@ static ResultCode SetThreadCoreMask32(Core::System& system, Handle thread_handle static ResultCode SignalEvent(Core::System& system, Handle event_handle) { LOG_DEBUG(Kernel_SVC, "called, event_handle=0x{:08X}", event_handle); + auto& kernel = system.Kernel(); // Get the current handle table. - const HandleTable& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); + const HandleTable& handle_table = kernel.CurrentProcess()->GetHandleTable(); + + // Reserve a new event from the process resource limit. + KScopedResourceReservation event_reservation(kernel.CurrentProcess(), + LimitableResource::Events); + if (!event_reservation.Succeeded()) { + LOG_ERROR(Kernel, "Could not reserve a new event"); + return ERR_RESOURCE_LIMIT_EXCEEDED; + } // Get the writable event. auto writable_event = handle_table.Get(event_handle); R_UNLESS(writable_event, Svc::ResultInvalidHandle); + // Commit the successfuly reservation. + event_reservation.Commit(); + return writable_event->Signal(); } diff --git a/src/core/hle/kernel/transfer_memory.cpp b/src/core/hle/kernel/transfer_memory.cpp index 765f408c3..6b0fc1591 100755 --- a/src/core/hle/kernel/transfer_memory.cpp +++ b/src/core/hle/kernel/transfer_memory.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "core/hle/kernel/k_resource_limit.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/memory/page_table.h" #include "core/hle/kernel/process.h" @@ -17,6 +18,7 @@ TransferMemory::TransferMemory(KernelCore& kernel, Core::Memory::Memory& memory) TransferMemory::~TransferMemory() { // Release memory region when transfer memory is destroyed Reset(); + owner_process->GetResourceLimit()->Release(LimitableResource::TransferMemory, 1); } std::shared_ptr TransferMemory::Create(KernelCore& kernel, diff --git a/src/tests/common/ring_buffer.cpp b/src/tests/common/ring_buffer.cpp index 54def22da..903626e4b 100755 --- a/src/tests/common/ring_buffer.cpp +++ b/src/tests/common/ring_buffer.cpp @@ -14,7 +14,7 @@ namespace Common { TEST_CASE("RingBuffer: Basic Tests", "[common]") { - RingBuffer buf; + RingBuffer buf; // Pushing values into a ring buffer with space should succeed. for (std::size_t i = 0; i < 4; i++) { @@ -77,7 +77,7 @@ TEST_CASE("RingBuffer: Basic Tests", "[common]") { } TEST_CASE("RingBuffer: Threaded Test", "[common]") { - RingBuffer buf; + RingBuffer buf; const char seed = 42; const std::size_t count = 1000000; std::size_t full = 0; @@ -92,8 +92,8 @@ TEST_CASE("RingBuffer: Threaded Test", "[common]") { std::array value = {seed, seed}; std::size_t i = 0; while (i < count) { - if (const std::size_t c = buf.Push(&value[0], 1); c > 0) { - REQUIRE(c == 1U); + if (const std::size_t c = buf.Push(&value[0], 2); c > 0) { + REQUIRE(c == 2U); i++; next_value(value); } else { @@ -107,7 +107,7 @@ TEST_CASE("RingBuffer: Threaded Test", "[common]") { std::array value = {seed, seed}; std::size_t i = 0; while (i < count) { - if (const std::vector v = buf.Pop(1); v.size() > 0) { + if (const std::vector v = buf.Pop(2); v.size() > 0) { REQUIRE(v.size() == 2U); REQUIRE(v[0] == value[0]); REQUIRE(v[1] == value[1]);