Compare commits

..

39 Commits
0.10.4 ... nim

Author SHA1 Message Date
Michael Scire
b8696bd5d4 fs: add an extension common name generator for sd card 2020-03-31 22:31:41 -07:00
Michael Scire
af7baa6b34 git subrepo push libraries
subrepo:
  subdir:   "libraries"
  merged:   "a40d4593"
upstream:
  origin:   "https://github.com/Atmosphere-NX/Atmosphere-libs"
  branch:   "master"
  commit:   "a40d4593"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2020-03-31 12:54:07 -07:00
Michael Scire
6de1361c8b nim: add DestroySystemUpdateTask/ListSystemUpdateTask 2020-03-31 01:31:23 -07:00
Michael Scire
2b930d21fd git subrepo push libraries
subrepo:
  subdir:   "libraries"
  merged:   "de221b5d"
upstream:
  origin:   "https://github.com/Atmosphere-NX/Atmosphere-libs"
  branch:   "master"
  commit:   "de221b5d"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2020-03-30 21:41:49 -07:00
Michael Scire
0b52596087 fix CONCATENATE 2020-03-30 21:40:48 -07:00
Michael Scire
e9134d8044 git subrepo push libraries
subrepo:
  subdir:   "libraries"
  merged:   "63d5df84"
upstream:
  origin:   "https://github.com/Atmosphere-NX/Atmosphere-libs"
  branch:   "master"
  commit:   "63d5df84"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2020-03-30 20:41:45 -07:00
Michael Scire
33d6dfb6b3 updater: improve api 2020-03-30 20:39:56 -07:00
bunnei
6096fa0e45 KPageHeap: Fix a typo in initialization block alignment. (#862) 2020-03-30 19:27:02 -07:00
Michael Scire
058f265bd6 lmem: fix memory block header placement error 2020-03-30 00:56:57 -07:00
Michael Scire
bd4c608b08 ncm: use static memory pools for different allocations 2020-03-29 17:20:25 -07:00
Michael Scire
7fc1e86bf5 stratosphere: fix building with latest libnx 2020-03-29 15:24:40 -07:00
SciresM
87ec045a98 mem: implement most of StandardAllocator (#860)
This was tested using `https://github.com/node-dot-cpp/alloc-test` plus a few other by-hand tests.

It seems to work for the case we care about (sysmodules without thread cache-ing).

External users are advised to build with assertions on and contact SciresM if you find issues.

This is a lot of code to have gotten right in one go, and it was written mostly after midnight while sick, so there are probably un-noticed issues.
2020-03-29 14:43:16 -07:00
Michael Scire
7502e2174f git subrepo push libraries
subrepo:
  subdir:   "libraries"
  merged:   "6f77a6bf"
upstream:
  origin:   "https://github.com/Atmosphere-NX/Atmosphere-libs"
  branch:   "master"
  commit:   "6f77a6bf"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2020-03-27 17:00:36 -07:00
Michael Scire
0545eb18c0 fs: add MountImageDirectory 2020-03-27 16:59:27 -07:00
Adubbz
0c161a4c1b fs: implement FileHandleStorage (#857)
* fs: implement FileHandleStorage

* fs: merge FileHandleStorage into file_storage TU

Co-authored-by: Michael Scire <SciresM@gmail.com>
2020-03-27 11:45:02 -07:00
Adubbz
3d518759da fssystem: Implement PartitionFileSystemCore (#856)
* fssystem: implement PartitionFileSystemMetaCore

* fssystem: PartitionFileSystemMetaCore cleanup

* fs: add IFile::DryWrite, update results

* fssystem: implement PartitionFileSystemCore

* fssystem: cleanup PartitionFileSystemCore

* fssystem: implement Sha256PartitionFileSystem

Co-authored-by: Michael Scire <SciresM@gmail.com>
2020-03-27 03:40:52 -07:00
Michael Scire
0af2758fde fs.mitm: use new namespace types for saves 2020-03-24 17:50:36 -07:00
Michael Scire
9bb5af9823 git subrepo push libraries
subrepo:
  subdir:   "libraries"
  merged:   "30f3e4c3"
upstream:
  origin:   "https://github.com/Atmosphere-NX/Atmosphere-libs"
  branch:   "master"
  commit:   "30f3e4c3"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2020-03-20 17:06:23 -07:00
Michael Scire
82eab9c8d0 Add Span<T>. 2020-03-20 17:04:01 -07:00
Michael Scire
3cca3801ca fs: fix bugs with external code filesystems 2020-03-20 11:47:19 -07:00
Michael Scire
03408f404a git subrepo push libraries
subrepo:
  subdir:   "libraries"
  merged:   "07630f73"
upstream:
  origin:   "https://github.com/Atmosphere-NX/Atmosphere-libs"
  branch:   "master"
  commit:   "07630f73"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2020-03-18 20:06:10 -07:00
Michael Scire
92e7a3ca08 fs: add MountDeviceSaveData 2020-03-18 20:05:39 -07:00
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
177 changed files with 10931 additions and 478 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 = a40d4593d7e46ea296802e5c50fd0da2d9f92a76
parent = 6de1361c8be886eda16faf87c73b9e4d01187bca
method = merge
cmdver = 0.4.1

View File

@@ -15,7 +15,7 @@ include $(DEVKITPRO)/libnx/switch_rules
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
export DEFINES = $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_STRATOSPHERE
export DEFINES = $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_STRATOSPHERE -D_GNU_SOURCE
export SETTINGS = $(ATMOSPHERE_SETTINGS) -O2
export CFLAGS = $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE)
export CXXFLAGS = $(CFLAGS) $(ATMOSPHERE_CXXFLAGS)

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

@@ -239,7 +239,7 @@ namespace ams::kern {
/* Align up the address. */
KVirtualAddress end = addr + size;
const size_t align = (this->next_block_shift != 0) ? (u64(1) << this->next_block_shift) : (this->block_shift);
const size_t align = (this->next_block_shift != 0) ? (u64(1) << this->next_block_shift) : (u64(1) << this->block_shift);
addr = util::AlignDown(GetInteger(addr), align);
end = util::AlignUp(GetInteger(end), align);

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

@@ -15,7 +15,7 @@ include $(DEVKITPRO)/libnx/switch_rules
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_STRATOSPHERE
DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_STRATOSPHERE -D_GNU_SOURCE
SETTINGS := $(ATMOSPHERE_SETTINGS) -O2
CFLAGS := $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE)
CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS) -flto

View File

@@ -22,11 +22,15 @@
/* Libstratosphere-only utility. */
#include "stratosphere/util.hpp"
/* Sadly required shims. */
#include "stratosphere/svc/svc_stratosphere_shims.hpp"
/* Critical modules with no dependencies. */
#include "stratosphere/ams.hpp"
#include "stratosphere/os.hpp"
#include "stratosphere/dd.hpp"
#include "stratosphere/lmem.hpp"
#include "stratosphere/mem.hpp"
/* Pull in all ID definitions from NCM. */
#include "stratosphere/ncm/ncm_ids.hpp"
@@ -44,6 +48,7 @@
#include "stratosphere/lr.hpp"
#include "stratosphere/map.hpp"
#include "stratosphere/ncm.hpp"
#include "stratosphere/nim.hpp"
#include "stratosphere/patcher.hpp"
#include "stratosphere/pm.hpp"
#include "stratosphere/reg.hpp"
@@ -57,4 +62,4 @@
/* Include FS last. */
#include "stratosphere/fs.hpp"
#include "stratosphere/fssrv.hpp"
#include "stratosphere/fssystem.hpp"
#include "stratosphere/fssystem.hpp"

View File

@@ -41,11 +41,13 @@
#include <stratosphere/fs/fs_code.hpp>
#include <stratosphere/fs/fs_content.hpp>
#include <stratosphere/fs/fs_content_storage.hpp>
#include <stratosphere/fs/fs_image_directory.hpp>
#include <stratosphere/fs/fs_game_card.hpp>
#include <stratosphere/fs/fs_save_data_types.hpp>
#include <stratosphere/fs/fs_save_data_management.hpp>
#include <stratosphere/fs/fs_save_data_transaction.hpp>
#include <stratosphere/fs/fs_device_save_data.hpp>
#include <stratosphere/fs/fs_system_save_data.hpp>
#include <stratosphere/fs/fs_sd_card.hpp>
#include <stratosphere/fs/fs_signed_system_partition.hpp>
#include <stratosphere/fs/fs_system_data.hpp>
#include <stratosphere/fs/fs_system_save_data.hpp>

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 "fs_common.hpp"
#include "fs_save_data_types.hpp"
namespace ams::fs {
Result MountDeviceSaveData(const char *name);
Result MountDeviceSaveData(const char *name, const ncm::ApplicationId application_id);
}

View File

@@ -14,13 +14,14 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "fs_common.hpp"
#include "fs_istorage.hpp"
#include "fsa/fs_ifile.hpp"
#include <stratosphere/fs/fs_common.hpp>
#include <stratosphere/fs/fs_istorage.hpp>
#include <stratosphere/fs/fsa/fs_ifile.hpp>
#include <stratosphere/fs/impl/fs_newable.hpp>
namespace ams::fs {
class FileStorage : public IStorage {
class FileStorage : public IStorage, public impl::Newable {
private:
static constexpr s64 InvalidSize = -1;
private:
@@ -53,4 +54,32 @@ namespace ams::fs {
virtual Result OperateRange(void *dst, size_t dst_size, OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override;
};
class FileHandleStorage : public IStorage, public impl::Newable {
private:
static constexpr s64 InvalidSize = -1;
private:
FileHandle handle;
bool close_file;
s64 size;
os::Mutex mutex;
public:
constexpr explicit FileHandleStorage(FileHandle handle, bool close_file) : handle(handle), close_file(close_file), size(InvalidSize), mutex() { /* ... */ }
constexpr explicit FileHandleStorage(FileHandle handle) : FileHandleStorage(handle, false) { /* ... */ }
virtual ~FileHandleStorage() override {
if (this->close_file) {
CloseFile(this->handle);
}
}
protected:
Result UpdateSize();
public:
virtual Result Read(s64 offset, void *buffer, size_t size) override;
virtual Result Write(s64 offset, const void *buffer, size_t size) override;
virtual Result Flush() override;
virtual Result GetSize(s64 *out_size) override;
virtual Result SetSize(s64 size) override;
virtual Result OperateRange(void *dst, size_t dst_size, OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override;
};
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "fs_common.hpp"
namespace ams::fs {
enum class ImageDirectoryId {
Nand = 0,
SdCard = 1,
};
Result MountImageDirectory(const char *name, ImageDirectoryId id);
}

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

@@ -85,7 +85,7 @@ namespace ams::fs::fsa {
protected:
Result DryRead(size_t *out, s64 offset, size_t size, const fs::ReadOption &option, OpenMode open_mode) {
/* Check that we can read. */
R_UNLESS((open_mode & OpenMode_Read) != 0, fs::ResultInvalidOperationForOpenMode());
R_UNLESS((open_mode & OpenMode_Read) != 0, fs::ResultReadNotPermitted());
/* Get the file size, and validate our offset. */
s64 file_size = 0;
@@ -98,12 +98,31 @@ namespace ams::fs::fsa {
Result DrySetSize(s64 size, fs::OpenMode open_mode) {
/* Check that we can write. */
R_UNLESS((open_mode & OpenMode_Write) != 0, fs::ResultInvalidOperationForOpenMode());
R_UNLESS((open_mode & OpenMode_Write) != 0, fs::ResultWriteNotPermitted());
AMS_ASSERT(size >= 0);
return ResultSuccess();
}
Result DryWrite(bool *out_append, s64 offset, size_t size, const fs::WriteOption &option, fs::OpenMode open_mode) {
/* Check that we can write. */
R_UNLESS((open_mode & OpenMode_Write) != 0, fs::ResultWriteNotPermitted());
/* Get the file size. */
s64 file_size = 0;
R_TRY(this->GetSize(&file_size));
/* Determine if we need to append. */
if (file_size < offset + static_cast<s64>(size)) {
R_UNLESS((open_mode & OpenMode_AllowAppend) != 0, fs::ResultFileExtensionWithoutOpenModeAllowAppend());
*out_append = true;
} else {
*out_append = false;
}
return ResultSuccess();
}
private:
virtual Result ReadImpl(size_t *out, s64 offset, void *buffer, size_t size, const fs::ReadOption &option) = 0;
virtual Result GetSizeImpl(s64 *out) = 0;

View File

@@ -17,6 +17,8 @@
#pragma once
#include "fssystem/fssystem_utility.hpp"
#include "fssystem/fssystem_external_code.hpp"
#include "fssystem/fssystem_partition_file_system.hpp"
#include "fssystem/fssystem_partition_file_system_meta.hpp"
#include "fssystem/fssystem_path_tool.hpp"
#include "fssystem/fssystem_subdirectory_filesystem.hpp"
#include "fssystem/fssystem_directory_redirection_filesystem.hpp"

View File

@@ -0,0 +1,72 @@
/*
* Copyright (c) 2018-2020 Adubbz, 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 "fssystem_partition_file_system_meta.hpp"
#include "../fs/fsa/fs_ifile.hpp"
#include "../fs/fsa/fs_idirectory.hpp"
#include "../fs/fsa/fs_ifilesystem.hpp"
namespace ams::fssystem {
template<typename MetaType>
class PartitionFileSystemCore : public fs::impl::Newable, public fs::fsa::IFileSystem {
NON_COPYABLE(PartitionFileSystemCore);
NON_MOVEABLE(PartitionFileSystemCore);
private:
class PartitionFile;
class PartitionDirectory;
private:
fs::IStorage *base_storage;
MetaType *meta_data;
bool initialized;
size_t meta_data_size;
std::unique_ptr<MetaType> unique_meta_data;
std::shared_ptr<fs::IStorage> shared_storage;
private:
Result Initialize(fs::IStorage *base_storage, MemoryResource *allocator);
public:
PartitionFileSystemCore();
virtual ~PartitionFileSystemCore() override;
Result Initialize(std::unique_ptr<MetaType> &&meta_data, std::shared_ptr<fs::IStorage> base_storage);
Result Initialize(MetaType *meta_data, std::shared_ptr<fs::IStorage> base_storage);
Result Initialize(fs::IStorage *base_storage);
Result Initialize(std::shared_ptr<fs::IStorage> base_storage);
Result Initialize(std::shared_ptr<fs::IStorage> base_storage, MemoryResource *allocator);
Result GetFileBaseOffset(s64 *out_offset, const char *path);
virtual Result CreateFileImpl(const char *path, s64 size, int option) override;
virtual Result DeleteFileImpl(const char *path) override;
virtual Result CreateDirectoryImpl(const char *path) override;
virtual Result DeleteDirectoryImpl(const char *path) override;
virtual Result DeleteDirectoryRecursivelyImpl(const char *path) override;
virtual Result RenameFileImpl(const char *old_path, const char *new_path) override;
virtual Result RenameDirectoryImpl(const char *old_path, const char *new_path) override;
virtual Result GetEntryTypeImpl(fs::DirectoryEntryType *out, const char *path) override;
virtual Result OpenFileImpl(std::unique_ptr<fs::fsa::IFile> *out_file, const char *path, fs::OpenMode mode) override;
virtual Result OpenDirectoryImpl(std::unique_ptr<fs::fsa::IDirectory> *out_dir, const char *path, fs::OpenDirectoryMode mode) override;
virtual Result CommitImpl() override;
virtual Result CleanDirectoryRecursivelyImpl(const char *path) override;
/* These aren't accessible as commands. */
virtual Result CommitProvisionallyImpl(s64 counter) override;
};
using PartitionFileSystem = PartitionFileSystemCore<PartitionFileSystemMeta>;
using Sha256PartitionFileSystem = PartitionFileSystemCore<Sha256PartitionFileSystemMeta>;
}

View File

@@ -0,0 +1,113 @@
/*
* Copyright (c) 2018-2020 Adubbz, 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/fs/fs_common.hpp>
#include <stratosphere/fs/impl/fs_newable.hpp>
namespace ams::fssystem {
namespace impl {
struct PartitionFileSystemFormat {
#pragma pack(push, 1)
struct PartitionEntry {
u64 offset;
u64 size;
u32 name_offset;
u32 reserved;
};
static_assert(std::is_pod<PartitionEntry>::value);
#pragma pack(pop)
static constexpr const char VersionSignature[] = { 'P', 'F', 'S', '0' };
static constexpr size_t EntryNameLengthMax = ::ams::fs::EntryNameLengthMax;
static constexpr size_t FileDataAlignmentSize = 0x20;
using ResultSignatureVerificationFailed = fs::ResultPartitionSignatureVerificationFailed;
};
struct Sha256PartitionFileSystemFormat {
static constexpr size_t HashSize = ::ams::crypto::Sha256Generator::HashSize;
#pragma pack(push, 1)
struct PartitionEntry {
u64 offset;
u64 size;
u32 name_offset;
u32 hash_target_size;
u64 hash_target_offset;
char hash[HashSize];
};
static_assert(std::is_pod<PartitionEntry>::value);
#pragma pack(pop)
static constexpr const char VersionSignature[] = { 'H', 'F', 'S', '0' };
static constexpr size_t EntryNameLengthMax = ::ams::fs::EntryNameLengthMax;
static constexpr size_t FileDataAlignmentSize = 0x200;
using ResultSignatureVerificationFailed = fs::ResultSha256PartitionSignatureVerificationFailed;
};
}
template<typename Format>
class PartitionFileSystemMetaCore : public fs::impl::Newable {
public:
static constexpr size_t EntryNameLengthMax = Format::EntryNameLengthMax;
static constexpr size_t FileDataAlignmentSize = Format::FileDataAlignmentSize;
/* Forward declare header. */
struct PartitionFileSystemHeader;
using PartitionEntry = typename Format::PartitionEntry;
protected:
bool initialized;
PartitionFileSystemHeader *header;
PartitionEntry *entries;
char *name_table;
size_t meta_data_size;
MemoryResource *allocator;
char *buffer;
public:
PartitionFileSystemMetaCore() : initialized(false), allocator(nullptr), buffer(nullptr) { /* ... */ }
~PartitionFileSystemMetaCore();
Result Initialize(fs::IStorage *storage, MemoryResource *allocator);
Result Initialize(fs::IStorage *storage, void *header, size_t header_size);
const PartitionEntry *GetEntry(s32 index) const;
s32 GetEntryCount() const;
s32 GetEntryIndex(const char *name) const;
const char *GetEntryName(s32 index) const;
size_t GetHeaderSize() const;
size_t GetMetaDataSize() const;
public:
static Result QueryMetaDataSize(size_t *out_size, fs::IStorage *storage);
protected:
void DeallocateBuffer();
};
using PartitionFileSystemMeta = PartitionFileSystemMetaCore<impl::PartitionFileSystemFormat>;
class Sha256PartitionFileSystemMeta : public PartitionFileSystemMetaCore<impl::Sha256PartitionFileSystemFormat> {
public:
using PartitionFileSystemMetaCore<impl::Sha256PartitionFileSystemFormat>::Initialize;
Result Initialize(fs::IStorage *base_storage, MemoryResource *allocator, const void *hash, size_t hash_size, std::optional<u8> suffix = std::nullopt);
};
}

View File

@@ -49,7 +49,7 @@ namespace ams::kvdb {
void Reset() {
if (this->buffer != nullptr) {
std::free(this->buffer);
delete[] this->buffer;
this->buffer = nullptr;
this->size = 0;
}
@@ -68,7 +68,7 @@ namespace ams::kvdb {
AMS_ABORT_UNLESS(this->buffer == nullptr);
/* Allocate a buffer. */
this->buffer = static_cast<u8 *>(std::malloc(size));
this->buffer = new (std::nothrow) u8[size];
R_UNLESS(this->buffer != nullptr, ResultAllocationFailed());
this->size = size;

View File

@@ -93,13 +93,14 @@ namespace ams::kvdb {
size_t count;
size_t capacity;
Entry *entries;
MemoryResource *memory_resource;
public:
Index() : count(0), capacity(0), entries(nullptr) { /* ... */ }
Index() : count(0), capacity(0), entries(nullptr), memory_resource(nullptr) { /* ... */ }
~Index() {
if (this->entries != nullptr) {
this->ResetEntries();
std::free(this->entries);
this->memory_resource->Deallocate(this->entries, sizeof(Entry) * this->capacity);
this->entries = nullptr;
}
}
@@ -114,30 +115,25 @@ namespace ams::kvdb {
void ResetEntries() {
for (size_t i = 0; i < this->count; i++) {
std::free(this->entries[i].GetValuePointer());
this->memory_resource->Deallocate(this->entries[i].GetValuePointer(), this->entries[i].GetValueSize());
}
this->count = 0;
}
Result Initialize(size_t capacity) {
this->entries = reinterpret_cast<Entry *>(std::malloc(sizeof(Entry) * capacity));
Result Initialize(size_t capacity, MemoryResource *mr) {
this->entries = reinterpret_cast<Entry *>(mr->Allocate(sizeof(Entry) * capacity));
R_UNLESS(this->entries != nullptr, ResultAllocationFailed());
this->capacity = capacity;
this->memory_resource = mr;
return ResultSuccess();
}
Result Set(const Key &key, const void *value, size_t value_size) {
/* Allocate new value. */
void *new_value = std::malloc(value_size);
R_UNLESS(new_value != nullptr, ResultAllocationFailed());
auto value_guard = SCOPE_GUARD { std::free(new_value); };
std::memcpy(new_value, value, value_size);
/* Find entry for key. */
Entry *it = this->lower_bound(key);
if (it != this->end() && it->GetKey() == key) {
/* Entry already exists. Free old value. */
std::free(it->GetValuePointer());
this->memory_resource->Deallocate(it->GetValuePointer(), it->GetValueSize());
} else {
/* We need to add a new entry. Check we have room, move future keys forward. */
R_UNLESS(this->count < this->capacity, ResultOutOfKeyResource());
@@ -145,8 +141,12 @@ namespace ams::kvdb {
this->count++;
}
/* Allocate new value. */
void *new_value = this->memory_resource->Allocate(value_size);
R_UNLESS(new_value != nullptr, ResultAllocationFailed());
std::memcpy(new_value, value, value_size);
/* Save the new Entry in the map. */
value_guard.Cancel();
*it = Entry(key, new_value, value_size);
return ResultSuccess();
}
@@ -164,7 +164,7 @@ namespace ams::kvdb {
R_UNLESS(it != this->end(), ResultKeyNotFound());
/* Free the value, move entries back. */
std::free(it->GetValuePointer());
this->memory_resource->Deallocate(it->GetValuePointer(), it->GetValueSize());
std::memmove(it, it + 1, sizeof(*it) * (this->end() - (it + 1)));
this->count--;
return ResultSuccess();
@@ -258,10 +258,11 @@ namespace ams::kvdb {
Index index;
Path path;
Path temp_path;
MemoryResource *memory_resource;
public:
MemoryKeyValueStore() { /* ... */ }
Result Initialize(const char *dir, size_t capacity) {
Result Initialize(const char *dir, size_t capacity, MemoryResource *mr) {
/* Ensure that the passed path is a directory. */
fs::DirectoryEntryType entry_type;
R_TRY(fs::GetEntryType(std::addressof(entry_type), dir));
@@ -272,18 +273,21 @@ namespace ams::kvdb {
this->temp_path.SetFormat("%s%s", dir, "/imkvdb.tmp");
/* Initialize our index. */
R_TRY(this->index.Initialize(capacity));
R_TRY(this->index.Initialize(capacity, mr));
this->memory_resource = mr;
return ResultSuccess();
}
Result Initialize(size_t capacity) {
Result Initialize(size_t capacity, MemoryResource *mr) {
/* This initializes without an archive file. */
/* A store initialized this way cannot have its contents loaded from or flushed to disk. */
this->path.Set("");
this->temp_path.Set("");
/* Initialize our index. */
R_TRY(this->index.Initialize(capacity));
R_TRY(this->index.Initialize(capacity, mr));
this->memory_resource = mr;
return ResultSuccess();
}
@@ -319,9 +323,9 @@ namespace ams::kvdb {
R_TRY(reader.GetEntrySize(&key_size, &value_size));
/* Allocate memory for value. */
void *new_value = std::malloc(value_size);
void *new_value = this->memory_resource->Allocate(value_size);
R_UNLESS(new_value != nullptr, ResultAllocationFailed());
auto value_guard = SCOPE_GUARD { std::free(new_value); };
auto value_guard = SCOPE_GUARD { this->memory_resource->Deallocate(new_value, value_size); };
/* Read key and value. */
Key key;

View File

@@ -0,0 +1,20 @@
/*
* 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/mem/mem_standard_allocator.hpp>
#include <stratosphere/mem/impl/mem_impl_heap.hpp>

View File

@@ -0,0 +1,63 @@
/*
* 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.hpp>
#include <stratosphere/mem/impl/mem_impl_common.hpp>
namespace ams::mem::impl::heap {
class TlsHeapCache;
class CachedHeap final {
NON_COPYABLE(CachedHeap);
private:
TlsHeapCache *tls_heap_cache;
public:
constexpr CachedHeap() : tls_heap_cache() { /* ... */ }
~CachedHeap() { this->Finalize(); }
ALWAYS_INLINE CachedHeap(CachedHeap &&rhs) : tls_heap_cache(rhs.tls_heap_cache) {
rhs.tls_heap_cache = nullptr;
}
ALWAYS_INLINE CachedHeap &operator=(CachedHeap &&rhs) {
this->Reset();
this->tls_heap_cache = rhs.tls_heap_cache;
rhs.tls_heap_cache = nullptr;
return *this;
}
void *Allocate(size_t n);
void *Allocate(size_t n, size_t align);
size_t GetAllocationSize(const void *ptr);
errno_t Free(void *p);
errno_t FreeWithSize(void *p, size_t size);
errno_t Reallocate(void *ptr, size_t size, void **p);
errno_t Shrink(void *ptr, size_t size);
void ReleaseAllCache();
void Finalize();
bool CheckCache();
errno_t QueryV(int query, std::va_list vl);
errno_t Query(int query, ...);
void Reset() { this->Finalize(); }
void Reset(TlsHeapCache *thc);
TlsHeapCache *Release();
constexpr explicit ALWAYS_INLINE operator bool() const { return this->tls_heap_cache != nullptr; }
};
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/mem/impl/mem_impl_common.hpp>
#include <stratosphere/mem/impl/mem_impl_declarations.hpp>
namespace ams::mem::impl::heap {
class CachedHeap;
class TlsHeapCentral;
using HeapWalkCallback = int (*)(void *ptr, size_t size, void *user_data);
class CentralHeap final {
NON_COPYABLE(CentralHeap);
NON_MOVEABLE(CentralHeap);
public:
static constexpr size_t PageSize = 4_KB;
static constexpr size_t MinimumAlignment = alignof(u64);
using DestructorHandler = void (*)(void *start, void *end);
private:
TlsHeapCentral *tls_heap_central;
bool use_virtual_memory;
u32 option;
u8 *start;
u8 *end;
public:
constexpr CentralHeap() : tls_heap_central(), use_virtual_memory(), option(), start(), end() { /* ... */ }
~CentralHeap() { this->Finalize(); }
errno_t Initialize(void *start, size_t size, u32 option);
void Finalize();
ALWAYS_INLINE void *Allocate(size_t n) { return this->Allocate(n, MinimumAlignment); }
void *Allocate(size_t n, size_t align);
size_t GetAllocationSize(const void *ptr);
errno_t Free(void *p);
errno_t FreeWithSize(void *p, size_t size);
errno_t Reallocate(void *ptr, size_t size, void **p);
errno_t Shrink(void *ptr, size_t size);
bool MakeCache(CachedHeap *cached_heap);
errno_t WalkAllocatedPointers(HeapWalkCallback callback, void *user_data);
errno_t QueryV(int query, std::va_list vl);
errno_t Query(int query, ...);
private:
errno_t QueryVImpl(int query, std::va_list *vl_ptr);
};
static_assert(sizeof(CentralHeap) <= sizeof(::ams::mem::impl::InternalCentralHeapStorage));
static_assert(alignof(CentralHeap) <= alignof(void *));
}

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 <vapours.hpp>
#include <errno.h>
namespace ams::mem::impl {
constexpr inline size_t MaxSize = static_cast<size_t>(std::numeric_limits<s64>::max());
using errno_t = int;
enum DumpMode {
DumpMode_Basic = (0 << 0),
DumpMode_Spans = (1 << 0),
DumpMode_Pointers = (1 << 1),
DumpMode_Pages = (1 << 2),
DumpMode_All = (DumpMode_Pages | DumpMode_Pointers | DumpMode_Spans | DumpMode_Basic),
};
enum AllocQuery {
AllocQuery_Dump = 0,
AllocQuery_PageSize = 1,
AllocQuery_AllocatedSize = 2,
AllocQuery_FreeSize = 3,
AllocQuery_SystemSize = 4,
AllocQuery_MaxAllocatableSize = 5,
AllocQuery_IsClean = 6,
AllocQuery_HeapHash = 7,
AllocQuery_UnifyFreeList = 8,
AllocQuery_SetColor = 9,
AllocQuery_GetColor = 10,
AllocQuery_SetName = 11,
AllocQuery_GetName = 12,
/* AllocQuery_Thirteen = 13, */
AllocQuery_CheckCache = 14,
AllocQuery_ClearCache = 15,
AllocQuery_FinalizeCache = 16,
AllocQuery_FreeSizeMapped = 17,
AllocQuery_MaxAllocatableSizeMapped = 18,
AllocQuery_DumpJson = 19,
};
enum HeapOption {
HeapOption_UseEnvironment = (1 << 0),
HeapOption_DisableCache = (1 << 2),
};
struct HeapHash {
size_t alloc_count;
size_t alloc_size;
size_t hash;
};
static_assert(std::is_pod<HeapHash>::value);
}

View File

@@ -0,0 +1,29 @@
/*
* 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
namespace ams::mem::impl {
namespace heap {
class CentralHeap;
}
using InternalCentralHeapStorage = ::ams::util::TypedStorage<::ams::mem::impl::heap::CentralHeap, sizeof(void *) * 6, alignof(void *)>;
}

View File

@@ -0,0 +1,21 @@
/*
* 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/mem/impl/mem_impl_common.hpp>
#include <stratosphere/mem/impl/heap/mem_impl_heap_cached_heap.hpp>
#include <stratosphere/mem/impl/heap/mem_impl_heap_central_heap.hpp>

View File

@@ -0,0 +1,73 @@
/*
* Copyright (c) 2019-2020 Adubbz, 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/os.hpp>
#include <stratosphere/mem/impl/mem_impl_declarations.hpp>
namespace ams::mem {
class StandardAllocator {
NON_COPYABLE(StandardAllocator);
NON_MOVEABLE(StandardAllocator);
public:
using WalkCallback = int (*)(void *ptr, size_t size, void *user_data);
struct AllocatorHash {
size_t allocated_count;
size_t allocated_size;
size_t hash;
};
private:
bool initialized;
bool enable_thread_cache;
uintptr_t unused;
os::TlsSlot tls_slot;
impl::InternalCentralHeapStorage central_heap_storage;
public:
StandardAllocator();
StandardAllocator(void *mem, size_t size);
StandardAllocator(void *mem, size_t size, bool enable_cache);
~StandardAllocator() {
if (this->initialized) {
this->Finalize();
}
}
void Initialize(void *mem, size_t size);
void Initialize(void *mem, size_t size, bool enable_cache);
void Finalize();
void *Allocate(size_t size);
void *Allocate(size_t size, size_t alignment);
void Free(void *ptr);
void *Reallocate(void *ptr, size_t new_size);
size_t Shrink(void *ptr, size_t new_size);
void ClearThreadCache() const;
void CleanUpManagementArea() const;
size_t GetSizeOf(const void *ptr) const;
size_t GetTotalFreeSize() const;
size_t GetAllocatableSize() const;
void WalkAllocatedBlocks(WalkCallback callback, void *user_data) const;
void Dump() const;
AllocatorHash Hash() const;
};
}

View File

@@ -49,7 +49,7 @@ namespace ams::ncm {
void Reset() {
if (this->buffer != nullptr) {
std::free(this->buffer);
delete[] this->buffer;
this->buffer = nullptr;
this->size = 0;
}
@@ -68,7 +68,7 @@ namespace ams::ncm {
AMS_ABORT_UNLESS(this->buffer == nullptr);
/* Allocate a buffer. */
this->buffer = static_cast<u8 *>(std::malloc(size));
this->buffer = new (std::nothrow) u8[size];
R_UNLESS(this->buffer != nullptr, ResultAllocationFailed());
this->size = size;
@@ -85,4 +85,5 @@ namespace ams::ncm {
return ResultSuccess();
}
};
}

View File

@@ -30,6 +30,16 @@
namespace ams::ncm {
class ContentMetaMemoryResource {
private:
mem::StandardAllocator allocator;
sf::StandardAllocatorMemoryResource memory_resource;
public:
ContentMetaMemoryResource(void *heap, size_t heap_size) : allocator(heap, heap_size), memory_resource(std::addressof(allocator)) { /* ... */ }
MemoryResource *Get() { return std::addressof(this->memory_resource); }
};
struct SystemSaveDataInfo {
u64 id;
u64 size;
@@ -67,6 +77,7 @@ namespace ams::ncm {
SystemSaveDataInfo info;
std::shared_ptr<IContentMetaDatabase> content_meta_database;
std::optional<kvdb::MemoryKeyValueStore<ContentMetaKey>> kvs;
ContentMetaMemoryResource *memory_resource;
u32 max_content_metas;
ContentMetaDatabaseRoot() { /* ... */ }
@@ -92,8 +103,8 @@ namespace ams::ncm {
Result InitializeContentStorageRoot(ContentStorageRoot *out, StorageId storage_id, fs::ContentStorageId content_storage_id);
Result InitializeGameCardContentStorageRoot(ContentStorageRoot *out);
Result InitializeContentMetaDatabaseRoot(ContentMetaDatabaseRoot *out, StorageId storage_id, const SystemSaveDataInfo &info, size_t max_content_metas);
Result InitializeGameCardContentMetaDatabaseRoot(ContentMetaDatabaseRoot *out, size_t max_content_metas);
Result InitializeContentMetaDatabaseRoot(ContentMetaDatabaseRoot *out, StorageId storage_id, const SystemSaveDataInfo &info, size_t max_content_metas, ContentMetaMemoryResource *mr);
Result InitializeGameCardContentMetaDatabaseRoot(ContentMetaDatabaseRoot *out, size_t max_content_metas, ContentMetaMemoryResource *mr);
Result BuildContentMetaDatabase(StorageId storage_id);
Result ImportContentMetaDatabase(StorageId storage_id, bool from_signed_partition);

View File

@@ -0,0 +1,19 @@
/*
* 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/nim/nim_network_install_manager_api.hpp>

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/>.
*/
#pragma once
#include <stratosphere/nim/nim_system_update_task_id.hpp>
namespace ams::nim {
/* Management. */
void InitializeForNetworkInstallManager();
void FinalizeForNetworkInstallManager();
/* Service API. */
Result DestroySystemUpdateTask(const SystemUpdateTaskId& id);
s32 ListSystemUpdateTask(SystemUpdateTaskId *out_list, size_t out_list_size);
}

View File

@@ -0,0 +1,24 @@
/*
* Copyright (c) 2019-2020 Adubbz, 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.hpp>
#include <stratosphere/nim/nim_task_id_common.hpp>
namespace ams::nim {
AMS_NIM_DEFINE_TASK_ID(SystemUpdateTaskId, 8);
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright (c) 2019-2020 Adubbz, 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.hpp>
#include <stratosphere/nim/nim_task_id_common.hpp>
namespace ams::nim {
#define AMS_NIM_DEFINE_TASK_ID(ID, ALIGN) \
struct alignas(ALIGN) ID { \
util::Uuid uuid; \
\
ALWAYS_INLINE bool IsValid() const { \
return this->uuid != util::InvalidUuid; \
} \
}; \
\
static_assert(alignof(ID) == ALIGN); \
\
ALWAYS_INLINE bool operator==(const ID &lhs, const ID &rhs) { \
return lhs.uuid == rhs.uuid; \
} \
\
ALWAYS_INLINE bool operator!=(const ID &lhs, const ID &rhs) { \
return lhs.uuid != rhs.uuid; \
} \
\
constexpr inline ID Invalid##ID = { util::InvalidUuid }
}

View File

@@ -16,20 +16,25 @@
#pragma once
#include "os/os_common_types.hpp"
#include "os/os_memory_common.hpp"
#include "os/os_managed_handle.hpp"
#include "os/os_process_handle.hpp"
#include "os/os_random.hpp"
#include "os/os_mutex.hpp"
#include "os/os_condvar.hpp"
#include "os/os_rw_lock.hpp"
#include "os/os_semaphore.hpp"
#include "os/os_timeout_helper.hpp"
#include "os/os_event.hpp"
#include "os/os_system_event.hpp"
#include "os/os_interrupt_event.hpp"
#include "os/os_thread.hpp"
#include "os/os_message_queue.hpp"
#include "os/os_waitable_holder.hpp"
#include "os/os_waitable_manager.hpp"
#include <stratosphere/os/os_common_types.hpp>
#include <stratosphere/os/os_tick.hpp>
#include <stratosphere/os/os_memory_common.hpp>
#include <stratosphere/os/os_memory_permission.hpp>
#include <stratosphere/os/os_memory_heap_api.hpp>
#include <stratosphere/os/os_memory_virtual_address_api.hpp>
#include <stratosphere/os/os_managed_handle.hpp>
#include <stratosphere/os/os_process_handle.hpp>
#include <stratosphere/os/os_random.hpp>
#include <stratosphere/os/os_mutex.hpp>
#include <stratosphere/os/os_condvar.hpp>
#include <stratosphere/os/os_rw_lock.hpp>
#include <stratosphere/os/os_semaphore.hpp>
#include <stratosphere/os/os_timeout_helper.hpp>
#include <stratosphere/os/os_event.hpp>
#include <stratosphere/os/os_system_event.hpp>
#include <stratosphere/os/os_interrupt_event.hpp>
#include <stratosphere/os/os_thread_local_storage_api.hpp>
#include <stratosphere/os/os_thread.hpp>
#include <stratosphere/os/os_message_queue.hpp>
#include <stratosphere/os/os_waitable_holder.hpp>
#include <stratosphere/os/os_waitable_manager.hpp>

View File

@@ -22,4 +22,12 @@ namespace ams::os {
constexpr inline size_t MemoryBlockUnitSize = 0x200000;
enum MemoryPermission {
MemoryPermission_None = (0 << 0),
MemoryPermission_ReadOnly = (1 << 0),
MemoryPermission_WriteOnly = (1 << 1),
MemoryPermission_ReadWrite = MemoryPermission_ReadOnly | MemoryPermission_WriteOnly,
};
}

View File

@@ -0,0 +1,26 @@
/*
* 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.hpp>
#include <stratosphere/os/os_common_types.hpp>
#include <stratosphere/os/os_memory_common.hpp>
namespace ams::os {
Result AllocateMemoryBlock(uintptr_t *out_address, size_t size);
void FreeMemoryBlock(uintptr_t address, size_t size);
}

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 <vapours.hpp>
#include <stratosphere/os/os_common_types.hpp>
#include <stratosphere/os/os_memory_common.hpp>
namespace ams::os {
void SetMemoryPermission(uintptr_t address, size_t size, MemoryPermission perm);
}

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 <vapours.hpp>
#include <stratosphere/os/os_common_types.hpp>
#include <stratosphere/os/os_memory_common.hpp>
namespace ams::os {
bool IsVirtualAddressMemoryEnabled();
}

View File

@@ -101,10 +101,19 @@ namespace ams::os {
}
};
NX_INLINE u32 GetCurrentThreadPriority() {
u32 prio;
ALWAYS_INLINE s32 GetCurrentThreadPriority() {
s32 prio;
R_ABORT_UNLESS(svcGetThreadPriority(&prio, CUR_THREAD_HANDLE));
return prio;
}
/* TODO: ThreadManager? */
ALWAYS_INLINE s32 GetCurrentProcessorNumber() {
return svcGetCurrentProcessorNumber();
}
ALWAYS_INLINE s32 GetCurrentCoreNumber() {
return GetCurrentProcessorNumber();
}
}

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/>.
*/
#pragma once
#include <stratosphere/os/os_common_types.hpp>
#include <stratosphere/os/os_memory_common.hpp>
#include <stratosphere/os/os_thread_local_storage_common.hpp>
namespace ams::os {
Result AllocateTlsSlot(TlsSlot *out, TlsDestructor destructor);
void FreeTlsSlot(TlsSlot slot);
uintptr_t GetTlsValue(TlsSlot slot);
void SetTlsValue(TlsSlot slot, uintptr_t value);
}

View File

@@ -0,0 +1,32 @@
/*
* 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/os/os_common_types.hpp>
#include <stratosphere/os/os_memory_common.hpp>
namespace ams::os {
struct TlsSlot {
u32 _value;
};
using TlsDestructor = void (*)(uintptr_t arg);
constexpr inline size_t TlsSlotCountMax = 16;
constexpr inline size_t SdkTlsSlotCountMax = 16;
}

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

@@ -17,6 +17,7 @@
#pragma once
#include "sf/sf_common.hpp"
#include "sf/sf_mem_utility.hpp"
#include "sf/sf_service_object.hpp"
#include "sf/hipc/sf_hipc_server_session_manager.hpp"

View File

@@ -16,6 +16,6 @@
#pragma once
#include <vapours.hpp>
#include "../ams.hpp"
#include "../os.hpp"
#include "../sm/sm_types.hpp"
#include <stratosphere/ams.hpp>
#include <stratosphere/os.hpp>
#include <stratosphere/sm/sm_types.hpp>

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 <stratosphere/sf/sf_common.hpp>
#include <stratosphere/mem.hpp>
namespace ams::sf {
class StandardAllocatorMemoryResource : public MemoryResource {
private:
mem::StandardAllocator *standard_allocator;
public:
explicit StandardAllocatorMemoryResource(mem::StandardAllocator *sa) : standard_allocator(sa) { /* ... */ }
mem::StandardAllocator *GetAllocator() const { return this->standard_allocator; }
private:
virtual void *AllocateImpl(size_t size, size_t alignment) override {
return this->standard_allocator->Allocate(size, alignment);
}
virtual void DeallocateImpl(void *buffer, size_t size, size_t alignment) override {
return this->standard_allocator->Free(buffer);
}
virtual bool IsEqualImpl(const MemoryResource &resource) const {
return this == std::addressof(resource);
}
};
}

View File

@@ -0,0 +1,491 @@
/*
* 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/>.
*/
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX) && defined(ATMOSPHERE_ARCH_ARM64)
namespace ams::svc::aarch64::lp64 {
ALWAYS_INLINE Result SetHeapSize(::ams::svc::Address *out_address, ::ams::svc::Size size) {
return ::svcSetHeapSize(reinterpret_cast<void **>(out_address), size);
}
ALWAYS_INLINE Result SetMemoryPermission(::ams::svc::Address address, ::ams::svc::Size size, ::ams::svc::MemoryPermission perm) {
return ::svcSetMemoryPermission(reinterpret_cast<void *>(static_cast<uintptr_t>(address)), size, static_cast<u32>(perm));
}
ALWAYS_INLINE Result SetMemoryAttribute(::ams::svc::Address address, ::ams::svc::Size size, uint32_t mask, uint32_t attr) {
return ::svcSetMemoryAttribute(reinterpret_cast<void *>(static_cast<uintptr_t>(address)), size, mask, attr);
}
ALWAYS_INLINE Result MapMemory(::ams::svc::Address dst_address, ::ams::svc::Address src_address, ::ams::svc::Size size) {
return ::svcMapMemory(reinterpret_cast<void *>(static_cast<uintptr_t>(dst_address)), reinterpret_cast<void *>(static_cast<uintptr_t>(src_address)), size);
}
ALWAYS_INLINE Result UnmapMemory(::ams::svc::Address dst_address, ::ams::svc::Address src_address, ::ams::svc::Size size) {
return ::svcUnmapMemory(reinterpret_cast<void *>(static_cast<uintptr_t>(dst_address)), reinterpret_cast<void *>(static_cast<uintptr_t>(src_address)), size);
}
ALWAYS_INLINE Result QueryMemory(::ams::svc::UserPointer< ::ams::svc::lp64::MemoryInfo *> out_memory_info, ::ams::svc::PageInfo *out_page_info, ::ams::svc::Address address) {
return ::svcQueryMemory(reinterpret_cast<::MemoryInfo *>(out_memory_info.GetPointerUnsafe()), reinterpret_cast<u32 *>(out_page_info), address);
}
ALWAYS_INLINE void ExitProcess() {
return ::svcExitProcess();
}
ALWAYS_INLINE Result CreateThread(::ams::svc::Handle *out_handle, ::ams::svc::ThreadFunc func, ::ams::svc::Address arg, ::ams::svc::Address stack_bottom, int32_t priority, int32_t core_id) {
return ::svcCreateThread(out_handle, reinterpret_cast<void *>(static_cast<uintptr_t>(func)), reinterpret_cast<void *>(static_cast<uintptr_t>(arg)), reinterpret_cast<void *>(static_cast<uintptr_t>(stack_bottom)), priority, core_id);
}
ALWAYS_INLINE Result StartThread(::ams::svc::Handle thread_handle) {
return ::svcStartThread(thread_handle);
}
ALWAYS_INLINE void ExitThread() {
return ::svcExitThread();
}
ALWAYS_INLINE void SleepThread(int64_t ns) {
return ::svcSleepThread(ns);
}
ALWAYS_INLINE Result GetThreadPriority(int32_t *out_priority, ::ams::svc::Handle thread_handle) {
return ::svcGetThreadPriority(out_priority, thread_handle);
}
ALWAYS_INLINE Result SetThreadPriority(::ams::svc::Handle thread_handle, int32_t priority) {
return ::svcSetThreadPriority(thread_handle, priority);
}
ALWAYS_INLINE Result GetThreadCoreMask(int32_t *out_core_id, uint64_t *out_affinity_mask, ::ams::svc::Handle thread_handle) {
return ::svcGetThreadCoreMask(out_core_id, out_affinity_mask, thread_handle);
}
ALWAYS_INLINE Result SetThreadCoreMask(::ams::svc::Handle thread_handle, int32_t core_id, uint64_t affinity_mask) {
return ::svcSetThreadCoreMask(thread_handle, core_id, affinity_mask);
}
ALWAYS_INLINE int32_t GetCurrentProcessorNumber() {
return ::svcGetCurrentProcessorNumber();
}
ALWAYS_INLINE Result SignalEvent(::ams::svc::Handle event_handle) {
return ::svcSignalEvent(event_handle);
}
ALWAYS_INLINE Result ClearEvent(::ams::svc::Handle event_handle) {
return ::svcClearEvent(event_handle);
}
ALWAYS_INLINE Result MapSharedMemory(::ams::svc::Handle shmem_handle, ::ams::svc::Address address, ::ams::svc::Size size, ::ams::svc::MemoryPermission map_perm) {
return ::svcMapSharedMemory(shmem_handle, reinterpret_cast<void *>(static_cast<uintptr_t>(address)), size, static_cast<u32>(map_perm));
}
ALWAYS_INLINE Result UnmapSharedMemory(::ams::svc::Handle shmem_handle, ::ams::svc::Address address, ::ams::svc::Size size) {
return ::svcUnmapSharedMemory(shmem_handle, reinterpret_cast<void *>(static_cast<uintptr_t>(address)), size);
}
ALWAYS_INLINE Result CreateTransferMemory(::ams::svc::Handle *out_handle, ::ams::svc::Address address, ::ams::svc::Size size, ::ams::svc::MemoryPermission map_perm) {
return ::svcCreateTransferMemory(out_handle, reinterpret_cast<void *>(static_cast<uintptr_t>(address)), size, static_cast<u32>(map_perm));
}
ALWAYS_INLINE Result CloseHandle(::ams::svc::Handle handle) {
return ::svcCloseHandle(handle);
}
ALWAYS_INLINE Result ResetSignal(::ams::svc::Handle handle) {
return ::svcResetSignal(handle);
}
ALWAYS_INLINE Result WaitSynchronization(int32_t *out_index, ::ams::svc::UserPointer<const ::ams::svc::Handle *> handles, int32_t numHandles, int64_t timeout_ns) {
return ::svcWaitSynchronization(out_index, handles.GetPointerUnsafe(), numHandles, timeout_ns);
}
ALWAYS_INLINE Result CancelSynchronization(::ams::svc::Handle handle) {
return ::svcCancelSynchronization(handle);
}
ALWAYS_INLINE Result ArbitrateLock(::ams::svc::Handle thread_handle, ::ams::svc::Address address, uint32_t tag) {
return ::svcArbitrateLock(thread_handle, reinterpret_cast<u32 *>(static_cast<uintptr_t>(address)), tag);
}
ALWAYS_INLINE Result ArbitrateUnlock(::ams::svc::Address address) {
return ::svcArbitrateUnlock(reinterpret_cast<u32 *>(static_cast<uintptr_t>(address)));
}
ALWAYS_INLINE Result WaitProcessWideKeyAtomic(::ams::svc::Address address, ::ams::svc::Address cv_key, uint32_t tag, int64_t timeout_ns) {
return ::svcWaitProcessWideKeyAtomic(reinterpret_cast<u32 *>(static_cast<uintptr_t>(address)), reinterpret_cast<u32 *>(static_cast<uintptr_t>(cv_key)), tag, timeout_ns);
}
ALWAYS_INLINE void SignalProcessWideKey(::ams::svc::Address cv_key, int32_t count) {
return ::svcSignalProcessWideKey(reinterpret_cast<u32 *>(static_cast<uintptr_t>(cv_key)), count);
}
ALWAYS_INLINE int64_t GetSystemTick() {
return ::svcGetSystemTick();
}
ALWAYS_INLINE Result ConnectToNamedPort(::ams::svc::Handle *out_handle, ::ams::svc::UserPointer<const char *> name) {
return ::svcConnectToNamedPort(out_handle, name.GetPointerUnsafe());
}
ALWAYS_INLINE Result SendSyncRequestLight(::ams::svc::Handle session_handle) {
return ::svcSendSyncRequestLight(session_handle);
}
ALWAYS_INLINE Result SendSyncRequest(::ams::svc::Handle session_handle) {
return ::svcSendSyncRequest(session_handle);
}
ALWAYS_INLINE Result SendSyncRequestWithUserBuffer(::ams::svc::Address message_buffer, ::ams::svc::Size message_buffer_size, ::ams::svc::Handle session_handle) {
return ::svcSendSyncRequestWithUserBuffer(reinterpret_cast<void *>(static_cast<uintptr_t>(message_buffer)), message_buffer_size, session_handle);
}
ALWAYS_INLINE Result SendAsyncRequestWithUserBuffer(::ams::svc::Handle *out_event_handle, ::ams::svc::Address message_buffer, ::ams::svc::Size message_buffer_size, ::ams::svc::Handle session_handle) {
return ::svcSendAsyncRequestWithUserBuffer(out_event_handle, reinterpret_cast<void *>(static_cast<uintptr_t>(message_buffer)), message_buffer_size, session_handle);
}
ALWAYS_INLINE Result GetProcessId(uint64_t *out_process_id, ::ams::svc::Handle process_handle) {
return ::svcGetProcessId(out_process_id, process_handle);
}
ALWAYS_INLINE Result GetThreadId(uint64_t *out_thread_id, ::ams::svc::Handle thread_handle) {
return ::svcGetThreadId(out_thread_id, thread_handle);
}
ALWAYS_INLINE void Break(::ams::svc::BreakReason break_reason, ::ams::svc::Address arg, ::ams::svc::Size size) {
::svcBreak(break_reason, arg, size);
}
ALWAYS_INLINE Result OutputDebugString(::ams::svc::UserPointer<const char *> debug_str, ::ams::svc::Size len) {
return ::svcOutputDebugString(debug_str.GetPointerUnsafe(), len);
}
ALWAYS_INLINE void ReturnFromException(::ams::Result result) {
return ::svcReturnFromException(result.GetValue());
}
ALWAYS_INLINE Result GetInfo(uint64_t *out, ::ams::svc::InfoType info_type, ::ams::svc::Handle handle, uint64_t info_subtype) {
return ::svcGetInfo(out, static_cast<u32>(info_type), handle, info_subtype);
}
ALWAYS_INLINE void FlushEntireDataCache() {
return ::svcFlushEntireDataCache();
}
ALWAYS_INLINE Result FlushDataCache(::ams::svc::Address address, ::ams::svc::Size size) {
return ::svcFlushDataCache(reinterpret_cast<void *>(static_cast<uintptr_t>(address)), size);
}
ALWAYS_INLINE Result MapPhysicalMemory(::ams::svc::Address address, ::ams::svc::Size size) {
return ::svcMapPhysicalMemory(reinterpret_cast<void *>(static_cast<uintptr_t>(address)), size);
}
ALWAYS_INLINE Result UnmapPhysicalMemory(::ams::svc::Address address, ::ams::svc::Size size) {
return ::svcUnmapPhysicalMemory(reinterpret_cast<void *>(static_cast<uintptr_t>(address)), size);
}
ALWAYS_INLINE Result GetDebugFutureThreadInfo(::ams::svc::lp64::LastThreadContext *out_context, uint64_t *thread_id, ::ams::svc::Handle debug_handle, int64_t ns) {
return ::svcGetDebugFutureThreadInfo(reinterpret_cast<::LastThreadContext *>(out_context), thread_id, debug_handle, ns);
}
ALWAYS_INLINE Result GetLastThreadInfo(::ams::svc::lp64::LastThreadContext *out_context, ::ams::svc::Address *out_tls_address, uint32_t *out_flags) {
return ::svcGetLastThreadInfo(reinterpret_cast<::LastThreadContext *>(out_context), reinterpret_cast<u64 *>(out_tls_address), out_flags);
}
ALWAYS_INLINE Result GetResourceLimitLimitValue(int64_t *out_limit_value, ::ams::svc::Handle resource_limit_handle, ::ams::svc::LimitableResource which) {
return ::svcGetResourceLimitLimitValue(out_limit_value, resource_limit_handle, static_cast<::LimitableResource>(which));
}
ALWAYS_INLINE Result GetResourceLimitCurrentValue(int64_t *out_current_value, ::ams::svc::Handle resource_limit_handle, ::ams::svc::LimitableResource which) {
return ::svcGetResourceLimitCurrentValue(out_current_value, resource_limit_handle, static_cast<::LimitableResource>(which));
}
ALWAYS_INLINE Result SetThreadActivity(::ams::svc::Handle thread_handle, ::ams::svc::ThreadActivity thread_activity) {
return ::svcSetThreadActivity(thread_handle, static_cast<::ThreadActivity>(thread_activity));
}
ALWAYS_INLINE Result GetThreadContext3(::ams::svc::UserPointer< ::ams::svc::ThreadContext *> out_context, ::ams::svc::Handle thread_handle) {
return ::svcGetThreadContext3(reinterpret_cast<::ThreadContext *>(out_context.GetPointerUnsafe()), thread_handle);
}
ALWAYS_INLINE Result WaitForAddress(::ams::svc::Address address, ::ams::svc::ArbitrationType arb_type, int32_t value, int64_t timeout_ns) {
return ::svcWaitForAddress(reinterpret_cast<void *>(static_cast<uintptr_t>(address)), arb_type, value, timeout_ns);
}
ALWAYS_INLINE Result SignalToAddress(::ams::svc::Address address, ::ams::svc::SignalType signal_type, int32_t value, int32_t count) {
return ::svcSignalToAddress(reinterpret_cast<void *>(static_cast<uintptr_t>(address)), signal_type, value, count);
}
ALWAYS_INLINE void SynchronizePreemptionState() {
return ::svcSynchronizePreemptionState();
}
ALWAYS_INLINE void KernelDebug(::ams::svc::KernelDebugType kern_debug_type, uint64_t arg0, uint64_t arg1, uint64_t arg2) {
return ::svcKernelDebug(kern_debug_type, arg0, arg1, arg2);
}
ALWAYS_INLINE void ChangeKernelTraceState(::ams::svc::KernelTraceState kern_trace_state) {
return ::svcChangeKernelTraceState(kern_trace_state);
}
ALWAYS_INLINE Result CreateSession(::ams::svc::Handle *out_server_session_handle, ::ams::svc::Handle *out_client_session_handle, bool is_light, ::ams::svc::Address name) {
return ::svcCreateSession(out_server_session_handle, out_client_session_handle, is_light, name);
}
ALWAYS_INLINE Result AcceptSession(::ams::svc::Handle *out_handle, ::ams::svc::Handle port) {
return ::svcAcceptSession(out_handle, port);
}
ALWAYS_INLINE Result ReplyAndReceiveLight(::ams::svc::Handle handle) {
return ::svcReplyAndReceiveLight(handle);
}
ALWAYS_INLINE Result ReplyAndReceive(int32_t *out_index, ::ams::svc::UserPointer<const ::ams::svc::Handle *> handles, int32_t num_handles, ::ams::svc::Handle reply_target, int64_t timeout_ns) {
return ::svcReplyAndReceive(out_index, handles.GetPointerUnsafe(), num_handles, reply_target, timeout_ns);
}
ALWAYS_INLINE Result ReplyAndReceiveWithUserBuffer(int32_t *out_index, ::ams::svc::Address message_buffer, ::ams::svc::Size message_buffer_size, ::ams::svc::UserPointer<const ::ams::svc::Handle *> handles, int32_t num_handles, ::ams::svc::Handle reply_target, int64_t timeout_ns) {
return ::svcReplyAndReceiveWithUserBuffer(out_index, reinterpret_cast<void *>(static_cast<uintptr_t>(message_buffer)), message_buffer_size, handles.GetPointerUnsafe(), num_handles, reply_target, timeout_ns);
}
ALWAYS_INLINE Result CreateEvent(::ams::svc::Handle *out_write_handle, ::ams::svc::Handle *out_read_handle) {
return ::svcCreateEvent(out_write_handle, out_read_handle);
}
ALWAYS_INLINE Result MapPhysicalMemoryUnsafe(::ams::svc::Address address, ::ams::svc::Size size) {
return ::svcMapPhysicalMemoryUnsafe(reinterpret_cast<void *>(static_cast<uintptr_t>(address)), size);
}
ALWAYS_INLINE Result UnmapPhysicalMemoryUnsafe(::ams::svc::Address address, ::ams::svc::Size size) {
return ::svcUnmapPhysicalMemoryUnsafe(reinterpret_cast<void *>(static_cast<uintptr_t>(address)), size);
}
ALWAYS_INLINE Result SetUnsafeLimit(::ams::svc::Size limit) {
return ::svcSetUnsafeLimit(limit);
}
ALWAYS_INLINE Result CreateCodeMemory(::ams::svc::Handle *out_handle, ::ams::svc::Address address, ::ams::svc::Size size) {
return ::svcCreateCodeMemory(out_handle, reinterpret_cast<void *>(static_cast<uintptr_t>(address)), size);
}
ALWAYS_INLINE Result ControlCodeMemory(::ams::svc::Handle code_memory_handle, ::ams::svc::CodeMemoryOperation operation, uint64_t address, uint64_t size, ::ams::svc::MemoryPermission perm) {
return ::svcControlCodeMemory(code_memory_handle, static_cast<::CodeMapOperation>(operation), reinterpret_cast<void *>(address), size, static_cast<u32>(perm));
}
ALWAYS_INLINE void SleepSystem() {
return ::svcSleepSystem();
}
ALWAYS_INLINE Result ReadWriteRegister(uint32_t *out_value, ::ams::svc::PhysicalAddress address, uint32_t mask, uint32_t value) {
return ::svcReadWriteRegister(out_value, address, mask, value);
}
ALWAYS_INLINE Result SetProcessActivity(::ams::svc::Handle process_handle, ::ams::svc::ProcessActivity process_activity) {
return ::svcSetProcessActivity(process_handle, static_cast<::ProcessActivity>(process_activity));
}
ALWAYS_INLINE Result CreateSharedMemory(::ams::svc::Handle *out_handle, ::ams::svc::Size size, ::ams::svc::MemoryPermission owner_perm, ::ams::svc::MemoryPermission remote_perm) {
return ::svcCreateSharedMemory(out_handle, size, static_cast<u32>(owner_perm), static_cast<u32>(remote_perm));
}
ALWAYS_INLINE Result MapTransferMemory(::ams::svc::Handle trmem_handle, ::ams::svc::Address address, ::ams::svc::Size size, ::ams::svc::MemoryPermission owner_perm) {
return ::svcMapTransferMemory(trmem_handle, reinterpret_cast<void *>(static_cast<uintptr_t>(address)), size, static_cast<u32>(owner_perm));
}
ALWAYS_INLINE Result UnmapTransferMemory(::ams::svc::Handle trmem_handle, ::ams::svc::Address address, ::ams::svc::Size size) {
return ::svcUnmapTransferMemory(trmem_handle, reinterpret_cast<void *>(static_cast<uintptr_t>(address)), size);
}
ALWAYS_INLINE Result CreateInterruptEvent(::ams::svc::Handle *out_read_handle, int32_t interrupt_id, ::ams::svc::InterruptType interrupt_type) {
return ::svcCreateInterruptEvent(out_read_handle, interrupt_id, static_cast<u32>(interrupt_type));
}
ALWAYS_INLINE Result QueryPhysicalAddress(::ams::svc::lp64::PhysicalMemoryInfo *out_info, ::ams::svc::Address address) {
return ::svcQueryPhysicalAddress(reinterpret_cast<::PhysicalMemoryInfo *>(out_info), address);
}
ALWAYS_INLINE Result QueryIoMapping(::ams::svc::Address *out_address, ::ams::svc::PhysicalAddress physical_address, ::ams::svc::Size size) {
return ::svcQueryIoMapping(reinterpret_cast<u64 *>(out_address), physical_address, size);
}
ALWAYS_INLINE Result CreateDeviceAddressSpace(::ams::svc::Handle *out_handle, uint64_t das_address, uint64_t das_size) {
return ::svcCreateDeviceAddressSpace(out_handle, das_address, das_size);
}
ALWAYS_INLINE Result AttachDeviceAddressSpace(::ams::svc::DeviceName device_name, ::ams::svc::Handle das_handle) {
return ::svcAttachDeviceAddressSpace(static_cast<u64>(device_name), das_handle);
}
ALWAYS_INLINE Result DetachDeviceAddressSpace(::ams::svc::DeviceName device_name, ::ams::svc::Handle das_handle) {
return ::svcDetachDeviceAddressSpace(static_cast<u64>(device_name), das_handle);
}
ALWAYS_INLINE Result MapDeviceAddressSpaceByForce(::ams::svc::Handle das_handle, ::ams::svc::Handle process_handle, uint64_t process_address, ::ams::svc::Size size, uint64_t device_address, ::ams::svc::MemoryPermission device_perm) {
return ::svcMapDeviceAddressSpaceByForce(das_handle, process_handle, process_address, size, device_address, static_cast<u32>(device_perm));
}
ALWAYS_INLINE Result MapDeviceAddressSpaceAligned(::ams::svc::Handle das_handle, ::ams::svc::Handle process_handle, uint64_t process_address, ::ams::svc::Size size, uint64_t device_address, ::ams::svc::MemoryPermission device_perm) {
return ::svcMapDeviceAddressSpaceAligned(das_handle, process_handle, process_address, size, device_address, static_cast<u32>(device_perm));
}
ALWAYS_INLINE Result MapDeviceAddressSpace(::ams::svc::Size *out_mapped_size, ::ams::svc::Handle das_handle, ::ams::svc::Handle process_handle, uint64_t process_address, ::ams::svc::Size size, uint64_t device_address, ::ams::svc::MemoryPermission device_perm) {
return ::svcMapDeviceAddressSpace(reinterpret_cast<u64 *>(out_mapped_size), das_handle, process_handle, process_address, size, device_address, static_cast<u32>(device_perm));
}
ALWAYS_INLINE Result UnmapDeviceAddressSpace(::ams::svc::Handle das_handle, ::ams::svc::Handle process_handle, uint64_t process_address, ::ams::svc::Size size, uint64_t device_address) {
return ::svcUnmapDeviceAddressSpace(das_handle, process_handle, process_address, size, device_address);
}
ALWAYS_INLINE Result InvalidateProcessDataCache(::ams::svc::Handle process_handle, uint64_t address, uint64_t size) {
return ::svcInvalidateProcessDataCache(process_handle, address, size);
}
ALWAYS_INLINE Result StoreProcessDataCache(::ams::svc::Handle process_handle, uint64_t address, uint64_t size) {
return ::svcStoreProcessDataCache(process_handle, address, size);
}
ALWAYS_INLINE Result FlushProcessDataCache(::ams::svc::Handle process_handle, uint64_t address, uint64_t size) {
return ::svcFlushProcessDataCache(process_handle, address, size);
}
ALWAYS_INLINE Result DebugActiveProcess(::ams::svc::Handle *out_handle, uint64_t process_id) {
return ::svcDebugActiveProcess(out_handle, process_id);
}
ALWAYS_INLINE Result BreakDebugProcess(::ams::svc::Handle debug_handle) {
return ::svcBreakDebugProcess(debug_handle);
}
ALWAYS_INLINE Result TerminateDebugProcess(::ams::svc::Handle debug_handle) {
return ::svcTerminateDebugProcess(debug_handle);
}
ALWAYS_INLINE Result GetDebugEvent(::ams::svc::UserPointer< ::ams::svc::lp64::DebugEventInfo *> out_info, ::ams::svc::Handle debug_handle) {
return ::svcGetDebugEvent(out_info.GetPointerUnsafe(), debug_handle);
}
ALWAYS_INLINE Result ContinueDebugEvent(::ams::svc::Handle debug_handle, uint32_t flags, ::ams::svc::UserPointer<const uint64_t *> thread_ids, int32_t num_thread_ids) {
return ::svcContinueDebugEvent(debug_handle, flags, const_cast<u64 *>(thread_ids.GetPointerUnsafe()), num_thread_ids);
}
ALWAYS_INLINE Result GetProcessList(int32_t *out_num_processes, ::ams::svc::UserPointer<uint64_t *> out_process_ids, int32_t max_out_count) {
return ::svcGetProcessList(out_num_processes, out_process_ids.GetPointerUnsafe(), max_out_count);
}
ALWAYS_INLINE Result GetThreadList(int32_t *out_num_threads, ::ams::svc::UserPointer<uint64_t *> out_thread_ids, int32_t max_out_count, ::ams::svc::Handle debug_handle) {
return ::svcGetThreadList(out_num_threads, out_thread_ids.GetPointerUnsafe(), max_out_count, debug_handle);
}
ALWAYS_INLINE Result GetDebugThreadContext(::ams::svc::UserPointer< ::ams::svc::ThreadContext *> out_context, ::ams::svc::Handle debug_handle, uint64_t thread_id, uint32_t context_flags) {
return ::svcGetDebugThreadContext(reinterpret_cast<::ThreadContext *>(out_context.GetPointerUnsafe()), debug_handle, thread_id, context_flags);
}
ALWAYS_INLINE Result SetDebugThreadContext(::ams::svc::Handle debug_handle, uint64_t thread_id, ::ams::svc::UserPointer<const ::ams::svc::ThreadContext *> context, uint32_t context_flags) {
return ::svcSetDebugThreadContext(debug_handle, thread_id, reinterpret_cast<const ::ThreadContext *>(context.GetPointerUnsafe()), context_flags);
}
ALWAYS_INLINE Result QueryDebugProcessMemory(::ams::svc::UserPointer< ::ams::svc::lp64::MemoryInfo *> out_memory_info, ::ams::svc::PageInfo *out_page_info, ::ams::svc::Handle process_handle, ::ams::svc::Address address) {
return ::svcQueryDebugProcessMemory(reinterpret_cast<::MemoryInfo *>(out_memory_info.GetPointerUnsafe()), reinterpret_cast<u32 *>(out_page_info), process_handle, address);
}
ALWAYS_INLINE Result ReadDebugProcessMemory(::ams::svc::Address buffer, ::ams::svc::Handle debug_handle, ::ams::svc::Address address, ::ams::svc::Size size) {
return ::svcReadDebugProcessMemory(reinterpret_cast<void *>(static_cast<uintptr_t>(buffer)), debug_handle, address, size);
}
ALWAYS_INLINE Result WriteDebugProcessMemory(::ams::svc::Handle debug_handle, ::ams::svc::Address buffer, ::ams::svc::Address address, ::ams::svc::Size size) {
return ::svcWriteDebugProcessMemory(debug_handle, reinterpret_cast<const void *>(static_cast<uintptr_t>(buffer)), address, size);
}
ALWAYS_INLINE Result SetHardwareBreakPoint(::ams::svc::HardwareBreakPointRegisterName name, uint64_t flags, uint64_t value) {
return ::svcSetHardwareBreakPoint(static_cast<u32>(name), flags, value);
}
ALWAYS_INLINE Result GetDebugThreadParam(uint64_t *out_64, uint32_t *out_32, ::ams::svc::Handle debug_handle, uint64_t thread_id, ::ams::svc::DebugThreadParam param) {
return ::svcGetDebugThreadParam(out_64, out_32, debug_handle, thread_id, static_cast<::DebugThreadParam>(param));
}
ALWAYS_INLINE Result GetSystemInfo(uint64_t *out, ::ams::svc::SystemInfoType info_type, ::ams::svc::Handle handle, uint64_t info_subtype) {
return ::svcGetSystemInfo(out, static_cast<u64>(info_type), handle, info_subtype);
}
ALWAYS_INLINE Result CreatePort(::ams::svc::Handle *out_server_handle, ::ams::svc::Handle *out_client_handle, int32_t max_sessions, bool is_light, ::ams::svc::Address name) {
return ::svcCreatePort(out_server_handle, out_client_handle, max_sessions, is_light, reinterpret_cast<const char *>(static_cast<uintptr_t>(name)));
}
ALWAYS_INLINE Result ManageNamedPort(::ams::svc::Handle *out_server_handle, ::ams::svc::UserPointer<const char *> name, int32_t max_sessions) {
return ::svcManageNamedPort(out_server_handle, name.GetPointerUnsafe(), max_sessions);
}
ALWAYS_INLINE Result ConnectToPort(::ams::svc::Handle *out_handle, ::ams::svc::Handle port) {
return ::svcConnectToPort(out_handle, port);
}
ALWAYS_INLINE Result SetProcessMemoryPermission(::ams::svc::Handle process_handle, uint64_t address, uint64_t size, ::ams::svc::MemoryPermission perm) {
return ::svcSetProcessMemoryPermission(process_handle, address, size, static_cast<u32>(perm));
}
ALWAYS_INLINE Result MapProcessMemory(::ams::svc::Address dst_address, ::ams::svc::Handle process_handle, uint64_t src_address, ::ams::svc::Size size) {
return ::svcMapProcessMemory(reinterpret_cast<void *>(static_cast<uintptr_t>(dst_address)), process_handle, src_address, size);
}
ALWAYS_INLINE Result UnmapProcessMemory(::ams::svc::Address dst_address, ::ams::svc::Handle process_handle, uint64_t src_address, ::ams::svc::Size size) {
return ::svcUnmapProcessMemory(reinterpret_cast<void *>(static_cast<uintptr_t>(dst_address)), process_handle, src_address, size);
}
ALWAYS_INLINE Result QueryProcessMemory(::ams::svc::UserPointer< ::ams::svc::lp64::MemoryInfo *> out_memory_info, ::ams::svc::PageInfo *out_page_info, ::ams::svc::Handle process_handle, uint64_t address) {
return ::svcQueryProcessMemory(reinterpret_cast<::MemoryInfo *>(out_memory_info.GetPointerUnsafe()), reinterpret_cast<u32 *>(out_page_info), process_handle, address);
}
ALWAYS_INLINE Result MapProcessCodeMemory(::ams::svc::Handle process_handle, uint64_t dst_address, uint64_t src_address, uint64_t size) {
return ::svcMapProcessCodeMemory(process_handle, dst_address, src_address, size);
}
ALWAYS_INLINE Result UnmapProcessCodeMemory(::ams::svc::Handle process_handle, uint64_t dst_address, uint64_t src_address, uint64_t size) {
return ::svcUnmapProcessCodeMemory(process_handle, dst_address, src_address, size);
}
ALWAYS_INLINE Result CreateProcess(::ams::svc::Handle *out_handle, ::ams::svc::UserPointer<const ::ams::svc::lp64::CreateProcessParameter *> parameters, ::ams::svc::UserPointer<const uint32_t *> caps, int32_t num_caps) {
return ::svcCreateProcess(out_handle, parameters.GetPointerUnsafe(), caps.GetPointerUnsafe(), num_caps);
}
ALWAYS_INLINE Result StartProcess(::ams::svc::Handle process_handle, int32_t priority, int32_t core_id, uint64_t main_thread_stack_size) {
return ::svcStartProcess(process_handle, priority, core_id, main_thread_stack_size);
}
ALWAYS_INLINE Result TerminateProcess(::ams::svc::Handle process_handle) {
return ::svcTerminateProcess(process_handle);
}
ALWAYS_INLINE Result GetProcessInfo(int64_t *out_info, ::ams::svc::Handle process_handle, ::ams::svc::ProcessInfoType info_type) {
return ::svcGetProcessInfo(out_info, process_handle, static_cast<::ProcessInfoType>(info_type));
}
ALWAYS_INLINE Result CreateResourceLimit(::ams::svc::Handle *out_handle) {
return ::svcCreateResourceLimit(out_handle);
}
ALWAYS_INLINE Result SetResourceLimitLimitValue(::ams::svc::Handle resource_limit_handle, ::ams::svc::LimitableResource which, int64_t limit_value) {
return ::svcSetResourceLimitLimitValue(resource_limit_handle, static_cast<::LimitableResource>(which), limit_value);
}
ALWAYS_INLINE void CallSecureMonitor(::ams::svc::lp64::SecureMonitorArguments *args) {
::svcCallSecureMonitor(reinterpret_cast<::SecmonArgs *>(args));
}
}
#endif

View File

@@ -21,7 +21,16 @@
namespace ams::updater {
/* Public API. */
BootImageUpdateType GetBootImageUpdateType(int boot_image_update_type);
BootImageUpdateType GetBootImageUpdateType(spl::HardwareType hw_type);
Result GetBootImagePackageId(ncm::SystemDataId *out_data_id, BootModeType mode, void *work_buffer, size_t work_buffer_size);
Result MarkVerifyingRequired(BootModeType mode, void *work_buffer, size_t work_buffer_size);
Result MarkVerified(BootModeType mode, void *work_buffer, size_t work_buffer_size);
Result UpdateBootImagesFromPackage(ncm::SystemDataId boot_image_package_id, BootModeType mode, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type);
Result VerifyBootImagesAndRepairIfNeeded(bool *out_repaired_normal, bool *out_repaired_safe, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type);
}

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

@@ -41,7 +41,7 @@ namespace ams::diag {
__builtin_unreachable();
}
ALWAYS_INLINE void DebugLog(const char *format, ...) __attribute__((format(printf, 1, 2)));
inline void DebugLog(const char *format, ...) __attribute__((format(printf, 1, 2)));
#ifdef AMS_ENABLE_DEBUG_PRINT
os::Mutex g_debug_log_lock;
@@ -55,7 +55,7 @@ namespace ams::diag {
svc::OutputDebugString(g_debug_buffer, strlen(g_debug_buffer));
}
void DebugLog(const char *format, ...) __attribute__((format(printf, 1, 2))) {
void DebugLog(const char *format, ...) {
::std::va_list vl;
va_start(vl, format);
DebugLogImpl(format, vl);

View File

@@ -241,6 +241,7 @@ namespace ams::fs {
R_TRY(OpenSdCardCodeOrCodeFileSystemImpl(std::addressof(fsa), path, program_id));
this->code_fs.emplace(std::move(fsa), program_id, is_specific);
this->program_id = program_id;
this->initialized = true;
return ResultSuccess();

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/>.
*/
#include <stratosphere.hpp>
#include "fsa/fs_mount_utils.hpp"
namespace ams::fs {
namespace {
constexpr inline SaveDataSpaceId DeviceSaveDataSpaceId = SaveDataSpaceId::User;
Result MountDeviceSaveDataImpl(const char *name, const SaveDataAttribute &attribute) {
/* Validate the mount name. */
R_TRY(impl::CheckMountName(name));
/* Open the filesystem, use libnx bindings. */
::FsFileSystem fs;
R_TRY(fsOpenSaveDataFileSystem(std::addressof(fs), static_cast<::FsSaveDataSpaceId>(DeviceSaveDataSpaceId), reinterpret_cast<const ::FsSaveDataAttribute *>(std::addressof(attribute))));
/* Allocate a new filesystem wrapper. */
auto fsa = std::make_unique<RemoteFileSystem>(fs);
R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInDeviceSaveDataA());
/* Register. */
return fsa::Register(name, std::move(fsa));
}
}
Result MountDeviceSaveData(const char *name) {
return MountDeviceSaveDataImpl(name, SaveDataAttribute::Make(ncm::InvalidProgramId, SaveDataType::Device, InvalidUserId, InvalidSystemSaveDataId));
}
Result MountDeviceSaveData(const char *name, const ncm::ApplicationId application_id) {
return MountDeviceSaveDataImpl(name, SaveDataAttribute::Make(application_id, SaveDataType::Device, InvalidUserId, InvalidSystemSaveDataId));
}
}

View File

@@ -90,4 +90,75 @@ namespace ams::fs {
}
}
Result FileHandleStorage::UpdateSize() {
R_SUCCEED_IF(this->size != InvalidSize);
return GetFileSize(std::addressof(this->size), this->handle);
}
Result FileHandleStorage::Read(s64 offset, void *buffer, size_t size) {
/* Lock the mutex. */
std::scoped_lock lk(this->mutex);
/* Immediately succeed if there's nothing to read. */
R_SUCCEED_IF(size == 0);
/* Validate buffer. */
R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument());
/* Ensure our size is valid. */
R_TRY(this->UpdateSize());
/* Ensure our access is valid. */
R_UNLESS(IStorage::IsRangeValid(offset, size, this->size), fs::ResultOutOfRange());
return ReadFile(this->handle, offset, buffer, size, fs::ReadOption());
}
Result FileHandleStorage::Write(s64 offset, const void *buffer, size_t size) {
/* Lock the mutex. */
std::scoped_lock lk(this->mutex);
/* Immediately succeed if there's nothing to write. */
R_SUCCEED_IF(size == 0);
/* Validate buffer. */
R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument());
/* Ensure our size is valid. */
R_TRY(this->UpdateSize());
/* Ensure our access is valid. */
R_UNLESS(IStorage::IsRangeValid(offset, size, this->size), fs::ResultOutOfRange());
return WriteFile(this->handle, offset, buffer, size, fs::WriteOption());
}
Result FileHandleStorage::Flush() {
return FlushFile(this->handle);
}
Result FileHandleStorage::GetSize(s64 *out_size) {
R_TRY(this->UpdateSize());
*out_size = this->size;
return ResultSuccess();
}
Result FileHandleStorage::SetSize(s64 size) {
this->size = InvalidSize;
return SetFileSize(this->handle, size);
}
Result FileHandleStorage::OperateRange(void *dst, size_t dst_size, OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) {
switch (op_id) {
case OperationId::QueryRange:
/* Validate buffer and size. */
R_UNLESS(dst != nullptr, fs::ResultNullptrArgument());
R_UNLESS(dst_size == sizeof(QueryRangeInfo), fs::ResultInvalidSize());
return QueryRange(static_cast<QueryRangeInfo *>(dst), this->handle, offset, size);
default:
return fs::ResultUnsupportedOperationInFileStorageB();
}
}
}

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 "fsa/fs_mount_utils.hpp"
namespace ams::fs {
Result MountImageDirectory(const char *name, ImageDirectoryId id) {
/* Validate the mount name. */
R_TRY(impl::CheckMountName(name));
/* Open the image directory. This uses libnx bindings. */
FsFileSystem fs;
R_TRY(fsOpenImageDirectoryFileSystem(std::addressof(fs), static_cast<::FsImageDirectoryId>(id)));
/* Allocate a new filesystem wrapper. */
auto fsa = std::make_unique<RemoteFileSystem>(fs);
R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInImageDirectoryA());
/* Register. */
return fsa::Register(name, std::move(fsa));
}
}

View File

@@ -18,6 +18,30 @@
namespace ams::fs {
namespace {
/* NOTE: Nintendo does not attach a generator to a mounted SD card filesystem. */
/* However, it is desirable for homebrew to be able to access SD via common path. */
class SdCardCommonMountNameGenerator : public fsa::ICommonMountNameGenerator, public impl::Newable {
public:
explicit SdCardCommonMountNameGenerator() { /* ... */ }
virtual Result GenerateCommonMountName(char *dst, size_t dst_size) override {
/* Determine how much space we need. */
const size_t needed_size = strnlen(impl::SdCardFileSystemMountName, MountNameLengthMax) + 2;
AMS_ABORT_UNLESS(dst_size >= needed_size);
/* Generate the name. */
auto size = std::snprintf(dst, dst_size, "%s:", impl::SdCardFileSystemMountName);
AMS_ASSERT(static_cast<size_t>(size) == needed_size - 1);
return ResultSuccess();
}
};
}
Result MountSdCard(const char *name) {
/* Validate the mount name. */
R_TRY(impl::CheckMountName(name));
@@ -30,8 +54,13 @@ namespace ams::fs {
auto fsa = std::make_unique<RemoteFileSystem>(fs);
R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInSdCardA());
/* Allocate a new mountname generator. */
/* NOTE: Nintendo does not attach a generator. */
auto generator = std::make_unique<SdCardCommonMountNameGenerator>();
R_UNLESS(generator != nullptr, fs::ResultAllocationFailureInSdCardA());
/* Register. */
return fsa::Register(name, std::move(fsa));
return fsa::Register(name, std::move(fsa), std::move(generator));
}
}

View File

@@ -0,0 +1,450 @@
/*
* Copyright (c) 2018-2020 Adubbz, 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>
namespace ams::fssystem {
namespace {
class PartitionFileSystemDefaultAllocator : public MemoryResource {
private:
virtual void *AllocateImpl(size_t size, size_t alignment) override {
return ::ams::fs::impl::Allocate(size);
}
virtual void DeallocateImpl(void *buffer, size_t size, size_t alignment) override {
::ams::fs::impl::Deallocate(buffer, size);
}
virtual bool IsEqualImpl(const MemoryResource &rhs) const override {
return this == std::addressof(rhs);
}
};
PartitionFileSystemDefaultAllocator g_partition_filesystem_default_allocator;
}
template <typename MetaType>
class PartitionFileSystemCore<MetaType>::PartitionFile : public fs::fsa::IFile, public fs::impl::Newable {
private:
const typename MetaType::PartitionEntry *partition_entry;
const PartitionFileSystemCore<MetaType> *parent;
const fs::OpenMode mode;
public:
PartitionFile(PartitionFileSystemCore<MetaType> *parent, const typename MetaType::PartitionEntry *partition_entry, fs::OpenMode mode) : partition_entry(partition_entry), parent(parent), mode(mode) { /* ... */ }
private:
virtual Result ReadImpl(size_t *out, s64 offset, void *buffer, size_t size, const fs::ReadOption &option) override final;
virtual Result GetSizeImpl(s64 *out) override final {
*out = this->partition_entry->size;
return ResultSuccess();
}
virtual Result FlushImpl() override final {
/* Nothing to do if writing disallowed. */
R_SUCCEED_IF((this->mode & fs::OpenMode_Write) == 0);
/* Flush base storage. */
return this->parent->base_storage->Flush();
}
virtual Result WriteImpl(s64 offset, const void *buffer, size_t size, const fs::WriteOption &option) override final {
/* Ensure appending is not required. */
bool needs_append;
R_TRY(this->DryWrite(std::addressof(needs_append), offset, size, option, this->mode));
R_UNLESS(!needs_append, fs::ResultUnsupportedOperationInPartitionFileA());
/* Appending is prohibited. */
AMS_ASSERT((this->mode & fs::OpenMode_AllowAppend) == 0);
/* Validate offset and size. */
R_UNLESS(offset <= static_cast<s64>(this->partition_entry->size), fs::ResultOutOfRange());
R_UNLESS(static_cast<s64>(offset + size) <= static_cast<s64>(this->partition_entry->size), fs::ResultInvalidSize());
/* Write to the base storage. */
return this->parent->base_storage->Write(this->parent->meta_data_size + this->partition_entry->offset + offset, buffer, size);
}
virtual Result SetSizeImpl(s64 size) override final {
R_TRY(this->DrySetSize(size, this->mode));
return fs::ResultUnsupportedOperationInPartitionFileA();
}
virtual Result OperateRangeImpl(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override final {
/* Validate preconditions for operation. */
switch (op_id) {
case fs::OperationId::InvalidateCache:
R_UNLESS((this->mode & fs::OpenMode_Read) != 0, fs::ResultReadNotPermitted());
R_UNLESS((this->mode & fs::OpenMode_Write) == 0, fs::ResultUnsupportedOperationInPartitionFileB());
break;
case fs::OperationId::QueryRange:
break;
default:
return fs::ResultUnsupportedOperationInPartitionFileB();
}
/* Validate offset and size. */
R_UNLESS(offset >= 0, fs::ResultOutOfRange());
R_UNLESS(offset <= static_cast<s64>(this->partition_entry->size), fs::ResultOutOfRange());
R_UNLESS(static_cast<s64>(offset + size) <= static_cast<s64>(this->partition_entry->size), fs::ResultInvalidSize());
R_UNLESS(static_cast<s64>(offset + size) >= offset, fs::ResultInvalidSize());
return this->parent->base_storage->OperateRange(dst, dst_size, op_id, this->parent->meta_data_size + this->partition_entry->offset + offset, size, src, src_size);
}
public:
virtual sf::cmif::DomainObjectId GetDomainObjectId() const override {
/* TODO: How should this be handled? */
return sf::cmif::InvalidDomainObjectId;
}
};
template<>
Result PartitionFileSystemCore<PartitionFileSystemMeta>::PartitionFile::ReadImpl(size_t *out, s64 offset, void *dst, size_t dst_size, const fs::ReadOption &option) {
/* Perform a dry read. */
size_t read_size = 0;
R_TRY(this->DryRead(std::addressof(read_size), offset, dst_size, option, this->mode));
/* Read from the base storage. */
R_TRY(this->parent->base_storage->Read(this->parent->meta_data_size + this->partition_entry->offset + offset, dst, read_size));
/* Set output size. */
*out = read_size;
return ResultSuccess();
}
template<>
Result PartitionFileSystemCore<Sha256PartitionFileSystemMeta>::PartitionFile::ReadImpl(size_t *out, s64 offset, void *dst, size_t dst_size, const fs::ReadOption &option) {
/* Perform a dry read. */
size_t read_size = 0;
R_TRY(this->DryRead(std::addressof(read_size), offset, dst_size, option, this->mode));
const s64 entry_start = this->parent->meta_data_size + this->partition_entry->offset;
const s64 read_end = static_cast<s64>(offset + read_size);
const s64 hash_start = static_cast<s64>(this->partition_entry->hash_target_offset);
const s64 hash_end = hash_start + this->partition_entry->hash_target_size;
if (read_end <= hash_start || hash_end <= offset) {
/* We aren't reading hashed data, so we can just read from the base storage. */
R_TRY(this->parent->base_storage->Read(entry_start + offset, dst, read_size));
} else {
/* Only hash target offset == 0 is supported. */
R_UNLESS(hash_start == 0, fs::ResultInvalidSha256PartitionHashTarget());
/* Ensure that the hash region is valid. */
R_UNLESS(this->partition_entry->hash_target_offset + this->partition_entry->hash_target_size <= this->partition_entry->size, fs::ResultInvalidSha256PartitionHashTarget());
/* Validate our read offset. */
const s64 read_offset = entry_start + offset;
R_UNLESS(read_offset >= offset, fs::ResultOutOfRange());
/* Prepare a buffer for our calculated hash. */
char hash[crypto::Sha256Generator::HashSize];
crypto::Sha256Generator generator;
/* Ensure we can perform our read. */
const bool hash_in_read = offset <= hash_start && hash_end <= read_end;
const bool read_in_hash = hash_start <= offset && read_end <= hash_end;
R_UNLESS(hash_in_read || read_in_hash, fs::ResultInvalidSha256PartitionHashTarget());
/* Initialize the generator. */
generator.Initialize();
if (hash_in_read) {
/* Easy case: hash region is contained within the bounds. */
R_TRY(this->parent->base_storage->Read(entry_start + offset, dst, read_size));
generator.Update(static_cast<u8 *>(dst) + hash_start - offset, this->partition_entry->hash_target_size);
} else /* if (read_in_hash) */ {
/* We're reading a portion of what's hashed. */
s64 remaining_hash_size = this->partition_entry->hash_target_size;
s64 hash_offset = entry_start + hash_start;
s64 remaining_size = read_size;
s64 copy_offset = 0;
while (remaining_hash_size > 0) {
/* Read some portion of data into the buffer. */
constexpr size_t HashBufferSize = 0x200;
char hash_buffer[HashBufferSize];
size_t cur_size = static_cast<size_t>(std::min(static_cast<s64>(HashBufferSize), remaining_hash_size));
R_TRY(this->parent->base_storage->Read(hash_offset, hash_buffer, cur_size));
/* Update the hash. */
generator.Update(hash_buffer, cur_size);
/* If we need to copy, do so. */
if (read_offset <= (hash_offset + static_cast<s64>(cur_size)) && remaining_size > 0) {
const s64 hash_buffer_offset = std::max<s64>(read_offset - hash_offset, 0);
const size_t copy_size = static_cast<size_t>(std::min<s64>(cur_size - hash_buffer_offset, remaining_size));
std::memcpy(static_cast<u8 *>(dst) + copy_offset, hash_buffer + hash_buffer_offset, copy_size);
remaining_size -= copy_size;
copy_offset += copy_size;
}
/* Update offsets. */
remaining_hash_size -= cur_size;
hash_offset += cur_size;
}
}
/* Get the hash. */
generator.GetHash(hash, sizeof(hash));
/* Validate the hash. */
auto hash_guard = SCOPE_GUARD { std::memset(dst, 0, read_size); };
R_UNLESS(crypto::IsSameBytes(this->partition_entry->hash, hash, sizeof(hash)), fs::ResultSha256PartitionHashVerificationFailed());
/* We successfully completed our read. */
hash_guard.Cancel();
}
/* Set output size. */
*out = read_size;
return ResultSuccess();
}
template <typename MetaType>
class PartitionFileSystemCore<MetaType>::PartitionDirectory : public fs::fsa::IDirectory, public fs::impl::Newable {
private:
u32 cur_index;
const PartitionFileSystemCore<MetaType> *parent;
const fs::OpenDirectoryMode mode;
public:
PartitionDirectory(PartitionFileSystemCore<MetaType> *parent, fs::OpenDirectoryMode mode) : cur_index(0), parent(parent), mode(mode) { /* ... */ }
public:
virtual Result ReadImpl(s64 *out_count, fs::DirectoryEntry *out_entries, s64 max_entries) override final {
/* There are no subdirectories. */
if ((this->mode & fs::OpenDirectoryMode_File) == 0) {
*out_count = 0;
return ResultSuccess();
}
/* Calculate number of entries. */
const s64 entry_count = std::min(max_entries, static_cast<s64>(this->parent->meta_data->GetEntryCount() - this->cur_index));
/* Populate output directory entries. */
for (auto i = 0; i < entry_count; i++, this->cur_index++) {
fs::DirectoryEntry &dir_entry = out_entries[i];
/* Setup the output directory entry. */
dir_entry.type = fs::DirectoryEntryType_File;
dir_entry.file_size = this->parent->meta_data->GetEntry(this->cur_index)->size;
std::strncpy(dir_entry.name, this->parent->meta_data->GetEntryName(this->cur_index), sizeof(dir_entry.name) - 1);
dir_entry.name[sizeof(dir_entry.name) - 1] = StringTraits::NullTerminator;
}
*out_count = entry_count;
return ResultSuccess();
}
virtual Result GetEntryCountImpl(s64 *out) override final {
/* Output the parent meta data entry count for files, otherwise 0. */
if (this->mode & fs::OpenDirectoryMode_File) {
*out = this->parent->meta_data->GetEntryCount();
} else {
*out = 0;
}
return ResultSuccess();
}
virtual sf::cmif::DomainObjectId GetDomainObjectId() const override {
/* TODO: How should this be handled? */
return sf::cmif::InvalidDomainObjectId;
}
};
template <typename MetaType>
PartitionFileSystemCore<MetaType>::PartitionFileSystemCore() : initialized(false) {
/* ... */
}
template <typename MetaType>
PartitionFileSystemCore<MetaType>::~PartitionFileSystemCore() {
/* ... */
}
template <typename MetaType>
Result PartitionFileSystemCore<MetaType>::Initialize(fs::IStorage *base_storage, MemoryResource *allocator) {
/* Validate preconditions. */
R_UNLESS(!this->initialized, fs::ResultPreconditionViolation());
/* Allocate meta data. */
this->unique_meta_data = std::make_unique<MetaType>();
R_UNLESS(this->unique_meta_data != nullptr, fs::ResultAllocationFailureInPartitionFileSystemA());
/* Initialize meta data. */
R_TRY(this->unique_meta_data->Initialize(base_storage, allocator));
/* Initialize members. */
this->meta_data = this->unique_meta_data.get();
this->base_storage = base_storage;
this->meta_data_size = this->meta_data->GetMetaDataSize();
this->initialized = true;
return ResultSuccess();
}
template <typename MetaType>
Result PartitionFileSystemCore<MetaType>::Initialize(std::unique_ptr<MetaType> &&meta_data, std::shared_ptr<fs::IStorage> base_storage) {
this->unique_meta_data = std::move(meta_data);
return this->Initialize(this->unique_meta_data.get(), base_storage);
}
template <typename MetaType>
Result PartitionFileSystemCore<MetaType>::Initialize(MetaType *meta_data, std::shared_ptr<fs::IStorage> base_storage) {
/* Validate preconditions. */
R_UNLESS(!this->initialized, fs::ResultPreconditionViolation());
/* Initialize members. */
this->shared_storage = std::move(base_storage);
this->base_storage = this->shared_storage.get();
this->meta_data = meta_data;
this->meta_data_size = this->meta_data->GetMetaDataSize();
this->initialized = true;
return ResultSuccess();
}
template <typename MetaType>
Result PartitionFileSystemCore<MetaType>::Initialize(fs::IStorage *base_storage) {
return this->Initialize(base_storage, std::addressof(g_partition_filesystem_default_allocator));
}
template <typename MetaType>
Result PartitionFileSystemCore<MetaType>::Initialize(std::shared_ptr<fs::IStorage> base_storage) {
this->shared_storage = std::move(base_storage);
return this->Initialize(this->shared_storage.get());
}
template <typename MetaType>
Result PartitionFileSystemCore<MetaType>::Initialize(std::shared_ptr<fs::IStorage> base_storage, MemoryResource *allocator) {
this->shared_storage = std::move(base_storage);
return this->Initialize(this->shared_storage.get(), allocator);
}
template <typename MetaType>
Result PartitionFileSystemCore<MetaType>::GetFileBaseOffset(s64 *out_offset, const char *path) {
/* Validate preconditions. */
R_UNLESS(this->initialized, fs::ResultPreconditionViolation());
/* Obtain and validate the entry index. */
const s32 entry_index = this->meta_data->GetEntryIndex(path + 1);
R_UNLESS(entry_index >= 0, fs::ResultPathNotFound());
/* Output offset. */
*out_offset = this->meta_data_size + this->meta_data->GetEntry(entry_index)->offset;
return ResultSuccess();
}
template <typename MetaType>
Result PartitionFileSystemCore<MetaType>::GetEntryTypeImpl(fs::DirectoryEntryType *out, const char *path) {
/* Validate preconditions. */
R_UNLESS(this->initialized, fs::ResultPreconditionViolation());
R_UNLESS(PathTool::IsSeparator(path[0]), fs::ResultInvalidPathFormat());
/* Check if the path is for a directory. */
if (std::strncmp(path, PathTool::RootPath, sizeof(PathTool::RootPath)) == 0) {
*out = fs::DirectoryEntryType_Directory;
return ResultSuccess();
}
/* Ensure that path is for a file. */
R_UNLESS(this->meta_data->GetEntryIndex(path + 1) >= 0, fs::ResultPathNotFound());
*out = fs::DirectoryEntryType_File;
return ResultSuccess();
}
template <typename MetaType>
Result PartitionFileSystemCore<MetaType>::OpenFileImpl(std::unique_ptr<fs::fsa::IFile> *out_file, const char *path, fs::OpenMode mode) {
/* Validate preconditions. */
R_UNLESS(this->initialized, fs::ResultPreconditionViolation());
/* Obtain and validate the entry index. */
const s32 entry_index = this->meta_data->GetEntryIndex(path + 1);
R_UNLESS(entry_index >= 0, fs::ResultPathNotFound());
/* Create and output the file directory. */
std::unique_ptr file = std::make_unique<PartitionFile>(this, this->meta_data->GetEntry(entry_index), mode);
R_UNLESS(file != nullptr, fs::ResultAllocationFailureInPartitionFileSystemB());
*out_file = std::move(file);
return ResultSuccess();
}
template <typename MetaType>
Result PartitionFileSystemCore<MetaType>::OpenDirectoryImpl(std::unique_ptr<fs::fsa::IDirectory> *out_dir, const char *path, fs::OpenDirectoryMode mode) {
/* Validate preconditions. */
R_UNLESS(this->initialized, fs::ResultPreconditionViolation());
R_UNLESS(std::strncmp(path, PathTool::RootPath, sizeof(PathTool::RootPath)) == 0, fs::ResultPathNotFound());
/* Create and output the partition directory. */
std::unique_ptr directory = std::make_unique<PartitionDirectory>(this, mode);
R_UNLESS(directory != nullptr, fs::ResultAllocationFailureInPartitionFileSystemC());
*out_dir = std::move(directory);
return ResultSuccess();
}
template <typename MetaType>
Result PartitionFileSystemCore<MetaType>::CommitImpl() {
return ResultSuccess();
}
template <typename MetaType>
Result PartitionFileSystemCore<MetaType>::CleanDirectoryRecursivelyImpl(const char *path) {
return fs::ResultUnsupportedOperationInPartitionFileSystemA();
}
template <typename MetaType>
Result PartitionFileSystemCore<MetaType>::CreateDirectoryImpl(const char *path) {
return fs::ResultUnsupportedOperationInPartitionFileSystemA();
}
template <typename MetaType>
Result PartitionFileSystemCore<MetaType>::CreateFileImpl(const char *path, s64 size, int option) {
return fs::ResultUnsupportedOperationInPartitionFileSystemA();
}
template <typename MetaType>
Result PartitionFileSystemCore<MetaType>::DeleteDirectoryImpl(const char *path) {
return fs::ResultUnsupportedOperationInPartitionFileSystemA();
}
template <typename MetaType>
Result PartitionFileSystemCore<MetaType>::DeleteDirectoryRecursivelyImpl(const char *path) {
return fs::ResultUnsupportedOperationInPartitionFileSystemA();
}
template <typename MetaType>
Result PartitionFileSystemCore<MetaType>::DeleteFileImpl(const char *path) {
return fs::ResultUnsupportedOperationInPartitionFileSystemA();
}
template <typename MetaType>
Result PartitionFileSystemCore<MetaType>::RenameDirectoryImpl(const char *old_path, const char *new_path) {
return fs::ResultUnsupportedOperationInPartitionFileSystemA();
}
template <typename MetaType>
Result PartitionFileSystemCore<MetaType>::RenameFileImpl(const char *old_path, const char *new_path) {
return fs::ResultUnsupportedOperationInPartitionFileSystemA();
}
template <typename MetaType>
Result PartitionFileSystemCore<MetaType>::CommitProvisionallyImpl(s64 counter) {
return fs::ResultUnsupportedOperationInPartitionFileSystemB();
}
template class PartitionFileSystemCore<PartitionFileSystemMeta>;
template class PartitionFileSystemCore<Sha256PartitionFileSystemMeta>;
}

View File

@@ -0,0 +1,219 @@
/*
* Copyright (c) 2018-2020 Adubbz, 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>
namespace ams::fssystem {
template <typename Format>
struct PartitionFileSystemMetaCore<Format>::PartitionFileSystemHeader {
char signature[sizeof(Format::VersionSignature)];
s32 entry_count;
u32 name_table_size;
u32 reserved;
};
static_assert(std::is_pod<PartitionFileSystemMeta::PartitionFileSystemHeader>::value);
static_assert(sizeof(PartitionFileSystemMeta::PartitionFileSystemHeader) == 0x10);
template <typename Format>
PartitionFileSystemMetaCore<Format>::~PartitionFileSystemMetaCore() {
this->DeallocateBuffer();
}
template <typename Format>
Result PartitionFileSystemMetaCore<Format>::Initialize(fs::IStorage *storage, MemoryResource *allocator) {
/* Validate preconditions. */
AMS_ASSERT(allocator != nullptr);
/* Determine the meta data size. */
R_TRY(this->QueryMetaDataSize(std::addressof(this->meta_data_size), storage));
/* Deallocate any old meta buffer and allocate a new one. */
this->DeallocateBuffer();
this->allocator = allocator;
this->buffer = static_cast<char *>(this->allocator->Allocate(this->meta_data_size));
R_UNLESS(this->buffer != nullptr, fs::ResultAllocationFailureInPartitionFileSystemMetaA());
/* Perform regular initialization. */
return this->Initialize(storage, this->buffer, this->meta_data_size);
}
template <typename Format>
Result PartitionFileSystemMetaCore<Format>::Initialize(fs::IStorage *storage, void *meta, size_t meta_size) {
/* Validate size for header. */
R_UNLESS(meta_size >= sizeof(PartitionFileSystemHeader), fs::ResultInvalidSize());
/* Read the header. */
R_TRY(storage->Read(0, meta, sizeof(PartitionFileSystemHeader)));
/* Set and validate the header. */
this->header = reinterpret_cast<PartitionFileSystemHeader *>(meta);
R_UNLESS(crypto::IsSameBytes(this->header->signature, Format::VersionSignature, sizeof(Format::VersionSignature)), typename Format::ResultSignatureVerificationFailed());
/* Setup entries and name table. */
const size_t entries_size = this->header->entry_count * sizeof(typename Format::PartitionEntry);
this->entries = reinterpret_cast<PartitionEntry *>(static_cast<u8 *>(meta) + sizeof(PartitionFileSystemHeader));
this->name_table = static_cast<char *>(meta) + sizeof(PartitionFileSystemHeader) + entries_size;
/* Validate size for header + entries + name table. */
R_UNLESS(meta_size >= sizeof(PartitionFileSystemHeader) + entries_size + this->header->name_table_size, fs::ResultInvalidSize());
/* Read entries and name table. */
R_TRY(storage->Read(sizeof(PartitionFileSystemHeader), this->entries, entries_size + this->header->name_table_size));
/* Mark as initialized. */
this->initialized = true;
return ResultSuccess();
}
template <typename Format>
void PartitionFileSystemMetaCore<Format>::DeallocateBuffer() {
if (this->buffer != nullptr) {
AMS_ABORT_UNLESS(this->allocator != nullptr);
this->allocator->Deallocate(this->buffer, this->meta_data_size);
this->buffer = nullptr;
}
}
template <typename Format>
const typename Format::PartitionEntry *PartitionFileSystemMetaCore<Format>::GetEntry(s32 index) const {
if (this->initialized && 0 <= index && index < static_cast<s32>(this->header->entry_count)) {
return std::addressof(this->entries[index]);
}
return nullptr;
}
template <typename Format>
s32 PartitionFileSystemMetaCore<Format>::GetEntryCount() const {
if (this->initialized) {
return this->header->entry_count;
}
return 0;
}
template <typename Format>
s32 PartitionFileSystemMetaCore<Format>::GetEntryIndex(const char *name) const {
/* Fail if not initialized. */
if (!this->initialized) {
return 0;
}
for (s32 i = 0; i < static_cast<s32>(this->header->entry_count); i++) {
const auto &entry = this->entries[i];
/* Name offset is invalid. */
if (entry.name_offset >= this->header->name_table_size) {
return 0;
}
/* Compare to input name. */
const s32 max_name_len = this->header->name_table_size - entry.name_offset;
if (std::strncmp(std::addressof(this->name_table[entry.name_offset]), name, max_name_len) == 0) {
return i;
}
}
/* Not found. */
return -1;
}
template <typename Format>
const char *PartitionFileSystemMetaCore<Format>::GetEntryName(s32 index) const {
if (this->initialized && index < static_cast<s32>(this->header->entry_count)) {
return std::addressof(this->name_table[this->GetEntry(index)->name_offset]);
}
return nullptr;
}
template <typename Format>
size_t PartitionFileSystemMetaCore<Format>::GetHeaderSize() const {
return sizeof(PartitionFileSystemHeader);
}
template <typename Format>
size_t PartitionFileSystemMetaCore<Format>::GetMetaDataSize() const {
return this->meta_data_size;
}
template <typename Format>
Result PartitionFileSystemMetaCore<Format>::QueryMetaDataSize(size_t *out_size, fs::IStorage *storage) {
/* Read and validate the header. */
PartitionFileSystemHeader header;
R_TRY(storage->Read(0, std::addressof(header), sizeof(PartitionFileSystemHeader)));
R_UNLESS(crypto::IsSameBytes(std::addressof(header), Format::VersionSignature, sizeof(Format::VersionSignature)), typename Format::ResultSignatureVerificationFailed());
/* Output size. */
*out_size = sizeof(PartitionFileSystemHeader) + header.entry_count * sizeof(typename Format::PartitionEntry) + header.name_table_size;
return ResultSuccess();
}
template class PartitionFileSystemMetaCore<impl::PartitionFileSystemFormat>;
template class PartitionFileSystemMetaCore<impl::Sha256PartitionFileSystemFormat>;
Result Sha256PartitionFileSystemMeta::Initialize(fs::IStorage *base_storage, MemoryResource *allocator, const void *hash, size_t hash_size, std::optional<u8> suffix) {
/* Ensure preconditions. */
R_UNLESS(hash_size == crypto::Sha256Generator::HashSize, fs::ResultPreconditionViolation());
/* Get metadata size. */
R_TRY(QueryMetaDataSize(std::addressof(this->meta_data_size), base_storage));
/* Ensure we have no buffer. */
this->DeallocateBuffer();
/* Set allocator and allocate buffer. */
this->allocator = allocator;
this->buffer = static_cast<char *>(this->allocator->Allocate(this->meta_data_size));
R_UNLESS(this->buffer != nullptr, fs::ResultAllocationFailureInPartitionFileSystemMetaB());
/* Read metadata. */
R_TRY(base_storage->Read(0, this->buffer, this->meta_data_size));
/* Calculate hash. */
char calc_hash[crypto::Sha256Generator::HashSize];
{
crypto::Sha256Generator generator;
generator.Initialize();
generator.Update(this->buffer, this->meta_data_size);
if (suffix) {
u8 suffix_val = *suffix;
generator.Update(std::addressof(suffix_val), 1);
}
generator.GetHash(calc_hash, sizeof(calc_hash));
}
/* Ensure hash is valid. */
R_UNLESS(crypto::IsSameBytes(hash, calc_hash, sizeof(calc_hash)), fs::ResultSha256PartitionHashVerificationFailed());
/* Give access to Format */
using Format = impl::Sha256PartitionFileSystemFormat;
/* Set header. */
this->header = reinterpret_cast<PartitionFileSystemHeader *>(this->buffer);
R_UNLESS(crypto::IsSameBytes(this->header->signature, Format::VersionSignature, sizeof(Format::VersionSignature)), typename Format::ResultSignatureVerificationFailed());
/* Validate size for entries and name table. */
const size_t entries_size = this->header->entry_count * sizeof(typename Format::PartitionEntry);
R_UNLESS(this->meta_data_size >= sizeof(PartitionFileSystemHeader) + entries_size + this->header->name_table_size, fs::ResultInvalidSha256PartitionMetaDataSize());
/* Set entries and name table. */
this->entries = reinterpret_cast<PartitionEntry *>(this->buffer + sizeof(PartitionFileSystemHeader));
this->name_table = this->buffer + sizeof(PartitionFileSystemHeader) + entries_size;
/* We initialized. */
this->initialized = true;
return ResultSuccess();
}
}

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

@@ -284,7 +284,7 @@ namespace ams::lmem::impl {
{
/* Create the used block */
MemoryRegion used_region{ .start = free_region_front.end, .end = free_region_back.start };
MemoryRegion used_region{ .start = reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(block) - sizeof(ExpHeapMemoryBlockHead)), .end = free_region_back.start };
ExpHeapMemoryBlockHead *used_block = InitializeUsedMemoryBlock(used_region);

View File

@@ -0,0 +1,116 @@
/*
* 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 "mem_impl_heap_platform.hpp"
#include "mem_impl_heap_tls_heap_static.hpp"
#include "mem_impl_heap_tls_heap_central.hpp"
namespace ams::mem::impl::heap {
void *CachedHeap::Allocate(size_t n) {
return this->tls_heap_cache->Allocate(n);
}
void *CachedHeap::Allocate(size_t n, size_t align) {
return this->tls_heap_cache->Allocate(n, align);
}
size_t CachedHeap::GetAllocationSize(const void *ptr) {
return this->tls_heap_cache->GetAllocationSize(ptr);
}
errno_t CachedHeap::Free(void *p) {
return this->tls_heap_cache->Free(p);
}
errno_t CachedHeap::FreeWithSize(void *p, size_t size) {
return this->tls_heap_cache->FreeWithSize(p, size);
}
errno_t CachedHeap::Reallocate(void *ptr, size_t size, void **p) {
return this->tls_heap_cache->Reallocate(ptr, size, p);
}
errno_t CachedHeap::Shrink(void *ptr, size_t size) {
return this->tls_heap_cache->Shrink(ptr, size);
}
void CachedHeap::ReleaseAllCache() {
if (this->tls_heap_cache) {
this->tls_heap_cache->ReleaseAllCache();
}
}
void CachedHeap::Finalize() {
if (this->tls_heap_cache) {
this->tls_heap_cache->Finalize();
this->tls_heap_cache = nullptr;
}
}
bool CachedHeap::CheckCache() {
bool cache = false;
auto err = this->Query(AllocQuery_CheckCache, std::addressof(cache));
AMS_ASSERT(err != 0);
return cache;
}
errno_t CachedHeap::QueryV(int _query, std::va_list vl) {
const AllocQuery query = static_cast<AllocQuery>(_query);
switch (query) {
case AllocQuery_CheckCache:
{
bool *out = va_arg(vl, bool *);
if (out) {
*out = (this->tls_heap_cache == nullptr) || this->tls_heap_cache->CheckCache();
}
return 0;
}
case AllocQuery_ClearCache:
{
this->ReleaseAllCache();
return 0;
}
case AllocQuery_FinalizeCache:
{
this->Finalize();
return 0;
}
default:
return EINVAL;
}
}
errno_t CachedHeap::Query(int query, ...) {
std::va_list vl;
va_start(vl, query);
auto err = this->QueryV(query, vl);
va_end(vl);
return err;
}
void CachedHeap::Reset(TlsHeapCache *thc) {
this->Finalize();
this->tls_heap_cache = thc;
}
TlsHeapCache *CachedHeap::Release() {
TlsHeapCache *ret = this->tls_heap_cache;
this->tls_heap_cache = nullptr;
return ret;
}
}

View File

@@ -0,0 +1,409 @@
/*
* 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 "mem_impl_heap_platform.hpp"
#include "mem_impl_heap_tls_heap_static.hpp"
#include "mem_impl_heap_tls_heap_central.hpp"
namespace ams::mem::impl::heap {
errno_t CentralHeap::Initialize(void *start, size_t size, u32 option) {
/* Validate size. */
if (size == 0 || !util::IsAligned(size, PageSize)) {
return EINVAL;
}
/* Don't allow initializing twice. */
if (this->start) {
return EEXIST;
}
if (start) {
/* We were provided with a region to use as backing memory. */
u8 *aligned_start = reinterpret_cast<u8 *>(util::AlignUp(reinterpret_cast<uintptr_t>(start), PageSize));
u8 *aligned_end = reinterpret_cast<u8 *>(util::AlignDown(reinterpret_cast<uintptr_t>(start) + size, PageSize));
if (aligned_start >= aligned_end) {
return EINVAL;
}
this->start = aligned_start;
this->end = aligned_end;
this->option = option;
this->tls_heap_central = new (this->start) TlsHeapCentral;
if (auto err = this->tls_heap_central->Initialize(this->start, this->end - this->start, false); err != 0) {
this->tls_heap_central->~TlsHeapCentral();
this->tls_heap_central = nullptr;
AMS_ASSERT(err == 0);
return err;
}
this->use_virtual_memory = false;
} else {
/* We were not provided with a region to use as backing. */
void *mem;
if (auto err = AllocateVirtualMemory(std::addressof(mem), size); err != 0) {
return err;
}
if (!util::IsAligned(reinterpret_cast<uintptr_t>(mem), PageSize)) {
FreeVirtualMemory(mem, size);
size += PageSize;
if (auto err = AllocateVirtualMemory(std::addressof(mem), size); err != 0) {
return err;
}
}
this->start = static_cast<u8 *>(mem);
this->end = this->start + size;
this->option = option;
void *central = reinterpret_cast<void *>(util::AlignUp(reinterpret_cast<uintptr_t>(mem), PageSize));
if (auto err = AllocatePhysicalMemory(central, sizeof(TlsHeapCentral)); err != 0) {
return err;
}
this->tls_heap_central = new (central) TlsHeapCentral;
if (auto err = this->tls_heap_central->Initialize(central, size, true); err != 0) {
this->tls_heap_central->~TlsHeapCentral();
this->tls_heap_central = nullptr;
AMS_ASSERT(err == 0);
return err;
}
this->use_virtual_memory = true;
}
return 0;
}
void CentralHeap::Finalize() {
if (this->tls_heap_central) {
this->tls_heap_central->~TlsHeapCentral();
}
if (this->use_virtual_memory) {
mem::impl::physical_free(util::AlignUp(static_cast<void *>(this->start), PageSize), this->end - this->start);
mem::impl::virtual_free(this->start, this->end - this->start);
}
this->tls_heap_central = nullptr;
this->use_virtual_memory = false;
this->option = 0;
this->start = nullptr;
this->end = nullptr;
}
void *CentralHeap::Allocate(size_t n, size_t align) {
if (!util::IsPowerOfTwo(align)) {
return nullptr;
}
if (n > MaxSize) {
return nullptr;
}
if (align > PageSize) {
return this->tls_heap_central->CacheLargeMemoryWithBigAlign(util::AlignUp(n, PageSize), align);
}
const size_t real_size = TlsHeapStatic::GetRealSizeFromSizeAndAlignment(util::AlignUp(n, align), align);
const auto cls = TlsHeapStatic::GetClassFromSize(real_size);
if (!cls) {
return this->tls_heap_central->CacheLargeMemory(real_size);
}
if (real_size == 0) {
return nullptr;
}
AMS_ASSERT(cls < TlsHeapStatic::NumClassInfo);
return this->tls_heap_central->CacheSmallMemory(cls, align);
}
size_t CentralHeap::GetAllocationSize(const void *ptr) {
const auto cls = this->tls_heap_central->GetClassFromPointer(ptr);
if (cls > 0) {
/* Check that the pointer has alignment from out allocator. */
if (!util::IsAligned(reinterpret_cast<uintptr_t>(ptr), MinimumAlignment)) {
return 0;
}
AMS_ASSERT(cls < TlsHeapStatic::NumClassInfo);
return TlsHeapStatic::GetChunkSize(cls);
} else if (ptr != nullptr) {
return this->tls_heap_central->GetAllocationSize(ptr);
} else {
return 0;
}
}
errno_t CentralHeap::Free(void *ptr) {
/* Allow Free(nullptr) */
if (ptr == nullptr) {
return 0;
}
/* Check that the pointer has alignment from out allocator. */
if(!util::IsAligned(reinterpret_cast<uintptr_t>(ptr), MinimumAlignment)) {
AMS_ASSERT(util::IsAligned(reinterpret_cast<uintptr_t>(ptr), MinimumAlignment));
return EFAULT;
}
const auto cls = this->tls_heap_central->GetClassFromPointer(ptr);
if (cls >= 0) {
AMS_ASSERT(cls < TlsHeapStatic::NumClassInfo);
if (cls) {
return this->tls_heap_central->UncacheSmallMemory(ptr);
} else {
return this->tls_heap_central->UncacheLargeMemory(ptr);
}
} else {
AMS_ASSERT(cls >= 0);
return EFAULT;
}
}
errno_t CentralHeap::FreeWithSize(void *ptr, size_t size) {
if (TlsHeapStatic::GetClassFromSize(size)) {
return this->tls_heap_central->UncacheSmallMemory(ptr);
} else {
return this->tls_heap_central->UncacheLargeMemory(ptr);
}
}
errno_t CentralHeap::Reallocate(void *ptr, size_t size, void **p) {
AMS_ASSERT(ptr != nullptr && size != 0);
if (!size) {
return EINVAL;
}
if (size > MaxSize) {
return ENOMEM;
}
const auto cls_from_size = TlsHeapStatic::GetClassFromSize(size);
const auto cls_from_ptr = this->tls_heap_central->GetClassFromPointer(ptr);
if (cls_from_ptr) {
if (cls_from_ptr <= 0) {
return EFAULT;
} else if (cls_from_size && cls_from_size <= cls_from_ptr) {
*p = ptr;
return 0;
} else {
const size_t new_chunk_size = TlsHeapStatic::GetChunkSize(cls_from_ptr);
*p = this->Allocate(new_chunk_size);
if (*p) {
std::memcpy(*p, ptr, size);
return this->tls_heap_central->UncacheSmallMemory(ptr);
} else {
return ENOMEM;
}
}
} else if (cls_from_size) {
*p = this->Allocate(size);
if (*p) {
std::memcpy(*p, ptr, size);
return this->tls_heap_central->UncacheLargeMemory(ptr);
} else {
return ENOMEM;
}
} else {
return this->tls_heap_central->ReallocateLargeMemory(ptr, size, p);
}
}
errno_t CentralHeap::Shrink(void *ptr, size_t size) {
AMS_ASSERT(ptr != nullptr && size != 0);
if (!size) {
return EINVAL;
}
if (size > MaxSize) {
return ENOMEM;
}
const auto cls_from_size = TlsHeapStatic::GetClassFromSize(size);
const auto cls_from_ptr = this->tls_heap_central->GetClassFromPointer(ptr);
if (cls_from_ptr) {
if (cls_from_ptr <= 0) {
return EFAULT;
} else if (cls_from_size && cls_from_size <= cls_from_ptr) {
return 0;
} else {
return EINVAL;
}
} else if (cls_from_size) {
return this->tls_heap_central->ShrinkLargeMemory(ptr, PageSize);
} else {
return this->tls_heap_central->ShrinkLargeMemory(ptr, size);
}
}
bool CentralHeap::MakeCache(CachedHeap *cached_heap) {
if (cached_heap == nullptr) {
return false;
}
AMS_ASSERT(this->tls_heap_central != nullptr);
const auto cls = TlsHeapStatic::GetClassFromSize(sizeof(*cached_heap));
void *tls_heap_cache = this->tls_heap_central->CacheSmallMemoryForSystem(cls);
if (tls_heap_cache == nullptr) {
return false;
}
new (tls_heap_cache) TlsHeapCache(this->tls_heap_central, this->option);
if (this->tls_heap_central->AddThreadCache(reinterpret_cast<TlsHeapCache *>(tls_heap_cache)) != 0) {
this->tls_heap_central->UncacheSmallMemory(tls_heap_cache);
return false;
}
cached_heap->Reset(reinterpret_cast<TlsHeapCache *>(tls_heap_cache));
return true;
}
errno_t CentralHeap::WalkAllocatedPointers(HeapWalkCallback callback, void *user_data) {
if (!callback || !this->tls_heap_central) {
return EINVAL;
}
return this->tls_heap_central->WalkAllocatedPointers(callback, user_data);
}
errno_t CentralHeap::QueryV(int query, std::va_list vl) {
return this->QueryVImpl(query, std::addressof(vl));
}
errno_t CentralHeap::Query(int query, ...) {
std::va_list vl;
va_start(vl, query);
auto err = this->QueryVImpl(query, std::addressof(vl));
va_end(vl);
return err;
}
errno_t CentralHeap::QueryVImpl(int _query, std::va_list *vl_ptr) {
const AllocQuery query = static_cast<AllocQuery>(_query);
switch (query) {
case AllocQuery_Dump:
case AllocQuery_DumpJson:
{
auto dump_mode = static_cast<DumpMode>(va_arg(*vl_ptr, int));
auto fd = va_arg(*vl_ptr, int);
if (this->tls_heap_central) {
this->tls_heap_central->Dump(dump_mode, fd, query == AllocQuery_DumpJson);
}
return 0;
}
case AllocQuery_PageSize:
{
size_t *out = va_arg(*vl_ptr, size_t *);
if (out) {
*out = PageSize;
}
return 0;
}
case AllocQuery_AllocatedSize:
case AllocQuery_FreeSize:
case AllocQuery_SystemSize:
case AllocQuery_MaxAllocatableSize:
{
size_t *out = va_arg(*vl_ptr, size_t *);
if (!out) {
return 0;
}
if (!this->tls_heap_central) {
*out = 0;
return 0;
}
TlsHeapMemStats stats;
this->tls_heap_central->GetMemStats(std::addressof(stats));
switch (query) {
case AllocQuery_AllocatedSize:
default:
*out = stats.allocated_size;
break;
case AllocQuery_FreeSize:
*out = stats.free_size;
break;
case AllocQuery_SystemSize:
*out = stats.system_size;
break;
case AllocQuery_MaxAllocatableSize:
*out = stats.max_allocatable_size;
break;
}
return 0;
}
case AllocQuery_IsClean:
{
int *out = va_arg(*vl_ptr, int *);
if (out) {
*out = !this->tls_heap_central || this->tls_heap_central->IsClean();
}
return 0;
}
case AllocQuery_HeapHash:
{
HeapHash *out = va_arg(*vl_ptr, HeapHash *);
if (out) {
if (this->tls_heap_central) {
this->tls_heap_central->CalculateHeapHash(out);
} else {
*out = {};
}
}
return 0;
}
case AllocQuery_UnifyFreeList:
/* NOTE: Nintendo does not check that the ptr is not null for this query, even though they do for other queries. */
this->tls_heap_central->IsClean();
return 0;
case AllocQuery_SetColor:
{
/* NOTE: Nintendo does not check that the ptr is not null for this query, even though they do for other queries. */
void *ptr = va_arg(*vl_ptr, void *);
int color = va_arg(*vl_ptr, int);
return this->tls_heap_central->SetColor(ptr, color);
}
case AllocQuery_GetColor:
{
/* NOTE: Nintendo does not check that the ptr is not null for this query, even though they do for other queries. */
void *ptr = va_arg(*vl_ptr, void *);
int *out = va_arg(*vl_ptr, int *);
return this->tls_heap_central->GetColor(ptr, out);
}
case AllocQuery_SetName:
{
/* NOTE: Nintendo does not check that the ptr is not null for this query, even though they do for other queries. */
void *ptr = va_arg(*vl_ptr, void *);
const char *name = va_arg(*vl_ptr, const char *);
return this->tls_heap_central->SetName(ptr, name);
}
case AllocQuery_GetName:
{
/* NOTE: Nintendo does not check that the ptr is not null for this query, even though they do for other queries. */
void *ptr = va_arg(*vl_ptr, void *);
char *dst = va_arg(*vl_ptr, char *);
size_t dst_size = va_arg(*vl_ptr, size_t);
return this->tls_heap_central->GetName(ptr, dst, dst_size);
}
case AllocQuery_FreeSizeMapped:
case AllocQuery_MaxAllocatableSizeMapped:
{
/* NOTE: Nintendo does not check that the ptr is not null for this query, even though they do for other queries. */
size_t *out = va_arg(*vl_ptr, size_t *);
size_t free_size;
size_t max_allocatable_size;
auto err = this->tls_heap_central->GetMappedMemStats(std::addressof(free_size), std::addressof(max_allocatable_size));
if (err == 0) {
if (query == AllocQuery_FreeSizeMapped) {
*out = free_size;
} else {
*out = max_allocatable_size;
}
}
return err;
}
default:
return EINVAL;
}
}
}

View File

@@ -0,0 +1,40 @@
/*
* 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 "../mem_impl_platform.hpp"
namespace ams::mem::impl::heap {
using Prot = mem::impl::Prot;
inline errno_t AllocateVirtualMemory(void **ptr, size_t size) {
return ::ams::mem::impl::virtual_alloc(ptr, size);
}
inline errno_t FreeVirtualMemory(void *ptr, size_t size) {
return ::ams::mem::impl::virtual_free(ptr, size);
}
inline errno_t AllocatePhysicalMemory(void *ptr, size_t size) {
return ::ams::mem::impl::physical_alloc(ptr, size, static_cast<Prot>(Prot_read | Prot_write));
}
inline errno_t FreePhysicalMemory(void *ptr, size_t size) {
return ::ams::mem::impl::physical_free(ptr, size);
}
}

View File

@@ -0,0 +1,557 @@
/*
* 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 "mem_impl_heap_platform.hpp"
#include "mem_impl_heap_tls_heap_static.hpp"
#include "mem_impl_heap_tls_heap_cache.hpp"
#include "mem_impl_heap_tls_heap_central.hpp"
namespace ams::mem::impl::heap {
TlsHeapCache::TlsHeapCache(TlsHeapCentral *central, u32 option) {
/* Choose function impls based on option. */
if ((option & HeapOption_DisableCache) != 0) {
this->allocate = AllocateImpl<false>;
this->allocate_aligned = AllocateAlignedImpl<false>;
this->free = FreeImpl<false>;
this->free_with_size = FreeWithSizeImpl<false>;
this->get_allocation_size = GetAllocationSizeImpl<false>;
this->reallocate = ReallocateImpl<false>;
this->shrink = ShrinkImpl<false>;
} else {
this->allocate = AllocateImpl<true>;
this->allocate_aligned = AllocateAlignedImpl<true>;
this->free = FreeImpl<true>;
this->free_with_size = FreeWithSizeImpl<true>;
this->get_allocation_size = GetAllocationSizeImpl<true>;
this->reallocate = ReallocateImpl<true>;
this->shrink = ShrinkImpl<true>;
}
/* Generate random bytes to mangle pointers. */
if (auto err = gen_random(std::addressof(this->mangle_val), sizeof(this->mangle_val)); err != 0) {
s64 epoch_time;
epochtime(std::addressof(epoch_time));
this->mangle_val = reinterpret_cast<uintptr_t>(std::addressof(epoch_time)) ^ static_cast<u64>(epoch_time);
}
/* Set member variables. */
this->central = central;
this->total_heap_size = central->GetTotalHeapSize();
this->heap_option = option;
this->total_cached_size = 0;
this->largest_class = 0;
/* Setup chunks. */
for (size_t i = 0; i < TlsHeapStatic::NumClassInfo; i++) {
this->small_mem_lists[i] = nullptr;
this->cached_size[i] = 0;
this->chunk_count[i] = 1;
}
/* Set fixed chunk counts for particularly small chunks. */
this->chunk_count[1] = MaxChunkCount;
this->chunk_count[2] = MaxChunkCount;
this->chunk_count[3] = MaxChunkCount;
this->chunk_count[4] = MaxChunkCount / 2;
this->chunk_count[5] = MaxChunkCount / 2;
this->chunk_count[6] = MaxChunkCount / 2;
this->chunk_count[7] = MaxChunkCount / 4;
this->chunk_count[8] = MaxChunkCount / 4;
this->chunk_count[9] = MaxChunkCount / 4;
}
void TlsHeapCache::Finalize() {
/* Free all small mem lists. */
this->ReleaseAllCache();
/* Remove this cache from the owner central heap. */
this->central->RemoveThreadCache(this);
this->central->UncacheSmallMemory(this);
}
bool TlsHeapCache::CheckCache() const {
for (size_t i = 0; i < util::size(this->small_mem_lists); i++) {
void *ptr = this->small_mem_lists[i];
if (ptr) {
s64 depth = -static_cast<s64>(this->cached_size[i] / TlsHeapStatic::GetChunkSize(i));
while (ptr) {
ptr = *reinterpret_cast<void **>(this->ManglePointer(ptr));
if ((++depth) == 0) {
AMS_ASSERT(ptr == nullptr);
break;
}
}
}
}
return true;
}
void TlsHeapCache::ReleaseAllCache() {
for (size_t i = 0; i < util::size(this->small_mem_lists); i++) {
if (this->small_mem_lists[i]) {
this->central->UncacheSmallMemoryList(this, this->small_mem_lists[i]);
this->small_mem_lists[i] = nullptr;
this->cached_size[i] = 0;
}
}
this->total_cached_size = 0;
this->largest_class = 0;
}
template<>
void *TlsHeapCache::AllocateImpl<false>(TlsHeapCache *_this, size_t size) {
/* Validate allocation size. */
if (size == 0 || size > MaxSize) {
return nullptr;
}
if (const size_t cls = TlsHeapStatic::GetClassFromSize(size); cls != 0) {
AMS_ASSERT(cls < TlsHeapStatic::NumClassInfo);
return _this->central->CacheSmallMemory(cls);
} else {
/* If allocating a huge size, release our cache. */
if (size >= _this->total_heap_size / 4) {
_this->ReleaseAllCache();
}
return _this->central->CacheLargeMemory(size);
}
}
template<>
void *TlsHeapCache::AllocateImpl<true>(TlsHeapCache *_this, size_t size) {
/* Validate allocation size. */
if (size == 0 || size > MaxSize) {
return nullptr;
}
if (size_t cls = TlsHeapStatic::GetClassFromSize(size); cls != 0) {
AMS_ASSERT(cls < TlsHeapStatic::NumClassInfo);
/* Allocate a chunk. */
void *ptr = _this->small_mem_lists[cls];
if (ptr == nullptr) {
const size_t prev_cls = cls;
size_t count = _this->chunk_count[cls];
size_t n = _this->central->CacheSmallMemoryList(_this, std::addressof(cls), count, std::addressof(ptr));
if (n == 0) {
return nullptr;
}
if (cls == prev_cls) {
if (count < MaxChunkCount) {
count++;
}
_this->chunk_count[cls] = std::max(count, n);
} else {
AMS_ASSERT(n == 1);
}
const size_t csize = TlsHeapStatic::GetChunkSize(cls) * (n - 1);
_this->cached_size[cls] += csize;
if (_this->cached_size[cls] > _this->cached_size[_this->largest_class]) {
_this->largest_class = cls;
}
_this->total_cached_size += csize;
}
/* Demangle our pointer, update free list. */
ptr = _this->ManglePointer(ptr);
_this->small_mem_lists[cls] = *reinterpret_cast<void **>(ptr);
return ptr;
} else {
/* If allocating a huge size, release our cache. */
if (size >= _this->total_heap_size / 4) {
_this->ReleaseAllCache();
}
return _this->central->CacheLargeMemory(size);
}
}
template<>
void *TlsHeapCache::AllocateAlignedImpl<false>(TlsHeapCache *_this, size_t size, size_t align) {
/* Ensure valid alignment. */
if (!util::IsPowerOfTwo(align)) {
return nullptr;
}
/* NOTE: Nintendo does not check size == 0 here, despite doing so in Alloc */
if (size > MaxSize) {
return nullptr;
}
/* Handle big alignment. */
if (align > TlsHeapStatic::PageSize) {
return _this->central->CacheLargeMemoryWithBigAlign(util::AlignUp(size, TlsHeapStatic::PageSize), align);
}
const size_t real_size = TlsHeapStatic::GetRealSizeFromSizeAndAlignment(util::AlignUp(size, align), align);
if (const size_t cls = TlsHeapStatic::GetClassFromSize(real_size); cls != 0) {
if (real_size == 0) {
return nullptr;
}
AMS_ASSERT(cls < TlsHeapStatic::NumClassInfo);
return _this->central->CacheSmallMemory(cls, align);
} else {
/* If allocating a huge size, release our cache. */
if (real_size >= _this->total_heap_size / 4) {
_this->ReleaseAllCache();
}
return _this->central->CacheLargeMemory(real_size);
}
}
template<>
void *TlsHeapCache::AllocateAlignedImpl<true>(TlsHeapCache *_this, size_t size, size_t align) {
/* Ensure valid alignment. */
if (!util::IsPowerOfTwo(align)) {
return nullptr;
}
/* NOTE: Nintendo does not check size == 0 here, despite doing so in Alloc */
if (size > MaxSize) {
return nullptr;
}
/* Handle big alignment. */
if (align > TlsHeapStatic::PageSize) {
return _this->central->CacheLargeMemoryWithBigAlign(util::AlignUp(size, TlsHeapStatic::PageSize), align);
}
const size_t real_size = TlsHeapStatic::GetRealSizeFromSizeAndAlignment(util::AlignUp(size, align), align);
if (size_t cls = TlsHeapStatic::GetClassFromSize(real_size); cls != 0) {
if (real_size == 0) {
return nullptr;
}
AMS_ASSERT(cls < TlsHeapStatic::NumClassInfo);
/* Allocate a chunk. */
void *ptr = _this->small_mem_lists[cls];
if (ptr == nullptr) {
const size_t prev_cls = cls;
size_t count = _this->chunk_count[cls];
size_t n = _this->central->CacheSmallMemoryList(_this, std::addressof(cls), count, std::addressof(ptr), align);
if (n == 0) {
return nullptr;
}
if (cls == prev_cls) {
if (count < MaxChunkCount) {
count++;
}
_this->chunk_count[cls] = std::max(count, n);
} else {
AMS_ASSERT(n == 1);
}
const s32 csize = TlsHeapStatic::GetChunkSize(cls) * (n - 1);
_this->total_cached_size += csize;
_this->cached_size[cls] += csize;
if (_this->cached_size[cls] > _this->cached_size[_this->largest_class]) {
_this->largest_class = cls;
}
}
/* Demangle our pointer, update free list. */
ptr = _this->ManglePointer(ptr);
_this->small_mem_lists[cls] = *reinterpret_cast<void **>(ptr);
return ptr;
} else {
/* If allocating a huge size, release our cache. */
if (size >= _this->total_heap_size / 4) {
_this->ReleaseAllCache();
}
return _this->central->CacheLargeMemory(size);
}
}
template<>
errno_t TlsHeapCache::FreeImpl<false>(TlsHeapCache *_this, void *ptr) {
const size_t cls = _this->central->GetClassFromPointer(ptr);
if (cls == 0) {
return _this->central->UncacheLargeMemory(ptr);
}
AMS_ASSERT(cls < TlsHeapStatic::NumClassInfo);
if (static_cast<s32>(cls) >= 0) {
return _this->central->UncacheSmallMemory(ptr);
} else if (ptr == nullptr) {
return 0;
} else {
return EFAULT;
}
}
template<>
errno_t TlsHeapCache::FreeImpl<true>(TlsHeapCache *_this, void *ptr) {
const size_t cls = _this->central->GetClassFromPointer(ptr);
if (cls == 0) {
return _this->central->UncacheLargeMemory(ptr);
}
AMS_ASSERT(cls < TlsHeapStatic::NumClassInfo);
if (static_cast<s32>(cls) >= 0) {
*reinterpret_cast<void **>(ptr) = _this->small_mem_lists[cls];
_this->small_mem_lists[cls] = _this->ManglePointer(ptr);
const s32 csize = TlsHeapStatic::GetChunkSize(cls);
_this->total_cached_size += csize;
_this->cached_size[cls] += csize;
if (_this->cached_size[cls] > _this->cached_size[_this->largest_class]) {
_this->largest_class = cls;
}
errno_t err = 0;
if (!_this->central->CheckCachedSize(_this->total_cached_size)) {
_this->central->UncacheSmallMemoryList(_this, _this->small_mem_lists[_this->largest_class]);
_this->small_mem_lists[_this->largest_class] = nullptr;
_this->total_cached_size -= _this->cached_size[_this->largest_class];
_this->cached_size[_this->largest_class] = 0;
s32 largest_class = 0;
s32 biggest_size = -1;
for (size_t i = 0; i < TlsHeapStatic::NumClassInfo; i++) {
if (biggest_size < _this->cached_size[i]) {
biggest_size = _this->cached_size[i];
largest_class = static_cast<s32>(i);
}
}
_this->largest_class = largest_class;
}
return err;
} else if (ptr == nullptr) {
return 0;
} else {
return EFAULT;
}
}
template<>
errno_t TlsHeapCache::FreeWithSizeImpl<false>(TlsHeapCache *_this, void *ptr, size_t size) {
if (ptr == nullptr) {
return 0;
}
const size_t cls = TlsHeapStatic::GetClassFromSize(size);
if (cls == 0) {
return _this->central->UncacheLargeMemory(ptr);
} else {
return _this->central->UncacheSmallMemory(ptr);
}
}
template<>
errno_t TlsHeapCache::FreeWithSizeImpl<true>(TlsHeapCache *_this, void *ptr, size_t size) {
if (ptr == nullptr) {
return 0;
}
const size_t cls = TlsHeapStatic::GetClassFromSize(size);
if (cls == 0) {
return _this->central->UncacheLargeMemory(ptr);
} else {
*reinterpret_cast<void **>(ptr) = _this->small_mem_lists[cls];
_this->small_mem_lists[cls] = _this->ManglePointer(ptr);
const s32 csize = TlsHeapStatic::GetChunkSize(cls);
_this->total_cached_size += csize;
_this->cached_size[cls] += csize;
if (_this->cached_size[cls] > _this->cached_size[_this->largest_class]) {
_this->largest_class = cls;
}
errno_t err = 0;
if (!_this->central->CheckCachedSize(_this->total_cached_size)) {
_this->central->UncacheSmallMemoryList(_this, _this->small_mem_lists[_this->largest_class]);
_this->small_mem_lists[_this->largest_class] = nullptr;
_this->total_cached_size -= _this->cached_size[_this->largest_class];
_this->cached_size[_this->largest_class] = 0;
s32 largest_class = 0;
s32 biggest_size = -1;
for (size_t i = 0; i < TlsHeapStatic::NumClassInfo; i++) {
if (biggest_size < _this->cached_size[i]) {
biggest_size = _this->cached_size[i];
largest_class = static_cast<s32>(i);
}
}
_this->largest_class = largest_class;
}
return err;
}
}
template<>
size_t TlsHeapCache::GetAllocationSizeImpl<false>(TlsHeapCache *_this, const void *ptr) {
return _this->GetAllocationSizeCommonImpl(ptr);
}
template<>
size_t TlsHeapCache::GetAllocationSizeImpl<true>(TlsHeapCache *_this, const void *ptr) {
return _this->GetAllocationSizeCommonImpl(ptr);
}
size_t TlsHeapCache::GetAllocationSizeCommonImpl(const void *ptr) const {
const s32 cls = this->central->GetClassFromPointer(ptr);
if (cls > 0) {
if (!util::IsAligned(ptr, alignof(u64))) {
/* All pointers we allocate have alignment at least 8. */
return 0;
}
/* Validate class. */
AMS_ASSERT(cls < static_cast<s32>(TlsHeapStatic::NumClassInfo));
if (cls < 0) {
return 0;
}
return TlsHeapStatic::GetChunkSize(cls);
} else if (ptr != nullptr) {
return this->central->GetAllocationSize(ptr);
} else {
return 0;
}
}
template<>
errno_t TlsHeapCache::ReallocateImpl<false>(TlsHeapCache *_this, void *ptr, size_t size, void **p) {
AMS_ASSERT(ptr != nullptr && size != 0);
if (size > MaxSize) {
return ENOMEM;
}
size_t alloc_size, copy_size;
const s32 cls_from_size = TlsHeapStatic::GetClassFromSize(size);
const s32 cls_from_ptr = _this->central->GetClassFromPointer(ptr);
if (cls_from_ptr < 0) {
/* error case. */
return EFAULT;
} else if (cls_from_size) {
if (cls_from_ptr > 0) {
if (cls_from_size <= cls_from_ptr) {
*p = ptr;
return 0;
} else {
alloc_size = size;
copy_size = TlsHeapStatic::GetChunkSize(cls_from_ptr);
}
} else /* if (cls_from_ptr == 0) */ {
alloc_size = size;
copy_size = size;
}
} else if (cls_from_ptr == 0) {
return _this->central->ReallocateLargeMemory(ptr, size, p);
} else /* if (cls_from_ptr > 0) */ {
alloc_size = size;
copy_size = TlsHeapStatic::GetChunkSize(cls_from_ptr);
}
*p = AllocateImpl<false>(_this, alloc_size);
if (*p == nullptr) {
return ENOMEM;
}
std::memcpy(*p, ptr, copy_size);
return FreeImpl<false>(_this, ptr);
}
template<>
errno_t TlsHeapCache::ReallocateImpl<true>(TlsHeapCache *_this, void *ptr, size_t size, void **p) {
AMS_ASSERT(ptr != nullptr && size != 0);
if (size > MaxSize) {
return ENOMEM;
}
size_t alloc_size, copy_size;
const s32 cls_from_size = TlsHeapStatic::GetClassFromSize(size);
const s32 cls_from_ptr = _this->central->GetClassFromPointer(ptr);
if (cls_from_ptr < 0) {
/* error case. */
return EFAULT;
} else if (cls_from_size) {
if (cls_from_ptr > 0) {
if (cls_from_size <= cls_from_ptr) {
*p = ptr;
return 0;
} else {
alloc_size = size;
copy_size = TlsHeapStatic::GetChunkSize(cls_from_ptr);
}
} else /* if (cls_from_ptr == 0) */ {
alloc_size = size;
copy_size = size;
}
} else if (cls_from_ptr == 0) {
return _this->central->ReallocateLargeMemory(ptr, size, p);
} else /* if (cls_from_ptr > 0) */ {
alloc_size = size;
copy_size = TlsHeapStatic::GetChunkSize(cls_from_ptr);
}
*p = AllocateImpl<true>(_this, alloc_size);
if (*p == nullptr) {
return ENOMEM;
}
std::memcpy(*p, ptr, copy_size);
return FreeImpl<true>(_this, ptr);
}
template<>
errno_t TlsHeapCache::ShrinkImpl<false>(TlsHeapCache *_this, void *ptr, size_t size) {
return _this->ShrinkCommonImpl(ptr, size);
}
template<>
errno_t TlsHeapCache::ShrinkImpl<true>(TlsHeapCache *_this, void *ptr, size_t size) {
return _this->ShrinkCommonImpl(ptr, size);
}
errno_t TlsHeapCache::ShrinkCommonImpl(void *ptr, size_t size) const {
AMS_ASSERT(ptr != nullptr && size != 0);
if (size > MaxSize) {
return ENOMEM;
}
const s32 cls_from_size = TlsHeapStatic::GetClassFromSize(size);
const s32 cls_from_ptr = this->central->GetClassFromPointer(ptr);
if (cls_from_ptr) {
if (cls_from_ptr <= 0) {
return EFAULT;
} else if (cls_from_size && cls_from_size <= cls_from_ptr) {
return 0;
} else {
return EINVAL;
}
} else if (cls_from_size) {
return this->central->ShrinkLargeMemory(ptr, TlsHeapStatic::PageSize);
} else {
return this->central->ShrinkLargeMemory(ptr, size);
}
}
}

View File

@@ -0,0 +1,102 @@
/*
* 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 "mem_impl_heap_platform.hpp"
#include "mem_impl_heap_tls_heap_static.hpp"
namespace ams::mem::impl::heap {
class TlsHeapCentral;
#define FOREACH_TLS_HEAP_CACHE_FUNC(HANDLER) \
HANDLER(void *, Allocate, allocate, size_t size); \
HANDLER(void *, AllocateAligned, allocate_aligned, size_t size, size_t align); \
HANDLER(errno_t, Free, free, void *ptr); \
HANDLER(errno_t, FreeWithSize, free_with_size, void *ptr, size_t size); \
HANDLER(size_t, GetAllocationSize, get_allocation_size, const void *ptr); \
HANDLER(errno_t, Reallocate, reallocate, void *ptr, size_t size, void **p); \
HANDLER(errno_t, Shrink, shrink, void *ptr, size_t size);
class TlsHeapCache {
public:
static constexpr size_t MaxChunkCount = BITSIZEOF(u64);
public:
#define TLS_HEAP_CACHE_DECLARE_TYPEDEF(RETURN, NAME, MEMBER_NAME, ...) \
using NAME##Func = RETURN (*)(TlsHeapCache *, ## __VA_ARGS__)
FOREACH_TLS_HEAP_CACHE_FUNC(TLS_HEAP_CACHE_DECLARE_TYPEDEF)
#undef TLS_HEAP_CACHE_DECLARE_TYPEDEF
private:
#define TLS_HEAP_CACHE_DECLARE_MEMBER(RETURN, NAME, MEMBER_NAME, ...) \
NAME##Func MEMBER_NAME;
FOREACH_TLS_HEAP_CACHE_FUNC(TLS_HEAP_CACHE_DECLARE_MEMBER)
#undef TLS_HEAP_CACHE_DECLARE_MEMBER
uintptr_t mangle_val;
TlsHeapCentral *central;
size_t total_heap_size;
u32 heap_option;
s32 total_cached_size;
s32 largest_class;
void *small_mem_lists[TlsHeapStatic::NumClassInfo];
s32 cached_size[TlsHeapStatic::NumClassInfo];
u8 chunk_count[TlsHeapStatic::NumClassInfo];
public:
TlsHeapCache(TlsHeapCentral *central, u32 option);
void Finalize();
void *ManglePointer(void *ptr) const {
return reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(ptr) ^ this->mangle_val);
}
bool CheckCache() const;
void ReleaseAllCache();
public:
/* TODO: Better handler with type info to macro this? */
ALWAYS_INLINE void *Allocate(size_t size) { return this->allocate(this, size); }
ALWAYS_INLINE void *Allocate(size_t size, size_t align) { return this->allocate_aligned(this, size, align); }
ALWAYS_INLINE errno_t Free(void *ptr) { return this->free(this, ptr); }
ALWAYS_INLINE errno_t FreeWithSize(void *ptr, size_t size) { return this->free_with_size(this, ptr, size); }
ALWAYS_INLINE size_t GetAllocationSize(const void *ptr) { return this->get_allocation_size(this, ptr); }
ALWAYS_INLINE errno_t Reallocate(void *ptr, size_t size, void **p) { return this->reallocate(this, ptr, size, p); }
ALWAYS_INLINE errno_t Shrink(void *ptr, size_t size) { return this->shrink(this, ptr, size); }
private:
#define TLS_HEAP_CACHE_DECLARE_TEMPLATE(RETURN, NAME, MEMBER_NAME, ...) \
template<bool Cache> static RETURN NAME##Impl(TlsHeapCache *_this, ## __VA_ARGS__ )
FOREACH_TLS_HEAP_CACHE_FUNC(TLS_HEAP_CACHE_DECLARE_TEMPLATE)
#undef TLS_HEAP_CACHE_DECLARE_TEMPLATE
size_t GetAllocationSizeCommonImpl(const void *ptr) const;
errno_t ShrinkCommonImpl(void *ptr, size_t size) const;
};
#define TLS_HEAP_CACHE_DECLARE_INSTANTIATION(RETURN, NAME, MEMBER_NAME, ...) \
template<> RETURN TlsHeapCache::NAME##Impl<false>(TlsHeapCache *_this, ##__VA_ARGS__); \
template<> RETURN TlsHeapCache::NAME##Impl<true>(TlsHeapCache *_this, ##__VA_ARGS__)
FOREACH_TLS_HEAP_CACHE_FUNC(TLS_HEAP_CACHE_DECLARE_INSTANTIATION)
#undef FOREACH_TLS_HEAP_CACHE_FUNC
}

File diff suppressed because it is too large Load Diff

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 <stratosphere.hpp>
#include "mem_impl_heap_platform.hpp"
#include "mem_impl_heap_tls_heap_static.hpp"
#include "mem_impl_heap_tls_heap_cache.hpp"
namespace ams::mem::impl::heap {
/* Simple intrusive list. */
template<typename T>
struct ListHeader {
T *list_next;
};
template<typename T>
struct ListElement : public ListHeader<T> {
T *list_prev;
};
template<typename T>
constexpr inline void ListClearLink(ListHeader<T> *l) {
l->list_next = nullptr;
}
template<typename T>
constexpr inline void ListClearLink(ListElement<T> *l) {
l->list_next = nullptr;
l->list_prev = nullptr;
}
template<typename T>
constexpr inline T *ListGetNext(const ListHeader<T> *l) {
return l->list_next;
}
template<typename T>
constexpr inline T *ListGetNext(const ListElement<T> *l) {
return l->list_next;
}
template<typename T>
constexpr inline T *ListGetPrev(const ListElement<T> *l) {
return l->list_prev;
}
template<typename T>
constexpr inline void ListInsertAfter(ListHeader<T> *hdr, T *e) {
e->list_next = hdr->list_next;
e->list_prev = static_cast<T *>(hdr);
if (hdr->list_next != nullptr) {
hdr->list_next->list_prev = e;
}
hdr->list_next = e;
}
template<typename T>
constexpr inline void ListRemoveSelf(T *e) {
if (e->list_next != nullptr) {
e->list_next->list_prev = e->list_prev;
}
if (e->list_prev != nullptr) {
e->list_prev->list_next = e->list_next;
}
e->list_next = nullptr;
e->list_prev = nullptr;
}
struct Span : public ListElement<Span> {
struct SmallMemory {
SmallMemory *next;
};
enum Status : u8 {
Status_NotUsed = 0,
Status_InUse = 1,
Status_InFreeList = 2,
Status_InUseSystem = 3,
};
u16 object_count;
u8 page_class;
u8 status;
s32 id;
union {
uintptr_t u;
void *p;
SmallMemory *sm;
char *cp;
} start;
uintptr_t num_pages;
union {
struct {
SmallMemory *objects;
u64 is_allocated[8];
} small;
struct {
u8 color[3];
char name[0x10];
} large;
struct {
u32 zero;
} large_clear;
} aux;
};
struct SpanPage : public ListElement<SpanPage> {
struct Info {
u64 alloc_bitmap;
u16 free_count;
u8 is_sticky;
Span span_of_spanpage;
} info;
Span spans[(TlsHeapStatic::PageSize - sizeof(Info) - sizeof(ListElement<SpanPage>)) / sizeof(Span)];
static constexpr size_t MaxSpanCount = sizeof(spans) / sizeof(spans[0]);
};
static_assert(sizeof(SpanPage) <= TlsHeapStatic::PageSize);
static constexpr ALWAYS_INLINE bool CanAllocateSpan(const SpanPage *span_page) {
return span_page->info.alloc_bitmap != ~(decltype(span_page->info.alloc_bitmap){});
}
struct SpanTable {
uintptr_t total_pages;
Span **page_to_span;
u8 *pageclass_cache;
};
struct TlsHeapMemStats {
size_t allocated_size;
size_t free_size;
size_t system_size;
size_t max_allocatable_size;
};
ALWAYS_INLINE Span *GetSpanFromPointer(const SpanTable *table, const void *ptr) {
const size_t idx = TlsHeapStatic::GetPageIndex(reinterpret_cast<uintptr_t>(ptr) - reinterpret_cast<uintptr_t>(table));
if (idx < table->total_pages) {
return table->page_to_span[idx];
} else {
return nullptr;
}
}
ALWAYS_INLINE SpanPage *GetSpanPage(Span *span) {
return reinterpret_cast<SpanPage *>(TlsHeapStatic::AlignDownPage(reinterpret_cast<uintptr_t>(span)));
}
ALWAYS_INLINE Span *GetSpanPageSpan(SpanPage *span_page) {
return std::addressof(span_page->info.span_of_spanpage);
}
ALWAYS_INLINE Span *GetPrevSpan(const SpanTable *span_table, const Span *span) {
return GetSpanFromPointer(span_table, reinterpret_cast<const void *>(span->start.u - 1));
}
ALWAYS_INLINE Span *GetNextSpan(const SpanTable *span_table, const Span *span) {
return GetSpanFromPointer(span_table, reinterpret_cast<const void *>(span->start.u + span->num_pages * TlsHeapStatic::PageSize));
}
class TlsHeapCentral {
private:
using FreeListAvailableWord = u64;
static constexpr size_t FreeListCount = 0x100;
static constexpr size_t NumFreeListBitmaps = FreeListCount / BITSIZEOF(FreeListAvailableWord);
static constexpr ALWAYS_INLINE size_t FreeListAvailableIndex(size_t which) {
return which / BITSIZEOF(FreeListAvailableWord);
}
static constexpr ALWAYS_INLINE size_t FreeListAvailableBit(size_t which) {
return which % BITSIZEOF(FreeListAvailableWord);
}
static constexpr ALWAYS_INLINE FreeListAvailableWord FreeListAvailableMask(size_t which) {
return static_cast<FreeListAvailableWord>(1) << FreeListAvailableBit(which);
}
static_assert(NumFreeListBitmaps * BITSIZEOF(FreeListAvailableWord) == FreeListCount);
private:
SpanTable span_table;
u8 *physical_page_flags;
s32 num_threads;
s32 static_thread_quota;
s32 dynamic_thread_quota;
bool use_virtual_memory;
os::RecursiveMutex lock;
ListHeader<SpanPage> spanpage_list;
ListHeader<SpanPage> full_spanpage_list;
ListHeader<Span> freelists[FreeListCount];
FreeListAvailableWord freelists_bitmap[NumFreeListBitmaps];
ListHeader<Span> smallmem_lists[TlsHeapStatic::NumClassInfo];
public:
TlsHeapCentral() {
this->span_table.total_pages = 0;
}
errno_t Initialize(void *start, size_t size, bool use_virtual_memory);
bool IsClean();
errno_t ReallocateLargeMemory(void *ptr, size_t size, void **p);
errno_t ShrinkLargeMemory(void *ptr, size_t size);
void CalculateHeapHash(HeapHash *out);
errno_t AddThreadCache(TlsHeapCache *cache) {
std::scoped_lock lk(this->lock);
/* Add thread and recalculate. */
this->num_threads++;
this->dynamic_thread_quota = this->GetTotalHeapSize() / (2 * this->num_threads);
return 0;
}
errno_t RemoveThreadCache(TlsHeapCache *cache) {
std::scoped_lock lk(this->lock);
/* Remove thread and recalculate. */
this->num_threads--;
this->dynamic_thread_quota = this->GetTotalHeapSize() / (2 * this->num_threads);
return 0;
}
void *CacheLargeMemory(size_t size) {
std::scoped_lock lk(this->lock);
const size_t num_pages = util::AlignUp(size, TlsHeapStatic::PageSize) / TlsHeapStatic::PageSize;
if (Span *span = this->AllocatePagesImpl(num_pages); span != nullptr) {
return span->start.p;
} else {
return nullptr;
}
}
void *CacheLargeMemoryWithBigAlign(size_t size, size_t align) {
std::scoped_lock lk(this->lock);
const size_t num_pages = util::AlignUp(size, TlsHeapStatic::PageSize) / TlsHeapStatic::PageSize;
Span *span = nullptr;
if (align > TlsHeapStatic::PageSize) {
span = this->AllocatePagesWithBigAlignImpl(num_pages, align);
} else {
span = this->AllocatePagesImpl(num_pages);
}
if (span != nullptr) {
return span->start.p;
} else {
return nullptr;
}
}
void *CacheSmallMemory(size_t cls, size_t align = 0) {
std::scoped_lock lk(this->lock);
return this->CacheSmallMemoryImpl(cls, align, false);
}
void *CacheSmallMemoryForSystem(size_t cls) {
std::scoped_lock lk(this->lock);
return this->CacheSmallMemoryImpl(cls, 0, true);
}
size_t CacheSmallMemoryList(TlsHeapCache *cache, size_t *cls, size_t count, void **p, size_t align = 0) {
std::scoped_lock lk(this->lock);
s32 cpu_id = 0;
if (*cls < 8) {
getcpu(std::addressof(cpu_id));
}
return this->CacheSmallMemoryListImpl(cache, cls, count, p, cpu_id, 0);
}
bool CheckCachedSize(s32 size) const {
return size < this->dynamic_thread_quota && size < this->static_thread_quota;
}
void Dump(DumpMode dump_mode, int fd, bool json) {
std::scoped_lock lk(this->lock);
return this->DumpImpl(dump_mode, fd, json);
}
size_t GetAllocationSize(const void *ptr) {
if (TlsHeapStatic::IsPageAligned(ptr)) {
Span *span = nullptr;
{
std::scoped_lock lk(this->lock);
span = GetSpanFromPointer(std::addressof(this->span_table), ptr);
}
if (span != nullptr) {
return span->num_pages * TlsHeapStatic::PageSize;
} else {
AMS_ASSERT(span != nullptr);
return 0;
}
} else {
/* TODO: Handle error? */
return 0;
}
}
size_t GetClassFromPointer(const void *ptr) {
std::atomic_thread_fence(std::memory_order_acquire);
const size_t idx = (reinterpret_cast<uintptr_t>(ptr) - reinterpret_cast<uintptr_t>(this)) / TlsHeapStatic::PageSize;
if (idx < this->span_table.total_pages) {
if (ptr != nullptr) {
std::scoped_lock lk(this->lock);
Span *span = GetSpanFromPointer(std::addressof(this->span_table), ptr);
if (span != nullptr) {
AMS_ASSERT(span->page_class == this->span_table.pageclass_cache[idx]);
} else {
AMS_ASSERT(span != nullptr);
}
}
return this->span_table.pageclass_cache[idx];
} else {
/* TODO: Handle error? */
return 0xFFFFFFFF;
}
}
errno_t GetColor(const void *ptr, int *out) {
if (out == nullptr) {
return EINVAL;
}
std::scoped_lock lk(this->lock);
if (Span *span = GetSpanFromPointer(std::addressof(this->span_table), ptr); span != nullptr && !span->page_class) {
*out = (span->aux.large.color[0] << 0) | (span->aux.large.color[1] << 0) | (span->aux.large.color[2] << 16);
return 0;
} else {
return EINVAL;
}
}
errno_t SetColor(const void *ptr, int color) {
std::scoped_lock lk(this->lock);
if (Span *span = GetSpanFromPointer(std::addressof(this->span_table), ptr); span != nullptr && !span->page_class) {
span->aux.large.color[0] = (color >> 0) & 0xFF;
span->aux.large.color[1] = (color >> 8) & 0xFF;
span->aux.large.color[2] = (color >> 16) & 0xFF;
return 0;
} else {
return EINVAL;
}
}
errno_t GetMappedMemStats(size_t *out_free_size, size_t *out_max_allocatable_size) {
std::scoped_lock lk(this->lock);
return this->GetMappedMemStatsImpl(out_free_size, out_max_allocatable_size);
}
errno_t GetMemStats(TlsHeapMemStats *out) {
std::scoped_lock lk(this->lock);
return this->GetMemStatsImpl(out);
}
errno_t GetName(const void *ptr, char *dst, size_t dst_size) {
std::scoped_lock lk(this->lock);
if (Span *span = GetSpanFromPointer(std::addressof(this->span_table), ptr); span != nullptr && !span->page_class) {
strlcpy(dst, span->aux.large.name, dst_size);
return 0;
} else {
return EINVAL;
}
}
errno_t SetName(const void *ptr, const char *name) {
std::scoped_lock lk(this->lock);
if (Span *span = GetSpanFromPointer(std::addressof(this->span_table), ptr); span != nullptr && !span->page_class) {
strlcpy(span->aux.large.name, name, sizeof(span->aux.large.name));
return 0;
} else {
return EINVAL;
}
}
size_t GetTotalHeapSize() const {
return this->span_table.total_pages * TlsHeapStatic::PageSize;
}
errno_t UncacheLargeMemory(void *ptr) {
if (TlsHeapStatic::IsPageAligned(ptr)) {
std::scoped_lock lk(this->lock);
if (Span *span = GetSpanFromPointer(std::addressof(this->span_table), ptr); span != nullptr) {
this->FreePagesImpl(span);
return 0;
} else {
return EFAULT;
}
} else {
return EFAULT;
}
}
errno_t UncacheSmallMemory(void *ptr) {
std::scoped_lock lk(this->lock);
return this->UncacheSmallMemoryImpl(ptr);
}
errno_t UncacheSmallMemoryList(TlsHeapCache *cache, void *ptr) {
std::scoped_lock lk(this->lock);
while (true) {
if (ptr == nullptr) {
return 0;
}
ptr = cache->ManglePointer(ptr);
void *next = *reinterpret_cast<void **>(ptr);
if (auto err = this->UncacheSmallMemoryImpl(ptr); err != 0) {
return err;
}
ptr = next;
}
}
errno_t WalkAllocatedPointers(HeapWalkCallback callback, void *user_data) {
/* Explicitly handle locking, as we will release the lock during callback. */
this->lock.lock();
ON_SCOPE_EXIT { this->lock.unlock(); };
return this->WalkAllocatedPointersImpl(callback, user_data);
}
private:
SpanPage *AllocateSpanPage();
Span *AllocateSpanFromSpanPage(SpanPage *sp);
Span *SplitSpan(Span *span, size_t num_pages, Span *new_span);
void MergeFreeSpans(Span *span, Span *span_to_merge, uintptr_t start);
bool DestroySpanPageIfEmpty(SpanPage *sp, bool full);
Span *GetFirstSpan() const;
Span *MakeFreeSpan(size_t num_pages);
Span *SearchFreeSpan(size_t num_pages) const;
void FreeSpanToSpanPage(Span *span, SpanPage *sp);
void FreeSpanToSpanPage(Span *span);
void MergeIntoFreeList(Span *&span);
errno_t AllocatePhysical(void *start, size_t size);
errno_t FreePhysical(void *start, size_t size);
private:
Span *AllocatePagesImpl(size_t num_pages);
Span *AllocatePagesWithBigAlignImpl(size_t num_pages, size_t align);
void FreePagesImpl(Span *span);
void *CacheSmallMemoryImpl(size_t cls, size_t align, bool for_system);
errno_t UncacheSmallMemoryImpl(void *ptr);
size_t CacheSmallMemoryListImpl(TlsHeapCache *cache, size_t *cls, size_t count, void **p, s32 cpu_id, size_t align);
errno_t WalkAllocatedPointersImpl(HeapWalkCallback callback, void *user_data);
errno_t GetMappedMemStatsImpl(size_t *out_free_size, size_t *out_max_allocatable_size);
errno_t GetMemStatsImpl(TlsHeapMemStats *out);
void DumpImpl(DumpMode dump_mode, int fd, bool json);
private:
size_t FreeListFirstNonEmpty(size_t start) const {
if (start < FreeListCount) {
for (size_t i = FreeListAvailableIndex(start); i < util::size(this->freelists_bitmap); i++) {
const FreeListAvailableWord masked = this->freelists_bitmap[i] & ~(FreeListAvailableMask(start) - 1);
if (masked) {
const size_t b = __builtin_ctzll(masked);
const size_t res = i * BITSIZEOF(FreeListAvailableWord) + b;
AMS_ASSERT(res < FreeListCount);
return res;
}
start = (i + 1) * BITSIZEOF(FreeListAvailableWord);
}
}
return FreeListCount;
}
ALWAYS_INLINE void AddToFreeBlockList(Span *span) {
AMS_ASSERT(GetSpanPageSpan(GetSpanPage(span)) != span);
AMS_ASSERT(span->status == Span::Status_InFreeList);
const size_t which = std::min(span->num_pages, FreeListCount) - 1;
ListInsertAfter(std::addressof(this->freelists[which]), span);
this->freelists_bitmap[FreeListAvailableIndex(which)] |= FreeListAvailableMask(which);
}
ALWAYS_INLINE void RemoveFromFreeBlockList(Span *span) {
const size_t which = std::min(span->num_pages, FreeListCount) - 1;
ListRemoveSelf(span);
if (!ListGetNext(std::addressof(this->freelists[which]))) {
this->freelists_bitmap[FreeListAvailableIndex(which)] &= ~FreeListAvailableMask(which);
}
}
Span *AllocateSpanStruct() {
SpanPage *sp = ListGetNext(std::addressof(this->spanpage_list));
while (sp && (sp->info.is_sticky || !CanAllocateSpan(sp))) {
sp = ListGetNext(sp);
}
if (sp == nullptr) {
sp = this->AllocateSpanPage();
}
if (sp != nullptr) {
return this->AllocateSpanFromSpanPage(sp);
} else {
return nullptr;
}
}
s32 CallWalkCallback(HeapWalkCallback callback, void *ptr, size_t size, void *user_data) {
this->lock.unlock();
int res = callback(ptr, size, user_data);
this->lock.lock();
if (res) {
return 0;
} else {
return -1;
}
}
};
}

View File

@@ -0,0 +1,210 @@
/*
* 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 "mem_impl_heap_platform.hpp"
namespace ams::mem::impl::heap {
class TlsHeapStatic {
public:
struct ClassInfo {
u16 num_pages;
u16 chunk_size;
};
static constexpr size_t NumClassInfo = 57;
static constexpr size_t MaxSizeWithClass = 0xC00;
static constexpr size_t ChunkGranularity = 0x10;
static constexpr size_t PageSize = 4_KB;
static constexpr size_t PhysicalPageSize = 256_KB;
public:
static constexpr inline std::array<ClassInfo, NumClassInfo> ClassInfos = {
ClassInfo{ .num_pages = 0, .chunk_size = 0x000, },
ClassInfo{ .num_pages = 1, .chunk_size = 0x010, },
ClassInfo{ .num_pages = 1, .chunk_size = 0x020, },
ClassInfo{ .num_pages = 3, .chunk_size = 0x030, },
ClassInfo{ .num_pages = 1, .chunk_size = 0x040, },
ClassInfo{ .num_pages = 1, .chunk_size = 0x050, },
ClassInfo{ .num_pages = 3, .chunk_size = 0x060, },
ClassInfo{ .num_pages = 2, .chunk_size = 0x070, },
ClassInfo{ .num_pages = 1, .chunk_size = 0x080, },
ClassInfo{ .num_pages = 3, .chunk_size = 0x090, },
ClassInfo{ .num_pages = 2, .chunk_size = 0x0A0, },
ClassInfo{ .num_pages = 1, .chunk_size = 0x0B0, },
ClassInfo{ .num_pages = 3, .chunk_size = 0x0C0, },
ClassInfo{ .num_pages = 3, .chunk_size = 0x0D0, },
ClassInfo{ .num_pages = 1, .chunk_size = 0x0E0, },
ClassInfo{ .num_pages = 1, .chunk_size = 0x0F0, },
ClassInfo{ .num_pages = 1, .chunk_size = 0x100, },
ClassInfo{ .num_pages = 1, .chunk_size = 0x110, },
ClassInfo{ .num_pages = 1, .chunk_size = 0x120, },
ClassInfo{ .num_pages = 3, .chunk_size = 0x130, },
ClassInfo{ .num_pages = 3, .chunk_size = 0x140, },
ClassInfo{ .num_pages = 1, .chunk_size = 0x150, },
ClassInfo{ .num_pages = 2, .chunk_size = 0x160, },
ClassInfo{ .num_pages = 1, .chunk_size = 0x170, },
ClassInfo{ .num_pages = 3, .chunk_size = 0x180, },
ClassInfo{ .num_pages = 1, .chunk_size = 0x190, },
ClassInfo{ .num_pages = 3, .chunk_size = 0x1A0, },
ClassInfo{ .num_pages = 3, .chunk_size = 0x1B0, },
ClassInfo{ .num_pages = 1, .chunk_size = 0x1C0, },
ClassInfo{ .num_pages = 3, .chunk_size = 0x1D0, },
ClassInfo{ .num_pages = 2, .chunk_size = 0x1E0, },
ClassInfo{ .num_pages = 1, .chunk_size = 0x200, },
ClassInfo{ .num_pages = 3, .chunk_size = 0x210, },
ClassInfo{ .num_pages = 2, .chunk_size = 0x220, },
ClassInfo{ .num_pages = 1, .chunk_size = 0x240, },
ClassInfo{ .num_pages = 3, .chunk_size = 0x260, },
ClassInfo{ .num_pages = 2, .chunk_size = 0x270, },
ClassInfo{ .num_pages = 3, .chunk_size = 0x280, },
ClassInfo{ .num_pages = 1, .chunk_size = 0x2A0, },
ClassInfo{ .num_pages = 3, .chunk_size = 0x2D0, },
ClassInfo{ .num_pages = 2, .chunk_size = 0x2E0, },
ClassInfo{ .num_pages = 3, .chunk_size = 0x300, },
ClassInfo{ .num_pages = 1, .chunk_size = 0x330, },
ClassInfo{ .num_pages = 3, .chunk_size = 0x360, },
ClassInfo{ .num_pages = 2, .chunk_size = 0x380, },
ClassInfo{ .num_pages = 3, .chunk_size = 0x3B0, },
ClassInfo{ .num_pages = 1, .chunk_size = 0x400, },
ClassInfo{ .num_pages = 3, .chunk_size = 0x450, },
ClassInfo{ .num_pages = 2, .chunk_size = 0x490, },
ClassInfo{ .num_pages = 3, .chunk_size = 0x4C0, },
ClassInfo{ .num_pages = 1, .chunk_size = 0x550, },
ClassInfo{ .num_pages = 3, .chunk_size = 0x600, },
ClassInfo{ .num_pages = 2, .chunk_size = 0x660, },
ClassInfo{ .num_pages = 3, .chunk_size = 0x6D0, },
ClassInfo{ .num_pages = 1, .chunk_size = 0x800, },
ClassInfo{ .num_pages = 3, .chunk_size = 0x990, },
ClassInfo{ .num_pages = 2, .chunk_size = 0xAA0, },
};
static constexpr inline std::array<size_t, MaxSizeWithClass / ChunkGranularity> SizeToClass = [] {
std::array<size_t, MaxSizeWithClass / ChunkGranularity> arr = {};
arr[0] = 1;
for (size_t i = 1; i < arr.size(); i++) {
const size_t cur_size = i * ChunkGranularity;
for (size_t j = 0; j < ClassInfos.size(); j++) {
if (ClassInfos[j].chunk_size >= cur_size) {
arr[i] = j;
break;
}
}
}
return arr;
}();
public:
static constexpr ALWAYS_INLINE size_t GetClassFromSize(size_t size) {
AMS_ASSERT(size <= MaxSize);
const size_t idx = util::AlignUp(size, ChunkGranularity) / ChunkGranularity;
if (idx < MaxSizeWithClass / ChunkGranularity) {
return SizeToClass[idx];
} else {
return 0;
}
}
static constexpr ALWAYS_INLINE size_t GetRealSizeFromSizeAndAlignment(size_t size, size_t align) {
AMS_ASSERT(size <= MaxSize);
const size_t idx = util::AlignUp(size, ChunkGranularity) / ChunkGranularity;
if (size == 0 || idx >= MaxSizeWithClass / ChunkGranularity) {
return size;
}
const auto cls = SizeToClass[idx];
if (!cls) {
return PageSize;
}
AMS_ASSERT(align != 0);
const size_t mask = align - 1;
for (auto i = cls; i < ClassInfos.size(); i++) {
if ((ClassInfos[i].chunk_size & mask) == 0) {
return ClassInfos[i].chunk_size;
}
}
return PageSize;
}
static constexpr ALWAYS_INLINE bool IsPageAligned(uintptr_t ptr) {
return util::IsAligned(ptr, PageSize);
}
static ALWAYS_INLINE bool IsPageAligned(const void *ptr) {
return IsPageAligned(reinterpret_cast<uintptr_t>(ptr));
}
static constexpr ALWAYS_INLINE size_t GetPageIndex(uintptr_t ptr) {
return ptr / PageSize;
}
static constexpr ALWAYS_INLINE size_t GetPhysicalPageIndex(uintptr_t ptr) {
return ptr / PhysicalPageSize;
}
static constexpr ALWAYS_INLINE uintptr_t AlignUpPage(uintptr_t ptr) {
return util::AlignUp(ptr, PageSize);
}
template<typename T>
static ALWAYS_INLINE T *AlignUpPage(T *ptr) {
static_assert(std::is_pod<T>::value);
static_assert(util::IsAligned(PageSize, alignof(T)));
return reinterpret_cast<T *>(AlignUpPage(reinterpret_cast<uintptr_t>(ptr)));
}
static constexpr ALWAYS_INLINE uintptr_t AlignDownPage(uintptr_t ptr) {
return util::AlignDown(ptr, PageSize);
}
template<typename T>
static ALWAYS_INLINE T *AlignDownPage(T *ptr) {
static_assert(std::is_pod<T>::value);
static_assert(util::IsAligned(PageSize, alignof(T)));
return reinterpret_cast<T *>(AlignDownPage(reinterpret_cast<uintptr_t>(ptr)));
}
static constexpr ALWAYS_INLINE uintptr_t AlignUpPhysicalPage(uintptr_t ptr) {
return util::AlignUp(ptr, PhysicalPageSize);
}
template<typename T>
static ALWAYS_INLINE T *AlignUpPhysicalPage(T *ptr) {
static_assert(std::is_pod<T>::value);
static_assert(util::IsAligned(PhysicalPageSize, alignof(T)));
return reinterpret_cast<T *>(AlignUpPhysicalPage(reinterpret_cast<uintptr_t>(ptr)));
}
static constexpr ALWAYS_INLINE uintptr_t AlignDownPhysicalPage(uintptr_t ptr) {
return util::AlignDown(ptr, PhysicalPageSize);
}
template<typename T>
static ALWAYS_INLINE T *AlignDownPhysicalPage(T *ptr) {
static_assert(std::is_pod<T>::value);
static_assert(util::IsAligned(PhysicalPageSize, alignof(T)));
return reinterpret_cast<T *>(AlignDownPhysicalPage(reinterpret_cast<uintptr_t>(ptr)));
}
static constexpr ALWAYS_INLINE size_t GetChunkSize(size_t cls) {
return ClassInfos[cls].chunk_size;
}
static constexpr ALWAYS_INLINE size_t GetNumPages(size_t cls) {
return ClassInfos[cls].num_pages;
}
};
}

View File

@@ -0,0 +1,41 @@
/*
* 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::mem::impl {
enum Prot {
Prot_none = (0 << 0),
Prot_read = (1 << 0),
Prot_write = (1 << 1),
Prot_exec = (1 << 2),
};
errno_t virtual_alloc(void **ptr, size_t size);
errno_t virtual_free(void *ptr, size_t size);
errno_t physical_alloc(void *ptr, size_t size, Prot prot);
errno_t physical_free(void *ptr, size_t size);
size_t strlcpy(char *dst, const char *src, size_t size);
errno_t gen_random(void *dst, size_t dst_size);
errno_t epochtime(s64 *dst);
errno_t getcpu(s32 *out);
}

View File

@@ -0,0 +1,161 @@
/*
* 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 "mem_impl_platform.hpp"
namespace ams::mem::impl {
namespace {
os::Mutex g_virt_mem_enabled_lock;
bool g_virt_mem_enabled_detected;
bool g_virt_mem_enabled;
void EnsureVirtualAddressMemoryDetected() {
std::scoped_lock lk(g_virt_mem_enabled_lock);
if (AMS_LIKELY(g_virt_mem_enabled_detected)) {
return;
}
g_virt_mem_enabled = os::IsVirtualAddressMemoryEnabled();
}
ALWAYS_INLINE bool IsVirtualAddressMemoryEnabled() {
EnsureVirtualAddressMemoryDetected();
return g_virt_mem_enabled;
}
ALWAYS_INLINE errno_t ConvertResult(Result result) {
/* TODO: Actually implement this in a meaningful way. */
if (R_FAILED(result)) {
return EINVAL;
}
return 0;
}
ALWAYS_INLINE os::MemoryPermission ConvertToOsPermission(Prot prot) {
static_assert(static_cast<int>(Prot_read) == static_cast<int>(os::MemoryPermission_ReadOnly));
static_assert(static_cast<int>(Prot_write) == static_cast<int>(os::MemoryPermission_WriteOnly));
return static_cast<os::MemoryPermission>(prot & os::MemoryPermission_ReadWrite);
}
}
errno_t virtual_alloc(void **ptr, size_t size) {
/* Ensure size isn't too large. */
if (size > mem::impl::MaxSize) {
return EINVAL;
}
/* Allocate virtual memory. */
uintptr_t addr;
if (IsVirtualAddressMemoryEnabled()) {
/* TODO: Support virtual address memory. */
AMS_ABORT("Virtual address memory not supported yet");
} else {
if (auto err = ConvertResult(os::AllocateMemoryBlock(std::addressof(addr), util::AlignUp(size, os::MemoryBlockUnitSize))); err != 0) {
return err;
}
os::SetMemoryPermission(addr, size, os::MemoryPermission_None);
}
return 0;
}
errno_t virtual_free(void *ptr, size_t size) {
/* Ensure size isn't zero. */
if (size == 0) {
return EINVAL;
}
if (IsVirtualAddressMemoryEnabled()) {
/* TODO: Support virtual address memory. */
AMS_ABORT("Virtual address memory not supported yet");
} else {
os::FreeMemoryBlock(reinterpret_cast<uintptr_t>(ptr), util::AlignUp(size, os::MemoryBlockUnitSize));
}
return 0;
}
errno_t physical_alloc(void *ptr, size_t size, Prot prot) {
/* Detect empty allocation. */
const uintptr_t aligned_start = util::AlignDown(reinterpret_cast<uintptr_t>(ptr), os::MemoryPageSize);
const uintptr_t aligned_end = util::AlignUp(reinterpret_cast<uintptr_t>(ptr) + size, os::MemoryPageSize);
const size_t aligned_size = aligned_end - aligned_start;
if (aligned_end <= aligned_start) {
return 0;
}
if (IsVirtualAddressMemoryEnabled()) {
/* TODO: Support virtual address memory. */
AMS_ABORT("Virtual address memory not supported yet");
} else {
os::SetMemoryPermission(aligned_start, aligned_size, ConvertToOsPermission(prot));
}
return 0;
}
errno_t physical_free(void *ptr, size_t size) {
/* Detect empty allocation. */
const uintptr_t aligned_start = util::AlignDown(reinterpret_cast<uintptr_t>(ptr), os::MemoryPageSize);
const uintptr_t aligned_end = util::AlignUp(reinterpret_cast<uintptr_t>(ptr) + size, os::MemoryPageSize);
const size_t aligned_size = aligned_end - aligned_start;
if (aligned_end <= aligned_start) {
return 0;
}
if (IsVirtualAddressMemoryEnabled()) {
/* TODO: Support virtual address memory. */
AMS_ABORT("Virtual address memory not supported yet");
} else {
os::SetMemoryPermission(aligned_start, aligned_size, os::MemoryPermission_None);
}
return 0;
}
size_t strlcpy(char *dst, const char *src, size_t size) {
const size_t src_size = std::strlen(src);
if (src_size >= size) {
if (size) {
std::memcpy(dst, src, size - 1);
dst[size - 1] = 0;
}
} else {
std::memcpy(dst, src, src_size + 1);
}
return src_size;
}
errno_t gen_random(void *dst, size_t dst_size) {
os::GenerateRandomBytes(dst, dst_size);
return 0;
}
errno_t epochtime(s64 *dst) {
/* TODO: What is this calc? */
auto ts = os::ConvertToTimeSpan(os::GetSystemTick());
*dst = (ts.GetNanoSeconds() / INT64_C(100)) + INT64_C(0x8A09F909AE60000);
return 0;
}
errno_t getcpu(s32 *out) {
*out = os::GetCurrentCoreNumber();
return 0;
}
}

View File

@@ -0,0 +1,344 @@
/*
* 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/mem_impl_platform.hpp"
#include "impl/heap/mem_impl_heap_tls_heap_static.hpp"
#include "impl/heap/mem_impl_heap_tls_heap_cache.hpp"
#include "impl/heap/mem_impl_heap_tls_heap_central.hpp"
namespace ams::mem {
constexpr inline size_t DefaultAlignment = alignof(std::max_align_t);
constexpr inline size_t MinimumAllocatorSize = 16_KB;
namespace {
void ThreadDestroy(uintptr_t arg) {
if (arg) {
reinterpret_cast<impl::heap::TlsHeapCache *>(arg)->Finalize();
}
}
ALWAYS_INLINE impl::heap::CentralHeap *GetCentral(const impl::InternalCentralHeapStorage *storage) {
return reinterpret_cast<impl::heap::CentralHeap *>(const_cast<impl::InternalCentralHeapStorage *>(storage));
}
ALWAYS_INLINE impl::heap::CentralHeap *GetCentral(const impl::InternalCentralHeapStorage &storage) {
return GetCentral(std::addressof(storage));
}
ALWAYS_INLINE void GetCache(impl::heap::CentralHeap *central, os::TlsSlot slot) {
impl::heap::CachedHeap tmp_cache;
if (central->MakeCache(std::addressof(tmp_cache))) {
impl::heap::TlsHeapCache *cache = tmp_cache.Release();
os::SetTlsValue(slot, reinterpret_cast<uintptr_t>(cache));
}
}
struct InternalHash {
size_t allocated_count;
size_t allocated_size;
crypto::Sha1Generator sha1;
};
int InternalHashCallback(void *ptr, size_t size, void *user_data) {
InternalHash *hash = reinterpret_cast<InternalHash *>(user_data);
hash->sha1.Update(reinterpret_cast<void *>(std::addressof(ptr)), sizeof(ptr));
hash->sha1.Update(reinterpret_cast<void *>(std::addressof(size)), sizeof(size));
hash->allocated_count++;
hash->allocated_size += size;
return 1;
}
}
StandardAllocator::StandardAllocator() : initialized(false), enable_thread_cache(false), unused(0) {
static_assert(sizeof(impl::heap::CentralHeap) <= sizeof(this->central_heap_storage));
new (std::addressof(this->central_heap_storage)) impl::heap::CentralHeap;
}
StandardAllocator::StandardAllocator(void *mem, size_t size) : StandardAllocator() {
this->Initialize(mem, size);
}
StandardAllocator::StandardAllocator(void *mem, size_t size, bool enable_cache) : StandardAllocator() {
this->Initialize(mem, size, enable_cache);
}
void StandardAllocator::Initialize(void *mem, size_t size) {
this->Initialize(mem, size, false);
}
void StandardAllocator::Initialize(void *mem, size_t size, bool enable_cache) {
AMS_ABORT_UNLESS(!this->initialized);
const uintptr_t aligned_start = util::AlignUp(reinterpret_cast<uintptr_t>(mem), impl::heap::TlsHeapStatic::PageSize);
const uintptr_t aligned_end = util::AlignDown(reinterpret_cast<uintptr_t>(mem) + size, impl::heap::TlsHeapStatic::PageSize);
const size_t aligned_size = aligned_end - aligned_start;
if (mem == nullptr) {
AMS_ABORT_UNLESS(os::IsVirtualAddressMemoryEnabled());
AMS_ABORT_UNLESS(GetCentral(this->central_heap_storage)->Initialize(nullptr, size, 0) == 0);
} else {
AMS_ABORT_UNLESS(aligned_start < aligned_end);
AMS_ABORT_UNLESS(aligned_size >= MinimumAllocatorSize);
AMS_ABORT_UNLESS(GetCentral(this->central_heap_storage)->Initialize(reinterpret_cast<void *>(aligned_start), aligned_size, 0) == 0);
}
this->enable_thread_cache = enable_cache;
if (this->enable_thread_cache) {
R_ABORT_UNLESS(os::AllocateTlsSlot(std::addressof(this->tls_slot), ThreadDestroy));
}
this->initialized = true;
}
void StandardAllocator::Finalize() {
AMS_ABORT_UNLESS(this->initialized);
if (this->enable_thread_cache) {
os::FreeTlsSlot(this->tls_slot);
}
GetCentral(this->central_heap_storage)->Finalize();
this->initialized = false;
}
void *StandardAllocator::Allocate(size_t size) {
AMS_ASSERT(this->initialized);
return this->Allocate(size, DefaultAlignment);
}
void *StandardAllocator::Allocate(size_t size, size_t alignment) {
AMS_ASSERT(this->initialized);
impl::heap::TlsHeapCache *heap_cache = nullptr;
if (this->enable_thread_cache) {
heap_cache = reinterpret_cast<impl::heap::TlsHeapCache *>(os::GetTlsValue(this->tls_slot));
if (!heap_cache) {
GetCache(GetCentral(this->central_heap_storage), this->tls_slot);
heap_cache = reinterpret_cast<impl::heap::TlsHeapCache *>(os::GetTlsValue(this->tls_slot));
}
}
void *ptr = nullptr;
if (heap_cache) {
ptr = heap_cache->Allocate(size, alignment);
if (ptr) {
return ptr;
}
impl::heap::CachedHeap cache;
cache.Reset(heap_cache);
cache.Query(impl::AllocQuery_FinalizeCache);
os::SetTlsValue(this->tls_slot, 0);
}
return GetCentral(this->central_heap_storage)->Allocate(size, alignment);
}
void StandardAllocator::Free(void *ptr) {
AMS_ASSERT(this->initialized);
if (ptr == nullptr) {
return;
}
if (this->enable_thread_cache) {
impl::heap::TlsHeapCache *heap_cache = reinterpret_cast<impl::heap::TlsHeapCache *>(os::GetTlsValue(this->tls_slot));
if (heap_cache) {
heap_cache->Free(ptr);
return;
}
}
auto err = GetCentral(this->central_heap_storage)->Free(ptr);
AMS_ASSERT(err == 0);
}
void *StandardAllocator::Reallocate(void *ptr, size_t new_size) {
AMS_ASSERT(this->initialized);
if (new_size > impl::MaxSize) {
return nullptr;
}
if (ptr == nullptr) {
return this->Allocate(new_size);
}
if (new_size == 0) {
this->Free(ptr);
return nullptr;
}
size_t aligned_new_size = util::AlignUp(new_size, DefaultAlignment);
impl::heap::TlsHeapCache *heap_cache = nullptr;
if (this->enable_thread_cache) {
heap_cache = reinterpret_cast<impl::heap::TlsHeapCache *>(os::GetTlsValue(this->tls_slot));
if (!heap_cache) {
GetCache(GetCentral(this->central_heap_storage), this->tls_slot);
heap_cache = reinterpret_cast<impl::heap::TlsHeapCache *>(os::GetTlsValue(this->tls_slot));
}
}
void *p = nullptr;
impl::errno_t err;
if (heap_cache) {
err = heap_cache->Reallocate(ptr, aligned_new_size, std::addressof(p));
} else {
err = GetCentral(this->central_heap_storage)->Reallocate(ptr, aligned_new_size, std::addressof(p));
}
if (err == 0) {
return p;
} else {
return nullptr;
}
}
size_t StandardAllocator::Shrink(void *ptr, size_t new_size) {
AMS_ASSERT(this->initialized);
if (this->enable_thread_cache) {
impl::heap::TlsHeapCache *heap_cache = reinterpret_cast<impl::heap::TlsHeapCache *>(os::GetTlsValue(this->tls_slot));
if (heap_cache) {
if (heap_cache->Shrink(ptr, new_size) == 0) {
return heap_cache->GetAllocationSize(ptr);
} else {
return 0;
}
}
}
if (GetCentral(this->central_heap_storage)->Shrink(ptr, new_size) == 0) {
return GetCentral(this->central_heap_storage)->GetAllocationSize(ptr);
} else {
return 0;
}
}
void StandardAllocator::ClearThreadCache() const {
if (this->enable_thread_cache) {
impl::heap::TlsHeapCache *heap_cache = reinterpret_cast<impl::heap::TlsHeapCache *>(os::GetTlsValue(this->tls_slot));
impl::heap::CachedHeap cache;
cache.Reset(heap_cache);
cache.Query(impl::AllocQuery_ClearCache);
cache.Release();
}
}
void StandardAllocator::CleanUpManagementArea() const {
AMS_ASSERT(this->initialized);
auto err = GetCentral(this->central_heap_storage)->Query(impl::AllocQuery_UnifyFreeList);
AMS_ASSERT(err == 0);
}
size_t StandardAllocator::GetSizeOf(const void *ptr) const {
AMS_ASSERT(this->initialized);
if (!util::IsAligned(reinterpret_cast<uintptr_t>(ptr), DefaultAlignment)) {
return 0;
}
impl::heap::TlsHeapCache *heap_cache = nullptr;
if (this->enable_thread_cache) {
heap_cache = reinterpret_cast<impl::heap::TlsHeapCache *>(os::GetTlsValue(this->tls_slot));
if (!heap_cache) {
GetCache(GetCentral(this->central_heap_storage), this->tls_slot);
heap_cache = reinterpret_cast<impl::heap::TlsHeapCache *>(os::GetTlsValue(this->tls_slot));
}
}
if (heap_cache) {
return heap_cache->GetAllocationSize(ptr);
} else {
return GetCentral(this->central_heap_storage)->GetAllocationSize(ptr);
}
}
size_t StandardAllocator::GetTotalFreeSize() const {
size_t size = 0;
auto err = GetCentral(this->central_heap_storage)->Query(impl::AllocQuery_FreeSizeMapped, std::addressof(size));
if (err != 0) {
err = GetCentral(this->central_heap_storage)->Query(impl::AllocQuery_FreeSize, std::addressof(size));
}
AMS_ASSERT(err == 0);
return size;
}
size_t StandardAllocator::GetAllocatableSize() const {
size_t size = 0;
auto err = GetCentral(this->central_heap_storage)->Query(impl::AllocQuery_MaxAllocatableSizeMapped, std::addressof(size));
if (err != 0) {
err = GetCentral(this->central_heap_storage)->Query(impl::AllocQuery_MaxAllocatableSize, std::addressof(size));
}
AMS_ASSERT(err == 0);
return size;
}
void StandardAllocator::WalkAllocatedBlocks(WalkCallback callback, void *user_data) const {
AMS_ASSERT(this->initialized);
this->ClearThreadCache();
GetCentral(this->central_heap_storage)->WalkAllocatedPointers(callback, user_data);
}
void StandardAllocator::Dump() const {
AMS_ASSERT(this->initialized);
size_t tmp;
auto err = GetCentral(this->central_heap_storage)->Query(impl::AllocQuery_MaxAllocatableSizeMapped, std::addressof(tmp));
if (err == 0) {
GetCentral(this->central_heap_storage)->Query(impl::AllocQuery_Dump, impl::DumpMode_Spans | impl::DumpMode_Pointers, 1);
} else {
GetCentral(this->central_heap_storage)->Query(impl::AllocQuery_Dump, impl::DumpMode_All, 1);
}
}
StandardAllocator::AllocatorHash StandardAllocator::Hash() const {
AMS_ASSERT(this->initialized);
AllocatorHash alloc_hash;
{
char temp_hash[crypto::Sha1Generator::HashSize];
InternalHash internal_hash;
internal_hash.allocated_count = 0;
internal_hash.allocated_size = 0;
internal_hash.sha1.Initialize();
this->WalkAllocatedBlocks(InternalHashCallback, reinterpret_cast<void *>(std::addressof(internal_hash)));
alloc_hash.allocated_count = internal_hash.allocated_count;
alloc_hash.allocated_size = internal_hash.allocated_size;
internal_hash.sha1.GetHash(temp_hash, sizeof(temp_hash));
std::memcpy(std::addressof(alloc_hash.hash), temp_hash, sizeof(alloc_hash.hash));
}
return alloc_hash;
}
}

Some files were not shown because too many files have changed in this diff Show More