diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index c28abc24c1..aa83b87332 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -161,6 +161,8 @@ add_library(core STATIC
     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_affinity_mask.h
     hle/kernel/k_condition_variable.cpp
     hle/kernel/k_condition_variable.h
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 0000000000..dbe237f09b
--- /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 0000000000..567dad204d
--- /dev/null
+++ b/src/core/hle/kernel/k_auto_object.h
@@ -0,0 +1,290 @@
+// 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_class_token.h"
+
+namespace Kernel {
+
+class Process;
+
+#define KERNEL_AUTOOBJECT_TRAITS(CLASS, BASE_CLASS)                                                \
+    NON_COPYABLE(CLASS);                                                                           \
+    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() {                                                            \
+        return GetStaticTypeName();                                                                \
+    }                                                                                              \
+                                                                                                   \
+private:
+
+class KAutoObject {
+protected:
+    class TypeObj {
+    private:
+        const char* m_name;
+        ClassTokenType m_class_token;
+
+    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) {
+            return this->GetClassToken() == rhs.GetClassToken();
+        }
+
+        constexpr bool operator!=(const TypeObj& rhs) {
+            return this->GetClassToken() != rhs.GetClassToken();
+        }
+
+        constexpr bool IsDerivedFrom(const TypeObj& rhs) {
+            return (this->GetClassToken() | rhs.GetClassToken()) == this->GetClassToken();
+        }
+    };
+
+private:
+    KERNEL_AUTOOBJECT_TRAITS(KAutoObject, KAutoObject);
+
+private:
+    std::atomic<u32> m_ref_count;
+
+public:
+    static KAutoObject* Create(KAutoObject* ptr);
+
+public:
+    constexpr explicit KAutoObject() : m_ref_count(0) {}
+    virtual ~KAutoObject() {}
+
+    // 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() {
+        UNIMPLEMENTED();
+    }
+
+    virtual Process* 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<Derived>::value);
+        using DerivedType = typename std::remove_pointer<Derived>::type;
+
+        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<Derived>::value);
+        using DerivedType = typename std::remove_pointer<Derived>::type;
+
+        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();
+        }
+    }
+};
+
+class KAutoObjectWithListContainer;
+
+class KAutoObjectWithList : public KAutoObject {
+private:
+    friend class KAutoObjectWithListContainer;
+
+private:
+    Common::IntrusiveRedBlackTreeNode list_node;
+
+public:
+    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);
+    }
+};
+
+template <typename T>
+class KScopedAutoObject {
+    NON_COPYABLE(KScopedAutoObject);
+
+private:
+    template <typename U>
+    friend class KScopedAutoObject;
+
+private:
+    T* m_obj;
+
+private:
+    constexpr void Swap(KScopedAutoObject& rhs) {
+        std::swap(m_obj, rhs.m_obj);
+    }
+
+public:
+    constexpr KScopedAutoObject() : m_obj(nullptr) { // ...
+    }
+    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* 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;
+    }
+};
+
+} // namespace Kernel