3 changed files with 179 additions and 0 deletions
-
2src/core/CMakeLists.txt
-
65src/core/hle/kernel/k_thread_local_page.cpp
-
112src/core/hle/kernel/k_thread_local_page.h
@ -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
|
|||
@ -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 |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue