diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h
index 4ace2cd33..17d1ee86b 100644
--- a/src/common/common_funcs.h
+++ b/src/common/common_funcs.h
@@ -108,6 +108,14 @@ __declspec(dllimport) void __stdcall DebugBreak(void);
         }                                                                                          \
     }
 
+#define YUZU_NON_COPYABLE(cls)                                                                     \
+    cls(const cls&) = delete;                                                                      \
+    cls& operator=(const cls&) = delete
+
+#define YUZU_NON_MOVEABLE(cls)                                                                     \
+    cls(cls&&) = delete;                                                                           \
+    cls& operator=(cls&&) = delete
+
 #define R_SUCCEEDED(res) (res.IsSuccess())
 
 /// Evaluates an expression that returns a result, and returns the result if it would fail.
@@ -128,4 +136,19 @@ namespace Common {
     return u32(a) | u32(b) << 8 | u32(c) << 16 | u32(d) << 24;
 }
 
+// std::size() does not support zero-size C arrays. We're fixing that.
+template <class C>
+constexpr auto Size(const C& c) -> decltype(c.size()) {
+    return std::size(c);
+}
+
+template <class C>
+constexpr std::size_t Size(const C& c) {
+    if constexpr (sizeof(C) == 0) {
+        return 0;
+    } else {
+        return std::size(c);
+    }
+}
+
 } // namespace Common
diff --git a/src/common/intrusive_red_black_tree.h b/src/common/intrusive_red_black_tree.h
index c0bbcd457..1f696fe80 100644
--- a/src/common/intrusive_red_black_tree.h
+++ b/src/common/intrusive_red_black_tree.h
@@ -509,7 +509,6 @@ private:
 
 private:
     static constexpr TypedStorage<Derived> DerivedStorage = {};
-    static_assert(GetParent(GetNode(GetPointer(DerivedStorage))) == GetPointer(DerivedStorage));
 };
 
 template <auto T, class Derived = impl::GetParentType<T>>
diff --git a/src/common/parent_of_member.h b/src/common/parent_of_member.h
index d9a14529d..e0f8ab5c8 100644
--- a/src/common/parent_of_member.h
+++ b/src/common/parent_of_member.h
@@ -133,27 +133,27 @@ template <auto MemberPtr>
 using GetMemberType = typename GetMemberPointerTraits<decltype(MemberPtr)>::Member;
 
 template <auto MemberPtr, typename RealParentType = GetParentType<MemberPtr>>
-static inline std::ptrdiff_t OffsetOf = [] {
+constexpr std::ptrdiff_t OffsetOf() {
     using DeducedParentType = GetParentType<MemberPtr>;
     using MemberType = GetMemberType<MemberPtr>;
     static_assert(std::is_base_of<DeducedParentType, RealParentType>::value ||
                   std::is_same<RealParentType, DeducedParentType>::value);
 
     return OffsetOfCalculator<RealParentType, MemberType>::OffsetOf(MemberPtr);
-}();
+};
 
 } // namespace impl
 
 template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
 constexpr RealParentType& GetParentReference(impl::GetMemberType<MemberPtr>* member) {
-    std::ptrdiff_t Offset = impl::OffsetOf<MemberPtr, RealParentType>;
+    std::ptrdiff_t Offset = impl::OffsetOf<MemberPtr, RealParentType>();
     return *static_cast<RealParentType*>(
         static_cast<void*>(static_cast<uint8_t*>(static_cast<void*>(member)) - Offset));
 }
 
 template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
 constexpr RealParentType const& GetParentReference(impl::GetMemberType<MemberPtr> const* member) {
-    std::ptrdiff_t Offset = impl::OffsetOf<MemberPtr, RealParentType>;
+    std::ptrdiff_t Offset = impl::OffsetOf<MemberPtr, RealParentType>();
     return *static_cast<const RealParentType*>(static_cast<const void*>(
         static_cast<const uint8_t*>(static_cast<const void*>(member)) - Offset));
 }
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index c28abc24c..83da30418 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -144,31 +144,40 @@ add_library(core STATIC
     hle/kernel/board/nintendo/nx/k_system_control.cpp
     hle/kernel/board/nintendo/nx/k_system_control.h
     hle/kernel/board/nintendo/nx/secure_monitor.h
-    hle/kernel/client_port.cpp
-    hle/kernel/client_port.h
-    hle/kernel/client_session.cpp
-    hle/kernel/client_session.h
     hle/kernel/code_set.cpp
     hle/kernel/code_set.h
     hle/kernel/svc_results.h
     hle/kernel/global_scheduler_context.cpp
     hle/kernel/global_scheduler_context.h
-    hle/kernel/handle_table.cpp
-    hle/kernel/handle_table.h
     hle/kernel/hle_ipc.cpp
     hle/kernel/hle_ipc.h
+    hle/kernel/init/init_slab_setup.cpp
+    hle/kernel/init/init_slab_setup.h
     hle/kernel/k_address_arbiter.cpp
     hle/kernel/k_address_arbiter.h
     hle/kernel/k_address_space_info.cpp
     hle/kernel/k_address_space_info.h
+    hle/kernel/k_auto_object.cpp
+    hle/kernel/k_auto_object.h
+    hle/kernel/k_auto_object_container.cpp
+    hle/kernel/k_auto_object_container.h
     hle/kernel/k_affinity_mask.h
+    hle/kernel/k_class_token.cpp
+    hle/kernel/k_class_token.h
+    hle/kernel/k_client_port.cpp
+    hle/kernel/k_client_port.h
+    hle/kernel/k_client_session.cpp
+    hle/kernel/k_client_session.h
     hle/kernel/k_condition_variable.cpp
     hle/kernel/k_condition_variable.h
     hle/kernel/k_event.cpp
     hle/kernel/k_event.h
+    hle/kernel/k_handle_table.cpp
+    hle/kernel/k_handle_table.h
     hle/kernel/k_light_condition_variable.h
     hle/kernel/k_light_lock.cpp
     hle/kernel/k_light_lock.h
+    hle/kernel/k_linked_list.h
     hle/kernel/k_memory_block.h
     hle/kernel/k_memory_block_manager.cpp
     hle/kernel/k_memory_block_manager.h
@@ -185,7 +194,11 @@ add_library(core STATIC
     hle/kernel/k_page_linked_list.h
     hle/kernel/k_page_table.cpp
     hle/kernel/k_page_table.h
+    hle/kernel/k_port.cpp
+    hle/kernel/k_port.h
     hle/kernel/k_priority_queue.h
+    hle/kernel/k_process.cpp
+    hle/kernel/k_process.h
     hle/kernel/k_readable_event.cpp
     hle/kernel/k_readable_event.h
     hle/kernel/k_resource_limit.cpp
@@ -196,6 +209,12 @@ add_library(core STATIC
     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_server_port.cpp
+    hle/kernel/k_server_port.h
+    hle/kernel/k_server_session.cpp
+    hle/kernel/k_server_session.h
+    hle/kernel/k_session.cpp
+    hle/kernel/k_session.h
     hle/kernel/k_shared_memory.cpp
     hle/kernel/k_shared_memory.h
     hle/kernel/k_slab_heap.h
@@ -208,28 +227,21 @@ add_library(core STATIC
     hle/kernel/k_thread.h
     hle/kernel/k_thread_queue.h
     hle/kernel/k_trace.h
+    hle/kernel/k_transfer_memory.cpp
+    hle/kernel/k_transfer_memory.h
     hle/kernel/k_writable_event.cpp
     hle/kernel/k_writable_event.h
     hle/kernel/kernel.cpp
     hle/kernel/kernel.h
     hle/kernel/memory_types.h
-    hle/kernel/object.cpp
-    hle/kernel/object.h
     hle/kernel/physical_core.cpp
     hle/kernel/physical_core.h
     hle/kernel/physical_memory.h
-    hle/kernel/process.cpp
-    hle/kernel/process.h
     hle/kernel/process_capability.cpp
     hle/kernel/process_capability.h
-    hle/kernel/server_port.cpp
-    hle/kernel/server_port.h
-    hle/kernel/server_session.cpp
-    hle/kernel/server_session.h
     hle/kernel/service_thread.cpp
     hle/kernel/service_thread.h
-    hle/kernel/session.cpp
-    hle/kernel/session.h
+    hle/kernel/slab_helpers.h
     hle/kernel/svc.cpp
     hle/kernel/svc.h
     hle/kernel/svc_common.h
@@ -237,8 +249,6 @@ add_library(core STATIC
     hle/kernel/svc_wrap.h
     hle/kernel/time_manager.cpp
     hle/kernel/time_manager.h
-    hle/kernel/transfer_memory.cpp
-    hle/kernel/transfer_memory.h
     hle/lock.cpp
     hle/lock.h
     hle/result.h
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index 4ff72abd8..653bb7a77 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -16,8 +16,8 @@
 #include "core/core.h"
 #include "core/core_timing.h"
 #include "core/hardware_properties.h"
+#include "core/hle/kernel/k_process.h"
 #include "core/hle/kernel/k_scheduler.h"
-#include "core/hle/kernel/process.h"
 #include "core/hle/kernel/svc.h"
 #include "core/memory.h"
 
diff --git a/src/core/core.cpp b/src/core/core.cpp
index d459d6c34..434bf3262 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -27,12 +27,12 @@
 #include "core/file_sys/vfs_concat.h"
 #include "core/file_sys/vfs_real.h"
 #include "core/hardware_interrupt_manager.h"
-#include "core/hle/kernel/client_port.h"
+#include "core/hle/kernel/k_client_port.h"
+#include "core/hle/kernel/k_process.h"
 #include "core/hle/kernel/k_scheduler.h"
 #include "core/hle/kernel/k_thread.h"
 #include "core/hle/kernel/kernel.h"
 #include "core/hle/kernel/physical_core.h"
-#include "core/hle/kernel/process.h"
 #include "core/hle/service/am/applets/applets.h"
 #include "core/hle/service/apm/controller.h"
 #include "core/hle/service/filesystem/filesystem.h"
@@ -166,9 +166,9 @@ struct System::Impl {
         cpu_manager.SetAsyncGpu(is_async_gpu);
         core_timing.SetMulticore(is_multicore);
 
-        core_timing.Initialize([&system]() { system.RegisterHostThread(); });
         kernel.Initialize();
         cpu_manager.Initialize();
+        core_timing.Initialize([&system]() { system.RegisterHostThread(); });
 
         const auto current_time = std::chrono::duration_cast<std::chrono::seconds>(
             std::chrono::system_clock::now().time_since_epoch());
@@ -233,8 +233,11 @@ struct System::Impl {
         }
 
         telemetry_session->AddInitialInfo(*app_loader, fs_controller, *content_provider);
-        auto main_process =
-            Kernel::Process::Create(system, "main", Kernel::Process::ProcessType::Userland);
+        auto main_process = Kernel::KProcess::Create(system.Kernel());
+        ASSERT(Kernel::KProcess::Initialize(main_process, system, "main",
+                                            Kernel::KProcess::ProcessType::Userland)
+                   .IsSuccess());
+        main_process->Open();
         const auto [load_result, load_parameters] = app_loader->Load(*main_process, system);
         if (load_result != Loader::ResultStatus::Success) {
             LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", load_result);
@@ -244,7 +247,7 @@ struct System::Impl {
                                              static_cast<u32>(load_result));
         }
         AddGlueRegistrationForProcess(*app_loader, *main_process);
-        kernel.MakeCurrentProcess(main_process.get());
+        kernel.MakeCurrentProcess(main_process);
         kernel.InitializeCores();
 
         // Initialize cheat engine
@@ -311,6 +314,7 @@ struct System::Impl {
         gpu_core.reset();
         perf_stats.reset();
         kernel.Shutdown();
+        memory.Reset();
         applet_manager.ClearAll();
 
         LOG_DEBUG(Core, "Shutdown OK");
@@ -322,7 +326,7 @@ struct System::Impl {
         return app_loader->ReadTitle(out);
     }
 
-    void AddGlueRegistrationForProcess(Loader::AppLoader& loader, Kernel::Process& process) {
+    void AddGlueRegistrationForProcess(Loader::AppLoader& loader, Kernel::KProcess& process) {
         std::vector<u8> nacp_data;
         FileSys::NACP nacp;
         if (loader.ReadControlData(nacp) == Loader::ResultStatus::Success) {
@@ -513,7 +517,7 @@ const Kernel::GlobalSchedulerContext& System::GlobalSchedulerContext() const {
     return impl->kernel.GlobalSchedulerContext();
 }
 
-Kernel::Process* System::CurrentProcess() {
+Kernel::KProcess* System::CurrentProcess() {
     return impl->kernel.CurrentProcess();
 }
 
@@ -525,7 +529,7 @@ const Core::DeviceMemory& System::DeviceMemory() const {
     return *impl->device_memory;
 }
 
-const Kernel::Process* System::CurrentProcess() const {
+const Kernel::KProcess* System::CurrentProcess() const {
     return impl->kernel.CurrentProcess();
 }
 
diff --git a/src/core/core.h b/src/core/core.h
index f1068d23f..8b93ba998 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -12,7 +12,6 @@
 
 #include "common/common_types.h"
 #include "core/file_sys/vfs_types.h"
-#include "core/hle/kernel/object.h"
 
 namespace Core::Frontend {
 class EmuWindow;
@@ -29,7 +28,7 @@ namespace Kernel {
 class GlobalSchedulerContext;
 class KernelCore;
 class PhysicalCore;
-class Process;
+class KProcess;
 class KScheduler;
 } // namespace Kernel
 
@@ -264,10 +263,10 @@ public:
     [[nodiscard]] const Core::DeviceMemory& DeviceMemory() const;
 
     /// Provides a pointer to the current process
-    [[nodiscard]] Kernel::Process* CurrentProcess();
+    [[nodiscard]] Kernel::KProcess* CurrentProcess();
 
     /// Provides a constant pointer to the current process.
-    [[nodiscard]] const Kernel::Process* CurrentProcess() const;
+    [[nodiscard]] const Kernel::KProcess* CurrentProcess() const;
 
     /// Provides a reference to the core timing instance.
     [[nodiscard]] Timing::CoreTiming& CoreTiming();
diff --git a/src/core/file_sys/romfs_factory.cpp b/src/core/file_sys/romfs_factory.cpp
index de6ab721d..aa7f3072f 100644
--- a/src/core/file_sys/romfs_factory.cpp
+++ b/src/core/file_sys/romfs_factory.cpp
@@ -13,7 +13,7 @@
 #include "core/file_sys/patch_manager.h"
 #include "core/file_sys/registered_cache.h"
 #include "core/file_sys/romfs_factory.h"
-#include "core/hle/kernel/process.h"
+#include "core/hle/kernel/k_process.h"
 #include "core/hle/service/filesystem/filesystem.h"
 #include "core/loader/loader.h"
 
diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp
index fa68af3a8..f973d1d21 100644
--- a/src/core/file_sys/savedata_factory.cpp
+++ b/src/core/file_sys/savedata_factory.cpp
@@ -9,7 +9,7 @@
 #include "core/core.h"
 #include "core/file_sys/savedata_factory.h"
 #include "core/file_sys/vfs.h"
-#include "core/hle/kernel/process.h"
+#include "core/hle/kernel/k_process.h"
 
 namespace FileSys {
 
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h
index 56cc911d1..0906b8cfb 100644
--- a/src/core/hle/ipc_helpers.h
+++ b/src/core/hle/ipc_helpers.h
@@ -13,12 +13,9 @@
 #include "common/assert.h"
 #include "common/common_types.h"
 #include "core/hle/ipc.h"
-#include "core/hle/kernel/client_port.h"
-#include "core/hle/kernel/client_session.h"
 #include "core/hle/kernel/hle_ipc.h"
-#include "core/hle/kernel/object.h"
-#include "core/hle/kernel/server_session.h"
-#include "core/hle/kernel/session.h"
+#include "core/hle/kernel/k_client_port.h"
+#include "core/hle/kernel/k_session.h"
 #include "core/hle/result.h"
 
 namespace IPC {
@@ -137,9 +134,11 @@ public:
         if (context->Session()->IsDomain()) {
             context->AddDomainObject(std::move(iface));
         } else {
-            auto [client, server] = Kernel::Session::Create(kernel, iface->GetServiceName());
-            context->AddMoveObject(std::move(client));
-            iface->ClientConnected(std::move(server));
+            auto* session = Kernel::KSession::Create(kernel);
+            session->Initialize(nullptr, iface->GetServiceName());
+
+            context->AddMoveObject(&session->GetClientSession());
+            iface->ClientConnected(&session->GetServerSession());
         }
     }
 
@@ -215,10 +214,16 @@ public:
     void PushRaw(const T& value);
 
     template <typename... O>
-    void PushMoveObjects(std::shared_ptr<O>... pointers);
+    void PushMoveObjects(O*... pointers);
 
     template <typename... O>
-    void PushCopyObjects(std::shared_ptr<O>... pointers);
+    void PushMoveObjects(O&... pointers);
+
+    template <typename... O>
+    void PushCopyObjects(O*... pointers);
+
+    template <typename... O>
+    void PushCopyObjects(O&... pointers);
 
 private:
     u32 normal_params_size{};
@@ -301,18 +306,34 @@ void ResponseBuilder::Push(const First& first_value, const Other&... other_value
 }
 
 template <typename... O>
-inline void ResponseBuilder::PushCopyObjects(std::shared_ptr<O>... pointers) {
+inline void ResponseBuilder::PushCopyObjects(O*... pointers) {
     auto objects = {pointers...};
     for (auto& object : objects) {
-        context->AddCopyObject(std::move(object));
+        context->AddCopyObject(object);
     }
 }
 
 template <typename... O>
-inline void ResponseBuilder::PushMoveObjects(std::shared_ptr<O>... pointers) {
+inline void ResponseBuilder::PushCopyObjects(O&... pointers) {
+    auto objects = {&pointers...};
+    for (auto& object : objects) {
+        context->AddCopyObject(object);
+    }
+}
+
+template <typename... O>
+inline void ResponseBuilder::PushMoveObjects(O*... pointers) {
     auto objects = {pointers...};
     for (auto& object : objects) {
-        context->AddMoveObject(std::move(object));
+        context->AddMoveObject(object);
+    }
+}
+
+template <typename... O>
+inline void ResponseBuilder::PushMoveObjects(O&... pointers) {
+    auto objects = {&pointers...};
+    for (auto& object : objects) {
+        context->AddMoveObject(object);
     }
 }
 
@@ -359,12 +380,6 @@ public:
     template <typename T>
     T PopRaw();
 
-    template <typename T>
-    std::shared_ptr<T> GetMoveObject(std::size_t index);
-
-    template <typename T>
-    std::shared_ptr<T> GetCopyObject(std::size_t index);
-
     template <class T>
     std::shared_ptr<T> PopIpcInterface() {
         ASSERT(context->Session()->IsDomain());
@@ -469,14 +484,4 @@ void RequestParser::Pop(First& first_value, Other&... other_values) {
     Pop(other_values...);
 }
 
-template <typename T>
-std::shared_ptr<T> RequestParser::GetMoveObject(std::size_t index) {
-    return context->GetMoveObject<T>(index);
-}
-
-template <typename T>
-std::shared_ptr<T> RequestParser::GetCopyObject(std::size_t index) {
-    return context->GetCopyObject<T>(index);
-}
-
 } // namespace IPC
diff --git a/src/core/hle/kernel/client_port.cpp b/src/core/hle/kernel/client_port.cpp
deleted file mode 100644
index 0b6957e31..000000000
--- a/src/core/hle/kernel/client_port.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2016 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include "core/hle/kernel/client_port.h"
-#include "core/hle/kernel/client_session.h"
-#include "core/hle/kernel/hle_ipc.h"
-#include "core/hle/kernel/object.h"
-#include "core/hle/kernel/server_port.h"
-#include "core/hle/kernel/session.h"
-#include "core/hle/kernel/svc_results.h"
-
-namespace Kernel {
-
-ClientPort::ClientPort(KernelCore& kernel) : Object{kernel} {}
-ClientPort::~ClientPort() = default;
-
-std::shared_ptr<ServerPort> ClientPort::GetServerPort() const {
-    return server_port;
-}
-
-ResultVal<std::shared_ptr<ClientSession>> ClientPort::Connect() {
-    if (active_sessions >= max_sessions) {
-        return ResultMaxConnectionsReached;
-    }
-    active_sessions++;
-
-    auto [client, server] = Kernel::Session::Create(kernel, name);
-
-    if (server_port->HasHLEHandler()) {
-        server_port->GetHLEHandler()->ClientConnected(std::move(server));
-    } else {
-        server_port->AppendPendingSession(std::move(server));
-    }
-
-    return MakeResult(std::move(client));
-}
-
-void ClientPort::ConnectionClosed() {
-    if (active_sessions == 0) {
-        return;
-    }
-
-    --active_sessions;
-}
-
-} // namespace Kernel
diff --git a/src/core/hle/kernel/client_port.h b/src/core/hle/kernel/client_port.h
deleted file mode 100644
index 77559ebf9..000000000
--- a/src/core/hle/kernel/client_port.h
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright 2016 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <memory>
-#include <string>
-
-#include "common/common_types.h"
-#include "core/hle/kernel/object.h"
-#include "core/hle/result.h"
-
-namespace Kernel {
-
-class ClientSession;
-class KernelCore;
-class ServerPort;
-
-class ClientPort final : public Object {
-public:
-    explicit ClientPort(KernelCore& kernel);
-    ~ClientPort() override;
-
-    friend class ServerPort;
-    std::string GetTypeName() const override {
-        return "ClientPort";
-    }
-    std::string GetName() const override {
-        return name;
-    }
-
-    static constexpr HandleType HANDLE_TYPE = HandleType::ClientPort;
-    HandleType GetHandleType() const override {
-        return HANDLE_TYPE;
-    }
-
-    std::shared_ptr<ServerPort> GetServerPort() const;
-
-    /**
-     * Creates a new Session pair, adds the created ServerSession to the associated ServerPort's
-     * list of pending sessions, and signals the ServerPort, causing any threads
-     * waiting on it to awake.
-     * @returns ClientSession The client endpoint of the created Session pair, or error code.
-     */
-    ResultVal<std::shared_ptr<ClientSession>> Connect();
-
-    /**
-     * Signifies that a previously active connection has been closed,
-     * decreasing the total number of active connections to this port.
-     */
-    void ConnectionClosed();
-
-    void Finalize() override {}
-
-private:
-    std::shared_ptr<ServerPort> server_port; ///< ServerPort associated with this client port.
-    u32 max_sessions = 0;    ///< Maximum number of simultaneous sessions the port can have
-    u32 active_sessions = 0; ///< Number of currently open sessions to this port
-    std::string name;        ///< Name of client port (optional)
-};
-
-} // namespace Kernel
diff --git a/src/core/hle/kernel/client_session.cpp b/src/core/hle/kernel/client_session.cpp
deleted file mode 100644
index e230f365a..000000000
--- a/src/core/hle/kernel/client_session.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include "core/hle/kernel/client_session.h"
-#include "core/hle/kernel/hle_ipc.h"
-#include "core/hle/kernel/k_thread.h"
-#include "core/hle/kernel/server_session.h"
-#include "core/hle/kernel/session.h"
-#include "core/hle/kernel/svc_results.h"
-#include "core/hle/result.h"
-
-namespace Kernel {
-
-ClientSession::ClientSession(KernelCore& kernel) : KSynchronizationObject{kernel} {}
-
-ClientSession::~ClientSession() {
-    // This destructor will be called automatically when the last ClientSession handle is closed by
-    // the emulated application.
-    if (parent->Server()) {
-        parent->Server()->ClientDisconnected();
-    }
-}
-
-bool ClientSession::IsSignaled() const {
-    UNIMPLEMENTED();
-    return true;
-}
-
-ResultVal<std::shared_ptr<ClientSession>> ClientSession::Create(KernelCore& kernel,
-                                                                std::shared_ptr<Session> parent,
-                                                                std::string name) {
-    std::shared_ptr<ClientSession> client_session{std::make_shared<ClientSession>(kernel)};
-
-    client_session->name = std::move(name);
-    client_session->parent = std::move(parent);
-
-    return MakeResult(std::move(client_session));
-}
-
-ResultCode ClientSession::SendSyncRequest(std::shared_ptr<KThread> thread,
-                                          Core::Memory::Memory& memory,
-                                          Core::Timing::CoreTiming& core_timing) {
-    // Keep ServerSession alive until we're done working with it.
-    if (!parent->Server()) {
-        return ResultSessionClosedByRemote;
-    }
-
-    // Signal the server session that new data is available
-    return parent->Server()->HandleSyncRequest(std::move(thread), memory, core_timing);
-}
-
-} // namespace Kernel
diff --git a/src/core/hle/kernel/client_session.h b/src/core/hle/kernel/client_session.h
deleted file mode 100644
index 85aafeaf4..000000000
--- a/src/core/hle/kernel/client_session.h
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <memory>
-#include <string>
-
-#include "core/hle/kernel/k_synchronization_object.h"
-#include "core/hle/result.h"
-
-union ResultCode;
-
-namespace Core::Memory {
-class Memory;
-}
-
-namespace Core::Timing {
-class CoreTiming;
-}
-
-namespace Kernel {
-
-class KernelCore;
-class Session;
-class KThread;
-
-class ClientSession final : public KSynchronizationObject {
-public:
-    explicit ClientSession(KernelCore& kernel);
-    ~ClientSession() override;
-
-    friend class Session;
-
-    std::string GetTypeName() const override {
-        return "ClientSession";
-    }
-
-    std::string GetName() const override {
-        return name;
-    }
-
-    static constexpr HandleType HANDLE_TYPE = HandleType::ClientSession;
-    HandleType GetHandleType() const override {
-        return HANDLE_TYPE;
-    }
-
-    ResultCode SendSyncRequest(std::shared_ptr<KThread> thread, Core::Memory::Memory& memory,
-                               Core::Timing::CoreTiming& core_timing);
-
-    bool IsSignaled() const override;
-
-    void Finalize() override {}
-
-private:
-    static ResultVal<std::shared_ptr<ClientSession>> Create(KernelCore& kernel,
-                                                            std::shared_ptr<Session> parent,
-                                                            std::string name = "Unknown");
-
-    /// The parent session, which links to the server endpoint.
-    std::shared_ptr<Session> parent;
-
-    /// Name of the client session (optional)
-    std::string name;
-};
-
-} // namespace Kernel
diff --git a/src/core/hle/kernel/global_scheduler_context.cpp b/src/core/hle/kernel/global_scheduler_context.cpp
index c6838649f..7c87cbada 100644
--- a/src/core/hle/kernel/global_scheduler_context.cpp
+++ b/src/core/hle/kernel/global_scheduler_context.cpp
@@ -17,12 +17,12 @@ GlobalSchedulerContext::GlobalSchedulerContext(KernelCore& kernel)
 
 GlobalSchedulerContext::~GlobalSchedulerContext() = default;
 
-void GlobalSchedulerContext::AddThread(std::shared_ptr<KThread> thread) {
+void GlobalSchedulerContext::AddThread(KThread* thread) {
     std::scoped_lock lock{global_list_guard};
-    thread_list.push_back(std::move(thread));
+    thread_list.push_back(thread);
 }
 
-void GlobalSchedulerContext::RemoveThread(std::shared_ptr<KThread> thread) {
+void GlobalSchedulerContext::RemoveThread(KThread* thread) {
     std::scoped_lock lock{global_list_guard};
     thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread),
                       thread_list.end());
diff --git a/src/core/hle/kernel/global_scheduler_context.h b/src/core/hle/kernel/global_scheduler_context.h
index 11592843e..ba8b67fd1 100644
--- a/src/core/hle/kernel/global_scheduler_context.h
+++ b/src/core/hle/kernel/global_scheduler_context.h
@@ -38,13 +38,13 @@ public:
     ~GlobalSchedulerContext();
 
     /// Adds a new thread to the scheduler
-    void AddThread(std::shared_ptr<KThread> thread);
+    void AddThread(KThread* thread);
 
     /// Removes a thread from the scheduler
-    void RemoveThread(std::shared_ptr<KThread> thread);
+    void RemoveThread(KThread* thread);
 
     /// Returns a list of all threads managed by the scheduler
-    [[nodiscard]] const std::vector<std::shared_ptr<KThread>>& GetThreadList() const {
+    [[nodiscard]] const std::vector<KThread*>& GetThreadList() const {
         return thread_list;
     }
 
@@ -79,7 +79,7 @@ private:
     LockType scheduler_lock;
 
     /// Lists all thread ids that aren't deleted/etc.
-    std::vector<std::shared_ptr<KThread>> thread_list;
+    std::vector<KThread*> thread_list;
     Common::SpinLock global_list_guard{};
 };
 
diff --git a/src/core/hle/kernel/handle_table.cpp b/src/core/hle/kernel/handle_table.cpp
deleted file mode 100644
index f96d34078..000000000
--- a/src/core/hle/kernel/handle_table.cpp
+++ /dev/null
@@ -1,131 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include <utility>
-#include "common/assert.h"
-#include "common/logging/log.h"
-#include "core/core.h"
-#include "core/hle/kernel/handle_table.h"
-#include "core/hle/kernel/k_scheduler.h"
-#include "core/hle/kernel/k_thread.h"
-#include "core/hle/kernel/kernel.h"
-#include "core/hle/kernel/process.h"
-#include "core/hle/kernel/svc_results.h"
-
-namespace Kernel {
-namespace {
-constexpr u16 GetSlot(Handle handle) {
-    return static_cast<u16>(handle >> 15);
-}
-
-constexpr u16 GetGeneration(Handle handle) {
-    return static_cast<u16>(handle & 0x7FFF);
-}
-} // Anonymous namespace
-
-HandleTable::HandleTable(KernelCore& kernel) : kernel{kernel} {
-    Clear();
-}
-
-HandleTable::~HandleTable() = default;
-
-ResultCode HandleTable::SetSize(s32 handle_table_size) {
-    if (static_cast<u32>(handle_table_size) > MAX_COUNT) {
-        LOG_ERROR(Kernel, "Handle table size {} is greater than {}", handle_table_size, MAX_COUNT);
-        return ResultOutOfMemory;
-    }
-
-    // Values less than or equal to zero indicate to use the maximum allowable
-    // size for the handle table in the actual kernel, so we ignore the given
-    // value in that case, since we assume this by default unless this function
-    // is called.
-    if (handle_table_size > 0) {
-        table_size = static_cast<u16>(handle_table_size);
-    }
-
-    return RESULT_SUCCESS;
-}
-
-ResultVal<Handle> HandleTable::Create(std::shared_ptr<Object> obj) {
-    DEBUG_ASSERT(obj != nullptr);
-
-    const u16 slot = next_free_slot;
-    if (slot >= table_size) {
-        LOG_ERROR(Kernel, "Unable to allocate Handle, too many slots in use.");
-        return ResultHandleTableFull;
-    }
-    next_free_slot = generations[slot];
-
-    const u16 generation = next_generation++;
-
-    // Overflow count so it fits in the 15 bits dedicated to the generation in the handle.
-    // Horizon OS uses zero to represent an invalid handle, so skip to 1.
-    if (next_generation >= (1 << 15)) {
-        next_generation = 1;
-    }
-
-    generations[slot] = generation;
-    objects[slot] = std::move(obj);
-
-    Handle handle = generation | (slot << 15);
-    return MakeResult<Handle>(handle);
-}
-
-ResultVal<Handle> HandleTable::Duplicate(Handle handle) {
-    std::shared_ptr<Object> object = GetGeneric(handle);
-    if (object == nullptr) {
-        LOG_ERROR(Kernel, "Tried to duplicate invalid handle: {:08X}", handle);
-        return ResultInvalidHandle;
-    }
-    return Create(std::move(object));
-}
-
-ResultCode HandleTable::Close(Handle handle) {
-    if (!IsValid(handle)) {
-        LOG_ERROR(Kernel, "Handle is not valid! handle={:08X}", handle);
-        return ResultInvalidHandle;
-    }
-
-    const u16 slot = GetSlot(handle);
-
-    if (objects[slot].use_count() == 1) {
-        objects[slot]->Finalize();
-    }
-
-    objects[slot] = nullptr;
-
-    generations[slot] = next_free_slot;
-    next_free_slot = slot;
-    return RESULT_SUCCESS;
-}
-
-bool HandleTable::IsValid(Handle handle) const {
-    const std::size_t slot = GetSlot(handle);
-    const u16 generation = GetGeneration(handle);
-
-    return slot < table_size && objects[slot] != nullptr && generations[slot] == generation;
-}
-
-std::shared_ptr<Object> HandleTable::GetGeneric(Handle handle) const {
-    if (handle == CurrentThread) {
-        return SharedFrom(kernel.CurrentScheduler()->GetCurrentThread());
-    } else if (handle == CurrentProcess) {
-        return SharedFrom(kernel.CurrentProcess());
-    }
-
-    if (!IsValid(handle)) {
-        return nullptr;
-    }
-    return objects[GetSlot(handle)];
-}
-
-void HandleTable::Clear() {
-    for (u16 i = 0; i < table_size; ++i) {
-        generations[i] = static_cast<u16>(i + 1);
-        objects[i] = nullptr;
-    }
-    next_free_slot = 0;
-}
-
-} // namespace Kernel
diff --git a/src/core/hle/kernel/handle_table.h b/src/core/hle/kernel/handle_table.h
deleted file mode 100644
index c9dab8cdd..000000000
--- a/src/core/hle/kernel/handle_table.h
+++ /dev/null
@@ -1,144 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <array>
-#include <cstddef>
-#include <memory>
-
-#include "common/common_types.h"
-#include "core/hle/kernel/object.h"
-#include "core/hle/result.h"
-
-namespace Kernel {
-
-class KernelCore;
-
-enum KernelHandle : Handle {
-    InvalidHandle = 0,
-    CurrentThread = 0xFFFF8000,
-    CurrentProcess = 0xFFFF8001,
-};
-
-/**
- * This class allows the creation of Handles, which are references to objects that can be tested
- * for validity and looked up. Here they are used to pass references to kernel objects to/from the
- * emulated process. it has been designed so that it follows the same handle format and has
- * approximately the same restrictions as the handle manager in the CTR-OS.
- *
- * Handles contain two sub-fields: a slot index (bits 31:15) and a generation value (bits 14:0).
- * The slot index is used to index into the arrays in this class to access the data corresponding
- * to the Handle.
- *
- * To prevent accidental use of a freed Handle whose slot has already been reused, a global counter
- * is kept and incremented every time a Handle is created. This is the Handle's "generation". The
- * value of the counter is stored into the Handle as well as in the handle table (in the
- * "generations" array). When looking up a handle, the Handle's generation must match with the
- * value stored on the class, otherwise the Handle is considered invalid.
- *
- * To find free slots when allocating a Handle without needing to scan the entire object array, the
- * generations field of unallocated slots is re-purposed as a linked list of indices to free slots.
- * When a Handle is created, an index is popped off the list and used for the new Handle. When it
- * is destroyed, it is again pushed onto the list to be re-used by the next allocation. It is
- * likely that this allocation strategy differs from the one used in CTR-OS, but this hasn't been
- * verified and isn't likely to cause any problems.
- */
-class HandleTable final : NonCopyable {
-public:
-    /// This is the maximum limit of handles allowed per process in Horizon
-    static constexpr std::size_t MAX_COUNT = 1024;
-
-    explicit HandleTable(KernelCore& kernel);
-    ~HandleTable();
-
-    /**
-     * Sets the number of handles that may be in use at one time
-     * for this handle table.
-     *
-     * @param handle_table_size The desired size to limit the handle table to.
-     *
-     * @returns an error code indicating if initialization was successful.
-     *          If initialization was not successful, then ERR_OUT_OF_MEMORY
-     *          will be returned.
-     *
-     * @pre handle_table_size must be within the range [0, 1024]
-     */
-    ResultCode SetSize(s32 handle_table_size);
-
-    /**
-     * Allocates a handle for the given object.
-     * @return The created Handle or one of the following errors:
-     *           - `ERR_HANDLE_TABLE_FULL`: the maximum number of handles has been exceeded.
-     */
-    ResultVal<Handle> Create(std::shared_ptr<Object> obj);
-
-    /**
-     * Returns a new handle that points to the same object as the passed in handle.
-     * @return The duplicated Handle or one of the following errors:
-     *           - `ERR_INVALID_HANDLE`: an invalid handle was passed in.
-     *           - Any errors returned by `Create()`.
-     */
-    ResultVal<Handle> Duplicate(Handle handle);
-
-    /**
-     * Closes a handle, removing it from the table and decreasing the object's ref-count.
-     * @return `RESULT_SUCCESS` or one of the following errors:
-     *           - `ERR_INVALID_HANDLE`: an invalid handle was passed in.
-     */
-    ResultCode Close(Handle handle);
-
-    /// Checks if a handle is valid and points to an existing object.
-    bool IsValid(Handle handle) const;
-
-    /**
-     * Looks up a handle.
-     * @return Pointer to the looked-up object, or `nullptr` if the handle is not valid.
-     */
-    std::shared_ptr<Object> GetGeneric(Handle handle) const;
-
-    /**
-     * Looks up a handle while verifying its type.
-     * @return Pointer to the looked-up object, or `nullptr` if the handle is not valid or its
-     *         type differs from the requested one.
-     */
-    template <class T>
-    std::shared_ptr<T> Get(Handle handle) const {
-        return DynamicObjectCast<T>(GetGeneric(handle));
-    }
-
-    /// Closes all handles held in this table.
-    void Clear();
-
-private:
-    /// Stores the Object referenced by the handle or null if the slot is empty.
-    std::array<std::shared_ptr<Object>, MAX_COUNT> objects;
-
-    /**
-     * The value of `next_generation` when the handle was created, used to check for validity. For
-     * empty slots, contains the index of the next free slot in the list.
-     */
-    std::array<u16, MAX_COUNT> generations;
-
-    /**
-     * The limited size of the handle table. This can be specified by process
-     * capabilities in order to restrict the overall number of handles that
-     * can be created in a process instance
-     */
-    u16 table_size = static_cast<u16>(MAX_COUNT);
-
-    /**
-     * Global counter of the number of created handles. Stored in `generations` when a handle is
-     * created, and wraps around to 1 when it hits 0x8000.
-     */
-    u16 next_generation = 1;
-
-    /// Head of the free slots linked list.
-    u16 next_free_slot = 0;
-
-    /// Underlying kernel instance that this handle table operates under.
-    KernelCore& kernel;
-};
-
-} // namespace Kernel
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index 2b363b1d9..b505d20a6 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -14,17 +14,16 @@
 #include "common/common_types.h"
 #include "common/logging/log.h"
 #include "core/hle/ipc_helpers.h"
-#include "core/hle/kernel/handle_table.h"
 #include "core/hle/kernel/hle_ipc.h"
+#include "core/hle/kernel/k_handle_table.h"
+#include "core/hle/kernel/k_process.h"
 #include "core/hle/kernel/k_readable_event.h"
 #include "core/hle/kernel/k_scheduler.h"
 #include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
+#include "core/hle/kernel/k_server_session.h"
 #include "core/hle/kernel/k_thread.h"
 #include "core/hle/kernel/k_writable_event.h"
 #include "core/hle/kernel/kernel.h"
-#include "core/hle/kernel/object.h"
-#include "core/hle/kernel/process.h"
-#include "core/hle/kernel/server_session.h"
 #include "core/hle/kernel/svc_results.h"
 #include "core/hle/kernel/time_manager.h"
 #include "core/memory.h"
@@ -35,28 +34,23 @@ SessionRequestHandler::SessionRequestHandler() = default;
 
 SessionRequestHandler::~SessionRequestHandler() = default;
 
-void SessionRequestHandler::ClientConnected(std::shared_ptr<ServerSession> server_session) {
-    server_session->SetHleHandler(shared_from_this());
-    connected_sessions.push_back(std::move(server_session));
+void SessionRequestHandler::ClientConnected(KServerSession* session) {
+    session->SetHleHandler(shared_from_this());
 }
 
-void SessionRequestHandler::ClientDisconnected(
-    const std::shared_ptr<ServerSession>& server_session) {
-    server_session->SetHleHandler(nullptr);
-    boost::range::remove_erase(connected_sessions, server_session);
+void SessionRequestHandler::ClientDisconnected(KServerSession* session) {
+    session->SetHleHandler(nullptr);
 }
 
-HLERequestContext::HLERequestContext(KernelCore& kernel, Core::Memory::Memory& memory,
-                                     std::shared_ptr<ServerSession> server_session,
-                                     std::shared_ptr<KThread> thread)
-    : server_session(std::move(server_session)),
-      thread(std::move(thread)), kernel{kernel}, memory{memory} {
+HLERequestContext::HLERequestContext(KernelCore& kernel_, Core::Memory::Memory& memory_,
+                                     KServerSession* server_session_, KThread* thread_)
+    : server_session(server_session_), thread(thread_), kernel{kernel_}, memory{memory_} {
     cmd_buf[0] = 0;
 }
 
 HLERequestContext::~HLERequestContext() = default;
 
-void HLERequestContext::ParseCommandBuffer(const HandleTable& handle_table, u32_le* src_cmdbuf,
+void HLERequestContext::ParseCommandBuffer(const KHandleTable& handle_table, u32_le* src_cmdbuf,
                                            bool incoming) {
     IPC::RequestParser rp(src_cmdbuf);
     command_header = rp.PopRaw<IPC::CommandHeader>();
@@ -77,12 +71,12 @@ void HLERequestContext::ParseCommandBuffer(const HandleTable& handle_table, u32_
             for (u32 handle = 0; handle < handle_descriptor_header->num_handles_to_copy; ++handle) {
                 const u32 copy_handle{rp.Pop<Handle>()};
                 copy_handles.push_back(copy_handle);
-                copy_objects.push_back(handle_table.GetGeneric(copy_handle));
+                copy_objects.push_back(handle_table.GetObject(copy_handle).GetPointerUnsafe());
             }
             for (u32 handle = 0; handle < handle_descriptor_header->num_handles_to_move; ++handle) {
                 const u32 move_handle{rp.Pop<Handle>()};
                 move_handles.push_back(move_handle);
-                move_objects.push_back(handle_table.GetGeneric(move_handle));
+                move_objects.push_back(handle_table.GetObject(move_handle).GetPointerUnsafe());
             }
         } else {
             // For responses we just ignore the handles, they're empty and will be populated when
@@ -169,7 +163,7 @@ void HLERequestContext::ParseCommandBuffer(const HandleTable& handle_table, u32_
     rp.Skip(1, false); // The command is actually an u64, but we don't use the high part.
 }
 
-ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const HandleTable& handle_table,
+ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const KHandleTable& handle_table,
                                                                 u32_le* src_cmdbuf) {
     ParseCommandBuffer(handle_table, src_cmdbuf, true);
     if (command_header->type == IPC::CommandType::Close) {
@@ -223,12 +217,12 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(KThread& thread) {
         // for specific values in each of these descriptors.
         for (auto& object : copy_objects) {
             ASSERT(object != nullptr);
-            dst_cmdbuf[current_offset++] = handle_table.Create(object).Unwrap();
+            R_TRY(handle_table.Add(&dst_cmdbuf[current_offset++], object));
         }
 
         for (auto& object : move_objects) {
             ASSERT(object != nullptr);
-            dst_cmdbuf[current_offset++] = handle_table.Create(object).Unwrap();
+            R_TRY(handle_table.Add(&dst_cmdbuf[current_offset++], object));
         }
     }
 
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h
index 6fba42615..fa031c121 100644
--- a/src/core/hle/kernel/hle_ipc.h
+++ b/src/core/hle/kernel/hle_ipc.h
@@ -16,7 +16,8 @@
 #include "common/concepts.h"
 #include "common/swap.h"
 #include "core/hle/ipc.h"
-#include "core/hle/kernel/object.h"
+#include "core/hle/kernel/k_auto_object.h"
+#include "core/hle/kernel/svc_common.h"
 
 union ResultCode;
 
@@ -35,13 +36,14 @@ class ServiceFrameworkBase;
 namespace Kernel {
 
 class Domain;
-class HandleTable;
 class HLERequestContext;
 class KernelCore;
-class Process;
-class ServerSession;
+class KHandleTable;
+class KProcess;
+class KServerSession;
 class KThread;
 class KReadableEvent;
+class KSession;
 class KWritableEvent;
 
 enum class ThreadWakeupReason;
@@ -71,20 +73,14 @@ public:
      * associated ServerSession alive for the duration of the connection.
      * @param server_session Owning pointer to the ServerSession associated with the connection.
      */
-    void ClientConnected(std::shared_ptr<ServerSession> server_session);
+    void ClientConnected(KServerSession* session);
 
     /**
      * Signals that a client has just disconnected from this HLE handler and releases the
      * associated ServerSession.
      * @param server_session ServerSession associated with the connection.
      */
-    void ClientDisconnected(const std::shared_ptr<ServerSession>& server_session);
-
-protected:
-    /// List of sessions that are connected to this handler.
-    /// A ServerSession whose server endpoint is an HLE implementation is kept alive by this list
-    /// for the duration of the connection.
-    std::vector<std::shared_ptr<ServerSession>> connected_sessions;
+    void ClientDisconnected(KServerSession* session);
 };
 
 /**
@@ -109,8 +105,7 @@ protected:
 class HLERequestContext {
 public:
     explicit HLERequestContext(KernelCore& kernel, Core::Memory::Memory& memory,
-                               std::shared_ptr<ServerSession> session,
-                               std::shared_ptr<KThread> thread);
+                               KServerSession* session, KThread* thread);
     ~HLERequestContext();
 
     /// Returns a pointer to the IPC command buffer for this request.
@@ -122,12 +117,12 @@ public:
      * Returns the session through which this request was made. This can be used as a map key to
      * access per-client data on services.
      */
-    const std::shared_ptr<Kernel::ServerSession>& Session() const {
+    Kernel::KServerSession* Session() {
         return server_session;
     }
 
     /// Populates this context with data from the requesting process/thread.
-    ResultCode PopulateFromIncomingCommandBuffer(const HandleTable& handle_table,
+    ResultCode PopulateFromIncomingCommandBuffer(const KHandleTable& handle_table,
                                                  u32_le* src_cmdbuf);
 
     /// Writes data from this context back to the requesting process/thread.
@@ -218,22 +213,12 @@ public:
         return move_handles.at(index);
     }
 
-    template <typename T>
-    std::shared_ptr<T> GetCopyObject(std::size_t index) {
-        return DynamicObjectCast<T>(copy_objects.at(index));
+    void AddMoveObject(KAutoObject* object) {
+        move_objects.emplace_back(object);
     }
 
-    template <typename T>
-    std::shared_ptr<T> GetMoveObject(std::size_t index) {
-        return DynamicObjectCast<T>(move_objects.at(index));
-    }
-
-    void AddMoveObject(std::shared_ptr<Object> object) {
-        move_objects.emplace_back(std::move(object));
-    }
-
-    void AddCopyObject(std::shared_ptr<Object> object) {
-        copy_objects.emplace_back(std::move(object));
+    void AddCopyObject(KAutoObject* object) {
+        copy_objects.emplace_back(object);
     }
 
     void AddDomainObject(std::shared_ptr<SessionRequestHandler> object) {
@@ -276,10 +261,6 @@ public:
         return *thread;
     }
 
-    const KThread& GetThread() const {
-        return *thread;
-    }
-
     bool IsThreadWaiting() const {
         return is_thread_waiting;
     }
@@ -287,16 +268,17 @@ public:
 private:
     friend class IPC::ResponseBuilder;
 
-    void ParseCommandBuffer(const HandleTable& handle_table, u32_le* src_cmdbuf, bool incoming);
+    void ParseCommandBuffer(const KHandleTable& handle_table, u32_le* src_cmdbuf, bool incoming);
 
     std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf;
-    std::shared_ptr<Kernel::ServerSession> server_session;
-    std::shared_ptr<KThread> thread;
+    Kernel::KServerSession* server_session{};
+    KThread* thread;
+
     // TODO(yuriks): Check common usage of this and optimize size accordingly
     boost::container::small_vector<Handle, 8> move_handles;
     boost::container::small_vector<Handle, 8> copy_handles;
-    boost::container::small_vector<std::shared_ptr<Object>, 8> move_objects;
-    boost::container::small_vector<std::shared_ptr<Object>, 8> copy_objects;
+    boost::container::small_vector<KAutoObject*, 8> move_objects;
+    boost::container::small_vector<KAutoObject*, 8> copy_objects;
     boost::container::small_vector<std::shared_ptr<SessionRequestHandler>, 8> domain_objects;
 
     std::optional<IPC::CommandHeader> command_header;
diff --git a/src/core/hle/kernel/init/init_slab_setup.cpp b/src/core/hle/kernel/init/init_slab_setup.cpp
new file mode 100644
index 000000000..69ae405e6
--- /dev/null
+++ b/src/core/hle/kernel/init/init_slab_setup.cpp
@@ -0,0 +1,192 @@
+// Copyright 2021 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/alignment.h"
+#include "common/assert.h"
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+#include "core/core.h"
+#include "core/hardware_properties.h"
+#include "core/hle/kernel/init/init_slab_setup.h"
+#include "core/hle/kernel/k_event.h"
+#include "core/hle/kernel/k_memory_layout.h"
+#include "core/hle/kernel/k_memory_manager.h"
+#include "core/hle/kernel/k_port.h"
+#include "core/hle/kernel/k_process.h"
+#include "core/hle/kernel/k_resource_limit.h"
+#include "core/hle/kernel/k_session.h"
+#include "core/hle/kernel/k_shared_memory.h"
+#include "core/hle/kernel/k_system_control.h"
+#include "core/hle/kernel/k_thread.h"
+#include "core/hle/kernel/k_transfer_memory.h"
+#include "core/hle/kernel/memory_types.h"
+#include "core/memory.h"
+
+namespace Kernel::Init {
+
+#define SLAB_COUNT(CLASS) kernel.SlabResourceCounts().num_##CLASS
+
+#define FOREACH_SLAB_TYPE(HANDLER, ...)                                                            \
+    HANDLER(KProcess, (SLAB_COUNT(KProcess)), ##__VA_ARGS__)                                       \
+    HANDLER(KThread, (SLAB_COUNT(KThread)), ##__VA_ARGS__)                                         \
+    HANDLER(KEvent, (SLAB_COUNT(KEvent)), ##__VA_ARGS__)                                           \
+    HANDLER(KPort, (SLAB_COUNT(KPort)), ##__VA_ARGS__)                                             \
+    HANDLER(KSharedMemory, (SLAB_COUNT(KSharedMemory)), ##__VA_ARGS__)                             \
+    HANDLER(KTransferMemory, (SLAB_COUNT(KTransferMemory)), ##__VA_ARGS__)                         \
+    HANDLER(KSession, (SLAB_COUNT(KSession)), ##__VA_ARGS__)                                       \
+    HANDLER(KResourceLimit, (SLAB_COUNT(KResourceLimit)), ##__VA_ARGS__)
+
+namespace {
+
+#define DEFINE_SLAB_TYPE_ENUM_MEMBER(NAME, COUNT, ...) KSlabType_##NAME,
+
+enum KSlabType : u32 {
+    FOREACH_SLAB_TYPE(DEFINE_SLAB_TYPE_ENUM_MEMBER) KSlabType_Count,
+};
+
+#undef DEFINE_SLAB_TYPE_ENUM_MEMBER
+
+// Constexpr counts.
+constexpr size_t SlabCountKProcess = 80;
+constexpr size_t SlabCountKThread = 800;
+constexpr size_t SlabCountKEvent = 700;
+constexpr size_t SlabCountKInterruptEvent = 100;
+constexpr size_t SlabCountKPort = 256 + 0x20; // Extra 0x20 ports over Nintendo for homebrew.
+constexpr size_t SlabCountKSharedMemory = 80;
+constexpr size_t SlabCountKTransferMemory = 200;
+constexpr size_t SlabCountKCodeMemory = 10;
+constexpr size_t SlabCountKDeviceAddressSpace = 300;
+constexpr size_t SlabCountKSession = 933;
+constexpr size_t SlabCountKLightSession = 100;
+constexpr size_t SlabCountKObjectName = 7;
+constexpr size_t SlabCountKResourceLimit = 5;
+constexpr size_t SlabCountKDebug = Core::Hardware::NUM_CPU_CORES;
+constexpr size_t SlabCountKAlpha = 1;
+constexpr size_t SlabCountKBeta = 6;
+
+constexpr size_t SlabCountExtraKThread = 160;
+
+template <typename T>
+VAddr InitializeSlabHeap(Core::System& system, KMemoryLayout& memory_layout, VAddr address,
+                         size_t num_objects) {
+    const size_t size = Common::AlignUp(sizeof(T) * num_objects, alignof(void*));
+    VAddr start = Common::AlignUp(address, alignof(T));
+
+    if (size > 0) {
+        const KMemoryRegion* region = memory_layout.FindVirtual(start + size - 1);
+        ASSERT(region != nullptr);
+        ASSERT(region->IsDerivedFrom(KMemoryRegionType_KernelSlab));
+        T::InitializeSlabHeap(system.Kernel(), system.Memory().GetKernelBuffer(start, size), size);
+    }
+
+    return start + size;
+}
+
+} // namespace
+
+KSlabResourceCounts KSlabResourceCounts::CreateDefault() {
+    return {
+        .num_KProcess = SlabCountKProcess,
+        .num_KThread = SlabCountKThread,
+        .num_KEvent = SlabCountKEvent,
+        .num_KInterruptEvent = SlabCountKInterruptEvent,
+        .num_KPort = SlabCountKPort,
+        .num_KSharedMemory = SlabCountKSharedMemory,
+        .num_KTransferMemory = SlabCountKTransferMemory,
+        .num_KCodeMemory = SlabCountKCodeMemory,
+        .num_KDeviceAddressSpace = SlabCountKDeviceAddressSpace,
+        .num_KSession = SlabCountKSession,
+        .num_KLightSession = SlabCountKLightSession,
+        .num_KObjectName = SlabCountKObjectName,
+        .num_KResourceLimit = SlabCountKResourceLimit,
+        .num_KDebug = SlabCountKDebug,
+        .num_KAlpha = SlabCountKAlpha,
+        .num_KBeta = SlabCountKBeta,
+    };
+}
+
+void InitializeSlabResourceCounts(KernelCore& kernel) {
+    kernel.SlabResourceCounts() = KSlabResourceCounts::CreateDefault();
+    if (KSystemControl::Init::ShouldIncreaseThreadResourceLimit()) {
+        kernel.SlabResourceCounts().num_KThread += SlabCountExtraKThread;
+    }
+}
+
+size_t CalculateTotalSlabHeapSize(const KernelCore& kernel) {
+    size_t size = 0;
+
+#define ADD_SLAB_SIZE(NAME, COUNT, ...)                                                            \
+    {                                                                                              \
+        size += alignof(NAME);                                                                     \
+        size += Common::AlignUp(sizeof(NAME) * (COUNT), alignof(void*));                           \
+    };
+
+    // Add the size required for each slab.
+    FOREACH_SLAB_TYPE(ADD_SLAB_SIZE)
+
+#undef ADD_SLAB_SIZE
+
+    // Add the reserved size.
+    size += KernelSlabHeapGapsSize;
+
+    return size;
+}
+
+void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) {
+    auto& kernel = system.Kernel();
+
+    // Get the start of the slab region, since that's where we'll be working.
+    VAddr address = memory_layout.GetSlabRegionAddress();
+
+    // Initialize slab type array to be in sorted order.
+    std::array<KSlabType, KSlabType_Count> slab_types;
+    for (size_t i = 0; i < slab_types.size(); i++) {
+        slab_types[i] = static_cast<KSlabType>(i);
+    }
+
+    // N shuffles the slab type array with the following simple algorithm.
+    for (size_t i = 0; i < slab_types.size(); i++) {
+        const size_t rnd = KSystemControl::GenerateRandomRange(0, slab_types.size() - 1);
+        std::swap(slab_types[i], slab_types[rnd]);
+    }
+
+    // Create an array to represent the gaps between the slabs.
+    const size_t total_gap_size = KernelSlabHeapGapsSize;
+    std::array<size_t, slab_types.size()> slab_gaps;
+    for (size_t i = 0; i < slab_gaps.size(); i++) {
+        // Note: This is an off-by-one error from Nintendo's intention, because GenerateRandomRange
+        // is inclusive. However, Nintendo also has the off-by-one error, and it's "harmless", so we
+        // will include it ourselves.
+        slab_gaps[i] = KSystemControl::GenerateRandomRange(0, total_gap_size);
+    }
+
+    // Sort the array, so that we can treat differences between values as offsets to the starts of
+    // slabs.
+    for (size_t i = 1; i < slab_gaps.size(); i++) {
+        for (size_t j = i; j > 0 && slab_gaps[j - 1] > slab_gaps[j]; j--) {
+            std::swap(slab_gaps[j], slab_gaps[j - 1]);
+        }
+    }
+
+    for (size_t i = 0; i < slab_types.size(); i++) {
+        // Add the random gap to the address.
+        address += (i == 0) ? slab_gaps[0] : slab_gaps[i] - slab_gaps[i - 1];
+
+#define INITIALIZE_SLAB_HEAP(NAME, COUNT, ...)                                                     \
+    case KSlabType_##NAME:                                                                         \
+        address = InitializeSlabHeap<NAME>(system, memory_layout, address, COUNT);                 \
+        break;
+
+        // Initialize the slabheap.
+        switch (slab_types[i]) {
+            // For each of the slab types, we want to initialize that heap.
+            FOREACH_SLAB_TYPE(INITIALIZE_SLAB_HEAP)
+            // If we somehow get an invalid type, abort.
+        default:
+            UNREACHABLE();
+        }
+    }
+}
+
+} // namespace Kernel::Init
diff --git a/src/core/hle/kernel/init/init_slab_setup.h b/src/core/hle/kernel/init/init_slab_setup.h
new file mode 100644
index 000000000..a8f7e0918
--- /dev/null
+++ b/src/core/hle/kernel/init/init_slab_setup.h
@@ -0,0 +1,43 @@
+// Copyright 2021 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+namespace Core {
+class System;
+} // namespace Core
+
+namespace Kernel {
+class KernelCore;
+class KMemoryLayout;
+} // namespace Kernel
+
+namespace Kernel::Init {
+
+struct KSlabResourceCounts {
+    static KSlabResourceCounts CreateDefault();
+
+    size_t num_KProcess;
+    size_t num_KThread;
+    size_t num_KEvent;
+    size_t num_KInterruptEvent;
+    size_t num_KPort;
+    size_t num_KSharedMemory;
+    size_t num_KTransferMemory;
+    size_t num_KCodeMemory;
+    size_t num_KDeviceAddressSpace;
+    size_t num_KSession;
+    size_t num_KLightSession;
+    size_t num_KObjectName;
+    size_t num_KResourceLimit;
+    size_t num_KDebug;
+    size_t num_KAlpha;
+    size_t num_KBeta;
+};
+
+void InitializeSlabResourceCounts(KernelCore& kernel);
+size_t CalculateTotalSlabHeapSize(const KernelCore& kernel);
+void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout);
+
+} // namespace Kernel::Init
diff --git a/src/core/hle/kernel/k_auto_object.cpp b/src/core/hle/kernel/k_auto_object.cpp
new file mode 100644
index 000000000..dbe237f09
--- /dev/null
+++ b/src/core/hle/kernel/k_auto_object.cpp
@@ -0,0 +1,14 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/kernel/k_auto_object.h"
+
+namespace Kernel {
+
+KAutoObject* KAutoObject::Create(KAutoObject* obj) {
+    obj->m_ref_count = 1;
+    return obj;
+}
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/k_auto_object.h b/src/core/hle/kernel/k_auto_object.h
new file mode 100644
index 000000000..765e46670
--- /dev/null
+++ b/src/core/hle/kernel/k_auto_object.h
@@ -0,0 +1,306 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <atomic>
+#include <string>
+
+#include "common/assert.h"
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+#include "common/intrusive_red_black_tree.h"
+#include "core/hle/kernel/k_class_token.h"
+
+namespace Kernel {
+
+class KernelCore;
+class KProcess;
+
+#define KERNEL_AUTOOBJECT_TRAITS(CLASS, BASE_CLASS)                                                \
+    YUZU_NON_COPYABLE(CLASS);                                                                      \
+    YUZU_NON_MOVEABLE(CLASS);                                                                      \
+                                                                                                   \
+private:                                                                                           \
+    friend class ::Kernel::KClassTokenGenerator;                                                   \
+    static constexpr inline auto ObjectType = ::Kernel::KClassTokenGenerator::ObjectType::CLASS;   \
+    static constexpr inline const char* const TypeName = #CLASS;                                   \
+    static constexpr inline ClassTokenType ClassToken() {                                          \
+        return ::Kernel::ClassToken<CLASS>;                                                        \
+    }                                                                                              \
+                                                                                                   \
+public:                                                                                            \
+    using BaseClass = BASE_CLASS;                                                                  \
+    static constexpr TypeObj GetStaticTypeObj() {                                                  \
+        constexpr ClassTokenType Token = ClassToken();                                             \
+        return TypeObj(TypeName, Token);                                                           \
+    }                                                                                              \
+    static constexpr const char* GetStaticTypeName() {                                             \
+        return TypeName;                                                                           \
+    }                                                                                              \
+    virtual TypeObj GetTypeObj() const {                                                           \
+        return GetStaticTypeObj();                                                                 \
+    }                                                                                              \
+    virtual const char* GetTypeName() const {                                                      \
+        return GetStaticTypeName();                                                                \
+    }                                                                                              \
+                                                                                                   \
+private:                                                                                           \
+    constexpr bool operator!=(const TypeObj& rhs)
+
+class KAutoObject {
+protected:
+    class TypeObj {
+    public:
+        constexpr explicit TypeObj(const char* n, ClassTokenType tok)
+            : m_name(n), m_class_token(tok) {}
+
+        constexpr const char* GetName() const {
+            return m_name;
+        }
+        constexpr ClassTokenType GetClassToken() const {
+            return m_class_token;
+        }
+
+        constexpr bool operator==(const TypeObj& rhs) const {
+            return this->GetClassToken() == rhs.GetClassToken();
+        }
+
+        constexpr bool operator!=(const TypeObj& rhs) const {
+            return this->GetClassToken() != rhs.GetClassToken();
+        }
+
+        constexpr bool IsDerivedFrom(const TypeObj& rhs) const {
+            return (this->GetClassToken() | rhs.GetClassToken()) == this->GetClassToken();
+        }
+
+    private:
+        const char* m_name;
+        ClassTokenType m_class_token;
+    };
+
+private:
+    KERNEL_AUTOOBJECT_TRAITS(KAutoObject, KAutoObject);
+
+public:
+    explicit KAutoObject(KernelCore& kernel_) : kernel(kernel_) {}
+    virtual ~KAutoObject() = default;
+
+    static KAutoObject* Create(KAutoObject* ptr);
+
+    // Destroy is responsible for destroying the auto object's resources when ref_count hits zero.
+    virtual void Destroy() {
+        UNIMPLEMENTED();
+    }
+
+    // Finalize is responsible for cleaning up resource, but does not destroy the object.
+    virtual void Finalize() {}
+
+    virtual KProcess* GetOwner() const {
+        return nullptr;
+    }
+
+    u32 GetReferenceCount() const {
+        return m_ref_count.load();
+    }
+
+    bool IsDerivedFrom(const TypeObj& rhs) const {
+        return this->GetTypeObj().IsDerivedFrom(rhs);
+    }
+
+    bool IsDerivedFrom(const KAutoObject& rhs) const {
+        return this->IsDerivedFrom(rhs.GetTypeObj());
+    }
+
+    template <typename Derived>
+    Derived DynamicCast() {
+        static_assert(std::is_pointer_v<Derived>);
+        using DerivedType = std::remove_pointer_t<Derived>;
+
+        if (this->IsDerivedFrom(DerivedType::GetStaticTypeObj())) {
+            return static_cast<Derived>(this);
+        } else {
+            return nullptr;
+        }
+    }
+
+    template <typename Derived>
+    const Derived DynamicCast() const {
+        static_assert(std::is_pointer_v<Derived>);
+        using DerivedType = std::remove_pointer_t<Derived>;
+
+        if (this->IsDerivedFrom(DerivedType::GetStaticTypeObj())) {
+            return static_cast<Derived>(this);
+        } else {
+            return nullptr;
+        }
+    }
+
+    bool Open() {
+        // Atomically increment the reference count, only if it's positive.
+        u32 cur_ref_count = m_ref_count.load(std::memory_order_acquire);
+        do {
+            if (cur_ref_count == 0) {
+                return false;
+            }
+            ASSERT(cur_ref_count < cur_ref_count + 1);
+        } while (!m_ref_count.compare_exchange_weak(cur_ref_count, cur_ref_count + 1,
+                                                    std::memory_order_relaxed));
+
+        return true;
+    }
+
+    void Close() {
+        // Atomically decrement the reference count, not allowing it to become negative.
+        u32 cur_ref_count = m_ref_count.load(std::memory_order_acquire);
+        do {
+            ASSERT(cur_ref_count > 0);
+        } while (!m_ref_count.compare_exchange_weak(cur_ref_count, cur_ref_count - 1,
+                                                    std::memory_order_relaxed));
+
+        // If ref count hits zero, destroy the object.
+        if (cur_ref_count - 1 == 0) {
+            this->Destroy();
+        }
+    }
+
+protected:
+    KernelCore& kernel;
+    std::string name;
+
+private:
+    std::atomic<u32> m_ref_count{};
+};
+
+class KAutoObjectWithListContainer;
+
+class KAutoObjectWithList : public KAutoObject {
+public:
+    explicit KAutoObjectWithList(KernelCore& kernel_) : KAutoObject(kernel_), kernel(kernel_) {}
+
+    static int Compare(const KAutoObjectWithList& lhs, const KAutoObjectWithList& rhs) {
+        const u64 lid = lhs.GetId();
+        const u64 rid = rhs.GetId();
+
+        if (lid < rid) {
+            return -1;
+        } else if (lid > rid) {
+            return 1;
+        } else {
+            return 0;
+        }
+    }
+
+public:
+    virtual u64 GetId() const {
+        return reinterpret_cast<u64>(this);
+    }
+
+    virtual const std::string& GetName() const {
+        return name;
+    }
+
+private:
+    friend class KAutoObjectWithListContainer;
+
+private:
+    Common::IntrusiveRedBlackTreeNode list_node;
+
+protected:
+    KernelCore& kernel;
+};
+
+template <typename T>
+class KScopedAutoObject {
+    YUZU_NON_COPYABLE(KScopedAutoObject);
+
+public:
+    constexpr KScopedAutoObject() = default;
+
+    constexpr KScopedAutoObject(T* o) : m_obj(o) {
+        if (m_obj != nullptr) {
+            m_obj->Open();
+        }
+    }
+
+    ~KScopedAutoObject() {
+        if (m_obj != nullptr) {
+            m_obj->Close();
+        }
+        m_obj = nullptr;
+    }
+
+    template <typename U>
+    requires(std::derived_from<T, U> ||
+             std::derived_from<U, T>) constexpr KScopedAutoObject(KScopedAutoObject<U>&& rhs) {
+        if constexpr (std::derived_from<U, T>) {
+            // Upcast.
+            m_obj = rhs.m_obj;
+            rhs.m_obj = nullptr;
+        } else {
+            // Downcast.
+            T* derived = nullptr;
+            if (rhs.m_obj != nullptr) {
+                derived = rhs.m_obj->template DynamicCast<T*>();
+                if (derived == nullptr) {
+                    rhs.m_obj->Close();
+                }
+            }
+
+            m_obj = derived;
+            rhs.m_obj = nullptr;
+        }
+    }
+
+    constexpr KScopedAutoObject<T>& operator=(KScopedAutoObject<T>&& rhs) {
+        rhs.Swap(*this);
+        return *this;
+    }
+
+    constexpr T* operator->() {
+        return m_obj;
+    }
+    constexpr T& operator*() {
+        return *m_obj;
+    }
+
+    constexpr void Reset(T* o) {
+        KScopedAutoObject(o).Swap(*this);
+    }
+
+    constexpr T* GetPointerUnsafe() {
+        return m_obj;
+    }
+
+    constexpr T* GetPointerUnsafe() const {
+        return m_obj;
+    }
+
+    constexpr T* ReleasePointerUnsafe() {
+        T* ret = m_obj;
+        m_obj = nullptr;
+        return ret;
+    }
+
+    constexpr bool IsNull() const {
+        return m_obj == nullptr;
+    }
+    constexpr bool IsNotNull() const {
+        return m_obj != nullptr;
+    }
+
+private:
+    template <typename U>
+    friend class KScopedAutoObject;
+
+private:
+    T* m_obj{};
+
+private:
+    constexpr void Swap(KScopedAutoObject& rhs) noexcept {
+        std::swap(m_obj, rhs.m_obj);
+    }
+};
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/k_auto_object_container.cpp b/src/core/hle/kernel/k_auto_object_container.cpp
new file mode 100644
index 000000000..fc0c28874
--- /dev/null
+++ b/src/core/hle/kernel/k_auto_object_container.cpp
@@ -0,0 +1,28 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/kernel/k_auto_object_container.h"
+
+namespace Kernel {
+
+void KAutoObjectWithListContainer::Register(KAutoObjectWithList* obj) {
+    KScopedLightLock lk(m_lock);
+
+    m_object_list.insert(*obj);
+}
+
+void KAutoObjectWithListContainer::Unregister(KAutoObjectWithList* obj) {
+    KScopedLightLock lk(m_lock);
+
+    m_object_list.erase(m_object_list.iterator_to(*obj));
+}
+
+size_t KAutoObjectWithListContainer::GetOwnedCount(KProcess* owner) {
+    KScopedLightLock lk(m_lock);
+
+    return std::count_if(m_object_list.begin(), m_object_list.end(),
+                         [&](const auto& obj) { return obj.GetOwner() == owner; });
+}
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/k_auto_object_container.h b/src/core/hle/kernel/k_auto_object_container.h
new file mode 100644
index 000000000..ff40cf5a7
--- /dev/null
+++ b/src/core/hle/kernel/k_auto_object_container.h
@@ -0,0 +1,70 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <atomic>
+
+#include "common/assert.h"
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+#include "common/intrusive_red_black_tree.h"
+#include "core/hle/kernel/k_auto_object.h"
+#include "core/hle/kernel/k_light_lock.h"
+
+namespace Kernel {
+
+class KernelCore;
+class KProcess;
+
+class KAutoObjectWithListContainer {
+    YUZU_NON_COPYABLE(KAutoObjectWithListContainer);
+    YUZU_NON_MOVEABLE(KAutoObjectWithListContainer);
+
+public:
+    using ListType = Common::IntrusiveRedBlackTreeMemberTraits<
+        &KAutoObjectWithList::list_node>::TreeType<KAutoObjectWithList>;
+
+public:
+    class ListAccessor : public KScopedLightLock {
+    public:
+        explicit ListAccessor(KAutoObjectWithListContainer* container)
+            : KScopedLightLock(container->m_lock), m_list(container->m_object_list) {}
+        explicit ListAccessor(KAutoObjectWithListContainer& container)
+            : KScopedLightLock(container.m_lock), m_list(container.m_object_list) {}
+
+        typename ListType::iterator begin() const {
+            return m_list.begin();
+        }
+
+        typename ListType::iterator end() const {
+            return m_list.end();
+        }
+
+        typename ListType::iterator find(typename ListType::const_reference ref) const {
+            return m_list.find(ref);
+        }
+
+    private:
+        ListType& m_list;
+    };
+
+    friend class ListAccessor;
+
+public:
+    KAutoObjectWithListContainer(KernelCore& kernel) : m_lock(kernel), m_object_list() {}
+
+    void Initialize() {}
+    void Finalize() {}
+
+    void Register(KAutoObjectWithList* obj);
+    void Unregister(KAutoObjectWithList* obj);
+    size_t GetOwnedCount(KProcess* owner);
+
+private:
+    KLightLock m_lock;
+    ListType m_object_list;
+};
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/k_class_token.cpp b/src/core/hle/kernel/k_class_token.cpp
new file mode 100644
index 000000000..beb8a2a05
--- /dev/null
+++ b/src/core/hle/kernel/k_class_token.cpp
@@ -0,0 +1,133 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/kernel/k_auto_object.h"
+#include "core/hle/kernel/k_class_token.h"
+#include "core/hle/kernel/k_client_port.h"
+#include "core/hle/kernel/k_client_session.h"
+#include "core/hle/kernel/k_event.h"
+#include "core/hle/kernel/k_port.h"
+#include "core/hle/kernel/k_process.h"
+#include "core/hle/kernel/k_readable_event.h"
+#include "core/hle/kernel/k_resource_limit.h"
+#include "core/hle/kernel/k_server_port.h"
+#include "core/hle/kernel/k_server_session.h"
+#include "core/hle/kernel/k_session.h"
+#include "core/hle/kernel/k_shared_memory.h"
+#include "core/hle/kernel/k_synchronization_object.h"
+#include "core/hle/kernel/k_thread.h"
+#include "core/hle/kernel/k_transfer_memory.h"
+#include "core/hle/kernel/k_writable_event.h"
+
+namespace Kernel {
+
+// Ensure that we generate correct class tokens for all types.
+
+// Ensure that the absolute token values are correct.
+static_assert(ClassToken<KAutoObject> == 0b00000000'00000000);
+static_assert(ClassToken<KSynchronizationObject> == 0b00000000'00000001);
+static_assert(ClassToken<KReadableEvent> == 0b00000000'00000011);
+// static_assert(ClassToken<KInterruptEvent> == 0b00000111'00000011);
+// static_assert(ClassToken<KDebug> == 0b00001011'00000001);
+static_assert(ClassToken<KThread> == 0b00010011'00000001);
+static_assert(ClassToken<KServerPort> == 0b00100011'00000001);
+static_assert(ClassToken<KServerSession> == 0b01000011'00000001);
+static_assert(ClassToken<KClientPort> == 0b10000011'00000001);
+static_assert(ClassToken<KClientSession> == 0b00001101'00000000);
+static_assert(ClassToken<KProcess> == 0b00010101'00000001);
+static_assert(ClassToken<KResourceLimit> == 0b00100101'00000000);
+// static_assert(ClassToken<KLightSession> == 0b01000101'00000000);
+static_assert(ClassToken<KPort> == 0b10000101'00000000);
+static_assert(ClassToken<KSession> == 0b00011001'00000000);
+static_assert(ClassToken<KSharedMemory> == 0b00101001'00000000);
+static_assert(ClassToken<KEvent> == 0b01001001'00000000);
+static_assert(ClassToken<KWritableEvent> == 0b10001001'00000000);
+// static_assert(ClassToken<KLightClientSession> == 0b00110001'00000000);
+// static_assert(ClassToken<KLightServerSession> == 0b01010001'00000000);
+static_assert(ClassToken<KTransferMemory> == 0b10010001'00000000);
+// static_assert(ClassToken<KDeviceAddressSpace> == 0b01100001'00000000);
+// static_assert(ClassToken<KSessionRequest> == 0b10100001'00000000);
+// static_assert(ClassToken<KCodeMemory> == 0b11000001'00000000);
+
+// Ensure that the token hierarchy is correct.
+
+// Base classes
+static_assert(ClassToken<KAutoObject> == (0b00000000));
+static_assert(ClassToken<KSynchronizationObject> == (0b00000001 | ClassToken<KAutoObject>));
+static_assert(ClassToken<KReadableEvent> == (0b00000010 | ClassToken<KSynchronizationObject>));
+
+// Final classes
+// static_assert(ClassToken<KInterruptEvent> == ((0b00000111 << 8) | ClassToken<KReadableEvent>));
+// static_assert(ClassToken<KDebug> == ((0b00001011 << 8) | ClassToken<KSynchronizationObject>));
+static_assert(ClassToken<KThread> == ((0b00010011 << 8) | ClassToken<KSynchronizationObject>));
+static_assert(ClassToken<KServerPort> == ((0b00100011 << 8) | ClassToken<KSynchronizationObject>));
+static_assert(ClassToken<KServerSession> ==
+              ((0b01000011 << 8) | ClassToken<KSynchronizationObject>));
+static_assert(ClassToken<KClientPort> == ((0b10000011 << 8) | ClassToken<KSynchronizationObject>));
+static_assert(ClassToken<KClientSession> == ((0b00001101 << 8) | ClassToken<KAutoObject>));
+static_assert(ClassToken<KProcess> == ((0b00010101 << 8) | ClassToken<KSynchronizationObject>));
+static_assert(ClassToken<KResourceLimit> == ((0b00100101 << 8) | ClassToken<KAutoObject>));
+// static_assert(ClassToken<KLightSession> == ((0b01000101 << 8) | ClassToken<KAutoObject>));
+static_assert(ClassToken<KPort> == ((0b10000101 << 8) | ClassToken<KAutoObject>));
+static_assert(ClassToken<KSession> == ((0b00011001 << 8) | ClassToken<KAutoObject>));
+static_assert(ClassToken<KSharedMemory> == ((0b00101001 << 8) | ClassToken<KAutoObject>));
+static_assert(ClassToken<KEvent> == ((0b01001001 << 8) | ClassToken<KAutoObject>));
+static_assert(ClassToken<KWritableEvent> == ((0b10001001 << 8) | ClassToken<KAutoObject>));
+// static_assert(ClassToken<KLightClientSession> == ((0b00110001 << 8) | ClassToken<KAutoObject>));
+// static_assert(ClassToken<KLightServerSession> == ((0b01010001 << 8) | ClassToken<KAutoObject>));
+static_assert(ClassToken<KTransferMemory> == ((0b10010001 << 8) | ClassToken<KAutoObject>));
+// static_assert(ClassToken<KDeviceAddressSpace> == ((0b01100001 << 8) | ClassToken<KAutoObject>));
+// static_assert(ClassToken<KSessionRequest> == ((0b10100001 << 8) | ClassToken<KAutoObject>));
+// static_assert(ClassToken<KCodeMemory> == ((0b11000001 << 8) | ClassToken<KAutoObject>));
+
+// Ensure that the token hierarchy reflects the class hierarchy.
+
+// Base classes.
+static_assert(!std::is_final<KSynchronizationObject>::value &&
+              std::is_base_of<KAutoObject, KSynchronizationObject>::value);
+static_assert(!std::is_final<KReadableEvent>::value &&
+              std::is_base_of<KSynchronizationObject, KReadableEvent>::value);
+
+// Final classes
+// static_assert(std::is_final<KInterruptEvent>::value &&
+//              std::is_base_of<KReadableEvent, KInterruptEvent>::value);
+// static_assert(std::is_final<KDebug>::value &&
+//              std::is_base_of<KSynchronizationObject, KDebug>::value);
+static_assert(std::is_final<KThread>::value &&
+              std::is_base_of<KSynchronizationObject, KThread>::value);
+static_assert(std::is_final<KServerPort>::value &&
+              std::is_base_of<KSynchronizationObject, KServerPort>::value);
+static_assert(std::is_final<KServerSession>::value &&
+              std::is_base_of<KSynchronizationObject, KServerSession>::value);
+static_assert(std::is_final<KClientPort>::value &&
+              std::is_base_of<KSynchronizationObject, KClientPort>::value);
+static_assert(std::is_final<KClientSession>::value &&
+              std::is_base_of<KAutoObject, KClientSession>::value);
+static_assert(std::is_final<KProcess>::value &&
+              std::is_base_of<KSynchronizationObject, KProcess>::value);
+static_assert(std::is_final<KResourceLimit>::value &&
+              std::is_base_of<KAutoObject, KResourceLimit>::value);
+// static_assert(std::is_final<KLightSession>::value &&
+//              std::is_base_of<KAutoObject, KLightSession>::value);
+static_assert(std::is_final<KPort>::value && std::is_base_of<KAutoObject, KPort>::value);
+static_assert(std::is_final<KSession>::value && std::is_base_of<KAutoObject, KSession>::value);
+static_assert(std::is_final<KSharedMemory>::value &&
+              std::is_base_of<KAutoObject, KSharedMemory>::value);
+static_assert(std::is_final<KEvent>::value && std::is_base_of<KAutoObject, KEvent>::value);
+static_assert(std::is_final<KWritableEvent>::value &&
+              std::is_base_of<KAutoObject, KWritableEvent>::value);
+// static_assert(std::is_final<KLightClientSession>::value &&
+//              std::is_base_of<KAutoObject, KLightClientSession>::value);
+// static_assert(std::is_final<KLightServerSession>::value &&
+//              std::is_base_of<KAutoObject, KLightServerSession>::value);
+static_assert(std::is_final<KTransferMemory>::value &&
+              std::is_base_of<KAutoObject, KTransferMemory>::value);
+// static_assert(std::is_final<KDeviceAddressSpace>::value &&
+//              std::is_base_of<KAutoObject, KDeviceAddressSpace>::value);
+// static_assert(std::is_final<KSessionRequest>::value &&
+//              std::is_base_of<KAutoObject, KSessionRequest>::value);
+// static_assert(std::is_final<KCodeMemory>::value &&
+//              std::is_base_of<KAutoObject, KCodeMemory>::value);
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/k_class_token.h b/src/core/hle/kernel/k_class_token.h
new file mode 100644
index 000000000..c28db49ec
--- /dev/null
+++ b/src/core/hle/kernel/k_class_token.h
@@ -0,0 +1,131 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <atomic>
+
+#include "common/assert.h"
+#include "common/bit_util.h"
+#include "common/common_types.h"
+
+namespace Kernel {
+
+class KAutoObject;
+
+class KClassTokenGenerator {
+public:
+    using TokenBaseType = u16;
+
+public:
+    static constexpr size_t BaseClassBits = 8;
+    static constexpr size_t FinalClassBits = (sizeof(TokenBaseType) * CHAR_BIT) - BaseClassBits;
+    // One bit per base class.
+    static constexpr size_t NumBaseClasses = BaseClassBits;
+    // Final classes are permutations of three bits.
+    static constexpr size_t NumFinalClasses = [] {
+        TokenBaseType index = 0;
+        for (size_t i = 0; i < FinalClassBits; i++) {
+            for (size_t j = i + 1; j < FinalClassBits; j++) {
+                for (size_t k = j + 1; k < FinalClassBits; k++) {
+                    index++;
+                }
+            }
+        }
+        return index;
+    }();
+
+private:
+    template <TokenBaseType Index>
+    static constexpr inline TokenBaseType BaseClassToken = 1U << Index;
+
+    template <TokenBaseType Index>
+    static constexpr inline TokenBaseType FinalClassToken = [] {
+        TokenBaseType index = 0;
+        for (size_t i = 0; i < FinalClassBits; i++) {
+            for (size_t j = i + 1; j < FinalClassBits; j++) {
+                for (size_t k = j + 1; k < FinalClassBits; k++) {
+                    if ((index++) == Index) {
+                        return static_cast<TokenBaseType>(((1ULL << i) | (1ULL << j) | (1ULL << k))
+                                                          << BaseClassBits);
+                    }
+                }
+            }
+        }
+    }();
+
+    template <typename T>
+    static constexpr inline TokenBaseType GetClassToken() {
+        static_assert(std::is_base_of<KAutoObject, T>::value);
+        if constexpr (std::is_same<T, KAutoObject>::value) {
+            static_assert(T::ObjectType == ObjectType::KAutoObject);
+            return 0;
+        } else if constexpr (!std::is_final<T>::value) {
+            static_assert(ObjectType::BaseClassesStart <= T::ObjectType &&
+                          T::ObjectType < ObjectType::BaseClassesEnd);
+            constexpr auto ClassIndex = static_cast<TokenBaseType>(T::ObjectType) -
+                                        static_cast<TokenBaseType>(ObjectType::BaseClassesStart);
+            return BaseClassToken<ClassIndex> | GetClassToken<typename T::BaseClass>();
+        } else if constexpr (ObjectType::FinalClassesStart <= T::ObjectType &&
+                             T::ObjectType < ObjectType::FinalClassesEnd) {
+            constexpr auto ClassIndex = static_cast<TokenBaseType>(T::ObjectType) -
+                                        static_cast<TokenBaseType>(ObjectType::FinalClassesStart);
+            return FinalClassToken<ClassIndex> | GetClassToken<typename T::BaseClass>();
+        } else {
+            static_assert(!std::is_same<T, T>::value, "GetClassToken: Invalid Type");
+        }
+    };
+
+public:
+    enum class ObjectType {
+        KAutoObject,
+
+        BaseClassesStart,
+
+        KSynchronizationObject = BaseClassesStart,
+        KReadableEvent,
+
+        BaseClassesEnd,
+
+        FinalClassesStart = BaseClassesEnd,
+
+        KInterruptEvent = FinalClassesStart,
+        KDebug,
+        KThread,
+        KServerPort,
+        KServerSession,
+        KClientPort,
+        KClientSession,
+        KProcess,
+        KResourceLimit,
+        KLightSession,
+        KPort,
+        KSession,
+        KSharedMemory,
+        KEvent,
+        KWritableEvent,
+        KLightClientSession,
+        KLightServerSession,
+        KTransferMemory,
+        KDeviceAddressSpace,
+        KSessionRequest,
+        KCodeMemory,
+
+        // NOTE: True order for these has not been determined yet.
+        KAlpha,
+        KBeta,
+
+        FinalClassesEnd = FinalClassesStart + NumFinalClasses,
+    };
+
+    template <typename T>
+    static constexpr inline TokenBaseType ClassToken = GetClassToken<T>();
+};
+
+using ClassTokenType = KClassTokenGenerator::TokenBaseType;
+
+template <typename T>
+static constexpr inline ClassTokenType ClassToken = KClassTokenGenerator::ClassToken<T>;
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/k_client_port.cpp b/src/core/hle/kernel/k_client_port.cpp
new file mode 100644
index 000000000..b6f1d713f
--- /dev/null
+++ b/src/core/hle/kernel/k_client_port.cpp
@@ -0,0 +1,125 @@
+// Copyright 2021 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/scope_exit.h"
+#include "core/hle/kernel/hle_ipc.h"
+#include "core/hle/kernel/k_client_port.h"
+#include "core/hle/kernel/k_port.h"
+#include "core/hle/kernel/k_scheduler.h"
+#include "core/hle/kernel/k_scoped_resource_reservation.h"
+#include "core/hle/kernel/k_session.h"
+#include "core/hle/kernel/svc_results.h"
+
+namespace Kernel {
+
+KClientPort::KClientPort(KernelCore& kernel) : KSynchronizationObject{kernel} {}
+KClientPort::~KClientPort() = default;
+
+void KClientPort::Initialize(KPort* parent_, s32 max_sessions_, std::string&& name_) {
+    // Set member variables.
+    num_sessions = 0;
+    peak_sessions = 0;
+    parent = parent_;
+    max_sessions = max_sessions_;
+    name = std::move(name_);
+}
+
+void KClientPort::OnSessionFinalized() {
+    KScopedSchedulerLock sl{kernel};
+
+    const auto prev = num_sessions--;
+    if (prev == max_sessions) {
+        this->NotifyAvailable();
+    }
+}
+
+void KClientPort::OnServerClosed() {}
+
+bool KClientPort::IsLight() const {
+    return this->GetParent()->IsLight();
+}
+
+bool KClientPort::IsServerClosed() const {
+    return this->GetParent()->IsServerClosed();
+}
+
+void KClientPort::Destroy() {
+    // Note with our parent that we're closed.
+    parent->OnClientClosed();
+
+    // Close our reference to our parent.
+    parent->Close();
+}
+
+bool KClientPort::IsSignaled() const {
+    return num_sessions < max_sessions;
+}
+
+ResultCode KClientPort::CreateSession(KClientSession** out) {
+    // Reserve a new session from the resource limit.
+    KScopedResourceReservation session_reservation(kernel.CurrentProcess()->GetResourceLimit(),
+                                                   LimitableResource::Sessions);
+    R_UNLESS(session_reservation.Succeeded(), ResultLimitReached);
+
+    // Update the session counts.
+    {
+        // Atomically increment the number of sessions.
+        s32 new_sessions;
+        {
+            const auto max = max_sessions;
+            auto cur_sessions = num_sessions.load(std::memory_order_acquire);
+            do {
+                R_UNLESS(cur_sessions < max, ResultOutOfSessions);
+                new_sessions = cur_sessions + 1;
+            } while (!num_sessions.compare_exchange_weak(cur_sessions, new_sessions,
+                                                         std::memory_order_relaxed));
+        }
+
+        // Atomically update the peak session tracking.
+        {
+            auto peak = peak_sessions.load(std::memory_order_acquire);
+            do {
+                if (peak >= new_sessions) {
+                    break;
+                }
+            } while (!peak_sessions.compare_exchange_weak(peak, new_sessions,
+                                                          std::memory_order_relaxed));
+        }
+    }
+
+    // Create a new session.
+    KSession* session = KSession::Create(kernel);
+    if (session == nullptr) {
+        /* Decrement the session count. */
+        const auto prev = num_sessions--;
+        if (prev == max_sessions) {
+            this->NotifyAvailable();
+        }
+
+        return ResultOutOfResource;
+    }
+
+    // Initialize the session.
+    session->Initialize(this, parent->GetName());
+
+    // Commit the session reservation.
+    session_reservation.Commit();
+
+    // Register the session.
+    KSession::Register(kernel, session);
+    auto session_guard = SCOPE_GUARD({
+        session->GetClientSession().Close();
+        session->GetServerSession().Close();
+    });
+
+    // Enqueue the session with our parent.
+    R_TRY(parent->EnqueueSession(std::addressof(session->GetServerSession())));
+
+    // We succeeded, so set the output.
+    session_guard.Cancel();
+    *out = std::addressof(session->GetClientSession());
+    return RESULT_SUCCESS;
+}
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/k_client_port.h b/src/core/hle/kernel/k_client_port.h
new file mode 100644
index 000000000..ec1d7e12e
--- /dev/null
+++ b/src/core/hle/kernel/k_client_port.h
@@ -0,0 +1,61 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include <string>
+
+#include "common/common_types.h"
+#include "core/hle/kernel/k_synchronization_object.h"
+#include "core/hle/result.h"
+
+namespace Kernel {
+
+class KClientSession;
+class KernelCore;
+class KPort;
+
+class KClientPort final : public KSynchronizationObject {
+    KERNEL_AUTOOBJECT_TRAITS(KClientPort, KSynchronizationObject);
+
+public:
+    explicit KClientPort(KernelCore& kernel);
+    virtual ~KClientPort() override;
+
+    void Initialize(KPort* parent_, s32 max_sessions_, std::string&& name_);
+    void OnSessionFinalized();
+    void OnServerClosed();
+
+    const KPort* GetParent() const {
+        return parent;
+    }
+
+    s32 GetNumSessions() const {
+        return num_sessions;
+    }
+    s32 GetPeakSessions() const {
+        return peak_sessions;
+    }
+    s32 GetMaxSessions() const {
+        return max_sessions;
+    }
+
+    bool IsLight() const;
+    bool IsServerClosed() const;
+
+    // Overridden virtual functions.
+    virtual void Destroy() override;
+    virtual bool IsSignaled() const override;
+
+    ResultCode CreateSession(KClientSession** out);
+
+private:
+    std::atomic<s32> num_sessions{};
+    std::atomic<s32> peak_sessions{};
+    s32 max_sessions{};
+    KPort* parent{};
+};
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/k_client_session.cpp b/src/core/hle/kernel/k_client_session.cpp
new file mode 100644
index 000000000..0618dc246
--- /dev/null
+++ b/src/core/hle/kernel/k_client_session.cpp
@@ -0,0 +1,31 @@
+// Copyright 2021 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/kernel/hle_ipc.h"
+#include "core/hle/kernel/k_client_session.h"
+#include "core/hle/kernel/k_server_session.h"
+#include "core/hle/kernel/k_session.h"
+#include "core/hle/kernel/k_thread.h"
+#include "core/hle/kernel/svc_results.h"
+#include "core/hle/result.h"
+
+namespace Kernel {
+
+KClientSession::KClientSession(KernelCore& kernel) : KAutoObjectWithSlabHeapAndContainer{kernel} {}
+KClientSession::~KClientSession() = default;
+
+void KClientSession::Destroy() {
+    parent->OnClientClosed();
+    parent->Close();
+}
+
+void KClientSession::OnServerClosed() {}
+
+ResultCode KClientSession::SendSyncRequest(KThread* thread, Core::Memory::Memory& memory,
+                                           Core::Timing::CoreTiming& core_timing) {
+    // Signal the server session that new data is available
+    return parent->GetServerSession().HandleSyncRequest(thread, memory, core_timing);
+}
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/k_client_session.h b/src/core/hle/kernel/k_client_session.h
new file mode 100644
index 000000000..6476a588b
--- /dev/null
+++ b/src/core/hle/kernel/k_client_session.h
@@ -0,0 +1,61 @@
+// Copyright 2021 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include <string>
+
+#include "core/hle/kernel/k_auto_object.h"
+#include "core/hle/kernel/k_synchronization_object.h"
+#include "core/hle/kernel/slab_helpers.h"
+#include "core/hle/result.h"
+
+union ResultCode;
+
+namespace Core::Memory {
+class Memory;
+}
+
+namespace Core::Timing {
+class CoreTiming;
+}
+
+namespace Kernel {
+
+class KernelCore;
+class KSession;
+class KThread;
+
+class KClientSession final
+    : public KAutoObjectWithSlabHeapAndContainer<KClientSession, KAutoObjectWithList> {
+    KERNEL_AUTOOBJECT_TRAITS(KClientSession, KAutoObject);
+
+public:
+    explicit KClientSession(KernelCore& kernel);
+    virtual ~KClientSession();
+
+    void Initialize(KSession* parent_, std::string&& name_) {
+        // Set member variables.
+        parent = parent_;
+        name = std::move(name_);
+    }
+
+    virtual void Destroy() override;
+    static void PostDestroy([[maybe_unused]] uintptr_t arg) {}
+
+    KSession* GetParent() const {
+        return parent;
+    }
+
+    ResultCode SendSyncRequest(KThread* thread, Core::Memory::Memory& memory,
+                               Core::Timing::CoreTiming& core_timing);
+
+    void OnServerClosed();
+
+private:
+    KSession* parent{};
+};
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/k_condition_variable.cpp b/src/core/hle/kernel/k_condition_variable.cpp
index 170d8fa0d..f51cf3e7b 100644
--- a/src/core/hle/kernel/k_condition_variable.cpp
+++ b/src/core/hle/kernel/k_condition_variable.cpp
@@ -7,12 +7,13 @@
 #include "core/arm/exclusive_monitor.h"
 #include "core/core.h"
 #include "core/hle/kernel/k_condition_variable.h"
+#include "core/hle/kernel/k_linked_list.h"
+#include "core/hle/kernel/k_process.h"
 #include "core/hle/kernel/k_scheduler.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"
 #include "core/hle/kernel/kernel.h"
-#include "core/hle/kernel/process.h"
 #include "core/hle/kernel/svc_common.h"
 #include "core/hle/kernel/svc_results.h"
 #include "core/memory.h"
@@ -107,8 +108,8 @@ ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 val
 
     // Wait for the address.
     {
-        std::shared_ptr<KThread> owner_thread;
-        ASSERT(!owner_thread);
+        KScopedAutoObject<KThread> owner_thread;
+        ASSERT(owner_thread.IsNull());
         {
             KScopedSchedulerLock sl(kernel);
             cur_thread->SetSyncedObject(nullptr, RESULT_SUCCESS);
@@ -126,8 +127,10 @@ ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 val
                 R_UNLESS(test_tag == (handle | Svc::HandleWaitMask), RESULT_SUCCESS);
 
                 // Get the lock owner thread.
-                owner_thread = kernel.CurrentProcess()->GetHandleTable().Get<KThread>(handle);
-                R_UNLESS(owner_thread, ResultInvalidHandle);
+                owner_thread =
+                    kernel.CurrentProcess()->GetHandleTable().GetObjectWithoutPseudoHandle<KThread>(
+                        handle);
+                R_UNLESS(owner_thread.IsNotNull(), ResultInvalidHandle);
 
                 // Update the lock.
                 cur_thread->SetAddressKey(addr, value);
@@ -137,7 +140,7 @@ ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 val
                 cur_thread->SetMutexWaitAddressForDebugging(addr);
             }
         }
-        ASSERT(owner_thread);
+        ASSERT(owner_thread.IsNotNull());
     }
 
     // Remove the thread as a waiter from the lock owner.
@@ -176,19 +179,22 @@ KThread* KConditionVariable::SignalImpl(KThread* thread) {
 
     KThread* thread_to_close = nullptr;
     if (can_access) {
-        if (prev_tag == InvalidHandle) {
+        if (prev_tag == Svc::InvalidHandle) {
             // If nobody held the lock previously, we're all good.
             thread->SetSyncedObject(nullptr, RESULT_SUCCESS);
             thread->Wakeup();
         } else {
             // Get the previous owner.
-            auto owner_thread = kernel.CurrentProcess()->GetHandleTable().Get<KThread>(
-                prev_tag & ~Svc::HandleWaitMask);
+            KThread* owner_thread = kernel.CurrentProcess()
+                                        ->GetHandleTable()
+                                        .GetObjectWithoutPseudoHandle<KThread>(
+                                            static_cast<Handle>(prev_tag & ~Svc::HandleWaitMask))
+                                        .ReleasePointerUnsafe();
 
             if (owner_thread) {
                 // Add the thread as a waiter on the owner.
                 owner_thread->AddWaiter(thread);
-                thread_to_close = owner_thread.get();
+                thread_to_close = owner_thread;
             } else {
                 // The lock was tagged with a thread that doesn't exist.
                 thread->SetSyncedObject(nullptr, ResultInvalidState);
@@ -208,9 +214,7 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) {
     // Prepare for signaling.
     constexpr int MaxThreads = 16;
 
-    // TODO(bunnei): This should just be Thread once we implement KAutoObject instead of using
-    // std::shared_ptr.
-    std::vector<std::shared_ptr<KThread>> thread_list;
+    KLinkedList<KThread> thread_list{kernel};
     std::array<KThread*, MaxThreads> thread_array;
     s32 num_to_close{};
 
@@ -228,7 +232,7 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) {
                 if (num_to_close < MaxThreads) {
                     thread_array[num_to_close++] = thread;
                 } else {
-                    thread_list.push_back(SharedFrom(thread));
+                    thread_list.push_back(*thread);
                 }
             }
 
@@ -250,8 +254,9 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) {
     }
 
     // Close threads in the list.
-    for (auto it = thread_list.begin(); it != thread_list.end(); it = thread_list.erase(it)) {
-        (*it)->Close();
+    for (auto it = thread_list.begin(); it != thread_list.end();
+         it = thread_list.erase(kernel, it)) {
+        (*it).Close();
     }
 }
 
diff --git a/src/core/hle/kernel/k_event.cpp b/src/core/hle/kernel/k_event.cpp
index bb2fa4ad5..986355b78 100644
--- a/src/core/hle/kernel/k_event.cpp
+++ b/src/core/hle/kernel/k_event.cpp
@@ -3,30 +3,53 @@
 // Refer to the license.txt file included.
 
 #include "core/hle/kernel/k_event.h"
-#include "core/hle/kernel/k_readable_event.h"
-#include "core/hle/kernel/k_writable_event.h"
+#include "core/hle/kernel/k_process.h"
+#include "core/hle/kernel/k_resource_limit.h"
 
 namespace Kernel {
 
-KEvent::KEvent(KernelCore& kernel, std::string&& name) : Object{kernel, std::move(name)} {}
+KEvent::KEvent(KernelCore& kernel)
+    : KAutoObjectWithSlabHeapAndContainer{kernel}, readable_event{kernel}, writable_event{kernel} {}
 
 KEvent::~KEvent() = default;
 
-std::shared_ptr<KEvent> KEvent::Create(KernelCore& kernel, std::string&& name) {
-    return std::make_shared<KEvent>(kernel, std::move(name));
-}
+void KEvent::Initialize(std::string&& name_) {
+    // Increment reference count.
+    // Because reference count is one on creation, this will result
+    // in a reference count of two. Thus, when both readable and
+    // writable events are closed this object will be destroyed.
+    Open();
 
-void KEvent::Initialize() {
     // Create our sub events.
-    readable_event = std::make_shared<KReadableEvent>(kernel, GetName() + ":Readable");
-    writable_event = std::make_shared<KWritableEvent>(kernel, GetName() + ":Writable");
+    KAutoObject::Create(std::addressof(readable_event));
+    KAutoObject::Create(std::addressof(writable_event));
 
     // Initialize our sub sessions.
-    readable_event->Initialize(this);
-    writable_event->Initialize(this);
+    readable_event.Initialize(this, name_ + ":Readable");
+    writable_event.Initialize(this, name_ + ":Writable");
+
+    // Set our owner process.
+    owner = kernel.CurrentProcess();
+    if (owner) {
+        owner->Open();
+    }
 
     // Mark initialized.
+    name = std::move(name_);
     initialized = true;
 }
 
+void KEvent::Finalize() {
+    KAutoObjectWithSlabHeapAndContainer<KEvent, KAutoObjectWithList>::Finalize();
+}
+
+void KEvent::PostDestroy(uintptr_t arg) {
+    // Release the event count resource the owner process holds.
+    KProcess* owner = reinterpret_cast<KProcess*>(arg);
+    if (owner) {
+        owner->GetResourceLimit()->Release(LimitableResource::Events, 1);
+        owner->Close();
+    }
+}
+
 } // namespace Kernel
diff --git a/src/core/hle/kernel/k_event.h b/src/core/hle/kernel/k_event.h
index 2fb887129..4ca869930 100644
--- a/src/core/hle/kernel/k_event.h
+++ b/src/core/hle/kernel/k_event.h
@@ -4,53 +4,54 @@
 
 #pragma once
 
-#include "core/hle/kernel/object.h"
+#include "core/hle/kernel/k_readable_event.h"
+#include "core/hle/kernel/k_writable_event.h"
+#include "core/hle/kernel/slab_helpers.h"
 
 namespace Kernel {
 
 class KernelCore;
 class KReadableEvent;
 class KWritableEvent;
+class KProcess;
+
+class KEvent final : public KAutoObjectWithSlabHeapAndContainer<KEvent, KAutoObjectWithList> {
+    KERNEL_AUTOOBJECT_TRAITS(KEvent, KAutoObject);
 
-class KEvent final : public Object {
 public:
-    explicit KEvent(KernelCore& kernel, std::string&& name);
-    ~KEvent() override;
+    explicit KEvent(KernelCore& kernel);
+    virtual ~KEvent();
 
-    static std::shared_ptr<KEvent> Create(KernelCore& kernel, std::string&& name);
+    void Initialize(std::string&& name);
 
-    void Initialize();
+    virtual void Finalize() override;
 
-    void Finalize() override {}
-
-    std::string GetTypeName() const override {
-        return "KEvent";
+    virtual bool IsInitialized() const override {
+        return initialized;
     }
 
-    static constexpr HandleType HANDLE_TYPE = HandleType::Event;
-    HandleType GetHandleType() const override {
-        return HANDLE_TYPE;
+    virtual uintptr_t GetPostDestroyArgument() const override {
+        return reinterpret_cast<uintptr_t>(owner);
     }
 
-    std::shared_ptr<KReadableEvent>& GetReadableEvent() {
+    static void PostDestroy(uintptr_t arg);
+
+    virtual KProcess* GetOwner() const override {
+        return owner;
+    }
+
+    KReadableEvent& GetReadableEvent() {
         return readable_event;
     }
 
-    std::shared_ptr<KWritableEvent>& GetWritableEvent() {
-        return writable_event;
-    }
-
-    const std::shared_ptr<KReadableEvent>& GetReadableEvent() const {
-        return readable_event;
-    }
-
-    const std::shared_ptr<KWritableEvent>& GetWritableEvent() const {
+    KWritableEvent& GetWritableEvent() {
         return writable_event;
     }
 
 private:
-    std::shared_ptr<KReadableEvent> readable_event;
-    std::shared_ptr<KWritableEvent> writable_event;
+    KReadableEvent readable_event;
+    KWritableEvent writable_event;
+    KProcess* owner{};
     bool initialized{};
 };
 
diff --git a/src/core/hle/kernel/k_handle_table.cpp b/src/core/hle/kernel/k_handle_table.cpp
new file mode 100644
index 000000000..0378447f6
--- /dev/null
+++ b/src/core/hle/kernel/k_handle_table.cpp
@@ -0,0 +1,135 @@
+// Copyright 2021 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/kernel/k_handle_table.h"
+
+namespace Kernel {
+
+KHandleTable::KHandleTable(KernelCore& kernel_) : kernel{kernel_} {}
+KHandleTable ::~KHandleTable() = default;
+
+ResultCode KHandleTable::Finalize() {
+    // Get the table and clear our record of it.
+    u16 saved_table_size = 0;
+    {
+        KScopedSpinLock lk(m_lock);
+
+        std::swap(m_table_size, saved_table_size);
+    }
+
+    // Close and free all entries.
+    for (size_t i = 0; i < saved_table_size; i++) {
+        if (KAutoObject* obj = m_objects[i]; obj != nullptr) {
+            obj->Close();
+        }
+    }
+
+    return RESULT_SUCCESS;
+}
+
+bool KHandleTable::Remove(Handle handle) {
+    // Don't allow removal of a pseudo-handle.
+    if (Svc::IsPseudoHandle(handle)) {
+        return false;
+    }
+
+    // Handles must not have reserved bits set.
+    const auto handle_pack = HandlePack(handle);
+    if (handle_pack.reserved != 0) {
+        return false;
+    }
+
+    // Find the object and free the entry.
+    KAutoObject* obj = nullptr;
+    {
+        KScopedSpinLock lk(m_lock);
+
+        if (this->IsValidHandle(handle)) {
+            const auto index = handle_pack.index;
+
+            obj = m_objects[index];
+            this->FreeEntry(index);
+        } else {
+            return false;
+        }
+    }
+
+    // Close the object.
+    obj->Close();
+    return true;
+}
+
+ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) {
+    KScopedSpinLock lk(m_lock);
+
+    // Never exceed our capacity.
+    R_UNLESS(m_count < m_table_size, ResultOutOfHandles);
+
+    // Allocate entry, set output handle.
+    {
+        const auto linear_id = this->AllocateLinearId();
+        const auto index = this->AllocateEntry();
+
+        m_entry_infos[index].info = {.linear_id = linear_id, .type = type};
+        m_objects[index] = obj;
+
+        obj->Open();
+
+        *out_handle = EncodeHandle(static_cast<u16>(index), linear_id);
+    }
+
+    return RESULT_SUCCESS;
+}
+
+ResultCode KHandleTable::Reserve(Handle* out_handle) {
+    KScopedSpinLock lk(m_lock);
+
+    // Never exceed our capacity.
+    R_UNLESS(m_count < m_table_size, ResultOutOfHandles);
+
+    *out_handle = EncodeHandle(static_cast<u16>(this->AllocateEntry()), this->AllocateLinearId());
+    return RESULT_SUCCESS;
+}
+
+void KHandleTable::Unreserve(Handle handle) {
+    KScopedSpinLock lk(m_lock);
+
+    // Unpack the handle.
+    const auto handle_pack = HandlePack(handle);
+    const auto index = handle_pack.index;
+    const auto linear_id = handle_pack.linear_id;
+    const auto reserved = handle_pack.reserved;
+    ASSERT(reserved == 0);
+    ASSERT(linear_id != 0);
+
+    if (index < m_table_size) {
+        // NOTE: This code does not check the linear id.
+        ASSERT(m_objects[index] == nullptr);
+        this->FreeEntry(index);
+    }
+}
+
+void KHandleTable::Register(Handle handle, KAutoObject* obj, u16 type) {
+    KScopedSpinLock lk(m_lock);
+
+    // Unpack the handle.
+    const auto handle_pack = HandlePack(handle);
+    const auto index = handle_pack.index;
+    const auto linear_id = handle_pack.linear_id;
+    const auto reserved = handle_pack.reserved;
+    ASSERT(reserved == 0);
+    ASSERT(linear_id != 0);
+
+    if (index < m_table_size) {
+        // Set the entry.
+        ASSERT(m_objects[index] == nullptr);
+
+        m_entry_infos[index].info = {.linear_id = static_cast<u16>(linear_id), .type = type};
+        m_objects[index] = obj;
+
+        obj->Open();
+    }
+}
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/k_handle_table.h b/src/core/hle/kernel/k_handle_table.h
new file mode 100644
index 000000000..ba9dd061d
--- /dev/null
+++ b/src/core/hle/kernel/k_handle_table.h
@@ -0,0 +1,310 @@
+// Copyright 2021 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+
+#include "common/assert.h"
+#include "common/bit_field.h"
+#include "common/bit_util.h"
+#include "common/common_types.h"
+#include "core/hle/kernel/k_auto_object.h"
+#include "core/hle/kernel/k_spin_lock.h"
+#include "core/hle/kernel/k_thread.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/svc_common.h"
+#include "core/hle/kernel/svc_results.h"
+#include "core/hle/result.h"
+
+namespace Kernel {
+
+class KernelCore;
+
+class KHandleTable {
+    YUZU_NON_COPYABLE(KHandleTable);
+    YUZU_NON_MOVEABLE(KHandleTable);
+
+public:
+    static constexpr size_t MaxTableSize = 1024;
+
+public:
+    explicit KHandleTable(KernelCore& kernel_);
+    ~KHandleTable();
+
+    ResultCode Initialize(s32 size) {
+        R_UNLESS(size <= static_cast<s32>(MaxTableSize), ResultOutOfMemory);
+
+        // Initialize all fields.
+        m_max_count = 0;
+        m_table_size = static_cast<u16>((size <= 0) ? MaxTableSize : size);
+        m_next_linear_id = MinLinearId;
+        m_count = 0;
+        m_free_head_index = -1;
+
+        // Free all entries.
+        for (s32 i = 0; i < static_cast<s32>(m_table_size); ++i) {
+            m_objects[i] = nullptr;
+            m_entry_infos[i].next_free_index = i - 1;
+            m_free_head_index = i;
+        }
+
+        return RESULT_SUCCESS;
+    }
+
+    size_t GetTableSize() const {
+        return m_table_size;
+    }
+    size_t GetCount() const {
+        return m_count;
+    }
+    size_t GetMaxCount() const {
+        return m_max_count;
+    }
+
+    ResultCode Finalize();
+    bool Remove(Handle handle);
+
+    template <typename T = KAutoObject>
+    KScopedAutoObject<T> GetObjectWithoutPseudoHandle(Handle handle) const {
+        // Lock and look up in table.
+        KScopedSpinLock lk(m_lock);
+
+        if constexpr (std::is_same_v<T, KAutoObject>) {
+            return this->GetObjectImpl(handle);
+        } else {
+            if (auto* obj = this->GetObjectImpl(handle); obj != nullptr) {
+                return obj->DynamicCast<T*>();
+            } else {
+                return nullptr;
+            }
+        }
+    }
+
+    template <typename T = KAutoObject>
+    KScopedAutoObject<T> GetObject(Handle handle) const {
+        // Handle pseudo-handles.
+        if constexpr (std::derived_from<KProcess, T>) {
+            if (handle == Svc::PseudoHandle::CurrentProcess) {
+                auto* const cur_process = kernel.CurrentProcess();
+                ASSERT(cur_process != nullptr);
+                return cur_process;
+            }
+        } else if constexpr (std::derived_from<KThread, T>) {
+            if (handle == Svc::PseudoHandle::CurrentThread) {
+                auto* const cur_thread = GetCurrentThreadPointer(kernel);
+                ASSERT(cur_thread != nullptr);
+                return cur_thread;
+            }
+        }
+
+        return this->template GetObjectWithoutPseudoHandle<T>(handle);
+    }
+
+    ResultCode Reserve(Handle* out_handle);
+    void Unreserve(Handle handle);
+
+    template <typename T>
+    ResultCode Add(Handle* out_handle, T* obj) {
+        static_assert(std::is_base_of_v<KAutoObject, T>);
+        return this->Add(out_handle, obj, obj->GetTypeObj().GetClassToken());
+    }
+
+    template <typename T>
+    void Register(Handle handle, T* obj) {
+        static_assert(std::is_base_of_v<KAutoObject, T>);
+        return this->Register(handle, obj, obj->GetTypeObj().GetClassToken());
+    }
+
+    template <typename T>
+    bool GetMultipleObjects(T** out, const Handle* handles, size_t num_handles) const {
+        // Try to convert and open all the handles.
+        size_t num_opened;
+        {
+            // Lock the table.
+            KScopedSpinLock lk(m_lock);
+            for (num_opened = 0; num_opened < num_handles; num_opened++) {
+                // Get the current handle.
+                const auto cur_handle = handles[num_opened];
+
+                // Get the object for the current handle.
+                KAutoObject* cur_object = this->GetObjectImpl(cur_handle);
+                if (cur_object == nullptr) {
+                    break;
+                }
+
+                // Cast the current object to the desired type.
+                T* cur_t = cur_object->DynamicCast<T*>();
+                if (cur_t == nullptr) {
+                    break;
+                }
+
+                // Open a reference to the current object.
+                cur_t->Open();
+                out[num_opened] = cur_t;
+            }
+        }
+
+        // If we converted every object, succeed.
+        if (num_opened == num_handles) {
+            return true;
+        }
+
+        // If we didn't convert entry object, close the ones we opened.
+        for (size_t i = 0; i < num_opened; i++) {
+            out[i]->Close();
+        }
+
+        return false;
+    }
+
+private:
+    ResultCode Add(Handle* out_handle, KAutoObject* obj, u16 type);
+    void Register(Handle handle, KAutoObject* obj, u16 type);
+
+    s32 AllocateEntry() {
+        ASSERT(m_count < m_table_size);
+
+        const auto index = m_free_head_index;
+
+        m_free_head_index = m_entry_infos[index].GetNextFreeIndex();
+
+        m_max_count = std::max(m_max_count, ++m_count);
+
+        return index;
+    }
+
+    void FreeEntry(s32 index) {
+        ASSERT(m_count > 0);
+
+        m_objects[index] = nullptr;
+        m_entry_infos[index].next_free_index = m_free_head_index;
+
+        m_free_head_index = index;
+
+        --m_count;
+    }
+
+    u16 AllocateLinearId() {
+        const u16 id = m_next_linear_id++;
+        if (m_next_linear_id > MaxLinearId) {
+            m_next_linear_id = MinLinearId;
+        }
+        return id;
+    }
+
+    bool IsValidHandle(Handle handle) const {
+        // Unpack the handle.
+        const auto handle_pack = HandlePack(handle);
+        const auto raw_value = handle_pack.raw;
+        const auto index = handle_pack.index;
+        const auto linear_id = handle_pack.linear_id;
+        const auto reserved = handle_pack.reserved;
+        ASSERT(reserved == 0);
+
+        // Validate our indexing information.
+        if (raw_value == 0) {
+            return false;
+        }
+        if (linear_id == 0) {
+            return false;
+        }
+        if (index >= m_table_size) {
+            return false;
+        }
+
+        // Check that there's an object, and our serial id is correct.
+        if (m_objects[index] == nullptr) {
+            return false;
+        }
+        if (m_entry_infos[index].GetLinearId() != linear_id) {
+            return false;
+        }
+
+        return true;
+    }
+
+    KAutoObject* GetObjectImpl(Handle handle) const {
+        // Handles must not have reserved bits set.
+        const auto handle_pack = HandlePack(handle);
+        if (handle_pack.reserved != 0) {
+            return nullptr;
+        }
+
+        if (this->IsValidHandle(handle)) {
+            return m_objects[handle_pack.index];
+        } else {
+            return nullptr;
+        }
+    }
+
+    KAutoObject* GetObjectByIndexImpl(Handle* out_handle, size_t index) const {
+
+        // Index must be in bounds.
+        if (index >= m_table_size) {
+            return nullptr;
+        }
+
+        // Ensure entry has an object.
+        if (KAutoObject* obj = m_objects[index]; obj != nullptr) {
+            *out_handle = EncodeHandle(static_cast<u16>(index), m_entry_infos[index].GetLinearId());
+            return obj;
+        } else {
+            return nullptr;
+        }
+    }
+
+private:
+    union HandlePack {
+        HandlePack() = default;
+        HandlePack(Handle handle) : raw{static_cast<u32>(handle)} {}
+
+        u32 raw;
+        BitField<0, 15, u32> index;
+        BitField<15, 15, u32> linear_id;
+        BitField<30, 2, u32> reserved;
+    };
+
+    static constexpr u16 MinLinearId = 1;
+    static constexpr u16 MaxLinearId = 0x7FFF;
+
+    static constexpr Handle EncodeHandle(u16 index, u16 linear_id) {
+        HandlePack handle{};
+        handle.index.Assign(index);
+        handle.linear_id.Assign(linear_id);
+        handle.reserved.Assign(0);
+        return handle.raw;
+    }
+
+    union EntryInfo {
+        struct {
+            u16 linear_id;
+            u16 type;
+        } info;
+        s32 next_free_index;
+
+        constexpr u16 GetLinearId() const {
+            return info.linear_id;
+        }
+        constexpr u16 GetType() const {
+            return info.type;
+        }
+        constexpr s32 GetNextFreeIndex() const {
+            return next_free_index;
+        }
+    };
+
+private:
+    std::array<EntryInfo, MaxTableSize> m_entry_infos{};
+    std::array<KAutoObject*, MaxTableSize> m_objects{};
+    s32 m_free_head_index{-1};
+    u16 m_table_size{};
+    u16 m_max_count{};
+    u16 m_next_linear_id{MinLinearId};
+    u16 m_count{};
+    mutable KSpinLock m_lock;
+    KernelCore& kernel;
+};
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/k_linked_list.h b/src/core/hle/kernel/k_linked_list.h
new file mode 100644
index 000000000..500f44685
--- /dev/null
+++ b/src/core/hle/kernel/k_linked_list.h
@@ -0,0 +1,238 @@
+// Copyright 2021 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <boost/intrusive/list.hpp>
+
+#include "common/assert.h"
+#include "core/hle/kernel/slab_helpers.h"
+
+namespace Kernel {
+
+class KernelCore;
+
+class KLinkedListNode : public boost::intrusive::list_base_hook<>,
+                        public KSlabAllocated<KLinkedListNode> {
+
+public:
+    KLinkedListNode() = default;
+
+    void Initialize(void* it) {
+        m_item = it;
+    }
+
+    void* GetItem() const {
+        return m_item;
+    }
+
+private:
+    void* m_item = nullptr;
+};
+
+template <typename T>
+class KLinkedList : private boost::intrusive::list<KLinkedListNode> {
+private:
+    using BaseList = boost::intrusive::list<KLinkedListNode>;
+
+public:
+    template <bool Const>
+    class Iterator;
+
+    using value_type = T;
+    using size_type = size_t;
+    using difference_type = ptrdiff_t;
+    using pointer = value_type*;
+    using const_pointer = const value_type*;
+    using reference = value_type&;
+    using const_reference = const value_type&;
+    using iterator = Iterator<false>;
+    using const_iterator = Iterator<true>;
+    using reverse_iterator = std::reverse_iterator<iterator>;
+    using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+
+    template <bool Const>
+    class Iterator {
+    private:
+        using BaseIterator = BaseList::iterator;
+        friend class KLinkedList;
+
+    public:
+        using iterator_category = std::bidirectional_iterator_tag;
+        using value_type = typename KLinkedList::value_type;
+        using difference_type = typename KLinkedList::difference_type;
+        using pointer = std::conditional_t<Const, KLinkedList::const_pointer, KLinkedList::pointer>;
+        using reference =
+            std::conditional_t<Const, KLinkedList::const_reference, KLinkedList::reference>;
+
+    public:
+        explicit Iterator(BaseIterator it) : m_base_it(it) {}
+
+        pointer GetItem() const {
+            return static_cast<pointer>(m_base_it->GetItem());
+        }
+
+        bool operator==(const Iterator& rhs) const {
+            return m_base_it == rhs.m_base_it;
+        }
+
+        bool operator!=(const Iterator& rhs) const {
+            return !(*this == rhs);
+        }
+
+        pointer operator->() const {
+            return this->GetItem();
+        }
+
+        reference operator*() const {
+            return *this->GetItem();
+        }
+
+        Iterator& operator++() {
+            ++m_base_it;
+            return *this;
+        }
+
+        Iterator& operator--() {
+            --m_base_it;
+            return *this;
+        }
+
+        Iterator operator++(int) {
+            const Iterator it{*this};
+            ++(*this);
+            return it;
+        }
+
+        Iterator operator--(int) {
+            const Iterator it{*this};
+            --(*this);
+            return it;
+        }
+
+        operator Iterator<true>() const {
+            return Iterator<true>(m_base_it);
+        }
+
+    private:
+        BaseIterator m_base_it;
+    };
+
+public:
+    constexpr KLinkedList(KernelCore& kernel_) : BaseList(), kernel{kernel_} {}
+
+    ~KLinkedList() {
+        // Erase all elements.
+        for (auto it = this->begin(); it != this->end(); it = this->erase(kernel, it)) {
+        }
+
+        // Ensure we succeeded.
+        ASSERT(this->empty());
+    }
+
+    // Iterator accessors.
+    iterator begin() {
+        return iterator(BaseList::begin());
+    }
+
+    const_iterator begin() const {
+        return const_iterator(BaseList::begin());
+    }
+
+    iterator end() {
+        return iterator(BaseList::end());
+    }
+
+    const_iterator end() const {
+        return const_iterator(BaseList::end());
+    }
+
+    const_iterator cbegin() const {
+        return this->begin();
+    }
+
+    const_iterator cend() const {
+        return this->end();
+    }
+
+    reverse_iterator rbegin() {
+        return reverse_iterator(this->end());
+    }
+
+    const_reverse_iterator rbegin() const {
+        return const_reverse_iterator(this->end());
+    }
+
+    reverse_iterator rend() {
+        return reverse_iterator(this->begin());
+    }
+
+    const_reverse_iterator rend() const {
+        return const_reverse_iterator(this->begin());
+    }
+
+    const_reverse_iterator crbegin() const {
+        return this->rbegin();
+    }
+
+    const_reverse_iterator crend() const {
+        return this->rend();
+    }
+
+    // Content management.
+    using BaseList::empty;
+    using BaseList::size;
+
+    reference back() {
+        return *(--this->end());
+    }
+
+    const_reference back() const {
+        return *(--this->end());
+    }
+
+    reference front() {
+        return *this->begin();
+    }
+
+    const_reference front() const {
+        return *this->begin();
+    }
+
+    iterator insert(const_iterator pos, reference ref) {
+        KLinkedListNode* node = KLinkedListNode::Allocate(kernel);
+        ASSERT(node != nullptr);
+        node->Initialize(std::addressof(ref));
+        return iterator(BaseList::insert(pos.m_base_it, *node));
+    }
+
+    void push_back(reference ref) {
+        this->insert(this->end(), ref);
+    }
+
+    void push_front(reference ref) {
+        this->insert(this->begin(), ref);
+    }
+
+    void pop_back() {
+        this->erase(--this->end());
+    }
+
+    void pop_front() {
+        this->erase(this->begin());
+    }
+
+    iterator erase(KernelCore& kernel, const iterator pos) {
+        KLinkedListNode* freed_node = std::addressof(*pos.m_base_it);
+        iterator ret = iterator(BaseList::erase(pos.m_base_it));
+        KLinkedListNode::Free(kernel, freed_node);
+
+        return ret;
+    }
+
+private:
+    KernelCore& kernel;
+};
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/k_memory_block.h b/src/core/hle/kernel/k_memory_block.h
index c5b9c5e85..a7fdb5fb8 100644
--- a/src/core/hle/kernel/k_memory_block.h
+++ b/src/core/hle/kernel/k_memory_block.h
@@ -134,6 +134,10 @@ enum class KMemoryPermission : u8 {
 };
 DECLARE_ENUM_FLAG_OPERATORS(KMemoryPermission);
 
+constexpr KMemoryPermission ConvertToKMemoryPermission(Svc::MemoryPermission perm) {
+    return static_cast<KMemoryPermission>(perm);
+}
+
 enum class KMemoryAttribute : u8 {
     None = 0x00,
     Mask = 0x7F,
diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp
index d09d5ce48..d4ce98ee3 100644
--- a/src/core/hle/kernel/k_page_table.cpp
+++ b/src/core/hle/kernel/k_page_table.cpp
@@ -11,11 +11,11 @@
 #include "core/hle/kernel/k_memory_block_manager.h"
 #include "core/hle/kernel/k_page_linked_list.h"
 #include "core/hle/kernel/k_page_table.h"
+#include "core/hle/kernel/k_process.h"
 #include "core/hle/kernel/k_resource_limit.h"
 #include "core/hle/kernel/k_scoped_resource_reservation.h"
 #include "core/hle/kernel/k_system_control.h"
 #include "core/hle/kernel/kernel.h"
-#include "core/hle/kernel/process.h"
 #include "core/hle/kernel/svc_results.h"
 #include "core/memory.h"
 
@@ -420,7 +420,7 @@ ResultCode KPageTable::MapPhysicalMemory(VAddr addr, std::size_t size) {
         remaining_size);
     if (!memory_reservation.Succeeded()) {
         LOG_ERROR(Kernel, "Could not reserve remaining {:X} bytes", remaining_size);
-        return ResultResourceLimitedExceeded;
+        return ResultLimitReached;
     }
 
     KPageLinkedList page_linked_list;
@@ -578,7 +578,7 @@ ResultCode KPageTable::Unmap(VAddr dst_addr, VAddr src_addr, std::size_t size) {
     AddRegionToPages(dst_addr, num_pages, dst_pages);
 
     if (!dst_pages.IsEqual(src_pages)) {
-        return ResultInvalidMemoryRange;
+        return ResultInvalidMemoryRegion;
     }
 
     {
@@ -641,6 +641,45 @@ ResultCode KPageTable::MapPages(VAddr addr, KPageLinkedList& page_linked_list, K
     return RESULT_SUCCESS;
 }
 
+ResultCode KPageTable::UnmapPages(VAddr addr, const KPageLinkedList& page_linked_list) {
+    VAddr cur_addr{addr};
+
+    for (const auto& node : page_linked_list.Nodes()) {
+        const std::size_t num_pages{(addr - cur_addr) / PageSize};
+        if (const auto result{
+                Operate(addr, num_pages, KMemoryPermission::None, OperationType::Unmap)};
+            result.IsError()) {
+            return result;
+        }
+
+        cur_addr += node.GetNumPages() * PageSize;
+    }
+
+    return RESULT_SUCCESS;
+}
+
+ResultCode KPageTable::UnmapPages(VAddr addr, KPageLinkedList& page_linked_list,
+                                  KMemoryState state) {
+    std::lock_guard lock{page_table_lock};
+
+    const std::size_t num_pages{page_linked_list.GetNumPages()};
+    const std::size_t size{num_pages * PageSize};
+
+    if (!CanContain(addr, size, state)) {
+        return ResultInvalidCurrentMemory;
+    }
+
+    if (IsRegionMapped(addr, num_pages * PageSize)) {
+        return ResultInvalidCurrentMemory;
+    }
+
+    CASCADE_CODE(UnmapPages(addr, page_linked_list));
+
+    block_manager->Update(addr, num_pages, state, KMemoryPermission::None);
+
+    return RESULT_SUCCESS;
+}
+
 ResultCode KPageTable::SetCodeMemoryPermission(VAddr addr, std::size_t size,
                                                KMemoryPermission perm) {
 
@@ -790,7 +829,7 @@ ResultVal<VAddr> KPageTable::SetHeapSize(std::size_t size) {
 
         if (!memory_reservation.Succeeded()) {
             LOG_ERROR(Kernel, "Could not reserve heap extension of size {:X} bytes", delta);
-            return ResultResourceLimitedExceeded;
+            return ResultLimitReached;
         }
 
         KPageLinkedList page_linked_list;
@@ -1067,7 +1106,7 @@ constexpr std::size_t KPageTable::GetRegionSize(KMemoryState state) const {
     }
 }
 
-constexpr bool KPageTable::CanContain(VAddr addr, std::size_t size, KMemoryState state) const {
+bool KPageTable::CanContain(VAddr addr, std::size_t size, KMemoryState state) const {
     const VAddr end{addr + size};
     const VAddr last{end - 1};
     const VAddr region_start{GetRegionAddress(state)};
diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h
index 49b824379..8c2cc03eb 100644
--- a/src/core/hle/kernel/k_page_table.h
+++ b/src/core/hle/kernel/k_page_table.h
@@ -40,6 +40,7 @@ public:
     ResultCode Unmap(VAddr dst_addr, VAddr src_addr, std::size_t size);
     ResultCode MapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state,
                         KMemoryPermission perm);
+    ResultCode UnmapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state);
     ResultCode SetCodeMemoryPermission(VAddr addr, std::size_t size, KMemoryPermission perm);
     KMemoryInfo QueryInfo(VAddr addr);
     ResultCode ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm);
@@ -63,6 +64,8 @@ public:
         return page_table_impl;
     }
 
+    bool CanContain(VAddr addr, std::size_t size, KMemoryState state) const;
+
 private:
     enum class OperationType : u32 {
         Map,
@@ -79,6 +82,7 @@ private:
     ResultCode InitializeMemoryLayout(VAddr start, VAddr end);
     ResultCode MapPages(VAddr addr, const KPageLinkedList& page_linked_list,
                         KMemoryPermission perm);
+    ResultCode UnmapPages(VAddr addr, const KPageLinkedList& page_linked_list);
     void MapPhysicalMemory(KPageLinkedList& page_linked_list, VAddr start, VAddr end);
     bool IsRegionMapped(VAddr address, u64 size);
     bool IsRegionContiguous(VAddr addr, u64 size) const;
@@ -92,7 +96,6 @@ private:
                        OperationType operation, PAddr map_addr = 0);
     constexpr VAddr GetRegionAddress(KMemoryState state) const;
     constexpr std::size_t GetRegionSize(KMemoryState state) const;
-    constexpr bool CanContain(VAddr addr, std::size_t size, KMemoryState state) const;
 
     constexpr ResultCode CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask,
                                           KMemoryState state, KMemoryPermission perm_mask,
@@ -216,8 +219,6 @@ public:
     constexpr PAddr GetPhysicalAddr(VAddr addr) {
         return page_table_impl.backing_addr[addr >> PageBits] + addr;
     }
-
-private:
     constexpr bool Contains(VAddr addr) const {
         return address_space_start <= addr && addr <= address_space_end - 1;
     }
@@ -225,6 +226,8 @@ private:
         return address_space_start <= addr && addr < addr + size &&
                addr + size - 1 <= address_space_end - 1;
     }
+
+private:
     constexpr bool IsKernel() const {
         return is_kernel;
     }
diff --git a/src/core/hle/kernel/k_port.cpp b/src/core/hle/kernel/k_port.cpp
new file mode 100644
index 000000000..734aa2a8c
--- /dev/null
+++ b/src/core/hle/kernel/k_port.cpp
@@ -0,0 +1,68 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/kernel/hle_ipc.h"
+#include "core/hle/kernel/k_port.h"
+#include "core/hle/kernel/k_scheduler.h"
+#include "core/hle/kernel/svc_results.h"
+
+namespace Kernel {
+
+KPort::KPort(KernelCore& kernel)
+    : KAutoObjectWithSlabHeapAndContainer{kernel}, server{kernel}, client{kernel} {}
+
+KPort::~KPort() = default;
+
+void KPort::Initialize(s32 max_sessions_, bool is_light_, const std::string& name_) {
+    // Open a new reference count to the initialized port.
+    Open();
+
+    // Create and initialize our server/client pair.
+    KAutoObject::Create(std::addressof(server));
+    KAutoObject::Create(std::addressof(client));
+    server.Initialize(this, name_ + ":Server");
+    client.Initialize(this, max_sessions_, name_ + ":Client");
+
+    // Set our member variables.
+    is_light = is_light_;
+    name = name_;
+    state = State::Normal;
+}
+
+void KPort::OnClientClosed() {
+    KScopedSchedulerLock sl{kernel};
+
+    if (state == State::Normal) {
+        state = State::ClientClosed;
+    }
+}
+
+void KPort::OnServerClosed() {
+    KScopedSchedulerLock sl{kernel};
+
+    if (state == State::Normal) {
+        state = State::ServerClosed;
+    }
+}
+
+bool KPort::IsServerClosed() const {
+    KScopedSchedulerLock sl{kernel};
+    return state == State::ServerClosed;
+}
+
+ResultCode KPort::EnqueueSession(KServerSession* session) {
+    KScopedSchedulerLock sl{kernel};
+
+    R_UNLESS(state == State::Normal, ResultPortClosed);
+
+    if (server.HasHLEHandler()) {
+        server.GetHLEHandler()->ClientConnected(session);
+    } else {
+        server.EnqueueSession(session);
+    }
+
+    return RESULT_SUCCESS;
+}
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/k_port.h b/src/core/hle/kernel/k_port.h
new file mode 100644
index 000000000..f1b2838d8
--- /dev/null
+++ b/src/core/hle/kernel/k_port.h
@@ -0,0 +1,69 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include <string>
+
+#include "common/common_types.h"
+#include "core/hle/kernel/k_client_port.h"
+#include "core/hle/kernel/k_server_port.h"
+#include "core/hle/kernel/slab_helpers.h"
+#include "core/hle/result.h"
+
+namespace Kernel {
+
+class KServerSession;
+
+class KPort final : public KAutoObjectWithSlabHeapAndContainer<KPort, KAutoObjectWithList> {
+    KERNEL_AUTOOBJECT_TRAITS(KPort, KAutoObject);
+
+public:
+    explicit KPort(KernelCore& kernel);
+    virtual ~KPort();
+
+    static void PostDestroy([[maybe_unused]] uintptr_t arg) {}
+
+    void Initialize(s32 max_sessions_, bool is_light_, const std::string& name_);
+    void OnClientClosed();
+    void OnServerClosed();
+
+    bool IsLight() const {
+        return is_light;
+    }
+
+    bool IsServerClosed() const;
+
+    ResultCode EnqueueSession(KServerSession* session);
+
+    KClientPort& GetClientPort() {
+        return client;
+    }
+    KServerPort& GetServerPort() {
+        return server;
+    }
+    const KClientPort& GetClientPort() const {
+        return client;
+    }
+    const KServerPort& GetServerPort() const {
+        return server;
+    }
+
+private:
+    enum class State : u8 {
+        Invalid = 0,
+        Normal = 1,
+        ClientClosed = 2,
+        ServerClosed = 3,
+    };
+
+private:
+    KServerPort server;
+    KClientPort client;
+    State state{State::Invalid};
+    bool is_light{};
+};
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/k_process.cpp
similarity index 76%
rename from src/core/hle/kernel/process.cpp
rename to src/core/hle/kernel/k_process.cpp
index e35deb8e2..174318180 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/k_process.cpp
@@ -17,13 +17,14 @@
 #include "core/hle/kernel/code_set.h"
 #include "core/hle/kernel/k_memory_block_manager.h"
 #include "core/hle/kernel/k_page_table.h"
+#include "core/hle/kernel/k_process.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_shared_memory.h"
 #include "core/hle/kernel/k_slab_heap.h"
 #include "core/hle/kernel/k_thread.h"
 #include "core/hle/kernel/kernel.h"
-#include "core/hle/kernel/process.h"
 #include "core/hle/kernel/svc_results.h"
 #include "core/hle/lock.h"
 #include "core/memory.h"
@@ -37,17 +38,20 @@ namespace {
  * @param owner_process The parent process for the main thread
  * @param priority The priority to give the main thread
  */
-void SetupMainThread(Core::System& system, Process& owner_process, u32 priority, VAddr stack_top) {
+void SetupMainThread(Core::System& system, KProcess& 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::CreateUserThread(system, ThreadType::User, "main", entry_point, priority, 0,
-                                  owner_process.GetIdealCoreId(), stack_top, &owner_process);
 
-    std::shared_ptr<KThread> thread = std::move(thread_res).Unwrap();
+    KThread* thread = KThread::Create(system.Kernel());
+    ASSERT(KThread::InitializeUserThread(system, thread, entry_point, 0, stack_top, priority,
+                                         owner_process.GetIdealCoreId(), &owner_process)
+               .IsSuccess());
 
     // Register 1 must be a handle to the main thread
-    const Handle thread_handle = owner_process.GetHandleTable().Create(thread).Unwrap();
+    Handle thread_handle{};
+    owner_process.GetHandleTable().Add(&thread_handle, thread);
+
+    thread->SetName("main");
     thread->GetContext32().cpu_registers[0] = 0;
     thread->GetContext64().cpu_registers[0] = 0;
     thread->GetContext32().cpu_registers[1] = thread_handle;
@@ -114,10 +118,10 @@ private:
     std::bitset<num_slot_entries> is_slot_used;
 };
 
-std::shared_ptr<Process> Process::Create(Core::System& system, std::string name, ProcessType type) {
+ResultCode KProcess::Initialize(KProcess* process, Core::System& system, std::string name,
+                                ProcessType type) {
     auto& kernel = system.Kernel();
 
-    std::shared_ptr<Process> process = std::make_shared<Process>(system);
     process->name = std::move(name);
 
     process->resource_limit = kernel.GetSystemResourceLimit();
@@ -126,6 +130,7 @@ std::shared_ptr<Process> Process::Create(Core::System& system, std::string name,
     process->process_id = type == ProcessType::KernelInternal ? kernel.CreateNewKernelProcessID()
                                                               : kernel.CreateNewUserProcessID();
     process->capabilities.InitializeForMetadatalessProcess();
+    process->is_initialized = true;
 
     std::mt19937 rng(Settings::values.rng_seed.GetValue().value_or(std::time(nullptr)));
     std::uniform_int_distribution<u64> distribution;
@@ -133,14 +138,18 @@ std::shared_ptr<Process> Process::Create(Core::System& system, std::string name,
                   [&] { return distribution(rng); });
 
     kernel.AppendNewProcess(process);
-    return process;
+
+    // Open a reference to the resource limit.
+    process->resource_limit->Open();
+
+    return RESULT_SUCCESS;
 }
 
-std::shared_ptr<KResourceLimit> Process::GetResourceLimit() const {
+KResourceLimit* KProcess::GetResourceLimit() const {
     return resource_limit;
 }
 
-void Process::IncrementThreadCount() {
+void KProcess::IncrementThreadCount() {
     ASSERT(num_threads >= 0);
     num_created_threads++;
 
@@ -149,7 +158,7 @@ void Process::IncrementThreadCount() {
     }
 }
 
-void Process::DecrementThreadCount() {
+void KProcess::DecrementThreadCount() {
     ASSERT(num_threads > 0);
 
     if (const auto count = --num_threads; count == 0) {
@@ -157,31 +166,34 @@ void Process::DecrementThreadCount() {
     }
 }
 
-u64 Process::GetTotalPhysicalMemoryAvailable() const {
+u64 KProcess::GetTotalPhysicalMemoryAvailable() const {
     const u64 capacity{resource_limit->GetFreeValue(LimitableResource::PhysicalMemory) +
                        page_table->GetTotalHeapSize() + GetSystemResourceSize() + image_size +
                        main_thread_stack_size};
-    ASSERT(capacity == kernel.MemoryManager().GetSize(KMemoryManager::Pool::Application));
+    if (const auto pool_size = kernel.MemoryManager().GetSize(KMemoryManager::Pool::Application);
+        capacity != pool_size) {
+        LOG_WARNING(Kernel, "capacity {} != application pool size {}", capacity, pool_size);
+    }
     if (capacity < memory_usage_capacity) {
         return capacity;
     }
     return memory_usage_capacity;
 }
 
-u64 Process::GetTotalPhysicalMemoryAvailableWithoutSystemResource() const {
+u64 KProcess::GetTotalPhysicalMemoryAvailableWithoutSystemResource() const {
     return GetTotalPhysicalMemoryAvailable() - GetSystemResourceSize();
 }
 
-u64 Process::GetTotalPhysicalMemoryUsed() const {
+u64 KProcess::GetTotalPhysicalMemoryUsed() const {
     return image_size + main_thread_stack_size + page_table->GetTotalHeapSize() +
            GetSystemResourceSize();
 }
 
-u64 Process::GetTotalPhysicalMemoryUsedWithoutSystemResource() const {
+u64 KProcess::GetTotalPhysicalMemoryUsedWithoutSystemResource() const {
     return GetTotalPhysicalMemoryUsed() - GetSystemResourceUsage();
 }
 
-bool Process::ReleaseUserException(KThread* thread) {
+bool KProcess::ReleaseUserException(KThread* thread) {
     KScopedSchedulerLock sl{kernel};
 
     if (exception_thread == thread) {
@@ -206,7 +218,7 @@ bool Process::ReleaseUserException(KThread* thread) {
     }
 }
 
-void Process::PinCurrentThread() {
+void KProcess::PinCurrentThread() {
     ASSERT(kernel.GlobalSchedulerContext().IsLocked());
 
     // Get the current thread.
@@ -221,7 +233,7 @@ void Process::PinCurrentThread() {
     KScheduler::SetSchedulerUpdateNeeded(kernel);
 }
 
-void Process::UnpinCurrentThread() {
+void KProcess::UnpinCurrentThread() {
     ASSERT(kernel.GlobalSchedulerContext().IsLocked());
 
     // Get the current thread.
@@ -236,15 +248,39 @@ void Process::UnpinCurrentThread() {
     KScheduler::SetSchedulerUpdateNeeded(kernel);
 }
 
-void Process::RegisterThread(const KThread* thread) {
+ResultCode KProcess::AddSharedMemory(KSharedMemory* shmem, [[maybe_unused]] VAddr address,
+                                     [[maybe_unused]] size_t size) {
+    // Lock ourselves, to prevent concurrent access.
+    KScopedLightLock lk(state_lock);
+
+    // TODO(bunnei): Manage KSharedMemoryInfo list here.
+
+    // Open a reference to the shared memory.
+    shmem->Open();
+
+    return RESULT_SUCCESS;
+}
+
+void KProcess::RemoveSharedMemory(KSharedMemory* shmem, [[maybe_unused]] VAddr address,
+                                  [[maybe_unused]] size_t size) {
+    // Lock ourselves, to prevent concurrent access.
+    KScopedLightLock lk(state_lock);
+
+    // TODO(bunnei): Manage KSharedMemoryInfo list here.
+
+    // Close a reference to the shared memory.
+    shmem->Close();
+}
+
+void KProcess::RegisterThread(const KThread* thread) {
     thread_list.push_back(thread);
 }
 
-void Process::UnregisterThread(const KThread* thread) {
+void KProcess::UnregisterThread(const KThread* thread) {
     thread_list.remove(thread);
 }
 
-ResultCode Process::Reset() {
+ResultCode KProcess::Reset() {
     // Lock the process and the scheduler.
     KScopedLightLock lk(state_lock);
     KScopedSchedulerLock sl{kernel};
@@ -258,8 +294,8 @@ ResultCode Process::Reset() {
     return RESULT_SUCCESS;
 }
 
-ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata,
-                                     std::size_t code_size) {
+ResultCode KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata,
+                                      std::size_t code_size) {
     program_id = metadata.GetTitleID();
     ideal_core = metadata.GetMainThreadCore();
     is_64bit_process = metadata.Is64BitProgram();
@@ -271,7 +307,7 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata,
     if (!memory_reservation.Succeeded()) {
         LOG_ERROR(Kernel, "Could not reserve process memory requirements of size {:X} bytes",
                   code_size + system_resource_size);
-        return ResultResourceLimitedExceeded;
+        return ResultLimitReached;
     }
     // Initialize proces address space
     if (const ResultCode result{
@@ -318,10 +354,10 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata,
     tls_region_address = CreateTLSRegion();
     memory_reservation.Commit();
 
-    return handle_table.SetSize(capabilities.GetHandleTableSize());
+    return handle_table.Initialize(capabilities.GetHandleTableSize());
 }
 
-void Process::Run(s32 main_thread_priority, u64 stack_size) {
+void KProcess::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);
@@ -331,18 +367,18 @@ void Process::Run(s32 main_thread_priority, u64 stack_size) {
 
     ChangeStatus(ProcessStatus::Running);
 
-    SetupMainThread(system, *this, main_thread_priority, main_thread_stack_top);
+    SetupMainThread(kernel.System(), *this, main_thread_priority, main_thread_stack_top);
 }
 
-void Process::PrepareForTermination() {
+void KProcess::PrepareForTermination() {
     ChangeStatus(ProcessStatus::Exiting);
 
-    const auto stop_threads = [this](const std::vector<std::shared_ptr<KThread>>& thread_list) {
+    const auto stop_threads = [this](const std::vector<KThread*>& thread_list) {
         for (auto& thread : thread_list) {
             if (thread->GetOwnerProcess() != this)
                 continue;
 
-            if (thread.get() == kernel.CurrentScheduler()->GetCurrentThread())
+            if (thread == kernel.CurrentScheduler()->GetCurrentThread())
                 continue;
 
             // TODO(Subv): When are the other running/ready threads terminated?
@@ -353,7 +389,7 @@ void Process::PrepareForTermination() {
         }
     };
 
-    stop_threads(system.GlobalSchedulerContext().GetThreadList());
+    stop_threads(kernel.System().GlobalSchedulerContext().GetThreadList());
 
     FreeTLSRegion(tls_region_address);
     tls_region_address = 0;
@@ -366,6 +402,16 @@ void Process::PrepareForTermination() {
     ChangeStatus(ProcessStatus::Exited);
 }
 
+void KProcess::Finalize() {
+    // Release memory to the resource limit.
+    if (resource_limit != nullptr) {
+        resource_limit->Close();
+    }
+
+    // Perform inherited finalization.
+    KAutoObjectWithSlabHeapAndContainer<KProcess, KSynchronizationObject>::Finalize();
+}
+
 /**
  * Attempts to find a TLS page that contains a free slot for
  * use by a thread.
@@ -379,8 +425,8 @@ static auto FindTLSPageWithAvailableSlots(std::vector<TLSPage>& tls_pages) {
                         [](const auto& page) { return page.HasAvailableSlots(); });
 }
 
-VAddr Process::CreateTLSRegion() {
-    KScopedSchedulerLock lock(system.Kernel());
+VAddr KProcess::CreateTLSRegion() {
+    KScopedSchedulerLock lock(kernel);
     if (auto tls_page_iter{FindTLSPageWithAvailableSlots(tls_pages)};
         tls_page_iter != tls_pages.cend()) {
         return *tls_page_iter->ReserveSlot();
@@ -391,7 +437,7 @@ VAddr Process::CreateTLSRegion() {
 
     const VAddr start{page_table->GetKernelMapRegionStart()};
     const VAddr size{page_table->GetKernelMapRegionEnd() - start};
-    const PAddr tls_map_addr{system.DeviceMemory().GetPhysicalAddr(tls_page_ptr)};
+    const PAddr tls_map_addr{kernel.System().DeviceMemory().GetPhysicalAddr(tls_page_ptr)};
     const VAddr tls_page_addr{page_table
                                   ->AllocateAndMapMemory(1, PageSize, true, start, size / PageSize,
                                                          KMemoryState::ThreadLocal,
@@ -410,8 +456,8 @@ VAddr Process::CreateTLSRegion() {
     return *reserve_result;
 }
 
-void Process::FreeTLSRegion(VAddr tls_address) {
-    KScopedSchedulerLock lock(system.Kernel());
+void KProcess::FreeTLSRegion(VAddr tls_address) {
+    KScopedSchedulerLock lock(kernel);
     const VAddr aligned_address = Common::AlignDown(tls_address, Core::Memory::PAGE_SIZE);
     auto iter =
         std::find_if(tls_pages.begin(), tls_pages.end(), [aligned_address](const auto& page) {
@@ -425,33 +471,34 @@ void Process::FreeTLSRegion(VAddr tls_address) {
     iter->ReleaseSlot(tls_address);
 }
 
-void Process::LoadModule(CodeSet code_set, VAddr base_addr) {
+void KProcess::LoadModule(CodeSet code_set, VAddr base_addr) {
     std::lock_guard lock{HLE::g_hle_lock};
     const auto ReprotectSegment = [&](const CodeSet::Segment& segment,
                                       KMemoryPermission permission) {
         page_table->SetCodeMemoryPermission(segment.addr + base_addr, segment.size, permission);
     };
 
-    system.Memory().WriteBlock(*this, base_addr, code_set.memory.data(), code_set.memory.size());
+    kernel.System().Memory().WriteBlock(*this, base_addr, code_set.memory.data(),
+                                        code_set.memory.size());
 
     ReprotectSegment(code_set.CodeSegment(), KMemoryPermission::ReadAndExecute);
     ReprotectSegment(code_set.RODataSegment(), KMemoryPermission::Read);
     ReprotectSegment(code_set.DataSegment(), KMemoryPermission::ReadAndWrite);
 }
 
-bool Process::IsSignaled() const {
+bool KProcess::IsSignaled() const {
     ASSERT(kernel.GlobalSchedulerContext().IsLocked());
     return is_signaled;
 }
 
-Process::Process(Core::System& system)
-    : KSynchronizationObject{system.Kernel()}, page_table{std::make_unique<KPageTable>(system)},
-      handle_table{system.Kernel()}, address_arbiter{system}, condition_var{system},
-      state_lock{system.Kernel()}, system{system} {}
+KProcess::KProcess(KernelCore& kernel)
+    : KAutoObjectWithSlabHeapAndContainer{kernel},
+      page_table{std::make_unique<KPageTable>(kernel.System())}, handle_table{kernel},
+      address_arbiter{kernel.System()}, condition_var{kernel.System()}, state_lock{kernel} {}
 
-Process::~Process() = default;
+KProcess::~KProcess() = default;
 
-void Process::ChangeStatus(ProcessStatus new_status) {
+void KProcess::ChangeStatus(ProcessStatus new_status) {
     if (status == new_status) {
         return;
     }
@@ -461,7 +508,7 @@ void Process::ChangeStatus(ProcessStatus new_status) {
     NotifyAvailable();
 }
 
-ResultCode Process::AllocateMainThreadStack(std::size_t stack_size) {
+ResultCode KProcess::AllocateMainThreadStack(std::size_t stack_size) {
     ASSERT(stack_size);
 
     // The kernel always ensures that the given stack size is page aligned.
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/k_process.h
similarity index 92%
rename from src/core/hle/kernel/process.h
rename to src/core/hle/kernel/k_process.h
index 45eefb90e..62ab26b05 100644
--- a/src/core/hle/kernel/process.h
+++ b/src/core/hle/kernel/k_process.h
@@ -11,11 +11,13 @@
 #include <unordered_map>
 #include <vector>
 #include "common/common_types.h"
-#include "core/hle/kernel/handle_table.h"
 #include "core/hle/kernel/k_address_arbiter.h"
+#include "core/hle/kernel/k_auto_object.h"
 #include "core/hle/kernel/k_condition_variable.h"
+#include "core/hle/kernel/k_handle_table.h"
 #include "core/hle/kernel/k_synchronization_object.h"
 #include "core/hle/kernel/process_capability.h"
+#include "core/hle/kernel/slab_helpers.h"
 #include "core/hle/result.h"
 
 namespace Core {
@@ -60,10 +62,13 @@ enum class ProcessStatus {
     DebugBreak,
 };
 
-class Process final : public KSynchronizationObject {
+class KProcess final
+    : public KAutoObjectWithSlabHeapAndContainer<KProcess, KSynchronizationObject> {
+    KERNEL_AUTOOBJECT_TRAITS(KProcess, KSynchronizationObject);
+
 public:
-    explicit Process(Core::System& system);
-    ~Process() override;
+    explicit KProcess(KernelCore& kernel);
+    ~KProcess() override;
 
     enum : u64 {
         /// Lowest allowed process ID for a kernel initial process.
@@ -85,20 +90,8 @@ public:
 
     static constexpr std::size_t RANDOM_ENTROPY_SIZE = 4;
 
-    static std::shared_ptr<Process> Create(Core::System& system, std::string name,
-                                           ProcessType type);
-
-    std::string GetTypeName() const override {
-        return "Process";
-    }
-    std::string GetName() const override {
-        return name;
-    }
-
-    static constexpr HandleType HANDLE_TYPE = HandleType::Process;
-    HandleType GetHandleType() const override {
-        return HANDLE_TYPE;
-    }
+    static ResultCode Initialize(KProcess* process, Core::System& system, std::string name,
+                                 ProcessType type);
 
     /// Gets a reference to the process' page table.
     KPageTable& PageTable() {
@@ -111,12 +104,12 @@ public:
     }
 
     /// Gets a reference to the process' handle table.
-    HandleTable& GetHandleTable() {
+    KHandleTable& GetHandleTable() {
         return handle_table;
     }
 
     /// Gets a const reference to the process' handle table.
-    const HandleTable& GetHandleTable() const {
+    const KHandleTable& GetHandleTable() const {
         return handle_table;
     }
 
@@ -167,7 +160,7 @@ public:
     }
 
     /// Gets the resource limit descriptor for this process
-    std::shared_ptr<KResourceLimit> GetResourceLimit() const;
+    KResourceLimit* GetResourceLimit() const;
 
     /// Gets the ideal CPU core ID for this process
     u8 GetIdealCoreId() const {
@@ -338,9 +331,19 @@ public:
 
     void LoadModule(CodeSet code_set, VAddr base_addr);
 
-    bool IsSignaled() const override;
+    virtual bool IsInitialized() const override {
+        return is_initialized;
+    }
 
-    void Finalize() override {}
+    static void PostDestroy([[maybe_unused]] uintptr_t arg) {}
+
+    virtual void Finalize();
+
+    virtual u64 GetId() const override final {
+        return GetProcessID();
+    }
+
+    virtual bool IsSignaled() const override;
 
     void PinCurrentThread();
     void UnpinCurrentThread();
@@ -349,6 +352,9 @@ public:
         return state_lock;
     }
 
+    ResultCode AddSharedMemory(KSharedMemory* shmem, VAddr address, size_t size);
+    void RemoveSharedMemory(KSharedMemory* shmem, VAddr address, size_t size);
+
     ///////////////////////////////////////////////////////////////////////////////////////////////
     // Thread-local storage management
 
@@ -399,7 +405,7 @@ private:
     u32 system_resource_size = 0;
 
     /// Resource limit descriptor for this process
-    std::shared_ptr<KResourceLimit> resource_limit;
+    KResourceLimit* resource_limit{};
 
     /// The ideal CPU core for this process, threads are scheduled on this core by default.
     u8 ideal_core = 0;
@@ -423,7 +429,7 @@ private:
     u64 total_process_running_time_ticks = 0;
 
     /// Per-process handle table for storing created object handles in.
-    HandleTable handle_table;
+    KHandleTable handle_table;
 
     /// Per-process address arbiter.
     KAddressArbiter address_arbiter;
@@ -454,14 +460,12 @@ private:
     /// Process total image size
     std::size_t image_size{};
 
-    /// Name of this process
-    std::string name;
-
     /// Schedule count of this process
     s64 schedule_count{};
 
     bool is_signaled{};
     bool is_suspended{};
+    bool is_initialized{};
 
     std::atomic<s32> num_created_threads{};
     std::atomic<u16> num_threads{};
@@ -474,9 +478,6 @@ private:
     KThread* exception_thread{};
 
     KLightLock state_lock;
-
-    /// System context
-    Core::System& system;
 };
 
 } // namespace Kernel
diff --git a/src/core/hle/kernel/k_readable_event.cpp b/src/core/hle/kernel/k_readable_event.cpp
index 4b4d34857..8fef4bb00 100644
--- a/src/core/hle/kernel/k_readable_event.cpp
+++ b/src/core/hle/kernel/k_readable_event.cpp
@@ -2,21 +2,18 @@
 // Licensed under GPLv2 or any later version
 // Refer to the license.txt file included.
 
-#include <algorithm>
 #include "common/assert.h"
-#include "common/common_funcs.h"
-#include "common/logging/log.h"
+#include "core/hle/kernel/k_event.h"
 #include "core/hle/kernel/k_readable_event.h"
 #include "core/hle/kernel/k_scheduler.h"
 #include "core/hle/kernel/k_thread.h"
 #include "core/hle/kernel/kernel.h"
-#include "core/hle/kernel/object.h"
 #include "core/hle/kernel/svc_results.h"
 
 namespace Kernel {
 
-KReadableEvent::KReadableEvent(KernelCore& kernel, std::string&& name)
-    : KSynchronizationObject{kernel, std::move(name)} {}
+KReadableEvent::KReadableEvent(KernelCore& kernel) : KSynchronizationObject{kernel} {}
+
 KReadableEvent::~KReadableEvent() = default;
 
 bool KReadableEvent::IsSignaled() const {
@@ -25,6 +22,12 @@ bool KReadableEvent::IsSignaled() const {
     return is_signaled;
 }
 
+void KReadableEvent::Destroy() {
+    if (parent) {
+        parent->Close();
+    }
+}
+
 ResultCode KReadableEvent::Signal() {
     KScopedSchedulerLock lk{kernel};
 
diff --git a/src/core/hle/kernel/k_readable_event.h b/src/core/hle/kernel/k_readable_event.h
index e6f0fd900..1783ef0b8 100644
--- a/src/core/hle/kernel/k_readable_event.h
+++ b/src/core/hle/kernel/k_readable_event.h
@@ -4,8 +4,9 @@
 
 #pragma once
 
+#include "core/hle/kernel/k_auto_object.h"
 #include "core/hle/kernel/k_synchronization_object.h"
-#include "core/hle/kernel/object.h"
+#include "core/hle/kernel/slab_helpers.h"
 #include "core/hle/result.h"
 
 namespace Kernel {
@@ -13,31 +14,25 @@ namespace Kernel {
 class KernelCore;
 class KEvent;
 
-class KReadableEvent final : public KSynchronizationObject {
+class KReadableEvent : public KSynchronizationObject {
+    KERNEL_AUTOOBJECT_TRAITS(KReadableEvent, KSynchronizationObject);
+
 public:
-    explicit KReadableEvent(KernelCore& kernel, std::string&& name);
+    explicit KReadableEvent(KernelCore& kernel);
     ~KReadableEvent() override;
 
-    std::string GetTypeName() const override {
-        return "KReadableEvent";
-    }
-
-    static constexpr HandleType HANDLE_TYPE = HandleType::ReadableEvent;
-    HandleType GetHandleType() const override {
-        return HANDLE_TYPE;
+    void Initialize(KEvent* parent_, std::string&& name_) {
+        is_signaled = false;
+        parent = parent_;
+        name = std::move(name_);
     }
 
     KEvent* GetParent() const {
         return parent;
     }
 
-    void Initialize(KEvent* parent_) {
-        is_signaled = false;
-        parent = parent_;
-    }
-
-    bool IsSignaled() const override;
-    void Finalize() override {}
+    virtual bool IsSignaled() const override;
+    virtual void Destroy() override;
 
     ResultCode Signal();
     ResultCode Clear();
diff --git a/src/core/hle/kernel/k_resource_limit.cpp b/src/core/hle/kernel/k_resource_limit.cpp
index d05b34ea3..ad5095bfd 100644
--- a/src/core/hle/kernel/k_resource_limit.cpp
+++ b/src/core/hle/kernel/k_resource_limit.cpp
@@ -10,10 +10,16 @@
 namespace Kernel {
 constexpr s64 DefaultTimeout = 10000000000; // 10 seconds
 
-KResourceLimit::KResourceLimit(KernelCore& kernel, const Core::Timing::CoreTiming& core_timing_)
-    : Object{kernel}, lock{kernel}, cond_var{kernel}, core_timing(core_timing_) {}
+KResourceLimit::KResourceLimit(KernelCore& kernel)
+    : KAutoObjectWithSlabHeapAndContainer{kernel}, lock{kernel}, cond_var{kernel} {}
 KResourceLimit::~KResourceLimit() = default;
 
+void KResourceLimit::Initialize(const Core::Timing::CoreTiming* core_timing_) {
+    core_timing = core_timing_;
+}
+
+void KResourceLimit::Finalize() {}
+
 s64 KResourceLimit::GetLimitValue(LimitableResource which) const {
     const auto index = static_cast<std::size_t>(which);
     s64 value{};
@@ -78,7 +84,7 @@ ResultCode KResourceLimit::SetLimitValue(LimitableResource which, s64 value) {
 }
 
 bool KResourceLimit::Reserve(LimitableResource which, s64 value) {
-    return Reserve(which, value, core_timing.GetGlobalTimeNs().count() + DefaultTimeout);
+    return Reserve(which, value, core_timing->GetGlobalTimeNs().count() + DefaultTimeout);
 }
 
 bool KResourceLimit::Reserve(LimitableResource which, s64 value, s64 timeout) {
@@ -109,7 +115,7 @@ bool KResourceLimit::Reserve(LimitableResource which, s64 value, s64 timeout) {
         }
 
         if (current_hints[index] + value <= limit_values[index] &&
-            (timeout < 0 || core_timing.GetGlobalTimeNs().count() < timeout)) {
+            (timeout < 0 || core_timing->GetGlobalTimeNs().count() < timeout)) {
             waiter_count++;
             cond_var.Wait(&lock, timeout);
             waiter_count--;
diff --git a/src/core/hle/kernel/k_resource_limit.h b/src/core/hle/kernel/k_resource_limit.h
index 4542317d0..66ebf32df 100644
--- a/src/core/hle/kernel/k_resource_limit.h
+++ b/src/core/hle/kernel/k_resource_limit.h
@@ -8,7 +8,6 @@
 #include "common/common_types.h"
 #include "core/hle/kernel/k_light_condition_variable.h"
 #include "core/hle/kernel/k_light_lock.h"
-#include "core/hle/kernel/object.h"
 
 union ResultCode;
 
@@ -32,10 +31,16 @@ constexpr bool IsValidResourceType(LimitableResource type) {
     return type < LimitableResource::Count;
 }
 
-class KResourceLimit final : public Object {
+class KResourceLimit final
+    : public KAutoObjectWithSlabHeapAndContainer<KResourceLimit, KAutoObjectWithList> {
+    KERNEL_AUTOOBJECT_TRAITS(KResourceLimit, KAutoObject);
+
 public:
-    explicit KResourceLimit(KernelCore& kernel, const Core::Timing::CoreTiming& core_timing_);
-    ~KResourceLimit();
+    explicit KResourceLimit(KernelCore& kernel);
+    virtual ~KResourceLimit();
+
+    void Initialize(const Core::Timing::CoreTiming* core_timing_);
+    virtual void Finalize() override;
 
     s64 GetLimitValue(LimitableResource which) const;
     s64 GetCurrentValue(LimitableResource which) const;
@@ -49,19 +54,7 @@ public:
     void Release(LimitableResource which, s64 value);
     void Release(LimitableResource which, s64 value, s64 hint);
 
-    std::string GetTypeName() const override {
-        return "KResourceLimit";
-    }
-    std::string GetName() const override {
-        return GetTypeName();
-    }
-
-    static constexpr HandleType HANDLE_TYPE = HandleType::ResourceLimit;
-    HandleType GetHandleType() const override {
-        return HANDLE_TYPE;
-    }
-
-    virtual void Finalize() override {}
+    static void PostDestroy([[maybe_unused]] uintptr_t arg) {}
 
 private:
     using ResourceArray = std::array<s64, static_cast<std::size_t>(LimitableResource::Count)>;
@@ -72,6 +65,6 @@ private:
     mutable KLightLock lock;
     s32 waiter_count{};
     KLightConditionVariable cond_var;
-    const Core::Timing::CoreTiming& core_timing;
+    const Core::Timing::CoreTiming* core_timing{};
 };
 } // namespace Kernel
diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp
index d1df97305..0115fe6d1 100644
--- a/src/core/hle/kernel/k_scheduler.cpp
+++ b/src/core/hle/kernel/k_scheduler.cpp
@@ -15,12 +15,12 @@
 #include "core/core.h"
 #include "core/core_timing.h"
 #include "core/cpu_manager.h"
+#include "core/hle/kernel/k_process.h"
 #include "core/hle/kernel/k_scheduler.h"
 #include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
 #include "core/hle/kernel/k_thread.h"
 #include "core/hle/kernel/kernel.h"
 #include "core/hle/kernel/physical_core.h"
-#include "core/hle/kernel/process.h"
 #include "core/hle/kernel/time_manager.h"
 
 namespace Kernel {
@@ -71,7 +71,7 @@ u64 KScheduler::UpdateHighestPriorityThread(KThread* highest_thread) {
         }
         if (state.should_count_idle) {
             if (highest_thread != nullptr) {
-                if (Process* process = highest_thread->GetOwnerProcess(); process != nullptr) {
+                if (KProcess* process = highest_thread->GetOwnerProcess(); process != nullptr) {
                     process->SetRunningThread(core_id, highest_thread, state.idle_count);
                 }
             } else {
@@ -104,7 +104,7 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
         if (top_thread != nullptr) {
             // If the thread has no waiters, we need to check if the process has a thread pinned.
             if (top_thread->GetNumKernelWaiters() == 0) {
-                if (Process* parent = top_thread->GetOwnerProcess(); parent != nullptr) {
+                if (KProcess* parent = top_thread->GetOwnerProcess(); parent != nullptr) {
                     if (KThread* pinned = parent->GetPinnedThread(static_cast<s32>(core_id));
                         pinned != nullptr && pinned != top_thread) {
                         // We prefer our parent's pinned thread if possible. However, we also don't
@@ -411,7 +411,7 @@ void KScheduler::YieldWithoutCoreMigration(KernelCore& kernel) {
 
     // Get the current thread and process.
     KThread& cur_thread = Kernel::GetCurrentThread(kernel);
-    Process& cur_process = *kernel.CurrentProcess();
+    KProcess& cur_process = *kernel.CurrentProcess();
 
     // If the thread's yield count matches, there's nothing for us to do.
     if (cur_thread.GetYieldScheduleCount() == cur_process.GetScheduledCount()) {
@@ -450,7 +450,7 @@ void KScheduler::YieldWithCoreMigration(KernelCore& kernel) {
 
     // Get the current thread and process.
     KThread& cur_thread = Kernel::GetCurrentThread(kernel);
-    Process& cur_process = *kernel.CurrentProcess();
+    KProcess& cur_process = *kernel.CurrentProcess();
 
     // If the thread's yield count matches, there's nothing for us to do.
     if (cur_thread.GetYieldScheduleCount() == cur_process.GetScheduledCount()) {
@@ -538,7 +538,7 @@ void KScheduler::YieldToAnyThread(KernelCore& kernel) {
 
     // Get the current thread and process.
     KThread& cur_thread = Kernel::GetCurrentThread(kernel);
-    Process& cur_process = *kernel.CurrentProcess();
+    KProcess& cur_process = *kernel.CurrentProcess();
 
     // If the thread's yield count matches, there's nothing for us to do.
     if (cur_thread.GetYieldScheduleCount() == cur_process.GetScheduledCount()) {
@@ -617,7 +617,12 @@ KScheduler::KScheduler(Core::System& system, s32 core_id) : system(system), core
     state.highest_priority_thread = nullptr;
 }
 
-KScheduler::~KScheduler() = default;
+KScheduler::~KScheduler() {
+    if (idle_thread) {
+        idle_thread->Close();
+        idle_thread = nullptr;
+    }
+}
 
 KThread* KScheduler::GetCurrentThread() const {
     if (auto result = current_thread.load(); result) {
@@ -719,7 +724,7 @@ void KScheduler::ScheduleImpl() {
 
     current_thread.store(next_thread);
 
-    Process* const previous_process = system.Kernel().CurrentProcess();
+    KProcess* const previous_process = system.Kernel().CurrentProcess();
 
     UpdateLastContextSwitchTime(previous_thread, previous_process);
 
@@ -775,7 +780,7 @@ void KScheduler::SwitchToCurrent() {
     }
 }
 
-void KScheduler::UpdateLastContextSwitchTime(KThread* thread, Process* process) {
+void KScheduler::UpdateLastContextSwitchTime(KThread* thread, KProcess* process) {
     const u64 prev_switch_ticks = last_context_switch_time;
     const u64 most_recent_switch_ticks = system.CoreTiming().GetCPUTicks();
     const u64 update_ticks = most_recent_switch_ticks - prev_switch_ticks;
@@ -792,13 +797,9 @@ void KScheduler::UpdateLastContextSwitchTime(KThread* thread, Process* process)
 }
 
 void KScheduler::Initialize() {
-    std::string name = "Idle Thread Id:" + std::to_string(core_id);
-    std::function<void(void*)> init_func = Core::CpuManager::GetIdleThreadStartFunc();
-    void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
-    auto thread_res = KThread::CreateThread(
-        system, ThreadType::Main, name, 0, KThread::IdleThreadPriority, 0,
-        static_cast<u32>(core_id), 0, nullptr, std::move(init_func), init_func_parameter);
-    idle_thread = thread_res.Unwrap().get();
+    idle_thread = KThread::Create(system.Kernel());
+    ASSERT(KThread::InitializeIdleThread(system, idle_thread, core_id).IsSuccess());
+    idle_thread->SetName(fmt::format("IdleThread:{}", core_id));
 }
 
 KScopedSchedulerLock::KScopedSchedulerLock(KernelCore& kernel)
diff --git a/src/core/hle/kernel/k_scheduler.h b/src/core/hle/kernel/k_scheduler.h
index 8e32865aa..b789a64a4 100644
--- a/src/core/hle/kernel/k_scheduler.h
+++ b/src/core/hle/kernel/k_scheduler.h
@@ -24,7 +24,7 @@ class System;
 namespace Kernel {
 
 class KernelCore;
-class Process;
+class KProcess;
 class SchedulerLock;
 class KThread;
 
@@ -165,7 +165,7 @@ private:
      * most recent tick count retrieved. No special arithmetic is
      * applied to it.
      */
-    void UpdateLastContextSwitchTime(KThread* thread, Process* process);
+    void UpdateLastContextSwitchTime(KThread* thread, KProcess* process);
 
     static void OnSwitch(void* this_scheduler);
     void SwitchToCurrent();
@@ -173,12 +173,12 @@ private:
     KThread* prev_thread{};
     std::atomic<KThread*> current_thread{};
 
-    KThread* idle_thread;
+    KThread* idle_thread{};
 
     std::shared_ptr<Common::Fiber> switch_fiber{};
 
     struct SchedulingState {
-        std::atomic<bool> needs_scheduling;
+        std::atomic<bool> needs_scheduling{};
         bool interrupt_task_thread_runnable{};
         bool should_count_idle{};
         u64 idle_count{};
diff --git a/src/core/hle/kernel/k_scoped_resource_reservation.h b/src/core/hle/kernel/k_scoped_resource_reservation.h
index c5deca00b..07272075d 100644
--- a/src/core/hle/kernel/k_scoped_resource_reservation.h
+++ b/src/core/hle/kernel/k_scoped_resource_reservation.h
@@ -8,15 +8,14 @@
 #pragma once
 
 #include "common/common_types.h"
+#include "core/hle/kernel/k_process.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)
+    explicit KScopedResourceReservation(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);
@@ -25,8 +24,7 @@ public:
         }
     }
 
-    explicit KScopedResourceReservation(std::shared_ptr<KResourceLimit> l, LimitableResource r,
-                                        s64 v = 1)
+    explicit KScopedResourceReservation(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);
@@ -35,10 +33,10 @@ public:
         }
     }
 
-    explicit KScopedResourceReservation(const Process* p, LimitableResource r, s64 v, s64 t)
+    explicit KScopedResourceReservation(const KProcess* p, LimitableResource r, s64 v, s64 t)
         : KScopedResourceReservation(p->GetResourceLimit(), r, v, t) {}
 
-    explicit KScopedResourceReservation(const Process* p, LimitableResource r, s64 v = 1)
+    explicit KScopedResourceReservation(const KProcess* p, LimitableResource r, s64 v = 1)
         : KScopedResourceReservation(p->GetResourceLimit(), r, v) {}
 
     ~KScopedResourceReservation() noexcept {
@@ -58,7 +56,7 @@ public:
     }
 
 private:
-    std::shared_ptr<KResourceLimit> resource_limit;
+    KResourceLimit* resource_limit{};
     s64 value;
     LimitableResource resource;
     bool success;
diff --git a/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h b/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h
index ebecf0c77..b5d405744 100644
--- a/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h
+++ b/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h
@@ -8,7 +8,7 @@
 #pragma once
 
 #include "common/common_types.h"
-#include "core/hle/kernel/handle_table.h"
+#include "core/hle/kernel/k_handle_table.h"
 #include "core/hle/kernel/k_thread.h"
 #include "core/hle/kernel/kernel.h"
 #include "core/hle/kernel/time_manager.h"
diff --git a/src/core/hle/kernel/k_server_port.cpp b/src/core/hle/kernel/k_server_port.cpp
new file mode 100644
index 000000000..5e44c48e2
--- /dev/null
+++ b/src/core/hle/kernel/k_server_port.cpp
@@ -0,0 +1,104 @@
+// Copyright 2021 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <tuple>
+#include "common/assert.h"
+#include "core/hle/kernel/k_client_port.h"
+#include "core/hle/kernel/k_port.h"
+#include "core/hle/kernel/k_scheduler.h"
+#include "core/hle/kernel/k_server_port.h"
+#include "core/hle/kernel/k_server_session.h"
+#include "core/hle/kernel/k_thread.h"
+#include "core/hle/kernel/svc_results.h"
+
+namespace Kernel {
+
+KServerPort::KServerPort(KernelCore& kernel) : KSynchronizationObject{kernel} {}
+KServerPort::~KServerPort() = default;
+
+void KServerPort::Initialize(KPort* parent_, std::string&& name_) {
+    // Set member variables.
+    parent = parent_;
+    name = std::move(name_);
+}
+
+bool KServerPort::IsLight() const {
+    return this->GetParent()->IsLight();
+}
+
+void KServerPort::CleanupSessions() {
+    // Ensure our preconditions are met.
+    if (this->IsLight()) {
+        UNIMPLEMENTED();
+    }
+
+    // Cleanup the session list.
+    while (true) {
+        // Get the last session in the list
+        KServerSession* session = nullptr;
+        {
+            KScopedSchedulerLock sl{kernel};
+            if (!session_list.empty()) {
+                session = std::addressof(session_list.front());
+                session_list.pop_front();
+            }
+        }
+
+        // Close the session.
+        if (session != nullptr) {
+            session->Close();
+        } else {
+            break;
+        }
+    }
+}
+
+void KServerPort::Destroy() {
+    // Note with our parent that we're closed.
+    parent->OnServerClosed();
+
+    // Perform necessary cleanup of our session lists.
+    this->CleanupSessions();
+
+    // Close our reference to our parent.
+    parent->Close();
+}
+
+bool KServerPort::IsSignaled() const {
+    if (this->IsLight()) {
+        UNIMPLEMENTED();
+        return false;
+    } else {
+        return !session_list.empty();
+    }
+}
+
+void KServerPort::EnqueueSession(KServerSession* session) {
+    ASSERT(!this->IsLight());
+
+    KScopedSchedulerLock sl{kernel};
+
+    // Add the session to our queue.
+    session_list.push_back(*session);
+    if (session_list.size() == 1) {
+        this->NotifyAvailable();
+    }
+}
+
+KServerSession* KServerPort::AcceptSession() {
+    ASSERT(!this->IsLight());
+
+    KScopedSchedulerLock sl{kernel};
+
+    // Return the first session in the list.
+    if (session_list.empty()) {
+        return nullptr;
+    }
+
+    KServerSession* session = std::addressof(session_list.front());
+    session_list.pop_front();
+    return session;
+}
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/k_server_port.h b/src/core/hle/kernel/k_server_port.h
new file mode 100644
index 000000000..558c8ed4d
--- /dev/null
+++ b/src/core/hle/kernel/k_server_port.h
@@ -0,0 +1,80 @@
+// Copyright 2021 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <boost/intrusive/list.hpp>
+
+#include "common/common_types.h"
+#include "core/hle/kernel/k_server_session.h"
+#include "core/hle/kernel/k_synchronization_object.h"
+#include "core/hle/result.h"
+
+namespace Kernel {
+
+class KernelCore;
+class KPort;
+class SessionRequestHandler;
+
+class KServerPort final : public KSynchronizationObject {
+    KERNEL_AUTOOBJECT_TRAITS(KServerPort, KSynchronizationObject);
+
+private:
+    using SessionList = boost::intrusive::list<KServerSession>;
+
+public:
+    explicit KServerPort(KernelCore& kernel);
+    virtual ~KServerPort() override;
+
+    using HLEHandler = std::shared_ptr<SessionRequestHandler>;
+
+    void Initialize(KPort* parent_, std::string&& name_);
+
+    /// Whether or not this server port has an HLE handler available.
+    bool HasHLEHandler() const {
+        return hle_handler != nullptr;
+    }
+
+    /// Gets the HLE handler for this port.
+    HLEHandler GetHLEHandler() const {
+        return hle_handler;
+    }
+
+    /**
+     * Sets the HLE handler template for the port. ServerSessions crated by connecting to this port
+     * will inherit a reference to this handler.
+     */
+    void SetHleHandler(HLEHandler hle_handler_) {
+        hle_handler = std::move(hle_handler_);
+    }
+
+    void EnqueueSession(KServerSession* pending_session);
+
+    KServerSession* AcceptSession();
+
+    const KPort* GetParent() const {
+        return parent;
+    }
+
+    bool IsLight() const;
+
+    // Overridden virtual functions.
+    virtual void Destroy() override;
+    virtual bool IsSignaled() const override;
+
+private:
+    void CleanupSessions();
+
+private:
+    SessionList session_list;
+    HLEHandler hle_handler;
+    KPort* parent{};
+};
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/k_server_session.cpp
similarity index 61%
rename from src/core/hle/kernel/server_session.cpp
rename to src/core/hle/kernel/k_server_session.cpp
index 790dbb998..c8acaa453 100644
--- a/src/core/hle/kernel/server_session.cpp
+++ b/src/core/hle/kernel/k_server_session.cpp
@@ -10,49 +10,39 @@
 #include "common/logging/log.h"
 #include "core/core_timing.h"
 #include "core/hle/ipc_helpers.h"
-#include "core/hle/kernel/client_port.h"
-#include "core/hle/kernel/client_session.h"
-#include "core/hle/kernel/handle_table.h"
 #include "core/hle/kernel/hle_ipc.h"
+#include "core/hle/kernel/k_client_port.h"
+#include "core/hle/kernel/k_handle_table.h"
+#include "core/hle/kernel/k_process.h"
 #include "core/hle/kernel/k_scheduler.h"
+#include "core/hle/kernel/k_server_session.h"
+#include "core/hle/kernel/k_session.h"
 #include "core/hle/kernel/k_thread.h"
 #include "core/hle/kernel/kernel.h"
-#include "core/hle/kernel/process.h"
-#include "core/hle/kernel/server_session.h"
-#include "core/hle/kernel/session.h"
 #include "core/memory.h"
 
 namespace Kernel {
 
-ServerSession::ServerSession(KernelCore& kernel) : KSynchronizationObject{kernel} {}
+KServerSession::KServerSession(KernelCore& kernel) : KSynchronizationObject{kernel} {}
 
-ServerSession::~ServerSession() {
+KServerSession::~KServerSession() {
     kernel.ReleaseServiceThread(service_thread);
 }
 
-ResultVal<std::shared_ptr<ServerSession>> ServerSession::Create(KernelCore& kernel,
-                                                                std::shared_ptr<Session> parent,
-                                                                std::string name) {
-    std::shared_ptr<ServerSession> session{std::make_shared<ServerSession>(kernel)};
-
-    session->name = std::move(name);
-    session->parent = std::move(parent);
-    session->service_thread = kernel.CreateServiceThread(session->name);
-
-    return MakeResult(std::move(session));
+void KServerSession::Initialize(KSession* parent_, std::string&& name_) {
+    // Set member variables.
+    parent = parent_;
+    name = std::move(name_);
+    service_thread = kernel.CreateServiceThread(name);
 }
 
-bool ServerSession::IsSignaled() const {
-    // Closed sessions should never wait, an error will be returned from svcReplyAndReceive.
-    if (!parent->Client()) {
-        return true;
-    }
+void KServerSession::Destroy() {
+    parent->OnServerClosed();
 
-    // Wait if we have no pending requests, or if we're currently handling a request.
-    return !pending_requesting_threads.empty() && currently_handling == nullptr;
+    parent->Close();
 }
 
-void ServerSession::ClientDisconnected() {
+void KServerSession::OnClientClosed() {
     // We keep a shared pointer to the hle handler to keep it alive throughout
     // the call to ClientDisconnected, as ClientDisconnected invalidates the
     // hle_handler member itself during the course of the function executing.
@@ -60,24 +50,31 @@ void ServerSession::ClientDisconnected() {
     if (handler) {
         // Note that after this returns, this server session's hle_handler is
         // invalidated (set to null).
-        handler->ClientDisconnected(SharedFrom(this));
+        handler->ClientDisconnected(this);
     }
-
-    // Clean up the list of client threads with pending requests, they are unneeded now that the
-    // client endpoint is closed.
-    pending_requesting_threads.clear();
-    currently_handling = nullptr;
 }
 
-void ServerSession::AppendDomainRequestHandler(std::shared_ptr<SessionRequestHandler> handler) {
+bool KServerSession::IsSignaled() const {
+    ASSERT(kernel.GlobalSchedulerContext().IsLocked());
+
+    // If the client is closed, we're always signaled.
+    if (parent->IsClientClosed()) {
+        return true;
+    }
+
+    // Otherwise, we're signaled if we have a request and aren't handling one.
+    return false;
+}
+
+void KServerSession::AppendDomainRequestHandler(std::shared_ptr<SessionRequestHandler> handler) {
     domain_request_handlers.push_back(std::move(handler));
 }
 
-std::size_t ServerSession::NumDomainRequestHandlers() const {
+std::size_t KServerSession::NumDomainRequestHandlers() const {
     return domain_request_handlers.size();
 }
 
-ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& context) {
+ResultCode KServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& context) {
     if (!context.HasDomainMessageHeader()) {
         return RESULT_SUCCESS;
     }
@@ -116,23 +113,21 @@ ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& con
     return RESULT_SUCCESS;
 }
 
-ResultCode ServerSession::QueueSyncRequest(std::shared_ptr<KThread> thread,
-                                           Core::Memory::Memory& memory) {
+ResultCode KServerSession::QueueSyncRequest(KThread* thread, Core::Memory::Memory& memory) {
     u32* cmd_buf{reinterpret_cast<u32*>(memory.GetPointer(thread->GetTLSAddress()))};
-    auto context =
-        std::make_shared<HLERequestContext>(kernel, memory, SharedFrom(this), std::move(thread));
+    auto context = std::make_shared<HLERequestContext>(kernel, memory, this, thread);
 
     context->PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf);
 
     if (auto strong_ptr = service_thread.lock()) {
-        strong_ptr->QueueSyncRequest(*this, std::move(context));
+        strong_ptr->QueueSyncRequest(*parent, std::move(context));
         return RESULT_SUCCESS;
     }
 
     return RESULT_SUCCESS;
 }
 
-ResultCode ServerSession::CompleteSyncRequest(HLERequestContext& context) {
+ResultCode KServerSession::CompleteSyncRequest(HLERequestContext& context) {
     ResultCode result = RESULT_SUCCESS;
     // If the session has been converted to a domain, handle the domain request
     if (IsDomain() && context.HasDomainMessageHeader()) {
@@ -161,10 +156,9 @@ ResultCode ServerSession::CompleteSyncRequest(HLERequestContext& context) {
     return result;
 }
 
-ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<KThread> thread,
-                                            Core::Memory::Memory& memory,
-                                            Core::Timing::CoreTiming& core_timing) {
-    return QueueSyncRequest(std::move(thread), memory);
+ResultCode KServerSession::HandleSyncRequest(KThread* thread, Core::Memory::Memory& memory,
+                                             Core::Timing::CoreTiming& core_timing) {
+    return QueueSyncRequest(thread, memory);
 }
 
 } // namespace Kernel
diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/k_server_session.h
similarity index 54%
rename from src/core/hle/kernel/server_session.h
rename to src/core/hle/kernel/k_server_session.h
index c42d5ee59..77095bb85 100644
--- a/src/core/hle/kernel/server_session.h
+++ b/src/core/hle/kernel/k_server_session.h
@@ -9,6 +9,8 @@
 #include <utility>
 #include <vector>
 
+#include <boost/intrusive/list.hpp>
+
 #include "common/threadsafe_queue.h"
 #include "core/hle/kernel/k_synchronization_object.h"
 #include "core/hle/kernel/service_thread.h"
@@ -27,55 +29,35 @@ namespace Kernel {
 
 class HLERequestContext;
 class KernelCore;
-class Session;
+class KSession;
 class SessionRequestHandler;
 class KThread;
 
-/**
- * Kernel object representing the server endpoint of an IPC session. Sessions are the basic CTR-OS
- * primitive for communication between different processes, and are used to implement service calls
- * to the various system services.
- *
- * To make a service call, the client must write the command header and parameters to the buffer
- * located at offset 0x80 of the TLS (Thread-Local Storage) area, then execute a SendSyncRequest
- * SVC call with its ClientSession handle. The kernel will read the command header, using it to
- * marshall the parameters to the process at the server endpoint of the session.
- * After the server replies to the request, the response is marshalled back to the caller's
- * TLS buffer and control is transferred back to it.
- */
-class ServerSession final : public KSynchronizationObject {
+class KServerSession final : public KSynchronizationObject,
+                             public boost::intrusive::list_base_hook<> {
+    KERNEL_AUTOOBJECT_TRAITS(KServerSession, KSynchronizationObject);
+
     friend class ServiceThread;
 
 public:
-    explicit ServerSession(KernelCore& kernel);
-    ~ServerSession() override;
+    explicit KServerSession(KernelCore& kernel);
+    virtual ~KServerSession() override;
 
-    friend class Session;
+    virtual void Destroy() override;
 
-    static ResultVal<std::shared_ptr<ServerSession>> Create(KernelCore& kernel,
-                                                            std::shared_ptr<Session> parent,
-                                                            std::string name = "Unknown");
+    void Initialize(KSession* parent_, std::string&& name_);
 
-    std::string GetTypeName() const override {
-        return "ServerSession";
+    KSession* GetParent() {
+        return parent;
     }
 
-    std::string GetName() const override {
-        return name;
+    const KSession* GetParent() const {
+        return parent;
     }
 
-    static constexpr HandleType HANDLE_TYPE = HandleType::ServerSession;
-    HandleType GetHandleType() const override {
-        return HANDLE_TYPE;
-    }
+    virtual bool IsSignaled() const override;
 
-    Session* GetParent() {
-        return parent.get();
-    }
-
-    const Session* GetParent() const {
-        return parent.get();
-    }
+    void OnClientClosed();
 
     /**
      * Sets the HLE handler for the session. This handler will be called to service IPC requests
@@ -95,12 +77,9 @@ public:
      *
      * @returns ResultCode from the operation.
      */
-    ResultCode HandleSyncRequest(std::shared_ptr<KThread> thread, Core::Memory::Memory& memory,
+    ResultCode HandleSyncRequest(KThread* thread, Core::Memory::Memory& memory,
                                  Core::Timing::CoreTiming& core_timing);
 
-    /// Called when a client disconnection occurs.
-    void ClientDisconnected();
-
     /// Adds a new domain request handler to the collection of request handlers within
     /// this ServerSession instance.
     void AppendDomainRequestHandler(std::shared_ptr<SessionRequestHandler> handler);
@@ -124,13 +103,9 @@ public:
         convert_to_domain = true;
     }
 
-    bool IsSignaled() const override;
-
-    void Finalize() override {}
-
 private:
     /// Queues a sync request from the emulated application.
-    ResultCode QueueSyncRequest(std::shared_ptr<KThread> thread, Core::Memory::Memory& memory);
+    ResultCode QueueSyncRequest(KThread* thread, Core::Memory::Memory& memory);
 
     /// Completes a sync request from the emulated application.
     ResultCode CompleteSyncRequest(HLERequestContext& context);
@@ -139,33 +114,20 @@ private:
     /// object handle.
     ResultCode HandleDomainSyncRequest(Kernel::HLERequestContext& context);
 
-    /// The parent session, which links to the client endpoint.
-    std::shared_ptr<Session> parent;
-
     /// This session's HLE request handler (applicable when not a domain)
     std::shared_ptr<SessionRequestHandler> hle_handler;
 
     /// This is the list of domain request handlers (after conversion to a domain)
     std::vector<std::shared_ptr<SessionRequestHandler>> domain_request_handlers;
 
-    /// List of threads that are pending a response after a sync request. This list is processed in
-    /// a LIFO manner, thus, the last request will be dispatched first.
-    /// TODO(Subv): Verify if this is indeed processed in LIFO using a hardware test.
-    std::vector<std::shared_ptr<KThread>> pending_requesting_threads;
-
-    /// Thread whose request is currently being handled. A request is considered "handled" when a
-    /// response is sent via svcReplyAndReceive.
-    /// TODO(Subv): Find a better name for this.
-    std::shared_ptr<KThread> currently_handling;
-
     /// When set to True, converts the session to a domain at the end of the command
     bool convert_to_domain{};
 
-    /// The name of this session (optional)
-    std::string name;
-
     /// Thread to dispatch service requests
     std::weak_ptr<ServiceThread> service_thread;
+
+    /// KSession that owns this KServerSession
+    KSession* parent{};
 };
 
 } // namespace Kernel
diff --git a/src/core/hle/kernel/k_session.cpp b/src/core/hle/kernel/k_session.cpp
new file mode 100644
index 000000000..7b0bc177d
--- /dev/null
+++ b/src/core/hle/kernel/k_session.cpp
@@ -0,0 +1,85 @@
+// Copyright 2021 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/assert.h"
+#include "core/hle/kernel/k_client_port.h"
+#include "core/hle/kernel/k_client_session.h"
+#include "core/hle/kernel/k_scoped_resource_reservation.h"
+#include "core/hle/kernel/k_server_session.h"
+#include "core/hle/kernel/k_session.h"
+
+namespace Kernel {
+
+KSession::KSession(KernelCore& kernel)
+    : KAutoObjectWithSlabHeapAndContainer{kernel}, server{kernel}, client{kernel} {}
+KSession::~KSession() = default;
+
+void KSession::Initialize(KClientPort* port_, const std::string& name_) {
+    // Increment reference count.
+    // Because reference count is one on creation, this will result
+    // in a reference count of two. Thus, when both server and client are closed
+    // this object will be destroyed.
+    Open();
+
+    // Create our sub sessions.
+    KAutoObject::Create(std::addressof(server));
+    KAutoObject::Create(std::addressof(client));
+
+    // Initialize our sub sessions.
+    server.Initialize(this, name_ + ":Server");
+    client.Initialize(this, name_ + ":Client");
+
+    // Set state and name.
+    SetState(State::Normal);
+    name = name_;
+
+    // Set our owner process.
+    process = kernel.CurrentProcess();
+    process->Open();
+
+    // Set our port.
+    port = port_;
+    if (port != nullptr) {
+        port->Open();
+    }
+
+    // Mark initialized.
+    initialized = true;
+}
+
+void KSession::Finalize() {
+    if (port == nullptr) {
+        return;
+    }
+
+    port->OnSessionFinalized();
+    port->Close();
+}
+
+void KSession::OnServerClosed() {
+    if (GetState() != State::Normal) {
+        return;
+    }
+
+    SetState(State::ServerClosed);
+    client.OnServerClosed();
+}
+
+void KSession::OnClientClosed() {
+    if (GetState() != State::Normal) {
+        return;
+    }
+
+    SetState(State::ClientClosed);
+    server.OnClientClosed();
+}
+
+void KSession::PostDestroy(uintptr_t arg) {
+    // Release the session count resource the owner process holds.
+    KProcess* owner = reinterpret_cast<KProcess*>(arg);
+    owner->GetResourceLimit()->Release(LimitableResource::Sessions, 1);
+    owner->Close();
+}
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/k_session.h b/src/core/hle/kernel/k_session.h
new file mode 100644
index 000000000..4321b7885
--- /dev/null
+++ b/src/core/hle/kernel/k_session.h
@@ -0,0 +1,96 @@
+// Copyright 2021 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <atomic>
+#include <string>
+
+#include "core/hle/kernel/k_client_session.h"
+#include "core/hle/kernel/k_server_session.h"
+#include "core/hle/kernel/slab_helpers.h"
+
+namespace Kernel {
+
+class KSession final : public KAutoObjectWithSlabHeapAndContainer<KSession, KAutoObjectWithList> {
+    KERNEL_AUTOOBJECT_TRAITS(KSession, KAutoObject);
+
+public:
+    explicit KSession(KernelCore& kernel);
+    virtual ~KSession() override;
+
+    void Initialize(KClientPort* port_, const std::string& name_);
+
+    virtual void Finalize() override;
+
+    virtual bool IsInitialized() const override {
+        return initialized;
+    }
+
+    virtual uintptr_t GetPostDestroyArgument() const override {
+        return reinterpret_cast<uintptr_t>(process);
+    }
+
+    static void PostDestroy(uintptr_t arg);
+
+    void OnServerClosed();
+
+    void OnClientClosed();
+
+    bool IsServerClosed() const {
+        return this->GetState() != State::Normal;
+    }
+
+    bool IsClientClosed() const {
+        return this->GetState() != State::Normal;
+    }
+
+    KClientSession& GetClientSession() {
+        return client;
+    }
+
+    KServerSession& GetServerSession() {
+        return server;
+    }
+
+    const KClientSession& GetClientSession() const {
+        return client;
+    }
+
+    const KServerSession& GetServerSession() const {
+        return server;
+    }
+
+    const KClientPort* GetParent() const {
+        return port;
+    }
+
+private:
+    enum class State : u8 {
+        Invalid = 0,
+        Normal = 1,
+        ClientClosed = 2,
+        ServerClosed = 3,
+    };
+
+private:
+    void SetState(State state) {
+        atomic_state = static_cast<u8>(state);
+    }
+
+    State GetState() const {
+        return static_cast<State>(atomic_state.load(std::memory_order_relaxed));
+    }
+
+private:
+    KServerSession server;
+    KClientSession client;
+    std::atomic<std::underlying_type_t<State>> atomic_state{
+        static_cast<std::underlying_type_t<State>>(State::Invalid)};
+    KClientPort* port{};
+    KProcess* process{};
+    bool initialized{};
+};
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/k_shared_memory.cpp b/src/core/hle/kernel/k_shared_memory.cpp
index 9b14f42b5..1da57a4c3 100644
--- a/src/core/hle/kernel/k_shared_memory.cpp
+++ b/src/core/hle/kernel/k_shared_memory.cpp
@@ -8,50 +8,74 @@
 #include "core/hle/kernel/k_scoped_resource_reservation.h"
 #include "core/hle/kernel/k_shared_memory.h"
 #include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/svc_results.h"
 
 namespace Kernel {
 
-KSharedMemory::KSharedMemory(KernelCore& kernel, Core::DeviceMemory& device_memory)
-    : Object{kernel}, device_memory{device_memory} {}
+KSharedMemory::KSharedMemory(KernelCore& kernel) : KAutoObjectWithSlabHeapAndContainer{kernel} {}
 
 KSharedMemory::~KSharedMemory() {
     kernel.GetSystemResourceLimit()->Release(LimitableResource::PhysicalMemory, size);
 }
 
-std::shared_ptr<KSharedMemory> KSharedMemory::Create(
-    KernelCore& kernel, Core::DeviceMemory& device_memory, Process* owner_process,
-    KPageLinkedList&& page_list, KMemoryPermission owner_permission,
-    KMemoryPermission user_permission, PAddr physical_address, std::size_t size, std::string name) {
+ResultCode KSharedMemory::Initialize(KernelCore& kernel_, Core::DeviceMemory& device_memory_,
+                                     KProcess* owner_process_, KPageLinkedList&& page_list_,
+                                     Svc::MemoryPermission owner_permission_,
+                                     Svc::MemoryPermission user_permission_,
+                                     PAddr physical_address_, std::size_t size_,
+                                     std::string name_) {
+    // Set members.
+    owner_process = owner_process_;
+    device_memory = &device_memory_;
+    page_list = std::move(page_list_);
+    owner_permission = owner_permission_;
+    user_permission = user_permission_;
+    physical_address = physical_address_;
+    size = size_;
+    name = name_;
 
-    const auto resource_limit = kernel.GetSystemResourceLimit();
-    KScopedResourceReservation memory_reservation(resource_limit, LimitableResource::PhysicalMemory,
-                                                  size);
-    ASSERT(memory_reservation.Succeeded());
+    // Get the resource limit.
+    KResourceLimit* reslimit = kernel.GetSystemResourceLimit();
 
-    std::shared_ptr<KSharedMemory> shared_memory{
-        std::make_shared<KSharedMemory>(kernel, device_memory)};
-
-    shared_memory->owner_process = owner_process;
-    shared_memory->page_list = std::move(page_list);
-    shared_memory->owner_permission = owner_permission;
-    shared_memory->user_permission = user_permission;
-    shared_memory->physical_address = physical_address;
-    shared_memory->size = size;
-    shared_memory->name = name;
+    // Reserve memory for ourselves.
+    KScopedResourceReservation memory_reservation(reslimit, LimitableResource::PhysicalMemory,
+                                                  size_);
+    R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
 
+    // Commit our reservation.
     memory_reservation.Commit();
-    return shared_memory;
+
+    // Set our resource limit.
+    resource_limit = reslimit;
+    resource_limit->Open();
+
+    // Mark initialized.
+    is_initialized = true;
+
+    // Clear all pages in the memory.
+    std::memset(device_memory_.GetPointer(physical_address_), 0, size_);
+
+    return RESULT_SUCCESS;
 }
 
-ResultCode KSharedMemory::Map(Process& target_process, VAddr address, std::size_t size,
-                              KMemoryPermission permissions) {
+void KSharedMemory::Finalize() {
+    // Release the memory reservation.
+    resource_limit->Release(LimitableResource::PhysicalMemory, size);
+    resource_limit->Close();
+
+    // Perform inherited finalization.
+    KAutoObjectWithSlabHeapAndContainer<KSharedMemory, KAutoObjectWithList>::Finalize();
+}
+
+ResultCode KSharedMemory::Map(KProcess& target_process, VAddr address, std::size_t size,
+                              Svc::MemoryPermission permissions) {
     const u64 page_count{(size + PageSize - 1) / PageSize};
 
     if (page_list.GetNumPages() != page_count) {
         UNIMPLEMENTED_MSG("Page count does not match");
     }
 
-    const KMemoryPermission expected =
+    const Svc::MemoryPermission expected =
         &target_process == owner_process ? owner_permission : user_permission;
 
     if (permissions != expected) {
@@ -59,7 +83,17 @@ ResultCode KSharedMemory::Map(Process& target_process, VAddr address, std::size_
     }
 
     return target_process.PageTable().MapPages(address, page_list, KMemoryState::Shared,
-                                               permissions);
+                                               ConvertToKMemoryPermission(permissions));
+}
+
+ResultCode KSharedMemory::Unmap(KProcess& target_process, VAddr address, std::size_t size) {
+    const u64 page_count{(size + PageSize - 1) / PageSize};
+
+    if (page_list.GetNumPages() != page_count) {
+        UNIMPLEMENTED_MSG("Page count does not match");
+    }
+
+    return target_process.PageTable().UnmapPages(address, page_list, KMemoryState::Shared);
 }
 
 } // namespace Kernel
diff --git a/src/core/hle/kernel/k_shared_memory.h b/src/core/hle/kernel/k_shared_memory.h
index 016e34be5..28939c93c 100644
--- a/src/core/hle/kernel/k_shared_memory.h
+++ b/src/core/hle/kernel/k_shared_memory.h
@@ -11,37 +11,27 @@
 #include "core/device_memory.h"
 #include "core/hle/kernel/k_memory_block.h"
 #include "core/hle/kernel/k_page_linked_list.h"
-#include "core/hle/kernel/object.h"
-#include "core/hle/kernel/process.h"
+#include "core/hle/kernel/k_process.h"
+#include "core/hle/kernel/slab_helpers.h"
 #include "core/hle/result.h"
 
 namespace Kernel {
 
 class KernelCore;
 
-class KSharedMemory final : public Object {
+class KSharedMemory final
+    : public KAutoObjectWithSlabHeapAndContainer<KSharedMemory, KAutoObjectWithList> {
+    KERNEL_AUTOOBJECT_TRAITS(KSharedMemory, KAutoObject);
+
 public:
-    explicit KSharedMemory(KernelCore& kernel, Core::DeviceMemory& device_memory);
+    explicit KSharedMemory(KernelCore& kernel);
     ~KSharedMemory() override;
 
-    static std::shared_ptr<KSharedMemory> Create(
-        KernelCore& kernel, Core::DeviceMemory& device_memory, Process* owner_process,
-        KPageLinkedList&& page_list, KMemoryPermission owner_permission,
-        KMemoryPermission user_permission, PAddr physical_address, std::size_t size,
-        std::string name);
-
-    std::string GetTypeName() const override {
-        return "SharedMemory";
-    }
-
-    std::string GetName() const override {
-        return name;
-    }
-
-    static constexpr HandleType HANDLE_TYPE = HandleType::SharedMemory;
-    HandleType GetHandleType() const override {
-        return HANDLE_TYPE;
-    }
+    ResultCode Initialize(KernelCore& kernel_, Core::DeviceMemory& device_memory_,
+                          KProcess* owner_process_, KPageLinkedList&& page_list_,
+                          Svc::MemoryPermission owner_permission_,
+                          Svc::MemoryPermission user_permission_, PAddr physical_address_,
+                          std::size_t size_, std::string name_);
 
     /**
      * Maps a shared memory block to an address in the target process' address space
@@ -50,8 +40,16 @@ public:
      * @param size Size of the shared memory block to map
      * @param permissions Memory block map permissions (specified by SVC field)
      */
-    ResultCode Map(Process& target_process, VAddr address, std::size_t size,
-                   KMemoryPermission permissions);
+    ResultCode Map(KProcess& target_process, VAddr address, std::size_t size,
+                   Svc::MemoryPermission permissions);
+
+    /**
+     * Unmaps a shared memory block from an address in the target process' address space
+     * @param target_process Process on which to unmap the memory block
+     * @param address Address in system memory to unmap shared memory block
+     * @param size Size of the shared memory block to unmap
+     */
+    ResultCode Unmap(KProcess& target_process, VAddr address, std::size_t size);
 
     /**
      * Gets a pointer to the shared memory block
@@ -59,7 +57,7 @@ public:
      * @return A pointer to the shared memory block from the specified offset
      */
     u8* GetPointer(std::size_t offset = 0) {
-        return device_memory.GetPointer(physical_address + offset);
+        return device_memory->GetPointer(physical_address + offset);
     }
 
     /**
@@ -68,20 +66,26 @@ public:
      * @return A pointer to the shared memory block from the specified offset
      */
     const u8* GetPointer(std::size_t offset = 0) const {
-        return device_memory.GetPointer(physical_address + offset);
+        return device_memory->GetPointer(physical_address + offset);
     }
 
-    void Finalize() override {}
+    virtual void Finalize() override;
+
+    virtual bool IsInitialized() const override {
+        return is_initialized;
+    }
+    static void PostDestroy([[maybe_unused]] uintptr_t arg) {}
 
 private:
-    Core::DeviceMemory& device_memory;
-    Process* owner_process{};
+    Core::DeviceMemory* device_memory;
+    KProcess* owner_process{};
     KPageLinkedList page_list;
-    KMemoryPermission owner_permission{};
-    KMemoryPermission user_permission{};
+    Svc::MemoryPermission owner_permission{};
+    Svc::MemoryPermission user_permission{};
     PAddr physical_address{};
     std::size_t size{};
-    std::string name;
+    KResourceLimit* resource_limit{};
+    bool is_initialized{};
 };
 
 } // namespace Kernel
diff --git a/src/core/hle/kernel/k_slab_heap.h b/src/core/hle/kernel/k_slab_heap.h
index aa4471d2f..5ce9a1d7c 100644
--- a/src/core/hle/kernel/k_slab_heap.h
+++ b/src/core/hle/kernel/k_slab_heap.h
@@ -97,6 +97,7 @@ public:
     void FreeImpl(void* obj) {
         // Don't allow freeing an object that wasn't allocated from this heap
         ASSERT(Contains(reinterpret_cast<uintptr_t>(obj)));
+
         impl.Free(obj);
     }
 
@@ -148,6 +149,14 @@ public:
         return obj;
     }
 
+    T* AllocateWithKernel(KernelCore& kernel) {
+        T* obj = static_cast<T*>(AllocateImpl());
+        if (obj != nullptr) {
+            new (obj) T(kernel);
+        }
+        return obj;
+    }
+
     void Free(T* obj) {
         FreeImpl(obj);
     }
diff --git a/src/core/hle/kernel/k_synchronization_object.cpp b/src/core/hle/kernel/k_synchronization_object.cpp
index 82f72a0fe..460b8a714 100644
--- a/src/core/hle/kernel/k_synchronization_object.cpp
+++ b/src/core/hle/kernel/k_synchronization_object.cpp
@@ -13,6 +13,11 @@
 
 namespace Kernel {
 
+void KSynchronizationObject::Finalize() {
+    this->OnFinalizeSynchronizationObject();
+    KAutoObject::Finalize();
+}
+
 ResultCode KSynchronizationObject::Wait(KernelCore& kernel, s32* out_index,
                                         KSynchronizationObject** objects, const s32 num_objects,
                                         s64 timeout) {
@@ -130,10 +135,7 @@ ResultCode KSynchronizationObject::Wait(KernelCore& kernel, s32* out_index,
     return wait_result;
 }
 
-KSynchronizationObject::KSynchronizationObject(KernelCore& kernel) : Object{kernel} {}
-
-KSynchronizationObject::KSynchronizationObject(KernelCore& kernel, std::string&& name)
-    : Object{kernel, std::move(name)} {}
+KSynchronizationObject::KSynchronizationObject(KernelCore& kernel) : KAutoObjectWithList{kernel} {}
 
 KSynchronizationObject::~KSynchronizationObject() = default;
 
diff --git a/src/core/hle/kernel/k_synchronization_object.h b/src/core/hle/kernel/k_synchronization_object.h
index 5803718fd..a41dd1220 100644
--- a/src/core/hle/kernel/k_synchronization_object.h
+++ b/src/core/hle/kernel/k_synchronization_object.h
@@ -6,7 +6,7 @@
 
 #include <vector>
 
-#include "core/hle/kernel/object.h"
+#include "core/hle/kernel/k_auto_object.h"
 #include "core/hle/result.h"
 
 namespace Kernel {
@@ -16,7 +16,9 @@ class Synchronization;
 class KThread;
 
 /// Class that represents a Kernel object that a thread can be waiting on
-class KSynchronizationObject : public Object {
+class KSynchronizationObject : public KAutoObjectWithList {
+    KERNEL_AUTOOBJECT_TRAITS(KSynchronizationObject, KAutoObject);
+
 public:
     struct ThreadListNode {
         ThreadListNode* next{};
@@ -27,15 +29,18 @@ public:
                                          KSynchronizationObject** objects, const s32 num_objects,
                                          s64 timeout);
 
+    virtual void Finalize() override;
+
     [[nodiscard]] virtual bool IsSignaled() const = 0;
 
     [[nodiscard]] std::vector<KThread*> GetWaitingThreadsForDebugging() const;
 
 protected:
     explicit KSynchronizationObject(KernelCore& kernel);
-    explicit KSynchronizationObject(KernelCore& kernel, std::string&& name);
     virtual ~KSynchronizationObject();
 
+    virtual void OnFinalizeSynchronizationObject() {}
+
     void NotifyAvailable(ResultCode result);
     void NotifyAvailable() {
         return this->NotifyAvailable(RESULT_SUCCESS);
@@ -46,14 +51,4 @@ private:
     ThreadListNode* thread_list_tail{};
 };
 
-// Specialization of DynamicObjectCast for KSynchronizationObjects
-template <>
-inline std::shared_ptr<KSynchronizationObject> DynamicObjectCast<KSynchronizationObject>(
-    std::shared_ptr<Object> object) {
-    if (object != nullptr && object->IsWaitable()) {
-        return std::static_pointer_cast<KSynchronizationObject>(object);
-    }
-    return nullptr;
-}
-
 } // namespace Kernel
diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp
index e0f53287c..ef6dfeeca 100644
--- a/src/core/hle/kernel/k_thread.cpp
+++ b/src/core/hle/kernel/k_thread.cpp
@@ -18,17 +18,16 @@
 #include "core/core.h"
 #include "core/cpu_manager.h"
 #include "core/hardware_properties.h"
-#include "core/hle/kernel/handle_table.h"
 #include "core/hle/kernel/k_condition_variable.h"
+#include "core/hle/kernel/k_handle_table.h"
 #include "core/hle/kernel/k_memory_layout.h"
+#include "core/hle/kernel/k_process.h"
 #include "core/hle/kernel/k_resource_limit.h"
 #include "core/hle/kernel/k_scheduler.h"
 #include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
 #include "core/hle/kernel/k_thread.h"
 #include "core/hle/kernel/k_thread_queue.h"
 #include "core/hle/kernel/kernel.h"
-#include "core/hle/kernel/object.h"
-#include "core/hle/kernel/process.h"
 #include "core/hle/kernel/svc_results.h"
 #include "core/hle/kernel/time_manager.h"
 #include "core/hle/result.h"
@@ -62,11 +61,11 @@ static void ResetThreadContext64(Core::ARM_Interface::ThreadContext64& context,
 namespace Kernel {
 
 KThread::KThread(KernelCore& kernel)
-    : KSynchronizationObject{kernel}, activity_pause_lock{kernel} {}
+    : KAutoObjectWithSlabHeapAndContainer{kernel}, activity_pause_lock{kernel} {}
 KThread::~KThread() = default;
 
 ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_stack_top, s32 prio,
-                               s32 virt_core, Process* owner, ThreadType type) {
+                               s32 virt_core, KProcess* owner, ThreadType type) {
     // Assert parameters are valid.
     ASSERT((type == ThreadType::Main) ||
            (Svc::HighestThreadPriority <= prio && prio <= Svc::LowestThreadPriority));
@@ -177,6 +176,7 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s
     // Set parent, if relevant.
     if (owner != nullptr) {
         parent = owner;
+        parent->Open();
         parent->IncrementThreadCount();
     }
 
@@ -209,14 +209,56 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s
 }
 
 ResultCode KThread::InitializeThread(KThread* thread, KThreadFunction func, uintptr_t arg,
-                                     VAddr user_stack_top, s32 prio, s32 core, Process* owner,
-                                     ThreadType type) {
+                                     VAddr user_stack_top, s32 prio, s32 core, KProcess* owner,
+                                     ThreadType type, std::function<void(void*)>&& init_func,
+                                     void* init_func_parameter) {
     // Initialize the thread.
     R_TRY(thread->Initialize(func, arg, user_stack_top, prio, core, owner, type));
 
+    // Initialize host context.
+    thread->host_context =
+        std::make_shared<Common::Fiber>(std::move(init_func), init_func_parameter);
+
     return RESULT_SUCCESS;
 }
 
+ResultCode KThread::InitializeDummyThread(KThread* thread) {
+    return thread->Initialize({}, {}, {}, DefaultThreadPriority, 3, {}, ThreadType::Main);
+}
+
+ResultCode KThread::InitializeIdleThread(Core::System& system, KThread* thread, s32 virt_core) {
+    return InitializeThread(thread, {}, {}, {}, IdleThreadPriority, virt_core, {}, ThreadType::Main,
+                            Core::CpuManager::GetIdleThreadStartFunc(),
+                            system.GetCpuManager().GetStartFuncParamater());
+}
+
+ResultCode KThread::InitializeHighPriorityThread(Core::System& system, KThread* thread,
+                                                 KThreadFunction func, uintptr_t arg,
+                                                 s32 virt_core) {
+    return InitializeThread(thread, func, arg, {}, {}, virt_core, nullptr, ThreadType::HighPriority,
+                            Core::CpuManager::GetSuspendThreadStartFunc(),
+                            system.GetCpuManager().GetStartFuncParamater());
+}
+
+ResultCode KThread::InitializeUserThread(Core::System& system, KThread* thread,
+                                         KThreadFunction func, uintptr_t arg, VAddr user_stack_top,
+                                         s32 prio, s32 virt_core, KProcess* owner) {
+    system.Kernel().GlobalSchedulerContext().AddThread(thread);
+    return InitializeThread(thread, func, arg, user_stack_top, prio, virt_core, owner,
+                            ThreadType::User, Core::CpuManager::GetGuestThreadStartFunc(),
+                            system.GetCpuManager().GetStartFuncParamater());
+}
+
+void KThread::PostDestroy(uintptr_t arg) {
+    KProcess* owner = reinterpret_cast<KProcess*>(arg & ~1ULL);
+    const bool resource_limit_release_hint = (arg & 1);
+    const s64 hint_value = (resource_limit_release_hint ? 0 : 1);
+    if (owner != nullptr) {
+        owner->GetResourceLimit()->Release(LimitableResource::Threads, 1, hint_value);
+        owner->Close();
+    }
+}
+
 void KThread::Finalize() {
     // If the thread has an owner process, unregister it.
     if (parent != nullptr) {
@@ -246,8 +288,10 @@ void KThread::Finalize() {
     // Decrement the parent process's thread count.
     if (parent != nullptr) {
         parent->DecrementThreadCount();
-        parent->GetResourceLimit()->Release(LimitableResource::Threads, 1);
     }
+
+    // Perform inherited finalization.
+    KAutoObjectWithSlabHeapAndContainer<KThread, KSynchronizationObject>::Finalize();
 }
 
 bool KThread::IsSignaled() const {
@@ -294,6 +338,9 @@ void KThread::StartTermination() {
 
     // Register terminated dpc flag.
     RegisterDpc(DpcFlag::Terminated);
+
+    // Close the thread.
+    this->Close();
 }
 
 void KThread::Pin() {
@@ -932,7 +979,7 @@ void KThread::Exit() {
 
     // Release the thread resource hint from parent.
     if (parent != nullptr) {
-        // TODO(bunnei): Hint that the resource is about to be released.
+        parent->GetResourceLimit()->Release(Kernel::LimitableResource::Threads, 0, 1);
         resource_limit_release_hint = true;
     }
 
@@ -995,56 +1042,6 @@ std::shared_ptr<Common::Fiber>& KThread::GetHostContext() {
     return host_context;
 }
 
-ResultVal<std::shared_ptr<KThread>> KThread::CreateThread(Core::System& system,
-                                                          ThreadType type_flags, std::string name,
-                                                          VAddr entry_point, u32 priority, u64 arg,
-                                                          s32 processor_id, VAddr stack_top,
-                                                          Process* owner_process) {
-    auto& kernel = system.Kernel();
-
-    std::shared_ptr<KThread> thread = std::make_shared<KThread>(kernel);
-
-    if (const auto result =
-            thread->InitializeThread(thread.get(), entry_point, arg, stack_top, priority,
-                                     processor_id, owner_process, type_flags);
-        result.IsError()) {
-        return result;
-    }
-
-    thread->name = name;
-
-    auto& scheduler = kernel.GlobalSchedulerContext();
-    scheduler.AddThread(thread);
-
-    return MakeResult<std::shared_ptr<KThread>>(std::move(thread));
-}
-
-ResultVal<std::shared_ptr<KThread>> KThread::CreateThread(
-    Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point, u32 priority,
-    u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process,
-    std::function<void(void*)>&& thread_start_func, void* thread_start_parameter) {
-    auto thread_result = CreateThread(system, type_flags, name, entry_point, priority, arg,
-                                      processor_id, stack_top, owner_process);
-
-    if (thread_result.Succeeded()) {
-        (*thread_result)->host_context =
-            std::make_shared<Common::Fiber>(std::move(thread_start_func), thread_start_parameter);
-    }
-
-    return thread_result;
-}
-
-ResultVal<std::shared_ptr<KThread>> KThread::CreateUserThread(
-    Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point, u32 priority,
-    u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process) {
-    std::function<void(void*)> init_func = Core::CpuManager::GetGuestThreadStartFunc();
-
-    void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
-
-    return CreateThread(system, type_flags, name, entry_point, priority, arg, processor_id,
-                        stack_top, owner_process, std::move(init_func), init_func_parameter);
-}
-
 KThread* GetCurrentThreadPointer(KernelCore& kernel) {
     return kernel.GetCurrentEmuThread();
 }
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h
index b442dfe57..4145ef56c 100644
--- a/src/core/hle/kernel/k_thread.h
+++ b/src/core/hle/kernel/k_thread.h
@@ -19,7 +19,7 @@
 #include "core/hle/kernel/k_light_lock.h"
 #include "core/hle/kernel/k_spin_lock.h"
 #include "core/hle/kernel/k_synchronization_object.h"
-#include "core/hle/kernel/object.h"
+#include "core/hle/kernel/slab_helpers.h"
 #include "core/hle/kernel/svc_common.h"
 #include "core/hle/kernel/svc_types.h"
 #include "core/hle/result.h"
@@ -37,7 +37,7 @@ namespace Kernel {
 
 class GlobalSchedulerContext;
 class KernelCore;
-class Process;
+class KProcess;
 class KScheduler;
 class KThreadQueue;
 
@@ -99,9 +99,13 @@ enum class ThreadWaitReasonForDebugging : u32 {
 [[nodiscard]] KThread& GetCurrentThread(KernelCore& kernel);
 [[nodiscard]] s32 GetCurrentCoreId(KernelCore& kernel);
 
-class KThread final : public KSynchronizationObject, public boost::intrusive::list_base_hook<> {
+class KThread final : public KAutoObjectWithSlabHeapAndContainer<KThread, KSynchronizationObject>,
+                      public boost::intrusive::list_base_hook<> {
+    KERNEL_AUTOOBJECT_TRAITS(KThread, KSynchronizationObject);
+
+private:
     friend class KScheduler;
-    friend class Process;
+    friend class KProcess;
 
 public:
     static constexpr s32 DefaultThreadPriority = 44;
@@ -115,74 +119,10 @@ public:
     using ThreadContext64 = Core::ARM_Interface::ThreadContext64;
     using WaiterList = boost::intrusive::list<KThread>;
 
-    /**
-     * Creates and returns a new thread.
-     * @param system The instance of the whole system
-     * @param name The friendly name desired for the thread
-     * @param entry_point The address at which the thread should start execution
-     * @param priority The thread's priority
-     * @param arg User data to pass to the thread
-     * @param processor_id The ID(s) of the processors on which the thread is desired to be run
-     * @param stack_top The address of the thread's stack top
-     * @param owner_process The parent process for the thread, if null, it's a kernel thread
-     * @return A shared pointer to the newly created thread
-     */
-    [[nodiscard]] static ResultVal<std::shared_ptr<KThread>> CreateThread(
-        Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point,
-        u32 priority, u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process);
-
-    /**
-     * Creates and returns a new thread, with a specified entry point.
-     * @param system The instance of the whole system
-     * @param name The friendly name desired for the thread
-     * @param entry_point The address at which the thread should start execution
-     * @param priority The thread's priority
-     * @param arg User data to pass to the thread
-     * @param processor_id The ID(s) of the processors on which the thread is desired to be run
-     * @param stack_top The address of the thread's stack top
-     * @param owner_process The parent process for the thread, if null, it's a kernel thread
-     * @param thread_start_func The function where the host context will start.
-     * @param thread_start_parameter The parameter which will passed to host context on init
-     * @return A shared pointer to the newly created thread
-     */
-    [[nodiscard]] static ResultVal<std::shared_ptr<KThread>> CreateThread(
-        Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point,
-        u32 priority, u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process,
-        std::function<void(void*)>&& thread_start_func, void* thread_start_parameter);
-
-    /**
-     * Creates and returns a new thread for the emulated "user" process.
-     * @param system The instance of the whole system
-     * @param name The friendly name desired for the thread
-     * @param entry_point The address at which the thread should start execution
-     * @param priority The thread's priority
-     * @param arg User data to pass to the thread
-     * @param processor_id The ID(s) of the processors on which the thread is desired to be run
-     * @param stack_top The address of the thread's stack top
-     * @param owner_process The parent process for the thread, if null, it's a kernel thread
-     * @return A shared pointer to the newly created thread
-     */
-    [[nodiscard]] static ResultVal<std::shared_ptr<KThread>> CreateUserThread(
-        Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point,
-        u32 priority, u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process);
-
-    [[nodiscard]] std::string GetName() const override {
-        return name;
-    }
-
     void SetName(std::string new_name) {
         name = std::move(new_name);
     }
 
-    [[nodiscard]] std::string GetTypeName() const override {
-        return "Thread";
-    }
-
-    static constexpr HandleType HANDLE_TYPE = HandleType::Thread;
-    [[nodiscard]] HandleType GetHandleType() const override {
-        return HANDLE_TYPE;
-    }
-
     /**
      * Gets the thread's current priority
      * @return The current thread's priority
@@ -257,10 +197,6 @@ public:
 
     void Suspend();
 
-    void Finalize() override;
-
-    bool IsSignaled() const override;
-
     void SetSyncedObject(KSynchronizationObject* obj, ResultCode wait_res) {
         synced_object = obj;
         wait_result = wait_res;
@@ -354,11 +290,11 @@ public:
         current_core_id = core;
     }
 
-    [[nodiscard]] Process* GetOwnerProcess() {
+    [[nodiscard]] KProcess* GetOwnerProcess() {
         return parent;
     }
 
-    [[nodiscard]] const Process* GetOwnerProcess() const {
+    [[nodiscard]] const KProcess* GetOwnerProcess() const {
         return parent;
     }
 
@@ -422,6 +358,40 @@ public:
         return termination_requested || GetRawState() == ThreadState::Terminated;
     }
 
+    [[nodiscard]] virtual u64 GetId() const override final {
+        return this->GetThreadID();
+    }
+
+    [[nodiscard]] virtual bool IsInitialized() const override {
+        return initialized;
+    }
+
+    [[nodiscard]] virtual uintptr_t GetPostDestroyArgument() const override {
+        return reinterpret_cast<uintptr_t>(parent) | (resource_limit_release_hint ? 1 : 0);
+    }
+
+    virtual void Finalize() override;
+
+    [[nodiscard]] virtual bool IsSignaled() const override;
+
+    static void PostDestroy(uintptr_t arg);
+
+    [[nodiscard]] static ResultCode InitializeDummyThread(KThread* thread);
+
+    [[nodiscard]] static ResultCode InitializeIdleThread(Core::System& system, KThread* thread,
+                                                         s32 virt_core);
+
+    [[nodiscard]] static ResultCode InitializeHighPriorityThread(Core::System& system,
+                                                                 KThread* thread,
+                                                                 KThreadFunction func,
+                                                                 uintptr_t arg, s32 virt_core);
+
+    [[nodiscard]] static ResultCode InitializeUserThread(Core::System& system, KThread* thread,
+                                                         KThreadFunction func, uintptr_t arg,
+                                                         VAddr user_stack_top, s32 prio,
+                                                         s32 virt_core, KProcess* owner);
+
+public:
     struct StackParameters {
         u8 svc_permission[0x10];
         std::atomic<u8> dpc_flags;
@@ -671,11 +641,13 @@ private:
     void StartTermination();
 
     [[nodiscard]] ResultCode Initialize(KThreadFunction func, uintptr_t arg, VAddr user_stack_top,
-                                        s32 prio, s32 virt_core, Process* owner, ThreadType type);
+                                        s32 prio, s32 virt_core, KProcess* owner, ThreadType type);
 
     [[nodiscard]] static ResultCode InitializeThread(KThread* thread, KThreadFunction func,
                                                      uintptr_t arg, VAddr user_stack_top, s32 prio,
-                                                     s32 core, Process* owner, ThreadType type);
+                                                     s32 core, KProcess* owner, ThreadType type,
+                                                     std::function<void(void*)>&& init_func,
+                                                     void* init_func_parameter);
 
     static void RestorePriority(KernelCore& kernel, KThread* thread);
 
@@ -697,7 +669,7 @@ private:
     std::atomic<s64> cpu_time{};
     KSynchronizationObject* synced_object{};
     VAddr address_key{};
-    Process* parent{};
+    KProcess* parent{};
     VAddr kernel_stack_top{};
     u32* light_ipc_data{};
     VAddr tls_address{};
@@ -742,7 +714,6 @@ private:
     VAddr mutex_wait_address_for_debugging{};
     ThreadWaitReasonForDebugging wait_reason_for_debugging{};
     ThreadType thread_type_for_debugging{};
-    std::string name;
 
 public:
     using ConditionVariableThreadTreeType = ConditionVariableThreadTree;
diff --git a/src/core/hle/kernel/k_transfer_memory.cpp b/src/core/hle/kernel/k_transfer_memory.cpp
new file mode 100644
index 000000000..201617d32
--- /dev/null
+++ b/src/core/hle/kernel/k_transfer_memory.cpp
@@ -0,0 +1,45 @@
+// Copyright 2021 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/kernel/k_process.h"
+#include "core/hle/kernel/k_resource_limit.h"
+#include "core/hle/kernel/k_transfer_memory.h"
+#include "core/hle/kernel/kernel.h"
+
+namespace Kernel {
+
+KTransferMemory::KTransferMemory(KernelCore& kernel)
+    : KAutoObjectWithSlabHeapAndContainer{kernel} {}
+
+KTransferMemory::~KTransferMemory() = default;
+
+ResultCode KTransferMemory::Initialize(VAddr address_, std::size_t size_,
+                                       Svc::MemoryPermission owner_perm_) {
+    // Set members.
+    owner = kernel.CurrentProcess();
+
+    // TODO(bunnei): Lock for transfer memory
+
+    // Set remaining tracking members.
+    owner->Open();
+    owner_perm = owner_perm_;
+    address = address_;
+    size = size_;
+    is_initialized = true;
+
+    return RESULT_SUCCESS;
+}
+
+void KTransferMemory::Finalize() {
+    // Perform inherited finalization.
+    KAutoObjectWithSlabHeapAndContainer<KTransferMemory, KAutoObjectWithList>::Finalize();
+}
+
+void KTransferMemory::PostDestroy(uintptr_t arg) {
+    KProcess* owner = reinterpret_cast<KProcess*>(arg);
+    owner->GetResourceLimit()->Release(LimitableResource::TransferMemory, 1);
+    owner->Close();
+}
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/k_transfer_memory.h b/src/core/hle/kernel/k_transfer_memory.h
new file mode 100644
index 000000000..f56398b9c
--- /dev/null
+++ b/src/core/hle/kernel/k_transfer_memory.h
@@ -0,0 +1,66 @@
+// Copyright 2021 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+
+#include "core/hle/kernel/slab_helpers.h"
+#include "core/hle/kernel/svc_types.h"
+#include "core/hle/result.h"
+
+union ResultCode;
+
+namespace Core::Memory {
+class Memory;
+}
+
+namespace Kernel {
+
+class KernelCore;
+class KProcess;
+
+class KTransferMemory final
+    : public KAutoObjectWithSlabHeapAndContainer<KTransferMemory, KAutoObjectWithList> {
+    KERNEL_AUTOOBJECT_TRAITS(KTransferMemory, KAutoObject);
+
+public:
+    explicit KTransferMemory(KernelCore& kernel);
+    virtual ~KTransferMemory() override;
+
+    ResultCode Initialize(VAddr address_, std::size_t size_, Svc::MemoryPermission owner_perm_);
+
+    virtual void Finalize() override;
+
+    virtual bool IsInitialized() const override {
+        return is_initialized;
+    }
+
+    virtual uintptr_t GetPostDestroyArgument() const override {
+        return reinterpret_cast<uintptr_t>(owner);
+    }
+
+    static void PostDestroy(uintptr_t arg);
+
+    KProcess* GetOwner() const {
+        return owner;
+    }
+
+    VAddr GetSourceAddress() const {
+        return address;
+    }
+
+    size_t GetSize() const {
+        return is_initialized ? size * PageSize : 0;
+    }
+
+private:
+    KProcess* owner{};
+    VAddr address{};
+    Svc::MemoryPermission owner_perm{};
+    size_t size{};
+    bool is_initialized{};
+};
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/k_writable_event.cpp b/src/core/hle/kernel/k_writable_event.cpp
index 25c52edb2..a430e0661 100644
--- a/src/core/hle/kernel/k_writable_event.cpp
+++ b/src/core/hle/kernel/k_writable_event.cpp
@@ -8,20 +8,28 @@
 
 namespace Kernel {
 
-KWritableEvent::KWritableEvent(KernelCore& kernel, std::string&& name)
-    : Object{kernel, std::move(name)} {}
+KWritableEvent::KWritableEvent(KernelCore& kernel) : KAutoObjectWithSlabHeapAndContainer{kernel} {}
+
 KWritableEvent::~KWritableEvent() = default;
 
-void KWritableEvent::Initialize(KEvent* parent_) {
+void KWritableEvent::Initialize(KEvent* parent_, std::string&& name_) {
     parent = parent_;
+    name = std::move(name_);
+    parent->GetReadableEvent().Open();
 }
 
 ResultCode KWritableEvent::Signal() {
-    return parent->GetReadableEvent()->Signal();
+    return parent->GetReadableEvent().Signal();
 }
 
 ResultCode KWritableEvent::Clear() {
-    return parent->GetReadableEvent()->Clear();
+    return parent->GetReadableEvent().Clear();
+}
+
+void KWritableEvent::Destroy() {
+    // Close our references.
+    parent->GetReadableEvent().Close();
+    parent->Close();
 }
 
 } // namespace Kernel
diff --git a/src/core/hle/kernel/k_writable_event.h b/src/core/hle/kernel/k_writable_event.h
index 518f5448d..154d2382c 100644
--- a/src/core/hle/kernel/k_writable_event.h
+++ b/src/core/hle/kernel/k_writable_event.h
@@ -4,7 +4,8 @@
 
 #pragma once
 
-#include "core/hle/kernel/object.h"
+#include "core/hle/kernel/k_auto_object.h"
+#include "core/hle/kernel/slab_helpers.h"
 #include "core/hle/result.h"
 
 namespace Kernel {
@@ -12,24 +13,19 @@ namespace Kernel {
 class KernelCore;
 class KEvent;
 
-class KWritableEvent final : public Object {
+class KWritableEvent final
+    : public KAutoObjectWithSlabHeapAndContainer<KWritableEvent, KAutoObjectWithList> {
+    KERNEL_AUTOOBJECT_TRAITS(KWritableEvent, KAutoObject);
+
 public:
-    explicit KWritableEvent(KernelCore& kernel, std::string&& name);
+    explicit KWritableEvent(KernelCore& kernel);
     ~KWritableEvent() override;
 
-    std::string GetTypeName() const override {
-        return "KWritableEvent";
-    }
+    virtual void Destroy() override;
 
-    static constexpr HandleType HANDLE_TYPE = HandleType::WritableEvent;
-    HandleType GetHandleType() const override {
-        return HANDLE_TYPE;
-    }
-
-    void Initialize(KEvent* parent_);
-
-    void Finalize() override {}
+    static void PostDestroy([[maybe_unused]] uintptr_t arg) {}
 
+    void Initialize(KEvent* parent_, std::string&& name_);
     ResultCode Signal();
     ResultCode Clear();
 
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 5c4f45ab4..32bbf2d9b 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -26,10 +26,12 @@
 #include "core/cpu_manager.h"
 #include "core/device_memory.h"
 #include "core/hardware_properties.h"
-#include "core/hle/kernel/client_port.h"
-#include "core/hle/kernel/handle_table.h"
+#include "core/hle/kernel/init/init_slab_setup.h"
+#include "core/hle/kernel/k_client_port.h"
+#include "core/hle/kernel/k_handle_table.h"
 #include "core/hle/kernel/k_memory_layout.h"
 #include "core/hle/kernel/k_memory_manager.h"
+#include "core/hle/kernel/k_process.h"
 #include "core/hle/kernel/k_resource_limit.h"
 #include "core/hle/kernel/k_scheduler.h"
 #include "core/hle/kernel/k_shared_memory.h"
@@ -37,7 +39,6 @@
 #include "core/hle/kernel/k_thread.h"
 #include "core/hle/kernel/kernel.h"
 #include "core/hle/kernel/physical_core.h"
-#include "core/hle/kernel/process.h"
 #include "core/hle/kernel/service_thread.h"
 #include "core/hle/kernel/svc_results.h"
 #include "core/hle/kernel/time_manager.h"
@@ -51,7 +52,7 @@ namespace Kernel {
 
 struct KernelCore::Impl {
     explicit Impl(Core::System& system, KernelCore& kernel)
-        : time_manager{system}, global_handle_table{kernel}, system{system} {}
+        : time_manager{system}, object_list_container{kernel}, system{system} {}
 
     void SetMulticore(bool is_multicore) {
         this->is_multicore = is_multicore;
@@ -59,8 +60,7 @@ struct KernelCore::Impl {
 
     void Initialize(KernelCore& kernel) {
         global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel);
-
-        RegisterHostThread();
+        global_handle_table = std::make_unique<Kernel::KHandleTable>(kernel);
 
         service_thread_manager =
             std::make_unique<Common::ThreadWorker>(1, "yuzu:ServiceThreadManager");
@@ -69,14 +69,20 @@ struct KernelCore::Impl {
         InitializePhysicalCores();
 
         // Derive the initial memory layout from the emulated board
+        Init::InitializeSlabResourceCounts(kernel);
         KMemoryLayout memory_layout;
         DeriveInitialMemoryLayout(memory_layout);
-        InitializeMemoryLayout(memory_layout);
+        Init::InitializeSlabHeaps(system, memory_layout);
+
+        // Initialize kernel memory and resources.
         InitializeSystemResourceLimit(kernel, system.CoreTiming(), memory_layout);
-        InitializeSlabHeaps();
+        InitializeMemoryLayout(memory_layout);
+        InitializePageSlab();
         InitializeSchedulers();
         InitializeSuspendThreads();
         InitializePreemption(kernel);
+
+        RegisterHostThread();
     }
 
     void InitializeCores() {
@@ -93,34 +99,49 @@ struct KernelCore::Impl {
         service_threads.clear();
 
         next_object_id = 0;
-        next_kernel_process_id = Process::InitialKIPIDMin;
-        next_user_process_id = Process::ProcessIDMin;
+        next_kernel_process_id = KProcess::InitialKIPIDMin;
+        next_user_process_id = KProcess::ProcessIDMin;
         next_thread_id = 1;
 
-        for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
-            if (suspend_threads[i]) {
-                suspend_threads[i].reset();
+        for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
+            if (suspend_threads[core_id]) {
+                suspend_threads[core_id]->Close();
+                suspend_threads[core_id] = nullptr;
             }
+
+            schedulers[core_id].reset();
         }
 
         cores.clear();
 
-        current_process = nullptr;
+        if (current_process) {
+            current_process->Close();
+            current_process = nullptr;
+        }
 
-        global_handle_table.Clear();
+        global_handle_table.reset();
 
         preemption_event = nullptr;
 
+        for (auto& iter : named_ports) {
+            iter.second->Close();
+        }
         named_ports.clear();
 
         exclusive_monitor.reset();
 
-        hid_shared_mem = nullptr;
-        font_shared_mem = nullptr;
-        irs_shared_mem = nullptr;
-        time_shared_mem = nullptr;
-
-        system_resource_limit = nullptr;
+        // Cleanup persistent kernel objects
+        auto CleanupObject = [](KAutoObject* obj) {
+            if (obj) {
+                obj->Close();
+                obj = nullptr;
+            }
+        };
+        CleanupObject(hid_shared_mem);
+        CleanupObject(font_shared_mem);
+        CleanupObject(irs_shared_mem);
+        CleanupObject(time_shared_mem);
+        CleanupObject(system_resource_limit);
 
         // Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others
         next_host_thread_id = Core::Hardware::NUM_CPU_CORES;
@@ -145,7 +166,9 @@ struct KernelCore::Impl {
     void InitializeSystemResourceLimit(KernelCore& kernel,
                                        const Core::Timing::CoreTiming& core_timing,
                                        const KMemoryLayout& memory_layout) {
-        system_resource_limit = std::make_shared<KResourceLimit>(kernel, core_timing);
+        system_resource_limit = KResourceLimit::Create(system.Kernel());
+        system_resource_limit->Initialize(&core_timing);
+
         const auto [total_size, kernel_size] = memory_layout.GetTotalAndKernelMemorySizes();
 
         // If setting the default system values fails, then something seriously wrong has occurred.
@@ -189,19 +212,16 @@ struct KernelCore::Impl {
     }
 
     void InitializeSuspendThreads() {
-        for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
-            std::string name = "Suspend Thread Id:" + std::to_string(i);
-            std::function<void(void*)> init_func = Core::CpuManager::GetSuspendThreadStartFunc();
-            void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
-            auto thread_res = KThread::CreateThread(
-                system, ThreadType::HighPriority, std::move(name), 0, 0, 0, static_cast<u32>(i), 0,
-                nullptr, std::move(init_func), init_func_parameter);
-
-            suspend_threads[i] = std::move(thread_res).Unwrap();
+        for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
+            suspend_threads[core_id] = KThread::Create(system.Kernel());
+            ASSERT(KThread::InitializeHighPriorityThread(system, suspend_threads[core_id], {}, {},
+                                                         core_id)
+                       .IsSuccess());
+            suspend_threads[core_id]->SetName(fmt::format("SuspendThread:{}", core_id));
         }
     }
 
-    void MakeCurrentProcess(Process* process) {
+    void MakeCurrentProcess(KProcess* process) {
         current_process = process;
         if (process == nullptr) {
             return;
@@ -232,11 +252,15 @@ struct KernelCore::Impl {
 
     // Gets the dummy KThread for the caller, allocating a new one if this is the first time
     KThread* GetHostDummyThread() {
-        const thread_local auto thread =
-            KThread::CreateThread(
-                system, ThreadType::Main, fmt::format("DummyThread:{}", GetHostThreadId()), 0,
-                KThread::DefaultThreadPriority, 0, static_cast<u32>(3), 0, nullptr)
-                .Unwrap();
+        auto make_thread = [this]() {
+            std::unique_ptr<KThread> thread = std::make_unique<KThread>(system.Kernel());
+            KAutoObject::Create(thread.get());
+            ASSERT(KThread::InitializeDummyThread(thread.get()).IsSuccess());
+            thread->SetName(fmt::format("DummyThread:{}", GetHostThreadId()));
+            return std::move(thread);
+        };
+
+        thread_local auto thread = make_thread();
         return thread.get();
     }
 
@@ -371,7 +395,8 @@ struct KernelCore::Impl {
         const size_t resource_region_size = memory_layout.GetResourceRegionSizeForInit();
 
         // Determine the size of the slab region.
-        const size_t slab_region_size = Common::AlignUp(KernelSlabHeapSize, PageSize);
+        const size_t slab_region_size =
+            Common::AlignUp(Init::CalculateTotalSlabHeapSize(system.Kernel()), PageSize);
         ASSERT(slab_region_size <= resource_region_size);
 
         // Setup the slab region.
@@ -569,25 +594,30 @@ struct KernelCore::Impl {
         const PAddr irs_phys_addr{system_pool.GetAddress() + hid_size + font_size};
         const PAddr time_phys_addr{system_pool.GetAddress() + hid_size + font_size + irs_size};
 
-        hid_shared_mem = Kernel::KSharedMemory::Create(
-            system.Kernel(), system.DeviceMemory(), nullptr, {hid_phys_addr, hid_size / PageSize},
-            KMemoryPermission::None, KMemoryPermission::Read, hid_phys_addr, hid_size,
-            "HID:SharedMemory");
-        font_shared_mem = Kernel::KSharedMemory::Create(
-            system.Kernel(), system.DeviceMemory(), nullptr, {font_phys_addr, font_size / PageSize},
-            KMemoryPermission::None, KMemoryPermission::Read, font_phys_addr, font_size,
-            "Font:SharedMemory");
-        irs_shared_mem = Kernel::KSharedMemory::Create(
-            system.Kernel(), system.DeviceMemory(), nullptr, {irs_phys_addr, irs_size / PageSize},
-            KMemoryPermission::None, KMemoryPermission::Read, irs_phys_addr, irs_size,
-            "IRS:SharedMemory");
-        time_shared_mem = Kernel::KSharedMemory::Create(
-            system.Kernel(), system.DeviceMemory(), nullptr, {time_phys_addr, time_size / PageSize},
-            KMemoryPermission::None, KMemoryPermission::Read, time_phys_addr, time_size,
-            "Time:SharedMemory");
+        hid_shared_mem = KSharedMemory::Create(system.Kernel());
+        font_shared_mem = KSharedMemory::Create(system.Kernel());
+        irs_shared_mem = KSharedMemory::Create(system.Kernel());
+        time_shared_mem = KSharedMemory::Create(system.Kernel());
+
+        hid_shared_mem->Initialize(system.Kernel(), system.DeviceMemory(), nullptr,
+                                   {hid_phys_addr, hid_size / PageSize},
+                                   Svc::MemoryPermission::None, Svc::MemoryPermission::Read,
+                                   hid_phys_addr, hid_size, "HID:SharedMemory");
+        font_shared_mem->Initialize(system.Kernel(), system.DeviceMemory(), nullptr,
+                                    {font_phys_addr, font_size / PageSize},
+                                    Svc::MemoryPermission::None, Svc::MemoryPermission::Read,
+                                    font_phys_addr, font_size, "Font:SharedMemory");
+        irs_shared_mem->Initialize(system.Kernel(), system.DeviceMemory(), nullptr,
+                                   {irs_phys_addr, irs_size / PageSize},
+                                   Svc::MemoryPermission::None, Svc::MemoryPermission::Read,
+                                   irs_phys_addr, irs_size, "IRS:SharedMemory");
+        time_shared_mem->Initialize(system.Kernel(), system.DeviceMemory(), nullptr,
+                                    {time_phys_addr, time_size / PageSize},
+                                    Svc::MemoryPermission::None, Svc::MemoryPermission::Read,
+                                    time_phys_addr, time_size, "Time:SharedMemory");
     }
 
-    void InitializeSlabHeaps() {
+    void InitializePageSlab() {
         // Allocate slab heaps
         user_slab_heap_pages = std::make_unique<KSlabHeap<Page>>();
 
@@ -596,30 +626,33 @@ struct KernelCore::Impl {
         // Reserve slab heaps
         ASSERT(
             system_resource_limit->Reserve(LimitableResource::PhysicalMemory, user_slab_heap_size));
-        // Initialize slab heaps
+        // Initialize slab heap
         user_slab_heap_pages->Initialize(
             system.DeviceMemory().GetPointer(Core::DramMemoryMap::SlabHeapBase),
             user_slab_heap_size);
     }
 
     std::atomic<u32> next_object_id{0};
-    std::atomic<u64> next_kernel_process_id{Process::InitialKIPIDMin};
-    std::atomic<u64> next_user_process_id{Process::ProcessIDMin};
+    std::atomic<u64> next_kernel_process_id{KProcess::InitialKIPIDMin};
+    std::atomic<u64> next_user_process_id{KProcess::ProcessIDMin};
     std::atomic<u64> next_thread_id{1};
 
     // Lists all processes that exist in the current session.
-    std::vector<std::shared_ptr<Process>> process_list;
-    Process* current_process = nullptr;
+    std::vector<KProcess*> process_list;
+    KProcess* current_process{};
     std::unique_ptr<Kernel::GlobalSchedulerContext> global_scheduler_context;
     Kernel::TimeManager time_manager;
 
-    std::shared_ptr<KResourceLimit> system_resource_limit;
+    Init::KSlabResourceCounts slab_resource_counts{};
+    KResourceLimit* system_resource_limit{};
 
     std::shared_ptr<Core::Timing::EventType> preemption_event;
 
     // This is the kernel's handle table or supervisor handle table which
     // stores all the objects in place.
-    HandleTable global_handle_table;
+    std::unique_ptr<KHandleTable> global_handle_table;
+
+    KAutoObjectWithListContainer object_list_container;
 
     /// Map of named ports managed by the kernel, which can be retrieved using
     /// the ConnectToPort SVC.
@@ -636,10 +669,10 @@ struct KernelCore::Impl {
     std::unique_ptr<KSlabHeap<Page>> user_slab_heap_pages;
 
     // Shared memory for services
-    std::shared_ptr<Kernel::KSharedMemory> hid_shared_mem;
-    std::shared_ptr<Kernel::KSharedMemory> font_shared_mem;
-    std::shared_ptr<Kernel::KSharedMemory> irs_shared_mem;
-    std::shared_ptr<Kernel::KSharedMemory> time_shared_mem;
+    Kernel::KSharedMemory* hid_shared_mem{};
+    Kernel::KSharedMemory* font_shared_mem{};
+    Kernel::KSharedMemory* irs_shared_mem{};
+    Kernel::KSharedMemory* time_shared_mem{};
 
     // Threads used for services
     std::unordered_set<std::shared_ptr<Kernel::ServiceThread>> service_threads;
@@ -648,7 +681,7 @@ struct KernelCore::Impl {
     // the release of itself
     std::unique_ptr<Common::ThreadWorker> service_thread_manager;
 
-    std::array<std::shared_ptr<KThread>, Core::Hardware::NUM_CPU_CORES> suspend_threads{};
+    std::array<KThread*, Core::Hardware::NUM_CPU_CORES> suspend_threads;
     std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES> interrupts{};
     std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{};
 
@@ -663,15 +696,14 @@ struct KernelCore::Impl {
 };
 
 KernelCore::KernelCore(Core::System& system) : impl{std::make_unique<Impl>(system, *this)} {}
-KernelCore::~KernelCore() {
-    Shutdown();
-}
+KernelCore::~KernelCore() = default;
 
 void KernelCore::SetMulticore(bool is_multicore) {
     impl->SetMulticore(is_multicore);
 }
 
 void KernelCore::Initialize() {
+    slab_heap_container = std::make_unique<SlabHeapContainer>();
     impl->Initialize(*this);
 }
 
@@ -683,31 +715,35 @@ void KernelCore::Shutdown() {
     impl->Shutdown();
 }
 
-std::shared_ptr<KResourceLimit> KernelCore::GetSystemResourceLimit() const {
+const KResourceLimit* KernelCore::GetSystemResourceLimit() const {
     return impl->system_resource_limit;
 }
 
-std::shared_ptr<KThread> KernelCore::RetrieveThreadFromGlobalHandleTable(Handle handle) const {
-    return impl->global_handle_table.Get<KThread>(handle);
+KResourceLimit* KernelCore::GetSystemResourceLimit() {
+    return impl->system_resource_limit;
 }
 
-void KernelCore::AppendNewProcess(std::shared_ptr<Process> process) {
-    impl->process_list.push_back(std::move(process));
+KScopedAutoObject<KThread> KernelCore::RetrieveThreadFromGlobalHandleTable(Handle handle) const {
+    return impl->global_handle_table->GetObject<KThread>(handle);
 }
 
-void KernelCore::MakeCurrentProcess(Process* process) {
+void KernelCore::AppendNewProcess(KProcess* process) {
+    impl->process_list.push_back(process);
+}
+
+void KernelCore::MakeCurrentProcess(KProcess* process) {
     impl->MakeCurrentProcess(process);
 }
 
-Process* KernelCore::CurrentProcess() {
+KProcess* KernelCore::CurrentProcess() {
     return impl->current_process;
 }
 
-const Process* KernelCore::CurrentProcess() const {
+const KProcess* KernelCore::CurrentProcess() const {
     return impl->current_process;
 }
 
-const std::vector<std::shared_ptr<Process>>& KernelCore::GetProcessList() const {
+const std::vector<KProcess*>& KernelCore::GetProcessList() const {
     return impl->process_list;
 }
 
@@ -781,6 +817,14 @@ const Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() const {
     return *impl->exclusive_monitor;
 }
 
+KAutoObjectWithListContainer& KernelCore::ObjectListContainer() {
+    return impl->object_list_container;
+}
+
+const KAutoObjectWithListContainer& KernelCore::ObjectListContainer() const {
+    return impl->object_list_container;
+}
+
 void KernelCore::InvalidateAllInstructionCaches() {
     for (auto& physical_core : impl->cores) {
         physical_core.ArmInterface().ClearInstructionCache();
@@ -800,8 +844,9 @@ void KernelCore::PrepareReschedule(std::size_t id) {
     // TODO: Reimplement, this
 }
 
-void KernelCore::AddNamedPort(std::string name, std::shared_ptr<ClientPort> port) {
-    impl->named_ports.emplace(std::move(name), std::move(port));
+void KernelCore::AddNamedPort(std::string name, KClientPort* port) {
+    port->Open();
+    impl->named_ports.emplace(std::move(name), port);
 }
 
 KernelCore::NamedPortTable::iterator KernelCore::FindNamedPort(const std::string& name) {
@@ -833,12 +878,12 @@ u64 KernelCore::CreateNewUserProcessID() {
     return impl->next_user_process_id++;
 }
 
-Kernel::HandleTable& KernelCore::GlobalHandleTable() {
-    return impl->global_handle_table;
+KHandleTable& KernelCore::GlobalHandleTable() {
+    return *impl->global_handle_table;
 }
 
-const Kernel::HandleTable& KernelCore::GlobalHandleTable() const {
-    return impl->global_handle_table;
+const KHandleTable& KernelCore::GlobalHandleTable() const {
+    return *impl->global_handle_table;
 }
 
 void KernelCore::RegisterCoreThread(std::size_t core_id) {
@@ -910,9 +955,9 @@ void KernelCore::Suspend(bool in_suspention) {
     {
         KScopedSchedulerLock lock(*this);
         const auto state = should_suspend ? ThreadState::Runnable : ThreadState::Waiting;
-        for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
-            impl->suspend_threads[i]->SetState(state);
-            impl->suspend_threads[i]->SetWaitReasonForDebugging(
+        for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
+            impl->suspend_threads[core_id]->SetState(state);
+            impl->suspend_threads[core_id]->SetWaitReasonForDebugging(
                 ThreadWaitReasonForDebugging::Suspended);
         }
     }
@@ -952,6 +997,14 @@ void KernelCore::ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> servi
     });
 }
 
+Init::KSlabResourceCounts& KernelCore::SlabResourceCounts() {
+    return impl->slab_resource_counts;
+}
+
+const Init::KSlabResourceCounts& KernelCore::SlabResourceCounts() const {
+    return impl->slab_resource_counts;
+}
+
 bool KernelCore::IsPhantomModeForSingleCore() const {
     return impl->IsPhantomModeForSingleCore();
 }
@@ -960,4 +1013,12 @@ void KernelCore::SetIsPhantomModeForSingleCore(bool value) {
     impl->SetIsPhantomModeForSingleCore(value);
 }
 
+Core::System& KernelCore::System() {
+    return impl->system;
+}
+
+const Core::System& KernelCore::System() const {
+    return impl->system;
+}
+
 } // namespace Kernel
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index a500e63bc..51aaccbc7 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -11,8 +11,10 @@
 #include <vector>
 #include "core/arm/cpu_interrupt_handler.h"
 #include "core/hardware_properties.h"
+#include "core/hle/kernel/k_auto_object.h"
+#include "core/hle/kernel/k_slab_heap.h"
 #include "core/hle/kernel/memory_types.h"
-#include "core/hle/kernel/object.h"
+#include "core/hle/kernel/svc_common.h"
 
 namespace Core {
 class CPUInterruptHandler;
@@ -27,20 +29,32 @@ struct EventType;
 
 namespace Kernel {
 
-class ClientPort;
+class KClientPort;
 class GlobalSchedulerContext;
-class HandleTable;
+class KAutoObjectWithListContainer;
+class KClientSession;
+class KEvent;
+class KHandleTable;
+class KLinkedListNode;
 class KMemoryManager;
+class KPort;
+class KProcess;
 class KResourceLimit;
 class KScheduler;
+class KSession;
 class KSharedMemory;
 class KThread;
+class KTransferMemory;
+class KWritableEvent;
 class PhysicalCore;
-class Process;
 class ServiceThread;
 class Synchronization;
 class TimeManager;
 
+namespace Init {
+struct KSlabResourceCounts;
+}
+
 template <typename T>
 class KSlabHeap;
 
@@ -51,7 +65,7 @@ constexpr EmuThreadHandle EmuThreadHandleReserved{1ULL << 63};
 /// Represents a single instance of the kernel.
 class KernelCore {
 private:
-    using NamedPortTable = std::unordered_map<std::string, std::shared_ptr<ClientPort>>;
+    using NamedPortTable = std::unordered_map<std::string, KClientPort*>;
 
 public:
     /// Constructs an instance of the kernel using the given System
@@ -83,25 +97,28 @@ public:
     void Shutdown();
 
     /// Retrieves a shared pointer to the system resource limit instance.
-    std::shared_ptr<KResourceLimit> GetSystemResourceLimit() const;
+    const KResourceLimit* GetSystemResourceLimit() const;
+
+    /// Retrieves a shared pointer to the system resource limit instance.
+    KResourceLimit* GetSystemResourceLimit();
 
     /// Retrieves a shared pointer to a Thread instance within the thread wakeup handle table.
-    std::shared_ptr<KThread> RetrieveThreadFromGlobalHandleTable(Handle handle) const;
+    KScopedAutoObject<KThread> RetrieveThreadFromGlobalHandleTable(Handle handle) const;
 
     /// Adds the given shared pointer to an internal list of active processes.
-    void AppendNewProcess(std::shared_ptr<Process> process);
+    void AppendNewProcess(KProcess* process);
 
     /// Makes the given process the new current process.
-    void MakeCurrentProcess(Process* process);
+    void MakeCurrentProcess(KProcess* process);
 
     /// Retrieves a pointer to the current process.
-    Process* CurrentProcess();
+    KProcess* CurrentProcess();
 
     /// Retrieves a const pointer to the current process.
-    const Process* CurrentProcess() const;
+    const KProcess* CurrentProcess() const;
 
     /// Retrieves the list of processes.
-    const std::vector<std::shared_ptr<Process>>& GetProcessList() const;
+    const std::vector<KProcess*>& GetProcessList() const;
 
     /// Gets the sole instance of the global scheduler
     Kernel::GlobalSchedulerContext& GlobalSchedulerContext();
@@ -143,6 +160,10 @@ public:
 
     const Core::ExclusiveMonitor& GetExclusiveMonitor() const;
 
+    KAutoObjectWithListContainer& ObjectListContainer();
+
+    const KAutoObjectWithListContainer& ObjectListContainer() const;
+
     std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& Interrupts();
 
     const std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& Interrupts() const;
@@ -152,7 +173,7 @@ public:
     void InvalidateCpuInstructionCacheRange(VAddr addr, std::size_t size);
 
     /// Adds a port to the named port table
-    void AddNamedPort(std::string name, std::shared_ptr<ClientPort> port);
+    void AddNamedPort(std::string name, KClientPort* port);
 
     /// Finds a port within the named port table with the given name.
     NamedPortTable::iterator FindNamedPort(const std::string& name);
@@ -225,9 +246,10 @@ public:
 
     /**
      * Creates an HLE service thread, which are used to execute service routines asynchronously.
-     * While these are allocated per ServerSession, these need to be owned and managed outside of
-     * ServerSession to avoid a circular dependency.
-     * @param name String name for the ServerSession creating this thread, used for debug purposes.
+     * While these are allocated per ServerSession, these need to be owned and managed outside
+     * of ServerSession to avoid a circular dependency.
+     * @param name String name for the ServerSession creating this thread, used for debug
+     * purposes.
      * @returns The a weak pointer newly created service thread.
      */
     std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(const std::string& name);
@@ -243,9 +265,45 @@ public:
     bool IsPhantomModeForSingleCore() const;
     void SetIsPhantomModeForSingleCore(bool value);
 
+    Core::System& System();
+    const Core::System& System() const;
+
+    /// Gets the slab heap for the specified kernel object type.
+    template <typename T>
+    KSlabHeap<T>& SlabHeap() {
+        if constexpr (std::is_same_v<T, KClientSession>) {
+            return slab_heap_container->client_session;
+        } else if constexpr (std::is_same_v<T, KEvent>) {
+            return slab_heap_container->event;
+        } else if constexpr (std::is_same_v<T, KLinkedListNode>) {
+            return slab_heap_container->linked_list_node;
+        } else if constexpr (std::is_same_v<T, KPort>) {
+            return slab_heap_container->port;
+        } else if constexpr (std::is_same_v<T, KProcess>) {
+            return slab_heap_container->process;
+        } else if constexpr (std::is_same_v<T, KResourceLimit>) {
+            return slab_heap_container->resource_limit;
+        } else if constexpr (std::is_same_v<T, KSession>) {
+            return slab_heap_container->session;
+        } else if constexpr (std::is_same_v<T, KSharedMemory>) {
+            return slab_heap_container->shared_memory;
+        } else if constexpr (std::is_same_v<T, KThread>) {
+            return slab_heap_container->thread;
+        } else if constexpr (std::is_same_v<T, KTransferMemory>) {
+            return slab_heap_container->transfer_memory;
+        } else if constexpr (std::is_same_v<T, KWritableEvent>) {
+            return slab_heap_container->writeable_event;
+        }
+    }
+
+    /// Gets the current slab resource counts.
+    Init::KSlabResourceCounts& SlabResourceCounts();
+
+    /// Gets the current slab resource counts.
+    const Init::KSlabResourceCounts& SlabResourceCounts() const;
+
 private:
-    friend class Object;
-    friend class Process;
+    friend class KProcess;
     friend class KThread;
 
     /// Creates a new object ID, incrementing the internal object ID counter.
@@ -261,14 +319,33 @@ private:
     u64 CreateNewThreadID();
 
     /// Provides a reference to the global handle table.
-    Kernel::HandleTable& GlobalHandleTable();
+    KHandleTable& GlobalHandleTable();
 
     /// Provides a const reference to the global handle table.
-    const Kernel::HandleTable& GlobalHandleTable() const;
+    const KHandleTable& GlobalHandleTable() const;
 
     struct Impl;
     std::unique_ptr<Impl> impl;
+
     bool exception_exited{};
+
+private:
+    /// Helper to encapsulate all slab heaps in a single heap allocated container
+    struct SlabHeapContainer {
+        KSlabHeap<KClientSession> client_session;
+        KSlabHeap<KEvent> event;
+        KSlabHeap<KLinkedListNode> linked_list_node;
+        KSlabHeap<KPort> port;
+        KSlabHeap<KProcess> process;
+        KSlabHeap<KResourceLimit> resource_limit;
+        KSlabHeap<KSession> session;
+        KSlabHeap<KSharedMemory> shared_memory;
+        KSlabHeap<KThread> thread;
+        KSlabHeap<KTransferMemory> transfer_memory;
+        KSlabHeap<KWritableEvent> writeable_event;
+    };
+
+    std::unique_ptr<SlabHeapContainer> slab_heap_container;
 };
 
 } // namespace Kernel
diff --git a/src/core/hle/kernel/object.cpp b/src/core/hle/kernel/object.cpp
deleted file mode 100644
index d7f40c403..000000000
--- a/src/core/hle/kernel/object.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2018 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include "common/assert.h"
-#include "core/hle/kernel/kernel.h"
-#include "core/hle/kernel/object.h"
-
-namespace Kernel {
-
-Object::Object(KernelCore& kernel_)
-    : kernel{kernel_}, object_id{kernel_.CreateNewObjectID()}, name{"[UNKNOWN KERNEL OBJECT]"} {}
-Object::Object(KernelCore& kernel_, std::string&& name_)
-    : kernel{kernel_}, object_id{kernel_.CreateNewObjectID()}, name{std::move(name_)} {}
-Object::~Object() = default;
-
-bool Object::IsWaitable() const {
-    switch (GetHandleType()) {
-    case HandleType::ReadableEvent:
-    case HandleType::Thread:
-    case HandleType::Process:
-    case HandleType::ServerPort:
-    case HandleType::ServerSession:
-        return true;
-
-    case HandleType::Unknown:
-    case HandleType::Event:
-    case HandleType::WritableEvent:
-    case HandleType::SharedMemory:
-    case HandleType::TransferMemory:
-    case HandleType::ResourceLimit:
-    case HandleType::ClientPort:
-    case HandleType::ClientSession:
-    case HandleType::Session:
-        return false;
-    }
-
-    UNREACHABLE();
-    return false;
-}
-
-} // namespace Kernel
diff --git a/src/core/hle/kernel/object.h b/src/core/hle/kernel/object.h
deleted file mode 100644
index 501e58b33..000000000
--- a/src/core/hle/kernel/object.h
+++ /dev/null
@@ -1,96 +0,0 @@
-// Copyright 2018 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <atomic>
-#include <memory>
-#include <string>
-
-#include "common/common_types.h"
-
-namespace Kernel {
-
-class KernelCore;
-
-using Handle = u32;
-
-enum class HandleType : u32 {
-    Unknown,
-    Event,
-    WritableEvent,
-    ReadableEvent,
-    SharedMemory,
-    TransferMemory,
-    Thread,
-    Process,
-    ResourceLimit,
-    ClientPort,
-    ServerPort,
-    ClientSession,
-    ServerSession,
-    Session,
-};
-
-class Object : NonCopyable, public std::enable_shared_from_this<Object> {
-public:
-    explicit Object(KernelCore& kernel_);
-    explicit Object(KernelCore& kernel_, std::string&& name_);
-    virtual ~Object();
-
-    /// Returns a unique identifier for the object. For debugging purposes only.
-    u32 GetObjectId() const {
-        return object_id.load(std::memory_order_relaxed);
-    }
-
-    virtual std::string GetTypeName() const {
-        return "[BAD KERNEL OBJECT TYPE]";
-    }
-    virtual std::string GetName() const {
-        return name;
-    }
-    virtual HandleType GetHandleType() const = 0;
-
-    void Close() {
-        // TODO(bunnei): This is a placeholder to decrement the reference count, which we will use
-        // when we implement KAutoObject instead of using shared_ptr.
-    }
-
-    /**
-     * Check if a thread can wait on the object
-     * @return True if a thread can wait on the object, otherwise false
-     */
-    bool IsWaitable() const;
-
-    virtual void Finalize() = 0;
-
-protected:
-    /// The kernel instance this object was created under.
-    KernelCore& kernel;
-
-private:
-    std::atomic<u32> object_id{0};
-    std::string name;
-};
-
-template <typename T>
-std::shared_ptr<T> SharedFrom(T* raw) {
-    if (raw == nullptr)
-        return nullptr;
-    return std::static_pointer_cast<T>(raw->shared_from_this());
-}
-
-/**
- * Attempts to downcast the given Object pointer to a pointer to T.
- * @return Derived pointer to the object, or `nullptr` if `object` isn't of type T.
- */
-template <typename T>
-inline std::shared_ptr<T> DynamicObjectCast(std::shared_ptr<Object> object) {
-    if (object != nullptr && object->GetHandleType() == T::HANDLE_TYPE) {
-        return std::static_pointer_cast<T>(object);
-    }
-    return nullptr;
-}
-
-} // namespace Kernel
diff --git a/src/core/hle/kernel/process_capability.cpp b/src/core/hle/kernel/process_capability.cpp
index 1006ee50c..fcb8b1ea5 100644
--- a/src/core/hle/kernel/process_capability.cpp
+++ b/src/core/hle/kernel/process_capability.cpp
@@ -6,7 +6,7 @@
 
 #include "common/bit_util.h"
 #include "common/logging/log.h"
-#include "core/hle/kernel/handle_table.h"
+#include "core/hle/kernel/k_handle_table.h"
 #include "core/hle/kernel/k_page_table.h"
 #include "core/hle/kernel/process_capability.h"
 #include "core/hle/kernel/svc_results.h"
@@ -99,7 +99,7 @@ void ProcessCapabilities::InitializeForMetadatalessProcess() {
     interrupt_capabilities.set();
 
     // Allow using the maximum possible amount of handles
-    handle_table_size = static_cast<s32>(HandleTable::MAX_COUNT);
+    handle_table_size = static_cast<s32>(KHandleTable::MaxTableSize);
 
     // Allow all debugging capabilities.
     is_debuggable = true;
@@ -159,7 +159,7 @@ ResultCode ProcessCapabilities::ParseSingleFlagCapability(u32& set_flags, u32& s
     const auto type = GetCapabilityType(flag);
 
     if (type == CapabilityType::Unset) {
-        return ResultInvalidCapabilityDescriptor;
+        return ResultInvalidArgument;
     }
 
     // Bail early on ignorable entries, as one would expect,
@@ -202,7 +202,7 @@ ResultCode ProcessCapabilities::ParseSingleFlagCapability(u32& set_flags, u32& s
     }
 
     LOG_ERROR(Kernel, "Invalid capability type! type={}", type);
-    return ResultInvalidCapabilityDescriptor;
+    return ResultInvalidArgument;
 }
 
 void ProcessCapabilities::Clear() {
@@ -225,7 +225,7 @@ ResultCode ProcessCapabilities::HandlePriorityCoreNumFlags(u32 flags) {
     if (priority_mask != 0 || core_mask != 0) {
         LOG_ERROR(Kernel, "Core or priority mask are not zero! priority_mask={}, core_mask={}",
                   priority_mask, core_mask);
-        return ResultInvalidCapabilityDescriptor;
+        return ResultInvalidArgument;
     }
 
     const u32 core_num_min = (flags >> 16) & 0xFF;
@@ -329,7 +329,7 @@ ResultCode ProcessCapabilities::HandleProgramTypeFlags(u32 flags) {
     const u32 reserved = flags >> 17;
     if (reserved != 0) {
         LOG_ERROR(Kernel, "Reserved value is non-zero! reserved={}", reserved);
-        return ResultReservedValue;
+        return ResultReservedUsed;
     }
 
     program_type = static_cast<ProgramType>((flags >> 14) & 0b111);
@@ -349,7 +349,7 @@ ResultCode ProcessCapabilities::HandleKernelVersionFlags(u32 flags) {
         LOG_ERROR(Kernel,
                   "Kernel version is non zero or flags are too small! major_version={}, flags={}",
                   major_version, flags);
-        return ResultInvalidCapabilityDescriptor;
+        return ResultInvalidArgument;
     }
 
     kernel_version = flags;
@@ -360,7 +360,7 @@ ResultCode ProcessCapabilities::HandleHandleTableFlags(u32 flags) {
     const u32 reserved = flags >> 26;
     if (reserved != 0) {
         LOG_ERROR(Kernel, "Reserved value is non-zero! reserved={}", reserved);
-        return ResultReservedValue;
+        return ResultReservedUsed;
     }
 
     handle_table_size = static_cast<s32>((flags >> 16) & 0x3FF);
@@ -371,7 +371,7 @@ ResultCode ProcessCapabilities::HandleDebugFlags(u32 flags) {
     const u32 reserved = flags >> 19;
     if (reserved != 0) {
         LOG_ERROR(Kernel, "Reserved value is non-zero! reserved={}", reserved);
-        return ResultReservedValue;
+        return ResultReservedUsed;
     }
 
     is_debuggable = (flags & 0x20000) != 0;
diff --git a/src/core/hle/kernel/server_port.cpp b/src/core/hle/kernel/server_port.cpp
deleted file mode 100644
index 5d17346ad..000000000
--- a/src/core/hle/kernel/server_port.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright 2016 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include <tuple>
-#include "common/assert.h"
-#include "core/hle/kernel/client_port.h"
-#include "core/hle/kernel/k_thread.h"
-#include "core/hle/kernel/object.h"
-#include "core/hle/kernel/server_port.h"
-#include "core/hle/kernel/server_session.h"
-#include "core/hle/kernel/svc_results.h"
-
-namespace Kernel {
-
-ServerPort::ServerPort(KernelCore& kernel) : KSynchronizationObject{kernel} {}
-ServerPort::~ServerPort() = default;
-
-ResultVal<std::shared_ptr<ServerSession>> ServerPort::Accept() {
-    if (pending_sessions.empty()) {
-        return ResultNotFound;
-    }
-
-    auto session = std::move(pending_sessions.back());
-    pending_sessions.pop_back();
-    return MakeResult(std::move(session));
-}
-
-void ServerPort::AppendPendingSession(std::shared_ptr<ServerSession> pending_session) {
-    pending_sessions.push_back(std::move(pending_session));
-    if (pending_sessions.size() == 1) {
-        NotifyAvailable();
-    }
-}
-
-bool ServerPort::IsSignaled() const {
-    return !pending_sessions.empty();
-}
-
-ServerPort::PortPair ServerPort::CreatePortPair(KernelCore& kernel, u32 max_sessions,
-                                                std::string name) {
-    std::shared_ptr<ServerPort> server_port = std::make_shared<ServerPort>(kernel);
-    std::shared_ptr<ClientPort> client_port = std::make_shared<ClientPort>(kernel);
-
-    server_port->name = name + "_Server";
-    client_port->name = name + "_Client";
-    client_port->server_port = server_port;
-    client_port->max_sessions = max_sessions;
-    client_port->active_sessions = 0;
-
-    return std::make_pair(std::move(server_port), std::move(client_port));
-}
-
-} // namespace Kernel
diff --git a/src/core/hle/kernel/server_port.h b/src/core/hle/kernel/server_port.h
deleted file mode 100644
index 29b4f2509..000000000
--- a/src/core/hle/kernel/server_port.h
+++ /dev/null
@@ -1,98 +0,0 @@
-// Copyright 2016 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <memory>
-#include <string>
-#include <utility>
-#include <vector>
-#include "common/common_types.h"
-#include "core/hle/kernel/k_synchronization_object.h"
-#include "core/hle/kernel/object.h"
-#include "core/hle/result.h"
-
-namespace Kernel {
-
-class ClientPort;
-class KernelCore;
-class ServerSession;
-class SessionRequestHandler;
-
-class ServerPort final : public KSynchronizationObject {
-public:
-    explicit ServerPort(KernelCore& kernel);
-    ~ServerPort() override;
-
-    using HLEHandler = std::shared_ptr<SessionRequestHandler>;
-    using PortPair = std::pair<std::shared_ptr<ServerPort>, std::shared_ptr<ClientPort>>;
-
-    /**
-     * Creates a pair of ServerPort and an associated ClientPort.
-     *
-     * @param kernel The kernel instance to create the port pair under.
-     * @param max_sessions Maximum number of sessions to the port
-     * @param name Optional name of the ports
-     * @return The created port tuple
-     */
-    static PortPair CreatePortPair(KernelCore& kernel, u32 max_sessions,
-                                   std::string name = "UnknownPort");
-
-    std::string GetTypeName() const override {
-        return "ServerPort";
-    }
-    std::string GetName() const override {
-        return name;
-    }
-
-    static constexpr HandleType HANDLE_TYPE = HandleType::ServerPort;
-    HandleType GetHandleType() const override {
-        return HANDLE_TYPE;
-    }
-
-    /**
-     * Accepts a pending incoming connection on this port. If there are no pending sessions, will
-     * return ERR_NO_PENDING_SESSIONS.
-     */
-    ResultVal<std::shared_ptr<ServerSession>> Accept();
-
-    /// Whether or not this server port has an HLE handler available.
-    bool HasHLEHandler() const {
-        return hle_handler != nullptr;
-    }
-
-    /// Gets the HLE handler for this port.
-    HLEHandler GetHLEHandler() const {
-        return hle_handler;
-    }
-
-    /**
-     * Sets the HLE handler template for the port. ServerSessions crated by connecting to this port
-     * will inherit a reference to this handler.
-     */
-    void SetHleHandler(HLEHandler hle_handler_) {
-        hle_handler = std::move(hle_handler_);
-    }
-
-    /// Appends a ServerSession to the collection of ServerSessions
-    /// waiting to be accepted by this port.
-    void AppendPendingSession(std::shared_ptr<ServerSession> pending_session);
-
-    bool IsSignaled() const override;
-
-    void Finalize() override {}
-
-private:
-    /// ServerSessions waiting to be accepted by the port
-    std::vector<std::shared_ptr<ServerSession>> pending_sessions;
-
-    /// This session's HLE request handler template (optional)
-    /// ServerSessions created from this port inherit a reference to this handler.
-    HLEHandler hle_handler;
-
-    /// Name of the port (optional)
-    std::string name;
-};
-
-} // namespace Kernel
diff --git a/src/core/hle/kernel/service_thread.cpp b/src/core/hle/kernel/service_thread.cpp
index ee46f3e21..04be8a502 100644
--- a/src/core/hle/kernel/service_thread.cpp
+++ b/src/core/hle/kernel/service_thread.cpp
@@ -13,8 +13,8 @@
 #include "common/scope_exit.h"
 #include "common/thread.h"
 #include "core/core.h"
+#include "core/hle/kernel/k_session.h"
 #include "core/hle/kernel/kernel.h"
-#include "core/hle/kernel/server_session.h"
 #include "core/hle/kernel/service_thread.h"
 #include "core/hle/lock.h"
 #include "video_core/renderer_base.h"
@@ -26,7 +26,7 @@ public:
     explicit Impl(KernelCore& kernel, std::size_t num_threads, const std::string& name);
     ~Impl();
 
-    void QueueSyncRequest(ServerSession& session, std::shared_ptr<HLERequestContext>&& context);
+    void QueueSyncRequest(KSession& session, std::shared_ptr<HLERequestContext>&& context);
 
 private:
     std::vector<std::thread> threads;
@@ -69,18 +69,27 @@ ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads, const std
         });
 }
 
-void ServiceThread::Impl::QueueSyncRequest(ServerSession& session,
+void ServiceThread::Impl::QueueSyncRequest(KSession& session,
                                            std::shared_ptr<HLERequestContext>&& context) {
     {
         std::unique_lock lock{queue_mutex};
 
-        // ServerSession owns the service thread, so we cannot caption a strong pointer here in the
-        // event that the ServerSession is terminated.
-        std::weak_ptr<ServerSession> weak_ptr{SharedFrom(&session)};
-        requests.emplace([weak_ptr, context{std::move(context)}]() {
-            if (auto strong_ptr = weak_ptr.lock()) {
-                strong_ptr->CompleteSyncRequest(*context);
+        // Open a reference to the session to ensure it is not closes while the service request
+        // completes asynchronously.
+        session.Open();
+
+        requests.emplace([session_ptr{&session}, context{std::move(context)}]() {
+            // Close the reference.
+            SCOPE_EXIT({ session_ptr->Close(); });
+
+            // If the session has been closed, we are done.
+            if (session_ptr->IsServerClosed()) {
+                return;
             }
+
+            // Complete the service request.
+            KScopedAutoObject server_session{&session_ptr->GetServerSession()};
+            server_session->CompleteSyncRequest(*context);
         });
     }
     condition.notify_one();
@@ -102,7 +111,7 @@ ServiceThread::ServiceThread(KernelCore& kernel, std::size_t num_threads, const
 
 ServiceThread::~ServiceThread() = default;
 
-void ServiceThread::QueueSyncRequest(ServerSession& session,
+void ServiceThread::QueueSyncRequest(KSession& session,
                                      std::shared_ptr<HLERequestContext>&& context) {
     impl->QueueSyncRequest(session, std::move(context));
 }
diff --git a/src/core/hle/kernel/service_thread.h b/src/core/hle/kernel/service_thread.h
index 025ab8fb5..6a7fd7c56 100644
--- a/src/core/hle/kernel/service_thread.h
+++ b/src/core/hle/kernel/service_thread.h
@@ -11,14 +11,14 @@ namespace Kernel {
 
 class HLERequestContext;
 class KernelCore;
-class ServerSession;
+class KSession;
 
 class ServiceThread final {
 public:
     explicit ServiceThread(KernelCore& kernel, std::size_t num_threads, const std::string& name);
     ~ServiceThread();
 
-    void QueueSyncRequest(ServerSession& session, std::shared_ptr<HLERequestContext>&& context);
+    void QueueSyncRequest(KSession& session, std::shared_ptr<HLERequestContext>&& context);
 
 private:
     class Impl;
diff --git a/src/core/hle/kernel/session.cpp b/src/core/hle/kernel/session.cpp
deleted file mode 100644
index 8830d4e91..000000000
--- a/src/core/hle/kernel/session.cpp
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#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() {
-    // 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()};
-
-    session->name = std::move(name);
-    session->client = client_session;
-    session->server = server_session;
-
-    session_reservation.Commit();
-    return std::make_pair(std::move(client_session), std::move(server_session));
-}
-
-bool Session::IsSignaled() const {
-    UNIMPLEMENTED();
-    return true;
-}
-
-} // namespace Kernel
diff --git a/src/core/hle/kernel/session.h b/src/core/hle/kernel/session.h
deleted file mode 100644
index fa3c5651a..000000000
--- a/src/core/hle/kernel/session.h
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <memory>
-#include <string>
-#include <utility>
-
-#include "core/hle/kernel/k_synchronization_object.h"
-
-namespace Kernel {
-
-class ClientSession;
-class ServerSession;
-
-/**
- * Parent structure to link the client and server endpoints of a session with their associated
- * client port.
- */
-class Session final : public KSynchronizationObject {
-public:
-    explicit Session(KernelCore& kernel);
-    ~Session() override;
-
-    using SessionPair = std::pair<std::shared_ptr<ClientSession>, std::shared_ptr<ServerSession>>;
-
-    static SessionPair Create(KernelCore& kernel, std::string name = "Unknown");
-
-    std::string GetName() const override {
-        return name;
-    }
-
-    static constexpr HandleType HANDLE_TYPE = HandleType::Session;
-    HandleType GetHandleType() const override {
-        return HANDLE_TYPE;
-    }
-
-    bool IsSignaled() const override;
-
-    void Finalize() override {}
-
-    std::shared_ptr<ClientSession> Client() {
-        if (auto result{client.lock()}) {
-            return result;
-        }
-        return {};
-    }
-
-    std::shared_ptr<ServerSession> Server() {
-        if (auto result{server.lock()}) {
-            return result;
-        }
-        return {};
-    }
-
-private:
-    std::string name;
-    std::weak_ptr<ClientSession> client;
-    std::weak_ptr<ServerSession> server;
-};
-
-} // namespace Kernel
diff --git a/src/core/hle/kernel/slab_helpers.h b/src/core/hle/kernel/slab_helpers.h
new file mode 100644
index 000000000..0c5995db0
--- /dev/null
+++ b/src/core/hle/kernel/slab_helpers.h
@@ -0,0 +1,148 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <atomic>
+
+#include "common/assert.h"
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+#include "common/intrusive_red_black_tree.h"
+#include "core/hle/kernel/k_auto_object.h"
+#include "core/hle/kernel/k_auto_object_container.h"
+#include "core/hle/kernel/k_light_lock.h"
+#include "core/hle/kernel/k_slab_heap.h"
+#include "core/hle/kernel/kernel.h"
+
+namespace Kernel {
+
+template <class Derived>
+class KSlabAllocated {
+public:
+    constexpr KSlabAllocated() = default;
+
+    size_t GetSlabIndex(KernelCore& kernel) const {
+        return kernel.SlabHeap<Derived>().GetIndex(static_cast<const Derived*>(this));
+    }
+
+public:
+    static void InitializeSlabHeap(KernelCore& kernel, void* memory, size_t memory_size) {
+        kernel.SlabHeap<Derived>().Initialize(memory, memory_size);
+    }
+
+    static Derived* Allocate(KernelCore& kernel) {
+        return kernel.SlabHeap<Derived>().Allocate();
+    }
+
+    static void Free(KernelCore& kernel, Derived* obj) {
+        kernel.SlabHeap<Derived>().Free(obj);
+    }
+
+    static size_t GetObjectSize(KernelCore& kernel) {
+        return kernel.SlabHeap<Derived>().GetObjectSize();
+    }
+
+    static size_t GetSlabHeapSize(KernelCore& kernel) {
+        return kernel.SlabHeap<Derived>().GetSlabHeapSize();
+    }
+
+    static size_t GetPeakIndex(KernelCore& kernel) {
+        return kernel.SlabHeap<Derived>().GetPeakIndex();
+    }
+
+    static uintptr_t GetSlabHeapAddress(KernelCore& kernel) {
+        return kernel.SlabHeap<Derived>().GetSlabHeapAddress();
+    }
+
+    static size_t GetNumRemaining(KernelCore& kernel) {
+        return kernel.SlabHeap<Derived>().GetNumRemaining();
+    }
+};
+
+template <typename Derived, typename Base>
+class KAutoObjectWithSlabHeapAndContainer : public Base {
+    static_assert(std::is_base_of<KAutoObjectWithList, Base>::value);
+
+private:
+    static Derived* Allocate(KernelCore& kernel) {
+        return kernel.SlabHeap<Derived>().AllocateWithKernel(kernel);
+    }
+
+    static void Free(KernelCore& kernel, Derived* obj) {
+        kernel.SlabHeap<Derived>().Free(obj);
+    }
+
+public:
+    KAutoObjectWithSlabHeapAndContainer(KernelCore& kernel_) : Base(kernel_), kernel(kernel_) {}
+    virtual ~KAutoObjectWithSlabHeapAndContainer() {}
+
+    virtual void Destroy() override {
+        const bool is_initialized = this->IsInitialized();
+        uintptr_t arg = 0;
+        if (is_initialized) {
+            kernel.ObjectListContainer().Unregister(this);
+            arg = this->GetPostDestroyArgument();
+            this->Finalize();
+        }
+        Free(kernel, static_cast<Derived*>(this));
+        if (is_initialized) {
+            Derived::PostDestroy(arg);
+        }
+    }
+
+    virtual bool IsInitialized() const {
+        return true;
+    }
+    virtual uintptr_t GetPostDestroyArgument() const {
+        return 0;
+    }
+
+    size_t GetSlabIndex() const {
+        return SlabHeap<Derived>(kernel).GetObjectIndex(static_cast<const Derived*>(this));
+    }
+
+public:
+    static void InitializeSlabHeap(KernelCore& kernel, void* memory, size_t memory_size) {
+        kernel.SlabHeap<Derived>().Initialize(memory, memory_size);
+        kernel.ObjectListContainer().Initialize();
+    }
+
+    static Derived* Create(KernelCore& kernel) {
+        Derived* obj = Allocate(kernel);
+        if (obj != nullptr) {
+            KAutoObject::Create(obj);
+        }
+        return obj;
+    }
+
+    static void Register(KernelCore& kernel, Derived* obj) {
+        return kernel.ObjectListContainer().Register(obj);
+    }
+
+    static size_t GetObjectSize(KernelCore& kernel) {
+        return kernel.SlabHeap<Derived>().GetObjectSize();
+    }
+
+    static size_t GetSlabHeapSize(KernelCore& kernel) {
+        return kernel.SlabHeap<Derived>().GetSlabHeapSize();
+    }
+
+    static size_t GetPeakIndex(KernelCore& kernel) {
+        return kernel.SlabHeap<Derived>().GetPeakIndex();
+    }
+
+    static uintptr_t GetSlabHeapAddress(KernelCore& kernel) {
+        return kernel.SlabHeap<Derived>().GetSlabHeapAddress();
+    }
+
+    static size_t GetNumRemaining(KernelCore& kernel) {
+        return kernel.SlabHeap<Derived>().GetNumRemaining();
+    }
+
+protected:
+    KernelCore& kernel;
+};
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index bebb86154..52011be9c 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -21,15 +21,16 @@
 #include "core/core_timing.h"
 #include "core/core_timing_util.h"
 #include "core/cpu_manager.h"
-#include "core/hle/kernel/client_port.h"
-#include "core/hle/kernel/client_session.h"
-#include "core/hle/kernel/handle_table.h"
 #include "core/hle/kernel/k_address_arbiter.h"
+#include "core/hle/kernel/k_client_port.h"
+#include "core/hle/kernel/k_client_session.h"
 #include "core/hle/kernel/k_condition_variable.h"
 #include "core/hle/kernel/k_event.h"
+#include "core/hle/kernel/k_handle_table.h"
 #include "core/hle/kernel/k_memory_block.h"
 #include "core/hle/kernel/k_memory_layout.h"
 #include "core/hle/kernel/k_page_table.h"
+#include "core/hle/kernel/k_process.h"
 #include "core/hle/kernel/k_readable_event.h"
 #include "core/hle/kernel/k_resource_limit.h"
 #include "core/hle/kernel/k_scheduler.h"
@@ -38,16 +39,15 @@
 #include "core/hle/kernel/k_shared_memory.h"
 #include "core/hle/kernel/k_synchronization_object.h"
 #include "core/hle/kernel/k_thread.h"
+#include "core/hle/kernel/k_transfer_memory.h"
 #include "core/hle/kernel/k_writable_event.h"
 #include "core/hle/kernel/kernel.h"
 #include "core/hle/kernel/physical_core.h"
-#include "core/hle/kernel/process.h"
 #include "core/hle/kernel/svc.h"
 #include "core/hle/kernel/svc_results.h"
 #include "core/hle/kernel/svc_types.h"
 #include "core/hle/kernel/svc_wrap.h"
 #include "core/hle/kernel/time_manager.h"
-#include "core/hle/kernel/transfer_memory.h"
 #include "core/hle/lock.h"
 #include "core/hle/result.h"
 #include "core/hle/service/service.h"
@@ -113,7 +113,7 @@ ResultCode MapUnmapMemorySanityChecks(const KPageTable& manager, VAddr dst_addr,
         LOG_ERROR(Kernel_SVC,
                   "Destination is not within the stack region, addr=0x{:016X}, size=0x{:016X}",
                   dst_addr, size);
-        return ResultInvalidMemoryRange;
+        return ResultInvalidMemoryRegion;
     }
 
     if (manager.IsInsideHeapRegion(dst_addr, size)) {
@@ -121,7 +121,7 @@ ResultCode MapUnmapMemorySanityChecks(const KPageTable& manager, VAddr dst_addr,
                   "Destination does not fit within the heap region, addr=0x{:016X}, "
                   "size=0x{:016X}",
                   dst_addr, size);
-        return ResultInvalidMemoryRange;
+        return ResultInvalidMemoryRegion;
     }
 
     if (manager.IsInsideAliasRegion(dst_addr, size)) {
@@ -129,7 +129,7 @@ ResultCode MapUnmapMemorySanityChecks(const KPageTable& manager, VAddr dst_addr,
                   "Destination does not fit within the map region, addr=0x{:016X}, "
                   "size=0x{:016X}",
                   dst_addr, size);
-        return ResultInvalidMemoryRange;
+        return ResultInvalidMemoryRegion;
     }
 
     return RESULT_SUCCESS;
@@ -141,38 +141,6 @@ enum class ResourceLimitValueType {
     PeakValue,
 };
 
-ResultVal<s64> RetrieveResourceLimitValue(Core::System& system, Handle resource_limit,
-                                          u32 resource_type, ResourceLimitValueType value_type) {
-    std::lock_guard lock{HLE::g_hle_lock};
-    const auto type = static_cast<LimitableResource>(resource_type);
-    if (!IsValidResourceType(type)) {
-        LOG_ERROR(Kernel_SVC, "Invalid resource limit type: '{}'", resource_type);
-        return ResultInvalidEnumValue;
-    }
-
-    const auto* const current_process = system.Kernel().CurrentProcess();
-    ASSERT(current_process != nullptr);
-
-    const auto resource_limit_object =
-        current_process->GetHandleTable().Get<KResourceLimit>(resource_limit);
-    if (!resource_limit_object) {
-        LOG_ERROR(Kernel_SVC, "Handle to non-existent resource limit instance used. Handle={:08X}",
-                  resource_limit);
-        return ResultInvalidHandle;
-    }
-
-    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 ResultInvalidEnumValue;
-    }
-}
 } // Anonymous namespace
 
 /// Set the process heap to a given Size. It can both extend and shrink the heap.
@@ -291,11 +259,8 @@ static ResultCode UnmapMemory32(Core::System& system, u32 dst_addr, u32 src_addr
 }
 
 /// Connect to an OS service given the port name, returns the handle to the port to out
-static ResultCode ConnectToNamedPort(Core::System& system, Handle* out_handle,
-                                     VAddr port_name_address) {
-    std::lock_guard lock{HLE::g_hle_lock};
+static ResultCode ConnectToNamedPort(Core::System& system, Handle* out, VAddr port_name_address) {
     auto& memory = system.Memory();
-
     if (!memory.IsValidVirtualAddress(port_name_address)) {
         LOG_ERROR(Kernel_SVC,
                   "Port Name Address is not a valid virtual address, port_name_address=0x{:016X}",
@@ -314,21 +279,33 @@ static ResultCode ConnectToNamedPort(Core::System& system, Handle* out_handle,
 
     LOG_TRACE(Kernel_SVC, "called port_name={}", port_name);
 
+    // Get the current handle table.
     auto& kernel = system.Kernel();
+    auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
+
+    // Find the client port.
     const auto it = kernel.FindNamedPort(port_name);
     if (!kernel.IsValidNamedPort(it)) {
         LOG_WARNING(Kernel_SVC, "tried to connect to unknown port: {}", port_name);
         return ResultNotFound;
     }
+    auto port = it->second;
 
-    auto client_port = it->second;
+    // Reserve a handle for the port.
+    // NOTE: Nintendo really does write directly to the output handle here.
+    R_TRY(handle_table.Reserve(out));
+    auto handle_guard = SCOPE_GUARD({ handle_table.Unreserve(*out); });
 
-    std::shared_ptr<ClientSession> client_session;
-    CASCADE_RESULT(client_session, client_port->Connect());
+    // Create a session.
+    KClientSession* session{};
+    R_TRY(port->CreateSession(std::addressof(session)));
 
-    // Return the client session
-    auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
-    CASCADE_RESULT(*out_handle, handle_table.Create(client_session));
+    // Register the session in the table, close the extra reference.
+    handle_table.Register(*out, session);
+    session->Close();
+
+    // We succeeded.
+    handle_guard.Cancel();
     return RESULT_SUCCESS;
 }
 
@@ -340,14 +317,12 @@ static ResultCode ConnectToNamedPort32(Core::System& system, Handle* out_handle,
 
 /// Makes a blocking IPC call to an OS service.
 static ResultCode SendSyncRequest(Core::System& system, Handle handle) {
-    auto& kernel = system.Kernel();
-    const auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
-    std::shared_ptr<ClientSession> session = handle_table.Get<ClientSession>(handle);
-    if (!session) {
-        LOG_ERROR(Kernel_SVC, "called with invalid handle=0x{:08X}", handle);
-        return ResultInvalidHandle;
-    }
 
+    auto& kernel = system.Kernel();
+
+    KScopedAutoObject session =
+        kernel.CurrentProcess()->GetHandleTable().GetObject<KClientSession>(handle);
+    R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
     LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName());
 
     auto thread = kernel.CurrentScheduler()->GetCurrentThread();
@@ -355,7 +330,7 @@ static ResultCode SendSyncRequest(Core::System& system, Handle handle) {
         KScopedSchedulerLock lock(kernel);
         thread->SetState(ThreadState::Waiting);
         thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC);
-        session->SendSyncRequest(SharedFrom(thread), system.Memory(), system.CoreTiming());
+        session->SendSyncRequest(thread, system.Memory(), system.CoreTiming());
     }
 
     KSynchronizationObject* dummy{};
@@ -368,18 +343,13 @@ static ResultCode SendSyncRequest32(Core::System& system, Handle handle) {
 
 /// Get the ID for the specified thread.
 static ResultCode GetThreadId(Core::System& system, u64* out_thread_id, Handle thread_handle) {
-    LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
-
     // Get the thread from its handle.
-    const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
-    const std::shared_ptr<KThread> thread = handle_table.Get<KThread>(thread_handle);
-    if (!thread) {
-        LOG_ERROR(Kernel_SVC, "Invalid thread handle provided (handle={:08X})", thread_handle);
-        return ResultInvalidHandle;
-    }
+    KScopedAutoObject thread =
+        system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle);
+    R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
 
     // Get the thread's id.
-    *out_thread_id = thread->GetThreadID();
+    *out_thread_id = thread->GetId();
     return RESULT_SUCCESS;
 }
 
@@ -395,110 +365,101 @@ static ResultCode GetThreadId32(Core::System& system, u32* out_thread_id_low,
 }
 
 /// Gets the ID of the specified process or a specified thread's owning process.
-static ResultCode GetProcessId(Core::System& system, u64* process_id, Handle handle) {
+static ResultCode GetProcessId(Core::System& system, u64* out_process_id, Handle handle) {
     LOG_DEBUG(Kernel_SVC, "called handle=0x{:08X}", handle);
 
-    const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
-    const std::shared_ptr<Process> process = handle_table.Get<Process>(handle);
-    if (process) {
-        *process_id = process->GetProcessID();
-        return RESULT_SUCCESS;
+    // Get the object from the handle table.
+    KScopedAutoObject obj =
+        system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KAutoObject>(
+            static_cast<Handle>(handle));
+    R_UNLESS(obj.IsNotNull(), ResultInvalidHandle);
+
+    // Get the process from the object.
+    KProcess* process = nullptr;
+    if (KProcess* p = obj->DynamicCast<KProcess*>(); p != nullptr) {
+        // The object is a process, so we can use it directly.
+        process = p;
+    } else if (KThread* t = obj->DynamicCast<KThread*>(); t != nullptr) {
+        // The object is a thread, so we want to use its parent.
+        process = reinterpret_cast<KThread*>(obj.GetPointerUnsafe())->GetOwnerProcess();
+    } else {
+        // TODO(bunnei): This should also handle debug objects before returning.
+        UNIMPLEMENTED_MSG("Debug objects not implemented");
     }
 
-    const std::shared_ptr<KThread> thread = handle_table.Get<KThread>(handle);
-    if (thread) {
-        const Process* const owner_process = thread->GetOwnerProcess();
-        if (!owner_process) {
-            LOG_ERROR(Kernel_SVC, "Non-existent owning process encountered.");
-            return ResultInvalidHandle;
-        }
+    // Make sure the target process exists.
+    R_UNLESS(process != nullptr, ResultInvalidHandle);
 
-        *process_id = owner_process->GetProcessID();
-        return RESULT_SUCCESS;
-    }
+    // Get the process id.
+    *out_process_id = process->GetId();
 
-    // NOTE: This should also handle debug objects before returning.
-
-    LOG_ERROR(Kernel_SVC, "Handle does not exist, handle=0x{:08X}", handle);
     return ResultInvalidHandle;
 }
 
-static ResultCode GetProcessId32(Core::System& system, u32* process_id_low, u32* process_id_high,
-                                 Handle handle) {
-    u64 process_id{};
-    const auto result = GetProcessId(system, &process_id, handle);
-    *process_id_low = static_cast<u32>(process_id);
-    *process_id_high = static_cast<u32>(process_id >> 32);
+static ResultCode GetProcessId32(Core::System& system, u32* out_process_id_low,
+                                 u32* out_process_id_high, Handle handle) {
+    u64 out_process_id{};
+    const auto result = GetProcessId(system, &out_process_id, handle);
+    *out_process_id_low = static_cast<u32>(out_process_id);
+    *out_process_id_high = static_cast<u32>(out_process_id >> 32);
     return result;
 }
 
 /// Wait for the given handles to synchronize, timeout after the specified nanoseconds
 static ResultCode WaitSynchronization(Core::System& system, s32* index, VAddr handles_address,
-                                      u64 handle_count, s64 nano_seconds) {
-    LOG_TRACE(Kernel_SVC, "called handles_address=0x{:X}, handle_count={}, nano_seconds={}",
-              handles_address, handle_count, nano_seconds);
+                                      u64 num_handles, s64 nano_seconds) {
+    LOG_TRACE(Kernel_SVC, "called handles_address=0x{:X}, num_handles={}, nano_seconds={}",
+              handles_address, num_handles, nano_seconds);
 
-    auto& memory = system.Memory();
-    if (!memory.IsValidVirtualAddress(handles_address)) {
-        LOG_ERROR(Kernel_SVC,
-                  "Handle address is not a valid virtual address, handle_address=0x{:016X}",
-                  handles_address);
-        return ResultInvalidPointer;
-    }
-
-    static constexpr u64 MaxHandles = 0x40;
-
-    if (handle_count > MaxHandles) {
-        LOG_ERROR(Kernel_SVC, "Handle count specified is too large, expected {} but got {}",
-                  MaxHandles, handle_count);
-        return ResultOutOfRange;
-    }
+    // Ensure number of handles is valid.
+    R_UNLESS(0 <= num_handles && num_handles <= ArgumentHandleCountMax, ResultOutOfRange);
 
     auto& kernel = system.Kernel();
-    std::vector<KSynchronizationObject*> objects(handle_count);
+    std::vector<KSynchronizationObject*> objs(num_handles);
     const auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
+    Handle* handles = system.Memory().GetPointer<Handle>(handles_address);
 
-    for (u64 i = 0; i < handle_count; ++i) {
-        const Handle handle = memory.Read32(handles_address + i * sizeof(Handle));
-        const auto object = handle_table.Get<KSynchronizationObject>(handle);
-
-        if (object == nullptr) {
-            LOG_ERROR(Kernel_SVC, "Object is a nullptr");
-            return ResultInvalidHandle;
-        }
-
-        objects[i] = object.get();
+    // Copy user handles.
+    if (num_handles > 0) {
+        // Convert the handles to objects.
+        R_UNLESS(handle_table.GetMultipleObjects<KSynchronizationObject>(objs.data(), handles,
+                                                                         num_handles),
+                 ResultInvalidHandle);
     }
-    return KSynchronizationObject::Wait(kernel, index, objects.data(),
-                                        static_cast<s32>(objects.size()), nano_seconds);
+
+    // Ensure handles are closed when we're done.
+    SCOPE_EXIT({
+        for (u64 i = 0; i < num_handles; ++i) {
+            objs[i]->Close();
+        }
+    });
+
+    return KSynchronizationObject::Wait(kernel, index, objs.data(), static_cast<s32>(objs.size()),
+                                        nano_seconds);
 }
 
 static ResultCode WaitSynchronization32(Core::System& system, u32 timeout_low, u32 handles_address,
-                                        s32 handle_count, u32 timeout_high, s32* index) {
+                                        s32 num_handles, u32 timeout_high, s32* index) {
     const s64 nano_seconds{(static_cast<s64>(timeout_high) << 32) | static_cast<s64>(timeout_low)};
-    return WaitSynchronization(system, index, handles_address, handle_count, nano_seconds);
+    return WaitSynchronization(system, index, handles_address, num_handles, nano_seconds);
 }
 
 /// Resumes a thread waiting on WaitSynchronization
-static ResultCode CancelSynchronization(Core::System& system, Handle thread_handle) {
-    LOG_TRACE(Kernel_SVC, "called thread=0x{:X}", thread_handle);
+static ResultCode CancelSynchronization(Core::System& system, Handle handle) {
+    LOG_TRACE(Kernel_SVC, "called handle=0x{:X}", handle);
 
     // Get the thread from its handle.
-    const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
-    std::shared_ptr<KThread> thread = handle_table.Get<KThread>(thread_handle);
-
-    if (!thread) {
-        LOG_ERROR(Kernel_SVC, "Invalid thread handle provided (handle={:08X})", thread_handle);
-        return ResultInvalidHandle;
-    }
+    KScopedAutoObject thread =
+        system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(
+            static_cast<Handle>(handle));
 
     // Cancel the thread's wait.
     thread->WaitCancel();
     return RESULT_SUCCESS;
 }
 
-static ResultCode CancelSynchronization32(Core::System& system, Handle thread_handle) {
-    return CancelSynchronization(system, thread_handle);
+static ResultCode CancelSynchronization32(Core::System& system, Handle handle) {
+    return CancelSynchronization(system, handle);
 }
 
 /// Attempts to locks a mutex
@@ -678,7 +639,7 @@ static void OutputDebugString(Core::System& system, VAddr address, u64 len) {
 }
 
 /// Gets system/memory information for the current process
-static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 handle,
+static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle handle,
                           u64 info_sub_id) {
     std::lock_guard lock{HLE::g_hle_lock};
     LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id,
@@ -744,10 +705,9 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
             return ResultInvalidEnumValue;
         }
 
-        const auto& current_process_handle_table =
-            system.Kernel().CurrentProcess()->GetHandleTable();
-        const auto process = current_process_handle_table.Get<Process>(static_cast<Handle>(handle));
-        if (!process) {
+        const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
+        KScopedAutoObject process = handle_table.GetObject<KProcess>(handle);
+        if (process.IsNull()) {
             LOG_ERROR(Kernel_SVC, "Process is not valid! info_id={}, info_sub_id={}, handle={:08X}",
                       info_id, info_sub_id, handle);
             return ResultInvalidHandle;
@@ -851,21 +811,19 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
             return ResultInvalidCombination;
         }
 
-        Process* const current_process = system.Kernel().CurrentProcess();
-        HandleTable& handle_table = current_process->GetHandleTable();
+        KProcess* const current_process = system.Kernel().CurrentProcess();
+        KHandleTable& handle_table = current_process->GetHandleTable();
         const auto resource_limit = current_process->GetResourceLimit();
         if (!resource_limit) {
-            *result = KernelHandle::InvalidHandle;
+            *result = Svc::InvalidHandle;
             // Yes, the kernel considers this a successful operation.
             return RESULT_SUCCESS;
         }
 
-        const auto table_result = handle_table.Create(resource_limit);
-        if (table_result.Failed()) {
-            return table_result.Code();
-        }
+        Handle handle{};
+        R_TRY(handle_table.Add(&handle, resource_limit));
 
-        *result = *table_result;
+        *result = handle;
         return RESULT_SUCCESS;
     }
 
@@ -876,9 +834,9 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
             return ResultInvalidHandle;
         }
 
-        if (info_sub_id >= Process::RANDOM_ENTROPY_SIZE) {
+        if (info_sub_id >= KProcess::RANDOM_ENTROPY_SIZE) {
             LOG_ERROR(Kernel_SVC, "Entropy size is out of range, expected {} but got {}",
-                      Process::RANDOM_ENTROPY_SIZE, info_sub_id);
+                      KProcess::RANDOM_ENTROPY_SIZE, info_sub_id);
             return ResultInvalidCombination;
         }
 
@@ -899,9 +857,10 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
             return ResultInvalidCombination;
         }
 
-        const auto thread = system.Kernel().CurrentProcess()->GetHandleTable().Get<KThread>(
-            static_cast<Handle>(handle));
-        if (!thread) {
+        KScopedAutoObject thread =
+            system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(
+                static_cast<Handle>(handle));
+        if (thread.IsNull()) {
             LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}",
                       static_cast<Handle>(handle));
             return ResultInvalidHandle;
@@ -910,7 +869,7 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
         const auto& core_timing = system.CoreTiming();
         const auto& scheduler = *system.Kernel().CurrentScheduler();
         const auto* const current_thread = scheduler.GetCurrentThread();
-        const bool same_thread = current_thread == thread.get();
+        const bool same_thread = current_thread == thread.GetPointerUnsafe();
 
         const u64 prev_ctx_ticks = scheduler.GetLastContextSwitchTicks();
         u64 out_ticks = 0;
@@ -966,10 +925,10 @@ static ResultCode MapPhysicalMemory(Core::System& system, VAddr addr, u64 size)
 
     if (!(addr < addr + size)) {
         LOG_ERROR(Kernel_SVC, "Size causes 64-bit overflow of address");
-        return ResultInvalidMemoryRange;
+        return ResultInvalidMemoryRegion;
     }
 
-    Process* const current_process{system.Kernel().CurrentProcess()};
+    KProcess* const current_process{system.Kernel().CurrentProcess()};
     auto& page_table{current_process->PageTable()};
 
     if (current_process->GetSystemResourceSize() == 0) {
@@ -981,14 +940,14 @@ static ResultCode MapPhysicalMemory(Core::System& system, VAddr addr, u64 size)
         LOG_ERROR(Kernel_SVC,
                   "Address is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr,
                   size);
-        return ResultInvalidMemoryRange;
+        return ResultInvalidMemoryRegion;
     }
 
     if (page_table.IsOutsideAliasRegion(addr, size)) {
         LOG_ERROR(Kernel_SVC,
                   "Address is not within the alias region, addr=0x{:016X}, size=0x{:016X}", addr,
                   size);
-        return ResultInvalidMemoryRange;
+        return ResultInvalidMemoryRegion;
     }
 
     return page_table.MapPhysicalMemory(addr, size);
@@ -1020,10 +979,10 @@ static ResultCode UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size
 
     if (!(addr < addr + size)) {
         LOG_ERROR(Kernel_SVC, "Size causes 64-bit overflow of address");
-        return ResultInvalidMemoryRange;
+        return ResultInvalidMemoryRegion;
     }
 
-    Process* const current_process{system.Kernel().CurrentProcess()};
+    KProcess* const current_process{system.Kernel().CurrentProcess()};
     auto& page_table{current_process->PageTable()};
 
     if (current_process->GetSystemResourceSize() == 0) {
@@ -1035,14 +994,14 @@ static ResultCode UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size
         LOG_ERROR(Kernel_SVC,
                   "Address is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr,
                   size);
-        return ResultInvalidMemoryRange;
+        return ResultInvalidMemoryRegion;
     }
 
     if (page_table.IsOutsideAliasRegion(addr, size)) {
         LOG_ERROR(Kernel_SVC,
                   "Address is not within the alias region, addr=0x{:016X}, size=0x{:016X}", addr,
                   size);
-        return ResultInvalidMemoryRange;
+        return ResultInvalidMemoryRegion;
     }
 
     return page_table.UnmapPhysicalMemory(addr, size);
@@ -1062,37 +1021,19 @@ static ResultCode SetThreadActivity(Core::System& system, Handle thread_handle,
     constexpr auto IsValidThreadActivity = [](ThreadActivity activity) {
         return activity == ThreadActivity::Runnable || activity == ThreadActivity::Paused;
     };
-    if (!IsValidThreadActivity(thread_activity)) {
-        LOG_ERROR(Kernel_SVC, "Invalid thread activity value provided (activity={})",
-                  thread_activity);
-        return ResultInvalidEnumValue;
-    }
+    R_UNLESS(IsValidThreadActivity(thread_activity), ResultInvalidEnumValue);
 
     // Get the thread from its handle.
-    auto& kernel = system.Kernel();
-    const auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
-    const std::shared_ptr<KThread> thread = handle_table.Get<KThread>(thread_handle);
-    if (!thread) {
-        LOG_ERROR(Kernel_SVC, "Invalid thread handle provided (handle={:08X})", thread_handle);
-        return ResultInvalidHandle;
-    }
+    KScopedAutoObject thread =
+        system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle);
+    R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
 
     // Check that the activity is being set on a non-current thread for the current process.
-    if (thread->GetOwnerProcess() != kernel.CurrentProcess()) {
-        LOG_ERROR(Kernel_SVC, "Invalid owning process for the created thread.");
-        return ResultInvalidHandle;
-    }
-    if (thread.get() == GetCurrentThreadPointer(kernel)) {
-        LOG_ERROR(Kernel_SVC, "Thread is busy");
-        return ResultBusy;
-    }
+    R_UNLESS(thread->GetOwnerProcess() == system.Kernel().CurrentProcess(), ResultInvalidHandle);
+    R_UNLESS(thread.GetPointerUnsafe() != GetCurrentThreadPointer(system.Kernel()), ResultBusy);
 
     // Set the activity.
-    const auto set_result = thread->SetActivity(thread_activity);
-    if (set_result.IsError()) {
-        LOG_ERROR(Kernel_SVC, "Failed to set thread activity.");
-        return set_result;
-    }
+    R_TRY(thread->SetActivity(thread_activity));
 
     return RESULT_SUCCESS;
 }
@@ -1107,36 +1048,55 @@ static ResultCode GetThreadContext(Core::System& system, VAddr out_context, Hand
     LOG_DEBUG(Kernel_SVC, "called, out_context=0x{:08X}, thread_handle=0x{:X}", out_context,
               thread_handle);
 
+    auto& kernel = system.Kernel();
+
     // Get the thread from its handle.
-    const auto* current_process = system.Kernel().CurrentProcess();
-    const std::shared_ptr<KThread> thread =
-        current_process->GetHandleTable().Get<KThread>(thread_handle);
-    if (!thread) {
-        LOG_ERROR(Kernel_SVC, "Invalid thread handle provided (handle={})", thread_handle);
-        return ResultInvalidHandle;
-    }
+    KScopedAutoObject thread =
+        kernel.CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle);
+    R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
 
     // Require the handle be to a non-current thread in the current process.
-    if (thread->GetOwnerProcess() != current_process) {
-        LOG_ERROR(Kernel_SVC, "Thread owning process is not the current process.");
-        return ResultInvalidHandle;
-    }
-    if (thread.get() == system.Kernel().CurrentScheduler()->GetCurrentThread()) {
-        LOG_ERROR(Kernel_SVC, "Current thread is busy.");
-        return ResultBusy;
-    }
+    const auto* current_process = kernel.CurrentProcess();
+    R_UNLESS(current_process == thread->GetOwnerProcess(), ResultInvalidId);
 
-    // Get the thread context.
-    std::vector<u8> context;
-    const auto context_result = thread->GetThreadContext3(context);
-    if (context_result.IsError()) {
-        LOG_ERROR(Kernel_SVC, "Unable to successfully retrieve thread context (result: {})",
-                  context_result.raw);
-        return context_result;
-    }
+    // Verify that the thread isn't terminated.
+    R_UNLESS(thread->GetState() != ThreadState::Terminated, ResultTerminationRequested);
 
-    // Copy the thread context to user space.
-    system.Memory().WriteBlock(out_context, context.data(), context.size());
+    /// Check that the thread is not the current one.
+    /// NOTE: Nintendo does not check this, and thus the following loop will deadlock.
+    R_UNLESS(thread.GetPointerUnsafe() != GetCurrentThreadPointer(kernel), ResultInvalidId);
+
+    // Try to get the thread context until the thread isn't current on any core.
+    while (true) {
+        KScopedSchedulerLock sl{kernel};
+
+        // TODO(bunnei): Enforce that thread is suspended for debug here.
+
+        // If the thread's raw state isn't runnable, check if it's current on some core.
+        if (thread->GetRawState() != ThreadState::Runnable) {
+            bool current = false;
+            for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) {
+                if (thread.GetPointerUnsafe() == kernel.Scheduler(i).GetCurrentThread()) {
+                    current = true;
+                }
+                break;
+            }
+
+            // If the thread is current, retry until it isn't.
+            if (current) {
+                continue;
+            }
+        }
+
+        // Get the thread context.
+        std::vector<u8> context;
+        R_TRY(thread->GetThreadContext3(context));
+
+        // Copy the thread context to user space.
+        system.Memory().WriteBlock(out_context, context.data(), context.size());
+
+        return RESULT_SUCCESS;
+    }
 
     return RESULT_SUCCESS;
 }
@@ -1150,12 +1110,9 @@ static ResultCode GetThreadPriority(Core::System& system, u32* out_priority, Han
     LOG_TRACE(Kernel_SVC, "called");
 
     // Get the thread from its handle.
-    const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
-    const std::shared_ptr<KThread> thread = handle_table.Get<KThread>(handle);
-    if (!thread) {
-        LOG_ERROR(Kernel_SVC, "Invalid thread handle provided (handle={:08X})", handle);
-        return ResultInvalidHandle;
-    }
+    KScopedAutoObject thread =
+        system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(handle);
+    R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
 
     // Get the thread's priority.
     *out_priority = thread->GetPriority();
@@ -1167,30 +1124,26 @@ static ResultCode GetThreadPriority32(Core::System& system, u32* out_priority, H
 }
 
 /// Sets the priority for the specified thread
-static ResultCode SetThreadPriority(Core::System& system, Handle handle, u32 priority) {
-    LOG_TRACE(Kernel_SVC, "called");
+static ResultCode SetThreadPriority(Core::System& system, Handle thread_handle, u32 priority) {
+    // Get the current process.
+    KProcess& process = *system.Kernel().CurrentProcess();
 
     // Validate the priority.
-    if (HighestThreadPriority > priority || priority > LowestThreadPriority) {
-        LOG_ERROR(Kernel_SVC, "Invalid thread priority specified (priority={})", priority);
-        return ResultInvalidPriority;
-    }
+    R_UNLESS(HighestThreadPriority <= priority && priority <= LowestThreadPriority,
+             ResultInvalidPriority);
+    R_UNLESS(process.CheckThreadPriority(priority), ResultInvalidPriority);
 
     // Get the thread from its handle.
-    const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
-    const std::shared_ptr<KThread> thread = handle_table.Get<KThread>(handle);
-    if (!thread) {
-        LOG_ERROR(Kernel_SVC, "Invalid handle provided (handle={:08X})", handle);
-        return ResultInvalidHandle;
-    }
+    KScopedAutoObject thread = process.GetHandleTable().GetObject<KThread>(thread_handle);
+    R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
 
     // Set the thread priority.
     thread->SetBasePriority(priority);
     return RESULT_SUCCESS;
 }
 
-static ResultCode SetThreadPriority32(Core::System& system, Handle handle, u32 priority) {
-    return SetThreadPriority(system, handle, priority);
+static ResultCode SetThreadPriority32(Core::System& system, Handle thread_handle, u32 priority) {
+    return SetThreadPriority(system, thread_handle, priority);
 }
 
 /// Get which CPU core is executing the current thread
@@ -1203,82 +1156,97 @@ static u32 GetCurrentProcessorNumber32(Core::System& system) {
     return GetCurrentProcessorNumber(system);
 }
 
-static ResultCode MapSharedMemory(Core::System& system, Handle shared_memory_handle, VAddr addr,
-                                  u64 size, u32 permissions) {
-    std::lock_guard lock{HLE::g_hle_lock};
-    LOG_TRACE(Kernel_SVC,
-              "called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}",
-              shared_memory_handle, addr, size, permissions);
-
-    if (!Common::Is4KBAligned(addr)) {
-        LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, addr=0x{:016X}", addr);
-        return ResultInvalidAddress;
+constexpr bool IsValidSharedMemoryPermission(Svc::MemoryPermission perm) {
+    switch (perm) {
+    case Svc::MemoryPermission::Read:
+    case Svc::MemoryPermission::ReadWrite:
+        return true;
+    default:
+        return false;
     }
-
-    if (size == 0) {
-        LOG_ERROR(Kernel_SVC, "Size is 0");
-        return ResultInvalidSize;
-    }
-
-    if (!Common::Is4KBAligned(size)) {
-        LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, size=0x{:016X}", size);
-        return ResultInvalidSize;
-    }
-
-    if (!IsValidAddressRange(addr, size)) {
-        LOG_ERROR(Kernel_SVC, "Region is not a valid address range, addr=0x{:016X}, size=0x{:016X}",
-                  addr, size);
-        return ResultInvalidCurrentMemory;
-    }
-
-    const auto permission_type = static_cast<MemoryPermission>(permissions);
-    if ((permission_type | MemoryPermission::Write) != MemoryPermission::ReadWrite) {
-        LOG_ERROR(Kernel_SVC, "Expected Read or ReadWrite permission but got permissions=0x{:08X}",
-                  permissions);
-        return ResultInvalidMemoryPermissions;
-    }
-
-    auto* const current_process{system.Kernel().CurrentProcess()};
-    auto& page_table{current_process->PageTable()};
-
-    if (page_table.IsInvalidRegion(addr, size)) {
-        LOG_ERROR(Kernel_SVC,
-                  "Addr does not fit within the valid region, addr=0x{:016X}, "
-                  "size=0x{:016X}",
-                  addr, size);
-        return ResultInvalidMemoryRange;
-    }
-
-    if (page_table.IsInsideHeapRegion(addr, size)) {
-        LOG_ERROR(Kernel_SVC,
-                  "Addr does not fit within the heap region, addr=0x{:016X}, "
-                  "size=0x{:016X}",
-                  addr, size);
-        return ResultInvalidMemoryRange;
-    }
-
-    if (page_table.IsInsideAliasRegion(addr, size)) {
-        LOG_ERROR(Kernel_SVC,
-                  "Address does not fit within the map region, addr=0x{:016X}, "
-                  "size=0x{:016X}",
-                  addr, size);
-        return ResultInvalidMemoryRange;
-    }
-
-    auto shared_memory{current_process->GetHandleTable().Get<KSharedMemory>(shared_memory_handle)};
-    if (!shared_memory) {
-        LOG_ERROR(Kernel_SVC, "Shared memory does not exist, shared_memory_handle=0x{:08X}",
-                  shared_memory_handle);
-        return ResultInvalidHandle;
-    }
-
-    return shared_memory->Map(*current_process, addr, size,
-                              static_cast<KMemoryPermission>(permission_type));
 }
 
-static ResultCode MapSharedMemory32(Core::System& system, Handle shared_memory_handle, u32 addr,
-                                    u32 size, u32 permissions) {
-    return MapSharedMemory(system, shared_memory_handle, addr, size, permissions);
+constexpr bool IsValidRemoteSharedMemoryPermission(Svc::MemoryPermission perm) {
+    return IsValidSharedMemoryPermission(perm) || perm == Svc::MemoryPermission::DontCare;
+}
+
+static ResultCode MapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address,
+                                  u64 size, Svc::MemoryPermission map_perm) {
+    LOG_TRACE(Kernel_SVC,
+              "called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}",
+              shmem_handle, address, size, map_perm);
+
+    // Validate the address/size.
+    R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
+    R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
+    R_UNLESS(size > 0, ResultInvalidSize);
+    R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
+
+    // Validate the permission.
+    R_UNLESS(IsValidSharedMemoryPermission(map_perm), ResultInvalidNewMemoryPermission);
+
+    // Get the current process.
+    auto& process = *system.Kernel().CurrentProcess();
+    auto& page_table = process.PageTable();
+
+    // Get the shared memory.
+    KScopedAutoObject shmem = process.GetHandleTable().GetObject<KSharedMemory>(shmem_handle);
+    R_UNLESS(shmem.IsNotNull(), ResultInvalidHandle);
+
+    // Verify that the mapping is in range.
+    R_UNLESS(page_table.CanContain(address, size, KMemoryState::Shared), ResultInvalidMemoryRegion);
+
+    // Add the shared memory to the process.
+    R_TRY(process.AddSharedMemory(shmem.GetPointerUnsafe(), address, size));
+
+    // Ensure that we clean up the shared memory if we fail to map it.
+    auto guard =
+        SCOPE_GUARD({ process.RemoveSharedMemory(shmem.GetPointerUnsafe(), address, size); });
+
+    // Map the shared memory.
+    R_TRY(shmem->Map(process, address, size, map_perm));
+
+    // We succeeded.
+    guard.Cancel();
+    return RESULT_SUCCESS;
+}
+
+static ResultCode MapSharedMemory32(Core::System& system, Handle shmem_handle, u32 address,
+                                    u32 size, Svc::MemoryPermission map_perm) {
+    return MapSharedMemory(system, shmem_handle, address, size, map_perm);
+}
+
+static ResultCode UnmapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address,
+                                    u64 size) {
+    // Validate the address/size.
+    R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
+    R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
+    R_UNLESS(size > 0, ResultInvalidSize);
+    R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
+
+    // Get the current process.
+    auto& process = *system.Kernel().CurrentProcess();
+    auto& page_table = process.PageTable();
+
+    // Get the shared memory.
+    KScopedAutoObject shmem = process.GetHandleTable().GetObject<KSharedMemory>(shmem_handle);
+    R_UNLESS(shmem.IsNotNull(), ResultInvalidHandle);
+
+    // Verify that the mapping is in range.
+    R_UNLESS(page_table.CanContain(address, size, KMemoryState::Shared), ResultInvalidMemoryRegion);
+
+    // Unmap the shared memory.
+    R_TRY(shmem->Unmap(process, address, size));
+
+    // Remove the shared memory from the process.
+    process.RemoveSharedMemory(shmem.GetPointerUnsafe(), address, size);
+
+    return RESULT_SUCCESS;
+}
+
+static ResultCode UnmapSharedMemory32(Core::System& system, Handle shmem_handle, u32 address,
+                                      u32 size) {
+    return UnmapSharedMemory(system, shmem_handle, address, size);
 }
 
 static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_address,
@@ -1287,8 +1255,8 @@ static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_add
     std::lock_guard lock{HLE::g_hle_lock};
     LOG_TRACE(Kernel_SVC, "called process=0x{:08X} address={:X}", process_handle, address);
     const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
-    std::shared_ptr<Process> process = handle_table.Get<Process>(process_handle);
-    if (!process) {
+    KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle);
+    if (process.IsNull()) {
         LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}",
                   process_handle);
         return ResultInvalidHandle;
@@ -1369,8 +1337,8 @@ static ResultCode MapProcessCodeMemory(Core::System& system, Handle process_hand
     }
 
     const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
-    auto process = handle_table.Get<Process>(process_handle);
-    if (!process) {
+    KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle);
+    if (process.IsNull()) {
         LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).",
                   process_handle);
         return ResultInvalidHandle;
@@ -1390,7 +1358,7 @@ static ResultCode MapProcessCodeMemory(Core::System& system, Handle process_hand
                   "Destination address range is not within the ASLR region (dst_address=0x{:016X}, "
                   "size=0x{:016X}).",
                   dst_address, size);
-        return ResultInvalidMemoryRange;
+        return ResultInvalidMemoryRegion;
     }
 
     return page_table.MapProcessCodeMemory(dst_address, src_address, size);
@@ -1437,8 +1405,8 @@ static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_ha
     }
 
     const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
-    auto process = handle_table.Get<Process>(process_handle);
-    if (!process) {
+    KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle);
+    if (process.IsNull()) {
         LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).",
                   process_handle);
         return ResultInvalidHandle;
@@ -1458,7 +1426,7 @@ static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_ha
                   "Destination address range is not within the ASLR region (dst_address=0x{:016X}, "
                   "size=0x{:016X}).",
                   dst_address, size);
-        return ResultInvalidMemoryRange;
+        return ResultInvalidMemoryRegion;
     }
 
     return page_table.UnmapProcessCodeMemory(dst_address, src_address, size);
@@ -1483,7 +1451,7 @@ static void ExitProcess32(Core::System& system) {
     ExitProcess(system);
 }
 
-static constexpr bool IsValidCoreId(int32_t core_id) {
+static constexpr bool IsValidVirtualCoreId(int32_t core_id) {
     return (0 <= core_id && core_id < static_cast<int32_t>(Core::Hardware::NUM_CPU_CORES));
 }
 
@@ -1503,7 +1471,7 @@ static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr e
     }
 
     // Validate arguments.
-    if (!IsValidCoreId(core_id)) {
+    if (!IsValidVirtualCoreId(core_id)) {
         LOG_ERROR(Kernel_SVC, "Invalid Core ID specified (id={})", core_id);
         return ResultInvalidCoreId;
     }
@@ -1521,35 +1489,42 @@ static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr e
         return ResultInvalidPriority;
     }
 
+    // Reserve a new thread from the process resource limit (waiting up to 100ms).
     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 ResultResourceLimitedExceeded;
+        return ResultLimitReached;
     }
 
-    std::shared_ptr<KThread> thread;
+    // Create the thread.
+    KThread* thread = KThread::Create(kernel);
+    if (!thread) {
+        LOG_ERROR(Kernel_SVC, "Unable to create new threads. Thread creation limit reached.");
+        return ResultOutOfResource;
+    }
+    SCOPE_EXIT({ thread->Close(); });
+
+    // Initialize the thread.
     {
         KScopedLightLock lk{process.GetStateLock()};
-        CASCADE_RESULT(thread,
-                       KThread::CreateUserThread(system, ThreadType::User, "", entry_point,
-                                                 priority, arg, core_id, stack_bottom, &process));
+        R_TRY(KThread::InitializeUserThread(system, thread, entry_point, arg, stack_bottom,
+                                            priority, core_id, &process));
     }
 
-    const auto new_thread_handle = process.GetHandleTable().Create(thread);
-    if (new_thread_handle.Failed()) {
-        LOG_ERROR(Kernel_SVC, "Failed to create handle with error=0x{:X}",
-                  new_thread_handle.Code().raw);
-        return new_thread_handle.Code();
-    }
-    *out_handle = *new_thread_handle;
-
     // Set the thread name for debugging purposes.
-    thread->SetName(
-        fmt::format("thread[entry_point={:X}, handle={:X}]", entry_point, *new_thread_handle));
+    thread->SetName(fmt::format("thread[entry_point={:X}, handle={:X}]", entry_point, *out_handle));
+
+    // Commit the thread reservation.
     thread_reservation.Commit();
 
+    // Register the new thread.
+    KThread::Register(kernel, thread);
+
+    // Add the thread to the handle table.
+    R_TRY(process.GetHandleTable().Add(out_handle, thread));
+
     return RESULT_SUCCESS;
 }
 
@@ -1563,21 +1538,15 @@ static ResultCode StartThread(Core::System& system, Handle thread_handle) {
     LOG_DEBUG(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
 
     // Get the thread from its handle.
-    const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
-    const std::shared_ptr<KThread> thread = handle_table.Get<KThread>(thread_handle);
-    if (!thread) {
-        LOG_ERROR(Kernel_SVC, "Invalid thread handle provided (handle={:08X})", thread_handle);
-        return ResultInvalidHandle;
-    }
+    KScopedAutoObject thread =
+        system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle);
+    R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
 
     // Try to start the thread.
-    const auto run_result = thread->Run();
-    if (run_result.IsError()) {
-        LOG_ERROR(Kernel_SVC,
-                  "Unable to successfuly start thread (thread handle={:08X}, result={})",
-                  thread_handle, run_result.raw);
-        return run_result;
-    }
+    R_TRY(thread->Run());
+
+    // If we succeeded, persist a reference to the thread.
+    thread->Open();
 
     return RESULT_SUCCESS;
 }
@@ -1591,7 +1560,7 @@ static void ExitThread(Core::System& system) {
     LOG_DEBUG(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC());
 
     auto* const current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread();
-    system.GlobalSchedulerContext().RemoveThread(SharedFrom(current_thread));
+    system.GlobalSchedulerContext().RemoveThread(current_thread);
     current_thread->Exit();
 }
 
@@ -1824,8 +1793,11 @@ static void GetSystemTick32(Core::System& system, u32* time_low, u32* time_high)
 static ResultCode CloseHandle(Core::System& system, Handle handle) {
     LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle);
 
-    auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
-    return handle_table.Close(handle);
+    // Remove the handle.
+    R_UNLESS(system.Kernel().CurrentProcess()->GetHandleTable().Remove(handle),
+             ResultInvalidHandle);
+
+    return RESULT_SUCCESS;
 }
 
 static ResultCode CloseHandle32(Core::System& system, Handle handle) {
@@ -1841,16 +1813,16 @@ static ResultCode ResetSignal(Core::System& system, Handle handle) {
 
     // Try to reset as readable event.
     {
-        auto readable_event = handle_table.Get<KReadableEvent>(handle);
-        if (readable_event) {
+        KScopedAutoObject readable_event = handle_table.GetObject<KReadableEvent>(handle);
+        if (readable_event.IsNotNull()) {
             return readable_event->Reset();
         }
     }
 
     // Try to reset as process.
     {
-        auto process = handle_table.Get<Process>(handle);
-        if (process) {
+        KScopedAutoObject process = handle_table.GetObject<KProcess>(handle);
+        if (process.IsNotNull()) {
             return process->Reset();
         }
     }
@@ -1864,65 +1836,68 @@ static ResultCode ResetSignal32(Core::System& system, Handle handle) {
     return ResetSignal(system, handle);
 }
 
+static constexpr bool IsValidTransferMemoryPermission(MemoryPermission perm) {
+    switch (perm) {
+    case MemoryPermission::None:
+    case MemoryPermission::Read:
+    case MemoryPermission::ReadWrite:
+        return true;
+    default:
+        return false;
+    }
+}
+
 /// Creates a TransferMemory object
-static ResultCode CreateTransferMemory(Core::System& system, Handle* handle, VAddr addr, u64 size,
-                                       u32 permissions) {
-    std::lock_guard lock{HLE::g_hle_lock};
-    LOG_DEBUG(Kernel_SVC, "called addr=0x{:X}, size=0x{:X}, perms=0x{:08X}", addr, size,
-              permissions);
-
-    if (!Common::Is4KBAligned(addr)) {
-        LOG_ERROR(Kernel_SVC, "Address ({:016X}) is not page aligned!", addr);
-        return ResultInvalidAddress;
-    }
-
-    if (!Common::Is4KBAligned(size) || size == 0) {
-        LOG_ERROR(Kernel_SVC, "Size ({:016X}) is not page aligned or equal to zero!", size);
-        return ResultInvalidAddress;
-    }
-
-    if (!IsValidAddressRange(addr, size)) {
-        LOG_ERROR(Kernel_SVC, "Address and size cause overflow! (address={:016X}, size={:016X})",
-                  addr, size);
-        return ResultInvalidCurrentMemory;
-    }
-
-    const auto perms{static_cast<MemoryPermission>(permissions)};
-    if (perms > MemoryPermission::ReadWrite || perms == MemoryPermission::Write) {
-        LOG_ERROR(Kernel_SVC, "Invalid memory permissions for transfer memory! (perms={:08X})",
-                  permissions);
-        return ResultInvalidMemoryPermissions;
-    }
-
+static ResultCode CreateTransferMemory(Core::System& system, Handle* out, VAddr address, u64 size,
+                                       MemoryPermission map_perm) {
     auto& kernel = system.Kernel();
+
+    // Validate the size.
+    R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
+    R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
+    R_UNLESS(size > 0, ResultInvalidSize);
+    R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
+
+    // Validate the permissions.
+    R_UNLESS(IsValidTransferMemoryPermission(map_perm), ResultInvalidNewMemoryPermission);
+
+    // Get the current process and handle table.
+    auto& process = *kernel.CurrentProcess();
+    auto& handle_table = process.GetHandleTable();
+
     // 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 ResultResourceLimitedExceeded;
-    }
-    auto transfer_mem_handle = TransferMemory::Create(kernel, system.Memory(), addr, size,
-                                                      static_cast<KMemoryPermission>(perms));
+    R_UNLESS(trmem_reservation.Succeeded(), ResultLimitReached);
 
-    if (const auto reserve_result{transfer_mem_handle->Reserve()}; reserve_result.IsError()) {
-        return reserve_result;
-    }
+    // Create the transfer memory.
+    KTransferMemory* trmem = KTransferMemory::Create(kernel);
+    R_UNLESS(trmem != nullptr, ResultOutOfResource);
 
-    auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
-    const auto result{handle_table.Create(std::move(transfer_mem_handle))};
-    if (result.Failed()) {
-        return result.Code();
-    }
+    // Ensure the only reference is in the handle table when we're done.
+    SCOPE_EXIT({ trmem->Close(); });
+
+    // Ensure that the region is in range.
+    R_UNLESS(process.PageTable().Contains(address, size), ResultInvalidCurrentMemory);
+
+    // Initialize the transfer memory.
+    R_TRY(trmem->Initialize(address, size, map_perm));
+
+    // Commit the reservation.
     trmem_reservation.Commit();
 
-    *handle = *result;
+    // Register the transfer memory.
+    KTransferMemory::Register(kernel, trmem);
+
+    // Add the transfer memory to the handle table.
+    R_TRY(handle_table.Add(out, trmem));
+
     return RESULT_SUCCESS;
 }
 
-static ResultCode CreateTransferMemory32(Core::System& system, Handle* handle, u32 addr, u32 size,
-                                         u32 permissions) {
-    return CreateTransferMemory(system, handle, addr, size, permissions);
+static ResultCode CreateTransferMemory32(Core::System& system, Handle* out, u32 address, u32 size,
+                                         MemoryPermission map_perm) {
+    return CreateTransferMemory(system, out, address, size, map_perm);
 }
 
 static ResultCode GetThreadCoreMask(Core::System& system, Handle thread_handle, s32* out_core_id,
@@ -1930,19 +1905,12 @@ static ResultCode GetThreadCoreMask(Core::System& system, Handle thread_handle,
     LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle);
 
     // Get the thread from its handle.
-    const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
-    const std::shared_ptr<KThread> thread = handle_table.Get<KThread>(thread_handle);
-    if (!thread) {
-        LOG_ERROR(Kernel_SVC, "Invalid thread handle specified (handle={:08X})", thread_handle);
-        return ResultInvalidHandle;
-    }
+    KScopedAutoObject thread =
+        system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle);
+    R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
 
     // Get the core mask.
-    const auto result = thread->GetCoreMask(out_core_id, out_affinity_mask);
-    if (result.IsError()) {
-        LOG_ERROR(Kernel_SVC, "Unable to successfully retrieve core mask (result={})", result.raw);
-        return result;
-    }
+    R_TRY(thread->GetCoreMask(out_core_id, out_affinity_mask));
 
     return RESULT_SUCCESS;
 }
@@ -1958,58 +1926,33 @@ static ResultCode GetThreadCoreMask32(Core::System& system, Handle thread_handle
 
 static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle, s32 core_id,
                                     u64 affinity_mask) {
-    LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, core_id=0x{:X}, affinity_mask=0x{:016X}",
-              thread_handle, core_id, affinity_mask);
-
-    const auto& current_process = *system.Kernel().CurrentProcess();
-
     // Determine the core id/affinity mask.
-    if (core_id == Svc::IdealCoreUseProcessValue) {
-        core_id = current_process.GetIdealCoreId();
+    if (core_id == IdealCoreUseProcessValue) {
+        core_id = system.Kernel().CurrentProcess()->GetIdealCoreId();
         affinity_mask = (1ULL << core_id);
     } else {
         // Validate the affinity mask.
-        const u64 process_core_mask = current_process.GetCoreMask();
-        if ((affinity_mask | process_core_mask) != process_core_mask) {
-            LOG_ERROR(Kernel_SVC,
-                      "Affinity mask does match the process core mask (affinity mask={:016X}, core "
-                      "mask={:016X})",
-                      affinity_mask, process_core_mask);
-            return ResultInvalidCoreId;
-        }
-        if (affinity_mask == 0) {
-            LOG_ERROR(Kernel_SVC, "Affinity mask is zero.");
-            return ResultInvalidCombination;
-        }
+        const u64 process_core_mask = system.Kernel().CurrentProcess()->GetCoreMask();
+        R_UNLESS((affinity_mask | process_core_mask) == process_core_mask, ResultInvalidCoreId);
+        R_UNLESS(affinity_mask != 0, ResultInvalidCombination);
 
         // Validate the core id.
-        if (IsValidCoreId(core_id)) {
-            if (((1ULL << core_id) & affinity_mask) == 0) {
-                LOG_ERROR(Kernel_SVC, "Invalid core ID (ID={})", core_id);
-                return ResultInvalidCombination;
-            }
+        if (IsValidVirtualCoreId(core_id)) {
+            R_UNLESS(((1ULL << core_id) & affinity_mask) != 0, ResultInvalidCombination);
         } else {
-            if (core_id != IdealCoreNoUpdate && core_id != IdealCoreDontCare) {
-                LOG_ERROR(Kernel_SVC, "Invalid core ID (ID={})", core_id);
-                return ResultInvalidCoreId;
-            }
+            R_UNLESS(core_id == IdealCoreNoUpdate || core_id == IdealCoreDontCare,
+                     ResultInvalidCoreId);
         }
     }
 
     // Get the thread from its handle.
-    const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
-    const std::shared_ptr<KThread> thread = handle_table.Get<KThread>(thread_handle);
-    if (!thread) {
-        LOG_ERROR(Kernel_SVC, "Invalid thread handle (handle={:08X})", thread_handle);
-        return ResultInvalidHandle;
-    }
+    KScopedAutoObject thread =
+        system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle);
+    R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
 
     // Set the core mask.
-    const auto set_result = thread->SetCoreMask(core_id, affinity_mask);
-    if (set_result.IsError()) {
-        LOG_ERROR(Kernel_SVC, "Unable to successfully set core mask (result={})", set_result.raw);
-        return set_result;
-    }
+    R_TRY(thread->SetCoreMask(core_id, affinity_mask));
+
     return RESULT_SUCCESS;
 }
 
@@ -2022,27 +1965,12 @@ 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 = 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 ResultResourceLimitedExceeded;
-    }
+    const KHandleTable& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
 
     // Get the writable event.
-    auto writable_event = handle_table.Get<KWritableEvent>(event_handle);
-    if (!writable_event) {
-        LOG_ERROR(Kernel_SVC, "Invalid event handle provided (handle={:08X})", event_handle);
-        return ResultInvalidHandle;
-    }
-
-    // Commit the successfuly reservation.
-    event_reservation.Commit();
+    KScopedAutoObject writable_event = handle_table.GetObject<KWritableEvent>(event_handle);
+    R_UNLESS(writable_event.IsNotNull(), ResultInvalidHandle);
 
     return writable_event->Signal();
 }
@@ -2059,16 +1987,16 @@ static ResultCode ClearEvent(Core::System& system, Handle event_handle) {
 
     // Try to clear the writable event.
     {
-        auto writable_event = handle_table.Get<KWritableEvent>(event_handle);
-        if (writable_event) {
+        KScopedAutoObject writable_event = handle_table.GetObject<KWritableEvent>(event_handle);
+        if (writable_event.IsNotNull()) {
             return writable_event->Clear();
         }
     }
 
     // Try to clear the readable event.
     {
-        auto readable_event = handle_table.Get<KReadableEvent>(event_handle);
-        if (readable_event) {
+        KScopedAutoObject readable_event = handle_table.GetObject<KReadableEvent>(event_handle);
+        if (readable_event.IsNotNull()) {
             return readable_event->Clear();
         }
     }
@@ -2087,34 +2015,40 @@ static ResultCode CreateEvent(Core::System& system, Handle* out_write, Handle* o
 
     // Get the kernel reference and handle table.
     auto& kernel = system.Kernel();
-    HandleTable& handle_table = kernel.CurrentProcess()->GetHandleTable();
+    auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
+
+    // Reserve a new event from the process resource limit
+    KScopedResourceReservation event_reservation(kernel.CurrentProcess(),
+                                                 LimitableResource::Events);
+    R_UNLESS(event_reservation.Succeeded(), ResultLimitReached);
 
     // Create a new event.
-    const auto event = KEvent::Create(kernel, "CreateEvent");
-    if (!event) {
-        LOG_ERROR(Kernel_SVC, "Unable to create new events. Event creation limit reached.");
-        return ResultOutOfResource;
-    }
+    KEvent* event = KEvent::Create(kernel);
+    R_UNLESS(event != nullptr, ResultOutOfResource);
 
     // Initialize the event.
-    event->Initialize();
+    event->Initialize("CreateEvent");
+
+    // Commit the thread reservation.
+    event_reservation.Commit();
+
+    // Ensure that we clean up the event (and its only references are handle table) on function end.
+    SCOPE_EXIT({
+        event->GetWritableEvent().Close();
+        event->GetReadableEvent().Close();
+    });
+
+    // Register the event.
+    KEvent::Register(kernel, event);
 
     // Add the writable event to the handle table.
-    const auto write_create_result = handle_table.Create(event->GetWritableEvent());
-    if (write_create_result.Failed()) {
-        return write_create_result.Code();
-    }
-    *out_write = *write_create_result;
+    R_TRY(handle_table.Add(out_write, std::addressof(event->GetWritableEvent())));
 
     // Add the writable event to the handle table.
-    auto handle_guard = SCOPE_GUARD({ handle_table.Close(*write_create_result); });
+    auto handle_guard = SCOPE_GUARD({ handle_table.Remove(*out_write); });
 
     // Add the readable event to the handle table.
-    const auto read_create_result = handle_table.Create(event->GetReadableEvent());
-    if (read_create_result.Failed()) {
-        return read_create_result.Code();
-    }
-    *out_read = *read_create_result;
+    R_TRY(handle_table.Add(out_read, std::addressof(event->GetReadableEvent())));
 
     // We succeeded.
     handle_guard.Cancel();
@@ -2134,8 +2068,8 @@ static ResultCode GetProcessInfo(Core::System& system, u64* out, Handle process_
     };
 
     const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
-    const auto process = handle_table.Get<Process>(process_handle);
-    if (!process) {
+    KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle);
+    if (process.IsNull()) {
         LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}",
                   process_handle);
         return ResultInvalidHandle;
@@ -2152,83 +2086,86 @@ static ResultCode GetProcessInfo(Core::System& system, u64* out, Handle process_
 }
 
 static ResultCode CreateResourceLimit(Core::System& system, Handle* out_handle) {
-    std::lock_guard lock{HLE::g_hle_lock};
     LOG_DEBUG(Kernel_SVC, "called");
 
+    // Create a new resource limit.
     auto& kernel = system.Kernel();
-    auto resource_limit = std::make_shared<KResourceLimit>(kernel, system.CoreTiming());
+    KResourceLimit* resource_limit = KResourceLimit::Create(kernel);
+    R_UNLESS(resource_limit != nullptr, ResultOutOfResource);
 
-    auto* const current_process = kernel.CurrentProcess();
-    ASSERT(current_process != nullptr);
+    // Ensure we don't leak a reference to the limit.
+    SCOPE_EXIT({ resource_limit->Close(); });
 
-    const auto handle = current_process->GetHandleTable().Create(std::move(resource_limit));
-    if (handle.Failed()) {
-        return handle.Code();
-    }
+    // Initialize the resource limit.
+    resource_limit->Initialize(&system.CoreTiming());
+
+    // Register the limit.
+    KResourceLimit::Register(kernel, resource_limit);
+
+    // Add the limit to the handle table.
+    R_TRY(kernel.CurrentProcess()->GetHandleTable().Add(out_handle, resource_limit));
 
-    *out_handle = *handle;
     return RESULT_SUCCESS;
 }
 
-static ResultCode GetResourceLimitLimitValue(Core::System& system, u64* out_value,
-                                             Handle resource_limit, u32 resource_type) {
-    LOG_DEBUG(Kernel_SVC, "called. Handle={:08X}, Resource type={}", resource_limit, resource_type);
+static ResultCode GetResourceLimitLimitValue(Core::System& system, u64* out_limit_value,
+                                             Handle resource_limit_handle,
+                                             LimitableResource which) {
+    LOG_DEBUG(Kernel_SVC, "called, resource_limit_handle={:08X}, which={}", resource_limit_handle,
+              which);
 
-    const auto limit_value = RetrieveResourceLimitValue(system, resource_limit, resource_type,
-                                                        ResourceLimitValueType::LimitValue);
-    if (limit_value.Failed()) {
-        return limit_value.Code();
-    }
+    // Validate the resource.
+    R_UNLESS(IsValidResourceType(which), ResultInvalidEnumValue);
+
+    // Get the resource limit.
+    auto& kernel = system.Kernel();
+    KScopedAutoObject resource_limit =
+        kernel.CurrentProcess()->GetHandleTable().GetObject<KResourceLimit>(resource_limit_handle);
+    R_UNLESS(resource_limit.IsNotNull(), ResultInvalidHandle);
+
+    // Get the limit value.
+    *out_limit_value = resource_limit->GetLimitValue(which);
 
-    *out_value = static_cast<u64>(*limit_value);
     return RESULT_SUCCESS;
 }
 
-static ResultCode GetResourceLimitCurrentValue(Core::System& system, u64* out_value,
-                                               Handle resource_limit, u32 resource_type) {
-    LOG_DEBUG(Kernel_SVC, "called. Handle={:08X}, Resource type={}", resource_limit, resource_type);
+static ResultCode GetResourceLimitCurrentValue(Core::System& system, u64* out_current_value,
+                                               Handle resource_limit_handle,
+                                               LimitableResource which) {
+    LOG_DEBUG(Kernel_SVC, "called, resource_limit_handle={:08X}, which={}", resource_limit_handle,
+              which);
 
-    const auto current_value = RetrieveResourceLimitValue(system, resource_limit, resource_type,
-                                                          ResourceLimitValueType::CurrentValue);
-    if (current_value.Failed()) {
-        return current_value.Code();
-    }
+    // Validate the resource.
+    R_UNLESS(IsValidResourceType(which), ResultInvalidEnumValue);
+
+    // Get the resource limit.
+    auto& kernel = system.Kernel();
+    KScopedAutoObject resource_limit =
+        kernel.CurrentProcess()->GetHandleTable().GetObject<KResourceLimit>(resource_limit_handle);
+    R_UNLESS(resource_limit.IsNotNull(), ResultInvalidHandle);
+
+    // Get the current value.
+    *out_current_value = resource_limit->GetCurrentValue(which);
 
-    *out_value = static_cast<u64>(*current_value);
     return RESULT_SUCCESS;
 }
 
-static ResultCode SetResourceLimitLimitValue(Core::System& system, Handle resource_limit,
-                                             u32 resource_type, u64 value) {
-    LOG_DEBUG(Kernel_SVC, "called. Handle={:08X}, Resource type={}, Value={}", resource_limit,
-              resource_type, value);
+static ResultCode SetResourceLimitLimitValue(Core::System& system, Handle resource_limit_handle,
+                                             LimitableResource which, u64 limit_value) {
+    LOG_DEBUG(Kernel_SVC, "called, resource_limit_handle={:08X}, which={}, limit_value={}",
+              resource_limit_handle, which, limit_value);
 
-    const auto type = static_cast<LimitableResource>(resource_type);
-    if (!IsValidResourceType(type)) {
-        LOG_ERROR(Kernel_SVC, "Invalid resource limit type: '{}'", resource_type);
-        return ResultInvalidEnumValue;
-    }
+    // Validate the resource.
+    R_UNLESS(IsValidResourceType(which), ResultInvalidEnumValue);
 
-    auto* const current_process = system.Kernel().CurrentProcess();
-    ASSERT(current_process != nullptr);
+    // Get the resource limit.
+    auto& kernel = system.Kernel();
+    KScopedAutoObject resource_limit =
+        kernel.CurrentProcess()->GetHandleTable().GetObject<KResourceLimit>(resource_limit_handle);
+    R_UNLESS(resource_limit.IsNotNull(), ResultInvalidHandle);
 
-    auto resource_limit_object =
-        current_process->GetHandleTable().Get<KResourceLimit>(resource_limit);
-    if (!resource_limit_object) {
-        LOG_ERROR(Kernel_SVC, "Handle to non-existent resource limit instance used. Handle={:08X}",
-                  resource_limit);
-        return ResultInvalidHandle;
-    }
-
-    const auto set_result = resource_limit_object->SetLimitValue(type, static_cast<s64>(value));
-    if (set_result.IsError()) {
-        LOG_ERROR(Kernel_SVC,
-                  "Attempted to lower resource limit ({}) for category '{}' below its current "
-                  "value ({})",
-                  resource_limit_object->GetLimitValue(type), resource_type,
-                  resource_limit_object->GetCurrentValue(type));
-        return set_result;
-    }
+    // Set the limit value.
+    R_TRY(resource_limit->SetLimitValue(which, limit_value));
 
     return RESULT_SUCCESS;
 }
@@ -2351,7 +2288,7 @@ static const FunctionDef SVC_Table_32[] = {
     {0x11, SvcWrap32<SignalEvent32>, "SignalEvent32"},
     {0x12, SvcWrap32<ClearEvent32>, "ClearEvent32"},
     {0x13, SvcWrap32<MapSharedMemory32>, "MapSharedMemory32"},
-    {0x14, nullptr, "UnmapSharedMemory32"},
+    {0x14, SvcWrap32<UnmapSharedMemory32>, "UnmapSharedMemory32"},
     {0x15, SvcWrap32<CreateTransferMemory32>, "CreateTransferMemory32"},
     {0x16, SvcWrap32<CloseHandle32>, "CloseHandle32"},
     {0x17, SvcWrap32<ResetSignal32>, "ResetSignal32"},
@@ -2546,7 +2483,7 @@ static const FunctionDef SVC_Table_64[] = {
     {0x11, SvcWrap64<SignalEvent>, "SignalEvent"},
     {0x12, SvcWrap64<ClearEvent>, "ClearEvent"},
     {0x13, SvcWrap64<MapSharedMemory>, "MapSharedMemory"},
-    {0x14, nullptr, "UnmapSharedMemory"},
+    {0x14, SvcWrap64<UnmapSharedMemory>, "UnmapSharedMemory"},
     {0x15, SvcWrap64<CreateTransferMemory>, "CreateTransferMemory"},
     {0x16, SvcWrap64<CloseHandle>, "CloseHandle"},
     {0x17, SvcWrap64<ResetSignal>, "ResetSignal"},
diff --git a/src/core/hle/kernel/svc_common.h b/src/core/hle/kernel/svc_common.h
index 4af049551..60ea2c405 100644
--- a/src/core/hle/kernel/svc_common.h
+++ b/src/core/hle/kernel/svc_common.h
@@ -6,9 +6,24 @@
 
 #include "common/common_types.h"
 
+namespace Kernel {
+using Handle = u32;
+}
+
 namespace Kernel::Svc {
 
 constexpr s32 ArgumentHandleCountMax = 0x40;
 constexpr u32 HandleWaitMask{1u << 30};
 
+constexpr inline Handle InvalidHandle = Handle(0);
+
+enum PseudoHandle : Handle {
+    CurrentThread = 0xFFFF8000,
+    CurrentProcess = 0xFFFF8001,
+};
+
+constexpr bool IsPseudoHandle(Handle handle) {
+    return handle == PseudoHandle::CurrentProcess || handle == PseudoHandle::CurrentThread;
+}
+
 } // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc_results.h b/src/core/hle/kernel/svc_results.h
index a26d9f2c9..53a940723 100644
--- a/src/core/hle/kernel/svc_results.h
+++ b/src/core/hle/kernel/svc_results.h
@@ -10,18 +10,18 @@ namespace Kernel {
 
 // Confirmed Switch kernel error codes
 
-constexpr ResultCode ResultMaxConnectionsReached{ErrorModule::Kernel, 7};
-constexpr ResultCode ResultInvalidCapabilityDescriptor{ErrorModule::Kernel, 14};
+constexpr ResultCode ResultOutOfSessions{ErrorModule::Kernel, 7};
+constexpr ResultCode ResultInvalidArgument{ErrorModule::Kernel, 14};
 constexpr ResultCode ResultNoSynchronizationObject{ErrorModule::Kernel, 57};
 constexpr ResultCode ResultTerminationRequested{ErrorModule::Kernel, 59};
 constexpr ResultCode ResultInvalidSize{ErrorModule::Kernel, 101};
 constexpr ResultCode ResultInvalidAddress{ErrorModule::Kernel, 102};
 constexpr ResultCode ResultOutOfResource{ErrorModule::Kernel, 103};
 constexpr ResultCode ResultOutOfMemory{ErrorModule::Kernel, 104};
-constexpr ResultCode ResultHandleTableFull{ErrorModule::Kernel, 105};
+constexpr ResultCode ResultOutOfHandles{ErrorModule::Kernel, 105};
 constexpr ResultCode ResultInvalidCurrentMemory{ErrorModule::Kernel, 106};
-constexpr ResultCode ResultInvalidMemoryPermissions{ErrorModule::Kernel, 108};
-constexpr ResultCode ResultInvalidMemoryRange{ErrorModule::Kernel, 110};
+constexpr ResultCode ResultInvalidNewMemoryPermission{ErrorModule::Kernel, 108};
+constexpr ResultCode ResultInvalidMemoryRegion{ErrorModule::Kernel, 110};
 constexpr ResultCode ResultInvalidPriority{ErrorModule::Kernel, 112};
 constexpr ResultCode ResultInvalidCoreId{ErrorModule::Kernel, 113};
 constexpr ResultCode ResultInvalidHandle{ErrorModule::Kernel, 114};
@@ -33,9 +33,11 @@ constexpr ResultCode ResultOutOfRange{ErrorModule::Kernel, 119};
 constexpr ResultCode ResultInvalidEnumValue{ErrorModule::Kernel, 120};
 constexpr ResultCode ResultNotFound{ErrorModule::Kernel, 121};
 constexpr ResultCode ResultBusy{ErrorModule::Kernel, 122};
-constexpr ResultCode ResultSessionClosedByRemote{ErrorModule::Kernel, 123};
+constexpr ResultCode ResultSessionClosed{ErrorModule::Kernel, 123};
 constexpr ResultCode ResultInvalidState{ErrorModule::Kernel, 125};
-constexpr ResultCode ResultReservedValue{ErrorModule::Kernel, 126};
-constexpr ResultCode ResultResourceLimitedExceeded{ErrorModule::Kernel, 132};
+constexpr ResultCode ResultReservedUsed{ErrorModule::Kernel, 126};
+constexpr ResultCode ResultPortClosed{ErrorModule::Kernel, 131};
+constexpr ResultCode ResultLimitReached{ErrorModule::Kernel, 132};
+constexpr ResultCode ResultInvalidId{ErrorModule::Kernel, 519};
 
 } // namespace Kernel
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h
index 96afd544b..913b16494 100644
--- a/src/core/hle/kernel/svc_wrap.h
+++ b/src/core/hle/kernel/svc_wrap.h
@@ -154,15 +154,28 @@ void SvcWrap64(Core::System& system) {
     FuncReturn(system, retval);
 }
 
+// Used by GetResourceLimitLimitValue.
+template <ResultCode func(Core::System&, u64*, Handle, LimitableResource)>
+void SvcWrap64(Core::System& system) {
+    u64 param_1 = 0;
+    const u32 retval = func(system, &param_1, static_cast<Handle>(Param(system, 1)),
+                            static_cast<LimitableResource>(Param(system, 2)))
+                           .raw;
+
+    system.CurrentArmInterface().SetReg(1, param_1);
+    FuncReturn(system, retval);
+}
+
 template <ResultCode func(Core::System&, u32, u64)>
 void SvcWrap64(Core::System& system) {
     FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1)).raw);
 }
 
-template <ResultCode func(Core::System&, u32, u32, u64)>
+// Used by SetResourceLimitLimitValue
+template <ResultCode func(Core::System&, Handle, LimitableResource, u64)>
 void SvcWrap64(Core::System& system) {
-    FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)),
-                            static_cast<u32>(Param(system, 1)), Param(system, 2))
+    FuncReturn(system, func(system, static_cast<Handle>(Param(system, 0)),
+                            static_cast<LimitableResource>(Param(system, 1)), Param(system, 2))
                            .raw);
 }
 
@@ -219,10 +232,11 @@ void SvcWrap64(Core::System& system) {
         func(system, Param(system, 0), Param(system, 1), static_cast<u32>(Param(system, 2))).raw);
 }
 
-template <ResultCode func(Core::System&, u32, u64, u64, u32)>
+// Used by MapSharedMemory
+template <ResultCode func(Core::System&, Handle, u64, u64, Svc::MemoryPermission)>
 void SvcWrap64(Core::System& system) {
-    FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1),
-                            Param(system, 2), static_cast<u32>(Param(system, 3)))
+    FuncReturn(system, func(system, static_cast<Handle>(Param(system, 0)), Param(system, 1),
+                            Param(system, 2), static_cast<Svc::MemoryPermission>(Param(system, 3)))
                            .raw);
 }
 
@@ -252,11 +266,13 @@ void SvcWrap64(Core::System& system) {
                            .raw);
 }
 
-template <ResultCode func(Core::System&, u64*, u64, u64, u64)>
+// Used by GetInfo
+template <ResultCode func(Core::System&, u64*, u64, Handle, u64)>
 void SvcWrap64(Core::System& system) {
     u64 param_1 = 0;
-    const u32 retval =
-        func(system, &param_1, Param(system, 1), Param(system, 2), Param(system, 3)).raw;
+    const u32 retval = func(system, &param_1, Param(system, 1),
+                            static_cast<Handle>(Param(system, 2)), Param(system, 3))
+                           .raw;
 
     system.CurrentArmInterface().SetReg(1, param_1);
     FuncReturn(system, retval);
@@ -273,11 +289,12 @@ void SvcWrap64(Core::System& system) {
     FuncReturn(system, retval);
 }
 
-template <ResultCode func(Core::System&, u32*, u64, u64, u32)>
+// Used by CreateTransferMemory
+template <ResultCode func(Core::System&, Handle*, u64, u64, Svc::MemoryPermission)>
 void SvcWrap64(Core::System& system) {
     u32 param_1 = 0;
     const u32 retval = func(system, &param_1, Param(system, 1), Param(system, 2),
-                            static_cast<u32>(Param(system, 3)))
+                            static_cast<Svc::MemoryPermission>(Param(system, 3)))
                            .raw;
 
     system.CurrentArmInterface().SetReg(1, param_1);
@@ -537,6 +554,16 @@ void SvcWrap32(Core::System& system) {
     FuncReturn(system, retval);
 }
 
+// Used by MapSharedMemory32
+template <ResultCode func(Core::System&, Handle, u32, u32, Svc::MemoryPermission)>
+void SvcWrap32(Core::System& system) {
+    const u32 retval = func(system, static_cast<Handle>(Param(system, 0)),
+                            static_cast<u32>(Param(system, 1)), static_cast<u32>(Param(system, 2)),
+                            static_cast<Svc::MemoryPermission>(Param(system, 3)))
+                           .raw;
+    FuncReturn(system, retval);
+}
+
 // Used by SetThreadCoreMask32
 template <ResultCode func(Core::System&, Handle, s32, u32, u32)>
 void SvcWrap32(Core::System& system) {
@@ -586,11 +613,12 @@ void SvcWrap32(Core::System& system) {
 }
 
 // Used by CreateTransferMemory32
-template <ResultCode func(Core::System&, Handle*, u32, u32, u32)>
+template <ResultCode func(Core::System&, Handle*, u32, u32, Svc::MemoryPermission)>
 void SvcWrap32(Core::System& system) {
     Handle handle = 0;
-    const u32 retval =
-        func(system, &handle, Param32(system, 1), Param32(system, 2), Param32(system, 3)).raw;
+    const u32 retval = func(system, &handle, Param32(system, 1), Param32(system, 2),
+                            static_cast<Svc::MemoryPermission>(Param32(system, 3)))
+                           .raw;
     system.CurrentArmInterface().SetReg(1, handle);
     FuncReturn(system, retval);
 }
diff --git a/src/core/hle/kernel/time_manager.cpp b/src/core/hle/kernel/time_manager.cpp
index fd0630019..ae9b4be2f 100644
--- a/src/core/hle/kernel/time_manager.cpp
+++ b/src/core/hle/kernel/time_manager.cpp
@@ -6,7 +6,6 @@
 #include "core/core.h"
 #include "core/core_timing.h"
 #include "core/core_timing_util.h"
-#include "core/hle/kernel/handle_table.h"
 #include "core/hle/kernel/k_scheduler.h"
 #include "core/hle/kernel/k_thread.h"
 #include "core/hle/kernel/kernel.h"
@@ -15,16 +14,12 @@
 namespace Kernel {
 
 TimeManager::TimeManager(Core::System& system_) : system{system_} {
-    time_manager_event_type = Core::Timing::CreateEvent(
-        "Kernel::TimeManagerCallback",
-        [this](std::uintptr_t thread_handle, std::chrono::nanoseconds) {
-            std::shared_ptr<KThread> thread;
-            {
-                std::lock_guard lock{mutex};
-                thread = SharedFrom<KThread>(reinterpret_cast<KThread*>(thread_handle));
-            }
-            thread->Wakeup();
-        });
+    time_manager_event_type =
+        Core::Timing::CreateEvent("Kernel::TimeManagerCallback",
+                                  [this](std::uintptr_t thread_handle, std::chrono::nanoseconds) {
+                                      KThread* thread = reinterpret_cast<KThread*>(thread_handle);
+                                      thread->Wakeup();
+                                  });
 }
 
 void TimeManager::ScheduleTimeEvent(KThread* thread, s64 nanoseconds) {
diff --git a/src/core/hle/kernel/time_manager.h b/src/core/hle/kernel/time_manager.h
index 0d7f05f30..2d175a9c4 100644
--- a/src/core/hle/kernel/time_manager.h
+++ b/src/core/hle/kernel/time_manager.h
@@ -8,8 +8,6 @@
 #include <mutex>
 #include <unordered_map>
 
-#include "core/hle/kernel/object.h"
-
 namespace Core {
 class System;
 } // namespace Core
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index 5450dcf0f..49c09a570 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -16,8 +16,8 @@
 #include "core/file_sys/control_metadata.h"
 #include "core/file_sys/patch_manager.h"
 #include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/k_process.h"
 #include "core/hle/kernel/kernel.h"
-#include "core/hle/kernel/process.h"
 #include "core/hle/service/acc/acc.h"
 #include "core/hle/service/acc/acc_aa.h"
 #include "core/hle/service/acc/acc_su.h"
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 58c7f2930..408e441dc 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -15,11 +15,11 @@
 #include "core/file_sys/savedata_factory.h"
 #include "core/hle/ipc_helpers.h"
 #include "core/hle/kernel/k_event.h"
+#include "core/hle/kernel/k_process.h"
 #include "core/hle/kernel/k_readable_event.h"
+#include "core/hle/kernel/k_transfer_memory.h"
 #include "core/hle/kernel/k_writable_event.h"
 #include "core/hle/kernel/kernel.h"
-#include "core/hle/kernel/process.h"
-#include "core/hle/kernel/transfer_memory.h"
 #include "core/hle/service/acc/profile_manager.h"
 #include "core/hle/service/am/am.h"
 #include "core/hle/service/am/applet_ae.h"
@@ -42,6 +42,7 @@
 #include "core/hle/service/set/set.h"
 #include "core/hle/service/sm/sm.h"
 #include "core/hle/service/vi/vi.h"
+#include "core/memory.h"
 
 namespace Service::AM {
 
@@ -253,7 +254,8 @@ IDebugFunctions::IDebugFunctions(Core::System& system_)
 IDebugFunctions::~IDebugFunctions() = default;
 
 ISelfController::ISelfController(Core::System& system_, NVFlinger::NVFlinger& nvflinger_)
-    : ServiceFramework{system_, "ISelfController"}, nvflinger{nvflinger_} {
+    : ServiceFramework{system_, "ISelfController"}, nvflinger{nvflinger_},
+      launchable_event{system.Kernel()}, accumulated_suspended_tick_changed_event{system.Kernel()} {
     // clang-format off
     static const FunctionInfo functions[] = {
         {0, &ISelfController::Exit, "Exit"},
@@ -306,19 +308,20 @@ ISelfController::ISelfController(Core::System& system_, NVFlinger::NVFlinger& nv
 
     RegisterHandlers(functions);
 
-    auto& kernel = system.Kernel();
-    launchable_event = Kernel::KEvent::Create(kernel, "ISelfController:LaunchableEvent");
-    launchable_event->Initialize();
+    Kernel::KAutoObject::Create(std::addressof(launchable_event));
+
+    launchable_event.Initialize("ISelfController:LaunchableEvent");
 
     // This event is created by AM on the first time GetAccumulatedSuspendedTickChangedEvent() is
     // called. Yuzu can just create it unconditionally, since it doesn't need to support multiple
     // ISelfControllers. The event is signaled on creation, and on transition from suspended -> not
     // suspended if the event has previously been created by a call to
     // GetAccumulatedSuspendedTickChangedEvent.
-    accumulated_suspended_tick_changed_event =
-        Kernel::KEvent::Create(kernel, "ISelfController:AccumulatedSuspendedTickChangedEvent");
-    accumulated_suspended_tick_changed_event->Initialize();
-    accumulated_suspended_tick_changed_event->GetWritableEvent()->Signal();
+
+    Kernel::KAutoObject::Create(std::addressof(accumulated_suspended_tick_changed_event));
+    accumulated_suspended_tick_changed_event.Initialize(
+        "ISelfController:AccumulatedSuspendedTickChangedEvent");
+    accumulated_suspended_tick_changed_event.GetWritableEvent().Signal();
 }
 
 ISelfController::~ISelfController() = default;
@@ -377,11 +380,11 @@ void ISelfController::LeaveFatalSection(Kernel::HLERequestContext& ctx) {
 void ISelfController::GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext& ctx) {
     LOG_WARNING(Service_AM, "(STUBBED) called");
 
-    launchable_event->GetWritableEvent()->Signal();
+    launchable_event.GetWritableEvent().Signal();
 
     IPC::ResponseBuilder rb{ctx, 2, 1};
     rb.Push(RESULT_SUCCESS);
-    rb.PushCopyObjects(launchable_event->GetReadableEvent());
+    rb.PushCopyObjects(launchable_event.GetReadableEvent());
 }
 
 void ISelfController::SetScreenShotPermission(Kernel::HLERequestContext& ctx) {
@@ -560,7 +563,7 @@ void ISelfController::GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequest
 
     IPC::ResponseBuilder rb{ctx, 2, 1};
     rb.Push(RESULT_SUCCESS);
-    rb.PushCopyObjects(accumulated_suspended_tick_changed_event->GetReadableEvent());
+    rb.PushCopyObjects(accumulated_suspended_tick_changed_event.GetReadableEvent());
 }
 
 void ISelfController::SetAlbumImageTakenNotificationEnabled(Kernel::HLERequestContext& ctx) {
@@ -578,39 +581,40 @@ void ISelfController::SetAlbumImageTakenNotificationEnabled(Kernel::HLERequestCo
     rb.Push(RESULT_SUCCESS);
 }
 
-AppletMessageQueue::AppletMessageQueue(Kernel::KernelCore& kernel) {
-    on_new_message = Kernel::KEvent::Create(kernel, "AMMessageQueue:OnMessageReceived");
-    on_new_message->Initialize();
-    on_operation_mode_changed =
-        Kernel::KEvent::Create(kernel, "AMMessageQueue:OperationModeChanged");
-    on_operation_mode_changed->Initialize();
+AppletMessageQueue::AppletMessageQueue(Kernel::KernelCore& kernel)
+    : on_new_message{kernel}, on_operation_mode_changed{kernel} {
+
+    Kernel::KAutoObject::Create(std::addressof(on_new_message));
+    Kernel::KAutoObject::Create(std::addressof(on_operation_mode_changed));
+
+    on_new_message.Initialize("AMMessageQueue:OnMessageReceived");
+    on_operation_mode_changed.Initialize("AMMessageQueue:OperationModeChanged");
 }
 
 AppletMessageQueue::~AppletMessageQueue() = default;
 
-const std::shared_ptr<Kernel::KReadableEvent>& AppletMessageQueue::GetMessageReceiveEvent() const {
-    return on_new_message->GetReadableEvent();
+Kernel::KReadableEvent& AppletMessageQueue::GetMessageReceiveEvent() {
+    return on_new_message.GetReadableEvent();
 }
 
-const std::shared_ptr<Kernel::KReadableEvent>& AppletMessageQueue::GetOperationModeChangedEvent()
-    const {
-    return on_operation_mode_changed->GetReadableEvent();
+Kernel::KReadableEvent& AppletMessageQueue::GetOperationModeChangedEvent() {
+    return on_operation_mode_changed.GetReadableEvent();
 }
 
 void AppletMessageQueue::PushMessage(AppletMessage msg) {
     messages.push(msg);
-    on_new_message->GetWritableEvent()->Signal();
+    on_new_message.GetWritableEvent().Signal();
 }
 
 AppletMessageQueue::AppletMessage AppletMessageQueue::PopMessage() {
     if (messages.empty()) {
-        on_new_message->GetWritableEvent()->Clear();
+        on_new_message.GetWritableEvent().Clear();
         return AppletMessage::NoMessage;
     }
     auto msg = messages.front();
     messages.pop();
     if (messages.empty()) {
-        on_new_message->GetWritableEvent()->Clear();
+        on_new_message.GetWritableEvent().Clear();
     }
     return msg;
 }
@@ -630,7 +634,7 @@ void AppletMessageQueue::FocusStateChanged() {
 void AppletMessageQueue::OperationModeChanged() {
     PushMessage(AppletMessage::OperationModeChanged);
     PushMessage(AppletMessage::PerformanceModeChanged);
-    on_operation_mode_changed->GetWritableEvent()->Signal();
+    on_operation_mode_changed.GetWritableEvent().Signal();
 }
 
 ICommonStateGetter::ICommonStateGetter(Core::System& system_,
@@ -927,11 +931,9 @@ private:
     void GetAppletStateChangedEvent(Kernel::HLERequestContext& ctx) {
         LOG_DEBUG(Service_AM, "called");
 
-        const auto event = applet->GetBroker().GetStateChangedEvent();
-
         IPC::ResponseBuilder rb{ctx, 2, 1};
         rb.Push(RESULT_SUCCESS);
-        rb.PushCopyObjects(event);
+        rb.PushCopyObjects(applet->GetBroker().GetStateChangedEvent());
     }
 
     void IsCompleted(Kernel::HLERequestContext& ctx) {
@@ -1213,16 +1215,16 @@ void ILibraryAppletCreator::CreateTransferMemoryStorage(Kernel::HLERequestContex
     }
 
     auto transfer_mem =
-        system.CurrentProcess()->GetHandleTable().Get<Kernel::TransferMemory>(handle);
+        system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(handle);
 
-    if (transfer_mem == nullptr) {
+    if (transfer_mem.IsNull()) {
         LOG_ERROR(Service_AM, "transfer_mem is a nullptr for handle={:08X}", handle);
         IPC::ResponseBuilder rb{ctx, 2};
         rb.Push(RESULT_UNKNOWN);
         return;
     }
 
-    const u8* const mem_begin = transfer_mem->GetPointer();
+    const u8* const mem_begin = system.Memory().GetPointer(transfer_mem->GetSourceAddress());
     const u8* const mem_end = mem_begin + transfer_mem->GetSize();
     std::vector<u8> memory{mem_begin, mem_end};
 
@@ -1247,16 +1249,16 @@ void ILibraryAppletCreator::CreateHandleStorage(Kernel::HLERequestContext& ctx)
     }
 
     auto transfer_mem =
-        system.CurrentProcess()->GetHandleTable().Get<Kernel::TransferMemory>(handle);
+        system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(handle);
 
-    if (transfer_mem == nullptr) {
+    if (transfer_mem.IsNull()) {
         LOG_ERROR(Service_AM, "transfer_mem is a nullptr for handle={:08X}", handle);
         IPC::ResponseBuilder rb{ctx, 2};
         rb.Push(RESULT_UNKNOWN);
         return;
     }
 
-    const u8* const mem_begin = transfer_mem->GetPointer();
+    const u8* const mem_begin = system.Memory().GetPointer(transfer_mem->GetSourceAddress());
     const u8* const mem_end = mem_begin + transfer_mem->GetSize();
     std::vector<u8> memory{mem_begin, mem_end};
 
@@ -1266,7 +1268,9 @@ void ILibraryAppletCreator::CreateHandleStorage(Kernel::HLERequestContext& ctx)
 }
 
 IApplicationFunctions::IApplicationFunctions(Core::System& system_)
-    : ServiceFramework{system_, "IApplicationFunctions"} {
+    : ServiceFramework{system_, "IApplicationFunctions"}, gpu_error_detected_event{system.Kernel()},
+      friend_invitation_storage_channel_event{system.Kernel()},
+      health_warning_disappeared_system_event{system.Kernel()} {
     // clang-format off
     static const FunctionInfo functions[] = {
         {1, &IApplicationFunctions::PopLaunchParameter, "PopLaunchParameter"},
@@ -1334,16 +1338,15 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_)
 
     RegisterHandlers(functions);
 
-    auto& kernel = system.Kernel();
-    gpu_error_detected_event =
-        Kernel::KEvent::Create(kernel, "IApplicationFunctions:GpuErrorDetectedSystemEvent");
-    gpu_error_detected_event->Initialize();
-    friend_invitation_storage_channel_event =
-        Kernel::KEvent::Create(kernel, "IApplicationFunctions:FriendInvitationStorageChannelEvent");
-    friend_invitation_storage_channel_event->Initialize();
-    health_warning_disappeared_system_event =
-        Kernel::KEvent::Create(kernel, "IApplicationFunctions:HealthWarningDisappearedSystemEvent");
-    health_warning_disappeared_system_event->Initialize();
+    Kernel::KAutoObject::Create(std::addressof(gpu_error_detected_event));
+    Kernel::KAutoObject::Create(std::addressof(friend_invitation_storage_channel_event));
+    Kernel::KAutoObject::Create(std::addressof(health_warning_disappeared_system_event));
+
+    gpu_error_detected_event.Initialize("IApplicationFunctions:GpuErrorDetectedSystemEvent");
+    friend_invitation_storage_channel_event.Initialize(
+        "IApplicationFunctions:FriendInvitationStorageChannelEvent");
+    health_warning_disappeared_system_event.Initialize(
+        "IApplicationFunctions:HealthWarningDisappearedSystemEvent");
 }
 
 IApplicationFunctions::~IApplicationFunctions() = default;
@@ -1740,7 +1743,7 @@ void IApplicationFunctions::GetGpuErrorDetectedSystemEvent(Kernel::HLERequestCon
 
     IPC::ResponseBuilder rb{ctx, 2, 1};
     rb.Push(RESULT_SUCCESS);
-    rb.PushCopyObjects(gpu_error_detected_event->GetReadableEvent());
+    rb.PushCopyObjects(gpu_error_detected_event.GetReadableEvent());
 }
 
 void IApplicationFunctions::GetFriendInvitationStorageChannelEvent(Kernel::HLERequestContext& ctx) {
@@ -1748,7 +1751,7 @@ void IApplicationFunctions::GetFriendInvitationStorageChannelEvent(Kernel::HLERe
 
     IPC::ResponseBuilder rb{ctx, 2, 1};
     rb.Push(RESULT_SUCCESS);
-    rb.PushCopyObjects(friend_invitation_storage_channel_event->GetReadableEvent());
+    rb.PushCopyObjects(friend_invitation_storage_channel_event.GetReadableEvent());
 }
 
 void IApplicationFunctions::TryPopFromFriendInvitationStorageChannel(
@@ -1764,7 +1767,7 @@ void IApplicationFunctions::GetHealthWarningDisappearedSystemEvent(Kernel::HLERe
 
     IPC::ResponseBuilder rb{ctx, 2, 1};
     rb.Push(RESULT_SUCCESS);
-    rb.PushCopyObjects(health_warning_disappeared_system_event->GetReadableEvent());
+    rb.PushCopyObjects(health_warning_disappeared_system_event.GetReadableEvent());
 }
 
 void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger,
@@ -1782,7 +1785,8 @@ void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger
 }
 
 IHomeMenuFunctions::IHomeMenuFunctions(Core::System& system_)
-    : ServiceFramework{system_, "IHomeMenuFunctions"} {
+    : ServiceFramework{system_, "IHomeMenuFunctions"}, pop_from_general_channel_event{
+                                                           system.Kernel()} {
     // clang-format off
     static const FunctionInfo functions[] = {
         {10, &IHomeMenuFunctions::RequestToGetForeground, "RequestToGetForeground"},
@@ -1803,9 +1807,8 @@ IHomeMenuFunctions::IHomeMenuFunctions(Core::System& system_)
 
     RegisterHandlers(functions);
 
-    pop_from_general_channel_event =
-        Kernel::KEvent::Create(system.Kernel(), "IHomeMenuFunctions:PopFromGeneralChannelEvent");
-    pop_from_general_channel_event->Initialize();
+    Kernel::KAutoObject::Create(std::addressof(pop_from_general_channel_event));
+    pop_from_general_channel_event.Initialize("IHomeMenuFunctions:PopFromGeneralChannelEvent");
 }
 
 IHomeMenuFunctions::~IHomeMenuFunctions() = default;
@@ -1822,7 +1825,7 @@ void IHomeMenuFunctions::GetPopFromGeneralChannelEvent(Kernel::HLERequestContext
 
     IPC::ResponseBuilder rb{ctx, 2, 1};
     rb.Push(RESULT_SUCCESS);
-    rb.PushCopyObjects(pop_from_general_channel_event->GetReadableEvent());
+    rb.PushCopyObjects(pop_from_general_channel_event.GetReadableEvent());
 }
 
 IGlobalStateController::IGlobalStateController(Core::System& system_)
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index 5d302e155..184030a8e 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -8,12 +8,12 @@
 #include <memory>
 #include <queue>
 
+#include "core/hle/kernel/k_event.h"
 #include "core/hle/service/service.h"
 
 namespace Kernel {
 class KernelCore;
-class KEvent;
-class TransferMemory;
+class KTransferMemory;
 } // namespace Kernel
 
 namespace Service::NVFlinger {
@@ -56,8 +56,8 @@ public:
     explicit AppletMessageQueue(Kernel::KernelCore& kernel);
     ~AppletMessageQueue();
 
-    const std::shared_ptr<Kernel::KReadableEvent>& GetMessageReceiveEvent() const;
-    const std::shared_ptr<Kernel::KReadableEvent>& GetOperationModeChangedEvent() const;
+    Kernel::KReadableEvent& GetMessageReceiveEvent();
+    Kernel::KReadableEvent& GetOperationModeChangedEvent();
     void PushMessage(AppletMessage msg);
     AppletMessage PopMessage();
     std::size_t GetMessageCount() const;
@@ -67,8 +67,8 @@ public:
 
 private:
     std::queue<AppletMessage> messages;
-    std::shared_ptr<Kernel::KEvent> on_new_message;
-    std::shared_ptr<Kernel::KEvent> on_operation_mode_changed;
+    Kernel::KEvent on_new_message;
+    Kernel::KEvent on_operation_mode_changed;
 };
 
 class IWindowController final : public ServiceFramework<IWindowController> {
@@ -156,8 +156,8 @@ private:
     };
 
     NVFlinger::NVFlinger& nvflinger;
-    std::shared_ptr<Kernel::KEvent> launchable_event;
-    std::shared_ptr<Kernel::KEvent> accumulated_suspended_tick_changed_event;
+    Kernel::KEvent launchable_event;
+    Kernel::KEvent accumulated_suspended_tick_changed_event;
 
     u32 idle_time_detection_extension = 0;
     u64 num_fatal_sections_entered = 0;
@@ -300,9 +300,9 @@ private:
     bool launch_popped_application_specific = false;
     bool launch_popped_account_preselect = false;
     s32 previous_program_index{-1};
-    std::shared_ptr<Kernel::KEvent> gpu_error_detected_event;
-    std::shared_ptr<Kernel::KEvent> friend_invitation_storage_channel_event;
-    std::shared_ptr<Kernel::KEvent> health_warning_disappeared_system_event;
+    Kernel::KEvent gpu_error_detected_event;
+    Kernel::KEvent friend_invitation_storage_channel_event;
+    Kernel::KEvent health_warning_disappeared_system_event;
 };
 
 class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> {
@@ -314,7 +314,7 @@ private:
     void RequestToGetForeground(Kernel::HLERequestContext& ctx);
     void GetPopFromGeneralChannelEvent(Kernel::HLERequestContext& ctx);
 
-    std::shared_ptr<Kernel::KEvent> pop_from_general_channel_event;
+    Kernel::KEvent pop_from_general_channel_event;
 };
 
 class IGlobalStateController final : public ServiceFramework<IGlobalStateController> {
diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp
index a56df6a7e..ae995df6b 100644
--- a/src/core/hle/service/am/applets/applets.cpp
+++ b/src/core/hle/service/am/applets/applets.cpp
@@ -12,10 +12,8 @@
 #include "core/frontend/applets/profile_select.h"
 #include "core/frontend/applets/software_keyboard.h"
 #include "core/frontend/applets/web_browser.h"
-#include "core/hle/kernel/k_event.h"
 #include "core/hle/kernel/k_readable_event.h"
 #include "core/hle/kernel/k_writable_event.h"
-#include "core/hle/kernel/server_session.h"
 #include "core/hle/service/am/am.h"
 #include "core/hle/service/am/applet_ae.h"
 #include "core/hle/service/am/applet_oe.h"
@@ -31,16 +29,16 @@
 namespace Service::AM::Applets {
 
 AppletDataBroker::AppletDataBroker(Core::System& system_, LibraryAppletMode applet_mode_)
-    : system{system_}, applet_mode{applet_mode_} {
-    state_changed_event =
-        Kernel::KEvent::Create(system.Kernel(), "ILibraryAppletAccessor:StateChangedEvent");
-    state_changed_event->Initialize();
-    pop_out_data_event =
-        Kernel::KEvent::Create(system.Kernel(), "ILibraryAppletAccessor:PopDataOutEvent");
-    pop_out_data_event->Initialize();
-    pop_interactive_out_data_event = Kernel::KEvent::Create(
-        system.Kernel(), "ILibraryAppletAccessor:PopInteractiveDataOutEvent");
-    pop_interactive_out_data_event->Initialize();
+    : system{system_}, applet_mode{applet_mode_}, state_changed_event{system.Kernel()},
+      pop_out_data_event{system.Kernel()}, pop_interactive_out_data_event{system.Kernel()} {
+
+    Kernel::KAutoObject::Create(std::addressof(state_changed_event));
+    Kernel::KAutoObject::Create(std::addressof(pop_out_data_event));
+    Kernel::KAutoObject::Create(std::addressof(pop_interactive_out_data_event));
+
+    state_changed_event.Initialize("ILibraryAppletAccessor:StateChangedEvent");
+    pop_out_data_event.Initialize("ILibraryAppletAccessor:PopDataOutEvent");
+    pop_interactive_out_data_event.Initialize("ILibraryAppletAccessor:PopInteractiveDataOutEvent");
 }
 
 AppletDataBroker::~AppletDataBroker() = default;
@@ -67,7 +65,7 @@ std::shared_ptr<IStorage> AppletDataBroker::PopNormalDataToGame() {
 
     auto out = std::move(out_channel.front());
     out_channel.pop_front();
-    pop_out_data_event->GetWritableEvent()->Clear();
+    pop_out_data_event.GetWritableEvent().Clear();
     return out;
 }
 
@@ -86,7 +84,7 @@ std::shared_ptr<IStorage> AppletDataBroker::PopInteractiveDataToGame() {
 
     auto out = std::move(out_interactive_channel.front());
     out_interactive_channel.pop_front();
-    pop_interactive_out_data_event->GetWritableEvent()->Clear();
+    pop_interactive_out_data_event.GetWritableEvent().Clear();
     return out;
 }
 
@@ -105,7 +103,7 @@ void AppletDataBroker::PushNormalDataFromGame(std::shared_ptr<IStorage>&& storag
 
 void AppletDataBroker::PushNormalDataFromApplet(std::shared_ptr<IStorage>&& storage) {
     out_channel.emplace_back(std::move(storage));
-    pop_out_data_event->GetWritableEvent()->Signal();
+    pop_out_data_event.GetWritableEvent().Signal();
 }
 
 void AppletDataBroker::PushInteractiveDataFromGame(std::shared_ptr<IStorage>&& storage) {
@@ -114,11 +112,11 @@ void AppletDataBroker::PushInteractiveDataFromGame(std::shared_ptr<IStorage>&& s
 
 void AppletDataBroker::PushInteractiveDataFromApplet(std::shared_ptr<IStorage>&& storage) {
     out_interactive_channel.emplace_back(std::move(storage));
-    pop_interactive_out_data_event->GetWritableEvent()->Signal();
+    pop_interactive_out_data_event.GetWritableEvent().Signal();
 }
 
-void AppletDataBroker::SignalStateChanged() const {
-    state_changed_event->GetWritableEvent()->Signal();
+void AppletDataBroker::SignalStateChanged() {
+    state_changed_event.GetWritableEvent().Signal();
 
     switch (applet_mode) {
     case LibraryAppletMode::AllForeground:
@@ -142,16 +140,16 @@ void AppletDataBroker::SignalStateChanged() const {
     }
 }
 
-std::shared_ptr<Kernel::KReadableEvent> AppletDataBroker::GetNormalDataEvent() const {
-    return pop_out_data_event->GetReadableEvent();
+Kernel::KReadableEvent& AppletDataBroker::GetNormalDataEvent() {
+    return pop_out_data_event.GetReadableEvent();
 }
 
-std::shared_ptr<Kernel::KReadableEvent> AppletDataBroker::GetInteractiveDataEvent() const {
-    return pop_interactive_out_data_event->GetReadableEvent();
+Kernel::KReadableEvent& AppletDataBroker::GetInteractiveDataEvent() {
+    return pop_interactive_out_data_event.GetReadableEvent();
 }
 
-std::shared_ptr<Kernel::KReadableEvent> AppletDataBroker::GetStateChangedEvent() const {
-    return state_changed_event->GetReadableEvent();
+Kernel::KReadableEvent& AppletDataBroker::GetStateChangedEvent() {
+    return state_changed_event.GetReadableEvent();
 }
 
 Applet::Applet(Core::System& system_, LibraryAppletMode applet_mode_)
diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h
index 4215d2232..5c0b4b459 100644
--- a/src/core/hle/service/am/applets/applets.h
+++ b/src/core/hle/service/am/applets/applets.h
@@ -8,7 +8,7 @@
 #include <queue>
 
 #include "common/swap.h"
-#include "core/hle/kernel/object.h"
+#include "core/hle/kernel/k_event.h"
 
 union ResultCode;
 
@@ -95,11 +95,11 @@ public:
     void PushInteractiveDataFromGame(std::shared_ptr<IStorage>&& storage);
     void PushInteractiveDataFromApplet(std::shared_ptr<IStorage>&& storage);
 
-    void SignalStateChanged() const;
+    void SignalStateChanged();
 
-    std::shared_ptr<Kernel::KReadableEvent> GetNormalDataEvent() const;
-    std::shared_ptr<Kernel::KReadableEvent> GetInteractiveDataEvent() const;
-    std::shared_ptr<Kernel::KReadableEvent> GetStateChangedEvent() const;
+    Kernel::KReadableEvent& GetNormalDataEvent();
+    Kernel::KReadableEvent& GetInteractiveDataEvent();
+    Kernel::KReadableEvent& GetStateChangedEvent();
 
 private:
     Core::System& system;
@@ -119,13 +119,13 @@ private:
     // PopInteractiveDataToGame and PushInteractiveDataFromApplet
     std::deque<std::shared_ptr<IStorage>> out_interactive_channel;
 
-    std::shared_ptr<Kernel::KEvent> state_changed_event;
+    Kernel::KEvent state_changed_event;
 
     // Signaled on PushNormalDataFromApplet
-    std::shared_ptr<Kernel::KEvent> pop_out_data_event;
+    Kernel::KEvent pop_out_data_event;
 
     // Signaled on PushInteractiveDataFromApplet
-    std::shared_ptr<Kernel::KEvent> pop_interactive_out_data_event;
+    Kernel::KEvent pop_interactive_out_data_event;
 };
 
 class Applet {
diff --git a/src/core/hle/service/am/applets/error.cpp b/src/core/hle/service/am/applets/error.cpp
index 0dd6ec68e..08348b180 100644
--- a/src/core/hle/service/am/applets/error.cpp
+++ b/src/core/hle/service/am/applets/error.cpp
@@ -9,7 +9,7 @@
 #include "common/string_util.h"
 #include "core/core.h"
 #include "core/frontend/applets/error.h"
-#include "core/hle/kernel/process.h"
+#include "core/hle/kernel/k_process.h"
 #include "core/hle/service/am/am.h"
 #include "core/hle/service/am/applets/error.h"
 #include "core/reporter.h"
diff --git a/src/core/hle/service/am/applets/general_backend.cpp b/src/core/hle/service/am/applets/general_backend.cpp
index b7483261e..e95499edd 100644
--- a/src/core/hle/service/am/applets/general_backend.cpp
+++ b/src/core/hle/service/am/applets/general_backend.cpp
@@ -9,7 +9,7 @@
 #include "common/logging/log.h"
 #include "core/core.h"
 #include "core/frontend/applets/general_frontend.h"
-#include "core/hle/kernel/process.h"
+#include "core/hle/kernel/k_process.h"
 #include "core/hle/result.h"
 #include "core/hle/service/am/am.h"
 #include "core/hle/service/am/applets/general_backend.h"
diff --git a/src/core/hle/service/am/applets/web_browser.cpp b/src/core/hle/service/am/applets/web_browser.cpp
index 2404921fd..e5f4a4485 100644
--- a/src/core/hle/service/am/applets/web_browser.cpp
+++ b/src/core/hle/service/am/applets/web_browser.cpp
@@ -17,7 +17,7 @@
 #include "core/file_sys/system_archive/system_archive.h"
 #include "core/file_sys/vfs_vector.h"
 #include "core/frontend/applets/web_browser.h"
-#include "core/hle/kernel/process.h"
+#include "core/hle/kernel/k_process.h"
 #include "core/hle/result.h"
 #include "core/hle/service/am/am.h"
 #include "core/hle/service/am/applets/web_browser.h"
diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp
index 75867e349..1863260f1 100644
--- a/src/core/hle/service/aoc/aoc_u.cpp
+++ b/src/core/hle/service/aoc/aoc_u.cpp
@@ -16,10 +16,9 @@
 #include "core/file_sys/patch_manager.h"
 #include "core/file_sys/registered_cache.h"
 #include "core/hle/ipc_helpers.h"
-#include "core/hle/kernel/k_event.h"
+#include "core/hle/kernel/k_process.h"
 #include "core/hle/kernel/k_readable_event.h"
 #include "core/hle/kernel/kernel.h"
-#include "core/hle/kernel/process.h"
 #include "core/hle/service/aoc/aoc_u.h"
 #include "core/loader/loader.h"
 
@@ -50,7 +49,7 @@ static std::vector<u64> AccumulateAOCTitleIDs(Core::System& system) {
 class IPurchaseEventManager final : public ServiceFramework<IPurchaseEventManager> {
 public:
     explicit IPurchaseEventManager(Core::System& system_)
-        : ServiceFramework{system_, "IPurchaseEventManager"} {
+        : ServiceFramework{system_, "IPurchaseEventManager"}, purchased_event{system.Kernel()} {
         // clang-format off
         static const FunctionInfo functions[] = {
             {0, &IPurchaseEventManager::SetDefaultDeliveryTarget, "SetDefaultDeliveryTarget"},
@@ -63,9 +62,8 @@ public:
 
         RegisterHandlers(functions);
 
-        purchased_event =
-            Kernel::KEvent::Create(system.Kernel(), "IPurchaseEventManager:PurchasedEvent");
-        purchased_event->Initialize();
+        Kernel::KAutoObject::Create(std::addressof(purchased_event));
+        purchased_event.Initialize("IPurchaseEventManager:PurchasedEvent");
     }
 
 private:
@@ -98,14 +96,15 @@ private:
 
         IPC::ResponseBuilder rb{ctx, 2, 1};
         rb.Push(RESULT_SUCCESS);
-        rb.PushCopyObjects(purchased_event->GetReadableEvent());
+        rb.PushCopyObjects(purchased_event.GetReadableEvent());
     }
 
-    std::shared_ptr<Kernel::KEvent> purchased_event;
+    Kernel::KEvent purchased_event;
 };
 
 AOC_U::AOC_U(Core::System& system_)
-    : ServiceFramework{system_, "aoc:u"}, add_on_content{AccumulateAOCTitleIDs(system)} {
+    : ServiceFramework{system_, "aoc:u"}, add_on_content{AccumulateAOCTitleIDs(system)},
+      aoc_change_event{system.Kernel()} {
     // clang-format off
     static const FunctionInfo functions[] = {
         {0, nullptr, "CountAddOnContentByApplicationId"},
@@ -127,9 +126,8 @@ AOC_U::AOC_U(Core::System& system_)
 
     RegisterHandlers(functions);
 
-    auto& kernel = system.Kernel();
-    aoc_change_event = Kernel::KEvent::Create(kernel, "GetAddOnContentListChanged:Event");
-    aoc_change_event->Initialize();
+    Kernel::KAutoObject::Create(std::addressof(aoc_change_event));
+    aoc_change_event.Initialize("GetAddOnContentListChanged:Event");
 }
 
 AOC_U::~AOC_U() = default;
@@ -256,7 +254,7 @@ void AOC_U::GetAddOnContentListChangedEvent(Kernel::HLERequestContext& ctx) {
 
     IPC::ResponseBuilder rb{ctx, 2, 1};
     rb.Push(RESULT_SUCCESS);
-    rb.PushCopyObjects(aoc_change_event->GetReadableEvent());
+    rb.PushCopyObjects(aoc_change_event.GetReadableEvent());
 }
 
 void AOC_U::CreateEcPurchasedEventManager(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/aoc/aoc_u.h b/src/core/hle/service/aoc/aoc_u.h
index 1aa23529e..65095baa2 100644
--- a/src/core/hle/service/aoc/aoc_u.h
+++ b/src/core/hle/service/aoc/aoc_u.h
@@ -4,6 +4,7 @@
 
 #pragma once
 
+#include "core/hle/kernel/k_event.h"
 #include "core/hle/service/service.h"
 
 namespace Core {
@@ -31,7 +32,7 @@ private:
     void CreatePermanentEcPurchasedEventManager(Kernel::HLERequestContext& ctx);
 
     std::vector<u64> add_on_content;
-    std::shared_ptr<Kernel::KEvent> aoc_change_event;
+    Kernel::KEvent aoc_change_event;
 };
 
 /// Registers all AOC services with the specified service manager.
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index 5f51fca9a..e1ae726f5 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -43,9 +43,9 @@ class IAudioOut final : public ServiceFramework<IAudioOut> {
 public:
     IAudioOut(Core::System& system_, AudoutParams audio_params_, AudioCore::AudioOut& audio_core_,
               std::string&& device_name_, std::string&& unique_name)
-        : ServiceFramework{system_, "IAudioOut"}, audio_core{audio_core_},
-          device_name{std::move(device_name_)}, audio_params{audio_params_}, main_memory{
-                                                                                 system.Memory()} {
+        : ServiceFramework{system_, "IAudioOut"}, audio_core{audio_core_}, device_name{std::move(
+                                                                               device_name_)},
+          audio_params{audio_params_}, buffer_event{system.Kernel()}, main_memory{system.Memory()} {
         // clang-format off
         static const FunctionInfo functions[] = {
             {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"},
@@ -67,13 +67,13 @@ public:
         RegisterHandlers(functions);
 
         // This is the event handle used to check if the audio buffer was released
-        buffer_event = Kernel::KEvent::Create(system.Kernel(), "IAudioOutBufferReleased");
-        buffer_event->Initialize();
+        Kernel::KAutoObject::Create(std::addressof(buffer_event));
+        buffer_event.Initialize("IAudioOutBufferReleased");
 
         stream = audio_core.OpenStream(system.CoreTiming(), audio_params.sample_rate,
                                        audio_params.channel_count, std::move(unique_name), [this] {
                                            const auto guard = LockService();
-                                           buffer_event->GetWritableEvent()->Signal();
+                                           buffer_event.GetWritableEvent().Signal();
                                        });
     }
 
@@ -126,7 +126,7 @@ private:
 
         IPC::ResponseBuilder rb{ctx, 2, 1};
         rb.Push(RESULT_SUCCESS);
-        rb.PushCopyObjects(buffer_event->GetReadableEvent());
+        rb.PushCopyObjects(buffer_event.GetReadableEvent());
     }
 
     void AppendAudioOutBufferImpl(Kernel::HLERequestContext& ctx) {
@@ -220,7 +220,7 @@ private:
     [[maybe_unused]] AudoutParams audio_params{};
 
     /// This is the event handle used to check if the audio buffer was released
-    std::shared_ptr<Kernel::KEvent> buffer_event;
+    Kernel::KEvent buffer_event;
     Core::Memory::Memory& main_memory;
 };
 
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index 3a48342fd..513bd3730 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -30,7 +30,7 @@ public:
     explicit IAudioRenderer(Core::System& system_,
                             const AudioCommon::AudioRendererParameter& audren_params,
                             const std::size_t instance_number)
-        : ServiceFramework{system_, "IAudioRenderer"} {
+        : ServiceFramework{system_, "IAudioRenderer"}, system_event{system.Kernel()} {
         // clang-format off
         static const FunctionInfo functions[] = {
             {0, &IAudioRenderer::GetSampleRate, "GetSampleRate"},
@@ -49,13 +49,13 @@ public:
         // clang-format on
         RegisterHandlers(functions);
 
-        system_event = Kernel::KEvent::Create(system.Kernel(), "IAudioRenderer:SystemEvent");
-        system_event->Initialize();
+        Kernel::KAutoObject::Create(std::addressof(system_event));
+        system_event.Initialize("IAudioRenderer:SystemEvent");
         renderer = std::make_unique<AudioCore::AudioRenderer>(
             system.CoreTiming(), system.Memory(), audren_params,
             [this]() {
                 const auto guard = LockService();
-                system_event->GetWritableEvent()->Signal();
+                system_event.GetWritableEvent().Signal();
             },
             instance_number);
     }
@@ -128,7 +128,7 @@ private:
 
         IPC::ResponseBuilder rb{ctx, 2, 1};
         rb.Push(RESULT_SUCCESS);
-        rb.PushCopyObjects(system_event->GetReadableEvent());
+        rb.PushCopyObjects(system_event.GetReadableEvent());
     }
 
     void SetRenderingTimeLimit(Kernel::HLERequestContext& ctx) {
@@ -162,7 +162,7 @@ private:
         rb.Push(ERR_NOT_SUPPORTED);
     }
 
-    std::shared_ptr<Kernel::KEvent> system_event;
+    Kernel::KEvent system_event;
     std::unique_ptr<AudioCore::AudioRenderer> renderer;
     u32 rendering_time_limit_percent = 100;
 };
@@ -170,7 +170,9 @@ private:
 class IAudioDevice final : public ServiceFramework<IAudioDevice> {
 public:
     explicit IAudioDevice(Core::System& system_, u32_le revision_num)
-        : ServiceFramework{system_, "IAudioDevice"}, revision{revision_num} {
+        : ServiceFramework{system_, "IAudioDevice"}, revision{revision_num},
+          buffer_event{system.Kernel()}, audio_input_device_switch_event{system.Kernel()},
+          audio_output_device_switch_event{system.Kernel()} {
         static const FunctionInfo functions[] = {
             {0, &IAudioDevice::ListAudioDeviceName, "ListAudioDeviceName"},
             {1, &IAudioDevice::SetAudioDeviceOutputVolume, "SetAudioDeviceOutputVolume"},
@@ -188,20 +190,17 @@ public:
         };
         RegisterHandlers(functions);
 
-        auto& kernel = system.Kernel();
-        buffer_event = Kernel::KEvent::Create(kernel, "IAudioOutBufferReleasedEvent");
-        buffer_event->Initialize();
+        Kernel::KAutoObject::Create(std::addressof(buffer_event));
+        buffer_event.Initialize("IAudioOutBufferReleasedEvent");
 
         // Should be similar to audio_output_device_switch_event
-        audio_input_device_switch_event =
-            Kernel::KEvent::Create(kernel, "IAudioDevice:AudioInputDeviceSwitchedEvent");
-        audio_input_device_switch_event->Initialize();
+        Kernel::KAutoObject::Create(std::addressof(audio_input_device_switch_event));
+        audio_input_device_switch_event.Initialize("IAudioDevice:AudioInputDeviceSwitchedEvent");
 
         // Should only be signalled when an audio output device has been changed, example: speaker
         // to headset
-        audio_output_device_switch_event =
-            Kernel::KEvent::Create(kernel, "IAudioDevice:AudioOutputDeviceSwitchedEvent");
-        audio_output_device_switch_event->Initialize();
+        Kernel::KAutoObject::Create(std::addressof(audio_output_device_switch_event));
+        audio_output_device_switch_event.Initialize("IAudioDevice:AudioOutputDeviceSwitchedEvent");
     }
 
 private:
@@ -290,11 +289,11 @@ private:
     void QueryAudioDeviceSystemEvent(Kernel::HLERequestContext& ctx) {
         LOG_WARNING(Service_Audio, "(STUBBED) called");
 
-        buffer_event->GetWritableEvent()->Signal();
+        buffer_event.GetWritableEvent().Signal();
 
         IPC::ResponseBuilder rb{ctx, 2, 1};
         rb.Push(RESULT_SUCCESS);
-        rb.PushCopyObjects(buffer_event->GetReadableEvent());
+        rb.PushCopyObjects(buffer_event.GetReadableEvent());
     }
 
     void GetActiveChannelCount(Kernel::HLERequestContext& ctx) {
@@ -311,7 +310,7 @@ private:
 
         IPC::ResponseBuilder rb{ctx, 2, 1};
         rb.Push(RESULT_SUCCESS);
-        rb.PushCopyObjects(audio_input_device_switch_event->GetReadableEvent());
+        rb.PushCopyObjects(audio_input_device_switch_event.GetReadableEvent());
     }
 
     void QueryAudioDeviceOutputEvent(Kernel::HLERequestContext& ctx) {
@@ -319,13 +318,13 @@ private:
 
         IPC::ResponseBuilder rb{ctx, 2, 1};
         rb.Push(RESULT_SUCCESS);
-        rb.PushCopyObjects(audio_output_device_switch_event->GetReadableEvent());
+        rb.PushCopyObjects(audio_output_device_switch_event.GetReadableEvent());
     }
 
     u32_le revision = 0;
-    std::shared_ptr<Kernel::KEvent> buffer_event;
-    std::shared_ptr<Kernel::KEvent> audio_input_device_switch_event;
-    std::shared_ptr<Kernel::KEvent> audio_output_device_switch_event;
+    Kernel::KEvent buffer_event;
+    Kernel::KEvent audio_input_device_switch_event;
+    Kernel::KEvent audio_output_device_switch_event;
 
 }; // namespace Audio
 
diff --git a/src/core/hle/service/bcat/backend/backend.cpp b/src/core/hle/service/bcat/backend/backend.cpp
index 92d25dbe4..0e935bfa6 100644
--- a/src/core/hle/service/bcat/backend/backend.cpp
+++ b/src/core/hle/service/bcat/backend/backend.cpp
@@ -5,7 +5,6 @@
 #include "common/hex_util.h"
 #include "common/logging/log.h"
 #include "core/core.h"
-#include "core/hle/kernel/k_event.h"
 #include "core/hle/kernel/k_readable_event.h"
 #include "core/hle/kernel/k_writable_event.h"
 #include "core/hle/lock.h"
@@ -14,14 +13,14 @@
 namespace Service::BCAT {
 
 ProgressServiceBackend::ProgressServiceBackend(Kernel::KernelCore& kernel,
-                                               std::string_view event_name) {
-    event = Kernel::KEvent::Create(kernel,
-                                   "ProgressServiceBackend:UpdateEvent:" + std::string(event_name));
-    event->Initialize();
+                                               std::string_view event_name)
+    : update_event{kernel} {
+    Kernel::KAutoObject::Create(std::addressof(update_event));
+    update_event.Initialize("ProgressServiceBackend:UpdateEvent:" + std::string(event_name));
 }
 
-std::shared_ptr<Kernel::KReadableEvent> ProgressServiceBackend::GetEvent() const {
-    return event->GetReadableEvent();
+Kernel::KReadableEvent& ProgressServiceBackend::GetEvent() {
+    return update_event.GetReadableEvent();
 }
 
 DeliveryCacheProgressImpl& ProgressServiceBackend::GetImpl() {
@@ -86,12 +85,12 @@ void ProgressServiceBackend::FinishDownload(ResultCode result) {
     SignalUpdate();
 }
 
-void ProgressServiceBackend::SignalUpdate() const {
+void ProgressServiceBackend::SignalUpdate() {
     if (need_hle_lock) {
         std::lock_guard lock(HLE::g_hle_lock);
-        event->GetWritableEvent()->Signal();
+        update_event.GetWritableEvent().Signal();
     } else {
-        event->GetWritableEvent()->Signal();
+        update_event.GetWritableEvent().Signal();
     }
 }
 
diff --git a/src/core/hle/service/bcat/backend/backend.h b/src/core/hle/service/bcat/backend/backend.h
index db585b069..f591a362a 100644
--- a/src/core/hle/service/bcat/backend/backend.h
+++ b/src/core/hle/service/bcat/backend/backend.h
@@ -11,6 +11,7 @@
 
 #include "common/common_types.h"
 #include "core/file_sys/vfs_types.h"
+#include "core/hle/kernel/k_event.h"
 #include "core/hle/result.h"
 
 namespace Core {
@@ -98,13 +99,13 @@ public:
 private:
     explicit ProgressServiceBackend(Kernel::KernelCore& kernel, std::string_view event_name);
 
-    std::shared_ptr<Kernel::KReadableEvent> GetEvent() const;
+    Kernel::KReadableEvent& GetEvent();
     DeliveryCacheProgressImpl& GetImpl();
 
-    void SignalUpdate() const;
+    void SignalUpdate();
 
     DeliveryCacheProgressImpl impl{};
-    std::shared_ptr<Kernel::KEvent> event;
+    Kernel::KEvent update_event;
     bool need_hle_lock = false;
 };
 
diff --git a/src/core/hle/service/bcat/module.cpp b/src/core/hle/service/bcat/module.cpp
index 285085f2a..0206cbb6a 100644
--- a/src/core/hle/service/bcat/module.cpp
+++ b/src/core/hle/service/bcat/module.cpp
@@ -12,9 +12,9 @@
 #include "core/core.h"
 #include "core/file_sys/vfs.h"
 #include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/k_process.h"
 #include "core/hle/kernel/k_readable_event.h"
 #include "core/hle/kernel/k_writable_event.h"
-#include "core/hle/kernel/process.h"
 #include "core/hle/service/bcat/backend/backend.h"
 #include "core/hle/service/bcat/bcat.h"
 #include "core/hle/service/bcat/module.h"
@@ -88,11 +88,9 @@ struct DeliveryCacheDirectoryEntry {
 
 class IDeliveryCacheProgressService final : public ServiceFramework<IDeliveryCacheProgressService> {
 public:
-    explicit IDeliveryCacheProgressService(Core::System& system_,
-                                           std::shared_ptr<Kernel::KReadableEvent> event_,
+    explicit IDeliveryCacheProgressService(Core::System& system_, Kernel::KReadableEvent& event_,
                                            const DeliveryCacheProgressImpl& impl_)
-        : ServiceFramework{system_, "IDeliveryCacheProgressService"}, event{std::move(event_)},
-          impl{impl_} {
+        : ServiceFramework{system_, "IDeliveryCacheProgressService"}, event{event_}, impl{impl_} {
         // clang-format off
         static const FunctionInfo functions[] = {
             {0, &IDeliveryCacheProgressService::GetEvent, "GetEvent"},
@@ -121,7 +119,7 @@ private:
         rb.Push(RESULT_SUCCESS);
     }
 
-    std::shared_ptr<Kernel::KReadableEvent> event;
+    Kernel::KReadableEvent& event;
     const DeliveryCacheProgressImpl& impl;
 };
 
diff --git a/src/core/hle/service/btdrv/btdrv.cpp b/src/core/hle/service/btdrv/btdrv.cpp
index af3a5842d..fd97a822c 100644
--- a/src/core/hle/service/btdrv/btdrv.cpp
+++ b/src/core/hle/service/btdrv/btdrv.cpp
@@ -17,7 +17,8 @@ namespace Service::BtDrv {
 
 class Bt final : public ServiceFramework<Bt> {
 public:
-    explicit Bt(Core::System& system_) : ServiceFramework{system_, "bt"} {
+    explicit Bt(Core::System& system_)
+        : ServiceFramework{system_, "bt"}, register_event{system.Kernel()} {
         // clang-format off
         static const FunctionInfo functions[] = {
             {0, nullptr, "LeClientReadCharacteristic"},
@@ -34,9 +35,8 @@ public:
         // clang-format on
         RegisterHandlers(functions);
 
-        auto& kernel = system.Kernel();
-        register_event = Kernel::KEvent::Create(kernel, "BT:RegisterEvent");
-        register_event->Initialize();
+        Kernel::KAutoObject::Create(std::addressof(register_event));
+        register_event.Initialize("BT:RegisterEvent");
     }
 
 private:
@@ -45,10 +45,10 @@ private:
 
         IPC::ResponseBuilder rb{ctx, 2, 1};
         rb.Push(RESULT_SUCCESS);
-        rb.PushCopyObjects(register_event->GetReadableEvent());
+        rb.PushCopyObjects(register_event.GetReadableEvent());
     }
 
-    std::shared_ptr<Kernel::KEvent> register_event;
+    Kernel::KEvent register_event;
 };
 
 class BtDrv final : public ServiceFramework<BtDrv> {
diff --git a/src/core/hle/service/btm/btm.cpp b/src/core/hle/service/btm/btm.cpp
index d1ebc2388..3b5ef69e1 100644
--- a/src/core/hle/service/btm/btm.cpp
+++ b/src/core/hle/service/btm/btm.cpp
@@ -18,7 +18,10 @@ namespace Service::BTM {
 
 class IBtmUserCore final : public ServiceFramework<IBtmUserCore> {
 public:
-    explicit IBtmUserCore(Core::System& system_) : ServiceFramework{system_, "IBtmUserCore"} {
+    explicit IBtmUserCore(Core::System& system_)
+        : ServiceFramework{system_, "IBtmUserCore"}, scan_event{system.Kernel()},
+          connection_event{system.Kernel()}, service_discovery{system.Kernel()},
+          config_event{system.Kernel()} {
         // clang-format off
         static const FunctionInfo functions[] = {
             {0, &IBtmUserCore::AcquireBleScanEvent, "AcquireBleScanEvent"},
@@ -57,15 +60,15 @@ public:
         // clang-format on
         RegisterHandlers(functions);
 
-        auto& kernel = system.Kernel();
-        scan_event = Kernel::KEvent::Create(kernel, "IBtmUserCore:ScanEvent");
-        scan_event->Initialize();
-        connection_event = Kernel::KEvent::Create(kernel, "IBtmUserCore:ConnectionEvent");
-        connection_event->Initialize();
-        service_discovery = Kernel::KEvent::Create(kernel, "IBtmUserCore:Discovery");
-        service_discovery->Initialize();
-        config_event = Kernel::KEvent::Create(kernel, "IBtmUserCore:ConfigEvent");
-        config_event->Initialize();
+        Kernel::KAutoObject::Create(std::addressof(scan_event));
+        Kernel::KAutoObject::Create(std::addressof(connection_event));
+        Kernel::KAutoObject::Create(std::addressof(service_discovery));
+        Kernel::KAutoObject::Create(std::addressof(config_event));
+
+        scan_event.Initialize("IBtmUserCore:ScanEvent");
+        connection_event.Initialize("IBtmUserCore:ConnectionEvent");
+        service_discovery.Initialize("IBtmUserCore:Discovery");
+        config_event.Initialize("IBtmUserCore:ConfigEvent");
     }
 
 private:
@@ -74,7 +77,7 @@ private:
 
         IPC::ResponseBuilder rb{ctx, 2, 1};
         rb.Push(RESULT_SUCCESS);
-        rb.PushCopyObjects(scan_event->GetReadableEvent());
+        rb.PushCopyObjects(scan_event.GetReadableEvent());
     }
 
     void AcquireBleConnectionEvent(Kernel::HLERequestContext& ctx) {
@@ -82,7 +85,7 @@ private:
 
         IPC::ResponseBuilder rb{ctx, 2, 1};
         rb.Push(RESULT_SUCCESS);
-        rb.PushCopyObjects(connection_event->GetReadableEvent());
+        rb.PushCopyObjects(connection_event.GetReadableEvent());
     }
 
     void AcquireBleServiceDiscoveryEvent(Kernel::HLERequestContext& ctx) {
@@ -90,7 +93,7 @@ private:
 
         IPC::ResponseBuilder rb{ctx, 2, 1};
         rb.Push(RESULT_SUCCESS);
-        rb.PushCopyObjects(service_discovery->GetReadableEvent());
+        rb.PushCopyObjects(service_discovery.GetReadableEvent());
     }
 
     void AcquireBleMtuConfigEvent(Kernel::HLERequestContext& ctx) {
@@ -98,13 +101,13 @@ private:
 
         IPC::ResponseBuilder rb{ctx, 2, 1};
         rb.Push(RESULT_SUCCESS);
-        rb.PushCopyObjects(config_event->GetReadableEvent());
+        rb.PushCopyObjects(config_event.GetReadableEvent());
     }
 
-    std::shared_ptr<Kernel::KEvent> scan_event;
-    std::shared_ptr<Kernel::KEvent> connection_event;
-    std::shared_ptr<Kernel::KEvent> service_discovery;
-    std::shared_ptr<Kernel::KEvent> config_event;
+    Kernel::KEvent scan_event;
+    Kernel::KEvent connection_event;
+    Kernel::KEvent service_discovery;
+    Kernel::KEvent config_event;
 };
 
 class BTM_USR final : public ServiceFramework<BTM_USR> {
diff --git a/src/core/hle/service/fatal/fatal.cpp b/src/core/hle/service/fatal/fatal.cpp
index 13147472e..432abde76 100644
--- a/src/core/hle/service/fatal/fatal.cpp
+++ b/src/core/hle/service/fatal/fatal.cpp
@@ -12,7 +12,7 @@
 #include "common/swap.h"
 #include "core/core.h"
 #include "core/hle/ipc_helpers.h"
-#include "core/hle/kernel/process.h"
+#include "core/hle/kernel/k_process.h"
 #include "core/hle/service/fatal/fatal.h"
 #include "core/hle/service/fatal/fatal_p.h"
 #include "core/hle/service/fatal/fatal_u.h"
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index 67b2b3102..67baaee9b 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -21,7 +21,7 @@
 #include "core/file_sys/sdmc_factory.h"
 #include "core/file_sys/vfs.h"
 #include "core/file_sys/vfs_offset.h"
-#include "core/hle/kernel/process.h"
+#include "core/hle/kernel/k_process.h"
 #include "core/hle/service/filesystem/filesystem.h"
 #include "core/hle/service/filesystem/fsp_ldr.h"
 #include "core/hle/service/filesystem/fsp_pr.h"
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index 7dc487e48..92ea27074 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -25,7 +25,7 @@
 #include "core/file_sys/system_archive/system_archive.h"
 #include "core/file_sys/vfs.h"
 #include "core/hle/ipc_helpers.h"
-#include "core/hle/kernel/process.h"
+#include "core/hle/kernel/k_process.h"
 #include "core/hle/service/filesystem/filesystem.h"
 #include "core/hle/service/filesystem/fsp_srv.h"
 #include "core/reporter.h"
diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp
index a35979053..91c202952 100644
--- a/src/core/hle/service/friend/friend.cpp
+++ b/src/core/hle/service/friend/friend.cpp
@@ -185,7 +185,8 @@ private:
 class INotificationService final : public ServiceFramework<INotificationService> {
 public:
     explicit INotificationService(Common::UUID uuid_, Core::System& system_)
-        : ServiceFramework{system_, "INotificationService"}, uuid{uuid_} {
+        : ServiceFramework{system_, "INotificationService"}, uuid{uuid_}, notification_event{
+                                                                              system.Kernel()} {
         // clang-format off
         static const FunctionInfo functions[] = {
             {0, &INotificationService::GetEvent, "GetEvent"},
@@ -196,9 +197,8 @@ public:
 
         RegisterHandlers(functions);
 
-        notification_event =
-            Kernel::KEvent::Create(system.Kernel(), "INotificationService:NotifyEvent");
-        notification_event->Initialize();
+        Kernel::KAutoObject::Create(std::addressof(notification_event));
+        notification_event.Initialize("INotificationService:NotifyEvent");
     }
 
 private:
@@ -207,7 +207,7 @@ private:
 
         IPC::ResponseBuilder rb{ctx, 2, 1};
         rb.Push(RESULT_SUCCESS);
-        rb.PushCopyObjects(notification_event->GetReadableEvent());
+        rb.PushCopyObjects(notification_event.GetReadableEvent());
     }
 
     void Clear(Kernel::HLERequestContext& ctx) {
@@ -273,7 +273,7 @@ private:
     };
 
     Common::UUID uuid{Common::INVALID_UUID};
-    std::shared_ptr<Kernel::KEvent> notification_event;
+    Kernel::KEvent notification_event;
     std::queue<SizedNotificationInfo> notifications;
     States states{};
 };
diff --git a/src/core/hle/service/glue/arp.cpp b/src/core/hle/service/glue/arp.cpp
index 7b1c6677c..6ad62ee5a 100644
--- a/src/core/hle/service/glue/arp.cpp
+++ b/src/core/hle/service/glue/arp.cpp
@@ -9,8 +9,8 @@
 #include "core/file_sys/control_metadata.h"
 #include "core/hle/ipc_helpers.h"
 #include "core/hle/kernel/hle_ipc.h"
+#include "core/hle/kernel/k_process.h"
 #include "core/hle/kernel/kernel.h"
-#include "core/hle/kernel/process.h"
 #include "core/hle/service/glue/arp.h"
 #include "core/hle/service/glue/errors.h"
 #include "core/hle/service/glue/manager.h"
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index d4678ef49..7acad3798 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -159,7 +159,7 @@ void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) {
     const auto controller_type = connected_controllers[controller_idx].type;
     auto& controller = shared_memory_entries[controller_idx];
     if (controller_type == NPadControllerType::None) {
-        styleset_changed_events[controller_idx]->GetWritableEvent()->Signal();
+        styleset_changed_events[controller_idx]->GetWritableEvent().Signal();
         return;
     }
     controller.style_set.raw = 0; // Zero out
@@ -253,9 +253,8 @@ void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) {
 void Controller_NPad::OnInit() {
     auto& kernel = system.Kernel();
     for (std::size_t i = 0; i < styleset_changed_events.size(); ++i) {
-        styleset_changed_events[i] =
-            Kernel::KEvent::Create(kernel, fmt::format("npad:NpadStyleSetChanged_{}", i));
-        styleset_changed_events[i]->Initialize();
+        styleset_changed_events[i] = Kernel::KEvent::Create(kernel);
+        styleset_changed_events[i]->Initialize(fmt::format("npad:NpadStyleSetChanged_{}", i));
     }
 
     if (!IsControllerActivated()) {
@@ -341,6 +340,11 @@ void Controller_NPad::OnRelease() {
             VibrateControllerAtIndex(npad_idx, device_idx, {});
         }
     }
+
+    for (std::size_t i = 0; i < styleset_changed_events.size(); ++i) {
+        styleset_changed_events[i]->Close();
+        styleset_changed_events[i] = nullptr;
+    }
 }
 
 void Controller_NPad::RequestPadStateUpdate(u32 npad_id) {
@@ -955,14 +959,12 @@ bool Controller_NPad::IsVibrationDeviceMounted(const DeviceHandle& vibration_dev
     return vibration_devices_mounted[npad_index][device_index];
 }
 
-std::shared_ptr<Kernel::KReadableEvent> Controller_NPad::GetStyleSetChangedEvent(
-    u32 npad_id) const {
-    const auto& styleset_event = styleset_changed_events[NPadIdToIndex(npad_id)];
-    return styleset_event->GetReadableEvent();
+Kernel::KReadableEvent& Controller_NPad::GetStyleSetChangedEvent(u32 npad_id) {
+    return styleset_changed_events[NPadIdToIndex(npad_id)]->GetReadableEvent();
 }
 
 void Controller_NPad::SignalStyleSetChangedEvent(u32 npad_id) const {
-    styleset_changed_events[NPadIdToIndex(npad_id)]->GetWritableEvent()->Signal();
+    styleset_changed_events[NPadIdToIndex(npad_id)]->GetWritableEvent().Signal();
 }
 
 void Controller_NPad::AddNewControllerAt(NPadControllerType controller, std::size_t npad_index) {
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index ea484d4bf..c050c9a44 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -11,7 +11,6 @@
 #include "common/quaternion.h"
 #include "common/settings.h"
 #include "core/frontend/input.h"
-#include "core/hle/kernel/object.h"
 #include "core/hle/service/hid/controllers/controller_base.h"
 
 namespace Kernel {
@@ -199,7 +198,7 @@ public:
 
     bool IsVibrationDeviceMounted(const DeviceHandle& vibration_device_handle) const;
 
-    std::shared_ptr<Kernel::KReadableEvent> GetStyleSetChangedEvent(u32 npad_id) const;
+    Kernel::KReadableEvent& GetStyleSetChangedEvent(u32 npad_id);
     void SignalStyleSetChangedEvent(u32 npad_id) const;
 
     // Adds a new controller at an index.
@@ -573,8 +572,9 @@ private:
     NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual};
     NpadCommunicationMode communication_mode{NpadCommunicationMode::Default};
     // Each controller should have their own styleset changed event
-    std::array<std::shared_ptr<Kernel::KEvent>, 10> styleset_changed_events;
-    std::array<std::array<std::chrono::steady_clock::time_point, 2>, 10> last_vibration_timepoints;
+    std::array<Kernel::KEvent*, 10> styleset_changed_events{};
+    std::array<std::array<std::chrono::steady_clock::time_point, 2>, 10>
+        last_vibration_timepoints{};
     std::array<std::array<VibrationValue, 2>, 10> latest_vibration_values{};
     bool permit_vibration_session_enabled{false};
     std::array<std::array<bool, 2>, 10> vibration_devices_mounted{};
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 9c4bf6d16..49c17fd14 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -13,18 +13,18 @@
 #include "core/frontend/input.h"
 #include "core/hardware_properties.h"
 #include "core/hle/ipc_helpers.h"
-#include "core/hle/kernel/client_port.h"
-#include "core/hle/kernel/client_session.h"
+#include "core/hle/kernel/k_client_port.h"
 #include "core/hle/kernel/k_readable_event.h"
 #include "core/hle/kernel/k_shared_memory.h"
+#include "core/hle/kernel/k_transfer_memory.h"
 #include "core/hle/kernel/k_writable_event.h"
 #include "core/hle/kernel/kernel.h"
-#include "core/hle/kernel/transfer_memory.h"
 #include "core/hle/service/hid/errors.h"
 #include "core/hle/service/hid/hid.h"
 #include "core/hle/service/hid/irs.h"
 #include "core/hle/service/hid/xcd.h"
 #include "core/hle/service/service.h"
+#include "core/memory.h"
 
 #include "core/hle/service/hid/controllers/console_sixaxis.h"
 #include "core/hle/service/hid/controllers/controller_base.h"
@@ -53,9 +53,6 @@ IAppletResource::IAppletResource(Core::System& system_)
     };
     RegisterHandlers(functions);
 
-    auto& kernel = system.Kernel();
-    shared_mem = SharedFrom(&kernel.GetHidSharedMem());
-
     MakeController<Controller_DebugPad>(HidController::DebugPad);
     MakeController<Controller_Touchscreen>(HidController::Touchscreen);
     MakeController<Controller_Mouse>(HidController::Mouse);
@@ -118,7 +115,7 @@ void IAppletResource::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
 
     IPC::ResponseBuilder rb{ctx, 2, 1};
     rb.Push(RESULT_SUCCESS);
-    rb.PushCopyObjects(shared_mem);
+    rb.PushCopyObjects(&system.Kernel().GetHidSharedMem());
 }
 
 void IAppletResource::UpdateControllers(std::uintptr_t user_data,
@@ -130,7 +127,8 @@ void IAppletResource::UpdateControllers(std::uintptr_t user_data,
         if (should_reload) {
             controller->OnLoadInputDevices();
         }
-        controller->OnUpdate(core_timing, shared_mem->GetPointer(), SHARED_MEMORY_SIZE);
+        controller->OnUpdate(core_timing, system.Kernel().GetHidSharedMem().GetPointer(),
+                             SHARED_MEMORY_SIZE);
     }
 
     // If ns_late is higher than the update rate ignore the delay
@@ -145,7 +143,7 @@ void IAppletResource::UpdateMotion(std::uintptr_t user_data, std::chrono::nanose
     auto& core_timing = system.CoreTiming();
 
     controllers[static_cast<size_t>(HidController::NPad)]->OnMotionUpdate(
-        core_timing, shared_mem->GetPointer(), SHARED_MEMORY_SIZE);
+        core_timing, system.Kernel().GetHidSharedMem().GetPointer(), SHARED_MEMORY_SIZE);
 
     // If ns_late is higher than the update rate ignore the delay
     if (ns_late > motion_update_ns) {
@@ -1496,20 +1494,20 @@ void Hid::InitializeSevenSixAxisSensor(Kernel::HLERequestContext& ctx) {
     ASSERT_MSG(t_mem_1_size == 0x1000, "t_mem_1_size is not 0x1000 bytes");
     ASSERT_MSG(t_mem_2_size == 0x7F000, "t_mem_2_size is not 0x7F000 bytes");
 
-    auto t_mem_1 =
-        system.CurrentProcess()->GetHandleTable().Get<Kernel::TransferMemory>(t_mem_1_handle);
+    auto t_mem_1 = system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
+        t_mem_1_handle);
 
-    if (t_mem_1 == nullptr) {
+    if (t_mem_1.IsNull()) {
         LOG_ERROR(Service_HID, "t_mem_1 is a nullptr for handle=0x{:08X}", t_mem_1_handle);
         IPC::ResponseBuilder rb{ctx, 2};
         rb.Push(RESULT_UNKNOWN);
         return;
     }
 
-    auto t_mem_2 =
-        system.CurrentProcess()->GetHandleTable().Get<Kernel::TransferMemory>(t_mem_2_handle);
+    auto t_mem_2 = system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
+        t_mem_2_handle);
 
-    if (t_mem_2 == nullptr) {
+    if (t_mem_2.IsNull()) {
         LOG_ERROR(Service_HID, "t_mem_2 is a nullptr for handle=0x{:08X}", t_mem_2_handle);
         IPC::ResponseBuilder rb{ctx, 2};
         rb.Push(RESULT_UNKNOWN);
@@ -1524,7 +1522,7 @@ void Hid::InitializeSevenSixAxisSensor(Kernel::HLERequestContext& ctx) {
         .ActivateController();
 
     applet_resource->GetController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor)
-        .SetTransferMemoryPointer(t_mem_1->GetPointer());
+        .SetTransferMemoryPointer(system.Memory().GetPointer(t_mem_1->GetSourceAddress()));
 
     LOG_WARNING(Service_HID,
                 "called, t_mem_1_handle=0x{:08X}, t_mem_2_handle=0x{:08X}, "
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index c2bdd39a3..aa3307955 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -13,10 +13,6 @@ namespace Core::Timing {
 struct EventType;
 }
 
-namespace Kernel {
-class KSharedMemory;
-}
-
 namespace Service::SM {
 class ServiceManager;
 }
@@ -69,8 +65,6 @@ private:
     void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
     void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
 
-    std::shared_ptr<Kernel::KSharedMemory> shared_mem;
-
     std::shared_ptr<Core::Timing::EventType> pad_update_event;
     std::shared_ptr<Core::Timing::EventType> motion_update_event;
 
diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp
index 2dfa936fb..3c6085990 100644
--- a/src/core/hle/service/hid/irs.cpp
+++ b/src/core/hle/service/hid/irs.cpp
@@ -37,10 +37,6 @@ IRS::IRS(Core::System& system_) : ServiceFramework{system_, "irs"} {
     // clang-format on
 
     RegisterHandlers(functions);
-
-    auto& kernel = system.Kernel();
-
-    shared_mem = SharedFrom(&kernel.GetIrsSharedMem());
 }
 
 void IRS::ActivateIrsensor(Kernel::HLERequestContext& ctx) {
@@ -62,7 +58,7 @@ void IRS::GetIrsensorSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
 
     IPC::ResponseBuilder rb{ctx, 2, 1};
     rb.Push(RESULT_SUCCESS);
-    rb.PushCopyObjects(shared_mem);
+    rb.PushCopyObjects(&system.Kernel().GetIrsSharedMem());
 }
 
 void IRS::StopImageProcessor(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/hid/irs.h b/src/core/hle/service/hid/irs.h
index b0c8c7168..9bc6462b0 100644
--- a/src/core/hle/service/hid/irs.h
+++ b/src/core/hle/service/hid/irs.h
@@ -4,17 +4,12 @@
 
 #pragma once
 
-#include "core/hle/kernel/object.h"
 #include "core/hle/service/service.h"
 
 namespace Core {
 class System;
 }
 
-namespace Kernel {
-class KSharedMemory;
-}
-
 namespace Service::HID {
 
 class IRS final : public ServiceFramework<IRS> {
@@ -42,7 +37,6 @@ private:
     void StopImageProcessorAsync(Kernel::HLERequestContext& ctx);
     void ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx);
 
-    std::shared_ptr<Kernel::KSharedMemory> shared_mem;
     const u32 device_handle{0xABCD};
 };
 
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp
index 89f6f6a2a..c3948eb8e 100644
--- a/src/core/hle/service/ldr/ldr.cpp
+++ b/src/core/hle/service/ldr/ldr.cpp
@@ -12,8 +12,8 @@
 #include "core/core.h"
 #include "core/hle/ipc_helpers.h"
 #include "core/hle/kernel/k_page_table.h"
+#include "core/hle/kernel/k_process.h"
 #include "core/hle/kernel/k_system_control.h"
-#include "core/hle/kernel/process.h"
 #include "core/hle/kernel/svc_results.h"
 #include "core/hle/service/ldr/ldr.h"
 #include "core/hle/service/service.h"
@@ -321,7 +321,7 @@ public:
         return addr;
     }
 
-    ResultVal<VAddr> MapProcessCodeMemory(Kernel::Process* process, VAddr baseAddress,
+    ResultVal<VAddr> MapProcessCodeMemory(Kernel::KProcess* process, VAddr baseAddress,
                                           u64 size) const {
         for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) {
             auto& page_table{process->PageTable()};
@@ -342,7 +342,7 @@ public:
         return ERROR_INSUFFICIENT_ADDRESS_SPACE;
     }
 
-    ResultVal<VAddr> MapNro(Kernel::Process* process, VAddr nro_addr, std::size_t nro_size,
+    ResultVal<VAddr> MapNro(Kernel::KProcess* process, VAddr nro_addr, std::size_t nro_size,
                             VAddr bss_addr, std::size_t bss_size, std::size_t size) const {
         for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) {
             auto& page_table{process->PageTable()};
@@ -378,7 +378,7 @@ public:
         return ERROR_INSUFFICIENT_ADDRESS_SPACE;
     }
 
-    ResultCode LoadNro(Kernel::Process* process, const NROHeader& nro_header, VAddr nro_addr,
+    ResultCode LoadNro(Kernel::KProcess* process, const NROHeader& nro_header, VAddr nro_addr,
                        VAddr start) const {
         const VAddr text_start{start + nro_header.segment_headers[TEXT_INDEX].memory_offset};
         const VAddr ro_start{start + nro_header.segment_headers[RO_INDEX].memory_offset};
diff --git a/src/core/hle/service/mm/mm_u.cpp b/src/core/hle/service/mm/mm_u.cpp
index b0cb07d24..c8519e2db 100644
--- a/src/core/hle/service/mm/mm_u.cpp
+++ b/src/core/hle/service/mm/mm_u.cpp
@@ -4,7 +4,6 @@
 
 #include "common/logging/log.h"
 #include "core/hle/ipc_helpers.h"
-#include "core/hle/kernel/client_session.h"
 #include "core/hle/service/mm/mm_u.h"
 #include "core/hle/service/sm/sm.h"
 
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp
index 2d1d4d67f..d25b20ab5 100644
--- a/src/core/hle/service/nfp/nfp.cpp
+++ b/src/core/hle/service/nfp/nfp.cpp
@@ -8,7 +8,6 @@
 #include "common/logging/log.h"
 #include "core/core.h"
 #include "core/hle/ipc_helpers.h"
-#include "core/hle/kernel/k_event.h"
 #include "core/hle/kernel/k_readable_event.h"
 #include "core/hle/kernel/k_thread.h"
 #include "core/hle/kernel/k_writable_event.h"
@@ -24,10 +23,9 @@ constexpr ResultCode ERR_NO_APPLICATION_AREA(ErrorModule::NFP, 152);
 
 Module::Interface::Interface(std::shared_ptr<Module> module_, Core::System& system_,
                              const char* name)
-    : ServiceFramework{system_, name}, module{std::move(module_)} {
-    auto& kernel = system.Kernel();
-    nfc_tag_load = Kernel::KEvent::Create(kernel, "IUser:NFCTagDetected");
-    nfc_tag_load->Initialize();
+    : ServiceFramework{system_, name}, nfc_tag_load{system.Kernel()}, module{std::move(module_)} {
+    Kernel::KAutoObject::Create(std::addressof(nfc_tag_load));
+    nfc_tag_load.Initialize("IUser:NFCTagDetected");
 }
 
 Module::Interface::~Interface() = default;
@@ -35,7 +33,8 @@ Module::Interface::~Interface() = default;
 class IUser final : public ServiceFramework<IUser> {
 public:
     explicit IUser(Module::Interface& nfp_interface_, Core::System& system_)
-        : ServiceFramework{system_, "NFP::IUser"}, nfp_interface{nfp_interface_} {
+        : ServiceFramework{system_, "NFP::IUser"}, nfp_interface{nfp_interface_},
+          deactivate_event{system.Kernel()}, availability_change_event{system.Kernel()} {
         static const FunctionInfo functions[] = {
             {0, &IUser::Initialize, "Initialize"},
             {1, &IUser::Finalize, "Finalize"},
@@ -65,11 +64,11 @@ public:
         };
         RegisterHandlers(functions);
 
-        auto& kernel = system.Kernel();
-        deactivate_event = Kernel::KEvent::Create(kernel, "IUser:DeactivateEvent");
-        deactivate_event->Initialize();
-        availability_change_event = Kernel::KEvent::Create(kernel, "IUser:AvailabilityChangeEvent");
-        availability_change_event->Initialize();
+        Kernel::KAutoObject::Create(std::addressof(deactivate_event));
+        Kernel::KAutoObject::Create(std::addressof(availability_change_event));
+
+        deactivate_event.Initialize("IUser:DeactivateEvent");
+        availability_change_event.Initialize("IUser:AvailabilityChangeEvent");
     }
 
 private:
@@ -167,7 +166,7 @@ private:
 
         IPC::ResponseBuilder rb{ctx, 2, 1};
         rb.Push(RESULT_SUCCESS);
-        rb.PushCopyObjects(deactivate_event->GetReadableEvent());
+        rb.PushCopyObjects(deactivate_event.GetReadableEvent());
     }
 
     void StopDetection(Kernel::HLERequestContext& ctx) {
@@ -176,7 +175,7 @@ private:
         switch (device_state) {
         case DeviceState::TagFound:
         case DeviceState::TagNearby:
-            deactivate_event->GetWritableEvent()->Signal();
+            deactivate_event.GetWritableEvent().Signal();
             device_state = DeviceState::Initialized;
             break;
         case DeviceState::SearchingForTag:
@@ -265,7 +264,7 @@ private:
 
         IPC::ResponseBuilder rb{ctx, 2, 1};
         rb.Push(RESULT_SUCCESS);
-        rb.PushCopyObjects(availability_change_event->GetReadableEvent());
+        rb.PushCopyObjects(availability_change_event.GetReadableEvent());
     }
 
     void GetRegisterInfo(Kernel::HLERequestContext& ctx) {
@@ -319,9 +318,9 @@ private:
     const u32 npad_id{0};       // Player 1 controller
     State state{State::NonInitialized};
     DeviceState device_state{DeviceState::Initialized};
-    std::shared_ptr<Kernel::KEvent> deactivate_event;
-    std::shared_ptr<Kernel::KEvent> availability_change_event;
-    const Module::Interface& nfp_interface;
+    Module::Interface& nfp_interface;
+    Kernel::KEvent deactivate_event;
+    Kernel::KEvent availability_change_event;
 };
 
 void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) {
@@ -339,12 +338,12 @@ bool Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) {
     }
 
     std::memcpy(&amiibo, buffer.data(), sizeof(amiibo));
-    nfc_tag_load->GetWritableEvent()->Signal();
+    nfc_tag_load.GetWritableEvent().Signal();
     return true;
 }
 
-const std::shared_ptr<Kernel::KReadableEvent>& Module::Interface::GetNFCEvent() const {
-    return nfc_tag_load->GetReadableEvent();
+Kernel::KReadableEvent& Module::Interface::GetNFCEvent() {
+    return nfc_tag_load.GetReadableEvent();
 }
 
 const Module::Interface::AmiiboFile& Module::Interface::GetAmiiboBuffer() const {
diff --git a/src/core/hle/service/nfp/nfp.h b/src/core/hle/service/nfp/nfp.h
index c46551760..5e4e49bc6 100644
--- a/src/core/hle/service/nfp/nfp.h
+++ b/src/core/hle/service/nfp/nfp.h
@@ -7,6 +7,7 @@
 #include <array>
 #include <vector>
 
+#include "core/hle/kernel/k_event.h"
 #include "core/hle/service/service.h"
 
 namespace Kernel {
@@ -38,11 +39,11 @@ public:
 
         void CreateUserInterface(Kernel::HLERequestContext& ctx);
         bool LoadAmiibo(const std::vector<u8>& buffer);
-        const std::shared_ptr<Kernel::KReadableEvent>& GetNFCEvent() const;
+        Kernel::KReadableEvent& GetNFCEvent();
         const AmiiboFile& GetAmiiboBuffer() const;
 
     private:
-        std::shared_ptr<Kernel::KEvent> nfc_tag_load;
+        Kernel::KEvent nfc_tag_load;
         AmiiboFile amiibo{};
 
     protected:
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp
index 9f110df8e..94ef3983a 100644
--- a/src/core/hle/service/nifm/nifm.cpp
+++ b/src/core/hle/service/nifm/nifm.cpp
@@ -127,7 +127,8 @@ public:
 
 class IRequest final : public ServiceFramework<IRequest> {
 public:
-    explicit IRequest(Core::System& system_) : ServiceFramework{system_, "IRequest"} {
+    explicit IRequest(Core::System& system_)
+        : ServiceFramework{system_, "IRequest"}, event1{system.Kernel()}, event2{system.Kernel()} {
         static const FunctionInfo functions[] = {
             {0, &IRequest::GetRequestState, "GetRequestState"},
             {1, &IRequest::GetResult, "GetResult"},
@@ -157,12 +158,11 @@ public:
         };
         RegisterHandlers(functions);
 
-        auto& kernel = system.Kernel();
+        Kernel::KAutoObject::Create(std::addressof(event1));
+        Kernel::KAutoObject::Create(std::addressof(event2));
 
-        event1 = Kernel::KEvent::Create(kernel, "IRequest:Event1");
-        event1->Initialize();
-        event2 = Kernel::KEvent::Create(kernel, "IRequest:Event2");
-        event2->Initialize();
+        event1.Initialize("IRequest:Event1");
+        event2.Initialize("IRequest:Event2");
     }
 
 private:
@@ -198,7 +198,7 @@ private:
 
         IPC::ResponseBuilder rb{ctx, 2, 2};
         rb.Push(RESULT_SUCCESS);
-        rb.PushCopyObjects(event1->GetReadableEvent(), event2->GetReadableEvent());
+        rb.PushCopyObjects(event1.GetReadableEvent(), event2.GetReadableEvent());
     }
 
     void Cancel(Kernel::HLERequestContext& ctx) {
@@ -229,7 +229,7 @@ private:
         rb.Push<u32>(0);
     }
 
-    std::shared_ptr<Kernel::KEvent> event1, event2;
+    Kernel::KEvent event1, event2;
 };
 
 class INetworkProfile final : public ServiceFramework<INetworkProfile> {
diff --git a/src/core/hle/service/nim/nim.cpp b/src/core/hle/service/nim/nim.cpp
index fee360ab9..420a5a075 100644
--- a/src/core/hle/service/nim/nim.cpp
+++ b/src/core/hle/service/nim/nim.cpp
@@ -300,7 +300,8 @@ class IEnsureNetworkClockAvailabilityService final
     : public ServiceFramework<IEnsureNetworkClockAvailabilityService> {
 public:
     explicit IEnsureNetworkClockAvailabilityService(Core::System& system_)
-        : ServiceFramework{system_, "IEnsureNetworkClockAvailabilityService"} {
+        : ServiceFramework{system_, "IEnsureNetworkClockAvailabilityService"},
+          finished_event{system.Kernel()} {
         static const FunctionInfo functions[] = {
             {0, &IEnsureNetworkClockAvailabilityService::StartTask, "StartTask"},
             {1, &IEnsureNetworkClockAvailabilityService::GetFinishNotificationEvent,
@@ -312,19 +313,17 @@ public:
         };
         RegisterHandlers(functions);
 
-        auto& kernel = system.Kernel();
-        finished_event =
-            Kernel::KEvent::Create(kernel, "IEnsureNetworkClockAvailabilityService:FinishEvent");
-        finished_event->Initialize();
+        Kernel::KAutoObject::Create(std::addressof(finished_event));
+        finished_event.Initialize("IEnsureNetworkClockAvailabilityService:FinishEvent");
     }
 
 private:
-    std::shared_ptr<Kernel::KEvent> finished_event;
+    Kernel::KEvent finished_event;
 
     void StartTask(Kernel::HLERequestContext& ctx) {
         // No need to connect to the internet, just finish the task straight away.
         LOG_DEBUG(Service_NIM, "called");
-        finished_event->GetWritableEvent()->Signal();
+        finished_event.GetWritableEvent().Signal();
         IPC::ResponseBuilder rb{ctx, 2};
         rb.Push(RESULT_SUCCESS);
     }
@@ -334,7 +333,7 @@ private:
 
         IPC::ResponseBuilder rb{ctx, 2, 1};
         rb.Push(RESULT_SUCCESS);
-        rb.PushCopyObjects(finished_event->GetReadableEvent());
+        rb.PushCopyObjects(finished_event.GetReadableEvent());
     }
 
     void GetResult(Kernel::HLERequestContext& ctx) {
@@ -346,7 +345,7 @@ private:
 
     void Cancel(Kernel::HLERequestContext& ctx) {
         LOG_DEBUG(Service_NIM, "called");
-        finished_event->GetWritableEvent()->Clear();
+        finished_event.GetWritableEvent().Clear();
         IPC::ResponseBuilder rb{ctx, 2};
         rb.Push(RESULT_SUCCESS);
     }
diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/pl_u.cpp
index da139fdc4..e14acce58 100644
--- a/src/core/hle/service/ns/pl_u.cpp
+++ b/src/core/hle/service/ns/pl_u.cpp
@@ -130,9 +130,6 @@ struct PL_U::Impl {
         }
     }
 
-    /// Handle to shared memory region designated for a shared font
-    std::shared_ptr<Kernel::KSharedMemory> shared_font_mem;
-
     /// Backing memory for the shared font data
     std::shared_ptr<Kernel::PhysicalMemory> shared_font;
 
@@ -260,14 +257,13 @@ void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) {
 
     // Create shared font memory object
     auto& kernel = system.Kernel();
-    impl->shared_font_mem = SharedFrom(&kernel.GetFontSharedMem());
 
-    std::memcpy(impl->shared_font_mem->GetPointer(), impl->shared_font->data(),
+    std::memcpy(kernel.GetFontSharedMem().GetPointer(), impl->shared_font->data(),
                 impl->shared_font->size());
 
     IPC::ResponseBuilder rb{ctx, 2, 1};
     rb.Push(RESULT_SUCCESS);
-    rb.PushCopyObjects(impl->shared_font_mem);
+    rb.PushCopyObjects(&kernel.GetFontSharedMem());
 }
 
 void PL_U::GetSharedFontInOrderOfPriority(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
index 6f4007294..775e76330 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -102,20 +102,20 @@ NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector
         return NvResult::Success;
     }
 
-    auto event = events_interface.events[event_id];
+    auto& event = events_interface.events[event_id];
     auto& gpu = system.GPU();
 
     // This is mostly to take into account unimplemented features. As synced
     // gpu is always synced.
     if (!gpu.IsAsync()) {
-        event.event->GetWritableEvent()->Signal();
+        event.event->GetWritableEvent().Signal();
         return NvResult::Success;
     }
     auto lock = gpu.LockSync();
     const u32 current_syncpoint_value = event.fence.value;
     const s32 diff = current_syncpoint_value - params.threshold;
     if (diff >= 0) {
-        event.event->GetWritableEvent()->Signal();
+        event.event->GetWritableEvent().Signal();
         params.value = current_syncpoint_value;
         std::memcpy(output.data(), &params, sizeof(params));
         return NvResult::Success;
@@ -142,7 +142,7 @@ NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector
             params.value = ((params.syncpt_id & 0xfff) << 16) | 0x10000000;
         }
         params.value |= event_id;
-        event.event->GetWritableEvent()->Clear();
+        event.event->GetWritableEvent().Clear();
         gpu.RegisterSyncptInterrupt(params.syncpt_id, target_value);
         std::memcpy(output.data(), &params, sizeof(params));
         return NvResult::Timeout;
diff --git a/src/core/hle/service/nvdrv/interface.cpp b/src/core/hle/service/nvdrv/interface.cpp
index eff9c3cc9..dc9b9341f 100644
--- a/src/core/hle/service/nvdrv/interface.cpp
+++ b/src/core/hle/service/nvdrv/interface.cpp
@@ -187,8 +187,8 @@ void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) {
     if (event_id < MaxNvEvents) {
         IPC::ResponseBuilder rb{ctx, 3, 1};
         rb.Push(RESULT_SUCCESS);
-        auto event = nvdrv->GetEvent(event_id);
-        event->Clear();
+        auto& event = nvdrv->GetEvent(event_id);
+        event.Clear();
         rb.PushCopyObjects(event);
         rb.PushEnum(NvResult::Success);
     } else {
diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp
index ede77858a..74796dce1 100644
--- a/src/core/hle/service/nvdrv/nvdrv.cpp
+++ b/src/core/hle/service/nvdrv/nvdrv.cpp
@@ -42,9 +42,8 @@ void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger
 Module::Module(Core::System& system) : syncpoint_manager{system.GPU()} {
     auto& kernel = system.Kernel();
     for (u32 i = 0; i < MaxNvEvents; i++) {
-        std::string event_label = fmt::format("NVDRV::NvEvent_{}", i);
-        events_interface.events[i] = {Kernel::KEvent::Create(kernel, std::move(event_label))};
-        events_interface.events[i].event->Initialize();
+        events_interface.events[i].event = Kernel::KEvent::Create(kernel);
+        events_interface.events[i].event->Initialize(fmt::format("NVDRV::NvEvent_{}", i));
         events_interface.status[i] = EventState::Free;
         events_interface.registered[i] = false;
     }
@@ -64,7 +63,12 @@ Module::Module(Core::System& system) : syncpoint_manager{system.GPU()} {
         std::make_shared<Devices::nvhost_vic>(system, nvmap_dev, syncpoint_manager);
 }
 
-Module::~Module() = default;
+Module::~Module() {
+    for (u32 i = 0; i < MaxNvEvents; i++) {
+        events_interface.events[i].event->Close();
+        events_interface.events[i].event = nullptr;
+    }
+}
 
 NvResult Module::VerifyFD(DeviceFD fd) const {
     if (fd < 0) {
@@ -172,16 +176,16 @@ void Module::SignalSyncpt(const u32 syncpoint_id, const u32 value) {
         if (events_interface.assigned_syncpt[i] == syncpoint_id &&
             events_interface.assigned_value[i] == value) {
             events_interface.LiberateEvent(i);
-            events_interface.events[i].event->GetWritableEvent()->Signal();
+            events_interface.events[i].event->GetWritableEvent().Signal();
         }
     }
 }
 
-std::shared_ptr<Kernel::KReadableEvent> Module::GetEvent(const u32 event_id) const {
+Kernel::KReadableEvent& Module::GetEvent(const u32 event_id) {
     return events_interface.events[event_id].event->GetReadableEvent();
 }
 
-std::shared_ptr<Kernel::KWritableEvent> Module::GetEventWriteable(const u32 event_id) const {
+Kernel::KWritableEvent& Module::GetEventWriteable(const u32 event_id) {
     return events_interface.events[event_id].event->GetWritableEvent();
 }
 
diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h
index 53719aadd..a43ceb7ae 100644
--- a/src/core/hle/service/nvdrv/nvdrv.h
+++ b/src/core/hle/service/nvdrv/nvdrv.h
@@ -35,7 +35,7 @@ class nvdevice;
 
 /// Represents an Nvidia event
 struct NvEvent {
-    std::shared_ptr<Kernel::KEvent> event;
+    Kernel::KEvent* event{};
     Fence fence{};
 };
 
@@ -136,9 +136,9 @@ public:
 
     void SignalSyncpt(const u32 syncpoint_id, const u32 value);
 
-    std::shared_ptr<Kernel::KReadableEvent> GetEvent(u32 event_id) const;
+    Kernel::KReadableEvent& GetEvent(u32 event_id);
 
-    std::shared_ptr<Kernel::KWritableEvent> GetEventWriteable(u32 event_id) const;
+    Kernel::KWritableEvent& GetEventWriteable(u32 event_id);
 
 private:
     /// Manages syncpoints on the host
diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp
index 7842a82ed..0b6e7430b 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue.cpp
@@ -7,7 +7,6 @@
 #include "common/assert.h"
 #include "common/logging/log.h"
 #include "core/core.h"
-#include "core/hle/kernel/k_event.h"
 #include "core/hle/kernel/k_writable_event.h"
 #include "core/hle/kernel/kernel.h"
 #include "core/hle/service/nvflinger/buffer_queue.h"
@@ -15,9 +14,9 @@
 namespace Service::NVFlinger {
 
 BufferQueue::BufferQueue(Kernel::KernelCore& kernel, u32 id, u64 layer_id)
-    : id(id), layer_id(layer_id) {
-    buffer_wait_event = Kernel::KEvent::Create(kernel, "BufferQueue:WaitEvent");
-    buffer_wait_event->Initialize();
+    : id(id), layer_id(layer_id), buffer_wait_event{kernel} {
+    Kernel::KAutoObject::Create(std::addressof(buffer_wait_event));
+    buffer_wait_event.Initialize("BufferQueue:WaitEvent");
 }
 
 BufferQueue::~BufferQueue() = default;
@@ -42,7 +41,7 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer)
         .multi_fence = {},
     };
 
-    buffer_wait_event->GetWritableEvent()->Signal();
+    buffer_wait_event.GetWritableEvent().Signal();
 }
 
 std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::DequeueBuffer(u32 width,
@@ -120,7 +119,7 @@ void BufferQueue::CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& mult
     }
     free_buffers_condition.notify_one();
 
-    buffer_wait_event->GetWritableEvent()->Signal();
+    buffer_wait_event.GetWritableEvent().Signal();
 }
 
 std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() {
@@ -155,7 +154,7 @@ void BufferQueue::ReleaseBuffer(u32 slot) {
     }
     free_buffers_condition.notify_one();
 
-    buffer_wait_event->GetWritableEvent()->Signal();
+    buffer_wait_event.GetWritableEvent().Signal();
 }
 
 void BufferQueue::Connect() {
@@ -170,7 +169,7 @@ void BufferQueue::Disconnect() {
         std::unique_lock lock{queue_sequence_mutex};
         queue_sequence.clear();
     }
-    buffer_wait_event->GetWritableEvent()->Signal();
+    buffer_wait_event.GetWritableEvent().Signal();
     is_connect = false;
     free_buffers_condition.notify_one();
 }
@@ -189,12 +188,12 @@ u32 BufferQueue::Query(QueryType type) {
     return 0;
 }
 
-std::shared_ptr<Kernel::KWritableEvent> BufferQueue::GetWritableBufferWaitEvent() const {
-    return buffer_wait_event->GetWritableEvent();
+Kernel::KWritableEvent& BufferQueue::GetWritableBufferWaitEvent() {
+    return buffer_wait_event.GetWritableEvent();
 }
 
-std::shared_ptr<Kernel::KReadableEvent> BufferQueue::GetBufferWaitEvent() const {
-    return buffer_wait_event->GetReadableEvent();
+Kernel::KReadableEvent& BufferQueue::GetBufferWaitEvent() {
+    return buffer_wait_event.GetReadableEvent();
 }
 
 } // namespace Service::NVFlinger
diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h
index 163fa4c54..4ec0b1506 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.h
+++ b/src/core/hle/service/nvflinger/buffer_queue.h
@@ -13,7 +13,8 @@
 #include "common/common_funcs.h"
 #include "common/math_util.h"
 #include "common/swap.h"
-#include "core/hle/kernel/object.h"
+#include "core/hle/kernel/k_event.h"
+#include "core/hle/kernel/k_readable_event.h"
 #include "core/hle/service/nvdrv/nvdata.h"
 
 namespace Kernel {
@@ -115,9 +116,9 @@ public:
         return is_connect;
     }
 
-    std::shared_ptr<Kernel::KWritableEvent> GetWritableBufferWaitEvent() const;
+    Kernel::KWritableEvent& GetWritableBufferWaitEvent();
 
-    std::shared_ptr<Kernel::KReadableEvent> GetBufferWaitEvent() const;
+    Kernel::KReadableEvent& GetBufferWaitEvent();
 
 private:
     BufferQueue(const BufferQueue&) = delete;
@@ -129,7 +130,7 @@ private:
     std::list<u32> free_buffers;
     std::array<Buffer, buffer_slots> buffers;
     std::list<u32> queue_sequence;
-    std::shared_ptr<Kernel::KEvent> buffer_wait_event;
+    Kernel::KEvent buffer_wait_event;
 
     std::mutex free_buffers_mutex;
     std::condition_variable free_buffers_condition;
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index c43593e7f..7fb9133c7 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -165,7 +165,7 @@ std::optional<u32> NVFlinger::FindBufferQueueId(u64 display_id, u64 layer_id) co
     return layer->GetBufferQueue().GetId();
 }
 
-std::shared_ptr<Kernel::KReadableEvent> NVFlinger::FindVsyncEvent(u64 display_id) const {
+Kernel::KReadableEvent* NVFlinger::FindVsyncEvent(u64 display_id) {
     const auto lock_guard = Lock();
     auto* const display = FindDisplay(display_id);
 
@@ -173,7 +173,7 @@ std::shared_ptr<Kernel::KReadableEvent> NVFlinger::FindVsyncEvent(u64 display_id
         return nullptr;
     }
 
-    return display->GetVSyncEvent();
+    return &display->GetVSyncEvent();
 }
 
 BufferQueue* NVFlinger::FindBufferQueue(u32 id) {
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h
index 6fe2c7f2a..b0febdaec 100644
--- a/src/core/hle/service/nvflinger/nvflinger.h
+++ b/src/core/hle/service/nvflinger/nvflinger.h
@@ -5,6 +5,7 @@
 #pragma once
 
 #include <atomic>
+#include <list>
 #include <memory>
 #include <mutex>
 #include <optional>
@@ -14,7 +15,6 @@
 #include <vector>
 
 #include "common/common_types.h"
-#include "core/hle/kernel/object.h"
 
 namespace Common {
 class Event;
@@ -72,7 +72,7 @@ public:
     /// Gets the vsync event for the specified display.
     ///
     /// If an invalid display ID is provided, then nullptr is returned.
-    [[nodiscard]] std::shared_ptr<Kernel::KReadableEvent> FindVsyncEvent(u64 display_id) const;
+    [[nodiscard]] Kernel::KReadableEvent* FindVsyncEvent(u64 display_id);
 
     /// Obtains a buffer queue identified by the ID.
     [[nodiscard]] BufferQueue* FindBufferQueue(u32 id);
@@ -106,7 +106,7 @@ private:
 
     std::shared_ptr<Nvidia::Module> nvdrv;
 
-    std::vector<VI::Display> displays;
+    std::list<VI::Display> displays;
     std::vector<std::unique_ptr<BufferQueue>> buffer_queues;
 
     /// Id to use for the next layer that is created, this counter is shared among all displays.
diff --git a/src/core/hle/service/pctl/module.cpp b/src/core/hle/service/pctl/module.cpp
index 9bebe6088..1c3d81143 100644
--- a/src/core/hle/service/pctl/module.cpp
+++ b/src/core/hle/service/pctl/module.cpp
@@ -7,7 +7,7 @@
 #include "core/file_sys/control_metadata.h"
 #include "core/file_sys/patch_manager.h"
 #include "core/hle/ipc_helpers.h"
-#include "core/hle/kernel/process.h"
+#include "core/hle/kernel/k_process.h"
 #include "core/hle/service/pctl/module.h"
 #include "core/hle/service/pctl/pctl.h"
 
diff --git a/src/core/hle/service/pm/pm.cpp b/src/core/hle/service/pm/pm.cpp
index 68736c40c..f4715935d 100644
--- a/src/core/hle/service/pm/pm.cpp
+++ b/src/core/hle/service/pm/pm.cpp
@@ -4,8 +4,8 @@
 
 #include "core/core.h"
 #include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/k_process.h"
 #include "core/hle/kernel/kernel.h"
-#include "core/hle/kernel/process.h"
 #include "core/hle/service/pm/pm.h"
 #include "core/hle/service/service.h"
 
@@ -17,9 +17,9 @@ constexpr ResultCode ERROR_PROCESS_NOT_FOUND{ErrorModule::PM, 1};
 
 constexpr u64 NO_PROCESS_FOUND_PID{0};
 
-std::optional<std::shared_ptr<Kernel::Process>> SearchProcessList(
-    const std::vector<std::shared_ptr<Kernel::Process>>& process_list,
-    std::function<bool(const std::shared_ptr<Kernel::Process>&)> predicate) {
+std::optional<Kernel::KProcess*> SearchProcessList(
+    const std::vector<Kernel::KProcess*>& process_list,
+    std::function<bool(Kernel::KProcess*)> predicate) {
     const auto iter = std::find_if(process_list.begin(), process_list.end(), predicate);
 
     if (iter == process_list.end()) {
@@ -30,9 +30,9 @@ std::optional<std::shared_ptr<Kernel::Process>> SearchProcessList(
 }
 
 void GetApplicationPidGeneric(Kernel::HLERequestContext& ctx,
-                              const std::vector<std::shared_ptr<Kernel::Process>>& process_list) {
+                              const std::vector<Kernel::KProcess*>& process_list) {
     const auto process = SearchProcessList(process_list, [](const auto& process) {
-        return process->GetProcessID() == Kernel::Process::ProcessIDMin;
+        return process->GetProcessID() == Kernel::KProcess::ProcessIDMin;
     });
 
     IPC::ResponseBuilder rb{ctx, 4};
@@ -125,8 +125,7 @@ private:
 
 class Info final : public ServiceFramework<Info> {
 public:
-    explicit Info(Core::System& system_,
-                  const std::vector<std::shared_ptr<Kernel::Process>>& process_list_)
+    explicit Info(Core::System& system_, const std::vector<Kernel::KProcess*>& process_list_)
         : ServiceFramework{system_, "pm:info"}, process_list{process_list_} {
         static const FunctionInfo functions[] = {
             {0, &Info::GetTitleId, "GetTitleId"},
@@ -156,7 +155,7 @@ private:
         rb.Push((*process)->GetTitleID());
     }
 
-    const std::vector<std::shared_ptr<Kernel::Process>>& process_list;
+    const std::vector<Kernel::KProcess*>& process_list;
 };
 
 class Shell final : public ServiceFramework<Shell> {
diff --git a/src/core/hle/service/prepo/prepo.cpp b/src/core/hle/service/prepo/prepo.cpp
index d5b3b17a5..c914f8145 100644
--- a/src/core/hle/service/prepo/prepo.cpp
+++ b/src/core/hle/service/prepo/prepo.cpp
@@ -6,7 +6,7 @@
 #include "common/logging/log.h"
 #include "core/core.h"
 #include "core/hle/ipc_helpers.h"
-#include "core/hle/kernel/process.h"
+#include "core/hle/kernel/k_process.h"
 #include "core/hle/service/acc/profile_manager.h"
 #include "core/hle/service/prepo/prepo.h"
 #include "core/hle/service/service.h"
@@ -60,7 +60,7 @@ private:
         const auto process_id = rp.PopRaw<u64>();
 
         const auto data1 = ctx.ReadBuffer(0);
-        const auto data2 = [ctx] {
+        const auto data2 = [&ctx] {
             if (ctx.CanReadBuffer(1)) {
                 return ctx.ReadBuffer(1);
             }
@@ -87,7 +87,7 @@ private:
         const auto process_id = rp.PopRaw<u64>();
 
         const auto data1 = ctx.ReadBuffer(0);
-        const auto data2 = [ctx] {
+        const auto data2 = [&ctx] {
             if (ctx.CanReadBuffer(1)) {
                 return ctx.ReadBuffer(1);
             }
@@ -139,7 +139,7 @@ private:
         const auto title_id = rp.PopRaw<u64>();
 
         const auto data1 = ctx.ReadBuffer(0);
-        const auto data2 = [ctx] {
+        const auto data2 = [&ctx] {
             if (ctx.CanReadBuffer(1)) {
                 return ctx.ReadBuffer(1);
             }
@@ -163,7 +163,7 @@ private:
         const auto title_id = rp.PopRaw<u64>();
 
         const auto data1 = ctx.ReadBuffer(0);
-        const auto data2 = [ctx] {
+        const auto data2 = [&ctx] {
             if (ctx.CanReadBuffer(1)) {
                 return ctx.ReadBuffer(1);
             }
diff --git a/src/core/hle/service/ptm/psm.cpp b/src/core/hle/service/ptm/psm.cpp
index 26ed52273..bb7af9217 100644
--- a/src/core/hle/service/ptm/psm.cpp
+++ b/src/core/hle/service/ptm/psm.cpp
@@ -19,7 +19,8 @@ namespace Service::PSM {
 
 class IPsmSession final : public ServiceFramework<IPsmSession> {
 public:
-    explicit IPsmSession(Core::System& system_) : ServiceFramework{system_, "IPsmSession"} {
+    explicit IPsmSession(Core::System& system_)
+        : ServiceFramework{system_, "IPsmSession"}, state_change_event{system.Kernel()} {
         // clang-format off
         static const FunctionInfo functions[] = {
             {0, &IPsmSession::BindStateChangeEvent, "BindStateChangeEvent"},
@@ -32,28 +33,27 @@ public:
 
         RegisterHandlers(functions);
 
-        state_change_event =
-            Kernel::KEvent::Create(system_.Kernel(), "IPsmSession::state_change_event");
-        state_change_event->Initialize();
+        Kernel::KAutoObject::Create(std::addressof(state_change_event));
+        state_change_event.Initialize("IPsmSession::state_change_event");
     }
 
     ~IPsmSession() override = default;
 
     void SignalChargerTypeChanged() {
         if (should_signal && should_signal_charger_type) {
-            state_change_event->GetWritableEvent()->Signal();
+            state_change_event.GetWritableEvent().Signal();
         }
     }
 
     void SignalPowerSupplyChanged() {
         if (should_signal && should_signal_power_supply) {
-            state_change_event->GetWritableEvent()->Signal();
+            state_change_event.GetWritableEvent().Signal();
         }
     }
 
     void SignalBatteryVoltageStateChanged() {
         if (should_signal && should_signal_battery_voltage) {
-            state_change_event->GetWritableEvent()->Signal();
+            state_change_event.GetWritableEvent().Signal();
         }
     }
 
@@ -65,7 +65,7 @@ private:
 
         IPC::ResponseBuilder rb{ctx, 2, 1};
         rb.Push(RESULT_SUCCESS);
-        rb.PushCopyObjects(state_change_event->GetReadableEvent());
+        rb.PushCopyObjects(state_change_event.GetReadableEvent());
     }
 
     void UnbindStateChangeEvent(Kernel::HLERequestContext& ctx) {
@@ -114,7 +114,7 @@ private:
     bool should_signal_power_supply{};
     bool should_signal_battery_voltage{};
     bool should_signal{};
-    std::shared_ptr<Kernel::KEvent> state_change_event;
+    Kernel::KEvent state_change_event;
 };
 
 class PSM final : public ServiceFramework<PSM> {
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 41a502d8d..00e683c2f 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -11,11 +11,11 @@
 #include "core/core.h"
 #include "core/hle/ipc.h"
 #include "core/hle/ipc_helpers.h"
-#include "core/hle/kernel/client_port.h"
+#include "core/hle/kernel/k_client_port.h"
+#include "core/hle/kernel/k_process.h"
+#include "core/hle/kernel/k_server_port.h"
 #include "core/hle/kernel/k_thread.h"
 #include "core/hle/kernel/kernel.h"
-#include "core/hle/kernel/process.h"
-#include "core/hle/kernel/server_port.h"
 #include "core/hle/service/acc/acc.h"
 #include "core/hle/service/am/am.h"
 #include "core/hle/service/aoc/aoc_u.h"
@@ -116,10 +116,11 @@ void ServiceFrameworkBase::InstallAsNamedPort(Kernel::KernelCore& kernel) {
 
     ASSERT(!port_installed);
 
-    auto [server_port, client_port] =
-        Kernel::ServerPort::CreatePortPair(kernel, max_sessions, service_name);
-    server_port->SetHleHandler(shared_from_this());
-    kernel.AddNamedPort(service_name, std::move(client_port));
+    auto* port = Kernel::KPort::Create(kernel);
+    port->Initialize(max_sessions, false, service_name);
+    port->GetServerPort().SetHleHandler(shared_from_this());
+    kernel.AddNamedPort(service_name, &port->GetClientPort());
+
     port_installed = true;
 }
 
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index 916445517..884951428 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -11,7 +11,6 @@
 #include "common/common_types.h"
 #include "common/spin_lock.h"
 #include "core/hle/kernel/hle_ipc.h"
-#include "core/hle/kernel/object.h"
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 // Namespace Service
@@ -21,11 +20,8 @@ class System;
 }
 
 namespace Kernel {
-class ClientPort;
-class ServerPort;
-class ServerSession;
 class HLERequestContext;
-} // namespace Kernel
+}
 
 namespace Service {
 
diff --git a/src/core/hle/service/set/set_sys.cpp b/src/core/hle/service/set/set_sys.cpp
index 5909fdd85..4f1ffe55f 100644
--- a/src/core/hle/service/set/set_sys.cpp
+++ b/src/core/hle/service/set/set_sys.cpp
@@ -7,7 +7,7 @@
 #include "core/file_sys/errors.h"
 #include "core/file_sys/system_archive/system_version.h"
 #include "core/hle/ipc_helpers.h"
-#include "core/hle/kernel/client_port.h"
+#include "core/hle/kernel/k_client_port.h"
 #include "core/hle/service/filesystem/filesystem.h"
 #include "core/hle/service/set/set_sys.h"
 
diff --git a/src/core/hle/service/sm/controller.cpp b/src/core/hle/service/sm/controller.cpp
index 916177efd..ee026e22f 100644
--- a/src/core/hle/service/sm/controller.cpp
+++ b/src/core/hle/service/sm/controller.cpp
@@ -5,16 +5,16 @@
 #include "common/assert.h"
 #include "common/logging/log.h"
 #include "core/hle/ipc_helpers.h"
-#include "core/hle/kernel/client_session.h"
-#include "core/hle/kernel/server_session.h"
-#include "core/hle/kernel/session.h"
+#include "core/hle/kernel/k_client_session.h"
+#include "core/hle/kernel/k_server_session.h"
+#include "core/hle/kernel/k_session.h"
 #include "core/hle/service/sm/controller.h"
 
 namespace Service::SM {
 
 void Controller::ConvertCurrentObjectToDomain(Kernel::HLERequestContext& ctx) {
     ASSERT_MSG(ctx.Session()->IsSession(), "Session is already a domain");
-    LOG_DEBUG(Service, "called, server_session={}", ctx.Session()->GetObjectId());
+    LOG_DEBUG(Service, "called, server_session={}", ctx.Session()->GetId());
     ctx.Session()->ConvertToDomain();
 
     IPC::ResponseBuilder rb{ctx, 3};
@@ -30,7 +30,7 @@ void Controller::CloneCurrentObject(Kernel::HLERequestContext& ctx) {
 
     IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
     rb.Push(RESULT_SUCCESS);
-    rb.PushMoveObjects(ctx.Session()->GetParent()->Client());
+    rb.PushMoveObjects(ctx.Session()->GetParent()->GetClientSession());
 }
 
 void Controller::CloneCurrentObjectEx(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp
index 94608d529..568effbc9 100644
--- a/src/core/hle/service/sm/sm.cpp
+++ b/src/core/hle/service/sm/sm.cpp
@@ -6,9 +6,12 @@
 #include "common/assert.h"
 #include "core/core.h"
 #include "core/hle/ipc_helpers.h"
-#include "core/hle/kernel/client_port.h"
-#include "core/hle/kernel/client_session.h"
-#include "core/hle/kernel/server_port.h"
+#include "core/hle/kernel/k_client_port.h"
+#include "core/hle/kernel/k_client_session.h"
+#include "core/hle/kernel/k_port.h"
+#include "core/hle/kernel/k_server_port.h"
+#include "core/hle/kernel/k_server_session.h"
+#include "core/hle/kernel/k_session.h"
 #include "core/hle/result.h"
 #include "core/hle/service/sm/controller.h"
 #include "core/hle/service/sm/sm.h"
@@ -47,8 +50,8 @@ void ServiceManager::InstallInterfaces(std::shared_ptr<ServiceManager> self, Cor
     self->controller_interface = std::make_unique<Controller>(system);
 }
 
-ResultVal<std::shared_ptr<Kernel::ServerPort>> ServiceManager::RegisterService(std::string name,
-                                                                               u32 max_sessions) {
+ResultVal<Kernel::KServerPort*> ServiceManager::RegisterService(std::string name,
+                                                                u32 max_sessions) {
 
     CASCADE_CODE(ValidateServiceName(name));
 
@@ -57,11 +60,12 @@ ResultVal<std::shared_ptr<Kernel::ServerPort>> ServiceManager::RegisterService(s
         return ERR_ALREADY_REGISTERED;
     }
 
-    auto [server_port, client_port] =
-        Kernel::ServerPort::CreatePortPair(kernel, max_sessions, name);
+    auto* port = Kernel::KPort::Create(kernel);
+    port->Initialize(max_sessions, false, name);
 
-    registered_services.emplace(std::move(name), std::move(client_port));
-    return MakeResult(std::move(server_port));
+    registered_services.emplace(std::move(name), port);
+
+    return MakeResult(&port->GetServerPort());
 }
 
 ResultCode ServiceManager::UnregisterService(const std::string& name) {
@@ -72,12 +76,14 @@ ResultCode ServiceManager::UnregisterService(const std::string& name) {
         LOG_ERROR(Service_SM, "Server is not registered! service={}", name);
         return ERR_SERVICE_NOT_REGISTERED;
     }
+
+    iter->second->Close();
+
     registered_services.erase(iter);
     return RESULT_SUCCESS;
 }
 
-ResultVal<std::shared_ptr<Kernel::ClientPort>> ServiceManager::GetServicePort(
-    const std::string& name) {
+ResultVal<Kernel::KPort*> ServiceManager::GetServicePort(const std::string& name) {
 
     CASCADE_CODE(ValidateServiceName(name));
     auto it = registered_services.find(name);
@@ -89,13 +95,6 @@ ResultVal<std::shared_ptr<Kernel::ClientPort>> ServiceManager::GetServicePort(
     return MakeResult(it->second);
 }
 
-ResultVal<std::shared_ptr<Kernel::ClientSession>> ServiceManager::ConnectToService(
-    const std::string& name) {
-
-    CASCADE_RESULT(auto client_port, GetServicePort(name));
-    return client_port->Connect();
-}
-
 SM::~SM() = default;
 
 /**
@@ -119,30 +118,32 @@ void SM::GetService(Kernel::HLERequestContext& ctx) {
 
     std::string name(name_buf.begin(), end);
 
-    auto client_port = service_manager->GetServicePort(name);
-    if (client_port.Failed()) {
+    auto result = service_manager->GetServicePort(name);
+    if (result.Failed()) {
         IPC::ResponseBuilder rb{ctx, 2};
-        rb.Push(client_port.Code());
-        LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, client_port.Code().raw);
+        rb.Push(result.Code());
+        LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, result.Code().raw);
         if (name.length() == 0)
             return; // LibNX Fix
         UNIMPLEMENTED();
         return;
     }
 
-    auto [client, server] = Kernel::Session::Create(kernel, name);
+    auto* port = result.Unwrap();
 
-    const auto& server_port = client_port.Unwrap()->GetServerPort();
-    if (server_port->GetHLEHandler()) {
-        server_port->GetHLEHandler()->ClientConnected(server);
+    auto* session = Kernel::KSession::Create(kernel);
+    session->Initialize(&port->GetClientPort(), std::move(name));
+
+    if (port->GetServerPort().GetHLEHandler()) {
+        port->GetServerPort().GetHLEHandler()->ClientConnected(&session->GetServerSession());
     } else {
-        server_port->AppendPendingSession(server);
+        port->EnqueueSession(&session->GetServerSession());
     }
 
-    LOG_DEBUG(Service_SM, "called service={} -> session={}", name, client->GetObjectId());
+    LOG_DEBUG(Service_SM, "called service={} -> session={}", name, session->GetId());
     IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
     rb.Push(RESULT_SUCCESS);
-    rb.PushMoveObjects(std::move(client));
+    rb.PushMoveObjects(session->GetClientSession());
 }
 
 void SM::RegisterService(Kernel::HLERequestContext& ctx) {
@@ -170,7 +171,9 @@ void SM::RegisterService(Kernel::HLERequestContext& ctx) {
 
     IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
     rb.Push(handle.Code());
-    rb.PushMoveObjects(std::move(handle).Unwrap());
+
+    auto server_port = handle.Unwrap();
+    rb.PushMoveObjects(server_port);
 }
 
 void SM::UnregisterService(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h
index 3f46ae44f..af5010c3b 100644
--- a/src/core/hle/service/sm/sm.h
+++ b/src/core/hle/service/sm/sm.h
@@ -10,9 +10,7 @@
 #include <unordered_map>
 
 #include "common/concepts.h"
-#include "core/hle/kernel/client_port.h"
-#include "core/hle/kernel/object.h"
-#include "core/hle/kernel/server_port.h"
+#include "core/hle/kernel/k_port.h"
 #include "core/hle/result.h"
 #include "core/hle/service/service.h"
 
@@ -21,10 +19,11 @@ class System;
 }
 
 namespace Kernel {
-class ClientPort;
-class ClientSession;
+class KClientPort;
+class KClientSession;
 class KernelCore;
-class ServerPort;
+class KPort;
+class KServerPort;
 class SessionRequestHandler;
 } // namespace Kernel
 
@@ -55,11 +54,9 @@ public:
     explicit ServiceManager(Kernel::KernelCore& kernel_);
     ~ServiceManager();
 
-    ResultVal<std::shared_ptr<Kernel::ServerPort>> RegisterService(std::string name,
-                                                                   u32 max_sessions);
+    ResultVal<Kernel::KServerPort*> RegisterService(std::string name, u32 max_sessions);
     ResultCode UnregisterService(const std::string& name);
-    ResultVal<std::shared_ptr<Kernel::ClientPort>> GetServicePort(const std::string& name);
-    ResultVal<std::shared_ptr<Kernel::ClientSession>> ConnectToService(const std::string& name);
+    ResultVal<Kernel::KPort*> GetServicePort(const std::string& name);
 
     template <Common::DerivedFrom<Kernel::SessionRequestHandler> T>
     std::shared_ptr<T> GetService(const std::string& service_name) const {
@@ -68,11 +65,11 @@ public:
             LOG_DEBUG(Service, "Can't find service: {}", service_name);
             return nullptr;
         }
-        auto port = service->second->GetServerPort();
+        auto* port = service->second;
         if (port == nullptr) {
             return nullptr;
         }
-        return std::static_pointer_cast<T>(port->GetHLEHandler());
+        return std::static_pointer_cast<T>(port->GetServerPort().GetHLEHandler());
     }
 
     void InvokeControlRequest(Kernel::HLERequestContext& context);
@@ -81,8 +78,8 @@ private:
     std::weak_ptr<SM> sm_interface;
     std::unique_ptr<Controller> controller_interface;
 
-    /// Map of registered services, retrieved using GetServicePort or ConnectToService.
-    std::unordered_map<std::string, std::shared_ptr<Kernel::ClientPort>> registered_services;
+    /// Map of registered services, retrieved using GetServicePort.
+    std::unordered_map<std::string, Kernel::KPort*> registered_services;
 
     /// Kernel context
     Kernel::KernelCore& kernel;
diff --git a/src/core/hle/service/time/standard_user_system_clock_core.cpp b/src/core/hle/service/time/standard_user_system_clock_core.cpp
index 3172acc5a..7f47b12b8 100644
--- a/src/core/hle/service/time/standard_user_system_clock_core.cpp
+++ b/src/core/hle/service/time/standard_user_system_clock_core.cpp
@@ -4,7 +4,6 @@
 
 #include "common/assert.h"
 #include "core/core.h"
-#include "core/hle/kernel/k_event.h"
 #include "core/hle/service/time/standard_local_system_clock_core.h"
 #include "core/hle/service/time/standard_network_system_clock_core.h"
 #include "core/hle/service/time/standard_user_system_clock_core.h"
@@ -17,10 +16,10 @@ StandardUserSystemClockCore::StandardUserSystemClockCore(
     : SystemClockCore(local_system_clock_core.GetSteadyClockCore()),
       local_system_clock_core{local_system_clock_core},
       network_system_clock_core{network_system_clock_core}, auto_correction_enabled{},
-      auto_correction_time{SteadyClockTimePoint::GetRandom()},
-      auto_correction_event{Kernel::KEvent::Create(
-          system.Kernel(), "StandardUserSystemClockCore:AutoCorrectionEvent")} {
-    auto_correction_event->Initialize();
+      auto_correction_time{SteadyClockTimePoint::GetRandom()}, auto_correction_event{
+                                                                   system.Kernel()} {
+    Kernel::KAutoObject::Create(std::addressof(auto_correction_event));
+    auto_correction_event.Initialize("StandardUserSystemClockCore:AutoCorrectionEvent");
 }
 
 ResultCode StandardUserSystemClockCore::SetAutomaticCorrectionEnabled(Core::System& system,
diff --git a/src/core/hle/service/time/standard_user_system_clock_core.h b/src/core/hle/service/time/standard_user_system_clock_core.h
index 5bc8bf5c2..1bff8a5af 100644
--- a/src/core/hle/service/time/standard_user_system_clock_core.h
+++ b/src/core/hle/service/time/standard_user_system_clock_core.h
@@ -4,6 +4,7 @@
 
 #pragma once
 
+#include "core/hle/kernel/k_event.h"
 #include "core/hle/service/time/clock_types.h"
 #include "core/hle/service/time/system_clock_core.h"
 
@@ -54,7 +55,7 @@ private:
     StandardNetworkSystemClockCore& network_system_clock_core;
     bool auto_correction_enabled{};
     SteadyClockTimePoint auto_correction_time;
-    std::shared_ptr<Kernel::KEvent> auto_correction_event;
+    Kernel::KEvent auto_correction_event;
 };
 
 } // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp
index 32f372d71..e7991012b 100644
--- a/src/core/hle/service/time/time.cpp
+++ b/src/core/hle/service/time/time.cpp
@@ -8,8 +8,7 @@
 #include "core/core_timing_util.h"
 #include "core/hardware_properties.h"
 #include "core/hle/ipc_helpers.h"
-#include "core/hle/kernel/client_port.h"
-#include "core/hle/kernel/client_session.h"
+#include "core/hle/kernel/k_client_port.h"
 #include "core/hle/kernel/k_scheduler.h"
 #include "core/hle/kernel/kernel.h"
 #include "core/hle/service/time/interface.h"
@@ -393,7 +392,7 @@ void Module::Interface::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& c
     LOG_DEBUG(Service_Time, "called");
     IPC::ResponseBuilder rb{ctx, 2, 1};
     rb.Push(RESULT_SUCCESS);
-    rb.PushCopyObjects(SharedFrom(&system.Kernel().GetTimeSharedMem()));
+    rb.PushCopyObjects(&system.Kernel().GetTimeSharedMem());
 }
 
 Module::Interface::Interface(std::shared_ptr<Module> module_, Core::System& system_,
diff --git a/src/core/hle/service/time/time_sharedmemory.cpp b/src/core/hle/service/time/time_sharedmemory.cpp
index 018ce94ed..eb57899f6 100644
--- a/src/core/hle/service/time/time_sharedmemory.cpp
+++ b/src/core/hle/service/time/time_sharedmemory.cpp
@@ -16,16 +16,11 @@ namespace Service::Time {
 static constexpr std::size_t SHARED_MEMORY_SIZE{0x1000};
 
 SharedMemory::SharedMemory(Core::System& system) : system(system) {
-    shared_memory_holder = SharedFrom(&system.Kernel().GetTimeSharedMem());
-    std::memset(shared_memory_holder->GetPointer(), 0, SHARED_MEMORY_SIZE);
+    std::memset(system.Kernel().GetTimeSharedMem().GetPointer(), 0, SHARED_MEMORY_SIZE);
 }
 
 SharedMemory::~SharedMemory() = default;
 
-std::shared_ptr<Kernel::KSharedMemory> SharedMemory::GetSharedMemoryHolder() const {
-    return shared_memory_holder;
-}
-
 void SharedMemory::SetupStandardSteadyClock(const Common::UUID& clock_source_id,
                                             Clock::TimeSpanType current_time_point) {
     const Clock::TimeSpanType ticks_time_span{Clock::TimeSpanType::FromTicks(
@@ -34,22 +29,22 @@ void SharedMemory::SetupStandardSteadyClock(const Common::UUID& clock_source_id,
         static_cast<u64>(current_time_point.nanoseconds - ticks_time_span.nanoseconds),
         clock_source_id};
     shared_memory_format.standard_steady_clock_timepoint.StoreData(
-        shared_memory_holder->GetPointer(), context);
+        system.Kernel().GetTimeSharedMem().GetPointer(), context);
 }
 
 void SharedMemory::UpdateLocalSystemClockContext(const Clock::SystemClockContext& context) {
     shared_memory_format.standard_local_system_clock_context.StoreData(
-        shared_memory_holder->GetPointer(), context);
+        system.Kernel().GetTimeSharedMem().GetPointer(), context);
 }
 
 void SharedMemory::UpdateNetworkSystemClockContext(const Clock::SystemClockContext& context) {
     shared_memory_format.standard_network_system_clock_context.StoreData(
-        shared_memory_holder->GetPointer(), context);
+        system.Kernel().GetTimeSharedMem().GetPointer(), context);
 }
 
 void SharedMemory::SetAutomaticCorrectionEnabled(bool is_enabled) {
     shared_memory_format.standard_user_system_clock_automatic_correction.StoreData(
-        shared_memory_holder->GetPointer(), is_enabled);
+        system.Kernel().GetTimeSharedMem().GetPointer(), is_enabled);
 }
 
 } // namespace Service::Time
diff --git a/src/core/hle/service/time/time_sharedmemory.h b/src/core/hle/service/time/time_sharedmemory.h
index 3bc749114..1ad9a286d 100644
--- a/src/core/hle/service/time/time_sharedmemory.h
+++ b/src/core/hle/service/time/time_sharedmemory.h
@@ -17,9 +17,6 @@ public:
     explicit SharedMemory(Core::System& system);
     ~SharedMemory();
 
-    // Return the shared memory handle
-    std::shared_ptr<Kernel::KSharedMemory> GetSharedMemoryHolder() const;
-
     // TODO(ogniK): We have to properly simulate memory barriers, how are we going to do this?
     template <typename T, std::size_t Offset>
     struct MemoryBarrier {
@@ -63,7 +60,6 @@ public:
     void SetAutomaticCorrectionEnabled(bool is_enabled);
 
 private:
-    std::shared_ptr<Kernel::KSharedMemory> shared_memory_holder;
     Core::System& system;
     Format shared_memory_format{};
 };
diff --git a/src/core/hle/service/vi/display/vi_display.cpp b/src/core/hle/service/vi/display/vi_display.cpp
index ac9e87338..becbd36c1 100644
--- a/src/core/hle/service/vi/display/vi_display.cpp
+++ b/src/core/hle/service/vi/display/vi_display.cpp
@@ -17,10 +17,10 @@
 
 namespace Service::VI {
 
-Display::Display(u64 id, std::string name, Core::System& system) : id{id}, name{std::move(name)} {
-    auto& kernel = system.Kernel();
-    vsync_event = Kernel::KEvent::Create(kernel, fmt::format("Display VSync Event {}", id));
-    vsync_event->Initialize();
+Display::Display(u64 id, std::string name, Core::System& system)
+    : id{id}, name{std::move(name)}, vsync_event{system.Kernel()} {
+    Kernel::KAutoObject::Create(std::addressof(vsync_event));
+    vsync_event.Initialize(fmt::format("Display VSync Event {}", id));
 }
 
 Display::~Display() = default;
@@ -33,12 +33,12 @@ const Layer& Display::GetLayer(std::size_t index) const {
     return *layers.at(index);
 }
 
-std::shared_ptr<Kernel::KReadableEvent> Display::GetVSyncEvent() const {
-    return vsync_event->GetReadableEvent();
+Kernel::KReadableEvent& Display::GetVSyncEvent() {
+    return vsync_event.GetReadableEvent();
 }
 
 void Display::SignalVSyncEvent() {
-    vsync_event->GetWritableEvent()->Signal();
+    vsync_event.GetWritableEvent().Signal();
 }
 
 void Display::CreateLayer(u64 layer_id, NVFlinger::BufferQueue& buffer_queue) {
diff --git a/src/core/hle/service/vi/display/vi_display.h b/src/core/hle/service/vi/display/vi_display.h
index 8340059de..388ce6083 100644
--- a/src/core/hle/service/vi/display/vi_display.h
+++ b/src/core/hle/service/vi/display/vi_display.h
@@ -8,6 +8,7 @@
 #include <string>
 #include <vector>
 
+#include "common/common_funcs.h"
 #include "common/common_types.h"
 
 namespace Kernel {
@@ -24,6 +25,9 @@ class Layer;
 
 /// Represents a single display type
 class Display {
+    YUZU_NON_COPYABLE(Display);
+    YUZU_NON_MOVEABLE(Display);
+
 public:
     /// Constructs a display with a given unique ID and name.
     ///
@@ -33,12 +37,6 @@ public:
     Display(u64 id, std::string name, Core::System& system);
     ~Display();
 
-    Display(const Display&) = delete;
-    Display& operator=(const Display&) = delete;
-
-    Display(Display&&) = default;
-    Display& operator=(Display&&) = default;
-
     /// Gets the unique ID assigned to this display.
     u64 GetID() const {
         return id;
@@ -61,7 +59,7 @@ public:
     const Layer& GetLayer(std::size_t index) const;
 
     /// Gets the readable vsync event.
-    std::shared_ptr<Kernel::KReadableEvent> GetVSyncEvent() const;
+    Kernel::KReadableEvent& GetVSyncEvent();
 
     /// Signals the internal vsync event.
     void SignalVSyncEvent();
@@ -102,7 +100,7 @@ private:
     std::string name;
 
     std::vector<std::shared_ptr<Layer>> layers;
-    std::shared_ptr<Kernel::KEvent> vsync_event;
+    Kernel::KEvent vsync_event;
 };
 
 } // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index 7ae07d072..32e47a43e 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -669,12 +669,10 @@ private:
 
         LOG_WARNING(Service_VI, "(STUBBED) called id={}, unknown={:08X}", id, unknown);
 
-        const auto& buffer_queue = *nv_flinger.FindBufferQueue(id);
-
         // TODO(Subv): Find out what this actually is.
         IPC::ResponseBuilder rb{ctx, 2, 1};
         rb.Push(RESULT_SUCCESS);
-        rb.PushCopyObjects(buffer_queue.GetBufferWaitEvent());
+        rb.PushCopyObjects(nv_flinger.FindBufferQueue(id)->GetBufferWaitEvent());
     }
 
     NVFlinger::NVFlinger& nv_flinger;
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index fed47ecda..42f023258 100644
--- a/src/core/loader/deconstructed_rom_directory.cpp
+++ b/src/core/loader/deconstructed_rom_directory.cpp
@@ -13,8 +13,8 @@
 #include "core/file_sys/patch_manager.h"
 #include "core/file_sys/romfs_factory.h"
 #include "core/hle/kernel/k_page_table.h"
+#include "core/hle/kernel/k_process.h"
 #include "core/hle/kernel/kernel.h"
-#include "core/hle/kernel/process.h"
 #include "core/hle/service/filesystem/filesystem.h"
 #include "core/loader/deconstructed_rom_directory.h"
 #include "core/loader/nso.h"
@@ -88,7 +88,7 @@ FileType AppLoader_DeconstructedRomDirectory::IdentifyType(const FileSys::Virtua
 }
 
 AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirectory::Load(
-    Kernel::Process& process, Core::System& system) {
+    Kernel::KProcess& process, Core::System& system) {
     if (is_loaded) {
         return {ResultStatus::ErrorAlreadyLoaded, {}};
     }
diff --git a/src/core/loader/deconstructed_rom_directory.h b/src/core/loader/deconstructed_rom_directory.h
index 22a4ec5a6..a49a8b001 100644
--- a/src/core/loader/deconstructed_rom_directory.h
+++ b/src/core/loader/deconstructed_rom_directory.h
@@ -44,7 +44,7 @@ public:
         return IdentifyType(file);
     }
 
-    LoadResult Load(Kernel::Process& process, Core::System& system) override;
+    LoadResult Load(Kernel::KProcess& process, Core::System& system) override;
 
     ResultStatus ReadRomFS(FileSys::VirtualFile& out_dir) override;
     ResultStatus ReadIcon(std::vector<u8>& out_buffer) override;
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp
index 627c18c7e..c062a4259 100644
--- a/src/core/loader/elf.cpp
+++ b/src/core/loader/elf.cpp
@@ -11,7 +11,7 @@
 #include "common/logging/log.h"
 #include "core/hle/kernel/code_set.h"
 #include "core/hle/kernel/k_page_table.h"
-#include "core/hle/kernel/process.h"
+#include "core/hle/kernel/k_process.h"
 #include "core/loader/elf.h"
 #include "core/memory.h"
 
@@ -386,7 +386,7 @@ FileType AppLoader_ELF::IdentifyType(const FileSys::VirtualFile& elf_file) {
     return FileType::Error;
 }
 
-AppLoader_ELF::LoadResult AppLoader_ELF::Load(Kernel::Process& process,
+AppLoader_ELF::LoadResult AppLoader_ELF::Load(Kernel::KProcess& process,
                                               [[maybe_unused]] Core::System& system) {
     if (is_loaded) {
         return {ResultStatus::ErrorAlreadyLoaded, {}};
diff --git a/src/core/loader/elf.h b/src/core/loader/elf.h
index 2b86c0b49..890299a20 100644
--- a/src/core/loader/elf.h
+++ b/src/core/loader/elf.h
@@ -32,7 +32,7 @@ public:
         return IdentifyType(file);
     }
 
-    LoadResult Load(Kernel::Process& process, Core::System& system) override;
+    LoadResult Load(Kernel::KProcess& process, Core::System& system) override;
 };
 
 } // namespace Loader
diff --git a/src/core/loader/kip.cpp b/src/core/loader/kip.cpp
index 9b447da2a..3ae9e6e0e 100644
--- a/src/core/loader/kip.cpp
+++ b/src/core/loader/kip.cpp
@@ -7,7 +7,7 @@
 #include "core/file_sys/program_metadata.h"
 #include "core/hle/kernel/code_set.h"
 #include "core/hle/kernel/k_page_table.h"
-#include "core/hle/kernel/process.h"
+#include "core/hle/kernel/k_process.h"
 #include "core/loader/kip.h"
 #include "core/memory.h"
 
@@ -42,7 +42,7 @@ FileType AppLoader_KIP::GetFileType() const {
                                                                          : FileType::Error;
 }
 
-AppLoader::LoadResult AppLoader_KIP::Load(Kernel::Process& process,
+AppLoader::LoadResult AppLoader_KIP::Load(Kernel::KProcess& process,
                                           [[maybe_unused]] Core::System& system) {
     if (is_loaded) {
         return {ResultStatus::ErrorAlreadyLoaded, {}};
diff --git a/src/core/loader/kip.h b/src/core/loader/kip.h
index 2fe636f01..5f914b4a8 100644
--- a/src/core/loader/kip.h
+++ b/src/core/loader/kip.h
@@ -32,7 +32,7 @@ public:
 
     FileType GetFileType() const override;
 
-    LoadResult Load(Kernel::Process& process, Core::System& system) override;
+    LoadResult Load(Kernel::KProcess& process, Core::System& system) override;
 
 private:
     std::unique_ptr<FileSys::KIP> kip;
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp
index e4f5fd40c..11b2d0837 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -11,7 +11,7 @@
 #include "common/logging/log.h"
 #include "common/string_util.h"
 #include "core/core.h"
-#include "core/hle/kernel/process.h"
+#include "core/hle/kernel/k_process.h"
 #include "core/loader/deconstructed_rom_directory.h"
 #include "core/loader/elf.h"
 #include "core/loader/kip.h"
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index bf6db1ab1..9eac11dec 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -25,7 +25,7 @@ class NACP;
 
 namespace Kernel {
 struct AddressMapping;
-class Process;
+class KProcess;
 } // namespace Kernel
 
 namespace Loader {
@@ -165,7 +165,7 @@ public:
      *
      * @return The status result of the operation.
      */
-    virtual LoadResult Load(Kernel::Process& process, Core::System& system) = 0;
+    virtual LoadResult Load(Kernel::KProcess& process, Core::System& system) = 0;
 
     /**
      * Get the code (typically .code section) of the application
diff --git a/src/core/loader/nax.cpp b/src/core/loader/nax.cpp
index f53c3a72c..aceb66414 100644
--- a/src/core/loader/nax.cpp
+++ b/src/core/loader/nax.cpp
@@ -6,7 +6,7 @@
 #include "core/file_sys/content_archive.h"
 #include "core/file_sys/romfs.h"
 #include "core/file_sys/xts_archive.h"
-#include "core/hle/kernel/process.h"
+#include "core/hle/kernel/k_process.h"
 #include "core/loader/nax.h"
 #include "core/loader/nca.h"
 
@@ -41,7 +41,7 @@ FileType AppLoader_NAX::GetFileType() const {
     return IdentifyTypeImpl(*nax);
 }
 
-AppLoader_NAX::LoadResult AppLoader_NAX::Load(Kernel::Process& process, Core::System& system) {
+AppLoader_NAX::LoadResult AppLoader_NAX::Load(Kernel::KProcess& process, Core::System& system) {
     if (is_loaded) {
         return {ResultStatus::ErrorAlreadyLoaded, {}};
     }
diff --git a/src/core/loader/nax.h b/src/core/loader/nax.h
index 68427c1cf..b3a50894f 100644
--- a/src/core/loader/nax.h
+++ b/src/core/loader/nax.h
@@ -37,7 +37,7 @@ public:
 
     FileType GetFileType() const override;
 
-    LoadResult Load(Kernel::Process& process, Core::System& system) override;
+    LoadResult Load(Kernel::KProcess& process, Core::System& system) override;
 
     ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
     u64 ReadRomFSIVFCOffset() const override;
diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp
index 47e7a77a9..418cbf61b 100644
--- a/src/core/loader/nca.cpp
+++ b/src/core/loader/nca.cpp
@@ -9,7 +9,7 @@
 #include "core/core.h"
 #include "core/file_sys/content_archive.h"
 #include "core/file_sys/romfs_factory.h"
-#include "core/hle/kernel/process.h"
+#include "core/hle/kernel/k_process.h"
 #include "core/hle/service/filesystem/filesystem.h"
 #include "core/loader/deconstructed_rom_directory.h"
 #include "core/loader/nca.h"
@@ -32,7 +32,7 @@ FileType AppLoader_NCA::IdentifyType(const FileSys::VirtualFile& nca_file) {
     return FileType::Error;
 }
 
-AppLoader_NCA::LoadResult AppLoader_NCA::Load(Kernel::Process& process, Core::System& system) {
+AppLoader_NCA::LoadResult AppLoader_NCA::Load(Kernel::KProcess& process, Core::System& system) {
     if (is_loaded) {
         return {ResultStatus::ErrorAlreadyLoaded, {}};
     }
diff --git a/src/core/loader/nca.h b/src/core/loader/nca.h
index c9792f390..f2ff080bb 100644
--- a/src/core/loader/nca.h
+++ b/src/core/loader/nca.h
@@ -39,7 +39,7 @@ public:
         return IdentifyType(file);
     }
 
-    LoadResult Load(Kernel::Process& process, Core::System& system) override;
+    LoadResult Load(Kernel::KProcess& process, Core::System& system) override;
 
     ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
     u64 ReadRomFSIVFCOffset() const override;
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
index 0597cfa60..ef54fa574 100644
--- a/src/core/loader/nro.cpp
+++ b/src/core/loader/nro.cpp
@@ -17,8 +17,8 @@
 #include "core/file_sys/vfs_offset.h"
 #include "core/hle/kernel/code_set.h"
 #include "core/hle/kernel/k_page_table.h"
+#include "core/hle/kernel/k_process.h"
 #include "core/hle/kernel/k_thread.h"
-#include "core/hle/kernel/process.h"
 #include "core/hle/service/filesystem/filesystem.h"
 #include "core/loader/nro.h"
 #include "core/loader/nso.h"
@@ -130,7 +130,7 @@ static constexpr u32 PageAlignSize(u32 size) {
     return static_cast<u32>((size + Core::Memory::PAGE_MASK) & ~Core::Memory::PAGE_MASK);
 }
 
-static bool LoadNroImpl(Kernel::Process& process, const std::vector<u8>& data) {
+static bool LoadNroImpl(Kernel::KProcess& process, const std::vector<u8>& data) {
     if (data.size() < sizeof(NroHeader)) {
         return {};
     }
@@ -199,11 +199,11 @@ static bool LoadNroImpl(Kernel::Process& process, const std::vector<u8>& data) {
     return true;
 }
 
-bool AppLoader_NRO::LoadNro(Kernel::Process& process, const FileSys::VfsFile& nro_file) {
+bool AppLoader_NRO::LoadNro(Kernel::KProcess& process, const FileSys::VfsFile& nro_file) {
     return LoadNroImpl(process, nro_file.ReadAllBytes());
 }
 
-AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::Process& process, Core::System& system) {
+AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::KProcess& process, Core::System& system) {
     if (is_loaded) {
         return {ResultStatus::ErrorAlreadyLoaded, {}};
     }
diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h
index 20bbaeb0e..fd453b402 100644
--- a/src/core/loader/nro.h
+++ b/src/core/loader/nro.h
@@ -19,7 +19,7 @@ class NACP;
 }
 
 namespace Kernel {
-class Process;
+class KProcess;
 }
 
 namespace Loader {
@@ -43,7 +43,7 @@ public:
         return IdentifyType(file);
     }
 
-    LoadResult Load(Kernel::Process& process, Core::System& system) override;
+    LoadResult Load(Kernel::KProcess& process, Core::System& system) override;
 
     ResultStatus ReadIcon(std::vector<u8>& buffer) override;
     ResultStatus ReadProgramId(u64& out_program_id) override;
@@ -53,7 +53,7 @@ public:
     bool IsRomFSUpdatable() const override;
 
 private:
-    bool LoadNro(Kernel::Process& process, const FileSys::VfsFile& nro_file);
+    bool LoadNro(Kernel::KProcess& process, const FileSys::VfsFile& nro_file);
 
     std::vector<u8> icon_data;
     std::unique_ptr<FileSys::NACP> nacp;
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index f671afe02..df59412cf 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -17,8 +17,8 @@
 #include "core/file_sys/patch_manager.h"
 #include "core/hle/kernel/code_set.h"
 #include "core/hle/kernel/k_page_table.h"
+#include "core/hle/kernel/k_process.h"
 #include "core/hle/kernel/k_thread.h"
-#include "core/hle/kernel/process.h"
 #include "core/loader/nso.h"
 #include "core/memory.h"
 
@@ -71,7 +71,7 @@ FileType AppLoader_NSO::IdentifyType(const FileSys::VirtualFile& in_file) {
     return FileType::NSO;
 }
 
-std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process, Core::System& system,
+std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::System& system,
                                                const FileSys::VfsFile& nso_file, VAddr load_base,
                                                bool should_pass_arguments, bool load_into_process,
                                                std::optional<FileSys::PatchManager> pm) {
@@ -162,7 +162,7 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process, Core::S
     return load_base + image_size;
 }
 
-AppLoader_NSO::LoadResult AppLoader_NSO::Load(Kernel::Process& process, Core::System& system) {
+AppLoader_NSO::LoadResult AppLoader_NSO::Load(Kernel::KProcess& process, Core::System& system) {
     if (is_loaded) {
         return {ResultStatus::ErrorAlreadyLoaded, {}};
     }
diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h
index 195149b55..f7b61bc2d 100644
--- a/src/core/loader/nso.h
+++ b/src/core/loader/nso.h
@@ -17,7 +17,7 @@ class System;
 }
 
 namespace Kernel {
-class Process;
+class KProcess;
 }
 
 namespace Loader {
@@ -86,12 +86,12 @@ public:
         return IdentifyType(file);
     }
 
-    static std::optional<VAddr> LoadModule(Kernel::Process& process, Core::System& system,
+    static std::optional<VAddr> LoadModule(Kernel::KProcess& process, Core::System& system,
                                            const FileSys::VfsFile& nso_file, VAddr load_base,
                                            bool should_pass_arguments, bool load_into_process,
                                            std::optional<FileSys::PatchManager> pm = {});
 
-    LoadResult Load(Kernel::Process& process, Core::System& system) override;
+    LoadResult Load(Kernel::KProcess& process, Core::System& system) override;
 
     ResultStatus ReadNSOModules(Modules& out_modules) override;
 
diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp
index d7e590f1c..d815a7cd3 100644
--- a/src/core/loader/nsp.cpp
+++ b/src/core/loader/nsp.cpp
@@ -13,7 +13,7 @@
 #include "core/file_sys/patch_manager.h"
 #include "core/file_sys/registered_cache.h"
 #include "core/file_sys/submission_package.h"
-#include "core/hle/kernel/process.h"
+#include "core/hle/kernel/k_process.h"
 #include "core/hle/service/filesystem/filesystem.h"
 #include "core/loader/deconstructed_rom_directory.h"
 #include "core/loader/nca.h"
@@ -79,7 +79,7 @@ FileType AppLoader_NSP::IdentifyType(const FileSys::VirtualFile& nsp_file) {
     return FileType::Error;
 }
 
-AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::Process& process, Core::System& system) {
+AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::KProcess& process, Core::System& system) {
     if (is_loaded) {
         return {ResultStatus::ErrorAlreadyLoaded, {}};
     }
diff --git a/src/core/loader/nsp.h b/src/core/loader/nsp.h
index 1660f1b94..644c0ff58 100644
--- a/src/core/loader/nsp.h
+++ b/src/core/loader/nsp.h
@@ -45,7 +45,7 @@ public:
         return IdentifyType(file);
     }
 
-    LoadResult Load(Kernel::Process& process, Core::System& system) override;
+    LoadResult Load(Kernel::KProcess& process, Core::System& system) override;
 
     ResultStatus ReadRomFS(FileSys::VirtualFile& out_file) override;
     u64 ReadRomFSIVFCOffset() const override;
diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp
index 0125ddf33..635d6ae15 100644
--- a/src/core/loader/xci.cpp
+++ b/src/core/loader/xci.cpp
@@ -13,7 +13,7 @@
 #include "core/file_sys/registered_cache.h"
 #include "core/file_sys/romfs.h"
 #include "core/file_sys/submission_package.h"
-#include "core/hle/kernel/process.h"
+#include "core/hle/kernel/k_process.h"
 #include "core/hle/service/filesystem/filesystem.h"
 #include "core/loader/nca.h"
 #include "core/loader/xci.h"
@@ -56,7 +56,7 @@ FileType AppLoader_XCI::IdentifyType(const FileSys::VirtualFile& xci_file) {
     return FileType::Error;
 }
 
-AppLoader_XCI::LoadResult AppLoader_XCI::Load(Kernel::Process& process, Core::System& system) {
+AppLoader_XCI::LoadResult AppLoader_XCI::Load(Kernel::KProcess& process, Core::System& system) {
     if (is_loaded) {
         return {ResultStatus::ErrorAlreadyLoaded, {}};
     }
diff --git a/src/core/loader/xci.h b/src/core/loader/xci.h
index 7ea8179af..708155c30 100644
--- a/src/core/loader/xci.h
+++ b/src/core/loader/xci.h
@@ -45,7 +45,7 @@ public:
         return IdentifyType(file);
     }
 
-    LoadResult Load(Kernel::Process& process, Core::System& system) override;
+    LoadResult Load(Kernel::KProcess& process, Core::System& system) override;
 
     ResultStatus ReadRomFS(FileSys::VirtualFile& out_file) override;
     u64 ReadRomFSIVFCOffset() const override;
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index b9dd3e275..b4c56e1c1 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -17,8 +17,8 @@
 #include "core/core.h"
 #include "core/device_memory.h"
 #include "core/hle/kernel/k_page_table.h"
+#include "core/hle/kernel/k_process.h"
 #include "core/hle/kernel/physical_memory.h"
-#include "core/hle/kernel/process.h"
 #include "core/memory.h"
 #include "video_core/gpu.h"
 
@@ -30,7 +30,7 @@ namespace Core::Memory {
 struct Memory::Impl {
     explicit Impl(Core::System& system_) : system{system_} {}
 
-    void SetCurrentPageTable(Kernel::Process& process, u32 core_id) {
+    void SetCurrentPageTable(Kernel::KProcess& process, u32 core_id) {
         current_page_table = &process.PageTable().PageTableImpl();
 
         const std::size_t address_space_width = process.PageTable().GetAddressSpaceWidth();
@@ -50,7 +50,7 @@ struct Memory::Impl {
         MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, 0, Common::PageType::Unmapped);
     }
 
-    bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) const {
+    bool IsValidVirtualAddress(const Kernel::KProcess& process, const VAddr vaddr) const {
         const auto& page_table = process.PageTable().PageTableImpl();
         const auto [pointer, type] = page_table.pointers[vaddr >> PAGE_BITS].PointerType();
         return pointer != nullptr || type == Common::PageType::RasterizerCachedMemory;
@@ -82,6 +82,22 @@ struct Memory::Impl {
         return nullptr;
     }
 
+    u8* GetKernelBuffer(VAddr start_vaddr, size_t size) {
+        // TODO(bunnei): This is just a workaround until we have kernel memory layout mapped &
+        // managed. Until then, we use this to allocate and access kernel memory regions.
+
+        auto search = kernel_memory_regions.find(start_vaddr);
+        if (search != kernel_memory_regions.end()) {
+            return search->second.get();
+        }
+
+        std::unique_ptr<u8[]> new_memory_region{new u8[size]};
+        u8* raw_ptr = new_memory_region.get();
+        kernel_memory_regions[start_vaddr] = std::move(new_memory_region);
+
+        return raw_ptr;
+    }
+
     u8 Read8(const VAddr addr) {
         return Read<u8>(addr);
     }
@@ -178,7 +194,7 @@ struct Memory::Impl {
         return string;
     }
 
-    void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_buffer,
+    void ReadBlock(const Kernel::KProcess& process, const VAddr src_addr, void* dest_buffer,
                    const std::size_t size) {
         const auto& page_table = process.PageTable().PageTableImpl();
 
@@ -223,7 +239,7 @@ struct Memory::Impl {
         }
     }
 
-    void ReadBlockUnsafe(const Kernel::Process& process, const VAddr src_addr, void* dest_buffer,
+    void ReadBlockUnsafe(const Kernel::KProcess& process, const VAddr src_addr, void* dest_buffer,
                          const std::size_t size) {
         const auto& page_table = process.PageTable().PageTableImpl();
 
@@ -275,7 +291,7 @@ struct Memory::Impl {
         ReadBlockUnsafe(*system.CurrentProcess(), src_addr, dest_buffer, size);
     }
 
-    void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const void* src_buffer,
+    void WriteBlock(const Kernel::KProcess& process, const VAddr dest_addr, const void* src_buffer,
                     const std::size_t size) {
         const auto& page_table = process.PageTable().PageTableImpl();
         std::size_t remaining_size = size;
@@ -318,7 +334,7 @@ struct Memory::Impl {
         }
     }
 
-    void WriteBlockUnsafe(const Kernel::Process& process, const VAddr dest_addr,
+    void WriteBlockUnsafe(const Kernel::KProcess& process, const VAddr dest_addr,
                           const void* src_buffer, const std::size_t size) {
         const auto& page_table = process.PageTable().PageTableImpl();
         std::size_t remaining_size = size;
@@ -368,7 +384,7 @@ struct Memory::Impl {
         WriteBlockUnsafe(*system.CurrentProcess(), dest_addr, src_buffer, size);
     }
 
-    void ZeroBlock(const Kernel::Process& process, const VAddr dest_addr, const std::size_t size) {
+    void ZeroBlock(const Kernel::KProcess& process, const VAddr dest_addr, const std::size_t size) {
         const auto& page_table = process.PageTable().PageTableImpl();
         std::size_t remaining_size = size;
         std::size_t page_index = dest_addr >> PAGE_BITS;
@@ -413,7 +429,7 @@ struct Memory::Impl {
         ZeroBlock(*system.CurrentProcess(), dest_addr, size);
     }
 
-    void CopyBlock(const Kernel::Process& process, VAddr dest_addr, VAddr src_addr,
+    void CopyBlock(const Kernel::KProcess& process, VAddr dest_addr, VAddr src_addr,
                    const std::size_t size) {
         const auto& page_table = process.PageTable().PageTableImpl();
         std::size_t remaining_size = size;
@@ -711,13 +727,21 @@ struct Memory::Impl {
     }
 
     Common::PageTable* current_page_table = nullptr;
+    std::unordered_map<VAddr, std::unique_ptr<u8[]>> kernel_memory_regions;
     Core::System& system;
 };
 
-Memory::Memory(Core::System& system) : impl{std::make_unique<Impl>(system)} {}
+Memory::Memory(Core::System& system_) : system{system_} {
+    Reset();
+}
+
 Memory::~Memory() = default;
 
-void Memory::SetCurrentPageTable(Kernel::Process& process, u32 core_id) {
+void Memory::Reset() {
+    impl = std::make_unique<Impl>(system);
+}
+
+void Memory::SetCurrentPageTable(Kernel::KProcess& process, u32 core_id) {
     impl->SetCurrentPageTable(process, core_id);
 }
 
@@ -729,7 +753,7 @@ void Memory::UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size) {
     impl->UnmapRegion(page_table, base, size);
 }
 
-bool Memory::IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) const {
+bool Memory::IsValidVirtualAddress(const Kernel::KProcess& process, const VAddr vaddr) const {
     return impl->IsValidVirtualAddress(process, vaddr);
 }
 
@@ -741,6 +765,10 @@ u8* Memory::GetPointer(VAddr vaddr) {
     return impl->GetPointer(vaddr);
 }
 
+u8* Memory::GetKernelBuffer(VAddr start_vaddr, size_t size) {
+    return impl->GetKernelBuffer(start_vaddr, size);
+}
+
 const u8* Memory::GetPointer(VAddr vaddr) const {
     return impl->GetPointer(vaddr);
 }
@@ -801,7 +829,7 @@ std::string Memory::ReadCString(VAddr vaddr, std::size_t max_length) {
     return impl->ReadCString(vaddr, max_length);
 }
 
-void Memory::ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_buffer,
+void Memory::ReadBlock(const Kernel::KProcess& process, const VAddr src_addr, void* dest_buffer,
                        const std::size_t size) {
     impl->ReadBlock(process, src_addr, dest_buffer, size);
 }
@@ -810,7 +838,7 @@ void Memory::ReadBlock(const VAddr src_addr, void* dest_buffer, const std::size_
     impl->ReadBlock(src_addr, dest_buffer, size);
 }
 
-void Memory::ReadBlockUnsafe(const Kernel::Process& process, const VAddr src_addr,
+void Memory::ReadBlockUnsafe(const Kernel::KProcess& process, const VAddr src_addr,
                              void* dest_buffer, const std::size_t size) {
     impl->ReadBlockUnsafe(process, src_addr, dest_buffer, size);
 }
@@ -819,7 +847,7 @@ void Memory::ReadBlockUnsafe(const VAddr src_addr, void* dest_buffer, const std:
     impl->ReadBlockUnsafe(src_addr, dest_buffer, size);
 }
 
-void Memory::WriteBlock(const Kernel::Process& process, VAddr dest_addr, const void* src_buffer,
+void Memory::WriteBlock(const Kernel::KProcess& process, VAddr dest_addr, const void* src_buffer,
                         std::size_t size) {
     impl->WriteBlock(process, dest_addr, src_buffer, size);
 }
@@ -828,7 +856,7 @@ void Memory::WriteBlock(const VAddr dest_addr, const void* src_buffer, const std
     impl->WriteBlock(dest_addr, src_buffer, size);
 }
 
-void Memory::WriteBlockUnsafe(const Kernel::Process& process, VAddr dest_addr,
+void Memory::WriteBlockUnsafe(const Kernel::KProcess& process, VAddr dest_addr,
                               const void* src_buffer, std::size_t size) {
     impl->WriteBlockUnsafe(process, dest_addr, src_buffer, size);
 }
@@ -838,7 +866,7 @@ void Memory::WriteBlockUnsafe(const VAddr dest_addr, const void* src_buffer,
     impl->WriteBlockUnsafe(dest_addr, src_buffer, size);
 }
 
-void Memory::ZeroBlock(const Kernel::Process& process, VAddr dest_addr, std::size_t size) {
+void Memory::ZeroBlock(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size) {
     impl->ZeroBlock(process, dest_addr, size);
 }
 
@@ -846,7 +874,7 @@ void Memory::ZeroBlock(VAddr dest_addr, std::size_t size) {
     impl->ZeroBlock(dest_addr, size);
 }
 
-void Memory::CopyBlock(const Kernel::Process& process, VAddr dest_addr, VAddr src_addr,
+void Memory::CopyBlock(const Kernel::KProcess& process, VAddr dest_addr, VAddr src_addr,
                        const std::size_t size) {
     impl->CopyBlock(process, dest_addr, src_addr, size);
 }
diff --git a/src/core/memory.h b/src/core/memory.h
index 6d34fcfe2..345fd870d 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -19,7 +19,7 @@ class System;
 
 namespace Kernel {
 class PhysicalMemory;
-class Process;
+class KProcess;
 } // namespace Kernel
 
 namespace Core::Memory {
@@ -58,12 +58,17 @@ public:
     Memory(Memory&&) = default;
     Memory& operator=(Memory&&) = default;
 
+    /**
+     * Resets the state of the Memory system.
+     */
+    void Reset();
+
     /**
      * Changes the currently active page table to that of the given process instance.
      *
      * @param process The process to use the page table of.
      */
-    void SetCurrentPageTable(Kernel::Process& process, u32 core_id);
+    void SetCurrentPageTable(Kernel::KProcess& process, u32 core_id);
 
     /**
      * Maps an allocated buffer onto a region of the emulated process address space.
@@ -94,7 +99,7 @@ public:
      *
      * @returns True if the given virtual address is valid, false otherwise.
      */
-    bool IsValidVirtualAddress(const Kernel::Process& process, VAddr vaddr) const;
+    bool IsValidVirtualAddress(const Kernel::KProcess& process, VAddr vaddr) const;
 
     /**
      * Checks whether or not the supplied address is a valid virtual
@@ -116,6 +121,15 @@ public:
      */
     u8* GetPointer(VAddr vaddr);
 
+    /**
+     * Gets a pointer to the start of a kernel heap allocated memory region. Will allocate one if it
+     * does not already exist.
+     *
+     * @param start_vaddr Start virtual address for the memory region.
+     * @param size Size of the memory region.
+     */
+    u8* GetKernelBuffer(VAddr start_vaddr, size_t size);
+
     template <typename T>
     T* GetPointer(VAddr vaddr) {
         return reinterpret_cast<T*>(GetPointer(vaddr));
@@ -319,7 +333,7 @@ public:
      * @post The range [dest_buffer, size) contains the read bytes from the
      *       process' address space.
      */
-    void ReadBlock(const Kernel::Process& process, VAddr src_addr, void* dest_buffer,
+    void ReadBlock(const Kernel::KProcess& process, VAddr src_addr, void* dest_buffer,
                    std::size_t size);
 
     /**
@@ -340,7 +354,7 @@ public:
      * @post The range [dest_buffer, size) contains the read bytes from the
      *       process' address space.
      */
-    void ReadBlockUnsafe(const Kernel::Process& process, VAddr src_addr, void* dest_buffer,
+    void ReadBlockUnsafe(const Kernel::KProcess& process, VAddr src_addr, void* dest_buffer,
                          std::size_t size);
 
     /**
@@ -400,7 +414,7 @@ public:
      *       and will mark that region as invalidated to caches that the active
      *       graphics backend may be maintaining over the course of execution.
      */
-    void WriteBlock(const Kernel::Process& process, VAddr dest_addr, const void* src_buffer,
+    void WriteBlock(const Kernel::KProcess& process, VAddr dest_addr, const void* src_buffer,
                     std::size_t size);
 
     /**
@@ -420,7 +434,7 @@ public:
      *       will be ignored and an error will be logged.
      *
      */
-    void WriteBlockUnsafe(const Kernel::Process& process, VAddr dest_addr, const void* src_buffer,
+    void WriteBlockUnsafe(const Kernel::KProcess& process, VAddr dest_addr, const void* src_buffer,
                           std::size_t size);
 
     /**
@@ -472,7 +486,7 @@ public:
      * @post The range [dest_addr, size) within the process' address space is
      *       filled with zeroes.
      */
-    void ZeroBlock(const Kernel::Process& process, VAddr dest_addr, std::size_t size);
+    void ZeroBlock(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size);
 
     /**
      * Fills the specified address range within the current process' address space with zeroes.
@@ -497,7 +511,7 @@ public:
      * @post The range [dest_addr, size) within the process' address space contains the
      *       same data within the range [src_addr, size).
      */
-    void CopyBlock(const Kernel::Process& process, VAddr dest_addr, VAddr src_addr,
+    void CopyBlock(const Kernel::KProcess& process, VAddr dest_addr, VAddr src_addr,
                    std::size_t size);
 
     /**
@@ -524,6 +538,8 @@ public:
     void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached);
 
 private:
+    Core::System& system;
+
     struct Impl;
     std::unique_ptr<Impl> impl;
 };
diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp
index 7643b7846..0f5ef7954 100644
--- a/src/core/memory/cheat_engine.cpp
+++ b/src/core/memory/cheat_engine.cpp
@@ -11,7 +11,7 @@
 #include "core/core_timing_util.h"
 #include "core/hardware_properties.h"
 #include "core/hle/kernel/k_page_table.h"
-#include "core/hle/kernel/process.h"
+#include "core/hle/kernel/k_process.h"
 #include "core/hle/service/hid/controllers/npad.h"
 #include "core/hle/service/hid/hid.h"
 #include "core/hle/service/sm/sm.h"
diff --git a/src/core/reporter.cpp b/src/core/reporter.cpp
index 311d4dda8..896add892 100644
--- a/src/core/reporter.cpp
+++ b/src/core/reporter.cpp
@@ -19,7 +19,7 @@
 #include "core/core.h"
 #include "core/hle/kernel/hle_ipc.h"
 #include "core/hle/kernel/k_page_table.h"
-#include "core/hle/kernel/process.h"
+#include "core/hle/kernel/k_process.h"
 #include "core/hle/result.h"
 #include "core/memory.h"
 #include "core/reporter.h"
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp
index 4eb71efbd..eb58ac6b6 100644
--- a/src/video_core/memory_manager.cpp
+++ b/src/video_core/memory_manager.cpp
@@ -7,7 +7,7 @@
 #include "common/logging/log.h"
 #include "core/core.h"
 #include "core/hle/kernel/k_page_table.h"
-#include "core/hle/kernel/process.h"
+#include "core/hle/kernel/k_process.h"
 #include "core/memory.h"
 #include "video_core/gpu.h"
 #include "video_core/memory_manager.h"
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 0863904e9..a5dbb9adf 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -19,7 +19,7 @@
 #include "common/scope_exit.h"
 #include "common/settings.h"
 #include "core/core.h"
-#include "core/hle/kernel/process.h"
+#include "core/hle/kernel/k_process.h"
 #include "core/memory.h"
 #include "video_core/engines/kepler_compute.h"
 #include "video_core/engines/maxwell_3d.h"
diff --git a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
index 97fb11ac6..dbcb751cb 100644
--- a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
@@ -15,7 +15,7 @@
 #include "common/settings.h"
 #include "common/zstd_compression.h"
 #include "core/core.h"
-#include "core/hle/kernel/process.h"
+#include "core/hle/kernel/k_process.h"
 #include "video_core/engines/shader_type.h"
 #include "video_core/renderer_opengl/gl_shader_cache.h"
 #include "video_core/renderer_opengl/gl_shader_disk_cache.h"
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 7ff9491f4..86495803e 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -32,7 +32,7 @@
 #include "common/settings.h"
 #include "core/core.h"
 #include "core/frontend/framebuffer_layout.h"
-#include "core/hle/kernel/process.h"
+#include "core/hle/kernel/k_process.h"
 #include "input_common/keyboard.h"
 #include "input_common/main.h"
 #include "input_common/mouse/mouse_input.h"
diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp
index 3bca6277b..bdfda6c54 100644
--- a/src/yuzu/debugger/wait_tree.cpp
+++ b/src/yuzu/debugger/wait_tree.cpp
@@ -12,12 +12,13 @@
 #include "common/assert.h"
 #include "core/arm/arm_interface.h"
 #include "core/core.h"
-#include "core/hle/kernel/handle_table.h"
+#include "core/hle/kernel/k_class_token.h"
+#include "core/hle/kernel/k_handle_table.h"
+#include "core/hle/kernel/k_process.h"
 #include "core/hle/kernel/k_readable_event.h"
 #include "core/hle/kernel/k_scheduler.h"
 #include "core/hle/kernel/k_synchronization_object.h"
 #include "core/hle/kernel/k_thread.h"
-#include "core/hle/kernel/process.h"
 #include "core/hle/kernel/svc_common.h"
 #include "core/hle/kernel/svc_types.h"
 #include "core/memory.h"
@@ -91,7 +92,7 @@ std::size_t WaitTreeItem::Row() const {
 std::vector<std::unique_ptr<WaitTreeThread>> WaitTreeItem::MakeThreadItemList() {
     std::vector<std::unique_ptr<WaitTreeThread>> item_list;
     std::size_t row = 0;
-    auto add_threads = [&](const std::vector<std::shared_ptr<Kernel::KThread>>& threads) {
+    auto add_threads = [&](const std::vector<Kernel::KThread*>& threads) {
         for (std::size_t i = 0; i < threads.size(); ++i) {
             if (threads[i]->GetThreadTypeForDebugging() == Kernel::ThreadType::User) {
                 item_list.push_back(std::make_unique<WaitTreeThread>(*threads[i]));
@@ -114,11 +115,11 @@ QString WaitTreeText::GetText() const {
     return text;
 }
 
-WaitTreeMutexInfo::WaitTreeMutexInfo(VAddr mutex_address, const Kernel::HandleTable& handle_table)
+WaitTreeMutexInfo::WaitTreeMutexInfo(VAddr mutex_address, const Kernel::KHandleTable& handle_table)
     : mutex_address(mutex_address) {
     mutex_value = Core::System::GetInstance().Memory().Read32(mutex_address);
     owner_handle = static_cast<Kernel::Handle>(mutex_value & Kernel::Svc::HandleWaitMask);
-    owner = handle_table.Get<Kernel::KThread>(owner_handle);
+    owner = handle_table.GetObject<Kernel::KThread>(owner_handle).GetPointerUnsafe();
 }
 
 WaitTreeMutexInfo::~WaitTreeMutexInfo() = default;
@@ -183,18 +184,20 @@ bool WaitTreeExpandableItem::IsExpandable() const {
 }
 
 QString WaitTreeSynchronizationObject::GetText() const {
-    return tr("[%1]%2 %3")
-        .arg(object.GetObjectId())
-        .arg(QString::fromStdString(object.GetTypeName()),
+    return tr("[%1] %2 %3")
+        .arg(object.GetId())
+        .arg(QString::fromStdString(object.GetTypeObj().GetName()),
              QString::fromStdString(object.GetName()));
 }
 
 std::unique_ptr<WaitTreeSynchronizationObject> WaitTreeSynchronizationObject::make(
     const Kernel::KSynchronizationObject& object) {
-    switch (object.GetHandleType()) {
-    case Kernel::HandleType::ReadableEvent:
+    const auto type =
+        static_cast<Kernel::KClassTokenGenerator::ObjectType>(object.GetTypeObj().GetClassToken());
+    switch (type) {
+    case Kernel::KClassTokenGenerator::ObjectType::KReadableEvent:
         return std::make_unique<WaitTreeEvent>(static_cast<const Kernel::KReadableEvent&>(object));
-    case Kernel::HandleType::Thread:
+    case Kernel::KClassTokenGenerator::ObjectType::KThread:
         return std::make_unique<WaitTreeThread>(static_cast<const Kernel::KThread&>(object));
     default:
         return std::make_unique<WaitTreeSynchronizationObject>(object);
@@ -204,12 +207,13 @@ std::unique_ptr<WaitTreeSynchronizationObject> WaitTreeSynchronizationObject::ma
 std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeSynchronizationObject::GetChildren() const {
     std::vector<std::unique_ptr<WaitTreeItem>> list;
 
-    const auto& threads = object.GetWaitingThreadsForDebugging();
+    auto threads = object.GetWaitingThreadsForDebugging();
     if (threads.empty()) {
         list.push_back(std::make_unique<WaitTreeText>(tr("waited by no thread")));
     } else {
-        list.push_back(std::make_unique<WaitTreeThreadList>(threads));
+        list.push_back(std::make_unique<WaitTreeThreadList>(std::move(threads)));
     }
+
     return list;
 }
 
@@ -377,8 +381,8 @@ WaitTreeEvent::WaitTreeEvent(const Kernel::KReadableEvent& object)
     : WaitTreeSynchronizationObject(object) {}
 WaitTreeEvent::~WaitTreeEvent() = default;
 
-WaitTreeThreadList::WaitTreeThreadList(const std::vector<Kernel::KThread*>& list)
-    : thread_list(list) {}
+WaitTreeThreadList::WaitTreeThreadList(std::vector<Kernel::KThread*>&& list)
+    : thread_list(std::move(list)) {}
 WaitTreeThreadList::~WaitTreeThreadList() = default;
 
 QString WaitTreeThreadList::GetText() const {
diff --git a/src/yuzu/debugger/wait_tree.h b/src/yuzu/debugger/wait_tree.h
index 3da2fdfd2..d450345df 100644
--- a/src/yuzu/debugger/wait_tree.h
+++ b/src/yuzu/debugger/wait_tree.h
@@ -11,13 +11,15 @@
 #include <QAbstractItemModel>
 #include <QDockWidget>
 #include <QTreeView>
+
 #include "common/common_types.h"
-#include "core/hle/kernel/object.h"
+#include "core/hle/kernel/k_auto_object.h"
+#include "core/hle/kernel/svc_common.h"
 
 class EmuThread;
 
 namespace Kernel {
-class HandleTable;
+class KHandleTable;
 class KReadableEvent;
 class KSynchronizationObject;
 class KThread;
@@ -73,17 +75,17 @@ public:
 class WaitTreeMutexInfo : public WaitTreeExpandableItem {
     Q_OBJECT
 public:
-    explicit WaitTreeMutexInfo(VAddr mutex_address, const Kernel::HandleTable& handle_table);
+    explicit WaitTreeMutexInfo(VAddr mutex_address, const Kernel::KHandleTable& handle_table);
     ~WaitTreeMutexInfo() override;
 
     QString GetText() const override;
     std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override;
 
 private:
-    VAddr mutex_address;
-    u32 mutex_value;
-    Kernel::Handle owner_handle;
-    std::shared_ptr<Kernel::KThread> owner;
+    VAddr mutex_address{};
+    u32 mutex_value{};
+    Kernel::Handle owner_handle{};
+    Kernel::KThread* owner{};
 };
 
 class WaitTreeCallstack : public WaitTreeExpandableItem {
@@ -149,14 +151,14 @@ public:
 class WaitTreeThreadList : public WaitTreeExpandableItem {
     Q_OBJECT
 public:
-    explicit WaitTreeThreadList(const std::vector<Kernel::KThread*>& list);
+    explicit WaitTreeThreadList(std::vector<Kernel::KThread*>&& list);
     ~WaitTreeThreadList() override;
 
     QString GetText() const override;
     std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override;
 
 private:
-    const std::vector<Kernel::KThread*>& thread_list;
+    std::vector<Kernel::KThread*> thread_list;
 };
 
 class WaitTreeModel : public QAbstractItemModel {
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 00d4cfe67..9e72acbf7 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -92,7 +92,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
 #include "core/file_sys/romfs.h"
 #include "core/file_sys/savedata_factory.h"
 #include "core/file_sys/submission_package.h"
-#include "core/hle/kernel/process.h"
+#include "core/hle/kernel/k_process.h"
 #include "core/hle/service/am/am.h"
 #include "core/hle/service/filesystem/filesystem.h"
 #include "core/hle/service/nfp/nfp.h"
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index 4871ac3bb..e2812ca61 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -27,7 +27,7 @@
 #include "core/crypto/key_manager.h"
 #include "core/file_sys/registered_cache.h"
 #include "core/file_sys/vfs_real.h"
-#include "core/hle/kernel/process.h"
+#include "core/hle/kernel/k_process.h"
 #include "core/hle/service/filesystem/filesystem.h"
 #include "core/loader/loader.h"
 #include "core/telemetry_session.h"