os: implement MemoryHeapManager, SetMemoryAttribute

This commit is contained in:
Michael Scire 2022-06-10 22:35:57 -07:00
parent 4e112de223
commit a65b6df8d2
26 changed files with 1120 additions and 13 deletions

View file

@ -21,7 +21,8 @@
#include <stratosphere/os/os_memory_common.hpp>
#include <stratosphere/os/os_memory_fence.hpp>
#include <stratosphere/os/os_memory_permission.hpp>
#include <stratosphere/os/os_memory_heap_api.hpp>
#include <stratosphere/os/os_memory_attribute.hpp>
#include <stratosphere/os/os_memory_heap.hpp>
#include <stratosphere/os/os_virtual_address_memory.hpp>
#include <stratosphere/os/os_native_handle.hpp>
#include <stratosphere/os/os_process_handle_api.hpp>

View file

@ -0,0 +1,30 @@
/*
* Copyright (c) Atmosphère-NX
*
* 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 <vapours.hpp>
#include <stratosphere/os/os_common_types.hpp>
#include <stratosphere/os/os_memory_common.hpp>
namespace ams::os {
enum MemoryAttribute {
MemoryAttribute_Normal,
MemoryAttribute_Uncached,
};
void SetMemoryAttribute(uintptr_t address, size_t size, MemoryAttribute attr);
}

View file

@ -18,10 +18,6 @@
namespace ams::os {
constexpr inline size_t MemoryPageSize = 0x1000;
constexpr inline size_t MemoryBlockUnitSize = 0x200000;
enum MemoryPermission {
MemoryPermission_None = (0 << 0),
MemoryPermission_ReadOnly = (1 << 0),

View file

@ -0,0 +1,19 @@
/*
* Copyright (c) Atmosphère-NX
*
* 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 <vapours.hpp>
#include <stratosphere/os/os_memory_heap_common.hpp>
#include <stratosphere/os/os_memory_heap_api.hpp>

View file

@ -16,10 +16,15 @@
#pragma once
#include <vapours.hpp>
#include <stratosphere/os/os_common_types.hpp>
#include <stratosphere/os/os_memory_common.hpp>
#include <stratosphere/os/os_memory_heap_common.hpp>
namespace ams::os {
Result SetMemoryHeapSize(size_t size);
uintptr_t GetMemoryHeapAddress();
size_t GetMemoryHeapSize();
Result AllocateMemoryBlock(uintptr_t *out_address, size_t size);
void FreeMemoryBlock(uintptr_t address, size_t size);

View file

@ -0,0 +1,28 @@
/*
* Copyright (c) Atmosphère-NX
*
* 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 <vapours.hpp>
#include <stratosphere/os/os_common_types.hpp>
#include <stratosphere/os/os_memory_common.hpp>
namespace ams::os {
constexpr inline size_t MemoryHeapUnitSize = 2_MB;
constexpr inline size_t MemoryBlockUnitSize = 2_MB;
constexpr inline size_t MemoryPageSize = 4_KB;
}

View file

@ -0,0 +1,23 @@
/*
* Copyright (c) Atmosphère-NX
*
* 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 <stratosphere.hpp>
namespace ams::os::impl {
void SetMemoryAttributeImpl(uintptr_t address, size_t size, MemoryAttribute attr);
}

View file

@ -0,0 +1,57 @@
/*
* Copyright (c) Atmosphère-NX
*
* 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/>.
*/
#include <stratosphere.hpp>
namespace ams::os::impl {
void SetMemoryAttributeImpl(uintptr_t address, size_t size, MemoryAttribute attr) {
/* Determine svc arguments. */
u32 svc_mask = svc::MemoryAttribute_Uncached;
u32 svc_attr = 0;
switch (attr) {
case os::MemoryAttribute_Normal: svc_attr = 0; break;
case os::MemoryAttribute_Uncached: svc_attr = svc::MemoryAttribute_Uncached; break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
/* Loop, setting attribute. */
auto cur_address = address;
auto remaining = size;
while (remaining > 0) {
/* Query the memory. */
svc::MemoryInfo mem_info;
svc::PageInfo page_info;
R_ABORT_UNLESS(svc::QueryMemory(std::addressof(mem_info), std::addressof(page_info), cur_address));
/* Determine the current size. */
const size_t cur_size = std::min<size_t>(mem_info.base_address + mem_info.size - cur_address, remaining);
/* Set the attribute, if necessary. */
if (mem_info.attribute != svc_attr) {
if (const auto res = svc::SetMemoryAttribute(address, size, svc_mask, svc_attr); R_FAILED(res)) {
/* NOTE: Nintendo logs here. */
R_ABORT_UNLESS(res);
}
}
/* Advance. */
cur_address += cur_size;
remaining -= cur_size;
}
}
}

View file

@ -0,0 +1,25 @@
/*
* Copyright (c) Atmosphère-NX
*
* 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/>.
*/
#include <stratosphere.hpp>
namespace ams::os::impl {
void SetMemoryAttributeImpl(uintptr_t address, size_t size, MemoryAttribute attr) {
/* TODO: Should this do anything? */
AMS_UNUSED(address, size, attr);
}
}

View file

@ -0,0 +1,25 @@
/*
* Copyright (c) Atmosphère-NX
*
* 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/>.
*/
#include <stratosphere.hpp>
namespace ams::os::impl {
void SetMemoryAttributeImpl(uintptr_t address, size_t size, MemoryAttribute attr) {
/* TODO: Should this do anything? */
AMS_UNUSED(address, size, attr);
}
}

View file

@ -0,0 +1,25 @@
/*
* Copyright (c) Atmosphère-NX
*
* 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/>.
*/
#include <stratosphere.hpp>
namespace ams::os::impl {
void SetMemoryAttributeImpl(uintptr_t address, size_t size, MemoryAttribute attr) {
/* TODO: Should this do anything? */
AMS_UNUSED(address, size, attr);
}
}

View file

@ -0,0 +1,221 @@
/*
* Copyright (c) Atmosphère-NX
*
* 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/>.
*/
#include <stratosphere.hpp>
#include "os_memory_heap_manager.hpp"
#include "os_thread_manager.hpp"
namespace ams::os::impl {
Result MemoryHeapManager::SetHeapSize(size_t size) {
/* Check pre-conditions. */
AMS_ASSERT(util::IsAligned(size, MemoryHeapUnitSize));
/* Acquire locks. */
std::scoped_lock lk1(util::GetReference(::ams::os::impl::GetCurrentThread()->cs_thread));
std::scoped_lock lk2(m_cs);
/* If we need to, expand the heap. */
if (size > m_heap_size) {
/* Set the new heap size. */
uintptr_t address = 0;
R_TRY(m_impl.SetHeapSize(std::addressof(address), size));
R_UNLESS(address != 0, os::ResultOutOfMemory());
/* Check that the new heap address is consistent. */
if (m_heap_size == 0) {
AMS_ASSERT(util::IsAligned(address, MemoryHeapUnitSize));
} else {
AMS_ASSERT(address == m_heap_address);
}
/* Set up the new heap address. */
this->AddToFreeSpaceUnsafe(address + m_heap_size, size - m_heap_size);
/* Set our heap address. */
m_heap_address = address;
m_heap_size = size;
} else if (size < m_heap_size) {
/* We're shrinking the heap, so we need to remove memory blocks. */
const uintptr_t end_address = m_heap_address + size;
const size_t remove_size = m_heap_size - size;
/* Get the end of the heap. */
auto it = m_free_memory_list.end();
--it;
R_UNLESS(it != m_free_memory_list.end(), os::ResultBusy());
/* Check that the block can be decommitted. */
R_UNLESS(it->GetAddress() <= end_address, os::ResultBusy());
R_UNLESS(it->GetSize() >= remove_size, os::ResultBusy());
/* Adjust the last node. */
if (const size_t node_size = it->GetSize() - remove_size; node_size == 0) {
m_free_memory_list.erase(it);
it->Clean();
} else {
it->SetSize(node_size);
}
/* Set the reduced heap size. */
uintptr_t address = 0;
R_ABORT_UNLESS(m_impl.SetHeapSize(std::addressof(address), size));
/* Set our heap address. */
m_heap_size = size;
if (size == 0) {
m_heap_address = 0;
}
}
R_SUCCEED();
}
Result MemoryHeapManager::AllocateFromHeap(uintptr_t *out_address, size_t size) {
/* Acquire locks. */
std::scoped_lock lk1(util::GetReference(::ams::os::impl::GetCurrentThread()->cs_thread));
std::scoped_lock lk2(m_cs);
/* Find free space. */
auto it = this->FindFreeSpaceUnsafe(size);
R_UNLESS(it != m_free_memory_list.end(), os::ResultOutOfMemory());
/* If necessary, split the memory block. */
if (it->GetSize() > size) {
this->SplitFreeMemoryNodeUnsafe(it, size);
}
/* Remove the block. */
m_free_memory_list.erase(it);
it->Clean();
/* Increment the used heap size. */
m_used_heap_size += size;
/* Set the output address. */
*out_address = it->GetAddress();
R_SUCCEED();
}
void MemoryHeapManager::ReleaseToHeap(uintptr_t address, size_t size) {
/* Acquire locks. */
std::scoped_lock lk1(util::GetReference(::ams::os::impl::GetCurrentThread()->cs_thread));
std::scoped_lock lk2(m_cs);
/* Check pre-condition. */
AMS_ABORT_UNLESS(this->IsRegionAllocatedMemoryUnsafe(address, size));
/* Restore the permissions on the memory. */
os::SetMemoryPermission(address, size, MemoryPermission_ReadWrite);
os::SetMemoryAttribute(address, size, MemoryAttribute_Normal);
/* Add the memory back to our free list. */
this->AddToFreeSpaceUnsafe(address, size);
/* Decrement the used heap size. */
m_used_heap_size -= size;
}
MemoryHeapManager::FreeMemoryList::iterator MemoryHeapManager::FindFreeSpaceUnsafe(size_t size) {
/* Find the best fit candidate. */
auto best = m_free_memory_list.end();
for (auto it = m_free_memory_list.begin(); it != m_free_memory_list.end(); ++it) {
if (const size_t node_size = it->GetSize(); node_size >= size) {
if (best == m_free_memory_list.end() || node_size < best->GetSize()) {
best = it;
}
}
}
return best;
}
MemoryHeapManager::FreeMemoryList::iterator MemoryHeapManager::ConcatenatePreviousFreeMemoryNodeUnsafe(FreeMemoryList::iterator node) {
/* Get the previous node. */
auto prev = node;
--prev;
/* If there's no previous, we're done. */
if (prev == m_free_memory_list.end() || node == m_free_memory_list.end()) {
return node;
}
/* Otherwise, if the previous isn't contiguous, we can't merge. */
if (prev->GetAddress() + prev->GetSize() != node->GetAddress()) {
return node;
}
/* Otherwise, increase the size of the previous node, and remove the current node. */
prev->SetSize(prev->GetSize() + node->GetSize());
m_free_memory_list.erase(node);
node->Clean();
return prev;
}
void MemoryHeapManager::SplitFreeMemoryNodeUnsafe(FreeMemoryList::iterator it, size_t size) {
/* Check pre-conditions. */
AMS_ASSERT(it->GetSize() > size);
AMS_ASSERT(util::IsAligned(size, MemoryBlockUnitSize));
/* Create new node. */
auto *new_node = std::construct_at(reinterpret_cast<FreeMemoryNode *>(it->GetAddress() + size));
new_node->SetSize(it->GetSize() - size);
/* Set the old node's size. */
it->SetSize(size);
/* Insert the new node. */
m_free_memory_list.insert(++it, *new_node);
}
void MemoryHeapManager::AddToFreeSpaceUnsafe(uintptr_t address, size_t size) {
/* Create new node. */
auto *new_node = std::construct_at(reinterpret_cast<FreeMemoryNode *>(address));
new_node->SetSize(size);
/* Find the appropriate place to insert the node. */
auto it = m_free_memory_list.begin();
for (/* ... */; it != m_free_memory_list.end(); ++it) {
if (address < it->GetAddress()) {
break;
}
}
/* Insert the new node. */
it = m_free_memory_list.insert(it, *new_node);
/* Perform coalescing as relevant. */
it = this->ConcatenatePreviousFreeMemoryNodeUnsafe(it);
this->ConcatenatePreviousFreeMemoryNodeUnsafe(++it);
}
bool MemoryHeapManager::IsRegionAllocatedMemoryUnsafe(uintptr_t address, size_t size) {
/* Look for a node containing the region. */
for (auto it = m_free_memory_list.begin(); it != m_free_memory_list.end(); ++it) {
const uintptr_t node_address = it->GetAddress();
const size_t node_size = it->GetSize();
if (node_address < address + size && address < node_address + node_size) {
return false;
}
}
return true;
}
}

View file

@ -0,0 +1,27 @@
/*
* Copyright (c) Atmosphère-NX
*
* 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 <stratosphere.hpp>
#include "os_memory_heap_manager_types.hpp"
#include "os_resource_manager.hpp"
namespace ams::os::impl {
ALWAYS_INLINE MemoryHeapManager &GetMemoryHeapManager() {
return GetResourceManager().GetMemoryHeapManager();
}
}

View file

@ -0,0 +1,36 @@
/*
* Copyright (c) Atmosphère-NX
*
* 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 <stratosphere.hpp>
namespace ams::os::impl {
class MemoryHeapManagerHorizonImpl {
public:
Result SetHeapSize(uintptr_t *out, size_t size) {
R_TRY_CATCH(svc::SetHeapSize(out, size)) {
R_CONVERT(svc::ResultOutOfMemory, os::ResultOutOfMemory())
R_CONVERT(svc::ResultLimitReached, os::ResultOutOfMemory())
R_CONVERT(svc::ResultOutOfResource, os::ResultOutOfMemory())
} R_END_TRY_CATCH;
R_SUCCEED();
}
};
using MemoryHeapManagerImpl = MemoryHeapManagerHorizonImpl;
}

View file

@ -0,0 +1,78 @@
/*
* Copyright (c) Atmosphère-NX
*
* 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 <stratosphere.hpp>
#include <sys/mman.h>
namespace ams::os::impl {
class MemoryHeapManagerLinuxImpl {
NON_COPYABLE(MemoryHeapManagerLinuxImpl);
NON_MOVEABLE(MemoryHeapManagerLinuxImpl);
private:
uintptr_t m_real_reserved_address;
size_t m_real_reserved_size;
uintptr_t m_aligned_reserved_heap_address;
size_t m_aligned_reserved_heap_size;
size_t m_committed_size;
public:
MemoryHeapManagerLinuxImpl() : m_real_reserved_address(0), m_real_reserved_size(0), m_aligned_reserved_heap_address(0), m_aligned_reserved_heap_size(0), m_committed_size(0) {
/* Reserve a 32 GB region of virtual address space. */
constexpr size_t TargetReservedSize = 32_GB;
const auto reserved = ::mmap(nullptr, TargetReservedSize, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
AMS_ABORT_UNLESS(reserved != MAP_FAILED);
m_real_reserved_address = reinterpret_cast<uintptr_t>(reserved);
m_real_reserved_size = TargetReservedSize;
m_aligned_reserved_heap_address = util::AlignUp(m_real_reserved_address, MemoryHeapUnitSize);
m_aligned_reserved_heap_size = m_real_reserved_size - MemoryHeapUnitSize;
}
Result SetHeapSize(uintptr_t *out, size_t size) {
/* Check that we have a reserved address. */
R_UNLESS(m_real_reserved_address != 0, os::ResultOutOfMemory());
/* If necessary, commit the new memory. */
if (size > m_committed_size) {
R_UNLESS(this->CommitMemory(size), os::ResultOutOfMemory());
} else if (size < m_committed_size) {
/* Otherwise, decommit. */
this->DecommitMemory(m_aligned_reserved_heap_address + size, m_committed_size - size);
}
/* Set the committed size. */
m_committed_size = size;
/* Set the out address. */
*out = m_aligned_reserved_heap_address;
R_SUCCEED();
}
private:
bool CommitMemory(size_t size) {
const auto res = ::mprotect(reinterpret_cast<void *>(m_aligned_reserved_heap_address), size, PROT_READ | PROT_WRITE);
return res == 0;
}
void DecommitMemory(uintptr_t address, size_t size) {
const auto reserved = ::mmap(reinterpret_cast<void *>(address), size, PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
AMS_ABORT_UNLESS(reserved != MAP_FAILED);
}
};
using MemoryHeapManagerImpl = MemoryHeapManagerLinuxImpl;
}

View file

@ -0,0 +1,78 @@
/*
* Copyright (c) Atmosphère-NX
*
* 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 <stratosphere.hpp>
#include <sys/mman.h>
namespace ams::os::impl {
class MemoryHeapManagerMacosImpl {
NON_COPYABLE(MemoryHeapManagerMacosImpl);
NON_MOVEABLE(MemoryHeapManagerMacosImpl);
private:
uintptr_t m_real_reserved_address;
size_t m_real_reserved_size;
uintptr_t m_aligned_reserved_heap_address;
size_t m_aligned_reserved_heap_size;
size_t m_committed_size;
public:
MemoryHeapManagerMacosImpl() : m_real_reserved_address(0), m_real_reserved_size(0), m_aligned_reserved_heap_address(0), m_aligned_reserved_heap_size(0), m_committed_size(0) {
/* Reserve a 32 GB region of virtual address space. */
constexpr size_t TargetReservedSize = 32_GB;
const auto reserved = ::mmap(nullptr, TargetReservedSize, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
AMS_ABORT_UNLESS(reserved != MAP_FAILED);
m_real_reserved_address = reinterpret_cast<uintptr_t>(reserved);
m_real_reserved_size = TargetReservedSize;
m_aligned_reserved_heap_address = util::AlignUp(m_real_reserved_address, MemoryHeapUnitSize);
m_aligned_reserved_heap_size = m_real_reserved_size - MemoryHeapUnitSize;
}
Result SetHeapSize(uintptr_t *out, size_t size) {
/* Check that we have a reserved address. */
R_UNLESS(m_real_reserved_address != 0, os::ResultOutOfMemory());
/* If necessary, commit the new memory. */
if (size > m_committed_size) {
R_UNLESS(this->CommitMemory(size), os::ResultOutOfMemory());
} else if (size < m_committed_size) {
/* Otherwise, decommit. */
this->DecommitMemory(m_aligned_reserved_heap_address + size, m_committed_size - size);
}
/* Set the committed size. */
m_committed_size = size;
/* Set the out address. */
*out = m_aligned_reserved_heap_address;
R_SUCCEED();
}
private:
bool CommitMemory(size_t size) {
const auto res = ::mprotect(reinterpret_cast<void *>(m_aligned_reserved_heap_address), size, PROT_READ | PROT_WRITE);
return res == 0;
}
void DecommitMemory(uintptr_t address, size_t size) {
const auto reserved = ::mmap(reinterpret_cast<void *>(address), size, PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
AMS_ABORT_UNLESS(reserved != MAP_FAILED);
}
};
using MemoryHeapManagerImpl = MemoryHeapManagerMacosImpl;
}

View file

@ -0,0 +1,132 @@
/*
* Copyright (c) Atmosphère-NX
*
* 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 <stratosphere.hpp>
#include <stratosphere/windows.hpp>
namespace ams::os::impl {
class MemoryHeapManagerWindowsImpl {
NON_COPYABLE(MemoryHeapManagerWindowsImpl);
NON_MOVEABLE(MemoryHeapManagerWindowsImpl);
private:
LPVOID m_real_reserved_address;
size_t m_real_reserved_size;
LPVOID m_aligned_reserved_heap_address;
size_t m_aligned_reserved_heap_size;
size_t m_committed_size;
public:
MemoryHeapManagerWindowsImpl() : m_real_reserved_address(nullptr), m_real_reserved_size(0), m_aligned_reserved_heap_address(nullptr), m_aligned_reserved_heap_size(0), m_committed_size(0) {
/* Define target size. */
constexpr size_t TargetReservedSize = 32_GB;
/* Allocate appropriate amount of virtual space. */
size_t reserved_size = 0;
size_t reserved_addend = TargetReservedSize;
while (reserved_addend >= MemoryHeapUnitSize) {
if (this->ReserveVirtualSpace(0, reserved_size + reserved_addend)) {
this->ReleaseVirtualSpace();
reserved_size += reserved_addend;
if (reserved_size >= TargetReservedSize) {
break;
}
}
reserved_addend /= 2;
}
/* Reserve virtual space. */
AMS_ABORT_UNLESS(this->ReserveVirtualSpace(0, reserved_size));
}
Result SetHeapSize(uintptr_t *out, size_t size) {
/* Check that we have a reserved address. */
R_UNLESS(m_real_reserved_address != nullptr, os::ResultOutOfMemory());
/* If necessary, commit the new memory. */
if (size > m_committed_size) {
R_UNLESS(this->CommitMemory(size), os::ResultOutOfMemory());
} else if (size < m_committed_size) {
/* Otherwise, decommit. */
this->DecommitMemory(reinterpret_cast<uintptr_t>(m_aligned_reserved_heap_address) + size, m_committed_size - size);
}
/* Set the committed size. */
m_committed_size = size;
/* Set the out address. */
*out = reinterpret_cast<uintptr_t>(m_aligned_reserved_heap_address);
R_SUCCEED();
}
private:
bool ReserveVirtualSpace(uintptr_t address, size_t size) {
AMS_ABORT_UNLESS(m_real_reserved_address == nullptr);
AMS_ABORT_UNLESS(m_real_reserved_size == 0);
size_t reserve_size = util::AlignUp(size, MemoryHeapUnitSize);
if constexpr (constexpr size_t VirtualAllocUnitSize = 64_KB; MemoryHeapUnitSize > VirtualAllocUnitSize) {
reserve_size += MemoryHeapUnitSize - VirtualAllocUnitSize;
}
LPVOID res = ::VirtualAlloc(reinterpret_cast<LPVOID>(address), reserve_size, MEM_RESERVE, PAGE_READWRITE);
if (res == nullptr) {
return false;
}
m_real_reserved_address = res;
m_real_reserved_size = reserve_size;
m_aligned_reserved_heap_address = reinterpret_cast<LPVOID>(util::AlignUp(reinterpret_cast<uintptr_t>(m_real_reserved_address), MemoryHeapUnitSize));
m_aligned_reserved_heap_size = size;
return true;
}
void ReleaseVirtualSpace() {
if (m_real_reserved_address != nullptr) {
auto res = ::VirtualFree(m_real_reserved_address, 0, MEM_RELEASE);
AMS_ASSERT(res);
AMS_UNUSED(res);
m_real_reserved_address = nullptr;
m_real_reserved_size = 0;
m_aligned_reserved_heap_address = nullptr;
m_aligned_reserved_heap_size = 0;
}
}
bool CommitMemory(size_t size) {
LPVOID address = ::VirtualAlloc(m_aligned_reserved_heap_address, static_cast<SIZE_T>(size), MEM_COMMIT, PAGE_READWRITE);
if (address == nullptr) {
return false;
}
AMS_ABORT_UNLESS(address == m_aligned_reserved_heap_address);
return true;
}
void DecommitMemory(uintptr_t address, size_t size) {
auto res = ::VirtualFree(reinterpret_cast<LPVOID>(address), static_cast<SIZE_T>(size), MEM_DECOMMIT);
AMS_ASSERT(res);
AMS_UNUSED(res);
}
};
using MemoryHeapManagerImpl = MemoryHeapManagerWindowsImpl;
}

View file

@ -0,0 +1,84 @@
/*
* Copyright (c) Atmosphère-NX
*
* 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 <stratosphere.hpp>
#if defined(ATMOSPHERE_OS_HORIZON)
#include "os_memory_heap_manager_impl.os.horizon.hpp"
#elif defined(ATMOSPHERE_OS_WINDOWS)
#include "os_memory_heap_manager_impl.os.windows.hpp"
#elif defined(ATMOSPHERE_OS_LINUX)
#include "os_memory_heap_manager_impl.os.linux.hpp"
#elif defined(ATMOSPHERE_OS_MACOS)
#include "os_memory_heap_manager_impl.os.macos.hpp"
#else
#error "Unknown OS for MemoryHeapManagerImpl"
#endif
namespace ams::os::impl {
class MemoryHeapManager;
class FreeMemoryNode {
private:
friend class MemoryHeapManager;
private:
util::IntrusiveListNode m_node;
size_t m_size;
public:
ALWAYS_INLINE uintptr_t GetAddress() const { return reinterpret_cast<uintptr_t>(this); }
ALWAYS_INLINE size_t GetSize() const { return m_size; }
ALWAYS_INLINE void SetSize(size_t size) { m_size = size; }
ALWAYS_INLINE void Clean() { std::memset(reinterpret_cast<void *>(this), 0, sizeof(FreeMemoryNode)); }
};
static_assert(sizeof(FreeMemoryNode) == sizeof(util::IntrusiveListNode) + sizeof(size_t));
class MemoryHeapManager {
NON_COPYABLE(MemoryHeapManager);
NON_MOVEABLE(MemoryHeapManager);
private:
using FreeMemoryList = typename util::IntrusiveListMemberTraits<&FreeMemoryNode::m_node>::ListType;
private:
uintptr_t m_heap_address;
size_t m_heap_size;
size_t m_used_heap_size;
FreeMemoryList m_free_memory_list;
InternalCriticalSection m_cs;
MemoryHeapManagerImpl m_impl;
public:
MemoryHeapManager() : m_heap_address(0), m_heap_size(0), m_used_heap_size(0) { /* ... */ }
Result SetHeapSize(size_t size);
Result AllocateFromHeap(uintptr_t *out_address, size_t size);
void ReleaseToHeap(uintptr_t address, size_t size);
bool IsRegionInMemoryHeap(uintptr_t address, size_t size) const {
return m_heap_address <= address && (address + size) <= (m_heap_address + m_heap_size);
}
uintptr_t GetHeapAddress() const { return m_heap_address; }
size_t GetHeapSize() const { return m_heap_size; }
size_t GetUsedHeapSize() const { return m_used_heap_size; }
private:
FreeMemoryList::iterator FindFreeSpaceUnsafe(size_t size);
FreeMemoryList::iterator ConcatenatePreviousFreeMemoryNodeUnsafe(FreeMemoryList::iterator node);
void SplitFreeMemoryNodeUnsafe(FreeMemoryList::iterator it, size_t size);
void AddToFreeSpaceUnsafe(uintptr_t address, size_t size);
bool IsRegionAllocatedMemoryUnsafe(uintptr_t address, size_t size);
};
}

View file

@ -0,0 +1,48 @@
/*
* Copyright (c) Atmosphère-NX
*
* 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/>.
*/
#include <stratosphere.hpp>
#include <sys/mman.h>
namespace ams::os::impl {
void SetMemoryPermissionImpl(uintptr_t address, size_t size, MemoryPermission perm) {
switch (perm) {
case MemoryPermission_None:
{
auto res = ::mprotect(reinterpret_cast<void *>(address), size, PROT_NONE);
AMS_ABORT_UNLESS(res);
AMS_UNUSED(res);
}
break;
case MemoryPermission_ReadOnly:
{
auto res = ::mprotect(reinterpret_cast<void *>(address), size, PROT_READ);
AMS_ABORT_UNLESS(res);
AMS_UNUSED(res);
}
break;
case MemoryPermission_ReadWrite:
{
auto res = ::mprotect(reinterpret_cast<void *>(address), size, PROT_READ | PROT_WRITE);
AMS_ABORT_UNLESS(res);
AMS_UNUSED(res);
}
break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
}

View file

@ -0,0 +1,48 @@
/*
* Copyright (c) Atmosphère-NX
*
* 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/>.
*/
#include <stratosphere.hpp>
#include <sys/mman.h>
namespace ams::os::impl {
void SetMemoryPermissionImpl(uintptr_t address, size_t size, MemoryPermission perm) {
switch (perm) {
case MemoryPermission_None:
{
auto res = ::mprotect(reinterpret_cast<void *>(address), size, PROT_NONE);
AMS_ABORT_UNLESS(res);
AMS_UNUSED(res);
}
break;
case MemoryPermission_ReadOnly:
{
auto res = ::mprotect(reinterpret_cast<void *>(address), size, PROT_READ);
AMS_ABORT_UNLESS(res);
AMS_UNUSED(res);
}
break;
case MemoryPermission_ReadWrite:
{
auto res = ::mprotect(reinterpret_cast<void *>(address), size, PROT_READ | PROT_WRITE);
AMS_ABORT_UNLESS(res);
AMS_UNUSED(res);
}
break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
}

View file

@ -0,0 +1,58 @@
/*
* Copyright (c) Atmosphère-NX
*
* 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/>.
*/
#include <stratosphere.hpp>
#include <stratosphere/windows.hpp>
namespace ams::os::impl {
void SetMemoryPermissionImpl(uintptr_t address, size_t size, MemoryPermission perm) {
DWORD old;
uintptr_t cur_address = address;
size_t remaining = size;
while (remaining > 0) {
const size_t cur_size = std::min<size_t>(remaining, 2_GB);
switch (perm) {
case MemoryPermission_None:
{
auto res = ::VirtualProtect(reinterpret_cast<LPVOID>(address), static_cast<DWORD>(cur_size), PAGE_NOACCESS, std::addressof(old));
AMS_ABORT_UNLESS(res);
AMS_UNUSED(res);
}
break;
case MemoryPermission_ReadOnly:
{
auto res = ::VirtualProtect(reinterpret_cast<LPVOID>(address), static_cast<DWORD>(cur_size), PAGE_READONLY, std::addressof(old));
AMS_ABORT_UNLESS(res);
AMS_UNUSED(res);
}
break;
case MemoryPermission_ReadWrite:
{
auto res = ::VirtualProtect(reinterpret_cast<LPVOID>(address), static_cast<DWORD>(cur_size), PAGE_READWRITE, std::addressof(old));
AMS_ABORT_UNLESS(res);
AMS_UNUSED(res);
}
break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
cur_address += cur_size;
remaining -= cur_size;
}
}
}

View file

@ -22,6 +22,7 @@
#include "os_aslr_space_manager_types.hpp"
#include "os_tls_manager_types.hpp"
#include "os_giant_lock_types.hpp"
#include "os_memory_heap_manager_types.hpp"
#include "os_vamm_manager_types.hpp"
namespace ams::os::impl {
@ -34,7 +35,7 @@ namespace ams::os::impl {
ThreadManager m_thread_manager{};
//TlsManager m_tls_manager{};
TickManager m_tick_manager{};
/* TODO */
MemoryHeapManager m_memory_heap_manager;
VammManager m_vamm_manager;
GiantLock m_giant_lock{};
public:
@ -46,6 +47,7 @@ namespace ams::os::impl {
constexpr ALWAYS_INLINE StackGuardManager &GetStackGuardManager() { return m_stack_guard_manager; }
//constexpr ALWAYS_INLINE TlsManager &GetTlsManager() { return m_tls_manager; }
constexpr ALWAYS_INLINE TickManager &GetTickManager() { return m_tick_manager; }
constexpr ALWAYS_INLINE MemoryHeapManager &GetMemoryHeapManager() { return m_memory_heap_manager; }
constexpr ALWAYS_INLINE VammManager &GetVammManager() { return m_vamm_manager; }
constexpr ALWAYS_INLINE GiantLock &GetGiantLock() { return m_giant_lock; }
};

View file

@ -37,7 +37,8 @@ namespace ams::os::impl {
}
static Result FreePhysicalMemoryImpl(uintptr_t address, size_t size) {
R_UNLESS(::mprotect(reinterpret_cast<void *>(address), size, PROT_NONE) == 0, os::ResultBusy());
const auto reserved = ::mmap(reinterpret_cast<void *>(address), size, PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
R_UNLESS(reserved != MAP_FAILED, os::ResultBusy());
R_SUCCEED();
}

View file

@ -37,7 +37,8 @@ namespace ams::os::impl {
}
static Result FreePhysicalMemoryImpl(uintptr_t address, size_t size) {
R_UNLESS(::mprotect(reinterpret_cast<void *>(address), size, PROT_NONE) == 0, os::ResultBusy());
const auto reserved = ::mmap(reinterpret_cast<void *>(address), size, PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
R_UNLESS(reserved != MAP_FAILED, os::ResultBusy());
R_SUCCEED();
}

View file

@ -0,0 +1,29 @@
/*
* Copyright (c) Atmosphère-NX
*
* 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/>.
*/
#include <stratosphere.hpp>
#include "impl/os_memory_attribute_impl.hpp"
namespace ams::os {
void SetMemoryAttribute(uintptr_t address, size_t size, MemoryAttribute attr) {
/* Check pre-conditions. */
AMS_ASSERT(util::IsAligned(address, MemoryPageSize));
AMS_ASSERT(util::IsAligned(size, MemoryPageSize));
return impl::SetMemoryAttributeImpl(address, size, attr);
}
}

View file

@ -14,17 +14,47 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "impl/os_memory_heap_manager.hpp"
namespace ams::os {
Result SetMemoryHeapSize(size_t size) {
/* Check pre-conditions. */
AMS_ASSERT(util::IsAligned(size, MemoryHeapUnitSize));
/* Set the heap size. */
R_RETURN(impl::GetMemoryHeapManager().SetHeapSize(size));
}
uintptr_t GetMemoryHeapAddress() {
return impl::GetMemoryHeapManager().GetHeapAddress();
}
size_t GetMemoryHeapSize() {
return impl::GetMemoryHeapManager().GetHeapSize();
}
Result AllocateMemoryBlock(uintptr_t *out_address, size_t size) {
AMS_UNUSED(out_address, size);
AMS_ABORT("Not implemented yet");
/* Check pre-conditions. */
AMS_ASSERT(size > 0);
AMS_ASSERT(util::IsAligned(size, MemoryBlockUnitSize));
/* Allocate from heap. */
R_RETURN(impl::GetMemoryHeapManager().AllocateFromHeap(out_address, size));
}
void FreeMemoryBlock(uintptr_t address, size_t size) {
AMS_UNUSED(address, size);
AMS_ABORT("Not implemented yet");
/* Get memory heap manager. */
auto &manager = impl::GetMemoryHeapManager();
/* Check pre-conditions. */
AMS_ASSERT(util::IsAligned(address, MemoryBlockUnitSize));
AMS_ASSERT(size > 0);
AMS_ASSERT(util::IsAligned(size, MemoryBlockUnitSize));
AMS_ABORT_UNLESS(manager.IsRegionInMemoryHeap(address, size));
/* Release the memory block. */
manager.ReleaseToHeap(address, size);
}
}