diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/core/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_thread_local_page.cpp | 65 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_thread_local_page.h | 112 |
3 files changed, 179 insertions, 0 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 43be5e652..6a83d5ceb 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -245,6 +245,8 @@ add_library(core STATIC | |||
| 245 | hle/kernel/k_system_control.h | 245 | hle/kernel/k_system_control.h |
| 246 | hle/kernel/k_thread.cpp | 246 | hle/kernel/k_thread.cpp |
| 247 | hle/kernel/k_thread.h | 247 | hle/kernel/k_thread.h |
| 248 | hle/kernel/k_thread_local_page.cpp | ||
| 249 | hle/kernel/k_thread_local_page.h | ||
| 248 | hle/kernel/k_thread_queue.cpp | 250 | hle/kernel/k_thread_queue.cpp |
| 249 | hle/kernel/k_thread_queue.h | 251 | hle/kernel/k_thread_queue.h |
| 250 | hle/kernel/k_trace.h | 252 | hle/kernel/k_trace.h |
diff --git a/src/core/hle/kernel/k_thread_local_page.cpp b/src/core/hle/kernel/k_thread_local_page.cpp new file mode 100644 index 000000000..4653c29f6 --- /dev/null +++ b/src/core/hle/kernel/k_thread_local_page.cpp | |||
| @@ -0,0 +1,65 @@ | |||
| 1 | // Copyright 2022 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/scope_exit.h" | ||
| 6 | #include "core/hle/kernel/k_memory_block.h" | ||
| 7 | #include "core/hle/kernel/k_page_table.h" | ||
| 8 | #include "core/hle/kernel/k_process.h" | ||
| 9 | #include "core/hle/kernel/k_thread_local_page.h" | ||
| 10 | #include "core/hle/kernel/kernel.h" | ||
| 11 | |||
| 12 | namespace Kernel { | ||
| 13 | |||
| 14 | ResultCode KThreadLocalPage::Initialize(KernelCore& kernel, KProcess* process) { | ||
| 15 | // Set that this process owns us. | ||
| 16 | m_owner = process; | ||
| 17 | m_kernel = &kernel; | ||
| 18 | |||
| 19 | // Allocate a new page. | ||
| 20 | KPageBuffer* page_buf = KPageBuffer::Allocate(kernel); | ||
| 21 | R_UNLESS(page_buf != nullptr, ResultOutOfMemory); | ||
| 22 | auto page_buf_guard = SCOPE_GUARD({ KPageBuffer::Free(kernel, page_buf); }); | ||
| 23 | |||
| 24 | // Map the address in. | ||
| 25 | const auto phys_addr = kernel.System().DeviceMemory().GetPhysicalAddr(page_buf); | ||
| 26 | R_TRY(m_owner->PageTable().MapPages(std::addressof(m_virt_addr), 1, PageSize, phys_addr, | ||
| 27 | KMemoryState::ThreadLocal, | ||
| 28 | KMemoryPermission::UserReadWrite)); | ||
| 29 | |||
| 30 | // We succeeded. | ||
| 31 | page_buf_guard.Cancel(); | ||
| 32 | |||
| 33 | return ResultSuccess; | ||
| 34 | } | ||
| 35 | |||
| 36 | ResultCode KThreadLocalPage::Finalize() { | ||
| 37 | // Get the physical address of the page. | ||
| 38 | const PAddr phys_addr = m_owner->PageTable().GetPhysicalAddr(m_virt_addr); | ||
| 39 | ASSERT(phys_addr); | ||
| 40 | |||
| 41 | // Unmap the page. | ||
| 42 | R_TRY(m_owner->PageTable().UnmapPages(this->GetAddress(), 1, KMemoryState::ThreadLocal)); | ||
| 43 | |||
| 44 | // Free the page. | ||
| 45 | KPageBuffer::Free(*m_kernel, KPageBuffer::FromPhysicalAddress(m_kernel->System(), phys_addr)); | ||
| 46 | |||
| 47 | return ResultSuccess; | ||
| 48 | } | ||
| 49 | |||
| 50 | VAddr KThreadLocalPage::Reserve() { | ||
| 51 | for (size_t i = 0; i < m_is_region_free.size(); i++) { | ||
| 52 | if (m_is_region_free[i]) { | ||
| 53 | m_is_region_free[i] = false; | ||
| 54 | return this->GetRegionAddress(i); | ||
| 55 | } | ||
| 56 | } | ||
| 57 | |||
| 58 | return 0; | ||
| 59 | } | ||
| 60 | |||
| 61 | void KThreadLocalPage::Release(VAddr addr) { | ||
| 62 | m_is_region_free[this->GetRegionIndex(addr)] = true; | ||
| 63 | } | ||
| 64 | |||
| 65 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/k_thread_local_page.h b/src/core/hle/kernel/k_thread_local_page.h new file mode 100644 index 000000000..658c67e94 --- /dev/null +++ b/src/core/hle/kernel/k_thread_local_page.h | |||
| @@ -0,0 +1,112 @@ | |||
| 1 | // Copyright 2022 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <algorithm> | ||
| 8 | #include <array> | ||
| 9 | |||
| 10 | #include "common/alignment.h" | ||
| 11 | #include "common/assert.h" | ||
| 12 | #include "common/common_types.h" | ||
| 13 | #include "common/intrusive_red_black_tree.h" | ||
| 14 | #include "core/hle/kernel/k_page_buffer.h" | ||
| 15 | #include "core/hle/kernel/memory_types.h" | ||
| 16 | #include "core/hle/kernel/slab_helpers.h" | ||
| 17 | #include "core/hle/result.h" | ||
| 18 | |||
| 19 | namespace Kernel { | ||
| 20 | |||
| 21 | class KernelCore; | ||
| 22 | class KProcess; | ||
| 23 | |||
| 24 | class KThreadLocalPage final : public Common::IntrusiveRedBlackTreeBaseNode<KThreadLocalPage>, | ||
| 25 | public KSlabAllocated<KThreadLocalPage> { | ||
| 26 | public: | ||
| 27 | static constexpr size_t RegionsPerPage = PageSize / Svc::ThreadLocalRegionSize; | ||
| 28 | static_assert(RegionsPerPage > 0); | ||
| 29 | |||
| 30 | public: | ||
| 31 | constexpr explicit KThreadLocalPage(VAddr addr = {}) : m_virt_addr(addr) { | ||
| 32 | m_is_region_free.fill(true); | ||
| 33 | } | ||
| 34 | |||
| 35 | constexpr VAddr GetAddress() const { | ||
| 36 | return m_virt_addr; | ||
| 37 | } | ||
| 38 | |||
| 39 | ResultCode Initialize(KernelCore& kernel, KProcess* process); | ||
| 40 | ResultCode Finalize(); | ||
| 41 | |||
| 42 | VAddr Reserve(); | ||
| 43 | void Release(VAddr addr); | ||
| 44 | |||
| 45 | bool IsAllUsed() const { | ||
| 46 | return std::ranges::all_of(m_is_region_free.begin(), m_is_region_free.end(), | ||
| 47 | [](bool is_free) { return !is_free; }); | ||
| 48 | } | ||
| 49 | |||
| 50 | bool IsAllFree() const { | ||
| 51 | return std::ranges::all_of(m_is_region_free.begin(), m_is_region_free.end(), | ||
| 52 | [](bool is_free) { return is_free; }); | ||
| 53 | } | ||
| 54 | |||
| 55 | bool IsAnyUsed() const { | ||
| 56 | return !this->IsAllFree(); | ||
| 57 | } | ||
| 58 | |||
| 59 | bool IsAnyFree() const { | ||
| 60 | return !this->IsAllUsed(); | ||
| 61 | } | ||
| 62 | |||
| 63 | public: | ||
| 64 | using RedBlackKeyType = VAddr; | ||
| 65 | |||
| 66 | static constexpr RedBlackKeyType GetRedBlackKey(const RedBlackKeyType& v) { | ||
| 67 | return v; | ||
| 68 | } | ||
| 69 | static constexpr RedBlackKeyType GetRedBlackKey(const KThreadLocalPage& v) { | ||
| 70 | return v.GetAddress(); | ||
| 71 | } | ||
| 72 | |||
| 73 | template <typename T> | ||
| 74 | requires(std::same_as<T, KThreadLocalPage> || | ||
| 75 | std::same_as<T, RedBlackKeyType>) static constexpr int Compare(const T& lhs, | ||
| 76 | const KThreadLocalPage& | ||
| 77 | rhs) { | ||
| 78 | const VAddr lval = GetRedBlackKey(lhs); | ||
| 79 | const VAddr rval = GetRedBlackKey(rhs); | ||
| 80 | |||
| 81 | if (lval < rval) { | ||
| 82 | return -1; | ||
| 83 | } else if (lval == rval) { | ||
| 84 | return 0; | ||
| 85 | } else { | ||
| 86 | return 1; | ||
| 87 | } | ||
| 88 | } | ||
| 89 | |||
| 90 | private: | ||
| 91 | constexpr VAddr GetRegionAddress(size_t i) const { | ||
| 92 | return this->GetAddress() + i * Svc::ThreadLocalRegionSize; | ||
| 93 | } | ||
| 94 | |||
| 95 | constexpr bool Contains(VAddr addr) const { | ||
| 96 | return this->GetAddress() <= addr && addr < this->GetAddress() + PageSize; | ||
| 97 | } | ||
| 98 | |||
| 99 | constexpr size_t GetRegionIndex(VAddr addr) const { | ||
| 100 | ASSERT(Common::IsAligned(addr, Svc::ThreadLocalRegionSize)); | ||
| 101 | ASSERT(this->Contains(addr)); | ||
| 102 | return (addr - this->GetAddress()) / Svc::ThreadLocalRegionSize; | ||
| 103 | } | ||
| 104 | |||
| 105 | private: | ||
| 106 | VAddr m_virt_addr{}; | ||
| 107 | KProcess* m_owner{}; | ||
| 108 | KernelCore* m_kernel{}; | ||
| 109 | std::array<bool, RegionsPerPage> m_is_region_free{}; | ||
| 110 | }; | ||
| 111 | |||
| 112 | } // namespace Kernel | ||