diff --git a/libraries/libstratosphere/include/stratosphere/lmem.hpp b/libraries/libstratosphere/include/stratosphere/lmem.hpp
index 18b8f8d45..fa85facb6 100644
--- a/libraries/libstratosphere/include/stratosphere/lmem.hpp
+++ b/libraries/libstratosphere/include/stratosphere/lmem.hpp
@@ -17,3 +17,4 @@
#include "lmem/lmem_common.hpp"
#include "lmem/lmem_exp_heap.hpp"
+#include "lmem/lmem_unit_heap.hpp"
diff --git a/libraries/libstratosphere/include/stratosphere/lmem/lmem_unit_heap.hpp b/libraries/libstratosphere/include/stratosphere/lmem/lmem_unit_heap.hpp
new file mode 100644
index 000000000..565ad825b
--- /dev/null
+++ b/libraries/libstratosphere/include/stratosphere/lmem/lmem_unit_heap.hpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2018-2019 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 "lmem_common.hpp"
+
+namespace ams::lmem {
+
+ enum InfoPlacement {
+ InfoPlacement_Head,
+ InfoPlacement_Tail,
+ };
+
+ HeapHandle CreateUnitHeap(void *address, size_t size, size_t unit_size, u32 option);
+ HeapHandle CreateUnitHeap(void *address, size_t size, size_t unit_size, u32 option, s32 alignment, InfoPlacement info_placement);
+ HeapHandle CreateUnitHeap(void *address, size_t size, size_t unit_size, u32 option, s32 alignment, HeapCommonHead *heap_head);
+ void DestroyUnitHeap(HeapHandle handle);
+
+ void InvalidateUnitHeap(HeapHandle handle);
+ void ExtendUnitHeap(HeapHandle handle, size_t size);
+
+ void *AllocateFromUnitHeap(HeapHandle handle);
+ void FreeToUnitHeap(HeapHandle handle, void *block);
+
+ size_t GetUnitHeapUnitSize(HeapHandle handle);
+ s32 GetUnitHeapAlignment(HeapHandle handle);
+ size_t GetUnitHeapFreeCount(HeapHandle handle);
+ size_t GetUnitHeapUsedCount(HeapHandle handle);
+
+ size_t GetUnitHeapRequiredSize(size_t unit_size, size_t unit_count, s32 alignment, bool internal_metadata);
+
+}
diff --git a/libraries/libstratosphere/source/lmem/impl/lmem_impl_common_heap.hpp b/libraries/libstratosphere/source/lmem/impl/lmem_impl_common_heap.hpp
index ba698be94..0e254f4fa 100644
--- a/libraries/libstratosphere/source/lmem/impl/lmem_impl_common_heap.hpp
+++ b/libraries/libstratosphere/source/lmem/impl/lmem_impl_common_heap.hpp
@@ -57,12 +57,12 @@ namespace ams::lmem::impl {
return end - start;
}
-
void InitializeHeapHead(HeapHead *out, u32 magic, void *start, void *end, u32 option);
void FinalizeHeap(HeapHead *heap);
bool ContainsAddress(HeapHandle handle, const void *address);
size_t GetHeapTotalSize(HeapHandle handle);
+ /* Debug Fill */
u32 GetDebugFillValue(FillType type);
u32 SetDebugFillValue(FillType type, u32 value);
diff --git a/libraries/libstratosphere/source/lmem/impl/lmem_impl_unit_heap.cpp b/libraries/libstratosphere/source/lmem/impl/lmem_impl_unit_heap.cpp
new file mode 100644
index 000000000..bbb59aa83
--- /dev/null
+++ b/libraries/libstratosphere/source/lmem/impl/lmem_impl_unit_heap.cpp
@@ -0,0 +1,263 @@
+/*
+ * Copyright (c) 2018-2019 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 "lmem_impl_unit_heap.hpp"
+
+namespace ams::lmem::impl {
+
+ namespace {
+
+ constexpr size_t MinimumAlignment = 4;
+
+ constexpr inline bool IsValidHeapHandle(HeapHandle handle) {
+ return handle->magic == UnitHeapMagic;
+ }
+
+ constexpr inline UnitHeapHead *GetUnitHeapHead(HeapHead *heap_head) {
+ return &heap_head->impl_head.unit_heap_head;
+ }
+
+ constexpr inline const UnitHeapHead *GetUnitHeapHead(const HeapHead *heap_head) {
+ return &heap_head->impl_head.unit_heap_head;
+ }
+
+ inline UnitHead *PopUnit(UnitHeapList *list) {
+ if (UnitHead *block = list->head; block != nullptr) {
+ list->head = block->next;
+ return block;
+ } else {
+ return nullptr;
+ }
+ }
+
+ inline void PushUnit(UnitHeapList *list, UnitHead *block) {
+ block->next = list->head;
+ list->head = block;
+ }
+
+ }
+
+ HeapHandle CreateUnitHeap(void *address, size_t size, size_t unit_size, s32 alignment, u16 option, InfoPlacement info_placement, HeapCommonHead *heap_head) {
+ AMS_ASSERT(address != nullptr);
+
+ /* Correct alignment, validate. */
+ if (alignment == 1 || alignment == 2) {
+ alignment = 4;
+ }
+ AMS_ASSERT(util::IsAligned(alignment, MinimumAlignment));
+ AMS_ASSERT(static_cast(MinimumAlignment) <= alignment);
+ AMS_ASSERT(unit_size >= sizeof(uintptr_t));
+
+ /* Setup heap metadata. */
+ UnitHeapHead *unit_heap = nullptr;
+ void *heap_start = nullptr;
+ void *heap_end = nullptr;
+ if (heap_head == nullptr) {
+ /* Internal heap metadata. */
+ if (info_placement == InfoPlacement_Head) {
+ heap_head = reinterpret_cast(util::AlignUp(address, MinimumAlignment));
+ unit_heap = GetUnitHeapHead(heap_head);
+ heap_end = util::AlignDown(reinterpret_cast(reinterpret_cast(address) + size), MinimumAlignment);
+ heap_start = util::AlignUp(reinterpret_cast(reinterpret_cast(heap_head) + sizeof(HeapHead)), alignment);
+ } else if (info_placement == InfoPlacement_Tail) {
+ heap_end = util::AlignDown(reinterpret_cast(reinterpret_cast(address) + size - sizeof(HeapHead)), MinimumAlignment);
+ heap_head = reinterpret_cast(heap_end);
+ unit_heap = GetUnitHeapHead(heap_head);
+ heap_start = util::AlignUp(address, alignment);
+ } else {
+ AMS_ASSERT(false);
+ }
+ } else {
+ /* External heap metadata. */
+ unit_heap = GetUnitHeapHead(heap_head);
+ heap_end = util::AlignDown(reinterpret_cast(reinterpret_cast(address) + size), MinimumAlignment);
+ heap_start = util::AlignUp(address, alignment);
+ }
+
+ /* Correct unit size. */
+ unit_size = util::AlignUp(unit_size, alignment);
+
+ /* Don't allow a heap with start after end. */
+ if (heap_start > heap_end) {
+ return nullptr;
+ }
+
+ /* Don't allow a heap with no units. */
+ size_t max_units = GetPointerDifference(heap_start, heap_end) / unit_size;
+ if (max_units == 0) {
+ return nullptr;
+ }
+
+ /* Set real heap end. */
+ heap_end = reinterpret_cast(reinterpret_cast(heap_start) + max_units * unit_size);
+
+ /* Initialize the parent heap. */
+ InitializeHeapHead(heap_head, UnitHeapMagic, heap_start, heap_end, option);
+
+ /* Initialize the actual unit heap. */
+ {
+ unit_heap->free_list.head = reinterpret_cast(heap_start);
+ unit_heap->unit_size = unit_size;
+ unit_heap->alignment = alignment;
+ unit_heap->num_units = 0;
+
+ /* Create the new units. */
+ UnitHead *cur_tail = unit_heap->free_list.head;
+ for (size_t i = 0; i < max_units - 1; i++) {
+ cur_tail->next = reinterpret_cast(reinterpret_cast(cur_tail) + unit_size);
+ cur_tail = cur_tail->next;
+ }
+ cur_tail->next = nullptr;
+ }
+
+ /* Return the heap header as handle. */
+ return heap_head;
+ }
+
+ void DestroyUnitHeap(HeapHandle handle) {
+ AMS_ASSERT(IsValidHeapHandle(handle));
+
+ /* Validate that the heap has no living units. */
+ UnitHeapHead *unit_heap = GetUnitHeapHead(handle);
+ if (unit_heap->free_list.head != nullptr) {
+ AMS_ASSERT(unit_heap->num_units == 0);
+ unit_heap->free_list.head = nullptr;
+ }
+
+ FinalizeHeap(handle);
+ }
+
+ void InvalidateUnitHeap(HeapHandle handle) {
+ AMS_ASSERT(IsValidHeapHandle(handle));
+ GetUnitHeapHead(handle)->free_list.head = nullptr;
+ }
+
+ void ExtendUnitHeap(HeapHandle handle, size_t size) {
+ AMS_ASSERT(IsValidHeapHandle(handle));
+
+ /* Find the current tail unit, and insert as next. */
+ UnitHeapHead *unit_heap = GetUnitHeapHead(handle);
+ UnitHead *cur_tail;
+ if (unit_heap->free_list.head != nullptr) {
+ cur_tail = unit_heap->free_list.head;
+
+ while (cur_tail->next != nullptr) {
+ cur_tail = cur_tail->next;
+ }
+
+ cur_tail->next = reinterpret_cast(handle->heap_end);
+ cur_tail = cur_tail->next;
+ cur_tail->next = nullptr;
+ } else {
+ /* All units are allocated, so set the free list to be at the end of the heap area. */
+ unit_heap->free_list.head = reinterpret_cast(handle->heap_end);
+ cur_tail = unit_heap->free_list.head;
+ cur_tail->next = nullptr;
+ }
+
+ /* Calculate new unit extents. */
+ void *new_units_start = handle->heap_end;
+ void *new_units_end = reinterpret_cast(reinterpret_cast(new_units_start) + size);
+ size_t num_new_units = GetPointerDifference(new_units_start, new_units_end) / unit_heap->unit_size;
+ AMS_ASSERT(num_new_units > 0);
+
+ /* Create the new units. */
+ for (size_t i = 0; i < num_new_units - 1; i++) {
+ cur_tail->next = reinterpret_cast(reinterpret_cast(cur_tail) + unit_heap->unit_size);
+ cur_tail = cur_tail->next;
+ }
+ cur_tail->next = nullptr;
+
+ /* Note that the heap is bigger. */
+ handle->heap_end = new_units_end;
+ }
+
+ void *AllocateFromUnitHeap(HeapHandle handle) {
+ AMS_ASSERT(IsValidHeapHandle(handle));
+
+ /* Allocate a unit. */
+ UnitHeapHead *unit_heap = GetUnitHeapHead(handle);
+ UnitHead *unit = PopUnit(&unit_heap->free_list);
+ if (unit != nullptr) {
+ /* Fill memory with pattern for debug, if needed. */
+ FillAllocatedMemory(handle, unit, unit_heap->unit_size);
+
+ /* Note that we allocated a unit. */
+ unit_heap->num_units++;
+ }
+
+ return unit;
+ }
+
+ void FreeToUnitHeap(HeapHandle handle, void *block) {
+ AMS_ASSERT(IsValidHeapHandle(handle));
+
+ /* Allow Free(nullptr) to succeed. */
+ if (block == nullptr) {
+ return;
+ }
+
+ /* Fill memory with pattern for debug, if needed. */
+ UnitHeapHead *unit_heap = GetUnitHeapHead(handle);
+ FillFreedMemory(handle, block, unit_heap->unit_size);
+
+ /* Push the unit onto the free list. */
+ PushUnit(&unit_heap->free_list, reinterpret_cast(block));
+
+ /* Note that we freed a unit. */
+ unit_heap->num_units--;
+ }
+
+ size_t GetUnitHeapUnitSize(HeapHandle handle) {
+ AMS_ASSERT(IsValidHeapHandle(handle));
+ return GetUnitHeapHead(handle)->unit_size;
+ }
+
+ s32 GetUnitHeapAlignment(HeapHandle handle) {
+ AMS_ASSERT(IsValidHeapHandle(handle));
+ return GetUnitHeapHead(handle)->alignment;
+ }
+
+ size_t GetUnitHeapFreeCount(HeapHandle handle) {
+ AMS_ASSERT(IsValidHeapHandle(handle));
+
+ size_t count = 0;
+ for (UnitHead *cur = GetUnitHeapHead(handle)->free_list.head; cur != nullptr; cur = cur->next) {
+ count++;
+ }
+ return count;
+ }
+
+ size_t GetUnitHeapUsedCount(HeapHandle handle) {
+ AMS_ASSERT(IsValidHeapHandle(handle));
+ return GetUnitHeapHead(handle)->num_units;
+ }
+
+ size_t GetUnitHeapRequiredSize(size_t unit_size, size_t unit_count, s32 alignment, bool internal_metadata) {
+ /* Nintendo does not round up alignment here, even though they do so in CreateUnitHeap. */
+ /* We will round up alignment to return more accurate results. */
+ if (alignment == 1 || alignment == 2) {
+ alignment = 4;
+ }
+
+ AMS_ASSERT(util::IsAligned(alignment, MinimumAlignment));
+ AMS_ASSERT(static_cast(MinimumAlignment) <= alignment);
+ AMS_ASSERT(unit_size >= sizeof(uintptr_t));
+
+ return (alignment - 1) + util::AlignUp(unit_size, alignment) + (internal_metadata ? sizeof(HeapHead) : 0);
+ }
+
+}
diff --git a/libraries/libstratosphere/source/lmem/impl/lmem_impl_unit_heap.hpp b/libraries/libstratosphere/source/lmem/impl/lmem_impl_unit_heap.hpp
new file mode 100644
index 000000000..4c4dd5a97
--- /dev/null
+++ b/libraries/libstratosphere/source/lmem/impl/lmem_impl_unit_heap.hpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2018-2019 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 "lmem_impl_common_heap.hpp"
+
+namespace ams::lmem::impl {
+
+ HeapHandle CreateUnitHeap(void *address, size_t size, size_t unit_size, s32 alignment, u16 option, InfoPlacement info_placement, HeapCommonHead *heap_head);
+ void DestroyUnitHeap(HeapHandle handle);
+
+ void InvalidateUnitHeap(HeapHandle handle);
+ void ExtendUnitHeap(HeapHandle handle, size_t size);
+
+ void *AllocateFromUnitHeap(HeapHandle handle);
+ void FreeToUnitHeap(HeapHandle handle, void *block);
+
+ size_t GetUnitHeapUnitSize(HeapHandle handle);
+ s32 GetUnitHeapAlignment(HeapHandle handle);
+ size_t GetUnitHeapFreeCount(HeapHandle handle);
+ size_t GetUnitHeapUsedCount(HeapHandle handle);
+
+ size_t GetUnitHeapRequiredSize(size_t unit_size, size_t unit_count, s32 alignment, bool internal_metadata);
+
+}
\ No newline at end of file
diff --git a/libraries/libstratosphere/source/lmem/lmem_unit_heap.cpp b/libraries/libstratosphere/source/lmem/lmem_unit_heap.cpp
new file mode 100644
index 000000000..3b613bb71
--- /dev/null
+++ b/libraries/libstratosphere/source/lmem/lmem_unit_heap.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2018-2019 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/lmem_impl_unit_heap.hpp"
+
+namespace ams::lmem {
+
+ HeapHandle CreateUnitHeap(void *address, size_t size, size_t unit_size, u32 option) {
+ return impl::CreateUnitHeap(address, size, unit_size, DefaultAlignment, static_cast(option), InfoPlacement_Head, nullptr);
+ }
+
+ HeapHandle CreateUnitHeap(void *address, size_t size, size_t unit_size, u32 option, s32 alignment, InfoPlacement info_placement) {
+ return impl::CreateUnitHeap(address, size, unit_size, alignment, static_cast(option), info_placement, nullptr);
+ }
+
+ HeapHandle CreateUnitHeap(void *address, size_t size, size_t unit_size, u32 option, s32 alignment, HeapCommonHead *heap_head) {
+ return impl::CreateUnitHeap(address, size, unit_size, alignment, static_cast(option), InfoPlacement_Head, heap_head);
+ }
+
+ void DestroyUnitHeap(HeapHandle handle) {
+ impl::DestroyUnitHeap(handle);
+ }
+
+ void InvalidateUnitHeap(HeapHandle handle) {
+ impl::ScopedHeapLock lk(handle);
+ impl::InvalidateUnitHeap(handle);
+ }
+
+ void ExtendUnitHeap(HeapHandle handle, size_t size) {
+ impl::ScopedHeapLock lk(handle);
+ impl::ExtendUnitHeap(handle, size);
+ }
+
+ void *AllocateFromUnitHeap(HeapHandle handle) {
+ impl::ScopedHeapLock lk(handle);
+ return impl::AllocateFromUnitHeap(handle);
+ }
+
+ void FreeToUnitHeap(HeapHandle handle, void *block) {
+ impl::ScopedHeapLock lk(handle);
+ impl::FreeToUnitHeap(handle, block);
+ }
+
+ size_t GetUnitHeapUnitSize(HeapHandle handle) {
+ /* Nintendo doesn't acquire a lock here. */
+ return impl::GetUnitHeapUnitSize(handle);
+ }
+
+ s32 GetUnitHeapAlignment(HeapHandle handle) {
+ /* Nintendo doesn't acquire a lock here. */
+ return impl::GetUnitHeapAlignment(handle);
+ }
+
+ size_t GetUnitHeapFreeCount(HeapHandle handle) {
+ impl::ScopedHeapLock lk(handle);
+ return impl::GetUnitHeapFreeCount(handle);
+ }
+
+ size_t GetUnitHeapUsedCount(HeapHandle handle) {
+ impl::ScopedHeapLock lk(handle);
+ return impl::GetUnitHeapUsedCount(handle);
+ }
+
+ size_t GetUnitHeapRequiredSize(size_t unit_size, size_t unit_count, s32 alignment, bool internal_metadata) {
+ return impl::GetUnitHeapRequiredSize(unit_size, unit_count, alignment, internal_metadata);
+ }
+
+}