diff --git a/libraries/libstratosphere/include/stratosphere.hpp b/libraries/libstratosphere/include/stratosphere.hpp
index 39a95e063..398bf579e 100644
--- a/libraries/libstratosphere/include/stratosphere.hpp
+++ b/libraries/libstratosphere/include/stratosphere.hpp
@@ -26,6 +26,7 @@
#include "stratosphere/ams.hpp"
#include "stratosphere/os.hpp"
#include "stratosphere/dd.hpp"
+#include "stratosphere/lmem.hpp"
/* Lots of things depend on NCM, for Program IDs. */
#include "stratosphere/ncm.hpp"
diff --git a/libraries/libstratosphere/include/stratosphere/lmem.hpp b/libraries/libstratosphere/include/stratosphere/lmem.hpp
new file mode 100644
index 000000000..fa85facb6
--- /dev/null
+++ b/libraries/libstratosphere/include/stratosphere/lmem.hpp
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+
+#include "lmem/lmem_common.hpp"
+#include "lmem/lmem_exp_heap.hpp"
+#include "lmem/lmem_unit_heap.hpp"
diff --git a/libraries/libstratosphere/include/stratosphere/lmem/impl/lmem_impl_common.hpp b/libraries/libstratosphere/include/stratosphere/lmem/impl/lmem_impl_common.hpp
new file mode 100644
index 000000000..10eb3a69a
--- /dev/null
+++ b/libraries/libstratosphere/include/stratosphere/lmem/impl/lmem_impl_common.hpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+#include
+#include "../../os.hpp"
+
+namespace ams::lmem::impl {
+
+ /* NOTE: Nintendo does not use util::IntrusiveListNode. */
+ /* They seem to manually manage linked list pointers. */
+ /* This is pretty gross, so we're going to use util::IntrusiveListNode. */
+
+ struct ExpHeapMemoryBlockHead {
+ u16 magic;
+ u32 attributes;
+ size_t block_size;
+ util::IntrusiveListNode list_node;
+ };
+ static_assert(std::is_trivially_destructible::value);
+
+ using ExpHeapMemoryBlockList = typename util::IntrusiveListMemberTraits<&ExpHeapMemoryBlockHead::list_node>::ListType;
+
+ struct ExpHeapHead {
+ ExpHeapMemoryBlockList free_list;
+ ExpHeapMemoryBlockList used_list;
+ u16 group_id;
+ u16 mode;
+ bool use_alignment_margins;
+ char pad[3];
+ };
+ static_assert(sizeof(ExpHeapHead) == 0x28);
+ static_assert(std::is_trivially_destructible::value);
+
+ struct FrameHeapHead {
+ void *next_block_head;
+ void *next_block_tail;
+ };
+ static_assert(sizeof(FrameHeapHead) == 0x10);
+ static_assert(std::is_trivially_destructible::value);
+
+ struct UnitHead {
+ UnitHead *next;
+ };
+
+ struct UnitHeapList {
+ UnitHead *head;
+ };
+
+ struct UnitHeapHead {
+ UnitHeapList free_list;
+ size_t unit_size;
+ s32 alignment;
+ s32 num_units;
+ };
+ static_assert(sizeof(UnitHeapHead) == 0x18);
+ static_assert(std::is_trivially_destructible::value);
+
+ union ImplementationHeapHead {
+ ExpHeapHead exp_heap_head;
+ FrameHeapHead frame_heap_head;
+ UnitHeapHead unit_heap_head;
+ };
+
+ struct HeapHead {
+ u32 magic;
+ util::IntrusiveListNode list_node;
+ typename util::IntrusiveListMemberTraits<&HeapHead::list_node>::ListType child_list;
+ void *heap_start;
+ void *heap_end;
+ os::Mutex mutex;
+ u8 option;
+ ImplementationHeapHead impl_head;
+ };
+ static_assert(std::is_trivially_destructible::value);
+
+}
diff --git a/libraries/libstratosphere/include/stratosphere/lmem/lmem_common.hpp b/libraries/libstratosphere/include/stratosphere/lmem/lmem_common.hpp
new file mode 100644
index 000000000..090fa9fa4
--- /dev/null
+++ b/libraries/libstratosphere/include/stratosphere/lmem/lmem_common.hpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+#include
+#include "impl/lmem_impl_common.hpp"
+
+namespace ams::lmem {
+
+ enum CreateOption {
+ CreateOption_None = (0),
+ CreateOption_ZeroClear = (1 << 0),
+ CreateOption_DebugFill = (1 << 1),
+ CreateOption_ThreadSafe = (1 << 2),
+ };
+
+ enum FillType {
+ FillType_Unallocated,
+ FillType_Allocated,
+ FillType_Freed,
+ FillType_Count,
+ };
+
+ namespace impl {
+
+ struct HeapHead;
+
+ }
+
+ using HeapHandle = impl::HeapHead *;
+
+ using HeapCommonHead = impl::HeapHead;
+
+ struct MemoryRange {
+ uintptr_t address;
+ size_t size;
+ };
+
+ constexpr inline s32 DefaultAlignment = 0x8;
+
+ /* Common API. */
+ u32 GetDebugFillValue(FillType fill_type);
+ void SetDebugFillValue(FillType fill_type, u32 value);
+
+ size_t GetTotalSize(HeapHandle handle);
+ void *GetStartAddress(HeapHandle handle);
+ bool ContainsAddress(HeapHandle handle, const void *address);
+
+}
diff --git a/libraries/libstratosphere/include/stratosphere/lmem/lmem_exp_heap.hpp b/libraries/libstratosphere/include/stratosphere/lmem/lmem_exp_heap.hpp
new file mode 100644
index 000000000..9660ca976
--- /dev/null
+++ b/libraries/libstratosphere/include/stratosphere/lmem/lmem_exp_heap.hpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+#include
+#include "lmem_common.hpp"
+
+namespace ams::lmem {
+
+ enum AllocationMode {
+ AllocationMode_FirstFit,
+ AllocationMode_BestFit,
+ };
+
+ enum AllocationDirection {
+ AllocationDirection_Front,
+ AllocationDirection_Back,
+ };
+
+ using HeapVisitor = void (*)(void *block, HeapHandle handle, uintptr_t user_data);
+
+ HeapHandle CreateExpHeap(void *address, size_t size, u32 option);
+ void DestroyExpHeap(HeapHandle handle);
+ MemoryRange AdjustExpHeap(HeapHandle handle);
+
+ void *AllocateFromExpHeap(HeapHandle handle, size_t size);
+ void *AllocateFromExpHeap(HeapHandle handle, size_t size, s32 alignment);
+ void FreeToExpHeap(HeapHandle handle, void *block);
+
+ size_t ResizeExpHeapMemoryBlock(HeapHandle handle, void *block, size_t size);
+
+ size_t GetExpHeapTotalFreeSize(HeapHandle handle);
+ size_t GetExpHeapAllocatableSize(HeapHandle handle, s32 alignment);
+
+ AllocationMode GetExpHeapAllocationMode(HeapHandle handle);
+ AllocationMode SetExpHeapAllocationMode(HeapHandle handle, AllocationMode new_mode);
+
+ bool GetExpHeapUseMarginsOfAlignment(HeapHandle handle);
+ bool SetExpHeapUseMarginsOfAlignment(HeapHandle handle, bool use_margins);
+
+ u16 GetExpHeapGroupId(HeapHandle handle);
+ u16 SetExpHeapGroupId(HeapHandle handle, u16 group_id);
+
+ size_t GetExpHeapMemoryBlockSize(const void *memory_block);
+ u16 GetExpHeapMemoryBlockGroupId(const void *memory_block);
+ AllocationDirection GetExpHeapMemoryBlockAllocationDirection(const void *memory_block);
+
+ void VisitExpHeapAllocatedBlocks(HeapHandle handle, HeapVisitor visitor, uintptr_t user_data);
+
+}
diff --git a/libraries/libstratosphere/include/stratosphere/lmem/lmem_unit_heap.hpp b/libraries/libstratosphere/include/stratosphere/lmem/lmem_unit_heap.hpp
new file mode 100644
index 000000000..565ad825b
--- /dev/null
+++ b/libraries/libstratosphere/include/stratosphere/lmem/lmem_unit_heap.hpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+#include
+#include "lmem_common.hpp"
+
+namespace ams::lmem {
+
+ enum InfoPlacement {
+ InfoPlacement_Head,
+ InfoPlacement_Tail,
+ };
+
+ HeapHandle CreateUnitHeap(void *address, size_t size, size_t unit_size, u32 option);
+ HeapHandle CreateUnitHeap(void *address, size_t size, size_t unit_size, u32 option, s32 alignment, InfoPlacement info_placement);
+ HeapHandle CreateUnitHeap(void *address, size_t size, size_t unit_size, u32 option, s32 alignment, HeapCommonHead *heap_head);
+ void DestroyUnitHeap(HeapHandle handle);
+
+ void InvalidateUnitHeap(HeapHandle handle);
+ void ExtendUnitHeap(HeapHandle handle, size_t size);
+
+ void *AllocateFromUnitHeap(HeapHandle handle);
+ void FreeToUnitHeap(HeapHandle handle, void *block);
+
+ size_t GetUnitHeapUnitSize(HeapHandle handle);
+ s32 GetUnitHeapAlignment(HeapHandle handle);
+ size_t GetUnitHeapFreeCount(HeapHandle handle);
+ size_t GetUnitHeapUsedCount(HeapHandle handle);
+
+ size_t GetUnitHeapRequiredSize(size_t unit_size, size_t unit_count, s32 alignment, bool internal_metadata);
+
+}
diff --git a/libraries/libstratosphere/include/stratosphere/os/os_condvar.hpp b/libraries/libstratosphere/include/stratosphere/os/os_condvar.hpp
index 9e31022b3..bf1d53a7e 100644
--- a/libraries/libstratosphere/include/stratosphere/os/os_condvar.hpp
+++ b/libraries/libstratosphere/include/stratosphere/os/os_condvar.hpp
@@ -39,7 +39,7 @@ namespace ams::os {
/* Abort on any error other than timed out/success. */
R_TRY_CATCH(condvarWaitTimeout(&this->cv, m, timeout)) {
R_CATCH(svc::ResultTimedOut) { return ConditionVariableStatus::TimedOut; }
- } R_END_TRY_CATCH_WITH_ASSERT;
+ } R_END_TRY_CATCH_WITH_ABORT_UNLESS;
return ConditionVariableStatus::Success;
}
diff --git a/libraries/libstratosphere/include/stratosphere/ro/ro_types.hpp b/libraries/libstratosphere/include/stratosphere/ro/ro_types.hpp
index db9134963..44856cc34 100644
--- a/libraries/libstratosphere/include/stratosphere/ro/ro_types.hpp
+++ b/libraries/libstratosphere/include/stratosphere/ro/ro_types.hpp
@@ -86,8 +86,12 @@ namespace ams::ro {
return this->num_hashes;
}
+ size_t GetHashesOffset() const {
+ return this->hashes_offset;
+ }
+
uintptr_t GetHashes() const {
- return reinterpret_cast(this) + this->hashes_offset;
+ return reinterpret_cast(this) + this->GetHashesOffset();
}
u32 GetKeyGeneration() const {
@@ -114,12 +118,16 @@ namespace ams::ro {
return reinterpret_cast(std::addressof(this->program_id));
}
- size_t GetSignedAreaSize() const;
+ size_t GetSignedAreaSize() const {
+ return this->size - GetSignedAreaOffset();
+ }
+
+ static constexpr size_t GetSignedAreaOffset();
};
static_assert(sizeof(NrrHeader) == 0x350, "NrrHeader definition!");
- inline size_t NrrHeader::GetSignedAreaSize() const {
- return this->size - OFFSETOF(NrrHeader, program_id);
+ constexpr size_t NrrHeader::GetSignedAreaOffset() {
+ return OFFSETOF(NrrHeader, program_id);
}
class NroHeader {
diff --git a/libraries/libstratosphere/include/stratosphere/spl/spl_types.hpp b/libraries/libstratosphere/include/stratosphere/spl/spl_types.hpp
index 50b6f33bc..7b1ca0bc3 100644
--- a/libraries/libstratosphere/include/stratosphere/spl/spl_types.hpp
+++ b/libraries/libstratosphere/include/stratosphere/spl/spl_types.hpp
@@ -107,10 +107,11 @@ namespace ams::spl {
}
enum class HardwareType {
- Icosa = 0,
- Copper = 1,
- Hoag = 2,
- Iowa = 3,
+ Icosa = 0,
+ Copper = 1,
+ Hoag = 2,
+ Iowa = 3,
+ Calcio = 4,
};
enum MemoryArrangement {
diff --git a/libraries/libstratosphere/include/stratosphere/util.hpp b/libraries/libstratosphere/include/stratosphere/util.hpp
index 2c4f3bd3b..db47d867f 100644
--- a/libraries/libstratosphere/include/stratosphere/util.hpp
+++ b/libraries/libstratosphere/include/stratosphere/util.hpp
@@ -16,5 +16,6 @@
#pragma once
+#include "util/util_uuid_api.hpp"
#include "util/util_compression.hpp"
#include "util/util_ini.hpp"
diff --git a/libraries/libstratosphere/include/stratosphere/util/util_uuid_api.hpp b/libraries/libstratosphere/include/stratosphere/util/util_uuid_api.hpp
new file mode 100644
index 000000000..4782a558d
--- /dev/null
+++ b/libraries/libstratosphere/include/stratosphere/util/util_uuid_api.hpp
@@ -0,0 +1,28 @@
+/*
+ * 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
+
+namespace ams::util {
+
+ /* Nintendo provides UUID generation following RFC 4122. */
+ /* By default, UUIDs are generated as version 4 (random). */
+
+ Uuid GenerateUuid();
+ Uuid GenerateUuidVersion5(const void *sha1_hash);
+
+}
\ No newline at end of file
diff --git a/libraries/libstratosphere/source/dd/dd_io_mappings.cpp b/libraries/libstratosphere/source/dd/dd_io_mappings.cpp
index 2095b2101..e71f98212 100644
--- a/libraries/libstratosphere/source/dd/dd_io_mappings.cpp
+++ b/libraries/libstratosphere/source/dd/dd_io_mappings.cpp
@@ -25,7 +25,7 @@ namespace ams::dd {
R_TRY_CATCH(svcQueryIoMapping(&virtual_addr, aligned_addr, aligned_size)) {
/* Official software handles this by returning 0. */
R_CATCH(svc::ResultNotFound) { return 0; }
- } R_END_TRY_CATCH_WITH_ASSERT;
+ } R_END_TRY_CATCH_WITH_ABORT_UNLESS;
return static_cast(virtual_addr + offset);
}
diff --git a/libraries/libstratosphere/source/lmem/impl/lmem_impl_common_heap.cpp b/libraries/libstratosphere/source/lmem/impl/lmem_impl_common_heap.cpp
new file mode 100644
index 000000000..3b58dd4ae
--- /dev/null
+++ b/libraries/libstratosphere/source/lmem/impl/lmem_impl_common_heap.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include "lmem_impl_common_heap.hpp"
+
+namespace ams::lmem::impl {
+
+ namespace {
+
+ u32 g_fill_values[FillType_Count] = {
+ 0xC3C3C3C3, /* FillType_Unallocated */
+ 0xF3F3F3F3, /* FillType_Allocated */
+ 0xD3D3D3D3, /* FillType_Freed */
+ };
+
+ }
+
+ void InitializeHeapHead(HeapHead *out, u32 magic, void *start, void *end, u32 option) {
+ /* Call member constructors. */
+ new (&out->list_node) util::IntrusiveListNode;
+ new (&out->child_list) decltype(out->child_list);
+
+ /* Only initialize mutex if option requires it. */
+ if (option & CreateOption_ThreadSafe) {
+ static_assert(std::is_trivially_destructible::value);
+ new (&out->mutex) os::Mutex;
+ }
+
+ /* Set fields. */
+ out->magic = magic;
+ out->heap_start = start;
+ out->heap_end = end;
+ out->option = static_cast(option);
+
+ /* Fill memory with pattern if needed. */
+ FillUnallocatedMemory(out, start, GetPointerDifference(start, end));
+ }
+
+ void FinalizeHeap(HeapHead *heap) {
+ /* Nothing actually needs to be done here. */
+ }
+
+ bool ContainsAddress(HeapHandle handle, const void *address) {
+ const uintptr_t uptr_handle = reinterpret_cast(handle);
+ const uintptr_t uptr_start = reinterpret_cast(handle->heap_start);
+ const uintptr_t uptr_end = reinterpret_cast(handle->heap_end);
+ const uintptr_t uptr_addr = reinterpret_cast(address);
+
+ if (uptr_start - sizeof(HeapHead) == uptr_handle) {
+ /* The heap head is at the start of the managed memory. */
+ return uptr_handle <= uptr_addr && uptr_addr < uptr_end;
+ } else if (uptr_handle == uptr_end) {
+ /* The heap head is at the end of the managed memory. */
+ return uptr_start <= uptr_addr && uptr_addr < uptr_end + sizeof(HeapHead);
+ } else {
+ /* Heap head is somewhere unrelated to managed memory. */
+ return uptr_start <= uptr_addr && uptr_addr < uptr_end;
+ }
+ }
+
+ size_t GetHeapTotalSize(HeapHandle handle) {
+ const uintptr_t uptr_start = reinterpret_cast(handle->heap_start);
+ const uintptr_t uptr_end = reinterpret_cast(handle->heap_end);
+
+ if (ContainsAddress(handle, reinterpret_cast(handle))) {
+ /* The heap metadata is contained within the heap, either before or after. */
+ return static_cast(uptr_end - uptr_start + sizeof(HeapHead));
+ } else {
+ /* The heap metadata is not contained within the heap. */
+ return static_cast(uptr_end - uptr_start);
+ }
+ }
+
+ u32 GetDebugFillValue(FillType type) {
+ return g_fill_values[type];
+ }
+
+ u32 SetDebugFillValue(FillType type, u32 value) {
+ const u32 old_value = g_fill_values[type];
+ g_fill_values[type] = value;
+ return old_value;
+ }
+
+
+}
\ No newline at end of file
diff --git a/libraries/libstratosphere/source/lmem/impl/lmem_impl_common_heap.hpp b/libraries/libstratosphere/source/lmem/impl/lmem_impl_common_heap.hpp
new file mode 100644
index 000000000..0e254f4fa
--- /dev/null
+++ b/libraries/libstratosphere/source/lmem/impl/lmem_impl_common_heap.hpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+
+namespace ams::lmem::impl {
+
+ constexpr inline u32 ExpHeapMagic = util::ReverseFourCC<'E','X','P','H'>::Code;
+ constexpr inline u32 FrameHeapMagic = util::ReverseFourCC<'F','R','M','H'>::Code;
+ constexpr inline u32 UnitHeapMagic = util::ReverseFourCC<'U','N','T','H'>::Code;
+
+ class ScopedHeapLock {
+ NON_COPYABLE(ScopedHeapLock);
+ NON_MOVEABLE(ScopedHeapLock);
+ private:
+ HeapHandle handle;
+ public:
+ explicit ScopedHeapLock(HeapHandle h) : handle(h) {
+ if (this->handle->option & CreateOption_ThreadSafe) {
+ this->handle->mutex.Lock();
+ }
+ }
+
+ ~ScopedHeapLock() {
+ if (this->handle->option & CreateOption_ThreadSafe) {
+ this->handle->mutex.Unlock();
+ }
+ }
+ };
+
+ constexpr inline MemoryRange MakeMemoryRange(void *address, size_t size) {
+ return MemoryRange{ .address = reinterpret_cast(address), .size = size };
+ }
+
+ constexpr inline void *GetHeapStartAddress(HeapHandle handle) {
+ return handle->heap_start;
+ }
+
+ constexpr inline size_t GetPointerDifference(const void *start, const void *end) {
+ return reinterpret_cast(end) - reinterpret_cast(start);
+ }
+
+ constexpr inline size_t GetPointerDifference(uintptr_t start, uintptr_t end) {
+ return end - start;
+ }
+
+ void InitializeHeapHead(HeapHead *out, u32 magic, void *start, void *end, u32 option);
+ void FinalizeHeap(HeapHead *heap);
+ bool ContainsAddress(HeapHandle handle, const void *address);
+ size_t GetHeapTotalSize(HeapHandle handle);
+
+ /* Debug Fill */
+ u32 GetDebugFillValue(FillType type);
+ u32 SetDebugFillValue(FillType type, u32 value);
+
+ inline void FillMemory(void *dst, u32 fill_value, size_t size) {
+ /* All heap blocks must be at least 32-bit aligned. */
+ /* AMS_ASSERT(util::IsAligned(dst, 4)); */
+ /* AMS_ASSERT(util::IsAligned(size, 4)); */
+ for (size_t i = 0; i < size / sizeof(fill_value); i++) {
+ reinterpret_cast(dst)[i] = fill_value;
+ }
+ }
+
+ inline void FillUnallocatedMemory(HeapHead *heap, void *address, size_t size) {
+ if (heap->option & CreateOption_DebugFill) {
+ FillMemory(address, impl::GetDebugFillValue(FillType_Unallocated), size);
+ }
+ }
+
+ inline void FillAllocatedMemory(HeapHead *heap, void *address, size_t size) {
+ if (heap->option & CreateOption_ZeroClear) {
+ FillMemory(address, 0, size);
+ } else if (heap->option & CreateOption_DebugFill) {
+ FillMemory(address, impl::GetDebugFillValue(FillType_Allocated), size);
+ }
+ }
+
+ inline void FillFreedMemory(HeapHead *heap, void *address, size_t size) {
+ if (heap->option & CreateOption_DebugFill) {
+ FillMemory(address, impl::GetDebugFillValue(FillType_Freed), size);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/libraries/libstratosphere/source/lmem/impl/lmem_impl_exp_heap.cpp b/libraries/libstratosphere/source/lmem/impl/lmem_impl_exp_heap.cpp
new file mode 100644
index 000000000..3b168682f
--- /dev/null
+++ b/libraries/libstratosphere/source/lmem/impl/lmem_impl_exp_heap.cpp
@@ -0,0 +1,641 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include "lmem_impl_exp_heap.hpp"
+
+namespace ams::lmem::impl {
+
+ namespace {
+
+ constexpr u16 FreeBlockMagic = 0x4652; /* FR */
+ constexpr u16 UsedBlockMagic = 0x5544; /* UD */
+
+ constexpr u16 DefaultGroupId = 0x00;
+ constexpr u16 MaxGroupId = 0xFF;
+
+ constexpr size_t MinimumAlignment = 4;
+ constexpr size_t MaximumPaddingalignment = 0x80;
+
+ constexpr AllocationMode DefaultAllocationMode = AllocationMode_FirstFit;
+
+ constexpr size_t MinimumFreeBlockSize = 4;
+
+ struct MemoryRegion {
+ void *start;
+ void *end;
+ };
+
+ constexpr inline bool IsValidHeapHandle(HeapHandle handle) {
+ return handle->magic == ExpHeapMagic;
+ }
+
+ constexpr inline ExpHeapHead *GetExpHeapHead(HeapHead *heap_head) {
+ return &heap_head->impl_head.exp_heap_head;
+ }
+
+ constexpr inline const ExpHeapHead *GetExpHeapHead(const HeapHead *heap_head) {
+ return &heap_head->impl_head.exp_heap_head;
+ }
+
+ constexpr inline HeapHead *GetHeapHead(ExpHeapHead *exp_heap_head) {
+ return util::GetParentPointer<&HeapHead::impl_head>(util::GetParentPointer<&ImplementationHeapHead::exp_heap_head>(exp_heap_head));
+ }
+
+ constexpr inline const HeapHead *GetHeapHead(const ExpHeapHead *exp_heap_head) {
+ return util::GetParentPointer<&HeapHead::impl_head>(util::GetParentPointer<&ImplementationHeapHead::exp_heap_head>(exp_heap_head));
+ }
+
+ constexpr inline void *GetExpHeapMemoryStart(ExpHeapHead *exp_heap_head) {
+ return reinterpret_cast(reinterpret_cast(exp_heap_head) + sizeof(ImplementationHeapHead));
+ }
+
+ constexpr inline void *GetMemoryBlockStart(ExpHeapMemoryBlockHead *head) {
+ return reinterpret_cast(reinterpret_cast(head) + sizeof(*head));
+ }
+
+ constexpr inline const void *GetMemoryBlockStart(const ExpHeapMemoryBlockHead *head) {
+ return reinterpret_cast(reinterpret_cast(head) + sizeof(*head));
+ }
+
+ constexpr inline void *GetMemoryBlockEnd(ExpHeapMemoryBlockHead *head) {
+ return reinterpret_cast(reinterpret_cast(GetMemoryBlockStart(head)) + head->block_size);
+ }
+
+ constexpr inline const void *GetMemoryBlockEnd(const ExpHeapMemoryBlockHead *head) {
+ return reinterpret_cast(reinterpret_cast(GetMemoryBlockStart(head)) + head->block_size);
+ }
+
+ constexpr inline ExpHeapMemoryBlockHead *GetHeadForMemoryBlock(const void *block) {
+ return reinterpret_cast(reinterpret_cast(block) - sizeof(ExpHeapMemoryBlockHead));
+ }
+
+ constexpr inline bool IsValidUsedMemoryBlock(const HeapHead *heap, const void *block) {
+ /* Block must fall within the heap range. */
+ if (heap != nullptr) {
+ if (block < heap->heap_start || heap->heap_end <= block) {
+ return false;
+ }
+ }
+
+ /* Block magic must be used. */
+ const ExpHeapMemoryBlockHead *head = GetHeadForMemoryBlock(block);
+ if (head->magic != UsedBlockMagic) {
+ return false;
+ }
+
+ /* End of block must remain within the heap range. */
+ if (heap != nullptr) {
+ if (reinterpret_cast(block) + head->block_size > reinterpret_cast(heap->heap_end)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ constexpr inline u16 GetMemoryBlockAlignmentPadding(const ExpHeapMemoryBlockHead *block_head) {
+ return static_cast((block_head->attributes >> 8) & 0x7F);
+ }
+
+ inline void SetMemoryBlockAlignmentPadding(ExpHeapMemoryBlockHead *block_head, u16 padding) {
+ block_head->attributes &= ~static_castattributes)>(0x7F << 8);
+ block_head->attributes |= static_castattributes)>(padding & 0x7F) << 8;
+ }
+
+ constexpr inline u16 GetMemoryBlockGroupId(const ExpHeapMemoryBlockHead *block_head) {
+ return static_cast(block_head->attributes & 0xFF);
+ }
+
+ inline void SetMemoryBlockGroupId(ExpHeapMemoryBlockHead *block_head, u16 group_id) {
+ block_head->attributes &= ~static_castattributes)>(0xFF);
+ block_head->attributes |= static_castattributes)>(group_id & 0xFF);
+ }
+
+ constexpr inline AllocationDirection GetMemoryBlockAllocationDirection(const ExpHeapMemoryBlockHead *block_head) {
+ return static_cast((block_head->attributes >> 15) & 1);
+ }
+
+ inline void SetMemoryBlockAllocationDirection(ExpHeapMemoryBlockHead *block_head, AllocationDirection dir) {
+ block_head->attributes &= ~static_castattributes)>(0x8000);
+ block_head->attributes |= static_castattributes)>(dir) << 15;
+ }
+
+ inline void GetMemoryBlockRegion(MemoryRegion *out, ExpHeapMemoryBlockHead *head) {
+ out->start = reinterpret_cast(reinterpret_cast(head) - GetMemoryBlockAlignmentPadding(head));
+ out->end = GetMemoryBlockEnd(head);
+ }
+
+ constexpr inline AllocationMode GetAllocationModeImpl(const ExpHeapHead *head) {
+ return static_cast(head->mode);
+ }
+
+ inline void SetAllocationModeImpl(ExpHeapHead *head, AllocationMode mode) {
+ head->mode = mode;
+ }
+
+ inline ExpHeapMemoryBlockHead *InitializeMemoryBlock(const MemoryRegion ®ion, u16 magic) {
+ ExpHeapMemoryBlockHead *block = reinterpret_cast(region.start);
+
+ /* Ensure all member constructors are called. */
+ new (block) ExpHeapMemoryBlockHead;
+
+ block->magic = magic;
+ block->attributes = 0;
+ block->block_size = GetPointerDifference(GetMemoryBlockStart(block), region.end);
+
+ return block;
+ }
+
+ inline ExpHeapMemoryBlockHead *InitializeFreeMemoryBlock(const MemoryRegion ®ion) {
+ return InitializeMemoryBlock(region, FreeBlockMagic);
+ }
+
+ inline ExpHeapMemoryBlockHead *InitializeUsedMemoryBlock(const MemoryRegion ®ion) {
+ return InitializeMemoryBlock(region, UsedBlockMagic);
+ }
+
+ HeapHead *InitializeExpHeap(void *start, void *end, u32 option) {
+ HeapHead *heap_head = reinterpret_cast(start);
+ ExpHeapHead *exp_heap_head = GetExpHeapHead(heap_head);
+
+ /* Initialize the parent heap. */
+ InitializeHeapHead(heap_head, ExpHeapMagic, GetExpHeapMemoryStart(exp_heap_head), end, option);
+
+ /* Call exp heap member constructors. */
+ new (&exp_heap_head->free_list) ExpHeapMemoryBlockList;
+ new (&exp_heap_head->used_list) ExpHeapMemoryBlockList;
+
+ /* Set exp heap fields. */
+ exp_heap_head->group_id = DefaultGroupId;
+ exp_heap_head->use_alignment_margins = false;
+ SetAllocationModeImpl(exp_heap_head, DefaultAllocationMode);
+
+ /* Initialize memory block. */
+ {
+ MemoryRegion region{ .start = heap_head->heap_start, .end = heap_head->heap_end, };
+ exp_heap_head->free_list.push_back(*InitializeFreeMemoryBlock(region));
+ }
+
+ return heap_head;
+ }
+
+ bool CoalesceFreedRegion(ExpHeapHead *head, const MemoryRegion *region) {
+ auto prev_free_block_it = head->free_list.end();
+ MemoryRegion free_region = *region;
+
+ /* Locate the block. */
+ for (auto it = head->free_list.begin(); it != head->free_list.end(); it++) {
+ ExpHeapMemoryBlockHead *cur_free_block = &*it;
+
+ if (cur_free_block < region->start) {
+ prev_free_block_it = it;
+ continue;
+ }
+
+ /* Coalesce block after, if possible. */
+ if (cur_free_block == region->end) {
+ free_region.end = GetMemoryBlockEnd(cur_free_block);
+ it = head->free_list.erase(it);
+
+ /* Fill the memory with a pattern, for debug. */
+ FillUnallocatedMemory(GetHeapHead(head), cur_free_block, sizeof(ExpHeapMemoryBlockHead));
+ }
+
+ break;
+ }
+
+ /* We'll want to insert after the previous free block. */
+ auto insertion_it = head->free_list.begin();
+ if (prev_free_block_it != head->free_list.end()) {
+ /* There's a previous free block, so we want to insert as the next iterator. */
+ if (GetMemoryBlockEnd(&*prev_free_block_it) == region->start) {
+ /* We can coalesce, so do so. */
+ free_region.start = &*prev_free_block_it;
+ insertion_it = head->free_list.erase(prev_free_block_it);
+ } else {
+ /* We can't coalesce, so just select the next iterator. */
+ insertion_it = (++prev_free_block_it);
+ }
+ }
+
+ /* Ensure region is big enough for a block. */
+ /* NOTE: Nintendo does not check against minimum block size here, only header size. */
+ /* We will check against minimum block size, to avoid the creation of zero-size blocks. */
+ if (GetPointerDifference(free_region.start, free_region.end) < sizeof(ExpHeapMemoryBlockHead) + MinimumFreeBlockSize) {
+ return false;
+ }
+
+ /* Fill the memory with a pattern, for debug. */
+ FillFreedMemory(GetHeapHead(head), free_region.start, GetPointerDifference(free_region.start, free_region.end));
+
+ /* Insert the new memory block. */
+ head->free_list.insert(insertion_it, *InitializeFreeMemoryBlock(free_region));
+
+ return true;
+ }
+
+ void *ConvertFreeBlockToUsedBlock(ExpHeapHead *head, ExpHeapMemoryBlockHead *block_head, void *block, size_t size, AllocationDirection direction) {
+ /* Calculate freed memory regions. */
+ MemoryRegion free_region_front;
+ GetMemoryBlockRegion(&free_region_front, block_head);
+ MemoryRegion free_region_back{ .start = reinterpret_cast(reinterpret_cast(block) + size), .end = free_region_front.end, };
+
+ /* Adjust end of head region. */
+ free_region_front.end = reinterpret_cast(reinterpret_cast(block) - sizeof(ExpHeapMemoryBlockHead));
+
+ /* Remove the old block. */
+ auto old_block_it = head->free_list.erase(head->free_list.iterator_to(*block_head));
+
+ /* If the front margins are big enough (and we're allowed to do so), make a new block. */
+ if ((GetPointerDifference(free_region_front.start, free_region_front.end) < sizeof(ExpHeapMemoryBlockHead) + MinimumFreeBlockSize) ||
+ (direction == AllocationDirection_Front && !head->use_alignment_margins && GetPointerDifference(free_region_front.start, free_region_front.end) < MaximumPaddingalignment)) {
+ /* There isn't enough space for a new block, or else we're not allowed to make one. */
+ free_region_front.end = free_region_front.start;
+ } else {
+ /* Make a new block! */
+ head->free_list.insert(old_block_it, *InitializeFreeMemoryBlock(free_region_front));
+ }
+
+ /* If the back margins are big enough (and we're allowed to do so), make a new block. */
+ if ((GetPointerDifference(free_region_back.start, free_region_back.end) < sizeof(ExpHeapMemoryBlockHead) + MinimumFreeBlockSize) ||
+ (direction == AllocationDirection_Back && !head->use_alignment_margins && GetPointerDifference(free_region_back.start, free_region_back.end) < MaximumPaddingalignment)) {
+ /* There isn't enough space for a new block, or else we're not allowed to make one. */
+ free_region_back.end = free_region_back.start;
+ } else {
+ /* Make a new block! */
+ head->free_list.insert(old_block_it, *InitializeFreeMemoryBlock(free_region_back));
+ }
+
+ /* Fill the memory with a pattern, for debug. */
+ FillAllocatedMemory(GetHeapHead(head), free_region_front.end, GetPointerDifference(free_region_front.end, free_region_back.start));
+
+ {
+ /* Create the used block */
+ MemoryRegion used_region{ .start = free_region_front.end, .end = free_region_back.start };
+
+ ExpHeapMemoryBlockHead *used_block = InitializeUsedMemoryBlock(used_region);
+
+ /* Insert it into the used list. */
+ head->used_list.push_back(*used_block);
+ SetMemoryBlockAllocationDirection(used_block, direction);
+ SetMemoryBlockAlignmentPadding(used_block, static_cast(GetPointerDifference(free_region_front.end, used_block)));
+ SetMemoryBlockGroupId(used_block, head->group_id);
+ }
+
+ return block;
+ }
+
+ void *AllocateFromHead(HeapHead *heap, size_t size, s32 alignment) {
+ ExpHeapHead *exp_heap_head = GetExpHeapHead(heap);
+
+ const bool is_first_fit = GetAllocationModeImpl(exp_heap_head) == AllocationMode_FirstFit;
+
+ /* Choose a block. */
+ ExpHeapMemoryBlockHead *found_block_head = nullptr;
+ void *found_block = nullptr;
+ size_t best_size = std::numeric_limits::max();
+
+ for (auto it = exp_heap_head->free_list.begin(); it != exp_heap_head->free_list.end(); it++) {
+ const uintptr_t absolute_block_start = reinterpret_cast(GetMemoryBlockStart(&*it));
+ const uintptr_t block_start = util::AlignUp(absolute_block_start, alignment);
+ const size_t block_offset = block_start - absolute_block_start;
+
+ if (it->block_size >= size + block_offset && best_size > it->block_size) {
+ found_block_head = &*it;
+ found_block = reinterpret_cast(block_start);
+ best_size = it->block_size;
+
+ if (is_first_fit || best_size == size) {
+ break;
+ }
+ }
+ }
+
+ /* If we didn't find a block, return nullptr. */
+ if (found_block_head == nullptr) {
+ return nullptr;
+ }
+
+ return ConvertFreeBlockToUsedBlock(exp_heap_head, found_block_head, found_block, size, AllocationDirection_Front);
+ }
+
+ void *AllocateFromTail(HeapHead *heap, size_t size, s32 alignment) {
+ ExpHeapHead *exp_heap_head = GetExpHeapHead(heap);
+
+ const bool is_first_fit = GetAllocationModeImpl(exp_heap_head) == AllocationMode_FirstFit;
+
+ /* Choose a block. */
+ ExpHeapMemoryBlockHead *found_block_head = nullptr;
+ void *found_block = nullptr;
+ size_t best_size = std::numeric_limits::max();
+
+ for (auto it = exp_heap_head->free_list.rbegin(); it != exp_heap_head->free_list.rend(); it++) {
+ const uintptr_t absolute_block_start = reinterpret_cast(GetMemoryBlockStart(&*it));
+ const uintptr_t block_start = util::AlignUp(absolute_block_start, alignment);
+ const size_t block_offset = block_start - absolute_block_start;
+
+ if (it->block_size >= size + block_offset && best_size > it->block_size) {
+ found_block_head = &*it;
+ found_block = reinterpret_cast(block_start);
+ best_size = it->block_size;
+
+ if (is_first_fit || best_size == size) {
+ break;
+ }
+ }
+ }
+
+ /* If we didn't find a block, return nullptr. */
+ if (found_block_head == nullptr) {
+ return nullptr;
+ }
+
+ return ConvertFreeBlockToUsedBlock(exp_heap_head, found_block_head, found_block, size, AllocationDirection_Back);
+ }
+
+ }
+
+ HeapHandle CreateExpHeap(void *address, size_t size, u32 option) {
+ const uintptr_t uptr_end = util::AlignDown(reinterpret_cast(address) + size, MinimumAlignment);
+ const uintptr_t uptr_start = util::AlignUp(reinterpret_cast(address), MinimumAlignment);
+
+ if (uptr_start > uptr_end || GetPointerDifference(uptr_start, uptr_end) < sizeof(ExpHeapMemoryBlockHead) + MinimumFreeBlockSize) {
+ return nullptr;
+ }
+
+ return InitializeExpHeap(reinterpret_cast(uptr_start), reinterpret_cast(uptr_end), option);
+ }
+
+ void DestroyExpHeap(HeapHandle handle) {
+ AMS_ASSERT(IsValidHeapHandle(handle));
+
+ FinalizeHeap(handle);
+ }
+
+ MemoryRange AdjustExpHeap(HeapHandle handle) {
+ AMS_ASSERT(IsValidHeapHandle(handle));
+
+ HeapHead *heap_head = handle;
+ ExpHeapHead *exp_heap_head = GetExpHeapHead(heap_head);
+
+ /* If there's no free blocks, we can't do anything. */
+ if (exp_heap_head->free_list.empty()) {
+ return MakeMemoryRange(handle->heap_end, 0);
+ }
+
+ /* Get the memory block end, make sure it really is the last block. */
+ ExpHeapMemoryBlockHead *block = &exp_heap_head->free_list.back();
+ void * const block_start = GetMemoryBlockStart(block);
+ const size_t block_size = block->block_size;
+ void * const block_end = reinterpret_cast(reinterpret_cast(block_start) + block_size);
+
+ if (block_end != handle->heap_end) {
+ return MakeMemoryRange(handle->heap_end, 0);
+ }
+
+ /* Remove the memory block. */
+ exp_heap_head->free_list.pop_back();
+
+ const size_t freed_size = block_size + sizeof(ExpHeapMemoryBlockHead);
+ heap_head->heap_end = reinterpret_cast(reinterpret_cast(heap_head->heap_end) - freed_size);
+ return MakeMemoryRange(heap_head->heap_end, freed_size);
+ }
+
+ void *AllocateFromExpHeap(HeapHandle handle, size_t size, s32 alignment) {
+ AMS_ASSERT(IsValidHeapHandle(handle));
+
+ /* Fix up alignments less than 4. */
+ if (alignment == 1 || alignment == 2) {
+ alignment = 4;
+ } else if (alignment == -1 || alignment == -2) {
+ alignment = -4;
+ }
+
+ /* Ensure the alignment is valid. */
+ const s32 abs_alignment = std::abs(alignment);
+ AMS_ASSERT((abs_alignment & (abs_alignment - 1)) == 0);
+ AMS_ASSERT(MinimumAlignment <= static_cast(abs_alignment));
+
+ /* Fix size to be correctly aligned. */
+ if (size == 0) {
+ size = 1;
+ }
+ size = util::AlignUp(size, MinimumAlignment);
+
+ /* Allocate a memory block. */
+ void *allocated_memory = nullptr;
+ if (alignment >= 0) {
+ allocated_memory = AllocateFromHead(handle, size, alignment);
+ } else {
+ allocated_memory = AllocateFromTail(handle, size, -alignment);
+ }
+
+ return allocated_memory;
+ }
+
+ void FreeToExpHeap(HeapHandle handle, void *mem_block) {
+ /* Ensure this is actually a valid heap and a valid memory block we allocated. */
+ AMS_ASSERT(IsValidHeapHandle(handle));
+ AMS_ASSERT(IsValidUsedMemoryBlock(handle, mem_block));
+
+ /* TODO: Nintendo does not allow FreeToExpHeap(nullptr). Should we? */
+
+ /* Get block pointers. */
+ HeapHead *heap_head = handle;
+ ExpHeapHead *exp_heap_head = GetExpHeapHead(heap_head);
+ ExpHeapMemoryBlockHead *block = GetHeadForMemoryBlock(mem_block);
+ MemoryRegion region;
+
+ /* Erase the heap from the used list, and coalesce it with adjacent blocks. */
+ GetMemoryBlockRegion(®ion, block);
+ exp_heap_head->used_list.erase(exp_heap_head->used_list.iterator_to(*block));
+ AMS_ASSERT(CoalesceFreedRegion(exp_heap_head, ®ion));
+ }
+
+ size_t ResizeExpHeapMemoryBlock(HeapHandle handle, void *mem_block, size_t size) {
+ /* Ensure this is actually a valid heap and a valid memory block we allocated. */
+ AMS_ASSERT(IsValidHeapHandle(handle));
+ AMS_ASSERT(IsValidUsedMemoryBlock(handle, mem_block));
+
+ ExpHeapHead *exp_heap_head = GetExpHeapHead(handle);
+ ExpHeapMemoryBlockHead *block_head = GetHeadForMemoryBlock(mem_block);
+ const size_t original_block_size = block_head->block_size;
+
+ /* It's possible that there's no actual resizing being done. */
+ size = util::AlignUp(size, MinimumAlignment);
+ if (size == original_block_size) {
+ return size;
+ }
+
+ /* We're resizing one way or the other. */
+ if (size > original_block_size) {
+ /* We want to try to make the block bigger. */
+
+ /* Find the free block after this one. */
+ void * const cur_block_end = GetMemoryBlockEnd(block_head);
+ ExpHeapMemoryBlockHead *next_block_head = nullptr;
+
+ for (auto it = exp_heap_head->free_list.begin(); it != exp_heap_head->free_list.end(); it++) {
+ if (&*it == cur_block_end) {
+ next_block_head = &*it;
+ break;
+ }
+ }
+
+ /* If we can't get a big enough allocation using the next block, give up. */
+ if (next_block_head == nullptr || size > original_block_size + sizeof(ExpHeapMemoryBlockHead) + next_block_head->block_size) {
+ return 0;
+ }
+
+ /* Grow the block to encompass the next block. */
+ {
+ /* Get block region. */
+ MemoryRegion new_free_region;
+ GetMemoryBlockRegion(&new_free_region, next_block_head);
+
+ /* Remove the next block from the free list. */
+ auto insertion_it = exp_heap_head->free_list.erase(exp_heap_head->free_list.iterator_to(*next_block_head));
+
+ /* Figure out the new block extents. */
+ void *old_start = new_free_region.start;
+ new_free_region.start = reinterpret_cast(reinterpret_cast(mem_block) + size);
+
+ /* Only maintain the new free region as a memory block candidate if it can hold a header. */
+ /* NOTE: Nintendo does not check against minimum block size here, only header size. */
+ /* We will check against minimum block size, to avoid the creation of zero-size blocks. */
+ if (GetPointerDifference(new_free_region.start, new_free_region.end) < sizeof(ExpHeapMemoryBlockHead) + MinimumFreeBlockSize) {
+ new_free_region.start = new_free_region.end;
+ }
+
+ /* Adjust block sizes. */
+ block_head->block_size = GetPointerDifference(mem_block, new_free_region.start);
+ if (GetPointerDifference(new_free_region.start, new_free_region.end) >= sizeof(ExpHeapMemoryBlockHead) + MinimumFreeBlockSize) {
+ exp_heap_head->free_list.insert(insertion_it, *InitializeFreeMemoryBlock(new_free_region));
+ }
+
+ /* Fill the memory with a pattern, for debug. */
+ FillAllocatedMemory(GetHeapHead(exp_heap_head), old_start, GetPointerDifference(old_start, new_free_region.start));
+ }
+ } else {
+ /* We're shrinking the block. Nice and easy. */
+ MemoryRegion new_free_region{ .start = reinterpret_cast(reinterpret_cast(mem_block)+ size), .end = GetMemoryBlockEnd(block_head) };
+
+ /* Try to free the new memory. */
+ block_head->block_size = size;
+ if (!CoalesceFreedRegion(exp_heap_head, &new_free_region)) {
+ /* We didn't shrink the block successfully, so restore the size. */
+ block_head->block_size = original_block_size;
+ }
+ }
+
+ return block_head->block_size;
+ }
+
+ size_t GetExpHeapTotalFreeSize(HeapHandle handle) {
+ AMS_ASSERT(IsValidHeapHandle(handle));
+
+ size_t total_size = 0;
+ for (const auto &it : GetExpHeapHead(handle)->free_list) {
+ total_size += it.block_size;
+ }
+ return total_size;
+ }
+
+ size_t GetExpHeapAllocatableSize(HeapHandle handle, s32 alignment) {
+ AMS_ASSERT(IsValidHeapHandle(handle));
+
+ /* Ensure alignment is positive. */
+ alignment = std::abs(alignment);
+
+ size_t max_size = std::numeric_limits::min();
+ size_t min_offset = std::numeric_limits::max();
+ for (const auto &it : GetExpHeapHead(handle)->free_list) {
+ const uintptr_t absolute_block_start = reinterpret_cast(GetMemoryBlockStart(&it));
+ const uintptr_t block_start = util::AlignUp(absolute_block_start, alignment);
+ const uintptr_t block_end = reinterpret_cast(GetMemoryBlockEnd(&it));
+
+ if (block_start < block_end) {
+ const size_t block_size = GetPointerDifference(block_start, block_end);
+ const size_t offset = GetPointerDifference(absolute_block_start, block_start);
+
+ if (block_size > max_size || (block_size == max_size && offset < min_offset)) {
+ max_size = block_size;
+ min_offset = offset;
+ }
+ }
+ }
+
+ return max_size;
+ }
+
+ AllocationMode GetExpHeapAllocationMode(HeapHandle handle) {
+ AMS_ASSERT(IsValidHeapHandle(handle));
+
+ return GetAllocationModeImpl(GetExpHeapHead(handle));
+ }
+
+ AllocationMode SetExpHeapAllocationMode(HeapHandle handle, AllocationMode new_mode) {
+ AMS_ASSERT(IsValidHeapHandle(handle));
+
+ ExpHeapHead *exp_heap_head = GetExpHeapHead(handle);
+ const AllocationMode old_mode = GetAllocationModeImpl(exp_heap_head);
+ SetAllocationModeImpl(exp_heap_head, new_mode);
+ return old_mode;
+ }
+
+ u16 GetExpHeapGroupId(HeapHandle handle) {
+ AMS_ASSERT(IsValidHeapHandle(handle));
+
+ return GetExpHeapHead(handle)->group_id;
+ }
+
+ u16 SetExpHeapGroupId(HeapHandle handle, u16 group_id) {
+ AMS_ASSERT(IsValidHeapHandle(handle));
+ AMS_ASSERT(group_id <= MaxGroupId);
+
+ ExpHeapHead *exp_heap_head = GetExpHeapHead(handle);
+ const u16 old_group_id = exp_heap_head->group_id;
+ exp_heap_head->group_id = group_id;
+ return old_group_id;
+ }
+
+ void VisitExpHeapAllocatedBlocks(HeapHandle handle, HeapVisitor visitor, uintptr_t user_data) {
+ AMS_ASSERT(IsValidHeapHandle(handle));
+
+ for (auto &it : GetExpHeapHead(handle)->used_list) {
+ (*visitor)(GetMemoryBlockStart(&it), handle, user_data);
+ }
+ }
+
+ size_t GetExpHeapMemoryBlockSize(const void *memory_block) {
+ AMS_ASSERT(IsValidUsedMemoryBlock(nullptr, memory_block));
+
+ return GetHeadForMemoryBlock(memory_block)->block_size;
+ }
+
+ u16 GetExpHeapMemoryBlockGroupId(const void *memory_block) {
+ AMS_ASSERT(IsValidUsedMemoryBlock(nullptr, memory_block));
+
+ return GetMemoryBlockGroupId(GetHeadForMemoryBlock(memory_block));
+ }
+
+ AllocationDirection GetExpHeapMemoryBlockAllocationDirection(const void *memory_block) {
+ AMS_ASSERT(IsValidUsedMemoryBlock(nullptr, memory_block));
+
+ return GetMemoryBlockAllocationDirection(GetHeadForMemoryBlock(memory_block));
+ }
+
+}
diff --git a/libraries/libstratosphere/source/lmem/impl/lmem_impl_exp_heap.hpp b/libraries/libstratosphere/source/lmem/impl/lmem_impl_exp_heap.hpp
new file mode 100644
index 000000000..4522a5344
--- /dev/null
+++ b/libraries/libstratosphere/source/lmem/impl/lmem_impl_exp_heap.hpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+#include "lmem_impl_common_heap.hpp"
+
+namespace ams::lmem::impl {
+
+ HeapHandle CreateExpHeap(void *address, size_t size, u32 option);
+ void DestroyExpHeap(HeapHandle handle);
+ MemoryRange AdjustExpHeap(HeapHandle handle);
+
+ void *AllocateFromExpHeap(HeapHandle handle, size_t size, s32 alignment);
+ void FreeToExpHeap(HeapHandle handle, void *block);
+
+ size_t ResizeExpHeapMemoryBlock(HeapHandle handle, void *block, size_t size);
+
+ size_t GetExpHeapTotalFreeSize(HeapHandle handle);
+ size_t GetExpHeapAllocatableSize(HeapHandle handle, s32 alignment);
+
+ AllocationMode GetExpHeapAllocationMode(HeapHandle handle);
+ AllocationMode SetExpHeapAllocationMode(HeapHandle handle, AllocationMode new_mode);
+
+ bool GetExpHeapUseMarginsOfAlignment(HeapHandle handle);
+ bool SetExpHeapUseMarginsOfAlignment(HeapHandle handle, bool use_margins);
+
+ u16 GetExpHeapGroupId(HeapHandle handle);
+ u16 SetExpHeapGroupId(HeapHandle handle, u16 group_id);
+
+ size_t GetExpHeapMemoryBlockSize(const void *memory_block);
+ u16 GetExpHeapMemoryBlockGroupId(const void *memory_block);
+ AllocationDirection GetExpHeapMemoryBlockAllocationDirection(const void *memory_block);
+
+ void VisitExpHeapAllocatedBlocks(HeapHandle handle, HeapVisitor visitor, uintptr_t user_data);
+
+}
\ No newline at end of file
diff --git a/libraries/libstratosphere/source/lmem/impl/lmem_impl_unit_heap.cpp b/libraries/libstratosphere/source/lmem/impl/lmem_impl_unit_heap.cpp
new file mode 100644
index 000000000..bbb59aa83
--- /dev/null
+++ b/libraries/libstratosphere/source/lmem/impl/lmem_impl_unit_heap.cpp
@@ -0,0 +1,263 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include "lmem_impl_unit_heap.hpp"
+
+namespace ams::lmem::impl {
+
+ namespace {
+
+ constexpr size_t MinimumAlignment = 4;
+
+ constexpr inline bool IsValidHeapHandle(HeapHandle handle) {
+ return handle->magic == UnitHeapMagic;
+ }
+
+ constexpr inline UnitHeapHead *GetUnitHeapHead(HeapHead *heap_head) {
+ return &heap_head->impl_head.unit_heap_head;
+ }
+
+ constexpr inline const UnitHeapHead *GetUnitHeapHead(const HeapHead *heap_head) {
+ return &heap_head->impl_head.unit_heap_head;
+ }
+
+ inline UnitHead *PopUnit(UnitHeapList *list) {
+ if (UnitHead *block = list->head; block != nullptr) {
+ list->head = block->next;
+ return block;
+ } else {
+ return nullptr;
+ }
+ }
+
+ inline void PushUnit(UnitHeapList *list, UnitHead *block) {
+ block->next = list->head;
+ list->head = block;
+ }
+
+ }
+
+ HeapHandle CreateUnitHeap(void *address, size_t size, size_t unit_size, s32 alignment, u16 option, InfoPlacement info_placement, HeapCommonHead *heap_head) {
+ AMS_ASSERT(address != nullptr);
+
+ /* Correct alignment, validate. */
+ if (alignment == 1 || alignment == 2) {
+ alignment = 4;
+ }
+ AMS_ASSERT(util::IsAligned(alignment, MinimumAlignment));
+ AMS_ASSERT(static_cast(MinimumAlignment) <= alignment);
+ AMS_ASSERT(unit_size >= sizeof(uintptr_t));
+
+ /* Setup heap metadata. */
+ UnitHeapHead *unit_heap = nullptr;
+ void *heap_start = nullptr;
+ void *heap_end = nullptr;
+ if (heap_head == nullptr) {
+ /* Internal heap metadata. */
+ if (info_placement == InfoPlacement_Head) {
+ heap_head = reinterpret_cast(util::AlignUp(address, MinimumAlignment));
+ unit_heap = GetUnitHeapHead(heap_head);
+ heap_end = util::AlignDown(reinterpret_cast(reinterpret_cast(address) + size), MinimumAlignment);
+ heap_start = util::AlignUp(reinterpret_cast(reinterpret_cast(heap_head) + sizeof(HeapHead)), alignment);
+ } else if (info_placement == InfoPlacement_Tail) {
+ heap_end = util::AlignDown(reinterpret_cast(reinterpret_cast(address) + size - sizeof(HeapHead)), MinimumAlignment);
+ heap_head = reinterpret_cast(heap_end);
+ unit_heap = GetUnitHeapHead(heap_head);
+ heap_start = util::AlignUp(address, alignment);
+ } else {
+ AMS_ASSERT(false);
+ }
+ } else {
+ /* External heap metadata. */
+ unit_heap = GetUnitHeapHead(heap_head);
+ heap_end = util::AlignDown(reinterpret_cast(reinterpret_cast(address) + size), MinimumAlignment);
+ heap_start = util::AlignUp(address, alignment);
+ }
+
+ /* Correct unit size. */
+ unit_size = util::AlignUp(unit_size, alignment);
+
+ /* Don't allow a heap with start after end. */
+ if (heap_start > heap_end) {
+ return nullptr;
+ }
+
+ /* Don't allow a heap with no units. */
+ size_t max_units = GetPointerDifference(heap_start, heap_end) / unit_size;
+ if (max_units == 0) {
+ return nullptr;
+ }
+
+ /* Set real heap end. */
+ heap_end = reinterpret_cast(reinterpret_cast(heap_start) + max_units * unit_size);
+
+ /* Initialize the parent heap. */
+ InitializeHeapHead(heap_head, UnitHeapMagic, heap_start, heap_end, option);
+
+ /* Initialize the actual unit heap. */
+ {
+ unit_heap->free_list.head = reinterpret_cast(heap_start);
+ unit_heap->unit_size = unit_size;
+ unit_heap->alignment = alignment;
+ unit_heap->num_units = 0;
+
+ /* Create the new units. */
+ UnitHead *cur_tail = unit_heap->free_list.head;
+ for (size_t i = 0; i < max_units - 1; i++) {
+ cur_tail->next = reinterpret_cast(reinterpret_cast(cur_tail) + unit_size);
+ cur_tail = cur_tail->next;
+ }
+ cur_tail->next = nullptr;
+ }
+
+ /* Return the heap header as handle. */
+ return heap_head;
+ }
+
+ void DestroyUnitHeap(HeapHandle handle) {
+ AMS_ASSERT(IsValidHeapHandle(handle));
+
+ /* Validate that the heap has no living units. */
+ UnitHeapHead *unit_heap = GetUnitHeapHead(handle);
+ if (unit_heap->free_list.head != nullptr) {
+ AMS_ASSERT(unit_heap->num_units == 0);
+ unit_heap->free_list.head = nullptr;
+ }
+
+ FinalizeHeap(handle);
+ }
+
+ void InvalidateUnitHeap(HeapHandle handle) {
+ AMS_ASSERT(IsValidHeapHandle(handle));
+ GetUnitHeapHead(handle)->free_list.head = nullptr;
+ }
+
+ void ExtendUnitHeap(HeapHandle handle, size_t size) {
+ AMS_ASSERT(IsValidHeapHandle(handle));
+
+ /* Find the current tail unit, and insert as next. */
+ UnitHeapHead *unit_heap = GetUnitHeapHead(handle);
+ UnitHead *cur_tail;
+ if (unit_heap->free_list.head != nullptr) {
+ cur_tail = unit_heap->free_list.head;
+
+ while (cur_tail->next != nullptr) {
+ cur_tail = cur_tail->next;
+ }
+
+ cur_tail->next = reinterpret_cast(handle->heap_end);
+ cur_tail = cur_tail->next;
+ cur_tail->next = nullptr;
+ } else {
+ /* All units are allocated, so set the free list to be at the end of the heap area. */
+ unit_heap->free_list.head = reinterpret_cast(handle->heap_end);
+ cur_tail = unit_heap->free_list.head;
+ cur_tail->next = nullptr;
+ }
+
+ /* Calculate new unit extents. */
+ void *new_units_start = handle->heap_end;
+ void *new_units_end = reinterpret_cast(reinterpret_cast(new_units_start) + size);
+ size_t num_new_units = GetPointerDifference(new_units_start, new_units_end) / unit_heap->unit_size;
+ AMS_ASSERT(num_new_units > 0);
+
+ /* Create the new units. */
+ for (size_t i = 0; i < num_new_units - 1; i++) {
+ cur_tail->next = reinterpret_cast(reinterpret_cast(cur_tail) + unit_heap->unit_size);
+ cur_tail = cur_tail->next;
+ }
+ cur_tail->next = nullptr;
+
+ /* Note that the heap is bigger. */
+ handle->heap_end = new_units_end;
+ }
+
+ void *AllocateFromUnitHeap(HeapHandle handle) {
+ AMS_ASSERT(IsValidHeapHandle(handle));
+
+ /* Allocate a unit. */
+ UnitHeapHead *unit_heap = GetUnitHeapHead(handle);
+ UnitHead *unit = PopUnit(&unit_heap->free_list);
+ if (unit != nullptr) {
+ /* Fill memory with pattern for debug, if needed. */
+ FillAllocatedMemory(handle, unit, unit_heap->unit_size);
+
+ /* Note that we allocated a unit. */
+ unit_heap->num_units++;
+ }
+
+ return unit;
+ }
+
+ void FreeToUnitHeap(HeapHandle handle, void *block) {
+ AMS_ASSERT(IsValidHeapHandle(handle));
+
+ /* Allow Free(nullptr) to succeed. */
+ if (block == nullptr) {
+ return;
+ }
+
+ /* Fill memory with pattern for debug, if needed. */
+ UnitHeapHead *unit_heap = GetUnitHeapHead(handle);
+ FillFreedMemory(handle, block, unit_heap->unit_size);
+
+ /* Push the unit onto the free list. */
+ PushUnit(&unit_heap->free_list, reinterpret_cast(block));
+
+ /* Note that we freed a unit. */
+ unit_heap->num_units--;
+ }
+
+ size_t GetUnitHeapUnitSize(HeapHandle handle) {
+ AMS_ASSERT(IsValidHeapHandle(handle));
+ return GetUnitHeapHead(handle)->unit_size;
+ }
+
+ s32 GetUnitHeapAlignment(HeapHandle handle) {
+ AMS_ASSERT(IsValidHeapHandle(handle));
+ return GetUnitHeapHead(handle)->alignment;
+ }
+
+ size_t GetUnitHeapFreeCount(HeapHandle handle) {
+ AMS_ASSERT(IsValidHeapHandle(handle));
+
+ size_t count = 0;
+ for (UnitHead *cur = GetUnitHeapHead(handle)->free_list.head; cur != nullptr; cur = cur->next) {
+ count++;
+ }
+ return count;
+ }
+
+ size_t GetUnitHeapUsedCount(HeapHandle handle) {
+ AMS_ASSERT(IsValidHeapHandle(handle));
+ return GetUnitHeapHead(handle)->num_units;
+ }
+
+ size_t GetUnitHeapRequiredSize(size_t unit_size, size_t unit_count, s32 alignment, bool internal_metadata) {
+ /* Nintendo does not round up alignment here, even though they do so in CreateUnitHeap. */
+ /* We will round up alignment to return more accurate results. */
+ if (alignment == 1 || alignment == 2) {
+ alignment = 4;
+ }
+
+ AMS_ASSERT(util::IsAligned(alignment, MinimumAlignment));
+ AMS_ASSERT(static_cast(MinimumAlignment) <= alignment);
+ AMS_ASSERT(unit_size >= sizeof(uintptr_t));
+
+ return (alignment - 1) + util::AlignUp(unit_size, alignment) + (internal_metadata ? sizeof(HeapHead) : 0);
+ }
+
+}
diff --git a/libraries/libstratosphere/source/lmem/impl/lmem_impl_unit_heap.hpp b/libraries/libstratosphere/source/lmem/impl/lmem_impl_unit_heap.hpp
new file mode 100644
index 000000000..4c4dd5a97
--- /dev/null
+++ b/libraries/libstratosphere/source/lmem/impl/lmem_impl_unit_heap.hpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+#include "lmem_impl_common_heap.hpp"
+
+namespace ams::lmem::impl {
+
+ HeapHandle CreateUnitHeap(void *address, size_t size, size_t unit_size, s32 alignment, u16 option, InfoPlacement info_placement, HeapCommonHead *heap_head);
+ void DestroyUnitHeap(HeapHandle handle);
+
+ void InvalidateUnitHeap(HeapHandle handle);
+ void ExtendUnitHeap(HeapHandle handle, size_t size);
+
+ void *AllocateFromUnitHeap(HeapHandle handle);
+ void FreeToUnitHeap(HeapHandle handle, void *block);
+
+ size_t GetUnitHeapUnitSize(HeapHandle handle);
+ s32 GetUnitHeapAlignment(HeapHandle handle);
+ size_t GetUnitHeapFreeCount(HeapHandle handle);
+ size_t GetUnitHeapUsedCount(HeapHandle handle);
+
+ size_t GetUnitHeapRequiredSize(size_t unit_size, size_t unit_count, s32 alignment, bool internal_metadata);
+
+}
\ No newline at end of file
diff --git a/libraries/libstratosphere/source/lmem/lmem_common.cpp b/libraries/libstratosphere/source/lmem/lmem_common.cpp
new file mode 100644
index 000000000..7d00f3b6a
--- /dev/null
+++ b/libraries/libstratosphere/source/lmem/lmem_common.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include "impl/lmem_impl_common_heap.hpp"
+
+namespace ams::lmem {
+
+ u32 GetDebugFillValue(FillType fill_type) {
+ return impl::GetDebugFillValue(fill_type);
+ }
+
+ void SetDebugFillValue(FillType fill_type, u32 value) {
+ impl::SetDebugFillValue(fill_type, value);
+ }
+
+ size_t GetTotalSize(HeapHandle handle) {
+ impl::ScopedHeapLock lk(handle);
+ return impl::GetHeapTotalSize(handle);
+ }
+
+ void *GetStartAddress(HeapHandle handle) {
+ impl::ScopedHeapLock lk(handle);
+ return impl::GetHeapStartAddress(handle);
+ }
+
+ bool ContainsAddress(HeapHandle handle, const void *address) {
+ impl::ScopedHeapLock lk(handle);
+ return impl::ContainsAddress(handle, address);
+ }
+
+}
diff --git a/libraries/libstratosphere/source/lmem/lmem_exp_heap.cpp b/libraries/libstratosphere/source/lmem/lmem_exp_heap.cpp
new file mode 100644
index 000000000..5d8e8352f
--- /dev/null
+++ b/libraries/libstratosphere/source/lmem/lmem_exp_heap.cpp
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include "impl/lmem_impl_exp_heap.hpp"
+
+namespace ams::lmem {
+
+ HeapHandle CreateExpHeap(void *address, size_t size, u32 option) {
+ return impl::CreateExpHeap(address, size, option);
+ }
+
+ void DestroyExpHeap(HeapHandle handle) {
+ impl::DestroyExpHeap(handle);
+ }
+
+ MemoryRange AdjustExpHeap(HeapHandle handle) {
+ impl::ScopedHeapLock lk(handle);
+ return impl::AdjustExpHeap(handle);
+ }
+
+ void *AllocateFromExpHeap(HeapHandle handle, size_t size) {
+ impl::ScopedHeapLock lk(handle);
+ return impl::AllocateFromExpHeap(handle, size, DefaultAlignment);
+ }
+
+ void *AllocateFromExpHeap(HeapHandle handle, size_t size, s32 alignment) {
+ impl::ScopedHeapLock lk(handle);
+ return impl::AllocateFromExpHeap(handle, size, alignment);
+ }
+
+ void FreeToExpHeap(HeapHandle handle, void *block) {
+ impl::ScopedHeapLock lk(handle);
+ impl::FreeToExpHeap(handle, block);
+ }
+
+ size_t ResizeExpHeapMemoryBlock(HeapHandle handle, void *block, size_t size) {
+ impl::ScopedHeapLock lk(handle);
+ return impl::ResizeExpHeapMemoryBlock(handle, block, size);
+ }
+
+ size_t GetExpHeapTotalFreeSize(HeapHandle handle) {
+ impl::ScopedHeapLock lk(handle);
+ return impl::GetExpHeapTotalFreeSize(handle);
+ }
+
+ size_t GetExpHeapAllocatableSize(HeapHandle handle, s32 alignment) {
+ impl::ScopedHeapLock lk(handle);
+ return impl::GetExpHeapAllocatableSize(handle, alignment);
+ }
+
+ AllocationMode GetExpHeapAllocationMode(HeapHandle handle) {
+ impl::ScopedHeapLock lk(handle);
+ return impl::GetExpHeapAllocationMode(handle);
+ }
+
+ AllocationMode SetExpHeapAllocationMode(HeapHandle handle, AllocationMode new_mode) {
+ impl::ScopedHeapLock lk(handle);
+ return impl::SetExpHeapAllocationMode(handle, new_mode);
+ }
+
+ bool GetExpHeapUseMarginsOfAlignment(HeapHandle handle) {
+ impl::ScopedHeapLock lk(handle);
+ return impl::GetExpHeapUseMarginsOfAlignment(handle);
+ }
+
+ bool SetExpHeapUseMarginsOfAlignment(HeapHandle handle, bool use_margins) {
+ impl::ScopedHeapLock lk(handle);
+ return impl::SetExpHeapUseMarginsOfAlignment(handle, use_margins);
+ }
+
+ u16 GetExpHeapGroupId(HeapHandle handle) {
+ impl::ScopedHeapLock lk(handle);
+ return impl::GetExpHeapGroupId(handle);
+ }
+
+ u16 SetExpHeapGroupId(HeapHandle handle, u16 group_id) {
+ impl::ScopedHeapLock lk(handle);
+ return impl::SetExpHeapGroupId(handle, group_id);
+ }
+
+ size_t GetExpHeapMemoryBlockSize(const void *memory_block) {
+ return impl::GetExpHeapMemoryBlockSize(memory_block);
+ }
+
+ u16 GetExpHeapMemoryBlockGroupId(const void *memory_block) {
+ return impl::GetExpHeapMemoryBlockGroupId(memory_block);
+ }
+
+ AllocationDirection GetExpHeapMemoryBlockAllocationDirection(const void *memory_block) {
+ return impl::GetExpHeapMemoryBlockAllocationDirection(memory_block);
+ }
+
+ void VisitExpHeapAllocatedBlocks(HeapHandle handle, HeapVisitor visitor, uintptr_t user_data) {
+ impl::ScopedHeapLock lk(handle);
+ impl::VisitExpHeapAllocatedBlocks(handle, visitor, user_data);
+ }
+
+}
diff --git a/libraries/libstratosphere/source/lmem/lmem_unit_heap.cpp b/libraries/libstratosphere/source/lmem/lmem_unit_heap.cpp
new file mode 100644
index 000000000..3b613bb71
--- /dev/null
+++ b/libraries/libstratosphere/source/lmem/lmem_unit_heap.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include "impl/lmem_impl_unit_heap.hpp"
+
+namespace ams::lmem {
+
+ HeapHandle CreateUnitHeap(void *address, size_t size, size_t unit_size, u32 option) {
+ return impl::CreateUnitHeap(address, size, unit_size, DefaultAlignment, static_cast(option), InfoPlacement_Head, nullptr);
+ }
+
+ HeapHandle CreateUnitHeap(void *address, size_t size, size_t unit_size, u32 option, s32 alignment, InfoPlacement info_placement) {
+ return impl::CreateUnitHeap(address, size, unit_size, alignment, static_cast(option), info_placement, nullptr);
+ }
+
+ HeapHandle CreateUnitHeap(void *address, size_t size, size_t unit_size, u32 option, s32 alignment, HeapCommonHead *heap_head) {
+ return impl::CreateUnitHeap(address, size, unit_size, alignment, static_cast(option), InfoPlacement_Head, heap_head);
+ }
+
+ void DestroyUnitHeap(HeapHandle handle) {
+ impl::DestroyUnitHeap(handle);
+ }
+
+ void InvalidateUnitHeap(HeapHandle handle) {
+ impl::ScopedHeapLock lk(handle);
+ impl::InvalidateUnitHeap(handle);
+ }
+
+ void ExtendUnitHeap(HeapHandle handle, size_t size) {
+ impl::ScopedHeapLock lk(handle);
+ impl::ExtendUnitHeap(handle, size);
+ }
+
+ void *AllocateFromUnitHeap(HeapHandle handle) {
+ impl::ScopedHeapLock lk(handle);
+ return impl::AllocateFromUnitHeap(handle);
+ }
+
+ void FreeToUnitHeap(HeapHandle handle, void *block) {
+ impl::ScopedHeapLock lk(handle);
+ impl::FreeToUnitHeap(handle, block);
+ }
+
+ size_t GetUnitHeapUnitSize(HeapHandle handle) {
+ /* Nintendo doesn't acquire a lock here. */
+ return impl::GetUnitHeapUnitSize(handle);
+ }
+
+ s32 GetUnitHeapAlignment(HeapHandle handle) {
+ /* Nintendo doesn't acquire a lock here. */
+ return impl::GetUnitHeapAlignment(handle);
+ }
+
+ size_t GetUnitHeapFreeCount(HeapHandle handle) {
+ impl::ScopedHeapLock lk(handle);
+ return impl::GetUnitHeapFreeCount(handle);
+ }
+
+ size_t GetUnitHeapUsedCount(HeapHandle handle) {
+ impl::ScopedHeapLock lk(handle);
+ return impl::GetUnitHeapUsedCount(handle);
+ }
+
+ size_t GetUnitHeapRequiredSize(size_t unit_size, size_t unit_count, s32 alignment, bool internal_metadata) {
+ return impl::GetUnitHeapRequiredSize(unit_size, unit_count, alignment, internal_metadata);
+ }
+
+}
diff --git a/libraries/libstratosphere/source/os/impl/os_inter_process_event.cpp b/libraries/libstratosphere/source/os/impl/os_inter_process_event.cpp
index e933a55bd..ae3e9863b 100644
--- a/libraries/libstratosphere/source/os/impl/os_inter_process_event.cpp
+++ b/libraries/libstratosphere/source/os/impl/os_inter_process_event.cpp
@@ -23,7 +23,7 @@ namespace ams::os::impl {
/* Create the event handles. */
R_TRY_CATCH(svcCreateEvent(out_writable, out_readable)) {
R_CONVERT(svc::ResultOutOfResource, ResultOutOfResource());
- } R_END_TRY_CATCH_WITH_ASSERT;
+ } R_END_TRY_CATCH_WITH_ABORT_UNLESS;
return ResultSuccess();
}
@@ -120,14 +120,14 @@ namespace ams::os::impl {
/* Continuously wait, until success. */
R_TRY_CATCH(svcWaitSynchronizationSingle(handle, U64_MAX)) {
R_CATCH(svc::ResultCancelled) { continue; }
- } R_END_TRY_CATCH_WITH_ASSERT;
+ } R_END_TRY_CATCH_WITH_ABORT_UNLESS;
/* Clear, if we must. */
if (this->auto_clear) {
R_TRY_CATCH(svcResetSignal(handle)) {
/* Some other thread might have caught this before we did. */
R_CATCH(svc::ResultInvalidState) { continue; }
- } R_END_TRY_CATCH_WITH_ASSERT;
+ } R_END_TRY_CATCH_WITH_ABORT_UNLESS;
}
return;
}
@@ -146,7 +146,7 @@ namespace ams::os::impl {
R_TRY_CATCH(svcWaitSynchronizationSingle(handle, 0)) {
R_CATCH(svc::ResultTimedOut) { return false; }
R_CATCH(svc::ResultCancelled) { continue; }
- } R_END_TRY_CATCH_WITH_ASSERT;
+ } R_END_TRY_CATCH_WITH_ABORT_UNLESS;
/* We succeeded, so we're signaled. */
return true;
@@ -163,14 +163,14 @@ namespace ams::os::impl {
R_TRY_CATCH(svcWaitSynchronizationSingle(handle, timeout_helper.NsUntilTimeout())) {
R_CATCH(svc::ResultTimedOut) { return false; }
R_CATCH(svc::ResultCancelled) { continue; }
- } R_END_TRY_CATCH_WITH_ASSERT;
+ } R_END_TRY_CATCH_WITH_ABORT_UNLESS;
/* Clear, if we must. */
if (this->auto_clear) {
R_TRY_CATCH(svcResetSignal(handle)) {
/* Some other thread might have caught this before we did. */
R_CATCH(svc::ResultInvalidState) { continue; }
- } R_END_TRY_CATCH_WITH_ASSERT;
+ } R_END_TRY_CATCH_WITH_ABORT_UNLESS;
}
return true;
diff --git a/libraries/libstratosphere/source/os/impl/os_waitable_manager_impl.cpp b/libraries/libstratosphere/source/os/impl/os_waitable_manager_impl.cpp
index 5f9b73b5e..2f66b7c0e 100644
--- a/libraries/libstratosphere/source/os/impl/os_waitable_manager_impl.cpp
+++ b/libraries/libstratosphere/source/os/impl/os_waitable_manager_impl.cpp
@@ -105,7 +105,7 @@ namespace ams::os::impl{
/* svc::ResultInvalidHandle. */
/* svc::ResultInvalidPointer */
/* svc::ResultOutOfRange */
- } R_END_TRY_CATCH_WITH_ASSERT;
+ } R_END_TRY_CATCH_WITH_ABORT_UNLESS;
return index;
}
diff --git a/libraries/libstratosphere/source/os/os_interrupt_event.cpp b/libraries/libstratosphere/source/os/os_interrupt_event.cpp
index efdee1a75..e700107c6 100644
--- a/libraries/libstratosphere/source/os/os_interrupt_event.cpp
+++ b/libraries/libstratosphere/source/os/os_interrupt_event.cpp
@@ -51,14 +51,14 @@ namespace ams::os {
/* Continuously wait, until success. */
R_TRY_CATCH(svcWaitSynchronizationSingle(this->handle.Get(), U64_MAX)) {
R_CATCH(svc::ResultCancelled) { continue; }
- } R_END_TRY_CATCH_WITH_ASSERT;
+ } R_END_TRY_CATCH_WITH_ABORT_UNLESS;
/* Clear, if we must. */
if (this->auto_clear) {
R_TRY_CATCH(svcResetSignal(this->handle.Get())) {
/* Some other thread might have caught this before we did. */
R_CATCH(svc::ResultInvalidState) { continue; }
- } R_END_TRY_CATCH_WITH_ASSERT;
+ } R_END_TRY_CATCH_WITH_ABORT_UNLESS;
}
return;
}
@@ -77,7 +77,7 @@ namespace ams::os {
R_TRY_CATCH(svcWaitSynchronizationSingle(this->handle.Get(), 0)) {
R_CATCH(svc::ResultTimedOut) { return false; }
R_CATCH(svc::ResultCancelled) { continue; }
- } R_END_TRY_CATCH_WITH_ASSERT;
+ } R_END_TRY_CATCH_WITH_ABORT_UNLESS;
/* We succeeded, so we're signaled. */
return true;
@@ -94,14 +94,14 @@ namespace ams::os {
R_TRY_CATCH(svcWaitSynchronizationSingle(this->handle.Get(), timeout_helper.NsUntilTimeout())) {
R_CATCH(svc::ResultTimedOut) { return false; }
R_CATCH(svc::ResultCancelled) { continue; }
- } R_END_TRY_CATCH_WITH_ASSERT;
+ } R_END_TRY_CATCH_WITH_ABORT_UNLESS;
/* Clear, if we must. */
if (this->auto_clear) {
R_TRY_CATCH(svcResetSignal(this->handle.Get())) {
/* Some other thread might have caught this before we did. */
R_CATCH(svc::ResultInvalidState) { continue; }
- } R_END_TRY_CATCH_WITH_ASSERT;
+ } R_END_TRY_CATCH_WITH_ABORT_UNLESS;
}
return true;
diff --git a/libraries/libstratosphere/source/sf/hipc/sf_hipc_server_manager.cpp b/libraries/libstratosphere/source/sf/hipc/sf_hipc_server_manager.cpp
index 1c6aef2e2..a3de70bad 100644
--- a/libraries/libstratosphere/source/sf/hipc/sf_hipc_server_manager.cpp
+++ b/libraries/libstratosphere/source/sf/hipc/sf_hipc_server_manager.cpp
@@ -167,7 +167,7 @@ namespace ams::sf::hipc {
needs_undefer_all = true;
continue;
}
- } R_END_TRY_CATCH_WITH_ASSERT;
+ } R_END_TRY_CATCH_WITH_ABORT_UNLESS;
/* We succeeded! Remove from deferred list. */
it = this->deferred_session_list.erase(it);
diff --git a/libraries/libstratosphere/source/util/util_uuid_api.cpp b/libraries/libstratosphere/source/util/util_uuid_api.cpp
new file mode 100644
index 000000000..b7f031959
--- /dev/null
+++ b/libraries/libstratosphere/source/util/util_uuid_api.cpp
@@ -0,0 +1,111 @@
+/*
+ * 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
+
+namespace ams::util {
+
+ namespace {
+
+ struct UuidImpl {
+ util::BitPack32 data[4];
+
+ using TimeLow = util::BitPack32::Field<0, BITSIZEOF(u32), u32>;
+
+ using TimeMid = util::BitPack32::Field<0, BITSIZEOF(u16), u16>;
+ using TimeHighAndVersion = util::BitPack32::Field;
+ using Version = util::BitPack32::Field;
+
+ static_assert(TimeHighAndVersion::Next == Version::Next);
+
+ using ClockSeqHiAndReserved = util::BitPack32::Field<0, BITSIZEOF(u8), u8>;
+ using Reserved = util::BitPack32::Field<6, 2, u8>;
+ using ClockSeqLow = util::BitPack32::Field;
+ using NodeLow = util::BitPack32::Field;
+
+ static_assert(ClockSeqHiAndReserved::Next == Reserved::Next);
+
+ using NodeHigh = util::BitPack32::Field<0, BITSIZEOF(u32), u32>;
+
+
+ inline Uuid Convert() const {
+ /* Convert the fields from native endian to big endian. */
+ util::BitPack32 converted[4] = {util::BitPack32(0), util::BitPack32(0), util::BitPack32(0), util::BitPack32(0)};
+
+ converted[0].Set(util::ConvertToBigEndian(this->data[0].Get()));
+
+ converted[1].Set(util::ConvertToBigEndian(this->data[1].Get()));
+ converted[1].Set(util::ConvertToBigEndian(this->data[1].Get()));
+
+
+ converted[2].Set(util::ConvertToBigEndian(this->data[2].Get()));
+ converted[2].Set(util::ConvertToBigEndian(this->data[2].Get()));
+
+ u64 node_lo = static_cast(this->data[2].Get());
+ u64 node_hi = static_cast(this->data[3].Get());
+ u64 node = util::ConvertToBigEndian48(static_cast((node_hi << BITSIZEOF(u16)) | (node_lo)));
+
+ constexpr u64 NodeLoMask = (UINT64_C(1) << BITSIZEOF(u16)) - 1u;
+ constexpr u64 NodeHiMask = (UINT64_C(1) << BITSIZEOF(u32)) - 1u;
+
+ converted[2].Set(static_cast(node & NodeLoMask));
+ converted[3].Set(static_cast((node >> BITSIZEOF(u16)) & NodeHiMask));
+
+ Uuid uuid;
+ std::memcpy(uuid.data, converted, sizeof(uuid.data));
+ return uuid;
+ }
+ };
+ static_assert(sizeof(UuidImpl) == sizeof(Uuid));
+
+ ALWAYS_INLINE Uuid GenerateUuidVersion4() {
+ constexpr u16 Version = 0x4;
+ constexpr u8 Reserved = 0x1;
+
+ /* Generate a random uuid. */
+ UuidImpl uuid = {util::BitPack32(0), util::BitPack32(0), util::BitPack32(0), util::BitPack32(0)};
+ os::GenerateRandomBytes(uuid.data, sizeof(uuid.data));
+
+ /* Set version and reserved. */
+ uuid.data[1].Set(Version);
+ uuid.data[2].Set(Reserved);
+
+ /* Return the uuid. */
+ return uuid.Convert();
+ }
+
+ }
+
+ Uuid GenerateUuid() {
+ return GenerateUuidVersion4();
+ }
+
+ Uuid GenerateUuidVersion5(const void *sha1_hash) {
+ constexpr u16 Version = 0x5;
+ constexpr u8 Reserved = 0x1;
+
+ /* Generate a uuid from a SHA1 hash. */
+ UuidImpl uuid = {util::BitPack32(0), util::BitPack32(0), util::BitPack32(0), util::BitPack32(0)};
+ std::memcpy(uuid.data, sha1_hash, sizeof(uuid.data));
+
+ /* Set version and reserved. */
+ uuid.data[1].Set(Version);
+ uuid.data[2].Set(Reserved);
+
+ /* Return the uuid. */
+ return uuid.Convert();
+ }
+
+}
diff --git a/libraries/libvapours/include/vapours/crypto.hpp b/libraries/libvapours/include/vapours/crypto.hpp
index 7e90f8ce1..8dc5ecb36 100644
--- a/libraries/libvapours/include/vapours/crypto.hpp
+++ b/libraries/libvapours/include/vapours/crypto.hpp
@@ -21,3 +21,5 @@
#include
#include
#include
+#include
+#include
diff --git a/libraries/libvapours/include/vapours/crypto/crypto_rsa_oaep_decryptor.hpp b/libraries/libvapours/include/vapours/crypto/crypto_rsa_oaep_decryptor.hpp
new file mode 100644
index 000000000..98280c44e
--- /dev/null
+++ b/libraries/libvapours/include/vapours/crypto/crypto_rsa_oaep_decryptor.hpp
@@ -0,0 +1,139 @@
+/*
+ * 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
+#include
+#include
+
+namespace ams::crypto {
+
+ template /* requires HashFunction */
+ class RsaOaepDecryptor {
+ NON_COPYABLE(RsaOaepDecryptor);
+ NON_MOVEABLE(RsaOaepDecryptor);
+ public:
+ static constexpr size_t HashSize = Hash::HashSize;
+ static constexpr size_t BlockSize = ModulusSize;
+ static constexpr size_t MaximumExponentSize = ModulusSize;
+ static constexpr size_t RequiredWorkBufferSize = RsaCalculator::RequiredWorkBufferSize;
+ private:
+ enum class State {
+ None,
+ Initialized,
+ Done,
+ };
+ private:
+ RsaCalculator calculator;
+ Hash hash;
+ bool set_label_digest;
+ u8 label_digest[HashSize];
+ State state;
+ public:
+ RsaOaepDecryptor() : set_label_digest(false), state(State::None) { /* ... */ }
+
+ ~RsaOaepDecryptor() {
+ ClearMemory(this->label_digest, sizeof(this->label_digest));
+ }
+
+ bool Initialize(const void *mod, size_t mod_size, const void *exp, size_t exp_size) {
+ this->hash.Initialize();
+ this->set_label_digest = false;
+ if (this->calculator.Initialize(mod, mod_size, exp, exp_size)) {
+ this->state = State::Initialized;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ void UpdateLabel(const void *data, size_t size) {
+ AMS_ASSERT(this->state == State::Initialized);
+
+ this->hash.Update(data, size);
+ }
+
+ void SetLabelDigest(const void *digest, size_t digest_size) {
+ AMS_ASSERT(this->state == State::Initialized);
+ AMS_ABORT_UNLESS(digest_size == sizeof(this->label_digest));
+
+ std::memcpy(this->label_digest, digest, digest_size);
+ this->set_label_digest = true;
+ }
+
+ size_t Decrypt(void *dst, size_t dst_size, const void *src, size_t src_size) {
+ AMS_ASSERT(this->state == State::Initialized);
+ ON_SCOPE_EXIT { this->state = State::Done; };
+
+ impl::RsaOaepImpl impl;
+ u8 message[BlockSize];
+ ON_SCOPE_EXIT { ClearMemory(message, sizeof(message)); };
+
+ if (!this->calculator.ExpMod(message, src, src_size)) {
+ return false;
+ }
+
+ if (!this->set_label_digest) {
+ this->hash.GetHash(this->label_digest, sizeof(this->label_digest));
+ this->set_label_digest = true;
+ }
+
+ return impl.Decode(dst, dst_size, this->label_digest, sizeof(this->label_digest), message, sizeof(message));
+ }
+
+ size_t Decrypt(void *dst, size_t dst_size, const void *src, size_t src_size, void *work_buf, size_t work_buf_size) {
+ AMS_ASSERT(this->state == State::Initialized);
+ ON_SCOPE_EXIT { this->state = State::Done; };
+
+ impl::RsaOaepImpl impl;
+ u8 message[BlockSize];
+ ON_SCOPE_EXIT { ClearMemory(message, sizeof(message)); };
+
+ if (!this->calculator.ExpMod(message, src, src_size, work_buf, work_buf_size)) {
+ return false;
+ }
+
+ if (!this->set_label_digest) {
+ this->hash.GetHash(this->label_digest, sizeof(this->label_digest));
+ this->set_label_digest = true;
+ }
+
+ return impl.Decode(dst, dst_size, this->label_digest, sizeof(this->label_digest), message, sizeof(message));
+ }
+
+ static size_t Decrypt(void *dst, size_t dst_size, const void *mod, size_t mod_size, const void *exp, size_t exp_size, const void *msg, size_t msg_size, const void *lab, size_t lab_size) {
+ RsaOaepDecryptor crypt;
+ if (!crypt.Initialize(mod, mod_size, exp, exp_size)) {
+ return 0;
+ }
+ crypt.UpdateLabel(lab, lab_size);
+ return crypt.Decrypt(dst, dst_size, msg, msg_size);
+ }
+
+ static size_t Decrypt(void *dst, size_t dst_size, const void *mod, size_t mod_size, const void *exp, size_t exp_size, const void *msg, size_t msg_size, const void *lab, size_t lab_size, void *work_buf, size_t work_buf_size) {
+ RsaOaepDecryptor crypt;
+ if (!crypt.Initialize(mod, mod_size, exp, exp_size)) {
+ return 0;
+ }
+ crypt.UpdateLabel(lab, lab_size);
+ return crypt.Decrypt(dst, dst_size, msg, msg_size, work_buf, work_buf_size);
+ }
+
+ };
+
+}
diff --git a/libraries/libvapours/include/vapours/crypto/crypto_rsa_oaep_sha256_decoder.hpp b/libraries/libvapours/include/vapours/crypto/crypto_rsa_oaep_sha256_decoder.hpp
new file mode 100644
index 000000000..fe33d20c8
--- /dev/null
+++ b/libraries/libvapours/include/vapours/crypto/crypto_rsa_oaep_sha256_decoder.hpp
@@ -0,0 +1,50 @@
+/*
+ * 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
+#include
+#include
+
+namespace ams::crypto {
+
+ inline size_t DecodeRsa2048OaepSha256(void *dst, size_t dst_size, const void *label_digest, size_t label_digest_size, const void *src, size_t src_size) {
+ constexpr size_t BlockSize = 2048 / BITSIZEOF(u8);
+ AMS_ABORT_UNLESS(src_size == BlockSize);
+
+ impl::RsaOaepImpl oaep;
+ u8 enc[BlockSize];
+ ON_SCOPE_EXIT { ClearMemory(enc, sizeof(enc)); };
+
+ std::memcpy(enc, src, src_size);
+ return oaep.Decode(dst, dst_size, label_digest, label_digest_size, enc, sizeof(enc));
+ }
+
+ inline size_t DecodeRsa4096OaepSha256(void *dst, size_t dst_size, const void *label_digest, size_t label_digest_size, const void *src, size_t src_size) {
+ constexpr size_t BlockSize = 4096 / BITSIZEOF(u8);
+ AMS_ABORT_UNLESS(src_size == BlockSize);
+
+ impl::RsaOaepImpl oaep;
+ u8 enc[BlockSize];
+ ON_SCOPE_EXIT { ClearMemory(enc, sizeof(enc)); };
+
+ std::memcpy(enc, src, src_size);
+ return oaep.Decode(dst, dst_size, label_digest, label_digest_size, enc, sizeof(enc));
+ }
+
+}
diff --git a/libraries/libvapours/include/vapours/crypto/crypto_rsa_oaep_sha256_decryptor.hpp b/libraries/libvapours/include/vapours/crypto/crypto_rsa_oaep_sha256_decryptor.hpp
new file mode 100644
index 000000000..f26239bc6
--- /dev/null
+++ b/libraries/libvapours/include/vapours/crypto/crypto_rsa_oaep_sha256_decryptor.hpp
@@ -0,0 +1,53 @@
+/*
+ * 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
+#include
+#include
+#include
+
+namespace ams::crypto {
+
+ namespace impl {
+
+ template
+ using RsaNOaepSha256Decryptor = ::ams::crypto::RsaOaepDecryptor;
+
+ }
+
+ using Rsa2048OaepSha256Decryptor = ::ams::crypto::impl::RsaNOaepSha256Decryptor<2048>;
+ using Rsa4096OaepSha256Decryptor = ::ams::crypto::impl::RsaNOaepSha256Decryptor<4096>;
+
+ inline size_t DecryptRsa2048OaepSha256(void *dst, size_t dst_size, const void *mod, size_t mod_size, const void *exp, size_t exp_size, const void *msg, size_t msg_size, const void *lab, size_t lab_size) {
+ return Rsa2048OaepSha256Decryptor::Decrypt(dst, dst_size, mod, mod_size, exp, exp_size, msg, msg_size, lab, lab_size);
+ }
+
+ inline size_t DecryptRsa2048OaepSha256(void *dst, size_t dst_size, const void *mod, size_t mod_size, const void *exp, size_t exp_size, const void *msg, size_t msg_size, const void *lab, size_t lab_size, void *work_buf, size_t work_buf_size) {
+ return Rsa2048OaepSha256Decryptor::Decrypt(dst, dst_size, mod, mod_size, exp, exp_size, msg, msg_size, lab, lab_size, work_buf, work_buf_size);
+ }
+
+ inline size_t DecryptRsa4096OaepSha256(void *dst, size_t dst_size, const void *mod, size_t mod_size, const void *exp, size_t exp_size, const void *msg, size_t msg_size, const void *lab, size_t lab_size) {
+ return Rsa4096OaepSha256Decryptor::Decrypt(dst, dst_size, mod, mod_size, exp, exp_size, msg, msg_size, lab, lab_size);
+ }
+
+ inline size_t DecryptRsa4096OaepSha256(void *dst, size_t dst_size, const void *mod, size_t mod_size, const void *exp, size_t exp_size, const void *msg, size_t msg_size, const void *lab, size_t lab_size, void *work_buf, size_t work_buf_size) {
+ return Rsa4096OaepSha256Decryptor::Decrypt(dst, dst_size, mod, mod_size, exp, exp_size, msg, msg_size, lab, lab_size, work_buf, work_buf_size);
+ }
+
+}
diff --git a/libraries/libvapours/include/vapours/crypto/impl/crypto_rsa_oaep_impl.hpp b/libraries/libvapours/include/vapours/crypto/impl/crypto_rsa_oaep_impl.hpp
new file mode 100644
index 000000000..bfe6afce5
--- /dev/null
+++ b/libraries/libvapours/include/vapours/crypto/impl/crypto_rsa_oaep_impl.hpp
@@ -0,0 +1,128 @@
+/*
+ * 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
+#include
+
+namespace ams::crypto::impl {
+
+ template /* requires HashFunction */
+ class RsaOaepImpl {
+ NON_COPYABLE(RsaOaepImpl);
+ NON_MOVEABLE(RsaOaepImpl);
+ public:
+ static constexpr size_t HashSize = Hash::HashSize;
+ private:
+ static constexpr u8 HeadMagic = 0x00;
+ private:
+ static void ComputeHashWithPadding(void *dst, Hash *hash, const void *salt, size_t salt_size) {
+ /* Initialize our buffer. */
+ u8 buf[8 + HashSize];
+ std::memset(buf, 0, 8);
+ hash->GetHash(buf + 8, HashSize);
+ ON_SCOPE_EXIT { ClearMemory(buf, sizeof(buf)); };
+
+
+ /* Calculate our hash. */
+ hash->Initialize();
+ hash->Update(buf, sizeof(buf));
+ hash->Update(salt, salt_size);
+ hash->GetHash(dst, HashSize);
+ }
+
+ static void ApplyMGF1(u8 *dst, size_t dst_size, const void *src, size_t src_size) {
+ u8 buf[HashSize];
+ ON_SCOPE_EXIT { ClearMemory(buf, sizeof(buf)); };
+
+ const size_t required_iters = (dst_size + HashSize - 1) / HashSize;
+ for (size_t i = 0; i < required_iters; i++) {
+ Hash hash;
+ hash.Initialize();
+ hash.Update(src, src_size);
+
+ const u32 tmp = util::ConvertToBigEndian(static_cast(i));
+ hash.Update(std::addressof(tmp), sizeof(tmp));
+
+ hash.GetHash(buf, HashSize);
+
+ const size_t start = HashSize * i;
+ const size_t end = std::min(dst_size, start + HashSize);
+ for (size_t j = start; j < end; j++) {
+ dst[j] ^= buf[j - start];
+ }
+ }
+ }
+ public:
+ RsaOaepImpl() { /* ... */ }
+
+ size_t Decode(void *dst, size_t dst_size, const void *label_digest, size_t label_digest_size, u8 *buf, size_t buf_size) {
+ /* Check our preconditions. */
+ AMS_ABORT_UNLESS(dst_size > 0);
+ AMS_ABORT_UNLESS(buf_size >= 2 * HashSize + 3);
+ AMS_ABORT_UNLESS(label_digest_size == HashSize);
+
+ /* Validate sanity byte. */
+ bool is_valid = buf[0] == HeadMagic;
+
+ /* Decrypt seed and masked db. */
+ size_t db_len = buf_size - HashSize - 1;
+ u8 *seed = buf + 1;
+ u8 *db = seed + HashSize;
+ ApplyMGF1(seed, HashSize, db, db_len);
+ ApplyMGF1(db, db_len, seed, HashSize);
+
+ /* Check the label digest. */
+ is_valid &= IsSameBytes(label_digest, db, HashSize);
+
+ /* Skip past the label digest. */
+ db += HashSize;
+ db_len -= HashSize;
+
+ /* Verify that DB is of the form 0000...0001 < message > */
+ s32 msg_ofs = 0;
+ {
+ int looking_for_one = 1;
+ int invalid_db_padding = 0;
+ int is_zero;
+ int is_one;
+ for (size_t i = 0; i < db_len; /* ... */) {
+ is_zero = (db[i] == 0);
+ is_one = (db[i] == 1);
+ msg_ofs += (looking_for_one & is_one) * (static_cast(++i));
+ looking_for_one &= ~is_one;
+ invalid_db_padding |= (looking_for_one & ~is_zero);
+ }
+
+ is_valid &= (invalid_db_padding == 0);
+ }
+
+ /* If we're invalid, return zero size. */
+ const size_t valid_msg_size = db_len - msg_ofs;
+ const size_t msg_size = std::min(dst_size, static_cast(is_valid) * valid_msg_size);
+
+ /* Copy to output. */
+ std::memcpy(dst, db + msg_ofs, msg_size);
+
+ /* Return copied size. */
+ return msg_size;
+ }
+
+ };
+
+}
diff --git a/libraries/libvapours/include/vapours/crypto/impl/crypto_rsa_pss_impl.hpp b/libraries/libvapours/include/vapours/crypto/impl/crypto_rsa_pss_impl.hpp
index 5a17ff4e5..fc0af1041 100644
--- a/libraries/libvapours/include/vapours/crypto/impl/crypto_rsa_pss_impl.hpp
+++ b/libraries/libvapours/include/vapours/crypto/impl/crypto_rsa_pss_impl.hpp
@@ -73,9 +73,7 @@ namespace ams::crypto::impl {
bool Verify(u8 *buf, size_t size, Hash *hash) {
/* Validate sanity byte. */
- if (buf[size - 1] != TailMagic) {
- return false;
- }
+ bool is_valid = buf[size - 1] == TailMagic;
/* Decrypt maskedDB */
const size_t db_len = size - HashSize - 1;
@@ -87,33 +85,38 @@ namespace ams::crypto::impl {
db[0] &= 0x7F;
/* Verify that DB is of the form 0000...0001 */
- s32 salt_ofs = -1;
- for (size_t i = 0; i < db_len; i++) {
- if (db[i] != 0) {
- salt_ofs = static_cast(i) + 1;
- break;
+ s32 salt_ofs = 0;
+ {
+ int looking_for_one = 1;
+ int invalid_db_padding = 0;
+ int is_zero;
+ int is_one;
+ for (size_t i = 0; i < db_len; /* ... */) {
+ is_zero = (db[i] == 0);
+ is_one = (db[i] == 1);
+ salt_ofs += (looking_for_one & is_one) * (static_cast(++i));
+ looking_for_one &= ~is_one;
+ invalid_db_padding |= (looking_for_one & ~is_zero);
}
- }
- if (salt_ofs == -1) {
- return false;
- }
- if (db[salt_ofs - 1] != 1) {
- return false;
+
+ is_valid &= (invalid_db_padding == 0);
}
/* Verify salt. */
const u8 *salt = db + salt_ofs;
const size_t salt_size = db_len - salt_ofs;
- if (salt_size == 0) {
- return false;
- }
+ is_valid &= (salt_size != 0);
+ is_valid &= (salt_size != db_len);
/* Verify hash. */
u8 cmp_hash[HashSize];
ON_SCOPE_EXIT { ClearMemory(cmp_hash, sizeof(cmp_hash)); };
ComputeHashWithPadding(cmp_hash, hash, salt, salt_size);
- return IsSameBytes(cmp_hash, h, HashSize);
+ is_valid &= IsSameBytes(cmp_hash, h, HashSize);
+
+ /* Succeed if all our checks succeeded. */
+ return is_valid;
}
};
diff --git a/libraries/libvapours/include/vapours/svc/svc_types_common.hpp b/libraries/libvapours/include/vapours/svc/svc_types_common.hpp
index 14a4db6ce..5ba4d3911 100644
--- a/libraries/libvapours/include/vapours/svc/svc_types_common.hpp
+++ b/libraries/libvapours/include/vapours/svc/svc_types_common.hpp
@@ -58,6 +58,8 @@ namespace ams::svc {
static constexpr bool IsInput = std::is_const::type>::value;
private:
T pointer;
+ public:
+ constexpr ALWAYS_INLINE UserPointer(T p) : pointer(p) { /* ... */ }
};
template
diff --git a/libraries/libvapours/include/vapours/util.hpp b/libraries/libvapours/include/vapours/util.hpp
index 37c7ae0e5..b1d000fea 100644
--- a/libraries/libvapours/include/vapours/util.hpp
+++ b/libraries/libvapours/include/vapours/util.hpp
@@ -21,14 +21,14 @@
#include
#include
#include
-#include
-#include
-#include
#include
#include
+#include
+#include
+#include
#include
+#include
#include
#include
#include
-#include
#include
diff --git a/libraries/libvapours/include/vapours/util/util_endian.hpp b/libraries/libvapours/include/vapours/util/util_endian.hpp
index c91b72558..fcf294baf 100644
--- a/libraries/libvapours/include/vapours/util/util_endian.hpp
+++ b/libraries/libvapours/include/vapours/util/util_endian.hpp
@@ -65,12 +65,26 @@ namespace ams::util {
((u & (ByteMask << 0)) << 8);
} else if constexpr (std::is_same::value) {
+ AMS_UNUSED(ByteMask);
return u;
} else {
static_assert(!std::is_same::value);
}
}
+ constexpr ALWAYS_INLINE u64 SwapBytes48(const u64 u) {
+ using U = u64;
+ static_assert(BITSIZEOF(u8) == 8);
+ constexpr U ByteMask = 0xFFu;
+ AMS_ASSERT((u & UINT64_C(0xFFFF000000000000)) == 0);
+ return ((u & (ByteMask << 40)) >> 40) |
+ ((u & (ByteMask << 32)) >> 24) |
+ ((u & (ByteMask << 24)) >> 8) |
+ ((u & (ByteMask << 16)) << 8) |
+ ((u & (ByteMask << 8)) << 24) |
+ ((u & (ByteMask << 0)) << 40);
+ }
+
template /* requires integral */
constexpr ALWAYS_INLINE void SwapBytes(T *ptr) {
using U = typename std::make_unsigned::type;
@@ -78,23 +92,55 @@ namespace ams::util {
*ptr = static_cast(SwapBytes(static_cast(*ptr)));
}
- template
+ template /* requires integral */
constexpr ALWAYS_INLINE T ConvertToBigEndian(const T val) {
+ using U = typename std::make_unsigned::type;
+
if constexpr (IsBigEndian()) {
- return val;
+ return static_cast(static_cast(val));
} else {
static_assert(IsLittleEndian());
- return SwapBytes(val);
+ return static_cast(SwapBytes(static_cast(val)));
}
}
- template
+ template /* requires integral */
constexpr ALWAYS_INLINE T ConvertToLittleEndian(const T val) {
+ using U = typename std::make_unsigned::type;
+
if constexpr (IsBigEndian()) {
- return SwapBytes(val);
+ return static_cast(SwapBytes(static_cast(val)));
} else {
static_assert(IsLittleEndian());
- return val;
+ return static_cast(static_cast(val));
+ }
+ }
+
+ template /* requires integral */
+ constexpr ALWAYS_INLINE T ConvertToBigEndian48(const T val) {
+ using U = typename std::make_unsigned::type;
+ static_assert(sizeof(T) == sizeof(u64));
+
+ if constexpr (IsBigEndian()) {
+ AMS_ASSERT((static_cast(val) & UINT64_C(0xFFFF000000000000)) == 0);
+ return static_cast(static_cast(val));
+ } else {
+ static_assert(IsLittleEndian());
+ return static_cast(SwapBytes48(static_cast(val)));
+ }
+ }
+
+ template /* requires integral */
+ constexpr ALWAYS_INLINE T ConvertToLittleEndian48(const T val) {
+ using U = typename std::make_unsigned::type;
+ static_assert(sizeof(T) == sizeof(u64));
+
+ if constexpr (IsBigEndian()) {
+ return static_cast(SwapBytes48(static_cast(val)));
+ } else {
+ static_assert(IsLittleEndian());
+ AMS_ASSERT((static_cast(val) & UINT64_C(0xFFFF000000000000)) == 0);
+ return static_cast(static_cast(val));
}
}
diff --git a/libraries/libvapours/include/vapours/util/util_uuid.hpp b/libraries/libvapours/include/vapours/util/util_uuid.hpp
index d3f19a955..c7ce3ef74 100644
--- a/libraries/libvapours/include/vapours/util/util_uuid.hpp
+++ b/libraries/libvapours/include/vapours/util/util_uuid.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018-2019 Atmosphère-NX
+ * 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,
@@ -21,23 +21,21 @@
namespace ams::util {
struct Uuid {
- u8 uuid[0x10];
+ static constexpr size_t Size = 0x10;
- bool operator==(const Uuid& other) const {
- return memcmp(this->uuid, other.uuid, sizeof(Uuid)) == 0;
+ u8 data[Size];
+
+ bool operator==(const Uuid &rhs) const {
+ return std::memcmp(this->data, rhs.data, Size) == 0;
}
- bool operator!=(const Uuid& other) const {
- return !(*this == other);
+ bool operator!=(const Uuid &rhs) const {
+ return !(*this == rhs);
}
- u8& operator[](size_t i) {
- return uuid[i];
+ u8 operator[](size_t i) const {
+ return this->data[i];
}
};
- static_assert(sizeof(Uuid) == 0x10, "Uuid definition!");
-
- static constexpr Uuid InvalidUuid = { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } };
-
-}
\ No newline at end of file
+}
diff --git a/stratosphere/boot/source/boot_battery_icon_charging.inc b/stratosphere/boot/source/boot_battery_icon_charging.inc
index 8a65d061a..4e41e9f49 100644
--- a/stratosphere/boot/source/boot_battery_icon_charging.inc
+++ b/stratosphere/boot/source/boot_battery_icon_charging.inc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018-2019 Atmosphère-NX
+ * 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,
diff --git a/stratosphere/boot/source/boot_battery_icon_charging_red.inc b/stratosphere/boot/source/boot_battery_icon_charging_red.inc
index 7db585004..77e697354 100644
--- a/stratosphere/boot/source/boot_battery_icon_charging_red.inc
+++ b/stratosphere/boot/source/boot_battery_icon_charging_red.inc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018-2019 Atmosphère-NX
+ * 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,
diff --git a/stratosphere/boot/source/boot_battery_icon_low.inc b/stratosphere/boot/source/boot_battery_icon_low.inc
index 9963a03d7..8b78331e6 100644
--- a/stratosphere/boot/source/boot_battery_icon_low.inc
+++ b/stratosphere/boot/source/boot_battery_icon_low.inc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018-2019 Atmosphère-NX
+ * 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,
diff --git a/stratosphere/boot/source/boot_battery_parameters.inc b/stratosphere/boot/source/boot_battery_parameters.inc
index 516ff74d9..fd7ca3586 100644
--- a/stratosphere/boot/source/boot_battery_parameters.inc
+++ b/stratosphere/boot/source/boot_battery_parameters.inc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018-2019 Atmosphère-NX
+ * 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,
diff --git a/stratosphere/boot/source/boot_display_config.inc b/stratosphere/boot/source/boot_display_config.inc
index de0ed33f5..246b8eea8 100644
--- a/stratosphere/boot/source/boot_display_config.inc
+++ b/stratosphere/boot/source/boot_display_config.inc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018-2019 Atmosphère-NX
+ * 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,
diff --git a/stratosphere/boot/source/boot_main.cpp b/stratosphere/boot/source/boot_main.cpp
index d14059023..6494f8857 100644
--- a/stratosphere/boot/source/boot_main.cpp
+++ b/stratosphere/boot/source/boot_main.cpp
@@ -121,7 +121,7 @@ int main(int argc, char **argv)
boot::DetectBootReason();
const auto hw_type = spl::GetHardwareType();
- if (hw_type != spl::HardwareType::Copper) {
+ if (hw_type != spl::HardwareType::Copper && hw_type != spl::HardwareType::Calcio) {
/* Display splash screen for two seconds. */
boot::ShowSplashScreen();
@@ -136,7 +136,7 @@ int main(int argc, char **argv)
boot::SetInitialWakePinConfiguration();
/* Configure output clock. */
- if (hw_type != spl::HardwareType::Copper) {
+ if (hw_type != spl::HardwareType::Copper && hw_type != spl::HardwareType::Calcio) {
boot::SetInitialClockConfiguration();
}
diff --git a/stratosphere/boot/source/boot_pmic_driver.cpp b/stratosphere/boot/source/boot_pmic_driver.cpp
index 4b390301b..846bc09c1 100644
--- a/stratosphere/boot/source/boot_pmic_driver.cpp
+++ b/stratosphere/boot/source/boot_pmic_driver.cpp
@@ -74,8 +74,8 @@ namespace ams::boot {
R_ABORT_UNLESS(ReadI2cRegister(this->i2c_session, &on_off_1_val, sizeof(on_off_1_val), &on_off_1_addr, sizeof(on_off_1_addr)));
on_off_1_val |= 0x80;
- /* Finalize the battery. */
- {
+ /* Finalize the battery on non-Calcio. */
+ if (spl::GetHardwareType() != spl::HardwareType::Calcio) {
BatteryDriver battery_driver;
this->FinalizeBattery(&battery_driver);
}
@@ -98,6 +98,15 @@ namespace ams::boot {
return;
}
+ /* On Hoag, we don't want to use the desired shutdown value when battery charged. */
+ bool use_desired_shutdown = true;
+ if (spl::GetHardwareType() == spl::HardwareType::Hoag) {
+ double battery_charge;
+ if (R_FAILED(battery_driver->GetSocRep(&battery_charge)) || battery_charge >= 80.0) {
+ use_desired_shutdown = false;
+ }
+ }
+
bool ac_ok;
bool desired_shutdown_enabled;
if (R_FAILED(this->GetAcOk(&ac_ok)) || ac_ok) {
@@ -106,6 +115,8 @@ namespace ams::boot {
desired_shutdown_enabled = true;
}
+ desired_shutdown_enabled &= use_desired_shutdown;
+
if (shutdown_enabled != desired_shutdown_enabled) {
battery_driver->SetShutdownEnabled(desired_shutdown_enabled);
}
diff --git a/stratosphere/boot/source/boot_splash_screen_notext.inc b/stratosphere/boot/source/boot_splash_screen_notext.inc
index f0b87d59e..007073856 100644
--- a/stratosphere/boot/source/boot_splash_screen_notext.inc
+++ b/stratosphere/boot/source/boot_splash_screen_notext.inc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018-2019 Atmosphère-NX
+ * 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,
diff --git a/stratosphere/boot/source/boot_splash_screen_text.inc b/stratosphere/boot/source/boot_splash_screen_text.inc
index ed9e84f27..01eba1e6f 100644
--- a/stratosphere/boot/source/boot_splash_screen_text.inc
+++ b/stratosphere/boot/source/boot_splash_screen_text.inc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018-2019 Atmosphère-NX
+ * 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,
diff --git a/stratosphere/boot/source/boot_wake_control_configs.inc b/stratosphere/boot/source/boot_wake_control_configs.inc
index 0761002c1..8bf77fa9e 100644
--- a/stratosphere/boot/source/boot_wake_control_configs.inc
+++ b/stratosphere/boot/source/boot_wake_control_configs.inc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018-2019 Atmosphère-NX
+ * 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,
@@ -32,4 +32,4 @@ constexpr WakeControlConfig WakeControlConfigs[] = {
{APBDEV_PMC_CNTRL2, 0x0001, true},
};
-constexpr size_t NumWakeControlConfigs = sizeof(WakeControlConfigs) / sizeof(WakeControlConfigs[0]);
+constexpr size_t NumWakeControlConfigs = util::size(WakeControlConfigs);
diff --git a/stratosphere/boot/source/boot_wake_pin_configuration_calcio.inc b/stratosphere/boot/source/boot_wake_pin_configuration_calcio.inc
new file mode 100644
index 000000000..abeb3e6c5
--- /dev/null
+++ b/stratosphere/boot/source/boot_wake_pin_configuration_calcio.inc
@@ -0,0 +1,69 @@
+/*
+ * 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 .
+ */
+
+static constexpr WakePinConfig WakePinConfigsCalcio[] = {
+ {0x00, false, 0x02},
+ {0x01, false, 0x02},
+ {0x02, false, 0x02},
+ {0x03, false, 0x02},
+ {0x04, false, 0x02},
+ {0x05, false, 0x02},
+ {0x06, false, 0x02},
+ {0x07, false, 0x02},
+ {0x08, true, 0x01},
+ {0x0A, false, 0x02},
+ {0x0B, false, 0x02},
+ {0x0C, false, 0x02},
+ {0x0D, false, 0x02},
+ {0x0E, true, 0x00},
+ {0x0F, false, 0x02},
+ {0x11, false, 0x02},
+ {0x12, false, 0x02},
+ {0x13, false, 0x02},
+ {0x14, false, 0x02},
+ {0x15, false, 0x02},
+ {0x16, false, 0x02},
+ {0x17, false, 0x02},
+ {0x18, false, 0x02},
+ {0x19, false, 0x02},
+ {0x1A, false, 0x02},
+ {0x1B, false, 0x00},
+ {0x1C, false, 0x02},
+ {0x21, false, 0x02},
+ {0x22, false, 0x00},
+ {0x23, true, 0x02},
+ {0x24, false, 0x02},
+ {0x2D, false, 0x02},
+ {0x2E, false, 0x02},
+ {0x2F, false, 0x02},
+ {0x30, false, 0x02},
+ {0x31, false, 0x02},
+ {0x32, false, 0x02},
+ {0x33, true, 0x00},
+ {0x34, true, 0x00},
+ {0x35, false, 0x02},
+ {0x36, false, 0x02},
+ {0x37, false, 0x02},
+ {0x38, false, 0x02},
+ {0x39, false, 0x00},
+ {0x3A, false, 0x02},
+ {0x3B, false, 0x02},
+ {0x3D, false, 0x02},
+ {0x3E, false, 0x02},
+ {0x3F, false, 0x02},
+};
+
+static constexpr size_t NumWakePinConfigsCalcio = util::size(WakePinConfigsCalcio);
diff --git a/stratosphere/boot/source/boot_wake_pin_configuration_copper.inc b/stratosphere/boot/source/boot_wake_pin_configuration_copper.inc
index 346f963f1..7105cb052 100644
--- a/stratosphere/boot/source/boot_wake_pin_configuration_copper.inc
+++ b/stratosphere/boot/source/boot_wake_pin_configuration_copper.inc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018-2019 Atmosphère-NX
+ * 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,
@@ -66,4 +66,4 @@ static constexpr WakePinConfig WakePinConfigsCopper[] = {
{0x3F, false, 0x02},
};
-static constexpr size_t NumWakePinConfigsCopper = sizeof(WakePinConfigsCopper) / sizeof(WakePinConfigsCopper[0]);
+static constexpr size_t NumWakePinConfigsCopper = util::size(WakePinConfigsCopper);
diff --git a/stratosphere/boot/source/boot_wake_pin_configuration_hoag.inc b/stratosphere/boot/source/boot_wake_pin_configuration_hoag.inc
new file mode 100644
index 000000000..c199fecd1
--- /dev/null
+++ b/stratosphere/boot/source/boot_wake_pin_configuration_hoag.inc
@@ -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 .
+ */
+
+static constexpr WakePinConfig WakePinConfigsHoag[] = {
+ {0x00, false, 0x02},
+ {0x01, false, 0x02},
+ {0x02, false, 0x02},
+ {0x03, false, 0x02},
+ {0x04, true, 0x02},
+ {0x05, true, 0x02},
+ {0x06, false, 0x02},
+ {0x07, true, 0x02},
+ {0x08, true, 0x01},
+ {0x0A, true, 0x02},
+ {0x0B, false, 0x02},
+ {0x0C, false, 0x02},
+ {0x0D, false, 0x02},
+ {0x0E, true, 0x00},
+ {0x0F, false, 0x02},
+ {0x11, false, 0x02},
+ {0x12, false, 0x02},
+ {0x13, false, 0x02},
+ {0x14, false, 0x02},
+ {0x15, false, 0x02},
+ {0x16, false, 0x02},
+ {0x17, false, 0x02},
+ {0x18, false, 0x02},
+ {0x19, false, 0x02},
+ {0x1A, false, 0x02},
+ {0x1B, true, 0x00},
+ {0x1C, false, 0x02},
+ {0x20, false, 0x02},
+ {0x21, false, 0x02},
+ {0x22, true, 0x00},
+ {0x23, true, 0x02},
+ {0x24, false, 0x02},
+ {0x2D, false, 0x02},
+ {0x2E, false, 0x02},
+ {0x2F, false, 0x02},
+ {0x30, true, 0x02},
+ {0x31, false, 0x02},
+ {0x32, false, 0x02},
+ {0x33, true, 0x00},
+ {0x34, true, 0x00},
+ {0x35, false, 0x02},
+ {0x36, false, 0x02},
+ {0x37, false, 0x02},
+ {0x38, false, 0x02},
+ {0x39, true, 0x00},
+ {0x3A, false, 0x02},
+ {0x3B, false, 0x02},
+ {0x3D, false, 0x02},
+ {0x3E, false, 0x02},
+ {0x3F, false, 0x02},
+};
+
+static constexpr size_t NumWakePinConfigsHoag = util::size(WakePinConfigsHoag);
diff --git a/stratosphere/boot/source/boot_wake_pin_configuration.inc b/stratosphere/boot/source/boot_wake_pin_configuration_icosa.inc
similarity index 89%
rename from stratosphere/boot/source/boot_wake_pin_configuration.inc
rename to stratosphere/boot/source/boot_wake_pin_configuration_icosa.inc
index 71c585f88..ddfb16a6f 100644
--- a/stratosphere/boot/source/boot_wake_pin_configuration.inc
+++ b/stratosphere/boot/source/boot_wake_pin_configuration_icosa.inc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018-2019 Atmosphère-NX
+ * 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,
@@ -14,7 +14,7 @@
* along with this program. If not, see .
*/
-static constexpr WakePinConfig WakePinConfigs[] = {
+static constexpr WakePinConfig WakePinConfigsIcosa[] = {
{0x00, false, 0x02},
{0x01, false, 0x02},
{0x02, false, 0x02},
@@ -23,7 +23,7 @@ static constexpr WakePinConfig WakePinConfigs[] = {
{0x05, false, 0x02},
{0x06, true, 0x02},
{0x07, true, 0x02},
- {0x08, false, 0x01},
+ {0x08, true, 0x01},
{0x0A, true, 0x02},
{0x0B, false, 0x02},
{0x0C, false, 0x02},
@@ -66,4 +66,4 @@ static constexpr WakePinConfig WakePinConfigs[] = {
{0x3F, false, 0x02},
};
-static constexpr size_t NumWakePinConfigs = sizeof(WakePinConfigs) / sizeof(WakePinConfigs[0]);
+static constexpr size_t NumWakePinConfigsIcosa = util::size(WakePinConfigsIcosa);
diff --git a/stratosphere/boot/source/boot_wake_pin_configuration_iowa.inc b/stratosphere/boot/source/boot_wake_pin_configuration_iowa.inc
new file mode 100644
index 000000000..53dbe0007
--- /dev/null
+++ b/stratosphere/boot/source/boot_wake_pin_configuration_iowa.inc
@@ -0,0 +1,69 @@
+/*
+ * 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 .
+ */
+
+static constexpr WakePinConfig WakePinConfigsIowa[] = {
+ {0x00, false, 0x02},
+ {0x01, false, 0x02},
+ {0x02, false, 0x02},
+ {0x03, false, 0x02},
+ {0x04, true, 0x02},
+ {0x05, false, 0x02},
+ {0x06, true, 0x02},
+ {0x07, true, 0x02},
+ {0x08, true, 0x01},
+ {0x0A, true, 0x02},
+ {0x0B, false, 0x02},
+ {0x0C, false, 0x02},
+ {0x0D, false, 0x02},
+ {0x0E, true, 0x00},
+ {0x0F, false, 0x02},
+ {0x11, false, 0x02},
+ {0x12, false, 0x02},
+ {0x13, false, 0x02},
+ {0x14, false, 0x02},
+ {0x15, false, 0x02},
+ {0x16, false, 0x02},
+ {0x17, false, 0x02},
+ {0x18, false, 0x02},
+ {0x19, false, 0x02},
+ {0x1A, false, 0x02},
+ {0x1B, true, 0x00},
+ {0x1C, false, 0x02},
+ {0x21, false, 0x02},
+ {0x22, true, 0x00},
+ {0x23, true, 0x02},
+ {0x24, false, 0x02},
+ {0x2D, false, 0x02},
+ {0x2E, false, 0x02},
+ {0x2F, false, 0x02},
+ {0x30, true, 0x02},
+ {0x31, false, 0x02},
+ {0x32, false, 0x02},
+ {0x33, true, 0x00},
+ {0x34, true, 0x00},
+ {0x35, false, 0x02},
+ {0x36, false, 0x02},
+ {0x37, false, 0x02},
+ {0x38, false, 0x02},
+ {0x39, false, 0x02},
+ {0x3A, false, 0x02},
+ {0x3B, false, 0x02},
+ {0x3D, false, 0x02},
+ {0x3E, false, 0x02},
+ {0x3F, false, 0x02},
+};
+
+static constexpr size_t NumWakePinConfigsIowa = util::size(WakePinConfigsIowa);
diff --git a/stratosphere/boot/source/boot_wake_pins.cpp b/stratosphere/boot/source/boot_wake_pins.cpp
index 118d7682b..f4921d051 100644
--- a/stratosphere/boot/source/boot_wake_pins.cpp
+++ b/stratosphere/boot/source/boot_wake_pins.cpp
@@ -30,8 +30,11 @@ namespace ams::boot {
};
#include "boot_wake_control_configs.inc"
-#include "boot_wake_pin_configuration.inc"
+#include "boot_wake_pin_configuration_icosa.inc"
#include "boot_wake_pin_configuration_copper.inc"
+#include "boot_wake_pin_configuration_hoag.inc"
+#include "boot_wake_pin_configuration_iowa.inc"
+#include "boot_wake_pin_configuration_calcio.inc"
}
@@ -91,16 +94,34 @@ namespace ams::boot {
InitializePmcWakeConfiguration(false);
/* Set wake event levels, wake event enables. */
- const WakePinConfig *configs;
- size_t num_configs;
- if (spl::GetHardwareType() == spl::HardwareType::Copper) {
- configs = WakePinConfigsCopper;
- num_configs = NumWakePinConfigsCopper;
- } else {
- configs = WakePinConfigs;
- num_configs = NumWakePinConfigs;
+ const WakePinConfig *configs = nullptr;
+ size_t num_configs = 0;
+
+ switch (spl::GetHardwareType()) {
+ case spl::HardwareType::Icosa:
+ configs = WakePinConfigsIcosa;
+ num_configs = NumWakePinConfigsIcosa;
+ break;
+ case spl::HardwareType::Copper:
+ configs = WakePinConfigsCopper;
+ num_configs = NumWakePinConfigsCopper;
+ break;
+ case spl::HardwareType::Hoag:
+ configs = WakePinConfigsHoag;
+ num_configs = NumWakePinConfigsHoag;
+ break;
+ case spl::HardwareType::Iowa:
+ configs = WakePinConfigsIowa;
+ num_configs = NumWakePinConfigsIowa;
+ case spl::HardwareType::Calcio:
+ configs = WakePinConfigsCalcio;
+ num_configs = NumWakePinConfigsCalcio;
+ break;
+ AMS_UNREACHABLE_DEFAULT_CASE();
}
+ AMS_ABORT_UNLESS(configs != nullptr);
+
for (size_t i = 0; i < num_configs; i++) {
SetWakeEventLevel(configs[i].index, configs[i].level);
SetWakeEventEnabled(configs[i].index, configs[i].enabled);
diff --git a/stratosphere/boot/source/gpio/gpio_initial_configuration.cpp b/stratosphere/boot/source/gpio/gpio_initial_configuration.cpp
index 60a2cc747..5b7efd8cd 100644
--- a/stratosphere/boot/source/gpio/gpio_initial_configuration.cpp
+++ b/stratosphere/boot/source/gpio/gpio_initial_configuration.cpp
@@ -34,6 +34,7 @@ namespace ams::gpio {
#include "gpio_initial_configuration_copper.inc"
#include "gpio_initial_configuration_hoag.inc"
#include "gpio_initial_configuration_iowa.inc"
+#include "gpio_initial_configuration_calcio.inc"
}
@@ -49,32 +50,36 @@ namespace ams::gpio {
case spl::HardwareType::Icosa:
{
if (hos_ver >= hos::Version_400) {
- configs = InitialConfigsIcosa4x;
+ configs = InitialConfigsIcosa4x;
num_configs = NumInitialConfigsIcosa4x;
} else {
- configs = InitialConfigsIcosa;
+ configs = InitialConfigsIcosa;
num_configs = NumInitialConfigsIcosa;
}
}
break;
case spl::HardwareType::Copper:
- configs = InitialConfigsCopper;
+ configs = InitialConfigsCopper;
num_configs = NumInitialConfigsCopper;
break;
case spl::HardwareType::Hoag:
- configs = InitialConfigsHoag;
+ configs = InitialConfigsHoag;
num_configs = NumInitialConfigsHoag;
break;
case spl::HardwareType::Iowa:
- configs = InitialConfigsIowa;
+ configs = InitialConfigsIowa;
num_configs = NumInitialConfigsIowa;
break;
+ case spl::HardwareType::Calcio:
+ configs = InitialConfigsCalcio;
+ num_configs = NumInitialConfigsCalcio;
+ break;
/* Unknown hardware type, we can't proceed. */
AMS_UNREACHABLE_DEFAULT_CASE();
}
} else {
/* Until 2.0.0, the GPIO map for Icosa was used for all hardware types. */
- configs = InitialConfigsIcosa;
+ configs = InitialConfigsIcosa;
num_configs = NumInitialConfigsIcosa;
}
diff --git a/stratosphere/boot/source/gpio/gpio_initial_configuration_calcio.inc b/stratosphere/boot/source/gpio/gpio_initial_configuration_calcio.inc
new file mode 100644
index 000000000..0a2f72d5b
--- /dev/null
+++ b/stratosphere/boot/source/gpio/gpio_initial_configuration_calcio.inc
@@ -0,0 +1,51 @@
+/*
+ * 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 .
+ */
+
+constexpr InitialConfig InitialConfigsCalcio[] = {
+ {0x50, GpioDirection_Output, GpioValue_Low},
+ {0x51, GpioDirection_Output, GpioValue_Low},
+ {0x52, GpioDirection_Output, GpioValue_Low},
+ {0x53, GpioDirection_Output, GpioValue_Low},
+ {0x02, GpioDirection_Output, GpioValue_Low},
+ {0x0B, GpioDirection_Input, GpioValue_Low},
+ {0x14, GpioDirection_Input, GpioValue_High},
+ {0x18, GpioDirection_Input, GpioValue_Low},
+ {0x19, GpioDirection_Input, GpioValue_High},
+ {0x1A, GpioDirection_Input, GpioValue_High},
+ {0x1C, GpioDirection_Input, GpioValue_High},
+ {0x20, GpioDirection_Output, GpioValue_Low},
+ {0x38, GpioDirection_Input, GpioValue_High},
+ {0x23, GpioDirection_Input, GpioValue_High},
+ {0x25, GpioDirection_Input, GpioValue_Low},
+ {0x26, GpioDirection_Input, GpioValue_Low},
+ {0x27, GpioDirection_Input, GpioValue_Low},
+ {0x28, GpioDirection_Input, GpioValue_High},
+ {0x4F, GpioDirection_Input, GpioValue_High},
+ {0x48, GpioDirection_Output, GpioValue_Low},
+ {0x4C, GpioDirection_Input, GpioValue_High},
+ {0x4A, GpioDirection_Output, GpioValue_Low},
+ {0x2D, GpioDirection_Output, GpioValue_Low},
+ {0x2E, GpioDirection_Output, GpioValue_Low},
+ {0x37, GpioDirection_Input, GpioValue_Low},
+ {0x2F, GpioDirection_Output, GpioValue_Low},
+ {0x03, GpioDirection_Output, GpioValue_Low},
+ {0x30, GpioDirection_Input, GpioValue_Low},
+ {0x31, GpioDirection_Output, GpioValue_Low},
+ {0x49, GpioDirection_Output, GpioValue_Low},
+ {0x4E, GpioDirection_Input, GpioValue_Low},
+};
+
+constexpr u32 NumInitialConfigsCalcio = util::size(InitialConfigsCalcio);
diff --git a/stratosphere/boot/source/gpio/gpio_initial_configuration_copper.inc b/stratosphere/boot/source/gpio/gpio_initial_configuration_copper.inc
index 8f4d8abc0..70771f22a 100644
--- a/stratosphere/boot/source/gpio/gpio_initial_configuration_copper.inc
+++ b/stratosphere/boot/source/gpio/gpio_initial_configuration_copper.inc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018-2019 Atmosphère-NX
+ * 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,
@@ -61,4 +61,4 @@ constexpr InitialConfig InitialConfigsCopper[] = {
{0x4E, GpioDirection_Input, GpioValue_Low},
};
-constexpr u32 NumInitialConfigsCopper = (sizeof(InitialConfigsCopper) / sizeof(InitialConfigsCopper[0]));
+constexpr u32 NumInitialConfigsCopper = util::size(InitialConfigsCopper);
diff --git a/stratosphere/boot/source/gpio/gpio_initial_configuration_hoag.inc b/stratosphere/boot/source/gpio/gpio_initial_configuration_hoag.inc
index 60ffdd364..8e18daf4b 100644
--- a/stratosphere/boot/source/gpio/gpio_initial_configuration_hoag.inc
+++ b/stratosphere/boot/source/gpio/gpio_initial_configuration_hoag.inc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018-2019 Atmosphère-NX
+ * 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,
@@ -15,64 +15,65 @@
*/
constexpr InitialConfig InitialConfigsHoag[] = {
- {0x04, GpioDirection_Input, GpioValue_High},
{0x05, GpioDirection_Output, GpioValue_Low},
- {0x06, GpioDirection_Input, GpioValue_Low},
+ {0x06, GpioDirection_Input, GpioValue_Low},
+ {0x50, GpioDirection_Output, GpioValue_Low},
+ {0x51, GpioDirection_Output, GpioValue_Low},
+ {0x52, GpioDirection_Output, GpioValue_Low},
+ {0x53, GpioDirection_Output, GpioValue_Low},
{0x02, GpioDirection_Output, GpioValue_Low},
- {0x3C, GpioDirection_Input, GpioValue_Low},
- {0x0F, GpioDirection_Input, GpioValue_High},
- {0x08, GpioDirection_Input, GpioValue_Low},
- {0x09, GpioDirection_Input, GpioValue_Low},
+ {0x3C, GpioDirection_Input, GpioValue_Low},
+ {0x56, GpioDirection_Input, GpioValue_High},
+ {0x0F, GpioDirection_Input, GpioValue_High},
+ {0x09, GpioDirection_Input, GpioValue_Low},
{0x0A, GpioDirection_Output, GpioValue_Low},
- {0x0B, GpioDirection_Input, GpioValue_Low},
+ {0x0B, GpioDirection_Input, GpioValue_Low},
+ {0x57, GpioDirection_Output, GpioValue_Low},
+ {0x58, GpioDirection_Output, GpioValue_Low},
{0x0D, GpioDirection_Output, GpioValue_Low},
- {0x0E, GpioDirection_Input, GpioValue_Low},
- {0x10, GpioDirection_Input, GpioValue_Low},
- {0x11, GpioDirection_Input, GpioValue_Low},
- {0x12, GpioDirection_Input, GpioValue_Low},
- {0x13, GpioDirection_Input, GpioValue_Low},
- {0x14, GpioDirection_Input, GpioValue_High},
- {0x16, GpioDirection_Input, GpioValue_Low},
- {0x15, GpioDirection_Input, GpioValue_Low},
- {0x17, GpioDirection_Input, GpioValue_High},
- {0x18, GpioDirection_Input, GpioValue_Low},
- {0x19, GpioDirection_Input, GpioValue_High},
- {0x1A, GpioDirection_Input, GpioValue_High},
- {0x1B, GpioDirection_Input, GpioValue_Low},
- {0x1C, GpioDirection_Input, GpioValue_Low},
+ {0x59, GpioDirection_Output, GpioValue_Low},
+ {0x14, GpioDirection_Input, GpioValue_High},
+ {0x16, GpioDirection_Input, GpioValue_Low},
+ {0x15, GpioDirection_Input, GpioValue_Low},
+ {0x17, GpioDirection_Input, GpioValue_High},
+ {0x18, GpioDirection_Input, GpioValue_Low},
+ {0x19, GpioDirection_Input, GpioValue_High},
+ {0x1A, GpioDirection_Input, GpioValue_High},
+ {0x1B, GpioDirection_Input, GpioValue_Low},
+ {0x1C, GpioDirection_Input, GpioValue_High},
{0x1D, GpioDirection_Output, GpioValue_Low},
{0x1E, GpioDirection_Output, GpioValue_Low},
+ {0x5B, GpioDirection_Input, GpioValue_High},
{0x20, GpioDirection_Output, GpioValue_Low},
- {0x21, GpioDirection_Input, GpioValue_Low},
- {0x38, GpioDirection_Input, GpioValue_High},
- {0x22, GpioDirection_Input, GpioValue_Low},
- {0x23, GpioDirection_Input, GpioValue_High},
+ {0x21, GpioDirection_Input, GpioValue_Low},
+ {0x38, GpioDirection_Input, GpioValue_High},
+ {0x23, GpioDirection_Input, GpioValue_High},
{0x01, GpioDirection_Output, GpioValue_Low},
- {0x39, GpioDirection_Output, GpioValue_Low},
+ {0x5C, GpioDirection_Output, GpioValue_Low},
+ {0x54, GpioDirection_Input, GpioValue_Low},
{0x24, GpioDirection_Output, GpioValue_Low},
- {0x34, GpioDirection_Input, GpioValue_Low},
- {0x25, GpioDirection_Input, GpioValue_Low},
- {0x26, GpioDirection_Input, GpioValue_Low},
- {0x27, GpioDirection_Input, GpioValue_Low},
- {0x2B, GpioDirection_Output, GpioValue_Low},
- {0x28, GpioDirection_Input, GpioValue_High},
+ {0x25, GpioDirection_Input, GpioValue_Low},
+ {0x26, GpioDirection_Input, GpioValue_Low},
+ {0x27, GpioDirection_Input, GpioValue_Low},
+ {0x28, GpioDirection_Input, GpioValue_High},
{0x1F, GpioDirection_Output, GpioValue_Low},
- {0x29, GpioDirection_Input, GpioValue_High},
- {0x3A, GpioDirection_Output, GpioValue_Low},
- {0x0C, GpioDirection_Input, GpioValue_Low},
+ {0x4F, GpioDirection_Input, GpioValue_High},
+ {0x55, GpioDirection_Output, GpioValue_Low},
+ {0x5F, GpioDirection_Input, GpioValue_Low},
+ {0x60, GpioDirection_Input, GpioValue_Low},
+ {0x61, GpioDirection_Input, GpioValue_Low},
+ {0x62, GpioDirection_Input, GpioValue_Low},
{0x2D, GpioDirection_Output, GpioValue_Low},
{0x2E, GpioDirection_Output, GpioValue_Low},
- {0x37, GpioDirection_Input, GpioValue_Low},
+ {0x37, GpioDirection_Input, GpioValue_Low},
{0x2F, GpioDirection_Output, GpioValue_Low},
{0x03, GpioDirection_Output, GpioValue_Low},
- {0x30, GpioDirection_Input, GpioValue_Low},
- {0x3B, GpioDirection_Input, GpioValue_Low},
+ {0x30, GpioDirection_Input, GpioValue_Low},
+ {0x3B, GpioDirection_Input, GpioValue_Low},
{0x31, GpioDirection_Output, GpioValue_Low},
{0x32, GpioDirection_Output, GpioValue_Low},
{0x33, GpioDirection_Output, GpioValue_Low},
- {0x35, GpioDirection_Input, GpioValue_High},
- {0x2C, GpioDirection_Output, GpioValue_Low},
- {0x36, GpioDirection_Output, GpioValue_Low},
+ {0x5A, GpioDirection_Output, GpioValue_Low},
};
-constexpr u32 NumInitialConfigsHoag = (sizeof(InitialConfigsHoag) / sizeof(InitialConfigsHoag[0]));
+constexpr u32 NumInitialConfigsHoag = util::size(InitialConfigsHoag);
diff --git a/stratosphere/boot/source/gpio/gpio_initial_configuration_icosa.inc b/stratosphere/boot/source/gpio/gpio_initial_configuration_icosa.inc
index 44c348234..06a80f49e 100644
--- a/stratosphere/boot/source/gpio/gpio_initial_configuration_icosa.inc
+++ b/stratosphere/boot/source/gpio/gpio_initial_configuration_icosa.inc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018-2019 Atmosphère-NX
+ * 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,
@@ -77,7 +77,7 @@ constexpr InitialConfig InitialConfigsIcosa[] = {
{0x36, GpioDirection_Output, GpioValue_Low},
};
-constexpr u32 NumInitialConfigsIcosa = (sizeof(InitialConfigsIcosa) / sizeof(InitialConfigsIcosa[0]));
+constexpr u32 NumInitialConfigsIcosa = util::size(InitialConfigsIcosa);
constexpr InitialConfig InitialConfigsIcosa4x[] = {
{0x04, GpioDirection_Input, GpioValue_High},
@@ -142,4 +142,4 @@ constexpr InitialConfig InitialConfigsIcosa4x[] = {
{0x36, GpioDirection_Output, GpioValue_Low},
};
-constexpr u32 NumInitialConfigsIcosa4x = (sizeof(InitialConfigsIcosa4x) / sizeof(InitialConfigsIcosa4x[0]));
+constexpr u32 NumInitialConfigsIcosa4x = util::size(InitialConfigsIcosa4x);
diff --git a/stratosphere/boot/source/gpio/gpio_initial_configuration_iowa.inc b/stratosphere/boot/source/gpio/gpio_initial_configuration_iowa.inc
index e6c0d16f3..f0a017897 100644
--- a/stratosphere/boot/source/gpio/gpio_initial_configuration_iowa.inc
+++ b/stratosphere/boot/source/gpio/gpio_initial_configuration_iowa.inc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018-2019 Atmosphère-NX
+ * 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,
@@ -15,64 +15,65 @@
*/
constexpr InitialConfig InitialConfigsIowa[] = {
- {0x04, GpioDirection_Input, GpioValue_High},
+ {0x04, GpioDirection_Input, GpioValue_High},
{0x05, GpioDirection_Output, GpioValue_Low},
- {0x06, GpioDirection_Input, GpioValue_Low},
+ {0x06, GpioDirection_Input, GpioValue_Low},
{0x02, GpioDirection_Output, GpioValue_Low},
- {0x3C, GpioDirection_Input, GpioValue_Low},
- {0x0F, GpioDirection_Input, GpioValue_High},
- {0x08, GpioDirection_Input, GpioValue_Low},
- {0x09, GpioDirection_Input, GpioValue_Low},
+ {0x3C, GpioDirection_Input, GpioValue_Low},
+ {0x0F, GpioDirection_Input, GpioValue_High},
+ {0x08, GpioDirection_Input, GpioValue_Low},
+ {0x09, GpioDirection_Input, GpioValue_Low},
{0x0A, GpioDirection_Output, GpioValue_Low},
- {0x0B, GpioDirection_Input, GpioValue_Low},
+ {0x0B, GpioDirection_Input, GpioValue_Low},
{0x0D, GpioDirection_Output, GpioValue_Low},
- {0x0E, GpioDirection_Input, GpioValue_Low},
- {0x10, GpioDirection_Input, GpioValue_Low},
- {0x11, GpioDirection_Input, GpioValue_Low},
- {0x12, GpioDirection_Input, GpioValue_Low},
- {0x13, GpioDirection_Input, GpioValue_Low},
- {0x14, GpioDirection_Input, GpioValue_High},
- {0x16, GpioDirection_Input, GpioValue_Low},
- {0x15, GpioDirection_Input, GpioValue_Low},
- {0x17, GpioDirection_Input, GpioValue_High},
- {0x18, GpioDirection_Input, GpioValue_Low},
- {0x19, GpioDirection_Input, GpioValue_High},
- {0x1A, GpioDirection_Input, GpioValue_High},
- {0x1B, GpioDirection_Input, GpioValue_Low},
- {0x1C, GpioDirection_Input, GpioValue_Low},
+ {0x0E, GpioDirection_Input, GpioValue_Low},
+ {0x10, GpioDirection_Input, GpioValue_Low},
+ {0x11, GpioDirection_Input, GpioValue_Low},
+ {0x12, GpioDirection_Input, GpioValue_Low},
+ {0x13, GpioDirection_Input, GpioValue_Low},
+ {0x59, GpioDirection_Output, GpioValue_Low},
+ {0x14, GpioDirection_Input, GpioValue_High},
+ {0x16, GpioDirection_Input, GpioValue_Low},
+ {0x15, GpioDirection_Input, GpioValue_Low},
+ {0x17, GpioDirection_Input, GpioValue_High},
+ {0x18, GpioDirection_Input, GpioValue_Low},
+ {0x19, GpioDirection_Input, GpioValue_High},
+ {0x1A, GpioDirection_Input, GpioValue_High},
+ {0x1B, GpioDirection_Input, GpioValue_Low},
+ {0x1C, GpioDirection_Input, GpioValue_Low},
{0x1D, GpioDirection_Output, GpioValue_Low},
{0x1E, GpioDirection_Output, GpioValue_Low},
{0x20, GpioDirection_Output, GpioValue_Low},
- {0x21, GpioDirection_Input, GpioValue_Low},
- {0x38, GpioDirection_Input, GpioValue_High},
- {0x22, GpioDirection_Input, GpioValue_Low},
- {0x23, GpioDirection_Input, GpioValue_High},
+ {0x21, GpioDirection_Input, GpioValue_Low},
+ {0x38, GpioDirection_Input, GpioValue_High},
+ {0x22, GpioDirection_Input, GpioValue_Low},
+ {0x23, GpioDirection_Input, GpioValue_High},
{0x01, GpioDirection_Output, GpioValue_Low},
{0x39, GpioDirection_Output, GpioValue_Low},
{0x24, GpioDirection_Output, GpioValue_Low},
- {0x34, GpioDirection_Input, GpioValue_Low},
- {0x25, GpioDirection_Input, GpioValue_Low},
- {0x26, GpioDirection_Input, GpioValue_Low},
- {0x27, GpioDirection_Input, GpioValue_Low},
+ {0x34, GpioDirection_Input, GpioValue_Low},
+ {0x25, GpioDirection_Input, GpioValue_Low},
+ {0x26, GpioDirection_Input, GpioValue_Low},
+ {0x27, GpioDirection_Input, GpioValue_Low},
{0x2B, GpioDirection_Output, GpioValue_Low},
- {0x28, GpioDirection_Input, GpioValue_High},
+ {0x28, GpioDirection_Input, GpioValue_High},
{0x1F, GpioDirection_Output, GpioValue_Low},
- {0x29, GpioDirection_Input, GpioValue_High},
+ {0x4F, GpioDirection_Input, GpioValue_High},
{0x3A, GpioDirection_Output, GpioValue_Low},
- {0x0C, GpioDirection_Input, GpioValue_Low},
+ {0x0C, GpioDirection_Input, GpioValue_Low},
{0x2D, GpioDirection_Output, GpioValue_Low},
{0x2E, GpioDirection_Output, GpioValue_Low},
- {0x37, GpioDirection_Input, GpioValue_Low},
+ {0x37, GpioDirection_Input, GpioValue_Low},
{0x2F, GpioDirection_Output, GpioValue_Low},
{0x03, GpioDirection_Output, GpioValue_Low},
- {0x30, GpioDirection_Input, GpioValue_Low},
- {0x3B, GpioDirection_Input, GpioValue_Low},
+ {0x30, GpioDirection_Input, GpioValue_Low},
+ {0x3B, GpioDirection_Input, GpioValue_Low},
{0x31, GpioDirection_Output, GpioValue_Low},
{0x32, GpioDirection_Output, GpioValue_Low},
{0x33, GpioDirection_Output, GpioValue_Low},
- {0x35, GpioDirection_Input, GpioValue_High},
+ {0x35, GpioDirection_Input, GpioValue_High},
{0x2C, GpioDirection_Output, GpioValue_Low},
{0x36, GpioDirection_Output, GpioValue_Low},
};
-constexpr u32 NumInitialConfigsIowa = (sizeof(InitialConfigsIowa) / sizeof(InitialConfigsIowa[0]));
+constexpr u32 NumInitialConfigsIowa = util::size(InitialConfigsIowa);
diff --git a/stratosphere/boot/source/gpio/gpio_map.inc b/stratosphere/boot/source/gpio/gpio_map.inc
index c3527da50..7a76dcc5e 100644
--- a/stratosphere/boot/source/gpio/gpio_map.inc
+++ b/stratosphere/boot/source/gpio/gpio_map.inc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018-2019 Atmosphère-NX
+ * 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,
@@ -103,6 +103,30 @@ constexpr u32 Map[] = {
/* 5.0.0+ Copper only (unused) */
0x00000056, /* Port K, Pin 6 */
+
+ /* 6.0.0+ */
+ 0x00000020, /* Port E, Pin 0 */
+ 0x00000021, /* Port E, Pin 1 */
+ 0x00000022, /* Port E, Pin 2 */
+ 0x00000023, /* Port E, Pin 3 */
+ 0x0000004C, /* Port J, Pin 4 */
+ 0x00000057, /* Port K, Pin 7 */
+ 0x00000027, /* Port S, Pin 4 */
+ 0x00000098, /* Port T, Pin 0 */
+ 0x00000099, /* Port T, Pin 1 */
+ 0x000000BB, /* Port X, Pin 3 */
+ 0x000000E5, /* Port CC, Pin 5 */
+ 0x000000AB, /* Port V, Pin 3 */
+ 0x0000004E, /* Port J, Pin 6 */
+
+ /* 7.0.0+ */
+ 0x00000032, /* Port G, Pin 2 */
+ 0x0000001B, /* Port D, Pin 3 */
+ 0x00000017, /* Port C, Pin 7 */
+ 0x00000018, /* Port D, Pin 0 */
+ 0x00000015, /* Port C, Pin 5 */
+ 0x00000016, /* Port C, Pin 6 */
+
};
-static constexpr u32 PadNameMax = (sizeof(Map) / sizeof(Map[0]));
+static constexpr u32 PadNameMax = util::size(Map);
diff --git a/stratosphere/boot/source/i2c/driver/impl/i2c_bus_accessor.cpp b/stratosphere/boot/source/i2c/driver/impl/i2c_bus_accessor.cpp
index 1cb79f720..16c53a487 100644
--- a/stratosphere/boot/source/i2c/driver/impl/i2c_bus_accessor.cpp
+++ b/stratosphere/boot/source/i2c/driver/impl/i2c_bus_accessor.cpp
@@ -413,7 +413,7 @@ namespace ams::i2c::driver::impl {
this->SetPacketMode();
this->FlushFifos();
}
- } R_END_TRY_CATCH_WITH_ASSERT;
+ } R_END_TRY_CATCH_WITH_ABORT_UNLESS;
}
Result BusAccessor::GetAndHandleTransactionResult() {
diff --git a/stratosphere/boot/source/pinmux/pinmux_initial_configuration.cpp b/stratosphere/boot/source/pinmux/pinmux_initial_configuration.cpp
index 47964833d..d9a651b12 100644
--- a/stratosphere/boot/source/pinmux/pinmux_initial_configuration.cpp
+++ b/stratosphere/boot/source/pinmux/pinmux_initial_configuration.cpp
@@ -31,7 +31,10 @@ namespace ams::pinmux {
#include "pinmux_initial_configuration_copper.inc"
#include "pinmux_initial_configuration_hoag.inc"
#include "pinmux_initial_configuration_iowa.inc"
+#include "pinmux_initial_configuration_calcio.inc"
+
#include "pinmux_initial_drive_pad_configuration.inc"
+#include "pinmux_initial_drive_pad_configuration_hoag.inc"
/* Configuration helpers. */
@@ -42,21 +45,25 @@ namespace ams::pinmux {
switch (hw_type) {
case spl::HardwareType::Icosa:
- configs = InitialConfigsIcosa;
+ configs = InitialConfigsIcosa;
num_configs = NumInitialConfigsIcosa;
break;
case spl::HardwareType::Copper:
- configs = InitialConfigsCopper;
+ configs = InitialConfigsCopper;
num_configs = NumInitialConfigsCopper;
break;
case spl::HardwareType::Hoag:
- configs = InitialConfigsHoag;
+ configs = InitialConfigsHoag;
num_configs = NumInitialConfigsHoag;
break;
case spl::HardwareType::Iowa:
- configs = InitialConfigsIowa;
+ configs = InitialConfigsIowa;
num_configs = NumInitialConfigsIowa;
break;
+ case spl::HardwareType::Calcio:
+ configs = InitialConfigsCalcio;
+ num_configs = NumInitialConfigsCalcio;
+ break;
/* Unknown hardware type, we can't proceed. */
AMS_UNREACHABLE_DEFAULT_CASE();
}
@@ -68,20 +75,42 @@ namespace ams::pinmux {
UpdatePad(configs[i].name, configs[i].val, configs[i].mask);
}
- /* Extra configs for iowa only. */
- if (hw_type == spl::HardwareType::Iowa) {
- static constexpr u32 ExtraIowaPadNames[] = {
+ /* Extra configs for mariko only. */
+ if (hw_type == spl::HardwareType::Hoag || hw_type == spl::HardwareType::Iowa || hw_type == spl::HardwareType::Calcio) {
+ static constexpr u32 ExtraMarikoPadNames[] = {
0xAA, 0xAC, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9
};
- for (size_t i = 0; i < util::size(ExtraIowaPadNames); i++) {
- UpdatePad(ExtraIowaPadNames[i], 0x2000, 0x2000);
+ for (size_t i = 0; i < util::size(ExtraMarikoPadNames); i++) {
+ UpdatePad(ExtraMarikoPadNames[i], 0x2000, 0x2000);
}
}
}
void ConfigureInitialDrivePads() {
- const InitialConfig *configs = InitialDrivePadConfigs;
- for (size_t i = 0; i < NumInitialDrivePadConfigs; i++) {
+ const InitialConfig *configs = nullptr;
+ size_t num_configs = 0;
+ const auto hw_type = spl::GetHardwareType();
+
+ switch (hw_type) {
+ case spl::HardwareType::Icosa:
+ case spl::HardwareType::Copper:
+ case spl::HardwareType::Iowa:
+ case spl::HardwareType::Calcio:
+ configs = InitialDrivePadConfigs;
+ num_configs = NumInitialDrivePadConfigs;
+ break;
+ case spl::HardwareType::Hoag:
+ configs = InitialDrivePadConfigsHoag;
+ num_configs = NumInitialDrivePadConfigsHoag;
+ break;
+ /* Unknown hardware type, we can't proceed. */
+ AMS_UNREACHABLE_DEFAULT_CASE();
+ }
+
+ /* Ensure we found an appropriate config. */
+ AMS_ABORT_UNLESS(configs != nullptr);
+
+ for (size_t i = 0; i < num_configs; i++) {
UpdateDrivePad(configs[i].name, configs[i].val, configs[i].mask);
}
}
diff --git a/stratosphere/boot/source/pinmux/pinmux_initial_configuration_calcio.inc b/stratosphere/boot/source/pinmux/pinmux_initial_configuration_calcio.inc
new file mode 100644
index 000000000..3dcb28611
--- /dev/null
+++ b/stratosphere/boot/source/pinmux/pinmux_initial_configuration_calcio.inc
@@ -0,0 +1,194 @@
+/*
+ * 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 .
+ */
+
+constexpr InitialConfig InitialConfigsCalcio[] = {
+ {0x0D, 0x20, 0x27F},
+ {0x0C, 0x00, 0x27F},
+ {0x10, 0x40, 0x27F},
+ {0x0F, 0x00, 0x27F},
+ {0x0E, 0x20, 0x27F},
+ {0x00, 0x40, 0x7F},
+ {0x01, 0x50, 0x7F},
+ {0x05, 0x50, 0x7F},
+ {0x04, 0x50, 0x7F},
+ {0x03, 0x50, 0x7F},
+ {0x02, 0x50, 0x7F},
+ {0x5B, 0x00, 0x78},
+ {0x80, 0x01, 0x7F},
+ {0x34, 0x40, 0x27F},
+ {0x35, 0x40, 0x27F},
+ {0x55, 0x20, 0x78},
+ {0x56, 0x01, 0x7F},
+ {0x5C, 0x00, 0x78},
+ {0x5A, 0x20, 0x78},
+ {0x2C, 0x40, 0x27F},
+ {0x2D, 0x40, 0x27F},
+ {0x36, 0x00, 0x7F},
+ {0x37, 0x30, 0x7F},
+ {0x38, 0x00, 0x7F},
+ {0x39, 0x28, 0x7F},
+ {0x54, 0x00, 0x67},
+ {0x42, 0x00, 0x7F},
+ {0x43, 0x28, 0x7F},
+ {0x44, 0x00, 0x7F},
+ {0x45, 0x28, 0x7F},
+ {0x4B, 0x28, 0x7F},
+ {0x4C, 0x00, 0x7F},
+ {0x4A, 0x00, 0x7F},
+ {0x4D, 0x00, 0x7F},
+ {0x63, 0x240, 0x27F},
+ {0x26, 0x04, 0x67},
+ {0x27, 0x04, 0x67},
+ {0x28, 0x04, 0x67},
+ {0x29, 0x04, 0x67},
+ {0x2A, 0x04, 0x67},
+ {0x78, 0x24, 0x7F},
+ {0x88, 0x34, 0x7F},
+ {0x89, 0x24, 0x7F},
+ {0x8A, 0x34, 0x7F},
+ {0x8B, 0x34, 0x7F},
+ {0x8D, 0x34, 0x7F},
+ {0x81, 0x04, 0x67},
+ {0x9D, 0x34, 0x7F},
+ {0x9F, 0x34, 0x7F},
+ {0x92, 0x4C, 0x7F},
+ {0x93, 0x4C, 0x7F},
+ {0x94, 0x44, 0x7F},
+ {0x96, 0x34, 0x7F},
+ {0x98, 0x34, 0x7F},
+ {0x12, 0x0C, 0x7F},
+ {0x13, 0x34, 0x7F},
+ {0x14, 0x0C, 0x7F},
+ {0x6A, 0x04, 0x67},
+ {0x6B, 0x04, 0x67},
+ {0x6C, 0x2C, 0x7F},
+ {0x6D, 0x04, 0x67},
+ {0x6E, 0x04, 0x67},
+ {0x6F, 0x24, 0x7F},
+ {0x70, 0x04, 0x7F},
+ {0x69, 0x0C, 0x7F},
+ {0x64, 0x24, 0x27F},
+ {0x5D, 0x05, 0x07},
+ {0x5E, 0x05, 0x07},
+ {0x5F, 0x05, 0x07},
+ {0x60, 0x05, 0x07},
+ {0x61, 0x05, 0x07},
+ {0x47, 0x05, 0x07},
+ {0x48, 0x05, 0x07},
+ {0x46, 0x05, 0x07},
+ {0x49, 0x05, 0x07},
+ {0x17, 0x05, 0x07},
+ {0x18, 0x05, 0x07},
+ {0x19, 0x05, 0x07},
+ {0x1A, 0x05, 0x07},
+ {0x1B, 0x05, 0x07},
+ {0x2B, 0x05, 0x07},
+ {0x8F, 0x05, 0x07},
+ {0x90, 0x05, 0x07},
+ {0x30, 0x05, 0x07},
+ {0x31, 0x05, 0x07},
+ {0x32, 0x05, 0x07},
+ {0x33, 0x05, 0x07},
+ {0x52, 0x05, 0x07},
+ {0x53, 0x05, 0x07},
+ {0x75, 0x05, 0x07},
+ {0x76, 0x05, 0x07},
+ {0x77, 0x05, 0x07},
+ {0x79, 0x05, 0x07},
+ {0x7A, 0x05, 0x07},
+ {0x11, 0x05, 0x07},
+ {0x8E, 0x05, 0x07},
+ {0xAA, 0x05, 0x07},
+ {0xAB, 0x05, 0x07},
+ {0xAC, 0x05, 0x07},
+ {0xA2, 0x05, 0x07},
+ {0xA3, 0x05, 0x07},
+ {0xA4, 0x05, 0x07},
+ {0xA5, 0x05, 0x07},
+ {0xA6, 0x05, 0x07},
+ {0xA7, 0x05, 0x07},
+ {0xA8, 0x05, 0x07},
+ {0xA9, 0x05, 0x07},
+ {0xAD, 0x05, 0x07},
+ {0xAE, 0x05, 0x07},
+ {0x06, 0x05, 0x07},
+ {0x07, 0x05, 0x07},
+ {0x08, 0x05, 0x07},
+ {0x09, 0x05, 0x07},
+ {0x0A, 0x05, 0x07},
+ {0x0B, 0x05, 0x07},
+ {0x87, 0x05, 0x07},
+ {0x86, 0x05, 0x07},
+ {0x82, 0x05, 0x07},
+ {0x83, 0x05, 0x07},
+ {0x85, 0x05, 0x07},
+ {0x84, 0x05, 0x07},
+ {0x8C, 0x05, 0x07},
+ {0x7B, 0x05, 0x07},
+ {0x7C, 0x05, 0x07},
+ {0x7D, 0x05, 0x07},
+ {0x7E, 0x05, 0x07},
+ {0x7F, 0x05, 0x07},
+ {0x9C, 0x05, 0x07},
+ {0x9E, 0x05, 0x07},
+ {0xA0, 0x05, 0x07},
+ {0xA1, 0x05, 0x07},
+ {0x58, 0x00, 0x18},
+ {0x59, 0x00, 0x18},
+ {0x4F, 0x05, 0x07},
+ {0x50, 0x05, 0x07},
+ {0x4E, 0x05, 0x07},
+ {0x51, 0x05, 0x07},
+ {0x2E, 0x05, 0x07},
+ {0x2F, 0x05, 0x07},
+ {0x3A, 0x05, 0x07},
+ {0x3B, 0x05, 0x07},
+ {0x3C, 0x05, 0x07},
+ {0x3D, 0x05, 0x07},
+ {0x95, 0x05, 0x07},
+ {0x97, 0x05, 0x07},
+ {0x99, 0x05, 0x07},
+ {0x9A, 0x05, 0x07},
+ {0x9B, 0x05, 0x07},
+ {0x15, 0x05, 0x07},
+ {0x16, 0x05, 0x07},
+ {0x1C, 0x05, 0x07},
+ {0x1D, 0x05, 0x07},
+ {0x1E, 0x05, 0x07},
+ {0x1F, 0x05, 0x07},
+ {0x3E, 0x05, 0x07},
+ {0x3F, 0x05, 0x07},
+ {0x40, 0x05, 0x07},
+ {0x41, 0x05, 0x07},
+ {0x91, 0x05, 0x07},
+ {0x71, 0x05, 0x07},
+ {0x72, 0x05, 0x07},
+ {0x73, 0x05, 0x07},
+ {0x74, 0x05, 0x07},
+ {0x22, 0x05, 0x07},
+ {0x23, 0x05, 0x07},
+ {0x20, 0x05, 0x07},
+ {0x21, 0x05, 0x07},
+ {0x24, 0x05, 0x07},
+ {0x25, 0x05, 0x07},
+ {0x62, 0x05, 0x07},
+ {0x65, 0x05, 0x07},
+ {0x66, 0x05, 0x07},
+ {0x67, 0x05, 0x07},
+ {0x68, 0x05, 0x07},
+};
+
+constexpr u32 NumInitialConfigsCalcio = util::size(InitialConfigsCalcio);
diff --git a/stratosphere/boot/source/pinmux/pinmux_initial_configuration_copper.inc b/stratosphere/boot/source/pinmux/pinmux_initial_configuration_copper.inc
index 6e8aa2f37..a130e4d05 100644
--- a/stratosphere/boot/source/pinmux/pinmux_initial_configuration_copper.inc
+++ b/stratosphere/boot/source/pinmux/pinmux_initial_configuration_copper.inc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018-2019 Atmosphère-NX
+ * 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,
@@ -178,4 +178,4 @@ constexpr InitialConfig InitialConfigsCopper[] = {
{0x68, 0x05, 0x07},
};
-constexpr u32 NumInitialConfigsCopper = (sizeof(InitialConfigsCopper) / sizeof(InitialConfigsCopper[0]));
+constexpr u32 NumInitialConfigsCopper = util::size(InitialConfigsCopper);
diff --git a/stratosphere/boot/source/pinmux/pinmux_initial_configuration_hoag.inc b/stratosphere/boot/source/pinmux/pinmux_initial_configuration_hoag.inc
index e33e88049..5f1924c45 100644
--- a/stratosphere/boot/source/pinmux/pinmux_initial_configuration_hoag.inc
+++ b/stratosphere/boot/source/pinmux/pinmux_initial_configuration_hoag.inc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018-2019 Atmosphère-NX
+ * 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,
@@ -15,85 +15,83 @@
*/
constexpr InitialConfig InitialConfigsHoag[] = {
- {0x5D, 0x00, 0x67},
+ {0x5D, 0x00, 0x7F},
{0x47, 0x28, 0x7F},
- {0x48, 0x00, 0x67},
- {0x46, 0x00, 0x67},
- {0x49, 0x00, 0x67},
+ {0x48, 0x00, 0x7F},
+ {0x46, 0x00, 0x7F},
+ {0x49, 0x00, 0x7F},
{0x30, 0x40, 0x27F},
{0x31, 0x40, 0x27F},
{0x0D, 0x20, 0x27F},
- {0x0C, 0x00, 0x267},
- {0x10, 0x20, 0x27F},
- {0x0F, 0x00, 0x267},
+ {0x0C, 0x00, 0x27F},
+ {0x10, 0x40, 0x27F},
+ {0x0F, 0x00, 0x27F},
{0x0E, 0x20, 0x27F},
- {0x00, 0x48, 0x7F},
+ {0x00, 0x40, 0x7F},
{0x01, 0x50, 0x7F},
{0x05, 0x50, 0x7F},
{0x04, 0x50, 0x7F},
{0x03, 0x50, 0x7F},
{0x02, 0x50, 0x7F},
+ {0xAA, 0x40, 0x7F},
+ {0xAC, 0x40, 0x7F},
+ {0xA2, 0x50, 0x7F},
+ {0xA3, 0x50, 0x7F},
+ {0xA4, 0x50, 0x7F},
+ {0xA5, 0x50, 0x7F},
+ {0xA6, 0x50, 0x7F},
+ {0xA7, 0x50, 0x7F},
+ {0xA8, 0x50, 0x7F},
+ {0xA9, 0x50, 0x7F},
+ {0x83, 0x00, 0x67},
{0x5B, 0x00, 0x78},
{0x7C, 0x01, 0x67},
{0x80, 0x01, 0x7F},
{0x34, 0x40, 0x27F},
{0x35, 0x40, 0x27F},
{0x55, 0x20, 0x78},
- {0x56, 0x20, 0x7F},
- {0xA1, 0x30, 0x7F},
+ {0x56, 0x01, 0x7F},
{0x5C, 0x00, 0x78},
- {0x59, 0x00, 0x60},
- {0x5A, 0x30, 0x78},
+ {0x5A, 0x20, 0x78},
{0x2C, 0x40, 0x27F},
{0x2D, 0x40, 0x27F},
{0x2E, 0x40, 0x27F},
{0x2F, 0x40, 0x27F},
- {0x3B, 0x20, 0x7F},
- {0x3C, 0x00, 0x67},
- {0x3D, 0x20, 0x7F},
- {0x36, 0x00, 0x67},
+ {0x36, 0x00, 0x7F},
{0x37, 0x30, 0x7F},
- {0x38, 0x00, 0x67},
+ {0x38, 0x00, 0x7F},
{0x39, 0x28, 0x7F},
{0x54, 0x00, 0x67},
{0x9B, 0x30, 0x7F},
- {0x1C, 0x00, 0x67},
- {0x1D, 0x30, 0x7F},
- {0x1E, 0x00, 0x67},
- {0x1F, 0x00, 0x67},
+ {0x3E, 0x00, 0x7F},
{0x3F, 0x20, 0x7F},
- {0x40, 0x00, 0x67},
- {0x41, 0x20, 0x7F},
- {0x42, 0x00, 0x67},
+ {0x40, 0x00, 0x7F},
+ {0x41, 0x30, 0x7F},
+ {0x42, 0x00, 0x7F},
{0x43, 0x28, 0x7F},
- {0x44, 0x00, 0x67},
+ {0x44, 0x00, 0x7F},
{0x45, 0x28, 0x7F},
- {0x22, 0x00, 0x67},
- {0x23, 0x28, 0x7F},
- {0x20, 0x00, 0x67},
- {0x21, 0x00, 0x67},
{0x4B, 0x28, 0x7F},
- {0x4C, 0x00, 0x67},
- {0x4A, 0x00, 0x67},
- {0x4D, 0x00, 0x67},
- {0x64, 0x20, 0x27F},
- {0x5F, 0x34, 0x7F},
+ {0x4C, 0x00, 0x7F},
+ {0x4A, 0x00, 0x7F},
+ {0x4D, 0x00, 0x7F},
{0x60, 0x04, 0x67},
{0x61, 0x2C, 0x7F},
+ {0x26, 0x04, 0x67},
+ {0x27, 0x04, 0x67},
+ {0x28, 0x04, 0x67},
+ {0x29, 0x04, 0x67},
{0x2A, 0x04, 0x67},
- {0x2B, 0x04, 0x67},
{0x8F, 0x24, 0x7F},
+ {0x90, 0x34, 0x7F},
{0x33, 0x34, 0x27F},
- {0x52, 0x2C, 0x7F},
{0x53, 0x24, 0x7F},
{0x77, 0x04, 0x67},
- {0x78, 0x34, 0x7F},
+ {0x78, 0x24, 0x7F},
+ {0x79, 0x04, 0x67},
+ {0x7A, 0x04, 0x67},
{0x11, 0x04, 0x67},
- {0x06, 0x2C, 0x7F},
- {0x08, 0x24, 0x7F},
- {0x09, 0x24, 0x7F},
- {0x0A, 0x24, 0x7F},
- {0x0B, 0x24, 0x7F},
+ {0x87, 0x04, 0x67},
{0x88, 0x34, 0x7F},
{0x86, 0x2C, 0x7F},
{0x82, 0x24, 0x7F},
@@ -101,29 +99,30 @@ constexpr InitialConfig InitialConfigsHoag[] = {
{0x89, 0x24, 0x7F},
{0x8A, 0x34, 0x7F},
{0x8B, 0x34, 0x7F},
- {0x8C, 0x34, 0x7F},
- {0x8D, 0x24, 0x7F},
+ {0x8C, 0x24, 0x7F},
+ {0x8D, 0x34, 0x7F},
{0x7D, 0x04, 0x67},
{0x7E, 0x04, 0x67},
+ {0x7F, 0x34, 0x7F},
{0x81, 0x04, 0x67},
- {0x9C, 0x34, 0x7F},
+ {0x9C, 0x24, 0x7F},
{0x9D, 0x34, 0x7F},
- {0x9E, 0x2C, 0x7F},
{0x9F, 0x34, 0x7F},
{0xA0, 0x04, 0x67},
- {0x4F, 0x04, 0x67},
+ {0x50, 0x04, 0x67},
+ {0x4E, 0x2C, 0x7F},
{0x51, 0x04, 0x67},
- {0x3A, 0x24, 0x7F},
{0x92, 0x4C, 0x7F},
{0x93, 0x4C, 0x7F},
{0x94, 0x44, 0x7F},
- {0x95, 0x04, 0x67},
{0x96, 0x34, 0x7F},
{0x97, 0x04, 0x67},
{0x98, 0x34, 0x7F},
- {0x99, 0x34, 0x7F},
- {0x9A, 0x04, 0x67},
- {0x3E, 0x24, 0x7F},
+ {0x99, 0x04, 0x67},
+ {0x1C, 0x24, 0x7F},
+ {0x1D, 0x24, 0x7F},
+ {0x1E, 0x24, 0x7F},
+ {0x1F, 0x24, 0x7F},
{0x6A, 0x04, 0x67},
{0x6B, 0x04, 0x67},
{0x6C, 0x2C, 0x7F},
@@ -134,35 +133,42 @@ constexpr InitialConfig InitialConfigsHoag[] = {
{0x70, 0x04, 0x7F},
{0x71, 0x04, 0x67},
{0x72, 0x04, 0x67},
- {0x65, 0x34, 0x7F},
- {0x66, 0x04, 0x67},
- {0x67, 0x04, 0x267},
+ {0x68, 0x204, 0x267},
{0x5E, 0x05, 0x07},
+ {0x5F, 0x05, 0x07},
{0x17, 0x05, 0x07},
{0x18, 0x05, 0x07},
{0x19, 0x05, 0x07},
{0x1A, 0x05, 0x07},
{0x1B, 0x05, 0x07},
- {0x26, 0x05, 0x07},
- {0x27, 0x05, 0x07},
- {0x28, 0x05, 0x07},
- {0x29, 0x05, 0x07},
- {0x90, 0x05, 0x07},
+ {0x2B, 0x05, 0x07},
{0x32, 0x05, 0x07},
+ {0x52, 0x05, 0x07},
{0x75, 0x05, 0x07},
{0x76, 0x05, 0x07},
- {0x79, 0x05, 0x07},
- {0x7A, 0x05, 0x07},
{0x8E, 0x05, 0x07},
+ {0xAB, 0x05, 0x07},
+ {0xAD, 0x05, 0x07},
+ {0xAE, 0x05, 0x07},
+ {0x06, 0x05, 0x07},
{0x07, 0x05, 0x07},
- {0x87, 0x05, 0x07},
- {0x83, 0x05, 0x07},
+ {0x08, 0x05, 0x07},
+ {0x09, 0x05, 0x07},
+ {0x0A, 0x05, 0x07},
+ {0x0B, 0x05, 0x07},
{0x84, 0x05, 0x07},
{0x7B, 0x05, 0x07},
- {0x7F, 0x05, 0x07},
+ {0x9E, 0x05, 0x07},
+ {0xA1, 0x05, 0x07},
{0x58, 0x00, 0x00},
- {0x50, 0x05, 0x07},
- {0x4E, 0x05, 0x07},
+ {0x59, 0x00, 0x00},
+ {0x4F, 0x05, 0x07},
+ {0x3A, 0x05, 0x07},
+ {0x3B, 0x05, 0x07},
+ {0x3C, 0x05, 0x07},
+ {0x3D, 0x05, 0x07},
+ {0x95, 0x05, 0x07},
+ {0x9A, 0x05, 0x07},
{0x12, 0x05, 0x07},
{0x13, 0x05, 0x07},
{0x14, 0x05, 0x07},
@@ -170,12 +176,19 @@ constexpr InitialConfig InitialConfigsHoag[] = {
{0x16, 0x05, 0x07},
{0x73, 0x05, 0x07},
{0x74, 0x05, 0x07},
+ {0x22, 0x05, 0x07},
+ {0x23, 0x05, 0x07},
+ {0x20, 0x05, 0x07},
+ {0x21, 0x05, 0x07},
{0x24, 0x05, 0x07},
{0x25, 0x05, 0x07},
{0x62, 0x05, 0x07},
- {0x68, 0x05, 0x07},
+ {0x65, 0x05, 0x07},
+ {0x66, 0x05, 0x07},
+ {0x67, 0x05, 0x07},
{0x69, 0x05, 0x07},
+ {0x64, 0x05, 0x07},
{0x63, 0x05, 0x07},
};
-constexpr u32 NumInitialConfigsHoag = (sizeof(InitialConfigsHoag) / sizeof(InitialConfigsHoag[0]));
+constexpr u32 NumInitialConfigsHoag = util::size(InitialConfigsHoag);
diff --git a/stratosphere/boot/source/pinmux/pinmux_initial_configuration_icosa.inc b/stratosphere/boot/source/pinmux/pinmux_initial_configuration_icosa.inc
index 17d9af09e..553f05dc2 100644
--- a/stratosphere/boot/source/pinmux/pinmux_initial_configuration_icosa.inc
+++ b/stratosphere/boot/source/pinmux/pinmux_initial_configuration_icosa.inc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018-2019 Atmosphère-NX
+ * 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,
@@ -98,7 +98,7 @@ constexpr InitialConfig InitialConfigsIcosa[] = {
{0x86, 0x2C, 0x7F},
{0x82, 0x24, 0x7F},
{0x85, 0x34, 0x7F},
- {0x89, 0x24, 0x7F},
+ {0x89, 0x34, 0x7F},
{0x8A, 0x34, 0x7F},
{0x8B, 0x34, 0x7F},
{0x8C, 0x34, 0x7F},
@@ -178,4 +178,4 @@ constexpr InitialConfig InitialConfigsIcosa[] = {
{0x63, 0x05, 0x07},
};
-constexpr u32 NumInitialConfigsIcosa = (sizeof(InitialConfigsIcosa) / sizeof(InitialConfigsIcosa[0]));
+constexpr u32 NumInitialConfigsIcosa = util::size(InitialConfigsIcosa);
diff --git a/stratosphere/boot/source/pinmux/pinmux_initial_configuration_iowa.inc b/stratosphere/boot/source/pinmux/pinmux_initial_configuration_iowa.inc
index 11e7b5722..471db0e55 100644
--- a/stratosphere/boot/source/pinmux/pinmux_initial_configuration_iowa.inc
+++ b/stratosphere/boot/source/pinmux/pinmux_initial_configuration_iowa.inc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018-2019 Atmosphère-NX
+ * 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,
@@ -98,6 +98,7 @@ constexpr InitialConfig InitialConfigsIowa[] = {
{0x09, 0x24, 0x7F},
{0x0A, 0x24, 0x7F},
{0x0B, 0x24, 0x7F},
+ {0x87, 0x04, 0x67},
{0x88, 0x34, 0x7F},
{0x86, 0x2C, 0x7F},
{0x82, 0x24, 0x7F},
@@ -162,7 +163,6 @@ constexpr InitialConfig InitialConfigsIowa[] = {
{0xAD, 0x05, 0x07},
{0xAE, 0x05, 0x07},
{0x07, 0x05, 0x07},
- {0x87, 0x05, 0x07},
{0x83, 0x05, 0x07},
{0x84, 0x05, 0x07},
{0x7B, 0x05, 0x07},
@@ -191,4 +191,4 @@ constexpr InitialConfig InitialConfigsIowa[] = {
{0x63, 0x05, 0x07},
};
-constexpr u32 NumInitialConfigsIowa = (sizeof(InitialConfigsIowa) / sizeof(InitialConfigsIowa[0]));
+constexpr u32 NumInitialConfigsIowa = util::size(InitialConfigsIowa);
diff --git a/stratosphere/boot/source/pinmux/pinmux_initial_drive_pad_configuration.inc b/stratosphere/boot/source/pinmux/pinmux_initial_drive_pad_configuration.inc
index 8d5d2fe14..cef51f40f 100644
--- a/stratosphere/boot/source/pinmux/pinmux_initial_drive_pad_configuration.inc
+++ b/stratosphere/boot/source/pinmux/pinmux_initial_drive_pad_configuration.inc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018-2019 Atmosphère-NX
+ * 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,
@@ -64,4 +64,4 @@ constexpr InitialConfig InitialDrivePadConfigs[] = {
{0x69, 0x51212000, 0xF1F1F000},
};
-constexpr u32 NumInitialDrivePadConfigs = (sizeof(InitialDrivePadConfigs) / sizeof(InitialDrivePadConfigs[0]));
+constexpr u32 NumInitialDrivePadConfigs = util::size(InitialDrivePadConfigs);
diff --git a/stratosphere/boot/source/pinmux/pinmux_initial_drive_pad_configuration_hoag.inc b/stratosphere/boot/source/pinmux/pinmux_initial_drive_pad_configuration_hoag.inc
new file mode 100644
index 000000000..05458c682
--- /dev/null
+++ b/stratosphere/boot/source/pinmux/pinmux_initial_drive_pad_configuration_hoag.inc
@@ -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 .
+ */
+
+constexpr InitialConfig InitialDrivePadConfigsHoag[] = {
+ {0x04, 0x01010000, 0x01F1F000},
+ {0x0D, 0x01010000, 0x01F1F000},
+ {0x10, 0x01010000, 0x01F1F000},
+ {0x12, 0x01010000, 0x01F1F000},
+ {0x13, 0x01010000, 0x01F1F000},
+ {0x14, 0x0001F000, 0x01F1F000},
+ {0x15, 0x0001F000, 0x01F1F000},
+ {0x24, 0x01010000, 0x01F1F000},
+ {0x25, 0x01010000, 0x01F1F000},
+ {0x26, 0x01010000, 0x01F1F000},
+ {0x27, 0x01010000, 0x01F1F000},
+ {0x28, 0x01010000, 0x01F1F000},
+ {0x29, 0x01010000, 0x01F1F000},
+ {0x2A, 0x01010000, 0x01F1F000},
+ {0x2B, 0x01010000, 0x01F1F000},
+ {0x2C, 0x01F1F000, 0x01F1F000},
+ {0x2D, 0x01F1F000, 0x01F1F000},
+ {0x2F, 0x01F1F000, 0x01F1F000},
+ {0x30, 0x01404000, 0x01F1F000},
+ {0x31, 0x0001F000, 0x01F1F000},
+ {0x32, 0x0001F000, 0x01F1F000},
+ {0x33, 0x00004000, 0x01F1F000},
+ {0x34, 0x00004000, 0x01F1F000},
+ {0x35, 0x00007000, 0x01F1F000},
+ {0x36, 0x00007000, 0x01F1F000},
+ {0x46, 0x01010000, 0x01F1F000},
+ {0x47, 0x01010000, 0x01F1F000},
+ {0x4C, 0x01404000, 0x01F1F000},
+ {0x4D, 0x01404000, 0x01F1F000},
+ {0x62, 0x0001F000, 0x01F1F000},
+ {0x63, 0x0001F000, 0x01F1F000},
+ {0x7C, 0x01414000, 0x01F1F000},
+ {0x87, 0x01404000, 0x01F1F000},
+ {0x88, 0x01404000, 0x01F1F000},
+ {0x89, 0x01404000, 0x01F1F000},
+ {0x8A, 0x01404000, 0x01F1F000},
+ {0x6D, 0x00000000, 0xF0000000},
+ {0x6E, 0x00000000, 0xF0000000},
+ {0x6F, 0x00000000, 0xF0000000},
+ {0x70, 0x00000000, 0xF0000000},
+ {0x71, 0x00000000, 0xF0000000},
+ {0x72, 0x00000000, 0xF0000000},
+ {0x73, 0x00000000, 0xF0000000},
+ {0x74, 0x00000000, 0xF0000000},
+ {0x75, 0x00000000, 0xF0000000},
+ {0x76, 0x00000000, 0xF0000000},
+ {0x69, 0x51212000, 0xF1F1F000},
+};
+
+constexpr u32 NumInitialDrivePadConfigsHoag = util::size(InitialDrivePadConfigsHoag);
diff --git a/stratosphere/boot/source/pinmux/pinmux_map.inc b/stratosphere/boot/source/pinmux/pinmux_map.inc
index 9890dd9e7..f6a18c37f 100644
--- a/stratosphere/boot/source/pinmux/pinmux_map.inc
+++ b/stratosphere/boot/source/pinmux/pinmux_map.inc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018-2019 Atmosphère-NX
+ * 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,
@@ -205,7 +205,7 @@ constexpr Definition Map[] = {
{0x000032C4, 0x1F2FF, 0x00}, /* Sdmmc2Dqsb */
};
-constexpr u32 PadNameMax = (sizeof(Map) / sizeof(Map[0]));
+constexpr u32 PadNameMax = util::size(Map);
constexpr DrivePadDefinition DrivePadMap[] = {
{0x000008E4, 0x01F1F000}, /* AlsProxInt */
@@ -358,4 +358,4 @@ constexpr DrivePadDefinition DrivePadMap[] = {
{0x00000B6C, 0x01F1F000}, /* WifiWakeAp */
};
-constexpr u32 DrivePadNameMax = (sizeof(DrivePadMap) / sizeof(DrivePadMap[0]));
+constexpr u32 DrivePadNameMax = util::size(DrivePadMap);
diff --git a/stratosphere/ro/source/impl/ro_nrr_utils.cpp b/stratosphere/ro/source/impl/ro_nrr_utils.cpp
index 3293a66dc..c814df6db 100644
--- a/stratosphere/ro/source/impl/ro_nrr_utils.cpp
+++ b/stratosphere/ro/source/impl/ro_nrr_utils.cpp
@@ -193,7 +193,7 @@ namespace ams::ro::impl {
}
/* Utilities for working with NRRs. */
- Result MapAndValidateNrr(NrrHeader **out_header, u64 *out_mapped_code_address, Handle process_handle, ncm::ProgramId program_id, u64 nrr_heap_address, u64 nrr_heap_size, ModuleType expected_type, bool enforce_type) {
+ Result MapAndValidateNrr(NrrHeader **out_header, u64 *out_mapped_code_address, void *out_hash, size_t out_hash_size, Handle process_handle, ncm::ProgramId program_id, u64 nrr_heap_address, u64 nrr_heap_size, ModuleType expected_type, bool enforce_type) {
map::MappedCodeMemory nrr_mcm(ResultInternalError{});
/* First, map the NRR. */
@@ -214,6 +214,9 @@ namespace ams::ro::impl {
nrr_map.Invalidate();
nrr_mcm.Invalidate();
+ /* Save a copy of the hash that we verified. */
+ crypto::GenerateSha256Hash(out_hash, out_hash_size, nrr_header->GetSignedArea(), nrr_header->GetSignedAreaSize());
+
*out_header = nrr_header;
*out_mapped_code_address = code_address;
return ResultSuccess();
@@ -225,4 +228,53 @@ namespace ams::ro::impl {
return ResultSuccess();
}
+ bool ValidateNrrHashTableEntry(const void *signed_area, size_t signed_area_size, size_t hashes_offset, size_t num_hashes, const void *nrr_hash, const u8 *hash_table, const void *desired_hash) {
+ crypto::Sha256Generator sha256;
+ sha256.Initialize();
+
+
+ /* Hash data before the hash table. */
+ const size_t pre_hash_table_size = hashes_offset - NrrHeader::GetSignedAreaOffset();
+ sha256.Update(signed_area, pre_hash_table_size);
+
+ /* Hash the hash table, checking if the desired hash exists inside it. */
+ size_t remaining_size = signed_area_size - pre_hash_table_size;
+ bool found_hash = false;
+ for (size_t i = 0; i < num_hashes; i++) {
+ /* Get the current hash. */
+ u8 cur_hash[crypto::Sha256Generator::HashSize];
+ std::memcpy(cur_hash, hash_table, sizeof(cur_hash));
+
+ /* Hash the current hash. */
+ sha256.Update(cur_hash, sizeof(cur_hash));
+
+ /* Check if the current hash is our target. */
+ found_hash |= std::memcmp(cur_hash, desired_hash, sizeof(cur_hash)) == 0;
+
+ /* Advance our pointers. */
+ hash_table += sizeof(cur_hash);
+ remaining_size -= sizeof(cur_hash);
+ }
+
+ /* Data after the hash table should be all zeroes. */
+ u8 work_buf[crypto::Sha256Generator::HashSize];
+ {
+ crypto::ClearMemory(work_buf, sizeof(work_buf));
+ while (remaining_size > 0) {
+ const size_t cur_size = std::min(remaining_size, sizeof(work_buf));
+ sha256.Update(work_buf, cur_size);
+ remaining_size -= cur_size;
+ }
+ }
+
+ /* Validate the final hash. */
+ sha256.GetHash(work_buf, sizeof(work_buf));
+
+ /* Use & operator to avoid short circuiting. */
+ const bool is_valid = found_hash & (std::memcmp(work_buf, nrr_hash, sizeof(work_buf)) == 0);
+
+ /* Return result. */
+ return is_valid;
+ }
+
}
diff --git a/stratosphere/ro/source/impl/ro_nrr_utils.hpp b/stratosphere/ro/source/impl/ro_nrr_utils.hpp
index 7bb0018b2..3a675ad8f 100644
--- a/stratosphere/ro/source/impl/ro_nrr_utils.hpp
+++ b/stratosphere/ro/source/impl/ro_nrr_utils.hpp
@@ -20,7 +20,9 @@
namespace ams::ro::impl {
/* Utilities for working with NRRs. */
- Result MapAndValidateNrr(NrrHeader **out_header, u64 *out_mapped_code_address, Handle process_handle, ncm::ProgramId program_id, u64 nrr_heap_address, u64 nrr_heap_size, ModuleType expected_type, bool enforce_type);
+ Result MapAndValidateNrr(NrrHeader **out_header, u64 *out_mapped_code_address, void *out_hash, size_t out_hash_size, Handle process_handle, ncm::ProgramId program_id, u64 nrr_heap_address, u64 nrr_heap_size, ModuleType expected_type, bool enforce_type);
Result UnmapNrr(Handle process_handle, const NrrHeader *header, u64 nrr_heap_address, u64 nrr_heap_size, u64 mapped_code_address);
+ bool ValidateNrrHashTableEntry(const void *signed_area, size_t signed_area_size, size_t hashes_offset, size_t num_hashes, const void *nrr_hash, const u8 *hash_table, const void *desired_hash);
+
}
\ No newline at end of file
diff --git a/stratosphere/ro/source/impl/ro_service_impl.cpp b/stratosphere/ro/source/impl/ro_service_impl.cpp
index 99f00e24c..88ddee35a 100644
--- a/stratosphere/ro/source/impl/ro_service_impl.cpp
+++ b/stratosphere/ro/source/impl/ro_service_impl.cpp
@@ -23,28 +23,28 @@ namespace ams::ro::impl {
namespace {
/* Convenience definitions. */
- constexpr size_t MaxSessions = 0x4;
+ constexpr size_t MaxSessions = 0x3; /* 2 official sessions (applet + application, 1 homebrew session). */
constexpr size_t MaxNrrInfos = 0x40;
constexpr size_t MaxNroInfos = 0x40;
/* Types. */
struct Sha256Hash {
- u8 hash[SHA256_HASH_SIZE];
+ u8 hash[crypto::Sha256Generator::HashSize];
bool operator==(const Sha256Hash &o) const {
- return std::memcmp(this, &o, sizeof(*this)) == 0;
+ return std::memcmp(this, std::addressof(o), sizeof(*this)) == 0;
}
bool operator!=(const Sha256Hash &o) const {
- return std::memcmp(this, &o, sizeof(*this)) != 0;
+ return std::memcmp(this, std::addressof(o), sizeof(*this)) != 0;
}
bool operator<(const Sha256Hash &o) const {
- return std::memcmp(this, &o, sizeof(*this)) < 0;
+ return std::memcmp(this, std::addressof(o), sizeof(*this)) < 0;
}
bool operator>(const Sha256Hash &o) const {
- return std::memcmp(this, &o, sizeof(*this)) > 0;
+ return std::memcmp(this, std::addressof(o), sizeof(*this)) > 0;
}
};
- static_assert(sizeof(Sha256Hash) == sizeof(Sha256Hash::hash), "Sha256Hash definition!");
+ static_assert(sizeof(Sha256Hash) == sizeof(Sha256Hash::hash));
struct NroInfo {
u64 base_address;
@@ -55,18 +55,25 @@ namespace ams::ro::impl {
u64 code_size;
u64 rw_size;
ModuleId module_id;
- bool in_use;
};
struct NrrInfo {
- const NrrHeader *header;
+ const NrrHeader *mapped_header;
u64 nrr_heap_address;
u64 nrr_heap_size;
u64 mapped_code_address;
- bool in_use;
+
+ /* Verification. */
+ u32 cached_signed_area_size;
+ u32 cached_hashes_offset;
+ u32 cached_num_hashes;
+ u8 cached_signed_area[sizeof(NrrHeader) - NrrHeader::GetSignedAreaOffset()];
+ Sha256Hash signed_area_hash;
};
struct ProcessContext {
+ bool nro_in_use[MaxNroInfos];
+ bool nrr_in_use[MaxNrrInfos];
NroInfo nro_infos[MaxNroInfos];
NrrInfo nrr_infos[MaxNrrInfos];
Handle process_handle;
@@ -93,7 +100,7 @@ namespace ams::ro::impl {
Result GetNrrInfoByAddress(NrrInfo **out, u64 nrr_heap_address) {
for (size_t i = 0; i < MaxNrrInfos; i++) {
- if (this->nrr_infos[i].in_use && this->nrr_infos[i].nrr_heap_address == nrr_heap_address) {
+ if (this->nrr_in_use[i] && this->nrr_infos[i].nrr_heap_address == nrr_heap_address) {
if (out != nullptr) {
*out = &this->nrr_infos[i];
}
@@ -105,7 +112,7 @@ namespace ams::ro::impl {
Result GetFreeNrrInfo(NrrInfo **out) {
for (size_t i = 0; i < MaxNrrInfos; i++) {
- if (!this->nrr_infos[i].in_use) {
+ if (!this->nrr_in_use[i]) {
if (out != nullptr) {
*out = &this->nrr_infos[i];
}
@@ -117,7 +124,7 @@ namespace ams::ro::impl {
Result GetNroInfoByAddress(NroInfo **out, u64 nro_address) {
for (size_t i = 0; i < MaxNroInfos; i++) {
- if (this->nro_infos[i].in_use && this->nro_infos[i].base_address == nro_address) {
+ if (this->nro_in_use[i] && this->nro_infos[i].base_address == nro_address) {
if (out != nullptr) {
*out = &this->nro_infos[i];
}
@@ -129,7 +136,7 @@ namespace ams::ro::impl {
Result GetNroInfoByModuleId(NroInfo **out, const ModuleId *module_id) {
for (size_t i = 0; i < MaxNroInfos; i++) {
- if (this->nro_infos[i].in_use && std::memcmp(&this->nro_infos[i].module_id, module_id, sizeof(*module_id)) == 0) {
+ if (this->nro_in_use[i] && std::memcmp(&this->nro_infos[i].module_id, module_id, sizeof(*module_id)) == 0) {
if (out != nullptr) {
*out = &this->nro_infos[i];
}
@@ -141,7 +148,7 @@ namespace ams::ro::impl {
Result GetFreeNroInfo(NroInfo **out) {
for (size_t i = 0; i < MaxNroInfos; i++) {
- if (!this->nro_infos[i].in_use) {
+ if (!this->nro_in_use[i]) {
if (out != nullptr) {
*out = &this->nro_infos[i];
}
@@ -154,14 +161,43 @@ namespace ams::ro::impl {
Result ValidateHasNroHash(const NroHeader *nro_header) const {
/* Calculate hash. */
Sha256Hash hash;
- sha256CalculateHash(&hash, nro_header, nro_header->GetSize());
+ crypto::GenerateSha256Hash(std::addressof(hash), sizeof(hash), nro_header, nro_header->GetSize());
for (size_t i = 0; i < MaxNrrInfos; i++) {
- if (this->nrr_infos[i].in_use) {
- const NrrHeader *nrr_header = this->nrr_infos[i].header;
- const Sha256Hash *nro_hashes = reinterpret_cast(nrr_header->GetHashes());
- R_UNLESS(!std::binary_search(nro_hashes, nro_hashes + nrr_header->GetNumHashes(), hash), ResultSuccess());
+ /* Ensure we only check NRRs that are used. */
+ if (!this->nrr_in_use[i]) {
+ continue;
}
+
+ /* Get the mapped header, ensure that it has hashes. */
+ const NrrHeader *mapped_nrr_header = this->nrr_infos[i].mapped_header;
+ const size_t mapped_num_hashes = mapped_nrr_header->GetNumHashes();
+ if (mapped_num_hashes == 0) {
+ continue;
+ }
+
+ /* Locate the hash within the mapped array. */
+ const Sha256Hash *mapped_nro_hashes_start = reinterpret_cast