2020-04-19 00:10:26 +00:00
|
|
|
/*
|
2021-10-04 19:59:10 +00:00
|
|
|
* Copyright (c) Atmosphère-NX
|
2020-04-19 00:10:26 +00:00
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
|
|
* under the terms and conditions of the GNU General Public License,
|
|
|
|
* version 2, as published by the Free Software Foundation.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
|
|
* more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <mesosphere/kern_common.hpp>
|
|
|
|
#include <mesosphere/kern_k_spin_lock.hpp>
|
|
|
|
#include <mesosphere/kern_k_slab_heap.hpp>
|
|
|
|
#include <mesosphere/kern_k_page_group.hpp>
|
|
|
|
#include <mesosphere/kern_k_memory_block.hpp>
|
|
|
|
#include <mesosphere/kern_k_page_bitmap.hpp>
|
|
|
|
#include <mesosphere/kern_select_interrupt_manager.hpp>
|
|
|
|
|
|
|
|
namespace ams::kern {
|
|
|
|
|
|
|
|
class KDynamicPageManager {
|
|
|
|
public:
|
|
|
|
class PageBuffer {
|
|
|
|
private:
|
2020-12-18 01:18:47 +00:00
|
|
|
u8 m_buffer[PageSize];
|
2020-04-19 00:10:26 +00:00
|
|
|
};
|
|
|
|
static_assert(sizeof(PageBuffer) == PageSize);
|
|
|
|
private:
|
2020-12-18 01:18:47 +00:00
|
|
|
KSpinLock m_lock;
|
|
|
|
KPageBitmap m_page_bitmap;
|
|
|
|
size_t m_used;
|
|
|
|
size_t m_peak;
|
|
|
|
size_t m_count;
|
|
|
|
KVirtualAddress m_address;
|
|
|
|
size_t m_size;
|
2020-04-19 00:10:26 +00:00
|
|
|
public:
|
2020-12-18 01:18:47 +00:00
|
|
|
KDynamicPageManager() : m_lock(), m_page_bitmap(), m_used(), m_peak(), m_count(), m_address(), m_size() { /* ... */ }
|
2020-04-19 00:10:26 +00:00
|
|
|
|
|
|
|
Result Initialize(KVirtualAddress memory, size_t sz) {
|
|
|
|
/* We need to have positive size. */
|
|
|
|
R_UNLESS(sz > 0, svc::ResultOutOfMemory());
|
|
|
|
|
2020-08-25 23:12:14 +00:00
|
|
|
/* Calculate management overhead. */
|
|
|
|
const size_t management_size = KPageBitmap::CalculateManagementOverheadSize(sz / sizeof(PageBuffer));
|
|
|
|
const size_t allocatable_size = sz - management_size;
|
2020-04-19 00:10:26 +00:00
|
|
|
|
|
|
|
/* Set tracking fields. */
|
2020-12-18 01:18:47 +00:00
|
|
|
m_address = memory;
|
|
|
|
m_size = util::AlignDown(allocatable_size, sizeof(PageBuffer));
|
|
|
|
m_count = allocatable_size / sizeof(PageBuffer);
|
|
|
|
R_UNLESS(m_count > 0, svc::ResultOutOfMemory());
|
2020-04-19 00:10:26 +00:00
|
|
|
|
2020-08-25 23:12:14 +00:00
|
|
|
/* Clear the management region. */
|
2020-12-18 01:18:47 +00:00
|
|
|
u64 *management_ptr = GetPointer<u64>(m_address + allocatable_size);
|
2020-08-25 23:12:14 +00:00
|
|
|
std::memset(management_ptr, 0, management_size);
|
2020-04-19 00:10:26 +00:00
|
|
|
|
|
|
|
/* Initialize the bitmap. */
|
2020-12-18 01:18:47 +00:00
|
|
|
m_page_bitmap.Initialize(management_ptr, m_count);
|
2020-04-19 00:10:26 +00:00
|
|
|
|
|
|
|
/* Free the pages to the bitmap. */
|
2020-12-18 01:18:47 +00:00
|
|
|
for (size_t i = 0; i < m_count; i++) {
|
2021-06-17 20:03:46 +00:00
|
|
|
/* Ensure the freed page is all-zero. */
|
|
|
|
cpu::ClearPageToZero(GetPointer<PageBuffer>(m_address) + i);
|
|
|
|
|
|
|
|
/* Set the bit for the free page. */
|
2020-12-18 01:18:47 +00:00
|
|
|
m_page_bitmap.SetBit(i);
|
2020-04-19 00:10:26 +00:00
|
|
|
}
|
2020-04-19 07:35:05 +00:00
|
|
|
|
|
|
|
return ResultSuccess();
|
2020-04-19 00:10:26 +00:00
|
|
|
}
|
|
|
|
|
2020-12-18 01:18:47 +00:00
|
|
|
constexpr KVirtualAddress GetAddress() const { return m_address; }
|
|
|
|
constexpr size_t GetSize() const { return m_size; }
|
|
|
|
constexpr size_t GetUsed() const { return m_used; }
|
|
|
|
constexpr size_t GetPeak() const { return m_peak; }
|
|
|
|
constexpr size_t GetCount() const { return m_count; }
|
2020-04-19 00:10:26 +00:00
|
|
|
|
|
|
|
PageBuffer *Allocate() {
|
|
|
|
/* Take the lock. */
|
|
|
|
KScopedInterruptDisable di;
|
2020-12-18 01:18:47 +00:00
|
|
|
KScopedSpinLock lk(m_lock);
|
2020-04-19 00:10:26 +00:00
|
|
|
|
|
|
|
/* Find a random free block. */
|
2020-12-18 01:18:47 +00:00
|
|
|
ssize_t soffset = m_page_bitmap.FindFreeBlock(true);
|
2020-04-19 00:10:26 +00:00
|
|
|
if (AMS_UNLIKELY(soffset < 0)) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
const size_t offset = static_cast<size_t>(soffset);
|
|
|
|
|
|
|
|
/* Update our tracking. */
|
2020-12-18 01:18:47 +00:00
|
|
|
m_page_bitmap.ClearBit(offset);
|
|
|
|
m_peak = std::max(m_peak, (++m_used));
|
2020-04-19 00:10:26 +00:00
|
|
|
|
2020-12-18 01:18:47 +00:00
|
|
|
return GetPointer<PageBuffer>(m_address) + offset;
|
2020-04-19 00:10:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Free(PageBuffer *pb) {
|
2021-06-17 20:03:46 +00:00
|
|
|
/* Ensure all pages in the heap are zero. */
|
|
|
|
cpu::ClearPageToZero(pb);
|
|
|
|
|
2020-04-19 00:10:26 +00:00
|
|
|
/* Take the lock. */
|
|
|
|
KScopedInterruptDisable di;
|
2020-12-18 01:18:47 +00:00
|
|
|
KScopedSpinLock lk(m_lock);
|
2020-04-19 00:10:26 +00:00
|
|
|
|
|
|
|
/* Set the bit for the free page. */
|
2020-12-18 01:18:47 +00:00
|
|
|
size_t offset = (reinterpret_cast<uintptr_t>(pb) - GetInteger(m_address)) / sizeof(PageBuffer);
|
|
|
|
m_page_bitmap.SetBit(offset);
|
2020-04-19 00:10:26 +00:00
|
|
|
|
|
|
|
/* Decrement our used count. */
|
2020-12-18 01:18:47 +00:00
|
|
|
--m_used;
|
2020-04-19 00:10:26 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|