Просмотр исходного кода

core: hle: kernel: Add KDynamicPageManager.

bunnei 3 лет назад
Родитель
Сommit
345b9e6a08
2 измененных файлов с 137 добавлено и 0 удалено
  1. 1 0
      src/core/CMakeLists.txt
  2. 136 0
      src/core/hle/kernel/k_dynamic_page_manager.h

+ 1 - 0
src/core/CMakeLists.txt

@@ -190,6 +190,7 @@ add_library(core STATIC
     hle/kernel/k_code_memory.h
     hle/kernel/k_code_memory.h
     hle/kernel/k_condition_variable.cpp
     hle/kernel/k_condition_variable.cpp
     hle/kernel/k_condition_variable.h
     hle/kernel/k_condition_variable.h
+    hle/kernel/k_dynamic_page_manager.h
     hle/kernel/k_event.cpp
     hle/kernel/k_event.cpp
     hle/kernel/k_event.h
     hle/kernel/k_event.h
     hle/kernel/k_handle_table.cpp
     hle/kernel/k_handle_table.cpp

+ 136 - 0
src/core/hle/kernel/k_dynamic_page_manager.h

@@ -0,0 +1,136 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "common/alignment.h"
+#include "common/common_types.h"
+#include "core/hle/kernel/k_page_bitmap.h"
+#include "core/hle/kernel/k_spin_lock.h"
+#include "core/hle/kernel/memory_types.h"
+#include "core/hle/kernel/svc_results.h"
+
+namespace Kernel {
+
+class KDynamicPageManager {
+public:
+    class PageBuffer {
+    private:
+        u8 m_buffer[PageSize];
+    };
+    static_assert(sizeof(PageBuffer) == PageSize);
+
+public:
+    KDynamicPageManager() = default;
+
+    template <typename T>
+    T* GetPointer(VAddr addr) {
+        return reinterpret_cast<T*>(m_backing_memory.data() + (addr - m_address));
+    }
+
+    template <typename T>
+    const T* GetPointer(VAddr addr) const {
+        return reinterpret_cast<T*>(m_backing_memory.data() + (addr - m_address));
+    }
+
+    Result Initialize(VAddr addr, size_t sz) {
+        // We need to have positive size.
+        R_UNLESS(sz > 0, ResultOutOfMemory);
+        m_backing_memory.resize(sz);
+
+        // Calculate management overhead.
+        const size_t management_size =
+            KPageBitmap::CalculateManagementOverheadSize(sz / sizeof(PageBuffer));
+        const size_t allocatable_size = sz - management_size;
+
+        // Set tracking fields.
+        m_address = addr;
+        m_size = Common::AlignDown(allocatable_size, sizeof(PageBuffer));
+        m_count = allocatable_size / sizeof(PageBuffer);
+        R_UNLESS(m_count > 0, ResultOutOfMemory);
+
+        // Clear the management region.
+        u64* management_ptr = GetPointer<u64>(m_address + allocatable_size);
+        std::memset(management_ptr, 0, management_size);
+
+        // Initialize the bitmap.
+        m_page_bitmap.Initialize(management_ptr, m_count);
+
+        // Free the pages to the bitmap.
+        for (size_t i = 0; i < m_count; i++) {
+            // Ensure the freed page is all-zero.
+            std::memset(GetPointer<PageBuffer>(m_address) + i, 0, PageSize);
+
+            // Set the bit for the free page.
+            m_page_bitmap.SetBit(i);
+        }
+
+        return ResultSuccess;
+    }
+
+    VAddr GetAddress() const {
+        return m_address;
+    }
+    size_t GetSize() const {
+        return m_size;
+    }
+    size_t GetUsed() const {
+        return m_used;
+    }
+    size_t GetPeak() const {
+        return m_peak;
+    }
+    size_t GetCount() const {
+        return m_count;
+    }
+
+    PageBuffer* Allocate() {
+        // Take the lock.
+        // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
+        KScopedSpinLock lk(m_lock);
+
+        // Find a random free block.
+        s64 soffset = m_page_bitmap.FindFreeBlock(true);
+        if (soffset < 0) [[unlikely]] {
+            return nullptr;
+        }
+
+        const size_t offset = static_cast<size_t>(soffset);
+
+        // Update our tracking.
+        m_page_bitmap.ClearBit(offset);
+        m_peak = std::max(m_peak, (++m_used));
+
+        return GetPointer<PageBuffer>(m_address) + offset;
+    }
+
+    void Free(PageBuffer* pb) {
+        // Ensure all pages in the heap are zero.
+        std::memset(pb, 0, PageSize);
+
+        // Take the lock.
+        // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
+        KScopedSpinLock lk(m_lock);
+
+        // Set the bit for the free page.
+        size_t offset = (reinterpret_cast<uintptr_t>(pb) - m_address) / sizeof(PageBuffer);
+        m_page_bitmap.SetBit(offset);
+
+        // Decrement our used count.
+        --m_used;
+    }
+
+private:
+    KSpinLock m_lock;
+    KPageBitmap m_page_bitmap;
+    size_t m_used{};
+    size_t m_peak{};
+    size_t m_count{};
+    VAddr m_address{};
+    size_t m_size{};
+
+    // TODO(bunnei): Back by host memory until we emulate kernel virtual address space.
+    std::vector<u8> m_backing_memory;
+};
+
+} // namespace Kernel