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(mapped_nrr_header->GetHashes()); + const Sha256Hash *mapped_nro_hashes_end = mapped_nro_hashes_start + mapped_nrr_header->GetNumHashes(); + + const Sha256Hash *mapped_lower_bound = std::lower_bound(mapped_nro_hashes_start, mapped_nro_hashes_end, hash); + if (mapped_lower_bound == mapped_nro_hashes_end || (*mapped_lower_bound != hash)) { + continue; + } + + /* Check that the hash entry is valid, since our heuristic passed. */ + const void *nrr_hash = std::addressof(this->nrr_infos[i].signed_area_hash); + const void *signed_area = this->nrr_infos[i].cached_signed_area; + const size_t signed_area_size = this->nrr_infos[i].cached_signed_area_size; + const size_t hashes_offset = this->nrr_infos[i].cached_hashes_offset; + const size_t num_hashes = this->nrr_infos[i].cached_num_hashes; + const u8 *hash_table = reinterpret_cast(mapped_nro_hashes_start); + if (!ValidateNrrHashTableEntry(signed_area, signed_area_size, hashes_offset, num_hashes, nrr_hash, hash_table, std::addressof(hash))) { + continue; + } + + /* The hash is valid! */ + return ResultSuccess(); } return ResultNotAuthorized(); @@ -227,6 +263,18 @@ namespace ams::ro::impl { *out_rw_size = rw_size; return ResultSuccess(); } + + void SetNrrInfoInUse(const NrrInfo *info, bool in_use) { + AMS_ASSERT(std::addressof(this->nrr_infos[0]) <= info && info <= std::addressof(this->nrr_infos[MaxNrrInfos - 1])); + const size_t index = info - std::addressof(this->nrr_infos[0]); + this->nrr_in_use[index] = in_use; + } + + void SetNroInfoInUse(const NroInfo *info, bool in_use) { + AMS_ASSERT(std::addressof(this->nro_infos[0]) <= info && info <= std::addressof(this->nro_infos[MaxNroInfos - 1])); + const size_t index = info - std::addressof(this->nro_infos[0]); + this->nro_in_use[index] = in_use; + } }; /* Globals. */ @@ -275,8 +323,8 @@ namespace ams::ro::impl { if (context != nullptr) { if (context->process_handle != INVALID_HANDLE) { for (size_t i = 0; i < MaxNrrInfos; i++) { - if (context->nrr_infos[i].in_use) { - UnmapNrr(context->process_handle, context->nrr_infos[i].header, context->nrr_infos[i].nrr_heap_address, context->nrr_infos[i].nrr_heap_size, context->nrr_infos[i].mapped_code_address); + if (context->nrr_in_use[i]) { + UnmapNrr(context->process_handle, context->nrr_infos[i].mapped_header, context->nrr_infos[i].nrr_heap_address, context->nrr_infos[i].nrr_heap_size, context->nrr_infos[i].mapped_code_address); } } svcCloseHandle(context->process_handle); @@ -379,18 +427,29 @@ namespace ams::ro::impl { NrrInfo *nrr_info = nullptr; R_TRY(context->GetFreeNrrInfo(&nrr_info)); + /* Prepare to cache the NRR's signature hash. */ + Sha256Hash signed_area_hash; + ON_SCOPE_EXIT { crypto::ClearMemory(std::addressof(signed_area_hash), sizeof(signed_area_hash)); }; + /* Map. */ NrrHeader *header = nullptr; u64 mapped_code_address = 0; - R_TRY(MapAndValidateNrr(&header, &mapped_code_address, context->process_handle, program_id, nrr_address, nrr_size, expected_type, enforce_type)); + R_TRY(MapAndValidateNrr(&header, &mapped_code_address, std::addressof(signed_area_hash), sizeof(signed_area_hash), context->process_handle, program_id, nrr_address, nrr_size, expected_type, enforce_type)); /* Set NRR info. */ - nrr_info->in_use = true; - nrr_info->header = header; + context->SetNrrInfoInUse(nrr_info, true); + nrr_info->mapped_header = header; nrr_info->nrr_heap_address = nrr_address; nrr_info->nrr_heap_size = nrr_size; nrr_info->mapped_code_address = mapped_code_address; + nrr_info->cached_signed_area_size = header->GetSignedAreaSize(); + nrr_info->cached_hashes_offset = header->GetHashesOffset(); + nrr_info->cached_num_hashes = header->GetNumHashes(); + + std::memcpy(nrr_info->cached_signed_area, header->GetSignedArea(), std::min(sizeof(nrr_info->cached_signed_area), header->GetHashesOffset() - header->GetSignedAreaOffset())); + std::memcpy(std::addressof(nrr_info->signed_area_hash), std::addressof(signed_area_hash), sizeof(signed_area_hash)); + return ResultSuccess(); } @@ -410,10 +469,10 @@ namespace ams::ro::impl { const NrrInfo nrr_backup = *nrr_info; { /* Nintendo does this unconditionally, whether or not the actual unmap succeeds. */ - nrr_info->in_use = false; + context->SetNrrInfoInUse(nrr_info, false); std::memset(nrr_info, 0, sizeof(*nrr_info)); } - return UnmapNrr(context->process_handle, nrr_backup.header, nrr_backup.nrr_heap_address, nrr_backup.nrr_heap_size, nrr_backup.mapped_code_address); + return UnmapNrr(context->process_handle, nrr_backup.mapped_header, nrr_backup.nrr_heap_address, nrr_backup.nrr_heap_size, nrr_backup.mapped_code_address); } Result LoadNro(u64 *out_address, size_t context_id, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size) { @@ -455,9 +514,9 @@ namespace ams::ro::impl { unmap_guard.Cancel(); } + context->SetNroInfoInUse(nro_info, true); nro_info->code_size = rx_size + ro_size; nro_info->rw_size = rw_size; - nro_info->in_use = true; *out_address = nro_info->base_address; return ResultSuccess(); } @@ -478,7 +537,7 @@ namespace ams::ro::impl { const NroInfo nro_backup = *nro_info; { /* Nintendo does this unconditionally, whether or not the actual unmap succeeds. */ - nro_info->in_use = false; + context->SetNroInfoInUse(nro_info, false); std::memset(nro_info, 0, sizeof(*nro_info)); } return UnmapNro(context->process_handle, nro_backup.base_address, nro_backup.nro_heap_address, nro_backup.bss_heap_address, nro_backup.bss_heap_size, nro_backup.code_size, nro_backup.rw_size); @@ -490,11 +549,12 @@ namespace ams::ro::impl { const ProcessContext *context = GetContextByProcessId(process_id); if (context != nullptr) { for (size_t i = 0; i < MaxNroInfos && count < max_out_count; i++) { - const NroInfo *nro_info = &context->nro_infos[i]; - if (!nro_info->in_use) { + if (!context->nro_in_use[i]) { continue; } + const NroInfo *nro_info = &context->nro_infos[i]; + /* Just copy out the info. */ LoaderModuleInfo *out_info = &out_infos[count++]; memcpy(out_info->build_id, &nro_info->module_id, sizeof(nro_info->module_id)); diff --git a/stratosphere/spl/source/spl_api_impl.cpp b/stratosphere/spl/source/spl_api_impl.cpp index 2416b3dee..13a56540e 100644 --- a/stratosphere/spl/source/spl_api_impl.cpp +++ b/stratosphere/spl/source/spl_api_impl.cpp @@ -151,80 +151,6 @@ namespace ams::spl::impl { R_ABORT_UNLESS(svcMapDeviceAddressSpaceAligned(g_se_das_hnd, dd::GetCurrentProcessHandle(), work_buffer_addr, sizeof(g_work_buffer), g_se_mapped_work_buffer_addr, 3)); } - /* RSA OAEP implementation helpers. */ - void CalcMgf1AndXor(void *dst, size_t dst_size, const void *src, size_t src_size) { - uint8_t *dst_u8 = reinterpret_cast(dst); - - u32 ctr = 0; - while (dst_size > 0) { - const size_t cur_size = std::min(size_t(SHA256_HASH_SIZE), dst_size); - dst_size -= cur_size; - - u32 ctr_be = __builtin_bswap32(ctr++); - u8 hash[SHA256_HASH_SIZE]; - { - Sha256Context ctx; - sha256ContextCreate(&ctx); - sha256ContextUpdate(&ctx, src, src_size); - sha256ContextUpdate(&ctx, &ctr_be, sizeof(ctr_be)); - sha256ContextGetHash(&ctx, hash); - } - - for (size_t i = 0; i < cur_size; i++) { - *(dst_u8++) ^= hash[i]; - } - } - } - - size_t DecodeRsaOaep(void *dst, size_t dst_size, const void *label_digest, size_t label_digest_size, const void *src, size_t src_size) { - /* Very basic validation. */ - if (dst_size == 0 || src_size != 0x100 || label_digest_size != SHA256_HASH_SIZE) { - return 0; - } - - u8 block[0x100]; - std::memcpy(block, src, sizeof(block)); - - /* First, validate byte 0 == 0, and unmask DB. */ - int invalid = block[0]; - u8 *salt = block + 1; - u8 *db = salt + SHA256_HASH_SIZE; - CalcMgf1AndXor(salt, SHA256_HASH_SIZE, db, src_size - (1 + SHA256_HASH_SIZE)); - CalcMgf1AndXor(db, src_size - (1 + SHA256_HASH_SIZE), salt, SHA256_HASH_SIZE); - - /* Validate label digest. */ - for (size_t i = 0; i < SHA256_HASH_SIZE; i++) { - invalid |= db[i] ^ reinterpret_cast(label_digest)[i]; - } - - /* Locate message after 00...0001 padding. */ - const u8 *padded_msg = db + SHA256_HASH_SIZE; - size_t padded_msg_size = src_size - (1 + 2 * SHA256_HASH_SIZE); - size_t msg_ind = 0; - int not_found = 1; - int wrong_padding = 0; - size_t i = 0; - while (i < padded_msg_size) { - int zero = (padded_msg[i] == 0); - int one = (padded_msg[i] == 1); - msg_ind += static_cast(not_found & one) * (++i); - not_found &= ~one; - wrong_padding |= (not_found & ~zero); - } - - if (invalid | not_found | wrong_padding) { - return 0; - } - - /* Copy message out. */ - size_t msg_size = padded_msg_size - msg_ind; - if (msg_size > dst_size) { - return 0; - } - std::memcpy(dst, padded_msg + msg_ind, msg_size); - return msg_size; - } - /* Internal RNG functionality. */ Result GenerateRandomBytesInternal(void *out, size_t size) { if (!g_drbg.GenerateRandomBytes(out, size)) { @@ -793,7 +719,7 @@ namespace ams::spl::impl { /* Nintendo doesn't check this result code, but we will. */ R_TRY(SecureExpMod(g_work_buffer, 0x100, base, base_size, mod, mod_size, smc::SecureExpModMode::Lotus)); - size_t data_size = DecodeRsaOaep(dst, dst_size, label_digest, label_digest_size, g_work_buffer, 0x100); + size_t data_size = crypto::DecodeRsa2048OaepSha256(dst, dst_size, label_digest, label_digest_size, g_work_buffer, 0x100); R_UNLESS(data_size > 0, spl::ResultDecryptionFailed()); *out_size = static_cast(data_size);