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