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

This commit is contained in:
bunnei 2022-03-11 00:36:33 -08:00
parent 08434842b3
commit 91819726b1
3 changed files with 179 additions and 0 deletions

View File

@ -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

View File

@ -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

View File

@ -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