diff --git a/libmesosphere/include/mesosphere/arch/arm64/kern_k_process_page_table.hpp b/libmesosphere/include/mesosphere/arch/arm64/kern_k_process_page_table.hpp index 1376a86..2df7f50 100644 --- a/libmesosphere/include/mesosphere/arch/arm64/kern_k_process_page_table.hpp +++ b/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/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_system_control.hpp b/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_system_control.hpp index 499159b..45fa322 100644 --- a/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_system_control.hpp +++ b/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/libmesosphere/include/mesosphere/kern_k_auto_object.hpp b/libmesosphere/include/mesosphere/kern_k_auto_object.hpp index 460dbe0..2ffc286 100644 --- a/libmesosphere/include/mesosphere/kern_k_auto_object.hpp +++ b/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/libmesosphere/include/mesosphere/kern_k_client_port.hpp b/libmesosphere/include/mesosphere/kern_k_client_port.hpp new file mode 100644 index 0000000..29768d9 --- /dev/null +++ b/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/libmesosphere/include/mesosphere/kern_k_client_session.hpp b/libmesosphere/include/mesosphere/kern_k_client_session.hpp new file mode 100644 index 0000000..c6eef38 --- /dev/null +++ b/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/libmesosphere/include/mesosphere/kern_k_handle_table.hpp b/libmesosphere/include/mesosphere/kern_k_handle_table.hpp index 6f8ceee..d6ba17c 100644 --- a/libmesosphere/include/mesosphere/kern_k_handle_table.hpp +++ b/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/libmesosphere/include/mesosphere/kern_k_light_client_session.hpp b/libmesosphere/include/mesosphere/kern_k_light_client_session.hpp new file mode 100644 index 0000000..474ae35 --- /dev/null +++ b/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/libmesosphere/include/mesosphere/kern_k_light_server_session.hpp b/libmesosphere/include/mesosphere/kern_k_light_server_session.hpp new file mode 100644 index 0000000..7fcb23a --- /dev/null +++ b/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/libmesosphere/include/mesosphere/kern_k_light_session.hpp b/libmesosphere/include/mesosphere/kern_k_light_session.hpp index 5edd889..b4257a1 100644 --- a/libmesosphere/include/mesosphere/kern_k_light_session.hpp +++ b/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/libmesosphere/include/mesosphere/kern_k_object_name.hpp b/libmesosphere/include/mesosphere/kern_k_object_name.hpp index 87e76c8..09ab273 100644 --- a/libmesosphere/include/mesosphere/kern_k_object_name.hpp +++ b/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/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp b/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp index aa121f6..e9630f5 100644 --- a/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp +++ b/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/libmesosphere/include/mesosphere/kern_k_port.hpp b/libmesosphere/include/mesosphere/kern_k_port.hpp index 40799b1..65a4609 100644 --- a/libmesosphere/include/mesosphere/kern_k_port.hpp +++ b/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/libmesosphere/include/mesosphere/kern_k_server_port.hpp b/libmesosphere/include/mesosphere/kern_k_server_port.hpp new file mode 100644 index 0000000..c8a429a --- /dev/null +++ b/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/libmesosphere/include/mesosphere/kern_k_server_session.hpp b/libmesosphere/include/mesosphere/kern_k_server_session.hpp new file mode 100644 index 0000000..70d0583 --- /dev/null +++ b/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/libmesosphere/include/mesosphere/kern_k_session.hpp b/libmesosphere/include/mesosphere/kern_k_session.hpp index ccd5bc0..0ee803b 100644 --- a/libmesosphere/include/mesosphere/kern_k_session.hpp +++ b/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/libmesosphere/include/mesosphere/kern_panic.hpp b/libmesosphere/include/mesosphere/kern_panic.hpp index 3df06b1..2f31453 100644 --- a/libmesosphere/include/mesosphere/kern_panic.hpp +++ b/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/libmesosphere/source/arch/arm64/kern_exception_handlers.cpp b/libmesosphere/source/arch/arm64/kern_exception_handlers.cpp index d284e39..37fefef 100644 --- a/libmesosphere/source/arch/arm64/kern_exception_handlers.cpp +++ b/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/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp b/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp index 790022f..b22e118 100644 --- a/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp +++ b/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/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.cpp b/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.cpp index 49f2ac2..3ae3750 100644 --- a/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.cpp +++ b/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/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.hpp b/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.hpp index fa78b0e..72abe5d 100644 --- a/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.hpp +++ b/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/libmesosphere/source/kern_k_client_port.cpp b/libmesosphere/source/kern_k_client_port.cpp new file mode 100644 index 0000000..10eb847 --- /dev/null +++ b/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/libmesosphere/source/kern_k_object_name.cpp b/libmesosphere/source/kern_k_object_name.cpp new file mode 100644 index 0000000..7631224 --- /dev/null +++ b/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/libmesosphere/source/kern_k_page_table_base.cpp b/libmesosphere/source/kern_k_page_table_base.cpp index 4b4eb1d..217a272 100644 --- a/libmesosphere/source/kern_k_page_table_base.cpp +++ b/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/libmesosphere/source/kern_k_port.cpp b/libmesosphere/source/kern_k_port.cpp new file mode 100644 index 0000000..9b081ed --- /dev/null +++ b/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/libmesosphere/source/kern_k_server_port.cpp b/libmesosphere/source/kern_k_server_port.cpp new file mode 100644 index 0000000..06096bc --- /dev/null +++ b/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/libmesosphere/source/libc/arch/arm64/asmdefs.h b/libmesosphere/source/libc/arch/arm64/asmdefs.h new file mode 100644 index 0000000..edc4e66 --- /dev/null +++ b/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/libmesosphere/source/libc/arch/arm64/memcmp.arch.arm64-broken.s b/libmesosphere/source/libc/arch/arm64/memcmp.arch.arm64-broken.s new file mode 100644 index 0000000..609f7e2 --- /dev/null +++ b/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/libmesosphere/source/libc/arch/arm64/memcpy.arch.arm64-broken.s b/libmesosphere/source/libc/arch/arm64/memcpy.arch.arm64-broken.s new file mode 100644 index 0000000..02ed1dd --- /dev/null +++ b/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/libmesosphere/source/libc/arch/arm64/memset.arch.arm64-broken.s b/libmesosphere/source/libc/arch/arm64/memset.arch.arm64-broken.s new file mode 100644 index 0000000..d8d2727 --- /dev/null +++ b/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/libmesosphere/source/libc/kern_libc.c b/libmesosphere/source/libc/kern_libc_generic.c similarity index 65% rename from libmesosphere/source/libc/kern_libc.c rename to libmesosphere/source/libc/kern_libc_generic.c index 839a81d..fb7e4cc 100644 --- a/libmesosphere/source/libc/kern_libc.c +++ b/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/libmesosphere/source/svc/kern_svc_info.cpp b/libmesosphere/source/svc/kern_svc_info.cpp index b1dc3a5..84f330e 100644 --- a/libmesosphere/source/svc/kern_svc_info.cpp +++ b/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/libmesosphere/source/svc/kern_svc_port.cpp b/libmesosphere/source/svc/kern_svc_port.cpp index e8b1a73..2aa297d 100644 --- a/libmesosphere/source/svc/kern_svc_port.cpp +++ b/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/libmesosphere/source/svc/kern_svc_process.cpp b/libmesosphere/source/svc/kern_svc_process.cpp index 17350db..4bf47cd 100644 --- a/libmesosphere/source/svc/kern_svc_process.cpp +++ b/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/libmesosphere/source/svc/kern_svc_secure_monitor_call.cpp b/libmesosphere/source/svc/kern_svc_secure_monitor_call.cpp index ccbb1b9..8c0034f 100644 --- a/libmesosphere/source/svc/kern_svc_secure_monitor_call.cpp +++ b/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/libstratosphere/include/stratosphere/fs/fs_substorage.hpp b/libstratosphere/include/stratosphere/fs/fs_substorage.hpp index 9470b47..4e39ae3 100644 --- a/libstratosphere/include/stratosphere/fs/fs_substorage.hpp +++ b/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/libstratosphere/include/stratosphere/os.hpp b/libstratosphere/include/stratosphere/os.hpp index 3ac4ef9..abd5e77 100644 --- a/libstratosphere/include/stratosphere/os.hpp +++ b/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/libstratosphere/include/stratosphere/os/os_tick.hpp b/libstratosphere/include/stratosphere/os/os_tick.hpp new file mode 100644 index 0000000..67d42b5 --- /dev/null +++ b/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/libstratosphere/source/ams/ams_bpc.c b/libstratosphere/source/ams/ams_bpc.c index 7f46a7c..d04a69b 100644 --- a/libstratosphere/source/ams/ams_bpc.c +++ b/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/libstratosphere/source/kvdb/kvdb_file_key_value_store.cpp b/libstratosphere/source/kvdb/kvdb_file_key_value_store.cpp index 540ef36..eb699b6 100644 --- a/libstratosphere/source/kvdb/kvdb_file_key_value_store.cpp +++ b/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/libstratosphere/source/os/impl/os_inter_process_event.cpp b/libstratosphere/source/os/impl/os_inter_process_event.cpp index ae3e986..4db3e0b 100644 --- a/libstratosphere/source/os/impl/os_inter_process_event.cpp +++ b/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/libstratosphere/source/os/impl/os_resource_manager.cpp b/libstratosphere/source/os/impl/os_resource_manager.cpp new file mode 100644 index 0000000..539f9e1 --- /dev/null +++ b/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/libstratosphere/source/os/impl/os_resource_manager.hpp b/libstratosphere/source/os/impl/os_resource_manager.hpp new file mode 100644 index 0000000..61525e9 --- /dev/null +++ b/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/libstratosphere/source/os/impl/os_rng_manager.hpp b/libstratosphere/source/os/impl/os_rng_manager.hpp new file mode 100644 index 0000000..7abc003 --- /dev/null +++ b/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/libstratosphere/source/os/impl/os_rng_manager_impl.cpp b/libstratosphere/source/os/impl/os_rng_manager_impl.cpp new file mode 100644 index 0000000..21edde4 --- /dev/null +++ b/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/libstratosphere/source/os/impl/os_rng_manager_impl.hpp b/libstratosphere/source/os/impl/os_rng_manager_impl.hpp new file mode 100644 index 0000000..2540663 --- /dev/null +++ b/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/libstratosphere/source/os/impl/os_rng_manager_impl.os.horizon.cpp b/libstratosphere/source/os/impl/os_rng_manager_impl.os.horizon.cpp new file mode 100644 index 0000000..84a350c --- /dev/null +++ b/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/libstratosphere/source/os/impl/os_tick_manager.hpp b/libstratosphere/source/os/impl/os_tick_manager.hpp new file mode 100644 index 0000000..69a8a5e --- /dev/null +++ b/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/libstratosphere/source/os/impl/os_tick_manager_impl.cpp b/libstratosphere/source/os/impl/os_tick_manager_impl.cpp new file mode 100644 index 0000000..209cc98 --- /dev/null +++ b/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/libstratosphere/source/os/impl/os_tick_manager_impl.hpp b/libstratosphere/source/os/impl/os_tick_manager_impl.hpp new file mode 100644 index 0000000..eda9cb7 --- /dev/null +++ b/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/libstratosphere/source/os/impl/os_tick_manager_impl.os.horizon.hpp b/libstratosphere/source/os/impl/os_tick_manager_impl.os.horizon.hpp new file mode 100644 index 0000000..3ab3221 --- /dev/null +++ b/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/libstratosphere/source/os/impl/os_waitable_holder_base.hpp b/libstratosphere/source/os/impl/os_waitable_holder_base.hpp index 441ac04..d64fc1b 100644 --- a/libstratosphere/source/os/impl/os_waitable_holder_base.hpp +++ b/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/libstratosphere/source/os/impl/os_waitable_manager_impl.cpp b/libstratosphere/source/os/impl/os_waitable_manager_impl.cpp index 2f66b7c..73691df 100644 --- a/libstratosphere/source/os/impl/os_waitable_manager_impl.cpp +++ b/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/libstratosphere/source/os/impl/os_waitable_manager_impl.hpp b/libstratosphere/source/os/impl/os_waitable_manager_impl.hpp index be2fc70..0c068dd 100644 --- a/libstratosphere/source/os/impl/os_waitable_manager_impl.hpp +++ b/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/libstratosphere/source/os/os_interrupt_event.cpp b/libstratosphere/source/os/os_interrupt_event.cpp index e700107..87e4f77 100644 --- a/libstratosphere/source/os/os_interrupt_event.cpp +++ b/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/libstratosphere/source/os/os_random.cpp b/libstratosphere/source/os/os_random.cpp index 3498b14..cd3962d 100644 --- a/libstratosphere/source/os/os_random.cpp +++ b/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/libstratosphere/source/os/os_tick.cpp b/libstratosphere/source/os/os_tick.cpp new file mode 100644 index 0000000..71804c3 --- /dev/null +++ b/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/libstratosphere/source/patcher/patcher_api.cpp b/libstratosphere/source/patcher/patcher_api.cpp index f405619..74c7637 100644 --- a/libstratosphere/source/patcher/patcher_api.cpp +++ b/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/libstratosphere/source/sf/hipc/sf_hipc_api.cpp b/libstratosphere/source/sf/hipc/sf_hipc_api.cpp index 4b109f9..3f6658a 100644 --- a/libstratosphere/source/sf/hipc/sf_hipc_api.cpp +++ b/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/libstratosphere/source/updater/updater_api.cpp b/libstratosphere/source/updater/updater_api.cpp index 55551c7..ec97811 100644 --- a/libstratosphere/source/updater/updater_api.cpp +++ b/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/libvapours/include/vapours/ams/ams_api_version.h b/libvapours/include/vapours/ams/ams_api_version.h index b443a59..986ac16 100644 --- a/libvapours/include/vapours/ams/ams_api_version.h +++ b/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/libvapours/include/vapours/crypto/crypto_sha256_generator.hpp b/libvapours/include/vapours/crypto/crypto_sha256_generator.hpp index 9b6a547..2585a35 100644 --- a/libvapours/include/vapours/crypto/crypto_sha256_generator.hpp +++ b/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/libvapours/include/vapours/crypto/impl/crypto_sha256_impl.hpp b/libvapours/include/vapours/crypto/impl/crypto_sha256_impl.hpp index 4fa60ca..bbd145e 100644 --- a/libvapours/include/vapours/crypto/impl/crypto_sha256_impl.hpp +++ b/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/libvapours/include/vapours/svc.hpp b/libvapours/include/vapours/svc.hpp index b264875..2ce0d36 100644 --- a/libvapours/include/vapours/svc.hpp +++ b/libvapours/include/vapours/svc.hpp @@ -21,3 +21,4 @@ #include #include +#include diff --git a/libvapours/include/vapours/svc/arch/arm64/svc_thread_local_region.hpp b/libvapours/include/vapours/svc/arch/arm64/svc_thread_local_region.hpp new file mode 100644 index 0000000..c871ff4 --- /dev/null +++ b/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/libvapours/include/vapours/svc/ipc/svc_message_buffer.hpp b/libvapours/include/vapours/svc/ipc/svc_message_buffer.hpp new file mode 100644 index 0000000..6afa9d9 --- /dev/null +++ b/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/libvapours/include/vapours/svc/svc_common.hpp b/libvapours/include/vapours/svc/svc_common.hpp index 49bd913..3e3850a 100644 --- a/libvapours/include/vapours/svc/svc_common.hpp +++ b/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/libvapours/include/vapours/svc/svc_select_thread_local_region.hpp b/libvapours/include/vapours/svc/svc_select_thread_local_region.hpp new file mode 100644 index 0000000..3f33936 --- /dev/null +++ b/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/libvapours/include/vapours/svc/svc_types.hpp b/libvapours/include/vapours/svc/svc_types.hpp index 9b33974..40f36cb 100644 --- a/libvapours/include/vapours/svc/svc_types.hpp +++ b/libvapours/include/vapours/svc/svc_types.hpp @@ -17,6 +17,7 @@ #include #include +#include #include #include #include diff --git a/libvapours/include/vapours/svc/svc_types_common.hpp b/libvapours/include/vapours/svc/svc_types_common.hpp index 5ba4d39..df74ea0 100644 --- a/libvapours/include/vapours/svc/svc_types_common.hpp +++ b/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/libvapours/source/crypto/impl/crypto_sha256_impl.arch.arm64.cpp b/libvapours/source/crypto/impl/crypto_sha256_impl.arch.arm64.cpp index e689333..d99c497 100644 --- a/libvapours/source/crypto/impl/crypto_sha256_impl.arch.arm64.cpp +++ b/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. */