From 1773a1039f7422df4faac08aa366b6a6bbd645e4 Mon Sep 17 00:00:00 2001 From: Liam Date: Thu, 16 Feb 2023 23:16:08 -0500 Subject: [PATCH] kernel: add KObjectName --- src/core/CMakeLists.txt | 2 + src/core/hle/kernel/init/init_slab_setup.cpp | 2 + src/core/hle/kernel/k_object_name.cpp | 102 +++++++++++++++++++ src/core/hle/kernel/k_object_name.h | 86 ++++++++++++++++ src/core/hle/kernel/kernel.cpp | 14 +++ src/core/hle/kernel/kernel.h | 8 ++ src/core/hle/kernel/svc/svc_port.cpp | 54 +++++++++- 7 files changed, 265 insertions(+), 3 deletions(-) create mode 100644 src/core/hle/kernel/k_object_name.cpp create mode 100644 src/core/hle/kernel/k_object_name.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 16ced4595..ff5502d87 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -225,6 +225,8 @@ add_library(core STATIC hle/kernel/k_memory_manager.h hle/kernel/k_memory_region.h hle/kernel/k_memory_region_type.h + hle/kernel/k_object_name.cpp + hle/kernel/k_object_name.h hle/kernel/k_page_bitmap.h hle/kernel/k_page_buffer.cpp hle/kernel/k_page_buffer.h diff --git a/src/core/hle/kernel/init/init_slab_setup.cpp b/src/core/hle/kernel/init/init_slab_setup.cpp index 571acf4b2..abdb5639f 100644 --- a/src/core/hle/kernel/init/init_slab_setup.cpp +++ b/src/core/hle/kernel/init/init_slab_setup.cpp @@ -16,6 +16,7 @@ #include "core/hle/kernel/k_event_info.h" #include "core/hle/kernel/k_memory_layout.h" #include "core/hle/kernel/k_memory_manager.h" +#include "core/hle/kernel/k_object_name.h" #include "core/hle/kernel/k_page_buffer.h" #include "core/hle/kernel/k_port.h" #include "core/hle/kernel/k_process.h" @@ -49,6 +50,7 @@ namespace Kernel::Init { HANDLER(KThreadLocalPage, \ (SLAB_COUNT(KProcess) + (SLAB_COUNT(KProcess) + SLAB_COUNT(KThread)) / 8), \ ##__VA_ARGS__) \ + HANDLER(KObjectName, (SLAB_COUNT(KObjectName)), ##__VA_ARGS__) \ HANDLER(KResourceLimit, (SLAB_COUNT(KResourceLimit)), ##__VA_ARGS__) \ HANDLER(KEventInfo, (SLAB_COUNT(KThread) + SLAB_COUNT(KDebug)), ##__VA_ARGS__) \ HANDLER(KDebug, (SLAB_COUNT(KDebug)), ##__VA_ARGS__) \ diff --git a/src/core/hle/kernel/k_object_name.cpp b/src/core/hle/kernel/k_object_name.cpp new file mode 100644 index 000000000..df3a1c4c5 --- /dev/null +++ b/src/core/hle/kernel/k_object_name.cpp @@ -0,0 +1,102 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/kernel/k_object_name.h" + +namespace Kernel { + +KObjectNameGlobalData::KObjectNameGlobalData(KernelCore& kernel) : m_object_list_lock{kernel} {} +KObjectNameGlobalData::~KObjectNameGlobalData() = default; + +void KObjectName::Initialize(KAutoObject* obj, const char* name) { + // Set member variables. + m_object = obj; + std::strncpy(m_name.data(), name, sizeof(m_name) - 1); + m_name[sizeof(m_name) - 1] = '\x00'; + + // Open a reference to the object we hold. + m_object->Open(); +} + +bool KObjectName::MatchesName(const char* name) const { + return std::strncmp(m_name.data(), name, sizeof(m_name)) == 0; +} + +Result KObjectName::NewFromName(KernelCore& kernel, KAutoObject* obj, const char* name) { + // Create a new object name. + KObjectName* new_name = KObjectName::Allocate(kernel); + R_UNLESS(new_name != nullptr, ResultOutOfResource); + + // Initialize the new name. + new_name->Initialize(obj, name); + + // Check if there's an existing name. + { + // Get the global data. + KObjectNameGlobalData& gd{kernel.ObjectNameGlobalData()}; + + // Ensure we have exclusive access to the global list. + KScopedLightLock lk{gd.GetObjectListLock()}; + + // If the object doesn't exist, put it into the list. + KScopedAutoObject existing_object = FindImpl(kernel, name); + if (existing_object.IsNull()) { + gd.GetObjectList().push_back(*new_name); + R_SUCCEED(); + } + } + + // The object already exists, which is an error condition. Perform cleanup. + obj->Close(); + KObjectName::Free(kernel, new_name); + R_THROW(ResultInvalidState); +} + +Result KObjectName::Delete(KernelCore& kernel, KAutoObject* obj, const char* compare_name) { + // Get the global data. + KObjectNameGlobalData& gd{kernel.ObjectNameGlobalData()}; + + // Ensure we have exclusive access to the global list. + KScopedLightLock lk{gd.GetObjectListLock()}; + + // Find a matching entry in the list, and delete it. + for (auto& name : gd.GetObjectList()) { + if (name.MatchesName(compare_name) && obj == name.GetObject()) { + // We found a match, clean up its resources. + obj->Close(); + gd.GetObjectList().erase(gd.GetObjectList().iterator_to(name)); + KObjectName::Free(kernel, std::addressof(name)); + R_SUCCEED(); + } + } + + // We didn't find the object in the list. + R_THROW(ResultNotFound); +} + +KScopedAutoObject KObjectName::Find(KernelCore& kernel, const char* name) { + // Get the global data. + KObjectNameGlobalData& gd{kernel.ObjectNameGlobalData()}; + + // Ensure we have exclusive access to the global list. + KScopedLightLock lk{gd.GetObjectListLock()}; + + return FindImpl(kernel, name); +} + +KScopedAutoObject KObjectName::FindImpl(KernelCore& kernel, const char* compare_name) { + // Get the global data. + KObjectNameGlobalData& gd{kernel.ObjectNameGlobalData()}; + + // Try to find a matching object in the global list. + for (const auto& name : gd.GetObjectList()) { + if (name.MatchesName(compare_name)) { + return name.GetObject(); + } + } + + // There's no matching entry in the list. + return nullptr; +} + +} // namespace Kernel diff --git a/src/core/hle/kernel/k_object_name.h b/src/core/hle/kernel/k_object_name.h new file mode 100644 index 000000000..b7f943134 --- /dev/null +++ b/src/core/hle/kernel/k_object_name.h @@ -0,0 +1,86 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include + +#include "core/hle/kernel/k_light_lock.h" +#include "core/hle/kernel/slab_helpers.h" +#include "core/hle/kernel/svc_results.h" + +namespace Kernel { + +class KObjectNameGlobalData; + +class KObjectName : public KSlabAllocated, public boost::intrusive::list_base_hook<> { +public: + explicit KObjectName(KernelCore&) {} + virtual ~KObjectName() = default; + + static constexpr size_t NameLengthMax = 12; + using List = boost::intrusive::list; + + static Result NewFromName(KernelCore& kernel, KAutoObject* obj, const char* name); + static Result Delete(KernelCore& kernel, KAutoObject* obj, const char* name); + + static KScopedAutoObject Find(KernelCore& kernel, const char* name); + + template + static Result Delete(KernelCore& kernel, const char* name) { + // Find the object. + KScopedAutoObject obj = Find(kernel, name); + R_UNLESS(obj.IsNotNull(), ResultNotFound); + + // Cast the object to the desired type. + Derived* derived = obj->DynamicCast(); + R_UNLESS(derived != nullptr, ResultNotFound); + + // Check that the object is closed. + R_UNLESS(derived->IsServerClosed(), ResultInvalidState); + + return Delete(kernel, obj.GetPointerUnsafe(), name); + } + + template + requires(std::derived_from) + static KScopedAutoObject Find(KernelCore& kernel, const char* name) { + return Find(kernel, name); + } + +private: + static KScopedAutoObject FindImpl(KernelCore& kernel, const char* name); + + void Initialize(KAutoObject* obj, const char* name); + + bool MatchesName(const char* name) const; + KAutoObject* GetObject() const { + return m_object; + } + +private: + std::array m_name{}; + KAutoObject* m_object{}; +}; + +class KObjectNameGlobalData { +public: + explicit KObjectNameGlobalData(KernelCore& kernel); + ~KObjectNameGlobalData(); + + KLightLock& GetObjectListLock() { + return m_object_list_lock; + } + + KObjectName::List& GetObjectList() { + return m_object_list; + } + +private: + KLightLock m_object_list_lock; + KObjectName::List m_object_list; +}; + +} // namespace Kernel diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index b1922659d..3a68a5633 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -29,6 +29,7 @@ #include "core/hle/kernel/k_hardware_timer.h" #include "core/hle/kernel/k_memory_layout.h" #include "core/hle/kernel/k_memory_manager.h" +#include "core/hle/kernel/k_object_name.h" #include "core/hle/kernel/k_page_buffer.h" #include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_resource_limit.h" @@ -84,6 +85,7 @@ struct KernelCore::Impl { InitializeShutdownThreads(); InitializePhysicalCores(); InitializePreemption(kernel); + InitializeGlobalData(kernel); // Initialize the Dynamic Slab Heaps. { @@ -194,6 +196,8 @@ struct KernelCore::Impl { } } + object_name_global_data.reset(); + // Ensure that the object list container is finalized and properly shutdown. global_object_list_container->Finalize(); global_object_list_container.reset(); @@ -363,6 +367,10 @@ struct KernelCore::Impl { } } + void InitializeGlobalData(KernelCore& kernel) { + object_name_global_data = std::make_unique(kernel); + } + void MakeApplicationProcess(KProcess* process) { application_process = process; } @@ -838,6 +846,8 @@ struct KernelCore::Impl { std::unique_ptr global_object_list_container; + std::unique_ptr object_name_global_data; + /// Map of named ports managed by the kernel, which can be retrieved using /// the ConnectToPort SVC. std::unordered_map service_interface_factory; @@ -1138,6 +1148,10 @@ void KernelCore::SetCurrentEmuThread(KThread* thread) { impl->SetCurrentEmuThread(thread); } +KObjectNameGlobalData& KernelCore::ObjectNameGlobalData() { + return *impl->object_name_global_data; +} + KMemoryManager& KernelCore::MemoryManager() { return *impl->memory_manager; } diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index a236e6b42..6e0668f7f 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -44,6 +44,8 @@ class KHardwareTimer; class KLinkedListNode; class KMemoryLayout; class KMemoryManager; +class KObjectName; +class KObjectNameGlobalData; class KPageBuffer; class KPageBufferSlabHeap; class KPort; @@ -240,6 +242,9 @@ public: /// Register the current thread as a non CPU core thread. void RegisterHostThread(KThread* existing_thread = nullptr); + /// Gets global data for KObjectName. + KObjectNameGlobalData& ObjectNameGlobalData(); + /// Gets the virtual memory manager for the kernel. KMemoryManager& MemoryManager(); @@ -372,6 +377,8 @@ public: return slab_heap_container->page_buffer; } else if constexpr (std::is_same_v) { return slab_heap_container->thread_local_page; + } else if constexpr (std::is_same_v) { + return slab_heap_container->object_name; } else if constexpr (std::is_same_v) { return slab_heap_container->session_request; } else if constexpr (std::is_same_v) { @@ -443,6 +450,7 @@ private: KSlabHeap device_address_space; KSlabHeap page_buffer; KSlabHeap thread_local_page; + KSlabHeap object_name; KSlabHeap session_request; KSlabHeap secure_system_resource; KSlabHeap event_info; diff --git a/src/core/hle/kernel/svc/svc_port.cpp b/src/core/hle/kernel/svc/svc_port.cpp index 2b7cebde5..2f9bfcb52 100644 --- a/src/core/hle/kernel/svc/svc_port.cpp +++ b/src/core/hle/kernel/svc/svc_port.cpp @@ -5,6 +5,7 @@ #include "core/core.h" #include "core/hle/kernel/k_client_port.h" #include "core/hle/kernel/k_client_session.h" +#include "core/hle/kernel/k_object_name.h" #include "core/hle/kernel/k_port.h" #include "core/hle/kernel/k_process.h" #include "core/hle/kernel/svc.h" @@ -74,10 +75,57 @@ Result ConnectToPort(Core::System& system, Handle* out_handle, Handle port) { R_THROW(ResultNotImplemented); } -Result ManageNamedPort(Core::System& system, Handle* out_server_handle, uint64_t name, +Result ManageNamedPort(Core::System& system, Handle* out_server_handle, uint64_t user_name, int32_t max_sessions) { - UNIMPLEMENTED(); - R_THROW(ResultNotImplemented); + // Copy the provided name from user memory to kernel memory. + std::array name{}; + system.Memory().ReadBlock(user_name, name.data(), sizeof(name)); + + // Validate that sessions and name are valid. + R_UNLESS(max_sessions >= 0, ResultOutOfRange); + R_UNLESS(name[sizeof(name) - 1] == '\x00', ResultOutOfRange); + + if (max_sessions > 0) { + // Get the current handle table. + auto& handle_table = GetCurrentProcess(system.Kernel()).GetHandleTable(); + + // Create a new port. + KPort* port = KPort::Create(system.Kernel()); + R_UNLESS(port != nullptr, ResultOutOfResource); + + // Initialize the new port. + port->Initialize(max_sessions, false, ""); + + // Register the port. + KPort::Register(system.Kernel(), port); + + // Ensure that our only reference to the port is in the handle table when we're done. + SCOPE_EXIT({ + port->GetClientPort().Close(); + port->GetServerPort().Close(); + }); + + // Register the handle in the table. + R_TRY(handle_table.Add(out_server_handle, std::addressof(port->GetServerPort()))); + ON_RESULT_FAILURE { + handle_table.Remove(*out_server_handle); + }; + + // Create a new object name. + R_TRY(KObjectName::NewFromName(system.Kernel(), std::addressof(port->GetClientPort()), + name.data())); + } else /* if (max_sessions == 0) */ { + // Ensure that this else case is correct. + ASSERT(max_sessions == 0); + + // If we're closing, there's no server handle. + *out_server_handle = InvalidHandle; + + // Delete the object. + R_TRY(KObjectName::Delete(system.Kernel(), name.data())); + } + + R_SUCCEED(); } Result ConnectToNamedPort64(Core::System& system, Handle* out_handle, uint64_t name) {