Compare commits

...

17 Commits

Author SHA1 Message Date
Michael Scire
b27c7552d2 git subrepo push libraries
subrepo:
  subdir:   "libraries"
  merged:   "38fc51c6"
upstream:
  origin:   "https://github.com/Atmosphere-NX/Atmosphere-libs"
  branch:   "master"
  commit:   "38fc51c6"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2020-03-18 16:20:29 -07:00
Michael Scire
426257d4ae ams: bump version to 0.10.5 in prep for release later tonight 2020-03-18 16:19:59 -07:00
Michael Scire
7d34d599bb git subrepo push libraries
subrepo:
  subdir:   "libraries"
  merged:   "07684b2c"
upstream:
  origin:   "https://github.com/Atmosphere-NX/Atmosphere-libs"
  branch:   "master"
  commit:   "07684b2c"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2020-03-18 00:15:32 -07:00
Michael Scire
067fe2d10f stratosphere: fix building with latest libnx 2020-03-18 00:14:34 -07:00
Michael Scire
4759c2f92c svc: add ipc accessor boilerplate 2020-03-18 00:09:51 -07:00
Michael Scire
ca26d8ce27 kern: Implement SvcManageNamedPort 2020-03-18 00:09:51 -07:00
Michael Scire
6c52cc3e26 memset: use neon-less impl, reformat other asm 2020-03-18 00:09:50 -07:00
Michael Scire
e42d3a3abf libmesosphere: use ARM-software/optimized-routines for memcpy/memset/memcmp 2020-03-18 00:09:50 -07:00
Michael Scire
884844bc23 svc: revert codegen changes 2020-03-18 00:09:50 -07:00
Michael Scire
f556db8c89 svc: make autogen asm register-clobber aware 2020-03-18 00:09:50 -07:00
Michael Scire
96d15b28c6 kern: implement CallSecureMonitor, some of GetInfo/GetSystemInfo 2020-03-18 00:09:50 -07:00
Michael Scire
37f7afb426 ams.mitm: greatly reduce memory requirements to build romfs 2020-03-18 00:07:19 -07:00
Michael Scire
7dd4e76c1d os: add rngmanager 2020-03-16 13:08:20 -07:00
Michael Scire
daa0deb1bf Add architecture-specific guard for get tick 2020-03-16 01:05:30 -07:00
Michael Scire
43bd733f0a os: implement Tick api, make build with -Werror 2020-03-16 01:02:55 -07:00
Michael Scire
70367e3e7c crypto: add Sha256Context 2020-03-11 03:26:55 -07:00
Michael Scire
45f8343659 kern: tweak KHandleTable impl 2020-03-10 04:54:53 -07:00
78 changed files with 3460 additions and 194 deletions

View File

@@ -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.

View File

@@ -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

View File

@@ -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();
}
};
}

View File

@@ -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);
};
}

View File

@@ -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; }
};

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <mesosphere/kern_common.hpp>
#include <mesosphere/kern_k_synchronization_object.hpp>
namespace ams::kern {
class KPort;
class KClientPort final : public KSynchronizationObject {
MESOSPHERE_AUTOOBJECT_TRAITS(KClientPort, KSynchronizationObject);
private:
std::atomic<s32> num_sessions;
std::atomic<s32> 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. */
};
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <mesosphere/kern_common.hpp>
#include <mesosphere/kern_k_synchronization_object.hpp>
namespace ams::kern {
class KSession;
class KClientSession final : public KAutoObjectWithSlabHeapAndContainer<KClientSession, KAutoObjectWithList> {
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. */
};
}

View File

@@ -130,11 +130,11 @@ namespace ams::kern {
MESOSPHERE_ASSERT_THIS();
/* Handle pseudo-handles. */
if constexpr (std::is_same<T, KProcess>::value) {
if constexpr (std::is_base_of<T, KProcess>::value) {
if (handle == ams::svc::PseudoHandle::CurrentProcess) {
return GetCurrentProcessPointer();
}
} else if constexpr (std::is_same<T, KThread>::value) {
} else if constexpr (std::is_base_of<T, KThread>::value) {
if (handle == ams::svc::PseudoHandle::CurrentThread) {
return GetCurrentThreadPointer();
}
@@ -156,11 +156,11 @@ namespace ams::kern {
static_assert(!std::is_base_of<KInterruptEvent, T>::value);
/* Handle pseudo-handles. */
if constexpr (std::is_same<T, KProcess>::value) {
if constexpr (std::is_base_of<T, KProcess>::value) {
if (handle == ams::svc::PseudoHandle::CurrentProcess) {
return GetCurrentProcessPointer();
}
} else if constexpr (std::is_same<T, KThread>::value) {
} else if constexpr (std::is_base_of<T, KThread>::value) {
if (handle == ams::svc::PseudoHandle::CurrentThread) {
return GetCurrentThreadPointer();
}
@@ -201,7 +201,7 @@ namespace ams::kern {
template<typename T>
ALWAYS_INLINE void Register(ams::svc::Handle handle, T *obj) {
static_assert(std::is_base_of<KAutoObject, T>::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<typename T>
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<T*>();
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;
}
};
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <mesosphere/kern_common.hpp>
#include <mesosphere/kern_k_synchronization_object.hpp>
namespace ams::kern {
class KLightSession;
class KLightClientSession final : public KAutoObjectWithSlabHeapAndContainer<KLightClientSession, KAutoObjectWithList> {
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. */
};
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <mesosphere/kern_common.hpp>
#include <mesosphere/kern_k_synchronization_object.hpp>
#include <mesosphere/kern_k_thread.hpp>
#include <mesosphere/kern_k_thread_queue.hpp>
namespace ams::kern {
class KLightSession;
class KLightServerSession final : public KAutoObjectWithSlabHeapAndContainer<KLightServerSession, KAutoObjectWithList>, public util::IntrusiveListBaseNode<KLightServerSession> {
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. */
};
}

View File

@@ -16,14 +16,52 @@
#pragma once
#include <mesosphere/kern_common.hpp>
#include <mesosphere/kern_k_synchronization_object.hpp>
#include <mesosphere/kern_k_light_server_session.hpp>
#include <mesosphere/kern_k_light_client_session.hpp>
#include <mesosphere/kern_slab_helpers.hpp>
namespace ams::kern {
class KClientPort;
class KProcess;
class KLightSession final : public KAutoObjectWithSlabHeapAndContainer<KLightSession, KAutoObjectWithList> {
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<uintptr_t>(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; }
};
}

View File

@@ -23,7 +23,39 @@ namespace ams::kern {
class KObjectName : public KSlabAllocated<KObjectName>, public util::IntrusiveListBaseNode<KObjectName> {
public:
/* TODO: This is a placeholder definition. */
static constexpr size_t NameLengthMax = 12;
using List = util::IntrusiveListBaseTraits<KObjectName>::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<KAutoObject> Find(const char *name);
template<typename Derived>
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<Derived *>();
R_UNLESS(derived != nullptr, svc::ResultNotFound());
return Delete(obj.GetPointerUnsafe(), name);
}
private:
static KScopedAutoObject<KAutoObject> FindImpl(const char *name);
void Initialize(KAutoObject *obj, const char *name);
bool MatchesName(const char *name) const;
KAutoObject *GetObject() const { return this->object; }
};
}

View File

@@ -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);

View File

@@ -16,14 +16,46 @@
#pragma once
#include <mesosphere/kern_common.hpp>
#include <mesosphere/kern_k_synchronization_object.hpp>
#include <mesosphere/kern_k_client_port.hpp>
#include <mesosphere/kern_k_server_port.hpp>
#include <mesosphere/kern_slab_helpers.hpp>
namespace ams::kern {
class KPort final : public KAutoObjectWithSlabHeapAndContainer<KPort, KAutoObjectWithList> {
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; }
};
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <mesosphere/kern_common.hpp>
#include <mesosphere/kern_k_synchronization_object.hpp>
#include <mesosphere/kern_slab_helpers.hpp>
namespace ams::kern {
class KPort;
class KServerSession;
class KLightServerSession;
class KServerPort final : public KSynchronizationObject {
MESOSPHERE_AUTOOBJECT_TRAITS(KServerPort, KSynchronizationObject);
private:
using SessionList = util::IntrusiveListBaseTraits<KServerSession>::ListType;
using LightSessionList = util::IntrusiveListBaseTraits<KLightServerSession>::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. */
};
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <mesosphere/kern_common.hpp>
#include <mesosphere/kern_k_synchronization_object.hpp>
#include <mesosphere/kern_k_session_request.hpp>
#include <mesosphere/kern_k_light_lock.hpp>
namespace ams::kern {
class KSession;
class KServerSession final : public KSynchronizationObject, public util::IntrusiveListBaseNode<KServerSession> {
MESOSPHERE_AUTOOBJECT_TRAITS(KServerSession, KSynchronizationObject);
private:
using RequestList = util::IntrusiveListBaseTraits<KSessionRequest>::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. */
};
}

View File

@@ -16,14 +16,52 @@
#pragma once
#include <mesosphere/kern_common.hpp>
#include <mesosphere/kern_k_synchronization_object.hpp>
#include <mesosphere/kern_k_server_session.hpp>
#include <mesosphere/kern_k_client_session.hpp>
#include <mesosphere/kern_slab_helpers.hpp>
namespace ams::kern {
class KClientPort;
class KProcess;
class KSession final : public KAutoObjectWithSlabHeapAndContainer<KSession, KAutoObjectWithList> {
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<uintptr_t>(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; }
};
}

View File

@@ -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

View File

@@ -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");
}

View File

@@ -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<KPageGroup, MaxMappedRegisters> 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();
}
}
}

View File

@@ -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);
}
}

View File

@@ -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);

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#include <mesosphere.hpp>
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;
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#include <mesosphere.hpp>
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<KAutoObject> KObjectName::Find(const char *name) {
/* Ensure we have exclusive access to the global list. */
KScopedLightLock lk(g_object_list_lock);
return FindImpl(name);
}
KScopedAutoObject<KAutoObject> 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;
}
}

View File

@@ -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();
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#include <mesosphere.hpp>
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();
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#include <mesosphere.hpp>
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();
}
}
}

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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
<<strncpy>>---counted copy string
INDEX
strncpy
SYNOPSIS
#include <string.h>
char *strncpy(char *restrict <[dst]>, const char *restrict <[src]>,
size_t <[length]>);
DESCRIPTION
<<strncpy>> 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
<<strncpy>> is ANSI C.
<<strncpy>> requires no supporting OS subroutines.
QUICKREF
strncpy ansi pure
*/
#include <string.h>
#include <limits.h>
/*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
<<strncmp>>---character string compare
INDEX
strncmp
SYNOPSIS
#include <string.h>
int strncmp(const char *<[a]>, const char * <[b]>, size_t <[length]>);
DESCRIPTION
<<strncmp>> compares up to <[length]> characters
from the string at <[a]> to the string at <[b]>.
RETURNS
If <<*<[a]>>> sorts lexicographically after <<*<[b]>>>,
<<strncmp>> returns a number greater than zero. If the two
strings are equivalent, <<strncmp>> returns zero. If <<*<[a]>>>
sorts lexicographically before <<*<[b]>>>, <<strncmp>> returns a
number less than zero.
PORTABILITY
<<strncmp>> is ANSI C.
<<strncmp>> requires no supporting OS subroutines.
QUICKREF
strncmp ansi pure
*/
#include <string.h>
#include <limits.h>
/* 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

View File

@@ -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<u32>(info_type), static_cast<u32>(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<KProcess>(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<u32>(info_type), static_cast<u32>(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<ams::svc::InitialProcessIdRangeInfo>(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);
}
}

View File

@@ -21,7 +21,61 @@ namespace ams::kern::svc {
namespace {
Result ManageNamedPort(ams::svc::Handle *out_server_handle, KUserPointer<const char *> 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<KClientPort>(name));
}
return ResultSuccess();
}
}
@@ -36,7 +90,7 @@ namespace ams::kern::svc {
}
Result ManageNamedPort64(ams::svc::Handle *out_server_handle, KUserPointer<const char *> 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<const char *> 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) {

View File

@@ -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<KAutoObject>(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<KProcess *>(obj.GetPointerUnsafe());
} else if (obj->IsDerivedFrom(KThread::GetStaticTypeObj())) {
/* The object is a thread, so we want to use its parent. */
process = reinterpret_cast<KThread *>(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<uint64_t *> 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<uint64_t *> out_process_ids, int32_t max_out_count) {

View File

@@ -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 ============================= */

View File

@@ -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<IStorage> storage, s64 o, s64 sz) : shared_base_storage(storage), base_storage(storage.get()), offset(o), size(sz) {
SubStorage(std::shared_ptr<IStorage> 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);

View File

@@ -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"

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#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);
}
};
}

View File

@@ -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 } },
);
}

View File

@@ -222,7 +222,7 @@ namespace ams::kvdb {
R_UNLESS(file_size <= static_cast<s64>(max_out_size), ResultBufferInsufficient());
/* Read the value. */
const size_t value_size = static_cast<size_t>(value_size);
const size_t value_size = static_cast<size_t>(file_size);
R_TRY(fs::ReadFile(file, 0, out_value, value_size));
*out_size = value_size;

View File

@@ -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<u64>::max())) {
R_CATCH(svc::ResultCancelled) { continue; }
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "os_resource_manager.hpp"
namespace ams::os::impl {
/* TODO: C++20 constinit */
OsResourceManager ResourceManagerHolder::s_resource_manager = {};
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#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();
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::os::impl {
ALWAYS_INLINE RngManager &GetRngManager() {
return GetResourceManager().GetRngManager();
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#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();
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
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();
};
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#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<u64 *>(&seed[0]), InfoType_RandomEntropy, INVALID_HANDLE, 2));
R_ABORT_UNLESS(svcGetInfo(reinterpret_cast<u64 *>(&seed[2]), InfoType_RandomEntropy, INVALID_HANDLE, 3));
this->mt.Initialize(seed, util::size(seed));
/* Note that we've initialized. */
this->initialized = true;
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#include "os_resource_manager.hpp"
namespace ams::os::impl {
ALWAYS_INLINE TickManager &GetTickManager() {
return GetResourceManager().GetTickManager();
}
ALWAYS_INLINE Tick GetCurrentTick() {
return GetTickManager().GetTick();
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#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<s64>::max());
} else if (tick_val < -GetMaxTick()) {
return TimeSpan::FromNanoSeconds(std::numeric_limits<s64>::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<s64>::max());
} else if (ns < -GetMaxTimeSpanNs()) {
return Tick(std::numeric_limits<s64>::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);
}
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#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<s64>::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;
};
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
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<s64>(::ams::svc::TicksPerSecond);
}
static constexpr ALWAYS_INLINE s64 GetMaxTick() {
static_assert(GetTickFrequency() <= TimeSpan::FromSeconds(1).GetNanoSeconds());
return (std::numeric_limits<s64>::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<s64>::max()).GetNanoSeconds();
}
};
}

View File

@@ -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<u64>::max();
}
/* Interface with manager. */

View File

@@ -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<u64>::max() : armTicksToNs(armGetSystemTick());
while (true) {
this->current_time = armTicksToNs(armGetSystemTick());

View File

@@ -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<u64>::max());
}
WaitableHolderBase *TryWaitAny() {

View File

@@ -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<u64>::max())) {
R_CATCH(svc::ResultCancelled) { continue; }
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;

View File

@@ -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;
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#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);
}
}

View File

@@ -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. */

View File

@@ -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<u64>::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<u64>::max());
}
}

View File

@@ -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. */

View File

@@ -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

View File

@@ -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);

View File

@@ -21,6 +21,12 @@
#include <vapours/crypto/impl/crypto_hash_function.hpp>
#include <vapours/crypto/crypto_memory_clear.hpp>
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<Sha256Impl>); */

View File

@@ -21,3 +21,4 @@
#include <vapours/svc/svc_types.hpp>
#include <vapours/svc/svc_definitions.hpp>
#include <vapours/svc/ipc/svc_message_buffer.hpp>

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours/svc/svc_types_common.hpp>
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;
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours/svc/svc_types_common.hpp>
#include <vapours/svc/svc_select_thread_local_region.hpp>
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<Tag::Next, 4, s32>;
using SendCount = util::BitPack32::Field<PointerCount::Next, 4, s32>;
using ReceiveCount = util::BitPack32::Field<SendCount::Next, 4, s32>;
using ExchangeCount = util::BitPack32::Field<ReceiveCount::Next, 4, s32>;
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<RawCount::Next, 4, s32>;
using Reserved0 = util::BitPack32::Field<ReceiveListCount::Next, 6, u32>;
using ReceiveListOffset = util::BitPack32::Field<Reserved0::Next, 11, s32>;
using HasSpecialHeader = util::BitPack32::Field<ReceiveListOffset::Next, 1, bool>;
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<Tag>(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>(tag);
this->header[0].Set<PointerCount>(ptr);
this->header[0].Set<SendCount>(send);
this->header[0].Set<ReceiveCount>(recv);
this->header[0].Set<ExchangeCount>(exch);
this->header[1].Set<RawCount>(raw);
this->header[1].Set<ReceiveListCount>(recv_list);
this->header[1].Set<HasSpecialHeader>(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<Tag>();
}
constexpr ALWAYS_INLINE s32 GetPointerCount() const {
return this->header[0].Get<PointerCount>();
}
constexpr ALWAYS_INLINE s32 GetSendCount() const {
return this->header[0].Get<SendCount>();
}
constexpr ALWAYS_INLINE s32 GetReceiveCount() const {
return this->header[0].Get<ReceiveCount>();
}
constexpr ALWAYS_INLINE s32 GetExchangeCount() const {
return this->header[0].Get<ExchangeCount>();
}
constexpr ALWAYS_INLINE s32 GetMapAliasCount() const {
return this->GetSendCount() + this->GetReceiveCount() + this->GetExchangeCount();
}
constexpr ALWAYS_INLINE s32 GetRawCount() const {
return this->header[1].Get<RawCount>();
}
constexpr ALWAYS_INLINE s32 GetReceiveListCount() const {
return this->header[1].Get<ReceiveListCount>();
}
constexpr ALWAYS_INLINE s32 GetReceiveListOffset() const {
return this->header[1].Get<ReceiveListOffset>();
}
constexpr ALWAYS_INLINE bool GetHasSpecialHeader() const {
return this->header[1].Get<HasSpecialHeader>();
}
constexpr ALWAYS_INLINE void SetReceiveListCount(s32 recv_list) {
this->header[1].Set<ReceiveListCount>(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<HasProcessId::Next, 4, s32>;
using MoveHandleCount = util::BitPack32::Field<CopyHandleCount::Next, 4, s32>;
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<HasProcessId>(pid);
this->header.Set<CopyHandleCount>(copy);
this->header.Set<MoveHandleCount>(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<HasProcessId>();
}
constexpr ALWAYS_INLINE bool GetCopyHandleCount() const {
return this->header.Get<CopyHandleCount>();
}
constexpr ALWAYS_INLINE bool GetMoveHandleCount() const {
return this->header.Get<MoveHandleCount>();
}
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<Attributes::Next, 3, u32>;
using Reserved = util::BitPack32::Field<AddressHigh::Next, 19, u32>;
using SizeHigh = util::BitPack32::Field<Reserved::Next, 4, u32>;
using AddressMid = util::BitPack32::Field<SizeHigh::Next, 4, u32>;
constexpr ALWAYS_INLINE u32 GetAddressMid(u64 address) {
return static_cast<u32>(address >> AddressLow::Count) & ((1u << AddressMid::Count) - 1);
}
constexpr ALWAYS_INLINE u32 GetAddressHigh(u64 address) {
return static_cast<u32>(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<u64>(buffer);
const u64 size = static_cast<u64>(_size);
this->data[0] = { static_cast<u32>(size) };
this->data[1] = { static_cast<u32>(address) };
this->data[2].Set<Attributes>(attr);
this->data[2].Set<AddressMid>(GetAddressMid(address));
this->data[2].Set<SizeHigh>(static_cast<u32>(size >> SizeLow::Count));
this->data[2].Set<AddressHigh>(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<u64>((this->data[2].Get<AddressHigh>() << AddressMid::Count) | this->data[2].Get<AddressMid>()) << AddressLow::Count) | this->data[1].Get<AddressLow>();
return address;
}
constexpr ALWAYS_INLINE uintptr_t GetSize() const {
const u64 size = (static_cast<u64>(this->data[2].Get<SizeHigh>()) << SizeLow::Count) | this->data[0].Get<SizeLow>();
return size;
}
constexpr ALWAYS_INLINE Attribute GetAttribute() const {
return this->data[2].Get<Attributes>();
}
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<Index::Next, 2, u32>;
using AddressHigh = util::BitPack32::Field<Reserved0::Next, 3, u32>;
using Reserved1 = util::BitPack32::Field<AddressHigh::Next, 3, u32>;
using AddressMid = util::BitPack32::Field<Reserved1::Next, 4, u32>;
using Size = util::BitPack32::Field<AddressMid::Next, 16, u32>;
/* 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<u32>(address >> AddressLow::Count) & ((1u << AddressMid::Count) - 1);
}
constexpr ALWAYS_INLINE u32 GetAddressHigh(u64 address) {
return static_cast<u32>(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<u64>(buffer);
this->data[0].Set<Index>(index);
this->data[0].Set<AddressHigh>(GetAddressHigh(address));
this->data[0].Set<AddressMid>(GetAddressMid(address));
this->data[0].Set<Size>(size);
this->data[1] = { static_cast<u32>(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<Index>();
}
constexpr ALWAYS_INLINE uintptr_t GetAddress() const {
const u64 address = (static_cast<u64>((this->data[0].Get<AddressHigh>() << AddressMid::Count) | this->data[0].Get<AddressMid>()) << AddressLow::Count) | this->data[1].Get<AddressLow>();
return address;
}
constexpr ALWAYS_INLINE size_t GetSize() const {
return this->data[0].Get<Size>();
}
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<AddressHigh::Next, 9, u32>;
using Size = util::BitPack32::Field<Reserved::Next, 16, u32>;
constexpr ALWAYS_INLINE u32 GetAddressHigh(u64 address) {
return static_cast<u32>(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<u64>(buffer);
this->data[0] = { static_cast<u32>(address) };
this->data[1].Set<AddressHigh>(GetAddressHigh(address));
this->data[1].Set<Size>(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<u64>(this->data[1].Get<AddressHigh>()) << AddressLow::Count) | this->data[0].Get<AddressLow>();
return address;
}
constexpr ALWAYS_INLINE size_t GetSize() const {
return this->data[1].Get<Size>();
}
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<typename T>
ALWAYS_INLINE const T &GetRaw(s32 index) const {
return *reinterpret_cast<const T *>(this->buffer + index);
}
template<typename T>
ALWAYS_INLINE s32 SetRaw(s32 index, const T &val) {
*reinterpret_cast<const T *>(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;
}
};
}

View File

@@ -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<Handle>(lhs) == static_cast<Handle>(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 { /* ... */ }

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours/svc/svc_common.hpp>
#if defined(ATMOSPHERE_ARCH_ARM64)
#include <vapours/svc/arch/arm64/svc_thread_local_region.hpp>
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);
}

View File

@@ -17,6 +17,7 @@
#include <vapours/svc/svc_common.hpp>
#include <vapours/svc/svc_tick.hpp>
#include <vapours/svc/svc_select_thread_local_region.hpp>
#include <vapours/svc/svc_types_common.hpp>
#include <vapours/svc/svc_types_base.hpp>
#include <vapours/svc/svc_types_dd.hpp>

View File

@@ -277,8 +277,6 @@ namespace ams::svc {
ThreadActivity_Paused = 1,
};
constexpr size_t ThreadLocalRegionSize = 0x200;
constexpr s32 LowestThreadPriority = 63;
constexpr s32 HighestThreadPriority = 0;

View File

@@ -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. */

View File

@@ -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

View File

@@ -64,21 +64,167 @@ namespace ams::mitm::fs {
};
static_assert(std::is_pod<FileEntry>::value && sizeof(FileEntry) == 0x20);
constexpr inline DirectoryEntry *GetDirectoryEntry(void *dir_table, u32 offset) {
return reinterpret_cast<DirectoryEntry *>(reinterpret_cast<uintptr_t>(dir_table) + offset);
}
template<typename Entry>
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<FileEntry *>(reinterpret_cast<uintptr_t>(file_table) + offset);
}
ALWAYS_INLINE void ReloadCache(size_t idx) {
if (this->cache_idx != idx) {
this->ReloadCacheImpl(idx);
}
}
constexpr inline const DirectoryEntry *GetDirectoryEntry(const void *dir_table, u32 offset) {
return reinterpret_cast<const DirectoryEntry *>(reinterpret_cast<uintptr_t>(dir_table) + offset);
}
ALWAYS_INLINE size_t GetCacheIndex(u32 ofs) {
return ofs / MaxCachedSize;
}
public:
TableReader(ams::fs::IStorage *s, size_t ofs, size_t sz) : storage(s), offset(ofs), size(sz), cache_idx(0) {
this->cache = std::malloc(std::min(sz, MaxCachedSize));
AMS_ABORT_UNLESS(this->cache != nullptr);
this->ReloadCacheImpl(0);
}
~TableReader() {
std::free(this->cache);
}
const Entry *GetEntry(u32 entry_offset) {
this->ReloadCache(this->GetCacheIndex(entry_offset));
const size_t ofs = entry_offset % MaxCachedSize;
const Entry *entry = reinterpret_cast<const Entry *>(reinterpret_cast<uintptr_t>(this->cache) + ofs);
if (AMS_UNLIKELY(this->GetCacheIndex(entry_offset) != this->GetCacheIndex(entry_offset + sizeof(Entry) + entry->name_size + sizeof(u32)))) {
this->Read(entry_offset, this->fallback_cache, std::min(this->size - entry_offset, FallbackCacheSize));
entry = reinterpret_cast<const Entry *>(this->fallback_cache);
}
return entry;
}
};
template<typename Entry>
class TableWriter {
NON_COPYABLE(TableWriter);
NON_MOVEABLE(TableWriter);
private:
static constexpr size_t MaxCachedSize = (1_MB / 4);
static constexpr size_t FallbackCacheSize = 1_KB;
private:
::FsFile *file;
size_t offset;
size_t size;
size_t cache_idx;
void *cache;
u8 fallback_cache[FallbackCacheSize];
size_t fallback_cache_entry_offset;
size_t fallback_cache_entry_size;
bool cache_dirty;
bool fallback_cache_dirty;
private:
ALWAYS_INLINE void Read(size_t ofs, void *dst, size_t sz) {
u64 read_size;
R_ABORT_UNLESS(fsFileRead(this->file, this->offset + ofs, dst, sz, 0, &read_size));
AMS_ABORT_UNLESS(read_size == sz);
}
ALWAYS_INLINE void Write(size_t ofs, const void *src, size_t sz) {
R_ABORT_UNLESS(fsFileWrite(this->file, this->offset + ofs, src, sz, FsWriteOption_None));
}
ALWAYS_INLINE void Flush() {
AMS_ABORT_UNLESS(!(this->cache_dirty && this->fallback_cache_dirty));
if (this->cache_dirty) {
const size_t ofs = this->cache_idx * MaxCachedSize;
this->Write(ofs, this->cache, std::min(this->size - ofs, MaxCachedSize));
this->cache_dirty = false;
}
if (this->fallback_cache_dirty) {
this->Write(this->fallback_cache_entry_offset, this->fallback_cache, this->fallback_cache_entry_size);
this->fallback_cache_dirty = false;
}
}
ALWAYS_INLINE size_t GetCacheIndex(u32 ofs) {
return ofs / MaxCachedSize;
}
ALWAYS_INLINE void RefreshCacheImpl() {
const size_t cur_cache = this->cache_idx * MaxCachedSize;
this->Read(cur_cache, this->cache, std::min(this->size - cur_cache, MaxCachedSize));
}
ALWAYS_INLINE void RefreshCache(u32 entry_offset) {
if (size_t idx = this->GetCacheIndex(entry_offset); idx != this->cache_idx || this->fallback_cache_dirty) {
this->Flush();
this->cache_idx = idx;
this->RefreshCacheImpl();
}
}
public:
TableWriter(::FsFile *f, size_t ofs, size_t sz) : file(f), offset(ofs), size(sz), cache_idx(0), fallback_cache_entry_offset(), fallback_cache_entry_size(), cache_dirty(), fallback_cache_dirty() {
const size_t cache_size = std::min(sz, MaxCachedSize);
this->cache = std::malloc(cache_size);
AMS_ABORT_UNLESS(this->cache != nullptr);
std::memset(this->cache, 0, cache_size);
std::memset(this->fallback_cache, 0, sizeof(this->fallback_cache));
for (size_t cur = 0; cur < this->size; cur += MaxCachedSize) {
this->Write(cur, this->cache, std::min(this->size - cur, MaxCachedSize));
}
}
~TableWriter() {
this->Flush();
}
Entry *GetEntry(u32 entry_offset, u32 name_len) {
this->RefreshCache(entry_offset);
const size_t ofs = entry_offset % MaxCachedSize;
Entry *entry = reinterpret_cast<Entry *>(reinterpret_cast<uintptr_t>(this->cache) + ofs);
if (ofs + sizeof(Entry) + util::AlignUp(name_len, sizeof(u32)) > MaxCachedSize) {
this->Flush();
this->fallback_cache_entry_offset = entry_offset;
this->fallback_cache_entry_size = sizeof(Entry) + util::AlignUp(name_len, sizeof(u32));
this->Read(this->fallback_cache_entry_offset, this->fallback_cache, this->fallback_cache_entry_size);
entry = reinterpret_cast<Entry *>(this->fallback_cache);
this->fallback_cache_dirty = true;
} else {
this->cache_dirty = true;
}
return entry;
}
};
using DirectoryTableWriter = TableWriter<DirectoryEntry>;
using FileTableWriter = TableWriter<FileEntry>;
constexpr inline const FileEntry *GetFileEntry(const void *file_table, u32 offset) {
return reinterpret_cast<const FileEntry *>(reinterpret_cast<uintptr_t>(file_table) + offset);
}
constexpr inline u32 CalculatePathHash(u32 parent, const char *_path, u32 start, size_t path_len) {
const unsigned char *path = reinterpret_cast<const unsigned char *>(_path);
@@ -214,34 +360,45 @@ namespace ams::mitm::fs {
}
void Builder::VisitDirectory(BuildDirectoryContext *parent, u32 parent_offset, const void *dir_table, size_t dir_table_size, const void *file_table, size_t file_table_size) {
const DirectoryEntry *parent_entry = GetDirectoryEntry(dir_table, parent_offset);
if (parent_entry->file != EmptyEntry) {
const FileEntry *cur_file = GetFileEntry(file_table, parent_entry->file);
while (true) {
this->AddFile(parent, std::make_unique<BuildFileContext>(cur_file->name, cur_file->name_size, cur_file->size, cur_file->offset, this->cur_source_type));
if (cur_file->sibling == EmptyEntry) {
break;
}
cur_file = GetFileEntry(file_table, cur_file->sibling);
}
class DirectoryTableReader : public TableReader<DirectoryEntry> {
public:
DirectoryTableReader(ams::fs::IStorage *s, size_t ofs, size_t sz) : TableReader(s, ofs, sz) { /* ... */ }
};
class FileTableReader : public TableReader<FileEntry> {
public:
FileTableReader(ams::fs::IStorage *s, size_t ofs, size_t sz) : TableReader(s, ofs, sz) { /* ... */ }
};
void Builder::VisitDirectory(BuildDirectoryContext *parent, u32 parent_offset, DirectoryTableReader &dir_table, FileTableReader &file_table) {
const DirectoryEntry *parent_entry = dir_table.GetEntry(parent_offset);
u32 cur_file_offset = parent_entry->file;
while (cur_file_offset != EmptyEntry) {
const FileEntry *cur_file = file_table.GetEntry(cur_file_offset);
this->AddFile(parent, std::make_unique<BuildFileContext>(cur_file->name, cur_file->name_size, cur_file->size, cur_file->offset, this->cur_source_type));
cur_file_offset = cur_file->sibling;
}
if (parent_entry->child != EmptyEntry) {
const DirectoryEntry *cur_child = GetDirectoryEntry(dir_table, parent_entry->child);
u32 cur_child_offset = parent_entry->child;
while (true) {
BuildDirectoryContext *real_child = nullptr;
u32 cur_child_offset = parent_entry->child;
while (cur_child_offset != EmptyEntry) {
BuildDirectoryContext *real_child = nullptr;
u32 next_child_offset = 0;
{
const DirectoryEntry *cur_child = dir_table.GetEntry(cur_child_offset);
this->AddDirectory(&real_child, parent, std::make_unique<BuildDirectoryContext>(cur_child->name, cur_child->name_size));
AMS_ABORT_UNLESS(real_child != nullptr);
this->VisitDirectory(real_child, cur_child_offset, dir_table, dir_table_size, file_table, file_table_size);
if (cur_child->sibling == EmptyEntry) {
break;
}
cur_child_offset = cur_child->sibling;
cur_child = GetDirectoryEntry(dir_table, cur_child_offset);
next_child_offset = cur_child->sibling;
__asm__ __volatile__("" ::: "memory");
}
this->VisitDirectory(real_child, cur_child_offset, dir_table, file_table);
cur_child_offset = next_child_offset;
}
}
@@ -271,15 +428,11 @@ namespace ams::mitm::fs {
AMS_ABORT_UNLESS(header.header_size == sizeof(Header));
/* Read tables. */
void *tables = std::malloc(header.dir_table_size + header.file_table_size);
ON_SCOPE_EXIT { std::free(tables); };
void *dir_table = tables;
void *file_table = reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(tables) + header.dir_table_size);
R_ABORT_UNLESS(storage->Read(header.dir_table_ofs, dir_table, size_t(header.dir_table_size)));
R_ABORT_UNLESS(storage->Read(header.file_table_ofs, file_table, size_t(header.file_table_size)));
DirectoryTableReader dir_table(storage, header.dir_table_ofs, header.dir_table_size);
FileTableReader file_table(storage, header.file_table_ofs, header.file_table_size);
this->cur_source_type = source_type;
this->VisitDirectory(this->root, 0x0, dir_table, size_t(header.dir_table_size), file_table, size_t(header.file_table_size));
this->VisitDirectory(this->root, 0x0, dir_table, file_table);
}
void Builder::Build(std::vector<SourceInfo> *out_infos) {
@@ -372,108 +525,114 @@ namespace ams::mitm::fs {
/* Populate file tables. */
{
void *ft_buf = std::malloc(this->file_table_size);
/* Allocate the hash table. */
void *fht_buf = std::malloc(this->file_hash_table_size);
ON_SCOPE_EXIT { std::free(fht_buf); std::free(ft_buf); };
AMS_ABORT_UNLESS(fht_buf != nullptr);
u32 *file_hash_table = reinterpret_cast<u32 *>(fht_buf);
FileEntry *file_table = reinterpret_cast<FileEntry *>(ft_buf);
std::memset(file_hash_table, 0xFF, this->file_hash_table_size);
ON_SCOPE_EXIT {
R_ABORT_UNLESS(fsFileWrite(&metadata_file, this->dir_hash_table_size + this->dir_table_size, file_hash_table, this->file_hash_table_size, FsWriteOption_None));
std::free(fht_buf);
};
for (const auto &it : this->files) {
BuildFileContext *cur_file = it.get();
FileEntry *cur_entry = GetFileEntry(file_table, cur_file->entry_offset);
/* Write the file table. */
{
FileTableWriter file_table(&metadata_file, this->dir_hash_table_size + this->dir_table_size + this->file_hash_table_size, this->file_table_size);
/* Set entry fields. */
cur_entry->parent = cur_file->parent->entry_offset;
cur_entry->sibling = (cur_file->sibling == nullptr) ? EmptyEntry : cur_file->sibling->entry_offset;
cur_entry->offset = cur_file->offset;
cur_entry->size = cur_file->size;
for (const auto &it : this->files) {
BuildFileContext *cur_file = it.get();
FileEntry *cur_entry = file_table.GetEntry(cur_file->entry_offset, cur_file->path_len);
/* Insert into hash table. */
const u32 name_size = cur_file->path_len;
const size_t hash_ind = CalculatePathHash(cur_entry->parent, cur_file->path.get(), 0, name_size) % num_file_hash_table_entries;
cur_entry->hash = file_hash_table[hash_ind];
file_hash_table[hash_ind] = cur_file->entry_offset;
/* Set entry fields. */
cur_entry->parent = cur_file->parent->entry_offset;
cur_entry->sibling = (cur_file->sibling == nullptr) ? EmptyEntry : cur_file->sibling->entry_offset;
cur_entry->offset = cur_file->offset;
cur_entry->size = cur_file->size;
/* Set name. */
cur_entry->name_size = name_size;
if (name_size) {
std::memcpy(cur_entry->name, cur_file->path.get(), name_size);
for (size_t i = name_size; i < util::AlignUp(name_size, 4); i++) {
cur_entry->name[i] = 0;
/* Insert into hash table. */
const u32 name_size = cur_file->path_len;
const size_t hash_ind = CalculatePathHash(cur_entry->parent, cur_file->path.get(), 0, name_size) % num_file_hash_table_entries;
cur_entry->hash = file_hash_table[hash_ind];
file_hash_table[hash_ind] = cur_file->entry_offset;
/* Set name. */
cur_entry->name_size = name_size;
if (name_size) {
std::memcpy(cur_entry->name, cur_file->path.get(), name_size);
for (size_t i = name_size; i < util::AlignUp(name_size, 4); i++) {
cur_entry->name[i] = 0;
}
}
/* Emplace a source. */
switch (cur_file->source_type) {
case DataSourceType::Storage:
case DataSourceType::File:
{
/* Try to compact if possible. */
auto &back = out_infos->back();
if (back.source_type == cur_file->source_type) {
back.size = cur_file->offset + FilePartitionOffset + cur_file->size - back.virtual_offset;
} else {
out_infos->emplace_back(cur_file->offset + FilePartitionOffset, cur_file->size, cur_file->source_type, cur_file->orig_offset + FilePartitionOffset);
}
}
break;
case DataSourceType::LooseSdFile:
{
char *new_path = new char[cur_file->GetPathLength() + 1];
cur_file->GetPath(new_path);
out_infos->emplace_back(cur_file->offset + FilePartitionOffset, cur_file->size, cur_file->source_type, new_path);
}
break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
/* Emplace a source. */
switch (cur_file->source_type) {
case DataSourceType::Storage:
case DataSourceType::File:
{
/* Try to compact if possible. */
auto &back = out_infos->back();
if (back.source_type == cur_file->source_type) {
back.size = cur_file->offset + FilePartitionOffset + cur_file->size - back.virtual_offset;
} else {
out_infos->emplace_back(cur_file->offset + FilePartitionOffset, cur_file->size, cur_file->source_type, cur_file->orig_offset + FilePartitionOffset);
}
}
break;
case DataSourceType::LooseSdFile:
{
char *new_path = new char[cur_file->GetPathLength() + 1];
cur_file->GetPath(new_path);
out_infos->emplace_back(cur_file->offset + FilePartitionOffset, cur_file->size, cur_file->source_type, new_path);
}
break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
/* Write to file. */
R_ABORT_UNLESS(fsFileWrite(&metadata_file, this->dir_hash_table_size + this->dir_table_size, file_hash_table, this->file_hash_table_size, FsWriteOption_None));
R_ABORT_UNLESS(fsFileWrite(&metadata_file, this->dir_hash_table_size + this->dir_table_size + this->file_hash_table_size, file_table, this->file_table_size, FsWriteOption_None));
}
/* Populate directory tables. */
{
void *dt_buf = std::malloc(this->dir_table_size);
/* Allocate the hash table. */
void *dht_buf = std::malloc(this->dir_hash_table_size);
ON_SCOPE_EXIT { std::free(dht_buf); std::free(dt_buf); };
AMS_ABORT_UNLESS(dht_buf != nullptr);
u32 *dir_hash_table = reinterpret_cast<u32 *>(dht_buf);
DirectoryEntry *dir_table = reinterpret_cast<DirectoryEntry *>(dt_buf);
std::memset(dir_hash_table, 0xFF, this->dir_hash_table_size);
ON_SCOPE_EXIT {
R_ABORT_UNLESS(fsFileWrite(&metadata_file, 0, dir_hash_table, this->dir_hash_table_size, FsWriteOption_None));
std::free(dht_buf);
};
for (const auto &it : this->directories) {
BuildDirectoryContext *cur_dir = it.get();
DirectoryEntry *cur_entry = GetDirectoryEntry(dir_table, cur_dir->entry_offset);
/* Write the file table. */
{
DirectoryTableWriter dir_table(&metadata_file, this->dir_hash_table_size, this->dir_table_size);
/* Set entry fields. */
cur_entry->parent = cur_dir == this->root ? 0 : cur_dir->parent->entry_offset;
cur_entry->sibling = (cur_dir->sibling == nullptr) ? EmptyEntry : cur_dir->sibling->entry_offset;
cur_entry->child = (cur_dir->child == nullptr) ? EmptyEntry : cur_dir->child->entry_offset;
cur_entry->file = (cur_dir->file == nullptr) ? EmptyEntry : cur_dir->file->entry_offset;
for (const auto &it : this->directories) {
BuildDirectoryContext *cur_dir = it.get();
DirectoryEntry *cur_entry = dir_table.GetEntry(cur_dir->entry_offset, cur_dir->path_len);
/* Insert into hash table. */
const u32 name_size = cur_dir->path_len;
const size_t hash_ind = CalculatePathHash(cur_entry->parent, cur_dir->path.get(), 0, name_size) % num_dir_hash_table_entries;
cur_entry->hash = dir_hash_table[hash_ind];
dir_hash_table[hash_ind] = cur_dir->entry_offset;
/* Set entry fields. */
cur_entry->parent = cur_dir == this->root ? 0 : cur_dir->parent->entry_offset;
cur_entry->sibling = (cur_dir->sibling == nullptr) ? EmptyEntry : cur_dir->sibling->entry_offset;
cur_entry->child = (cur_dir->child == nullptr) ? EmptyEntry : cur_dir->child->entry_offset;
cur_entry->file = (cur_dir->file == nullptr) ? EmptyEntry : cur_dir->file->entry_offset;
/* Set name. */
cur_entry->name_size = name_size;
if (name_size) {
std::memcpy(cur_entry->name, cur_dir->path.get(), name_size);
for (size_t i = name_size; i < util::AlignUp(name_size, 4); i++) {
cur_entry->name[i] = 0;
/* Insert into hash table. */
const u32 name_size = cur_dir->path_len;
const size_t hash_ind = CalculatePathHash(cur_entry->parent, cur_dir->path.get(), 0, name_size) % num_dir_hash_table_entries;
cur_entry->hash = dir_hash_table[hash_ind];
dir_hash_table[hash_ind] = cur_dir->entry_offset;
/* Set name. */
cur_entry->name_size = name_size;
if (name_size) {
std::memcpy(cur_entry->name, cur_dir->path.get(), name_size);
for (size_t i = name_size; i < util::AlignUp(name_size, 4); i++) {
cur_entry->name[i] = 0;
}
}
}
}
/* Write to file. */
R_ABORT_UNLESS(fsFileWrite(&metadata_file, 0, dir_hash_table, this->dir_hash_table_size, FsWriteOption_None));
R_ABORT_UNLESS(fsFileWrite(&metadata_file, this->dir_hash_table_size, dir_table, this->dir_table_size, FsWriteOption_None));
}
/* Delete maps. */

View File

@@ -199,6 +199,9 @@ namespace ams::mitm::fs::romfs {
}
};
class DirectoryTableReader;
class FileTableReader;
struct Builder {
NON_COPYABLE(Builder);
NON_MOVEABLE(Builder);
@@ -243,7 +246,7 @@ namespace ams::mitm::fs::romfs {
DataSourceType cur_source_type;
private:
void VisitDirectory(FsFileSystem *fs, BuildDirectoryContext *parent);
void VisitDirectory(BuildDirectoryContext *parent, u32 parent_offset, const void *dir_table, size_t dir_table_size, const void *file_table, size_t file_table_size);
void VisitDirectory(BuildDirectoryContext *parent, u32 parent_offset, DirectoryTableReader &dir_table, FileTableReader &file_table);
void AddDirectory(BuildDirectoryContext **out, BuildDirectoryContext *parent_ctx, std::unique_ptr<BuildDirectoryContext> file_ctx);
void AddFile(BuildDirectoryContext *parent_ctx, std::unique_ptr<BuildFileContext> file_ctx);

View File

@@ -522,7 +522,7 @@ namespace ams::dmnt::cheat::impl {
Event hook;
while (true) {
eventLoadRemote(&hook, this_ptr->HookToCreateApplicationProcess(), true);
if (R_SUCCEEDED(eventWait(&hook, U64_MAX))) {
if (R_SUCCEEDED(eventWait(&hook, std::numeric_limits<u64>::max()))) {
this_ptr->AttachToApplicationProcess(true);
}
eventClose(&hook);
@@ -535,7 +535,7 @@ namespace ams::dmnt::cheat::impl {
/* Atomically wait (and clear) signal for new process. */
this_ptr->debug_events_event.Wait();
while (true) {
while (R_SUCCEEDED(svcWaitSynchronizationSingle(this_ptr->GetCheatProcessHandle(), U64_MAX))) {
while (R_SUCCEEDED(svcWaitSynchronizationSingle(this_ptr->GetCheatProcessHandle(), std::numeric_limits<u64>::max()))) {
std::scoped_lock lk(this_ptr->cheat_lock);
/* Handle any pending debug events. */

View File

@@ -25,7 +25,7 @@ namespace ams::fatal::srv {
/* Event creator. */
Handle GetFatalDirtyEventReadableHandle() {
Event evt;
R_ABORT_UNLESS(setsysBindFatalDirtyFlagEvent(&evt));
R_ABORT_UNLESS(setsysAcquireFatalDirtyFlagEventHandle(&evt));
return evt.revent;
}

View File

@@ -418,7 +418,7 @@ namespace ams::fatal::srv {
Result ShowFatalTask::Run() {
/* Don't show the fatal error screen until we've verified the battery is okay. */
eventWait(const_cast<Event *>(&this->context->battery_event), U64_MAX);
eventWait(const_cast<Event *>(&this->context->battery_event), std::numeric_limits<u64>::max());
return ShowFatal();
}