diff --git a/libraries/libstratosphere/include/stratosphere/os.hpp b/libraries/libstratosphere/include/stratosphere/os.hpp index bb0a0e27e..7bb10a349 100644 --- a/libraries/libstratosphere/include/stratosphere/os.hpp +++ b/libraries/libstratosphere/include/stratosphere/os.hpp @@ -21,7 +21,8 @@ #include #include #include -#include +#include +#include #include #include #include diff --git a/libraries/libstratosphere/include/stratosphere/os/os_memory_attribute.hpp b/libraries/libstratosphere/include/stratosphere/os/os_memory_attribute.hpp new file mode 100644 index 000000000..068516e8a --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/os/os_memory_attribute.hpp @@ -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 . + */ +#pragma once +#include +#include +#include + +namespace ams::os { + + enum MemoryAttribute { + MemoryAttribute_Normal, + MemoryAttribute_Uncached, + }; + + void SetMemoryAttribute(uintptr_t address, size_t size, MemoryAttribute attr); + +} diff --git a/libraries/libstratosphere/include/stratosphere/os/os_memory_common.hpp b/libraries/libstratosphere/include/stratosphere/os/os_memory_common.hpp index 523d2d127..0a418b632 100644 --- a/libraries/libstratosphere/include/stratosphere/os/os_memory_common.hpp +++ b/libraries/libstratosphere/include/stratosphere/os/os_memory_common.hpp @@ -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), diff --git a/libraries/libstratosphere/include/stratosphere/os/os_memory_heap.hpp b/libraries/libstratosphere/include/stratosphere/os/os_memory_heap.hpp new file mode 100644 index 000000000..e49d9b304 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/os/os_memory_heap.hpp @@ -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 . + */ +#pragma once +#include +#include +#include diff --git a/libraries/libstratosphere/include/stratosphere/os/os_memory_heap_api.hpp b/libraries/libstratosphere/include/stratosphere/os/os_memory_heap_api.hpp index 127820ea5..5f782c1f8 100644 --- a/libraries/libstratosphere/include/stratosphere/os/os_memory_heap_api.hpp +++ b/libraries/libstratosphere/include/stratosphere/os/os_memory_heap_api.hpp @@ -16,10 +16,15 @@ #pragma once #include #include -#include +#include 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); diff --git a/libraries/libstratosphere/include/stratosphere/os/os_memory_heap_common.hpp b/libraries/libstratosphere/include/stratosphere/os/os_memory_heap_common.hpp new file mode 100644 index 000000000..3091541a3 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/os/os_memory_heap_common.hpp @@ -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 . + */ +#pragma once +#include +#include +#include + +namespace ams::os { + + constexpr inline size_t MemoryHeapUnitSize = 2_MB; + constexpr inline size_t MemoryBlockUnitSize = 2_MB; + + constexpr inline size_t MemoryPageSize = 4_KB; + +} diff --git a/libraries/libstratosphere/source/os/impl/os_memory_attribute_impl.hpp b/libraries/libstratosphere/source/os/impl/os_memory_attribute_impl.hpp new file mode 100644 index 000000000..2bbda482b --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_memory_attribute_impl.hpp @@ -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 . + */ +#pragma once +#include + +namespace ams::os::impl { + + void SetMemoryAttributeImpl(uintptr_t address, size_t size, MemoryAttribute attr); + +} diff --git a/libraries/libstratosphere/source/os/impl/os_memory_attribute_impl.os.horizon.cpp b/libraries/libstratosphere/source/os/impl/os_memory_attribute_impl.os.horizon.cpp new file mode 100644 index 000000000..23ec70283 --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_memory_attribute_impl.os.horizon.cpp @@ -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 . + */ +#include + +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(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; + } + } + +} diff --git a/libraries/libstratosphere/source/os/impl/os_memory_attribute_impl.os.linux.cpp b/libraries/libstratosphere/source/os/impl/os_memory_attribute_impl.os.linux.cpp new file mode 100644 index 000000000..285806af3 --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_memory_attribute_impl.os.linux.cpp @@ -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 . + */ +#include + +namespace ams::os::impl { + + void SetMemoryAttributeImpl(uintptr_t address, size_t size, MemoryAttribute attr) { + /* TODO: Should this do anything? */ + AMS_UNUSED(address, size, attr); + } + +} diff --git a/libraries/libstratosphere/source/os/impl/os_memory_attribute_impl.os.macos.cpp b/libraries/libstratosphere/source/os/impl/os_memory_attribute_impl.os.macos.cpp new file mode 100644 index 000000000..285806af3 --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_memory_attribute_impl.os.macos.cpp @@ -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 . + */ +#include + +namespace ams::os::impl { + + void SetMemoryAttributeImpl(uintptr_t address, size_t size, MemoryAttribute attr) { + /* TODO: Should this do anything? */ + AMS_UNUSED(address, size, attr); + } + +} diff --git a/libraries/libstratosphere/source/os/impl/os_memory_attribute_impl.os.windows.cpp b/libraries/libstratosphere/source/os/impl/os_memory_attribute_impl.os.windows.cpp new file mode 100644 index 000000000..285806af3 --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_memory_attribute_impl.os.windows.cpp @@ -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 . + */ +#include + +namespace ams::os::impl { + + void SetMemoryAttributeImpl(uintptr_t address, size_t size, MemoryAttribute attr) { + /* TODO: Should this do anything? */ + AMS_UNUSED(address, size, attr); + } + +} diff --git a/libraries/libstratosphere/source/os/impl/os_memory_heap_manager.cpp b/libraries/libstratosphere/source/os/impl/os_memory_heap_manager.cpp new file mode 100644 index 000000000..a20119176 --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_memory_heap_manager.cpp @@ -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 . + */ +#include +#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(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(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; + } + +} diff --git a/libraries/libstratosphere/source/os/impl/os_memory_heap_manager.hpp b/libraries/libstratosphere/source/os/impl/os_memory_heap_manager.hpp new file mode 100644 index 000000000..b932e896d --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_memory_heap_manager.hpp @@ -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 . + */ +#pragma once +#include +#include "os_memory_heap_manager_types.hpp" +#include "os_resource_manager.hpp" + +namespace ams::os::impl { + + ALWAYS_INLINE MemoryHeapManager &GetMemoryHeapManager() { + return GetResourceManager().GetMemoryHeapManager(); + } + +} diff --git a/libraries/libstratosphere/source/os/impl/os_memory_heap_manager_impl.os.horizon.hpp b/libraries/libstratosphere/source/os/impl/os_memory_heap_manager_impl.os.horizon.hpp new file mode 100644 index 000000000..b44caf1a3 --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_memory_heap_manager_impl.os.horizon.hpp @@ -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 . + */ +#pragma once +#include + +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; + +} diff --git a/libraries/libstratosphere/source/os/impl/os_memory_heap_manager_impl.os.linux.hpp b/libraries/libstratosphere/source/os/impl/os_memory_heap_manager_impl.os.linux.hpp new file mode 100644 index 000000000..18632f27d --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_memory_heap_manager_impl.os.linux.hpp @@ -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 . + */ +#pragma once +#include +#include + +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(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(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(address), size, PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + AMS_ABORT_UNLESS(reserved != MAP_FAILED); + } + }; + + using MemoryHeapManagerImpl = MemoryHeapManagerLinuxImpl; + +} diff --git a/libraries/libstratosphere/source/os/impl/os_memory_heap_manager_impl.os.macos.hpp b/libraries/libstratosphere/source/os/impl/os_memory_heap_manager_impl.os.macos.hpp new file mode 100644 index 000000000..7a40c4a8c --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_memory_heap_manager_impl.os.macos.hpp @@ -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 . + */ +#pragma once +#include +#include + +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(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(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(address), size, PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + AMS_ABORT_UNLESS(reserved != MAP_FAILED); + } + }; + + using MemoryHeapManagerImpl = MemoryHeapManagerMacosImpl; + +} diff --git a/libraries/libstratosphere/source/os/impl/os_memory_heap_manager_impl.os.windows.hpp b/libraries/libstratosphere/source/os/impl/os_memory_heap_manager_impl.os.windows.hpp new file mode 100644 index 000000000..ea4fc4058 --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_memory_heap_manager_impl.os.windows.hpp @@ -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 . + */ +#pragma once +#include +#include + +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(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(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(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(util::AlignUp(reinterpret_cast(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), 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(address), static_cast(size), MEM_DECOMMIT); + AMS_ASSERT(res); + AMS_UNUSED(res); + } + }; + + using MemoryHeapManagerImpl = MemoryHeapManagerWindowsImpl; + +} diff --git a/libraries/libstratosphere/source/os/impl/os_memory_heap_manager_types.hpp b/libraries/libstratosphere/source/os/impl/os_memory_heap_manager_types.hpp new file mode 100644 index 000000000..9f990a1ca --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_memory_heap_manager_types.hpp @@ -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 . + */ +#pragma once +#include + +#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(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(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); + }; + +} diff --git a/libraries/libstratosphere/source/os/impl/os_memory_permission_impl.os.linux.cpp b/libraries/libstratosphere/source/os/impl/os_memory_permission_impl.os.linux.cpp new file mode 100644 index 000000000..d75a4891a --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_memory_permission_impl.os.linux.cpp @@ -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 . + */ +#include +#include + +namespace ams::os::impl { + + void SetMemoryPermissionImpl(uintptr_t address, size_t size, MemoryPermission perm) { + switch (perm) { + case MemoryPermission_None: + { + auto res = ::mprotect(reinterpret_cast(address), size, PROT_NONE); + AMS_ABORT_UNLESS(res); + AMS_UNUSED(res); + } + break; + case MemoryPermission_ReadOnly: + { + auto res = ::mprotect(reinterpret_cast(address), size, PROT_READ); + AMS_ABORT_UNLESS(res); + AMS_UNUSED(res); + } + break; + case MemoryPermission_ReadWrite: + { + auto res = ::mprotect(reinterpret_cast(address), size, PROT_READ | PROT_WRITE); + AMS_ABORT_UNLESS(res); + AMS_UNUSED(res); + } + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + +} diff --git a/libraries/libstratosphere/source/os/impl/os_memory_permission_impl.os.macos.cpp b/libraries/libstratosphere/source/os/impl/os_memory_permission_impl.os.macos.cpp new file mode 100644 index 000000000..d75a4891a --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_memory_permission_impl.os.macos.cpp @@ -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 . + */ +#include +#include + +namespace ams::os::impl { + + void SetMemoryPermissionImpl(uintptr_t address, size_t size, MemoryPermission perm) { + switch (perm) { + case MemoryPermission_None: + { + auto res = ::mprotect(reinterpret_cast(address), size, PROT_NONE); + AMS_ABORT_UNLESS(res); + AMS_UNUSED(res); + } + break; + case MemoryPermission_ReadOnly: + { + auto res = ::mprotect(reinterpret_cast(address), size, PROT_READ); + AMS_ABORT_UNLESS(res); + AMS_UNUSED(res); + } + break; + case MemoryPermission_ReadWrite: + { + auto res = ::mprotect(reinterpret_cast(address), size, PROT_READ | PROT_WRITE); + AMS_ABORT_UNLESS(res); + AMS_UNUSED(res); + } + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + +} diff --git a/libraries/libstratosphere/source/os/impl/os_memory_permission_impl.os.windows.cpp b/libraries/libstratosphere/source/os/impl/os_memory_permission_impl.os.windows.cpp new file mode 100644 index 000000000..5f5dd2799 --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_memory_permission_impl.os.windows.cpp @@ -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 . + */ +#include +#include + +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(remaining, 2_GB); + switch (perm) { + case MemoryPermission_None: + { + auto res = ::VirtualProtect(reinterpret_cast(address), static_cast(cur_size), PAGE_NOACCESS, std::addressof(old)); + AMS_ABORT_UNLESS(res); + AMS_UNUSED(res); + } + break; + case MemoryPermission_ReadOnly: + { + auto res = ::VirtualProtect(reinterpret_cast(address), static_cast(cur_size), PAGE_READONLY, std::addressof(old)); + AMS_ABORT_UNLESS(res); + AMS_UNUSED(res); + } + break; + case MemoryPermission_ReadWrite: + { + auto res = ::VirtualProtect(reinterpret_cast(address), static_cast(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; + } + } + +} diff --git a/libraries/libstratosphere/source/os/impl/os_resource_manager.hpp b/libraries/libstratosphere/source/os/impl/os_resource_manager.hpp index a4fcc645b..d717a37cb 100644 --- a/libraries/libstratosphere/source/os/impl/os_resource_manager.hpp +++ b/libraries/libstratosphere/source/os/impl/os_resource_manager.hpp @@ -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; } }; diff --git a/libraries/libstratosphere/source/os/impl/os_vamm_manager_impl.os.linux.hpp b/libraries/libstratosphere/source/os/impl/os_vamm_manager_impl.os.linux.hpp index 9f6ad083b..a15c838b3 100644 --- a/libraries/libstratosphere/source/os/impl/os_vamm_manager_impl.os.linux.hpp +++ b/libraries/libstratosphere/source/os/impl/os_vamm_manager_impl.os.linux.hpp @@ -37,7 +37,8 @@ namespace ams::os::impl { } static Result FreePhysicalMemoryImpl(uintptr_t address, size_t size) { - R_UNLESS(::mprotect(reinterpret_cast(address), size, PROT_NONE) == 0, os::ResultBusy()); + const auto reserved = ::mmap(reinterpret_cast(address), size, PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + R_UNLESS(reserved != MAP_FAILED, os::ResultBusy()); R_SUCCEED(); } diff --git a/libraries/libstratosphere/source/os/impl/os_vamm_manager_impl.os.macos.hpp b/libraries/libstratosphere/source/os/impl/os_vamm_manager_impl.os.macos.hpp index 16eb23bf0..f5b5855c8 100644 --- a/libraries/libstratosphere/source/os/impl/os_vamm_manager_impl.os.macos.hpp +++ b/libraries/libstratosphere/source/os/impl/os_vamm_manager_impl.os.macos.hpp @@ -37,7 +37,8 @@ namespace ams::os::impl { } static Result FreePhysicalMemoryImpl(uintptr_t address, size_t size) { - R_UNLESS(::mprotect(reinterpret_cast(address), size, PROT_NONE) == 0, os::ResultBusy()); + const auto reserved = ::mmap(reinterpret_cast(address), size, PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + R_UNLESS(reserved != MAP_FAILED, os::ResultBusy()); R_SUCCEED(); } diff --git a/libraries/libstratosphere/source/os/os_memory_attribute.cpp b/libraries/libstratosphere/source/os/os_memory_attribute.cpp new file mode 100644 index 000000000..c6c8f8ed9 --- /dev/null +++ b/libraries/libstratosphere/source/os/os_memory_attribute.cpp @@ -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 . + */ +#include +#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); + } + +} diff --git a/libraries/libstratosphere/source/os/os_memory_heap.cpp b/libraries/libstratosphere/source/os/os_memory_heap.cpp index f16871831..5b41789f6 100644 --- a/libraries/libstratosphere/source/os/os_memory_heap.cpp +++ b/libraries/libstratosphere/source/os/os_memory_heap.cpp @@ -14,17 +14,47 @@ * along with this program. If not, see . */ #include +#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); } }