From 6d4f411c08146696caf45a29db2a4aa25a55c639 Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Sat, 29 Oct 2022 13:35:50 -0700
Subject: [PATCH] core: hle: kernel: Add KPageTableSlabHeap.

---
 src/core/CMakeLists.txt                      |  1 +
 src/core/hle/kernel/k_page_table_slab_heap.h | 93 ++++++++++++++++++++
 2 files changed, 94 insertions(+)
 create mode 100644 src/core/hle/kernel/k_page_table_slab_heap.h

diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 457a0bfd2b..199e0e69f8 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -224,6 +224,7 @@ add_library(core STATIC
     hle/kernel/k_page_group.h
     hle/kernel/k_page_table.cpp
     hle/kernel/k_page_table.h
+    hle/kernel/k_page_table_slab_heap.h
     hle/kernel/k_port.cpp
     hle/kernel/k_port.h
     hle/kernel/k_priority_queue.h
diff --git a/src/core/hle/kernel/k_page_table_slab_heap.h b/src/core/hle/kernel/k_page_table_slab_heap.h
new file mode 100644
index 0000000000..a9543cbd0b
--- /dev/null
+++ b/src/core/hle/kernel/k_page_table_slab_heap.h
@@ -0,0 +1,93 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <array>
+#include <vector>
+
+#include "common/common_types.h"
+#include "core/hle/kernel/k_dynamic_slab_heap.h"
+#include "core/hle/kernel/slab_helpers.h"
+
+namespace Kernel {
+
+namespace impl {
+
+class PageTablePage {
+public:
+    // Do not initialize anything.
+    PageTablePage() = default;
+
+private:
+    std::array<u8, PageSize> m_buffer{};
+};
+static_assert(sizeof(PageTablePage) == PageSize);
+
+} // namespace impl
+
+class KPageTableSlabHeap : public KDynamicSlabHeap<impl::PageTablePage, true> {
+public:
+    using RefCount = u16;
+    static constexpr size_t PageTableSize = sizeof(impl::PageTablePage);
+    static_assert(PageTableSize == PageSize);
+
+public:
+    KPageTableSlabHeap() = default;
+
+    static constexpr size_t CalculateReferenceCountSize(size_t size) {
+        return (size / PageSize) * sizeof(RefCount);
+    }
+
+    void Initialize(KDynamicPageManager* page_allocator, size_t object_count, RefCount* rc) {
+        BaseHeap::Initialize(page_allocator, object_count);
+        this->Initialize(rc);
+    }
+
+    RefCount GetRefCount(VAddr addr) {
+        ASSERT(this->IsInRange(addr));
+        return *this->GetRefCountPointer(addr);
+    }
+
+    void Open(VAddr addr, int count) {
+        ASSERT(this->IsInRange(addr));
+
+        *this->GetRefCountPointer(addr) += static_cast<RefCount>(count);
+
+        ASSERT(this->GetRefCount(addr) > 0);
+    }
+
+    bool Close(VAddr addr, int count) {
+        ASSERT(this->IsInRange(addr));
+        ASSERT(this->GetRefCount(addr) >= count);
+
+        *this->GetRefCountPointer(addr) -= static_cast<RefCount>(count);
+        return this->GetRefCount(addr) == 0;
+    }
+
+    bool IsInPageTableHeap(VAddr addr) const {
+        return this->IsInRange(addr);
+    }
+
+private:
+    void Initialize([[maybe_unused]] RefCount* rc) {
+        // TODO(bunnei): Use rc once we support kernel virtual memory allocations.
+        const auto count = this->GetSize() / PageSize;
+        m_ref_counts.resize(count);
+
+        for (size_t i = 0; i < count; i++) {
+            m_ref_counts[i] = 0;
+        }
+    }
+
+    RefCount* GetRefCountPointer(VAddr addr) {
+        return m_ref_counts.data() + ((addr - this->GetAddress()) / PageSize);
+    }
+
+private:
+    using BaseHeap = KDynamicSlabHeap<impl::PageTablePage, true>;
+
+    std::vector<RefCount> m_ref_counts;
+};
+
+} // namespace Kernel