From 1773a1039f7422df4faac08aa366b6a6bbd645e4 Mon Sep 17 00:00:00 2001
From: Liam <byteslice@airmail.cc>
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 16ced4595b..ff5502d873 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 571acf4b21..abdb5639fa 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 0000000000..df3a1c4c56
--- /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<KAutoObject> 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<KAutoObject> 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 0000000000..b7f943134e
--- /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 <array>
+#include <memory>
+#include <boost/intrusive/list.hpp>
+
+#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<KObjectName>, public boost::intrusive::list_base_hook<> {
+public:
+    explicit KObjectName(KernelCore&) {}
+    virtual ~KObjectName() = default;
+
+    static constexpr size_t NameLengthMax = 12;
+    using List = boost::intrusive::list<KObjectName>;
+
+    static Result NewFromName(KernelCore& kernel, KAutoObject* obj, const char* name);
+    static Result Delete(KernelCore& kernel, KAutoObject* obj, const char* name);
+
+    static KScopedAutoObject<KAutoObject> Find(KernelCore& kernel, const char* name);
+
+    template <typename Derived>
+    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<Derived*>();
+        R_UNLESS(derived != nullptr, ResultNotFound);
+
+        // Check that the object is closed.
+        R_UNLESS(derived->IsServerClosed(), ResultInvalidState);
+
+        return Delete(kernel, obj.GetPointerUnsafe(), name);
+    }
+
+    template <typename Derived>
+        requires(std::derived_from<Derived, KAutoObject>)
+    static KScopedAutoObject<Derived> Find(KernelCore& kernel, const char* name) {
+        return Find(kernel, name);
+    }
+
+private:
+    static KScopedAutoObject<KAutoObject> 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<char, NameLengthMax> 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 b1922659d1..3a68a56332 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<KObjectNameGlobalData>(kernel);
+    }
+
     void MakeApplicationProcess(KProcess* process) {
         application_process = process;
     }
@@ -838,6 +846,8 @@ struct KernelCore::Impl {
 
     std::unique_ptr<KAutoObjectWithListContainer> global_object_list_container;
 
+    std::unique_ptr<KObjectNameGlobalData> object_name_global_data;
+
     /// Map of named ports managed by the kernel, which can be retrieved using
     /// the ConnectToPort SVC.
     std::unordered_map<std::string, ServiceInterfaceFactory> 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 a236e6b42c..6e0668f7f9 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<T, KThreadLocalPage>) {
             return slab_heap_container->thread_local_page;
+        } else if constexpr (std::is_same_v<T, KObjectName>) {
+            return slab_heap_container->object_name;
         } else if constexpr (std::is_same_v<T, KSessionRequest>) {
             return slab_heap_container->session_request;
         } else if constexpr (std::is_same_v<T, KSecureSystemResource>) {
@@ -443,6 +450,7 @@ private:
         KSlabHeap<KDeviceAddressSpace> device_address_space;
         KSlabHeap<KPageBuffer> page_buffer;
         KSlabHeap<KThreadLocalPage> thread_local_page;
+        KSlabHeap<KObjectName> object_name;
         KSlabHeap<KSessionRequest> session_request;
         KSlabHeap<KSecureSystemResource> secure_system_resource;
         KSlabHeap<KEventInfo> event_info;
diff --git a/src/core/hle/kernel/svc/svc_port.cpp b/src/core/hle/kernel/svc/svc_port.cpp
index 2b7cebde53..2f9bfcb527 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<char, KObjectName::NameLengthMax> 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<KClientPort>(system.Kernel(), name.data()));
+    }
+
+    R_SUCCEED();
 }
 
 Result ConnectToNamedPort64(Core::System& system, Handle* out_handle, uint64_t name) {