early-access version 1420
This commit is contained in:
		| @@ -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 | ||||
|  | ||||
|   | ||||
| @@ -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 <typename T, std::size_t capacity, std::size_t granularity = 1> | ||||
| template <typename T, std::size_t capacity> | ||||
| 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<T>); | ||||
|     // Ensure capacity is sensible. | ||||
|     static_assert(capacity < std::numeric_limits<std::size_t>::max() / 2 / granularity); | ||||
|     static_assert(capacity < std::numeric_limits<std::size_t>::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<const char*>(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<char*>(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<T> Pop(std::size_t max_slots = ~std::size_t(0)) { | ||||
|         std::vector<T> out(std::min(max_slots, capacity) * granularity); | ||||
|         const std::size_t count = Pop(out.data(), out.size() / granularity); | ||||
|         out.resize(count * granularity); | ||||
|         std::vector<T> 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<T, granularity * capacity> m_data; | ||||
|     std::array<T, capacity> m_data; | ||||
| }; | ||||
|  | ||||
| } // namespace Common | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
							
								
								
									
										67
									
								
								src/core/hle/kernel/k_scoped_resource_reservation.h
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										67
									
								
								src/core/hle/kernel/k_scoped_resource_reservation.h
									
									
									
									
									
										Executable file
									
								
							| @@ -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<KResourceLimit> 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<KResourceLimit> 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<KResourceLimit> resource_limit; | ||||
|     s64 value; | ||||
|     LimitableResource resource; | ||||
|     bool success; | ||||
| }; | ||||
|  | ||||
| } // namespace Kernel | ||||
| @@ -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<Memory::SlabHeap<Memory::Page>>(); | ||||
|  | ||||
|         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); | ||||
|   | ||||
| @@ -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<VAddr> 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<VAddr> 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); | ||||
|  | ||||
|   | ||||
| @@ -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> Process::Create(Core::System& system, std::string name, | ||||
|  | ||||
|     std::shared_ptr<Process> process = std::make_shared<Process>(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<KResourceLimit>(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); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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<Session>(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)); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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> SharedMemory::Create( | ||||
|     KernelCore& kernel, Core::DeviceMemory& device_memory, Process* owner_process, | ||||
| @@ -21,6 +24,11 @@ std::shared_ptr<SharedMemory> 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<SharedMemory> shared_memory{ | ||||
|         std::make_shared<SharedMemory>(kernel, device_memory)}; | ||||
|  | ||||
| @@ -32,6 +40,7 @@ std::shared_ptr<SharedMemory> SharedMemory::Create( | ||||
|     shared_memory->size = size; | ||||
|     shared_memory->name = name; | ||||
|  | ||||
|     memory_reservation.Commit(); | ||||
|     return shared_memory; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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<s64> RetrieveResourceLimitValue(Core::System& system, Handle resource_limit, | ||||
| @@ -160,11 +162,17 @@ ResultVal<s64> 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<ClientSession> 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<KThread> 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<KWritableEvent>(event_handle); | ||||
|     R_UNLESS(writable_event, Svc::ResultInvalidHandle); | ||||
|  | ||||
|     // Commit the successfuly reservation. | ||||
|     event_reservation.Commit(); | ||||
|  | ||||
|     return writable_event->Signal(); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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> TransferMemory::Create(KernelCore& kernel, | ||||
|   | ||||
| @@ -14,7 +14,7 @@ | ||||
| namespace Common { | ||||
|  | ||||
| TEST_CASE("RingBuffer: Basic Tests", "[common]") { | ||||
|     RingBuffer<char, 4, 1> buf; | ||||
|     RingBuffer<char, 4> 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<char, 4, 2> buf; | ||||
|     RingBuffer<char, 8> 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<char, 2> 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<char, 2> value = {seed, seed}; | ||||
|         std::size_t i = 0; | ||||
|         while (i < count) { | ||||
|             if (const std::vector<char> v = buf.Pop(1); v.size() > 0) { | ||||
|             if (const std::vector<char> v = buf.Pop(2); v.size() > 0) { | ||||
|                 REQUIRE(v.size() == 2U); | ||||
|                 REQUIRE(v[0] == value[0]); | ||||
|                 REQUIRE(v[1] == value[1]); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user