diff --git a/docs/changelog.md b/docs/changelog.md index d3f22406..c6c1a5ce 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,4 +1,16 @@ # Changelog +## 0.10.5 ++ Changes were made to the way fs.mitm builds images when providing a layeredfs romfs. + + Building romfs metadata previously had a memory cost of about ~4-5x the file table size. + + This caused games that have particularly enormous file metadata tables (> 4 MB) to exhaust fs.mitm's 16 MB memory pool. + + The code that creates romfs images was thus changed to be significantly more memory efficient, again. + + Memory requirements have been lowered from ~4x file table size to ~2x file table size + 0.5 MB. + + There is a slight speed penalty to this, but testing on Football Manager 2020 only took an extra ~1.5 seconds for the game to boot with many modded files. + + This shouldn't be noticeable thanks to the async changes made in 0.10.2. + + If you encounter a game that exhausts ams.mitm's memory (crashing it) when loading layeredfs mods, please contact SciresM. + + Romfs building can be made even more memory efficient, but unless games show up with even more absurdly huge file tables it seems not worth the speed trade-off. ++ A bug was fixed that caused Atmosphere's fatal error context to not dump TLS for certain processes. ++ General system stability improvements to enhance the user's experience. ## 0.10.4 + With major thanks to @Adubbz for his work, the NCM system module has now been re-implemented. + This was a major stepping stone towards the goal of having implementations everything in the Switch's package1/package2 firmware. diff --git a/libraries/.gitrepo b/libraries/.gitrepo index 0d0b453e..c3485737 100644 --- a/libraries/.gitrepo +++ b/libraries/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = https://github.com/Atmosphere-NX/Atmosphere-libs branch = master - commit = a4ce117292cc86e951d82666f4e13bc1dc40443f - parent = 95d5375158f6df5376ce876e6ed8c22150ad88ff + commit = 38fc51c6ef35dfd9d0bd02446144317d85fc0f5d + parent = 426257d4ae37d8c4517fba814562bd80f52093ef method = merge cmdver = 0.4.1 diff --git a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_process_page_table.hpp b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_process_page_table.hpp index 1376a865..2df7f508 100644 --- a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_process_page_table.hpp +++ b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_process_page_table.hpp @@ -84,6 +84,10 @@ namespace ams::kern::arch::arm64 { return this->page_table.UnmapPages(addr, num_pages, state); } + Result MakeAndOpenPageGroup(KPageGroup *out, KProcessAddress address, size_t num_pages, u32 state_mask, u32 state, u32 perm_mask, u32 perm, u32 attr_mask, u32 attr) { + return this->page_table.MakeAndOpenPageGroup(out, address, num_pages, state_mask, state, perm_mask, perm, attr_mask, attr); + } + bool GetPhysicalAddress(KPhysicalAddress *out, KProcessAddress address) const { return this->page_table.GetPhysicalAddress(out, address); } @@ -96,12 +100,23 @@ namespace ams::kern::arch::arm64 { KProcessAddress GetAliasRegionStart() const { return this->page_table.GetAliasRegionStart(); } KProcessAddress GetStackRegionStart() const { return this->page_table.GetStackRegionStart(); } KProcessAddress GetKernelMapRegionStart() const { return this->page_table.GetKernelMapRegionStart(); } + KProcessAddress GetAliasCodeRegionStart() const { return this->page_table.GetAliasCodeRegionStart(); } size_t GetAddressSpaceSize() const { return this->page_table.GetAddressSpaceSize(); } size_t GetHeapRegionSize() const { return this->page_table.GetHeapRegionSize(); } size_t GetAliasRegionSize() const { return this->page_table.GetAliasRegionSize(); } size_t GetStackRegionSize() const { return this->page_table.GetStackRegionSize(); } size_t GetKernelMapRegionSize() const { return this->page_table.GetKernelMapRegionSize(); } + size_t GetAliasCodeRegionSize() const { return this->page_table.GetAliasCodeRegionSize(); } + + KPhysicalAddress GetHeapPhysicalAddress(KVirtualAddress address) const { + /* TODO: Better way to convert address type? */ + return this->page_table.GetHeapPhysicalAddress(address); + } + + KBlockInfoManager *GetBlockInfoManager() { + return this->page_table.GetBlockInfoManager(); + } }; } diff --git a/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_system_control.hpp b/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_system_control.hpp index 499159bb..45fa322a 100644 --- a/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_system_control.hpp +++ b/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_system_control.hpp @@ -63,6 +63,9 @@ namespace ams::kern::board::nintendo::nx { /* Power management. */ static void SleepSystem(); static NORETURN void StopSystem(); + + /* User access. */ + static void CallSecureMonitorFromUser(ams::svc::lp64::SecureMonitorArguments *args); }; } \ No newline at end of file diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_auto_object.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_auto_object.hpp index 460dbe0f..2ffc2869 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_auto_object.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_auto_object.hpp @@ -222,6 +222,8 @@ namespace ams::kern { KScopedAutoObject(o).Swap(*this); } + constexpr ALWAYS_INLINE T *GetPointerUnsafe() { return this->obj; } + constexpr ALWAYS_INLINE bool IsNull() const { return this->obj == nullptr; } constexpr ALWAYS_INLINE bool IsNotNull() const { return this->obj != nullptr; } }; diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_client_port.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_client_port.hpp new file mode 100644 index 00000000..29768d90 --- /dev/null +++ b/libraries/libmesosphere/include/mesosphere/kern_k_client_port.hpp @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::kern { + + class KPort; + + class KClientPort final : public KSynchronizationObject { + MESOSPHERE_AUTOOBJECT_TRAITS(KClientPort, KSynchronizationObject); + private: + std::atomic num_sessions; + std::atomic peak_sessions; + s32 max_sessions; + KPort *parent; + public: + constexpr KClientPort() : num_sessions(), peak_sessions(), max_sessions(), parent() { /* ... */ } + virtual ~KClientPort() { /* ... */ } + + void Initialize(KPort *parent, s32 max_sessions); + + constexpr const KPort *GetParent() const { return this->parent; } + + bool IsLight() const; + + /* Overridden virtual functions. */ + virtual void Destroy() override; + virtual bool IsSignaled() const override; + + /* TODO: More of KClientPort. */ + }; + +} diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_client_session.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_client_session.hpp new file mode 100644 index 00000000..c6eef389 --- /dev/null +++ b/libraries/libmesosphere/include/mesosphere/kern_k_client_session.hpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::kern { + + class KSession; + + class KClientSession final : public KAutoObjectWithSlabHeapAndContainer { + MESOSPHERE_AUTOOBJECT_TRAITS(KClientSession, KAutoObject); + private: + KSession *parent; + public: + constexpr KClientSession() : parent() { /* ... */ } + virtual ~KClientSession() { /* ... */ } + + void Initialize(KSession *parent) { + /* Set member variables. */ + this->parent = parent; + } + + static void PostDestroy(uintptr_t arg) { /* ... */ } + + constexpr const KSession *GetParent() const { return this->parent; } + + /* TODO: More of KClientSession. */ + }; + +} diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_handle_table.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_handle_table.hpp index 6f8ceeeb..d6ba17cd 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_handle_table.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_handle_table.hpp @@ -130,11 +130,11 @@ namespace ams::kern { MESOSPHERE_ASSERT_THIS(); /* Handle pseudo-handles. */ - if constexpr (std::is_same::value) { + if constexpr (std::is_base_of::value) { if (handle == ams::svc::PseudoHandle::CurrentProcess) { return GetCurrentProcessPointer(); } - } else if constexpr (std::is_same::value) { + } else if constexpr (std::is_base_of::value) { if (handle == ams::svc::PseudoHandle::CurrentThread) { return GetCurrentThreadPointer(); } @@ -156,11 +156,11 @@ namespace ams::kern { static_assert(!std::is_base_of::value); /* Handle pseudo-handles. */ - if constexpr (std::is_same::value) { + if constexpr (std::is_base_of::value) { if (handle == ams::svc::PseudoHandle::CurrentProcess) { return GetCurrentProcessPointer(); } - } else if constexpr (std::is_same::value) { + } else if constexpr (std::is_base_of::value) { if (handle == ams::svc::PseudoHandle::CurrentThread) { return GetCurrentThreadPointer(); } @@ -201,7 +201,7 @@ namespace ams::kern { template ALWAYS_INLINE void Register(ams::svc::Handle handle, T *obj) { static_assert(std::is_base_of::value); - return this->Add(handle, obj, obj->GetTypeObj().GetClassToken()); + return this->Register(handle, obj, obj->GetTypeObj().GetClassToken()); } private: NOINLINE Result Add(ams::svc::Handle *out_handle, KAutoObject *obj, u16 type); @@ -278,7 +278,7 @@ namespace ams::kern { return entry; } - constexpr NOINLINE KAutoObject *GetObjectImpl(ams::svc::Handle handle) const { + constexpr ALWAYS_INLINE KAutoObject *GetObjectImpl(ams::svc::Handle handle) const { MESOSPHERE_ASSERT_THIS(); /* Handles must not have reserved bits set. */ @@ -293,7 +293,7 @@ namespace ams::kern { } } - constexpr NOINLINE KAutoObject *GetObjectByIndexImpl(ams::svc::Handle *out_handle, size_t index) const { + constexpr ALWAYS_INLINE KAutoObject *GetObjectByIndexImpl(ams::svc::Handle *out_handle, size_t index) const { MESOSPHERE_ASSERT_THIS(); /* Index must be in bounds. */ @@ -310,6 +310,49 @@ namespace ams::kern { *out_handle = EncodeHandle(index, entry->GetLinearId()); return entry->GetObject(); } + + template + ALWAYS_INLINE bool GetMultipleObjects(T **out, const ams::svc::Handle *handles, size_t num_handles) const { + /* Try to convert and open all the handles. */ + size_t num_opened; + { + /* Lock the table. */ + KScopedDisableDispatch dd; + KScopedSpinLock lk(this->lock); + for (num_opened = 0; num_opened < num_handles; num_opened++) { + /* Get the current handle. */ + const auto cur_handle = handles[num_opened]; + + /* Get the object for the current handle. */ + KAutoObject *cur_object = this->GetObjectImpl(cur_handle); + if (AMS_UNLIKELY(cur_object == nullptr)) { + break; + } + + /* Cast the current object to the desired type. */ + T *cur_t = cur_object->DynamicCast(); + if (AMS_UNLIKELY(cur_t == nullptr)) { + break; + } + + /* Open a reference to the current object. */ + cur_t->Open(); + out[num_opened] = cur_t; + } + } + + /* If we converted every object, succeed. */ + if (AMS_LIKELY(num_opened == num_handles)) { + return true; + } + + /* If we didn't convert entry object, close the ones we opened. */ + for (size_t i = 0; i < num_opened; i++) { + out[i]->Close(); + } + + return false; + } }; } diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_light_client_session.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_light_client_session.hpp new file mode 100644 index 00000000..474ae35e --- /dev/null +++ b/libraries/libmesosphere/include/mesosphere/kern_k_light_client_session.hpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::kern { + + class KLightSession; + + class KLightClientSession final : public KAutoObjectWithSlabHeapAndContainer { + MESOSPHERE_AUTOOBJECT_TRAITS(KLightClientSession, KAutoObject); + private: + KLightSession *parent; + public: + constexpr KLightClientSession() : parent() { /* ... */ } + virtual ~KLightClientSession() { /* ... */ } + + void Initialize(KLightSession *parent) { + /* Set member variables. */ + this->parent = parent; + } + + static void PostDestroy(uintptr_t arg) { /* ... */ } + + constexpr const KLightSession *GetParent() const { return this->parent; } + + /* TODO: More of KLightClientSession. */ + }; + +} diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_light_server_session.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_light_server_session.hpp new file mode 100644 index 00000000..7fcb23a1 --- /dev/null +++ b/libraries/libmesosphere/include/mesosphere/kern_k_light_server_session.hpp @@ -0,0 +1,47 @@ +/* + * 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::kern { + + class KLightSession; + + class KLightServerSession final : public KAutoObjectWithSlabHeapAndContainer, public util::IntrusiveListBaseNode { + MESOSPHERE_AUTOOBJECT_TRAITS(KLightServerSession, KAutoObject); + private: + KLightSession *parent; + KThreadQueue request_queue; + KThreadQueue server_queue; + KThread *current_request; + KThread *server_thread; + public: + constexpr KLightServerSession() : parent(), request_queue(), server_queue(), current_request(), server_thread() { /* ... */ } + virtual ~KLightServerSession() { /* ... */ } + + void Initialize(KLightSession *parent); + + static void PostDestroy(uintptr_t arg) { /* ... */ } + + constexpr const KLightSession *GetParent() const { return this->parent; } + + /* TODO: More of KLightServerSession. */ + }; + +} diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_light_session.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_light_session.hpp index 5edd8899..b4257a15 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_light_session.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_light_session.hpp @@ -16,14 +16,52 @@ #pragma once #include #include +#include +#include #include namespace ams::kern { + class KClientPort; + class KProcess; + class KLightSession final : public KAutoObjectWithSlabHeapAndContainer { MESOSPHERE_AUTOOBJECT_TRAITS(KLightSession, KAutoObject); + private: + enum class State : u8 { + Invalid = 0, + Normal = 1, + ClientClosed = 2, + ServerClosed = 3, + }; + private: + KLightServerSession server; + KLightClientSession client; + State state; + KClientPort *port; + uintptr_t name; + KProcess *process; + bool initialized; public: + constexpr KLightSession() + : server(), client(), state(State::Invalid), port(), name(), process(), initialized() + { + /* ... */ + } + + virtual ~KLightSession() { /* ... */ } + + virtual bool IsInitialized() const override { return this->initialized; } + virtual uintptr_t GetPostDestroyArgument() const override { return reinterpret_cast(this->process); } + + static void PostDestroy(uintptr_t arg); + /* TODO: This is a placeholder definition. */ + + KLightClientSession &GetClientSession() { return this->client; } + KLightServerSession &GetServerSession() { return this->server; } + const KLightClientSession &GetClientSession() const { return this->client; } + const KLightServerSession &GetServerSession() const { return this->server; } }; } diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_object_name.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_object_name.hpp index 87e76c8c..09ab2732 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_object_name.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_object_name.hpp @@ -23,7 +23,39 @@ namespace ams::kern { class KObjectName : public KSlabAllocated, public util::IntrusiveListBaseNode { public: - /* TODO: This is a placeholder definition. */ + static constexpr size_t NameLengthMax = 12; + + using List = util::IntrusiveListBaseTraits::ListType; + private: + char name[NameLengthMax]; + KAutoObject *object; + public: + constexpr KObjectName() : name(), object() { /* ... */ } + public: + static Result NewFromName(KAutoObject *obj, const char *name); + static Result Delete(KAutoObject *obj, const char *name); + + static KScopedAutoObject Find(const char *name); + + template + static Result Delete(const char *name) { + /* Find the object. */ + KScopedAutoObject obj = Find(name); + R_UNLESS(obj.IsNotNull(), svc::ResultNotFound()); + + /* Cast the object to the desired type. */ + Derived *derived = obj->DynamicCast(); + R_UNLESS(derived != nullptr, svc::ResultNotFound()); + + return Delete(obj.GetPointerUnsafe(), name); + } + private: + static KScopedAutoObject FindImpl(const char *name); + + void Initialize(KAutoObject *obj, const char *name); + + bool MatchesName(const char *name) const; + KAutoObject *GetObject() const { return this->object; } }; } diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp index aa121f6a..e9630f52 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp @@ -191,8 +191,6 @@ namespace ams::kern { KPageTableImpl &GetImpl() { return this->impl; } const KPageTableImpl &GetImpl() const { return this->impl; } - KBlockInfoManager *GetBlockInfoManager() const { return this->block_info_manager; } - bool IsLockedByCurrentThread() const { return this->general_lock.IsLockedByCurrentThread(); } bool IsHeapPhysicalAddress(KPhysicalAddress phys_addr) { @@ -245,6 +243,8 @@ namespace ams::kern { return this->GetImpl().GetPhysicalAddress(out, virt_addr); } + KBlockInfoManager *GetBlockInfoManager() const { return this->block_info_manager; } + Result SetMemoryPermission(KProcessAddress addr, size_t size, ams::svc::MemoryPermission perm); Result SetProcessMemoryPermission(KProcessAddress addr, size_t size, ams::svc::MemoryPermission perm); Result SetHeapSize(KProcessAddress *out, size_t size); @@ -270,18 +270,22 @@ namespace ams::kern { Result MapPageGroup(KProcessAddress *out_addr, const KPageGroup &pg, KProcessAddress region_start, size_t region_num_pages, KMemoryState state, KMemoryPermission perm); Result MapPageGroup(KProcessAddress address, const KPageGroup &pg, KMemoryState state, KMemoryPermission perm); Result UnmapPageGroup(KProcessAddress address, const KPageGroup &pg, KMemoryState state); + + Result MakeAndOpenPageGroup(KPageGroup *out, KProcessAddress address, size_t num_pages, u32 state_mask, u32 state, u32 perm_mask, u32 perm, u32 attr_mask, u32 attr); public: KProcessAddress GetAddressSpaceStart() const { return this->address_space_start; } KProcessAddress GetHeapRegionStart() const { return this->heap_region_start; } KProcessAddress GetAliasRegionStart() const { return this->alias_region_start; } KProcessAddress GetStackRegionStart() const { return this->stack_region_start; } KProcessAddress GetKernelMapRegionStart() const { return this->kernel_map_region_start; } + KProcessAddress GetAliasCodeRegionStart() const { return this->alias_code_region_start; } size_t GetAddressSpaceSize() const { return this->address_space_end - this->address_space_start; } size_t GetHeapRegionSize() const { return this->heap_region_end - this->heap_region_start; } size_t GetAliasRegionSize() const { return this->alias_region_end - this->alias_region_start; } size_t GetStackRegionSize() const { return this->stack_region_end - this->stack_region_start; } size_t GetKernelMapRegionSize() const { return this->kernel_map_region_end - this->kernel_map_region_start; } + size_t GetAliasCodeRegionSize() const { return this->alias_code_region_end - this->alias_code_region_start; } public: static ALWAYS_INLINE KVirtualAddress GetLinearVirtualAddress(KPhysicalAddress addr) { return KMemoryLayout::GetLinearVirtualAddress(addr); diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_port.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_port.hpp index 40799b14..65a4609f 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_port.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_port.hpp @@ -16,14 +16,46 @@ #pragma once #include #include +#include +#include #include namespace ams::kern { class KPort final : public KAutoObjectWithSlabHeapAndContainer { MESOSPHERE_AUTOOBJECT_TRAITS(KPort, KAutoObject); + private: + enum class State : u8 { + Invalid = 0, + Normal = 1, + ClientClosed = 2, + ServerClosed = 3, + }; + private: + KServerPort server; + KClientPort client; + uintptr_t name; + State state; + bool is_light; public: - /* TODO: This is a placeholder definition. */ + constexpr KPort() : server(), client(), name(), state(State::Invalid), is_light() { /* ... */ } + virtual ~KPort() { /* ... */ } + + static void PostDestroy(uintptr_t arg) { /* ... */ } + + void Initialize(s32 max_sessions, bool is_light, uintptr_t name); + void OnClientClosed(); + void OnServerClosed(); + + uintptr_t GetName() const { return this->name; } + bool IsLight() const { return this->is_light; } + + /* TODO: More of KPort */ + + KClientPort &GetClientPort() { return this->client; } + KServerPort &GetServerPort() { return this->server; } + const KClientPort &GetClientPort() const { return this->client; } + const KServerPort &GetServerPort() const { return this->server; } }; } diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_server_port.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_server_port.hpp new file mode 100644 index 00000000..c8a429a5 --- /dev/null +++ b/libraries/libmesosphere/include/mesosphere/kern_k_server_port.hpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include + +namespace ams::kern { + + class KPort; + class KServerSession; + class KLightServerSession; + + class KServerPort final : public KSynchronizationObject { + MESOSPHERE_AUTOOBJECT_TRAITS(KServerPort, KSynchronizationObject); + private: + using SessionList = util::IntrusiveListBaseTraits::ListType; + using LightSessionList = util::IntrusiveListBaseTraits::ListType; + private: + SessionList session_list; + LightSessionList light_session_list; + KPort *parent; + public: + constexpr KServerPort() : session_list(), light_session_list(), parent() { /* ... */ } + virtual ~KServerPort() { /* ... */ } + + void Initialize(KPort *parent); + + constexpr const KPort *GetParent() const { return this->parent; } + + bool IsLight() const; + + /* Overridden virtual functions. */ + virtual void Destroy() override; + virtual bool IsSignaled() const override; + + /* TODO: More of KClientPort. */ + private: + void CleanupSessions(); + /* TODO: This is a placeholder definition. */ + }; + +} diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_server_session.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_server_session.hpp new file mode 100644 index 00000000..70d0583e --- /dev/null +++ b/libraries/libmesosphere/include/mesosphere/kern_k_server_session.hpp @@ -0,0 +1,48 @@ +/* + * 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::kern { + + class KSession; + + class KServerSession final : public KSynchronizationObject, public util::IntrusiveListBaseNode { + MESOSPHERE_AUTOOBJECT_TRAITS(KServerSession, KSynchronizationObject); + private: + using RequestList = util::IntrusiveListBaseTraits::ListType; + private: + KSession *parent; + RequestList request_list; + KSessionRequest *current_request; + KLightLock lock; + public: + constexpr KServerSession() : parent(), request_list(), current_request(), lock() { /* ... */ } + virtual ~KServerSession() { /* ... */ } + + void Initialize(KSession *parent); + + constexpr const KSession *GetParent() const { return this->parent; } + + virtual bool IsSignaled() const override { MESOSPHERE_UNIMPLEMENTED(); } + + /* TODO: More of KServerSession. */ + }; + +} diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_session.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_session.hpp index ccd5bc06..0ee803b0 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_session.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_session.hpp @@ -16,14 +16,52 @@ #pragma once #include #include +#include +#include #include namespace ams::kern { + class KClientPort; + class KProcess; + class KSession final : public KAutoObjectWithSlabHeapAndContainer { MESOSPHERE_AUTOOBJECT_TRAITS(KSession, KAutoObject); + private: + enum class State : u8 { + Invalid = 0, + Normal = 1, + ClientClosed = 2, + ServerClosed = 3, + }; + private: + KServerSession server; + KClientSession client; + State state; + KClientPort *port; + uintptr_t name; + KProcess *process; + bool initialized; public: + constexpr KSession() + : server(), client(), state(State::Invalid), port(), name(), process(), initialized() + { + /* ... */ + } + + virtual ~KSession() { /* ... */ } + + virtual bool IsInitialized() const override { return this->initialized; } + virtual uintptr_t GetPostDestroyArgument() const override { return reinterpret_cast(this->process); } + + static void PostDestroy(uintptr_t arg); + /* TODO: This is a placeholder definition. */ + + KClientSession &GetClientSession() { return this->client; } + KServerSession &GetServerSession() { return this->server; } + const KClientSession &GetClientSession() const { return this->client; } + const KServerSession &GetServerSession() const { return this->server; } }; } diff --git a/libraries/libmesosphere/include/mesosphere/kern_panic.hpp b/libraries/libmesosphere/include/mesosphere/kern_panic.hpp index 3df06b17..2f31453a 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_panic.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_panic.hpp @@ -32,7 +32,7 @@ namespace ams::kern { #define MESOSPHERE_UNUSED(...) ::ams::kern::UnusedImpl(__VA_ARGS__) #ifdef MESOSPHERE_ENABLE_DEBUG_PRINT -#define MESOSPHERE_PANIC(...) do { ::ams::kern::Panic(__FILE__, __LINE__, __VA_ARGS__); } while(0) +#define MESOSPHERE_PANIC(...) do { ::ams::kern::Panic(__FILE__, __LINE__, ## __VA_ARGS__); } while(0) #else #define MESOSPHERE_PANIC(...) do { MESOSPHERE_UNUSED(__VA_ARGS__); ::ams::kern::Panic(); } while(0) #endif diff --git a/libraries/libmesosphere/source/arch/arm64/kern_exception_handlers.cpp b/libraries/libmesosphere/source/arch/arm64/kern_exception_handlers.cpp index d284e398..37fefefb 100644 --- a/libraries/libmesosphere/source/arch/arm64/kern_exception_handlers.cpp +++ b/libraries/libmesosphere/source/arch/arm64/kern_exception_handlers.cpp @@ -150,6 +150,15 @@ namespace ams::kern::arch::arm64 { HandleUserException(context, esr, far, afsr0, afsr1, data); } } else { + MESOSPHERE_LOG("Unhandled Exception in Supervisor Mode\n"); + MESOSPHERE_LOG("Current Process = %s\n", GetCurrentProcess().GetName()); + + for (size_t i = 0; i < 31; i++) { + MESOSPHERE_LOG("X[%02zu] = %016lx\n", i, context->x[i]); + } + MESOSPHERE_LOG("PC = %016lx\n", context->pc); + MESOSPHERE_LOG("SP = %016lx\n", context->sp); + MESOSPHERE_PANIC("Unhandled Exception in Supervisor Mode\n"); } diff --git a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp index 790022ff..b22e1189 100644 --- a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp +++ b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp @@ -327,4 +327,53 @@ namespace ams::kern::board::nintendo::nx { while (true) { /* ... */ } } + /* User access. */ + void KSystemControl::CallSecureMonitorFromUser(ams::svc::lp64::SecureMonitorArguments *args) { + /* Get the function id for the current call. */ + u64 function_id = args->r[0]; + + MESOSPHERE_LOG("CallSecureMonitor(%lx, %lx, %lx, %lx, %lx, %lx, %lx, %lx);\n", args->r[0], args->r[1], args->r[2], args->r[3], args->r[4], args->r[5], args->r[6], args->r[7]); + + /* We'll need to map in pages if arguments are pointers. Prepare page groups to do so. */ + auto &page_table = GetCurrentProcess().GetPageTable(); + auto *bim = page_table.GetBlockInfoManager(); + + constexpr size_t MaxMappedRegisters = 7; + std::array page_groups = { KPageGroup(bim), KPageGroup(bim), KPageGroup(bim), KPageGroup(bim), KPageGroup(bim), KPageGroup(bim), KPageGroup(bim), }; + + for (size_t i = 0; i < MaxMappedRegisters; i++) { + const size_t reg_id = i + 1; + if (function_id & (1ul << (8 + reg_id))) { + /* Create and open a new page group for the address. */ + KVirtualAddress virt_addr = args->r[reg_id]; + + if (R_SUCCEEDED(page_table.MakeAndOpenPageGroup(std::addressof(page_groups[i]), util::AlignDown(GetInteger(virt_addr), PageSize), 1, KMemoryState_None, KMemoryState_None, KMemoryPermission_UserReadWrite, KMemoryPermission_UserReadWrite, KMemoryAttribute_None, KMemoryAttribute_None))) { + /* Translate the virtual address to a physical address. */ + const auto it = page_groups[i].begin(); + MESOSPHERE_ASSERT(it != page_groups[i].end()); + MESOSPHERE_ASSERT(it->GetNumPages() == 1); + + KPhysicalAddress phys_addr = page_table.GetHeapPhysicalAddress(it->GetAddress()); + + args->r[reg_id] = GetInteger(phys_addr) | (GetInteger(virt_addr) & (PageSize - 1)); + MESOSPHERE_LOG("Mapped arg %zu\n", reg_id); + } else { + /* If we couldn't map, we should clear the address. */ + MESOSPHERE_LOG("Failed to map arg %zu\n", reg_id); + args->r[reg_id] = 0; + } + } + } + + /* Invoke the secure monitor. */ + smc::CallSecureMonitorFromUser(args); + + MESOSPHERE_LOG("Secure Monitor Returned: (%lx, %lx, %lx, %lx, %lx, %lx, %lx, %lx);\n", args->r[0], args->r[1], args->r[2], args->r[3], args->r[4], args->r[5], args->r[6], args->r[7]); + + /* Make sure that we close any pages that we opened. */ + for (size_t i = 0; i < MaxMappedRegisters; i++) { + page_groups[i].Close(); + } + } + } \ No newline at end of file diff --git a/libraries/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.cpp b/libraries/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.cpp index 49f2ac28..3ae37506 100644 --- a/libraries/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.cpp +++ b/libraries/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.cpp @@ -71,6 +71,42 @@ namespace ams::kern::board::nintendo::nx::smc { args.x[7] = x7; } + void CallUserSecureMonitorFunction(ams::svc::lp64::SecureMonitorArguments *args) { + /* Load arguments into registers. */ + register u64 x0 asm("x0") = args->r[0]; + register u64 x1 asm("x1") = args->r[1]; + register u64 x2 asm("x2") = args->r[2]; + register u64 x3 asm("x3") = args->r[3]; + register u64 x4 asm("x4") = args->r[4]; + register u64 x5 asm("x5") = args->r[5]; + register u64 x6 asm("x6") = args->r[6]; + register u64 x7 asm("x7") = args->r[7]; + + /* Actually make the call. */ + { + /* Disable interrupts while making the call. */ + KScopedInterruptDisable intr_disable; + __asm__ __volatile__("smc #0" + : "+r"(x0), "+r"(x1), "+r"(x2), "+r"(x3), "+r"(x4), "+r"(x5), "+r"(x6), "+r"(x7) + : + : "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "cc", "memory" + ); + + /* Restore the CoreLocalRegion into X18. */ + cpu::SetCoreLocalRegionAddress(cpu::GetTpidrEl1()); + } + + /* Store arguments to output. */ + args->r[0] = x0; + args->r[1] = x1; + args->r[2] = x2; + args->r[3] = x3; + args->r[4] = x4; + args->r[5] = x5; + args->r[6] = x6; + args->r[7] = x7; + } + void CallPrivilegedSecureMonitorFunctionForInit(SecureMonitorArguments &args) { /* Load arguments into registers. */ register u64 x0 asm("x0") = args.x[0]; @@ -188,4 +224,8 @@ namespace ams::kern::board::nintendo::nx::smc { while (true) { /* ... */ } } + void CallSecureMonitorFromUser(ams::svc::lp64::SecureMonitorArguments *args) { + CallUserSecureMonitorFunction(args); + } + } \ No newline at end of file diff --git a/libraries/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.hpp b/libraries/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.hpp index fa78b0ee..72abe5de 100644 --- a/libraries/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.hpp +++ b/libraries/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.hpp @@ -91,6 +91,8 @@ namespace ams::kern::board::nintendo::nx::smc { void NORETURN Panic(u32 color); + void CallSecureMonitorFromUser(ams::svc::lp64::SecureMonitorArguments *args); + namespace init { void CpuOn(u64 core_id, uintptr_t entrypoint, uintptr_t arg); diff --git a/libraries/libmesosphere/source/kern_k_client_port.cpp b/libraries/libmesosphere/source/kern_k_client_port.cpp new file mode 100644 index 00000000..10eb8476 --- /dev/null +++ b/libraries/libmesosphere/source/kern_k_client_port.cpp @@ -0,0 +1,46 @@ +/* + * 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::kern { + + void KClientPort::Initialize(KPort *parent, s32 max_sessions) { + /* Set member variables. */ + this->num_sessions = 0; + this->peak_sessions = 0; + this->parent = parent; + this->max_sessions = max_sessions; + } + + bool KClientPort::IsLight() const { + return this->GetParent()->IsLight(); + } + + void KClientPort::Destroy() { + /* Note with our parent that we're closed. */ + this->parent->OnClientClosed(); + + /* Close our reference to our parent. */ + this->parent->Close(); + } + + bool KClientPort::IsSignaled() const { + /* TODO: Check preconditions later. */ + MESOSPHERE_ASSERT_THIS(); + return this->num_sessions < this->max_sessions; + } + +} diff --git a/libraries/libmesosphere/source/kern_k_object_name.cpp b/libraries/libmesosphere/source/kern_k_object_name.cpp new file mode 100644 index 00000000..76312245 --- /dev/null +++ b/libraries/libmesosphere/source/kern_k_object_name.cpp @@ -0,0 +1,107 @@ +/* + * 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::kern { + + namespace { + + /* TODO: C++20 constinit */ + KLightLock g_object_list_lock; + KObjectName::List g_object_list; + + } + + void KObjectName::Initialize(KAutoObject *obj, const char *name) { + /* Set member variables. */ + this->object = obj; + std::strncpy(this->name, name, sizeof(this->name)); + this->name[sizeof(this->name) - 1] = '\x00'; + + /* Open a reference to the object we hold. */ + this->object->Open(); + } + + bool KObjectName::MatchesName(const char *name) const { + return std::strncmp(this->name, name, sizeof(this->name)) == 0; + } + + Result KObjectName::NewFromName(KAutoObject *obj, const char *name) { + /* Create a new object name. */ + KObjectName *new_name = KObjectName::Allocate(); + R_UNLESS(new_name != nullptr, svc::ResultOutOfResource()); + + /* Initialize the new name. */ + new_name->Initialize(obj, name); + + /* Check if there's an existing name. */ + { + /* Ensure we have exclusive access to the global list. */ + KScopedLightLock lk(g_object_list_lock); + + /* If the object doesn't exist, put it into the list. */ + KScopedAutoObject existing_object = FindImpl(name); + if (existing_object.IsNull()) { + g_object_list.push_back(*new_name); + return ResultSuccess(); + } + } + + /* The object already exists, which is an error condition. Perform cleanup. */ + obj->Close(); + KObjectName::Free(new_name); + return svc::ResultInvalidState(); + } + + Result KObjectName::Delete(KAutoObject *obj, const char *compare_name) { + /* Ensure we have exclusive access to the global list. */ + KScopedLightLock lk(g_object_list_lock); + + /* Find a matching entry in the list, and delete it. */ + for (auto &name : g_object_list) { + if (name.MatchesName(compare_name) && obj == name.GetObject()) { + /* We found a match, clean up its resources. */ + obj->Close(); + g_object_list.erase(g_object_list.iterator_to(name)); + KObjectName::Free(std::addressof(name)); + return ResultSuccess(); + } + } + + /* We didn't find the object in the list. */ + return svc::ResultNotFound(); + } + + KScopedAutoObject KObjectName::Find(const char *name) { + /* Ensure we have exclusive access to the global list. */ + KScopedLightLock lk(g_object_list_lock); + + return FindImpl(name); + } + + KScopedAutoObject KObjectName::FindImpl(const char *compare_name) { + /* Try to find a matching object in the global list. */ + for (const auto &name : g_object_list) { + if (name.MatchesName(compare_name)) { + return name.GetObject(); + } + } + + /* There's no matching entry in the list. */ + return nullptr; + } + +} diff --git a/libraries/libmesosphere/source/kern_k_page_table_base.cpp b/libraries/libmesosphere/source/kern_k_page_table_base.cpp index 4b4eb1d3..217a2721 100644 --- a/libraries/libmesosphere/source/kern_k_page_table_base.cpp +++ b/libraries/libmesosphere/source/kern_k_page_table_base.cpp @@ -1020,4 +1020,27 @@ namespace ams::kern { return ResultSuccess(); } + Result KPageTableBase::MakeAndOpenPageGroup(KPageGroup *out, KProcessAddress address, size_t num_pages, u32 state_mask, u32 state, u32 perm_mask, u32 perm, u32 attr_mask, u32 attr) { + /* Ensure that the page group isn't null. */ + AMS_ASSERT(out != nullptr); + + /* Make sure that the region we're mapping is valid for the table. */ + const size_t size = num_pages * PageSize; + R_UNLESS(this->Contains(address, size), svc::ResultInvalidCurrentMemory()); + + /* Lock the table. */ + KScopedLightLock lk(this->general_lock); + + /* Check if state allows us to create the group. */ + R_TRY(this->CheckMemoryState(address, size, state_mask | KMemoryState_FlagReferenceCounted, state | KMemoryState_FlagReferenceCounted, perm_mask, perm, attr_mask, attr)); + + /* Create a new page group for the region. */ + R_TRY(this->MakePageGroup(*out, address, num_pages)); + + /* Open a new reference to the pages in the group. */ + out->Open(); + + return ResultSuccess(); + } + } diff --git a/libraries/libmesosphere/source/kern_k_port.cpp b/libraries/libmesosphere/source/kern_k_port.cpp new file mode 100644 index 00000000..9b081ed3 --- /dev/null +++ b/libraries/libmesosphere/source/kern_k_port.cpp @@ -0,0 +1,44 @@ +/* + * 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::kern { + + void KPort::Initialize(s32 max_sessions, bool is_light, uintptr_t name) { + /* Open a new reference count to the initialized port. */ + this->Open(); + + /* Create and initialize our server/client pair. */ + KAutoObject::Create(std::addressof(this->server)); + KAutoObject::Create(std::addressof(this->client)); + this->server.Initialize(this); + this->client.Initialize(this, max_sessions); + + /* Set our member variables. */ + this->is_light = is_light; + this->name = name; + this->state = State::Normal; + } + + void KPort::OnClientClosed() { + MESOSPHERE_UNIMPLEMENTED(); + } + + void KPort::OnServerClosed() { + MESOSPHERE_UNIMPLEMENTED(); + } + +} diff --git a/libraries/libmesosphere/source/kern_k_server_port.cpp b/libraries/libmesosphere/source/kern_k_server_port.cpp new file mode 100644 index 00000000..06096bc8 --- /dev/null +++ b/libraries/libmesosphere/source/kern_k_server_port.cpp @@ -0,0 +1,96 @@ +/* + * 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::kern { + + void KServerPort::Initialize(KPort *parent) { + /* Set member variables. */ + this->parent = parent; + } + + bool KServerPort::IsLight() const { + return this->GetParent()->IsLight(); + } + + void KServerPort::CleanupSessions() { + /* Ensure our preconditions are met. */ + MESOSPHERE_ASSERT(this->IsLight() || this->session_list.empty()); + MESOSPHERE_ASSERT(!this->IsLight() || this->light_session_list.empty()); + + /* Cleanup the session list. */ + while (true) { + /* Get the last session in the list */ + KServerSession *session = nullptr; + { + KScopedSchedulerLock sl; + while (!this->session_list.empty()) { + session = std::addressof(this->session_list.front()); + this->session_list.pop_front(); + } + } + + /* Close the session. */ + if (session != nullptr) { + session->Close(); + } else { + break; + } + } + + /* Cleanup the light session list. */ + while (true) { + /* Get the last session in the list */ + KLightServerSession *session = nullptr; + { + KScopedSchedulerLock sl; + while (!this->light_session_list.empty()) { + session = std::addressof(this->light_session_list.front()); + this->light_session_list.pop_front(); + } + } + + /* Close the session. */ + if (session != nullptr) { + session->Close(); + } else { + break; + } + } + } + + void KServerPort::Destroy() { + /* Note with our parent that we're closed. */ + this->parent->OnClientClosed(); + + /* Perform necessary cleanup of our session lists. */ + this->CleanupSessions(); + + /* Close our reference to our parent. */ + this->parent->Close(); + } + + bool KServerPort::IsSignaled() const { + /* TODO: Check preconditions later. */ + MESOSPHERE_ASSERT_THIS(); + if (this->IsLight()) { + return !this->light_session_list.empty(); + } else { + return this->session_list.empty(); + } + } + +} diff --git a/libraries/libmesosphere/source/libc/arch/arm64/asmdefs.h b/libraries/libmesosphere/source/libc/arch/arm64/asmdefs.h new file mode 100644 index 00000000..edc4e66c --- /dev/null +++ b/libraries/libmesosphere/source/libc/arch/arm64/asmdefs.h @@ -0,0 +1,31 @@ +/* + * Macros for asm code. + * + * Copyright (c) 2019, Arm Limited. + * SPDX-License-Identifier: MIT + */ + +#ifndef _ASMDEFS_H +#define _ASMDEFS_H + +#define ENTRY_ALIGN(name, alignment) \ + .global name; \ + .type name,%function; \ + .align alignment; \ + name: \ + .cfi_startproc; + +#define ENTRY(name) ENTRY_ALIGN(name, 6) + +#define ENTRY_ALIAS(name) \ + .global name; \ + .type name,%function; \ + name: + +#define END(name) \ + .cfi_endproc; \ + .size name, .-name; + +#define L(l) .L ## l + +#endif diff --git a/libraries/libmesosphere/source/libc/arch/arm64/memcmp.arch.arm64-broken.s b/libraries/libmesosphere/source/libc/arch/arm64/memcmp.arch.arm64-broken.s new file mode 100644 index 00000000..609f7e20 --- /dev/null +++ b/libraries/libmesosphere/source/libc/arch/arm64/memcmp.arch.arm64-broken.s @@ -0,0 +1,133 @@ +/* memcmp - compare memory + * + * Copyright (c) 2013, Arm Limited. + * SPDX-License-Identifier: MIT + */ + +/* Assumptions: + * + * ARMv8-a, AArch64, unaligned accesses. + */ + +#include "asmdefs.h" + +/* Parameters and result. */ +#define src1 x0 +#define src2 x1 +#define limit x2 +#define result w0 + +/* Internal variables. */ +#define data1 x3 +#define data1w w3 +#define data1h x4 +#define data2 x5 +#define data2w w5 +#define data2h x6 +#define tmp1 x7 +#define tmp2 x8 + +ENTRY (memcmp) + subs limit, limit, 8 + b.lo L(less8) + + ldr data1, [src1], 8 + ldr data2, [src2], 8 + cmp data1, data2 + b.ne L(return) + + subs limit, limit, 8 + b.gt L(more16) + + ldr data1, [src1, limit] + ldr data2, [src2, limit] + b L(return) + +L(more16): + ldr data1, [src1], 8 + ldr data2, [src2], 8 + cmp data1, data2 + bne L(return) + + /* Jump directly to comparing the last 16 bytes for 32 byte (or less) + strings. */ + subs limit, limit, 16 + b.ls L(last_bytes) + + /* We overlap loads between 0-32 bytes at either side of SRC1 when we + try to align, so limit it only to strings larger than 128 bytes. */ + cmp limit, 96 + b.ls L(loop16) + + /* Align src1 and adjust src2 with bytes not yet done. */ + and tmp1, src1, 15 + add limit, limit, tmp1 + sub src1, src1, tmp1 + sub src2, src2, tmp1 + + /* Loop performing 16 bytes per iteration using aligned src1. + Limit is pre-decremented by 16 and must be larger than zero. + Exit if <= 16 bytes left to do or if the data is not equal. */ + .p2align 4 +L(loop16): + ldp data1, data1h, [src1], 16 + ldp data2, data2h, [src2], 16 + subs limit, limit, 16 + ccmp data1, data2, 0, hi + ccmp data1h, data2h, 0, eq + b.eq L(loop16) + + cmp data1, data2 + bne L(return) + mov data1, data1h + mov data2, data2h + cmp data1, data2 + bne L(return) + + /* Compare last 1-16 bytes using unaligned access. */ +L(last_bytes): + add src1, src1, limit + add src2, src2, limit + ldp data1, data1h, [src1] + ldp data2, data2h, [src2] + cmp data1, data2 + bne L(return) + mov data1, data1h + mov data2, data2h + cmp data1, data2 + + /* Compare data bytes and set return value to 0, -1 or 1. */ +L(return): +#ifndef __AARCH64EB__ + rev data1, data1 + rev data2, data2 +#endif + cmp data1, data2 +L(ret_eq): + cset result, ne + cneg result, result, lo + ret + + .p2align 4 + /* Compare up to 8 bytes. Limit is [-8..-1]. */ +L(less8): + adds limit, limit, 4 + b.lo L(less4) + ldr data1w, [src1], 4 + ldr data2w, [src2], 4 + cmp data1w, data2w + b.ne L(return) + sub limit, limit, 4 +L(less4): + adds limit, limit, 4 + beq L(ret_eq) +L(byte_loop): + ldrb data1w, [src1], 1 + ldrb data2w, [src2], 1 + subs limit, limit, 1 + ccmp data1w, data2w, 0, ne /* NZCV = 0b0000. */ + b.eq L(byte_loop) + sub result, data1w, data2w + ret + +END (memcmp) diff --git a/libraries/libmesosphere/source/libc/arch/arm64/memcpy.arch.arm64-broken.s b/libraries/libmesosphere/source/libc/arch/arm64/memcpy.arch.arm64-broken.s new file mode 100644 index 00000000..02ed1dd8 --- /dev/null +++ b/libraries/libmesosphere/source/libc/arch/arm64/memcpy.arch.arm64-broken.s @@ -0,0 +1,239 @@ +/* + * memcpy - copy memory area + * + * Copyright (c) 2012-2020, Arm Limited. + * SPDX-License-Identifier: MIT + */ + +/* Assumptions: + * + * ARMv8-a, AArch64, unaligned accesses. + * + */ + +#include "asmdefs.h" + +#define dstin x0 +#define src x1 +#define count x2 +#define dst x3 +#define srcend x4 +#define dstend x5 +#define A_l x6 +#define A_lw w6 +#define A_h x7 +#define B_l x8 +#define B_lw w8 +#define B_h x9 +#define C_l x10 +#define C_lw w10 +#define C_h x11 +#define D_l x12 +#define D_h x13 +#define E_l x14 +#define E_h x15 +#define F_l x16 +#define F_h x17 +#define G_l count +#define G_h dst +#define H_l src +#define H_h srcend +#define tmp1 x14 + +/* This implementation handles overlaps and supports both memcpy and memmove + from a single entry point. It uses unaligned accesses and branchless + sequences to keep the code small, simple and improve performance. + + Copies are split into 3 main cases: small copies of up to 32 bytes, medium + copies of up to 128 bytes, and large copies. The overhead of the overlap + check is negligible since it is only required for large copies. + + Large copies use a software pipelined loop processing 64 bytes per iteration. + The destination pointer is 16-byte aligned to minimize unaligned accesses. + The loop tail is handled by always copying 64 bytes from the end. +*/ + +ENTRY (memcpy) +ENTRY_ALIAS (memmove) + add srcend, src, count + add dstend, dstin, count + cmp count, 128 + b.hi L(copy_long) + cmp count, 32 + b.hi L(copy32_128) + + /* Small copies: 0..32 bytes. */ + cmp count, 16 + b.lo L(copy16) + ldp A_l, A_h, [src] + ldp D_l, D_h, [srcend, -16] + stp A_l, A_h, [dstin] + stp D_l, D_h, [dstend, -16] + ret + + /* Copy 8-15 bytes. */ +L(copy16): + tbz count, 3, L(copy8) + ldr A_l, [src] + ldr A_h, [srcend, -8] + str A_l, [dstin] + str A_h, [dstend, -8] + ret + + .p2align 3 + /* Copy 4-7 bytes. */ +L(copy8): + tbz count, 2, L(copy4) + ldr A_lw, [src] + ldr B_lw, [srcend, -4] + str A_lw, [dstin] + str B_lw, [dstend, -4] + ret + + /* Copy 0..3 bytes using a branchless sequence. */ +L(copy4): + cbz count, L(copy0) + lsr tmp1, count, 1 + ldrb A_lw, [src] + ldrb C_lw, [srcend, -1] + ldrb B_lw, [src, tmp1] + strb A_lw, [dstin] + strb B_lw, [dstin, tmp1] + strb C_lw, [dstend, -1] +L(copy0): + ret + + .p2align 4 + /* Medium copies: 33..128 bytes. */ +L(copy32_128): + ldp A_l, A_h, [src] + ldp B_l, B_h, [src, 16] + ldp C_l, C_h, [srcend, -32] + ldp D_l, D_h, [srcend, -16] + cmp count, 64 + b.hi L(copy128) + stp A_l, A_h, [dstin] + stp B_l, B_h, [dstin, 16] + stp C_l, C_h, [dstend, -32] + stp D_l, D_h, [dstend, -16] + ret + + .p2align 4 + /* Copy 65..128 bytes. */ +L(copy128): + ldp E_l, E_h, [src, 32] + ldp F_l, F_h, [src, 48] + cmp count, 96 + b.ls L(copy96) + ldp G_l, G_h, [srcend, -64] + ldp H_l, H_h, [srcend, -48] + stp G_l, G_h, [dstend, -64] + stp H_l, H_h, [dstend, -48] +L(copy96): + stp A_l, A_h, [dstin] + stp B_l, B_h, [dstin, 16] + stp E_l, E_h, [dstin, 32] + stp F_l, F_h, [dstin, 48] + stp C_l, C_h, [dstend, -32] + stp D_l, D_h, [dstend, -16] + ret + + .p2align 4 + /* Copy more than 128 bytes. */ +L(copy_long): + /* Use backwards copy if there is an overlap. */ + sub tmp1, dstin, src + cbz tmp1, L(copy0) + cmp tmp1, count + b.lo L(copy_long_backwards) + + /* Copy 16 bytes and then align dst to 16-byte alignment. */ + + ldp D_l, D_h, [src] + and tmp1, dstin, 15 + bic dst, dstin, 15 + sub src, src, tmp1 + add count, count, tmp1 /* Count is now 16 too large. */ + ldp A_l, A_h, [src, 16] + stp D_l, D_h, [dstin] + ldp B_l, B_h, [src, 32] + ldp C_l, C_h, [src, 48] + ldp D_l, D_h, [src, 64]! + subs count, count, 128 + 16 /* Test and readjust count. */ + b.ls L(copy64_from_end) + +L(loop64): + stp A_l, A_h, [dst, 16] + ldp A_l, A_h, [src, 16] + stp B_l, B_h, [dst, 32] + ldp B_l, B_h, [src, 32] + stp C_l, C_h, [dst, 48] + ldp C_l, C_h, [src, 48] + stp D_l, D_h, [dst, 64]! + ldp D_l, D_h, [src, 64]! + subs count, count, 64 + b.hi L(loop64) + + /* Write the last iteration and copy 64 bytes from the end. */ +L(copy64_from_end): + ldp E_l, E_h, [srcend, -64] + stp A_l, A_h, [dst, 16] + ldp A_l, A_h, [srcend, -48] + stp B_l, B_h, [dst, 32] + ldp B_l, B_h, [srcend, -32] + stp C_l, C_h, [dst, 48] + ldp C_l, C_h, [srcend, -16] + stp D_l, D_h, [dst, 64] + stp E_l, E_h, [dstend, -64] + stp A_l, A_h, [dstend, -48] + stp B_l, B_h, [dstend, -32] + stp C_l, C_h, [dstend, -16] + ret + + .p2align 4 + + /* Large backwards copy for overlapping copies. + Copy 16 bytes and then align dst to 16-byte alignment. */ +L(copy_long_backwards): + ldp D_l, D_h, [srcend, -16] + and tmp1, dstend, 15 + sub srcend, srcend, tmp1 + sub count, count, tmp1 + ldp A_l, A_h, [srcend, -16] + stp D_l, D_h, [dstend, -16] + ldp B_l, B_h, [srcend, -32] + ldp C_l, C_h, [srcend, -48] + ldp D_l, D_h, [srcend, -64]! + sub dstend, dstend, tmp1 + subs count, count, 128 + b.ls L(copy64_from_start) + +L(loop64_backwards): + stp A_l, A_h, [dstend, -16] + ldp A_l, A_h, [srcend, -16] + stp B_l, B_h, [dstend, -32] + ldp B_l, B_h, [srcend, -32] + stp C_l, C_h, [dstend, -48] + ldp C_l, C_h, [srcend, -48] + stp D_l, D_h, [dstend, -64]! + ldp D_l, D_h, [srcend, -64]! + subs count, count, 64 + b.hi L(loop64_backwards) + + /* Write the last iteration and copy 64 bytes from the start. */ +L(copy64_from_start): + ldp G_l, G_h, [src, 48] + stp A_l, A_h, [dstend, -16] + ldp A_l, A_h, [src, 32] + stp B_l, B_h, [dstend, -32] + ldp B_l, B_h, [src, 16] + stp C_l, C_h, [dstend, -48] + ldp C_l, C_h, [src] + stp D_l, D_h, [dstend, -64] + stp G_l, G_h, [dstin, 48] + stp A_l, A_h, [dstin, 32] + stp B_l, B_h, [dstin, 16] + stp C_l, C_h, [dstin] + ret + +END (memcpy) diff --git a/libraries/libmesosphere/source/libc/arch/arm64/memset.arch.arm64-broken.s b/libraries/libmesosphere/source/libc/arch/arm64/memset.arch.arm64-broken.s new file mode 100644 index 00000000..d8d27272 --- /dev/null +++ b/libraries/libmesosphere/source/libc/arch/arm64/memset.arch.arm64-broken.s @@ -0,0 +1,170 @@ +/* + * memset - fill memory with a constant byte + * + * Copyright (c) 2012-2020, Arm Limited. + * SPDX-License-Identifier: MIT + */ + +/* Assumptions: + * + * ARMv8-a, AArch64, Advanced SIMD, unaligned accesses. + * + */ + +#include "asmdefs.h" + +#define DC_ZVA_THRESHOLD 512 + +#define dstin x0 +#define val x1 +#define valw w1 +#define count x2 +#define dst x3 +#define dstend x4 +#define zva_val x5 + +ENTRY (memset) + + bfi valw, valw, 8, 8 + bfi valw, valw, 16, 16 + bfi val, val, 32, 32 + + add dstend, dstin, count + + cmp count, 96 + b.hi L(set_long) + cmp count, 16 + b.hs L(set_medium) + + /* Set 0..15 bytes. */ + tbz count, 3, 1f + str val, [dstin] + str val, [dstend, -8] + ret +1: tbz count, 2, 2f + str valw, [dstin] + str valw, [dstend, -4] + ret +2: cbz count, 3f + strb valw, [dstin] + tbz count, 1, 3f + strh valw, [dstend, -2] +3: ret + + /* Set 16..96 bytes. */ + .p2align 4 +L(set_medium): + stp val, val, [dstin] + tbnz count, 6, L(set96) + stp val, val, [dstend, -16] + tbz count, 5, 1f + stp val, val, [dstin, 16] + stp val, val, [dstend, -32] +1: ret + + .p2align 4 + /* Set 64..96 bytes. Write 64 bytes from the start and + 32 bytes from the end. */ +L(set96): + stp val, val, [dstin, 16] + stp val, val, [dstin, 32] + stp val, val, [dstin, 48] + stp val, val, [dstend, -32] + stp val, val, [dstend, -16] + ret + + .p2align 4 +L(set_long): + stp val, val, [dstin] + bic dst, dstin, 15 +#if DC_ZVA_THRESHOLD + cmp count, DC_ZVA_THRESHOLD + ccmp val, 0, 0, cs + b.eq L(zva_64) +#endif + /* Small-size or non-zero memset does not use DC ZVA. */ + sub count, dstend, dst + + /* + * Adjust count and bias for loop. By substracting extra 1 from count, + * it is easy to use tbz instruction to check whether loop tailing + * count is less than 33 bytes, so as to bypass 2 unneccesary stps. + */ + sub count, count, 64+16+1 + +#if DC_ZVA_THRESHOLD + /* Align loop on 16-byte boundary, this might be friendly to i-cache. */ + nop +#endif + +1: stp val, val, [dst, 16] + stp val, val, [dst, 32] + stp val, val, [dst, 48] + stp val, val, [dst, 64]! + subs count, count, 64 + b.hs 1b + + tbz count, 5, 1f /* Remaining count is less than 33 bytes? */ + stp val, val, [dst, 16] + stp val, val, [dst, 32] +1: stp val, val, [dstend, -32] + stp val, val, [dstend, -16] + ret + +#if DC_ZVA_THRESHOLD + .p2align 4 +L(zva_64): + stp val, val, [dst, 16] + stp val, val, [dst, 32] + stp val, val, [dst, 48] + bic dst, dst, 63 + + /* + * Previous memory writes might cross cache line boundary, and cause + * cache line partially dirty. Zeroing this kind of cache line using + * DC ZVA will incur extra cost, for it requires loading untouched + * part of the line from memory before zeoring. + * + * So, write the first 64 byte aligned block using stp to force + * fully dirty cache line. + */ + stp val, val, [dst, 64] + stp val, val, [dst, 80] + stp val, val, [dst, 96] + stp val, val, [dst, 112] + + sub count, dstend, dst + /* + * Adjust count and bias for loop. By substracting extra 1 from count, + * it is easy to use tbz instruction to check whether loop tailing + * count is less than 33 bytes, so as to bypass 2 unneccesary stps. + */ + sub count, count, 128+64+64+1 + add dst, dst, 128 + nop + + /* DC ZVA sets 64 bytes each time. */ +1: dc zva, dst + add dst, dst, 64 + subs count, count, 64 + b.hs 1b + + /* + * Write the last 64 byte aligned block using stp to force fully + * dirty cache line. + */ + stp val, val, [dst, 0] + stp val, val, [dst, 16] + stp val, val, [dst, 32] + stp val, val, [dst, 48] + + tbz count, 5, 1f /* Remaining count is less than 33 bytes? */ + stp val, val, [dst, 64] + stp val, val, [dst, 80] +1: stp val, val, [dstend, -32] + stp val, val, [dstend, -16] + ret +#endif + + +END (memset) diff --git a/libraries/libmesosphere/source/libc/kern_libc.c b/libraries/libmesosphere/source/libc/kern_libc_generic.c similarity index 65% rename from libraries/libmesosphere/source/libc/kern_libc.c rename to libraries/libmesosphere/source/libc/kern_libc_generic.c index 839a81dc..fb7e4cc7 100644 --- a/libraries/libmesosphere/source/libc/kern_libc.c +++ b/libraries/libmesosphere/source/libc/kern_libc_generic.c @@ -61,6 +61,7 @@ QUICKREF /*SUPPRESS 20*/ void * //__inhibit_loop_to_libcall +__attribute__((weak)) memmove (void *dst_void, const void *src_void, size_t length) @@ -169,6 +170,7 @@ QUICKREF */ void * +__attribute__((weak)) memcpy (void * dst0, const void * __restrict src0, size_t len0) @@ -259,6 +261,7 @@ QUICKREF #define TOO_SMALL(LEN) ((LEN) < LBLOCKSIZE) void * +__attribute__((weak)) memset (void *m, int c, size_t n) @@ -357,6 +360,7 @@ QUICKREF #define TOO_SMALL(LEN) ((LEN) < LBLOCKSIZE) int +__attribute__((weak)) memcmp (const void *m1, const void *m2, size_t n) @@ -417,6 +421,228 @@ memcmp (const void *m1, #endif /* not PREFER_SIZE_OVER_SPEED */ } +/* +FUNCTION + <>---counted copy string +INDEX + strncpy +SYNOPSIS + #include + char *strncpy(char *restrict <[dst]>, const char *restrict <[src]>, + size_t <[length]>); +DESCRIPTION + <> copies not more than <[length]> characters from the + the string pointed to by <[src]> (including the terminating + null character) to the array pointed to by <[dst]>. If the + string pointed to by <[src]> is shorter than <[length]> + characters, null characters are appended to the destination + array until a total of <[length]> characters have been + written. +RETURNS + This function returns the initial value of <[dst]>. +PORTABILITY +<> is ANSI C. +<> requires no supporting OS subroutines. +QUICKREF + strncpy ansi pure +*/ + +#include +#include + +/*SUPPRESS 560*/ +/*SUPPRESS 530*/ + +/* Nonzero if either X or Y is not aligned on a "long" boundary. */ +#define UNALIGNED(X, Y) \ + (((long)X & (sizeof (long) - 1)) | ((long)Y & (sizeof (long) - 1))) + +#if LONG_MAX == 2147483647L +#define DETECTNULL(X) (((X) - 0x01010101) & ~(X) & 0x80808080) +#else +#if LONG_MAX == 9223372036854775807L +/* Nonzero if X (a long int) contains a NULL byte. */ +#define DETECTNULL(X) (((X) - 0x0101010101010101) & ~(X) & 0x8080808080808080) +#else +#error long int is not a 32bit or 64bit type. +#endif +#endif + +#ifndef DETECTNULL +#error long int is not a 32bit or 64bit byte +#endif + +#undef TOO_SMALL +#define TOO_SMALL(LEN) ((LEN) < sizeof (long)) + +char * +strncpy (char *__restrict dst0, + const char *__restrict src0, + size_t count) +{ +#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__) + char *dscan; + const char *sscan; + + dscan = dst0; + sscan = src0; + while (count > 0) + { + --count; + if ((*dscan++ = *sscan++) == '\0') + break; + } + while (count-- > 0) + *dscan++ = '\0'; + + return dst0; +#else + char *dst = dst0; + const char *src = src0; + long *aligned_dst; + const long *aligned_src; + + /* If SRC and DEST is aligned and count large enough, then copy words. */ + if (!UNALIGNED (src, dst) && !TOO_SMALL (count)) + { + aligned_dst = (long*)dst; + aligned_src = (long*)src; + + /* SRC and DEST are both "long int" aligned, try to do "long int" + sized copies. */ + while (count >= sizeof (long int) && !DETECTNULL(*aligned_src)) + { + count -= sizeof (long int); + *aligned_dst++ = *aligned_src++; + } + + dst = (char*)aligned_dst; + src = (char*)aligned_src; + } + + while (count > 0) + { + --count; + if ((*dst++ = *src++) == '\0') + break; + } + + while (count-- > 0) + *dst++ = '\0'; + + return dst0; +#endif /* not PREFER_SIZE_OVER_SPEED */ +} + +/* +FUNCTION + <>---character string compare + +INDEX + strncmp +SYNOPSIS + #include + int strncmp(const char *<[a]>, const char * <[b]>, size_t <[length]>); +DESCRIPTION + <> compares up to <[length]> characters + from the string at <[a]> to the string at <[b]>. +RETURNS + If <<*<[a]>>> sorts lexicographically after <<*<[b]>>>, + <> returns a number greater than zero. If the two + strings are equivalent, <> returns zero. If <<*<[a]>>> + sorts lexicographically before <<*<[b]>>>, <> returns a + number less than zero. +PORTABILITY +<> is ANSI C. +<> requires no supporting OS subroutines. +QUICKREF + strncmp ansi pure +*/ + +#include +#include + +/* Nonzero if either X or Y is not aligned on a "long" boundary. */ +#define UNALIGNED(X, Y) \ + (((long)X & (sizeof (long) - 1)) | ((long)Y & (sizeof (long) - 1))) + +/* DETECTNULL returns nonzero if (long)X contains a NULL byte. */ +#if LONG_MAX == 2147483647L +#define DETECTNULL(X) (((X) - 0x01010101) & ~(X) & 0x80808080) +#else +#if LONG_MAX == 9223372036854775807L +#define DETECTNULL(X) (((X) - 0x0101010101010101) & ~(X) & 0x8080808080808080) +#else +#error long int is not a 32bit or 64bit type. +#endif +#endif + +#ifndef DETECTNULL +#error long int is not a 32bit or 64bit byte +#endif + +int +strncmp (const char *s1, + const char *s2, + size_t n) +{ +#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__) + if (n == 0) + return 0; + + while (n-- != 0 && *s1 == *s2) + { + if (n == 0 || *s1 == '\0') + break; + s1++; + s2++; + } + + return (*(unsigned char *) s1) - (*(unsigned char *) s2); +#else + unsigned long *a1; + unsigned long *a2; + + if (n == 0) + return 0; + + /* If s1 or s2 are unaligned, then compare bytes. */ + if (!UNALIGNED (s1, s2)) + { + /* If s1 and s2 are word-aligned, compare them a word at a time. */ + a1 = (unsigned long*)s1; + a2 = (unsigned long*)s2; + while (n >= sizeof (long) && *a1 == *a2) + { + n -= sizeof (long); + + /* If we've run out of bytes or hit a null, return zero + since we already know *a1 == *a2. */ + if (n == 0 || DETECTNULL (*a1)) + return 0; + + a1++; + a2++; + } + + /* A difference was detected in last few bytes of s1, so search bytewise */ + s1 = (char*)a1; + s2 = (char*)a2; + } + + while (n-- > 0 && *s1 == *s2) + { + /* If we've run out of bytes or hit a null, return zero + since we already know *s1 == *s2. */ + if (n == 0 || *s1 == '\0') + return 0; + s1++; + s2++; + } + return (*(unsigned char *) s1) - (*(unsigned char *) s2); +#endif /* not PREFER_SIZE_OVER_SPEED */ +} + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/libraries/libmesosphere/source/svc/kern_svc_info.cpp b/libraries/libmesosphere/source/svc/kern_svc_info.cpp index b1dc3a53..84f330e3 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_info.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_info.cpp @@ -21,28 +21,112 @@ namespace ams::kern::svc { namespace { + Result GetInfo(u64 *out, ams::svc::InfoType info_type, ams::svc::Handle handle, u64 info_subtype) { + MESOSPHERE_LOG("GetInfo(%p, %u, %08x, %lu) was called\n", out, static_cast(info_type), static_cast(handle), info_subtype); + ON_SCOPE_EXIT{ MESOSPHERE_LOG("GetInfo returned %016lx\n", *out); }; + switch (info_type) { + case ams::svc::InfoType_AliasRegionAddress: + case ams::svc::InfoType_AliasRegionSize: + case ams::svc::InfoType_HeapRegionAddress: + case ams::svc::InfoType_HeapRegionSize: + case ams::svc::InfoType_AslrRegionAddress: + case ams::svc::InfoType_AslrRegionSize: + case ams::svc::InfoType_StackRegionAddress: + case ams::svc::InfoType_StackRegionSize: + { + /* These info types don't support non-zero subtypes. */ + R_UNLESS(info_subtype == 0, svc::ResultInvalidCombination()); + + /* Get the process from its handle. */ + KScopedAutoObject process = GetCurrentProcess().GetHandleTable().GetObject(handle); + R_UNLESS(process.IsNotNull(), svc::ResultInvalidHandle()); + + switch (info_type) { + case ams::svc::InfoType_AliasRegionAddress: + *out = GetInteger(process->GetPageTable().GetAliasRegionStart()); + break; + case ams::svc::InfoType_AliasRegionSize: + *out = process->GetPageTable().GetAliasRegionSize(); + break; + case ams::svc::InfoType_HeapRegionAddress: + *out = GetInteger(process->GetPageTable().GetHeapRegionStart()); + break; + case ams::svc::InfoType_HeapRegionSize: + *out = process->GetPageTable().GetHeapRegionSize(); + break; + case ams::svc::InfoType_AslrRegionAddress: + *out = GetInteger(process->GetPageTable().GetAliasCodeRegionStart()); + break; + case ams::svc::InfoType_AslrRegionSize: + *out = process->GetPageTable().GetAliasCodeRegionSize(); + break; + case ams::svc::InfoType_StackRegionAddress: + *out = GetInteger(process->GetPageTable().GetStackRegionStart()); + break; + case ams::svc::InfoType_StackRegionSize: + *out = process->GetPageTable().GetStackRegionSize(); + break; + MESOSPHERE_UNREACHABLE_DEFAULT_CASE(); + } + } + break; + default: + return svc::ResultInvalidEnumValue(); + } + + return ResultSuccess(); + } + + Result GetSystemInfo(u64 *out, ams::svc::SystemInfoType info_type, ams::svc::Handle handle, u64 info_subtype) { + MESOSPHERE_LOG("GetSystemInfo(%p, %u, %08x, %lu) was called\n", out, static_cast(info_type), static_cast(handle), info_subtype); + ON_SCOPE_EXIT{ MESOSPHERE_LOG("GetSystemInfo returned %016lx\n", *out); }; + + switch (info_type) { + case ams::svc::SystemInfoType_InitialProcessIdRange: + { + R_UNLESS(handle == ams::svc::InvalidHandle, svc::ResultInvalidHandle()); + switch (static_cast(info_subtype)) { + case ams::svc::InitialProcessIdRangeInfo_Minimum: + MESOSPHERE_ABORT_UNLESS(GetInitialProcessIdMin() <= GetInitialProcessIdMax()); + *out = GetInitialProcessIdMin(); + break; + case ams::svc::InitialProcessIdRangeInfo_Maximum: + MESOSPHERE_ABORT_UNLESS(GetInitialProcessIdMin() <= GetInitialProcessIdMax()); + *out = GetInitialProcessIdMax(); + break; + default: + return svc::ResultInvalidCombination(); + } + } + break; + default: + return svc::ResultInvalidEnumValue(); + } + + return ResultSuccess(); + } } /* ============================= 64 ABI ============================= */ Result GetInfo64(uint64_t *out, ams::svc::InfoType info_type, ams::svc::Handle handle, uint64_t info_subtype) { - MESOSPHERE_PANIC("Stubbed SvcGetInfo64 was called."); + return GetInfo(out, info_type, handle, info_subtype); } Result GetSystemInfo64(uint64_t *out, ams::svc::SystemInfoType info_type, ams::svc::Handle handle, uint64_t info_subtype) { - MESOSPHERE_PANIC("Stubbed SvcGetSystemInfo64 was called."); + return GetSystemInfo(out, info_type, handle, info_subtype); } /* ============================= 64From32 ABI ============================= */ Result GetInfo64From32(uint64_t *out, ams::svc::InfoType info_type, ams::svc::Handle handle, uint64_t info_subtype) { - MESOSPHERE_PANIC("Stubbed SvcGetInfo64From32 was called."); + return GetInfo(out, info_type, handle, info_subtype); } Result GetSystemInfo64From32(uint64_t *out, ams::svc::SystemInfoType info_type, ams::svc::Handle handle, uint64_t info_subtype) { - MESOSPHERE_PANIC("Stubbed SvcGetSystemInfo64From32 was called."); + return GetSystemInfo(out, info_type, handle, info_subtype); } } diff --git a/libraries/libmesosphere/source/svc/kern_svc_port.cpp b/libraries/libmesosphere/source/svc/kern_svc_port.cpp index e8b1a73a..2aa297d0 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_port.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_port.cpp @@ -21,7 +21,61 @@ namespace ams::kern::svc { namespace { + Result ManageNamedPort(ams::svc::Handle *out_server_handle, KUserPointer user_name, s32 max_sessions) { + /* Copy the provided name from user memory to kernel memory. */ + char name[KObjectName::NameLengthMax] = {}; + R_TRY(user_name.CopyStringTo(name, sizeof(name))); + /* Validate that sessions and name are valid. */ + R_UNLESS(max_sessions >= 0, svc::ResultOutOfRange()); + R_UNLESS(name[sizeof(name) - 1] == '\x00', svc::ResultOutOfRange()); + + if (max_sessions > 0) { + MESOSPHERE_LOG("Creating Named Port %s (max sessions = %d)\n", name, max_sessions); + /* Get the current handle table. */ + auto &handle_table = GetCurrentProcess().GetHandleTable(); + + /* Create a new port. */ + KPort *port = KPort::Create(); + R_UNLESS(port != nullptr, svc::ResultOutOfResource()); + + /* Reserve a handle for the server port. */ + R_TRY(handle_table.Reserve(out_server_handle)); + auto reserve_guard = SCOPE_GUARD { handle_table.Unreserve(*out_server_handle); }; + + /* Initialize the new port. */ + port->Initialize(max_sessions, false, 0); + + /* Register the port. */ + KPort::Register(port); + + /* Register the handle in the table. */ + handle_table.Register(*out_server_handle, std::addressof(port->GetServerPort())); + reserve_guard.Cancel(); + auto register_guard = SCOPE_GUARD { handle_table.Remove(*out_server_handle); }; + + /* Create a new object name. */ + R_TRY(KObjectName::NewFromName(std::addressof(port->GetClientPort()), name)); + + /* Perform resource cleanup. */ + port->GetServerPort().Close(); + port->GetClientPort().Close(); + register_guard.Cancel(); + } else /* if (max_sessions == 0) */ { + MESOSPHERE_LOG("Deleting Named Port %s\n", name); + + /* Ensure that this else case is correct. */ + MESOSPHERE_AUDIT(max_sessions == 0); + + /* If we're closing, there's no server handle. */ + *out_server_handle = ams::svc::InvalidHandle; + + /* Delete the object. */ + R_TRY(KObjectName::Delete(name)); + } + + return ResultSuccess(); + } } @@ -36,7 +90,7 @@ namespace ams::kern::svc { } Result ManageNamedPort64(ams::svc::Handle *out_server_handle, KUserPointer name, int32_t max_sessions) { - MESOSPHERE_PANIC("Stubbed SvcManageNamedPort64 was called."); + return ManageNamedPort(out_server_handle, name, max_sessions); } Result ConnectToPort64(ams::svc::Handle *out_handle, ams::svc::Handle port) { @@ -54,7 +108,7 @@ namespace ams::kern::svc { } Result ManageNamedPort64From32(ams::svc::Handle *out_server_handle, KUserPointer name, int32_t max_sessions) { - MESOSPHERE_PANIC("Stubbed SvcManageNamedPort64From32 was called."); + return ManageNamedPort(out_server_handle, name, max_sessions); } Result ConnectToPort64From32(ams::svc::Handle *out_handle, ams::svc::Handle port) { diff --git a/libraries/libmesosphere/source/svc/kern_svc_process.cpp b/libraries/libmesosphere/source/svc/kern_svc_process.cpp index 17350db5..4bf47cdd 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_process.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_process.cpp @@ -21,6 +21,32 @@ namespace ams::kern::svc { namespace { + Result GetProcessId(u64 *out_process_id, ams::svc::Handle handle) { + /* Get the object from the handle table. */ + KScopedAutoObject obj = GetCurrentProcess().GetHandleTable().GetObject(handle); + R_UNLESS(obj.IsNotNull(), svc::ResultInvalidHandle()); + + /* Get the process from the object. */ + KProcess *process = nullptr; + if (obj->IsDerivedFrom(KProcess::GetStaticTypeObj())) { + /* The object is a process, so we can use it directly. */ + process = reinterpret_cast(obj.GetPointerUnsafe()); + } else if (obj->IsDerivedFrom(KThread::GetStaticTypeObj())) { + /* The object is a thread, so we want to use its parent. */ + process = reinterpret_cast(obj.GetPointerUnsafe())->GetOwnerProcess(); + } else if (obj->IsDerivedFrom(KDebug::GetStaticTypeObj())) { + /* The object is a debug, so we want to use the process it's attached to. */ + MESOSPHERE_UNIMPLEMENTED(); + } + + /* Make sure the target process exists. */ + R_UNLESS(process != nullptr, svc::ResultInvalidHandle()); + + /* Get the process id. */ + *out_process_id = process->GetId(); + return ResultSuccess(); + } + } @@ -32,7 +58,7 @@ namespace ams::kern::svc { } Result GetProcessId64(uint64_t *out_process_id, ams::svc::Handle process_handle) { - MESOSPHERE_PANIC("Stubbed SvcGetProcessId64 was called."); + return GetProcessId(out_process_id, process_handle); } Result GetProcessList64(int32_t *out_num_processes, KUserPointer out_process_ids, int32_t max_out_count) { @@ -62,7 +88,7 @@ namespace ams::kern::svc { } Result GetProcessId64From32(uint64_t *out_process_id, ams::svc::Handle process_handle) { - MESOSPHERE_PANIC("Stubbed SvcGetProcessId64From32 was called."); + return GetProcessId(out_process_id, process_handle); } Result GetProcessList64From32(int32_t *out_num_processes, KUserPointer out_process_ids, int32_t max_out_count) { diff --git a/libraries/libmesosphere/source/svc/kern_svc_secure_monitor_call.cpp b/libraries/libmesosphere/source/svc/kern_svc_secure_monitor_call.cpp index ccbb1b90..8c0034ff 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_secure_monitor_call.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_secure_monitor_call.cpp @@ -28,7 +28,7 @@ namespace ams::kern::svc { /* ============================= 64 ABI ============================= */ void CallSecureMonitor64(ams::svc::lp64::SecureMonitorArguments *args) { - MESOSPHERE_PANIC("Stubbed SvcCallSecureMonitor64 was called."); + KSystemControl::CallSecureMonitorFromUser(args); } /* ============================= 64From32 ABI ============================= */ diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_substorage.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_substorage.hpp index 9470b474..4e39ae36 100644 --- a/libraries/libstratosphere/include/stratosphere/fs/fs_substorage.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_substorage.hpp @@ -33,7 +33,7 @@ namespace ams::fs { public: SubStorage() : shared_base_storage(), base_storage(nullptr), offset(0), size(0), resizable(false) { /* ... */ } - SubStorage(const SubStorage &rhs) : shared_base_storage(), base_storage(rhs.base_storage), offset(rhs.offset), size(rhs.size), resizable(rhs.resizable) { /* ... */} + SubStorage(const SubStorage &rhs) : shared_base_storage(), base_storage(rhs.base_storage), offset(rhs.offset), size(rhs.size), resizable(rhs.resizable) { /* ... */} SubStorage &operator=(const SubStorage &rhs) { if (this != std::addressof(rhs)) { this->base_storage = rhs.base_storage; @@ -44,19 +44,19 @@ namespace ams::fs { return *this; } - SubStorage(IStorage *storage, s64 o, s64 sz) : shared_base_storage(), base_storage(storage), offset(o), size(sz) { + SubStorage(IStorage *storage, s64 o, s64 sz) : shared_base_storage(), base_storage(storage), offset(o), size(sz), resizable(false) { AMS_ABORT_UNLESS(this->IsValid()); AMS_ABORT_UNLESS(this->offset >= 0); AMS_ABORT_UNLESS(this->size >= 0); } - SubStorage(std::shared_ptr storage, s64 o, s64 sz) : shared_base_storage(storage), base_storage(storage.get()), offset(o), size(sz) { + SubStorage(std::shared_ptr storage, s64 o, s64 sz) : shared_base_storage(storage), base_storage(storage.get()), offset(o), size(sz), resizable(false) { AMS_ABORT_UNLESS(this->IsValid()); AMS_ABORT_UNLESS(this->offset >= 0); AMS_ABORT_UNLESS(this->size >= 0); } - SubStorage(SubStorage *sub, s64 o, s64 sz) : shared_base_storage(), base_storage(sub->base_storage), offset(o + sub->offset), size(sz) { + SubStorage(SubStorage *sub, s64 o, s64 sz) : shared_base_storage(), base_storage(sub->base_storage), offset(o + sub->offset), size(sz), resizable(false) { AMS_ABORT_UNLESS(this->IsValid()); AMS_ABORT_UNLESS(this->offset >= 0); AMS_ABORT_UNLESS(this->size >= 0); diff --git a/libraries/libstratosphere/include/stratosphere/os.hpp b/libraries/libstratosphere/include/stratosphere/os.hpp index 3ac4ef90..abd5e77d 100644 --- a/libraries/libstratosphere/include/stratosphere/os.hpp +++ b/libraries/libstratosphere/include/stratosphere/os.hpp @@ -18,6 +18,7 @@ #include "os/os_common_types.hpp" #include "os/os_memory_common.hpp" +#include "os/os_tick.hpp" #include "os/os_managed_handle.hpp" #include "os/os_process_handle.hpp" #include "os/os_random.hpp" diff --git a/libraries/libstratosphere/include/stratosphere/os/os_tick.hpp b/libraries/libstratosphere/include/stratosphere/os/os_tick.hpp new file mode 100644 index 00000000..67d42b58 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/os/os_tick.hpp @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include "os_common_types.hpp" + +namespace ams::os { + + class Tick; + + /* Tick API. */ + Tick GetSystemTick(); + s64 GetSystemTickFrequency(); + TimeSpan ConvertToTimeSpan(Tick tick); + Tick ConvertToTick(TimeSpan ts); + + class Tick { + private: + s64 tick; + public: + constexpr explicit Tick(s64 t = 0) : tick(t) { /* ... */ } + Tick(TimeSpan ts) : tick(ConvertToTick(ts).GetInt64Value()) { /* ... */ } + public: + constexpr s64 GetInt64Value() const { return this->tick; } + TimeSpan ToTimeSpan() const { return ConvertToTimeSpan(*this); } + + /* Tick arithmetic. */ + constexpr Tick &operator+=(Tick rhs) { this->tick += rhs.tick; return *this; } + constexpr Tick &operator-=(Tick rhs) { this->tick -= rhs.tick; return *this; } + constexpr Tick operator+(Tick rhs) const { Tick r(*this); return r += rhs; } + constexpr Tick operator-(Tick rhs) const { Tick r(*this); return r -= rhs; } + + constexpr bool operator==(const Tick &rhs) const { + return this->tick == rhs.tick; + } + + constexpr bool operator!=(const Tick &rhs) const { + return !(*this == rhs); + } + + constexpr bool operator<(const Tick &rhs) const { + return this->tick < rhs.tick; + } + + constexpr bool operator>=(const Tick &rhs) const { + return !(*this < rhs); + } + + constexpr bool operator>(const Tick &rhs) const { + return this->tick > rhs.tick; + } + + constexpr bool operator<=(const Tick &rhs) const { + return !(*this > rhs); + } + }; + +} diff --git a/libraries/libstratosphere/source/ams/ams_bpc.c b/libraries/libstratosphere/source/ams/ams_bpc.c index 7f46a7c6..d04a69b7 100644 --- a/libraries/libstratosphere/source/ams/ams_bpc.c +++ b/libraries/libstratosphere/source/ams/ams_bpc.c @@ -38,9 +38,9 @@ Service *amsBpcGetServiceSession(void) { Result amsBpcRebootToFatalError(void *ctx) { /* Note: this takes in an sts::ams::FatalErrorContext. */ - /* static_assert(sizeof() == 0x350) is done at type definition. */ + /* static_assert(sizeof() == 0x450) is done at type definition. */ return serviceDispatch(&g_amsBpcSrv, 65000, .buffer_attrs = { SfBufferAttr_In | SfBufferAttr_HipcMapAlias | SfBufferAttr_FixedSize }, - .buffers = { { ctx, 0x350 } }, + .buffers = { { ctx, 0x450 } }, ); } diff --git a/libraries/libstratosphere/source/kvdb/kvdb_file_key_value_store.cpp b/libraries/libstratosphere/source/kvdb/kvdb_file_key_value_store.cpp index 540ef360..eb699b60 100644 --- a/libraries/libstratosphere/source/kvdb/kvdb_file_key_value_store.cpp +++ b/libraries/libstratosphere/source/kvdb/kvdb_file_key_value_store.cpp @@ -222,7 +222,7 @@ namespace ams::kvdb { R_UNLESS(file_size <= static_cast(max_out_size), ResultBufferInsufficient()); /* Read the value. */ - const size_t value_size = static_cast(value_size); + const size_t value_size = static_cast(file_size); R_TRY(fs::ReadFile(file, 0, out_value, value_size)); *out_size = value_size; 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 ae3e9863..4db3e0b5 100644 --- a/libraries/libstratosphere/source/os/impl/os_inter_process_event.cpp +++ b/libraries/libstratosphere/source/os/impl/os_inter_process_event.cpp @@ -118,7 +118,7 @@ namespace ams::os::impl { while (true) { /* Continuously wait, until success. */ - R_TRY_CATCH(svcWaitSynchronizationSingle(handle, U64_MAX)) { + R_TRY_CATCH(svcWaitSynchronizationSingle(handle, std::numeric_limits::max())) { R_CATCH(svc::ResultCancelled) { continue; } } R_END_TRY_CATCH_WITH_ABORT_UNLESS; diff --git a/libraries/libstratosphere/source/os/impl/os_resource_manager.cpp b/libraries/libstratosphere/source/os/impl/os_resource_manager.cpp new file mode 100644 index 00000000..539f9e17 --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_resource_manager.cpp @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "os_resource_manager.hpp" + +namespace ams::os::impl { + + /* TODO: C++20 constinit */ + OsResourceManager ResourceManagerHolder::s_resource_manager = {}; + +} diff --git a/libraries/libstratosphere/source/os/impl/os_resource_manager.hpp b/libraries/libstratosphere/source/os/impl/os_resource_manager.hpp new file mode 100644 index 00000000..61525e90 --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_resource_manager.hpp @@ -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 . + */ +#pragma once +#include +#include "os_rng_manager_impl.hpp" +#include "os_tick_manager_impl.hpp" + +namespace ams::os::impl { + + class OsResourceManager { + private: + RngManager rng_manager{}; + /* TODO */ + TickManager tick_manager{}; + /* TODO */ + public: + constexpr OsResourceManager() = default; + + constexpr ALWAYS_INLINE RngManager &GetRngManager() { return this->rng_manager; } + constexpr ALWAYS_INLINE TickManager &GetTickManager() { return this->tick_manager; } + }; + + class ResourceManagerHolder { + private: + static /* TODO: C++20 constinit */ OsResourceManager s_resource_manager; + private: + constexpr ResourceManagerHolder() { /* ... */ } + public: + static ALWAYS_INLINE OsResourceManager &GetResourceManagerInstance() { + return s_resource_manager; + } + }; + + ALWAYS_INLINE OsResourceManager &GetResourceManager() { + return ResourceManagerHolder::GetResourceManagerInstance(); + } + +} diff --git a/libraries/libstratosphere/source/os/impl/os_rng_manager.hpp b/libraries/libstratosphere/source/os/impl/os_rng_manager.hpp new file mode 100644 index 00000000..7abc003f --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_rng_manager.hpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::os::impl { + + ALWAYS_INLINE RngManager &GetRngManager() { + return GetResourceManager().GetRngManager(); + } + +} diff --git a/libraries/libstratosphere/source/os/impl/os_rng_manager_impl.cpp b/libraries/libstratosphere/source/os/impl/os_rng_manager_impl.cpp new file mode 100644 index 00000000..21edde42 --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_rng_manager_impl.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "os_rng_manager_impl.hpp" + +namespace ams::os::impl { + + u64 RngManager::GenerateRandomU64() { + std::scoped_lock lk(this->lock); + + if (AMS_UNLIKELY(!this->initialized)) { + this->Initialize(); + } + + return this->mt.GenerateRandomU64(); + } + +} diff --git a/libraries/libstratosphere/source/os/impl/os_rng_manager_impl.hpp b/libraries/libstratosphere/source/os/impl/os_rng_manager_impl.hpp new file mode 100644 index 00000000..25406633 --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_rng_manager_impl.hpp @@ -0,0 +1,34 @@ +/* + * 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::os::impl { + + class RngManager { + private: + util::TinyMT mt; + os::Mutex lock; + bool initialized; + private: + void Initialize(); + public: + constexpr RngManager() : mt(), lock(), initialized() { /* ... */ } + public: + u64 GenerateRandomU64(); + }; + +} diff --git a/libraries/libstratosphere/source/os/impl/os_rng_manager_impl.os.horizon.cpp b/libraries/libstratosphere/source/os/impl/os_rng_manager_impl.os.horizon.cpp new file mode 100644 index 00000000..84a350c3 --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_rng_manager_impl.os.horizon.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "os_rng_manager_impl.hpp" + +namespace ams::os::impl { + + void RngManager::Initialize() { + /* Retrieve entropy from kernel. */ + u32 seed[4]; + static_assert(util::size(seed) == util::TinyMT::NumStateWords); + + /* Nintendo does not check the result of these invocations, but we will for safety. */ + /* Nintendo uses entropy values 0, 1 to seed the public TinyMT random, and values */ + /* 2, 3 to seed os::detail::RngManager's private TinyMT random. */ + R_ABORT_UNLESS(svcGetInfo(reinterpret_cast(&seed[0]), InfoType_RandomEntropy, INVALID_HANDLE, 2)); + R_ABORT_UNLESS(svcGetInfo(reinterpret_cast(&seed[2]), InfoType_RandomEntropy, INVALID_HANDLE, 3)); + + this->mt.Initialize(seed, util::size(seed)); + + /* Note that we've initialized. */ + this->initialized = true; + } + +} diff --git a/libraries/libstratosphere/source/os/impl/os_tick_manager.hpp b/libraries/libstratosphere/source/os/impl/os_tick_manager.hpp new file mode 100644 index 00000000..69a8a5ee --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_tick_manager.hpp @@ -0,0 +1,30 @@ +/* + * 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 "os_resource_manager.hpp" + +namespace ams::os::impl { + + ALWAYS_INLINE TickManager &GetTickManager() { + return GetResourceManager().GetTickManager(); + } + + ALWAYS_INLINE Tick GetCurrentTick() { + return GetTickManager().GetTick(); + } + +} diff --git a/libraries/libstratosphere/source/os/impl/os_tick_manager_impl.cpp b/libraries/libstratosphere/source/os/impl/os_tick_manager_impl.cpp new file mode 100644 index 00000000..209cc984 --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_tick_manager_impl.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "os_tick_manager.hpp" + +namespace ams::os::impl { + + TimeSpan TickManager::ConvertToTimeSpan(Tick tick) const { + /* Get the tick value. */ + const s64 tick_val = tick.GetInt64Value(); + + /* Get the tick frequency. */ + const s64 tick_freq = GetTickFrequency(); + AMS_AUDIT(tick_freq < MaxTickFrequency); + + /* Clamp tick to range. */ + if (tick_val > GetMaxTick()) { + return TimeSpan::FromNanoSeconds(std::numeric_limits::max()); + } else if (tick_val < -GetMaxTick()) { + return TimeSpan::FromNanoSeconds(std::numeric_limits::min()); + } else { + /* Convert to timespan. */ + constexpr s64 NanoSecondsPerSecond = TimeSpan::FromSeconds(1).GetNanoSeconds(); + const s64 seconds = tick_val / tick_freq; + const s64 frac = tick_val % tick_freq; + const TimeSpan ts = TimeSpan::FromSeconds(seconds) + TimeSpan::FromNanoSeconds(frac * NanoSecondsPerSecond / tick_freq); + + constexpr TimeSpan ZeroTS = TimeSpan::FromNanoSeconds(0); + AMS_ASSERT(!((tick_val > 0 && ts < ZeroTS) || (tick_val < 0 && ts > ZeroTS))); + + return ts; + } + } + + Tick TickManager::ConvertToTick(TimeSpan ts) const { + /* Get the TimeSpan in nanoseconds. */ + const s64 ns = ts.GetNanoSeconds(); + + /* Clamp ns to range. */ + if (ns > GetMaxTimeSpanNs()) { + return Tick(std::numeric_limits::max()); + } else if (ns < -GetMaxTimeSpanNs()) { + return Tick(std::numeric_limits::min()); + } else { + /* Get the tick frequency. */ + const s64 tick_freq = GetTickFrequency(); + AMS_AUDIT(tick_freq < MaxTickFrequency); + + /* Convert to tick. */ + constexpr s64 NanoSecondsPerSecond = TimeSpan::FromSeconds(1).GetNanoSeconds(); + const bool negative = ns < 0; + s64 seconds = ns / NanoSecondsPerSecond; + s64 frac = ns % NanoSecondsPerSecond; + + /* If negative, negate seconds/frac. */ + if (negative) { + seconds = -seconds; + frac = -frac; + } + + /* Calculate the tick, and invert back to negative if needed. */ + s64 tick = (seconds * tick_freq) + ((frac * tick_freq + NanoSecondsPerSecond - 1) / NanoSecondsPerSecond); + if (negative) { + tick = -tick; + } + + return Tick(tick); + } + } + +} diff --git a/libraries/libstratosphere/source/os/impl/os_tick_manager_impl.hpp b/libraries/libstratosphere/source/os/impl/os_tick_manager_impl.hpp new file mode 100644 index 00000000..eda9cb7c --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_tick_manager_impl.hpp @@ -0,0 +1,56 @@ +/* + * 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 + +#ifdef ATMOSPHERE_OS_HORIZON + #include "os_tick_manager_impl.os.horizon.hpp" +#else + #error "Unknown OS for TickManagerImpl" +#endif + +namespace ams::os::impl { + + /* Tick frequency must be less than INT64_MAX / 1 second. */ + static constexpr s64 MaxTickFrequency = (std::numeric_limits::max() / TimeSpan::FromSeconds(1).GetNanoSeconds()) - 1; + + class TickManager { + private: + TickManagerImpl impl; + public: + constexpr TickManager() : impl() { /* ... */ } + + ALWAYS_INLINE Tick GetTick() const { + return this->impl.GetTick(); + } + + ALWAYS_INLINE s64 GetTickFrequency() const { + return this->impl.GetTickFrequency(); + } + + ALWAYS_INLINE s64 GetMaxTick() const { + return this->impl.GetMaxTick(); + } + + ALWAYS_INLINE s64 GetMaxTimeSpanNs() const { + return this->impl.GetMaxTimeSpanNs(); + } + + TimeSpan ConvertToTimeSpan(Tick tick) const; + Tick ConvertToTick(TimeSpan ts) const; + }; + +} diff --git a/libraries/libstratosphere/source/os/impl/os_tick_manager_impl.os.horizon.hpp b/libraries/libstratosphere/source/os/impl/os_tick_manager_impl.os.horizon.hpp new file mode 100644 index 00000000..3ab32219 --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_tick_manager_impl.os.horizon.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 + +namespace ams::os::impl { + + class TickManagerImpl { + public: + constexpr TickManagerImpl() { /* ... */ } + + ALWAYS_INLINE Tick GetTick() const { + s64 tick; + #if defined(ATMOSPHERE_ARCH_ARM64) + __asm__ __volatile__("mrs %[tick], cntpct_el0" : [tick]"=&r"(tick) :: "memory"); + #else + #error "Unknown Architecture for TickManagerImpl::GetTick" + #endif + return Tick(tick); + } + + static constexpr ALWAYS_INLINE s64 GetTickFrequency() { + return static_cast(::ams::svc::TicksPerSecond); + } + + static constexpr ALWAYS_INLINE s64 GetMaxTick() { + static_assert(GetTickFrequency() <= TimeSpan::FromSeconds(1).GetNanoSeconds()); + return (std::numeric_limits::max() / TimeSpan::FromSeconds(1).GetNanoSeconds()) * GetTickFrequency(); + } + + static constexpr ALWAYS_INLINE s64 GetMaxTimeSpanNs() { + static_assert(GetTickFrequency() <= TimeSpan::FromSeconds(1).GetNanoSeconds()); + return TimeSpan::FromNanoSeconds(std::numeric_limits::max()).GetNanoSeconds(); + } + }; + +} diff --git a/libraries/libstratosphere/source/os/impl/os_waitable_holder_base.hpp b/libraries/libstratosphere/source/os/impl/os_waitable_holder_base.hpp index 441ac04b..d64fc1b1 100644 --- a/libraries/libstratosphere/source/os/impl/os_waitable_holder_base.hpp +++ b/libraries/libstratosphere/source/os/impl/os_waitable_holder_base.hpp @@ -38,7 +38,7 @@ namespace ams::os::impl { virtual Handle GetHandle() const = 0; /* Gets the amount of time remaining until this wakes up. */ virtual u64 GetWakeupTime() const { - return U64_MAX; + return std::numeric_limits::max(); } /* Interface with manager. */ 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 2f66b7c0..73691df5 100644 --- a/libraries/libstratosphere/source/os/impl/os_waitable_manager_impl.cpp +++ b/libraries/libstratosphere/source/os/impl/os_waitable_manager_impl.cpp @@ -51,7 +51,7 @@ namespace ams::os::impl{ WaitableHolderBase *objects[MaximumHandleCount]; const size_t count = this->BuildHandleArray(object_handles, objects); - const u64 end_time = infinite ? U64_MAX : armTicksToNs(armGetSystemTick()); + const u64 end_time = infinite ? std::numeric_limits::max() : armTicksToNs(armGetSystemTick()); while (true) { this->current_time = armTicksToNs(armGetSystemTick()); diff --git a/libraries/libstratosphere/source/os/impl/os_waitable_manager_impl.hpp b/libraries/libstratosphere/source/os/impl/os_waitable_manager_impl.hpp index be2fc702..0c068dd4 100644 --- a/libraries/libstratosphere/source/os/impl/os_waitable_manager_impl.hpp +++ b/libraries/libstratosphere/source/os/impl/os_waitable_manager_impl.hpp @@ -44,7 +44,7 @@ namespace ams::os::impl { public: /* Wait. */ WaitableHolderBase *WaitAny() { - return this->WaitAnyImpl(true, U64_MAX); + return this->WaitAnyImpl(true, std::numeric_limits::max()); } WaitableHolderBase *TryWaitAny() { diff --git a/libraries/libstratosphere/source/os/os_interrupt_event.cpp b/libraries/libstratosphere/source/os/os_interrupt_event.cpp index e700107c..87e4f77f 100644 --- a/libraries/libstratosphere/source/os/os_interrupt_event.cpp +++ b/libraries/libstratosphere/source/os/os_interrupt_event.cpp @@ -49,7 +49,7 @@ namespace ams::os { while (true) { /* Continuously wait, until success. */ - R_TRY_CATCH(svcWaitSynchronizationSingle(this->handle.Get(), U64_MAX)) { + R_TRY_CATCH(svcWaitSynchronizationSingle(this->handle.Get(), std::numeric_limits::max())) { R_CATCH(svc::ResultCancelled) { continue; } } R_END_TRY_CATCH_WITH_ABORT_UNLESS; diff --git a/libraries/libstratosphere/source/os/os_random.cpp b/libraries/libstratosphere/source/os/os_random.cpp index 3498b14a..cd3962d2 100644 --- a/libraries/libstratosphere/source/os/os_random.cpp +++ b/libraries/libstratosphere/source/os/os_random.cpp @@ -42,7 +42,7 @@ namespace ams::os { void GenerateRandomBytes(void *dst, size_t size) { std::scoped_lock lk(g_random_mutex); - if (!g_initialized_random) { + if (AMS_UNLIKELY(!g_initialized_random)) { impl::InitializeRandomImpl(&g_random); g_initialized_random = true; } diff --git a/libraries/libstratosphere/source/os/os_tick.cpp b/libraries/libstratosphere/source/os/os_tick.cpp new file mode 100644 index 00000000..71804c35 --- /dev/null +++ b/libraries/libstratosphere/source/os/os_tick.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "impl/os_tick_manager.hpp" + +namespace ams::os { + + Tick GetSystemTick() { + return impl::GetTickManager().GetTick(); + } + + s64 GetSystemTickFrequency() { + return impl::GetTickManager().GetTickFrequency(); + } + + TimeSpan ConvertToTimeSpan(Tick tick) { + return impl::GetTickManager().ConvertToTimeSpan(tick); + } + + Tick ConvertToTick(TimeSpan ts) { + return impl::GetTickManager().ConvertToTick(ts); + } + +} diff --git a/libraries/libstratosphere/source/patcher/patcher_api.cpp b/libraries/libstratosphere/source/patcher/patcher_api.cpp index f4056190..74c7637f 100644 --- a/libraries/libstratosphere/source/patcher/patcher_api.cpp +++ b/libraries/libstratosphere/source/patcher/patcher_api.cpp @@ -237,7 +237,10 @@ namespace ams::patcher { } /* Print the path for this directory. */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-truncation" std::snprintf(path + patches_dir_path_len, sizeof(path) - patches_dir_path_len, "/%s", entry.name); +#pragma GCC diagnostic pop const size_t patch_dir_path_len = patches_dir_path_len + 1 + std::strlen(entry.name); /* Open the patch directory. */ diff --git a/libraries/libstratosphere/source/sf/hipc/sf_hipc_api.cpp b/libraries/libstratosphere/source/sf/hipc/sf_hipc_api.cpp index 4b109f96..3f6658a6 100644 --- a/libraries/libstratosphere/source/sf/hipc/sf_hipc_api.cpp +++ b/libraries/libstratosphere/source/sf/hipc/sf_hipc_api.cpp @@ -23,9 +23,9 @@ namespace ams::sf::hipc { s32 unused_index; if (message_buf == armGetTls()) { /* Consider: AMS_ABORT_UNLESS(message_buf_size == TlsMessageBufferSize); */ - return svcReplyAndReceive(&unused_index, &session_handle, 1, INVALID_HANDLE, U64_MAX); + return svcReplyAndReceive(&unused_index, &session_handle, 1, INVALID_HANDLE, std::numeric_limits::max()); } else { - return svcReplyAndReceiveWithUserBuffer(&unused_index, message_buf, message_buf_size, &session_handle, 1, INVALID_HANDLE, U64_MAX); + return svcReplyAndReceiveWithUserBuffer(&unused_index, message_buf, message_buf_size, &session_handle, 1, INVALID_HANDLE, std::numeric_limits::max()); } } diff --git a/libraries/libstratosphere/source/updater/updater_api.cpp b/libraries/libstratosphere/source/updater/updater_api.cpp index 55551c7e..ec978113 100644 --- a/libraries/libstratosphere/source/updater/updater_api.cpp +++ b/libraries/libstratosphere/source/updater/updater_api.cpp @@ -115,7 +115,7 @@ namespace ams::updater { Result VerifyBootImagesAndRepairIfNeeded(bool *out_repaired, BootModeType mode, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type) { /* Get system data id for boot images (819/81A/81B/81C). */ - ncm::SystemDataId bip_data_id; + ncm::SystemDataId bip_data_id = {}; R_TRY(GetBootImagePackageId(&bip_data_id, mode, work_buffer, work_buffer_size)); /* Verify the boot images in NAND. */ diff --git a/libraries/libvapours/include/vapours/ams/ams_api_version.h b/libraries/libvapours/include/vapours/ams/ams_api_version.h index b443a597..986ac169 100644 --- a/libraries/libvapours/include/vapours/ams/ams_api_version.h +++ b/libraries/libvapours/include/vapours/ams/ams_api_version.h @@ -17,7 +17,7 @@ #define ATMOSPHERE_RELEASE_VERSION_MAJOR 0 #define ATMOSPHERE_RELEASE_VERSION_MINOR 10 -#define ATMOSPHERE_RELEASE_VERSION_MICRO 4 +#define ATMOSPHERE_RELEASE_VERSION_MICRO 5 #define ATMOSPHERE_RELEASE_VERSION ATMOSPHERE_RELEASE_VERSION_MAJOR, ATMOSPHERE_RELEASE_VERSION_MINOR, ATMOSPHERE_RELEASE_VERSION_MICRO diff --git a/libraries/libvapours/include/vapours/crypto/crypto_sha256_generator.hpp b/libraries/libvapours/include/vapours/crypto/crypto_sha256_generator.hpp index 9b6a5477..2585a35d 100644 --- a/libraries/libvapours/include/vapours/crypto/crypto_sha256_generator.hpp +++ b/libraries/libvapours/include/vapours/crypto/crypto_sha256_generator.hpp @@ -22,6 +22,11 @@ namespace ams::crypto { + struct Sha256Context { + u32 intermediate_hash[impl::Sha256Impl::HashSize / sizeof(u32)]; + u64 bits_consumed; + }; + class Sha256Generator { private: using Impl = impl::Sha256Impl; @@ -54,6 +59,22 @@ namespace ams::crypto { void GetHash(void *dst, size_t size) { this->impl.GetHash(dst, size); } + + void InitializeWithContext(const Sha256Context *context) { + this->impl.InitializeWithContext(context); + } + + size_t GetContext(Sha256Context *context) const { + return this->impl.GetContext(context); + } + + size_t GetBufferedDataSize() const { + return this->impl.GetBufferedDataSize(); + } + + void GetBufferedData(void *dst, size_t dst_size) const { + return this->impl.GetBufferedData(dst, dst_size); + } }; void GenerateSha256Hash(void *dst, size_t dst_size, const void *src, size_t src_size); diff --git a/libraries/libvapours/include/vapours/crypto/impl/crypto_sha256_impl.hpp b/libraries/libvapours/include/vapours/crypto/impl/crypto_sha256_impl.hpp index 4fa60ca2..bbd145ef 100644 --- a/libraries/libvapours/include/vapours/crypto/impl/crypto_sha256_impl.hpp +++ b/libraries/libvapours/include/vapours/crypto/impl/crypto_sha256_impl.hpp @@ -21,6 +21,12 @@ #include #include +namespace ams::crypto { + + struct Sha256Context; + +} + namespace ams::crypto::impl { class Sha256Impl { @@ -47,6 +53,17 @@ namespace ams::crypto::impl { void Initialize(); void Update(const void *data, size_t size); void GetHash(void *dst, size_t size); + + void InitializeWithContext(const Sha256Context *context); + size_t GetContext(Sha256Context *context) const; + + size_t GetBufferedDataSize() const { return this->state.num_buffered; } + + void GetBufferedData(void *dst, size_t dst_size) const { + AMS_ASSERT(dst_size >= this->GetBufferedDataSize()); + + std::memcpy(dst, this->state.buffer, this->GetBufferedDataSize()); + } }; /* static_assert(HashFunction); */ diff --git a/libraries/libvapours/include/vapours/svc.hpp b/libraries/libvapours/include/vapours/svc.hpp index b2648752..2ce0d36a 100644 --- a/libraries/libvapours/include/vapours/svc.hpp +++ b/libraries/libvapours/include/vapours/svc.hpp @@ -21,3 +21,4 @@ #include #include +#include diff --git a/libraries/libvapours/include/vapours/svc/arch/arm64/svc_thread_local_region.hpp b/libraries/libvapours/include/vapours/svc/arch/arm64/svc_thread_local_region.hpp new file mode 100644 index 00000000..c871ff4c --- /dev/null +++ b/libraries/libvapours/include/vapours/svc/arch/arm64/svc_thread_local_region.hpp @@ -0,0 +1,38 @@ +/* + * 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::svc::arch::arm64 { + + constexpr inline size_t NumTlsSlots = 16; + constexpr inline size_t MessageBufferSize = 0x100; + + struct ThreadLocalRegion { + u32 message_buffer[MessageBufferSize / sizeof(u32)]; + u16 disable_count; + u16 preemption_state; + /* TODO: How should we handle libnx vs Nintendo user thread local space? */ + uintptr_t TODO[(0x200 - 0x108) / sizeof(uintptr_t)]; + }; + + ALWAYS_INLINE ThreadLocalRegion *GetThreadLocalRegion() { + ThreadLocalRegion *tlr; + __asm__ __volatile__("mrs %[tlr], tpidrro_el0" : [tlr]"=&r"(tlr) :: "memory"); + return tlr; + } + +} diff --git a/libraries/libvapours/include/vapours/svc/ipc/svc_message_buffer.hpp b/libraries/libvapours/include/vapours/svc/ipc/svc_message_buffer.hpp new file mode 100644 index 00000000..6afa9d97 --- /dev/null +++ b/libraries/libvapours/include/vapours/svc/ipc/svc_message_buffer.hpp @@ -0,0 +1,547 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::svc::ipc { + + ALWAYS_INLINE u32 *GetMessageBuffer() { + return GetThreadLocalRegion()->message_buffer; + } + + constexpr inline size_t MessageBufferSize = sizeof(::ams::svc::ThreadLocalRegion::message_buffer); + + class MessageBuffer { + public: + class MessageHeader { + private: + /* Define fields for the first header word. */ + using Tag = util::BitPack32::Field<0, BITSIZEOF(u16), u16>; + using PointerCount = util::BitPack32::Field; + using SendCount = util::BitPack32::Field; + using ReceiveCount = util::BitPack32::Field; + using ExchangeCount = util::BitPack32::Field; + static_assert(ExchangeCount::Next == BITSIZEOF(u32)); + + /* Define fields for the second header word. */ + using RawCount = util::BitPack32::Field<0, 10, s32>; + using ReceiveListCount = util::BitPack32::Field; + using Reserved0 = util::BitPack32::Field; + using ReceiveListOffset = util::BitPack32::Field; + using HasSpecialHeader = util::BitPack32::Field; + + static constexpr inline u64 NullTag = 0; + static_assert(HasSpecialHeader::Next == BITSIZEOF(u32)); + public: + enum ReceiveListCountType { + ReceiveListCountType_None = 0, + ReceiveListCountType_ToMessageBuffer = 1, + ReceiveListCountType_ToSingleBuffer = 2, + + ReceiveListCountType_CountOffset = 2, + ReceiveListCountType_CountMax = 13, + }; + private: + util::BitPack32 header[2]; + public: + constexpr ALWAYS_INLINE MessageHeader() : header({util::BitPack32(0), util::BitPack32(0)}) { + this->header[0].Set(NullTag); + } + + constexpr ALWAYS_INLINE MessageHeader(u16 tag, bool special, s32 ptr, s32 send, s32 recv, s32 exch, s32 raw, s32 recv_list) : header({util::BitPack32(0), util::BitPack32(0)}) { + this->header[0].Set(tag); + this->header[0].Set(ptr); + this->header[0].Set(send); + this->header[0].Set(recv); + this->header[0].Set(exch); + + this->header[1].Set(raw); + this->header[1].Set(recv_list); + this->header[1].Set(special); + } + + ALWAYS_INLINE explicit MessageHeader(const MessageBuffer &buf) : header({util::BitPack32(0), util::BitPack32(0)}) { + buf.Get(0, this->header, util::size(this->header)); + } + + ALWAYS_INLINE explicit MessageHeader(const u32 *msg) : header({util::BitPack32(msg[0]), util::BitPack32(msg[1])}) { /* ... */ } + + constexpr ALWAYS_INLINE u16 GetTag() const { + return this->header[0].Get(); + } + + constexpr ALWAYS_INLINE s32 GetPointerCount() const { + return this->header[0].Get(); + } + + constexpr ALWAYS_INLINE s32 GetSendCount() const { + return this->header[0].Get(); + } + + constexpr ALWAYS_INLINE s32 GetReceiveCount() const { + return this->header[0].Get(); + } + + constexpr ALWAYS_INLINE s32 GetExchangeCount() const { + return this->header[0].Get(); + } + + constexpr ALWAYS_INLINE s32 GetMapAliasCount() const { + return this->GetSendCount() + this->GetReceiveCount() + this->GetExchangeCount(); + } + + constexpr ALWAYS_INLINE s32 GetRawCount() const { + return this->header[1].Get(); + } + + constexpr ALWAYS_INLINE s32 GetReceiveListCount() const { + return this->header[1].Get(); + } + + constexpr ALWAYS_INLINE s32 GetReceiveListOffset() const { + return this->header[1].Get(); + } + + constexpr ALWAYS_INLINE bool GetHasSpecialHeader() const { + return this->header[1].Get(); + } + + constexpr ALWAYS_INLINE void SetReceiveListCount(s32 recv_list) { + this->header[1].Set(recv_list); + } + + constexpr ALWAYS_INLINE const util::BitPack32 *GetData() const { + return this->header; + } + + static constexpr ALWAYS_INLINE size_t GetDataSize() { + return sizeof(header); + } + }; + + class SpecialHeader { + private: + /* Define fields for the header word. */ + using HasProcessId = util::BitPack32::Field<0, 1, bool>; + using CopyHandleCount = util::BitPack32::Field; + using MoveHandleCount = util::BitPack32::Field; + private: + util::BitPack32 header; + bool has_header; + public: + constexpr ALWAYS_INLINE explicit SpecialHeader(bool pid, s32 copy, s32 move) : header(0), has_header(true) { + this->header.Set(pid); + this->header.Set(copy); + this->header.Set(move); + } + + ALWAYS_INLINE explicit SpecialHeader(const MessageBuffer &buf, const MessageHeader &hdr) : header(0), has_header(hdr.GetHasSpecialHeader()) { + if (this->has_header) { + buf.Get(MessageHeader::GetDataSize() / sizeof(util::BitPack32), std::addressof(this->header), sizeof(this->header) / sizeof(util::BitPack32)); + } + } + + constexpr ALWAYS_INLINE bool GetHasProcessId() const { + return this->header.Get(); + } + + constexpr ALWAYS_INLINE bool GetCopyHandleCount() const { + return this->header.Get(); + } + + constexpr ALWAYS_INLINE bool GetMoveHandleCount() const { + return this->header.Get(); + } + + constexpr ALWAYS_INLINE const util::BitPack32 *GetHeader() const { + return std::addressof(this->header); + } + + constexpr ALWAYS_INLINE size_t GetHeaderSize() const { + if (this->has_header) { + return sizeof(this->header); + } else { + return 0; + } + } + + constexpr ALWAYS_INLINE size_t GetDataSize() const { + if (this->has_header) { + return (this->GetHasProcessId() ? sizeof(u64) : 0) + + (this->GetCopyHandleCount() * sizeof(Handle)) + + (this->GetMoveHandleCount() * sizeof(Handle)); + } else { + return 0; + } + } + }; + + class MapAliasDescriptor { + public: + enum Attribute { + Attribute_Ipc = 0, + Attribute_NonSecureIpc = 1, + Attribute_NonDeviceIpc = 3, + }; + private: + /* Define fields for the first two words. */ + using SizeLow = util::BitPack32::Field<0, BITSIZEOF(u32), u32>; + using AddressLow = util::BitPack32::Field<0, BITSIZEOF(u32), u32>; + + /* Define fields for the packed descriptor word. */ + using Attributes = util::BitPack32::Field<0, 2, Attribute>; + using AddressHigh = util::BitPack32::Field; + using Reserved = util::BitPack32::Field; + using SizeHigh = util::BitPack32::Field; + using AddressMid = util::BitPack32::Field; + + constexpr ALWAYS_INLINE u32 GetAddressMid(u64 address) { + return static_cast(address >> AddressLow::Count) & ((1u << AddressMid::Count) - 1); + } + + constexpr ALWAYS_INLINE u32 GetAddressHigh(u64 address) { + return static_cast(address >> (AddressLow::Count + AddressMid::Count)); + } + private: + util::BitPack32 data[3]; + public: + constexpr ALWAYS_INLINE MapAliasDescriptor() : data({util::BitPack32(0), util::BitPack32(0), util::BitPack32(0)}) { /* ... */ } + + ALWAYS_INLINE MapAliasDescriptor(const void *buffer, size_t _size, Attribute attr = Attribute_Ipc) : data({util::BitPack32(0), util::BitPack32(0), util::BitPack32(0)}) { + const u64 address = reinterpret_cast(buffer); + const u64 size = static_cast(_size); + this->data[0] = { static_cast(size) }; + this->data[1] = { static_cast(address) }; + + this->data[2].Set(attr); + this->data[2].Set(GetAddressMid(address)); + this->data[2].Set(static_cast(size >> SizeLow::Count)); + this->data[2].Set(GetAddressHigh(address)); + } + + ALWAYS_INLINE MapAliasDescriptor(const MessageBuffer &buf, s32 index) : data({util::BitPack32(0), util::BitPack32(0), util::BitPack32(0)}) { + buf.Get(index, this->data, util::size(this->data)); + } + + constexpr ALWAYS_INLINE uintptr_t GetAddress() const { + const u64 address = (static_cast((this->data[2].Get() << AddressMid::Count) | this->data[2].Get()) << AddressLow::Count) | this->data[1].Get(); + return address; + } + + constexpr ALWAYS_INLINE uintptr_t GetSize() const { + const u64 size = (static_cast(this->data[2].Get()) << SizeLow::Count) | this->data[0].Get(); + return size; + } + + constexpr ALWAYS_INLINE Attribute GetAttribute() const { + return this->data[2].Get(); + } + + constexpr ALWAYS_INLINE const util::BitPack32 *GetData() const { + return this->data; + } + + static constexpr ALWAYS_INLINE size_t GetDataSize() { + return sizeof(data); + } + }; + + class PointerDescriptor { + private: + /* Define fields for the packed descriptor word. */ + using Index = util::BitPack32::Field<0, 4, s32>; + using Reserved0 = util::BitPack32::Field; + using AddressHigh = util::BitPack32::Field; + using Reserved1 = util::BitPack32::Field; + using AddressMid = util::BitPack32::Field; + using Size = util::BitPack32::Field; + + /* Define fields for the second word. */ + using AddressLow = util::BitPack32::Field<0, BITSIZEOF(u32), u32>; + + constexpr ALWAYS_INLINE u32 GetAddressMid(u64 address) { + return static_cast(address >> AddressLow::Count) & ((1u << AddressMid::Count) - 1); + } + + constexpr ALWAYS_INLINE u32 GetAddressHigh(u64 address) { + return static_cast(address >> (AddressLow::Count + AddressMid::Count)); + } + private: + util::BitPack32 data[2]; + public: + constexpr ALWAYS_INLINE PointerDescriptor() : data({util::BitPack32(0), util::BitPack32(0)}) { /* ... */ } + + ALWAYS_INLINE PointerDescriptor(const void *buffer, size_t size, s32 index) : data({util::BitPack32(0), util::BitPack32(0)}) { + const u64 address = reinterpret_cast(buffer); + + this->data[0].Set(index); + this->data[0].Set(GetAddressHigh(address)); + this->data[0].Set(GetAddressMid(address)); + this->data[0].Set(size); + + this->data[1] = { static_cast(address) }; + } + + ALWAYS_INLINE PointerDescriptor(const MessageBuffer &buf, s32 index) : data({util::BitPack32(0), util::BitPack32(0)}) { + buf.Get(index, this->data, util::size(this->data)); + } + + constexpr ALWAYS_INLINE s32 GetIndex() const { + return this->data[0].Get(); + } + + constexpr ALWAYS_INLINE uintptr_t GetAddress() const { + const u64 address = (static_cast((this->data[0].Get() << AddressMid::Count) | this->data[0].Get()) << AddressLow::Count) | this->data[1].Get(); + return address; + } + + constexpr ALWAYS_INLINE size_t GetSize() const { + return this->data[0].Get(); + } + + constexpr ALWAYS_INLINE const util::BitPack32 *GetData() const { + return this->data; + } + + static constexpr ALWAYS_INLINE size_t GetDataSize() { + return sizeof(data); + } + }; + + class ReceiveListEntry { + private: + /* Define fields for the first word. */ + using AddressLow = util::BitPack32::Field<0, BITSIZEOF(u32), u32>; + + /* Define fields for the packed descriptor word. */ + using AddressHigh = util::BitPack32::Field<0, 7, u32>; + using Reserved = util::BitPack32::Field; + using Size = util::BitPack32::Field; + + constexpr ALWAYS_INLINE u32 GetAddressHigh(u64 address) { + return static_cast(address >> (AddressLow::Count)); + } + private: + util::BitPack32 data[2]; + public: + constexpr ALWAYS_INLINE ReceiveListEntry() : data({util::BitPack32(0), util::BitPack32(0)}) { /* ... */ } + + ALWAYS_INLINE ReceiveListEntry(const void *buffer, size_t size) : data({util::BitPack32(0), util::BitPack32(0)}) { + const u64 address = reinterpret_cast(buffer); + + this->data[0] = { static_cast(address) }; + + this->data[1].Set(GetAddressHigh(address)); + this->data[1].Set(size); + } + + ALWAYS_INLINE ReceiveListEntry(u32 a, u32 b) : data({util::BitPack32(a), util::BitPack32(b)}) { /* ... */ } + + constexpr ALWAYS_INLINE uintptr_t GetAddress() { + const u64 address = (static_cast(this->data[1].Get()) << AddressLow::Count) | this->data[0].Get(); + return address; + } + + constexpr ALWAYS_INLINE size_t GetSize() const { + return this->data[1].Get(); + } + + constexpr ALWAYS_INLINE const util::BitPack32 *GetData() const { + return this->data; + } + + static constexpr ALWAYS_INLINE size_t GetDataSize() { + return sizeof(data); + } + }; + private: + u32 *buffer; + size_t size; + public: + constexpr MessageBuffer(u32 *b, size_t sz) : buffer(b), size(sz) { /* ... */ } + constexpr explicit MessageBuffer(u32 *b) : buffer(b), size(sizeof(::ams::svc::ThreadLocalRegion::message_buffer)) { /* ... */ } + + constexpr ALWAYS_INLINE size_t GetBufferSize() const { + return this->size; + } + + ALWAYS_INLINE void Get(s32 index, util::BitPack32 *dst, size_t count) const { + /* Ensure that this doesn't get re-ordered. */ + __asm__ __volatile__("" ::: "memory"); + + /* Get the words. */ + static_assert(sizeof(*dst) == sizeof(*this->buffer)); + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wclass-memaccess" + __builtin_memcpy(dst, this->buffer + index, count * sizeof(*dst)); +#pragma GCC diagnostic pop + } + + ALWAYS_INLINE s32 Set(s32 index, const util::BitPack32 *src, size_t count) const { + /* Ensure that this doesn't get re-ordered. */ + __asm__ __volatile__("" ::: "memory"); + + /* Set the words. */ + __builtin_memcpy(this->buffer + index, src, count * sizeof(*src)); + + /* Ensure that this doesn't get re-ordered. */ + __asm__ __volatile__("" ::: "memory"); + + return index + count; + } + + template + ALWAYS_INLINE const T &GetRaw(s32 index) const { + return *reinterpret_cast(this->buffer + index); + } + + template + ALWAYS_INLINE s32 SetRaw(s32 index, const T &val) { + *reinterpret_cast(this->buffer + index) = val; + return index + (util::AlignUp(sizeof(val), sizeof(*this->buffer)) / sizeof(*this->buffer)); + } + + ALWAYS_INLINE void GetRawArray(s32 index, void *dst, size_t len) { + __builtin_memcpy(dst, this->buffer + index, len); + } + + ALWAYS_INLINE void SetRawArray(s32 index, const void *src, size_t len) { + __builtin_memcpy(this->buffer + index, src, len); + } + + ALWAYS_INLINE void SetNull() const { + this->Set(MessageHeader()); + } + + ALWAYS_INLINE s32 Set(const MessageHeader &hdr) const { + __builtin_memcpy(this->buffer, hdr.GetData(), hdr.GetDataSize()); + return hdr.GetDataSize() / sizeof(*this->buffer); + } + + ALWAYS_INLINE s32 Set(const SpecialHeader &spc) const { + const s32 index = MessageHeader::GetDataSize() / sizeof(*this->buffer); + __builtin_memcpy(this->buffer + index, spc.GetHeader(), spc.GetHeaderSize()); + return index + (spc.GetHeaderSize() / sizeof(*this->buffer)); + } + + ALWAYS_INLINE s32 SetHandle(s32 index, const ::ams::svc::Handle &hnd) { + static_assert(util::IsAligned(sizeof(hnd), sizeof(*this->buffer))); + __builtin_memcpy(this->buffer + index, std::addressof(hnd), sizeof(hnd)); + return index + (sizeof(hnd) / sizeof(*this->buffer)); + } + + ALWAYS_INLINE s32 SetProcessId(s32 index, const u64 pid) { + static_assert(util::IsAligned(sizeof(pid), sizeof(*this->buffer))); + __builtin_memcpy(this->buffer + index, std::addressof(pid), sizeof(pid)); + return index + (sizeof(pid) / sizeof(*this->buffer)); + } + + ALWAYS_INLINE s32 Set(s32 index, const MapAliasDescriptor &desc) { + __builtin_memcpy(this->buffer + index, desc.GetData(), desc.GetDataSize()); + return index + (desc.GetDataSize() / sizeof(*this->buffer)); + } + + ALWAYS_INLINE s32 Set(s32 index, const PointerDescriptor &desc) { + __builtin_memcpy(this->buffer + index, desc.GetData(), desc.GetDataSize()); + return index + (desc.GetDataSize() / sizeof(*this->buffer)); + } + + ALWAYS_INLINE s32 Set(s32 index, const ReceiveListEntry &desc) { + __builtin_memcpy(this->buffer + index, desc.GetData(), desc.GetDataSize()); + return index + (desc.GetDataSize() / sizeof(*this->buffer)); + } + + ALWAYS_INLINE s32 Set(s32 index, const u32 val) { + static_assert(util::IsAligned(sizeof(val), sizeof(*this->buffer))); + __builtin_memcpy(this->buffer + index, std::addressof(val), sizeof(val)); + return index + (sizeof(val) / sizeof(*this->buffer)); + } + + ALWAYS_INLINE Result GetAsyncResult() const { + MessageHeader hdr(this->buffer); + MessageHeader null{}; + R_SUCCEED_IF(AMS_UNLIKELY((__builtin_memcmp(hdr.GetData(), null.GetData(), MessageHeader::GetDataSize()) != 0))); + return this->buffer[MessageHeader::GetDataSize() / sizeof(*this->buffer)]; + } + + ALWAYS_INLINE void SetAsyncResult(Result res) const { + const s32 index = this->Set(MessageHeader()); + const auto value = res.GetValue(); + static_assert(util::IsAligned(sizeof(value), sizeof(*this->buffer))); + __builtin_memcpy(this->buffer + index, std::addressof(value), sizeof(value)); + } + + ALWAYS_INLINE u64 GetProcessId(s32 index) const { + u64 pid; + __builtin_memcpy(std::addressof(pid), this->buffer + index, sizeof(pid)); + return pid; + } + + ALWAYS_INLINE ams::svc::Handle GetHandle(s32 index) const { + static_assert(sizeof(ams::svc::Handle) == sizeof(*this->buffer)); + return ::ams::svc::Handle(this->buffer[index]); + } + + static constexpr ALWAYS_INLINE s32 GetSpecialDataIndex(const MessageHeader &hdr, const SpecialHeader &spc) { + return (MessageHeader::GetDataSize() / sizeof(util::BitPack32)) + (spc.GetHeaderSize() / sizeof(util::BitPack32)); + } + + static constexpr ALWAYS_INLINE s32 GetPointerDescriptorIndex(const MessageHeader &hdr, const SpecialHeader &spc) { + return GetSpecialDataIndex(hdr, spc) + (spc.GetDataSize() / sizeof(util::BitPack32)); + } + + static constexpr ALWAYS_INLINE s32 GetMapAliasDescriptorIndex(const MessageHeader &hdr, const SpecialHeader &spc) { + return GetPointerDescriptorIndex(hdr, spc) + (hdr.GetPointerCount() * PointerDescriptor::GetDataSize() / sizeof(util::BitPack32)); + } + + static constexpr ALWAYS_INLINE s32 GetRawDataIndex(const MessageHeader &hdr, const SpecialHeader &spc) { + return GetMapAliasDescriptorIndex(hdr, spc) + (hdr.GetMapAliasCount() * MapAliasDescriptor::GetDataSize() / sizeof(util::BitPack32)); + } + + static constexpr ALWAYS_INLINE s32 GetReceiveListIndex(const MessageHeader &hdr, const SpecialHeader &spc) { + if (const s32 recv_list_index = hdr.GetReceiveListOffset()) { + return recv_list_index; + } else { + return GetRawDataIndex(hdr, spc) + hdr.GetRawCount(); + } + } + + static constexpr ALWAYS_INLINE s32 GetMessageBufferSize(const MessageHeader &hdr, const SpecialHeader &spc) { + /* Get the size of the plain message. */ + size_t msg_size = GetReceiveListIndex(hdr, spc) * sizeof(util::BitPack32); + + /* Add the size of the receive list. */ + const auto count = hdr.GetReceiveListCount(); + switch (count) { + case MessageHeader::ReceiveListCountType_None: + break; + case MessageHeader::ReceiveListCountType_ToMessageBuffer: + break; + case MessageHeader::ReceiveListCountType_ToSingleBuffer: + msg_size += ReceiveListEntry::GetDataSize(); + break; + default: + msg_size += (count - MessageHeader::ReceiveListCountType_CountOffset) * ReceiveListEntry::GetDataSize(); + break; + } + + return msg_size; + } + }; + +} diff --git a/libraries/libvapours/include/vapours/svc/svc_common.hpp b/libraries/libvapours/include/vapours/svc/svc_common.hpp index 49bd9132..3e3850a0 100644 --- a/libraries/libvapours/include/vapours/svc/svc_common.hpp +++ b/libraries/libvapours/include/vapours/svc/svc_common.hpp @@ -28,13 +28,15 @@ namespace ams::svc { using Handle = u32; #endif - static constexpr size_t MaxWaitSynchronizationHandleCount = 0x40; + constexpr inline size_t MaxWaitSynchronizationHandleCount = 0x40; enum PseudoHandle : Handle { CurrentThread = 0xFFFF8000, CurrentProcess = 0xFFFF8001, }; + constexpr inline Handle InvalidHandle = Handle(0); + constexpr ALWAYS_INLINE bool operator==(const Handle &lhs, const PseudoHandle &rhs) { return static_cast(lhs) == static_cast(rhs); } @@ -55,7 +57,7 @@ namespace ams::svc { return handle == PseudoHandle::CurrentProcess || handle == PseudoHandle::CurrentThread; } -#ifdef ATMOSPHERE_ARCH_ARM64 +#if defined(ATMOSPHERE_ARCH_ARM64) namespace lp64 { /* ... */ } @@ -72,7 +74,7 @@ namespace ams::svc { using namespace ::ams::svc::aarch64::ilp32; #endif -#elif defined ATMOSPHERE_ARCH_ARM +#elif defined(ATMOSPHERE_ARCH_ARM) namespace ilp32 { /* ... */ } namespace aarch32 { /* ... */ } diff --git a/libraries/libvapours/include/vapours/svc/svc_select_thread_local_region.hpp b/libraries/libvapours/include/vapours/svc/svc_select_thread_local_region.hpp new file mode 100644 index 00000000..3f339366 --- /dev/null +++ b/libraries/libvapours/include/vapours/svc/svc_select_thread_local_region.hpp @@ -0,0 +1,39 @@ +/* + * 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 + +#if defined(ATMOSPHERE_ARCH_ARM64) + + #include + namespace ams::svc { + using ams::svc::arch::arm64::ThreadLocalRegion; + using ams::svc::arch::arm64::GetThreadLocalRegion; + } + +#else + + #error "Unknown architecture for svc::ThreadLocalRegion" + +#endif + +namespace ams::svc { + + constexpr inline size_t ThreadLocalRegionSize = 0x200; + static_assert(sizeof(::ams::svc::ThreadLocalRegion) == ThreadLocalRegionSize); + +} \ No newline at end of file diff --git a/libraries/libvapours/include/vapours/svc/svc_types.hpp b/libraries/libvapours/include/vapours/svc/svc_types.hpp index 9b339743..40f36cbf 100644 --- a/libraries/libvapours/include/vapours/svc/svc_types.hpp +++ b/libraries/libvapours/include/vapours/svc/svc_types.hpp @@ -17,6 +17,7 @@ #include #include +#include #include #include #include diff --git a/libraries/libvapours/include/vapours/svc/svc_types_common.hpp b/libraries/libvapours/include/vapours/svc/svc_types_common.hpp index 5ba4d391..df74ea0f 100644 --- a/libraries/libvapours/include/vapours/svc/svc_types_common.hpp +++ b/libraries/libvapours/include/vapours/svc/svc_types_common.hpp @@ -277,8 +277,6 @@ namespace ams::svc { ThreadActivity_Paused = 1, }; - constexpr size_t ThreadLocalRegionSize = 0x200; - constexpr s32 LowestThreadPriority = 63; constexpr s32 HighestThreadPriority = 0; diff --git a/libraries/libvapours/source/crypto/impl/crypto_sha256_impl.arch.arm64.cpp b/libraries/libvapours/source/crypto/impl/crypto_sha256_impl.arch.arm64.cpp index e6893335..d99c497c 100644 --- a/libraries/libvapours/source/crypto/impl/crypto_sha256_impl.arch.arm64.cpp +++ b/libraries/libvapours/source/crypto/impl/crypto_sha256_impl.arch.arm64.cpp @@ -35,6 +35,27 @@ namespace ams::crypto::impl { ::sha256ContextGetHash(reinterpret_cast<::Sha256Context *>(std::addressof(this->state)), dst); } + void Sha256Impl::InitializeWithContext(const Sha256Context *context) { + static_assert(sizeof(this->state) == sizeof(::Sha256Context)); + + /* Copy state in from the context. */ + std::memcpy(this->state.intermediate_hash, context->intermediate_hash, sizeof(this->state.intermediate_hash)); + this->state.bits_consumed = context->bits_consumed; + + /* Clear the rest of state. */ + std::memset(this->state.buffer, 0, sizeof(this->state.buffer)); + this->state.num_buffered = 0; + this->state.finalized = false; + } + + size_t Sha256Impl::GetContext(Sha256Context *context) const { + static_assert(sizeof(this->state) == sizeof(::Sha256Context)); + std::memcpy(context->intermediate_hash, this->state.intermediate_hash, sizeof(context->intermediate_hash)); + context->bits_consumed = this->state.bits_consumed; + + return this->state.num_buffered; + } + #else /* TODO: Non-EL0 implementation. */ diff --git a/mesosphere/kernel/source/arch/arm64/kern_k_thread_context_asm.s b/mesosphere/kernel/source/arch/arm64/kern_k_thread_context_asm.s index b6867cb4..c50e28da 100644 --- a/mesosphere/kernel/source/arch/arm64/kern_k_thread_context_asm.s +++ b/mesosphere/kernel/source/arch/arm64/kern_k_thread_context_asm.s @@ -100,22 +100,22 @@ _ZN3ams4kern4arch5arm6414KThreadContext21RestoreFpuRegisters64ERKS3_: msr fpsr, x1 /* Restore the FPU registers. */ - ldp q0, q1, [sp, #(16 * 0 + 0x80)] - ldp q2, q3, [sp, #(16 * 2 + 0x80)] - ldp q4, q5, [sp, #(16 * 4 + 0x80)] - ldp q6, q7, [sp, #(16 * 6 + 0x80)] - ldp q8, q9, [sp, #(16 * 8 + 0x80)] - ldp q10, q11, [sp, #(16 * 10 + 0x80)] - ldp q12, q13, [sp, #(16 * 12 + 0x80)] - ldp q14, q15, [sp, #(16 * 14 + 0x80)] - ldp q16, q17, [sp, #(16 * 16 + 0x80)] - ldp q18, q19, [sp, #(16 * 18 + 0x80)] - ldp q20, q21, [sp, #(16 * 20 + 0x80)] - ldp q22, q23, [sp, #(16 * 22 + 0x80)] - ldp q24, q25, [sp, #(16 * 24 + 0x80)] - ldp q26, q27, [sp, #(16 * 26 + 0x80)] - ldp q28, q29, [sp, #(16 * 28 + 0x80)] - ldp q30, q31, [sp, #(16 * 30 + 0x80)] + ldp q0, q1, [x0, #(16 * 0 + 0x80)] + ldp q2, q3, [x0, #(16 * 2 + 0x80)] + ldp q4, q5, [x0, #(16 * 4 + 0x80)] + ldp q6, q7, [x0, #(16 * 6 + 0x80)] + ldp q8, q9, [x0, #(16 * 8 + 0x80)] + ldp q10, q11, [x0, #(16 * 10 + 0x80)] + ldp q12, q13, [x0, #(16 * 12 + 0x80)] + ldp q14, q15, [x0, #(16 * 14 + 0x80)] + ldp q16, q17, [x0, #(16 * 16 + 0x80)] + ldp q18, q19, [x0, #(16 * 18 + 0x80)] + ldp q20, q21, [x0, #(16 * 20 + 0x80)] + ldp q22, q23, [x0, #(16 * 22 + 0x80)] + ldp q24, q25, [x0, #(16 * 24 + 0x80)] + ldp q26, q27, [x0, #(16 * 26 + 0x80)] + ldp q28, q29, [x0, #(16 * 28 + 0x80)] + ldp q30, q31, [x0, #(16 * 30 + 0x80)] ret @@ -131,13 +131,13 @@ _ZN3ams4kern4arch5arm6414KThreadContext21RestoreFpuRegisters32ERKS3_: msr fpsr, x1 /* Restore the FPU registers. */ - ldp q0, q1, [sp, #(16 * 0 + 0x80)] - ldp q2, q3, [sp, #(16 * 2 + 0x80)] - ldp q4, q5, [sp, #(16 * 4 + 0x80)] - ldp q6, q7, [sp, #(16 * 6 + 0x80)] - ldp q8, q9, [sp, #(16 * 8 + 0x80)] - ldp q10, q11, [sp, #(16 * 10 + 0x80)] - ldp q12, q13, [sp, #(16 * 12 + 0x80)] - ldp q14, q15, [sp, #(16 * 14 + 0x80)] + ldp q0, q1, [x0, #(16 * 0 + 0x80)] + ldp q2, q3, [x0, #(16 * 2 + 0x80)] + ldp q4, q5, [x0, #(16 * 4 + 0x80)] + ldp q6, q7, [x0, #(16 * 6 + 0x80)] + ldp q8, q9, [x0, #(16 * 8 + 0x80)] + ldp q10, q11, [x0, #(16 * 10 + 0x80)] + ldp q12, q13, [x0, #(16 * 12 + 0x80)] + ldp q14, q15, [x0, #(16 * 14 + 0x80)] ret diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_romfs.cpp b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_romfs.cpp index b0d037f7..5d46b0f1 100644 --- a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_romfs.cpp +++ b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_romfs.cpp @@ -64,21 +64,167 @@ namespace ams::mitm::fs { }; static_assert(std::is_pod::value && sizeof(FileEntry) == 0x20); - constexpr inline DirectoryEntry *GetDirectoryEntry(void *dir_table, u32 offset) { - return reinterpret_cast(reinterpret_cast(dir_table) + offset); - } + template + class TableReader { + NON_COPYABLE(TableReader); + NON_MOVEABLE(TableReader); + private: + static constexpr size_t MaxCachedSize = (1_MB / 4); + static constexpr size_t FallbackCacheSize = 1_KB; + private: + ams::fs::IStorage *storage; + size_t offset; + size_t size; + size_t cache_idx; + void *cache; + u8 fallback_cache[FallbackCacheSize]; + private: + ALWAYS_INLINE void Read(size_t ofs, void *dst, size_t size) { + R_ABORT_UNLESS(this->storage->Read(this->offset + ofs, dst, size)); + } + ALWAYS_INLINE void ReloadCacheImpl(size_t idx) { + const size_t rel_ofs = idx * MaxCachedSize; + AMS_ABORT_UNLESS(rel_ofs < this->size); + const size_t new_cache_size = std::min(this->size - rel_ofs, MaxCachedSize); + this->Read(rel_ofs, this->cache, new_cache_size); + this->cache_idx = idx; + } - constexpr inline FileEntry *GetFileEntry(void *file_table, u32 offset) { - return reinterpret_cast(reinterpret_cast(file_table) + offset); - } + ALWAYS_INLINE void ReloadCache(size_t idx) { + if (this->cache_idx != idx) { + this->ReloadCacheImpl(idx); + } + } - constexpr inline const DirectoryEntry *GetDirectoryEntry(const void *dir_table, u32 offset) { - return reinterpret_cast(reinterpret_cast(dir_table) + offset); - } + ALWAYS_INLINE size_t GetCacheIndex(u32 ofs) { + return ofs / MaxCachedSize; + } + public: + TableReader(ams::fs::IStorage *s, size_t ofs, size_t sz) : storage(s), offset(ofs), size(sz), cache_idx(0) { + this->cache = std::malloc(std::min(sz, MaxCachedSize)); + AMS_ABORT_UNLESS(this->cache != nullptr); + this->ReloadCacheImpl(0); + } + + ~TableReader() { + std::free(this->cache); + } + + const Entry *GetEntry(u32 entry_offset) { + this->ReloadCache(this->GetCacheIndex(entry_offset)); + + const size_t ofs = entry_offset % MaxCachedSize; + + const Entry *entry = reinterpret_cast(reinterpret_cast(this->cache) + ofs); + if (AMS_UNLIKELY(this->GetCacheIndex(entry_offset) != this->GetCacheIndex(entry_offset + sizeof(Entry) + entry->name_size + sizeof(u32)))) { + this->Read(entry_offset, this->fallback_cache, std::min(this->size - entry_offset, FallbackCacheSize)); + entry = reinterpret_cast(this->fallback_cache); + } + return entry; + } + }; + + template + class TableWriter { + NON_COPYABLE(TableWriter); + NON_MOVEABLE(TableWriter); + private: + static constexpr size_t MaxCachedSize = (1_MB / 4); + static constexpr size_t FallbackCacheSize = 1_KB; + private: + ::FsFile *file; + size_t offset; + size_t size; + size_t cache_idx; + void *cache; + u8 fallback_cache[FallbackCacheSize]; + size_t fallback_cache_entry_offset; + size_t fallback_cache_entry_size; + bool cache_dirty; + bool fallback_cache_dirty; + private: + ALWAYS_INLINE void Read(size_t ofs, void *dst, size_t sz) { + u64 read_size; + R_ABORT_UNLESS(fsFileRead(this->file, this->offset + ofs, dst, sz, 0, &read_size)); + AMS_ABORT_UNLESS(read_size == sz); + } + + ALWAYS_INLINE void Write(size_t ofs, const void *src, size_t sz) { + R_ABORT_UNLESS(fsFileWrite(this->file, this->offset + ofs, src, sz, FsWriteOption_None)); + } + + ALWAYS_INLINE void Flush() { + AMS_ABORT_UNLESS(!(this->cache_dirty && this->fallback_cache_dirty)); + + if (this->cache_dirty) { + const size_t ofs = this->cache_idx * MaxCachedSize; + this->Write(ofs, this->cache, std::min(this->size - ofs, MaxCachedSize)); + this->cache_dirty = false; + } + if (this->fallback_cache_dirty) { + this->Write(this->fallback_cache_entry_offset, this->fallback_cache, this->fallback_cache_entry_size); + this->fallback_cache_dirty = false; + } + } + + ALWAYS_INLINE size_t GetCacheIndex(u32 ofs) { + return ofs / MaxCachedSize; + } + + ALWAYS_INLINE void RefreshCacheImpl() { + const size_t cur_cache = this->cache_idx * MaxCachedSize; + this->Read(cur_cache, this->cache, std::min(this->size - cur_cache, MaxCachedSize)); + } + + ALWAYS_INLINE void RefreshCache(u32 entry_offset) { + if (size_t idx = this->GetCacheIndex(entry_offset); idx != this->cache_idx || this->fallback_cache_dirty) { + this->Flush(); + this->cache_idx = idx; + this->RefreshCacheImpl(); + } + } + public: + TableWriter(::FsFile *f, size_t ofs, size_t sz) : file(f), offset(ofs), size(sz), cache_idx(0), fallback_cache_entry_offset(), fallback_cache_entry_size(), cache_dirty(), fallback_cache_dirty() { + const size_t cache_size = std::min(sz, MaxCachedSize); + this->cache = std::malloc(cache_size); + AMS_ABORT_UNLESS(this->cache != nullptr); + std::memset(this->cache, 0, cache_size); + std::memset(this->fallback_cache, 0, sizeof(this->fallback_cache)); + for (size_t cur = 0; cur < this->size; cur += MaxCachedSize) { + this->Write(cur, this->cache, std::min(this->size - cur, MaxCachedSize)); + } + } + + ~TableWriter() { + this->Flush(); + } + + Entry *GetEntry(u32 entry_offset, u32 name_len) { + this->RefreshCache(entry_offset); + + const size_t ofs = entry_offset % MaxCachedSize; + + Entry *entry = reinterpret_cast(reinterpret_cast(this->cache) + ofs); + if (ofs + sizeof(Entry) + util::AlignUp(name_len, sizeof(u32)) > MaxCachedSize) { + this->Flush(); + + this->fallback_cache_entry_offset = entry_offset; + this->fallback_cache_entry_size = sizeof(Entry) + util::AlignUp(name_len, sizeof(u32)); + this->Read(this->fallback_cache_entry_offset, this->fallback_cache, this->fallback_cache_entry_size); + + entry = reinterpret_cast(this->fallback_cache); + this->fallback_cache_dirty = true; + } else { + this->cache_dirty = true; + } + + return entry; + } + }; + + using DirectoryTableWriter = TableWriter; + using FileTableWriter = TableWriter; - constexpr inline const FileEntry *GetFileEntry(const void *file_table, u32 offset) { - return reinterpret_cast(reinterpret_cast(file_table) + offset); - } constexpr inline u32 CalculatePathHash(u32 parent, const char *_path, u32 start, size_t path_len) { const unsigned char *path = reinterpret_cast(_path); @@ -214,34 +360,45 @@ namespace ams::mitm::fs { } - void Builder::VisitDirectory(BuildDirectoryContext *parent, u32 parent_offset, const void *dir_table, size_t dir_table_size, const void *file_table, size_t file_table_size) { - const DirectoryEntry *parent_entry = GetDirectoryEntry(dir_table, parent_offset); - if (parent_entry->file != EmptyEntry) { - const FileEntry *cur_file = GetFileEntry(file_table, parent_entry->file); - while (true) { - this->AddFile(parent, std::make_unique(cur_file->name, cur_file->name_size, cur_file->size, cur_file->offset, this->cur_source_type)); - if (cur_file->sibling == EmptyEntry) { - break; - } - cur_file = GetFileEntry(file_table, cur_file->sibling); - } + class DirectoryTableReader : public TableReader { + public: + DirectoryTableReader(ams::fs::IStorage *s, size_t ofs, size_t sz) : TableReader(s, ofs, sz) { /* ... */ } + }; + + class FileTableReader : public TableReader { + public: + FileTableReader(ams::fs::IStorage *s, size_t ofs, size_t sz) : TableReader(s, ofs, sz) { /* ... */ } + }; + + void Builder::VisitDirectory(BuildDirectoryContext *parent, u32 parent_offset, DirectoryTableReader &dir_table, FileTableReader &file_table) { + const DirectoryEntry *parent_entry = dir_table.GetEntry(parent_offset); + + u32 cur_file_offset = parent_entry->file; + while (cur_file_offset != EmptyEntry) { + const FileEntry *cur_file = file_table.GetEntry(cur_file_offset); + + this->AddFile(parent, std::make_unique(cur_file->name, cur_file->name_size, cur_file->size, cur_file->offset, this->cur_source_type)); + + cur_file_offset = cur_file->sibling; } - if (parent_entry->child != EmptyEntry) { - const DirectoryEntry *cur_child = GetDirectoryEntry(dir_table, parent_entry->child); - u32 cur_child_offset = parent_entry->child; - while (true) { - BuildDirectoryContext *real_child = nullptr; + + u32 cur_child_offset = parent_entry->child; + while (cur_child_offset != EmptyEntry) { + BuildDirectoryContext *real_child = nullptr; + u32 next_child_offset = 0; + { + const DirectoryEntry *cur_child = dir_table.GetEntry(cur_child_offset); + this->AddDirectory(&real_child, parent, std::make_unique(cur_child->name, cur_child->name_size)); AMS_ABORT_UNLESS(real_child != nullptr); - this->VisitDirectory(real_child, cur_child_offset, dir_table, dir_table_size, file_table, file_table_size); - - if (cur_child->sibling == EmptyEntry) { - break; - } - cur_child_offset = cur_child->sibling; - cur_child = GetDirectoryEntry(dir_table, cur_child_offset); + next_child_offset = cur_child->sibling; + __asm__ __volatile__("" ::: "memory"); } + + this->VisitDirectory(real_child, cur_child_offset, dir_table, file_table); + + cur_child_offset = next_child_offset; } } @@ -271,15 +428,11 @@ namespace ams::mitm::fs { AMS_ABORT_UNLESS(header.header_size == sizeof(Header)); /* Read tables. */ - void *tables = std::malloc(header.dir_table_size + header.file_table_size); - ON_SCOPE_EXIT { std::free(tables); }; - void *dir_table = tables; - void *file_table = reinterpret_cast(reinterpret_cast(tables) + header.dir_table_size); - R_ABORT_UNLESS(storage->Read(header.dir_table_ofs, dir_table, size_t(header.dir_table_size))); - R_ABORT_UNLESS(storage->Read(header.file_table_ofs, file_table, size_t(header.file_table_size))); + DirectoryTableReader dir_table(storage, header.dir_table_ofs, header.dir_table_size); + FileTableReader file_table(storage, header.file_table_ofs, header.file_table_size); this->cur_source_type = source_type; - this->VisitDirectory(this->root, 0x0, dir_table, size_t(header.dir_table_size), file_table, size_t(header.file_table_size)); + this->VisitDirectory(this->root, 0x0, dir_table, file_table); } void Builder::Build(std::vector *out_infos) { @@ -372,108 +525,114 @@ namespace ams::mitm::fs { /* Populate file tables. */ { - void *ft_buf = std::malloc(this->file_table_size); + /* Allocate the hash table. */ void *fht_buf = std::malloc(this->file_hash_table_size); - ON_SCOPE_EXIT { std::free(fht_buf); std::free(ft_buf); }; - + AMS_ABORT_UNLESS(fht_buf != nullptr); u32 *file_hash_table = reinterpret_cast(fht_buf); - FileEntry *file_table = reinterpret_cast(ft_buf); std::memset(file_hash_table, 0xFF, this->file_hash_table_size); + ON_SCOPE_EXIT { + R_ABORT_UNLESS(fsFileWrite(&metadata_file, this->dir_hash_table_size + this->dir_table_size, file_hash_table, this->file_hash_table_size, FsWriteOption_None)); + std::free(fht_buf); + }; - for (const auto &it : this->files) { - BuildFileContext *cur_file = it.get(); - FileEntry *cur_entry = GetFileEntry(file_table, cur_file->entry_offset); - - /* Set entry fields. */ - cur_entry->parent = cur_file->parent->entry_offset; - cur_entry->sibling = (cur_file->sibling == nullptr) ? EmptyEntry : cur_file->sibling->entry_offset; - cur_entry->offset = cur_file->offset; - cur_entry->size = cur_file->size; - - /* Insert into hash table. */ - const u32 name_size = cur_file->path_len; - const size_t hash_ind = CalculatePathHash(cur_entry->parent, cur_file->path.get(), 0, name_size) % num_file_hash_table_entries; - cur_entry->hash = file_hash_table[hash_ind]; - file_hash_table[hash_ind] = cur_file->entry_offset; - - /* Set name. */ - cur_entry->name_size = name_size; - if (name_size) { - std::memcpy(cur_entry->name, cur_file->path.get(), name_size); - for (size_t i = name_size; i < util::AlignUp(name_size, 4); i++) { - cur_entry->name[i] = 0; + /* Write the file table. */ + { + FileTableWriter file_table(&metadata_file, this->dir_hash_table_size + this->dir_table_size + this->file_hash_table_size, this->file_table_size); + + for (const auto &it : this->files) { + BuildFileContext *cur_file = it.get(); + FileEntry *cur_entry = file_table.GetEntry(cur_file->entry_offset, cur_file->path_len); + + /* Set entry fields. */ + cur_entry->parent = cur_file->parent->entry_offset; + cur_entry->sibling = (cur_file->sibling == nullptr) ? EmptyEntry : cur_file->sibling->entry_offset; + cur_entry->offset = cur_file->offset; + cur_entry->size = cur_file->size; + + /* Insert into hash table. */ + const u32 name_size = cur_file->path_len; + const size_t hash_ind = CalculatePathHash(cur_entry->parent, cur_file->path.get(), 0, name_size) % num_file_hash_table_entries; + cur_entry->hash = file_hash_table[hash_ind]; + file_hash_table[hash_ind] = cur_file->entry_offset; + + /* Set name. */ + cur_entry->name_size = name_size; + if (name_size) { + std::memcpy(cur_entry->name, cur_file->path.get(), name_size); + for (size_t i = name_size; i < util::AlignUp(name_size, 4); i++) { + cur_entry->name[i] = 0; + } } - } - /* Emplace a source. */ - switch (cur_file->source_type) { - case DataSourceType::Storage: - case DataSourceType::File: - { - /* Try to compact if possible. */ - auto &back = out_infos->back(); - if (back.source_type == cur_file->source_type) { - back.size = cur_file->offset + FilePartitionOffset + cur_file->size - back.virtual_offset; - } else { - out_infos->emplace_back(cur_file->offset + FilePartitionOffset, cur_file->size, cur_file->source_type, cur_file->orig_offset + FilePartitionOffset); + /* Emplace a source. */ + switch (cur_file->source_type) { + case DataSourceType::Storage: + case DataSourceType::File: + { + /* Try to compact if possible. */ + auto &back = out_infos->back(); + if (back.source_type == cur_file->source_type) { + back.size = cur_file->offset + FilePartitionOffset + cur_file->size - back.virtual_offset; + } else { + out_infos->emplace_back(cur_file->offset + FilePartitionOffset, cur_file->size, cur_file->source_type, cur_file->orig_offset + FilePartitionOffset); + } } - } - break; - case DataSourceType::LooseSdFile: - { - char *new_path = new char[cur_file->GetPathLength() + 1]; - cur_file->GetPath(new_path); - out_infos->emplace_back(cur_file->offset + FilePartitionOffset, cur_file->size, cur_file->source_type, new_path); - } - break; - AMS_UNREACHABLE_DEFAULT_CASE(); + break; + case DataSourceType::LooseSdFile: + { + char *new_path = new char[cur_file->GetPathLength() + 1]; + cur_file->GetPath(new_path); + out_infos->emplace_back(cur_file->offset + FilePartitionOffset, cur_file->size, cur_file->source_type, new_path); + } + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } } } - - /* Write to file. */ - R_ABORT_UNLESS(fsFileWrite(&metadata_file, this->dir_hash_table_size + this->dir_table_size, file_hash_table, this->file_hash_table_size, FsWriteOption_None)); - R_ABORT_UNLESS(fsFileWrite(&metadata_file, this->dir_hash_table_size + this->dir_table_size + this->file_hash_table_size, file_table, this->file_table_size, FsWriteOption_None)); } /* Populate directory tables. */ { - void *dt_buf = std::malloc(this->dir_table_size); + /* Allocate the hash table. */ void *dht_buf = std::malloc(this->dir_hash_table_size); - ON_SCOPE_EXIT { std::free(dht_buf); std::free(dt_buf); }; - + AMS_ABORT_UNLESS(dht_buf != nullptr); u32 *dir_hash_table = reinterpret_cast(dht_buf); - DirectoryEntry *dir_table = reinterpret_cast(dt_buf); std::memset(dir_hash_table, 0xFF, this->dir_hash_table_size); + ON_SCOPE_EXIT { + R_ABORT_UNLESS(fsFileWrite(&metadata_file, 0, dir_hash_table, this->dir_hash_table_size, FsWriteOption_None)); + std::free(dht_buf); + }; - for (const auto &it : this->directories) { - BuildDirectoryContext *cur_dir = it.get(); - DirectoryEntry *cur_entry = GetDirectoryEntry(dir_table, cur_dir->entry_offset); - - /* Set entry fields. */ - cur_entry->parent = cur_dir == this->root ? 0 : cur_dir->parent->entry_offset; - cur_entry->sibling = (cur_dir->sibling == nullptr) ? EmptyEntry : cur_dir->sibling->entry_offset; - cur_entry->child = (cur_dir->child == nullptr) ? EmptyEntry : cur_dir->child->entry_offset; - cur_entry->file = (cur_dir->file == nullptr) ? EmptyEntry : cur_dir->file->entry_offset; - - /* Insert into hash table. */ - const u32 name_size = cur_dir->path_len; - const size_t hash_ind = CalculatePathHash(cur_entry->parent, cur_dir->path.get(), 0, name_size) % num_dir_hash_table_entries; - cur_entry->hash = dir_hash_table[hash_ind]; - dir_hash_table[hash_ind] = cur_dir->entry_offset; - - /* Set name. */ - cur_entry->name_size = name_size; - if (name_size) { - std::memcpy(cur_entry->name, cur_dir->path.get(), name_size); - for (size_t i = name_size; i < util::AlignUp(name_size, 4); i++) { - cur_entry->name[i] = 0; + /* Write the file table. */ + { + DirectoryTableWriter dir_table(&metadata_file, this->dir_hash_table_size, this->dir_table_size); + + for (const auto &it : this->directories) { + BuildDirectoryContext *cur_dir = it.get(); + DirectoryEntry *cur_entry = dir_table.GetEntry(cur_dir->entry_offset, cur_dir->path_len); + + /* Set entry fields. */ + cur_entry->parent = cur_dir == this->root ? 0 : cur_dir->parent->entry_offset; + cur_entry->sibling = (cur_dir->sibling == nullptr) ? EmptyEntry : cur_dir->sibling->entry_offset; + cur_entry->child = (cur_dir->child == nullptr) ? EmptyEntry : cur_dir->child->entry_offset; + cur_entry->file = (cur_dir->file == nullptr) ? EmptyEntry : cur_dir->file->entry_offset; + + /* Insert into hash table. */ + const u32 name_size = cur_dir->path_len; + const size_t hash_ind = CalculatePathHash(cur_entry->parent, cur_dir->path.get(), 0, name_size) % num_dir_hash_table_entries; + cur_entry->hash = dir_hash_table[hash_ind]; + dir_hash_table[hash_ind] = cur_dir->entry_offset; + + /* Set name. */ + cur_entry->name_size = name_size; + if (name_size) { + std::memcpy(cur_entry->name, cur_dir->path.get(), name_size); + for (size_t i = name_size; i < util::AlignUp(name_size, 4); i++) { + cur_entry->name[i] = 0; + } } } } - - /* Write to file. */ - R_ABORT_UNLESS(fsFileWrite(&metadata_file, 0, dir_hash_table, this->dir_hash_table_size, FsWriteOption_None)); - R_ABORT_UNLESS(fsFileWrite(&metadata_file, this->dir_hash_table_size, dir_table, this->dir_table_size, FsWriteOption_None)); } /* Delete maps. */ diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_romfs.hpp b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_romfs.hpp index edb30bbf..96b8cf69 100644 --- a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_romfs.hpp +++ b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_romfs.hpp @@ -199,6 +199,9 @@ namespace ams::mitm::fs::romfs { } }; + class DirectoryTableReader; + class FileTableReader; + struct Builder { NON_COPYABLE(Builder); NON_MOVEABLE(Builder); @@ -243,7 +246,7 @@ namespace ams::mitm::fs::romfs { DataSourceType cur_source_type; private: void VisitDirectory(FsFileSystem *fs, BuildDirectoryContext *parent); - void VisitDirectory(BuildDirectoryContext *parent, u32 parent_offset, const void *dir_table, size_t dir_table_size, const void *file_table, size_t file_table_size); + void VisitDirectory(BuildDirectoryContext *parent, u32 parent_offset, DirectoryTableReader &dir_table, FileTableReader &file_table); void AddDirectory(BuildDirectoryContext **out, BuildDirectoryContext *parent_ctx, std::unique_ptr file_ctx); void AddFile(BuildDirectoryContext *parent_ctx, std::unique_ptr file_ctx); diff --git a/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_api.cpp b/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_api.cpp index ccd463fc..43bd4cd8 100644 --- a/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_api.cpp +++ b/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_api.cpp @@ -522,7 +522,7 @@ namespace ams::dmnt::cheat::impl { Event hook; while (true) { eventLoadRemote(&hook, this_ptr->HookToCreateApplicationProcess(), true); - if (R_SUCCEEDED(eventWait(&hook, U64_MAX))) { + if (R_SUCCEEDED(eventWait(&hook, std::numeric_limits::max()))) { this_ptr->AttachToApplicationProcess(true); } eventClose(&hook); @@ -535,7 +535,7 @@ namespace ams::dmnt::cheat::impl { /* Atomically wait (and clear) signal for new process. */ this_ptr->debug_events_event.Wait(); while (true) { - while (R_SUCCEEDED(svcWaitSynchronizationSingle(this_ptr->GetCheatProcessHandle(), U64_MAX))) { + while (R_SUCCEEDED(svcWaitSynchronizationSingle(this_ptr->GetCheatProcessHandle(), std::numeric_limits::max()))) { std::scoped_lock lk(this_ptr->cheat_lock); /* Handle any pending debug events. */ diff --git a/stratosphere/fatal/source/fatal_config.cpp b/stratosphere/fatal/source/fatal_config.cpp index 85c25121..0ee0edef 100644 --- a/stratosphere/fatal/source/fatal_config.cpp +++ b/stratosphere/fatal/source/fatal_config.cpp @@ -25,7 +25,7 @@ namespace ams::fatal::srv { /* Event creator. */ Handle GetFatalDirtyEventReadableHandle() { Event evt; - R_ABORT_UNLESS(setsysBindFatalDirtyFlagEvent(&evt)); + R_ABORT_UNLESS(setsysAcquireFatalDirtyFlagEventHandle(&evt)); return evt.revent; } diff --git a/stratosphere/fatal/source/fatal_task_screen.cpp b/stratosphere/fatal/source/fatal_task_screen.cpp index 5b94b9e2..9d6262ca 100644 --- a/stratosphere/fatal/source/fatal_task_screen.cpp +++ b/stratosphere/fatal/source/fatal_task_screen.cpp @@ -418,7 +418,7 @@ namespace ams::fatal::srv { Result ShowFatalTask::Run() { /* Don't show the fatal error screen until we've verified the battery is okay. */ - eventWait(const_cast(&this->context->battery_event), U64_MAX); + eventWait(const_cast(&this->context->battery_event), std::numeric_limits::max()); return ShowFatal(); }