Преглед изворни кода

core: hle: kernel: k_page_buffer: Add KThreadLocalPage primitive.

bunnei пре 4 година
родитељ
комит
91819726b1

+ 2 - 0
src/core/CMakeLists.txt

@@ -245,6 +245,8 @@ add_library(core STATIC
     hle/kernel/k_system_control.h
     hle/kernel/k_system_control.h
     hle/kernel/k_thread.cpp
     hle/kernel/k_thread.cpp
     hle/kernel/k_thread.h
     hle/kernel/k_thread.h
+    hle/kernel/k_thread_local_page.cpp
+    hle/kernel/k_thread_local_page.h
     hle/kernel/k_thread_queue.cpp
     hle/kernel/k_thread_queue.cpp
     hle/kernel/k_thread_queue.h
     hle/kernel/k_thread_queue.h
     hle/kernel/k_trace.h
     hle/kernel/k_trace.h

+ 65 - 0
src/core/hle/kernel/k_thread_local_page.cpp

@@ -0,0 +1,65 @@
+// Copyright 2022 yuzu 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/k_memory_block.h"
+#include "core/hle/kernel/k_page_table.h"
+#include "core/hle/kernel/k_process.h"
+#include "core/hle/kernel/k_thread_local_page.h"
+#include "core/hle/kernel/kernel.h"
+
+namespace Kernel {
+
+ResultCode KThreadLocalPage::Initialize(KernelCore& kernel, KProcess* process) {
+    // Set that this process owns us.
+    m_owner = process;
+    m_kernel = &kernel;
+
+    // Allocate a new page.
+    KPageBuffer* page_buf = KPageBuffer::Allocate(kernel);
+    R_UNLESS(page_buf != nullptr, ResultOutOfMemory);
+    auto page_buf_guard = SCOPE_GUARD({ KPageBuffer::Free(kernel, page_buf); });
+
+    // Map the address in.
+    const auto phys_addr = kernel.System().DeviceMemory().GetPhysicalAddr(page_buf);
+    R_TRY(m_owner->PageTable().MapPages(std::addressof(m_virt_addr), 1, PageSize, phys_addr,
+                                        KMemoryState::ThreadLocal,
+                                        KMemoryPermission::UserReadWrite));
+
+    // We succeeded.
+    page_buf_guard.Cancel();
+
+    return ResultSuccess;
+}
+
+ResultCode KThreadLocalPage::Finalize() {
+    // Get the physical address of the page.
+    const PAddr phys_addr = m_owner->PageTable().GetPhysicalAddr(m_virt_addr);
+    ASSERT(phys_addr);
+
+    // Unmap the page.
+    R_TRY(m_owner->PageTable().UnmapPages(this->GetAddress(), 1, KMemoryState::ThreadLocal));
+
+    // Free the page.
+    KPageBuffer::Free(*m_kernel, KPageBuffer::FromPhysicalAddress(m_kernel->System(), phys_addr));
+
+    return ResultSuccess;
+}
+
+VAddr KThreadLocalPage::Reserve() {
+    for (size_t i = 0; i < m_is_region_free.size(); i++) {
+        if (m_is_region_free[i]) {
+            m_is_region_free[i] = false;
+            return this->GetRegionAddress(i);
+        }
+    }
+
+    return 0;
+}
+
+void KThreadLocalPage::Release(VAddr addr) {
+    m_is_region_free[this->GetRegionIndex(addr)] = true;
+}
+
+} // namespace Kernel

+ 112 - 0
src/core/hle/kernel/k_thread_local_page.h

@@ -0,0 +1,112 @@
+// Copyright 2022 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <algorithm>
+#include <array>
+
+#include "common/alignment.h"
+#include "common/assert.h"
+#include "common/common_types.h"
+#include "common/intrusive_red_black_tree.h"
+#include "core/hle/kernel/k_page_buffer.h"
+#include "core/hle/kernel/memory_types.h"
+#include "core/hle/kernel/slab_helpers.h"
+#include "core/hle/result.h"
+
+namespace Kernel {
+
+class KernelCore;
+class KProcess;
+
+class KThreadLocalPage final : public Common::IntrusiveRedBlackTreeBaseNode<KThreadLocalPage>,
+                               public KSlabAllocated<KThreadLocalPage> {
+public:
+    static constexpr size_t RegionsPerPage = PageSize / Svc::ThreadLocalRegionSize;
+    static_assert(RegionsPerPage > 0);
+
+public:
+    constexpr explicit KThreadLocalPage(VAddr addr = {}) : m_virt_addr(addr) {
+        m_is_region_free.fill(true);
+    }
+
+    constexpr VAddr GetAddress() const {
+        return m_virt_addr;
+    }
+
+    ResultCode Initialize(KernelCore& kernel, KProcess* process);
+    ResultCode Finalize();
+
+    VAddr Reserve();
+    void Release(VAddr addr);
+
+    bool IsAllUsed() const {
+        return std::ranges::all_of(m_is_region_free.begin(), m_is_region_free.end(),
+                                   [](bool is_free) { return !is_free; });
+    }
+
+    bool IsAllFree() const {
+        return std::ranges::all_of(m_is_region_free.begin(), m_is_region_free.end(),
+                                   [](bool is_free) { return is_free; });
+    }
+
+    bool IsAnyUsed() const {
+        return !this->IsAllFree();
+    }
+
+    bool IsAnyFree() const {
+        return !this->IsAllUsed();
+    }
+
+public:
+    using RedBlackKeyType = VAddr;
+
+    static constexpr RedBlackKeyType GetRedBlackKey(const RedBlackKeyType& v) {
+        return v;
+    }
+    static constexpr RedBlackKeyType GetRedBlackKey(const KThreadLocalPage& v) {
+        return v.GetAddress();
+    }
+
+    template <typename T>
+    requires(std::same_as<T, KThreadLocalPage> ||
+             std::same_as<T, RedBlackKeyType>) static constexpr int Compare(const T& lhs,
+                                                                            const KThreadLocalPage&
+                                                                                rhs) {
+        const VAddr lval = GetRedBlackKey(lhs);
+        const VAddr rval = GetRedBlackKey(rhs);
+
+        if (lval < rval) {
+            return -1;
+        } else if (lval == rval) {
+            return 0;
+        } else {
+            return 1;
+        }
+    }
+
+private:
+    constexpr VAddr GetRegionAddress(size_t i) const {
+        return this->GetAddress() + i * Svc::ThreadLocalRegionSize;
+    }
+
+    constexpr bool Contains(VAddr addr) const {
+        return this->GetAddress() <= addr && addr < this->GetAddress() + PageSize;
+    }
+
+    constexpr size_t GetRegionIndex(VAddr addr) const {
+        ASSERT(Common::IsAligned(addr, Svc::ThreadLocalRegionSize));
+        ASSERT(this->Contains(addr));
+        return (addr - this->GetAddress()) / Svc::ThreadLocalRegionSize;
+    }
+
+private:
+    VAddr m_virt_addr{};
+    KProcess* m_owner{};
+    KernelCore* m_kernel{};
+    std::array<bool, RegionsPerPage> m_is_region_free{};
+};
+
+} // namespace Kernel