core: hle: kernel: Add KDynamicPageManager.
This commit is contained in:
parent
25dcaf1eca
commit
345b9e6a08
|
@ -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
|
||||||
|
|
|
@ -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
|
Reference in New Issue