diff --git a/libraries/config/templates/stratosphere.mk b/libraries/config/templates/stratosphere.mk
index f1eebe79c..f8a86bb54 100644
--- a/libraries/config/templates/stratosphere.mk
+++ b/libraries/config/templates/stratosphere.mk
@@ -15,7 +15,7 @@ include $(DEVKITPRO)/libnx/switch_rules
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
-export DEFINES = $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_STRATOSPHERE
+export DEFINES = $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_STRATOSPHERE -D_GNU_SOURCE
export SETTINGS = $(ATMOSPHERE_SETTINGS) -O2
export CFLAGS = $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE)
export CXXFLAGS = $(CFLAGS) $(ATMOSPHERE_CXXFLAGS)
diff --git a/libraries/libstratosphere/Makefile b/libraries/libstratosphere/Makefile
index 16328c048..15aad5a6c 100644
--- a/libraries/libstratosphere/Makefile
+++ b/libraries/libstratosphere/Makefile
@@ -15,7 +15,7 @@ include $(DEVKITPRO)/libnx/switch_rules
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
-DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_STRATOSPHERE
+DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_STRATOSPHERE -D_GNU_SOURCE
SETTINGS := $(ATMOSPHERE_SETTINGS) -O2
CFLAGS := $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE)
CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS) -flto
diff --git a/libraries/libstratosphere/include/stratosphere.hpp b/libraries/libstratosphere/include/stratosphere.hpp
index bc308430d..eafde1b2b 100644
--- a/libraries/libstratosphere/include/stratosphere.hpp
+++ b/libraries/libstratosphere/include/stratosphere.hpp
@@ -22,11 +22,15 @@
/* Libstratosphere-only utility. */
#include "stratosphere/util.hpp"
+/* Sadly required shims. */
+#include "stratosphere/svc/svc_stratosphere_shims.hpp"
+
/* Critical modules with no dependencies. */
#include "stratosphere/ams.hpp"
#include "stratosphere/os.hpp"
#include "stratosphere/dd.hpp"
#include "stratosphere/lmem.hpp"
+#include "stratosphere/mem.hpp"
/* Pull in all ID definitions from NCM. */
#include "stratosphere/ncm/ncm_ids.hpp"
@@ -54,7 +58,8 @@
#include "stratosphere/spl.hpp"
#include "stratosphere/updater.hpp"
+
/* Include FS last. */
#include "stratosphere/fs.hpp"
#include "stratosphere/fssrv.hpp"
-#include "stratosphere/fssystem.hpp"
+#include "stratosphere/fssystem.hpp"
\ No newline at end of file
diff --git a/libraries/libstratosphere/include/stratosphere/mem.hpp b/libraries/libstratosphere/include/stratosphere/mem.hpp
new file mode 100644
index 000000000..c583d6e8c
--- /dev/null
+++ b/libraries/libstratosphere/include/stratosphere/mem.hpp
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2018-2020 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
diff --git a/libraries/libstratosphere/include/stratosphere/mem/impl/heap/mem_impl_heap_cached_heap.hpp b/libraries/libstratosphere/include/stratosphere/mem/impl/heap/mem_impl_heap_cached_heap.hpp
new file mode 100644
index 000000000..8f0c5197e
--- /dev/null
+++ b/libraries/libstratosphere/include/stratosphere/mem/impl/heap/mem_impl_heap_cached_heap.hpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2018-2020 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::mem::impl::heap {
+
+ class TlsHeapCache;
+
+ class CachedHeap final {
+ NON_COPYABLE(CachedHeap);
+ private:
+ TlsHeapCache *tls_heap_cache;
+ public:
+ constexpr CachedHeap() : tls_heap_cache() { /* ... */ }
+ ~CachedHeap() { this->Finalize(); }
+
+ ALWAYS_INLINE CachedHeap(CachedHeap &&rhs) : tls_heap_cache(rhs.tls_heap_cache) {
+ rhs.tls_heap_cache = nullptr;
+ }
+ ALWAYS_INLINE CachedHeap &operator=(CachedHeap &&rhs) {
+ this->Reset();
+ this->tls_heap_cache = rhs.tls_heap_cache;
+ rhs.tls_heap_cache = nullptr;
+ return *this;
+ }
+
+ void *Allocate(size_t n);
+ void *Allocate(size_t n, size_t align);
+ size_t GetAllocationSize(const void *ptr);
+ errno_t Free(void *p);
+ errno_t FreeWithSize(void *p, size_t size);
+ errno_t Reallocate(void *ptr, size_t size, void **p);
+ errno_t Shrink(void *ptr, size_t size);
+
+ void ReleaseAllCache();
+ void Finalize();
+ bool CheckCache();
+ errno_t QueryV(int query, std::va_list vl);
+ errno_t Query(int query, ...);
+
+ void Reset() { this->Finalize(); }
+ void Reset(TlsHeapCache *thc);
+ TlsHeapCache *Release();
+
+ constexpr explicit ALWAYS_INLINE operator bool() const { return this->tls_heap_cache != nullptr; }
+ };
+
+}
diff --git a/libraries/libstratosphere/include/stratosphere/mem/impl/heap/mem_impl_heap_central_heap.hpp b/libraries/libstratosphere/include/stratosphere/mem/impl/heap/mem_impl_heap_central_heap.hpp
new file mode 100644
index 000000000..8b2e7d73d
--- /dev/null
+++ b/libraries/libstratosphere/include/stratosphere/mem/impl/heap/mem_impl_heap_central_heap.hpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2018-2020 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::mem::impl::heap {
+
+ class CachedHeap;
+ class TlsHeapCentral;
+
+ using HeapWalkCallback = int (*)(void *ptr, size_t size, void *user_data);
+
+ class CentralHeap final {
+ NON_COPYABLE(CentralHeap);
+ NON_MOVEABLE(CentralHeap);
+ public:
+ static constexpr size_t PageSize = 4_KB;
+ static constexpr size_t MinimumAlignment = alignof(u64);
+ using DestructorHandler = void (*)(void *start, void *end);
+ private:
+ TlsHeapCentral *tls_heap_central;
+ bool use_virtual_memory;
+ u32 option;
+ u8 *start;
+ u8 *end;
+ public:
+ constexpr CentralHeap() : tls_heap_central(), use_virtual_memory(), option(), start(), end() { /* ... */ }
+ ~CentralHeap() { this->Finalize(); }
+
+ errno_t Initialize(void *start, size_t size, u32 option);
+ void Finalize();
+
+ ALWAYS_INLINE void *Allocate(size_t n) { return this->Allocate(n, MinimumAlignment); }
+ void *Allocate(size_t n, size_t align);
+ size_t GetAllocationSize(const void *ptr);
+ errno_t Free(void *p);
+ errno_t FreeWithSize(void *p, size_t size);
+ errno_t Reallocate(void *ptr, size_t size, void **p);
+ errno_t Shrink(void *ptr, size_t size);
+
+ bool MakeCache(CachedHeap *cached_heap);
+ errno_t WalkAllocatedPointers(HeapWalkCallback callback, void *user_data);
+ errno_t QueryV(int query, std::va_list vl);
+ errno_t Query(int query, ...);
+ private:
+ errno_t QueryVImpl(int query, std::va_list *vl_ptr);
+ };
+
+ static_assert(sizeof(CentralHeap) <= sizeof(::ams::mem::impl::InternalCentralHeapStorage));
+ static_assert(alignof(CentralHeap) <= alignof(void *));
+
+}
diff --git a/libraries/libstratosphere/include/stratosphere/mem/impl/mem_impl_common.hpp b/libraries/libstratosphere/include/stratosphere/mem/impl/mem_impl_common.hpp
new file mode 100644
index 000000000..ff0bbaf56
--- /dev/null
+++ b/libraries/libstratosphere/include/stratosphere/mem/impl/mem_impl_common.hpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2018-2020 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::mem::impl {
+
+ constexpr inline size_t MaxSize = static_cast(std::numeric_limits::max());
+
+ using errno_t = int;
+
+ enum DumpMode {
+ DumpMode_Basic = (0 << 0),
+ DumpMode_Spans = (1 << 0),
+ DumpMode_Pointers = (1 << 1),
+ DumpMode_Pages = (1 << 2),
+ DumpMode_All = (DumpMode_Pages | DumpMode_Pointers | DumpMode_Spans | DumpMode_Basic),
+ };
+
+ enum AllocQuery {
+ AllocQuery_Dump = 0,
+ AllocQuery_PageSize = 1,
+ AllocQuery_AllocatedSize = 2,
+ AllocQuery_FreeSize = 3,
+ AllocQuery_SystemSize = 4,
+ AllocQuery_MaxAllocatableSize = 5,
+ AllocQuery_IsClean = 6,
+ AllocQuery_HeapHash = 7,
+ AllocQuery_UnifyFreeList = 8,
+ AllocQuery_SetColor = 9,
+ AllocQuery_GetColor = 10,
+ AllocQuery_SetName = 11,
+ AllocQuery_GetName = 12,
+ /* AllocQuery_Thirteen = 13, */
+ AllocQuery_CheckCache = 14,
+ AllocQuery_ClearCache = 15,
+ AllocQuery_FinalizeCache = 16,
+ AllocQuery_FreeSizeMapped = 17,
+ AllocQuery_MaxAllocatableSizeMapped = 18,
+ AllocQuery_DumpJson = 19,
+ };
+
+ enum HeapOption {
+ HeapOption_UseEnvironment = (1 << 0),
+ HeapOption_DisableCache = (1 << 2),
+ };
+
+ struct HeapHash {
+ size_t alloc_count;
+ size_t alloc_size;
+ size_t hash;
+ };
+ static_assert(std::is_pod::value);
+
+}
diff --git a/libraries/libstratosphere/include/stratosphere/mem/impl/mem_impl_declarations.hpp b/libraries/libstratosphere/include/stratosphere/mem/impl/mem_impl_declarations.hpp
new file mode 100644
index 000000000..1784e05b2
--- /dev/null
+++ b/libraries/libstratosphere/include/stratosphere/mem/impl/mem_impl_declarations.hpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2018-2020 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
+
+namespace ams::mem::impl {
+
+ namespace heap {
+
+ class CentralHeap;
+
+ }
+
+ using InternalCentralHeapStorage = ::ams::util::TypedStorage<::ams::mem::impl::heap::CentralHeap, sizeof(void *) * 6, alignof(void *)>;
+
+}
\ No newline at end of file
diff --git a/libraries/libstratosphere/include/stratosphere/mem/impl/mem_impl_heap.hpp b/libraries/libstratosphere/include/stratosphere/mem/impl/mem_impl_heap.hpp
new file mode 100644
index 000000000..0a918081a
--- /dev/null
+++ b/libraries/libstratosphere/include/stratosphere/mem/impl/mem_impl_heap.hpp
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2018-2020 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/mem/mem_standard_allocator.hpp b/libraries/libstratosphere/include/stratosphere/mem/mem_standard_allocator.hpp
new file mode 100644
index 000000000..4a265c4ca
--- /dev/null
+++ b/libraries/libstratosphere/include/stratosphere/mem/mem_standard_allocator.hpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2019-2020 Adubbz, 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::mem {
+
+ class StandardAllocator {
+ NON_COPYABLE(StandardAllocator);
+ NON_MOVEABLE(StandardAllocator);
+ public:
+ using WalkCallback = int (*)(void *ptr, size_t size, void *user_data);
+
+ struct AllocatorHash {
+ size_t allocated_count;
+ size_t allocated_size;
+ size_t hash;
+ };
+ private:
+ bool initialized;
+ bool enable_thread_cache;
+ uintptr_t unused;
+ os::TlsSlot tls_slot;
+ impl::InternalCentralHeapStorage central_heap_storage;
+ public:
+ StandardAllocator();
+ StandardAllocator(void *mem, size_t size);
+ StandardAllocator(void *mem, size_t size, bool enable_cache);
+
+ ~StandardAllocator() {
+ if (this->initialized) {
+ this->Finalize();
+ }
+ }
+
+ void Initialize(void *mem, size_t size);
+ void Initialize(void *mem, size_t size, bool enable_cache);
+ void Finalize();
+
+ void *Allocate(size_t size);
+ void *Allocate(size_t size, size_t alignment);
+ void Free(void *ptr);
+ void *Reallocate(void *ptr, size_t new_size);
+ size_t Shrink(void *ptr, size_t new_size);
+
+ void ClearThreadCache() const;
+ void CleanUpManagementArea() const;
+
+ size_t GetSizeOf(const void *ptr) const;
+ size_t GetTotalFreeSize() const;
+ size_t GetAllocatableSize() const;
+
+ void WalkAllocatedBlocks(WalkCallback callback, void *user_data) const;
+
+ void Dump() const;
+ AllocatorHash Hash() const;
+ };
+
+}
diff --git a/libraries/libstratosphere/include/stratosphere/os.hpp b/libraries/libstratosphere/include/stratosphere/os.hpp
index abd5e77d2..061da55e1 100644
--- a/libraries/libstratosphere/include/stratosphere/os.hpp
+++ b/libraries/libstratosphere/include/stratosphere/os.hpp
@@ -16,21 +16,25 @@
#pragma once
-#include "os/os_common_types.hpp"
-#include "os/os_memory_common.hpp"
-#include "os/os_tick.hpp"
-#include "os/os_managed_handle.hpp"
-#include "os/os_process_handle.hpp"
-#include "os/os_random.hpp"
-#include "os/os_mutex.hpp"
-#include "os/os_condvar.hpp"
-#include "os/os_rw_lock.hpp"
-#include "os/os_semaphore.hpp"
-#include "os/os_timeout_helper.hpp"
-#include "os/os_event.hpp"
-#include "os/os_system_event.hpp"
-#include "os/os_interrupt_event.hpp"
-#include "os/os_thread.hpp"
-#include "os/os_message_queue.hpp"
-#include "os/os_waitable_holder.hpp"
-#include "os/os_waitable_manager.hpp"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
diff --git a/libraries/libstratosphere/include/stratosphere/os/os_memory_common.hpp b/libraries/libstratosphere/include/stratosphere/os/os_memory_common.hpp
index dcf1ad944..070e3a8ab 100644
--- a/libraries/libstratosphere/include/stratosphere/os/os_memory_common.hpp
+++ b/libraries/libstratosphere/include/stratosphere/os/os_memory_common.hpp
@@ -22,4 +22,12 @@ namespace ams::os {
constexpr inline size_t MemoryBlockUnitSize = 0x200000;
+ enum MemoryPermission {
+ MemoryPermission_None = (0 << 0),
+ MemoryPermission_ReadOnly = (1 << 0),
+ MemoryPermission_WriteOnly = (1 << 1),
+
+ MemoryPermission_ReadWrite = MemoryPermission_ReadOnly | MemoryPermission_WriteOnly,
+ };
+
}
diff --git a/libraries/libstratosphere/include/stratosphere/os/os_memory_heap_api.hpp b/libraries/libstratosphere/include/stratosphere/os/os_memory_heap_api.hpp
new file mode 100644
index 000000000..fc051cd01
--- /dev/null
+++ b/libraries/libstratosphere/include/stratosphere/os/os_memory_heap_api.hpp
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2018-2020 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 {
+
+ 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_permission.hpp b/libraries/libstratosphere/include/stratosphere/os/os_memory_permission.hpp
new file mode 100644
index 000000000..588bba655
--- /dev/null
+++ b/libraries/libstratosphere/include/stratosphere/os/os_memory_permission.hpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2018-2020 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 {
+
+ void SetMemoryPermission(uintptr_t address, size_t size, MemoryPermission perm);
+
+}
diff --git a/libraries/libstratosphere/include/stratosphere/os/os_memory_virtual_address_api.hpp b/libraries/libstratosphere/include/stratosphere/os/os_memory_virtual_address_api.hpp
new file mode 100644
index 000000000..be91e072f
--- /dev/null
+++ b/libraries/libstratosphere/include/stratosphere/os/os_memory_virtual_address_api.hpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2018-2020 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 {
+
+ bool IsVirtualAddressMemoryEnabled();
+
+}
diff --git a/libraries/libstratosphere/include/stratosphere/os/os_thread.hpp b/libraries/libstratosphere/include/stratosphere/os/os_thread.hpp
index 4dab3d8eb..2e24d57be 100644
--- a/libraries/libstratosphere/include/stratosphere/os/os_thread.hpp
+++ b/libraries/libstratosphere/include/stratosphere/os/os_thread.hpp
@@ -101,10 +101,19 @@ namespace ams::os {
}
};
- NX_INLINE u32 GetCurrentThreadPriority() {
- u32 prio;
+ ALWAYS_INLINE s32 GetCurrentThreadPriority() {
+ s32 prio;
R_ABORT_UNLESS(svcGetThreadPriority(&prio, CUR_THREAD_HANDLE));
return prio;
}
+ /* TODO: ThreadManager? */
+ ALWAYS_INLINE s32 GetCurrentProcessorNumber() {
+ return svcGetCurrentProcessorNumber();
+ }
+
+ ALWAYS_INLINE s32 GetCurrentCoreNumber() {
+ return GetCurrentProcessorNumber();
+ }
+
}
diff --git a/libraries/libstratosphere/include/stratosphere/os/os_thread_local_storage_api.hpp b/libraries/libstratosphere/include/stratosphere/os/os_thread_local_storage_api.hpp
new file mode 100644
index 000000000..0d6cd2a2a
--- /dev/null
+++ b/libraries/libstratosphere/include/stratosphere/os/os_thread_local_storage_api.hpp
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2018-2020 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 {
+
+ Result AllocateTlsSlot(TlsSlot *out, TlsDestructor destructor);
+
+ void FreeTlsSlot(TlsSlot slot);
+
+ uintptr_t GetTlsValue(TlsSlot slot);
+ void SetTlsValue(TlsSlot slot, uintptr_t value);
+
+}
diff --git a/libraries/libstratosphere/include/stratosphere/os/os_thread_local_storage_common.hpp b/libraries/libstratosphere/include/stratosphere/os/os_thread_local_storage_common.hpp
new file mode 100644
index 000000000..055ce4bc0
--- /dev/null
+++ b/libraries/libstratosphere/include/stratosphere/os/os_thread_local_storage_common.hpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2018-2020 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 {
+
+ struct TlsSlot {
+ u32 _value;
+ };
+
+ using TlsDestructor = void (*)(uintptr_t arg);
+
+ constexpr inline size_t TlsSlotCountMax = 16;
+ constexpr inline size_t SdkTlsSlotCountMax = 16;
+
+}
diff --git a/libraries/libstratosphere/include/stratosphere/svc/svc_stratosphere_shims.hpp b/libraries/libstratosphere/include/stratosphere/svc/svc_stratosphere_shims.hpp
new file mode 100644
index 000000000..e519c4754
--- /dev/null
+++ b/libraries/libstratosphere/include/stratosphere/svc/svc_stratosphere_shims.hpp
@@ -0,0 +1,491 @@
+/*
+ * Copyright (c) 2018-2020 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 .
+ */
+
+#if defined(ATMOSPHERE_BOARD_NINTENDO_NX) && defined(ATMOSPHERE_ARCH_ARM64)
+
+namespace ams::svc::aarch64::lp64 {
+
+ ALWAYS_INLINE Result SetHeapSize(::ams::svc::Address *out_address, ::ams::svc::Size size) {
+ return ::svcSetHeapSize(reinterpret_cast(out_address), size);
+ }
+
+ ALWAYS_INLINE Result SetMemoryPermission(::ams::svc::Address address, ::ams::svc::Size size, ::ams::svc::MemoryPermission perm) {
+ return ::svcSetMemoryPermission(reinterpret_cast(static_cast(address)), size, static_cast(perm));
+ }
+
+ ALWAYS_INLINE Result SetMemoryAttribute(::ams::svc::Address address, ::ams::svc::Size size, uint32_t mask, uint32_t attr) {
+ return ::svcSetMemoryAttribute(reinterpret_cast(static_cast(address)), size, mask, attr);
+ }
+
+ ALWAYS_INLINE Result MapMemory(::ams::svc::Address dst_address, ::ams::svc::Address src_address, ::ams::svc::Size size) {
+ return ::svcMapMemory(reinterpret_cast(static_cast(dst_address)), reinterpret_cast(static_cast(src_address)), size);
+ }
+
+ ALWAYS_INLINE Result UnmapMemory(::ams::svc::Address dst_address, ::ams::svc::Address src_address, ::ams::svc::Size size) {
+ return ::svcUnmapMemory(reinterpret_cast(static_cast(dst_address)), reinterpret_cast(static_cast(src_address)), size);
+ }
+
+ ALWAYS_INLINE Result QueryMemory(::ams::svc::UserPointer< ::ams::svc::lp64::MemoryInfo *> out_memory_info, ::ams::svc::PageInfo *out_page_info, ::ams::svc::Address address) {
+ return ::svcQueryMemory(reinterpret_cast<::MemoryInfo *>(out_memory_info.GetPointerUnsafe()), reinterpret_cast(out_page_info), address);
+ }
+
+ ALWAYS_INLINE void ExitProcess() {
+ return ::svcExitProcess();
+ }
+
+ ALWAYS_INLINE Result CreateThread(::ams::svc::Handle *out_handle, ::ams::svc::ThreadFunc func, ::ams::svc::Address arg, ::ams::svc::Address stack_bottom, int32_t priority, int32_t core_id) {
+ return ::svcCreateThread(out_handle, reinterpret_cast(static_cast(func)), reinterpret_cast(static_cast(arg)), reinterpret_cast(static_cast(stack_bottom)), priority, core_id);
+ }
+
+ ALWAYS_INLINE Result StartThread(::ams::svc::Handle thread_handle) {
+ return ::svcStartThread(thread_handle);
+ }
+
+ ALWAYS_INLINE void ExitThread() {
+ return ::svcExitThread();
+ }
+
+ ALWAYS_INLINE void SleepThread(int64_t ns) {
+ return ::svcSleepThread(ns);
+ }
+
+ ALWAYS_INLINE Result GetThreadPriority(int32_t *out_priority, ::ams::svc::Handle thread_handle) {
+ return ::svcGetThreadPriority(out_priority, thread_handle);
+ }
+
+ ALWAYS_INLINE Result SetThreadPriority(::ams::svc::Handle thread_handle, int32_t priority) {
+ return ::svcSetThreadPriority(thread_handle, priority);
+ }
+
+ ALWAYS_INLINE Result GetThreadCoreMask(int32_t *out_core_id, uint64_t *out_affinity_mask, ::ams::svc::Handle thread_handle) {
+ return ::svcGetThreadCoreMask(out_core_id, out_affinity_mask, thread_handle);
+ }
+
+ ALWAYS_INLINE Result SetThreadCoreMask(::ams::svc::Handle thread_handle, int32_t core_id, uint64_t affinity_mask) {
+ return ::svcSetThreadCoreMask(thread_handle, core_id, affinity_mask);
+ }
+
+ ALWAYS_INLINE int32_t GetCurrentProcessorNumber() {
+ return ::svcGetCurrentProcessorNumber();
+ }
+
+ ALWAYS_INLINE Result SignalEvent(::ams::svc::Handle event_handle) {
+ return ::svcSignalEvent(event_handle);
+ }
+
+ ALWAYS_INLINE Result ClearEvent(::ams::svc::Handle event_handle) {
+ return ::svcClearEvent(event_handle);
+ }
+
+ ALWAYS_INLINE Result MapSharedMemory(::ams::svc::Handle shmem_handle, ::ams::svc::Address address, ::ams::svc::Size size, ::ams::svc::MemoryPermission map_perm) {
+ return ::svcMapSharedMemory(shmem_handle, reinterpret_cast(static_cast(address)), size, static_cast(map_perm));
+ }
+
+ ALWAYS_INLINE Result UnmapSharedMemory(::ams::svc::Handle shmem_handle, ::ams::svc::Address address, ::ams::svc::Size size) {
+ return ::svcUnmapSharedMemory(shmem_handle, reinterpret_cast(static_cast(address)), size);
+ }
+
+ ALWAYS_INLINE Result CreateTransferMemory(::ams::svc::Handle *out_handle, ::ams::svc::Address address, ::ams::svc::Size size, ::ams::svc::MemoryPermission map_perm) {
+ return ::svcCreateTransferMemory(out_handle, reinterpret_cast(static_cast(address)), size, static_cast(map_perm));
+ }
+
+ ALWAYS_INLINE Result CloseHandle(::ams::svc::Handle handle) {
+ return ::svcCloseHandle(handle);
+ }
+
+ ALWAYS_INLINE Result ResetSignal(::ams::svc::Handle handle) {
+ return ::svcResetSignal(handle);
+ }
+
+ ALWAYS_INLINE Result WaitSynchronization(int32_t *out_index, ::ams::svc::UserPointer handles, int32_t numHandles, int64_t timeout_ns) {
+ return ::svcWaitSynchronization(out_index, handles.GetPointerUnsafe(), numHandles, timeout_ns);
+ }
+
+ ALWAYS_INLINE Result CancelSynchronization(::ams::svc::Handle handle) {
+ return ::svcCancelSynchronization(handle);
+ }
+
+ ALWAYS_INLINE Result ArbitrateLock(::ams::svc::Handle thread_handle, ::ams::svc::Address address, uint32_t tag) {
+ return ::svcArbitrateLock(thread_handle, reinterpret_cast(static_cast(address)), tag);
+ }
+
+ ALWAYS_INLINE Result ArbitrateUnlock(::ams::svc::Address address) {
+ return ::svcArbitrateUnlock(reinterpret_cast(static_cast(address)));
+ }
+
+ ALWAYS_INLINE Result WaitProcessWideKeyAtomic(::ams::svc::Address address, ::ams::svc::Address cv_key, uint32_t tag, int64_t timeout_ns) {
+ return ::svcWaitProcessWideKeyAtomic(reinterpret_cast(static_cast(address)), reinterpret_cast(static_cast(cv_key)), tag, timeout_ns);
+ }
+
+ ALWAYS_INLINE void SignalProcessWideKey(::ams::svc::Address cv_key, int32_t count) {
+ return ::svcSignalProcessWideKey(reinterpret_cast(static_cast(cv_key)), count);
+ }
+
+ ALWAYS_INLINE int64_t GetSystemTick() {
+ return ::svcGetSystemTick();
+ }
+
+ ALWAYS_INLINE Result ConnectToNamedPort(::ams::svc::Handle *out_handle, ::ams::svc::UserPointer name) {
+ return ::svcConnectToNamedPort(out_handle, name.GetPointerUnsafe());
+ }
+
+ ALWAYS_INLINE Result SendSyncRequestLight(::ams::svc::Handle session_handle) {
+ return ::svcSendSyncRequestLight(session_handle);
+ }
+
+ ALWAYS_INLINE Result SendSyncRequest(::ams::svc::Handle session_handle) {
+ return ::svcSendSyncRequest(session_handle);
+ }
+
+ ALWAYS_INLINE Result SendSyncRequestWithUserBuffer(::ams::svc::Address message_buffer, ::ams::svc::Size message_buffer_size, ::ams::svc::Handle session_handle) {
+ return ::svcSendSyncRequestWithUserBuffer(reinterpret_cast(static_cast(message_buffer)), message_buffer_size, session_handle);
+ }
+
+ ALWAYS_INLINE Result SendAsyncRequestWithUserBuffer(::ams::svc::Handle *out_event_handle, ::ams::svc::Address message_buffer, ::ams::svc::Size message_buffer_size, ::ams::svc::Handle session_handle) {
+ return ::svcSendAsyncRequestWithUserBuffer(out_event_handle, reinterpret_cast(static_cast(message_buffer)), message_buffer_size, session_handle);
+ }
+
+ ALWAYS_INLINE Result GetProcessId(uint64_t *out_process_id, ::ams::svc::Handle process_handle) {
+ return ::svcGetProcessId(out_process_id, process_handle);
+ }
+
+ ALWAYS_INLINE Result GetThreadId(uint64_t *out_thread_id, ::ams::svc::Handle thread_handle) {
+ return ::svcGetThreadId(out_thread_id, thread_handle);
+ }
+
+ ALWAYS_INLINE void Break(::ams::svc::BreakReason break_reason, ::ams::svc::Address arg, ::ams::svc::Size size) {
+ ::svcBreak(break_reason, arg, size);
+ }
+
+ ALWAYS_INLINE Result OutputDebugString(::ams::svc::UserPointer debug_str, ::ams::svc::Size len) {
+ return ::svcOutputDebugString(debug_str.GetPointerUnsafe(), len);
+ }
+
+ ALWAYS_INLINE void ReturnFromException(::ams::Result result) {
+ return ::svcReturnFromException(result.GetValue());
+ }
+
+ ALWAYS_INLINE Result GetInfo(uint64_t *out, ::ams::svc::InfoType info_type, ::ams::svc::Handle handle, uint64_t info_subtype) {
+ return ::svcGetInfo(out, static_cast(info_type), handle, info_subtype);
+ }
+
+ ALWAYS_INLINE void FlushEntireDataCache() {
+ return ::svcFlushEntireDataCache();
+ }
+
+ ALWAYS_INLINE Result FlushDataCache(::ams::svc::Address address, ::ams::svc::Size size) {
+ return ::svcFlushDataCache(reinterpret_cast(static_cast(address)), size);
+ }
+
+ ALWAYS_INLINE Result MapPhysicalMemory(::ams::svc::Address address, ::ams::svc::Size size) {
+ return ::svcMapPhysicalMemory(reinterpret_cast(static_cast(address)), size);
+ }
+
+ ALWAYS_INLINE Result UnmapPhysicalMemory(::ams::svc::Address address, ::ams::svc::Size size) {
+ return ::svcUnmapPhysicalMemory(reinterpret_cast(static_cast(address)), size);
+ }
+
+ ALWAYS_INLINE Result GetDebugFutureThreadInfo(::ams::svc::lp64::LastThreadContext *out_context, uint64_t *thread_id, ::ams::svc::Handle debug_handle, int64_t ns) {
+ return ::svcGetDebugFutureThreadInfo(reinterpret_cast<::LastThreadContext *>(out_context), thread_id, debug_handle, ns);
+ }
+
+ ALWAYS_INLINE Result GetLastThreadInfo(::ams::svc::lp64::LastThreadContext *out_context, ::ams::svc::Address *out_tls_address, uint32_t *out_flags) {
+ return ::svcGetLastThreadInfo(reinterpret_cast<::LastThreadContext *>(out_context), reinterpret_cast(out_tls_address), out_flags);
+ }
+
+ ALWAYS_INLINE Result GetResourceLimitLimitValue(int64_t *out_limit_value, ::ams::svc::Handle resource_limit_handle, ::ams::svc::LimitableResource which) {
+ return ::svcGetResourceLimitLimitValue(out_limit_value, resource_limit_handle, static_cast<::LimitableResource>(which));
+ }
+
+ ALWAYS_INLINE Result GetResourceLimitCurrentValue(int64_t *out_current_value, ::ams::svc::Handle resource_limit_handle, ::ams::svc::LimitableResource which) {
+ return ::svcGetResourceLimitCurrentValue(out_current_value, resource_limit_handle, static_cast<::LimitableResource>(which));
+ }
+
+ ALWAYS_INLINE Result SetThreadActivity(::ams::svc::Handle thread_handle, ::ams::svc::ThreadActivity thread_activity) {
+ return ::svcSetThreadActivity(thread_handle, static_cast<::ThreadActivity>(thread_activity));
+ }
+
+ ALWAYS_INLINE Result GetThreadContext3(::ams::svc::UserPointer< ::ams::svc::ThreadContext *> out_context, ::ams::svc::Handle thread_handle) {
+ return ::svcGetThreadContext3(reinterpret_cast<::ThreadContext *>(out_context.GetPointerUnsafe()), thread_handle);
+ }
+
+ ALWAYS_INLINE Result WaitForAddress(::ams::svc::Address address, ::ams::svc::ArbitrationType arb_type, int32_t value, int64_t timeout_ns) {
+ return ::svcWaitForAddress(reinterpret_cast(static_cast(address)), arb_type, value, timeout_ns);
+ }
+
+ ALWAYS_INLINE Result SignalToAddress(::ams::svc::Address address, ::ams::svc::SignalType signal_type, int32_t value, int32_t count) {
+ return ::svcSignalToAddress(reinterpret_cast(static_cast(address)), signal_type, value, count);
+ }
+
+ ALWAYS_INLINE void SynchronizePreemptionState() {
+ return ::svcSynchronizePreemptionState();
+ }
+
+ ALWAYS_INLINE void KernelDebug(::ams::svc::KernelDebugType kern_debug_type, uint64_t arg0, uint64_t arg1, uint64_t arg2) {
+ return ::svcKernelDebug(kern_debug_type, arg0, arg1, arg2);
+ }
+
+ ALWAYS_INLINE void ChangeKernelTraceState(::ams::svc::KernelTraceState kern_trace_state) {
+ return ::svcChangeKernelTraceState(kern_trace_state);
+ }
+
+ ALWAYS_INLINE Result CreateSession(::ams::svc::Handle *out_server_session_handle, ::ams::svc::Handle *out_client_session_handle, bool is_light, ::ams::svc::Address name) {
+ return ::svcCreateSession(out_server_session_handle, out_client_session_handle, is_light, name);
+ }
+
+ ALWAYS_INLINE Result AcceptSession(::ams::svc::Handle *out_handle, ::ams::svc::Handle port) {
+ return ::svcAcceptSession(out_handle, port);
+ }
+
+ ALWAYS_INLINE Result ReplyAndReceiveLight(::ams::svc::Handle handle) {
+ return ::svcReplyAndReceiveLight(handle);
+ }
+
+ ALWAYS_INLINE Result ReplyAndReceive(int32_t *out_index, ::ams::svc::UserPointer handles, int32_t num_handles, ::ams::svc::Handle reply_target, int64_t timeout_ns) {
+ return ::svcReplyAndReceive(out_index, handles.GetPointerUnsafe(), num_handles, reply_target, timeout_ns);
+ }
+
+ ALWAYS_INLINE Result ReplyAndReceiveWithUserBuffer(int32_t *out_index, ::ams::svc::Address message_buffer, ::ams::svc::Size message_buffer_size, ::ams::svc::UserPointer handles, int32_t num_handles, ::ams::svc::Handle reply_target, int64_t timeout_ns) {
+ return ::svcReplyAndReceiveWithUserBuffer(out_index, reinterpret_cast(static_cast(message_buffer)), message_buffer_size, handles.GetPointerUnsafe(), num_handles, reply_target, timeout_ns);
+ }
+
+ ALWAYS_INLINE Result CreateEvent(::ams::svc::Handle *out_write_handle, ::ams::svc::Handle *out_read_handle) {
+ return ::svcCreateEvent(out_write_handle, out_read_handle);
+ }
+
+ ALWAYS_INLINE Result MapPhysicalMemoryUnsafe(::ams::svc::Address address, ::ams::svc::Size size) {
+ return ::svcMapPhysicalMemoryUnsafe(reinterpret_cast(static_cast(address)), size);
+ }
+
+ ALWAYS_INLINE Result UnmapPhysicalMemoryUnsafe(::ams::svc::Address address, ::ams::svc::Size size) {
+ return ::svcUnmapPhysicalMemoryUnsafe(reinterpret_cast(static_cast(address)), size);
+ }
+
+ ALWAYS_INLINE Result SetUnsafeLimit(::ams::svc::Size limit) {
+ return ::svcSetUnsafeLimit(limit);
+ }
+
+ ALWAYS_INLINE Result CreateCodeMemory(::ams::svc::Handle *out_handle, ::ams::svc::Address address, ::ams::svc::Size size) {
+ return ::svcCreateCodeMemory(out_handle, reinterpret_cast(static_cast(address)), size);
+ }
+
+ ALWAYS_INLINE Result ControlCodeMemory(::ams::svc::Handle code_memory_handle, ::ams::svc::CodeMemoryOperation operation, uint64_t address, uint64_t size, ::ams::svc::MemoryPermission perm) {
+ return ::svcControlCodeMemory(code_memory_handle, static_cast<::CodeMapOperation>(operation), reinterpret_cast(address), size, static_cast(perm));
+ }
+
+ ALWAYS_INLINE void SleepSystem() {
+ return ::svcSleepSystem();
+ }
+
+ ALWAYS_INLINE Result ReadWriteRegister(uint32_t *out_value, ::ams::svc::PhysicalAddress address, uint32_t mask, uint32_t value) {
+ return ::svcReadWriteRegister(out_value, address, mask, value);
+ }
+
+ ALWAYS_INLINE Result SetProcessActivity(::ams::svc::Handle process_handle, ::ams::svc::ProcessActivity process_activity) {
+ return ::svcSetProcessActivity(process_handle, static_cast<::ProcessActivity>(process_activity));
+ }
+
+ ALWAYS_INLINE Result CreateSharedMemory(::ams::svc::Handle *out_handle, ::ams::svc::Size size, ::ams::svc::MemoryPermission owner_perm, ::ams::svc::MemoryPermission remote_perm) {
+ return ::svcCreateSharedMemory(out_handle, size, static_cast(owner_perm), static_cast(remote_perm));
+ }
+
+ ALWAYS_INLINE Result MapTransferMemory(::ams::svc::Handle trmem_handle, ::ams::svc::Address address, ::ams::svc::Size size, ::ams::svc::MemoryPermission owner_perm) {
+ return ::svcMapTransferMemory(trmem_handle, reinterpret_cast(static_cast(address)), size, static_cast(owner_perm));
+ }
+
+ ALWAYS_INLINE Result UnmapTransferMemory(::ams::svc::Handle trmem_handle, ::ams::svc::Address address, ::ams::svc::Size size) {
+ return ::svcUnmapTransferMemory(trmem_handle, reinterpret_cast(static_cast(address)), size);
+ }
+
+ ALWAYS_INLINE Result CreateInterruptEvent(::ams::svc::Handle *out_read_handle, int32_t interrupt_id, ::ams::svc::InterruptType interrupt_type) {
+ return ::svcCreateInterruptEvent(out_read_handle, interrupt_id, static_cast(interrupt_type));
+ }
+
+ ALWAYS_INLINE Result QueryPhysicalAddress(::ams::svc::lp64::PhysicalMemoryInfo *out_info, ::ams::svc::Address address) {
+ return ::svcQueryPhysicalAddress(reinterpret_cast<::PhysicalMemoryInfo *>(out_info), address);
+ }
+
+ ALWAYS_INLINE Result QueryIoMapping(::ams::svc::Address *out_address, ::ams::svc::PhysicalAddress physical_address, ::ams::svc::Size size) {
+ return ::svcQueryIoMapping(reinterpret_cast(out_address), physical_address, size);
+ }
+
+ ALWAYS_INLINE Result CreateDeviceAddressSpace(::ams::svc::Handle *out_handle, uint64_t das_address, uint64_t das_size) {
+ return ::svcCreateDeviceAddressSpace(out_handle, das_address, das_size);
+ }
+
+ ALWAYS_INLINE Result AttachDeviceAddressSpace(::ams::svc::DeviceName device_name, ::ams::svc::Handle das_handle) {
+ return ::svcAttachDeviceAddressSpace(static_cast(device_name), das_handle);
+ }
+
+ ALWAYS_INLINE Result DetachDeviceAddressSpace(::ams::svc::DeviceName device_name, ::ams::svc::Handle das_handle) {
+ return ::svcDetachDeviceAddressSpace(static_cast(device_name), das_handle);
+ }
+
+ ALWAYS_INLINE Result MapDeviceAddressSpaceByForce(::ams::svc::Handle das_handle, ::ams::svc::Handle process_handle, uint64_t process_address, ::ams::svc::Size size, uint64_t device_address, ::ams::svc::MemoryPermission device_perm) {
+ return ::svcMapDeviceAddressSpaceByForce(das_handle, process_handle, process_address, size, device_address, static_cast(device_perm));
+ }
+
+ ALWAYS_INLINE Result MapDeviceAddressSpaceAligned(::ams::svc::Handle das_handle, ::ams::svc::Handle process_handle, uint64_t process_address, ::ams::svc::Size size, uint64_t device_address, ::ams::svc::MemoryPermission device_perm) {
+ return ::svcMapDeviceAddressSpaceAligned(das_handle, process_handle, process_address, size, device_address, static_cast(device_perm));
+ }
+
+ ALWAYS_INLINE Result MapDeviceAddressSpace(::ams::svc::Size *out_mapped_size, ::ams::svc::Handle das_handle, ::ams::svc::Handle process_handle, uint64_t process_address, ::ams::svc::Size size, uint64_t device_address, ::ams::svc::MemoryPermission device_perm) {
+ return ::svcMapDeviceAddressSpace(reinterpret_cast(out_mapped_size), das_handle, process_handle, process_address, size, device_address, static_cast(device_perm));
+ }
+
+ ALWAYS_INLINE Result UnmapDeviceAddressSpace(::ams::svc::Handle das_handle, ::ams::svc::Handle process_handle, uint64_t process_address, ::ams::svc::Size size, uint64_t device_address) {
+ return ::svcUnmapDeviceAddressSpace(das_handle, process_handle, process_address, size, device_address);
+ }
+
+ ALWAYS_INLINE Result InvalidateProcessDataCache(::ams::svc::Handle process_handle, uint64_t address, uint64_t size) {
+ return ::svcInvalidateProcessDataCache(process_handle, address, size);
+ }
+
+ ALWAYS_INLINE Result StoreProcessDataCache(::ams::svc::Handle process_handle, uint64_t address, uint64_t size) {
+ return ::svcStoreProcessDataCache(process_handle, address, size);
+ }
+
+ ALWAYS_INLINE Result FlushProcessDataCache(::ams::svc::Handle process_handle, uint64_t address, uint64_t size) {
+ return ::svcFlushProcessDataCache(process_handle, address, size);
+ }
+
+ ALWAYS_INLINE Result DebugActiveProcess(::ams::svc::Handle *out_handle, uint64_t process_id) {
+ return ::svcDebugActiveProcess(out_handle, process_id);
+ }
+
+ ALWAYS_INLINE Result BreakDebugProcess(::ams::svc::Handle debug_handle) {
+ return ::svcBreakDebugProcess(debug_handle);
+ }
+
+ ALWAYS_INLINE Result TerminateDebugProcess(::ams::svc::Handle debug_handle) {
+ return ::svcTerminateDebugProcess(debug_handle);
+ }
+
+ ALWAYS_INLINE Result GetDebugEvent(::ams::svc::UserPointer< ::ams::svc::lp64::DebugEventInfo *> out_info, ::ams::svc::Handle debug_handle) {
+ return ::svcGetDebugEvent(out_info.GetPointerUnsafe(), debug_handle);
+ }
+
+ ALWAYS_INLINE Result ContinueDebugEvent(::ams::svc::Handle debug_handle, uint32_t flags, ::ams::svc::UserPointer thread_ids, int32_t num_thread_ids) {
+ return ::svcContinueDebugEvent(debug_handle, flags, const_cast(thread_ids.GetPointerUnsafe()), num_thread_ids);
+ }
+
+ ALWAYS_INLINE Result GetProcessList(int32_t *out_num_processes, ::ams::svc::UserPointer out_process_ids, int32_t max_out_count) {
+ return ::svcGetProcessList(out_num_processes, out_process_ids.GetPointerUnsafe(), max_out_count);
+ }
+
+ ALWAYS_INLINE Result GetThreadList(int32_t *out_num_threads, ::ams::svc::UserPointer out_thread_ids, int32_t max_out_count, ::ams::svc::Handle debug_handle) {
+ return ::svcGetThreadList(out_num_threads, out_thread_ids.GetPointerUnsafe(), max_out_count, debug_handle);
+ }
+
+ ALWAYS_INLINE Result GetDebugThreadContext(::ams::svc::UserPointer< ::ams::svc::ThreadContext *> out_context, ::ams::svc::Handle debug_handle, uint64_t thread_id, uint32_t context_flags) {
+ return ::svcGetDebugThreadContext(reinterpret_cast<::ThreadContext *>(out_context.GetPointerUnsafe()), debug_handle, thread_id, context_flags);
+ }
+
+ ALWAYS_INLINE Result SetDebugThreadContext(::ams::svc::Handle debug_handle, uint64_t thread_id, ::ams::svc::UserPointer context, uint32_t context_flags) {
+ return ::svcSetDebugThreadContext(debug_handle, thread_id, reinterpret_cast(context.GetPointerUnsafe()), context_flags);
+ }
+
+ ALWAYS_INLINE Result QueryDebugProcessMemory(::ams::svc::UserPointer< ::ams::svc::lp64::MemoryInfo *> out_memory_info, ::ams::svc::PageInfo *out_page_info, ::ams::svc::Handle process_handle, ::ams::svc::Address address) {
+ return ::svcQueryDebugProcessMemory(reinterpret_cast<::MemoryInfo *>(out_memory_info.GetPointerUnsafe()), reinterpret_cast(out_page_info), process_handle, address);
+ }
+
+ ALWAYS_INLINE Result ReadDebugProcessMemory(::ams::svc::Address buffer, ::ams::svc::Handle debug_handle, ::ams::svc::Address address, ::ams::svc::Size size) {
+ return ::svcReadDebugProcessMemory(reinterpret_cast(static_cast(buffer)), debug_handle, address, size);
+ }
+
+ ALWAYS_INLINE Result WriteDebugProcessMemory(::ams::svc::Handle debug_handle, ::ams::svc::Address buffer, ::ams::svc::Address address, ::ams::svc::Size size) {
+ return ::svcWriteDebugProcessMemory(debug_handle, reinterpret_cast(static_cast(buffer)), address, size);
+ }
+
+ ALWAYS_INLINE Result SetHardwareBreakPoint(::ams::svc::HardwareBreakPointRegisterName name, uint64_t flags, uint64_t value) {
+ return ::svcSetHardwareBreakPoint(static_cast(name), flags, value);
+ }
+
+ ALWAYS_INLINE Result GetDebugThreadParam(uint64_t *out_64, uint32_t *out_32, ::ams::svc::Handle debug_handle, uint64_t thread_id, ::ams::svc::DebugThreadParam param) {
+ return ::svcGetDebugThreadParam(out_64, out_32, debug_handle, thread_id, static_cast<::DebugThreadParam>(param));
+ }
+
+ ALWAYS_INLINE Result GetSystemInfo(uint64_t *out, ::ams::svc::SystemInfoType info_type, ::ams::svc::Handle handle, uint64_t info_subtype) {
+ return ::svcGetSystemInfo(out, static_cast(info_type), handle, info_subtype);
+ }
+
+ ALWAYS_INLINE Result CreatePort(::ams::svc::Handle *out_server_handle, ::ams::svc::Handle *out_client_handle, int32_t max_sessions, bool is_light, ::ams::svc::Address name) {
+ return ::svcCreatePort(out_server_handle, out_client_handle, max_sessions, is_light, reinterpret_cast(static_cast(name)));
+ }
+
+ ALWAYS_INLINE Result ManageNamedPort(::ams::svc::Handle *out_server_handle, ::ams::svc::UserPointer name, int32_t max_sessions) {
+ return ::svcManageNamedPort(out_server_handle, name.GetPointerUnsafe(), max_sessions);
+ }
+
+ ALWAYS_INLINE Result ConnectToPort(::ams::svc::Handle *out_handle, ::ams::svc::Handle port) {
+ return ::svcConnectToPort(out_handle, port);
+ }
+
+ ALWAYS_INLINE Result SetProcessMemoryPermission(::ams::svc::Handle process_handle, uint64_t address, uint64_t size, ::ams::svc::MemoryPermission perm) {
+ return ::svcSetProcessMemoryPermission(process_handle, address, size, static_cast(perm));
+ }
+
+ ALWAYS_INLINE Result MapProcessMemory(::ams::svc::Address dst_address, ::ams::svc::Handle process_handle, uint64_t src_address, ::ams::svc::Size size) {
+ return ::svcMapProcessMemory(reinterpret_cast(static_cast(dst_address)), process_handle, src_address, size);
+ }
+
+ ALWAYS_INLINE Result UnmapProcessMemory(::ams::svc::Address dst_address, ::ams::svc::Handle process_handle, uint64_t src_address, ::ams::svc::Size size) {
+ return ::svcUnmapProcessMemory(reinterpret_cast(static_cast(dst_address)), process_handle, src_address, size);
+ }
+
+ ALWAYS_INLINE Result QueryProcessMemory(::ams::svc::UserPointer< ::ams::svc::lp64::MemoryInfo *> out_memory_info, ::ams::svc::PageInfo *out_page_info, ::ams::svc::Handle process_handle, uint64_t address) {
+ return ::svcQueryProcessMemory(reinterpret_cast<::MemoryInfo *>(out_memory_info.GetPointerUnsafe()), reinterpret_cast(out_page_info), process_handle, address);
+ }
+
+ ALWAYS_INLINE Result MapProcessCodeMemory(::ams::svc::Handle process_handle, uint64_t dst_address, uint64_t src_address, uint64_t size) {
+ return ::svcMapProcessCodeMemory(process_handle, dst_address, src_address, size);
+ }
+
+ ALWAYS_INLINE Result UnmapProcessCodeMemory(::ams::svc::Handle process_handle, uint64_t dst_address, uint64_t src_address, uint64_t size) {
+ return ::svcUnmapProcessCodeMemory(process_handle, dst_address, src_address, size);
+ }
+
+ ALWAYS_INLINE Result CreateProcess(::ams::svc::Handle *out_handle, ::ams::svc::UserPointer parameters, ::ams::svc::UserPointer caps, int32_t num_caps) {
+ return ::svcCreateProcess(out_handle, parameters.GetPointerUnsafe(), caps.GetPointerUnsafe(), num_caps);
+ }
+
+ ALWAYS_INLINE Result StartProcess(::ams::svc::Handle process_handle, int32_t priority, int32_t core_id, uint64_t main_thread_stack_size) {
+ return ::svcStartProcess(process_handle, priority, core_id, main_thread_stack_size);
+ }
+
+ ALWAYS_INLINE Result TerminateProcess(::ams::svc::Handle process_handle) {
+ return ::svcTerminateProcess(process_handle);
+ }
+
+ ALWAYS_INLINE Result GetProcessInfo(int64_t *out_info, ::ams::svc::Handle process_handle, ::ams::svc::ProcessInfoType info_type) {
+ return ::svcGetProcessInfo(out_info, process_handle, static_cast<::ProcessInfoType>(info_type));
+ }
+
+ ALWAYS_INLINE Result CreateResourceLimit(::ams::svc::Handle *out_handle) {
+ return ::svcCreateResourceLimit(out_handle);
+ }
+
+ ALWAYS_INLINE Result SetResourceLimitLimitValue(::ams::svc::Handle resource_limit_handle, ::ams::svc::LimitableResource which, int64_t limit_value) {
+ return ::svcSetResourceLimitLimitValue(resource_limit_handle, static_cast<::LimitableResource>(which), limit_value);
+ }
+
+ ALWAYS_INLINE void CallSecureMonitor(::ams::svc::lp64::SecureMonitorArguments *args) {
+ ::svcCallSecureMonitor(reinterpret_cast<::SecmonArgs *>(args));
+ }
+
+}
+
+#endif
\ No newline at end of file
diff --git a/libraries/libstratosphere/source/diag/diag_assertion_impl.cpp b/libraries/libstratosphere/source/diag/diag_assertion_impl.cpp
index 33fb125c8..ca66e9a1a 100644
--- a/libraries/libstratosphere/source/diag/diag_assertion_impl.cpp
+++ b/libraries/libstratosphere/source/diag/diag_assertion_impl.cpp
@@ -41,7 +41,7 @@ namespace ams::diag {
__builtin_unreachable();
}
- ALWAYS_INLINE void DebugLog(const char *format, ...) __attribute__((format(printf, 1, 2)));
+ inline void DebugLog(const char *format, ...) __attribute__((format(printf, 1, 2)));
#ifdef AMS_ENABLE_DEBUG_PRINT
os::Mutex g_debug_log_lock;
@@ -55,7 +55,7 @@ namespace ams::diag {
svc::OutputDebugString(g_debug_buffer, strlen(g_debug_buffer));
}
- void DebugLog(const char *format, ...) __attribute__((format(printf, 1, 2))) {
+ void DebugLog(const char *format, ...) {
::std::va_list vl;
va_start(vl, format);
DebugLogImpl(format, vl);
diff --git a/libraries/libstratosphere/source/mem/impl/heap/mem_impl_heap_cached_heap.cpp b/libraries/libstratosphere/source/mem/impl/heap/mem_impl_heap_cached_heap.cpp
new file mode 100644
index 000000000..7f70a68ba
--- /dev/null
+++ b/libraries/libstratosphere/source/mem/impl/heap/mem_impl_heap_cached_heap.cpp
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2018-2020 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 "mem_impl_heap_platform.hpp"
+#include "mem_impl_heap_tls_heap_static.hpp"
+#include "mem_impl_heap_tls_heap_central.hpp"
+
+namespace ams::mem::impl::heap {
+
+ void *CachedHeap::Allocate(size_t n) {
+ return this->tls_heap_cache->Allocate(n);
+ }
+
+ void *CachedHeap::Allocate(size_t n, size_t align) {
+ return this->tls_heap_cache->Allocate(n, align);
+ }
+
+ size_t CachedHeap::GetAllocationSize(const void *ptr) {
+ return this->tls_heap_cache->GetAllocationSize(ptr);
+ }
+
+ errno_t CachedHeap::Free(void *p) {
+ return this->tls_heap_cache->Free(p);
+ }
+
+ errno_t CachedHeap::FreeWithSize(void *p, size_t size) {
+ return this->tls_heap_cache->FreeWithSize(p, size);
+ }
+
+ errno_t CachedHeap::Reallocate(void *ptr, size_t size, void **p) {
+ return this->tls_heap_cache->Reallocate(ptr, size, p);
+ }
+
+ errno_t CachedHeap::Shrink(void *ptr, size_t size) {
+ return this->tls_heap_cache->Shrink(ptr, size);
+ }
+
+ void CachedHeap::ReleaseAllCache() {
+ if (this->tls_heap_cache) {
+ this->tls_heap_cache->ReleaseAllCache();
+ }
+ }
+
+ void CachedHeap::Finalize() {
+ if (this->tls_heap_cache) {
+ this->tls_heap_cache->Finalize();
+ this->tls_heap_cache = nullptr;
+ }
+ }
+
+ bool CachedHeap::CheckCache() {
+ bool cache = false;
+ auto err = this->Query(AllocQuery_CheckCache, std::addressof(cache));
+ AMS_ASSERT(err != 0);
+ return cache;
+ }
+
+ errno_t CachedHeap::QueryV(int _query, std::va_list vl) {
+ const AllocQuery query = static_cast(_query);
+ switch (query) {
+ case AllocQuery_CheckCache:
+ {
+ bool *out = va_arg(vl, bool *);
+ if (out) {
+ *out = (this->tls_heap_cache == nullptr) || this->tls_heap_cache->CheckCache();
+ }
+ return 0;
+ }
+ case AllocQuery_ClearCache:
+ {
+ this->ReleaseAllCache();
+ return 0;
+ }
+ case AllocQuery_FinalizeCache:
+ {
+ this->Finalize();
+ return 0;
+ }
+ default:
+ return EINVAL;
+ }
+ }
+
+ errno_t CachedHeap::Query(int query, ...) {
+ std::va_list vl;
+ va_start(vl, query);
+ auto err = this->QueryV(query, vl);
+ va_end(vl);
+ return err;
+ }
+
+ void CachedHeap::Reset(TlsHeapCache *thc) {
+ this->Finalize();
+ this->tls_heap_cache = thc;
+ }
+
+ TlsHeapCache *CachedHeap::Release() {
+ TlsHeapCache *ret = this->tls_heap_cache;
+ this->tls_heap_cache = nullptr;
+ return ret;
+ }
+
+}
diff --git a/libraries/libstratosphere/source/mem/impl/heap/mem_impl_heap_central_heap.cpp b/libraries/libstratosphere/source/mem/impl/heap/mem_impl_heap_central_heap.cpp
new file mode 100644
index 000000000..4685dbf4a
--- /dev/null
+++ b/libraries/libstratosphere/source/mem/impl/heap/mem_impl_heap_central_heap.cpp
@@ -0,0 +1,409 @@
+/*
+ * Copyright (c) 2018-2020 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 "mem_impl_heap_platform.hpp"
+#include "mem_impl_heap_tls_heap_static.hpp"
+#include "mem_impl_heap_tls_heap_central.hpp"
+
+namespace ams::mem::impl::heap {
+
+ errno_t CentralHeap::Initialize(void *start, size_t size, u32 option) {
+ /* Validate size. */
+ if (size == 0 || !util::IsAligned(size, PageSize)) {
+ return EINVAL;
+ }
+
+ /* Don't allow initializing twice. */
+ if (this->start) {
+ return EEXIST;
+ }
+
+ if (start) {
+ /* We were provided with a region to use as backing memory. */
+ u8 *aligned_start = reinterpret_cast(util::AlignUp(reinterpret_cast(start), PageSize));
+ u8 *aligned_end = reinterpret_cast(util::AlignDown(reinterpret_cast(start) + size, PageSize));
+ if (aligned_start >= aligned_end) {
+ return EINVAL;
+ }
+
+ this->start = aligned_start;
+ this->end = aligned_end;
+ this->option = option;
+ this->tls_heap_central = new (this->start) TlsHeapCentral;
+ if (auto err = this->tls_heap_central->Initialize(this->start, this->end - this->start, false); err != 0) {
+ this->tls_heap_central->~TlsHeapCentral();
+ this->tls_heap_central = nullptr;
+ AMS_ASSERT(err == 0);
+ return err;
+ }
+ this->use_virtual_memory = false;
+ } else {
+ /* We were not provided with a region to use as backing. */
+ void *mem;
+ if (auto err = AllocateVirtualMemory(std::addressof(mem), size); err != 0) {
+ return err;
+ }
+ if (!util::IsAligned(reinterpret_cast(mem), PageSize)) {
+ FreeVirtualMemory(mem, size);
+ size += PageSize;
+ if (auto err = AllocateVirtualMemory(std::addressof(mem), size); err != 0) {
+ return err;
+ }
+ }
+ this->start = static_cast(mem);
+ this->end = this->start + size;
+ this->option = option;
+ void *central = reinterpret_cast(util::AlignUp(reinterpret_cast(mem), PageSize));
+ if (auto err = AllocatePhysicalMemory(central, sizeof(TlsHeapCentral)); err != 0) {
+ return err;
+ }
+ this->tls_heap_central = new (central) TlsHeapCentral;
+ if (auto err = this->tls_heap_central->Initialize(central, size, true); err != 0) {
+ this->tls_heap_central->~TlsHeapCentral();
+ this->tls_heap_central = nullptr;
+ AMS_ASSERT(err == 0);
+ return err;
+ }
+ this->use_virtual_memory = true;
+ }
+
+ return 0;
+ }
+
+ void CentralHeap::Finalize() {
+ if (this->tls_heap_central) {
+ this->tls_heap_central->~TlsHeapCentral();
+ }
+ if (this->use_virtual_memory) {
+ mem::impl::physical_free(util::AlignUp(static_cast(this->start), PageSize), this->end - this->start);
+ mem::impl::virtual_free(this->start, this->end - this->start);
+ }
+ this->tls_heap_central = nullptr;
+ this->use_virtual_memory = false;
+ this->option = 0;
+ this->start = nullptr;
+ this->end = nullptr;
+ }
+
+ void *CentralHeap::Allocate(size_t n, size_t align) {
+ if (!util::IsPowerOfTwo(align)) {
+ return nullptr;
+ }
+ if (n > MaxSize) {
+ return nullptr;
+ }
+ if (align > PageSize) {
+ return this->tls_heap_central->CacheLargeMemoryWithBigAlign(util::AlignUp(n, PageSize), align);
+ }
+
+ const size_t real_size = TlsHeapStatic::GetRealSizeFromSizeAndAlignment(util::AlignUp(n, align), align);
+ const auto cls = TlsHeapStatic::GetClassFromSize(real_size);
+ if (!cls) {
+ return this->tls_heap_central->CacheLargeMemory(real_size);
+ }
+ if (real_size == 0) {
+ return nullptr;
+ }
+ AMS_ASSERT(cls < TlsHeapStatic::NumClassInfo);
+ return this->tls_heap_central->CacheSmallMemory(cls, align);
+ }
+
+ size_t CentralHeap::GetAllocationSize(const void *ptr) {
+ const auto cls = this->tls_heap_central->GetClassFromPointer(ptr);
+ if (cls > 0) {
+ /* Check that the pointer has alignment from out allocator. */
+ if (!util::IsAligned(reinterpret_cast(ptr), MinimumAlignment)) {
+ return 0;
+ }
+ AMS_ASSERT(cls < TlsHeapStatic::NumClassInfo);
+ return TlsHeapStatic::GetChunkSize(cls);
+ } else if (ptr != nullptr) {
+ return this->tls_heap_central->GetAllocationSize(ptr);
+ } else {
+ return 0;
+ }
+ }
+
+ errno_t CentralHeap::Free(void *ptr) {
+ /* Allow Free(nullptr) */
+ if (ptr == nullptr) {
+ return 0;
+ }
+
+ /* Check that the pointer has alignment from out allocator. */
+ if(!util::IsAligned(reinterpret_cast(ptr), MinimumAlignment)) {
+ AMS_ASSERT(util::IsAligned(reinterpret_cast(ptr), MinimumAlignment));
+ return EFAULT;
+ }
+
+ const auto cls = this->tls_heap_central->GetClassFromPointer(ptr);
+ if (cls >= 0) {
+ AMS_ASSERT(cls < TlsHeapStatic::NumClassInfo);
+ if (cls) {
+ return this->tls_heap_central->UncacheSmallMemory(ptr);
+ } else {
+ return this->tls_heap_central->UncacheLargeMemory(ptr);
+ }
+ } else {
+ AMS_ASSERT(cls >= 0);
+ return EFAULT;
+ }
+ }
+
+ errno_t CentralHeap::FreeWithSize(void *ptr, size_t size) {
+ if (TlsHeapStatic::GetClassFromSize(size)) {
+ return this->tls_heap_central->UncacheSmallMemory(ptr);
+ } else {
+ return this->tls_heap_central->UncacheLargeMemory(ptr);
+ }
+ }
+
+ errno_t CentralHeap::Reallocate(void *ptr, size_t size, void **p) {
+ AMS_ASSERT(ptr != nullptr && size != 0);
+ if (!size) {
+ return EINVAL;
+ }
+ if (size > MaxSize) {
+ return ENOMEM;
+ }
+
+ const auto cls_from_size = TlsHeapStatic::GetClassFromSize(size);
+ const auto cls_from_ptr = this->tls_heap_central->GetClassFromPointer(ptr);
+ if (cls_from_ptr) {
+ if (cls_from_ptr <= 0) {
+ return EFAULT;
+ } else if (cls_from_size && cls_from_size <= cls_from_ptr) {
+ *p = ptr;
+ return 0;
+ } else {
+ const size_t new_chunk_size = TlsHeapStatic::GetChunkSize(cls_from_ptr);
+ *p = this->Allocate(new_chunk_size);
+ if (*p) {
+ std::memcpy(*p, ptr, size);
+ return this->tls_heap_central->UncacheSmallMemory(ptr);
+ } else {
+ return ENOMEM;
+ }
+ }
+ } else if (cls_from_size) {
+ *p = this->Allocate(size);
+ if (*p) {
+ std::memcpy(*p, ptr, size);
+ return this->tls_heap_central->UncacheLargeMemory(ptr);
+ } else {
+ return ENOMEM;
+ }
+ } else {
+ return this->tls_heap_central->ReallocateLargeMemory(ptr, size, p);
+ }
+ }
+
+ errno_t CentralHeap::Shrink(void *ptr, size_t size) {
+ AMS_ASSERT(ptr != nullptr && size != 0);
+ if (!size) {
+ return EINVAL;
+ }
+ if (size > MaxSize) {
+ return ENOMEM;
+ }
+
+ const auto cls_from_size = TlsHeapStatic::GetClassFromSize(size);
+ const auto cls_from_ptr = this->tls_heap_central->GetClassFromPointer(ptr);
+ if (cls_from_ptr) {
+ if (cls_from_ptr <= 0) {
+ return EFAULT;
+ } else if (cls_from_size && cls_from_size <= cls_from_ptr) {
+ return 0;
+ } else {
+ return EINVAL;
+ }
+ } else if (cls_from_size) {
+ return this->tls_heap_central->ShrinkLargeMemory(ptr, PageSize);
+ } else {
+ return this->tls_heap_central->ShrinkLargeMemory(ptr, size);
+ }
+ }
+
+ bool CentralHeap::MakeCache(CachedHeap *cached_heap) {
+ if (cached_heap == nullptr) {
+ return false;
+ }
+
+ AMS_ASSERT(this->tls_heap_central != nullptr);
+ const auto cls = TlsHeapStatic::GetClassFromSize(sizeof(*cached_heap));
+ void *tls_heap_cache = this->tls_heap_central->CacheSmallMemoryForSystem(cls);
+ if (tls_heap_cache == nullptr) {
+ return false;
+ }
+
+ new (tls_heap_cache) TlsHeapCache(this->tls_heap_central, this->option);
+ if (this->tls_heap_central->AddThreadCache(reinterpret_cast(tls_heap_cache)) != 0) {
+ this->tls_heap_central->UncacheSmallMemory(tls_heap_cache);
+ return false;
+ }
+
+ cached_heap->Reset(reinterpret_cast(tls_heap_cache));
+ return true;
+ }
+
+ errno_t CentralHeap::WalkAllocatedPointers(HeapWalkCallback callback, void *user_data) {
+ if (!callback || !this->tls_heap_central) {
+ return EINVAL;
+ }
+ return this->tls_heap_central->WalkAllocatedPointers(callback, user_data);
+ }
+
+ errno_t CentralHeap::QueryV(int query, std::va_list vl) {
+ return this->QueryVImpl(query, std::addressof(vl));
+ }
+
+ errno_t CentralHeap::Query(int query, ...) {
+ std::va_list vl;
+ va_start(vl, query);
+ auto err = this->QueryVImpl(query, std::addressof(vl));
+ va_end(vl);
+ return err;
+ }
+
+ errno_t CentralHeap::QueryVImpl(int _query, std::va_list *vl_ptr) {
+ const AllocQuery query = static_cast(_query);
+ switch (query) {
+ case AllocQuery_Dump:
+ case AllocQuery_DumpJson:
+ {
+ auto dump_mode = static_cast(va_arg(*vl_ptr, int));
+ auto fd = va_arg(*vl_ptr, int);
+ if (this->tls_heap_central) {
+ this->tls_heap_central->Dump(dump_mode, fd, query == AllocQuery_DumpJson);
+ }
+ return 0;
+ }
+ case AllocQuery_PageSize:
+ {
+ size_t *out = va_arg(*vl_ptr, size_t *);
+ if (out) {
+ *out = PageSize;
+ }
+ return 0;
+ }
+ case AllocQuery_AllocatedSize:
+ case AllocQuery_FreeSize:
+ case AllocQuery_SystemSize:
+ case AllocQuery_MaxAllocatableSize:
+ {
+ size_t *out = va_arg(*vl_ptr, size_t *);
+ if (!out) {
+ return 0;
+ }
+ if (!this->tls_heap_central) {
+ *out = 0;
+ return 0;
+ }
+ TlsHeapMemStats stats;
+ this->tls_heap_central->GetMemStats(std::addressof(stats));
+ switch (query) {
+ case AllocQuery_AllocatedSize:
+ default:
+ *out = stats.allocated_size;
+ break;
+ case AllocQuery_FreeSize:
+ *out = stats.free_size;
+ break;
+ case AllocQuery_SystemSize:
+ *out = stats.system_size;
+ break;
+ case AllocQuery_MaxAllocatableSize:
+ *out = stats.max_allocatable_size;
+ break;
+ }
+ return 0;
+ }
+ case AllocQuery_IsClean:
+ {
+ int *out = va_arg(*vl_ptr, int *);
+ if (out) {
+ *out = !this->tls_heap_central || this->tls_heap_central->IsClean();
+ }
+ return 0;
+ }
+ case AllocQuery_HeapHash:
+ {
+ HeapHash *out = va_arg(*vl_ptr, HeapHash *);
+ if (out) {
+ if (this->tls_heap_central) {
+ this->tls_heap_central->CalculateHeapHash(out);
+ } else {
+ *out = {};
+ }
+ }
+ return 0;
+ }
+ case AllocQuery_UnifyFreeList:
+ /* NOTE: Nintendo does not check that the ptr is not null for this query, even though they do for other queries. */
+ this->tls_heap_central->IsClean();
+ return 0;
+ case AllocQuery_SetColor:
+ {
+ /* NOTE: Nintendo does not check that the ptr is not null for this query, even though they do for other queries. */
+ void *ptr = va_arg(*vl_ptr, void *);
+ int color = va_arg(*vl_ptr, int);
+ return this->tls_heap_central->SetColor(ptr, color);
+ }
+ case AllocQuery_GetColor:
+ {
+ /* NOTE: Nintendo does not check that the ptr is not null for this query, even though they do for other queries. */
+ void *ptr = va_arg(*vl_ptr, void *);
+ int *out = va_arg(*vl_ptr, int *);
+ return this->tls_heap_central->GetColor(ptr, out);
+ }
+ case AllocQuery_SetName:
+ {
+ /* NOTE: Nintendo does not check that the ptr is not null for this query, even though they do for other queries. */
+ void *ptr = va_arg(*vl_ptr, void *);
+ const char *name = va_arg(*vl_ptr, const char *);
+ return this->tls_heap_central->SetName(ptr, name);
+ }
+ case AllocQuery_GetName:
+ {
+ /* NOTE: Nintendo does not check that the ptr is not null for this query, even though they do for other queries. */
+ void *ptr = va_arg(*vl_ptr, void *);
+ char *dst = va_arg(*vl_ptr, char *);
+ size_t dst_size = va_arg(*vl_ptr, size_t);
+ return this->tls_heap_central->GetName(ptr, dst, dst_size);
+ }
+ case AllocQuery_FreeSizeMapped:
+ case AllocQuery_MaxAllocatableSizeMapped:
+ {
+ /* NOTE: Nintendo does not check that the ptr is not null for this query, even though they do for other queries. */
+ size_t *out = va_arg(*vl_ptr, size_t *);
+ size_t free_size;
+ size_t max_allocatable_size;
+ auto err = this->tls_heap_central->GetMappedMemStats(std::addressof(free_size), std::addressof(max_allocatable_size));
+ if (err == 0) {
+ if (query == AllocQuery_FreeSizeMapped) {
+ *out = free_size;
+ } else {
+ *out = max_allocatable_size;
+ }
+ }
+ return err;
+ }
+ default:
+ return EINVAL;
+ }
+ }
+
+}
diff --git a/libraries/libstratosphere/source/mem/impl/heap/mem_impl_heap_platform.hpp b/libraries/libstratosphere/source/mem/impl/heap/mem_impl_heap_platform.hpp
new file mode 100644
index 000000000..06088c0be
--- /dev/null
+++ b/libraries/libstratosphere/source/mem/impl/heap/mem_impl_heap_platform.hpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2018-2020 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 "../mem_impl_platform.hpp"
+
+namespace ams::mem::impl::heap {
+
+ using Prot = mem::impl::Prot;
+
+ inline errno_t AllocateVirtualMemory(void **ptr, size_t size) {
+ return ::ams::mem::impl::virtual_alloc(ptr, size);
+ }
+
+ inline errno_t FreeVirtualMemory(void *ptr, size_t size) {
+ return ::ams::mem::impl::virtual_free(ptr, size);
+ }
+
+ inline errno_t AllocatePhysicalMemory(void *ptr, size_t size) {
+ return ::ams::mem::impl::physical_alloc(ptr, size, static_cast(Prot_read | Prot_write));
+ }
+
+ inline errno_t FreePhysicalMemory(void *ptr, size_t size) {
+ return ::ams::mem::impl::physical_free(ptr, size);
+ }
+
+}
\ No newline at end of file
diff --git a/libraries/libstratosphere/source/mem/impl/heap/mem_impl_heap_tls_heap_cache.cpp b/libraries/libstratosphere/source/mem/impl/heap/mem_impl_heap_tls_heap_cache.cpp
new file mode 100644
index 000000000..2f5a453ad
--- /dev/null
+++ b/libraries/libstratosphere/source/mem/impl/heap/mem_impl_heap_tls_heap_cache.cpp
@@ -0,0 +1,557 @@
+/*
+ * Copyright (c) 2018-2020 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 "mem_impl_heap_platform.hpp"
+#include "mem_impl_heap_tls_heap_static.hpp"
+#include "mem_impl_heap_tls_heap_cache.hpp"
+#include "mem_impl_heap_tls_heap_central.hpp"
+
+namespace ams::mem::impl::heap {
+
+ TlsHeapCache::TlsHeapCache(TlsHeapCentral *central, u32 option) {
+ /* Choose function impls based on option. */
+ if ((option & HeapOption_DisableCache) != 0) {
+ this->allocate = AllocateImpl;
+ this->allocate_aligned = AllocateAlignedImpl;
+ this->free = FreeImpl;
+ this->free_with_size = FreeWithSizeImpl;
+ this->get_allocation_size = GetAllocationSizeImpl;
+ this->reallocate = ReallocateImpl;
+ this->shrink = ShrinkImpl;
+ } else {
+ this->allocate = AllocateImpl;
+ this->allocate_aligned = AllocateAlignedImpl;
+ this->free = FreeImpl;
+ this->free_with_size = FreeWithSizeImpl;
+ this->get_allocation_size = GetAllocationSizeImpl;
+ this->reallocate = ReallocateImpl;
+ this->shrink = ShrinkImpl;
+ }
+
+ /* Generate random bytes to mangle pointers. */
+ if (auto err = gen_random(std::addressof(this->mangle_val), sizeof(this->mangle_val)); err != 0) {
+ s64 epoch_time;
+ epochtime(std::addressof(epoch_time));
+ this->mangle_val = reinterpret_cast(std::addressof(epoch_time)) ^ static_cast(epoch_time);
+ }
+
+ /* Set member variables. */
+ this->central = central;
+ this->total_heap_size = central->GetTotalHeapSize();
+ this->heap_option = option;
+ this->total_cached_size = 0;
+ this->largest_class = 0;
+
+ /* Setup chunks. */
+ for (size_t i = 0; i < TlsHeapStatic::NumClassInfo; i++) {
+ this->small_mem_lists[i] = nullptr;
+ this->cached_size[i] = 0;
+ this->chunk_count[i] = 1;
+ }
+
+ /* Set fixed chunk counts for particularly small chunks. */
+ this->chunk_count[1] = MaxChunkCount;
+ this->chunk_count[2] = MaxChunkCount;
+ this->chunk_count[3] = MaxChunkCount;
+ this->chunk_count[4] = MaxChunkCount / 2;
+ this->chunk_count[5] = MaxChunkCount / 2;
+ this->chunk_count[6] = MaxChunkCount / 2;
+ this->chunk_count[7] = MaxChunkCount / 4;
+ this->chunk_count[8] = MaxChunkCount / 4;
+ this->chunk_count[9] = MaxChunkCount / 4;
+ }
+
+ void TlsHeapCache::Finalize() {
+ /* Free all small mem lists. */
+ this->ReleaseAllCache();
+
+ /* Remove this cache from the owner central heap. */
+ this->central->RemoveThreadCache(this);
+ this->central->UncacheSmallMemory(this);
+ }
+
+ bool TlsHeapCache::CheckCache() const {
+ for (size_t i = 0; i < util::size(this->small_mem_lists); i++) {
+ void *ptr = this->small_mem_lists[i];
+ if (ptr) {
+ s64 depth = -static_cast(this->cached_size[i] / TlsHeapStatic::GetChunkSize(i));
+ while (ptr) {
+ ptr = *reinterpret_cast(this->ManglePointer(ptr));
+ if ((++depth) == 0) {
+ AMS_ASSERT(ptr == nullptr);
+ break;
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+
+ void TlsHeapCache::ReleaseAllCache() {
+ for (size_t i = 0; i < util::size(this->small_mem_lists); i++) {
+ if (this->small_mem_lists[i]) {
+ this->central->UncacheSmallMemoryList(this, this->small_mem_lists[i]);
+ this->small_mem_lists[i] = nullptr;
+ this->cached_size[i] = 0;
+ }
+ }
+
+ this->total_cached_size = 0;
+ this->largest_class = 0;
+ }
+
+ template<>
+ void *TlsHeapCache::AllocateImpl(TlsHeapCache *_this, size_t size) {
+ /* Validate allocation size. */
+ if (size == 0 || size > MaxSize) {
+ return nullptr;
+ }
+
+ if (const size_t cls = TlsHeapStatic::GetClassFromSize(size); cls != 0) {
+ AMS_ASSERT(cls < TlsHeapStatic::NumClassInfo);
+ return _this->central->CacheSmallMemory(cls);
+ } else {
+ /* If allocating a huge size, release our cache. */
+ if (size >= _this->total_heap_size / 4) {
+ _this->ReleaseAllCache();
+ }
+ return _this->central->CacheLargeMemory(size);
+ }
+ }
+
+ template<>
+ void *TlsHeapCache::AllocateImpl(TlsHeapCache *_this, size_t size) {
+ /* Validate allocation size. */
+ if (size == 0 || size > MaxSize) {
+ return nullptr;
+ }
+
+ if (size_t cls = TlsHeapStatic::GetClassFromSize(size); cls != 0) {
+ AMS_ASSERT(cls < TlsHeapStatic::NumClassInfo);
+ /* Allocate a chunk. */
+ void *ptr = _this->small_mem_lists[cls];
+ if (ptr == nullptr) {
+ const size_t prev_cls = cls;
+ size_t count = _this->chunk_count[cls];
+
+ size_t n = _this->central->CacheSmallMemoryList(_this, std::addressof(cls), count, std::addressof(ptr));
+ if (n == 0) {
+ return nullptr;
+ }
+
+ if (cls == prev_cls) {
+ if (count < MaxChunkCount) {
+ count++;
+ }
+ _this->chunk_count[cls] = std::max(count, n);
+ } else {
+ AMS_ASSERT(n == 1);
+ }
+
+ const size_t csize = TlsHeapStatic::GetChunkSize(cls) * (n - 1);
+ _this->cached_size[cls] += csize;
+ if (_this->cached_size[cls] > _this->cached_size[_this->largest_class]) {
+ _this->largest_class = cls;
+ }
+ _this->total_cached_size += csize;
+ }
+
+ /* Demangle our pointer, update free list. */
+ ptr = _this->ManglePointer(ptr);
+ _this->small_mem_lists[cls] = *reinterpret_cast(ptr);
+
+ return ptr;
+ } else {
+ /* If allocating a huge size, release our cache. */
+ if (size >= _this->total_heap_size / 4) {
+ _this->ReleaseAllCache();
+ }
+ return _this->central->CacheLargeMemory(size);
+ }
+ }
+
+ template<>
+ void *TlsHeapCache::AllocateAlignedImpl(TlsHeapCache *_this, size_t size, size_t align) {
+ /* Ensure valid alignment. */
+ if (!util::IsPowerOfTwo(align)) {
+ return nullptr;
+ }
+
+ /* NOTE: Nintendo does not check size == 0 here, despite doing so in Alloc */
+ if (size > MaxSize) {
+ return nullptr;
+ }
+
+ /* Handle big alignment. */
+ if (align > TlsHeapStatic::PageSize) {
+ return _this->central->CacheLargeMemoryWithBigAlign(util::AlignUp(size, TlsHeapStatic::PageSize), align);
+ }
+
+ const size_t real_size = TlsHeapStatic::GetRealSizeFromSizeAndAlignment(util::AlignUp(size, align), align);
+
+ if (const size_t cls = TlsHeapStatic::GetClassFromSize(real_size); cls != 0) {
+ if (real_size == 0) {
+ return nullptr;
+ }
+
+ AMS_ASSERT(cls < TlsHeapStatic::NumClassInfo);
+ return _this->central->CacheSmallMemory(cls, align);
+ } else {
+ /* If allocating a huge size, release our cache. */
+ if (real_size >= _this->total_heap_size / 4) {
+ _this->ReleaseAllCache();
+ }
+ return _this->central->CacheLargeMemory(real_size);
+ }
+ }
+
+ template<>
+ void *TlsHeapCache::AllocateAlignedImpl(TlsHeapCache *_this, size_t size, size_t align) {
+ /* Ensure valid alignment. */
+ if (!util::IsPowerOfTwo(align)) {
+ return nullptr;
+ }
+
+ /* NOTE: Nintendo does not check size == 0 here, despite doing so in Alloc */
+ if (size > MaxSize) {
+ return nullptr;
+ }
+
+ /* Handle big alignment. */
+ if (align > TlsHeapStatic::PageSize) {
+ return _this->central->CacheLargeMemoryWithBigAlign(util::AlignUp(size, TlsHeapStatic::PageSize), align);
+ }
+
+ const size_t real_size = TlsHeapStatic::GetRealSizeFromSizeAndAlignment(util::AlignUp(size, align), align);
+
+ if (size_t cls = TlsHeapStatic::GetClassFromSize(real_size); cls != 0) {
+ if (real_size == 0) {
+ return nullptr;
+ }
+
+ AMS_ASSERT(cls < TlsHeapStatic::NumClassInfo);
+
+ /* Allocate a chunk. */
+ void *ptr = _this->small_mem_lists[cls];
+ if (ptr == nullptr) {
+ const size_t prev_cls = cls;
+ size_t count = _this->chunk_count[cls];
+
+ size_t n = _this->central->CacheSmallMemoryList(_this, std::addressof(cls), count, std::addressof(ptr), align);
+ if (n == 0) {
+ return nullptr;
+ }
+
+ if (cls == prev_cls) {
+ if (count < MaxChunkCount) {
+ count++;
+ }
+ _this->chunk_count[cls] = std::max(count, n);
+ } else {
+ AMS_ASSERT(n == 1);
+ }
+
+ const s32 csize = TlsHeapStatic::GetChunkSize(cls) * (n - 1);
+ _this->total_cached_size += csize;
+ _this->cached_size[cls] += csize;
+ if (_this->cached_size[cls] > _this->cached_size[_this->largest_class]) {
+ _this->largest_class = cls;
+ }
+ }
+
+ /* Demangle our pointer, update free list. */
+ ptr = _this->ManglePointer(ptr);
+ _this->small_mem_lists[cls] = *reinterpret_cast(ptr);
+
+ return ptr;
+ } else {
+ /* If allocating a huge size, release our cache. */
+ if (size >= _this->total_heap_size / 4) {
+ _this->ReleaseAllCache();
+ }
+ return _this->central->CacheLargeMemory(size);
+ }
+ }
+
+ template<>
+ errno_t TlsHeapCache::FreeImpl(TlsHeapCache *_this, void *ptr) {
+ const size_t cls = _this->central->GetClassFromPointer(ptr);
+ if (cls == 0) {
+ return _this->central->UncacheLargeMemory(ptr);
+ }
+
+ AMS_ASSERT(cls < TlsHeapStatic::NumClassInfo);
+
+ if (static_cast(cls) >= 0) {
+ return _this->central->UncacheSmallMemory(ptr);
+ } else if (ptr == nullptr) {
+ return 0;
+ } else {
+ return EFAULT;
+ }
+ }
+
+ template<>
+ errno_t TlsHeapCache::FreeImpl(TlsHeapCache *_this, void *ptr) {
+ const size_t cls = _this->central->GetClassFromPointer(ptr);
+ if (cls == 0) {
+ return _this->central->UncacheLargeMemory(ptr);
+ }
+
+ AMS_ASSERT(cls < TlsHeapStatic::NumClassInfo);
+
+ if (static_cast(cls) >= 0) {
+ *reinterpret_cast(ptr) = _this->small_mem_lists[cls];
+ _this->small_mem_lists[cls] = _this->ManglePointer(ptr);
+
+ const s32 csize = TlsHeapStatic::GetChunkSize(cls);
+ _this->total_cached_size += csize;
+ _this->cached_size[cls] += csize;
+ if (_this->cached_size[cls] > _this->cached_size[_this->largest_class]) {
+ _this->largest_class = cls;
+ }
+
+ errno_t err = 0;
+ if (!_this->central->CheckCachedSize(_this->total_cached_size)) {
+ _this->central->UncacheSmallMemoryList(_this, _this->small_mem_lists[_this->largest_class]);
+ _this->small_mem_lists[_this->largest_class] = nullptr;
+ _this->total_cached_size -= _this->cached_size[_this->largest_class];
+ _this->cached_size[_this->largest_class] = 0;
+
+ s32 largest_class = 0;
+ s32 biggest_size = -1;
+ for (size_t i = 0; i < TlsHeapStatic::NumClassInfo; i++) {
+ if (biggest_size < _this->cached_size[i]) {
+ biggest_size = _this->cached_size[i];
+ largest_class = static_cast(i);
+ }
+ }
+ _this->largest_class = largest_class;
+ }
+ return err;
+ } else if (ptr == nullptr) {
+ return 0;
+ } else {
+ return EFAULT;
+ }
+ }
+
+ template<>
+ errno_t TlsHeapCache::FreeWithSizeImpl(TlsHeapCache *_this, void *ptr, size_t size) {
+ if (ptr == nullptr) {
+ return 0;
+ }
+
+ const size_t cls = TlsHeapStatic::GetClassFromSize(size);
+ if (cls == 0) {
+ return _this->central->UncacheLargeMemory(ptr);
+ } else {
+ return _this->central->UncacheSmallMemory(ptr);
+ }
+ }
+
+ template<>
+ errno_t TlsHeapCache::FreeWithSizeImpl(TlsHeapCache *_this, void *ptr, size_t size) {
+ if (ptr == nullptr) {
+ return 0;
+ }
+
+ const size_t cls = TlsHeapStatic::GetClassFromSize(size);
+ if (cls == 0) {
+ return _this->central->UncacheLargeMemory(ptr);
+ } else {
+ *reinterpret_cast(ptr) = _this->small_mem_lists[cls];
+ _this->small_mem_lists[cls] = _this->ManglePointer(ptr);
+
+ const s32 csize = TlsHeapStatic::GetChunkSize(cls);
+ _this->total_cached_size += csize;
+ _this->cached_size[cls] += csize;
+ if (_this->cached_size[cls] > _this->cached_size[_this->largest_class]) {
+ _this->largest_class = cls;
+ }
+
+ errno_t err = 0;
+ if (!_this->central->CheckCachedSize(_this->total_cached_size)) {
+ _this->central->UncacheSmallMemoryList(_this, _this->small_mem_lists[_this->largest_class]);
+ _this->small_mem_lists[_this->largest_class] = nullptr;
+ _this->total_cached_size -= _this->cached_size[_this->largest_class];
+ _this->cached_size[_this->largest_class] = 0;
+
+ s32 largest_class = 0;
+ s32 biggest_size = -1;
+ for (size_t i = 0; i < TlsHeapStatic::NumClassInfo; i++) {
+ if (biggest_size < _this->cached_size[i]) {
+ biggest_size = _this->cached_size[i];
+ largest_class = static_cast(i);
+ }
+ }
+ _this->largest_class = largest_class;
+ }
+ return err;
+ }
+ }
+
+ template<>
+ size_t TlsHeapCache::GetAllocationSizeImpl(TlsHeapCache *_this, const void *ptr) {
+ return _this->GetAllocationSizeCommonImpl(ptr);
+ }
+
+ template<>
+ size_t TlsHeapCache::GetAllocationSizeImpl(TlsHeapCache *_this, const void *ptr) {
+ return _this->GetAllocationSizeCommonImpl(ptr);
+ }
+
+ size_t TlsHeapCache::GetAllocationSizeCommonImpl(const void *ptr) const {
+ const s32 cls = this->central->GetClassFromPointer(ptr);
+ if (cls > 0) {
+ if (!util::IsAligned(ptr, alignof(u64))) {
+ /* All pointers we allocate have alignment at least 8. */
+ return 0;
+ }
+
+ /* Validate class. */
+ AMS_ASSERT(cls < static_cast(TlsHeapStatic::NumClassInfo));
+ if (cls < 0) {
+ return 0;
+ }
+
+ return TlsHeapStatic::GetChunkSize(cls);
+ } else if (ptr != nullptr) {
+ return this->central->GetAllocationSize(ptr);
+ } else {
+ return 0;
+ }
+ }
+
+ template<>
+ errno_t TlsHeapCache::ReallocateImpl(TlsHeapCache *_this, void *ptr, size_t size, void **p) {
+ AMS_ASSERT(ptr != nullptr && size != 0);
+ if (size > MaxSize) {
+ return ENOMEM;
+ }
+
+ size_t alloc_size, copy_size;
+
+ const s32 cls_from_size = TlsHeapStatic::GetClassFromSize(size);
+ const s32 cls_from_ptr = _this->central->GetClassFromPointer(ptr);
+ if (cls_from_ptr < 0) {
+ /* error case. */
+ return EFAULT;
+ } else if (cls_from_size) {
+ if (cls_from_ptr > 0) {
+ if (cls_from_size <= cls_from_ptr) {
+ *p = ptr;
+ return 0;
+ } else {
+ alloc_size = size;
+ copy_size = TlsHeapStatic::GetChunkSize(cls_from_ptr);
+ }
+ } else /* if (cls_from_ptr == 0) */ {
+ alloc_size = size;
+ copy_size = size;
+ }
+ } else if (cls_from_ptr == 0) {
+ return _this->central->ReallocateLargeMemory(ptr, size, p);
+ } else /* if (cls_from_ptr > 0) */ {
+ alloc_size = size;
+ copy_size = TlsHeapStatic::GetChunkSize(cls_from_ptr);
+ }
+
+ *p = AllocateImpl(_this, alloc_size);
+ if (*p == nullptr) {
+ return ENOMEM;
+ }
+ std::memcpy(*p, ptr, copy_size);
+ return FreeImpl(_this, ptr);
+ }
+
+ template<>
+ errno_t TlsHeapCache::ReallocateImpl(TlsHeapCache *_this, void *ptr, size_t size, void **p) {
+ AMS_ASSERT(ptr != nullptr && size != 0);
+ if (size > MaxSize) {
+ return ENOMEM;
+ }
+
+ size_t alloc_size, copy_size;
+
+ const s32 cls_from_size = TlsHeapStatic::GetClassFromSize(size);
+ const s32 cls_from_ptr = _this->central->GetClassFromPointer(ptr);
+ if (cls_from_ptr < 0) {
+ /* error case. */
+ return EFAULT;
+ } else if (cls_from_size) {
+ if (cls_from_ptr > 0) {
+ if (cls_from_size <= cls_from_ptr) {
+ *p = ptr;
+ return 0;
+ } else {
+ alloc_size = size;
+ copy_size = TlsHeapStatic::GetChunkSize(cls_from_ptr);
+ }
+ } else /* if (cls_from_ptr == 0) */ {
+ alloc_size = size;
+ copy_size = size;
+ }
+ } else if (cls_from_ptr == 0) {
+ return _this->central->ReallocateLargeMemory(ptr, size, p);
+ } else /* if (cls_from_ptr > 0) */ {
+ alloc_size = size;
+ copy_size = TlsHeapStatic::GetChunkSize(cls_from_ptr);
+ }
+
+ *p = AllocateImpl(_this, alloc_size);
+ if (*p == nullptr) {
+ return ENOMEM;
+ }
+ std::memcpy(*p, ptr, copy_size);
+ return FreeImpl(_this, ptr);
+ }
+
+ template<>
+ errno_t TlsHeapCache::ShrinkImpl(TlsHeapCache *_this, void *ptr, size_t size) {
+ return _this->ShrinkCommonImpl(ptr, size);
+ }
+
+ template<>
+ errno_t TlsHeapCache::ShrinkImpl(TlsHeapCache *_this, void *ptr, size_t size) {
+ return _this->ShrinkCommonImpl(ptr, size);
+ }
+
+ errno_t TlsHeapCache::ShrinkCommonImpl(void *ptr, size_t size) const {
+ AMS_ASSERT(ptr != nullptr && size != 0);
+ if (size > MaxSize) {
+ return ENOMEM;
+ }
+
+ const s32 cls_from_size = TlsHeapStatic::GetClassFromSize(size);
+ const s32 cls_from_ptr = this->central->GetClassFromPointer(ptr);
+ if (cls_from_ptr) {
+ if (cls_from_ptr <= 0) {
+ return EFAULT;
+ } else if (cls_from_size && cls_from_size <= cls_from_ptr) {
+ return 0;
+ } else {
+ return EINVAL;
+ }
+ } else if (cls_from_size) {
+ return this->central->ShrinkLargeMemory(ptr, TlsHeapStatic::PageSize);
+ } else {
+ return this->central->ShrinkLargeMemory(ptr, size);
+ }
+ }
+
+}
diff --git a/libraries/libstratosphere/source/mem/impl/heap/mem_impl_heap_tls_heap_cache.hpp b/libraries/libstratosphere/source/mem/impl/heap/mem_impl_heap_tls_heap_cache.hpp
new file mode 100644
index 000000000..694d5902a
--- /dev/null
+++ b/libraries/libstratosphere/source/mem/impl/heap/mem_impl_heap_tls_heap_cache.hpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2018-2020 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 "mem_impl_heap_platform.hpp"
+#include "mem_impl_heap_tls_heap_static.hpp"
+
+namespace ams::mem::impl::heap {
+
+ class TlsHeapCentral;
+
+ #define FOREACH_TLS_HEAP_CACHE_FUNC(HANDLER) \
+ HANDLER(void *, Allocate, allocate, size_t size); \
+ HANDLER(void *, AllocateAligned, allocate_aligned, size_t size, size_t align); \
+ HANDLER(errno_t, Free, free, void *ptr); \
+ HANDLER(errno_t, FreeWithSize, free_with_size, void *ptr, size_t size); \
+ HANDLER(size_t, GetAllocationSize, get_allocation_size, const void *ptr); \
+ HANDLER(errno_t, Reallocate, reallocate, void *ptr, size_t size, void **p); \
+ HANDLER(errno_t, Shrink, shrink, void *ptr, size_t size);
+
+ class TlsHeapCache {
+ public:
+ static constexpr size_t MaxChunkCount = BITSIZEOF(u64);
+ public:
+ #define TLS_HEAP_CACHE_DECLARE_TYPEDEF(RETURN, NAME, MEMBER_NAME, ...) \
+ using NAME##Func = RETURN (*)(TlsHeapCache *, ## __VA_ARGS__)
+
+ FOREACH_TLS_HEAP_CACHE_FUNC(TLS_HEAP_CACHE_DECLARE_TYPEDEF)
+
+ #undef TLS_HEAP_CACHE_DECLARE_TYPEDEF
+ private:
+ #define TLS_HEAP_CACHE_DECLARE_MEMBER(RETURN, NAME, MEMBER_NAME, ...) \
+ NAME##Func MEMBER_NAME;
+
+ FOREACH_TLS_HEAP_CACHE_FUNC(TLS_HEAP_CACHE_DECLARE_MEMBER)
+
+ #undef TLS_HEAP_CACHE_DECLARE_MEMBER
+
+ uintptr_t mangle_val;
+ TlsHeapCentral *central;
+ size_t total_heap_size;
+ u32 heap_option;
+ s32 total_cached_size;
+ s32 largest_class;
+ void *small_mem_lists[TlsHeapStatic::NumClassInfo];
+ s32 cached_size[TlsHeapStatic::NumClassInfo];
+ u8 chunk_count[TlsHeapStatic::NumClassInfo];
+ public:
+ TlsHeapCache(TlsHeapCentral *central, u32 option);
+ void Finalize();
+
+ void *ManglePointer(void *ptr) const {
+ return reinterpret_cast(reinterpret_cast(ptr) ^ this->mangle_val);
+ }
+
+ bool CheckCache() const;
+ void ReleaseAllCache();
+
+ public:
+ /* TODO: Better handler with type info to macro this? */
+ ALWAYS_INLINE void *Allocate(size_t size) { return this->allocate(this, size); }
+ ALWAYS_INLINE void *Allocate(size_t size, size_t align) { return this->allocate_aligned(this, size, align); }
+ ALWAYS_INLINE errno_t Free(void *ptr) { return this->free(this, ptr); }
+ ALWAYS_INLINE errno_t FreeWithSize(void *ptr, size_t size) { return this->free_with_size(this, ptr, size); }
+ ALWAYS_INLINE size_t GetAllocationSize(const void *ptr) { return this->get_allocation_size(this, ptr); }
+ ALWAYS_INLINE errno_t Reallocate(void *ptr, size_t size, void **p) { return this->reallocate(this, ptr, size, p); }
+ ALWAYS_INLINE errno_t Shrink(void *ptr, size_t size) { return this->shrink(this, ptr, size); }
+ private:
+ #define TLS_HEAP_CACHE_DECLARE_TEMPLATE(RETURN, NAME, MEMBER_NAME, ...) \
+ template static RETURN NAME##Impl(TlsHeapCache *_this, ## __VA_ARGS__ )
+
+ FOREACH_TLS_HEAP_CACHE_FUNC(TLS_HEAP_CACHE_DECLARE_TEMPLATE)
+
+ #undef TLS_HEAP_CACHE_DECLARE_TEMPLATE
+
+ size_t GetAllocationSizeCommonImpl(const void *ptr) const;
+ errno_t ShrinkCommonImpl(void *ptr, size_t size) const;
+ };
+
+ #define TLS_HEAP_CACHE_DECLARE_INSTANTIATION(RETURN, NAME, MEMBER_NAME, ...) \
+ template<> RETURN TlsHeapCache::NAME##Impl(TlsHeapCache *_this, ##__VA_ARGS__); \
+ template<> RETURN TlsHeapCache::NAME##Impl(TlsHeapCache *_this, ##__VA_ARGS__)
+
+ FOREACH_TLS_HEAP_CACHE_FUNC(TLS_HEAP_CACHE_DECLARE_INSTANTIATION)
+
+ #undef FOREACH_TLS_HEAP_CACHE_FUNC
+
+
+}
\ No newline at end of file
diff --git a/libraries/libstratosphere/source/mem/impl/heap/mem_impl_heap_tls_heap_central.cpp b/libraries/libstratosphere/source/mem/impl/heap/mem_impl_heap_tls_heap_central.cpp
new file mode 100644
index 000000000..fd2554985
--- /dev/null
+++ b/libraries/libstratosphere/source/mem/impl/heap/mem_impl_heap_tls_heap_central.cpp
@@ -0,0 +1,1492 @@
+/*
+ * Copyright (c) 2018-2020 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 "mem_impl_heap_platform.hpp"
+#include "mem_impl_heap_tls_heap_static.hpp"
+#include "mem_impl_heap_tls_heap_central.hpp"
+
+namespace ams::mem::impl::heap {
+
+ namespace {
+
+ char g_log_buf[4_KB];
+
+ void InitializeSpanPage(SpanPage *sp) {
+ static_assert(SpanPage::MaxSpanCount <= BITSIZEOF(u64));
+ constexpr size_t NumUnusedBits = BITSIZEOF(u64) - SpanPage::MaxSpanCount;
+
+ sp->info.free_count = SpanPage::MaxSpanCount;
+ sp->info.is_sticky = 0;
+ sp->info.alloc_bitmap = (static_cast(1) << NumUnusedBits) - 1;
+
+ ListClearLink(sp);
+
+ sp->info.span_of_spanpage.start.u = 0;
+ sp->info.span_of_spanpage.num_pages = 0;
+ sp->info.span_of_spanpage.aux.small.objects = nullptr;
+ sp->info.span_of_spanpage.object_count = 0;
+ sp->info.span_of_spanpage.page_class = 0;
+ sp->info.span_of_spanpage.status = Span::Status_NotUsed;
+ sp->info.span_of_spanpage.id = 0;
+
+ ListClearLink(std::addressof(sp->info.span_of_spanpage));
+ }
+
+ void RegisterSpan(SpanTable *span_table, Span *span) {
+ const size_t idx = TlsHeapStatic::GetPageIndex(span->start.u - reinterpret_cast(span_table));
+ span->page_class = 0;
+ span_table->page_to_span[idx] = span;
+ span_table->page_to_span[idx + span->num_pages - 1] = span;
+ }
+
+ void UnregisterSpan(SpanTable *span_table, Span *span) {
+ AMS_ASSERT(span->page_class == 0);
+ const size_t idx = TlsHeapStatic::GetPageIndex(span->start.u - reinterpret_cast(span_table));
+ span_table->page_to_span[idx] = nullptr;
+ span_table->page_to_span[idx + span->num_pages - 1] = nullptr;
+ }
+
+ void ChangeRangeOfSpan(SpanTable *span_table, Span *span, uintptr_t start, size_t new_pages) {
+ const size_t idx = TlsHeapStatic::GetPageIndex(span->start.u - reinterpret_cast(span_table));
+ if (span->start.u == start) {
+ if (span->num_pages != 1) {
+ span_table->page_to_span[idx + span->num_pages - 1] = nullptr;
+ }
+ span_table->page_to_span[idx + new_pages - 1] = span;
+ span->num_pages = new_pages;
+ } else {
+ span_table->page_to_span[idx] = nullptr;
+ span_table->page_to_span[idx + span->num_pages - 1] = nullptr;
+
+ const size_t new_idx = TlsHeapStatic::GetPageIndex(start - reinterpret_cast(span_table));
+ span_table->page_to_span[new_idx] = span;
+ span_table->page_to_span[new_idx + new_pages - 1] = span;
+
+ span->start.u = start;
+ span->num_pages = new_pages;
+ }
+ }
+
+ void MigrateSpan(SpanTable *span_table, Span *from, Span *to) {
+ AMS_ASSERT(from != to);
+
+ std::memcpy(to, from, sizeof(*from));
+
+ from->status = Span::Status_NotUsed;
+
+ if (from->list_next) {
+ to->list_next = from->list_next;
+ from->list_next->list_prev = to;
+ from->list_next = nullptr;
+ } else {
+ to->list_next = nullptr;
+ }
+
+ if (from->list_prev) {
+ to->list_prev = from->list_prev;
+ from->list_prev->list_next = to;
+ from->list_prev = nullptr;
+ } else {
+ to->list_prev = nullptr;
+ }
+
+ const size_t idx = TlsHeapStatic::GetPageIndex(to->start.u - reinterpret_cast(span_table));
+ if (from->page_class) {
+ for (size_t i = 0; i < to->num_pages; i++) {
+ span_table->page_to_span[idx + i] = to;
+ }
+ } else {
+ span_table->page_to_span[idx] = to;
+ span_table->page_to_span[idx + to->num_pages - 1] = to;
+ }
+ }
+
+ bool IsNthSmallMemoryMarked(const Span *span, size_t n) {
+ return (span->aux.small.is_allocated[n / BITSIZEOF(u64)] & (1ull << (n % BITSIZEOF(u64)))) != 0;
+ }
+
+ void MarkNthSmallMemory(Span *span, size_t n) {
+ span->aux.small.is_allocated[n / BITSIZEOF(u64)] |= (1ull << (n % BITSIZEOF(u64)));
+ }
+
+ void UnmarkNthSmallMemory(Span *span, size_t n) {
+ span->aux.small.is_allocated[n / BITSIZEOF(u64)] &= ~(1ull << (n % BITSIZEOF(u64)));
+ }
+
+ void *AllocateSmallMemory(Span *span) {
+ Span::SmallMemory *sm = span->aux.small.objects;
+ const size_t chunk_size = TlsHeapStatic::GetChunkSize(span->page_class);
+ const uintptr_t span_end = span->start.u + (span->num_pages * TlsHeapStatic::PageSize);
+ if (span->start.u <= reinterpret_cast(sm) && reinterpret_cast(sm) < span_end) {
+ const size_t idx = (reinterpret_cast(sm) - span->start.u) / chunk_size;
+ if (reinterpret_cast(sm) == (span->start.u + idx * chunk_size) && !IsNthSmallMemoryMarked(span, idx)) {
+ MarkNthSmallMemory(span, idx);
+ span->aux.small.objects = sm->next;
+ sm->next = nullptr;
+ span->object_count++;
+ return sm;
+ }
+ } else if (sm == nullptr) {
+ return nullptr;
+ }
+
+ /* Data corruption error. */
+ AMS_ASSERT(false);
+ span->aux.small.objects = nullptr;
+ return nullptr;
+ }
+
+ struct MangledSmallMemory {
+ Span::SmallMemory *from;
+ Span::SmallMemory *to;
+ };
+
+ size_t AllocateSmallMemory(Span *span, TlsHeapCache *cache, size_t n, MangledSmallMemory *memlist) {
+ auto ManglePointer = [cache](void *ptr) ALWAYS_INLINE_LAMBDA {
+ return static_cast(cache->ManglePointer(ptr));
+ };
+
+ Span::SmallMemory *sm = span->aux.small.objects;
+ if (sm) {
+ size_t count = 0;
+ memlist->from = ManglePointer(sm);
+
+ const size_t chunk_size = TlsHeapStatic::GetChunkSize(span->page_class);
+ const uintptr_t span_end = span->start.u + (span->num_pages * TlsHeapStatic::PageSize);
+ while (span->start.u <= reinterpret_cast(sm) && reinterpret_cast(sm) < span_end) {
+ const size_t idx = (reinterpret_cast(sm) - span->start.u) / chunk_size;
+ if (span->start.u + idx * chunk_size != reinterpret_cast(sm)) {
+ break;
+ }
+ if (IsNthSmallMemoryMarked(span, idx)) {
+ break;
+ }
+ MarkNthSmallMemory(span, idx);
+ count++;
+
+ Span::SmallMemory *next = sm->next;
+ sm->next = ManglePointer(next);
+ if (count >= n || next == nullptr) {
+ memlist->to = sm;
+ memlist->to->next = nullptr;
+ span->aux.small.objects = next;
+ span->object_count += count;
+ return count;
+ }
+
+ sm = next;
+ }
+
+ /* Data corruption error. */
+ Span::SmallMemory *prev = span->aux.small.objects;
+ Span::SmallMemory *cur = span->aux.small.objects;
+ while (cur != sm) {
+ prev = cur;
+ cur = ManglePointer(cur->next);
+ }
+
+ memlist->to = ManglePointer(prev);
+ memlist->to->next = nullptr;
+ span->aux.small.objects = nullptr;
+ span->object_count += count;
+ return count;
+ } else {
+ memlist->from = nullptr;
+ memlist->to = nullptr;
+ return 0;
+ }
+ }
+
+ void ReleaseSmallMemory(Span *span, void *ptr) {
+ AMS_ASSERT(span->object_count > 0);
+
+ const size_t span_ofs = reinterpret_cast(ptr) - span->start.u;
+ const size_t chunk_size = TlsHeapStatic::GetChunkSize(span->page_class);
+ const size_t span_idx = span_ofs / chunk_size;
+ if (span_ofs != (span_idx * chunk_size)) {
+ /* Invalid pointer. Do the best we can. */
+ ptr = reinterpret_cast(span->start.u + span_idx * chunk_size);
+ }
+ if (IsNthSmallMemoryMarked(span, span_idx)) {
+ UnmarkNthSmallMemory(span, span_idx);
+
+ Span::SmallMemory *sm = reinterpret_cast(ptr);
+ sm->next = span->aux.small.objects;
+ span->aux.small.objects = sm;
+ span->object_count--;
+ } else {
+ /* Double free error. */
+ /* TODO: Anything? */
+ }
+ }
+
+ void SpanToSmallMemorySpan(SpanTable *span_table, Span *span, size_t cls) {
+ AMS_ASSERT(cls != 0 && span->page_class == 0);
+
+ const size_t span_idx = TlsHeapStatic::GetPageIndex(span->start.u - reinterpret_cast